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