1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2018 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 <https://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"
65 #include "file-type.h"
69 #include "mountlist.h"
71 #include "stat-size.h"
72 #include "stat-time.h"
74 #include "find-mount-point.h"
75 #include "xvasprintf.h"
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 STRUCT_STATVFS struct statvfs64
85 # define STATFS statvfs64
87 # define STRUCT_STATVFS struct statvfs
88 # define STATFS statvfs
90 # define STATFS_FRSIZE(S) ((S)->f_frsize)
92 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
93 # if HAVE_STRUCT_STATFS_F_NAMELEN
94 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
95 # elif HAVE_STRUCT_STATFS_F_NAMEMAX
96 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
98 # define STATFS statfs
99 # if HAVE_OS_H /* BeOS */
100 /* BeOS has a statvfs function, but it does not return sensible values
101 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
102 f_fstypename. Use 'struct fs_info' instead. */
103 static int ATTRIBUTE_WARN_UNUSED_RESULT
104 statfs (char const *filename
, struct fs_info
*buf
)
106 dev_t device
= dev_for_path (filename
);
109 errno
= (device
== B_ENTRY_NOT_FOUND
? ENOENT
110 : device
== B_BAD_VALUE
? EINVAL
111 : device
== B_NAME_TOO_LONG
? ENAMETOOLONG
112 : device
== B_NO_MEMORY
? ENOMEM
113 : device
== B_FILE_ERROR
? EIO
117 /* If successful, buf->dev will be == device. */
118 return fs_stat_dev (device
, buf
);
121 # define f_blocks total_blocks
122 # define f_bfree free_blocks
123 # define f_bavail free_blocks
124 # define f_bsize io_size
125 # define f_files total_nodes
126 # define f_ffree free_nodes
127 # define STRUCT_STATVFS struct fs_info
128 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
129 # define STATFS_FRSIZE(S) ((S)->block_size)
131 # define STRUCT_STATVFS struct statfs
132 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
133 # if HAVE_STRUCT_STATFS_F_FRSIZE
134 # define STATFS_FRSIZE(S) ((S)->f_frsize)
136 # define STATFS_FRSIZE(S) 0
142 # define OUT_NAMEMAX out_uint
144 /* Depending on whether statvfs or statfs is used,
145 neither f_namemax or f_namelen may be available. */
146 # define SB_F_NAMEMAX(S) "?"
147 # define OUT_NAMEMAX out_string
150 #if HAVE_STRUCT_STATVFS_F_BASETYPE
151 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
153 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
154 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
155 # elif HAVE_OS_H /* BeOS */
156 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
162 # include <sys/nvpair.h>
165 /* FIXME: these are used by printf.c, too */
166 #define isodigit(c) ('0' <= (c) && (c) <= '7')
167 #define octtobin(c) ((c) - '0')
168 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
169 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
171 static char const digits
[] = "0123456789";
173 /* Flags that are portable for use in printf, for at least one
174 conversion specifier; make_format removes unportable flags as
175 needed for particular specifiers. The glibc 2.2 extension "I" is
176 listed here; it is removed by make_format because it has undefined
177 behavior elsewhere and because it is incompatible with
179 static char const printf_flags
[] = "'-+ #0I";
181 /* Formats for the --terse option. */
182 static char const fmt_terse_fs
[] = "%n %i %l %t %s %S %b %f %a %c %d\n";
183 static char const fmt_terse_regular
[] = "%n %s %b %f %u %g %D %i %h %t %T"
185 static char const fmt_terse_selinux
[] = "%n %s %b %f %u %g %D %i %h %t %T"
186 " %X %Y %Z %W %o %C\n";
188 #define PROGRAM_NAME "stat"
190 #define AUTHORS proper_name ("Michael Meskes")
194 PRINTF_OPTION
= CHAR_MAX
+ 1
197 static struct option
const long_options
[] =
199 {"dereference", no_argument
, NULL
, 'L'},
200 {"file-system", no_argument
, NULL
, 'f'},
201 {"format", required_argument
, NULL
, 'c'},
202 {"printf", required_argument
, NULL
, PRINTF_OPTION
},
203 {"terse", no_argument
, NULL
, 't'},
204 {GETOPT_HELP_OPTION_DECL
},
205 {GETOPT_VERSION_OPTION_DECL
},
209 /* Whether to follow symbolic links; True for --dereference (-L). */
210 static bool follow_links
;
212 /* Whether to interpret backslash-escape sequences.
213 True for --printf=FMT, not for --format=FMT (-c). */
214 static bool interpret_backslash_escapes
;
216 /* The trailing delimiter string:
217 "" for --printf=FMT, "\n" for --format=FMT (-c). */
218 static char const *trailing_delim
= "";
220 /* The representation of the decimal point in the current locale. */
221 static char const *decimal_point
;
222 static size_t decimal_point_len
;
224 /* Return the type of the specified file system.
225 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
226 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
227 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
228 Still others have neither and have to get by with f_type (GNU/Linux).
229 But f_type may only exist in statfs (Cygwin). */
230 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
231 human_fstype (STRUCT_STATVFS
const *statfsbuf
)
233 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
234 return statfsbuf
->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
;
236 switch (statfsbuf
->f_type
)
238 # if defined __linux__
240 /* Compare with what's in libc:
241 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
242 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
243 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
244 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
246 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
247 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
249 diff -u sym_stat sym_libc
252 /* Also compare with the list in "man 2 statfs" using the
253 fs-magic-compare make target. */
255 /* IMPORTANT NOTE: Each of the following 'case S_MAGIC_...:'
256 statements must be followed by a hexadecimal constant in
257 a comment. The S_MAGIC_... name and constant are automatically
258 combined to produce the #define directives in fs.h. */
260 case S_MAGIC_AAFS
: /* 0x5A3C69F0 local */
262 case S_MAGIC_ACFS
: /* 0x61636673 remote */
264 case S_MAGIC_ADFS
: /* 0xADF5 local */
266 case S_MAGIC_AFFS
: /* 0xADFF local */
268 case S_MAGIC_AFS
: /* 0x5346414F remote */
270 case S_MAGIC_ANON_INODE_FS
: /* 0x09041934 local */
271 return "anon-inode FS";
272 case S_MAGIC_AUFS
: /* 0x61756673 remote */
273 /* FIXME: change syntax or add an optional attribute like "inotify:no".
274 The above is labeled as "remote" so that tail always uses polling,
275 but this isn't really a remote file system type. */
277 case S_MAGIC_AUTOFS
: /* 0x0187 local */
279 case S_MAGIC_BALLOON_KVM
: /* 0x13661366 local */
280 return "balloon-kvm-fs";
281 case S_MAGIC_BEFS
: /* 0x42465331 local */
283 case S_MAGIC_BDEVFS
: /* 0x62646576 local */
285 case S_MAGIC_BFS
: /* 0x1BADFACE local */
287 case S_MAGIC_BPF_FS
: /* 0xCAFE4A11 local */
289 case S_MAGIC_BINFMTFS
: /* 0x42494E4D local */
290 return "binfmt_misc";
291 case S_MAGIC_BTRFS
: /* 0x9123683E local */
293 case S_MAGIC_BTRFS_TEST
: /* 0x73727279 local */
295 case S_MAGIC_CEPH
: /* 0x00C36400 remote */
297 case S_MAGIC_CGROUP
: /* 0x0027E0EB local */
299 case S_MAGIC_CGROUP2
: /* 0x63677270 local */
301 case S_MAGIC_CIFS
: /* 0xFF534D42 remote */
303 case S_MAGIC_CODA
: /* 0x73757245 remote */
305 case S_MAGIC_COH
: /* 0x012FF7B7 local */
307 case S_MAGIC_CONFIGFS
: /* 0x62656570 local */
309 case S_MAGIC_CRAMFS
: /* 0x28CD3D45 local */
311 case S_MAGIC_CRAMFS_WEND
: /* 0x453DCD28 local */
312 return "cramfs-wend";
313 case S_MAGIC_DAXFS
: /* 0x64646178 local */
315 case S_MAGIC_DEBUGFS
: /* 0x64626720 local */
317 case S_MAGIC_DEVFS
: /* 0x1373 local */
319 case S_MAGIC_DEVPTS
: /* 0x1CD1 local */
321 case S_MAGIC_ECRYPTFS
: /* 0xF15F local */
323 case S_MAGIC_EFIVARFS
: /* 0xDE5E81E4 local */
325 case S_MAGIC_EFS
: /* 0x00414A53 local */
327 case S_MAGIC_EXOFS
: /* 0x5DF5 local */
329 case S_MAGIC_EXT
: /* 0x137D local */
331 case S_MAGIC_EXT2
: /* 0xEF53 local */
333 case S_MAGIC_EXT2_OLD
: /* 0xEF51 local */
335 case S_MAGIC_F2FS
: /* 0xF2F52010 local */
337 case S_MAGIC_FAT
: /* 0x4006 local */
339 case S_MAGIC_FHGFS
: /* 0x19830326 remote */
341 case S_MAGIC_FUSEBLK
: /* 0x65735546 remote */
343 case S_MAGIC_FUSECTL
: /* 0x65735543 remote */
345 case S_MAGIC_FUTEXFS
: /* 0x0BAD1DEA local */
347 case S_MAGIC_GFS
: /* 0x01161970 remote */
349 case S_MAGIC_GPFS
: /* 0x47504653 remote */
351 case S_MAGIC_HFS
: /* 0x4244 local */
353 case S_MAGIC_HFS_PLUS
: /* 0x482B local */
355 case S_MAGIC_HFS_X
: /* 0x4858 local */
357 case S_MAGIC_HOSTFS
: /* 0x00C0FFEE local */
359 case S_MAGIC_HPFS
: /* 0xF995E849 local */
361 case S_MAGIC_HUGETLBFS
: /* 0x958458F6 local */
363 case S_MAGIC_MTD_INODE_FS
: /* 0x11307854 local */
365 case S_MAGIC_IBRIX
: /* 0x013111A8 remote */
367 case S_MAGIC_INOTIFYFS
: /* 0x2BAD1DEA local */
369 case S_MAGIC_ISOFS
: /* 0x9660 local */
371 case S_MAGIC_ISOFS_R_WIN
: /* 0x4004 local */
373 case S_MAGIC_ISOFS_WIN
: /* 0x4000 local */
375 case S_MAGIC_JFFS
: /* 0x07C0 local */
377 case S_MAGIC_JFFS2
: /* 0x72B6 local */
379 case S_MAGIC_JFS
: /* 0x3153464A local */
381 case S_MAGIC_KAFS
: /* 0x6B414653 remote */
383 case S_MAGIC_LOGFS
: /* 0xC97E8168 local */
385 case S_MAGIC_LUSTRE
: /* 0x0BD00BD0 remote */
387 case S_MAGIC_M1FS
: /* 0x5346314D local */
389 case S_MAGIC_MINIX
: /* 0x137F local */
391 case S_MAGIC_MINIX_30
: /* 0x138F local */
392 return "minix (30 char.)";
393 case S_MAGIC_MINIX_V2
: /* 0x2468 local */
395 case S_MAGIC_MINIX_V2_30
: /* 0x2478 local */
396 return "minix v2 (30 char.)";
397 case S_MAGIC_MINIX_V3
: /* 0x4D5A local */
399 case S_MAGIC_MQUEUE
: /* 0x19800202 local */
401 case S_MAGIC_MSDOS
: /* 0x4D44 local */
403 case S_MAGIC_NCP
: /* 0x564C remote */
405 case S_MAGIC_NFS
: /* 0x6969 remote */
407 case S_MAGIC_NFSD
: /* 0x6E667364 remote */
409 case S_MAGIC_NILFS
: /* 0x3434 local */
411 case S_MAGIC_NSFS
: /* 0x6E736673 local */
413 case S_MAGIC_NTFS
: /* 0x5346544E local */
415 case S_MAGIC_OPENPROM
: /* 0x9FA1 local */
417 case S_MAGIC_OCFS2
: /* 0x7461636F remote */
419 case S_MAGIC_OVERLAYFS
: /* 0x794C7630 remote */
420 /* This may overlay remote file systems.
421 Also there have been issues reported with inotify and overlayfs,
422 so mark as "remote" so that polling is used. */
424 case S_MAGIC_PANFS
: /* 0xAAD7AAEA remote */
426 case S_MAGIC_PIPEFS
: /* 0x50495045 remote */
427 /* FIXME: change syntax or add an optional attribute like "inotify:no".
428 pipefs and prlfs are labeled as "remote" so that tail always polls,
429 but these aren't really remote file system types. */
431 case S_MAGIC_PRL_FS
: /* 0x7C7C6673 remote */
433 case S_MAGIC_PROC
: /* 0x9FA0 local */
435 case S_MAGIC_PSTOREFS
: /* 0x6165676C local */
437 case S_MAGIC_QNX4
: /* 0x002F local */
439 case S_MAGIC_QNX6
: /* 0x68191122 local */
441 case S_MAGIC_RAMFS
: /* 0x858458F6 local */
443 case S_MAGIC_RDTGROUP
: /* 0x07655821 local */
445 case S_MAGIC_REISERFS
: /* 0x52654973 local */
447 case S_MAGIC_ROMFS
: /* 0x7275 local */
449 case S_MAGIC_RPC_PIPEFS
: /* 0x67596969 local */
451 case S_MAGIC_SECURITYFS
: /* 0x73636673 local */
453 case S_MAGIC_SELINUX
: /* 0xF97CFF8C local */
455 case S_MAGIC_SMACK
: /* 0x43415D53 local */
457 case S_MAGIC_SMB
: /* 0x517B remote */
459 case S_MAGIC_SMB2
: /* 0xFE534D42 remote */
461 case S_MAGIC_SNFS
: /* 0xBEEFDEAD remote */
463 case S_MAGIC_SOCKFS
: /* 0x534F434B local */
465 case S_MAGIC_SQUASHFS
: /* 0x73717368 local */
467 case S_MAGIC_SYSFS
: /* 0x62656572 local */
469 case S_MAGIC_SYSV2
: /* 0x012FF7B6 local */
471 case S_MAGIC_SYSV4
: /* 0x012FF7B5 local */
473 case S_MAGIC_TMPFS
: /* 0x01021994 local */
475 case S_MAGIC_TRACEFS
: /* 0x74726163 local */
477 case S_MAGIC_UBIFS
: /* 0x24051905 local */
479 case S_MAGIC_UDF
: /* 0x15013346 local */
481 case S_MAGIC_UFS
: /* 0x00011954 local */
483 case S_MAGIC_UFS_BYTESWAPPED
: /* 0x54190100 local */
485 case S_MAGIC_USBDEVFS
: /* 0x9FA2 local */
487 case S_MAGIC_V9FS
: /* 0x01021997 local */
489 case S_MAGIC_VMHGFS
: /* 0xBACBACBC remote */
491 case S_MAGIC_VXFS
: /* 0xA501FCF5 remote */
492 /* Veritas File System can run in single instance or clustered mode,
493 so mark as remote to cater for the latter case. */
495 case S_MAGIC_VZFS
: /* 0x565A4653 local */
497 case S_MAGIC_WSLFS
: /* 0x53464846 local */
499 case S_MAGIC_XENFS
: /* 0xABBA1974 local */
501 case S_MAGIC_XENIX
: /* 0x012FF7B4 local */
503 case S_MAGIC_XFS
: /* 0x58465342 local */
505 case S_MAGIC_XIAFS
: /* 0x012FD16D local */
507 case S_MAGIC_ZFS
: /* 0x2FC12FC1 local */
509 case S_MAGIC_ZSMALLOC
: /* 0x58295829 local */
571 unsigned long int type
= statfsbuf
->f_type
;
572 static char buf
[sizeof "UNKNOWN (0x%lx)" - 3
573 + (sizeof type
* CHAR_BIT
+ 3) / 4];
574 sprintf (buf
, "UNKNOWN (0x%lx)", type
);
581 static char * ATTRIBUTE_WARN_UNUSED_RESULT
582 human_access (struct stat
const *statbuf
)
584 static char modebuf
[12];
585 filemodestring (statbuf
, modebuf
);
590 static char * ATTRIBUTE_WARN_UNUSED_RESULT
591 human_time (struct timespec t
)
593 /* STR must be at least this big, either because localtime_rz fails,
594 or because the time zone is truly outlandish so that %z expands
596 enum { intmax_bufsize
= INT_BUFSIZE_BOUND (intmax_t) };
598 static char str
[intmax_bufsize
599 + INT_STRLEN_BOUND (int) /* YYYY */
600 + 1 /* because YYYY might equal INT_MAX + 1900 */
601 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +"];
602 static timezone_t tz
;
604 tz
= tzalloc (getenv ("TZ"));
607 if (localtime_rz (tz
, &t
.tv_sec
, &tm
))
608 nstrftime (str
, sizeof str
, "%Y-%m-%d %H:%M:%S.%N %z", &tm
, tz
, ns
);
611 char secbuf
[INT_BUFSIZE_BOUND (intmax_t)];
612 sprintf (str
, "%s.%09d", timetostr (t
.tv_sec
, secbuf
), ns
);
617 /* PFORMAT points to a '%' followed by a prefix of a format, all of
618 size PREFIX_LEN. The flags allowed for this format are
619 ALLOWED_FLAGS; remove other printf flags from the prefix, then
622 make_format (char *pformat
, size_t prefix_len
, char const *allowed_flags
,
625 char *dst
= pformat
+ 1;
627 char const *srclim
= pformat
+ prefix_len
;
628 for (src
= dst
; src
< srclim
&& strchr (printf_flags
, *src
); src
++)
629 if (strchr (allowed_flags
, *src
))
633 strcpy (dst
, suffix
);
637 out_string (char *pformat
, size_t prefix_len
, char const *arg
)
639 make_format (pformat
, prefix_len
, "-", "s");
640 printf (pformat
, arg
);
643 out_int (char *pformat
, size_t prefix_len
, intmax_t arg
)
645 make_format (pformat
, prefix_len
, "'-+ 0", PRIdMAX
);
646 return printf (pformat
, arg
);
649 out_uint (char *pformat
, size_t prefix_len
, uintmax_t arg
)
651 make_format (pformat
, prefix_len
, "'-0", PRIuMAX
);
652 return printf (pformat
, arg
);
655 out_uint_o (char *pformat
, size_t prefix_len
, uintmax_t arg
)
657 make_format (pformat
, prefix_len
, "-#0", PRIoMAX
);
658 printf (pformat
, arg
);
661 out_uint_x (char *pformat
, size_t prefix_len
, uintmax_t arg
)
663 make_format (pformat
, prefix_len
, "-#0", PRIxMAX
);
664 printf (pformat
, arg
);
667 out_minus_zero (char *pformat
, size_t prefix_len
)
669 make_format (pformat
, prefix_len
, "'-+ 0", ".0f");
670 return printf (pformat
, -0.25);
673 /* Output the number of seconds since the Epoch, using a format that
674 acts like printf's %f format. */
676 out_epoch_sec (char *pformat
, size_t prefix_len
,
677 struct stat
const *statbuf _GL_UNUSED
,
680 char *dot
= memchr (pformat
, '.', prefix_len
);
681 size_t sec_prefix_len
= prefix_len
;
684 bool frac_left_adjust
= false;
688 sec_prefix_len
= dot
- pformat
;
689 pformat
[prefix_len
] = '\0';
691 if (ISDIGIT (dot
[1]))
693 long int lprec
= strtol (dot
+ 1, NULL
, 10);
694 precision
= (lprec
<= INT_MAX
? lprec
: INT_MAX
);
701 if (precision
&& ISDIGIT (dot
[-1]))
703 /* If a nontrivial width is given, subtract the width of the
704 decimal point and PRECISION digits that will be output
711 while (ISDIGIT (p
[-1]));
713 long int lwidth
= strtol (p
, NULL
, 10);
714 width
= (lwidth
<= INT_MAX
? lwidth
: INT_MAX
);
718 sec_prefix_len
= p
- pformat
;
719 int w_d
= (decimal_point_len
< width
720 ? width
- decimal_point_len
724 int w
= w_d
- precision
;
728 for (char const *src
= dst
; src
< p
; src
++)
731 frac_left_adjust
= true;
737 + (frac_left_adjust
? 0 : sprintf (dst
, "%d", w
)));
745 for (int i
= precision
; i
< 9; i
++)
747 int frac_sec
= arg
.tv_nsec
/ divisor
;
750 if (TYPE_SIGNED (time_t))
752 bool minus_zero
= false;
753 if (arg
.tv_sec
< 0 && arg
.tv_nsec
!= 0)
755 int frac_sec_modulus
= 1000000000 / divisor
;
756 frac_sec
= (frac_sec_modulus
- frac_sec
757 - (arg
.tv_nsec
% divisor
!= 0));
758 arg
.tv_sec
+= (frac_sec
!= 0);
759 minus_zero
= (arg
.tv_sec
== 0);
761 int_len
= (minus_zero
762 ? out_minus_zero (pformat
, sec_prefix_len
)
763 : out_int (pformat
, sec_prefix_len
, arg
.tv_sec
));
766 int_len
= out_uint (pformat
, sec_prefix_len
, arg
.tv_sec
);
770 int prec
= (precision
< 9 ? precision
: 9);
771 int trailing_prec
= precision
- prec
;
772 int ilen
= (int_len
< 0 ? 0 : int_len
);
773 int trailing_width
= (ilen
< width
&& decimal_point_len
< width
- ilen
774 ? width
- ilen
- decimal_point_len
- prec
776 printf ("%s%.*d%-*.*d", decimal_point
, prec
, frac_sec
,
777 trailing_width
, trailing_prec
, 0);
781 /* Print the context information of FILENAME, and return true iff the
782 context could not be obtained. */
783 static bool ATTRIBUTE_WARN_UNUSED_RESULT
784 out_file_context (char *pformat
, size_t prefix_len
, char const *filename
)
790 ? getfilecon (filename
, &scontext
)
791 : lgetfilecon (filename
, &scontext
)) < 0)
793 error (0, errno
, _("failed to get security context of %s"),
798 strcpy (pformat
+ prefix_len
, "s");
799 printf (pformat
, (scontext
? scontext
: "?"));
805 /* Print statfs info. Return zero upon success, nonzero upon failure. */
806 static bool ATTRIBUTE_WARN_UNUSED_RESULT
807 print_statfs (char *pformat
, size_t prefix_len
, unsigned int m
,
808 int fd
, char const *filename
,
811 STRUCT_STATVFS
const *statfsbuf
= data
;
817 out_string (pformat
, prefix_len
, filename
);
822 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
823 uintmax_t fsid
= statfsbuf
->f_fsid
;
825 typedef unsigned int fsid_word
;
826 verify (alignof (STRUCT_STATVFS
) % alignof (fsid_word
) == 0);
827 verify (offsetof (STRUCT_STATVFS
, f_fsid
) % alignof (fsid_word
) == 0);
828 verify (sizeof statfsbuf
->f_fsid
% alignof (fsid_word
) == 0);
829 fsid_word
const *p
= (fsid_word
*) &statfsbuf
->f_fsid
;
831 /* Assume a little-endian word order, as that is compatible
832 with glibc's statvfs implementation. */
834 int words
= sizeof statfsbuf
->f_fsid
/ sizeof *p
;
835 for (int i
= 0; i
< words
&& i
* sizeof *p
< sizeof fsid
; i
++)
837 uintmax_t u
= p
[words
- 1 - i
];
838 fsid
|= u
<< (i
* CHAR_BIT
* sizeof *p
);
841 out_uint_x (pformat
, prefix_len
, fsid
);
846 OUT_NAMEMAX (pformat
, prefix_len
, SB_F_NAMEMAX (statfsbuf
));
849 #if HAVE_STRUCT_STATXFS_F_TYPE
850 out_uint_x (pformat
, prefix_len
, statfsbuf
->f_type
);
856 out_string (pformat
, prefix_len
, human_fstype (statfsbuf
));
859 out_int (pformat
, prefix_len
, statfsbuf
->f_blocks
);
862 out_int (pformat
, prefix_len
, statfsbuf
->f_bfree
);
865 out_int (pformat
, prefix_len
, statfsbuf
->f_bavail
);
868 out_uint (pformat
, prefix_len
, statfsbuf
->f_bsize
);
872 uintmax_t frsize
= STATFS_FRSIZE (statfsbuf
);
874 frsize
= statfsbuf
->f_bsize
;
875 out_uint (pformat
, prefix_len
, frsize
);
879 out_uint (pformat
, prefix_len
, statfsbuf
->f_files
);
882 out_int (pformat
, prefix_len
, statfsbuf
->f_ffree
);
891 /* Return any bind mounted source for a path.
892 The caller should not free the returned buffer.
893 Return NULL if no bind mount found. */
894 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
895 find_bind_mount (char const * name
)
897 char const * bind_mount
= NULL
;
899 static struct mount_entry
*mount_list
;
900 static bool tried_mount_list
= false;
901 if (!tried_mount_list
) /* attempt/warn once per process. */
903 if (!(mount_list
= read_file_system_list (false)))
904 error (0, errno
, "%s", _("cannot read table of mounted file systems"));
905 tried_mount_list
= true;
908 struct stat name_stats
;
909 if (stat (name
, &name_stats
) != 0)
912 struct mount_entry
*me
;
913 for (me
= mount_list
; me
; me
= me
->me_next
)
915 if (me
->me_dummy
&& me
->me_devname
[0] == '/'
916 && STREQ (me
->me_mountdir
, name
))
918 struct stat dev_stats
;
920 if (stat (me
->me_devname
, &dev_stats
) == 0
921 && SAME_INODE (name_stats
, dev_stats
))
923 bind_mount
= me
->me_devname
;
932 /* Print mount point. Return zero upon success, nonzero upon failure. */
933 static bool ATTRIBUTE_WARN_UNUSED_RESULT
934 out_mount_point (char const *filename
, char *pformat
, size_t prefix_len
,
935 const struct stat
*statp
)
938 char const *np
= "?", *bp
= NULL
;
942 /* Look for bind mounts first. Note we output the immediate alias,
943 rather than further resolving to a base device mount point. */
944 if (follow_links
|| !S_ISLNK (statp
->st_mode
))
946 char *resolved
= canonicalize_file_name (filename
);
949 error (0, errno
, _("failed to canonicalize %s"), quoteaf (filename
));
950 goto print_mount_point
;
952 bp
= find_bind_mount (resolved
);
957 goto print_mount_point
;
961 /* If there is no direct bind mount, then navigate
962 back up the tree looking for a device change.
963 Note we don't detect if any of the directory components
964 are bind mounted to the same device, but that's OK
965 since we've not directly queried them. */
966 if ((mp
= find_mount_point (filename
, statp
)))
968 /* This dir might be bind mounted to another device,
969 so we resolve the bound source in that case also. */
970 bp
= find_bind_mount (mp
);
976 out_string (pformat
, prefix_len
, bp
? bp
: mp
? mp
: np
);
981 static struct timespec
982 get_birthtime (int fd
, char const *filename
, struct stat
const *st
)
984 struct timespec ts
= get_stat_birthtime (st
);
991 ? getattrat (AT_FDCWD
, XATTR_VIEW_READWRITE
, filename
, &response
)
992 : fgetattr (fd
, XATTR_VIEW_READWRITE
, &response
))
997 if (nvlist_lookup_uint64_array (response
, A_CRTIME
, &val
, &n
) == 0
999 && val
[0] <= TYPE_MAXIMUM (time_t)
1000 && val
[1] < 1000000000 * 2 /* for leap seconds */)
1003 ts
.tv_nsec
= val
[1];
1005 nvlist_free (response
);
1013 /* Map a TS with negative TS.tv_nsec to {0,0}. */
1014 static inline struct timespec
1015 neg_to_zero (struct timespec ts
)
1017 if (0 <= ts
.tv_nsec
)
1019 struct timespec z
= {0, 0};
1023 /* Set the quoting style default if the environment variable
1024 QUOTING_STYLE is set. */
1027 getenv_quoting_style (void)
1029 char const *q_style
= getenv ("QUOTING_STYLE");
1032 int i
= ARGMATCH (q_style
, quoting_style_args
, quoting_style_vals
);
1034 set_quoting_style (NULL
, quoting_style_vals
[i
]);
1037 set_quoting_style (NULL
, shell_escape_always_quoting_style
);
1038 error (0, 0, _("ignoring invalid value of environment "
1039 "variable QUOTING_STYLE: %s"), quote (q_style
));
1043 set_quoting_style (NULL
, shell_escape_always_quoting_style
);
1046 /* Equivalent to quotearg(), but explicit to avoid syntax checks. */
1047 #define quoteN(x) quotearg_style (get_quoting_style (NULL), x)
1049 /* Print stat info. Return zero upon success, nonzero upon failure. */
1051 print_stat (char *pformat
, size_t prefix_len
, unsigned int m
,
1052 int fd
, char const *filename
, void const *data
)
1054 struct stat
*statbuf
= (struct stat
*) data
;
1055 struct passwd
*pw_ent
;
1056 struct group
*gw_ent
;
1062 out_string (pformat
, prefix_len
, filename
);
1065 out_string (pformat
, prefix_len
, quoteN (filename
));
1066 if (S_ISLNK (statbuf
->st_mode
))
1068 char *linkname
= areadlink_with_size (filename
, statbuf
->st_size
);
1069 if (linkname
== NULL
)
1071 error (0, errno
, _("cannot read symbolic link %s"),
1072 quoteaf (filename
));
1076 out_string (pformat
, prefix_len
, quoteN (linkname
));
1081 out_uint (pformat
, prefix_len
, statbuf
->st_dev
);
1084 out_uint_x (pformat
, prefix_len
, statbuf
->st_dev
);
1087 out_uint (pformat
, prefix_len
, statbuf
->st_ino
);
1090 out_uint_o (pformat
, prefix_len
, statbuf
->st_mode
& CHMOD_MODE_BITS
);
1093 out_string (pformat
, prefix_len
, human_access (statbuf
));
1096 out_uint_x (pformat
, prefix_len
, statbuf
->st_mode
);
1099 out_string (pformat
, prefix_len
, file_type (statbuf
));
1102 out_uint (pformat
, prefix_len
, statbuf
->st_nlink
);
1105 out_uint (pformat
, prefix_len
, statbuf
->st_uid
);
1108 pw_ent
= getpwuid (statbuf
->st_uid
);
1109 out_string (pformat
, prefix_len
,
1110 pw_ent
? pw_ent
->pw_name
: "UNKNOWN");
1113 out_uint (pformat
, prefix_len
, statbuf
->st_gid
);
1116 gw_ent
= getgrgid (statbuf
->st_gid
);
1117 out_string (pformat
, prefix_len
,
1118 gw_ent
? gw_ent
->gr_name
: "UNKNOWN");
1121 out_uint_x (pformat
, prefix_len
, major (statbuf
->st_rdev
));
1124 fail
|= out_mount_point (filename
, pformat
, prefix_len
, statbuf
);
1127 out_uint_x (pformat
, prefix_len
, minor (statbuf
->st_rdev
));
1130 out_int (pformat
, prefix_len
, statbuf
->st_size
);
1133 out_uint (pformat
, prefix_len
, ST_NBLOCKSIZE
);
1136 out_uint (pformat
, prefix_len
, ST_NBLOCKS (*statbuf
));
1139 out_uint (pformat
, prefix_len
, ST_BLKSIZE (*statbuf
));
1143 struct timespec t
= get_birthtime (fd
, filename
, statbuf
);
1145 out_string (pformat
, prefix_len
, "-");
1147 out_string (pformat
, prefix_len
, human_time (t
));
1151 out_epoch_sec (pformat
, prefix_len
, statbuf
,
1152 neg_to_zero (get_birthtime (fd
, filename
, statbuf
)));
1155 out_string (pformat
, prefix_len
, human_time (get_stat_atime (statbuf
)));
1158 out_epoch_sec (pformat
, prefix_len
, statbuf
, get_stat_atime (statbuf
));
1161 out_string (pformat
, prefix_len
, human_time (get_stat_mtime (statbuf
)));
1164 out_epoch_sec (pformat
, prefix_len
, statbuf
, get_stat_mtime (statbuf
));
1167 out_string (pformat
, prefix_len
, human_time (get_stat_ctime (statbuf
)));
1170 out_epoch_sec (pformat
, prefix_len
, statbuf
, get_stat_ctime (statbuf
));
1173 fail
|= out_file_context (pformat
, prefix_len
, filename
);
1176 fputc ('?', stdout
);
1182 /* Output a single-character \ escape. */
1185 print_esc_char (char c
)
1189 case 'a': /* Alert. */
1192 case 'b': /* Backspace. */
1195 case 'e': /* Escape. */
1198 case 'f': /* Form feed. */
1201 case 'n': /* New line. */
1204 case 'r': /* Carriage return. */
1207 case 't': /* Horizontal tab. */
1210 case 'v': /* Vertical tab. */
1217 error (0, 0, _("warning: unrecognized escape '\\%c'"), c
);
1223 /* Print the information specified by the format string, FORMAT,
1224 calling PRINT_FUNC for each %-directive encountered.
1225 Return zero upon success, nonzero upon failure. */
1226 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1227 print_it (char const *format
, int fd
, char const *filename
,
1228 bool (*print_func
) (char *, size_t, unsigned int,
1229 int, char const *, void const *),
1234 /* Add 2 to accommodate our conversion of the stat '%s' format string
1235 to the longer printf '%llu' one. */
1238 MAX_ADDITIONAL_BYTES
=
1239 (MAX (sizeof PRIdMAX
,
1240 MAX (sizeof PRIoMAX
, MAX (sizeof PRIuMAX
, sizeof PRIxMAX
)))
1243 size_t n_alloc
= strlen (format
) + MAX_ADDITIONAL_BYTES
+ 1;
1244 char *dest
= xmalloc (n_alloc
);
1246 for (b
= format
; *b
; b
++)
1252 size_t len
= strspn (b
+ 1, printf_flags
);
1253 char const *fmt_char
= b
+ len
+ 1;
1254 fmt_char
+= strspn (fmt_char
, digits
);
1255 if (*fmt_char
== '.')
1256 fmt_char
+= 1 + strspn (fmt_char
+ 1, digits
);
1257 len
= fmt_char
- (b
+ 1);
1258 unsigned int fmt_code
= *fmt_char
;
1259 memcpy (dest
, b
, len
+ 1);
1270 dest
[len
+ 1] = *fmt_char
;
1271 dest
[len
+ 2] = '\0';
1272 die (EXIT_FAILURE
, 0, _("%s: invalid directive"),
1278 fail
|= print_func (dest
, len
+ 1, fmt_code
,
1279 fd
, filename
, data
);
1286 if ( ! interpret_backslash_escapes
)
1294 int esc_value
= octtobin (*b
);
1295 int esc_length
= 1; /* number of octal digits */
1296 for (++b
; esc_length
< 3 && isodigit (*b
);
1299 esc_value
= esc_value
* 8 + octtobin (*b
);
1301 putchar (esc_value
);
1304 else if (*b
== 'x' && isxdigit (to_uchar (b
[1])))
1306 int esc_value
= hextobin (b
[1]); /* Value of \xhh escape. */
1307 /* A hexadecimal \xhh escape sequence must have
1308 1 or 2 hex. digits. */
1310 if (isxdigit (to_uchar (b
[1])))
1313 esc_value
= esc_value
* 16 + hextobin (*b
);
1315 putchar (esc_value
);
1317 else if (*b
== '\0')
1319 error (0, 0, _("warning: backslash at end of format"));
1321 /* Arrange to exit the loop. */
1326 print_esc_char (*b
);
1337 fputs (trailing_delim
, stdout
);
1342 /* Stat the file system and print what we find. */
1343 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1344 do_statfs (char const *filename
, char const *format
)
1346 STRUCT_STATVFS statfsbuf
;
1348 if (STREQ (filename
, "-"))
1350 error (0, 0, _("using %s to denote standard input does not work"
1351 " in file system mode"), quoteaf (filename
));
1355 if (STATFS (filename
, &statfsbuf
) != 0)
1357 error (0, errno
, _("cannot read file system information for %s"),
1358 quoteaf (filename
));
1362 bool fail
= print_it (format
, -1, filename
, print_statfs
, &statfsbuf
);
1366 /* stat the file and print what we find */
1367 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1368 do_stat (char const *filename
, char const *format
,
1369 char const *format2
)
1371 int fd
= STREQ (filename
, "-") ? 0 : -1;
1372 struct stat statbuf
;
1376 if (fstat (fd
, &statbuf
) != 0)
1378 error (0, errno
, _("cannot stat standard input"));
1382 /* We can't use the shorter
1383 (follow_links?stat:lstat) (filename, &statbug)
1384 since stat might be a function-like macro. */
1385 else if ((follow_links
1386 ? stat (filename
, &statbuf
)
1387 : lstat (filename
, &statbuf
)) != 0)
1389 error (0, errno
, _("cannot stat %s"), quoteaf (filename
));
1393 if (S_ISBLK (statbuf
.st_mode
) || S_ISCHR (statbuf
.st_mode
))
1396 bool fail
= print_it (format
, fd
, filename
, print_stat
, &statbuf
);
1400 /* Return an allocated format string in static storage that
1401 corresponds to whether FS and TERSE options were declared. */
1403 default_format (bool fs
, bool terse
, bool device
)
1409 format
= xstrdup (fmt_terse_fs
);
1412 /* TRANSLATORS: This string uses format specifiers from
1413 'stat --help' with --file-system, and NOT from printf. */
1414 format
= xstrdup (_(" File: \"%n\"\n"
1415 " ID: %-8i Namelen: %-7l Type: %T\n"
1416 "Block size: %-10s Fundamental block size: %S\n"
1417 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
1418 "Inodes: Total: %-10c Free: %d\n"));
1425 if (0 < is_selinux_enabled ())
1426 format
= xstrdup (fmt_terse_selinux
);
1428 format
= xstrdup (fmt_terse_regular
);
1433 /* TRANSLATORS: This string uses format specifiers from
1434 'stat --help' without --file-system, and NOT from printf. */
1435 format
= xstrdup (_("\
1437 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1443 /* TRANSLATORS: This string uses format specifiers from
1444 'stat --help' without --file-system, and NOT from printf. */
1445 format
= xasprintf ("%s%s", format
, _("\
1446 " "Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
1451 /* TRANSLATORS: This string uses format specifiers from
1452 'stat --help' without --file-system, and NOT from printf. */
1453 format
= xasprintf ("%s%s", format
, _("\
1454 " "Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1460 /* TRANSLATORS: This string uses format specifiers from
1461 'stat --help' without --file-system, and NOT from printf. */
1462 format
= xasprintf ("%s%s", format
, _("\
1463 " "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1467 if (0 < is_selinux_enabled ())
1470 /* TRANSLATORS: This string uses format specifiers from
1471 'stat --help' without --file-system, and NOT from printf. */
1472 format
= xasprintf ("%s%s", format
, _("Context: %C\n"));
1477 /* TRANSLATORS: This string uses format specifiers from
1478 'stat --help' without --file-system, and NOT from printf. */
1479 format
= xasprintf ("%s%s", format
,
1493 if (status
!= EXIT_SUCCESS
)
1497 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name
);
1499 Display file or file system status.\n\
1502 emit_mandatory_arg_note ();
1505 -L, --dereference follow links\n\
1506 -f, --file-system display file system status instead of file status\n\
1509 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1510 output a newline after each use of FORMAT\n\
1511 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1512 and do not output a mandatory trailing newline;\n\
1513 if you want a newline, include \\n in FORMAT\n\
1514 -t, --terse print the information in terse form\n\
1516 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
1517 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
1520 The valid format sequences for files (without --file-system):\n\
1522 %a access rights in octal (note '#' and '0' printf flags)\n\
1523 %A access rights in human readable form\n\
1524 %b number of blocks allocated (see %B)\n\
1525 %B the size in bytes of each block reported by %b\n\
1526 %C SELinux security context string\n\
1529 %d device number in decimal\n\
1530 %D device number in hex\n\
1531 %f raw mode in hex\n\
1533 %g group ID of owner\n\
1534 %G group name of owner\n\
1537 %h number of hard links\n\
1541 %N quoted file name with dereference if symbolic link\n\
1542 %o optimal I/O transfer size hint\n\
1543 %s total size, in bytes\n\
1544 %t major device type in hex, for character/block device special files\n\
1545 %T minor device type in hex, for character/block device special files\n\
1548 %u user ID of owner\n\
1549 %U user name of owner\n\
1550 %w time of file birth, human-readable; - if unknown\n\
1551 %W time of file birth, seconds since Epoch; 0 if unknown\n\
1552 %x time of last access, human-readable\n\
1553 %X time of last access, seconds since Epoch\n\
1554 %y time of last data modification, human-readable\n\
1555 %Y time of last data modification, seconds since Epoch\n\
1556 %z time of last status change, human-readable\n\
1557 %Z time of last status change, seconds since Epoch\n\
1562 Valid format sequences for file systems:\n\
1564 %a free blocks available to non-superuser\n\
1565 %b total data blocks in file system\n\
1566 %c total file nodes in file system\n\
1567 %d free file nodes in file system\n\
1568 %f free blocks in file system\n\
1571 %i file system ID in hex\n\
1572 %l maximum length of filenames\n\
1574 %s block size (for faster transfers)\n\
1575 %S fundamental block size (for block counts)\n\
1576 %t file system type in hex\n\
1577 %T file system type in human readable form\n\
1581 --terse is equivalent to the following FORMAT:\n\
1584 #if HAVE_SELINUX_SELINUX_H
1592 --terse --file-system is equivalent to the following FORMAT:\n\
1596 printf (USAGE_BUILTIN_WARNING
, PROGRAM_NAME
);
1597 emit_ancillary_info (PROGRAM_NAME
);
1603 main (int argc
, char *argv
[])
1608 char *format
= NULL
;
1612 initialize_main (&argc
, &argv
);
1613 set_program_name (argv
[0]);
1614 setlocale (LC_ALL
, "");
1615 bindtextdomain (PACKAGE
, LOCALEDIR
);
1616 textdomain (PACKAGE
);
1618 struct lconv
const *locale
= localeconv ();
1619 decimal_point
= (locale
->decimal_point
[0] ? locale
->decimal_point
: ".");
1620 decimal_point_len
= strlen (decimal_point
);
1622 atexit (close_stdout
);
1624 while ((c
= getopt_long (argc
, argv
, "c:fLt", long_options
, NULL
)) != -1)
1630 interpret_backslash_escapes
= true;
1631 trailing_delim
= "";
1636 interpret_backslash_escapes
= false;
1637 trailing_delim
= "\n";
1641 follow_links
= true;
1652 case_GETOPT_HELP_CHAR
;
1654 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
1657 usage (EXIT_FAILURE
);
1663 error (0, 0, _("missing operand"));
1664 usage (EXIT_FAILURE
);
1669 if (strstr (format
, "%N"))
1670 getenv_quoting_style ();
1675 format
= default_format (fs
, terse
, /* device= */ false);
1676 format2
= default_format (fs
, terse
, /* device= */ true);
1679 for (int i
= optind
; i
< argc
; i
++)
1681 ? do_statfs (argv
[i
], format
)
1682 : do_stat (argv
[i
], format
, format2
));
1684 return ok
? EXIT_SUCCESS
: EXIT_FAILURE
;