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