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