*/
#define _XOPEN_SOURCE 600 /* for inclusion of PATH_MAX in Solaris */
+#define _BSD_SOURCE /* for makedev() and major() */
+#define _DEFAULT_SOURCE /* since glibc 2.20 _BSD_SOURCE is deprecated */
#include "config.h"
#include <stdio.h>
+#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#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 "e2p/e2p.h"
#include "ext2fs/ext2fs.h"
#include "util.h"
-#include "profile.h"
-#include "prof_err.h"
-#include "nls-enable.h"
+#include "support/profile.h"
+#include "support/prof_err.h"
+#include "support/nls-enable.h"
#include "mke2fs.h"
static int uid;
static char *fn_numbuf;
int zero_hugefile = 1;
+#define SYSFS_PATH_LEN 300
+typedef char sysfs_path_t[SYSFS_PATH_LEN];
+
+#ifndef HAVE_SNPRINTF
+/*
+ * We are very careful to avoid needing to worry about buffer
+ * overflows, so we don't really need to use snprintf() except as an
+ * additional safety check. So if snprintf() is not present, it's
+ * safe to fall back to vsprintf(). This provides portability since
+ * vsprintf() is guaranteed by C89, while snprintf() is only
+ * guaranteed by C99 --- which for example, Microsoft Visual Studio
+ * has *still* not bothered to implement. :-/ (Not that I expect
+ * mke2fs to be ported to MS Visual Studio any time soon, but
+ * libext2fs *does* get built on Microsoft platforms, and we might
+ * want to move this into libext2fs some day.)
+ */
+static int my_snprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = vsprintf(str, format, ap);
+ va_end(ap);
+ return ret;
+}
+
+#define snprintf my_snprintf
+#endif
+
+/*
+ * Fall back to Linux's definitions of makedev and major are needed.
+ * The search_sysfs_block() function is highly unlikely to work on
+ * non-Linux systems anyway.
+ */
+#ifndef makedev
+#define makedev(maj, min) (((maj) << 8) + (min))
+#endif
+
+static char *search_sysfs_block(dev_t devno, sysfs_path_t ret_path)
+{
+ struct dirent *de, *p_de;
+ DIR *dir = NULL, *p_dir = NULL;
+ FILE *f;
+ sysfs_path_t path, p_path;
+ unsigned int major, minor;
+ char *ret = ret_path;
+
+ ret_path[0] = 0;
+ if ((dir = opendir("/sys/block")) == NULL)
+ return NULL;
+ while ((de = readdir(dir)) != NULL) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
+ strlen(de->d_name) > sizeof(path)-32)
+ continue;
+ snprintf(path, SYSFS_PATH_LEN,
+ "/sys/block/%s/dev", de->d_name);
+ f = fopen(path, "r");
+ if (f &&
+ (fscanf(f, "%u:%u", &major, &minor) == 2)) {
+ fclose(f); f = NULL;
+ if (makedev(major, minor) == devno) {
+ snprintf(ret_path, SYSFS_PATH_LEN,
+ "/sys/block/%s", de->d_name);
+ goto success;
+ }
+#ifdef major
+ if (major(devno) != major)
+ continue;
+#endif
+ }
+ if (f)
+ fclose(f);
+
+ snprintf(path, SYSFS_PATH_LEN, "/sys/block/%s", de->d_name);
+
+ if (p_dir)
+ closedir(p_dir);
+ if ((p_dir = opendir(path)) == NULL)
+ continue;
+ while ((p_de = readdir(p_dir)) != NULL) {
+ if (!strcmp(p_de->d_name, ".") ||
+ !strcmp(p_de->d_name, "..") ||
+ (strlen(p_de->d_name) >
+ SYSFS_PATH_LEN - strlen(path) - 32))
+ continue;
+ snprintf(p_path, SYSFS_PATH_LEN, "%s/%s/dev",
+ path, p_de->d_name);
+
+ f = fopen(p_path, "r");
+ if (f &&
+ (fscanf(f, "%u:%u", &major, &minor) == 2) &&
+ (((major << 8) + minor) == devno)) {
+ fclose(f);
+ snprintf(ret_path, SYSFS_PATH_LEN, "%s/%s",
+ path, p_de->d_name);
+ goto success;
+ }
+ if (f)
+ fclose(f);
+ }
+ }
+ ret = NULL;
+success:
+ if (dir)
+ closedir(dir);
+ if (p_dir)
+ closedir(p_dir);
+ return ret;
+}
+
+static blk64_t get_partition_start(const char *device_name)
+{
+ unsigned long long start;
+ sysfs_path_t path;
+ struct stat st;
+ FILE *f;
+ char *cp;
+ int n;
+
+ if ((stat(device_name, &st) < 0) || !S_ISBLK(st.st_mode))
+ return 0;
+
+ cp = search_sysfs_block(st.st_rdev, path);
+ if (!cp)
+ return 0;
+ if (strlen(path) > SYSFS_PATH_LEN - sizeof("/start"))
+ return 0;
+ strcat(path, "/start");
+ f = fopen(path, "r");
+ if (!f)
+ return 0;
+ n = fscanf(f, "%llu", &start);
+ fclose(f);
+ return (n == 1) ? start : 0;
+}
+
static errcode_t create_directory(ext2_filsys fs, char *dir,
ext2_ino_t *ret_ino)
{
errcode_t retval;
- blk64_t lblk, bend;
+ blk64_t lblk, bend = 0;
__u64 size;
blk64_t left;
blk64_t count = 0;
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) {
if (retval)
goto errout;
size = (__u64) count * fs->blocksize;
- inode.i_size = size & 0xffffffff;
- inode.i_size_high = (size >> 32);
+ retval = ext2fs_inode_size_set(fs, &inode, size);
+ if (retval)
+ goto errout;
retval = ext2fs_write_new_inode(fs, *ino, &inode);
if (retval)
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;
+ return (e_blocks + e_blocks2 + e_blocks3 + e_blocks4) *
+ EXT2FS_CLUSTER_RATIO(fs);
}
/*
blk, last_blk, &next);
if (retval)
next = last_blk;
- next--;
if (next - blk > slack) {
blk += slack;
return blk;
}
-static blk64_t round_up_align(blk64_t b, unsigned long align)
+static blk64_t round_up_align(blk64_t b, unsigned long align,
+ blk64_t part_offset)
{
unsigned long m;
if (align == 0)
return b;
- m = b % align;
+ part_offset = part_offset % align;
+ m = (b + part_offset) % align;
if (m)
b += align - m;
return b;
}
-errcode_t mk_hugefiles(ext2_filsys fs)
+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;
+ 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);
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_int_from_profile(fs_types, "zero_hugefiles",
- zero_hugefile);
+ 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"),
+ 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);
fs_blocks = ext2fs_free_blocks_count(fs->super);
if (fs_blocks < num_slack + align)
- return ENOMEM;
+ return ENOSPC;
fs_blocks -= num_slack + align;
if (num_blocks && num_blocks > fs_blocks)
- return ENOMEM;
+ return ENOSPC;
if (num_blocks == 0 && num_files == 0)
num_files = 1;
num_blocks = fs_blocks / num_files;
}
- num_slack += calc_overhead(fs, num_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);
+ 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(_("Huge files will be zero'ed\n"));
+ 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);