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