1 /* stat.c -- display file or file system status
2 Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation.
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 2, or (at your option)
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, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 Written by Michael Meskes. */
23 && (HAVE_STRUCT_STATVFS_F_BASETYPE || ! HAVE_STRUCT_STATFS_F_FSTYPENAME))
24 # define USE_STATVFS 1
26 # define USE_STATVFS 0
30 #include <sys/types.h>
34 # include <sys/statvfs.h>
37 #elif HAVE_SYS_MOUNT_H && HAVE_SYS_PARAM_H
38 /* NOTE: freebsd5.0 needs sys/param.h and sys/mount.h for statfs.
39 It does have statvfs.h, but shouldn't use it, since it doesn't
40 HAVE_STRUCT_STATVFS_F_BASETYPE. So find a clean way to fix it. */
41 /* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
42 # include <sys/param.h>
43 # include <sys/mount.h>
44 # if HAVE_NETINET_IN_H && HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
45 /* Ultrix 4.4 needs these for the declaration of struct statfs. */
46 # include <netinet/in.h>
47 # include <nfs/nfs_clnt.h>
56 #include "file-type.h"
62 #include "stat-time.h"
64 #include "xreadlink.h"
67 # define STRUCT_STATVFS struct statvfs
68 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
69 # if HAVE_STRUCT_STATVFS_F_NAMEMAX
70 # define SB_F_NAMEMAX(S) ((uintmax_t) ((S)->f_namemax))
72 # define STATFS statvfs
73 # define STATFS_FRSIZE(S) ((S)->f_frsize)
75 # define STRUCT_STATVFS struct statfs
76 # define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
77 # if HAVE_STRUCT_STATFS_F_NAMELEN
78 # define SB_F_NAMEMAX(S) ((uintmax_t) ((S)->f_namelen))
80 # define STATFS statfs
81 # define STATFS_FRSIZE(S) 0
85 # define NAMEMAX_FORMAT PRIuMAX
87 /* NetBSD 1.5.2 has neither f_namemax nor f_namelen. */
88 # define SB_F_NAMEMAX(S) "*"
89 # define NAMEMAX_FORMAT "s"
92 #if HAVE_STRUCT_STATVFS_F_BASETYPE
93 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_basetype
95 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
96 # define STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME f_fstypename
100 /* FIXME: these are used by printf.c, too */
101 #define isodigit(c) ('0' <= (c) && (c) <= '7')
102 #define octtobin(c) ((c) - '0')
103 #define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
104 (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
106 #define PROGRAM_NAME "stat"
108 #define AUTHORS "Michael Meskes"
112 PRINTF_OPTION = CHAR_MAX + 1,
115 static struct option const long_options[] = {
116 {"dereference", no_argument, NULL, 'L'},
117 {"file-system", no_argument, NULL, 'f'},
118 {"filesystem", no_argument, NULL, 'f'}, /* obsolete and undocumented alias */
119 {"format", required_argument, NULL, 'c'},
120 {"printf", required_argument, NULL, PRINTF_OPTION},
121 {"terse", no_argument, NULL, 't'},
122 {GETOPT_HELP_OPTION_DECL},
123 {GETOPT_VERSION_OPTION_DECL},
129 /* Whether to interpret backslash-escape sequences.
130 True for --printf=FMT, not for --format=FMT (-c). */
131 static bool interpret_backslash_escapes;
133 /* The trailing delimiter string:
134 "" for --printf=FMT, "\n" for --format=FMT (-c). */
135 static char const *trailing_delim = "";
137 /* Return the type of the specified file system.
138 Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris)
139 Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2)
140 Still others have neither and have to get by with f_type (Linux). */
142 human_fstype (STRUCT_STATVFS const *statfsbuf)
144 #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
145 return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
147 switch (statfsbuf->f_type)
149 # if defined __linux__
151 /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
152 statements must be followed by a hexadecimal constant in
153 a comment. The S_MAGIC_... name and constant are automatically
154 combined to produce the #define directives in fs.h. */
156 case S_MAGIC_AFFS: /* 0xADFF */
158 case S_MAGIC_DEVPTS: /* 0x1CD1 */
160 case S_MAGIC_EXT: /* 0x137D */
162 case S_MAGIC_EXT2_OLD: /* 0xEF51 */
164 case S_MAGIC_EXT2: /* 0xEF53 */
166 case S_MAGIC_JFS: /* 0x3153464a */
168 case S_MAGIC_XFS: /* 0x58465342 */
170 case S_MAGIC_HPFS: /* 0xF995E849 */
172 case S_MAGIC_ISOFS: /* 0x9660 */
174 case S_MAGIC_ISOFS_WIN: /* 0x4000 */
176 case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
178 case S_MAGIC_MINIX: /* 0x137F */
180 case S_MAGIC_MINIX_30: /* 0x138F */
181 return "minix (30 char.)";
182 case S_MAGIC_MINIX_V2: /* 0x2468 */
184 case S_MAGIC_MINIX_V2_30: /* 0x2478 */
185 return "minix v2 (30 char.)";
186 case S_MAGIC_MSDOS: /* 0x4d44 */
188 case S_MAGIC_FAT: /* 0x4006 */
190 case S_MAGIC_NCP: /* 0x564c */
192 case S_MAGIC_NFS: /* 0x6969 */
194 case S_MAGIC_PROC: /* 0x9fa0 */
196 case S_MAGIC_SMB: /* 0x517B */
198 case S_MAGIC_XENIX: /* 0x012FF7B4 */
200 case S_MAGIC_SYSV4: /* 0x012FF7B5 */
202 case S_MAGIC_SYSV2: /* 0x012FF7B6 */
204 case S_MAGIC_COH: /* 0x012FF7B7 */
206 case S_MAGIC_UFS: /* 0x00011954 */
208 case S_MAGIC_XIAFS: /* 0x012FD16D */
210 case S_MAGIC_NTFS: /* 0x5346544e */
212 case S_MAGIC_TMPFS: /* 0x1021994 */
214 case S_MAGIC_REISERFS: /* 0x52654973 */
216 case S_MAGIC_CRAMFS: /* 0x28cd3d45 */
218 case S_MAGIC_ROMFS: /* 0x7275 */
220 case S_MAGIC_RAMFS: /* 0x858458f6 */
222 case S_MAGIC_SQUASHFS: /* 0x73717368 */
224 case S_MAGIC_SYSFS: /* 0x62656572 */
284 unsigned long int type = statfsbuf->f_type;
285 static char buf[sizeof "UNKNOWN (0x%lx)" - 3
286 + (sizeof type * CHAR_BIT + 3) / 4];
287 sprintf (buf, "UNKNOWN (0x%lx)", type);
295 human_access (struct stat const *statbuf)
297 static char modebuf[12];
298 filemodestring (statbuf, modebuf);
304 human_time (struct timespec t)
306 static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
307 (INT_STRLEN_BOUND (int) /* YYYY */
308 + 1 /* because YYYY might equal INT_MAX + 1900 */
309 + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
310 struct tm const *tm = localtime (&t.tv_sec);
312 return (TYPE_SIGNED (time_t)
313 ? imaxtostr (t.tv_sec, str)
314 : umaxtostr (t.tv_sec, str));
315 nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
319 /* Like strcat, but don't return anything and do check that
320 DEST_BUFSIZE is at least a long as strlen (DEST) + strlen (SRC) + 1.
321 The signature is deliberately different from that of strncat. */
323 xstrcat (char *dest, size_t dest_bufsize, char const *src)
325 size_t dest_len = strlen (dest);
326 size_t src_len = strlen (src);
327 if (dest_bufsize < dest_len + src_len + 1)
329 memcpy (dest + dest_len, src, src_len + 1);
332 /* print statfs info */
334 print_statfs (char *pformat, size_t buf_len, char m, char const *filename,
337 STRUCT_STATVFS const *statfsbuf = data;
342 xstrcat (pformat, buf_len, "s");
343 printf (pformat, filename);
347 #if HAVE_STRUCT_STATXFS_F_FSID___VAL
348 xstrcat (pformat, buf_len, "x %-8x");
349 printf (pformat, statfsbuf->f_fsid.__val[0], /* u_long */
350 statfsbuf->f_fsid.__val[1]);
352 xstrcat (pformat, buf_len, "Lx");
353 printf (pformat, statfsbuf->f_fsid);
358 xstrcat (pformat, buf_len, NAMEMAX_FORMAT);
359 printf (pformat, SB_F_NAMEMAX (statfsbuf));
362 #if HAVE_STRUCT_STATXFS_F_TYPE
363 xstrcat (pformat, buf_len, "lx");
365 (unsigned long int) (statfsbuf->f_type)); /* no equiv. */
371 xstrcat (pformat, buf_len, "s");
372 printf (pformat, human_fstype (statfsbuf));
375 xstrcat (pformat, buf_len, PRIdMAX);
376 printf (pformat, (intmax_t) (statfsbuf->f_blocks));
379 xstrcat (pformat, buf_len, PRIdMAX);
380 printf (pformat, (intmax_t) (statfsbuf->f_bfree));
383 xstrcat (pformat, buf_len, PRIdMAX);
384 printf (pformat, (intmax_t) (statfsbuf->f_bavail));
387 xstrcat (pformat, buf_len, "lu");
388 printf (pformat, (unsigned long int) (statfsbuf->f_bsize));
392 unsigned long int frsize = STATFS_FRSIZE (statfsbuf);
394 frsize = statfsbuf->f_bsize;
395 xstrcat (pformat, buf_len, "lu");
396 printf (pformat, frsize);
400 xstrcat (pformat, buf_len, PRIdMAX);
401 printf (pformat, (intmax_t) (statfsbuf->f_files));
404 xstrcat (pformat, buf_len, PRIdMAX);
405 printf (pformat, (intmax_t) (statfsbuf->f_ffree));
409 xstrcat (pformat, buf_len, "c");
415 /* print stat info */
417 print_stat (char *pformat, size_t buf_len, char m,
418 char const *filename, void const *data)
420 struct stat *statbuf = (struct stat *) data;
421 struct passwd *pw_ent;
422 struct group *gw_ent;
427 xstrcat (pformat, buf_len, "s");
428 printf (pformat, filename);
431 xstrcat (pformat, buf_len, "s");
432 if (S_ISLNK (statbuf->st_mode))
434 char *linkname = xreadlink (filename, statbuf->st_size);
435 if (linkname == NULL)
437 error (0, errno, _("cannot read symbolic link %s"),
441 /*printf("\"%s\" -> \"%s\"", filename, linkname); */
442 printf (pformat, quote (filename));
444 printf (pformat, quote (linkname));
448 printf (pformat, quote (filename));
452 xstrcat (pformat, buf_len, PRIuMAX);
453 printf (pformat, (uintmax_t) statbuf->st_dev);
456 xstrcat (pformat, buf_len, PRIxMAX);
457 printf (pformat, (uintmax_t) statbuf->st_dev);
460 xstrcat (pformat, buf_len, PRIuMAX);
461 printf (pformat, (uintmax_t) statbuf->st_ino);
464 xstrcat (pformat, buf_len, "lo");
466 (unsigned long int) (statbuf->st_mode & CHMOD_MODE_BITS));
469 xstrcat (pformat, buf_len, "s");
470 printf (pformat, human_access (statbuf));
473 xstrcat (pformat, buf_len, "lx");
474 printf (pformat, (unsigned long int) statbuf->st_mode);
477 xstrcat (pformat, buf_len, "s");
478 printf (pformat, file_type (statbuf));
481 xstrcat (pformat, buf_len, "lu");
482 printf (pformat, (unsigned long int) statbuf->st_nlink);
485 xstrcat (pformat, buf_len, "lu");
486 printf (pformat, (unsigned long int) statbuf->st_uid);
489 xstrcat (pformat, buf_len, "s");
491 pw_ent = getpwuid (statbuf->st_uid);
492 printf (pformat, (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN");
495 xstrcat (pformat, buf_len, "lu");
496 printf (pformat, (unsigned long int) statbuf->st_gid);
499 xstrcat (pformat, buf_len, "s");
501 gw_ent = getgrgid (statbuf->st_gid);
502 printf (pformat, (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN");
505 xstrcat (pformat, buf_len, "lx");
506 printf (pformat, (unsigned long int) major (statbuf->st_rdev));
509 xstrcat (pformat, buf_len, "lx");
510 printf (pformat, (unsigned long int) minor (statbuf->st_rdev));
513 xstrcat (pformat, buf_len, PRIuMAX);
514 printf (pformat, (uintmax_t) (statbuf->st_size));
517 xstrcat (pformat, buf_len, "lu");
518 printf (pformat, (unsigned long int) ST_NBLOCKSIZE);
521 xstrcat (pformat, buf_len, PRIuMAX);
522 printf (pformat, (uintmax_t) ST_NBLOCKS (*statbuf));
525 xstrcat (pformat, buf_len, "lu");
526 printf (pformat, (unsigned long int) statbuf->st_blksize);
529 xstrcat (pformat, buf_len, "s");
530 printf (pformat, human_time (get_stat_atime (statbuf)));
533 xstrcat (pformat, buf_len, TYPE_SIGNED (time_t) ? "ld" : "lu");
534 printf (pformat, (unsigned long int) statbuf->st_atime);
537 xstrcat (pformat, buf_len, "s");
538 printf (pformat, human_time (get_stat_mtime (statbuf)));
541 xstrcat (pformat, buf_len, TYPE_SIGNED (time_t) ? "ld" : "lu");
542 printf (pformat, (unsigned long int) statbuf->st_mtime);
545 xstrcat (pformat, buf_len, "s");
546 printf (pformat, human_time (get_stat_ctime (statbuf)));
549 xstrcat (pformat, buf_len, TYPE_SIGNED (time_t) ? "ld" : "lu");
550 printf (pformat, (unsigned long int) statbuf->st_ctime);
553 xstrcat (pformat, buf_len, "c");
559 /* Output a single-character \ escape. */
562 print_esc_char (char c)
566 case 'a': /* Alert. */
569 case 'b': /* Backspace. */
572 case 'f': /* Form feed. */
575 case 'n': /* New line. */
578 case 'r': /* Carriage return. */
581 case 't': /* Horizontal tab. */
584 case 'v': /* Vertical tab. */
591 error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
598 print_it (char const *format, char const *filename,
599 void (*print_func) (char *, size_t, char, char const *, void const *),
602 /* Add 2 to accommodate our conversion of the stat `%s' format string
603 to the longer printf `%llu' one. */
604 size_t n_alloc = strlen (format) + 2 + 1;
605 char *dest = xmalloc (n_alloc);
607 for (b = format; *b; b++)
613 size_t len = strspn (b + 1, "#-+.I 0123456789");
614 char const *fmt_char = b + 1 + len;
615 memcpy (dest, b, 1 + len);
626 error (EXIT_FAILURE, 0, _("%s%s: invalid directive"),
627 quotearg_colon (dest), *fmt_char ? "%" : "");
631 print_func (dest, n_alloc, *fmt_char, filename, data);
638 if ( ! interpret_backslash_escapes)
646 int esc_value = octtobin (*b);
647 int esc_length = 1; /* number of octal digits */
648 for (++b; esc_length < 3 && isodigit (*b);
651 esc_value = esc_value * 8 + octtobin (*b);
656 else if (*b == 'x' && ISXDIGIT (b[1]))
658 int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
659 /* A hexadecimal \xhh escape sequence must have
660 1 or 2 hex. digits. */
665 esc_value = esc_value * 16 + hextobin (*b);
671 error (0, 0, _("warning: backslash at end of format"));
673 /* Arrange to exit the loop. */
689 fputs (trailing_delim, stdout);
692 /* Stat the file system and print what we find. */
694 do_statfs (char const *filename, bool terse, char const *format)
696 STRUCT_STATVFS statfsbuf;
698 if (STATFS (filename, &statfsbuf) != 0)
700 error (0, errno, _("cannot read file system information for %s"),
708 ? "%n %i %l %t %s %S %b %f %a %c %d\n"
710 " ID: %-8i Namelen: %-7l Type: %T\n"
711 "Block size: %-10s Fundamental block size: %S\n"
712 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
713 "Inodes: Total: %-10c Free: %d\n");
716 print_it (format, filename, print_statfs, &statfsbuf);
720 /* stat the file and print what we find */
722 do_stat (char const *filename, bool follow_links, bool terse,
727 if ((follow_links ? stat : lstat) (filename, &statbuf) != 0)
729 error (0, errno, _("cannot stat %s"), quote (filename));
737 format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
741 /* Temporary hack to match original output until conditional
743 if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
747 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
748 "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
749 " Device type: %t,%T\n"
750 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
751 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
757 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
758 "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
759 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
760 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
764 print_it (format, filename, print_stat, &statbuf);
771 if (status != EXIT_SUCCESS)
772 fprintf (stderr, _("Try `%s --help' for more information.\n"),
776 printf (_("Usage: %s [OPTION] FILE...\n"), program_name);
778 Display file or file system status.\n\
780 -L, --dereference follow links\n\
781 -f, --file-system display file system status instead of file status\n\
784 -c --format=FORMAT use the specified FORMAT instead of the default;\n\
785 output a newline after each use of FORMAT\n\
786 --printf=FORMAT like --format, but interpret backslash escapes,\n\
787 and do not output a mandatory trailing newline.\n\
788 If you want a newline, include \\n in FORMAT.\n\
789 -t, --terse print the information in terse form\n\
791 fputs (HELP_OPTION_DESCRIPTION, stdout);
792 fputs (VERSION_OPTION_DESCRIPTION, stdout);
795 The valid format sequences for files (without --file-system):\n\
797 %a Access rights in octal\n\
798 %A Access rights in human readable form\n\
799 %b Number of blocks allocated (see %B)\n\
800 %B The size in bytes of each block reported by %b\n\
803 %d Device number in decimal\n\
804 %D Device number in hex\n\
805 %f Raw mode in hex\n\
807 %g Group ID of owner\n\
808 %G Group name of owner\n\
811 %h Number of hard links\n\
814 %N Quoted file name with dereference if symbolic link\n\
816 %s Total size, in bytes\n\
817 %t Major device type in hex\n\
818 %T Minor device type in hex\n\
821 %u User ID of owner\n\
822 %U User name of owner\n\
823 %x Time of last access\n\
824 %X Time of last access as seconds since Epoch\n\
825 %y Time of last modification\n\
826 %Y Time of last modification as seconds since Epoch\n\
827 %z Time of last change\n\
828 %Z Time of last change as seconds since Epoch\n\
833 Valid format sequences for file systems:\n\
835 %a Free blocks available to non-superuser\n\
836 %b Total data blocks in file system\n\
837 %c Total file nodes in file system\n\
838 %d Free file nodes in file system\n\
839 %f Free blocks in file system\n\
842 %i File System ID in hex\n\
843 %l Maximum length of filenames\n\
845 %s Block size (for faster transfers)\n\
846 %S Fundamental block size (for block counts)\n\
848 %T Type in human readable form\n\
850 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
851 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
857 main (int argc, char *argv[])
861 bool follow_links = false;
867 initialize_main (&argc, &argv);
868 program_name = argv[0];
869 setlocale (LC_ALL, "");
870 bindtextdomain (PACKAGE, LOCALEDIR);
871 textdomain (PACKAGE);
873 atexit (close_stdout);
875 while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
881 interpret_backslash_escapes = true;
887 interpret_backslash_escapes = false;
888 trailing_delim = "\n";
903 case_GETOPT_HELP_CHAR;
905 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
908 usage (EXIT_FAILURE);
914 error (0, 0, _("missing operand"));
915 usage (EXIT_FAILURE);
918 for (i = optind; i < argc; i++)
920 ? do_statfs (argv[i], terse, format)
921 : do_stat (argv[i], follow_links, terse, format));
923 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);