4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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
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.
20 * Copyright (c) 2019, DDN Storage Corporation.
23 * This file is part of Lustre, http://www.lustre.org/
27 * Test for Lustre statx().
28 * It uses some code in coreutils ('ls.c' and 'stat.c') for reference.
30 * Author: Qian Yingjin <qian@ddn.com>
32 #define _ATFILE_SOURCE
46 #include <sys/syscall.h>
47 #include <sys/types.h>
48 #include <sys/param.h>
49 #include <sys/sysmacros.h>
53 #include <linux/lustre/lustre_user.h>
56 #include <selinux/selinux.h>
59 /* Factor out some of the common --help and --version processing code. */
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.
66 PRINTF_OPTION = (CHAR_MAX + 1),
67 GETOPT_HELP_CHAR = (CHAR_MIN - 2),
68 GETOPT_VERSION_CHAR = (CHAR_MIN - 3)
76 #define AT_STATX_SYNC_TYPE 0x6000
77 #define AT_STATX_FORCE_SYNC 0x2000
78 #define AT_STATX_DONT_SYNC 0x4000
80 static __attribute__((unused))
81 ssize_t statx(int dfd, const char *filename, int flags,
82 unsigned int mask, struct statx *buffer)
84 return syscall(__NR_statx, dfd, filename, flags, mask, buffer);
86 #endif /* HAVE_STATX */
88 #define xstrdup(str) strdup(str)
90 char *xasprintf(const char *fmt, const char *old_fmt, const char *str)
94 if (asprintf(&tmp, fmt, old_fmt, str) < 0) {
95 fprintf(stderr, "asprintf allocation failed\n");
103 /* coreutils/lib/intprops.h */
104 #define _GL_SIGNED_TYPE_OR_EXPR(t) TYPE_SIGNED(__typeof__(t))
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.
110 #define INT_BITS_STRLEN_BOUND(b) (((b) * 146 + 484) / 485)
112 /* The width in bits of the integer type or expression T.
114 * Padding bits are not supported; this is checked at compile-time below.
116 #define TYPE_WIDTH(t) (sizeof(t) * CHAR_BIT)
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.
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.
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))
130 /* Bound on buffer size needed to represent an integer type or expression T,
131 * including the terminating null.
133 #define INT_BUFSIZE_BOUND(t) (INT_STRLEN_BOUND(t) + 1)
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) \
140 : ((((t)1 << (TYPE_WIDTH(t) - 2)) - 1) * 2 + 1)))
142 static bool o_dir_list;
143 static bool long_format; /* use a long listing format */
145 /* Current time in seconds and nanoseconds since 1970, updated as
146 * needed when deciding whether a file is recent.
148 static struct timespec current_time;
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')
156 #define ISDIGIT(c) ((unsigned int)(c) - '0' <= 9)
158 /* True if the real type T is signed. */
159 #define TYPE_SIGNED(t) (!((t)0 < (t)-1))
161 static char const digits[] = "0123456789";
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
170 static char const printf_flags[] = "'-+ #0I";
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"
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";
182 /* Whether to follow symbolic links; True for --dereference (-L). */
183 static bool follow_links;
185 /* Whether to interpret backslash-escape sequences.
186 * True for --printf=FMT, not for --format=FMT (-c).
188 static bool interpret_backslash_escapes;
190 /* The trailing delimiter string:
191 * "" for --printf=FMT, "\n" for --format=FMT (-c).
193 static char const *trailing_delim = "";
195 /* The representation of the decimal point in the current locale. */
196 static char const *decimal_point;
197 static size_t decimal_point_len;
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.
203 static inline unsigned char to_uchar(char ch)
208 void usage(char *prog)
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"
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 "
220 "\t output a newline after each use of "
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"
231 "The valid format sequences for files (without --file-system):\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"
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"
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: "
253 "\t%%s total size, in bytes\n"
254 "\t%%t major device type in hex, for character/block device "
256 "\t%%T minor device type in hex, for character/block device "
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");
271 /* gnulib/lib/filemode.c */
272 /* Return a character indicating the type of file described by
275 * 'b' block special file
276 * 'c' character special file
277 * 'C' high performance ("contiguous data") file
281 * 'm' multiplexed file (7th edition Unix; obsolete)
282 * 'n' network special file (HP-UX)
283 * 'p' fifo (named pipe)
286 * 'w' whiteout (4.4BSD)
287 * '?' some other file type
289 static char ftypelet(mode_t bits)
291 /* These are the most common, so test for them first.*/
297 /* Other letters standardized by POSIX 1003.1-2004.*/
307 /* Other file types (though not letters) standardized by POSIX.*/
314 /* Like filemodestring, but rely only on MODE.*/
315 static void strmode(mode_t mode, char *str)
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' : '-'));
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:
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:
346 * 'M' migrated file (Cray DMF)
348 * 'S' shared memory object
349 * 'T' typed memory object
351 * 1 'r' if the owner may read, '-' otherwise.
353 * 2 'w' if the owner may write, '-' otherwise.
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
360 * 4 'r' if group members may read, '-' otherwise.
362 * 5 'w' if group members may write, '-' otherwise.
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.
368 * 7 'r' if any user may read, '-' otherwise.
370 * 8 'w' if any user may write, '-' otherwise.
372 * 9 'x' if any user may execute, 't' if the file is "sticky"
373 * (will be retained in swap space after execution), '-'
375 * 'T' if the file is sticky but not executable.
377 * 10 ' ' for compatibility with 4.4BSD strmode,
378 * since this interface does not support ACLs.
382 static void filemodestring(struct statx const *stxp, char *str)
384 strmode(stxp->stx_mode, str);
387 if (S_TYPEISSEM(statp))
389 else if (IS_MIGRATED_FILE (statp))
391 else if (S_TYPEISMQ (statp))
393 else if (S_TYPEISSHM (statp))
395 else if (S_TYPEISTMO (statp))
400 /* gnulib/lib/file-type.c */
401 static char const *file_type(struct statx const *stx)
403 /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107 for some of
406 * To keep diagnostics grammatical in English, the returned string
407 * must start with a consonant.
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" :
414 if (S_ISDIR(stx->stx_mode))
417 if (S_ISLNK(stx->stx_mode))
418 return "symbolic link";
420 /* The remaining are in alphabetical order. */
421 if (S_ISBLK(stx->stx_mode))
422 return "block special file";
424 if (S_ISCHR(stx->stx_mode))
425 return "character special file";
427 if (S_ISFIFO(stx->stx_mode))
430 if (S_ISSOCK(stx->stx_mode))
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.
442 #define SYMLINK_MAX 1024
445 #define MAXSIZE (SIZE_MAX < SSIZE_MAX ? SIZE_MAX : SSIZE_MAX)
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).
454 static char *areadlink_with_size(char const *file, size_t size)
456 /* Some buggy file systems report garbage in st_size. Defend
457 * against them by ignoring outlandish st_size values in the initial
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);
472 char stackbuf[stackbuf_size];
473 char *buf = stackbuf;
476 if (!(size == 0 && buf_size == stackbuf_size)) {
477 buf = buffer = malloc(buf_size);
482 r = readlink(file, buf, buf_size);
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.
488 if (r < 0 && errno != ERANGE) {
489 int saved_errno = errno;
496 if (link_length < buf_size) {
497 buf[link_length] = 0;
499 buffer = malloc(link_length + 1);
501 return memcpy(buffer, buf,
503 } else if (link_length + 1 < buf_size) {
504 /* Shrink BUFFER before returning it. */
505 char *shrinked_buffer;
507 shrinked_buffer = realloc(buffer,
509 if (shrinked_buffer != NULL)
510 buffer = shrinked_buffer;
516 if (buf_size <= MAXSIZE / 2) {
518 } else if (buf_size < MAXSIZE) {
527 /* coreutils/src/stat.c */
528 /* Output a single-character \ escape. */
529 static void print_esc_char(char c)
532 case 'a': /* Alert. */
535 case 'b': /* Backspace. */
538 case 'e': /* Escape. */
541 case 'f': /* Form feed. */
544 case 'n': /* New line. */
547 case 'r': /* Carriage return. */
550 case 't': /* Horizontal tab. */
553 case 'v': /* Vertical tab. */
560 printf("warning: unrecognized escape '\\%c'", c);
566 static size_t format_code_offset(char const *directive)
568 size_t len = strspn(directive + 1, printf_flags);
569 char const *fmt_char = directive + len + 1;
571 fmt_char += strspn(fmt_char, digits);
572 if (*fmt_char == '.')
573 fmt_char += 1 + strspn(fmt_char + 1, digits);
575 return fmt_char - directive;
578 static unsigned int fmt_to_mask(char fmt)
592 return STATX_MODE|STATX_TYPE;
604 return STATX_MODE|STATX_INO;
628 static unsigned int format_to_mask(char const *format)
630 unsigned int mask = 0;
633 for (b = format; *b; b++) {
637 b += format_code_offset(b);
640 mask |= fmt_to_mask(*b);
646 static char *human_access(struct statx const *stxbuf)
648 static char modebuf[12];
650 filemodestring(stxbuf, modebuf);
655 static inline struct timespec
656 statx_timestamp_to_timespec(struct statx_timestamp tsx)
660 ts.tv_sec = tsx.tv_sec;
661 ts.tv_nsec = tsx.tv_nsec;
666 static int timespec_cmp(struct timespec a, struct timespec b)
668 if (a.tv_sec < b.tv_sec)
670 if (a.tv_sec > b.tv_sec)
673 return a.tv_nsec - b.tv_nsec;
676 static char *human_time(const struct statx_timestamp *ts)
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.
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 +"];
692 if (!localtime_r(&tim, &tm)) {
693 perror("localtime_r");
697 if (o_dir_list && long_format) {
698 struct timespec when_timespec;
699 struct timespec six_months_ago;
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.
707 if (timespec_cmp(current_time, when_timespec) < 0) {
710 gettimeofday(&tv, NULL);
711 current_time.tv_sec = tv.tv_sec;
712 current_time.tv_nsec = tv.tv_usec * 1000;
715 /* Consider a time to be recent if it is within the past six
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.
721 six_months_ago.tv_sec = current_time.tv_sec - 31556952 / 2;
722 six_months_ago.tv_nsec = current_time.tv_nsec;
724 recent = (timespec_cmp(six_months_ago, when_timespec) < 0 &&
725 (timespec_cmp(when_timespec, current_time) < 0));
727 /* We assume here that all time zones are offset from UTC by a
728 * whole number of seconds.
730 len = strftime(str, sizeof(str),
731 recent ? "%b %e %H:%M" : "%b %e %Y", &tm);
740 len = strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", &tm);
746 len2 = snprintf(str + len, sizeof(str) - len, ".%09u ", ts->tv_nsec);
747 len = strftime(str + len + len2, sizeof(str) - len - len2, "%z", &tm);
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
761 static void make_format(char *pformat, size_t prefix_len,
762 char const *allowed_flags, char const *suffix)
764 char *dst = pformat + 1;
766 char const *srclim = pformat + prefix_len;
768 for (src = dst; src < srclim && strchr(printf_flags, *src); src++)
769 if (strchr(allowed_flags, *src))
776 static void out_string(char *pformat, size_t prefix_len, char const *arg)
778 make_format(pformat, prefix_len, "-", "s");
779 printf(pformat, arg);
782 static int out_int(char *pformat, size_t prefix_len, intmax_t arg)
784 make_format(pformat, prefix_len, "'-+ 0", PRIdMAX);
785 return printf(pformat, arg);
788 static int out_uint(char *pformat, size_t prefix_len, uintmax_t arg)
790 make_format(pformat, prefix_len, "'-0", PRIuMAX);
791 return printf(pformat, arg);
794 static void out_uint_o(char *pformat, size_t prefix_len, uintmax_t arg)
796 make_format(pformat, prefix_len, "-#0", PRIoMAX);
797 printf(pformat, arg);
800 static void out_uint_x(char *pformat, size_t prefix_len, uintmax_t arg)
802 make_format(pformat, prefix_len, "-#0", PRIxMAX);
803 printf(pformat, arg);
806 static int out_minus_zero(char *pformat, size_t prefix_len)
808 make_format(pformat, prefix_len, "'-+ 0", ".0f");
809 return printf(pformat, -0.25);
812 /* Output the number of seconds since the Epoch, using a format that
813 * acts like printf's %f format.
815 static void out_epoch_sec(char *pformat, size_t prefix_len,
818 char *dot = memchr(pformat, '.', prefix_len);
819 size_t sec_prefix_len = prefix_len;
822 bool frac_left_adjust = false;
825 sec_prefix_len = dot - pformat;
826 pformat[prefix_len] = '\0';
828 if (ISDIGIT(dot[1])) {
829 long int lprec = strtol(dot + 1, NULL, 10);
831 precision = (lprec <= INT_MAX ? lprec : INT_MAX);
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
847 while (ISDIGIT(p[-1]));
849 long int lwidth = strtol(p, NULL, 10);
851 width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
854 sec_prefix_len = p - pformat;
856 int w_d = (decimal_point_len < width ?
857 width - decimal_point_len : 0);
860 int w = w_d - precision;
864 char const *src = dst;
865 for (; src < p; src++) {
867 frac_left_adjust = true;
873 + (frac_left_adjust ? 0 : sprintf(dst, "%d", w)));
883 for (i = precision; i < 9; i++)
886 int frac_sec = arg.tv_nsec / divisor;
890 if (TYPE_SIGNED(time_t)) {
891 bool minus_zero = false;
893 if (arg.tv_sec < 0 && arg.tv_nsec != 0) {
894 int frac_sec_modulus = 1000000000 / divisor;
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);
901 int_len = (minus_zero ?
902 out_minus_zero(pformat, sec_prefix_len) :
903 out_int(pformat, sec_prefix_len, arg.tv_sec));
905 int_len = out_uint(pformat, sec_prefix_len, arg.tv_sec);
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 :
917 printf("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
918 trailing_width, trailing_prec, 0);
922 /* Print the context information of FILENAME, and return true iff the
923 * context could not be obtained.
925 static int out_file_context(char *pformat, size_t prefix_len,
926 char const *filename)
928 char *scontext = NULL;
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));
941 strcpy(pformat + prefix_len, "s");
942 printf(pformat, (scontext ? scontext : "?"));
950 /* Map a TS with negative TS.tv_nsec to {0,0}. */
951 static inline struct timespec neg_to_zero(struct timespec ts)
953 if (ts.tv_nsec >= 0) {
956 struct timespec z = {0, 0};
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)
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)
970 struct passwd *pw_ent;
971 struct group *gw_ent;
977 out_string(pformat, prefix_len,
978 o_dir_list ? strrchr(filename, '/') + 1 : filename);
981 out_string(pformat, prefix_len,
982 o_dir_list ? strrchr(filename, '/') + 1 : filename);
983 if (S_ISLNK(stx->stx_mode)) {
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));
993 out_string(pformat, prefix_len, linkname);
998 out_uint(pformat, prefix_len, makedev(stx->stx_dev_major,
999 stx->stx_dev_minor));
1002 out_uint_x(pformat, prefix_len, makedev(stx->stx_dev_major,
1003 stx->stx_dev_minor));
1006 out_uint(pformat, prefix_len, stx->stx_ino);
1009 out_uint_o(pformat, prefix_len,
1010 stx->stx_mode & CHMOD_MODE_BITS);
1013 out_string(pformat, prefix_len, human_access(stx));
1016 out_uint_x(pformat, prefix_len, stx->stx_mode);
1019 out_string(pformat, prefix_len, file_type(stx));
1022 out_uint(pformat, prefix_len, stx->stx_nlink);
1025 out_uint(pformat, prefix_len, stx->stx_uid);
1028 pw_ent = getpwuid(stx->stx_uid);
1029 out_string(pformat, prefix_len,
1030 pw_ent ? pw_ent->pw_name : "UNKNOWN");
1033 out_uint(pformat, prefix_len, stx->stx_gid);
1036 gw_ent = getgrgid(stx->stx_gid);
1037 out_string(pformat, prefix_len,
1038 gw_ent ? gw_ent->gr_name : "UNKNOWN");
1042 * fail |= out_mount_point(filename, pformat, prefix_len,
1049 out_int(pformat, prefix_len, stx->stx_size);
1052 out_uint_x(pformat, prefix_len,
1053 major(makedev(stx->stx_rdev_major,
1054 stx->stx_rdev_minor)));
1057 out_uint_x(pformat, prefix_len,
1058 minor(makedev(stx->stx_rdev_major,
1059 stx->stx_rdev_minor)));
1062 out_uint(pformat, prefix_len, S_BLKSIZE);
1065 out_uint(pformat, prefix_len, stx->stx_blocks);
1068 out_uint(pformat, prefix_len, stx->stx_blksize);
1071 out_uint_x(pformat, prefix_len, stx->stx_attributes_mask);
1074 out_uint_x(pformat, prefix_len, stx->stx_attributes);
1077 if (stx->stx_btime.tv_nsec < 0)
1078 out_string(pformat, prefix_len, "-");
1080 out_string(pformat, prefix_len,
1081 human_time(&stx->stx_btime));
1084 out_epoch_sec(pformat, prefix_len,
1085 neg_to_zero(statx_timestamp_to_timespec(
1089 out_string(pformat, prefix_len,
1090 human_time(&stx->stx_atime));
1093 out_epoch_sec(pformat, prefix_len,
1094 neg_to_zero(statx_timestamp_to_timespec(
1098 out_string(pformat, prefix_len,
1099 human_time(&stx->stx_mtime));
1102 out_epoch_sec(pformat, prefix_len,
1103 neg_to_zero(statx_timestamp_to_timespec(
1107 out_string(pformat, prefix_len,
1108 human_time(&stx->stx_ctime));
1111 out_epoch_sec(pformat, prefix_len,
1112 neg_to_zero(statx_timestamp_to_timespec(
1116 ret = out_file_context(pformat, prefix_len, filename);
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 *),
1133 /* Add 2 to accommodate our conversion of the stat '%s' format string
1134 * to the longer printf '%llu' one.
1137 MAX_ADDITIONAL_BYTES = (MAX(sizeof(PRIdMAX),
1138 MAX(sizeof(PRIoMAX),
1139 MAX(sizeof(PRIuMAX),
1140 sizeof(PRIxMAX)))) - 1)
1150 n_alloc = strlen(format) + MAX_ADDITIONAL_BYTES + 1;
1151 dest = malloc(n_alloc);
1155 for (b = format; *b; b++) {
1158 size_t len = format_code_offset(b);
1159 char const *fmt_char = b + len;
1162 memcpy(dest, b, len);
1165 switch (*fmt_char) {
1170 dest[len] = *fmt_char;
1171 dest[len + 1] = '\0';
1172 printf("%s: invalid directive", dest);
1178 ret = print_func(dest, len, to_uchar(*fmt_char),
1179 fd, filename, data);
1187 if (!interpret_backslash_escapes) {
1193 int esc_value = octtobin(*b);
1194 int esc_length = 1; /* number of octal digits */
1196 for (++b; esc_length < 3 && isodigit(*b);
1197 ++esc_length, ++b) {
1198 esc_value = esc_value * 8 +
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.
1211 if (isxdigit(to_uchar(b[1]))) {
1213 esc_value = esc_value * 16 +
1217 } else if (*b == '\0') {
1218 printf("warning: backslash at end of format");
1220 /* Arrange to exit the loop. */
1234 fputs(trailing_delim, stdout);
1239 /* Return an allocated format string in static storage that
1240 * corresponds to whether FS and TERSE options were declared.
1242 static char *default_format(bool fs, bool terse, bool device)
1248 format = xstrdup(fmt_terse_fs);
1250 /* TRANSLATORS: This string uses format specifiers from
1251 * 'stat --help' with --file-system, and NOT from
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");
1264 if (is_selinux_enabled() > 0)
1265 format = xstrdup(fmt_terse_selinux);
1268 format = xstrdup(fmt_terse_regular);
1272 /* TRANSLATORS: This string uses format specifiers from
1273 * 'stat --help' without --file-system, and NOT from
1278 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1283 /* TRANSLATORS: This string uses format
1284 * specifiers from 'stat --help' without
1285 * --file-system, and NOT from printf.
1287 format = xasprintf("%s%s", format, "\
1288 " "Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
1291 /* TRANSLATORS: This string uses format
1292 * specifiers from 'stat --help' without
1293 * --file-system, and NOT from printf.
1295 format = xasprintf("%s%s", format, "\
1296 " "Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1302 /* TRANSLATORS: This string uses format specifiers from
1303 * 'stat --help' without --file-system, and NOT from
1306 format = xasprintf("%s%s", format, "\
1307 " "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1312 if (is_selinux_enabled() > 0) {
1314 /* TRANSLATORS: This string uses format
1315 * specifiers from 'stat --help' without
1316 * --file-system, and NOT from printf.
1318 format = xasprintf("%s%s", format,
1324 /* TRANSLATORS: This string uses format specifiers from
1325 * 'stat --help' without --file-system, and NOT from
1328 format = xasprintf("%s%s", format,
1339 static char *list_long_format(void)
1344 " "%10.10A %h %8U %8G %-10s %y %N\
1350 static int do_statx(char const *filename, unsigned int request_mask, int flags)
1352 const char *pathname = filename;
1353 struct statx stx = { 0, };
1356 if (strcmp(filename, "-") == 0)
1361 if (fd != AT_FDCWD) {
1363 flags |= AT_EMPTY_PATH;
1366 fd = statx(fd, pathname, flags, request_mask, &stx);
1368 if (flags & AT_EMPTY_PATH)
1369 printf("cannot stat standard input\n");
1371 printf("cannot statx %s: %s\n",
1372 filename, strerror(errno));
1377 return print_it(fd, filename, print_statx, &stx);
1380 /* Return true if FILE should be ignored. */
1381 static bool file_ignored(char const *name)
1383 return name[0] == '.';
1386 static int do_dir_list(char const *dirname, unsigned int request_mask,
1391 char fullname[PATH_MAX];
1394 dir = opendir(dirname);
1397 printf("lsx: cannot open directory '%s': %s\n",
1398 dirname, strerror(errno));
1402 while ((ent = readdir(dir)) != NULL) {
1405 /* skip "." and ".." */
1406 if (file_ignored(ent->d_name))
1414 printf("%s", ent->d_name);
1417 if (strlen(ent->d_name) + strlen(dirname) + 1 >=
1419 errno = ENAMETOOLONG;
1421 "lsx: ignored too long path: %s/%s\n",
1422 dirname, ent->d_name);
1427 snprintf(fullname, PATH_MAX, "%s/%s",
1428 dirname, ent->d_name);
1429 ret = do_statx(fullname, request_mask, flags);
1441 int main(int argc, char **argv)
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},
1457 unsigned int request_mask;
1458 int flags = AT_SYMLINK_NOFOLLOW;
1459 struct lconv const *locale = localeconv();
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;
1469 while ((c = getopt_long(argc, argv, "c:DqLlt", options, NULL)) != EOF) {
1472 flags &= ~AT_SYMLINK_NOFOLLOW;
1473 follow_links = true;
1477 interpret_backslash_escapes = true;
1478 trailing_delim = "";
1482 interpret_backslash_escapes = false;
1483 trailing_delim = "\n";
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;
1509 printf("%s: invalid cached mode: %s\n",
1514 case GETOPT_HELP_CHAR:
1516 case GETOPT_VERSION_CHAR:
1518 printf("Lustre statx: version 0.1\n");
1521 printf("%s: unknown option '-%c'\n",
1528 request_mask = format_to_mask(format);
1530 request_mask = STATX_ALL;
1532 format = long_format ? list_long_format() : NULL;
1534 format = default_format(false, terse, false);
1537 if (optind == argc) {
1539 return do_dir_list(".", request_mask, flags);
1541 printf("statx: missing operand\n"
1542 "Try 'stat --help' for more information.\n");
1546 for (i = optind; i < argc; i++) {
1550 ret = do_dir_list(argv[i], request_mask, flags);
1552 ret = do_statx(argv[i], request_mask, flags);
1561 int main(int argc, char **argv)
1563 static struct option options[] = {
1564 {"version", no_argument, NULL, GETOPT_VERSION_CHAR},
1565 {"quiet", no_argument, NULL, 'q'},
1570 while ((c = getopt_long(argc, argv, "q", options, NULL)) != EOF) {
1575 case GETOPT_VERSION_CHAR:
1577 printf("statx: Not support statx() syscall\n");
1581 printf("Skip: system does not support statx syscall.\n");
1584 #endif /* __NR_statx */