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