1 /* stat.c -- display file or file system status
2 Copyright (C) 2001-2011 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-size.h"
71 #include "stat-time.h"
73 #include "find-mount-point.h"
74 #include "xvasprintf.h"
77 # define STRUCT_STATVFS struct statvfs
78 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
79 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
80 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
81 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
83 # define STATFS statvfs
84 # define STATFS_FRSIZE(S) ((S)->f_frsize)
86 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
87 # if HAVE_STRUCT_STATFS_F_NAMELEN
88 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
90 # define STATFS statfs
91 # if HAVE_OS_H /* BeOS */
92 /* BeOS has a statvfs function, but it does not return sensible values
93 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
94 f_fstypename. Use 'struct fs_info' instead. */
95 static int ATTRIBUTE_WARN_UNUSED_RESULT
96 statfs (char const *filename
, struct fs_info
*buf
)
98 dev_t device
= dev_for_path (filename
);
101 errno
= (device
== B_ENTRY_NOT_FOUND
? ENOENT
102 : device
== B_BAD_VALUE
? EINVAL
103 : device
== B_NAME_TOO_LONG
? ENAMETOOLONG
104 : device
== B_NO_MEMORY
? ENOMEM
105 : device
== B_FILE_ERROR
? EIO
109 /* If successful, buf->dev will be == device. */
110 return fs_stat_dev (device
, buf
);
113 # define f_blocks total_blocks
114 # define f_bfree free_blocks
115 # define f_bavail free_blocks
116 # define f_bsize io_size
117 # define f_files total_nodes
118 # define f_ffree free_nodes
119 # define STRUCT_STATVFS struct fs_info
120 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
121 # define STATFS_FRSIZE(S) ((S)->block_size)
123 # define STRUCT_STATVFS struct statfs
124 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
125 # define STATFS_FRSIZE(S) 0
130 # define OUT_NAMEMAX out_uint
132 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */
133 # define SB_F_NAMEMAX(S) "*"
134 # define OUT_NAMEMAX out_string
137 #if HAVE_STRUCT_STATVFS_F_BASETYPE
138 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
140 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
141 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
142 # elif HAVE_OS_H /* BeOS */
143 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
147 /* FIXME: these are used by printf.c, too */
148 #define isodigit(c) ('0' <= (c) && (c) <= '7')
149 #define octtobin(c) ((c) - '0')
150 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
151 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
153 static char const digits
[] = "0123456789";
155 /* Flags that are portable for use in printf, for at least one
156 conversion specifier; make_format removes unportable flags as
157 needed for particular specifiers. The glibc 2.2 extension "I" is
158 listed here; it is removed by make_format because it has undefined
159 behavior elsewhere and because it is incompatible with
161 static char const printf_flags
[] = "'-+ #0I";
163 #define PROGRAM_NAME "stat"
165 #define AUTHORS proper_name ("Michael Meskes")
169 PRINTF_OPTION
= CHAR_MAX
+ 1
172 static struct option
const long_options
[] =
174 {"context", no_argument
, 0, 'Z'},
175 {"dereference", no_argument
, NULL
, 'L'},
176 {"file-system", no_argument
, NULL
, 'f'},
177 {"format", required_argument
, NULL
, 'c'},
178 {"printf", required_argument
, NULL
, PRINTF_OPTION
},
179 {"terse", no_argument
, NULL
, 't'},
180 {GETOPT_HELP_OPTION_DECL
},
181 {GETOPT_VERSION_OPTION_DECL
},
185 /* Whether to follow symbolic links; True for --dereference (-L). */
186 static bool follow_links
;
188 /* Whether to interpret backslash-escape sequences.
189 True for --printf=FMT, not for --format=FMT (-c). */
190 static bool interpret_backslash_escapes
;
192 /* The trailing delimiter string:
193 "" for --printf=FMT, "\n" for --format=FMT (-c). */
194 static char const *trailing_delim
= "";
196 /* The representation of the decimal point in the current locale. */
197 static char const *decimal_point
;
198 static size_t decimal_point_len
;
200 /* Return the type of the specified file system.
201 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
202 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
203 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
204 Still others have neither and have to get by with f_type (GNU/Linux).
205 But f_type may only exist in statfs (Cygwin). */
206 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
207 human_fstype (STRUCT_STATVFS
const *statfsbuf
)
209 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
210 return statfsbuf
->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
;
212 switch (statfsbuf
->f_type
)
214 # if defined __linux__
216 /* Compare with what's in libc:
217 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
218 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
219 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
220 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
222 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
223 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
225 diff -u sym_stat sym_libc
228 /* Also compare with the list in "man 2 statfs" using the
229 fs-magic-compare make target. */
231 /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
232 statements must be followed by a hexadecimal constant in
233 a comment. The S_MAGIC_... name and constant are automatically
234 combined to produce the #define directives in fs.h. */
236 case S_MAGIC_ADFS
: /* 0xADF5 */
238 case S_MAGIC_AFFS
: /* 0xADFF */
240 case S_MAGIC_AFS
: /* 0x5346414F */
242 case S_MAGIC_ANON_INODE_FS
: /* 0x09041934 */
243 return "anon-inode FS";
244 case S_MAGIC_AUTOFS
: /* 0x0187 */
246 case S_MAGIC_BEFS
: /* 0x42465331 */
248 case S_MAGIC_BFS
: /* 0x1BADFACE */
250 case S_MAGIC_BINFMT_MISC
: /* 0x42494E4D */
251 return "binfmt_misc";
252 case S_MAGIC_BTRFS
: /* 0x9123683E */
254 case S_MAGIC_CGROUP
: /* 0x0027E0EB */
256 case S_MAGIC_CIFS
: /* 0xFF534D42 */
258 case S_MAGIC_CODA
: /* 0x73757245 */
260 case S_MAGIC_COH
: /* 0x012FF7B7 */
262 case S_MAGIC_CRAMFS
: /* 0x28CD3D45 */
264 case S_MAGIC_CRAMFS_WEND
: /* 0x453DCD28 */
265 return "cramfs-wend";
266 case S_MAGIC_DEBUGFS
: /* 0x64626720 */
268 case S_MAGIC_DEVFS
: /* 0x1373 */
270 case S_MAGIC_DEVPTS
: /* 0x1CD1 */
272 case S_MAGIC_ECRYPTFS
: /* 0xF15F */
274 case S_MAGIC_EFS
: /* 0x00414A53 */
276 case S_MAGIC_EXT
: /* 0x137D */
278 case S_MAGIC_EXT2
: /* 0xEF53 */
280 case S_MAGIC_EXT2_OLD
: /* 0xEF51 */
282 case S_MAGIC_FAT
: /* 0x4006 */
284 case S_MAGIC_FUSEBLK
: /* 0x65735546 */
286 case S_MAGIC_FUSECTL
: /* 0x65735543 */
288 case S_MAGIC_FUTEXFS
: /* 0x0BAD1DEA */
290 case S_MAGIC_GFS
: /* 0x1161970 */
292 case S_MAGIC_GPFS
: /* 0x47504653 */
294 case S_MAGIC_HFS
: /* 0x4244 */
296 case S_MAGIC_HPFS
: /* 0xF995E849 */
298 case S_MAGIC_HUGETLBFS
: /* 0x958458F6 */
300 case S_MAGIC_INOTIFYFS
: /* 0x2BAD1DEA */
302 case S_MAGIC_ISOFS
: /* 0x9660 */
304 case S_MAGIC_ISOFS_R_WIN
: /* 0x4004 */
306 case S_MAGIC_ISOFS_WIN
: /* 0x4000 */
308 case S_MAGIC_JFFS
: /* 0x07C0 */
310 case S_MAGIC_JFFS2
: /* 0x72B6 */
312 case S_MAGIC_JFS
: /* 0x3153464A */
314 case S_MAGIC_KAFS
: /* 0x6B414653 */
316 case S_MAGIC_LUSTRE
: /* 0x0BD00BD0 */
318 case S_MAGIC_MINIX
: /* 0x137F */
320 case S_MAGIC_MINIX_30
: /* 0x138F */
321 return "minix (30 char.)";
322 case S_MAGIC_MINIX_V2
: /* 0x2468 */
324 case S_MAGIC_MINIX_V2_30
: /* 0x2478 */
325 return "minix v2 (30 char.)";
326 case S_MAGIC_MINIX_V3
: /* 0x4D5A */
328 case S_MAGIC_MQUEUE
: /* 0x19800202 */
330 case S_MAGIC_MSDOS
: /* 0x4D44 */
332 case S_MAGIC_NCP
: /* 0x564C */
334 case S_MAGIC_NFS
: /* 0x6969 */
336 case S_MAGIC_NFSD
: /* 0x6E667364 */
338 case S_MAGIC_NILFS
: /* 0x3434 */
340 case S_MAGIC_NTFS
: /* 0x5346544E */
342 case S_MAGIC_OPENPROM
: /* 0x9FA1 */
344 case S_MAGIC_OCFS2
: /* 0x7461636f */
346 case S_MAGIC_PROC
: /* 0x9FA0 */
348 case S_MAGIC_PSTOREFS
: /* 0x6165676C */
350 case S_MAGIC_QNX4
: /* 0x002F */
352 case S_MAGIC_RAMFS
: /* 0x858458F6 */
354 case S_MAGIC_REISERFS
: /* 0x52654973 */
356 case S_MAGIC_ROMFS
: /* 0x7275 */
358 case S_MAGIC_RPC_PIPEFS
: /* 0x67596969 */
360 case S_MAGIC_SECURITYFS
: /* 0x73636673 */
362 case S_MAGIC_SELINUX
: /* 0xF97CFF8C */
364 case S_MAGIC_SMB
: /* 0x517B */
366 case S_MAGIC_SOCKFS
: /* 0x534F434B */
368 case S_MAGIC_SQUASHFS
: /* 0x73717368 */
370 case S_MAGIC_SYSFS
: /* 0x62656572 */
372 case S_MAGIC_SYSV2
: /* 0x012FF7B6 */
374 case S_MAGIC_SYSV4
: /* 0x012FF7B5 */
376 case S_MAGIC_TMPFS
: /* 0x01021994 */
378 case S_MAGIC_UDF
: /* 0x15013346 */
380 case S_MAGIC_UFS
: /* 0x00011954 */
382 case S_MAGIC_UFS_BYTESWAPPED
: /* 0x54190100 */
384 case S_MAGIC_USBDEVFS
: /* 0x9FA2 */
386 case S_MAGIC_V9FS
: /* 0x01021997 */
388 case S_MAGIC_VXFS
: /* 0xA501FCF5 */
390 case S_MAGIC_XENFS
: /* 0xABBA1974 */
392 case S_MAGIC_XENIX
: /* 0x012FF7B4 */
394 case S_MAGIC_XFS
: /* 0x58465342 */
396 case S_MAGIC_XIAFS
: /* 0x012FD16D */
457 unsigned long int type
= statfsbuf
->f_type
;
458 static char buf
[sizeof "UNKNOWN (0x%lx)" - 3
459 + (sizeof type
* CHAR_BIT
+ 3) / 4];
460 sprintf (buf
, "UNKNOWN (0x%lx)", type
);
467 static char * ATTRIBUTE_WARN_UNUSED_RESULT
468 human_access (struct stat
const *statbuf
)
470 static char modebuf
[12];
471 filemodestring (statbuf
, modebuf
);
476 static char * ATTRIBUTE_WARN_UNUSED_RESULT
477 human_time (struct timespec t
)
479 static char str
[MAX (INT_BUFSIZE_BOUND (intmax_t),
480 (INT_STRLEN_BOUND (int) /* YYYY */
481 + 1 /* because YYYY might equal INT_MAX + 1900 */
482 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
483 struct tm
const *tm
= localtime (&t
.tv_sec
);
485 return timetostr (t
.tv_sec
, str
);
486 nstrftime (str
, sizeof str
, "%Y-%m-%d %H:%M:%S.%N %z", tm
, 0, t
.tv_nsec
);
490 /* PFORMAT points to a '%' followed by a prefix of a format, all of
491 size PREFIX_LEN. The flags allowed for this format are
492 ALLOWED_FLAGS; remove other printf flags from the prefix, then
495 make_format (char *pformat
, size_t prefix_len
, char const *allowed_flags
,
498 char *dst
= pformat
+ 1;
500 char const *srclim
= pformat
+ prefix_len
;
501 for (src
= dst
; src
< srclim
&& strchr (printf_flags
, *src
); src
++)
502 if (strchr (allowed_flags
, *src
))
506 strcpy (dst
, suffix
);
510 out_string (char *pformat
, size_t prefix_len
, char const *arg
)
512 make_format (pformat
, prefix_len
, "-", "s");
513 printf (pformat
, arg
);
516 out_int (char *pformat
, size_t prefix_len
, intmax_t arg
)
518 make_format (pformat
, prefix_len
, "'-+ 0", PRIdMAX
);
519 return printf (pformat
, arg
);
522 out_uint (char *pformat
, size_t prefix_len
, uintmax_t arg
)
524 make_format (pformat
, prefix_len
, "'-0", PRIuMAX
);
525 return printf (pformat
, arg
);
528 out_uint_o (char *pformat
, size_t prefix_len
, uintmax_t arg
)
530 make_format (pformat
, prefix_len
, "-#0", PRIoMAX
);
531 printf (pformat
, arg
);
534 out_uint_x (char *pformat
, size_t prefix_len
, uintmax_t arg
)
536 make_format (pformat
, prefix_len
, "-#0", PRIxMAX
);
537 printf (pformat
, arg
);
540 out_minus_zero (char *pformat
, size_t prefix_len
)
542 make_format (pformat
, prefix_len
, "'-+ 0", ".0f");
543 return printf (pformat
, -0.25);
546 /* Output the number of seconds since the Epoch, using a format that
547 acts like printf's %f format. */
549 out_epoch_sec (char *pformat
, size_t prefix_len
, struct stat
const *statbuf
,
552 char *dot
= memchr (pformat
, '.', prefix_len
);
553 size_t sec_prefix_len
= prefix_len
;
556 bool frac_left_adjust
= false;
560 sec_prefix_len
= dot
- pformat
;
561 pformat
[prefix_len
] = '\0';
563 if (ISDIGIT (dot
[1]))
565 long int lprec
= strtol (dot
+ 1, NULL
, 10);
566 precision
= (lprec
<= INT_MAX
? lprec
: INT_MAX
);
573 if (precision
&& ISDIGIT (dot
[-1]))
575 /* If a nontrivial width is given, subtract the width of the
576 decimal point and PRECISION digits that will be output
583 while (ISDIGIT (p
[-1]));
585 long int lwidth
= strtol (p
, NULL
, 10);
586 width
= (lwidth
<= INT_MAX
? lwidth
: INT_MAX
);
590 sec_prefix_len
= p
- pformat
;
591 int w_d
= (decimal_point_len
< width
592 ? width
- decimal_point_len
596 int w
= w_d
- precision
;
600 for (char const *src
= dst
; src
< p
; src
++)
603 frac_left_adjust
= true;
609 + (frac_left_adjust
? 0 : sprintf (dst
, "%d", w
)));
617 for (int i
= precision
; i
< 9; i
++)
619 int frac_sec
= arg
.tv_nsec
/ divisor
;
622 if (TYPE_SIGNED (time_t))
624 bool minus_zero
= false;
625 if (arg
.tv_sec
< 0 && arg
.tv_nsec
!= 0)
627 int frac_sec_modulus
= 1000000000 / divisor
;
628 frac_sec
= (frac_sec_modulus
- frac_sec
629 - (arg
.tv_nsec
% divisor
!= 0));
630 arg
.tv_sec
+= (frac_sec
!= 0);
631 minus_zero
= (arg
.tv_sec
== 0);
633 int_len
= (minus_zero
634 ? out_minus_zero (pformat
, sec_prefix_len
)
635 : out_int (pformat
, sec_prefix_len
, arg
.tv_sec
));
638 int_len
= out_uint (pformat
, sec_prefix_len
, arg
.tv_sec
);
642 int prec
= (precision
< 9 ? precision
: 9);
643 int trailing_prec
= precision
- prec
;
644 int ilen
= (int_len
< 0 ? 0 : int_len
);
645 int trailing_width
= (ilen
< width
&& decimal_point_len
< width
- ilen
646 ? width
- ilen
- decimal_point_len
- prec
648 printf ("%s%.*d%-*.*d", decimal_point
, prec
, frac_sec
,
649 trailing_width
, trailing_prec
, 0);
653 /* Print the context information of FILENAME, and return true iff the
654 context could not be obtained. */
655 static bool ATTRIBUTE_WARN_UNUSED_RESULT
656 out_file_context (char *pformat
, size_t prefix_len
, char const *filename
)
662 ? getfilecon (filename
, &scontext
)
663 : lgetfilecon (filename
, &scontext
)) < 0)
665 error (0, errno
, _("failed to get security context of %s"),
670 strcpy (pformat
+ prefix_len
, "s");
671 printf (pformat
, (scontext
? scontext
: "?"));
677 /* Print statfs info. Return zero upon success, nonzero upon failure. */
678 static bool ATTRIBUTE_WARN_UNUSED_RESULT
679 print_statfs (char *pformat
, size_t prefix_len
, unsigned int m
,
680 char const *filename
,
683 STRUCT_STATVFS
const *statfsbuf
= data
;
689 out_string (pformat
, prefix_len
, filename
);
694 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
695 uintmax_t fsid
= statfsbuf
->f_fsid
;
697 typedef unsigned int fsid_word
;
698 verify (alignof (STRUCT_STATVFS
) % alignof (fsid_word
) == 0);
699 verify (offsetof (STRUCT_STATVFS
, f_fsid
) % alignof (fsid_word
) == 0);
700 verify (sizeof statfsbuf
->f_fsid
% alignof (fsid_word
) == 0);
701 fsid_word
const *p
= (fsid_word
*) &statfsbuf
->f_fsid
;
703 /* Assume a little-endian word order, as that is compatible
704 with glibc's statvfs implementation. */
706 int words
= sizeof statfsbuf
->f_fsid
/ sizeof *p
;
708 for (i
= 0; i
< words
&& i
* sizeof *p
< sizeof fsid
; i
++)
710 uintmax_t u
= p
[words
- 1 - i
];
711 fsid
|= u
<< (i
* CHAR_BIT
* sizeof *p
);
714 out_uint_x (pformat
, prefix_len
, fsid
);
719 OUT_NAMEMAX (pformat
, prefix_len
, SB_F_NAMEMAX (statfsbuf
));
722 #if HAVE_STRUCT_STATXFS_F_TYPE
723 out_uint_x (pformat
, prefix_len
, statfsbuf
->f_type
);
729 out_string (pformat
, prefix_len
, human_fstype (statfsbuf
));
732 out_int (pformat
, prefix_len
, statfsbuf
->f_blocks
);
735 out_int (pformat
, prefix_len
, statfsbuf
->f_bfree
);
738 out_int (pformat
, prefix_len
, statfsbuf
->f_bavail
);
741 out_uint (pformat
, prefix_len
, statfsbuf
->f_bsize
);
745 uintmax_t frsize
= STATFS_FRSIZE (statfsbuf
);
747 frsize
= statfsbuf
->f_bsize
;
748 out_uint (pformat
, prefix_len
, frsize
);
752 out_uint (pformat
, prefix_len
, statfsbuf
->f_files
);
755 out_int (pformat
, prefix_len
, statfsbuf
->f_ffree
);
764 /* Return any bind mounted source for a path.
765 The caller should not free the returned buffer.
766 Return NULL if no bind mount found. */
767 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
768 find_bind_mount (char const * name
)
770 char const * bind_mount
= NULL
;
772 static struct mount_entry
*mount_list
;
773 static bool tried_mount_list
= false;
774 if (!tried_mount_list
) /* attempt/warn once per process. */
776 if (!(mount_list
= read_file_system_list (false)))
777 error (0, errno
, "%s", _("cannot read table of mounted file systems"));
778 tried_mount_list
= true;
781 struct mount_entry
*me
;
782 for (me
= mount_list
; me
; me
= me
->me_next
)
784 if (me
->me_dummy
&& me
->me_devname
[0] == '/'
785 && STREQ (me
->me_mountdir
, name
))
787 struct stat name_stats
;
788 struct stat dev_stats
;
790 if (stat (name
, &name_stats
) == 0
791 && stat (me
->me_devname
, &dev_stats
) == 0
792 && SAME_INODE (name_stats
, dev_stats
))
794 bind_mount
= me
->me_devname
;
803 /* Print mount point. Return zero upon success, nonzero upon failure. */
804 static bool ATTRIBUTE_WARN_UNUSED_RESULT
805 out_mount_point (char const *filename
, char *pformat
, size_t prefix_len
,
806 const struct stat
*statp
)
809 char const *np
= "?", *bp
= NULL
;
813 /* Look for bind mounts first. Note we output the immediate alias,
814 rather than further resolving to a base device mount point. */
815 if (follow_links
|| !S_ISLNK (statp
->st_mode
))
817 char *resolved
= canonicalize_file_name (filename
);
820 error (0, errno
, _("failed to canonicalize %s"), quote (filename
));
821 goto print_mount_point
;
823 bp
= find_bind_mount (resolved
);
828 goto print_mount_point
;
832 /* If there is no direct bind mount, then navigate
833 back up the tree looking for a device change.
834 Note we don't detect if any of the directory components
835 are bind mounted to the same device, but that's OK
836 since we've not directly queried them. */
837 if ((mp
= find_mount_point (filename
, statp
)))
839 /* This dir might be bind mounted to another device,
840 so we resolve the bound source in that case also. */
841 bp
= find_bind_mount (mp
);
847 out_string (pformat
, prefix_len
, bp
? bp
: mp
? mp
: np
);
852 /* Map a TS with negative TS.tv_nsec to {0,0}. */
853 static inline struct timespec
854 neg_to_zero (struct timespec ts
)
858 struct timespec z
= {0, 0};
862 /* Print stat info. Return zero upon success, nonzero upon failure. */
864 print_stat (char *pformat
, size_t prefix_len
, unsigned int m
,
865 char const *filename
, void const *data
)
867 struct stat
*statbuf
= (struct stat
*) data
;
868 struct passwd
*pw_ent
;
869 struct group
*gw_ent
;
875 out_string (pformat
, prefix_len
, filename
);
878 out_string (pformat
, prefix_len
, quote (filename
));
879 if (S_ISLNK (statbuf
->st_mode
))
881 char *linkname
= areadlink_with_size (filename
, statbuf
->st_size
);
882 if (linkname
== NULL
)
884 error (0, errno
, _("cannot read symbolic link %s"),
889 out_string (pformat
, prefix_len
, quote (linkname
));
894 out_uint (pformat
, prefix_len
, statbuf
->st_dev
);
897 out_uint_x (pformat
, prefix_len
, statbuf
->st_dev
);
900 out_uint (pformat
, prefix_len
, statbuf
->st_ino
);
903 out_uint_o (pformat
, prefix_len
, statbuf
->st_mode
& CHMOD_MODE_BITS
);
906 out_string (pformat
, prefix_len
, human_access (statbuf
));
909 out_uint_x (pformat
, prefix_len
, statbuf
->st_mode
);
912 out_string (pformat
, prefix_len
, file_type (statbuf
));
915 out_uint (pformat
, prefix_len
, statbuf
->st_nlink
);
918 out_uint (pformat
, prefix_len
, statbuf
->st_uid
);
922 pw_ent
= getpwuid (statbuf
->st_uid
);
923 out_string (pformat
, prefix_len
,
924 pw_ent
? pw_ent
->pw_name
: "UNKNOWN");
927 out_uint (pformat
, prefix_len
, statbuf
->st_gid
);
931 gw_ent
= getgrgid (statbuf
->st_gid
);
932 out_string (pformat
, prefix_len
,
933 gw_ent
? gw_ent
->gr_name
: "UNKNOWN");
936 out_uint_x (pformat
, prefix_len
, major (statbuf
->st_rdev
));
939 fail
|= out_mount_point (filename
, pformat
, prefix_len
, statbuf
);
942 out_uint_x (pformat
, prefix_len
, minor (statbuf
->st_rdev
));
945 out_uint (pformat
, prefix_len
, statbuf
->st_size
);
948 out_uint (pformat
, prefix_len
, ST_NBLOCKSIZE
);
951 out_uint (pformat
, prefix_len
, ST_NBLOCKS (*statbuf
));
954 out_uint (pformat
, prefix_len
, statbuf
->st_blksize
);
958 struct timespec t
= get_stat_birthtime (statbuf
);
960 out_string (pformat
, prefix_len
, "-");
962 out_string (pformat
, prefix_len
, human_time (t
));
966 out_epoch_sec (pformat
, prefix_len
, statbuf
,
967 neg_to_zero (get_stat_birthtime (statbuf
)));
970 out_string (pformat
, prefix_len
, human_time (get_stat_atime (statbuf
)));
973 out_epoch_sec (pformat
, prefix_len
, statbuf
, get_stat_atime (statbuf
));
976 out_string (pformat
, prefix_len
, human_time (get_stat_mtime (statbuf
)));
979 out_epoch_sec (pformat
, prefix_len
, statbuf
, get_stat_mtime (statbuf
));
982 out_string (pformat
, prefix_len
, human_time (get_stat_ctime (statbuf
)));
985 out_epoch_sec (pformat
, prefix_len
, statbuf
, get_stat_ctime (statbuf
));
988 fail
|= out_file_context (pformat
, prefix_len
, filename
);
997 /* Output a single-character \ escape. */
1000 print_esc_char (char c
)
1004 case 'a': /* Alert. */
1007 case 'b': /* Backspace. */
1010 case 'e': /* Escape. */
1013 case 'f': /* Form feed. */
1016 case 'n': /* New line. */
1019 case 'r': /* Carriage return. */
1022 case 't': /* Horizontal tab. */
1025 case 'v': /* Vertical tab. */
1032 error (0, 0, _("warning: unrecognized escape `\\%c'"), c
);
1038 /* Print the information specified by the format string, FORMAT,
1039 calling PRINT_FUNC for each %-directive encountered.
1040 Return zero upon success, nonzero upon failure. */
1041 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1042 print_it (char const *format
, char const *filename
,
1043 bool (*print_func
) (char *, size_t, unsigned int,
1044 char const *, void const *),
1049 /* Add 2 to accommodate our conversion of the stat `%s' format string
1050 to the longer printf `%llu' one. */
1053 MAX_ADDITIONAL_BYTES
=
1054 (MAX (sizeof PRIdMAX
,
1055 MAX (sizeof PRIoMAX
, MAX (sizeof PRIuMAX
, sizeof PRIxMAX
)))
1058 size_t n_alloc
= strlen (format
) + MAX_ADDITIONAL_BYTES
+ 1;
1059 char *dest
= xmalloc (n_alloc
);
1061 for (b
= format
; *b
; b
++)
1067 size_t len
= strspn (b
+ 1, printf_flags
);
1068 char const *fmt_char
= b
+ len
+ 1;
1069 fmt_char
+= strspn (fmt_char
, digits
);
1070 if (*fmt_char
== '.')
1071 fmt_char
+= 1 + strspn (fmt_char
+ 1, digits
);
1072 len
= fmt_char
- (b
+ 1);
1073 unsigned int fmt_code
= *fmt_char
;
1074 memcpy (dest
, b
, len
+ 1);
1085 dest
[len
+ 1] = *fmt_char
;
1086 dest
[len
+ 2] = '\0';
1087 error (EXIT_FAILURE
, 0, _("%s: invalid directive"),
1088 quotearg_colon (dest
));
1093 fail
|= print_func (dest
, len
+ 1, fmt_code
, filename
, data
);
1100 if ( ! interpret_backslash_escapes
)
1108 int esc_value
= octtobin (*b
);
1109 int esc_length
= 1; /* number of octal digits */
1110 for (++b
; esc_length
< 3 && isodigit (*b
);
1113 esc_value
= esc_value
* 8 + octtobin (*b
);
1115 putchar (esc_value
);
1118 else if (*b
== 'x' && isxdigit (to_uchar (b
[1])))
1120 int esc_value
= hextobin (b
[1]); /* Value of \xhh escape. */
1121 /* A hexadecimal \xhh escape sequence must have
1122 1 or 2 hex. digits. */
1124 if (isxdigit (to_uchar (b
[1])))
1127 esc_value
= esc_value
* 16 + hextobin (*b
);
1129 putchar (esc_value
);
1131 else if (*b
== '\0')
1133 error (0, 0, _("warning: backslash at end of format"));
1135 /* Arrange to exit the loop. */
1140 print_esc_char (*b
);
1151 fputs (trailing_delim
, stdout
);
1156 /* Stat the file system and print what we find. */
1157 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1158 do_statfs (char const *filename
, bool terse
, char const *format
)
1160 STRUCT_STATVFS statfsbuf
;
1162 if (STREQ (filename
, "-"))
1164 error (0, 0, _("using %s to denote standard input does not work"
1165 " in file system mode"), quote (filename
));
1169 if (STATFS (filename
, &statfsbuf
) != 0)
1171 error (0, errno
, _("cannot read file system information for %s"),
1176 bool fail
= print_it (format
, filename
, print_statfs
, &statfsbuf
);
1180 /* stat the file and print what we find */
1181 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1182 do_stat (char const *filename
, bool terse
, char const *format
,
1183 char const *format2
)
1185 struct stat statbuf
;
1187 if (STREQ (filename
, "-"))
1189 if (fstat (STDIN_FILENO
, &statbuf
) != 0)
1191 error (0, errno
, _("cannot stat standard input"));
1195 /* We can't use the shorter
1196 (follow_links?stat:lstat) (filename, &statbug)
1197 since stat might be a function-like macro. */
1198 else if ((follow_links
1199 ? stat (filename
, &statbuf
)
1200 : lstat (filename
, &statbuf
)) != 0)
1202 error (0, errno
, _("cannot stat %s"), quote (filename
));
1206 if (S_ISBLK (statbuf
.st_mode
) || S_ISCHR (statbuf
.st_mode
))
1209 bool fail
= print_it (format
, filename
, print_stat
, &statbuf
);
1213 /* Return an allocated format string in static storage that
1214 corresponds to whether FS and TERSE options were declared. */
1216 default_format (bool fs
, bool terse
, bool device
)
1222 format
= xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
1225 /* TRANSLATORS: This string uses format specifiers from
1226 'stat --help' with --file-system, and NOT from printf. */
1227 format
= xstrdup (_("\
1229 ID: %-8i Namelen: %-7l Type: %T\n\
1230 Block size: %-10s Fundamental block size: %S\n\
1231 Blocks: Total: %-10b Free: %-10f Available: %a\n\
1232 Inodes: Total: %-10c Free: %d\n\
1240 if (0 < is_selinux_enabled ())
1241 format
= xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1242 " %X %Y %Z %W %o %C\n");
1244 format
= xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
1245 " %X %Y %Z %W %o\n");
1250 /* TRANSLATORS: This string uses format specifiers from
1251 'stat --help' without --file-system, and NOT from printf. */
1252 format
= xstrdup (_("\
1254 Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
1260 /* TRANSLATORS: This string uses format specifiers from
1261 'stat --help' without --file-system, and NOT from printf. */
1262 format
= xasprintf ("%s%s", format
, _("\
1263 Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
1268 /* TRANSLATORS: This string uses format specifiers from
1269 'stat --help' without --file-system, and NOT from printf. */
1270 format
= xasprintf ("%s%s", format
, _("\
1271 Device: %Dh/%dd\tInode: %-10i Links: %h\n\
1277 /* TRANSLATORS: This string uses format specifiers from
1278 'stat --help' without --file-system, and NOT from printf. */
1279 format
= xasprintf ("%s%s", format
, _("\
1280 Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
1284 if (0 < is_selinux_enabled ())
1287 /* TRANSLATORS: This string uses format specifiers from
1288 'stat --help' without --file-system, and NOT from printf. */
1289 format
= xasprintf ("%s%s", format
, _("\
1296 /* TRANSLATORS: This string uses format specifiers from
1297 'stat --help' without --file-system, and NOT from printf. */
1298 format
= xasprintf ("%s%s", format
, _("\
1313 if (status
!= EXIT_SUCCESS
)
1314 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
1318 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name
);
1320 Display file or file system status.\n\
1322 -L, --dereference follow links\n\
1323 -f, --file-system display file system status instead of file status\n\
1326 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1327 output a newline after each use of FORMAT\n\
1328 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1329 and do not output a mandatory trailing newline.\n\
1330 If you want a newline, include \\n in FORMAT\n\
1331 -t, --terse print the information in terse form\n\
1333 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
1334 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
1337 The valid format sequences for files (without --file-system):\n\
1339 %a Access rights in octal\n\
1340 %A Access rights in human readable form\n\
1341 %b Number of blocks allocated (see %B)\n\
1342 %B The size in bytes of each block reported by %b\n\
1343 %C SELinux security context string\n\
1346 %d Device number in decimal\n\
1347 %D Device number in hex\n\
1348 %f Raw mode in hex\n\
1350 %g Group ID of owner\n\
1351 %G Group name of owner\n\
1354 %h Number of hard links\n\
1358 %N Quoted file name with dereference if symbolic link\n\
1359 %o I/O block size\n\
1360 %s Total size, in bytes\n\
1361 %t Major device type in hex\n\
1362 %T Minor device type in hex\n\
1365 %u User ID of owner\n\
1366 %U User name of owner\n\
1367 %w Time of file birth, human-readable; - if unknown\n\
1368 %W Time of file birth, seconds since Epoch; 0 if unknown\n\
1369 %x Time of last access, human-readable\n\
1370 %X Time of last access, seconds since Epoch\n\
1371 %y Time of last modification, human-readable\n\
1372 %Y Time of last modification, seconds since Epoch\n\
1373 %z Time of last change, human-readable\n\
1374 %Z Time of last change, seconds since Epoch\n\
1379 Valid format sequences for file systems:\n\
1381 %a Free blocks available to non-superuser\n\
1382 %b Total data blocks in file system\n\
1383 %c Total file nodes in file system\n\
1384 %d Free file nodes in file system\n\
1385 %f Free blocks in file system\n\
1388 %i File System ID in hex\n\
1389 %l Maximum length of filenames\n\
1391 %s Block size (for faster transfers)\n\
1392 %S Fundamental block size (for block counts)\n\
1394 %T Type in human readable form\n\
1396 printf (USAGE_BUILTIN_WARNING
, PROGRAM_NAME
);
1397 emit_ancillary_info ();
1403 main (int argc
, char *argv
[])
1409 char *format
= NULL
;
1413 initialize_main (&argc
, &argv
);
1414 set_program_name (argv
[0]);
1415 setlocale (LC_ALL
, "");
1416 bindtextdomain (PACKAGE
, LOCALEDIR
);
1417 textdomain (PACKAGE
);
1419 struct lconv
const *locale
= localeconv ();
1420 decimal_point
= (locale
->decimal_point
[0] ? locale
->decimal_point
: ".");
1421 decimal_point_len
= strlen (decimal_point
);
1423 atexit (close_stdout
);
1425 while ((c
= getopt_long (argc
, argv
, "c:fLt", long_options
, NULL
)) != -1)
1431 interpret_backslash_escapes
= true;
1432 trailing_delim
= "";
1437 interpret_backslash_escapes
= false;
1438 trailing_delim
= "\n";
1442 follow_links
= true;
1453 case_GETOPT_HELP_CHAR
;
1455 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
1458 usage (EXIT_FAILURE
);
1464 error (0, 0, _("missing operand"));
1465 usage (EXIT_FAILURE
);
1472 format
= default_format (fs
, terse
, false);
1473 format2
= default_format (fs
, terse
, true);
1476 for (i
= optind
; i
< argc
; i
++)
1478 ? do_statfs (argv
[i
], terse
, format
)
1479 : do_stat (argv
[i
], terse
, format
, format2
));
1481 exit (ok
? EXIT_SUCCESS
: EXIT_FAILURE
);