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.44 2008/08/21 23:32:27 thomas 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,
44 static hammer_off_t format_root(const char *label);
45 static u_int64_t nowtime(void);
46 static void usage(void);
48 static int ForceOpt = 0;
49 static int HammerVersion = -1;
51 #define GIG (1024LL*1024*1024)
54 main(int ac, char **av)
60 const char *label = NULL;
61 struct volume_info *vol;
65 * Sanity check basic filesystem structures. No cookies for us
68 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
69 assert(sizeof(struct hammer_blockmap_layer1) == 32);
70 assert(sizeof(struct hammer_blockmap_layer2) == 16);
73 * Generate a filesystem 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");
85 while ((ch = getopt(ac, av, "fL:b:m:u:V:")) != -1) {
94 BootAreaSize = getsize(optarg,
96 HAMMER_BOOT_MAXBYTES, 2);
99 MemAreaSize = getsize(optarg,
101 HAMMER_MEM_MAXBYTES, 2);
104 UndoBufferSize = getsize(optarg,
105 HAMMER_LARGEBLOCK_SIZE,
106 HAMMER_LARGEBLOCK_SIZE *
107 HAMMER_UNDO_LAYER2, 2);
108 if (UndoBufferSize < 100*1024*1024 && ForceOpt == 0)
109 errx(1, "The minimum UNDO fifo size is 100MB\n");
110 if (UndoBufferSize < 100*1024*1024) {
112 "WARNING: you have specified an UNDO "
113 "FIFO size less than 100MB, which may\n"
114 "lead to VFS panics.\n");
118 HammerVersion = strtol(optarg, NULL, 0);
119 if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
120 HammerVersion >= HAMMER_VOL_VERSION_WIP) {
122 "I don't understand how to format "
123 "HAMMER version %d\n",
135 "newfs_hammer: A filesystem label must be specified\n");
139 if (HammerVersion < 0) {
140 size_t olen = sizeof(HammerVersion);
141 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
142 if (sysctlbyname("vfs.hammer.supported_version",
143 &HammerVersion, &olen, NULL, 0) == 0) {
144 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
145 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
147 "newfs_hammer: WARNING: HAMMER VFS "
148 "supports higher version than I "
150 "using version %d\n",
155 "newfs_hammer: WARNING: HAMMER VFS not "
156 "loaded, cannot get version info.\n"
157 "Using version %d\n",
158 HAMMER_VOL_VERSION_DEFAULT);
163 * Collect volume information
170 if (NumVolumes == 0) {
172 "newfs_hammer: You must specify at least one special file (volume)\n");
176 if (NumVolumes > HAMMER_MAX_VOLUMES) {
178 "newfs_hammer: The maximum number of volumes is %d\n", HAMMER_MAX_VOLUMES);
183 for (i = 0; i < NumVolumes; ++i) {
184 vol = setup_volume(i, av[i], 1, O_RDWR);
187 * Load up information on the volume and initialize
188 * its remaining fields.
195 * Calculate defaults for the boot and memory area sizes.
197 if (BootAreaSize == 0) {
198 BootAreaSize = HAMMER_BOOT_NOMBYTES;
199 while (BootAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
201 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
203 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
204 BootAreaSize = HAMMER_BOOT_MINBYTES;
206 if (MemAreaSize == 0) {
207 MemAreaSize = HAMMER_MEM_NOMBYTES;
208 while (MemAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
210 if (MemAreaSize < HAMMER_MEM_MINBYTES)
212 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
213 MemAreaSize = HAMMER_MEM_MINBYTES;
217 * Format the volumes. Format the root volume first so we can
218 * bootstrap the freemap.
220 format_volume(get_volume(RootVolNo), NumVolumes, label, total);
221 for (i = 0; i < NumVolumes; ++i) {
223 format_volume(get_volume(i), NumVolumes, label, total);
227 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
228 * limit. If we do this the filesystem does not have to allocate
229 * new layer2 blocks which reduces the chances of the reblocker
230 * having to fallback to an extremely inefficient algorithm.
232 vol = get_volume(RootVolNo);
233 vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks;
234 vol->cache.modified = 1;
235 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
237 printf("---------------------------------------------\n");
238 printf("%d volume%s total size %s version %d\n",
239 NumVolumes, (NumVolumes == 1 ? "" : "s"),
240 sizetostr(total), HammerVersion);
241 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
242 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
243 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
244 printf("total-pre-allocated: %s\n",
245 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
246 printf("fsid: %s\n", fsidstr);
248 printf("NOTE: Please remember that you may have to manually set up a\n"
249 "cron(8) job to prune and reblock the filesystem regularly.\n"
250 "By default, the system automatically runs 'hammer cleanup'\n"
251 "on a nightly basis. The periodic.conf(5) variable\n"
252 "'daily_clean_hammer_enable' can be unset to disable this.\n"
253 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
254 if (total < 10*GIG) {
255 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you"
256 "really should not\n"
257 " try to format a HAMMER filesystem this small\n");
259 if (total < 50*GIG) {
260 printf("\nWARNING: HAMMER filesystems less than 50GB are "
262 "You may have to run 'hammer prune-everything' and "
264 "quite often, even if using a nohistory mount.\n");
275 "usage: newfs_hammer -L label [-f] [-b bootsize] [-m savesize] [-u undosize]\n"
276 " [-V version] special ...\n"
282 * Convert the size in bytes to a human readable string.
286 sizetostr(off_t size)
290 if (size < 1024 / 2) {
291 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
292 } else if (size < 1024 * 1024 / 2) {
293 snprintf(buf, sizeof(buf), "%6.2fKB",
294 (double)size / 1024);
295 } else if (size < 1024 * 1024 * 1024LL / 2) {
296 snprintf(buf, sizeof(buf), "%6.2fMB",
297 (double)size / (1024 * 1024));
298 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
299 snprintf(buf, sizeof(buf), "%6.2fGB",
300 (double)size / (1024 * 1024 * 1024LL));
302 snprintf(buf, sizeof(buf), "%6.2fTB",
303 (double)size / (1024 * 1024 * 1024LL * 1024LL));
309 * Convert a string to a 64 bit signed integer with various requirements.
312 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
317 val = strtoll(str, &ptr, 0);
336 errx(1, "Unknown suffix in number '%s'\n", str);
340 errx(1, "Unknown suffix in number '%s'\n", str);
344 errx(1, "Value too small: %s, min is %s\n",
345 str, sizetostr(minval));
349 errx(1, "Value too large: %s, max is %s\n",
350 str, sizetostr(maxval));
353 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
354 errx(1, "Value not power of 2: %s\n", str);
357 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
358 errx(1, "Value not an integral multiple of %dK: %s",
359 HAMMER_BUFSIZE / 1024, str);
366 * Generate a transaction id. Transaction ids are no longer time-based.
367 * Put the nail in the coffin by not making the first one time-based.
369 * We could start at 1 here but start at 2^32 to reserve a small domain for
370 * possible future use.
375 static hammer_tid_t lasttid;
378 lasttid = 0x0000000100000000ULL;
388 gettimeofday(&tv, NULL);
389 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
394 * Check basic volume characteristics. HAMMER filesystems use a minimum
395 * of a 16KB filesystem buffer size.
399 check_volume(struct volume_info *vol)
401 struct partinfo pinfo;
405 * Get basic information about the volume
407 vol->fd = open(vol->name, O_RDWR);
409 err(1, "Unable to open %s R+W", vol->name);
410 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
412 * Allow the formatting of regular files as HAMMER volumes
414 if (fstat(vol->fd, &st) < 0)
415 err(1, "Unable to stat %s", vol->name);
416 vol->size = st.st_size;
417 vol->type = "REGFILE";
420 * When formatting a block device as a HAMMER volume the
421 * sector size must be compatible. HAMMER uses 16384 byte
422 * filesystem buffers.
424 if (pinfo.reserved_blocks) {
425 errx(1, "HAMMER cannot be placed in a partition "
426 "which overlaps the disklabel or MBR");
428 if (pinfo.media_blksize > 16384 ||
429 16384 % pinfo.media_blksize) {
430 errx(1, "A media sector size of %d is not supported",
431 pinfo.media_blksize);
434 vol->size = pinfo.media_size;
435 vol->type = "DEVICE";
437 printf("Volume %d %s %-15s size %s\n",
438 vol->vol_no, vol->type, vol->name,
439 sizetostr(vol->size));
442 * Reserve space for (future) header junk, setup our poor-man's
443 * bigblock allocator.
445 vol->vol_alloc = HAMMER_BUFSIZE * 16;
449 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
453 format_volume(struct volume_info *vol, int nvols, const char *label,
454 off_t total_size __unused)
456 struct volume_info *root_vol;
457 struct hammer_volume_ondisk *ondisk;
463 * Initialize basic information in the on-disk volume structure.
465 ondisk = vol->ondisk;
467 ondisk->vol_fsid = Hammer_FSId;
468 ondisk->vol_fstype = Hammer_FSType;
469 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
470 ondisk->vol_no = vol->vol_no;
471 ondisk->vol_count = nvols;
472 ondisk->vol_version = HammerVersion;
474 ondisk->vol_bot_beg = vol->vol_alloc;
475 vol->vol_alloc += BootAreaSize;
476 ondisk->vol_mem_beg = vol->vol_alloc;
477 vol->vol_alloc += MemAreaSize;
480 * The remaining area is the zone 2 buffer allocation area. These
483 ondisk->vol_buf_beg = vol->vol_alloc;
484 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
486 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
487 errx(1, "volume %d %s is too small to hold the volume header",
488 vol->vol_no, vol->name);
491 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
493 ondisk->vol_blocksize = HAMMER_BUFSIZE;
495 ondisk->vol_rootvol = RootVolNo;
496 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
498 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
499 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, (ondisk->vol_buf_end - ondisk->vol_buf_beg) & ~HAMMER_LARGEBLOCK_MASK64);
502 * Format the root volume.
504 if (vol->vol_no == RootVolNo) {
508 ondisk->vol0_next_tid = createtid();
511 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
513 freeblks = initialize_freemap(vol);
514 ondisk->vol0_stat_freebigblocks = freeblks;
516 freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64;
517 if (freebytes < 1*GIG && ForceOpt == 0) {
518 errx(1, "Cannot create a HAMMER filesystem less than "
519 "1GB unless you use -f. HAMMER filesystems\n"
520 "less than 50GB are not recommended\n");
523 for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
524 format_blockmap(&ondisk->vol0_blockmap[i],
525 HAMMER_ZONE_ENCODE(i, 0));
527 format_undomap(ondisk);
529 ondisk->vol0_btree_root = format_root(label);
530 ++ondisk->vol0_stat_inodes; /* root inode */
532 freeblks = initialize_freemap(vol);
533 root_vol = get_volume(RootVolNo);
534 root_vol->cache.modified = 1;
535 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
536 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
537 rel_volume(root_vol);
542 * Format the root directory.
546 format_root(const char *label)
548 hammer_off_t btree_off;
549 hammer_off_t pfsd_off;
550 hammer_off_t data_off;
551 hammer_tid_t create_tid;
552 hammer_node_ondisk_t bnode;
553 struct hammer_inode_data *idata;
554 hammer_pseudofs_data_t pfsd;
555 struct buffer_info *data_buffer1 = NULL;
556 struct buffer_info *data_buffer2 = NULL;
557 hammer_btree_elm_t elm;
560 bnode = alloc_btree_element(&btree_off);
561 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
562 pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
563 create_tid = createtid();
567 * Populate the inode data and inode record for the root directory.
569 idata->version = HAMMER_INODE_DATA_VERSION;
571 idata->ctime = xtime;
572 idata->mtime = xtime;
573 idata->atime = xtime;
574 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
577 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
578 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
579 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
580 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
582 pfsd->sync_low_tid = 1;
583 pfsd->sync_beg_tid = 0;
584 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
585 pfsd->shared_uuid = Hammer_FSId;
586 pfsd->unique_uuid = Hammer_FSId;
587 pfsd->reserved01 = 0;
588 pfsd->mirror_flags = 0;
589 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
592 * Create the root of the B-Tree. The root is a leaf node so we
593 * do not have to worry about boundary elements.
595 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
597 bnode->type = HAMMER_BTREE_TYPE_LEAF;
599 elm = &bnode->elms[0];
600 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
601 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
602 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
603 elm->leaf.base.key = 0;
604 elm->leaf.base.create_tid = create_tid;
605 elm->leaf.base.delete_tid = 0;
606 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
607 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
608 elm->leaf.create_ts = (u_int32_t)time(NULL);
610 elm->leaf.data_offset = data_off;
611 elm->leaf.data_len = sizeof(*idata);
612 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
614 elm = &bnode->elms[1];
615 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
616 elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
617 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
618 elm->leaf.base.key = 0;
619 elm->leaf.base.create_tid = create_tid;
620 elm->leaf.base.delete_tid = 0;
621 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
622 elm->leaf.base.obj_type = 0;
623 elm->leaf.create_ts = (u_int32_t)time(NULL);
625 elm->leaf.data_offset = pfsd_off;
626 elm->leaf.data_len = sizeof(*pfsd);
627 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
629 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);