1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2010 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 Written by Michael Meskes. */
21 /* Keep this conditional in sync with the similar conditional in
22 ../m4/stat-prog.m4. */
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
33 #include <sys/types.h>
37 # include <sys/statvfs.h>
40 #elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
41 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
42 It does have statvfs.h, but shouldn't use it, since it doesn't
43 HAVE_STRUCT_STATVFS_F_BASETYPE. So find a clean way to fix it. */
44 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
45 # include <sys/param.h>
46 # include <sys/mount.h>
47 # if HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
48 /* Ultrix 4.4 needs these for the declaration of struct statfs. */
49 # include <netinet/in.h>
50 # include <nfs/nfs_clnt.h>
53 #elif HAVE_OS_H /* BeOS */
56 #include <selinux/selinux.h>
61 #include "areadlink.h"
63 #include "file-type.h"
67 #include "mountlist.h"
70 #include "stat-time.h"
72 #include "find-mount-point.h"
73 #include "xvasprintf.h"
76 # define STRUCT_STATVFS struct statvfs
77 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
78 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
79 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
80 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
82 # define STATFS statvfs
83 # define STATFS_FRSIZE(S) ((S)->f_frsize)
85 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
86 # if HAVE_STRUCT_STATFS_F_NAMELEN
87 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
89 # define STATFS statfs
90 # if HAVE_OS_H /* BeOS */
91 /* BeOS has a statvfs function, but it does not return sensible values
92 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
93 f_fstypename. Use 'struct fs_info' instead. */
94 static int ATTRIBUTE_WARN_UNUSED_RESULT
95 statfs (char const *filename
, struct fs_info
*buf
)
97 dev_t device
= dev_for_path (filename
);
100 errno
= (device
== B_ENTRY_NOT_FOUND
? ENOENT
101 : device
== B_BAD_VALUE
? EINVAL
102 : device
== B_NAME_TOO_LONG
? ENAMETOOLONG
103 : device
== B_NO_MEMORY
? ENOMEM
104 : device
== B_FILE_ERROR
? EIO
108 /* If successful, buf->dev will be == device. */
109 return fs_stat_dev (device
, buf
);
112 # define f_blocks total_blocks
113 # define f_bfree free_blocks
114 # define f_bavail free_blocks
115 # define f_bsize io_size
116 # define f_files total_nodes
117 # define f_ffree free_nodes
118 # define STRUCT_STATVFS struct fs_info
119 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
120 # define STATFS_FRSIZE(S) ((S)->block_size)
122 # define STRUCT_STATVFS struct statfs
123 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
124 # define STATFS_FRSIZE(S) 0
129 # define OUT_NAMEMAX out_uint
131 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */
132 # define SB_F_NAMEMAX(S) "*"
133 # define OUT_NAMEMAX out_string
136 #if HAVE_STRUCT_STATVFS_F_BASETYPE
137 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
139 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
140 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
141 # elif HAVE_OS_H /* BeOS */
142 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
146 /* FIXME: these are used by printf.c, too */
147 #define isodigit(c) ('0' <= (c) && (c) <= '7')
148 #define octtobin(c) ((c) - '0')
149 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
150 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
152 #define PROGRAM_NAME "stat"
154 #define AUTHORS proper_name ("Michael Meskes")
158 PRINTF_OPTION
= CHAR_MAX
+ 1
161 static struct option
const long_options
[] =
163 {"context", no_argument
, 0, 'Z'},
164 {"dereference", no_argument
, NULL
, 'L'},
165 {"file-system", no_argument
, NULL
, 'f'},
166 {"format", required_argument
, NULL
, 'c'},
167 {"printf", required_argument
, NULL
, PRINTF_OPTION
},
168 {"terse", no_argument
, NULL
, 't'},
169 {GETOPT_HELP_OPTION_DECL
},
170 {GETOPT_VERSION_OPTION_DECL
},
174 /* Whether to follow symbolic links; True for --dereference (-L). */
175 static bool follow_links
;
177 /* Whether to interpret backslash-escape sequences.
178 True for --printf=FMT, not for --format=FMT (-c). */
179 static bool interpret_backslash_escapes
;
181 /* The trailing delimiter string:
182 "" for --printf=FMT, "\n" for --format=FMT (-c). */
183 static char const *trailing_delim
= "";
185 /* Return the type of the specified file system.
186 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
187 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
188 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
189 Still others have neither and have to get by with f_type (GNU/Linux).
190 But f_type may only exist in statfs (Cygwin). */
191 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
192 human_fstype (STRUCT_STATVFS
const *statfsbuf
)
194 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
195 return statfsbuf
->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
;
197 switch (statfsbuf
->f_type
)
199 # if defined __linux__
201 /* Compare with what's in libc:
202 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
203 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
204 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
205 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
207 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
208 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
210 diff -u sym_stat sym_libc
213 /* Also compare with the list in "man 2 statfs" using the
214 fs-magic-compare make target. */
216 /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
217 statements must be followed by a hexadecimal constant in
218 a comment. The S_MAGIC_... name and constant are automatically
219 combined to produce the #define directives in fs.h. */
221 case S_MAGIC_ADFS
: /* 0xADF5 */
223 case S_MAGIC_AFFS
: /* 0xADFF */
225 case S_MAGIC_AFS
: /* 0x5346414F */
227 case S_MAGIC_ANON_INODE_FS
: /* 0x09041934 */
228 return "anon-inode FS";
229 case S_MAGIC_AUTOFS
: /* 0x0187 */
231 case S_MAGIC_BEFS
: /* 0x42465331 */
233 case S_MAGIC_BFS
: /* 0x1BADFACE */
235 case S_MAGIC_BINFMT_MISC
: /* 0x42494E4D */
236 return "binfmt_misc";
237 case S_MAGIC_BTRFS
: /* 0x9123683E */
239 case S_MAGIC_CGROUP
: /* 0x0027E0EB */
241 case S_MAGIC_CIFS
: /* 0xFF534D42 */
243 case S_MAGIC_CODA
: /* 0x73757245 */
245 case S_MAGIC_COH
: /* 0x012FF7B7 */
247 case S_MAGIC_CRAMFS
: /* 0x28CD3D45 */
249 case S_MAGIC_CRAMFS_WEND
: /* 0x453DCD28 */
250 return "cramfs-wend";
251 case S_MAGIC_DEBUGFS
: /* 0x64626720 */
253 case S_MAGIC_DEVFS
: /* 0x1373 */
255 case S_MAGIC_DEVPTS
: /* 0x1CD1 */
257 case S_MAGIC_EFS
: /* 0x00414A53 */
259 case S_MAGIC_EXT
: /* 0x137D */
261 case S_MAGIC_EXT2
: /* 0xEF53 */
263 case S_MAGIC_EXT2_OLD
: /* 0xEF51 */
265 case S_MAGIC_FAT
: /* 0x4006 */
267 case S_MAGIC_FUSEBLK
: /* 0x65735546 */
269 case S_MAGIC_FUSECTL
: /* 0x65735543 */
271 case S_MAGIC_FUTEXFS
: /* 0x0BAD1DEA */
273 case S_MAGIC_GFS
: /* 0x1161970 */
275 case S_MAGIC_HFS
: /* 0x4244 */
277 case S_MAGIC_HPFS
: /* 0xF995E849 */
279 case S_MAGIC_HUGETLBFS
: /* 0x958458F6 */
281 case S_MAGIC_INOTIFYFS
: /* 0x2BAD1DEA */
283 case S_MAGIC_ISOFS
: /* 0x9660 */
285 case S_MAGIC_ISOFS_R_WIN
: /* 0x4004 */
287 case S_MAGIC_ISOFS_WIN
: /* 0x4000 */
289 case S_MAGIC_JFFS
: /* 0x07C0 */
291 case S_MAGIC_JFFS2
: /* 0x72B6 */
293 case S_MAGIC_JFS
: /* 0x3153464A */
295 case S_MAGIC_KAFS
: /* 0x6B414653 */
297 case S_MAGIC_LUSTRE
: /* 0x0BD00BD0 */
299 case S_MAGIC_MINIX
: /* 0x137F */
301 case S_MAGIC_MINIX_30
: /* 0x138F */
302 return "minix (30 char.)";
303 case S_MAGIC_MINIX_V2
: /* 0x2468 */
305 case S_MAGIC_MINIX_V2_30
: /* 0x2478 */
306 return "minix v2 (30 char.)";
307 case S_MAGIC_MINIX_V3
: /* 0x4D5A */
309 case S_MAGIC_MSDOS
: /* 0x4D44 */
311 case S_MAGIC_NCP
: /* 0x564C */
313 case S_MAGIC_NFS
: /* 0x6969 */
315 case S_MAGIC_NFSD
: /* 0x6E667364 */
317 case S_MAGIC_NILFS
: /* 0x3434 */
319 case S_MAGIC_NTFS
: /* 0x5346544E */
321 case S_MAGIC_OPENPROM
: /* 0x9FA1 */
323 case S_MAGIC_OCFS2
: /* 0x7461636f */
325 case S_MAGIC_PROC
: /* 0x9FA0 */
327 case S_MAGIC_QNX4
: /* 0x002F */
329 case S_MAGIC_RAMFS
: /* 0x858458F6 */
331 case S_MAGIC_REISERFS
: /* 0x52654973 */
333 case S_MAGIC_ROMFS
: /* 0x7275 */
335 case S_MAGIC_RPC_PIPEFS
: /* 0x67596969 */
337 case S_MAGIC_SECURITYFS
: /* 0x73636673 */
339 case S_MAGIC_SELINUX
: /* 0xF97CFF8C */
341 case S_MAGIC_SMB
: /* 0x517B */
343 case S_MAGIC_SOCKFS
: /* 0x534F434B */
345 case S_MAGIC_SQUASHFS
: /* 0x73717368 */
347 case S_MAGIC_SYSFS
: /* 0x62656572 */
349 case S_MAGIC_SYSV2
: /* 0x012FF7B6 */
351 case S_MAGIC_SYSV4
: /* 0x012FF7B5 */
353 case S_MAGIC_TMPFS
: /* 0x01021994 */
355 case S_MAGIC_UDF
: /* 0x15013346 */
357 case S_MAGIC_UFS
: /* 0x00011954 */
359 case S_MAGIC_UFS_BYTESWAPPED
: /* 0x54190100 */
361 case S_MAGIC_USBDEVFS
: /* 0x9FA2 */
363 case S_MAGIC_VXFS
: /* 0xA501FCF5 */
365 case S_MAGIC_XENFS
: /* 0xABBA1974 */
367 case S_MAGIC_XENIX
: /* 0x012FF7B4 */
369 case S_MAGIC_XFS
: /* 0x58465342 */
371 case S_MAGIC_XIAFS
: /* 0x012FD16D */
432 unsigned long int type
= statfsbuf
->f_type
;
433 static char buf
[sizeof "UNKNOWN (0x%lx)" - 3
434 + (sizeof type
* CHAR_BIT
+ 3) / 4];
435 sprintf (buf
, "UNKNOWN (0x%lx)", type
);
442 static char * ATTRIBUTE_WARN_UNUSED_RESULT
443 human_access (struct stat
const *statbuf
)
445 static char modebuf
[12];
446 filemodestring (statbuf
, modebuf
);
451 static char * ATTRIBUTE_WARN_UNUSED_RESULT
452 human_time (struct timespec t
)
454 static char str
[MAX (INT_BUFSIZE_BOUND (intmax_t),
455 (INT_STRLEN_BOUND (int) /* YYYY */
456 + 1 /* because YYYY might equal INT_MAX + 1900 */
457 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
458 struct tm
const *tm
= localtime (&t
.tv_sec
);
460 return timetostr (t
.tv_sec
, str
);
461 nstrftime (str
, sizeof str
, "%Y-%m-%d %H:%M:%S.%N %z", tm
, 0, t
.tv_nsec
);
465 static char * ATTRIBUTE_WARN_UNUSED_RESULT
466 epoch_time (struct timespec t
)
468 static char str
[INT_STRLEN_BOUND (time_t) + sizeof ".NNNNNNNNN"];
469 /* Note that time_t can technically be a floating point value, such
470 that casting to [u]intmax_t could lose a fractional value or
471 suffer from overflow. However, most porting targets have an
472 integral time_t; also, we know of no file systems that store
473 valid time values outside the bounds of intmax_t even if that
474 value were represented as a floating point. Besides, the cost of
475 converting to struct tm just to use nstrftime (str, len, "%s.%N",
476 tm, 0, t.tv_nsec) is pointless, since nstrftime would have to
477 convert back to seconds as time_t. */
478 if (TYPE_SIGNED (time_t))
479 sprintf (str
, "%" PRIdMAX
".%09ld", (intmax_t) t
.tv_sec
, t
.tv_nsec
);
481 sprintf (str
, "%" PRIuMAX
".%09ld", (uintmax_t) t
.tv_sec
, t
.tv_nsec
);
486 out_string (char *pformat
, size_t prefix_len
, char const *arg
)
488 strcpy (pformat
+ prefix_len
, "s");
489 printf (pformat
, arg
);
492 out_int (char *pformat
, size_t prefix_len
, intmax_t arg
)
494 strcpy (pformat
+ prefix_len
, PRIdMAX
);
495 printf (pformat
, arg
);
498 out_uint (char *pformat
, size_t prefix_len
, uintmax_t arg
)
500 strcpy (pformat
+ prefix_len
, PRIuMAX
);
501 printf (pformat
, arg
);
504 out_uint_o (char *pformat
, size_t prefix_len
, uintmax_t arg
)
506 strcpy (pformat
+ prefix_len
, PRIoMAX
);
507 printf (pformat
, arg
);
510 out_uint_x (char *pformat
, size_t prefix_len
, uintmax_t arg
)
512 strcpy (pformat
+ prefix_len
, PRIxMAX
);
513 printf (pformat
, arg
);
516 /* Very specialized function (modifies FORMAT), just so as to avoid
517 duplicating this code between both print_statfs and print_stat.
518 Return zero upon success, nonzero upon failure. */
519 static bool ATTRIBUTE_WARN_UNUSED_RESULT
520 out_file_context (char const *filename
, char *pformat
, size_t prefix_len
)
526 ? getfilecon (filename
, &scontext
)
527 : lgetfilecon (filename
, &scontext
)) < 0)
529 error (0, errno
, _("failed to get security context of %s"),
534 strcpy (pformat
+ prefix_len
, "s");
535 printf (pformat
, (scontext
? scontext
: "?"));
541 /* Print statfs info. Return zero upon success, nonzero upon failure. */
542 static bool ATTRIBUTE_WARN_UNUSED_RESULT
543 print_statfs (char *pformat
, size_t prefix_len
, char m
, char const *filename
,
546 STRUCT_STATVFS
const *statfsbuf
= data
;
552 out_string (pformat
, prefix_len
, filename
);
557 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
558 uintmax_t fsid
= statfsbuf
->f_fsid
;
560 typedef unsigned int fsid_word
;
561 verify (alignof (STRUCT_STATVFS
) % alignof (fsid_word
) == 0);
562 verify (offsetof (STRUCT_STATVFS
, f_fsid
) % alignof (fsid_word
) == 0);
563 verify (sizeof statfsbuf
->f_fsid
% alignof (fsid_word
) == 0);
564 fsid_word
const *p
= (fsid_word
*) &statfsbuf
->f_fsid
;
566 /* Assume a little-endian word order, as that is compatible
567 with glibc's statvfs implementation. */
569 int words
= sizeof statfsbuf
->f_fsid
/ sizeof *p
;
571 for (i
= 0; i
< words
&& i
* sizeof *p
< sizeof fsid
; i
++)
573 uintmax_t u
= p
[words
- 1 - i
];
574 fsid
|= u
<< (i
* CHAR_BIT
* sizeof *p
);
577 out_uint_x (pformat
, prefix_len
, fsid
);
582 OUT_NAMEMAX (pformat
, prefix_len
, SB_F_NAMEMAX (statfsbuf
));
585 #if HAVE_STRUCT_STATXFS_F_TYPE
586 out_uint_x (pformat
, prefix_len
, statfsbuf
->f_type
);
592 out_string (pformat
, prefix_len
, human_fstype (statfsbuf
));
595 out_int (pformat
, prefix_len
, statfsbuf
->f_blocks
);
598 out_int (pformat
, prefix_len
, statfsbuf
->f_bfree
);
601 out_int (pformat
, prefix_len
, statfsbuf
->f_bavail
);
604 out_uint (pformat
, prefix_len
, statfsbuf
->f_bsize
);
608 uintmax_t frsize
= STATFS_FRSIZE (statfsbuf
);
610 frsize
= statfsbuf
->f_bsize
;
611 out_uint (pformat
, prefix_len
, frsize
);
615 out_uint (pformat
, prefix_len
, statfsbuf
->f_files
);
618 out_int (pformat
, prefix_len
, statfsbuf
->f_ffree
);
621 fail
|= out_file_context (filename
, pformat
, prefix_len
);
630 /* Return any bind mounted source for a path.
631 The caller should not free the returned buffer.
632 Return NULL if no bind mount found. */
633 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
634 find_bind_mount (char const * name
)
636 char const * bind_mount
= NULL
;
638 static struct mount_entry
*mount_list
;
639 static bool tried_mount_list
= false;
640 if (!tried_mount_list
) /* attempt/warn once per process. */
642 if (!(mount_list
= read_file_system_list (false)))
643 error (0, errno
, "%s", _("cannot read table of mounted file systems"));
644 tried_mount_list
= true;
647 struct mount_entry
*me
;
648 for (me
= mount_list
; me
; me
= me
->me_next
)
650 if (me
->me_dummy
&& me
->me_devname
[0] == '/'
651 && STREQ (me
->me_mountdir
, name
))
653 struct stat name_stats
;
654 struct stat dev_stats
;
656 if (stat (name
, &name_stats
) == 0
657 && stat (me
->me_devname
, &dev_stats
) == 0
658 && SAME_INODE (name_stats
, dev_stats
))
660 bind_mount
= me
->me_devname
;
669 /* Print mount point. Return zero upon success, nonzero upon failure. */
670 static bool ATTRIBUTE_WARN_UNUSED_RESULT
671 out_mount_point (char const *filename
, char *pformat
, size_t prefix_len
,
672 const struct stat
*statp
)
675 char const *np
= "?", *bp
= NULL
;
679 /* Look for bind mounts first. Note we output the immediate alias,
680 rather than further resolving to a base device mount point. */
681 if (follow_links
|| !S_ISLNK (statp
->st_mode
))
683 char *resolved
= canonicalize_file_name (filename
);
686 error (0, errno
, _("failed to canonicalize %s"), quote (filename
));
687 goto print_mount_point
;
689 bp
= find_bind_mount (resolved
);
694 goto print_mount_point
;
698 /* If there is no direct bind mount, then navigate
699 back up the tree looking for a device change.
700 Note we don't detect if any of the directory components
701 are bind mounted to the same device, but that's OK
702 since we've not directly queried them. */
703 if ((mp
= find_mount_point (filename
, statp
)))
705 /* This dir might be bind mounted to another device,
706 so we resolve the bound source in that case also. */
707 bp
= find_bind_mount (mp
);
713 out_string (pformat
, prefix_len
, bp
? bp
: mp
? mp
: np
);
718 /* Print stat info. Return zero upon success, nonzero upon failure. */
720 print_stat (char *pformat
, size_t prefix_len
, char m
,
721 char const *filename
, void const *data
)
723 struct stat
*statbuf
= (struct stat
*) data
;
724 struct passwd
*pw_ent
;
725 struct group
*gw_ent
;
731 out_string (pformat
, prefix_len
, filename
);
734 out_string (pformat
, prefix_len
, quote (filename
));
735 if (S_ISLNK (statbuf
->st_mode
))
737 char *linkname
= areadlink_with_size (filename
, statbuf
->st_size
);
738 if (linkname
== NULL
)
740 error (0, errno
, _("cannot read symbolic link %s"),
745 out_string (pformat
, prefix_len
, quote (linkname
));
750 out_uint (pformat
, prefix_len
, statbuf
->st_dev
);
753 out_uint_x (pformat
, prefix_len
, statbuf
->st_dev
);
756 out_uint (pformat
, prefix_len
, statbuf
->st_ino
);
759 out_uint_o (pformat
, prefix_len
, statbuf
->st_mode
& CHMOD_MODE_BITS
);
762 out_string (pformat
, prefix_len
, human_access (statbuf
));
765 out_uint_x (pformat
, prefix_len
, statbuf
->st_mode
);
768 out_string (pformat
, prefix_len
, file_type (statbuf
));
771 out_uint (pformat
, prefix_len
, statbuf
->st_nlink
);
774 out_uint (pformat
, prefix_len
, statbuf
->st_uid
);
778 pw_ent
= getpwuid (statbuf
->st_uid
);
779 out_string (pformat
, prefix_len
,
780 pw_ent
? pw_ent
->pw_name
: "UNKNOWN");
783 out_uint (pformat
, prefix_len
, statbuf
->st_gid
);
787 gw_ent
= getgrgid (statbuf
->st_gid
);
788 out_string (pformat
, prefix_len
,
789 gw_ent
? gw_ent
->gr_name
: "UNKNOWN");
792 out_uint_x (pformat
, prefix_len
, major (statbuf
->st_rdev
));
795 fail
|= out_mount_point (filename
, pformat
, prefix_len
, statbuf
);
798 out_uint_x (pformat
, prefix_len
, minor (statbuf
->st_rdev
));
801 out_uint (pformat
, prefix_len
, statbuf
->st_size
);
804 out_uint (pformat
, prefix_len
, ST_NBLOCKSIZE
);
807 out_uint (pformat
, prefix_len
, ST_NBLOCKS (*statbuf
));
810 out_uint (pformat
, prefix_len
, statbuf
->st_blksize
);
814 struct timespec t
= get_stat_birthtime (statbuf
);
816 out_string (pformat
, prefix_len
, "-");
818 out_string (pformat
, prefix_len
, human_time (t
));
823 struct timespec t
= get_stat_birthtime (statbuf
);
825 out_string (pformat
, prefix_len
, "-");
827 out_string (pformat
, prefix_len
, epoch_time (t
));
831 out_string (pformat
, prefix_len
, human_time (get_stat_atime (statbuf
)));
834 out_string (pformat
, prefix_len
, epoch_time (get_stat_atime (statbuf
)));
837 out_string (pformat
, prefix_len
, human_time (get_stat_mtime (statbuf
)));
840 out_string (pformat
, prefix_len
, epoch_time (get_stat_mtime (statbuf
)));
843 out_string (pformat
, prefix_len
, human_time (get_stat_ctime (statbuf
)));
846 out_string (pformat
, prefix_len
, epoch_time (get_stat_ctime (statbuf
)));
849 fail
|= out_file_context (filename
, pformat
, prefix_len
);
858 /* Output a single-character \ escape. */
861 print_esc_char (char c
)
865 case 'a': /* Alert. */
868 case 'b': /* Backspace. */
871 case 'e': /* Escape. */
874 case 'f': /* Form feed. */
877 case 'n': /* New line. */
880 case 'r': /* Carriage return. */
883 case 't': /* Horizontal tab. */
886 case 'v': /* Vertical tab. */
893 error (0, 0, _("warning: unrecognized escape `\\%c'"), c
);
899 /* Print the information specified by the format string, FORMAT,
900 calling PRINT_FUNC for each %-directive encountered.
901 Return zero upon success, nonzero upon failure. */
902 static bool ATTRIBUTE_WARN_UNUSED_RESULT
903 print_it (char const *format
, char const *filename
,
904 bool (*print_func
) (char *, size_t, char, char const *, void const *),
909 /* Add 2 to accommodate our conversion of the stat `%s' format string
910 to the longer printf `%llu' one. */
913 MAX_ADDITIONAL_BYTES
=
914 (MAX (sizeof PRIdMAX
,
915 MAX (sizeof PRIoMAX
, MAX (sizeof PRIuMAX
, sizeof PRIxMAX
)))
918 size_t n_alloc
= strlen (format
) + MAX_ADDITIONAL_BYTES
+ 1;
919 char *dest
= xmalloc (n_alloc
);
921 for (b
= format
; *b
; b
++)
927 size_t len
= strspn (b
+ 1, "#-+.I 0123456789");
928 char const *fmt_char
= b
+ len
+ 1;
929 memcpy (dest
, b
, len
+ 1);
940 dest
[len
+ 1] = *fmt_char
;
941 dest
[len
+ 2] = '\0';
942 error (EXIT_FAILURE
, 0, _("%s: invalid directive"),
943 quotearg_colon (dest
));
948 fail
|= print_func (dest
, len
+ 1, *fmt_char
, filename
, data
);
955 if ( ! interpret_backslash_escapes
)
963 int esc_value
= octtobin (*b
);
964 int esc_length
= 1; /* number of octal digits */
965 for (++b
; esc_length
< 3 && isodigit (*b
);
968 esc_value
= esc_value
* 8 + octtobin (*b
);
973 else if (*b
== 'x' && isxdigit (to_uchar (b
[1])))
975 int esc_value
= hextobin (b
[1]); /* Value of \xhh escape. */
976 /* A hexadecimal \xhh escape sequence must have
977 1 or 2 hex. digits. */
979 if (isxdigit (to_uchar (b
[1])))
982 esc_value
= esc_value
* 16 + hextobin (*b
);
988 error (0, 0, _("warning: backslash at end of format"));
990 /* Arrange to exit the loop. */
1006 fputs (trailing_delim
, stdout
);
1011 /* Stat the file system and print what we find. */
1012 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1013 do_statfs (char const *filename
, bool terse
, char const *format
)
1015 STRUCT_STATVFS statfsbuf
;
1017 if (STREQ (filename
, "-"))
1019 error (0, 0, _("using %s to denote standard input does not work"
1020 " in file system mode"), quote (filename
));
1024 if (STATFS (filename
, &statfsbuf
) != 0)
1026 error (0, errno
, _("cannot read file system information for %s"),
1031 bool fail
= print_it (format
, filename
, print_statfs
, &statfsbuf
);
1035 /* stat the file and print what we find */
1036 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1037 do_stat (char const *filename
, bool terse
, char const *format
,
1038 char const *format2
)
1040 struct stat statbuf
;
1042 if (STREQ (filename
, "-"))
1044 if (fstat (STDIN_FILENO
, &statbuf
) != 0)
1046 error (0, errno
, _("cannot stat standard input"));
1050 /* We can't use the shorter
1051 (follow_links?stat:lstat) (filename, &statbug)
1052 since stat might be a function-like macro. */
1053 else if ((follow_links
1054 ? stat (filename
, &statbuf
)
1055 : lstat (filename
, &statbuf
)) != 0)
1057 error (0, errno
, _("cannot stat %s"), quote (filename
));
1061 if (S_ISBLK (statbuf
.st_mode
) || S_ISCHR (statbuf
.st_mode
))
1064 bool fail
= print_it (format
, filename
, print_stat
, &statbuf
);
1068 /* Return an allocated format string in static storage that
1069 corresponds to whether FS and TERSE options were declared. */
1071 default_format (bool fs
, bool terse
, bool device
)
1077 format
= xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
1080 /* TRANSLATORS: This string uses format specifiers from
1081 'stat --help' with --file-system, and NOT from printf. */
1082 format
= xstrdup (_("\
1084 ID: %-8i Namelen: %-7l Type: %T\n\
1085 Block size: %-10s Fundamental block size: %S\n\
1086 Blocks: Total: %-10b Free: %-10f Available: %a\n\
1087 Inodes: Total: %-10c Free: %d\n\
1095 if (0 < is_selinux_enabled ())
1096 format
= xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1097 " %X %Y %Z %W %o %C\n");
1099 format
= xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1100 " %X %Y %Z %W %o\n");
1105 /* TRANSLATORS: This string uses format specifiers from
1106 'stat --help' without --file-system, and NOT from printf. */
1107 format
= xstrdup (_("\
1109 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1115 /* TRANSLATORS: This string uses format specifiers from
1116 'stat --help' without --file-system, and NOT from printf. */
1117 format
= xasprintf ("%s%s", format
, _("\
1118 Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
1123 /* TRANSLATORS: This string uses format specifiers from
1124 'stat --help' without --file-system, and NOT from printf. */
1125 format
= xasprintf ("%s%s", format
, _("\
1126 Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1132 /* TRANSLATORS: This string uses format specifiers from
1133 'stat --help' without --file-system, and NOT from printf. */
1134 format
= xasprintf ("%s%s", format
, _("\
1135 Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1139 if (0 < is_selinux_enabled ())
1142 /* TRANSLATORS: This string uses format specifiers from
1143 'stat --help' without --file-system, and NOT from printf. */
1144 format
= xasprintf ("%s%s", format
, _("\
1151 /* TRANSLATORS: This string uses format specifiers from
1152 'stat --help' without --file-system, and NOT from printf. */
1153 format
= xasprintf ("%s%s", format
, _("\
1168 if (status
!= EXIT_SUCCESS
)
1169 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
1173 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name
);
1175 Display file or file system status.\n\
1177 -L, --dereference follow links\n\
1178 -f, --file-system display file system status instead of file status\n\
1181 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1182 output a newline after each use of FORMAT\n\
1183 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1184 and do not output a mandatory trailing newline.\n\
1185 If you want a newline, include \\n in FORMAT\n\
1186 -t, --terse print the information in terse form\n\
1188 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
1189 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
1192 The valid format sequences for files (without --file-system):\n\
1194 %a Access rights in octal\n\
1195 %A Access rights in human readable form\n\
1196 %b Number of blocks allocated (see %B)\n\
1197 %B The size in bytes of each block reported by %b\n\
1198 %C SELinux security context string\n\
1201 %d Device number in decimal\n\
1202 %D Device number in hex\n\
1203 %f Raw mode in hex\n\
1205 %g Group ID of owner\n\
1206 %G Group name of owner\n\
1209 %h Number of hard links\n\
1213 %N Quoted file name with dereference if symbolic link\n\
1214 %o I/O block size\n\
1215 %s Total size, in bytes\n\
1216 %t Major device type in hex\n\
1217 %T Minor device type in hex\n\
1220 %u User ID of owner\n\
1221 %U User name of owner\n\
1222 %w Time of file birth, or - if unknown\n\
1223 %W Time of file birth as seconds since Epoch, or - if unknown\n\
1224 %x Time of last access\n\
1225 %X Time of last access as seconds since Epoch\n\
1226 %y Time of last modification\n\
1227 %Y Time of last modification as seconds since Epoch\n\
1228 %z Time of last change\n\
1229 %Z Time of last change as seconds since Epoch\n\
1234 Valid format sequences for file systems:\n\
1236 %a Free blocks available to non-superuser\n\
1237 %b Total data blocks in file system\n\
1238 %c Total file nodes in file system\n\
1239 %d Free file nodes in file system\n\
1240 %f Free blocks in file system\n\
1241 %C SELinux security context string\n\
1244 %i File System ID in hex\n\
1245 %l Maximum length of filenames\n\
1247 %s Block size (for faster transfers)\n\
1248 %S Fundamental block size (for block counts)\n\
1250 %T Type in human readable form\n\
1252 printf (USAGE_BUILTIN_WARNING
, PROGRAM_NAME
);
1253 emit_ancillary_info ();
1259 main (int argc
, char *argv
[])
1265 char *format
= NULL
;
1269 initialize_main (&argc
, &argv
);
1270 set_program_name (argv
[0]);
1271 setlocale (LC_ALL
, "");
1272 bindtextdomain (PACKAGE
, LOCALEDIR
);
1273 textdomain (PACKAGE
);
1275 atexit (close_stdout
);
1277 while ((c
= getopt_long (argc
, argv
, "c:fLt", long_options
, NULL
)) != -1)
1283 interpret_backslash_escapes
= true;
1284 trailing_delim
= "";
1289 interpret_backslash_escapes
= false;
1290 trailing_delim
= "\n";
1294 follow_links
= true;
1305 case_GETOPT_HELP_CHAR
;
1307 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
1310 usage (EXIT_FAILURE
);
1316 error (0, 0, _("missing operand"));
1317 usage (EXIT_FAILURE
);
1324 format
= default_format (fs
, terse
, false);
1325 format2
= default_format (fs
, terse
, true);
1328 for (i
= optind
; i
< argc
; i
++)
1330 ? do_statfs (argv
[i
], terse
, format
)
1331 : do_stat (argv
[i
], terse
, format
, format2
));
1333 exit (ok
? EXIT_SUCCESS
: EXIT_FAILURE
);