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