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