stat: add %m to output the mount point for a file
[coreutils/ericb.git] / src / stat.c
blobf98597885822f49b80fca21fc9511ec5c897dede
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. */
19 #include <config.h>
21 /* Keep this conditional in sync with the similar conditional in
22 ../m4/stat-prog.m4. */
23 #if (STAT_STATVFS \
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
27 #else
28 # define USE_STATVFS 0
29 #endif
31 #include <stddef.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #if USE_STATVFS
37 # include <sys/statvfs.h>
38 #elif HAVE_SYS_VFS_H
39 # include <sys/vfs.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>
51 # include <nfs/vfs.h>
52 # endif
53 #elif HAVE_OS_H /* BeOS */
54 # include <fs_info.h>
55 #endif
56 #include <selinux/selinux.h>
58 #include "system.h"
60 #include "alignof.h"
61 #include "areadlink.h"
62 #include "error.h"
63 #include "file-type.h"
64 #include "filemode.h"
65 #include "fs.h"
66 #include "getopt.h"
67 #include "mountlist.h"
68 #include "quote.h"
69 #include "quotearg.h"
70 #include "stat-time.h"
71 #include "strftime.h"
72 #include "find-mount-point.h"
74 #if USE_STATVFS
75 # define STRUCT_STATVFS struct statvfs
76 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
77 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
78 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
79 # define SB_F_NAMEMAX(S) ((S)->f_namemax)
80 # endif
81 # define STATFS statvfs
82 # define STATFS_FRSIZE(S) ((S)->f_frsize)
83 #else
84 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
85 # if HAVE_STRUCT_STATFS_F_NAMELEN
86 # define SB_F_NAMEMAX(S) ((S)->f_namelen)
87 # endif
88 # define STATFS statfs
89 # if HAVE_OS_H /* BeOS */
90 /* BeOS has a statvfs function, but it does not return sensible values
91 for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
92 f_fstypename. Use 'struct fs_info' instead. */
93 static int ATTRIBUTE_WARN_UNUSED_RESULT
94 statfs (char const *filename, struct fs_info *buf)
96 dev_t device = dev_for_path (filename);
97 if (device < 0)
99 errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
100 : device == B_BAD_VALUE ? EINVAL
101 : device == B_NAME_TOO_LONG ? ENAMETOOLONG
102 : device == B_NO_MEMORY ? ENOMEM
103 : device == B_FILE_ERROR ? EIO
104 : 0);
105 return -1;
107 /* If successful, buf->dev will be == device. */
108 return fs_stat_dev (device, buf);
110 # define f_fsid dev
111 # define f_blocks total_blocks
112 # define f_bfree free_blocks
113 # define f_bavail free_blocks
114 # define f_bsize io_size
115 # define f_files total_nodes
116 # define f_ffree free_nodes
117 # define STRUCT_STATVFS struct fs_info
118 # define STRUCT_STATXFS_F_FSID_IS_INTEGER true
119 # define STATFS_FRSIZE(S) ((S)->block_size)
120 # else
121 # define STRUCT_STATVFS struct statfs
122 # define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
123 # define STATFS_FRSIZE(S) 0
124 # endif
125 #endif
127 #ifdef SB_F_NAMEMAX
128 # define OUT_NAMEMAX out_uint
129 #else
130 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */
131 # define SB_F_NAMEMAX(S) "*"
132 # define OUT_NAMEMAX out_string
133 #endif
135 #if HAVE_STRUCT_STATVFS_F_BASETYPE
136 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
137 #else
138 # if HAVE_STRUCT_STATVFS_F_FSTYPENAME || HAVE_STRUCT_STATFS_F_FSTYPENAME
139 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
140 # elif HAVE_OS_H /* BeOS */
141 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME fsh_name
142 # endif
143 #endif
145 /* FIXME: these are used by printf.c, too */
146 #define isodigit(c) ('0' <= (c) && (c) <= '7')
147 #define octtobin(c) ((c) - '0')
148 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
149 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
151 #define PROGRAM_NAME "stat"
153 #define AUTHORS proper_name ("Michael Meskes")
155 enum
157 PRINTF_OPTION = CHAR_MAX + 1
160 static struct option const long_options[] =
162 {"context", no_argument, 0, 'Z'},
163 {"dereference", no_argument, NULL, 'L'},
164 {"file-system", no_argument, NULL, 'f'},
165 {"format", required_argument, NULL, 'c'},
166 {"printf", required_argument, NULL, PRINTF_OPTION},
167 {"terse", no_argument, NULL, 't'},
168 {GETOPT_HELP_OPTION_DECL},
169 {GETOPT_VERSION_OPTION_DECL},
170 {NULL, 0, NULL, 0}
173 /* Whether to follow symbolic links; True for --dereference (-L). */
174 static bool follow_links;
176 /* Whether to interpret backslash-escape sequences.
177 True for --printf=FMT, not for --format=FMT (-c). */
178 static bool interpret_backslash_escapes;
180 /* The trailing delimiter string:
181 "" for --printf=FMT, "\n" for --format=FMT (-c). */
182 static char const *trailing_delim = "";
184 /* Return the type of the specified file system.
185 Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
186 Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
187 Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
188 Still others have neither and have to get by with f_type (GNU/Linux).
189 But f_type may only exist in statfs (Cygwin). */
190 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
191 human_fstype (STRUCT_STATVFS const *statfsbuf)
193 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
194 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
195 #else
196 switch (statfsbuf->f_type)
198 # if defined __linux__
200 /* Compare with what's in libc:
201 f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
202 sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
203 | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
204 -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
205 | sort > sym_libc
206 perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
207 -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
208 | sort > sym_stat
209 diff -u sym_stat sym_libc
212 /* Also compare with the list in "man 2 statfs" using the
213 fs-magic-compare make target. */
215 /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
216 statements must be followed by a hexadecimal constant in
217 a comment. The S_MAGIC_... name and constant are automatically
218 combined to produce the #define directives in fs.h. */
220 case S_MAGIC_ADFS: /* 0xADF5 */
221 return "adfs";
222 case S_MAGIC_AFFS: /* 0xADFF */
223 return "affs";
224 case S_MAGIC_AFS: /* 0x5346414F */
225 return "afs";
226 case S_MAGIC_ANON_INODE_FS: /* 0x09041934 */
227 return "anon-inode FS";
228 case S_MAGIC_AUTOFS: /* 0x0187 */
229 return "autofs";
230 case S_MAGIC_BEFS: /* 0x42465331 */
231 return "befs";
232 case S_MAGIC_BFS: /* 0x1BADFACE */
233 return "bfs";
234 case S_MAGIC_BINFMT_MISC: /* 0x42494E4D */
235 return "binfmt_misc";
236 case S_MAGIC_BTRFS: /* 0x9123683E */
237 return "btrfs";
238 case S_MAGIC_CGROUP: /* 0x0027E0EB */
239 return "cgroupfs";
240 case S_MAGIC_CIFS: /* 0xFF534D42 */
241 return "cifs";
242 case S_MAGIC_CODA: /* 0x73757245 */
243 return "coda";
244 case S_MAGIC_COH: /* 0x012FF7B7 */
245 return "coh";
246 case S_MAGIC_CRAMFS: /* 0x28CD3D45 */
247 return "cramfs";
248 case S_MAGIC_CRAMFS_WEND: /* 0x453DCD28 */
249 return "cramfs-wend";
250 case S_MAGIC_DEBUGFS: /* 0x64626720 */
251 return "debugfs";
252 case S_MAGIC_DEVFS: /* 0x1373 */
253 return "devfs";
254 case S_MAGIC_DEVPTS: /* 0x1CD1 */
255 return "devpts";
256 case S_MAGIC_EFS: /* 0x00414A53 */
257 return "efs";
258 case S_MAGIC_EXT: /* 0x137D */
259 return "ext";
260 case S_MAGIC_EXT2: /* 0xEF53 */
261 return "ext2/ext3";
262 case S_MAGIC_EXT2_OLD: /* 0xEF51 */
263 return "ext2";
264 case S_MAGIC_FAT: /* 0x4006 */
265 return "fat";
266 case S_MAGIC_FUSEBLK: /* 0x65735546 */
267 return "fuseblk";
268 case S_MAGIC_FUSECTL: /* 0x65735543 */
269 return "fusectl";
270 case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA */
271 return "futexfs";
272 case S_MAGIC_GFS: /* 0x1161970 */
273 return "gfs/gfs2";
274 case S_MAGIC_HFS: /* 0x4244 */
275 return "hfs";
276 case S_MAGIC_HPFS: /* 0xF995E849 */
277 return "hpfs";
278 case S_MAGIC_HUGETLBFS: /* 0x958458F6 */
279 return "hugetlbfs";
280 case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA */
281 return "inotifyfs";
282 case S_MAGIC_ISOFS: /* 0x9660 */
283 return "isofs";
284 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
285 return "isofs";
286 case S_MAGIC_ISOFS_WIN: /* 0x4000 */
287 return "isofs";
288 case S_MAGIC_JFFS: /* 0x07C0 */
289 return "jffs";
290 case S_MAGIC_JFFS2: /* 0x72B6 */
291 return "jffs2";
292 case S_MAGIC_JFS: /* 0x3153464A */
293 return "jfs";
294 case S_MAGIC_KAFS: /* 0x6B414653 */
295 return "k-afs";
296 case S_MAGIC_LUSTRE: /* 0x0BD00BD0 */
297 return "lustre";
298 case S_MAGIC_MINIX: /* 0x137F */
299 return "minix";
300 case S_MAGIC_MINIX_30: /* 0x138F */
301 return "minix (30 char.)";
302 case S_MAGIC_MINIX_V2: /* 0x2468 */
303 return "minix v2";
304 case S_MAGIC_MINIX_V2_30: /* 0x2478 */
305 return "minix v2 (30 char.)";
306 case S_MAGIC_MINIX_V3: /* 0x4D5A */
307 return "minix3";
308 case S_MAGIC_MSDOS: /* 0x4D44 */
309 return "msdos";
310 case S_MAGIC_NCP: /* 0x564C */
311 return "novell";
312 case S_MAGIC_NFS: /* 0x6969 */
313 return "nfs";
314 case S_MAGIC_NFSD: /* 0x6E667364 */
315 return "nfsd";
316 case S_MAGIC_NILFS: /* 0x3434 */
317 return "nilfs";
318 case S_MAGIC_NTFS: /* 0x5346544E */
319 return "ntfs";
320 case S_MAGIC_OPENPROM: /* 0x9FA1 */
321 return "openprom";
322 case S_MAGIC_OCFS2: /* 0x7461636f */
323 return "ocfs2";
324 case S_MAGIC_PROC: /* 0x9FA0 */
325 return "proc";
326 case S_MAGIC_QNX4: /* 0x002F */
327 return "qnx4";
328 case S_MAGIC_RAMFS: /* 0x858458F6 */
329 return "ramfs";
330 case S_MAGIC_REISERFS: /* 0x52654973 */
331 return "reiserfs";
332 case S_MAGIC_ROMFS: /* 0x7275 */
333 return "romfs";
334 case S_MAGIC_RPC_PIPEFS: /* 0x67596969 */
335 return "rpc_pipefs";
336 case S_MAGIC_SECURITYFS: /* 0x73636673 */
337 return "securityfs";
338 case S_MAGIC_SELINUX: /* 0xF97CFF8C */
339 return "selinux";
340 case S_MAGIC_SMB: /* 0x517B */
341 return "smb";
342 case S_MAGIC_SOCKFS: /* 0x534F434B */
343 return "sockfs";
344 case S_MAGIC_SQUASHFS: /* 0x73717368 */
345 return "squashfs";
346 case S_MAGIC_SYSFS: /* 0x62656572 */
347 return "sysfs";
348 case S_MAGIC_SYSV2: /* 0x012FF7B6 */
349 return "sysv2";
350 case S_MAGIC_SYSV4: /* 0x012FF7B5 */
351 return "sysv4";
352 case S_MAGIC_TMPFS: /* 0x01021994 */
353 return "tmpfs";
354 case S_MAGIC_UDF: /* 0x15013346 */
355 return "udf";
356 case S_MAGIC_UFS: /* 0x00011954 */
357 return "ufs";
358 case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 */
359 return "ufs";
360 case S_MAGIC_USBDEVFS: /* 0x9FA2 */
361 return "usbdevfs";
362 case S_MAGIC_VXFS: /* 0xA501FCF5 */
363 return "vxfs";
364 case S_MAGIC_XENFS: /* 0xABBA1974 */
365 return "xenfs";
366 case S_MAGIC_XENIX: /* 0x012FF7B4 */
367 return "xenix";
368 case S_MAGIC_XFS: /* 0x58465342 */
369 return "xfs";
370 case S_MAGIC_XIAFS: /* 0x012FD16D */
371 return "xia";
373 # elif __GNU__
374 case FSTYPE_UFS:
375 return "ufs";
376 case FSTYPE_NFS:
377 return "nfs";
378 case FSTYPE_GFS:
379 return "gfs";
380 case FSTYPE_LFS:
381 return "lfs";
382 case FSTYPE_SYSV:
383 return "sysv";
384 case FSTYPE_FTP:
385 return "ftp";
386 case FSTYPE_TAR:
387 return "tar";
388 case FSTYPE_AR:
389 return "ar";
390 case FSTYPE_CPIO:
391 return "cpio";
392 case FSTYPE_MSLOSS:
393 return "msloss";
394 case FSTYPE_CPM:
395 return "cpm";
396 case FSTYPE_HFS:
397 return "hfs";
398 case FSTYPE_DTFS:
399 return "dtfs";
400 case FSTYPE_GRFS:
401 return "grfs";
402 case FSTYPE_TERM:
403 return "term";
404 case FSTYPE_DEV:
405 return "dev";
406 case FSTYPE_PROC:
407 return "proc";
408 case FSTYPE_IFSOCK:
409 return "ifsock";
410 case FSTYPE_AFS:
411 return "afs";
412 case FSTYPE_DFS:
413 return "dfs";
414 case FSTYPE_PROC9:
415 return "proc9";
416 case FSTYPE_SOCKET:
417 return "socket";
418 case FSTYPE_MISC:
419 return "misc";
420 case FSTYPE_EXT2FS:
421 return "ext2/ext3";
422 case FSTYPE_HTTP:
423 return "http";
424 case FSTYPE_MEMFS:
425 return "memfs";
426 case FSTYPE_ISO9660:
427 return "iso9660";
428 # endif
429 default:
431 unsigned long int type = statfsbuf->f_type;
432 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
433 + (sizeof type * CHAR_BIT + 3) / 4];
434 sprintf (buf, "UNKNOWN (0x%lx)", type);
435 return buf;
438 #endif
441 static char * ATTRIBUTE_WARN_UNUSED_RESULT
442 human_access (struct stat const *statbuf)
444 static char modebuf[12];
445 filemodestring (statbuf, modebuf);
446 modebuf[10] = 0;
447 return modebuf;
450 static char * ATTRIBUTE_WARN_UNUSED_RESULT
451 human_time (struct timespec t)
453 static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
454 (INT_STRLEN_BOUND (int) /* YYYY */
455 + 1 /* because YYYY might equal INT_MAX + 1900 */
456 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
457 struct tm const *tm = localtime (&t.tv_sec);
458 if (tm == NULL)
459 return timetostr (t.tv_sec, str);
460 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
461 return str;
464 static void
465 out_string (char *pformat, size_t prefix_len, char const *arg)
467 strcpy (pformat + prefix_len, "s");
468 printf (pformat, arg);
470 static void
471 out_int (char *pformat, size_t prefix_len, intmax_t arg)
473 strcpy (pformat + prefix_len, PRIdMAX);
474 printf (pformat, arg);
476 static void
477 out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
479 strcpy (pformat + prefix_len, PRIuMAX);
480 printf (pformat, arg);
482 static void
483 out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
485 strcpy (pformat + prefix_len, PRIoMAX);
486 printf (pformat, arg);
488 static void
489 out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
491 strcpy (pformat + prefix_len, PRIxMAX);
492 printf (pformat, arg);
495 /* Very specialized function (modifies FORMAT), just so as to avoid
496 duplicating this code between both print_statfs and print_stat.
497 Return zero upon success, nonzero upon failure. */
498 static bool ATTRIBUTE_WARN_UNUSED_RESULT
499 out_file_context (char const *filename, char *pformat, size_t prefix_len)
501 char *scontext;
502 bool fail = false;
504 if ((follow_links
505 ? getfilecon (filename, &scontext)
506 : lgetfilecon (filename, &scontext)) < 0)
508 error (0, errno, _("failed to get security context of %s"),
509 quote (filename));
510 scontext = NULL;
511 fail = true;
513 strcpy (pformat + prefix_len, "s");
514 printf (pformat, (scontext ? scontext : "?"));
515 if (scontext)
516 freecon (scontext);
517 return fail;
520 /* Print statfs info. Return zero upon success, nonzero upon failure. */
521 static bool ATTRIBUTE_WARN_UNUSED_RESULT
522 print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
523 void const *data)
525 STRUCT_STATVFS const *statfsbuf = data;
526 bool fail = false;
528 switch (m)
530 case 'n':
531 out_string (pformat, prefix_len, filename);
532 break;
534 case 'i':
536 #if STRUCT_STATXFS_F_FSID_IS_INTEGER
537 uintmax_t fsid = statfsbuf->f_fsid;
538 #else
539 typedef unsigned int fsid_word;
540 verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
541 verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
542 verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
543 fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
545 /* Assume a little-endian word order, as that is compatible
546 with glibc's statvfs implementation. */
547 uintmax_t fsid = 0;
548 int words = sizeof statfsbuf->f_fsid / sizeof *p;
549 int i;
550 for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
552 uintmax_t u = p[words - 1 - i];
553 fsid |= u << (i * CHAR_BIT * sizeof *p);
555 #endif
556 out_uint_x (pformat, prefix_len, fsid);
558 break;
560 case 'l':
561 OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
562 break;
563 case 't':
564 #if HAVE_STRUCT_STATXFS_F_TYPE
565 out_uint_x (pformat, prefix_len, statfsbuf->f_type);
566 #else
567 fputc ('?', stdout);
568 #endif
569 break;
570 case 'T':
571 out_string (pformat, prefix_len, human_fstype (statfsbuf));
572 break;
573 case 'b':
574 out_int (pformat, prefix_len, statfsbuf->f_blocks);
575 break;
576 case 'f':
577 out_int (pformat, prefix_len, statfsbuf->f_bfree);
578 break;
579 case 'a':
580 out_int (pformat, prefix_len, statfsbuf->f_bavail);
581 break;
582 case 's':
583 out_uint (pformat, prefix_len, statfsbuf->f_bsize);
584 break;
585 case 'S':
587 uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
588 if (! frsize)
589 frsize = statfsbuf->f_bsize;
590 out_uint (pformat, prefix_len, frsize);
592 break;
593 case 'c':
594 out_uint (pformat, prefix_len, statfsbuf->f_files);
595 break;
596 case 'd':
597 out_int (pformat, prefix_len, statfsbuf->f_ffree);
598 break;
599 case 'C':
600 fail |= out_file_context (filename, pformat, prefix_len);
601 break;
602 default:
603 fputc ('?', stdout);
604 break;
606 return fail;
609 /* Return any bind mounted source for a path.
610 The caller should not free the returned buffer.
611 Return NULL if no bind mount found. */
612 static char const * ATTRIBUTE_WARN_UNUSED_RESULT
613 find_bind_mount (char const * name)
615 char const * bind_mount = NULL;
617 static struct mount_entry *mount_list;
618 static bool tried_mount_list = false;
619 if (!tried_mount_list) /* attempt/warn once per process. */
621 if (!(mount_list = read_file_system_list (false)))
622 error (0, errno, "%s", _("cannot read table of mounted file systems"));
623 tried_mount_list = true;
626 struct mount_entry *me;
627 for (me = mount_list; me; me = me->me_next)
629 if (me->me_dummy && me->me_devname[0] == '/'
630 && STREQ (me->me_mountdir, name))
632 struct stat name_stats;
633 struct stat dev_stats;
635 if (stat (name, &name_stats) == 0
636 && stat (me->me_devname, &dev_stats) == 0
637 && SAME_INODE (name_stats, dev_stats))
639 bind_mount = me->me_devname;
640 break;
645 return bind_mount;
648 /* Print mount point. Return zero upon success, nonzero upon failure. */
649 static bool ATTRIBUTE_WARN_UNUSED_RESULT
650 out_mount_point (char const *filename, char *pformat, size_t prefix_len,
651 const struct stat *statp)
654 char const *np = "?", *bp = NULL;
655 char *mp = NULL;
656 bool fail = true;
658 /* Look for bind mounts first. Note we output the immediate alias,
659 rather than further resolving to a base device mount point. */
660 if (follow_links || !S_ISLNK (statp->st_mode))
662 char *resolved = canonicalize_file_name (filename);
663 if (!resolved)
665 error (0, errno, _("failed to canonicalize %s"), quote (filename));
666 goto print_mount_point;
668 bp = find_bind_mount (resolved);
669 free (resolved);
670 if (bp)
672 fail = false;
673 goto print_mount_point;
677 /* If there is no direct bind mount, then navigate
678 back up the tree looking for a device change.
679 Note we don't detect if any of the directory components
680 are bind mounted to the same device, but that's OK
681 since we've not directly queried them. */
682 if ((mp = find_mount_point (filename, statp)))
684 /* This dir might be bind mounted to another device,
685 so we resolve the bound source in that case also. */
686 bp = find_bind_mount (mp);
687 fail = false;
690 print_mount_point:
692 out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
693 free (mp);
694 return fail;
697 /* Print stat info. Return zero upon success, nonzero upon failure. */
698 static bool
699 print_stat (char *pformat, size_t prefix_len, char m,
700 char const *filename, void const *data)
702 struct stat *statbuf = (struct stat *) data;
703 struct passwd *pw_ent;
704 struct group *gw_ent;
705 bool fail = false;
707 switch (m)
709 case 'n':
710 out_string (pformat, prefix_len, filename);
711 break;
712 case 'N':
713 out_string (pformat, prefix_len, quote (filename));
714 if (S_ISLNK (statbuf->st_mode))
716 char *linkname = areadlink_with_size (filename, statbuf->st_size);
717 if (linkname == NULL)
719 error (0, errno, _("cannot read symbolic link %s"),
720 quote (filename));
721 return true;
723 printf (" -> ");
724 out_string (pformat, prefix_len, quote (linkname));
725 free (linkname);
727 break;
728 case 'd':
729 out_uint (pformat, prefix_len, statbuf->st_dev);
730 break;
731 case 'D':
732 out_uint_x (pformat, prefix_len, statbuf->st_dev);
733 break;
734 case 'i':
735 out_uint (pformat, prefix_len, statbuf->st_ino);
736 break;
737 case 'a':
738 out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
739 break;
740 case 'A':
741 out_string (pformat, prefix_len, human_access (statbuf));
742 break;
743 case 'f':
744 out_uint_x (pformat, prefix_len, statbuf->st_mode);
745 break;
746 case 'F':
747 out_string (pformat, prefix_len, file_type (statbuf));
748 break;
749 case 'h':
750 out_uint (pformat, prefix_len, statbuf->st_nlink);
751 break;
752 case 'u':
753 out_uint (pformat, prefix_len, statbuf->st_uid);
754 break;
755 case 'U':
756 setpwent ();
757 pw_ent = getpwuid (statbuf->st_uid);
758 out_string (pformat, prefix_len,
759 pw_ent ? pw_ent->pw_name : "UNKNOWN");
760 break;
761 case 'g':
762 out_uint (pformat, prefix_len, statbuf->st_gid);
763 break;
764 case 'G':
765 setgrent ();
766 gw_ent = getgrgid (statbuf->st_gid);
767 out_string (pformat, prefix_len,
768 gw_ent ? gw_ent->gr_name : "UNKNOWN");
769 break;
770 case 't':
771 out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
772 break;
773 case 'm':
774 fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
775 break;
776 case 'T':
777 out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
778 break;
779 case 's':
780 out_uint (pformat, prefix_len, statbuf->st_size);
781 break;
782 case 'B':
783 out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
784 break;
785 case 'b':
786 out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
787 break;
788 case 'o':
789 out_uint (pformat, prefix_len, statbuf->st_blksize);
790 break;
791 case 'x':
792 out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
793 break;
794 case 'X':
795 if (TYPE_SIGNED (time_t))
796 out_int (pformat, prefix_len, statbuf->st_atime);
797 else
798 out_uint (pformat, prefix_len, statbuf->st_atime);
799 break;
800 case 'y':
801 out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
802 break;
803 case 'Y':
804 if (TYPE_SIGNED (time_t))
805 out_int (pformat, prefix_len, statbuf->st_mtime);
806 else
807 out_uint (pformat, prefix_len, statbuf->st_mtime);
808 break;
809 case 'z':
810 out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
811 break;
812 case 'Z':
813 if (TYPE_SIGNED (time_t))
814 out_int (pformat, prefix_len, statbuf->st_ctime);
815 else
816 out_uint (pformat, prefix_len, statbuf->st_ctime);
817 break;
818 case 'C':
819 fail |= out_file_context (filename, pformat, prefix_len);
820 break;
821 default:
822 fputc ('?', stdout);
823 break;
825 return fail;
828 /* Output a single-character \ escape. */
830 static void
831 print_esc_char (char c)
833 switch (c)
835 case 'a': /* Alert. */
836 c ='\a';
837 break;
838 case 'b': /* Backspace. */
839 c ='\b';
840 break;
841 case 'e': /* Escape. */
842 c ='\x1B';
843 break;
844 case 'f': /* Form feed. */
845 c ='\f';
846 break;
847 case 'n': /* New line. */
848 c ='\n';
849 break;
850 case 'r': /* Carriage return. */
851 c ='\r';
852 break;
853 case 't': /* Horizontal tab. */
854 c ='\t';
855 break;
856 case 'v': /* Vertical tab. */
857 c ='\v';
858 break;
859 case '"':
860 case '\\':
861 break;
862 default:
863 error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
864 break;
866 putchar (c);
869 /* Print the information specified by the format string, FORMAT,
870 calling PRINT_FUNC for each %-directive encountered.
871 Return zero upon success, nonzero upon failure. */
872 static bool ATTRIBUTE_WARN_UNUSED_RESULT
873 print_it (char const *format, char const *filename,
874 bool (*print_func) (char *, size_t, char, char const *, void const *),
875 void const *data)
877 bool fail = false;
879 /* Add 2 to accommodate our conversion of the stat `%s' format string
880 to the longer printf `%llu' one. */
881 enum
883 MAX_ADDITIONAL_BYTES =
884 (MAX (sizeof PRIdMAX,
885 MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
886 - 1)
888 size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
889 char *dest = xmalloc (n_alloc);
890 char const *b;
891 for (b = format; *b; b++)
893 switch (*b)
895 case '%':
897 size_t len = strspn (b + 1, "#-+.I 0123456789");
898 char const *fmt_char = b + len + 1;
899 memcpy (dest, b, len + 1);
901 b = fmt_char;
902 switch (*fmt_char)
904 case '\0':
905 --b;
906 /* fall through */
907 case '%':
908 if (0 < len)
910 dest[len + 1] = *fmt_char;
911 dest[len + 2] = '\0';
912 error (EXIT_FAILURE, 0, _("%s: invalid directive"),
913 quotearg_colon (dest));
915 putchar ('%');
916 break;
917 default:
918 fail |= print_func (dest, len + 1, *fmt_char, filename, data);
919 break;
921 break;
924 case '\\':
925 if ( ! interpret_backslash_escapes)
927 putchar ('\\');
928 break;
930 ++b;
931 if (isodigit (*b))
933 int esc_value = octtobin (*b);
934 int esc_length = 1; /* number of octal digits */
935 for (++b; esc_length < 3 && isodigit (*b);
936 ++esc_length, ++b)
938 esc_value = esc_value * 8 + octtobin (*b);
940 putchar (esc_value);
941 --b;
943 else if (*b == 'x' && isxdigit (to_uchar (b[1])))
945 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
946 /* A hexadecimal \xhh escape sequence must have
947 1 or 2 hex. digits. */
948 ++b;
949 if (isxdigit (to_uchar (b[1])))
951 ++b;
952 esc_value = esc_value * 16 + hextobin (*b);
954 putchar (esc_value);
956 else if (*b == '\0')
958 error (0, 0, _("warning: backslash at end of format"));
959 putchar ('\\');
960 /* Arrange to exit the loop. */
961 --b;
963 else
965 print_esc_char (*b);
967 break;
969 default:
970 putchar (*b);
971 break;
974 free (dest);
976 fputs (trailing_delim, stdout);
978 return fail;
981 /* Stat the file system and print what we find. */
982 static bool ATTRIBUTE_WARN_UNUSED_RESULT
983 do_statfs (char const *filename, bool terse, char const *format)
985 STRUCT_STATVFS statfsbuf;
987 if (STREQ (filename, "-"))
989 error (0, 0, _("using %s to denote standard input does not work"
990 " in file system mode"), quote (filename));
991 return false;
994 if (STATFS (filename, &statfsbuf) != 0)
996 error (0, errno, _("cannot read file system information for %s"),
997 quote (filename));
998 return false;
1001 if (format == NULL)
1003 format = (terse
1004 ? "%n %i %l %t %s %S %b %f %a %c %d\n"
1005 : " File: \"%n\"\n"
1006 " ID: %-8i Namelen: %-7l Type: %T\n"
1007 "Block size: %-10s Fundamental block size: %S\n"
1008 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
1009 "Inodes: Total: %-10c Free: %d\n");
1012 bool fail = print_it (format, filename, print_statfs, &statfsbuf);
1013 return ! fail;
1016 /* stat the file and print what we find */
1017 static bool ATTRIBUTE_WARN_UNUSED_RESULT
1018 do_stat (char const *filename, bool terse, char const *format)
1020 struct stat statbuf;
1022 if (STREQ (filename, "-"))
1024 if (fstat (STDIN_FILENO, &statbuf) != 0)
1026 error (0, errno, _("cannot stat standard input"));
1027 return false;
1030 /* We can't use the shorter
1031 (follow_links?stat:lstat) (filename, &statbug)
1032 since stat might be a function-like macro. */
1033 else if ((follow_links
1034 ? stat (filename, &statbuf)
1035 : lstat (filename, &statbuf)) != 0)
1037 error (0, errno, _("cannot stat %s"), quote (filename));
1038 return false;
1041 if (format == NULL)
1043 if (terse)
1045 format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
1047 else
1049 /* Temporary hack to match original output until conditional
1050 implemented. */
1051 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
1053 format =
1054 " File: %N\n"
1055 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
1056 "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
1057 " Device type: %t,%T\n"
1058 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
1059 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
1061 else
1063 format =
1064 " File: %N\n"
1065 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
1066 "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
1067 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
1068 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
1072 bool fail = print_it (format, filename, print_stat, &statbuf);
1073 return ! fail;
1076 void
1077 usage (int status)
1079 if (status != EXIT_SUCCESS)
1080 fprintf (stderr, _("Try `%s --help' for more information.\n"),
1081 program_name);
1082 else
1084 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
1085 fputs (_("\
1086 Display file or file system status.\n\
1088 -L, --dereference follow links\n\
1089 -f, --file-system display file system status instead of file status\n\
1090 "), stdout);
1091 fputs (_("\
1092 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
1093 output a newline after each use of FORMAT\n\
1094 --printf=FORMAT like --format, but interpret backslash escapes,\n\
1095 and do not output a mandatory trailing newline.\n\
1096 If you want a newline, include \\n in FORMAT\n\
1097 -t, --terse print the information in terse form\n\
1098 "), stdout);
1099 fputs (HELP_OPTION_DESCRIPTION, stdout);
1100 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1102 fputs (_("\n\
1103 The valid format sequences for files (without --file-system):\n\
1105 %a Access rights in octal\n\
1106 %A Access rights in human readable form\n\
1107 %b Number of blocks allocated (see %B)\n\
1108 %B The size in bytes of each block reported by %b\n\
1109 %C SELinux security context string\n\
1110 "), stdout);
1111 fputs (_("\
1112 %d Device number in decimal\n\
1113 %D Device number in hex\n\
1114 %f Raw mode in hex\n\
1115 %F File type\n\
1116 %g Group ID of owner\n\
1117 %G Group name of owner\n\
1118 "), stdout);
1119 fputs (_("\
1120 %h Number of hard links\n\
1121 %i Inode number\n\
1122 %m Mount point\n\
1123 %n File name\n\
1124 %N Quoted file name with dereference if symbolic link\n\
1125 %o I/O block size\n\
1126 %s Total size, in bytes\n\
1127 %t Major device type in hex\n\
1128 %T Minor device type in hex\n\
1129 "), stdout);
1130 fputs (_("\
1131 %u User ID of owner\n\
1132 %U User name of owner\n\
1133 %x Time of last access\n\
1134 %X Time of last access as seconds since Epoch\n\
1135 %y Time of last modification\n\
1136 %Y Time of last modification as seconds since Epoch\n\
1137 %z Time of last change\n\
1138 %Z Time of last change as seconds since Epoch\n\
1140 "), stdout);
1142 fputs (_("\
1143 Valid format sequences for file systems:\n\
1145 %a Free blocks available to non-superuser\n\
1146 %b Total data blocks in file system\n\
1147 %c Total file nodes in file system\n\
1148 %d Free file nodes in file system\n\
1149 %f Free blocks in file system\n\
1150 %C SELinux security context string\n\
1151 "), stdout);
1152 fputs (_("\
1153 %i File System ID in hex\n\
1154 %l Maximum length of filenames\n\
1155 %n File name\n\
1156 %s Block size (for faster transfers)\n\
1157 %S Fundamental block size (for block counts)\n\
1158 %t Type in hex\n\
1159 %T Type in human readable form\n\
1160 "), stdout);
1161 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
1162 emit_ancillary_info ();
1164 exit (status);
1168 main (int argc, char *argv[])
1170 int c;
1171 int i;
1172 bool fs = false;
1173 bool terse = false;
1174 char *format = NULL;
1175 bool ok = true;
1177 initialize_main (&argc, &argv);
1178 set_program_name (argv[0]);
1179 setlocale (LC_ALL, "");
1180 bindtextdomain (PACKAGE, LOCALEDIR);
1181 textdomain (PACKAGE);
1183 atexit (close_stdout);
1185 while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
1187 switch (c)
1189 case PRINTF_OPTION:
1190 format = optarg;
1191 interpret_backslash_escapes = true;
1192 trailing_delim = "";
1193 break;
1195 case 'c':
1196 format = optarg;
1197 interpret_backslash_escapes = false;
1198 trailing_delim = "\n";
1199 break;
1201 case 'L':
1202 follow_links = true;
1203 break;
1205 case 'f':
1206 fs = true;
1207 break;
1209 case 't':
1210 terse = true;
1211 break;
1213 case_GETOPT_HELP_CHAR;
1215 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1217 default:
1218 usage (EXIT_FAILURE);
1222 if (argc == optind)
1224 error (0, 0, _("missing operand"));
1225 usage (EXIT_FAILURE);
1228 for (i = optind; i < argc; i++)
1229 ok &= (fs
1230 ? do_statfs (argv[i], terse, format)
1231 : do_stat (argv[i], terse, format));
1233 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);