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