du: trim help text a bit more
[platform/upstream/busybox.git] / coreutils / stat.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * stat -- display file or file system status
4  *
5  * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation.
6  * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org>
7  * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
8  * Copyright (C) 2006 by Yoshinori Sato <ysato@users.sourceforge.jp>
9  *
10  * Written by Michael Meskes
11  * Taken from coreutils and turned into a busybox applet by Mike Frysinger
12  *
13  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
14  */
15
16 //usage:#define stat_trivial_usage
17 //usage:       "[OPTIONS] FILE..."
18 //usage:#define stat_full_usage "\n\n"
19 //usage:       "Display file (default) or filesystem status\n"
20 //usage:        IF_FEATURE_STAT_FORMAT(
21 //usage:     "\n        -c fmt  Use the specified format"
22 //usage:        )
23 //usage:     "\n        -f      Display filesystem status"
24 //usage:     "\n        -L      Follow links"
25 //usage:     "\n        -t      Display info in terse form"
26 //usage:        IF_SELINUX(
27 //usage:     "\n        -Z      Print security context"
28 //usage:        )
29 //usage:        IF_FEATURE_STAT_FORMAT(
30 //usage:       "\n\nValid format sequences for files:\n"
31 //usage:       " %a     Access rights in octal\n"
32 //usage:       " %A     Access rights in human readable form\n"
33 //usage:       " %b     Number of blocks allocated (see %B)\n"
34 //usage:       " %B     The size in bytes of each block reported by %b\n"
35 //usage:       " %d     Device number in decimal\n"
36 //usage:       " %D     Device number in hex\n"
37 //usage:       " %f     Raw mode in hex\n"
38 //usage:       " %F     File type\n"
39 //usage:       " %g     Group ID of owner\n"
40 //usage:       " %G     Group name of owner\n"
41 //usage:       " %h     Number of hard links\n"
42 //usage:       " %i     Inode number\n"
43 //usage:       " %n     File name\n"
44 //usage:       " %N     File name, with -> TARGET if symlink\n"
45 //usage:       " %o     I/O block size\n"
46 //usage:       " %s     Total size, in bytes\n"
47 //usage:       " %t     Major device type in hex\n"
48 //usage:       " %T     Minor device type in hex\n"
49 //usage:       " %u     User ID of owner\n"
50 //usage:       " %U     User name of owner\n"
51 //usage:       " %x     Time of last access\n"
52 //usage:       " %X     Time of last access as seconds since Epoch\n"
53 //usage:       " %y     Time of last modification\n"
54 //usage:       " %Y     Time of last modification as seconds since Epoch\n"
55 //usage:       " %z     Time of last change\n"
56 //usage:       " %Z     Time of last change as seconds since Epoch\n"
57 //usage:       "\nValid format sequences for file systems:\n"
58 //usage:       " %a     Free blocks available to non-superuser\n"
59 //usage:       " %b     Total data blocks in file system\n"
60 //usage:       " %c     Total file nodes in file system\n"
61 //usage:       " %d     Free file nodes in file system\n"
62 //usage:       " %f     Free blocks in file system\n"
63 //usage:        IF_SELINUX(
64 //usage:       " %C     Security context in selinux\n"
65 //usage:        )
66 //usage:       " %i     File System ID in hex\n"
67 //usage:       " %l     Maximum length of filenames\n"
68 //usage:       " %n     File name\n"
69 //usage:       " %s     Block size (for faster transfer)\n"
70 //usage:       " %S     Fundamental block size (for block counts)\n"
71 //usage:       " %t     Type in hex\n"
72 //usage:       " %T     Type in human readable form"
73 //usage:        )
74
75 #include "libbb.h"
76
77 #define OPT_FILESYS     (1 << 0)
78 #define OPT_TERSE       (1 << 1)
79 #define OPT_DEREFERENCE (1 << 2)
80 #define OPT_SELINUX     (1 << 3)
81
82 #if ENABLE_FEATURE_STAT_FORMAT
83 typedef bool (*statfunc_ptr)(const char *, const char *);
84 #else
85 typedef bool (*statfunc_ptr)(const char *);
86 #endif
87
88 static const char *file_type(const struct stat *st)
89 {
90         /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107
91          * for some of these formats.
92          * To keep diagnostics grammatical in English, the
93          * returned string must start with a consonant.
94          */
95         if (S_ISREG(st->st_mode))  return st->st_size == 0 ? "regular empty file" : "regular file";
96         if (S_ISDIR(st->st_mode))  return "directory";
97         if (S_ISBLK(st->st_mode))  return "block special file";
98         if (S_ISCHR(st->st_mode))  return "character special file";
99         if (S_ISFIFO(st->st_mode)) return "fifo";
100         if (S_ISLNK(st->st_mode))  return "symbolic link";
101         if (S_ISSOCK(st->st_mode)) return "socket";
102         if (S_TYPEISMQ(st))        return "message queue";
103         if (S_TYPEISSEM(st))       return "semaphore";
104         if (S_TYPEISSHM(st))       return "shared memory object";
105 #ifdef S_TYPEISTMO
106         if (S_TYPEISTMO(st))       return "typed memory object";
107 #endif
108         return "weird file";
109 }
110
111 static const char *human_time(time_t t)
112 {
113         /* Old
114         static char *str;
115         str = ctime(&t);
116         str[strlen(str)-1] = '\0';
117         return str;
118         */
119         /* coreutils 6.3 compat: */
120
121         /*static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")] ALIGN1;*/
122 #define buf bb_common_bufsiz1
123
124         strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.000000000", localtime(&t));
125         return buf;
126 #undef buf
127 }
128
129 /* Return the type of the specified file system.
130  * Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris)
131  * Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2)
132  * Still others have neither and have to get by with f_type (Linux).
133  */
134 static const char *human_fstype(uint32_t f_type)
135 {
136         static const struct types {
137                 uint32_t type;
138                 const char *const fs;
139         } humantypes[] = {
140                 { 0xADFF,     "affs" },
141                 { 0x1Cd1,     "devpts" },
142                 { 0x137D,     "ext" },
143                 { 0xEF51,     "ext2" },
144                 { 0xEF53,     "ext2/ext3" },
145                 { 0x3153464a, "jfs" },
146                 { 0x58465342, "xfs" },
147                 { 0xF995E849, "hpfs" },
148                 { 0x9660,     "isofs" },
149                 { 0x4000,     "isofs" },
150                 { 0x4004,     "isofs" },
151                 { 0x137F,     "minix" },
152                 { 0x138F,     "minix (30 char.)" },
153                 { 0x2468,     "minix v2" },
154                 { 0x2478,     "minix v2 (30 char.)" },
155                 { 0x4d44,     "msdos" },
156                 { 0x4006,     "fat" },
157                 { 0x564c,     "novell" },
158                 { 0x6969,     "nfs" },
159                 { 0x9fa0,     "proc" },
160                 { 0x517B,     "smb" },
161                 { 0x012FF7B4, "xenix" },
162                 { 0x012FF7B5, "sysv4" },
163                 { 0x012FF7B6, "sysv2" },
164                 { 0x012FF7B7, "coh" },
165                 { 0x00011954, "ufs" },
166                 { 0x012FD16D, "xia" },
167                 { 0x5346544e, "ntfs" },
168                 { 0x1021994,  "tmpfs" },
169                 { 0x52654973, "reiserfs" },
170                 { 0x28cd3d45, "cramfs" },
171                 { 0x7275,     "romfs" },
172                 { 0x858458f6, "romfs" },
173                 { 0x73717368, "squashfs" },
174                 { 0x62656572, "sysfs" },
175                 { 0, "UNKNOWN" }
176         };
177
178         int i;
179
180         for (i = 0; humantypes[i].type; ++i)
181                 if (humantypes[i].type == f_type)
182                         break;
183         return humantypes[i].fs;
184 }
185
186 /* "man statfs" says that statfsbuf->f_fsid is a mess */
187 /* coreutils treats it as an array of ints, most significant first */
188 static unsigned long long get_f_fsid(const struct statfs *statfsbuf)
189 {
190         const unsigned *p = (const void*) &statfsbuf->f_fsid;
191         unsigned sz = sizeof(statfsbuf->f_fsid) / sizeof(unsigned);
192         unsigned long long r = 0;
193
194         do
195                 r = (r << (sizeof(unsigned)*8)) | *p++;
196         while (--sz > 0);
197         return r;
198 }
199
200 #if ENABLE_FEATURE_STAT_FORMAT
201 static void strcatc(char *str, char c)
202 {
203         int len = strlen(str);
204         str[len++] = c;
205         str[len] = '\0';
206 }
207
208 static void printfs(char *pformat, const char *msg)
209 {
210         strcatc(pformat, 's');
211         printf(pformat, msg);
212 }
213
214 /* print statfs info */
215 static void FAST_FUNC print_statfs(char *pformat, const char m,
216                 const char *const filename, const void *data
217                 IF_SELINUX(, security_context_t scontext))
218 {
219         const struct statfs *statfsbuf = data;
220         if (m == 'n') {
221                 printfs(pformat, filename);
222         } else if (m == 'i') {
223                 strcat(pformat, "llx");
224                 printf(pformat, get_f_fsid(statfsbuf));
225         } else if (m == 'l') {
226                 strcat(pformat, "lu");
227                 printf(pformat, (unsigned long) statfsbuf->f_namelen);
228         } else if (m == 't') {
229                 strcat(pformat, "lx");
230                 printf(pformat, (unsigned long) statfsbuf->f_type); /* no equiv */
231         } else if (m == 'T') {
232                 printfs(pformat, human_fstype(statfsbuf->f_type));
233         } else if (m == 'b') {
234                 strcat(pformat, "llu");
235                 printf(pformat, (unsigned long long) statfsbuf->f_blocks);
236         } else if (m == 'f') {
237                 strcat(pformat, "llu");
238                 printf(pformat, (unsigned long long) statfsbuf->f_bfree);
239         } else if (m == 'a') {
240                 strcat(pformat, "llu");
241                 printf(pformat, (unsigned long long) statfsbuf->f_bavail);
242         } else if (m == 's' || m == 'S') {
243                 strcat(pformat, "lu");
244                 printf(pformat, (unsigned long) statfsbuf->f_bsize);
245         } else if (m == 'c') {
246                 strcat(pformat, "llu");
247                 printf(pformat, (unsigned long long) statfsbuf->f_files);
248         } else if (m == 'd') {
249                 strcat(pformat, "llu");
250                 printf(pformat, (unsigned long long) statfsbuf->f_ffree);
251 # if ENABLE_SELINUX
252         } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
253                 printfs(pformat, scontext);
254 # endif
255         } else {
256                 strcatc(pformat, 'c');
257                 printf(pformat, m);
258         }
259 }
260
261 /* print stat info */
262 static void FAST_FUNC print_stat(char *pformat, const char m,
263                 const char *const filename, const void *data
264                 IF_SELINUX(, security_context_t scontext))
265 {
266 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
267         struct stat *statbuf = (struct stat *) data;
268         struct passwd *pw_ent;
269         struct group *gw_ent;
270
271         if (m == 'n') {
272                 printfs(pformat, filename);
273         } else if (m == 'N') {
274                 strcatc(pformat, 's');
275                 if (S_ISLNK(statbuf->st_mode)) {
276                         char *linkname = xmalloc_readlink_or_warn(filename);
277                         if (linkname == NULL)
278                                 return;
279                         printf("'%s' -> '%s'", filename, linkname);
280                         free(linkname);
281                 } else {
282                         printf(pformat, filename);
283                 }
284         } else if (m == 'd') {
285                 strcat(pformat, "llu");
286                 printf(pformat, (unsigned long long) statbuf->st_dev);
287         } else if (m == 'D') {
288                 strcat(pformat, "llx");
289                 printf(pformat, (unsigned long long) statbuf->st_dev);
290         } else if (m == 'i') {
291                 strcat(pformat, "llu");
292                 printf(pformat, (unsigned long long) statbuf->st_ino);
293         } else if (m == 'a') {
294                 strcat(pformat, "lo");
295                 printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)));
296         } else if (m == 'A') {
297                 printfs(pformat, bb_mode_string(statbuf->st_mode));
298         } else if (m == 'f') {
299                 strcat(pformat, "lx");
300                 printf(pformat, (unsigned long) statbuf->st_mode);
301         } else if (m == 'F') {
302                 printfs(pformat, file_type(statbuf));
303         } else if (m == 'h') {
304                 strcat(pformat, "lu");
305                 printf(pformat, (unsigned long) statbuf->st_nlink);
306         } else if (m == 'u') {
307                 strcat(pformat, "lu");
308                 printf(pformat, (unsigned long) statbuf->st_uid);
309         } else if (m == 'U') {
310                 pw_ent = getpwuid(statbuf->st_uid);
311                 printfs(pformat, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN");
312         } else if (m == 'g') {
313                 strcat(pformat, "lu");
314                 printf(pformat, (unsigned long) statbuf->st_gid);
315         } else if (m == 'G') {
316                 gw_ent = getgrgid(statbuf->st_gid);
317                 printfs(pformat, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN");
318         } else if (m == 't') {
319                 strcat(pformat, "lx");
320                 printf(pformat, (unsigned long) major(statbuf->st_rdev));
321         } else if (m == 'T') {
322                 strcat(pformat, "lx");
323                 printf(pformat, (unsigned long) minor(statbuf->st_rdev));
324         } else if (m == 's') {
325                 strcat(pformat, "llu");
326                 printf(pformat, (unsigned long long) statbuf->st_size);
327         } else if (m == 'B') {
328                 strcat(pformat, "lu");
329                 printf(pformat, (unsigned long) 512); //ST_NBLOCKSIZE
330         } else if (m == 'b') {
331                 strcat(pformat, "llu");
332                 printf(pformat, (unsigned long long) statbuf->st_blocks);
333         } else if (m == 'o') {
334                 strcat(pformat, "lu");
335                 printf(pformat, (unsigned long) statbuf->st_blksize);
336         } else if (m == 'x') {
337                 printfs(pformat, human_time(statbuf->st_atime));
338         } else if (m == 'X') {
339                 strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
340                 /* note: (unsigned long) would be wrong:
341                  * imagine (unsigned long64)int32 */
342                 printf(pformat, (long) statbuf->st_atime);
343         } else if (m == 'y') {
344                 printfs(pformat, human_time(statbuf->st_mtime));
345         } else if (m == 'Y') {
346                 strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
347                 printf(pformat, (long) statbuf->st_mtime);
348         } else if (m == 'z') {
349                 printfs(pformat, human_time(statbuf->st_ctime));
350         } else if (m == 'Z') {
351                 strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
352                 printf(pformat, (long) statbuf->st_ctime);
353 # if ENABLE_SELINUX
354         } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
355                 printfs(pformat, scontext);
356 # endif
357         } else {
358                 strcatc(pformat, 'c');
359                 printf(pformat, m);
360         }
361 }
362
363 static void print_it(const char *masterformat,
364                 const char *filename,
365                 void FAST_FUNC (*print_func)(char*, char, const char*, const void* IF_SELINUX(, security_context_t scontext)),
366                 const void *data
367                 IF_SELINUX(, security_context_t scontext))
368 {
369         /* Create a working copy of the format string */
370         char *format = xstrdup(masterformat);
371         /* Add 2 to accomodate our conversion of the stat '%s' format string
372          * to the printf '%llu' one.  */
373         char *dest = xmalloc(strlen(format) + 2 + 1);
374         char *b;
375
376         b = format;
377         while (b) {
378                 /* Each iteration finds next %spec,
379                  * prints preceding string and handles found %spec
380                  */
381                 size_t len;
382                 char *p = strchr(b, '%');
383                 if (!p) {
384                         /* coreutils 6.3 always prints newline at the end */
385                         /*fputs(b, stdout);*/
386                         puts(b);
387                         break;
388                 }
389
390                 /* dest = "%<modifiers>" */
391                 len = 1 + strspn(p + 1, "#-+.I 0123456789");
392                 memcpy(dest, p, len);
393                 dest[len] = '\0';
394
395                 /* print preceding string */
396                 *p = '\0';
397                 fputs(b, stdout);
398
399                 p += len;
400                 b = p + 1;
401                 switch (*p) {
402                 case '\0':
403                         b = NULL;
404                         /* fall through */
405                 case '%':
406                         bb_putchar('%');
407                         break;
408                 default:
409                         /* Completes "%<modifiers>" with specifier and printfs */
410                         print_func(dest, *p, filename, data IF_SELINUX(,scontext));
411                         break;
412                 }
413         }
414
415         free(format);
416         free(dest);
417 }
418 #endif  /* FEATURE_STAT_FORMAT */
419
420 /* Stat the file system and print what we find.  */
421 #if !ENABLE_FEATURE_STAT_FORMAT
422 #define do_statfs(filename, format) do_statfs(filename)
423 #endif
424 static bool do_statfs(const char *filename, const char *format)
425 {
426         struct statfs statfsbuf;
427 #if !ENABLE_FEATURE_STAT_FORMAT
428         const char *format;
429 #endif
430 #if ENABLE_SELINUX
431         security_context_t scontext = NULL;
432
433         if (option_mask32 & OPT_SELINUX) {
434                 if ((option_mask32 & OPT_DEREFERENCE
435                      ? lgetfilecon(filename, &scontext)
436                      : getfilecon(filename, &scontext)
437                     ) < 0
438                 ) {
439                         bb_perror_msg(filename);
440                         return 0;
441                 }
442         }
443 #endif
444         if (statfs(filename, &statfsbuf) != 0) {
445                 bb_perror_msg("can't read file system information for '%s'", filename);
446                 return 0;
447         }
448
449 #if ENABLE_FEATURE_STAT_FORMAT
450         if (format == NULL) {
451 # if !ENABLE_SELINUX
452                 format = (option_mask32 & OPT_TERSE
453                         ? "%n %i %l %t %s %b %f %a %c %d\n"
454                         : "  File: \"%n\"\n"
455                           "    ID: %-8i Namelen: %-7l Type: %T\n"
456                           "Block size: %-10s\n"
457                           "Blocks: Total: %-10b Free: %-10f Available: %a\n"
458                           "Inodes: Total: %-10c Free: %d");
459 # else
460                 format = (option_mask32 & OPT_TERSE
461                         ? (option_mask32 & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n":
462                         "%n %i %l %t %s %b %f %a %c %d\n")
463                         : (option_mask32 & OPT_SELINUX ?
464                         "  File: \"%n\"\n"
465                         "    ID: %-8i Namelen: %-7l Type: %T\n"
466                         "Block size: %-10s\n"
467                         "Blocks: Total: %-10b Free: %-10f Available: %a\n"
468                         "Inodes: Total: %-10c Free: %d"
469                         "  S_context: %C\n":
470                         "  File: \"%n\"\n"
471                         "    ID: %-8i Namelen: %-7l Type: %T\n"
472                         "Block size: %-10s\n"
473                         "Blocks: Total: %-10b Free: %-10f Available: %a\n"
474                         "Inodes: Total: %-10c Free: %d\n")
475                         );
476 # endif /* SELINUX */
477         }
478         print_it(format, filename, print_statfs, &statfsbuf IF_SELINUX(, scontext));
479 #else /* FEATURE_STAT_FORMAT */
480         format = (option_mask32 & OPT_TERSE
481                 ? "%s %llx %lu "
482                 : "  File: \"%s\"\n"
483                   "    ID: %-8llx Namelen: %-7lu ");
484         printf(format,
485                filename,
486                get_f_fsid(&statfsbuf),
487                statfsbuf.f_namelen);
488
489         if (option_mask32 & OPT_TERSE)
490                 printf("%lx ", (unsigned long) statfsbuf.f_type);
491         else
492                 printf("Type: %s\n", human_fstype(statfsbuf.f_type));
493
494 # if !ENABLE_SELINUX
495         format = (option_mask32 & OPT_TERSE
496                 ? "%lu %llu %llu %llu %llu %llu\n"
497                 : "Block size: %-10lu\n"
498                   "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
499                   "Inodes: Total: %-10llu Free: %llu\n");
500         printf(format,
501                (unsigned long) statfsbuf.f_bsize,
502                (unsigned long long) statfsbuf.f_blocks,
503                (unsigned long long) statfsbuf.f_bfree,
504                (unsigned long long) statfsbuf.f_bavail,
505                (unsigned long long) statfsbuf.f_files,
506                (unsigned long long) statfsbuf.f_ffree);
507 # else
508         format = (option_mask32 & OPT_TERSE
509                 ? (option_mask32 & OPT_SELINUX ? "%lu %llu %llu %llu %llu %llu %C\n" : "%lu %llu %llu %llu %llu %llu\n")
510                 : (option_mask32 & OPT_SELINUX
511                         ?       "Block size: %-10lu\n"
512                                 "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
513                                 "Inodes: Total: %-10llu Free: %llu"
514                                 "S_context: %C\n"
515                         :       "Block size: %-10lu\n"
516                                 "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
517                                 "Inodes: Total: %-10llu Free: %llu\n"
518                         )
519                 );
520         printf(format,
521                 (unsigned long) statfsbuf.f_bsize,
522                 (unsigned long long) statfsbuf.f_blocks,
523                 (unsigned long long) statfsbuf.f_bfree,
524                 (unsigned long long) statfsbuf.f_bavail,
525                 (unsigned long long) statfsbuf.f_files,
526                 (unsigned long long) statfsbuf.f_ffree,
527                 scontext);
528
529         if (scontext)
530                 freecon(scontext);
531 # endif
532 #endif  /* FEATURE_STAT_FORMAT */
533         return 1;
534 }
535
536 /* stat the file and print what we find */
537 #if !ENABLE_FEATURE_STAT_FORMAT
538 #define do_stat(filename, format) do_stat(filename)
539 #endif
540 static bool do_stat(const char *filename, const char *format)
541 {
542         struct stat statbuf;
543 #if ENABLE_SELINUX
544         security_context_t scontext = NULL;
545
546         if (option_mask32 & OPT_SELINUX) {
547                 if ((option_mask32 & OPT_DEREFERENCE
548                      ? lgetfilecon(filename, &scontext)
549                      : getfilecon(filename, &scontext)
550                     ) < 0
551                 ) {
552                         bb_perror_msg(filename);
553                         return 0;
554                 }
555         }
556 #endif
557         if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) {
558                 bb_perror_msg("can't stat '%s'", filename);
559                 return 0;
560         }
561
562 #if ENABLE_FEATURE_STAT_FORMAT
563         if (format == NULL) {
564 # if !ENABLE_SELINUX
565                 if (option_mask32 & OPT_TERSE) {
566                         format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
567                 } else {
568                         if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
569                                 format =
570                                         "  File: %N\n"
571                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
572                                         "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
573                                         " Device type: %t,%T\n"
574                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
575                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n";
576                         } else {
577                                 format =
578                                         "  File: %N\n"
579                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
580                                         "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
581                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
582                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n";
583                         }
584                 }
585 # else
586                 if (option_mask32 & OPT_TERSE) {
587                         format = (option_mask32 & OPT_SELINUX ?
588                                   "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n":
589                                   "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n");
590                 } else {
591                         if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
592                                 format = (option_mask32 & OPT_SELINUX ?
593                                           "  File: %N\n"
594                                           "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
595                                           "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
596                                           " Device type: %t,%T\n"
597                                           "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
598                                           "   S_Context: %C\n"
599                                           "Access: %x\n" "Modify: %y\n" "Change: %z\n":
600                                           "  File: %N\n"
601                                           "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
602                                           "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
603                                           " Device type: %t,%T\n"
604                                           "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
605                                           "Access: %x\n" "Modify: %y\n" "Change: %z\n");
606                         } else {
607                                 format = (option_mask32 & OPT_SELINUX ?
608                                           "  File: %N\n"
609                                           "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
610                                           "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
611                                           "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
612                                           "S_Context: %C\n"
613                                           "Access: %x\n" "Modify: %y\n" "Change: %z\n":
614                                           "  File: %N\n"
615                                           "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
616                                           "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
617                                           "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
618                                           "Access: %x\n" "Modify: %y\n" "Change: %z\n");
619                         }
620                 }
621 # endif
622         }
623         print_it(format, filename, print_stat, &statbuf IF_SELINUX(, scontext));
624 #else   /* FEATURE_STAT_FORMAT */
625         if (option_mask32 & OPT_TERSE) {
626                 printf("%s %llu %llu %lx %lu %lu %llx %llu %lu %lx %lx %lu %lu %lu %lu"
627                        IF_NOT_SELINUX("\n"),
628                        filename,
629                        (unsigned long long) statbuf.st_size,
630                        (unsigned long long) statbuf.st_blocks,
631                        (unsigned long) statbuf.st_mode,
632                        (unsigned long) statbuf.st_uid,
633                        (unsigned long) statbuf.st_gid,
634                        (unsigned long long) statbuf.st_dev,
635                        (unsigned long long) statbuf.st_ino,
636                        (unsigned long) statbuf.st_nlink,
637                        (unsigned long) major(statbuf.st_rdev),
638                        (unsigned long) minor(statbuf.st_rdev),
639                        (unsigned long) statbuf.st_atime,
640                        (unsigned long) statbuf.st_mtime,
641                        (unsigned long) statbuf.st_ctime,
642                        (unsigned long) statbuf.st_blksize
643                 );
644 # if ENABLE_SELINUX
645                 if (option_mask32 & OPT_SELINUX)
646                         printf(" %lc\n", *scontext);
647                 else
648                         bb_putchar('\n');
649 # endif
650         } else {
651                 char *linkname = NULL;
652                 struct passwd *pw_ent;
653                 struct group *gw_ent;
654
655                 gw_ent = getgrgid(statbuf.st_gid);
656                 pw_ent = getpwuid(statbuf.st_uid);
657
658                 if (S_ISLNK(statbuf.st_mode))
659                         linkname = xmalloc_readlink_or_warn(filename);
660                 if (linkname) {
661                         printf("  File: '%s' -> '%s'\n", filename, linkname);
662                         free(linkname);
663                 } else {
664                         printf("  File: '%s'\n", filename);
665                 }
666
667                 printf("  Size: %-10llu\tBlocks: %-10llu IO Block: %-6lu %s\n"
668                        "Device: %llxh/%llud\tInode: %-10llu  Links: %-5lu",
669                        (unsigned long long) statbuf.st_size,
670                        (unsigned long long) statbuf.st_blocks,
671                        (unsigned long) statbuf.st_blksize,
672                        file_type(&statbuf),
673                        (unsigned long long) statbuf.st_dev,
674                        (unsigned long long) statbuf.st_dev,
675                        (unsigned long long) statbuf.st_ino,
676                        (unsigned long) statbuf.st_nlink);
677                 if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode))
678                         printf(" Device type: %lx,%lx\n",
679                                (unsigned long) major(statbuf.st_rdev),
680                                (unsigned long) minor(statbuf.st_rdev));
681                 else
682                         bb_putchar('\n');
683                 printf("Access: (%04lo/%10.10s)  Uid: (%5lu/%8s)   Gid: (%5lu/%8s)\n",
684                        (unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)),
685                        bb_mode_string(statbuf.st_mode),
686                        (unsigned long) statbuf.st_uid,
687                        (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN",
688                        (unsigned long) statbuf.st_gid,
689                        (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN");
690 # if ENABLE_SELINUX
691                 printf("   S_Context: %lc\n", *scontext);
692 # endif
693                 printf("Access: %s\n", human_time(statbuf.st_atime));
694                 printf("Modify: %s\n", human_time(statbuf.st_mtime));
695                 printf("Change: %s\n", human_time(statbuf.st_ctime));
696         }
697 #endif  /* FEATURE_STAT_FORMAT */
698         return 1;
699 }
700
701 int stat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
702 int stat_main(int argc UNUSED_PARAM, char **argv)
703 {
704         IF_FEATURE_STAT_FORMAT(char *format = NULL;)
705         int i;
706         int ok;
707         unsigned opts;
708         statfunc_ptr statfunc = do_stat;
709
710         opt_complementary = "-1"; /* min one arg */
711         opts = getopt32(argv, "ftL"
712                 IF_SELINUX("Z")
713                 IF_FEATURE_STAT_FORMAT("c:", &format)
714         );
715         if (opts & OPT_FILESYS) /* -f */
716                 statfunc = do_statfs;
717 #if ENABLE_SELINUX
718         if (opts & OPT_SELINUX) {
719                 selinux_or_die();
720         }
721 #endif
722         ok = 1;
723         argv += optind;
724         for (i = 0; argv[i]; ++i)
725                 ok &= statfunc(argv[i] IF_FEATURE_STAT_FORMAT(, format));
726
727         return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
728 }