Merge branch 'vendor/NVI2'
[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->cache.modified = 1;
262         uuid_to_string(&Hammer_FSId, &fsidstr, &status);
263
264         printf("---------------------------------------------\n");
265         printf("%d volume%s total size %s version %d\n",
266                 NumVolumes, (NumVolumes == 1 ? "" : "s"),
267                 sizetostr(total), HammerVersion);
268         printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
269         printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
270         printf("undo-buffer-size:    %s\n", sizetostr(UndoBufferSize));
271         printf("total-pre-allocated: %s\n",
272                 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
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 500MB, you "
283                        "really should not\n"
284                        "try to format a HAMMER filesystem this small.\n");
285         }
286         if (total < 50*GIG) {
287                 printf("\nWARNING: HAMMER filesystems less than 50GB are "
288                         "not recommended!\n"
289                         "You may have to run 'hammer prune-everything' and "
290                         "'hammer reblock'\n"
291                         "quite often, even if using a nohistory mount.\n");
292         }
293         flush_all_volumes();
294         return(0);
295 }
296
297 static
298 void
299 usage(void)
300 {
301         fprintf(stderr,
302                 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
303                 "                    [-V version] special ...\n"
304         );
305         exit(1);
306 }
307
308 /*
309  * Convert the size in bytes to a human readable string.
310  */
311 static
312 const char *
313 sizetostr(off_t size)
314 {
315         static char buf[32];
316
317         if (size < 1024 / 2) {
318                 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
319         } else if (size < 1024 * 1024 / 2) {
320                 snprintf(buf, sizeof(buf), "%6.2fKB",
321                         (double)size / 1024);
322         } else if (size < 1024 * 1024 * 1024LL / 2) {
323                 snprintf(buf, sizeof(buf), "%6.2fMB",
324                         (double)size / (1024 * 1024));
325         } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
326                 snprintf(buf, sizeof(buf), "%6.2fGB",
327                         (double)size / (1024 * 1024 * 1024LL));
328         } else {
329                 snprintf(buf, sizeof(buf), "%6.2fTB",
330                         (double)size / (1024 * 1024 * 1024LL * 1024LL));
331         }
332         return(buf);
333 }
334
335 /*
336  * Convert a string to a 64 bit signed integer with various requirements.
337  */
338 static int64_t
339 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
340 {
341         int64_t val;
342         char *ptr;
343
344         val = strtoll(str, &ptr, 0);
345         switch(*ptr) {
346         case 't':
347         case 'T':
348                 val *= 1024;
349                 /* fall through */
350         case 'g':
351         case 'G':
352                 val *= 1024;
353                 /* fall through */
354         case 'm':
355         case 'M':
356                 val *= 1024;
357                 /* fall through */
358         case 'k':
359         case 'K':
360                 val *= 1024;
361                 break;
362         default:
363                 errx(1, "Unknown suffix in number '%s'\n", str);
364                 /* not reached */
365         }
366         if (ptr[1]) {
367                 errx(1, "Unknown suffix in number '%s'\n", str);
368                 /* not reached */
369         }
370         if (val < minval) {
371                 errx(1, "Value too small: %s, min is %s\n",
372                      str, sizetostr(minval));
373                 /* not reached */
374         }
375         if (val > maxval) {
376                 errx(1, "Value too large: %s, max is %s\n",
377                      str, sizetostr(maxval));
378                 /* not reached */
379         }
380         if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
381                 errx(1, "Value not power of 2: %s\n", str);
382                 /* not reached */
383         }
384         if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
385                 errx(1, "Value not an integral multiple of %dK: %s",
386                      HAMMER_BUFSIZE / 1024, str);
387                 /* not reached */
388         }
389         return(val);
390 }
391
392 /*
393  * Generate a transaction id.  Transaction ids are no longer time-based.
394  * Put the nail in the coffin by not making the first one time-based.
395  *
396  * We could start at 1 here but start at 2^32 to reserve a small domain for
397  * possible future use.
398  */
399 static hammer_tid_t
400 createtid(void)
401 {
402         static hammer_tid_t lasttid;
403
404         if (lasttid == 0)
405                 lasttid = 0x0000000100000000ULL;
406         return(lasttid++);
407 }
408
409 static u_int64_t
410 nowtime(void)
411 {
412         struct timeval tv;
413         u_int64_t xtime;
414
415         gettimeofday(&tv, NULL);
416         xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
417         return(xtime);
418 }
419
420 /*
421  * TRIM the volume, but only if the backing store is a DEVICE
422  */
423 static
424 void
425 trim_volume(struct volume_info *vol)
426 {
427         if (strncmp(vol->type, "DEVICE", sizeof("DEVICE")) == 0) {
428                 off_t ioarg[2];
429
430                 /* 1MB offset to prevent destroying disk-reserved area */
431                 ioarg[0] = vol->device_offset;
432                 ioarg[1] = vol->size;
433                 printf("Trimming Device:%s, sectors (%llu -%llu)\n",vol->name,
434                     (unsigned long long)ioarg[0]/512,
435                     (unsigned long long)ioarg[1]/512);
436                 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
437                         printf("Device trim failed\n");
438                         usage ();
439                 }
440         }
441 }
442
443 /*
444  * Check basic volume characteristics.  HAMMER filesystems use a minimum
445  * of a 16KB filesystem buffer size.
446  */
447 static
448 void
449 check_volume(struct volume_info *vol)
450 {
451         struct partinfo pinfo;
452         struct stat st;
453
454         /*
455          * Get basic information about the volume
456          */
457         vol->fd = open(vol->name, O_RDWR);
458         if (vol->fd < 0)
459                 err(1, "Unable to open %s R+W", vol->name);
460         if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
461                 /*
462                  * Allow the formatting of regular files as HAMMER volumes
463                  */
464                 if (fstat(vol->fd, &st) < 0)
465                         err(1, "Unable to stat %s", vol->name);
466                 vol->size = st.st_size;
467                 vol->type = "REGFILE";
468
469                 if (Eflag)
470                         errx(1,"Cannot TRIM regular file %s\n", vol->name);
471
472         } else {
473                 /*
474                  * When formatting a block device as a HAMMER volume the
475                  * sector size must be compatible.  HAMMER uses 16384 byte
476                  * filesystem buffers.
477                  */
478                 if (pinfo.reserved_blocks) {
479                         errx(1, "HAMMER cannot be placed in a partition "
480                                 "which overlaps the disklabel or MBR");
481                 }
482                 if (pinfo.media_blksize > HAMMER_BUFSIZE ||
483                     HAMMER_BUFSIZE % pinfo.media_blksize) {
484                         errx(1, "A media sector size of %d is not supported",
485                              pinfo.media_blksize);
486                 }
487
488                 vol->size = pinfo.media_size;
489                 vol->device_offset = pinfo.media_offset;
490                 vol->type = "DEVICE";
491         }
492         printf("Volume %d %s %-15s size %s\n",
493                vol->vol_no, vol->type, vol->name,
494                sizetostr(vol->size));
495
496         /*
497          * Reserve space for (future) header junk, setup our poor-man's
498          * big-block allocator.
499          */
500         vol->vol_alloc = HAMMER_BUFSIZE * 16;
501 }
502
503 /*
504  * Format a HAMMER volume.  Cluster 0 will be initially placed in volume 0.
505  */
506 static
507 void
508 format_volume(struct volume_info *vol, int nvols, const char *label,
509               off_t total_size __unused)
510 {
511         struct volume_info *root_vol;
512         struct hammer_volume_ondisk *ondisk;
513         int64_t freeblks;
514         int64_t freebytes;
515         int i;
516
517         /*
518          * Initialize basic information in the on-disk volume structure.
519          */
520         ondisk = vol->ondisk;
521
522         ondisk->vol_fsid = Hammer_FSId;
523         ondisk->vol_fstype = Hammer_FSType;
524         snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
525         ondisk->vol_no = vol->vol_no;
526         ondisk->vol_count = nvols;
527         ondisk->vol_version = HammerVersion;
528
529         ondisk->vol_bot_beg = vol->vol_alloc;
530         vol->vol_alloc += BootAreaSize;
531         ondisk->vol_mem_beg = vol->vol_alloc;
532         vol->vol_alloc += MemAreaSize;
533
534         /*
535          * The remaining area is the zone 2 buffer allocation area.
536          */
537         ondisk->vol_buf_beg = vol->vol_alloc;
538         ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
539
540         if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
541                 errx(1, "volume %d %s is too small to hold the volume header",
542                      vol->vol_no, vol->name);
543         }
544
545         ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
546                               HAMMER_BUFSIZE;
547         ondisk->vol_blocksize = HAMMER_BUFSIZE;
548
549         ondisk->vol_rootvol = RootVolNo;
550         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
551
552         vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
553         vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
554                                 (ondisk->vol_buf_end - ondisk->vol_buf_beg) &
555                                 ~HAMMER_BIGBLOCK_MASK64);
556
557         /*
558          * Format the root volume.
559          */
560         if (vol->vol_no == RootVolNo) {
561                 /*
562                  * Check freemap counts before formatting
563                  */
564                 freeblks = count_freemap(vol);
565                 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
566                 if (freebytes < 10*GIG && ForceOpt == 0) {
567                         errx(1, "Cannot create a HAMMER filesystem less than 10GB "
568                                 "unless you use -f\n(for the size of Volume %d).  "
569                                 "HAMMER filesystems less than 50GB are not "
570                                 "recommended.\n", RootVolNo);
571                 }
572
573                 /*
574                  * Starting TID
575                  */
576                 ondisk->vol0_next_tid = createtid();
577
578                 /*
579                  * Format freemap.  vol0_stat_freebigblocks is
580                  * the number of big-blocks available for anything
581                  * other than freemap zone at this point.
582                  */
583                 format_freemap(vol);
584                 assert(ondisk->vol0_stat_freebigblocks == 0);
585                 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
586
587                 /*
588                  * Format zones that are mapped to zone-2.
589                  */
590                 for (i = HAMMER_ZONE2_MAPPED_INDEX; i < HAMMER_MAX_ZONES; ++i) {
591                         format_blockmap(&ondisk->vol0_blockmap[i], i, 0);
592                 }
593
594                 /*
595                  * Format undo zone.  Formatting decrements
596                  * vol0_stat_freebigblocks whenever a new big-block
597                  * is allocated for undo zone.
598                  */
599                 format_undomap(vol);
600                 assert(ondisk->vol0_stat_bigblocks == 0);
601                 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
602
603                 /*
604                  * Format the root directory.  Formatting decrements
605                  * vol0_stat_freebigblocks whenever a new big-block
606                  * is allocated for required zones.
607                  */
608                 ondisk->vol0_btree_root = format_root(label);
609                 ++ondisk->vol0_stat_inodes;     /* root inode */
610
611                 rel_volume(vol);
612         } else {
613                 freeblks = initialize_freemap(vol);
614                 root_vol = get_volume(RootVolNo);
615                 root_vol->cache.modified = 1;
616                 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
617                 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
618                 rel_volume(root_vol);
619         }
620 }
621
622 /*
623  * Format the root directory.
624  */
625 static
626 hammer_off_t
627 format_root(const char *label)
628 {
629         hammer_off_t btree_off;
630         hammer_off_t pfsd_off;
631         hammer_off_t data_off;
632         hammer_tid_t create_tid;
633         hammer_node_ondisk_t bnode;
634         struct hammer_inode_data *idata;
635         hammer_pseudofs_data_t pfsd;
636         struct buffer_info *data_buffer0 = NULL;
637         struct buffer_info *data_buffer1 = NULL;
638         struct buffer_info *data_buffer2 = NULL;
639         hammer_btree_elm_t elm;
640         u_int64_t xtime;
641
642         /*
643          * Allocate zero-filled root btree node, inode and pfs
644          */
645         bnode = alloc_btree_element(&btree_off, &data_buffer0);
646         idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
647         pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
648         create_tid = createtid();
649         xtime = nowtime();
650
651         /*
652          * Populate the inode data and inode record for the root directory.
653          */
654         idata->version = HAMMER_INODE_DATA_VERSION;
655         idata->mode = 0755;
656         idata->ctime = xtime;
657         idata->mtime = xtime;
658         idata->atime = xtime;
659         idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
660         idata->size = 0;
661         idata->nlinks = 1;
662         if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
663                 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
664         if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
665                 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
666
667         pfsd->sync_low_tid = 1;
668         pfsd->sync_beg_tid = 0;
669         pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
670         pfsd->shared_uuid = Hammer_FSId;
671         pfsd->unique_uuid = Hammer_FSId;
672         pfsd->reserved01 = 0;
673         pfsd->mirror_flags = 0;
674         snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
675
676         /*
677          * Create the root of the B-Tree.  The root is a leaf node so we
678          * do not have to worry about boundary elements.
679          */
680         bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
681         bnode->count = 2;
682         bnode->type = HAMMER_BTREE_TYPE_LEAF;
683
684         elm = &bnode->elms[0];
685         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
686         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
687                                       HAMMER_LOCALIZE_INODE;
688         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
689         elm->leaf.base.key = 0;
690         elm->leaf.base.create_tid = create_tid;
691         elm->leaf.base.delete_tid = 0;
692         elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
693         elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
694         elm->leaf.create_ts = (u_int32_t)time(NULL);
695
696         elm->leaf.data_offset = data_off;
697         elm->leaf.data_len = sizeof(*idata);
698         elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
699
700         elm = &bnode->elms[1];
701         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
702         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
703                                       HAMMER_LOCALIZE_MISC;
704         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
705         elm->leaf.base.key = 0;
706         elm->leaf.base.create_tid = create_tid;
707         elm->leaf.base.delete_tid = 0;
708         elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
709         elm->leaf.base.obj_type = 0;
710         elm->leaf.create_ts = (u_int32_t)time(NULL);
711
712         elm->leaf.data_offset = pfsd_off;
713         elm->leaf.data_len = sizeof(*pfsd);
714         elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
715
716         bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
717
718         return(btree_off);
719 }