Whamcloud - gitweb
Prevent i_dtime from being mistaken for an inode number post-2038 wraparound
[tools/e2fsprogs.git] / misc / e4defrag.c
index b6e2e31..e3011d7 100644 (file)
 #include <unistd.h>
 #include <ext2fs/ext2_types.h>
 #include <ext2fs/ext2fs.h>
-#include <linux/fs.h>
 #include <sys/ioctl.h>
 #include <ext2fs/fiemap.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/statfs.h>
-#include <sys/syscall.h>
 #include <sys/vfs.h>
 
+#include "../version.h"
+
 /* A relatively new ioctl interface ... */
 #ifndef EXT4_IOC_MOVE_EXT
 #define EXT4_IOC_MOVE_EXT      _IOWR('f', 15, struct move_extent)
@@ -164,97 +164,38 @@ struct frag_statistic_ino {
        char msg_buffer[PATH_MAX + 1];  /* pathname of the file */
 };
 
-char   lost_found_dir[PATH_MAX + 1];
-int    block_size;
-int    extents_before_defrag;
-int    extents_after_defrag;
-int    mode_flag;
-unsigned int   current_uid;
-unsigned int   defraged_file_count;
-unsigned int   frag_files_before_defrag;
-unsigned int   frag_files_after_defrag;
-unsigned int   regular_count;
-unsigned int   succeed_cnt;
-unsigned int   total_count;
-__u8 log_groups_per_flex;
-__u32 blocks_per_group;
-__u32 feature_incompat;
-ext4_fsblk_t   files_block_count;
-struct frag_statistic_ino      frag_rank[SHOW_FRAG_FILES];
-
-
-/* Local definitions of some syscalls glibc may not yet have */
-
-#ifndef HAVE_POSIX_FADVISE
-#warning Using locally defined posix_fadvise interface.
-
-#ifndef __NR_fadvise64_64
-#error Your kernel headers dont define __NR_fadvise64_64
-#endif
+static char    lost_found_dir[PATH_MAX + 1];
+static int     block_size;
+static int     extents_before_defrag;
+static int     extents_after_defrag;
+static int     mode_flag;
+static unsigned int    current_uid;
+static unsigned int    defraged_file_count;
+static unsigned int    frag_files_before_defrag;
+static unsigned int    frag_files_after_defrag;
+static unsigned int    regular_count;
+static unsigned int    succeed_cnt;
+static unsigned int    total_count;
+static __u8 log_groups_per_flex;
+static __u32 blocks_per_group;
+static __u32 feature_incompat;
+static ext4_fsblk_t    files_block_count;
+static struct frag_statistic_ino       frag_rank[SHOW_FRAG_FILES];
 
-/*
- * fadvise() -         Give advice about file access.
- *
- * @fd:                        defrag target file's descriptor.
- * @offset:            file offset.
- * @len:               area length.
- * @advise:            process flag.
- */
-static int posix_fadvise(int fd, loff_t offset, size_t len, int advise)
-{
-       return syscall(__NR_fadvise64_64, fd, offset, len, advise);
-}
-#endif /* ! HAVE_FADVISE64_64 */
-
-#ifndef HAVE_SYNC_FILE_RANGE
-#warning Using locally defined sync_file_range interface.
-
-#ifndef __NR_sync_file_range
-#ifndef __NR_sync_file_range2 /* ppc */
-#error Your kernel headers dont define __NR_sync_file_range
-#endif
-#endif
 
 /*
- * sync_file_range() - Sync file region.
- *
- * @fd:                        defrag target file's descriptor.
- * @offset:            file offset.
- * @length:            area length.
- * @flag:              process flag.
+ * We prefer posix_fadvise64 when available, as it allows 64bit offset on
+ * 32bit systems
  */
-int sync_file_range(int fd, loff_t offset, loff_t length, unsigned int flag)
-{
-#ifdef __NR_sync_file_range
-       return syscall(__NR_sync_file_range, fd, offset, length, flag);
-#else
-       return syscall(__NR_sync_file_range2, fd, flag, offset, length);
-#endif
-}
-#endif /* ! HAVE_SYNC_FILE_RANGE */
-
-#ifndef HAVE_FALLOCATE64
-#warning Using locally defined fallocate syscall interface.
-
-#ifndef __NR_fallocate
-#error Your kernel headers dont define __NR_fallocate
+#if defined(HAVE_POSIX_FADVISE64)
+#define posix_fadvise  posix_fadvise64
+#elif defined(HAVE_FADVISE64)
+#define posix_fadvise  fadvise64
+#elif !defined(HAVE_POSIX_FADVISE)
+#error posix_fadvise not available!
 #endif
 
 /*
- * fallocate64() -     Manipulate file space.
- *
- * @fd:                        defrag target file's descriptor.
- * @mode:              process flag.
- * @offset:            file offset.
- * @len:               file size.
- */
-static int fallocate64(int fd, int mode, loff_t offset, loff_t len)
-{
-       return syscall(__NR_fallocate, fd, mode, offset, len);
-}
-#endif /* ! HAVE_FALLOCATE */
-
-/*
  * get_mount_point() - Get device's mount point.
  *
  * @devname:           the device's name.
@@ -313,12 +254,12 @@ static int get_mount_point(const char *devname, char *mount_point,
  *
  * @file:              the file's name.
  */
-static int is_ext4(const char *file, char *devname)
+static int is_ext4(const char *file, char devname[PATH_MAX + 1])
 {
        int     maxlen = 0;
        int     len, ret;
+       int     type_is_ext4 = 0;
        FILE    *fp = NULL;
-       char    *mnt_type = NULL;
        /* Refer to /etc/mtab */
        const char      *mtab = MOUNTED;
        char    file_path[PATH_MAX + 1];
@@ -362,26 +303,16 @@ static int is_ext4(const char *file, char *devname)
 
                maxlen = len;
 
-               mnt_type = realloc(mnt_type, strlen(mnt->mnt_type) + 1);
-               if (mnt_type == NULL) {
-                       endmntent(fp);
-                       return -1;
-               }
-               memset(mnt_type, 0, strlen(mnt->mnt_type) + 1);
-               strncpy(mnt_type, mnt->mnt_type, strlen(mnt->mnt_type));
+               type_is_ext4 = !strcmp(mnt->mnt_type, FS_EXT4);
                strncpy(lost_found_dir, mnt->mnt_dir, PATH_MAX);
-               strncpy(devname, mnt->mnt_fsname, strlen(mnt->mnt_fsname) + 1);
+               strncpy(devname, mnt->mnt_fsname, PATH_MAX);
        }
 
        endmntent(fp);
-       if (mnt_type && strcmp(mnt_type, FS_EXT4) == 0) {
-               FREE(mnt_type);
+       if (type_is_ext4)
                return 0;
-       } else {
-               FREE(mnt_type);
-               PRINT_ERR_MSG(NGMSG_EXT4);
-               return -1;
-       }
+       PRINT_ERR_MSG(NGMSG_EXT4);
+       return -1;
 }
 
 /*
@@ -417,7 +348,7 @@ static int page_in_core(int fd, struct move_extent defrag_data,
 {
        long    pagesize;
        void    *page = NULL;
-       loff_t  offset, end_offset, length;
+       ext2_loff_t offset, end_offset, length;
 
        if (vec == NULL || *vec != NULL)
                return -1;
@@ -426,8 +357,8 @@ static int page_in_core(int fd, struct move_extent defrag_data,
        if (pagesize < 0)
                return -1;
        /* In mmap, offset should be a multiple of the page size */
-       offset = (loff_t)defrag_data.orig_start * block_size;
-       length = (loff_t)defrag_data.len * block_size;
+       offset = (ext2_loff_t)defrag_data.orig_start * block_size;
+       length = (ext2_loff_t)defrag_data.len * block_size;
        end_offset = offset + length;
        /* Round the offset down to the nearest multiple of pagesize */
        offset = (offset / pagesize) * pagesize;
@@ -440,8 +371,10 @@ static int page_in_core(int fd, struct move_extent defrag_data,
        *page_num = 0;
        *page_num = (length + pagesize - 1) / pagesize;
        *vec = (unsigned char *)calloc(*page_num, 1);
-       if (*vec == NULL)
+       if (*vec == NULL) {
+               munmap(page, length);
                return -1;
+       }
 
        /* Get information on whether pages are in core */
        if (mincore(page, (size_t)length, *vec) == -1 ||
@@ -471,15 +404,20 @@ static int defrag_fadvise(int fd, struct move_extent defrag_data,
                            SYNC_FILE_RANGE_WRITE |
                            SYNC_FILE_RANGE_WAIT_AFTER;
        unsigned int    i;
-       loff_t  offset;
+       ext2_loff_t     offset;
+
+       if (pagesize < 1)
+               return -1;
 
-       offset = (loff_t)defrag_data.orig_start * block_size;
+       offset = (ext2_loff_t)defrag_data.orig_start * block_size;
        offset = (offset / pagesize) * pagesize;
 
+#ifdef HAVE_SYNC_FILE_RANGE
        /* Sync file for fadvise process */
        if (sync_file_range(fd, offset,
-               (loff_t)pagesize * page_num, sync_flag) < 0)
+               (ext2_loff_t)pagesize * page_num, sync_flag) < 0)
                return -1;
+#endif
 
        /* Try to release buffer cache which this process used,
         * then other process can use the released buffer
@@ -489,7 +427,8 @@ static int defrag_fadvise(int fd, struct move_extent defrag_data,
                        offset += pagesize;
                        continue;
                }
-               if (posix_fadvise(fd, offset, pagesize, fadvise_flag) < 0) {
+               if ((errno = posix_fadvise(fd, offset,
+                                          pagesize, fadvise_flag)) != 0) {
                        if ((mode_flag & DETAIL) && flag) {
                                perror("\tFailed to fadvise");
                                flag = 0;
@@ -938,7 +877,9 @@ static int get_physical_count(struct fiemap_extent_list *physical_list_head)
 
        do {
                if ((ext_list_tmp->data.physical + ext_list_tmp->data.len)
-                               != ext_list_tmp->next->data.physical) {
+                               != ext_list_tmp->next->data.physical ||
+                   (ext_list_tmp->data.logical + ext_list_tmp->data.len)
+                               != ext_list_tmp->next->data.logical) {
                        /* This extent and next extent are not continuous. */
                        ret++;
                }
@@ -1062,9 +1003,11 @@ static int get_best_count(ext4_fsblk_t block_count)
        int ret;
        unsigned int flex_bg_num;
 
-       /* Calcuate best extents count */
+       if (blocks_per_group == 0)
+               return 1;
+
        if (feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) {
-               flex_bg_num = 1 << log_groups_per_flex;
+               flex_bg_num = 1U << log_groups_per_flex;
                ret = ((block_count - 1) /
                        ((ext4_fsblk_t)blocks_per_group *
                                flex_bg_num)) + 1;
@@ -1094,11 +1037,13 @@ static int file_statistic(const char *file, const struct stat64 *buf,
        __u64   size_per_ext = 0;
        float   ratio = 0.0;
        ext4_fsblk_t    blk_count = 0;
-       char    msg_buffer[PATH_MAX + 24];
+       char    msg_buffer[PATH_MAX + 48];
        struct fiemap_extent_list *physical_list_head = NULL;
        struct fiemap_extent_list *logical_list_head = NULL;
 
        defraged_file_count++;
+       if (defraged_file_count > total_count)
+               total_count = defraged_file_count;
 
        if (mode_flag & DETAIL) {
                if (total_count == 1 && regular_count == 1)
@@ -1220,10 +1165,13 @@ static int file_statistic(const char *file, const struct stat64 *buf,
                        do {
                                count++;
                                printf("[ext %d]:\tstart %llu:\tlogical "
-                                               "%llu:\tlen %llu\n", count,
-                                               ext_list_tmp->data.physical,
-                                               ext_list_tmp->data.logical,
-                                               ext_list_tmp->data.len);
+                                      "%llu:\tlen %llu\n", count,
+                                      (unsigned long long)
+                                      ext_list_tmp->data.physical,
+                                      (unsigned long long)
+                                      ext_list_tmp->data.logical,
+                                      (unsigned long long)
+                                      ext_list_tmp->data.len);
                                ext_list_tmp = ext_list_tmp->next;
                        } while (ext_list_tmp != logical_list_head);
 
@@ -1233,12 +1181,14 @@ static int file_statistic(const char *file, const struct stat64 *buf,
                        if (current_uid == ROOT_UID) {
                                if (strlen(file) > 40)
                                        printf("%s\n%50d/%-10d%6llu KB\n",
-                                               file, now_ext_count,
-                                               best_ext_count, size_per_ext);
+                                              file, now_ext_count,
+                                              best_ext_count,
+                                              (unsigned long long) size_per_ext);
                                else
                                        printf("%-40s%10d/%-10d%6llu KB\n",
-                                               file, now_ext_count,
-                                               best_ext_count, size_per_ext);
+                                              file, now_ext_count,
+                                              best_ext_count,
+                                              (unsigned long long) size_per_ext);
                        } else {
                                if (strlen(file) > 40)
                                        printf("%s\n%50d/%-10s%7s\n",
@@ -1256,19 +1206,22 @@ static int file_statistic(const char *file, const struct stat64 *buf,
 
        if (mode_flag & DETAIL) {
                /* Print statistic info */
-               sprintf(msg_buffer, "[%u/%u]%s",
-                               defraged_file_count, total_count, file);
+               sprintf(msg_buffer, "[%u/%u]%.*s",
+                               defraged_file_count, total_count,
+                       PATH_MAX, file);
                if (current_uid == ROOT_UID) {
                        if (strlen(msg_buffer) > 40)
                                printf("\033[79;0H\033[K%s\n"
-                                               "%50d/%-10d%6llu KB\n",
-                                               msg_buffer, now_ext_count,
-                                               best_ext_count, size_per_ext);
+                                      "%50d/%-10d%6llu KB\n",
+                                      msg_buffer, now_ext_count,
+                                      best_ext_count,
+                                      (unsigned long long) size_per_ext);
                        else
                                printf("\033[79;0H\033[K%-40s"
-                                               "%10d/%-10d%6llu KB\n",
-                                               msg_buffer, now_ext_count,
-                                               best_ext_count, size_per_ext);
+                                      "%10d/%-10d%6llu KB\n",
+                                      msg_buffer, now_ext_count,
+                                      best_ext_count,
+                                      (unsigned long long) size_per_ext);
                } else {
                        if (strlen(msg_buffer) > 40)
                                printf("\033[79;0H\033[K%s\n%50d/%-10s%7s\n",
@@ -1327,7 +1280,8 @@ out:
  * @start:             logical offset for defrag target file
  * @file_size:         defrag target filesize
  */
-static void print_progress(const char *file, loff_t start, loff_t file_size)
+static void print_progress(const char *file, ext2_loff_t start,
+                          ext2_loff_t file_size)
 {
        int percent = (start * 100) / file_size;
        printf("\033[79;0H\033[K[%u/%u]%s:\t%3d%%",
@@ -1349,7 +1303,7 @@ static void print_progress(const char *file, loff_t start, loff_t file_size)
 static int call_defrag(int fd, int donor_fd, const char *file,
        const struct stat64 *buf, struct fiemap_extent_list *ext_list_head)
 {
-       loff_t  start = 0;
+       ext2_loff_t     start = 0;
        unsigned int    page_num;
        unsigned char   *vec = NULL;
        int     defraged_ret = 0;
@@ -1465,6 +1419,8 @@ static int file_defrag(const char *file, const struct stat64 *buf,
        struct fiemap_extent_group      *orig_group_tmp = NULL;
 
        defraged_file_count++;
+       if (defraged_file_count > total_count)
+               total_count = defraged_file_count;
 
        if (mode_flag & DETAIL) {
                printf("[%u/%u]", defraged_file_count, total_count);
@@ -1554,10 +1510,7 @@ static int file_defrag(const char *file, const struct stat64 *buf,
                goto out;
        }
 
-       if (current_uid == ROOT_UID)
-               best = get_best_count(blk_count);
-       else
-               best = 1;
+       best = get_best_count(blk_count);
 
        if (file_frags_start <= best)
                goto check_improvement;
@@ -1602,9 +1555,9 @@ static int file_defrag(const char *file, const struct stat64 *buf,
        /* Allocate space for donor inode */
        orig_group_tmp = orig_group_head;
        do {
-               ret = fallocate64(donor_fd, 0,
-                 (loff_t)orig_group_tmp->start->data.logical * block_size,
-                 (loff_t)orig_group_tmp->len * block_size);
+               ret = fallocate(donor_fd, 0,
+                 (ext2_loff_t)orig_group_tmp->start->data.logical * block_size,
+                 (ext2_loff_t)orig_group_tmp->len * block_size);
                if (ret < 0) {
                        if (mode_flag & DETAIL) {
                                PRINT_FILE_NAME(file);
@@ -1626,7 +1579,7 @@ static int file_defrag(const char *file, const struct stat64 *buf,
                goto out;
        }
 
-       /* Calcuate donor inode's continuous physical region */
+       /* Calculate donor inode's continuous physical region */
        donor_physical_cnt = get_physical_count(donor_list_physical);
 
        /* Change donor extent list from physical to logical */
@@ -1721,12 +1674,15 @@ int main(int argc, char *argv[])
        int     i, j, ret = 0;
        int     flags = FTW_PHYS | FTW_MOUNT;
        int     arg_type = -1;
+       int     mount_dir_len = 0;
        int     success_flag = 0;
        char    dir_name[PATH_MAX + 1];
        char    dev_name[PATH_MAX + 1];
        struct stat64   buf;
        ext2_filsys fs = NULL;
 
+       printf("e4defrag %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
+
        /* Parse arguments */
        if (argc == 1)
                goto out;
@@ -1844,40 +1800,39 @@ int main(int argc, char *argv[])
 
                if (current_uid == ROOT_UID) {
                        /* Get super block info */
-                       ret = ext2fs_open(dev_name, 0, 0, block_size,
-                                       unix_io_manager, &fs);
+                       ret = ext2fs_open(dev_name, EXT2_FLAG_64BITS, 0,
+                                         block_size, unix_io_manager, &fs);
                        if (ret) {
-                               if (mode_flag & DETAIL) {
-                                       perror("Can't get super block info");
-                                       PRINT_FILE_NAME(argv[i]);
-                               }
-                               continue;
+                               if (mode_flag & DETAIL)
+                                       fprintf(stderr,
+                                               "Warning: couldn't get file "
+                                               "system details for %s: %s\n",
+                                               dev_name, error_message(ret));
+                       } else {
+                               blocks_per_group = fs->super->s_blocks_per_group;
+                               feature_incompat = fs->super->s_feature_incompat;
+                               log_groups_per_flex = fs->super->s_log_groups_per_flex;
+                               ext2fs_close_free(&fs);
                        }
-
-                       blocks_per_group = fs->super->s_blocks_per_group;
-                       feature_incompat = fs->super->s_feature_incompat;
-                       log_groups_per_flex = fs->super->s_log_groups_per_flex;
-
-                       ext2fs_close(fs);
                }
 
                switch (arg_type) {
+
                case DIRNAME:
                        if (!(mode_flag & STATISTIC))
                                printf("ext4 defragmentation "
                                        "for directory(%s)\n", argv[i]);
 
-                       int mount_dir_len = 0;
                        mount_dir_len = strnlen(lost_found_dir, PATH_MAX);
 
                        strncat(lost_found_dir, "/lost+found",
                                PATH_MAX - strnlen(lost_found_dir, PATH_MAX));
 
-                       /* Not the case("e4defrag  mount_piont_dir") */
+                       /* Not the case("e4defrag  mount_point_dir") */
                        if (dir_name[mount_dir_len] != '\0') {
                                /*
-                                * "e4defrag mount_piont_dir/lost+found"
-                                * or "e4defrag mount_piont_dir/lost+found/"
+                                * "e4defrag mount_point_dir/lost+found"
+                                * or "e4defrag mount_point_dir/lost+found/"
                                 */
                                if (strncmp(lost_found_dir, dir_name,
                                            strnlen(lost_found_dir,
@@ -1891,16 +1846,15 @@ int main(int argc, char *argv[])
                                        continue;
                                }
 
-                               /* "e4defrag mount_piont_dir/else_dir" */
+                               /* "e4defrag mount_point_dir/else_dir" */
                                memset(lost_found_dir, 0, PATH_MAX + 1);
                        }
+                       /* fall through */
                case DEVNAME:
                        if (arg_type == DEVNAME) {
-                               strncpy(lost_found_dir, dir_name,
-                                       strnlen(dir_name, PATH_MAX));
+                               strcpy(lost_found_dir, dir_name);
                                strncat(lost_found_dir, "/lost+found/",
-                                       PATH_MAX - strnlen(lost_found_dir,
-                                                          PATH_MAX));
+                                       PATH_MAX - strlen(lost_found_dir));
                        }
 
                        nftw64(dir_name, calc_entry_counts, FTW_OPEN_FD, flags);
@@ -1936,6 +1890,7 @@ int main(int argc, char *argv[])
                                                        frag_rank[j].msg_buffer,
                                                        frag_rank[j].now_count,
                                                        frag_rank[j].best_count,
+                                                       (unsigned long long)
                                                        frag_rank[j].
                                                                size_per_ext);
                                                } else if (strlen(frag_rank[j].
@@ -1946,6 +1901,7 @@ int main(int argc, char *argv[])
                                                        frag_rank[j].msg_buffer,
                                                        frag_rank[j].now_count,
                                                        frag_rank[j].best_count,
+                                                       (unsigned long long)
                                                        frag_rank[j].
                                                                size_per_ext);
                                                } else
@@ -2038,12 +1994,12 @@ int main(int argc, char *argv[])
                                                100 / files_block_count;
                                score = CALC_SCORE(files_ratio);
                                printf("\n Total/best extents\t\t\t\t%d/%d\n"
-                                       " Average size per extent"
-                                       "\t\t\t%llu KB\n"
-                                       " Fragmentation score\t\t\t\t%.0f\n",
-                                               extents_before_defrag,
-                                               extents_after_defrag,
-                                               size_per_ext, score);
+                                      " Average size per extent"
+                                      "\t\t\t%llu KB\n"
+                                      " Fragmentation score\t\t\t\t%.0f\n",
+                                      extents_before_defrag,
+                                      extents_after_defrag,
+                                      (unsigned long long) size_per_ext, score);
                                printf(" [0-30 no problem:"
                                        " 31-55 a little bit fragmented:"
                                        " 56- needs defrag]\n");