Whamcloud - gitweb
5b72cfacae6662fff19f63b705d98287b7096b35
[tools/e2fsprogs.git] / misc / e4defrag.c
1 /*
2  * e4defrag.c - ext4 filesystem defragmenter
3  *
4  * Copyright (C) 2009 NEC Software Tohoku, Ltd.
5  *
6  * Author: Akira Fujita <a-fujita@rs.jp.nec.com>
7  *         Takashi Sato <t-sato@yk.jp.nec.com>
8  */
9
10 #ifndef _LARGEFILE_SOURCE
11 #define _LARGEFILE_SOURCE
12 #endif
13
14 #ifndef _LARGEFILE64_SOURCE
15 #define _LARGEFILE64_SOURCE
16 #endif
17
18 #ifndef _GNU_SOURCE
19 #define _GNU_SOURCE
20 #endif
21
22 #include <ctype.h>
23 #include <dirent.h>
24 #include <endian.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <ftw.h>
28 #include <limits.h>
29 #include <mntent.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <ext2fs/ext2_types.h>
35 #include <ext2fs/ext2fs.h>
36 #include <linux/fs.h>
37 #include <sys/ioctl.h>
38 #include <ext2fs/fiemap.h>
39 #include <sys/mman.h>
40 #include <sys/stat.h>
41 #include <sys/statfs.h>
42 #include <sys/syscall.h>
43 #include <sys/vfs.h>
44
45 /* A relatively new ioctl interface ... */
46 #ifndef EXT4_IOC_MOVE_EXT
47 #define EXT4_IOC_MOVE_EXT      _IOWR('f', 15, struct move_extent)
48 #endif
49
50 /* Macro functions */
51 #define PRINT_ERR_MSG(msg)      fprintf(stderr, "%s\n", (msg))
52 #define IN_FTW_PRINT_ERR_MSG(msg)       \
53         fprintf(stderr, "\t%s\t\t[ NG ]\n", (msg))
54 #define PRINT_FILE_NAME(file)   fprintf(stderr, " \"%s\"\n", (file))
55 #define PRINT_ERR_MSG_WITH_ERRNO(msg)   \
56         fprintf(stderr, "\t%s:%s\t[ NG ]\n", (msg), strerror(errno))
57 #define STATISTIC_ERR_MSG(msg)  \
58         fprintf(stderr, "\t%s\n", (msg))
59 #define STATISTIC_ERR_MSG_WITH_ERRNO(msg)       \
60         fprintf(stderr, "\t%s:%s\n", (msg), strerror(errno))
61 #define min(x, y) (((x) > (y)) ? (y) : (x))
62 #define SECTOR_TO_BLOCK(sectors, blocksize) \
63         ((sectors) / ((blocksize) >> 9))
64 #define CALC_SCORE(ratio) \
65         ((ratio) > 10 ? (80 + 20 * (ratio) / 100) : (8 * (ratio)))
66 /* Wrap up the free function */
67 #define FREE(tmp)                               \
68         do {                                    \
69                 if ((tmp) != NULL)              \
70                         free(tmp);              \
71         } while (0)                             \
72 /* Insert list2 after list1 */
73 #define insert(list1, list2)                    \
74         do {                                    \
75                 list2->next = list1->next;      \
76                 list1->next->prev = list2;      \
77                 list2->prev = list1;            \
78                 list1->next = list2;            \
79         } while (0)
80
81 /* To delete unused warning */
82 #ifdef __GNUC__
83 #define EXT2FS_ATTR(x) __attribute__(x)
84 #else
85 #define EXT2FS_ATTR(x)
86 #endif
87
88 /* The mode of defrag */
89 #define DETAIL                  0x01
90 #define STATISTIC               0x02
91
92 #define DEVNAME                 0
93 #define DIRNAME                 1
94 #define FILENAME                2
95
96 #define FTW_OPEN_FD             2000
97
98 #define FS_EXT4                 "ext4"
99 #define ROOT_UID                0
100
101 #define BOUND_SCORE             55
102 #define SHOW_FRAG_FILES 5
103
104 /* Magic number for ext4 */
105 #define EXT4_SUPER_MAGIC        0xEF53
106
107 /* Definition of flex_bg */
108 #define EXT4_FEATURE_INCOMPAT_FLEX_BG           0x0200
109
110 /* The following macro is used for ioctl FS_IOC_FIEMAP
111  * EXTENT_MAX_COUNT:    the maximum number of extents for exchanging between
112  *                      kernel-space and user-space per ioctl
113  */
114 #define EXTENT_MAX_COUNT        512
115
116 /* The following macros are error message */
117 #define MSG_USAGE               \
118 "Usage  : e4defrag [-v] file...| directory...| device...\n\
119         : e4defrag  -c  file...| directory...| device...\n"
120
121 #define NGMSG_EXT4              "Filesystem is not ext4 filesystem"
122 #define NGMSG_FILE_EXTENT       "Failed to get file extents"
123 #define NGMSG_FILE_INFO         "Failed to get file information"
124 #define NGMSG_FILE_OPEN         "Failed to open"
125 #define NGMSG_FILE_UNREG        "File is not regular file"
126 #define NGMSG_LOST_FOUND        "Can not process \"lost+found\""
127
128 /* Data type for filesystem-wide blocks number */
129 typedef unsigned long long ext4_fsblk_t;
130
131 struct fiemap_extent_data {
132         __u64 len;                      /* blocks count */
133         __u64 logical;          /* start logical block number */
134         ext4_fsblk_t physical;          /* start physical block number */
135 };
136
137 struct fiemap_extent_list {
138         struct fiemap_extent_list *prev;
139         struct fiemap_extent_list *next;
140         struct fiemap_extent_data data; /* extent belong to file */
141 };
142
143 struct fiemap_extent_group {
144         struct fiemap_extent_group *prev;
145         struct fiemap_extent_group *next;
146         __u64 len;      /* length of this continuous region */
147         struct fiemap_extent_list *start;       /* start ext */
148         struct fiemap_extent_list *end;         /* end ext */
149 };
150
151 struct move_extent {
152         __s32 reserved; /* original file descriptor */
153         __u32 donor_fd; /* donor file descriptor */
154         __u64 orig_start;       /* logical start offset in block for orig */
155         __u64 donor_start;      /* logical start offset in block for donor */
156         __u64 len;      /* block length to be moved */
157         __u64 moved_len;        /* moved block length */
158 };
159
160 struct frag_statistic_ino {
161         int now_count;  /* the file's extents count of before defrag */
162         int best_count; /* the best file's extents count */
163         float ratio;    /* the ratio of fragmentation */
164         char msg_buffer[PATH_MAX + 1];  /* pathname of the file */
165 };
166
167 typedef __u16 __le16;
168 typedef __u32 __le32;
169 typedef __u64 __le64;
170
171 /*
172  * Structure of the super block
173  */
174 struct ext4_super_block {
175 /*00*/  __le32  s_inodes_count;         /* Inodes count */
176         __le32  s_blocks_count_lo;      /* Blocks count */
177         __le32  s_r_blocks_count_lo;    /* Reserved blocks count */
178         __le32  s_free_blocks_count_lo; /* Free blocks count */
179 /*10*/  __le32  s_free_inodes_count;    /* Free inodes count */
180         __le32  s_first_data_block;     /* First Data Block */
181         __le32  s_log_block_size;       /* Block size */
182         __le32  s_obso_log_frag_size;   /* Obsoleted fragment size */
183 /*20*/  __le32  s_blocks_per_group;     /* # Blocks per group */
184         __le32  s_obso_frags_per_group; /* Obsoleted fragments per group */
185         __le32  s_inodes_per_group;     /* # Inodes per group */
186         __le32  s_mtime;                /* Mount time */
187 /*30*/  __le32  s_wtime;                /* Write time */
188         __le16  s_mnt_count;            /* Mount count */
189         __le16  s_max_mnt_count;        /* Maximal mount count */
190         __le16  s_magic;                /* Magic signature */
191         __le16  s_state;                /* File system state */
192         __le16  s_errors;               /* Behaviour when detecting errors */
193         __le16  s_minor_rev_level;      /* minor revision level */
194 /*40*/  __le32  s_lastcheck;            /* time of last check */
195         __le32  s_checkinterval;        /* max. time between checks */
196         __le32  s_creator_os;           /* OS */
197         __le32  s_rev_level;            /* Revision level */
198 /*50*/  __le16  s_def_resuid;           /* Default uid for reserved blocks */
199         __le16  s_def_resgid;           /* Default gid for reserved blocks */
200         /*
201          * These fields are for EXT4_DYNAMIC_REV superblocks only.
202          *
203          * Note: the difference between the compatible feature set and
204          * the incompatible feature set is that if there is a bit set
205          * in the incompatible feature set that the kernel doesn't
206          * know about, it should refuse to mount the filesystem.
207          *
208          * e2fsck's requirements are more strict; if it doesn't know
209          * about a feature in either the compatible or incompatible
210          * feature set, it must abort and not try to meddle with
211          * things it doesn't understand...
212          */
213         __le32  s_first_ino;            /* First non-reserved inode */
214         __le16  s_inode_size;           /* size of inode structure */
215         __le16  s_block_group_nr;       /* block group # of this superblock */
216         __le32  s_feature_compat;       /* compatible feature set */
217 /*60*/  __le32  s_feature_incompat;     /* incompatible feature set */
218         __le32  s_feature_ro_compat;    /* readonly-compatible feature set */
219 /*68*/  __u8    s_uuid[16];             /* 128-bit uuid for volume */
220 /*78*/  char    s_volume_name[16];      /* volume name */
221 /*88*/  char    s_last_mounted[64];     /* directory where last mounted */
222 /*C8*/  __le32  s_algorithm_usage_bitmap; /* For compression */
223         /*
224          * Performance hints.  Directory preallocation should only
225          * happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on.
226          */
227         __u8    s_prealloc_blocks;      /* Nr of blocks to try to preallocate*/
228         __u8    s_prealloc_dir_blocks;  /* Nr to preallocate for dirs */
229         __le16  s_reserved_gdt_blocks;  /* Per group desc for online growth */
230         /*
231          * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set.
232          */
233 /*D0*/  __u8    s_journal_uuid[16];     /* uuid of journal superblock */
234 /*E0*/  __le32  s_journal_inum;         /* inode number of journal file */
235         __le32  s_journal_dev;          /* device number of journal file */
236         __le32  s_last_orphan;          /* start of list of inodes to delete */
237         __le32  s_hash_seed[4];         /* HTREE hash seed */
238         __u8    s_def_hash_version;     /* Default hash version to use */
239         __u8    s_reserved_char_pad;
240         __le16  s_desc_size;            /* size of group descriptor */
241 /*100*/ __le32  s_default_mount_opts;
242         __le32  s_first_meta_bg;        /* First metablock block group */
243         __le32  s_mkfs_time;            /* When the filesystem was created */
244         __le32  s_jnl_blocks[17];       /* Backup of the journal inode */
245         /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
246 /*150*/ __le32  s_blocks_count_hi;      /* Blocks count */
247         __le32  s_r_blocks_count_hi;    /* Reserved blocks count */
248         __le32  s_free_blocks_count_hi; /* Free blocks count */
249         __le16  s_min_extra_isize;      /* All inodes have at least # bytes */
250         __le16  s_want_extra_isize;     /* New inodes should reserve # bytes */
251         __le32  s_flags;                /* Miscellaneous flags */
252         __le16  s_raid_stride;          /* RAID stride */
253         __le16  s_mmp_interval;         /* # seconds to wait in MMP checking */
254         __le64  s_mmp_block;            /* Block for multi-mount protection */
255         __le32  s_raid_stripe_width;    /* blocks on all data disks (N*stride)*/
256         __u8    s_log_groups_per_flex;  /* FLEX_BG group size */
257         __u8    s_reserved_char_pad2;
258         __le16  s_reserved_pad;
259         __u32   s_reserved[162];        /* Padding to the end of the block */
260 };
261
262 char    lost_found_dir[PATH_MAX + 1];
263 int     block_size;
264 int     extents_before_defrag;
265 int     extents_after_defrag;
266 int     mode_flag;
267 unsigned int    current_uid;
268 unsigned int    defraged_file_count;
269 unsigned int    frag_files_before_defrag;
270 unsigned int    frag_files_after_defrag;
271 unsigned int    regular_count;
272 unsigned int    succeed_cnt;
273 unsigned int    total_count;
274 __u8 log_groups_per_flex;
275 __le32 blocks_per_group;
276 __le32 feature_incompat;
277 ext4_fsblk_t    files_block_count;
278 struct frag_statistic_ino       frag_rank[SHOW_FRAG_FILES];
279
280
281 /* Local definitions of some syscalls glibc may not yet have */
282
283 #ifndef HAVE_POSIX_FADVISE
284 #warning Using locally defined posix_fadvise interface.
285
286 #ifndef __NR_fadvise64_64
287 #error Your kernel headers dont define __NR_fadvise64_64
288 #endif
289
290 /*
291  * fadvise() -          Give advice about file access.
292  *
293  * @fd:                 defrag target file's descriptor.
294  * @offset:             file offset.
295  * @len:                area length.
296  * @advise:             process flag.
297  */
298 static int posix_fadvise(int fd, loff_t offset, size_t len, int advise)
299 {
300         return syscall(__NR_fadvise64_64, fd, offset, len, advise);
301 }
302 #endif /* ! HAVE_FADVISE64_64 */
303
304 #ifndef HAVE_SYNC_FILE_RANGE
305 #warning Using locally defined sync_file_range interface.
306
307 #ifndef __NR_sync_file_range
308 #ifndef __NR_sync_file_range2 /* ppc */
309 #error Your kernel headers dont define __NR_sync_file_range
310 #endif
311 #endif
312
313 /*
314  * sync_file_range() -  Sync file region.
315  *
316  * @fd:                 defrag target file's descriptor.
317  * @offset:             file offset.
318  * @length:             area length.
319  * @flag:               process flag.
320  */
321 int sync_file_range(int fd, loff_t offset, loff_t length, unsigned int flag)
322 {
323 #ifdef __NR_sync_file_range
324         return syscall(__NR_sync_file_range, fd, offset, length, flag);
325 #else
326         return syscall(__NR_sync_file_range2, fd, flag, offset, length);
327 #endif
328 }
329 #endif /* ! HAVE_SYNC_FILE_RANGE */
330
331 #ifndef HAVE_FALLOCATE
332 #warning Using locally defined fallocate syscall interface.
333
334 #ifndef __NR_fallocate
335 #error Your kernel headers dont define __NR_fallocate
336 #endif
337
338 /*
339  * fallocate() -        Manipulate file space.
340  *
341  * @fd:                 defrag target file's descriptor.
342  * @mode:               process flag.
343  * @offset:             file offset.
344  * @len:                file size.
345  */
346 static int fallocate(int fd, int mode, loff_t offset, loff_t len)
347 {
348         return syscall(__NR_fallocate, fd, mode, offset, len);
349 }
350 #endif /* ! HAVE_FALLOCATE */
351
352 /*
353  * get_mount_point() -  Get device's mount point.
354  *
355  * @devname:            the device's name.
356  * @mount_point:        the mount point.
357  * @dir_path_len:       the length of directory.
358  */
359 static int get_mount_point(const char *devname, char *mount_point,
360                                                         int dir_path_len)
361 {
362         /* Refer to /etc/mtab */
363         const char      *mtab = MOUNTED;
364         FILE    *fp = NULL;
365         struct mntent   *mnt = NULL;
366
367         fp = setmntent(mtab, "r");
368         if (fp == NULL) {
369                 perror("Couldn't access /etc/mtab");
370                 return -1;
371         }
372
373         while ((mnt = getmntent(fp)) != NULL) {
374                 if (strcmp(devname, mnt->mnt_fsname) != 0)
375                         continue;
376
377                 endmntent(fp);
378                 if (strcmp(mnt->mnt_type, FS_EXT4) == 0) {
379                         strncpy(mount_point, mnt->mnt_dir,
380                                 dir_path_len);
381                         return 0;
382                 }
383                 PRINT_ERR_MSG(NGMSG_EXT4);
384                 return -1;
385         }
386         endmntent(fp);
387         PRINT_ERR_MSG("Filesystem is not mounted");
388         return -1;
389 }
390
391 /*
392  * is_ext4() -          Whether on an ext4 filesystem.
393  *
394  * @file:               the file's name.
395  */
396 static int is_ext4(const char *file)
397 {
398         int     maxlen = 0;
399         int     len, ret;
400         FILE    *fp = NULL;
401         char    *mnt_type = NULL;
402         /* Refer to /etc/mtab */
403         const char      *mtab = MOUNTED;
404         char    file_path[PATH_MAX + 1];
405         struct mntent   *mnt = NULL;
406         struct statfs64 fsbuf;
407
408         /* Get full path */
409         if (realpath(file, file_path) == NULL) {
410                 perror("Couldn't get full path");
411                 PRINT_FILE_NAME(file);
412                 return -1;
413         }
414
415         if (statfs64(file_path, &fsbuf) < 0) {
416                 perror("Failed to get filesystem information");
417                 PRINT_FILE_NAME(file);
418                 return -1;
419         }
420
421         if (fsbuf.f_type != EXT4_SUPER_MAGIC) {
422                 PRINT_ERR_MSG(NGMSG_EXT4);
423                 return -1;
424         }
425
426         fp = setmntent(mtab, "r");
427         if (fp == NULL) {
428                 perror("Couldn't access /etc/mtab");
429                 return -1;
430         }
431
432         while ((mnt = getmntent(fp)) != NULL) {
433                 if (mnt->mnt_fsname[0] != '/')
434                         continue;
435                 len = strlen(mnt->mnt_dir);
436                 ret = memcmp(file_path, mnt->mnt_dir, len);
437                 if (ret != 0)
438                         continue;
439
440                 if (maxlen >= len)
441                         continue;
442
443                 maxlen = len;
444
445                 mnt_type = realloc(mnt_type, strlen(mnt->mnt_type) + 1);
446                 if (mnt_type == NULL) {
447                         endmntent(fp);
448                         return -1;
449                 }
450                 memset(mnt_type, 0, strlen(mnt->mnt_type) + 1);
451                 strncpy(mnt_type, mnt->mnt_type, strlen(mnt->mnt_type));
452                 strncpy(lost_found_dir, mnt->mnt_dir, PATH_MAX);
453         }
454
455         endmntent(fp);
456         if (strcmp(mnt_type, FS_EXT4) == 0) {
457                 FREE(mnt_type);
458                 return 0;
459         } else {
460                 FREE(mnt_type);
461                 PRINT_ERR_MSG(NGMSG_EXT4);
462                 return -1;
463         }
464 }
465
466 /*
467  * calc_entry_counts() -        Calculate file counts.
468  *
469  * @file:               file name.
470  * @buf:                file info.
471  * @flag:               file type.
472  * @ftwbuf:             the pointer of a struct FTW.
473  */
474 static int calc_entry_counts(const char *file EXT2FS_ATTR((unused)),
475                 const struct stat64 *buf, int flag EXT2FS_ATTR((unused)),
476                 struct FTW *ftwbuf EXT2FS_ATTR((unused)))
477 {
478         if (S_ISREG(buf->st_mode))
479                 regular_count++;
480
481         total_count++;
482
483         return 0;
484 }
485
486 /*
487  * page_in_core() -     Get information on whether pages are in core.
488  *
489  * @fd:                 defrag target file's descriptor.
490  * @defrag_data:        data used for defrag.
491  * @vec:                page state array.
492  * @page_num:           page number.
493  */
494 static int page_in_core(int fd, struct move_extent defrag_data,
495                         unsigned char **vec, unsigned int *page_num)
496 {
497         long    pagesize = sysconf(_SC_PAGESIZE);
498         void    *page = NULL;
499         loff_t  offset, end_offset, length;
500
501         if (vec == NULL || *vec != NULL)
502                 return -1;
503
504         /* In mmap, offset should be a multiple of the page size */
505         offset = (loff_t)defrag_data.orig_start * block_size;
506         length = (loff_t)defrag_data.len * block_size;
507         end_offset = offset + length;
508         /* Round the offset down to the nearest multiple of pagesize */
509         offset = (offset / pagesize) * pagesize;
510         length = end_offset - offset;
511
512         page = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, offset);
513         if (page == MAP_FAILED)
514                 return -1;
515
516         *page_num = 0;
517         *page_num = (length + pagesize - 1) / pagesize;
518         *vec = (unsigned char *)calloc(*page_num, 1);
519         if (*vec == NULL)
520                 return -1;
521
522         /* Get information on whether pages are in core */
523         if (mincore(page, (size_t)length, *vec) == -1 ||
524                 munmap(page, length) == -1) {
525                 FREE(*vec);
526                 return -1;
527         }
528
529         return 0;
530 }
531
532 /*
533  * defrag_fadvise() -   Predeclare an access pattern for file data.
534  *
535  * @fd:                 defrag target file's descriptor.
536  * @defrag_data:        data used for defrag.
537  * @vec:                page state array.
538  * @page_num:           page number.
539  */
540 static int defrag_fadvise(int fd, struct move_extent defrag_data,
541                    unsigned char *vec, unsigned int page_num)
542 {
543         int     flag = 1;
544         long    pagesize = sysconf(_SC_PAGESIZE);
545         int     fadvise_flag = POSIX_FADV_DONTNEED;
546         int     sync_flag = SYNC_FILE_RANGE_WAIT_BEFORE |
547                             SYNC_FILE_RANGE_WRITE |
548                             SYNC_FILE_RANGE_WAIT_AFTER;
549         unsigned int    i;
550         loff_t  offset;
551
552         offset = (loff_t)defrag_data.orig_start * block_size;
553         offset = (offset / pagesize) * pagesize;
554
555         /* Sync file for fadvise process */
556         if (sync_file_range(fd, offset,
557                 (loff_t)pagesize * page_num, sync_flag) < 0)
558                 return -1;
559
560         /* Try to release buffer cache which this process used,
561          * then other process can use the released buffer
562          */
563         for (i = 0; i < page_num; i++) {
564                 if ((vec[i] & 0x1) == 0) {
565                         offset += pagesize;
566                         continue;
567                 }
568                 if (posix_fadvise(fd, offset, pagesize, fadvise_flag) < 0) {
569                         if ((mode_flag & DETAIL) && flag) {
570                                 perror("\tFailed to fadvise");
571                                 flag = 0;
572                         }
573                 }
574                 offset += pagesize;
575         }
576
577         return 0;
578 }
579
580 /*
581  * check_free_size() -  Check if there's enough disk space.
582  *
583  * @fd:                 defrag target file's descriptor.
584  * @file:               file name.
585  * @buf:                the pointer of the struct stat64.
586  */
587 static int check_free_size(int fd, const char *file, const struct stat64 *buf)
588 {
589         ext4_fsblk_t    blk_count;
590         ext4_fsblk_t    free_blk_count;
591         struct statfs64 fsbuf;
592
593         if (fstatfs64(fd, &fsbuf) < 0) {
594                 if (mode_flag & DETAIL) {
595                         PRINT_FILE_NAME(file);
596                         PRINT_ERR_MSG_WITH_ERRNO(
597                                 "Failed to get filesystem information");
598                 }
599                 return -1;
600         }
601
602         /* Target file size measured by filesystem IO blocksize */
603         blk_count = SECTOR_TO_BLOCK(buf->st_blocks, fsbuf.f_bsize);
604
605         /* Compute free space for root and normal user separately */
606         if (current_uid == ROOT_UID)
607                 free_blk_count = fsbuf.f_bfree;
608         else
609                 free_blk_count = fsbuf.f_bavail;
610
611         if (free_blk_count >= blk_count)
612                 return 0;
613
614         return -ENOSPC;
615 }
616
617 /*
618  * file_frag_count() -  Get file fragment count.
619  *
620  * @fd:                 defrag target file's descriptor.
621  */
622 static int file_frag_count(int fd)
623 {
624         int     ret;
625         struct fiemap   fiemap_buf;
626
627         /* When fm_extent_count is 0,
628          * ioctl just get file fragment count.
629          */
630         memset(&fiemap_buf, 0, sizeof(struct fiemap));
631         fiemap_buf.fm_start = 0;
632         fiemap_buf.fm_length = FIEMAP_MAX_OFFSET;
633         fiemap_buf.fm_flags |= FIEMAP_FLAG_SYNC;
634
635         ret = ioctl(fd, FS_IOC_FIEMAP, &fiemap_buf);
636         if (ret < 0)
637                 return ret;
638
639         return fiemap_buf.fm_mapped_extents;
640 }
641
642 /*
643  * file_check() -       Check file's attributes.
644  *
645  * @fd:                 defrag target file's descriptor.
646  * @buf:                a pointer of the struct stat64.
647  * @file:               the file's name.
648  * @extents:            the file's extents.
649  */
650 static int file_check(int fd, const struct stat64 *buf, const char *file,
651                 int extents)
652 {
653         int     ret;
654         struct flock    lock;
655
656         /* Write-lock check is more reliable */
657         lock.l_type = F_WRLCK;
658         lock.l_start = 0;
659         lock.l_whence = SEEK_SET;
660         lock.l_len = 0;
661
662         /* Free space */
663         ret = check_free_size(fd, file, buf);
664         if (ret < 0) {
665                 if ((mode_flag & DETAIL) && ret == -ENOSPC) {
666                         printf("\033[79;0H\033[K[%u/%u] \"%s\"\t\t"
667                                 "  extents: %d -> %d\n", defraged_file_count,
668                                 total_count, file, extents, extents);
669                         IN_FTW_PRINT_ERR_MSG(
670                         "Defrag size is larger than filesystem's free space");
671                 }
672                 return -1;
673         }
674
675         /* Access authority */
676         if (current_uid != ROOT_UID &&
677                 buf->st_uid != current_uid) {
678                 if (mode_flag & DETAIL) {
679                         printf("\033[79;0H\033[K[%u/%u] \"%s\"\t\t"
680                                 "  extents: %d -> %d\n", defraged_file_count,
681                                 total_count, file, extents, extents);
682                         IN_FTW_PRINT_ERR_MSG(
683                                 "File is not current user's file"
684                                 " or current user is not root");
685                 }
686                 return -1;
687         }
688
689         /* Lock status */
690         if (fcntl(fd, F_GETLK, &lock) < 0) {
691                 if (mode_flag & DETAIL) {
692                         PRINT_FILE_NAME(file);
693                         PRINT_ERR_MSG_WITH_ERRNO(
694                                 "Failed to get lock information");
695                 }
696                 return -1;
697         } else if (lock.l_type != F_UNLCK) {
698                 if (mode_flag & DETAIL) {
699                         PRINT_FILE_NAME(file);
700                         IN_FTW_PRINT_ERR_MSG("File has been locked");
701                 }
702                 return -1;
703         }
704
705         return 0;
706 }
707
708 /*
709  * insert_extent_by_logical() - Sequentially insert extent by logical.
710  *
711  * @ext_list_head:      the head of logical extent list.
712  * @ext:                the extent element which will be inserted.
713  */
714 static int insert_extent_by_logical(struct fiemap_extent_list **ext_list_head,
715                         struct fiemap_extent_list *ext)
716 {
717         struct fiemap_extent_list       *ext_list_tmp = *ext_list_head;
718
719         if (ext == NULL)
720                 goto out;
721
722         /* First element */
723         if (*ext_list_head == NULL) {
724                 (*ext_list_head) = ext;
725                 (*ext_list_head)->prev = *ext_list_head;
726                 (*ext_list_head)->next = *ext_list_head;
727                 return 0;
728         }
729
730         if (ext->data.logical <= ext_list_tmp->data.logical) {
731                 /* Insert before head */
732                 if (ext_list_tmp->data.logical <
733                         ext->data.logical + ext->data.len)
734                         /* Overlap */
735                         goto out;
736                 /* Adjust head */
737                 *ext_list_head = ext;
738         } else {
739                 /* Insert into the middle or last of the list */
740                 do {
741                         if (ext->data.logical < ext_list_tmp->data.logical)
742                                 break;
743                         ext_list_tmp = ext_list_tmp->next;
744                 } while (ext_list_tmp != (*ext_list_head));
745                 if (ext->data.logical <
746                     ext_list_tmp->prev->data.logical +
747                         ext_list_tmp->prev->data.len)
748                         /* Overlap */
749                         goto out;
750
751                 if (ext_list_tmp != *ext_list_head &&
752                     ext_list_tmp->data.logical <
753                     ext->data.logical + ext->data.len)
754                         /* Overlap */
755                         goto out;
756         }
757         ext_list_tmp = ext_list_tmp->prev;
758         /* Insert "ext" after "ext_list_tmp" */
759         insert(ext_list_tmp, ext);
760         return 0;
761 out:
762         errno = EINVAL;
763         return -1;
764 }
765
766 /*
767  * insert_extent_by_physical() -        Sequentially insert extent by physical.
768  *
769  * @ext_list_head:      the head of physical extent list.
770  * @ext:                the extent element which will be inserted.
771  */
772 static int insert_extent_by_physical(struct fiemap_extent_list **ext_list_head,
773                         struct fiemap_extent_list *ext)
774 {
775         struct fiemap_extent_list       *ext_list_tmp = *ext_list_head;
776
777         if (ext == NULL)
778                 goto out;
779
780         /* First element */
781         if (*ext_list_head == NULL) {
782                 (*ext_list_head) = ext;
783                 (*ext_list_head)->prev = *ext_list_head;
784                 (*ext_list_head)->next = *ext_list_head;
785                 return 0;
786         }
787
788         if (ext->data.physical <= ext_list_tmp->data.physical) {
789                 /* Insert before head */
790                 if (ext_list_tmp->data.physical <
791                                         ext->data.physical + ext->data.len)
792                         /* Overlap */
793                         goto out;
794                 /* Adjust head */
795                 *ext_list_head = ext;
796         } else {
797                 /* Insert into the middle or last of the list */
798                 do {
799                         if (ext->data.physical < ext_list_tmp->data.physical)
800                                 break;
801                         ext_list_tmp = ext_list_tmp->next;
802                 } while (ext_list_tmp != (*ext_list_head));
803                 if (ext->data.physical <
804                     ext_list_tmp->prev->data.physical +
805                                 ext_list_tmp->prev->data.len)
806                         /* Overlap */
807                         goto out;
808
809                 if (ext_list_tmp != *ext_list_head &&
810                     ext_list_tmp->data.physical <
811                                 ext->data.physical + ext->data.len)
812                         /* Overlap */
813                         goto out;
814         }
815         ext_list_tmp = ext_list_tmp->prev;
816         /* Insert "ext" after "ext_list_tmp" */
817         insert(ext_list_tmp, ext);
818         return 0;
819 out:
820         errno = EINVAL;
821         return -1;
822 }
823
824 /*
825  * insert_exts_group() -        Insert a exts_group.
826  *
827  * @ext_group_head:             the head of a exts_group list.
828  * @exts_group:                 the exts_group element which will be inserted.
829  */
830 static int insert_exts_group(struct fiemap_extent_group **ext_group_head,
831                                 struct fiemap_extent_group *exts_group)
832 {
833         struct fiemap_extent_group      *ext_group_tmp = NULL;
834
835         if (exts_group == NULL) {
836                 errno = EINVAL;
837                 return -1;
838         }
839
840         /* Initialize list */
841         if (*ext_group_head == NULL) {
842                 (*ext_group_head) = exts_group;
843                 (*ext_group_head)->prev = *ext_group_head;
844                 (*ext_group_head)->next = *ext_group_head;
845                 return 0;
846         }
847
848         ext_group_tmp = (*ext_group_head)->prev;
849         insert(ext_group_tmp, exts_group);
850
851         return 0;
852 }
853
854 /*
855  * join_extents() -             Find continuous region(exts_group).
856  *
857  * @ext_list_head:              the head of the extent list.
858  * @ext_group_head:             the head of the target exts_group list.
859  */
860 static int join_extents(struct fiemap_extent_list *ext_list_head,
861                 struct fiemap_extent_group **ext_group_head)
862 {
863         __u64   len = ext_list_head->data.len;
864         struct fiemap_extent_list *ext_list_start = ext_list_head;
865         struct fiemap_extent_list *ext_list_tmp = ext_list_head->next;
866
867         do {
868                 struct fiemap_extent_group      *ext_group_tmp = NULL;
869
870                 /* This extent and previous extent are not continuous,
871                  * so, all previous extents are treated as an extent group.
872                  */
873                 if ((ext_list_tmp->prev->data.logical +
874                         ext_list_tmp->prev->data.len)
875                                 != ext_list_tmp->data.logical) {
876                         ext_group_tmp =
877                                 malloc(sizeof(struct fiemap_extent_group));
878                         if (ext_group_tmp == NULL)
879                                 return -1;
880
881                         memset(ext_group_tmp, 0,
882                                 sizeof(struct fiemap_extent_group));
883                         ext_group_tmp->len = len;
884                         ext_group_tmp->start = ext_list_start;
885                         ext_group_tmp->end = ext_list_tmp->prev;
886
887                         if (insert_exts_group(ext_group_head,
888                                 ext_group_tmp) < 0) {
889                                 FREE(ext_group_tmp);
890                                 return -1;
891                         }
892                         ext_list_start = ext_list_tmp;
893                         len = ext_list_tmp->data.len;
894                         ext_list_tmp = ext_list_tmp->next;
895                         continue;
896                 }
897
898                 /* This extent and previous extent are continuous,
899                  * so, they belong to the same extent group, and we check
900                  * if the next extent belongs to the same extent group.
901                  */
902                 len += ext_list_tmp->data.len;
903                 ext_list_tmp = ext_list_tmp->next;
904         } while (ext_list_tmp != ext_list_head->next);
905
906         return 0;
907 }
908
909 /*
910  * get_file_extents() - Get file's extent list.
911  *
912  * @fd:                 defrag target file's descriptor.
913  * @ext_list_head:      the head of the extent list.
914  */
915 static int get_file_extents(int fd, struct fiemap_extent_list **ext_list_head)
916 {
917         __u32   i;
918         int     ret;
919         int     ext_buf_size, fie_buf_size;
920         __u64   pos = 0;
921         struct fiemap   *fiemap_buf = NULL;
922         struct fiemap_extent    *ext_buf = NULL;
923         struct fiemap_extent_list       *ext_list = NULL;
924
925         /* Convert units, in bytes.
926          * Be careful : now, physical block number in extent is 48bit,
927          * and the maximum blocksize for ext4 is 4K(12bit),
928          * so there is no overflow, but in future it may be changed.
929          */
930
931         /* Alloc space for fiemap */
932         ext_buf_size = EXTENT_MAX_COUNT * sizeof(struct fiemap_extent);
933         fie_buf_size = sizeof(struct fiemap) + ext_buf_size;
934
935         fiemap_buf = malloc(fie_buf_size);
936         if (fiemap_buf == NULL)
937                 return -1;
938
939         ext_buf = fiemap_buf->fm_extents;
940         memset(fiemap_buf, 0, fie_buf_size);
941         fiemap_buf->fm_length = FIEMAP_MAX_OFFSET;
942         fiemap_buf->fm_flags |= FIEMAP_FLAG_SYNC;
943         fiemap_buf->fm_extent_count = EXTENT_MAX_COUNT;
944
945         do {
946                 fiemap_buf->fm_start = pos;
947                 memset(ext_buf, 0, ext_buf_size);
948                 ret = ioctl(fd, FS_IOC_FIEMAP, fiemap_buf);
949                 if (ret < 0)
950                         goto out;
951                 for (i = 0; i < fiemap_buf->fm_mapped_extents; i++) {
952                         ext_list = NULL;
953                         ext_list = malloc(sizeof(struct fiemap_extent_list));
954                         if (ext_list == NULL)
955                                 goto out;
956
957                         ext_list->data.physical = ext_buf[i].fe_physical
958                                                 / block_size;
959                         ext_list->data.logical = ext_buf[i].fe_logical
960                                                 / block_size;
961                         ext_list->data.len = ext_buf[i].fe_length
962                                                 / block_size;
963
964                         ret = insert_extent_by_physical(
965                                         ext_list_head, ext_list);
966                         if (ret < 0) {
967                                 FREE(ext_list);
968                                 goto out;
969                         }
970                 }
971                 /* Record file's logical offset this time */
972                 pos = ext_buf[EXTENT_MAX_COUNT-1].fe_logical +
973                         ext_buf[EXTENT_MAX_COUNT-1].fe_length;
974                 /*
975                  * If fm_extents array has been filled and
976                  * there are extents left, continue to cycle.
977                  */
978         } while (fiemap_buf->fm_mapped_extents
979                                         == EXTENT_MAX_COUNT &&
980                 !(ext_buf[EXTENT_MAX_COUNT-1].fe_flags
981                                         & FIEMAP_EXTENT_LAST));
982
983         FREE(fiemap_buf);
984         return 0;
985 out:
986         FREE(fiemap_buf);
987         return -1;
988 }
989
990 /*
991  * get_logical_count() -        Get the file logical extents count.
992  *
993  * @logical_list_head:  the head of the logical extent list.
994  */
995 static int get_logical_count(struct fiemap_extent_list *logical_list_head)
996 {
997         int ret = 0;
998         struct fiemap_extent_list *ext_list_tmp  = logical_list_head;
999
1000         do {
1001                 ret++;
1002                 ext_list_tmp = ext_list_tmp->next;
1003         } while (ext_list_tmp != logical_list_head);
1004
1005         return ret;
1006 }
1007
1008 /*
1009  * get_physical_count() -       Get the file physical extents count.
1010  *
1011  * @physical_list_head: the head of the physical extent list.
1012  */
1013 static int get_physical_count(struct fiemap_extent_list *physical_list_head)
1014 {
1015         int ret = 0;
1016         struct fiemap_extent_list *ext_list_tmp = physical_list_head;
1017
1018         do {
1019                 if ((ext_list_tmp->data.physical + ext_list_tmp->data.len)
1020                                 != ext_list_tmp->next->data.physical) {
1021                         /* This extent and next extent are not continuous. */
1022                         ret++;
1023                 }
1024
1025                 ext_list_tmp = ext_list_tmp->next;
1026         } while (ext_list_tmp != physical_list_head);
1027
1028         return ret;
1029 }
1030
1031 /*
1032  * change_physical_to_logical() -       Change list from physical to logical.
1033  *
1034  * @physical_list_head: the head of physical extent list.
1035  * @logical_list_head:  the head of logical extent list.
1036  */
1037 static int change_physical_to_logical(
1038                         struct fiemap_extent_list **physical_list_head,
1039                         struct fiemap_extent_list **logical_list_head)
1040 {
1041         int ret;
1042         struct fiemap_extent_list *ext_list_tmp = *physical_list_head;
1043         struct fiemap_extent_list *ext_list_next = ext_list_tmp->next;
1044
1045         while (1) {
1046                 if (ext_list_tmp == ext_list_next) {
1047                         ret = insert_extent_by_logical(
1048                                 logical_list_head, ext_list_tmp);
1049                         if (ret < 0)
1050                                 return -1;
1051
1052                         *physical_list_head = NULL;
1053                         break;
1054                 }
1055
1056                 ext_list_tmp->prev->next = ext_list_tmp->next;
1057                 ext_list_tmp->next->prev = ext_list_tmp->prev;
1058                 *physical_list_head = ext_list_next;
1059
1060                 ret = insert_extent_by_logical(
1061                         logical_list_head, ext_list_tmp);
1062                 if (ret < 0) {
1063                         FREE(ext_list_tmp);
1064                         return -1;
1065                 }
1066                 ext_list_tmp = ext_list_next;
1067                 ext_list_next = ext_list_next->next;
1068         }
1069
1070         return 0;
1071 }
1072
1073 /*
1074  * free_ext() -         Free the extent list.
1075  *
1076  * @ext_list_head:      the extent list head of which will be free.
1077  */
1078 static void free_ext(struct fiemap_extent_list *ext_list_head)
1079 {
1080         struct fiemap_extent_list       *ext_list_tmp = NULL;
1081
1082         if (ext_list_head == NULL)
1083                 return;
1084
1085         while (ext_list_head->next != ext_list_head) {
1086                 ext_list_tmp = ext_list_head;
1087                 ext_list_head->prev->next = ext_list_head->next;
1088                 ext_list_head->next->prev = ext_list_head->prev;
1089                 ext_list_head = ext_list_head->next;
1090                 free(ext_list_tmp);
1091         }
1092         free(ext_list_head);
1093 }
1094
1095 /*
1096  * free_exts_group() -          Free the exts_group.
1097  *
1098  * @*ext_group_head:    the exts_group list head which will be free.
1099  */
1100 static void free_exts_group(struct fiemap_extent_group *ext_group_head)
1101 {
1102         struct fiemap_extent_group      *ext_group_tmp = NULL;
1103
1104         if (ext_group_head == NULL)
1105                 return;
1106
1107         while (ext_group_head->next != ext_group_head) {
1108                 ext_group_tmp = ext_group_head;
1109                 ext_group_head->prev->next = ext_group_head->next;
1110                 ext_group_head->next->prev = ext_group_head->prev;
1111                 ext_group_head = ext_group_head->next;
1112                 free(ext_group_tmp);
1113         }
1114         free(ext_group_head);
1115 }
1116
1117 /*
1118  * get_superblock_info() -      Get superblock info by the file name.
1119  *
1120  * @file:               the file's name.
1121  * @sb:         the pointer of the struct ext4_super_block.
1122  */
1123 static int get_superblock_info(const char *file, struct ext4_super_block *sb)
1124 {
1125         /* Refer to /etc/mtab */
1126         const char      *mtab = MOUNTED;
1127         FILE    *fp = NULL;
1128
1129         int     fd = -1;
1130         int     ret;
1131         size_t maxlen = 0;
1132         size_t len;
1133         char    dev_name[PATH_MAX + 1];
1134         struct mntent   *mnt = NULL;
1135
1136         fp = setmntent(mtab, "r");
1137         if (fp == NULL)
1138                 return -1;
1139
1140         while ((mnt = getmntent(fp)) != NULL) {
1141                 len = strlen(mnt->mnt_dir);
1142                 ret = memcmp(file, mnt->mnt_dir, len);
1143                 if (ret != 0)
1144                         continue;
1145
1146                 if (len < maxlen)
1147                         continue;
1148
1149                 maxlen = len;
1150
1151                 memset(dev_name, 0, PATH_MAX + 1);
1152                 strncpy(dev_name, mnt->mnt_fsname,
1153                                 strnlen(mnt->mnt_fsname, PATH_MAX));
1154         }
1155
1156         fd = open64(dev_name, O_RDONLY);
1157         if (fd < 0) {
1158                 ret = -1;
1159                 goto out;
1160         }
1161
1162         /* Set offset to read superblock */
1163         ret = lseek64(fd, SUPERBLOCK_OFFSET, SEEK_SET);
1164         if (ret < 0)
1165                 goto out;
1166
1167         ret = read(fd, sb, sizeof(struct ext4_super_block));
1168         if (ret < 0)
1169                 goto out;
1170
1171 out:
1172         if (fd != -1)
1173                 close(fd);
1174         endmntent(fp);
1175         return ret;
1176 }
1177
1178 /*
1179  * get_best_count() -   Get the file best extents count.
1180  *
1181  * @block_count:                the file's physical block count.
1182  */
1183 static int get_best_count(ext4_fsblk_t block_count)
1184 {
1185         int ret;
1186         unsigned int flex_bg_num;
1187
1188         /* Calcuate best extents count */
1189         if (feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) {
1190                 flex_bg_num = 1 << log_groups_per_flex;
1191                 ret = ((block_count - 1) /
1192                         ((ext4_fsblk_t)blocks_per_group *
1193                                 flex_bg_num)) + 1;
1194         } else
1195                 ret = ((block_count - 1) / blocks_per_group) + 1;
1196
1197         return ret;
1198 }
1199
1200
1201 /*
1202  * file_statistic() -   Get statistic info of the file's fragments.
1203  *
1204  * @file:               the file's name.
1205  * @buf:                the pointer of the struct stat64.
1206  * @flag:               file type.
1207  * @ftwbuf:             the pointer of a struct FTW.
1208  */
1209 static int file_statistic(const char *file, const struct stat64 *buf,
1210                         int flag EXT2FS_ATTR((unused)),
1211                         struct FTW *ftwbuf EXT2FS_ATTR((unused)))
1212 {
1213         int     fd;
1214         int     ret;
1215         int     now_ext_count, best_ext_count = 0, physical_ext_count;
1216         int     i, j;
1217         float   ratio = 0.0;
1218         ext4_fsblk_t    blk_count = 0;
1219         char    msg_buffer[PATH_MAX + 24];
1220         struct fiemap_extent_list *physical_list_head = NULL;
1221         struct fiemap_extent_list *logical_list_head = NULL;
1222
1223         defraged_file_count++;
1224
1225         if (mode_flag & DETAIL) {
1226                 if (total_count == 1 && regular_count == 1)
1227                         printf("<File>\n");
1228                 else {
1229                         printf("[%u/%u]", defraged_file_count, total_count);
1230                         fflush(stdout);
1231                 }
1232         }
1233         if (lost_found_dir[0] != '\0' &&
1234             !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) {
1235                 if (mode_flag & DETAIL) {
1236                         PRINT_FILE_NAME(file);
1237                         STATISTIC_ERR_MSG(NGMSG_LOST_FOUND);
1238                 }
1239                         return 0;
1240         }
1241
1242         if (!S_ISREG(buf->st_mode)) {
1243                 if (mode_flag & DETAIL) {
1244                         PRINT_FILE_NAME(file);
1245                         STATISTIC_ERR_MSG(NGMSG_FILE_UNREG);
1246                 }
1247                 return 0;
1248         }
1249
1250         /* Access authority */
1251         if (current_uid != ROOT_UID &&
1252                 buf->st_uid != current_uid) {
1253                 if (mode_flag & DETAIL) {
1254                         PRINT_FILE_NAME(file);
1255                         STATISTIC_ERR_MSG(
1256                                 "File is not current user's file"
1257                                 " or current user is not root");
1258                 }
1259                 return 0;
1260         }
1261
1262         /* Empty file */
1263         if (buf->st_size == 0) {
1264                 if (mode_flag & DETAIL) {
1265                         PRINT_FILE_NAME(file);
1266                         STATISTIC_ERR_MSG("File size is 0");
1267                 }
1268                 return 0;
1269         }
1270
1271         /* Has no blocks */
1272         if (buf->st_blocks == 0) {
1273                 if (mode_flag & DETAIL) {
1274                         PRINT_FILE_NAME(file);
1275                         STATISTIC_ERR_MSG("File has no blocks");
1276                 }
1277                 return 0;
1278         }
1279
1280         fd = open64(file, O_RDONLY);
1281         if (fd < 0) {
1282                 if (mode_flag & DETAIL) {
1283                         PRINT_FILE_NAME(file);
1284                         STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN);
1285                 }
1286                 return 0;
1287         }
1288
1289         /* Get file's physical extents  */
1290         ret = get_file_extents(fd, &physical_list_head);
1291         if (ret < 0) {
1292                 if (mode_flag & DETAIL) {
1293                         PRINT_FILE_NAME(file);
1294                         STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1295                 }
1296                 goto out;
1297         }
1298
1299         /* Get the count of file's continuous physical region */
1300         physical_ext_count = get_physical_count(physical_list_head);
1301
1302         /* Change list from physical to logical */
1303         ret = change_physical_to_logical(&physical_list_head,
1304                                                         &logical_list_head);
1305         if (ret < 0) {
1306                 if (mode_flag & DETAIL) {
1307                         PRINT_FILE_NAME(file);
1308                         STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1309                 }
1310                 goto out;
1311         }
1312
1313         /* Count file fragments before defrag */
1314         now_ext_count = get_logical_count(logical_list_head);
1315
1316         if (current_uid == ROOT_UID) {
1317                 /* Calculate fragment ratio  */
1318                 blk_count =
1319                                 SECTOR_TO_BLOCK(buf->st_blocks, block_size);
1320
1321                 best_ext_count = get_best_count(blk_count);
1322
1323                 ratio = (float)(physical_ext_count - best_ext_count) * 100 /
1324                                                         blk_count;
1325
1326                 extents_before_defrag += now_ext_count;
1327                 extents_after_defrag += best_ext_count;
1328                 files_block_count += blk_count;
1329         }
1330
1331         if (total_count == 1 && regular_count == 1) {
1332                 /* File only */
1333                 if (mode_flag & DETAIL) {
1334                         int count = 0;
1335                         struct fiemap_extent_list *ext_list_tmp =
1336                                                 logical_list_head;
1337
1338                         /* Print extents info */
1339                         do {
1340                                 count++;
1341                                 printf("[ext %d]:\tstart %llu:\tlogical "
1342                                                 "%llu:\tlen %llu\n", count,
1343                                                 ext_list_tmp->data.physical,
1344                                                 ext_list_tmp->data.logical,
1345                                                 ext_list_tmp->data.len);
1346                                 ext_list_tmp = ext_list_tmp->next;
1347                         } while (ext_list_tmp != logical_list_head);
1348
1349                 } else {
1350                         printf("%-40s%10s/%-10s%9s\n",
1351                                         "<File>", "now", "best", "ratio");
1352                         if (current_uid == ROOT_UID) {
1353                                 if (strlen(file) > 40)
1354                                         printf("%s\n%50d/%-10d%8.2f%%\n",
1355                                                 file, now_ext_count,
1356                                                 best_ext_count, ratio);
1357                                 else
1358                                         printf("%-40s%10d/%-10d%8.2f%%\n",
1359                                                 file, now_ext_count,
1360                                                 best_ext_count, ratio);
1361                         } else {
1362                                 if (strlen(file) > 40)
1363                                         printf("%s\n%50d/%-10s%7s\n",
1364                                                         file, now_ext_count,
1365                                                         "-", "-");
1366                                 else
1367                                         printf("%-40s%10d/%-10s%7s\n",
1368                                                         file, now_ext_count,
1369                                                         "-", "-");
1370                         }
1371                 }
1372                 succeed_cnt++;
1373                 goto out;
1374         }
1375
1376         if (mode_flag & DETAIL) {
1377                 /* Print statistic info */
1378                 sprintf(msg_buffer, "[%u/%u]%s",
1379                                 defraged_file_count, total_count, file);
1380                 if (current_uid == ROOT_UID) {
1381                         if (strlen(msg_buffer) > 40)
1382                                 printf("\033[79;0H\033[K%s\n"
1383                                                 "%50d/%-10d%8.2f%%\n",
1384                                                 msg_buffer, now_ext_count,
1385                                                 best_ext_count, ratio);
1386                         else
1387                                 printf("\033[79;0H\033[K%-40s"
1388                                                 "%10d/%-10d%8.2f%%\n",
1389                                                 msg_buffer, now_ext_count,
1390                                                 best_ext_count, ratio);
1391                 } else {
1392                         if (strlen(msg_buffer) > 40)
1393                                 printf("\033[79;0H\033[K%s\n%50d/%-10s%7s\n",
1394                                                 msg_buffer, now_ext_count,
1395                                                         "-", "-");
1396                         else
1397                                 printf("\033[79;0H\033[K%-40s%10d/%-10s%7s\n",
1398                                                 msg_buffer, now_ext_count,
1399                                                         "-", "-");
1400                 }
1401         }
1402
1403         for (i = 0; i < SHOW_FRAG_FILES; i++) {
1404                 if (ratio >= frag_rank[i].ratio) {
1405                         for (j = SHOW_FRAG_FILES - 1; j > i; j--) {
1406                                 memcpy(&frag_rank[j], &frag_rank[j - 1],
1407                                         sizeof(struct frag_statistic_ino));
1408                         }
1409                         memset(&frag_rank[i], 0,
1410                                         sizeof(struct frag_statistic_ino));
1411                         strncpy(frag_rank[i].msg_buffer, file,
1412                                                 strnlen(file, PATH_MAX));
1413                         frag_rank[i].now_count = now_ext_count;
1414                         frag_rank[i].best_count = best_ext_count;
1415                         frag_rank[i].ratio = ratio;
1416                         break;
1417                 }
1418         }
1419
1420         succeed_cnt++;
1421
1422 out:
1423         close(fd);
1424         free_ext(physical_list_head);
1425         free_ext(logical_list_head);
1426         return 0;
1427 }
1428
1429 /*
1430  * print_progress -     Print defrag progress
1431  *
1432  * @file:               file name.
1433  * @start:              logical offset for defrag target file
1434  * @file_size:          defrag target filesize
1435  */
1436 static void print_progress(const char *file, loff_t start, loff_t file_size)
1437 {
1438         int percent = (start * 100) / file_size;
1439         printf("\033[79;0H\033[K[%u/%u]%s:\t%3d%%",
1440                 defraged_file_count, total_count, file, min(percent, 100));
1441         fflush(stdout);
1442
1443         return;
1444 }
1445
1446 /*
1447  * call_defrag() -      Execute the defrag program.
1448  *
1449  * @fd:                 target file descriptor.
1450  * @donor_fd:           donor file descriptor.
1451  * @file:                       target file name.
1452  * @buf:                        pointer of the struct stat64.
1453  * @ext_list_head:      head of the extent list.
1454  */
1455 static int call_defrag(int fd, int donor_fd, const char *file,
1456         const struct stat64 *buf, struct fiemap_extent_list *ext_list_head)
1457 {
1458         loff_t  start = 0;
1459         unsigned int    page_num;
1460         unsigned char   *vec = NULL;
1461         int     defraged_ret = 0;
1462         int     ret;
1463         struct move_extent      move_data;
1464         struct fiemap_extent_list       *ext_list_tmp = NULL;
1465
1466         memset(&move_data, 0, sizeof(struct move_extent));
1467         move_data.donor_fd = donor_fd;
1468
1469         /* Print defrag progress */
1470         print_progress(file, start, buf->st_size);
1471
1472         ext_list_tmp = ext_list_head;
1473         do {
1474                 move_data.orig_start = ext_list_tmp->data.logical;
1475                 /* Logical offset of orig and donor should be same */
1476                 move_data.donor_start = move_data.orig_start;
1477                 move_data.len = ext_list_tmp->data.len;
1478                 move_data.moved_len = 0;
1479
1480                 ret = page_in_core(fd, move_data, &vec, &page_num);
1481                 if (ret < 0) {
1482                         if (mode_flag & DETAIL) {
1483                                 printf("\n");
1484                                 PRINT_ERR_MSG_WITH_ERRNO(
1485                                                 "Failed to get file map");
1486                         } else {
1487                                 printf("\t[ NG ]\n");
1488                         }
1489                         return -1;
1490                 }
1491
1492                 /* EXT4_IOC_MOVE_EXT */
1493                 defraged_ret =
1494                         ioctl(fd, EXT4_IOC_MOVE_EXT, &move_data);
1495
1496                 /* Free pages */
1497                 ret = defrag_fadvise(fd, move_data, vec, page_num);
1498                 if (vec) {
1499                         free(vec);
1500                         vec = NULL;
1501                 }
1502                 if (ret < 0) {
1503                         if (mode_flag & DETAIL) {
1504                                 printf("\n");
1505                                 PRINT_ERR_MSG_WITH_ERRNO(
1506                                         "Failed to free page");
1507                         } else {
1508                                 printf("\t[ NG ]\n");
1509                         }
1510                         return -1;
1511                 }
1512
1513                 if (defraged_ret < 0) {
1514                         if (mode_flag & DETAIL) {
1515                                 printf("\n");
1516                                 PRINT_ERR_MSG_WITH_ERRNO(
1517                                         "Failed to defrag with "
1518                                         "EXT4_IOC_MOVE_EXT ioctl");
1519                                 if (errno == ENOTTY)
1520                                         printf("\tAt least 2.6.31-rc1 of "
1521                                                 "vanilla kernel is required\n");
1522                         } else {
1523                                 printf("\t[ NG ]\n");
1524                         }
1525                         return -1;
1526                 }
1527                 /* Adjust logical offset for next ioctl */
1528                 move_data.orig_start += move_data.moved_len;
1529                 move_data.donor_start = move_data.orig_start;
1530
1531                 start = move_data.orig_start * buf->st_blksize;
1532
1533                 /* Print defrag progress */
1534                 print_progress(file, start, buf->st_size);
1535
1536                 /* End of file */
1537                 if (start >= buf->st_size)
1538                         break;
1539
1540                 ext_list_tmp = ext_list_tmp->next;
1541         } while (ext_list_tmp != ext_list_head);
1542
1543         return 0;
1544 }
1545
1546 /*
1547  * file_defrag() -              Check file attributes and call ioctl to defrag.
1548  *
1549  * @file:               the file's name.
1550  * @buf:                the pointer of the struct stat64.
1551  * @flag:               file type.
1552  * @ftwbuf:             the pointer of a struct FTW.
1553  */
1554 static int file_defrag(const char *file, const struct stat64 *buf,
1555                         int flag EXT2FS_ATTR((unused)),
1556                         struct FTW *ftwbuf EXT2FS_ATTR((unused)))
1557 {
1558         int     fd;
1559         int     donor_fd = -1;
1560         int     ret;
1561         int     best;
1562         int     file_frags_start, file_frags_end;
1563         int     orig_physical_cnt, donor_physical_cnt = 0;
1564         char    tmp_inode_name[PATH_MAX + 8];
1565         struct fiemap_extent_list       *orig_list_physical = NULL;
1566         struct fiemap_extent_list       *orig_list_logical = NULL;
1567         struct fiemap_extent_list       *donor_list_physical = NULL;
1568         struct fiemap_extent_list       *donor_list_logical = NULL;
1569         struct fiemap_extent_group      *orig_group_head = NULL;
1570         struct fiemap_extent_group      *orig_group_tmp = NULL;
1571
1572         defraged_file_count++;
1573
1574         if (mode_flag & DETAIL) {
1575                 printf("[%u/%u]", defraged_file_count, total_count);
1576                 fflush(stdout);
1577         }
1578
1579         if (lost_found_dir[0] != '\0' &&
1580             !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) {
1581                 if (mode_flag & DETAIL) {
1582                         PRINT_FILE_NAME(file);
1583                         IN_FTW_PRINT_ERR_MSG(NGMSG_LOST_FOUND);
1584                 }
1585                 return 0;
1586         }
1587
1588         if (!S_ISREG(buf->st_mode)) {
1589                 if (mode_flag & DETAIL) {
1590                         PRINT_FILE_NAME(file);
1591                         IN_FTW_PRINT_ERR_MSG(NGMSG_FILE_UNREG);
1592                 }
1593                 return 0;
1594         }
1595
1596         /* Empty file */
1597         if (buf->st_size == 0) {
1598                 if (mode_flag & DETAIL) {
1599                         PRINT_FILE_NAME(file);
1600                         IN_FTW_PRINT_ERR_MSG("File size is 0");
1601                 }
1602                 return 0;
1603         }
1604
1605         /* Has no blocks */
1606         if (buf->st_blocks == 0) {
1607                 if (mode_flag & DETAIL) {
1608                         PRINT_FILE_NAME(file);
1609                         STATISTIC_ERR_MSG("File has no blocks");
1610                 }
1611                 return 0;
1612         }
1613
1614         fd = open64(file, O_RDWR);
1615         if (fd < 0) {
1616                 if (mode_flag & DETAIL) {
1617                         PRINT_FILE_NAME(file);
1618                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN);
1619                 }
1620                 return 0;
1621         }
1622
1623         /* Get file's extents */
1624         ret = get_file_extents(fd, &orig_list_physical);
1625         if (ret < 0) {
1626                 if (mode_flag & DETAIL) {
1627                         PRINT_FILE_NAME(file);
1628                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1629                 }
1630                 goto out;
1631         }
1632
1633         /* Get the count of file's continuous physical region */
1634         orig_physical_cnt = get_physical_count(orig_list_physical);
1635
1636         /* Change list from physical to logical */
1637         ret = change_physical_to_logical(&orig_list_physical,
1638                                                         &orig_list_logical);
1639         if (ret < 0) {
1640                 if (mode_flag & DETAIL) {
1641                         PRINT_FILE_NAME(file);
1642                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1643                 }
1644                 goto out;
1645         }
1646
1647         /* Count file fragments before defrag */
1648         file_frags_start = get_logical_count(orig_list_logical);
1649
1650         if (file_check(fd, buf, file, file_frags_start) < 0)
1651                 goto out;
1652
1653         if (fsync(fd) < 0) {
1654                 if (mode_flag & DETAIL) {
1655                         PRINT_FILE_NAME(file);
1656                         PRINT_ERR_MSG_WITH_ERRNO("Failed to sync(fsync)");
1657                 }
1658                 goto out;
1659         }
1660
1661         if (current_uid == ROOT_UID)
1662                 best =
1663                 get_best_count(SECTOR_TO_BLOCK(buf->st_blocks, block_size));
1664         else
1665                 best = 1;
1666
1667         if (file_frags_start <= best)
1668                 goto check_improvement;
1669
1670         /* Combine extents to group */
1671         ret = join_extents(orig_list_logical, &orig_group_head);
1672         if (ret < 0) {
1673                 if (mode_flag & DETAIL) {
1674                         PRINT_FILE_NAME(file);
1675                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1676                 }
1677                 goto out;
1678         }
1679
1680         /* Create donor inode */
1681         memset(tmp_inode_name, 0, PATH_MAX + 8);
1682         sprintf(tmp_inode_name, "%.*s.defrag",
1683                                 (int)strnlen(file, PATH_MAX), file);
1684         donor_fd = open64(tmp_inode_name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
1685         if (donor_fd < 0) {
1686                 if (mode_flag & DETAIL) {
1687                         PRINT_FILE_NAME(file);
1688                         if (errno == EEXIST)
1689                                 PRINT_ERR_MSG_WITH_ERRNO(
1690                                 "File is being defraged by other program");
1691                         else
1692                                 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN);
1693                 }
1694                 goto out;
1695         }
1696
1697         /* Unlink donor inode */
1698         ret = unlink(tmp_inode_name);
1699         if (ret < 0) {
1700                 if (mode_flag & DETAIL) {
1701                         PRINT_FILE_NAME(file);
1702                         PRINT_ERR_MSG_WITH_ERRNO("Failed to unlink");
1703                 }
1704                 goto out;
1705         }
1706
1707         /* Allocate space for donor inode */
1708         orig_group_tmp = orig_group_head;
1709         do {
1710                 ret = fallocate(donor_fd, 0,
1711                   (loff_t)orig_group_tmp->start->data.logical * block_size,
1712                   (loff_t)orig_group_tmp->len * block_size);
1713                 if (ret < 0) {
1714                         if (mode_flag & DETAIL) {
1715                                 PRINT_FILE_NAME(file);
1716                                 PRINT_ERR_MSG_WITH_ERRNO("Failed to fallocate");
1717                         }
1718                         goto out;
1719                 }
1720
1721                 orig_group_tmp = orig_group_tmp->next;
1722         } while (orig_group_tmp != orig_group_head);
1723
1724         /* Get donor inode's extents */
1725         ret = get_file_extents(donor_fd, &donor_list_physical);
1726         if (ret < 0) {
1727                 if (mode_flag & DETAIL) {
1728                         PRINT_FILE_NAME(file);
1729                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1730                 }
1731                 goto out;
1732         }
1733
1734         /* Calcuate donor inode's continuous physical region */
1735         donor_physical_cnt = get_physical_count(donor_list_physical);
1736
1737         /* Change donor extent list from physical to logical */
1738         ret = change_physical_to_logical(&donor_list_physical,
1739                                                         &donor_list_logical);
1740         if (ret < 0) {
1741                 if (mode_flag & DETAIL) {
1742                         PRINT_FILE_NAME(file);
1743                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1744                 }
1745                 goto out;
1746         }
1747
1748 check_improvement:
1749         if (mode_flag & DETAIL) {
1750                 if (file_frags_start != 1)
1751                         frag_files_before_defrag++;
1752
1753                 extents_before_defrag += file_frags_start;
1754         }
1755
1756         if (file_frags_start <= best ||
1757                         orig_physical_cnt <= donor_physical_cnt) {
1758                 printf("\033[79;0H\033[K[%u/%u]%s:\t%3d%%",
1759                         defraged_file_count, total_count, file, 100);
1760                 if (mode_flag & DETAIL)
1761                         printf("  extents: %d -> %d",
1762                                 file_frags_start, file_frags_start);
1763
1764                 printf("\t[ OK ]\n");
1765                 succeed_cnt++;
1766
1767                 if (file_frags_start != 1)
1768                         frag_files_after_defrag++;
1769
1770                 extents_after_defrag += file_frags_start;
1771                 goto out;
1772         }
1773
1774         /* Defrag the file */
1775         ret = call_defrag(fd, donor_fd, file, buf, donor_list_logical);
1776
1777         /* Count file fragments after defrag and print extents info */
1778         if (mode_flag & DETAIL) {
1779                 file_frags_end = file_frag_count(fd);
1780                 if (file_frags_end < 0) {
1781                         printf("\n");
1782                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_INFO);
1783                         goto out;
1784                 }
1785
1786                 if (file_frags_end != 1)
1787                         frag_files_after_defrag++;
1788
1789                 extents_after_defrag += file_frags_end;
1790
1791                 if (ret < 0)
1792                         goto out;
1793
1794                 printf("  extents: %d -> %d",
1795                         file_frags_start, file_frags_end);
1796                 fflush(stdout);
1797         }
1798
1799         if (ret < 0)
1800                 goto out;
1801
1802         printf("\t[ OK ]\n");
1803         fflush(stdout);
1804         succeed_cnt++;
1805
1806 out:
1807         close(fd);
1808         if (donor_fd != -1)
1809                 close(donor_fd);
1810         free_ext(orig_list_physical);
1811         free_ext(orig_list_logical);
1812         free_ext(donor_list_physical);
1813         free_exts_group(orig_group_head);
1814         return 0;
1815 }
1816
1817 /*
1818  * main() -             Ext4 online defrag.
1819  *
1820  * @argc:               the number of parameter.
1821  * @argv[]:             the pointer array of parameter.
1822  */
1823 int main(int argc, char *argv[])
1824 {
1825         int     opt;
1826         int     i, j;
1827         int     flags = FTW_PHYS | FTW_MOUNT;
1828         int     arg_type = -1;
1829         int     success_flag = 0;
1830         char    dir_name[PATH_MAX + 1];
1831         struct stat64   buf;
1832         struct ext4_super_block sb;
1833
1834         /* Parse arguments */
1835         if (argc == 1)
1836                 goto out;
1837
1838         while ((opt = getopt(argc, argv, "vc")) != EOF) {
1839                 switch (opt) {
1840                 case 'v':
1841                         mode_flag |= DETAIL;
1842                         break;
1843                 case 'c':
1844                         mode_flag |= STATISTIC;
1845                         break;
1846                 default:
1847                         goto out;
1848                 }
1849         }
1850
1851         if (argc == optind)
1852                 goto out;
1853
1854         current_uid = getuid();
1855
1856         /* Main process */
1857         for (i = optind; i < argc; i++) {
1858                 succeed_cnt = 0;
1859                 regular_count = 0;
1860                 total_count = 0;
1861                 frag_files_before_defrag = 0;
1862                 frag_files_after_defrag = 0;
1863                 extents_before_defrag = 0;
1864                 extents_after_defrag = 0;
1865                 defraged_file_count = 0;
1866                 files_block_count = 0;
1867                 blocks_per_group = 0;
1868                 feature_incompat = 0;
1869                 log_groups_per_flex = 0;
1870
1871                 memset(dir_name, 0, PATH_MAX + 1);
1872                 memset(lost_found_dir, 0, PATH_MAX + 1);
1873                 memset(frag_rank, 0,
1874                         sizeof(struct frag_statistic_ino) * SHOW_FRAG_FILES);
1875
1876                 if ((mode_flag & STATISTIC) && i > optind)
1877                         printf("\n");
1878
1879 #if BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN
1880                 PRINT_ERR_MSG("Endian's type is not big/little endian");
1881                 PRINT_FILE_NAME(argv[i]);
1882                 continue;
1883 #endif
1884
1885                 if (lstat64(argv[i], &buf) < 0) {
1886                         perror(NGMSG_FILE_INFO);
1887                         PRINT_FILE_NAME(argv[i]);
1888                         continue;
1889                 }
1890
1891                 if (S_ISBLK(buf.st_mode)) {
1892                         /* Block device */
1893                         if (get_mount_point(argv[i], dir_name, PATH_MAX) < 0)
1894                                 continue;
1895                         if (lstat64(dir_name, &buf) < 0) {
1896                                 perror(NGMSG_FILE_INFO);
1897                                 PRINT_FILE_NAME(argv[i]);
1898                                 continue;
1899                         }
1900                         arg_type = DEVNAME;
1901                         if (!(mode_flag & STATISTIC))
1902                                 printf("ext4 defragmentation for device(%s)\n",
1903                                         argv[i]);
1904                 } else if (S_ISDIR(buf.st_mode)) {
1905                         /* Directory */
1906                         if (access(argv[i], R_OK) < 0) {
1907                                 perror(argv[i]);
1908                                 continue;
1909                         }
1910                         arg_type = DIRNAME;
1911                         strncpy(dir_name, argv[i], strnlen(argv[i], PATH_MAX));
1912                 } else if (S_ISREG(buf.st_mode)) {
1913                         /* Regular file */
1914                         arg_type = FILENAME;
1915                 } else {
1916                         /* Irregular file */
1917                         PRINT_ERR_MSG(NGMSG_FILE_UNREG);
1918                         PRINT_FILE_NAME(argv[i]);
1919                         continue;
1920                 }
1921
1922                 /* Set blocksize */
1923                 block_size = buf.st_blksize;
1924
1925                 /* For device case,
1926                  * filesystem type checked in get_mount_point()
1927                  */
1928                 if (arg_type == FILENAME || arg_type == DIRNAME) {
1929                         if (is_ext4(argv[i]) < 0)
1930                                 continue;
1931                         if (realpath(argv[i], dir_name) == NULL) {
1932                                 perror("Couldn't get full path");
1933                                 PRINT_FILE_NAME(argv[i]);
1934                                 continue;
1935                         }
1936                 }
1937
1938                 if (current_uid == ROOT_UID) {
1939                         /* Get super block info */
1940                         memset(&sb, 0, sizeof(struct ext4_super_block));
1941                         if (get_superblock_info(dir_name, &sb) < 0) {
1942                                 if (mode_flag & DETAIL) {
1943                                         perror("Can't get super block info");
1944                                         PRINT_FILE_NAME(argv[i]);
1945                                 }
1946                                 continue;
1947                         }
1948
1949                         blocks_per_group = ext2fs_swab32(sb.s_blocks_per_group);
1950                         feature_incompat = ext2fs_swab32(sb.s_feature_incompat);
1951                         log_groups_per_flex = sb.s_log_groups_per_flex;
1952                 }
1953
1954                 switch (arg_type) {
1955                 case DIRNAME:
1956                         if (!(mode_flag & STATISTIC))
1957                                 printf("ext4 defragmentation "
1958                                         "for directory(%s)\n", argv[i]);
1959
1960                         int mount_dir_len = 0;
1961                         mount_dir_len = strnlen(lost_found_dir, PATH_MAX);
1962
1963                         strncat(lost_found_dir, "/lost+found",
1964                                 PATH_MAX - strnlen(lost_found_dir, PATH_MAX));
1965
1966                         /* Not the case("e4defrag  mount_piont_dir") */
1967                         if (dir_name[mount_dir_len] != '\0') {
1968                                 /*
1969                                  * "e4defrag mount_piont_dir/lost+found"
1970                                  * or "e4defrag mount_piont_dir/lost+found/"
1971                                  */
1972                                 if (strncmp(lost_found_dir, dir_name,
1973                                             strnlen(lost_found_dir,
1974                                                     PATH_MAX)) == 0 &&
1975                                     (dir_name[strnlen(lost_found_dir,
1976                                                       PATH_MAX)] == '\0' ||
1977                                      dir_name[strnlen(lost_found_dir,
1978                                                       PATH_MAX)] == '/')) {
1979                                         PRINT_ERR_MSG(NGMSG_LOST_FOUND);
1980                                         PRINT_FILE_NAME(argv[i]);
1981                                         continue;
1982                                 }
1983
1984                                 /* "e4defrag mount_piont_dir/else_dir" */
1985                                 memset(lost_found_dir, 0, PATH_MAX + 1);
1986                         }
1987                 case DEVNAME:
1988                         if (arg_type == DEVNAME) {
1989                                 strncpy(lost_found_dir, dir_name,
1990                                         strnlen(dir_name, PATH_MAX));
1991                                 strncat(lost_found_dir, "/lost+found/",
1992                                         PATH_MAX - strnlen(lost_found_dir,
1993                                                            PATH_MAX));
1994                         }
1995
1996                         nftw64(dir_name, calc_entry_counts, FTW_OPEN_FD, flags);
1997
1998                         if (mode_flag & STATISTIC) {
1999                                 if (mode_flag & DETAIL)
2000                                         printf("%-40s%10s/%-10s%9s\n",
2001                                         "<File>", "now", "best", "ratio");
2002
2003                                 if (!(mode_flag & DETAIL) &&
2004                                                 current_uid != ROOT_UID) {
2005                                         printf(" Done.\n");
2006                                         continue;
2007                                 }
2008
2009                                 nftw64(dir_name, file_statistic,
2010                                                         FTW_OPEN_FD, flags);
2011
2012                                 if (succeed_cnt != 0 &&
2013                                         current_uid == ROOT_UID) {
2014                                         if (mode_flag & DETAIL)
2015                                                 printf("\n");
2016                                         printf("%-40s%10s/%-10s%9s\n",
2017                                                 "<Fragmented files>", "now",
2018                                                 "best", "ratio");
2019                                         for (j = 0; j < SHOW_FRAG_FILES; j++) {
2020                                                 if (strlen(frag_rank[j].
2021                                                         msg_buffer) > 37) {
2022                                                         printf("%d. %s\n%50d/"
2023                                                         "%-10d%8.2f%%\n", j + 1,
2024                                                         frag_rank[j].msg_buffer,
2025                                                         frag_rank[j].now_count,
2026                                                         frag_rank[j].best_count,
2027                                                         frag_rank[j].ratio);
2028                                                 } else if (strlen(frag_rank[j].
2029                                                         msg_buffer) > 0) {
2030                                                         printf("%d. %-37s%10d/"
2031                                                         "%-10d%8.2f%%\n", j + 1,
2032                                                         frag_rank[j].msg_buffer,
2033                                                         frag_rank[j].now_count,
2034                                                         frag_rank[j].best_count,
2035                                                         frag_rank[j].ratio);
2036                                                 } else
2037                                                         break;
2038                                         }
2039                                 }
2040                                 break;
2041                         }
2042                         /* File tree walk */
2043                         nftw64(dir_name, file_defrag, FTW_OPEN_FD, flags);
2044                         printf("\n\tSuccess:\t\t\t[ %u/%u ]\n", succeed_cnt,
2045                                 total_count);
2046                         printf("\tFailure:\t\t\t[ %u/%u ]\n",
2047                                 total_count - succeed_cnt, total_count);
2048                         if (mode_flag & DETAIL) {
2049                                 printf("\tTotal extents:\t\t\t%4d->%d\n",
2050                                         extents_before_defrag,
2051                                         extents_after_defrag);
2052                                 printf("\tFragmented percentage:\t\t"
2053                                         "%3llu%%->%llu%%\n",
2054                                         !regular_count ? 0 :
2055                                         ((unsigned long long)
2056                                         frag_files_before_defrag * 100) /
2057                                         regular_count,
2058                                         !regular_count ? 0 :
2059                                         ((unsigned long long)
2060                                         frag_files_after_defrag * 100) /
2061                                         regular_count);
2062                         }
2063                         break;
2064                 case FILENAME:
2065                         total_count = 1;
2066                         regular_count = 1;
2067                         strncat(lost_found_dir, "/lost+found/",
2068                                 PATH_MAX - strnlen(lost_found_dir,
2069                                                    PATH_MAX));
2070                         if (strncmp(lost_found_dir, dir_name,
2071                                     strnlen(lost_found_dir,
2072                                             PATH_MAX)) == 0) {
2073                                 PRINT_ERR_MSG(NGMSG_LOST_FOUND);
2074                                 PRINT_FILE_NAME(argv[i]);
2075                                 continue;
2076                         }
2077
2078                         if (mode_flag & STATISTIC) {
2079                                 file_statistic(argv[i], &buf, FTW_F, NULL);
2080                                 break;
2081                         } else
2082                                 printf("ext4 defragmentation for %s\n",
2083                                                                  argv[i]);
2084                         /* Defrag single file process */
2085                         file_defrag(argv[i], &buf, FTW_F, NULL);
2086                         if (succeed_cnt != 0)
2087                                 printf(" Success:\t\t\t[1/1]\n");
2088                         else
2089                                 printf(" Success:\t\t\t[0/1]\n");
2090
2091                         break;
2092                 }
2093
2094                 if (succeed_cnt != 0)
2095                         success_flag = 1;
2096                 if (mode_flag & STATISTIC) {
2097                         if (current_uid != ROOT_UID) {
2098                                 printf(" Done.\n");
2099                                 continue;
2100                         }
2101
2102                         if (!succeed_cnt) {
2103                                 if (mode_flag & DETAIL)
2104                                         printf("\n");
2105
2106                                 if (arg_type == DEVNAME)
2107                                         printf(" In this device(%s), "
2108                                         "none can be defragmented.\n", argv[i]);
2109                                 else if (arg_type == DIRNAME)
2110                                         printf(" In this directory(%s), "
2111                                         "none can be defragmented.\n", argv[i]);
2112                                 else
2113                                         printf(" This file(%s) "
2114                                         "can't be defragmented.\n", argv[i]);
2115                         } else {
2116                                 float files_ratio = 0.0;
2117                                 float score = 0.0;
2118                                 files_ratio = (float)(extents_before_defrag -
2119                                                 extents_after_defrag) *
2120                                                 100 / files_block_count;
2121                                 score = CALC_SCORE(files_ratio);
2122                                 printf("\n Total/best extents\t\t\t\t%d/%d\n"
2123                                         " Fragmentation ratio\t\t\t\t%.2f%%\n"
2124                                         " Fragmentation score\t\t\t\t%.2f\n",
2125                                                 extents_before_defrag,
2126                                                 extents_after_defrag,
2127                                                 files_ratio, score);
2128                                 printf(" [0-30 no problem:"
2129                                         " 31-55 a little bit fragmented:"
2130                                         " 55- needs defrag]\n");
2131
2132                                 if (arg_type == DEVNAME)
2133                                         printf(" This device(%s) ", argv[i]);
2134                                 else if (arg_type == DIRNAME)
2135                                         printf(" This directory(%s) ", argv[i]);
2136                                 else
2137                                         printf(" This file(%s) ", argv[i]);
2138
2139                                 if (score > BOUND_SCORE)
2140                                         printf("needs defragmentation.\n");
2141                                 else
2142                                         printf("does not need "
2143                                                         "defragmentation.\n");
2144                         }
2145                         printf(" Done.\n");
2146                 }
2147
2148         }
2149
2150         if (success_flag)
2151                 return 0;
2152
2153         exit(1);
2154
2155 out:
2156         printf(MSG_USAGE);
2157         exit(1);
2158 }
2159