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 			off_t total_size);
45 static hammer_off_t format_root(const char *label);
46 static u_int64_t nowtime(void);
47 static void usage(void);
48 
49 static int ForceOpt = 0;
50 static int HammerVersion = -1;
51 
52 #define GIG	(1024LL*1024*1024)
53 
54 int
55 main(int ac, char **av)
56 {
57 	u_int32_t status;
58 	off_t total;
59 	off_t avg_vol_size;
60 	int ch;
61 	int i;
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 	NumVolumes = ac;
175 	RootVolNo = 0;
176 
177 	if (NumVolumes == 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 (NumVolumes > 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 < NumVolumes; ++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 / NumVolumes;
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), NumVolumes, label, total);
250 	for (i = 0; i < NumVolumes; ++i) {
251 		if (i != RootVolNo)
252 			format_volume(get_volume(i), NumVolumes, label, total);
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 		NumVolumes, (NumVolumes == 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 u_int64_t
412 nowtime(void)
413 {
414 	struct timeval tv;
415 	u_int64_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 	      off_t total_size __unused)
455 {
456 	struct volume_info *root_vol;
457 	struct hammer_volume_ondisk *ondisk;
458 	int64_t freeblks;
459 	int64_t freebytes;
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 	ondisk->vol_bot_beg = vol->vol_alloc;
475 	vol->vol_alloc += BootAreaSize;
476 	ondisk->vol_mem_beg = vol->vol_alloc;
477 	vol->vol_alloc += MemAreaSize;
478 
479 	/*
480 	 * The remaining area is the zone 2 buffer allocation area.
481 	 */
482 	ondisk->vol_buf_beg = vol->vol_alloc;
483 	ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
484 
485 	if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
486 		errx(1, "volume %d %s is too small to hold the volume header",
487 		     vol->vol_no, vol->name);
488 	}
489 
490 	ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
491 			      HAMMER_BUFSIZE;
492 	ondisk->vol_blocksize = HAMMER_BUFSIZE;
493 
494 	ondisk->vol_rootvol = RootVolNo;
495 	ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
496 
497 	vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
498 	vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
499 				(ondisk->vol_buf_end - ondisk->vol_buf_beg) &
500 				~HAMMER_BIGBLOCK_MASK64);
501 
502 	/*
503 	 * Format the root volume.
504 	 */
505 	if (vol->vol_no == RootVolNo) {
506 		/*
507 		 * Check freemap counts before formatting
508 		 */
509 		freeblks = count_freemap(vol);
510 		freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
511 		if (freebytes < 10*GIG && ForceOpt == 0) {
512 			errx(1, "Cannot create a HAMMER filesystem less than 10GB "
513 				"unless you use -f\n(for the size of Volume %d).  "
514 				"HAMMER filesystems less than 50GB are not "
515 				"recommended.\n", RootVolNo);
516 		}
517 
518 		/*
519 		 * Starting TID
520 		 */
521 		ondisk->vol0_next_tid = createtid();
522 
523 		/*
524 		 * Format freemap.  vol0_stat_freebigblocks is
525 		 * the number of big-blocks available for anything
526 		 * other than freemap zone at this point.
527 		 */
528 		format_freemap(vol);
529 		assert(ondisk->vol0_stat_freebigblocks == 0);
530 		ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
531 
532 		/*
533 		 * Format zones that are mapped to zone-2.
534 		 */
535 		for (i = HAMMER_ZONE2_MAPPED_INDEX; i < HAMMER_MAX_ZONES; ++i) {
536 			format_blockmap(&ondisk->vol0_blockmap[i], i, 0);
537 		}
538 
539 		/*
540 		 * Format undo zone.  Formatting decrements
541 		 * vol0_stat_freebigblocks whenever a new big-block
542 		 * is allocated for undo zone.
543 		 */
544 		format_undomap(vol);
545 		assert(ondisk->vol0_stat_bigblocks == 0);
546 		ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
547 
548 		/*
549 		 * Format the root directory.  Formatting decrements
550 		 * vol0_stat_freebigblocks whenever a new big-block
551 		 * is allocated for required zones.
552 		 */
553 		ondisk->vol0_btree_root = format_root(label);
554 		++ondisk->vol0_stat_inodes;	/* root inode */
555 
556 		rel_volume(vol);
557 	} else {
558 		freeblks = initialize_freemap(vol);
559 		root_vol = get_volume(RootVolNo);
560 		root_vol->cache.modified = 1;
561 		root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
562 		root_vol->ondisk->vol0_stat_bigblocks += freeblks;
563 		rel_volume(root_vol);
564 	}
565 }
566 
567 /*
568  * Format the root directory.
569  */
570 static
571 hammer_off_t
572 format_root(const char *label)
573 {
574 	hammer_off_t btree_off;
575 	hammer_off_t pfsd_off;
576 	hammer_off_t data_off;
577 	hammer_tid_t create_tid;
578 	hammer_node_ondisk_t bnode;
579 	struct hammer_inode_data *idata;
580 	hammer_pseudofs_data_t pfsd;
581 	struct buffer_info *data_buffer0 = NULL;
582 	struct buffer_info *data_buffer1 = NULL;
583 	struct buffer_info *data_buffer2 = NULL;
584 	hammer_btree_elm_t elm;
585 	u_int64_t xtime;
586 
587 	/*
588 	 * Allocate zero-filled root btree node, inode and pfs
589 	 */
590 	bnode = alloc_btree_element(&btree_off, &data_buffer0);
591 	idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
592 	pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
593 	create_tid = createtid();
594 	xtime = nowtime();
595 
596 	/*
597 	 * Populate the inode data and inode record for the root directory.
598 	 */
599 	idata->version = HAMMER_INODE_DATA_VERSION;
600 	idata->mode = 0755;
601 	idata->ctime = xtime;
602 	idata->mtime = xtime;
603 	idata->atime = xtime;
604 	idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
605 	idata->size = 0;
606 	idata->nlinks = 1;
607 	if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
608 		idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
609 	if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
610 		idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
611 
612 	pfsd->sync_low_tid = 1;
613 	pfsd->sync_beg_tid = 0;
614 	pfsd->sync_end_tid = 0;	/* overriden by vol0_next_tid on pfs0 */
615 	pfsd->shared_uuid = Hammer_FSId;
616 	pfsd->unique_uuid = Hammer_FSId;
617 	pfsd->reserved01 = 0;
618 	pfsd->mirror_flags = 0;
619 	snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
620 
621 	/*
622 	 * Create the root of the B-Tree.  The root is a leaf node so we
623 	 * do not have to worry about boundary elements.
624 	 */
625 	bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
626 	bnode->count = 2;
627 	bnode->type = HAMMER_BTREE_TYPE_LEAF;
628 
629 	elm = &bnode->elms[0];
630 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
631 	elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
632 				      HAMMER_LOCALIZE_INODE;
633 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
634 	elm->leaf.base.key = 0;
635 	elm->leaf.base.create_tid = create_tid;
636 	elm->leaf.base.delete_tid = 0;
637 	elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
638 	elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
639 	elm->leaf.create_ts = (u_int32_t)time(NULL);
640 
641 	elm->leaf.data_offset = data_off;
642 	elm->leaf.data_len = sizeof(*idata);
643 	elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
644 
645 	elm = &bnode->elms[1];
646 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
647 	elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
648 				      HAMMER_LOCALIZE_MISC;
649 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
650 	elm->leaf.base.key = 0;
651 	elm->leaf.base.create_tid = create_tid;
652 	elm->leaf.base.delete_tid = 0;
653 	elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
654 	elm->leaf.base.obj_type = 0;
655 	elm->leaf.create_ts = (u_int32_t)time(NULL);
656 
657 	elm->leaf.data_offset = pfsd_off;
658 	elm->leaf.data_len = sizeof(*pfsd);
659 	elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
660 
661 	bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
662 
663 	return(btree_off);
664 }
665