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  * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.7 2007/11/20 07:16:27 dillon Exp $
35  */
36 
37 #include "newfs_hammer.h"
38 
39 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
40 static const char *sizetostr(off_t size);
41 static void check_volume(struct volume_info *vol);
42 static void format_volume(struct volume_info *vol, int nvols,const char *label);
43 static int32_t format_cluster(struct volume_info *vol, int isroot);
44 static void format_root(struct cluster_info *cluster);
45 static void usage(void);
46 
47 struct hammer_alist_config Buf_alist_config;
48 struct hammer_alist_config Vol_normal_alist_config;
49 struct hammer_alist_config Vol_super_alist_config;
50 struct hammer_alist_config Supercl_alist_config;
51 struct hammer_alist_config Clu_master_alist_config;
52 struct hammer_alist_config Clu_slave_alist_config;
53 uuid_t Hammer_FSType;
54 uuid_t Hammer_FSId;
55 int64_t ClusterSize;
56 int64_t BootAreaSize;
57 int64_t MemAreaSize;
58 int	UsingSuperClusters;
59 int	NumVolumes;
60 struct volume_info *VolBase;
61 
62 int
63 main(int ac, char **av)
64 {
65 	int i;
66 	int ch;
67 	u_int32_t status;
68 	off_t total;
69 	int64_t max_volume_size;
70 	const char *label = NULL;
71 
72 	/*
73 	 * Sanity check basic filesystem structures.  No cookies for us
74 	 * if it gets broken!
75 	 */
76 	assert(sizeof(struct hammer_almeta) == HAMMER_ALMETA_SIZE);
77 	assert(sizeof(struct hammer_fsbuf_head) == HAMMER_FSBUF_HEAD_SIZE);
78 	assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
79 	assert(sizeof(struct hammer_cluster_ondisk) <= HAMMER_BUFSIZE);
80 	assert(sizeof(struct hammer_fsbuf_data) == HAMMER_BUFSIZE);
81 	assert(sizeof(struct hammer_fsbuf_recs) == HAMMER_BUFSIZE);
82 	assert(sizeof(struct hammer_fsbuf_btree) == HAMMER_BUFSIZE);
83 	assert(sizeof(union hammer_fsbuf_ondisk) == HAMMER_BUFSIZE);
84 
85 	/*
86 	 * Generate a filesysem id and lookup the filesystem type
87 	 */
88 	uuidgen(&Hammer_FSId, 1);
89 	uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
90 	if (status != uuid_s_ok) {
91 		errx(1, "uuids file does not have the DragonFly "
92 			"HAMMER filesystem type");
93 	}
94 
95 	/*
96 	 * Initialize the alist templates we will be using
97 	 */
98 	hammer_alist_template(&Buf_alist_config, HAMMER_FSBUF_MAXBLKS,
99 			      1, HAMMER_FSBUF_METAELMS);
100 	hammer_alist_template(&Vol_normal_alist_config, HAMMER_VOL_MAXCLUSTERS,
101 			      1, HAMMER_VOL_METAELMS_1LYR);
102 	hammer_alist_template(&Vol_super_alist_config,
103 			      HAMMER_VOL_MAXSUPERCLUSTERS,
104 			      HAMMER_SCL_MAXCLUSTERS, HAMMER_VOL_METAELMS_2LYR);
105 	hammer_super_alist_template(&Vol_super_alist_config);
106 	hammer_alist_template(&Supercl_alist_config, HAMMER_VOL_MAXCLUSTERS,
107 			      1, HAMMER_SUPERCL_METAELMS);
108 	hammer_alist_template(&Clu_master_alist_config, HAMMER_CLU_MAXBUFFERS,
109 			      1, HAMMER_CLU_MASTER_METAELMS);
110 	hammer_alist_template(&Clu_slave_alist_config, HAMMER_CLU_MAXBUFFERS,
111 			      HAMMER_FSBUF_MAXBLKS, HAMMER_CLU_SLAVE_METAELMS);
112 	hammer_buffer_alist_template(&Clu_slave_alist_config);
113 
114 	/*
115 	 * Parse arguments
116 	 */
117 	while ((ch = getopt(ac, av, "L:b:c:m:S")) != -1) {
118 		switch(ch) {
119 		case 'L':
120 			label = optarg;
121 			break;
122 		case 'b':
123 			BootAreaSize = getsize(optarg,
124 					 HAMMER_BUFSIZE,
125 					 HAMMER_BOOT_MAXBYTES, 2);
126 			break;
127 		case 'c':
128 			ClusterSize = getsize(optarg,
129 					 HAMMER_BUFSIZE * 256LL,
130 					 HAMMER_CLU_MAXBYTES, 1);
131 			break;
132 		case 'm':
133 			MemAreaSize = getsize(optarg,
134 					 HAMMER_BUFSIZE,
135 					 HAMMER_MEM_MAXBYTES, 2);
136 			break;
137 		case 'S':
138 			/*
139 			 * Force the use of super-clusters
140 			 */
141 			UsingSuperClusters = 1;
142 			break;
143 		default:
144 			usage();
145 			break;
146 		}
147 	}
148 
149 	if (label == NULL) {
150 		fprintf(stderr,
151 			"newfs_hammer: A filesystem label must be specified\n");
152 		exit(1);
153 	}
154 
155 	/*
156 	 * Collect volume information
157 	 */
158 	ac -= optind;
159 	av += optind;
160 	NumVolumes = ac;
161 
162 	total = 0;
163 	for (i = 0; i < NumVolumes; ++i) {
164 		struct volume_info *vol;
165 
166 		vol = calloc(1, sizeof(struct volume_info));
167 		vol->fd = -1;
168 		vol->vol_no = i;
169 		vol->name = av[i];
170 		vol->next = VolBase;
171 		VolBase = vol;
172 
173 		/*
174 		 * Load up information on the volume and initialize
175 		 * its remaining fields.
176 		 */
177 		check_volume(vol);
178 		total += vol->size;
179 	}
180 
181 	/*
182 	 * Calculate the size of a cluster.  A cluster is broken
183 	 * down into 256 chunks which must be at least filesystem buffer
184 	 * sized.  This gives us a minimum chunk size of around 4MB.
185 	 */
186 	if (ClusterSize == 0) {
187 		ClusterSize = HAMMER_BUFSIZE * 256;
188 		while (ClusterSize < total / NumVolumes / 256 &&
189 		       ClusterSize < HAMMER_CLU_MAXBYTES) {
190 			ClusterSize <<= 1;
191 		}
192 	}
193 
194 	/*
195 	 * Calculate defaults for the boot and memory area sizes.
196 	 */
197 	if (BootAreaSize == 0) {
198 		BootAreaSize = HAMMER_BOOT_NOMBYTES;
199 		while (BootAreaSize > total / NumVolumes / 256)
200 			BootAreaSize >>= 1;
201 		if (BootAreaSize < HAMMER_BOOT_MINBYTES)
202 			BootAreaSize = 0;
203 	} else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
204 		BootAreaSize = HAMMER_BOOT_MINBYTES;
205 	}
206 	if (MemAreaSize == 0) {
207 		MemAreaSize = HAMMER_MEM_NOMBYTES;
208 		while (MemAreaSize > total / NumVolumes / 256)
209 			MemAreaSize >>= 1;
210 		if (MemAreaSize < HAMMER_MEM_MINBYTES)
211 			MemAreaSize = 0;
212 	} else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
213 		MemAreaSize = HAMMER_MEM_MINBYTES;
214 	}
215 
216 	printf("---------------------------------------------\n");
217 	printf("%d volume%s total size %s\n",
218 		NumVolumes, (NumVolumes == 1 ? "" : "s"), sizetostr(total));
219 	printf("cluster-size:        %s\n", sizetostr(ClusterSize));
220 
221 	if (UsingSuperClusters) {
222 		max_volume_size = (int64_t)HAMMER_VOL_MAXSUPERCLUSTERS * \
223 				  HAMMER_SCL_MAXCLUSTERS * ClusterSize;
224 	} else {
225 		max_volume_size = HAMMER_VOL_MAXCLUSTERS * ClusterSize;
226 	}
227 	printf("max-volume-size:     %s\n", sizetostr(max_volume_size));
228 
229 	printf("max-filesystem-size: %s\n",
230 	       (max_volume_size * 32768LL < max_volume_size) ?
231 	       "Unlimited" :
232 	       sizetostr(max_volume_size * 32768LL));
233 	printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
234 	printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
235 	printf("\n");
236 
237 	/*
238 	 * Format the volumes.
239 	 */
240 	for (i = 0; i < NumVolumes; ++i) {
241 		format_volume(get_volume(i), NumVolumes, label);
242 	}
243 	flush_all_volumes();
244 	return(0);
245 }
246 
247 static
248 void
249 usage(void)
250 {
251 	fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n");
252 	exit(1);
253 }
254 
255 /*
256  * Convert the size in bytes to a human readable string.
257  */
258 static const char *
259 sizetostr(off_t size)
260 {
261 	static char buf[32];
262 
263 	if (size < 1024 / 2) {
264 		snprintf(buf, sizeof(buf), "%6.2f", (double)size);
265 	} else if (size < 1024 * 1024 / 2) {
266 		snprintf(buf, sizeof(buf), "%6.2fKB",
267 			(double)size / 1024);
268 	} else if (size < 1024 * 1024 * 1024LL / 2) {
269 		snprintf(buf, sizeof(buf), "%6.2fMB",
270 			(double)size / (1024 * 1024));
271 	} else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
272 		snprintf(buf, sizeof(buf), "%6.2fGB",
273 			(double)size / (1024 * 1024 * 1024LL));
274 	} else {
275 		snprintf(buf, sizeof(buf), "%6.2fTB",
276 			(double)size / (1024 * 1024 * 1024LL * 1024LL));
277 	}
278 	return(buf);
279 }
280 
281 /*
282  * Convert a string to a 64 bit signed integer with various requirements.
283  */
284 static int64_t
285 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
286 {
287 	int64_t val;
288 	char *ptr;
289 
290 	val = strtoll(str, &ptr, 0);
291 	switch(*ptr) {
292 	case 't':
293 	case 'T':
294 		val *= 1024;
295 		/* fall through */
296 	case 'g':
297 	case 'G':
298 		val *= 1024;
299 		/* fall through */
300 	case 'm':
301 	case 'M':
302 		val *= 1024;
303 		/* fall through */
304 	case 'k':
305 	case 'K':
306 		val *= 1024;
307 		break;
308 	default:
309 		errx(1, "Unknown suffix in number '%s'\n", str);
310 		/* not reached */
311 	}
312 	if (ptr[1]) {
313 		errx(1, "Unknown suffix in number '%s'\n", str);
314 		/* not reached */
315 	}
316 	if (val < minval) {
317 		errx(1, "Value too small: %s, min is %s\n",
318 		     str, sizetostr(minval));
319 		/* not reached */
320 	}
321 	if (val > maxval) {
322 		errx(1, "Value too large: %s, max is %s\n",
323 		     str, sizetostr(maxval));
324 		/* not reached */
325 	}
326 	if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
327 		errx(1, "Value not power of 2: %s\n", str);
328 		/* not reached */
329 	}
330 	if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
331 		errx(1, "Value not an integral multiple of %dK: %s",
332 		     HAMMER_BUFSIZE / 1024, str);
333 		/* not reached */
334 	}
335 	return(val);
336 }
337 
338 /*
339  * Generate a transaction id
340  */
341 static hammer_tid_t
342 createtid(void)
343 {
344 	static hammer_tid_t lasttid;
345 	struct timeval tv;
346 
347 	if (lasttid == 0) {
348 		gettimeofday(&tv, NULL);
349 		lasttid = tv.tv_sec * 1000000000LL +
350 			  tv.tv_usec * 1000LL;
351 	}
352 	return(lasttid++);
353 }
354 
355 /*
356  * Check basic volume characteristics.  HAMMER filesystems use a minimum
357  * of a 16KB filesystem buffer size.
358  */
359 static
360 void
361 check_volume(struct volume_info *vol)
362 {
363 	struct partinfo pinfo;
364 	struct stat st;
365 
366 	/*
367 	 * Get basic information about the volume
368 	 */
369 	vol->fd = open(vol->name, O_RDWR);
370 	if (vol->fd < 0)
371 		err(1, "Unable to open %s R+W", vol->name);
372 	if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
373 		/*
374 		 * Allow the formatting of regular filews as HAMMER volumes
375 		 */
376 		if (fstat(vol->fd, &st) < 0)
377 			err(1, "Unable to stat %s", vol->name);
378 		vol->size = st.st_size;
379 		vol->type = "REGFILE";
380 	} else {
381 		/*
382 		 * When formatting a block device as a HAMMER volume the
383 		 * sector size must be compatible.  HAMMER uses 16384 byte
384 		 * filesystem buffers.
385 		 */
386 		if (pinfo.reserved_blocks) {
387 			errx(1, "HAMMER cannot be placed in a partition "
388 				"which overlaps the disklabel or MBR");
389 		}
390 		if (pinfo.media_blksize > 16384 ||
391 		    16384 % pinfo.media_blksize) {
392 			errx(1, "A media sector size of %d is not supported",
393 			     pinfo.media_blksize);
394 		}
395 
396 		vol->size = pinfo.media_size;
397 		vol->type = "DEVICE";
398 	}
399 	printf("Volume %d %s %-15s size %s\n",
400 	       vol->vol_no, vol->type, vol->name,
401 	       sizetostr(vol->size));
402 
403 	/*
404 	 * Strictly speaking we do not need to enable super clusters unless
405 	 * we have volumes > 2TB, but turning them on doesn't really hurt
406 	 * and if we don't the user may get confused if he tries to expand
407 	 * the size of an existing volume.
408 	 */
409 	if (vol->size > 200LL * 1024 * 1024 * 1024 && !UsingSuperClusters) {
410 		UsingSuperClusters = 1;
411 		printf("Enabling super-clusters\n");
412 	}
413 
414 	/*
415 	 * Reserve space for (future) header junk
416 	 */
417 	vol->vol_alloc = HAMMER_BUFSIZE * 16;
418 }
419 
420 /*
421  * Format a HAMMER volume.  Cluster 0 will be initially placed in volume 0.
422  */
423 static
424 void
425 format_volume(struct volume_info *vol, int nvols, const char *label)
426 {
427 	struct hammer_volume_ondisk *ondisk;
428 	int32_t nclusters;
429 	int32_t minclsize;
430 	int32_t nscl_groups;
431 	int64_t scl_group_size;
432 	int64_t scl_header_size;
433 	int64_t n64;
434 
435 	/*
436 	 * The last cluster in a volume may wind up truncated.  It must be
437 	 * at least minclsize to really be workable as a cluster.
438 	 */
439 	minclsize = (int32_t)(ClusterSize / 4);
440 	if (minclsize < HAMMER_BUFSIZE * 64)
441 		minclsize = HAMMER_BUFSIZE * 64;
442 
443 	/*
444 	 * Initialize basic information in the on-disk volume structure.
445 	 */
446 	ondisk = vol->ondisk;
447 
448 	ondisk->vol_fsid = Hammer_FSId;
449 	ondisk->vol_fstype = Hammer_FSType;
450 	snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
451 	ondisk->vol_no = vol->vol_no;
452 	ondisk->vol_count = nvols;
453 	ondisk->vol_version = 1;
454 	ondisk->vol_clsize = (int32_t)ClusterSize;
455 	if (UsingSuperClusters)
456 		ondisk->vol_flags = HAMMER_VOLF_USINGSUPERCL;
457 
458 	ondisk->vol_bot_beg = vol->vol_alloc;
459 	vol->vol_alloc += BootAreaSize;
460 	ondisk->vol_mem_beg = vol->vol_alloc;
461 	vol->vol_alloc += MemAreaSize;
462 	ondisk->vol_clo_beg = vol->vol_alloc;
463 	ondisk->vol_clo_end = vol->size;
464 
465 	if (ondisk->vol_clo_end < ondisk->vol_clo_beg) {
466 		errx(1, "volume %d %s is too small to hold the volume header",
467 		     vol->vol_no, vol->name);
468 	}
469 
470 	/*
471 	 * Our A-lists have been initialized but are marked all-allocated.
472 	 * Calculate the actual number of clusters in the volume and free
473 	 * them to get the filesystem ready for work.  The clusters will
474 	 * be initialized on-demand.
475 	 *
476 	 * If using super-clusters we must still calculate nclusters but
477 	 * we only need to initialize superclusters that are not going
478 	 * to wind up in the all-free state, which will only be the last
479 	 * supercluster.  hammer_alist_free() will recurse into the
480 	 * supercluster infrastructure and create the necessary superclusters.
481 	 *
482 	 * NOTE: The nclusters calculation ensures that the volume EOF does
483 	 * not occur in the middle of a supercluster buffer array.
484 	 */
485 	if (UsingSuperClusters) {
486 		/*
487 		 * Figure out how many full super-cluster groups we will have.
488 		 * This calculation does not include the partial supercluster
489 		 * group at the end.
490 		 */
491 		scl_header_size = (int64_t)HAMMER_BUFSIZE *
492 				  HAMMER_VOL_SUPERCLUSTER_GROUP;
493 		scl_group_size = scl_header_size +
494 				 (int64_t)HAMMER_VOL_SUPERCLUSTER_GROUP *
495 				 ClusterSize * HAMMER_SCL_MAXCLUSTERS;
496 		nscl_groups = (ondisk->vol_clo_end - ondisk->vol_clo_beg) /
497 				scl_group_size;
498 		nclusters = nscl_groups * HAMMER_SCL_MAXCLUSTERS *
499 				HAMMER_VOL_SUPERCLUSTER_GROUP;
500 
501 		/*
502 		 * Figure out how much space we have left and calculate the
503 		 * remaining number of clusters.
504 		 */
505 		n64 = (ondisk->vol_clo_end - ondisk->vol_clo_beg) -
506 			(nscl_groups * scl_group_size);
507 		if (n64 > scl_header_size) {
508 			nclusters += (n64 + minclsize) / ClusterSize;
509 		}
510 		printf("%d clusters, %d full super-cluster groups\n",
511 			nclusters, nscl_groups);
512 		hammer_alist_free(&vol->clu_alist, 0, nclusters);
513 	} else {
514 		nclusters = (ondisk->vol_clo_end - ondisk->vol_clo_beg +
515 			     minclsize) / ClusterSize;
516 		if (nclusters > HAMMER_VOL_MAXCLUSTERS) {
517 			errx(1, "Volume is too large, max %s\n",
518 			     sizetostr(nclusters * ClusterSize));
519 		}
520 		hammer_alist_free(&vol->clu_alist, 0, nclusters);
521 	}
522 	ondisk->vol_nclusters = nclusters;
523 
524 	/*
525 	 * Place the root cluster in volume 0.
526 	 */
527 	ondisk->vol_rootvol = 0;
528 	if (ondisk->vol_no == ondisk->vol_rootvol) {
529 		ondisk->vol0_root_clu_id = format_cluster(vol, 1);
530 		ondisk->vol0_recid = 1;
531 		/* global next TID */
532 		ondisk->vol0_nexttid = createtid();
533 	}
534 }
535 
536 /*
537  * Format a hammer cluster.  Returns byte offset in volume of cluster.
538  */
539 static
540 int32_t
541 format_cluster(struct volume_info *vol, int isroot)
542 {
543 	hammer_tid_t clu_id = createtid();
544 	struct cluster_info *cluster;
545 	struct hammer_cluster_ondisk *ondisk;
546 	int nbuffers;
547 	int clno;
548 
549 	/*
550 	 * Allocate a cluster
551 	 */
552 	clno = hammer_alist_alloc(&vol->clu_alist, 1);
553 	if (clno == HAMMER_ALIST_BLOCK_NONE) {
554 		fprintf(stderr, "volume %d %s has insufficient space\n",
555 			vol->vol_no, vol->name);
556 		exit(1);
557 	}
558 	cluster = get_cluster(vol, clno);
559 	printf("allocate cluster id=%016llx %d@%08llx\n",
560 	       clu_id, clno, cluster->clu_offset);
561 
562 	ondisk = cluster->ondisk;
563 
564 	ondisk->vol_fsid = vol->ondisk->vol_fsid;
565 	ondisk->vol_fstype = vol->ondisk->vol_fstype;
566 	ondisk->clu_gen = 1;
567 	ondisk->clu_id = clu_id;
568 	ondisk->clu_no = clno;
569 	ondisk->clu_flags = 0;
570 	ondisk->clu_start = HAMMER_BUFSIZE;
571 	if (vol->size - cluster->clu_offset > ClusterSize)
572 		ondisk->clu_limit = (u_int32_t)ClusterSize;
573 	else
574 		ondisk->clu_limit = (u_int32_t)(vol->size - cluster->clu_offset);
575 
576 	/*
577 	 * In-band filesystem buffer management A-List.  The first filesystem
578 	 * buffer is the cluster header itself.
579 	 */
580 	nbuffers = ondisk->clu_limit / HAMMER_BUFSIZE;
581 	hammer_alist_free(&cluster->alist_master, 1, nbuffers - 1);
582 	printf("cluster %d has %d buffers\n", cluster->clu_no, nbuffers);
583 
584 	/*
585 	 * Buffer Iterators in elements.  Each buffer has 256 elements.
586 	 * The data and B-Tree indices are forward allocations while the
587 	 * record index allocates backwards.
588 	 */
589 	ondisk->idx_data = 1 * HAMMER_FSBUF_MAXBLKS;
590 	ondisk->idx_index = 0 * HAMMER_FSBUF_MAXBLKS;
591 	ondisk->idx_record = nbuffers * HAMMER_FSBUF_MAXBLKS;
592 
593 	/*
594 	 * Initialize root cluster's parent cluster info.  -1's
595 	 * indicate we are the root cluster and no parent exists.
596 	 */
597 	ondisk->clu_btree_parent_vol_no = -1;
598 	ondisk->clu_btree_parent_clu_no = -1;
599 	ondisk->clu_btree_parent_offset = -1;
600 	ondisk->clu_btree_parent_clu_gen = -1;
601 
602 	/*
603 	 * Cluster 0 is the root cluster.  Set the B-Tree range for this
604 	 * cluster to the entire key space and format the root directory.
605 	 *
606 	 * Note that delete_tid for the ending range must be set to 0,
607 	 * 0 indicates 'not deleted', aka 'the most recent'.  See
608 	 * hammer_btree_cmp() in sys/vfs/hammer/hammer_btree.c.
609 	 *
610 	 * The root cluster's key space represents the entire key space for
611 	 * the filesystem.  The btree_end element appears to be inclusive
612 	 * only because we can't overflow our variables.  It's actually
613 	 * non-inclusive... that is, it is a right-side boundary element.
614 	 */
615 	if (isroot) {
616 		ondisk->clu_btree_beg.obj_id = -0x8000000000000000LL;
617 		ondisk->clu_btree_beg.key = -0x8000000000000000LL;
618 		ondisk->clu_btree_beg.create_tid = 0;
619 		ondisk->clu_btree_beg.delete_tid = 0;
620 		ondisk->clu_btree_beg.rec_type = 0;
621 		ondisk->clu_btree_beg.obj_type = 0;
622 
623 		ondisk->clu_btree_end.obj_id = 0x7FFFFFFFFFFFFFFFLL;
624 		ondisk->clu_btree_end.key = 0x7FFFFFFFFFFFFFFFLL;
625 		ondisk->clu_btree_end.create_tid = 0xFFFFFFFFFFFFFFFFULL;
626 		ondisk->clu_btree_end.delete_tid = 0;	/* special case */
627 		ondisk->clu_btree_end.rec_type = 0xFFFFU;
628 		ondisk->clu_btree_end.obj_type = 0;
629 
630 		format_root(cluster);
631 	}
632 
633 	/*
634 	 * Write-out and update the index, record, and cluster buffers
635 	 */
636 	return(clno);
637 }
638 
639 /*
640  * Format the root directory.
641  */
642 static
643 void
644 format_root(struct cluster_info *cluster)
645 {
646 	int32_t btree_off;
647 	int32_t rec_off;
648 	int32_t data_off;
649 	hammer_node_ondisk_t bnode;
650 	union hammer_record_ondisk *rec;
651 	struct hammer_inode_data *idata;
652 	hammer_btree_elm_t elm;
653 
654 	bnode = alloc_btree_element(cluster, &btree_off);
655 	rec = alloc_record_element(cluster, &rec_off);
656 	idata = alloc_data_element(cluster, sizeof(*idata), &data_off);
657 
658 	/*
659 	 * Populate the inode data and inode record for the root directory.
660 	 */
661 	idata->version = HAMMER_INODE_DATA_VERSION;
662 	idata->mode = 0755;
663 
664 	rec->base.base.obj_id = 1;
665 	rec->base.base.key = 0;
666 	rec->base.base.create_tid = createtid();
667 	rec->base.base.delete_tid = 0;
668 	rec->base.base.rec_type = HAMMER_RECTYPE_INODE;
669 	rec->base.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
670 	rec->base.data_offset = data_off;
671 	rec->base.data_len = sizeof(*idata);
672 	rec->base.data_crc = crc32(idata, sizeof(*idata));
673 	rec->inode.ino_atime  = rec->base.base.create_tid;
674 	rec->inode.ino_mtime  = rec->base.base.create_tid;
675 	rec->inode.ino_size   = 0;
676 	rec->inode.ino_nlinks = 1;
677 
678 	/*
679 	 * Assign the cluster's root B-Tree node.
680 	 */
681 	assert(cluster->ondisk->clu_btree_root == 0);
682 	cluster->ondisk->clu_btree_root = btree_off;
683 
684 	/*
685 	 * Create the root of the B-Tree.  The root is a leaf node so we
686 	 * do not have to worry about boundary elements.
687 	 */
688 	bnode->count = 1;
689 	bnode->type = HAMMER_BTREE_TYPE_LEAF;
690 
691 	elm = &bnode->elms[0];
692 	elm->base = rec->base.base;
693 	elm->leaf.rec_offset = rec_off;
694 	elm->leaf.data_offset = rec->base.data_offset;
695 	elm->leaf.data_len = rec->base.data_len;
696 	elm->leaf.data_crc = rec->base.data_crc;
697 }
698 
699 void
700 panic(const char *ctl, ...)
701 {
702 	va_list va;
703 
704 	va_start(va, ctl);
705 	vfprintf(stderr, ctl, va);
706 	va_end(va);
707 	fprintf(stderr, "\n");
708 	exit(1);
709 }
710 
711