Merge branch 'vendor/NCURSES'
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/sysctl.h>
36 #include <sys/ioctl_compat.h>
37
38 #include "hammer_util.h"
39
40 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
41 static const char *sizetostr(off_t size);
42 static void trim_volume(struct volume_info *vol);
43 static void format_volume(struct volume_info *vol, int nvols,const char *label);
44 static hammer_off_t format_root(const char *label);
45 static uint64_t nowtime(void);
46 static void usage(void);
47
48 static int ForceOpt = 0;
49 static int HammerVersion = -1;
50
51 #define GIG     (1024LL*1024*1024)
52
53 int
54 main(int ac, char **av)
55 {
56         uint32_t status;
57         off_t total;
58         off_t avg_vol_size;
59         int ch;
60         int i;
61         int nvols;
62         int eflag = 0;
63         const char *label = NULL;
64         struct volume_info *vol;
65         char *fsidstr;
66
67         /*
68          * Sanity check basic filesystem structures.  No cookies for us
69          * if it gets broken!
70          */
71         assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
72         assert(sizeof(struct hammer_blockmap_layer1) == 32);
73         assert(sizeof(struct hammer_blockmap_layer2) == 16);
74
75         /*
76          * Generate a filesystem id and lookup the filesystem type
77          */
78         uuidgen(&Hammer_FSId, 1);
79         uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
80         if (status != uuid_s_ok) {
81                 errx(1, "uuids file does not have the DragonFly "
82                         "HAMMER filesystem type");
83         }
84
85         /*
86          * Parse arguments
87          */
88         while ((ch = getopt(ac, av, "fEL:b:m:u:V:")) != -1) {
89                 switch(ch) {
90                 case 'f':
91                         ForceOpt = 1;
92                         break;
93                 case 'E':
94                         eflag = 1;
95                         break;
96                 case 'L':
97                         label = optarg;
98                         break;
99                 case 'b':
100                         BootAreaSize = getsize(optarg,
101                                          HAMMER_BUFSIZE,
102                                          HAMMER_BOOT_MAXBYTES, 2);
103                         break;
104                 case 'm':
105                         MemAreaSize = getsize(optarg,
106                                          HAMMER_BUFSIZE,
107                                          HAMMER_MEM_MAXBYTES, 2);
108                         break;
109                 case 'u':
110                         UndoBufferSize = getsize(optarg,
111                                          HAMMER_BIGBLOCK_SIZE,
112                                          HAMMER_BIGBLOCK_SIZE *
113                                          HAMMER_UNDO_LAYER2, 2);
114                         if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
115                                 errx(1, "The minimum UNDO/REDO FIFO size is "
116                                         "500MB\n");
117                         if (UndoBufferSize < 500*1024*1024) {
118                                 fprintf(stderr,
119                                         "WARNING: you have specified an "
120                                         "UNDO/REDO FIFO size less than 500MB,\n"
121                                         "which may lead to VFS panics.\n");
122                         }
123                         break;
124                 case 'V':
125                         HammerVersion = strtol(optarg, NULL, 0);
126                         if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
127                             HammerVersion >= HAMMER_VOL_VERSION_WIP) {
128                                 errx(1,
129                                      "I don't understand how to format "
130                                      "HAMMER version %d\n",
131                                      HammerVersion);
132                         }
133                         break;
134                 default:
135                         usage();
136                         break;
137                 }
138         }
139
140         if (label == NULL) {
141                 fprintf(stderr,
142                         "newfs_hammer: A filesystem label must be specified\n");
143                 usage();
144         }
145
146         if (HammerVersion < 0) {
147                 size_t olen = sizeof(HammerVersion);
148                 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
149                 if (sysctlbyname("vfs.hammer.supported_version",
150                                  &HammerVersion, &olen, NULL, 0) == 0) {
151                         if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
152                                 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
153                                 fprintf(stderr,
154                                         "newfs_hammer: WARNING: HAMMER VFS "
155                                         "supports higher version than I "
156                                         "understand,\n"
157                                         "using version %d\n",
158                                         HammerVersion);
159                         }
160                 } else {
161                         fprintf(stderr,
162                                 "newfs_hammer: WARNING: HAMMER VFS not "
163                                 "loaded, cannot get version info.\n"
164                                 "Using version %d\n",
165                                 HAMMER_VOL_VERSION_DEFAULT);
166                 }
167         }
168
169         /*
170          * Collect volume information
171          */
172         ac -= optind;
173         av += optind;
174         nvols = ac;
175         RootVolNo = 0;
176
177         if (nvols == 0) {
178                 fprintf(stderr,
179                         "newfs_hammer: You must specify at least one "
180                         "special file (volume)\n");
181                 exit(1);
182         }
183
184         if (nvols > HAMMER_MAX_VOLUMES) {
185                 fprintf(stderr,
186                         "newfs_hammer: The maximum number of volumes is %d\n",
187                         HAMMER_MAX_VOLUMES);
188                 exit(1);
189         }
190
191         total = 0;
192         for (i = 0; i < nvols; ++i) {
193                 vol = setup_volume(i, av[i], 1, O_RDWR);
194
195                 /*
196                  * Load up information on the volume and initialize
197                  * its remaining fields.
198                  */
199                 check_volume(vol);
200                 printf("Volume %d %s %-15s size %s\n",
201                         vol->vol_no, vol->type, vol->name,
202                         sizetostr(vol->size));
203
204                 if (eflag) {
205                         if (strcmp(vol->type, "REGFILE") == 0) {
206                                 fprintf(stderr, "Cannot TRIM regular file %s\n",
207                                         vol->name);
208                                 exit(1);
209                         }
210
211                         char sysctl_name[64];
212                         int trim_enabled = 0;
213                         size_t olen = sizeof(trim_enabled);
214                         char *dev_name = strdup(vol->name);
215                         dev_name = strtok(dev_name + strlen("/dev/da"),"s");
216
217                         sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled",
218                             dev_name);
219                         errno=0;
220                         sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
221                         if(errno == ENOENT) {
222                                 printf("%s %s (%s) does not support the TRIM "
223                                     "command\n",
224                                     vol->type, vol->name, sysctl_name);
225                                 usage();
226                         }
227                         if(!trim_enabled) {
228                                 printf("Erase device option selected, but "
229                                     "sysctl (%s) is not enabled\n", sysctl_name);
230                                 usage();
231
232                         }
233                         trim_volume(vol);
234                 }
235                 total += vol->size;
236         }
237
238         /*
239          * Calculate defaults for the boot and memory area sizes.
240          */
241         avg_vol_size = total / nvols;
242         BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
243         MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size);
244
245         /*
246          * Format the volumes.  Format the root volume first so we can
247          * bootstrap the freemap.
248          */
249         format_volume(get_volume(RootVolNo), nvols, label);
250         for (i = 0; i < nvols; ++i) {
251                 if (i != RootVolNo)
252                         format_volume(get_volume(i), nvols, label);
253         }
254
255         /*
256          * Pre-size the blockmap layer1/layer2 infrastructure to the zone
257          * limit.  If we do this the filesystem does not have to allocate
258          * new layer2 blocks which reduces the chances of the reblocker
259          * having to fallback to an extremely inefficient algorithm.
260          */
261         vol = get_volume(RootVolNo);
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                 nvols, (nvols == 1 ? "" : "s"),
268                 sizetostr(total), HammerVersion);
269         printf("root-volume:         %s\n", vol->name);
270         printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
271         printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
272         printf("undo-buffer-size:    %s\n", sizetostr(UndoBufferSize));
273         printf("total-pre-allocated: %s\n",
274                 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
275         printf("fsid:                %s\n", fsidstr);
276         printf("\n");
277         printf("NOTE: Please remember that you may have to manually set up a\n"
278                 "cron(8) job to prune and reblock the filesystem regularly.\n"
279                 "By default, the system automatically runs 'hammer cleanup'\n"
280                 "on a nightly basis.  The periodic.conf(5) variable\n"
281                 "'daily_clean_hammer_enable' can be unset to disable this.\n"
282                 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
283         if (total < 10*GIG) {
284                 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
285                        "really should not\n"
286                        "try to format a HAMMER filesystem this small.\n");
287         }
288         if (total < 50*GIG) {
289                 printf("\nWARNING: HAMMER filesystems less than 50GB are "
290                         "not recommended!\n"
291                         "You may have to run 'hammer prune-everything' and "
292                         "'hammer reblock'\n"
293                         "quite often, even if using a nohistory mount.\n");
294         }
295         flush_all_volumes();
296         return(0);
297 }
298
299 static
300 void
301 usage(void)
302 {
303         fprintf(stderr,
304                 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
305                 "                    [-V version] special ...\n"
306         );
307         exit(1);
308 }
309
310 /*
311  * Convert the size in bytes to a human readable string.
312  */
313 static
314 const char *
315 sizetostr(off_t size)
316 {
317         static char buf[32];
318
319         if (size < 1024 / 2) {
320                 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
321         } else if (size < 1024 * 1024 / 2) {
322                 snprintf(buf, sizeof(buf), "%6.2fKB",
323                         (double)size / 1024);
324         } else if (size < 1024 * 1024 * 1024LL / 2) {
325                 snprintf(buf, sizeof(buf), "%6.2fMB",
326                         (double)size / (1024 * 1024));
327         } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
328                 snprintf(buf, sizeof(buf), "%6.2fGB",
329                         (double)size / (1024 * 1024 * 1024LL));
330         } else {
331                 snprintf(buf, sizeof(buf), "%6.2fTB",
332                         (double)size / (1024 * 1024 * 1024LL * 1024LL));
333         }
334         return(buf);
335 }
336
337 /*
338  * Convert a string to a 64 bit signed integer with various requirements.
339  */
340 static int64_t
341 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
342 {
343         int64_t val;
344         char *ptr;
345
346         val = strtoll(str, &ptr, 0);
347         switch(*ptr) {
348         case 't':
349         case 'T':
350                 val *= 1024;
351                 /* fall through */
352         case 'g':
353         case 'G':
354                 val *= 1024;
355                 /* fall through */
356         case 'm':
357         case 'M':
358                 val *= 1024;
359                 /* fall through */
360         case 'k':
361         case 'K':
362                 val *= 1024;
363                 break;
364         default:
365                 errx(1, "Unknown suffix in number '%s'\n", str);
366                 /* not reached */
367         }
368         if (ptr[1]) {
369                 errx(1, "Unknown suffix in number '%s'\n", str);
370                 /* not reached */
371         }
372         if (val < minval) {
373                 errx(1, "Value too small: %s, min is %s\n",
374                      str, sizetostr(minval));
375                 /* not reached */
376         }
377         if (val > maxval) {
378                 errx(1, "Value too large: %s, max is %s\n",
379                      str, sizetostr(maxval));
380                 /* not reached */
381         }
382         if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
383                 errx(1, "Value not power of 2: %s\n", str);
384                 /* not reached */
385         }
386         if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
387                 errx(1, "Value not an integral multiple of %dK: %s",
388                      HAMMER_BUFSIZE / 1024, str);
389                 /* not reached */
390         }
391         return(val);
392 }
393
394 /*
395  * Generate a transaction id.  Transaction ids are no longer time-based.
396  * Put the nail in the coffin by not making the first one time-based.
397  *
398  * We could start at 1 here but start at 2^32 to reserve a small domain for
399  * possible future use.
400  */
401 static hammer_tid_t
402 createtid(void)
403 {
404         static hammer_tid_t lasttid;
405
406         if (lasttid == 0)
407                 lasttid = 0x0000000100000000ULL;
408         return(lasttid++);
409 }
410
411 static uint64_t
412 nowtime(void)
413 {
414         struct timeval tv;
415         uint64_t xtime;
416
417         gettimeofday(&tv, NULL);
418         xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
419         return(xtime);
420 }
421
422 /*
423  * TRIM the volume, but only if the backing store is a DEVICE
424  */
425 static
426 void
427 trim_volume(struct volume_info *vol)
428 {
429         if (strncmp(vol->type, "DEVICE", sizeof("DEVICE")) == 0) {
430                 off_t ioarg[2];
431
432                 /* 1MB offset to prevent destroying disk-reserved area */
433                 ioarg[0] = vol->device_offset;
434                 ioarg[1] = vol->size;
435
436                 printf("Trimming %s %s, sectors (%llu -%llu)\n",
437                     vol->type, vol->name,
438                     (unsigned long long)ioarg[0]/512,
439                     (unsigned long long)ioarg[1]/512);
440
441                 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
442                         printf("Device trim failed\n");
443                         usage ();
444                 }
445         }
446 }
447
448 /*
449  * Format a HAMMER volume.
450  */
451 static
452 void
453 format_volume(struct volume_info *vol, int nvols, const char *label)
454 {
455         struct volume_info *root_vol;
456         struct hammer_volume_ondisk *ondisk;
457         int64_t freeblks;
458         int64_t freebytes;
459         hammer_off_t vol_alloc;
460         int i;
461
462         /*
463          * Initialize basic information in the on-disk volume structure.
464          */
465         ondisk = vol->ondisk;
466
467         ondisk->vol_fsid = Hammer_FSId;
468         ondisk->vol_fstype = Hammer_FSType;
469         snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
470         ondisk->vol_no = vol->vol_no;
471         ondisk->vol_count = nvols;
472         ondisk->vol_version = HammerVersion;
473
474         /*
475          * Reserve space for (future) header junk, setup our poor-man's
476          * big-block allocator.
477          */
478         vol_alloc = HAMMER_BUFSIZE * 16;  /* 262144 */
479
480         ondisk->vol_bot_beg = vol_alloc;
481         vol_alloc += BootAreaSize;
482         ondisk->vol_mem_beg = vol_alloc;
483         vol_alloc += MemAreaSize;
484
485         /*
486          * The remaining area is the zone 2 buffer allocation area.
487          */
488         ondisk->vol_buf_beg = vol_alloc;
489         ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
490
491         if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
492                 errx(1, "volume %d %s is too small to hold the volume header",
493                      vol->vol_no, vol->name);
494         }
495
496         ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
497                               HAMMER_BUFSIZE;
498         ondisk->vol_blocksize = HAMMER_BUFSIZE;
499
500         ondisk->vol_rootvol = RootVolNo;
501         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
502
503         vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
504         vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
505                                 (ondisk->vol_buf_end - ondisk->vol_buf_beg) &
506                                 ~HAMMER_BIGBLOCK_MASK64);
507
508         /*
509          * Format the root volume.
510          */
511         if (vol->vol_no == RootVolNo) {
512                 /*
513                  * Check freemap counts before formatting
514                  */
515                 freeblks = count_freemap(vol);
516                 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
517                 if (freebytes < 10*GIG && ForceOpt == 0) {
518                         errx(1, "Cannot create a HAMMER filesystem less than 10GB "
519                                 "unless you use -f\n(for the size of Volume %d).  "
520                                 "HAMMER filesystems less than 50GB are not "
521                                 "recommended.\n", RootVolNo);
522                 }
523
524                 /*
525                  * Starting TID
526                  */
527                 ondisk->vol0_next_tid = createtid();
528
529                 /*
530                  * Format freemap.  vol0_stat_freebigblocks is
531                  * the number of big-blocks available for anything
532                  * other than freemap zone at this point.
533                  */
534                 format_freemap(vol);
535                 assert(ondisk->vol0_stat_freebigblocks == 0);
536                 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
537
538                 /*
539                  * Format zones that are mapped to zone-2.
540                  */
541                 for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
542                         if (hammer_is_zone2_mapped_index(i))
543                                 format_blockmap(&ondisk->vol0_blockmap[i], i, 0);
544                 }
545
546                 /*
547                  * Format undo zone.  Formatting decrements
548                  * vol0_stat_freebigblocks whenever a new big-block
549                  * is allocated for undo zone.
550                  */
551                 format_undomap(vol);
552                 assert(ondisk->vol0_stat_bigblocks == 0);
553                 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
554
555                 /*
556                  * Format the root directory.  Formatting decrements
557                  * vol0_stat_freebigblocks whenever a new big-block
558                  * is allocated for required zones.
559                  */
560                 ondisk->vol0_btree_root = format_root(label);
561                 ++ondisk->vol0_stat_inodes;     /* root inode */
562
563                 rel_volume(vol);
564         } else {
565                 freeblks = initialize_freemap(vol);
566                 root_vol = get_volume(RootVolNo);
567                 root_vol->cache.modified = 1;
568                 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
569                 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
570                 rel_volume(root_vol);
571         }
572 }
573
574 /*
575  * Format the root directory.
576  */
577 static
578 hammer_off_t
579 format_root(const char *label)
580 {
581         hammer_off_t btree_off;
582         hammer_off_t pfsd_off;
583         hammer_off_t data_off;
584         hammer_tid_t create_tid;
585         hammer_node_ondisk_t bnode;
586         struct hammer_inode_data *idata;
587         hammer_pseudofs_data_t pfsd;
588         struct buffer_info *data_buffer0 = NULL;
589         struct buffer_info *data_buffer1 = NULL;
590         struct buffer_info *data_buffer2 = NULL;
591         hammer_btree_elm_t elm;
592         uint64_t xtime;
593
594         /*
595          * Allocate zero-filled root btree node, inode and pfs
596          */
597         bnode = alloc_btree_element(&btree_off, &data_buffer0);
598         idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
599         pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
600         create_tid = createtid();
601         xtime = nowtime();
602
603         /*
604          * Populate the inode data and inode record for the root directory.
605          */
606         idata->version = HAMMER_INODE_DATA_VERSION;
607         idata->mode = 0755;
608         idata->ctime = xtime;
609         idata->mtime = xtime;
610         idata->atime = xtime;
611         idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
612         idata->size = 0;
613         idata->nlinks = 1;
614         if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
615                 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
616         if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
617                 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
618
619         pfsd->sync_low_tid = 1;
620         pfsd->sync_beg_tid = 0;
621         pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
622         pfsd->shared_uuid = Hammer_FSId;
623         pfsd->unique_uuid = Hammer_FSId;
624         pfsd->reserved01 = 0;
625         pfsd->mirror_flags = 0;
626         snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
627
628         /*
629          * Create the root of the B-Tree.  The root is a leaf node so we
630          * do not have to worry about boundary elements.
631          */
632         bnode->count = 2;
633         bnode->type = HAMMER_BTREE_TYPE_LEAF;
634
635         elm = &bnode->elms[0];
636         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
637         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
638                                       HAMMER_LOCALIZE_INODE;
639         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
640         elm->leaf.base.key = 0;
641         elm->leaf.base.create_tid = create_tid;
642         elm->leaf.base.delete_tid = 0;
643         elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
644         elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
645         elm->leaf.create_ts = (uint32_t)time(NULL);
646
647         elm->leaf.data_offset = data_off;
648         elm->leaf.data_len = sizeof(*idata);
649         elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
650
651         elm = &bnode->elms[1];
652         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
653         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
654                                       HAMMER_LOCALIZE_MISC;
655         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
656         elm->leaf.base.key = 0;
657         elm->leaf.base.create_tid = create_tid;
658         elm->leaf.base.delete_tid = 0;
659         elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
660         elm->leaf.base.obj_type = 0;
661         elm->leaf.create_ts = (uint32_t)time(NULL);
662
663         elm->leaf.data_offset = pfsd_off;
664         elm->leaf.data_len = sizeof(*pfsd);
665         elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
666
667         bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
668
669         return(btree_off);
670 }