Whamcloud - gitweb
build: fix unused/uninitialized variable warnings
[tools/e2fsprogs.git] / misc / mk_hugefiles.c
index d4dadc4..7f1be2e 100644 (file)
@@ -3,9 +3,11 @@
  */
 
 #define _XOPEN_SOURCE 600 /* for inclusion of PATH_MAX in Solaris */
+#define _BSD_SOURCE      /* for makedev() and major() */
 
 #include "config.h"
 #include <stdio.h>
+#include <stdarg.h>
 #include <string.h>
 #include <strings.h>
 #include <fcntl.h>
@@ -60,6 +62,143 @@ static char *fn_buf;
 static char *fn_numbuf;
 int zero_hugefile = 1;
 
+#define SYSFS_PATH_LEN 256
+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)
 
@@ -119,7 +258,7 @@ static errcode_t mk_hugefile(ext2_filsys fs, blk64_t num,
 
 {
        errcode_t               retval;
-       blk64_t                 lblk, bend;
+       blk64_t                 lblk, bend = 0;
        __u64                   size;
        blk64_t                 left;
        blk64_t                 count = 0;
@@ -310,24 +449,26 @@ static blk64_t get_start_block(ext2_filsys fs, blk64_t 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;
@@ -348,7 +489,10 @@ errcode_t mk_hugefiles(ext2_filsys fs)
        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);
+       if (get_bool_from_profile(fs_types, "hugefiles_align_disk", 0))
+               part_offset = get_partition_start(device_name) /
+                       (fs->blocksize / 512);
+       num_blocks = round_up_align(num_blocks, align, 0);
        zero_hugefile = get_bool_from_profile(fs_types, "zero_hugefiles",
                                              zero_hugefile);
 
@@ -400,7 +544,7 @@ errcode_t mk_hugefiles(ext2_filsys fs)
        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);
+       goal = round_up_align(goal, align, part_offset);
 
        if ((num_blocks ? num_blocks : fs_blocks) >
            (0x80000000UL / fs->blocksize))