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