2010-08-25 09:01:37

by Kazuya Mio

[permalink] [raw]
Subject: [RFC][PATCH V2 4/5] e4defrag: unified bugfix/improvement patches

This patch unifies the following patches that have been posted but not merged
into e2fsprogs yet. And this patch also includes a little bugfix that doesn't
use ext2fs_swab32() in case of getting superblock information. You can use the
latest e4defrag if you apply this patch to e2fsprogs git tree.

Skip "rootfs" entry when checking for ext4 filesystem.
(Posted by Eric Sandeen)
http://marc.info/?l=linux-ext4&m=125238215627201&w=3%29

[RFC][PATCH v2 1/4] e4defrag: output size per extent by -c option
http://marc.info/?l=linux-ext4&m=125602666514382&w=4

[RFC][PATCH v2 2/4] e4defrag: fix file blocks calculation
http://marc.info/?l=linux-ext4&m=125602676114522&w=4

[RFC][PATCH v2 3/4] e4defrag: avoid unsuccessful return in non-privilege
http://marc.info/?l=linux-ext4&m=125602682614647&w=4

[RFC][PATCH v2 3/4] e4defrag: update man page about -c option
http://marc.info/?l=linux-ext4&m=125602694414881&w=4

[PATCH] e2fsprogs: Fix the overflow in e4defrag with 2GB over file
http://marc.info/?l=linux-ext4&m=126993099707986&w=4

Open the source file in read write mode to avoid failures from EXT4_IOC_EXT_MOVE
(Posted by Manish Katiyar)
http://marc.info/?l=linux-ext4&m=126387489114532&w=4

Signed-off-by: Kazuya Mio <[email protected]>
Signed-off-by: Akira Fujita <[email protected]>
---
misc/e4defrag.8.in | 14 ++--
misc/e4defrag.c | 179 +++++++++++++++++++++++++++++++----------------------
2 files changed, 116 insertions(+), 77 deletions(-)

diff --git a/misc/e4defrag.8.in b/misc/e4defrag.8.in
index 2c43b32..1a15755 100644
--- a/misc/e4defrag.8.in
+++ b/misc/e4defrag.8.in
@@ -35,16 +35,20 @@ point.
.SH OPTIONS
.TP
.B \-c
-Get the fragmentation count and calculate fragmentation score based on it
-before and after defrag. By seeing this score, we can determine whether we
-should execute
+Get a current fragmentation count and an ideal fragmentation count, and
+calculate fragmentation score based on them. By seeing this score, we can
+determine whether we should execute
.B e4defrag
to
.IR target .
When used with
.B \-v
-option, the fragmentation count before and after defrag is printed for each
-file.
+option, the current fragmentation count and the ideal fragmentation count are
+printed for each file.
+.IP
+Also this option outputs the average data size in one extent. If you see it,
+you'll find the file has close to ideal extents or not. Note that the maximum
+extent size is 131072KB in ext4 filesystem (if block size is 4KB).
.IP
If this option is specified,
.I target
diff --git a/misc/e4defrag.c b/misc/e4defrag.c
index 82e3868..61eb259 100644
--- a/misc/e4defrag.c
+++ b/misc/e4defrag.c
@@ -7,13 +7,7 @@
* Takashi Sato <[email protected]>
*/

-#ifndef _LARGEFILE_SOURCE
-#define _LARGEFILE_SOURCE
-#endif
-
-#ifndef _LARGEFILE64_SOURCE
-#define _LARGEFILE64_SOURCE
-#endif
+#define _FILE_OFFSET_BITS 64

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
@@ -59,8 +53,6 @@
#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 */
@@ -160,6 +152,7 @@ struct move_extent {
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 */
};
@@ -403,7 +396,7 @@ static int is_ext4(const char *file)
const char *mtab = MOUNTED;
char file_path[PATH_MAX + 1];
struct mntent *mnt = NULL;
- struct statfs64 fsbuf;
+ struct statfs fsbuf;

/* Get full path */
if (realpath(file, file_path) == NULL) {
@@ -412,7 +405,7 @@ static int is_ext4(const char *file)
return -1;
}

- if (statfs64(file_path, &fsbuf) < 0) {
+ if (statfs(file_path, &fsbuf) < 0) {
perror("Failed to get filesystem information");
PRINT_FILE_NAME(file);
return -1;
@@ -430,6 +423,8 @@ static int is_ext4(const char *file)
}

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)
@@ -470,7 +465,7 @@ static int is_ext4(const char *file)
* @ftwbuf: the pointer of a struct FTW.
*/
static int calc_entry_counts(const char *file EXT2FS_ATTR((unused)),
- const struct stat64 *buf, int flag EXT2FS_ATTR((unused)),
+ const struct stat *buf, int flag EXT2FS_ATTR((unused)),
struct FTW *ftwbuf EXT2FS_ATTR((unused)))
{
if (S_ISREG(buf->st_mode))
@@ -580,15 +575,14 @@ static int defrag_fadvise(int fd, struct move_extent defrag_data,
*
* @fd: defrag target file's descriptor.
* @file: file name.
- * @buf: the pointer of the struct stat64.
+ * @blk_count: file blocks.
*/
-static 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;
+ struct statfs fsbuf;

- if (fstatfs64(fd, &fsbuf) < 0) {
+ if (fstatfs(fd, &fsbuf) < 0) {
if (mode_flag & DETAIL) {
PRINT_FILE_NAME(file);
PRINT_ERR_MSG_WITH_ERRNO(
@@ -597,9 +591,6 @@ static int check_free_size(int fd, const char *file, const struct stat64 *buf)
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;
@@ -615,7 +606,7 @@ static int check_free_size(int fd, const char *file, const struct stat64 *buf)
/*
* file_frag_count() - Get file fragment count.
*
- * @fd: defrag target file's descriptor.
+ * @fd: defrag target file's descriptor.
*/
static int file_frag_count(int fd)
{
@@ -641,12 +632,13 @@ static int file_frag_count(int fd)
* file_check() - Check file's attributes.
*
* @fd: defrag target file's descriptor.
- * @buf: a pointer of the struct stat64.
- * @file: the file's name.
- * @extents: the file's extents.
+ * @buf: a pointer of the struct stat.
+ * @file: file name.
+ * @extents: file extents.
+ * @blk_count: file blocks.
*/
-static int file_check(int fd, const struct stat64 *buf, const char *file,
- int extents)
+static int file_check(int fd, const struct stat *buf, const char *file,
+ int extents, ext4_fsblk_t blk_count)
{
int ret;
struct flock lock;
@@ -658,7 +650,7 @@ static int file_check(int fd, const struct stat64 *buf, const char *file,
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"
@@ -1068,6 +1060,23 @@ static int change_physical_to_logical(
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.
*
@@ -1151,14 +1160,14 @@ static int get_superblock_info(const char *file, struct ext4_super_block *sb)
strnlen(mnt->mnt_fsname, PATH_MAX));
}

- fd = open64(dev_name, O_RDONLY);
+ fd = open(dev_name, O_RDONLY);
if (fd < 0) {
ret = -1;
goto out;
}

/* Set offset to read superblock */
- ret = lseek64(fd, SUPERBLOCK_OFFSET, SEEK_SET);
+ ret = lseek(fd, SUPERBLOCK_OFFSET, SEEK_SET);
if (ret < 0)
goto out;

@@ -1200,11 +1209,11 @@ static int get_best_count(ext4_fsblk_t block_count)
* file_statistic() - Get statistic info of the file's fragments.
*
* @file: the file's name.
- * @buf: the pointer of the struct stat64.
+ * @buf: the pointer of the struct stat.
* @flag: file type.
* @ftwbuf: the pointer of a struct FTW.
*/
-static int file_statistic(const char *file, const struct stat64 *buf,
+static int file_statistic(const char *file, const struct stat *buf,
int flag EXT2FS_ATTR((unused)),
struct FTW *ftwbuf EXT2FS_ATTR((unused)))
{
@@ -1212,6 +1221,7 @@ static int file_statistic(const char *file, const struct stat64 *buf,
int ret;
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];
@@ -1275,7 +1285,7 @@ static int file_statistic(const char *file, const struct stat64 *buf,
return 0;
}

- fd = open64(file, O_RDONLY);
+ fd = open(file, O_RDONLY);
if (fd < 0) {
if (mode_flag & DETAIL) {
PRINT_FILE_NAME(file);
@@ -1312,12 +1322,14 @@ static int file_statistic(const char *file, const struct stat64 *buf,
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 blocksize boundary */
+ size_per_ext = blk_count * (buf->st_blksize / 1024) /
+ now_ext_count;
ratio = (float)(physical_ext_count - best_ext_count) * 100 /
blk_count;

@@ -1346,16 +1358,16 @@ static int file_statistic(const char *file, const struct stat64 *buf,

} 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",
@@ -1378,14 +1390,14 @@ static int file_statistic(const char *file, const struct stat64 *buf,
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",
@@ -1401,8 +1413,20 @@ static int file_statistic(const char *file, const struct stat64 *buf,
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));
@@ -1410,6 +1434,7 @@ static int file_statistic(const char *file, const struct stat64 *buf,
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;
}
@@ -1447,11 +1472,11 @@ static void print_progress(const char *file, loff_t start, loff_t file_size)
* @fd: target file descriptor.
* @donor_fd: donor file descriptor.
* @file: target file name.
- * @buf: pointer of the struct stat64.
+ * @buf: pointer of the struct stat.
* @ext_list_head: head of the extent list.
*/
static int call_defrag(int fd, int donor_fd, const char *file,
- const struct stat64 *buf, struct fiemap_extent_list *ext_list_head)
+ const struct stat *buf, struct fiemap_extent_list *ext_list_head)
{
loff_t start = 0;
unsigned int page_num;
@@ -1541,11 +1566,11 @@ static int call_defrag(int fd, int donor_fd, const char *file,
* file_defrag() - Check file attributes and call ioctl to defrag.
*
* @file: the file's name.
- * @buf: the pointer of the struct stat64.
+ * @buf: the pointer of the struct stat.
* @flag: file type.
* @ftwbuf: the pointer of a struct FTW.
*/
-static int file_defrag(const char *file, const struct stat64 *buf,
+static int file_defrag(const char *file, const struct stat *buf,
int flag EXT2FS_ATTR((unused)),
struct FTW *ftwbuf EXT2FS_ATTR((unused)))
{
@@ -1556,6 +1581,7 @@ static int file_defrag(const char *file, const struct stat64 *buf,
int file_frags_start, file_frags_end;
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;
@@ -1605,7 +1631,7 @@ static int file_defrag(const char *file, const struct stat64 *buf,
return 0;
}

- fd = open64(file, O_RDONLY);
+ fd = open(file, O_RDWR);
if (fd < 0) {
if (mode_flag & DETAIL) {
PRINT_FILE_NAME(file);
@@ -1641,7 +1667,8 @@ static int file_defrag(const char *file, const struct stat64 *buf,
/* 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) {
@@ -1653,8 +1680,7 @@ static int file_defrag(const char *file, const struct stat64 *buf,
}

if (current_uid == ROOT_UID)
- best =
- get_best_count(SECTOR_TO_BLOCK(buf->st_blocks, block_size));
+ best = get_best_count(blk_count);
else
best = 1;

@@ -1675,7 +1701,7 @@ static int file_defrag(const char *file, const struct stat64 *buf,
memset(tmp_inode_name, 0, PATH_MAX + 8);
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);
+ donor_fd = open(tmp_inode_name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
if (donor_fd < 0) {
if (mode_flag & DETAIL) {
PRINT_FILE_NAME(file);
@@ -1822,7 +1848,7 @@ int main(int argc, char *argv[])
int arg_type = -1;
int success_flag = 0;
char dir_name[PATH_MAX + 1];
- struct stat64 buf;
+ struct stat buf;
struct ext4_super_block sb;

/* Parse arguments */
@@ -1876,7 +1902,7 @@ int main(int argc, char *argv[])
continue;
#endif

- if (lstat64(argv[i], &buf) < 0) {
+ if (lstat(argv[i], &buf) < 0) {
perror(NGMSG_FILE_INFO);
PRINT_FILE_NAME(argv[i]);
continue;
@@ -1886,7 +1912,7 @@ int main(int argc, char *argv[])
/* Block device */
if (get_mount_point(argv[i], dir_name, PATH_MAX) < 0)
continue;
- if (lstat64(dir_name, &buf) < 0) {
+ if (lstat(dir_name, &buf) < 0) {
perror(NGMSG_FILE_INFO);
PRINT_FILE_NAME(argv[i]);
continue;
@@ -1940,8 +1966,8 @@ int main(int argc, char *argv[])
continue;
}

- blocks_per_group = ext2fs_swab32(sb.s_blocks_per_group);
- feature_incompat = ext2fs_swab32(sb.s_feature_incompat);
+ blocks_per_group = sb.s_blocks_per_group;
+ feature_incompat = sb.s_feature_incompat;
log_groups_per_flex = sb.s_log_groups_per_flex;
}

@@ -1987,20 +2013,21 @@ int main(int argc, char *argv[])
PATH_MAX));
}

- nftw64(dir_name, calc_entry_counts, FTW_OPEN_FD, flags);
+ nftw(dir_name, calc_entry_counts, FTW_OPEN_FD, flags);

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;
}

- nftw64(dir_name, file_statistic,
+ nftw(dir_name, file_statistic,
FTW_OPEN_FD, flags);

if (succeed_cnt != 0 &&
@@ -2009,24 +2036,28 @@ int main(int argc, char *argv[])
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;
}
@@ -2034,7 +2065,7 @@ int main(int argc, char *argv[])
break;
}
/* File tree walk */
- nftw64(dir_name, file_defrag, FTW_OPEN_FD, flags);
+ nftw(dir_name, file_defrag, FTW_OPEN_FD, flags);
printf("\n\tSuccess:\t\t\t[ %u/%u ]\n", succeed_cnt,
total_count);
printf("\tFailure:\t\t\t[ %u/%u ]\n",
@@ -2109,19 +2140,23 @@ int main(int argc, char *argv[])
} 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]);


2010-11-16 17:48:25

by Eric Sandeen

[permalink] [raw]
Subject: Re: [RFC][PATCH V2 4/5] e4defrag: unified bugfix/improvement patches

On 8/25/10 3:52 AM, Kazuya Mio wrote:
> This patch unifies the following patches that have been posted but not merged
> into e2fsprogs yet. And this patch also includes a little bugfix that doesn't
> use ext2fs_swab32() in case of getting superblock information. You can use the
> latest e4defrag if you apply this patch to e2fsprogs git tree.

Ted, any plans to pull some of this in?

I don't think rolling this all up into 1 patch makes sense though; we
should maintain the individual changes and proper authorship.

thanks,
-Eric

> Skip "rootfs" entry when checking for ext4 filesystem.
> (Posted by Eric Sandeen)
> http://marc.info/?l=linux-ext4&m=125238215627201&w=3%29
>
> [RFC][PATCH v2 1/4] e4defrag: output size per extent by -c option
> http://marc.info/?l=linux-ext4&m=125602666514382&w=4
>
> [RFC][PATCH v2 2/4] e4defrag: fix file blocks calculation
> http://marc.info/?l=linux-ext4&m=125602676114522&w=4
>
> [RFC][PATCH v2 3/4] e4defrag: avoid unsuccessful return in non-privilege
> http://marc.info/?l=linux-ext4&m=125602682614647&w=4
>
> [RFC][PATCH v2 3/4] e4defrag: update man page about -c option
> http://marc.info/?l=linux-ext4&m=125602694414881&w=4
>
> [PATCH] e2fsprogs: Fix the overflow in e4defrag with 2GB over file
> http://marc.info/?l=linux-ext4&m=126993099707986&w=4
>
> Open the source file in read write mode to avoid failures from EXT4_IOC_EXT_MOVE
> (Posted by Manish Katiyar)
> http://marc.info/?l=linux-ext4&m=126387489114532&w=4
>
> Signed-off-by: Kazuya Mio <[email protected]>
> Signed-off-by: Akira Fujita <[email protected]>
> ---
> misc/e4defrag.8.in | 14 ++--
> misc/e4defrag.c | 179 +++++++++++++++++++++++++++++++----------------------
> 2 files changed, 116 insertions(+), 77 deletions(-)
>
> diff --git a/misc/e4defrag.8.in b/misc/e4defrag.8.in
> index 2c43b32..1a15755 100644
> --- a/misc/e4defrag.8.in
> +++ b/misc/e4defrag.8.in
> @@ -35,16 +35,20 @@ point.
> .SH OPTIONS
> .TP
> .B \-c
> -Get the fragmentation count and calculate fragmentation score based on it
> -before and after defrag. By seeing this score, we can determine whether we
> -should execute
> +Get a current fragmentation count and an ideal fragmentation count, and
> +calculate fragmentation score based on them. By seeing this score, we can
> +determine whether we should execute
> .B e4defrag
> to
> .IR target .
> When used with
> .B \-v
> -option, the fragmentation count before and after defrag is printed for each
> -file.
> +option, the current fragmentation count and the ideal fragmentation count are
> +printed for each file.
> +.IP
> +Also this option outputs the average data size in one extent. If you see it,
> +you'll find the file has close to ideal extents or not. Note that the maximum
> +extent size is 131072KB in ext4 filesystem (if block size is 4KB).
> .IP
> If this option is specified,
> .I target
> diff --git a/misc/e4defrag.c b/misc/e4defrag.c
> index 82e3868..61eb259 100644
> --- a/misc/e4defrag.c
> +++ b/misc/e4defrag.c
> @@ -7,13 +7,7 @@
> * Takashi Sato <[email protected]>
> */
>
> -#ifndef _LARGEFILE_SOURCE
> -#define _LARGEFILE_SOURCE
> -#endif
> -
> -#ifndef _LARGEFILE64_SOURCE
> -#define _LARGEFILE64_SOURCE
> -#endif
> +#define _FILE_OFFSET_BITS 64
>
> #ifndef _GNU_SOURCE
> #define _GNU_SOURCE
> @@ -59,8 +53,6 @@
> #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 */
> @@ -160,6 +152,7 @@ struct move_extent {
> 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 */
> };
> @@ -403,7 +396,7 @@ static int is_ext4(const char *file)
> const char *mtab = MOUNTED;
> char file_path[PATH_MAX + 1];
> struct mntent *mnt = NULL;
> - struct statfs64 fsbuf;
> + struct statfs fsbuf;
>
> /* Get full path */
> if (realpath(file, file_path) == NULL) {
> @@ -412,7 +405,7 @@ static int is_ext4(const char *file)
> return -1;
> }
>
> - if (statfs64(file_path, &fsbuf) < 0) {
> + if (statfs(file_path, &fsbuf) < 0) {
> perror("Failed to get filesystem information");
> PRINT_FILE_NAME(file);
> return -1;
> @@ -430,6 +423,8 @@ static int is_ext4(const char *file)
> }
>
> 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)
> @@ -470,7 +465,7 @@ static int is_ext4(const char *file)
> * @ftwbuf: the pointer of a struct FTW.
> */
> static int calc_entry_counts(const char *file EXT2FS_ATTR((unused)),
> - const struct stat64 *buf, int flag EXT2FS_ATTR((unused)),
> + const struct stat *buf, int flag EXT2FS_ATTR((unused)),
> struct FTW *ftwbuf EXT2FS_ATTR((unused)))
> {
> if (S_ISREG(buf->st_mode))
> @@ -580,15 +575,14 @@ static int defrag_fadvise(int fd, struct move_extent defrag_data,
> *
> * @fd: defrag target file's descriptor.
> * @file: file name.
> - * @buf: the pointer of the struct stat64.
> + * @blk_count: file blocks.
> */
> -static 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;
> + struct statfs fsbuf;
>
> - if (fstatfs64(fd, &fsbuf) < 0) {
> + if (fstatfs(fd, &fsbuf) < 0) {
> if (mode_flag & DETAIL) {
> PRINT_FILE_NAME(file);
> PRINT_ERR_MSG_WITH_ERRNO(
> @@ -597,9 +591,6 @@ static int check_free_size(int fd, const char *file, const struct stat64 *buf)
> 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;
> @@ -615,7 +606,7 @@ static int check_free_size(int fd, const char *file, const struct stat64 *buf)
> /*
> * file_frag_count() - Get file fragment count.
> *
> - * @fd: defrag target file's descriptor.
> + * @fd: defrag target file's descriptor.
> */
> static int file_frag_count(int fd)
> {
> @@ -641,12 +632,13 @@ static int file_frag_count(int fd)
> * file_check() - Check file's attributes.
> *
> * @fd: defrag target file's descriptor.
> - * @buf: a pointer of the struct stat64.
> - * @file: the file's name.
> - * @extents: the file's extents.
> + * @buf: a pointer of the struct stat.
> + * @file: file name.
> + * @extents: file extents.
> + * @blk_count: file blocks.
> */
> -static int file_check(int fd, const struct stat64 *buf, const char *file,
> - int extents)
> +static int file_check(int fd, const struct stat *buf, const char *file,
> + int extents, ext4_fsblk_t blk_count)
> {
> int ret;
> struct flock lock;
> @@ -658,7 +650,7 @@ static int file_check(int fd, const struct stat64 *buf, const char *file,
> 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"
> @@ -1068,6 +1060,23 @@ static int change_physical_to_logical(
> 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.
> *
> @@ -1151,14 +1160,14 @@ static int get_superblock_info(const char *file, struct ext4_super_block *sb)
> strnlen(mnt->mnt_fsname, PATH_MAX));
> }
>
> - fd = open64(dev_name, O_RDONLY);
> + fd = open(dev_name, O_RDONLY);
> if (fd < 0) {
> ret = -1;
> goto out;
> }
>
> /* Set offset to read superblock */
> - ret = lseek64(fd, SUPERBLOCK_OFFSET, SEEK_SET);
> + ret = lseek(fd, SUPERBLOCK_OFFSET, SEEK_SET);
> if (ret < 0)
> goto out;
>
> @@ -1200,11 +1209,11 @@ static int get_best_count(ext4_fsblk_t block_count)
> * file_statistic() - Get statistic info of the file's fragments.
> *
> * @file: the file's name.
> - * @buf: the pointer of the struct stat64.
> + * @buf: the pointer of the struct stat.
> * @flag: file type.
> * @ftwbuf: the pointer of a struct FTW.
> */
> -static int file_statistic(const char *file, const struct stat64 *buf,
> +static int file_statistic(const char *file, const struct stat *buf,
> int flag EXT2FS_ATTR((unused)),
> struct FTW *ftwbuf EXT2FS_ATTR((unused)))
> {
> @@ -1212,6 +1221,7 @@ static int file_statistic(const char *file, const struct stat64 *buf,
> int ret;
> 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];
> @@ -1275,7 +1285,7 @@ static int file_statistic(const char *file, const struct stat64 *buf,
> return 0;
> }
>
> - fd = open64(file, O_RDONLY);
> + fd = open(file, O_RDONLY);
> if (fd < 0) {
> if (mode_flag & DETAIL) {
> PRINT_FILE_NAME(file);
> @@ -1312,12 +1322,14 @@ static int file_statistic(const char *file, const struct stat64 *buf,
> 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 blocksize boundary */
> + size_per_ext = blk_count * (buf->st_blksize / 1024) /
> + now_ext_count;
> ratio = (float)(physical_ext_count - best_ext_count) * 100 /
> blk_count;
>
> @@ -1346,16 +1358,16 @@ static int file_statistic(const char *file, const struct stat64 *buf,
>
> } 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",
> @@ -1378,14 +1390,14 @@ static int file_statistic(const char *file, const struct stat64 *buf,
> 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",
> @@ -1401,8 +1413,20 @@ static int file_statistic(const char *file, const struct stat64 *buf,
> 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));
> @@ -1410,6 +1434,7 @@ static int file_statistic(const char *file, const struct stat64 *buf,
> 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;
> }
> @@ -1447,11 +1472,11 @@ static void print_progress(const char *file, loff_t start, loff_t file_size)
> * @fd: target file descriptor.
> * @donor_fd: donor file descriptor.
> * @file: target file name.
> - * @buf: pointer of the struct stat64.
> + * @buf: pointer of the struct stat.
> * @ext_list_head: head of the extent list.
> */
> static int call_defrag(int fd, int donor_fd, const char *file,
> - const struct stat64 *buf, struct fiemap_extent_list *ext_list_head)
> + const struct stat *buf, struct fiemap_extent_list *ext_list_head)
> {
> loff_t start = 0;
> unsigned int page_num;
> @@ -1541,11 +1566,11 @@ static int call_defrag(int fd, int donor_fd, const char *file,
> * file_defrag() - Check file attributes and call ioctl to defrag.
> *
> * @file: the file's name.
> - * @buf: the pointer of the struct stat64.
> + * @buf: the pointer of the struct stat.
> * @flag: file type.
> * @ftwbuf: the pointer of a struct FTW.
> */
> -static int file_defrag(const char *file, const struct stat64 *buf,
> +static int file_defrag(const char *file, const struct stat *buf,
> int flag EXT2FS_ATTR((unused)),
> struct FTW *ftwbuf EXT2FS_ATTR((unused)))
> {
> @@ -1556,6 +1581,7 @@ static int file_defrag(const char *file, const struct stat64 *buf,
> int file_frags_start, file_frags_end;
> 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;
> @@ -1605,7 +1631,7 @@ static int file_defrag(const char *file, const struct stat64 *buf,
> return 0;
> }
>
> - fd = open64(file, O_RDONLY);
> + fd = open(file, O_RDWR);
> if (fd < 0) {
> if (mode_flag & DETAIL) {
> PRINT_FILE_NAME(file);
> @@ -1641,7 +1667,8 @@ static int file_defrag(const char *file, const struct stat64 *buf,
> /* 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) {
> @@ -1653,8 +1680,7 @@ static int file_defrag(const char *file, const struct stat64 *buf,
> }
>
> if (current_uid == ROOT_UID)
> - best =
> - get_best_count(SECTOR_TO_BLOCK(buf->st_blocks, block_size));
> + best = get_best_count(blk_count);
> else
> best = 1;
>
> @@ -1675,7 +1701,7 @@ static int file_defrag(const char *file, const struct stat64 *buf,
> memset(tmp_inode_name, 0, PATH_MAX + 8);
> 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);
> + donor_fd = open(tmp_inode_name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
> if (donor_fd < 0) {
> if (mode_flag & DETAIL) {
> PRINT_FILE_NAME(file);
> @@ -1822,7 +1848,7 @@ int main(int argc, char *argv[])
> int arg_type = -1;
> int success_flag = 0;
> char dir_name[PATH_MAX + 1];
> - struct stat64 buf;
> + struct stat buf;
> struct ext4_super_block sb;
>
> /* Parse arguments */
> @@ -1876,7 +1902,7 @@ int main(int argc, char *argv[])
> continue;
> #endif
>
> - if (lstat64(argv[i], &buf) < 0) {
> + if (lstat(argv[i], &buf) < 0) {
> perror(NGMSG_FILE_INFO);
> PRINT_FILE_NAME(argv[i]);
> continue;
> @@ -1886,7 +1912,7 @@ int main(int argc, char *argv[])
> /* Block device */
> if (get_mount_point(argv[i], dir_name, PATH_MAX) < 0)
> continue;
> - if (lstat64(dir_name, &buf) < 0) {
> + if (lstat(dir_name, &buf) < 0) {
> perror(NGMSG_FILE_INFO);
> PRINT_FILE_NAME(argv[i]);
> continue;
> @@ -1940,8 +1966,8 @@ int main(int argc, char *argv[])
> continue;
> }
>
> - blocks_per_group = ext2fs_swab32(sb.s_blocks_per_group);
> - feature_incompat = ext2fs_swab32(sb.s_feature_incompat);
> + blocks_per_group = sb.s_blocks_per_group;
> + feature_incompat = sb.s_feature_incompat;
> log_groups_per_flex = sb.s_log_groups_per_flex;
> }
>
> @@ -1987,20 +2013,21 @@ int main(int argc, char *argv[])
> PATH_MAX));
> }
>
> - nftw64(dir_name, calc_entry_counts, FTW_OPEN_FD, flags);
> + nftw(dir_name, calc_entry_counts, FTW_OPEN_FD, flags);
>
> 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;
> }
>
> - nftw64(dir_name, file_statistic,
> + nftw(dir_name, file_statistic,
> FTW_OPEN_FD, flags);
>
> if (succeed_cnt != 0 &&
> @@ -2009,24 +2036,28 @@ int main(int argc, char *argv[])
> 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;
> }
> @@ -2034,7 +2065,7 @@ int main(int argc, char *argv[])
> break;
> }
> /* File tree walk */
> - nftw64(dir_name, file_defrag, FTW_OPEN_FD, flags);
> + nftw(dir_name, file_defrag, FTW_OPEN_FD, flags);
> printf("\n\tSuccess:\t\t\t[ %u/%u ]\n", succeed_cnt,
> total_count);
> printf("\tFailure:\t\t\t[ %u/%u ]\n",
> @@ -2109,19 +2140,23 @@ int main(int argc, char *argv[])
> } 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]);
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html