src/open_by_handle: verify dir content only with -r flag
[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 /* Make sure we get ENXIO if we pass in a negative offset. */
278 static int test18(int fd, int testnum)
279 {
280         int ret = 0;
281
282         /* file size doesn't matter in this test, set to 0 */
283         ret += do_lseek(testnum, 1, fd, 0, SEEK_HOLE, -1, -1);
284         ret += do_lseek(testnum, 2, fd, 0, SEEK_DATA, -1, -1);
285
286         return ret;
287 }
288
289 static int test17(int fd, int testnum)
290 {
291         char *buf = NULL;
292         int pagesz = sysconf(_SC_PAGE_SIZE);
293         int bufsz, filsz;
294         int ret = 0;
295
296         if (!unwritten_extents) {
297                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
298                 goto out;
299         }
300
301         if (pagesz < 4 * alloc_size) {
302                 fprintf(stdout, "Test skipped as page size (%d) is less than "
303                         "four times allocation size (%d).\n",
304                         pagesz, (int)alloc_size);
305                 goto out;
306         }
307         bufsz = alloc_size;
308         filsz = 3 * bufsz;
309
310         buf = do_malloc(bufsz);
311         if (!buf) {
312                 ret = -1;
313                 goto out;
314         }
315         memset(buf, 'a', bufsz);
316
317         ret = do_fallocate(fd, 0, filsz, 0);
318         if (ret < 0)
319                 goto out;
320
321         ret = do_pwrite(fd, buf, bufsz, 0);
322         if (ret)
323                 goto out;
324
325         ret = do_pwrite(fd, buf, bufsz, 2 * bufsz);
326         if (ret)
327                 goto out;
328
329         ret += do_lseek(testnum,  1, fd, filsz, SEEK_DATA, 0, 0);
330         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 0, bufsz);
331         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 1, 1);
332         ret += do_lseek(testnum,  4, fd, filsz, SEEK_HOLE, 1, bufsz);
333         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, bufsz, 2 * bufsz);
334         ret += do_lseek(testnum,  6, fd, filsz, SEEK_HOLE, bufsz, bufsz);
335         ret += do_lseek(testnum,  7, fd, filsz, SEEK_DATA, bufsz + 1, 2 * bufsz);
336         ret += do_lseek(testnum,  8, fd, filsz, SEEK_HOLE, bufsz + 1, bufsz + 1);
337         ret += do_lseek(testnum,  9, fd, filsz, SEEK_DATA, 2 * bufsz, 2 * bufsz);
338         ret += do_lseek(testnum, 10, fd, filsz, SEEK_HOLE, 2 * bufsz, 3 * bufsz);
339         ret += do_lseek(testnum, 11, fd, filsz, SEEK_DATA, 2 * bufsz + 1, 2 * bufsz + 1);
340         ret += do_lseek(testnum, 12, fd, filsz, SEEK_HOLE, 2 * bufsz + 1, 3 * bufsz);
341
342         filsz += bufsz;
343         ret += do_fallocate(fd, 0, filsz, 0);
344
345         ret += do_lseek(testnum, 13, fd, filsz, SEEK_DATA, 3 * bufsz, -1);
346         ret += do_lseek(testnum, 14, fd, filsz, SEEK_HOLE, 3 * bufsz, 3 * bufsz);
347         ret += do_lseek(testnum, 15, fd, filsz, SEEK_DATA, 3 * bufsz + 1, -1);
348         ret += do_lseek(testnum, 16, fd, filsz, SEEK_HOLE, 3 * bufsz + 1, 3 * bufsz + 1);
349
350 out:
351         do_free(buf);
352         return ret;
353 }
354
355 /*
356  * test file with unwritten extents, having non-contiguous dirty pages in
357  * the unwritten extent.
358  */
359 static int test16(int fd, int testnum)
360 {
361         int ret = 0;
362         char *buf = NULL;
363         int bufsz = sysconf(_SC_PAGE_SIZE);
364         int filsz = 4 << 20;
365
366         if (!unwritten_extents) {
367                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
368                 goto out;
369         }
370
371         /* HOLE - unwritten DATA in dirty page */
372         /* Each unit is bufsz */
373         buf = do_malloc(bufsz);
374         if (!buf)
375                 goto out;
376         memset(buf, 'a', bufsz);
377
378         /* preallocate 4M space to file */
379         ret = do_fallocate(fd, 0, filsz, 0);
380         if (ret < 0)
381                 goto out;
382
383         ret = do_pwrite(fd, buf, bufsz, 0);
384         if (ret)
385                 goto out;
386
387         ret = do_pwrite(fd, buf, bufsz, filsz/2);
388         if (ret)
389                 goto out;
390
391         /* offset at the beginning */
392         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
393         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
394         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
395         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
396         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, bufsz, filsz/2);
397         ret += do_lseek(testnum,  6, fd, filsz, SEEK_HOLE, filsz/2,
398                         filsz/2 + bufsz);
399
400 out:
401         do_free(buf);
402         return ret;
403 }
404
405 /*
406  * test file with unwritten extents, having page just after end of unwritten
407  * extent.
408  */
409 static int test15(int fd, int testnum)
410 {
411         int ret = 0;
412         char *buf = NULL;
413         int bufsz = sysconf(_SC_PAGE_SIZE);
414         int filsz = 4 << 20;
415
416         if (!unwritten_extents) {
417                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
418                 goto out;
419         }
420
421         /* HOLE - unwritten DATA in dirty page */
422         /* Each unit is bufsz */
423         buf = do_malloc(bufsz);
424         if (!buf)
425                 goto out;
426         memset(buf, 'a', bufsz);
427
428         /* preallocate 4M space to file */
429         ret = do_fallocate(fd, 0, filsz, 0);
430         if (ret < 0)
431                 goto out;
432
433         ret = do_pwrite(fd, buf, bufsz, 0);
434         if (ret)
435                 goto out;
436
437         /* One page written just after end of unwritten extent... */
438         ret = do_pwrite(fd, buf, bufsz, filsz);
439         if (ret)
440                 goto out;
441
442         /* update file size */
443         filsz += bufsz;
444
445         /* offset at the beginning */
446         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
447         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
448         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
449         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
450         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, bufsz, filsz - bufsz);
451
452 out:
453         do_free(buf);
454         return ret;
455 }
456
457 /*
458  * test file with unwritten extents, only have pagevec worth of dirty pages
459  * in page cache, a hole and then another page.
460  */
461 static int test14(int fd, int testnum)
462 {
463         int ret = 0;
464         char *buf = NULL;
465         int bufsz = sysconf(_SC_PAGE_SIZE) * 14;
466         int filsz = 4 << 20;
467
468         if (!unwritten_extents) {
469                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
470                 goto out;
471         }
472
473         /* HOLE - unwritten DATA in dirty page */
474         /* Each unit is bufsz */
475         buf = do_malloc(bufsz);
476         if (!buf)
477                 goto out;
478         memset(buf, 'a', bufsz);
479
480         /* preallocate 4M space to file */
481         ret = do_fallocate(fd, 0, filsz, 0);
482         if (ret < 0)
483                 goto out;
484
485         ret = do_pwrite(fd, buf, bufsz, 0);
486         if (ret)
487                 goto out;
488
489         ret = do_pwrite(fd, buf, bufsz, 3 * bufsz);
490         if (ret)
491                 goto out;
492
493         /* offset at the beginning */
494         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
495         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
496         ret += do_lseek(testnum,  3, fd, filsz, SEEK_HOLE, 3 * bufsz, 4 * bufsz);
497         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 0, 0);
498         ret += do_lseek(testnum,  5, fd, filsz, SEEK_DATA, 1, 1);
499         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, bufsz, 3 * bufsz);
500
501 out:
502         do_free(buf);
503         return ret;
504 }
505
506 /*
507  * test file with unwritten extents, only have pagevec worth of dirty pages
508  * in page cache.
509  */
510 static int test13(int fd, int testnum)
511 {
512         int ret = 0;
513         char *buf = NULL;
514         int bufsz = sysconf(_SC_PAGE_SIZE) * 14;
515         int filsz = 4 << 20;
516
517         if (!unwritten_extents) {
518                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
519                 goto out;
520         }
521
522         /* HOLE - unwritten DATA in dirty page */
523         /* Each unit is bufsz */
524         buf = do_malloc(bufsz);
525         if (!buf)
526                 goto out;
527         memset(buf, 'a', bufsz);
528
529         /* preallocate 4M space to file */
530         ret = do_fallocate(fd, 0, filsz, 0);
531         if (ret < 0)
532                 goto out;
533
534         ret = do_pwrite(fd, buf, bufsz, 0);
535         if (ret)
536                 goto out;
537
538         /* offset at the beginning */
539         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
540         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
541         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
542         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
543
544 out:
545         do_free(buf);
546         return ret;
547 }
548
549 /*
550  * Test huge file to check for overflows of block counts due to usage of
551  * 32-bit types.
552  */
553 static int test12(int fd, int testnum)
554 {
555         return huge_file_test(fd, testnum,
556                                 ((long long)alloc_size << 32) + (1 << 20));
557 }
558
559 /*
560  * Test huge file to check for overflows of block counts due to usage of
561  * signed types
562  */
563 static int test11(int fd, int testnum)
564 {
565         return huge_file_test(fd, testnum,
566                                 ((long long)alloc_size << 31) + (1 << 20));
567 }
568
569 /* Test an 8G file to check for offset overflows at 1 << 32 */
570 static int test10(int fd, int testnum)
571 {
572         return huge_file_test(fd, testnum, 8ULL << 30);
573 }
574
575 /*
576  * test file with unwritten extents, have both dirty and
577  * writeback pages in page cache.
578  */
579 static int test09(int fd, int testnum)
580 {
581         int ret = 0;
582         char *buf = NULL;
583         int bufsz = alloc_size;
584         int filsz = bufsz * 100 + bufsz;
585
586         if (!unwritten_extents) {
587                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
588                 goto out;
589         }
590
591         /*
592          * HOLE - unwritten DATA in dirty page - HOLE -
593          * unwritten DATA in writeback page
594          */
595
596         /* Each unit is bufsz */
597         buf = do_malloc(bufsz);
598         if (!buf)
599                 goto out;
600         memset(buf, 'a', bufsz);
601
602         /* preallocate 8M space to file */
603         ret = do_fallocate(fd, 0, filsz, 0);
604         if (ret < 0)
605                 goto out;
606
607         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
608         if (!ret) {
609                 ret = do_pwrite(fd, buf, bufsz, bufsz * 100);
610                 if (ret)
611                         goto out;
612         }
613
614         /*
615          * Sync out dirty pages from bufsz * 100, this will convert
616          * the dirty page to writeback.
617          */
618         ret = do_sync_dirty_pages(fd, bufsz * 100, 0);
619         if (ret)
620                 goto out;
621
622         /* offset at the beginning */
623         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
624         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
625         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
626         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
627
628 out:
629         do_free(buf);
630         return ret;
631 }
632
633 /* test file with unwritten extent, only have writeback page */
634 static int test08(int fd, int testnum)
635 {
636         int ret = 0;
637         char *buf = NULL;
638         int bufsz = alloc_size;
639         int filsz = bufsz * 10 + bufsz;
640
641         if (!unwritten_extents) {
642                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
643                 goto out;
644         }
645
646         /* HOLE - unwritten DATA in writeback page */
647         /* Each unit is bufsz */
648         buf = do_malloc(bufsz);
649         if (!buf)
650                 goto out;
651         memset(buf, 'a', bufsz);
652
653         /* preallocate 4M space to file */
654         ret = do_fallocate(fd, 0, filsz, 0);
655         if (ret < 0)
656                 goto out;
657
658         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
659         if (ret)
660                 goto out;
661
662         /* Sync out all file */
663         ret = do_sync_dirty_pages(fd, 0, 0);
664         if (ret)
665                 goto out;
666
667         /* offset at the beginning */
668         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
669         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
670         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
671         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
672
673 out:
674         do_free(buf);
675         return ret;
676 }
677
678 /*
679  * test file with unwritten extents, only have dirty pages
680  * in page cache.
681  */
682 static int test07(int fd, int testnum)
683 {
684         int ret = 0;
685         char *buf = NULL;
686         int bufsz = alloc_size;
687         int filsz = bufsz * 10 + bufsz;
688
689         if (!unwritten_extents) {
690                 fprintf(stdout, "Test skipped as fs doesn't support unwritten extents.\n");
691                 goto out;
692         }
693
694         /* HOLE - unwritten DATA in dirty page */
695         /* Each unit is bufsz */
696         buf = do_malloc(bufsz);
697         if (!buf)
698                 goto out;
699         memset(buf, 'a', bufsz);
700
701         /* preallocate 4M space to file */
702         ret = do_fallocate(fd, 0, filsz, 0);
703         if (ret < 0)
704                 goto out;
705
706         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
707         if (ret)
708                 goto out;
709
710         /* offset at the beginning */
711         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
712         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
713         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
714         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
715
716 out:
717         do_free(buf);
718         return ret;
719 }
720
721 /* test hole data hole data */
722 static int test06(int fd, int testnum)
723 {
724         int ret = -1;
725         char *buf = NULL;
726         int bufsz = alloc_size;
727         int filsz = bufsz * 4;
728         int off;
729
730         /* HOLE - DATA - HOLE - DATA */
731         /* Each unit is bufsz */
732
733         buf = do_malloc(bufsz);
734         if (!buf)
735                 goto out;
736
737         memset(buf, 'a', bufsz);
738
739         ret = do_pwrite(fd, buf, bufsz, bufsz);
740         if (!ret)
741                 do_pwrite(fd, buf, bufsz, bufsz * 3);
742         if (ret)
743                 goto out;
744
745         /* offset at the beginning */
746         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
747         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
748         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz);
749         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz);
750
751         /* offset around first hole-data boundary */
752         off = bufsz;
753         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, off - 1, off - 1);
754         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, off - 1, off);
755         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, off,     bufsz * 2);
756         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, off,     off);
757         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, off + 1, bufsz * 2);
758         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, off + 1, off + 1);
759
760         /* offset around data-hole boundary */
761         off = bufsz * 2;
762         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, off - 1, off);
763         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, off - 1, off - 1);
764         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, off,     off);
765         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, off,     bufsz * 3);
766         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, off + 1, off + 1);
767         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, off + 1, bufsz * 3);
768
769         /* offset around second hole-data boundary */
770         off = bufsz * 3;
771         ret += do_lseek(testnum, 17, fd, filsz, SEEK_HOLE, off - 1, off - 1);
772         ret += do_lseek(testnum, 18, fd, filsz, SEEK_DATA, off - 1, off);
773         ret += do_lseek(testnum, 19, fd, filsz, SEEK_HOLE, off,     filsz);
774         ret += do_lseek(testnum, 20, fd, filsz, SEEK_DATA, off,     off);
775         ret += do_lseek(testnum, 21, fd, filsz, SEEK_HOLE, off + 1, filsz);
776         ret += do_lseek(testnum, 22, fd, filsz, SEEK_DATA, off + 1, off + 1);
777
778         /* offset around the end of file */
779         off = filsz;
780         ret += do_lseek(testnum, 23, fd, filsz, SEEK_HOLE, off - 1, filsz);
781         ret += do_lseek(testnum, 24, fd, filsz, SEEK_DATA, off - 1, filsz - 1);
782         ret += do_lseek(testnum, 25, fd, filsz, SEEK_HOLE, off, -1);
783         ret += do_lseek(testnum, 26, fd, filsz, SEEK_DATA, off, -1);
784         ret += do_lseek(testnum, 27, fd, filsz, SEEK_HOLE, off + 1, -1);
785         ret += do_lseek(testnum, 28, fd, filsz, SEEK_DATA, off + 1, -1);
786
787 out:
788         do_free(buf);
789         return ret;
790 }
791
792 /* test file with data at the beginning and a hole at the end */
793 static int test05(int fd, int testnum)
794 {
795         int ret = -1;
796         char *buf = NULL;
797         int bufsz = alloc_size;
798         int filsz = bufsz * 4;
799
800         /* |- DATA -|- HOLE -|- HOLE -|- HOLE -| */
801
802         buf = do_malloc(bufsz);
803         if (!buf)
804                 goto out;
805         memset(buf, 'a', bufsz);
806
807         ret = do_truncate(fd, filsz);
808         if (!ret)
809                 ret = do_pwrite(fd, buf, bufsz, 0);
810         if (ret)
811                 goto out;
812
813         /* offset at the beginning */
814
815         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
816         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
817
818         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
819         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
820
821         /* offset around data-hole boundary */
822         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, bufsz - 1, bufsz);
823         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
824
825         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, bufsz,     bufsz);
826         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, bufsz,     -1);
827         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, bufsz + 1, bufsz + 1);
828         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, bufsz + 1, -1);
829
830         /* offset around eof */
831         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, filsz - 1, filsz - 1);
832         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, filsz - 1, -1);
833         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, filsz,     -1);
834         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, filsz,     -1);
835         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, filsz + 1, -1);
836         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, filsz + 1, -1);
837 out:
838         do_free(buf);
839         return ret;
840 }
841 /* test hole begin and data end */
842 static int test04(int fd, int testnum)
843 {
844         int ret;
845         char *buf = "ABCDEFGH";
846         int bufsz, holsz, filsz;
847
848         bufsz = strlen(buf);
849         holsz = alloc_size * 2;
850         filsz = holsz + bufsz;
851
852         /* |- HOLE -|- HOLE -|- DATA -| */
853
854         ret = do_pwrite(fd, buf, bufsz, holsz);
855         if (ret)
856                 goto out;
857
858         /* offset at the beginning */
859         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
860         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
861         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, holsz);
862         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, holsz);
863         /* offset around hole-data boundary */
864         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, holsz - 1, holsz - 1);
865         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, holsz - 1, holsz);
866         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, holsz,     filsz);
867         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, holsz,     holsz);
868         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, holsz + 1, filsz);
869         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, holsz + 1, holsz + 1);
870
871         /* offset around eof */
872         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, filsz - 1, filsz);
873         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, filsz - 1, filsz - 1);
874         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, filsz,     -1);
875         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, filsz,     -1);
876         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, filsz + 1, -1);
877         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, filsz + 1, -1);
878 out:
879         return ret;
880 }
881
882 /* test a larger full file */
883 static int test03(int fd, int testnum)
884 {
885         char *buf = NULL;
886         int bufsz = alloc_size * 2 + 100;
887         int filsz = bufsz;
888         int ret = -1;
889
890         buf = do_malloc(bufsz);
891         if (!buf)
892                 goto out;
893         memset(buf, 'a', bufsz);
894
895         ret = do_pwrite(fd, buf, bufsz, 0);
896         if (ret)
897                 goto out;
898
899         /* offset at the beginning */
900         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
901         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
902         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
903         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
904
905         /* offset around eof */
906         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, bufsz - 1, bufsz);
907         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
908         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, bufsz,     -1);
909         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, bufsz,     -1);
910         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, bufsz + 1, -1);
911         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, bufsz + 1, -1);
912
913 out:
914         do_free(buf);
915         return ret;
916 }
917
918 /* test tiny full file */
919 static int test02(int fd, int testnum)
920 {
921         int ret;
922         char buf[] = "ABCDEFGH";
923         int bufsz, filsz;
924
925         bufsz = strlen(buf);
926         filsz = bufsz;
927
928         /* |- DATA -| */
929
930         ret = do_pwrite(fd, buf, bufsz, 0);
931         if (ret)
932                 goto out;
933
934         ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, filsz);
935         ret += do_lseek(testnum, 2, fd, filsz, SEEK_DATA, 0, 0);
936         ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 1, 1);
937         ret += do_lseek(testnum, 4, fd, filsz, SEEK_HOLE, bufsz - 1, filsz);
938         ret += do_lseek(testnum, 5, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
939         ret += do_lseek(testnum, 6, fd, filsz, SEEK_HOLE, bufsz,     -1);
940         ret += do_lseek(testnum, 7, fd, filsz, SEEK_DATA, bufsz,     -1);
941         ret += do_lseek(testnum, 8, fd, filsz, SEEK_HOLE, bufsz + 1, -1);
942         ret += do_lseek(testnum, 9, fd, filsz, SEEK_DATA, bufsz + 1, -1);
943
944 out:
945         return ret;
946 }
947
948 /* test empty file */
949 static int test01(int fd, int testnum)
950 {
951         int ret = 0;
952
953         ret += do_lseek(testnum, 1, fd, 0, SEEK_DATA, 0, -1);
954         ret += do_lseek(testnum, 2, fd, 0, SEEK_HOLE, 0, -1);
955         ret += do_lseek(testnum, 3, fd, 0, SEEK_HOLE, 1, -1);
956
957         return ret;
958 }
959
960 struct testrec {
961        int     test_num;
962        int     (*test_func)(int fd, int testnum);
963        char    *test_desc;
964 };
965
966 struct testrec seek_tests[] = {
967        {  1, test01, "Test empty file" },
968        {  2, test02, "Test a tiny full file" },
969        {  3, test03, "Test a larger full file" },
970        {  4, test04, "Test file hole at beg, data at end" },
971        {  5, test05, "Test file data at beg, hole at end" },
972        {  6, test06, "Test file hole data hole data" },
973        {  7, test07, "Test file with unwritten extents, only have dirty pages" },
974        {  8, test08, "Test file with unwritten extents, only have unwritten pages" },
975        {  9, test09, "Test file with unwritten extents, have both dirty && unwritten pages" },
976        { 10, test10, "Test a huge file for offset overflow" },
977        { 11, test11, "Test a huge file for block number signed" },
978        { 12, test12, "Test a huge file for block number overflow" },
979        { 13, test13, "Test file with unwritten extents, only have pagevec dirty pages" },
980        { 14, test14, "Test file with unwritten extents, small hole after pagevec dirty pages" },
981        { 15, test15, "Test file with unwritten extents, page after unwritten extent" },
982        { 16, test16, "Test file with unwritten extents, non-contiguous dirty pages" },
983        { 17, test17, "Test file with unwritten extents, data-hole-data inside page" },
984        { 18, test18, "Test file with negative SEEK_{HOLE,DATA} offsets" },
985 };
986
987 static int run_test(struct testrec *tr)
988 {
989         int ret = 0, fd = -1;
990         char filename[255];
991
992         snprintf(filename, sizeof(filename), "%s%02d", base_file_path, tr->test_num);
993
994         fd = do_create(filename);
995         if (fd > -1) {
996                 printf("%02d. %-50s\n", tr->test_num, tr->test_desc);
997                 ret = tr->test_func(fd, tr->test_num);
998                 printf("\n");
999         }
1000
1001         do_close(fd);
1002         return ret;
1003 }
1004
1005 static int test_basic_support(void)
1006 {
1007         int ret = -1, fd;
1008         off_t pos;
1009         char *buf = NULL;
1010         int bufsz, filsz;
1011
1012         fd = do_create(base_file_path);
1013         if (fd == -1)
1014                 goto out;
1015
1016         get_file_system(fd);
1017
1018         ret = get_io_sizes(fd);
1019         if (ret)
1020                 goto out;
1021
1022         ftruncate(fd, 0);
1023         bufsz = alloc_size * 2;
1024         filsz = bufsz * 2;
1025
1026         buf = do_malloc(bufsz);
1027         if (!buf)
1028                 goto out;
1029         memset(buf, 'a', bufsz);
1030
1031         /* File with 2 allocated blocks.... */
1032         ret = do_pwrite(fd, buf, bufsz, 0);
1033         if (ret)
1034                 goto out;
1035
1036         /* followed by a hole... */
1037         ret = do_truncate(fd, filsz);
1038         if (ret)
1039                 goto out;
1040
1041         /* Is SEEK_DATA and SEEK_HOLE supported in the kernel? */
1042         pos = lseek(fd, 0, SEEK_HOLE);
1043         if (pos == -1) {
1044                 fprintf(stderr, "Kernel does not support llseek(2) extension "
1045                         "SEEK_HOLE. Aborting.\n");
1046                 ret = -1;
1047                 goto out;
1048         }
1049
1050         if (pos == filsz) {
1051                 default_behavior = 1;
1052                 fprintf(stderr, "File system supports the default behavior.\n");
1053         }
1054
1055         ftruncate(fd, 0);
1056         if (fallocate(fd, 0, 0, alloc_size) == -1) {
1057                 if (errno == EOPNOTSUPP)
1058                         fprintf(stderr, "File system does not support fallocate.");
1059                 else {
1060                         fprintf(stderr, "ERROR %d: Failed to preallocate "
1061                                 "space to %ld bytes. Aborting.\n", errno, (long) alloc_size);
1062                         ret = -1;
1063                 }
1064                 goto out;
1065         }
1066
1067         pos = lseek(fd, 0, SEEK_DATA);
1068         if (pos == 0) {
1069                 fprintf(stderr, "File system does not support unwritten extents.\n");
1070                 goto out;
1071         }
1072         unwritten_extents = 1;
1073
1074         printf("\n");
1075
1076 out:
1077         do_free(buf);
1078         do_close(fd);
1079         return ret;
1080 }
1081
1082 void usage(char *cmd)
1083 {
1084         fprintf(stdout, "Usage: %s [-t] [-s <starttest>] [-e <endtest>] base_file_path\n", cmd);
1085         exit(1);
1086 }
1087
1088 int main(int argc, char **argv)
1089 {
1090         int ret = -1;
1091         int i = 0;
1092         int opt;
1093         int check_support = 0;
1094         int numtests = sizeof(seek_tests) / sizeof(struct testrec);
1095         int teststart, testend;
1096
1097         /*
1098          * First twelve tests are used by generic/285. To run these is the
1099          * default. Further tests are run e.g. by generic/436. They are not
1100          * run by default in order to not regress older kernels.
1101          */
1102         teststart = 1;
1103         testend = 12;
1104
1105         while ((opt = getopt(argc, argv, "ts:e:")) != -1) {
1106                 switch (opt) {
1107                 case 't':
1108                         check_support++;
1109                         break;
1110                 case 's':
1111                         teststart = strtol(optarg, NULL, 10);
1112                         if (teststart <= 0 || teststart > numtests) {
1113                                 fprintf(stdout, "Invalid starting test: %s\n",
1114                                         optarg);
1115                                 usage(argv[0]);
1116                         }
1117                         break;
1118                 case 'e':
1119                         testend = strtol(optarg, NULL, 10);
1120                         if (testend <= 0 || testend > numtests) {
1121                                 fprintf(stdout, "Invalid final test: %s\n",
1122                                         optarg);
1123                                 usage(argv[0]);
1124                         }
1125                         break;
1126                 default:
1127                         usage(argv[0]);
1128                 }
1129         }
1130
1131         /* should be exactly one arg left, the filename */
1132         if (optind != argc - 1)
1133                 usage(argv[0]);
1134
1135         if (teststart > testend) {
1136                 fprintf(stdout, "Starting test is larger than final test!\n");
1137                 usage(argv[0]);
1138         }
1139
1140         base_file_path = (char *)strdup(argv[optind]);
1141
1142         ret = test_basic_support();
1143         if (ret || check_support)
1144                 goto out;
1145
1146         for (i = 0; i < numtests; ++i) {
1147                 if (seek_tests[i].test_num >= teststart &&
1148                     seek_tests[i].test_num <= testend) {
1149                         ret = run_test(&seek_tests[i]);
1150                         if (ret)
1151                                 break;
1152                 }
1153         }
1154
1155 out:
1156         free(base_file_path);
1157         return ret;
1158 }