doc: pwd: improve the -P help description
[coreutils.git] / src / stat.c
blob91795471fd777138b0f0e789d47bdf6009cd905f
1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2024 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>
56 #include <getopt.h>
58 #include "system.h"
60 #include "areadlink.h"
61 #include "argmatch.h"
62 #include "c-ctype.h"
63 #include "file-type.h"
64 #include "filemode.h"
65 #include "fs.h"
66 #include "mountlist.h"
67 #include "octhexdigits.h"
68 #include "quote.h"
69 #include "stat-size.h"
70 #include "stat-time.h"
71 #include "strftime.h"
72 #include "find-mount-point.h"
73 #include "xvasprintf.h"
74 #include "statx.h"
76 #if HAVE_STATX && defined STATX_INO
77 # define USE_STATX 1
78 #else
79 # define USE_STATX 0
80 #endif
82 #if USE_STATVFS
83 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
84 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
85 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
86 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
87 # endif
88 # if ! STAT_STATVFS && STAT_STATVFS64
89 # define STRUCT_STATVFS struct statvfs64
90 # define STATFS statvfs64
91 # else
92 # define STRUCT_STATVFS struct statvfs
93 # define STATFS statvfs
94 # endif
95 # define STATFS_FRSIZE(S) ((S)->f_frsize)
96 #else
97 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
98 # if HAVE_STRUCT_STATFS_F_NAMELEN
99 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
100 # elif HAVE_STRUCT_STATFS_F_NAMEMAX
101 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
102 # endif
103 # define STATFS statfs
104 # if HAVE_OS_H /* BeOS */
105 /* BeOS has a statvfs function, but it does not return sensible values
106 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
107 f_fstypename. Use 'struct fs_info' instead. */
108 NODISCARD
109 static int
110 statfs (char const *filename, struct fs_info *buf)
112 dev_t device = dev_for_path (filename);
113 if (device < 0)
115 errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
116 : device == B_BAD_VALUE ? EINVAL
117 : device == B_NAME_TOO_LONG ? ENAMETOOLONG
118 : device == B_NO_MEMORY ? ENOMEM
119 : device == B_FILE_ERROR ? EIO
120 : 0);
121 return -1;
123 /* If successful, buf->dev will be == device. */
124 return fs_stat_dev (device, buf);
126 # define f_fsid dev
127 # define f_blocks total_blocks
128 # define f_bfree free_blocks
129 # define f_bavail free_blocks
130 # define f_bsize io_size
131 # define f_files total_nodes
132 # define f_ffree free_nodes
133 # define STRUCT_STATVFS struct fs_info
134 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
135 # define STATFS_FRSIZE(S) ((S)->block_size)
136 # else
137 # define STRUCT_STATVFS struct statfs
138 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
139 # if HAVE_STRUCT_STATFS_F_FRSIZE
140 # define STATFS_FRSIZE(S) ((S)->f_frsize)
141 # else
142 # define STATFS_FRSIZE(S) 0
143 # endif
144 # endif
145 #endif
147 #ifdef SB_F_NAMEMAX
148 # define OUT_NAMEMAX out_uint
149 #else
150 /* Depending on whether statvfs or statfs is used,
151 neither f_namemax or f_namelen may be available. */
152 # define SB_F_NAMEMAX(S) "?"
153 # define OUT_NAMEMAX out_string
154 #endif
156 #if HAVE_STRUCT_STATVFS_F_BASETYPE
157 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
158 #else
159 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
160 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
161 # elif HAVE_OS_H /* BeOS */
162 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
163 # endif
164 #endif
166 #if HAVE_GETATTRAT
167 # include <attr.h>
168 # include <sys/nvpair.h>
169 #endif
171 static char const digits[] = "0123456789";
173 /* Flags that are portable for use in printf, for at least one
174 conversion specifier; make_format removes non-portable flags as
175 needed for particular specifiers. The glibc 2.2 extension "I" is
176 listed here; it is removed by make_format because it has undefined
177 behavior elsewhere and because it is incompatible with
178 out_epoch_sec. */
179 static char const printf_flags[] = "'-+ #0I";
181 /* Formats for the --terse option. */
182 static char const fmt_terse_fs[] = "%n %i %l %t %s %S %b %f %a %c %d\n";
183 static char const fmt_terse_regular[] = "%n %s %b %f %u %g %D %i %h %t %T"
184 " %X %Y %Z %W %o\n";
185 static char const fmt_terse_selinux[] = "%n %s %b %f %u %g %D %i %h %t %T"
186 " %X %Y %Z %W %o %C\n";
188 #define PROGRAM_NAME "stat"
190 #define AUTHORS proper_name ("Michael Meskes")
192 enum
194 PRINTF_OPTION = CHAR_MAX + 1
197 enum cached_mode
199 cached_default,
200 cached_never,
201 cached_always
204 static char const *const cached_args[] =
206 "default", "never", "always", nullptr
209 static enum cached_mode const cached_modes[] =
211 cached_default, cached_never, cached_always
214 static struct option const long_options[] =
216 {"dereference", no_argument, nullptr, 'L'},
217 {"file-system", no_argument, nullptr, 'f'},
218 {"format", required_argument, nullptr, 'c'},
219 {"printf", required_argument, nullptr, PRINTF_OPTION},
220 {"terse", no_argument, nullptr, 't'},
221 {"cached", required_argument, nullptr, 0},
222 {GETOPT_HELP_OPTION_DECL},
223 {GETOPT_VERSION_OPTION_DECL},
224 {nullptr, 0, nullptr, 0}
227 /* Whether to follow symbolic links; True for --dereference (-L). */
228 static bool follow_links;
230 /* Whether to interpret backslash-escape sequences.
231 True for --printf=FMT, not for --format=FMT (-c). */
232 static bool interpret_backslash_escapes;
234 /* The trailing delimiter string:
235 "" for --printf=FMT, "\n" for --format=FMT (-c). */
236 static char const *trailing_delim = "";
238 /* The representation of the decimal point in the current locale. */
239 static char const *decimal_point;
240 static size_t decimal_point_len;
242 static bool
243 print_stat (char *pformat, size_t prefix_len, char mod, char m,
244 int fd, char const *filename, void const *data);
246 /* Return the type of the specified file system.
247 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
248 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
249 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
250 Still others have neither and have to get by with f_type (GNU/Linux).
251 But f_type may only exist in statfs (Cygwin). */
252 NODISCARD
253 static char const *
254 human_fstype (STRUCT_STATVFS const *statfsbuf)
256 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
257 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
258 #else
259 switch (statfsbuf->f_type)
261 # if defined __linux__ || defined __ANDROID__
263 /* Compare with what's in libc:
264 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
265 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
266 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
267 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
268 | sort > sym_libc
269 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
270 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
271 | sort > sym_stat
272 diff -u sym_stat sym_libc
275 /* Also compare with the list in "man 2 statfs" using the
276 fs-magic-compare make target. */
278 /* IMPORTANT NOTE: Each of the following 'case S_MAGIC_...:'
279 statements must be followed by a hexadecimal constant in
280 a comment. The S_MAGIC_... name and constant are automatically
281 combined to produce the #define directives in fs.h. */
283 case S_MAGIC_AAFS: /* 0x5A3C69F0 local */
284 return "aafs";
285 case S_MAGIC_ACFS: /* 0x61636673 remote */
286 return "acfs";
287 case S_MAGIC_ADFS: /* 0xADF5 local */
288 return "adfs";
289 case S_MAGIC_AFFS: /* 0xADFF local */
290 return "affs";
291 case S_MAGIC_AFS: /* 0x5346414F remote */
292 return "afs";
293 case S_MAGIC_ANON_INODE_FS: /* 0x09041934 local */
294 return "anon-inode FS";
295 case S_MAGIC_AUFS: /* 0x61756673 remote */
296 /* FIXME: change syntax or add an optional attribute like "inotify:no".
297 The above is labeled as "remote" so that tail always uses polling,
298 but this isn't really a remote file system type. */
299 return "aufs";
300 case S_MAGIC_AUTOFS: /* 0x0187 local */
301 return "autofs";
302 case S_MAGIC_BALLOON_KVM: /* 0x13661366 local */
303 return "balloon-kvm-fs";
304 case S_MAGIC_BEFS: /* 0x42465331 local */
305 return "befs";
306 case S_MAGIC_BDEVFS: /* 0x62646576 local */
307 return "bdevfs";
308 case S_MAGIC_BFS: /* 0x1BADFACE local */
309 return "bfs";
310 case S_MAGIC_BINDERFS: /* 0x6C6F6F70 local */
311 return "binderfs";
312 case S_MAGIC_BPF_FS: /* 0xCAFE4A11 local */
313 return "bpf_fs";
314 case S_MAGIC_BINFMTFS: /* 0x42494E4D local */
315 return "binfmt_misc";
316 case S_MAGIC_BTRFS: /* 0x9123683E local */
317 return "btrfs";
318 case S_MAGIC_BTRFS_TEST: /* 0x73727279 local */
319 return "btrfs_test";
320 case S_MAGIC_CEPH: /* 0x00C36400 remote */
321 return "ceph";
322 case S_MAGIC_CGROUP: /* 0x0027E0EB local */
323 return "cgroupfs";
324 case S_MAGIC_CGROUP2: /* 0x63677270 local */
325 return "cgroup2fs";
326 case S_MAGIC_CIFS: /* 0xFF534D42 remote */
327 return "cifs";
328 case S_MAGIC_CODA: /* 0x73757245 remote */
329 return "coda";
330 case S_MAGIC_COH: /* 0x012FF7B7 local */
331 return "coh";
332 case S_MAGIC_CONFIGFS: /* 0x62656570 local */
333 return "configfs";
334 case S_MAGIC_CRAMFS: /* 0x28CD3D45 local */
335 return "cramfs";
336 case S_MAGIC_CRAMFS_WEND: /* 0x453DCD28 local */
337 return "cramfs-wend";
338 case S_MAGIC_DAXFS: /* 0x64646178 local */
339 return "daxfs";
340 case S_MAGIC_DEBUGFS: /* 0x64626720 local */
341 return "debugfs";
342 case S_MAGIC_DEVFS: /* 0x1373 local */
343 return "devfs";
344 case S_MAGIC_DEVMEM: /* 0x454D444D local */
345 return "devmem";
346 case S_MAGIC_DEVPTS: /* 0x1CD1 local */
347 return "devpts";
348 case S_MAGIC_DMA_BUF: /* 0x444D4142 local */
349 return "dma-buf-fs";
350 case S_MAGIC_ECRYPTFS: /* 0xF15F local */
351 return "ecryptfs";
352 case S_MAGIC_EFIVARFS: /* 0xDE5E81E4 local */
353 return "efivarfs";
354 case S_MAGIC_EFS: /* 0x00414A53 local */
355 return "efs";
356 case S_MAGIC_EROFS_V1: /* 0xE0F5E1E2 local */
357 return "erofs";
358 case S_MAGIC_EXFAT: /* 0x2011BAB0 local */
359 return "exfat";
360 case S_MAGIC_EXFS: /* 0x45584653 local */
361 return "exfs";
362 case S_MAGIC_EXOFS: /* 0x5DF5 local */
363 return "exofs";
364 case S_MAGIC_EXT: /* 0x137D local */
365 return "ext";
366 case S_MAGIC_EXT2: /* 0xEF53 local */
367 return "ext2/ext3";
368 case S_MAGIC_EXT2_OLD: /* 0xEF51 local */
369 return "ext2";
370 case S_MAGIC_F2FS: /* 0xF2F52010 local */
371 return "f2fs";
372 case S_MAGIC_FAT: /* 0x4006 local */
373 return "fat";
374 case S_MAGIC_FHGFS: /* 0x19830326 remote */
375 return "fhgfs";
376 case S_MAGIC_FUSEBLK: /* 0x65735546 remote */
377 return "fuseblk";
378 case S_MAGIC_FUSECTL: /* 0x65735543 remote */
379 return "fusectl";
380 case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA local */
381 return "futexfs";
382 case S_MAGIC_GFS: /* 0x01161970 remote */
383 return "gfs/gfs2";
384 case S_MAGIC_GPFS: /* 0x47504653 remote */
385 return "gpfs";
386 case S_MAGIC_HFS: /* 0x4244 local */
387 return "hfs";
388 case S_MAGIC_HFS_PLUS: /* 0x482B local */
389 return "hfs+";
390 case S_MAGIC_HFS_X: /* 0x4858 local */
391 return "hfsx";
392 case S_MAGIC_HOSTFS: /* 0x00C0FFEE local */
393 return "hostfs";
394 case S_MAGIC_HPFS: /* 0xF995E849 local */
395 return "hpfs";
396 case S_MAGIC_HUGETLBFS: /* 0x958458F6 local */
397 return "hugetlbfs";
398 case S_MAGIC_MTD_INODE_FS: /* 0x11307854 local */
399 return "inodefs";
400 case S_MAGIC_IBRIX: /* 0x013111A8 remote */
401 return "ibrix";
402 case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA local */
403 return "inotifyfs";
404 case S_MAGIC_ISOFS: /* 0x9660 local */
405 return "isofs";
406 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 local */
407 return "isofs";
408 case S_MAGIC_ISOFS_WIN: /* 0x4000 local */
409 return "isofs";
410 case S_MAGIC_JFFS: /* 0x07C0 local */
411 return "jffs";
412 case S_MAGIC_JFFS2: /* 0x72B6 local */
413 return "jffs2";
414 case S_MAGIC_JFS: /* 0x3153464A local */
415 return "jfs";
416 case S_MAGIC_KAFS: /* 0x6B414653 remote */
417 return "k-afs";
418 case S_MAGIC_LOGFS: /* 0xC97E8168 local */
419 return "logfs";
420 case S_MAGIC_LUSTRE: /* 0x0BD00BD0 remote */
421 return "lustre";
422 case S_MAGIC_M1FS: /* 0x5346314D local */
423 return "m1fs";
424 case S_MAGIC_MINIX: /* 0x137F local */
425 return "minix";
426 case S_MAGIC_MINIX_30: /* 0x138F local */
427 return "minix (30 char.)";
428 case S_MAGIC_MINIX_V2: /* 0x2468 local */
429 return "minix v2";
430 case S_MAGIC_MINIX_V2_30: /* 0x2478 local */
431 return "minix v2 (30 char.)";
432 case S_MAGIC_MINIX_V3: /* 0x4D5A local */
433 return "minix3";
434 case S_MAGIC_MQUEUE: /* 0x19800202 local */
435 return "mqueue";
436 case S_MAGIC_MSDOS: /* 0x4D44 local */
437 return "msdos";
438 case S_MAGIC_NCP: /* 0x564C remote */
439 return "novell";
440 case S_MAGIC_NFS: /* 0x6969 remote */
441 return "nfs";
442 case S_MAGIC_NFSD: /* 0x6E667364 remote */
443 return "nfsd";
444 case S_MAGIC_NILFS: /* 0x3434 local */
445 return "nilfs";
446 case S_MAGIC_NSFS: /* 0x6E736673 local */
447 return "nsfs";
448 case S_MAGIC_NTFS: /* 0x5346544E local */
449 return "ntfs";
450 case S_MAGIC_OPENPROM: /* 0x9FA1 local */
451 return "openprom";
452 case S_MAGIC_OCFS2: /* 0x7461636F remote */
453 return "ocfs2";
454 case S_MAGIC_OVERLAYFS: /* 0x794C7630 remote */
455 /* This may overlay remote file systems.
456 Also there have been issues reported with inotify and overlayfs,
457 so mark as "remote" so that polling is used. */
458 return "overlayfs";
459 case S_MAGIC_PANFS: /* 0xAAD7AAEA remote */
460 return "panfs";
461 case S_MAGIC_PIPEFS: /* 0x50495045 remote */
462 /* FIXME: change syntax or add an optional attribute like "inotify:no".
463 pipefs and prlfs are labeled as "remote" so that tail always polls,
464 but these aren't really remote file system types. */
465 return "pipefs";
466 case S_MAGIC_PPC_CMM: /* 0xC7571590 local */
467 return "ppc-cmm-fs";
468 case S_MAGIC_PRL_FS: /* 0x7C7C6673 remote */
469 return "prl_fs";
470 case S_MAGIC_PROC: /* 0x9FA0 local */
471 return "proc";
472 case S_MAGIC_PSTOREFS: /* 0x6165676C local */
473 return "pstorefs";
474 case S_MAGIC_QNX4: /* 0x002F local */
475 return "qnx4";
476 case S_MAGIC_QNX6: /* 0x68191122 local */
477 return "qnx6";
478 case S_MAGIC_RAMFS: /* 0x858458F6 local */
479 return "ramfs";
480 case S_MAGIC_RDTGROUP: /* 0x07655821 local */
481 return "rdt";
482 case S_MAGIC_REISERFS: /* 0x52654973 local */
483 return "reiserfs";
484 case S_MAGIC_ROMFS: /* 0x7275 local */
485 return "romfs";
486 case S_MAGIC_RPC_PIPEFS: /* 0x67596969 local */
487 return "rpc_pipefs";
488 case S_MAGIC_SDCARDFS: /* 0x5DCA2DF5 local */
489 return "sdcardfs";
490 case S_MAGIC_SECRETMEM: /* 0x5345434D local */
491 return "secretmem";
492 case S_MAGIC_SECURITYFS: /* 0x73636673 local */
493 return "securityfs";
494 case S_MAGIC_SELINUX: /* 0xF97CFF8C local */
495 return "selinux";
496 case S_MAGIC_SMACK: /* 0x43415D53 local */
497 return "smackfs";
498 case S_MAGIC_SMB: /* 0x517B remote */
499 return "smb";
500 case S_MAGIC_SMB2: /* 0xFE534D42 remote */
501 return "smb2";
502 case S_MAGIC_SNFS: /* 0xBEEFDEAD remote */
503 return "snfs";
504 case S_MAGIC_SOCKFS: /* 0x534F434B local */
505 return "sockfs";
506 case S_MAGIC_SQUASHFS: /* 0x73717368 local */
507 return "squashfs";
508 case S_MAGIC_SYSFS: /* 0x62656572 local */
509 return "sysfs";
510 case S_MAGIC_SYSV2: /* 0x012FF7B6 local */
511 return "sysv2";
512 case S_MAGIC_SYSV4: /* 0x012FF7B5 local */
513 return "sysv4";
514 case S_MAGIC_TMPFS: /* 0x01021994 local */
515 return "tmpfs";
516 case S_MAGIC_TRACEFS: /* 0x74726163 local */
517 return "tracefs";
518 case S_MAGIC_UBIFS: /* 0x24051905 local */
519 return "ubifs";
520 case S_MAGIC_UDF: /* 0x15013346 local */
521 return "udf";
522 case S_MAGIC_UFS: /* 0x00011954 local */
523 return "ufs";
524 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 local */
525 return "ufs";
526 case S_MAGIC_USBDEVFS: /* 0x9FA2 local */
527 return "usbdevfs";
528 case S_MAGIC_V9FS: /* 0x01021997 local */
529 return "v9fs";
530 case S_MAGIC_VBOXSF: /* 0x786F4256 remote */
531 return "vboxsf";
532 case S_MAGIC_VMHGFS: /* 0xBACBACBC remote */
533 return "vmhgfs";
534 case S_MAGIC_VXFS: /* 0xA501FCF5 remote */
535 /* Veritas File System can run in single instance or clustered mode,
536 so mark as remote to cater for the latter case. */
537 return "vxfs";
538 case S_MAGIC_VZFS: /* 0x565A4653 local */
539 return "vzfs";
540 case S_MAGIC_WSLFS: /* 0x53464846 local */
541 return "wslfs";
542 case S_MAGIC_XENFS: /* 0xABBA1974 local */
543 return "xenfs";
544 case S_MAGIC_XENIX: /* 0x012FF7B4 local */
545 return "xenix";
546 case S_MAGIC_XFS: /* 0x58465342 local */
547 return "xfs";
548 case S_MAGIC_XIAFS: /* 0x012FD16D local */
549 return "xia";
550 case S_MAGIC_Z3FOLD: /* 0x0033 local */
551 return "z3fold";
552 case S_MAGIC_ZFS: /* 0x2FC12FC1 local */
553 return "zfs";
554 case S_MAGIC_ZONEFS: /* 0x5A4F4653 local */
555 return "zonefs";
556 case S_MAGIC_ZSMALLOC: /* 0x58295829 local */
557 return "zsmallocfs";
560 # elif __GNU__
561 case FSTYPE_UFS:
562 return "ufs";
563 case FSTYPE_NFS:
564 return "nfs";
565 case FSTYPE_GFS:
566 return "gfs";
567 case FSTYPE_LFS:
568 return "lfs";
569 case FSTYPE_SYSV:
570 return "sysv";
571 case FSTYPE_FTP:
572 return "ftp";
573 case FSTYPE_TAR:
574 return "tar";
575 case FSTYPE_AR:
576 return "ar";
577 case FSTYPE_CPIO:
578 return "cpio";
579 case FSTYPE_MSLOSS:
580 return "msloss";
581 case FSTYPE_CPM:
582 return "cpm";
583 case FSTYPE_HFS:
584 return "hfs";
585 case FSTYPE_DTFS:
586 return "dtfs";
587 case FSTYPE_GRFS:
588 return "grfs";
589 case FSTYPE_TERM:
590 return "term";
591 case FSTYPE_DEV:
592 return "dev";
593 case FSTYPE_PROC:
594 return "proc";
595 case FSTYPE_IFSOCK:
596 return "ifsock";
597 case FSTYPE_AFS:
598 return "afs";
599 case FSTYPE_DFS:
600 return "dfs";
601 case FSTYPE_PROC9:
602 return "proc9";
603 case FSTYPE_SOCKET:
604 return "socket";
605 case FSTYPE_MISC:
606 return "misc";
607 case FSTYPE_EXT2FS:
608 return "ext2/ext3";
609 case FSTYPE_HTTP:
610 return "http";
611 case FSTYPE_MEMFS:
612 return "memfs";
613 case FSTYPE_ISO9660:
614 return "iso9660";
615 # endif
616 default:
618 unsigned long int type = statfsbuf->f_type;
619 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
620 + (sizeof type * CHAR_BIT + 3) / 4];
621 sprintf (buf, "UNKNOWN (0x%lx)", type);
622 return buf;
625 #endif
628 NODISCARD
629 static char *
630 human_access (struct stat const *statbuf)
632 static char modebuf[12];
633 filemodestring (statbuf, modebuf);
634 modebuf[10] = 0;
635 return modebuf;
638 NODISCARD
639 static char *
640 human_time (struct timespec t)
642 /* STR must be at least INT_BUFSIZE_BOUND (intmax_t) big, either
643 because localtime_rz fails, or because the time zone is truly
644 outlandish so that %z expands to a long string. */
645 static char str[INT_BUFSIZE_BOUND (intmax_t)
646 + INT_STRLEN_BOUND (int) /* YYYY */
647 + 1 /* because YYYY might equal INT_MAX + 1900 */
648 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +"];
649 static timezone_t tz;
650 if (!tz)
651 tz = tzalloc (getenv ("TZ"));
652 struct tm tm;
653 int ns = t.tv_nsec;
654 if (localtime_rz (tz, &t.tv_sec, &tm))
655 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", &tm, tz, ns);
656 else
658 char secbuf[INT_BUFSIZE_BOUND (intmax_t)];
659 sprintf (str, "%s.%09d", timetostr (t.tv_sec, secbuf), ns);
661 return str;
664 /* PFORMAT points to a '%' followed by a prefix of a format, all of
665 size PREFIX_LEN. The flags allowed for this format are
666 ALLOWED_FLAGS; remove other printf flags from the prefix, then
667 append SUFFIX. */
668 static void
669 make_format (char *pformat, size_t prefix_len, char const *allowed_flags,
670 char const *suffix)
672 char *dst = pformat + 1;
673 char const *src;
674 char const *srclim = pformat + prefix_len;
675 for (src = dst; src < srclim && strchr (printf_flags, *src); src++)
676 if (strchr (allowed_flags, *src))
677 *dst++ = *src;
678 while (src < srclim)
679 *dst++ = *src++;
680 strcpy (dst, suffix);
683 static void
684 out_string (char *pformat, size_t prefix_len, char const *arg)
686 make_format (pformat, prefix_len, "-", "s");
687 printf (pformat, arg);
689 static int
690 out_int (char *pformat, size_t prefix_len, intmax_t arg)
692 make_format (pformat, prefix_len, "'-+ 0", "jd");
693 return printf (pformat, arg);
695 static int
696 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
698 make_format (pformat, prefix_len, "'-0", "ju");
699 return printf (pformat, arg);
701 static void
702 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
704 make_format (pformat, prefix_len, "-#0", "jo");
705 printf (pformat, arg);
707 static void
708 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
710 make_format (pformat, prefix_len, "-#0", "jx");
711 printf (pformat, arg);
713 static int
714 out_minus_zero (char *pformat, size_t prefix_len)
716 make_format (pformat, prefix_len, "'-+ 0", ".0f");
717 return printf (pformat, -0.25);
720 /* Output the number of seconds since the Epoch, using a format that
721 acts like printf's %f format. */
722 static void
723 out_epoch_sec (char *pformat, size_t prefix_len,
724 struct timespec arg)
726 char *dot = memchr (pformat, '.', prefix_len);
727 size_t sec_prefix_len = prefix_len;
728 int width = 0;
729 int precision = 0;
730 bool frac_left_adjust = false;
732 if (dot)
734 sec_prefix_len = dot - pformat;
735 pformat[prefix_len] = '\0';
737 if (ISDIGIT (dot[1]))
739 long int lprec = strtol (dot + 1, nullptr, 10);
740 precision = (lprec <= INT_MAX ? lprec : INT_MAX);
742 else
744 precision = 9;
747 if (precision && ISDIGIT (dot[-1]))
749 /* If a nontrivial width is given, subtract the width of the
750 decimal point and PRECISION digits that will be output
751 later. */
752 char *p = dot;
753 *dot = '\0';
756 --p;
757 while (ISDIGIT (p[-1]));
759 long int lwidth = strtol (p, nullptr, 10);
760 width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
761 if (1 < width)
763 p += (*p == '0');
764 sec_prefix_len = p - pformat;
765 int w_d = (decimal_point_len < width
766 ? width - decimal_point_len
767 : 0);
768 if (1 < w_d)
770 int w = w_d - precision;
771 if (1 < w)
773 char *dst = pformat;
774 for (char const *src = dst; src < p; src++)
776 if (*src == '-')
777 frac_left_adjust = true;
778 else
779 *dst++ = *src;
781 sec_prefix_len =
782 (dst - pformat
783 + (frac_left_adjust ? 0 : sprintf (dst, "%d", w)));
790 int divisor = 1;
791 for (int i = precision; i < 9; i++)
792 divisor *= 10;
793 int frac_sec = arg.tv_nsec / divisor;
794 int int_len;
796 if (TYPE_SIGNED (time_t))
798 bool minus_zero = false;
799 if (arg.tv_sec < 0 && arg.tv_nsec != 0)
801 int frac_sec_modulus = 1000000000 / divisor;
802 frac_sec = (frac_sec_modulus - frac_sec
803 - (arg.tv_nsec % divisor != 0));
804 arg.tv_sec += (frac_sec != 0);
805 minus_zero = (arg.tv_sec == 0);
807 int_len = (minus_zero
808 ? out_minus_zero (pformat, sec_prefix_len)
809 : out_int (pformat, sec_prefix_len, arg.tv_sec));
811 else
812 int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec);
814 if (precision)
816 int prec = (precision < 9 ? precision : 9);
817 int trailing_prec = precision - prec;
818 int ilen = (int_len < 0 ? 0 : int_len);
819 int trailing_width = (ilen < width && decimal_point_len < width - ilen
820 ? width - ilen - decimal_point_len - prec
821 : 0);
822 printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
823 trailing_width, trailing_prec, 0);
827 /* Print the context information of FILENAME, and return true iff the
828 context could not be obtained. */
829 NODISCARD
830 static bool
831 out_file_context (char *pformat, size_t prefix_len, char const *filename)
833 char *scontext;
834 bool fail = false;
836 if ((follow_links
837 ? getfilecon (filename, &scontext)
838 : lgetfilecon (filename, &scontext)) < 0)
840 error (0, errno, _("failed to get security context of %s"),
841 quoteaf (filename));
842 scontext = nullptr;
843 fail = true;
845 strcpy (pformat + prefix_len, "s");
846 printf (pformat, (scontext ? scontext : "?"));
847 if (scontext)
848 freecon (scontext);
849 return fail;
852 /* Print statfs info. Return zero upon success, nonzero upon failure. */
853 NODISCARD
854 static bool
855 print_statfs (char *pformat, size_t prefix_len, MAYBE_UNUSED char mod, char m,
856 int fd, char const *filename,
857 void const *data)
859 STRUCT_STATVFS const *statfsbuf = data;
860 bool fail = false;
862 switch (m)
864 case 'n':
865 out_string (pformat, prefix_len, filename);
866 break;
868 case 'i':
870 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
871 uintmax_t fsid = statfsbuf->f_fsid;
872 #else
873 typedef unsigned int fsid_word;
874 static_assert (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
875 static_assert (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word)
876 == 0);
877 static_assert (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
878 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
880 /* Assume a little-endian word order, as that is compatible
881 with glibc's statvfs implementation. */
882 uintmax_t fsid = 0;
883 int words = sizeof statfsbuf->f_fsid / sizeof *p;
884 for (int i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
886 uintmax_t u = p[words - 1 - i];
887 fsid |= u << (i * CHAR_BIT * sizeof *p);
889 #endif
890 out_uint_x (pformat, prefix_len, fsid);
892 break;
894 case 'l':
895 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
896 break;
897 case 't':
898 #if HAVE_STRUCT_STATXFS_F_TYPE
899 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
900 #else
901 fputc ('?', stdout);
902 #endif
903 break;
904 case 'T':
905 out_string (pformat, prefix_len, human_fstype (statfsbuf));
906 break;
907 case 'b':
908 out_int (pformat, prefix_len, statfsbuf->f_blocks);
909 break;
910 case 'f':
911 out_int (pformat, prefix_len, statfsbuf->f_bfree);
912 break;
913 case 'a':
914 out_int (pformat, prefix_len, statfsbuf->f_bavail);
915 break;
916 case 's':
917 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
918 break;
919 case 'S':
921 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
922 if (! frsize)
923 frsize = statfsbuf->f_bsize;
924 out_uint (pformat, prefix_len, frsize);
926 break;
927 case 'c':
928 out_uint (pformat, prefix_len, statfsbuf->f_files);
929 break;
930 case 'd':
931 out_int (pformat, prefix_len, statfsbuf->f_ffree);
932 break;
933 default:
934 fputc ('?', stdout);
935 break;
937 return fail;
940 /* Return any bind mounted source for a path.
941 The caller should not free the returned buffer.
942 Return nullptr if no bind mount found. */
943 NODISCARD
944 static char const *
945 find_bind_mount (char const * name)
947 char const * bind_mount = nullptr;
949 static struct mount_entry *mount_list;
950 static bool tried_mount_list = false;
951 if (!tried_mount_list) /* attempt/warn once per process. */
953 if (!(mount_list = read_file_system_list (false)))
954 error (0, errno, "%s", _("cannot read table of mounted file systems"));
955 tried_mount_list = true;
958 struct stat name_stats;
959 if (stat (name, &name_stats) != 0)
960 return nullptr;
962 struct mount_entry *me;
963 for (me = mount_list; me; me = me->me_next)
965 if (me->me_dummy && me->me_devname[0] == '/'
966 && STREQ (me->me_mountdir, name))
968 struct stat dev_stats;
970 if (stat (me->me_devname, &dev_stats) == 0
971 && psame_inode (&name_stats, &dev_stats))
973 bind_mount = me->me_devname;
974 break;
979 return bind_mount;
982 /* Print mount point. Return zero upon success, nonzero upon failure. */
983 NODISCARD
984 static bool
985 out_mount_point (char const *filename, char *pformat, size_t prefix_len,
986 const struct stat *statp)
989 char const *np = "?", *bp = nullptr;
990 char *mp = nullptr;
991 bool fail = true;
993 /* Look for bind mounts first. Note we output the immediate alias,
994 rather than further resolving to a base device mount point. */
995 if (follow_links || !S_ISLNK (statp->st_mode))
997 char *resolved = canonicalize_file_name (filename);
998 if (!resolved)
1000 error (0, errno, _("failed to canonicalize %s"), quoteaf (filename));
1001 goto print_mount_point;
1003 bp = find_bind_mount (resolved);
1004 free (resolved);
1005 if (bp)
1007 fail = false;
1008 goto print_mount_point;
1012 /* If there is no direct bind mount, then navigate
1013 back up the tree looking for a device change.
1014 Note we don't detect if any of the directory components
1015 are bind mounted to the same device, but that's OK
1016 since we've not directly queried them. */
1017 if ((mp = find_mount_point (filename, statp)))
1019 /* This dir might be bind mounted to another device,
1020 so we resolve the bound source in that case also. */
1021 bp = find_bind_mount (mp);
1022 fail = false;
1025 print_mount_point:
1027 out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
1028 free (mp);
1029 return fail;
1032 /* Map a TS with negative TS.tv_nsec to {0,0}. */
1033 static inline struct timespec
1034 neg_to_zero (struct timespec ts)
1036 if (0 <= ts.tv_nsec)
1037 return ts;
1038 struct timespec z = {0};
1039 return z;
1042 /* Set the quoting style default if the environment variable
1043 QUOTING_STYLE is set. */
1045 static void
1046 getenv_quoting_style (void)
1048 char const *q_style = getenv ("QUOTING_STYLE");
1049 if (q_style)
1051 int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals);
1052 if (0 <= i)
1053 set_quoting_style (nullptr, quoting_style_vals[i]);
1054 else
1056 set_quoting_style (nullptr, shell_escape_always_quoting_style);
1057 error (0, 0, _("ignoring invalid value of environment "
1058 "variable QUOTING_STYLE: %s"), quote (q_style));
1061 else
1062 set_quoting_style (nullptr, shell_escape_always_quoting_style);
1065 /* Equivalent to quotearg(), but explicit to avoid syntax checks. */
1066 #define quoteN(x) quotearg_style (get_quoting_style (nullptr), x)
1068 /* Output a single-character \ escape. */
1070 static void
1071 print_esc_char (char c)
1073 switch (c)
1075 case 'a': /* Alert. */
1076 c ='\a';
1077 break;
1078 case 'b': /* Backspace. */
1079 c ='\b';
1080 break;
1081 case 'e': /* Escape. */
1082 c ='\x1B';
1083 break;
1084 case 'f': /* Form feed. */
1085 c ='\f';
1086 break;
1087 case 'n': /* New line. */
1088 c ='\n';
1089 break;
1090 case 'r': /* Carriage return. */
1091 c ='\r';
1092 break;
1093 case 't': /* Horizontal tab. */
1094 c ='\t';
1095 break;
1096 case 'v': /* Vertical tab. */
1097 c ='\v';
1098 break;
1099 case '"':
1100 case '\\':
1101 break;
1102 default:
1103 error (0, 0, _("warning: unrecognized escape '\\%c'"), c);
1104 break;
1106 putchar (c);
1109 ATTRIBUTE_PURE
1110 static size_t
1111 format_code_offset (char const *directive)
1113 size_t len = strspn (directive + 1, printf_flags);
1114 char const *fmt_char = directive + len + 1;
1115 fmt_char += strspn (fmt_char, digits);
1116 if (*fmt_char == '.')
1117 fmt_char += 1 + strspn (fmt_char + 1, digits);
1118 return fmt_char - directive;
1121 /* Print the information specified by the format string, FORMAT,
1122 calling PRINT_FUNC for each %-directive encountered.
1123 Return zero upon success, nonzero upon failure. */
1124 NODISCARD
1125 static bool
1126 print_it (char const *format, int fd, char const *filename,
1127 bool (*print_func) (char *, size_t, char, char,
1128 int, char const *, void const *),
1129 void const *data)
1131 bool fail = false;
1133 /* Add 2 to accommodate our conversion of the stat '%s' format string
1134 to the longer printf '%llu' one. */
1135 enum
1137 MAX_ADDITIONAL_BYTES =
1138 (MAX (sizeof "jd",
1139 MAX (sizeof "jo", MAX (sizeof "ju", sizeof "jx")))
1140 - 1)
1142 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
1143 char *dest = xmalloc (n_alloc);
1144 char const *b;
1145 for (b = format; *b; b++)
1147 switch (*b)
1149 case '%':
1151 size_t len = format_code_offset (b);
1152 char fmt_char = *(b + len);
1153 char mod_char = 0;
1154 memcpy (dest, b, len);
1155 b += len;
1157 switch (fmt_char)
1159 case '\0':
1160 --b;
1161 FALLTHROUGH;
1162 case '%':
1163 if (1 < len)
1165 dest[len] = fmt_char;
1166 dest[len + 1] = '\0';
1167 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
1168 quote (dest));
1170 putchar ('%');
1171 break;
1172 case 'H':
1173 case 'L':
1174 mod_char = fmt_char;
1175 fmt_char = *(b + 1);
1176 if (print_func == print_stat
1177 && (fmt_char == 'd' || fmt_char == 'r'))
1179 b++;
1181 else
1183 fmt_char = mod_char;
1184 mod_char = 0;
1186 FALLTHROUGH;
1187 default:
1188 fail |= print_func (dest, len, mod_char, fmt_char,
1189 fd, filename, data);
1190 break;
1192 break;
1195 case '\\':
1196 if ( ! interpret_backslash_escapes)
1198 putchar ('\\');
1199 break;
1201 ++b;
1202 if (isoct (*b))
1204 int esc_value = fromoct (*b);
1205 int esc_length = 1; /* number of octal digits */
1206 for (++b; esc_length < 3 && isoct (*b);
1207 ++esc_length, ++b)
1209 esc_value = esc_value * 8 + fromoct (*b);
1211 putchar (esc_value);
1212 --b;
1214 else if (*b == 'x' && c_isxdigit (to_uchar (b[1])))
1216 int esc_value = fromhex (b[1]); /* Value of \xhh escape. */
1217 /* A hexadecimal \xhh escape sequence must have
1218 1 or 2 hex. digits. */
1219 ++b;
1220 if (c_isxdigit (to_uchar (b[1])))
1222 ++b;
1223 esc_value = esc_value * 16 + fromhex (*b);
1225 putchar (esc_value);
1227 else if (*b == '\0')
1229 error (0, 0, _("warning: backslash at end of format"));
1230 putchar ('\\');
1231 /* Arrange to exit the loop. */
1232 --b;
1234 else
1236 print_esc_char (*b);
1238 break;
1240 default:
1241 putchar (*b);
1242 break;
1245 free (dest);
1247 fputs (trailing_delim, stdout);
1249 return fail;
1252 /* Stat the file system and print what we find. */
1253 NODISCARD
1254 static bool
1255 do_statfs (char const *filename, char const *format)
1257 STRUCT_STATVFS statfsbuf;
1259 if (STREQ (filename, "-"))
1261 error (0, 0, _("using %s to denote standard input does not work"
1262 " in file system mode"), quoteaf (filename));
1263 return false;
1266 if (STATFS (filename, &statfsbuf) != 0)
1268 error (0, errno, _("cannot read file system information for %s"),
1269 quoteaf (filename));
1270 return false;
1273 bool fail = print_it (format, -1, filename, print_statfs, &statfsbuf);
1274 return ! fail;
1277 struct print_args {
1278 struct stat *st;
1279 struct timespec btime;
1282 /* Ask statx to avoid syncing? */
1283 static bool dont_sync;
1285 /* Ask statx to force sync? */
1286 static bool force_sync;
1288 #if USE_STATX
1289 static unsigned int
1290 fmt_to_mask (char fmt)
1292 switch (fmt)
1294 case 'N':
1295 return STATX_MODE;
1296 case 'd':
1297 case 'D':
1298 return STATX_MODE;
1299 case 'i':
1300 return STATX_INO;
1301 case 'a':
1302 case 'A':
1303 return STATX_MODE;
1304 case 'f':
1305 return STATX_MODE|STATX_TYPE;
1306 case 'F':
1307 return STATX_TYPE;
1308 case 'h':
1309 return STATX_NLINK;
1310 case 'u':
1311 case 'U':
1312 return STATX_UID;
1313 case 'g':
1314 case 'G':
1315 return STATX_GID;
1316 case 'm':
1317 return STATX_MODE|STATX_INO;
1318 case 's':
1319 return STATX_SIZE;
1320 case 't':
1321 case 'T':
1322 return STATX_MODE;
1323 case 'b':
1324 return STATX_BLOCKS;
1325 case 'w':
1326 case 'W':
1327 return STATX_BTIME;
1328 case 'x':
1329 case 'X':
1330 return STATX_ATIME;
1331 case 'y':
1332 case 'Y':
1333 return STATX_MTIME;
1334 case 'z':
1335 case 'Z':
1336 return STATX_CTIME;
1338 return 0;
1341 ATTRIBUTE_PURE
1342 static unsigned int
1343 format_to_mask (char const *format)
1345 unsigned int mask = 0;
1346 char const *b;
1348 for (b = format; *b; b++)
1350 if (*b != '%')
1351 continue;
1353 b += format_code_offset (b);
1354 if (*b == '\0')
1355 break;
1356 mask |= fmt_to_mask (*b);
1358 return mask;
1361 /* statx the file and print what we find */
1362 NODISCARD
1363 static bool
1364 do_stat (char const *filename, char const *format, char const *format2)
1366 int fd = STREQ (filename, "-") ? 0 : AT_FDCWD;
1367 int flags = 0;
1368 struct stat st;
1369 struct statx stx = {0};
1370 char const *pathname = filename;
1371 struct print_args pa;
1372 pa.st = &st;
1373 pa.btime = (struct timespec) {.tv_sec = -1, .tv_nsec = -1};
1375 if (AT_FDCWD != fd)
1377 pathname = "";
1378 flags = AT_EMPTY_PATH;
1380 else if (!follow_links)
1382 flags = AT_SYMLINK_NOFOLLOW;
1385 if (dont_sync)
1386 flags |= AT_STATX_DONT_SYNC;
1387 else if (force_sync)
1388 flags |= AT_STATX_FORCE_SYNC;
1390 if (! force_sync)
1391 flags |= AT_NO_AUTOMOUNT;
1393 fd = statx (fd, pathname, flags, format_to_mask (format), &stx);
1394 if (fd < 0)
1396 if (flags & AT_EMPTY_PATH)
1397 error (0, errno, _("cannot stat standard input"));
1398 else
1399 error (0, errno, _("cannot statx %s"), quoteaf (filename));
1400 return false;
1403 if (S_ISBLK (stx.stx_mode) || S_ISCHR (stx.stx_mode))
1404 format = format2;
1406 statx_to_stat (&stx, &st);
1407 if (stx.stx_mask & STATX_BTIME)
1408 pa.btime = statx_timestamp_to_timespec (stx.stx_btime);
1410 bool fail = print_it (format, fd, filename, print_stat, &pa);
1411 return ! fail;
1414 #else /* USE_STATX */
1416 static struct timespec
1417 get_birthtime (int fd, char const *filename, struct stat const *st)
1419 struct timespec ts = get_stat_birthtime (st);
1421 # if HAVE_GETATTRAT
1422 if (ts.tv_nsec < 0)
1424 nvlist_t *response;
1425 if ((fd < 0
1426 ? getattrat (AT_FDCWD, XATTR_VIEW_READWRITE, filename, &response)
1427 : fgetattr (fd, XATTR_VIEW_READWRITE, &response))
1428 == 0)
1430 uint64_t *val;
1431 uint_t n;
1432 if (nvlist_lookup_uint64_array (response, A_CRTIME, &val, &n) == 0
1433 && 2 <= n
1434 && val[0] <= TYPE_MAXIMUM (time_t)
1435 && val[1] < 1000000000 * 2 /* for leap seconds */)
1437 ts.tv_sec = val[0];
1438 ts.tv_nsec = val[1];
1440 nvlist_free (response);
1443 # endif
1445 return ts;
1449 /* stat the file and print what we find */
1450 NODISCARD
1451 static bool
1452 do_stat (char const *filename, char const *format,
1453 char const *format2)
1455 int fd = STREQ (filename, "-") ? 0 : -1;
1456 struct stat statbuf;
1457 struct print_args pa;
1458 pa.st = &statbuf;
1459 pa.btime = (struct timespec) {.tv_sec = -1, .tv_nsec = -1};
1461 if (0 <= fd)
1463 if (fstat (fd, &statbuf) != 0)
1465 error (0, errno, _("cannot stat standard input"));
1466 return false;
1469 /* We can't use the shorter
1470 (follow_links?stat:lstat) (filename, &statbug)
1471 since stat might be a function-like macro. */
1472 else if ((follow_links
1473 ? stat (filename, &statbuf)
1474 : lstat (filename, &statbuf)) != 0)
1476 error (0, errno, _("cannot stat %s"), quoteaf (filename));
1477 return false;
1480 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
1481 format = format2;
1483 bool fail = print_it (format, fd, filename, print_stat, &pa);
1484 return ! fail;
1486 #endif /* USE_STATX */
1488 /* POSIX requires 'ls' to print file sizes without a sign, even
1489 when negative. Be consistent with that. */
1491 static uintmax_t
1492 unsigned_file_size (off_t size)
1494 return size + (size < 0) * ((uintmax_t) OFF_T_MAX - OFF_T_MIN + 1);
1497 /* Print stat info. Return zero upon success, nonzero upon failure. */
1498 static bool
1499 print_stat (char *pformat, size_t prefix_len, char mod, char m,
1500 int fd, char const *filename, void const *data)
1502 struct print_args *parg = (struct print_args *) data;
1503 struct stat *statbuf = parg->st;
1504 struct timespec btime = parg->btime;
1505 struct passwd *pw_ent;
1506 struct group *gw_ent;
1507 bool fail = false;
1509 switch (m)
1511 case 'n':
1512 out_string (pformat, prefix_len, filename);
1513 break;
1514 case 'N':
1515 out_string (pformat, prefix_len, quoteN (filename));
1516 if (S_ISLNK (statbuf->st_mode))
1518 char *linkname = areadlink_with_size (filename, statbuf->st_size);
1519 if (linkname == nullptr)
1521 error (0, errno, _("cannot read symbolic link %s"),
1522 quoteaf (filename));
1523 return true;
1525 printf (" -> ");
1526 out_string (pformat, prefix_len, quoteN (linkname));
1527 free (linkname);
1529 break;
1530 case 'd':
1531 if (mod == 'H')
1532 out_uint (pformat, prefix_len, major (statbuf->st_dev));
1533 else if (mod == 'L')
1534 out_uint (pformat, prefix_len, minor (statbuf->st_dev));
1535 else
1536 out_uint (pformat, prefix_len, statbuf->st_dev);
1537 break;
1538 case 'D':
1539 out_uint_x (pformat, prefix_len, statbuf->st_dev);
1540 break;
1541 case 'i':
1542 out_uint (pformat, prefix_len, statbuf->st_ino);
1543 break;
1544 case 'a':
1545 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
1546 break;
1547 case 'A':
1548 out_string (pformat, prefix_len, human_access (statbuf));
1549 break;
1550 case 'f':
1551 out_uint_x (pformat, prefix_len, statbuf->st_mode);
1552 break;
1553 case 'F':
1554 out_string (pformat, prefix_len, file_type (statbuf));
1555 break;
1556 case 'h':
1557 out_uint (pformat, prefix_len, statbuf->st_nlink);
1558 break;
1559 case 'u':
1560 out_uint (pformat, prefix_len, statbuf->st_uid);
1561 break;
1562 case 'U':
1563 pw_ent = getpwuid (statbuf->st_uid);
1564 out_string (pformat, prefix_len,
1565 pw_ent ? pw_ent->pw_name : "UNKNOWN");
1566 break;
1567 case 'g':
1568 out_uint (pformat, prefix_len, statbuf->st_gid);
1569 break;
1570 case 'G':
1571 gw_ent = getgrgid (statbuf->st_gid);
1572 out_string (pformat, prefix_len,
1573 gw_ent ? gw_ent->gr_name : "UNKNOWN");
1574 break;
1575 case 'm':
1576 fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
1577 break;
1578 case 's':
1579 out_uint (pformat, prefix_len, unsigned_file_size (statbuf->st_size));
1580 break;
1581 case 'r':
1582 if (mod == 'H')
1583 out_uint (pformat, prefix_len, major (statbuf->st_rdev));
1584 else if (mod == 'L')
1585 out_uint (pformat, prefix_len, minor (statbuf->st_rdev));
1586 else
1587 out_uint (pformat, prefix_len, statbuf->st_rdev);
1588 break;
1589 case 'R':
1590 out_uint_x (pformat, prefix_len, statbuf->st_rdev);
1591 break;
1592 case 't':
1593 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
1594 break;
1595 case 'T':
1596 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
1597 break;
1598 case 'B':
1599 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
1600 break;
1601 case 'b':
1602 out_uint (pformat, prefix_len, STP_NBLOCKS (statbuf));
1603 break;
1604 case 'o':
1605 out_uint (pformat, prefix_len, STP_BLKSIZE (statbuf));
1606 break;
1607 case 'w':
1609 #if ! USE_STATX
1610 btime = get_birthtime (fd, filename, statbuf);
1611 #endif
1612 if (btime.tv_nsec < 0)
1613 out_string (pformat, prefix_len, "-");
1614 else
1615 out_string (pformat, prefix_len, human_time (btime));
1617 break;
1618 case 'W':
1620 #if ! USE_STATX
1621 btime = get_birthtime (fd, filename, statbuf);
1622 #endif
1623 out_epoch_sec (pformat, prefix_len, neg_to_zero (btime));
1625 break;
1626 case 'x':
1627 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
1628 break;
1629 case 'X':
1630 out_epoch_sec (pformat, prefix_len, get_stat_atime (statbuf));
1631 break;
1632 case 'y':
1633 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
1634 break;
1635 case 'Y':
1636 out_epoch_sec (pformat, prefix_len, get_stat_mtime (statbuf));
1637 break;
1638 case 'z':
1639 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
1640 break;
1641 case 'Z':
1642 out_epoch_sec (pformat, prefix_len, get_stat_ctime (statbuf));
1643 break;
1644 case 'C':
1645 fail |= out_file_context (pformat, prefix_len, filename);
1646 break;
1647 default:
1648 fputc ('?', stdout);
1649 break;
1651 return fail;
1654 /* Return an allocated format string in static storage that
1655 corresponds to whether FS and TERSE options were declared. */
1656 static char *
1657 default_format (bool fs, bool terse, bool device)
1659 char *format;
1660 if (fs)
1662 if (terse)
1663 format = xstrdup (fmt_terse_fs);
1664 else
1666 /* TRANSLATORS: This string uses format specifiers from
1667 'stat --help' with --file-system, and NOT from printf. */
1668 format = xstrdup (_(" File: \"%n\"\n"
1669 " ID: %-8i Namelen: %-7l Type: %T\n"
1670 "Block size: %-10s Fundamental block size: %S\n"
1671 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
1672 "Inodes: Total: %-10c Free: %d\n"));
1675 else /* ! fs */
1677 if (terse)
1679 if (0 < is_selinux_enabled ())
1680 format = xstrdup (fmt_terse_selinux);
1681 else
1682 format = xstrdup (fmt_terse_regular);
1684 else
1686 char *temp;
1687 /* TRANSLATORS: This string uses format specifiers from
1688 'stat --help' without --file-system, and NOT from printf. */
1689 format = xstrdup (_("\
1690 File: %N\n\
1691 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1692 "));
1694 temp = format;
1695 if (device)
1697 /* TRANSLATORS: This string uses format specifiers from
1698 'stat --help' without --file-system, and NOT from printf. */
1699 format = xasprintf ("%s%s", format, _("\
1700 " "Device: %Hd,%Ld\tInode: %-10i Links: %-5h Device type: %Hr,%Lr\n\
1701 "));
1703 else
1705 /* TRANSLATORS: This string uses format specifiers from
1706 'stat --help' without --file-system, and NOT from printf. */
1707 format = xasprintf ("%s%s", format, _("\
1708 " "Device: %Hd,%Ld\tInode: %-10i Links: %h\n\
1709 "));
1711 free (temp);
1713 temp = format;
1714 /* TRANSLATORS: This string uses format specifiers from
1715 'stat --help' without --file-system, and NOT from printf. */
1716 format = xasprintf ("%s%s", format, _("\
1717 " "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1718 "));
1719 free (temp);
1721 if (0 < is_selinux_enabled ())
1723 temp = format;
1724 /* TRANSLATORS: This string uses format specifiers from
1725 'stat --help' without --file-system, and NOT from printf. */
1726 format = xasprintf ("%s%s", format, _("Context: %C\n"));
1727 free (temp);
1730 temp = format;
1731 /* TRANSLATORS: This string uses format specifiers from
1732 'stat --help' without --file-system, and NOT from printf. */
1733 format = xasprintf ("%s%s", format,
1734 _("Access: %x\n"
1735 "Modify: %y\n"
1736 "Change: %z\n"
1737 " Birth: %w\n"));
1738 free (temp);
1741 return format;
1744 void
1745 usage (int status)
1747 if (status != EXIT_SUCCESS)
1748 emit_try_help ();
1749 else
1751 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
1752 fputs (_("\
1753 Display file or file system status.\n\
1754 "), stdout);
1756 emit_mandatory_arg_note ();
1758 fputs (_("\
1759 -L, --dereference follow links\n\
1760 -f, --file-system display file system status instead of file status\n\
1761 "), stdout);
1762 fputs (_("\
1763 --cached=MODE specify how to use cached attributes;\n\
1764 useful on remote file systems. See MODE below\n\
1765 "), stdout);
1766 fputs (_("\
1767 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1768 output a newline after each use of FORMAT\n\
1769 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1770 and do not output a mandatory trailing newline;\n\
1771 if you want a newline, include \\n in FORMAT\n\
1772 -t, --terse print the information in terse form\n\
1773 "), stdout);
1774 fputs (HELP_OPTION_DESCRIPTION, stdout);
1775 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1777 fputs (_("\n\
1778 The MODE argument of --cached can be: always, never, or default.\n\
1779 'always' will use cached attributes if available, while\n\
1780 'never' will try to synchronize with the latest attributes, and\n\
1781 'default' will leave it up to the underlying file system.\n\
1782 "), stdout);
1784 fputs (_("\n\
1785 The valid format sequences for files (without --file-system):\n\
1787 %a permission bits in octal (see '#' and '0' printf flags)\n\
1788 %A permission bits and file type in human readable form\n\
1789 %b number of blocks allocated (see %B)\n\
1790 %B the size in bytes of each block reported by %b\n\
1791 %C SELinux security context string\n\
1792 "), stdout);
1793 fputs (_("\
1794 %d device number in decimal (st_dev)\n\
1795 %D device number in hex (st_dev)\n\
1796 %Hd major device number in decimal\n\
1797 %Ld minor device number in decimal\n\
1798 %f raw mode in hex\n\
1799 %F file type\n\
1800 %g group ID of owner\n\
1801 %G group name of owner\n\
1802 "), stdout);
1803 fputs (_("\
1804 %h number of hard links\n\
1805 %i inode number\n\
1806 %m mount point\n\
1807 %n file name\n\
1808 %N quoted file name with dereference if symbolic link\n\
1809 %o optimal I/O transfer size hint\n\
1810 %s total size, in bytes\n\
1811 %r device type in decimal (st_rdev)\n\
1812 %R device type in hex (st_rdev)\n\
1813 %Hr major device type in decimal, for character/block device special files\n\
1814 %Lr minor device type in decimal, for character/block device special files\n\
1815 %t major device type in hex, for character/block device special files\n\
1816 %T minor device type in hex, for character/block device special files\n\
1817 "), stdout);
1818 fputs (_("\
1819 %u user ID of owner\n\
1820 %U user name of owner\n\
1821 %w time of file birth, human-readable; - if unknown\n\
1822 %W time of file birth, seconds since Epoch; 0 if unknown\n\
1823 %x time of last access, human-readable\n\
1824 %X time of last access, seconds since Epoch\n\
1825 %y time of last data modification, human-readable\n\
1826 %Y time of last data modification, seconds since Epoch\n\
1827 %z time of last status change, human-readable\n\
1828 %Z time of last status change, seconds since Epoch\n\
1830 "), stdout);
1832 fputs (_("\
1833 Valid format sequences for file systems:\n\
1835 %a free blocks available to non-superuser\n\
1836 %b total data blocks in file system\n\
1837 %c total file nodes in file system\n\
1838 %d free file nodes in file system\n\
1839 %f free blocks in file system\n\
1840 "), stdout);
1841 fputs (_("\
1842 %i file system ID in hex\n\
1843 %l maximum length of filenames\n\
1844 %n file name\n\
1845 %s block size (for faster transfers)\n\
1846 %S fundamental block size (for block counts)\n\
1847 %t file system type in hex\n\
1848 %T file system type in human readable form\n\
1849 "), stdout);
1851 printf (_("\n\
1852 --terse is equivalent to the following FORMAT:\n\
1855 #if HAVE_SELINUX_SELINUX_H
1856 fmt_terse_selinux
1857 #else
1858 fmt_terse_regular
1859 #endif
1862 printf (_("\
1863 --terse --file-system is equivalent to the following FORMAT:\n\
1865 "), fmt_terse_fs);
1867 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1868 emit_ancillary_info (PROGRAM_NAME);
1870 exit (status);
1874 main (int argc, char *argv[])
1876 int c;
1877 bool fs = false;
1878 bool terse = false;
1879 char *format = nullptr;
1880 char *format2;
1881 bool ok = true;
1883 initialize_main (&argc, &argv);
1884 set_program_name (argv[0]);
1885 setlocale (LC_ALL, "");
1886 bindtextdomain (PACKAGE, LOCALEDIR);
1887 textdomain (PACKAGE);
1889 struct lconv const *locale = localeconv ();
1890 decimal_point = (locale->decimal_point[0] ? locale->decimal_point : ".");
1891 decimal_point_len = strlen (decimal_point);
1893 atexit (close_stdout);
1895 while ((c = getopt_long (argc, argv, "c:fLt", long_options, nullptr)) != -1)
1897 switch (c)
1899 case PRINTF_OPTION:
1900 format = optarg;
1901 interpret_backslash_escapes = true;
1902 trailing_delim = "";
1903 break;
1905 case 'c':
1906 format = optarg;
1907 interpret_backslash_escapes = false;
1908 trailing_delim = "\n";
1909 break;
1911 case 'L':
1912 follow_links = true;
1913 break;
1915 case 'f':
1916 fs = true;
1917 break;
1919 case 't':
1920 terse = true;
1921 break;
1923 case 0:
1924 switch (XARGMATCH ("--cached", optarg, cached_args, cached_modes))
1926 case cached_never:
1927 force_sync = true;
1928 dont_sync = false;
1929 break;
1930 case cached_always:
1931 force_sync = false;
1932 dont_sync = true;
1933 break;
1934 case cached_default:
1935 force_sync = false;
1936 dont_sync = false;
1938 break;
1940 case_GETOPT_HELP_CHAR;
1942 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1944 default:
1945 usage (EXIT_FAILURE);
1949 if (argc == optind)
1951 error (0, 0, _("missing operand"));
1952 usage (EXIT_FAILURE);
1955 if (format)
1957 if (strstr (format, "%N"))
1958 getenv_quoting_style ();
1959 format2 = format;
1961 else
1963 format = default_format (fs, terse, /* device= */ false);
1964 format2 = default_format (fs, terse, /* device= */ true);
1967 for (int i = optind; i < argc; i++)
1968 ok &= (fs
1969 ? do_statfs (argv[i], format)
1970 : do_stat (argv[i], format, format2));
1972 main_exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);