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