Whamcloud - gitweb
LU-10934 llite: integrate statx() API with Lustre
[fs/lustre-release.git] / lustre / tests / statx.c
1 /*
2  * LGPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the GNU Lesser General Public License
8  * (LGPL) version 2.1 or (at your discretion) any later version.
9  * (LGPL) version 2.1 accompanies this distribution, and is available at
10  * http://www.gnu.org/licenses/lgpl-2.1.html
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * LGPL HEADER END
18  */
19 /*
20  * Copyright (c) 2019, DDN Storage Corporation.
21  */
22 /*
23  * This file is part of Lustre, http://www.lustre.org/
24  */
25 /*
26  *
27  * Test for Lustre statx().
28  * It uses some code in coreutils ('ls.c' and 'stat.c') for reference.
29  *
30  * Author: Qian Yingjin <qian@ddn.com>
31  */
32 #define _ATFILE_SOURCE
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <getopt.h>
37 #include <limits.h>
38 #include <unistd.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <time.h>
42 #include <sys/time.h>
43 #include <pwd.h>
44 #include <grp.h>
45 #include <dirent.h>
46 #include <sys/syscall.h>
47 #include <sys/types.h>
48 #include <sys/param.h>
49 #include <sys/sysmacros.h>
50 #include <inttypes.h>
51 #include <fcntl.h>
52 #include <locale.h>
53 #include <libiberty.h>
54 #include <linux/lustre/lustre_user.h>
55
56 #ifdef HAVE_SELINUX
57 #include <selinux/selinux.h>
58 #endif
59
60 /* Factor out some of the common --help and --version processing code. */
61
62 /* These enum values cannot possibly conflict with the option values
63  * ordinarily used by commands, including CHAR_MAX + 1, etc.  Avoid
64  * CHAR_MIN - 1, as it may equal -1, the getopt end-of-options value.
65  */
66 enum {
67         PRINTF_OPTION = (CHAR_MAX + 1),
68         GETOPT_HELP_CHAR = (CHAR_MIN - 2),
69         GETOPT_VERSION_CHAR = (CHAR_MIN - 3)
70 };
71
72 static bool o_quiet;
73
74 #ifdef __NR_statx
75 #ifndef HAVE_STATX
76 static __attribute__((unused))
77 ssize_t statx(int dfd, const char *filename, int flags,
78               unsigned int mask, struct statx *buffer)
79 {
80         return syscall(__NR_statx, dfd, filename, flags, mask, buffer);
81 }
82 #endif /* HAVE_STATX */
83
84 /* coreutils/lib/intprops.h */
85 #define _GL_SIGNED_TYPE_OR_EXPR(t) TYPE_SIGNED(__typeof__(t))
86
87 /* Bound on length of the string representing an unsigned integer
88  * value representable in B bits.  log10 (2.0) < 146/485.  The
89  * smallest value of B where this bound is not tight is 2621.
90  */
91 #define INT_BITS_STRLEN_BOUND(b) (((b) * 146 + 484) / 485)
92
93 /* The width in bits of the integer type or expression T.
94  * Do not evaluate T.
95  * Padding bits are not supported; this is checked at compile-time below.
96  */
97 #define TYPE_WIDTH(t) (sizeof(t) * CHAR_BIT)
98
99 /* Bound on length of the string representing an integer type or expression T.
100  * Subtract 1 for the sign bit if T is signed, and then add 1 more for
101  * a minus sign if needed.
102  *
103  * Because _GL_SIGNED_TYPE_OR_EXPR sometimes returns 1 when its argument is
104  * unsigned, this macro may overestimate the true bound by one byte when
105  * applied to unsigned types of size 2, 4, 16, ... bytes.
106  */
107 #define INT_STRLEN_BOUND(t)                                     \
108         (INT_BITS_STRLEN_BOUND(TYPE_WIDTH(t) - _GL_SIGNED_TYPE_OR_EXPR(t)) \
109         + _GL_SIGNED_TYPE_OR_EXPR(t))
110
111 /* Bound on buffer size needed to represent an integer type or expression T,
112  * including the terminating null.
113  */
114 #define INT_BUFSIZE_BOUND(t) (INT_STRLEN_BOUND(t) + 1)
115
116 /* The maximum and minimum values for the integer type T.  */
117 #define TYPE_MINIMUM(t) ((t)~TYPE_MAXIMUM(t))
118 #define TYPE_MAXIMUM(t)                                         \
119         ((t) (!TYPE_SIGNED(t)                                   \
120         ? (t)-1                                         \
121         : ((((t)1 << (TYPE_WIDTH(t) - 2)) - 1) * 2 + 1)))
122
123 static bool o_dir_list;
124 static bool long_format; /* use a long listing format */
125
126 /* Current time in seconds and nanoseconds since 1970, updated as
127  * needed when deciding whether a file is recent.
128  */
129 static struct timespec current_time;
130
131 /* FIXME: these are used by printf.c, too */
132 #define isodigit(c) ('0' <= (c) && (c) <= '7')
133 #define octtobin(c) ((c) - '0')
134 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
135                      (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
136
137 #define ISDIGIT(c) ((unsigned int)(c) - '0' <= 9)
138
139 /* True if the real type T is signed.  */
140 #define TYPE_SIGNED(t) (!((t)0 < (t)-1))
141
142 static char const digits[] = "0123456789";
143
144 /* Flags that are portable for use in printf, for at least one
145  * conversion specifier; make_format removes unportable flags as
146  * needed for particular specifiers.  The glibc 2.2 extension "I" is
147  * listed here; it is removed by make_format because it has undefined
148  * behavior elsewhere and because it is incompatible with
149  * out_epoch_sec.
150  */
151 static char const printf_flags[] = "'-+ #0I";
152
153 /* Formats for the --terse option. */
154 static char const fmt_terse_fs[] = "%n %i %l %t %s %S %b %f %a %c %d\n";
155 static char const fmt_terse_regular[] = "%n %s %b %f %u %g %D %i %h %t %T"
156                                         " %X %Y %Z %W %o\n";
157 static char const fmt_terse_selinux[] = "%n %s %b %f %u %g %D %i %h %t %T"
158                                         " %X %Y %Z %W %o %C\n";
159
160 static char *format;
161
162 /* Whether to follow symbolic links;  True for --dereference (-L).  */
163 static bool follow_links;
164
165 /* Whether to interpret backslash-escape sequences.
166  * True for --printf=FMT, not for --format=FMT (-c).
167  */
168 static bool interpret_backslash_escapes;
169
170 /* The trailing delimiter string:
171  * "" for --printf=FMT, "\n" for --format=FMT (-c).
172  */
173 static char const *trailing_delim = "";
174
175 /* The representation of the decimal point in the current locale.  */
176 static char const *decimal_point;
177 static size_t decimal_point_len;
178
179 /* Convert a possibly-signed character to an unsigned character.  This is
180  * a bit safer than casting to unsigned char, since it catches some type
181  * errors that the cast doesn't.
182  */
183 static inline unsigned char to_uchar(char ch)
184 {
185         return ch;
186 }
187
188 void usage(char *prog)
189 {
190         printf("Usage: %s [options] <FILE>...\n", prog);
191         printf("Display file status via statx() syscall.\n"
192                "List information about the FILE "
193                "(the current diretory by default) via statx() syscall.\n"
194                "options:\n"
195                "\t-L --dereference   follow links\n"
196                "\t--cached=MODE      specify how to use cached attributes;\n"
197                "\t                     See MODE below\n"
198                "\t-c --format=FORMAT use the specified FORMAT instead of the "
199                "default;\n"
200                "\t                     output a newline after each use of "
201                "FORMAT\n"
202                "\t-t, --terse        print the information in terse form\n"
203                "\t-D --dir           list information about the FILE (ls)\n"
204                "\t-l                 use a long listing format\n"
205                "\t-q --quiet         do not display results, test only\n\n"
206                "The --cached MODE argument can be; always, never, or default.\n"
207                "`always` will use cached attributes if available, while\n"
208                "`never` will try to synchronize with the latest attributes,\n"
209                "and `default` will leave it up to the underlying file system.\n"
210                "\n"
211                "The valid format sequences for files (without --file-system):\n"
212                "\n"
213                "\t%%a  access rights in octal (note '#' and '0' printf flags)\n"
214                "\t%%A   access rights in human readable form\n"
215                "\t%%b   number of blocks allocated (see %%B)\n"
216                "\t%%B   the size in bytes of each block reported by %%b\n"
217                "\t%%C   SELinux security context string\n"
218                "\t%%d   device number in decimal\n"
219                "\t%%D   device number in hex\n"
220                "\t%%f   raw mode in hex\n"
221                "\t%%F   file type\n"
222                "\t%%g   group ID of owner\n"
223                "\t%%G   group name of owner\n"
224                "\t%%h   number of hard links\n"
225                "\t%%i   inode number\n"
226                "\t%%m   mount point\n"
227                "\t%%n   file name\n"
228                "\t%%N   quoted file name with dereference if symbolic link\n"
229                "\t%%o   optimal I/O transfer size hint\n"
230                "\t%%q   Mask to show what's supported in stx_attributes\n"
231                "\t%%r   Flags conveying information about the file: "
232                "stx_attributes\n"
233                "\t%%s   total size, in bytes\n"
234                "\t%%t   major device type in hex, for character/block device "
235                "special files\n"
236                "\t%%T   minor device type in hex, for character/block device "
237                "special files\n"
238                "\t%%u   user ID of owner\n"
239                "\t%%U   user name of owner\n"
240                "\t%%w   time of file birth, human-readable; - if unknown\n"
241                "\t%%W   time of file birth, seconds since Epoch; 0 if unknown\n"
242                "\t%%x   time of last access, human-readable\n"
243                "\t%%X   time of last access, seconds since Epoch\n"
244                "\t%%y   time of last data modification, human-readable\n"
245                "\t%%Y   time of last data modification, seconds since Epoch\n"
246                "\t%%z   time of last status change, human-readable\n"
247                "\t%%Z   time of last status change, seconds since Epoch\n");
248         exit(0);
249 }
250
251 /* gnulib/lib/filemode.c */
252 /* Return a character indicating the type of file described by
253  * file mode BITS:
254  * '-' regular file
255  * 'b' block special file
256  * 'c' character special file
257  * 'C' high performance ("contiguous data") file
258  * 'd' directory
259  * 'D' door
260  * 'l' symbolic link
261  * 'm' multiplexed file (7th edition Unix; obsolete)
262  * 'n' network special file (HP-UX)
263  * 'p' fifo (named pipe)
264  * 'P' port
265  * 's' socket
266  * 'w' whiteout (4.4BSD)
267  * '?' some other file type
268  */
269 static char ftypelet(mode_t bits)
270 {
271         /* These are the most common, so test for them first.*/
272         if (S_ISREG(bits))
273                 return '-';
274         if (S_ISDIR(bits))
275                 return 'd';
276
277         /* Other letters standardized by POSIX 1003.1-2004.*/
278         if (S_ISBLK(bits))
279                 return 'b';
280         if (S_ISCHR(bits))
281                 return 'c';
282         if (S_ISLNK(bits))
283                 return 'l';
284         if (S_ISFIFO(bits))
285                 return 'p';
286
287         /* Other file types (though not letters) standardized by POSIX.*/
288         if (S_ISSOCK(bits))
289                 return 's';
290
291         return '?';
292 }
293
294 /* Like filemodestring, but rely only on MODE.*/
295 static void strmode(mode_t mode, char *str)
296 {
297         str[0] = ftypelet(mode);
298         str[1] = mode & S_IRUSR ? 'r' : '-';
299         str[2] = mode & S_IWUSR ? 'w' : '-';
300         str[3] = (mode & S_ISUID
301                         ? (mode & S_IXUSR ? 's' : 'S')
302                         : (mode & S_IXUSR ? 'x' : '-'));
303         str[4] = mode & S_IRGRP ? 'r' : '-';
304         str[5] = mode & S_IWGRP ? 'w' : '-';
305         str[6] = (mode & S_ISGID
306                         ? (mode & S_IXGRP ? 's' : 'S')
307                         : (mode & S_IXGRP ? 'x' : '-'));
308         str[7] = mode & S_IROTH ? 'r' : '-';
309         str[8] = mode & S_IWOTH ? 'w' : '-';
310         str[9] = (mode & S_ISVTX
311                         ? (mode & S_IXOTH ? 't' : 'T')
312                         : (mode & S_IXOTH ? 'x' : '-'));
313         str[10] = ' ';
314         str[11] = '\0';
315 }
316
317 /* filemodestring - fill in string STR with an ls-style ASCII
318  * representation of the st_mode field of file stats block STATP.
319  * 12 characters are stored in STR.
320  * The characters stored in STR are:
321  *
322  * 0    File type, as in ftypelet above, except that other letters are used
323  *      for files whose type cannot be determined solely from st_mode:
324  *
325  *          'F' semaphore
326  *          'M' migrated file (Cray DMF)
327  *          'Q' message queue
328  *          'S' shared memory object
329  *          'T' typed memory object
330  *
331  * 1    'r' if the owner may read, '-' otherwise.
332  *
333  * 2    'w' if the owner may write, '-' otherwise.
334  *
335  * 3    'x' if the owner may execute, 's' if the file is
336  *      set-user-id, '-' otherwise.
337  *      'S' if the file is set-user-id, but the execute
338  *      bit isn't set.
339  *
340  * 4    'r' if group members may read, '-' otherwise.
341  *
342  * 5    'w' if group members may write, '-' otherwise.
343  *
344  * 6    'x' if group members may execute, 's' if the file is
345  *      set-group-id, '-' otherwise.
346  *      'S' if it is set-group-id but not executable.
347  *
348  * 7    'r' if any user may read, '-' otherwise.
349  *
350  * 8    'w' if any user may write, '-' otherwise.
351  *
352  * 9    'x' if any user may execute, 't' if the file is "sticky"
353  *      (will be retained in swap space after execution), '-'
354  *      otherwise.
355  *      'T' if the file is sticky but not executable.
356  *
357  * 10   ' ' for compatibility with 4.4BSD strmode,
358  *      since this interface does not support ACLs.
359  *
360  * 11   '\0'.
361  */
362 static void filemodestring(struct statx const *stxp, char *str)
363 {
364         strmode(stxp->stx_mode, str);
365
366 /*
367         if (S_TYPEISSEM(statp))
368                 str[0] = 'F';
369         else if (IS_MIGRATED_FILE (statp))
370                 str[0] = 'M';
371         else if (S_TYPEISMQ (statp))
372                 str[0] = 'Q';
373         else if (S_TYPEISSHM (statp))
374                 str[0] = 'S';
375         else if (S_TYPEISTMO (statp))
376                 str[0] = 'T';
377  */
378 }
379
380 /* gnulib/lib/file-type.c */
381 static char const *file_type(struct statx const *stx)
382 {
383         /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107 for some of
384          * these formats.
385          *
386          * To keep diagnostics grammatical in English, the returned string
387          * must start with a consonant.
388          */
389         /* Do these three first, as they're the most common.  */
390         if (S_ISREG(stx->stx_mode))
391                 return stx->stx_size == 0 ? "regular empty file" :
392                                             "regular file";
393
394         if (S_ISDIR(stx->stx_mode))
395                 return "directory";
396
397         if (S_ISLNK(stx->stx_mode))
398                 return "symbolic link";
399
400         /* The remaining are in alphabetical order.  */
401         if (S_ISBLK(stx->stx_mode))
402                 return "block special file";
403
404         if (S_ISCHR(stx->stx_mode))
405                 return "character special file";
406
407         if (S_ISFIFO(stx->stx_mode))
408                 return "fifo";
409
410         if (S_ISSOCK(stx->stx_mode))
411                 return "socket";
412
413         return "weird file";
414 }
415
416 /* gnulib/lib/areadlink-with-size.c */
417 /* SYMLINK_MAX is used only for an initial memory-allocation sanity
418  * check, so it's OK to guess too small on hosts where there is no
419  * arbitrary limit to symbolic link length.
420  */
421 #ifndef SYMLINK_MAX
422 #define SYMLINK_MAX 1024
423 #endif
424
425 #define MAXSIZE (SIZE_MAX < SSIZE_MAX ? SIZE_MAX : SSIZE_MAX)
426
427 /* Call readlink to get the symbolic link value of FILE.
428  * SIZE is a hint as to how long the link is expected to be;
429  * typically it is taken from st_size.  It need not be correct.
430  * Return a pointer to that NUL-terminated string in malloc'd storage.
431  * If readlink fails, malloc fails, or if the link value is longer
432  * than SSIZE_MAX, return NULL (caller may use errno to diagnose).
433  */
434 static char *areadlink_with_size(char const *file, size_t size)
435 {
436         /* Some buggy file systems report garbage in st_size.  Defend
437          * against them by ignoring outlandish st_size values in the initial
438          * memory allocation.
439          */
440         size_t symlink_max = SYMLINK_MAX;
441         size_t INITIAL_LIMIT_BOUND = 8 * 1024;
442         size_t initial_limit = (symlink_max < INITIAL_LIMIT_BOUND ?
443                                 symlink_max + 1 : INITIAL_LIMIT_BOUND);
444         enum { stackbuf_size = 128 };
445         /* The initial buffer size for the link value. */
446         size_t buf_size = (size == 0 ? stackbuf_size : size < initial_limit ?
447                            size + 1 : initial_limit);
448
449         while (1) {
450                 ssize_t r;
451                 size_t link_length;
452                 char stackbuf[stackbuf_size];
453                 char *buf = stackbuf;
454                 char *buffer = NULL;
455
456                 if (!(size == 0 && buf_size == stackbuf_size)) {
457                         buf = buffer = malloc(buf_size);
458                         if (!buffer)
459                                 return NULL;
460                 }
461
462                 r = readlink(file, buf, buf_size);
463                 link_length = r;
464
465                 /* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1
466                  * with errno == ERANGE if the buffer is too small.
467                  */
468                 if (r < 0 && errno != ERANGE) {
469                         int saved_errno = errno;
470
471                         free(buffer);
472                         errno = saved_errno;
473                         return NULL;
474                 }
475
476                 if (link_length < buf_size) {
477                         buf[link_length] = 0;
478                         if (!buffer) {
479                                 buffer = malloc(link_length + 1);
480                                 if (buffer)
481                                         return memcpy(buffer, buf,
482                                                       link_length + 1);
483                         } else if (link_length + 1 < buf_size) {
484                                 /* Shrink BUFFER before returning it. */
485                                 char *shrinked_buffer;
486
487                                 shrinked_buffer = realloc(buffer,
488                                                           link_length + 1);
489                                 if (shrinked_buffer != NULL)
490                                         buffer = shrinked_buffer;
491                         }
492                         return buffer;
493                 }
494
495                 free(buffer);
496                 if (buf_size <= MAXSIZE / 2) {
497                         buf_size *= 2;
498                 } else if (buf_size < MAXSIZE) {
499                         buf_size = MAXSIZE;
500                 } else {
501                         errno = ENOMEM;
502                         return NULL;
503                 }
504         }
505 }
506
507 /* coreutils/src/stat.c */
508 /* Output a single-character \ escape.  */
509 static void print_esc_char(char c)
510 {
511         switch (c) {
512         case 'a':                       /* Alert. */
513                 c = '\a';
514                 break;
515         case 'b':                       /* Backspace. */
516                 c = '\b';
517                 break;
518         case 'e':                       /* Escape. */
519                 c = '\x1B';
520                 break;
521         case 'f':                       /* Form feed. */
522                 c = '\f';
523                 break;
524         case 'n':                       /* New line. */
525                 c = '\n';
526                 break;
527         case 'r':                       /* Carriage return. */
528                 c = '\r';
529                 break;
530         case 't':                       /* Horizontal tab. */
531                 c = '\t';
532                 break;
533         case 'v':                       /* Vertical tab. */
534                 c = '\v';
535                 break;
536         case '"':
537         case '\\':
538                 break;
539         default:
540                 printf("warning: unrecognized escape '\\%c'", c);
541                 break;
542         }
543         putchar (c);
544 }
545
546 static size_t format_code_offset(char const *directive)
547 {
548         size_t len = strspn(directive + 1, printf_flags);
549         char const *fmt_char = directive + len + 1;
550
551         fmt_char += strspn(fmt_char, digits);
552         if (*fmt_char == '.')
553                 fmt_char += 1 + strspn(fmt_char + 1, digits);
554
555         return fmt_char - directive;
556 }
557
558 static unsigned int fmt_to_mask(char fmt)
559 {
560         switch (fmt) {
561         case 'N':
562                 return STATX_MODE;
563         case 'd':
564         case 'D':
565                 return STATX_MODE;
566         case 'i':
567                 return STATX_INO;
568         case 'a':
569         case 'A':
570                 return STATX_MODE;
571         case 'f':
572                 return STATX_MODE|STATX_TYPE;
573         case 'F':
574                 return STATX_TYPE;
575         case 'h':
576                 return STATX_NLINK;
577         case 'u':
578         case 'U':
579                 return STATX_UID;
580         case 'g':
581         case 'G':
582                 return STATX_GID;
583         case 'm':
584                 return STATX_MODE|STATX_INO;
585         case 's':
586                 return STATX_SIZE;
587         case 't':
588         case 'T':
589                 return STATX_MODE;
590         case 'b':
591                 return STATX_BLOCKS;
592         case 'w':
593         case 'W':
594                 return STATX_BTIME;
595         case 'x':
596         case 'X':
597                 return STATX_ATIME;
598         case 'y':
599         case 'Y':
600                 return STATX_MTIME;
601         case 'z':
602         case 'Z':
603                 return STATX_CTIME;
604         }
605         return 0;
606 }
607
608 static unsigned int format_to_mask(char const *format)
609 {
610         unsigned int mask = 0;
611         char const *b;
612
613         for (b = format; *b; b++) {
614                 if (*b != '%')
615                         continue;
616
617                 b += format_code_offset(b);
618                 if (*b == '\0')
619                         break;
620                 mask |= fmt_to_mask(*b);
621         }
622
623         return mask;
624 }
625
626 static char *human_access(struct statx const *stxbuf)
627 {
628         static char modebuf[12];
629
630         filemodestring(stxbuf, modebuf);
631         modebuf[10] = 0;
632         return modebuf;
633 }
634
635 static inline struct timespec
636 statx_timestamp_to_timespec(struct statx_timestamp tsx)
637 {
638         struct timespec ts;
639
640         ts.tv_sec = tsx.tv_sec;
641         ts.tv_nsec = tsx.tv_nsec;
642
643         return ts;
644 }
645
646 static int timespec_cmp(struct timespec a, struct timespec b)
647 {
648         if (a.tv_sec < b.tv_sec)
649                 return -1;
650         if (a.tv_sec > b.tv_sec)
651                 return 1;
652
653         return a.tv_nsec - b.tv_nsec;
654 }
655
656 static char *human_time(const struct statx_timestamp *ts)
657 {
658         /* STR must be at least INT_BUFSIZE_BOUND (intmax_t) big, either
659          * because localtime_rz fails, or because the time zone is truly
660          * outlandish so that %z expands to a long string.
661          */
662         static char str[INT_BUFSIZE_BOUND(intmax_t)
663                 + INT_STRLEN_BOUND(int) /* YYYY */
664                 + 1 /* because YYYY might equal INT_MAX + 1900 */
665                 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +"];
666         struct tm tm;
667         time_t tim;
668         int len;
669         int len2;
670
671         tim = ts->tv_sec;
672         if (!localtime_r(&tim, &tm)) {
673                 perror("localtime_r");
674                 exit(EXIT_FAILURE);
675         }
676
677         if (o_dir_list && long_format) {
678                 struct timespec when_timespec;
679                 struct timespec six_months_ago;
680                 bool recent;
681
682                 when_timespec = statx_timestamp_to_timespec(*ts);
683                 /* If the file appears to be in the future, update the current
684                  * time, in case the file happens to have been modified since
685                  * the last time we checked the clock.
686                  */
687                 if (timespec_cmp(current_time, when_timespec) < 0) {
688                         struct timeval tv;
689
690                         gettimeofday(&tv, NULL);
691                         current_time.tv_sec = tv.tv_sec;
692                         current_time.tv_nsec = tv.tv_usec * 1000;
693                 }
694
695                 /* Consider a time to be recent if it is within the past six
696                  * months.
697                  * A Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952
698                  * seconds on the average.  Write this value as an integer
699                  * constant to avoid floating point hassles.
700                  */
701                 six_months_ago.tv_sec = current_time.tv_sec - 31556952 / 2;
702                 six_months_ago.tv_nsec = current_time.tv_nsec;
703
704                 recent = (timespec_cmp(six_months_ago, when_timespec) < 0 &&
705                           (timespec_cmp(when_timespec, current_time) < 0));
706
707                 /* We assume here that all time zones are offset from UTC by a
708                  * whole number of seconds.
709                  */
710                 len = strftime(str, sizeof(str),
711                                recent ? "%b %e %H:%M" : "%b %e %Y", &tm);
712                 if (len == 0) {
713                         perror("strftime");
714                         exit(EXIT_FAILURE);
715                 }
716
717                 return str;
718         }
719
720         len = strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", &tm);
721         if (len == 0) {
722                 perror("strftime");
723                 exit(EXIT_FAILURE);
724         }
725
726         len2 = snprintf(str + len, sizeof(str) - len, ".%09u ", ts->tv_nsec);
727         len = strftime(str + len + len2, sizeof(str) - len - len2, "%z", &tm);
728         if (len == 0) {
729                 perror("strftime2");
730                 exit(1);
731         }
732
733         return str;
734 }
735
736 /* PFORMAT points to a '%' followed by a prefix of a format, all of
737  * size PREFIX_LEN.  The flags allowed for this format are
738  * ALLOWED_FLAGS; remove other printf flags from the prefix, then
739  * append SUFFIX.
740  */
741 static void make_format(char *pformat, size_t prefix_len,
742                         char const *allowed_flags, char const *suffix)
743 {
744         char *dst = pformat + 1;
745         char const *src;
746         char const *srclim = pformat + prefix_len;
747
748         for (src = dst; src < srclim && strchr(printf_flags, *src); src++)
749                 if (strchr(allowed_flags, *src))
750                         *dst++ = *src;
751         while (src < srclim)
752                 *dst++ = *src++;
753         strcpy(dst, suffix);
754 }
755
756 static void out_string(char *pformat, size_t prefix_len, char const *arg)
757 {
758         make_format(pformat, prefix_len, "-", "s");
759         printf(pformat, arg);
760 }
761
762 static int out_int(char *pformat, size_t prefix_len, intmax_t arg)
763 {
764         make_format(pformat, prefix_len, "'-+ 0", PRIdMAX);
765         return printf(pformat, arg);
766 }
767
768 static int out_uint(char *pformat, size_t prefix_len, uintmax_t arg)
769 {
770         make_format(pformat, prefix_len, "'-0", PRIuMAX);
771         return printf(pformat, arg);
772 }
773
774 static void out_uint_o(char *pformat, size_t prefix_len, uintmax_t arg)
775 {
776         make_format(pformat, prefix_len, "-#0", PRIoMAX);
777         printf(pformat, arg);
778 }
779
780 static void out_uint_x(char *pformat, size_t prefix_len, uintmax_t arg)
781 {
782         make_format(pformat, prefix_len, "-#0", PRIxMAX);
783         printf(pformat, arg);
784 }
785
786 static int out_minus_zero(char *pformat, size_t prefix_len)
787 {
788         make_format(pformat, prefix_len, "'-+ 0", ".0f");
789         return printf(pformat, -0.25);
790 }
791
792 /* Output the number of seconds since the Epoch, using a format that
793  * acts like printf's %f format.
794  */
795 static void out_epoch_sec(char *pformat, size_t prefix_len,
796                           struct timespec arg)
797 {
798         char *dot = memchr(pformat, '.', prefix_len);
799         size_t sec_prefix_len = prefix_len;
800         int width = 0;
801         int precision = 0;
802         bool frac_left_adjust = false;
803
804         if (dot) {
805                 sec_prefix_len = dot - pformat;
806                 pformat[prefix_len] = '\0';
807
808                 if (ISDIGIT(dot[1])) {
809                         long int lprec = strtol(dot + 1, NULL, 10);
810
811                         precision = (lprec <= INT_MAX ? lprec : INT_MAX);
812                 } else {
813                         precision = 9;
814                 }
815
816                 if (precision && ISDIGIT(dot[-1])) {
817                         /* If a nontrivial width is given, subtract the width
818                          * of the decimal point and PRECISION digits that will
819                          * be output later.
820                          */
821                         char *p = dot;
822
823                         *dot = '\0';
824
825                         do
826                                 --p;
827                         while (ISDIGIT(p[-1]));
828
829                         long int lwidth = strtol(p, NULL, 10);
830
831                         width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
832                         if (width > 1) {
833                                 p += (*p == '0');
834                                 sec_prefix_len = p - pformat;
835
836                                 int w_d = (decimal_point_len < width ?
837                                            width - decimal_point_len : 0);
838
839                                 if (w_d > 1) {
840                                         int w = w_d - precision;
841
842                                         if (w > 1) {
843                                                 char *dst = pformat;
844                                                 char const *src = dst;
845                                                 for (; src < p; src++) {
846                                                         if (*src == '-')
847                                                                 frac_left_adjust = true;
848                                                         else
849                                                                 *dst++ = *src;
850                                                 }
851                                                 sec_prefix_len =
852                                                         (dst - pformat
853                         + (frac_left_adjust ? 0 : sprintf(dst, "%d", w)));
854                                         }
855                                 }
856                         }
857                 }
858         }
859
860         int divisor = 1;
861         int i;
862
863         for (i = precision; i < 9; i++)
864                 divisor *= 10;
865
866         int frac_sec = arg.tv_nsec / divisor;
867         int int_len;
868
869
870         if (TYPE_SIGNED(time_t)) {
871                 bool minus_zero = false;
872
873                 if (arg.tv_sec < 0 && arg.tv_nsec != 0) {
874                         int frac_sec_modulus = 1000000000 / divisor;
875
876                         frac_sec = (frac_sec_modulus - frac_sec
877                                     - (arg.tv_nsec % divisor != 0));
878                         arg.tv_sec += (frac_sec != 0);
879                         minus_zero = (arg.tv_sec == 0);
880                 }
881                 int_len = (minus_zero ?
882                            out_minus_zero(pformat, sec_prefix_len) :
883                            out_int(pformat, sec_prefix_len, arg.tv_sec));
884         } else {
885                 int_len = out_uint(pformat, sec_prefix_len, arg.tv_sec);
886         }
887
888         if (precision) {
889                 int prec = (precision < 9 ? precision : 9);
890                 int trailing_prec = precision - prec;
891                 int ilen = (int_len < 0 ? 0 : int_len);
892                 int trailing_width = (ilen < width &&
893                                       decimal_point_len < width - ilen ?
894                                       width - ilen - decimal_point_len - prec :
895                                       0);
896
897                 printf("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
898                         trailing_width, trailing_prec, 0);
899         }
900 }
901
902 /* Print the context information of FILENAME, and return true iff the
903  * context could not be obtained.
904  */
905 static int out_file_context(char *pformat, size_t prefix_len,
906                             char const *filename)
907 {
908         char *scontext = NULL;
909         int rc = 0;
910
911 #ifdef HAVE_SELINUX
912         if ((follow_links ? getfilecon(filename, &scontext) :
913                             lgetfilecon(filename, &scontext)) < 0) {
914                 printf("failed to get security context of %s: %s\n",
915                        filename, strerror(errno));
916                 scontext = NULL;
917                 rc  = -errno;
918         }
919 #endif
920
921         strcpy(pformat + prefix_len, "s");
922         printf(pformat, (scontext ? scontext : "?"));
923         if (scontext)
924                 freecon(scontext);
925         return rc;
926 }
927
928 /* Map a TS with negative TS.tv_nsec to {0,0}.  */
929 static inline struct timespec neg_to_zero(struct timespec ts)
930 {
931         if (ts.tv_nsec >= 0) {
932                 return ts;
933         } else {
934                 struct timespec z = {0, 0};
935
936                 return z;
937         }
938 }
939
940 /* All the mode bits that can be affected by chmod.  */
941 #define CHMOD_MODE_BITS \
942         (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
943
944 /* Print statx info.  Return zero upon success, nonzero upon failure.  */
945 static int print_statx(char *pformat, size_t prefix_len, unsigned int m,
946                        int fd, char const *filename, struct statx const *stx)
947 {
948         struct passwd *pw_ent;
949         struct group *gw_ent;
950         int rc = 0;
951         int ret;
952
953         switch (m) {
954         case 'n':
955                 out_string(pformat, prefix_len,
956                            o_dir_list ? strrchr(filename, '/') + 1 : filename);
957                 break;
958         case 'N':
959                 out_string(pformat, prefix_len,
960                            o_dir_list ? strrchr(filename, '/')  + 1 : filename);
961                 if (S_ISLNK(stx->stx_mode)) {
962                         char *linkname;
963
964                         linkname = areadlink_with_size(filename, stx->stx_size);
965                         if (linkname == NULL) {
966                                 printf("cannot read symbolic link %s: %s",
967                                        filename, strerror(errno));
968                                 return -errno;
969                         }
970                         printf(" -> ");
971                         out_string(pformat, prefix_len, linkname);
972                         free(linkname);
973                 }
974                 break;
975         case 'd':
976                 out_uint(pformat, prefix_len, makedev(stx->stx_dev_major,
977                                                       stx->stx_dev_minor));
978                 break;
979         case 'D':
980                 out_uint_x(pformat, prefix_len, makedev(stx->stx_dev_major,
981                                                         stx->stx_dev_minor));
982                 break;
983         case 'i':
984                 out_uint(pformat, prefix_len, stx->stx_ino);
985                 break;
986         case 'a':
987                 out_uint_o(pformat, prefix_len,
988                            stx->stx_mode & CHMOD_MODE_BITS);
989                 break;
990         case 'A':
991                 out_string(pformat, prefix_len, human_access(stx));
992                 break;
993         case 'f':
994                 out_uint_x(pformat, prefix_len, stx->stx_mode);
995                 break;
996         case 'F':
997                 out_string(pformat, prefix_len, file_type(stx));
998                 break;
999         case 'h':
1000                 out_uint(pformat, prefix_len, stx->stx_nlink);
1001                 break;
1002         case 'u':
1003                 out_uint(pformat, prefix_len, stx->stx_uid);
1004                 break;
1005         case 'U':
1006                 pw_ent = getpwuid(stx->stx_uid);
1007                 out_string(pformat, prefix_len,
1008                            pw_ent ? pw_ent->pw_name : "UNKNOWN");
1009                 break;
1010         case 'g':
1011                 out_uint(pformat, prefix_len, stx->stx_gid);
1012                 break;
1013         case 'G':
1014                 gw_ent = getgrgid(stx->stx_gid);
1015                 out_string(pformat, prefix_len,
1016                            gw_ent ? gw_ent->gr_name : "UNKNOWN");
1017                 break;
1018         case 'm':
1019                 /*
1020                  * fail |= out_mount_point(filename, pformat, prefix_len,
1021                  *                         statbuf);
1022                  */
1023                 if (!rc)
1024                         rc = -ENOTSUP;
1025                 break;
1026         case 's':
1027                 out_int(pformat, prefix_len, stx->stx_size);
1028                 break;
1029         case 't':
1030                 out_uint_x(pformat, prefix_len,
1031                            major(makedev(stx->stx_rdev_major,
1032                                          stx->stx_rdev_minor)));
1033                 break;
1034         case 'T':
1035                 out_uint_x(pformat, prefix_len,
1036                            minor(makedev(stx->stx_rdev_major,
1037                                          stx->stx_rdev_minor)));
1038                 break;
1039         case 'B':
1040                 out_uint(pformat, prefix_len, S_BLKSIZE);
1041                 break;
1042         case 'b':
1043                 out_uint(pformat, prefix_len, stx->stx_blocks);
1044                 break;
1045         case 'o':
1046                 out_uint(pformat, prefix_len, stx->stx_blksize);
1047                 break;
1048         case 'p':
1049                 out_uint_x(pformat, prefix_len, stx->stx_attributes_mask);
1050                 break;
1051         case 'r':
1052                 out_uint_x(pformat, prefix_len, stx->stx_attributes);
1053                 break;
1054         case 'w':
1055                 if (stx->stx_btime.tv_nsec < 0)
1056                         out_string(pformat, prefix_len, "-");
1057                 else
1058                         out_string(pformat, prefix_len,
1059                                    human_time(&stx->stx_btime));
1060                 break;
1061         case 'W':
1062                 out_epoch_sec(pformat, prefix_len,
1063                               neg_to_zero(statx_timestamp_to_timespec(
1064                                               stx->stx_btime)));
1065                 break;
1066         case 'x':
1067                 out_string(pformat, prefix_len,
1068                            human_time(&stx->stx_atime));
1069                 break;
1070         case 'X':
1071                 out_epoch_sec(pformat, prefix_len,
1072                               neg_to_zero(statx_timestamp_to_timespec(
1073                                               stx->stx_atime)));
1074                 break;
1075         case 'y':
1076                 out_string(pformat, prefix_len,
1077                            human_time(&stx->stx_mtime));
1078                 break;
1079         case 'Y':
1080                 out_epoch_sec(pformat, prefix_len,
1081                               neg_to_zero(statx_timestamp_to_timespec(
1082                                               stx->stx_mtime)));
1083                 break;
1084         case 'z':
1085                 out_string(pformat, prefix_len,
1086                            human_time(&stx->stx_ctime));
1087                 break;
1088         case 'Z':
1089                 out_epoch_sec(pformat, prefix_len,
1090                               neg_to_zero(statx_timestamp_to_timespec(
1091                                               stx->stx_ctime)));
1092                 break;
1093         case 'C':
1094                 ret = out_file_context(pformat, prefix_len, filename);
1095                 if (!rc && ret)
1096                         rc = ret;
1097                 break;
1098         default:
1099                 fputc('?', stdout);
1100                 break;
1101         }
1102
1103         return rc;
1104 }
1105
1106 static int print_it(int fd, char const *filename,
1107                     int (*print_func)(char *, size_t, unsigned int,
1108                                       int, char const *, struct statx const *),
1109                     void const *data)
1110 {
1111         /* Add 2 to accommodate our conversion of the stat '%s' format string
1112          * to the longer printf '%llu' one.
1113          */
1114         enum {
1115                 MAX_ADDITIONAL_BYTES = (MAX(sizeof(PRIdMAX),
1116                                         MAX(sizeof(PRIoMAX),
1117                                             MAX(sizeof(PRIuMAX),
1118                                                 sizeof(PRIxMAX)))) - 1)
1119         };
1120         size_t n_alloc;
1121         char *dest;
1122         char const *b;
1123         int rc = 0;
1124
1125         if (o_quiet)
1126                 return 0;
1127
1128         n_alloc = strlen(format) + MAX_ADDITIONAL_BYTES + 1;
1129         dest = malloc(n_alloc);
1130         if (dest == NULL)
1131                 return -ENOMEM;
1132
1133         for (b = format; *b; b++) {
1134                 switch (*b) {
1135                 case '%': {
1136                         size_t len = format_code_offset(b);
1137                         char const *fmt_char = b + len;
1138                         int ret;
1139
1140                         memcpy(dest, b, len);
1141                         b += len;
1142
1143                         switch (*fmt_char) {
1144                         case '\0':
1145                                 --b;
1146                         case '%':
1147                                 if (len > 1) {
1148                                         dest[len] = *fmt_char;
1149                                         dest[len + 1] = '\0';
1150                                         printf("%s: invalid directive", dest);
1151                                         return -EINVAL;
1152                                 }
1153                                 putchar('%');
1154                                 break;
1155                         default:
1156                                 ret = print_func(dest, len, to_uchar(*fmt_char),
1157                                                  fd, filename, data);
1158                                 if (rc == 0 && ret)
1159                                         rc = ret;
1160                                 break;
1161                         }
1162                         break;
1163                 }
1164                 case '\\':
1165                         if (!interpret_backslash_escapes) {
1166                                 putchar ('\\');
1167                                 break;
1168                         }
1169                         ++b;
1170                         if (isodigit(*b)) {
1171                                 int esc_value = octtobin(*b);
1172                                 int esc_length = 1; /* number of octal digits */
1173
1174                                 for (++b; esc_length < 3 && isodigit(*b);
1175                                      ++esc_length, ++b) {
1176                                         esc_value = esc_value * 8 +
1177                                                     octtobin(*b);
1178                                 }
1179                                 putchar(esc_value);
1180                                 --b;
1181                         } else if (*b == 'x' && isxdigit(to_uchar(b[1]))) {
1182                                 /* Value of \xhh escape. */
1183                                 int esc_value = hextobin(b[1]);
1184                                 /* A hexadecimal \xhh escape sequence must have
1185                                  * 1 or 2 hex. digits.
1186                                  */
1187
1188                                 ++b;
1189                                 if (isxdigit(to_uchar(b[1]))) {
1190                                         ++b;
1191                                         esc_value = esc_value * 16 +
1192                                                     hextobin(*b);
1193                                 }
1194                                 putchar(esc_value);
1195                         } else if (*b == '\0') {
1196                                 printf("warning: backslash at end of format");
1197                                 putchar('\\');
1198                                 /* Arrange to exit the loop.  */
1199                                 --b;
1200                         } else {
1201                                 print_esc_char(*b);
1202                         }
1203                         break;
1204
1205                 default:
1206                         putchar(*b);
1207                         break;
1208                 }
1209         }
1210         free(dest);
1211
1212         fputs(trailing_delim, stdout);
1213
1214         return rc;
1215 }
1216
1217 /* Return an allocated format string in static storage that
1218  * corresponds to whether FS and TERSE options were declared.
1219  */
1220 static char *default_format(bool fs, bool terse, bool device)
1221 {
1222         char *format;
1223
1224         if (fs) {
1225                 if (terse) {
1226                         format = xstrdup(fmt_terse_fs);
1227                 } else {
1228                         /* TRANSLATORS: This string uses format specifiers from
1229                          * 'stat --help' with --file-system, and NOT from
1230                          * printf.
1231                          */
1232                         format = xstrdup(
1233                         "  File: \"%n\"\n"
1234                         "    ID: %-8i Namelen: %-7l Type: %T\n"
1235                         "Block size: %-10s Fundamental block size: %S\n"
1236                         "Blocks: Total: %-10b Free: %-10f Available: %a\n"
1237                         "Inodes: Total: %-10c Free: %d\n");
1238                 }
1239         } else /* ! fs */ {
1240                 if (terse) {
1241 #ifdef HAVE_SELINUX
1242                         if (is_selinux_enabled() > 0)
1243                                 format = xstrdup(fmt_terse_selinux);
1244                         else
1245 #endif
1246                                 format = xstrdup(fmt_terse_regular);
1247                 } else {
1248                         char *temp;
1249
1250                         /* TRANSLATORS: This string uses format specifiers from
1251                          * 'stat --help' without --file-system, and NOT from
1252                          * printf.
1253                          */
1254                         format = xstrdup("\
1255   File: %N\n\
1256   Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1257 ");
1258
1259                         temp = format;
1260                         if (device) {
1261                                 /* TRANSLATORS: This string uses format
1262                                  * specifiers from 'stat --help' without
1263                                  * --file-system, and NOT from printf.
1264                                  */
1265                                 format = xasprintf("%s%s", format, "\
1266 " "Device: %Dh/%dd\tInode: %-10i  Links: %-5h Device type: %t,%T\n\
1267 ");
1268                         } else {
1269                                 /* TRANSLATORS: This string uses format
1270                                  * specifiers from 'stat --help' without
1271                                  * --file-system, and NOT from printf.
1272                                  */
1273                                 format = xasprintf("%s%s", format, "\
1274 " "Device: %Dh/%dd\tInode: %-10i  Links: %h\n\
1275 ");
1276                         }
1277                         free(temp);
1278
1279                         temp = format;
1280                         /* TRANSLATORS: This string uses format specifiers from
1281                          * 'stat --help' without --file-system, and NOT from
1282                          * printf.
1283                          */
1284                         format = xasprintf("%s%s", format, "\
1285 " "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n\
1286 ");
1287                         free(temp);
1288
1289                         if (is_selinux_enabled() > 0) {
1290                                 temp = format;
1291                                 /* TRANSLATORS: This string uses format
1292                                  * specifiers from 'stat --help' without
1293                                  * --file-system, and NOT from printf.
1294                                  */
1295                                 format = xasprintf("%s%s", format,
1296                                                    "Context: %C\n");
1297                                 free(temp);
1298                         }
1299
1300                         temp = format;
1301                         /* TRANSLATORS: This string uses format specifiers from
1302                          * 'stat --help' without --file-system, and NOT from
1303                          * printf.
1304                          */
1305                         format = xasprintf("%s%s", format,
1306                                            "Access: %x\n"
1307                                            "Modify: %y\n"
1308                                            "Change: %z\n"
1309                                            " Birth: %w\n");
1310                         free(temp);
1311                 }
1312         }
1313         return format;
1314 }
1315
1316 static char *list_long_format(void)
1317 {
1318         char *format;
1319
1320         format = xstrdup("\
1321 " "%10.10A %h %8U %8G %-10s %y %N\
1322 ");
1323
1324         return format;
1325 }
1326
1327 static int do_statx(char const *filename, unsigned int request_mask, int flags)
1328 {
1329         const char *pathname = filename;
1330         struct statx stx = { 0, };
1331         int fd;
1332
1333         if (strcmp(filename, "-") == 0)
1334                 fd = 0;
1335         else
1336                 fd = AT_FDCWD;
1337
1338         if (fd != AT_FDCWD) {
1339                 pathname = "";
1340                 flags |= AT_EMPTY_PATH;
1341         }
1342
1343         fd = statx(fd, pathname, flags, request_mask, &stx);
1344         if (fd < 0) {
1345                 if (flags & AT_EMPTY_PATH)
1346                         printf("cannot stat standard input\n");
1347                 else
1348                         printf("cannot statx %s: %s\n",
1349                                filename, strerror(errno));
1350
1351                 return -errno;
1352         }
1353
1354         return print_it(fd, filename, print_statx, &stx);
1355 }
1356
1357 /* Return true if FILE should be ignored. */
1358 static bool file_ignored(char const *name)
1359 {
1360         return name[0] == '.';
1361 }
1362
1363 static int do_dir_list(char const *dirname, unsigned int request_mask,
1364                        int flags)
1365 {
1366         DIR *dir;
1367         struct dirent *ent;
1368         char fullname[PATH_MAX];
1369         int rc = 0;
1370
1371         dir = opendir(dirname);
1372         if (!dir) {
1373                 rc = -errno;
1374                 printf("lsx: cannot open directory '%s': %s\n",
1375                        dirname, strerror(errno));
1376                 return rc;
1377         }
1378
1379         while ((ent = readdir(dir)) != NULL) {
1380                 int ret;
1381
1382                 /* skip "." and ".." */
1383                 if (file_ignored(ent->d_name))
1384                         continue;
1385
1386                 /* ls -1 */
1387                 if (!format) {
1388                         if (o_quiet)
1389                                 continue;
1390
1391                         printf("%s", ent->d_name);
1392                         putchar('\n');
1393                 } else {
1394                         if (strlen(ent->d_name) + strlen(dirname) + 1 >=
1395                             sizeof(fullname)) {
1396                                 errno = ENAMETOOLONG;
1397                                 fprintf(stderr,
1398                                         "lsx: ignored too long path: %s/%s\n",
1399                                         dirname, ent->d_name);
1400                                 if (!rc)
1401                                         rc = -ENAMETOOLONG;
1402                                 continue;
1403                         }
1404                         snprintf(fullname, PATH_MAX, "%s/%s",
1405                                  dirname, ent->d_name);
1406                         ret = do_statx(fullname, request_mask, flags);
1407                         if (!ret)
1408                                 putchar('\n');
1409                         else if (rc == 0)
1410                                 rc = ret;
1411                 }
1412         }
1413
1414         closedir(dir);
1415         return rc;
1416 }
1417
1418 int main(int argc, char **argv)
1419 {
1420         static struct option options[] = {
1421                 {"dereference", no_argument, NULL, 'L'},
1422                 {"format", required_argument, NULL, 'c'},
1423                 {"printf", required_argument, NULL, PRINTF_OPTION},
1424                 {"terse", no_argument, NULL, 't'},
1425                 {"cached", required_argument, NULL, 0},
1426                 {"dir", no_argument, NULL, 'D'},
1427                 {"long-format", no_argument, NULL, 'l'},
1428                 {"quiet", no_argument, NULL, 'q'},
1429                 {"help", no_argument, NULL, GETOPT_HELP_CHAR},
1430                 {"version", no_argument, NULL, GETOPT_VERSION_CHAR},
1431                 {NULL, 0, NULL, 0}
1432         };
1433         bool terse = false;
1434         unsigned int request_mask;
1435         int flags = AT_SYMLINK_NOFOLLOW;
1436         struct lconv const *locale = localeconv();
1437         int c;
1438         int rc = 0;
1439         int i = 0;
1440
1441         decimal_point = locale->decimal_point[0] ? locale->decimal_point : ".";
1442         decimal_point_len = strlen(decimal_point);
1443         current_time.tv_sec = TYPE_MINIMUM(time_t);
1444         current_time.tv_nsec = -1;
1445
1446         while ((c = getopt_long(argc, argv, "c:DqLlt", options, NULL)) != EOF) {
1447                 switch (c) {
1448                 case 'L':
1449                         flags &= ~AT_SYMLINK_NOFOLLOW;
1450                         follow_links = true;
1451                         break;
1452                 case PRINTF_OPTION:
1453                         format = optarg;
1454                         interpret_backslash_escapes = true;
1455                         trailing_delim = "";
1456                         break;
1457                 case 'c':
1458                         format = optarg;
1459                         interpret_backslash_escapes = false;
1460                         trailing_delim = "\n";
1461                         break;
1462                 case 'q':
1463                         o_quiet = true;
1464                         break;
1465                 case 'l':
1466                         o_dir_list = true;
1467                         long_format = true;
1468                         break;
1469                 case 'D':
1470                         o_dir_list = true;
1471                         break;
1472                 case 't':
1473                         terse = true;
1474                         break;
1475                 case 0:
1476                         if (strcmp(optarg, "never") == 0) {
1477                                 flags &= ~AT_STATX_SYNC_TYPE;
1478                                 flags |= AT_STATX_FORCE_SYNC;
1479                         } else if (strcmp(optarg, "always") == 0) {
1480                                 flags &= ~AT_STATX_SYNC_TYPE;
1481                                 flags |= AT_STATX_DONT_SYNC;
1482                         } else if (strcmp(optarg, "default") == 0) {
1483                                 flags &= ~AT_STATX_SYNC_TYPE;
1484                                 flags |= AT_SYMLINK_NOFOLLOW;
1485                         } else {
1486                                 printf("%s: invalid cached mode: %s\n",
1487                                        argv[0], optarg);
1488                                 return -EINVAL;
1489                         }
1490                         break;
1491                 case GETOPT_HELP_CHAR:
1492                         usage(argv[0]);
1493                 case GETOPT_VERSION_CHAR:
1494                         if (!o_quiet)
1495                                 printf("Lustre statx: version 0.1\n");
1496                         return 0;
1497                 default:
1498                         printf("%s: unknown option '-%c'\n",
1499                                argv[0], optopt);
1500                         return -EINVAL;
1501                 }
1502         }
1503
1504         if (format) {
1505                 request_mask = format_to_mask(format);
1506         } else {
1507                 request_mask = STATX_ALL;
1508                 if (o_dir_list)
1509                         format = long_format ? list_long_format() : NULL;
1510                 else
1511                         format = default_format(false, terse, false);
1512         }
1513
1514         if (optind == argc) {
1515                 if (o_dir_list)
1516                         return do_dir_list(".", request_mask, flags);
1517
1518                 printf("statx: missing operand\n"
1519                        "Try 'stat --help' for more information.\n");
1520                 return 0;
1521         }
1522
1523         for (i = optind; i < argc; i++) {
1524                 int ret;
1525
1526                 if (o_dir_list)
1527                         ret = do_dir_list(argv[i], request_mask, flags);
1528                 else
1529                         ret = do_statx(argv[i], request_mask, flags);
1530
1531                 if (rc == 0 && ret)
1532                         rc = ret;
1533         }
1534
1535         return rc;
1536 }
1537 #else
1538 int main(int argc, char **argv)
1539 {
1540         static struct option options[] = {
1541                 {"version", no_argument, NULL, GETOPT_VERSION_CHAR},
1542                 {"quiet", no_argument, NULL, 'q'},
1543                 {NULL, 0, NULL, 0}
1544         };
1545         int c;
1546
1547         while ((c = getopt_long(argc, argv, "q", options, NULL)) != EOF) {
1548                 switch (c) {
1549                 case 'q':
1550                         o_quiet = true;
1551                         break;
1552                 case GETOPT_VERSION_CHAR:
1553                         if (!o_quiet)
1554                                 printf("statx: Not support statx() syscall\n");
1555                         return -ENOTSUP;
1556                 }
1557         }
1558         printf("Skip: system does not support statx syscall.\n");
1559         return 0;
1560 }
1561 #endif /* __NR_statx */