generic: Test that SEEK_HOLE can find a punched hole
[xfstests-dev.git] / src / seek_sanity_test.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2011 Oracle.  All rights reserved.
4  * Copyright (C) 2011 Red Hat.  All rights reserved.
5  */
6
7 #define _XOPEN_SOURCE 500
8 #define _FILE_OFFSET_BITS 64
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <sys/vfs.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <stdlib.h>
18 #include <assert.h>
19 #include "global.h"
20
21 #ifndef SEEK_DATA
22 #define SEEK_DATA      3
23 #define SEEK_HOLE      4
24 #endif
25
26 static blksize_t alloc_size;
27 int allow_default_behavior = 1;
28 int default_behavior = 0;
29 int unwritten_extents = 0;
30 int punch_hole = 0;
31 char *base_file_path;
32
33 static void get_file_system(int fd)
34 {
35         struct statfs buf;
36
37         if (!fstatfs(fd, &buf)) {
38                 fprintf(stdout, "File system magic#: 0x%lx\n",
39                                 (unsigned long int)buf.f_type);
40         }
41 }
42
43 static int get_io_sizes(int fd)
44 {
45         off_t pos = 0, offset = 1;
46         struct stat buf;
47         int shift, ret;
48
49         ret = fstat(fd, &buf);
50         if (ret) {
51                 fprintf(stderr, "  ERROR %d: Failed to find io blocksize\n",
52                         errno);
53                 return ret;
54         }
55
56         /* st_blksize is typically also the allocation size */
57         alloc_size = buf.st_blksize;
58
59         /* try to discover the actual alloc size */
60         while (pos == 0 && offset < alloc_size) {
61                 offset <<= 1;
62                 ftruncate(fd, 0);
63                 pwrite(fd, "a", 1, offset);
64                 pos = lseek(fd, 0, SEEK_DATA);
65                 if (pos == -1)
66                         goto fail;
67         }
68
69         /* bisect */
70         shift = offset >> 2;
71         while (shift && offset < alloc_size) {
72                 ftruncate(fd, 0);
73                 pwrite(fd, "a", 1, offset);
74                 pos = lseek(fd, 0, SEEK_DATA);
75                 if (pos == -1)
76                         goto fail;
77                 offset += pos ? -shift : shift;
78                 shift >>= 1;
79         }
80         if (!shift)
81                 offset += pos ? 0 : 1;
82         alloc_size = offset;
83         fprintf(stdout, "Allocation size: %ld\n", alloc_size);
84         return 0;
85
86 fail:
87         fprintf(stderr, "Kernel does not support llseek(2) extension "
88                 "SEEK_DATA. Aborting.\n");
89         return -1;
90 }
91
92 #define do_free(x)     do { if(x) free(x); } while(0);
93
94 static void *do_malloc(size_t size)
95 {
96        void *buf;
97
98        buf = malloc(size);
99        if (!buf)
100                fprintf(stderr, "  ERROR: Unable to allocate %ld bytes\n",
101                        (long)size);
102
103        return buf;
104 }
105
106 static int do_truncate(int fd, off_t length)
107 {
108        int ret;
109
110        ret = ftruncate(fd, length);
111        if (ret)
112                fprintf(stderr, "  ERROR %d: Failed to extend file "
113                        "to %ld bytes\n", errno, (long)length);
114        return ret;
115 }
116
117 static int do_fallocate(int fd, off_t offset, off_t length, int mode)
118 {
119         int ret;
120
121         ret = fallocate(fd, mode, offset, length);
122         if (ret)
123                 fprintf(stderr, "  ERROR %d: Failed to %s of %ld bytes\n",
124                         errno, (mode & FALLOC_FL_PUNCH_HOLE) ? "punch hole" :
125                         "preallocate space", (long) length);
126
127         return ret;
128 }
129
130 /*
131  * Synchnorize all dirty pages in the file range starting from
132  * offset to nbytes length.
133  */
134 static int do_sync_dirty_pages(int fd, off64_t offset, off64_t nbytes)
135 {
136         int ret;
137
138         ret = sync_file_range(fd, offset, nbytes, SYNC_FILE_RANGE_WRITE);
139         if (ret)
140                 fprintf(stderr, "  ERROR %d: Failed to sync out dirty "
141                         "pages\n", errno);
142
143         return ret;
144 }
145
146 static ssize_t do_pwrite(int fd, const void *buf, size_t count, off_t offset)
147 {
148         ssize_t ret, written = 0;
149
150         while (count > written) {
151                 ret = pwrite(fd, buf + written, count - written, offset + written);
152                 if (ret < 0) {
153                         /* Don't warn about too large file. It's fs dependent. */
154                         if (errno == EFBIG)
155                                 return ret;
156                         fprintf(stderr, "  ERROR %d: Failed to write %ld "
157                                 "bytes\n", errno, (long)count);
158                         return ret;
159                 }
160                 written += ret;
161         }
162
163         return 0;
164 }
165
166 #define do_close(x)     do { if ((x) > -1) close(x); } while(0);
167
168 static int do_create(const char *filename)
169 {
170         int fd;
171
172         fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644);
173         if (fd < 0)
174                 fprintf(stderr, " ERROR %d: Failed to create file '%s'\n",
175                         errno, filename);
176
177         return fd;
178 }
179
180 static int do_lseek(int testnum, int subtest, int fd, off_t filsz, int origin,
181                     off_t set, off_t exp)
182 {
183         off_t pos, exp2;
184         int x, ret;
185
186         assert(!(origin != SEEK_HOLE && origin != SEEK_DATA));
187
188         /*
189          * The file pointer can be set to different values depending
190          * on the implementation. For SEEK_HOLE, EOF could be a valid
191          * value. For SEEK_DATA, supplied offset could be the valid
192          * value.
193          */
194         exp2 = exp;
195         if (origin == SEEK_HOLE && exp2 != -1)
196                 exp2 = filsz;
197         if (origin == SEEK_DATA && default_behavior && set < filsz)
198                 exp2 = set;
199
200         pos = lseek(fd, set, origin);
201
202         if (pos == -1 && exp == -1) {
203                 x = fprintf(stdout, "%02d.%02d %s expected -1 with errno %d, got %d. ",
204                             testnum, subtest,
205                             (origin == SEEK_HOLE) ? "SEEK_HOLE" : "SEEK_DATA",
206                             -ENXIO, -errno);
207                 ret = !(errno == ENXIO);
208         } else {
209
210                 x = fprintf(stdout, "%02d.%02d %s expected %lld or %lld, got %lld. ",
211                             testnum, subtest,
212                             (origin == SEEK_HOLE) ? "SEEK_HOLE" : "SEEK_DATA",
213                             (long long)exp, (long long)exp2, (long long)pos);
214                 ret = !(pos == exp || pos == exp2);
215         }
216
217         fprintf(stdout, "%*s\n", (70 - x), ret ? "FAIL" : "succ");
218
219         return ret;
220 }
221
222 static int huge_file_test(int fd, int testnum, off_t filsz)
223 {
224         char *buf = NULL;
225         int bufsz = alloc_size * 16;    /* XFS seems to round allocated size */
226         off_t off = filsz - bufsz;
227         int ret = -1;
228
229         buf = do_malloc(bufsz);
230         if (!buf)
231                 goto out;
232         memset(buf, 'a', bufsz);
233
234         /* |- DATA -|- HUGE HOLE -|- DATA -| */
235         ret = do_pwrite(fd, buf, bufsz, 0);
236         if (ret)
237                 goto out;
238         ret = do_pwrite(fd, buf, bufsz, off);
239         if (ret) {
240                 /*
241                  * Report success. Filesystem just cannot handle so large
242                  * offsets and correctly reports it.
243                  */
244                 if (errno == EFBIG) {
245                         fprintf(stdout, "Test skipped as fs doesn't support so large files.\n");
246                         ret = 0;
247                 }
248                 goto out;
249         }
250
251         /* offset at the beginning */
252         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
253         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
254         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
255         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
256
257         /* offset around eof */
258         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, off, off + bufsz);
259         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, off, off);
260         ret += do_lseek(testnum,  7, fd, filsz, SEEK_DATA, off + 1, off + 1);
261         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, off - bufsz, off);
262
263 out:
264         do_free(buf);
265         return ret;
266 }
267
268 /*
269  * Make sure hole size is properly reported when punched in the middle of a file
270  */
271 static int test21(int fd, int testnum)
272 {
273         char *buf = NULL;
274         int bufsz, filsz;
275         int ret = 0;
276
277         if (!punch_hole) {
278                 fprintf(stdout, "Test skipped as fs doesn't support punch hole.\n");
279                 goto out;
280         }
281
282         if (default_behavior) {
283                 fprintf(stdout, "Test skipped as fs doesn't support seeking a punched hole.\n");
284                 goto out;
285         }
286
287         bufsz = alloc_size * 3;
288         buf = do_malloc(bufsz);
289         if (!buf) {
290                 ret = -1;
291                 goto out;
292         }
293         memset(buf, 'a', bufsz);
294
295         ret = do_pwrite(fd, buf, bufsz, 0);
296         if (ret)
297                 goto out;
298
299         filsz = bufsz;
300         ret = do_fallocate(fd, alloc_size, alloc_size,
301                            FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE);
302         if (ret < 0)
303                 goto out;
304
305         ret += do_lseek(testnum, 1, fd, filsz, SEEK_DATA, 0, 0);
306         ret += do_lseek(testnum, 2, fd, filsz, SEEK_HOLE, 0, alloc_size);
307         ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, alloc_size, alloc_size * 2);
308 out:
309         if (buf)
310                 free(buf);
311         return ret;
312 }
313
314 /*
315  * Make sure hole size is properly reported when starting in the middle of a
316  * hole in ext? doubly indirect tree
317  */
318 static int test20(int fd, int testnum)
319 {
320         int ret = -1;
321         char *buf = NULL;
322         loff_t bufsz, filsz;
323
324         bufsz = alloc_size;
325         buf = do_malloc(bufsz);
326         if (!buf)
327                 goto out;
328         memset(buf, 'a', bufsz);
329
330         /* Magic size in the middle of ext[23] triple indirect tree */
331         filsz = (12 + bufsz / 4 + 8 * bufsz / 4 * bufsz / 4 + 2 * bufsz / 4 + 5) * bufsz;
332         ret = do_pwrite(fd, buf, bufsz, filsz - bufsz);
333         if (ret) {
334                 /*
335                  * Report success. Filesystem just cannot handle so large
336                  * offsets and correctly reports it.
337                  */
338                 if (errno == EFBIG) {
339                         fprintf(stdout, "Test skipped as fs doesn't support so large files.\n");
340                         ret = 0;
341                 }
342                 goto out;
343         }
344
345         /* Offset inside ext[23] indirect block */
346         ret += do_lseek(testnum, 1, fd, filsz, SEEK_DATA, 14 * bufsz, filsz - bufsz);
347         /* Offset inside ext[23] doubly indirect block */
348         ret += do_lseek(testnum, 2, fd, filsz, SEEK_DATA, (12 + 2 * bufsz / 4) * bufsz, filsz - bufsz);
349         /* Offsets inside ext[23] triply indirect block */
350         ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA,
351                 (12 + bufsz / 4 + bufsz / 4 * bufsz / 4 + 3 * bufsz / 4 + 5) * bufsz, filsz - bufsz);
352         ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA,
353                 (12 + bufsz / 4 + 7 * bufsz / 4 * bufsz / 4 + 5 * bufsz / 4) * bufsz, filsz - bufsz);
354         ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA,
355                 (12 + bufsz / 4 + 8 * bufsz / 4 * bufsz / 4 + bufsz / 4 + 11) * bufsz, filsz - bufsz);
356 out:
357         if (buf)
358                 free(buf);
359         return ret;
360 }
361 /*
362  * Make sure hole size is properly reported when starting in the middle of a
363  * hole in ext? indirect tree
364  */
365 static int test19(int fd, int testnum)
366 {
367         int ret = -1;
368         char *buf = NULL;
369         int bufsz, filsz;
370
371         bufsz = alloc_size;
372         buf = do_malloc(bufsz);
373         if (!buf)
374                 goto out;
375         memset(buf, 'a', bufsz);
376
377         /* Magic size just beyond ext[23] indirect tree size */
378         filsz = (12 + bufsz / 4 + 1) * bufsz;
379         ret = do_pwrite(fd, buf, bufsz, filsz - bufsz);
380         if (ret)
381                 goto out;
382
383         ret += do_lseek(testnum, 1, fd, filsz, SEEK_DATA, bufsz, filsz - bufsz);
384         ret += do_lseek(testnum, 2, fd, filsz, SEEK_DATA, 12*bufsz, filsz - bufsz);
385         ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, (12 + bufsz / 4 - 8)*bufsz, filsz - bufsz);
386 out:
387         if (buf)
388                 free(buf);
389         return ret;
390 }
391
392 /* Make sure we get ENXIO if we pass in a negative offset. */
393 static int test18(int fd, int testnum)
394 {
395         int ret = 0;
396
397         /* file size doesn't matter in this test, set to 0 */
398         ret += do_lseek(testnum, 1, fd, 0, SEEK_HOLE, -1, -1);
399         ret += do_lseek(testnum, 2, fd, 0, SEEK_DATA, -1, -1);
400
401         return ret;
402 }
403
404 static int test17(int fd, int testnum)
405 {
406         char *buf = NULL;
407         int pagesz = sysconf(_SC_PAGE_SIZE);
408         int bufsz, filsz;
409         int ret = 0;
410
411         if (!unwritten_extents) {
412                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
413                 goto out;
414         }
415
416         if (pagesz < 4 * alloc_size) {
417                 fprintf(stdout, "Test skipped as page size (%d) is less than "
418                         "four times allocation size (%d).\n",
419                         pagesz, (int)alloc_size);
420                 goto out;
421         }
422         bufsz = alloc_size;
423         filsz = 3 * bufsz;
424
425         buf = do_malloc(bufsz);
426         if (!buf) {
427                 ret = -1;
428                 goto out;
429         }
430         memset(buf, 'a', bufsz);
431
432         ret = do_fallocate(fd, 0, filsz, 0);
433         if (ret < 0)
434                 goto out;
435
436         ret = do_pwrite(fd, buf, bufsz, 0);
437         if (ret)
438                 goto out;
439
440         ret = do_pwrite(fd, buf, bufsz, 2 * bufsz);
441         if (ret)
442                 goto out;
443
444         ret += do_lseek(testnum,  1, fd, filsz, SEEK_DATA, 0, 0);
445         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 0, bufsz);
446         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 1, 1);
447         ret += do_lseek(testnum,  4, fd, filsz, SEEK_HOLE, 1, bufsz);
448         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, bufsz, 2 * bufsz);
449         ret += do_lseek(testnum,  6, fd, filsz, SEEK_HOLE, bufsz, bufsz);
450         ret += do_lseek(testnum,  7, fd, filsz, SEEK_DATA, bufsz + 1, 2 * bufsz);
451         ret += do_lseek(testnum,  8, fd, filsz, SEEK_HOLE, bufsz + 1, bufsz + 1);
452         ret += do_lseek(testnum,  9, fd, filsz, SEEK_DATA, 2 * bufsz, 2 * bufsz);
453         ret += do_lseek(testnum, 10, fd, filsz, SEEK_HOLE, 2 * bufsz, 3 * bufsz);
454         ret += do_lseek(testnum, 11, fd, filsz, SEEK_DATA, 2 * bufsz + 1, 2 * bufsz + 1);
455         ret += do_lseek(testnum, 12, fd, filsz, SEEK_HOLE, 2 * bufsz + 1, 3 * bufsz);
456
457         filsz += bufsz;
458         ret += do_fallocate(fd, 0, filsz, 0);
459
460         ret += do_lseek(testnum, 13, fd, filsz, SEEK_DATA, 3 * bufsz, -1);
461         ret += do_lseek(testnum, 14, fd, filsz, SEEK_HOLE, 3 * bufsz, 3 * bufsz);
462         ret += do_lseek(testnum, 15, fd, filsz, SEEK_DATA, 3 * bufsz + 1, -1);
463         ret += do_lseek(testnum, 16, fd, filsz, SEEK_HOLE, 3 * bufsz + 1, 3 * bufsz + 1);
464
465 out:
466         do_free(buf);
467         return ret;
468 }
469
470 /*
471  * test file with unwritten extents, having non-contiguous dirty pages in
472  * the unwritten extent.
473  */
474 static int test16(int fd, int testnum)
475 {
476         int ret = 0;
477         char *buf = NULL;
478         int bufsz = sysconf(_SC_PAGE_SIZE);
479         int filsz = 4 << 20;
480
481         if (!unwritten_extents) {
482                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
483                 goto out;
484         }
485
486         /* HOLE - unwritten DATA in dirty page */
487         /* Each unit is bufsz */
488         buf = do_malloc(bufsz);
489         if (!buf)
490                 goto out;
491         memset(buf, 'a', bufsz);
492
493         /* preallocate 4M space to file */
494         ret = do_fallocate(fd, 0, filsz, 0);
495         if (ret < 0)
496                 goto out;
497
498         ret = do_pwrite(fd, buf, bufsz, 0);
499         if (ret)
500                 goto out;
501
502         ret = do_pwrite(fd, buf, bufsz, filsz/2);
503         if (ret)
504                 goto out;
505
506         /* offset at the beginning */
507         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
508         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
509         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
510         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
511         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, bufsz, filsz/2);
512         ret += do_lseek(testnum,  6, fd, filsz, SEEK_HOLE, filsz/2,
513                         filsz/2 + bufsz);
514
515 out:
516         do_free(buf);
517         return ret;
518 }
519
520 /*
521  * test file with unwritten extents, having page just after end of unwritten
522  * extent.
523  */
524 static int test15(int fd, int testnum)
525 {
526         int ret = 0;
527         char *buf = NULL;
528         int bufsz = sysconf(_SC_PAGE_SIZE);
529         int filsz = 4 << 20;
530
531         if (!unwritten_extents) {
532                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
533                 goto out;
534         }
535
536         /* HOLE - unwritten DATA in dirty page */
537         /* Each unit is bufsz */
538         buf = do_malloc(bufsz);
539         if (!buf)
540                 goto out;
541         memset(buf, 'a', bufsz);
542
543         /* preallocate 4M space to file */
544         ret = do_fallocate(fd, 0, filsz, 0);
545         if (ret < 0)
546                 goto out;
547
548         ret = do_pwrite(fd, buf, bufsz, 0);
549         if (ret)
550                 goto out;
551
552         /* One page written just after end of unwritten extent... */
553         ret = do_pwrite(fd, buf, bufsz, filsz);
554         if (ret)
555                 goto out;
556
557         /* update file size */
558         filsz += bufsz;
559
560         /* offset at the beginning */
561         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
562         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
563         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
564         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
565         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, bufsz, filsz - bufsz);
566
567 out:
568         do_free(buf);
569         return ret;
570 }
571
572 /*
573  * test file with unwritten extents, only have pagevec worth of dirty pages
574  * in page cache, a hole and then another page.
575  */
576 static int test14(int fd, int testnum)
577 {
578         int ret = 0;
579         char *buf = NULL;
580         int bufsz = sysconf(_SC_PAGE_SIZE) * 14;
581         int filsz = 4 << 20;
582
583         if (!unwritten_extents) {
584                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
585                 goto out;
586         }
587
588         /* HOLE - unwritten DATA in dirty page */
589         /* Each unit is bufsz */
590         buf = do_malloc(bufsz);
591         if (!buf)
592                 goto out;
593         memset(buf, 'a', bufsz);
594
595         /* preallocate 4M space to file */
596         ret = do_fallocate(fd, 0, filsz, 0);
597         if (ret < 0)
598                 goto out;
599
600         ret = do_pwrite(fd, buf, bufsz, 0);
601         if (ret)
602                 goto out;
603
604         ret = do_pwrite(fd, buf, bufsz, 3 * bufsz);
605         if (ret)
606                 goto out;
607
608         /* offset at the beginning */
609         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
610         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
611         ret += do_lseek(testnum,  3, fd, filsz, SEEK_HOLE, 3 * bufsz, 4 * bufsz);
612         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 0, 0);
613         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, 1, 1);
614         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, bufsz, 3 * bufsz);
615
616 out:
617         do_free(buf);
618         return ret;
619 }
620
621 /*
622  * test file with unwritten extents, only have pagevec worth of dirty pages
623  * in page cache.
624  */
625 static int test13(int fd, int testnum)
626 {
627         int ret = 0;
628         char *buf = NULL;
629         int bufsz = sysconf(_SC_PAGE_SIZE) * 14;
630         int filsz = 4 << 20;
631
632         if (!unwritten_extents) {
633                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
634                 goto out;
635         }
636
637         /* HOLE - unwritten DATA in dirty page */
638         /* Each unit is bufsz */
639         buf = do_malloc(bufsz);
640         if (!buf)
641                 goto out;
642         memset(buf, 'a', bufsz);
643
644         /* preallocate 4M space to file */
645         ret = do_fallocate(fd, 0, filsz, 0);
646         if (ret < 0)
647                 goto out;
648
649         ret = do_pwrite(fd, buf, bufsz, 0);
650         if (ret)
651                 goto out;
652
653         /* offset at the beginning */
654         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
655         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
656         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
657         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
658
659 out:
660         do_free(buf);
661         return ret;
662 }
663
664 /*
665  * Test huge file to check for overflows of block counts due to usage of
666  * 32-bit types.
667  */
668 static int test12(int fd, int testnum)
669 {
670         return huge_file_test(fd, testnum,
671                                 ((long long)alloc_size << 32) + (1 << 20));
672 }
673
674 /*
675  * Test huge file to check for overflows of block counts due to usage of
676  * signed types
677  */
678 static int test11(int fd, int testnum)
679 {
680         return huge_file_test(fd, testnum,
681                                 ((long long)alloc_size << 31) + (1 << 20));
682 }
683
684 /* Test an 8G file to check for offset overflows at 1 << 32 */
685 static int test10(int fd, int testnum)
686 {
687         return huge_file_test(fd, testnum, 8ULL << 30);
688 }
689
690 /*
691  * test file with unwritten extents, have both dirty and
692  * writeback pages in page cache.
693  */
694 static int test09(int fd, int testnum)
695 {
696         int ret = 0;
697         char *buf = NULL;
698         int bufsz = alloc_size;
699         int filsz = bufsz * 100 + bufsz;
700
701         if (!unwritten_extents) {
702                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
703                 goto out;
704         }
705
706         /*
707          * HOLE - unwritten DATA in dirty page - HOLE -
708          * unwritten DATA in writeback page
709          */
710
711         /* Each unit is bufsz */
712         buf = do_malloc(bufsz);
713         if (!buf)
714                 goto out;
715         memset(buf, 'a', bufsz);
716
717         /* preallocate 8M space to file */
718         ret = do_fallocate(fd, 0, filsz, 0);
719         if (ret < 0)
720                 goto out;
721
722         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
723         if (!ret) {
724                 ret = do_pwrite(fd, buf, bufsz, bufsz * 100);
725                 if (ret)
726                         goto out;
727         }
728
729         /*
730          * Sync out dirty pages from bufsz * 100, this will convert
731          * the dirty page to writeback.
732          */
733         ret = do_sync_dirty_pages(fd, bufsz * 100, 0);
734         if (ret)
735                 goto out;
736
737         /* offset at the beginning */
738         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
739         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
740         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
741         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
742
743 out:
744         do_free(buf);
745         return ret;
746 }
747
748 /* test file with unwritten extent, only have writeback page */
749 static int test08(int fd, int testnum)
750 {
751         int ret = 0;
752         char *buf = NULL;
753         int bufsz = alloc_size;
754         int filsz = bufsz * 10 + bufsz;
755
756         if (!unwritten_extents) {
757                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
758                 goto out;
759         }
760
761         /* HOLE - unwritten DATA in writeback page */
762         /* Each unit is bufsz */
763         buf = do_malloc(bufsz);
764         if (!buf)
765                 goto out;
766         memset(buf, 'a', bufsz);
767
768         /* preallocate 4M space to file */
769         ret = do_fallocate(fd, 0, filsz, 0);
770         if (ret < 0)
771                 goto out;
772
773         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
774         if (ret)
775                 goto out;
776
777         /* Sync out all file */
778         ret = do_sync_dirty_pages(fd, 0, 0);
779         if (ret)
780                 goto out;
781
782         /* offset at the beginning */
783         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
784         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
785         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
786         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
787
788 out:
789         do_free(buf);
790         return ret;
791 }
792
793 /*
794  * test file with unwritten extents, only have dirty pages
795  * in page cache.
796  */
797 static int test07(int fd, int testnum)
798 {
799         int ret = 0;
800         char *buf = NULL;
801         int bufsz = alloc_size;
802         int filsz = bufsz * 10 + bufsz;
803
804         if (!unwritten_extents) {
805                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
806                 goto out;
807         }
808
809         /* HOLE - unwritten DATA in dirty page */
810         /* Each unit is bufsz */
811         buf = do_malloc(bufsz);
812         if (!buf)
813                 goto out;
814         memset(buf, 'a', bufsz);
815
816         /* preallocate 4M space to file */
817         ret = do_fallocate(fd, 0, filsz, 0);
818         if (ret < 0)
819                 goto out;
820
821         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
822         if (ret)
823                 goto out;
824
825         /* offset at the beginning */
826         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
827         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
828         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
829         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
830
831 out:
832         do_free(buf);
833         return ret;
834 }
835
836 /* test hole data hole data */
837 static int test06(int fd, int testnum)
838 {
839         int ret = -1;
840         char *buf = NULL;
841         int bufsz = alloc_size;
842         int filsz = bufsz * 4;
843         int off;
844
845         /* HOLE - DATA - HOLE - DATA */
846         /* Each unit is bufsz */
847
848         buf = do_malloc(bufsz);
849         if (!buf)
850                 goto out;
851
852         memset(buf, 'a', bufsz);
853
854         ret = do_pwrite(fd, buf, bufsz, bufsz);
855         if (!ret)
856                 do_pwrite(fd, buf, bufsz, bufsz * 3);
857         if (ret)
858                 goto out;
859
860         /* offset at the beginning */
861         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
862         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
863         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz);
864         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz);
865
866         /* offset around first hole-data boundary */
867         off = bufsz;
868         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, off - 1, off - 1);
869         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, off - 1, off);
870         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, off,     bufsz * 2);
871         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, off,     off);
872         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, off + 1, bufsz * 2);
873         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, off + 1, off + 1);
874
875         /* offset around data-hole boundary */
876         off = bufsz * 2;
877         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, off - 1, off);
878         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, off - 1, off - 1);
879         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, off,     off);
880         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, off,     bufsz * 3);
881         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, off + 1, off + 1);
882         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, off + 1, bufsz * 3);
883
884         /* offset around second hole-data boundary */
885         off = bufsz * 3;
886         ret += do_lseek(testnum, 17, fd, filsz, SEEK_HOLE, off - 1, off - 1);
887         ret += do_lseek(testnum, 18, fd, filsz, SEEK_DATA, off - 1, off);
888         ret += do_lseek(testnum, 19, fd, filsz, SEEK_HOLE, off,     filsz);
889         ret += do_lseek(testnum, 20, fd, filsz, SEEK_DATA, off,     off);
890         ret += do_lseek(testnum, 21, fd, filsz, SEEK_HOLE, off + 1, filsz);
891         ret += do_lseek(testnum, 22, fd, filsz, SEEK_DATA, off + 1, off + 1);
892
893         /* offset around the end of file */
894         off = filsz;
895         ret += do_lseek(testnum, 23, fd, filsz, SEEK_HOLE, off - 1, filsz);
896         ret += do_lseek(testnum, 24, fd, filsz, SEEK_DATA, off - 1, filsz - 1);
897         ret += do_lseek(testnum, 25, fd, filsz, SEEK_HOLE, off, -1);
898         ret += do_lseek(testnum, 26, fd, filsz, SEEK_DATA, off, -1);
899         ret += do_lseek(testnum, 27, fd, filsz, SEEK_HOLE, off + 1, -1);
900         ret += do_lseek(testnum, 28, fd, filsz, SEEK_DATA, off + 1, -1);
901
902 out:
903         do_free(buf);
904         return ret;
905 }
906
907 /* test file with data at the beginning and a hole at the end */
908 static int test05(int fd, int testnum)
909 {
910         int ret = -1;
911         char *buf = NULL;
912         int bufsz = alloc_size;
913         int filsz = bufsz * 4;
914
915         /* |- DATA -|- HOLE -|- HOLE -|- HOLE -| */
916
917         buf = do_malloc(bufsz);
918         if (!buf)
919                 goto out;
920         memset(buf, 'a', bufsz);
921
922         ret = do_truncate(fd, filsz);
923         if (!ret)
924                 ret = do_pwrite(fd, buf, bufsz, 0);
925         if (ret)
926                 goto out;
927
928         /* offset at the beginning */
929
930         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
931         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
932
933         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
934         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
935
936         /* offset around data-hole boundary */
937         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, bufsz - 1, bufsz);
938         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
939
940         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, bufsz,     bufsz);
941         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, bufsz,     -1);
942         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, bufsz + 1, bufsz + 1);
943         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, bufsz + 1, -1);
944
945         /* offset around eof */
946         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, filsz - 1, filsz - 1);
947         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, filsz - 1, -1);
948         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, filsz,     -1);
949         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, filsz,     -1);
950         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, filsz + 1, -1);
951         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, filsz + 1, -1);
952 out:
953         do_free(buf);
954         return ret;
955 }
956 /* test hole begin and data end */
957 static int test04(int fd, int testnum)
958 {
959         int ret;
960         char *buf = "ABCDEFGH";
961         int bufsz, holsz, filsz;
962
963         bufsz = strlen(buf);
964         holsz = alloc_size * 2;
965         filsz = holsz + bufsz;
966
967         /* |- HOLE -|- HOLE -|- DATA -| */
968
969         ret = do_pwrite(fd, buf, bufsz, holsz);
970         if (ret)
971                 goto out;
972
973         /* offset at the beginning */
974         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
975         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
976         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, holsz);
977         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, holsz);
978         /* offset around hole-data boundary */
979         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, holsz - 1, holsz - 1);
980         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, holsz - 1, holsz);
981         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, holsz,     filsz);
982         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, holsz,     holsz);
983         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, holsz + 1, filsz);
984         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, holsz + 1, holsz + 1);
985
986         /* offset around eof */
987         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, filsz - 1, filsz);
988         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, filsz - 1, filsz - 1);
989         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, filsz,     -1);
990         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, filsz,     -1);
991         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, filsz + 1, -1);
992         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, filsz + 1, -1);
993 out:
994         return ret;
995 }
996
997 /* test a larger full file */
998 static int test03(int fd, int testnum)
999 {
1000         char *buf = NULL;
1001         int bufsz = alloc_size * 2 + 100;
1002         int filsz = bufsz;
1003         int ret = -1;
1004
1005         buf = do_malloc(bufsz);
1006         if (!buf)
1007                 goto out;
1008         memset(buf, 'a', bufsz);
1009
1010         ret = do_pwrite(fd, buf, bufsz, 0);
1011         if (ret)
1012                 goto out;
1013
1014         /* offset at the beginning */
1015         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
1016         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
1017         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
1018         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
1019
1020         /* offset around eof */
1021         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, bufsz - 1, bufsz);
1022         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
1023         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, bufsz,     -1);
1024         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, bufsz,     -1);
1025         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, bufsz + 1, -1);
1026         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, bufsz + 1, -1);
1027
1028 out:
1029         do_free(buf);
1030         return ret;
1031 }
1032
1033 /* test tiny full file */
1034 static int test02(int fd, int testnum)
1035 {
1036         int ret;
1037         char buf[] = "ABCDEFGH";
1038         int bufsz, filsz;
1039
1040         bufsz = strlen(buf);
1041         filsz = bufsz;
1042
1043         /* |- DATA -| */
1044
1045         ret = do_pwrite(fd, buf, bufsz, 0);
1046         if (ret)
1047                 goto out;
1048
1049         ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, filsz);
1050         ret += do_lseek(testnum, 2, fd, filsz, SEEK_DATA, 0, 0);
1051         ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 1, 1);
1052         ret += do_lseek(testnum, 4, fd, filsz, SEEK_HOLE, bufsz - 1, filsz);
1053         ret += do_lseek(testnum, 5, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
1054         ret += do_lseek(testnum, 6, fd, filsz, SEEK_HOLE, bufsz,     -1);
1055         ret += do_lseek(testnum, 7, fd, filsz, SEEK_DATA, bufsz,     -1);
1056         ret += do_lseek(testnum, 8, fd, filsz, SEEK_HOLE, bufsz + 1, -1);
1057         ret += do_lseek(testnum, 9, fd, filsz, SEEK_DATA, bufsz + 1, -1);
1058
1059 out:
1060         return ret;
1061 }
1062
1063 /* test empty file */
1064 static int test01(int fd, int testnum)
1065 {
1066         int ret = 0;
1067
1068         ret += do_lseek(testnum, 1, fd, 0, SEEK_DATA, 0, -1);
1069         ret += do_lseek(testnum, 2, fd, 0, SEEK_HOLE, 0, -1);
1070         ret += do_lseek(testnum, 3, fd, 0, SEEK_HOLE, 1, -1);
1071
1072         return ret;
1073 }
1074
1075 struct testrec {
1076        int     test_num;
1077        int     (*test_func)(int fd, int testnum);
1078        char    *test_desc;
1079 };
1080
1081 struct testrec seek_tests[] = {
1082        {  1, test01, "Test empty file" },
1083        {  2, test02, "Test a tiny full file" },
1084        {  3, test03, "Test a larger full file" },
1085        {  4, test04, "Test file hole at beg, data at end" },
1086        {  5, test05, "Test file data at beg, hole at end" },
1087        {  6, test06, "Test file hole data hole data" },
1088        {  7, test07, "Test file with unwritten extents, only have dirty pages" },
1089        {  8, test08, "Test file with unwritten extents, only have unwritten pages" },
1090        {  9, test09, "Test file with unwritten extents, have both dirty && unwritten pages" },
1091        { 10, test10, "Test a huge file for offset overflow" },
1092        { 11, test11, "Test a huge file for block number signed" },
1093        { 12, test12, "Test a huge file for block number overflow" },
1094        { 13, test13, "Test file with unwritten extents, only have pagevec dirty pages" },
1095        { 14, test14, "Test file with unwritten extents, small hole after pagevec dirty pages" },
1096        { 15, test15, "Test file with unwritten extents, page after unwritten extent" },
1097        { 16, test16, "Test file with unwritten extents, non-contiguous dirty pages" },
1098        { 17, test17, "Test file with unwritten extents, data-hole-data inside page" },
1099        { 18, test18, "Test file with negative SEEK_{HOLE,DATA} offsets" },
1100        { 19, test19, "Test file SEEK_DATA from middle of a large hole" },
1101        { 20, test20, "Test file SEEK_DATA from middle of a huge hole" },
1102        { 21, test21, "Test file SEEK_HOLE that was created by PUNCH_HOLE" },
1103 };
1104
1105 static int run_test(struct testrec *tr)
1106 {
1107         int ret = 0, fd = -1;
1108         char filename[255];
1109
1110         snprintf(filename, sizeof(filename), "%s%02d", base_file_path, tr->test_num);
1111
1112         fd = do_create(filename);
1113         if (fd > -1) {
1114                 printf("%02d. %-50s\n", tr->test_num, tr->test_desc);
1115                 ret = tr->test_func(fd, tr->test_num);
1116                 printf("\n");
1117         }
1118
1119         do_close(fd);
1120         return ret;
1121 }
1122
1123 static int test_basic_support(void)
1124 {
1125         int ret = -1, fd;
1126         off_t pos;
1127         char *buf = NULL;
1128         int bufsz, filsz;
1129
1130         fd = do_create(base_file_path);
1131         if (fd == -1)
1132                 goto out;
1133
1134         get_file_system(fd);
1135
1136         ret = get_io_sizes(fd);
1137         if (ret)
1138                 goto out;
1139
1140         ftruncate(fd, 0);
1141         bufsz = alloc_size * 2;
1142         filsz = bufsz * 2;
1143
1144         buf = do_malloc(bufsz);
1145         if (!buf)
1146                 goto out;
1147         memset(buf, 'a', bufsz);
1148
1149         /* File with 2 allocated blocks.... */
1150         ret = do_pwrite(fd, buf, bufsz, 0);
1151         if (ret)
1152                 goto out;
1153
1154         /* followed by a hole... */
1155         ret = do_truncate(fd, filsz);
1156         if (ret)
1157                 goto out;
1158
1159         /* Is SEEK_DATA and SEEK_HOLE supported in the kernel? */
1160         pos = lseek(fd, 0, SEEK_HOLE);
1161         if (pos == -1) {
1162                 fprintf(stderr, "Kernel does not support llseek(2) extension "
1163                         "SEEK_HOLE. Aborting.\n");
1164                 ret = -1;
1165                 goto out;
1166         }
1167
1168         if (pos == filsz) {
1169                 default_behavior = 1;
1170                 fprintf(stderr, "File system supports the default behavior.\n");
1171         }
1172
1173         if (default_behavior && !allow_default_behavior) {
1174                 fprintf(stderr, "Default behavior is not allowed. Aborting.\n");
1175                 ret = -1;
1176                 goto out;
1177         }
1178
1179         ftruncate(fd, 0);
1180         if (fallocate(fd, 0, 0, alloc_size * 2) == -1) {
1181                 if (errno == EOPNOTSUPP)
1182                         fprintf(stderr, "File system does not support fallocate.\n");
1183                 else {
1184                         fprintf(stderr, "ERROR %d: Failed to preallocate "
1185                                 "space to %ld bytes. Aborting.\n", errno, (long) alloc_size);
1186                         ret = -1;
1187                 }
1188                 goto out;
1189         } else if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
1190                              0, alloc_size) == -1) {
1191                 fprintf(stderr, "File system does not support punch hole.\n");
1192         } else {
1193                 punch_hole = 1;
1194         }
1195
1196         pos = lseek(fd, 0, SEEK_DATA);
1197         if (pos == 0) {
1198                 fprintf(stderr, "File system does not support unwritten extents.\n");
1199                 goto out;
1200         }
1201         unwritten_extents = 1;
1202
1203         printf("\n");
1204
1205 out:
1206         do_free(buf);
1207         do_close(fd);
1208         return ret;
1209 }
1210
1211 void usage(char *cmd)
1212 {
1213         fprintf(stdout, "Usage: %s [-tf] [-s <starttest>] [-e <endtest>] base_file_path\n", cmd);
1214         exit(1);
1215 }
1216
1217 int main(int argc, char **argv)
1218 {
1219         int ret = -1;
1220         int i = 0;
1221         int opt;
1222         int check_support = 0;
1223         int numtests = sizeof(seek_tests) / sizeof(struct testrec);
1224         int teststart, testend;
1225
1226         /*
1227          * First twelve tests are used by generic/285. To run these is the
1228          * default. Further tests are run e.g. by generic/436. They are not
1229          * run by default in order to not regress older kernels.
1230          */
1231         teststart = 1;
1232         testend = 12;
1233
1234         while ((opt = getopt(argc, argv, "tfs:e:")) != -1) {
1235                 switch (opt) {
1236                 case 't':
1237                         check_support++;
1238                         break;
1239                 case 'f':
1240                         allow_default_behavior = 0;
1241                         break;
1242                 case 's':
1243                         teststart = strtol(optarg, NULL, 10);
1244                         if (teststart <= 0 || teststart > numtests) {
1245                                 fprintf(stdout, "Invalid starting test: %s\n",
1246                                         optarg);
1247                                 usage(argv[0]);
1248                         }
1249                         break;
1250                 case 'e':
1251                         testend = strtol(optarg, NULL, 10);
1252                         if (testend <= 0 || testend > numtests) {
1253                                 fprintf(stdout, "Invalid final test: %s\n",
1254                                         optarg);
1255                                 usage(argv[0]);
1256                         }
1257                         break;
1258                 default:
1259                         usage(argv[0]);
1260                 }
1261         }
1262
1263         /* should be exactly one arg left, the filename */
1264         if (optind != argc - 1)
1265                 usage(argv[0]);
1266
1267         if (teststart > testend) {
1268                 fprintf(stdout, "Starting test is larger than final test!\n");
1269                 usage(argv[0]);
1270         }
1271
1272         base_file_path = (char *)strdup(argv[optind]);
1273
1274         ret = test_basic_support();
1275         if (ret || check_support)
1276                 goto out;
1277
1278         for (i = 0; i < numtests; ++i) {
1279                 if (seek_tests[i].test_num >= teststart &&
1280                     seek_tests[i].test_num <= testend) {
1281                         ret = run_test(&seek_tests[i]);
1282                         if (ret)
1283                                 break;
1284                 }
1285         }
1286
1287 out:
1288         free(base_file_path);
1289         return ret;
1290 }