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