4e7279e117409e592de36894e0a7c9e2baec1d19
[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 "newfs_hammer.h"
36
37 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
38 static const char *sizetostr(off_t size);
39 static void trim_volume(struct volume_info *vol);
40 static void check_volume(struct volume_info *vol);
41 static void format_volume(struct volume_info *vol, int nvols,const char *label,
42                         off_t total_size);
43 static hammer_off_t format_root(const char *label);
44 static u_int64_t nowtime(void);
45 static void usage(void);
46
47 static int ForceOpt = 0;
48 static int HammerVersion = -1;
49 static int Eflag = 0;
50
51 #define GIG     (1024LL*1024*1024)
52
53 int
54 main(int ac, char **av)
55 {
56         u_int32_t status;
57         off_t total;
58         off_t avg_vol_size;
59         int ch;
60         int i;
61         const char *label = NULL;
62         struct volume_info *vol;
63         char *fsidstr;
64
65         /*
66          * Sanity check basic filesystem structures.  No cookies for us
67          * if it gets broken!
68          */
69         assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
70         assert(sizeof(struct hammer_blockmap_layer1) == 32);
71         assert(sizeof(struct hammer_blockmap_layer2) == 16);
72
73         /*
74          * Generate a filesystem id and lookup the filesystem type
75          */
76         uuidgen(&Hammer_FSId, 1);
77         uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
78         if (status != uuid_s_ok) {
79                 errx(1, "uuids file does not have the DragonFly "
80                         "HAMMER filesystem type");
81         }
82
83         /*
84          * Parse arguments
85          */
86         while ((ch = getopt(ac, av, "fEL:b:m:u:V:")) != -1) {
87                 switch(ch) {
88                 case 'f':
89                         ForceOpt = 1;
90                         break;
91                 case 'E':
92                         Eflag = 1;
93                         break;
94                 case 'L':
95                         label = optarg;
96                         break;
97                 case 'b':
98                         BootAreaSize = getsize(optarg,
99                                          HAMMER_BUFSIZE,
100                                          HAMMER_BOOT_MAXBYTES, 2);
101                         break;
102                 case 'm':
103                         MemAreaSize = getsize(optarg,
104                                          HAMMER_BUFSIZE,
105                                          HAMMER_MEM_MAXBYTES, 2);
106                         break;
107                 case 'u':
108                         UndoBufferSize = getsize(optarg,
109                                          HAMMER_BIGBLOCK_SIZE,
110                                          HAMMER_BIGBLOCK_SIZE *
111                                          HAMMER_UNDO_LAYER2, 2);
112                         if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
113                                 errx(1, "The minimum UNDO/REDO FIFO size is "
114                                         "500MB\n");
115                         if (UndoBufferSize < 500*1024*1024) {
116                                 fprintf(stderr,
117                                         "WARNING: you have specified an "
118                                         "UNDO/REDO FIFO size less than 500MB,\n"
119                                         "which may lead to VFS panics.\n");
120                         }
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\n",
129                                      HammerVersion);
130                         }
131                         break;
132                 default:
133                         usage();
134                         break;
135                 }
136         }
137
138         if (label == NULL) {
139                 fprintf(stderr,
140                         "newfs_hammer: A filesystem label must be specified\n");
141                 usage();
142         }
143
144         if (HammerVersion < 0) {
145                 size_t olen = sizeof(HammerVersion);
146                 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
147                 if (sysctlbyname("vfs.hammer.supported_version",
148                                  &HammerVersion, &olen, NULL, 0) == 0) {
149                         if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
150                                 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
151                                 fprintf(stderr,
152                                         "newfs_hammer: WARNING: HAMMER VFS "
153                                         "supports higher version than I "
154                                         "understand,\n"
155                                         "using version %d\n",
156                                         HammerVersion);
157                         }
158                 } else {
159                         fprintf(stderr,
160                                 "newfs_hammer: WARNING: HAMMER VFS not "
161                                 "loaded, cannot get version info.\n"
162                                 "Using version %d\n",
163                                 HAMMER_VOL_VERSION_DEFAULT);
164                 }
165         }
166
167         /*
168          * Collect volume information
169          */
170         ac -= optind;
171         av += optind;
172         NumVolumes = ac;
173         RootVolNo = 0;
174
175         if (NumVolumes == 0) {
176                 fprintf(stderr,
177                         "newfs_hammer: You must specify at least one "
178                         "special file (volume)\n");
179                 exit(1);
180         }
181
182         if (NumVolumes > HAMMER_MAX_VOLUMES) {
183                 fprintf(stderr,
184                         "newfs_hammer: The maximum number of volumes is %d\n",
185                         HAMMER_MAX_VOLUMES);
186                 exit(1);
187         }
188
189         total = 0;
190         for (i = 0; i < NumVolumes; ++i) {
191                 vol = setup_volume(i, av[i], 1, O_RDWR);
192
193                 /*
194                  * Load up information on the volume and initialize
195                  * its remaining fields.
196                  */
197                 check_volume(vol);
198                 if (Eflag) {
199                         char sysctl_name[64];
200                         int trim_enabled = 0;
201                         size_t olen = sizeof(trim_enabled);
202                         char *dev_name = strdup(vol->name);
203                         dev_name = strtok(dev_name + strlen("/dev/da"),"s");
204
205                         sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled",
206                             dev_name);
207                         errno=0;
208                         sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
209                         if(errno == ENOENT) {
210                                 printf("Device:%s (%s) does not support the "
211                                     "TRIM command\n", vol->name,sysctl_name);
212                                 usage();
213                         }
214                         if(!trim_enabled) {
215                                 printf("Erase device option selected, but "
216                                     "sysctl (%s) is not enabled\n", sysctl_name);
217                                 usage();
218
219                         }
220                         trim_volume(vol);
221                 }
222                 total += vol->size;
223         }
224
225         /*
226          * Calculate defaults for the boot and memory area sizes.
227          */
228         avg_vol_size = total / NumVolumes;
229         BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
230         MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size);
231
232         /*
233          * Format the volumes.  Format the root volume first so we can
234          * bootstrap the freemap.
235          */
236         format_volume(get_volume(RootVolNo), NumVolumes, label, total);
237         for (i = 0; i < NumVolumes; ++i) {
238                 if (i != RootVolNo)
239                         format_volume(get_volume(i), NumVolumes, label, total);
240         }
241
242         /*
243          * Pre-size the blockmap layer1/layer2 infrastructure to the zone
244          * limit.  If we do this the filesystem does not have to allocate
245          * new layer2 blocks which reduces the chances of the reblocker
246          * having to fallback to an extremely inefficient algorithm.
247          */
248         vol = get_volume(RootVolNo);
249         vol->cache.modified = 1;
250         uuid_to_string(&Hammer_FSId, &fsidstr, &status);
251
252         printf("---------------------------------------------\n");
253         printf("%d volume%s total size %s version %d\n",
254                 NumVolumes, (NumVolumes == 1 ? "" : "s"),
255                 sizetostr(total), HammerVersion);
256         printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
257         printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
258         printf("undo-buffer-size:    %s\n", sizetostr(UndoBufferSize));
259         printf("total-pre-allocated: %s\n",
260                 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
261         printf("fsid:                %s\n", fsidstr);
262         printf("\n");
263         printf("NOTE: Please remember that you may have to manually set up a\n"
264                 "cron(8) job to prune and reblock the filesystem regularly.\n"
265                 "By default, the system automatically runs 'hammer cleanup'\n"
266                 "on a nightly basis.  The periodic.conf(5) variable\n"
267                 "'daily_clean_hammer_enable' can be unset to disable this.\n"
268                 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
269         if (total < 10*GIG) {
270                 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
271                        "really should not\n"
272                        "try to format a HAMMER filesystem this small.\n");
273         }
274         if (total < 50*GIG) {
275                 printf("\nWARNING: HAMMER filesystems less than 50GB are "
276                         "not recommended!\n"
277                         "You may have to run 'hammer prune-everything' and "
278                         "'hammer reblock'\n"
279                         "quite often, even if using a nohistory mount.\n");
280         }
281         flush_all_volumes();
282         return(0);
283 }
284
285 static
286 void
287 usage(void)
288 {
289         fprintf(stderr,
290                 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
291                 "                    [-V version] special ...\n"
292         );
293         exit(1);
294 }
295
296 /*
297  * Convert the size in bytes to a human readable string.
298  */
299 static
300 const char *
301 sizetostr(off_t size)
302 {
303         static char buf[32];
304
305         if (size < 1024 / 2) {
306                 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
307         } else if (size < 1024 * 1024 / 2) {
308                 snprintf(buf, sizeof(buf), "%6.2fKB",
309                         (double)size / 1024);
310         } else if (size < 1024 * 1024 * 1024LL / 2) {
311                 snprintf(buf, sizeof(buf), "%6.2fMB",
312                         (double)size / (1024 * 1024));
313         } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
314                 snprintf(buf, sizeof(buf), "%6.2fGB",
315                         (double)size / (1024 * 1024 * 1024LL));
316         } else {
317                 snprintf(buf, sizeof(buf), "%6.2fTB",
318                         (double)size / (1024 * 1024 * 1024LL * 1024LL));
319         }
320         return(buf);
321 }
322
323 /*
324  * Convert a string to a 64 bit signed integer with various requirements.
325  */
326 static int64_t
327 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
328 {
329         int64_t val;
330         char *ptr;
331
332         val = strtoll(str, &ptr, 0);
333         switch(*ptr) {
334         case 't':
335         case 'T':
336                 val *= 1024;
337                 /* fall through */
338         case 'g':
339         case 'G':
340                 val *= 1024;
341                 /* fall through */
342         case 'm':
343         case 'M':
344                 val *= 1024;
345                 /* fall through */
346         case 'k':
347         case 'K':
348                 val *= 1024;
349                 break;
350         default:
351                 errx(1, "Unknown suffix in number '%s'\n", str);
352                 /* not reached */
353         }
354         if (ptr[1]) {
355                 errx(1, "Unknown suffix in number '%s'\n", str);
356                 /* not reached */
357         }
358         if (val < minval) {
359                 errx(1, "Value too small: %s, min is %s\n",
360                      str, sizetostr(minval));
361                 /* not reached */
362         }
363         if (val > maxval) {
364                 errx(1, "Value too large: %s, max is %s\n",
365                      str, sizetostr(maxval));
366                 /* not reached */
367         }
368         if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
369                 errx(1, "Value not power of 2: %s\n", str);
370                 /* not reached */
371         }
372         if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
373                 errx(1, "Value not an integral multiple of %dK: %s",
374                      HAMMER_BUFSIZE / 1024, str);
375                 /* not reached */
376         }
377         return(val);
378 }
379
380 /*
381  * Generate a transaction id.  Transaction ids are no longer time-based.
382  * Put the nail in the coffin by not making the first one time-based.
383  *
384  * We could start at 1 here but start at 2^32 to reserve a small domain for
385  * possible future use.
386  */
387 static hammer_tid_t
388 createtid(void)
389 {
390         static hammer_tid_t lasttid;
391
392         if (lasttid == 0)
393                 lasttid = 0x0000000100000000ULL;
394         return(lasttid++);
395 }
396
397 static u_int64_t
398 nowtime(void)
399 {
400         struct timeval tv;
401         u_int64_t xtime;
402
403         gettimeofday(&tv, NULL);
404         xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
405         return(xtime);
406 }
407
408 /*
409  * TRIM the volume, but only if the backing store is a DEVICE
410  */
411 static
412 void
413 trim_volume(struct volume_info *vol)
414 {
415         if (strncmp(vol->type, "DEVICE", sizeof("DEVICE")) == 0) {
416                 off_t ioarg[2];
417
418                 /* 1MB offset to prevent destroying disk-reserved area */
419                 ioarg[0] = vol->device_offset;
420                 ioarg[1] = vol->size;
421                 printf("Trimming Device:%s, sectors (%llu -%llu)\n",vol->name,
422                     (unsigned long long)ioarg[0]/512,
423                     (unsigned long long)ioarg[1]/512);
424                 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
425                         printf("Device trim failed\n");
426                         usage ();
427                 }
428         }
429 }
430
431 /*
432  * Check basic volume characteristics.  HAMMER filesystems use a minimum
433  * of a 16KB filesystem buffer size.
434  */
435 static
436 void
437 check_volume(struct volume_info *vol)
438 {
439         struct partinfo pinfo;
440         struct stat st;
441
442         /*
443          * Get basic information about the volume
444          */
445         vol->fd = open(vol->name, O_RDWR);
446         if (vol->fd < 0)
447                 err(1, "Unable to open %s R+W", vol->name);
448         if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
449                 /*
450                  * Allow the formatting of regular files as HAMMER volumes
451                  */
452                 if (fstat(vol->fd, &st) < 0)
453                         err(1, "Unable to stat %s", vol->name);
454                 vol->size = st.st_size;
455                 vol->type = "REGFILE";
456
457                 if (Eflag)
458                         errx(1,"Cannot TRIM regular file %s\n", vol->name);
459
460         } else {
461                 /*
462                  * When formatting a block device as a HAMMER volume the
463                  * sector size must be compatible.  HAMMER uses 16384 byte
464                  * filesystem buffers.
465                  */
466                 if (pinfo.reserved_blocks) {
467                         errx(1, "HAMMER cannot be placed in a partition "
468                                 "which overlaps the disklabel or MBR");
469                 }
470                 if (pinfo.media_blksize > HAMMER_BUFSIZE ||
471                     HAMMER_BUFSIZE % pinfo.media_blksize) {
472                         errx(1, "A media sector size of %d is not supported",
473                              pinfo.media_blksize);
474                 }
475
476                 vol->size = pinfo.media_size;
477                 vol->device_offset = pinfo.media_offset;
478                 vol->type = "DEVICE";
479         }
480         printf("Volume %d %s %-15s size %s\n",
481                vol->vol_no, vol->type, vol->name,
482                sizetostr(vol->size));
483
484         /*
485          * Reserve space for (future) header junk, setup our poor-man's
486          * big-block allocator.
487          */
488         vol->vol_alloc = HAMMER_BUFSIZE * 16;
489 }
490
491 /*
492  * Format a HAMMER volume.  Cluster 0 will be initially placed in volume 0.
493  */
494 static
495 void
496 format_volume(struct volume_info *vol, int nvols, const char *label,
497               off_t total_size __unused)
498 {
499         struct volume_info *root_vol;
500         struct hammer_volume_ondisk *ondisk;
501         int64_t freeblks;
502         int64_t freebytes;
503         int i;
504
505         /*
506          * Initialize basic information in the on-disk volume structure.
507          */
508         ondisk = vol->ondisk;
509
510         ondisk->vol_fsid = Hammer_FSId;
511         ondisk->vol_fstype = Hammer_FSType;
512         snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
513         ondisk->vol_no = vol->vol_no;
514         ondisk->vol_count = nvols;
515         ondisk->vol_version = HammerVersion;
516
517         ondisk->vol_bot_beg = vol->vol_alloc;
518         vol->vol_alloc += BootAreaSize;
519         ondisk->vol_mem_beg = vol->vol_alloc;
520         vol->vol_alloc += MemAreaSize;
521
522         /*
523          * The remaining area is the zone 2 buffer allocation area.
524          */
525         ondisk->vol_buf_beg = vol->vol_alloc;
526         ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
527
528         if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
529                 errx(1, "volume %d %s is too small to hold the volume header",
530                      vol->vol_no, vol->name);
531         }
532
533         ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
534                               HAMMER_BUFSIZE;
535         ondisk->vol_blocksize = HAMMER_BUFSIZE;
536
537         ondisk->vol_rootvol = RootVolNo;
538         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
539
540         vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
541         vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
542                                 (ondisk->vol_buf_end - ondisk->vol_buf_beg) &
543                                 ~HAMMER_BIGBLOCK_MASK64);
544
545         /*
546          * Format the root volume.
547          */
548         if (vol->vol_no == RootVolNo) {
549                 /*
550                  * Check freemap counts before formatting
551                  */
552                 freeblks = count_freemap(vol);
553                 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
554                 if (freebytes < 10*GIG && ForceOpt == 0) {
555                         errx(1, "Cannot create a HAMMER filesystem less than 10GB "
556                                 "unless you use -f\n(for the size of Volume %d).  "
557                                 "HAMMER filesystems less than 50GB are not "
558                                 "recommended.\n", RootVolNo);
559                 }
560
561                 /*
562                  * Starting TID
563                  */
564                 ondisk->vol0_next_tid = createtid();
565
566                 /*
567                  * Format freemap.  vol0_stat_freebigblocks is
568                  * the number of big-blocks available for anything
569                  * other than freemap zone at this point.
570                  */
571                 format_freemap(vol);
572                 assert(ondisk->vol0_stat_freebigblocks == 0);
573                 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
574
575                 /*
576                  * Format zones that are mapped to zone-2.
577                  */
578                 for (i = HAMMER_ZONE2_MAPPED_INDEX; i < HAMMER_MAX_ZONES; ++i) {
579                         format_blockmap(&ondisk->vol0_blockmap[i], i, 0);
580                 }
581
582                 /*
583                  * Format undo zone.  Formatting decrements
584                  * vol0_stat_freebigblocks whenever a new big-block
585                  * is allocated for undo zone.
586                  */
587                 format_undomap(vol);
588                 assert(ondisk->vol0_stat_bigblocks == 0);
589                 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
590
591                 /*
592                  * Format the root directory.  Formatting decrements
593                  * vol0_stat_freebigblocks whenever a new big-block
594                  * is allocated for required zones.
595                  */
596                 ondisk->vol0_btree_root = format_root(label);
597                 ++ondisk->vol0_stat_inodes;     /* root inode */
598
599                 rel_volume(vol);
600         } else {
601                 freeblks = initialize_freemap(vol);
602                 root_vol = get_volume(RootVolNo);
603                 root_vol->cache.modified = 1;
604                 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
605                 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
606                 rel_volume(root_vol);
607         }
608 }
609
610 /*
611  * Format the root directory.
612  */
613 static
614 hammer_off_t
615 format_root(const char *label)
616 {
617         hammer_off_t btree_off;
618         hammer_off_t pfsd_off;
619         hammer_off_t data_off;
620         hammer_tid_t create_tid;
621         hammer_node_ondisk_t bnode;
622         struct hammer_inode_data *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         u_int64_t xtime;
629
630         /*
631          * Allocate zero-filled root btree node, inode and pfs
632          */
633         bnode = alloc_btree_element(&btree_off, &data_buffer0);
634         idata = alloc_meta_element(&data_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         pfsd->sync_low_tid = 1;
656         pfsd->sync_beg_tid = 0;
657         pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
658         pfsd->shared_uuid = Hammer_FSId;
659         pfsd->unique_uuid = Hammer_FSId;
660         pfsd->reserved01 = 0;
661         pfsd->mirror_flags = 0;
662         snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
663
664         /*
665          * Create the root of the B-Tree.  The root is a leaf node so we
666          * do not have to worry about boundary elements.
667          */
668         bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
669         bnode->count = 2;
670         bnode->type = HAMMER_BTREE_TYPE_LEAF;
671
672         elm = &bnode->elms[0];
673         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
674         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
675                                       HAMMER_LOCALIZE_INODE;
676         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
677         elm->leaf.base.key = 0;
678         elm->leaf.base.create_tid = create_tid;
679         elm->leaf.base.delete_tid = 0;
680         elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
681         elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
682         elm->leaf.create_ts = (u_int32_t)time(NULL);
683
684         elm->leaf.data_offset = data_off;
685         elm->leaf.data_len = sizeof(*idata);
686         elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
687
688         elm = &bnode->elms[1];
689         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
690         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
691                                       HAMMER_LOCALIZE_MISC;
692         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
693         elm->leaf.base.key = 0;
694         elm->leaf.base.create_tid = create_tid;
695         elm->leaf.base.delete_tid = 0;
696         elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
697         elm->leaf.base.obj_type = 0;
698         elm->leaf.create_ts = (u_int32_t)time(NULL);
699
700         elm->leaf.data_offset = pfsd_off;
701         elm->leaf.data_len = sizeof(*pfsd);
702         elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
703
704         bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
705
706         return(btree_off);
707 }