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 		printf("Volume %d %s %-15s size %s\n",
206 			vol->vol_no, vol->type, vol->name,
207 			sizetostr(vol->size));
208 
209 		if (eflag) {
210 			int res = trim_volume(vol);
211 			if (res == -1 || (res == 1 && ForceOpt == 0))
212 				exit(1);
213 		}
214 		total += vol->size;
215 	}
216 
217 	/*
218 	 * Calculate defaults for the boot and memory area sizes.
219 	 */
220 	avg_vol_size = total / nvols;
221 	BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
222 	MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size);
223 
224 	/*
225 	 * Format the volumes.  Format the root volume first so we can
226 	 * bootstrap the freemap.
227 	 */
228 	format_volume(get_root_volume(), nvols, label);
229 	for (i = 0; i < nvols; ++i) {
230 		if (i != HAMMER_ROOT_VOLNO)
231 			format_volume(get_volume(i), nvols, label);
232 	}
233 
234 	/*
235 	 * Print information stored in the root volume header.
236 	 */
237 	vol = get_root_volume();
238 	uuid_to_string(&Hammer_FSId, &fsidstr, &status);
239 
240 	printf("---------------------------------------------\n");
241 	printf("%d volume%s total size %s version %d\n",
242 		nvols, (nvols == 1 ? "" : "s"),
243 		sizetostr(total), HammerVersion);
244 	printf("root-volume:         %s\n", vol->name);
245 	printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
246 	printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
247 	printf("undo-buffer-size:    %s\n", sizetostr(UndoBufferSize));
248 	printf("total-pre-allocated: %s\n",
249 		sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
250 	printf("fsid:                %s\n", fsidstr);
251 	printf("\n");
252 	printf("NOTE: Please remember that you may have to manually set up a\n"
253 		"cron(8) job to prune and reblock the filesystem regularly.\n"
254 		"By default, the system automatically runs 'hammer cleanup'\n"
255 		"on a nightly basis.  The periodic.conf(5) variable\n"
256 		"'daily_clean_hammer_enable' can be unset to disable this.\n"
257 		"Also see 'man hammer' and 'man HAMMER' for more information.\n");
258 	if (total < 10*GIG) {
259 		printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
260 		       "really should not\n"
261 		       "try to format a HAMMER filesystem this small.\n");
262 	}
263 	if (total < 50*GIG) {
264 		printf("\nWARNING: HAMMER filesystems less than 50GB are "
265 			"not recommended!\n"
266 			"You may have to run 'hammer prune-everything' and "
267 			"'hammer reblock'\n"
268 			"quite often, even if using a nohistory mount.\n");
269 	}
270 	flush_all_volumes();
271 	return(0);
272 }
273 
274 static
275 void
276 usage(int exit_code)
277 {
278 	fprintf(stderr,
279 		"usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n"
280 		"                    [-C cachesize[:readahead]] [-V version] special ...\n"
281 	);
282 	exit(exit_code);
283 }
284 
285 /*
286  * Convert a string to a 64 bit signed integer with various requirements.
287  */
288 static int64_t
289 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
290 {
291 	int64_t val;
292 	char *ptr;
293 
294 	val = strtoll(str, &ptr, 0);
295 	switch(*ptr) {
296 	case 't':
297 	case 'T':
298 		val *= 1024;
299 		/* fall through */
300 	case 'g':
301 	case 'G':
302 		val *= 1024;
303 		/* fall through */
304 	case 'm':
305 	case 'M':
306 		val *= 1024;
307 		/* fall through */
308 	case 'k':
309 	case 'K':
310 		val *= 1024;
311 		break;
312 	default:
313 		errx(1, "Unknown suffix in number '%s'\n", str);
314 		/* not reached */
315 	}
316 	if (ptr[1]) {
317 		errx(1, "Unknown suffix in number '%s'\n", str);
318 		/* not reached */
319 	}
320 	if (val < minval) {
321 		errx(1, "Value too small: %s, min is %s\n",
322 		     str, sizetostr(minval));
323 		/* not reached */
324 	}
325 	if (val > maxval) {
326 		errx(1, "Value too large: %s, max is %s\n",
327 		     str, sizetostr(maxval));
328 		/* not reached */
329 	}
330 	if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
331 		errx(1, "Value not power of 2: %s\n", str);
332 		/* not reached */
333 	}
334 	if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
335 		errx(1, "Value not an integral multiple of %dK: %s",
336 		     HAMMER_BUFSIZE / 1024, str);
337 		/* not reached */
338 	}
339 	return(val);
340 }
341 
342 /*
343  * Generate a transaction id.  Transaction ids are no longer time-based.
344  * Put the nail in the coffin by not making the first one time-based.
345  *
346  * We could start at 1 here but start at 2^32 to reserve a small domain for
347  * possible future use.
348  */
349 static hammer_tid_t
350 createtid(void)
351 {
352 	static hammer_tid_t lasttid;
353 
354 	if (lasttid == 0)
355 		lasttid = 0x0000000100000000ULL;
356 	return(lasttid++);
357 }
358 
359 static uint64_t
360 nowtime(void)
361 {
362 	struct timeval tv;
363 	uint64_t xtime;
364 
365 	gettimeofday(&tv, NULL);
366 	xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
367 	return(xtime);
368 }
369 
370 /*
371  * TRIM the volume, but only if the backing store is a DEVICE
372  */
373 static
374 int
375 trim_volume(struct volume_info *vol)
376 {
377 	size_t olen;
378 	char *dev_name, *p;
379 	char sysctl_name[64];
380 	int trim_enabled;
381 	off_t ioarg[2];
382 
383 	if (strcmp(vol->type, "DEVICE")) {
384 		fprintf(stderr, "Cannot TRIM regular file %s\n", vol->name);
385 		return(1);
386 	}
387 	if (strncmp(vol->name, "/dev/da", 7)) {
388 		fprintf(stderr, "%s does not support the TRIM command\n",
389 			vol->name);
390 		return(1);
391 	}
392 
393 	/* Extract a number from /dev/da?s? */
394 	dev_name = strdup(vol->name);
395 	p = strtok(dev_name + strlen("/dev/da"), "s");
396 	sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p);
397 	free(dev_name);
398 
399 	trim_enabled = 0;
400 	olen = sizeof(trim_enabled);
401 
402 	if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) {
403 		fprintf(stderr, "%s (%s) does not support the TRIM command\n",
404 			vol->name, sysctl_name);
405 		return(1);
406 	}
407 	if (!trim_enabled) {
408 		fprintf(stderr, "Erase device option selected, but sysctl (%s) "
409 			"is not enabled\n", sysctl_name);
410 		return(1);
411 	}
412 
413 	ioarg[0] = vol->device_offset;
414 	ioarg[1] = vol->size;
415 
416 	printf("Trimming %s %s, sectors %llu-%llu\n",
417 		vol->type, vol->name,
418 		(unsigned long long)ioarg[0] / 512,
419 		(unsigned long long)ioarg[1] / 512);
420 
421 	if (ioctl(vol->fd, IOCTLTRIM, ioarg) == -1) {
422 		fprintf(stderr, "Device trim failed\n");
423 		return(-1);
424 	}
425 
426 	return(0);
427 }
428 
429 /*
430  * Format a HAMMER volume.
431  */
432 static
433 void
434 format_volume(struct volume_info *vol, int nvols, const char *label)
435 {
436 	struct volume_info *root_vol;
437 	hammer_volume_ondisk_t ondisk;
438 	int64_t freeblks;
439 	int64_t freebytes;
440 	int64_t vol_buf_size;
441 	hammer_off_t vol_alloc;
442 	int i;
443 
444 	/*
445 	 * Initialize basic information in the on-disk volume structure.
446 	 */
447 	ondisk = vol->ondisk;
448 
449 	ondisk->vol_fsid = Hammer_FSId;
450 	ondisk->vol_fstype = Hammer_FSType;
451 	snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
452 	ondisk->vol_no = vol->vol_no;
453 	ondisk->vol_count = nvols;
454 	ondisk->vol_version = HammerVersion;
455 
456 	/*
457 	 * Reserve space for (future) header junk, setup our poor-man's
458 	 * big-block allocator.
459 	 */
460 	vol_alloc = HAMMER_VOL_ALLOC;
461 
462 	ondisk->vol_bot_beg = vol_alloc;
463 	vol_alloc += BootAreaSize;
464 	ondisk->vol_mem_beg = vol_alloc;
465 	vol_alloc += MemAreaSize;
466 
467 	/*
468 	 * The remaining area is the zone 2 buffer allocation area.
469 	 */
470 	ondisk->vol_buf_beg = vol_alloc;
471 	ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
472 	vol_buf_size = HAMMER_VOL_BUF_SIZE(ondisk);
473 
474 	if (vol_buf_size < 0) {
475 		errx(1, "volume %d %s is too small to hold the volume header",
476 		     vol->vol_no, vol->name);
477 	}
478 
479 	if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) {
480 		errx(1, "volume %d %s is too large", vol->vol_no, vol->name);
481 	}
482 
483 	ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
484 	ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
485 
486 	vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
487 	vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
488 				vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
489 
490 	/*
491 	 * Format the root volume.
492 	 */
493 	if (vol->vol_no == HAMMER_ROOT_VOLNO) {
494 		/*
495 		 * Check freemap counts before formatting
496 		 */
497 		freeblks = count_freemap(vol);
498 		freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
499 		if (freebytes < 10*GIG && ForceOpt == 0) {
500 			errx(1, "Cannot create a HAMMER filesystem less than 10GB "
501 				"unless you use -f\n(for the size of Volume %d).  "
502 				"HAMMER filesystems less than 50GB are not "
503 				"recommended.\n", HAMMER_ROOT_VOLNO);
504 		}
505 
506 		/*
507 		 * Starting TID
508 		 */
509 		ondisk->vol0_next_tid = createtid();
510 
511 		/*
512 		 * Format freemap.  vol0_stat_freebigblocks is
513 		 * the number of big-blocks available for anything
514 		 * other than freemap zone at this point.
515 		 */
516 		format_freemap(vol);
517 		assert(ondisk->vol0_stat_freebigblocks == 0);
518 		ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
519 
520 		/*
521 		 * Format zones that are mapped to zone-2.
522 		 */
523 		for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
524 			if (hammer_is_zone2_mapped_index(i))
525 				format_blockmap(vol, i, 0);
526 		}
527 
528 		/*
529 		 * Format undo zone.  Formatting decrements
530 		 * vol0_stat_freebigblocks whenever a new big-block
531 		 * is allocated for undo zone.
532 		 */
533 		format_undomap(vol, &UndoBufferSize);
534 		assert(ondisk->vol0_stat_bigblocks == 0);
535 		ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
536 
537 		/*
538 		 * Format the root directory.  Formatting decrements
539 		 * vol0_stat_freebigblocks whenever a new big-block
540 		 * is allocated for required zones.
541 		 */
542 		ondisk->vol0_btree_root = format_root_directory(label);
543 		++ondisk->vol0_stat_inodes;	/* root inode */
544 	} else {
545 		freeblks = initialize_freemap(vol);
546 		root_vol = get_root_volume();
547 		root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
548 		root_vol->ondisk->vol0_stat_bigblocks += freeblks;
549 	}
550 }
551 
552 /*
553  * Format the root directory.
554  */
555 static
556 hammer_off_t
557 format_root_directory(const char *label)
558 {
559 	hammer_off_t btree_off;
560 	hammer_off_t idata_off;
561 	hammer_off_t pfsd_off;
562 	hammer_tid_t create_tid;
563 	hammer_node_ondisk_t bnode;
564 	hammer_inode_data_t idata;
565 	hammer_pseudofs_data_t pfsd;
566 	struct buffer_info *data_buffer0 = NULL;
567 	struct buffer_info *data_buffer1 = NULL;
568 	struct buffer_info *data_buffer2 = NULL;
569 	hammer_btree_elm_t elm;
570 	uint64_t xtime;
571 
572 	/*
573 	 * Allocate zero-filled root btree node, inode and pfs
574 	 */
575 	bnode = alloc_btree_element(&btree_off, &data_buffer0);
576 	idata = alloc_meta_element(&idata_off, sizeof(*idata), &data_buffer1);
577 	pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
578 	create_tid = createtid();
579 	xtime = nowtime();
580 
581 	/*
582 	 * Populate the inode data and inode record for the root directory.
583 	 */
584 	idata->version = HAMMER_INODE_DATA_VERSION;
585 	idata->mode = 0755;
586 	idata->ctime = xtime;
587 	idata->mtime = xtime;
588 	idata->atime = xtime;
589 	idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
590 	idata->size = 0;
591 	idata->nlinks = 1;
592 	if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
593 		idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
594 	if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
595 		idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
596 
597 	/*
598 	 * Populate the PFS data for the root PFS.
599 	 */
600 	pfsd->sync_low_tid = 1;
601 	pfsd->sync_beg_tid = 0;
602 	pfsd->sync_end_tid = 0;	/* overriden by vol0_next_tid on pfs0 */
603 	pfsd->shared_uuid = Hammer_FSId;
604 	pfsd->unique_uuid = Hammer_FSId;
605 	pfsd->mirror_flags = 0;
606 	snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
607 
608 	/*
609 	 * Create the root of the B-Tree.  The root is a leaf node so we
610 	 * do not have to worry about boundary elements.
611 	 */
612 	bnode->parent = 0;  /* no parent */
613 	bnode->count = 2;
614 	bnode->type = HAMMER_BTREE_TYPE_LEAF;
615 	bnode->mirror_tid = 0;
616 
617 	/*
618 	 * Create the first node element for the inode.
619 	 */
620 	elm = &bnode->elms[0];
621 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
622 	elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
623 				      HAMMER_LOCALIZE_INODE;
624 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
625 	elm->leaf.base.key = 0;
626 	elm->leaf.base.create_tid = create_tid;
627 	elm->leaf.base.delete_tid = 0;
628 	elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
629 	elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
630 	elm->leaf.create_ts = (uint32_t)time(NULL);
631 
632 	elm->leaf.data_offset = idata_off;
633 	elm->leaf.data_len = sizeof(*idata);
634 	elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
635 
636 	/*
637 	 * Create the second node element for the PFS data.
638 	 * This is supposed to be a record part of the root ip (inode),
639 	 * so it should have the same obj_type value as above.
640 	 */
641 	elm = &bnode->elms[1];
642 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
643 	elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
644 				      HAMMER_LOCALIZE_MISC;
645 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
646 	elm->leaf.base.key = 0;
647 	elm->leaf.base.create_tid = create_tid;
648 	elm->leaf.base.delete_tid = 0;
649 	elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
650 	elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
651 	elm->leaf.create_ts = (uint32_t)time(NULL);
652 
653 	elm->leaf.data_offset = pfsd_off;
654 	elm->leaf.data_len = sizeof(*pfsd);
655 	elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
656 
657 	bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
658 
659 	rel_buffer(data_buffer0);
660 	rel_buffer(data_buffer1);
661 	rel_buffer(data_buffer2);
662 
663 	return(btree_off);
664 }
665