1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2012 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. */
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
28 # define USE_STATVFS 0
34 #include <sys/types.h>
38 # include <sys/statvfs.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>
54 #elif HAVE_OS_H /* BeOS */
57 #include <selinux/selinux.h>
61 #include "areadlink.h"
63 #include "file-type.h"
67 #include "mountlist.h"
70 #include "stat-size.h"
71 #include "stat-time.h"
73 #include "find-mount-point.h"
74 #include "xvasprintf.h"
77 # define STRUCT_STATVFS struct 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)
83 # if ! STAT_STATVFS && STAT_STATVFS64
84 # define STATFS statvfs64
86 # define STATFS statvfs
88 # define STATFS_FRSIZE(S) ((S)->f_frsize)
90 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
91 # if HAVE_STRUCT_STATFS_F_NAMELEN
92 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
94 # define STATFS statfs
95 # if HAVE_OS_H /* BeOS */
96 /* BeOS has a statvfs function, but it does not return sensible values
97 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
98 f_fstypename. Use 'struct fs_info' instead. */
99 static int ATTRIBUTE_WARN_UNUSED_RESULT
100 statfs (char const *filename, struct fs_info *buf)
102 dev_t device = dev_for_path (filename);
105 errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
106 : device == B_BAD_VALUE ? EINVAL
107 : device == B_NAME_TOO_LONG ? ENAMETOOLONG
108 : device == B_NO_MEMORY ? ENOMEM
109 : device == B_FILE_ERROR ? EIO
113 /* If successful, buf->dev will be == device. */
114 return fs_stat_dev (device, buf);
117 # define f_blocks total_blocks
118 # define f_bfree free_blocks
119 # define f_bavail free_blocks
120 # define f_bsize io_size
121 # define f_files total_nodes
122 # define f_ffree free_nodes
123 # define STRUCT_STATVFS struct fs_info
124 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
125 # define STATFS_FRSIZE(S) ((S)->block_size)
127 # define STRUCT_STATVFS struct statfs
128 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
129 # if HAVE_STRUCT_STATFS_F_FRSIZE
130 # define STATFS_FRSIZE(S) ((S)->f_frsize)
132 # define STATFS_FRSIZE(S) 0
138 # define OUT_NAMEMAX out_uint
140 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */
141 # define SB_F_NAMEMAX(S) "*"
142 # define OUT_NAMEMAX out_string
145 #if HAVE_STRUCT_STATVFS_F_BASETYPE
146 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
148 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
149 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
150 # elif HAVE_OS_H /* BeOS */
151 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
155 /* FIXME: these are used by printf.c, too */
156 #define isodigit(c) ('0' <= (c) && (c) <= '7')
157 #define octtobin(c) ((c) - '0')
158 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
159 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
161 static char const digits[] = "0123456789";
163 /* Flags that are portable for use in printf, for at least one
164 conversion specifier; make_format removes unportable flags as
165 needed for particular specifiers. The glibc 2.2 extension "I" is
166 listed here; it is removed by make_format because it has undefined
167 behavior elsewhere and because it is incompatible with
169 static char const printf_flags[] = "'-+ #0I";
171 #define PROGRAM_NAME "stat"
173 #define AUTHORS proper_name ("Michael Meskes")
177 PRINTF_OPTION = CHAR_MAX + 1
180 static struct option const long_options[] =
182 {"context", no_argument, 0, 'Z'},
183 {"dereference", no_argument, NULL, 'L'},
184 {"file-system", no_argument, NULL, 'f'},
185 {"format", required_argument, NULL, 'c'},
186 {"printf", required_argument, NULL, PRINTF_OPTION},
187 {"terse", no_argument, NULL, 't'},
188 {GETOPT_HELP_OPTION_DECL},
189 {GETOPT_VERSION_OPTION_DECL},
193 /* Whether to follow symbolic links; True for --dereference (-L). */
194 static bool follow_links;
196 /* Whether to interpret backslash-escape sequences.
197 True for --printf=FMT, not for --format=FMT (-c). */
198 static bool interpret_backslash_escapes;
200 /* The trailing delimiter string:
201 "" for --printf=FMT, "\n" for --format=FMT (-c). */
202 static char const *trailing_delim = "";
204 /* The representation of the decimal point in the current locale. */
205 static char const *decimal_point;
206 static size_t decimal_point_len;
208 /* Return the type of the specified file system.
209 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
210 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
211 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
212 Still others have neither and have to get by with f_type (GNU/Linux).
213 But f_type may only exist in statfs (Cygwin). */
214 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
215 human_fstype (STRUCT_STATVFS const *statfsbuf)
217 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
218 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
220 switch (statfsbuf->f_type)
222 # if defined __linux__
224 /* Compare with what's in libc:
225 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
226 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
227 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
228 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
230 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
231 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
233 diff -u sym_stat sym_libc
236 /* Also compare with the list in "man 2 statfs" using the
237 fs-magic-compare make target. */
239 /* IMPORTANT NOTE: Each of the following 'case S_MAGIC_...:'
240 statements must be followed by a hexadecimal constant in
241 a comment. The S_MAGIC_... name and constant are automatically
242 combined to produce the #define directives in fs.h. */
244 case S_MAGIC_ADFS: /* 0xADF5 local */
246 case S_MAGIC_AFFS: /* 0xADFF local */
248 case S_MAGIC_AFS: /* 0x5346414F remote */
250 case S_MAGIC_ANON_INODE_FS: /* 0x09041934 local */
251 return "anon-inode FS";
252 case S_MAGIC_AUFS: /* 0x61756673 remote */
253 /* FIXME: change syntax or add an optional attribute like "inotify:no".
254 The above is labeled as "remote" so that tail always uses polling,
255 but this isn't really a remote file system type. */
257 case S_MAGIC_AUTOFS: /* 0x0187 local */
259 case S_MAGIC_BEFS: /* 0x42465331 local */
261 case S_MAGIC_BDEVFS: /* 0x62646576 local */
263 case S_MAGIC_BFS: /* 0x1BADFACE local */
265 case S_MAGIC_BINFMTFS: /* 0x42494E4D local */
266 return "binfmt_misc";
267 case S_MAGIC_BTRFS: /* 0x9123683E local */
269 case S_MAGIC_CEPH: /* 0x00C36400 remote */
271 case S_MAGIC_CGROUP: /* 0x0027E0EB local */
273 case S_MAGIC_CIFS: /* 0xFF534D42 remote */
275 case S_MAGIC_CODA: /* 0x73757245 remote */
277 case S_MAGIC_COH: /* 0x012FF7B7 local */
279 case S_MAGIC_CRAMFS: /* 0x28CD3D45 local */
281 case S_MAGIC_CRAMFS_WEND: /* 0x453DCD28 local */
282 return "cramfs-wend";
283 case S_MAGIC_DEBUGFS: /* 0x64626720 local */
285 case S_MAGIC_DEVFS: /* 0x1373 local */
287 case S_MAGIC_DEVPTS: /* 0x1CD1 local */
289 case S_MAGIC_ECRYPTFS: /* 0xF15F local */
291 case S_MAGIC_EFS: /* 0x00414A53 local */
293 case S_MAGIC_EXT: /* 0x137D local */
295 case S_MAGIC_EXT2: /* 0xEF53 local */
297 case S_MAGIC_EXT2_OLD: /* 0xEF51 local */
299 case S_MAGIC_FAT: /* 0x4006 local */
301 case S_MAGIC_FHGFS: /* 0x19830326 remote */
303 case S_MAGIC_FUSEBLK: /* 0x65735546 remote */
305 case S_MAGIC_FUSECTL: /* 0x65735543 remote */
307 case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA local */
309 case S_MAGIC_GFS: /* 0x1161970 remote */
311 case S_MAGIC_GPFS: /* 0x47504653 remote */
313 case S_MAGIC_HFS: /* 0x4244 local */
315 case S_MAGIC_HPFS: /* 0xF995E849 local */
317 case S_MAGIC_HUGETLBFS: /* 0x958458F6 local */
319 case S_MAGIC_MTD_INODE_FS: /* 0x11307854 local */
321 case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA local */
323 case S_MAGIC_ISOFS: /* 0x9660 local */
325 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 local */
327 case S_MAGIC_ISOFS_WIN: /* 0x4000 local */
329 case S_MAGIC_JFFS: /* 0x07C0 local */
331 case S_MAGIC_JFFS2: /* 0x72B6 local */
333 case S_MAGIC_JFS: /* 0x3153464A local */
335 case S_MAGIC_KAFS: /* 0x6B414653 remote */
337 case S_MAGIC_LUSTRE: /* 0x0BD00BD0 remote */
339 case S_MAGIC_MINIX: /* 0x137F local */
341 case S_MAGIC_MINIX_30: /* 0x138F local */
342 return "minix (30 char.)";
343 case S_MAGIC_MINIX_V2: /* 0x2468 local */
345 case S_MAGIC_MINIX_V2_30: /* 0x2478 local */
346 return "minix v2 (30 char.)";
347 case S_MAGIC_MINIX_V3: /* 0x4D5A local */
349 case S_MAGIC_MQUEUE: /* 0x19800202 local */
351 case S_MAGIC_MSDOS: /* 0x4D44 local */
353 case S_MAGIC_NCP: /* 0x564C remote */
355 case S_MAGIC_NFS: /* 0x6969 remote */
357 case S_MAGIC_NFSD: /* 0x6E667364 remote */
359 case S_MAGIC_NILFS: /* 0x3434 local */
361 case S_MAGIC_NTFS: /* 0x5346544E local */
363 case S_MAGIC_OPENPROM: /* 0x9FA1 local */
365 case S_MAGIC_OCFS2: /* 0x7461636f remote */
367 case S_MAGIC_PANFS: /* 0xAAD7AAEA remote */
369 case S_MAGIC_PIPEFS: /* 0x50495045 remote */
370 /* FIXME: change syntax or add an optional attribute like "inotify:no".
371 The above is labeled as "remote" so that tail always uses polling,
372 but this isn't really a remote file system type. */
374 case S_MAGIC_PROC: /* 0x9FA0 local */
376 case S_MAGIC_PSTOREFS: /* 0x6165676C local */
378 case S_MAGIC_QNX4: /* 0x002F local */
380 case S_MAGIC_QNX6: /* 0x68191122 local */
382 case S_MAGIC_RAMFS: /* 0x858458F6 local */
384 case S_MAGIC_REISERFS: /* 0x52654973 local */
386 case S_MAGIC_ROMFS: /* 0x7275 local */
388 case S_MAGIC_RPC_PIPEFS: /* 0x67596969 local */
390 case S_MAGIC_SECURITYFS: /* 0x73636673 local */
392 case S_MAGIC_SELINUX: /* 0xF97CFF8C local */
394 case S_MAGIC_SMB: /* 0x517B remote */
396 case S_MAGIC_SOCKFS: /* 0x534F434B local */
398 case S_MAGIC_SQUASHFS: /* 0x73717368 local */
400 case S_MAGIC_SYSFS: /* 0x62656572 local */
402 case S_MAGIC_SYSV2: /* 0x012FF7B6 local */
404 case S_MAGIC_SYSV4: /* 0x012FF7B5 local */
406 case S_MAGIC_TMPFS: /* 0x01021994 local */
408 case S_MAGIC_UDF: /* 0x15013346 local */
410 case S_MAGIC_UFS: /* 0x00011954 local */
412 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 local */
414 case S_MAGIC_USBDEVFS: /* 0x9FA2 local */
416 case S_MAGIC_V9FS: /* 0x01021997 local */
418 case S_MAGIC_VMHGFS: /* 0xBACBACBC remote */
420 case S_MAGIC_VXFS: /* 0xA501FCF5 local */
422 case S_MAGIC_VZFS: /* 0x565A4653 local */
424 case S_MAGIC_XENFS: /* 0xABBA1974 local */
426 case S_MAGIC_XENIX: /* 0x012FF7B4 local */
428 case S_MAGIC_XFS: /* 0x58465342 local */
430 case S_MAGIC_XIAFS: /* 0x012FD16D local */
432 case S_MAGIC_ZFS: /* 0x2FC12FC1 local */
493 unsigned long int type = statfsbuf->f_type;
494 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
495 + (sizeof type * CHAR_BIT + 3) / 4];
496 sprintf (buf, "UNKNOWN (0x%lx)", type);
503 static char * ATTRIBUTE_WARN_UNUSED_RESULT
504 human_access (struct stat const *statbuf)
506 static char modebuf[12];
507 filemodestring (statbuf, modebuf);
512 static char * ATTRIBUTE_WARN_UNUSED_RESULT
513 human_time (struct timespec t)
515 static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
516 (INT_STRLEN_BOUND (int) /* YYYY */
517 + 1 /* because YYYY might equal INT_MAX + 1900 */
518 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
519 struct tm const *tm = localtime (&t.tv_sec);
521 return timetostr (t.tv_sec, str);
522 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
526 /* PFORMAT points to a '%' followed by a prefix of a format, all of
527 size PREFIX_LEN. The flags allowed for this format are
528 ALLOWED_FLAGS; remove other printf flags from the prefix, then
531 make_format (char *pformat, size_t prefix_len, char const *allowed_flags,
534 char *dst = pformat + 1;
536 char const *srclim = pformat + prefix_len;
537 for (src = dst; src < srclim && strchr (printf_flags, *src); src++)
538 if (strchr (allowed_flags, *src))
542 strcpy (dst, suffix);
546 out_string (char *pformat, size_t prefix_len, char const *arg)
548 make_format (pformat, prefix_len, "-", "s");
549 printf (pformat, arg);
552 out_int (char *pformat, size_t prefix_len, intmax_t arg)
554 make_format (pformat, prefix_len, "'-+ 0", PRIdMAX);
555 return printf (pformat, arg);
558 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
560 make_format (pformat, prefix_len, "'-0", PRIuMAX);
561 return printf (pformat, arg);
564 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
566 make_format (pformat, prefix_len, "-#0", PRIoMAX);
567 printf (pformat, arg);
570 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
572 make_format (pformat, prefix_len, "-#0", PRIxMAX);
573 printf (pformat, arg);
576 out_minus_zero (char *pformat, size_t prefix_len)
578 make_format (pformat, prefix_len, "'-+ 0", ".0f");
579 return printf (pformat, -0.25);
582 /* Output the number of seconds since the Epoch, using a format that
583 acts like printf's %f format. */
585 out_epoch_sec (char *pformat, size_t prefix_len,
586 struct stat const *statbuf ATTRIBUTE_UNUSED,
589 char *dot = memchr (pformat, '.', prefix_len);
590 size_t sec_prefix_len = prefix_len;
593 bool frac_left_adjust = false;
597 sec_prefix_len = dot - pformat;
598 pformat[prefix_len] = '\0';
600 if (ISDIGIT (dot[1]))
602 long int lprec = strtol (dot + 1, NULL, 10);
603 precision = (lprec <= INT_MAX ? lprec : INT_MAX);
610 if (precision && ISDIGIT (dot[-1]))
612 /* If a nontrivial width is given, subtract the width of the
613 decimal point and PRECISION digits that will be output
620 while (ISDIGIT (p[-1]));
622 long int lwidth = strtol (p, NULL, 10);
623 width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
627 sec_prefix_len = p - pformat;
628 int w_d = (decimal_point_len < width
629 ? width - decimal_point_len
633 int w = w_d - precision;
637 for (char const *src = dst; src < p; src++)
640 frac_left_adjust = true;
646 + (frac_left_adjust ? 0 : sprintf (dst, "%d", w)));
654 for (int i = precision; i < 9; i++)
656 int frac_sec = arg.tv_nsec / divisor;
659 if (TYPE_SIGNED (time_t))
661 bool minus_zero = false;
662 if (arg.tv_sec < 0 && arg.tv_nsec != 0)
664 int frac_sec_modulus = 1000000000 / divisor;
665 frac_sec = (frac_sec_modulus - frac_sec
666 - (arg.tv_nsec % divisor != 0));
667 arg.tv_sec += (frac_sec != 0);
668 minus_zero = (arg.tv_sec == 0);
670 int_len = (minus_zero
671 ? out_minus_zero (pformat, sec_prefix_len)
672 : out_int (pformat, sec_prefix_len, arg.tv_sec));
675 int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec);
679 int prec = (precision < 9 ? precision : 9);
680 int trailing_prec = precision - prec;
681 int ilen = (int_len < 0 ? 0 : int_len);
682 int trailing_width = (ilen < width && decimal_point_len < width - ilen
683 ? width - ilen - decimal_point_len - prec
685 printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
686 trailing_width, trailing_prec, 0);
690 /* Print the context information of FILENAME, and return true iff the
691 context could not be obtained. */
692 static bool ATTRIBUTE_WARN_UNUSED_RESULT
693 out_file_context (char *pformat, size_t prefix_len, char const *filename)
699 ? getfilecon (filename, &scontext)
700 : lgetfilecon (filename, &scontext)) < 0)
702 error (0, errno, _("failed to get security context of %s"),
707 strcpy (pformat + prefix_len, "s");
708 printf (pformat, (scontext ? scontext : "?"));
714 /* Print statfs info. Return zero upon success, nonzero upon failure. */
715 static bool ATTRIBUTE_WARN_UNUSED_RESULT
716 print_statfs (char *pformat, size_t prefix_len, unsigned int m,
717 char const *filename,
720 STRUCT_STATVFS const *statfsbuf = data;
726 out_string (pformat, prefix_len, filename);
731 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
732 uintmax_t fsid = statfsbuf->f_fsid;
734 typedef unsigned int fsid_word;
735 verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
736 verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
737 verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
738 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
740 /* Assume a little-endian word order, as that is compatible
741 with glibc's statvfs implementation. */
743 int words = sizeof statfsbuf->f_fsid / sizeof *p;
745 for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
747 uintmax_t u = p[words - 1 - i];
748 fsid |= u << (i * CHAR_BIT * sizeof *p);
751 out_uint_x (pformat, prefix_len, fsid);
756 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
759 #if HAVE_STRUCT_STATXFS_F_TYPE
760 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
766 out_string (pformat, prefix_len, human_fstype (statfsbuf));
769 out_int (pformat, prefix_len, statfsbuf->f_blocks);
772 out_int (pformat, prefix_len, statfsbuf->f_bfree);
775 out_int (pformat, prefix_len, statfsbuf->f_bavail);
778 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
782 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
784 frsize = statfsbuf->f_bsize;
785 out_uint (pformat, prefix_len, frsize);
789 out_uint (pformat, prefix_len, statfsbuf->f_files);
792 out_int (pformat, prefix_len, statfsbuf->f_ffree);
801 /* Return any bind mounted source for a path.
802 The caller should not free the returned buffer.
803 Return NULL if no bind mount found. */
804 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
805 find_bind_mount (char const * name)
807 char const * bind_mount = NULL;
809 static struct mount_entry *mount_list;
810 static bool tried_mount_list = false;
811 if (!tried_mount_list) /* attempt/warn once per process. */
813 if (!(mount_list = read_file_system_list (false)))
814 error (0, errno, "%s", _("cannot read table of mounted file systems"));
815 tried_mount_list = true;
818 struct mount_entry *me;
819 for (me = mount_list; me; me = me->me_next)
821 if (me->me_dummy && me->me_devname[0] == '/'
822 && STREQ (me->me_mountdir, name))
824 struct stat name_stats;
825 struct stat dev_stats;
827 if (stat (name, &name_stats) == 0
828 && stat (me->me_devname, &dev_stats) == 0
829 && SAME_INODE (name_stats, dev_stats))
831 bind_mount = me->me_devname;
840 /* Print mount point. Return zero upon success, nonzero upon failure. */
841 static bool ATTRIBUTE_WARN_UNUSED_RESULT
842 out_mount_point (char const *filename, char *pformat, size_t prefix_len,
843 const struct stat *statp)
846 char const *np = "?", *bp = NULL;
850 /* Look for bind mounts first. Note we output the immediate alias,
851 rather than further resolving to a base device mount point. */
852 if (follow_links || !S_ISLNK (statp->st_mode))
854 char *resolved = canonicalize_file_name (filename);
857 error (0, errno, _("failed to canonicalize %s"), quote (filename));
858 goto print_mount_point;
860 bp = find_bind_mount (resolved);
865 goto print_mount_point;
869 /* If there is no direct bind mount, then navigate
870 back up the tree looking for a device change.
871 Note we don't detect if any of the directory components
872 are bind mounted to the same device, but that's OK
873 since we've not directly queried them. */
874 if ((mp = find_mount_point (filename, statp)))
876 /* This dir might be bind mounted to another device,
877 so we resolve the bound source in that case also. */
878 bp = find_bind_mount (mp);
884 out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
889 /* Map a TS with negative TS.tv_nsec to {0,0}. */
890 static inline struct timespec
891 neg_to_zero (struct timespec ts)
895 struct timespec z = {0, 0};
899 /* Print stat info. Return zero upon success, nonzero upon failure. */
901 print_stat (char *pformat, size_t prefix_len, unsigned int m,
902 char const *filename, void const *data)
904 struct stat *statbuf = (struct stat *) data;
905 struct passwd *pw_ent;
906 struct group *gw_ent;
912 out_string (pformat, prefix_len, filename);
915 out_string (pformat, prefix_len, quote (filename));
916 if (S_ISLNK (statbuf->st_mode))
918 char *linkname = areadlink_with_size (filename, statbuf->st_size);
919 if (linkname == NULL)
921 error (0, errno, _("cannot read symbolic link %s"),
926 out_string (pformat, prefix_len, quote (linkname));
931 out_uint (pformat, prefix_len, statbuf->st_dev);
934 out_uint_x (pformat, prefix_len, statbuf->st_dev);
937 out_uint (pformat, prefix_len, statbuf->st_ino);
940 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
943 out_string (pformat, prefix_len, human_access (statbuf));
946 out_uint_x (pformat, prefix_len, statbuf->st_mode);
949 out_string (pformat, prefix_len, file_type (statbuf));
952 out_uint (pformat, prefix_len, statbuf->st_nlink);
955 out_uint (pformat, prefix_len, statbuf->st_uid);
959 pw_ent = getpwuid (statbuf->st_uid);
960 out_string (pformat, prefix_len,
961 pw_ent ? pw_ent->pw_name : "UNKNOWN");
964 out_uint (pformat, prefix_len, statbuf->st_gid);
968 gw_ent = getgrgid (statbuf->st_gid);
969 out_string (pformat, prefix_len,
970 gw_ent ? gw_ent->gr_name : "UNKNOWN");
973 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
976 fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
979 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
982 out_int (pformat, prefix_len, statbuf->st_size);
985 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
988 out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
991 out_uint (pformat, prefix_len, ST_BLKSIZE (*statbuf));
995 struct timespec t = get_stat_birthtime (statbuf);
997 out_string (pformat, prefix_len, "-");
999 out_string (pformat, prefix_len, human_time (t));
1003 out_epoch_sec (pformat, prefix_len, statbuf,
1004 neg_to_zero (get_stat_birthtime (statbuf)));
1007 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
1010 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_atime (statbuf));
1013 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
1016 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_mtime (statbuf));
1019 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
1022 out_epoch_sec (pformat, prefix_len, statbuf, get_stat_ctime (statbuf));
1025 fail |= out_file_context (pformat, prefix_len, filename);
1028 fputc ('?', stdout);
1034 /* Output a single-character \ escape. */
1037 print_esc_char (char c)
1041 case 'a': /* Alert. */
1044 case 'b': /* Backspace. */
1047 case 'e': /* Escape. */
1050 case 'f': /* Form feed. */
1053 case 'n': /* New line. */
1056 case 'r': /* Carriage return. */
1059 case 't': /* Horizontal tab. */
1062 case 'v': /* Vertical tab. */
1069 error (0, 0, _("warning: unrecognized escape '\\%c'"), c);
1075 /* Print the information specified by the format string, FORMAT,
1076 calling PRINT_FUNC for each %-directive encountered.
1077 Return zero upon success, nonzero upon failure. */
1078 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1079 print_it (char const *format, char const *filename,
1080 bool (*print_func) (char *, size_t, unsigned int,
1081 char const *, void const *),
1086 /* Add 2 to accommodate our conversion of the stat '%s' format string
1087 to the longer printf '%llu' one. */
1090 MAX_ADDITIONAL_BYTES =
1091 (MAX (sizeof PRIdMAX,
1092 MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
1095 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
1096 char *dest = xmalloc (n_alloc);
1098 for (b = format; *b; b++)
1104 size_t len = strspn (b + 1, printf_flags);
1105 char const *fmt_char = b + len + 1;
1106 fmt_char += strspn (fmt_char, digits);
1107 if (*fmt_char == '.')
1108 fmt_char += 1 + strspn (fmt_char + 1, digits);
1109 len = fmt_char - (b + 1);
1110 unsigned int fmt_code = *fmt_char;
1111 memcpy (dest, b, len + 1);
1122 dest[len + 1] = *fmt_char;
1123 dest[len + 2] = '\0';
1124 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
1125 quotearg_colon (dest));
1130 fail |= print_func (dest, len + 1, fmt_code, filename, data);
1137 if ( ! interpret_backslash_escapes)
1145 int esc_value = octtobin (*b);
1146 int esc_length = 1; /* number of octal digits */
1147 for (++b; esc_length < 3 && isodigit (*b);
1150 esc_value = esc_value * 8 + octtobin (*b);
1152 putchar (esc_value);
1155 else if (*b == 'x' && isxdigit (to_uchar (b[1])))
1157 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
1158 /* A hexadecimal \xhh escape sequence must have
1159 1 or 2 hex. digits. */
1161 if (isxdigit (to_uchar (b[1])))
1164 esc_value = esc_value * 16 + hextobin (*b);
1166 putchar (esc_value);
1168 else if (*b == '\0')
1170 error (0, 0, _("warning: backslash at end of format"));
1172 /* Arrange to exit the loop. */
1177 print_esc_char (*b);
1188 fputs (trailing_delim, stdout);
1193 /* Stat the file system and print what we find. */
1194 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1195 do_statfs (char const *filename, char const *format)
1197 STRUCT_STATVFS statfsbuf;
1199 if (STREQ (filename, "-"))
1201 error (0, 0, _("using %s to denote standard input does not work"
1202 " in file system mode"), quote (filename));
1206 if (STATFS (filename, &statfsbuf) != 0)
1208 error (0, errno, _("cannot read file system information for %s"),
1213 bool fail = print_it (format, filename, print_statfs, &statfsbuf);
1217 /* stat the file and print what we find */
1218 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1219 do_stat (char const *filename, char const *format,
1220 char const *format2)
1222 struct stat statbuf;
1224 if (STREQ (filename, "-"))
1226 if (fstat (STDIN_FILENO, &statbuf) != 0)
1228 error (0, errno, _("cannot stat standard input"));
1232 /* We can't use the shorter
1233 (follow_links?stat:lstat) (filename, &statbug)
1234 since stat might be a function-like macro. */
1235 else if ((follow_links
1236 ? stat (filename, &statbuf)
1237 : lstat (filename, &statbuf)) != 0)
1239 error (0, errno, _("cannot stat %s"), quote (filename));
1243 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
1246 bool fail = print_it (format, filename, print_stat, &statbuf);
1250 /* Return an allocated format string in static storage that
1251 corresponds to whether FS and TERSE options were declared. */
1253 default_format (bool fs, bool terse, bool device)
1259 format = xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
1262 /* TRANSLATORS: This string uses format specifiers from
1263 'stat --help' with --file-system, and NOT from printf. */
1264 format = xstrdup (_(" File: \"%n\"\n"
1265 " ID: %-8i Namelen: %-7l Type: %T\n"
1266 "Block size: %-10s Fundamental block size: %S\n"
1267 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
1268 "Inodes: Total: %-10c Free: %d\n"));
1275 if (0 < is_selinux_enabled ())
1276 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1277 " %X %Y %Z %W %o %C\n");
1279 format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1280 " %X %Y %Z %W %o\n");
1285 /* TRANSLATORS: This string uses format specifiers from
1286 'stat --help' without --file-system, and NOT from printf. */
1287 format = xstrdup (_("\
1289 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1295 /* TRANSLATORS: This string uses format specifiers from
1296 'stat --help' without --file-system, and NOT from printf. */
1297 format = xasprintf ("%s%s", format, _("\
1298 " "Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
1303 /* TRANSLATORS: This string uses format specifiers from
1304 'stat --help' without --file-system, and NOT from printf. */
1305 format = xasprintf ("%s%s", format, _("\
1306 " "Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1312 /* TRANSLATORS: This string uses format specifiers from
1313 'stat --help' without --file-system, and NOT from printf. */
1314 format = xasprintf ("%s%s", format, _("\
1315 " "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1319 if (0 < is_selinux_enabled ())
1322 /* TRANSLATORS: This string uses format specifiers from
1323 'stat --help' without --file-system, and NOT from printf. */
1324 format = xasprintf ("%s%s", format, _("Context: %C\n"));
1329 /* TRANSLATORS: This string uses format specifiers from
1330 'stat --help' without --file-system, and NOT from printf. */
1331 format = xasprintf ("%s%s", format,
1345 if (status != EXIT_SUCCESS)
1349 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
1351 Display file or file system status.\n\
1353 -L, --dereference follow links\n\
1354 -f, --file-system display file system status instead of file status\n\
1357 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1358 output a newline after each use of FORMAT\n\
1359 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1360 and do not output a mandatory trailing newline.\n\
1361 If you want a newline, include \\n in FORMAT\n\
1362 -t, --terse print the information in terse form\n\
1364 fputs (HELP_OPTION_DESCRIPTION, stdout);
1365 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1368 The valid format sequences for files (without --file-system):\n\
1370 %a access rights in octal\n\
1371 %A access rights in human readable form\n\
1372 %b number of blocks allocated (see %B)\n\
1373 %B the size in bytes of each block reported by %b\n\
1374 %C SELinux security context string\n\
1377 %d device number in decimal\n\
1378 %D device number in hex\n\
1379 %f raw mode in hex\n\
1381 %g group ID of owner\n\
1382 %G group name of owner\n\
1385 %h number of hard links\n\
1389 %N quoted file name with dereference if symbolic link\n\
1390 %o optimal I/O transfer size hint\n\
1391 %s total size, in bytes\n\
1392 %t major device type in hex\n\
1393 %T minor device type in hex\n\
1396 %u user ID of owner\n\
1397 %U user name of owner\n\
1398 %w time of file birth, human-readable; - if unknown\n\
1399 %W time of file birth, seconds since Epoch; 0 if unknown\n\
1400 %x time of last access, human-readable\n\
1401 %X time of last access, seconds since Epoch\n\
1402 %y time of last modification, human-readable\n\
1403 %Y time of last modification, seconds since Epoch\n\
1404 %z time of last change, human-readable\n\
1405 %Z time of last change, seconds since Epoch\n\
1410 Valid format sequences for file systems:\n\
1412 %a free blocks available to non-superuser\n\
1413 %b total data blocks in file system\n\
1414 %c total file nodes in file system\n\
1415 %d free file nodes in file system\n\
1416 %f free blocks in file system\n\
1419 %i file system ID in hex\n\
1420 %l maximum length of filenames\n\
1422 %s block size (for faster transfers)\n\
1423 %S fundamental block size (for block counts)\n\
1424 %t file system type in hex\n\
1425 %T file system type in human readable form\n\
1427 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1428 emit_ancillary_info ();
1434 main (int argc, char *argv[])
1440 char *format = NULL;
1444 initialize_main (&argc, &argv);
1445 set_program_name (argv[0]);
1446 setlocale (LC_ALL, "");
1447 bindtextdomain (PACKAGE, LOCALEDIR);
1448 textdomain (PACKAGE);
1450 struct lconv const *locale = localeconv ();
1451 decimal_point = (locale->decimal_point[0] ? locale->decimal_point : ".");
1452 decimal_point_len = strlen (decimal_point);
1454 atexit (close_stdout);
1456 while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
1462 interpret_backslash_escapes = true;
1463 trailing_delim = "";
1468 interpret_backslash_escapes = false;
1469 trailing_delim = "\n";
1473 follow_links = true;
1484 case_GETOPT_HELP_CHAR;
1486 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1489 usage (EXIT_FAILURE);
1495 error (0, 0, _("missing operand"));
1496 usage (EXIT_FAILURE);
1503 format = default_format (fs, terse, false);
1504 format2 = default_format (fs, terse, true);
1507 for (i = optind; i < argc; i++)
1509 ? do_statfs (argv[i], format)
1510 : do_stat (argv[i], format, format2));
1512 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);