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