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 100M\n");
110 			if (UndoBufferSize < 100*1024*1024) {
111 				fprintf(stderr,
112 					"WARNING: you have specified an UNDO "
113 					"FIFO size less than 100M, 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 		exit(1);
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 then 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 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 < 50*GIG) {
255 		printf("\nWARNING: HAMMER filesystems less than 50G are "
256 			"not recommended!\n"
257 			"You may have to run 'hammer prune-everything' and "
258 			"'hammer reblock'\n"
259 			"quite often, even if using a nohistory mount.\n");
260 	}
261 	flush_all_volumes();
262 	return(0);
263 }
264 
265 static
266 void
267 usage(void)
268 {
269 	fprintf(stderr,
270 		"newfs_hammer -L label [-b bootsize] [-m savesize] [-u undosize] "
271 			"special ...\n"
272 	);
273 	exit(1);
274 }
275 
276 /*
277  * Convert the size in bytes to a human readable string.
278  */
279 static
280 const char *
281 sizetostr(off_t size)
282 {
283 	static char buf[32];
284 
285 	if (size < 1024 / 2) {
286 		snprintf(buf, sizeof(buf), "%6.2f", (double)size);
287 	} else if (size < 1024 * 1024 / 2) {
288 		snprintf(buf, sizeof(buf), "%6.2fKB",
289 			(double)size / 1024);
290 	} else if (size < 1024 * 1024 * 1024LL / 2) {
291 		snprintf(buf, sizeof(buf), "%6.2fMB",
292 			(double)size / (1024 * 1024));
293 	} else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
294 		snprintf(buf, sizeof(buf), "%6.2fGB",
295 			(double)size / (1024 * 1024 * 1024LL));
296 	} else {
297 		snprintf(buf, sizeof(buf), "%6.2fTB",
298 			(double)size / (1024 * 1024 * 1024LL * 1024LL));
299 	}
300 	return(buf);
301 }
302 
303 /*
304  * Convert a string to a 64 bit signed integer with various requirements.
305  */
306 static int64_t
307 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
308 {
309 	int64_t val;
310 	char *ptr;
311 
312 	val = strtoll(str, &ptr, 0);
313 	switch(*ptr) {
314 	case 't':
315 	case 'T':
316 		val *= 1024;
317 		/* fall through */
318 	case 'g':
319 	case 'G':
320 		val *= 1024;
321 		/* fall through */
322 	case 'm':
323 	case 'M':
324 		val *= 1024;
325 		/* fall through */
326 	case 'k':
327 	case 'K':
328 		val *= 1024;
329 		break;
330 	default:
331 		errx(1, "Unknown suffix in number '%s'\n", str);
332 		/* not reached */
333 	}
334 	if (ptr[1]) {
335 		errx(1, "Unknown suffix in number '%s'\n", str);
336 		/* not reached */
337 	}
338 	if (val < minval) {
339 		errx(1, "Value too small: %s, min is %s\n",
340 		     str, sizetostr(minval));
341 		/* not reached */
342 	}
343 	if (val > maxval) {
344 		errx(1, "Value too large: %s, max is %s\n",
345 		     str, sizetostr(maxval));
346 		/* not reached */
347 	}
348 	if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
349 		errx(1, "Value not power of 2: %s\n", str);
350 		/* not reached */
351 	}
352 	if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
353 		errx(1, "Value not an integral multiple of %dK: %s",
354 		     HAMMER_BUFSIZE / 1024, str);
355 		/* not reached */
356 	}
357 	return(val);
358 }
359 
360 /*
361  * Generate a transaction id.  Transaction ids are no longer time-based.
362  * Put the nail in the coffin by not making the first one time-based.
363  *
364  * We could start at 1 here but start at 2^32 to reserve a small domain for
365  * possible future use.
366  */
367 static hammer_tid_t
368 createtid(void)
369 {
370 	static hammer_tid_t lasttid;
371 
372 	if (lasttid == 0)
373 		lasttid = 0x0000000100000000ULL;
374 	return(lasttid++);
375 }
376 
377 static u_int64_t
378 nowtime(void)
379 {
380 	struct timeval tv;
381 	u_int64_t xtime;
382 
383 	gettimeofday(&tv, NULL);
384 	xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
385 	return(xtime);
386 }
387 
388 /*
389  * Check basic volume characteristics.  HAMMER filesystems use a minimum
390  * of a 16KB filesystem buffer size.
391  */
392 static
393 void
394 check_volume(struct volume_info *vol)
395 {
396 	struct partinfo pinfo;
397 	struct stat st;
398 
399 	/*
400 	 * Get basic information about the volume
401 	 */
402 	vol->fd = open(vol->name, O_RDWR);
403 	if (vol->fd < 0)
404 		err(1, "Unable to open %s R+W", vol->name);
405 	if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
406 		/*
407 		 * Allow the formatting of regular files as HAMMER volumes
408 		 */
409 		if (fstat(vol->fd, &st) < 0)
410 			err(1, "Unable to stat %s", vol->name);
411 		vol->size = st.st_size;
412 		vol->type = "REGFILE";
413 	} else {
414 		/*
415 		 * When formatting a block device as a HAMMER volume the
416 		 * sector size must be compatible.  HAMMER uses 16384 byte
417 		 * filesystem buffers.
418 		 */
419 		if (pinfo.reserved_blocks) {
420 			errx(1, "HAMMER cannot be placed in a partition "
421 				"which overlaps the disklabel or MBR");
422 		}
423 		if (pinfo.media_blksize > 16384 ||
424 		    16384 % pinfo.media_blksize) {
425 			errx(1, "A media sector size of %d is not supported",
426 			     pinfo.media_blksize);
427 		}
428 
429 		vol->size = pinfo.media_size;
430 		vol->type = "DEVICE";
431 	}
432 	printf("Volume %d %s %-15s size %s\n",
433 	       vol->vol_no, vol->type, vol->name,
434 	       sizetostr(vol->size));
435 
436 	/*
437 	 * Reserve space for (future) header junk, setup our poor-man's
438 	 * bigblock allocator.
439 	 */
440 	vol->vol_alloc = HAMMER_BUFSIZE * 16;
441 }
442 
443 /*
444  * Format a HAMMER volume.  Cluster 0 will be initially placed in volume 0.
445  */
446 static
447 void
448 format_volume(struct volume_info *vol, int nvols, const char *label,
449 	      off_t total_size __unused)
450 {
451 	struct volume_info *root_vol;
452 	struct hammer_volume_ondisk *ondisk;
453 	int64_t freeblks;
454 	int64_t freebytes;
455 	int i;
456 
457 	/*
458 	 * Initialize basic information in the on-disk volume structure.
459 	 */
460 	ondisk = vol->ondisk;
461 
462 	ondisk->vol_fsid = Hammer_FSId;
463 	ondisk->vol_fstype = Hammer_FSType;
464 	snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
465 	ondisk->vol_no = vol->vol_no;
466 	ondisk->vol_count = nvols;
467 	ondisk->vol_version = HammerVersion;
468 
469 	ondisk->vol_bot_beg = vol->vol_alloc;
470 	vol->vol_alloc += BootAreaSize;
471 	ondisk->vol_mem_beg = vol->vol_alloc;
472 	vol->vol_alloc += MemAreaSize;
473 
474 	/*
475 	 * The remaining area is the zone 2 buffer allocation area.  These
476 	 * buffers
477 	 */
478 	ondisk->vol_buf_beg = vol->vol_alloc;
479 	ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
480 
481 	if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
482 		errx(1, "volume %d %s is too small to hold the volume header",
483 		     vol->vol_no, vol->name);
484 	}
485 
486 	ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
487 			      HAMMER_BUFSIZE;
488 	ondisk->vol_blocksize = HAMMER_BUFSIZE;
489 
490 	ondisk->vol_rootvol = RootVolNo;
491 	ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
492 
493 	vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
494 	vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, (ondisk->vol_buf_end - ondisk->vol_buf_beg) & ~HAMMER_LARGEBLOCK_MASK64);
495 
496 	/*
497 	 * Format the root volume.
498 	 */
499 	if (vol->vol_no == RootVolNo) {
500 		/*
501 		 * Starting TID
502 		 */
503 		ondisk->vol0_next_tid = createtid();
504 
505 		format_freemap(vol,
506 			&ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
507 
508 		freeblks = initialize_freemap(vol);
509 		ondisk->vol0_stat_freebigblocks = freeblks;
510 
511 		freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64;
512 		if (freebytes < 1*GIG && ForceOpt == 0) {
513 			errx(1, "Cannot create a HAMMER filesystem less than "
514 				"1GB unless you use -f.  HAMMER filesystems\n"
515 				"less than 50G are not recommended\n");
516 		}
517 
518 		for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
519 			format_blockmap(&ondisk->vol0_blockmap[i],
520 					HAMMER_ZONE_ENCODE(i, 0));
521 		}
522 		format_undomap(ondisk);
523 
524 		ondisk->vol0_btree_root = format_root(label);
525 		++ondisk->vol0_stat_inodes;	/* root inode */
526 	} else {
527 		freeblks = initialize_freemap(vol);
528 		root_vol = get_volume(RootVolNo);
529 		root_vol->cache.modified = 1;
530 		root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
531 		root_vol->ondisk->vol0_stat_bigblocks += freeblks;
532 		rel_volume(root_vol);
533 	}
534 }
535 
536 /*
537  * Format the root directory.
538  */
539 static
540 hammer_off_t
541 format_root(const char *label)
542 {
543 	hammer_off_t btree_off;
544 	hammer_off_t pfsd_off;
545 	hammer_off_t data_off;
546 	hammer_tid_t create_tid;
547 	hammer_node_ondisk_t bnode;
548 	struct hammer_inode_data *idata;
549 	hammer_pseudofs_data_t pfsd;
550 	struct buffer_info *data_buffer1 = NULL;
551 	struct buffer_info *data_buffer2 = NULL;
552 	hammer_btree_elm_t elm;
553 	u_int64_t xtime;
554 
555 	bnode = alloc_btree_element(&btree_off);
556 	idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
557 	pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
558 	create_tid = createtid();
559 	xtime = nowtime();
560 
561 	/*
562 	 * Populate the inode data and inode record for the root directory.
563 	 */
564 	idata->version = HAMMER_INODE_DATA_VERSION;
565 	idata->mode = 0755;
566 	idata->ctime = xtime;
567 	idata->mtime = xtime;
568 	idata->atime = xtime;
569 	idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
570 	idata->size = 0;
571 	idata->nlinks = 1;
572 	if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
573 		idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
574 
575 	pfsd->sync_low_tid = 1;
576 	pfsd->sync_beg_tid = 0;
577 	pfsd->sync_end_tid = 0;	/* overriden by vol0_next_tid on pfs0 */
578 	pfsd->shared_uuid = Hammer_FSId;
579 	pfsd->unique_uuid = Hammer_FSId;
580 	pfsd->reserved01 = 0;
581 	pfsd->mirror_flags = 0;
582 	snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
583 
584 	/*
585 	 * Create the root of the B-Tree.  The root is a leaf node so we
586 	 * do not have to worry about boundary elements.
587 	 */
588 	bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
589 	bnode->count = 2;
590 	bnode->type = HAMMER_BTREE_TYPE_LEAF;
591 
592 	elm = &bnode->elms[0];
593 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
594 	elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
595 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
596 	elm->leaf.base.key = 0;
597 	elm->leaf.base.create_tid = create_tid;
598 	elm->leaf.base.delete_tid = 0;
599 	elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
600 	elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
601 	elm->leaf.create_ts = (u_int32_t)time(NULL);
602 
603 	elm->leaf.data_offset = data_off;
604 	elm->leaf.data_len = sizeof(*idata);
605 	elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
606 
607 	elm = &bnode->elms[1];
608 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
609 	elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
610 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
611 	elm->leaf.base.key = 0;
612 	elm->leaf.base.create_tid = create_tid;
613 	elm->leaf.base.delete_tid = 0;
614 	elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
615 	elm->leaf.base.obj_type = 0;
616 	elm->leaf.create_ts = (u_int32_t)time(NULL);
617 
618 	elm->leaf.data_offset = pfsd_off;
619 	elm->leaf.data_len = sizeof(*pfsd);
620 	elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
621 
622 	bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
623 
624 	return(btree_off);
625 }
626 
627