HAMMER VFS - Bump the default hammer rev to 6
[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  * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.44 2008/08/21 23:32:27 thomas Exp $
35  */
36
37 #include "newfs_hammer.h"
38
39 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
40 static const char *sizetostr(off_t size);
41 static void check_volume(struct volume_info *vol);
42 static void format_volume(struct volume_info *vol, int nvols,const char *label,
43                         off_t total_size);
44 static hammer_off_t format_root(const char *label);
45 static u_int64_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         u_int32_t status;
57         off_t total;
58         int ch;
59         int i;
60         const char *label = NULL;
61         struct volume_info *vol;
62         char *fsidstr;
63
64         /*
65          * Sanity check basic filesystem structures.  No cookies for us
66          * if it gets broken!
67          */
68         assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
69         assert(sizeof(struct hammer_blockmap_layer1) == 32);
70         assert(sizeof(struct hammer_blockmap_layer2) == 16);
71
72         /*
73          * Generate a filesystem id and lookup the filesystem type
74          */
75         uuidgen(&Hammer_FSId, 1);
76         uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
77         if (status != uuid_s_ok) {
78                 errx(1, "uuids file does not have the DragonFly "
79                         "HAMMER filesystem type");
80         }
81
82         /*
83          * Parse arguments
84          */
85         while ((ch = getopt(ac, av, "fL:b:m:u:V:")) != -1) {
86                 switch(ch) {
87                 case 'f':
88                         ForceOpt = 1;
89                         break;
90                 case 'L':
91                         label = optarg;
92                         break;
93                 case 'b':
94                         BootAreaSize = getsize(optarg,
95                                          HAMMER_BUFSIZE,
96                                          HAMMER_BOOT_MAXBYTES, 2);
97                         break;
98                 case 'm':
99                         MemAreaSize = getsize(optarg,
100                                          HAMMER_BUFSIZE,
101                                          HAMMER_MEM_MAXBYTES, 2);
102                         break;
103                 case 'u':
104                         UndoBufferSize = getsize(optarg,
105                                          HAMMER_LARGEBLOCK_SIZE,
106                                          HAMMER_LARGEBLOCK_SIZE *
107                                          HAMMER_UNDO_LAYER2, 2);
108                         if (UndoBufferSize < 100*1024*1024 && ForceOpt == 0)
109                                 errx(1, "The minimum UNDO fifo size is 100MB\n");
110                         if (UndoBufferSize < 100*1024*1024) {
111                                 fprintf(stderr, 
112                                         "WARNING: you have specified an UNDO "
113                                         "FIFO size less than 100MB, which may\n"
114                                         "lead to VFS panics.\n");
115                         }
116                         break;
117                 case 'V':
118                         HammerVersion = strtol(optarg, NULL, 0);
119                         if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
120                             HammerVersion >= HAMMER_VOL_VERSION_WIP) {
121                                 errx(1,
122                                      "I don't understand how to format "
123                                      "HAMMER version %d\n",
124                                      HammerVersion);
125                         }
126                         break;
127                 default:
128                         usage();
129                         break;
130                 }
131         }
132
133         if (label == NULL) {
134                 fprintf(stderr,
135                         "newfs_hammer: A filesystem label must be specified\n");
136                 usage();
137         }
138
139         if (HammerVersion < 0) {
140                 size_t olen = sizeof(HammerVersion);
141                 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
142                 if (sysctlbyname("vfs.hammer.supported_version",
143                                  &HammerVersion, &olen, NULL, 0) == 0) {
144                         if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
145                                 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
146                                 fprintf(stderr,
147                                         "newfs_hammer: WARNING: HAMMER VFS "
148                                         "supports higher version than I "
149                                         "understand,\n"
150                                         "using version %d\n",
151                                         HammerVersion);
152                         }
153                 } else {
154                         fprintf(stderr,
155                                 "newfs_hammer: WARNING: HAMMER VFS not "
156                                 "loaded, cannot get version info.\n"
157                                 "Using version %d\n",
158                                 HAMMER_VOL_VERSION_DEFAULT);
159                 }
160         }
161
162         /*
163          * Collect volume information
164          */
165         ac -= optind;
166         av += optind;
167         NumVolumes = ac;
168         RootVolNo = 0;
169
170         if (NumVolumes == 0) {
171                 fprintf(stderr,
172                         "newfs_hammer: You must specify at least one special file (volume)\n");
173                 exit(1);
174         }
175
176         if (NumVolumes > HAMMER_MAX_VOLUMES) {
177                 fprintf(stderr,
178                         "newfs_hammer: The maximum number of volumes is %d\n", HAMMER_MAX_VOLUMES);
179                 exit(1);
180         }
181
182         total = 0;
183         for (i = 0; i < NumVolumes; ++i) {
184                 vol = setup_volume(i, av[i], 1, O_RDWR);
185
186                 /*
187                  * Load up information on the volume and initialize
188                  * its remaining fields.
189                  */
190                 check_volume(vol);
191                 total += vol->size;
192         }
193
194         /*
195          * Calculate defaults for the boot and memory area sizes.
196          */
197         if (BootAreaSize == 0) {
198                 BootAreaSize = HAMMER_BOOT_NOMBYTES;
199                 while (BootAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
200                         BootAreaSize >>= 1;
201                 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
202                         BootAreaSize = 0;
203         } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
204                 BootAreaSize = HAMMER_BOOT_MINBYTES;
205         }
206         if (MemAreaSize == 0) {
207                 MemAreaSize = HAMMER_MEM_NOMBYTES;
208                 while (MemAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
209                         MemAreaSize >>= 1;
210                 if (MemAreaSize < HAMMER_MEM_MINBYTES)
211                         MemAreaSize = 0;
212         } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
213                 MemAreaSize = HAMMER_MEM_MINBYTES;
214         }
215
216         /*
217          * Format the volumes.  Format the root volume first so we can
218          * bootstrap the freemap.
219          */
220         format_volume(get_volume(RootVolNo), NumVolumes, label, total);
221         for (i = 0; i < NumVolumes; ++i) {
222                 if (i != RootVolNo)
223                         format_volume(get_volume(i), NumVolumes, label, total);
224         }
225
226         /*
227          * Pre-size the blockmap layer1/layer2 infrastructure to the zone
228          * limit.  If we do this the filesystem does not have to allocate
229          * new layer2 blocks which reduces the chances of the reblocker
230          * having to fallback to an extremely inefficient algorithm.
231          */
232         vol = get_volume(RootVolNo);
233         vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks;
234         vol->cache.modified = 1;
235         uuid_to_string(&Hammer_FSId, &fsidstr, &status);
236
237         printf("---------------------------------------------\n");
238         printf("%d volume%s total size %s version %d\n",
239                 NumVolumes, (NumVolumes == 1 ? "" : "s"),
240                 sizetostr(total), HammerVersion);
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 [-f] [-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 u_int64_t
383 nowtime(void)
384 {
385         struct timeval tv;
386         u_int64_t xtime;
387
388         gettimeofday(&tv, NULL);
389         xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
390         return(xtime);
391 }
392
393 /*
394  * Check basic volume characteristics.  HAMMER filesystems use a minimum
395  * of a 16KB filesystem buffer size.
396  */
397 static
398 void
399 check_volume(struct volume_info *vol)
400 {
401         struct partinfo pinfo;
402         struct stat st;
403
404         /*
405          * Get basic information about the volume
406          */
407         vol->fd = open(vol->name, O_RDWR);
408         if (vol->fd < 0)
409                 err(1, "Unable to open %s R+W", vol->name);
410         if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
411                 /*
412                  * Allow the formatting of regular files as HAMMER volumes
413                  */
414                 if (fstat(vol->fd, &st) < 0)
415                         err(1, "Unable to stat %s", vol->name);
416                 vol->size = st.st_size;
417                 vol->type = "REGFILE";
418         } else {
419                 /*
420                  * When formatting a block device as a HAMMER volume the
421                  * sector size must be compatible.  HAMMER uses 16384 byte
422                  * filesystem buffers.
423                  */
424                 if (pinfo.reserved_blocks) {
425                         errx(1, "HAMMER cannot be placed in a partition "
426                                 "which overlaps the disklabel or MBR");
427                 }
428                 if (pinfo.media_blksize > 16384 ||
429                     16384 % pinfo.media_blksize) {
430                         errx(1, "A media sector size of %d is not supported",
431                              pinfo.media_blksize);
432                 }
433
434                 vol->size = pinfo.media_size;
435                 vol->type = "DEVICE";
436         }
437         printf("Volume %d %s %-15s size %s\n",
438                vol->vol_no, vol->type, vol->name,
439                sizetostr(vol->size));
440
441         /*
442          * Reserve space for (future) header junk, setup our poor-man's
443          * bigblock allocator.
444          */
445         vol->vol_alloc = HAMMER_BUFSIZE * 16;
446 }
447
448 /*
449  * Format a HAMMER volume.  Cluster 0 will be initially placed in volume 0.
450  */
451 static
452 void
453 format_volume(struct volume_info *vol, int nvols, const char *label,
454               off_t total_size __unused)
455 {
456         struct volume_info *root_vol;
457         struct hammer_volume_ondisk *ondisk;
458         int64_t freeblks;
459         int64_t freebytes;
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         ondisk->vol_bot_beg = vol->vol_alloc;
475         vol->vol_alloc += BootAreaSize;
476         ondisk->vol_mem_beg = vol->vol_alloc;
477         vol->vol_alloc += MemAreaSize;
478
479         /*
480          * The remaining area is the zone 2 buffer allocation area.  These
481          * buffers
482          */
483         ondisk->vol_buf_beg = vol->vol_alloc;
484         ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
485
486         if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
487                 errx(1, "volume %d %s is too small to hold the volume header",
488                      vol->vol_no, vol->name);
489         }
490
491         ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
492                               HAMMER_BUFSIZE;
493         ondisk->vol_blocksize = HAMMER_BUFSIZE;
494
495         ondisk->vol_rootvol = RootVolNo;
496         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
497
498         vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
499         vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, (ondisk->vol_buf_end - ondisk->vol_buf_beg) & ~HAMMER_LARGEBLOCK_MASK64);
500
501         /*
502          * Format the root volume.
503          */
504         if (vol->vol_no == RootVolNo) {
505                 /*
506                  * Starting TID
507                  */
508                 ondisk->vol0_next_tid = createtid();
509
510                 format_freemap(vol,
511                         &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
512
513                 freeblks = initialize_freemap(vol);
514                 ondisk->vol0_stat_freebigblocks = freeblks;
515
516                 freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64;
517                 if (freebytes < 1*GIG && ForceOpt == 0) {
518                         errx(1, "Cannot create a HAMMER filesystem less than "
519                                 "1GB unless you use -f.  HAMMER filesystems\n"
520                                 "less than 50GB are not recommended\n");
521                 }
522                         
523                 for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
524                         format_blockmap(&ondisk->vol0_blockmap[i],
525                                         HAMMER_ZONE_ENCODE(i, 0));
526                 }
527                 format_undomap(ondisk);
528
529                 ondisk->vol0_btree_root = format_root(label);
530                 ++ondisk->vol0_stat_inodes;     /* root inode */
531         } else {
532                 freeblks = initialize_freemap(vol);
533                 root_vol = get_volume(RootVolNo);
534                 root_vol->cache.modified = 1;
535                 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
536                 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
537                 rel_volume(root_vol);
538         }
539 }
540
541 /*
542  * Format the root directory.
543  */
544 static
545 hammer_off_t
546 format_root(const char *label)
547 {
548         hammer_off_t btree_off;
549         hammer_off_t pfsd_off;
550         hammer_off_t data_off;
551         hammer_tid_t create_tid;
552         hammer_node_ondisk_t bnode;
553         struct hammer_inode_data *idata;
554         hammer_pseudofs_data_t pfsd;
555         struct buffer_info *data_buffer1 = NULL;
556         struct buffer_info *data_buffer2 = NULL;
557         hammer_btree_elm_t elm;
558         u_int64_t xtime;
559
560         bnode = alloc_btree_element(&btree_off);
561         idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
562         pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
563         create_tid = createtid();
564         xtime = nowtime();
565
566         /*
567          * Populate the inode data and inode record for the root directory.
568          */
569         idata->version = HAMMER_INODE_DATA_VERSION;
570         idata->mode = 0755;
571         idata->ctime = xtime;
572         idata->mtime = xtime;
573         idata->atime = xtime;
574         idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
575         idata->size = 0;
576         idata->nlinks = 1;
577         if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
578                 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
579         if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
580                 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
581
582         pfsd->sync_low_tid = 1;
583         pfsd->sync_beg_tid = 0;
584         pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
585         pfsd->shared_uuid = Hammer_FSId;
586         pfsd->unique_uuid = Hammer_FSId;
587         pfsd->reserved01 = 0;
588         pfsd->mirror_flags = 0;
589         snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
590
591         /*
592          * Create the root of the B-Tree.  The root is a leaf node so we
593          * do not have to worry about boundary elements.
594          */
595         bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
596         bnode->count = 2;
597         bnode->type = HAMMER_BTREE_TYPE_LEAF;
598
599         elm = &bnode->elms[0];
600         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
601         elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
602         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
603         elm->leaf.base.key = 0;
604         elm->leaf.base.create_tid = create_tid;
605         elm->leaf.base.delete_tid = 0;
606         elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
607         elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
608         elm->leaf.create_ts = (u_int32_t)time(NULL);
609
610         elm->leaf.data_offset = data_off;
611         elm->leaf.data_len = sizeof(*idata);
612         elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
613
614         elm = &bnode->elms[1];
615         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
616         elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
617         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
618         elm->leaf.base.key = 0;
619         elm->leaf.base.create_tid = create_tid;
620         elm->leaf.base.delete_tid = 0;
621         elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
622         elm->leaf.base.obj_type = 0;
623         elm->leaf.create_ts = (u_int32_t)time(NULL);
624
625         elm->leaf.data_offset = pfsd_off;
626         elm->leaf.data_len = sizeof(*pfsd);
627         elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
628
629         bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
630
631         return(btree_off);
632 }
633