mirror of https://github.com/vitalif/e2fsprogs
435 lines
10 KiB
C
435 lines
10 KiB
C
/*
|
|
* mk_hugefiles.c -- create huge files
|
|
*/
|
|
|
|
#define _XOPEN_SOURCE 600 /* for inclusion of PATH_MAX in Solaris */
|
|
|
|
#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
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#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 "profile.h"
|
|
#include "prof_err.h"
|
|
#include "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 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;
|
|
|
|
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"), 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;
|
|
inode.i_size = size & 0xffffffff;
|
|
inode.i_size_high = (size >> 32);
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
next--;
|
|
|
|
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)
|
|
{
|
|
unsigned long m;
|
|
|
|
if (align == 0)
|
|
return b;
|
|
m = b % align;
|
|
if (m)
|
|
b += align - m;
|
|
return b;
|
|
}
|
|
|
|
errcode_t mk_hugefiles(ext2_filsys fs)
|
|
{
|
|
unsigned long i;
|
|
ext2_ino_t dir;
|
|
errcode_t retval;
|
|
blk64_t fs_blocks;
|
|
unsigned long align;
|
|
int d, dsize;
|
|
char *t;
|
|
|
|
if (!get_bool_from_profile(fs_types, "make_hugefiles", 0))
|
|
return 0;
|
|
|
|
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);
|
|
num_blocks = round_up_align(num_blocks, align);
|
|
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 ENOMEM;
|
|
fs_blocks -= num_slack + align;
|
|
if (num_blocks && num_blocks > fs_blocks)
|
|
return ENOMEM;
|
|
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_files;
|
|
num_slack += (num_files / 16) + 1; /* space for dir entries */
|
|
goal = get_start_block(fs, num_slack);
|
|
goal = round_up_align(goal, align);
|
|
|
|
if ((num_blocks ? num_blocks : fs_blocks) >
|
|
(0x80000000UL / fs->blocksize))
|
|
fs->super->s_feature_ro_compat |=
|
|
EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
|
|
|
|
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"), 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;
|
|
}
|