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