1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2016 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"
69 #include "stat-size.h"
70 #include "stat-time.h"
72 #include "find-mount-point.h"
73 #include "xvasprintf.h"
76 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
77 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
78 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
79 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
81 # if ! STAT_STATVFS && STAT_STATVFS64
82 # define STRUCT_STATVFS struct statvfs64
83 # define STATFS statvfs64
85 # define STRUCT_STATVFS struct statvfs
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
157 # include <sys/nvpair.h>
160 /* FIXME: these are used by printf.c, too */
161 #define isodigit(c) ('0' <= (c) && (c) <= '7')
162 #define octtobin(c) ((c) - '0')
163 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
164 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
166 static char const digits
[] = "0123456789";
168 /* Flags that are portable for use in printf, for at least one
169 conversion specifier; make_format removes unportable flags as
170 needed for particular specifiers. The glibc 2.2 extension "I" is
171 listed here; it is removed by make_format because it has undefined
172 behavior elsewhere and because it is incompatible with
174 static char const printf_flags
[] = "'-+ #0I";
176 #define PROGRAM_NAME "stat"
178 #define AUTHORS proper_name ("Michael Meskes")
182 PRINTF_OPTION
= CHAR_MAX
+ 1
185 static struct option
const long_options
[] =
187 {"dereference", no_argument
, NULL
, 'L'},
188 {"file-system", no_argument
, NULL
, 'f'},
189 {"format", required_argument
, NULL
, 'c'},
190 {"printf", required_argument
, NULL
, PRINTF_OPTION
},
191 {"terse", no_argument
, NULL
, 't'},
192 {GETOPT_HELP_OPTION_DECL
},
193 {GETOPT_VERSION_OPTION_DECL
},
197 /* Whether to follow symbolic links; True for --dereference (-L). */
198 static bool follow_links
;
200 /* Whether to interpret backslash-escape sequences.
201 True for --printf=FMT, not for --format=FMT (-c). */
202 static bool interpret_backslash_escapes
;
204 /* The trailing delimiter string:
205 "" for --printf=FMT, "\n" for --format=FMT (-c). */
206 static char const *trailing_delim
= "";
208 /* The representation of the decimal point in the current locale. */
209 static char const *decimal_point
;
210 static size_t decimal_point_len
;
212 /* Return the type of the specified file system.
213 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
214 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
215 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
216 Still others have neither and have to get by with f_type (GNU/Linux).
217 But f_type may only exist in statfs (Cygwin). */
218 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
219 human_fstype (STRUCT_STATVFS
const *statfsbuf
)
221 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
222 return statfsbuf
->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
;
224 switch (statfsbuf
->f_type
)
226 # if defined __linux__
228 /* Compare with what's in libc:
229 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
230 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
231 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
232 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
234 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
235 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
237 diff -u sym_stat sym_libc
240 /* Also compare with the list in "man 2 statfs" using the
241 fs-magic-compare make target. */
243 /* IMPORTANT NOTE: Each of the following 'case S_MAGIC_...:'
244 statements must be followed by a hexadecimal constant in
245 a comment. The S_MAGIC_... name and constant are automatically
246 combined to produce the #define directives in fs.h. */
248 case S_MAGIC_ACFS
: /* 0x61636673 remote */
250 case S_MAGIC_ADFS
: /* 0xADF5 local */
252 case S_MAGIC_AFFS
: /* 0xADFF local */
254 case S_MAGIC_AFS
: /* 0x5346414F remote */
256 case S_MAGIC_ANON_INODE_FS
: /* 0x09041934 local */
257 return "anon-inode FS";
258 case S_MAGIC_AUFS
: /* 0x61756673 remote */
259 /* FIXME: change syntax or add an optional attribute like "inotify:no".
260 The above is labeled as "remote" so that tail always uses polling,
261 but this isn't really a remote file system type. */
263 case S_MAGIC_AUTOFS
: /* 0x0187 local */
265 case S_MAGIC_BEFS
: /* 0x42465331 local */
267 case S_MAGIC_BDEVFS
: /* 0x62646576 local */
269 case S_MAGIC_BFS
: /* 0x1BADFACE local */
271 case S_MAGIC_BPF_FS
: /* 0xCAFE4A11 local */
273 case S_MAGIC_BINFMTFS
: /* 0x42494E4D local */
274 return "binfmt_misc";
275 case S_MAGIC_BTRFS
: /* 0x9123683E local */
277 case S_MAGIC_BTRFS_TEST
: /* 0x73727279 local */
279 case S_MAGIC_CEPH
: /* 0x00C36400 remote */
281 case S_MAGIC_CGROUP
: /* 0x0027E0EB local */
283 case S_MAGIC_CIFS
: /* 0xFF534D42 remote */
285 case S_MAGIC_CODA
: /* 0x73757245 remote */
287 case S_MAGIC_COH
: /* 0x012FF7B7 local */
289 case S_MAGIC_CONFIGFS
: /* 0x62656570 local */
291 case S_MAGIC_CRAMFS
: /* 0x28CD3D45 local */
293 case S_MAGIC_CRAMFS_WEND
: /* 0x453DCD28 local */
294 return "cramfs-wend";
295 case S_MAGIC_DEBUGFS
: /* 0x64626720 local */
297 case S_MAGIC_DEVFS
: /* 0x1373 local */
299 case S_MAGIC_DEVPTS
: /* 0x1CD1 local */
301 case S_MAGIC_ECRYPTFS
: /* 0xF15F local */
303 case S_MAGIC_EFIVARFS
: /* 0xDE5E81E4 local */
305 case S_MAGIC_EFS
: /* 0x00414A53 local */
307 case S_MAGIC_EXOFS
: /* 0x5DF5 local */
309 case S_MAGIC_EXT
: /* 0x137D local */
311 case S_MAGIC_EXT2
: /* 0xEF53 local */
313 case S_MAGIC_EXT2_OLD
: /* 0xEF51 local */
315 case S_MAGIC_F2FS
: /* 0xF2F52010 local */
317 case S_MAGIC_FAT
: /* 0x4006 local */
319 case S_MAGIC_FHGFS
: /* 0x19830326 remote */
321 case S_MAGIC_FUSEBLK
: /* 0x65735546 remote */
323 case S_MAGIC_FUSECTL
: /* 0x65735543 remote */
325 case S_MAGIC_FUTEXFS
: /* 0x0BAD1DEA local */
327 case S_MAGIC_GFS
: /* 0x01161970 remote */
329 case S_MAGIC_GPFS
: /* 0x47504653 remote */
331 case S_MAGIC_HFS
: /* 0x4244 local */
333 case S_MAGIC_HFS_PLUS
: /* 0x482B local */
335 case S_MAGIC_HFS_X
: /* 0x4858 local */
337 case S_MAGIC_HOSTFS
: /* 0x00C0FFEE local */
339 case S_MAGIC_HPFS
: /* 0xF995E849 local */
341 case S_MAGIC_HUGETLBFS
: /* 0x958458F6 local */
343 case S_MAGIC_MTD_INODE_FS
: /* 0x11307854 local */
345 case S_MAGIC_IBRIX
: /* 0x013111A8 remote */
347 case S_MAGIC_INOTIFYFS
: /* 0x2BAD1DEA local */
349 case S_MAGIC_ISOFS
: /* 0x9660 local */
351 case S_MAGIC_ISOFS_R_WIN
: /* 0x4004 local */
353 case S_MAGIC_ISOFS_WIN
: /* 0x4000 local */
355 case S_MAGIC_JFFS
: /* 0x07C0 local */
357 case S_MAGIC_JFFS2
: /* 0x72B6 local */
359 case S_MAGIC_JFS
: /* 0x3153464A local */
361 case S_MAGIC_KAFS
: /* 0x6B414653 remote */
363 case S_MAGIC_LOGFS
: /* 0xC97E8168 local */
365 case S_MAGIC_LUSTRE
: /* 0x0BD00BD0 remote */
367 case S_MAGIC_MINIX
: /* 0x137F local */
369 case S_MAGIC_MINIX_30
: /* 0x138F local */
370 return "minix (30 char.)";
371 case S_MAGIC_MINIX_V2
: /* 0x2468 local */
373 case S_MAGIC_MINIX_V2_30
: /* 0x2478 local */
374 return "minix v2 (30 char.)";
375 case S_MAGIC_MINIX_V3
: /* 0x4D5A local */
377 case S_MAGIC_MQUEUE
: /* 0x19800202 local */
379 case S_MAGIC_MSDOS
: /* 0x4D44 local */
381 case S_MAGIC_NCP
: /* 0x564C remote */
383 case S_MAGIC_NFS
: /* 0x6969 remote */
385 case S_MAGIC_NFSD
: /* 0x6E667364 remote */
387 case S_MAGIC_NILFS
: /* 0x3434 local */
389 case S_MAGIC_NSFS
: /* 0x6E736673 local */
391 case S_MAGIC_NTFS
: /* 0x5346544E local */
393 case S_MAGIC_OPENPROM
: /* 0x9FA1 local */
395 case S_MAGIC_OCFS2
: /* 0x7461636F remote */
397 case S_MAGIC_OVERLAYFS
: /* 0x794C7630 remote */
398 /* This may overlay remote file systems.
399 Also there have been issues reported with inotify and overlayfs,
400 so mark as "remote" so that polling is used. */
402 case S_MAGIC_PANFS
: /* 0xAAD7AAEA remote */
404 case S_MAGIC_PIPEFS
: /* 0x50495045 remote */
405 /* FIXME: change syntax or add an optional attribute like "inotify:no".
406 The above is labeled as "remote" so that tail always uses polling,
407 but this isn't really a remote file system type. */
409 case S_MAGIC_PROC
: /* 0x9FA0 local */
411 case S_MAGIC_PSTOREFS
: /* 0x6165676C local */
413 case S_MAGIC_QNX4
: /* 0x002F local */
415 case S_MAGIC_QNX6
: /* 0x68191122 local */
417 case S_MAGIC_RAMFS
: /* 0x858458F6 local */
419 case S_MAGIC_REISERFS
: /* 0x52654973 local */
421 case S_MAGIC_ROMFS
: /* 0x7275 local */
423 case S_MAGIC_RPC_PIPEFS
: /* 0x67596969 local */
425 case S_MAGIC_SECURITYFS
: /* 0x73636673 local */
427 case S_MAGIC_SELINUX
: /* 0xF97CFF8C local */
429 case S_MAGIC_SMACK
: /* 0x43415D53 local */
431 case S_MAGIC_SMB
: /* 0x517B remote */
433 case S_MAGIC_SNFS
: /* 0xBEEFDEAD remote */
435 case S_MAGIC_SOCKFS
: /* 0x534F434B local */
437 case S_MAGIC_SQUASHFS
: /* 0x73717368 local */
439 case S_MAGIC_SYSFS
: /* 0x62656572 local */
441 case S_MAGIC_SYSV2
: /* 0x012FF7B6 local */
443 case S_MAGIC_SYSV4
: /* 0x012FF7B5 local */
445 case S_MAGIC_TMPFS
: /* 0x01021994 local */
447 case S_MAGIC_TRACEFS
: /* 0x74726163 local */
449 case S_MAGIC_UBIFS
: /* 0x24051905 local */
451 case S_MAGIC_UDF
: /* 0x15013346 local */
453 case S_MAGIC_UFS
: /* 0x00011954 local */
455 case S_MAGIC_UFS_BYTESWAPPED
: /* 0x54190100 local */
457 case S_MAGIC_USBDEVFS
: /* 0x9FA2 local */
459 case S_MAGIC_V9FS
: /* 0x01021997 local */
461 case S_MAGIC_VMHGFS
: /* 0xBACBACBC remote */
463 case S_MAGIC_VXFS
: /* 0xA501FCF5 remote */
464 /* Veritas File System can run in single instance or clustered mode,
465 so mark as remote to cater for the latter case. */
467 case S_MAGIC_VZFS
: /* 0x565A4653 local */
469 case S_MAGIC_XENFS
: /* 0xABBA1974 local */
471 case S_MAGIC_XENIX
: /* 0x012FF7B4 local */
473 case S_MAGIC_XFS
: /* 0x58465342 local */
475 case S_MAGIC_XIAFS
: /* 0x012FD16D local */
477 case S_MAGIC_ZFS
: /* 0x2FC12FC1 local */
538 unsigned long int type
= statfsbuf
->f_type
;
539 static char buf
[sizeof "UNKNOWN (0x%lx)" - 3
540 + (sizeof type
* CHAR_BIT
+ 3) / 4];
541 sprintf (buf
, "UNKNOWN (0x%lx)", type
);
548 static char * ATTRIBUTE_WARN_UNUSED_RESULT
549 human_access (struct stat
const *statbuf
)
551 static char modebuf
[12];
552 filemodestring (statbuf
, modebuf
);
557 static char * ATTRIBUTE_WARN_UNUSED_RESULT
558 human_time (struct timespec t
)
560 static char str
[MAX (INT_BUFSIZE_BOUND (intmax_t),
561 (INT_STRLEN_BOUND (int) /* YYYY */
562 + 1 /* because YYYY might equal INT_MAX + 1900 */
563 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
564 static timezone_t tz
;
566 tz
= tzalloc (getenv ("TZ"));
567 struct tm
const *tm
= localtime (&t
.tv_sec
);
569 return timetostr (t
.tv_sec
, str
);
570 nstrftime (str
, sizeof str
, "%Y-%m-%d %H:%M:%S.%N %z", tm
, tz
, t
.tv_nsec
);
574 /* PFORMAT points to a '%' followed by a prefix of a format, all of
575 size PREFIX_LEN. The flags allowed for this format are
576 ALLOWED_FLAGS; remove other printf flags from the prefix, then
579 make_format (char *pformat
, size_t prefix_len
, char const *allowed_flags
,
582 char *dst
= pformat
+ 1;
584 char const *srclim
= pformat
+ prefix_len
;
585 for (src
= dst
; src
< srclim
&& strchr (printf_flags
, *src
); src
++)
586 if (strchr (allowed_flags
, *src
))
590 strcpy (dst
, suffix
);
594 out_string (char *pformat
, size_t prefix_len
, char const *arg
)
596 make_format (pformat
, prefix_len
, "-", "s");
597 printf (pformat
, arg
);
600 out_int (char *pformat
, size_t prefix_len
, intmax_t arg
)
602 make_format (pformat
, prefix_len
, "'-+ 0", PRIdMAX
);
603 return printf (pformat
, arg
);
606 out_uint (char *pformat
, size_t prefix_len
, uintmax_t arg
)
608 make_format (pformat
, prefix_len
, "'-0", PRIuMAX
);
609 return printf (pformat
, arg
);
612 out_uint_o (char *pformat
, size_t prefix_len
, uintmax_t arg
)
614 make_format (pformat
, prefix_len
, "-#0", PRIoMAX
);
615 printf (pformat
, arg
);
618 out_uint_x (char *pformat
, size_t prefix_len
, uintmax_t arg
)
620 make_format (pformat
, prefix_len
, "-#0", PRIxMAX
);
621 printf (pformat
, arg
);
624 out_minus_zero (char *pformat
, size_t prefix_len
)
626 make_format (pformat
, prefix_len
, "'-+ 0", ".0f");
627 return printf (pformat
, -0.25);
630 /* Output the number of seconds since the Epoch, using a format that
631 acts like printf's %f format. */
633 out_epoch_sec (char *pformat
, size_t prefix_len
,
634 struct stat
const *statbuf _GL_UNUSED
,
637 char *dot
= memchr (pformat
, '.', prefix_len
);
638 size_t sec_prefix_len
= prefix_len
;
641 bool frac_left_adjust
= false;
645 sec_prefix_len
= dot
- pformat
;
646 pformat
[prefix_len
] = '\0';
648 if (ISDIGIT (dot
[1]))
650 long int lprec
= strtol (dot
+ 1, NULL
, 10);
651 precision
= (lprec
<= INT_MAX
? lprec
: INT_MAX
);
658 if (precision
&& ISDIGIT (dot
[-1]))
660 /* If a nontrivial width is given, subtract the width of the
661 decimal point and PRECISION digits that will be output
668 while (ISDIGIT (p
[-1]));
670 long int lwidth
= strtol (p
, NULL
, 10);
671 width
= (lwidth
<= INT_MAX
? lwidth
: INT_MAX
);
675 sec_prefix_len
= p
- pformat
;
676 int w_d
= (decimal_point_len
< width
677 ? width
- decimal_point_len
681 int w
= w_d
- precision
;
685 for (char const *src
= dst
; src
< p
; src
++)
688 frac_left_adjust
= true;
694 + (frac_left_adjust
? 0 : sprintf (dst
, "%d", w
)));
702 for (int i
= precision
; i
< 9; i
++)
704 int frac_sec
= arg
.tv_nsec
/ divisor
;
707 if (TYPE_SIGNED (time_t))
709 bool minus_zero
= false;
710 if (arg
.tv_sec
< 0 && arg
.tv_nsec
!= 0)
712 int frac_sec_modulus
= 1000000000 / divisor
;
713 frac_sec
= (frac_sec_modulus
- frac_sec
714 - (arg
.tv_nsec
% divisor
!= 0));
715 arg
.tv_sec
+= (frac_sec
!= 0);
716 minus_zero
= (arg
.tv_sec
== 0);
718 int_len
= (minus_zero
719 ? out_minus_zero (pformat
, sec_prefix_len
)
720 : out_int (pformat
, sec_prefix_len
, arg
.tv_sec
));
723 int_len
= out_uint (pformat
, sec_prefix_len
, arg
.tv_sec
);
727 int prec
= (precision
< 9 ? precision
: 9);
728 int trailing_prec
= precision
- prec
;
729 int ilen
= (int_len
< 0 ? 0 : int_len
);
730 int trailing_width
= (ilen
< width
&& decimal_point_len
< width
- ilen
731 ? width
- ilen
- decimal_point_len
- prec
733 printf ("%s%.*d%-*.*d", decimal_point
, prec
, frac_sec
,
734 trailing_width
, trailing_prec
, 0);
738 /* Print the context information of FILENAME, and return true iff the
739 context could not be obtained. */
740 static bool ATTRIBUTE_WARN_UNUSED_RESULT
741 out_file_context (char *pformat
, size_t prefix_len
, char const *filename
)
747 ? getfilecon (filename
, &scontext
)
748 : lgetfilecon (filename
, &scontext
)) < 0)
750 error (0, errno
, _("failed to get security context of %s"),
755 strcpy (pformat
+ prefix_len
, "s");
756 printf (pformat
, (scontext
? scontext
: "?"));
762 /* Print statfs info. Return zero upon success, nonzero upon failure. */
763 static bool ATTRIBUTE_WARN_UNUSED_RESULT
764 print_statfs (char *pformat
, size_t prefix_len
, unsigned int m
,
765 int fd
, char const *filename
,
768 STRUCT_STATVFS
const *statfsbuf
= data
;
774 out_string (pformat
, prefix_len
, filename
);
779 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
780 uintmax_t fsid
= statfsbuf
->f_fsid
;
782 typedef unsigned int fsid_word
;
783 verify (alignof (STRUCT_STATVFS
) % alignof (fsid_word
) == 0);
784 verify (offsetof (STRUCT_STATVFS
, f_fsid
) % alignof (fsid_word
) == 0);
785 verify (sizeof statfsbuf
->f_fsid
% alignof (fsid_word
) == 0);
786 fsid_word
const *p
= (fsid_word
*) &statfsbuf
->f_fsid
;
788 /* Assume a little-endian word order, as that is compatible
789 with glibc's statvfs implementation. */
791 int words
= sizeof statfsbuf
->f_fsid
/ sizeof *p
;
793 for (i
= 0; i
< words
&& i
* sizeof *p
< sizeof fsid
; i
++)
795 uintmax_t u
= p
[words
- 1 - i
];
796 fsid
|= u
<< (i
* CHAR_BIT
* sizeof *p
);
799 out_uint_x (pformat
, prefix_len
, fsid
);
804 OUT_NAMEMAX (pformat
, prefix_len
, SB_F_NAMEMAX (statfsbuf
));
807 #if HAVE_STRUCT_STATXFS_F_TYPE
808 out_uint_x (pformat
, prefix_len
, statfsbuf
->f_type
);
814 out_string (pformat
, prefix_len
, human_fstype (statfsbuf
));
817 out_int (pformat
, prefix_len
, statfsbuf
->f_blocks
);
820 out_int (pformat
, prefix_len
, statfsbuf
->f_bfree
);
823 out_int (pformat
, prefix_len
, statfsbuf
->f_bavail
);
826 out_uint (pformat
, prefix_len
, statfsbuf
->f_bsize
);
830 uintmax_t frsize
= STATFS_FRSIZE (statfsbuf
);
832 frsize
= statfsbuf
->f_bsize
;
833 out_uint (pformat
, prefix_len
, frsize
);
837 out_uint (pformat
, prefix_len
, statfsbuf
->f_files
);
840 out_int (pformat
, prefix_len
, statfsbuf
->f_ffree
);
849 /* Return any bind mounted source for a path.
850 The caller should not free the returned buffer.
851 Return NULL if no bind mount found. */
852 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
853 find_bind_mount (char const * name
)
855 char const * bind_mount
= NULL
;
857 static struct mount_entry
*mount_list
;
858 static bool tried_mount_list
= false;
859 if (!tried_mount_list
) /* attempt/warn once per process. */
861 if (!(mount_list
= read_file_system_list (false)))
862 error (0, errno
, "%s", _("cannot read table of mounted file systems"));
863 tried_mount_list
= true;
866 struct stat name_stats
;
867 if (stat (name
, &name_stats
) != 0)
870 struct mount_entry
*me
;
871 for (me
= mount_list
; me
; me
= me
->me_next
)
873 if (me
->me_dummy
&& me
->me_devname
[0] == '/'
874 && STREQ (me
->me_mountdir
, name
))
876 struct stat dev_stats
;
878 if (stat (me
->me_devname
, &dev_stats
) == 0
879 && SAME_INODE (name_stats
, dev_stats
))
881 bind_mount
= me
->me_devname
;
890 /* Print mount point. Return zero upon success, nonzero upon failure. */
891 static bool ATTRIBUTE_WARN_UNUSED_RESULT
892 out_mount_point (char const *filename
, char *pformat
, size_t prefix_len
,
893 const struct stat
*statp
)
896 char const *np
= "?", *bp
= NULL
;
900 /* Look for bind mounts first. Note we output the immediate alias,
901 rather than further resolving to a base device mount point. */
902 if (follow_links
|| !S_ISLNK (statp
->st_mode
))
904 char *resolved
= canonicalize_file_name (filename
);
907 error (0, errno
, _("failed to canonicalize %s"), quoteaf (filename
));
908 goto print_mount_point
;
910 bp
= find_bind_mount (resolved
);
915 goto print_mount_point
;
919 /* If there is no direct bind mount, then navigate
920 back up the tree looking for a device change.
921 Note we don't detect if any of the directory components
922 are bind mounted to the same device, but that's OK
923 since we've not directly queried them. */
924 if ((mp
= find_mount_point (filename
, statp
)))
926 /* This dir might be bind mounted to another device,
927 so we resolve the bound source in that case also. */
928 bp
= find_bind_mount (mp
);
934 out_string (pformat
, prefix_len
, bp
? bp
: mp
? mp
: np
);
939 static struct timespec
940 get_birthtime (int fd
, char const *filename
, struct stat
const *st
)
942 struct timespec ts
= get_stat_birthtime (st
);
949 ? getattrat (AT_FDCWD
, XATTR_VIEW_READWRITE
, filename
, &response
)
950 : fgetattr (fd
, XATTR_VIEW_READWRITE
, &response
))
955 if (nvlist_lookup_uint64_array (response
, A_CRTIME
, &val
, &n
) == 0
957 && val
[0] <= TYPE_MAXIMUM (time_t)
958 && val
[1] < 1000000000 * 2 /* for leap seconds */)
963 nvlist_free (response
);
971 /* Map a TS with negative TS.tv_nsec to {0,0}. */
972 static inline struct timespec
973 neg_to_zero (struct timespec ts
)
977 struct timespec z
= {0, 0};
981 /* Print stat info. Return zero upon success, nonzero upon failure. */
983 print_stat (char *pformat
, size_t prefix_len
, unsigned int m
,
984 int fd
, char const *filename
, void const *data
)
986 struct stat
*statbuf
= (struct stat
*) data
;
987 struct passwd
*pw_ent
;
988 struct group
*gw_ent
;
994 out_string (pformat
, prefix_len
, filename
);
997 out_string (pformat
, prefix_len
, quoteaf (filename
));
998 if (S_ISLNK (statbuf
->st_mode
))
1000 char *linkname
= areadlink_with_size (filename
, statbuf
->st_size
);
1001 if (linkname
== NULL
)
1003 error (0, errno
, _("cannot read symbolic link %s"),
1004 quoteaf (filename
));
1008 out_string (pformat
, prefix_len
, quoteaf (linkname
));
1013 out_uint (pformat
, prefix_len
, statbuf
->st_dev
);
1016 out_uint_x (pformat
, prefix_len
, statbuf
->st_dev
);
1019 out_uint (pformat
, prefix_len
, statbuf
->st_ino
);
1022 out_uint_o (pformat
, prefix_len
, statbuf
->st_mode
& CHMOD_MODE_BITS
);
1025 out_string (pformat
, prefix_len
, human_access (statbuf
));
1028 out_uint_x (pformat
, prefix_len
, statbuf
->st_mode
);
1031 out_string (pformat
, prefix_len
, file_type (statbuf
));
1034 out_uint (pformat
, prefix_len
, statbuf
->st_nlink
);
1037 out_uint (pformat
, prefix_len
, statbuf
->st_uid
);
1040 pw_ent
= getpwuid (statbuf
->st_uid
);
1041 out_string (pformat
, prefix_len
,
1042 pw_ent
? pw_ent
->pw_name
: "UNKNOWN");
1045 out_uint (pformat
, prefix_len
, statbuf
->st_gid
);
1048 gw_ent
= getgrgid (statbuf
->st_gid
);
1049 out_string (pformat
, prefix_len
,
1050 gw_ent
? gw_ent
->gr_name
: "UNKNOWN");
1053 out_uint_x (pformat
, prefix_len
, major (statbuf
->st_rdev
));
1056 fail
|= out_mount_point (filename
, pformat
, prefix_len
, statbuf
);
1059 out_uint_x (pformat
, prefix_len
, minor (statbuf
->st_rdev
));
1062 out_int (pformat
, prefix_len
, statbuf
->st_size
);
1065 out_uint (pformat
, prefix_len
, ST_NBLOCKSIZE
);
1068 out_uint (pformat
, prefix_len
, ST_NBLOCKS (*statbuf
));
1071 out_uint (pformat
, prefix_len
, ST_BLKSIZE (*statbuf
));
1075 struct timespec t
= get_birthtime (fd
, filename
, statbuf
);
1077 out_string (pformat
, prefix_len
, "-");
1079 out_string (pformat
, prefix_len
, human_time (t
));
1083 out_epoch_sec (pformat
, prefix_len
, statbuf
,
1084 neg_to_zero (get_birthtime (fd
, filename
, statbuf
)));
1087 out_string (pformat
, prefix_len
, human_time (get_stat_atime (statbuf
)));
1090 out_epoch_sec (pformat
, prefix_len
, statbuf
, get_stat_atime (statbuf
));
1093 out_string (pformat
, prefix_len
, human_time (get_stat_mtime (statbuf
)));
1096 out_epoch_sec (pformat
, prefix_len
, statbuf
, get_stat_mtime (statbuf
));
1099 out_string (pformat
, prefix_len
, human_time (get_stat_ctime (statbuf
)));
1102 out_epoch_sec (pformat
, prefix_len
, statbuf
, get_stat_ctime (statbuf
));
1105 fail
|= out_file_context (pformat
, prefix_len
, filename
);
1108 fputc ('?', stdout
);
1114 /* Output a single-character \ escape. */
1117 print_esc_char (char c
)
1121 case 'a': /* Alert. */
1124 case 'b': /* Backspace. */
1127 case 'e': /* Escape. */
1130 case 'f': /* Form feed. */
1133 case 'n': /* New line. */
1136 case 'r': /* Carriage return. */
1139 case 't': /* Horizontal tab. */
1142 case 'v': /* Vertical tab. */
1149 error (0, 0, _("warning: unrecognized escape '\\%c'"), c
);
1155 /* Print the information specified by the format string, FORMAT,
1156 calling PRINT_FUNC for each %-directive encountered.
1157 Return zero upon success, nonzero upon failure. */
1158 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1159 print_it (char const *format
, int fd
, char const *filename
,
1160 bool (*print_func
) (char *, size_t, unsigned int,
1161 int, char const *, void const *),
1166 /* Add 2 to accommodate our conversion of the stat '%s' format string
1167 to the longer printf '%llu' one. */
1170 MAX_ADDITIONAL_BYTES
=
1171 (MAX (sizeof PRIdMAX
,
1172 MAX (sizeof PRIoMAX
, MAX (sizeof PRIuMAX
, sizeof PRIxMAX
)))
1175 size_t n_alloc
= strlen (format
) + MAX_ADDITIONAL_BYTES
+ 1;
1176 char *dest
= xmalloc (n_alloc
);
1178 for (b
= format
; *b
; b
++)
1184 size_t len
= strspn (b
+ 1, printf_flags
);
1185 char const *fmt_char
= b
+ len
+ 1;
1186 fmt_char
+= strspn (fmt_char
, digits
);
1187 if (*fmt_char
== '.')
1188 fmt_char
+= 1 + strspn (fmt_char
+ 1, digits
);
1189 len
= fmt_char
- (b
+ 1);
1190 unsigned int fmt_code
= *fmt_char
;
1191 memcpy (dest
, b
, len
+ 1);
1202 dest
[len
+ 1] = *fmt_char
;
1203 dest
[len
+ 2] = '\0';
1204 error (EXIT_FAILURE
, 0, _("%s: invalid directive"),
1210 fail
|= print_func (dest
, len
+ 1, fmt_code
,
1211 fd
, filename
, data
);
1218 if ( ! interpret_backslash_escapes
)
1226 int esc_value
= octtobin (*b
);
1227 int esc_length
= 1; /* number of octal digits */
1228 for (++b
; esc_length
< 3 && isodigit (*b
);
1231 esc_value
= esc_value
* 8 + octtobin (*b
);
1233 putchar (esc_value
);
1236 else if (*b
== 'x' && isxdigit (to_uchar (b
[1])))
1238 int esc_value
= hextobin (b
[1]); /* Value of \xhh escape. */
1239 /* A hexadecimal \xhh escape sequence must have
1240 1 or 2 hex. digits. */
1242 if (isxdigit (to_uchar (b
[1])))
1245 esc_value
= esc_value
* 16 + hextobin (*b
);
1247 putchar (esc_value
);
1249 else if (*b
== '\0')
1251 error (0, 0, _("warning: backslash at end of format"));
1253 /* Arrange to exit the loop. */
1258 print_esc_char (*b
);
1269 fputs (trailing_delim
, stdout
);
1274 /* Stat the file system and print what we find. */
1275 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1276 do_statfs (char const *filename
, char const *format
)
1278 STRUCT_STATVFS statfsbuf
;
1280 if (STREQ (filename
, "-"))
1282 error (0, 0, _("using %s to denote standard input does not work"
1283 " in file system mode"), quoteaf (filename
));
1287 if (STATFS (filename
, &statfsbuf
) != 0)
1289 error (0, errno
, _("cannot read file system information for %s"),
1290 quoteaf (filename
));
1294 bool fail
= print_it (format
, -1, filename
, print_statfs
, &statfsbuf
);
1298 /* stat the file and print what we find */
1299 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1300 do_stat (char const *filename
, char const *format
,
1301 char const *format2
)
1303 int fd
= STREQ (filename
, "-") ? 0 : -1;
1304 struct stat statbuf
;
1308 if (fstat (fd
, &statbuf
) != 0)
1310 error (0, errno
, _("cannot stat standard input"));
1314 /* We can't use the shorter
1315 (follow_links?stat:lstat) (filename, &statbug)
1316 since stat might be a function-like macro. */
1317 else if ((follow_links
1318 ? stat (filename
, &statbuf
)
1319 : lstat (filename
, &statbuf
)) != 0)
1321 error (0, errno
, _("cannot stat %s"), quoteaf (filename
));
1325 if (S_ISBLK (statbuf
.st_mode
) || S_ISCHR (statbuf
.st_mode
))
1328 bool fail
= print_it (format
, fd
, filename
, print_stat
, &statbuf
);
1332 /* Return an allocated format string in static storage that
1333 corresponds to whether FS and TERSE options were declared. */
1335 default_format (bool fs
, bool terse
, bool device
)
1341 format
= xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
1344 /* TRANSLATORS: This string uses format specifiers from
1345 'stat --help' with --file-system, and NOT from printf. */
1346 format
= xstrdup (_(" File: \"%n\"\n"
1347 " ID: %-8i Namelen: %-7l Type: %T\n"
1348 "Block size: %-10s Fundamental block size: %S\n"
1349 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
1350 "Inodes: Total: %-10c Free: %d\n"));
1357 if (0 < is_selinux_enabled ())
1358 format
= xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1359 " %X %Y %Z %W %o %C\n");
1361 format
= xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1362 " %X %Y %Z %W %o\n");
1367 /* TRANSLATORS: This string uses format specifiers from
1368 'stat --help' without --file-system, and NOT from printf. */
1369 format
= xstrdup (_("\
1371 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1377 /* TRANSLATORS: This string uses format specifiers from
1378 'stat --help' without --file-system, and NOT from printf. */
1379 format
= xasprintf ("%s%s", format
, _("\
1380 " "Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
1385 /* TRANSLATORS: This string uses format specifiers from
1386 'stat --help' without --file-system, and NOT from printf. */
1387 format
= xasprintf ("%s%s", format
, _("\
1388 " "Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1394 /* TRANSLATORS: This string uses format specifiers from
1395 'stat --help' without --file-system, and NOT from printf. */
1396 format
= xasprintf ("%s%s", format
, _("\
1397 " "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1401 if (0 < is_selinux_enabled ())
1404 /* TRANSLATORS: This string uses format specifiers from
1405 'stat --help' without --file-system, and NOT from printf. */
1406 format
= xasprintf ("%s%s", format
, _("Context: %C\n"));
1411 /* TRANSLATORS: This string uses format specifiers from
1412 'stat --help' without --file-system, and NOT from printf. */
1413 format
= xasprintf ("%s%s", format
,
1427 if (status
!= EXIT_SUCCESS
)
1431 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name
);
1433 Display file or file system status.\n\
1436 emit_mandatory_arg_note ();
1439 -L, --dereference follow links\n\
1440 -f, --file-system display file system status instead of file status\n\
1443 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1444 output a newline after each use of FORMAT\n\
1445 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1446 and do not output a mandatory trailing newline;\n\
1447 if you want a newline, include \\n in FORMAT\n\
1448 -t, --terse print the information in terse form\n\
1450 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
1451 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
1454 The valid format sequences for files (without --file-system):\n\
1456 %a access rights in octal (note '#' and '0' printf flags)\n\
1457 %A access rights in human readable form\n\
1458 %b number of blocks allocated (see %B)\n\
1459 %B the size in bytes of each block reported by %b\n\
1460 %C SELinux security context string\n\
1463 %d device number in decimal\n\
1464 %D device number in hex\n\
1465 %f raw mode in hex\n\
1467 %g group ID of owner\n\
1468 %G group name of owner\n\
1471 %h number of hard links\n\
1475 %N quoted file name with dereference if symbolic link\n\
1476 %o optimal I/O transfer size hint\n\
1477 %s total size, in bytes\n\
1478 %t major device type in hex, for character/block device special files\n\
1479 %T minor device type in hex, for character/block device special files\n\
1482 %u user ID of owner\n\
1483 %U user name of owner\n\
1484 %w time of file birth, human-readable; - if unknown\n\
1485 %W time of file birth, seconds since Epoch; 0 if unknown\n\
1486 %x time of last access, human-readable\n\
1487 %X time of last access, seconds since Epoch\n\
1488 %y time of last data modification, human-readable\n\
1489 %Y time of last data modification, seconds since Epoch\n\
1490 %z time of last status change, human-readable\n\
1491 %Z time of last status change, seconds since Epoch\n\
1496 Valid format sequences for file systems:\n\
1498 %a free blocks available to non-superuser\n\
1499 %b total data blocks in file system\n\
1500 %c total file nodes in file system\n\
1501 %d free file nodes in file system\n\
1502 %f free blocks in file system\n\
1505 %i file system ID in hex\n\
1506 %l maximum length of filenames\n\
1508 %s block size (for faster transfers)\n\
1509 %S fundamental block size (for block counts)\n\
1510 %t file system type in hex\n\
1511 %T file system type in human readable form\n\
1513 printf (USAGE_BUILTIN_WARNING
, PROGRAM_NAME
);
1514 emit_ancillary_info (PROGRAM_NAME
);
1520 main (int argc
, char *argv
[])
1526 char *format
= NULL
;
1530 initialize_main (&argc
, &argv
);
1531 set_program_name (argv
[0]);
1532 setlocale (LC_ALL
, "");
1533 bindtextdomain (PACKAGE
, LOCALEDIR
);
1534 textdomain (PACKAGE
);
1536 struct lconv
const *locale
= localeconv ();
1537 decimal_point
= (locale
->decimal_point
[0] ? locale
->decimal_point
: ".");
1538 decimal_point_len
= strlen (decimal_point
);
1540 atexit (close_stdout
);
1542 while ((c
= getopt_long (argc
, argv
, "c:fLt", long_options
, NULL
)) != -1)
1548 interpret_backslash_escapes
= true;
1549 trailing_delim
= "";
1554 interpret_backslash_escapes
= false;
1555 trailing_delim
= "\n";
1559 follow_links
= true;
1570 case_GETOPT_HELP_CHAR
;
1572 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
1575 usage (EXIT_FAILURE
);
1581 error (0, 0, _("missing operand"));
1582 usage (EXIT_FAILURE
);
1589 format
= default_format (fs
, terse
, false);
1590 format2
= default_format (fs
, terse
, true);
1593 for (i
= optind
; i
< argc
; i
++)
1595 ? do_statfs (argv
[i
], format
)
1596 : do_stat (argv
[i
], format
, format2
));
1598 return ok
? EXIT_SUCCESS
: EXIT_FAILURE
;