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