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