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