Add patch SR-ext4-resize-mark-new-group-EXT_BG_INODE_ZEROED.patch
[ext4-patch-queue/an.git] / defrag-09-online-defrag-command
blob86ee65817164c47c9b47a21ade89b04f0b2f8ee6
1 ext4: online defrag -- Online defrag command
3 From: Akira Fujita <a-fujita@rs.jp.nec.com>
5 - The defrag command. Usage is as follows:
6   - Put the multiple files closer together.
7     # e4defrag -r directory-name
8   - Defrag for free space fragmentation.
9     # e4defrag -f file-name
10   - Defrag for a single file.
11     # e4defrag file-name
12   - Defrag for all files on ext4.
13     # e4defrag device-name
15 Signed-off-by: Akira Fujita <a-fujita@rs.jp.nec.com>
16 Signed-off-by: Takashi Sato <t-sato@yk.jp.nec.com>
17 ---
19  * e4defrag.c - ext4 filesystem defragmenter
20  *
21  * Copyright (C) 2008 NEC Software Tohoku, Ltd.
22  *
23  * Author: Akira Fujita <a-fujita@rs.jp.nec.com>
24  *         Takashi Sato <t-sato@yk.jp.nec.com>
25  */
27 #ifndef _LARGEFILE_SOURCE
28 #define _LARGEFILE_SOURCE
29 #endif
31 #ifndef _LARGEFILE64_SOURCE
32 #define _LARGEFILE64_SOURCE
33 #endif
35 #define _XOPEN_SOURCE   500
36 #define _GNU_SOURCE
37 #include <ftw.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include <dirent.h>
43 #include <limits.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <sys/statfs.h>
48 #include <sys/vfs.h>
49 #include <sys/ioctl.h>
50 #include <mntent.h>
51 #include <linux/fs.h>
52 #include <ctype.h>
53 #include <sys/syscall.h>
54 #include <sys/mman.h>
55 #include <ext2fs/ext2_types.h>
56 #include <endian.h>
58 /* Ioctl command */
59 #define EXT4_IOC_DEFRAG         _IOW('f', 15, struct ext4_ext_defrag_data)
60 #define EXT4_IOC_SUPER_INFO     _IOR('f', 16, struct ext4_super_block)
61 #define EXT4_IOC_FREE_BLOCKS_INFO _IOWR('f', 17, struct ext4_extents_info)
62 #define EXT4_IOC_FIEMAP_INO     _IOWR('f', 18, struct fiemap_ino)
63 #define EXT4_IOC_MOVE_VICTIM    _IOW('f', 19, struct ext4_extents_info)
64 #define FS_IOC_FIEMAP           _IOWR('f', 11, struct fiemap)
66 /* Macro functions */
67 #define PRINT_ERR_MSG(msg)      fprintf(stderr, "%s\n", (msg));
68 #define IN_FTW_PRINT_ERR_MSG(msg)       \
69         fprintf(stderr, "\t%s\t\t[ NG ]\n", (msg));
70 #define PRINT_FILE_NAME(file)   fprintf(stderr, " \"%s\"\n", (file));
71 #define PRINT_ERR_MSG_WITH_ERRNO(msg)   \
72         fprintf(stderr, "\t%s:%s\t[ NG ]\n", (msg), strerror(errno));
73 #define min(x, y) (((x) > (y)) ? (y) : (x))
74 /* Wrap up the free function */
75 #define FREE(tmp)                               \
76         do {                                    \
77                 if (tmp)                        \
78                         free(tmp);              \
79         } while (0)                             \
80 /* Insert list2 after list1 */
81 #define insert(list1, list2)                    \
82         do {                                    \
83                 list2->next = list1->next;      \
84                 list1->next->prev = list2;      \
85                 list2->prev = list1;            \
86                 list1->next = list2;            \
87         } while (0)
89 #ifndef __NR_fadvise64
90 #define __NR_fadvise64          250
91 #endif
93 #ifndef __NR_sync_file_range
94 #define __NR_sync_file_range    314
95 #endif
97 #ifndef POSIX_FADV_DONTNEED
98 #if defined(__s390x__)
99 #define POSIX_FADV_DONTNEED     6 /* Don't need these pages */
100 #else
101 #define POSIX_FADV_DONTNEED     4 /* Don't need these pages */
102 #endif
103 #endif
105 #ifndef SYNC_FILE_RANGE_WAIT_BEFORE
106 #define SYNC_FILE_RANGE_WAIT_BEFORE     1
107 #endif
108 #ifndef SYNC_FILE_RANGE_WRITE
109 #define SYNC_FILE_RANGE_WRITE           2
110 #endif
111 #ifndef SYNC_FILE_RANGE_WAIT_AFTER
112 #define SYNC_FILE_RANGE_WAIT_AFTER      4
113 #endif
115 #define DEVNAME                 0
116 #define DIRNAME                 1
117 #define FILENAME                2
119 #define RETURN_OK               0
120 #define RETURN_NG               -1
121 #define FTW_CONT                0
122 #define FTW_OPEN_FD             2000
124 #define FS_EXT4                 "ext4"
125 #define ROOT_UID                0
126 #define CHECK_FRAG_COUNT        1
128 /* Extent status which are used in extent_t */
129 #define EXT4_EXT_USE            0
130 #define EXT4_EXT_FREE           1
132 /* The maximum number of extents for exchanging between
133  * kernel-space and user-space per ioctl
134  */
135 #define DEFRAG_MAX_ENT  32
137 /* The phase of force defrag mode */
138 #define DEFRAG_FORCE_TRY        1
139 #define DEFRAG_FORCE_GATHER     3
141 /* Magic number for ext4 */
142 #define EXT4_SUPER_MAGIC        0xEF53
144 /* Defrag size(64MB) per ioctl(not including force mode) */
145 #define DEFRAG_SIZE             ((unsigned long)1 << 26)
147 /* Force defrag mode: Max file size in bytes (128MB) */
148 #define MAX_FILE_SIZE           ((unsigned long)1 << 27)
151 /* The following four macros are used for ioctl FS_IOC_FIEMAP
152  * FIEMAP_FLAG_SYNC:    sync file data before map.
153  * FIEMAP_EXTENT_LAST:  last extent in file.
154  * FIEMAP_MAX_OFFSET:   max file offset.
155  * EXTENT_MAX_COUNT:    the maximum number of extents for exchanging between
156                         kernel-space and user-space per ioctl
157  */
158 #define FIEMAP_FLAG_SYNC        0x00000001
159 #define FIEMAP_EXTENT_LAST      0x00000001
160 #define FIEMAP_MAX_OFFSET       (~0ULL)
161 #define EXTENT_MAX_COUNT        512
163 /* The following macros are error message */
164 #define MSG_USAGE               \
165 "Usage : e4defrag [-v] file...| directory...| device...\n\
166       : e4defrag -f file [blocknr] \n\
167       : e4defrag -r directory... | device... \n"
169 #define MSG_R_OPTION            " with regional block allocation mode.\n"
170 #define NGMSG_MTAB              "Can not access /etc/mtab"
171 #define NGMSG_UNMOUNT           "FS is not mounted"
172 #define NGMSG_EXT4              "FS is not ext4 File System"
173 #define NGMSG_FS_INFO           "Get FSInfo fail"
174 #define NGMSG_FILE_INFO         "Get FileInfo fail"
175 #define NGMSG_FILE_OPEN         "Open fail"
176 #define NGMSG_FILE_SYNC         "Sync(fsync) fail"
177 #define NGMSG_FILE_DEFRAG       "Defrag fail"
178 #define NGMSG_FILE_BLOCKSIZE    "Can't get blocksize"
179 #define NGMSG_FILE_SUPER        "Can't get super block info"
180 #define NGMSG_FILE_UNREG        "File is not regular file"
181 #define NGMSG_VICTIM_UID        "Victim file is not current user's file"
183 #define NGMSG_FILE_LARGE        \
184         "Defrag size is larger than FileSystem's free space"
186 #define NGMSG_FILE_PRIORITY     \
187 "File is not current user's file or current user is not root"
189 #define NGMSG_FILE_LOCK         "File is locked"
190 #define NGMSG_FILE_BLANK        "File size is 0"
191 #define NGMSG_GET_LCKINFO       "Get LockInfo fail"
192 #define NGMSG_TYPE              \
193         "Can not process %s in regional mode.\n"
194 #define NGMSG_LOST_FOUND        "Can not process \"lost+found\""
195 #define NGMSG_REALPATH          "Can not get full path"
196 #define NGMSG_FILE_MAP          "Get file map fail"
197 #define NGMSG_FILE_DROP_BUFFER  "Free page fail"
198 #define NGMSG_FADVISE_SYSCALL   "\tFadvise fail"
199 #define NGMSG_FORCE_DEFRAG_SIZE \
200 "Cannot specify a file larger than %luMB in force defrag mode"
201 #define NGMSG_ENDIAN            "Endian type is not big/little endian"
203 /* Data type for filesystem-wide blocks number */
204 typedef unsigned long long ext4_fsblk_t;
206 /* Data type for file logical block number */
207 typedef unsigned int ext4_lblk_t;
209 /* Data type for block offset of block group */
210 typedef int ext4_grpblk_t;
212 struct ext4_extent_data {
213         ext4_lblk_t  block;             /* start logical block number */
214         ext4_fsblk_t start;             /* start physical block number */
215         int len;                        /* blocks count */
218 /* Used for defrag */
219 struct ext4_ext_defrag_data {
220         ext4_lblk_t start_offset;       /* start offset to defrag in blocks */
221         ext4_lblk_t defrag_size;        /* size of defrag in blocks */
222         ext4_fsblk_t goal;              /* block offset for allocation */
223         int flag;                       /* free space mode flag */
224         struct ext4_extent_data ext;
227 typedef __u16 __le16;
228 typedef __u32 __le32;
229 typedef __u64 __le64;
232  * Structure of the super block
233  */
234 struct ext4_super_block {
235 /*00*/  __le32  s_inodes_count;         /* Inodes count */
236         __le32  s_blocks_count_lo;      /* Blocks count */
237         __le32  s_r_blocks_count_lo;    /* Reserved blocks count */
238         __le32  s_free_blocks_count_lo; /* Free blocks count */
239 /*10*/  __le32  s_free_inodes_count;    /* Free inodes count */
240         __le32  s_first_data_block;     /* First Data Block */
241         __le32  s_log_block_size;       /* Block size */
242         __le32  s_obso_log_frag_size;   /* Obsoleted fragment size */
243 /*20*/  __le32  s_blocks_per_group;     /* # Blocks per group */
244         __le32  s_obso_frags_per_group; /* Obsoleted fragments per group */
245         __le32  s_inodes_per_group;     /* # Inodes per group */
246         __le32  s_mtime;                /* Mount time */
247 /*30*/  __le32  s_wtime;                /* Write time */
248         __le16  s_mnt_count;            /* Mount count */
249         __le16  s_max_mnt_count;        /* Maximal mount count */
250         __le16  s_magic;                /* Magic signature */
251         __le16  s_state;                /* File system state */
252         __le16  s_errors;               /* Behaviour when detecting errors */
253         __le16  s_minor_rev_level;      /* minor revision level */
254 /*40*/  __le32  s_lastcheck;            /* time of last check */
255         __le32  s_checkinterval;        /* max. time between checks */
256         __le32  s_creator_os;           /* OS */
257         __le32  s_rev_level;            /* Revision level */
258 /*50*/  __le16  s_def_resuid;           /* Default uid for reserved blocks */
259         __le16  s_def_resgid;           /* Default gid for reserved blocks */
260         /*
261          * These fields are for EXT4_DYNAMIC_REV superblocks only.
262          *
263          * Note: the difference between the compatible feature set and
264          * the incompatible feature set is that if there is a bit set
265          * in the incompatible feature set that the kernel doesn't
266          * know about, it should refuse to mount the filesystem.
267          *
268          * e2fsck's requirements are more strict; if it doesn't know
269          * about a feature in either the compatible or incompatible
270          * feature set, it must abort and not try to meddle with
271          * things it doesn't understand...
272          */
273         __le32  s_first_ino;            /* First non-reserved inode */
274         __le16  s_inode_size;           /* size of inode structure */
275         __le16  s_block_group_nr;       /* block group # of this superblock */
276         __le32  s_feature_compat;       /* compatible feature set */
277 /*60*/  __le32  s_feature_incompat;     /* incompatible feature set */
278         __le32  s_feature_ro_compat;    /* readonly-compatible feature set */
279 /*68*/  __u8    s_uuid[16];             /* 128-bit uuid for volume */
280 /*78*/  char    s_volume_name[16];      /* volume name */
281 /*88*/  char    s_last_mounted[64];     /* directory where last mounted */
282 /*C8*/  __le32  s_algorithm_usage_bitmap; /* For compression */
283         /*
284          * Performance hints.  Directory preallocation should only
285          * happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on.
286          */
287         __u8    s_prealloc_blocks;      /* Nr of blocks to try to preallocate*/
288         __u8    s_prealloc_dir_blocks;  /* Nr to preallocate for dirs */
289         __le16  s_reserved_gdt_blocks;  /* Per group desc for online growth */
290         /*
291          * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set.
292          */
293 /*D0*/  __u8    s_journal_uuid[16];     /* uuid of journal superblock */
294 /*E0*/  __le32  s_journal_inum;         /* inode number of journal file */
295         __le32  s_journal_dev;          /* device number of journal file */
296         __le32  s_last_orphan;          /* start of list of inodes to delete */
297         __le32  s_hash_seed[4];         /* HTREE hash seed */
298         __u8    s_def_hash_version;     /* Default hash version to use */
299         __u8    s_reserved_char_pad;
300         __le16  s_desc_size;            /* size of group descriptor */
301 /*100*/ __le32  s_default_mount_opts;
302         __le32  s_first_meta_bg;        /* First metablock block group */
303         __le32  s_mkfs_time;            /* When the filesystem was created */
304         __le32  s_jnl_blocks[17];       /* Backup of the journal inode */
305         /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
306 /*150*/ __le32  s_blocks_count_hi;      /* Blocks count */
307         __le32  s_r_blocks_count_hi;    /* Reserved blocks count */
308         __le32  s_free_blocks_count_hi; /* Free blocks count */
309         __le16  s_min_extra_isize;      /* All inodes have at least # bytes */
310         __le16  s_want_extra_isize;     /* New inodes should reserve # bytes */
311         __le32  s_flags;                /* Miscellaneous flags */
312         __le16  s_raid_stride;          /* RAID stride */
313         __le16  s_mmp_interval;         /* # seconds to wait in MMP checking */
314         __le64  s_mmp_block;            /* Block for multi-mount protection */
315         __le32  s_raid_stripe_width;    /* blocks on all data disks (N*stride)*/
316         __u8    s_log_groups_per_flex;  /* FLEX_BG group size */
317         __u8    s_reserved_char_pad2;
318         __le16  s_reserved_pad;
319         __u32   s_reserved[162];        /* Padding to the end of the block */
322 struct ext4_extents_info {
323         unsigned long long ino;         /* inode number */
324         int max_entries;                /* maximum extents count */
325         int entries;                    /* extent number/count */
326         ext4_lblk_t  f_offset;          /* file offset */
327         ext4_grpblk_t g_offset;         /* group offset */
328         ext4_fsblk_t goal;
329         struct ext4_extent_data ext[DEFRAG_MAX_ENT];
332 typedef struct extent {
333         struct extent *prev;
334         unsigned long tag;              /* extent status */
335         unsigned long ino;              /* file's inode number */
336         struct ext4_extent_data data;   /* extent belong to file */
337         struct extent *next;
338 } extent_t;
340 typedef struct exts_group {
341         struct exts_group *prev;
342         extent_t *start;                /* start ext */
343         extent_t *end;                  /* end ext */
344         int len;                        /* length of this continuous region */
345         struct exts_group *next;
346 } exts_group_t;
348 struct fiemap_extent {
349         __u64 fe_logical;       /* logical offset in bytes for the start of
350                                 * the extent from the beginning of the file */
351         __u64 fe_physical;      /* physical offset in bytes for the start
352                                 * of the extent from the beginning
353                                 * of the disk */
354         __u64 fe_length;        /* length in bytes for this extent */
355         __u64 fe_reserved64[2];
356         __u32 fe_flags;    /* FIEMAP_EXTENT_* flags for this extent */
357         __u32 fe_reserved[3];
360 struct fiemap {
361         __u64 fm_start;         /* logical offset (inclusive) at
362                                 * which to start mapping (in) */
363         __u64 fm_length;        /* logical length of mapping which
364                                 * userspace wants (in) */
365         __u32 fm_flags;         /* FIEMAP_FLAG_* flags for request (in/out) */
366         __u32 fm_mapped_extents;/* number of extents that were mapped (out) */
367         __u32 fm_extent_count;  /* size of fm_extents array (in) */
368         __u32 fm_reserved;
369         struct fiemap_extent fm_extents[0];/* array of mapped extents (out) */
372 struct fiemap_ino {
373         __u64 ino;
374         struct fiemap fiemap;
377 int     force_flag;
378 int     detail_flag;
379 int     regional_flag;
380 int     extents_before_defrag;
381 int     extents_after_defrag;
382 unsigned long   succeed_cnt;
383 unsigned long   regular_count;
384 unsigned long   total_count;
385 unsigned long   frag_files_before_defrag;
386 unsigned long   frag_files_after_defrag;
387 unsigned long   defraged_file_count;
388 char    lost_found_dir[PATH_MAX + 1];
389 ext4_fsblk_t    goal;
390 ext4_fsblk_t    fgoal = -1;
393  * ext2fs_swab32() -    Change endian.
395  * @val:                the entry used for change.
396  */
397 __u32 ext2fs_swab32(__u32 val)
399 #if BYTE_ORDER == BIG_ENDIAN
400         return (val>>24) | ((val>>8)&0xFF00) |
401                 ((val<<8)&0xFF0000) | (val<<24);
402 #else
403         return val;
404 #endif
408  * fadvise() -          Give advice about file access.
410  * @fd:                 the file's descriptor.
411  * @offset:             file offset.
412  * @len:                area length.
413  * @advise:             process flag.
414  */
415 int fadvise(int fd, loff_t offset, size_t len, int advise)
417         return syscall(__NR_fadvise64, fd, offset, len, advise);
421  * sync_file_range() -  Sync file region.
423  * @fd:                 the file's descriptor.
424  * @offset:             file offset.
425  * @length:             area length.
426  * @flag:               process flag.
427  */
428 int sync_file_range(int fd, loff_t offset, loff_t length, unsigned int flag)
430         return syscall(__NR_sync_file_range, fd, offset, length, flag);
434  * page_in_core() -     Get information on whether pages are in core.
436  * @fd:                 the file's descriptor.
437  * @defrag_data:        data used for defrag.
438  * @vec:                page state array.
439  * @page_num:           page number.
440  */
441 int page_in_core(int fd, struct ext4_ext_defrag_data defrag_data,
442                  unsigned char **vec, unsigned long *page_num)
444         int blocksize;
445         int pagesize = getpagesize();
446         void *page = NULL;
447         loff_t offset, end_offset, length;
449         if (vec == NULL || *vec != NULL)
450                 return RETURN_NG;
452         if (ioctl(fd, FIGETBSZ, &blocksize) < 0)
453                 return RETURN_NG;
455         /* In mmap, offset should be a multiple of the page size */
456         offset = defrag_data.start_offset * blocksize;
457         length = defrag_data.defrag_size * blocksize;
458         end_offset = offset + length;
459         /* Round the offset down to the nearest multiple of pagesize */
460         offset = (offset / pagesize) * pagesize;
461         length = end_offset - offset;
463         page = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, offset);
464         if (page == MAP_FAILED)
465                 return RETURN_NG;
467         *page_num = 0;
468         *page_num = (length + pagesize - 1) / pagesize;
469         *vec = (unsigned char *)calloc(*page_num, 1);
470         if (*vec == NULL)
471                 return RETURN_NG;
473         /* Get information on whether pages are in core */
474         if (mincore(page, (size_t)length, *vec) == -1 ||
475             munmap(page, length) == -1) {
476                 FREE(*vec);
477                 return RETURN_NG;
478         }
480         return RETURN_OK;
484  * defrag_fadvise() -   Predeclare an access pattern for file data.
486  * @fd:                 the file's descriptor.
487  * @defrag_data:        data used for defrag.
488  * @vec:                page state array.
489  * @page_num:           page number.
490  */
491 int defrag_fadvise(int fd, struct ext4_ext_defrag_data defrag_data,
492                    unsigned char *vec, unsigned long page_num)
494         int flag = 1;
495         int blocksize;
496         int pagesize = getpagesize();
497         int fadvise_flag = POSIX_FADV_DONTNEED;
498         int sync_flag = SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE|
499                         SYNC_FILE_RANGE_WAIT_AFTER;
500         unsigned long i;
501         loff_t offset;
503         if (ioctl(fd, FIGETBSZ, &blocksize) < 0)
504                 return RETURN_NG;
506         offset = (loff_t)defrag_data.start_offset * blocksize;
507         offset = (offset / pagesize) * pagesize;
509         /* Sync file for fadvise process */
510         if (sync_file_range(fd, offset,
511                 (loff_t)pagesize*page_num, sync_flag) != 0)
512                 return RETURN_NG;
514         /* Try to release buffer cache which this process used,
515          * then other process can use the released buffer
516          */
517         for (i = 0; i < page_num; i++) {
518                 if ((vec[i] & 0x1) == 0) {
519                         offset += pagesize;
520                         continue;
521                 }
522                 if (fadvise(fd, offset, pagesize, fadvise_flag) != 0) {
523                         if (detail_flag && flag) {
524                                 perror(NGMSG_FADVISE_SYSCALL);
525                                 flag = 0;
526                         }
527                 }
528                 offset += pagesize;
529         }
531         return RETURN_OK;
535  * check_free_size() -  Check if there's enough disk space.
537  * @fd:                 the file's descriptor.
538  * @file_name           file name.
539  * @buf:                the pointer of the struct stat64.
540  */
541 int check_free_size(int fd, const char *file_name, const struct stat64 *buf)
543         off64_t size = 0;
544         off64_t free_size = 0;
545         struct statfs   fsbuf;
547         /* Target file size */
548         size = buf->st_size;
550         if (fstatfs(fd, &fsbuf) < 0) {
551                 if (detail_flag) {
552                         PRINT_FILE_NAME(file_name);
553                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FS_INFO);
554                 }
555                 return RETURN_NG;
556         }
558         /* Compute free space for root and normal user separately */
559         if (getuid() == ROOT_UID)
560                 free_size = (off64_t)fsbuf.f_bsize * fsbuf.f_bfree;
561         else
562                 free_size = (off64_t)fsbuf.f_bsize * fsbuf.f_bavail;
564         if (free_size >= size)
565                 return RETURN_OK;
567         if (detail_flag) {
568                 PRINT_FILE_NAME(file_name);
569                 IN_FTW_PRINT_ERR_MSG(NGMSG_FILE_LARGE);
570         }
571         return RETURN_NG;
574 int file_check(int fd, const struct stat64 *buf, const char *file_name);
575 int force_defrag(int fd, const char *file_name,
576                 const struct stat64 *buf, int blocksize);
579  * file_frag_count() -  Get file fragment count.
581  * @fd:                 the file's descriptor.
582  */
583 long long file_frag_count(int fd)
585         int ret = RETURN_NG;
586         struct fiemap file_extent_map;
588         /* When fm_extent_count is 0,
589          * ioctl just get file fragment count.
590          */
591         memset(&file_extent_map, 0, sizeof(struct fiemap));
592         file_extent_map.fm_start = 0;
593         file_extent_map.fm_length = FIEMAP_MAX_OFFSET;
594         file_extent_map.fm_flags |= FIEMAP_FLAG_SYNC;
596         ret = ioctl(fd, FS_IOC_FIEMAP, &file_extent_map);
597         if (ret < 0)
598                 return RETURN_NG;
600         return file_extent_map.fm_mapped_extents;
604  * ftw_fn() -           Check file attributes and ioctl call to avoid.
605  *                      illegal operations.
607  * @file:               the file's name.
608  * @buf:                the pointer of the struct stat64.
609  * @flag:               file type.
610  * @ftwbuf:             the pointer of a struct FTW.
611  */
612 int ftw_fn(const char *file, const struct stat64 *buf, int flag,
613            struct FTW *ftwbuf)
615         int     fd;
616         int     blocksize;
617         int     percent = 0;
618         int     defrag_fail = 0;
619         int     defraged_size = 0;
620         int     ret = RETURN_NG;
621         long long       file_frags_start, file_frags_end;
622         unsigned long   page_num;
623         unsigned char   *vec = NULL;
624         loff_t  start = 0;
625         struct ext4_ext_defrag_data     df_data;
627         defraged_file_count++;
629         if (detail_flag) {
630                 printf("[%lu/%lu]", defraged_file_count, total_count);
631                 fflush(stdout);
632         }
634         if (lost_found_dir[0] != '\0' &&
635             !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) {
636                 if (detail_flag) {
637                         PRINT_FILE_NAME(file);
638                         IN_FTW_PRINT_ERR_MSG(NGMSG_LOST_FOUND);
639                 }
640                 return FTW_CONT;
641         }
643         if (flag != FTW_F) {
644                 if (detail_flag) {
645                         PRINT_FILE_NAME(file);
646                         IN_FTW_PRINT_ERR_MSG(NGMSG_FILE_UNREG);
647                 }
648                 return FTW_CONT;
649         }
651         fd = open64(file, O_RDONLY);
652         if (fd < 0) {
653                 if (detail_flag) {
654                         PRINT_FILE_NAME(file);
655                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN);
656                 }
657                 return FTW_CONT;
658         }
660         if (file_check(fd, buf, file) == RETURN_NG)
661                 goto out;
663         if (fsync(fd) < 0) {
664                 if (detail_flag) {
665                         PRINT_FILE_NAME(file);
666                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_SYNC);
667                 }
668                 goto out;
669         }
670         /* Get blocksize */
671         if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
672                 if (detail_flag) {
673                         PRINT_FILE_NAME(file);
674                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_BLOCKSIZE);
675                 }
676                 goto out;
677         }
678         /* Ioctl call does the actual defragment job */
679         df_data.start_offset = 0;
680         df_data.goal = goal;
681         df_data.ext.len = 0;
682         df_data.flag = force_flag;
684         /* Count file fragments before defrag */
685         if (detail_flag) {
686                 file_frags_start = file_frag_count(fd);
687                 if (file_frags_start == RETURN_NG) {
688                         PRINT_FILE_NAME(file);
689                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_INFO);
690                         goto out;
691                 }
693                 if (file_frags_start != 1)
694                         frag_files_before_defrag++;
696                 extents_before_defrag += file_frags_start;
697         }
699         /* Print process progress */
700         percent = (start * 100) / buf->st_size;
701         printf("\033[79;0H\033[K[%lu/%lu]%s:\t%3d%%",
702                 defraged_file_count, total_count, file, min(percent, 100));
703         fflush(stdout);
705         while (1) {
706                 df_data.defrag_size = (min((buf->st_size - start),
707                         DEFRAG_SIZE) + blocksize - 1) / blocksize;
708                 ret = page_in_core(fd, df_data, &vec, &page_num);
709                 if (ret == RETURN_NG) {
710                         if (detail_flag) {
711                                 printf("\n");
712                                 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_MAP);
713                         } else {
714                                 printf("\t[ NG ]\n");
715                         }
716                         defrag_fail = 1;
717                         break;
718                 }
720                 /* EXT4_IOC_DEFRAG */
721                 defraged_size = ioctl(fd, EXT4_IOC_DEFRAG, &df_data);
723                 /* Free pages */
724                 ret = defrag_fadvise(fd, df_data, vec, page_num);
725                 if (vec) {
726                         free(vec);
727                         vec = NULL;
728                 }
729                 if (ret == RETURN_NG) {
730                         if (detail_flag) {
731                                 printf("\n");
732                                 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_DROP_BUFFER
733                                         );
734                         } else {
735                                 printf("\t[ NG ]\n");
736                         }
737                         defrag_fail = 1;
738                         break;
739                 }
741                 /* Go into force defrag mode */
742                 if (defraged_size < 0 && force_flag == DEFRAG_FORCE_TRY
743                                         && errno == ENOSPC) {
744                         /* File size is larger than max size of
745                          * force defrag mode
746                          */
747                         if (buf->st_size > MAX_FILE_SIZE) {
748                                 if (detail_flag) {
749                                         fprintf(stderr, "\n\t");
750                                         fprintf(stderr, NGMSG_FORCE_DEFRAG_SIZE,
751                                                 (MAX_FILE_SIZE) / (1024 * 1024)
752                                                 );
753                                         fprintf(stderr, "\t[ NG ]\n");
754                                 } else {
755                                         printf("\t[ NG ]\n");
756                                 }
757                                 defrag_fail = 1;
758                                 break;
759                         }
761                         printf("\033[79;0H\033[K");
762                         defraged_size = force_defrag(fd, file, buf, blocksize);
763                         if (defraged_size * blocksize >= buf->st_size)
764                                 /* Whole file is enforcedly defraged */
765                                 break;
766                         else {
767                                 if (!detail_flag) {
768                                         printf("\033[79;0H\033[K[%lu/%lu]%s\t"
769                                                 "0%%\t[ NG ]\n",
770                                                 defraged_file_count,
771                                                 total_count, file);
772                                 }
773                                 defrag_fail = 1;
774                                 break;
775                         }
776                 }
777                 if (defraged_size < 0) {
778                         if (detail_flag) {
779                                 printf("\n");
780                                 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_DEFRAG);
781                         } else {
782                                 printf("\t[ NG ]\n");
783                         }
784                         defrag_fail = 1;
785                         break;
786                 }
787                 df_data.start_offset += defraged_size;
788                 start = (long long)df_data.start_offset * blocksize;
790                 /* Print process progress */
791                 percent = (start * 100) / buf->st_size;
793                 /* Disk space file used is bigger than logical size */
794                 printf("\033[79;0H\033[K[%lu/%lu]%s:\t%3d%%",
795                         defraged_file_count, total_count, file,
796                                 min(percent, 100));
797                 fflush(stdout);
799                 /* End of file */
800                 if (start >= buf->st_size)
801                         break;
802         }
804         /* Count file fragments after defrag and print extents info */
805         if (detail_flag) {
806                 file_frags_end = file_frag_count(fd);
807                 if (file_frags_end == RETURN_NG) {
808                         printf("\n");
809                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_INFO);
810                         goto out;
811                 }
813                 if (file_frags_end != 1)
814                         frag_files_after_defrag++;
816                 extents_after_defrag += file_frags_end;
818                 if (defrag_fail)
819                         goto out;
821                 printf("  extents: %lld -> %lld",
822                         file_frags_start, file_frags_end);
823                 fflush(stdout);
824         }
826         if (defrag_fail)
827                 goto out;
829         printf("\t[ OK ]\n");
830         fflush(stdout);
831         succeed_cnt++;
833 out:
834         close(fd);
835         return FTW_CONT;
839  * file_check() -       Check file's attributes.
841  * @fd:                 the file's descriptor.
842  * @buf:                a pointer of the struct stat64.
843  * @file_name:          the file's name.
844  */
845 int file_check(int fd, const struct stat64 *buf, const char *file_name)
847         struct flock    lock;
849         /* Write-lock check is more reliable */
850         lock.l_type = F_WRLCK;
851         lock.l_start = 0;
852         lock.l_whence = SEEK_SET;
853         lock.l_len = 0;
855         /* Regular file */
856         if (S_ISREG(buf->st_mode) == 0) {
857                 if (detail_flag) {
858                         PRINT_FILE_NAME(file_name);
859                         IN_FTW_PRINT_ERR_MSG(NGMSG_FILE_UNREG);
860                 }
861                 return RETURN_NG;
862         }
864         /* Free space */
865         if (check_free_size(fd, file_name, buf) == RETURN_NG)
866                 return RETURN_NG;
868         /* Priority */
869         if (getuid() != ROOT_UID &&
870                 buf->st_uid != getuid()) {
871                 if (detail_flag) {
872                         PRINT_FILE_NAME(file_name);
873                         IN_FTW_PRINT_ERR_MSG(NGMSG_FILE_PRIORITY);
874                 }
875                 return RETURN_NG;
876         }
878         /* Lock status */
879         if (fcntl(fd, F_GETLK, &lock) < 0) {
880                 if (detail_flag) {
881                         PRINT_FILE_NAME(file_name);
882                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_GET_LCKINFO);
883                 }
884                 return RETURN_NG;
885         } else if (lock.l_type != F_UNLCK) {
886                 if (detail_flag) {
887                         PRINT_FILE_NAME(file_name);
888                         IN_FTW_PRINT_ERR_MSG(NGMSG_FILE_LOCK);
889                 }
890                 return RETURN_NG;
891         }
893         /* Empty file */
894         if (buf->st_size == 0) {
895                 if (detail_flag) {
896                         PRINT_FILE_NAME(file_name);
897                         IN_FTW_PRINT_ERR_MSG(NGMSG_FILE_BLANK);
898                 }
899                 return RETURN_NG;
900         }
902         return RETURN_OK;
906  * is_ext4() -          Whether on an ext4 filesystem.
908  * @filename:           the file's name.
909  */
910 int is_ext4(const char *filename)
912         int     maxlen, len, ret;
913         FILE    *fp = NULL;
914         char    *mnt_type = NULL;
915         /* Refer to /etc/mtab */
916         char    *mtab = MOUNTED;
917         char    file_path[PATH_MAX + 1];
918         struct mntent   *mnt = NULL;
919         struct statfs   buffs;
921         /* Get full path */
922         if (realpath(filename, file_path) == NULL) {
923                 perror(NGMSG_REALPATH);
924                 PRINT_FILE_NAME(filename);
925                 return RETURN_NG;
926         }
928         if (statfs(file_path, &buffs) < 0) {
929                 perror(NGMSG_FS_INFO);
930                 PRINT_FILE_NAME(filename);
931                 return RETURN_NG;
932         }
934         if (buffs.f_type != EXT4_SUPER_MAGIC) {
935                 PRINT_ERR_MSG(NGMSG_EXT4);
936                 return RETURN_NG;
937         }
939         fp = setmntent(mtab, "r");
940         if (fp == NULL) {
941                 perror(NGMSG_MTAB);
942                 return RETURN_NG;
943         }
945         maxlen = 0;
946         while ((mnt = getmntent(fp)) != NULL) {
947                 len = strlen(mnt->mnt_dir);
948                 ret = memcmp(file_path, mnt->mnt_dir, len);
949                 if (ret != 0)
950                         continue;
952                 if (maxlen >= len)
953                         continue;
955                 maxlen = len;
957                 mnt_type = realloc(mnt_type, strlen(mnt->mnt_type) + 1);
958                 if (!mnt_type) {
959                         endmntent(fp);
960                         return RETURN_NG;
961                 }
962                 strcpy(mnt_type, mnt->mnt_type);
963                 strncpy(lost_found_dir, mnt->mnt_dir, PATH_MAX);
964         }
966         endmntent(fp);
967         if (strcmp(mnt_type, FS_EXT4) == 0) {
968                 FREE(mnt_type);
969                 return RETURN_OK;
970         } else {
971                 FREE(mnt_type);
972                 PRINT_ERR_MSG(NGMSG_EXT4);
973                 return RETURN_NG;
974         }
978  * calc_entry_counts -  calculate file counts
980  * @file:               file name.
981  * @buf:                file info.
982  * @flag:               file type.
983  * @ftwbuf:             the pointer of a struct FTW.
984  */
985 int calc_entry_counts(const char *file, const struct stat64 *buf, int flag,
986                            struct FTW *ftwbuf)
989         if (flag == FTW_F)
990                 regular_count++;
992         total_count++;
994         return FTW_CONT;
998  * get_mount_point() -  Get device's mount point.
1000  * @devname:            the device's name.
1001  * @mount_point:        the mount point.
1002  * @dir_path_len:       the length of directory.
1003  */
1004 int get_mount_point(const char *devname, char *mount_point,
1005                 int dir_path_len)
1007         /* Refer to /etc/mtab */
1008         char    *mtab = MOUNTED;
1009         FILE    *fp = NULL;
1010         struct mntent   *mnt = NULL;
1012         fp = setmntent(mtab, "r");
1013         if (fp == NULL) {
1014                 perror(NGMSG_MTAB);
1015                 return RETURN_NG;
1016         }
1018         while ((mnt = getmntent(fp)) != NULL) {
1019                 if (strcmp(devname, mnt->mnt_fsname) != 0)
1020                         continue;
1022                 endmntent(fp);
1023                 if (strcmp(mnt->mnt_type, FS_EXT4) == 0) {
1024                         strncpy(mount_point, mnt->mnt_dir,
1025                                 dir_path_len);
1026                         return RETURN_OK;
1027                 }
1028                 PRINT_ERR_MSG(NGMSG_EXT4);
1029                 return RETURN_NG;
1030         }
1031         endmntent(fp);
1032         PRINT_ERR_MSG(NGMSG_UNMOUNT);
1033         return RETURN_NG;
1037  * main() -             ext4 online defrag.
1039  * @argc:               the number of parameter.
1040  * @argv[]:             the pointer array of parameter.
1041  */
1042 int main(int argc, char *argv[])
1044         int     fd;
1045         int     opt;
1046         int     i, flags;
1047         int     arg_type;
1048         int     detail_tmp;
1049         int     success_flag;
1050         char    dir_name[PATH_MAX + 1];
1051         struct stat64   buf;
1053         i = 1;
1054         flags = 0;
1055         arg_type = -1;
1056         detail_tmp = -1;
1057         success_flag = 0;
1058         /* Do not follow symlink */
1059         flags |= FTW_PHYS;
1060         /* Stay within the same filesystem */
1061         flags |= FTW_MOUNT;
1063         /* Parse arguments */
1064         if (argc == 1 || (argc == 2 && argv[1][0] == '-'))
1065                 goto out;
1067         while ((opt = getopt(argc, argv, "rvf")) != EOF) {
1068                 switch (opt) {
1069                 case 'r':
1070                         regional_flag = 1;
1071                         i = 2;
1072                         break;
1073                 case 'v':
1074                         detail_flag = 1;
1075                         i = 2;
1076                         break;
1077                 case 'f':
1078                         force_flag = DEFRAG_FORCE_TRY;
1079                         i = 2;
1081                         if (argc == 3)
1082                                 break;
1084                         if (argc > 4) {
1085                                 printf("Illegal argument\n");
1086                                 goto out;
1087                         }
1089                         /* Now the agrc must be 4(e4defrag -f file blocknr) */
1090                         int res_strlen;
1091                         res_strlen = strlen(argv[3]);
1092                         for (res_strlen -= 1; res_strlen >= 0; res_strlen--) {
1093                                 if (!isdigit(argv[3][res_strlen])) {
1094                                         printf("Illegal argument\n");
1095                                         goto out;
1096                                 }
1097                         }
1099                         fgoal = strtoul(argv[3], NULL, 0);
1100                         if (errno) {
1101                                 printf("block num shold be < 4294967295"
1102                                         "(max num of unsigned long 32bit)\n");
1103                                 exit(1);
1104                         }
1105                         if (!fgoal)
1106                                 fgoal = -1;
1107                         break;
1108                 default:
1109                         goto out;
1110                 }
1111         }
1113         /* Main process */
1114         for (; i < argc; i++) {
1115                 succeed_cnt = 0;
1116                 regular_count = 0;
1117                 total_count = 0;
1118                 frag_files_before_defrag = 0;
1119                 frag_files_after_defrag = 0;
1120                 extents_before_defrag = 0;
1121                 extents_after_defrag = 0;
1122                 defraged_file_count = 0;
1124                 memset(dir_name, 0, PATH_MAX + 1);
1125                 memset(lost_found_dir, 0, PATH_MAX + 1);
1127                 if (force_flag && i == 3)
1128                         break;
1130                 #if BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN
1131                         PRINT_ERR_MSG(NGMSG_ENDIAN);
1132                         PRINT_FILE_NAME(argv[i]);
1133                         continue;
1134                 #endif
1136                 if (lstat64(argv[i], &buf) < 0) {
1137                         perror(NGMSG_FILE_INFO);
1138                         PRINT_FILE_NAME(argv[i]);
1139                         continue;
1140                 }
1142                 /* Only regular file is acceptalbe with force defrag mode */
1143                 if (force_flag && !S_ISREG(buf.st_mode)) {
1144                         printf("Inappropriate file type\n");
1145                         goto out;
1146                 }
1148                 if (S_ISBLK(buf.st_mode)) {
1149                         /* Block device */
1150                         if (get_mount_point(argv[i], dir_name, PATH_MAX)
1151                                                         == RETURN_NG)
1152                                 continue;
1153                         arg_type = DEVNAME;
1154                         printf("ext4 defragmentation for device(%s)\n",
1155                                 argv[i]);
1156                 } else if (S_ISDIR(buf.st_mode)) {
1157                         /* Directory */
1158                         if (access(argv[i], R_OK) < 0) {
1159                                 perror(argv[i]);
1160                                 continue;
1161                         }
1162                         arg_type = DIRNAME;
1163                         strcpy(dir_name, argv[i]);
1164                 } else if (S_ISREG(buf.st_mode)) {
1165                         /* Regular file */
1166                         arg_type = FILENAME;
1167                 } else {
1168                         /* Irregular file */
1169                         PRINT_ERR_MSG(NGMSG_FILE_UNREG);
1170                         PRINT_FILE_NAME(argv[i]);
1171                         continue;
1172                 }
1174                 /* For device case,
1175                  * filesystem type checked in get_mount_point()
1176                  */
1177                 if (arg_type == FILENAME || arg_type == DIRNAME) {
1178                         if (is_ext4(argv[i]) == RETURN_NG)
1179                                 continue;
1180                         if (realpath(argv[i], dir_name) == NULL) {
1181                                 perror(NGMSG_REALPATH);
1182                                 PRINT_FILE_NAME(argv[i]);
1183                                 continue;
1184                         }
1185                 }
1187                 switch (arg_type) {
1188                 case DIRNAME:
1189                         printf("ext4 defragmentation for directory(%s)\n",
1190                                 argv[i]);
1192                         int mount_dir_len = 0;
1193                         mount_dir_len = strnlen(lost_found_dir, PATH_MAX);
1195                         strncat(lost_found_dir, "/lost+found",
1196                                 PATH_MAX - strnlen(lost_found_dir, PATH_MAX));
1198                         /* Not the case("e4defrag  mount_piont_dir") */
1199                         if (dir_name[mount_dir_len] != '\0') {
1200                                 /* "e4defrag mount_piont_dir/lost+found"
1201                                  * or "e4defrag mount_piont_dir/lost+found/"
1202                                  */
1203                                 if (strncmp(lost_found_dir, dir_name,
1204                                             strnlen(lost_found_dir,
1205                                                     PATH_MAX)) == 0 &&
1206                                     (dir_name[strnlen(lost_found_dir,
1207                                                       PATH_MAX)] == '\0' ||
1208                                      dir_name[strnlen(lost_found_dir,
1209                                                       PATH_MAX)] == '/')) {
1210                                         PRINT_ERR_MSG(NGMSG_LOST_FOUND);
1211                                         PRINT_FILE_NAME(argv[i]);
1212                                         continue;
1213                                 }
1215                                 /* "e4defrag mount_piont_dir/else_dir" */
1216                                 memset(lost_found_dir, 0, PATH_MAX + 1);
1217                         }
1218                 case DEVNAME:
1219                         if (arg_type == DEVNAME) {
1220                                 strncpy(lost_found_dir, dir_name,
1221                                         strnlen(dir_name, PATH_MAX));
1222                                 strncat(lost_found_dir, "/lost+found/",
1223                                         PATH_MAX - strnlen(lost_found_dir,
1224                                                            PATH_MAX));
1225                         }
1227                         /* Regional block allocation */
1228                         if (regional_flag) {
1229                                 unsigned int bg_num = 0;
1230                                 int ret = 0;
1231                                 struct ext4_super_block sb;
1233                                 printf(MSG_R_OPTION);
1235                                 fd = open64(dir_name, O_RDONLY);
1236                                 if (fd < 0) {
1237                                         if (detail_flag) {
1238                                                 perror(NGMSG_FILE_OPEN);
1239                                                 PRINT_FILE_NAME(dir_name);
1240                                         }
1241                                         continue;
1242                                 }
1244                                 memset(&sb, 0, sizeof(struct ext4_super_block));
1246                                 ret = ioctl(fd, EXT4_IOC_SUPER_INFO, &sb);
1247                                 if (ret < 0) {
1248                                         perror(NGMSG_FILE_SUPER);
1249                                         PRINT_FILE_NAME(dir_name);
1250                                         close(fd);
1251                                         continue;
1252                                 }
1253                                 close(fd);
1255                                 sb.s_blocks_per_group =
1256                                         ext2fs_swab32(sb.s_blocks_per_group);
1257                                 sb.s_inodes_per_group =
1258                                         ext2fs_swab32(sb.s_inodes_per_group);
1259                                 sb.s_first_data_block =
1260                                         ext2fs_swab32(sb.s_first_data_block);
1262                                 bg_num = (buf.st_ino - 1) /
1263                                                         sb.s_inodes_per_group;
1264                                 goal = bg_num * sb.s_blocks_per_group +
1265                                                         sb.s_first_data_block;
1266                         }
1267                         nftw64(dir_name, calc_entry_counts, FTW_OPEN_FD, flags);
1269                         /* File tree walk */
1270                         nftw64(dir_name, ftw_fn, FTW_OPEN_FD, flags);
1271                         printf("\n\tSuccess:\t\t\t[ %lu/%lu ]\n", succeed_cnt,
1272                                 total_count);
1273                         printf("\tFailure:\t\t\t[ %lu/%lu ]\n",
1274                                 total_count - succeed_cnt, total_count);
1275                         if (detail_flag) {
1276                                 printf("\tTotal extents:\t\t\t%4d->%d\n",
1277                                         extents_before_defrag,
1278                                         extents_after_defrag);
1279                                 printf("\tFragmented percentage:\t\t"
1280                                         "%3llu%%->%llu%%\n",
1281                                         !regular_count ? 0 :
1282                                         ((unsigned long long)
1283                                         frag_files_before_defrag * 100) /
1284                                         regular_count,
1285                                         !regular_count ? 0 :
1286                                         ((unsigned long long)
1287                                         frag_files_after_defrag * 100) /
1288                                         regular_count);
1289                         }
1290                         break;
1291                 case FILENAME:
1292                         total_count = 1;
1293                         strncat(lost_found_dir, "/lost+found/",
1294                                 PATH_MAX - strnlen(lost_found_dir,
1295                                                    PATH_MAX));
1296                         if (strncmp(lost_found_dir, dir_name,
1297                                     strnlen(lost_found_dir,
1298                                             PATH_MAX)) == 0) {
1299                                 PRINT_ERR_MSG(NGMSG_LOST_FOUND);
1300                                 PRINT_FILE_NAME(argv[i]);
1301                                 continue;
1302                         }
1304                         if (regional_flag) {
1305                                 fprintf(stderr, NGMSG_TYPE, argv[i]);
1306                                 continue;
1307                         }
1308                         printf("ext4 defragmentation for %s\n", argv[i]);
1309                         /* Defrag single file process */
1310                         ftw_fn(argv[i], &buf, FTW_F, NULL);
1311                         if (succeed_cnt != 0)
1312                                 printf(" Success:\t\t\t[1/1]\n");
1313                         else
1314                                 printf(" Success:\t\t\t[0/1]\n");
1316                         break;
1317                 }
1319                 if (succeed_cnt != 0)
1320                         success_flag = 1;
1321         }
1323         if (success_flag)
1324                 return RETURN_OK;
1326         exit(1);
1328 out:
1329         printf(MSG_USAGE);
1330         exit(1);
1334  * insert_extent() -    Sequentially insert extent by physical block number.
1336  * @extlist_head:       the head of an extent list.
1337  * @ext:                the extent element which will be inserted.
1338  */
1339 int insert_extent(extent_t **extlist_head, extent_t *ext)
1341         extent_t        *ext_tmp = *extlist_head;
1343         if (ext == NULL)
1344                 return RETURN_NG;
1346         /* First element */
1347         if (*extlist_head == NULL) {
1348                 (*extlist_head) = ext;
1349                 (*extlist_head)->prev = *extlist_head;
1350                 (*extlist_head)->next = *extlist_head;
1351                 return RETURN_OK;
1352         }
1354         if (ext->data.start <= ext_tmp->data.start) {
1355                 /* Insert before head */
1356                 if (ext_tmp->data.start < ext->data.start + ext->data.len)
1357                         /* Overlap */
1358                         return RETURN_NG;
1359                 /* Adjust head */
1360                 *extlist_head = ext;
1361         } else {
1362                 /* Insert into the middle or last of the list */
1363                 do {
1364                         if (ext->data.start < ext_tmp->data.start)
1365                                 break;
1366                         ext_tmp = ext_tmp->next;
1367                 } while (ext_tmp != (*extlist_head));
1368                 if (ext->data.start <
1369                     ext_tmp->prev->data.start + ext_tmp->prev->data.len)
1370                         /* Overlap */
1371                         return RETURN_NG;
1373                 if (ext_tmp != *extlist_head &&
1374                     ext_tmp->data.start < ext->data.start + ext->data.len)
1375                         /* Overlap */
1376                         return RETURN_NG;
1377         }
1378         ext_tmp = ext_tmp->prev;
1379         /* Insert "ext" after "ext_tmp" */
1380         insert(ext_tmp, ext);
1381         return RETURN_OK;
1385  * insert_exts_group() -        Insert a exts_group in decreasing order
1386  *                              of length.
1388  * @exts_group_list_head:       the head of a exts_group list.
1389  * @exts_group:                 the exts_group element which will be inserted.
1390  */
1391 int insert_exts_group(exts_group_t **exts_group_list_head,
1392                       exts_group_t *exts_group)
1394         exts_group_t    *exts_group_tmp = NULL;
1396         if (exts_group == NULL)
1397                 return RETURN_NG;
1399         /* Initialize list */
1400         if (*exts_group_list_head == NULL) {
1401                 (*exts_group_list_head) = exts_group;
1402                 (*exts_group_list_head)->prev = *exts_group_list_head;
1403                 (*exts_group_list_head)->next = *exts_group_list_head;
1404                 return RETURN_OK;
1405         }
1407         if (exts_group->len >= (*exts_group_list_head)->len) {
1408                 /* Insert before exts_group_list_head */
1409                 exts_group_tmp = (*exts_group_list_head)->prev;
1410                 insert(exts_group_tmp, exts_group);
1411                 *exts_group_list_head = exts_group;
1412                 return RETURN_OK;
1413         }
1415         /* Find insertion positon */
1416         for (exts_group_tmp = (*exts_group_list_head)->next;
1417              exts_group_tmp != *exts_group_list_head;
1418              exts_group_tmp = exts_group_tmp->next) {
1419                 if (exts_group_tmp->len < exts_group->len)
1420                         break;
1421         }
1422         exts_group_tmp = exts_group_tmp->prev;
1423         insert(exts_group_tmp, exts_group);
1425         return RETURN_OK;
1429  * get_exts_group() -           Get element from the exts_group list.
1431  * @exts_group_list_head:       the head of a exts_group list.
1432  * @exts_group:                 the exts_group element which will be got.
1433  */
1434 exts_group_t *get_exts_group(exts_group_t **exts_group_list_head,
1435                               exts_group_t *exts_group)
1437         if (exts_group == NULL || *exts_group_list_head == NULL)
1438                 return NULL;
1440         /* Keep "exts_group_list_head" point to the largest extent group */
1441         if (exts_group == *exts_group_list_head)
1442                 *exts_group_list_head = exts_group->next;
1444         if (*exts_group_list_head == (*exts_group_list_head)->next &&
1445             exts_group == *exts_group_list_head)
1446                 /* Delete the last element in the list */
1447                 *exts_group_list_head = NULL;
1449         exts_group->prev->next = exts_group->next;
1450         exts_group->next->prev = exts_group->prev;
1451         return exts_group;
1455  * free_exts_group() -          Free the exts_group.
1457  * @*exts_group_list_head:      the exts_group list head which will be free.
1458  */
1459  void free_exts_group(exts_group_t *exts_group_list_head)
1461         exts_group_t *exts_group_tmp = NULL;
1463         if (exts_group_list_head == NULL)
1464                 return;
1466         while (exts_group_list_head->next != exts_group_list_head) {
1467                 exts_group_tmp = exts_group_list_head;
1468                 exts_group_list_head->prev->next = exts_group_list_head->next;
1469                 exts_group_list_head->next->prev = exts_group_list_head->prev;
1470                 exts_group_list_head = exts_group_list_head->next;
1471                 free(exts_group_tmp);
1472         }
1473         free(exts_group_list_head);
1477  * free_ext() -         Free the extent list.
1479  * @extent_list_head:   the extent list head of which will be free.
1480  */
1481 void free_ext(extent_t *extent_list_head)
1483         extent_t *extent_tmp = NULL;
1485         if (extent_list_head == NULL)
1486                 return;
1488         while (extent_list_head->next != extent_list_head) {
1489                 extent_tmp = extent_list_head;
1490                 extent_list_head->prev->next = extent_list_head->next;
1491                 extent_list_head->next->prev = extent_list_head->prev;
1492                 extent_list_head = extent_list_head->next;
1493                 free(extent_tmp);
1494         }
1495         free(extent_list_head);
1499  * do_defrag() -        Execute the defrag program in force mode.
1501  * @fd:                 the file's descriptor.
1502  * @exts_group:         the exts_group which will be defraged.
1503  * @defrag_data:        the data which will be defraged.
1504  */
1505 int do_defrag(int fd, exts_group_t *exts_group,
1506               struct ext4_ext_defrag_data defrag_data)
1508         int defraged_size = 0;
1509         int fadvise_ret = 0;
1510         unsigned long page_num;
1511         unsigned char *vec = NULL;
1513         /* Init defrag_data for defrag */
1514         defrag_data.ext.start = exts_group->start->data.start;
1515         defrag_data.ext.len = exts_group->len;
1516         defrag_data.ext.block = 0;
1517         defrag_data.defrag_size = exts_group->len;
1518         /* Move victim extents to make sufficient space */
1519         defrag_data.flag = DEFRAG_FORCE_GATHER;
1520         defrag_data.goal = exts_group->start->data.start;
1522         if (page_in_core(fd, defrag_data, &vec, &page_num) == RETURN_NG)
1523                 return RETURN_NG;
1525         defraged_size = ioctl(fd, EXT4_IOC_DEFRAG, &defrag_data);
1527         /* Free pages */
1528         fadvise_ret = defrag_fadvise(fd, defrag_data, vec, page_num);
1529         FREE(vec);
1531         if (fadvise_ret == RETURN_NG || defraged_size < 0)
1532                 return RETURN_NG;
1534         return defraged_size;
1538  * get_used_extent() -  Get used extent in the block group.
1540  * @fd:                 the file's descriptor.
1541  * @ext_list_head:      the head of the extent list.
1542  * @istart:             start inode in the block group.
1543  * @iend:               end inode in the block group.
1544  * @bstart:             start block in the block group.
1545  * @bend:               end block in the block group.
1546  */
1547 int get_used_extent(int fd, extent_t **ext_list_head,
1548                     unsigned long long istart, unsigned long long iend,
1549                     ext4_fsblk_t bstart, ext4_fsblk_t bend)
1551         int     ret = 0;
1552         int     blocksize;
1553         int     extents_buffer_size, fiemap_ino_size;
1554         __u64   pos = 0;
1555         __u64   group_start, group_end;
1556         unsigned long long      ino;
1557         struct  fiemap_extent *extents_buf = NULL;
1558         struct  fiemap_ino *fiemap_ino_buf = NULL;
1560         ret = ioctl(fd, FIGETBSZ, &blocksize);
1561         if (ret < 0) {
1562                 if (detail_flag)
1563                         perror(NGMSG_FILE_BLOCKSIZE);
1565                 return RETURN_NG;
1566         }
1568         /* Convert units, in bytes.
1569          * Be careful : now, physical block number in extent is 48bit,
1570          * and the maximum blocksize for ext4 is 4K(12bit),
1571          * so there is no overflow, but in future it may be changed.
1572          */
1573         group_start = bstart * blocksize;
1574         group_end = bend * blocksize;
1576         /* Alloc space for fiemap_ino */
1577         fiemap_ino_size = sizeof(struct fiemap_ino);
1578         extents_buffer_size = EXTENT_MAX_COUNT * sizeof(struct fiemap_extent);
1580         fiemap_ino_buf = malloc(fiemap_ino_size + extents_buffer_size);
1581         if (!fiemap_ino_buf)
1582                 return RETURN_NG;
1584         extents_buf = fiemap_ino_buf->fiemap.fm_extents;
1585         memset(fiemap_ino_buf, 0, fiemap_ino_size);
1586         fiemap_ino_buf->fiemap.fm_length = FIEMAP_MAX_OFFSET;
1587         fiemap_ino_buf->fiemap.fm_flags |= FIEMAP_FLAG_SYNC;
1588         fiemap_ino_buf->fiemap.fm_extent_count = EXTENT_MAX_COUNT;
1590         for (ino = istart; ino <= iend; ino++) {
1591                 fiemap_ino_buf->ino = ino;
1592                 pos = 0;
1593                 do {
1594                         int i;
1595                         fiemap_ino_buf->fiemap.fm_start = pos;
1596                         memset(extents_buf, 0, extents_buffer_size);
1598                         ret = ioctl(fd, EXT4_IOC_FIEMAP_INO, fiemap_ino_buf);
1599                         if (ret < 0) {
1600                                 /* Skip non-regular file and unused inode */
1601                                 if (errno == EOPNOTSUPP || errno == ESTALE
1602                                         || errno == ENOENT || errno == EACCES)
1603                                         continue;
1604                                 goto out;
1605                         }
1607                         for (i = 0; i < fiemap_ino_buf->
1608                                         fiemap.fm_mapped_extents; i++) {
1609                                 extent_t        *extent = NULL;
1611                                 /* Is this extent in current block group ? */
1612                                 if (extents_buf[i].fe_physical < group_start ||
1613                                         extents_buf[i].fe_physical > group_end)
1614                                         continue;
1616                                 extent = malloc(sizeof(extent_t));
1617                                 if (extent == NULL)
1618                                         goto out;
1620                                 extent->data.start = extents_buf[i].fe_physical
1621                                                         / blocksize;
1622                                 extent->data.block = extents_buf[i].fe_logical
1623                                                         / blocksize;
1624                                 extent->data.len = extents_buf[i].fe_length
1625                                                         / blocksize;
1626                                 extent->ino = ino;
1627                                 extent->tag = EXT4_EXT_USE;
1629                                 if (insert_extent(ext_list_head, extent) < 0) {
1630                                         FREE(extent);
1631                                         goto out;
1632                                 }
1633                         }
1635                         /* Record file's logical offset this time */
1636                         pos = extents_buf[EXTENT_MAX_COUNT-1].fe_logical +
1637                                 extents_buf[EXTENT_MAX_COUNT-1].fe_length;
1638                 /* If fm_extents array has been filled and
1639                  * there are extents left, continue to cycle.
1640                  */
1641                 } while (fiemap_ino_buf->fiemap.fm_mapped_extents
1642                                                 == EXTENT_MAX_COUNT &&
1643                         !(extents_buf[EXTENT_MAX_COUNT-1].fe_flags
1644                                                 & FIEMAP_EXTENT_LAST));
1645         }
1647         FREE(fiemap_ino_buf);
1648         return RETURN_OK;
1649 out:
1650         FREE(fiemap_ino_buf);
1651         return RETURN_NG;
1655  * get_free_extent() -  Get used extent in the block group.
1657  * @fd:                 the file's descriptor.
1658  * @ino:                inode number from struct stat64.
1659  * @blocks_per_group:   the block number of each block group.
1660  * @ext_list_head:      the head of the extent list.
1661  */
1662 int get_free_extent(int fd, unsigned long long ino,
1663                     int blocks_per_group, extent_t **ext_list_head)
1665         ext4_grpblk_t   pos = 0;
1666         struct ext4_extents_info        extents_info;
1668         memset(&extents_info, 0, sizeof(struct ext4_extents_info));
1669         extents_info.ino = ino;
1670         extents_info.max_entries = DEFRAG_MAX_ENT;
1671         while (pos < blocks_per_group) {
1672                 int     i;
1673                 if (ioctl(fd, EXT4_IOC_FREE_BLOCKS_INFO, &extents_info) < 0)
1674                         return RETURN_NG;
1676                 for (i = 0;
1677                      extents_info.ext[i].len != 0 && i < DEFRAG_MAX_ENT; i++) {
1678                         /* Alloc space for extent */
1679                         extent_t        *extent = NULL;
1681                         extent = malloc(sizeof(extent_t));
1682                         if (extent == NULL)
1683                                 return RETURN_NG;
1684                         memset(extent, 0, sizeof(extent_t));
1685                         memcpy(&(extent->data), &(extents_info.ext[i]),
1686                                sizeof(struct ext4_extent_data));
1687                         /* Free extent */
1688                         extent->tag = EXT4_EXT_FREE;
1689                         if (insert_extent(ext_list_head, extent) < 0) {
1690                                 FREE(extent);
1691                                 return RETURN_NG;
1692                         }
1693                 }
1695                 /* No free extent after the logical block number "pos".
1696                  * In other word, offset this time equals to prev recursion.
1697                  */
1698                 if (pos == extents_info.g_offset)
1699                         break;
1700                 if (i < DEFRAG_MAX_ENT)
1701                         break;
1702                 /* Record the offset of logical block number this time */
1703                 pos = extents_info.g_offset;
1704                 memset(extents_info.ext, 0,
1705                        sizeof(struct ext4_extent_data) * DEFRAG_MAX_ENT);
1706         }
1708         return RETURN_OK;
1712  * join_extents() -             Find continuous region(exts_group).
1714  * @ext_list_head:              the head of the extent list.
1715  * @target_exts_group_list_head:the head of the target exts_group list.
1716  * @exts_group_list_head:       the head of the original exts_group list.
1717  * @filesize:                   the file's descriptor.
1718  * @max:                        the max size of free space.
1719  */
1720 int join_extents(extent_t *ext_list_head,
1721                  exts_group_t **target_exts_group_list_head,
1722                  exts_group_t **exts_group_list_head,
1723                  unsigned long filesize, int *max)
1725         int len;
1726         extent_t *ext_start, *extent_tmp;
1728         ext_start = ext_list_head;
1729         extent_tmp = ext_list_head;
1730         *max = 0;
1731         len = ext_list_head->data.len;
1732         extent_tmp = extent_tmp->next;
1733         do {
1734                 exts_group_t    *exts_group_tmp = NULL;
1736                 if (len >= filesize) {
1737                         /* Hit on the way,
1738                          * one extent group is enough for defrag, so return.
1739                          */
1740                         exts_group_tmp = malloc(sizeof(exts_group_t));
1741                         if (!exts_group_tmp)
1742                                 return RETURN_NG;
1744                         exts_group_tmp->prev = NULL;
1745                         exts_group_tmp->next = NULL;
1746                         exts_group_tmp->start = ext_start;
1747                         exts_group_tmp->end = extent_tmp->prev;
1748                         exts_group_tmp->len = len;
1749                         if (insert_exts_group(target_exts_group_list_head,
1750                                               exts_group_tmp) < 0) {
1751                                 FREE(exts_group_tmp);
1752                                 return RETURN_NG;
1753                         }
1754                         return CHECK_FRAG_COUNT;
1755                 }
1757                 /* This extent and previous extent are not continuous,
1758                  * so, all previous extents are treated as an extent group.
1759                  */
1760                 if ((extent_tmp->prev->data.start + extent_tmp->prev->data.len)
1761                                             != extent_tmp->data.start) {
1762                         exts_group_tmp = malloc(sizeof(exts_group_t));
1763                         if (exts_group_tmp == NULL)
1764                                 return RETURN_NG;
1766                         memset(exts_group_tmp, 0, sizeof(exts_group_t));
1767                         exts_group_tmp->len = len;
1768                         exts_group_tmp->start = ext_start;
1769                         exts_group_tmp->end = extent_tmp->prev;
1771                         if (insert_exts_group(exts_group_list_head,
1772                                               exts_group_tmp) < 0) {
1773                                 FREE(exts_group_tmp);
1774                                 return RETURN_NG;
1775                         }
1776                         *max += len;
1777                         ext_start = extent_tmp;
1778                         len = extent_tmp->data.len;
1779                         extent_tmp = extent_tmp->next;
1780                         continue;
1781                 }
1783                 /* This extent and previous extent are continuous,
1784                  * so, they belong to the same extent group, and we check
1785                  * if the next extent belongs to the same extent group.
1786                  */
1787                 len += extent_tmp->data.len;
1788                 extent_tmp = extent_tmp->next;
1789         } while (extent_tmp != ext_list_head->next);
1791         return RETURN_OK;
1795  * find_exts_group() -                  Find target exts_group.
1797  * @ext_count:                          the number of extents.
1798  * @filesize:                           the file's size.
1799  * @exts_group_list_head:               the head of the original exts_group list
1800  * @target_exts_group_list_head:        the head of the target exts_group list.
1801  */
1802 int find_exts_group(int *ext_count, unsigned long filesize,
1803                     exts_group_t **exts_group_list_head,
1804                     exts_group_t **target_exts_group_list_head)
1806         /* Blocks we found for target file */
1807         int len = 0;
1809         if (!(*exts_group_list_head))
1810                 return RETURN_NG;
1812         while (*exts_group_list_head) {
1813                 exts_group_t    *exts_group_tmp = NULL;
1815                 /* Even add the current largest extent group,
1816                  * the sapce we found is still not enough for the file,
1817                  * so just add the current largest extent group,
1818                  * and do the next loop.
1819                  */
1820                 if ((*exts_group_list_head)->len + len < filesize) {
1821                         len += (*exts_group_list_head)->len;
1822                         exts_group_tmp = get_exts_group(exts_group_list_head,
1823                                 *exts_group_list_head);
1824                         if (insert_exts_group(target_exts_group_list_head,
1825                                       exts_group_tmp) < 0) {
1826                                 FREE(exts_group_tmp);
1827                                 return RETURN_NG;
1828                         }
1829                         (*ext_count)++;
1830                         continue;
1831                 }
1833                 /* Now, if add the current largest extent group,
1834                  * the space we found is enough,
1835                  * So, search from the smallest extent group
1836                  * to avoid waste of space
1837                  */
1838                 exts_group_tmp = (*exts_group_list_head)->prev;
1839                 do {
1840                         if (exts_group_tmp->len + len >= filesize) {
1841                                 len += exts_group_tmp->len;
1842                                 exts_group_tmp =
1843                                 get_exts_group(exts_group_list_head,
1844                                                exts_group_tmp);
1845                                 if (insert_exts_group
1846                                         (target_exts_group_list_head,
1847                                          exts_group_tmp) < 0) {
1848                                         FREE(exts_group_tmp);
1849                                         return RETURN_NG;
1850                                 }
1851                                 (*ext_count)++;
1852                                 /* The only entry go out normally */
1853                                 return RETURN_OK;
1854                         }
1855                         exts_group_tmp = exts_group_tmp->prev;
1856                 } while (exts_group_tmp !=
1857                          (*exts_group_list_head)->prev);
1859                 /* If we come here, there must be something wrong */
1860                 return RETURN_NG;
1861         }
1863         /* If we come here, there must be something wrong(space not enough) */
1864         return RETURN_NG;
1868  * check_frag_count() -         Check file frag.
1870  * @fd:                         the file's discriptor.
1871  * @extent_count:               the number of extents.
1872  */
1873 int check_frag_count(int fd, int extent_count)
1875         long long file_extent_count = RETURN_NG;
1877         file_extent_count = file_frag_count(fd);
1878         if (file_extent_count == RETURN_NG)
1879                 return RETURN_NG;
1881         if (extent_count >= file_extent_count) {
1882                 /* No improvment */
1883                 errno = ENOSPC;
1884                 return RETURN_NG;
1885         }
1887         return RETURN_OK;
1891  * defrag_proc() -              Reserve extent group and execute
1892  *                              the defrag program.
1894  * @fd:                         the file's discriptor.
1895  * @file_name                   file name.
1896  * @target_exts_group_head:     the head of the original exts_group list.
1897  * @ino:                        inode number from struct stat64.
1898  */
1899 int defrag_proc(int fd, const char *file_name,
1900                 exts_group_t *target_exts_group_head, unsigned long long ino)
1902         int ret = 0;
1903         int flag = 0;
1904         int count = 0;
1905         int percent = 0;
1906         int blocksize = 0;
1907         struct stat64   buf;
1908         exts_group_t                    *exts_group = NULL;
1909         extent_t                        *extent = NULL;
1910         struct ext4_extents_info        extents_info;
1911         struct ext4_ext_defrag_data     defrag_data;
1913         if (!target_exts_group_head)
1914                 return RETURN_NG;
1916         /* Get file size */
1917         memset(&buf, 0, sizeof(struct stat64));
1918         ret = fstat64(fd, &buf);
1919         if (ret < 0) {
1920                 if (detail_flag) {
1921                         printf("\033[79;0H\033[K[%lu/%lu]%s\n",
1922                                         defraged_file_count,
1923                                         total_count, file_name);
1924                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_INFO);
1925                 }
1926                 return RETURN_NG;
1927         }
1929         /* Get block size */
1930         ret = ioctl(fd, FIGETBSZ, &blocksize);
1931         if (ret < 0) {
1932                 if (detail_flag) {
1933                         printf("\033[79;0H\033[K[%lu/%lu]%s\n",
1934                                         defraged_file_count,
1935                                         total_count, file_name);
1936                         PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_BLOCKSIZE);
1937                 }
1938                 return RETURN_NG;
1939         }
1941         memset(&extents_info, 0, sizeof(extents_info));
1942         memset(&defrag_data, 0, sizeof(struct ext4_ext_defrag_data));
1943         extents_info.ino = 0;
1944         exts_group = target_exts_group_head;
1945         extents_info.max_entries = DEFRAG_MAX_ENT;
1946         extents_info.ino = ino;
1947         defrag_data.start_offset = 0;
1949         do {
1950                 /* Move victim extents to make sufficient space */
1951                 extent = exts_group->start;
1952                 int flush_count = 0;
1953                 do {
1954                         if (extent->tag != EXT4_EXT_USE) {
1955                                 extent = extent->next;
1956                                 continue;
1957                         }
1958                         extents_info.ino = extent->ino;
1959                         extents_info.goal = fgoal;
1960                         memcpy(extents_info.ext, &extent->data,
1961                                sizeof(struct ext4_extent_data));
1963                         extent = extent->next;
1964                         extents_info.entries = 1;
1965                         ret = ioctl(fd, EXT4_IOC_MOVE_VICTIM, &extents_info);
1966                         if (ret < 0) {
1967                                 if (errno == EACCES && detail_flag) {
1968                                         printf("\033[79;0H\033[K[%lu/%lu]%s\n",
1969                                         defraged_file_count,
1970                                         total_count, file_name);
1971                                         PRINT_ERR_MSG_WITH_ERRNO
1972                                                 (NGMSG_VICTIM_UID);
1973                                 }
1974                                 return ret;
1975                         }
1976                         count++;
1977                         if (detail_flag) {
1978                                 if (!flag) {
1979                                         printf(" Move victim extents to");
1980                                         printf(" the other block group\n");
1981                                         flag = 1;
1982                                 }
1983                                 /* moved extent info */
1984                                 if (!flush_count) {
1985                                         /* flush the defrag process info */
1986                                         printf("\033[79;0H\033[K ext%-3d",
1987                                                 count);
1988                                         printf(" phys:\t%8llu logical:\t%8u "
1989                                                 "length:\t%8d\n",
1990                                                 (extents_info.ext[0]).start,
1991                                                 (extents_info.ext[0]).block,
1992                                                 (extents_info.ext[0]).len);
1993                                         flush_count = 1;
1994                                 } else {
1995                                         printf(" ext%-3d phys:\t%8llu logical:"
1996                                                 "\t%8u length:\t%8d\n", count,
1997                                                 (extents_info.ext[0]).start,
1998                                                 (extents_info.ext[0]).block,
1999                                                 (extents_info.ext[0]).len);
2000                                 }
2001                         }
2002                 } while (extent != exts_group->end->next);
2004                 if (fsync(fd) < 0) {
2005                         if (detail_flag) {
2006                                 printf("\033[79;0H\033[K[%lu/%lu]%s\n",
2007                                         defraged_file_count,
2008                                         total_count, file_name);
2009                                 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_SYNC);
2010                         }
2011                         return ret;
2012                 }
2014                 /* Init extents_info for ioctl (RESERVE) */
2015                 extents_info.entries = 1;
2016                 extents_info.ext[0].block = exts_group->start->data.block;
2017                 extents_info.ext[0].start = exts_group->start->data.start;
2018                 extents_info.ext[0].len = exts_group->len;
2020                 /* Defrag */
2021                 ret = do_defrag(fd, exts_group, defrag_data);
2022                 if (ret < 0) {
2023                         if (detail_flag) {
2024                                 printf("\033[79;0H\033[K[%lu/%lu]%s\n",
2025                                         defraged_file_count,
2026                                         total_count, file_name);
2027                                 printf("\tDEFRAG_ERROR ret = %d\t[ NG ]\n",
2028                                         ret);
2029                         }
2030                         return ret;
2031                 }
2032                 /* Defrag success, so update offset */
2033                 defrag_data.start_offset += ret;
2034                 ret = defrag_data.start_offset;
2036                 /* Print process progress */
2037                 {
2038                         percent = ((long long)ret * blocksize * 100) /
2039                                   buf.st_size;
2040                         printf("\033[79;0H\033[K[%lu/%lu]%s:\t%d%%",
2041                                 defraged_file_count, total_count,
2042                                 file_name, min(percent, 100));
2043                         fflush(stdout);
2044                 }
2046                 exts_group = exts_group->next;
2047         } while (exts_group != target_exts_group_head);
2048         return ret;
2052  * force_defrag() -     Execute the defrag program in force defrag mode.
2054  * @fd:                 the file's descriptor.
2055  * @file_name           file name.
2056  * @buf:                a pointer of the struct stat64.
2057  * @blocksize:          block size in byte.
2058  */
2059 int force_defrag(int fd, const char *file_name,
2060                 const struct stat64 *buf, int blocksize)
2062         int     ret = 0;
2063         int     exts = 0;
2064         int     maxlen = 0;
2065         unsigned int    gnumber;
2066         unsigned long   filesize;
2067         unsigned long long      istart, iend;
2068         ext4_fsblk_t    bstart, bend;
2069         extent_t        *extlist_head = NULL;
2070         exts_group_t    *exts_group_list_head, *exts_group_list_target_head;
2071         struct ext4_super_block sb;
2073         exts_group_list_head = NULL;
2074         exts_group_list_target_head = NULL;
2076         /* Get super block info */
2077         memset(&sb, 0, sizeof(struct ext4_super_block));
2079         if (ioctl(fd, EXT4_IOC_SUPER_INFO, &sb) < 0)
2080                 return RETURN_NG;
2082         sb.s_blocks_per_group = ext2fs_swab32(sb.s_blocks_per_group);
2083         sb.s_inodes_per_group = ext2fs_swab32(sb.s_inodes_per_group);
2085         gnumber = (buf->st_ino - 1) / sb.s_inodes_per_group;
2086         istart = gnumber * sb.s_inodes_per_group;
2087         iend = istart + sb.s_inodes_per_group - 1;
2088         bstart = gnumber * sb.s_blocks_per_group;
2089         bend = bstart + sb.s_blocks_per_group - 1;
2091         /* Compute filesize in block */
2092         filesize = (buf->st_size + blocksize - 1) / blocksize;
2094         /* Get used extents in the block group */
2095         ret = get_used_extent(fd, &extlist_head, istart, iend, bstart, bend);
2096         if (ret == RETURN_NG)
2097                 goto freelist;
2099         /* Get free extents in the group */
2100         ret = get_free_extent(fd, buf->st_ino,
2101                              sb.s_blocks_per_group, &extlist_head);
2102         if (ret == RETURN_NG)
2103                 goto freelist;
2105         /* All space in this group is used by other groups' inodes */
2106         if (extlist_head == NULL) {
2107                 ret = RETURN_NG;
2108                 goto freelist;
2109         }
2111         /* Get continuous region(extents group) */
2112         ret = join_extents(extlist_head, &exts_group_list_target_head,
2113                                   &exts_group_list_head, filesize, &maxlen);
2114         if (ret == RETURN_NG)
2115                 goto freelist;
2116         if (ret == CHECK_FRAG_COUNT) {
2117                 exts = 1;
2118                 goto frag_check;
2119         }
2121         if (maxlen < filesize) {
2122                 /* No enough space */
2123                 errno = ENOSPC;
2124                 ret = RETURN_NG;
2125                 goto freelist;
2126         }
2128         if (!exts_group_list_head) {
2129                 ret = RETURN_NG;
2130                 goto freelist;
2131         }
2133         /* Find target extents group */
2134         ret = find_exts_group(&exts, filesize, &exts_group_list_head,
2135                                       &exts_group_list_target_head);
2136         if (ret == RETURN_NG)
2137                 goto freelist;
2139 frag_check:
2140         /* Check file extent count */
2141         ret = check_frag_count(fd, exts);
2142         if (ret == RETURN_NG)
2143                 goto freelist;
2145         /* Reserve extent group and execute the defrag program */
2146         ret = defrag_proc(fd, file_name,
2147                         exts_group_list_target_head, buf->st_ino);
2149 freelist:
2150         free_exts_group(exts_group_list_target_head);
2151         free_exts_group(exts_group_list_head);
2152         free_ext(extlist_head);
2153         return ret;