diff options
Diffstat (limited to '')
-rw-r--r-- | misc/mk_hugefiles.c | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/misc/mk_hugefiles.c b/misc/mk_hugefiles.c new file mode 100644 index 0000000..3caaf1b --- /dev/null +++ b/misc/mk_hugefiles.c @@ -0,0 +1,491 @@ +/* + * mk_hugefiles.c -- create huge files + */ + +#include "config.h" +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <fcntl.h> +#include <ctype.h> +#include <time.h> +#ifdef __linux__ +#include <sys/utsname.h> +#endif +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else +extern char *optarg; +extern int optind; +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_SYSMACROS_H +#include <sys/sysmacros.h> +#endif +#include <libgen.h> +#include <limits.h> +#include <blkid/blkid.h> + +#include "ext2fs/ext2_fs.h" +#include "ext2fs/ext2fsP.h" +#include "et/com_err.h" +#include "uuid/uuid.h" +#include "e2p/e2p.h" +#include "ext2fs/ext2fs.h" +#include "util.h" +#include "support/profile.h" +#include "support/prof_err.h" +#include "support/nls-enable.h" +#include "mke2fs.h" + +static int uid; +static int gid; +static blk64_t num_blocks; +static blk64_t num_slack; +static unsigned long num_files; +static blk64_t goal; +static char *fn_prefix; +static int idx_digits; +static char *fn_buf; +static char *fn_numbuf; +int zero_hugefile = 1; + +static blk64_t +get_partition_start(const char *device_name EXT2FS_ATTR((unused))) +{ +#ifdef __linux__ + unsigned long long start; + char path[128]; + struct stat st; + FILE *f; + int n; + + if ((stat(device_name, &st) < 0) || !S_ISBLK(st.st_mode)) + return 0; + + sprintf(path, "/sys/dev/block/%d:%d/start", + major(st.st_rdev), minor(st.st_rdev)); + f = fopen(path, "r"); + if (!f) + return 0; + n = fscanf(f, "%llu", &start); + fclose(f); + return (n == 1) ? start : 0; +#else + return 0; +#endif +} + +static errcode_t create_directory(ext2_filsys fs, char *dir, + ext2_ino_t *ret_ino) + +{ + struct ext2_inode inode; + ext2_ino_t ino = EXT2_ROOT_INO; + ext2_ino_t newdir; + errcode_t retval = 0; + char *fn, *cp, *next; + + fn = malloc(strlen(dir) + 1); + if (fn == NULL) + return ENOMEM; + + strcpy(fn, dir); + cp = fn; + while(1) { + next = strchr(cp, '/'); + if (next) + *next++ = 0; + if (*cp) { + retval = ext2fs_new_inode(fs, ino, LINUX_S_IFDIR, + NULL, &newdir); + if (retval) + goto errout; + + retval = ext2fs_mkdir(fs, ino, newdir, cp); + if (retval) + goto errout; + + ino = newdir; + retval = ext2fs_read_inode(fs, ino, &inode); + if (retval) + goto errout; + + inode.i_uid = uid & 0xFFFF; + ext2fs_set_i_uid_high(inode, (uid >> 16) & 0xffff); + inode.i_gid = gid & 0xFFFF; + ext2fs_set_i_gid_high(inode, (gid >> 16) & 0xffff); + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) + goto errout; + } + if (next == NULL || *next == '\0') + break; + cp = next; + } +errout: + free(fn); + if (retval == 0) + *ret_ino = ino; + return retval; +} + +static errcode_t mk_hugefile(ext2_filsys fs, blk64_t num, + ext2_ino_t dir, unsigned long idx, ext2_ino_t *ino) + +{ + errcode_t retval; + blk64_t lblk, bend = 0; + __u64 size; + blk64_t left; + blk64_t count = 0; + struct ext2_inode inode; + ext2_extent_handle_t handle; + + retval = ext2fs_new_inode(fs, 0, LINUX_S_IFREG, NULL, ino); + if (retval) + return retval; + + memset(&inode, 0, sizeof(struct ext2_inode)); + inode.i_mode = LINUX_S_IFREG | (0666 & ~fs->umask); + inode.i_links_count = 1; + inode.i_uid = uid & 0xFFFF; + ext2fs_set_i_uid_high(inode, (uid >> 16) & 0xffff); + inode.i_gid = gid & 0xFFFF; + ext2fs_set_i_gid_high(inode, (gid >> 16) & 0xffff); + + retval = ext2fs_write_new_inode(fs, *ino, &inode); + if (retval) + return retval; + + ext2fs_inode_alloc_stats2(fs, *ino, +1, 0); + + retval = ext2fs_extent_open2(fs, *ino, &inode, &handle); + if (retval) + return retval; + + /* + * We don't use ext2fs_fallocate() here because hugefiles are + * designed to be physically contiguous (if the block group + * descriptors are configured to be in a single block at the + * beginning of the file system, by using the + * packed_meta_blocks layout), with the extent tree blocks + * allocated near the beginning of the file system. + */ + lblk = 0; + left = num ? num : 1; + while (left) { + blk64_t pblk, end; + blk64_t n = left; + + retval = ext2fs_find_first_zero_block_bitmap2(fs->block_map, + goal, ext2fs_blocks_count(fs->super) - 1, &end); + if (retval) + goto errout; + goal = end; + + retval = ext2fs_find_first_set_block_bitmap2(fs->block_map, goal, + ext2fs_blocks_count(fs->super) - 1, &bend); + if (retval == ENOENT) { + bend = ext2fs_blocks_count(fs->super); + if (num == 0) + left = 0; + } + if (!num || bend - goal < left) + n = bend - goal; + pblk = goal; + if (num) + left -= n; + goal += n; + count += n; + ext2fs_block_alloc_stats_range(fs, pblk, n, +1); + + if (zero_hugefile) { + blk64_t ret_blk; + retval = ext2fs_zero_blocks2(fs, pblk, n, + &ret_blk, NULL); + + if (retval) + com_err(program_name, retval, + _("while zeroing block %llu " + "for hugefile"), + (unsigned long long) ret_blk); + } + + while (n) { + blk64_t l = n; + struct ext2fs_extent newextent; + + if (l > EXT_INIT_MAX_LEN) + l = EXT_INIT_MAX_LEN; + + newextent.e_len = l; + newextent.e_pblk = pblk; + newextent.e_lblk = lblk; + newextent.e_flags = 0; + + retval = ext2fs_extent_insert(handle, + EXT2_EXTENT_INSERT_AFTER, &newextent); + if (retval) + return retval; + pblk += l; + lblk += l; + n -= l; + } + } + + retval = ext2fs_read_inode(fs, *ino, &inode); + if (retval) + goto errout; + + retval = ext2fs_iblk_add_blocks(fs, &inode, + count / EXT2FS_CLUSTER_RATIO(fs)); + if (retval) + goto errout; + size = (__u64) count * fs->blocksize; + retval = ext2fs_inode_size_set(fs, &inode, size); + if (retval) + goto errout; + + retval = ext2fs_write_new_inode(fs, *ino, &inode); + if (retval) + goto errout; + + if (idx_digits) + sprintf(fn_numbuf, "%0*lu", idx_digits, idx); + else if (num_files > 1) + sprintf(fn_numbuf, "%lu", idx); + +retry: + retval = ext2fs_link(fs, dir, fn_buf, *ino, EXT2_FT_REG_FILE); + if (retval == EXT2_ET_DIR_NO_SPACE) { + retval = ext2fs_expand_dir(fs, dir); + if (retval) + goto errout; + goto retry; + } + + if (retval) + goto errout; + +errout: + if (handle) + ext2fs_extent_free(handle); + + return retval; +} + +static blk64_t calc_overhead(ext2_filsys fs, blk64_t num) +{ + blk64_t e_blocks, e_blocks2, e_blocks3, e_blocks4; + int extents_per_block; + int extents = (num + EXT_INIT_MAX_LEN - 1) / EXT_INIT_MAX_LEN; + + if (extents <= 4) + return 0; + + /* + * This calculation is due to the fact that we are inefficient + * in how handle extent splits when appending to the end of + * the extent tree. Sigh. We should fix this so that we can + * actually store 340 extents per 4k block, instead of only 170. + */ + extents_per_block = ((fs->blocksize - + sizeof(struct ext3_extent_header)) / + sizeof(struct ext3_extent)); + extents_per_block = (extents_per_block/ 2) - 1; + + e_blocks = (extents + extents_per_block - 1) / extents_per_block; + e_blocks2 = (e_blocks + extents_per_block - 1) / extents_per_block; + e_blocks3 = (e_blocks2 + extents_per_block - 1) / extents_per_block; + e_blocks4 = (e_blocks3 + extents_per_block - 1) / extents_per_block; + return (e_blocks + e_blocks2 + e_blocks3 + e_blocks4) * + EXT2FS_CLUSTER_RATIO(fs); +} + +/* + * Find the place where we should start allocating blocks for the huge + * files. Leave <slack> free blocks at the beginning of the file + * system for things like metadata blocks. + */ +static blk64_t get_start_block(ext2_filsys fs, blk64_t slack) +{ + errcode_t retval; + blk64_t blk = fs->super->s_first_data_block, next; + blk64_t last_blk = ext2fs_blocks_count(fs->super) - 1; + + while (slack) { + retval = ext2fs_find_first_zero_block_bitmap2(fs->block_map, + blk, last_blk, &blk); + if (retval) + break; + + retval = ext2fs_find_first_set_block_bitmap2(fs->block_map, + blk, last_blk, &next); + if (retval) + next = last_blk; + + if (next - blk > slack) { + blk += slack; + break; + } + + slack -= (next - blk); + blk = next; + } + return blk; +} + +static blk64_t round_up_align(blk64_t b, unsigned long align, + blk64_t part_offset) +{ + unsigned long m; + + if (align == 0) + return b; + part_offset = part_offset % align; + m = (b + part_offset) % align; + if (m) + b += align - m; + return b; +} + +errcode_t mk_hugefiles(ext2_filsys fs, const char *device_name) +{ + unsigned long i; + ext2_ino_t dir; + errcode_t retval; + blk64_t fs_blocks, part_offset = 0; + unsigned long align; + int d, dsize; + char *t; + + if (!get_bool_from_profile(fs_types, "make_hugefiles", 0)) + return 0; + + if (!ext2fs_has_feature_extents(fs->super)) + return EXT2_ET_EXTENT_NOT_SUPPORTED; + + uid = get_int_from_profile(fs_types, "hugefiles_uid", 0); + gid = get_int_from_profile(fs_types, "hugefiles_gid", 0); + fs->umask = get_int_from_profile(fs_types, "hugefiles_umask", 077); + num_files = get_int_from_profile(fs_types, "num_hugefiles", 0); + t = get_string_from_profile(fs_types, "hugefiles_slack", "1M"); + num_slack = parse_num_blocks2(t, fs->super->s_log_block_size); + free(t); + t = get_string_from_profile(fs_types, "hugefiles_size", "0"); + num_blocks = parse_num_blocks2(t, fs->super->s_log_block_size); + free(t); + t = get_string_from_profile(fs_types, "hugefiles_align", "0"); + align = parse_num_blocks2(t, fs->super->s_log_block_size); + free(t); + if (get_bool_from_profile(fs_types, "hugefiles_align_disk", 0)) { + part_offset = get_partition_start(device_name) / + (fs->blocksize / 512); + if (part_offset % EXT2FS_CLUSTER_RATIO(fs)) { + fprintf(stderr, + _("Partition offset of %llu (%uk) blocks " + "not compatible with cluster size %u.\n"), + (unsigned long long) part_offset, fs->blocksize, + EXT2_CLUSTER_SIZE(fs->super)); + exit(1); + } + } + num_blocks = round_up_align(num_blocks, align, 0); + zero_hugefile = get_bool_from_profile(fs_types, "zero_hugefiles", + zero_hugefile); + + t = get_string_from_profile(fs_types, "hugefiles_dir", "/"); + retval = create_directory(fs, t, &dir); + free(t); + if (retval) + return retval; + + fn_prefix = get_string_from_profile(fs_types, "hugefiles_name", + "hugefile"); + idx_digits = get_int_from_profile(fs_types, "hugefiles_digits", 5); + d = int_log10(num_files) + 1; + if (idx_digits > d) + d = idx_digits; + dsize = strlen(fn_prefix) + d + 16; + fn_buf = malloc(dsize); + if (!fn_buf) { + free(fn_prefix); + return ENOMEM; + } + strcpy(fn_buf, fn_prefix); + fn_numbuf = fn_buf + strlen(fn_prefix); + free(fn_prefix); + + fs_blocks = ext2fs_free_blocks_count(fs->super); + if (fs_blocks < num_slack + align) + return ENOSPC; + fs_blocks -= num_slack + align; + if (num_blocks && num_blocks > fs_blocks) + return ENOSPC; + if (num_blocks == 0 && num_files == 0) + num_files = 1; + + if (num_files == 0 && num_blocks) { + num_files = fs_blocks / num_blocks; + fs_blocks -= (num_files / 16) + 1; + fs_blocks -= calc_overhead(fs, num_blocks) * num_files; + num_files = fs_blocks / num_blocks; + } + + if (num_blocks == 0 && num_files > 1) { + num_blocks = fs_blocks / num_files; + fs_blocks -= (num_files / 16) + 1; + fs_blocks -= calc_overhead(fs, num_blocks) * num_files; + num_blocks = fs_blocks / num_files; + } + + num_slack += (calc_overhead(fs, num_blocks ? num_blocks : fs_blocks) * + num_files); + num_slack += (num_files / 16) + 1; /* space for dir entries */ + goal = get_start_block(fs, num_slack); + goal = round_up_align(goal, align, part_offset); + + if ((num_blocks ? num_blocks : fs_blocks) > + (0x80000000UL / fs->blocksize)) + ext2fs_set_feature_large_file(fs->super); + + if (!quiet) { + if (zero_hugefile && verbose) + printf("%s", _("Huge files will be zero'ed\n")); + printf(_("Creating %lu huge file(s) "), num_files); + if (num_blocks) + printf(_("with %llu blocks each"), + (unsigned long long) num_blocks); + fputs(": ", stdout); + } + for (i=0; i < num_files; i++) { + ext2_ino_t ino; + + retval = mk_hugefile(fs, num_blocks, dir, i, &ino); + if (retval) { + com_err(program_name, retval, + _("while creating huge file %lu"), i); + goto errout; + } + } + if (!quiet) + fputs(_("done\n"), stdout); + +errout: + free(fn_buf); + return retval; +} |