maint: pacify ‘make syntax-check’
[coreutils.git] / src / stat.c
blob522e922ed4407b394b8bccc82d626502571d6f2c
1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2023 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>.
17 Written by Michael Meskes. */
19 #include <config.h>
21 /* Keep this conditional in sync with the similar conditional in
22 ../m4/stat-prog.m4. */
23 #if ((STAT_STATVFS || STAT_STATVFS64) \
24 && (HAVE_STRUCT_STATVFS_F_BASETYPE || HAVE_STRUCT_STATVFS_F_FSTYPENAME \
25 || (! HAVE_STRUCT_STATFS_F_FSTYPENAME && HAVE_STRUCT_STATVFS_F_TYPE)))
26 # define USE_STATVFS 1
27 #else
28 # define USE_STATVFS 0
29 #endif
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <pwd.h>
34 #include <grp.h>
35 #if USE_STATVFS
36 # include <sys/statvfs.h>
37 #elif HAVE_SYS_VFS_H
38 # include <sys/vfs.h>
39 #elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
40 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
41 It does have statvfs.h, but shouldn't use it, since it doesn't
42 HAVE_STRUCT_STATVFS_F_BASETYPE. So find a clean way to fix it. */
43 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
44 # include <sys/param.h>
45 # include <sys/mount.h>
46 # if HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
47 /* Ultrix 4.4 needs these for the declaration of struct statfs. */
48 # include <netinet/in.h>
49 # include <nfs/nfs_clnt.h>
50 # include <nfs/vfs.h>
51 # endif
52 #elif HAVE_OS_H /* BeOS */
53 # include <fs_info.h>
54 #endif
55 #include <selinux/selinux.h>
57 #include "system.h"
59 #include "areadlink.h"
60 #include "argmatch.h"
61 #include "c-ctype.h"
62 #include "file-type.h"
63 #include "filemode.h"
64 #include "fs.h"
65 #include "getopt.h"
66 #include "mountlist.h"
67 #include "quote.h"
68 #include "stat-size.h"
69 #include "stat-time.h"
70 #include "strftime.h"
71 #include "find-mount-point.h"
72 #include "xvasprintf.h"
73 #include "statx.h"
75 #if HAVE_STATX && defined STATX_INO
76 # define USE_STATX 1
77 #else
78 # define USE_STATX 0
79 #endif
81 #if USE_STATVFS
82 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
83 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
84 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
85 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
86 # endif
87 # if ! STAT_STATVFS && STAT_STATVFS64
88 # define STRUCT_STATVFS struct statvfs64
89 # define STATFS statvfs64
90 # else
91 # define STRUCT_STATVFS struct statvfs
92 # define STATFS statvfs
93 # endif
94 # define STATFS_FRSIZE(S) ((S)->f_frsize)
95 #else
96 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
97 # if HAVE_STRUCT_STATFS_F_NAMELEN
98 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
99 # elif HAVE_STRUCT_STATFS_F_NAMEMAX
100 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
101 # endif
102 # define STATFS statfs
103 # if HAVE_OS_H /* BeOS */
104 /* BeOS has a statvfs function, but it does not return sensible values
105 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
106 f_fstypename. Use 'struct fs_info' instead. */
107 NODISCARD
108 static int
109 statfs (char const *filename, struct fs_info *buf)
111 dev_t device = dev_for_path (filename);
112 if (device < 0)
114 errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
115 : device == B_BAD_VALUE ? EINVAL
116 : device == B_NAME_TOO_LONG ? ENAMETOOLONG
117 : device == B_NO_MEMORY ? ENOMEM
118 : device == B_FILE_ERROR ? EIO
119 : 0);
120 return -1;
122 /* If successful, buf->dev will be == device. */
123 return fs_stat_dev (device, buf);
125 # define f_fsid dev
126 # define f_blocks total_blocks
127 # define f_bfree free_blocks
128 # define f_bavail free_blocks
129 # define f_bsize io_size
130 # define f_files total_nodes
131 # define f_ffree free_nodes
132 # define STRUCT_STATVFS struct fs_info
133 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
134 # define STATFS_FRSIZE(S) ((S)->block_size)
135 # else
136 # define STRUCT_STATVFS struct statfs
137 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
138 # if HAVE_STRUCT_STATFS_F_FRSIZE
139 # define STATFS_FRSIZE(S) ((S)->f_frsize)
140 # else
141 # define STATFS_FRSIZE(S) 0
142 # endif
143 # endif
144 #endif
146 #ifdef SB_F_NAMEMAX
147 # define OUT_NAMEMAX out_uint
148 #else
149 /* Depending on whether statvfs or statfs is used,
150 neither f_namemax or f_namelen may be available. */
151 # define SB_F_NAMEMAX(S) "?"
152 # define OUT_NAMEMAX out_string
153 #endif
155 #if HAVE_STRUCT_STATVFS_F_BASETYPE
156 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
157 #else
158 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
159 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
160 # elif HAVE_OS_H /* BeOS */
161 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
162 # endif
163 #endif
165 #if HAVE_GETATTRAT
166 # include <attr.h>
167 # include <sys/nvpair.h>
168 #endif
170 /* FIXME: these are used by printf.c, too */
171 #define isodigit(c) ('0' <= (c) && (c) <= '7')
172 #define octtobin(c) ((c) - '0')
173 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
174 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
176 static char const digits[] = "0123456789";
178 /* Flags that are portable for use in printf, for at least one
179 conversion specifier; make_format removes non-portable flags as
180 needed for particular specifiers. The glibc 2.2 extension "I" is
181 listed here; it is removed by make_format because it has undefined
182 behavior elsewhere and because it is incompatible with
183 out_epoch_sec. */
184 static char const printf_flags[] = "'-+ #0I";
186 /* Formats for the --terse option. */
187 static char const fmt_terse_fs[] = "%n %i %l %t %s %S %b %f %a %c %d\n";
188 static char const fmt_terse_regular[] = "%n %s %b %f %u %g %D %i %h %t %T"
189 " %X %Y %Z %W %o\n";
190 static char const fmt_terse_selinux[] = "%n %s %b %f %u %g %D %i %h %t %T"
191 " %X %Y %Z %W %o %C\n";
193 #define PROGRAM_NAME "stat"
195 #define AUTHORS proper_name ("Michael Meskes")
197 enum
199 PRINTF_OPTION = CHAR_MAX + 1
202 enum cached_mode
204 cached_default,
205 cached_never,
206 cached_always
209 static char const *const cached_args[] =
211 "default", "never", "always", nullptr
214 static enum cached_mode const cached_modes[] =
216 cached_default, cached_never, cached_always
219 static struct option const long_options[] =
221 {"dereference", no_argument, nullptr, 'L'},
222 {"file-system", no_argument, nullptr, 'f'},
223 {"format", required_argument, nullptr, 'c'},
224 {"printf", required_argument, nullptr, PRINTF_OPTION},
225 {"terse", no_argument, nullptr, 't'},
226 {"cached", required_argument, nullptr, 0},
227 {GETOPT_HELP_OPTION_DECL},
228 {GETOPT_VERSION_OPTION_DECL},
229 {nullptr, 0, nullptr, 0}
232 /* Whether to follow symbolic links; True for --dereference (-L). */
233 static bool follow_links;
235 /* Whether to interpret backslash-escape sequences.
236 True for --printf=FMT, not for --format=FMT (-c). */
237 static bool interpret_backslash_escapes;
239 /* The trailing delimiter string:
240 "" for --printf=FMT, "\n" for --format=FMT (-c). */
241 static char const *trailing_delim = "";
243 /* The representation of the decimal point in the current locale. */
244 static char const *decimal_point;
245 static size_t decimal_point_len;
247 static bool
248 print_stat (char *pformat, size_t prefix_len, char mod, char m,
249 int fd, char const *filename, void const *data);
251 /* Return the type of the specified file system.
252 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
253 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
254 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
255 Still others have neither and have to get by with f_type (GNU/Linux).
256 But f_type may only exist in statfs (Cygwin). */
257 NODISCARD
258 static char const *
259 human_fstype (STRUCT_STATVFS const *statfsbuf)
261 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
262 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
263 #else
264 switch (statfsbuf->f_type)
266 # if defined __linux__ || defined __ANDROID__
268 /* Compare with what's in libc:
269 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
270 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
271 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
272 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
273 | sort > sym_libc
274 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
275 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
276 | sort > sym_stat
277 diff -u sym_stat sym_libc
280 /* Also compare with the list in "man 2 statfs" using the
281 fs-magic-compare make target. */
283 /* IMPORTANT NOTE: Each of the following 'case S_MAGIC_...:'
284 statements must be followed by a hexadecimal constant in
285 a comment. The S_MAGIC_... name and constant are automatically
286 combined to produce the #define directives in fs.h. */
288 case S_MAGIC_AAFS: /* 0x5A3C69F0 local */
289 return "aafs";
290 case S_MAGIC_ACFS: /* 0x61636673 remote */
291 return "acfs";
292 case S_MAGIC_ADFS: /* 0xADF5 local */
293 return "adfs";
294 case S_MAGIC_AFFS: /* 0xADFF local */
295 return "affs";
296 case S_MAGIC_AFS: /* 0x5346414F remote */
297 return "afs";
298 case S_MAGIC_ANON_INODE_FS: /* 0x09041934 local */
299 return "anon-inode FS";
300 case S_MAGIC_AUFS: /* 0x61756673 remote */
301 /* FIXME: change syntax or add an optional attribute like "inotify:no".
302 The above is labeled as "remote" so that tail always uses polling,
303 but this isn't really a remote file system type. */
304 return "aufs";
305 case S_MAGIC_AUTOFS: /* 0x0187 local */
306 return "autofs";
307 case S_MAGIC_BALLOON_KVM: /* 0x13661366 local */
308 return "balloon-kvm-fs";
309 case S_MAGIC_BEFS: /* 0x42465331 local */
310 return "befs";
311 case S_MAGIC_BDEVFS: /* 0x62646576 local */
312 return "bdevfs";
313 case S_MAGIC_BFS: /* 0x1BADFACE local */
314 return "bfs";
315 case S_MAGIC_BINDERFS: /* 0x6C6F6F70 local */
316 return "binderfs";
317 case S_MAGIC_BPF_FS: /* 0xCAFE4A11 local */
318 return "bpf_fs";
319 case S_MAGIC_BINFMTFS: /* 0x42494E4D local */
320 return "binfmt_misc";
321 case S_MAGIC_BTRFS: /* 0x9123683E local */
322 return "btrfs";
323 case S_MAGIC_BTRFS_TEST: /* 0x73727279 local */
324 return "btrfs_test";
325 case S_MAGIC_CEPH: /* 0x00C36400 remote */
326 return "ceph";
327 case S_MAGIC_CGROUP: /* 0x0027E0EB local */
328 return "cgroupfs";
329 case S_MAGIC_CGROUP2: /* 0x63677270 local */
330 return "cgroup2fs";
331 case S_MAGIC_CIFS: /* 0xFF534D42 remote */
332 return "cifs";
333 case S_MAGIC_CODA: /* 0x73757245 remote */
334 return "coda";
335 case S_MAGIC_COH: /* 0x012FF7B7 local */
336 return "coh";
337 case S_MAGIC_CONFIGFS: /* 0x62656570 local */
338 return "configfs";
339 case S_MAGIC_CRAMFS: /* 0x28CD3D45 local */
340 return "cramfs";
341 case S_MAGIC_CRAMFS_WEND: /* 0x453DCD28 local */
342 return "cramfs-wend";
343 case S_MAGIC_DAXFS: /* 0x64646178 local */
344 return "daxfs";
345 case S_MAGIC_DEBUGFS: /* 0x64626720 local */
346 return "debugfs";
347 case S_MAGIC_DEVFS: /* 0x1373 local */
348 return "devfs";
349 case S_MAGIC_DEVMEM: /* 0x454D444D local */
350 return "devmem";
351 case S_MAGIC_DEVPTS: /* 0x1CD1 local */
352 return "devpts";
353 case S_MAGIC_DMA_BUF: /* 0x444D4142 local */
354 return "dma-buf-fs";
355 case S_MAGIC_ECRYPTFS: /* 0xF15F local */
356 return "ecryptfs";
357 case S_MAGIC_EFIVARFS: /* 0xDE5E81E4 local */
358 return "efivarfs";
359 case S_MAGIC_EFS: /* 0x00414A53 local */
360 return "efs";
361 case S_MAGIC_EROFS_V1: /* 0xE0F5E1E2 local */
362 return "erofs";
363 case S_MAGIC_EXFAT: /* 0x2011BAB0 local */
364 return "exfat";
365 case S_MAGIC_EXFS: /* 0x45584653 local */
366 return "exfs";
367 case S_MAGIC_EXOFS: /* 0x5DF5 local */
368 return "exofs";
369 case S_MAGIC_EXT: /* 0x137D local */
370 return "ext";
371 case S_MAGIC_EXT2: /* 0xEF53 local */
372 return "ext2/ext3";
373 case S_MAGIC_EXT2_OLD: /* 0xEF51 local */
374 return "ext2";
375 case S_MAGIC_F2FS: /* 0xF2F52010 local */
376 return "f2fs";
377 case S_MAGIC_FAT: /* 0x4006 local */
378 return "fat";
379 case S_MAGIC_FHGFS: /* 0x19830326 remote */
380 return "fhgfs";
381 case S_MAGIC_FUSEBLK: /* 0x65735546 remote */
382 return "fuseblk";
383 case S_MAGIC_FUSECTL: /* 0x65735543 remote */
384 return "fusectl";
385 case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA local */
386 return "futexfs";
387 case S_MAGIC_GFS: /* 0x01161970 remote */
388 return "gfs/gfs2";
389 case S_MAGIC_GPFS: /* 0x47504653 remote */
390 return "gpfs";
391 case S_MAGIC_HFS: /* 0x4244 local */
392 return "hfs";
393 case S_MAGIC_HFS_PLUS: /* 0x482B local */
394 return "hfs+";
395 case S_MAGIC_HFS_X: /* 0x4858 local */
396 return "hfsx";
397 case S_MAGIC_HOSTFS: /* 0x00C0FFEE local */
398 return "hostfs";
399 case S_MAGIC_HPFS: /* 0xF995E849 local */
400 return "hpfs";
401 case S_MAGIC_HUGETLBFS: /* 0x958458F6 local */
402 return "hugetlbfs";
403 case S_MAGIC_MTD_INODE_FS: /* 0x11307854 local */
404 return "inodefs";
405 case S_MAGIC_IBRIX: /* 0x013111A8 remote */
406 return "ibrix";
407 case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA local */
408 return "inotifyfs";
409 case S_MAGIC_ISOFS: /* 0x9660 local */
410 return "isofs";
411 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 local */
412 return "isofs";
413 case S_MAGIC_ISOFS_WIN: /* 0x4000 local */
414 return "isofs";
415 case S_MAGIC_JFFS: /* 0x07C0 local */
416 return "jffs";
417 case S_MAGIC_JFFS2: /* 0x72B6 local */
418 return "jffs2";
419 case S_MAGIC_JFS: /* 0x3153464A local */
420 return "jfs";
421 case S_MAGIC_KAFS: /* 0x6B414653 remote */
422 return "k-afs";
423 case S_MAGIC_LOGFS: /* 0xC97E8168 local */
424 return "logfs";
425 case S_MAGIC_LUSTRE: /* 0x0BD00BD0 remote */
426 return "lustre";
427 case S_MAGIC_M1FS: /* 0x5346314D local */
428 return "m1fs";
429 case S_MAGIC_MINIX: /* 0x137F local */
430 return "minix";
431 case S_MAGIC_MINIX_30: /* 0x138F local */
432 return "minix (30 char.)";
433 case S_MAGIC_MINIX_V2: /* 0x2468 local */
434 return "minix v2";
435 case S_MAGIC_MINIX_V2_30: /* 0x2478 local */
436 return "minix v2 (30 char.)";
437 case S_MAGIC_MINIX_V3: /* 0x4D5A local */
438 return "minix3";
439 case S_MAGIC_MQUEUE: /* 0x19800202 local */
440 return "mqueue";
441 case S_MAGIC_MSDOS: /* 0x4D44 local */
442 return "msdos";
443 case S_MAGIC_NCP: /* 0x564C remote */
444 return "novell";
445 case S_MAGIC_NFS: /* 0x6969 remote */
446 return "nfs";
447 case S_MAGIC_NFSD: /* 0x6E667364 remote */
448 return "nfsd";
449 case S_MAGIC_NILFS: /* 0x3434 local */
450 return "nilfs";
451 case S_MAGIC_NSFS: /* 0x6E736673 local */
452 return "nsfs";
453 case S_MAGIC_NTFS: /* 0x5346544E local */
454 return "ntfs";
455 case S_MAGIC_OPENPROM: /* 0x9FA1 local */
456 return "openprom";
457 case S_MAGIC_OCFS2: /* 0x7461636F remote */
458 return "ocfs2";
459 case S_MAGIC_OVERLAYFS: /* 0x794C7630 remote */
460 /* This may overlay remote file systems.
461 Also there have been issues reported with inotify and overlayfs,
462 so mark as "remote" so that polling is used. */
463 return "overlayfs";
464 case S_MAGIC_PANFS: /* 0xAAD7AAEA remote */
465 return "panfs";
466 case S_MAGIC_PIPEFS: /* 0x50495045 remote */
467 /* FIXME: change syntax or add an optional attribute like "inotify:no".
468 pipefs and prlfs are labeled as "remote" so that tail always polls,
469 but these aren't really remote file system types. */
470 return "pipefs";
471 case S_MAGIC_PPC_CMM: /* 0xC7571590 local */
472 return "ppc-cmm-fs";
473 case S_MAGIC_PRL_FS: /* 0x7C7C6673 remote */
474 return "prl_fs";
475 case S_MAGIC_PROC: /* 0x9FA0 local */
476 return "proc";
477 case S_MAGIC_PSTOREFS: /* 0x6165676C local */
478 return "pstorefs";
479 case S_MAGIC_QNX4: /* 0x002F local */
480 return "qnx4";
481 case S_MAGIC_QNX6: /* 0x68191122 local */
482 return "qnx6";
483 case S_MAGIC_RAMFS: /* 0x858458F6 local */
484 return "ramfs";
485 case S_MAGIC_RDTGROUP: /* 0x07655821 local */
486 return "rdt";
487 case S_MAGIC_REISERFS: /* 0x52654973 local */
488 return "reiserfs";
489 case S_MAGIC_ROMFS: /* 0x7275 local */
490 return "romfs";
491 case S_MAGIC_RPC_PIPEFS: /* 0x67596969 local */
492 return "rpc_pipefs";
493 case S_MAGIC_SDCARDFS: /* 0x5DCA2DF5 local */
494 return "sdcardfs";
495 case S_MAGIC_SECRETMEM: /* 0x5345434D local */
496 return "secretmem";
497 case S_MAGIC_SECURITYFS: /* 0x73636673 local */
498 return "securityfs";
499 case S_MAGIC_SELINUX: /* 0xF97CFF8C local */
500 return "selinux";
501 case S_MAGIC_SMACK: /* 0x43415D53 local */
502 return "smackfs";
503 case S_MAGIC_SMB: /* 0x517B remote */
504 return "smb";
505 case S_MAGIC_SMB2: /* 0xFE534D42 remote */
506 return "smb2";
507 case S_MAGIC_SNFS: /* 0xBEEFDEAD remote */
508 return "snfs";
509 case S_MAGIC_SOCKFS: /* 0x534F434B local */
510 return "sockfs";
511 case S_MAGIC_SQUASHFS: /* 0x73717368 local */
512 return "squashfs";
513 case S_MAGIC_SYSFS: /* 0x62656572 local */
514 return "sysfs";
515 case S_MAGIC_SYSV2: /* 0x012FF7B6 local */
516 return "sysv2";
517 case S_MAGIC_SYSV4: /* 0x012FF7B5 local */
518 return "sysv4";
519 case S_MAGIC_TMPFS: /* 0x01021994 local */
520 return "tmpfs";
521 case S_MAGIC_TRACEFS: /* 0x74726163 local */
522 return "tracefs";
523 case S_MAGIC_UBIFS: /* 0x24051905 local */
524 return "ubifs";
525 case S_MAGIC_UDF: /* 0x15013346 local */
526 return "udf";
527 case S_MAGIC_UFS: /* 0x00011954 local */
528 return "ufs";
529 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 local */
530 return "ufs";
531 case S_MAGIC_USBDEVFS: /* 0x9FA2 local */
532 return "usbdevfs";
533 case S_MAGIC_V9FS: /* 0x01021997 local */
534 return "v9fs";
535 case S_MAGIC_VBOXSF: /* 0x786F4256 remote */
536 return "vboxsf";
537 case S_MAGIC_VMHGFS: /* 0xBACBACBC remote */
538 return "vmhgfs";
539 case S_MAGIC_VXFS: /* 0xA501FCF5 remote */
540 /* Veritas File System can run in single instance or clustered mode,
541 so mark as remote to cater for the latter case. */
542 return "vxfs";
543 case S_MAGIC_VZFS: /* 0x565A4653 local */
544 return "vzfs";
545 case S_MAGIC_WSLFS: /* 0x53464846 local */
546 return "wslfs";
547 case S_MAGIC_XENFS: /* 0xABBA1974 local */
548 return "xenfs";
549 case S_MAGIC_XENIX: /* 0x012FF7B4 local */
550 return "xenix";
551 case S_MAGIC_XFS: /* 0x58465342 local */
552 return "xfs";
553 case S_MAGIC_XIAFS: /* 0x012FD16D local */
554 return "xia";
555 case S_MAGIC_Z3FOLD: /* 0x0033 local */
556 return "z3fold";
557 case S_MAGIC_ZFS: /* 0x2FC12FC1 local */
558 return "zfs";
559 case S_MAGIC_ZONEFS: /* 0x5A4F4653 local */
560 return "zonefs";
561 case S_MAGIC_ZSMALLOC: /* 0x58295829 local */
562 return "zsmallocfs";
565 # elif __GNU__
566 case FSTYPE_UFS:
567 return "ufs";
568 case FSTYPE_NFS:
569 return "nfs";
570 case FSTYPE_GFS:
571 return "gfs";
572 case FSTYPE_LFS:
573 return "lfs";
574 case FSTYPE_SYSV:
575 return "sysv";
576 case FSTYPE_FTP:
577 return "ftp";
578 case FSTYPE_TAR:
579 return "tar";
580 case FSTYPE_AR:
581 return "ar";
582 case FSTYPE_CPIO:
583 return "cpio";
584 case FSTYPE_MSLOSS:
585 return "msloss";
586 case FSTYPE_CPM:
587 return "cpm";
588 case FSTYPE_HFS:
589 return "hfs";
590 case FSTYPE_DTFS:
591 return "dtfs";
592 case FSTYPE_GRFS:
593 return "grfs";
594 case FSTYPE_TERM:
595 return "term";
596 case FSTYPE_DEV:
597 return "dev";
598 case FSTYPE_PROC:
599 return "proc";
600 case FSTYPE_IFSOCK:
601 return "ifsock";
602 case FSTYPE_AFS:
603 return "afs";
604 case FSTYPE_DFS:
605 return "dfs";
606 case FSTYPE_PROC9:
607 return "proc9";
608 case FSTYPE_SOCKET:
609 return "socket";
610 case FSTYPE_MISC:
611 return "misc";
612 case FSTYPE_EXT2FS:
613 return "ext2/ext3";
614 case FSTYPE_HTTP:
615 return "http";
616 case FSTYPE_MEMFS:
617 return "memfs";
618 case FSTYPE_ISO9660:
619 return "iso9660";
620 # endif
621 default:
623 unsigned long int type = statfsbuf->f_type;
624 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
625 + (sizeof type * CHAR_BIT + 3) / 4];
626 sprintf (buf, "UNKNOWN (0x%lx)", type);
627 return buf;
630 #endif
633 NODISCARD
634 static char *
635 human_access (struct stat const *statbuf)
637 static char modebuf[12];
638 filemodestring (statbuf, modebuf);
639 modebuf[10] = 0;
640 return modebuf;
643 NODISCARD
644 static char *
645 human_time (struct timespec t)
647 /* STR must be at least INT_BUFSIZE_BOUND (intmax_t) big, either
648 because localtime_rz fails, or because the time zone is truly
649 outlandish so that %z expands to a long string. */
650 static char str[INT_BUFSIZE_BOUND (intmax_t)
651 + INT_STRLEN_BOUND (int) /* YYYY */
652 + 1 /* because YYYY might equal INT_MAX + 1900 */
653 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +"];
654 static timezone_t tz;
655 if (!tz)
656 tz = tzalloc (getenv ("TZ"));
657 struct tm tm;
658 int ns = t.tv_nsec;
659 if (localtime_rz (tz, &t.tv_sec, &tm))
660 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", &tm, tz, ns);
661 else
663 char secbuf[INT_BUFSIZE_BOUND (intmax_t)];
664 sprintf (str, "%s.%09d", timetostr (t.tv_sec, secbuf), ns);
666 return str;
669 /* PFORMAT points to a '%' followed by a prefix of a format, all of
670 size PREFIX_LEN. The flags allowed for this format are
671 ALLOWED_FLAGS; remove other printf flags from the prefix, then
672 append SUFFIX. */
673 static void
674 make_format (char *pformat, size_t prefix_len, char const *allowed_flags,
675 char const *suffix)
677 char *dst = pformat + 1;
678 char const *src;
679 char const *srclim = pformat + prefix_len;
680 for (src = dst; src < srclim && strchr (printf_flags, *src); src++)
681 if (strchr (allowed_flags, *src))
682 *dst++ = *src;
683 while (src < srclim)
684 *dst++ = *src++;
685 strcpy (dst, suffix);
688 static void
689 out_string (char *pformat, size_t prefix_len, char const *arg)
691 make_format (pformat, prefix_len, "-", "s");
692 printf (pformat, arg);
694 static int
695 out_int (char *pformat, size_t prefix_len, intmax_t arg)
697 make_format (pformat, prefix_len, "'-+ 0", "jd");
698 return printf (pformat, arg);
700 static int
701 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
703 make_format (pformat, prefix_len, "'-0", "ju");
704 return printf (pformat, arg);
706 static void
707 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
709 make_format (pformat, prefix_len, "-#0", "jo");
710 printf (pformat, arg);
712 static void
713 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
715 make_format (pformat, prefix_len, "-#0", "jx");
716 printf (pformat, arg);
718 static int
719 out_minus_zero (char *pformat, size_t prefix_len)
721 make_format (pformat, prefix_len, "'-+ 0", ".0f");
722 return printf (pformat, -0.25);
725 /* Output the number of seconds since the Epoch, using a format that
726 acts like printf's %f format. */
727 static void
728 out_epoch_sec (char *pformat, size_t prefix_len,
729 struct timespec arg)
731 char *dot = memchr (pformat, '.', prefix_len);
732 size_t sec_prefix_len = prefix_len;
733 int width = 0;
734 int precision = 0;
735 bool frac_left_adjust = false;
737 if (dot)
739 sec_prefix_len = dot - pformat;
740 pformat[prefix_len] = '\0';
742 if (ISDIGIT (dot[1]))
744 long int lprec = strtol (dot + 1, nullptr, 10);
745 precision = (lprec <= INT_MAX ? lprec : INT_MAX);
747 else
749 precision = 9;
752 if (precision && ISDIGIT (dot[-1]))
754 /* If a nontrivial width is given, subtract the width of the
755 decimal point and PRECISION digits that will be output
756 later. */
757 char *p = dot;
758 *dot = '\0';
761 --p;
762 while (ISDIGIT (p[-1]));
764 long int lwidth = strtol (p, nullptr, 10);
765 width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
766 if (1 < width)
768 p += (*p == '0');
769 sec_prefix_len = p - pformat;
770 int w_d = (decimal_point_len < width
771 ? width - decimal_point_len
772 : 0);
773 if (1 < w_d)
775 int w = w_d - precision;
776 if (1 < w)
778 char *dst = pformat;
779 for (char const *src = dst; src < p; src++)
781 if (*src == '-')
782 frac_left_adjust = true;
783 else
784 *dst++ = *src;
786 sec_prefix_len =
787 (dst - pformat
788 + (frac_left_adjust ? 0 : sprintf (dst, "%d", w)));
795 int divisor = 1;
796 for (int i = precision; i < 9; i++)
797 divisor *= 10;
798 int frac_sec = arg.tv_nsec / divisor;
799 int int_len;
801 if (TYPE_SIGNED (time_t))
803 bool minus_zero = false;
804 if (arg.tv_sec < 0 && arg.tv_nsec != 0)
806 int frac_sec_modulus = 1000000000 / divisor;
807 frac_sec = (frac_sec_modulus - frac_sec
808 - (arg.tv_nsec % divisor != 0));
809 arg.tv_sec += (frac_sec != 0);
810 minus_zero = (arg.tv_sec == 0);
812 int_len = (minus_zero
813 ? out_minus_zero (pformat, sec_prefix_len)
814 : out_int (pformat, sec_prefix_len, arg.tv_sec));
816 else
817 int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec);
819 if (precision)
821 int prec = (precision < 9 ? precision : 9);
822 int trailing_prec = precision - prec;
823 int ilen = (int_len < 0 ? 0 : int_len);
824 int trailing_width = (ilen < width && decimal_point_len < width - ilen
825 ? width - ilen - decimal_point_len - prec
826 : 0);
827 printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
828 trailing_width, trailing_prec, 0);
832 /* Print the context information of FILENAME, and return true iff the
833 context could not be obtained. */
834 NODISCARD
835 static bool
836 out_file_context (char *pformat, size_t prefix_len, char const *filename)
838 char *scontext;
839 bool fail = false;
841 if ((follow_links
842 ? getfilecon (filename, &scontext)
843 : lgetfilecon (filename, &scontext)) < 0)
845 error (0, errno, _("failed to get security context of %s"),
846 quoteaf (filename));
847 scontext = nullptr;
848 fail = true;
850 strcpy (pformat + prefix_len, "s");
851 printf (pformat, (scontext ? scontext : "?"));
852 if (scontext)
853 freecon (scontext);
854 return fail;
857 /* Print statfs info. Return zero upon success, nonzero upon failure. */
858 NODISCARD
859 static bool
860 print_statfs (char *pformat, size_t prefix_len, MAYBE_UNUSED char mod, char m,
861 int fd, char const *filename,
862 void const *data)
864 STRUCT_STATVFS const *statfsbuf = data;
865 bool fail = false;
867 switch (m)
869 case 'n':
870 out_string (pformat, prefix_len, filename);
871 break;
873 case 'i':
875 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
876 uintmax_t fsid = statfsbuf->f_fsid;
877 #else
878 typedef unsigned int fsid_word;
879 static_assert (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
880 static_assert (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word)
881 == 0);
882 static_assert (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
883 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
885 /* Assume a little-endian word order, as that is compatible
886 with glibc's statvfs implementation. */
887 uintmax_t fsid = 0;
888 int words = sizeof statfsbuf->f_fsid / sizeof *p;
889 for (int i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
891 uintmax_t u = p[words - 1 - i];
892 fsid |= u << (i * CHAR_BIT * sizeof *p);
894 #endif
895 out_uint_x (pformat, prefix_len, fsid);
897 break;
899 case 'l':
900 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
901 break;
902 case 't':
903 #if HAVE_STRUCT_STATXFS_F_TYPE
904 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
905 #else
906 fputc ('?', stdout);
907 #endif
908 break;
909 case 'T':
910 out_string (pformat, prefix_len, human_fstype (statfsbuf));
911 break;
912 case 'b':
913 out_int (pformat, prefix_len, statfsbuf->f_blocks);
914 break;
915 case 'f':
916 out_int (pformat, prefix_len, statfsbuf->f_bfree);
917 break;
918 case 'a':
919 out_int (pformat, prefix_len, statfsbuf->f_bavail);
920 break;
921 case 's':
922 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
923 break;
924 case 'S':
926 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
927 if (! frsize)
928 frsize = statfsbuf->f_bsize;
929 out_uint (pformat, prefix_len, frsize);
931 break;
932 case 'c':
933 out_uint (pformat, prefix_len, statfsbuf->f_files);
934 break;
935 case 'd':
936 out_int (pformat, prefix_len, statfsbuf->f_ffree);
937 break;
938 default:
939 fputc ('?', stdout);
940 break;
942 return fail;
945 /* Return any bind mounted source for a path.
946 The caller should not free the returned buffer.
947 Return nullptr if no bind mount found. */
948 NODISCARD
949 static char const *
950 find_bind_mount (char const * name)
952 char const * bind_mount = nullptr;
954 static struct mount_entry *mount_list;
955 static bool tried_mount_list = false;
956 if (!tried_mount_list) /* attempt/warn once per process. */
958 if (!(mount_list = read_file_system_list (false)))
959 error (0, errno, "%s", _("cannot read table of mounted file systems"));
960 tried_mount_list = true;
963 struct stat name_stats;
964 if (stat (name, &name_stats) != 0)
965 return nullptr;
967 struct mount_entry *me;
968 for (me = mount_list; me; me = me->me_next)
970 if (me->me_dummy && me->me_devname[0] == '/'
971 && STREQ (me->me_mountdir, name))
973 struct stat dev_stats;
975 if (stat (me->me_devname, &dev_stats) == 0
976 && psame_inode (&name_stats, &dev_stats))
978 bind_mount = me->me_devname;
979 break;
984 return bind_mount;
987 /* Print mount point. Return zero upon success, nonzero upon failure. */
988 NODISCARD
989 static bool
990 out_mount_point (char const *filename, char *pformat, size_t prefix_len,
991 const struct stat *statp)
994 char const *np = "?", *bp = nullptr;
995 char *mp = nullptr;
996 bool fail = true;
998 /* Look for bind mounts first. Note we output the immediate alias,
999 rather than further resolving to a base device mount point. */
1000 if (follow_links || !S_ISLNK (statp->st_mode))
1002 char *resolved = canonicalize_file_name (filename);
1003 if (!resolved)
1005 error (0, errno, _("failed to canonicalize %s"), quoteaf (filename));
1006 goto print_mount_point;
1008 bp = find_bind_mount (resolved);
1009 free (resolved);
1010 if (bp)
1012 fail = false;
1013 goto print_mount_point;
1017 /* If there is no direct bind mount, then navigate
1018 back up the tree looking for a device change.
1019 Note we don't detect if any of the directory components
1020 are bind mounted to the same device, but that's OK
1021 since we've not directly queried them. */
1022 if ((mp = find_mount_point (filename, statp)))
1024 /* This dir might be bind mounted to another device,
1025 so we resolve the bound source in that case also. */
1026 bp = find_bind_mount (mp);
1027 fail = false;
1030 print_mount_point:
1032 out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
1033 free (mp);
1034 return fail;
1037 /* Map a TS with negative TS.tv_nsec to {0,0}. */
1038 static inline struct timespec
1039 neg_to_zero (struct timespec ts)
1041 if (0 <= ts.tv_nsec)
1042 return ts;
1043 struct timespec z = {0};
1044 return z;
1047 /* Set the quoting style default if the environment variable
1048 QUOTING_STYLE is set. */
1050 static void
1051 getenv_quoting_style (void)
1053 char const *q_style = getenv ("QUOTING_STYLE");
1054 if (q_style)
1056 int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals);
1057 if (0 <= i)
1058 set_quoting_style (nullptr, quoting_style_vals[i]);
1059 else
1061 set_quoting_style (nullptr, shell_escape_always_quoting_style);
1062 error (0, 0, _("ignoring invalid value of environment "
1063 "variable QUOTING_STYLE: %s"), quote (q_style));
1066 else
1067 set_quoting_style (nullptr, shell_escape_always_quoting_style);
1070 /* Equivalent to quotearg(), but explicit to avoid syntax checks. */
1071 #define quoteN(x) quotearg_style (get_quoting_style (nullptr), x)
1073 /* Output a single-character \ escape. */
1075 static void
1076 print_esc_char (char c)
1078 switch (c)
1080 case 'a': /* Alert. */
1081 c ='\a';
1082 break;
1083 case 'b': /* Backspace. */
1084 c ='\b';
1085 break;
1086 case 'e': /* Escape. */
1087 c ='\x1B';
1088 break;
1089 case 'f': /* Form feed. */
1090 c ='\f';
1091 break;
1092 case 'n': /* New line. */
1093 c ='\n';
1094 break;
1095 case 'r': /* Carriage return. */
1096 c ='\r';
1097 break;
1098 case 't': /* Horizontal tab. */
1099 c ='\t';
1100 break;
1101 case 'v': /* Vertical tab. */
1102 c ='\v';
1103 break;
1104 case '"':
1105 case '\\':
1106 break;
1107 default:
1108 error (0, 0, _("warning: unrecognized escape '\\%c'"), c);
1109 break;
1111 putchar (c);
1114 ATTRIBUTE_PURE
1115 static size_t
1116 format_code_offset (char const *directive)
1118 size_t len = strspn (directive + 1, printf_flags);
1119 char const *fmt_char = directive + len + 1;
1120 fmt_char += strspn (fmt_char, digits);
1121 if (*fmt_char == '.')
1122 fmt_char += 1 + strspn (fmt_char + 1, digits);
1123 return fmt_char - directive;
1126 /* Print the information specified by the format string, FORMAT,
1127 calling PRINT_FUNC for each %-directive encountered.
1128 Return zero upon success, nonzero upon failure. */
1129 NODISCARD
1130 static bool
1131 print_it (char const *format, int fd, char const *filename,
1132 bool (*print_func) (char *, size_t, char, char,
1133 int, char const *, void const *),
1134 void const *data)
1136 bool fail = false;
1138 /* Add 2 to accommodate our conversion of the stat '%s' format string
1139 to the longer printf '%llu' one. */
1140 enum
1142 MAX_ADDITIONAL_BYTES =
1143 (MAX (sizeof "jd",
1144 MAX (sizeof "jo", MAX (sizeof "ju", sizeof "jx")))
1145 - 1)
1147 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
1148 char *dest = xmalloc (n_alloc);
1149 char const *b;
1150 for (b = format; *b; b++)
1152 switch (*b)
1154 case '%':
1156 size_t len = format_code_offset (b);
1157 char fmt_char = *(b + len);
1158 char mod_char = 0;
1159 memcpy (dest, b, len);
1160 b += len;
1162 switch (fmt_char)
1164 case '\0':
1165 --b;
1166 FALLTHROUGH;
1167 case '%':
1168 if (1 < len)
1170 dest[len] = fmt_char;
1171 dest[len + 1] = '\0';
1172 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
1173 quote (dest));
1175 putchar ('%');
1176 break;
1177 case 'H':
1178 case 'L':
1179 mod_char = fmt_char;
1180 fmt_char = *(b + 1);
1181 if (print_func == print_stat
1182 && (fmt_char == 'd' || fmt_char == 'r'))
1184 b++;
1186 else
1188 fmt_char = mod_char;
1189 mod_char = 0;
1191 FALLTHROUGH;
1192 default:
1193 fail |= print_func (dest, len, mod_char, fmt_char,
1194 fd, filename, data);
1195 break;
1197 break;
1200 case '\\':
1201 if ( ! interpret_backslash_escapes)
1203 putchar ('\\');
1204 break;
1206 ++b;
1207 if (isodigit (*b))
1209 int esc_value = octtobin (*b);
1210 int esc_length = 1; /* number of octal digits */
1211 for (++b; esc_length < 3 && isodigit (*b);
1212 ++esc_length, ++b)
1214 esc_value = esc_value * 8 + octtobin (*b);
1216 putchar (esc_value);
1217 --b;
1219 else if (*b == 'x' && c_isxdigit (to_uchar (b[1])))
1221 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
1222 /* A hexadecimal \xhh escape sequence must have
1223 1 or 2 hex. digits. */
1224 ++b;
1225 if (c_isxdigit (to_uchar (b[1])))
1227 ++b;
1228 esc_value = esc_value * 16 + hextobin (*b);
1230 putchar (esc_value);
1232 else if (*b == '\0')
1234 error (0, 0, _("warning: backslash at end of format"));
1235 putchar ('\\');
1236 /* Arrange to exit the loop. */
1237 --b;
1239 else
1241 print_esc_char (*b);
1243 break;
1245 default:
1246 putchar (*b);
1247 break;
1250 free (dest);
1252 fputs (trailing_delim, stdout);
1254 return fail;
1257 /* Stat the file system and print what we find. */
1258 NODISCARD
1259 static bool
1260 do_statfs (char const *filename, char const *format)
1262 STRUCT_STATVFS statfsbuf;
1264 if (STREQ (filename, "-"))
1266 error (0, 0, _("using %s to denote standard input does not work"
1267 " in file system mode"), quoteaf (filename));
1268 return false;
1271 if (STATFS (filename, &statfsbuf) != 0)
1273 error (0, errno, _("cannot read file system information for %s"),
1274 quoteaf (filename));
1275 return false;
1278 bool fail = print_it (format, -1, filename, print_statfs, &statfsbuf);
1279 return ! fail;
1282 struct print_args {
1283 struct stat *st;
1284 struct timespec btime;
1287 /* Ask statx to avoid syncing? */
1288 static bool dont_sync;
1290 /* Ask statx to force sync? */
1291 static bool force_sync;
1293 #if USE_STATX
1294 static unsigned int
1295 fmt_to_mask (char fmt)
1297 switch (fmt)
1299 case 'N':
1300 return STATX_MODE;
1301 case 'd':
1302 case 'D':
1303 return STATX_MODE;
1304 case 'i':
1305 return STATX_INO;
1306 case 'a':
1307 case 'A':
1308 return STATX_MODE;
1309 case 'f':
1310 return STATX_MODE|STATX_TYPE;
1311 case 'F':
1312 return STATX_TYPE;
1313 case 'h':
1314 return STATX_NLINK;
1315 case 'u':
1316 case 'U':
1317 return STATX_UID;
1318 case 'g':
1319 case 'G':
1320 return STATX_GID;
1321 case 'm':
1322 return STATX_MODE|STATX_INO;
1323 case 's':
1324 return STATX_SIZE;
1325 case 't':
1326 case 'T':
1327 return STATX_MODE;
1328 case 'b':
1329 return STATX_BLOCKS;
1330 case 'w':
1331 case 'W':
1332 return STATX_BTIME;
1333 case 'x':
1334 case 'X':
1335 return STATX_ATIME;
1336 case 'y':
1337 case 'Y':
1338 return STATX_MTIME;
1339 case 'z':
1340 case 'Z':
1341 return STATX_CTIME;
1343 return 0;
1346 ATTRIBUTE_PURE
1347 static unsigned int
1348 format_to_mask (char const *format)
1350 unsigned int mask = 0;
1351 char const *b;
1353 for (b = format; *b; b++)
1355 if (*b != '%')
1356 continue;
1358 b += format_code_offset (b);
1359 if (*b == '\0')
1360 break;
1361 mask |= fmt_to_mask (*b);
1363 return mask;
1366 /* statx the file and print what we find */
1367 NODISCARD
1368 static bool
1369 do_stat (char const *filename, char const *format, char const *format2)
1371 int fd = STREQ (filename, "-") ? 0 : AT_FDCWD;
1372 int flags = 0;
1373 struct stat st;
1374 struct statx stx = {0};
1375 char const *pathname = filename;
1376 struct print_args pa;
1377 pa.st = &st;
1378 pa.btime = (struct timespec) {.tv_sec = -1, .tv_nsec = -1};
1380 if (AT_FDCWD != fd)
1382 pathname = "";
1383 flags = AT_EMPTY_PATH;
1385 else if (!follow_links)
1387 flags = AT_SYMLINK_NOFOLLOW;
1390 if (dont_sync)
1391 flags |= AT_STATX_DONT_SYNC;
1392 else if (force_sync)
1393 flags |= AT_STATX_FORCE_SYNC;
1395 if (! force_sync)
1396 flags |= AT_NO_AUTOMOUNT;
1398 fd = statx (fd, pathname, flags, format_to_mask (format), &stx);
1399 if (fd < 0)
1401 if (flags & AT_EMPTY_PATH)
1402 error (0, errno, _("cannot stat standard input"));
1403 else
1404 error (0, errno, _("cannot statx %s"), quoteaf (filename));
1405 return false;
1408 if (S_ISBLK (stx.stx_mode) || S_ISCHR (stx.stx_mode))
1409 format = format2;
1411 statx_to_stat (&stx, &st);
1412 if (stx.stx_mask & STATX_BTIME)
1413 pa.btime = statx_timestamp_to_timespec (stx.stx_btime);
1415 bool fail = print_it (format, fd, filename, print_stat, &pa);
1416 return ! fail;
1419 #else /* USE_STATX */
1421 static struct timespec
1422 get_birthtime (int fd, char const *filename, struct stat const *st)
1424 struct timespec ts = get_stat_birthtime (st);
1426 # if HAVE_GETATTRAT
1427 if (ts.tv_nsec < 0)
1429 nvlist_t *response;
1430 if ((fd < 0
1431 ? getattrat (AT_FDCWD, XATTR_VIEW_READWRITE, filename, &response)
1432 : fgetattr (fd, XATTR_VIEW_READWRITE, &response))
1433 == 0)
1435 uint64_t *val;
1436 uint_t n;
1437 if (nvlist_lookup_uint64_array (response, A_CRTIME, &val, &n) == 0
1438 && 2 <= n
1439 && val[0] <= TYPE_MAXIMUM (time_t)
1440 && val[1] < 1000000000 * 2 /* for leap seconds */)
1442 ts.tv_sec = val[0];
1443 ts.tv_nsec = val[1];
1445 nvlist_free (response);
1448 # endif
1450 return ts;
1454 /* stat the file and print what we find */
1455 NODISCARD
1456 static bool
1457 do_stat (char const *filename, char const *format,
1458 char const *format2)
1460 int fd = STREQ (filename, "-") ? 0 : -1;
1461 struct stat statbuf;
1462 struct print_args pa;
1463 pa.st = &statbuf;
1464 pa.btime = (struct timespec) {.tv_sec = -1, .tv_nsec = -1};
1466 if (0 <= fd)
1468 if (fstat (fd, &statbuf) != 0)
1470 error (0, errno, _("cannot stat standard input"));
1471 return false;
1474 /* We can't use the shorter
1475 (follow_links?stat:lstat) (filename, &statbug)
1476 since stat might be a function-like macro. */
1477 else if ((follow_links
1478 ? stat (filename, &statbuf)
1479 : lstat (filename, &statbuf)) != 0)
1481 error (0, errno, _("cannot stat %s"), quoteaf (filename));
1482 return false;
1485 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
1486 format = format2;
1488 bool fail = print_it (format, fd, filename, print_stat, &pa);
1489 return ! fail;
1491 #endif /* USE_STATX */
1493 /* POSIX requires 'ls' to print file sizes without a sign, even
1494 when negative. Be consistent with that. */
1496 static uintmax_t
1497 unsigned_file_size (off_t size)
1499 return size + (size < 0) * ((uintmax_t) OFF_T_MAX - OFF_T_MIN + 1);
1502 /* Print stat info. Return zero upon success, nonzero upon failure. */
1503 static bool
1504 print_stat (char *pformat, size_t prefix_len, char mod, char m,
1505 int fd, char const *filename, void const *data)
1507 struct print_args *parg = (struct print_args *) data;
1508 struct stat *statbuf = parg->st;
1509 struct timespec btime = parg->btime;
1510 struct passwd *pw_ent;
1511 struct group *gw_ent;
1512 bool fail = false;
1514 switch (m)
1516 case 'n':
1517 out_string (pformat, prefix_len, filename);
1518 break;
1519 case 'N':
1520 out_string (pformat, prefix_len, quoteN (filename));
1521 if (S_ISLNK (statbuf->st_mode))
1523 char *linkname = areadlink_with_size (filename, statbuf->st_size);
1524 if (linkname == nullptr)
1526 error (0, errno, _("cannot read symbolic link %s"),
1527 quoteaf (filename));
1528 return true;
1530 printf (" -> ");
1531 out_string (pformat, prefix_len, quoteN (linkname));
1532 free (linkname);
1534 break;
1535 case 'd':
1536 if (mod == 'H')
1537 out_uint (pformat, prefix_len, major (statbuf->st_dev));
1538 else if (mod == 'L')
1539 out_uint (pformat, prefix_len, minor (statbuf->st_dev));
1540 else
1541 out_uint (pformat, prefix_len, statbuf->st_dev);
1542 break;
1543 case 'D':
1544 out_uint_x (pformat, prefix_len, statbuf->st_dev);
1545 break;
1546 case 'i':
1547 out_uint (pformat, prefix_len, statbuf->st_ino);
1548 break;
1549 case 'a':
1550 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
1551 break;
1552 case 'A':
1553 out_string (pformat, prefix_len, human_access (statbuf));
1554 break;
1555 case 'f':
1556 out_uint_x (pformat, prefix_len, statbuf->st_mode);
1557 break;
1558 case 'F':
1559 out_string (pformat, prefix_len, file_type (statbuf));
1560 break;
1561 case 'h':
1562 out_uint (pformat, prefix_len, statbuf->st_nlink);
1563 break;
1564 case 'u':
1565 out_uint (pformat, prefix_len, statbuf->st_uid);
1566 break;
1567 case 'U':
1568 pw_ent = getpwuid (statbuf->st_uid);
1569 out_string (pformat, prefix_len,
1570 pw_ent ? pw_ent->pw_name : "UNKNOWN");
1571 break;
1572 case 'g':
1573 out_uint (pformat, prefix_len, statbuf->st_gid);
1574 break;
1575 case 'G':
1576 gw_ent = getgrgid (statbuf->st_gid);
1577 out_string (pformat, prefix_len,
1578 gw_ent ? gw_ent->gr_name : "UNKNOWN");
1579 break;
1580 case 'm':
1581 fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
1582 break;
1583 case 's':
1584 out_uint (pformat, prefix_len, unsigned_file_size (statbuf->st_size));
1585 break;
1586 case 'r':
1587 if (mod == 'H')
1588 out_uint (pformat, prefix_len, major (statbuf->st_rdev));
1589 else if (mod == 'L')
1590 out_uint (pformat, prefix_len, minor (statbuf->st_rdev));
1591 else
1592 out_uint (pformat, prefix_len, statbuf->st_rdev);
1593 break;
1594 case 'R':
1595 out_uint_x (pformat, prefix_len, statbuf->st_rdev);
1596 break;
1597 case 't':
1598 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
1599 break;
1600 case 'T':
1601 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
1602 break;
1603 case 'B':
1604 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
1605 break;
1606 case 'b':
1607 out_uint (pformat, prefix_len, STP_NBLOCKS (statbuf));
1608 break;
1609 case 'o':
1610 out_uint (pformat, prefix_len, STP_BLKSIZE (statbuf));
1611 break;
1612 case 'w':
1614 #if ! USE_STATX
1615 btime = get_birthtime (fd, filename, statbuf);
1616 #endif
1617 if (btime.tv_nsec < 0)
1618 out_string (pformat, prefix_len, "-");
1619 else
1620 out_string (pformat, prefix_len, human_time (btime));
1622 break;
1623 case 'W':
1625 #if ! USE_STATX
1626 btime = get_birthtime (fd, filename, statbuf);
1627 #endif
1628 out_epoch_sec (pformat, prefix_len, neg_to_zero (btime));
1630 break;
1631 case 'x':
1632 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
1633 break;
1634 case 'X':
1635 out_epoch_sec (pformat, prefix_len, get_stat_atime (statbuf));
1636 break;
1637 case 'y':
1638 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
1639 break;
1640 case 'Y':
1641 out_epoch_sec (pformat, prefix_len, get_stat_mtime (statbuf));
1642 break;
1643 case 'z':
1644 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
1645 break;
1646 case 'Z':
1647 out_epoch_sec (pformat, prefix_len, get_stat_ctime (statbuf));
1648 break;
1649 case 'C':
1650 fail |= out_file_context (pformat, prefix_len, filename);
1651 break;
1652 default:
1653 fputc ('?', stdout);
1654 break;
1656 return fail;
1659 /* Return an allocated format string in static storage that
1660 corresponds to whether FS and TERSE options were declared. */
1661 static char *
1662 default_format (bool fs, bool terse, bool device)
1664 char *format;
1665 if (fs)
1667 if (terse)
1668 format = xstrdup (fmt_terse_fs);
1669 else
1671 /* TRANSLATORS: This string uses format specifiers from
1672 'stat --help' with --file-system, and NOT from printf. */
1673 format = xstrdup (_(" File: \"%n\"\n"
1674 " ID: %-8i Namelen: %-7l Type: %T\n"
1675 "Block size: %-10s Fundamental block size: %S\n"
1676 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
1677 "Inodes: Total: %-10c Free: %d\n"));
1680 else /* ! fs */
1682 if (terse)
1684 if (0 < is_selinux_enabled ())
1685 format = xstrdup (fmt_terse_selinux);
1686 else
1687 format = xstrdup (fmt_terse_regular);
1689 else
1691 char *temp;
1692 /* TRANSLATORS: This string uses format specifiers from
1693 'stat --help' without --file-system, and NOT from printf. */
1694 format = xstrdup (_("\
1695 File: %N\n\
1696 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1697 "));
1699 temp = format;
1700 if (device)
1702 /* TRANSLATORS: This string uses format specifiers from
1703 'stat --help' without --file-system, and NOT from printf. */
1704 format = xasprintf ("%s%s", format, _("\
1705 " "Device: %Hd,%Ld\tInode: %-10i Links: %-5h Device type: %Hr,%Lr\n\
1706 "));
1708 else
1710 /* TRANSLATORS: This string uses format specifiers from
1711 'stat --help' without --file-system, and NOT from printf. */
1712 format = xasprintf ("%s%s", format, _("\
1713 " "Device: %Hd,%Ld\tInode: %-10i Links: %h\n\
1714 "));
1716 free (temp);
1718 temp = format;
1719 /* TRANSLATORS: This string uses format specifiers from
1720 'stat --help' without --file-system, and NOT from printf. */
1721 format = xasprintf ("%s%s", format, _("\
1722 " "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1723 "));
1724 free (temp);
1726 if (0 < is_selinux_enabled ())
1728 temp = format;
1729 /* TRANSLATORS: This string uses format specifiers from
1730 'stat --help' without --file-system, and NOT from printf. */
1731 format = xasprintf ("%s%s", format, _("Context: %C\n"));
1732 free (temp);
1735 temp = format;
1736 /* TRANSLATORS: This string uses format specifiers from
1737 'stat --help' without --file-system, and NOT from printf. */
1738 format = xasprintf ("%s%s", format,
1739 _("Access: %x\n"
1740 "Modify: %y\n"
1741 "Change: %z\n"
1742 " Birth: %w\n"));
1743 free (temp);
1746 return format;
1749 void
1750 usage (int status)
1752 if (status != EXIT_SUCCESS)
1753 emit_try_help ();
1754 else
1756 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
1757 fputs (_("\
1758 Display file or file system status.\n\
1759 "), stdout);
1761 emit_mandatory_arg_note ();
1763 fputs (_("\
1764 -L, --dereference follow links\n\
1765 -f, --file-system display file system status instead of file status\n\
1766 "), stdout);
1767 fputs (_("\
1768 --cached=MODE specify how to use cached attributes;\n\
1769 useful on remote file systems. See MODE below\n\
1770 "), stdout);
1771 fputs (_("\
1772 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1773 output a newline after each use of FORMAT\n\
1774 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1775 and do not output a mandatory trailing newline;\n\
1776 if you want a newline, include \\n in FORMAT\n\
1777 -t, --terse print the information in terse form\n\
1778 "), stdout);
1779 fputs (HELP_OPTION_DESCRIPTION, stdout);
1780 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1782 fputs (_("\n\
1783 The MODE argument of --cached can be: always, never, or default.\n\
1784 'always' will use cached attributes if available, while\n\
1785 'never' will try to synchronize with the latest attributes, and\n\
1786 'default' will leave it up to the underlying file system.\n\
1787 "), stdout);
1789 fputs (_("\n\
1790 The valid format sequences for files (without --file-system):\n\
1792 %a permission bits in octal (note '#' and '0' printf flags)\n\
1793 %A permission bits and file type in human readable form\n\
1794 %b number of blocks allocated (see %B)\n\
1795 %B the size in bytes of each block reported by %b\n\
1796 %C SELinux security context string\n\
1797 "), stdout);
1798 fputs (_("\
1799 %d device number in decimal (st_dev)\n\
1800 %D device number in hex (st_dev)\n\
1801 %Hd major device number in decimal\n\
1802 %Ld minor device number in decimal\n\
1803 %f raw mode in hex\n\
1804 %F file type\n\
1805 %g group ID of owner\n\
1806 %G group name of owner\n\
1807 "), stdout);
1808 fputs (_("\
1809 %h number of hard links\n\
1810 %i inode number\n\
1811 %m mount point\n\
1812 %n file name\n\
1813 %N quoted file name with dereference if symbolic link\n\
1814 %o optimal I/O transfer size hint\n\
1815 %s total size, in bytes\n\
1816 %r device type in decimal (st_rdev)\n\
1817 %R device type in hex (st_rdev)\n\
1818 %Hr major device type in decimal, for character/block device special files\n\
1819 %Lr minor device type in decimal, for character/block device special files\n\
1820 %t major device type in hex, for character/block device special files\n\
1821 %T minor device type in hex, for character/block device special files\n\
1822 "), stdout);
1823 fputs (_("\
1824 %u user ID of owner\n\
1825 %U user name of owner\n\
1826 %w time of file birth, human-readable; - if unknown\n\
1827 %W time of file birth, seconds since Epoch; 0 if unknown\n\
1828 %x time of last access, human-readable\n\
1829 %X time of last access, seconds since Epoch\n\
1830 %y time of last data modification, human-readable\n\
1831 %Y time of last data modification, seconds since Epoch\n\
1832 %z time of last status change, human-readable\n\
1833 %Z time of last status change, seconds since Epoch\n\
1835 "), stdout);
1837 fputs (_("\
1838 Valid format sequences for file systems:\n\
1840 %a free blocks available to non-superuser\n\
1841 %b total data blocks in file system\n\
1842 %c total file nodes in file system\n\
1843 %d free file nodes in file system\n\
1844 %f free blocks in file system\n\
1845 "), stdout);
1846 fputs (_("\
1847 %i file system ID in hex\n\
1848 %l maximum length of filenames\n\
1849 %n file name\n\
1850 %s block size (for faster transfers)\n\
1851 %S fundamental block size (for block counts)\n\
1852 %t file system type in hex\n\
1853 %T file system type in human readable form\n\
1854 "), stdout);
1856 printf (_("\n\
1857 --terse is equivalent to the following FORMAT:\n\
1860 #if HAVE_SELINUX_SELINUX_H
1861 fmt_terse_selinux
1862 #else
1863 fmt_terse_regular
1864 #endif
1867 printf (_("\
1868 --terse --file-system is equivalent to the following FORMAT:\n\
1870 "), fmt_terse_fs);
1872 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1873 emit_ancillary_info (PROGRAM_NAME);
1875 exit (status);
1879 main (int argc, char *argv[])
1881 int c;
1882 bool fs = false;
1883 bool terse = false;
1884 char *format = nullptr;
1885 char *format2;
1886 bool ok = true;
1888 initialize_main (&argc, &argv);
1889 set_program_name (argv[0]);
1890 setlocale (LC_ALL, "");
1891 bindtextdomain (PACKAGE, LOCALEDIR);
1892 textdomain (PACKAGE);
1894 struct lconv const *locale = localeconv ();
1895 decimal_point = (locale->decimal_point[0] ? locale->decimal_point : ".");
1896 decimal_point_len = strlen (decimal_point);
1898 atexit (close_stdout);
1900 while ((c = getopt_long (argc, argv, "c:fLt", long_options, nullptr)) != -1)
1902 switch (c)
1904 case PRINTF_OPTION:
1905 format = optarg;
1906 interpret_backslash_escapes = true;
1907 trailing_delim = "";
1908 break;
1910 case 'c':
1911 format = optarg;
1912 interpret_backslash_escapes = false;
1913 trailing_delim = "\n";
1914 break;
1916 case 'L':
1917 follow_links = true;
1918 break;
1920 case 'f':
1921 fs = true;
1922 break;
1924 case 't':
1925 terse = true;
1926 break;
1928 case 0:
1929 switch (XARGMATCH ("--cached", optarg, cached_args, cached_modes))
1931 case cached_never:
1932 force_sync = true;
1933 dont_sync = false;
1934 break;
1935 case cached_always:
1936 force_sync = false;
1937 dont_sync = true;
1938 break;
1939 case cached_default:
1940 force_sync = false;
1941 dont_sync = false;
1943 break;
1945 case_GETOPT_HELP_CHAR;
1947 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1949 default:
1950 usage (EXIT_FAILURE);
1954 if (argc == optind)
1956 error (0, 0, _("missing operand"));
1957 usage (EXIT_FAILURE);
1960 if (format)
1962 if (strstr (format, "%N"))
1963 getenv_quoting_style ();
1964 format2 = format;
1966 else
1968 format = default_format (fs, terse, /* device= */ false);
1969 format2 = default_format (fs, terse, /* device= */ true);
1972 for (int i = optind; i < argc; i++)
1973 ok &= (fs
1974 ? do_statfs (argv[i], format)
1975 : do_stat (argv[i], format, format2));
1977 main_exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);