src/e4compact: handle various block_size correctly
[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/types.h>
20 #include <fcntl.h>
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/param.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <unistd.h>
30
31 #ifndef EXT4_IOC_MOVE_EXT
32 struct move_extent {
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 */
39 };
40
41 #define EXT4_IOC_MOVE_EXT      _IOWR('f', 15, struct move_extent)
42 #endif
43
44 struct donor_info
45 {
46         int fd;
47         __u64 offset;
48         __u64 length;
49 };
50
51 static int ignore_error = 0;
52 static int verbose = 0;
53 static unsigned blk_per_pg;
54 static unsigned blk_sz;
55
56
57 static int do_defrag_one(int fd, char *name,__u64 start, __u64 len, struct donor_info *donor)
58 {
59         int ret, retry;
60         struct move_extent mv_ioc;
61         __u64 moved = 0;
62         int i = 0;
63
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);
68
69         mv_ioc.donor_fd = donor->fd;
70         mv_ioc.orig_start = start;
71         mv_ioc.donor_start = donor->offset;
72         mv_ioc.len = len;
73
74         if (verbose)
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);
80         retry= 3;
81         do {
82                 i++;
83                 errno = 0;
84                 mv_ioc.moved_len = 0;
85                 ret = ioctl(fd, EXT4_IOC_MOVE_EXT, &mv_ioc);
86                 if (verbose)
87                         printf("process %s  it:%d start:%lld len:%lld donor:%lld,"
88                                "moved:%lld ret:%d errno:%d\n",
89                                name, i,
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,
94                        ret, errno);
95                 if (ret < 0) {
96                         if (verbose)
97                                 printf("%s EXT4_IOC_MOVE_EXT failed err:%d\n",
98                                        __func__, errno);
99                         if (errno != EBUSY || !retry--)
100                                 break;
101                 } else {
102                         retry = 3;
103                         /* Nothing to swap */
104                         if (mv_ioc.moved_len == 0)
105                                 break;
106                 }
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);
113
114         if (ret && ignore_error &&
115             (errno == EBUSY || errno == ENODATA || errno == EOPNOTSUPP))
116                 ret = 0;
117         donor->length -= moved;
118         donor->offset += moved;
119         return ret;
120 }
121
122 void usage()
123 {
124         printf("Usage: -f donor_file [-o donor_offset] [-v] [-i]\n"
125                "\t\t -v: verbose\n"
126                "\t\t -i: ignore errors\n");
127 }
128
129 int main(int argc, char **argv)
130 {
131         int fd, ret = 0;
132         char *line = NULL;
133         size_t len = 0;
134         ssize_t read;
135         struct donor_info donor;
136         struct stat st;
137         extern char *optarg;
138         extern int optind;
139         int c;
140         char * donor_name = NULL;
141
142         donor.offset = 0;
143         while ((c = getopt(argc, argv, "f:o:iv")) != -1) {
144                 switch (c) {
145                 case 'o':
146                         donor.offset = atol(optarg);
147                         break;
148                 case 'i':
149                         ignore_error = 1;
150                         break;
151                 case 'v':
152                         verbose = 1;
153                         break;
154                 case 'f':
155                         donor_name = (optarg);
156                         break;
157
158                 default:
159                         usage();
160                         exit(1);
161                 }
162         }
163         if (!donor_name) {
164                 usage();
165                 exit(1);
166         }
167         donor.fd = open(donor_name, O_RDWR);
168         if (donor.fd < 0) {
169                 perror("can not open donor file");
170                 exit(1);
171         }
172         if (fstat(donor.fd, &st)) {
173                 perror("can not stat donor fd");
174                 exit(1);
175         }
176         donor.length = st.st_size / st.st_blksize;
177         if (donor.offset)
178                 donor.offset /= st.st_blksize;
179         blk_sz = st.st_blksize;
180         blk_per_pg = sysconf(_SC_PAGESIZE) / blk_sz;
181
182         if (verbose)
183                 printf("Init donor %s off:%lld len:%lld bsz:%lu\n",
184                        donor_name, donor.offset, donor.length, st.st_blksize);
185
186         while ((read = getline(&line, &len, stdin)) != -1) {
187
188                 if (line[read -1] == '\n')
189                         line[read -1] = 0;
190
191                 fd = open(line, O_RDWR);
192                 if (fd < 0) {
193                         if (verbose)
194                                 printf("Can not open %s errno:%d\n", line, errno);
195                         if (ignore_error)
196                                 continue;
197                         else
198                                 break;
199                 }
200                 if(fstat(fd, &st)) {
201                         if (verbose)
202                                 perror("Can not stat ");
203                         continue;
204                         if (ignore_error)
205                                 continue;
206                         else
207                                 break;
208
209                 }
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,
213                                             &donor);
214                         if (ret && !ignore_error)
215                                 break;
216                 }
217         }
218         free(line);
219         return ret;
220 }