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
35 #include <sys/sysctl.h>
36 #include <sys/ioctl_compat.h>
38 #include "hammer_util.h"
40 static int64_t getsize(const char *str
, int pw
);
41 static int trim_volume(volume_info_t volume
);
42 static void format_volume(volume_info_t volume
, int nvols
,const char *label
);
43 static hammer_off_t
format_root_directory(const char *label
);
44 static uint64_t nowtime(void);
45 static void print_volume(const volume_info_t volume
);
46 static void usage(int exit_code
);
47 static void test_header_junk_size(int64_t size
);
48 static void test_boot_area_size(int64_t size
);
49 static void test_memory_log_size(int64_t size
);
50 static void test_undo_buffer_size(int64_t size
);
53 static int64_t HeaderJunkSize
= -1;
54 static int64_t BootAreaSize
= -1;
55 static int64_t MemoryLogSize
= -1;
56 static int64_t UndoBufferSize
;
58 #define GIG (1024LL*1024*1024)
61 main(int ac
, char **av
)
70 const char *label
= NULL
;
74 * Sanity check basic filesystem structures. No cookies for us
77 assert(sizeof(struct hammer_volume_ondisk
) <= HAMMER_BUFSIZE
);
78 assert(sizeof(struct hammer_volume_ondisk
) <= HAMMER_MIN_VOL_JUNK
);
79 assert(sizeof(struct hammer_blockmap_layer1
) == 32);
80 assert(sizeof(struct hammer_blockmap_layer2
) == 16);
85 while ((ch
= getopt(ac
, av
, "dfEL:j:b:m:u:hC:V:")) != -1) {
99 case 'j': /* Not mentioned in newfs_hammer(8) */
100 HeaderJunkSize
= getsize(optarg
, 2);
101 test_header_junk_size(HeaderJunkSize
);
104 BootAreaSize
= getsize(optarg
, 2);
105 test_boot_area_size(BootAreaSize
);
108 MemoryLogSize
= getsize(optarg
, 2);
109 test_memory_log_size(MemoryLogSize
);
112 UndoBufferSize
= getsize(optarg
, 2);
113 test_undo_buffer_size(UndoBufferSize
);
120 if (hammer_parse_cache_size(optarg
) == -1) {
126 HammerVersion
= strtol(optarg
, NULL
, 0);
127 if (HammerVersion
< HAMMER_VOL_VERSION_MIN
||
128 HammerVersion
>= HAMMER_VOL_VERSION_WIP
) {
130 "I don't understand how to format "
146 if (HammerVersion
== (uint32_t)-1) {
147 size_t olen
= sizeof(HammerVersion
);
148 HammerVersion
= HAMMER_VOL_VERSION_DEFAULT
;
150 if (sysctlbyname("vfs.hammer.supported_version",
151 &HammerVersion
, &olen
, NULL
, 0)) {
152 hwarn("HAMMER VFS not loaded, cannot get version info, "
155 } else if (HammerVersion
>= HAMMER_VOL_VERSION_WIP
) {
156 HammerVersion
= HAMMER_VOL_VERSION_WIP
- 1;
157 hwarn("HAMMER VFS supports higher version than "
158 "I understand, using version %d",
164 errx(1, "You must specify at least one special file (volume)");
167 if (nvols
> HAMMER_MAX_VOLUMES
) {
168 errx(1, "The maximum number of volumes is %d",
174 hwarnx("A filesystem label must be specified");
180 * Generate a filesystem id and lookup the filesystem type
182 uuidgen(&Hammer_FSId
, 1);
183 uuid_name_lookup(&Hammer_FSType
, HAMMER_FSTYPE_STRING
, &status
);
184 if (status
!= uuid_s_ok
) {
185 errx(1, "uuids file does not have the DragonFly "
186 "HAMMER filesystem type");
191 for (i
= 0; i
< nvols
; ++i
) {
192 volume
= init_volume(av
[i
], O_RDWR
, i
);
193 printf("Volume %d %s %-15s size %s\n",
194 volume
->vol_no
, volume
->type
, volume
->name
,
195 sizetostr(volume
->size
));
198 if (trim_volume(volume
) == -1 && ForceOpt
== 0) {
199 errx(1, "Use -f option to proceed");
203 total
+= volume
->size
;
207 * Reserve space for (future) header junk, setup our poor-man's
208 * big-block allocator. Note that the header junk space includes
209 * volume header which is 1928 bytes.
211 if (HeaderJunkSize
== -1)
212 HeaderJunkSize
= HAMMER_VOL_JUNK_SIZE
;
213 else if (HeaderJunkSize
< (int64_t)sizeof(struct hammer_volume_ondisk
))
214 HeaderJunkSize
= sizeof(struct hammer_volume_ondisk
);
215 HeaderJunkSize
= HAMMER_BUFSIZE_DOALIGN(HeaderJunkSize
);
218 * Calculate defaults for the boot area and memory log sizes,
219 * only if not specified by -b or -m option.
221 avg_vol_size
= total
/ nvols
;
222 if (BootAreaSize
== -1)
223 BootAreaSize
= init_boot_area_size(BootAreaSize
, avg_vol_size
);
224 if (MemoryLogSize
== -1)
225 MemoryLogSize
= init_memory_log_size(MemoryLogSize
, avg_vol_size
);
228 * Format the volumes. Format the root volume first so we can
229 * bootstrap the freemap.
231 format_volume(get_root_volume(), nvols
, label
);
232 for (i
= 0; i
< nvols
; ++i
) {
233 if (i
!= HAMMER_ROOT_VOLNO
)
234 format_volume(get_volume(i
), nvols
, label
);
237 print_volume(get_root_volume());
245 print_volume(const volume_info_t volume
)
247 hammer_volume_ondisk_t ondisk
;
248 hammer_blockmap_t blockmap
;
249 hammer_off_t total
= 0;
252 const char *name
= NULL
;
255 ondisk
= volume
->ondisk
;
256 blockmap
= &ondisk
->vol0_blockmap
[HAMMER_ZONE_UNDO_INDEX
];
258 nvols
= ondisk
->vol_count
;
259 for (i
= 0; i
< nvols
; ++i
) {
260 volume_info_t p
= get_volume(i
);
262 if (p
->vol_no
== HAMMER_ROOT_VOLNO
) {
263 assert(name
== NULL
);
268 uuid_to_string(&Hammer_FSId
, &fsidstr
, &status
);
270 printf("---------------------------------------------\n");
271 printf("HAMMER version %d\n", HammerVersion
);
272 printf("%d volume%s total size %s\n",
273 nvols
, (nvols
== 1 ? "" : "s"), sizetostr(total
));
274 printf("root-volume: %s\n", name
);
276 printf("header-junk-size: %s\n",
277 sizetostr(ondisk
->vol_bot_beg
));
278 printf("boot-area-size: %s\n",
279 sizetostr(ondisk
->vol_mem_beg
- ondisk
->vol_bot_beg
));
280 printf("memory-log-size: %s\n",
281 sizetostr(ondisk
->vol_buf_beg
- ondisk
->vol_mem_beg
));
282 printf("undo-buffer-size: %s\n",
283 sizetostr(HAMMER_OFF_LONG_ENCODE(blockmap
->alloc_offset
)));
284 printf("total-pre-allocated: %s\n",
285 sizetostr(HAMMER_OFF_SHORT_ENCODE(volume
->vol_free_off
)));
286 printf("fsid: %s\n", fsidstr
);
288 printf("NOTE: Please remember that you may have to manually set up a\n"
289 "cron(8) job to prune and reblock the filesystem regularly.\n"
290 "By default, the system automatically runs 'hammer cleanup'\n"
291 "on a nightly basis. The periodic.conf(5) variable\n"
292 "'daily_clean_hammer_enable' can be unset to disable this.\n"
293 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
294 if (total
< 10*GIG
) {
295 printf("\nWARNING: The minimum UNDO/REDO FIFO is %s, "
296 "you really should not\n"
297 "try to format a HAMMER filesystem this small.\n",
298 sizetostr(HAMMER_BIGBLOCK_SIZE
*
299 HAMMER_MIN_UNDO_BIGBLOCKS
));
301 if (total
< 50*GIG
) {
302 printf("\nWARNING: HAMMER filesystems less than 50GB are "
304 "You may have to run 'hammer prune-everything' and "
306 "quite often, even if using a nohistory mount.\n");
315 "usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n"
316 " [-C cachesize[:readahead]] [-V version] special ...\n"
322 test_header_junk_size(int64_t size
)
324 if (size
< HAMMER_MIN_VOL_JUNK
) {
326 errx(1, "The minimum header junk size is %s",
327 sizetostr(HAMMER_MIN_VOL_JUNK
));
330 hwarnx("You have specified header junk size less than %s",
331 sizetostr(HAMMER_MIN_VOL_JUNK
));
333 } else if (size
> HAMMER_MAX_VOL_JUNK
) {
334 errx(1, "The maximum header junk size is %s",
335 sizetostr(HAMMER_MAX_VOL_JUNK
));
341 test_boot_area_size(int64_t size
)
343 if (size
< HAMMER_BOOT_MINBYTES
) {
345 errx(1, "The minimum boot area size is %s",
346 sizetostr(HAMMER_BOOT_MINBYTES
));
349 hwarnx("You have specified boot area size less than %s",
350 sizetostr(HAMMER_BOOT_MINBYTES
));
352 } else if (size
> HAMMER_BOOT_MAXBYTES
) {
353 errx(1, "The maximum boot area size is %s",
354 sizetostr(HAMMER_BOOT_MAXBYTES
));
360 test_memory_log_size(int64_t size
)
362 if (size
< HAMMER_MEM_MINBYTES
) {
364 errx(1, "The minimum memory log size is %s",
365 sizetostr(HAMMER_MEM_MINBYTES
));
368 hwarnx("You have specified memory log size less than %s",
369 sizetostr(HAMMER_MEM_MINBYTES
));
371 } else if (size
> HAMMER_MEM_MAXBYTES
) {
372 errx(1, "The maximum memory log size is %s",
373 sizetostr(HAMMER_MEM_MAXBYTES
));
379 test_undo_buffer_size(int64_t size
)
381 int64_t minbuf
, maxbuf
;
383 minbuf
= HAMMER_BIGBLOCK_SIZE
* HAMMER_MIN_UNDO_BIGBLOCKS
;
384 maxbuf
= HAMMER_BIGBLOCK_SIZE
* HAMMER_MAX_UNDO_BIGBLOCKS
;
388 errx(1, "The minimum UNDO/REDO FIFO size is %s",
392 hwarnx("You have specified an UNDO/REDO FIFO size less "
393 "than %s, which may lead to VFS panics",
396 } else if (size
> maxbuf
) {
397 errx(1, "The maximum UNDO/REDO FIFO size is %s",
404 * Convert a string to a 64 bit signed integer with various requirements.
407 getsize(const char *str
, int powerof2
)
412 val
= strtoll(str
, &ptr
, 0);
431 errx(1, "Unknown suffix in number '%s'", str
);
436 errx(1, "Unknown suffix in number '%s'", str
);
439 if ((powerof2
& 1) && (val
^ (val
- 1)) != ((val
<< 1) - 1)) {
440 errx(1, "Value not power of 2: %s", str
);
443 if ((powerof2
& 2) && (val
& HAMMER_BUFMASK
)) {
444 errx(1, "Value not an integral multiple of %dK: %s",
445 HAMMER_BUFSIZE
/ 1024, str
);
452 * Generate a transaction id. Transaction ids are no longer time-based.
453 * Put the nail in the coffin by not making the first one time-based.
455 * We could start at 1 here but start at 2^32 to reserve a small domain for
456 * possible future use.
461 static hammer_tid_t lasttid
;
464 lasttid
= 0x0000000100000000ULL
;
474 gettimeofday(&tv
, NULL
);
475 xtime
= tv
.tv_sec
* 1000000LL + tv
.tv_usec
;
480 * TRIM the volume, but only if the backing store is not a regular file
484 trim_volume(volume_info_t volume
)
488 char sysctl_name
[64];
492 if (is_regfile(volume
)) {
493 hwarnx("Cannot TRIM regular file %s", volume
->name
);
496 if (strncmp(volume
->name
, "/dev/da", 7)) {
497 hwarnx("%s does not support the TRIM command", volume
->name
);
501 /* Extract a number from /dev/da?s? */
502 dev_name
= strdup(volume
->name
);
503 p
= strtok(dev_name
+ strlen("/dev/da"), "s");
504 sprintf(sysctl_name
, "kern.cam.da.%s.trim_enabled", p
);
508 olen
= sizeof(trim_enabled
);
510 if (sysctlbyname(sysctl_name
, &trim_enabled
, &olen
, NULL
, 0) == -1) {
511 hwarnx("%s (%s) does not support the TRIM command",
512 volume
->name
, sysctl_name
);
516 hwarnx("Erase device option selected, but sysctl (%s) "
517 "is not enabled", sysctl_name
);
521 ioarg
[0] = volume
->device_offset
;
522 ioarg
[1] = volume
->size
;
524 printf("Trimming %s %s, sectors %llu-%llu\n",
525 volume
->type
, volume
->name
,
526 (unsigned long long)ioarg
[0] / 512,
527 (unsigned long long)ioarg
[1] / 512);
529 if (ioctl(volume
->fd
, IOCTLTRIM
, ioarg
) == -1) {
530 err(1, "Trimming %s failed", volume
->name
);
538 * Format a HAMMER volume.
542 format_volume(volume_info_t volume
, int nvols
, const char *label
)
544 volume_info_t root_vol
;
545 hammer_volume_ondisk_t ondisk
;
548 int64_t vol_buf_size
;
549 hammer_off_t vol_alloc
;
553 * Initialize basic information in the on-disk volume structure.
555 ondisk
= volume
->ondisk
;
557 ondisk
->vol_fsid
= Hammer_FSId
;
558 ondisk
->vol_fstype
= Hammer_FSType
;
559 snprintf(ondisk
->vol_label
, sizeof(ondisk
->vol_label
), "%s", label
);
560 ondisk
->vol_no
= volume
->vol_no
;
561 ondisk
->vol_count
= nvols
;
562 ondisk
->vol_version
= HammerVersion
;
564 vol_alloc
= HeaderJunkSize
;
565 ondisk
->vol_bot_beg
= vol_alloc
;
566 vol_alloc
+= BootAreaSize
;
567 ondisk
->vol_mem_beg
= vol_alloc
;
568 vol_alloc
+= MemoryLogSize
;
571 * The remaining area is the zone 2 buffer allocation area.
573 ondisk
->vol_buf_beg
= vol_alloc
;
574 ondisk
->vol_buf_end
= volume
->size
& ~(int64_t)HAMMER_BUFMASK
;
575 vol_buf_size
= HAMMER_VOL_BUF_SIZE(ondisk
);
577 if (vol_buf_size
< (int64_t)sizeof(*ondisk
)) {
578 errx(1, "volume %d %s is too small to hold the volume header",
579 volume
->vol_no
, volume
->name
);
582 if ((vol_buf_size
& ~HAMMER_OFF_SHORT_MASK
) != 0) {
583 errx(1, "volume %d %s is too large", volume
->vol_no
, volume
->name
);
587 ondisk
->vol_rootvol
= HAMMER_ROOT_VOLNO
;
588 ondisk
->vol_signature
= HAMMER_FSBUF_VOLUME
;
590 volume
->vol_free_off
= HAMMER_ENCODE_RAW_BUFFER(volume
->vol_no
, 0);
591 volume
->vol_free_end
= HAMMER_ENCODE_RAW_BUFFER(volume
->vol_no
,
592 vol_buf_size
& ~HAMMER_BIGBLOCK_MASK64
);
595 * Format the root volume.
597 if (volume
->vol_no
== HAMMER_ROOT_VOLNO
) {
599 * Check freemap counts before formatting
601 freeblks
= count_freemap(volume
);
602 freebytes
= freeblks
* HAMMER_BIGBLOCK_SIZE64
;
603 if (freebytes
< 10*GIG
&& ForceOpt
== 0) {
604 errx(1, "Cannot create a HAMMER filesystem less than 10GB "
605 "unless you use -f\n(for the size of Volume %d). "
606 "HAMMER filesystems less than 50GB are not "
607 "recommended.", HAMMER_ROOT_VOLNO
);
614 ondisk
->vol0_next_tid
= createtid();
617 * Format freemap. vol0_stat_freebigblocks is
618 * the number of big-blocks available for anything
619 * other than freemap zone at this point.
621 format_freemap(volume
);
622 assert(ondisk
->vol0_stat_freebigblocks
== 0);
623 ondisk
->vol0_stat_freebigblocks
= initialize_freemap(volume
);
626 * Format zones that are mapped to zone-2.
628 for (i
= 0; i
< HAMMER_MAX_ZONES
; ++i
) {
629 if (hammer_is_index_record(i
))
630 format_blockmap(volume
, i
, 0);
634 * Format undo zone. Formatting decrements
635 * vol0_stat_freebigblocks whenever a new big-block
636 * is allocated for undo zone.
638 format_undomap(volume
, &UndoBufferSize
);
639 assert(ondisk
->vol0_stat_bigblocks
== 0);
640 ondisk
->vol0_stat_bigblocks
= ondisk
->vol0_stat_freebigblocks
;
643 * Format the root directory. Formatting decrements
644 * vol0_stat_freebigblocks whenever a new big-block
645 * is allocated for required zones.
647 ondisk
->vol0_btree_root
= format_root_directory(label
);
648 ++ondisk
->vol0_stat_inodes
; /* root inode */
650 freeblks
= initialize_freemap(volume
);
651 root_vol
= get_root_volume();
652 root_vol
->ondisk
->vol0_stat_freebigblocks
+= freeblks
;
653 root_vol
->ondisk
->vol0_stat_bigblocks
+= freeblks
;
658 * Format the root directory.
662 format_root_directory(const char *label
)
664 hammer_off_t btree_off
;
665 hammer_off_t idata_off
;
666 hammer_off_t pfsd_off
;
667 hammer_tid_t create_tid
;
668 hammer_node_ondisk_t bnode
;
669 hammer_inode_data_t idata
;
670 hammer_pseudofs_data_t pfsd
;
671 buffer_info_t data_buffer0
= NULL
;
672 buffer_info_t data_buffer1
= NULL
;
673 buffer_info_t data_buffer2
= NULL
;
674 hammer_btree_elm_t elm
;
678 * Allocate zero-filled root btree node, inode and pfs
680 bnode
= alloc_btree_node(&btree_off
, &data_buffer0
);
681 idata
= alloc_meta_element(&idata_off
, sizeof(*idata
), &data_buffer1
);
682 pfsd
= alloc_meta_element(&pfsd_off
, sizeof(*pfsd
), &data_buffer2
);
683 create_tid
= createtid();
687 * Populate the inode data and inode record for the root directory.
689 idata
->version
= HAMMER_INODE_DATA_VERSION
;
691 idata
->ctime
= xtime
;
692 idata
->mtime
= xtime
;
693 idata
->atime
= xtime
;
694 idata
->obj_type
= HAMMER_OBJTYPE_DIRECTORY
;
697 if (HammerVersion
>= HAMMER_VOL_VERSION_TWO
)
698 idata
->cap_flags
|= HAMMER_INODE_CAP_DIR_LOCAL_INO
;
699 if (HammerVersion
>= HAMMER_VOL_VERSION_SIX
)
700 idata
->cap_flags
|= HAMMER_INODE_CAP_DIRHASH_ALG1
;
703 * Populate the PFS data for the root PFS.
705 pfsd
->sync_low_tid
= 1;
706 pfsd
->sync_beg_tid
= 0;
707 pfsd
->sync_end_tid
= 0; /* overriden by vol0_next_tid on root PFS */
708 pfsd
->shared_uuid
= Hammer_FSId
;
709 pfsd
->unique_uuid
= Hammer_FSId
;
710 pfsd
->mirror_flags
= 0;
711 snprintf(pfsd
->label
, sizeof(pfsd
->label
), "%s", label
);
714 * Create the root of the B-Tree. The root is a leaf node so we
715 * do not have to worry about boundary elements.
717 bnode
->parent
= 0; /* no parent */
719 bnode
->type
= HAMMER_BTREE_TYPE_LEAF
;
720 bnode
->mirror_tid
= 0;
723 * Create the first node element for the inode.
725 elm
= &bnode
->elms
[0];
726 elm
->leaf
.base
.btype
= HAMMER_BTREE_TYPE_RECORD
;
727 elm
->leaf
.base
.localization
= HAMMER_DEF_LOCALIZATION
|
728 HAMMER_LOCALIZE_INODE
;
729 elm
->leaf
.base
.obj_id
= HAMMER_OBJID_ROOT
;
730 elm
->leaf
.base
.key
= 0;
731 elm
->leaf
.base
.create_tid
= create_tid
;
732 elm
->leaf
.base
.delete_tid
= 0;
733 elm
->leaf
.base
.rec_type
= HAMMER_RECTYPE_INODE
;
734 elm
->leaf
.base
.obj_type
= HAMMER_OBJTYPE_DIRECTORY
;
735 elm
->leaf
.create_ts
= (uint32_t)time(NULL
);
737 elm
->leaf
.data_offset
= idata_off
;
738 elm
->leaf
.data_len
= sizeof(*idata
);
739 hammer_crc_set_leaf(HammerVersion
, idata
, &elm
->leaf
);
742 * Create the second node element for the PFS data.
743 * This is supposed to be a record part of the root ip (inode),
744 * so it should have the same obj_type value as above.
746 elm
= &bnode
->elms
[1];
747 elm
->leaf
.base
.btype
= HAMMER_BTREE_TYPE_RECORD
;
748 elm
->leaf
.base
.localization
= HAMMER_DEF_LOCALIZATION
|
749 HAMMER_LOCALIZE_MISC
;
750 elm
->leaf
.base
.obj_id
= HAMMER_OBJID_ROOT
;
751 elm
->leaf
.base
.key
= 0;
752 elm
->leaf
.base
.create_tid
= create_tid
;
753 elm
->leaf
.base
.delete_tid
= 0;
754 elm
->leaf
.base
.rec_type
= HAMMER_RECTYPE_PFS
;
755 elm
->leaf
.base
.obj_type
= HAMMER_OBJTYPE_DIRECTORY
;
756 elm
->leaf
.create_ts
= (uint32_t)time(NULL
);
758 elm
->leaf
.data_offset
= pfsd_off
;
759 elm
->leaf
.data_len
= sizeof(*pfsd
);
760 hammer_crc_set_leaf(HammerVersion
, pfsd
, &elm
->leaf
);
762 hammer_crc_set_btree(HammerVersion
, bnode
);
764 rel_buffer(data_buffer0
);
765 rel_buffer(data_buffer1
);
766 rel_buffer(data_buffer2
);