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