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
20 #include <linux/fiemap.h>
21 #include <linux/types.h>
23 #include <sys/ioctl.h>
24 #include <sys/types.h>
26 #include <sys/param.h>
33 #ifndef EXT4_IOC_MOVE_EXT
35 __s32 reserved; /* original file descriptor */
36 __u32 donor_fd; /* donor file descriptor */
37 __u64 orig_start; /* logical start offset in block for orig */
38 __u64 donor_start; /* logical start offset in block for donor */
39 __u64 len; /* block length to be moved */
40 __u64 moved_len; /* moved block length */
43 #define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent)
45 #define EXTENT_MAX_COUNT 512
54 static int ignore_error = 0;
55 static int verbose = 0;
56 static int do_sparse = 0;
57 static unsigned blk_per_pg;
58 static unsigned blk_sz;
60 static int do_defrag_range(int fd, char *name,__u64 start, __u64 len,
61 struct donor_info *donor)
64 struct move_extent mv_ioc;
68 assert(donor->length >= len);
69 /* EXT4_IOC_MOVE_EXT requires both files has same offset inside page */
70 donor->offset += (blk_per_pg - (donor->offset & (blk_per_pg -1)) +
71 (start & (blk_per_pg -1))) & (blk_per_pg -1);
73 mv_ioc.donor_fd = donor->fd;
74 mv_ioc.orig_start = start;
75 mv_ioc.donor_start = donor->offset;
79 printf("%s %s start:%lld len:%lld donor [%lld, %lld]\n", __func__,
80 name, (unsigned long long) start,
81 (unsigned long long) len,
82 (unsigned long long)donor->offset,
83 (unsigned long long)donor->length);
89 ret = ioctl(fd, EXT4_IOC_MOVE_EXT, &mv_ioc);
91 printf("process %s it:%d start:%lld len:%lld donor:%lld,"
92 "moved:%lld ret:%d errno:%d\n",
94 (unsigned long long) mv_ioc.orig_start,
95 (unsigned long long) mv_ioc.len,
96 (unsigned long long)mv_ioc.donor_start,
97 (unsigned long long)mv_ioc.moved_len,
101 printf("%s EXT4_IOC_MOVE_EXT failed err:%d\n",
103 if (errno != EBUSY || !retry--)
107 /* Nothing to swap */
108 if (mv_ioc.moved_len == 0)
111 assert(mv_ioc.len >= mv_ioc.moved_len);
112 mv_ioc.len -= mv_ioc.moved_len;
113 mv_ioc.orig_start += mv_ioc.moved_len;
114 mv_ioc.donor_start += mv_ioc.moved_len;
115 moved += mv_ioc.moved_len;
116 } while (mv_ioc.len);
118 if (ret && ignore_error &&
119 (errno == EBUSY || errno == ENODATA || errno == EOPNOTSUPP))
121 donor->length -= moved;
122 donor->offset += moved;
126 static int do_defrag_sparse(int fd, char *name,__u64 start, __u64 len,
127 struct donor_info *donor)
130 struct fiemap *fiemap_buf = NULL;
131 struct fiemap_extent *ext_buf = NULL;
133 fiemap_buf = malloc(EXTENT_MAX_COUNT * sizeof(struct fiemap_extent)
134 + sizeof(struct fiemap));
135 if (fiemap_buf == NULL) {
136 fprintf(stderr, "%s Can not allocate memory\n", __func__);
139 ext_buf = fiemap_buf->fm_extents;
140 memset(fiemap_buf, 0, sizeof(struct fiemap));
141 fiemap_buf->fm_flags |= FIEMAP_FLAG_SYNC;
142 fiemap_buf->fm_extent_count = EXTENT_MAX_COUNT;
147 fiemap_buf->fm_start = start * blk_sz;
148 fiemap_buf->fm_length = len * blk_sz;
149 ret = ioctl(fd, FS_IOC_FIEMAP, fiemap_buf);
150 if (ret < 0 || fiemap_buf->fm_mapped_extents == 0) {
151 fprintf(stderr, "%s Can get extent info for %s ret:%d mapped:%d",
152 __func__, name, ret, fiemap_buf->fm_mapped_extents);
155 for (i = 0; i < fiemap_buf->fm_mapped_extents; i++) {
156 ret = do_defrag_range(fd, name,
157 ext_buf[i].fe_logical / blk_sz,
158 ext_buf[i].fe_length / blk_sz,
163 next = (ext_buf[fiemap_buf->fm_mapped_extents -1].fe_logical +
164 ext_buf[fiemap_buf->fm_mapped_extents -1].fe_length) /
166 if (next < start + len) {
172 } while (fiemap_buf->fm_mapped_extents == EXTENT_MAX_COUNT &&
173 !(ext_buf[EXTENT_MAX_COUNT-1].fe_flags & FIEMAP_EXTENT_LAST));
181 printf("Usage: -f donor_file [-o donor_offset] [-v] [-i]\n"
183 "\t\t -s: enable sparse file optimization\n"
184 "\t\t -i: ignore errors\n");
187 int main(int argc, char **argv)
193 struct donor_info donor;
198 char * donor_name = NULL;
202 while ((c = getopt(argc, argv, "f:o:isv")) != -1) {
205 donor.offset = atol(optarg);
217 donor_name = (optarg);
229 donor.fd = open(donor_name, O_RDWR);
231 perror("can not open donor file");
234 if (fstat(donor.fd, &st)) {
235 perror("can not stat donor fd");
238 donor.length = st.st_size / st.st_blksize;
240 donor.offset /= st.st_blksize;
241 blk_sz = st.st_blksize;
242 blk_per_pg = sysconf(_SC_PAGESIZE) / blk_sz;
245 printf("Init donor %s off:%lld len:%lld bsz:%lu\n",
246 donor_name, donor.offset, donor.length, st.st_blksize);
248 while ((read = getline(&line, &len, stdin)) != -1) {
250 if (line[read -1] == '\n')
253 fd = open(line, O_RDWR);
256 printf("Can not open %s errno:%d\n", line, errno);
264 perror("Can not stat ");
272 if (!(st.st_size && st.st_blocks))
275 eof_blk = (st.st_size + blk_sz-1) / blk_sz;
277 ret = do_defrag_sparse(fd, line, 0, eof_blk, &donor);
279 ret = do_defrag_range(fd, line, 0, eof_blk, &donor);
280 if (ret && !ignore_error)