]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - misc/e4defrag.c
e4defrag: output size per extent by -c option
[thirdparty/e2fsprogs.git] / misc / e4defrag.c
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 */
129 typedef unsigned long long ext4_fsblk_t;
130
131 struct fiemap_extent_data {
132 __u64 len; /* blocks count */
133 __u64 logical; /* start logical block number */
134 ext4_fsblk_t physical; /* start physical block number */
135 };
136
137 struct fiemap_extent_list {
138 struct fiemap_extent_list *prev;
139 struct fiemap_extent_list *next;
140 struct fiemap_extent_data data; /* extent belong to file */
141 };
142
143 struct fiemap_extent_group {
144 struct fiemap_extent_group *prev;
145 struct fiemap_extent_group *next;
146 __u64 len; /* length of this continuous region */
147 struct fiemap_extent_list *start; /* start ext */
148 struct fiemap_extent_list *end; /* end ext */
149 };
150
151 struct 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
160 struct frag_statistic_ino {
161 int now_count; /* the file's extents count of before defrag */
162 int best_count; /* the best file's extents count */
163 __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
168 typedef __u16 __le16;
169 typedef __u32 __le32;
170 typedef __u64 __le64;
171
172 /*
173 * Structure of the super block
174 */
175 struct 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
263 char lost_found_dir[PATH_MAX + 1];
264 int block_size;
265 int extents_before_defrag;
266 int extents_after_defrag;
267 int mode_flag;
268 unsigned int current_uid;
269 unsigned int defraged_file_count;
270 unsigned int frag_files_before_defrag;
271 unsigned int frag_files_after_defrag;
272 unsigned int regular_count;
273 unsigned int succeed_cnt;
274 unsigned int total_count;
275 __u8 log_groups_per_flex;
276 __le32 blocks_per_group;
277 __le32 feature_incompat;
278 ext4_fsblk_t files_block_count;
279 struct 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 */
299 static 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 */
322 int 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 */
347 static 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 */
360 static 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 */
397 static 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 */
475 static 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 */
495 static 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 */
541 static 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 */
588 static 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 */
623 static 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 */
651 static 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 */
715 static 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;
762 out:
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 */
773 static 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;
820 out:
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 */
831 static 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 */
861 static 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 */
916 static 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;
986 out:
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 */
996 static 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 */
1014 static 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 */
1038 static 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 */
1079 static 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 */
1101 static 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 */
1124 static 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
1172 out:
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 */
1184 static 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 */
1210 static 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
1441 out:
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 */
1455 static 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 */
1474 static 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 */
1573 static 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
1767 check_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
1825 out:
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 */
1842 int 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
2183 out:
2184 printf(MSG_USAGE);
2185 exit(1);
2186 }
2187