Line data Source code
1 : /* stat.c -- display file or file system status
2 : Copyright (C) 2001-2008 Free Software Foundation, Inc.
3 :
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.
8 :
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.
13 :
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/>.
16 :
17 : Written by Michael Meskes. */
18 :
19 : #include <config.h>
20 :
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
30 :
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_NETINET_IN_H && 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>
57 :
58 : #include "system.h"
59 :
60 : #include "error.h"
61 : #include "filemode.h"
62 : #include "file-type.h"
63 : #include "fs.h"
64 : #include "getopt.h"
65 : #include "inttostr.h"
66 : #include "quote.h"
67 : #include "quotearg.h"
68 : #include "stat-time.h"
69 : #include "strftime.h"
70 : #include "areadlink.h"
71 :
72 : #define alignof(type) offsetof (struct { char c; type x; }, x)
73 :
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
94 : statfs (char const *filename, struct fs_info *buf)
95 : {
96 : dev_t device = dev_for_path (filename);
97 : if (device < 0)
98 : {
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;
106 : }
107 : /* If successful, buf->dev will be == device. */
108 : return fs_stat_dev (device, buf);
109 : }
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
126 :
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
134 :
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
144 :
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')
150 :
151 : #define PROGRAM_NAME "stat"
152 :
153 : #define AUTHORS "Michael Meskes"
154 :
155 : enum
156 : {
157 : PRINTF_OPTION = CHAR_MAX + 1
158 : };
159 :
160 : static struct option const long_options[] = {
161 : {"context", no_argument, 0, 'Z'},
162 : {"dereference", no_argument, NULL, 'L'},
163 : {"file-system", no_argument, NULL, 'f'},
164 :
165 : /* obsolete and undocumented alias: FIXME: remove in 2009 */
166 : {"filesystem", no_argument, NULL, 'f'},
167 :
168 : {"format", required_argument, NULL, 'c'},
169 : {"printf", required_argument, NULL, PRINTF_OPTION},
170 : {"terse", no_argument, NULL, 't'},
171 : {GETOPT_HELP_OPTION_DECL},
172 : {GETOPT_VERSION_OPTION_DECL},
173 : {NULL, 0, NULL, 0}
174 : };
175 :
176 : char *program_name;
177 :
178 : /* Whether to follow symbolic links; True for --dereference (-L). */
179 : static bool follow_links;
180 :
181 : /* Whether to interpret backslash-escape sequences.
182 : True for --printf=FMT, not for --format=FMT (-c). */
183 : static bool interpret_backslash_escapes;
184 :
185 : /* The trailing delimiter string:
186 : "" for --printf=FMT, "\n" for --format=FMT (-c). */
187 : static char const *trailing_delim = "";
188 :
189 : /* Return the type of the specified file system.
190 : Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
191 : Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
192 : Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
193 : Still others have neither and have to get by with f_type (Linux).
194 : But f_type may only exist in statfs (Cygwin). */
195 : static char const *
196 6 : human_fstype (STRUCT_STATVFS const *statfsbuf)
197 : {
198 : #ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
199 : return statfsbuf->STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME;
200 : #else
201 6 : switch (statfsbuf->f_type)
202 : {
203 : # if defined __linux__
204 :
205 : /* Compare with what's in libc:
206 : f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
207 : sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
208 : | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
209 : -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
210 : | sort > sym_libc
211 : perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
212 : -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
213 : | sort > sym_stat
214 : diff -u sym_stat sym_libc
215 : */
216 :
217 : /* Also sync from the list in "man 2 statfs". */
218 :
219 : /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
220 : statements must be followed by a hexadecimal constant in
221 : a comment. The S_MAGIC_... name and constant are automatically
222 : combined to produce the #define directives in fs.h. */
223 :
224 0 : case S_MAGIC_ADFS: /* 0xADF5 */
225 0 : return "adfs";
226 0 : case S_MAGIC_AFFS: /* 0xADFF */
227 0 : return "affs";
228 0 : case S_MAGIC_AUTOFS: /* 0x187 */
229 0 : return "autofs";
230 0 : case S_MAGIC_BEFS: /* 0x42465331 */
231 0 : return "befs";
232 0 : case S_MAGIC_BFS: /* 0x1BADFACE */
233 0 : return "bfs";
234 0 : case S_MAGIC_BINFMT_MISC: /* 0x42494e4d */
235 0 : return "binfmt_misc";
236 0 : case S_MAGIC_CODA: /* 0x73757245 */
237 0 : return "coda";
238 0 : case S_MAGIC_COH: /* 0x012FF7B7 */
239 0 : return "coh";
240 0 : case S_MAGIC_CRAMFS: /* 0x28CD3D45 */
241 0 : return "cramfs";
242 0 : case S_MAGIC_DEVFS: /* 0x1373 */
243 0 : return "devfs";
244 0 : case S_MAGIC_DEVPTS: /* 0x1CD1 */
245 0 : return "devpts";
246 0 : case S_MAGIC_EFS: /* 0x414A53 */
247 0 : return "efs";
248 0 : case S_MAGIC_EXT: /* 0x137D */
249 0 : return "ext";
250 0 : case S_MAGIC_EXT2: /* 0xEF53 */
251 0 : return "ext2/ext3";
252 0 : case S_MAGIC_EXT2_OLD: /* 0xEF51 */
253 0 : return "ext2";
254 0 : case S_MAGIC_FAT: /* 0x4006 */
255 0 : return "fat";
256 0 : case S_MAGIC_FUSECTL: /* 0x65735543 */
257 0 : return "fusectl";
258 0 : case S_MAGIC_HPFS: /* 0xF995E849 */
259 0 : return "hpfs";
260 0 : case S_MAGIC_HUGETLBFS: /* 0x958458f6 */
261 0 : return "hugetlbfs";
262 0 : case S_MAGIC_ISOFS: /* 0x9660 */
263 0 : return "isofs";
264 0 : case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
265 0 : return "isofs";
266 0 : case S_MAGIC_ISOFS_WIN: /* 0x4000 */
267 0 : return "isofs";
268 0 : case S_MAGIC_JFFS2: /* 0x72B6 */
269 0 : return "jffs2";
270 0 : case S_MAGIC_JFFS: /* 0x07C0 */
271 0 : return "jffs";
272 0 : case S_MAGIC_JFS: /* 0x3153464A */
273 0 : return "jfs";
274 0 : case S_MAGIC_MINIX: /* 0x137F */
275 0 : return "minix";
276 0 : case S_MAGIC_MINIX_30: /* 0x138F */
277 0 : return "minix (30 char.)";
278 0 : case S_MAGIC_MINIX_V2: /* 0x2468 */
279 0 : return "minix v2";
280 0 : case S_MAGIC_MINIX_V2_30: /* 0x2478 */
281 0 : return "minix v2 (30 char.)";
282 0 : case S_MAGIC_MSDOS: /* 0x4D44 */
283 0 : return "msdos";
284 0 : case S_MAGIC_NCP: /* 0x564C */
285 0 : return "novell";
286 0 : case S_MAGIC_NFS: /* 0x6969 */
287 0 : return "nfs";
288 0 : case S_MAGIC_NFSD: /* 0x6E667364 */
289 0 : return "nfsd";
290 0 : case S_MAGIC_NTFS: /* 0x5346544E */
291 0 : return "ntfs";
292 0 : case S_MAGIC_OPENPROM: /* 0x9fa1 */
293 0 : return "openprom";
294 0 : case S_MAGIC_PROC: /* 0x9FA0 */
295 0 : return "proc";
296 0 : case S_MAGIC_QNX4: /* 0x002F */
297 0 : return "qnx4";
298 0 : case S_MAGIC_RAMFS: /* 0x858458F6 */
299 0 : return "ramfs";
300 0 : case S_MAGIC_REISERFS: /* 0x52654973 */
301 0 : return "reiserfs";
302 0 : case S_MAGIC_ROMFS: /* 0x7275 */
303 0 : return "romfs";
304 0 : case S_MAGIC_SMB: /* 0x517B */
305 0 : return "smb";
306 0 : case S_MAGIC_SQUASHFS: /* 0x73717368 */
307 0 : return "squashfs";
308 0 : case S_MAGIC_SYSFS: /* 0x62656572 */
309 0 : return "sysfs";
310 0 : case S_MAGIC_SYSV2: /* 0x012FF7B6 */
311 0 : return "sysv2";
312 0 : case S_MAGIC_SYSV4: /* 0x012FF7B5 */
313 0 : return "sysv4";
314 0 : case S_MAGIC_TMPFS: /* 0x1021994 */
315 0 : return "tmpfs";
316 0 : case S_MAGIC_UDF: /* 0x15013346 */
317 0 : return "udf";
318 0 : case S_MAGIC_UFS: /* 0x00011954 */
319 0 : return "ufs";
320 0 : case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 */
321 0 : return "ufs";
322 0 : case S_MAGIC_USBDEVFS: /* 0x9FA2 */
323 0 : return "usbdevfs";
324 0 : case S_MAGIC_VXFS: /* 0xA501FCF5 */
325 0 : return "vxfs";
326 0 : case S_MAGIC_XENIX: /* 0x012FF7B4 */
327 0 : return "xenix";
328 0 : case S_MAGIC_XFS: /* 0x58465342 */
329 0 : return "xfs";
330 0 : case S_MAGIC_XIAFS: /* 0x012FD16D */
331 0 : return "xia";
332 :
333 : # elif __GNU__
334 : case FSTYPE_UFS:
335 : return "ufs";
336 : case FSTYPE_NFS:
337 : return "nfs";
338 : case FSTYPE_GFS:
339 : return "gfs";
340 : case FSTYPE_LFS:
341 : return "lfs";
342 : case FSTYPE_SYSV:
343 : return "sysv";
344 : case FSTYPE_FTP:
345 : return "ftp";
346 : case FSTYPE_TAR:
347 : return "tar";
348 : case FSTYPE_AR:
349 : return "ar";
350 : case FSTYPE_CPIO:
351 : return "cpio";
352 : case FSTYPE_MSLOSS:
353 : return "msloss";
354 : case FSTYPE_CPM:
355 : return "cpm";
356 : case FSTYPE_HFS:
357 : return "hfs";
358 : case FSTYPE_DTFS:
359 : return "dtfs";
360 : case FSTYPE_GRFS:
361 : return "grfs";
362 : case FSTYPE_TERM:
363 : return "term";
364 : case FSTYPE_DEV:
365 : return "dev";
366 : case FSTYPE_PROC:
367 : return "proc";
368 : case FSTYPE_IFSOCK:
369 : return "ifsock";
370 : case FSTYPE_AFS:
371 : return "afs";
372 : case FSTYPE_DFS:
373 : return "dfs";
374 : case FSTYPE_PROC9:
375 : return "proc9";
376 : case FSTYPE_SOCKET:
377 : return "socket";
378 : case FSTYPE_MISC:
379 : return "misc";
380 : case FSTYPE_EXT2FS:
381 : return "ext2/ext3";
382 : case FSTYPE_HTTP:
383 : return "http";
384 : case FSTYPE_MEMFS:
385 : return "memfs";
386 : case FSTYPE_ISO9660:
387 : return "iso9660";
388 : # endif
389 6 : default:
390 : {
391 6 : unsigned long int type = statfsbuf->f_type;
392 : static char buf[sizeof "UNKNOWN (0x%lx)" - 3
393 : + (sizeof type * CHAR_BIT + 3) / 4];
394 6 : sprintf (buf, "UNKNOWN (0x%lx)", type);
395 6 : return buf;
396 : }
397 : }
398 : #endif
399 : }
400 :
401 : static char *
402 36 : human_access (struct stat const *statbuf)
403 : {
404 : static char modebuf[12];
405 36 : filemodestring (statbuf, modebuf);
406 36 : modebuf[10] = 0;
407 36 : return modebuf;
408 : }
409 :
410 : static char *
411 108 : human_time (struct timespec t)
412 : {
413 : static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
414 : (INT_STRLEN_BOUND (int) /* YYYY */
415 : + 1 /* because YYYY might equal INT_MAX + 1900 */
416 : + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
417 108 : struct tm const *tm = localtime (&t.tv_sec);
418 108 : if (tm == NULL)
419 : return (TYPE_SIGNED (time_t)
420 : ? imaxtostr (t.tv_sec, str)
421 5 : : umaxtostr (t.tv_sec, str));
422 103 : nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
423 103 : return str;
424 : }
425 :
426 : static void
427 304 : out_string (char *pformat, size_t prefix_len, char const *arg)
428 : {
429 304 : strcpy (pformat + prefix_len, "s");
430 304 : printf (pformat, arg);
431 304 : }
432 : static void
433 33 : out_int (char *pformat, size_t prefix_len, intmax_t arg)
434 : {
435 33 : strcpy (pformat + prefix_len, PRIdMAX);
436 33 : printf (pformat, arg);
437 33 : }
438 : static void
439 315 : out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
440 : {
441 315 : strcpy (pformat + prefix_len, PRIuMAX);
442 315 : printf (pformat, arg);
443 315 : }
444 : static void
445 36 : out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
446 : {
447 36 : strcpy (pformat + prefix_len, PRIoMAX);
448 36 : printf (pformat, arg);
449 36 : }
450 : static void
451 46 : out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
452 : {
453 46 : strcpy (pformat + prefix_len, PRIxMAX);
454 46 : printf (pformat, arg);
455 46 : }
456 :
457 : /* Very specialized function (modifies FORMAT), just so as to avoid
458 : duplicating this code between both print_statfs and print_stat. */
459 : static void
460 2 : out_file_context (char const *filename, char *pformat, size_t prefix_len)
461 : {
462 : char *scontext;
463 2 : if ((follow_links
464 2 : ? getfilecon (filename, &scontext)
465 2 : : lgetfilecon (filename, &scontext)) < 0)
466 : {
467 2 : error (0, errno, _("failed to get security context of %s"),
468 : quote (filename));
469 2 : scontext = NULL;
470 : }
471 2 : strcpy (pformat + prefix_len, "s");
472 2 : printf (pformat, (scontext ? scontext : "?"));
473 2 : if (scontext)
474 0 : freecon (scontext);
475 2 : }
476 :
477 : /* print statfs info */
478 : static void
479 66 : print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
480 : void const *data)
481 : {
482 66 : STRUCT_STATVFS const *statfsbuf = data;
483 :
484 66 : switch (m)
485 : {
486 6 : case 'n':
487 6 : out_string (pformat, prefix_len, filename);
488 6 : break;
489 :
490 6 : case 'i':
491 : {
492 : #if STRUCT_STATXFS_F_FSID_IS_INTEGER
493 : uintmax_t fsid = statfsbuf->f_fsid;
494 : #else
495 : typedef unsigned int fsid_word;
496 : verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
497 : verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
498 : verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
499 6 : fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
500 :
501 : /* Assume a little-endian word order, as that is compatible
502 : with glibc's statvfs implementation. */
503 6 : uintmax_t fsid = 0;
504 6 : int words = sizeof statfsbuf->f_fsid / sizeof *p;
505 : int i;
506 18 : for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
507 : {
508 12 : uintmax_t u = p[words - 1 - i];
509 12 : fsid |= u << (i * CHAR_BIT * sizeof *p);
510 : }
511 : #endif
512 6 : out_uint_x (pformat, prefix_len, fsid);
513 : }
514 6 : break;
515 :
516 6 : case 'l':
517 6 : OUT_NAMEMAX (pformat, prefix_len, SB_F_NAMEMAX (statfsbuf));
518 6 : break;
519 0 : case 't':
520 : #if HAVE_STRUCT_STATXFS_F_TYPE
521 0 : out_uint_x (pformat, prefix_len, statfsbuf->f_type);
522 : #else
523 : fputc ('?', stdout);
524 : #endif
525 0 : break;
526 6 : case 'T':
527 6 : out_string (pformat, prefix_len, human_fstype (statfsbuf));
528 6 : break;
529 6 : case 'b':
530 6 : out_int (pformat, prefix_len, statfsbuf->f_blocks);
531 6 : break;
532 6 : case 'f':
533 6 : out_int (pformat, prefix_len, statfsbuf->f_bfree);
534 6 : break;
535 6 : case 'a':
536 6 : out_int (pformat, prefix_len, statfsbuf->f_bavail);
537 6 : break;
538 6 : case 's':
539 6 : out_uint (pformat, prefix_len, statfsbuf->f_bsize);
540 6 : break;
541 6 : case 'S':
542 : {
543 6 : uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
544 6 : if (! frsize)
545 6 : frsize = statfsbuf->f_bsize;
546 6 : out_uint (pformat, prefix_len, frsize);
547 : }
548 6 : break;
549 6 : case 'c':
550 6 : out_int (pformat, prefix_len, statfsbuf->f_files);
551 6 : break;
552 6 : case 'd':
553 6 : out_int (pformat, prefix_len, statfsbuf->f_ffree);
554 6 : break;
555 0 : case 'C':
556 0 : out_file_context (filename, pformat, prefix_len);
557 0 : break;
558 0 : default:
559 0 : fputc ('?', stdout);
560 0 : break;
561 : }
562 66 : }
563 :
564 : /* print stat info */
565 : static void
566 669 : print_stat (char *pformat, size_t prefix_len, char m,
567 : char const *filename, void const *data)
568 : {
569 669 : struct stat *statbuf = (struct stat *) data;
570 : struct passwd *pw_ent;
571 : struct group *gw_ent;
572 :
573 669 : switch (m)
574 : {
575 1 : case 'n':
576 1 : out_string (pformat, prefix_len, filename);
577 1 : break;
578 36 : case 'N':
579 36 : out_string (pformat, prefix_len, quote (filename));
580 36 : if (S_ISLNK (statbuf->st_mode))
581 : {
582 3 : char *linkname = areadlink_with_size (filename, statbuf->st_size);
583 3 : if (linkname == NULL)
584 : {
585 0 : error (0, errno, _("cannot read symbolic link %s"),
586 : quote (filename));
587 0 : return;
588 : }
589 3 : printf (" -> ");
590 3 : out_string (pformat, prefix_len, quote (linkname));
591 : }
592 36 : break;
593 36 : case 'd':
594 36 : out_uint (pformat, prefix_len, statbuf->st_dev);
595 36 : break;
596 37 : case 'D':
597 37 : out_uint_x (pformat, prefix_len, statbuf->st_dev);
598 37 : break;
599 37 : case 'i':
600 37 : out_uint (pformat, prefix_len, statbuf->st_ino);
601 37 : break;
602 36 : case 'a':
603 36 : out_uint_o (pformat, prefix_len, statbuf->st_mode & CHMOD_MODE_BITS);
604 36 : break;
605 36 : case 'A':
606 36 : out_string (pformat, prefix_len, human_access (statbuf));
607 36 : break;
608 1 : case 'f':
609 1 : out_uint_x (pformat, prefix_len, statbuf->st_mode);
610 1 : break;
611 36 : case 'F':
612 36 : out_string (pformat, prefix_len, file_type (statbuf));
613 36 : break;
614 37 : case 'h':
615 37 : out_uint (pformat, prefix_len, statbuf->st_nlink);
616 37 : break;
617 37 : case 'u':
618 37 : out_uint (pformat, prefix_len, statbuf->st_uid);
619 37 : break;
620 36 : case 'U':
621 36 : setpwent ();
622 36 : pw_ent = getpwuid (statbuf->st_uid);
623 36 : out_string (pformat, prefix_len,
624 : pw_ent ? pw_ent->pw_name : "UNKNOWN");
625 36 : break;
626 37 : case 'g':
627 37 : out_uint (pformat, prefix_len, statbuf->st_gid);
628 37 : break;
629 36 : case 'G':
630 36 : setgrent ();
631 36 : gw_ent = getgrgid (statbuf->st_gid);
632 36 : out_string (pformat, prefix_len,
633 : gw_ent ? gw_ent->gr_name : "UNKNOWN");
634 36 : break;
635 1 : case 't':
636 1 : out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
637 1 : break;
638 1 : case 'T':
639 1 : out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
640 1 : break;
641 37 : case 's':
642 37 : out_uint (pformat, prefix_len, statbuf->st_size);
643 37 : break;
644 2 : case 'B':
645 2 : out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
646 2 : break;
647 37 : case 'b':
648 37 : out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
649 37 : break;
650 37 : case 'o':
651 37 : out_uint (pformat, prefix_len, statbuf->st_blksize);
652 37 : break;
653 36 : case 'x':
654 36 : out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
655 36 : break;
656 1 : case 'X':
657 : if (TYPE_SIGNED (time_t))
658 1 : out_int (pformat, prefix_len, statbuf->st_atime);
659 : else
660 : out_uint (pformat, prefix_len, statbuf->st_atime);
661 1 : break;
662 36 : case 'y':
663 36 : out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
664 36 : break;
665 1 : case 'Y':
666 : if (TYPE_SIGNED (time_t))
667 1 : out_int (pformat, prefix_len, statbuf->st_mtime);
668 : else
669 : out_uint (pformat, prefix_len, statbuf->st_mtime);
670 1 : break;
671 36 : case 'z':
672 36 : out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
673 36 : break;
674 1 : case 'Z':
675 : if (TYPE_SIGNED (time_t))
676 1 : out_int (pformat, prefix_len, statbuf->st_ctime);
677 : else
678 : out_uint (pformat, prefix_len, statbuf->st_ctime);
679 1 : break;
680 2 : case 'C':
681 2 : out_file_context (filename, pformat, prefix_len);
682 2 : break;
683 2 : default:
684 2 : fputc ('?', stdout);
685 2 : break;
686 : }
687 : }
688 :
689 : /* Output a single-character \ escape. */
690 :
691 : static void
692 8 : print_esc_char (char c)
693 : {
694 8 : switch (c)
695 : {
696 1 : case 'a': /* Alert. */
697 1 : c ='\a';
698 1 : break;
699 1 : case 'b': /* Backspace. */
700 1 : c ='\b';
701 1 : break;
702 1 : case 'f': /* Form feed. */
703 1 : c ='\f';
704 1 : break;
705 1 : case 'n': /* New line. */
706 1 : c ='\n';
707 1 : break;
708 1 : case 'r': /* Carriage return. */
709 1 : c ='\r';
710 1 : break;
711 1 : case 't': /* Horizontal tab. */
712 1 : c ='\t';
713 1 : break;
714 1 : case 'v': /* Vertical tab. */
715 1 : c ='\v';
716 1 : break;
717 0 : case '"':
718 : case '\\':
719 0 : break;
720 1 : default:
721 1 : error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
722 1 : break;
723 : }
724 8 : putchar (c);
725 8 : }
726 :
727 : static void
728 69 : print_it (char const *format, char const *filename,
729 : void (*print_func) (char *, size_t, char, char const *, void const *),
730 : void const *data)
731 : {
732 : /* Add 2 to accommodate our conversion of the stat `%s' format string
733 : to the longer printf `%llu' one. */
734 : enum
735 : {
736 : MAX_ADDITIONAL_BYTES =
737 : (MAX (sizeof PRIdMAX,
738 : MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
739 : - 1)
740 : };
741 69 : size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
742 69 : char *dest = xmalloc (n_alloc);
743 : char const *b;
744 6247 : for (b = format; *b; b++)
745 : {
746 6179 : switch (*b)
747 : {
748 737 : case '%':
749 : {
750 737 : size_t len = strspn (b + 1, "#-+.I 0123456789");
751 737 : char const *fmt_char = b + len + 1;
752 737 : memcpy (dest, b, len + 1);
753 :
754 737 : b = fmt_char;
755 737 : switch (*fmt_char)
756 : {
757 2 : case '\0':
758 2 : --b;
759 : /* fall through */
760 2 : case '%':
761 2 : if (0 < len)
762 : {
763 1 : dest[len + 1] = *fmt_char;
764 1 : dest[len + 2] = '\0';
765 1 : error (EXIT_FAILURE, 0, _("%s: invalid directive"),
766 : quotearg_colon (dest));
767 : }
768 1 : putchar ('%');
769 1 : break;
770 735 : default:
771 735 : print_func (dest, len + 1, *fmt_char, filename, data);
772 735 : break;
773 : }
774 736 : break;
775 : }
776 :
777 20 : case '\\':
778 20 : if ( ! interpret_backslash_escapes)
779 : {
780 9 : putchar ('\\');
781 9 : break;
782 : }
783 11 : ++b;
784 11 : if (isodigit (*b))
785 1 : {
786 1 : int esc_value = octtobin (*b);
787 1 : int esc_length = 1; /* number of octal digits */
788 2 : for (++b; esc_length < 3 && isodigit (*b);
789 0 : ++esc_length, ++b)
790 : {
791 0 : esc_value = esc_value * 8 + octtobin (*b);
792 : }
793 1 : putchar (esc_value);
794 1 : --b;
795 : }
796 10 : else if (*b == 'x' && isxdigit (to_uchar (b[1])))
797 0 : {
798 0 : int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
799 : /* A hexadecimal \xhh escape sequence must have
800 : 1 or 2 hex. digits. */
801 0 : ++b;
802 0 : if (isxdigit (to_uchar (b[1])))
803 : {
804 0 : ++b;
805 0 : esc_value = esc_value * 16 + hextobin (*b);
806 : }
807 0 : putchar (esc_value);
808 : }
809 10 : else if (*b == '\0')
810 : {
811 2 : error (0, 0, _("warning: backslash at end of format"));
812 2 : putchar ('\\');
813 : /* Arrange to exit the loop. */
814 2 : --b;
815 : }
816 : else
817 : {
818 8 : print_esc_char (*b);
819 : }
820 11 : break;
821 :
822 5422 : default:
823 5422 : putchar (*b);
824 5422 : break;
825 : }
826 : }
827 68 : free (dest);
828 :
829 68 : fputs (trailing_delim, stdout);
830 68 : }
831 :
832 : /* Stat the file system and print what we find. */
833 : static bool
834 9 : do_statfs (char const *filename, bool terse, char const *format)
835 : {
836 : STRUCT_STATVFS statfsbuf;
837 :
838 9 : if (STATFS (filename, &statfsbuf) != 0)
839 : {
840 2 : error (0, errno, _("cannot read file system information for %s"),
841 : quote (filename));
842 2 : return false;
843 : }
844 :
845 7 : if (format == NULL)
846 : {
847 6 : format = (terse
848 : ? "%n %i %l %t %s %S %b %f %a %c %d\n"
849 6 : : " File: \"%n\"\n"
850 : " ID: %-8i Namelen: %-7l Type: %T\n"
851 : "Block size: %-10s Fundamental block size: %S\n"
852 : "Blocks: Total: %-10b Free: %-10f Available: %a\n"
853 : "Inodes: Total: %-10c Free: %d\n");
854 : }
855 :
856 7 : print_it (format, filename, print_statfs, &statfsbuf);
857 7 : return true;
858 : }
859 :
860 : /* stat the file and print what we find */
861 : static bool
862 83 : do_stat (char const *filename, bool terse, char const *format)
863 : {
864 : struct stat statbuf;
865 :
866 83 : if ((follow_links ? stat : lstat) (filename, &statbuf) != 0)
867 : {
868 21 : error (0, errno, _("cannot stat %s"), quote (filename));
869 21 : return false;
870 : }
871 :
872 62 : if (format == NULL)
873 : {
874 37 : if (terse)
875 : {
876 1 : format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
877 : }
878 : else
879 : {
880 : /* Temporary hack to match original output until conditional
881 : implemented. */
882 36 : if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
883 : {
884 0 : format =
885 : " File: %N\n"
886 : " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
887 : "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
888 : " Device type: %t,%T\n"
889 : "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
890 : "Access: %x\n" "Modify: %y\n" "Change: %z\n";
891 : }
892 : else
893 : {
894 36 : format =
895 : " File: %N\n"
896 : " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
897 : "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
898 : "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
899 : "Access: %x\n" "Modify: %y\n" "Change: %z\n";
900 : }
901 : }
902 : }
903 62 : print_it (format, filename, print_stat, &statbuf);
904 61 : return true;
905 : }
906 :
907 : void
908 12 : usage (int status)
909 : {
910 12 : if (status != EXIT_SUCCESS)
911 11 : fprintf (stderr, _("Try `%s --help' for more information.\n"),
912 : program_name);
913 : else
914 : {
915 1 : printf (_("Usage: %s [OPTION] FILE...\n"), program_name);
916 1 : fputs (_("\
917 : Display file or file system status.\n\
918 : \n\
919 : -L, --dereference follow links\n\
920 : -f, --file-system display file system status instead of file status\n\
921 : "), stdout);
922 1 : fputs (_("\
923 : -c --format=FORMAT use the specified FORMAT instead of the default;\n\
924 : output a newline after each use of FORMAT\n\
925 : --printf=FORMAT like --format, but interpret backslash escapes,\n\
926 : and do not output a mandatory trailing newline.\n\
927 : If you want a newline, include \\n in FORMAT.\n\
928 : -t, --terse print the information in terse form\n\
929 : "), stdout);
930 1 : fputs (HELP_OPTION_DESCRIPTION, stdout);
931 1 : fputs (VERSION_OPTION_DESCRIPTION, stdout);
932 :
933 1 : fputs (_("\n\
934 : The valid format sequences for files (without --file-system):\n\
935 : \n\
936 : %a Access rights in octal\n\
937 : %A Access rights in human readable form\n\
938 : %b Number of blocks allocated (see %B)\n\
939 : %B The size in bytes of each block reported by %b\n\
940 : %C SELinux security context string\n\
941 : "), stdout);
942 1 : fputs (_("\
943 : %d Device number in decimal\n\
944 : %D Device number in hex\n\
945 : %f Raw mode in hex\n\
946 : %F File type\n\
947 : %g Group ID of owner\n\
948 : %G Group name of owner\n\
949 : "), stdout);
950 1 : fputs (_("\
951 : %h Number of hard links\n\
952 : %i Inode number\n\
953 : %n File name\n\
954 : %N Quoted file name with dereference if symbolic link\n\
955 : %o I/O block size\n\
956 : %s Total size, in bytes\n\
957 : %t Major device type in hex\n\
958 : %T Minor device type in hex\n\
959 : "), stdout);
960 1 : fputs (_("\
961 : %u User ID of owner\n\
962 : %U User name of owner\n\
963 : %x Time of last access\n\
964 : %X Time of last access as seconds since Epoch\n\
965 : %y Time of last modification\n\
966 : %Y Time of last modification as seconds since Epoch\n\
967 : %z Time of last change\n\
968 : %Z Time of last change as seconds since Epoch\n\
969 : \n\
970 : "), stdout);
971 :
972 1 : fputs (_("\
973 : Valid format sequences for file systems:\n\
974 : \n\
975 : %a Free blocks available to non-superuser\n\
976 : %b Total data blocks in file system\n\
977 : %c Total file nodes in file system\n\
978 : %d Free file nodes in file system\n\
979 : %f Free blocks in file system\n\
980 : %C SELinux security context string\n\
981 : "), stdout);
982 1 : fputs (_("\
983 : %i File System ID in hex\n\
984 : %l Maximum length of filenames\n\
985 : %n File name\n\
986 : %s Block size (for faster transfers)\n\
987 : %S Fundamental block size (for block counts)\n\
988 : %t Type in hex\n\
989 : %T Type in human readable form\n\
990 : "), stdout);
991 1 : printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
992 1 : emit_bug_reporting_address ();
993 : }
994 12 : exit (status);
995 : }
996 :
997 : int
998 77 : main (int argc, char *argv[])
999 : {
1000 : int c;
1001 : int i;
1002 77 : bool fs = false;
1003 77 : bool terse = false;
1004 77 : char *format = NULL;
1005 77 : bool ok = true;
1006 :
1007 : initialize_main (&argc, &argv);
1008 77 : program_name = argv[0];
1009 77 : setlocale (LC_ALL, "");
1010 : bindtextdomain (PACKAGE, LOCALEDIR);
1011 : textdomain (PACKAGE);
1012 :
1013 77 : atexit (close_stdout);
1014 :
1015 193 : while ((c = getopt_long (argc, argv, "c:fLtZ", long_options, NULL)) != -1)
1016 : {
1017 50 : switch (c)
1018 : {
1019 12 : case PRINTF_OPTION:
1020 12 : format = optarg;
1021 12 : interpret_backslash_escapes = true;
1022 12 : trailing_delim = "";
1023 12 : break;
1024 :
1025 11 : case 'c':
1026 11 : format = optarg;
1027 11 : interpret_backslash_escapes = false;
1028 11 : trailing_delim = "\n";
1029 11 : break;
1030 :
1031 5 : case 'L':
1032 5 : follow_links = true;
1033 5 : break;
1034 :
1035 9 : case 'f':
1036 9 : fs = true;
1037 9 : break;
1038 :
1039 1 : case 't':
1040 1 : terse = true;
1041 1 : break;
1042 :
1043 1 : case 'Z': /* FIXME: remove in 2010, warn in mid 2008 */
1044 : /* Ignored, for compatibility with distributions
1045 : that implemented this before upstream. */
1046 1 : break;
1047 :
1048 1 : case_GETOPT_HELP_CHAR;
1049 :
1050 1 : case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1051 :
1052 9 : default:
1053 9 : usage (EXIT_FAILURE);
1054 : }
1055 : }
1056 :
1057 66 : if (argc == optind)
1058 : {
1059 2 : error (0, 0, _("missing operand"));
1060 2 : usage (EXIT_FAILURE);
1061 : }
1062 :
1063 155 : for (i = optind; i < argc; i++)
1064 91 : ok &= (fs
1065 9 : ? do_statfs (argv[i], terse, format)
1066 101 : : do_stat (argv[i], terse, format));
1067 :
1068 63 : exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
1069 : }
|