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