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