busybox/coreutils/stat.c
<<
>>
Prefs
   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
  83typedef bool (*statfunc_ptr)(const char *, const char *);
  84#else
  85typedef bool (*statfunc_ptr)(const char *);
  86#endif
  87
  88static 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#ifdef S_TYPEISMQ
 103        if (S_TYPEISMQ(st))        return "message queue";
 104#endif
 105#ifdef S_TYPEISSEM
 106        if (S_TYPEISSEM(st))       return "semaphore";
 107#endif
 108#ifdef S_TYPEISSHM
 109        if (S_TYPEISSHM(st))       return "shared memory object";
 110#endif
 111#ifdef S_TYPEISTMO
 112        if (S_TYPEISTMO(st))       return "typed memory object";
 113#endif
 114        return "weird file";
 115}
 116
 117static const char *human_time(time_t t)
 118{
 119        /* Old
 120        static char *str;
 121        str = ctime(&t);
 122        str[strlen(str)-1] = '\0';
 123        return str;
 124        */
 125        /* coreutils 6.3 compat: */
 126
 127        /*static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")] ALIGN1;*/
 128#define buf bb_common_bufsiz1
 129
 130        strcpy(strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &t), ".000000000");
 131        return buf;
 132#undef buf
 133}
 134
 135/* Return the type of the specified file system.
 136 * Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris)
 137 * Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2)
 138 * Still others have neither and have to get by with f_type (Linux).
 139 */
 140static const char *human_fstype(uint32_t f_type)
 141{
 142        static const struct types {
 143                uint32_t type;
 144                const char *const fs;
 145        } humantypes[] = {
 146                { 0xADFF,     "affs" },
 147                { 0x1Cd1,     "devpts" },
 148                { 0x137D,     "ext" },
 149                { 0xEF51,     "ext2" },
 150                { 0xEF53,     "ext2/ext3" },
 151                { 0x3153464a, "jfs" },
 152                { 0x58465342, "xfs" },
 153                { 0xF995E849, "hpfs" },
 154                { 0x9660,     "isofs" },
 155                { 0x4000,     "isofs" },
 156                { 0x4004,     "isofs" },
 157                { 0x137F,     "minix" },
 158                { 0x138F,     "minix (30 char.)" },
 159                { 0x2468,     "minix v2" },
 160                { 0x2478,     "minix v2 (30 char.)" },
 161                { 0x4d44,     "msdos" },
 162                { 0x4006,     "fat" },
 163                { 0x564c,     "novell" },
 164                { 0x6969,     "nfs" },
 165                { 0x9fa0,     "proc" },
 166                { 0x517B,     "smb" },
 167                { 0x012FF7B4, "xenix" },
 168                { 0x012FF7B5, "sysv4" },
 169                { 0x012FF7B6, "sysv2" },
 170                { 0x012FF7B7, "coh" },
 171                { 0x00011954, "ufs" },
 172                { 0x012FD16D, "xia" },
 173                { 0x5346544e, "ntfs" },
 174                { 0x1021994,  "tmpfs" },
 175                { 0x52654973, "reiserfs" },
 176                { 0x28cd3d45, "cramfs" },
 177                { 0x7275,     "romfs" },
 178                { 0x858458f6, "romfs" },
 179                { 0x73717368, "squashfs" },
 180                { 0x62656572, "sysfs" },
 181                { 0, "UNKNOWN" }
 182        };
 183
 184        int i;
 185
 186        for (i = 0; humantypes[i].type; ++i)
 187                if (humantypes[i].type == f_type)
 188                        break;
 189        return humantypes[i].fs;
 190}
 191
 192/* "man statfs" says that statfsbuf->f_fsid is a mess */
 193/* coreutils treats it as an array of ints, most significant first */
 194static unsigned long long get_f_fsid(const struct statfs *statfsbuf)
 195{
 196        const unsigned *p = (const void*) &statfsbuf->f_fsid;
 197        unsigned sz = sizeof(statfsbuf->f_fsid) / sizeof(unsigned);
 198        unsigned long long r = 0;
 199
 200        do
 201                r = (r << (sizeof(unsigned)*8)) | *p++;
 202        while (--sz > 0);
 203        return r;
 204}
 205
 206#if ENABLE_FEATURE_STAT_FORMAT
 207static void strcatc(char *str, char c)
 208{
 209        int len = strlen(str);
 210        str[len++] = c;
 211        str[len] = '\0';
 212}
 213
 214static void printfs(char *pformat, const char *msg)
 215{
 216        strcatc(pformat, 's');
 217        printf(pformat, msg);
 218}
 219
 220/* print statfs info */
 221static void FAST_FUNC print_statfs(char *pformat, const char m,
 222                const char *const filename, const void *data
 223                IF_SELINUX(, security_context_t scontext))
 224{
 225        const struct statfs *statfsbuf = data;
 226        if (m == 'n') {
 227                printfs(pformat, filename);
 228        } else if (m == 'i') {
 229                strcat(pformat, "llx");
 230                printf(pformat, get_f_fsid(statfsbuf));
 231        } else if (m == 'l') {
 232                strcat(pformat, "lu");
 233                printf(pformat, (unsigned long) statfsbuf->f_namelen);
 234        } else if (m == 't') {
 235                strcat(pformat, "lx");
 236                printf(pformat, (unsigned long) statfsbuf->f_type); /* no equiv */
 237        } else if (m == 'T') {
 238                printfs(pformat, human_fstype(statfsbuf->f_type));
 239        } else if (m == 'b') {
 240                strcat(pformat, "llu");
 241                printf(pformat, (unsigned long long) statfsbuf->f_blocks);
 242        } else if (m == 'f') {
 243                strcat(pformat, "llu");
 244                printf(pformat, (unsigned long long) statfsbuf->f_bfree);
 245        } else if (m == 'a') {
 246                strcat(pformat, "llu");
 247                printf(pformat, (unsigned long long) statfsbuf->f_bavail);
 248        } else if (m == 's' || m == 'S') {
 249                strcat(pformat, "lu");
 250                printf(pformat, (unsigned long) statfsbuf->f_bsize);
 251        } else if (m == 'c') {
 252                strcat(pformat, "llu");
 253                printf(pformat, (unsigned long long) statfsbuf->f_files);
 254        } else if (m == 'd') {
 255                strcat(pformat, "llu");
 256                printf(pformat, (unsigned long long) statfsbuf->f_ffree);
 257# if ENABLE_SELINUX
 258        } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
 259                printfs(pformat, scontext);
 260# endif
 261        } else {
 262                strcatc(pformat, 'c');
 263                printf(pformat, m);
 264        }
 265}
 266
 267/* print stat info */
 268static void FAST_FUNC print_stat(char *pformat, const char m,
 269                const char *const filename, const void *data
 270                IF_SELINUX(, security_context_t scontext))
 271{
 272#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
 273        struct stat *statbuf = (struct stat *) data;
 274        struct passwd *pw_ent;
 275        struct group *gw_ent;
 276
 277        if (m == 'n') {
 278                printfs(pformat, filename);
 279        } else if (m == 'N') {
 280                strcatc(pformat, 's');
 281                if (S_ISLNK(statbuf->st_mode)) {
 282                        char *linkname = xmalloc_readlink_or_warn(filename);
 283                        if (linkname == NULL)
 284                                return;
 285                        printf("'%s' -> '%s'", filename, linkname);
 286                        free(linkname);
 287                } else {
 288                        printf(pformat, filename);
 289                }
 290        } else if (m == 'd') {
 291                strcat(pformat, "llu");
 292                printf(pformat, (unsigned long long) statbuf->st_dev);
 293        } else if (m == 'D') {
 294                strcat(pformat, "llx");
 295                printf(pformat, (unsigned long long) statbuf->st_dev);
 296        } else if (m == 'i') {
 297                strcat(pformat, "llu");
 298                printf(pformat, (unsigned long long) statbuf->st_ino);
 299        } else if (m == 'a') {
 300                strcat(pformat, "lo");
 301                printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)));
 302        } else if (m == 'A') {
 303                printfs(pformat, bb_mode_string(statbuf->st_mode));
 304        } else if (m == 'f') {
 305                strcat(pformat, "lx");
 306                printf(pformat, (unsigned long) statbuf->st_mode);
 307        } else if (m == 'F') {
 308                printfs(pformat, file_type(statbuf));
 309        } else if (m == 'h') {
 310                strcat(pformat, "lu");
 311                printf(pformat, (unsigned long) statbuf->st_nlink);
 312        } else if (m == 'u') {
 313                strcat(pformat, "lu");
 314                printf(pformat, (unsigned long) statbuf->st_uid);
 315        } else if (m == 'U') {
 316                pw_ent = getpwuid(statbuf->st_uid);
 317                printfs(pformat, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN");
 318        } else if (m == 'g') {
 319                strcat(pformat, "lu");
 320                printf(pformat, (unsigned long) statbuf->st_gid);
 321        } else if (m == 'G') {
 322                gw_ent = getgrgid(statbuf->st_gid);
 323                printfs(pformat, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN");
 324        } else if (m == 't') {
 325                strcat(pformat, "lx");
 326                printf(pformat, (unsigned long) major(statbuf->st_rdev));
 327        } else if (m == 'T') {
 328                strcat(pformat, "lx");
 329                printf(pformat, (unsigned long) minor(statbuf->st_rdev));
 330        } else if (m == 's') {
 331                strcat(pformat, "llu");
 332                printf(pformat, (unsigned long long) statbuf->st_size);
 333        } else if (m == 'B') {
 334                strcat(pformat, "lu");
 335                printf(pformat, (unsigned long) 512); //ST_NBLOCKSIZE
 336        } else if (m == 'b') {
 337                strcat(pformat, "llu");
 338                printf(pformat, (unsigned long long) statbuf->st_blocks);
 339        } else if (m == 'o') {
 340                strcat(pformat, "lu");
 341                printf(pformat, (unsigned long) statbuf->st_blksize);
 342        } else if (m == 'x') {
 343                printfs(pformat, human_time(statbuf->st_atime));
 344        } else if (m == 'X') {
 345                strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
 346                /* note: (unsigned long) would be wrong:
 347                 * imagine (unsigned long64)int32 */
 348                printf(pformat, (long) statbuf->st_atime);
 349        } else if (m == 'y') {
 350                printfs(pformat, human_time(statbuf->st_mtime));
 351        } else if (m == 'Y') {
 352                strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
 353                printf(pformat, (long) statbuf->st_mtime);
 354        } else if (m == 'z') {
 355                printfs(pformat, human_time(statbuf->st_ctime));
 356        } else if (m == 'Z') {
 357                strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
 358                printf(pformat, (long) statbuf->st_ctime);
 359# if ENABLE_SELINUX
 360        } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
 361                printfs(pformat, scontext);
 362# endif
 363        } else {
 364                strcatc(pformat, 'c');
 365                printf(pformat, m);
 366        }
 367}
 368
 369static void print_it(const char *masterformat,
 370                const char *filename,
 371                void FAST_FUNC (*print_func)(char*, char, const char*, const void* IF_SELINUX(, security_context_t scontext)),
 372                const void *data
 373                IF_SELINUX(, security_context_t scontext))
 374{
 375        /* Create a working copy of the format string */
 376        char *format = xstrdup(masterformat);
 377        /* Add 2 to accomodate our conversion of the stat '%s' format string
 378         * to the printf '%llu' one.  */
 379        char *dest = xmalloc(strlen(format) + 2 + 1);
 380        char *b;
 381
 382        b = format;
 383        while (b) {
 384                /* Each iteration finds next %spec,
 385                 * prints preceding string and handles found %spec
 386                 */
 387                size_t len;
 388                char *p = strchr(b, '%');
 389                if (!p) {
 390                        /* coreutils 6.3 always prints newline at the end */
 391                        /*fputs(b, stdout);*/
 392                        puts(b);
 393                        break;
 394                }
 395
 396                /* dest = "%<modifiers>" */
 397                len = 1 + strspn(p + 1, "#-+.I 0123456789");
 398                memcpy(dest, p, len);
 399                dest[len] = '\0';
 400
 401                /* print preceding string */
 402                *p = '\0';
 403                fputs(b, stdout);
 404
 405                p += len;
 406                b = p + 1;
 407                switch (*p) {
 408                case '\0':
 409                        b = NULL;
 410                        /* fall through */
 411                case '%':
 412                        bb_putchar('%');
 413                        break;
 414                default:
 415                        /* Completes "%<modifiers>" with specifier and printfs */
 416                        print_func(dest, *p, filename, data IF_SELINUX(,scontext));
 417                        break;
 418                }
 419        }
 420
 421        free(format);
 422        free(dest);
 423}
 424#endif  /* FEATURE_STAT_FORMAT */
 425
 426/* Stat the file system and print what we find.  */
 427#if !ENABLE_FEATURE_STAT_FORMAT
 428#define do_statfs(filename, format) do_statfs(filename)
 429#endif
 430static bool do_statfs(const char *filename, const char *format)
 431{
 432        struct statfs statfsbuf;
 433#if !ENABLE_FEATURE_STAT_FORMAT
 434        const char *format;
 435#endif
 436#if ENABLE_SELINUX
 437        security_context_t scontext = NULL;
 438
 439        if (option_mask32 & OPT_SELINUX) {
 440                if ((option_mask32 & OPT_DEREFERENCE
 441                     ? lgetfilecon(filename, &scontext)
 442                     : getfilecon(filename, &scontext)
 443                    ) < 0
 444                ) {
 445                        bb_simple_perror_msg(filename);
 446                        return 0;
 447                }
 448        }
 449#endif
 450        if (statfs(filename, &statfsbuf) != 0) {
 451                bb_perror_msg("can't read file system information for '%s'", filename);
 452                return 0;
 453        }
 454
 455#if ENABLE_FEATURE_STAT_FORMAT
 456        if (format == NULL) {
 457# if !ENABLE_SELINUX
 458                format = (option_mask32 & OPT_TERSE
 459                        ? "%n %i %l %t %s %b %f %a %c %d\n"
 460                        : "  File: \"%n\"\n"
 461                          "    ID: %-8i Namelen: %-7l Type: %T\n"
 462                          "Block size: %-10s\n"
 463                          "Blocks: Total: %-10b Free: %-10f Available: %a\n"
 464                          "Inodes: Total: %-10c Free: %d");
 465# else
 466                format = (option_mask32 & OPT_TERSE
 467                        ? (option_mask32 & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n":
 468                        "%n %i %l %t %s %b %f %a %c %d\n")
 469                        : (option_mask32 & OPT_SELINUX ?
 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"
 475                        "  S_context: %C\n":
 476                        "  File: \"%n\"\n"
 477                        "    ID: %-8i Namelen: %-7l Type: %T\n"
 478                        "Block size: %-10s\n"
 479                        "Blocks: Total: %-10b Free: %-10f Available: %a\n"
 480                        "Inodes: Total: %-10c Free: %d\n")
 481                        );
 482# endif /* SELINUX */
 483        }
 484        print_it(format, filename, print_statfs, &statfsbuf IF_SELINUX(, scontext));
 485#else /* FEATURE_STAT_FORMAT */
 486        format = (option_mask32 & OPT_TERSE
 487                ? "%s %llx %lu "
 488                : "  File: \"%s\"\n"
 489                  "    ID: %-8llx Namelen: %-7lu ");
 490        printf(format,
 491               filename,
 492               get_f_fsid(&statfsbuf),
 493               statfsbuf.f_namelen);
 494
 495        if (option_mask32 & OPT_TERSE)
 496                printf("%lx ", (unsigned long) statfsbuf.f_type);
 497        else
 498                printf("Type: %s\n", human_fstype(statfsbuf.f_type));
 499
 500# if !ENABLE_SELINUX
 501        format = (option_mask32 & OPT_TERSE
 502                ? "%lu %llu %llu %llu %llu %llu\n"
 503                : "Block size: %-10lu\n"
 504                  "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
 505                  "Inodes: Total: %-10llu Free: %llu\n");
 506        printf(format,
 507               (unsigned long) statfsbuf.f_bsize,
 508               (unsigned long long) statfsbuf.f_blocks,
 509               (unsigned long long) statfsbuf.f_bfree,
 510               (unsigned long long) statfsbuf.f_bavail,
 511               (unsigned long long) statfsbuf.f_files,
 512               (unsigned long long) statfsbuf.f_ffree);
 513# else
 514        format = (option_mask32 & OPT_TERSE
 515                ? (option_mask32 & OPT_SELINUX ? "%lu %llu %llu %llu %llu %llu %C\n" : "%lu %llu %llu %llu %llu %llu\n")
 516                : (option_mask32 & OPT_SELINUX
 517                        ?       "Block size: %-10lu\n"
 518                                "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
 519                                "Inodes: Total: %-10llu Free: %llu"
 520                                "S_context: %C\n"
 521                        :       "Block size: %-10lu\n"
 522                                "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
 523                                "Inodes: Total: %-10llu Free: %llu\n"
 524                        )
 525                );
 526        printf(format,
 527                (unsigned long) statfsbuf.f_bsize,
 528                (unsigned long long) statfsbuf.f_blocks,
 529                (unsigned long long) statfsbuf.f_bfree,
 530                (unsigned long long) statfsbuf.f_bavail,
 531                (unsigned long long) statfsbuf.f_files,
 532                (unsigned long long) statfsbuf.f_ffree,
 533                scontext);
 534
 535        if (scontext)
 536                freecon(scontext);
 537# endif
 538#endif  /* FEATURE_STAT_FORMAT */
 539        return 1;
 540}
 541
 542/* stat the file and print what we find */
 543#if !ENABLE_FEATURE_STAT_FORMAT
 544#define do_stat(filename, format) do_stat(filename)
 545#endif
 546static bool do_stat(const char *filename, const char *format)
 547{
 548        struct stat statbuf;
 549#if ENABLE_SELINUX
 550        security_context_t scontext = NULL;
 551
 552        if (option_mask32 & OPT_SELINUX) {
 553                if ((option_mask32 & OPT_DEREFERENCE
 554                     ? lgetfilecon(filename, &scontext)
 555                     : getfilecon(filename, &scontext)
 556                    ) < 0
 557                ) {
 558                        bb_simple_perror_msg(filename);
 559                        return 0;
 560                }
 561        }
 562#endif
 563        if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) {
 564                bb_perror_msg("can't stat '%s'", filename);
 565                return 0;
 566        }
 567
 568#if ENABLE_FEATURE_STAT_FORMAT
 569        if (format == NULL) {
 570# if !ENABLE_SELINUX
 571                if (option_mask32 & OPT_TERSE) {
 572                        format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
 573                } else {
 574                        if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
 575                                format =
 576                                        "  File: %N\n"
 577                                        "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 578                                        "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
 579                                        " Device type: %t,%T\n"
 580                                        "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 581                                        "Access: %x\n" "Modify: %y\n" "Change: %z\n";
 582                        } else {
 583                                format =
 584                                        "  File: %N\n"
 585                                        "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 586                                        "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
 587                                        "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 588                                        "Access: %x\n" "Modify: %y\n" "Change: %z\n";
 589                        }
 590                }
 591# else
 592                if (option_mask32 & OPT_TERSE) {
 593                        format = (option_mask32 & OPT_SELINUX ?
 594                                "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n"
 595                                :
 596                                "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n"
 597                                );
 598                } else {
 599                        if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
 600                                format = (option_mask32 & OPT_SELINUX ?
 601                                        "  File: %N\n"
 602                                        "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 603                                        "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
 604                                        " Device type: %t,%T\n"
 605                                        "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 606                                        "   S_Context: %C\n"
 607                                        "Access: %x\n" "Modify: %y\n" "Change: %z\n"
 608                                        :
 609                                        "  File: %N\n"
 610                                        "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 611                                        "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
 612                                        " Device type: %t,%T\n"
 613                                        "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 614                                        "Access: %x\n" "Modify: %y\n" "Change: %z\n"
 615                                        );
 616                        } else {
 617                                format = (option_mask32 & OPT_SELINUX ?
 618                                        "  File: %N\n"
 619                                        "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 620                                        "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
 621                                        "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 622                                        "S_Context: %C\n"
 623                                        "Access: %x\n" "Modify: %y\n" "Change: %z\n"
 624                                        :
 625                                        "  File: %N\n"
 626                                        "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 627                                        "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
 628                                        "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 629                                        "Access: %x\n" "Modify: %y\n" "Change: %z\n"
 630                                        );
 631                        }
 632                }
 633# endif
 634        }
 635        print_it(format, filename, print_stat, &statbuf IF_SELINUX(, scontext));
 636#else   /* FEATURE_STAT_FORMAT */
 637        if (option_mask32 & OPT_TERSE) {
 638                printf("%s %llu %llu %lx %lu %lu %llx %llu %lu %lx %lx %lu %lu %lu %lu"
 639                       IF_NOT_SELINUX("\n"),
 640                       filename,
 641                       (unsigned long long) statbuf.st_size,
 642                       (unsigned long long) statbuf.st_blocks,
 643                       (unsigned long) statbuf.st_mode,
 644                       (unsigned long) statbuf.st_uid,
 645                       (unsigned long) statbuf.st_gid,
 646                       (unsigned long long) statbuf.st_dev,
 647                       (unsigned long long) statbuf.st_ino,
 648                       (unsigned long) statbuf.st_nlink,
 649                       (unsigned long) major(statbuf.st_rdev),
 650                       (unsigned long) minor(statbuf.st_rdev),
 651                       (unsigned long) statbuf.st_atime,
 652                       (unsigned long) statbuf.st_mtime,
 653                       (unsigned long) statbuf.st_ctime,
 654                       (unsigned long) statbuf.st_blksize
 655                );
 656# if ENABLE_SELINUX
 657                if (option_mask32 & OPT_SELINUX)
 658                        printf(" %s\n", scontext);
 659                else
 660                        bb_putchar('\n');
 661# endif
 662        } else {
 663                char *linkname = NULL;
 664                struct passwd *pw_ent;
 665                struct group *gw_ent;
 666
 667                gw_ent = getgrgid(statbuf.st_gid);
 668                pw_ent = getpwuid(statbuf.st_uid);
 669
 670                if (S_ISLNK(statbuf.st_mode))
 671                        linkname = xmalloc_readlink_or_warn(filename);
 672                if (linkname) {
 673                        printf("  File: '%s' -> '%s'\n", filename, linkname);
 674                        free(linkname);
 675                } else {
 676                        printf("  File: '%s'\n", filename);
 677                }
 678
 679                printf("  Size: %-10llu\tBlocks: %-10llu IO Block: %-6lu %s\n"
 680                       "Device: %llxh/%llud\tInode: %-10llu  Links: %-5lu",
 681                       (unsigned long long) statbuf.st_size,
 682                       (unsigned long long) statbuf.st_blocks,
 683                       (unsigned long) statbuf.st_blksize,
 684                       file_type(&statbuf),
 685                       (unsigned long long) statbuf.st_dev,
 686                       (unsigned long long) statbuf.st_dev,
 687                       (unsigned long long) statbuf.st_ino,
 688                       (unsigned long) statbuf.st_nlink);
 689                if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode))
 690                        printf(" Device type: %lx,%lx\n",
 691                               (unsigned long) major(statbuf.st_rdev),
 692                               (unsigned long) minor(statbuf.st_rdev));
 693                else
 694                        bb_putchar('\n');
 695                printf("Access: (%04lo/%10.10s)  Uid: (%5lu/%8s)   Gid: (%5lu/%8s)\n",
 696                       (unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)),
 697                       bb_mode_string(statbuf.st_mode),
 698                       (unsigned long) statbuf.st_uid,
 699                       (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN",
 700                       (unsigned long) statbuf.st_gid,
 701                       (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN");
 702# if ENABLE_SELINUX
 703                if (option_mask32 & OPT_SELINUX)
 704                        printf("   S_Context: %s\n", scontext);
 705# endif
 706                printf("Access: %s\n", human_time(statbuf.st_atime));
 707                printf("Modify: %s\n", human_time(statbuf.st_mtime));
 708                printf("Change: %s\n", human_time(statbuf.st_ctime));
 709        }
 710#endif  /* FEATURE_STAT_FORMAT */
 711        return 1;
 712}
 713
 714int stat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 715int stat_main(int argc UNUSED_PARAM, char **argv)
 716{
 717        IF_FEATURE_STAT_FORMAT(char *format = NULL;)
 718        int i;
 719        int ok;
 720        unsigned opts;
 721        statfunc_ptr statfunc = do_stat;
 722
 723        opt_complementary = "-1"; /* min one arg */
 724        opts = getopt32(argv, "ftL"
 725                IF_SELINUX("Z")
 726                IF_FEATURE_STAT_FORMAT("c:", &format)
 727        );
 728        if (opts & OPT_FILESYS) /* -f */
 729                statfunc = do_statfs;
 730#if ENABLE_SELINUX
 731        if (opts & OPT_SELINUX) {
 732                selinux_or_die();
 733        }
 734#endif
 735        ok = 1;
 736        argv += optind;
 737        for (i = 0; argv[i]; ++i)
 738                ok &= statfunc(argv[i] IF_FEATURE_STAT_FORMAT(, format));
 739
 740        return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
 741}
 742