alloc: upgrade to fallocate
[xfstests-dev.git] / src / stat_test.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /* Perform various tests on stat and statx output
3  * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  */
6
7 #include <stdarg.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdbool.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/sysmacros.h>
18 #include "statx.h"
19
20 static bool failed = false;
21 static bool is_verbose = 0;
22 static const char *prog;
23 static const char *testfile;
24
25 /* Reference data */
26 static struct statx ref;
27 static struct statx_timestamp origin;
28 static bool ref_set, origin_set;
29
30 /*
31  * Field IDs, sorted for bsearch() on field_list[].
32  */
33 enum fields {
34         stx_atime_tv_nsec,
35         stx_atime_tv_sec,
36         stx_attributes,
37         stx_blksize,
38         stx_blocks,
39         stx_btime_tv_nsec,
40         stx_btime_tv_sec,
41         stx_ctime_tv_nsec,
42         stx_ctime_tv_sec,
43         stx_dev_major,
44         stx_dev_minor,
45         stx_gid,
46         stx_ino,
47         stx_mask,
48         stx_mode,
49         stx_mtime_tv_nsec,
50         stx_mtime_tv_sec,
51         stx_nlink,
52         stx_rdev_major,
53         stx_rdev_minor,
54         stx_size,
55         stx_type,
56         stx_uid,
57         nr__fields
58 };
59
60 struct field {
61         const char *name;               /* Name on command line */
62         unsigned int mask_bit;
63 };
64
65 /*
66  * List of fields, sorted for bsearch().
67  */
68 static const struct field field_list[nr__fields] = {
69         [stx_atime_tv_nsec]     = { "stx_atime.tv_nsec",        STATX_ATIME },
70         [stx_atime_tv_sec]      = { "stx_atime.tv_sec",         STATX_ATIME },
71         [stx_attributes]        = { "stx_attributes",           0 },
72         [stx_blksize]           = { "stx_blksize",              0 },
73         [stx_blocks]            = { "stx_blocks",               STATX_BLOCKS },
74         [stx_btime_tv_nsec]     = { "stx_btime.tv_nsec",        STATX_BTIME },
75         [stx_btime_tv_sec]      = { "stx_btime.tv_sec",         STATX_BTIME },
76         [stx_ctime_tv_nsec]     = { "stx_ctime.tv_nsec",        STATX_CTIME },
77         [stx_ctime_tv_sec]      = { "stx_ctime.tv_sec",         STATX_CTIME },
78         [stx_dev_major]         = { "stx_dev_major",            0 },
79         [stx_dev_minor]         = { "stx_dev_minor",            0 },
80         [stx_gid]               = { "stx_gid",                  STATX_GID },
81         [stx_ino]               = { "stx_ino",                  STATX_INO },
82         [stx_mask]              = { "stx_mask",                 0 },
83         [stx_mode]              = { "stx_mode",                 STATX_MODE },
84         [stx_mtime_tv_nsec]     = { "stx_mtime.tv_nsec",        STATX_MTIME },
85         [stx_mtime_tv_sec]      = { "stx_mtime.tv_sec",         STATX_MTIME },
86         [stx_nlink]             = { "stx_nlink",                STATX_NLINK },
87         [stx_rdev_major]        = { "stx_rdev_major",           0 },
88         [stx_rdev_minor]        = { "stx_rdev_minor",           0 },
89         [stx_size]              = { "stx_size",                 STATX_SIZE },
90         [stx_type]              = { "stx_type",                 STATX_TYPE },
91         [stx_uid]               = { "stx_uid",                  STATX_UID },
92 };
93
94 static int field_cmp(const void *_key, const void *_p)
95 {
96         const char *key = _key;
97         const struct field *p = _p;
98         return strcmp(key, p->name);
99 }
100
101 /*
102  * Sorted list of attribute flags for bsearch().
103  */
104 struct attr_name {
105         const char      *name;
106         __u64           attr_flag;
107 };
108
109 static const struct attr_name attr_list[] = {
110         { "append",     STATX_ATTR_APPEND },
111         { "automount",  STATX_ATTR_AUTOMOUNT },
112         { "compressed", STATX_ATTR_COMPRESSED },
113         { "encrypted",  STATX_ATTR_ENCRYPTED },
114         { "immutable",  STATX_ATTR_IMMUTABLE },
115         { "nodump",     STATX_ATTR_NODUMP },
116 };
117
118 static int attr_name_cmp(const void *_key, const void *_p)
119 {
120         const char *key = _key;
121         const struct attr_name *p = _p;
122         return strcmp(key, p->name);
123 }
124
125 struct file_type {
126         const char *name;
127         mode_t mode;
128 };
129
130 /*
131  * List of file types.
132  */
133 static const struct file_type file_types[] = {
134         { "fifo",       S_IFIFO },
135         { "char",       S_IFCHR },
136         { "dir",        S_IFDIR },
137         { "block",      S_IFBLK },
138         { "file",       S_IFREG },
139         { "sym",        S_IFLNK },
140         { "sock",       S_IFSOCK },
141         { NULL }
142 };
143
144 static __attribute__((noreturn))
145 void format(void)
146 {
147         fprintf(stderr, "usage: %s --check-statx\n", prog);
148         fprintf(stderr, "usage: %s [-v] [-m<mask>] <testfile> [checks]\n", prog);
149         fprintf(stderr, "\t<mask> can be basic, all or a number; all is the default\n");
150         fprintf(stderr, "checks is a list of zero or more of:\n");
151         fprintf(stderr, "\tattr=[+-]<name> -- check an attribute in stx_attributes\n");
152         fprintf(stderr, "\t\tappend -- The file is marked as append only\n");
153         fprintf(stderr, "\t\tautomount -- The object is an automount point\n");
154         fprintf(stderr, "\t\tcompressed -- The file is marked as compressed\n");
155         fprintf(stderr, "\t\tencrypted -- The file is marked as encrypted\n");
156         fprintf(stderr, "\t\timmutable -- The file is marked as immutable\n");
157         fprintf(stderr, "\t\tnodump -- The file is marked as no-dump\n");
158         fprintf(stderr, "\tcmp_ref -- check that the reference file has identical stats\n");
159         fprintf(stderr, "\tref=<file> -- get reference stats from file\n");
160         fprintf(stderr, "\tstx_<field>=<val> -- statx field value check\n");
161         fprintf(stderr, "\tts=<a>,<b> -- timestamp a <= b, where a and b can each be one of:\n");
162         fprintf(stderr, "\t\t[abcm] -- the timestamps from testfile\n");
163         fprintf(stderr, "\t\t[ABCM] -- the timestamps from the reference file\n");
164         fprintf(stderr, "\t\t0 -- the origin timestamp\n");
165         fprintf(stderr, "\tts_origin=<sec>.<nsec> -- set the origin timestamp\n");
166         fprintf(stderr, "\tts_order -- check the timestamp order\n");
167         fprintf(stderr, "\t\t(for stx_type, fifo char dir, block, file, sym, sock can be used)\n");
168         exit(2);
169 }
170
171 static __attribute__((noreturn, format(printf, 1, 2)))
172 void bad_arg(const char *fmt, ...)
173 {
174         va_list va;
175
176         va_start(va, fmt);
177         vfprintf(stderr, fmt, va);
178         va_end(va);
179         exit(2);
180 }
181
182 static __attribute__((format(printf, 1, 2)))
183 void verbose(const char *fmt, ...)
184 {
185         va_list va;
186
187         if (is_verbose) {
188                 va_start(va, fmt);
189                 fputs(" - ", stdout);
190                 vprintf(fmt, va);
191                 va_end(va);
192         }
193 }
194
195 static __attribute__((format(printf, 2, 3)))
196 void check(bool good, const char *fmt, ...)
197 {
198         va_list va;
199
200         if (!good) {
201                 va_start(va, fmt);
202                 fputs("[!] ", stdout);
203                 vprintf(fmt, va);
204                 va_end(va);
205                 failed = true;
206         }
207 }
208
209 /*
210  * Compare the contents of a statx struct with that of a stat struct and check
211  * that they're the same.
212  */
213 static void cmp_statx(const struct statx *stx, const struct stat *st)
214 {
215 #define cmp(fmt, x)                                                   \
216         do {                                                          \
217                 check(stx->stx_##x == st->st_##x,                     \
218                       "stat.%s differs, "fmt" != "fmt"\n",            \
219                       #x,                                             \
220                       (unsigned long long)stx->stx_##x,               \
221                       (unsigned long long)st->st_##x);                \
222         } while (0)
223
224         cmp("%llu", blksize);
225         cmp("%llu", nlink);
226         cmp("%llu", uid);
227         cmp("%llu", gid);
228         cmp("%llo", mode);
229         cmp("%llu", ino);
230         cmp("%llu", size);
231         cmp("%llu", blocks);
232
233 #define devcmp(x) \
234         do {                                                            \
235                 check(stx->stx_##x##_major == major(st->st_##x),        \
236                       "stat.%s.major differs, %u != %u\n",              \
237                       #x,                                               \
238                       stx->stx_##x##_major,                             \
239                       major(st->st_##x));                               \
240                 check(stx->stx_##x##_minor == minor(st->st_##x),        \
241                       "stat.%s.minor differs, %u != %u\n",              \
242                       #x,                                               \
243                       stx->stx_##x##_minor,                             \
244                       minor(st->st_##x));                               \
245         } while (0)
246
247         devcmp(dev);
248         devcmp(rdev);
249
250 #define timecmp(x) \
251         do {                                                            \
252                 check(stx->stx_##x##time.tv_sec == st->st_##x##tim.tv_sec, \
253                       "stat.%stime.tv_sec differs, %lld != %lld\n",     \
254                       #x,                                               \
255                       (long long)stx->stx_##x##time.tv_sec,             \
256                       (long long)st->st_##x##tim.tv_sec);               \
257                 check(stx->stx_##x##time.tv_nsec == st->st_##x##tim.tv_nsec, \
258                       "stat.%stime.tv_nsec differs, %lld != %lld\n",    \
259                       #x,                                               \
260                       (long long)stx->stx_##x##time.tv_nsec,            \
261                       (long long)st->st_##x##tim.tv_nsec);              \
262         } while (0)
263
264         timecmp(a);
265         timecmp(c);
266         timecmp(m);
267 }
268
269 /*
270  * Set origin timestamp from a "<sec>.<nsec>" string.
271  */
272 static void set_origin_timestamp(const char *arg)
273 {
274         long long sec;
275         int nsec;
276
277         switch (sscanf(arg, "%lld.%d", &sec, &nsec)) {
278         case 0:
279                 bad_arg("ts_origin= missing seconds value");
280         case 1:
281                 bad_arg("ts_origin= missing nanoseconds value");
282         default:
283                 origin.tv_sec = sec;
284                 origin.tv_nsec = nsec;
285                 origin_set = true;
286                 break;
287         }
288 }
289
290 /*
291  * Get reference stats from a file.
292  */
293 static void get_reference(const char *file, unsigned int mask)
294 {
295         int ret;
296
297         if (!*file)
298                 bad_arg("ref= requires a filename\n");
299
300         memset(&ref, 0xfb, sizeof(ref));
301         ret = xfstests_statx(AT_FDCWD, file, AT_SYMLINK_NOFOLLOW, mask, &ref);
302         switch (ret) {
303         case 0:
304                 ref_set = true;
305                 break;
306         case -1:
307                 perror(file);
308                 exit(1);
309         default:
310                 fprintf(stderr, "Unexpected return %d from statx()\n", ret);
311                 exit(1);
312         }
313 }
314
315 /*
316  * Check a pair of timestamps.
317  */
318 static void check_earlier(const struct statx_timestamp *A,
319                           const struct statx_timestamp *B,
320                           const char *A_name,
321                           const char *B_name)
322 {
323
324         check((B->tv_sec - A->tv_sec) >= 0,
325               "%s.sec is before %s.sec (%lld < %lld)\n",
326               B_name, A_name, B->tv_sec, A->tv_sec);
327
328         if (B->tv_sec == A->tv_sec)
329                 check((B->tv_nsec - A->tv_nsec) >= 0,
330                       "%s.nsec is before %s.nsec (%d < %d)\n",
331                       B_name, A_name, B->tv_nsec, A->tv_nsec);
332 }
333
334 /*
335  * Check that the timestamps are reasonably ordered.
336  *
337  * We require the following to hold true immediately after creation if the
338  * relevant timestamps exist on the filesystem:
339  *
340  *      btime <= atime
341  *      btime <= mtime <= ctime
342  */
343 static void check_timestamp_order(const struct statx *stx)
344 {
345         if ((stx->stx_mask & (STATX_BTIME | STATX_ATIME)) == (STATX_BTIME | STATX_ATIME))
346                 check_earlier(&stx->stx_btime, &stx->stx_atime, "btime", "atime");
347         if ((stx->stx_mask & (STATX_BTIME | STATX_MTIME)) == (STATX_BTIME | STATX_MTIME))
348                 check_earlier(&stx->stx_btime, &stx->stx_mtime, "btime", "mtime");
349         if ((stx->stx_mask & (STATX_BTIME | STATX_CTIME)) == (STATX_BTIME | STATX_CTIME))
350                 check_earlier(&stx->stx_btime, &stx->stx_ctime, "btime", "ctime");
351         if ((stx->stx_mask & (STATX_MTIME | STATX_CTIME)) == (STATX_MTIME | STATX_CTIME))
352                 check_earlier(&stx->stx_mtime, &stx->stx_ctime, "mtime", "ctime");
353 }
354
355 /*
356  * Check that the second timestamp is the same as or after the first timestamp.
357  */
358 static void check_timestamp(const struct statx *stx, char *arg)
359 {
360         const struct statx_timestamp *a, *b;
361         const char *an, *bn;
362         unsigned int mask;
363
364         if (strlen(arg) != 3 || arg[1] != ',')
365                 bad_arg("ts= requires <a>,<b>\n");
366
367         switch (arg[0]) {
368         case 'a': a = &stx->stx_atime;  an = "atime";   mask = STATX_ATIME; break;
369         case 'b': a = &stx->stx_btime;  an = "btime";   mask = STATX_BTIME; break;
370         case 'c': a = &stx->stx_ctime;  an = "ctime";   mask = STATX_CTIME; break;
371         case 'm': a = &stx->stx_mtime;  an = "mtime";   mask = STATX_MTIME; break;
372         case 'A': a = &ref.stx_atime;   an = "ref_a";   mask = STATX_ATIME; break;
373         case 'B': a = &ref.stx_btime;   an = "ref_b";   mask = STATX_BTIME; break;
374         case 'C': a = &ref.stx_ctime;   an = "ref_c";   mask = STATX_CTIME; break;
375         case 'M': a = &ref.stx_mtime;   an = "ref_m";   mask = STATX_MTIME; break;
376         case '0': a = &origin;          an = "origin";  mask = 0; break;
377         default:
378                 bad_arg("ts= timestamp '%c' not supported\n", arg[0]);
379         }
380
381         if (arg[0] == '0') {
382                 if (!origin_set)
383                         bad_arg("ts= timestamp '%c' requires origin= first\n", arg[0]);
384         } else if (arg[0] <= 'Z') {
385                 if (!ref_set)
386                         bad_arg("ts= timestamp '%c' requires ref= first\n", arg[0]);
387                 if (!(ref.stx_mask & mask))
388                         return;
389         } else {
390                 if (!(stx->stx_mask & mask))
391                         return;
392         }
393
394         switch (arg[2]) {
395         case 'a': b = &stx->stx_atime;  bn = "atime";   mask = STATX_ATIME; break;
396         case 'b': b = &stx->stx_btime;  bn = "btime";   mask = STATX_BTIME; break;
397         case 'c': b = &stx->stx_ctime;  bn = "ctime";   mask = STATX_CTIME; break;
398         case 'm': b = &stx->stx_mtime;  bn = "mtime";   mask = STATX_MTIME; break;
399         case 'A': b = &ref.stx_atime;   bn = "ref_a";   mask = STATX_ATIME; break;
400         case 'B': b = &ref.stx_btime;   bn = "ref_b";   mask = STATX_BTIME; break;
401         case 'C': b = &ref.stx_ctime;   bn = "ref_c";   mask = STATX_CTIME; break;
402         case 'M': b = &ref.stx_mtime;   bn = "ref_m";   mask = STATX_MTIME; break;
403         case '0': b = &origin;          bn = "origin";  mask = 0; break;
404         default:
405                 bad_arg("ts= timestamp '%c' not supported\n", arg[2]);
406         }
407
408         if (arg[2] == '0') {
409                 if (!origin_set)
410                         bad_arg("ts= timestamp '%c' requires origin= first\n", arg[0]);
411         } else if (arg[2] <= 'Z') {
412                 if (!ref_set)
413                         bad_arg("ts= timestamp '%c' requires ref= first\n", arg[2]);
414                 if (!(ref.stx_mask & mask))
415                         return;
416         } else {
417                 if (!(stx->stx_mask & mask))
418                         return;
419         }
420
421         verbose("check %s <= %s\n", an, bn);
422         check_earlier(a, b, an, bn);
423 }
424
425 /*
426  * Compare to reference file.
427  */
428 static void cmp_ref(const struct statx *stx, unsigned int mask)
429 {
430 #undef cmp
431 #define cmp(fmt, x)                                                     \
432         do {                                                            \
433                 check(stx->x == ref.x,                                  \
434                       "attr '%s' differs from ref file, "fmt" != "fmt"\n", \
435                       #x,                                               \
436                       (unsigned long long)stx->x,                       \
437                       (unsigned long long)ref.x);                       \
438         } while (0)
439
440         cmp("%llx", stx_mask);
441         cmp("%llx", stx_attributes);
442         cmp("%llu", stx_blksize);
443         cmp("%llu", stx_attributes);
444         cmp("%llu", stx_nlink);
445         cmp("%llu", stx_uid);
446         cmp("%llu", stx_gid);
447         cmp("%llo", stx_mode);
448         cmp("%llu", stx_ino);
449         cmp("%llu", stx_size);
450         cmp("%llu", stx_blocks);
451         cmp("%lld", stx_atime.tv_sec);
452         cmp("%lld", stx_atime.tv_nsec);
453         cmp("%lld", stx_btime.tv_sec);
454         cmp("%lld", stx_btime.tv_nsec);
455         cmp("%lld", stx_ctime.tv_sec);
456         cmp("%lld", stx_ctime.tv_nsec);
457         cmp("%lld", stx_mtime.tv_sec);
458         cmp("%lld", stx_mtime.tv_nsec);
459         cmp("%llu", stx_rdev_major);
460         cmp("%llu", stx_rdev_minor);
461         cmp("%llu", stx_dev_major);
462         cmp("%llu", stx_dev_minor);
463 }
464
465 /*
466  * Check an field restriction.  Specified on the command line as a key=val pair
467  * in the checks section.  For instance:
468  *
469  *      stx_type=char
470  *      stx_mode=0644
471  */
472 static void check_field(const struct statx *stx, char *arg)
473 {
474         const struct file_type *type;
475         const struct field *field;
476         unsigned long long ucheck, uval = 0;
477         long long scheck, sval = 0;
478         char *key, *val, *p;
479
480         verbose("check %s\n", arg);
481
482         key = arg;
483         val = strchr(key, '=');
484         if (!val || !val[1])
485                 bad_arg("%s check requires value\n", key);
486         *(val++) = 0;
487
488         field = bsearch(key, field_list, nr__fields, sizeof(*field), field_cmp);
489         if (!field)
490                 bad_arg("Field '%s' not supported\n", key);
491
492         /* Read the stat information specified by the key. */
493         switch ((enum fields)(field - field_list)) {
494         case stx_mask:          uval = stx->stx_mask;           break;
495         case stx_blksize:       uval = stx->stx_blksize;        break;
496         case stx_attributes:    uval = stx->stx_attributes;     break;
497         case stx_nlink:         uval = stx->stx_nlink;          break;
498         case stx_uid:           uval = stx->stx_uid;            break;
499         case stx_gid:           uval = stx->stx_gid;            break;
500         case stx_type:          uval = stx->stx_mode & ~07777;  break;
501         case stx_mode:          uval = stx->stx_mode & 07777;   break;
502         case stx_ino:           uval = stx->stx_ino;            break;
503         case stx_size:          uval = stx->stx_size;           break;
504         case stx_blocks:        uval = stx->stx_blocks;         break;
505         case stx_rdev_major:    uval = stx->stx_rdev_major;     break;
506         case stx_rdev_minor:    uval = stx->stx_rdev_minor;     break;
507         case stx_dev_major:     uval = stx->stx_dev_major;      break;
508         case stx_dev_minor:     uval = stx->stx_dev_minor;      break;
509
510         case stx_atime_tv_sec:  sval = stx->stx_atime.tv_sec;   break;
511         case stx_atime_tv_nsec: sval = stx->stx_atime.tv_nsec;  break;
512         case stx_btime_tv_sec:  sval = stx->stx_btime.tv_sec;   break;
513         case stx_btime_tv_nsec: sval = stx->stx_btime.tv_nsec;  break;
514         case stx_ctime_tv_sec:  sval = stx->stx_ctime.tv_sec;   break;
515         case stx_ctime_tv_nsec: sval = stx->stx_ctime.tv_nsec;  break;
516         case stx_mtime_tv_sec:  sval = stx->stx_mtime.tv_sec;   break;
517         case stx_mtime_tv_nsec: sval = stx->stx_mtime.tv_nsec;  break;
518         default:
519                 break;
520         }
521
522         /* Parse the specified value as signed or unsigned as
523          * appropriate and compare to the stat information.
524          */
525         switch ((enum fields)(field - field_list)) {
526         case stx_mask:
527         case stx_attributes:
528                 ucheck = strtoull(val, &p, 0);
529                 if (*p)
530                         bad_arg("Field '%s' requires unsigned integer\n", key);
531                 check(uval == ucheck,
532                       "%s differs, 0x%llx != 0x%llx\n", key, uval, ucheck);
533                 break;
534
535         case stx_type:
536                 for (type = file_types; type->name; type++) {
537                         if (strcmp(type->name, val) == 0) {
538                                 ucheck = type->mode;
539                                 goto octal_check;
540                         }
541                 }
542
543                 /* fall through */
544
545         case stx_mode:
546                 ucheck = strtoull(val, &p, 0);
547                 if (*p)
548                         bad_arg("Field '%s' requires unsigned integer\n", key);
549         octal_check:
550                 check(uval == ucheck,
551                       "%s differs, 0%llo != 0%llo\n", key, uval, ucheck);
552                 break;
553
554         case stx_blksize:
555         case stx_nlink:
556         case stx_uid:
557         case stx_gid:
558         case stx_ino:
559         case stx_size:
560         case stx_blocks:
561         case stx_rdev_major:
562         case stx_rdev_minor:
563         case stx_dev_major:
564         case stx_dev_minor:
565                 ucheck = strtoull(val, &p, 0);
566                 if (*p)
567                         bad_arg("Field '%s' requires unsigned integer\n", key);
568                 check(uval == ucheck,
569                       "%s differs, %llu != %llu\n", key, uval, ucheck);
570                 break;
571
572         case stx_atime_tv_sec:
573         case stx_atime_tv_nsec:
574         case stx_btime_tv_sec:
575         case stx_btime_tv_nsec:
576         case stx_ctime_tv_sec:
577         case stx_ctime_tv_nsec:
578         case stx_mtime_tv_sec:
579         case stx_mtime_tv_nsec:
580                 scheck = strtoll(val, &p, 0);
581                 if (*p)
582                         bad_arg("Field '%s' requires integer\n", key);
583                 check(sval == scheck,
584                       "%s differs, %lld != %lld\n", key, sval, scheck);
585                 break;
586
587         default:
588                 break;
589         }
590 }
591
592 /*
593  * Check attributes in stx_attributes.  When stx_attributes_mask gets in
594  * upstream, we will need to consider that also.
595  */
596 static void check_attribute(const struct statx *stx, char *arg)
597 {
598         const struct attr_name *p;
599         __u64 attr;
600         bool set;
601
602         verbose("check attr %s\n", arg);
603         switch (arg[0]) {
604         case '+': set = true;   break;
605         case '-': set = false;  break;
606         default:
607                 bad_arg("attr flag must be marked + (set) or - (unset)\n");
608         }
609         arg++;
610
611         p = bsearch(arg, attr_list, sizeof(attr_list) / sizeof(attr_list[0]),
612                     sizeof(attr_list[0]), attr_name_cmp);
613         if (!p)
614                 bad_arg("Unrecognised attr name '%s'\n", arg);
615
616         attr = p->attr_flag;
617         if (set) {
618                 check((stx->stx_attributes & attr) == attr,
619                       "Attribute %s should be set\n", arg);
620         } else {
621                 check((stx->stx_attributes & attr) == 0,
622                       "Attribute %s should be unset\n", arg);
623         }
624 }
625
626 /*
627  * Do the testing.
628  */
629 int main(int argc, char **argv)
630 {
631         struct statx stx;
632         struct stat st;
633         unsigned int mask = STATX_ALL;
634         unsigned int atflags = AT_STATX_SYNC_AS_STAT;
635         char *p;
636         int c, ret;
637
638         if (argc == 2 && strcmp(argv[1], "--check-statx") == 0) {
639                 errno = 0;
640                 return (xfstests_statx(AT_FDCWD, "/", 0, 0, &stx) == -1 &&
641                         errno == ENOSYS) ? 1 : 0;
642         }
643
644         prog = argv[0];
645         while (c = getopt(argc, argv, "+DFm:v"),
646                c != -1
647                ) {
648                 switch (c) {
649                 case 'F':
650                         atflags &= ~AT_STATX_SYNC_TYPE;
651                         atflags |= AT_STATX_FORCE_SYNC;
652                         break;
653                 case 'D':
654                         atflags &= ~AT_STATX_SYNC_TYPE;
655                         atflags |= AT_STATX_DONT_SYNC;
656                         break;
657                 case 'm':
658                         if (strcmp(optarg, "basic") == 0) {
659                                 mask = STATX_BASIC_STATS;
660                         } else if (strcmp(optarg, "all") == 0) {
661                                 mask = STATX_ALL;
662                         } else {
663                                 mask = strtoul(optarg, &p, 0);
664                                 if (*p)
665                                         format();
666                         }
667                         break;
668                 case 'v':
669                         is_verbose = 1;
670                         break;
671                 default:
672                         format();
673                 }
674         }
675
676         argc -= optind;
677         argv += optind;
678         if (argc < 1)
679                 format();
680         testfile = argv[0];
681         argv += 1;
682
683         /* Gather the stats.  We want both statx and stat so that we can
684          * compare what's in the buffers.
685          */
686         verbose("call statx %s\n", testfile);
687         memset(&stx, 0xfb, sizeof(stx));
688         ret = xfstests_statx(AT_FDCWD, testfile, atflags | AT_SYMLINK_NOFOLLOW,
689                              mask, &stx);
690         switch (ret) {
691         case 0:
692                 break;
693         case -1:
694                 perror(testfile);
695                 exit(1);
696         default:
697                 fprintf(stderr, "Unexpected return %d from statx()\n", ret);
698                 exit(1);
699         }
700
701         verbose("call stat %s\n", testfile);
702         ret = fstatat(AT_FDCWD, testfile, &st, AT_SYMLINK_NOFOLLOW);
703         switch (ret) {
704         case 0:
705                 break;
706         case -1:
707                 perror(testfile);
708                 exit(1);
709         default:
710                 fprintf(stderr, "Unexpected return %d from stat()\n", ret);
711                 exit(1);
712         }
713
714         verbose("compare statx and stat\n");
715         cmp_statx(&stx, &st);
716
717         /* Display the available timestamps */
718         verbose("begin time %llu.%09u\n", origin.tv_sec, origin.tv_nsec);
719         if (stx.stx_mask & STATX_BTIME)
720                 verbose("     btime %llu.%09u\n", stx.stx_btime.tv_sec, stx.stx_btime.tv_nsec);
721         if (stx.stx_mask & STATX_ATIME)
722                 verbose("     atime %llu.%09u\n", stx.stx_atime.tv_sec, stx.stx_atime.tv_nsec);
723         if (stx.stx_mask & STATX_MTIME)
724                 verbose("     mtime %llu.%09u\n", stx.stx_mtime.tv_sec, stx.stx_mtime.tv_nsec);
725         if (stx.stx_mask & STATX_CTIME)
726                 verbose("     ctime %llu.%09u\n", stx.stx_ctime.tv_sec, stx.stx_ctime.tv_nsec);
727
728         /* Handle additional checks the user specified */
729         for (; *argv; argv++) {
730                 char *arg = *argv;
731
732                 if (strncmp("attr=", arg, 5) == 0) {
733                         /* attr=[+-]<attr> - check attribute flag */
734                         check_attribute(&stx, arg + 5);
735                         continue;
736                 }
737
738                 if (strcmp("cmp_ref", arg) == 0) {
739                         /* cmp_ref - check ref file has same stats */
740                         cmp_ref(&stx, mask);
741                         continue;
742                 }
743
744                 if (strncmp(arg, "stx_", 4) == 0) {
745                         /* stx_<field>=<n> - check field set to n */
746                         check_field(&stx, *argv);
747                         continue;
748                 }
749
750                 if (strncmp("ref=", arg, 4) == 0) {
751                         /* ref=<file> - set reference stats from file */
752                         get_reference(arg + 4, mask);
753                         continue;
754                 }
755
756                 if (strcmp("ts_order", arg) == 0) {
757                         /* ts_order - check timestamp order */
758                         check_timestamp_order(&stx);
759                         continue;
760                 }
761
762                 if (strncmp("ts_origin=", arg, 10) == 0) {
763                         /* ts_origin=<sec>.<nsec> - set origin timestamp */
764                         set_origin_timestamp(arg + 10);
765                         continue;
766                 }
767
768                 if (strncmp("ts=", arg, 3) == 0) {
769                         /* ts=<a>,<b> - check timestamp b is same as a or after */
770                         check_timestamp(&stx, arg + 3);
771                         continue;
772                 }
773
774                 bad_arg("check '%s' not supported\n", arg);
775         }
776
777         if (failed) {
778                 printf("Failed\n");
779                 exit(1);
780         }
781
782         verbose("Success\n");
783         exit(0);
784 }