fstests: Replace all __[u]intNN_t types with standard [u]intNN_t
[xfstests-dev.git] / src / e4compact.c
1 /* E4COMPACT
2  *
3  * Compact list of files sequentially
4  *
5  * Usage example:
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
10  */
11 #ifndef _GNU_SOURCE
12 #define _GNU_SOURCE
13 #endif
14
15 #include <assert.h>
16 #include <ctype.h>
17 #include <limits.h>
18 #include <errno.h>
19 #include <linux/fs.h>
20 #include <linux/fiemap.h>
21 #include <linux/types.h>
22 #include <fcntl.h>
23 #include <sys/ioctl.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/param.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <unistd.h>
32
33 #ifndef EXT4_IOC_MOVE_EXT
34 struct move_extent {
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 */
41 };
42
43 #define EXT4_IOC_MOVE_EXT      _IOWR('f', 15, struct move_extent)
44 #endif
45 #define EXTENT_MAX_COUNT        512
46
47 struct donor_info
48 {
49         int fd;
50         __u64 offset;
51         __u64 length;
52 };
53
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;
59
60 static int do_defrag_range(int fd, char *name,__u64 start, __u64 len,
61                            struct donor_info *donor)
62 {
63         int ret, retry;
64         struct move_extent mv_ioc;
65         __u64 moved = 0;
66         int i = 0;
67
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);
72
73         mv_ioc.donor_fd = donor->fd;
74         mv_ioc.orig_start = start;
75         mv_ioc.donor_start = donor->offset;
76         mv_ioc.len = len;
77
78         if (verbose)
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);
84         retry= 3;
85         do {
86                 i++;
87                 errno = 0;
88                 mv_ioc.moved_len = 0;
89                 ret = ioctl(fd, EXT4_IOC_MOVE_EXT, &mv_ioc);
90                 if (verbose)
91                         printf("process %s  it:%d start:%lld len:%lld donor:%lld,"
92                                "moved:%lld ret:%d errno:%d\n",
93                                name, i,
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,
98                        ret, errno);
99                 if (ret < 0) {
100                         if (verbose)
101                                 printf("%s EXT4_IOC_MOVE_EXT failed err:%d\n",
102                                        __func__, errno);
103                         if (errno != EBUSY || !retry--)
104                                 break;
105                 } else {
106                         retry = 3;
107                         /* Nothing to swap */
108                         if (mv_ioc.moved_len == 0)
109                                 break;
110                 }
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);
117
118         if (ret && ignore_error &&
119             (errno == EBUSY || errno == ENODATA || errno == EOPNOTSUPP))
120                 ret = 0;
121         donor->length -= moved;
122         donor->offset += moved;
123         return ret;
124 }
125
126 static int do_defrag_sparse(int fd, char *name,__u64 start, __u64 len,
127                             struct donor_info *donor)
128 {
129         int i, ret = 0;
130         struct fiemap   *fiemap_buf = NULL;
131         struct fiemap_extent    *ext_buf = NULL;
132
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__);
137                 return -1;
138         }
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;
143
144         do {
145                 __u64 next;
146
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);
153                         goto out;
154                 }
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,
159                                               donor);
160                         if (ret)
161                                 goto out;
162                 }
163                 next = (ext_buf[fiemap_buf->fm_mapped_extents -1].fe_logical +
164                            ext_buf[fiemap_buf->fm_mapped_extents -1].fe_length) /
165                         blk_sz;
166                 if (next <  start + len) {
167                         len -= next  - start;
168                         start = next;
169                 } else
170                         break;
171
172         } while (fiemap_buf->fm_mapped_extents == EXTENT_MAX_COUNT &&
173                  !(ext_buf[EXTENT_MAX_COUNT-1].fe_flags & FIEMAP_EXTENT_LAST));
174 out:
175         free(fiemap_buf);
176         return ret;
177 }
178
179 void usage()
180 {
181         printf("Usage: -f donor_file [-o donor_offset] [-v] [-i]\n"
182                "\t\t -v: verbose\n"
183                "\t\t -s: enable sparse file optimization\n"
184                "\t\t -i: ignore errors\n");
185 }
186
187 int main(int argc, char **argv)
188 {
189         int fd, ret = 0;
190         char *line = NULL;
191         size_t len = 0;
192         ssize_t read;
193         struct donor_info donor;
194         struct stat st;
195         extern char *optarg;
196         extern int optind;
197         int c;
198         char * donor_name = NULL;
199         __u64 eof_blk;
200
201         donor.offset = 0;
202         while ((c = getopt(argc, argv, "f:o:isv")) != -1) {
203                 switch (c) {
204                 case 'o':
205                         donor.offset = atol(optarg);
206                         break;
207                 case 'i':
208                         ignore_error = 1;
209                         break;
210                 case 'v':
211                         verbose = 1;
212                         break;
213                 case 's':
214                         do_sparse = 1;
215                         break;
216                 case 'f':
217                         donor_name = (optarg);
218                         break;
219
220                 default:
221                         usage();
222                         exit(1);
223                 }
224         }
225         if (!donor_name) {
226                 usage();
227                 exit(1);
228         }
229         donor.fd = open(donor_name, O_RDWR);
230         if (donor.fd < 0) {
231                 perror("can not open donor file");
232                 exit(1);
233         }
234         if (fstat(donor.fd, &st)) {
235                 perror("can not stat donor fd");
236                 exit(1);
237         }
238         donor.length = st.st_size / st.st_blksize;
239         if (donor.offset)
240                 donor.offset /= st.st_blksize;
241         blk_sz = st.st_blksize;
242         blk_per_pg = sysconf(_SC_PAGESIZE) / blk_sz;
243
244         if (verbose)
245                 printf("Init donor %s off:%lld len:%lld bsz:%lu\n",
246                        donor_name, donor.offset, donor.length, st.st_blksize);
247
248         while ((read = getline(&line, &len, stdin)) != -1) {
249
250                 if (line[read -1] == '\n')
251                         line[read -1] = 0;
252
253                 fd = open(line, O_RDWR);
254                 if (fd < 0) {
255                         if (verbose)
256                                 printf("Can not open %s errno:%d\n", line, errno);
257                         if (ignore_error)
258                                 continue;
259                         else
260                                 break;
261                 }
262                 if(fstat(fd, &st)) {
263                         if (verbose)
264                                 perror("Can not stat ");
265                         continue;
266                         if (ignore_error)
267                                 continue;
268                         else
269                                 break;
270
271                 }
272                 if (!(st.st_size && st.st_blocks))
273                         continue;
274
275                 eof_blk = (st.st_size + blk_sz-1) / blk_sz;
276                 if (do_sparse)
277                         ret = do_defrag_sparse(fd, line, 0, eof_blk, &donor);
278                 else
279                         ret = do_defrag_range(fd, line, 0, eof_blk, &donor);
280                 if (ret && !ignore_error)
281                         break;
282
283         }
284         free(line);
285         return ret;
286 }