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 "newfs_hammer.h"
36 
37 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
38 static const char *sizetostr(off_t size);
39 static void trim_volume(struct volume_info *vol);
40 static void check_volume(struct volume_info *vol);
41 static void format_volume(struct volume_info *vol, int nvols,const char *label,
42 			off_t total_size);
43 static hammer_off_t format_root(const char *label);
44 static u_int64_t nowtime(void);
45 static void usage(void);
46 
47 static int ForceOpt = 0;
48 static int HammerVersion = -1;
49 static int Eflag = 0;
50 
51 #define GIG	(1024LL*1024*1024)
52 
53 int
54 main(int ac, char **av)
55 {
56 	u_int32_t status;
57 	off_t total;
58 	int ch;
59 	int i;
60 	const char *label = NULL;
61 	struct volume_info *vol;
62 	char *fsidstr;
63 
64 	/*
65 	 * Sanity check basic filesystem structures.  No cookies for us
66 	 * if it gets broken!
67 	 */
68 	assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
69 	assert(sizeof(struct hammer_blockmap_layer1) == 32);
70 	assert(sizeof(struct hammer_blockmap_layer2) == 16);
71 
72 	/*
73 	 * Generate a filesystem id and lookup the filesystem type
74 	 */
75 	uuidgen(&Hammer_FSId, 1);
76 	uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
77 	if (status != uuid_s_ok) {
78 		errx(1, "uuids file does not have the DragonFly "
79 			"HAMMER filesystem type");
80 	}
81 
82 	/*
83 	 * Parse arguments
84 	 */
85 	while ((ch = getopt(ac, av, "fEL:b:m:u:V:")) != -1) {
86 		switch(ch) {
87 		case 'f':
88 			ForceOpt = 1;
89 			break;
90 		case 'E':
91 			Eflag = 1;
92 			break;
93 		case 'L':
94 			label = optarg;
95 			break;
96 		case 'b':
97 			BootAreaSize = getsize(optarg,
98 					 HAMMER_BUFSIZE,
99 					 HAMMER_BOOT_MAXBYTES, 2);
100 			break;
101 		case 'm':
102 			MemAreaSize = getsize(optarg,
103 					 HAMMER_BUFSIZE,
104 					 HAMMER_MEM_MAXBYTES, 2);
105 			break;
106 		case 'u':
107 			UndoBufferSize = getsize(optarg,
108 					 HAMMER_LARGEBLOCK_SIZE,
109 					 HAMMER_LARGEBLOCK_SIZE *
110 					 HAMMER_UNDO_LAYER2, 2);
111 			if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
112 				errx(1, "The minimum UNDO/REDO FIFO size is "
113 					"500MB\n");
114 			if (UndoBufferSize < 500*1024*1024) {
115 				fprintf(stderr,
116 					"WARNING: you have specified an "
117 					"UNDO/REDO FIFO size less than 500MB,\n"
118 					"which may lead to VFS panics.\n");
119 			}
120 			break;
121 		case 'V':
122 			HammerVersion = strtol(optarg, NULL, 0);
123 			if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
124 			    HammerVersion >= HAMMER_VOL_VERSION_WIP) {
125 				errx(1,
126 				     "I don't understand how to format "
127 				     "HAMMER version %d\n",
128 				     HammerVersion);
129 			}
130 			break;
131 		default:
132 			usage();
133 			break;
134 		}
135 	}
136 
137 	if (label == NULL) {
138 		fprintf(stderr,
139 			"newfs_hammer: A filesystem label must be specified\n");
140 		usage();
141 	}
142 
143 	if (HammerVersion < 0) {
144 		size_t olen = sizeof(HammerVersion);
145 		HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
146 		if (sysctlbyname("vfs.hammer.supported_version",
147 				 &HammerVersion, &olen, NULL, 0) == 0) {
148 			if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
149 				HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
150 				fprintf(stderr,
151 					"newfs_hammer: WARNING: HAMMER VFS "
152 					"supports higher version than I "
153 					"understand,\n"
154 					"using version %d\n",
155 					HammerVersion);
156 			}
157 		} else {
158 			fprintf(stderr,
159 				"newfs_hammer: WARNING: HAMMER VFS not "
160 				"loaded, cannot get version info.\n"
161 				"Using version %d\n",
162 				HAMMER_VOL_VERSION_DEFAULT);
163 		}
164 	}
165 
166 	/*
167 	 * Collect volume information
168 	 */
169 	ac -= optind;
170 	av += optind;
171 	NumVolumes = ac;
172 	RootVolNo = 0;
173 
174         if (NumVolumes == 0) {
175                 fprintf(stderr,
176                         "newfs_hammer: You must specify at least one special file (volume)\n");
177                 exit(1);
178         }
179 
180 	if (NumVolumes > HAMMER_MAX_VOLUMES) {
181                 fprintf(stderr,
182                         "newfs_hammer: The maximum number of volumes is %d\n", HAMMER_MAX_VOLUMES);
183 		exit(1);
184 	}
185 
186 	total = 0;
187 	for (i = 0; i < NumVolumes; ++i) {
188 		vol = setup_volume(i, av[i], 1, O_RDWR);
189 
190 		/*
191 		 * Load up information on the volume and initialize
192 		 * its remaining fields.
193 		 */
194 		check_volume(vol);
195 		if (Eflag) {
196 			char sysctl_name[64];
197 			int trim_enabled = 0;
198 			size_t olen = sizeof(trim_enabled);
199 			char *dev_name = strdup(vol->name);
200 			dev_name = strtok(dev_name + strlen("/dev/da"),"s");
201 
202 			sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled",
203 			    dev_name);
204 			errno=0;
205 			sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
206 			if(errno == ENOENT) {
207 				printf("Device:%s (%s) does not support the "
208 				    "TRIM command\n", vol->name,sysctl_name);
209 				usage();
210 			}
211 			if(!trim_enabled) {
212 				printf("Erase device option selected, but "
213 				    "sysctl (%s) is not enabled\n", sysctl_name);
214 				usage();
215 
216 			}
217 			trim_volume(vol);
218 		}
219 		total += vol->size;
220 	}
221 
222 	/*
223 	 * Calculate defaults for the boot and memory area sizes.
224 	 */
225 	if (BootAreaSize == 0) {
226 		BootAreaSize = HAMMER_BOOT_NOMBYTES;
227 		while (BootAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
228 			BootAreaSize >>= 1;
229 		if (BootAreaSize < HAMMER_BOOT_MINBYTES)
230 			BootAreaSize = 0;
231 	} else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
232 		BootAreaSize = HAMMER_BOOT_MINBYTES;
233 	}
234 	if (MemAreaSize == 0) {
235 		MemAreaSize = HAMMER_MEM_NOMBYTES;
236 		while (MemAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
237 			MemAreaSize >>= 1;
238 		if (MemAreaSize < HAMMER_MEM_MINBYTES)
239 			MemAreaSize = 0;
240 	} else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
241 		MemAreaSize = HAMMER_MEM_MINBYTES;
242 	}
243 
244 	/*
245 	 * Format the volumes.  Format the root volume first so we can
246 	 * bootstrap the freemap.
247 	 */
248 	format_volume(get_volume(RootVolNo), NumVolumes, label, total);
249 	for (i = 0; i < NumVolumes; ++i) {
250 		if (i != RootVolNo)
251 			format_volume(get_volume(i), NumVolumes, label, total);
252 	}
253 
254 	/*
255 	 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
256 	 * limit.  If we do this the filesystem does not have to allocate
257 	 * new layer2 blocks which reduces the chances of the reblocker
258 	 * having to fallback to an extremely inefficient algorithm.
259 	 */
260 	vol = get_volume(RootVolNo);
261 	vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks;
262 	vol->cache.modified = 1;
263 	uuid_to_string(&Hammer_FSId, &fsidstr, &status);
264 
265 	printf("---------------------------------------------\n");
266 	printf("%d volume%s total size %s version %d\n",
267 		NumVolumes, (NumVolumes == 1 ? "" : "s"),
268 		sizetostr(total), HammerVersion);
269 	printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
270 	printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
271 	printf("undo-buffer-size:    %s\n", sizetostr(UndoBufferSize));
272 	printf("total-pre-allocated: %s\n",
273 		sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
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 500MB, you "
284 		       "really should not\n"
285 		       "try to format a HAMMER filesystem this small.\n");
286 	}
287 	if (total < 50*GIG) {
288 		printf("\nWARNING: HAMMER filesystems less than 50GB are "
289 			"not recommended!\n"
290 			"You may have to run 'hammer prune-everything' and "
291 			"'hammer reblock'\n"
292 			"quite often, even if using a nohistory mount.\n");
293 	}
294 	flush_all_volumes();
295 	return(0);
296 }
297 
298 static
299 void
300 usage(void)
301 {
302 	fprintf(stderr,
303 		"usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
304 		"                    [-V version] special ...\n"
305 	);
306 	exit(1);
307 }
308 
309 /*
310  * Convert the size in bytes to a human readable string.
311  */
312 static
313 const char *
314 sizetostr(off_t size)
315 {
316 	static char buf[32];
317 
318 	if (size < 1024 / 2) {
319 		snprintf(buf, sizeof(buf), "%6.2f", (double)size);
320 	} else if (size < 1024 * 1024 / 2) {
321 		snprintf(buf, sizeof(buf), "%6.2fKB",
322 			(double)size / 1024);
323 	} else if (size < 1024 * 1024 * 1024LL / 2) {
324 		snprintf(buf, sizeof(buf), "%6.2fMB",
325 			(double)size / (1024 * 1024));
326 	} else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
327 		snprintf(buf, sizeof(buf), "%6.2fGB",
328 			(double)size / (1024 * 1024 * 1024LL));
329 	} else {
330 		snprintf(buf, sizeof(buf), "%6.2fTB",
331 			(double)size / (1024 * 1024 * 1024LL * 1024LL));
332 	}
333 	return(buf);
334 }
335 
336 /*
337  * Convert a string to a 64 bit signed integer with various requirements.
338  */
339 static int64_t
340 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
341 {
342 	int64_t val;
343 	char *ptr;
344 
345 	val = strtoll(str, &ptr, 0);
346 	switch(*ptr) {
347 	case 't':
348 	case 'T':
349 		val *= 1024;
350 		/* fall through */
351 	case 'g':
352 	case 'G':
353 		val *= 1024;
354 		/* fall through */
355 	case 'm':
356 	case 'M':
357 		val *= 1024;
358 		/* fall through */
359 	case 'k':
360 	case 'K':
361 		val *= 1024;
362 		break;
363 	default:
364 		errx(1, "Unknown suffix in number '%s'\n", str);
365 		/* not reached */
366 	}
367 	if (ptr[1]) {
368 		errx(1, "Unknown suffix in number '%s'\n", str);
369 		/* not reached */
370 	}
371 	if (val < minval) {
372 		errx(1, "Value too small: %s, min is %s\n",
373 		     str, sizetostr(minval));
374 		/* not reached */
375 	}
376 	if (val > maxval) {
377 		errx(1, "Value too large: %s, max is %s\n",
378 		     str, sizetostr(maxval));
379 		/* not reached */
380 	}
381 	if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
382 		errx(1, "Value not power of 2: %s\n", str);
383 		/* not reached */
384 	}
385 	if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
386 		errx(1, "Value not an integral multiple of %dK: %s",
387 		     HAMMER_BUFSIZE / 1024, str);
388 		/* not reached */
389 	}
390 	return(val);
391 }
392 
393 /*
394  * Generate a transaction id.  Transaction ids are no longer time-based.
395  * Put the nail in the coffin by not making the first one time-based.
396  *
397  * We could start at 1 here but start at 2^32 to reserve a small domain for
398  * possible future use.
399  */
400 static hammer_tid_t
401 createtid(void)
402 {
403 	static hammer_tid_t lasttid;
404 
405 	if (lasttid == 0)
406 		lasttid = 0x0000000100000000ULL;
407 	return(lasttid++);
408 }
409 
410 static u_int64_t
411 nowtime(void)
412 {
413 	struct timeval tv;
414 	u_int64_t xtime;
415 
416 	gettimeofday(&tv, NULL);
417 	xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
418 	return(xtime);
419 }
420 
421 /*
422  * TRIM the volume, but only if the backing store is a DEVICE
423  */
424 static
425 void
426 trim_volume(struct volume_info *vol)
427 {
428 	if (strncmp(vol->type, "DEVICE", sizeof(vol->type)) == 0) {
429 		off_t ioarg[2];
430 
431 		/* 1MB offset to prevent destroying disk-reserved area */
432 		ioarg[0] = vol->device_offset;
433 		ioarg[1] = vol->size;
434 		printf("Trimming Device:%s, sectors (%llu -%llu)\n",vol->name,
435 		    (unsigned long long)ioarg[0]/512,
436 		    (unsigned long long)ioarg[1]/512);
437 		if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
438 			printf("Device trim failed\n");
439 			usage ();
440 		}
441 	}
442 }
443 
444 /*
445  * Check basic volume characteristics.  HAMMER filesystems use a minimum
446  * of a 16KB filesystem buffer size.
447  */
448 static
449 void
450 check_volume(struct volume_info *vol)
451 {
452 	struct partinfo pinfo;
453 	struct stat st;
454 
455 	/*
456 	 * Get basic information about the volume
457 	 */
458 	vol->fd = open(vol->name, O_RDWR);
459 	if (vol->fd < 0)
460 		err(1, "Unable to open %s R+W", vol->name);
461 	if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
462 		/*
463 		 * Allow the formatting of regular files as HAMMER volumes
464 		 */
465 		if (fstat(vol->fd, &st) < 0)
466 			err(1, "Unable to stat %s", vol->name);
467 		vol->size = st.st_size;
468 		vol->type = "REGFILE";
469 
470 		if (Eflag)
471 			errx(1,"Cannot TRIM regular file %s\n", vol->name);
472 
473 	} else {
474 		/*
475 		 * When formatting a block device as a HAMMER volume the
476 		 * sector size must be compatible.  HAMMER uses 16384 byte
477 		 * filesystem buffers.
478 		 */
479 		if (pinfo.reserved_blocks) {
480 			errx(1, "HAMMER cannot be placed in a partition "
481 				"which overlaps the disklabel or MBR");
482 		}
483 		if (pinfo.media_blksize > 16384 ||
484 		    16384 % pinfo.media_blksize) {
485 			errx(1, "A media sector size of %d is not supported",
486 			     pinfo.media_blksize);
487 		}
488 
489 		vol->size = pinfo.media_size;
490 		vol->device_offset = pinfo.media_offset;
491 		vol->type = "DEVICE";
492 	}
493 	printf("Volume %d %s %-15s size %s\n",
494 	       vol->vol_no, vol->type, vol->name,
495 	       sizetostr(vol->size));
496 
497 	/*
498 	 * Reserve space for (future) header junk, setup our poor-man's
499 	 * bigblock allocator.
500 	 */
501 	vol->vol_alloc = HAMMER_BUFSIZE * 16;
502 }
503 
504 /*
505  * Format a HAMMER volume.  Cluster 0 will be initially placed in volume 0.
506  */
507 static
508 void
509 format_volume(struct volume_info *vol, int nvols, const char *label,
510 	      off_t total_size __unused)
511 {
512 	struct volume_info *root_vol;
513 	struct hammer_volume_ondisk *ondisk;
514 	int64_t freeblks;
515 	int64_t freebytes;
516 	int i;
517 
518 	/*
519 	 * Initialize basic information in the on-disk volume structure.
520 	 */
521 	ondisk = vol->ondisk;
522 
523 	ondisk->vol_fsid = Hammer_FSId;
524 	ondisk->vol_fstype = Hammer_FSType;
525 	snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
526 	ondisk->vol_no = vol->vol_no;
527 	ondisk->vol_count = nvols;
528 	ondisk->vol_version = HammerVersion;
529 
530 	ondisk->vol_bot_beg = vol->vol_alloc;
531 	vol->vol_alloc += BootAreaSize;
532 	ondisk->vol_mem_beg = vol->vol_alloc;
533 	vol->vol_alloc += MemAreaSize;
534 
535 	/*
536 	 * The remaining area is the zone 2 buffer allocation area.  These
537 	 * buffers
538 	 */
539 	ondisk->vol_buf_beg = vol->vol_alloc;
540 	ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
541 
542 	if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
543 		errx(1, "volume %d %s is too small to hold the volume header",
544 		     vol->vol_no, vol->name);
545 	}
546 
547 	ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
548 			      HAMMER_BUFSIZE;
549 	ondisk->vol_blocksize = HAMMER_BUFSIZE;
550 
551 	ondisk->vol_rootvol = RootVolNo;
552 	ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
553 
554 	vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
555 	vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
556 				(ondisk->vol_buf_end - ondisk->vol_buf_beg) &
557 				~HAMMER_LARGEBLOCK_MASK64);
558 
559 	/*
560 	 * Format the root volume.
561 	 */
562 	if (vol->vol_no == RootVolNo) {
563 		/*
564 		 * Starting TID
565 		 */
566 		ondisk->vol0_next_tid = createtid();
567 
568 		format_freemap(vol,
569 			&ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
570 
571 		freeblks = initialize_freemap(vol);
572 		ondisk->vol0_stat_freebigblocks = freeblks;
573 
574 		freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64;
575 		if (freebytes < 10*GIG && ForceOpt == 0) {
576 			errx(1, "Cannot create a HAMMER filesystem less than "
577 				"10GB unless you use -f.  HAMMER filesystems\n"
578 				"less than 50GB are not recommended\n");
579 		}
580 
581 		for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
582 			format_blockmap(&ondisk->vol0_blockmap[i],
583 					HAMMER_ZONE_ENCODE(i, 0));
584 		}
585 		format_undomap(ondisk);
586 
587 		ondisk->vol0_btree_root = format_root(label);
588 		++ondisk->vol0_stat_inodes;	/* root inode */
589 	} else {
590 		freeblks = initialize_freemap(vol);
591 		root_vol = get_volume(RootVolNo);
592 		root_vol->cache.modified = 1;
593 		root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
594 		root_vol->ondisk->vol0_stat_bigblocks += freeblks;
595 		rel_volume(root_vol);
596 	}
597 }
598 
599 /*
600  * Format the root directory.
601  */
602 static
603 hammer_off_t
604 format_root(const char *label)
605 {
606 	hammer_off_t btree_off;
607 	hammer_off_t pfsd_off;
608 	hammer_off_t data_off;
609 	hammer_tid_t create_tid;
610 	hammer_node_ondisk_t bnode;
611 	struct hammer_inode_data *idata;
612 	hammer_pseudofs_data_t pfsd;
613 	struct buffer_info *data_buffer1 = NULL;
614 	struct buffer_info *data_buffer2 = NULL;
615 	hammer_btree_elm_t elm;
616 	u_int64_t xtime;
617 
618 	bnode = alloc_btree_element(&btree_off);
619 	idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
620 	pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
621 	create_tid = createtid();
622 	xtime = nowtime();
623 
624 	/*
625 	 * Populate the inode data and inode record for the root directory.
626 	 */
627 	idata->version = HAMMER_INODE_DATA_VERSION;
628 	idata->mode = 0755;
629 	idata->ctime = xtime;
630 	idata->mtime = xtime;
631 	idata->atime = xtime;
632 	idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
633 	idata->size = 0;
634 	idata->nlinks = 1;
635 	if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
636 		idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
637 	if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
638 		idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
639 
640 	pfsd->sync_low_tid = 1;
641 	pfsd->sync_beg_tid = 0;
642 	pfsd->sync_end_tid = 0;	/* overriden by vol0_next_tid on pfs0 */
643 	pfsd->shared_uuid = Hammer_FSId;
644 	pfsd->unique_uuid = Hammer_FSId;
645 	pfsd->reserved01 = 0;
646 	pfsd->mirror_flags = 0;
647 	snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
648 
649 	/*
650 	 * Create the root of the B-Tree.  The root is a leaf node so we
651 	 * do not have to worry about boundary elements.
652 	 */
653 	bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
654 	bnode->count = 2;
655 	bnode->type = HAMMER_BTREE_TYPE_LEAF;
656 
657 	elm = &bnode->elms[0];
658 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
659 	elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
660 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
661 	elm->leaf.base.key = 0;
662 	elm->leaf.base.create_tid = create_tid;
663 	elm->leaf.base.delete_tid = 0;
664 	elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
665 	elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
666 	elm->leaf.create_ts = (u_int32_t)time(NULL);
667 
668 	elm->leaf.data_offset = data_off;
669 	elm->leaf.data_len = sizeof(*idata);
670 	elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
671 
672 	elm = &bnode->elms[1];
673 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
674 	elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
675 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
676 	elm->leaf.base.key = 0;
677 	elm->leaf.base.create_tid = create_tid;
678 	elm->leaf.base.delete_tid = 0;
679 	elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
680 	elm->leaf.base.obj_type = 0;
681 	elm->leaf.create_ts = (u_int32_t)time(NULL);
682 
683 	elm->leaf.data_offset = pfsd_off;
684 	elm->leaf.data_len = sizeof(*pfsd);
685 	elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
686 
687 	bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
688 
689 	return(btree_off);
690 }
691