33f01deb06dc0d4d3f9497230c35d7bec1cfef00
[oweals/busybox.git] / stat.c
1 /*
2  * stat -- display file or file system status
3  *
4  * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation.
5  * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org>
6  * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
7  *
8  * Written by Michael Meskes
9  * Taken from coreutils and turned into a busybox applet by Mike Frysinger
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24  *
25  */
26
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <pwd.h>
30 #include <grp.h>
31 #include <sys/vfs.h>
32 #include <time.h>
33 #include <getopt.h>
34 #include <sys/stat.h>
35 #include <string.h>
36 #include "busybox.h"
37
38 /* vars to control behavior */
39 #define OPT_TERSE 2
40 #define OPT_DEREFERNCE 4
41 static long flags;
42
43 static char const *file_type(struct stat const *st)
44 {
45         /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107 
46          * for some of these formats.
47          * To keep diagnostics grammatical in English, the 
48          * returned string must start with a consonant.
49          */
50         if (S_ISREG(st->st_mode))  return st->st_size == 0 ? "regular empty file" : "regular file";
51         if (S_ISDIR(st->st_mode))  return "directory";
52         if (S_ISBLK(st->st_mode))  return "block special file";
53         if (S_ISCHR(st->st_mode))  return "character special file";
54         if (S_ISFIFO(st->st_mode)) return "fifo";
55         if (S_ISLNK(st->st_mode))  return "symbolic link";
56         if (S_ISSOCK(st->st_mode)) return "socket";
57         if (S_TYPEISMQ(st))        return "message queue";
58         if (S_TYPEISSEM(st))       return "semaphore";
59         if (S_TYPEISSHM(st))       return "shared memory object";
60 #ifdef S_TYPEISTMO
61         if (S_TYPEISTMO(st))       return "typed memory object";
62 #endif
63         return "weird file";
64 }
65
66 static char const *human_time(time_t t)
67 {
68         static char *str;
69         str = ctime(&t);
70         str[strlen(str)-1] = '\0';
71         return str;
72 }
73
74 /* Return the type of the specified file system.
75  * Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris)
76  * Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2)
77  * Still others have neither and have to get by with f_type (Linux).
78  */
79 static char const *human_fstype(long f_type)
80 {
81         int i;
82         static struct types {
83                 long type;
84                 char *fs;
85         } humantypes[] = {
86                 { 0xADFF,     "affs" },
87                 { 0x1Cd1,     "devpts" },
88                 { 0x137D,     "ext" },
89                 { 0xEF51,     "ext2" },
90                 { 0xEF53,     "ext2/ext3" },
91                 { 0x3153464a, "jfs" },
92                 { 0x58465342, "xfs" },
93                 { 0xF995E849, "hpfs" },
94                 { 0x9660,     "isofs" },
95                 { 0x4000,     "isofs" },
96                 { 0x4004,     "isofs" },
97                 { 0x137F,     "minix" },
98                 { 0x138F,     "minix (30 char.)" },
99                 { 0x2468,     "minix v2" },
100                 { 0x2478,     "minix v2 (30 char.)" },
101                 { 0x4d44,     "msdos" },
102                 { 0x4006,     "fat" },
103                 { 0x564c,     "novell" },
104                 { 0x6969,     "nfs" },
105                 { 0x9fa0,     "proc" },
106                 { 0x517B,     "smb" },
107                 { 0x012FF7B4, "xenix" },
108                 { 0x012FF7B5, "sysv4" },
109                 { 0x012FF7B6, "sysv2" },
110                 { 0x012FF7B7, "coh" },
111                 { 0x00011954, "ufs" },
112                 { 0x012FD16D, "xia" },
113                 { 0x5346544e, "ntfs" },
114                 { 0x1021994,  "tmpfs" },
115                 { 0x52654973, "reiserfs" },
116                 { 0x28cd3d45, "cramfs" },
117                 { 0x7275,     "romfs" },
118                 { 0x858458f6, "romfs" },
119                 { 0x73717368, "squashfs" },
120                 { 0x62656572, "sysfs" },
121                 { 0, "UNKNOWN" },
122                 { 0, NULL }
123         };
124         for (i=0; humantypes[i].type; ++i)
125                 if (humantypes[i].type == f_type)
126                         return humantypes[i].fs;
127         return humantypes[i].fs;
128 }
129
130 #ifdef CONFIG_FEATURE_STAT_FORMAT
131 /* print statfs info */
132 static void print_statfs(char *pformat, size_t buf_len, char m, 
133                          char const *filename, void const *data)
134 {
135         struct statfs const *statfsbuf = data;
136
137         switch (m) {
138         case 'n':
139                 strncat(pformat, "s", buf_len);
140                 printf(pformat, filename);
141                 break;
142         case 'i':
143                 strncat(pformat, "Lx", buf_len);
144                 printf(pformat, statfsbuf->f_fsid);
145                 break;
146         case 'l':
147                 strncat(pformat, "lu", buf_len);
148                 printf(pformat, statfsbuf->f_namelen);
149                 break;
150         case 't':
151                 strncat(pformat, "lx", buf_len);
152                 printf(pformat, (unsigned long int) (statfsbuf->f_type));  /* no equiv. */
153                 break;
154         case 'T':
155                 strncat(pformat, "s", buf_len);
156                 printf(pformat, human_fstype(statfsbuf->f_type));
157                 break;
158         case 'b':
159                 strncat(pformat, "ld", buf_len);
160                 printf(pformat, (intmax_t) (statfsbuf->f_blocks));
161                 break;
162         case 'f':
163                 strncat(pformat, "ld", buf_len);
164                 printf(pformat, (intmax_t) (statfsbuf->f_bfree));
165                 break;
166         case 'a':
167                 strncat(pformat, "ld", buf_len);
168                 printf(pformat, (intmax_t) (statfsbuf->f_bavail));
169                 break;
170         case 's':
171                 strncat(pformat, "lu", buf_len);
172                 printf(pformat, (unsigned long int) (statfsbuf->f_bsize));
173                 break;
174         case 'S': {
175                 unsigned long int frsize = statfsbuf->f_frsize;
176                 if (!frsize)
177                         frsize = statfsbuf->f_bsize;
178                 strncat(pformat, "lu", buf_len);
179                 printf(pformat, frsize);
180                 break;
181         }
182         case 'c':
183                 strncat(pformat, "ld", buf_len);
184                 printf(pformat, (intmax_t) (statfsbuf->f_files));
185                 break;
186         case 'd':
187                 strncat(pformat, "ld", buf_len);
188                 printf(pformat, (intmax_t) (statfsbuf->f_ffree));
189                 break;
190         default:
191                 strncat(pformat, "c", buf_len);
192                 printf(pformat, m);
193                 break;
194         }
195 }
196
197 /* print stat info */
198 static void print_stat(char *pformat, size_t buf_len, char m, 
199                        char const *filename, void const *data)
200 {
201 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
202         struct stat *statbuf = (struct stat *) data;
203         struct passwd *pw_ent;
204         struct group *gw_ent;
205
206         switch (m) {
207         case 'n':
208                 strncat(pformat, "s", buf_len);
209                 printf(pformat, filename);
210                 break;
211         case 'N':
212                 strncat(pformat, "s", buf_len);
213                 if (S_ISLNK(statbuf->st_mode)) {
214                         char *linkname = xreadlink(filename);
215                         if (linkname == NULL) {
216                                 bb_perror_msg("cannot read symbolic link '%s'", filename);
217                                 return;
218                         }
219                         /*printf("\"%s\" -> \"%s\"", filename, linkname); */
220                         printf(pformat, filename);
221                         printf(" -> ");
222                         printf(pformat, linkname);
223                 } else {
224                         printf(pformat, filename);
225                 }
226                 break;
227         case 'd':
228                 strncat(pformat, "lu", buf_len);
229                 printf(pformat, (uintmax_t) statbuf->st_dev);
230                 break;
231         case 'D':
232                 strncat(pformat, "lx", buf_len);
233                 printf(pformat, (uintmax_t) statbuf->st_dev);
234                 break;
235         case 'i':
236                 strncat(pformat, "lu", buf_len);
237                 printf(pformat, (uintmax_t) statbuf->st_ino);
238                 break;
239         case 'a':
240                 strncat(pformat, "lo", buf_len);
241                 printf(pformat, (unsigned long int) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)));
242                 break;
243         case 'A':
244                 strncat(pformat, "s", buf_len);
245                 printf(pformat, bb_mode_string(statbuf->st_mode));
246                 break;
247         case 'f':
248                 strncat(pformat, "lx", buf_len);
249                 printf(pformat, (unsigned long int) statbuf->st_mode);
250                 break;
251         case 'F':
252                 strncat(pformat, "s", buf_len);
253                 printf(pformat, file_type(statbuf));
254                 break;
255         case 'h':
256                 strncat(pformat, "lu", buf_len);
257                 printf(pformat, (unsigned long int) statbuf->st_nlink);
258                 break;
259         case 'u':
260                 strncat(pformat, "lu", buf_len);
261                 printf(pformat, (unsigned long int) statbuf->st_uid);
262                 break;
263         case 'U':
264                 strncat(pformat, "s", buf_len);
265                 setpwent();
266                 pw_ent = getpwuid(statbuf->st_uid);
267                 printf(pformat, (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN");
268                 break;
269         case 'g':
270                 strncat(pformat, "lu", buf_len);
271                 printf(pformat, (unsigned long int) statbuf->st_gid);
272                 break;
273         case 'G':
274                 strncat(pformat, "s", buf_len);
275                 setgrent();
276                 gw_ent = getgrgid(statbuf->st_gid);
277                 printf(pformat, (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN");
278                 break;
279         case 't':
280                 strncat(pformat, "lx", buf_len);
281                 printf(pformat, (unsigned long int) major(statbuf->st_rdev));
282                 break;
283         case 'T':
284                 strncat(pformat, "lx", buf_len);
285                 printf(pformat, (unsigned long int) minor(statbuf->st_rdev));
286                 break;
287         case 's':
288                 strncat(pformat, "lu", buf_len);
289                 printf(pformat, (uintmax_t) (statbuf->st_size));
290                 break;
291         case 'B':
292                 strncat(pformat, "lu", buf_len);
293                 printf(pformat, (unsigned long int) 512); //ST_NBLOCKSIZE
294                 break;
295         case 'b':
296                 strncat(pformat, "lu", buf_len);
297                 printf(pformat, (uintmax_t) statbuf->st_blocks);
298                 break;
299         case 'o':
300                 strncat(pformat, "lu", buf_len);
301                 printf(pformat, (unsigned long int) statbuf->st_blksize);
302                 break;
303         case 'x':
304                 strncat(pformat, "s", buf_len);
305                 printf(pformat, human_time(statbuf->st_atime));
306                 break;
307         case 'X':
308                 strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
309                 printf(pformat, (unsigned long int) statbuf->st_atime);
310                 break;
311         case 'y':
312                 strncat(pformat, "s", buf_len);
313                 printf(pformat, human_time(statbuf->st_mtime));
314                 break;
315         case 'Y':
316                 strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
317                 printf(pformat, (unsigned long int) statbuf->st_mtime);
318                 break;
319         case 'z':
320                 strncat(pformat, "s", buf_len);
321                 printf(pformat, human_time(statbuf->st_ctime));
322                 break;
323         case 'Z':
324                 strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
325                 printf(pformat, (unsigned long int) statbuf->st_ctime);
326                 break;
327         default:
328                 strncat(pformat, "c", buf_len);
329                 printf(pformat, m);
330                 break;
331         }
332 }
333
334 static void print_it(char const *masterformat, char const *filename, 
335                      void (*print_func) (char *, size_t, char, char const *, void const *), 
336                      void const *data)
337 {
338         char *b;
339
340         /* create a working copy of the format string */
341         char *format = bb_xstrdup(masterformat);
342
343         /* Add 2 to accommodate our conversion of the stat `%s' format string
344          * to the printf `%llu' one.  */
345         size_t n_alloc = strlen(format) + 2 + 1;
346         char *dest = xmalloc(n_alloc);
347
348         b = format;
349         while (b) {
350                 char *p = strchr(b, '%');
351                 if (p != NULL) {
352                         size_t len;
353                         *p++ = '\0';
354                         fputs(b, stdout);
355
356                         len = strspn(p, "#-+.I 0123456789");
357                         dest[0] = '%';
358                         memcpy(dest + 1, p, len);
359                         dest[1 + len] = 0;
360                         p += len;
361
362                         b = p + 1;
363                         switch (*p) {
364                                 case '\0':
365                                         b = NULL;
366                                         /* fall through */
367                                 case '%':
368                                         putchar('%');
369                                         break;
370                                 default:
371                                         print_func(dest, n_alloc, *p, filename, data);
372                                         break;
373                         }
374
375                 } else {
376                         fputs(b, stdout);
377                         b = NULL;
378                 }
379         }
380
381         free(format);
382         free(dest);
383 }
384 #endif
385
386 /* Stat the file system and print what we find.  */
387 static int do_statfs(char const *filename, char const *format)
388 {
389         struct statfs statfsbuf;
390
391         if (statfs(filename, &statfsbuf) != 0) {
392                 bb_perror_msg("cannot read file system information for '%s'", filename);
393                 return 0;
394         }
395
396 #ifdef CONFIG_FEATURE_STAT_FORMAT
397         if (format == NULL)
398                 format = (flags & OPT_TERSE
399                         ? "%n %i %l %t %s %S %b %f %a %c %d\n"
400                         : "  File: \"%n\"\n"
401                           "    ID: %-8i Namelen: %-7l Type: %T\n"
402                           "Block size: %-10s Fundamental block size: %S\n"
403                           "Blocks: Total: %-10b Free: %-10f Available: %a\n"
404                           "Inodes: Total: %-10c Free: %d\n");
405         print_it(format, filename, print_statfs, &statfsbuf);
406 #else
407
408         format = (flags & OPT_TERSE
409                 ? "%s %Lx %lu "
410                 : "  File: \"%s\"\n"
411                   "    ID: %-8Lx Namelen: %-7lu ");
412         printf(format,
413                filename,
414                statfsbuf.f_fsid,
415                statfsbuf.f_namelen);
416
417         if (flags & OPT_TERSE)
418                 printf("%lx ", (unsigned long int) (statfsbuf.f_type));
419         else
420                 printf("Type: %s\n", human_fstype(statfsbuf.f_type));
421
422         format = (flags & OPT_TERSE
423                 ? "%lu %lu %ld %ld %ld %ld %ld\n"
424                 : "Block size: %-10lu Fundamental block size: %lu\n"
425                   "Blocks: Total: %-10ld Free: %-10ld Available: %ld\n"
426                   "Inodes: Total: %-10ld Free: %ld\n");
427         printf(format,
428                (unsigned long int) (statfsbuf.f_bsize),
429                statfsbuf.f_frsize ? statfsbuf.f_frsize : statfsbuf.f_bsize,
430                (intmax_t) (statfsbuf.f_blocks),
431                (intmax_t) (statfsbuf.f_bfree),
432                (intmax_t) (statfsbuf.f_bavail),
433                (intmax_t) (statfsbuf.f_files),
434                (intmax_t) (statfsbuf.f_ffree));
435 #endif
436
437         return 1;
438 }
439
440 /* stat the file and print what we find */
441 static int do_stat(char const *filename, char const *format)
442 {
443         struct stat statbuf;
444
445         if ((flags & OPT_DEREFERNCE ? stat : lstat) (filename, &statbuf) != 0) {
446                 bb_perror_msg("cannot stat '%s'", filename);
447                 return 0;
448         }
449
450 #ifdef CONFIG_FEATURE_STAT_FORMAT
451         if (format == NULL) {
452                 if (flags & OPT_TERSE) {
453                         format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
454                 } else {
455                         if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
456                                 format =
457                                         "  File: \"%N\"\n"
458                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
459                                         "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
460                                         " Device type: %t,%T\n"
461                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
462                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n";
463                         } else {
464                                 format =
465                                         "  File: \"%N\"\n"
466                                         "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
467                                         "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
468                                         "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
469                                         "Access: %x\n" "Modify: %y\n" "Change: %z\n";
470                         }
471                 }
472         }
473         print_it(format, filename, print_stat, &statbuf);
474 #else
475         if (flags & OPT_TERSE) {
476                 printf("%s %lu %lu %lx %lu %lu %lx %lu %lu %lx %lx %lu %lu %lu %lu\n",
477                        filename,
478                        (uintmax_t) (statbuf.st_size),
479                        (uintmax_t) statbuf.st_blocks,
480                        (unsigned long int) statbuf.st_mode,
481                        (unsigned long int) statbuf.st_uid,
482                        (unsigned long int) statbuf.st_gid,
483                        (uintmax_t) statbuf.st_dev,
484                        (uintmax_t) statbuf.st_ino,
485                        (unsigned long int) statbuf.st_nlink,
486                        (unsigned long int) major(statbuf.st_rdev),
487                        (unsigned long int) minor(statbuf.st_rdev),
488                        (unsigned long int) statbuf.st_atime,
489                        (unsigned long int) statbuf.st_mtime,
490                        (unsigned long int) statbuf.st_ctime,
491                        (unsigned long int) statbuf.st_blksize
492                 );
493         } else {
494                 char *linkname = NULL;
495
496                 struct passwd *pw_ent;
497                 struct group *gw_ent;
498                 setgrent();
499                 gw_ent = getgrgid(statbuf.st_gid);
500                 setpwent();
501                 pw_ent = getpwuid(statbuf.st_uid);
502
503                 if (S_ISLNK(statbuf.st_mode))
504                         linkname = xreadlink(filename);
505                 if (linkname)
506                         printf("  File: \"%s\" -> \"%s\"\n", filename, linkname);
507                 else
508                         printf("  File: \"%s\"\n", filename);
509
510                 printf("  Size: %-10lu\tBlocks: %-10lu IO Block: %-6lu %s\n"
511                        "Device: %lxh/%lud\tInode: %-10lu  Links: %-5lu",
512                        (uintmax_t) (statbuf.st_size),
513                        (uintmax_t) statbuf.st_blocks,
514                        (unsigned long int) statbuf.st_blksize,
515                        file_type(&statbuf),
516                        (uintmax_t) statbuf.st_dev,
517                        (uintmax_t) statbuf.st_dev,
518                        (uintmax_t) statbuf.st_ino,
519                        (unsigned long int) statbuf.st_nlink);
520                 if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode))
521                         printf(" Device type: %lx,%lx\n",
522                                (unsigned long int) major(statbuf.st_rdev),
523                                (unsigned long int) minor(statbuf.st_rdev));
524                 else
525                         putchar('\n');
526                 printf("Access: (%04lo/%10.10s)  Uid: (%5lu/%8s)   Gid: (%5lu/%8s)\n"
527                        "Access: %s\n" "Modify: %s\n" "Change: %s\n",
528                        (unsigned long int) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)),
529                        bb_mode_string(statbuf.st_mode),
530                        (unsigned long int) statbuf.st_uid,
531                        (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN",
532                        (unsigned long int) statbuf.st_gid,
533                        (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN",
534                        human_time(statbuf.st_atime),
535                        human_time(statbuf.st_mtime),
536                        human_time(statbuf.st_ctime));
537         }
538 #endif
539         return 1;
540 }
541
542 int stat_main(int argc, char **argv)
543 {
544         int i;
545         char *format = NULL;
546         int ok = 1;
547         int (*statfunc)(char const *, char const *) = do_stat;
548
549         flags = bb_getopt_ulflags(argc, argv, "ftL"
550 #ifdef CONFIG_FEATURE_STAT_FORMAT
551         "c:", &format
552 #endif
553         );
554
555         if (flags & 1)                /* -f */
556                 statfunc = do_statfs;
557         if (argc == optind)           /* files */
558                 bb_show_usage();
559
560         for (i = optind; i < argc; ++i)
561                 ok &= statfunc(argv[i], format);
562
563         return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
564 }