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