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