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