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