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