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 check_volume(struct volume_info *vol);
41 static void format_volume(struct volume_info *vol, int nvols,const char *label,
42 			off_t total_size);
43 static hammer_off_t format_root(const char *label);
44 static u_int64_t nowtime(void);
45 static void usage(void);
46 
47 static int ForceOpt = 0;
48 static int HammerVersion = -1;
49 static int Eflag = 0;
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, "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 special file (volume)\n");
177                 exit(1);
178         }
179 
180 	if (NumVolumes > HAMMER_MAX_VOLUMES) {
181                 fprintf(stderr,
182                         "newfs_hammer: The maximum number of volumes is %d\n", HAMMER_MAX_VOLUMES);
183 		exit(1);
184 	}
185 
186 	total = 0;
187 	for (i = 0; i < NumVolumes; ++i) {
188 		vol = setup_volume(i, av[i], 1, O_RDWR);
189 
190 		/*
191 		 * Load up information on the volume and initialize
192 		 * its remaining fields.
193 		 */
194 		check_volume(vol);
195 		if (Eflag) {
196 			char sysctl_name[64];
197 			int trim_enabled = 0;
198 			size_t olen = sizeof(trim_enabled);
199 			char *dev_name = strdup(vol->name);
200 			dev_name = strtok(dev_name + strlen("/dev/da"),"s");
201 
202 			sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled",
203 			    dev_name);
204 			errno=0;
205 			sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
206 			if(errno == ENOENT) {
207 				printf("Device:%s (%s) does not support the "
208 				    "TRIM command\n", vol->name,sysctl_name);
209 				usage();
210 			}
211 			if(!trim_enabled) {
212 				printf("Erase device option selected, but "
213 				    "sysctl (%s) is not enabled\n", sysctl_name);
214 				usage();
215 
216 			}
217 			trim_volume(vol);
218 		}
219 		total += vol->size;
220 	}
221 
222 	/*
223 	 * Calculate defaults for the boot and memory area sizes.
224 	 */
225 	if (BootAreaSize == 0) {
226 		BootAreaSize = HAMMER_BOOT_NOMBYTES;
227 		while (BootAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
228 			BootAreaSize >>= 1;
229 		if (BootAreaSize < HAMMER_BOOT_MINBYTES)
230 			BootAreaSize = 0;
231 	} else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
232 		BootAreaSize = HAMMER_BOOT_MINBYTES;
233 	}
234 	if (MemAreaSize == 0) {
235 		MemAreaSize = HAMMER_MEM_NOMBYTES;
236 		while (MemAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
237 			MemAreaSize >>= 1;
238 		if (MemAreaSize < HAMMER_MEM_MINBYTES)
239 			MemAreaSize = 0;
240 	} else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
241 		MemAreaSize = HAMMER_MEM_MINBYTES;
242 	}
243 
244 	/*
245 	 * Format the volumes.  Format the root volume first so we can
246 	 * bootstrap the freemap.
247 	 */
248 	format_volume(get_volume(RootVolNo), NumVolumes, label, total);
249 	for (i = 0; i < NumVolumes; ++i) {
250 		if (i != RootVolNo)
251 			format_volume(get_volume(i), NumVolumes, label, total);
252 	}
253 
254 	/*
255 	 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
256 	 * limit.  If we do this the filesystem does not have to allocate
257 	 * new layer2 blocks which reduces the chances of the reblocker
258 	 * having to fallback to an extremely inefficient algorithm.
259 	 */
260 	vol = get_volume(RootVolNo);
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 		NumVolumes, (NumVolumes == 1 ? "" : "s"),
267 		sizetostr(total), HammerVersion);
268 	printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
269 	printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
270 	printf("undo-buffer-size:    %s\n", sizetostr(UndoBufferSize));
271 	printf("total-pre-allocated: %s\n",
272 		sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
273 	printf("fsid:                %s\n", fsidstr);
274 	printf("\n");
275 	printf("NOTE: Please remember that you may have to manually set up a\n"
276 		"cron(8) job to prune and reblock the filesystem regularly.\n"
277 		"By default, the system automatically runs 'hammer cleanup'\n"
278 		"on a nightly basis.  The periodic.conf(5) variable\n"
279 		"'daily_clean_hammer_enable' can be unset to disable this.\n"
280 		"Also see 'man hammer' and 'man HAMMER' for more information.\n");
281 	if (total < 10*GIG) {
282 		printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
283 		       "really should not\n"
284 		       "try to format a HAMMER filesystem this small.\n");
285 	}
286 	if (total < 50*GIG) {
287 		printf("\nWARNING: HAMMER filesystems less than 50GB are "
288 			"not recommended!\n"
289 			"You may have to run 'hammer prune-everything' and "
290 			"'hammer reblock'\n"
291 			"quite often, even if using a nohistory mount.\n");
292 	}
293 	flush_all_volumes();
294 	return(0);
295 }
296 
297 static
298 void
299 usage(void)
300 {
301 	fprintf(stderr,
302 		"usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
303 		"                    [-V version] special ...\n"
304 	);
305 	exit(1);
306 }
307 
308 /*
309  * Convert the size in bytes to a human readable string.
310  */
311 static
312 const char *
313 sizetostr(off_t size)
314 {
315 	static char buf[32];
316 
317 	if (size < 1024 / 2) {
318 		snprintf(buf, sizeof(buf), "%6.2f", (double)size);
319 	} else if (size < 1024 * 1024 / 2) {
320 		snprintf(buf, sizeof(buf), "%6.2fKB",
321 			(double)size / 1024);
322 	} else if (size < 1024 * 1024 * 1024LL / 2) {
323 		snprintf(buf, sizeof(buf), "%6.2fMB",
324 			(double)size / (1024 * 1024));
325 	} else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
326 		snprintf(buf, sizeof(buf), "%6.2fGB",
327 			(double)size / (1024 * 1024 * 1024LL));
328 	} else {
329 		snprintf(buf, sizeof(buf), "%6.2fTB",
330 			(double)size / (1024 * 1024 * 1024LL * 1024LL));
331 	}
332 	return(buf);
333 }
334 
335 /*
336  * Convert a string to a 64 bit signed integer with various requirements.
337  */
338 static int64_t
339 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
340 {
341 	int64_t val;
342 	char *ptr;
343 
344 	val = strtoll(str, &ptr, 0);
345 	switch(*ptr) {
346 	case 't':
347 	case 'T':
348 		val *= 1024;
349 		/* fall through */
350 	case 'g':
351 	case 'G':
352 		val *= 1024;
353 		/* fall through */
354 	case 'm':
355 	case 'M':
356 		val *= 1024;
357 		/* fall through */
358 	case 'k':
359 	case 'K':
360 		val *= 1024;
361 		break;
362 	default:
363 		errx(1, "Unknown suffix in number '%s'\n", str);
364 		/* not reached */
365 	}
366 	if (ptr[1]) {
367 		errx(1, "Unknown suffix in number '%s'\n", str);
368 		/* not reached */
369 	}
370 	if (val < minval) {
371 		errx(1, "Value too small: %s, min is %s\n",
372 		     str, sizetostr(minval));
373 		/* not reached */
374 	}
375 	if (val > maxval) {
376 		errx(1, "Value too large: %s, max is %s\n",
377 		     str, sizetostr(maxval));
378 		/* not reached */
379 	}
380 	if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
381 		errx(1, "Value not power of 2: %s\n", str);
382 		/* not reached */
383 	}
384 	if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
385 		errx(1, "Value not an integral multiple of %dK: %s",
386 		     HAMMER_BUFSIZE / 1024, str);
387 		/* not reached */
388 	}
389 	return(val);
390 }
391 
392 /*
393  * Generate a transaction id.  Transaction ids are no longer time-based.
394  * Put the nail in the coffin by not making the first one time-based.
395  *
396  * We could start at 1 here but start at 2^32 to reserve a small domain for
397  * possible future use.
398  */
399 static hammer_tid_t
400 createtid(void)
401 {
402 	static hammer_tid_t lasttid;
403 
404 	if (lasttid == 0)
405 		lasttid = 0x0000000100000000ULL;
406 	return(lasttid++);
407 }
408 
409 static u_int64_t
410 nowtime(void)
411 {
412 	struct timeval tv;
413 	u_int64_t xtime;
414 
415 	gettimeofday(&tv, NULL);
416 	xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
417 	return(xtime);
418 }
419 
420 /*
421  * TRIM the volume, but only if the backing store is a DEVICE
422  */
423 static
424 void
425 trim_volume(struct volume_info *vol)
426 {
427 	if (strncmp(vol->type, "DEVICE", sizeof("DEVICE")) == 0) {
428 		off_t ioarg[2];
429 
430 		/* 1MB offset to prevent destroying disk-reserved area */
431 		ioarg[0] = vol->device_offset;
432 		ioarg[1] = vol->size;
433 		printf("Trimming Device:%s, sectors (%llu -%llu)\n",vol->name,
434 		    (unsigned long long)ioarg[0]/512,
435 		    (unsigned long long)ioarg[1]/512);
436 		if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
437 			printf("Device trim failed\n");
438 			usage ();
439 		}
440 	}
441 }
442 
443 /*
444  * Check basic volume characteristics.  HAMMER filesystems use a minimum
445  * of a 16KB filesystem buffer size.
446  */
447 static
448 void
449 check_volume(struct volume_info *vol)
450 {
451 	struct partinfo pinfo;
452 	struct stat st;
453 
454 	/*
455 	 * Get basic information about the volume
456 	 */
457 	vol->fd = open(vol->name, O_RDWR);
458 	if (vol->fd < 0)
459 		err(1, "Unable to open %s R+W", vol->name);
460 	if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
461 		/*
462 		 * Allow the formatting of regular files as HAMMER volumes
463 		 */
464 		if (fstat(vol->fd, &st) < 0)
465 			err(1, "Unable to stat %s", vol->name);
466 		vol->size = st.st_size;
467 		vol->type = "REGFILE";
468 
469 		if (Eflag)
470 			errx(1,"Cannot TRIM regular file %s\n", vol->name);
471 
472 	} else {
473 		/*
474 		 * When formatting a block device as a HAMMER volume the
475 		 * sector size must be compatible.  HAMMER uses 16384 byte
476 		 * filesystem buffers.
477 		 */
478 		if (pinfo.reserved_blocks) {
479 			errx(1, "HAMMER cannot be placed in a partition "
480 				"which overlaps the disklabel or MBR");
481 		}
482 		if (pinfo.media_blksize > 16384 ||
483 		    16384 % pinfo.media_blksize) {
484 			errx(1, "A media sector size of %d is not supported",
485 			     pinfo.media_blksize);
486 		}
487 
488 		vol->size = pinfo.media_size;
489 		vol->device_offset = pinfo.media_offset;
490 		vol->type = "DEVICE";
491 	}
492 	printf("Volume %d %s %-15s size %s\n",
493 	       vol->vol_no, vol->type, vol->name,
494 	       sizetostr(vol->size));
495 
496 	/*
497 	 * Reserve space for (future) header junk, setup our poor-man's
498 	 * big-block allocator.
499 	 */
500 	vol->vol_alloc = HAMMER_BUFSIZE * 16;
501 }
502 
503 /*
504  * Format a HAMMER volume.  Cluster 0 will be initially placed in volume 0.
505  */
506 static
507 void
508 format_volume(struct volume_info *vol, int nvols, const char *label,
509 	      off_t total_size __unused)
510 {
511 	struct volume_info *root_vol;
512 	struct hammer_volume_ondisk *ondisk;
513 	int64_t freeblks;
514 	int64_t freebytes;
515 	int i;
516 
517 	/*
518 	 * Initialize basic information in the on-disk volume structure.
519 	 */
520 	ondisk = vol->ondisk;
521 
522 	ondisk->vol_fsid = Hammer_FSId;
523 	ondisk->vol_fstype = Hammer_FSType;
524 	snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
525 	ondisk->vol_no = vol->vol_no;
526 	ondisk->vol_count = nvols;
527 	ondisk->vol_version = HammerVersion;
528 
529 	ondisk->vol_bot_beg = vol->vol_alloc;
530 	vol->vol_alloc += BootAreaSize;
531 	ondisk->vol_mem_beg = vol->vol_alloc;
532 	vol->vol_alloc += MemAreaSize;
533 
534 	/*
535 	 * The remaining area is the zone 2 buffer allocation area.
536 	 */
537 	ondisk->vol_buf_beg = vol->vol_alloc;
538 	ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
539 
540 	if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
541 		errx(1, "volume %d %s is too small to hold the volume header",
542 		     vol->vol_no, vol->name);
543 	}
544 
545 	ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
546 			      HAMMER_BUFSIZE;
547 	ondisk->vol_blocksize = HAMMER_BUFSIZE;
548 
549 	ondisk->vol_rootvol = RootVolNo;
550 	ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
551 
552 	vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
553 	vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
554 				(ondisk->vol_buf_end - ondisk->vol_buf_beg) &
555 				~HAMMER_BIGBLOCK_MASK64);
556 
557 	/*
558 	 * Format the root volume.
559 	 */
560 	if (vol->vol_no == RootVolNo) {
561 		/*
562 		 * Check freemap counts before formatting
563 		 */
564 		freeblks = count_freemap(vol);
565 		freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
566 		if (freebytes < 10*GIG && ForceOpt == 0) {
567 			errx(1, "Cannot create a HAMMER filesystem less than 10GB "
568 				"unless you use -f\n(for the size of Volume %d).  "
569 				"HAMMER filesystems less than 50GB are not "
570 				"recommended.\n", RootVolNo);
571 		}
572 
573 		/*
574 		 * Starting TID
575 		 */
576 		ondisk->vol0_next_tid = createtid();
577 
578 		/*
579 		 * Format freemap.  vol0_stat_freebigblocks is
580 		 * the number of big-blocks available for anything
581 		 * other than freemap zone at this point.
582 		 */
583 		format_freemap(vol);
584 		ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
585 
586 		/*
587 		 * Format zones that are mapped to zone-2.
588 		 */
589 		for (i = HAMMER_ZONE2_MAPPED_INDEX; i < HAMMER_MAX_ZONES; ++i) {
590 			format_blockmap(&ondisk->vol0_blockmap[i], i, 0);
591 		}
592 
593 		/*
594 		 * Format undo zone.  Formatting decrements
595 		 * vol0_stat_freebigblocks whenever a new big-block
596 		 * is allocated for undo zone.
597 		 */
598 		format_undomap(vol);
599 		ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
600 
601 		/*
602 		 * Format the root directory.  Formatting decrements
603 		 * vol0_stat_freebigblocks whenever a new big-block
604 		 * is allocated for required zones.
605 		 */
606 		ondisk->vol0_btree_root = format_root(label);
607 		++ondisk->vol0_stat_inodes;	/* root inode */
608 	} else {
609 		freeblks = initialize_freemap(vol);
610 		root_vol = get_volume(RootVolNo);
611 		root_vol->cache.modified = 1;
612 		root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
613 		root_vol->ondisk->vol0_stat_bigblocks += freeblks;
614 		rel_volume(root_vol);
615 	}
616 }
617 
618 /*
619  * Format the root directory.
620  */
621 static
622 hammer_off_t
623 format_root(const char *label)
624 {
625 	hammer_off_t btree_off;
626 	hammer_off_t pfsd_off;
627 	hammer_off_t data_off;
628 	hammer_tid_t create_tid;
629 	hammer_node_ondisk_t bnode;
630 	struct hammer_inode_data *idata;
631 	hammer_pseudofs_data_t pfsd;
632 	struct buffer_info *data_buffer0 = NULL;
633 	struct buffer_info *data_buffer1 = NULL;
634 	struct buffer_info *data_buffer2 = NULL;
635 	hammer_btree_elm_t elm;
636 	u_int64_t xtime;
637 
638 	/*
639 	 * Allocate zero-filled root btree node, inode and pfs
640 	 */
641 	bnode = alloc_btree_element(&btree_off, &data_buffer0);
642 	idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
643 	pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
644 	create_tid = createtid();
645 	xtime = nowtime();
646 
647 	/*
648 	 * Populate the inode data and inode record for the root directory.
649 	 */
650 	idata->version = HAMMER_INODE_DATA_VERSION;
651 	idata->mode = 0755;
652 	idata->ctime = xtime;
653 	idata->mtime = xtime;
654 	idata->atime = xtime;
655 	idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
656 	idata->size = 0;
657 	idata->nlinks = 1;
658 	if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
659 		idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
660 	if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
661 		idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
662 
663 	pfsd->sync_low_tid = 1;
664 	pfsd->sync_beg_tid = 0;
665 	pfsd->sync_end_tid = 0;	/* overriden by vol0_next_tid on pfs0 */
666 	pfsd->shared_uuid = Hammer_FSId;
667 	pfsd->unique_uuid = Hammer_FSId;
668 	pfsd->reserved01 = 0;
669 	pfsd->mirror_flags = 0;
670 	snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
671 
672 	/*
673 	 * Create the root of the B-Tree.  The root is a leaf node so we
674 	 * do not have to worry about boundary elements.
675 	 */
676 	bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
677 	bnode->count = 2;
678 	bnode->type = HAMMER_BTREE_TYPE_LEAF;
679 
680 	elm = &bnode->elms[0];
681 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
682 	elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
683 				      HAMMER_LOCALIZE_INODE;
684 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
685 	elm->leaf.base.key = 0;
686 	elm->leaf.base.create_tid = create_tid;
687 	elm->leaf.base.delete_tid = 0;
688 	elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
689 	elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
690 	elm->leaf.create_ts = (u_int32_t)time(NULL);
691 
692 	elm->leaf.data_offset = data_off;
693 	elm->leaf.data_len = sizeof(*idata);
694 	elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
695 
696 	elm = &bnode->elms[1];
697 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
698 	elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
699 				      HAMMER_LOCALIZE_MISC;
700 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
701 	elm->leaf.base.key = 0;
702 	elm->leaf.base.create_tid = create_tid;
703 	elm->leaf.base.delete_tid = 0;
704 	elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
705 	elm->leaf.base.obj_type = 0;
706 	elm->leaf.create_ts = (u_int32_t)time(NULL);
707 
708 	elm->leaf.data_offset = pfsd_off;
709 	elm->leaf.data_len = sizeof(*pfsd);
710 	elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
711 
712 	bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
713 
714 	return(btree_off);
715 }
716