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