sbin/hammer: Make info command print a proper error message
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/sysctl.h>
36 #include <sys/ioctl_compat.h>
37
38 #include "hammer_util.h"
39
40 static int64_t getsize(const char *str, int pw);
41 static int trim_volume(struct volume_info *volume);
42 static void format_volume(struct volume_info *volume, int nvols,const char *label);
43 static hammer_off_t format_root_directory(const char *label);
44 static uint64_t nowtime(void);
45 static void print_volume(const struct volume_info *volume);
46 static void usage(int exit_code);
47 static void test_header_junk_size(int64_t size);
48 static void test_boot_area_size(int64_t size);
49 static void test_memory_log_size(int64_t size);
50 static void test_undo_buffer_size(int64_t size);
51
52 static int ForceOpt;
53 static int64_t HeaderJunkSize = -1;
54 static int64_t BootAreaSize = -1;
55 static int64_t MemoryLogSize = -1;
56 static int64_t UndoBufferSize;
57
58 #define GIG     (1024LL*1024*1024)
59
60 int
61 main(int ac, char **av)
62 {
63         uint32_t status;
64         off_t total;
65         off_t avg_vol_size;
66         int ch;
67         int i;
68         int nvols;
69         int eflag = 0;
70         const char *label = NULL;
71         struct volume_info *volume;
72
73         /*
74          * Sanity check basic filesystem structures.  No cookies for us
75          * if it gets broken!
76          */
77         assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
78         assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_MIN_VOL_JUNK);
79         assert(sizeof(struct hammer_blockmap_layer1) == 32);
80         assert(sizeof(struct hammer_blockmap_layer2) == 16);
81
82         /*
83          * Parse arguments
84          */
85         while ((ch = getopt(ac, av, "dfEL:j:b:m:u:hC:V:")) != -1) {
86                 switch(ch) {
87                 case 'd':
88                         ++DebugOpt;
89                         break;
90                 case 'f':
91                         ForceOpt = 1;
92                         break;
93                 case 'E':
94                         eflag = 1;
95                         break;
96                 case 'L':
97                         label = optarg;
98                         break;
99                 case 'j': /* Not mentioned in newfs_hammer(8) */
100                         HeaderJunkSize = getsize(optarg, 2);
101                         test_header_junk_size(HeaderJunkSize);
102                         break;
103                 case 'b':
104                         BootAreaSize = getsize(optarg, 2);
105                         test_boot_area_size(BootAreaSize);
106                         break;
107                 case 'm':
108                         MemoryLogSize = getsize(optarg, 2);
109                         test_memory_log_size(MemoryLogSize);
110                         break;
111                 case 'u':
112                         UndoBufferSize = getsize(optarg, 2);
113                         test_undo_buffer_size(UndoBufferSize);
114                         break;
115                 case 'h':
116                         usage(0);
117                         break;
118                 case 'C':
119                         if (hammer_parse_cache_size(optarg) == -1)
120                                 usage(1);
121                         break;
122                 case 'V':
123                         HammerVersion = strtol(optarg, NULL, 0);
124                         if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
125                             HammerVersion >= HAMMER_VOL_VERSION_WIP)
126                                 errx(1,
127                                      "I don't understand how to format "
128                                      "HAMMER version %d",
129                                      HammerVersion);
130                         break;
131                 default:
132                         usage(1);
133                         break;
134                 }
135         }
136         ac -= optind;
137         av += optind;
138         nvols = ac;
139
140         if (HammerVersion == (uint32_t)-1) {
141                 size_t olen = sizeof(HammerVersion);
142                 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
143
144                 if (sysctlbyname("vfs.hammer.supported_version",
145                                  &HammerVersion, &olen, NULL, 0)) {
146                         hwarn("HAMMER VFS not loaded, cannot get version info, "
147                                 "using version %d",
148                                 HammerVersion);
149                 } else if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
150                         HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
151                         hwarn("HAMMER VFS supports higher version than "
152                                 "I understand, using version %d",
153                                 HammerVersion);
154                 }
155         }
156
157         if (nvols == 0)
158                 errx(1, "You must specify at least one special file (volume)");
159         if (nvols > HAMMER_MAX_VOLUMES)
160                 errx(1, "The maximum number of volumes is %d",
161                         HAMMER_MAX_VOLUMES);
162
163         if (label == NULL) {
164                 hwarnx("A filesystem label must be specified");
165                 usage(1);
166         }
167
168         /*
169          * Generate a filesystem id and lookup the filesystem type
170          */
171         uuidgen(&Hammer_FSId, 1);
172         uuid_name_lookup(&Hammer_FSType, HAMMER_FSTYPE_STRING, &status);
173         if (status != uuid_s_ok)
174                 errx(1, "uuids file does not have the DragonFly "
175                         "HAMMER filesystem type");
176
177         total = 0;
178         for (i = 0; i < nvols; ++i) {
179                 volume = init_volume(av[i], O_RDWR, i);
180                 printf("Volume %d %s %-15s size %s\n",
181                         volume->vol_no, volume->type, volume->name,
182                         sizetostr(volume->size));
183
184                 if (eflag)
185                         if (trim_volume(volume) == -1 && ForceOpt == 0)
186                                 errx(1, "Use -f option to proceed");
187                 total += volume->size;
188         }
189
190         /*
191          * Reserve space for (future) header junk, setup our poor-man's
192          * big-block allocator.  Note that the header junk space includes
193          * volume header which is 1928 bytes.
194          */
195         if (HeaderJunkSize == -1)
196                 HeaderJunkSize = HAMMER_VOL_JUNK_SIZE;
197         else if (HeaderJunkSize < (int64_t)sizeof(struct hammer_volume_ondisk))
198                 HeaderJunkSize = sizeof(struct hammer_volume_ondisk);
199         HeaderJunkSize = HAMMER_BUFSIZE_DOALIGN(HeaderJunkSize);
200
201         /*
202          * Calculate defaults for the boot area and memory log sizes,
203          * only if not specified by -b or -m option.
204          */
205         avg_vol_size = total / nvols;
206         if (BootAreaSize == -1)
207                 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
208         if (MemoryLogSize == -1)
209                 MemoryLogSize = init_memory_log_size(MemoryLogSize, avg_vol_size);
210
211         /*
212          * Format the volumes.  Format the root volume first so we can
213          * bootstrap the freemap.
214          */
215         format_volume(get_root_volume(), nvols, label);
216         for (i = 0; i < nvols; ++i)
217                 if (i != HAMMER_ROOT_VOLNO)
218                         format_volume(get_volume(i), nvols, label);
219
220         print_volume(get_root_volume());
221
222         flush_all_volumes();
223         return(0);
224 }
225
226 static
227 void
228 print_volume(const struct volume_info *volume)
229 {
230         hammer_volume_ondisk_t ondisk;
231         hammer_blockmap_t blockmap;
232         hammer_off_t total = 0;
233         int i, nvols;
234         uint32_t status;
235         const char *name = NULL;
236         char *fsidstr;
237
238         ondisk = volume->ondisk;
239         blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
240
241         nvols = ondisk->vol_count;
242         for (i = 0; i < nvols; ++i) {
243                 struct volume_info *p = get_volume(i);
244                 total += p->size;
245                 if (p->vol_no == HAMMER_ROOT_VOLNO) {
246                         assert(name == NULL);
247                         name = p->name;
248                 }
249         }
250
251         uuid_to_string(&Hammer_FSId, &fsidstr, &status);
252
253         printf("---------------------------------------------\n");
254         printf("HAMMER version %d\n", HammerVersion);
255         printf("%d volume%s total size %s\n",
256                 nvols, (nvols == 1 ? "" : "s"), sizetostr(total));
257         printf("root-volume:         %s\n", name);
258         if (DebugOpt)
259                 printf("header-junk-size:    %s\n",
260                         sizetostr(ondisk->vol_bot_beg));
261         printf("boot-area-size:      %s\n",
262                 sizetostr(ondisk->vol_mem_beg - ondisk->vol_bot_beg));
263         printf("memory-log-size:     %s\n",
264                 sizetostr(ondisk->vol_buf_beg - ondisk->vol_mem_beg));
265         printf("undo-buffer-size:    %s\n",
266                 sizetostr(HAMMER_OFF_LONG_ENCODE(blockmap->alloc_offset)));
267         printf("total-pre-allocated: %s\n",
268                 sizetostr(HAMMER_OFF_SHORT_ENCODE(volume->vol_free_off)));
269         printf("fsid:                %s\n", fsidstr);
270         printf("\n");
271         printf("NOTE: Please remember that you may have to manually set up a\n"
272                 "cron(8) job to prune and reblock the filesystem regularly.\n"
273                 "By default, the system automatically runs 'hammer cleanup'\n"
274                 "on a nightly basis.  The periodic.conf(5) variable\n"
275                 "'daily_clean_hammer_enable' can be unset to disable this.\n"
276                 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
277         if (total < 10*GIG)
278                 printf("\nWARNING: The minimum UNDO/REDO FIFO is %s, "
279                         "you really should not\n"
280                         "try to format a HAMMER filesystem this small.\n",
281                         sizetostr(HAMMER_BIGBLOCK_SIZE *
282                                HAMMER_MIN_UNDO_BIGBLOCKS));
283         if (total < 50*GIG)
284                 printf("\nWARNING: HAMMER filesystems less than 50GB are "
285                         "not recommended!\n"
286                         "You may have to run 'hammer prune-everything' and "
287                         "'hammer reblock'\n"
288                         "quite often, even if using a nohistory mount.\n");
289 }
290
291 static
292 void
293 usage(int exit_code)
294 {
295         fprintf(stderr,
296                 "usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n"
297                 "                    [-C cachesize[:readahead]] [-V version] special ...\n"
298         );
299         exit(exit_code);
300 }
301
302 static void
303 test_header_junk_size(int64_t size)
304 {
305         if (size < HAMMER_MIN_VOL_JUNK) {
306                 if (ForceOpt == 0)
307                         errx(1, "The minimum header junk size is %s",
308                                 sizetostr(HAMMER_MIN_VOL_JUNK));
309                 else
310                         hwarnx("You have specified header junk size less than %s",
311                                 sizetostr(HAMMER_MIN_VOL_JUNK));
312         } else if (size > HAMMER_MAX_VOL_JUNK) {
313                 errx(1, "The maximum header junk size is %s",
314                         sizetostr(HAMMER_MAX_VOL_JUNK));
315         }
316 }
317
318 static void
319 test_boot_area_size(int64_t size)
320 {
321         if (size < HAMMER_BOOT_MINBYTES) {
322                 if (ForceOpt == 0)
323                         errx(1, "The minimum boot area size is %s",
324                                 sizetostr(HAMMER_BOOT_MINBYTES));
325                 else
326                         hwarnx("You have specified boot area size less than %s",
327                                 sizetostr(HAMMER_BOOT_MINBYTES));
328         } else if (size > HAMMER_BOOT_MAXBYTES) {
329                 errx(1, "The maximum boot area size is %s",
330                         sizetostr(HAMMER_BOOT_MAXBYTES));
331         }
332 }
333
334 static void
335 test_memory_log_size(int64_t size)
336 {
337         if (size < HAMMER_MEM_MINBYTES) {
338                 if (ForceOpt == 0)
339                         errx(1, "The minimum memory log size is %s",
340                                 sizetostr(HAMMER_MEM_MINBYTES));
341                 else
342                         hwarnx("You have specified memory log size less than %s",
343                                 sizetostr(HAMMER_MEM_MINBYTES));
344         } else if (size > HAMMER_MEM_MAXBYTES) {
345                 errx(1, "The maximum memory log size is %s",
346                         sizetostr(HAMMER_MEM_MAXBYTES));
347         }
348 }
349
350 static void
351 test_undo_buffer_size(int64_t size)
352 {
353         int64_t minbuf, maxbuf;
354
355         minbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MIN_UNDO_BIGBLOCKS;
356         maxbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MAX_UNDO_BIGBLOCKS;
357
358         if (size < minbuf) {
359                 if (ForceOpt == 0)
360                         errx(1, "The minimum UNDO/REDO FIFO size is %s",
361                                 sizetostr(minbuf));
362                 else
363                         hwarnx("You have specified an UNDO/REDO FIFO size less "
364                                 "than %s, which may lead to VFS panics",
365                                 sizetostr(minbuf));
366         } else if (size > maxbuf) {
367                 errx(1, "The maximum UNDO/REDO FIFO size is %s",
368                         sizetostr(maxbuf));
369         }
370 }
371
372 /*
373  * Convert a string to a 64 bit signed integer with various requirements.
374  */
375 static int64_t
376 getsize(const char *str, int powerof2)
377 {
378         int64_t val;
379         char *ptr;
380
381         val = strtoll(str, &ptr, 0);
382         switch(*ptr) {
383         case 't':
384         case 'T':
385                 val *= 1024;
386                 /* fall through */
387         case 'g':
388         case 'G':
389                 val *= 1024;
390                 /* fall through */
391         case 'm':
392         case 'M':
393                 val *= 1024;
394                 /* fall through */
395         case 'k':
396         case 'K':
397                 val *= 1024;
398                 break;
399         default:
400                 errx(1, "Unknown suffix in number '%s'", str);
401         }
402
403         if (ptr[1])
404                 errx(1, "Unknown suffix in number '%s'", str);
405         if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1))
406                 errx(1, "Value not power of 2: %s", str);
407         if ((powerof2 & 2) && (val & HAMMER_BUFMASK))
408                 errx(1, "Value not an integral multiple of %dK: %s",
409                      HAMMER_BUFSIZE / 1024, str);
410         return(val);
411 }
412
413 /*
414  * Generate a transaction id.  Transaction ids are no longer time-based.
415  * Put the nail in the coffin by not making the first one time-based.
416  *
417  * We could start at 1 here but start at 2^32 to reserve a small domain for
418  * possible future use.
419  */
420 static hammer_tid_t
421 createtid(void)
422 {
423         static hammer_tid_t lasttid;
424
425         if (lasttid == 0)
426                 lasttid = 0x0000000100000000ULL;
427         return(lasttid++);
428 }
429
430 static uint64_t
431 nowtime(void)
432 {
433         struct timeval tv;
434         uint64_t xtime;
435
436         gettimeofday(&tv, NULL);
437         xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
438         return(xtime);
439 }
440
441 /*
442  * TRIM the volume, but only if the backing store is a DEVICE
443  */
444 static
445 int
446 trim_volume(struct volume_info *volume)
447 {
448         size_t olen;
449         char *dev_name, *p;
450         char sysctl_name[64];
451         int trim_enabled;
452         off_t ioarg[2];
453
454         if (strcmp(volume->type, "DEVICE")) {
455                 hwarnx("Cannot TRIM regular file %s", volume->name);
456                 return(-1);
457         }
458         if (strncmp(volume->name, "/dev/da", 7)) {
459                 hwarnx("%s does not support the TRIM command", volume->name);
460                 return(-1);
461         }
462
463         /* Extract a number from /dev/da?s? */
464         dev_name = strdup(volume->name);
465         p = strtok(dev_name + strlen("/dev/da"), "s");
466         sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p);
467         free(dev_name);
468
469         trim_enabled = 0;
470         olen = sizeof(trim_enabled);
471
472         if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) {
473                 hwarnx("%s (%s) does not support the TRIM command",
474                         volume->name, sysctl_name);
475                 return(-1);
476         }
477         if (!trim_enabled) {
478                 hwarnx("Erase device option selected, but sysctl (%s) "
479                         "is not enabled", sysctl_name);
480                 return(-1);
481         }
482
483         ioarg[0] = volume->device_offset;
484         ioarg[1] = volume->size;
485
486         printf("Trimming %s %s, sectors %llu-%llu\n",
487                 volume->type, volume->name,
488                 (unsigned long long)ioarg[0] / 512,
489                 (unsigned long long)ioarg[1] / 512);
490
491         if (ioctl(volume->fd, IOCTLTRIM, ioarg) == -1)
492                 err(1, "Trimming %s failed", volume->name);
493
494         return(0);
495 }
496
497 /*
498  * Format a HAMMER volume.
499  */
500 static
501 void
502 format_volume(struct volume_info *volume, int nvols, const char *label)
503 {
504         struct volume_info *root_vol;
505         hammer_volume_ondisk_t ondisk;
506         int64_t freeblks;
507         int64_t freebytes;
508         int64_t vol_buf_size;
509         hammer_off_t vol_alloc;
510         int i;
511
512         /*
513          * Initialize basic information in the on-disk volume structure.
514          */
515         ondisk = volume->ondisk;
516
517         ondisk->vol_fsid = Hammer_FSId;
518         ondisk->vol_fstype = Hammer_FSType;
519         snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
520         ondisk->vol_no = volume->vol_no;
521         ondisk->vol_count = nvols;
522         ondisk->vol_version = HammerVersion;
523
524         vol_alloc = HeaderJunkSize;
525         ondisk->vol_bot_beg = vol_alloc;
526         vol_alloc += BootAreaSize;
527         ondisk->vol_mem_beg = vol_alloc;
528         vol_alloc += MemoryLogSize;
529
530         /*
531          * The remaining area is the zone 2 buffer allocation area.
532          */
533         ondisk->vol_buf_beg = vol_alloc;
534         ondisk->vol_buf_end = volume->size & ~(int64_t)HAMMER_BUFMASK;
535         vol_buf_size = HAMMER_VOL_BUF_SIZE(ondisk);
536
537         if (vol_buf_size < (int64_t)sizeof(*ondisk))
538                 errx(1, "volume %d %s is too small to hold the volume header",
539                      volume->vol_no, volume->name);
540         if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0)
541                 errx(1, "volume %d %s is too large", volume->vol_no, volume->name);
542
543         ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
544         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
545
546         volume->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0);
547         volume->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no,
548                                 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
549
550         /*
551          * Format the root volume.
552          */
553         if (volume->vol_no == HAMMER_ROOT_VOLNO) {
554                 /*
555                  * Check freemap counts before formatting
556                  */
557                 freeblks = count_freemap(volume);
558                 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
559                 if (freebytes < 10*GIG && ForceOpt == 0)
560                         errx(1, "Cannot create a HAMMER filesystem less than 10GB "
561                                 "unless you use -f\n(for the size of Volume %d).  "
562                                 "HAMMER filesystems less than 50GB are not "
563                                 "recommended.", HAMMER_ROOT_VOLNO);
564
565                 /*
566                  * Starting TID
567                  */
568                 ondisk->vol0_next_tid = createtid();
569
570                 /*
571                  * Format freemap.  vol0_stat_freebigblocks is
572                  * the number of big-blocks available for anything
573                  * other than freemap zone at this point.
574                  */
575                 format_freemap(volume);
576                 assert(ondisk->vol0_stat_freebigblocks == 0);
577                 ondisk->vol0_stat_freebigblocks = initialize_freemap(volume);
578
579                 /*
580                  * Format zones that are mapped to zone-2.
581                  */
582                 for (i = 0; i < HAMMER_MAX_ZONES; ++i)
583                         if (hammer_is_index_record(i))
584                                 format_blockmap(volume, i, 0);
585
586                 /*
587                  * Format undo zone.  Formatting decrements
588                  * vol0_stat_freebigblocks whenever a new big-block
589                  * is allocated for undo zone.
590                  */
591                 format_undomap(volume, &UndoBufferSize);
592                 assert(ondisk->vol0_stat_bigblocks == 0);
593                 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
594
595                 /*
596                  * Format the root directory.  Formatting decrements
597                  * vol0_stat_freebigblocks whenever a new big-block
598                  * is allocated for required zones.
599                  */
600                 ondisk->vol0_btree_root = format_root_directory(label);
601                 ++ondisk->vol0_stat_inodes;     /* root inode */
602         } else {
603                 freeblks = initialize_freemap(volume);
604                 root_vol = get_root_volume();
605                 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
606                 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
607         }
608 }
609
610 /*
611  * Format the root directory.
612  */
613 static
614 hammer_off_t
615 format_root_directory(const char *label)
616 {
617         hammer_off_t btree_off;
618         hammer_off_t idata_off;
619         hammer_off_t pfsd_off;
620         hammer_tid_t create_tid;
621         hammer_node_ondisk_t bnode;
622         hammer_inode_data_t idata;
623         hammer_pseudofs_data_t pfsd;
624         struct buffer_info *data_buffer0 = NULL;
625         struct buffer_info *data_buffer1 = NULL;
626         struct buffer_info *data_buffer2 = NULL;
627         hammer_btree_elm_t elm;
628         uint64_t xtime;
629
630         /*
631          * Allocate zero-filled root btree node, inode and pfs
632          */
633         bnode = alloc_btree_node(&btree_off, &data_buffer0);
634         idata = alloc_meta_element(&idata_off, sizeof(*idata), &data_buffer1);
635         pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
636         create_tid = createtid();
637         xtime = nowtime();
638
639         /*
640          * Populate the inode data and inode record for the root directory.
641          */
642         idata->version = HAMMER_INODE_DATA_VERSION;
643         idata->mode = 0755;
644         idata->ctime = xtime;
645         idata->mtime = xtime;
646         idata->atime = xtime;
647         idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
648         idata->size = 0;
649         idata->nlinks = 1;
650         if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
651                 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
652         if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
653                 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
654
655         /*
656          * Populate the PFS data for the root PFS.
657          */
658         pfsd->sync_low_tid = 1;
659         pfsd->sync_beg_tid = 0;
660         pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on root PFS */
661         pfsd->shared_uuid = Hammer_FSId;
662         pfsd->unique_uuid = Hammer_FSId;
663         pfsd->mirror_flags = 0;
664         snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
665
666         /*
667          * Create the root of the B-Tree.  The root is a leaf node so we
668          * do not have to worry about boundary elements.
669          */
670         bnode->parent = 0;  /* no parent */
671         bnode->count = 2;
672         bnode->type = HAMMER_BTREE_TYPE_LEAF;
673         bnode->mirror_tid = 0;
674
675         /*
676          * Create the first node element for the inode.
677          */
678         elm = &bnode->elms[0];
679         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
680         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
681                                       HAMMER_LOCALIZE_INODE;
682         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
683         elm->leaf.base.key = 0;
684         elm->leaf.base.create_tid = create_tid;
685         elm->leaf.base.delete_tid = 0;
686         elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
687         elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
688         elm->leaf.create_ts = (uint32_t)time(NULL);
689
690         elm->leaf.data_offset = idata_off;
691         elm->leaf.data_len = sizeof(*idata);
692         hammer_crc_set_leaf(HammerVersion, idata, &elm->leaf);
693
694         /*
695          * Create the second node element for the PFS data.
696          * This is supposed to be a record part of the root ip (inode),
697          * so it should have the same obj_type value as above.
698          */
699         elm = &bnode->elms[1];
700         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
701         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
702                                       HAMMER_LOCALIZE_MISC;
703         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
704         elm->leaf.base.key = 0;
705         elm->leaf.base.create_tid = create_tid;
706         elm->leaf.base.delete_tid = 0;
707         elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
708         elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
709         elm->leaf.create_ts = (uint32_t)time(NULL);
710
711         elm->leaf.data_offset = pfsd_off;
712         elm->leaf.data_len = sizeof(*pfsd);
713         hammer_crc_set_leaf(HammerVersion, pfsd, &elm->leaf);
714
715         hammer_crc_set_btree(HammerVersion, bnode);
716
717         rel_buffer(data_buffer0);
718         rel_buffer(data_buffer1);
719         rel_buffer(data_buffer2);
720
721         return(btree_off);
722 }