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