Add ext4-printk-throttling patch
[ext4-patch-queue.git] / ext4-online-defrag-command.patch
blobeb4c5c8425921abe91367bc57cd226b6e2bfc9a7
1 ext4: online defrag-- Online defrag command
3 From: Akira Fujita <a-fujita@rs.jp.nec.com>
5 - The defrag command. Usage is as follows:
6 - Put the multiple files closer together.
7 # e4defrag -r directory-name
8 - Defrag for free space fragmentation.
9 # e4defrag -f file-name
10 - Defrag for a single file.
11 # e4defrag file-name
12 - Defrag for all files on ext4.
13 # e4defrag device-name
15 Signed-off-by: Akira Fujita <a-fujita@rs.jp.nec.com>
16 Signed-off-by: Takashi Sato <t-sato@yk.jp.nec.com>
17 ---
19 * e4defrag.c - ext4 filesystem defragmenter
21 * Copyright (C) 2008 NEC Software Tohoku, Ltd.
23 * Author: Akira Fujita <a-fujita@rs.jp.nec.com>
24 * Takashi Sato <t-sato@yk.jp.nec.com>
27 #ifndef _LARGEFILE_SOURCE
28 #define _LARGEFILE_SOURCE
29 #endif
31 #ifndef _LARGEFILE64_SOURCE
32 #define _LARGEFILE64_SOURCE
33 #endif
35 #define _XOPEN_SOURCE 500
36 #define _GNU_SOURCE
37 #include <ftw.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include <dirent.h>
43 #include <limits.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <sys/statfs.h>
48 #include <sys/vfs.h>
49 #include <sys/ioctl.h>
50 #include <mntent.h>
51 #include <linux/fs.h>
52 #include <ctype.h>
53 #include <sys/syscall.h>
54 #include <sys/mman.h>
56 #define EXT4_IOC_DEFRAG _IOW('f', 10, struct ext4_ext_defrag_data)
57 #define EXT4_IOC_GROUP_INFO _IOW('f', 11, struct ext4_group_data_info)
58 #define EXT4_IOC_FREE_BLOCKS_INFO _IOW('f', 12, struct ext4_extents_info)
59 #define EXT4_IOC_EXTENTS_INFO _IOW('f', 13, struct ext4_extents_info)
60 #define EXT4_IOC_RESERVE_BLOCK _IOW('f', 14, struct ext4_extents_info)
61 #define EXT4_IOC_MOVE_VICTIM _IOW('f', 15, struct ext4_extents_info)
62 #define EXT4_IOC_BLOCK_RELEASE _IO('f', 8)
64 #define DEFRAG_MAX_ENT 32
66 /* Extent status which are used in extent_t */
67 #define EXT4_EXT_USE 0
68 #define EXT4_EXT_FREE 1
69 #define EXT4_EXT_RESERVE 2
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)
80 #define DEFRAG_FORCE_VICTIM 2
82 /* Magic number for ext4 */
83 #define EXT4_SUPER_MAGIC 0xEF53
85 /* Force defrag mode: Max file size in bytes (128MB) */
86 #define MAX_FILE_SIZE (unsigned long)1 << 27
88 /* Data type for filesystem-wide blocks number */
89 typedef unsigned long long ext4_fsblk_t;
91 /* data type for file logical block number */
92 typedef unsigned int ext4_lblk_t;
94 /* data type for block offset of block group */
95 typedef int ext4_grpblk_t;
97 /* Ioctl command */
98 #define EXT4_IOC_FIBMAP _IOW('f', 9, ext4_fsblk_t)
100 #ifndef __NR_fadvise64
101 #define __NR_fadvise64 250
102 #endif
104 #ifndef __NR_sync_file_range
105 #define __NR_sync_file_range 314
106 #endif
108 #ifndef POSIX_FADV_DONTNEED
109 #if defined(__s390x__)
110 #define POSIX_FADV_DONTNEED 6 /* Don't need these pages. */
111 #else
112 #define POSIX_FADV_DONTNEED 4 /* Don't need these pages. */
113 #endif
114 #endif
116 #ifndef SYNC_FILE_RANGE_WAIT_BEFORE
117 #define SYNC_FILE_RANGE_WAIT_BEFORE 1
118 #endif
119 #ifndef SYNC_FILE_RANGE_WRITE
120 #define SYNC_FILE_RANGE_WRITE 2
121 #endif
122 #ifndef SYNC_FILE_RANGE_WAIT_AFTER
123 #define SYNC_FILE_RANGE_WAIT_AFTER 4
124 #endif
126 #define DEVNAME 0
127 #define DIRNAME 1
128 #define FILENAME 2
130 #define RETURN_OK 0
131 #define RETURN_NG -1
132 #define FTW_CONT 0
133 #define FTW_OPEN_FD 2000
134 #define FILE_CHK_OK 0
135 #define FILE_CHK_NG -1
136 #define FS_EXT4 "ext4dev"
137 #define ROOT_UID 0
138 #define CHECK_FRAG_COUNT 1
140 /* Defrag block size, in bytes */
141 #define DEFRAG_SIZE 67108864
143 #define min(x, y) (((x) > (y)) ? (y) : (x))
145 #define PRINT_ERR_MSG(msg) fprintf(stderr, "%s\n", (msg));
146 #define PRINT_FILE_NAME(file) fprintf(stderr, "\t\t \"%s\"\n", (file));
148 #define MSG_USAGE \
149 "Usage : e4defrag [-v] file...| directory...| device...\n\
150 : e4defrag -f file [blocknr] \n\
151 : e4defrag -r directory... | device... \n"
153 #define MSG_R_OPTION " with regional block allocation mode.\n"
154 #define NGMSG_MTAB "\te4defrag : Can not access /etc/mtab."
155 #define NGMSG_UNMOUNT "\te4defrag : FS is not mounted."
156 #define NGMSG_EXT4 "\te4defrag : FS is not ext4 File System."
157 #define NGMSG_FS_INFO "\te4defrag : get FSInfo fail."
158 #define NGMSG_FILE_INFO "\te4defrag : get FileInfo fail."
159 #define NGMSG_FILE_OPEN "\te4defrag : open fail."
160 #define NGMSG_FILE_SYNC "\te4defrag : sync(fsync) fail."
161 #define NGMSG_FILE_DEFRAG "\te4defrag : defrag fail."
162 #define NGMSG_FILE_BLOCKSIZE "\te4defrag : can't get blocksize."
163 #define NGMSG_FILE_FIBMAP "\te4defrag : can't get block number."
164 #define NGMSG_FILE_UNREG "\te4defrag : File is not regular file."
166 #define NGMSG_FILE_LARGE \
167 "\te4defrag : Defrag size is larger than FileSystem's free space."
169 #define NGMSG_FILE_PRIORITY \
170 "\te4defrag : File is not current user's file or current user is not root."
172 #define NGMSG_FILE_LOCK "\te4defrag : File is locked."
173 #define NGMSG_FILE_BLANK "\te4defrag : File size is 0."
174 #define NGMSG_GET_LCKINFO "\te4defrag : get LockInfo fail."
175 #define NGMSG_TYPE \
176 "e4defrag : Can not process %s in regional mode.\n"
177 #define NGMSG_LOST_FOUND "\te4defrag : Can not process \"lost+found\"."
178 #define NGMSG_REALPATH "\te4defrag : Can not get full path."
179 #define NGMSG_FILE_MAP "\te4defrag : get file map fail."
180 #define NGMSG_FILE_DROP_BUFFER "\te4defrag : free page fail."
181 #define NGMSG_FADVISE_SYSCALL "\tfadvise fail."
182 #define NGMSG_FORCE_DEFRAG_SIZE \
183 "\te4defrag : Cannot specify a file larger than %luMB in force defrag mode."
185 struct ext4_extent_data {
186 ext4_lblk_t block; /* start logical block number */
187 ext4_fsblk_t start; /* start physical block number */
188 int len; /* blocks count */
191 /* Used for defrag */
192 struct ext4_ext_defrag_data {
193 ext4_lblk_t start_offset; /* start offset to defrag in blocks */
194 ext4_lblk_t defrag_size; /* size of defrag in blocks */
195 ext4_fsblk_t goal; /* block offset for allocation */
196 int flag; /* free space mode flag */
197 struct ext4_extent_data ext;
200 struct ext4_group_data_info {
201 int s_blocks_per_group; /* blocks per group */
202 int s_inodes_per_group; /* inodes per group */
205 struct ext4_extents_info {
206 unsigned long long ino; /* inode number */
207 int max_entries; /* maximum extents count */
208 int entries; /* extent number/count */
209 ext4_lblk_t f_offset; /* file offset */
210 ext4_grpblk_t g_offset; /* group offset */
211 ext4_fsblk_t goal;
212 struct ext4_extent_data ext[DEFRAG_MAX_ENT];
215 typedef struct extent {
216 struct extent *prev;
217 unsigned long tag; /* Extent status */
218 unsigned long ino; /* File's inode number */
219 struct ext4_extent_data data; /* Extent belong to file */
220 struct extent *next;
221 } extent_t;
223 typedef struct exts_group {
224 struct exts_group *prev;
225 extent_t *start; /* Start ext */
226 extent_t *end; /* End ext */
227 int len; /* Length of this continuous region */
228 struct exts_group *next;
229 } exts_group_t;
231 typedef struct extent_wrap {
232 struct extent_wrap *prev, *next;
233 struct extent *group_ext;
234 } extent_wrap_t;
236 int force_flag;
237 int detail_flag;
238 int regional_flag;
239 int amount_cnt;
240 int succeed_cnt;
241 char lost_found_dir[PATH_MAX + 1];
242 ext4_fsblk_t goal;
243 ext4_fsblk_t fgoal = -1;
246 * fadvise() - advise operater system process page cache.
248 * @fd: file descriptor.
249 * @offset: file offset.
250 * @len: area length.
251 * @advise: process flag.
253 int fadvise(int fd, loff_t offset, size_t len, int advise)
255 return syscall(__NR_fadvise64, fd, offset, len, advise);
259 * sync_file_range() - sync file region.
261 * @fd: file descriptor.
262 * @offset: file offset.
263 * @length: area length.
264 * @advise: process flag.
266 int sync_file_range(int fd, loff_t offset, loff_t length, unsigned int flag)
268 return syscall(__NR_sync_file_range, fd, offset, length, flag);
272 * page_in_core() - get information on whether pages are in core.
274 * @fd: file descriptor.
275 * @defrag_data: data used for defrag.
276 * @vec: page state array.
277 * @page_num: page number.
279 int page_in_core(int fd, struct ext4_ext_defrag_data defrag_data,
280 unsigned char **vec, unsigned long *page_num)
282 int blocksize;
283 int pagesize = getpagesize();
284 void *page = NULL;
285 loff_t offset, end_offset, length;
287 if (vec == NULL || *vec != NULL)
288 return RETURN_NG;
290 if (ioctl(fd, FIGETBSZ, &blocksize) < 0)
291 return RETURN_NG;
293 /*in mmap, offset should be a multiple of the page size */
294 offset = defrag_data.start_offset * blocksize;
295 length = defrag_data.defrag_size * blocksize;
296 end_offset = offset + length;
297 /* round the offset down to the nearest multiple of pagesize */
298 offset = (offset / pagesize) * pagesize;
299 length = end_offset - offset;
301 page = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, offset);
302 if (page == MAP_FAILED)
303 return RETURN_NG;
305 *page_num = 0;
306 *page_num = (length + pagesize - 1) / pagesize;
307 *vec = (unsigned char *)calloc(*page_num, 1);
308 if (*vec == NULL)
309 return RETURN_NG;
311 /* get information on whether pages are in core */
312 if (mincore(page, (size_t)length, *vec) == -1) {
313 if (*vec)
314 free(*vec);
315 return RETURN_NG;
318 if (munmap(page, length) == -1) {
319 if (*vec)
320 free(*vec);
321 return RETURN_NG;
324 return RETURN_OK;
328 * defrag_fadvise() - predeclare an access pattern for file data.
330 * @fd: file descriptor.
331 * @defrag_data: data used for defrag.
332 * @vec: page state array.
333 * @page_num: page number.
335 int defrag_fadvise(int fd, struct ext4_ext_defrag_data defrag_data,
336 unsigned char *vec, unsigned long page_num)
338 int flag = 1;
339 int blocksize;
340 int pagesize = getpagesize();
341 int fadvise_flag = POSIX_FADV_DONTNEED;
342 int sync_flag = SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE|
343 SYNC_FILE_RANGE_WAIT_AFTER;
344 unsigned long i;
345 loff_t offset;
347 if (ioctl(fd, FIGETBSZ, &blocksize) < 0)
348 return RETURN_NG;
350 offset = (loff_t)defrag_data.start_offset * blocksize;
351 offset = (offset / pagesize) * pagesize;
353 /* sync file for fadvise process */
354 if (sync_file_range(fd, offset,
355 (loff_t)pagesize*page_num, sync_flag) != 0)
356 return RETURN_NG;
358 /* try to release buffer cache this process used,
359 * then other process can use the released buffer */
360 for (i = 0; i < page_num; i++) {
361 if ((vec[i] & 0x1) == 0) {
362 offset += pagesize;
363 continue;
365 if (fadvise(fd, offset, pagesize, fadvise_flag) != 0) {
366 if (detail_flag && flag) {
367 perror(NGMSG_FADVISE_SYSCALL);
368 flag = 0;
371 offset += pagesize;
374 return RETURN_OK;
377 * check_free_size() - Check if there's enough disk space.
379 * @fd: the file's descriptor.
380 * @buf: the pointer of the struct stat64.
382 int check_free_size(int fd, const struct stat64 *buf)
384 off64_t size = 0;
385 off64_t free_size = 0;
386 struct statfs fsbuf;
388 /* target file size */
389 size = buf->st_size;
391 if (fstatfs(fd, &fsbuf) < 0) {
392 if (detail_flag)
393 perror(NGMSG_FS_INFO);
394 return RETURN_NG;
397 /* Compute free space for root and normal user separately */
398 if (getuid() == ROOT_UID)
399 free_size = (off64_t)fsbuf.f_bsize * fsbuf.f_bfree;
400 else
401 free_size = (off64_t)fsbuf.f_bsize * fsbuf.f_bavail;
403 if (free_size >= size)
404 return RETURN_OK;
406 return RETURN_NG;
409 int file_check(int fd, const struct stat64 *buf, const char *file_name);
410 int force_defrag(int fd, const struct stat64 *buf, int blocksize);
413 * ftw_fn() - Check file attributes and ioctl call to avoid
414 * illegal operations.
416 * @file: the file's name.
417 * @buf: the pointer of the struct stat64.
418 * @flag: file type.
419 * @ftwbuf: the pointer of a struct FTW.
421 int ftw_fn(const char *file, const struct stat64 *buf, int flag,
422 struct FTW *ftwbuf)
424 int fd;
425 int blocksize;
426 int percent = 0;
427 int defraged_size = 0;
428 int ret = RETURN_NG;
429 int pos, file_frags_start, file_frags_end;
430 unsigned long page_num;
431 unsigned char *vec = NULL;
432 loff_t start = 0;
433 struct ext4_ext_defrag_data df_data;
434 struct ext4_extents_info extents_info;
436 if (lost_found_dir[0] != '\0' &&
437 !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) {
438 if (detail_flag) {
439 PRINT_ERR_MSG(NGMSG_LOST_FOUND);
440 PRINT_FILE_NAME(file);
442 return FTW_CONT;
445 if (flag != FTW_F)
446 goto out;
448 amount_cnt++;
449 fd = open64(file, O_RDONLY);
450 if (fd < 0) {
451 if (detail_flag) {
452 perror(NGMSG_FILE_OPEN);
453 PRINT_FILE_NAME(file);
455 return FTW_CONT;
458 if (file_check(fd, buf, file) == FILE_CHK_NG) {
459 close(fd);
460 return FTW_CONT;
463 if (fsync(fd) < 0) {
464 if (detail_flag) {
465 perror(NGMSG_FILE_SYNC);
466 PRINT_FILE_NAME(file);
468 close(fd);
469 return FTW_CONT;
471 /* Get blocksize */
472 if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
473 if (detail_flag) {
474 perror(NGMSG_FILE_BLOCKSIZE);
475 PRINT_FILE_NAME(file);
477 close(fd);
478 return FTW_CONT;
480 /* Ioctl call does the actual defragment job. */
481 df_data.start_offset = 0;
482 df_data.goal = goal;
483 df_data.ext.len = 0;
484 if (force_flag)
485 df_data.flag = 1;
486 else
487 df_data.flag = 0;
489 /* count file frags before defrag if detail_flag set */
490 if (detail_flag) {
491 pos = 0;
492 file_frags_start = 0;
493 memset(&extents_info, 0, sizeof(struct ext4_extents_info));
494 extents_info.ino = buf->st_ino;
495 extents_info.max_entries = DEFRAG_MAX_ENT;
496 extents_info.entries = 0;
498 do {
499 extents_info.entries += pos;
500 pos = extents_info.entries;
501 ret = ioctl(fd, EXT4_IOC_EXTENTS_INFO, &extents_info);
502 if (ret < 0) {
503 perror(NGMSG_FILE_INFO);
504 PRINT_FILE_NAME(file);
505 close(fd);
506 return FTW_CONT;
508 file_frags_start += extents_info.entries;
509 } while (extents_info.entries == DEFRAG_MAX_ENT && ret == 0);
512 /* print process progress */
513 printf("\tprocessing -------> %s:\n", file);
514 percent = (start * 100) / buf->st_size;
515 printf("\033[79;16H\033[K progressing ====> %d%%", percent);
516 fflush(stdout);
518 while (1) {
519 df_data.defrag_size = (min((buf->st_size - start),
520 DEFRAG_SIZE) + blocksize - 1) / blocksize;
521 ret = page_in_core(fd, df_data, &vec, &page_num);
522 if (ret == RETURN_NG) {
523 if (detail_flag) {
524 perror(NGMSG_FILE_MAP);
525 PRINT_FILE_NAME(file);
526 } else {
527 printf("\n");
529 close(fd);
530 return FTW_CONT;
533 /* EXT4_IOC_DEFRAG */
534 defraged_size = ioctl(fd, EXT4_IOC_DEFRAG, &df_data);
536 /* free page */
537 ret = defrag_fadvise(fd, df_data, vec, page_num);
538 if (vec) {
539 free(vec);
540 vec = NULL;
542 if (ret == RETURN_NG) {
543 if (detail_flag) {
544 perror(NGMSG_FILE_DROP_BUFFER);
545 PRINT_FILE_NAME(file);
546 } else {
547 printf("\n");
549 close(fd);
550 return FTW_CONT;
553 /* Go into force defrag mode */
554 if (defraged_size < 0 && force_flag == 1 && errno == ENOSPC) {
556 * File size is larger than max size of
557 * force defrag mode
559 if (buf->st_size > MAX_FILE_SIZE) {
560 fprintf(stderr, NGMSG_FORCE_DEFRAG_SIZE,
561 (MAX_FILE_SIZE) / (1024 * 1024));
562 close(fd);
563 return FTW_CONT;
566 defraged_size = force_defrag(fd, buf, blocksize);
567 if (defraged_size * blocksize >= buf->st_size)
568 /* Whole file is enforcedly defraged */
569 break;
570 else
571 defraged_size = RETURN_NG;
573 if (defraged_size < 0) {
574 if (detail_flag) {
575 perror(NGMSG_FILE_DEFRAG);
576 PRINT_FILE_NAME(file);
577 } else {
578 printf("\n");
580 close(fd);
581 return FTW_CONT;
583 df_data.start_offset += defraged_size;
584 start = (long long)df_data.start_offset * blocksize;
586 /* print process progress */
587 percent = (start * 100) / buf->st_size;
589 /* disk space file used is bigger than logical size */
590 if (percent > 100)
591 percent = 100;
592 printf("\033[79;16H\033[K progressing ====> %d%%", percent);
593 fflush(stdout);
595 /* End of file */
596 if (start >= buf->st_size)
597 break;
600 /* count file frags after defrag and print extents info */
601 if (detail_flag) {
602 pos = 0;
603 ret = RETURN_NG;
604 file_frags_end = 0;
605 extents_info.entries = 0;
607 do {
608 extents_info.entries += pos;
609 pos = extents_info.entries;
610 ret = ioctl(fd, EXT4_IOC_EXTENTS_INFO, &extents_info);
611 if (ret < 0) {
612 printf("\n");
613 perror(NGMSG_FILE_INFO);
614 PRINT_FILE_NAME(file);
615 close(fd);
616 return FTW_CONT;
618 file_frags_end += extents_info.entries;
619 } while (extents_info.entries == DEFRAG_MAX_ENT && ret == 0);
621 printf("\n\t\textents: %d ==> %d",
622 file_frags_start, file_frags_end);
624 printf("\n");
625 close(fd);
626 succeed_cnt++;
628 out:
629 if (flag != FTW_F && detail_flag) {
630 PRINT_ERR_MSG(NGMSG_FILE_UNREG);
631 PRINT_FILE_NAME(file);
634 return FTW_CONT;
638 * file_check() - Check file's attributes.
640 * @fd: the file's descriptor.
641 * @buf: a pointer of the struct stat64.
642 * @file_name: the file's name.
644 int file_check(int fd, const struct stat64 *buf, const char *file_name)
646 struct flock lock;
648 lock.l_type = F_WRLCK; /* Write-lock check is more reliable. */
649 lock.l_start = 0;
650 lock.l_whence = SEEK_SET;
651 lock.l_len = 0;
653 /* Regular file */
654 if (S_ISREG(buf->st_mode) == 0) {
655 if (detail_flag) {
656 PRINT_ERR_MSG(NGMSG_FILE_UNREG);
657 PRINT_FILE_NAME(file_name);
659 return FILE_CHK_NG;
662 /* Free space */
663 if (check_free_size(fd, buf) == RETURN_NG) {
664 if (detail_flag) {
665 PRINT_ERR_MSG(NGMSG_FILE_LARGE);
666 PRINT_FILE_NAME(file_name);
668 return FILE_CHK_NG;
671 /* Priority */
672 if (getuid() != ROOT_UID &&
673 buf->st_uid != getuid()) {
674 if (detail_flag) {
675 PRINT_ERR_MSG(NGMSG_FILE_PRIORITY);
676 PRINT_FILE_NAME(file_name);
678 return FILE_CHK_NG;
681 /* Lock status */
682 if (fcntl(fd, F_GETLK, &lock) < 0) {
683 if (detail_flag) {
684 perror(NGMSG_GET_LCKINFO);
685 PRINT_FILE_NAME(file_name);
687 return FILE_CHK_NG;
688 } else if (lock.l_type != F_UNLCK) {
689 if (detail_flag) {
690 PRINT_ERR_MSG(NGMSG_FILE_LOCK);
691 PRINT_FILE_NAME(file_name);
693 return FILE_CHK_NG;
696 /* Empty file */
697 if (buf->st_size == 0) {
698 if (detail_flag) {
699 PRINT_ERR_MSG(NGMSG_FILE_BLANK);
700 PRINT_FILE_NAME(file_name);
702 return FILE_CHK_NG;
705 return FILE_CHK_OK;
709 * is_ext4() - Whether on an ext4 filesystem.
711 * @filename: the file's name.
713 int is_ext4(const char *filename)
715 int maxlen, len;
716 FILE *fp = NULL;
717 char *mnt_type = NULL;
718 char *mtab = MOUNTED; /* Refer to /etc/mtab */
719 char file_path[PATH_MAX + 1];
720 struct mntent *mnt = NULL;
721 struct statfs buffs;
723 /* Get full path */
724 if (realpath(filename, file_path) == NULL) {
725 perror(NGMSG_REALPATH);
726 PRINT_FILE_NAME(filename);
727 return RETURN_NG;
730 if (statfs(file_path, &buffs) < 0) {
731 perror(NGMSG_FS_INFO);
732 PRINT_FILE_NAME(filename);
733 return RETURN_NG;
736 if (buffs.f_type != EXT4_SUPER_MAGIC) {
737 PRINT_ERR_MSG(NGMSG_EXT4);
738 return RETURN_NG;
741 if ((fp = setmntent(mtab, "r")) == NULL) {
742 perror(NGMSG_MTAB);
743 return RETURN_NG;
746 maxlen = 0;
747 while ((mnt = getmntent(fp)) != NULL) {
748 len = strlen(mnt->mnt_dir);
749 if (memcmp(file_path, mnt->mnt_dir, len) == 0) {
750 if (maxlen < len) {
751 maxlen = len;
752 mnt_type = realloc(mnt_type,
753 strlen(mnt->mnt_type) + 1);
754 if (!mnt_type) {
755 endmntent(fp);
756 return RETURN_NG;
758 strcpy(mnt_type, mnt->mnt_type);
759 strncpy(lost_found_dir, mnt->mnt_dir, PATH_MAX);
764 if (strcmp(mnt_type, FS_EXT4) == 0) {
765 endmntent(fp);
766 if (mnt_type)
767 free(mnt_type);
768 return RETURN_OK;
769 } else {
770 endmntent(fp);
771 if (mnt_type)
772 free(mnt_type);
773 PRINT_ERR_MSG(NGMSG_EXT4);
774 return RETURN_NG;
779 * get_mount_point() - Get device's mount point.
781 * @devname: the device's name.
782 * @mount_point: the mount point.
783 * @dir_path_len: the length of directory.
785 int get_mount_point(const char *devname, char *mount_point, int
786 dir_path_len)
788 char *mtab = MOUNTED; /* Refer to /etc/mtab */
789 FILE *fp = NULL;
790 struct mntent *mnt = NULL;
792 if ((fp = setmntent(mtab, "r")) == NULL) {
793 perror(NGMSG_MTAB);
794 return RETURN_NG;
797 while ((mnt = getmntent(fp)) != NULL) {
798 if (strcmp(devname, mnt->mnt_fsname) == 0) {
799 endmntent(fp);
800 if (strcmp(mnt->mnt_type, FS_EXT4) == 0) {
801 strncpy(mount_point, mnt->mnt_dir,
802 dir_path_len);
803 return RETURN_OK;
805 PRINT_ERR_MSG(NGMSG_EXT4);
806 return RETURN_NG;
809 endmntent(fp);
810 PRINT_ERR_MSG(NGMSG_UNMOUNT);
811 return RETURN_NG;
815 * main() - ext4 online defrag.
817 * @argc: the number of parameter.
818 * @argv[]: the pointer array of parameter.
820 int main(int argc, char *argv[])
822 int fd;
823 int ret;
824 int opt;
825 int i, flags;
826 int arg_type;
827 int detail_tmp;
828 int success_flag;
829 char dir_name[PATH_MAX + 1];
830 struct stat64 buf;
832 i = 1;
833 flags = 0;
834 arg_type = -1;
835 detail_tmp = -1;
836 success_flag = 0;
837 flags |= FTW_PHYS; /* Do not follow symlink */
838 flags |= FTW_MOUNT; /* Stay within the same filesystem */
839 /* Parse arguments */
840 if (argc == 1 || (argc == 2 && argv[1][0] == '-')) {
841 printf(MSG_USAGE);
842 exit(1);
845 while ((opt = getopt(argc, argv, "rvf")) != EOF) {
846 switch (opt) {
847 case 'r':
848 regional_flag = 1;
849 i = 2;
850 break;
851 case 'v':
852 detail_flag = 1;
853 i = 2;
854 break;
855 case 'f':
856 force_flag = 1;
857 i = 2;
859 if (argc > 4) {
860 printf("Illegal argument\n\n");
861 printf(MSG_USAGE);
862 exit(1);
865 if (argc == 4) {
866 int res_strlen;
867 res_strlen = strlen(argv[3]);
868 for (res_strlen -= 1; res_strlen >= 0;
869 res_strlen--) {
870 if (!isdigit(argv[3][res_strlen])) {
871 printf("Illegal argument\n\n");
872 printf(MSG_USAGE);
873 exit(1);
877 fgoal = strtoul(argv[3], NULL, 0);
878 if (errno) {
879 printf("block num shold be < 32bit\n");
880 exit(1);
883 if (!fgoal)
884 fgoal = -1;
885 break;
886 default:
887 printf(MSG_USAGE);
888 exit(1);
892 /* Main process */
893 for (; i < argc; i++) {
894 amount_cnt = 0;
895 succeed_cnt = 0;
896 memset(dir_name, 0, PATH_MAX + 1);
897 memset(lost_found_dir, 0, PATH_MAX + 1);
899 if (force_flag && i == 3)
900 break;
902 if (lstat64(argv[i], &buf) < 0) {
903 perror(NGMSG_FILE_INFO);
904 PRINT_FILE_NAME(argv[i]);
905 continue;
908 /* Regular file is acceptalbe with force defrag mode */
909 if (force_flag && !S_ISREG(buf.st_mode)) {
910 printf("Inappropriate file type \n\n");
911 printf(MSG_USAGE);
912 exit(1);
915 /* Block device */
916 if (S_ISBLK(buf.st_mode)) {
917 arg_type = DEVNAME;
918 if (get_mount_point(argv[i], dir_name, PATH_MAX)
919 == RETURN_NG)
920 continue;
921 printf("Start defragment for device(%s)\n", argv[i]);
922 } else if (S_ISDIR(buf.st_mode)) {
923 /* Directory */
924 arg_type = DIRNAME;
925 if (access(argv[i], R_OK) < 0) {
926 perror(argv[i]);
927 continue;
929 strcpy(dir_name, argv[i]);
930 } else if (S_ISREG(buf.st_mode)) {
931 /* Regular file */
932 arg_type = FILENAME;
933 } else {
934 /* Irregular file */
935 PRINT_ERR_MSG(NGMSG_FILE_UNREG);
936 PRINT_FILE_NAME(argv[i]);
937 continue;
940 /* Device's ext4 check is in get_mount_point() */
941 if (arg_type == FILENAME || arg_type == DIRNAME) {
942 if (is_ext4(argv[i]) == RETURN_NG)
943 continue;
944 if (realpath(argv[i], dir_name) == NULL) {
945 perror(NGMSG_REALPATH);
946 PRINT_FILE_NAME(argv[i]);
947 continue;
951 switch (arg_type) {
952 case DIRNAME:
953 printf("Start defragment for directory(%s)\n",
954 argv[i]);
956 int mount_dir_len = 0;
957 mount_dir_len = strnlen(lost_found_dir, PATH_MAX);
959 strncat(lost_found_dir, "/lost+found",
960 PATH_MAX - strnlen(lost_found_dir, PATH_MAX));
962 /* not the case("e4defrag mount_piont_dir") */
963 if (dir_name[mount_dir_len] != '\0') {
964 /* "e4defrag mount_piont_dir/lost+found" */
965 /* or "e4defrag mount_piont_dir/lost+found/" */
966 if (strncmp(lost_found_dir, dir_name,
967 strnlen(lost_found_dir,
968 PATH_MAX)) == 0 &&
969 (dir_name[strnlen(lost_found_dir,
970 PATH_MAX)] == '\0' ||
971 dir_name[strnlen(lost_found_dir,
972 PATH_MAX)] == '/')) {
973 PRINT_ERR_MSG(NGMSG_LOST_FOUND);
974 PRINT_FILE_NAME(argv[i]);
975 continue;
978 /* "e4defrag mount_piont_dir/else_dir" */
979 memset(lost_found_dir, 0, PATH_MAX + 1);
981 case DEVNAME:
982 if (arg_type == DEVNAME) {
983 strncpy(lost_found_dir, dir_name,
984 strnlen(dir_name, PATH_MAX));
985 strncat(lost_found_dir, "/lost+found/",
986 PATH_MAX - strnlen(lost_found_dir,
987 PATH_MAX));
990 /* Regional block allocation */
991 if (regional_flag) {
992 printf(MSG_R_OPTION);
994 if ((fd = open64(dir_name, O_RDONLY)) < 0) {
995 if (detail_flag) {
996 perror(NGMSG_FILE_OPEN);
997 PRINT_FILE_NAME(dir_name);
999 continue;
1002 goal = 0;
1003 if ((ret = ioctl(fd, EXT4_IOC_FIBMAP,
1004 &goal)) != 0) {
1005 perror(NGMSG_FILE_FIBMAP);
1006 PRINT_FILE_NAME(dir_name);
1007 close(fd);
1008 continue;
1010 close(fd);
1013 /* File tree walk */
1014 nftw64(dir_name, ftw_fn, FTW_OPEN_FD, flags);
1015 printf("\tTotal:\t\t%12d\n", amount_cnt);
1016 printf("\tSuccess:\t%12d\n", succeed_cnt);
1017 printf("\tFailure:\t%12d\n",
1018 amount_cnt - succeed_cnt);
1019 break;
1020 case FILENAME:
1021 strncat(lost_found_dir, "/lost+found/",
1022 PATH_MAX - strnlen(lost_found_dir,
1023 PATH_MAX));
1024 if (strncmp(lost_found_dir, dir_name,
1025 strnlen(lost_found_dir,
1026 PATH_MAX)) == 0) {
1027 PRINT_ERR_MSG(NGMSG_LOST_FOUND);
1028 PRINT_FILE_NAME(argv[i]);
1029 continue;
1032 if (regional_flag) {
1033 fprintf(stderr, NGMSG_TYPE, argv[i]);
1034 continue;
1036 detail_tmp = detail_flag;
1037 detail_flag = 1;
1038 printf("Start defragment for %s\n", argv[i]);
1039 /* Single file process */
1040 ftw_fn(argv[i], &buf, FTW_F, NULL);
1041 if (succeed_cnt != 0)
1042 printf("\tSUCCESS\t:file defrag success.\n");
1043 detail_flag = detail_tmp;
1044 break;
1047 if (succeed_cnt != 0)
1048 success_flag = 1;
1051 if (success_flag)
1052 return RETURN_OK;
1054 exit(1);
1057 * insert_extent() - Sequentially insert extent by physical block number.
1059 * @extlist_head: the head of an extent list.
1060 * @ext: the extent element which will be inserted.
1062 int insert_extent(extent_t **extlist_head, extent_t *ext)
1064 extent_t *ext_tmp = *extlist_head;
1066 if (ext == NULL)
1067 return RETURN_NG;
1069 /* First element */
1070 if (*extlist_head == NULL) {
1071 (*extlist_head) = ext;
1072 (*extlist_head)->prev = *extlist_head;
1073 (*extlist_head)->next = *extlist_head;
1074 return RETURN_OK;
1077 if (ext->data.start <= ext_tmp->data.start) {
1078 /* Insert before head */
1079 if (ext_tmp->data.start < ext->data.start + ext->data.len)
1080 /* Overlap */
1081 return RETURN_NG;
1082 /* Adjust head */
1083 *extlist_head = ext;
1084 } else {
1085 /* Insert into the middle or last of the list */
1086 do {
1087 if (ext->data.start < ext_tmp->data.start)
1088 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;
1122 /* Initialize list */
1123 if (*exts_group_list_head == NULL) {
1124 (*exts_group_list_head) = exts_group;
1125 (*exts_group_list_head)->prev = *exts_group_list_head;
1126 (*exts_group_list_head)->next = *exts_group_list_head;
1127 return RETURN_OK;
1130 if (exts_group->len >= (*exts_group_list_head)->len) {
1131 /* Insert before exts_group_list_head */
1132 exts_group_tmp = (*exts_group_list_head)->prev;
1133 insert(exts_group_tmp, exts_group);
1134 *exts_group_list_head = exts_group;
1135 return RETURN_OK;
1138 /* Find insertion positon */
1139 for (exts_group_tmp = (*exts_group_list_head)->next;
1140 exts_group_tmp != *exts_group_list_head;
1141 exts_group_tmp = exts_group_tmp->next) {
1142 if (exts_group_tmp->len < exts_group->len)
1143 break;
1145 exts_group_tmp = exts_group_tmp->prev;
1146 insert(exts_group_tmp, exts_group);
1148 return RETURN_OK;
1152 * get_exts_group() - Get element from the exts_group list.
1154 * @exts_group_list_head: the head of a exts_group list.
1155 * @exts_group: the exts_group element which will be geted.
1157 exts_group_t *get_exts_group(exts_group_t **exts_group_list_head,
1158 exts_group_t *exts_group)
1160 if (exts_group == NULL || *exts_group_list_head == NULL)
1161 return NULL;
1163 /* Keep "exts_group_list_head" point to the largest extent group*/
1164 if (exts_group == *exts_group_list_head)
1165 *exts_group_list_head = exts_group->next;
1167 if (*exts_group_list_head == (*exts_group_list_head)->next &&
1168 exts_group == *exts_group_list_head) {
1169 /* Delete the last element in the list */
1170 *exts_group_list_head = NULL;
1172 exts_group->prev->next = exts_group->next;
1173 exts_group->next->prev = exts_group->prev;
1174 return exts_group;
1178 * free_exts_group() - Free the exts_group.
1180 * @*exts_group_list_head: the exts_group list head which will be free.
1183 void free_exts_group(exts_group_t *exts_group_list_head)
1185 exts_group_t *exts_group_tmp = NULL;
1187 if (exts_group_list_head == NULL)
1188 return;
1190 while (exts_group_list_head->next != exts_group_list_head) {
1191 exts_group_tmp = exts_group_list_head;
1192 exts_group_list_head->prev->next = exts_group_list_head->next;
1193 exts_group_list_head->next->prev = exts_group_list_head->prev;
1194 exts_group_list_head = exts_group_list_head->next;
1195 free(exts_group_tmp);
1197 free(exts_group_list_head);
1201 * free_ext() - Free the extent list.
1203 * @extent_list_head: the extent list head of which will be free.
1205 void free_ext(extent_t *extent_list_head)
1207 extent_t *extent_tmp = NULL;
1209 if (extent_list_head == NULL)
1210 return;
1212 while (extent_list_head->next != extent_list_head) {
1213 extent_tmp = extent_list_head;
1214 extent_list_head->prev->next = extent_list_head->next;
1215 extent_list_head->next->prev = extent_list_head->prev;
1216 extent_list_head = extent_list_head->next;
1217 free(extent_tmp);
1219 free(extent_list_head);
1223 * move_wrap() - Move a ext_wrap from one list to another.
1225 * @from: the list which will be moved from.
1226 * @to: the list which will be moved to.
1227 * @entry: the ext_wrap which will be moved.
1229 int move_wrap(extent_wrap_t **from, extent_wrap_t **to,
1230 extent_wrap_t *entry)
1232 if (!to || !entry)
1233 return RETURN_NG;
1235 if (from && *from == entry) {
1236 if ((*from)->next == *from)
1237 *from = NULL;
1238 else
1239 *from = (*from)->next;
1241 entry->next->prev = entry->prev;
1242 entry->prev->next = entry->next;
1243 if (!(*to)) {
1244 *to = entry;
1245 (*to)->prev = (*to)->next = *to;
1246 } else {
1247 entry->next = *to;
1248 entry->prev = (*to)->prev;
1249 (*to)->prev->next = entry;
1250 (*to)->prev = entry;
1252 return RETURN_OK;
1256 * mark_wrap() - Mark extent status as "EXT4_EXT_RESERVE".
1258 * @ext_wrap_list: the ext_wrap list which will be marked.
1260 void mark_wrap(extent_wrap_t *ext_wrap_list)
1262 extent_wrap_t *wrap = ext_wrap_list;
1264 if (!ext_wrap_list)
1265 return;
1267 do {
1268 wrap->group_ext->tag |= EXT4_EXT_RESERVE;
1269 wrap = wrap->next;
1270 } while (wrap != ext_wrap_list);
1274 * free_wrap_list() - Free the ext_wrap list.
1276 * @ext_wrap_head: the ext_wrap list head which will be free.
1278 void free_wrap_list(extent_wrap_t **ext_wrap_head)
1280 extent_wrap_t *wrap, *ext_wrap_tmp;
1282 if (!ext_wrap_head || !(*ext_wrap_head))
1283 return;
1285 wrap = *ext_wrap_head;
1286 do {
1287 ext_wrap_tmp = wrap;
1288 wrap = wrap->next;
1289 free(ext_wrap_tmp);
1290 } while (wrap != *ext_wrap_head);
1291 *ext_wrap_head = NULL;
1295 * do_defrag() - Execute the defrag program.
1297 * @fd: the file's descriptor.
1298 * @exts_group: the exts_group which will be defraged.
1299 * @defrag_data: the data which will be defraged.
1301 static inline int do_defrag(int fd, exts_group_t *exts_group,
1302 struct ext4_ext_defrag_data defrag_data)
1304 int ret = 0;
1305 int defraged_size = 0;
1306 int fadvise_ret = 0;
1307 unsigned long page_num;
1308 unsigned char *vec = NULL;
1309 extent_t *extent = NULL;
1311 /* Defrag */
1312 defrag_data.ext.start = exts_group->start->data.start;
1313 defrag_data.ext.len = exts_group->len;
1314 defrag_data.ext.block = 0;
1315 defrag_data.defrag_size = exts_group->len;
1316 defrag_data.flag = DEFRAG_FORCE_VICTIM;
1317 defrag_data.goal = exts_group->start->data.start;
1319 if (page_in_core(fd, defrag_data, &vec, &page_num) == RETURN_NG)
1320 return RETURN_NG;
1322 defraged_size = ioctl(fd, EXT4_IOC_DEFRAG, &defrag_data);
1324 /* free pages */
1325 fadvise_ret = defrag_fadvise(fd, defrag_data, vec, page_num);
1326 if (vec)
1327 free(vec);
1329 if (fadvise_ret == RETURN_NG || defraged_size < 0)
1330 return RETURN_NG;
1332 /* Release reserved sign */
1333 extent = exts_group->start;
1334 do {
1335 extent->tag &= ~EXT4_EXT_RESERVE;
1336 extent = extent->next;
1337 } while (extent != exts_group->end->next);
1339 ret += defraged_size;
1341 return ret;
1345 * get_used_extent() - Get used extent in the block group.
1347 * @fd: the file's descriptor.
1348 * @ext_list_head: the head of the extent list.
1349 * @istart: the start of the inode.
1350 * @iend: the end of the inode.
1351 * @bstart: the start of the block.
1352 * @bend: the end of the block.
1354 int get_used_extent(int fd, extent_t **ext_list_head,
1355 unsigned long long istart, unsigned long long iend,
1356 ext4_fsblk_t bstart, ext4_fsblk_t bend)
1358 struct ext4_extents_info extents_info;
1359 unsigned long long inode;
1360 int pos = 0;
1361 int ret = 0;
1363 memset(&extents_info, 0, sizeof(struct ext4_extents_info));
1364 extents_info.max_entries = DEFRAG_MAX_ENT;
1366 for (inode = istart; inode <= iend; inode++) {
1367 extents_info.ino = inode;
1368 extents_info.entries = 0;
1369 pos = 0;
1370 do {
1371 /* Get extents info */
1372 int i;
1373 extents_info.entries += pos;/* Offset */
1374 pos = extents_info.entries;
1375 memset(extents_info.ext, 0,
1376 sizeof(struct ext4_extent_data) *
1377 DEFRAG_MAX_ENT);
1378 ret = ioctl(fd, EXT4_IOC_EXTENTS_INFO, &extents_info);
1379 if (ret < 0) {
1380 if (errno != ENOMEM)
1381 continue;
1382 else
1383 return RETURN_NG;
1386 for (i = 0; i < extents_info.entries; i++) {
1387 extent_t *extent = NULL;
1388 /* Is this extent in current block group? */
1389 if (extents_info.ext[i].start < bstart ||
1390 extents_info.ext[i].start > bend)
1391 continue;
1392 extent = malloc(sizeof(extent_t));
1393 if (extent == NULL)
1394 return RETURN_NG;
1395 memset(extent, 0, sizeof(extent_t));
1396 memcpy(&(extent->data), &extents_info.ext[i],
1397 sizeof(struct ext4_extent_data));
1398 extent->ino = inode;
1399 if (insert_extent(ext_list_head, extent) < 0) {
1400 if (extent)
1401 free(extent);
1402 return RETURN_NG;
1405 } while (extents_info.entries == DEFRAG_MAX_ENT && ret == 0);
1408 if (ret < 0) {
1409 if (errno == ENOENT || errno == ESTALE)
1410 return RETURN_OK;
1412 return ret;
1416 * get_free_extent() - Get used extent in the block group.
1418 * @fd: the file's descriptor.
1419 * @inode: inode number from struct stat64.
1420 * @blocks_per_group: the block number of each block group.
1421 * @ext_list_head: the head of the extent list.
1423 int get_free_extent(int fd, unsigned long long inode,
1424 int blocks_per_group, extent_t **ext_list_head)
1426 ext4_grpblk_t pos = 0;
1427 struct ext4_extents_info extents_info;
1429 memset(&extents_info, 0, sizeof(struct ext4_extents_info));
1430 extents_info.ino = inode;
1431 extents_info.max_entries = DEFRAG_MAX_ENT;
1432 while (pos < blocks_per_group) {
1433 int i;
1434 if (ioctl(fd, EXT4_IOC_FREE_BLOCKS_INFO, &extents_info) < 0)
1435 return RETURN_NG;
1437 * No free extent after the logical block number "pos".
1438 * In other word, offset this time equals to prev recursion.
1440 for (i = 0;
1441 extents_info.ext[i].len != 0 && i < DEFRAG_MAX_ENT; i++) {
1442 /* Alloc list node store extent */
1443 extent_t *extent = NULL;
1444 extent = malloc(sizeof(extent_t));
1445 if (extent == NULL)
1446 return RETURN_NG;
1447 memset(extent, 0, sizeof(extent_t));
1448 memcpy(&(extent->data), &(extents_info.ext[i]),
1449 sizeof(struct ext4_extent_data));
1450 extent->tag = EXT4_EXT_FREE;/* Free extent */
1451 if (insert_extent(ext_list_head, extent) < 0) {
1452 if (extent)
1453 free(extent);
1454 return RETURN_NG;
1458 * No free extent after the logical block number "pos".
1459 * In other word, offset this time equals to prev recursion.
1461 if (pos == extents_info.g_offset)
1462 break;
1463 if (i < DEFRAG_MAX_ENT)
1464 break;
1465 /* Record the offset of logical block number this time */
1466 pos = extents_info.g_offset;
1467 memset(extents_info.ext, 0,
1468 sizeof(struct ext4_extent_data) * DEFRAG_MAX_ENT);
1471 return RETURN_OK;
1475 * join_extents() - Find continuous region(exts_group).
1477 * @ext_list_head: the head of the extent list.
1478 * @target_exts_group_list_head:the head of the target exts_group list.
1479 * @exts_group_list_head: the head of the original exts_group list.
1480 * @filesize: the file's descriptor.
1481 * @max: the max size of free space.
1483 int join_extents(extent_t *ext_list_head,
1484 exts_group_t **target_exts_group_list_head,
1485 exts_group_t **exts_group_list_head,
1486 unsigned long filesize, int *max)
1488 int len;
1489 extent_t *ext_start, *extent_tmp;
1491 ext_start = extent_tmp = ext_list_head;
1492 *max = 0;
1493 len = ext_list_head->data.len;
1494 extent_tmp = extent_tmp->next;
1495 do {
1496 if (len >= filesize) {
1498 * Hit on the way,
1499 * one extent group is enough for defrag, so return.
1501 exts_group_t *exts_group_tmp = NULL;
1502 exts_group_tmp = malloc(sizeof(exts_group_t));
1503 if (!exts_group_tmp)
1504 return RETURN_NG;
1506 exts_group_tmp->prev = exts_group_tmp->next = NULL;
1507 exts_group_tmp->start = ext_start;
1508 exts_group_tmp->end = extent_tmp->prev;
1509 exts_group_tmp->len = len;
1510 if (insert_exts_group(target_exts_group_list_head,
1511 exts_group_tmp) < 0) {
1512 if (exts_group_tmp)
1513 free(exts_group_tmp);
1514 return RETURN_NG;
1516 return CHECK_FRAG_COUNT;
1519 * This extent and previous extent is not continuous,
1520 * so, all previous extents is treated as an extent group.
1522 if ((extent_tmp->prev->data.start + extent_tmp->prev->data.len)
1523 != extent_tmp->data.start) {
1524 exts_group_t *exts_group_tmp = NULL;
1525 exts_group_tmp = malloc(sizeof(exts_group_t));
1526 if (exts_group_tmp == NULL)
1527 return RETURN_NG;
1529 memset(exts_group_tmp, 0, sizeof(exts_group_t));
1530 exts_group_tmp->len = len;
1531 exts_group_tmp->start = ext_start;
1532 exts_group_tmp->end = extent_tmp->prev;
1534 if (insert_exts_group(exts_group_list_head,
1535 exts_group_tmp) < 0) {
1536 if (exts_group_tmp)
1537 free(exts_group_tmp);
1538 return RETURN_NG;
1540 *max += len;
1541 ext_start = extent_tmp;
1542 len = extent_tmp->data.len;
1543 extent_tmp = extent_tmp->next;
1544 continue;
1547 * This extent and previous extent is continuous,
1548 * so, they belong to the same extent group, and we check
1549 * if the next extent belong to the same extent group.
1551 len += extent_tmp->data.len;
1552 extent_tmp = extent_tmp->next;
1553 } while (extent_tmp != ext_list_head->next);
1555 return RETURN_OK;
1559 *find_exts_group() - Find target exts_group.
1561 * @ext_count: the number of extents.
1562 * @filesize: the file's size.
1563 * @exts_group_list_head: the head of the original exts_group list
1564 * @target_exts_group_list_head: the head of the target exts_group list.
1566 int find_exts_group(int *ext_count, unsigned long filesize,
1567 exts_group_t **exts_group_list_head,
1568 exts_group_t **target_exts_group_list_head)
1570 int len;
1572 len = 0;/* Blocks we found for target file */
1574 if (!(*exts_group_list_head))
1575 return RETURN_NG;
1577 while (*exts_group_list_head) {
1578 exts_group_t *exts_group_tmp;
1579 if ((*exts_group_list_head)->len + len >= filesize) {
1581 * Search from the smallest extent group
1582 * to avoid waste of space
1584 exts_group_tmp = (*exts_group_list_head)->prev;
1585 do {
1586 if (exts_group_tmp->len + len >= filesize) {
1587 len += exts_group_tmp->len;
1588 exts_group_tmp =
1589 get_exts_group(exts_group_list_head,
1590 exts_group_tmp);
1591 if (insert_exts_group
1592 (target_exts_group_list_head,
1593 exts_group_tmp) < 0) {
1594 if (exts_group_tmp)
1595 free(exts_group_tmp);
1596 return RETURN_NG;
1598 (*ext_count)++;
1599 /* The only entry go out normally*/
1600 return RETURN_OK;
1602 exts_group_tmp = exts_group_tmp->prev;
1603 } while (exts_group_tmp !=
1604 (*exts_group_list_head)->prev);
1606 len += (*exts_group_list_head)->len;
1607 exts_group_tmp = get_exts_group(exts_group_list_head,
1608 *exts_group_list_head);
1609 if (insert_exts_group(target_exts_group_list_head,
1610 exts_group_tmp) < 0) {
1611 if (exts_group_tmp)
1612 free(exts_group_tmp);
1613 return RETURN_NG;
1615 (*ext_count)++;
1618 return RETURN_NG;
1622 * check_frag_count() - Check file frag.
1624 * @fd: the file's discriptor.
1625 * @inode: inode number from struct stat64.
1626 * @extent_count: the number of extents.
1628 int check_frag_count(int fd, unsigned long long inode, int extent_count)
1630 int ret, pos, file_extent_count;
1631 struct ext4_extents_info extents_info;
1633 /* Count file exts */
1634 memset(&extents_info, 0, sizeof(struct ext4_extents_info));
1635 file_extent_count = 0;/* Extents count of file */
1636 extents_info.ino = inode;
1637 extents_info.max_entries = DEFRAG_MAX_ENT;
1638 extents_info.entries = 0;
1639 pos = 0;
1640 ret = 0;
1642 do {
1643 extents_info.entries += pos;
1644 pos = extents_info.entries;
1645 ret = ioctl(fd, EXT4_IOC_EXTENTS_INFO, &extents_info);
1646 if (ret < 0)
1647 return RETURN_NG;
1648 file_extent_count += extents_info.entries;
1649 } while (extents_info.entries == DEFRAG_MAX_ENT && ret == 0);
1651 if (extent_count >= file_extent_count) {
1652 /* No improvment */
1653 errno = ENOSPC;
1654 return RETURN_NG;
1657 return RETURN_OK;
1661 * defrag_proc() - Reserve extent group and execute the defrag program
1663 * @fd: the file's discriptor.
1664 * @target_exts_group_head: the head of the original exts_group list.
1665 * @inode: inode number from struct stat64.
1667 int defrag_proc(int fd, exts_group_t *target_exts_group_head,
1668 unsigned long long inode)
1670 int ret = 0;
1671 int percent = 0;
1672 int blocksize = 0;
1673 int data_len = 0;
1674 struct stat64 buf;
1675 exts_group_t *exts_group;
1676 extent_t *extent;
1677 struct ext4_extents_info extents_info;
1678 struct ext4_ext_defrag_data defrag_data;
1679 extent_wrap_t *wrap_list = NULL;
1681 /* Reserve free extents */
1682 if (!target_exts_group_head)
1683 /* Fault */
1684 return RETURN_NG;
1686 /* get file size */
1687 memset(&buf, 0, sizeof(struct stat64));
1688 ret = fstat64(fd, &buf);
1689 if (ret < 0) {
1690 perror(NGMSG_FILE_INFO);
1691 return RETURN_NG;
1693 /* get block size */
1694 ret = ioctl(fd, FIGETBSZ, &blocksize);
1695 if (ret < 0) {
1696 perror(NGMSG_FILE_BLOCKSIZE);
1697 return RETURN_NG;
1699 memset(&extents_info, 0, sizeof(extents_info));
1700 memset(&defrag_data, 0, sizeof(struct ext4_ext_defrag_data));
1702 extents_info.ino = 0;
1703 exts_group = target_exts_group_head;
1704 extents_info.max_entries = DEFRAG_MAX_ENT;
1705 extents_info.ino = inode;
1706 ext4_fsblk_t data_block = 0;
1707 ext4_fsblk_t data_start = 0;
1708 defrag_data.start_offset = 0;
1710 do {
1711 extent_wrap_t *wrap;
1712 extent = exts_group->start;
1713 data_len = 0;
1714 data_start = extent->data.start;
1715 data_block = extent->data.block;
1716 do {
1717 data_len += extent->data.len;
1718 if (extent->tag != EXT4_EXT_USE) {
1719 extent->tag = EXT4_EXT_RESERVE;
1720 extent = extent->next;
1721 continue;
1723 extents_info.ino = extent->ino;
1724 extents_info.goal = fgoal;
1725 memcpy(extents_info.ext, &extent->data,
1726 sizeof(struct ext4_extent_data));
1727 wrap = malloc(sizeof(extent_wrap_t));
1728 if (!wrap)
1729 goto release_blocks;
1730 wrap->group_ext = extent;
1731 wrap->next = wrap->prev = wrap;
1732 if (move_wrap(NULL, &wrap_list, wrap) < 0) {
1733 if (wrap)
1734 free(wrap);
1735 goto release_blocks;
1737 extent = extent->next;
1738 extents_info.entries = 1;
1739 ret = ioctl(fd, EXT4_IOC_MOVE_VICTIM, &extents_info);
1740 if (ret < 0)
1741 goto release_blocks;
1742 mark_wrap(wrap_list);
1743 free_wrap_list(&wrap_list);
1744 } while (extent != exts_group->end->next);
1746 if (fsync(fd) < 0) {
1747 if (detail_flag)
1748 perror(NGMSG_FILE_SYNC);
1749 return ret;
1752 extents_info.entries = 1;
1753 extents_info.ext[0].block = data_block;
1754 extents_info.ext[0].start = data_start;
1755 extents_info.ext[0].len = exts_group->len;
1756 ret = ioctl(fd, EXT4_IOC_RESERVE_BLOCK, &extents_info);
1757 if (ret < 0) {
1758 printf("RESERVE_ERROR ret = %d\n", ret);
1759 printf("block is already used\n");
1760 goto release_blocks;
1762 ret = do_defrag(fd, exts_group, defrag_data);
1763 if (ret < 0) {
1764 printf("DEFRAG_ERROR ret = %d\n", ret);
1765 goto release_blocks;
1767 defrag_data.start_offset += ret;
1768 ret = defrag_data.start_offset;
1770 /* print process progress */
1771 if (detail_flag) {
1772 percent = ((long long)ret * blocksize * 100) /
1773 buf.st_size;
1774 if (percent > 100)
1775 percent = 100;
1776 printf("\033[79;16H\033[K progressing ====> %d%%",
1777 percent);
1778 fflush(stdout);
1781 exts_group = exts_group->next;
1782 } while (exts_group != target_exts_group_head);
1783 return ret;
1785 release_blocks:
1786 free_wrap_list(&wrap_list);
1787 ret = ioctl(fd, EXT4_IOC_BLOCK_RELEASE);
1788 if (ret < 0)
1789 return RETURN_NG;
1791 return ret;
1795 * force_defrag() - Execute the defrag program in force defrag mode.
1797 * @fd: the file's descriptor.
1798 * @buf: a pointer of the struct stat64.
1799 * @blocksize: block size in byte.
1801 int force_defrag(int fd, const struct stat64 *buf, int blocksize)
1803 int ret = 0;
1804 int exts = 0;
1805 int maxlen = 0;
1806 unsigned int gnumber;
1807 unsigned long filesize;
1808 unsigned long long istart, iend;
1809 ext4_fsblk_t bstart, bend;
1810 extent_t *extlist_head = NULL;
1811 exts_group_t *exts_group_list_head, *exts_group_list_target_head;
1812 struct ext4_group_data_info ext4_group_data;
1814 exts_group_list_head = exts_group_list_target_head = NULL;
1816 /* Get group info */
1817 memset(&ext4_group_data, 0, sizeof(struct ext4_group_data_info));
1818 if (ioctl(fd, EXT4_IOC_GROUP_INFO, &ext4_group_data) < 0)
1819 return RETURN_NG;
1821 gnumber = (buf->st_ino - 1) / ext4_group_data.s_inodes_per_group;
1822 istart = gnumber * ext4_group_data.s_inodes_per_group;
1823 iend = istart + ext4_group_data.s_inodes_per_group - 1;
1824 bstart = gnumber * ext4_group_data.s_blocks_per_group;
1825 bend = bstart + ext4_group_data.s_blocks_per_group - 1;
1827 /* Compute filesize in block */
1828 filesize = (buf->st_size + blocksize - 1) / blocksize;
1830 /* Get used extents in the block group */
1831 ret = get_used_extent(fd, &extlist_head, istart, iend, bstart, bend);
1832 if (ret == RETURN_NG)
1833 goto freelist;
1835 /* Get free extents in the group */
1836 ret = get_free_extent(fd, buf->st_ino,
1837 ext4_group_data.s_blocks_per_group, &extlist_head);
1838 if (ret == RETURN_NG)
1839 goto freelist;
1841 /* All space in this group is used by other groups' inodes */
1842 if (extlist_head == NULL) {
1843 ret = RETURN_NG;
1844 goto freelist;
1847 /* Get continuous region(extents group) */
1848 ret = join_extents(extlist_head, &exts_group_list_target_head,
1849 &exts_group_list_head, filesize, &maxlen);
1850 if (ret == RETURN_NG)
1851 goto freelist;
1852 if (ret == CHECK_FRAG_COUNT) {
1853 exts = 1;
1854 goto frag_check;
1857 if (maxlen < filesize) {
1858 /* No enough space */
1859 errno = ENOSPC;
1860 ret = RETURN_NG;
1861 goto freelist;
1864 if (!exts_group_list_head) {
1865 ret = RETURN_NG;
1866 goto freelist;
1869 /* Find target extents group */
1870 ret = find_exts_group(&exts, filesize, &exts_group_list_head,
1871 &exts_group_list_target_head);
1872 if (ret == RETURN_NG)
1873 goto freelist;
1875 frag_check:
1876 /* Check file extent count*/
1877 ret = check_frag_count(fd, buf->st_ino, exts);
1878 if (ret == RETURN_NG)
1879 goto freelist;
1881 /* Reserve extent group and execute the defrag program */
1882 ret = defrag_proc(fd, exts_group_list_target_head, buf->st_ino);
1884 freelist:
1885 free_exts_group(exts_group_list_target_head);
1886 free_exts_group(exts_group_list_head);
1887 free_ext(extlist_head);
1888 return ret;
1892 To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
1893 the body of a message to majordomo@vger.kernel.org
1894 More majordomo info at http://vger.kernel.org/majordomo-info.html