stat: drop %C support when printing file system details
[coreutils.git] / src / stat.c
blobfabbc17e1ca632a6474965bae1e220b07bf134d3
1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2010 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 Written by Michael Meskes. */
19 #include <config.h>
21 /* Keep this conditional in sync with the similar conditional in
22 ../m4/stat-prog.m4. */
23 #if (STAT_STATVFS \
24 && (HAVE_STRUCT_STATVFS_F_BASETYPE || HAVE_STRUCT_STATVFS_F_FSTYPENAME \
25 || (! HAVE_STRUCT_STATFS_F_FSTYPENAME && HAVE_STRUCT_STATVFS_F_TYPE)))
26 # define USE_STATVFS 1
27 #else
28 # define USE_STATVFS 0
29 #endif
31 #include <stddef.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #if USE_STATVFS
37 # include <sys/statvfs.h>
38 #elif HAVE_SYS_VFS_H
39 # include <sys/vfs.h>
40 #elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
41 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
42 It does have statvfs.h, but shouldn't use it, since it doesn't
43 HAVE_STRUCT_STATVFS_F_BASETYPE. So find a clean way to fix it. */
44 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
45 # include <sys/param.h>
46 # include <sys/mount.h>
47 # if HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
48 /* Ultrix 4.4 needs these for the declaration of struct statfs. */
49 # include <netinet/in.h>
50 # include <nfs/nfs_clnt.h>
51 # include <nfs/vfs.h>
52 # endif
53 #elif HAVE_OS_H /* BeOS */
54 # include <fs_info.h>
55 #endif
56 #include <selinux/selinux.h>
58 #include "system.h"
60 #include "alignof.h"
61 #include "areadlink.h"
62 #include "error.h"
63 #include "file-type.h"
64 #include "filemode.h"
65 #include "fs.h"
66 #include "getopt.h"
67 #include "mountlist.h"
68 #include "quote.h"
69 #include "quotearg.h"
70 #include "stat-time.h"
71 #include "strftime.h"
72 #include "find-mount-point.h"
73 #include "xvasprintf.h"
75 #if USE_STATVFS
76 # define STRUCT_STATVFS struct statvfs
77 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
78 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
79 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
80 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
81 # endif
82 # define STATFS statvfs
83 # define STATFS_FRSIZE(S) ((S)->f_frsize)
84 #else
85 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
86 # if HAVE_STRUCT_STATFS_F_NAMELEN
87 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
88 # endif
89 # define STATFS statfs
90 # if HAVE_OS_H /* BeOS */
91 /* BeOS has a statvfs function, but it does not return sensible values
92 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
93 f_fstypename. Use 'struct fs_info' instead. */
94 static int ATTRIBUTE_WARN_UNUSED_RESULT
95 statfs (char const *filename, struct fs_info *buf)
97 dev_t device = dev_for_path (filename);
98 if (device < 0)
100 errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
101 : device == B_BAD_VALUE ? EINVAL
102 : device == B_NAME_TOO_LONG ? ENAMETOOLONG
103 : device == B_NO_MEMORY ? ENOMEM
104 : device == B_FILE_ERROR ? EIO
105 : 0);
106 return -1;
108 /* If successful, buf->dev will be == device. */
109 return fs_stat_dev (device, buf);
111 # define f_fsid dev
112 # define f_blocks total_blocks
113 # define f_bfree free_blocks
114 # define f_bavail free_blocks
115 # define f_bsize io_size
116 # define f_files total_nodes
117 # define f_ffree free_nodes
118 # define STRUCT_STATVFS struct fs_info
119 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
120 # define STATFS_FRSIZE(S) ((S)->block_size)
121 # else
122 # define STRUCT_STATVFS struct statfs
123 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
124 # define STATFS_FRSIZE(S) 0
125 # endif
126 #endif
128 #ifdef SB_F_NAMEMAX
129 # define OUT_NAMEMAX out_uint
130 #else
131 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */
132 # define SB_F_NAMEMAX(S) "*"
133 # define OUT_NAMEMAX out_string
134 #endif
136 #if HAVE_STRUCT_STATVFS_F_BASETYPE
137 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
138 #else
139 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
140 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
141 # elif HAVE_OS_H /* BeOS */
142 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
143 # endif
144 #endif
146 /* FIXME: these are used by printf.c, too */
147 #define isodigit(c) ('0' <= (c) && (c) <= '7')
148 #define octtobin(c) ((c) - '0')
149 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
150 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
152 #define PROGRAM_NAME "stat"
154 #define AUTHORS proper_name ("Michael Meskes")
156 enum
158 PRINTF_OPTION = CHAR_MAX + 1
161 static struct option const long_options[] =
163 {"context", no_argument, 0, 'Z'},
164 {"dereference", no_argument, NULL, 'L'},
165 {"file-system", no_argument, NULL, 'f'},
166 {"format", required_argument, NULL, 'c'},
167 {"printf", required_argument, NULL, PRINTF_OPTION},
168 {"terse", no_argument, NULL, 't'},
169 {GETOPT_HELP_OPTION_DECL},
170 {GETOPT_VERSION_OPTION_DECL},
171 {NULL, 0, NULL, 0}
174 /* Whether to follow symbolic links; True for --dereference (-L). */
175 static bool follow_links;
177 /* Whether to interpret backslash-escape sequences.
178 True for --printf=FMT, not for --format=FMT (-c). */
179 static bool interpret_backslash_escapes;
181 /* The trailing delimiter string:
182 "" for --printf=FMT, "\n" for --format=FMT (-c). */
183 static char const *trailing_delim = "";
185 /* Return the type of the specified file system.
186 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
187 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
188 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
189 Still others have neither and have to get by with f_type (GNU/Linux).
190 But f_type may only exist in statfs (Cygwin). */
191 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
192 human_fstype (STRUCT_STATVFS const *statfsbuf)
194 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
195 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
196 #else
197 switch (statfsbuf->f_type)
199 # if defined __linux__
201 /* Compare with what's in libc:
202 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
203 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
204 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
205 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
206 | sort > sym_libc
207 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
208 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
209 | sort > sym_stat
210 diff -u sym_stat sym_libc
213 /* Also compare with the list in "man 2 statfs" using the
214 fs-magic-compare make target. */
216 /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
217 statements must be followed by a hexadecimal constant in
218 a comment. The S_MAGIC_... name and constant are automatically
219 combined to produce the #define directives in fs.h. */
221 case S_MAGIC_ADFS: /* 0xADF5 */
222 return "adfs";
223 case S_MAGIC_AFFS: /* 0xADFF */
224 return "affs";
225 case S_MAGIC_AFS: /* 0x5346414F */
226 return "afs";
227 case S_MAGIC_ANON_INODE_FS: /* 0x09041934 */
228 return "anon-inode FS";
229 case S_MAGIC_AUTOFS: /* 0x0187 */
230 return "autofs";
231 case S_MAGIC_BEFS: /* 0x42465331 */
232 return "befs";
233 case S_MAGIC_BFS: /* 0x1BADFACE */
234 return "bfs";
235 case S_MAGIC_BINFMT_MISC: /* 0x42494E4D */
236 return "binfmt_misc";
237 case S_MAGIC_BTRFS: /* 0x9123683E */
238 return "btrfs";
239 case S_MAGIC_CGROUP: /* 0x0027E0EB */
240 return "cgroupfs";
241 case S_MAGIC_CIFS: /* 0xFF534D42 */
242 return "cifs";
243 case S_MAGIC_CODA: /* 0x73757245 */
244 return "coda";
245 case S_MAGIC_COH: /* 0x012FF7B7 */
246 return "coh";
247 case S_MAGIC_CRAMFS: /* 0x28CD3D45 */
248 return "cramfs";
249 case S_MAGIC_CRAMFS_WEND: /* 0x453DCD28 */
250 return "cramfs-wend";
251 case S_MAGIC_DEBUGFS: /* 0x64626720 */
252 return "debugfs";
253 case S_MAGIC_DEVFS: /* 0x1373 */
254 return "devfs";
255 case S_MAGIC_DEVPTS: /* 0x1CD1 */
256 return "devpts";
257 case S_MAGIC_EFS: /* 0x00414A53 */
258 return "efs";
259 case S_MAGIC_EXT: /* 0x137D */
260 return "ext";
261 case S_MAGIC_EXT2: /* 0xEF53 */
262 return "ext2/ext3";
263 case S_MAGIC_EXT2_OLD: /* 0xEF51 */
264 return "ext2";
265 case S_MAGIC_FAT: /* 0x4006 */
266 return "fat";
267 case S_MAGIC_FUSEBLK: /* 0x65735546 */
268 return "fuseblk";
269 case S_MAGIC_FUSECTL: /* 0x65735543 */
270 return "fusectl";
271 case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA */
272 return "futexfs";
273 case S_MAGIC_GFS: /* 0x1161970 */
274 return "gfs/gfs2";
275 case S_MAGIC_HFS: /* 0x4244 */
276 return "hfs";
277 case S_MAGIC_HPFS: /* 0xF995E849 */
278 return "hpfs";
279 case S_MAGIC_HUGETLBFS: /* 0x958458F6 */
280 return "hugetlbfs";
281 case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA */
282 return "inotifyfs";
283 case S_MAGIC_ISOFS: /* 0x9660 */
284 return "isofs";
285 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
286 return "isofs";
287 case S_MAGIC_ISOFS_WIN: /* 0x4000 */
288 return "isofs";
289 case S_MAGIC_JFFS: /* 0x07C0 */
290 return "jffs";
291 case S_MAGIC_JFFS2: /* 0x72B6 */
292 return "jffs2";
293 case S_MAGIC_JFS: /* 0x3153464A */
294 return "jfs";
295 case S_MAGIC_KAFS: /* 0x6B414653 */
296 return "k-afs";
297 case S_MAGIC_LUSTRE: /* 0x0BD00BD0 */
298 return "lustre";
299 case S_MAGIC_MINIX: /* 0x137F */
300 return "minix";
301 case S_MAGIC_MINIX_30: /* 0x138F */
302 return "minix (30 char.)";
303 case S_MAGIC_MINIX_V2: /* 0x2468 */
304 return "minix v2";
305 case S_MAGIC_MINIX_V2_30: /* 0x2478 */
306 return "minix v2 (30 char.)";
307 case S_MAGIC_MINIX_V3: /* 0x4D5A */
308 return "minix3";
309 case S_MAGIC_MSDOS: /* 0x4D44 */
310 return "msdos";
311 case S_MAGIC_NCP: /* 0x564C */
312 return "novell";
313 case S_MAGIC_NFS: /* 0x6969 */
314 return "nfs";
315 case S_MAGIC_NFSD: /* 0x6E667364 */
316 return "nfsd";
317 case S_MAGIC_NILFS: /* 0x3434 */
318 return "nilfs";
319 case S_MAGIC_NTFS: /* 0x5346544E */
320 return "ntfs";
321 case S_MAGIC_OPENPROM: /* 0x9FA1 */
322 return "openprom";
323 case S_MAGIC_OCFS2: /* 0x7461636f */
324 return "ocfs2";
325 case S_MAGIC_PROC: /* 0x9FA0 */
326 return "proc";
327 case S_MAGIC_QNX4: /* 0x002F */
328 return "qnx4";
329 case S_MAGIC_RAMFS: /* 0x858458F6 */
330 return "ramfs";
331 case S_MAGIC_REISERFS: /* 0x52654973 */
332 return "reiserfs";
333 case S_MAGIC_ROMFS: /* 0x7275 */
334 return "romfs";
335 case S_MAGIC_RPC_PIPEFS: /* 0x67596969 */
336 return "rpc_pipefs";
337 case S_MAGIC_SECURITYFS: /* 0x73636673 */
338 return "securityfs";
339 case S_MAGIC_SELINUX: /* 0xF97CFF8C */
340 return "selinux";
341 case S_MAGIC_SMB: /* 0x517B */
342 return "smb";
343 case S_MAGIC_SOCKFS: /* 0x534F434B */
344 return "sockfs";
345 case S_MAGIC_SQUASHFS: /* 0x73717368 */
346 return "squashfs";
347 case S_MAGIC_SYSFS: /* 0x62656572 */
348 return "sysfs";
349 case S_MAGIC_SYSV2: /* 0x012FF7B6 */
350 return "sysv2";
351 case S_MAGIC_SYSV4: /* 0x012FF7B5 */
352 return "sysv4";
353 case S_MAGIC_TMPFS: /* 0x01021994 */
354 return "tmpfs";
355 case S_MAGIC_UDF: /* 0x15013346 */
356 return "udf";
357 case S_MAGIC_UFS: /* 0x00011954 */
358 return "ufs";
359 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 */
360 return "ufs";
361 case S_MAGIC_USBDEVFS: /* 0x9FA2 */
362 return "usbdevfs";
363 case S_MAGIC_VXFS: /* 0xA501FCF5 */
364 return "vxfs";
365 case S_MAGIC_XENFS: /* 0xABBA1974 */
366 return "xenfs";
367 case S_MAGIC_XENIX: /* 0x012FF7B4 */
368 return "xenix";
369 case S_MAGIC_XFS: /* 0x58465342 */
370 return "xfs";
371 case S_MAGIC_XIAFS: /* 0x012FD16D */
372 return "xia";
374 # elif __GNU__
375 case FSTYPE_UFS:
376 return "ufs";
377 case FSTYPE_NFS:
378 return "nfs";
379 case FSTYPE_GFS:
380 return "gfs";
381 case FSTYPE_LFS:
382 return "lfs";
383 case FSTYPE_SYSV:
384 return "sysv";
385 case FSTYPE_FTP:
386 return "ftp";
387 case FSTYPE_TAR:
388 return "tar";
389 case FSTYPE_AR:
390 return "ar";
391 case FSTYPE_CPIO:
392 return "cpio";
393 case FSTYPE_MSLOSS:
394 return "msloss";
395 case FSTYPE_CPM:
396 return "cpm";
397 case FSTYPE_HFS:
398 return "hfs";
399 case FSTYPE_DTFS:
400 return "dtfs";
401 case FSTYPE_GRFS:
402 return "grfs";
403 case FSTYPE_TERM:
404 return "term";
405 case FSTYPE_DEV:
406 return "dev";
407 case FSTYPE_PROC:
408 return "proc";
409 case FSTYPE_IFSOCK:
410 return "ifsock";
411 case FSTYPE_AFS:
412 return "afs";
413 case FSTYPE_DFS:
414 return "dfs";
415 case FSTYPE_PROC9:
416 return "proc9";
417 case FSTYPE_SOCKET:
418 return "socket";
419 case FSTYPE_MISC:
420 return "misc";
421 case FSTYPE_EXT2FS:
422 return "ext2/ext3";
423 case FSTYPE_HTTP:
424 return "http";
425 case FSTYPE_MEMFS:
426 return "memfs";
427 case FSTYPE_ISO9660:
428 return "iso9660";
429 # endif
430 default:
432 unsigned long int type = statfsbuf->f_type;
433 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
434 + (sizeof type * CHAR_BIT + 3) / 4];
435 sprintf (buf, "UNKNOWN (0x%lx)", type);
436 return buf;
439 #endif
442 static char * ATTRIBUTE_WARN_UNUSED_RESULT
443 human_access (struct stat const *statbuf)
445 static char modebuf[12];
446 filemodestring (statbuf, modebuf);
447 modebuf[10] = 0;
448 return modebuf;
451 static char * ATTRIBUTE_WARN_UNUSED_RESULT
452 human_time (struct timespec t)
454 static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
455 (INT_STRLEN_BOUND (int) /* YYYY */
456 + 1 /* because YYYY might equal INT_MAX + 1900 */
457 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
458 struct tm const *tm = localtime (&t.tv_sec);
459 if (tm == NULL)
460 return timetostr (t.tv_sec, str);
461 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
462 return str;
465 static char * ATTRIBUTE_WARN_UNUSED_RESULT
466 epoch_time (struct timespec t)
468 static char str[INT_STRLEN_BOUND (time_t) + sizeof ".NNNNNNNNN"];
469 /* Note that time_t can technically be a floating point value, such
470 that casting to [u]intmax_t could lose a fractional value or
471 suffer from overflow. However, most porting targets have an
472 integral time_t; also, we know of no file systems that store
473 valid time values outside the bounds of intmax_t even if that
474 value were represented as a floating point. Besides, the cost of
475 converting to struct tm just to use nstrftime (str, len, "%s.%N",
476 tm, 0, t.tv_nsec) is pointless, since nstrftime would have to
477 convert back to seconds as time_t. */
478 if (TYPE_SIGNED (time_t))
479 sprintf (str, "%" PRIdMAX ".%09ld", (intmax_t) t.tv_sec, t.tv_nsec);
480 else
481 sprintf (str, "%" PRIuMAX ".%09ld", (uintmax_t) t.tv_sec, t.tv_nsec);
482 return str;
485 static void
486 out_string (char *pformat, size_t prefix_len, char const *arg)
488 strcpy (pformat + prefix_len, "s");
489 printf (pformat, arg);
491 static void
492 out_int (char *pformat, size_t prefix_len, intmax_t arg)
494 strcpy (pformat + prefix_len, PRIdMAX);
495 printf (pformat, arg);
497 static void
498 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
500 strcpy (pformat + prefix_len, PRIuMAX);
501 printf (pformat, arg);
503 static void
504 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
506 strcpy (pformat + prefix_len, PRIoMAX);
507 printf (pformat, arg);
509 static void
510 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
512 strcpy (pformat + prefix_len, PRIxMAX);
513 printf (pformat, arg);
516 /* Print the context information of FILENAME, and return true iff the
517 context could not be obtained. */
518 static bool ATTRIBUTE_WARN_UNUSED_RESULT
519 out_file_context (char *pformat, size_t prefix_len, char const *filename)
521 char *scontext;
522 bool fail = false;
524 if ((follow_links
525 ? getfilecon (filename, &scontext)
526 : lgetfilecon (filename, &scontext)) < 0)
528 error (0, errno, _("failed to get security context of %s"),
529 quote (filename));
530 scontext = NULL;
531 fail = true;
533 strcpy (pformat + prefix_len, "s");
534 printf (pformat, (scontext ? scontext : "?"));
535 if (scontext)
536 freecon (scontext);
537 return fail;
540 /* Print statfs info. Return zero upon success, nonzero upon failure. */
541 static bool ATTRIBUTE_WARN_UNUSED_RESULT
542 print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
543 void const *data)
545 STRUCT_STATVFS const *statfsbuf = data;
546 bool fail = false;
548 switch (m)
550 case 'n':
551 out_string (pformat, prefix_len, filename);
552 break;
554 case 'i':
556 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
557 uintmax_t fsid = statfsbuf->f_fsid;
558 #else
559 typedef unsigned int fsid_word;
560 verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
561 verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
562 verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
563 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
565 /* Assume a little-endian word order, as that is compatible
566 with glibc's statvfs implementation. */
567 uintmax_t fsid = 0;
568 int words = sizeof statfsbuf->f_fsid / sizeof *p;
569 int i;
570 for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
572 uintmax_t u = p[words - 1 - i];
573 fsid |= u << (i * CHAR_BIT * sizeof *p);
575 #endif
576 out_uint_x (pformat, prefix_len, fsid);
578 break;
580 case 'l':
581 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
582 break;
583 case 't':
584 #if HAVE_STRUCT_STATXFS_F_TYPE
585 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
586 #else
587 fputc ('?', stdout);
588 #endif
589 break;
590 case 'T':
591 out_string (pformat, prefix_len, human_fstype (statfsbuf));
592 break;
593 case 'b':
594 out_int (pformat, prefix_len, statfsbuf->f_blocks);
595 break;
596 case 'f':
597 out_int (pformat, prefix_len, statfsbuf->f_bfree);
598 break;
599 case 'a':
600 out_int (pformat, prefix_len, statfsbuf->f_bavail);
601 break;
602 case 's':
603 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
604 break;
605 case 'S':
607 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
608 if (! frsize)
609 frsize = statfsbuf->f_bsize;
610 out_uint (pformat, prefix_len, frsize);
612 break;
613 case 'c':
614 out_uint (pformat, prefix_len, statfsbuf->f_files);
615 break;
616 case 'd':
617 out_int (pformat, prefix_len, statfsbuf->f_ffree);
618 break;
619 default:
620 fputc ('?', stdout);
621 break;
623 return fail;
626 /* Return any bind mounted source for a path.
627 The caller should not free the returned buffer.
628 Return NULL if no bind mount found. */
629 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
630 find_bind_mount (char const * name)
632 char const * bind_mount = NULL;
634 static struct mount_entry *mount_list;
635 static bool tried_mount_list = false;
636 if (!tried_mount_list) /* attempt/warn once per process. */
638 if (!(mount_list = read_file_system_list (false)))
639 error (0, errno, "%s", _("cannot read table of mounted file systems"));
640 tried_mount_list = true;
643 struct mount_entry *me;
644 for (me = mount_list; me; me = me->me_next)
646 if (me->me_dummy && me->me_devname[0] == '/'
647 && STREQ (me->me_mountdir, name))
649 struct stat name_stats;
650 struct stat dev_stats;
652 if (stat (name, &name_stats) == 0
653 && stat (me->me_devname, &dev_stats) == 0
654 && SAME_INODE (name_stats, dev_stats))
656 bind_mount = me->me_devname;
657 break;
662 return bind_mount;
665 /* Print mount point. Return zero upon success, nonzero upon failure. */
666 static bool ATTRIBUTE_WARN_UNUSED_RESULT
667 out_mount_point (char const *filename, char *pformat, size_t prefix_len,
668 const struct stat *statp)
671 char const *np = "?", *bp = NULL;
672 char *mp = NULL;
673 bool fail = true;
675 /* Look for bind mounts first. Note we output the immediate alias,
676 rather than further resolving to a base device mount point. */
677 if (follow_links || !S_ISLNK (statp->st_mode))
679 char *resolved = canonicalize_file_name (filename);
680 if (!resolved)
682 error (0, errno, _("failed to canonicalize %s"), quote (filename));
683 goto print_mount_point;
685 bp = find_bind_mount (resolved);
686 free (resolved);
687 if (bp)
689 fail = false;
690 goto print_mount_point;
694 /* If there is no direct bind mount, then navigate
695 back up the tree looking for a device change.
696 Note we don't detect if any of the directory components
697 are bind mounted to the same device, but that's OK
698 since we've not directly queried them. */
699 if ((mp = find_mount_point (filename, statp)))
701 /* This dir might be bind mounted to another device,
702 so we resolve the bound source in that case also. */
703 bp = find_bind_mount (mp);
704 fail = false;
707 print_mount_point:
709 out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
710 free (mp);
711 return fail;
714 /* Print stat info. Return zero upon success, nonzero upon failure. */
715 static bool
716 print_stat (char *pformat, size_t prefix_len, char m,
717 char const *filename, void const *data)
719 struct stat *statbuf = (struct stat *) data;
720 struct passwd *pw_ent;
721 struct group *gw_ent;
722 bool fail = false;
724 switch (m)
726 case 'n':
727 out_string (pformat, prefix_len, filename);
728 break;
729 case 'N':
730 out_string (pformat, prefix_len, quote (filename));
731 if (S_ISLNK (statbuf->st_mode))
733 char *linkname = areadlink_with_size (filename, statbuf->st_size);
734 if (linkname == NULL)
736 error (0, errno, _("cannot read symbolic link %s"),
737 quote (filename));
738 return true;
740 printf (" -> ");
741 out_string (pformat, prefix_len, quote (linkname));
742 free (linkname);
744 break;
745 case 'd':
746 out_uint (pformat, prefix_len, statbuf->st_dev);
747 break;
748 case 'D':
749 out_uint_x (pformat, prefix_len, statbuf->st_dev);
750 break;
751 case 'i':
752 out_uint (pformat, prefix_len, statbuf->st_ino);
753 break;
754 case 'a':
755 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
756 break;
757 case 'A':
758 out_string (pformat, prefix_len, human_access (statbuf));
759 break;
760 case 'f':
761 out_uint_x (pformat, prefix_len, statbuf->st_mode);
762 break;
763 case 'F':
764 out_string (pformat, prefix_len, file_type (statbuf));
765 break;
766 case 'h':
767 out_uint (pformat, prefix_len, statbuf->st_nlink);
768 break;
769 case 'u':
770 out_uint (pformat, prefix_len, statbuf->st_uid);
771 break;
772 case 'U':
773 setpwent ();
774 pw_ent = getpwuid (statbuf->st_uid);
775 out_string (pformat, prefix_len,
776 pw_ent ? pw_ent->pw_name : "UNKNOWN");
777 break;
778 case 'g':
779 out_uint (pformat, prefix_len, statbuf->st_gid);
780 break;
781 case 'G':
782 setgrent ();
783 gw_ent = getgrgid (statbuf->st_gid);
784 out_string (pformat, prefix_len,
785 gw_ent ? gw_ent->gr_name : "UNKNOWN");
786 break;
787 case 't':
788 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
789 break;
790 case 'm':
791 fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
792 break;
793 case 'T':
794 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
795 break;
796 case 's':
797 out_uint (pformat, prefix_len, statbuf->st_size);
798 break;
799 case 'B':
800 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
801 break;
802 case 'b':
803 out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
804 break;
805 case 'o':
806 out_uint (pformat, prefix_len, statbuf->st_blksize);
807 break;
808 case 'w':
810 struct timespec t = get_stat_birthtime (statbuf);
811 if (t.tv_nsec < 0)
812 out_string (pformat, prefix_len, "-");
813 else
814 out_string (pformat, prefix_len, human_time (t));
816 break;
817 case 'W':
819 struct timespec t = get_stat_birthtime (statbuf);
820 if (t.tv_nsec < 0)
821 out_string (pformat, prefix_len, "-");
822 else
823 out_string (pformat, prefix_len, epoch_time (t));
825 break;
826 case 'x':
827 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
828 break;
829 case 'X':
830 out_string (pformat, prefix_len, epoch_time (get_stat_atime (statbuf)));
831 break;
832 case 'y':
833 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
834 break;
835 case 'Y':
836 out_string (pformat, prefix_len, epoch_time (get_stat_mtime (statbuf)));
837 break;
838 case 'z':
839 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
840 break;
841 case 'Z':
842 out_string (pformat, prefix_len, epoch_time (get_stat_ctime (statbuf)));
843 break;
844 case 'C':
845 fail |= out_file_context (pformat, prefix_len, filename);
846 break;
847 default:
848 fputc ('?', stdout);
849 break;
851 return fail;
854 /* Output a single-character \ escape. */
856 static void
857 print_esc_char (char c)
859 switch (c)
861 case 'a': /* Alert. */
862 c ='\a';
863 break;
864 case 'b': /* Backspace. */
865 c ='\b';
866 break;
867 case 'e': /* Escape. */
868 c ='\x1B';
869 break;
870 case 'f': /* Form feed. */
871 c ='\f';
872 break;
873 case 'n': /* New line. */
874 c ='\n';
875 break;
876 case 'r': /* Carriage return. */
877 c ='\r';
878 break;
879 case 't': /* Horizontal tab. */
880 c ='\t';
881 break;
882 case 'v': /* Vertical tab. */
883 c ='\v';
884 break;
885 case '"':
886 case '\\':
887 break;
888 default:
889 error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
890 break;
892 putchar (c);
895 /* Print the information specified by the format string, FORMAT,
896 calling PRINT_FUNC for each %-directive encountered.
897 Return zero upon success, nonzero upon failure. */
898 static bool ATTRIBUTE_WARN_UNUSED_RESULT
899 print_it (char const *format, char const *filename,
900 bool (*print_func) (char *, size_t, char, char const *, void const *),
901 void const *data)
903 bool fail = false;
905 /* Add 2 to accommodate our conversion of the stat `%s' format string
906 to the longer printf `%llu' one. */
907 enum
909 MAX_ADDITIONAL_BYTES =
910 (MAX (sizeof PRIdMAX,
911 MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
912 - 1)
914 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
915 char *dest = xmalloc (n_alloc);
916 char const *b;
917 for (b = format; *b; b++)
919 switch (*b)
921 case '%':
923 size_t len = strspn (b + 1, "#-+.I 0123456789");
924 char const *fmt_char = b + len + 1;
925 memcpy (dest, b, len + 1);
927 b = fmt_char;
928 switch (*fmt_char)
930 case '\0':
931 --b;
932 /* fall through */
933 case '%':
934 if (0 < len)
936 dest[len + 1] = *fmt_char;
937 dest[len + 2] = '\0';
938 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
939 quotearg_colon (dest));
941 putchar ('%');
942 break;
943 default:
944 fail |= print_func (dest, len + 1, *fmt_char, filename, data);
945 break;
947 break;
950 case '\\':
951 if ( ! interpret_backslash_escapes)
953 putchar ('\\');
954 break;
956 ++b;
957 if (isodigit (*b))
959 int esc_value = octtobin (*b);
960 int esc_length = 1; /* number of octal digits */
961 for (++b; esc_length < 3 && isodigit (*b);
962 ++esc_length, ++b)
964 esc_value = esc_value * 8 + octtobin (*b);
966 putchar (esc_value);
967 --b;
969 else if (*b == 'x' && isxdigit (to_uchar (b[1])))
971 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
972 /* A hexadecimal \xhh escape sequence must have
973 1 or 2 hex. digits. */
974 ++b;
975 if (isxdigit (to_uchar (b[1])))
977 ++b;
978 esc_value = esc_value * 16 + hextobin (*b);
980 putchar (esc_value);
982 else if (*b == '\0')
984 error (0, 0, _("warning: backslash at end of format"));
985 putchar ('\\');
986 /* Arrange to exit the loop. */
987 --b;
989 else
991 print_esc_char (*b);
993 break;
995 default:
996 putchar (*b);
997 break;
1000 free (dest);
1002 fputs (trailing_delim, stdout);
1004 return fail;
1007 /* Stat the file system and print what we find. */
1008 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1009 do_statfs (char const *filename, bool terse, char const *format)
1011 STRUCT_STATVFS statfsbuf;
1013 if (STREQ (filename, "-"))
1015 error (0, 0, _("using %s to denote standard input does not work"
1016 " in file system mode"), quote (filename));
1017 return false;
1020 if (STATFS (filename, &statfsbuf) != 0)
1022 error (0, errno, _("cannot read file system information for %s"),
1023 quote (filename));
1024 return false;
1027 bool fail = print_it (format, filename, print_statfs, &statfsbuf);
1028 return ! fail;
1031 /* stat the file and print what we find */
1032 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1033 do_stat (char const *filename, bool terse, char const *format,
1034 char const *format2)
1036 struct stat statbuf;
1038 if (STREQ (filename, "-"))
1040 if (fstat (STDIN_FILENO, &statbuf) != 0)
1042 error (0, errno, _("cannot stat standard input"));
1043 return false;
1046 /* We can't use the shorter
1047 (follow_links?stat:lstat) (filename, &statbug)
1048 since stat might be a function-like macro. */
1049 else if ((follow_links
1050 ? stat (filename, &statbuf)
1051 : lstat (filename, &statbuf)) != 0)
1053 error (0, errno, _("cannot stat %s"), quote (filename));
1054 return false;
1057 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
1058 format = format2;
1060 bool fail = print_it (format, filename, print_stat, &statbuf);
1061 return ! fail;
1064 /* Return an allocated format string in static storage that
1065 corresponds to whether FS and TERSE options were declared. */
1066 static char *
1067 default_format (bool fs, bool terse, bool device)
1069 char *format;
1070 if (fs)
1072 if (terse)
1073 format = xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
1074 else
1076 /* TRANSLATORS: This string uses format specifiers from
1077 'stat --help' with --file-system, and NOT from printf. */
1078 format = xstrdup (_("\
1079 File: \"%n\"\n\
1080 ID: %-8i Namelen: %-7l Type: %T\n\
1081 Block size: %-10s Fundamental block size: %S\n\
1082 Blocks: Total: %-10b Free: %-10f Available: %a\n\
1083 Inodes: Total: %-10c Free: %d\n\
1084 "));
1087 else /* ! fs */
1089 if (terse)
1091 if (0 < is_selinux_enabled ())
1092 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1093 " %X %Y %Z %W %o %C\n");
1094 else
1095 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1096 " %X %Y %Z %W %o\n");
1098 else
1100 char *temp;
1101 /* TRANSLATORS: This string uses format specifiers from
1102 'stat --help' without --file-system, and NOT from printf. */
1103 format = xstrdup (_("\
1104 File: %N\n\
1105 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1106 "));
1108 temp = format;
1109 if (device)
1111 /* TRANSLATORS: This string uses format specifiers from
1112 'stat --help' without --file-system, and NOT from printf. */
1113 format = xasprintf ("%s%s", format, _("\
1114 Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
1115 "));
1117 else
1119 /* TRANSLATORS: This string uses format specifiers from
1120 'stat --help' without --file-system, and NOT from printf. */
1121 format = xasprintf ("%s%s", format, _("\
1122 Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1123 "));
1125 free (temp);
1127 temp = format;
1128 /* TRANSLATORS: This string uses format specifiers from
1129 'stat --help' without --file-system, and NOT from printf. */
1130 format = xasprintf ("%s%s", format, _("\
1131 Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1132 "));
1133 free (temp);
1135 if (0 < is_selinux_enabled ())
1137 temp = format;
1138 /* TRANSLATORS: This string uses format specifiers from
1139 'stat --help' without --file-system, and NOT from printf. */
1140 format = xasprintf ("%s%s", format, _("\
1141 Context: %C\n\
1142 "));
1143 free (temp);
1146 temp = format;
1147 /* TRANSLATORS: This string uses format specifiers from
1148 'stat --help' without --file-system, and NOT from printf. */
1149 format = xasprintf ("%s%s", format, _("\
1150 Access: %x\n\
1151 Modify: %y\n\
1152 Change: %z\n\
1153 Birth: %w\n\
1154 "));
1155 free (temp);
1158 return format;
1161 void
1162 usage (int status)
1164 if (status != EXIT_SUCCESS)
1165 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1166 program_name);
1167 else
1169 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
1170 fputs (_("\
1171 Display file or file system status.\n\
1173 -L, --dereference follow links\n\
1174 -f, --file-system display file system status instead of file status\n\
1175 "), stdout);
1176 fputs (_("\
1177 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1178 output a newline after each use of FORMAT\n\
1179 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1180 and do not output a mandatory trailing newline.\n\
1181 If you want a newline, include \\n in FORMAT\n\
1182 -t, --terse print the information in terse form\n\
1183 "), stdout);
1184 fputs (HELP_OPTION_DESCRIPTION, stdout);
1185 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1187 fputs (_("\n\
1188 The valid format sequences for files (without --file-system):\n\
1190 %a Access rights in octal\n\
1191 %A Access rights in human readable form\n\
1192 %b Number of blocks allocated (see %B)\n\
1193 %B The size in bytes of each block reported by %b\n\
1194 %C SELinux security context string\n\
1195 "), stdout);
1196 fputs (_("\
1197 %d Device number in decimal\n\
1198 %D Device number in hex\n\
1199 %f Raw mode in hex\n\
1200 %F File type\n\
1201 %g Group ID of owner\n\
1202 %G Group name of owner\n\
1203 "), stdout);
1204 fputs (_("\
1205 %h Number of hard links\n\
1206 %i Inode number\n\
1207 %m Mount point\n\
1208 %n File name\n\
1209 %N Quoted file name with dereference if symbolic link\n\
1210 %o I/O block size\n\
1211 %s Total size, in bytes\n\
1212 %t Major device type in hex\n\
1213 %T Minor device type in hex\n\
1214 "), stdout);
1215 fputs (_("\
1216 %u User ID of owner\n\
1217 %U User name of owner\n\
1218 %w Time of file birth, or - if unknown\n\
1219 %W Time of file birth as seconds since Epoch, or - if unknown\n\
1220 %x Time of last access\n\
1221 %X Time of last access as seconds since Epoch\n\
1222 %y Time of last modification\n\
1223 %Y Time of last modification as seconds since Epoch\n\
1224 %z Time of last change\n\
1225 %Z Time of last change as seconds since Epoch\n\
1227 "), stdout);
1229 fputs (_("\
1230 Valid format sequences for file systems:\n\
1232 %a Free blocks available to non-superuser\n\
1233 %b Total data blocks in file system\n\
1234 %c Total file nodes in file system\n\
1235 %d Free file nodes in file system\n\
1236 %f Free blocks in file system\n\
1237 "), stdout);
1238 fputs (_("\
1239 %i File System ID in hex\n\
1240 %l Maximum length of filenames\n\
1241 %n File name\n\
1242 %s Block size (for faster transfers)\n\
1243 %S Fundamental block size (for block counts)\n\
1244 %t Type in hex\n\
1245 %T Type in human readable form\n\
1246 "), stdout);
1247 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1248 emit_ancillary_info ();
1250 exit (status);
1254 main (int argc, char *argv[])
1256 int c;
1257 int i;
1258 bool fs = false;
1259 bool terse = false;
1260 char *format = NULL;
1261 char *format2;
1262 bool ok = true;
1264 initialize_main (&argc, &argv);
1265 set_program_name (argv[0]);
1266 setlocale (LC_ALL, "");
1267 bindtextdomain (PACKAGE, LOCALEDIR);
1268 textdomain (PACKAGE);
1270 atexit (close_stdout);
1272 while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
1274 switch (c)
1276 case PRINTF_OPTION:
1277 format = optarg;
1278 interpret_backslash_escapes = true;
1279 trailing_delim = "";
1280 break;
1282 case 'c':
1283 format = optarg;
1284 interpret_backslash_escapes = false;
1285 trailing_delim = "\n";
1286 break;
1288 case 'L':
1289 follow_links = true;
1290 break;
1292 case 'f':
1293 fs = true;
1294 break;
1296 case 't':
1297 terse = true;
1298 break;
1300 case_GETOPT_HELP_CHAR;
1302 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1304 default:
1305 usage (EXIT_FAILURE);
1309 if (argc == optind)
1311 error (0, 0, _("missing operand"));
1312 usage (EXIT_FAILURE);
1315 if (format)
1316 format2 = format;
1317 else
1319 format = default_format (fs, terse, false);
1320 format2 = default_format (fs, terse, true);
1323 for (i = optind; i < argc; i++)
1324 ok &= (fs
1325 ? do_statfs (argv[i], terse, format)
1326 : do_stat (argv[i], terse, format, format2));
1328 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);