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.16 2008/01/25 21:52:10 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 static int64_t ClusterSize;
50 main(int ac, char **av)
56 int64_t max_volume_size;
57 const char *label = NULL;
60 * Sanity check basic filesystem structures. No cookies for us
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);
73 * Generate a filesysem id and lookup the filesystem type
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");
82 init_alist_templates();
87 while ((ch = getopt(ac, av, "L:b:c:m:S")) != -1) {
93 BootAreaSize = getsize(optarg,
95 HAMMER_BOOT_MAXBYTES, 2);
98 ClusterSize = getsize(optarg,
99 HAMMER_BUFSIZE * 256LL,
100 HAMMER_CLU_MAXBYTES, 1);
103 MemAreaSize = getsize(optarg,
105 HAMMER_MEM_MAXBYTES, 2);
109 * Force the use of super-clusters
111 UsingSuperClusters = 1;
121 "newfs_hammer: A filesystem label must be specified\n");
126 * Collect volume information
133 for (i = 0; i < NumVolumes; ++i) {
134 struct volume_info *vol;
136 vol = setup_volume(i, av[i], 1, O_RDWR);
139 * Load up information on the volume and initialize
140 * its remaining fields.
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.
151 if (ClusterSize == 0) {
152 ClusterSize = HAMMER_BUFSIZE * 256;
153 while (ClusterSize < total / NumVolumes / 256 &&
154 ClusterSize < HAMMER_CLU_MAXBYTES) {
160 * Calculate defaults for the boot and memory area sizes.
162 if (BootAreaSize == 0) {
163 BootAreaSize = HAMMER_BOOT_NOMBYTES;
164 while (BootAreaSize > total / NumVolumes / 256)
166 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
168 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
169 BootAreaSize = HAMMER_BOOT_MINBYTES;
171 if (MemAreaSize == 0) {
172 MemAreaSize = HAMMER_MEM_NOMBYTES;
173 while (MemAreaSize > total / NumVolumes / 256)
175 if (MemAreaSize < HAMMER_MEM_MINBYTES)
177 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
178 MemAreaSize = HAMMER_MEM_MINBYTES;
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));
186 if (UsingSuperClusters) {
187 max_volume_size = (int64_t)HAMMER_VOL_MAXSUPERCLUSTERS * \
188 HAMMER_SCL_MAXCLUSTERS * ClusterSize;
190 max_volume_size = HAMMER_VOL_MAXCLUSTERS * ClusterSize;
192 printf("max-volume-size: %s\n", sizetostr(max_volume_size));
194 printf("max-filesystem-size: %s\n",
195 (max_volume_size * 32768LL < max_volume_size) ?
197 sizetostr(max_volume_size * 32768LL));
198 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
199 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
203 * Format the volumes.
205 for (i = 0; i < NumVolumes; ++i) {
206 format_volume(get_volume(i), NumVolumes, label);
216 fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n");
221 * Convert the size in bytes to a human readable string.
224 sizetostr(off_t size)
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));
240 snprintf(buf, sizeof(buf), "%6.2fTB",
241 (double)size / (1024 * 1024 * 1024LL * 1024LL));
247 * Convert a string to a 64 bit signed integer with various requirements.
250 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
255 val = strtoll(str, &ptr, 0);
274 errx(1, "Unknown suffix in number '%s'\n", str);
278 errx(1, "Unknown suffix in number '%s'\n", str);
282 errx(1, "Value too small: %s, min is %s\n",
283 str, sizetostr(minval));
287 errx(1, "Value too large: %s, max is %s\n",
288 str, sizetostr(maxval));
291 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
292 errx(1, "Value not power of 2: %s\n", str);
295 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
296 errx(1, "Value not an integral multiple of %dK: %s",
297 HAMMER_BUFSIZE / 1024, str);
304 * Generate a transaction id
309 static hammer_tid_t lasttid;
313 gettimeofday(&tv, NULL);
314 lasttid = tv.tv_sec * 1000000000LL +
321 * Check basic volume characteristics. HAMMER filesystems use a minimum
322 * of a 16KB filesystem buffer size.
326 check_volume(struct volume_info *vol)
328 struct partinfo pinfo;
332 * Get basic information about the volume
334 vol->fd = open(vol->name, O_RDWR);
336 err(1, "Unable to open %s R+W", vol->name);
337 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
339 * Allow the formatting of regular filews as HAMMER volumes
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";
347 * When formatting a block device as a HAMMER volume the
348 * sector size must be compatible. HAMMER uses 16384 byte
349 * filesystem buffers.
351 if (pinfo.reserved_blocks) {
352 errx(1, "HAMMER cannot be placed in a partition "
353 "which overlaps the disklabel or MBR");
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);
361 vol->size = pinfo.media_size;
362 vol->type = "DEVICE";
364 printf("Volume %d %s %-15s size %s\n",
365 vol->vol_no, vol->type, vol->name,
366 sizetostr(vol->size));
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.
374 if (vol->size > 200LL * 1024 * 1024 * 1024 && !UsingSuperClusters) {
375 UsingSuperClusters = 1;
376 printf("Enabling super-clusters\n");
380 * Reserve space for (future) header junk
382 vol->vol_alloc = HAMMER_BUFSIZE * 16;
386 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
390 format_volume(struct volume_info *vol, int nvols, const char *label)
392 struct hammer_volume_ondisk *ondisk;
396 int64_t scl_group_size;
397 int64_t scl_header_size;
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.
404 minclsize = (int32_t)(ClusterSize / 4);
405 if (minclsize < HAMMER_BUFSIZE * 64)
406 minclsize = HAMMER_BUFSIZE * 64;
409 * Initialize basic information in the on-disk volume structure.
411 ondisk = vol->ondisk;
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;
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;
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);
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.
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.
447 * NOTE: The nclusters calculation ensures that the volume EOF does
448 * not occur in the middle of a supercluster buffer array.
450 if (UsingSuperClusters) {
452 * Figure out how many full super-cluster groups we will have.
453 * This calculation does not include the partial supercluster
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) /
463 nclusters = nscl_groups * HAMMER_SCL_MAXCLUSTERS *
464 HAMMER_VOL_SUPERCLUSTER_GROUP;
467 * Figure out how much space we have left and calculate the
468 * remaining number of clusters.
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;
475 printf("%d clusters, %d full super-cluster groups\n",
476 nclusters, nscl_groups);
477 hammer_alist_init(&vol->clu_alist, 0, nclusters,
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));
486 hammer_alist_init(&vol->clu_alist, 0, nclusters,
489 ondisk->vol_nclusters = nclusters;
490 ondisk->vol_nblocks = nclusters * ClusterSize / HAMMER_BUFSIZE -
492 ondisk->vol_blocksize = HAMMER_BUFSIZE;
495 * Place the root cluster in volume 0.
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();
507 * Format a hammer cluster. Returns byte offset in volume of cluster.
511 format_cluster(struct volume_info *vol, int isroot)
513 hammer_tid_t clu_id = createtid();
514 struct cluster_info *cluster;
515 struct hammer_cluster_ondisk *ondisk;
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);
528 cluster = get_cluster(vol, clno, 1);
529 printf("allocate cluster id=%016llx %d@%08llx\n",
530 clu_id, clno, cluster->clu_offset);
532 ondisk = cluster->ondisk;
534 ondisk->vol_fsid = vol->ondisk->vol_fsid;
535 ondisk->vol_fstype = vol->ondisk->vol_fstype;
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;
544 ondisk->clu_limit = (u_int32_t)(vol->size - cluster->clu_offset);
547 * In-band filesystem buffer management A-List. The first filesystem
548 * buffer is the cluster header itself.
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);
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.
559 ondisk->idx_data = 1 * HAMMER_FSBUF_MAXBLKS;
560 ondisk->idx_index = 0 * HAMMER_FSBUF_MAXBLKS;
561 ondisk->idx_record = nbuffers * HAMMER_FSBUF_MAXBLKS;
564 * Iterator for whole-buffer data allocations. The iterator is
567 ondisk->idx_ldata = 1;
570 * Initialize root cluster's parent cluster info. -1's
571 * indicate we are the root cluster and no parent exists.
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;
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.
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.
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.
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;
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;
606 format_root(cluster);
610 * Write-out and update the index, record, and cluster buffers
616 * Format the root directory.
620 format_root(struct cluster_info *cluster)
625 hammer_node_ondisk_t bnode;
626 union hammer_record_ondisk *rec;
627 struct hammer_inode_data *idata;
628 hammer_btree_elm_t elm;
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);
635 * Populate the inode data and inode record for the root directory.
637 idata->version = HAMMER_INODE_DATA_VERSION;
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;
656 ++cluster->volume->ondisk->vol0_stat_inodes;
659 * Assign the cluster's root B-Tree node.
661 assert(cluster->ondisk->clu_btree_root == 0);
662 cluster->ondisk->clu_btree_root = btree_off;
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.
669 bnode->type = HAMMER_BTREE_TYPE_LEAF;
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;