More patch description fixups. Standardize case.
[ext4-patch-queue.git] / ext4-online-defrag-defrag-command.c
blob10b4dc184044a94b4a002a9e7e988a6b47a065fd
1 /*
2 * e4defrag.c - ext4 filesystem defragmenter
3 */
5 #ifndef _LARGEFILE_SOURCE
6 #define _LARGEFILE_SOURCE
7 #endif
9 #ifndef _LARGEFILE64_SOURCE
10 #define _LARGEFILE64_SOURCE
11 #endif
13 #define _XOPEN_SOURCE 500
14 #define _GNU_SOURCE
15 #include <ftw.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/stat.h>
20 #include <dirent.h>
21 #include <limits.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <sys/statfs.h>
26 #include <sys/vfs.h>
27 #include <sys/ioctl.h>
28 #include <mntent.h>
29 #include <linux/fs.h>
30 #include <ctype.h>
31 #include <sys/syscall.h>
32 #include <sys/mman.h>
34 #define EXT4_IOC_DEFRAG _IOW('f', 10, struct ext4_ext_defrag_data)
35 #define EXT4_IOC_GROUP_INFO _IOW('f', 11, struct ext4_group_data_info)
36 #define EXT4_IOC_FREE_BLOCKS_INFO _IOW('f', 12, struct ext4_extents_info)
37 #define EXT4_IOC_EXTENTS_INFO _IOW('f', 13, struct ext4_extents_info)
38 #define EXT4_IOC_RESERVE_BLOCK _IOW('f', 14, struct ext4_extents_info)
39 #define EXT4_IOC_MOVE_VICTIM _IOW('f', 15, struct ext4_extents_info)
40 #define EXT4_IOC_BLOCK_RELEASE _IO('f', 16)
42 #define DEFRAG_MAX_ENT 32
44 /* Extent status which are used in extent_t */
45 #define EXT4_EXT_USE 0
46 #define EXT4_EXT_FREE 1
47 #define EXT4_EXT_RESERVE 2
49 /* Insert list2 after list1 */
50 #define insert(list1, list2) \
51 do { \
52 list2->next = list1->next; \
53 list1->next->prev = list2; \
54 list2->prev = list1; \
55 list1->next = list2; \
56 } while (0)
58 #define DEFRAG_FORCE_VICTIM 2
60 /* Magic number for ext4 */
61 #define EXT4_SUPER_MAGIC 0xEF53
63 /* Force defrag mode: Max file size in bytes (128MB) */
64 #define MAX_FILE_SIZE (unsigned long)1 << 27
66 /* Data type for filesystem-wide blocks number */
67 typedef unsigned long long ext4_fsblk_t;
69 /* data type for file logical block number */
70 typedef unsigned int ext4_lblk_t;
72 /* data type for block offset of block group */
73 typedef int ext4_grpblk_t;
75 /* Ioctl command */
76 #define EXT4_IOC_FIBMAP _IOW('f', 9, ext4_fsblk_t)
78 #ifndef __NR_fadvise64
79 #define __NR_fadvise64 250
80 #endif
82 #ifndef __NR_sync_file_range
83 #define __NR_sync_file_range 314
84 #endif
86 #ifndef POSIX_FADV_DONTNEED
87 #if defined(__s390x__)
88 #define POSIX_FADV_DONTNEED 6 /* Don't need these pages. */
89 #else
90 #define POSIX_FADV_DONTNEED 4 /* Don't need these pages. */
91 #endif
92 #endif
94 #ifndef SYNC_FILE_RANGE_WAIT_BEFORE
95 #define SYNC_FILE_RANGE_WAIT_BEFORE 1
96 #endif
97 #ifndef SYNC_FILE_RANGE_WRITE
98 #define SYNC_FILE_RANGE_WRITE 2
99 #endif
100 #ifndef SYNC_FILE_RANGE_WAIT_AFTER
101 #define SYNC_FILE_RANGE_WAIT_AFTER 4
102 #endif
104 #define DEVNAME 0
105 #define DIRNAME 1
106 #define FILENAME 2
108 #define RETURN_OK 0
109 #define RETURN_NG -1
110 #define FTW_CONT 0
111 #define FTW_OPEN_FD 2000
112 #define FILE_CHK_OK 0
113 #define FILE_CHK_NG -1
114 #define FS_EXT4 "ext4dev"
115 #define ROOT_UID 0
116 #define CHECK_FRAG_COUNT 1
118 /* Defrag block size, in bytes */
119 #define DEFRAG_SIZE 67108864
121 #define min(x, y) (((x) > (y)) ? (y) : (x))
123 #define PRINT_ERR_MSG(msg) fprintf(stderr, "%s\n", (msg));
124 #define PRINT_FILE_NAME(file) fprintf(stderr, "\t\t \"%s\"\n", (file));
126 #define MSG_USAGE \
127 "Usage : e4defrag [-v] file...| directory...| device...\n\
128 : e4defrag -f file [blocknr] \n\
129 : e4defrag -r directory... | device... \n"
131 #define MSG_R_OPTION " with regional block allocation mode.\n"
132 #define NGMSG_MTAB "\te4defrag : Can not access /etc/mtab."
133 #define NGMSG_UNMOUNT "\te4defrag : FS is not mounted."
134 #define NGMSG_EXT4 "\te4defrag : FS is not ext4 File System."
135 #define NGMSG_FS_INFO "\te4defrag : get FSInfo fail."
136 #define NGMSG_FILE_INFO "\te4defrag : get FileInfo fail."
137 #define NGMSG_FILE_OPEN "\te4defrag : open fail."
138 #define NGMSG_FILE_SYNC "\te4defrag : sync(fsync) fail."
139 #define NGMSG_FILE_DEFRAG "\te4defrag : defrag fail."
140 #define NGMSG_FILE_BLOCKSIZE "\te4defrag : can't get blocksize."
141 #define NGMSG_FILE_FIBMAP "\te4defrag : can't get block number."
142 #define NGMSG_FILE_UNREG "\te4defrag : File is not regular file."
144 #define NGMSG_FILE_LARGE \
145 "\te4defrag : Defrag size is larger than FileSystem's free space."
147 #define NGMSG_FILE_PRIORITY \
148 "\te4defrag : File is not current user's file or current user is not root."
150 #define NGMSG_FILE_LOCK "\te4defrag : File is locked."
151 #define NGMSG_FILE_BLANK "\te4defrag : File size is 0."
152 #define NGMSG_GET_LCKINFO "\te4defrag : get LockInfo fail."
153 #define NGMSG_TYPE \
154 "e4defrag : Can not process %s in regional mode.\n"
155 #define NGMSG_LOST_FOUND "\te4defrag : Can not process \"lost+found\"."
156 #define NGMSG_REALPATH "\te4defrag : Can not get full path."
157 #define NGMSG_FILE_MAP "\te4defrag : get file map fail."
158 #define NGMSG_FILE_DROP_BUFFER "\te4defrag : free page fail."
159 #define NGMSG_FADVISE_SYSCALL "\tfadvise fail."
161 struct ext4_extent_data {
162 ext4_lblk_t block; /* start logical block number */
163 ext4_fsblk_t start; /* start physical block number */
164 int len; /* blocks count */
167 /* Used for defrag */
168 struct ext4_ext_defrag_data {
169 ext4_lblk_t start_offset; /* start offset to defrag in blocks */
170 ext4_lblk_t defrag_size; /* size of defrag in blocks */
171 ext4_fsblk_t goal; /* block offset for allocation */
172 int flag; /* free space mode flag */
173 struct ext4_extent_data ext;
176 struct ext4_group_data_info {
177 int s_blocks_per_group; /* blocks per group */
178 int s_inodes_per_group; /* inodes per group */
181 struct ext4_extents_info {
182 unsigned long long ino; /* inode number */
183 int max_entries; /* maximum extents count */
184 int entries; /* extent number/count */
185 ext4_lblk_t f_offset; /* file offset */
186 ext4_grpblk_t g_offset; /* group offset */
187 ext4_fsblk_t goal;
188 struct ext4_extent_data ext[DEFRAG_MAX_ENT];
191 typedef struct extent {
192 struct extent *prev;
193 unsigned long tag; /* Extent status */
194 unsigned long ino; /* File's inode number */
195 struct ext4_extent_data data; /* Extent belong to file */
196 struct extent *next;
197 } extent_t;
199 typedef struct exts_group {
200 struct exts_group *prev;
201 extent_t *start; /* Start ext */
202 extent_t *end; /* End ext */
203 int len; /* Length of this continuous region */
204 struct exts_group *next;
205 } exts_group_t;
207 typedef struct extent_wrap {
208 struct extent_wrap *prev, *next;
209 struct extent *group_ext;
210 } extent_wrap_t;
212 int force_flag;
213 int detail_flag;
214 int regional_flag;
215 int amount_cnt;
216 int succeed_cnt;
217 char lost_found_dir[PATH_MAX + 1];
218 ext4_fsblk_t goal;
219 ext4_fsblk_t fgoal = -1;
222 * fadvise() - advise operater system process page cache.
224 * @fd: file descriptor.
225 * @offset: file offset.
226 * @len: area length.
227 * @advise: process flag.
229 int fadvise(int fd, loff_t offset, size_t len, int advise)
231 return syscall(__NR_fadvise64, fd, offset, len, advise);
235 * sync_file_range() - sync file region.
237 * @fd: file descriptor.
238 * @offset: file offset.
239 * @length: area length.
240 * @advise: process flag.
242 int sync_file_range(int fd, loff_t offset, loff_t length, unsigned int flag)
244 return syscall(__NR_sync_file_range, fd, offset, length, flag);
248 * page_in_core() - get information on whether pages are in core.
250 * @fd: file descriptor.
251 * @defrag_data: data used for defrag.
252 * @vec: page state array.
253 * @page_num: page number.
255 int page_in_core(int fd, struct ext4_ext_defrag_data defrag_data,
256 unsigned char **vec, unsigned long *page_num)
258 int blocksize;
259 int pagesize = getpagesize();
260 void *page = NULL;
261 loff_t offset, end_offset, length;
263 if (vec == NULL || *vec != NULL) {
264 return RETURN_NG;
267 if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
268 return RETURN_NG;
271 /*in mmap, offset should be a multiple of the page size */
272 offset = defrag_data.start_offset * blocksize;
273 length = defrag_data.defrag_size * blocksize;
274 end_offset = offset + length;
275 /* round the offset down to the nearest multiple of pagesize */
276 offset = (offset / pagesize) * pagesize;
277 length = end_offset - offset;
279 page = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, offset);
280 if (page == MAP_FAILED) {
281 return RETURN_NG;
284 *page_num = 0;
285 *page_num = (length + pagesize - 1) / pagesize;
286 *vec = (unsigned char *)calloc(*page_num, 1);
287 if (*vec == NULL) {
288 return RETURN_NG;
291 /* get information on whether pages are in core */
292 if (mincore(page, (size_t)length, *vec) == -1) {
293 if (*vec) {
294 free(*vec);
296 return RETURN_NG;
299 if (munmap(page, length) == -1) {
300 if (*vec) {
301 free(*vec);
303 return RETURN_NG;
306 return RETURN_OK;
310 * defrag_fadvise() - predeclare an access pattern for file data.
312 * @fd: file descriptor.
313 * @defrag_data: data used for defrag.
314 * @vec: page state array.
315 * @page_num: page number.
317 int defrag_fadvise(int fd, struct ext4_ext_defrag_data defrag_data,
318 unsigned char *vec, unsigned long page_num)
320 int flag = 1;
321 int blocksize;
322 int pagesize = getpagesize();
323 int fadvise_flag = POSIX_FADV_DONTNEED;
324 int sync_flag = SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE|
325 SYNC_FILE_RANGE_WAIT_AFTER;
326 unsigned long i;
327 loff_t offset;
329 if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
330 return RETURN_NG;
333 offset = (loff_t)defrag_data.start_offset * blocksize;
334 offset = (offset / pagesize) * pagesize;
336 /* sync file for fadvise process */
337 if (sync_file_range(fd, offset, (loff_t)pagesize*page_num, sync_flag)
338 != 0) {
339 return RETURN_NG;
342 /* try to release buffer cache this process used,
343 * then other process can use the released buffer */
344 for (i = 0; i < page_num; i++) {
345 if ((vec[i] & 0x1) == 0) {
346 offset += pagesize;
347 continue;
349 if (fadvise(fd, offset, pagesize, fadvise_flag) != 0) {
350 if (detail_flag && flag) {
351 perror(NGMSG_FADVISE_SYSCALL);
352 flag = 0;
355 offset += pagesize;
358 return RETURN_OK;
361 * check_free_size() - Check if there's enough disk space.
363 * @fd: the file's descriptor.
364 * @buf: the pointer of the struct stat64.
366 int check_free_size(int fd, const struct stat64 *buf)
368 off64_t size = 0;
369 off64_t free_size = 0;
370 struct statfs fsbuf;
372 /* target file size */
373 size = buf->st_size;
375 if (fstatfs(fd, &fsbuf) < 0) {
376 if (detail_flag) {
377 perror(NGMSG_FS_INFO);
379 return RETURN_NG;
382 /* Compute free space for root and normal user separately */
383 if (getuid() == ROOT_UID) {
384 free_size = (off64_t)fsbuf.f_bsize * fsbuf.f_bfree;
385 } else {
386 free_size = (off64_t)fsbuf.f_bsize * fsbuf.f_bavail;
389 if (free_size >= size) {
390 return RETURN_OK;
393 return RETURN_NG;
396 int file_check(int fd, const struct stat64 *buf, const char *file_name);
397 int force_defrag(int fd, const struct stat64 *buf, int blocksize);
400 * ftw_fn() - Check file attributes and ioctl call to avoid
401 * illegal operations.
403 * @file: the file's name.
404 * @buf: the pointer of the struct stat64.
405 * @flag: file type.
406 * @ftwbuf: the pointer of a struct FTW.
409 ftw_fn(const char *file, const struct stat64 *buf, int flag, struct FTW *ftwbuf)
411 int fd;
412 int blocksize;
413 int percent = 0;
414 int defraged_size = 0;
415 int ret = RETURN_NG;
416 int pos, file_frags_start, file_frags_end;
417 unsigned long page_num;
418 unsigned char *vec = NULL;
419 loff_t start = 0;
420 struct ext4_ext_defrag_data df_data;
421 struct ext4_extents_info extents_info;
423 if (lost_found_dir[0] != '\0' &&
424 !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) {
425 if (detail_flag) {
426 PRINT_ERR_MSG(NGMSG_LOST_FOUND);
427 PRINT_FILE_NAME(file);
429 return FTW_CONT;
432 if (flag == FTW_F) {
433 amount_cnt++;
434 if ((fd = open64(file, O_RDONLY)) < 0) {
435 if (detail_flag) {
436 perror(NGMSG_FILE_OPEN);
437 PRINT_FILE_NAME(file);
439 return FTW_CONT;
442 if (file_check(fd, buf, file) == FILE_CHK_NG) {
443 close(fd);
444 return FTW_CONT;
447 if (fsync(fd) < 0) {
448 if (detail_flag) {
449 perror(NGMSG_FILE_SYNC);
450 PRINT_FILE_NAME(file);
452 close(fd);
453 return FTW_CONT;
455 /* Get blocksize */
456 if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
457 if (detail_flag) {
458 perror(NGMSG_FILE_BLOCKSIZE);
459 PRINT_FILE_NAME(file);
461 close(fd);
462 return FTW_CONT;
464 /* Ioctl call does the actual defragment job. */
465 df_data.start_offset = 0;
466 df_data.goal = goal;
467 df_data.ext.len = 0;
469 if (force_flag)
470 df_data.flag = 1;
472 /* count file frags before defrag if detail_flag set */
473 if (detail_flag) {
474 pos = 0;
475 file_frags_start = 0;
476 memset(&extents_info, 0,
477 sizeof(struct ext4_extents_info));
478 extents_info.ino = buf->st_ino;
479 extents_info.max_entries = DEFRAG_MAX_ENT;
480 extents_info.entries = 0;
482 do {
483 extents_info.entries += pos;
484 pos = extents_info.entries;
485 ret = ioctl(fd, EXT4_IOC_EXTENTS_INFO,
486 &extents_info);
487 if (ret < 0) {
488 perror(NGMSG_FILE_INFO);
489 PRINT_FILE_NAME(file);
490 close(fd);
491 return FTW_CONT;
493 file_frags_start += extents_info.entries;
494 } while (extents_info.entries == DEFRAG_MAX_ENT &&
495 ret == 0);
498 /* print process progress */
499 printf("\tprocessing -------> %s:\n", file);
500 percent = (start * 100) / buf->st_size;
501 printf("\033[79;16H\033[K progressing ====> %d%%", percent);
502 fflush(stdout);
504 while (1) {
505 df_data.defrag_size =
506 (min((buf->st_size - start),
507 DEFRAG_SIZE) + blocksize - 1) /
508 blocksize;
510 ret = page_in_core(fd, df_data, &vec, &page_num);
511 if (ret == RETURN_NG) {
512 if (detail_flag) {
513 perror(NGMSG_FILE_MAP);
514 PRINT_FILE_NAME(file);
515 } else {
516 printf("\n");
518 close(fd);
519 return FTW_CONT;
522 /* EXT4_IOC_DEFRAG */
523 defraged_size = ioctl(fd, EXT4_IOC_DEFRAG, &df_data);
525 /* free page */
526 ret = defrag_fadvise(fd, df_data, vec, page_num);
527 if (vec) {
528 free(vec);
529 vec = NULL;
531 if (ret == RETURN_NG) {
532 if (detail_flag) {
533 perror(NGMSG_FILE_DROP_BUFFER);
534 PRINT_FILE_NAME(file);
535 } else {
536 printf("\n");
538 close(fd);
539 return FTW_CONT;
542 if ((defraged_size < 0) && (force_flag == 1) &&
543 (errno == ENOSPC) && buf->st_size <= MAX_FILE_SIZE) {
544 defraged_size = force_defrag(fd, buf,
545 blocksize);
546 if (defraged_size * blocksize >= buf->st_size) {
547 /* Whole file is enforcedly defraged */
548 break;
549 } else {
550 defraged_size = RETURN_NG;
553 if (defraged_size < 0) {
554 if (detail_flag) {
555 perror(NGMSG_FILE_DEFRAG);
556 PRINT_FILE_NAME(file);
557 } else {
558 printf("\n");
560 close(fd);
561 return FTW_CONT;
563 df_data.start_offset += defraged_size;
564 start = df_data.start_offset * blocksize;
566 /* print process progress */
567 if (start > ((long long)1 << 56)) {
568 /* consider overflow("start * 100" beyond 64bits) */
569 start = start >> 8;
570 percent = (start * 100) / (buf->st_size >> 8);
571 } else {
572 percent = (start * 100) / buf->st_size;
575 /* disk space file used is bigger than logical size */
576 if (percent > 100) {
577 percent = 100;
579 printf("\033[79;16H\033[K progressing ====> %d%%",
580 percent);
581 fflush(stdout);
583 /* End of file */
584 if (start >= buf->st_size) {
585 break;
589 /* count file frags after defrag and print extents info */
590 if (detail_flag) {
591 pos = 0;
592 ret = RETURN_NG;
593 file_frags_end = 0;
594 extents_info.entries = 0;
596 do {
597 extents_info.entries += pos;
598 pos = extents_info.entries;
599 ret = ioctl(fd, EXT4_IOC_EXTENTS_INFO,
600 &extents_info);
601 if (ret < 0) {
602 printf("\n");
603 perror(NGMSG_FILE_INFO);
604 PRINT_FILE_NAME(file);
605 close(fd);
606 return FTW_CONT;
608 file_frags_end += extents_info.entries;
609 } while (extents_info.entries == DEFRAG_MAX_ENT &&
610 ret == 0);
612 printf("\n\t\textents: %d ==> %d",
613 file_frags_start, file_frags_end);
615 printf("\n");
616 close(fd);
617 succeed_cnt++;
618 } else {
619 if (detail_flag) {
620 PRINT_ERR_MSG(NGMSG_FILE_UNREG);
621 PRINT_FILE_NAME(file);
625 return FTW_CONT;
629 * file_check() - Check file's attributes.
631 * @fd: the file's descriptor.
632 * @buf: a pointer of the struct stat64.
633 * @file_name: the file's name.
635 int file_check(int fd, const struct stat64 *buf, const char *file_name)
637 struct flock lock;
639 lock.l_type = F_WRLCK; /* Write-lock check is more reliable. */
640 lock.l_start = 0;
641 lock.l_whence = SEEK_SET;
642 lock.l_len = 0;
644 /* Regular file */
645 if (S_ISREG(buf->st_mode) == 0) {
646 if (detail_flag) {
647 PRINT_ERR_MSG(NGMSG_FILE_UNREG);
648 PRINT_FILE_NAME(file_name);
650 return FILE_CHK_NG;
653 /* Free space */
654 if (check_free_size(fd, buf) == RETURN_NG) {
656 if (detail_flag) {
657 PRINT_ERR_MSG(NGMSG_FILE_LARGE);
658 PRINT_FILE_NAME(file_name);
660 return FILE_CHK_NG;
663 /* Priority */
664 if (getuid() != ROOT_UID &&
665 buf->st_uid != getuid()) {
666 if (detail_flag) {
667 PRINT_ERR_MSG(NGMSG_FILE_PRIORITY);
668 PRINT_FILE_NAME(file_name);
670 return FILE_CHK_NG;
673 /* Lock status */
674 if (fcntl(fd, F_GETLK, &lock) < 0) {
675 if (detail_flag) {
676 perror(NGMSG_GET_LCKINFO);
677 PRINT_FILE_NAME(file_name);
679 return FILE_CHK_NG;
680 } else if (lock.l_type != F_UNLCK) {
681 if (detail_flag) {
682 PRINT_ERR_MSG(NGMSG_FILE_LOCK);
683 PRINT_FILE_NAME(file_name);
685 return FILE_CHK_NG;
688 /* Empty file */
689 if (buf->st_size == 0) {
690 if (detail_flag) {
691 PRINT_ERR_MSG(NGMSG_FILE_BLANK);
692 PRINT_FILE_NAME(file_name);
694 return FILE_CHK_NG;
697 return FILE_CHK_OK;
701 * is_ext4() - Whether on an ext4 filesystem.
703 * @filename: the file's name.
705 int is_ext4(const char *filename)
707 int maxlen, len;
708 FILE *fp = NULL;
709 char *mnt_type = NULL;
710 char *mtab = MOUNTED; /* Refer to /etc/mtab */
711 char file_path[PATH_MAX + 1];
712 struct mntent *mnt = NULL;
713 struct statfs buffs;
715 /* Get full path */
716 if (realpath(filename, file_path) == NULL) {
717 perror(NGMSG_REALPATH);
718 PRINT_FILE_NAME(filename);
719 return RETURN_NG;
722 if (statfs(file_path, &buffs) < 0) {
723 perror(NGMSG_FS_INFO);
724 PRINT_FILE_NAME(filename);
725 return RETURN_NG;
728 if (buffs.f_type != EXT4_SUPER_MAGIC) {
729 PRINT_ERR_MSG(NGMSG_EXT4);
730 return RETURN_NG;
733 if ((fp = setmntent(mtab, "r")) == NULL) {
734 perror(NGMSG_MTAB);
735 return RETURN_NG;
738 maxlen = 0;
739 while ((mnt = getmntent(fp)) != NULL) {
740 len = strlen(mnt->mnt_dir);
741 if (memcmp(file_path, mnt->mnt_dir, len) == 0) {
742 if (maxlen < len) {
743 maxlen = len;
744 mnt_type = realloc(mnt_type,
745 strlen(mnt->mnt_type) + 1);
746 if (!mnt_type) {
747 endmntent(fp);
748 return RETURN_NG;
750 strcpy(mnt_type, mnt->mnt_type);
751 strncpy(lost_found_dir, mnt->mnt_dir, PATH_MAX);
756 if (strcmp(mnt_type, FS_EXT4) == 0) {
757 endmntent(fp);
758 if (mnt_type) {
759 free(mnt_type);
761 return RETURN_OK;
762 } else {
763 endmntent(fp);
764 if (mnt_type) {
765 free(mnt_type);
767 PRINT_ERR_MSG(NGMSG_EXT4);
768 return RETURN_NG;
773 * get_mount_point() - Get device's mount point.
775 * @devname: the device's name.
776 * @mount_point: the mount point.
777 * @dir_path_len: the length of directory.
779 int get_mount_point(const char *devname, char *mount_point, int dir_path_len)
781 char *mtab = MOUNTED; /* Refer to /etc/mtab */
782 FILE *fp = NULL;
783 struct mntent *mnt = NULL;
785 if ((fp = setmntent(mtab, "r")) == NULL) {
786 perror(NGMSG_MTAB);
787 return RETURN_NG;
790 while ((mnt = getmntent(fp)) != NULL) {
791 if (strcmp(devname, mnt->mnt_fsname) == 0) {
792 endmntent(fp);
793 if (strcmp(mnt->mnt_type, FS_EXT4) == 0) {
794 strncpy(mount_point, mnt->mnt_dir,
795 dir_path_len);
796 return RETURN_OK;
798 PRINT_ERR_MSG(NGMSG_EXT4);
799 return RETURN_NG;
802 endmntent(fp);
803 PRINT_ERR_MSG(NGMSG_UNMOUNT);
804 return RETURN_NG;
808 * main() - ext4 online defrag.
810 * @argc: the number of parameter.
811 * @argv[]: the pointer array of parameter.
813 int main(int argc, char *argv[])
815 int fd;
816 int ret;
817 int opt;
818 int i, flags;
819 int arg_type;
820 int detail_tmp;
821 int success_flag;
822 char dir_name[PATH_MAX + 1];
823 struct stat64 buf;
825 i = 1;
826 flags = 0;
827 arg_type = -1;
828 detail_tmp = -1;
829 success_flag = 0;
830 flags |= FTW_PHYS; /* Do not follow symlink */
831 flags |= FTW_MOUNT; /* Stay within the same filesystem */
832 /* Parse arguments */
833 if (argc == 1 || (argc == 2 && argv[1][0] == '-')) {
834 printf(MSG_USAGE);
835 exit(1);
838 while ((opt = getopt(argc, argv, "rvf")) != EOF) {
839 switch (opt) {
840 case 'r':
841 regional_flag = 1;
842 i = 2;
843 break;
844 case 'v':
845 detail_flag = 1;
846 i = 2;
847 break;
848 case 'f':
849 force_flag = 1;
850 i = 2;
852 if (argc > 4) {
853 printf("Illegal argument\n\n");
854 printf(MSG_USAGE);
855 exit(1);
858 if (argc == 4) {
859 int res_strlen;
860 res_strlen = strlen(argv[3]);
861 for (res_strlen -= 1; res_strlen >= 0;
862 res_strlen--) {
863 if (!isdigit(argv[3][res_strlen])) {
864 printf("Illegal argument\n\n");
865 printf(MSG_USAGE);
866 exit(1);
870 fgoal = strtoul(argv[3], NULL, 0);
871 if (errno) {
872 printf("block num shold be < 32bit\n");
873 exit(1);
876 if (!fgoal)
877 fgoal = -1;
878 break;
879 default:
880 printf(MSG_USAGE);
881 exit(1);
885 /* Main process */
886 for (; i < argc; i++) {
887 amount_cnt = 0;
888 succeed_cnt = 0;
889 memset(dir_name, 0, PATH_MAX + 1);
890 memset(lost_found_dir, 0, PATH_MAX + 1);
892 if (force_flag && i == 3)
893 break;
895 if (lstat64(argv[i], &buf) < 0) {
896 perror(NGMSG_FILE_INFO);
897 PRINT_FILE_NAME(argv[i]);
898 continue;
901 /* Regular file is acceptalbe with force mode */
902 if (force_flag && !S_ISREG(buf.st_mode)) {
903 printf("Inappropriate file type \n\n");
904 printf(MSG_USAGE);
905 exit(1);
908 /* Block device */
909 if (S_ISBLK(buf.st_mode)) {
910 arg_type = DEVNAME;
911 if (get_mount_point(argv[i], dir_name, PATH_MAX) ==
912 RETURN_NG) {
913 continue;
915 printf("Start defragment for device(%s)\n", argv[i]);
916 } else if (S_ISDIR(buf.st_mode)) {
917 /* Directory */
918 arg_type = DIRNAME;
919 if (access(argv[i], R_OK) < 0) {
920 perror(argv[i]);
921 continue;
923 strcpy(dir_name, argv[i]);
924 } else if (S_ISREG(buf.st_mode)) {
925 /* Regular file */
926 arg_type = FILENAME;
927 } else {
928 /* Irregular file */
929 PRINT_ERR_MSG(NGMSG_FILE_UNREG);
930 PRINT_FILE_NAME(argv[i]);
931 continue;
934 /* Device's ext4 check is in get_mount_point() */
935 if (arg_type == FILENAME || arg_type == DIRNAME) {
936 if (is_ext4(argv[i]) == RETURN_NG) {
937 continue;
939 if (realpath(argv[i], dir_name) == NULL) {
940 perror(NGMSG_REALPATH);
941 PRINT_FILE_NAME(argv[i]);
942 continue;
946 switch (arg_type) {
947 case DIRNAME:
948 printf("Start defragment for directory(%s)\n",
949 argv[i]);
951 int mount_dir_len = 0;
952 mount_dir_len = strnlen(lost_found_dir, PATH_MAX);
954 strncat(lost_found_dir, "/lost+found",
955 PATH_MAX - strnlen(lost_found_dir, PATH_MAX));
957 /* not the case("e4defrag mount_piont_dir") */
958 if (dir_name[mount_dir_len] != '\0') {
959 /* "e4defrag mount_piont_dir/lost+found" */
960 /* or "e4defrag mount_piont_dir/lost+found/" */
961 if (strncmp(lost_found_dir, dir_name,
962 strnlen(lost_found_dir,
963 PATH_MAX)) == 0 &&
964 (dir_name[strnlen(lost_found_dir,
965 PATH_MAX)] == '\0' ||
966 dir_name[strnlen(lost_found_dir,
967 PATH_MAX)] == '/')) {
968 PRINT_ERR_MSG(NGMSG_LOST_FOUND);
969 PRINT_FILE_NAME(argv[i]);
970 continue;
973 /* "e4defrag mount_piont_dir/else_dir" */
974 memset(lost_found_dir, 0, PATH_MAX + 1);
976 case DEVNAME:
977 if (arg_type == DEVNAME) {
978 strncpy(lost_found_dir, dir_name,
979 strnlen(dir_name, PATH_MAX));
980 strncat(lost_found_dir, "/lost+found/",
981 PATH_MAX - strnlen(lost_found_dir,
982 PATH_MAX));
985 /* Regional block allocation */
986 if (regional_flag) {
987 printf(MSG_R_OPTION);
989 if ((fd = open64(dir_name, O_RDONLY)) < 0) {
990 if (detail_flag) {
991 perror(NGMSG_FILE_OPEN);
992 PRINT_FILE_NAME(dir_name);
994 continue;
997 goal = 0;
998 if ((ret = ioctl(fd, EXT4_IOC_FIBMAP,
999 &goal)) != 0) {
1000 perror(NGMSG_FILE_FIBMAP);
1001 PRINT_FILE_NAME(dir_name);
1002 close(fd);
1003 continue;
1005 close(fd);
1008 /* File tree walk */
1009 nftw64(dir_name, ftw_fn, FTW_OPEN_FD, flags);
1010 printf("\tTotal:\t\t%12d\n", amount_cnt);
1011 printf("\tSuccess:\t%12d\n", succeed_cnt);
1012 printf("\tFailure:\t%12d\n",
1013 amount_cnt - succeed_cnt);
1014 break;
1015 case FILENAME:
1016 strncat(lost_found_dir, "/lost+found/",
1017 PATH_MAX - strnlen(lost_found_dir,
1018 PATH_MAX));
1019 if (strncmp(lost_found_dir, dir_name,
1020 strnlen(lost_found_dir,
1021 PATH_MAX)) == 0) {
1022 PRINT_ERR_MSG(NGMSG_LOST_FOUND);
1023 PRINT_FILE_NAME(argv[i]);
1024 continue;
1027 if (regional_flag) {
1028 fprintf(stderr, NGMSG_TYPE, argv[i]);
1029 continue;
1031 detail_tmp = detail_flag;
1032 detail_flag = 1;
1033 printf("Start defragment for %s\n", argv[i]);
1034 /* Single file process */
1035 ftw_fn(argv[i], &buf, FTW_F, NULL);
1036 if (succeed_cnt != 0) {
1037 printf(
1038 "\tSUCCESS\t:file defrag success.\n"
1041 detail_flag = detail_tmp;
1042 break;
1045 if (succeed_cnt != 0)
1046 success_flag = 1;
1049 if (success_flag)
1050 return RETURN_OK;
1052 exit(1);
1055 * insert_extent() - Sequentially insert extent by physical block number.
1057 * @extlist_head: the head of an extent list.
1058 * @ext: the extent element which will be inserted.
1060 int insert_extent(extent_t **extlist_head, extent_t *ext)
1062 extent_t *ext_tmp = *extlist_head;
1064 if (ext == NULL) {
1065 return RETURN_NG;
1067 /* First element */
1068 if (*extlist_head == NULL) {
1069 (*extlist_head) = ext;
1070 (*extlist_head)->prev = *extlist_head;
1071 (*extlist_head)->next = *extlist_head;
1072 return RETURN_OK;
1075 if (ext->data.start <= ext_tmp->data.start) {
1076 /* Insert before head */
1077 if (ext_tmp->data.start < ext->data.start + ext->data.len) {
1078 /* Overlap */
1079 return RETURN_NG;
1081 /* Adjust head */
1082 *extlist_head = ext;
1083 } else {
1084 /* Insert into the middle or last of the list */
1085 do {
1086 if (ext->data.start < ext_tmp->data.start) {
1087 break;
1089 ext_tmp = ext_tmp->next;
1090 } while (ext_tmp != (*extlist_head));
1091 if (ext->data.start <
1092 ext_tmp->prev->data.start + ext_tmp->prev->data.len) {
1093 /* Overlap */
1094 return RETURN_NG;
1096 if (ext_tmp != *extlist_head &&
1097 ext_tmp->data.start < ext->data.start + ext->data.len) {
1098 /* Overlap */
1099 return RETURN_NG;
1102 ext_tmp = ext_tmp->prev;
1103 /* Insert "ext" after "ext_tmp" */
1104 insert(ext_tmp, ext);
1105 return RETURN_OK;
1109 * insert_exts_group() - Insert a exts_group in decreasing order of length.
1111 * @exts_group_list_head: the head of a exts_group list.
1112 * @exts_group: the exts_group element which will be inserted.
1114 int insert_exts_group(exts_group_t **exts_group_list_head,
1115 exts_group_t *exts_group)
1117 exts_group_t *exts_group_tmp = NULL;
1119 if (exts_group == NULL) {
1120 return RETURN_NG;
1123 /* Initialize list */
1124 if (*exts_group_list_head == NULL) {
1125 (*exts_group_list_head) = exts_group;
1126 (*exts_group_list_head)->prev = *exts_group_list_head;
1127 (*exts_group_list_head)->next = *exts_group_list_head;
1128 return RETURN_OK;
1131 if (exts_group->len >= (*exts_group_list_head)->len) {
1132 /* Insert before exts_group_list_head */
1133 exts_group_tmp = (*exts_group_list_head)->prev;
1134 insert(exts_group_tmp, exts_group);
1135 *exts_group_list_head = exts_group;
1136 return RETURN_OK;
1139 /* Find insertion positon */
1140 for (exts_group_tmp = (*exts_group_list_head)->next;
1141 exts_group_tmp != *exts_group_list_head;
1142 exts_group_tmp = exts_group_tmp->next) {
1143 if (exts_group_tmp->len < exts_group->len) {
1144 break;
1147 exts_group_tmp = exts_group_tmp->prev;
1148 insert(exts_group_tmp, exts_group);
1150 return RETURN_OK;
1154 * get_exts_group() - Get element from the exts_group list.
1156 * @exts_group_list_head: the head of a exts_group list.
1157 * @exts_group: the exts_group element which will be geted.
1159 exts_group_t *get_exts_group(exts_group_t **exts_group_list_head,
1160 exts_group_t *exts_group)
1162 if (exts_group == NULL || *exts_group_list_head == NULL) {
1163 return NULL;
1165 /* Keep "exts_group_list_head" point to the largest extent group*/
1166 if (exts_group == *exts_group_list_head) {
1167 *exts_group_list_head = exts_group->next;
1169 if (*exts_group_list_head == (*exts_group_list_head)->next &&
1170 exts_group == *exts_group_list_head) {
1171 /* Delete the last element in the list */
1172 *exts_group_list_head = NULL;
1174 exts_group->prev->next = exts_group->next;
1175 exts_group->next->prev = exts_group->prev;
1176 return exts_group;
1180 * free_exts_group() - Free the exts_group.
1182 * @*exts_group_list_head: the exts_group list head which will be free.
1185 void free_exts_group(exts_group_t *exts_group_list_head)
1187 exts_group_t *exts_group_tmp = NULL;
1189 if (exts_group_list_head == NULL) {
1190 return;
1192 while (exts_group_list_head->next != exts_group_list_head) {
1193 exts_group_tmp = exts_group_list_head;
1194 exts_group_list_head->prev->next = exts_group_list_head->next;
1195 exts_group_list_head->next->prev = exts_group_list_head->prev;
1196 exts_group_list_head = exts_group_list_head->next;
1197 free(exts_group_tmp);
1199 free(exts_group_list_head);
1203 * free_ext() - Free the extent list.
1205 * @extent_list_head: the extent list head of which will be free.
1207 void free_ext(extent_t *extent_list_head)
1209 extent_t *extent_tmp = NULL;
1211 if (extent_list_head == NULL) {
1212 return;
1214 while (extent_list_head->next != extent_list_head) {
1215 extent_tmp = extent_list_head;
1216 extent_list_head->prev->next = extent_list_head->next;
1217 extent_list_head->next->prev = extent_list_head->prev;
1218 extent_list_head = extent_list_head->next;
1219 free(extent_tmp);
1221 free(extent_list_head);
1225 * move_wrap() - Move a ext_wrap from one list to another.
1227 * @from: the list which will be moved from.
1228 * @to: the list which will be moved to.
1229 * @entry: the ext_wrap which will be moved.
1231 int move_wrap(extent_wrap_t **from, extent_wrap_t **to,
1232 extent_wrap_t *entry)
1234 if (!to || !entry) {
1235 return RETURN_NG;
1237 if (from && *from == entry) {
1238 if ((*from)->next == *from) {
1239 *from = NULL;
1240 } else {
1241 *from = (*from)->next;
1244 entry->next->prev = entry->prev;
1245 entry->prev->next = entry->next;
1246 if (!(*to)) {
1247 *to = entry;
1248 (*to)->prev = (*to)->next = *to;
1249 } else {
1250 entry->next = *to;
1251 entry->prev = (*to)->prev;
1252 (*to)->prev->next = entry;
1253 (*to)->prev = entry;
1255 return RETURN_OK;
1259 * mark_wrap() - Mark extent status as "EXT4_EXT_RESERVE".
1261 * @ext_wrap_list: the ext_wrap list which will be marked.
1263 void mark_wrap(extent_wrap_t *ext_wrap_list)
1265 extent_wrap_t *wrap = ext_wrap_list;
1267 if (!ext_wrap_list) {
1268 return;
1270 do {
1271 wrap->group_ext->tag |= EXT4_EXT_RESERVE;
1272 wrap = wrap->next;
1273 } while (wrap != ext_wrap_list);
1277 * free_wrap_list() - Free the ext_wrap list.
1279 * @ext_wrap_head: the ext_wrap list head which will be free.
1281 void free_wrap_list(extent_wrap_t **ext_wrap_head)
1283 extent_wrap_t *wrap, *ext_wrap_tmp;
1285 if (!ext_wrap_head || !(*ext_wrap_head)) {
1286 return;
1288 wrap = *ext_wrap_head;
1289 do {
1290 ext_wrap_tmp = wrap;
1291 wrap = wrap->next;
1292 free(ext_wrap_tmp);
1293 } while (wrap != *ext_wrap_head);
1294 *ext_wrap_head = NULL;
1298 * do_defrag() - Execute the defrag program.
1300 * @fd: the file's descriptor.
1301 * @exts_group: the exts_group which will be defraged.
1302 * @defrag_data: the data which will be defraged.
1304 static inline int do_defrag(int fd, exts_group_t *exts_group,
1305 struct ext4_ext_defrag_data defrag_data)
1307 int ret = 0;
1308 int defraged_size = 0;
1309 int fadvise_ret = 0;
1310 unsigned long page_num;
1311 unsigned char *vec = NULL;
1312 extent_t *extent = NULL;
1314 /* Defrag */
1315 defrag_data.ext.start = exts_group->start->data.start;
1316 defrag_data.ext.len = exts_group->len;
1317 defrag_data.ext.block = 0;
1318 defrag_data.defrag_size = exts_group->len;
1319 defrag_data.flag = DEFRAG_FORCE_VICTIM;
1320 defrag_data.goal = exts_group->start->data.start;
1322 if (page_in_core(fd, defrag_data, &vec, &page_num) == RETURN_NG) {
1323 return RETURN_NG;
1326 defraged_size = ioctl(fd, EXT4_IOC_DEFRAG, &defrag_data);
1328 /* free pages */
1329 fadvise_ret = defrag_fadvise(fd, defrag_data, vec, page_num);
1330 if (vec) {
1331 free(vec);
1333 if (fadvise_ret == RETURN_NG || defraged_size < 0) {
1334 return RETURN_NG;
1337 /* Release reserved sign */
1338 extent = exts_group->start;
1339 do {
1340 extent->tag &= ~EXT4_EXT_RESERVE;
1341 extent = extent->next;
1342 } while (extent != exts_group->end->next);
1344 ret += defraged_size;
1346 return ret;
1350 * get_used_extent() - Get used extent in the block group.
1352 * @fd: the file's descriptor.
1353 * @ext_list_head: the head of the extent list.
1354 * @istart: the start of the inode.
1355 * @iend: the end of the inode.
1356 * @bstart: the start of the block.
1357 * @bend: the end of the block.
1359 int get_used_extent(int fd, extent_t **ext_list_head,
1360 unsigned long long istart, unsigned long long iend,
1361 ext4_fsblk_t bstart, ext4_fsblk_t bend)
1363 struct ext4_extents_info extents_info;
1364 unsigned long long inode;
1365 int pos = 0;
1366 int ret = 0;
1368 memset(&extents_info, 0, sizeof(struct ext4_extents_info));
1369 extents_info.max_entries = DEFRAG_MAX_ENT;
1371 for (inode = istart; inode <= iend; inode++) {
1372 extents_info.ino = inode;
1373 extents_info.entries = 0;
1374 pos = 0;
1375 do {
1376 /* Get extents info */
1377 int i;
1378 extents_info.entries += pos;/* Offset */
1379 pos = extents_info.entries;
1380 memset(extents_info.ext, 0,
1381 sizeof(struct ext4_extent_data) *
1382 DEFRAG_MAX_ENT);
1383 ret = ioctl(fd, EXT4_IOC_EXTENTS_INFO, &extents_info);
1384 if (ret < 0) {
1385 if (errno == ENOENT) {
1386 continue;
1387 } else {
1388 /* Without ENOENT case*/
1389 return RETURN_NG;
1393 for (i = 0; i < extents_info.entries; i++) {
1394 extent_t *extent = NULL;
1395 /* Is this extent in current block group? */
1396 if (extents_info.ext[i].start < bstart ||
1397 extents_info.ext[i].start > bend) {
1398 continue;
1400 extent = malloc(sizeof(extent_t));
1401 if (extent == NULL) {
1402 return RETURN_NG;
1404 memset(extent, 0, sizeof(extent_t));
1405 memcpy(&(extent->data), &extents_info.ext[i],
1406 sizeof(struct ext4_extent_data));
1407 extent->ino = inode;
1408 if (insert_extent(ext_list_head, extent) < 0) {
1409 if (extent) {
1410 free(extent);
1412 return RETURN_NG;
1415 } while (extents_info.entries == DEFRAG_MAX_ENT && ret == 0);
1418 if (ret < 0) {
1419 if (errno == ENOENT) {
1420 return RETURN_OK;
1423 return ret;
1427 * get_free_extent() - Get used extent in the block group.
1429 * @fd: the file's descriptor.
1430 * @inode: inode number from struct stat64.
1431 * @blocks_per_group: the block number of each block group.
1432 * @ext_list_head: the head of the extent list.
1434 int get_free_extent(int fd, unsigned long long inode,
1435 int blocks_per_group, extent_t **ext_list_head)
1437 ext4_grpblk_t pos = 0;
1438 struct ext4_extents_info extents_info;
1440 memset(&extents_info, 0, sizeof(struct ext4_extents_info));
1441 extents_info.ino = inode;
1442 extents_info.max_entries = DEFRAG_MAX_ENT;
1443 while (pos < blocks_per_group) {
1444 int i;
1445 if (ioctl(fd, EXT4_IOC_FREE_BLOCKS_INFO, &extents_info) < 0) {
1446 return RETURN_NG;
1449 * No free extent after the logical block number "pos".
1450 * In other word, offset this time equals to prev recursion.
1452 for (i = 0;
1453 extents_info.ext[i].len != 0 && i < DEFRAG_MAX_ENT; i++) {
1454 /* Alloc list node store extent */
1455 extent_t *extent = NULL;
1456 extent = malloc(sizeof(extent_t));
1457 if (extent == NULL) {
1458 return RETURN_NG;
1460 memset(extent, 0, sizeof(extent_t));
1461 memcpy(&(extent->data), &(extents_info.ext[i]),
1462 sizeof(struct ext4_extent_data));
1463 extent->tag = EXT4_EXT_FREE;/* Free extent */
1464 if (insert_extent(ext_list_head, extent) < 0) {
1465 if (extent) {
1466 free(extent);
1468 return RETURN_NG;
1472 * No free extent after the logical block number "pos".
1473 * In other word, offset this time equals to prev recursion.
1475 if (pos == extents_info.g_offset) {
1476 break;
1478 if (i < DEFRAG_MAX_ENT) {
1479 break;
1481 /* Record the offset of logical block number this time */
1482 pos = extents_info.g_offset;
1483 memset(extents_info.ext, 0,
1484 sizeof(struct ext4_extent_data) * DEFRAG_MAX_ENT);
1487 return RETURN_OK;
1491 * join_extents() - Find continuous region(exts_group).
1493 * @ext_list_head: the head of the extent list.
1494 * @target_exts_group_list_head:the head of the target exts_group list.
1495 * @exts_group_list_head: the head of the original exts_group list.
1496 * @filesize: the file's descriptor.
1497 * @max: the max size of free space.
1499 int join_extents(extent_t *ext_list_head,
1500 exts_group_t **target_exts_group_list_head,
1501 exts_group_t **exts_group_list_head,
1502 unsigned long filesize, int *max)
1504 int len;
1505 extent_t *ext_start, *extent_tmp;
1507 ext_start = extent_tmp = ext_list_head;
1508 *max = 0;
1509 len = ext_list_head->data.len;
1510 extent_tmp = extent_tmp->next;
1511 do {
1512 if (len >= filesize) {
1514 * Hit on the way,
1515 * one extent group is enough for defrag, so return.
1517 exts_group_t *exts_group_tmp = NULL;
1518 exts_group_tmp = malloc(sizeof(exts_group_t));
1519 if (!exts_group_tmp) {
1520 return RETURN_NG;
1522 exts_group_tmp->prev = exts_group_tmp->next = NULL;
1523 exts_group_tmp->start = ext_start;
1524 exts_group_tmp->end = extent_tmp->prev;
1525 exts_group_tmp->len = len;
1526 if (insert_exts_group(target_exts_group_list_head,
1527 exts_group_tmp) < 0) {
1528 if (exts_group_tmp) {
1529 free(exts_group_tmp);
1531 return RETURN_NG;
1533 return CHECK_FRAG_COUNT;
1536 * This extent and previous extent is not continuous,
1537 * so, all previous extents is treated as an extent group.
1539 if ((extent_tmp->prev->data.start + extent_tmp->prev->data.len)
1540 != extent_tmp->data.start) {
1541 exts_group_t *exts_group_tmp = NULL;
1542 exts_group_tmp = malloc(sizeof(exts_group_t));
1543 if (exts_group_tmp == NULL) {
1544 return RETURN_NG;
1546 memset(exts_group_tmp, 0, sizeof(exts_group_t));
1547 exts_group_tmp->len = len;
1548 exts_group_tmp->start = ext_start;
1549 exts_group_tmp->end = extent_tmp->prev;
1551 if (insert_exts_group(exts_group_list_head,
1552 exts_group_tmp) < 0) {
1553 if (exts_group_tmp) {
1554 free(exts_group_tmp);
1556 return RETURN_NG;
1558 *max += len;
1559 ext_start = extent_tmp;
1560 len = extent_tmp->data.len;
1561 extent_tmp = extent_tmp->next;
1562 continue;
1565 * This extent and previous extent is continuous,
1566 * so, they belong to the same extent group, and we check
1567 * if the next extent belong to the same extent group.
1569 len += extent_tmp->data.len;
1570 extent_tmp = extent_tmp->next;
1571 } while (extent_tmp != ext_list_head->next);
1573 return RETURN_OK;
1577 *find_exts_group() - Find target exts_group.
1579 * @ext_count: the number of extents.
1580 * @filesize: the file's size.
1581 * @exts_group_list_head: the head of the original exts_group list
1582 * @target_exts_group_list_head: the head of the target exts_group list.
1584 int find_exts_group(int *ext_count, unsigned long filesize,
1585 exts_group_t **exts_group_list_head,
1586 exts_group_t **target_exts_group_list_head)
1588 int len;
1590 len = 0;/* Blocks we found for target file */
1592 if (!(*exts_group_list_head)) {
1593 return RETURN_NG;
1596 while (*exts_group_list_head) {
1597 exts_group_t *exts_group_tmp;
1598 if ((*exts_group_list_head)->len + len >= filesize) {
1600 * Search from the smallest extent group
1601 * to avoid waste of space
1603 exts_group_tmp = (*exts_group_list_head)->prev;
1604 do {
1605 if (exts_group_tmp->len + len >= filesize) {
1606 len += exts_group_tmp->len;
1607 exts_group_tmp =
1608 get_exts_group(exts_group_list_head,
1609 exts_group_tmp);
1610 if (insert_exts_group
1611 (target_exts_group_list_head,
1612 exts_group_tmp) < 0) {
1613 if (exts_group_tmp) {
1614 free(exts_group_tmp);
1616 return RETURN_NG;
1618 (*ext_count)++;
1619 /* The only entry go out normally*/
1620 return RETURN_OK;
1622 exts_group_tmp = exts_group_tmp->prev;
1623 } while (exts_group_tmp !=
1624 (*exts_group_list_head)->prev);
1626 len += (*exts_group_list_head)->len;
1627 exts_group_tmp = get_exts_group(exts_group_list_head,
1628 *exts_group_list_head);
1629 if (insert_exts_group(target_exts_group_list_head,
1630 exts_group_tmp) < 0) {
1631 if (exts_group_tmp) {
1632 free(exts_group_tmp);
1634 return RETURN_NG;
1636 (*ext_count)++;
1639 return RETURN_NG;
1643 * check_frag_count() - Check file frag.
1645 * @fd: the file's discriptor.
1646 * @inode: inode number from struct stat64.
1647 * @extent_count: the number of extents.
1649 int check_frag_count(int fd, unsigned long long inode, int extent_count)
1651 int ret, pos, file_extent_count;
1652 struct ext4_extents_info extents_info;
1654 /* Count file exts */
1655 memset(&extents_info, 0, sizeof(struct ext4_extents_info));
1656 file_extent_count = 0;/* Extents count of file */
1657 extents_info.ino = inode;
1658 extents_info.max_entries = DEFRAG_MAX_ENT;
1659 extents_info.entries = 0;
1660 pos = 0;
1661 ret = 0;
1663 do {
1664 extents_info.entries += pos;
1665 pos = extents_info.entries;
1666 ret = ioctl(fd, EXT4_IOC_EXTENTS_INFO, &extents_info);
1667 if (ret < 0) {
1668 return RETURN_NG;
1670 file_extent_count += extents_info.entries;
1671 } while (extents_info.entries == DEFRAG_MAX_ENT && ret == 0);
1673 if (extent_count >= file_extent_count) {
1674 /* No improvment */
1675 errno = ENOSPC;
1676 return RETURN_NG;
1679 return RETURN_OK;
1683 * defrag_proc() - Reserve extent group and execute the defrag program
1685 * @fd: the file's discriptor.
1686 * @target_exts_group_head: the head of the original exts_group list.
1687 * @inode: inode number from struct stat64.
1689 int defrag_proc(int fd, exts_group_t *target_exts_group_head,
1690 unsigned long long inode)
1692 int ret = 0;
1693 int percent = 0;
1694 int blocksize = 0;
1695 int data_len = 0;
1696 struct stat64 buf;
1697 exts_group_t *exts_group;
1698 extent_t *extent;
1699 struct ext4_extents_info extents_info;
1700 struct ext4_ext_defrag_data defrag_data;
1701 extent_wrap_t *wrap_list = NULL;
1703 /* Reserve free extents */
1704 if (!target_exts_group_head) {
1705 /* Fault */
1706 return RETURN_NG;
1709 /* get file size */
1710 memset(&buf, 0, sizeof(struct stat64));
1711 ret = fstat64(fd, &buf);
1712 if (ret < 0) {
1713 perror(NGMSG_FILE_INFO);
1714 return RETURN_NG;
1716 /* get block size */
1717 ret = ioctl(fd, FIGETBSZ, &blocksize);
1718 if (ret < 0) {
1719 perror(NGMSG_FILE_BLOCKSIZE);
1720 return RETURN_NG;
1722 memset(&extents_info, 0, sizeof(extents_info));
1723 memset(&defrag_data, 0, sizeof(struct ext4_ext_defrag_data));
1725 extents_info.ino = 0;
1726 exts_group = target_exts_group_head;
1727 extents_info.max_entries = DEFRAG_MAX_ENT;
1728 extents_info.ino = inode;
1729 ext4_fsblk_t data_block = 0;
1730 ext4_fsblk_t data_start = 0;
1731 defrag_data.start_offset = 0;
1733 do {
1734 extent_wrap_t *wrap;
1735 extent = exts_group->start;
1736 data_len = 0;
1737 data_start = extent->data.start;
1738 data_block = extent->data.block;
1739 do {
1740 data_len += extent->data.len;
1741 if (extent->tag != EXT4_EXT_USE) {
1742 extent->tag = EXT4_EXT_RESERVE;
1743 extent = extent->next;
1744 continue;
1746 extents_info.ino = extent->ino;
1747 extents_info.goal = fgoal;
1748 memcpy(extents_info.ext, &extent->data,
1749 sizeof(struct ext4_extent_data));
1750 wrap = malloc(sizeof(extent_wrap_t));
1751 if (!wrap) {
1752 goto release_blocks;
1754 wrap->group_ext = extent;
1755 wrap->next = wrap->prev = wrap;
1756 if (move_wrap(NULL, &wrap_list, wrap) < 0) {
1757 if (wrap) {
1758 free(wrap);
1760 goto release_blocks;
1762 extent = extent->next;
1763 extents_info.entries = 1;
1764 ret = ioctl(fd, EXT4_IOC_MOVE_VICTIM, &extents_info);
1765 if (ret < 0) {
1766 goto release_blocks;
1768 mark_wrap(wrap_list);
1769 free_wrap_list(&wrap_list);
1770 } while (extent != exts_group->end->next);
1772 if (fsync(fd) < 0) {
1773 if (detail_flag) {
1774 perror(NGMSG_FILE_SYNC);
1776 return ret;
1779 extents_info.entries = 1;
1780 extents_info.ext[0].block = data_block;
1781 extents_info.ext[0].start = data_start;
1782 extents_info.ext[0].len = exts_group->len;
1783 ret = ioctl(fd, EXT4_IOC_RESERVE_BLOCK, &extents_info);
1784 if (ret < 0) {
1785 printf("RESERVE_ERROR ret = %d\n", ret);
1786 printf("block is already used\n");
1787 goto release_blocks;
1789 ret = do_defrag(fd, exts_group, defrag_data);
1790 if (ret < 0) {
1791 printf("DEFRAG_ERROR ret = %d\n", ret);
1792 goto release_blocks;
1794 defrag_data.start_offset += ret;
1795 ret = defrag_data.start_offset;
1797 /* print process progress */
1798 if (detail_flag) {
1799 percent = ((long long)ret * blocksize * 100) /
1800 buf.st_size;
1801 if (percent > 100) {
1802 percent = 100;
1804 printf("\033[79;16H\033[K progressing ====> %d%%",
1805 percent);
1806 fflush(stdout);
1809 exts_group = exts_group->next;
1810 } while (exts_group != target_exts_group_head);
1811 return ret;
1813 release_blocks:
1814 free_wrap_list(&wrap_list);
1815 ret = ioctl(fd, EXT4_IOC_BLOCK_RELEASE);
1816 if (ret < 0) {
1817 return RETURN_NG;
1820 return ret;
1824 * force_defrag() - Execute the defrag program in force mode.
1826 * @fd: the file's descriptor.
1827 * @buf: a pointer of the struct stat64.
1828 * @blocksize: block size in byte.
1830 int force_defrag(int fd, const struct stat64 *buf, int blocksize)
1832 int ret = 0;
1833 int exts = 0;
1834 int maxlen = 0;
1835 unsigned int gnumber;
1836 unsigned long filesize;
1837 unsigned long long istart, iend;
1838 ext4_fsblk_t bstart, bend;
1839 extent_t *extlist_head = NULL;
1840 exts_group_t *exts_group_list_head, *exts_group_list_target_head;
1841 struct ext4_group_data_info ext4_group_data;
1843 exts_group_list_head = exts_group_list_target_head = NULL;
1845 /* Get group info */
1846 memset(&ext4_group_data, 0, sizeof(struct ext4_group_data_info));
1847 if (ioctl(fd, EXT4_IOC_GROUP_INFO, &ext4_group_data) < 0) {
1848 return RETURN_NG;
1851 gnumber = (buf->st_ino - 1) / ext4_group_data.s_inodes_per_group;
1852 istart = gnumber * ext4_group_data.s_inodes_per_group;
1853 iend = istart + ext4_group_data.s_inodes_per_group - 1;
1854 bstart = gnumber * ext4_group_data.s_blocks_per_group;
1855 bend = bstart + ext4_group_data.s_blocks_per_group - 1;
1857 /* Compute filesize in block */
1858 filesize = (buf->st_size + blocksize - 1) / blocksize;
1860 /* Get used extents in the block group */
1861 ret = get_used_extent(fd, &extlist_head, istart, iend, bstart, bend);
1862 if (ret == RETURN_NG) {
1863 goto freelist;
1866 /* Get free extents in the group */
1867 ret = get_free_extent(fd, buf->st_ino,
1868 ext4_group_data.s_blocks_per_group, &extlist_head);
1869 if (ret == RETURN_NG) {
1870 goto freelist;
1873 /* All space in this group is used by other groups' inodes */
1874 if (extlist_head == NULL) {
1875 ret = RETURN_NG;
1876 goto freelist;
1879 /* Get continuous region(extents group) */
1880 ret = join_extents(extlist_head, &exts_group_list_target_head,
1881 &exts_group_list_head, filesize, &maxlen);
1882 if (ret == RETURN_NG) {
1883 goto freelist;
1885 if (ret == CHECK_FRAG_COUNT) {
1886 exts = 1;
1887 goto frag_check;
1890 if (maxlen < filesize) {
1891 /* No enough space */
1892 errno = ENOSPC;
1893 ret = RETURN_NG;
1894 goto freelist;
1897 if (!exts_group_list_head) {
1898 ret = RETURN_NG;
1899 goto freelist;
1902 /* Find target extents group */
1903 ret = find_exts_group(&exts, filesize, &exts_group_list_head,
1904 &exts_group_list_target_head);
1905 if (ret == RETURN_NG) {
1906 goto freelist;
1909 frag_check:
1910 /* Check file extent count*/
1911 ret = check_frag_count(fd, buf->st_ino, exts);
1912 if (ret == RETURN_NG) {
1913 goto freelist;
1916 /* Reserve extent group and execute the defrag program */
1917 ret = defrag_proc(fd, exts_group_list_target_head, buf->st_ino);
1919 freelist:
1920 free_exts_group(exts_group_list_target_head);
1921 free_exts_group(exts_group_list_head);
1922 free_ext(extlist_head);
1923 return ret;