alloc: upgrade to fallocate
[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         ftruncate(fd, 0);
399
400         ret += do_lseek(testnum, 1, fd, 0, SEEK_HOLE, -1, -1);
401         ret += do_lseek(testnum, 2, fd, 0, SEEK_DATA, -1, -1);
402         ret += do_lseek(testnum, 3, fd, 0, SEEK_HOLE, LLONG_MIN, -1);
403         ret += do_lseek(testnum, 4, fd, 0, SEEK_DATA, LLONG_MIN, -1);
404
405         return ret;
406 }
407
408 static int test17(int fd, int testnum)
409 {
410         char *buf = NULL;
411         int pagesz = sysconf(_SC_PAGE_SIZE);
412         int bufsz, filsz;
413         int ret = 0;
414
415         if (!unwritten_extents) {
416                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
417                 goto out;
418         }
419
420         if (pagesz < 4 * alloc_size) {
421                 fprintf(stdout, "Test skipped as page size (%d) is less than "
422                         "four times allocation size (%d).\n",
423                         pagesz, (int)alloc_size);
424                 goto out;
425         }
426         bufsz = alloc_size;
427         filsz = 3 * bufsz;
428
429         buf = do_malloc(bufsz);
430         if (!buf) {
431                 ret = -1;
432                 goto out;
433         }
434         memset(buf, 'a', bufsz);
435
436         ret = do_fallocate(fd, 0, filsz, 0);
437         if (ret < 0)
438                 goto out;
439
440         ret = do_pwrite(fd, buf, bufsz, 0);
441         if (ret)
442                 goto out;
443
444         ret = do_pwrite(fd, buf, bufsz, 2 * bufsz);
445         if (ret)
446                 goto out;
447
448         ret += do_lseek(testnum,  1, fd, filsz, SEEK_DATA, 0, 0);
449         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 0, bufsz);
450         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 1, 1);
451         ret += do_lseek(testnum,  4, fd, filsz, SEEK_HOLE, 1, bufsz);
452         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, bufsz, 2 * bufsz);
453         ret += do_lseek(testnum,  6, fd, filsz, SEEK_HOLE, bufsz, bufsz);
454         ret += do_lseek(testnum,  7, fd, filsz, SEEK_DATA, bufsz + 1, 2 * bufsz);
455         ret += do_lseek(testnum,  8, fd, filsz, SEEK_HOLE, bufsz + 1, bufsz + 1);
456         ret += do_lseek(testnum,  9, fd, filsz, SEEK_DATA, 2 * bufsz, 2 * bufsz);
457         ret += do_lseek(testnum, 10, fd, filsz, SEEK_HOLE, 2 * bufsz, 3 * bufsz);
458         ret += do_lseek(testnum, 11, fd, filsz, SEEK_DATA, 2 * bufsz + 1, 2 * bufsz + 1);
459         ret += do_lseek(testnum, 12, fd, filsz, SEEK_HOLE, 2 * bufsz + 1, 3 * bufsz);
460
461         filsz += bufsz;
462         ret += do_fallocate(fd, 0, filsz, 0);
463
464         ret += do_lseek(testnum, 13, fd, filsz, SEEK_DATA, 3 * bufsz, -1);
465         ret += do_lseek(testnum, 14, fd, filsz, SEEK_HOLE, 3 * bufsz, 3 * bufsz);
466         ret += do_lseek(testnum, 15, fd, filsz, SEEK_DATA, 3 * bufsz + 1, -1);
467         ret += do_lseek(testnum, 16, fd, filsz, SEEK_HOLE, 3 * bufsz + 1, 3 * bufsz + 1);
468
469 out:
470         do_free(buf);
471         return ret;
472 }
473
474 /*
475  * test file with unwritten extents, having non-contiguous dirty pages in
476  * the unwritten extent.
477  */
478 static int test16(int fd, int testnum)
479 {
480         int ret = 0;
481         char *buf = NULL;
482         int bufsz = sysconf(_SC_PAGE_SIZE);
483         int filsz = 4 << 20;
484
485         if (!unwritten_extents) {
486                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
487                 goto out;
488         }
489
490         /* HOLE - unwritten DATA in dirty page */
491         /* Each unit is bufsz */
492         buf = do_malloc(bufsz);
493         if (!buf)
494                 goto out;
495         memset(buf, 'a', bufsz);
496
497         /* preallocate 4M space to file */
498         ret = do_fallocate(fd, 0, filsz, 0);
499         if (ret < 0)
500                 goto out;
501
502         ret = do_pwrite(fd, buf, bufsz, 0);
503         if (ret)
504                 goto out;
505
506         ret = do_pwrite(fd, buf, bufsz, filsz/2);
507         if (ret)
508                 goto out;
509
510         /* offset at the beginning */
511         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
512         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
513         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
514         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
515         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, bufsz, filsz/2);
516         ret += do_lseek(testnum,  6, fd, filsz, SEEK_HOLE, filsz/2,
517                         filsz/2 + bufsz);
518
519 out:
520         do_free(buf);
521         return ret;
522 }
523
524 /*
525  * test file with unwritten extents, having page just after end of unwritten
526  * extent.
527  */
528 static int test15(int fd, int testnum)
529 {
530         int ret = 0;
531         char *buf = NULL;
532         int bufsz = sysconf(_SC_PAGE_SIZE);
533         int filsz = 4 << 20;
534
535         if (!unwritten_extents) {
536                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
537                 goto out;
538         }
539
540         /* HOLE - unwritten DATA in dirty page */
541         /* Each unit is bufsz */
542         buf = do_malloc(bufsz);
543         if (!buf)
544                 goto out;
545         memset(buf, 'a', bufsz);
546
547         /* preallocate 4M space to file */
548         ret = do_fallocate(fd, 0, filsz, 0);
549         if (ret < 0)
550                 goto out;
551
552         ret = do_pwrite(fd, buf, bufsz, 0);
553         if (ret)
554                 goto out;
555
556         /* One page written just after end of unwritten extent... */
557         ret = do_pwrite(fd, buf, bufsz, filsz);
558         if (ret)
559                 goto out;
560
561         /* update file size */
562         filsz += bufsz;
563
564         /* offset at the beginning */
565         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
566         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
567         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
568         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
569         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, bufsz, filsz - bufsz);
570
571 out:
572         do_free(buf);
573         return ret;
574 }
575
576 /*
577  * test file with unwritten extents, only have pagevec worth of dirty pages
578  * in page cache, a hole and then another page.
579  */
580 static int test14(int fd, int testnum)
581 {
582         int ret = 0;
583         char *buf = NULL;
584         int bufsz = sysconf(_SC_PAGE_SIZE) * 14;
585         int filsz = 4 << 20;
586
587         if (!unwritten_extents) {
588                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
589                 goto out;
590         }
591
592         /* HOLE - unwritten DATA in dirty page */
593         /* Each unit is bufsz */
594         buf = do_malloc(bufsz);
595         if (!buf)
596                 goto out;
597         memset(buf, 'a', bufsz);
598
599         /* preallocate 4M space to file */
600         ret = do_fallocate(fd, 0, filsz, 0);
601         if (ret < 0)
602                 goto out;
603
604         ret = do_pwrite(fd, buf, bufsz, 0);
605         if (ret)
606                 goto out;
607
608         ret = do_pwrite(fd, buf, bufsz, 3 * bufsz);
609         if (ret)
610                 goto out;
611
612         /* offset at the beginning */
613         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
614         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
615         ret += do_lseek(testnum,  3, fd, filsz, SEEK_HOLE, 3 * bufsz, 4 * bufsz);
616         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 0, 0);
617         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, 1, 1);
618         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, bufsz, 3 * bufsz);
619
620 out:
621         do_free(buf);
622         return ret;
623 }
624
625 /*
626  * test file with unwritten extents, only have pagevec worth of dirty pages
627  * in page cache.
628  */
629 static int test13(int fd, int testnum)
630 {
631         int ret = 0;
632         char *buf = NULL;
633         int bufsz = sysconf(_SC_PAGE_SIZE) * 14;
634         int filsz = 4 << 20;
635
636         if (!unwritten_extents) {
637                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
638                 goto out;
639         }
640
641         /* HOLE - unwritten DATA in dirty page */
642         /* Each unit is bufsz */
643         buf = do_malloc(bufsz);
644         if (!buf)
645                 goto out;
646         memset(buf, 'a', bufsz);
647
648         /* preallocate 4M space to file */
649         ret = do_fallocate(fd, 0, filsz, 0);
650         if (ret < 0)
651                 goto out;
652
653         ret = do_pwrite(fd, buf, bufsz, 0);
654         if (ret)
655                 goto out;
656
657         /* offset at the beginning */
658         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
659         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
660         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
661         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
662
663 out:
664         do_free(buf);
665         return ret;
666 }
667
668 /*
669  * Test huge file to check for overflows of block counts due to usage of
670  * 32-bit types.
671  */
672 static int test12(int fd, int testnum)
673 {
674         return huge_file_test(fd, testnum,
675                                 ((long long)alloc_size << 32) + (1 << 20));
676 }
677
678 /*
679  * Test huge file to check for overflows of block counts due to usage of
680  * signed types
681  */
682 static int test11(int fd, int testnum)
683 {
684         return huge_file_test(fd, testnum,
685                                 ((long long)alloc_size << 31) + (1 << 20));
686 }
687
688 /* Test an 8G file to check for offset overflows at 1 << 32 */
689 static int test10(int fd, int testnum)
690 {
691         return huge_file_test(fd, testnum, 8ULL << 30);
692 }
693
694 /*
695  * test file with unwritten extents, have both dirty and
696  * writeback pages in page cache.
697  */
698 static int test09(int fd, int testnum)
699 {
700         int ret = 0;
701         char *buf = NULL;
702         int bufsz = alloc_size;
703         int filsz = bufsz * 100 + bufsz;
704
705         if (!unwritten_extents) {
706                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
707                 goto out;
708         }
709
710         /*
711          * HOLE - unwritten DATA in dirty page - HOLE -
712          * unwritten DATA in writeback page
713          */
714
715         /* Each unit is bufsz */
716         buf = do_malloc(bufsz);
717         if (!buf)
718                 goto out;
719         memset(buf, 'a', bufsz);
720
721         /* preallocate 8M space to file */
722         ret = do_fallocate(fd, 0, filsz, 0);
723         if (ret < 0)
724                 goto out;
725
726         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
727         if (!ret) {
728                 ret = do_pwrite(fd, buf, bufsz, bufsz * 100);
729                 if (ret)
730                         goto out;
731         }
732
733         /*
734          * Sync out dirty pages from bufsz * 100, this will convert
735          * the dirty page to writeback.
736          */
737         ret = do_sync_dirty_pages(fd, bufsz * 100, 0);
738         if (ret)
739                 goto out;
740
741         /* offset at the beginning */
742         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
743         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
744         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
745         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
746
747 out:
748         do_free(buf);
749         return ret;
750 }
751
752 /* test file with unwritten extent, only have writeback page */
753 static int test08(int fd, int testnum)
754 {
755         int ret = 0;
756         char *buf = NULL;
757         int bufsz = alloc_size;
758         int filsz = bufsz * 10 + bufsz;
759
760         if (!unwritten_extents) {
761                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
762                 goto out;
763         }
764
765         /* HOLE - unwritten DATA in writeback page */
766         /* Each unit is bufsz */
767         buf = do_malloc(bufsz);
768         if (!buf)
769                 goto out;
770         memset(buf, 'a', bufsz);
771
772         /* preallocate 4M space to file */
773         ret = do_fallocate(fd, 0, filsz, 0);
774         if (ret < 0)
775                 goto out;
776
777         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
778         if (ret)
779                 goto out;
780
781         /* Sync out all file */
782         ret = do_sync_dirty_pages(fd, 0, 0);
783         if (ret)
784                 goto out;
785
786         /* offset at the beginning */
787         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
788         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
789         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
790         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
791
792 out:
793         do_free(buf);
794         return ret;
795 }
796
797 /*
798  * test file with unwritten extents, only have dirty pages
799  * in page cache.
800  */
801 static int test07(int fd, int testnum)
802 {
803         int ret = 0;
804         char *buf = NULL;
805         int bufsz = alloc_size;
806         int filsz = bufsz * 10 + bufsz;
807
808         if (!unwritten_extents) {
809                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
810                 goto out;
811         }
812
813         /* HOLE - unwritten DATA in dirty page */
814         /* Each unit is bufsz */
815         buf = do_malloc(bufsz);
816         if (!buf)
817                 goto out;
818         memset(buf, 'a', bufsz);
819
820         /* preallocate 4M space to file */
821         ret = do_fallocate(fd, 0, filsz, 0);
822         if (ret < 0)
823                 goto out;
824
825         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
826         if (ret)
827                 goto out;
828
829         /* offset at the beginning */
830         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
831         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
832         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
833         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
834
835 out:
836         do_free(buf);
837         return ret;
838 }
839
840 /* test hole data hole data */
841 static int test06(int fd, int testnum)
842 {
843         int ret = -1;
844         char *buf = NULL;
845         int bufsz = alloc_size;
846         int filsz = bufsz * 4;
847         int off;
848
849         /* HOLE - DATA - HOLE - DATA */
850         /* Each unit is bufsz */
851
852         buf = do_malloc(bufsz);
853         if (!buf)
854                 goto out;
855
856         memset(buf, 'a', bufsz);
857
858         ret = do_pwrite(fd, buf, bufsz, bufsz);
859         if (!ret)
860                 do_pwrite(fd, buf, bufsz, bufsz * 3);
861         if (ret)
862                 goto out;
863
864         /* offset at the beginning */
865         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
866         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
867         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz);
868         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz);
869
870         /* offset around first hole-data boundary */
871         off = bufsz;
872         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, off - 1, off - 1);
873         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, off - 1, off);
874         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, off,     bufsz * 2);
875         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, off,     off);
876         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, off + 1, bufsz * 2);
877         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, off + 1, off + 1);
878
879         /* offset around data-hole boundary */
880         off = bufsz * 2;
881         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, off - 1, off);
882         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, off - 1, off - 1);
883         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, off,     off);
884         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, off,     bufsz * 3);
885         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, off + 1, off + 1);
886         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, off + 1, bufsz * 3);
887
888         /* offset around second hole-data boundary */
889         off = bufsz * 3;
890         ret += do_lseek(testnum, 17, fd, filsz, SEEK_HOLE, off - 1, off - 1);
891         ret += do_lseek(testnum, 18, fd, filsz, SEEK_DATA, off - 1, off);
892         ret += do_lseek(testnum, 19, fd, filsz, SEEK_HOLE, off,     filsz);
893         ret += do_lseek(testnum, 20, fd, filsz, SEEK_DATA, off,     off);
894         ret += do_lseek(testnum, 21, fd, filsz, SEEK_HOLE, off + 1, filsz);
895         ret += do_lseek(testnum, 22, fd, filsz, SEEK_DATA, off + 1, off + 1);
896
897         /* offset around the end of file */
898         off = filsz;
899         ret += do_lseek(testnum, 23, fd, filsz, SEEK_HOLE, off - 1, filsz);
900         ret += do_lseek(testnum, 24, fd, filsz, SEEK_DATA, off - 1, filsz - 1);
901         ret += do_lseek(testnum, 25, fd, filsz, SEEK_HOLE, off, -1);
902         ret += do_lseek(testnum, 26, fd, filsz, SEEK_DATA, off, -1);
903         ret += do_lseek(testnum, 27, fd, filsz, SEEK_HOLE, off + 1, -1);
904         ret += do_lseek(testnum, 28, fd, filsz, SEEK_DATA, off + 1, -1);
905
906 out:
907         do_free(buf);
908         return ret;
909 }
910
911 /* test file with data at the beginning and a hole at the end */
912 static int test05(int fd, int testnum)
913 {
914         int ret = -1;
915         char *buf = NULL;
916         int bufsz = alloc_size;
917         int filsz = bufsz * 4;
918
919         /* |- DATA -|- HOLE -|- HOLE -|- HOLE -| */
920
921         buf = do_malloc(bufsz);
922         if (!buf)
923                 goto out;
924         memset(buf, 'a', bufsz);
925
926         ret = do_truncate(fd, filsz);
927         if (!ret)
928                 ret = do_pwrite(fd, buf, bufsz, 0);
929         if (ret)
930                 goto out;
931
932         /* offset at the beginning */
933
934         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
935         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
936
937         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
938         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
939
940         /* offset around data-hole boundary */
941         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, bufsz - 1, bufsz);
942         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
943
944         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, bufsz,     bufsz);
945         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, bufsz,     -1);
946         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, bufsz + 1, bufsz + 1);
947         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, bufsz + 1, -1);
948
949         /* offset around eof */
950         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, filsz - 1, filsz - 1);
951         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, filsz - 1, -1);
952         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, filsz,     -1);
953         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, filsz,     -1);
954         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, filsz + 1, -1);
955         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, filsz + 1, -1);
956 out:
957         do_free(buf);
958         return ret;
959 }
960 /* test hole begin and data end */
961 static int test04(int fd, int testnum)
962 {
963         int ret;
964         char *buf = "ABCDEFGH";
965         int bufsz, holsz, filsz;
966
967         bufsz = strlen(buf);
968         holsz = alloc_size * 2;
969         filsz = holsz + bufsz;
970
971         /* |- HOLE -|- HOLE -|- DATA -| */
972
973         ret = do_pwrite(fd, buf, bufsz, holsz);
974         if (ret)
975                 goto out;
976
977         /* offset at the beginning */
978         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
979         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
980         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, holsz);
981         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, holsz);
982         /* offset around hole-data boundary */
983         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, holsz - 1, holsz - 1);
984         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, holsz - 1, holsz);
985         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, holsz,     filsz);
986         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, holsz,     holsz);
987         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, holsz + 1, filsz);
988         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, holsz + 1, holsz + 1);
989
990         /* offset around eof */
991         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, filsz - 1, filsz);
992         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, filsz - 1, filsz - 1);
993         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, filsz,     -1);
994         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, filsz,     -1);
995         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, filsz + 1, -1);
996         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, filsz + 1, -1);
997 out:
998         return ret;
999 }
1000
1001 /* test a larger full file */
1002 static int test03(int fd, int testnum)
1003 {
1004         char *buf = NULL;
1005         int bufsz = alloc_size * 2 + 100;
1006         int filsz = bufsz;
1007         int ret = -1;
1008
1009         buf = do_malloc(bufsz);
1010         if (!buf)
1011                 goto out;
1012         memset(buf, 'a', bufsz);
1013
1014         ret = do_pwrite(fd, buf, bufsz, 0);
1015         if (ret)
1016                 goto out;
1017
1018         /* offset at the beginning */
1019         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
1020         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
1021         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
1022         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
1023
1024         /* offset around eof */
1025         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, bufsz - 1, bufsz);
1026         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
1027         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, bufsz,     -1);
1028         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, bufsz,     -1);
1029         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, bufsz + 1, -1);
1030         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, bufsz + 1, -1);
1031
1032 out:
1033         do_free(buf);
1034         return ret;
1035 }
1036
1037 /* test tiny full file */
1038 static int test02(int fd, int testnum)
1039 {
1040         int ret;
1041         char buf[] = "ABCDEFGH";
1042         int bufsz, filsz;
1043
1044         bufsz = strlen(buf);
1045         filsz = bufsz;
1046
1047         /* |- DATA -| */
1048
1049         ret = do_pwrite(fd, buf, bufsz, 0);
1050         if (ret)
1051                 goto out;
1052
1053         ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, filsz);
1054         ret += do_lseek(testnum, 2, fd, filsz, SEEK_DATA, 0, 0);
1055         ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 1, 1);
1056         ret += do_lseek(testnum, 4, fd, filsz, SEEK_HOLE, bufsz - 1, filsz);
1057         ret += do_lseek(testnum, 5, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
1058         ret += do_lseek(testnum, 6, fd, filsz, SEEK_HOLE, bufsz,     -1);
1059         ret += do_lseek(testnum, 7, fd, filsz, SEEK_DATA, bufsz,     -1);
1060         ret += do_lseek(testnum, 8, fd, filsz, SEEK_HOLE, bufsz + 1, -1);
1061         ret += do_lseek(testnum, 9, fd, filsz, SEEK_DATA, bufsz + 1, -1);
1062
1063 out:
1064         return ret;
1065 }
1066
1067 /* test empty file */
1068 static int test01(int fd, int testnum)
1069 {
1070         int ret = 0;
1071
1072         ret += do_lseek(testnum, 1, fd, 0, SEEK_DATA, 0, -1);
1073         ret += do_lseek(testnum, 2, fd, 0, SEEK_HOLE, 0, -1);
1074         ret += do_lseek(testnum, 3, fd, 0, SEEK_HOLE, 1, -1);
1075
1076         return ret;
1077 }
1078
1079 struct testrec {
1080        int     test_num;
1081        int     (*test_func)(int fd, int testnum);
1082        char    *test_desc;
1083 };
1084
1085 struct testrec seek_tests[] = {
1086        {  1, test01, "Test empty file" },
1087        {  2, test02, "Test a tiny full file" },
1088        {  3, test03, "Test a larger full file" },
1089        {  4, test04, "Test file hole at beg, data at end" },
1090        {  5, test05, "Test file data at beg, hole at end" },
1091        {  6, test06, "Test file hole data hole data" },
1092        {  7, test07, "Test file with unwritten extents, only have dirty pages" },
1093        {  8, test08, "Test file with unwritten extents, only have unwritten pages" },
1094        {  9, test09, "Test file with unwritten extents, have both dirty && unwritten pages" },
1095        { 10, test10, "Test a huge file for offset overflow" },
1096        { 11, test11, "Test a huge file for block number signed" },
1097        { 12, test12, "Test a huge file for block number overflow" },
1098        { 13, test13, "Test file with unwritten extents, only have pagevec dirty pages" },
1099        { 14, test14, "Test file with unwritten extents, small hole after pagevec dirty pages" },
1100        { 15, test15, "Test file with unwritten extents, page after unwritten extent" },
1101        { 16, test16, "Test file with unwritten extents, non-contiguous dirty pages" },
1102        { 17, test17, "Test file with unwritten extents, data-hole-data inside page" },
1103        { 18, test18, "Test file with negative SEEK_{HOLE,DATA} offsets" },
1104        { 19, test19, "Test file SEEK_DATA from middle of a large hole" },
1105        { 20, test20, "Test file SEEK_DATA from middle of a huge hole" },
1106        { 21, test21, "Test file SEEK_HOLE that was created by PUNCH_HOLE" },
1107 };
1108
1109 static int run_test(struct testrec *tr)
1110 {
1111         int ret = 0, fd = -1;
1112         char filename[255];
1113
1114         snprintf(filename, sizeof(filename), "%s%02d", base_file_path, tr->test_num);
1115
1116         fd = do_create(filename);
1117         if (fd > -1) {
1118                 printf("%02d. %-50s\n", tr->test_num, tr->test_desc);
1119                 ret = tr->test_func(fd, tr->test_num);
1120                 printf("\n");
1121         }
1122
1123         do_close(fd);
1124         return ret;
1125 }
1126
1127 static int test_basic_support(void)
1128 {
1129         int ret = -1, fd;
1130         off_t pos;
1131         char *buf = NULL;
1132         int bufsz, filsz;
1133
1134         fd = do_create(base_file_path);
1135         if (fd == -1)
1136                 goto out;
1137
1138         get_file_system(fd);
1139
1140         ret = get_io_sizes(fd);
1141         if (ret)
1142                 goto out;
1143
1144         ftruncate(fd, 0);
1145         bufsz = alloc_size * 2;
1146         filsz = bufsz * 2;
1147
1148         buf = do_malloc(bufsz);
1149         if (!buf)
1150                 goto out;
1151         memset(buf, 'a', bufsz);
1152
1153         /* File with 2 allocated blocks.... */
1154         ret = do_pwrite(fd, buf, bufsz, 0);
1155         if (ret)
1156                 goto out;
1157
1158         /* followed by a hole... */
1159         ret = do_truncate(fd, filsz);
1160         if (ret)
1161                 goto out;
1162
1163         /* Is SEEK_DATA supported in the kernel? */
1164         pos = lseek(fd, 0, SEEK_DATA);
1165         if (pos == -1) {
1166                 fprintf(stderr, "Kernel does not support llseek(2) extension "
1167                         "SEEK_DATA. Aborting.\n");
1168                 ret = -1;
1169                 goto out;
1170         }
1171
1172         /* Is SEEK_HOLE supported in the kernel? */
1173         pos = lseek(fd, 0, SEEK_HOLE);
1174         if (pos == -1) {
1175                 fprintf(stderr, "Kernel does not support llseek(2) extension "
1176                         "SEEK_HOLE. Aborting.\n");
1177                 ret = -1;
1178                 goto out;
1179         }
1180
1181         if (pos == filsz) {
1182                 default_behavior = 1;
1183                 fprintf(stderr, "File system supports the default behavior.\n");
1184         }
1185
1186         if (default_behavior && !allow_default_behavior) {
1187                 fprintf(stderr, "Default behavior is not allowed. Aborting.\n");
1188                 ret = -1;
1189                 goto out;
1190         }
1191
1192         /* Is unwritten extent supported? */
1193         ftruncate(fd, 0);
1194         if (fallocate(fd, 0, 0, alloc_size * 2) == -1) {
1195                 if (errno == EOPNOTSUPP) {
1196                         fprintf(stderr, "File system does not support fallocate.\n");
1197                 } else {
1198                         fprintf(stderr, "ERROR %d: Failed to preallocate "
1199                                 "space to %ld bytes. Aborting.\n", errno, (long) alloc_size);
1200                         ret = -1;
1201                 }
1202                 goto out;
1203         }
1204
1205         pos = lseek(fd, 0, SEEK_DATA);
1206         if (pos == 0)
1207                 fprintf(stderr, "File system does not support unwritten extents.\n");
1208         else
1209                 unwritten_extents = 1;
1210
1211         /* Is punch hole supported? */
1212         if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
1213                       0, alloc_size) == -1)
1214                 fprintf(stderr, "File system does not support punch hole.\n");
1215         else
1216                 punch_hole = 1;
1217
1218         printf("\n");
1219
1220 out:
1221         do_free(buf);
1222         do_close(fd);
1223         return ret;
1224 }
1225
1226 void usage(char *cmd)
1227 {
1228         fprintf(stdout, "Usage: %s [-tf] [-s <starttest>] [-e <endtest>] base_file_path\n", cmd);
1229         exit(1);
1230 }
1231
1232 int main(int argc, char **argv)
1233 {
1234         int ret = -1;
1235         int i = 0;
1236         int opt;
1237         int check_support = 0;
1238         int numtests = sizeof(seek_tests) / sizeof(struct testrec);
1239         int teststart, testend;
1240
1241         /*
1242          * First twelve tests are used by generic/285. To run these is the
1243          * default. Further tests are run e.g. by generic/436. They are not
1244          * run by default in order to not regress older kernels.
1245          */
1246         teststart = 1;
1247         testend = 12;
1248
1249         while ((opt = getopt(argc, argv, "tfs:e:")) != -1) {
1250                 switch (opt) {
1251                 case 't':
1252                         check_support++;
1253                         break;
1254                 case 'f':
1255                         allow_default_behavior = 0;
1256                         break;
1257                 case 's':
1258                         teststart = strtol(optarg, NULL, 10);
1259                         if (teststart <= 0 || teststart > numtests) {
1260                                 fprintf(stdout, "Invalid starting test: %s\n",
1261                                         optarg);
1262                                 usage(argv[0]);
1263                         }
1264                         break;
1265                 case 'e':
1266                         testend = strtol(optarg, NULL, 10);
1267                         if (testend <= 0 || testend > numtests) {
1268                                 fprintf(stdout, "Invalid final test: %s\n",
1269                                         optarg);
1270                                 usage(argv[0]);
1271                         }
1272                         break;
1273                 default:
1274                         usage(argv[0]);
1275                 }
1276         }
1277
1278         /* should be exactly one arg left, the filename */
1279         if (optind != argc - 1)
1280                 usage(argv[0]);
1281
1282         if (teststart > testend) {
1283                 fprintf(stdout, "Starting test is larger than final test!\n");
1284                 usage(argv[0]);
1285         }
1286
1287         base_file_path = (char *)strdup(argv[optind]);
1288
1289         ret = test_basic_support();
1290         if (ret || check_support)
1291                 goto out;
1292
1293         for (i = 0; i < numtests; ++i) {
1294                 if (seek_tests[i].test_num >= teststart &&
1295                     seek_tests[i].test_num <= testend) {
1296                         ret = run_test(&seek_tests[i]);
1297                         if (ret)
1298                                 break;
1299                 }
1300         }
1301
1302 out:
1303         free(base_file_path);
1304         return ret;
1305 }