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