stat: adjust the printing of SELinux context
[coreutils/ericb.git] / src / stat.c
blobf742f1ed315f7bedcbe57329172f4e8e7fbefcc4
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 /* Very specialized function (modifies FORMAT), just so as to avoid
517 duplicating this code between both print_statfs and print_stat.
518 Return zero upon success, nonzero upon failure. */
519 static bool ATTRIBUTE_WARN_UNUSED_RESULT
520 out_file_context (char const *filename, char *pformat, size_t prefix_len)
522 char *scontext;
523 bool fail = false;
525 if ((follow_links
526 ? getfilecon (filename, &scontext)
527 : lgetfilecon (filename, &scontext)) < 0)
529 error (0, errno, _("failed to get security context of %s"),
530 quote (filename));
531 scontext = NULL;
532 fail = true;
534 strcpy (pformat + prefix_len, "s");
535 printf (pformat, (scontext ? scontext : "?"));
536 if (scontext)
537 freecon (scontext);
538 return fail;
541 /* Print statfs info. Return zero upon success, nonzero upon failure. */
542 static bool ATTRIBUTE_WARN_UNUSED_RESULT
543 print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
544 void const *data)
546 STRUCT_STATVFS const *statfsbuf = data;
547 bool fail = false;
549 switch (m)
551 case 'n':
552 out_string (pformat, prefix_len, filename);
553 break;
555 case 'i':
557 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
558 uintmax_t fsid = statfsbuf->f_fsid;
559 #else
560 typedef unsigned int fsid_word;
561 verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
562 verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
563 verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
564 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
566 /* Assume a little-endian word order, as that is compatible
567 with glibc's statvfs implementation. */
568 uintmax_t fsid = 0;
569 int words = sizeof statfsbuf->f_fsid / sizeof *p;
570 int i;
571 for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
573 uintmax_t u = p[words - 1 - i];
574 fsid |= u << (i * CHAR_BIT * sizeof *p);
576 #endif
577 out_uint_x (pformat, prefix_len, fsid);
579 break;
581 case 'l':
582 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
583 break;
584 case 't':
585 #if HAVE_STRUCT_STATXFS_F_TYPE
586 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
587 #else
588 fputc ('?', stdout);
589 #endif
590 break;
591 case 'T':
592 out_string (pformat, prefix_len, human_fstype (statfsbuf));
593 break;
594 case 'b':
595 out_int (pformat, prefix_len, statfsbuf->f_blocks);
596 break;
597 case 'f':
598 out_int (pformat, prefix_len, statfsbuf->f_bfree);
599 break;
600 case 'a':
601 out_int (pformat, prefix_len, statfsbuf->f_bavail);
602 break;
603 case 's':
604 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
605 break;
606 case 'S':
608 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
609 if (! frsize)
610 frsize = statfsbuf->f_bsize;
611 out_uint (pformat, prefix_len, frsize);
613 break;
614 case 'c':
615 out_uint (pformat, prefix_len, statfsbuf->f_files);
616 break;
617 case 'd':
618 out_int (pformat, prefix_len, statfsbuf->f_ffree);
619 break;
620 case 'C':
621 fail |= out_file_context (filename, pformat, prefix_len);
622 break;
623 default:
624 fputc ('?', stdout);
625 break;
627 return fail;
630 /* Return any bind mounted source for a path.
631 The caller should not free the returned buffer.
632 Return NULL if no bind mount found. */
633 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
634 find_bind_mount (char const * name)
636 char const * bind_mount = NULL;
638 static struct mount_entry *mount_list;
639 static bool tried_mount_list = false;
640 if (!tried_mount_list) /* attempt/warn once per process. */
642 if (!(mount_list = read_file_system_list (false)))
643 error (0, errno, "%s", _("cannot read table of mounted file systems"));
644 tried_mount_list = true;
647 struct mount_entry *me;
648 for (me = mount_list; me; me = me->me_next)
650 if (me->me_dummy && me->me_devname[0] == '/'
651 && STREQ (me->me_mountdir, name))
653 struct stat name_stats;
654 struct stat dev_stats;
656 if (stat (name, &name_stats) == 0
657 && stat (me->me_devname, &dev_stats) == 0
658 && SAME_INODE (name_stats, dev_stats))
660 bind_mount = me->me_devname;
661 break;
666 return bind_mount;
669 /* Print mount point. Return zero upon success, nonzero upon failure. */
670 static bool ATTRIBUTE_WARN_UNUSED_RESULT
671 out_mount_point (char const *filename, char *pformat, size_t prefix_len,
672 const struct stat *statp)
675 char const *np = "?", *bp = NULL;
676 char *mp = NULL;
677 bool fail = true;
679 /* Look for bind mounts first. Note we output the immediate alias,
680 rather than further resolving to a base device mount point. */
681 if (follow_links || !S_ISLNK (statp->st_mode))
683 char *resolved = canonicalize_file_name (filename);
684 if (!resolved)
686 error (0, errno, _("failed to canonicalize %s"), quote (filename));
687 goto print_mount_point;
689 bp = find_bind_mount (resolved);
690 free (resolved);
691 if (bp)
693 fail = false;
694 goto print_mount_point;
698 /* If there is no direct bind mount, then navigate
699 back up the tree looking for a device change.
700 Note we don't detect if any of the directory components
701 are bind mounted to the same device, but that's OK
702 since we've not directly queried them. */
703 if ((mp = find_mount_point (filename, statp)))
705 /* This dir might be bind mounted to another device,
706 so we resolve the bound source in that case also. */
707 bp = find_bind_mount (mp);
708 fail = false;
711 print_mount_point:
713 out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
714 free (mp);
715 return fail;
718 /* Print stat info. Return zero upon success, nonzero upon failure. */
719 static bool
720 print_stat (char *pformat, size_t prefix_len, char m,
721 char const *filename, void const *data)
723 struct stat *statbuf = (struct stat *) data;
724 struct passwd *pw_ent;
725 struct group *gw_ent;
726 bool fail = false;
728 switch (m)
730 case 'n':
731 out_string (pformat, prefix_len, filename);
732 break;
733 case 'N':
734 out_string (pformat, prefix_len, quote (filename));
735 if (S_ISLNK (statbuf->st_mode))
737 char *linkname = areadlink_with_size (filename, statbuf->st_size);
738 if (linkname == NULL)
740 error (0, errno, _("cannot read symbolic link %s"),
741 quote (filename));
742 return true;
744 printf (" -> ");
745 out_string (pformat, prefix_len, quote (linkname));
746 free (linkname);
748 break;
749 case 'd':
750 out_uint (pformat, prefix_len, statbuf->st_dev);
751 break;
752 case 'D':
753 out_uint_x (pformat, prefix_len, statbuf->st_dev);
754 break;
755 case 'i':
756 out_uint (pformat, prefix_len, statbuf->st_ino);
757 break;
758 case 'a':
759 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
760 break;
761 case 'A':
762 out_string (pformat, prefix_len, human_access (statbuf));
763 break;
764 case 'f':
765 out_uint_x (pformat, prefix_len, statbuf->st_mode);
766 break;
767 case 'F':
768 out_string (pformat, prefix_len, file_type (statbuf));
769 break;
770 case 'h':
771 out_uint (pformat, prefix_len, statbuf->st_nlink);
772 break;
773 case 'u':
774 out_uint (pformat, prefix_len, statbuf->st_uid);
775 break;
776 case 'U':
777 setpwent ();
778 pw_ent = getpwuid (statbuf->st_uid);
779 out_string (pformat, prefix_len,
780 pw_ent ? pw_ent->pw_name : "UNKNOWN");
781 break;
782 case 'g':
783 out_uint (pformat, prefix_len, statbuf->st_gid);
784 break;
785 case 'G':
786 setgrent ();
787 gw_ent = getgrgid (statbuf->st_gid);
788 out_string (pformat, prefix_len,
789 gw_ent ? gw_ent->gr_name : "UNKNOWN");
790 break;
791 case 't':
792 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
793 break;
794 case 'm':
795 fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
796 break;
797 case 'T':
798 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
799 break;
800 case 's':
801 out_uint (pformat, prefix_len, statbuf->st_size);
802 break;
803 case 'B':
804 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
805 break;
806 case 'b':
807 out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
808 break;
809 case 'o':
810 out_uint (pformat, prefix_len, statbuf->st_blksize);
811 break;
812 case 'w':
814 struct timespec t = get_stat_birthtime (statbuf);
815 if (t.tv_nsec < 0)
816 out_string (pformat, prefix_len, "-");
817 else
818 out_string (pformat, prefix_len, human_time (t));
820 break;
821 case 'W':
823 struct timespec t = get_stat_birthtime (statbuf);
824 if (t.tv_nsec < 0)
825 out_string (pformat, prefix_len, "-");
826 else
827 out_string (pformat, prefix_len, epoch_time (t));
829 break;
830 case 'x':
831 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
832 break;
833 case 'X':
834 out_string (pformat, prefix_len, epoch_time (get_stat_atime (statbuf)));
835 break;
836 case 'y':
837 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
838 break;
839 case 'Y':
840 out_string (pformat, prefix_len, epoch_time (get_stat_mtime (statbuf)));
841 break;
842 case 'z':
843 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
844 break;
845 case 'Z':
846 out_string (pformat, prefix_len, epoch_time (get_stat_ctime (statbuf)));
847 break;
848 case 'C':
849 fail |= out_file_context (filename, pformat, prefix_len);
850 break;
851 default:
852 fputc ('?', stdout);
853 break;
855 return fail;
858 /* Output a single-character \ escape. */
860 static void
861 print_esc_char (char c)
863 switch (c)
865 case 'a': /* Alert. */
866 c ='\a';
867 break;
868 case 'b': /* Backspace. */
869 c ='\b';
870 break;
871 case 'e': /* Escape. */
872 c ='\x1B';
873 break;
874 case 'f': /* Form feed. */
875 c ='\f';
876 break;
877 case 'n': /* New line. */
878 c ='\n';
879 break;
880 case 'r': /* Carriage return. */
881 c ='\r';
882 break;
883 case 't': /* Horizontal tab. */
884 c ='\t';
885 break;
886 case 'v': /* Vertical tab. */
887 c ='\v';
888 break;
889 case '"':
890 case '\\':
891 break;
892 default:
893 error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
894 break;
896 putchar (c);
899 /* Print the information specified by the format string, FORMAT,
900 calling PRINT_FUNC for each %-directive encountered.
901 Return zero upon success, nonzero upon failure. */
902 static bool ATTRIBUTE_WARN_UNUSED_RESULT
903 print_it (char const *format, char const *filename,
904 bool (*print_func) (char *, size_t, char, char const *, void const *),
905 void const *data)
907 bool fail = false;
909 /* Add 2 to accommodate our conversion of the stat `%s' format string
910 to the longer printf `%llu' one. */
911 enum
913 MAX_ADDITIONAL_BYTES =
914 (MAX (sizeof PRIdMAX,
915 MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
916 - 1)
918 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
919 char *dest = xmalloc (n_alloc);
920 char const *b;
921 for (b = format; *b; b++)
923 switch (*b)
925 case '%':
927 size_t len = strspn (b + 1, "#-+.I 0123456789");
928 char const *fmt_char = b + len + 1;
929 memcpy (dest, b, len + 1);
931 b = fmt_char;
932 switch (*fmt_char)
934 case '\0':
935 --b;
936 /* fall through */
937 case '%':
938 if (0 < len)
940 dest[len + 1] = *fmt_char;
941 dest[len + 2] = '\0';
942 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
943 quotearg_colon (dest));
945 putchar ('%');
946 break;
947 default:
948 fail |= print_func (dest, len + 1, *fmt_char, filename, data);
949 break;
951 break;
954 case '\\':
955 if ( ! interpret_backslash_escapes)
957 putchar ('\\');
958 break;
960 ++b;
961 if (isodigit (*b))
963 int esc_value = octtobin (*b);
964 int esc_length = 1; /* number of octal digits */
965 for (++b; esc_length < 3 && isodigit (*b);
966 ++esc_length, ++b)
968 esc_value = esc_value * 8 + octtobin (*b);
970 putchar (esc_value);
971 --b;
973 else if (*b == 'x' && isxdigit (to_uchar (b[1])))
975 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
976 /* A hexadecimal \xhh escape sequence must have
977 1 or 2 hex. digits. */
978 ++b;
979 if (isxdigit (to_uchar (b[1])))
981 ++b;
982 esc_value = esc_value * 16 + hextobin (*b);
984 putchar (esc_value);
986 else if (*b == '\0')
988 error (0, 0, _("warning: backslash at end of format"));
989 putchar ('\\');
990 /* Arrange to exit the loop. */
991 --b;
993 else
995 print_esc_char (*b);
997 break;
999 default:
1000 putchar (*b);
1001 break;
1004 free (dest);
1006 fputs (trailing_delim, stdout);
1008 return fail;
1011 /* Stat the file system and print what we find. */
1012 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1013 do_statfs (char const *filename, bool terse, char const *format)
1015 STRUCT_STATVFS statfsbuf;
1017 if (STREQ (filename, "-"))
1019 error (0, 0, _("using %s to denote standard input does not work"
1020 " in file system mode"), quote (filename));
1021 return false;
1024 if (STATFS (filename, &statfsbuf) != 0)
1026 error (0, errno, _("cannot read file system information for %s"),
1027 quote (filename));
1028 return false;
1031 bool fail = print_it (format, filename, print_statfs, &statfsbuf);
1032 return ! fail;
1035 /* stat the file and print what we find */
1036 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1037 do_stat (char const *filename, bool terse, char const *format,
1038 char const *format2)
1040 struct stat statbuf;
1042 if (STREQ (filename, "-"))
1044 if (fstat (STDIN_FILENO, &statbuf) != 0)
1046 error (0, errno, _("cannot stat standard input"));
1047 return false;
1050 /* We can't use the shorter
1051 (follow_links?stat:lstat) (filename, &statbug)
1052 since stat might be a function-like macro. */
1053 else if ((follow_links
1054 ? stat (filename, &statbuf)
1055 : lstat (filename, &statbuf)) != 0)
1057 error (0, errno, _("cannot stat %s"), quote (filename));
1058 return false;
1061 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
1062 format = format2;
1064 bool fail = print_it (format, filename, print_stat, &statbuf);
1065 return ! fail;
1068 /* Return an allocated format string in static storage that
1069 corresponds to whether FS and TERSE options were declared. */
1070 static char *
1071 default_format (bool fs, bool terse, bool device)
1073 char *format;
1074 if (fs)
1076 if (terse)
1077 format = xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
1078 else
1080 /* TRANSLATORS: This string uses format specifiers from
1081 'stat --help' with --file-system, and NOT from printf. */
1082 format = xstrdup (_("\
1083 File: \"%n\"\n\
1084 ID: %-8i Namelen: %-7l Type: %T\n\
1085 Block size: %-10s Fundamental block size: %S\n\
1086 Blocks: Total: %-10b Free: %-10f Available: %a\n\
1087 Inodes: Total: %-10c Free: %d\n\
1088 "));
1091 else /* ! fs */
1093 if (terse)
1095 if (0 < is_selinux_enabled ())
1096 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1097 " %X %Y %Z %W %o %C\n");
1098 else
1099 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1100 " %X %Y %Z %W %o\n");
1102 else
1104 char *temp;
1105 /* TRANSLATORS: This string uses format specifiers from
1106 'stat --help' without --file-system, and NOT from printf. */
1107 format = xstrdup (_("\
1108 File: %N\n\
1109 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1110 "));
1112 temp = format;
1113 if (device)
1115 /* TRANSLATORS: This string uses format specifiers from
1116 'stat --help' without --file-system, and NOT from printf. */
1117 format = xasprintf ("%s%s", format, _("\
1118 Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
1119 "));
1121 else
1123 /* TRANSLATORS: This string uses format specifiers from
1124 'stat --help' without --file-system, and NOT from printf. */
1125 format = xasprintf ("%s%s", format, _("\
1126 Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1127 "));
1129 free (temp);
1131 temp = format;
1132 /* TRANSLATORS: This string uses format specifiers from
1133 'stat --help' without --file-system, and NOT from printf. */
1134 format = xasprintf ("%s%s", format, _("\
1135 Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1136 "));
1137 free (temp);
1139 if (0 < is_selinux_enabled ())
1141 temp = format;
1142 /* TRANSLATORS: This string uses format specifiers from
1143 'stat --help' without --file-system, and NOT from printf. */
1144 format = xasprintf ("%s%s", format, _("\
1145 Context: %C\n\
1146 "));
1147 free (temp);
1150 temp = format;
1151 /* TRANSLATORS: This string uses format specifiers from
1152 'stat --help' without --file-system, and NOT from printf. */
1153 format = xasprintf ("%s%s", format, _("\
1154 Access: %x\n\
1155 Modify: %y\n\
1156 Change: %z\n\
1157 Birth: %w\n\
1158 "));
1159 free (temp);
1162 return format;
1165 void
1166 usage (int status)
1168 if (status != EXIT_SUCCESS)
1169 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1170 program_name);
1171 else
1173 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
1174 fputs (_("\
1175 Display file or file system status.\n\
1177 -L, --dereference follow links\n\
1178 -f, --file-system display file system status instead of file status\n\
1179 "), stdout);
1180 fputs (_("\
1181 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1182 output a newline after each use of FORMAT\n\
1183 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1184 and do not output a mandatory trailing newline.\n\
1185 If you want a newline, include \\n in FORMAT\n\
1186 -t, --terse print the information in terse form\n\
1187 "), stdout);
1188 fputs (HELP_OPTION_DESCRIPTION, stdout);
1189 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1191 fputs (_("\n\
1192 The valid format sequences for files (without --file-system):\n\
1194 %a Access rights in octal\n\
1195 %A Access rights in human readable form\n\
1196 %b Number of blocks allocated (see %B)\n\
1197 %B The size in bytes of each block reported by %b\n\
1198 %C SELinux security context string\n\
1199 "), stdout);
1200 fputs (_("\
1201 %d Device number in decimal\n\
1202 %D Device number in hex\n\
1203 %f Raw mode in hex\n\
1204 %F File type\n\
1205 %g Group ID of owner\n\
1206 %G Group name of owner\n\
1207 "), stdout);
1208 fputs (_("\
1209 %h Number of hard links\n\
1210 %i Inode number\n\
1211 %m Mount point\n\
1212 %n File name\n\
1213 %N Quoted file name with dereference if symbolic link\n\
1214 %o I/O block size\n\
1215 %s Total size, in bytes\n\
1216 %t Major device type in hex\n\
1217 %T Minor device type in hex\n\
1218 "), stdout);
1219 fputs (_("\
1220 %u User ID of owner\n\
1221 %U User name of owner\n\
1222 %w Time of file birth, or - if unknown\n\
1223 %W Time of file birth as seconds since Epoch, or - if unknown\n\
1224 %x Time of last access\n\
1225 %X Time of last access as seconds since Epoch\n\
1226 %y Time of last modification\n\
1227 %Y Time of last modification as seconds since Epoch\n\
1228 %z Time of last change\n\
1229 %Z Time of last change as seconds since Epoch\n\
1231 "), stdout);
1233 fputs (_("\
1234 Valid format sequences for file systems:\n\
1236 %a Free blocks available to non-superuser\n\
1237 %b Total data blocks in file system\n\
1238 %c Total file nodes in file system\n\
1239 %d Free file nodes in file system\n\
1240 %f Free blocks in file system\n\
1241 %C SELinux security context string\n\
1242 "), stdout);
1243 fputs (_("\
1244 %i File System ID in hex\n\
1245 %l Maximum length of filenames\n\
1246 %n File name\n\
1247 %s Block size (for faster transfers)\n\
1248 %S Fundamental block size (for block counts)\n\
1249 %t Type in hex\n\
1250 %T Type in human readable form\n\
1251 "), stdout);
1252 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1253 emit_ancillary_info ();
1255 exit (status);
1259 main (int argc, char *argv[])
1261 int c;
1262 int i;
1263 bool fs = false;
1264 bool terse = false;
1265 char *format = NULL;
1266 char *format2;
1267 bool ok = true;
1269 initialize_main (&argc, &argv);
1270 set_program_name (argv[0]);
1271 setlocale (LC_ALL, "");
1272 bindtextdomain (PACKAGE, LOCALEDIR);
1273 textdomain (PACKAGE);
1275 atexit (close_stdout);
1277 while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
1279 switch (c)
1281 case PRINTF_OPTION:
1282 format = optarg;
1283 interpret_backslash_escapes = true;
1284 trailing_delim = "";
1285 break;
1287 case 'c':
1288 format = optarg;
1289 interpret_backslash_escapes = false;
1290 trailing_delim = "\n";
1291 break;
1293 case 'L':
1294 follow_links = true;
1295 break;
1297 case 'f':
1298 fs = true;
1299 break;
1301 case 't':
1302 terse = true;
1303 break;
1305 case_GETOPT_HELP_CHAR;
1307 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1309 default:
1310 usage (EXIT_FAILURE);
1314 if (argc == optind)
1316 error (0, 0, _("missing operand"));
1317 usage (EXIT_FAILURE);
1320 if (format)
1321 format2 = format;
1322 else
1324 format = default_format (fs, terse, false);
1325 format2 = default_format (fs, terse, true);
1328 for (i = optind; i < argc; i++)
1329 ok &= (fs
1330 ? do_statfs (argv[i], terse, format)
1331 : do_stat (argv[i], terse, format, format2));
1333 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);