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