#include "hammer_util.h"
#include <sys/sysctl.h>
#include <bus/cam/scsi/scsi_daio.h>
static int64_t getsize(const char *str, int pw);
static int trim_volume(volume_info_t volume);
static void format_volume(volume_info_t volume, int nvols,const char *label);
static hammer_off_t format_root_directory(const char *label);
static uint64_t nowtime(void);
static void print_volume(const volume_info_t volume);
static void usage(int exit_code);
static void test_header_junk_size(int64_t size);
static void test_boot_area_size(int64_t size);
static void test_memory_log_size(int64_t size);
static void test_undo_buffer_size(int64_t size);
static int ForceOpt;
static int64_t HeaderJunkSize = -1;
static int64_t BootAreaSize = -1;
static int64_t MemoryLogSize = -1;
static int64_t UndoBufferSize;
#define GIG (1024LL*1024*1024)
int
main(int ac, char **av)
{
off_t total;
off_t avg_vol_size;
int ch;
int i;
int nvols;
int eflag = 0;
const char *label = NULL;
volume_info_t volume;
assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_MIN_VOL_JUNK);
assert(sizeof(struct hammer_blockmap_layer1) == 32);
assert(sizeof(struct hammer_blockmap_layer2) == 16);
while ((ch = getopt(ac, av, "dfEL:j:b:m:u:hC:V:")) != -1) {
switch(ch) {
case 'd':
++DebugOpt;
break;
case 'f':
ForceOpt = 1;
break;
case 'E':
eflag = 1;
break;
case 'L':
label = optarg;
break;
case 'j':
HeaderJunkSize = getsize(optarg, 2);
test_header_junk_size(HeaderJunkSize);
break;
case 'b':
BootAreaSize = getsize(optarg, 2);
test_boot_area_size(BootAreaSize);
break;
case 'm':
MemoryLogSize = getsize(optarg, 2);
test_memory_log_size(MemoryLogSize);
break;
case 'u':
UndoBufferSize = getsize(optarg, 2);
test_undo_buffer_size(UndoBufferSize);
break;
case 'h':
usage(0);
break;
case 'C':
if (hammer_parse_cache_size(optarg) == -1) {
usage(1);
}
break;
case 'V':
HammerVersion = strtol(optarg, NULL, 0);
if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
HammerVersion >= HAMMER_VOL_VERSION_WIP) {
errx(1,
"I don't understand how to format "
"HAMMER version %d",
HammerVersion);
}
break;
default:
usage(1);
break;
}
}
ac -= optind;
av += optind;
nvols = ac;
if (HammerVersion == (uint32_t)-1) {
size_t olen = sizeof(HammerVersion);
HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
if (sysctlbyname("vfs.hammer.supported_version",
&HammerVersion, &olen, NULL, 0)) {
hwarn("HAMMER VFS not loaded, cannot get version info, "
"using version %d",
HammerVersion);
} else if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
hwarnx("HAMMER VFS supports higher version than "
"I understand, using version %d",
HammerVersion);
}
}
if (nvols == 0) {
errx(1, "You must specify at least one special file (volume)");
}
if (nvols > HAMMER_MAX_VOLUMES) {
errx(1, "The maximum number of volumes is %d",
HAMMER_MAX_VOLUMES);
}
if (label == NULL) {
hwarnx("A filesystem label must be specified");
usage(1);
}
hammer_uuid_create(&Hammer_FSId);
if (hammer_uuid_name_lookup(&Hammer_FSType, HAMMER_FSTYPE_STRING)) {
errx(1, "uuids file does not have the DragonFly "
"HAMMER filesystem type");
}
total = 0;
for (i = 0; i < nvols; ++i) {
volume = init_volume(av[i], O_RDWR, i);
printf("Volume %d %s %-15s size %s\n",
volume->vol_no, volume->type, volume->name,
sizetostr(volume->size));
if (eflag) {
if (trim_volume(volume) == -1 && ForceOpt == 0) {
errx(1, "Use -f option to proceed");
}
}
total += volume->size;
}
if (HeaderJunkSize == -1)
HeaderJunkSize = HAMMER_VOL_JUNK_SIZE;
else if (HeaderJunkSize < (int64_t)sizeof(struct hammer_volume_ondisk))
HeaderJunkSize = sizeof(struct hammer_volume_ondisk);
HeaderJunkSize = HAMMER_BUFSIZE_DOALIGN(HeaderJunkSize);
avg_vol_size = total / nvols;
if (BootAreaSize == -1)
BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
if (MemoryLogSize == -1)
MemoryLogSize = init_memory_log_size(MemoryLogSize, avg_vol_size);
format_volume(get_root_volume(), nvols, label);
for (i = 0; i < nvols; ++i) {
if (i != HAMMER_ROOT_VOLNO)
format_volume(get_volume(i), nvols, label);
}
print_volume(get_root_volume());
flush_all_volumes();
return(0);
}
static
void
print_volume(const volume_info_t volume)
{
hammer_volume_ondisk_t ondisk;
hammer_blockmap_t blockmap;
hammer_off_t total = 0;
int i, nvols;
const char *name = NULL;
char *fsidstr;
ondisk = volume->ondisk;
blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
nvols = ondisk->vol_count;
for (i = 0; i < nvols; ++i) {
volume_info_t p = get_volume(i);
total += p->size;
if (p->vol_no == HAMMER_ROOT_VOLNO) {
assert(name == NULL);
name = p->name;
}
}
hammer_uuid_to_string(&Hammer_FSId, &fsidstr);
printf("---------------------------------------------\n");
printf("HAMMER version %d\n", HammerVersion);
printf("%d volume%s total size %s\n",
nvols, (nvols == 1 ? "" : "s"), sizetostr(total));
printf("root-volume: %s\n", name);
if (DebugOpt) {
printf("header-junk-size: %s\n",
sizetostr(ondisk->vol_bot_beg));
}
printf("boot-area-size: %s\n",
sizetostr(ondisk->vol_mem_beg - ondisk->vol_bot_beg));
printf("memory-log-size: %s\n",
sizetostr(ondisk->vol_buf_beg - ondisk->vol_mem_beg));
printf("undo-buffer-size: %s\n",
sizetostr(HAMMER_OFF_LONG_ENCODE(blockmap->alloc_offset)));
printf("total-pre-allocated: %s\n",
sizetostr(HAMMER_OFF_SHORT_ENCODE(volume->vol_free_off)));
printf("fsid: %s\n", fsidstr);
printf("\n");
printf("NOTE: Please remember that you may have to manually set up a\n"
"cron(8) job to prune and reblock the filesystem regularly.\n"
"By default, the system automatically runs 'hammer cleanup'\n"
"on a nightly basis. The periodic.conf(5) variable\n"
"'daily_clean_hammer_enable' can be unset to disable this.\n"
"Also see 'man hammer' and 'man HAMMER' for more information.\n");
if (total < 10*GIG) {
printf("\nWARNING: The minimum UNDO/REDO FIFO is %s, "
"you really should not\n"
"try to format a HAMMER filesystem this small.\n",
sizetostr(HAMMER_BIGBLOCK_SIZE *
HAMMER_MIN_UNDO_BIGBLOCKS));
}
if (total < 50*GIG) {
printf("\nWARNING: HAMMER filesystems less than 50GB are "
"not recommended!\n"
"You may have to run 'hammer prune-everything' and "
"'hammer reblock'\n"
"quite often, even if using a nohistory mount.\n");
}
}
static
void
usage(int exit_code)
{
fprintf(stderr,
"usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n"
" [-C cachesize[:readahead]] [-V version] special ...\n"
);
exit(exit_code);
}
static
void
test_header_junk_size(int64_t size)
{
if (size < HAMMER_MIN_VOL_JUNK) {
if (ForceOpt == 0) {
errx(1, "The minimum header junk size is %s",
sizetostr(HAMMER_MIN_VOL_JUNK));
} else {
hwarnx("You have specified header junk size less than %s",
sizetostr(HAMMER_MIN_VOL_JUNK));
}
} else if (size > HAMMER_MAX_VOL_JUNK) {
errx(1, "The maximum header junk size is %s",
sizetostr(HAMMER_MAX_VOL_JUNK));
}
}
static
void
test_boot_area_size(int64_t size)
{
if (size < HAMMER_BOOT_MINBYTES) {
if (ForceOpt == 0) {
errx(1, "The minimum boot area size is %s",
sizetostr(HAMMER_BOOT_MINBYTES));
} else {
hwarnx("You have specified boot area size less than %s",
sizetostr(HAMMER_BOOT_MINBYTES));
}
} else if (size > HAMMER_BOOT_MAXBYTES) {
errx(1, "The maximum boot area size is %s",
sizetostr(HAMMER_BOOT_MAXBYTES));
}
}
static
void
test_memory_log_size(int64_t size)
{
if (size < HAMMER_MEM_MINBYTES) {
if (ForceOpt == 0) {
errx(1, "The minimum memory log size is %s",
sizetostr(HAMMER_MEM_MINBYTES));
} else {
hwarnx("You have specified memory log size less than %s",
sizetostr(HAMMER_MEM_MINBYTES));
}
} else if (size > HAMMER_MEM_MAXBYTES) {
errx(1, "The maximum memory log size is %s",
sizetostr(HAMMER_MEM_MAXBYTES));
}
}
static
void
test_undo_buffer_size(int64_t size)
{
int64_t minbuf, maxbuf;
minbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MIN_UNDO_BIGBLOCKS;
maxbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MAX_UNDO_BIGBLOCKS;
if (size < minbuf) {
if (ForceOpt == 0) {
errx(1, "The minimum UNDO/REDO FIFO size is %s",
sizetostr(minbuf));
} else {
hwarnx("You have specified an UNDO/REDO FIFO size less "
"than %s, which may lead to VFS panics",
sizetostr(minbuf));
}
} else if (size > maxbuf) {
errx(1, "The maximum UNDO/REDO FIFO size is %s",
sizetostr(maxbuf));
}
}
static
int64_t
getsize(const char *str, int powerof2)
{
int64_t val;
char *ptr;
val = strtoll(str, &ptr, 0);
switch(*ptr) {
case 't':
case 'T':
val *= 1024;
case 'g':
case 'G':
val *= 1024;
case 'm':
case 'M':
val *= 1024;
case 'k':
case 'K':
val *= 1024;
break;
default:
errx(1, "Unknown suffix in number '%s'", str);
}
if (ptr[1]) {
errx(1, "Unknown suffix in number '%s'", str);
}
if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
errx(1, "Value not power of 2: %s", str);
}
if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
errx(1, "Value not an integral multiple of %dK: %s",
HAMMER_BUFSIZE / 1024, str);
}
return(val);
}
static
hammer_tid_t
createtid(void)
{
static hammer_tid_t lasttid;
if (lasttid == 0)
lasttid = 0x0000000100000000ULL;
return(lasttid++);
}
static
uint64_t
nowtime(void)
{
struct timeval tv;
uint64_t xtime;
gettimeofday(&tv, NULL);
xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
return(xtime);
}
static
int
trim_volume(volume_info_t volume)
{
size_t olen;
char *dev_name, *p;
char sysctl_name[64];
int trim_enabled;
off_t ioarg[2];
if (is_regfile(volume)) {
hwarnx("Cannot TRIM regular file %s", volume->name);
return(-1);
}
if (strncmp(volume->name, "/dev/da", 7)) {
hwarnx("%s does not support the TRIM command", volume->name);
return(-1);
}
dev_name = strdup(volume->name);
p = strtok(dev_name + strlen("/dev/da"), "s");
sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p);
free(dev_name);
trim_enabled = 0;
olen = sizeof(trim_enabled);
if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) {
hwarnx("%s (%s) does not support the TRIM command",
volume->name, sysctl_name);
return(-1);
}
if (!trim_enabled) {
hwarnx("Erase device option selected, but sysctl (%s) "
"is not enabled", sysctl_name);
return(-1);
}
ioarg[0] = volume->device_offset;
ioarg[1] = volume->size;
printf("Trimming %s %s, sectors %llu-%llu\n",
volume->type, volume->name,
(unsigned long long)ioarg[0] / 512,
(unsigned long long)ioarg[1] / 512);
if (ioctl(volume->fd, DAIOCTRIM, ioarg) == -1) {
err(1, "Trimming %s failed", volume->name);
}
return(0);
}
static
void
format_volume(volume_info_t volume, int nvols, const char *label)
{
volume_info_t root_vol;
hammer_volume_ondisk_t ondisk;
int64_t freeblks;
int64_t freebytes;
int64_t vol_buf_size;
hammer_off_t vol_alloc;
int i;
ondisk = volume->ondisk;
ondisk->vol_fsid = Hammer_FSId;
ondisk->vol_fstype = Hammer_FSType;
snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
ondisk->vol_no = volume->vol_no;
ondisk->vol_count = nvols;
ondisk->vol_version = HammerVersion;
vol_alloc = HeaderJunkSize;
ondisk->vol_bot_beg = vol_alloc;
vol_alloc += BootAreaSize;
ondisk->vol_mem_beg = vol_alloc;
vol_alloc += MemoryLogSize;
ondisk->vol_buf_beg = vol_alloc;
ondisk->vol_buf_end = volume->size & ~(int64_t)HAMMER_BUFMASK;
vol_buf_size = HAMMER_VOL_BUF_SIZE(ondisk);
if (vol_buf_size < (int64_t)sizeof(*ondisk)) {
errx(1, "volume %d %s is too small to hold the volume header",
volume->vol_no, volume->name);
}
if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) {
errx(1, "volume %d %s is too large", volume->vol_no, volume->name);
}
ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
volume->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0);
volume->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no,
vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
if (volume->vol_no == HAMMER_ROOT_VOLNO) {
freeblks = count_freemap(volume);
freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
if (freebytes < 10*GIG && ForceOpt == 0) {
errx(1, "Cannot create a HAMMER filesystem less than 10GB "
"unless you use -f\n(for the size of Volume %d). "
"HAMMER filesystems less than 50GB are not "
"recommended.", HAMMER_ROOT_VOLNO);
}
ondisk->vol0_next_tid = createtid();
format_freemap(volume);
assert(ondisk->vol0_stat_freebigblocks == 0);
ondisk->vol0_stat_freebigblocks = initialize_freemap(volume);
for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
if (hammer_is_index_record(i))
format_blockmap(volume, i, 0);
}
format_undomap(volume, &UndoBufferSize);
assert(ondisk->vol0_stat_bigblocks == 0);
ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
ondisk->vol0_btree_root = format_root_directory(label);
++ondisk->vol0_stat_inodes;
} else {
freeblks = initialize_freemap(volume);
root_vol = get_root_volume();
root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
root_vol->ondisk->vol0_stat_bigblocks += freeblks;
}
}
static
hammer_off_t
format_root_directory(const char *label)
{
hammer_off_t btree_off;
hammer_off_t idata_off;
hammer_off_t pfsd_off;
hammer_tid_t create_tid;
hammer_node_ondisk_t bnode;
hammer_inode_data_t idata;
hammer_pseudofs_data_t pfsd;
buffer_info_t data_buffer0 = NULL;
buffer_info_t data_buffer1 = NULL;
buffer_info_t data_buffer2 = NULL;
hammer_btree_elm_t elm;
uint64_t xtime;
bnode = alloc_btree_node(&btree_off, &data_buffer0);
idata = alloc_meta_element(&idata_off, sizeof(*idata), &data_buffer1);
pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
create_tid = createtid();
xtime = nowtime();
idata->version = HAMMER_INODE_DATA_VERSION;
idata->mode = 0755;
idata->ctime = xtime;
idata->mtime = xtime;
idata->atime = xtime;
idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
idata->size = 0;
idata->nlinks = 1;
if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
pfsd->sync_low_tid = 1;
pfsd->sync_beg_tid = 0;
pfsd->sync_end_tid = 0;
pfsd->shared_uuid = Hammer_FSId;
pfsd->unique_uuid = Hammer_FSId;
pfsd->mirror_flags = 0;
snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
bnode->parent = 0;
bnode->count = 2;
bnode->type = HAMMER_BTREE_TYPE_LEAF;
bnode->mirror_tid = 0;
elm = &bnode->elms[0];
elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
HAMMER_LOCALIZE_INODE;
elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
elm->leaf.base.key = 0;
elm->leaf.base.create_tid = create_tid;
elm->leaf.base.delete_tid = 0;
elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
elm->leaf.create_ts = (uint32_t)time(NULL);
elm->leaf.data_offset = idata_off;
elm->leaf.data_len = sizeof(*idata);
hammer_crc_set_leaf(HammerVersion, idata, &elm->leaf);
elm = &bnode->elms[1];
elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
HAMMER_LOCALIZE_MISC;
elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
elm->leaf.base.key = 0;
elm->leaf.base.create_tid = create_tid;
elm->leaf.base.delete_tid = 0;
elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
elm->leaf.create_ts = (uint32_t)time(NULL);
elm->leaf.data_offset = pfsd_off;
elm->leaf.data_len = sizeof(*pfsd);
hammer_crc_set_leaf(HammerVersion, pfsd, &elm->leaf);
hammer_crc_set_btree(HammerVersion, bnode);
rel_buffer(data_buffer0);
rel_buffer(data_buffer1);
rel_buffer(data_buffer2);
return(btree_off);
}