Merge from vendor branch OPENSSL:
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
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.2 2007/10/16 18:30:53 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:s:S")) != -1) {
116                 switch(ch) {
117                 case 'L':
118                         label = optarg;
119                         break;
120                 case 's':
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_SUPERCL_ENABLE |
416                                     HAMMER_VOLF_SUPERCL_RESERVE;
417         }
418
419         ondisk->vol_beg = vol->vol_cluster_off;
420         ondisk->vol_end = vol->size;
421
422         if (ondisk->vol_end < ondisk->vol_beg) {
423                 errx(1, "volume %d %s is too small to hold the volume header",
424                      vol->vol_no, vol->name);
425         }
426
427         /*
428          * Our A-lists have been initialized but are marked all-allocated.
429          * Calculate the actual number of clusters in the volume and free
430          * them to get the filesystem ready for work.  The clusters will
431          * be initialized on-demand.
432          *
433          * If using super-clusters we must still calculate nclusters but
434          * we only need to initialize superclusters that are not going
435          * to wind up in the all-free state, which will only be the last
436          * supercluster.  hammer_alist_free() will recurse into the
437          * supercluster infrastructure and create the necessary superclusters.
438          *
439          * NOTE: The nclusters calculation ensures that the volume EOF does
440          * not occur in the middle of a supercluster buffer array.
441          */
442         if (UsingSuperClusters) {
443                 /*
444                  * Figure out how many full super-cluster groups we will have.
445                  * This calculation does not include the partial supercluster
446                  * group at the end.
447                  */
448                 scl_header_size = (int64_t)HAMMER_BUFSIZE *
449                                   HAMMER_VOL_SUPERCLUSTER_GROUP;
450                 scl_group_size = scl_header_size +
451                                  (int64_t)HAMMER_VOL_SUPERCLUSTER_GROUP *
452                                  ClusterSize * HAMMER_SCL_MAXCLUSTERS;
453                 nscl_groups = (ondisk->vol_end - ondisk->vol_beg) /
454                                 scl_group_size;
455                 nclusters = nscl_groups * HAMMER_SCL_MAXCLUSTERS *
456                                 HAMMER_VOL_SUPERCLUSTER_GROUP;
457
458                 /*
459                  * Figure out how much space we have left and calculate the
460                  * remaining number of clusters.
461                  */
462                 n64 = (ondisk->vol_end - ondisk->vol_beg) -
463                         (nscl_groups * scl_group_size);
464                 if (n64 > scl_header_size) {
465                         nclusters += (n64 + minclsize) / ClusterSize;
466                 }
467                 printf("%d clusters, %d full super-cluster groups\n",
468                         nclusters, nscl_groups);
469                 hammer_alist_free(&vol->alist, 0, nclusters);
470         } else {
471                 nclusters = (ondisk->vol_end - ondisk->vol_beg + minclsize) /
472                             ClusterSize;
473                 if (nclusters > HAMMER_VOL_MAXCLUSTERS) {
474                         errx(1, "Volume is too large, max %s\n",
475                              sizetostr((int64_t)nclusters * ClusterSize));
476                 }
477                 hammer_alist_free(&vol->alist, 0, nclusters);
478         }
479         ondisk->vol_nclusters = nclusters;
480
481         /*
482          * Place the root cluster in volume 0.
483          */
484         ondisk->vol_rootvol = 0;
485         if (ondisk->vol_no == ondisk->vol_rootvol) {
486                 ondisk->vol0_rootcluster = format_cluster(vol, 1);
487                 ondisk->vol0_recid = 1;
488                 /* global next TID */
489                 ondisk->vol0_nexttid = createtid();
490         }
491 }
492
493 /*
494  * Format a hammer cluster.  Returns byte offset in volume of cluster.
495  */
496 static
497 int32_t
498 format_cluster(struct volume_info *vol, int isroot)
499 {
500         hammer_tid_t clu_id = createtid();
501         struct cluster_info *cluster;
502         struct hammer_cluster_ondisk *ondisk;
503         int nbuffers;
504         int clno;
505
506         /*
507          * Allocate a cluster
508          */
509         clno = hammer_alist_alloc(&vol->alist, 1);
510         if (clno == HAMMER_ALIST_BLOCK_NONE) {
511                 fprintf(stderr, "volume %d %s has insufficient space\n",
512                         vol->vol_no, vol->name);
513                 exit(1);
514         }
515         cluster = get_cluster(vol, clno);
516         printf("allocate cluster id=%016llx %d@%08llx\n",
517                clu_id, clno, cluster->clu_offset);
518
519         ondisk = cluster->ondisk;
520
521         ondisk->vol_fsid = vol->ondisk->vol_fsid;
522         ondisk->vol_fstype = vol->ondisk->vol_fstype;
523         ondisk->clu_gen = 1;
524         ondisk->clu_id = clu_id;
525         ondisk->clu_no = clno;
526         ondisk->clu_flags = 0;
527         ondisk->clu_start = HAMMER_BUFSIZE;
528         if (vol->size - cluster->clu_offset > ClusterSize)
529                 ondisk->clu_limit = ClusterSize;
530         else
531                 ondisk->clu_limit = (u_int32_t)(vol->size - cluster->clu_offset);
532
533         /*
534          * In-band filesystem buffer management A-List.  The first filesystem
535          * buffer is the cluster header itself.
536          */
537         nbuffers = ondisk->clu_limit / HAMMER_BUFSIZE;
538         hammer_alist_free(&cluster->alist_master, 1, nbuffers - 1);
539         printf("cluster %d has %d buffers\n", cluster->clu_no, nbuffers);
540
541         /*
542          * Buffer Iterators in elements.  Each buffer has 256 elements.
543          * The data and B-Tree indices are forward allocations while the
544          * record index allocates backwards.
545          */
546         ondisk->idx_data = 1 * HAMMER_FSBUF_MAXBLKS;
547         ondisk->idx_index = 0 * HAMMER_FSBUF_MAXBLKS;
548         ondisk->idx_record = nbuffers * HAMMER_FSBUF_MAXBLKS;
549
550         /* XXX root cluster */
551         ondisk->clu_btree_parent_vol_no = 0;
552         ondisk->clu_btree_parent_clu_no = 0;
553         ondisk->clu_btree_parent_clu_id = 0;
554
555         /*
556          * Cluster 0 is the root cluster.  Set the B-Tree range for this
557          * cluster to the entire key space and format the root directory. 
558          */
559         if (isroot) {
560                 ondisk->clu_btree_beg.obj_id = -0x8000000000000000LL;
561                 ondisk->clu_btree_beg.key = -0x8000000000000000LL;
562                 ondisk->clu_btree_beg.create_tid = 0;
563                 ondisk->clu_btree_beg.delete_tid = 0;
564                 ondisk->clu_btree_beg.rec_type = 0;
565                 ondisk->clu_btree_beg.obj_type = 0;
566
567                 ondisk->clu_btree_end.obj_id = 0x7FFFFFFFFFFFFFFFLL;
568                 ondisk->clu_btree_end.key = 0x7FFFFFFFFFFFFFFFLL;
569                 ondisk->clu_btree_end.create_tid = 0xFFFFFFFFFFFFFFFFULL;
570                 ondisk->clu_btree_end.delete_tid = 0xFFFFFFFFFFFFFFFFULL;
571                 ondisk->clu_btree_end.rec_type = 0xFFFFU;
572                 ondisk->clu_btree_end.obj_type = 0xFFFFU;
573
574                 format_root(cluster);
575         }
576
577         /*
578          * Write-out and update the index, record, and cluster buffers
579          */
580         return(clno);
581 }
582
583 /*
584  * Format the root directory.
585  */
586 static
587 void
588 format_root(struct cluster_info *cluster)
589 {
590         int32_t btree_off;
591         int32_t rec_off;
592         int32_t data_off;
593         struct hammer_btree_node *bnode;
594         union hammer_record_ondisk *rec;
595         struct hammer_inode_data *idata;
596         struct hammer_record_elm *elm;
597
598         bnode = alloc_btree_element(cluster, &btree_off);
599         rec = alloc_record_element(cluster, &rec_off);
600         idata = alloc_data_element(cluster, sizeof(*idata), &data_off);
601
602         /*
603          * Populate the inode data and inode record for the root directory.
604          */
605         idata->version = HAMMER_INODE_DATA_VERSION;
606         idata->mode = 0755;
607
608         rec->base.obj_id = 1;
609         rec->base.key = 0;
610         rec->base.create_tid = createtid();
611         rec->base.delete_tid = 0;
612         rec->base.rec_type = HAMMER_RECTYPE_INODE;
613         rec->base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
614         rec->base.data_offset = data_off;
615         rec->base.data_len = sizeof(*idata);
616         rec->base.data_crc = crc32(idata, sizeof(*idata));
617         rec->inode.ino_atime  = rec->base.create_tid;
618         rec->inode.ino_mtime  = rec->base.create_tid;
619         rec->inode.ino_size   = 0;
620         rec->inode.ino_nlinks = 1;
621
622         /*
623          * Assign the cluster's root B-Tree node.
624          */
625         assert(cluster->ondisk->clu_btree_root == 0);
626         cluster->ondisk->clu_btree_root = btree_off;
627
628         /*
629          * Create a root with a single element pointing to the inode
630          * record.
631          */
632         bnode->count = 1;
633
634         elm = &bnode->elms[0].record;
635         elm->base.obj_id = rec->base.obj_id;
636         elm->base.key = rec->base.key;
637         elm->base.create_tid = rec->base.create_tid;
638         elm->base.delete_tid = rec->base.delete_tid;
639         elm->base.rec_type = rec->base.rec_type;
640         elm->base.obj_type = rec->base.obj_type;
641
642         elm->rec_offset = rec_off;
643         elm->data_offset = rec->base.data_offset;
644         elm->data_len = rec->base.data_len;
645         elm->data_crc = rec->base.data_crc;
646 }
647
648 void
649 panic(const char *ctl, ...)
650 {
651         va_list va;
652
653         va_start(va, ctl);
654         vfprintf(stderr, ctl, va);
655         va_end(va);
656         fprintf(stderr, "\n");
657         exit(1);
658 }
659