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