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