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