2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
34 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.2 2007/10/16 18:30:53 dillon Exp $
37 #include "newfs_hammer.h"
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);
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;
56 int UsingSuperClusters;
58 struct volume_info *VolBase;
61 main(int ac, char **av)
67 int64_t max_volume_size;
68 const char *label = NULL;
71 * Sanity check basic filesystem structures. No cookies for us
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);
84 * Generate a filesysem id and lookup the filesystem type
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");
94 * Initialize the alist templates we will be using
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);
115 while ((ch = getopt(ac, av, "L:s:S")) != -1) {
121 ClusterSize = getsize(optarg,
122 HAMMER_BUFSIZE * 256LL,
123 HAMMER_CLU_MAXBYTES, 1);
127 * Force the use of super-clusters
129 UsingSuperClusters = 1;
139 "newfs_hammer: A filesystem label must be specified\n");
144 * Collect volume information
151 for (i = 0; i < NumVolumes; ++i) {
152 struct volume_info *vol;
154 vol = calloc(1, sizeof(struct volume_info));
162 * Load up information on the volume and initialize
163 * its remaining fields.
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.
174 if (ClusterSize == 0) {
175 ClusterSize = HAMMER_BUFSIZE * 256;
176 while (ClusterSize < total / NumVolumes / 256 &&
177 ClusterSize < HAMMER_CLU_MAXBYTES) {
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));
187 if (UsingSuperClusters) {
188 max_volume_size = (int64_t)HAMMER_VOL_MAXSUPERCLUSTERS * \
189 HAMMER_SCL_MAXCLUSTERS * ClusterSize;
191 max_volume_size = (int64_t)HAMMER_VOL_MAXCLUSTERS * ClusterSize;
193 printf("max-volume-size: %s\n", sizetostr(max_volume_size));
195 printf("max-filesystem-size: %s\n",
196 (max_volume_size * 32768LL < max_volume_size) ?
198 sizetostr(max_volume_size * 32768LL));
202 * Format the volumes.
204 for (i = 0; i < NumVolumes; ++i) {
205 format_volume(get_volume(i), NumVolumes, label);
215 fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n");
220 * Convert the size in bytes to a human readable string.
223 sizetostr(off_t size)
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));
239 snprintf(buf, sizeof(buf), "%6.2fTB",
240 (double)size / (1024 * 1024 * 1024LL * 1024LL));
246 * Convert a string to a 64 bit signed integer with various requirements.
249 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
254 val = strtoll(str, &ptr, 0);
273 errx(1, "Unknown suffix in number '%s'\n", str);
277 errx(1, "Unknown suffix in number '%s'\n", str);
281 errx(1, "Value too small: %s, min is %s\n",
282 str, sizetostr(minval));
286 errx(1, "Value too large: %s, max is %s\n",
287 str, sizetostr(maxval));
290 if (powerof2 && (val ^ (val - 1)) != ((val << 1) - 1)) {
291 errx(1, "Value not power of 2: %s\n", str);
298 * Generate a transaction id
303 static hammer_tid_t lasttid;
307 gettimeofday(&tv, NULL);
308 lasttid = tv.tv_sec * 1000000000LL +
315 * Check basic volume characteristics. HAMMER filesystems use a minimum
316 * of a 16KB filesystem buffer size.
320 check_volume(struct volume_info *vol)
322 struct partinfo pinfo;
326 * Get basic information about the volume
328 vol->fd = open(vol->name, O_RDWR);
330 err(1, "Unable to open %s R+W", vol->name);
331 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
333 * Allow the formatting of regular filews as HAMMER volumes
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";
341 * When formatting a block device as a HAMMER volume the
342 * sector size must be compatible. HAMMER uses 16384 byte
343 * filesystem buffers.
345 if (pinfo.reserved_blocks) {
346 errx(1, "HAMMER cannot be placed in a partition "
347 "which overlaps the disklabel or MBR");
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);
355 vol->size = pinfo.media_size;
356 vol->type = "DEVICE";
358 printf("Volume %d %s %-15s size %s\n",
359 vol->vol_no, vol->type, vol->name,
360 sizetostr(vol->size));
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.
368 if (vol->size > 200LL * 1024 * 1024 * 1024 && !UsingSuperClusters) {
369 UsingSuperClusters = 1;
370 printf("Enabling super-clusters\n");
374 * Reserve space for (future) boot junk
376 vol->vol_cluster_off = HAMMER_BUFSIZE * 16;
380 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
384 format_volume(struct volume_info *vol, int nvols, const char *label)
386 struct hammer_volume_ondisk *ondisk;
390 int64_t scl_group_size;
391 int64_t scl_header_size;
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.
398 minclsize = ClusterSize / 4;
399 if (minclsize < HAMMER_BUFSIZE * 64)
400 minclsize = HAMMER_BUFSIZE * 64;
403 * Initialize basic information in the on-disk volume structure.
405 ondisk = vol->ondisk;
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;
419 ondisk->vol_beg = vol->vol_cluster_off;
420 ondisk->vol_end = vol->size;
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);
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.
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.
439 * NOTE: The nclusters calculation ensures that the volume EOF does
440 * not occur in the middle of a supercluster buffer array.
442 if (UsingSuperClusters) {
444 * Figure out how many full super-cluster groups we will have.
445 * This calculation does not include the partial supercluster
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) /
455 nclusters = nscl_groups * HAMMER_SCL_MAXCLUSTERS *
456 HAMMER_VOL_SUPERCLUSTER_GROUP;
459 * Figure out how much space we have left and calculate the
460 * remaining number of clusters.
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;
467 printf("%d clusters, %d full super-cluster groups\n",
468 nclusters, nscl_groups);
469 hammer_alist_free(&vol->alist, 0, nclusters);
471 nclusters = (ondisk->vol_end - ondisk->vol_beg + minclsize) /
473 if (nclusters > HAMMER_VOL_MAXCLUSTERS) {
474 errx(1, "Volume is too large, max %s\n",
475 sizetostr((int64_t)nclusters * ClusterSize));
477 hammer_alist_free(&vol->alist, 0, nclusters);
479 ondisk->vol_nclusters = nclusters;
482 * Place the root cluster in volume 0.
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();
494 * Format a hammer cluster. Returns byte offset in volume of cluster.
498 format_cluster(struct volume_info *vol, int isroot)
500 hammer_tid_t clu_id = createtid();
501 struct cluster_info *cluster;
502 struct hammer_cluster_ondisk *ondisk;
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);
515 cluster = get_cluster(vol, clno);
516 printf("allocate cluster id=%016llx %d@%08llx\n",
517 clu_id, clno, cluster->clu_offset);
519 ondisk = cluster->ondisk;
521 ondisk->vol_fsid = vol->ondisk->vol_fsid;
522 ondisk->vol_fstype = vol->ondisk->vol_fstype;
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;
531 ondisk->clu_limit = (u_int32_t)(vol->size - cluster->clu_offset);
534 * In-band filesystem buffer management A-List. The first filesystem
535 * buffer is the cluster header itself.
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);
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.
546 ondisk->idx_data = 1 * HAMMER_FSBUF_MAXBLKS;
547 ondisk->idx_index = 0 * HAMMER_FSBUF_MAXBLKS;
548 ondisk->idx_record = nbuffers * HAMMER_FSBUF_MAXBLKS;
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;
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.
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;
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;
574 format_root(cluster);
578 * Write-out and update the index, record, and cluster buffers
584 * Format the root directory.
588 format_root(struct cluster_info *cluster)
593 struct hammer_btree_node *bnode;
594 union hammer_record_ondisk *rec;
595 struct hammer_inode_data *idata;
596 struct hammer_record_elm *elm;
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);
603 * Populate the inode data and inode record for the root directory.
605 idata->version = HAMMER_INODE_DATA_VERSION;
608 rec->base.obj_id = 1;
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;
623 * Assign the cluster's root B-Tree node.
625 assert(cluster->ondisk->clu_btree_root == 0);
626 cluster->ondisk->clu_btree_root = btree_off;
629 * Create a root with a single element pointing to the inode
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;
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;
649 panic(const char *ctl, ...)
654 vfprintf(stderr, ctl, va);
656 fprintf(stderr, "\n");