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_BIGBLOCK_SIZE,
109 					 HAMMER_BIGBLOCK_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("DEVICE")) == 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.
537 	 */
538 	ondisk->vol_buf_beg = vol->vol_alloc;
539 	ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
540 
541 	if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
542 		errx(1, "volume %d %s is too small to hold the volume header",
543 		     vol->vol_no, vol->name);
544 	}
545 
546 	ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
547 			      HAMMER_BUFSIZE;
548 	ondisk->vol_blocksize = HAMMER_BUFSIZE;
549 
550 	ondisk->vol_rootvol = RootVolNo;
551 	ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
552 
553 	vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
554 	vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
555 				(ondisk->vol_buf_end - ondisk->vol_buf_beg) &
556 				~HAMMER_BIGBLOCK_MASK64);
557 
558 	/*
559 	 * Format the root volume.
560 	 */
561 	if (vol->vol_no == RootVolNo) {
562 		/*
563 		 * Starting TID
564 		 */
565 		ondisk->vol0_next_tid = createtid();
566 
567 		format_freemap(vol,
568 			&ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
569 
570 		freeblks = initialize_freemap(vol);
571 		ondisk->vol0_stat_freebigblocks = freeblks;
572 
573 		freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
574 		if (freebytes < 10*GIG && ForceOpt == 0) {
575 			errx(1, "Cannot create a HAMMER filesystem less than "
576 				"10GB unless you use -f.  HAMMER filesystems\n"
577 				"less than 50GB are not recommended\n");
578 		}
579 
580 		for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
581 			format_blockmap(&ondisk->vol0_blockmap[i],
582 					HAMMER_ZONE_ENCODE(i, 0));
583 		}
584 		format_undomap(ondisk);
585 
586 		ondisk->vol0_btree_root = format_root(label);
587 		++ondisk->vol0_stat_inodes;	/* root inode */
588 	} else {
589 		freeblks = initialize_freemap(vol);
590 		root_vol = get_volume(RootVolNo);
591 		root_vol->cache.modified = 1;
592 		root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
593 		root_vol->ondisk->vol0_stat_bigblocks += freeblks;
594 		rel_volume(root_vol);
595 	}
596 }
597 
598 /*
599  * Format the root directory.
600  */
601 static
602 hammer_off_t
603 format_root(const char *label)
604 {
605 	hammer_off_t btree_off;
606 	hammer_off_t pfsd_off;
607 	hammer_off_t data_off;
608 	hammer_tid_t create_tid;
609 	hammer_node_ondisk_t bnode;
610 	struct hammer_inode_data *idata;
611 	hammer_pseudofs_data_t pfsd;
612 	struct buffer_info *data_buffer1 = NULL;
613 	struct buffer_info *data_buffer2 = NULL;
614 	hammer_btree_elm_t elm;
615 	u_int64_t xtime;
616 
617 	bnode = alloc_btree_element(&btree_off);
618 	idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
619 	pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
620 	create_tid = createtid();
621 	xtime = nowtime();
622 
623 	/*
624 	 * Populate the inode data and inode record for the root directory.
625 	 */
626 	idata->version = HAMMER_INODE_DATA_VERSION;
627 	idata->mode = 0755;
628 	idata->ctime = xtime;
629 	idata->mtime = xtime;
630 	idata->atime = xtime;
631 	idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
632 	idata->size = 0;
633 	idata->nlinks = 1;
634 	if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
635 		idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
636 	if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
637 		idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
638 
639 	pfsd->sync_low_tid = 1;
640 	pfsd->sync_beg_tid = 0;
641 	pfsd->sync_end_tid = 0;	/* overriden by vol0_next_tid on pfs0 */
642 	pfsd->shared_uuid = Hammer_FSId;
643 	pfsd->unique_uuid = Hammer_FSId;
644 	pfsd->reserved01 = 0;
645 	pfsd->mirror_flags = 0;
646 	snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
647 
648 	/*
649 	 * Create the root of the B-Tree.  The root is a leaf node so we
650 	 * do not have to worry about boundary elements.
651 	 */
652 	bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
653 	bnode->count = 2;
654 	bnode->type = HAMMER_BTREE_TYPE_LEAF;
655 
656 	elm = &bnode->elms[0];
657 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
658 	elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
659 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
660 	elm->leaf.base.key = 0;
661 	elm->leaf.base.create_tid = create_tid;
662 	elm->leaf.base.delete_tid = 0;
663 	elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
664 	elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
665 	elm->leaf.create_ts = (u_int32_t)time(NULL);
666 
667 	elm->leaf.data_offset = data_off;
668 	elm->leaf.data_len = sizeof(*idata);
669 	elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
670 
671 	elm = &bnode->elms[1];
672 	elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
673 	elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
674 	elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
675 	elm->leaf.base.key = 0;
676 	elm->leaf.base.create_tid = create_tid;
677 	elm->leaf.base.delete_tid = 0;
678 	elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
679 	elm->leaf.base.obj_type = 0;
680 	elm->leaf.create_ts = (u_int32_t)time(NULL);
681 
682 	elm->leaf.data_offset = pfsd_off;
683 	elm->leaf.data_len = sizeof(*pfsd);
684 	elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
685 
686 	bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
687 
688 	return(btree_off);
689 }
690