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