2 * e4defrag.c - ext4 filesystem defragmenter
5 #ifndef _LARGEFILE_SOURCE
6 #define _LARGEFILE_SOURCE
9 #ifndef _LARGEFILE64_SOURCE
10 #define _LARGEFILE64_SOURCE
13 #define _XOPEN_SOURCE 500
25 #include <sys/statfs.h>
27 #include <sys/ioctl.h>
31 #include <sys/syscall.h>
34 #define EXT4_IOC_DEFRAG _IOW('f', 10, struct ext4_ext_defrag_data)
35 #define EXT4_IOC_GROUP_INFO _IOW('f', 11, struct ext4_group_data_info)
36 #define EXT4_IOC_FREE_BLOCKS_INFO _IOW('f', 12, struct ext4_extents_info)
37 #define EXT4_IOC_EXTENTS_INFO _IOW('f', 13, struct ext4_extents_info)
38 #define EXT4_IOC_RESERVE_BLOCK _IOW('f', 14, struct ext4_extents_info)
39 #define EXT4_IOC_MOVE_VICTIM _IOW('f', 15, struct ext4_extents_info)
40 #define EXT4_IOC_BLOCK_RELEASE _IO('f', 16)
42 #define DEFRAG_MAX_ENT 32
44 /* Extent status which are used in extent_t */
45 #define EXT4_EXT_USE 0
46 #define EXT4_EXT_FREE 1
47 #define EXT4_EXT_RESERVE 2
49 /* Insert list2 after list1 */
50 #define insert(list1, list2) \
52 list2->next = list1->next; \
53 list1->next->prev = list2; \
54 list2->prev = list1; \
55 list1->next = list2; \
58 #define DEFRAG_FORCE_VICTIM 2
60 /* Magic number for ext4 */
61 #define EXT4_SUPER_MAGIC 0xEF53
63 /* Force defrag mode: Max file size in bytes (128MB) */
64 #define MAX_FILE_SIZE (unsigned long)1 << 27
66 /* Data type for filesystem-wide blocks number */
67 typedef unsigned long long ext4_fsblk_t
;
69 /* data type for file logical block number */
70 typedef unsigned int ext4_lblk_t
;
72 /* data type for block offset of block group */
73 typedef int ext4_grpblk_t
;
76 #define EXT4_IOC_FIBMAP _IOW('f', 9, ext4_fsblk_t)
78 #ifndef __NR_fadvise64
79 #define __NR_fadvise64 250
82 #ifndef __NR_sync_file_range
83 #define __NR_sync_file_range 314
86 #ifndef POSIX_FADV_DONTNEED
87 #if defined(__s390x__)
88 #define POSIX_FADV_DONTNEED 6 /* Don't need these pages. */
90 #define POSIX_FADV_DONTNEED 4 /* Don't need these pages. */
94 #ifndef SYNC_FILE_RANGE_WAIT_BEFORE
95 #define SYNC_FILE_RANGE_WAIT_BEFORE 1
97 #ifndef SYNC_FILE_RANGE_WRITE
98 #define SYNC_FILE_RANGE_WRITE 2
100 #ifndef SYNC_FILE_RANGE_WAIT_AFTER
101 #define SYNC_FILE_RANGE_WAIT_AFTER 4
111 #define FTW_OPEN_FD 2000
112 #define FILE_CHK_OK 0
113 #define FILE_CHK_NG -1
114 #define FS_EXT4 "ext4dev"
116 #define CHECK_FRAG_COUNT 1
118 /* Defrag block size, in bytes */
119 #define DEFRAG_SIZE 67108864
121 #define min(x, y) (((x) > (y)) ? (y) : (x))
123 #define PRINT_ERR_MSG(msg) fprintf(stderr, "%s\n", (msg));
124 #define PRINT_FILE_NAME(file) fprintf(stderr, "\t\t \"%s\"\n", (file));
127 "Usage : e4defrag [-v] file...| directory...| device...\n\
128 : e4defrag -f file [blocknr] \n\
129 : e4defrag -r directory... | device... \n"
131 #define MSG_R_OPTION " with regional block allocation mode.\n"
132 #define NGMSG_MTAB "\te4defrag : Can not access /etc/mtab."
133 #define NGMSG_UNMOUNT "\te4defrag : FS is not mounted."
134 #define NGMSG_EXT4 "\te4defrag : FS is not ext4 File System."
135 #define NGMSG_FS_INFO "\te4defrag : get FSInfo fail."
136 #define NGMSG_FILE_INFO "\te4defrag : get FileInfo fail."
137 #define NGMSG_FILE_OPEN "\te4defrag : open fail."
138 #define NGMSG_FILE_SYNC "\te4defrag : sync(fsync) fail."
139 #define NGMSG_FILE_DEFRAG "\te4defrag : defrag fail."
140 #define NGMSG_FILE_BLOCKSIZE "\te4defrag : can't get blocksize."
141 #define NGMSG_FILE_FIBMAP "\te4defrag : can't get block number."
142 #define NGMSG_FILE_UNREG "\te4defrag : File is not regular file."
144 #define NGMSG_FILE_LARGE \
145 "\te4defrag : Defrag size is larger than FileSystem's free space."
147 #define NGMSG_FILE_PRIORITY \
148 "\te4defrag : File is not current user's file or current user is not root."
150 #define NGMSG_FILE_LOCK "\te4defrag : File is locked."
151 #define NGMSG_FILE_BLANK "\te4defrag : File size is 0."
152 #define NGMSG_GET_LCKINFO "\te4defrag : get LockInfo fail."
154 "e4defrag : Can not process %s in regional mode.\n"
155 #define NGMSG_LOST_FOUND "\te4defrag : Can not process \"lost+found\"."
156 #define NGMSG_REALPATH "\te4defrag : Can not get full path."
157 #define NGMSG_FILE_MAP "\te4defrag : get file map fail."
158 #define NGMSG_FILE_DROP_BUFFER "\te4defrag : free page fail."
159 #define NGMSG_FADVISE_SYSCALL "\tfadvise fail."
161 struct ext4_extent_data
{
162 ext4_lblk_t block
; /* start logical block number */
163 ext4_fsblk_t start
; /* start physical block number */
164 int len
; /* blocks count */
167 /* Used for defrag */
168 struct ext4_ext_defrag_data
{
169 ext4_lblk_t start_offset
; /* start offset to defrag in blocks */
170 ext4_lblk_t defrag_size
; /* size of defrag in blocks */
171 ext4_fsblk_t goal
; /* block offset for allocation */
172 int flag
; /* free space mode flag */
173 struct ext4_extent_data ext
;
176 struct ext4_group_data_info
{
177 int s_blocks_per_group
; /* blocks per group */
178 int s_inodes_per_group
; /* inodes per group */
181 struct ext4_extents_info
{
182 unsigned long long ino
; /* inode number */
183 int max_entries
; /* maximum extents count */
184 int entries
; /* extent number/count */
185 ext4_lblk_t f_offset
; /* file offset */
186 ext4_grpblk_t g_offset
; /* group offset */
188 struct ext4_extent_data ext
[DEFRAG_MAX_ENT
];
191 typedef struct extent
{
193 unsigned long tag
; /* Extent status */
194 unsigned long ino
; /* File's inode number */
195 struct ext4_extent_data data
; /* Extent belong to file */
199 typedef struct exts_group
{
200 struct exts_group
*prev
;
201 extent_t
*start
; /* Start ext */
202 extent_t
*end
; /* End ext */
203 int len
; /* Length of this continuous region */
204 struct exts_group
*next
;
207 typedef struct extent_wrap
{
208 struct extent_wrap
*prev
, *next
;
209 struct extent
*group_ext
;
217 char lost_found_dir
[PATH_MAX
+ 1];
219 ext4_fsblk_t fgoal
= -1;
222 * fadvise() - advise operater system process page cache.
224 * @fd: file descriptor.
225 * @offset: file offset.
227 * @advise: process flag.
229 int fadvise(int fd
, loff_t offset
, size_t len
, int advise
)
231 return syscall(__NR_fadvise64
, fd
, offset
, len
, advise
);
235 * sync_file_range() - sync file region.
237 * @fd: file descriptor.
238 * @offset: file offset.
239 * @length: area length.
240 * @advise: process flag.
242 int sync_file_range(int fd
, loff_t offset
, loff_t length
, unsigned int flag
)
244 return syscall(__NR_sync_file_range
, fd
, offset
, length
, flag
);
248 * page_in_core() - get information on whether pages are in core.
250 * @fd: file descriptor.
251 * @defrag_data: data used for defrag.
252 * @vec: page state array.
253 * @page_num: page number.
255 int page_in_core(int fd
, struct ext4_ext_defrag_data defrag_data
,
256 unsigned char **vec
, unsigned long *page_num
)
259 int pagesize
= getpagesize();
261 loff_t offset
, end_offset
, length
;
263 if (vec
== NULL
|| *vec
!= NULL
) {
267 if (ioctl(fd
, FIGETBSZ
, &blocksize
) < 0) {
271 /*in mmap, offset should be a multiple of the page size */
272 offset
= defrag_data
.start_offset
* blocksize
;
273 length
= defrag_data
.defrag_size
* blocksize
;
274 end_offset
= offset
+ length
;
275 /* round the offset down to the nearest multiple of pagesize */
276 offset
= (offset
/ pagesize
) * pagesize
;
277 length
= end_offset
- offset
;
279 page
= mmap(NULL
, length
, PROT_READ
, MAP_SHARED
, fd
, offset
);
280 if (page
== MAP_FAILED
) {
285 *page_num
= (length
+ pagesize
- 1) / pagesize
;
286 *vec
= (unsigned char *)calloc(*page_num
, 1);
291 /* get information on whether pages are in core */
292 if (mincore(page
, (size_t)length
, *vec
) == -1) {
299 if (munmap(page
, length
) == -1) {
310 * defrag_fadvise() - predeclare an access pattern for file data.
312 * @fd: file descriptor.
313 * @defrag_data: data used for defrag.
314 * @vec: page state array.
315 * @page_num: page number.
317 int defrag_fadvise(int fd
, struct ext4_ext_defrag_data defrag_data
,
318 unsigned char *vec
, unsigned long page_num
)
322 int pagesize
= getpagesize();
323 int fadvise_flag
= POSIX_FADV_DONTNEED
;
324 int sync_flag
= SYNC_FILE_RANGE_WAIT_BEFORE
| SYNC_FILE_RANGE_WRITE
|
325 SYNC_FILE_RANGE_WAIT_AFTER
;
329 if (ioctl(fd
, FIGETBSZ
, &blocksize
) < 0) {
333 offset
= (loff_t
)defrag_data
.start_offset
* blocksize
;
334 offset
= (offset
/ pagesize
) * pagesize
;
336 /* sync file for fadvise process */
337 if (sync_file_range(fd
, offset
, (loff_t
)pagesize
*page_num
, sync_flag
)
342 /* try to release buffer cache this process used,
343 * then other process can use the released buffer */
344 for (i
= 0; i
< page_num
; i
++) {
345 if ((vec
[i
] & 0x1) == 0) {
349 if (fadvise(fd
, offset
, pagesize
, fadvise_flag
) != 0) {
350 if (detail_flag
&& flag
) {
351 perror(NGMSG_FADVISE_SYSCALL
);
361 * check_free_size() - Check if there's enough disk space.
363 * @fd: the file's descriptor.
364 * @buf: the pointer of the struct stat64.
366 int check_free_size(int fd
, const struct stat64
*buf
)
369 off64_t free_size
= 0;
372 /* target file size */
375 if (fstatfs(fd
, &fsbuf
) < 0) {
377 perror(NGMSG_FS_INFO
);
382 /* Compute free space for root and normal user separately */
383 if (getuid() == ROOT_UID
) {
384 free_size
= (off64_t
)fsbuf
.f_bsize
* fsbuf
.f_bfree
;
386 free_size
= (off64_t
)fsbuf
.f_bsize
* fsbuf
.f_bavail
;
389 if (free_size
>= size
) {
396 int file_check(int fd
, const struct stat64
*buf
, const char *file_name
);
397 int force_defrag(int fd
, const struct stat64
*buf
, int blocksize
);
400 * ftw_fn() - Check file attributes and ioctl call to avoid
401 * illegal operations.
403 * @file: the file's name.
404 * @buf: the pointer of the struct stat64.
406 * @ftwbuf: the pointer of a struct FTW.
409 ftw_fn(const char *file
, const struct stat64
*buf
, int flag
, struct FTW
*ftwbuf
)
414 int defraged_size
= 0;
416 int pos
, file_frags_start
, file_frags_end
;
417 unsigned long page_num
;
418 unsigned char *vec
= NULL
;
420 struct ext4_ext_defrag_data df_data
;
421 struct ext4_extents_info extents_info
;
423 if (lost_found_dir
[0] != '\0' &&
424 !memcmp(file
, lost_found_dir
, strnlen(lost_found_dir
, PATH_MAX
))) {
426 PRINT_ERR_MSG(NGMSG_LOST_FOUND
);
427 PRINT_FILE_NAME(file
);
434 if ((fd
= open64(file
, O_RDONLY
)) < 0) {
436 perror(NGMSG_FILE_OPEN
);
437 PRINT_FILE_NAME(file
);
442 if (file_check(fd
, buf
, file
) == FILE_CHK_NG
) {
449 perror(NGMSG_FILE_SYNC
);
450 PRINT_FILE_NAME(file
);
456 if (ioctl(fd
, FIGETBSZ
, &blocksize
) < 0) {
458 perror(NGMSG_FILE_BLOCKSIZE
);
459 PRINT_FILE_NAME(file
);
464 /* Ioctl call does the actual defragment job. */
465 df_data
.start_offset
= 0;
472 /* count file frags before defrag if detail_flag set */
475 file_frags_start
= 0;
476 memset(&extents_info
, 0,
477 sizeof(struct ext4_extents_info
));
478 extents_info
.ino
= buf
->st_ino
;
479 extents_info
.max_entries
= DEFRAG_MAX_ENT
;
480 extents_info
.entries
= 0;
483 extents_info
.entries
+= pos
;
484 pos
= extents_info
.entries
;
485 ret
= ioctl(fd
, EXT4_IOC_EXTENTS_INFO
,
488 perror(NGMSG_FILE_INFO
);
489 PRINT_FILE_NAME(file
);
493 file_frags_start
+= extents_info
.entries
;
494 } while (extents_info
.entries
== DEFRAG_MAX_ENT
&&
498 /* print process progress */
499 printf("\tprocessing -------> %s:\n", file
);
500 percent
= (start
* 100) / buf
->st_size
;
501 printf("\033[79;16H\033[K progressing ====> %d%%", percent
);
505 df_data
.defrag_size
=
506 (min((buf
->st_size
- start
),
507 DEFRAG_SIZE
) + blocksize
- 1) /
510 ret
= page_in_core(fd
, df_data
, &vec
, &page_num
);
511 if (ret
== RETURN_NG
) {
513 perror(NGMSG_FILE_MAP
);
514 PRINT_FILE_NAME(file
);
522 /* EXT4_IOC_DEFRAG */
523 defraged_size
= ioctl(fd
, EXT4_IOC_DEFRAG
, &df_data
);
526 ret
= defrag_fadvise(fd
, df_data
, vec
, page_num
);
531 if (ret
== RETURN_NG
) {
533 perror(NGMSG_FILE_DROP_BUFFER
);
534 PRINT_FILE_NAME(file
);
542 if ((defraged_size
< 0) && (force_flag
== 1) &&
543 (errno
== ENOSPC
) && buf
->st_size
<= MAX_FILE_SIZE
) {
544 defraged_size
= force_defrag(fd
, buf
,
546 if (defraged_size
* blocksize
>= buf
->st_size
) {
547 /* Whole file is enforcedly defraged */
550 defraged_size
= RETURN_NG
;
553 if (defraged_size
< 0) {
555 perror(NGMSG_FILE_DEFRAG
);
556 PRINT_FILE_NAME(file
);
563 df_data
.start_offset
+= defraged_size
;
564 start
= df_data
.start_offset
* blocksize
;
566 /* print process progress */
567 if (start
> ((long long)1 << 56)) {
568 /* consider overflow("start * 100" beyond 64bits) */
570 percent
= (start
* 100) / (buf
->st_size
>> 8);
572 percent
= (start
* 100) / buf
->st_size
;
575 /* disk space file used is bigger than logical size */
579 printf("\033[79;16H\033[K progressing ====> %d%%",
584 if (start
>= buf
->st_size
) {
589 /* count file frags after defrag and print extents info */
594 extents_info
.entries
= 0;
597 extents_info
.entries
+= pos
;
598 pos
= extents_info
.entries
;
599 ret
= ioctl(fd
, EXT4_IOC_EXTENTS_INFO
,
603 perror(NGMSG_FILE_INFO
);
604 PRINT_FILE_NAME(file
);
608 file_frags_end
+= extents_info
.entries
;
609 } while (extents_info
.entries
== DEFRAG_MAX_ENT
&&
612 printf("\n\t\textents: %d ==> %d",
613 file_frags_start
, file_frags_end
);
620 PRINT_ERR_MSG(NGMSG_FILE_UNREG
);
621 PRINT_FILE_NAME(file
);
629 * file_check() - Check file's attributes.
631 * @fd: the file's descriptor.
632 * @buf: a pointer of the struct stat64.
633 * @file_name: the file's name.
635 int file_check(int fd
, const struct stat64
*buf
, const char *file_name
)
639 lock
.l_type
= F_WRLCK
; /* Write-lock check is more reliable. */
641 lock
.l_whence
= SEEK_SET
;
645 if (S_ISREG(buf
->st_mode
) == 0) {
647 PRINT_ERR_MSG(NGMSG_FILE_UNREG
);
648 PRINT_FILE_NAME(file_name
);
654 if (check_free_size(fd
, buf
) == RETURN_NG
) {
657 PRINT_ERR_MSG(NGMSG_FILE_LARGE
);
658 PRINT_FILE_NAME(file_name
);
664 if (getuid() != ROOT_UID
&&
665 buf
->st_uid
!= getuid()) {
667 PRINT_ERR_MSG(NGMSG_FILE_PRIORITY
);
668 PRINT_FILE_NAME(file_name
);
674 if (fcntl(fd
, F_GETLK
, &lock
) < 0) {
676 perror(NGMSG_GET_LCKINFO
);
677 PRINT_FILE_NAME(file_name
);
680 } else if (lock
.l_type
!= F_UNLCK
) {
682 PRINT_ERR_MSG(NGMSG_FILE_LOCK
);
683 PRINT_FILE_NAME(file_name
);
689 if (buf
->st_size
== 0) {
691 PRINT_ERR_MSG(NGMSG_FILE_BLANK
);
692 PRINT_FILE_NAME(file_name
);
701 * is_ext4() - Whether on an ext4 filesystem.
703 * @filename: the file's name.
705 int is_ext4(const char *filename
)
709 char *mnt_type
= NULL
;
710 char *mtab
= MOUNTED
; /* Refer to /etc/mtab */
711 char file_path
[PATH_MAX
+ 1];
712 struct mntent
*mnt
= NULL
;
716 if (realpath(filename
, file_path
) == NULL
) {
717 perror(NGMSG_REALPATH
);
718 PRINT_FILE_NAME(filename
);
722 if (statfs(file_path
, &buffs
) < 0) {
723 perror(NGMSG_FS_INFO
);
724 PRINT_FILE_NAME(filename
);
728 if (buffs
.f_type
!= EXT4_SUPER_MAGIC
) {
729 PRINT_ERR_MSG(NGMSG_EXT4
);
733 if ((fp
= setmntent(mtab
, "r")) == NULL
) {
739 while ((mnt
= getmntent(fp
)) != NULL
) {
740 len
= strlen(mnt
->mnt_dir
);
741 if (memcmp(file_path
, mnt
->mnt_dir
, len
) == 0) {
744 mnt_type
= realloc(mnt_type
,
745 strlen(mnt
->mnt_type
) + 1);
750 strcpy(mnt_type
, mnt
->mnt_type
);
751 strncpy(lost_found_dir
, mnt
->mnt_dir
, PATH_MAX
);
756 if (strcmp(mnt_type
, FS_EXT4
) == 0) {
767 PRINT_ERR_MSG(NGMSG_EXT4
);
773 * get_mount_point() - Get device's mount point.
775 * @devname: the device's name.
776 * @mount_point: the mount point.
777 * @dir_path_len: the length of directory.
779 int get_mount_point(const char *devname
, char *mount_point
, int dir_path_len
)
781 char *mtab
= MOUNTED
; /* Refer to /etc/mtab */
783 struct mntent
*mnt
= NULL
;
785 if ((fp
= setmntent(mtab
, "r")) == NULL
) {
790 while ((mnt
= getmntent(fp
)) != NULL
) {
791 if (strcmp(devname
, mnt
->mnt_fsname
) == 0) {
793 if (strcmp(mnt
->mnt_type
, FS_EXT4
) == 0) {
794 strncpy(mount_point
, mnt
->mnt_dir
,
798 PRINT_ERR_MSG(NGMSG_EXT4
);
803 PRINT_ERR_MSG(NGMSG_UNMOUNT
);
808 * main() - ext4 online defrag.
810 * @argc: the number of parameter.
811 * @argv[]: the pointer array of parameter.
813 int main(int argc
, char *argv
[])
822 char dir_name
[PATH_MAX
+ 1];
830 flags
|= FTW_PHYS
; /* Do not follow symlink */
831 flags
|= FTW_MOUNT
; /* Stay within the same filesystem */
832 /* Parse arguments */
833 if (argc
== 1 || (argc
== 2 && argv
[1][0] == '-')) {
838 while ((opt
= getopt(argc
, argv
, "rvf")) != EOF
) {
853 printf("Illegal argument\n\n");
860 res_strlen
= strlen(argv
[3]);
861 for (res_strlen
-= 1; res_strlen
>= 0;
863 if (!isdigit(argv
[3][res_strlen
])) {
864 printf("Illegal argument\n\n");
870 fgoal
= strtoul(argv
[3], NULL
, 0);
872 printf("block num shold be < 32bit\n");
886 for (; i
< argc
; i
++) {
889 memset(dir_name
, 0, PATH_MAX
+ 1);
890 memset(lost_found_dir
, 0, PATH_MAX
+ 1);
892 if (force_flag
&& i
== 3)
895 if (lstat64(argv
[i
], &buf
) < 0) {
896 perror(NGMSG_FILE_INFO
);
897 PRINT_FILE_NAME(argv
[i
]);
901 /* Regular file is acceptalbe with force mode */
902 if (force_flag
&& !S_ISREG(buf
.st_mode
)) {
903 printf("Inappropriate file type \n\n");
909 if (S_ISBLK(buf
.st_mode
)) {
911 if (get_mount_point(argv
[i
], dir_name
, PATH_MAX
) ==
915 printf("Start defragment for device(%s)\n", argv
[i
]);
916 } else if (S_ISDIR(buf
.st_mode
)) {
919 if (access(argv
[i
], R_OK
) < 0) {
923 strcpy(dir_name
, argv
[i
]);
924 } else if (S_ISREG(buf
.st_mode
)) {
929 PRINT_ERR_MSG(NGMSG_FILE_UNREG
);
930 PRINT_FILE_NAME(argv
[i
]);
934 /* Device's ext4 check is in get_mount_point() */
935 if (arg_type
== FILENAME
|| arg_type
== DIRNAME
) {
936 if (is_ext4(argv
[i
]) == RETURN_NG
) {
939 if (realpath(argv
[i
], dir_name
) == NULL
) {
940 perror(NGMSG_REALPATH
);
941 PRINT_FILE_NAME(argv
[i
]);
948 printf("Start defragment for directory(%s)\n",
951 int mount_dir_len
= 0;
952 mount_dir_len
= strnlen(lost_found_dir
, PATH_MAX
);
954 strncat(lost_found_dir
, "/lost+found",
955 PATH_MAX
- strnlen(lost_found_dir
, PATH_MAX
));
957 /* not the case("e4defrag mount_piont_dir") */
958 if (dir_name
[mount_dir_len
] != '\0') {
959 /* "e4defrag mount_piont_dir/lost+found" */
960 /* or "e4defrag mount_piont_dir/lost+found/" */
961 if (strncmp(lost_found_dir
, dir_name
,
962 strnlen(lost_found_dir
,
964 (dir_name
[strnlen(lost_found_dir
,
965 PATH_MAX
)] == '\0' ||
966 dir_name
[strnlen(lost_found_dir
,
967 PATH_MAX
)] == '/')) {
968 PRINT_ERR_MSG(NGMSG_LOST_FOUND
);
969 PRINT_FILE_NAME(argv
[i
]);
973 /* "e4defrag mount_piont_dir/else_dir" */
974 memset(lost_found_dir
, 0, PATH_MAX
+ 1);
977 if (arg_type
== DEVNAME
) {
978 strncpy(lost_found_dir
, dir_name
,
979 strnlen(dir_name
, PATH_MAX
));
980 strncat(lost_found_dir
, "/lost+found/",
981 PATH_MAX
- strnlen(lost_found_dir
,
985 /* Regional block allocation */
987 printf(MSG_R_OPTION
);
989 if ((fd
= open64(dir_name
, O_RDONLY
)) < 0) {
991 perror(NGMSG_FILE_OPEN
);
992 PRINT_FILE_NAME(dir_name
);
998 if ((ret
= ioctl(fd
, EXT4_IOC_FIBMAP
,
1000 perror(NGMSG_FILE_FIBMAP
);
1001 PRINT_FILE_NAME(dir_name
);
1008 /* File tree walk */
1009 nftw64(dir_name
, ftw_fn
, FTW_OPEN_FD
, flags
);
1010 printf("\tTotal:\t\t%12d\n", amount_cnt
);
1011 printf("\tSuccess:\t%12d\n", succeed_cnt
);
1012 printf("\tFailure:\t%12d\n",
1013 amount_cnt
- succeed_cnt
);
1016 strncat(lost_found_dir
, "/lost+found/",
1017 PATH_MAX
- strnlen(lost_found_dir
,
1019 if (strncmp(lost_found_dir
, dir_name
,
1020 strnlen(lost_found_dir
,
1022 PRINT_ERR_MSG(NGMSG_LOST_FOUND
);
1023 PRINT_FILE_NAME(argv
[i
]);
1027 if (regional_flag
) {
1028 fprintf(stderr
, NGMSG_TYPE
, argv
[i
]);
1031 detail_tmp
= detail_flag
;
1033 printf("Start defragment for %s\n", argv
[i
]);
1034 /* Single file process */
1035 ftw_fn(argv
[i
], &buf
, FTW_F
, NULL
);
1036 if (succeed_cnt
!= 0) {
1038 "\tSUCCESS\t:file defrag success.\n"
1041 detail_flag
= detail_tmp
;
1045 if (succeed_cnt
!= 0)
1055 * insert_extent() - Sequentially insert extent by physical block number.
1057 * @extlist_head: the head of an extent list.
1058 * @ext: the extent element which will be inserted.
1060 int insert_extent(extent_t
**extlist_head
, extent_t
*ext
)
1062 extent_t
*ext_tmp
= *extlist_head
;
1068 if (*extlist_head
== NULL
) {
1069 (*extlist_head
) = ext
;
1070 (*extlist_head
)->prev
= *extlist_head
;
1071 (*extlist_head
)->next
= *extlist_head
;
1075 if (ext
->data
.start
<= ext_tmp
->data
.start
) {
1076 /* Insert before head */
1077 if (ext_tmp
->data
.start
< ext
->data
.start
+ ext
->data
.len
) {
1082 *extlist_head
= ext
;
1084 /* Insert into the middle or last of the list */
1086 if (ext
->data
.start
< ext_tmp
->data
.start
) {
1089 ext_tmp
= ext_tmp
->next
;
1090 } while (ext_tmp
!= (*extlist_head
));
1091 if (ext
->data
.start
<
1092 ext_tmp
->prev
->data
.start
+ ext_tmp
->prev
->data
.len
) {
1096 if (ext_tmp
!= *extlist_head
&&
1097 ext_tmp
->data
.start
< ext
->data
.start
+ ext
->data
.len
) {
1102 ext_tmp
= ext_tmp
->prev
;
1103 /* Insert "ext" after "ext_tmp" */
1104 insert(ext_tmp
, ext
);
1109 * insert_exts_group() - Insert a exts_group in decreasing order of length.
1111 * @exts_group_list_head: the head of a exts_group list.
1112 * @exts_group: the exts_group element which will be inserted.
1114 int insert_exts_group(exts_group_t
**exts_group_list_head
,
1115 exts_group_t
*exts_group
)
1117 exts_group_t
*exts_group_tmp
= NULL
;
1119 if (exts_group
== NULL
) {
1123 /* Initialize list */
1124 if (*exts_group_list_head
== NULL
) {
1125 (*exts_group_list_head
) = exts_group
;
1126 (*exts_group_list_head
)->prev
= *exts_group_list_head
;
1127 (*exts_group_list_head
)->next
= *exts_group_list_head
;
1131 if (exts_group
->len
>= (*exts_group_list_head
)->len
) {
1132 /* Insert before exts_group_list_head */
1133 exts_group_tmp
= (*exts_group_list_head
)->prev
;
1134 insert(exts_group_tmp
, exts_group
);
1135 *exts_group_list_head
= exts_group
;
1139 /* Find insertion positon */
1140 for (exts_group_tmp
= (*exts_group_list_head
)->next
;
1141 exts_group_tmp
!= *exts_group_list_head
;
1142 exts_group_tmp
= exts_group_tmp
->next
) {
1143 if (exts_group_tmp
->len
< exts_group
->len
) {
1147 exts_group_tmp
= exts_group_tmp
->prev
;
1148 insert(exts_group_tmp
, exts_group
);
1154 * get_exts_group() - Get element from the exts_group list.
1156 * @exts_group_list_head: the head of a exts_group list.
1157 * @exts_group: the exts_group element which will be geted.
1159 exts_group_t
*get_exts_group(exts_group_t
**exts_group_list_head
,
1160 exts_group_t
*exts_group
)
1162 if (exts_group
== NULL
|| *exts_group_list_head
== NULL
) {
1165 /* Keep "exts_group_list_head" point to the largest extent group*/
1166 if (exts_group
== *exts_group_list_head
) {
1167 *exts_group_list_head
= exts_group
->next
;
1169 if (*exts_group_list_head
== (*exts_group_list_head
)->next
&&
1170 exts_group
== *exts_group_list_head
) {
1171 /* Delete the last element in the list */
1172 *exts_group_list_head
= NULL
;
1174 exts_group
->prev
->next
= exts_group
->next
;
1175 exts_group
->next
->prev
= exts_group
->prev
;
1180 * free_exts_group() - Free the exts_group.
1182 * @*exts_group_list_head: the exts_group list head which will be free.
1185 void free_exts_group(exts_group_t
*exts_group_list_head
)
1187 exts_group_t
*exts_group_tmp
= NULL
;
1189 if (exts_group_list_head
== NULL
) {
1192 while (exts_group_list_head
->next
!= exts_group_list_head
) {
1193 exts_group_tmp
= exts_group_list_head
;
1194 exts_group_list_head
->prev
->next
= exts_group_list_head
->next
;
1195 exts_group_list_head
->next
->prev
= exts_group_list_head
->prev
;
1196 exts_group_list_head
= exts_group_list_head
->next
;
1197 free(exts_group_tmp
);
1199 free(exts_group_list_head
);
1203 * free_ext() - Free the extent list.
1205 * @extent_list_head: the extent list head of which will be free.
1207 void free_ext(extent_t
*extent_list_head
)
1209 extent_t
*extent_tmp
= NULL
;
1211 if (extent_list_head
== NULL
) {
1214 while (extent_list_head
->next
!= extent_list_head
) {
1215 extent_tmp
= extent_list_head
;
1216 extent_list_head
->prev
->next
= extent_list_head
->next
;
1217 extent_list_head
->next
->prev
= extent_list_head
->prev
;
1218 extent_list_head
= extent_list_head
->next
;
1221 free(extent_list_head
);
1225 * move_wrap() - Move a ext_wrap from one list to another.
1227 * @from: the list which will be moved from.
1228 * @to: the list which will be moved to.
1229 * @entry: the ext_wrap which will be moved.
1231 int move_wrap(extent_wrap_t
**from
, extent_wrap_t
**to
,
1232 extent_wrap_t
*entry
)
1234 if (!to
|| !entry
) {
1237 if (from
&& *from
== entry
) {
1238 if ((*from
)->next
== *from
) {
1241 *from
= (*from
)->next
;
1244 entry
->next
->prev
= entry
->prev
;
1245 entry
->prev
->next
= entry
->next
;
1248 (*to
)->prev
= (*to
)->next
= *to
;
1251 entry
->prev
= (*to
)->prev
;
1252 (*to
)->prev
->next
= entry
;
1253 (*to
)->prev
= entry
;
1259 * mark_wrap() - Mark extent status as "EXT4_EXT_RESERVE".
1261 * @ext_wrap_list: the ext_wrap list which will be marked.
1263 void mark_wrap(extent_wrap_t
*ext_wrap_list
)
1265 extent_wrap_t
*wrap
= ext_wrap_list
;
1267 if (!ext_wrap_list
) {
1271 wrap
->group_ext
->tag
|= EXT4_EXT_RESERVE
;
1273 } while (wrap
!= ext_wrap_list
);
1277 * free_wrap_list() - Free the ext_wrap list.
1279 * @ext_wrap_head: the ext_wrap list head which will be free.
1281 void free_wrap_list(extent_wrap_t
**ext_wrap_head
)
1283 extent_wrap_t
*wrap
, *ext_wrap_tmp
;
1285 if (!ext_wrap_head
|| !(*ext_wrap_head
)) {
1288 wrap
= *ext_wrap_head
;
1290 ext_wrap_tmp
= wrap
;
1293 } while (wrap
!= *ext_wrap_head
);
1294 *ext_wrap_head
= NULL
;
1298 * do_defrag() - Execute the defrag program.
1300 * @fd: the file's descriptor.
1301 * @exts_group: the exts_group which will be defraged.
1302 * @defrag_data: the data which will be defraged.
1304 static inline int do_defrag(int fd
, exts_group_t
*exts_group
,
1305 struct ext4_ext_defrag_data defrag_data
)
1308 int defraged_size
= 0;
1309 int fadvise_ret
= 0;
1310 unsigned long page_num
;
1311 unsigned char *vec
= NULL
;
1312 extent_t
*extent
= NULL
;
1315 defrag_data
.ext
.start
= exts_group
->start
->data
.start
;
1316 defrag_data
.ext
.len
= exts_group
->len
;
1317 defrag_data
.ext
.block
= 0;
1318 defrag_data
.defrag_size
= exts_group
->len
;
1319 defrag_data
.flag
= DEFRAG_FORCE_VICTIM
;
1320 defrag_data
.goal
= exts_group
->start
->data
.start
;
1322 if (page_in_core(fd
, defrag_data
, &vec
, &page_num
) == RETURN_NG
) {
1326 defraged_size
= ioctl(fd
, EXT4_IOC_DEFRAG
, &defrag_data
);
1329 fadvise_ret
= defrag_fadvise(fd
, defrag_data
, vec
, page_num
);
1333 if (fadvise_ret
== RETURN_NG
|| defraged_size
< 0) {
1337 /* Release reserved sign */
1338 extent
= exts_group
->start
;
1340 extent
->tag
&= ~EXT4_EXT_RESERVE
;
1341 extent
= extent
->next
;
1342 } while (extent
!= exts_group
->end
->next
);
1344 ret
+= defraged_size
;
1350 * get_used_extent() - Get used extent in the block group.
1352 * @fd: the file's descriptor.
1353 * @ext_list_head: the head of the extent list.
1354 * @istart: the start of the inode.
1355 * @iend: the end of the inode.
1356 * @bstart: the start of the block.
1357 * @bend: the end of the block.
1359 int get_used_extent(int fd
, extent_t
**ext_list_head
,
1360 unsigned long long istart
, unsigned long long iend
,
1361 ext4_fsblk_t bstart
, ext4_fsblk_t bend
)
1363 struct ext4_extents_info extents_info
;
1364 unsigned long long inode
;
1368 memset(&extents_info
, 0, sizeof(struct ext4_extents_info
));
1369 extents_info
.max_entries
= DEFRAG_MAX_ENT
;
1371 for (inode
= istart
; inode
<= iend
; inode
++) {
1372 extents_info
.ino
= inode
;
1373 extents_info
.entries
= 0;
1376 /* Get extents info */
1378 extents_info
.entries
+= pos
;/* Offset */
1379 pos
= extents_info
.entries
;
1380 memset(extents_info
.ext
, 0,
1381 sizeof(struct ext4_extent_data
) *
1383 ret
= ioctl(fd
, EXT4_IOC_EXTENTS_INFO
, &extents_info
);
1385 if (errno
== ENOENT
) {
1388 /* Without ENOENT case*/
1393 for (i
= 0; i
< extents_info
.entries
; i
++) {
1394 extent_t
*extent
= NULL
;
1395 /* Is this extent in current block group? */
1396 if (extents_info
.ext
[i
].start
< bstart
||
1397 extents_info
.ext
[i
].start
> bend
) {
1400 extent
= malloc(sizeof(extent_t
));
1401 if (extent
== NULL
) {
1404 memset(extent
, 0, sizeof(extent_t
));
1405 memcpy(&(extent
->data
), &extents_info
.ext
[i
],
1406 sizeof(struct ext4_extent_data
));
1407 extent
->ino
= inode
;
1408 if (insert_extent(ext_list_head
, extent
) < 0) {
1415 } while (extents_info
.entries
== DEFRAG_MAX_ENT
&& ret
== 0);
1419 if (errno
== ENOENT
) {
1427 * get_free_extent() - Get used extent in the block group.
1429 * @fd: the file's descriptor.
1430 * @inode: inode number from struct stat64.
1431 * @blocks_per_group: the block number of each block group.
1432 * @ext_list_head: the head of the extent list.
1434 int get_free_extent(int fd
, unsigned long long inode
,
1435 int blocks_per_group
, extent_t
**ext_list_head
)
1437 ext4_grpblk_t pos
= 0;
1438 struct ext4_extents_info extents_info
;
1440 memset(&extents_info
, 0, sizeof(struct ext4_extents_info
));
1441 extents_info
.ino
= inode
;
1442 extents_info
.max_entries
= DEFRAG_MAX_ENT
;
1443 while (pos
< blocks_per_group
) {
1445 if (ioctl(fd
, EXT4_IOC_FREE_BLOCKS_INFO
, &extents_info
) < 0) {
1449 * No free extent after the logical block number "pos".
1450 * In other word, offset this time equals to prev recursion.
1453 extents_info
.ext
[i
].len
!= 0 && i
< DEFRAG_MAX_ENT
; i
++) {
1454 /* Alloc list node store extent */
1455 extent_t
*extent
= NULL
;
1456 extent
= malloc(sizeof(extent_t
));
1457 if (extent
== NULL
) {
1460 memset(extent
, 0, sizeof(extent_t
));
1461 memcpy(&(extent
->data
), &(extents_info
.ext
[i
]),
1462 sizeof(struct ext4_extent_data
));
1463 extent
->tag
= EXT4_EXT_FREE
;/* Free extent */
1464 if (insert_extent(ext_list_head
, extent
) < 0) {
1472 * No free extent after the logical block number "pos".
1473 * In other word, offset this time equals to prev recursion.
1475 if (pos
== extents_info
.g_offset
) {
1478 if (i
< DEFRAG_MAX_ENT
) {
1481 /* Record the offset of logical block number this time */
1482 pos
= extents_info
.g_offset
;
1483 memset(extents_info
.ext
, 0,
1484 sizeof(struct ext4_extent_data
) * DEFRAG_MAX_ENT
);
1491 * join_extents() - Find continuous region(exts_group).
1493 * @ext_list_head: the head of the extent list.
1494 * @target_exts_group_list_head:the head of the target exts_group list.
1495 * @exts_group_list_head: the head of the original exts_group list.
1496 * @filesize: the file's descriptor.
1497 * @max: the max size of free space.
1499 int join_extents(extent_t
*ext_list_head
,
1500 exts_group_t
**target_exts_group_list_head
,
1501 exts_group_t
**exts_group_list_head
,
1502 unsigned long filesize
, int *max
)
1505 extent_t
*ext_start
, *extent_tmp
;
1507 ext_start
= extent_tmp
= ext_list_head
;
1509 len
= ext_list_head
->data
.len
;
1510 extent_tmp
= extent_tmp
->next
;
1512 if (len
>= filesize
) {
1515 * one extent group is enough for defrag, so return.
1517 exts_group_t
*exts_group_tmp
= NULL
;
1518 exts_group_tmp
= malloc(sizeof(exts_group_t
));
1519 if (!exts_group_tmp
) {
1522 exts_group_tmp
->prev
= exts_group_tmp
->next
= NULL
;
1523 exts_group_tmp
->start
= ext_start
;
1524 exts_group_tmp
->end
= extent_tmp
->prev
;
1525 exts_group_tmp
->len
= len
;
1526 if (insert_exts_group(target_exts_group_list_head
,
1527 exts_group_tmp
) < 0) {
1528 if (exts_group_tmp
) {
1529 free(exts_group_tmp
);
1533 return CHECK_FRAG_COUNT
;
1536 * This extent and previous extent is not continuous,
1537 * so, all previous extents is treated as an extent group.
1539 if ((extent_tmp
->prev
->data
.start
+ extent_tmp
->prev
->data
.len
)
1540 != extent_tmp
->data
.start
) {
1541 exts_group_t
*exts_group_tmp
= NULL
;
1542 exts_group_tmp
= malloc(sizeof(exts_group_t
));
1543 if (exts_group_tmp
== NULL
) {
1546 memset(exts_group_tmp
, 0, sizeof(exts_group_t
));
1547 exts_group_tmp
->len
= len
;
1548 exts_group_tmp
->start
= ext_start
;
1549 exts_group_tmp
->end
= extent_tmp
->prev
;
1551 if (insert_exts_group(exts_group_list_head
,
1552 exts_group_tmp
) < 0) {
1553 if (exts_group_tmp
) {
1554 free(exts_group_tmp
);
1559 ext_start
= extent_tmp
;
1560 len
= extent_tmp
->data
.len
;
1561 extent_tmp
= extent_tmp
->next
;
1565 * This extent and previous extent is continuous,
1566 * so, they belong to the same extent group, and we check
1567 * if the next extent belong to the same extent group.
1569 len
+= extent_tmp
->data
.len
;
1570 extent_tmp
= extent_tmp
->next
;
1571 } while (extent_tmp
!= ext_list_head
->next
);
1577 *find_exts_group() - Find target exts_group.
1579 * @ext_count: the number of extents.
1580 * @filesize: the file's size.
1581 * @exts_group_list_head: the head of the original exts_group list
1582 * @target_exts_group_list_head: the head of the target exts_group list.
1584 int find_exts_group(int *ext_count
, unsigned long filesize
,
1585 exts_group_t
**exts_group_list_head
,
1586 exts_group_t
**target_exts_group_list_head
)
1590 len
= 0;/* Blocks we found for target file */
1592 if (!(*exts_group_list_head
)) {
1596 while (*exts_group_list_head
) {
1597 exts_group_t
*exts_group_tmp
;
1598 if ((*exts_group_list_head
)->len
+ len
>= filesize
) {
1600 * Search from the smallest extent group
1601 * to avoid waste of space
1603 exts_group_tmp
= (*exts_group_list_head
)->prev
;
1605 if (exts_group_tmp
->len
+ len
>= filesize
) {
1606 len
+= exts_group_tmp
->len
;
1608 get_exts_group(exts_group_list_head
,
1610 if (insert_exts_group
1611 (target_exts_group_list_head
,
1612 exts_group_tmp
) < 0) {
1613 if (exts_group_tmp
) {
1614 free(exts_group_tmp
);
1619 /* The only entry go out normally*/
1622 exts_group_tmp
= exts_group_tmp
->prev
;
1623 } while (exts_group_tmp
!=
1624 (*exts_group_list_head
)->prev
);
1626 len
+= (*exts_group_list_head
)->len
;
1627 exts_group_tmp
= get_exts_group(exts_group_list_head
,
1628 *exts_group_list_head
);
1629 if (insert_exts_group(target_exts_group_list_head
,
1630 exts_group_tmp
) < 0) {
1631 if (exts_group_tmp
) {
1632 free(exts_group_tmp
);
1643 * check_frag_count() - Check file frag.
1645 * @fd: the file's discriptor.
1646 * @inode: inode number from struct stat64.
1647 * @extent_count: the number of extents.
1649 int check_frag_count(int fd
, unsigned long long inode
, int extent_count
)
1651 int ret
, pos
, file_extent_count
;
1652 struct ext4_extents_info extents_info
;
1654 /* Count file exts */
1655 memset(&extents_info
, 0, sizeof(struct ext4_extents_info
));
1656 file_extent_count
= 0;/* Extents count of file */
1657 extents_info
.ino
= inode
;
1658 extents_info
.max_entries
= DEFRAG_MAX_ENT
;
1659 extents_info
.entries
= 0;
1664 extents_info
.entries
+= pos
;
1665 pos
= extents_info
.entries
;
1666 ret
= ioctl(fd
, EXT4_IOC_EXTENTS_INFO
, &extents_info
);
1670 file_extent_count
+= extents_info
.entries
;
1671 } while (extents_info
.entries
== DEFRAG_MAX_ENT
&& ret
== 0);
1673 if (extent_count
>= file_extent_count
) {
1683 * defrag_proc() - Reserve extent group and execute the defrag program
1685 * @fd: the file's discriptor.
1686 * @target_exts_group_head: the head of the original exts_group list.
1687 * @inode: inode number from struct stat64.
1689 int defrag_proc(int fd
, exts_group_t
*target_exts_group_head
,
1690 unsigned long long inode
)
1697 exts_group_t
*exts_group
;
1699 struct ext4_extents_info extents_info
;
1700 struct ext4_ext_defrag_data defrag_data
;
1701 extent_wrap_t
*wrap_list
= NULL
;
1703 /* Reserve free extents */
1704 if (!target_exts_group_head
) {
1710 memset(&buf
, 0, sizeof(struct stat64
));
1711 ret
= fstat64(fd
, &buf
);
1713 perror(NGMSG_FILE_INFO
);
1716 /* get block size */
1717 ret
= ioctl(fd
, FIGETBSZ
, &blocksize
);
1719 perror(NGMSG_FILE_BLOCKSIZE
);
1722 memset(&extents_info
, 0, sizeof(extents_info
));
1723 memset(&defrag_data
, 0, sizeof(struct ext4_ext_defrag_data
));
1725 extents_info
.ino
= 0;
1726 exts_group
= target_exts_group_head
;
1727 extents_info
.max_entries
= DEFRAG_MAX_ENT
;
1728 extents_info
.ino
= inode
;
1729 ext4_fsblk_t data_block
= 0;
1730 ext4_fsblk_t data_start
= 0;
1731 defrag_data
.start_offset
= 0;
1734 extent_wrap_t
*wrap
;
1735 extent
= exts_group
->start
;
1737 data_start
= extent
->data
.start
;
1738 data_block
= extent
->data
.block
;
1740 data_len
+= extent
->data
.len
;
1741 if (extent
->tag
!= EXT4_EXT_USE
) {
1742 extent
->tag
= EXT4_EXT_RESERVE
;
1743 extent
= extent
->next
;
1746 extents_info
.ino
= extent
->ino
;
1747 extents_info
.goal
= fgoal
;
1748 memcpy(extents_info
.ext
, &extent
->data
,
1749 sizeof(struct ext4_extent_data
));
1750 wrap
= malloc(sizeof(extent_wrap_t
));
1752 goto release_blocks
;
1754 wrap
->group_ext
= extent
;
1755 wrap
->next
= wrap
->prev
= wrap
;
1756 if (move_wrap(NULL
, &wrap_list
, wrap
) < 0) {
1760 goto release_blocks
;
1762 extent
= extent
->next
;
1763 extents_info
.entries
= 1;
1764 ret
= ioctl(fd
, EXT4_IOC_MOVE_VICTIM
, &extents_info
);
1766 goto release_blocks
;
1768 mark_wrap(wrap_list
);
1769 free_wrap_list(&wrap_list
);
1770 } while (extent
!= exts_group
->end
->next
);
1772 if (fsync(fd
) < 0) {
1774 perror(NGMSG_FILE_SYNC
);
1779 extents_info
.entries
= 1;
1780 extents_info
.ext
[0].block
= data_block
;
1781 extents_info
.ext
[0].start
= data_start
;
1782 extents_info
.ext
[0].len
= exts_group
->len
;
1783 ret
= ioctl(fd
, EXT4_IOC_RESERVE_BLOCK
, &extents_info
);
1785 printf("RESERVE_ERROR ret = %d\n", ret
);
1786 printf("block is already used\n");
1787 goto release_blocks
;
1789 ret
= do_defrag(fd
, exts_group
, defrag_data
);
1791 printf("DEFRAG_ERROR ret = %d\n", ret
);
1792 goto release_blocks
;
1794 defrag_data
.start_offset
+= ret
;
1795 ret
= defrag_data
.start_offset
;
1797 /* print process progress */
1799 percent
= ((long long)ret
* blocksize
* 100) /
1801 if (percent
> 100) {
1804 printf("\033[79;16H\033[K progressing ====> %d%%",
1809 exts_group
= exts_group
->next
;
1810 } while (exts_group
!= target_exts_group_head
);
1814 free_wrap_list(&wrap_list
);
1815 ret
= ioctl(fd
, EXT4_IOC_BLOCK_RELEASE
);
1824 * force_defrag() - Execute the defrag program in force mode.
1826 * @fd: the file's descriptor.
1827 * @buf: a pointer of the struct stat64.
1828 * @blocksize: block size in byte.
1830 int force_defrag(int fd
, const struct stat64
*buf
, int blocksize
)
1835 unsigned int gnumber
;
1836 unsigned long filesize
;
1837 unsigned long long istart
, iend
;
1838 ext4_fsblk_t bstart
, bend
;
1839 extent_t
*extlist_head
= NULL
;
1840 exts_group_t
*exts_group_list_head
, *exts_group_list_target_head
;
1841 struct ext4_group_data_info ext4_group_data
;
1843 exts_group_list_head
= exts_group_list_target_head
= NULL
;
1845 /* Get group info */
1846 memset(&ext4_group_data
, 0, sizeof(struct ext4_group_data_info
));
1847 if (ioctl(fd
, EXT4_IOC_GROUP_INFO
, &ext4_group_data
) < 0) {
1851 gnumber
= (buf
->st_ino
- 1) / ext4_group_data
.s_inodes_per_group
;
1852 istart
= gnumber
* ext4_group_data
.s_inodes_per_group
;
1853 iend
= istart
+ ext4_group_data
.s_inodes_per_group
- 1;
1854 bstart
= gnumber
* ext4_group_data
.s_blocks_per_group
;
1855 bend
= bstart
+ ext4_group_data
.s_blocks_per_group
- 1;
1857 /* Compute filesize in block */
1858 filesize
= (buf
->st_size
+ blocksize
- 1) / blocksize
;
1860 /* Get used extents in the block group */
1861 ret
= get_used_extent(fd
, &extlist_head
, istart
, iend
, bstart
, bend
);
1862 if (ret
== RETURN_NG
) {
1866 /* Get free extents in the group */
1867 ret
= get_free_extent(fd
, buf
->st_ino
,
1868 ext4_group_data
.s_blocks_per_group
, &extlist_head
);
1869 if (ret
== RETURN_NG
) {
1873 /* All space in this group is used by other groups' inodes */
1874 if (extlist_head
== NULL
) {
1879 /* Get continuous region(extents group) */
1880 ret
= join_extents(extlist_head
, &exts_group_list_target_head
,
1881 &exts_group_list_head
, filesize
, &maxlen
);
1882 if (ret
== RETURN_NG
) {
1885 if (ret
== CHECK_FRAG_COUNT
) {
1890 if (maxlen
< filesize
) {
1891 /* No enough space */
1897 if (!exts_group_list_head
) {
1902 /* Find target extents group */
1903 ret
= find_exts_group(&exts
, filesize
, &exts_group_list_head
,
1904 &exts_group_list_target_head
);
1905 if (ret
== RETURN_NG
) {
1910 /* Check file extent count*/
1911 ret
= check_frag_count(fd
, buf
->st_ino
, exts
);
1912 if (ret
== RETURN_NG
) {
1916 /* Reserve extent group and execute the defrag program */
1917 ret
= defrag_proc(fd
, exts_group_list_target_head
, buf
->st_ino
);
1920 free_exts_group(exts_group_list_target_head
);
1921 free_exts_group(exts_group_list_head
);
1922 free_ext(extlist_head
);