3 * Compact list of files sequentially
6 * find /etc -type f > etc_list
7 * fallocate -l100M /etc/.tmp_donor_file
8 * cat etc_list | ./e4defrag /etc/.tmp_donor_file
9 * unlink /etc/.tmp_donor_file
19 #include <linux/types.h>
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
24 #include <sys/param.h>
31 #ifndef EXT4_IOC_MOVE_EXT
33 __s32 reserved; /* original file descriptor */
34 __u32 donor_fd; /* donor file descriptor */
35 __u64 orig_start; /* logical start offset in block for orig */
36 __u64 donor_start; /* logical start offset in block for donor */
37 __u64 len; /* block length to be moved */
38 __u64 moved_len; /* moved block length */
41 #define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent)
51 static int ignore_error = 0;
52 static int verbose = 0;
53 static unsigned blk_per_pg;
54 static unsigned blk_sz;
57 static int do_defrag_one(int fd, char *name,__u64 start, __u64 len, struct donor_info *donor)
60 struct move_extent mv_ioc;
64 assert(donor->length >= len);
65 /* EXT4_IOC_MOVE_EXT requires both files has same offset inside page */
66 donor->offset += (blk_per_pg - (donor->offset & (blk_per_pg -1)) +
67 (start & (blk_per_pg -1))) & (blk_per_pg -1);
69 mv_ioc.donor_fd = donor->fd;
70 mv_ioc.orig_start = start;
71 mv_ioc.donor_start = donor->offset;
75 printf("%s %s start:%lld len:%lld donor [%lld, %lld]\n", __func__,
76 name, (unsigned long long) start,
77 (unsigned long long) len,
78 (unsigned long long)donor->offset,
79 (unsigned long long)donor->length);
85 ret = ioctl(fd, EXT4_IOC_MOVE_EXT, &mv_ioc);
87 printf("process %s it:%d start:%lld len:%lld donor:%lld,"
88 "moved:%lld ret:%d errno:%d\n",
90 (unsigned long long) mv_ioc.orig_start,
91 (unsigned long long) mv_ioc.len,
92 (unsigned long long)mv_ioc.donor_start,
93 (unsigned long long)mv_ioc.moved_len,
97 printf("%s EXT4_IOC_MOVE_EXT failed err:%d\n",
99 if (errno != EBUSY || !retry--)
103 /* Nothing to swap */
104 if (mv_ioc.moved_len == 0)
107 assert(mv_ioc.len >= mv_ioc.moved_len);
108 mv_ioc.len -= mv_ioc.moved_len;
109 mv_ioc.orig_start += mv_ioc.moved_len;
110 mv_ioc.donor_start += mv_ioc.moved_len;
111 moved += mv_ioc.moved_len;
112 } while (mv_ioc.len);
114 if (ret && ignore_error &&
115 (errno == EBUSY || errno == ENODATA || errno == EOPNOTSUPP))
117 donor->length -= moved;
118 donor->offset += moved;
124 printf("Usage: -f donor_file [-o donor_offset] [-v] [-i]\n"
126 "\t\t -i: ignore errors\n");
129 int main(int argc, char **argv)
135 struct donor_info donor;
140 char * donor_name = NULL;
143 while ((c = getopt(argc, argv, "f:o:iv")) != -1) {
146 donor.offset = atol(optarg);
155 donor_name = (optarg);
167 donor.fd = open(donor_name, O_RDWR);
169 perror("can not open donor file");
172 if (fstat(donor.fd, &st)) {
173 perror("can not stat donor fd");
176 donor.length = st.st_size / st.st_blksize;
178 donor.offset /= st.st_blksize;
179 blk_sz = st.st_blksize;
180 blk_per_pg = sysconf(_SC_PAGESIZE) / blk_sz;
183 printf("Init donor %s off:%lld len:%lld bsz:%lu\n",
184 donor_name, donor.offset, donor.length, st.st_blksize);
186 while ((read = getline(&line, &len, stdin)) != -1) {
188 if (line[read -1] == '\n')
191 fd = open(line, O_RDWR);
194 printf("Can not open %s errno:%d\n", line, errno);
202 perror("Can not stat ");
210 if (st.st_size && st.st_blocks) {
211 ret = do_defrag_one(fd, line, 0,
212 (st.st_size + blk_sz-1)/ blk_sz,
214 if (ret && !ignore_error)