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