Whamcloud - gitweb
e4defrag: fix ppc build
[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                 len = strlen(mnt->mnt_dir);
434                 ret = memcmp(file_path, mnt->mnt_dir, len);
435                 if (ret != 0)
436                         continue;
437
438                 if (maxlen >= len)
439                         continue;
440
441                 maxlen = len;
442
443                 mnt_type = realloc(mnt_type, strlen(mnt->mnt_type) + 1);
444                 if (mnt_type == NULL) {
445                         endmntent(fp);
446                         return -1;
447                 }
448                 memset(mnt_type, 0, strlen(mnt->mnt_type) + 1);
449                 strncpy(mnt_type, mnt->mnt_type, strlen(mnt->mnt_type));
450                 strncpy(lost_found_dir, mnt->mnt_dir, PATH_MAX);
451         }
452
453         endmntent(fp);
454         if (strcmp(mnt_type, FS_EXT4) == 0) {
455                 FREE(mnt_type);
456                 return 0;
457         } else {
458                 FREE(mnt_type);
459                 PRINT_ERR_MSG(NGMSG_EXT4);
460                 return -1;
461         }
462 }
463
464 /*
465  * calc_entry_counts() -        Calculate file counts.
466  *
467  * @file:               file name.
468  * @buf:                file info.
469  * @flag:               file type.
470  * @ftwbuf:             the pointer of a struct FTW.
471  */
472 static int calc_entry_counts(const char *file EXT2FS_ATTR((unused)),
473                 const struct stat64 *buf, int flag EXT2FS_ATTR((unused)),
474                 struct FTW *ftwbuf EXT2FS_ATTR((unused)))
475 {
476         if (S_ISREG(buf->st_mode))
477                 regular_count++;
478
479         total_count++;
480
481         return 0;
482 }
483
484 /*
485  * page_in_core() -     Get information on whether pages are in core.
486  *
487  * @fd:                 defrag target file's descriptor.
488  * @defrag_data:        data used for defrag.
489  * @vec:                page state array.
490  * @page_num:           page number.
491  */
492 static int page_in_core(int fd, struct move_extent defrag_data,
493                         unsigned char **vec, unsigned int *page_num)
494 {
495         long    pagesize = sysconf(_SC_PAGESIZE);
496         void    *page = NULL;
497         loff_t  offset, end_offset, length;
498
499         if (vec == NULL || *vec != NULL)
500                 return -1;
501
502         /* In mmap, offset should be a multiple of the page size */
503         offset = (loff_t)defrag_data.orig_start * block_size;
504         length = (loff_t)defrag_data.len * block_size;
505         end_offset = offset + length;
506         /* Round the offset down to the nearest multiple of pagesize */
507         offset = (offset / pagesize) * pagesize;
508         length = end_offset - offset;
509
510         page = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, offset);
511         if (page == MAP_FAILED)
512                 return -1;
513
514         *page_num = 0;
515         *page_num = (length + pagesize - 1) / pagesize;
516         *vec = (unsigned char *)calloc(*page_num, 1);
517         if (*vec == NULL)
518                 return -1;
519
520         /* Get information on whether pages are in core */
521         if (mincore(page, (size_t)length, *vec) == -1 ||
522                 munmap(page, length) == -1) {
523                 FREE(*vec);
524                 return -1;
525         }
526
527         return 0;
528 }
529
530 /*
531  * defrag_fadvise() -   Predeclare an access pattern for file data.
532  *
533  * @fd:                 defrag target file's descriptor.
534  * @defrag_data:        data used for defrag.
535  * @vec:                page state array.
536  * @page_num:           page number.
537  */
538 static int defrag_fadvise(int fd, struct move_extent defrag_data,
539                    unsigned char *vec, unsigned int page_num)
540 {
541         int     flag = 1;
542         long    pagesize = sysconf(_SC_PAGESIZE);
543         int     fadvise_flag = POSIX_FADV_DONTNEED;
544         int     sync_flag = SYNC_FILE_RANGE_WAIT_BEFORE |
545                             SYNC_FILE_RANGE_WRITE |
546                             SYNC_FILE_RANGE_WAIT_AFTER;
547         unsigned int    i;
548         loff_t  offset;
549
550         offset = (loff_t)defrag_data.orig_start * block_size;
551         offset = (offset / pagesize) * pagesize;
552
553         /* Sync file for fadvise process */
554         if (sync_file_range(fd, offset,
555                 (loff_t)pagesize * page_num, sync_flag) < 0)
556                 return -1;
557
558         /* Try to release buffer cache which this process used,
559          * then other process can use the released buffer
560          */
561         for (i = 0; i < page_num; i++) {
562                 if ((vec[i] & 0x1) == 0) {
563                         offset += pagesize;
564                         continue;
565                 }
566                 if (posix_fadvise(fd, offset, pagesize, fadvise_flag) < 0) {
567                         if ((mode_flag & DETAIL) && flag) {
568                                 perror("\tFailed to fadvise");
569                                 flag = 0;
570                         }
571                 }
572                 offset += pagesize;
573         }
574
575         return 0;
576 }
577
578 /*
579  * check_free_size() -  Check if there's enough disk space.
580  *
581  * @fd:                 defrag target file's descriptor.
582  * @file:               file name.
583  * @buf:                the pointer of the struct stat64.
584  */
585 static int check_free_size(int fd, const char *file, const struct stat64 *buf)
586 {
587         ext4_fsblk_t    blk_count;
588         ext4_fsblk_t    free_blk_count;
589         struct statfs64 fsbuf;
590
591         if (fstatfs64(fd, &fsbuf) < 0) {
592                 if (mode_flag & DETAIL) {
593                         PRINT_FILE_NAME(file);
594                         PRINT_ERR_MSG_WITH_ERRNO(
595                                 "Failed to get filesystem information");
596                 }
597                 return -1;
598         }
599
600         /* Target file size measured by filesystem IO blocksize */
601         blk_count = SECTOR_TO_BLOCK(buf->st_blocks, fsbuf.f_bsize);
602
603         /* Compute free space for root and normal user separately */
604         if (current_uid == ROOT_UID)
605                 free_blk_count = fsbuf.f_bfree;
606         else
607                 free_blk_count = fsbuf.f_bavail;
608
609         if (free_blk_count >= blk_count)
610                 return 0;
611
612         return -ENOSPC;
613 }
614
615 /*
616  * file_frag_count() -  Get file fragment count.
617  *
618  * @fd:                 defrag target file's descriptor.
619  */
620 static int file_frag_count(int fd)
621 {
622         int     ret;
623         struct fiemap   fiemap_buf;
624
625         /* When fm_extent_count is 0,
626          * ioctl just get file fragment count.
627          */
628         memset(&fiemap_buf, 0, sizeof(struct fiemap));
629         fiemap_buf.fm_start = 0;
630         fiemap_buf.fm_length = FIEMAP_MAX_OFFSET;
631         fiemap_buf.fm_flags |= FIEMAP_FLAG_SYNC;
632
633         ret = ioctl(fd, FS_IOC_FIEMAP, &fiemap_buf);
634         if (ret < 0)
635                 return ret;
636
637         return fiemap_buf.fm_mapped_extents;
638 }
639
640 /*
641  * file_check() -       Check file's attributes.
642  *
643  * @fd:                 defrag target file's descriptor.
644  * @buf:                a pointer of the struct stat64.
645  * @file:               the file's name.
646  * @extents:            the file's extents.
647  */
648 static int file_check(int fd, const struct stat64 *buf, const char *file,
649                 int extents)
650 {
651         int     ret;
652         struct flock    lock;
653
654         /* Write-lock check is more reliable */
655         lock.l_type = F_WRLCK;
656         lock.l_start = 0;
657         lock.l_whence = SEEK_SET;
658         lock.l_len = 0;
659
660         /* Free space */
661         ret = check_free_size(fd, file, buf);
662         if (ret < 0) {
663                 if ((mode_flag & DETAIL) && ret == -ENOSPC) {
664                         printf("\033[79;0H\033[K[%u/%u] \"%s\"\t\t"
665                                 "  extents: %d -> %d\n", defraged_file_count,
666                                 total_count, file, extents, extents);
667                         IN_FTW_PRINT_ERR_MSG(
668                         "Defrag size is larger than filesystem's free space");
669                 }
670                 return -1;
671         }
672
673         /* Access authority */
674         if (current_uid != ROOT_UID &&
675                 buf->st_uid != current_uid) {
676                 if (mode_flag & DETAIL) {
677                         printf("\033[79;0H\033[K[%u/%u] \"%s\"\t\t"
678                                 "  extents: %d -> %d\n", defraged_file_count,
679                                 total_count, file, extents, extents);
680                         IN_FTW_PRINT_ERR_MSG(
681                                 "File is not current user's file"
682                                 " or current user is not root");
683                 }
684                 return -1;
685         }
686
687         /* Lock status */
688         if (fcntl(fd, F_GETLK, &lock) < 0) {
689                 if (mode_flag & DETAIL) {
690                         PRINT_FILE_NAME(file);
691                         PRINT_ERR_MSG_WITH_ERRNO(
692                                 "Failed to get lock information");
693                 }
694                 return -1;
695         } else if (lock.l_type != F_UNLCK) {
696                 if (mode_flag & DETAIL) {
697                         PRINT_FILE_NAME(file);
698                         IN_FTW_PRINT_ERR_MSG("File has been locked");
699                 }
700                 return -1;
701         }
702
703         return 0;
704 }
705
706 /*
707  * insert_extent_by_logical() - Sequentially insert extent by logical.
708  *
709  * @ext_list_head:      the head of logical extent list.
710  * @ext:                the extent element which will be inserted.
711  */
712 static int insert_extent_by_logical(struct fiemap_extent_list **ext_list_head,
713                         struct fiemap_extent_list *ext)
714 {
715         struct fiemap_extent_list       *ext_list_tmp = *ext_list_head;
716
717         if (ext == NULL)
718                 goto out;
719
720         /* First element */
721         if (*ext_list_head == NULL) {
722                 (*ext_list_head) = ext;
723                 (*ext_list_head)->prev = *ext_list_head;
724                 (*ext_list_head)->next = *ext_list_head;
725                 return 0;
726         }
727
728         if (ext->data.logical <= ext_list_tmp->data.logical) {
729                 /* Insert before head */
730                 if (ext_list_tmp->data.logical <
731                         ext->data.logical + ext->data.len)
732                         /* Overlap */
733                         goto out;
734                 /* Adjust head */
735                 *ext_list_head = ext;
736         } else {
737                 /* Insert into the middle or last of the list */
738                 do {
739                         if (ext->data.logical < ext_list_tmp->data.logical)
740                                 break;
741                         ext_list_tmp = ext_list_tmp->next;
742                 } while (ext_list_tmp != (*ext_list_head));
743                 if (ext->data.logical <
744                     ext_list_tmp->prev->data.logical +
745                         ext_list_tmp->prev->data.len)
746                         /* Overlap */
747                         goto out;
748
749                 if (ext_list_tmp != *ext_list_head &&
750                     ext_list_tmp->data.logical <
751                     ext->data.logical + ext->data.len)
752                         /* Overlap */
753                         goto out;
754         }
755         ext_list_tmp = ext_list_tmp->prev;
756         /* Insert "ext" after "ext_list_tmp" */
757         insert(ext_list_tmp, ext);
758         return 0;
759 out:
760         errno = EINVAL;
761         return -1;
762 }
763
764 /*
765  * insert_extent_by_physical() -        Sequentially insert extent by physical.
766  *
767  * @ext_list_head:      the head of physical extent list.
768  * @ext:                the extent element which will be inserted.
769  */
770 static int insert_extent_by_physical(struct fiemap_extent_list **ext_list_head,
771                         struct fiemap_extent_list *ext)
772 {
773         struct fiemap_extent_list       *ext_list_tmp = *ext_list_head;
774
775         if (ext == NULL)
776                 goto out;
777
778         /* First element */
779         if (*ext_list_head == NULL) {
780                 (*ext_list_head) = ext;
781                 (*ext_list_head)->prev = *ext_list_head;
782                 (*ext_list_head)->next = *ext_list_head;
783                 return 0;
784         }
785
786         if (ext->data.physical <= ext_list_tmp->data.physical) {
787                 /* Insert before head */
788                 if (ext_list_tmp->data.physical <
789                                         ext->data.physical + ext->data.len)
790                         /* Overlap */
791                         goto out;
792                 /* Adjust head */
793                 *ext_list_head = ext;
794         } else {
795                 /* Insert into the middle or last of the list */
796                 do {
797                         if (ext->data.physical < ext_list_tmp->data.physical)
798                                 break;
799                         ext_list_tmp = ext_list_tmp->next;
800                 } while (ext_list_tmp != (*ext_list_head));
801                 if (ext->data.physical <
802                     ext_list_tmp->prev->data.physical +
803                                 ext_list_tmp->prev->data.len)
804                         /* Overlap */
805                         goto out;
806
807                 if (ext_list_tmp != *ext_list_head &&
808                     ext_list_tmp->data.physical <
809                                 ext->data.physical + ext->data.len)
810                         /* Overlap */
811                         goto out;
812         }
813         ext_list_tmp = ext_list_tmp->prev;
814         /* Insert "ext" after "ext_list_tmp" */
815         insert(ext_list_tmp, ext);
816         return 0;
817 out:
818         errno = EINVAL;
819         return -1;
820 }
821
822 /*
823  * insert_exts_group() -        Insert a exts_group.
824  *
825  * @ext_group_head:             the head of a exts_group list.
826  * @exts_group:                 the exts_group element which will be inserted.
827  */
828 static int insert_exts_group(struct fiemap_extent_group **ext_group_head,
829                                 struct fiemap_extent_group *exts_group)
830 {
831         struct fiemap_extent_group      *ext_group_tmp = NULL;
832
833         if (exts_group == NULL) {
834                 errno = EINVAL;
835                 return -1;
836         }
837
838         /* Initialize list */
839         if (*ext_group_head == NULL) {
840                 (*ext_group_head) = exts_group;
841                 (*ext_group_head)->prev = *ext_group_head;
842                 (*ext_group_head)->next = *ext_group_head;
843                 return 0;
844         }
845
846         ext_group_tmp = (*ext_group_head)->prev;
847         insert(ext_group_tmp, exts_group);
848
849         return 0;
850 }
851
852 /*
853  * join_extents() -             Find continuous region(exts_group).
854  *
855  * @ext_list_head:              the head of the extent list.
856  * @ext_group_head:             the head of the target exts_group list.
857  */
858 static int join_extents(struct fiemap_extent_list *ext_list_head,
859                 struct fiemap_extent_group **ext_group_head)
860 {
861         __u64   len = ext_list_head->data.len;
862         struct fiemap_extent_list *ext_list_start = ext_list_head;
863         struct fiemap_extent_list *ext_list_tmp = ext_list_head->next;
864
865         do {
866                 struct fiemap_extent_group      *ext_group_tmp = NULL;
867
868                 /* This extent and previous extent are not continuous,
869                  * so, all previous extents are treated as an extent group.
870                  */
871                 if ((ext_list_tmp->prev->data.logical +
872                         ext_list_tmp->prev->data.len)
873                                 != ext_list_tmp->data.logical) {
874                         ext_group_tmp =
875                                 malloc(sizeof(struct fiemap_extent_group));
876                         if (ext_group_tmp == NULL)
877                                 return -1;
878
879                         memset(ext_group_tmp, 0,
880                                 sizeof(struct fiemap_extent_group));
881                         ext_group_tmp->len = len;
882                         ext_group_tmp->start = ext_list_start;
883                         ext_group_tmp->end = ext_list_tmp->prev;
884
885                         if (insert_exts_group(ext_group_head,
886                                 ext_group_tmp) < 0) {
887                                 FREE(ext_group_tmp);
888                                 return -1;
889                         }
890                         ext_list_start = ext_list_tmp;
891                         len = ext_list_tmp->data.len;
892                         ext_list_tmp = ext_list_tmp->next;
893                         continue;
894                 }
895
896                 /* This extent and previous extent are continuous,
897                  * so, they belong to the same extent group, and we check
898                  * if the next extent belongs to the same extent group.
899                  */
900                 len += ext_list_tmp->data.len;
901                 ext_list_tmp = ext_list_tmp->next;
902         } while (ext_list_tmp != ext_list_head->next);
903
904         return 0;
905 }
906
907 /*
908  * get_file_extents() - Get file's extent list.
909  *
910  * @fd:                 defrag target file's descriptor.
911  * @ext_list_head:      the head of the extent list.
912  */
913 static int get_file_extents(int fd, struct fiemap_extent_list **ext_list_head)
914 {
915         __u32   i;
916         int     ret;
917         int     ext_buf_size, fie_buf_size;
918         __u64   pos = 0;
919         struct fiemap   *fiemap_buf = NULL;
920         struct fiemap_extent    *ext_buf = NULL;
921         struct fiemap_extent_list       *ext_list = NULL;
922
923         /* Convert units, in bytes.
924          * Be careful : now, physical block number in extent is 48bit,
925          * and the maximum blocksize for ext4 is 4K(12bit),
926          * so there is no overflow, but in future it may be changed.
927          */
928
929         /* Alloc space for fiemap */
930         ext_buf_size = EXTENT_MAX_COUNT * sizeof(struct fiemap_extent);
931         fie_buf_size = sizeof(struct fiemap) + ext_buf_size;
932
933         fiemap_buf = malloc(fie_buf_size);
934         if (fiemap_buf == NULL)
935                 return -1;
936
937         ext_buf = fiemap_buf->fm_extents;
938         memset(fiemap_buf, 0, fie_buf_size);
939         fiemap_buf->fm_length = FIEMAP_MAX_OFFSET;
940         fiemap_buf->fm_flags |= FIEMAP_FLAG_SYNC;
941         fiemap_buf->fm_extent_count = EXTENT_MAX_COUNT;
942
943         do {
944                 fiemap_buf->fm_start = pos;
945                 memset(ext_buf, 0, ext_buf_size);
946                 ret = ioctl(fd, FS_IOC_FIEMAP, fiemap_buf);
947                 if (ret < 0)
948                         goto out;
949                 for (i = 0; i < fiemap_buf->fm_mapped_extents; i++) {
950                         ext_list = NULL;
951                         ext_list = malloc(sizeof(struct fiemap_extent_list));
952                         if (ext_list == NULL)
953                                 goto out;
954
955                         ext_list->data.physical = ext_buf[i].fe_physical
956                                                 / block_size;
957                         ext_list->data.logical = ext_buf[i].fe_logical
958                                                 / block_size;
959                         ext_list->data.len = ext_buf[i].fe_length
960                                                 / block_size;
961
962                         ret = insert_extent_by_physical(
963                                         ext_list_head, ext_list);
964                         if (ret < 0) {
965                                 FREE(ext_list);
966                                 goto out;
967                         }
968                 }
969                 /* Record file's logical offset this time */
970                 pos = ext_buf[EXTENT_MAX_COUNT-1].fe_logical +
971                         ext_buf[EXTENT_MAX_COUNT-1].fe_length;
972                 /*
973                  * If fm_extents array has been filled and
974                  * there are extents left, continue to cycle.
975                  */
976         } while (fiemap_buf->fm_mapped_extents
977                                         == EXTENT_MAX_COUNT &&
978                 !(ext_buf[EXTENT_MAX_COUNT-1].fe_flags
979                                         & FIEMAP_EXTENT_LAST));
980
981         FREE(fiemap_buf);
982         return 0;
983 out:
984         FREE(fiemap_buf);
985         return -1;
986 }
987
988 /*
989  * get_logical_count() -        Get the file logical extents count.
990  *
991  * @logical_list_head:  the head of the logical extent list.
992  */
993 static int get_logical_count(struct fiemap_extent_list *logical_list_head)
994 {
995         int ret = 0;
996         struct fiemap_extent_list *ext_list_tmp  = logical_list_head;
997
998         do {
999                 ret++;
1000                 ext_list_tmp = ext_list_tmp->next;
1001         } while (ext_list_tmp != logical_list_head);
1002
1003         return ret;
1004 }
1005
1006 /*
1007  * get_physical_count() -       Get the file physical extents count.
1008  *
1009  * @physical_list_head: the head of the physical extent list.
1010  */
1011 static int get_physical_count(struct fiemap_extent_list *physical_list_head)
1012 {
1013         int ret = 0;
1014         struct fiemap_extent_list *ext_list_tmp = physical_list_head;
1015
1016         do {
1017                 if ((ext_list_tmp->data.physical + ext_list_tmp->data.len)
1018                                 != ext_list_tmp->next->data.physical) {
1019                         /* This extent and next extent are not continuous. */
1020                         ret++;
1021                 }
1022
1023                 ext_list_tmp = ext_list_tmp->next;
1024         } while (ext_list_tmp != physical_list_head);
1025
1026         return ret;
1027 }
1028
1029 /*
1030  * change_physical_to_logical() -       Change list from physical to logical.
1031  *
1032  * @physical_list_head: the head of physical extent list.
1033  * @logical_list_head:  the head of logical extent list.
1034  */
1035 static int change_physical_to_logical(
1036                         struct fiemap_extent_list **physical_list_head,
1037                         struct fiemap_extent_list **logical_list_head)
1038 {
1039         int ret;
1040         struct fiemap_extent_list *ext_list_tmp = *physical_list_head;
1041         struct fiemap_extent_list *ext_list_next = ext_list_tmp->next;
1042
1043         while (1) {
1044                 if (ext_list_tmp == ext_list_next) {
1045                         ret = insert_extent_by_logical(
1046                                 logical_list_head, ext_list_tmp);
1047                         if (ret < 0)
1048                                 return -1;
1049
1050                         *physical_list_head = NULL;
1051                         break;
1052                 }
1053
1054                 ext_list_tmp->prev->next = ext_list_tmp->next;
1055                 ext_list_tmp->next->prev = ext_list_tmp->prev;
1056                 *physical_list_head = ext_list_next;
1057
1058                 ret = insert_extent_by_logical(
1059                         logical_list_head, ext_list_tmp);
1060                 if (ret < 0) {
1061                         FREE(ext_list_tmp);
1062                         return -1;
1063                 }
1064                 ext_list_tmp = ext_list_next;
1065                 ext_list_next = ext_list_next->next;
1066         }
1067
1068         return 0;
1069 }
1070
1071 /*
1072  * free_ext() -         Free the extent list.
1073  *
1074  * @ext_list_head:      the extent list head of which will be free.
1075  */
1076 static void free_ext(struct fiemap_extent_list *ext_list_head)
1077 {
1078         struct fiemap_extent_list       *ext_list_tmp = NULL;
1079
1080         if (ext_list_head == NULL)
1081                 return;
1082
1083         while (ext_list_head->next != ext_list_head) {
1084                 ext_list_tmp = ext_list_head;
1085                 ext_list_head->prev->next = ext_list_head->next;
1086                 ext_list_head->next->prev = ext_list_head->prev;
1087                 ext_list_head = ext_list_head->next;
1088                 free(ext_list_tmp);
1089         }
1090         free(ext_list_head);
1091 }
1092
1093 /*
1094  * free_exts_group() -          Free the exts_group.
1095  *
1096  * @*ext_group_head:    the exts_group list head which will be free.
1097  */
1098 static void free_exts_group(struct fiemap_extent_group *ext_group_head)
1099 {
1100         struct fiemap_extent_group      *ext_group_tmp = NULL;
1101
1102         if (ext_group_head == NULL)
1103                 return;
1104
1105         while (ext_group_head->next != ext_group_head) {
1106                 ext_group_tmp = ext_group_head;
1107                 ext_group_head->prev->next = ext_group_head->next;
1108                 ext_group_head->next->prev = ext_group_head->prev;
1109                 ext_group_head = ext_group_head->next;
1110                 free(ext_group_tmp);
1111         }
1112         free(ext_group_head);
1113 }
1114
1115 /*
1116  * get_superblock_info() -      Get superblock info by the file name.
1117  *
1118  * @file:               the file's name.
1119  * @sb:         the pointer of the struct ext4_super_block.
1120  */
1121 static int get_superblock_info(const char *file, struct ext4_super_block *sb)
1122 {
1123         /* Refer to /etc/mtab */
1124         const char      *mtab = MOUNTED;
1125         FILE    *fp = NULL;
1126
1127         int     fd = -1;
1128         int     ret;
1129         size_t maxlen = 0;
1130         size_t len;
1131         char    dev_name[PATH_MAX + 1];
1132         struct mntent   *mnt = NULL;
1133
1134         fp = setmntent(mtab, "r");
1135         if (fp == NULL)
1136                 return -1;
1137
1138         while ((mnt = getmntent(fp)) != NULL) {
1139                 len = strlen(mnt->mnt_dir);
1140                 ret = memcmp(file, mnt->mnt_dir, len);
1141                 if (ret != 0)
1142                         continue;
1143
1144                 if (len < maxlen)
1145                         continue;
1146
1147                 maxlen = len;
1148
1149                 memset(dev_name, 0, PATH_MAX + 1);
1150                 strncpy(dev_name, mnt->mnt_fsname,
1151                                 strnlen(mnt->mnt_fsname, PATH_MAX));
1152         }
1153
1154         fd = open64(dev_name, O_RDONLY);
1155         if (fd < 0) {
1156                 ret = -1;
1157                 goto out;
1158         }
1159
1160         /* Set offset to read superblock */
1161         ret = lseek64(fd, SUPERBLOCK_OFFSET, SEEK_SET);
1162         if (ret < 0)
1163                 goto out;
1164
1165         ret = read(fd, sb, sizeof(struct ext4_super_block));
1166         if (ret < 0)
1167                 goto out;
1168
1169 out:
1170         if (fd != -1)
1171                 close(fd);
1172         endmntent(fp);
1173         return ret;
1174 }
1175
1176 /*
1177  * get_best_count() -   Get the file best extents count.
1178  *
1179  * @block_count:                the file's physical block count.
1180  */
1181 static int get_best_count(ext4_fsblk_t block_count)
1182 {
1183         int ret;
1184         unsigned int flex_bg_num;
1185
1186         /* Calcuate best extents count */
1187         if (feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) {
1188                 flex_bg_num = 1 << log_groups_per_flex;
1189                 ret = ((block_count - 1) /
1190                         ((ext4_fsblk_t)blocks_per_group *
1191                                 flex_bg_num)) + 1;
1192         } else
1193                 ret = ((block_count - 1) / blocks_per_group) + 1;
1194
1195         return ret;
1196 }
1197
1198
1199 /*
1200  * file_statistic() -   Get statistic info of the file's fragments.
1201  *
1202  * @file:               the file's name.
1203  * @buf:                the pointer of the struct stat64.
1204  * @flag:               file type.
1205  * @ftwbuf:             the pointer of a struct FTW.
1206  */
1207 static int file_statistic(const char *file, const struct stat64 *buf,
1208                         int flag EXT2FS_ATTR((unused)),
1209                         struct FTW *ftwbuf EXT2FS_ATTR((unused)))
1210 {
1211         int     fd;
1212         int     ret;
1213         int     now_ext_count, best_ext_count = 0, physical_ext_count;
1214         int     i, j;
1215         float   ratio = 0.0;
1216         ext4_fsblk_t    blk_count = 0;
1217         char    msg_buffer[PATH_MAX + 24];
1218         struct fiemap_extent_list *physical_list_head = NULL;
1219         struct fiemap_extent_list *logical_list_head = NULL;
1220
1221         defraged_file_count++;
1222
1223         if (mode_flag & DETAIL) {
1224                 if (total_count == 1 && regular_count == 1)
1225                         printf("<File>\n");
1226                 else {
1227                         printf("[%u/%u]", defraged_file_count, total_count);
1228                         fflush(stdout);
1229                 }
1230         }
1231         if (lost_found_dir[0] != '\0' &&
1232             !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) {
1233                 if (mode_flag & DETAIL) {
1234                         PRINT_FILE_NAME(file);
1235                         STATISTIC_ERR_MSG(NGMSG_LOST_FOUND);
1236                 }
1237                         return 0;
1238         }
1239
1240         if (!S_ISREG(buf->st_mode)) {
1241                 if (mode_flag & DETAIL) {
1242                         PRINT_FILE_NAME(file);
1243                         STATISTIC_ERR_MSG(NGMSG_FILE_UNREG);
1244                 }
1245                 return 0;
1246         }
1247
1248         /* Access authority */
1249         if (current_uid != ROOT_UID &&
1250                 buf->st_uid != current_uid) {
1251                 if (mode_flag & DETAIL) {
1252                         PRINT_FILE_NAME(file);
1253                         STATISTIC_ERR_MSG(
1254                                 "File is not current user's file"
1255                                 " or current user is not root");
1256                 }
1257                 return 0;
1258         }
1259
1260         /* Empty file */
1261         if (buf->st_size == 0) {
1262                 if (mode_flag & DETAIL) {
1263                         PRINT_FILE_NAME(file);
1264                         STATISTIC_ERR_MSG("File size is 0");
1265                 }
1266                 return 0;
1267         }
1268
1269         /* Has no blocks */
1270         if (buf->st_blocks == 0) {
1271                 if (mode_flag & DETAIL) {
1272                         PRINT_FILE_NAME(file);
1273                         STATISTIC_ERR_MSG("File has no blocks");
1274                 }
1275                 return 0;
1276         }
1277
1278         fd = open64(file, O_RDONLY);
1279         if (fd < 0) {
1280                 if (mode_flag & DETAIL) {
1281                         PRINT_FILE_NAME(file);
1282                         STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN);
1283                 }
1284                 return 0;
1285         }
1286
1287         /* Get file's physical extents  */
1288         ret = get_file_extents(fd, &physical_list_head);
1289         if (ret < 0) {
1290                 if (mode_flag & DETAIL) {
1291                         PRINT_FILE_NAME(file);
1292                         STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1293                 }
1294                 goto out;
1295         }
1296
1297         /* Get the count of file's continuous physical region */
1298         physical_ext_count = get_physical_count(physical_list_head);
1299
1300         /* Change list from physical to logical */
1301         ret = change_physical_to_logical(&physical_list_head,
1302                                                         &logical_list_head);
1303         if (ret < 0) {
1304                 if (mode_flag & DETAIL) {
1305                         PRINT_FILE_NAME(file);
1306                         STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1307                 }
1308                 goto out;
1309         }
1310
1311         /* Count file fragments before defrag */
1312         now_ext_count = get_logical_count(logical_list_head);
1313
1314         if (current_uid == ROOT_UID) {
1315                 /* Calculate fragment ratio  */
1316                 blk_count =
1317                                 SECTOR_TO_BLOCK(buf->st_blocks, block_size);
1318
1319                 best_ext_count = get_best_count(blk_count);
1320
1321                 ratio = (float)(physical_ext_count - best_ext_count) * 100 /
1322                                                         blk_count;
1323
1324                 extents_before_defrag += now_ext_count;
1325                 extents_after_defrag += best_ext_count;
1326                 files_block_count += blk_count;
1327         }
1328
1329         if (total_count == 1 && regular_count == 1) {
1330                 /* File only */
1331                 if (mode_flag & DETAIL) {
1332                         int count = 0;
1333                         struct fiemap_extent_list *ext_list_tmp =
1334                                                 logical_list_head;
1335
1336                         /* Print extents info */
1337                         do {
1338                                 count++;
1339                                 printf("[ext %d]:\tstart %llu:\tlogical "
1340                                                 "%llu:\tlen %llu\n", count,
1341                                                 ext_list_tmp->data.physical,
1342                                                 ext_list_tmp->data.logical,
1343                                                 ext_list_tmp->data.len);
1344                                 ext_list_tmp = ext_list_tmp->next;
1345                         } while (ext_list_tmp != logical_list_head);
1346
1347                 } else {
1348                         printf("%-40s%10s/%-10s%9s\n",
1349                                         "<File>", "now", "best", "ratio");
1350                         if (current_uid == ROOT_UID) {
1351                                 if (strlen(file) > 40)
1352                                         printf("%s\n%50d/%-10d%8.2f%%\n",
1353                                                 file, now_ext_count,
1354                                                 best_ext_count, ratio);
1355                                 else
1356                                         printf("%-40s%10d/%-10d%8.2f%%\n",
1357                                                 file, now_ext_count,
1358                                                 best_ext_count, ratio);
1359                         } else {
1360                                 if (strlen(file) > 40)
1361                                         printf("%s\n%50d/%-10s%7s\n",
1362                                                         file, now_ext_count,
1363                                                         "-", "-");
1364                                 else
1365                                         printf("%-40s%10d/%-10s%7s\n",
1366                                                         file, now_ext_count,
1367                                                         "-", "-");
1368                         }
1369                 }
1370                 succeed_cnt++;
1371                 goto out;
1372         }
1373
1374         if (mode_flag & DETAIL) {
1375                 /* Print statistic info */
1376                 sprintf(msg_buffer, "[%u/%u]%s",
1377                                 defraged_file_count, total_count, file);
1378                 if (current_uid == ROOT_UID) {
1379                         if (strlen(msg_buffer) > 40)
1380                                 printf("\033[79;0H\033[K%s\n"
1381                                                 "%50d/%-10d%8.2f%%\n",
1382                                                 msg_buffer, now_ext_count,
1383                                                 best_ext_count, ratio);
1384                         else
1385                                 printf("\033[79;0H\033[K%-40s"
1386                                                 "%10d/%-10d%8.2f%%\n",
1387                                                 msg_buffer, now_ext_count,
1388                                                 best_ext_count, ratio);
1389                 } else {
1390                         if (strlen(msg_buffer) > 40)
1391                                 printf("\033[79;0H\033[K%s\n%50d/%-10s%7s\n",
1392                                                 msg_buffer, now_ext_count,
1393                                                         "-", "-");
1394                         else
1395                                 printf("\033[79;0H\033[K%-40s%10d/%-10s%7s\n",
1396                                                 msg_buffer, now_ext_count,
1397                                                         "-", "-");
1398                 }
1399         }
1400
1401         for (i = 0; i < SHOW_FRAG_FILES; i++) {
1402                 if (ratio >= frag_rank[i].ratio) {
1403                         for (j = SHOW_FRAG_FILES - 1; j > i; j--) {
1404                                 memcpy(&frag_rank[j], &frag_rank[j - 1],
1405                                         sizeof(struct frag_statistic_ino));
1406                         }
1407                         memset(&frag_rank[i], 0,
1408                                         sizeof(struct frag_statistic_ino));
1409                         strncpy(frag_rank[i].msg_buffer, file,
1410                                                 strnlen(file, PATH_MAX));
1411                         frag_rank[i].now_count = now_ext_count;
1412                         frag_rank[i].best_count = best_ext_count;
1413                         frag_rank[i].ratio = ratio;
1414                         break;
1415                 }
1416         }
1417
1418         succeed_cnt++;
1419
1420 out:
1421         close(fd);
1422         free_ext(physical_list_head);
1423         free_ext(logical_list_head);
1424         return 0;
1425 }
1426
1427 /*
1428  * print_progress -     Print defrag progress
1429  *
1430  * @file:               file name.
1431  * @start:              logical offset for defrag target file
1432  * @file_size:          defrag target filesize
1433  */
1434 static void print_progress(const char *file, loff_t start, loff_t file_size)
1435 {
1436         int percent = (start * 100) / file_size;
1437         printf("\033[79;0H\033[K[%u/%u]%s:\t%3d%%",
1438                 defraged_file_count, total_count, file, min(percent, 100));
1439         fflush(stdout);
1440
1441         return;
1442 }
1443
1444 /*
1445  * call_defrag() -      Execute the defrag program.
1446  *
1447  * @fd:                 target file descriptor.
1448  * @donor_fd:           donor file descriptor.
1449  * @file:                       target file name.
1450  * @buf:                        pointer of the struct stat64.
1451  * @ext_list_head:      head of the extent list.
1452  */
1453 static int call_defrag(int fd, int donor_fd, const char *file,
1454         const struct stat64 *buf, struct fiemap_extent_list *ext_list_head)
1455 {
1456         loff_t  start = 0;
1457         unsigned int    page_num;
1458         unsigned char   *vec = NULL;
1459         int     defraged_ret = 0;
1460         int     ret;
1461         struct move_extent      move_data;
1462         struct fiemap_extent_list       *ext_list_tmp = NULL;
1463
1464         memset(&move_data, 0, sizeof(struct move_extent));
1465         move_data.donor_fd = donor_fd;
1466
1467         /* Print defrag progress */
1468         print_progress(file, start, buf->st_size);
1469
1470         ext_list_tmp = ext_list_head;
1471         do {
1472                 move_data.orig_start = ext_list_tmp->data.logical;
1473                 /* Logical offset of orig and donor should be same */
1474                 move_data.donor_start = move_data.orig_start;
1475                 move_data.len = ext_list_tmp->data.len;
1476                 move_data.moved_len = 0;
1477
1478                 ret = page_in_core(fd, move_data, &vec, &page_num);
1479                 if (ret < 0) {
1480                         if (mode_flag & DETAIL) {
1481                                 printf("\n");
1482                                 PRINT_ERR_MSG_WITH_ERRNO(
1483                                                 "Failed to get file map");
1484                         } else {
1485                                 printf("\t[ NG ]\n");
1486                         }
1487                         return -1;
1488                 }
1489
1490                 /* EXT4_IOC_MOVE_EXT */
1491                 defraged_ret =
1492                         ioctl(fd, EXT4_IOC_MOVE_EXT, &move_data);
1493
1494                 /* Free pages */
1495                 ret = defrag_fadvise(fd, move_data, vec, page_num);
1496                 if (vec) {
1497                         free(vec);
1498                         vec = NULL;
1499                 }
1500                 if (ret < 0) {
1501                         if (mode_flag & DETAIL) {
1502                                 printf("\n");
1503                                 PRINT_ERR_MSG_WITH_ERRNO(
1504                                         "Failed to free page");
1505                         } else {
1506                                 printf("\t[ NG ]\n");
1507                         }
1508                         return -1;
1509                 }
1510
1511                 if (defraged_ret < 0) {
1512                         if (mode_flag & DETAIL) {
1513                                 printf("\n");
1514                                 PRINT_ERR_MSG_WITH_ERRNO(
1515                                                 "Failed to defrag");
1516                         } else {
1517                                 printf("\t[ NG ]\n");
1518                         }
1519                         return -1;
1520                 }
1521                 /* Adjust logical offset for next ioctl */
1522                 move_data.orig_start += move_data.moved_len;
1523                 move_data.donor_start = move_data.orig_start;
1524
1525                 start = move_data.orig_start * buf->st_blksize;
1526
1527                 /* Print defrag progress */
1528                 print_progress(file, start, buf->st_size);
1529
1530                 /* End of file */
1531                 if (start >= buf->st_size)
1532                         break;
1533
1534                 ext_list_tmp = ext_list_tmp->next;
1535         } while (ext_list_tmp != ext_list_head);
1536
1537         return 0;
1538 }
1539
1540 /*
1541  * file_defrag() -              Check file attributes and call ioctl to defrag.
1542  *
1543  * @file:               the file's name.
1544  * @buf:                the pointer of the struct stat64.
1545  * @flag:               file type.
1546  * @ftwbuf:             the pointer of a struct FTW.
1547  */
1548 static int file_defrag(const char *file, const struct stat64 *buf,
1549                         int flag EXT2FS_ATTR((unused)),
1550                         struct FTW *ftwbuf EXT2FS_ATTR((unused)))
1551 {
1552         int     fd;
1553         int     donor_fd = -1;
1554         int     ret;
1555         int     best;
1556         int     file_frags_start, file_frags_end;
1557         int     orig_physical_cnt, donor_physical_cnt = 0;
1558         char    tmp_inode_name[PATH_MAX + 8];
1559         struct fiemap_extent_list       *orig_list_physical = NULL;
1560         struct fiemap_extent_list       *orig_list_logical = NULL;
1561         struct fiemap_extent_list       *donor_list_physical = NULL;
1562         struct fiemap_extent_list       *donor_list_logical = NULL;
1563         struct fiemap_extent_group      *orig_group_head = NULL;
1564         struct fiemap_extent_group      *orig_group_tmp = NULL;
1565
1566         defraged_file_count++;
1567
1568         if (mode_flag & DETAIL) {
1569                 printf("[%u/%u]", defraged_file_count, total_count);
1570                 fflush(stdout);
1571         }
1572
1573         if (lost_found_dir[0] != '\0' &&
1574             !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) {
1575                 if (mode_flag & DETAIL) {
1576                         PRINT_FILE_NAME(file);
1577                         IN_FTW_PRINT_ERR_MSG(NGMSG_LOST_FOUND);
1578                 }
1579                 return 0;
1580         }
1581
1582         if (!S_ISREG(buf->st_mode)) {
1583                 if (mode_flag & DETAIL) {
1584                         PRINT_FILE_NAME(file);
1585                         IN_FTW_PRINT_ERR_MSG(NGMSG_FILE_UNREG);
1586                 }
1587                 return 0;
1588         }
1589
1590         /* Empty file */
1591         if (buf->st_size == 0) {
1592                 if (mode_flag & DETAIL) {
1593                         PRINT_FILE_NAME(file);
1594                         IN_FTW_PRINT_ERR_MSG("File size is 0");
1595                 }
1596                 return 0;
1597         }
1598
1599         /* Has no blocks */
1600         if (buf->st_blocks == 0) {
1601                 if (mode_flag & DETAIL) {
1602                         PRINT_FILE_NAME(file);
1603                         STATISTIC_ERR_MSG("File has no blocks");
1604                 }
1605                 return 0;
1606         }
1607
1608         fd = open64(file, O_RDONLY);
1609         if (fd < 0) {
1610                 if (mode_flag & DETAIL) {
1611                         PRINT_FILE_NAME(file);
1612                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN);
1613                 }
1614                 return 0;
1615         }
1616
1617         /* Get file's extents */
1618         ret = get_file_extents(fd, &orig_list_physical);
1619         if (ret < 0) {
1620                 if (mode_flag & DETAIL) {
1621                         PRINT_FILE_NAME(file);
1622                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1623                 }
1624                 goto out;
1625         }
1626
1627         /* Get the count of file's continuous physical region */
1628         orig_physical_cnt = get_physical_count(orig_list_physical);
1629
1630         /* Change list from physical to logical */
1631         ret = change_physical_to_logical(&orig_list_physical,
1632                                                         &orig_list_logical);
1633         if (ret < 0) {
1634                 if (mode_flag & DETAIL) {
1635                         PRINT_FILE_NAME(file);
1636                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1637                 }
1638                 goto out;
1639         }
1640
1641         /* Count file fragments before defrag */
1642         file_frags_start = get_logical_count(orig_list_logical);
1643
1644         if (file_check(fd, buf, file, file_frags_start) < 0)
1645                 goto out;
1646
1647         if (fsync(fd) < 0) {
1648                 if (mode_flag & DETAIL) {
1649                         PRINT_FILE_NAME(file);
1650                         PRINT_ERR_MSG_WITH_ERRNO("Failed to sync(fsync)");
1651                 }
1652                 goto out;
1653         }
1654
1655         if (current_uid == ROOT_UID)
1656                 best =
1657                 get_best_count(SECTOR_TO_BLOCK(buf->st_blocks, block_size));
1658         else
1659                 best = 1;
1660
1661         if (file_frags_start <= best)
1662                 goto check_improvement;
1663
1664         /* Combine extents to group */
1665         ret = join_extents(orig_list_logical, &orig_group_head);
1666         if (ret < 0) {
1667                 if (mode_flag & DETAIL) {
1668                         PRINT_FILE_NAME(file);
1669                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1670                 }
1671                 goto out;
1672         }
1673
1674         /* Create donor inode */
1675         memset(tmp_inode_name, 0, PATH_MAX + 8);
1676         sprintf(tmp_inode_name, "%.*s.defrag",
1677                                 (int)strnlen(file, PATH_MAX), file);
1678         donor_fd = open64(tmp_inode_name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
1679         if (donor_fd < 0) {
1680                 if (mode_flag & DETAIL) {
1681                         PRINT_FILE_NAME(file);
1682                         if (errno == EEXIST)
1683                                 PRINT_ERR_MSG_WITH_ERRNO(
1684                                 "File is being defraged by other program");
1685                         else
1686                                 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN);
1687                 }
1688                 goto out;
1689         }
1690
1691         /* Unlink donor inode */
1692         ret = unlink(tmp_inode_name);
1693         if (ret < 0) {
1694                 if (mode_flag & DETAIL) {
1695                         PRINT_FILE_NAME(file);
1696                         PRINT_ERR_MSG_WITH_ERRNO("Failed to unlink");
1697                 }
1698                 goto out;
1699         }
1700
1701         /* Allocate space for donor inode */
1702         orig_group_tmp = orig_group_head;
1703         do {
1704                 ret = fallocate(donor_fd, 0,
1705                   (loff_t)orig_group_tmp->start->data.logical * block_size,
1706                   (loff_t)orig_group_tmp->len * block_size);
1707                 if (ret < 0) {
1708                         if (mode_flag & DETAIL) {
1709                                 PRINT_FILE_NAME(file);
1710                                 PRINT_ERR_MSG_WITH_ERRNO("Failed to fallocate");
1711                         }
1712                         goto out;
1713                 }
1714
1715                 orig_group_tmp = orig_group_tmp->next;
1716         } while (orig_group_tmp != orig_group_head);
1717
1718         /* Get donor inode's extents */
1719         ret = get_file_extents(donor_fd, &donor_list_physical);
1720         if (ret < 0) {
1721                 if (mode_flag & DETAIL) {
1722                         PRINT_FILE_NAME(file);
1723                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1724                 }
1725                 goto out;
1726         }
1727
1728         /* Calcuate donor inode's continuous physical region */
1729         donor_physical_cnt = get_physical_count(donor_list_physical);
1730
1731         /* Change donor extent list from physical to logical */
1732         ret = change_physical_to_logical(&donor_list_physical,
1733                                                         &donor_list_logical);
1734         if (ret < 0) {
1735                 if (mode_flag & DETAIL) {
1736                         PRINT_FILE_NAME(file);
1737                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT);
1738                 }
1739                 goto out;
1740         }
1741
1742 check_improvement:
1743         if (mode_flag & DETAIL) {
1744                 if (file_frags_start != 1)
1745                         frag_files_before_defrag++;
1746
1747                 extents_before_defrag += file_frags_start;
1748         }
1749
1750         if (file_frags_start <= best ||
1751                         orig_physical_cnt <= donor_physical_cnt) {
1752                 printf("\033[79;0H\033[K[%u/%u]%s:\t%3d%%",
1753                         defraged_file_count, total_count, file, 100);
1754                 if (mode_flag & DETAIL)
1755                         printf("  extents: %d -> %d",
1756                                 file_frags_start, file_frags_start);
1757
1758                 printf("\t[ OK ]\n");
1759                 succeed_cnt++;
1760
1761                 if (file_frags_start != 1)
1762                         frag_files_after_defrag++;
1763
1764                 extents_after_defrag += file_frags_start;
1765                 goto out;
1766         }
1767
1768         /* Defrag the file */
1769         ret = call_defrag(fd, donor_fd, file, buf, donor_list_logical);
1770
1771         /* Count file fragments after defrag and print extents info */
1772         if (mode_flag & DETAIL) {
1773                 file_frags_end = file_frag_count(fd);
1774                 if (file_frags_end < 0) {
1775                         printf("\n");
1776                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_INFO);
1777                         goto out;
1778                 }
1779
1780                 if (file_frags_end != 1)
1781                         frag_files_after_defrag++;
1782
1783                 extents_after_defrag += file_frags_end;
1784
1785                 if (ret < 0)
1786                         goto out;
1787
1788                 printf("  extents: %d -> %d",
1789                         file_frags_start, file_frags_end);
1790                 fflush(stdout);
1791         }
1792
1793         if (ret < 0)
1794                 goto out;
1795
1796         printf("\t[ OK ]\n");
1797         fflush(stdout);
1798         succeed_cnt++;
1799
1800 out:
1801         close(fd);
1802         if (donor_fd != -1)
1803                 close(donor_fd);
1804         free_ext(orig_list_physical);
1805         free_ext(orig_list_logical);
1806         free_ext(donor_list_physical);
1807         free_exts_group(orig_group_head);
1808         return 0;
1809 }
1810
1811 /*
1812  * main() -             Ext4 online defrag.
1813  *
1814  * @argc:               the number of parameter.
1815  * @argv[]:             the pointer array of parameter.
1816  */
1817 int main(int argc, char *argv[])
1818 {
1819         int     opt;
1820         int     i, j;
1821         int     flags = FTW_PHYS | FTW_MOUNT;
1822         int     arg_type = -1;
1823         int     success_flag = 0;
1824         char    dir_name[PATH_MAX + 1];
1825         struct stat64   buf;
1826         struct ext4_super_block sb;
1827
1828         /* Parse arguments */
1829         if (argc == 1)
1830                 goto out;
1831
1832         while ((opt = getopt(argc, argv, "vc")) != EOF) {
1833                 switch (opt) {
1834                 case 'v':
1835                         mode_flag |= DETAIL;
1836                         break;
1837                 case 'c':
1838                         mode_flag |= STATISTIC;
1839                         break;
1840                 default:
1841                         goto out;
1842                 }
1843         }
1844
1845         if (argc == optind)
1846                 goto out;
1847
1848         current_uid = getuid();
1849
1850         /* Main process */
1851         for (i = optind; i < argc; i++) {
1852                 succeed_cnt = 0;
1853                 regular_count = 0;
1854                 total_count = 0;
1855                 frag_files_before_defrag = 0;
1856                 frag_files_after_defrag = 0;
1857                 extents_before_defrag = 0;
1858                 extents_after_defrag = 0;
1859                 defraged_file_count = 0;
1860                 files_block_count = 0;
1861                 blocks_per_group = 0;
1862                 feature_incompat = 0;
1863                 log_groups_per_flex = 0;
1864
1865                 memset(dir_name, 0, PATH_MAX + 1);
1866                 memset(lost_found_dir, 0, PATH_MAX + 1);
1867                 memset(frag_rank, 0,
1868                         sizeof(struct frag_statistic_ino) * SHOW_FRAG_FILES);
1869
1870                 if ((mode_flag & STATISTIC) && i > optind)
1871                         printf("\n");
1872
1873 #if BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN
1874                 PRINT_ERR_MSG("Endian's type is not big/little endian");
1875                 PRINT_FILE_NAME(argv[i]);
1876                 continue;
1877 #endif
1878
1879                 if (lstat64(argv[i], &buf) < 0) {
1880                         perror(NGMSG_FILE_INFO);
1881                         PRINT_FILE_NAME(argv[i]);
1882                         continue;
1883                 }
1884
1885                 if (S_ISBLK(buf.st_mode)) {
1886                         /* Block device */
1887                         if (get_mount_point(argv[i], dir_name, PATH_MAX) < 0)
1888                                 continue;
1889                         if (lstat64(dir_name, &buf) < 0) {
1890                                 perror(NGMSG_FILE_INFO);
1891                                 PRINT_FILE_NAME(argv[i]);
1892                                 continue;
1893                         }
1894                         arg_type = DEVNAME;
1895                         if (!(mode_flag & STATISTIC))
1896                                 printf("ext4 defragmentation for device(%s)\n",
1897                                         argv[i]);
1898                 } else if (S_ISDIR(buf.st_mode)) {
1899                         /* Directory */
1900                         if (access(argv[i], R_OK) < 0) {
1901                                 perror(argv[i]);
1902                                 continue;
1903                         }
1904                         arg_type = DIRNAME;
1905                         strncpy(dir_name, argv[i], strnlen(argv[i], PATH_MAX));
1906                 } else if (S_ISREG(buf.st_mode)) {
1907                         /* Regular file */
1908                         arg_type = FILENAME;
1909                 } else {
1910                         /* Irregular file */
1911                         PRINT_ERR_MSG(NGMSG_FILE_UNREG);
1912                         PRINT_FILE_NAME(argv[i]);
1913                         continue;
1914                 }
1915
1916                 /* Set blocksize */
1917                 block_size = buf.st_blksize;
1918
1919                 /* For device case,
1920                  * filesystem type checked in get_mount_point()
1921                  */
1922                 if (arg_type == FILENAME || arg_type == DIRNAME) {
1923                         if (is_ext4(argv[i]) < 0)
1924                                 continue;
1925                         if (realpath(argv[i], dir_name) == NULL) {
1926                                 perror("Couldn't get full path");
1927                                 PRINT_FILE_NAME(argv[i]);
1928                                 continue;
1929                         }
1930                 }
1931
1932                 if (current_uid == ROOT_UID) {
1933                         /* Get super block info */
1934                         memset(&sb, 0, sizeof(struct ext4_super_block));
1935                         if (get_superblock_info(dir_name, &sb) < 0) {
1936                                 if (mode_flag & DETAIL) {
1937                                         perror("Can't get super block info");
1938                                         PRINT_FILE_NAME(argv[i]);
1939                                 }
1940                                 continue;
1941                         }
1942
1943                         blocks_per_group = ext2fs_swab32(sb.s_blocks_per_group);
1944                         feature_incompat = ext2fs_swab32(sb.s_feature_incompat);
1945                         log_groups_per_flex = sb.s_log_groups_per_flex;
1946                 }
1947
1948                 switch (arg_type) {
1949                 case DIRNAME:
1950                         if (!(mode_flag & STATISTIC))
1951                                 printf("ext4 defragmentation "
1952                                         "for directory(%s)\n", argv[i]);
1953
1954                         int mount_dir_len = 0;
1955                         mount_dir_len = strnlen(lost_found_dir, PATH_MAX);
1956
1957                         strncat(lost_found_dir, "/lost+found",
1958                                 PATH_MAX - strnlen(lost_found_dir, PATH_MAX));
1959
1960                         /* Not the case("e4defrag  mount_piont_dir") */
1961                         if (dir_name[mount_dir_len] != '\0') {
1962                                 /*
1963                                  * "e4defrag mount_piont_dir/lost+found"
1964                                  * or "e4defrag mount_piont_dir/lost+found/"
1965                                  */
1966                                 if (strncmp(lost_found_dir, dir_name,
1967                                             strnlen(lost_found_dir,
1968                                                     PATH_MAX)) == 0 &&
1969                                     (dir_name[strnlen(lost_found_dir,
1970                                                       PATH_MAX)] == '\0' ||
1971                                      dir_name[strnlen(lost_found_dir,
1972                                                       PATH_MAX)] == '/')) {
1973                                         PRINT_ERR_MSG(NGMSG_LOST_FOUND);
1974                                         PRINT_FILE_NAME(argv[i]);
1975                                         continue;
1976                                 }
1977
1978                                 /* "e4defrag mount_piont_dir/else_dir" */
1979                                 memset(lost_found_dir, 0, PATH_MAX + 1);
1980                         }
1981                 case DEVNAME:
1982                         if (arg_type == DEVNAME) {
1983                                 strncpy(lost_found_dir, dir_name,
1984                                         strnlen(dir_name, PATH_MAX));
1985                                 strncat(lost_found_dir, "/lost+found/",
1986                                         PATH_MAX - strnlen(lost_found_dir,
1987                                                            PATH_MAX));
1988                         }
1989
1990                         nftw64(dir_name, calc_entry_counts, FTW_OPEN_FD, flags);
1991
1992                         if (mode_flag & STATISTIC) {
1993                                 if (mode_flag & DETAIL)
1994                                         printf("%-40s%10s/%-10s%9s\n",
1995                                         "<File>", "now", "best", "ratio");
1996
1997                                 if (!(mode_flag & DETAIL) &&
1998                                                 current_uid != ROOT_UID) {
1999                                         printf(" Done.\n");
2000                                         continue;
2001                                 }
2002
2003                                 nftw64(dir_name, file_statistic,
2004                                                         FTW_OPEN_FD, flags);
2005
2006                                 if (succeed_cnt != 0 &&
2007                                         current_uid == ROOT_UID) {
2008                                         if (mode_flag & DETAIL)
2009                                                 printf("\n");
2010                                         printf("%-40s%10s/%-10s%9s\n",
2011                                                 "<Fragmented files>", "now",
2012                                                 "best", "ratio");
2013                                         for (j = 0; j < SHOW_FRAG_FILES; j++) {
2014                                                 if (strlen(frag_rank[j].
2015                                                         msg_buffer) > 37) {
2016                                                         printf("%d. %s\n%50d/"
2017                                                         "%-10d%8.2f%%\n", j + 1,
2018                                                         frag_rank[j].msg_buffer,
2019                                                         frag_rank[j].now_count,
2020                                                         frag_rank[j].best_count,
2021                                                         frag_rank[j].ratio);
2022                                                 } else if (strlen(frag_rank[j].
2023                                                         msg_buffer) > 0) {
2024                                                         printf("%d. %-37s%10d/"
2025                                                         "%-10d%8.2f%%\n", j + 1,
2026                                                         frag_rank[j].msg_buffer,
2027                                                         frag_rank[j].now_count,
2028                                                         frag_rank[j].best_count,
2029                                                         frag_rank[j].ratio);
2030                                                 } else
2031                                                         break;
2032                                         }
2033                                 }
2034                                 break;
2035                         }
2036                         /* File tree walk */
2037                         nftw64(dir_name, file_defrag, FTW_OPEN_FD, flags);
2038                         printf("\n\tSuccess:\t\t\t[ %u/%u ]\n", succeed_cnt,
2039                                 total_count);
2040                         printf("\tFailure:\t\t\t[ %u/%u ]\n",
2041                                 total_count - succeed_cnt, total_count);
2042                         if (mode_flag & DETAIL) {
2043                                 printf("\tTotal extents:\t\t\t%4d->%d\n",
2044                                         extents_before_defrag,
2045                                         extents_after_defrag);
2046                                 printf("\tFragmented percentage:\t\t"
2047                                         "%3llu%%->%llu%%\n",
2048                                         !regular_count ? 0 :
2049                                         ((unsigned long long)
2050                                         frag_files_before_defrag * 100) /
2051                                         regular_count,
2052                                         !regular_count ? 0 :
2053                                         ((unsigned long long)
2054                                         frag_files_after_defrag * 100) /
2055                                         regular_count);
2056                         }
2057                         break;
2058                 case FILENAME:
2059                         total_count = 1;
2060                         regular_count = 1;
2061                         strncat(lost_found_dir, "/lost+found/",
2062                                 PATH_MAX - strnlen(lost_found_dir,
2063                                                    PATH_MAX));
2064                         if (strncmp(lost_found_dir, dir_name,
2065                                     strnlen(lost_found_dir,
2066                                             PATH_MAX)) == 0) {
2067                                 PRINT_ERR_MSG(NGMSG_LOST_FOUND);
2068                                 PRINT_FILE_NAME(argv[i]);
2069                                 continue;
2070                         }
2071
2072                         if (mode_flag & STATISTIC) {
2073                                 file_statistic(argv[i], &buf, FTW_F, NULL);
2074                                 break;
2075                         } else
2076                                 printf("ext4 defragmentation for %s\n",
2077                                                                  argv[i]);
2078                         /* Defrag single file process */
2079                         file_defrag(argv[i], &buf, FTW_F, NULL);
2080                         if (succeed_cnt != 0)
2081                                 printf(" Success:\t\t\t[1/1]\n");
2082                         else
2083                                 printf(" Success:\t\t\t[0/1]\n");
2084
2085                         break;
2086                 }
2087
2088                 if (succeed_cnt != 0)
2089                         success_flag = 1;
2090                 if (mode_flag & STATISTIC) {
2091                         if (current_uid != ROOT_UID) {
2092                                 printf(" Done.\n");
2093                                 continue;
2094                         }
2095
2096                         if (!succeed_cnt) {
2097                                 if (mode_flag & DETAIL)
2098                                         printf("\n");
2099
2100                                 if (arg_type == DEVNAME)
2101                                         printf(" In this device(%s), "
2102                                         "none can be defragmented.\n", argv[i]);
2103                                 else if (arg_type == DIRNAME)
2104                                         printf(" In this directory(%s), "
2105                                         "none can be defragmented.\n", argv[i]);
2106                                 else
2107                                         printf(" This file(%s) "
2108                                         "can't be defragmented.\n", argv[i]);
2109                         } else {
2110                                 float files_ratio = 0.0;
2111                                 float score = 0.0;
2112                                 files_ratio = (float)(extents_before_defrag -
2113                                                 extents_after_defrag) *
2114                                                 100 / files_block_count;
2115                                 score = CALC_SCORE(files_ratio);
2116                                 printf("\n Total/best extents\t\t\t\t%d/%d\n"
2117                                         " Fragmentation ratio\t\t\t\t%.2f%%\n"
2118                                         " Fragmentation score\t\t\t\t%.2f\n",
2119                                                 extents_before_defrag,
2120                                                 extents_after_defrag,
2121                                                 files_ratio, score);
2122                                 printf(" [0-30 no problem:"
2123                                         " 31-55 a little bit fragmented:"
2124                                         " 55- needs defrag]\n");
2125
2126                                 if (arg_type == DEVNAME)
2127                                         printf(" This device(%s) ", argv[i]);
2128                                 else if (arg_type == DIRNAME)
2129                                         printf(" This directory(%s) ", argv[i]);
2130                                 else
2131                                         printf(" This file(%s) ", argv[i]);
2132
2133                                 if (score > BOUND_SCORE)
2134                                         printf("needs defragmentation.\n");
2135                                 else
2136                                         printf("does not need "
2137                                                         "defragmentation.\n");
2138                         }
2139                         printf(" Done.\n");
2140                 }
2141
2142         }
2143
2144         if (success_flag)
2145                 return 0;
2146
2147         exit(1);
2148
2149 out:
2150         printf(MSG_USAGE);
2151         exit(1);
2152 }
2153