mirror of
git://git.sv.gnu.org/coreutils.git
synced 2026-03-27 15:25:07 +02:00
* src/mktemp.c: Update copyright to use newer form. Make first copyright lines consistent. Some did not end in ", Inc.". Due to that missing ", Inc" at end of line, emacs' copyright-updating code missed some opportunities. This corrects most of those, and adds some mistakenly omitted years. * src/chown-core.c: Adjust. * src/chown-core.h: Likewise. * src/copy.c: Likewise. * src/copy.h: Likewise. * src/cp-hash.c: Likewise. * src/cp.c: Likewise. * src/group-list.h: Likewise. * src/mktemp.c: Likewise. * src/stat.c: Likewise. * tests/chmod/equal-x: Likewise. * tests/dd/skip-seek: Likewise. * tests/envvar-check: Likewise. * tests/mv/into-self-2: Likewise.
1070 lines
28 KiB
C
1070 lines
28 KiB
C
/* stat.c -- display file or file system status
|
|
Copyright (C) 2001-2008 Free Software Foundation, Inc.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
Written by Michael Meskes. */
|
|
|
|
#include <config.h>
|
|
|
|
/* Keep this conditional in sync with the similar conditional in
|
|
../m4/stat-prog.m4. */
|
|
#if (STAT_STATVFS \
|
|
&& (HAVE_STRUCT_STATVFS_F_BASETYPE || HAVE_STRUCT_STATVFS_F_FSTYPENAME \
|
|
|| (! HAVE_STRUCT_STATFS_F_FSTYPENAME && HAVE_STRUCT_STATVFS_F_TYPE)))
|
|
# define USE_STATVFS 1
|
|
#else
|
|
# define USE_STATVFS 0
|
|
#endif
|
|
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#if USE_STATVFS
|
|
# include <sys/statvfs.h>
|
|
#elif HAVE_SYS_VFS_H
|
|
# include <sys/vfs.h>
|
|
#elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
|
|
/* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
|
|
It does have statvfs.h, but shouldn't use it, since it doesn't
|
|
HAVE_STRUCT_STATVFS_F_BASETYPE. So find a clean way to fix it. */
|
|
/* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
|
|
# include <sys/param.h>
|
|
# include <sys/mount.h>
|
|
# if HAVE_NETINET_IN_H && HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
|
|
/* Ultrix 4.4 needs these for the declaration of struct statfs. */
|
|
# include <netinet/in.h>
|
|
# include <nfs/nfs_clnt.h>
|
|
# include <nfs/vfs.h>
|
|
# endif
|
|
#elif HAVE_OS_H /* BeOS */
|
|
# include <fs_info.h>
|
|
#endif
|
|
#include <selinux/selinux.h>
|
|
|
|
#include "system.h"
|
|
|
|
#include "error.h"
|
|
#include "filemode.h"
|
|
#include "file-type.h"
|
|
#include "fs.h"
|
|
#include "getopt.h"
|
|
#include "inttostr.h"
|
|
#include "quote.h"
|
|
#include "quotearg.h"
|
|
#include "stat-time.h"
|
|
#include "strftime.h"
|
|
#include "areadlink.h"
|
|
|
|
#define alignof(type) offsetof (struct { char c; type x; }, x)
|
|
|
|
#if USE_STATVFS
|
|
# define STRUCT_STATVFS struct statvfs
|
|
# define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
|
|
# define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
|
|
# if HAVE_STRUCT_STATVFS_F_NAMEMAX
|
|
# define SB_F_NAMEMAX(S) ((S)->f_namemax)
|
|
# endif
|
|
# define STATFS statvfs
|
|
# define STATFS_FRSIZE(S) ((S)->f_frsize)
|
|
#else
|
|
# define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
|
|
# if HAVE_STRUCT_STATFS_F_NAMELEN
|
|
# define SB_F_NAMEMAX(S) ((S)->f_namelen)
|
|
# endif
|
|
# define STATFS statfs
|
|
# if HAVE_OS_H /* BeOS */
|
|
/* BeOS has a statvfs function, but it does not return sensible values
|
|
for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
|
|
f_fstypename. Use 'struct fs_info' instead. */
|
|
static int
|
|
statfs (char const *filename, struct fs_info *buf)
|
|
{
|
|
dev_t device = dev_for_path (filename);
|
|
if (device < 0)
|
|
{
|
|
errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
|
|
: device == B_BAD_VALUE ? EINVAL
|
|
: device == B_NAME_TOO_LONG ? ENAMETOOLONG
|
|
: device == B_NO_MEMORY ? ENOMEM
|
|
: device == B_FILE_ERROR ? EIO
|
|
: 0);
|
|
return -1;
|
|
}
|
|
/* If successful, buf->dev will be == device. */
|
|
return fs_stat_dev (device, buf);
|
|
}
|
|
# define f_fsid dev
|
|
# define f_blocks total_blocks
|
|
# define f_bfree free_blocks
|
|
# define f_bavail free_blocks
|
|
# define f_bsize io_size
|
|
# define f_files total_nodes
|
|
# define f_ffree free_nodes
|
|
# define STRUCT_STATVFS struct fs_info
|
|
# define STRUCT_STATXFS_F_FSID_IS_INTEGER true
|
|
# define STATFS_FRSIZE(S) ((S)->block_size)
|
|
# else
|
|
# define STRUCT_STATVFS struct statfs
|
|
# define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
|
|
# define STATFS_FRSIZE(S) 0
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef SB_F_NAMEMAX
|
|
# define OUT_NAMEMAX out_uint
|
|
#else
|
|
/* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */
|
|
# define SB_F_NAMEMAX(S) "*"
|
|
# define OUT_NAMEMAX out_string
|
|
#endif
|
|
|
|
#if HAVE_STRUCT_STATVFS_F_BASETYPE
|
|
# define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
|
|
#else
|
|
# if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
|
|
# define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
|
|
# elif HAVE_OS_H /* BeOS */
|
|
# define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
|
|
# endif
|
|
#endif
|
|
|
|
/* FIXME: these are used by printf.c, too */
|
|
#define isodigit(c) ('0' <= (c) && (c) <= '7')
|
|
#define octtobin(c) ((c) - '0')
|
|
#define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
|
|
(c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
|
|
|
|
#define PROGRAM_NAME "stat"
|
|
|
|
#define AUTHORS "Michael Meskes"
|
|
|
|
enum
|
|
{
|
|
PRINTF_OPTION = CHAR_MAX + 1
|
|
};
|
|
|
|
static struct option const long_options[] = {
|
|
{"context", no_argument, 0, 'Z'},
|
|
{"dereference", no_argument, NULL, 'L'},
|
|
{"file-system", no_argument, NULL, 'f'},
|
|
|
|
/* obsolete and undocumented alias: FIXME: remove in 2009 */
|
|
{"filesystem", no_argument, NULL, 'f'},
|
|
|
|
{"format", required_argument, NULL, 'c'},
|
|
{"printf", required_argument, NULL, PRINTF_OPTION},
|
|
{"terse", no_argument, NULL, 't'},
|
|
{GETOPT_HELP_OPTION_DECL},
|
|
{GETOPT_VERSION_OPTION_DECL},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
char *program_name;
|
|
|
|
/* Whether to follow symbolic links; True for --dereference (-L). */
|
|
static bool follow_links;
|
|
|
|
/* Whether to interpret backslash-escape sequences.
|
|
True for --printf=FMT, not for --format=FMT (-c). */
|
|
static bool interpret_backslash_escapes;
|
|
|
|
/* The trailing delimiter string:
|
|
"" for --printf=FMT, "\n" for --format=FMT (-c). */
|
|
static char const *trailing_delim = "";
|
|
|
|
/* Return the type of the specified file system.
|
|
Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
|
|
Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
|
|
Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
|
|
Still others have neither and have to get by with f_type (Linux).
|
|
But f_type may only exist in statfs (Cygwin). */
|
|
static char const *
|
|
human_fstype (STRUCT_STATVFS const *statfsbuf)
|
|
{
|
|
#ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
|
|
return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
|
|
#else
|
|
switch (statfsbuf->f_type)
|
|
{
|
|
# if defined __linux__
|
|
|
|
/* Compare with what's in libc:
|
|
f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
|
|
sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
|
|
| perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
|
|
-e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
|
|
| sort > sym_libc
|
|
perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
|
|
-e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
|
|
| sort > sym_stat
|
|
diff -u sym_stat sym_libc
|
|
*/
|
|
|
|
/* Also sync from the list in "man 2 statfs". */
|
|
|
|
/* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
|
|
statements must be followed by a hexadecimal constant in
|
|
a comment. The S_MAGIC_... name and constant are automatically
|
|
combined to produce the #define directives in fs.h. */
|
|
|
|
case S_MAGIC_ADFS: /* 0xADF5 */
|
|
return "adfs";
|
|
case S_MAGIC_AFFS: /* 0xADFF */
|
|
return "affs";
|
|
case S_MAGIC_AUTOFS: /* 0x187 */
|
|
return "autofs";
|
|
case S_MAGIC_BEFS: /* 0x42465331 */
|
|
return "befs";
|
|
case S_MAGIC_BFS: /* 0x1BADFACE */
|
|
return "bfs";
|
|
case S_MAGIC_BINFMT_MISC: /* 0x42494e4d */
|
|
return "binfmt_misc";
|
|
case S_MAGIC_CODA: /* 0x73757245 */
|
|
return "coda";
|
|
case S_MAGIC_COH: /* 0x012FF7B7 */
|
|
return "coh";
|
|
case S_MAGIC_CRAMFS: /* 0x28CD3D45 */
|
|
return "cramfs";
|
|
case S_MAGIC_DEVFS: /* 0x1373 */
|
|
return "devfs";
|
|
case S_MAGIC_DEVPTS: /* 0x1CD1 */
|
|
return "devpts";
|
|
case S_MAGIC_EFS: /* 0x414A53 */
|
|
return "efs";
|
|
case S_MAGIC_EXT: /* 0x137D */
|
|
return "ext";
|
|
case S_MAGIC_EXT2: /* 0xEF53 */
|
|
return "ext2/ext3";
|
|
case S_MAGIC_EXT2_OLD: /* 0xEF51 */
|
|
return "ext2";
|
|
case S_MAGIC_FAT: /* 0x4006 */
|
|
return "fat";
|
|
case S_MAGIC_FUSECTL: /* 0x65735543 */
|
|
return "fusectl";
|
|
case S_MAGIC_HPFS: /* 0xF995E849 */
|
|
return "hpfs";
|
|
case S_MAGIC_HUGETLBFS: /* 0x958458f6 */
|
|
return "hugetlbfs";
|
|
case S_MAGIC_ISOFS: /* 0x9660 */
|
|
return "isofs";
|
|
case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
|
|
return "isofs";
|
|
case S_MAGIC_ISOFS_WIN: /* 0x4000 */
|
|
return "isofs";
|
|
case S_MAGIC_JFFS2: /* 0x72B6 */
|
|
return "jffs2";
|
|
case S_MAGIC_JFFS: /* 0x07C0 */
|
|
return "jffs";
|
|
case S_MAGIC_JFS: /* 0x3153464A */
|
|
return "jfs";
|
|
case S_MAGIC_MINIX: /* 0x137F */
|
|
return "minix";
|
|
case S_MAGIC_MINIX_30: /* 0x138F */
|
|
return "minix (30 char.)";
|
|
case S_MAGIC_MINIX_V2: /* 0x2468 */
|
|
return "minix v2";
|
|
case S_MAGIC_MINIX_V2_30: /* 0x2478 */
|
|
return "minix v2 (30 char.)";
|
|
case S_MAGIC_MSDOS: /* 0x4D44 */
|
|
return "msdos";
|
|
case S_MAGIC_NCP: /* 0x564C */
|
|
return "novell";
|
|
case S_MAGIC_NFS: /* 0x6969 */
|
|
return "nfs";
|
|
case S_MAGIC_NFSD: /* 0x6E667364 */
|
|
return "nfsd";
|
|
case S_MAGIC_NTFS: /* 0x5346544E */
|
|
return "ntfs";
|
|
case S_MAGIC_OPENPROM: /* 0x9fa1 */
|
|
return "openprom";
|
|
case S_MAGIC_PROC: /* 0x9FA0 */
|
|
return "proc";
|
|
case S_MAGIC_QNX4: /* 0x002F */
|
|
return "qnx4";
|
|
case S_MAGIC_RAMFS: /* 0x858458F6 */
|
|
return "ramfs";
|
|
case S_MAGIC_REISERFS: /* 0x52654973 */
|
|
return "reiserfs";
|
|
case S_MAGIC_ROMFS: /* 0x7275 */
|
|
return "romfs";
|
|
case S_MAGIC_SMB: /* 0x517B */
|
|
return "smb";
|
|
case S_MAGIC_SQUASHFS: /* 0x73717368 */
|
|
return "squashfs";
|
|
case S_MAGIC_SYSFS: /* 0x62656572 */
|
|
return "sysfs";
|
|
case S_MAGIC_SYSV2: /* 0x012FF7B6 */
|
|
return "sysv2";
|
|
case S_MAGIC_SYSV4: /* 0x012FF7B5 */
|
|
return "sysv4";
|
|
case S_MAGIC_TMPFS: /* 0x1021994 */
|
|
return "tmpfs";
|
|
case S_MAGIC_UDF: /* 0x15013346 */
|
|
return "udf";
|
|
case S_MAGIC_UFS: /* 0x00011954 */
|
|
return "ufs";
|
|
case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 */
|
|
return "ufs";
|
|
case S_MAGIC_USBDEVFS: /* 0x9FA2 */
|
|
return "usbdevfs";
|
|
case S_MAGIC_VXFS: /* 0xA501FCF5 */
|
|
return "vxfs";
|
|
case S_MAGIC_XENIX: /* 0x012FF7B4 */
|
|
return "xenix";
|
|
case S_MAGIC_XFS: /* 0x58465342 */
|
|
return "xfs";
|
|
case S_MAGIC_XIAFS: /* 0x012FD16D */
|
|
return "xia";
|
|
|
|
# elif __GNU__
|
|
case FSTYPE_UFS:
|
|
return "ufs";
|
|
case FSTYPE_NFS:
|
|
return "nfs";
|
|
case FSTYPE_GFS:
|
|
return "gfs";
|
|
case FSTYPE_LFS:
|
|
return "lfs";
|
|
case FSTYPE_SYSV:
|
|
return "sysv";
|
|
case FSTYPE_FTP:
|
|
return "ftp";
|
|
case FSTYPE_TAR:
|
|
return "tar";
|
|
case FSTYPE_AR:
|
|
return "ar";
|
|
case FSTYPE_CPIO:
|
|
return "cpio";
|
|
case FSTYPE_MSLOSS:
|
|
return "msloss";
|
|
case FSTYPE_CPM:
|
|
return "cpm";
|
|
case FSTYPE_HFS:
|
|
return "hfs";
|
|
case FSTYPE_DTFS:
|
|
return "dtfs";
|
|
case FSTYPE_GRFS:
|
|
return "grfs";
|
|
case FSTYPE_TERM:
|
|
return "term";
|
|
case FSTYPE_DEV:
|
|
return "dev";
|
|
case FSTYPE_PROC:
|
|
return "proc";
|
|
case FSTYPE_IFSOCK:
|
|
return "ifsock";
|
|
case FSTYPE_AFS:
|
|
return "afs";
|
|
case FSTYPE_DFS:
|
|
return "dfs";
|
|
case FSTYPE_PROC9:
|
|
return "proc9";
|
|
case FSTYPE_SOCKET:
|
|
return "socket";
|
|
case FSTYPE_MISC:
|
|
return "misc";
|
|
case FSTYPE_EXT2FS:
|
|
return "ext2/ext3";
|
|
case FSTYPE_HTTP:
|
|
return "http";
|
|
case FSTYPE_MEMFS:
|
|
return "memfs";
|
|
case FSTYPE_ISO9660:
|
|
return "iso9660";
|
|
# endif
|
|
default:
|
|
{
|
|
unsigned long int type = statfsbuf->f_type;
|
|
static char buf[sizeof "UNKNOWN (0x%lx)" - 3
|
|
+ (sizeof type * CHAR_BIT + 3) / 4];
|
|
sprintf (buf, "UNKNOWN (0x%lx)", type);
|
|
return buf;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static char *
|
|
human_access (struct stat const *statbuf)
|
|
{
|
|
static char modebuf[12];
|
|
filemodestring (statbuf, modebuf);
|
|
modebuf[10] = 0;
|
|
return modebuf;
|
|
}
|
|
|
|
static char *
|
|
human_time (struct timespec t)
|
|
{
|
|
static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
|
|
(INT_STRLEN_BOUND (int) /* YYYY */
|
|
+ 1 /* because YYYY might equal INT_MAX + 1900 */
|
|
+ sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
|
|
struct tm const *tm = localtime (&t.tv_sec);
|
|
if (tm == NULL)
|
|
return (TYPE_SIGNED (time_t)
|
|
? imaxtostr (t.tv_sec, str)
|
|
: umaxtostr (t.tv_sec, str));
|
|
nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
|
|
return str;
|
|
}
|
|
|
|
static void
|
|
out_string (char *pformat, size_t prefix_len, char const *arg)
|
|
{
|
|
strcpy (pformat + prefix_len, "s");
|
|
printf (pformat, arg);
|
|
}
|
|
static void
|
|
out_int (char *pformat, size_t prefix_len, intmax_t arg)
|
|
{
|
|
strcpy (pformat + prefix_len, PRIdMAX);
|
|
printf (pformat, arg);
|
|
}
|
|
static void
|
|
out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
|
|
{
|
|
strcpy (pformat + prefix_len, PRIuMAX);
|
|
printf (pformat, arg);
|
|
}
|
|
static void
|
|
out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
|
|
{
|
|
strcpy (pformat + prefix_len, PRIoMAX);
|
|
printf (pformat, arg);
|
|
}
|
|
static void
|
|
out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
|
|
{
|
|
strcpy (pformat + prefix_len, PRIxMAX);
|
|
printf (pformat, arg);
|
|
}
|
|
|
|
/* Very specialized function (modifies FORMAT), just so as to avoid
|
|
duplicating this code between both print_statfs and print_stat. */
|
|
static void
|
|
out_file_context (char const *filename, char *pformat, size_t prefix_len)
|
|
{
|
|
char *scontext;
|
|
if ((follow_links
|
|
? getfilecon (filename, &scontext)
|
|
: lgetfilecon (filename, &scontext)) < 0)
|
|
{
|
|
error (0, errno, _("failed to get security context of %s"),
|
|
quote (filename));
|
|
scontext = NULL;
|
|
}
|
|
strcpy (pformat + prefix_len, "s");
|
|
printf (pformat, (scontext ? scontext : "?"));
|
|
if (scontext)
|
|
freecon (scontext);
|
|
}
|
|
|
|
/* print statfs info */
|
|
static void
|
|
print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
|
|
void const *data)
|
|
{
|
|
STRUCT_STATVFS const *statfsbuf = data;
|
|
|
|
switch (m)
|
|
{
|
|
case 'n':
|
|
out_string (pformat, prefix_len, filename);
|
|
break;
|
|
|
|
case 'i':
|
|
{
|
|
#if STRUCT_STATXFS_F_FSID_IS_INTEGER
|
|
uintmax_t fsid = statfsbuf->f_fsid;
|
|
#else
|
|
typedef unsigned int fsid_word;
|
|
verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
|
|
verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
|
|
verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
|
|
fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
|
|
|
|
/* Assume a little-endian word order, as that is compatible
|
|
with glibc's statvfs implementation. */
|
|
uintmax_t fsid = 0;
|
|
int words = sizeof statfsbuf->f_fsid / sizeof *p;
|
|
int i;
|
|
for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
|
|
{
|
|
uintmax_t u = p[words - 1 - i];
|
|
fsid |= u << (i * CHAR_BIT * sizeof *p);
|
|
}
|
|
#endif
|
|
out_uint_x (pformat, prefix_len, fsid);
|
|
}
|
|
break;
|
|
|
|
case 'l':
|
|
OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
|
|
break;
|
|
case 't':
|
|
#if HAVE_STRUCT_STATXFS_F_TYPE
|
|
out_uint_x (pformat, prefix_len, statfsbuf->f_type);
|
|
#else
|
|
fputc ('?', stdout);
|
|
#endif
|
|
break;
|
|
case 'T':
|
|
out_string (pformat, prefix_len, human_fstype (statfsbuf));
|
|
break;
|
|
case 'b':
|
|
out_int (pformat, prefix_len, statfsbuf->f_blocks);
|
|
break;
|
|
case 'f':
|
|
out_int (pformat, prefix_len, statfsbuf->f_bfree);
|
|
break;
|
|
case 'a':
|
|
out_int (pformat, prefix_len, statfsbuf->f_bavail);
|
|
break;
|
|
case 's':
|
|
out_uint (pformat, prefix_len, statfsbuf->f_bsize);
|
|
break;
|
|
case 'S':
|
|
{
|
|
uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
|
|
if (! frsize)
|
|
frsize = statfsbuf->f_bsize;
|
|
out_uint (pformat, prefix_len, frsize);
|
|
}
|
|
break;
|
|
case 'c':
|
|
out_int (pformat, prefix_len, statfsbuf->f_files);
|
|
break;
|
|
case 'd':
|
|
out_int (pformat, prefix_len, statfsbuf->f_ffree);
|
|
break;
|
|
case 'C':
|
|
out_file_context (filename, pformat, prefix_len);
|
|
break;
|
|
default:
|
|
fputc ('?', stdout);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* print stat info */
|
|
static void
|
|
print_stat (char *pformat, size_t prefix_len, char m,
|
|
char const *filename, void const *data)
|
|
{
|
|
struct stat *statbuf = (struct stat *) data;
|
|
struct passwd *pw_ent;
|
|
struct group *gw_ent;
|
|
|
|
switch (m)
|
|
{
|
|
case 'n':
|
|
out_string (pformat, prefix_len, filename);
|
|
break;
|
|
case 'N':
|
|
out_string (pformat, prefix_len, quote (filename));
|
|
if (S_ISLNK (statbuf->st_mode))
|
|
{
|
|
char *linkname = areadlink_with_size (filename, statbuf->st_size);
|
|
if (linkname == NULL)
|
|
{
|
|
error (0, errno, _("cannot read symbolic link %s"),
|
|
quote (filename));
|
|
return;
|
|
}
|
|
printf (" -> ");
|
|
out_string (pformat, prefix_len, quote (linkname));
|
|
}
|
|
break;
|
|
case 'd':
|
|
out_uint (pformat, prefix_len, statbuf->st_dev);
|
|
break;
|
|
case 'D':
|
|
out_uint_x (pformat, prefix_len, statbuf->st_dev);
|
|
break;
|
|
case 'i':
|
|
out_uint (pformat, prefix_len, statbuf->st_ino);
|
|
break;
|
|
case 'a':
|
|
out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
|
|
break;
|
|
case 'A':
|
|
out_string (pformat, prefix_len, human_access (statbuf));
|
|
break;
|
|
case 'f':
|
|
out_uint_x (pformat, prefix_len, statbuf->st_mode);
|
|
break;
|
|
case 'F':
|
|
out_string (pformat, prefix_len, file_type (statbuf));
|
|
break;
|
|
case 'h':
|
|
out_uint (pformat, prefix_len, statbuf->st_nlink);
|
|
break;
|
|
case 'u':
|
|
out_uint (pformat, prefix_len, statbuf->st_uid);
|
|
break;
|
|
case 'U':
|
|
setpwent ();
|
|
pw_ent = getpwuid (statbuf->st_uid);
|
|
out_string (pformat, prefix_len,
|
|
pw_ent ? pw_ent->pw_name : "UNKNOWN");
|
|
break;
|
|
case 'g':
|
|
out_uint (pformat, prefix_len, statbuf->st_gid);
|
|
break;
|
|
case 'G':
|
|
setgrent ();
|
|
gw_ent = getgrgid (statbuf->st_gid);
|
|
out_string (pformat, prefix_len,
|
|
gw_ent ? gw_ent->gr_name : "UNKNOWN");
|
|
break;
|
|
case 't':
|
|
out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
|
|
break;
|
|
case 'T':
|
|
out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
|
|
break;
|
|
case 's':
|
|
out_uint (pformat, prefix_len, statbuf->st_size);
|
|
break;
|
|
case 'B':
|
|
out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
|
|
break;
|
|
case 'b':
|
|
out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
|
|
break;
|
|
case 'o':
|
|
out_uint (pformat, prefix_len, statbuf->st_blksize);
|
|
break;
|
|
case 'x':
|
|
out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
|
|
break;
|
|
case 'X':
|
|
if (TYPE_SIGNED (time_t))
|
|
out_int (pformat, prefix_len, statbuf->st_atime);
|
|
else
|
|
out_uint (pformat, prefix_len, statbuf->st_atime);
|
|
break;
|
|
case 'y':
|
|
out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
|
|
break;
|
|
case 'Y':
|
|
if (TYPE_SIGNED (time_t))
|
|
out_int (pformat, prefix_len, statbuf->st_mtime);
|
|
else
|
|
out_uint (pformat, prefix_len, statbuf->st_mtime);
|
|
break;
|
|
case 'z':
|
|
out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
|
|
break;
|
|
case 'Z':
|
|
if (TYPE_SIGNED (time_t))
|
|
out_int (pformat, prefix_len, statbuf->st_ctime);
|
|
else
|
|
out_uint (pformat, prefix_len, statbuf->st_ctime);
|
|
break;
|
|
case 'C':
|
|
out_file_context (filename, pformat, prefix_len);
|
|
break;
|
|
default:
|
|
fputc ('?', stdout);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Output a single-character \ escape. */
|
|
|
|
static void
|
|
print_esc_char (char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'a': /* Alert. */
|
|
c ='\a';
|
|
break;
|
|
case 'b': /* Backspace. */
|
|
c ='\b';
|
|
break;
|
|
case 'f': /* Form feed. */
|
|
c ='\f';
|
|
break;
|
|
case 'n': /* New line. */
|
|
c ='\n';
|
|
break;
|
|
case 'r': /* Carriage return. */
|
|
c ='\r';
|
|
break;
|
|
case 't': /* Horizontal tab. */
|
|
c ='\t';
|
|
break;
|
|
case 'v': /* Vertical tab. */
|
|
c ='\v';
|
|
break;
|
|
case '"':
|
|
case '\\':
|
|
break;
|
|
default:
|
|
error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
|
|
break;
|
|
}
|
|
putchar (c);
|
|
}
|
|
|
|
static void
|
|
print_it (char const *format, char const *filename,
|
|
void (*print_func) (char *, size_t, char, char const *, void const *),
|
|
void const *data)
|
|
{
|
|
/* Add 2 to accommodate our conversion of the stat `%s' format string
|
|
to the longer printf `%llu' one. */
|
|
enum
|
|
{
|
|
MAX_ADDITIONAL_BYTES =
|
|
(MAX (sizeof PRIdMAX,
|
|
MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
|
|
- 1)
|
|
};
|
|
size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
|
|
char *dest = xmalloc (n_alloc);
|
|
char const *b;
|
|
for (b = format; *b; b++)
|
|
{
|
|
switch (*b)
|
|
{
|
|
case '%':
|
|
{
|
|
size_t len = strspn (b + 1, "#-+.I 0123456789");
|
|
char const *fmt_char = b + len + 1;
|
|
memcpy (dest, b, len + 1);
|
|
|
|
b = fmt_char;
|
|
switch (*fmt_char)
|
|
{
|
|
case '\0':
|
|
--b;
|
|
/* fall through */
|
|
case '%':
|
|
if (0 < len)
|
|
{
|
|
dest[len + 1] = *fmt_char;
|
|
dest[len + 2] = '\0';
|
|
error (EXIT_FAILURE, 0, _("%s: invalid directive"),
|
|
quotearg_colon (dest));
|
|
}
|
|
putchar ('%');
|
|
break;
|
|
default:
|
|
print_func (dest, len + 1, *fmt_char, filename, data);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case '\\':
|
|
if ( ! interpret_backslash_escapes)
|
|
{
|
|
putchar ('\\');
|
|
break;
|
|
}
|
|
++b;
|
|
if (isodigit (*b))
|
|
{
|
|
int esc_value = octtobin (*b);
|
|
int esc_length = 1; /* number of octal digits */
|
|
for (++b; esc_length < 3 && isodigit (*b);
|
|
++esc_length, ++b)
|
|
{
|
|
esc_value = esc_value * 8 + octtobin (*b);
|
|
}
|
|
putchar (esc_value);
|
|
--b;
|
|
}
|
|
else if (*b == 'x' && isxdigit (to_uchar (b[1])))
|
|
{
|
|
int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
|
|
/* A hexadecimal \xhh escape sequence must have
|
|
1 or 2 hex. digits. */
|
|
++b;
|
|
if (isxdigit (to_uchar (b[1])))
|
|
{
|
|
++b;
|
|
esc_value = esc_value * 16 + hextobin (*b);
|
|
}
|
|
putchar (esc_value);
|
|
}
|
|
else if (*b == '\0')
|
|
{
|
|
error (0, 0, _("warning: backslash at end of format"));
|
|
putchar ('\\');
|
|
/* Arrange to exit the loop. */
|
|
--b;
|
|
}
|
|
else
|
|
{
|
|
print_esc_char (*b);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
putchar (*b);
|
|
break;
|
|
}
|
|
}
|
|
free (dest);
|
|
|
|
fputs (trailing_delim, stdout);
|
|
}
|
|
|
|
/* Stat the file system and print what we find. */
|
|
static bool
|
|
do_statfs (char const *filename, bool terse, char const *format)
|
|
{
|
|
STRUCT_STATVFS statfsbuf;
|
|
|
|
if (STATFS (filename, &statfsbuf) != 0)
|
|
{
|
|
error (0, errno, _("cannot read file system information for %s"),
|
|
quote (filename));
|
|
return false;
|
|
}
|
|
|
|
if (format == NULL)
|
|
{
|
|
format = (terse
|
|
? "%n %i %l %t %s %S %b %f %a %c %d\n"
|
|
: " File: \"%n\"\n"
|
|
" ID: %-8i Namelen: %-7l Type: %T\n"
|
|
"Block size: %-10s Fundamental block size: %S\n"
|
|
"Blocks: Total: %-10b Free: %-10f Available: %a\n"
|
|
"Inodes: Total: %-10c Free: %d\n");
|
|
}
|
|
|
|
print_it (format, filename, print_statfs, &statfsbuf);
|
|
return true;
|
|
}
|
|
|
|
/* stat the file and print what we find */
|
|
static bool
|
|
do_stat (char const *filename, bool terse, char const *format)
|
|
{
|
|
struct stat statbuf;
|
|
|
|
if ((follow_links ? stat : lstat) (filename, &statbuf) != 0)
|
|
{
|
|
error (0, errno, _("cannot stat %s"), quote (filename));
|
|
return false;
|
|
}
|
|
|
|
if (format == NULL)
|
|
{
|
|
if (terse)
|
|
{
|
|
format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
|
|
}
|
|
else
|
|
{
|
|
/* Temporary hack to match original output until conditional
|
|
implemented. */
|
|
if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
|
|
{
|
|
format =
|
|
" File: %N\n"
|
|
" Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
|
|
"Device: %Dh/%dd\tInode: %-10i Links: %-5h"
|
|
" Device type: %t,%T\n"
|
|
"Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
|
|
"Access: %x\n" "Modify: %y\n" "Change: %z\n";
|
|
}
|
|
else
|
|
{
|
|
format =
|
|
" File: %N\n"
|
|
" Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
|
|
"Device: %Dh/%dd\tInode: %-10i Links: %h\n"
|
|
"Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
|
|
"Access: %x\n" "Modify: %y\n" "Change: %z\n";
|
|
}
|
|
}
|
|
}
|
|
print_it (format, filename, print_stat, &statbuf);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
usage (int status)
|
|
{
|
|
if (status != EXIT_SUCCESS)
|
|
fprintf (stderr, _("Try `%s --help' for more information.\n"),
|
|
program_name);
|
|
else
|
|
{
|
|
printf (_("Usage: %s [OPTION] FILE...\n"), program_name);
|
|
fputs (_("\
|
|
Display file or file system status.\n\
|
|
\n\
|
|
-L, --dereference follow links\n\
|
|
-f, --file-system display file system status instead of file status\n\
|
|
"), stdout);
|
|
fputs (_("\
|
|
-c --format=FORMAT use the specified FORMAT instead of the default;\n\
|
|
output a newline after each use of FORMAT\n\
|
|
--printf=FORMAT like --format, but interpret backslash escapes,\n\
|
|
and do not output a mandatory trailing newline.\n\
|
|
If you want a newline, include \\n in FORMAT.\n\
|
|
-t, --terse print the information in terse form\n\
|
|
"), stdout);
|
|
fputs (HELP_OPTION_DESCRIPTION, stdout);
|
|
fputs (VERSION_OPTION_DESCRIPTION, stdout);
|
|
|
|
fputs (_("\n\
|
|
The valid format sequences for files (without --file-system):\n\
|
|
\n\
|
|
%a Access rights in octal\n\
|
|
%A Access rights in human readable form\n\
|
|
%b Number of blocks allocated (see %B)\n\
|
|
%B The size in bytes of each block reported by %b\n\
|
|
%C SELinux security context string\n\
|
|
"), stdout);
|
|
fputs (_("\
|
|
%d Device number in decimal\n\
|
|
%D Device number in hex\n\
|
|
%f Raw mode in hex\n\
|
|
%F File type\n\
|
|
%g Group ID of owner\n\
|
|
%G Group name of owner\n\
|
|
"), stdout);
|
|
fputs (_("\
|
|
%h Number of hard links\n\
|
|
%i Inode number\n\
|
|
%n File name\n\
|
|
%N Quoted file name with dereference if symbolic link\n\
|
|
%o I/O block size\n\
|
|
%s Total size, in bytes\n\
|
|
%t Major device type in hex\n\
|
|
%T Minor device type in hex\n\
|
|
"), stdout);
|
|
fputs (_("\
|
|
%u User ID of owner\n\
|
|
%U User name of owner\n\
|
|
%x Time of last access\n\
|
|
%X Time of last access as seconds since Epoch\n\
|
|
%y Time of last modification\n\
|
|
%Y Time of last modification as seconds since Epoch\n\
|
|
%z Time of last change\n\
|
|
%Z Time of last change as seconds since Epoch\n\
|
|
\n\
|
|
"), stdout);
|
|
|
|
fputs (_("\
|
|
Valid format sequences for file systems:\n\
|
|
\n\
|
|
%a Free blocks available to non-superuser\n\
|
|
%b Total data blocks in file system\n\
|
|
%c Total file nodes in file system\n\
|
|
%d Free file nodes in file system\n\
|
|
%f Free blocks in file system\n\
|
|
%C SELinux security context string\n\
|
|
"), stdout);
|
|
fputs (_("\
|
|
%i File System ID in hex\n\
|
|
%l Maximum length of filenames\n\
|
|
%n File name\n\
|
|
%s Block size (for faster transfers)\n\
|
|
%S Fundamental block size (for block counts)\n\
|
|
%t Type in hex\n\
|
|
%T Type in human readable form\n\
|
|
"), stdout);
|
|
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
|
|
emit_bug_reporting_address ();
|
|
}
|
|
exit (status);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
int c;
|
|
int i;
|
|
bool fs = false;
|
|
bool terse = false;
|
|
char *format = NULL;
|
|
bool ok = true;
|
|
|
|
initialize_main (&argc, &argv);
|
|
program_name = argv[0];
|
|
setlocale (LC_ALL, "");
|
|
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
textdomain (PACKAGE);
|
|
|
|
atexit (close_stdout);
|
|
|
|
while ((c = getopt_long (argc, argv, "c:fLtZ", long_options, NULL)) != -1)
|
|
{
|
|
switch (c)
|
|
{
|
|
case PRINTF_OPTION:
|
|
format = optarg;
|
|
interpret_backslash_escapes = true;
|
|
trailing_delim = "";
|
|
break;
|
|
|
|
case 'c':
|
|
format = optarg;
|
|
interpret_backslash_escapes = false;
|
|
trailing_delim = "\n";
|
|
break;
|
|
|
|
case 'L':
|
|
follow_links = true;
|
|
break;
|
|
|
|
case 'f':
|
|
fs = true;
|
|
break;
|
|
|
|
case 't':
|
|
terse = true;
|
|
break;
|
|
|
|
case 'Z': /* FIXME: remove in 2010, warn in mid 2008 */
|
|
/* Ignored, for compatibility with distributions
|
|
that implemented this before upstream. */
|
|
break;
|
|
|
|
case_GETOPT_HELP_CHAR;
|
|
|
|
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
|
|
default:
|
|
usage (EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
if (argc == optind)
|
|
{
|
|
error (0, 0, _("missing operand"));
|
|
usage (EXIT_FAILURE);
|
|
}
|
|
|
|
for (i = optind; i < argc; i++)
|
|
ok &= (fs
|
|
? do_statfs (argv[i], terse, format)
|
|
: do_stat (argv[i], terse, format));
|
|
|
|
exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
|
|
}
|