#define _LARGEFILE64_SOURCE
#endif
-#define _XOPEN_SOURCE 500
+#ifndef _GNU_SOURCE
#define _GNU_SOURCE
+#endif
+
+#include "config.h"
#include <ctype.h>
#include <dirent.h>
#include <endian.h>
#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>
-/* Ioctl command */
-#define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap)
-#define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent)
+#include "../version.h"
+
+/* A relatively new ioctl interface ... */
+#ifndef EXT4_IOC_MOVE_EXT
+#define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent)
+#endif
/* Macro functions */
#define PRINT_ERR_MSG(msg) fprintf(stderr, "%s\n", (msg))
#define STATISTIC_ERR_MSG_WITH_ERRNO(msg) \
fprintf(stderr, "\t%s:%s\n", (msg), strerror(errno))
#define min(x, y) (((x) > (y)) ? (y) : (x))
-#define SECTOR_TO_BLOCK(sectors, blocksize) \
- ((sectors) / ((blocksize) >> 9))
#define CALC_SCORE(ratio) \
((ratio) > 10 ? (80 + 20 * (ratio) / 100) : (8 * (ratio)))
/* Wrap up the free function */
#define EXT2FS_ATTR(x)
#endif
-#ifndef __NR_fadvise64
-#define __NR_fadvise64 250
-#endif
-
-#ifndef __NR_sync_file_range
-#define __NR_sync_file_range 314
-#endif
-
-#ifndef __NR_fallocate
-#define __NR_fallocate 324
-#endif
-
-#ifndef POSIX_FADV_DONTNEED
-#if defined(__s390x__)
-#define POSIX_FADV_DONTNEED 6 /* Don't need these pages */
-#else
-#define POSIX_FADV_DONTNEED 4 /* Don't need these pages */
-#endif
-#endif
-
-#ifndef SYNC_FILE_RANGE_WAIT_BEFORE
-#define SYNC_FILE_RANGE_WAIT_BEFORE 1
-#endif
-#ifndef SYNC_FILE_RANGE_WRITE
-#define SYNC_FILE_RANGE_WRITE 2
-#endif
-#ifndef SYNC_FILE_RANGE_WAIT_AFTER
-#define SYNC_FILE_RANGE_WAIT_AFTER 4
-#endif
-
/* The mode of defrag */
#define DETAIL 0x01
#define STATISTIC 0x02
/* Definition of flex_bg */
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
-/* The following four macros are used for ioctl FS_IOC_FIEMAP
- * FIEMAP_FLAG_SYNC: sync file data before map.
- * FIEMAP_EXTENT_LAST: last extent in file.
- * FIEMAP_MAX_OFFSET: max file offset.
+/* The following macro is used for ioctl FS_IOC_FIEMAP
* EXTENT_MAX_COUNT: the maximum number of extents for exchanging between
* kernel-space and user-space per ioctl
*/
-#define FIEMAP_FLAG_SYNC 0x00000001
-#define FIEMAP_EXTENT_LAST 0x00000001
-#define FIEMAP_EXTENT_UNWRITTEN 0x00000800
-#define FIEMAP_MAX_OFFSET (~0ULL)
#define EXTENT_MAX_COUNT 512
/* The following macros are error message */
__u64 moved_len; /* moved block length */
};
-struct fiemap_extent {
- __u64 fe_logical; /* logical offset in bytes for the start of
- * the extent from the beginning of the file */
- __u64 fe_physical; /* physical offset in bytes for the start
- * of the extent from the beginning
- * of the disk */
- __u64 fe_length; /* length in bytes for this extent */
- __u64 fe_reserved64[2];
- __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */
- __u32 fe_reserved[3];
-};
-
-struct fiemap {
- __u64 fm_start; /* logical offset (inclusive) at
- * which to start mapping (in) */
- __u64 fm_length; /* logical length of mapping which
- * userspace wants (in) */
- __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */
- __u32 fm_mapped_extents;/* number of extents that were mapped (out) */
- __u32 fm_extent_count; /* size of fm_extents array (in) */
- __u32 fm_reserved;
- struct fiemap_extent fm_extents[0];/* array of mapped extents (out) */
-};
-
struct frag_statistic_ino {
int now_count; /* the file's extents count of before defrag */
int best_count; /* the best file's extents count */
+ __u64 size_per_ext; /* size(KB) per extent */
float ratio; /* the ratio of fragmentation */
char msg_buffer[PATH_MAX + 1]; /* pathname of the file */
};
-typedef __u16 __le16;
-typedef __u32 __le32;
-typedef __u64 __le64;
-
-/*
- * Structure of the super block
- */
-struct ext4_super_block {
-/*00*/ __le32 s_inodes_count; /* Inodes count */
- __le32 s_blocks_count_lo; /* Blocks count */
- __le32 s_r_blocks_count_lo; /* Reserved blocks count */
- __le32 s_free_blocks_count_lo; /* Free blocks count */
-/*10*/ __le32 s_free_inodes_count; /* Free inodes count */
- __le32 s_first_data_block; /* First Data Block */
- __le32 s_log_block_size; /* Block size */
- __le32 s_obso_log_frag_size; /* Obsoleted fragment size */
-/*20*/ __le32 s_blocks_per_group; /* # Blocks per group */
- __le32 s_obso_frags_per_group; /* Obsoleted fragments per group */
- __le32 s_inodes_per_group; /* # Inodes per group */
- __le32 s_mtime; /* Mount time */
-/*30*/ __le32 s_wtime; /* Write time */
- __le16 s_mnt_count; /* Mount count */
- __le16 s_max_mnt_count; /* Maximal mount count */
- __le16 s_magic; /* Magic signature */
- __le16 s_state; /* File system state */
- __le16 s_errors; /* Behaviour when detecting errors */
- __le16 s_minor_rev_level; /* minor revision level */
-/*40*/ __le32 s_lastcheck; /* time of last check */
- __le32 s_checkinterval; /* max. time between checks */
- __le32 s_creator_os; /* OS */
- __le32 s_rev_level; /* Revision level */
-/*50*/ __le16 s_def_resuid; /* Default uid for reserved blocks */
- __le16 s_def_resgid; /* Default gid for reserved blocks */
- /*
- * These fields are for EXT4_DYNAMIC_REV superblocks only.
- *
- * Note: the difference between the compatible feature set and
- * the incompatible feature set is that if there is a bit set
- * in the incompatible feature set that the kernel doesn't
- * know about, it should refuse to mount the filesystem.
- *
- * e2fsck's requirements are more strict; if it doesn't know
- * about a feature in either the compatible or incompatible
- * feature set, it must abort and not try to meddle with
- * things it doesn't understand...
- */
- __le32 s_first_ino; /* First non-reserved inode */
- __le16 s_inode_size; /* size of inode structure */
- __le16 s_block_group_nr; /* block group # of this superblock */
- __le32 s_feature_compat; /* compatible feature set */
-/*60*/ __le32 s_feature_incompat; /* incompatible feature set */
- __le32 s_feature_ro_compat; /* readonly-compatible feature set */
-/*68*/ __u8 s_uuid[16]; /* 128-bit uuid for volume */
-/*78*/ char s_volume_name[16]; /* volume name */
-/*88*/ char s_last_mounted[64]; /* directory where last mounted */
-/*C8*/ __le32 s_algorithm_usage_bitmap; /* For compression */
- /*
- * Performance hints. Directory preallocation should only
- * happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on.
- */
- __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
- __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
- __le16 s_reserved_gdt_blocks; /* Per group desc for online growth */
- /*
- * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set.
- */
-/*D0*/ __u8 s_journal_uuid[16]; /* uuid of journal superblock */
-/*E0*/ __le32 s_journal_inum; /* inode number of journal file */
- __le32 s_journal_dev; /* device number of journal file */
- __le32 s_last_orphan; /* start of list of inodes to delete */
- __le32 s_hash_seed[4]; /* HTREE hash seed */
- __u8 s_def_hash_version; /* Default hash version to use */
- __u8 s_reserved_char_pad;
- __le16 s_desc_size; /* size of group descriptor */
-/*100*/ __le32 s_default_mount_opts;
- __le32 s_first_meta_bg; /* First metablock block group */
- __le32 s_mkfs_time; /* When the filesystem was created */
- __le32 s_jnl_blocks[17]; /* Backup of the journal inode */
- /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
-/*150*/ __le32 s_blocks_count_hi; /* Blocks count */
- __le32 s_r_blocks_count_hi; /* Reserved blocks count */
- __le32 s_free_blocks_count_hi; /* Free blocks count */
- __le16 s_min_extra_isize; /* All inodes have at least # bytes */
- __le16 s_want_extra_isize; /* New inodes should reserve # bytes */
- __le32 s_flags; /* Miscellaneous flags */
- __le16 s_raid_stride; /* RAID stride */
- __le16 s_mmp_interval; /* # seconds to wait in MMP checking */
- __le64 s_mmp_block; /* Block for multi-mount protection */
- __le32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
- __u8 s_log_groups_per_flex; /* FLEX_BG group size */
- __u8 s_reserved_char_pad2;
- __le16 s_reserved_pad;
- __u32 s_reserved[162]; /* Padding to the end of the block */
-};
+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];
-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;
-__le32 blocks_per_group;
-__le32 feature_incompat;
-ext4_fsblk_t files_block_count;
-struct frag_statistic_ino frag_rank[SHOW_FRAG_FILES];
/*
- * ext2fs_swab32() - Change endian.
- *
- * @val: the entry used for change.
+ * We prefer posix_fadvise64 when available, as it allows 64bit offset on
+ * 32bit systems
*/
-__u32 ext2fs_swab32(__u32 val)
-{
-#if BYTE_ORDER == BIG_ENDIAN
- return (val >> 24) | ((val >> 8) & 0xFF00) |
- ((val << 8) & 0xFF0000) | (val << 24);
-#else
- return val;
+#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
-}
-
-/*
- * fadvise() - Give advice about file access.
- *
- * @fd: defrag target file's descriptor.
- * @offset: file offset.
- * @len: area length.
- * @advise: process flag.
- */
-int fadvise(int fd, loff_t offset, size_t len, int advise)
-{
- return syscall(__NR_fadvise64, fd, offset, len, advise);
-}
-
-/*
- * sync_file_range() - Sync file region.
- *
- * @fd: defrag target file's descriptor.
- * @offset: file offset.
- * @length: area length.
- * @flag: process flag.
- */
-int sync_file_range(int fd, loff_t offset, loff_t length, unsigned int flag)
-{
- return syscall(__NR_sync_file_range, fd, offset, length, flag);
-}
-/*
- * fallocate() - Manipulate file space.
- *
- * @fd: defrag target file's descriptor.
- * @mode: process flag.
- * @offset: file offset.
- * @len: file size.
- */
-int fallocate(int fd, int mode, loff_t offset, loff_t len)
-{
- return syscall(__NR_fallocate, fd, mode, offset, len);
-}
+#ifndef HAVE_FALLOCATE64
+#error fallocate64 not available!
+#endif /* ! HAVE_FALLOCATE64 */
/*
* get_mount_point() - Get device's mount point.
* @mount_point: the mount point.
* @dir_path_len: the length of directory.
*/
-int get_mount_point(const char *devname, char *mount_point, int dir_path_len)
+static int get_mount_point(const char *devname, char *mount_point,
+ int dir_path_len)
{
/* Refer to /etc/mtab */
- char *mtab = MOUNTED;
- FILE *fp = NULL;
+ const char *mtab = MOUNTED;
+ FILE *fp = NULL;
struct mntent *mnt = NULL;
+ struct stat64 sb;
+
+ if (stat64(devname, &sb) < 0) {
+ perror(NGMSG_FILE_INFO);
+ PRINT_FILE_NAME(devname);
+ return -1;
+ }
fp = setmntent(mtab, "r");
if (fp == NULL) {
}
while ((mnt = getmntent(fp)) != NULL) {
- if (strcmp(devname, mnt->mnt_fsname) != 0)
+ struct stat64 ms;
+
+ /*
+ * To handle device symlinks, we see if the
+ * device number matches, not the name
+ */
+ if (stat64(mnt->mnt_fsname, &ms) < 0)
+ continue;
+ if (sb.st_rdev != ms.st_rdev)
continue;
endmntent(fp);
*
* @file: the file's name.
*/
-int is_ext4(const char *file)
+static int is_ext4(const char *file, char *devname)
{
int maxlen = 0;
int len, ret;
FILE *fp = NULL;
char *mnt_type = NULL;
/* Refer to /etc/mtab */
- char *mtab = MOUNTED;
+ const char *mtab = MOUNTED;
char file_path[PATH_MAX + 1];
struct mntent *mnt = NULL;
struct statfs64 fsbuf;
}
while ((mnt = getmntent(fp)) != NULL) {
+ if (mnt->mnt_fsname[0] != '/')
+ continue;
len = strlen(mnt->mnt_dir);
ret = memcmp(file_path, mnt->mnt_dir, len);
if (ret != 0)
memset(mnt_type, 0, strlen(mnt->mnt_type) + 1);
strncpy(mnt_type, mnt->mnt_type, strlen(mnt->mnt_type));
strncpy(lost_found_dir, mnt->mnt_dir, PATH_MAX);
+ strncpy(devname, mnt->mnt_fsname, strlen(mnt->mnt_fsname) + 1);
}
endmntent(fp);
- if (strcmp(mnt_type, FS_EXT4) == 0) {
+ if (mnt_type && strcmp(mnt_type, FS_EXT4) == 0) {
FREE(mnt_type);
return 0;
} else {
* @flag: file type.
* @ftwbuf: the pointer of a struct FTW.
*/
-int calc_entry_counts(const char *file EXT2FS_ATTR((unused)),
+static int calc_entry_counts(const char *file EXT2FS_ATTR((unused)),
const struct stat64 *buf, int flag EXT2FS_ATTR((unused)),
struct FTW *ftwbuf EXT2FS_ATTR((unused)))
{
* @vec: page state array.
* @page_num: page number.
*/
-int page_in_core(int fd, struct move_extent defrag_data,
+static int page_in_core(int fd, struct move_extent defrag_data,
unsigned char **vec, unsigned int *page_num)
{
- long pagesize = sysconf(_SC_PAGESIZE);
+ long pagesize;
void *page = NULL;
loff_t offset, end_offset, length;
if (vec == NULL || *vec != NULL)
return -1;
+ pagesize = sysconf(_SC_PAGESIZE);
+ 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;
*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 ||
* @vec: page state array.
* @page_num: page number.
*/
-int defrag_fadvise(int fd, struct move_extent defrag_data,
+static int defrag_fadvise(int fd, struct move_extent defrag_data,
unsigned char *vec, unsigned int page_num)
{
int flag = 1;
unsigned int i;
loff_t offset;
+ if (pagesize < 1)
+ return -1;
+
offset = (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)
return -1;
+#endif
/* Try to release buffer cache which this process used,
* then other process can use the released buffer
offset += pagesize;
continue;
}
- if (fadvise(fd, offset, pagesize, fadvise_flag) < 0) {
+ if (posix_fadvise(fd, offset, pagesize, fadvise_flag) < 0) {
if ((mode_flag & DETAIL) && flag) {
perror("\tFailed to fadvise");
flag = 0;
*
* @fd: defrag target file's descriptor.
* @file: file name.
- * @buf: the pointer of the struct stat64.
+ * @blk_count: file blocks.
*/
-int check_free_size(int fd, const char *file, const struct stat64 *buf)
+static int check_free_size(int fd, const char *file, ext4_fsblk_t blk_count)
{
- ext4_fsblk_t blk_count;
ext4_fsblk_t free_blk_count;
struct statfs64 fsbuf;
return -1;
}
- /* Target file size measured by filesystem IO blocksize */
- blk_count = SECTOR_TO_BLOCK(buf->st_blocks, fsbuf.f_bsize);
-
/* Compute free space for root and normal user separately */
if (current_uid == ROOT_UID)
free_blk_count = fsbuf.f_bfree;
*
* @fd: defrag target file's descriptor.
*/
-int file_frag_count(int fd)
+static int file_frag_count(int fd)
{
int ret;
struct fiemap fiemap_buf;
*
* @fd: defrag target file's descriptor.
* @buf: a pointer of the struct stat64.
- * @file: the file's name.
- * @extents: the file's extents.
+ * @file: file name.
+ * @extents: file extents.
+ * @blk_count: file blocks.
*/
-int file_check(int fd, const struct stat64 *buf, const char *file,
- int extents)
+static int file_check(int fd, const struct stat64 *buf, const char *file,
+ int extents, ext4_fsblk_t blk_count)
{
int ret;
struct flock lock;
lock.l_len = 0;
/* Free space */
- ret = check_free_size(fd, file, buf);
+ ret = check_free_size(fd, file, blk_count);
if (ret < 0) {
if ((mode_flag & DETAIL) && ret == -ENOSPC) {
printf("\033[79;0H\033[K[%u/%u] \"%s\"\t\t"
* @ext_list_head: the head of logical extent list.
* @ext: the extent element which will be inserted.
*/
-int insert_extent_by_logical(struct fiemap_extent_list **ext_list_head,
+static int insert_extent_by_logical(struct fiemap_extent_list **ext_list_head,
struct fiemap_extent_list *ext)
{
struct fiemap_extent_list *ext_list_tmp = *ext_list_head;
* @ext_list_head: the head of physical extent list.
* @ext: the extent element which will be inserted.
*/
-int insert_extent_by_physical(struct fiemap_extent_list **ext_list_head,
+static int insert_extent_by_physical(struct fiemap_extent_list **ext_list_head,
struct fiemap_extent_list *ext)
{
struct fiemap_extent_list *ext_list_tmp = *ext_list_head;
* @ext_group_head: the head of a exts_group list.
* @exts_group: the exts_group element which will be inserted.
*/
-int insert_exts_group(struct fiemap_extent_group **ext_group_head,
+static int insert_exts_group(struct fiemap_extent_group **ext_group_head,
struct fiemap_extent_group *exts_group)
{
struct fiemap_extent_group *ext_group_tmp = NULL;
* @ext_list_head: the head of the extent list.
* @ext_group_head: the head of the target exts_group list.
*/
-int join_extents(struct fiemap_extent_list *ext_list_head,
+static int join_extents(struct fiemap_extent_list *ext_list_head,
struct fiemap_extent_group **ext_group_head)
{
__u64 len = ext_list_head->data.len;
* @fd: defrag target file's descriptor.
* @ext_list_head: the head of the extent list.
*/
-int get_file_extents(int fd, struct fiemap_extent_list **ext_list_head)
+static int get_file_extents(int fd, struct fiemap_extent_list **ext_list_head)
{
__u32 i;
int ret;
fiemap_buf->fm_start = pos;
memset(ext_buf, 0, ext_buf_size);
ret = ioctl(fd, FS_IOC_FIEMAP, fiemap_buf);
- if (ret < 0)
+ if (ret < 0 || fiemap_buf->fm_mapped_extents == 0)
goto out;
for (i = 0; i < fiemap_buf->fm_mapped_extents; i++) {
ext_list = NULL;
*
* @logical_list_head: the head of the logical extent list.
*/
-int get_logical_count(struct fiemap_extent_list *logical_list_head)
+static int get_logical_count(struct fiemap_extent_list *logical_list_head)
{
int ret = 0;
struct fiemap_extent_list *ext_list_tmp = logical_list_head;
*
* @physical_list_head: the head of the physical extent list.
*/
-int get_physical_count(struct fiemap_extent_list *physical_list_head)
+static int get_physical_count(struct fiemap_extent_list *physical_list_head)
{
int ret = 0;
struct fiemap_extent_list *ext_list_tmp = 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++;
}
* @physical_list_head: the head of physical extent list.
* @logical_list_head: the head of logical extent list.
*/
-int change_physical_to_logical(struct fiemap_extent_list **physical_list_head,
- struct fiemap_extent_list **logical_list_head)
+static int change_physical_to_logical(
+ struct fiemap_extent_list **physical_list_head,
+ struct fiemap_extent_list **logical_list_head)
{
int ret;
struct fiemap_extent_list *ext_list_tmp = *physical_list_head;
return 0;
}
+/* get_file_blocks() - Get total file blocks.
+ *
+ * @ext_list_head: the extent list head of the target file
+ */
+static ext4_fsblk_t get_file_blocks(struct fiemap_extent_list *ext_list_head)
+{
+ ext4_fsblk_t blk_count = 0;
+ struct fiemap_extent_list *ext_list_tmp = ext_list_head;
+
+ do {
+ blk_count += ext_list_tmp->data.len;
+ ext_list_tmp = ext_list_tmp->next;
+ } while (ext_list_tmp != ext_list_head);
+
+ return blk_count;
+}
+
/*
* free_ext() - Free the extent list.
*
* @ext_list_head: the extent list head of which will be free.
*/
-void free_ext(struct fiemap_extent_list *ext_list_head)
+static void free_ext(struct fiemap_extent_list *ext_list_head)
{
struct fiemap_extent_list *ext_list_tmp = NULL;
*
* @*ext_group_head: the exts_group list head which will be free.
*/
- void free_exts_group(struct fiemap_extent_group *ext_group_head)
+static void free_exts_group(struct fiemap_extent_group *ext_group_head)
{
struct fiemap_extent_group *ext_group_tmp = NULL;
free(ext_group_head);
}
-/*
- * get_superblock_info() - Get superblock info by the file name.
- *
- * @file: the file's name.
- * @sb: the pointer of the struct ext4_super_block.
- */
-int get_superblock_info(const char *file, struct ext4_super_block *sb)
-{
- /* Refer to /etc/mtab */
- char *mtab = MOUNTED;
- FILE *fp = NULL;
-
- int fd = -1;
- int ret;
- size_t maxlen = 0;
- size_t len;
- char dev_name[PATH_MAX + 1];
- struct mntent *mnt = NULL;
-
- fp = setmntent(mtab, "r");
- if (fp == NULL)
- return -1;
-
- while ((mnt = getmntent(fp)) != NULL) {
- len = strlen(mnt->mnt_dir);
- ret = memcmp(file, mnt->mnt_dir, len);
- if (ret != 0)
- continue;
-
- if (len < maxlen)
- continue;
-
- maxlen = len;
-
- memset(dev_name, 0, PATH_MAX + 1);
- strncpy(dev_name, mnt->mnt_fsname,
- strnlen(mnt->mnt_fsname, PATH_MAX));
- }
-
- fd = open64(dev_name, O_RDONLY);
- if (fd < 0) {
- ret = -1;
- goto out;
- }
-
- /* Set offset to read superblock */
- ret = lseek64(fd, SUPERBLOCK_OFFSET, SEEK_SET);
- if (ret < 0)
- goto out;
-
- ret = read(fd, sb, sizeof(struct ext4_super_block));
- if (ret < 0)
- goto out;
-
-out:
- if (fd != -1)
- close(fd);
- endmntent(fp);
- return ret;
-}
-
/*
* get_best_count() - Get the file best extents count.
*
* @block_count: the file's physical block count.
*/
-int get_best_count(ext4_fsblk_t block_count)
+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;
ret = ((block_count - 1) /
* @flag: file type.
* @ftwbuf: the pointer of a struct FTW.
*/
-int file_statistic(const char *file, const struct stat64 *buf,
+static int file_statistic(const char *file, const struct stat64 *buf,
int flag EXT2FS_ATTR((unused)),
struct FTW *ftwbuf EXT2FS_ATTR((unused)))
{
int fd;
int ret;
- int now_ext_count, best_ext_count, physical_ext_count;
+ int now_ext_count, best_ext_count = 0, physical_ext_count;
int i, j;
+ __u64 size_per_ext = 0;
float ratio = 0.0;
ext4_fsblk_t blk_count = 0;
char msg_buffer[PATH_MAX + 24];
now_ext_count = get_logical_count(logical_list_head);
if (current_uid == ROOT_UID) {
- /* Calculate fragment ratio */
- blk_count =
- SECTOR_TO_BLOCK(buf->st_blocks, block_size);
+ /* Calculate the size per extent */
+ blk_count = get_file_blocks(logical_list_head);
best_ext_count = get_best_count(blk_count);
+ /* e4defrag rounds size_per_ext up to a block size boundary */
+ size_per_ext = blk_count * (buf->st_blksize / 1024) /
+ now_ext_count;
+
ratio = (float)(physical_ext_count - best_ext_count) * 100 /
blk_count;
} else {
printf("%-40s%10s/%-10s%9s\n",
- "<File>", "now", "best", "ratio");
+ "<File>", "now", "best", "size/ext");
if (current_uid == ROOT_UID) {
if (strlen(file) > 40)
- printf("%s\n%50d/%-10d%8.2f%%\n",
+ printf("%s\n%50d/%-10d%6llu KB\n",
file, now_ext_count,
- best_ext_count, ratio);
+ best_ext_count, size_per_ext);
else
- printf("%-40s%10d/%-10d%8.2f%%\n",
+ printf("%-40s%10d/%-10d%6llu KB\n",
file, now_ext_count,
- best_ext_count, ratio);
+ best_ext_count, size_per_ext);
} else {
if (strlen(file) > 40)
printf("%s\n%50d/%-10s%7s\n",
if (current_uid == ROOT_UID) {
if (strlen(msg_buffer) > 40)
printf("\033[79;0H\033[K%s\n"
- "%50d/%-10d%8.2f%%\n",
+ "%50d/%-10d%6llu KB\n",
msg_buffer, now_ext_count,
- best_ext_count, ratio);
+ best_ext_count, size_per_ext);
else
printf("\033[79;0H\033[K%-40s"
- "%10d/%-10d%8.2f%%\n",
+ "%10d/%-10d%6llu KB\n",
msg_buffer, now_ext_count,
- best_ext_count, ratio);
+ best_ext_count, size_per_ext);
} else {
if (strlen(msg_buffer) > 40)
printf("\033[79;0H\033[K%s\n%50d/%-10s%7s\n",
for (i = 0; i < SHOW_FRAG_FILES; i++) {
if (ratio >= frag_rank[i].ratio) {
for (j = SHOW_FRAG_FILES - 1; j > i; j--) {
- memcpy(&frag_rank[j], &frag_rank[j - 1],
+ memset(&frag_rank[j], 0,
sizeof(struct frag_statistic_ino));
+ strncpy(frag_rank[j].msg_buffer,
+ frag_rank[j - 1].msg_buffer,
+ strnlen(frag_rank[j - 1].msg_buffer,
+ PATH_MAX));
+ frag_rank[j].now_count =
+ frag_rank[j - 1].now_count;
+ frag_rank[j].best_count =
+ frag_rank[j - 1].best_count;
+ frag_rank[j].size_per_ext =
+ frag_rank[j - 1].size_per_ext;
+ frag_rank[j].ratio =
+ frag_rank[j - 1].ratio;
}
memset(&frag_rank[i], 0,
sizeof(struct frag_statistic_ino));
strnlen(file, PATH_MAX));
frag_rank[i].now_count = now_ext_count;
frag_rank[i].best_count = best_ext_count;
+ frag_rank[i].size_per_ext = size_per_ext;
frag_rank[i].ratio = ratio;
break;
}
* @start: logical offset for defrag target file
* @file_size: defrag target filesize
*/
-void print_progress(const char *file, loff_t start, loff_t file_size)
+static void print_progress(const char *file, loff_t start, loff_t file_size)
{
int percent = (start * 100) / file_size;
printf("\033[79;0H\033[K[%u/%u]%s:\t%3d%%",
* @buf: pointer of the struct stat64.
* @ext_list_head: head of the extent list.
*/
-int call_defrag(int fd, int donor_fd, const char *file,
+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;
if (mode_flag & DETAIL) {
printf("\n");
PRINT_ERR_MSG_WITH_ERRNO(
- "Failed to defrag");
+ "Failed to defrag with "
+ "EXT4_IOC_MOVE_EXT ioctl");
+ if (errno == ENOTTY)
+ printf("\tAt least 2.6.31-rc1 of "
+ "vanilla kernel is required\n");
} else {
printf("\t[ NG ]\n");
}
* @flag: file type.
* @ftwbuf: the pointer of a struct FTW.
*/
-int file_defrag(const char *file, const struct stat64 *buf,
+static int file_defrag(const char *file, const struct stat64 *buf,
int flag EXT2FS_ATTR((unused)),
struct FTW *ftwbuf EXT2FS_ATTR((unused)))
{
int ret;
int best;
int file_frags_start, file_frags_end;
- int orig_physical_cnt, donor_physical_cnt;
+ int orig_physical_cnt, donor_physical_cnt = 0;
char tmp_inode_name[PATH_MAX + 8];
+ ext4_fsblk_t blk_count = 0;
struct fiemap_extent_list *orig_list_physical = NULL;
struct fiemap_extent_list *orig_list_logical = NULL;
struct fiemap_extent_list *donor_list_physical = NULL;
return 0;
}
- fd = open64(file, O_RDONLY);
+ fd = open64(file, O_RDWR);
if (fd < 0) {
if (mode_flag & DETAIL) {
PRINT_FILE_NAME(file);
/* Count file fragments before defrag */
file_frags_start = get_logical_count(orig_list_logical);
- if (file_check(fd, buf, file, file_frags_start) < 0)
+ blk_count = get_file_blocks(orig_list_logical);
+ if (file_check(fd, buf, file, file_frags_start, blk_count) < 0)
goto out;
if (fsync(fd) < 0) {
goto out;
}
- if (current_uid == ROOT_UID)
- best =
- get_best_count(SECTOR_TO_BLOCK(buf->st_blocks, block_size));
- else
- best = 1;
+ best = get_best_count(blk_count);
if (file_frags_start <= best)
goto check_improvement;
/* Create donor inode */
memset(tmp_inode_name, 0, PATH_MAX + 8);
- sprintf(tmp_inode_name, "%.*s.defrag", strnlen(file, PATH_MAX), file);
+ sprintf(tmp_inode_name, "%.*s.defrag",
+ (int)strnlen(file, PATH_MAX), file);
donor_fd = open64(tmp_inode_name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
if (donor_fd < 0) {
if (mode_flag & DETAIL) {
/* Allocate space for donor inode */
orig_group_tmp = orig_group_head;
do {
- ret = fallocate(donor_fd, 0,
+ ret = fallocate64(donor_fd, 0,
(loff_t)orig_group_tmp->start->data.logical * block_size,
(loff_t)orig_group_tmp->len * block_size);
if (ret < 0) {
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 */
int main(int argc, char *argv[])
{
int opt;
- int i, j;
+ 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;
- struct ext4_super_block sb;
+ ext2_filsys fs = NULL;
+
+ printf("e4defrag %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
/* Parse arguments */
if (argc == 1)
log_groups_per_flex = 0;
memset(dir_name, 0, PATH_MAX + 1);
+ memset(dev_name, 0, PATH_MAX + 1);
memset(lost_found_dir, 0, PATH_MAX + 1);
memset(frag_rank, 0,
sizeof(struct frag_statistic_ino) * SHOW_FRAG_FILES);
continue;
}
+ /* Handle i.e. lvm device symlinks */
+ if (S_ISLNK(buf.st_mode)) {
+ struct stat64 buf2;
+
+ if (stat64(argv[i], &buf2) == 0 &&
+ S_ISBLK(buf2.st_mode))
+ buf = buf2;
+ }
+
if (S_ISBLK(buf.st_mode)) {
/* Block device */
+ strncpy(dev_name, argv[i], strnlen(argv[i], PATH_MAX));
if (get_mount_point(argv[i], dir_name, PATH_MAX) < 0)
continue;
if (lstat64(dir_name, &buf) < 0) {
* filesystem type checked in get_mount_point()
*/
if (arg_type == FILENAME || arg_type == DIRNAME) {
- if (is_ext4(argv[i]) < 0)
+ if (is_ext4(argv[i], dev_name) < 0)
continue;
if (realpath(argv[i], dir_name) == NULL) {
perror("Couldn't get full path");
if (current_uid == ROOT_UID) {
/* Get super block info */
- memset(&sb, 0, sizeof(struct ext4_super_block));
- if (get_superblock_info(dir_name, &sb) < 0) {
- if (mode_flag & DETAIL) {
- perror("Can't get super block info");
- PRINT_FILE_NAME(argv[i]);
- }
- continue;
+ ret = ext2fs_open(dev_name, EXT2_FLAG_64BITS, 0,
+ block_size, unix_io_manager, &fs);
+ if (ret) {
+ 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 = ext2fs_swab32(sb.s_blocks_per_group);
- feature_incompat = ext2fs_swab32(sb.s_feature_incompat);
- log_groups_per_flex = sb.s_log_groups_per_flex;
}
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,
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,
if (mode_flag & STATISTIC) {
if (mode_flag & DETAIL)
printf("%-40s%10s/%-10s%9s\n",
- "<File>", "now", "best", "ratio");
+ "<File>", "now", "best", "size/ext");
if (!(mode_flag & DETAIL) &&
current_uid != ROOT_UID) {
printf(" Done.\n");
+ success_flag = 1;
continue;
}
printf("\n");
printf("%-40s%10s/%-10s%9s\n",
"<Fragmented files>", "now",
- "best", "ratio");
+ "best", "size/ext");
for (j = 0; j < SHOW_FRAG_FILES; j++) {
if (strlen(frag_rank[j].
msg_buffer) > 37) {
printf("%d. %s\n%50d/"
- "%-10d%8.2f%%\n", j + 1,
+ "%-10d%6llu KB\n",
+ j + 1,
frag_rank[j].msg_buffer,
frag_rank[j].now_count,
frag_rank[j].best_count,
- frag_rank[j].ratio);
+ frag_rank[j].
+ size_per_ext);
} else if (strlen(frag_rank[j].
msg_buffer) > 0) {
printf("%d. %-37s%10d/"
- "%-10d%8.2f%%\n", j + 1,
+ "%-10d%6llu KB\n",
+ j + 1,
frag_rank[j].msg_buffer,
frag_rank[j].now_count,
frag_rank[j].best_count,
- frag_rank[j].ratio);
+ frag_rank[j].
+ size_per_ext);
} else
break;
}
} else {
float files_ratio = 0.0;
float score = 0.0;
+ __u64 size_per_ext = files_block_count *
+ (buf.st_blksize / 1024) /
+ extents_before_defrag;
files_ratio = (float)(extents_before_defrag -
extents_after_defrag) *
100 / files_block_count;
score = CALC_SCORE(files_ratio);
printf("\n Total/best extents\t\t\t\t%d/%d\n"
- " Fragmentation ratio\t\t\t\t%.2f%%\n"
- " Fragmentation score\t\t\t\t%.2f\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,
- files_ratio, score);
+ size_per_ext, score);
printf(" [0-30 no problem:"
" 31-55 a little bit fragmented:"
- " 55- needs defrag]\n");
+ " 56- needs defrag]\n");
if (arg_type == DEVNAME)
- printf(" This device(%s) ", argv[i]);
+ printf(" This device (%s) ", argv[i]);
else if (arg_type == DIRNAME)
- printf(" This directory(%s) ", argv[i]);
+ printf(" This directory (%s) ",
+ argv[i]);
else
- printf(" This file(%s) ", argv[i]);
+ printf(" This file (%s) ", argv[i]);
if (score > BOUND_SCORE)
printf("needs defragmentation.\n");