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 			if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) < 0) {
223 				printf("%s %s (%s) does not support the TRIM "
224 				    "command\n",
225 				    vol->type, vol->name, sysctl_name);
226 				usage();
227 			}
228 			if(!trim_enabled) {
229 				printf("Erase device option selected, but "
230 				    "sysctl (%s) is not enabled\n", sysctl_name);
231 				usage();
232 
233 			}
234 			trim_volume(vol);
235 		}
236 		total += vol->size;
237 	}
238 
239 	/*
240 	 * Calculate defaults for the boot and memory area sizes.
241 	 */
242 	avg_vol_size = total / nvols;
243 	BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
244 	MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size);
245 
246 	/*
247 	 * Format the volumes.  Format the root volume first so we can
248 	 * bootstrap the freemap.
249 	 */
250 	format_volume(get_root_volume(), nvols, label);
251 	for (i = 0; i < nvols; ++i) {
252 		if (i != HAMMER_ROOT_VOLNO)
253 			format_volume(get_volume(i), nvols, label);
254 	}
255 
256 	/*
257 	 * Print information stored in the root volume header.
258 	 */
259 	vol = get_root_volume();
260 	uuid_to_string(&Hammer_FSId, &fsidstr, &status);
261 
262 	printf("---------------------------------------------\n");
263 	printf("%d volume%s total size %s version %d\n",
264 		nvols, (nvols == 1 ? "" : "s"),
265 		sizetostr(total), HammerVersion);
266 	printf("root-volume:         %s\n", vol->name);
267 	printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
268 	printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
269 	printf("undo-buffer-size:    %s\n", sizetostr(UndoBufferSize));
270 	printf("total-pre-allocated: %s\n",
271 		sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
272 	printf("fsid:                %s\n", fsidstr);
273 	printf("\n");
274 	printf("NOTE: Please remember that you may have to manually set up a\n"
275 		"cron(8) job to prune and reblock the filesystem regularly.\n"
276 		"By default, the system automatically runs 'hammer cleanup'\n"
277 		"on a nightly basis.  The periodic.conf(5) variable\n"
278 		"'daily_clean_hammer_enable' can be unset to disable this.\n"
279 		"Also see 'man hammer' and 'man HAMMER' for more information.\n");
280 	if (total < 10*GIG) {
281 		printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
282 		       "really should not\n"
283 		       "try to format a HAMMER filesystem this small.\n");
284 	}
285 	if (total < 50*GIG) {
286 		printf("\nWARNING: HAMMER filesystems less than 50GB are "
287 			"not recommended!\n"
288 			"You may have to run 'hammer prune-everything' and "
289 			"'hammer reblock'\n"
290 			"quite often, even if using a nohistory mount.\n");
291 	}
292 	flush_all_volumes();
293 	return(0);
294 }
295 
296 static
297 void
298 usage(void)
299 {
300 	fprintf(stderr,
301 		"usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
302 		"                    [-V version] special ...\n"
303 	);
304 	exit(1);
305 }
306 
307 /*
308  * Convert the size in bytes to a human readable string.
309  */
310 static
311 const char *
312 sizetostr(off_t size)
313 {
314 	static char buf[32];
315 
316 	if (size < 1024 / 2) {
317 		snprintf(buf, sizeof(buf), "%6.2f", (double)size);
318 	} else if (size < 1024 * 1024 / 2) {
319 		snprintf(buf, sizeof(buf), "%6.2fKB",
320 			(double)size / 1024);
321 	} else if (size < 1024 * 1024 * 1024LL / 2) {
322 		snprintf(buf, sizeof(buf), "%6.2fMB",
323 			(double)size / (1024 * 1024));
324 	} else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
325 		snprintf(buf, sizeof(buf), "%6.2fGB",
326 			(double)size / (1024 * 1024 * 1024LL));
327 	} else {
328 		snprintf(buf, sizeof(buf), "%6.2fTB",
329 			(double)size / (1024 * 1024 * 1024LL * 1024LL));
330 	}
331 	return(buf);
332 }
333 
334 /*
335  * Convert a string to a 64 bit signed integer with various requirements.
336  */
337 static int64_t
338 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
339 {
340 	int64_t val;
341 	char *ptr;
342 
343 	val = strtoll(str, &ptr, 0);
344 	switch(*ptr) {
345 	case 't':
346 	case 'T':
347 		val *= 1024;
348 		/* fall through */
349 	case 'g':
350 	case 'G':
351 		val *= 1024;
352 		/* fall through */
353 	case 'm':
354 	case 'M':
355 		val *= 1024;
356 		/* fall through */
357 	case 'k':
358 	case 'K':
359 		val *= 1024;
360 		break;
361 	default:
362 		errx(1, "Unknown suffix in number '%s'\n", str);
363 		/* not reached */
364 	}
365 	if (ptr[1]) {
366 		errx(1, "Unknown suffix in number '%s'\n", str);
367 		/* not reached */
368 	}
369 	if (val < minval) {
370 		errx(1, "Value too small: %s, min is %s\n",
371 		     str, sizetostr(minval));
372 		/* not reached */
373 	}
374 	if (val > maxval) {
375 		errx(1, "Value too large: %s, max is %s\n",
376 		     str, sizetostr(maxval));
377 		/* not reached */
378 	}
379 	if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
380 		errx(1, "Value not power of 2: %s\n", str);
381 		/* not reached */
382 	}
383 	if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
384 		errx(1, "Value not an integral multiple of %dK: %s",
385 		     HAMMER_BUFSIZE / 1024, str);
386 		/* not reached */
387 	}
388 	return(val);
389 }
390 
391 /*
392  * Generate a transaction id.  Transaction ids are no longer time-based.
393  * Put the nail in the coffin by not making the first one time-based.
394  *
395  * We could start at 1 here but start at 2^32 to reserve a small domain for
396  * possible future use.
397  */
398 static hammer_tid_t
399 createtid(void)
400 {
401 	static hammer_tid_t lasttid;
402 
403 	if (lasttid == 0)
404 		lasttid = 0x0000000100000000ULL;
405 	return(lasttid++);
406 }
407 
408 static uint64_t
409 nowtime(void)
410 {
411 	struct timeval tv;
412 	uint64_t xtime;
413 
414 	gettimeofday(&tv, NULL);
415 	xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
416 	return(xtime);
417 }
418 
419 /*
420  * TRIM the volume, but only if the backing store is a DEVICE
421  */
422 static
423 void
424 trim_volume(struct volume_info *vol)
425 {
426 	if (strncmp(vol->type, "DEVICE", sizeof("DEVICE")) == 0) {
427 		off_t ioarg[2];
428 
429 		/* 1MB offset to prevent destroying disk-reserved area */
430 		ioarg[0] = vol->device_offset;
431 		ioarg[1] = vol->size;
432 
433 		printf("Trimming %s %s, sectors (%llu -%llu)\n",
434 		    vol->type, vol->name,
435 		    (unsigned long long)ioarg[0]/512,
436 		    (unsigned long long)ioarg[1]/512);
437 
438 		if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
439 			printf("Device trim failed\n");
440 			usage ();
441 		}
442 	}
443 }
444 
445 /*
446  * Format a HAMMER volume.
447  */
448 static
449 void
450 format_volume(struct volume_info *vol, int nvols, const char *label)
451 {
452 	struct volume_info *root_vol;
453 	struct hammer_volume_ondisk *ondisk;
454 	int64_t freeblks;
455 	int64_t freebytes;
456 	int64_t vol_buf_size;
457 	hammer_off_t vol_alloc;
458 	int i;
459 
460 	/*
461 	 * Initialize basic information in the on-disk volume structure.
462 	 */
463 	ondisk = vol->ondisk;
464 
465 	ondisk->vol_fsid = Hammer_FSId;
466 	ondisk->vol_fstype = Hammer_FSType;
467 	snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
468 	ondisk->vol_no = vol->vol_no;
469 	ondisk->vol_count = nvols;
470 	ondisk->vol_version = HammerVersion;
471 
472 	/*
473 	 * Reserve space for (future) header junk, setup our poor-man's
474 	 * big-block allocator.
475 	 */
476 	vol_alloc = HAMMER_VOL_ALLOC;
477 
478 	ondisk->vol_bot_beg = vol_alloc;
479 	vol_alloc += BootAreaSize;
480 	ondisk->vol_mem_beg = vol_alloc;
481 	vol_alloc += MemAreaSize;
482 
483 	/*
484 	 * The remaining area is the zone 2 buffer allocation area.
485 	 */
486 	ondisk->vol_buf_beg = vol_alloc;
487 	ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
488 	vol_buf_size = ondisk->vol_buf_end - ondisk->vol_buf_beg;
489 
490 	if (vol_buf_size < 0) {
491 		errx(1, "volume %d %s is too small to hold the volume header",
492 		     vol->vol_no, vol->name);
493 	}
494 
495 	if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) {
496 		errx(1, "volume %d %s is too large", vol->vol_no, vol->name);
497 	}
498 
499 	ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
500 	ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
501 
502 	vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
503 	vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
504 				vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
505 
506 	/*
507 	 * Format the root volume.
508 	 */
509 	if (vol->vol_no == HAMMER_ROOT_VOLNO) {
510 		/*
511 		 * Check freemap counts before formatting
512 		 */
513 		freeblks = count_freemap(vol);
514 		freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
515 		if (freebytes < 10*GIG && ForceOpt == 0) {
516 			errx(1, "Cannot create a HAMMER filesystem less than 10GB "
517 				"unless you use -f\n(for the size of Volume %d).  "
518 				"HAMMER filesystems less than 50GB are not "
519 				"recommended.\n", HAMMER_ROOT_VOLNO);
520 		}
521 
522 		/*
523 		 * Starting TID
524 		 */
525 		ondisk->vol0_next_tid = createtid();
526 
527 		/*
528 		 * Format freemap.  vol0_stat_freebigblocks is
529 		 * the number of big-blocks available for anything
530 		 * other than freemap zone at this point.
531 		 */
532 		format_freemap(vol);
533 		assert(ondisk->vol0_stat_freebigblocks == 0);
534 		ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
535 
536 		/*
537 		 * Format zones that are mapped to zone-2.
538 		 */
539 		for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
540 			if (hammer_is_zone2_mapped_index(i))
541 				format_blockmap(vol, i, 0);
542 		}
543 
544 		/*
545 		 * Format undo zone.  Formatting decrements
546 		 * vol0_stat_freebigblocks whenever a new big-block
547 		 * is allocated for undo zone.
548 		 */
549 		format_undomap(vol, &UndoBufferSize);
550 		assert(ondisk->vol0_stat_bigblocks == 0);
551 		ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
552 
553 		/*
554 		 * Format the root directory.  Formatting decrements
555 		 * vol0_stat_freebigblocks whenever a new big-block
556 		 * is allocated for required zones.
557 		 */
558 		ondisk->vol0_btree_root = format_root_directory(label);
559 		++ondisk->vol0_stat_inodes;	/* root inode */
560 
561 		rel_volume(vol);
562 	} else {
563 		freeblks = initialize_freemap(vol);
564 		root_vol = get_root_volume();
565 		root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
566 		root_vol->ondisk->vol0_stat_bigblocks += freeblks;
567 		rel_volume(root_vol);
568 	}
569 }
570 
571 /*
572  * Format the root directory.
573  */
574 static
575 hammer_off_t
576 format_root_directory(const char *label)
577 {
578 	hammer_off_t btree_off;
579 	hammer_off_t pfsd_off;
580 	hammer_off_t data_off;
581 	hammer_tid_t create_tid;
582 	hammer_node_ondisk_t bnode;
583 	struct hammer_inode_data *idata;
584 	hammer_pseudofs_data_t pfsd;
585 	struct buffer_info *data_buffer0 = NULL;
586 	struct buffer_info *data_buffer1 = NULL;
587 	struct buffer_info *data_buffer2 = NULL;
588 	hammer_btree_elm_t elm;
589 	uint64_t xtime;
590 
591 	/*
592 	 * Allocate zero-filled root btree node, inode and pfs
593 	 */
594 	bnode = alloc_btree_element(&btree_off, &data_buffer0);
595 	idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
596 	pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
597 	create_tid = createtid();
598 	xtime = nowtime();
599 
600 	/*
601 	 * Populate the inode data and inode record for the root directory.
602 	 */
603 	idata->version = HAMMER_INODE_DATA_VERSION;
604 	idata->mode = 0755;
605 	idata->ctime = xtime;
606 	idata->mtime = xtime;
607 	idata->atime = xtime;
608 	idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
609 	idata->size = 0;
610 	idata->nlinks = 1;
611 	if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
612 		idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
613 	if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
614 		idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
615 
616 	/*
617 	 * Populate the PFS data for the root PFS.
618 	 */
619 	pfsd->sync_low_tid = 1;
620 	pfsd->sync_beg_tid = 0;
621 	pfsd->sync_end_tid = 0;	/* overriden by vol0_next_tid on pfs0 */
622 	pfsd->shared_uuid = Hammer_FSId;
623 	pfsd->unique_uuid = Hammer_FSId;
624 	pfsd->reserved01 = 0;
625 	pfsd->mirror_flags = 0;
626 	snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
627 
628 	/*
629 	 * Create the root of the B-Tree.  The root is a leaf node so we
630 	 * do not have to worry about boundary elements.
631 	 */
632 	bnode->count = 2;
633 	bnode->type = HAMMER_BTREE_TYPE_LEAF;
634 
635 	/*
636 	 * Create the first node element for the inode.
637 	 */
638 	elm = &bnode->elms[0];
639 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
640 	elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
641 				      HAMMER_LOCALIZE_INODE;
642 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
643 	elm->leaf.base.key = 0;
644 	elm->leaf.base.create_tid = create_tid;
645 	elm->leaf.base.delete_tid = 0;
646 	elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
647 	elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
648 	elm->leaf.create_ts = (uint32_t)time(NULL);
649 
650 	elm->leaf.data_offset = data_off;
651 	elm->leaf.data_len = sizeof(*idata);
652 	elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
653 
654 	/*
655 	 * Create the second node element for the PFS data.
656 	 */
657 	elm = &bnode->elms[1];
658 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
659 	elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
660 				      HAMMER_LOCALIZE_MISC;
661 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
662 	elm->leaf.base.key = 0;
663 	elm->leaf.base.create_tid = create_tid;
664 	elm->leaf.base.delete_tid = 0;
665 	elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
666 	elm->leaf.base.obj_type = 0;
667 	elm->leaf.create_ts = (uint32_t)time(NULL);
668 
669 	elm->leaf.data_offset = pfsd_off;
670 	elm->leaf.data_len = sizeof(*pfsd);
671 	elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
672 
673 	bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
674 
675 	rel_buffer(data_buffer0);
676 	rel_buffer(data_buffer1);
677 	rel_buffer(data_buffer2);
678 
679 	return(btree_off);
680 }
681