common/rc: improve checks for YP/NIS
[xfstests-dev.git] / src / log-writes / log-writes.c
1 #include <linux/fs.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <sys/ioctl.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <errno.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include "log-writes.h"
12
13 int log_writes_verbose = 0;
14
15 /*
16  * @log: the log to free.
17  *
18  * This will close any open fd's the log has and free up its memory.
19  */
20 void log_free(struct log *log)
21 {
22         if (log->replayfd >= 0)
23                 close(log->replayfd);
24         if (log->logfd >= 0)
25                 close(log->logfd);
26         free(log);
27 }
28
29 static int discard_range(struct log *log, u64 start, u64 len)
30 {
31         u64 range[2] = { start, len };
32
33         if (ioctl(log->replayfd, BLKDISCARD, &range) < 0) {
34                 if (log_writes_verbose)
35                         printf("replay device doesn't support discard, "
36                                "switching to writing zeros\n");
37                 log->flags |= LOG_DISCARD_NOT_SUPP;
38         }
39         return 0;
40 }
41
42 static int zero_range(struct log *log, u64 start, u64 len)
43 {
44         u64 bufsize = len;
45         ssize_t ret;
46         char *buf = NULL;
47
48         if (log->max_zero_size < len) {
49                 if (log_writes_verbose)
50                         printf("discard len %llu larger than max %llu\n",
51                                (unsigned long long)len,
52                                (unsigned long long)log->max_zero_size);
53                 return 0;
54         }
55
56         while (!buf) {
57                 buf = malloc(bufsize);
58                 if (!buf)
59                         bufsize >>= 1;
60                 if (!bufsize) {
61                         fprintf(stderr, "Couldn't allocate zero buffer");
62                         return -1;
63                 }
64         }
65
66         memset(buf, 0, bufsize);
67         while (len) {
68                 if (len < bufsize)
69                         bufsize = len;
70
71                 ret = pwrite(log->replayfd, buf, bufsize, start);
72                 if (ret != bufsize) {
73                         fprintf(stderr, "Error zeroing file: %d\n", errno);
74                         free(buf);
75                         return -1;
76                 }
77                 len -= ret;
78                 start += ret;
79         }
80         free(buf);
81         return 0;
82 }
83
84 /*
85  * @log: the log we are replaying.
86  * @entry: the discard entry.
87  *
88  * Discard the given length.  If the device supports discard we will call that
89  * ioctl, otherwise we will write 0's to emulate discard.  If the discard size
90  * is larger than log->max_zero_size then we will simply skip the zero'ing if
91  * the drive doesn't support discard.
92  */
93 int log_discard(struct log *log, struct log_write_entry *entry)
94 {
95         u64 start = le64_to_cpu(entry->sector) * log->sectorsize;
96         u64 size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
97         u64 max_chunk = 1 * 1024 * 1024 * 1024;
98
99         if (log->flags & LOG_IGNORE_DISCARD)
100                 return 0;
101
102         while (size) {
103                 u64 len = size > max_chunk ? max_chunk : size;
104                 int ret;
105
106                 /*
107                  * Do this check first in case it is our first discard, that way
108                  * if we return EOPNOTSUPP we will fall back to the 0 method
109                  * automatically.
110                  */
111                 if (!(log->flags & LOG_DISCARD_NOT_SUPP))
112                         ret = discard_range(log, start, len);
113                 if (log->flags & LOG_DISCARD_NOT_SUPP)
114                         ret = zero_range(log, start, len);
115                 if (ret)
116                         return -1;
117                 size -= len;
118                 start += len;
119         }
120         return 0;
121 }
122
123 /*
124  * @log: the log we are replaying.
125  * @entry: entry to be replayed.
126  *
127  * @return: 0 if we should replay the entry, > 0 if we should skip it.
128  *
129  * Should we skip the entry in our log or replay onto the replay device.
130  */
131 int log_should_skip(struct log *log, struct log_write_entry *entry)
132 {
133         u64 sector = le64_to_cpu(entry->sector);
134         u64 nr_sectors = le64_to_cpu(entry->nr_sectors);
135
136         if (!nr_sectors)
137                 return 0;
138         if (sector + nr_sectors <= log->start_sector ||
139             sector > log->end_sector)
140                 return 1;
141         return 0;
142 }
143
144 /*
145  * @entry: entry to be replayed.
146  *
147  * @return: 1 if the entry is sane, 0 if it is invalid.
148  *
149  * Check if this is a sane log entry.
150  */
151 int log_entry_valid(struct log_write_entry *entry)
152 {
153         u64 flags = le64_to_cpu(entry->flags);
154
155         /* Suspect all zeroes entry */
156         if (!flags && !entry->nr_sectors)
157                 return 0;
158         /* Suspect non zero padded entry */
159         if (flags != LOG_MARK_FLAG && entry->data[0] != 0)
160                 return 0;
161         return 1;
162 }
163
164 /*
165  * @log: the log we are replaying.
166  * @entry: where we put the entry.
167  * @read_data: read the entry data as well, entry must be log->sectorsize sized
168  * if this is set.
169  *
170  * @return: 0 if we replayed, 1 if we are at the end, -1 if there was an error.
171  *
172  * Replay the next entry in our log onto the replay device.
173  */
174 int log_replay_next_entry(struct log *log, struct log_write_entry *entry,
175                           int read_data)
176 {
177         u64 size;
178         u64 flags;
179         size_t read_size = read_data ? log->sectorsize :
180                 sizeof(struct log_write_entry);
181         char *buf;
182         ssize_t ret;
183         off_t offset;
184         int skip = 0;
185
186         if (log->cur_entry >= log->nr_entries)
187                 return 1;
188
189         ret = read(log->logfd, entry, read_size);
190         if (ret != read_size) {
191                 fprintf(stderr, "Error reading entry: %d\n", errno);
192                 return -1;
193         }
194         if (!log_entry_valid(entry)) {
195                 fprintf(stderr, "Malformed entry @%llu\n",
196                                 log->cur_pos / log->sectorsize);
197                 return -1;
198         }
199         log->cur_entry++;
200
201         size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
202         if (read_size < log->sectorsize) {
203                 log->cur_pos = lseek(log->logfd,
204                         log->sectorsize - sizeof(struct log_write_entry), SEEK_CUR);
205                 if (log->cur_pos == (off_t)-1) {
206                         fprintf(stderr, "Error seeking in log: %d\n", errno);
207                         return -1;
208                 }
209         } else {
210                 log->cur_pos += read_size;
211         }
212
213         skip = log_should_skip(log, entry);
214         if (log_writes_verbose > 1 || (log_writes_verbose && !skip)) {
215                 printf("%s %d@%llu: sector %llu, size %llu, flags %llu\n",
216                        skip ? "skipping" : "replaying",
217                        (int)log->cur_entry - 1, log->cur_pos / log->sectorsize,
218                        (unsigned long long)le64_to_cpu(entry->sector),
219                        (unsigned long long)size,
220                        (unsigned long long)le64_to_cpu(entry->flags));
221         }
222         if (!size)
223                 return 0;
224
225         flags = le64_to_cpu(entry->flags);
226         if (flags & LOG_DISCARD_FLAG)
227                 return log_discard(log, entry);
228
229         if (skip) {
230                 log->cur_pos = lseek(log->logfd, size, SEEK_CUR);
231                 if (log->cur_pos == (off_t)-1) {
232                         fprintf(stderr, "Error seeking in log: %d\n", errno);
233                         return -1;
234                 }
235                 return 0;
236         }
237
238         buf = malloc(size);
239         if (!buf) {
240                 fprintf(stderr, "Error allocating buffer %llu entry %llu\n", (unsigned long long)size, (unsigned long long)log->cur_entry - 1);
241                 return -1;
242         }
243
244         ret = read(log->logfd, buf, size);
245         if (ret != size) {
246                 fprintf(stderr, "Error reading data: %d\n", errno);
247                 free(buf);
248                 return -1;
249         }
250         log->cur_pos += size;
251
252         offset = le64_to_cpu(entry->sector) * log->sectorsize;
253         ret = pwrite(log->replayfd, buf, size, offset);
254         free(buf);
255         if (ret != size) {
256                 fprintf(stderr, "Error writing data: %d\n", errno);
257                 return -1;
258         }
259
260         return 0;
261 }
262
263 /*
264  * @log: the log we are manipulating.
265  * @entry_num: the entry we want.
266  *
267  * Seek to the given entry in the log, starting at 0 and ending at
268  * log->nr_entries - 1.
269  */
270 int log_seek_entry(struct log *log, u64 entry_num)
271 {
272         u64 i = 0;
273
274         if (entry_num >= log->nr_entries) {
275                 fprintf(stderr, "Invalid entry number\n");
276                 return -1;
277         }
278
279         /* Skip the first sector containing the log super block */
280         log->cur_pos = lseek(log->logfd, log->sectorsize, SEEK_SET);
281         if (log->cur_pos == (off_t)-1) {
282                 fprintf(stderr, "Error seeking in file: %d\n", errno);
283                 return -1;
284         }
285
286         log->cur_entry = 0;
287         for (i = 0; i < entry_num; i++) {
288                 struct log_write_entry entry;
289                 ssize_t ret;
290                 off_t seek_size;
291                 u64 flags;
292
293                 ret = read(log->logfd, &entry, sizeof(entry));
294                 if (ret != sizeof(entry)) {
295                         fprintf(stderr, "Error reading entry: %d\n", errno);
296                         return -1;
297                 }
298                 if (!log_entry_valid(&entry)) {
299                         fprintf(stderr, "Malformed entry @%llu\n",
300                                         log->cur_pos / log->sectorsize);
301                         return -1;
302                 }
303                 if (log_writes_verbose > 1)
304                         printf("seek entry %d@%llu: %llu, size %llu, flags %llu\n",
305                                (int)i, log->cur_pos / log->sectorsize,
306                                (unsigned long long)le64_to_cpu(entry.sector),
307                                (unsigned long long)le64_to_cpu(entry.nr_sectors),
308                                (unsigned long long)le64_to_cpu(entry.flags));
309                 flags = le64_to_cpu(entry.flags);
310                 seek_size = log->sectorsize - sizeof(entry);
311                 if (!(flags & LOG_DISCARD_FLAG))
312                         seek_size += le64_to_cpu(entry.nr_sectors) *
313                                 log->sectorsize;
314                 log->cur_pos = lseek(log->logfd, seek_size, SEEK_CUR);
315                 if (log->cur_pos == (off_t)-1) {
316                         fprintf(stderr, "Error seeking in file: %d\n", errno);
317                         return -1;
318                 }
319                 log->cur_entry++;
320         }
321
322         return 0;
323 }
324
325 /*
326  * @log: the log we are manipulating.
327  * @entry: the entry we read.
328  * @read_data: read the extra data for the entry, your entry must be
329  * log->sectorsize large.
330  *
331  * @return: 1 if we hit the end of the log, 0 we got the next entry, < 0 if
332  * there was an error.
333  *
334  * Seek to the next entry in the log.
335  */
336 int log_seek_next_entry(struct log *log, struct log_write_entry *entry,
337                         int read_data)
338 {
339         size_t read_size = read_data ? log->sectorsize :
340                 sizeof(struct log_write_entry);
341         u64 flags;
342         ssize_t ret;
343
344         if (log->cur_entry >= log->nr_entries)
345                 return 1;
346
347         ret = read(log->logfd, entry, read_size);
348         if (ret != read_size) {
349                 fprintf(stderr, "Error reading entry: %d\n", errno);
350                 return -1;
351         }
352         if (!log_entry_valid(entry)) {
353                 fprintf(stderr, "Malformed entry @%llu\n",
354                                 log->cur_pos / log->sectorsize);
355                 return -1;
356         }
357         log->cur_entry++;
358
359         if (read_size < log->sectorsize) {
360                 log->cur_pos = lseek(log->logfd,
361                         log->sectorsize - sizeof(struct log_write_entry), SEEK_CUR);
362                 if (log->cur_pos == (off_t)-1) {
363                         fprintf(stderr, "Error seeking in log: %d\n", errno);
364                         return -1;
365                 }
366         } else {
367                 log->cur_pos += read_size;
368         }
369         if (log_writes_verbose > 1)
370                 printf("seek entry %d@%llu: %llu, size %llu, flags %llu\n",
371                        (int)log->cur_entry - 1, log->cur_pos / log->sectorsize,
372                        (unsigned long long)le64_to_cpu(entry->sector),
373                        (unsigned long long)le64_to_cpu(entry->nr_sectors),
374                        (unsigned long long)le64_to_cpu(entry->flags));
375
376         flags = le64_to_cpu(entry->flags);
377         read_size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
378         if (!read_size || (flags & LOG_DISCARD_FLAG))
379                 return 0;
380
381         log->cur_pos = lseek(log->logfd, read_size, SEEK_CUR);
382         if (log->cur_pos == (off_t)-1) {
383                 fprintf(stderr, "Error seeking in log: %d\n", errno);
384                 return -1;
385         }
386
387         return 0;
388 }
389
390 /*
391  * @logfile: the file that contains the write log.
392  * @replayfile: the file/device to replay onto, can be NULL.
393  *
394  * Opens a logfile and makes sure it is valid and returns a struct log.
395  */
396 struct log *log_open(char *logfile, char *replayfile)
397 {
398         struct log *log;
399         struct log_write_super super;
400         ssize_t ret;
401
402         log = malloc(sizeof(struct log));
403         if (!log) {
404                 fprintf(stderr, "Couldn't alloc log\n");
405                 return NULL;
406         }
407
408         log->replayfd = -1;
409
410         log->logfd = open(logfile, O_RDONLY);
411         if (log->logfd < 0) {
412                 fprintf(stderr, "Couldn't open log %s: %d\n", logfile,
413                         errno);
414                 log_free(log);
415                 return NULL;
416         }
417
418         if (replayfile) {
419                 log->replayfd = open(replayfile, O_WRONLY);
420                 if (log->replayfd < 0) {
421                         fprintf(stderr, "Couldn't open replay file %s: %d\n",
422                                 replayfile, errno);
423                         log_free(log);
424                         return NULL;
425                 }
426         }
427
428         ret = read(log->logfd, &super, sizeof(struct log_write_super));
429         if (ret < sizeof(struct log_write_super)) {
430                 fprintf(stderr, "Error reading super: %d\n", errno);
431                 log_free(log);
432                 return NULL;
433         }
434
435         if (le64_to_cpu(super.magic) != WRITE_LOG_MAGIC) {
436                 fprintf(stderr, "Magic doesn't match\n");
437                 log_free(log);
438                 return NULL;
439         }
440
441         if (le64_to_cpu(super.version) != WRITE_LOG_VERSION) {
442                 fprintf(stderr, "Version mismatch, wanted %d, have %d\n",
443                         WRITE_LOG_VERSION, (int)le64_to_cpu(super.version));
444                 log_free(log);
445                 return NULL;
446         }
447
448         log->sectorsize = le32_to_cpu(super.sectorsize);
449         log->nr_entries = le64_to_cpu(super.nr_entries);
450         log->max_zero_size = 128 * 1024 * 1024;
451
452         log->cur_pos = lseek(log->logfd, log->sectorsize - sizeof(super), SEEK_CUR);
453         if (log->cur_pos == (off_t) -1) {
454                 fprintf(stderr, "Error seeking to first entry: %d\n", errno);
455                 log_free(log);
456                 return NULL;
457         }
458         log->cur_entry = 0;
459
460         return log;
461 }