Merge branch 'master' of git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev
[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 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/vfs.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <assert.h>
31
32 #ifndef SEEK_DATA
33 #define SEEK_DATA      3
34 #define SEEK_HOLE      4
35 #endif
36
37 static blksize_t alloc_size;
38 int default_behavior = 0;
39 char *base_file_path;
40
41 static void get_file_system(int fd)
42 {
43         struct statfs buf;
44
45         if (!fstatfs(fd, &buf))
46                 fprintf(stdout, "File system magic#: 0x%lx\n", buf.f_type);
47 }
48
49 static int get_io_sizes(int fd)
50 {
51        struct stat buf;
52        int ret;
53
54        ret = fstat(fd, &buf);
55        if (ret)
56                fprintf(stderr, "  ERROR %d: Failed to find io blocksize\n",
57                        errno);
58
59        /* st_blksize is typically also the allocation size */
60        alloc_size = buf.st_blksize;
61        fprintf(stdout, "Allocation size: %ld\n", alloc_size);
62
63        return ret;
64 }
65
66 #define do_free(x)     do { if(x) free(x); } while(0);
67
68 static void *do_malloc(size_t size)
69 {
70        void *buf;
71
72        buf = malloc(size);
73        if (!buf)
74                fprintf(stderr, "  ERROR: Unable to allocate %ld bytes\n",
75                        (long)size);
76
77        return buf;
78 }
79
80 static int do_truncate(int fd, off_t length)
81 {
82        int ret;
83
84        ret = ftruncate(fd, length);
85        if (ret)
86                fprintf(stderr, "  ERROR %d: Failed to extend file "
87                        "to %ld bytes\n", errno, (long)length);
88        return ret;
89 }
90
91 static int do_fallocate(int fd, off_t offset, off_t length, int mode)
92 {
93         int ret;
94
95         ret = fallocate(fd, mode, offset, length);
96         if (ret)
97                 fprintf(stderr, "  ERROR %d: Failed to preallocate "
98                         "space to %ld bytes\n", errno, (long) length);
99
100         return ret;
101 }
102
103 /*
104  * Synchnorize all dirty pages in the file range starting from
105  * offset to nbytes length.
106  */
107 static int do_sync_dirty_pages(int fd, off64_t offset, off64_t nbytes)
108 {
109         int ret;
110
111         ret = sync_file_range(fd, offset, nbytes, SYNC_FILE_RANGE_WRITE);
112         if (ret)
113                 fprintf(stderr, "  ERROR %d: Failed to sync out dirty "
114                         "pages\n", errno);
115
116         return ret;
117 }
118
119 static ssize_t do_pwrite(int fd, const void *buf, size_t count, off_t offset)
120 {
121        ssize_t ret, written = 0;
122
123        while (count > written) {
124                ret = pwrite(fd, buf + written, count - written, offset + written);
125                if (ret < 0) {
126                        fprintf(stderr, "  ERROR %d: Failed to write %ld "
127                                "bytes\n", errno, (long)count);
128                        return ret;
129                }
130                written += ret;
131        }
132
133        return 0;
134 }
135
136 #define do_close(x)     do { if ((x) > -1) close(x); } while(0);
137
138 static int do_create(const char *filename)
139 {
140         int fd;
141
142         fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644);
143         if (fd < 0)
144                 fprintf(stderr, " ERROR %d: Failed to create file '%s'\n",
145                         errno, filename);
146
147         return fd;
148 }
149
150 static int do_lseek(int testnum, int subtest, int fd, int filsz, int origin,
151                     off_t set, off_t exp)
152 {
153         off_t pos, exp2;
154         int x, ret;
155
156         assert(!(origin != SEEK_HOLE && origin != SEEK_DATA));
157
158         /*
159          * The file pointer can be set to different values depending
160          * on the implementation. For SEEK_HOLE, EOF could be a valid
161          * value. For SEEK_DATA, supplied offset could be the valid
162          * value.
163          */
164         exp2 = exp;
165         if (origin == SEEK_HOLE && exp2 != -1)
166                 exp2 = filsz;
167         if (origin == SEEK_DATA && default_behavior && set < filsz)
168                 exp2 = set;
169
170         pos = lseek(fd, set, origin);
171
172         if (pos == -1 && exp == -1) {
173                 x = fprintf(stdout, "%02d.%02d %s expected -1 with errno %d, got %d. ",
174                             testnum, subtest,
175                             (origin == SEEK_HOLE) ? "SEEK_HOLE" : "SEEK_DATA",
176                             -ENXIO, -errno);
177                 ret = !(errno == ENXIO);
178         } else {
179
180                 x = fprintf(stdout, "%02d.%02d %s expected %ld or %ld, got %ld. ",
181                             testnum, subtest,
182                             (origin == SEEK_HOLE) ? "SEEK_HOLE" : "SEEK_DATA",
183                             (long)exp, (long)exp2, (long)pos);
184                 ret = !(pos == exp || pos == exp2);
185         }
186
187         fprintf(stdout, "%*s\n", (70 - x), ret ? "FAIL" : "succ");
188
189         return ret;
190 }
191
192 /*
193  * test file with unwritten extents, have both dirty and
194  * writeback pages in page cache.
195  */
196 static int test09(int fd, int testnum)
197 {
198         int ret = 0;
199         char *buf = NULL;
200         int bufsz = alloc_size;
201         int filsz = 8 << 20;
202
203         /*
204          * HOLE - unwritten DATA in dirty page - HOLE -
205          * unwritten DATA in writeback page
206          */
207
208         /* Each unit is bufsz */
209         buf = do_malloc(bufsz);
210         if (!buf)
211                 goto out;
212         memset(buf, 'a', bufsz);
213
214         /* preallocate 8M space to file */
215         ret = do_fallocate(fd, 0, filsz, 0);
216         if (ret < 0)
217                 goto out;
218
219         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
220         if (!ret) {
221                 ret = do_pwrite(fd, buf, bufsz, bufsz * 100);
222                 if (ret)
223                         goto out;
224         }
225
226         /*
227          * Sync out dirty pages from bufsz * 100, this will convert
228          * the dirty page to writeback.
229          */
230         ret = do_sync_dirty_pages(fd, bufsz * 100, 0);
231         if (ret)
232                 goto out;
233
234         /* offset at the beginning */
235         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
236         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
237         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
238         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
239
240 out:
241         do_free(buf);
242         return ret;
243 }
244
245 /* test file with unwritten extent, only have writeback page */
246 static int test08(int fd, int testnum)
247 {
248         int ret = 0;
249         char *buf = NULL;
250         int bufsz = alloc_size;
251         int filsz = 4 << 20;
252
253         /* HOLE - unwritten DATA in writeback page */
254         /* Each unit is bufsz */
255         buf = do_malloc(bufsz);
256         if (!buf)
257                 goto out;
258         memset(buf, 'a', bufsz);
259
260         /* preallocate 4M space to file */
261         ret = do_fallocate(fd, 0, filsz, 0);
262         if (ret < 0)
263                 goto out;
264
265         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
266         if (ret)
267                 goto out;
268
269         /* Sync out all file */
270         ret = do_sync_dirty_pages(fd, 0, 0);
271         if (ret)
272                 goto out;
273
274         /* offset at the beginning */
275         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
276         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
277         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
278         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
279
280 out:
281         do_free(buf);
282         return ret;
283 }
284
285 /*
286  * test file with unwritten extents, only have dirty pages
287  * in page cache.
288  */
289 static int test07(int fd, int testnum)
290 {
291         int ret = 0;
292         char *buf = NULL;
293         int bufsz = alloc_size;
294         int filsz = 4 << 20;
295
296         /* HOLE - unwritten DATA in dirty page */
297         /* Each unit is bufsz */
298         buf = do_malloc(bufsz);
299         if (!buf)
300                 goto out;
301         memset(buf, 'a', bufsz);
302
303         /* preallocate 4M space to file */
304         ret = do_fallocate(fd, 0, filsz, 0);
305         if (ret < 0)
306                 goto out;
307
308         ret = do_pwrite(fd, buf, bufsz, bufsz * 10);
309         if (ret)
310                 goto out;
311
312         /* offset at the beginning */
313         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
314         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
315         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz * 10);
316         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz * 10);
317
318 out:
319         do_free(buf);
320         return ret;
321 }
322
323 /* test hole data hole data */
324 static int test06(int fd, int testnum)
325 {
326         int ret = -1;
327         char *buf = NULL;
328         int bufsz = alloc_size;
329         int filsz = bufsz * 4;
330         int off;
331
332         /* HOLE - DATA - HOLE - DATA */
333         /* Each unit is bufsz */
334
335         buf = do_malloc(bufsz);
336         if (!buf)
337                 goto out;
338
339         memset(buf, 'a', bufsz);
340
341         ret = do_pwrite(fd, buf, bufsz, bufsz);
342         if (!ret)
343                 do_pwrite(fd, buf, bufsz, bufsz * 3);
344         if (ret)
345                 goto out;
346
347         /* offset at the beginning */
348         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
349         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
350         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, bufsz);
351         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, bufsz);
352
353         /* offset around first hole-data boundary */
354         off = bufsz;
355         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, off - 1, off - 1);
356         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, off - 1, off);
357         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, off,     bufsz * 2);
358         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, off,     off);
359         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, off + 1, bufsz * 2);
360         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, off + 1, off + 1);
361
362         /* offset around data-hole boundary */
363         off = bufsz * 2;
364         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, off - 1, off);
365         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, off - 1, off - 1);
366         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, off,     off);
367         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, off,     bufsz * 3);
368         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, off + 1, off + 1);
369         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, off + 1, bufsz * 3);
370
371         /* offset around second hole-data boundary */
372         off = bufsz * 3;
373         ret += do_lseek(testnum, 17, fd, filsz, SEEK_HOLE, off - 1, off - 1);
374         ret += do_lseek(testnum, 18, fd, filsz, SEEK_DATA, off - 1, off);
375         ret += do_lseek(testnum, 19, fd, filsz, SEEK_HOLE, off,     filsz);
376         ret += do_lseek(testnum, 20, fd, filsz, SEEK_DATA, off,     off);
377         ret += do_lseek(testnum, 21, fd, filsz, SEEK_HOLE, off + 1, filsz);
378         ret += do_lseek(testnum, 22, fd, filsz, SEEK_DATA, off + 1, off + 1);
379
380         /* offset around the end of file */
381         off = filsz;
382         ret += do_lseek(testnum, 23, fd, filsz, SEEK_HOLE, off - 1, filsz);
383         ret += do_lseek(testnum, 24, fd, filsz, SEEK_DATA, off - 1, filsz - 1);
384         ret += do_lseek(testnum, 25, fd, filsz, SEEK_HOLE, off, -1);
385         ret += do_lseek(testnum, 26, fd, filsz, SEEK_DATA, off, -1);
386         ret += do_lseek(testnum, 27, fd, filsz, SEEK_HOLE, off + 1, -1);
387         ret += do_lseek(testnum, 28, fd, filsz, SEEK_DATA, off + 1, -1);
388
389 out:
390         do_free(buf);
391         return ret;
392 }
393
394 /* test file with data at the beginning and a hole at the end */
395 static int test05(int fd, int testnum)
396 {
397         int ret = -1;
398         char *buf = NULL;
399         int bufsz = alloc_size;
400         int filsz = bufsz * 4;
401
402         /* |- DATA -|- HOLE -|- HOLE -|- HOLE -| */
403
404         buf = do_malloc(bufsz);
405         if (!buf)
406                 goto out;
407         memset(buf, 'a', bufsz);
408
409         ret = do_truncate(fd, filsz);
410         if (!ret)
411                 ret = do_pwrite(fd, buf, bufsz, 0);
412         if (ret)
413                 goto out;
414
415         /* offset at the beginning */
416
417         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
418         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
419
420         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
421         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
422
423         /* offset around data-hole boundary */
424         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, bufsz - 1, bufsz);
425         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
426
427         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, bufsz,     bufsz);
428         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, bufsz,     -1);
429         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, bufsz + 1, bufsz + 1);
430         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, bufsz + 1, -1);
431
432         /* offset around eof */
433         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, filsz - 1, filsz - 1);
434         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, filsz - 1, -1);
435         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, filsz,     -1);
436         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, filsz,     -1);
437         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, filsz + 1, -1);
438         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, filsz + 1, -1);
439 out:
440         do_free(buf);
441         return ret;
442 }
443 /* test hole begin and data end */
444 static int test04(int fd, int testnum)
445 {
446         int ret;
447         char *buf = "ABCDEFGH";
448         int bufsz, holsz, filsz;
449
450         bufsz = strlen(buf);
451         holsz = alloc_size * 2;
452         filsz = holsz + bufsz;
453
454         /* |- HOLE -|- HOLE -|- DATA -| */
455
456         ret = do_pwrite(fd, buf, bufsz, holsz);
457         if (ret)
458                 goto out;
459
460         /* offset at the beginning */
461         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, 0);
462         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, 1);
463         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, holsz);
464         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, holsz);
465         /* offset around hole-data boundary */
466         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, holsz - 1, holsz - 1);
467         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, holsz - 1, holsz);
468         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, holsz,     filsz);
469         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, holsz,     holsz);
470         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, holsz + 1, filsz);
471         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, holsz + 1, holsz + 1);
472
473         /* offset around eof */
474         ret += do_lseek(testnum, 11, fd, filsz, SEEK_HOLE, filsz - 1, filsz);
475         ret += do_lseek(testnum, 12, fd, filsz, SEEK_DATA, filsz - 1, filsz - 1);
476         ret += do_lseek(testnum, 13, fd, filsz, SEEK_HOLE, filsz,     -1);
477         ret += do_lseek(testnum, 14, fd, filsz, SEEK_DATA, filsz,     -1);
478         ret += do_lseek(testnum, 15, fd, filsz, SEEK_HOLE, filsz + 1, -1);
479         ret += do_lseek(testnum, 16, fd, filsz, SEEK_DATA, filsz + 1, -1);
480 out:
481         return ret;
482 }
483
484 /* test a larger full file */
485 static int test03(int fd, int testnum)
486 {
487         char *buf = NULL;
488         int bufsz = alloc_size * 2 + 100;
489         int filsz = bufsz;
490         int ret = -1;
491
492         buf = do_malloc(bufsz);
493         if (!buf)
494                 goto out;
495         memset(buf, 'a', bufsz);
496
497         ret = do_pwrite(fd, buf, bufsz, 0);
498         if (ret)
499                 goto out;
500
501         /* offset at the beginning */
502         ret += do_lseek(testnum,  1, fd, filsz, SEEK_HOLE, 0, bufsz);
503         ret += do_lseek(testnum,  2, fd, filsz, SEEK_HOLE, 1, bufsz);
504         ret += do_lseek(testnum,  3, fd, filsz, SEEK_DATA, 0, 0);
505         ret += do_lseek(testnum,  4, fd, filsz, SEEK_DATA, 1, 1);
506
507         /* offset around eof */
508         ret += do_lseek(testnum,  5, fd, filsz, SEEK_HOLE, bufsz - 1, bufsz);
509         ret += do_lseek(testnum,  6, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
510         ret += do_lseek(testnum,  7, fd, filsz, SEEK_HOLE, bufsz,     -1);
511         ret += do_lseek(testnum,  8, fd, filsz, SEEK_DATA, bufsz,     -1);
512         ret += do_lseek(testnum,  9, fd, filsz, SEEK_HOLE, bufsz + 1, -1);
513         ret += do_lseek(testnum, 10, fd, filsz, SEEK_DATA, bufsz + 1, -1);
514
515 out:
516         do_free(buf);
517         return ret;
518 }
519
520 /* test tiny full file */
521 static int test02(int fd, int testnum)
522 {
523         int ret;
524         char buf[] = "ABCDEFGH";
525         int bufsz, filsz;
526
527         bufsz = strlen(buf);
528         filsz = bufsz;
529
530         /* |- DATA -| */
531
532         ret = do_pwrite(fd, buf, bufsz, 0);
533         if (ret)
534                 goto out;
535
536         ret += do_lseek(testnum, 1, fd, filsz, SEEK_HOLE, 0, filsz);
537         ret += do_lseek(testnum, 2, fd, filsz, SEEK_DATA, 0, 0);
538         ret += do_lseek(testnum, 3, fd, filsz, SEEK_DATA, 1, 1);
539         ret += do_lseek(testnum, 4, fd, filsz, SEEK_HOLE, bufsz - 1, filsz);
540         ret += do_lseek(testnum, 5, fd, filsz, SEEK_DATA, bufsz - 1, bufsz - 1);
541         ret += do_lseek(testnum, 6, fd, filsz, SEEK_HOLE, bufsz,     -1);
542         ret += do_lseek(testnum, 7, fd, filsz, SEEK_DATA, bufsz,     -1);
543         ret += do_lseek(testnum, 8, fd, filsz, SEEK_HOLE, bufsz + 1, -1);
544         ret += do_lseek(testnum, 9, fd, filsz, SEEK_DATA, bufsz + 1, -1);
545
546 out:
547         return ret;
548 }
549
550 /* test empty file */
551 static int test01(int fd, int testnum)
552 {
553         int ret = 0;
554
555         ret += do_lseek(testnum, 1, fd, 0, SEEK_DATA, 0, -1);
556         ret += do_lseek(testnum, 2, fd, 0, SEEK_HOLE, 0, -1);
557         ret += do_lseek(testnum, 3, fd, 0, SEEK_HOLE, 1, -1);
558
559         return ret;
560 }
561
562 struct testrec {
563        int     test_num;
564        int     (*test_func)(int fd, int testnum);
565        char    *test_desc;
566 };
567
568 struct testrec seek_tests[] = {
569        {  1, test01, "Test empty file" },
570        {  2, test02, "Test a tiny full file" },
571        {  3, test03, "Test a larger full file" },
572        {  4, test04, "Test file hole at beg, data at end" },
573        {  5, test05, "Test file data at beg, hole at end" },
574        {  6, test06, "Test file hole data hole data" },
575        {  7, test07, "Test file with unwritten extents, only have dirty pages" },
576        {  8, test08, "Test file with unwritten extents, only have unwritten pages" },
577        {  9, test09, "Test file with unwritten extents, have both dirty && unwritten pages" },
578 };
579
580 static int run_test(struct testrec *tr)
581 {
582         int ret = 0, fd = -1;
583         char filename[255];
584
585         snprintf(filename, sizeof(filename), "%s%02d", base_file_path, tr->test_num);
586
587         fd = do_create(filename);
588         if (fd > -1) {
589                 printf("%02d. %-50s\n", tr->test_num, tr->test_desc);
590                 ret = tr->test_func(fd, tr->test_num);
591                 printf("\n");
592         }
593
594         do_close(fd);
595         return ret;
596 }
597
598 static int test_basic_support(void)
599 {
600         int ret = -1, fd;
601         off_t pos;
602         char *buf = NULL;
603         int bufsz, filsz;
604
605         fd = do_create(base_file_path);
606         if (fd == -1)
607                 goto out;
608
609         get_file_system(fd);
610
611         ret = get_io_sizes(fd);
612         if (ret)
613                 goto out;
614
615         bufsz = alloc_size * 2;
616         filsz = bufsz * 2;
617
618         buf = do_malloc(bufsz);
619         if (!buf)
620                 goto out;
621         memset(buf, 'a', bufsz);
622
623         /* File with 2 allocated blocks.... */
624         ret = do_pwrite(fd, buf, bufsz, 0);
625         if (ret)
626                 goto out;
627
628         /* followed by a hole... */
629         ret = do_truncate(fd, filsz);
630         if (ret)
631                 goto out;
632
633         /* Is SEEK_DATA and SEEK_HOLE supported in the kernel? */
634         pos = lseek(fd, 0, SEEK_DATA);
635         if (pos != -1)
636                 pos = lseek(fd, 0, SEEK_HOLE);
637         if (pos == -1) {
638                 fprintf(stderr, "Kernel does not support llseek(2) extensions "
639                         "SEEK_HOLE and/or SEEK_DATA. Aborting.\n");
640                 ret = -1;
641                 goto out;
642         }
643
644         if (pos == filsz) {
645                 default_behavior = 1;
646                 fprintf(stderr, "File system supports the default behavior.\n");
647         }
648
649         printf("\n");
650
651 out:
652         do_free(buf);
653         do_close(fd);
654         return ret;
655 }
656
657 int main(int argc, char **argv)
658 {
659         int ret = -1;
660         int i = 0;
661         int numtests = sizeof(seek_tests) / sizeof(struct testrec);
662
663         if (argc != 2) {
664                 fprintf(stdout, "Usage: %s base_file_path\n", argv[0]);
665                 return ret;
666         }
667
668         base_file_path = (char *)strdup(argv[1]);
669
670         ret = test_basic_support();
671         if (ret)
672                 goto out;
673
674         for (i = 0; i < numtests; ++i) {
675                 if (ret)
676                         goto out;
677                 run_test(&seek_tests[i]);
678         }
679
680 out:
681         free(base_file_path);
682         return ret;
683 }