2 * e4defrag.c - ext4 filesystem defragmenter
4 * Copyright (C) 2009 NEC Software Tohoku, Ltd.
6 * Author: Akira Fujita <a-fujita@rs.jp.nec.com>
7 * Takashi Sato <t-sato@yk.jp.nec.com>
10 #ifndef _LARGEFILE_SOURCE
11 #define _LARGEFILE_SOURCE
14 #ifndef _LARGEFILE64_SOURCE
15 #define _LARGEFILE64_SOURCE
34 #include <ext2fs/ext2_types.h>
35 #include <ext2fs/ext2fs.h>
37 #include <sys/ioctl.h>
38 #include <ext2fs/fiemap.h>
41 #include <sys/statfs.h>
42 #include <sys/syscall.h>
45 /* A relatively new ioctl interface ... */
46 #ifndef EXT4_IOC_MOVE_EXT
47 #define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent)
51 #define PRINT_ERR_MSG(msg) fprintf(stderr, "%s\n", (msg))
52 #define IN_FTW_PRINT_ERR_MSG(msg) \
53 fprintf(stderr, "\t%s\t\t[ NG ]\n", (msg))
54 #define PRINT_FILE_NAME(file) fprintf(stderr, " \"%s\"\n", (file))
55 #define PRINT_ERR_MSG_WITH_ERRNO(msg) \
56 fprintf(stderr, "\t%s:%s\t[ NG ]\n", (msg), strerror(errno))
57 #define STATISTIC_ERR_MSG(msg) \
58 fprintf(stderr, "\t%s\n", (msg))
59 #define STATISTIC_ERR_MSG_WITH_ERRNO(msg) \
60 fprintf(stderr, "\t%s:%s\n", (msg), strerror(errno))
61 #define min(x, y) (((x) > (y)) ? (y) : (x))
62 #define SECTOR_TO_BLOCK(sectors, blocksize) \
63 ((sectors) / ((blocksize) >> 9))
64 #define CALC_SCORE(ratio) \
65 ((ratio) > 10 ? (80 + 20 * (ratio) / 100) : (8 * (ratio)))
66 /* Wrap up the free function */
72 /* Insert list2 after list1 */
73 #define insert(list1, list2) \
75 list2->next = list1->next; \
76 list1->next->prev = list2; \
77 list2->prev = list1; \
78 list1->next = list2; \
81 /* To delete unused warning */
83 #define EXT2FS_ATTR(x) __attribute__(x)
85 #define EXT2FS_ATTR(x)
88 /* The mode of defrag */
90 #define STATISTIC 0x02
96 #define FTW_OPEN_FD 2000
98 #define FS_EXT4 "ext4"
101 #define BOUND_SCORE 55
102 #define SHOW_FRAG_FILES 5
104 /* Magic number for ext4 */
105 #define EXT4_SUPER_MAGIC 0xEF53
107 /* Definition of flex_bg */
108 #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
110 /* The following macro is used for ioctl FS_IOC_FIEMAP
111 * EXTENT_MAX_COUNT: the maximum number of extents for exchanging between
112 * kernel-space and user-space per ioctl
114 #define EXTENT_MAX_COUNT 512
116 /* The following macros are error message */
118 "Usage : e4defrag [-v] file...| directory...| device...\n\
119 : e4defrag -c file...| directory...| device...\n"
121 #define NGMSG_EXT4 "Filesystem is not ext4 filesystem"
122 #define NGMSG_FILE_EXTENT "Failed to get file extents"
123 #define NGMSG_FILE_INFO "Failed to get file information"
124 #define NGMSG_FILE_OPEN "Failed to open"
125 #define NGMSG_FILE_UNREG "File is not regular file"
126 #define NGMSG_LOST_FOUND "Can not process \"lost+found\""
128 /* Data type for filesystem-wide blocks number */
129 typedef unsigned long long ext4_fsblk_t
;
131 struct fiemap_extent_data
{
132 __u64 len
; /* blocks count */
133 __u64 logical
; /* start logical block number */
134 ext4_fsblk_t physical
; /* start physical block number */
137 struct fiemap_extent_list
{
138 struct fiemap_extent_list
*prev
;
139 struct fiemap_extent_list
*next
;
140 struct fiemap_extent_data data
; /* extent belong to file */
143 struct fiemap_extent_group
{
144 struct fiemap_extent_group
*prev
;
145 struct fiemap_extent_group
*next
;
146 __u64 len
; /* length of this continuous region */
147 struct fiemap_extent_list
*start
; /* start ext */
148 struct fiemap_extent_list
*end
; /* end ext */
152 __s32 reserved
; /* original file descriptor */
153 __u32 donor_fd
; /* donor file descriptor */
154 __u64 orig_start
; /* logical start offset in block for orig */
155 __u64 donor_start
; /* logical start offset in block for donor */
156 __u64 len
; /* block length to be moved */
157 __u64 moved_len
; /* moved block length */
160 struct frag_statistic_ino
{
161 int now_count
; /* the file's extents count of before defrag */
162 int best_count
; /* the best file's extents count */
163 float ratio
; /* the ratio of fragmentation */
164 char msg_buffer
[PATH_MAX
+ 1]; /* pathname of the file */
167 typedef __u16 __le16
;
168 typedef __u32 __le32
;
169 typedef __u64 __le64
;
172 * Structure of the super block
174 struct ext4_super_block
{
175 /*00*/ __le32 s_inodes_count
; /* Inodes count */
176 __le32 s_blocks_count_lo
; /* Blocks count */
177 __le32 s_r_blocks_count_lo
; /* Reserved blocks count */
178 __le32 s_free_blocks_count_lo
; /* Free blocks count */
179 /*10*/ __le32 s_free_inodes_count
; /* Free inodes count */
180 __le32 s_first_data_block
; /* First Data Block */
181 __le32 s_log_block_size
; /* Block size */
182 __le32 s_obso_log_frag_size
; /* Obsoleted fragment size */
183 /*20*/ __le32 s_blocks_per_group
; /* # Blocks per group */
184 __le32 s_obso_frags_per_group
; /* Obsoleted fragments per group */
185 __le32 s_inodes_per_group
; /* # Inodes per group */
186 __le32 s_mtime
; /* Mount time */
187 /*30*/ __le32 s_wtime
; /* Write time */
188 __le16 s_mnt_count
; /* Mount count */
189 __le16 s_max_mnt_count
; /* Maximal mount count */
190 __le16 s_magic
; /* Magic signature */
191 __le16 s_state
; /* File system state */
192 __le16 s_errors
; /* Behaviour when detecting errors */
193 __le16 s_minor_rev_level
; /* minor revision level */
194 /*40*/ __le32 s_lastcheck
; /* time of last check */
195 __le32 s_checkinterval
; /* max. time between checks */
196 __le32 s_creator_os
; /* OS */
197 __le32 s_rev_level
; /* Revision level */
198 /*50*/ __le16 s_def_resuid
; /* Default uid for reserved blocks */
199 __le16 s_def_resgid
; /* Default gid for reserved blocks */
201 * These fields are for EXT4_DYNAMIC_REV superblocks only.
203 * Note: the difference between the compatible feature set and
204 * the incompatible feature set is that if there is a bit set
205 * in the incompatible feature set that the kernel doesn't
206 * know about, it should refuse to mount the filesystem.
208 * e2fsck's requirements are more strict; if it doesn't know
209 * about a feature in either the compatible or incompatible
210 * feature set, it must abort and not try to meddle with
211 * things it doesn't understand...
213 __le32 s_first_ino
; /* First non-reserved inode */
214 __le16 s_inode_size
; /* size of inode structure */
215 __le16 s_block_group_nr
; /* block group # of this superblock */
216 __le32 s_feature_compat
; /* compatible feature set */
217 /*60*/ __le32 s_feature_incompat
; /* incompatible feature set */
218 __le32 s_feature_ro_compat
; /* readonly-compatible feature set */
219 /*68*/ __u8 s_uuid
[16]; /* 128-bit uuid for volume */
220 /*78*/ char s_volume_name
[16]; /* volume name */
221 /*88*/ char s_last_mounted
[64]; /* directory where last mounted */
222 /*C8*/ __le32 s_algorithm_usage_bitmap
; /* For compression */
224 * Performance hints. Directory preallocation should only
225 * happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on.
227 __u8 s_prealloc_blocks
; /* Nr of blocks to try to preallocate*/
228 __u8 s_prealloc_dir_blocks
; /* Nr to preallocate for dirs */
229 __le16 s_reserved_gdt_blocks
; /* Per group desc for online growth */
231 * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set.
233 /*D0*/ __u8 s_journal_uuid
[16]; /* uuid of journal superblock */
234 /*E0*/ __le32 s_journal_inum
; /* inode number of journal file */
235 __le32 s_journal_dev
; /* device number of journal file */
236 __le32 s_last_orphan
; /* start of list of inodes to delete */
237 __le32 s_hash_seed
[4]; /* HTREE hash seed */
238 __u8 s_def_hash_version
; /* Default hash version to use */
239 __u8 s_reserved_char_pad
;
240 __le16 s_desc_size
; /* size of group descriptor */
241 /*100*/ __le32 s_default_mount_opts
;
242 __le32 s_first_meta_bg
; /* First metablock block group */
243 __le32 s_mkfs_time
; /* When the filesystem was created */
244 __le32 s_jnl_blocks
[17]; /* Backup of the journal inode */
245 /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
246 /*150*/ __le32 s_blocks_count_hi
; /* Blocks count */
247 __le32 s_r_blocks_count_hi
; /* Reserved blocks count */
248 __le32 s_free_blocks_count_hi
; /* Free blocks count */
249 __le16 s_min_extra_isize
; /* All inodes have at least # bytes */
250 __le16 s_want_extra_isize
; /* New inodes should reserve # bytes */
251 __le32 s_flags
; /* Miscellaneous flags */
252 __le16 s_raid_stride
; /* RAID stride */
253 __le16 s_mmp_interval
; /* # seconds to wait in MMP checking */
254 __le64 s_mmp_block
; /* Block for multi-mount protection */
255 __le32 s_raid_stripe_width
; /* blocks on all data disks (N*stride)*/
256 __u8 s_log_groups_per_flex
; /* FLEX_BG group size */
257 __u8 s_reserved_char_pad2
;
258 __le16 s_reserved_pad
;
259 __u32 s_reserved
[162]; /* Padding to the end of the block */
262 char lost_found_dir
[PATH_MAX
+ 1];
264 int extents_before_defrag
;
265 int extents_after_defrag
;
267 unsigned int current_uid
;
268 unsigned int defraged_file_count
;
269 unsigned int frag_files_before_defrag
;
270 unsigned int frag_files_after_defrag
;
271 unsigned int regular_count
;
272 unsigned int succeed_cnt
;
273 unsigned int total_count
;
274 __u8 log_groups_per_flex
;
275 __le32 blocks_per_group
;
276 __le32 feature_incompat
;
277 ext4_fsblk_t files_block_count
;
278 struct frag_statistic_ino frag_rank
[SHOW_FRAG_FILES
];
281 /* Local definitions of some syscalls glibc may not yet have */
283 #ifndef HAVE_POSIX_FADVISE
284 #warning Using locally defined posix_fadvise interface.
286 #ifndef __NR_fadvise64_64
287 #error Your kernel headers dont define __NR_fadvise64_64
291 * fadvise() - Give advice about file access.
293 * @fd: defrag target file's descriptor.
294 * @offset: file offset.
296 * @advise: process flag.
298 static int posix_fadvise(int fd
, loff_t offset
, size_t len
, int advise
)
300 return syscall(__NR_fadvise64_64
, fd
, offset
, len
, advise
);
302 #endif /* ! HAVE_FADVISE64_64 */
304 #ifndef HAVE_SYNC_FILE_RANGE
305 #warning Using locally defined sync_file_range interface.
307 #ifndef __NR_sync_file_range
308 #error Your kernel headers dont define __NR_sync_file_range
312 * sync_file_range() - Sync file region.
314 * @fd: defrag target file's descriptor.
315 * @offset: file offset.
316 * @length: area length.
317 * @flag: process flag.
319 int sync_file_range(int fd
, loff_t offset
, loff_t length
, unsigned int flag
)
321 return syscall(__NR_sync_file_range
, fd
, offset
, length
, flag
);
323 #endif /* ! HAVE_SYNC_FILE_RANGE */
325 #ifndef HAVE_FALLOCATE
326 #warning Using locally defined fallocate syscall interface.
328 #ifndef __NR_fallocate
329 #error Your kernel headers dont define __NR_fallocate
333 * fallocate() - Manipulate file space.
335 * @fd: defrag target file's descriptor.
336 * @mode: process flag.
337 * @offset: file offset.
340 static int fallocate(int fd
, int mode
, loff_t offset
, loff_t len
)
342 return syscall(__NR_fallocate
, fd
, mode
, offset
, len
);
344 #endif /* ! HAVE_FALLOCATE */
347 * get_mount_point() - Get device's mount point.
349 * @devname: the device's name.
350 * @mount_point: the mount point.
351 * @dir_path_len: the length of directory.
353 static int get_mount_point(const char *devname
, char *mount_point
,
356 /* Refer to /etc/mtab */
357 const char *mtab
= MOUNTED
;
359 struct mntent
*mnt
= NULL
;
361 fp
= setmntent(mtab
, "r");
363 perror("Couldn't access /etc/mtab");
367 while ((mnt
= getmntent(fp
)) != NULL
) {
368 if (strcmp(devname
, mnt
->mnt_fsname
) != 0)
372 if (strcmp(mnt
->mnt_type
, FS_EXT4
) == 0) {
373 strncpy(mount_point
, mnt
->mnt_dir
,
377 PRINT_ERR_MSG(NGMSG_EXT4
);
381 PRINT_ERR_MSG("Filesystem is not mounted");
386 * is_ext4() - Whether on an ext4 filesystem.
388 * @file: the file's name.
390 static int is_ext4(const char *file
)
395 char *mnt_type
= NULL
;
396 /* Refer to /etc/mtab */
397 const char *mtab
= MOUNTED
;
398 char file_path
[PATH_MAX
+ 1];
399 struct mntent
*mnt
= NULL
;
400 struct statfs64 fsbuf
;
403 if (realpath(file
, file_path
) == NULL
) {
404 perror("Couldn't get full path");
405 PRINT_FILE_NAME(file
);
409 if (statfs64(file_path
, &fsbuf
) < 0) {
410 perror("Failed to get filesystem information");
411 PRINT_FILE_NAME(file
);
415 if (fsbuf
.f_type
!= EXT4_SUPER_MAGIC
) {
416 PRINT_ERR_MSG(NGMSG_EXT4
);
420 fp
= setmntent(mtab
, "r");
422 perror("Couldn't access /etc/mtab");
426 while ((mnt
= getmntent(fp
)) != NULL
) {
427 len
= strlen(mnt
->mnt_dir
);
428 ret
= memcmp(file_path
, mnt
->mnt_dir
, len
);
437 mnt_type
= realloc(mnt_type
, strlen(mnt
->mnt_type
) + 1);
438 if (mnt_type
== NULL
) {
442 memset(mnt_type
, 0, strlen(mnt
->mnt_type
) + 1);
443 strncpy(mnt_type
, mnt
->mnt_type
, strlen(mnt
->mnt_type
));
444 strncpy(lost_found_dir
, mnt
->mnt_dir
, PATH_MAX
);
448 if (strcmp(mnt_type
, FS_EXT4
) == 0) {
453 PRINT_ERR_MSG(NGMSG_EXT4
);
459 * calc_entry_counts() - Calculate file counts.
464 * @ftwbuf: the pointer of a struct FTW.
466 static int calc_entry_counts(const char *file
EXT2FS_ATTR((unused
)),
467 const struct stat64
*buf
, int flag
EXT2FS_ATTR((unused
)),
468 struct FTW
*ftwbuf
EXT2FS_ATTR((unused
)))
470 if (S_ISREG(buf
->st_mode
))
479 * page_in_core() - Get information on whether pages are in core.
481 * @fd: defrag target file's descriptor.
482 * @defrag_data: data used for defrag.
483 * @vec: page state array.
484 * @page_num: page number.
486 static int page_in_core(int fd
, struct move_extent defrag_data
,
487 unsigned char **vec
, unsigned int *page_num
)
489 long pagesize
= sysconf(_SC_PAGESIZE
);
491 loff_t offset
, end_offset
, length
;
493 if (vec
== NULL
|| *vec
!= NULL
)
496 /* In mmap, offset should be a multiple of the page size */
497 offset
= (loff_t
)defrag_data
.orig_start
* block_size
;
498 length
= (loff_t
)defrag_data
.len
* block_size
;
499 end_offset
= offset
+ length
;
500 /* Round the offset down to the nearest multiple of pagesize */
501 offset
= (offset
/ pagesize
) * pagesize
;
502 length
= end_offset
- offset
;
504 page
= mmap(NULL
, length
, PROT_READ
, MAP_SHARED
, fd
, offset
);
505 if (page
== MAP_FAILED
)
509 *page_num
= (length
+ pagesize
- 1) / pagesize
;
510 *vec
= (unsigned char *)calloc(*page_num
, 1);
514 /* Get information on whether pages are in core */
515 if (mincore(page
, (size_t)length
, *vec
) == -1 ||
516 munmap(page
, length
) == -1) {
525 * defrag_fadvise() - Predeclare an access pattern for file data.
527 * @fd: defrag target file's descriptor.
528 * @defrag_data: data used for defrag.
529 * @vec: page state array.
530 * @page_num: page number.
532 static int defrag_fadvise(int fd
, struct move_extent defrag_data
,
533 unsigned char *vec
, unsigned int page_num
)
536 long pagesize
= sysconf(_SC_PAGESIZE
);
537 int fadvise_flag
= POSIX_FADV_DONTNEED
;
538 int sync_flag
= SYNC_FILE_RANGE_WAIT_BEFORE
|
539 SYNC_FILE_RANGE_WRITE
|
540 SYNC_FILE_RANGE_WAIT_AFTER
;
544 offset
= (loff_t
)defrag_data
.orig_start
* block_size
;
545 offset
= (offset
/ pagesize
) * pagesize
;
547 /* Sync file for fadvise process */
548 if (sync_file_range(fd
, offset
,
549 (loff_t
)pagesize
* page_num
, sync_flag
) < 0)
552 /* Try to release buffer cache which this process used,
553 * then other process can use the released buffer
555 for (i
= 0; i
< page_num
; i
++) {
556 if ((vec
[i
] & 0x1) == 0) {
560 if (posix_fadvise(fd
, offset
, pagesize
, fadvise_flag
) < 0) {
561 if ((mode_flag
& DETAIL
) && flag
) {
562 perror("\tFailed to fadvise");
573 * check_free_size() - Check if there's enough disk space.
575 * @fd: defrag target file's descriptor.
577 * @buf: the pointer of the struct stat64.
579 static int check_free_size(int fd
, const char *file
, const struct stat64
*buf
)
581 ext4_fsblk_t blk_count
;
582 ext4_fsblk_t free_blk_count
;
583 struct statfs64 fsbuf
;
585 if (fstatfs64(fd
, &fsbuf
) < 0) {
586 if (mode_flag
& DETAIL
) {
587 PRINT_FILE_NAME(file
);
588 PRINT_ERR_MSG_WITH_ERRNO(
589 "Failed to get filesystem information");
594 /* Target file size measured by filesystem IO blocksize */
595 blk_count
= SECTOR_TO_BLOCK(buf
->st_blocks
, fsbuf
.f_bsize
);
597 /* Compute free space for root and normal user separately */
598 if (current_uid
== ROOT_UID
)
599 free_blk_count
= fsbuf
.f_bfree
;
601 free_blk_count
= fsbuf
.f_bavail
;
603 if (free_blk_count
>= blk_count
)
610 * file_frag_count() - Get file fragment count.
612 * @fd: defrag target file's descriptor.
614 static int file_frag_count(int fd
)
617 struct fiemap fiemap_buf
;
619 /* When fm_extent_count is 0,
620 * ioctl just get file fragment count.
622 memset(&fiemap_buf
, 0, sizeof(struct fiemap
));
623 fiemap_buf
.fm_start
= 0;
624 fiemap_buf
.fm_length
= FIEMAP_MAX_OFFSET
;
625 fiemap_buf
.fm_flags
|= FIEMAP_FLAG_SYNC
;
627 ret
= ioctl(fd
, FS_IOC_FIEMAP
, &fiemap_buf
);
631 return fiemap_buf
.fm_mapped_extents
;
635 * file_check() - Check file's attributes.
637 * @fd: defrag target file's descriptor.
638 * @buf: a pointer of the struct stat64.
639 * @file: the file's name.
640 * @extents: the file's extents.
642 static int file_check(int fd
, const struct stat64
*buf
, const char *file
,
648 /* Write-lock check is more reliable */
649 lock
.l_type
= F_WRLCK
;
651 lock
.l_whence
= SEEK_SET
;
655 ret
= check_free_size(fd
, file
, buf
);
657 if ((mode_flag
& DETAIL
) && ret
== -ENOSPC
) {
658 printf("\033[79;0H\033[K[%u/%u] \"%s\"\t\t"
659 " extents: %d -> %d\n", defraged_file_count
,
660 total_count
, file
, extents
, extents
);
661 IN_FTW_PRINT_ERR_MSG(
662 "Defrag size is larger than filesystem's free space");
667 /* Access authority */
668 if (current_uid
!= ROOT_UID
&&
669 buf
->st_uid
!= current_uid
) {
670 if (mode_flag
& DETAIL
) {
671 printf("\033[79;0H\033[K[%u/%u] \"%s\"\t\t"
672 " extents: %d -> %d\n", defraged_file_count
,
673 total_count
, file
, extents
, extents
);
674 IN_FTW_PRINT_ERR_MSG(
675 "File is not current user's file"
676 " or current user is not root");
682 if (fcntl(fd
, F_GETLK
, &lock
) < 0) {
683 if (mode_flag
& DETAIL
) {
684 PRINT_FILE_NAME(file
);
685 PRINT_ERR_MSG_WITH_ERRNO(
686 "Failed to get lock information");
689 } else if (lock
.l_type
!= F_UNLCK
) {
690 if (mode_flag
& DETAIL
) {
691 PRINT_FILE_NAME(file
);
692 IN_FTW_PRINT_ERR_MSG("File has been locked");
701 * insert_extent_by_logical() - Sequentially insert extent by logical.
703 * @ext_list_head: the head of logical extent list.
704 * @ext: the extent element which will be inserted.
706 static int insert_extent_by_logical(struct fiemap_extent_list
**ext_list_head
,
707 struct fiemap_extent_list
*ext
)
709 struct fiemap_extent_list
*ext_list_tmp
= *ext_list_head
;
715 if (*ext_list_head
== NULL
) {
716 (*ext_list_head
) = ext
;
717 (*ext_list_head
)->prev
= *ext_list_head
;
718 (*ext_list_head
)->next
= *ext_list_head
;
722 if (ext
->data
.logical
<= ext_list_tmp
->data
.logical
) {
723 /* Insert before head */
724 if (ext_list_tmp
->data
.logical
<
725 ext
->data
.logical
+ ext
->data
.len
)
729 *ext_list_head
= ext
;
731 /* Insert into the middle or last of the list */
733 if (ext
->data
.logical
< ext_list_tmp
->data
.logical
)
735 ext_list_tmp
= ext_list_tmp
->next
;
736 } while (ext_list_tmp
!= (*ext_list_head
));
737 if (ext
->data
.logical
<
738 ext_list_tmp
->prev
->data
.logical
+
739 ext_list_tmp
->prev
->data
.len
)
743 if (ext_list_tmp
!= *ext_list_head
&&
744 ext_list_tmp
->data
.logical
<
745 ext
->data
.logical
+ ext
->data
.len
)
749 ext_list_tmp
= ext_list_tmp
->prev
;
750 /* Insert "ext" after "ext_list_tmp" */
751 insert(ext_list_tmp
, ext
);
759 * insert_extent_by_physical() - Sequentially insert extent by physical.
761 * @ext_list_head: the head of physical extent list.
762 * @ext: the extent element which will be inserted.
764 static int insert_extent_by_physical(struct fiemap_extent_list
**ext_list_head
,
765 struct fiemap_extent_list
*ext
)
767 struct fiemap_extent_list
*ext_list_tmp
= *ext_list_head
;
773 if (*ext_list_head
== NULL
) {
774 (*ext_list_head
) = ext
;
775 (*ext_list_head
)->prev
= *ext_list_head
;
776 (*ext_list_head
)->next
= *ext_list_head
;
780 if (ext
->data
.physical
<= ext_list_tmp
->data
.physical
) {
781 /* Insert before head */
782 if (ext_list_tmp
->data
.physical
<
783 ext
->data
.physical
+ ext
->data
.len
)
787 *ext_list_head
= ext
;
789 /* Insert into the middle or last of the list */
791 if (ext
->data
.physical
< ext_list_tmp
->data
.physical
)
793 ext_list_tmp
= ext_list_tmp
->next
;
794 } while (ext_list_tmp
!= (*ext_list_head
));
795 if (ext
->data
.physical
<
796 ext_list_tmp
->prev
->data
.physical
+
797 ext_list_tmp
->prev
->data
.len
)
801 if (ext_list_tmp
!= *ext_list_head
&&
802 ext_list_tmp
->data
.physical
<
803 ext
->data
.physical
+ ext
->data
.len
)
807 ext_list_tmp
= ext_list_tmp
->prev
;
808 /* Insert "ext" after "ext_list_tmp" */
809 insert(ext_list_tmp
, ext
);
817 * insert_exts_group() - Insert a exts_group.
819 * @ext_group_head: the head of a exts_group list.
820 * @exts_group: the exts_group element which will be inserted.
822 static int insert_exts_group(struct fiemap_extent_group
**ext_group_head
,
823 struct fiemap_extent_group
*exts_group
)
825 struct fiemap_extent_group
*ext_group_tmp
= NULL
;
827 if (exts_group
== NULL
) {
832 /* Initialize list */
833 if (*ext_group_head
== NULL
) {
834 (*ext_group_head
) = exts_group
;
835 (*ext_group_head
)->prev
= *ext_group_head
;
836 (*ext_group_head
)->next
= *ext_group_head
;
840 ext_group_tmp
= (*ext_group_head
)->prev
;
841 insert(ext_group_tmp
, exts_group
);
847 * join_extents() - Find continuous region(exts_group).
849 * @ext_list_head: the head of the extent list.
850 * @ext_group_head: the head of the target exts_group list.
852 static int join_extents(struct fiemap_extent_list
*ext_list_head
,
853 struct fiemap_extent_group
**ext_group_head
)
855 __u64 len
= ext_list_head
->data
.len
;
856 struct fiemap_extent_list
*ext_list_start
= ext_list_head
;
857 struct fiemap_extent_list
*ext_list_tmp
= ext_list_head
->next
;
860 struct fiemap_extent_group
*ext_group_tmp
= NULL
;
862 /* This extent and previous extent are not continuous,
863 * so, all previous extents are treated as an extent group.
865 if ((ext_list_tmp
->prev
->data
.logical
+
866 ext_list_tmp
->prev
->data
.len
)
867 != ext_list_tmp
->data
.logical
) {
869 malloc(sizeof(struct fiemap_extent_group
));
870 if (ext_group_tmp
== NULL
)
873 memset(ext_group_tmp
, 0,
874 sizeof(struct fiemap_extent_group
));
875 ext_group_tmp
->len
= len
;
876 ext_group_tmp
->start
= ext_list_start
;
877 ext_group_tmp
->end
= ext_list_tmp
->prev
;
879 if (insert_exts_group(ext_group_head
,
880 ext_group_tmp
) < 0) {
884 ext_list_start
= ext_list_tmp
;
885 len
= ext_list_tmp
->data
.len
;
886 ext_list_tmp
= ext_list_tmp
->next
;
890 /* This extent and previous extent are continuous,
891 * so, they belong to the same extent group, and we check
892 * if the next extent belongs to the same extent group.
894 len
+= ext_list_tmp
->data
.len
;
895 ext_list_tmp
= ext_list_tmp
->next
;
896 } while (ext_list_tmp
!= ext_list_head
->next
);
902 * get_file_extents() - Get file's extent list.
904 * @fd: defrag target file's descriptor.
905 * @ext_list_head: the head of the extent list.
907 static int get_file_extents(int fd
, struct fiemap_extent_list
**ext_list_head
)
911 int ext_buf_size
, fie_buf_size
;
913 struct fiemap
*fiemap_buf
= NULL
;
914 struct fiemap_extent
*ext_buf
= NULL
;
915 struct fiemap_extent_list
*ext_list
= NULL
;
917 /* Convert units, in bytes.
918 * Be careful : now, physical block number in extent is 48bit,
919 * and the maximum blocksize for ext4 is 4K(12bit),
920 * so there is no overflow, but in future it may be changed.
923 /* Alloc space for fiemap */
924 ext_buf_size
= EXTENT_MAX_COUNT
* sizeof(struct fiemap_extent
);
925 fie_buf_size
= sizeof(struct fiemap
) + ext_buf_size
;
927 fiemap_buf
= malloc(fie_buf_size
);
928 if (fiemap_buf
== NULL
)
931 ext_buf
= fiemap_buf
->fm_extents
;
932 memset(fiemap_buf
, 0, fie_buf_size
);
933 fiemap_buf
->fm_length
= FIEMAP_MAX_OFFSET
;
934 fiemap_buf
->fm_flags
|= FIEMAP_FLAG_SYNC
;
935 fiemap_buf
->fm_extent_count
= EXTENT_MAX_COUNT
;
938 fiemap_buf
->fm_start
= pos
;
939 memset(ext_buf
, 0, ext_buf_size
);
940 ret
= ioctl(fd
, FS_IOC_FIEMAP
, fiemap_buf
);
943 for (i
= 0; i
< fiemap_buf
->fm_mapped_extents
; i
++) {
945 ext_list
= malloc(sizeof(struct fiemap_extent_list
));
946 if (ext_list
== NULL
)
949 ext_list
->data
.physical
= ext_buf
[i
].fe_physical
951 ext_list
->data
.logical
= ext_buf
[i
].fe_logical
953 ext_list
->data
.len
= ext_buf
[i
].fe_length
956 ret
= insert_extent_by_physical(
957 ext_list_head
, ext_list
);
963 /* Record file's logical offset this time */
964 pos
= ext_buf
[EXTENT_MAX_COUNT
-1].fe_logical
+
965 ext_buf
[EXTENT_MAX_COUNT
-1].fe_length
;
967 * If fm_extents array has been filled and
968 * there are extents left, continue to cycle.
970 } while (fiemap_buf
->fm_mapped_extents
971 == EXTENT_MAX_COUNT
&&
972 !(ext_buf
[EXTENT_MAX_COUNT
-1].fe_flags
973 & FIEMAP_EXTENT_LAST
));
983 * get_logical_count() - Get the file logical extents count.
985 * @logical_list_head: the head of the logical extent list.
987 static int get_logical_count(struct fiemap_extent_list
*logical_list_head
)
990 struct fiemap_extent_list
*ext_list_tmp
= logical_list_head
;
994 ext_list_tmp
= ext_list_tmp
->next
;
995 } while (ext_list_tmp
!= logical_list_head
);
1001 * get_physical_count() - Get the file physical extents count.
1003 * @physical_list_head: the head of the physical extent list.
1005 static int get_physical_count(struct fiemap_extent_list
*physical_list_head
)
1008 struct fiemap_extent_list
*ext_list_tmp
= physical_list_head
;
1011 if ((ext_list_tmp
->data
.physical
+ ext_list_tmp
->data
.len
)
1012 != ext_list_tmp
->next
->data
.physical
) {
1013 /* This extent and next extent are not continuous. */
1017 ext_list_tmp
= ext_list_tmp
->next
;
1018 } while (ext_list_tmp
!= physical_list_head
);
1024 * change_physical_to_logical() - Change list from physical to logical.
1026 * @physical_list_head: the head of physical extent list.
1027 * @logical_list_head: the head of logical extent list.
1029 static int change_physical_to_logical(
1030 struct fiemap_extent_list
**physical_list_head
,
1031 struct fiemap_extent_list
**logical_list_head
)
1034 struct fiemap_extent_list
*ext_list_tmp
= *physical_list_head
;
1035 struct fiemap_extent_list
*ext_list_next
= ext_list_tmp
->next
;
1038 if (ext_list_tmp
== ext_list_next
) {
1039 ret
= insert_extent_by_logical(
1040 logical_list_head
, ext_list_tmp
);
1044 *physical_list_head
= NULL
;
1048 ext_list_tmp
->prev
->next
= ext_list_tmp
->next
;
1049 ext_list_tmp
->next
->prev
= ext_list_tmp
->prev
;
1050 *physical_list_head
= ext_list_next
;
1052 ret
= insert_extent_by_logical(
1053 logical_list_head
, ext_list_tmp
);
1058 ext_list_tmp
= ext_list_next
;
1059 ext_list_next
= ext_list_next
->next
;
1066 * free_ext() - Free the extent list.
1068 * @ext_list_head: the extent list head of which will be free.
1070 static void free_ext(struct fiemap_extent_list
*ext_list_head
)
1072 struct fiemap_extent_list
*ext_list_tmp
= NULL
;
1074 if (ext_list_head
== NULL
)
1077 while (ext_list_head
->next
!= ext_list_head
) {
1078 ext_list_tmp
= ext_list_head
;
1079 ext_list_head
->prev
->next
= ext_list_head
->next
;
1080 ext_list_head
->next
->prev
= ext_list_head
->prev
;
1081 ext_list_head
= ext_list_head
->next
;
1084 free(ext_list_head
);
1088 * free_exts_group() - Free the exts_group.
1090 * @*ext_group_head: the exts_group list head which will be free.
1092 static void free_exts_group(struct fiemap_extent_group
*ext_group_head
)
1094 struct fiemap_extent_group
*ext_group_tmp
= NULL
;
1096 if (ext_group_head
== NULL
)
1099 while (ext_group_head
->next
!= ext_group_head
) {
1100 ext_group_tmp
= ext_group_head
;
1101 ext_group_head
->prev
->next
= ext_group_head
->next
;
1102 ext_group_head
->next
->prev
= ext_group_head
->prev
;
1103 ext_group_head
= ext_group_head
->next
;
1104 free(ext_group_tmp
);
1106 free(ext_group_head
);
1110 * get_superblock_info() - Get superblock info by the file name.
1112 * @file: the file's name.
1113 * @sb: the pointer of the struct ext4_super_block.
1115 static int get_superblock_info(const char *file
, struct ext4_super_block
*sb
)
1117 /* Refer to /etc/mtab */
1118 const char *mtab
= MOUNTED
;
1125 char dev_name
[PATH_MAX
+ 1];
1126 struct mntent
*mnt
= NULL
;
1128 fp
= setmntent(mtab
, "r");
1132 while ((mnt
= getmntent(fp
)) != NULL
) {
1133 len
= strlen(mnt
->mnt_dir
);
1134 ret
= memcmp(file
, mnt
->mnt_dir
, len
);
1143 memset(dev_name
, 0, PATH_MAX
+ 1);
1144 strncpy(dev_name
, mnt
->mnt_fsname
,
1145 strnlen(mnt
->mnt_fsname
, PATH_MAX
));
1148 fd
= open64(dev_name
, O_RDONLY
);
1154 /* Set offset to read superblock */
1155 ret
= lseek64(fd
, SUPERBLOCK_OFFSET
, SEEK_SET
);
1159 ret
= read(fd
, sb
, sizeof(struct ext4_super_block
));
1171 * get_best_count() - Get the file best extents count.
1173 * @block_count: the file's physical block count.
1175 static int get_best_count(ext4_fsblk_t block_count
)
1178 unsigned int flex_bg_num
;
1180 /* Calcuate best extents count */
1181 if (feature_incompat
& EXT4_FEATURE_INCOMPAT_FLEX_BG
) {
1182 flex_bg_num
= 1 << log_groups_per_flex
;
1183 ret
= ((block_count
- 1) /
1184 ((ext4_fsblk_t
)blocks_per_group
*
1187 ret
= ((block_count
- 1) / blocks_per_group
) + 1;
1194 * file_statistic() - Get statistic info of the file's fragments.
1196 * @file: the file's name.
1197 * @buf: the pointer of the struct stat64.
1199 * @ftwbuf: the pointer of a struct FTW.
1201 static int file_statistic(const char *file
, const struct stat64
*buf
,
1202 int flag
EXT2FS_ATTR((unused
)),
1203 struct FTW
*ftwbuf
EXT2FS_ATTR((unused
)))
1207 int now_ext_count
, best_ext_count
= 0, physical_ext_count
;
1210 ext4_fsblk_t blk_count
= 0;
1211 char msg_buffer
[PATH_MAX
+ 24];
1212 struct fiemap_extent_list
*physical_list_head
= NULL
;
1213 struct fiemap_extent_list
*logical_list_head
= NULL
;
1215 defraged_file_count
++;
1217 if (mode_flag
& DETAIL
) {
1218 if (total_count
== 1 && regular_count
== 1)
1221 printf("[%u/%u]", defraged_file_count
, total_count
);
1225 if (lost_found_dir
[0] != '\0' &&
1226 !memcmp(file
, lost_found_dir
, strnlen(lost_found_dir
, PATH_MAX
))) {
1227 if (mode_flag
& DETAIL
) {
1228 PRINT_FILE_NAME(file
);
1229 STATISTIC_ERR_MSG(NGMSG_LOST_FOUND
);
1234 if (!S_ISREG(buf
->st_mode
)) {
1235 if (mode_flag
& DETAIL
) {
1236 PRINT_FILE_NAME(file
);
1237 STATISTIC_ERR_MSG(NGMSG_FILE_UNREG
);
1242 /* Access authority */
1243 if (current_uid
!= ROOT_UID
&&
1244 buf
->st_uid
!= current_uid
) {
1245 if (mode_flag
& DETAIL
) {
1246 PRINT_FILE_NAME(file
);
1248 "File is not current user's file"
1249 " or current user is not root");
1255 if (buf
->st_size
== 0) {
1256 if (mode_flag
& DETAIL
) {
1257 PRINT_FILE_NAME(file
);
1258 STATISTIC_ERR_MSG("File size is 0");
1264 if (buf
->st_blocks
== 0) {
1265 if (mode_flag
& DETAIL
) {
1266 PRINT_FILE_NAME(file
);
1267 STATISTIC_ERR_MSG("File has no blocks");
1272 fd
= open64(file
, O_RDONLY
);
1274 if (mode_flag
& DETAIL
) {
1275 PRINT_FILE_NAME(file
);
1276 STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN
);
1281 /* Get file's physical extents */
1282 ret
= get_file_extents(fd
, &physical_list_head
);
1284 if (mode_flag
& DETAIL
) {
1285 PRINT_FILE_NAME(file
);
1286 STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT
);
1291 /* Get the count of file's continuous physical region */
1292 physical_ext_count
= get_physical_count(physical_list_head
);
1294 /* Change list from physical to logical */
1295 ret
= change_physical_to_logical(&physical_list_head
,
1296 &logical_list_head
);
1298 if (mode_flag
& DETAIL
) {
1299 PRINT_FILE_NAME(file
);
1300 STATISTIC_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT
);
1305 /* Count file fragments before defrag */
1306 now_ext_count
= get_logical_count(logical_list_head
);
1308 if (current_uid
== ROOT_UID
) {
1309 /* Calculate fragment ratio */
1311 SECTOR_TO_BLOCK(buf
->st_blocks
, block_size
);
1313 best_ext_count
= get_best_count(blk_count
);
1315 ratio
= (float)(physical_ext_count
- best_ext_count
) * 100 /
1318 extents_before_defrag
+= now_ext_count
;
1319 extents_after_defrag
+= best_ext_count
;
1320 files_block_count
+= blk_count
;
1323 if (total_count
== 1 && regular_count
== 1) {
1325 if (mode_flag
& DETAIL
) {
1327 struct fiemap_extent_list
*ext_list_tmp
=
1330 /* Print extents info */
1333 printf("[ext %d]:\tstart %llu:\tlogical "
1334 "%llu:\tlen %llu\n", count
,
1335 ext_list_tmp
->data
.physical
,
1336 ext_list_tmp
->data
.logical
,
1337 ext_list_tmp
->data
.len
);
1338 ext_list_tmp
= ext_list_tmp
->next
;
1339 } while (ext_list_tmp
!= logical_list_head
);
1342 printf("%-40s%10s/%-10s%9s\n",
1343 "<File>", "now", "best", "ratio");
1344 if (current_uid
== ROOT_UID
) {
1345 if (strlen(file
) > 40)
1346 printf("%s\n%50d/%-10d%8.2f%%\n",
1347 file
, now_ext_count
,
1348 best_ext_count
, ratio
);
1350 printf("%-40s%10d/%-10d%8.2f%%\n",
1351 file
, now_ext_count
,
1352 best_ext_count
, ratio
);
1354 if (strlen(file
) > 40)
1355 printf("%s\n%50d/%-10s%7s\n",
1356 file
, now_ext_count
,
1359 printf("%-40s%10d/%-10s%7s\n",
1360 file
, now_ext_count
,
1368 if (mode_flag
& DETAIL
) {
1369 /* Print statistic info */
1370 sprintf(msg_buffer
, "[%u/%u]%s",
1371 defraged_file_count
, total_count
, file
);
1372 if (current_uid
== ROOT_UID
) {
1373 if (strlen(msg_buffer
) > 40)
1374 printf("\033[79;0H\033[K%s\n"
1375 "%50d/%-10d%8.2f%%\n",
1376 msg_buffer
, now_ext_count
,
1377 best_ext_count
, ratio
);
1379 printf("\033[79;0H\033[K%-40s"
1380 "%10d/%-10d%8.2f%%\n",
1381 msg_buffer
, now_ext_count
,
1382 best_ext_count
, ratio
);
1384 if (strlen(msg_buffer
) > 40)
1385 printf("\033[79;0H\033[K%s\n%50d/%-10s%7s\n",
1386 msg_buffer
, now_ext_count
,
1389 printf("\033[79;0H\033[K%-40s%10d/%-10s%7s\n",
1390 msg_buffer
, now_ext_count
,
1395 for (i
= 0; i
< SHOW_FRAG_FILES
; i
++) {
1396 if (ratio
>= frag_rank
[i
].ratio
) {
1397 for (j
= SHOW_FRAG_FILES
- 1; j
> i
; j
--) {
1398 memcpy(&frag_rank
[j
], &frag_rank
[j
- 1],
1399 sizeof(struct frag_statistic_ino
));
1401 memset(&frag_rank
[i
], 0,
1402 sizeof(struct frag_statistic_ino
));
1403 strncpy(frag_rank
[i
].msg_buffer
, file
,
1404 strnlen(file
, PATH_MAX
));
1405 frag_rank
[i
].now_count
= now_ext_count
;
1406 frag_rank
[i
].best_count
= best_ext_count
;
1407 frag_rank
[i
].ratio
= ratio
;
1416 free_ext(physical_list_head
);
1417 free_ext(logical_list_head
);
1422 * print_progress - Print defrag progress
1425 * @start: logical offset for defrag target file
1426 * @file_size: defrag target filesize
1428 static void print_progress(const char *file
, loff_t start
, loff_t file_size
)
1430 int percent
= (start
* 100) / file_size
;
1431 printf("\033[79;0H\033[K[%u/%u]%s:\t%3d%%",
1432 defraged_file_count
, total_count
, file
, min(percent
, 100));
1439 * call_defrag() - Execute the defrag program.
1441 * @fd: target file descriptor.
1442 * @donor_fd: donor file descriptor.
1443 * @file: target file name.
1444 * @buf: pointer of the struct stat64.
1445 * @ext_list_head: head of the extent list.
1447 static int call_defrag(int fd
, int donor_fd
, const char *file
,
1448 const struct stat64
*buf
, struct fiemap_extent_list
*ext_list_head
)
1451 unsigned int page_num
;
1452 unsigned char *vec
= NULL
;
1453 int defraged_ret
= 0;
1455 struct move_extent move_data
;
1456 struct fiemap_extent_list
*ext_list_tmp
= NULL
;
1458 memset(&move_data
, 0, sizeof(struct move_extent
));
1459 move_data
.donor_fd
= donor_fd
;
1461 /* Print defrag progress */
1462 print_progress(file
, start
, buf
->st_size
);
1464 ext_list_tmp
= ext_list_head
;
1466 move_data
.orig_start
= ext_list_tmp
->data
.logical
;
1467 /* Logical offset of orig and donor should be same */
1468 move_data
.donor_start
= move_data
.orig_start
;
1469 move_data
.len
= ext_list_tmp
->data
.len
;
1470 move_data
.moved_len
= 0;
1472 ret
= page_in_core(fd
, move_data
, &vec
, &page_num
);
1474 if (mode_flag
& DETAIL
) {
1476 PRINT_ERR_MSG_WITH_ERRNO(
1477 "Failed to get file map");
1479 printf("\t[ NG ]\n");
1484 /* EXT4_IOC_MOVE_EXT */
1486 ioctl(fd
, EXT4_IOC_MOVE_EXT
, &move_data
);
1489 ret
= defrag_fadvise(fd
, move_data
, vec
, page_num
);
1495 if (mode_flag
& DETAIL
) {
1497 PRINT_ERR_MSG_WITH_ERRNO(
1498 "Failed to free page");
1500 printf("\t[ NG ]\n");
1505 if (defraged_ret
< 0) {
1506 if (mode_flag
& DETAIL
) {
1508 PRINT_ERR_MSG_WITH_ERRNO(
1509 "Failed to defrag");
1511 printf("\t[ NG ]\n");
1515 /* Adjust logical offset for next ioctl */
1516 move_data
.orig_start
+= move_data
.moved_len
;
1517 move_data
.donor_start
= move_data
.orig_start
;
1519 start
= move_data
.orig_start
* buf
->st_blksize
;
1521 /* Print defrag progress */
1522 print_progress(file
, start
, buf
->st_size
);
1525 if (start
>= buf
->st_size
)
1528 ext_list_tmp
= ext_list_tmp
->next
;
1529 } while (ext_list_tmp
!= ext_list_head
);
1535 * file_defrag() - Check file attributes and call ioctl to defrag.
1537 * @file: the file's name.
1538 * @buf: the pointer of the struct stat64.
1540 * @ftwbuf: the pointer of a struct FTW.
1542 static int file_defrag(const char *file
, const struct stat64
*buf
,
1543 int flag
EXT2FS_ATTR((unused
)),
1544 struct FTW
*ftwbuf
EXT2FS_ATTR((unused
)))
1550 int file_frags_start
, file_frags_end
;
1551 int orig_physical_cnt
, donor_physical_cnt
= 0;
1552 char tmp_inode_name
[PATH_MAX
+ 8];
1553 struct fiemap_extent_list
*orig_list_physical
= NULL
;
1554 struct fiemap_extent_list
*orig_list_logical
= NULL
;
1555 struct fiemap_extent_list
*donor_list_physical
= NULL
;
1556 struct fiemap_extent_list
*donor_list_logical
= NULL
;
1557 struct fiemap_extent_group
*orig_group_head
= NULL
;
1558 struct fiemap_extent_group
*orig_group_tmp
= NULL
;
1560 defraged_file_count
++;
1562 if (mode_flag
& DETAIL
) {
1563 printf("[%u/%u]", defraged_file_count
, total_count
);
1567 if (lost_found_dir
[0] != '\0' &&
1568 !memcmp(file
, lost_found_dir
, strnlen(lost_found_dir
, PATH_MAX
))) {
1569 if (mode_flag
& DETAIL
) {
1570 PRINT_FILE_NAME(file
);
1571 IN_FTW_PRINT_ERR_MSG(NGMSG_LOST_FOUND
);
1576 if (!S_ISREG(buf
->st_mode
)) {
1577 if (mode_flag
& DETAIL
) {
1578 PRINT_FILE_NAME(file
);
1579 IN_FTW_PRINT_ERR_MSG(NGMSG_FILE_UNREG
);
1585 if (buf
->st_size
== 0) {
1586 if (mode_flag
& DETAIL
) {
1587 PRINT_FILE_NAME(file
);
1588 IN_FTW_PRINT_ERR_MSG("File size is 0");
1594 if (buf
->st_blocks
== 0) {
1595 if (mode_flag
& DETAIL
) {
1596 PRINT_FILE_NAME(file
);
1597 STATISTIC_ERR_MSG("File has no blocks");
1602 fd
= open64(file
, O_RDONLY
);
1604 if (mode_flag
& DETAIL
) {
1605 PRINT_FILE_NAME(file
);
1606 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN
);
1611 /* Get file's extents */
1612 ret
= get_file_extents(fd
, &orig_list_physical
);
1614 if (mode_flag
& DETAIL
) {
1615 PRINT_FILE_NAME(file
);
1616 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT
);
1621 /* Get the count of file's continuous physical region */
1622 orig_physical_cnt
= get_physical_count(orig_list_physical
);
1624 /* Change list from physical to logical */
1625 ret
= change_physical_to_logical(&orig_list_physical
,
1626 &orig_list_logical
);
1628 if (mode_flag
& DETAIL
) {
1629 PRINT_FILE_NAME(file
);
1630 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT
);
1635 /* Count file fragments before defrag */
1636 file_frags_start
= get_logical_count(orig_list_logical
);
1638 if (file_check(fd
, buf
, file
, file_frags_start
) < 0)
1641 if (fsync(fd
) < 0) {
1642 if (mode_flag
& DETAIL
) {
1643 PRINT_FILE_NAME(file
);
1644 PRINT_ERR_MSG_WITH_ERRNO("Failed to sync(fsync)");
1649 if (current_uid
== ROOT_UID
)
1651 get_best_count(SECTOR_TO_BLOCK(buf
->st_blocks
, block_size
));
1655 if (file_frags_start
<= best
)
1656 goto check_improvement
;
1658 /* Combine extents to group */
1659 ret
= join_extents(orig_list_logical
, &orig_group_head
);
1661 if (mode_flag
& DETAIL
) {
1662 PRINT_FILE_NAME(file
);
1663 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT
);
1668 /* Create donor inode */
1669 memset(tmp_inode_name
, 0, PATH_MAX
+ 8);
1670 sprintf(tmp_inode_name
, "%.*s.defrag",
1671 (int)strnlen(file
, PATH_MAX
), file
);
1672 donor_fd
= open64(tmp_inode_name
, O_WRONLY
| O_CREAT
| O_EXCL
, S_IRUSR
);
1674 if (mode_flag
& DETAIL
) {
1675 PRINT_FILE_NAME(file
);
1676 if (errno
== EEXIST
)
1677 PRINT_ERR_MSG_WITH_ERRNO(
1678 "File is being defraged by other program");
1680 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN
);
1685 /* Unlink donor inode */
1686 ret
= unlink(tmp_inode_name
);
1688 if (mode_flag
& DETAIL
) {
1689 PRINT_FILE_NAME(file
);
1690 PRINT_ERR_MSG_WITH_ERRNO("Failed to unlink");
1695 /* Allocate space for donor inode */
1696 orig_group_tmp
= orig_group_head
;
1698 ret
= fallocate(donor_fd
, 0,
1699 (loff_t
)orig_group_tmp
->start
->data
.logical
* block_size
,
1700 (loff_t
)orig_group_tmp
->len
* block_size
);
1702 if (mode_flag
& DETAIL
) {
1703 PRINT_FILE_NAME(file
);
1704 PRINT_ERR_MSG_WITH_ERRNO("Failed to fallocate");
1709 orig_group_tmp
= orig_group_tmp
->next
;
1710 } while (orig_group_tmp
!= orig_group_head
);
1712 /* Get donor inode's extents */
1713 ret
= get_file_extents(donor_fd
, &donor_list_physical
);
1715 if (mode_flag
& DETAIL
) {
1716 PRINT_FILE_NAME(file
);
1717 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT
);
1722 /* Calcuate donor inode's continuous physical region */
1723 donor_physical_cnt
= get_physical_count(donor_list_physical
);
1725 /* Change donor extent list from physical to logical */
1726 ret
= change_physical_to_logical(&donor_list_physical
,
1727 &donor_list_logical
);
1729 if (mode_flag
& DETAIL
) {
1730 PRINT_FILE_NAME(file
);
1731 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_EXTENT
);
1737 if (mode_flag
& DETAIL
) {
1738 if (file_frags_start
!= 1)
1739 frag_files_before_defrag
++;
1741 extents_before_defrag
+= file_frags_start
;
1744 if (file_frags_start
<= best
||
1745 orig_physical_cnt
<= donor_physical_cnt
) {
1746 printf("\033[79;0H\033[K[%u/%u]%s:\t%3d%%",
1747 defraged_file_count
, total_count
, file
, 100);
1748 if (mode_flag
& DETAIL
)
1749 printf(" extents: %d -> %d",
1750 file_frags_start
, file_frags_start
);
1752 printf("\t[ OK ]\n");
1755 if (file_frags_start
!= 1)
1756 frag_files_after_defrag
++;
1758 extents_after_defrag
+= file_frags_start
;
1762 /* Defrag the file */
1763 ret
= call_defrag(fd
, donor_fd
, file
, buf
, donor_list_logical
);
1765 /* Count file fragments after defrag and print extents info */
1766 if (mode_flag
& DETAIL
) {
1767 file_frags_end
= file_frag_count(fd
);
1768 if (file_frags_end
< 0) {
1770 PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_INFO
);
1774 if (file_frags_end
!= 1)
1775 frag_files_after_defrag
++;
1777 extents_after_defrag
+= file_frags_end
;
1782 printf(" extents: %d -> %d",
1783 file_frags_start
, file_frags_end
);
1790 printf("\t[ OK ]\n");
1798 free_ext(orig_list_physical
);
1799 free_ext(orig_list_logical
);
1800 free_ext(donor_list_physical
);
1801 free_exts_group(orig_group_head
);
1806 * main() - Ext4 online defrag.
1808 * @argc: the number of parameter.
1809 * @argv[]: the pointer array of parameter.
1811 int main(int argc
, char *argv
[])
1815 int flags
= FTW_PHYS
| FTW_MOUNT
;
1817 int success_flag
= 0;
1818 char dir_name
[PATH_MAX
+ 1];
1820 struct ext4_super_block sb
;
1822 /* Parse arguments */
1826 while ((opt
= getopt(argc
, argv
, "vc")) != EOF
) {
1829 mode_flag
|= DETAIL
;
1832 mode_flag
|= STATISTIC
;
1842 current_uid
= getuid();
1845 for (i
= optind
; i
< argc
; i
++) {
1849 frag_files_before_defrag
= 0;
1850 frag_files_after_defrag
= 0;
1851 extents_before_defrag
= 0;
1852 extents_after_defrag
= 0;
1853 defraged_file_count
= 0;
1854 files_block_count
= 0;
1855 blocks_per_group
= 0;
1856 feature_incompat
= 0;
1857 log_groups_per_flex
= 0;
1859 memset(dir_name
, 0, PATH_MAX
+ 1);
1860 memset(lost_found_dir
, 0, PATH_MAX
+ 1);
1861 memset(frag_rank
, 0,
1862 sizeof(struct frag_statistic_ino
) * SHOW_FRAG_FILES
);
1864 if ((mode_flag
& STATISTIC
) && i
> optind
)
1867 #if BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN
1868 PRINT_ERR_MSG("Endian's type is not big/little endian");
1869 PRINT_FILE_NAME(argv
[i
]);
1873 if (lstat64(argv
[i
], &buf
) < 0) {
1874 perror(NGMSG_FILE_INFO
);
1875 PRINT_FILE_NAME(argv
[i
]);
1879 if (S_ISBLK(buf
.st_mode
)) {
1881 if (get_mount_point(argv
[i
], dir_name
, PATH_MAX
) < 0)
1883 if (lstat64(dir_name
, &buf
) < 0) {
1884 perror(NGMSG_FILE_INFO
);
1885 PRINT_FILE_NAME(argv
[i
]);
1889 if (!(mode_flag
& STATISTIC
))
1890 printf("ext4 defragmentation for device(%s)\n",
1892 } else if (S_ISDIR(buf
.st_mode
)) {
1894 if (access(argv
[i
], R_OK
) < 0) {
1899 strncpy(dir_name
, argv
[i
], strnlen(argv
[i
], PATH_MAX
));
1900 } else if (S_ISREG(buf
.st_mode
)) {
1902 arg_type
= FILENAME
;
1904 /* Irregular file */
1905 PRINT_ERR_MSG(NGMSG_FILE_UNREG
);
1906 PRINT_FILE_NAME(argv
[i
]);
1911 block_size
= buf
.st_blksize
;
1914 * filesystem type checked in get_mount_point()
1916 if (arg_type
== FILENAME
|| arg_type
== DIRNAME
) {
1917 if (is_ext4(argv
[i
]) < 0)
1919 if (realpath(argv
[i
], dir_name
) == NULL
) {
1920 perror("Couldn't get full path");
1921 PRINT_FILE_NAME(argv
[i
]);
1926 if (current_uid
== ROOT_UID
) {
1927 /* Get super block info */
1928 memset(&sb
, 0, sizeof(struct ext4_super_block
));
1929 if (get_superblock_info(dir_name
, &sb
) < 0) {
1930 if (mode_flag
& DETAIL
) {
1931 perror("Can't get super block info");
1932 PRINT_FILE_NAME(argv
[i
]);
1937 blocks_per_group
= ext2fs_swab32(sb
.s_blocks_per_group
);
1938 feature_incompat
= ext2fs_swab32(sb
.s_feature_incompat
);
1939 log_groups_per_flex
= sb
.s_log_groups_per_flex
;
1944 if (!(mode_flag
& STATISTIC
))
1945 printf("ext4 defragmentation "
1946 "for directory(%s)\n", argv
[i
]);
1948 int mount_dir_len
= 0;
1949 mount_dir_len
= strnlen(lost_found_dir
, PATH_MAX
);
1951 strncat(lost_found_dir
, "/lost+found",
1952 PATH_MAX
- strnlen(lost_found_dir
, PATH_MAX
));
1954 /* Not the case("e4defrag mount_piont_dir") */
1955 if (dir_name
[mount_dir_len
] != '\0') {
1957 * "e4defrag mount_piont_dir/lost+found"
1958 * or "e4defrag mount_piont_dir/lost+found/"
1960 if (strncmp(lost_found_dir
, dir_name
,
1961 strnlen(lost_found_dir
,
1963 (dir_name
[strnlen(lost_found_dir
,
1964 PATH_MAX
)] == '\0' ||
1965 dir_name
[strnlen(lost_found_dir
,
1966 PATH_MAX
)] == '/')) {
1967 PRINT_ERR_MSG(NGMSG_LOST_FOUND
);
1968 PRINT_FILE_NAME(argv
[i
]);
1972 /* "e4defrag mount_piont_dir/else_dir" */
1973 memset(lost_found_dir
, 0, PATH_MAX
+ 1);
1976 if (arg_type
== DEVNAME
) {
1977 strncpy(lost_found_dir
, dir_name
,
1978 strnlen(dir_name
, PATH_MAX
));
1979 strncat(lost_found_dir
, "/lost+found/",
1980 PATH_MAX
- strnlen(lost_found_dir
,
1984 nftw64(dir_name
, calc_entry_counts
, FTW_OPEN_FD
, flags
);
1986 if (mode_flag
& STATISTIC
) {
1987 if (mode_flag
& DETAIL
)
1988 printf("%-40s%10s/%-10s%9s\n",
1989 "<File>", "now", "best", "ratio");
1991 if (!(mode_flag
& DETAIL
) &&
1992 current_uid
!= ROOT_UID
) {
1997 nftw64(dir_name
, file_statistic
,
1998 FTW_OPEN_FD
, flags
);
2000 if (succeed_cnt
!= 0 &&
2001 current_uid
== ROOT_UID
) {
2002 if (mode_flag
& DETAIL
)
2004 printf("%-40s%10s/%-10s%9s\n",
2005 "<Fragmented files>", "now",
2007 for (j
= 0; j
< SHOW_FRAG_FILES
; j
++) {
2008 if (strlen(frag_rank
[j
].
2010 printf("%d. %s\n%50d/"
2011 "%-10d%8.2f%%\n", j
+ 1,
2012 frag_rank
[j
].msg_buffer
,
2013 frag_rank
[j
].now_count
,
2014 frag_rank
[j
].best_count
,
2015 frag_rank
[j
].ratio
);
2016 } else if (strlen(frag_rank
[j
].
2018 printf("%d. %-37s%10d/"
2019 "%-10d%8.2f%%\n", j
+ 1,
2020 frag_rank
[j
].msg_buffer
,
2021 frag_rank
[j
].now_count
,
2022 frag_rank
[j
].best_count
,
2023 frag_rank
[j
].ratio
);
2030 /* File tree walk */
2031 nftw64(dir_name
, file_defrag
, FTW_OPEN_FD
, flags
);
2032 printf("\n\tSuccess:\t\t\t[ %u/%u ]\n", succeed_cnt
,
2034 printf("\tFailure:\t\t\t[ %u/%u ]\n",
2035 total_count
- succeed_cnt
, total_count
);
2036 if (mode_flag
& DETAIL
) {
2037 printf("\tTotal extents:\t\t\t%4d->%d\n",
2038 extents_before_defrag
,
2039 extents_after_defrag
);
2040 printf("\tFragmented percentage:\t\t"
2041 "%3llu%%->%llu%%\n",
2042 !regular_count
? 0 :
2043 ((unsigned long long)
2044 frag_files_before_defrag
* 100) /
2046 !regular_count
? 0 :
2047 ((unsigned long long)
2048 frag_files_after_defrag
* 100) /
2055 strncat(lost_found_dir
, "/lost+found/",
2056 PATH_MAX
- strnlen(lost_found_dir
,
2058 if (strncmp(lost_found_dir
, dir_name
,
2059 strnlen(lost_found_dir
,
2061 PRINT_ERR_MSG(NGMSG_LOST_FOUND
);
2062 PRINT_FILE_NAME(argv
[i
]);
2066 if (mode_flag
& STATISTIC
) {
2067 file_statistic(argv
[i
], &buf
, FTW_F
, NULL
);
2070 printf("ext4 defragmentation for %s\n",
2072 /* Defrag single file process */
2073 file_defrag(argv
[i
], &buf
, FTW_F
, NULL
);
2074 if (succeed_cnt
!= 0)
2075 printf(" Success:\t\t\t[1/1]\n");
2077 printf(" Success:\t\t\t[0/1]\n");
2082 if (succeed_cnt
!= 0)
2084 if (mode_flag
& STATISTIC
) {
2085 if (current_uid
!= ROOT_UID
) {
2091 if (mode_flag
& DETAIL
)
2094 if (arg_type
== DEVNAME
)
2095 printf(" In this device(%s), "
2096 "none can be defragmented.\n", argv
[i
]);
2097 else if (arg_type
== DIRNAME
)
2098 printf(" In this directory(%s), "
2099 "none can be defragmented.\n", argv
[i
]);
2101 printf(" This file(%s) "
2102 "can't be defragmented.\n", argv
[i
]);
2104 float files_ratio
= 0.0;
2106 files_ratio
= (float)(extents_before_defrag
-
2107 extents_after_defrag
) *
2108 100 / files_block_count
;
2109 score
= CALC_SCORE(files_ratio
);
2110 printf("\n Total/best extents\t\t\t\t%d/%d\n"
2111 " Fragmentation ratio\t\t\t\t%.2f%%\n"
2112 " Fragmentation score\t\t\t\t%.2f\n",
2113 extents_before_defrag
,
2114 extents_after_defrag
,
2115 files_ratio
, score
);
2116 printf(" [0-30 no problem:"
2117 " 31-55 a little bit fragmented:"
2118 " 55- needs defrag]\n");
2120 if (arg_type
== DEVNAME
)
2121 printf(" This device(%s) ", argv
[i
]);
2122 else if (arg_type
== DIRNAME
)
2123 printf(" This directory(%s) ", argv
[i
]);
2125 printf(" This file(%s) ", argv
[i
]);
2127 if (score
> BOUND_SCORE
)
2128 printf("needs defragmentation.\n");
2130 printf("does not need "
2131 "defragmentation.\n");