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 tarball for details.
  14 */
  15
  16#include "libbb.h"
  17
  18/* vars to control behavior */
  19#define OPT_FILESYS     (1 << 0)
  20#define OPT_TERSE       (1 << 1)
  21#define OPT_DEREFERENCE (1 << 2)
  22#define OPT_SELINUX     (1 << 3)
  23
  24#if ENABLE_FEATURE_STAT_FORMAT
  25typedef bool (*statfunc_ptr)(const char *, const char *);
  26#else
  27typedef bool (*statfunc_ptr)(const char *);
  28#endif
  29
  30static const char *file_type(const struct stat *st)
  31{
  32        /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107
  33         * for some of these formats.
  34         * To keep diagnostics grammatical in English, the
  35         * returned string must start with a consonant.
  36         */
  37        if (S_ISREG(st->st_mode))  return st->st_size == 0 ? "regular empty file" : "regular file";
  38        if (S_ISDIR(st->st_mode))  return "directory";
  39        if (S_ISBLK(st->st_mode))  return "block special file";
  40        if (S_ISCHR(st->st_mode))  return "character special file";
  41        if (S_ISFIFO(st->st_mode)) return "fifo";
  42        if (S_ISLNK(st->st_mode))  return "symbolic link";
  43        if (S_ISSOCK(st->st_mode)) return "socket";
  44        if (S_TYPEISMQ(st))        return "message queue";
  45        if (S_TYPEISSEM(st))       return "semaphore";
  46        if (S_TYPEISSHM(st))       return "shared memory object";
  47#ifdef S_TYPEISTMO
  48        if (S_TYPEISTMO(st))       return "typed memory object";
  49#endif
  50        return "weird file";
  51}
  52
  53static const char *human_time(time_t t)
  54{
  55        /* Old
  56        static char *str;
  57        str = ctime(&t);
  58        str[strlen(str)-1] = '\0';
  59        return str;
  60        */
  61        /* coreutils 6.3 compat: */
  62
  63        /*static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")] ALIGN1;*/
  64#define buf bb_common_bufsiz1
  65
  66        strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.000000000", localtime(&t));
  67        return buf;
  68#undef buf
  69}
  70
  71/* Return the type of the specified file system.
  72 * Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris)
  73 * Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2)
  74 * Still others have neither and have to get by with f_type (Linux).
  75 */
  76static const char *human_fstype(uint32_t f_type)
  77{
  78        static const struct types {
  79                uint32_t type;
  80                const char *const fs;
  81        } humantypes[] = {
  82                { 0xADFF,     "affs" },
  83                { 0x1Cd1,     "devpts" },
  84                { 0x137D,     "ext" },
  85                { 0xEF51,     "ext2" },
  86                { 0xEF53,     "ext2/ext3" },
  87                { 0x3153464a, "jfs" },
  88                { 0x58465342, "xfs" },
  89                { 0xF995E849, "hpfs" },
  90                { 0x9660,     "isofs" },
  91                { 0x4000,     "isofs" },
  92                { 0x4004,     "isofs" },
  93                { 0x137F,     "minix" },
  94                { 0x138F,     "minix (30 char.)" },
  95                { 0x2468,     "minix v2" },
  96                { 0x2478,     "minix v2 (30 char.)" },
  97                { 0x4d44,     "msdos" },
  98                { 0x4006,     "fat" },
  99                { 0x564c,     "novell" },
 100                { 0x6969,     "nfs" },
 101                { 0x9fa0,     "proc" },
 102                { 0x517B,     "smb" },
 103                { 0x012FF7B4, "xenix" },
 104                { 0x012FF7B5, "sysv4" },
 105                { 0x012FF7B6, "sysv2" },
 106                { 0x012FF7B7, "coh" },
 107                { 0x00011954, "ufs" },
 108                { 0x012FD16D, "xia" },
 109                { 0x5346544e, "ntfs" },
 110                { 0x1021994,  "tmpfs" },
 111                { 0x52654973, "reiserfs" },
 112                { 0x28cd3d45, "cramfs" },
 113                { 0x7275,     "romfs" },
 114                { 0x858458f6, "romfs" },
 115                { 0x73717368, "squashfs" },
 116                { 0x62656572, "sysfs" },
 117                { 0, "UNKNOWN" }
 118        };
 119
 120        int i;
 121
 122        for (i = 0; humantypes[i].type; ++i)
 123                if (humantypes[i].type == f_type)
 124                        break;
 125        return humantypes[i].fs;
 126}
 127
 128#if ENABLE_FEATURE_STAT_FORMAT
 129static void strcatc(char *str, char c)
 130{
 131        int len = strlen(str);
 132        str[len++] = c;
 133        str[len] = '\0';
 134}
 135
 136static void printfs(char *pformat, const char *msg)
 137{
 138        strcatc(pformat, 's');
 139        printf(pformat, msg);
 140}
 141
 142/* print statfs info */
 143static void print_statfs(char *pformat, const char m,
 144                const char *const filename, const void *data
 145                USE_SELINUX(, security_context_t scontext))
 146{
 147        const struct statfs *statfsbuf = data;
 148        if (m == 'n') {
 149                printfs(pformat, filename);
 150        } else if (m == 'i') {
 151                strcat(pformat, "Lx");
 152                printf(pformat, statfsbuf->f_fsid);
 153        } else if (m == 'l') {
 154                strcat(pformat, "lu");
 155                printf(pformat, statfsbuf->f_namelen);
 156        } else if (m == 't') {
 157                strcat(pformat, "lx");
 158                printf(pformat, (unsigned long) (statfsbuf->f_type)); /* no equiv */
 159        } else if (m == 'T') {
 160                printfs(pformat, human_fstype(statfsbuf->f_type));
 161        } else if (m == 'b') {
 162                strcat(pformat, "jd");
 163                printf(pformat, (intmax_t) (statfsbuf->f_blocks));
 164        } else if (m == 'f') {
 165                strcat(pformat, "jd");
 166                printf(pformat, (intmax_t) (statfsbuf->f_bfree));
 167        } else if (m == 'a') {
 168                strcat(pformat, "jd");
 169                printf(pformat, (intmax_t) (statfsbuf->f_bavail));
 170        } else if (m == 's' || m == 'S') {
 171                strcat(pformat, "lu");
 172                printf(pformat, (unsigned long) (statfsbuf->f_bsize));
 173        } else if (m == 'c') {
 174                strcat(pformat, "jd");
 175                printf(pformat, (intmax_t) (statfsbuf->f_files));
 176        } else if (m == 'd') {
 177                strcat(pformat, "jd");
 178                printf(pformat, (intmax_t) (statfsbuf->f_ffree));
 179#if ENABLE_SELINUX
 180        } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
 181                printfs(pformat, scontext);
 182#endif
 183        } else {
 184                strcatc(pformat, 'c');
 185                printf(pformat, m);
 186        }
 187}
 188
 189/* print stat info */
 190static void print_stat(char *pformat, const char m,
 191                const char *const filename, const void *data
 192                USE_SELINUX(, security_context_t scontext))
 193{
 194#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
 195        struct stat *statbuf = (struct stat *) data;
 196        struct passwd *pw_ent;
 197        struct group *gw_ent;
 198
 199        if (m == 'n') {
 200                printfs(pformat, filename);
 201        } else if (m == 'N') {
 202                strcatc(pformat, 's');
 203                if (S_ISLNK(statbuf->st_mode)) {
 204                        char *linkname = xmalloc_readlink_or_warn(filename);
 205                        if (linkname == NULL)
 206                                return;
 207                        /*printf("\"%s\" -> \"%s\"", filename, linkname); */
 208                        printf(pformat, filename);
 209                        printf(" -> ");
 210                        printf(pformat, linkname);
 211                        free(linkname);
 212                } else {
 213                        printf(pformat, filename);
 214                }
 215        } else if (m == 'd') {
 216                strcat(pformat, "ju");
 217                printf(pformat, (uintmax_t) statbuf->st_dev);
 218        } else if (m == 'D') {
 219                strcat(pformat, "jx");
 220                printf(pformat, (uintmax_t) statbuf->st_dev);
 221        } else if (m == 'i') {
 222                strcat(pformat, "ju");
 223                printf(pformat, (uintmax_t) statbuf->st_ino);
 224        } else if (m == 'a') {
 225                strcat(pformat, "lo");
 226                printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)));
 227        } else if (m == 'A') {
 228                printfs(pformat, bb_mode_string(statbuf->st_mode));
 229        } else if (m == 'f') {
 230                strcat(pformat, "lx");
 231                printf(pformat, (unsigned long) statbuf->st_mode);
 232        } else if (m == 'F') {
 233                printfs(pformat, file_type(statbuf));
 234        } else if (m == 'h') {
 235                strcat(pformat, "lu");
 236                printf(pformat, (unsigned long) statbuf->st_nlink);
 237        } else if (m == 'u') {
 238                strcat(pformat, "lu");
 239                printf(pformat, (unsigned long) statbuf->st_uid);
 240        } else if (m == 'U') {
 241                setpwent();
 242                pw_ent = getpwuid(statbuf->st_uid);
 243                printfs(pformat, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN");
 244        } else if (m == 'g') {
 245                strcat(pformat, "lu");
 246                printf(pformat, (unsigned long) statbuf->st_gid);
 247        } else if (m == 'G') {
 248                setgrent();
 249                gw_ent = getgrgid(statbuf->st_gid);
 250                printfs(pformat, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN");
 251        } else if (m == 't') {
 252                strcat(pformat, "lx");
 253                printf(pformat, (unsigned long) major(statbuf->st_rdev));
 254        } else if (m == 'T') {
 255                strcat(pformat, "lx");
 256                printf(pformat, (unsigned long) minor(statbuf->st_rdev));
 257        } else if (m == 's') {
 258                strcat(pformat, "ju");
 259                printf(pformat, (uintmax_t) (statbuf->st_size));
 260        } else if (m == 'B') {
 261                strcat(pformat, "lu");
 262                printf(pformat, (unsigned long) 512); //ST_NBLOCKSIZE
 263        } else if (m == 'b') {
 264                strcat(pformat, "ju");
 265                printf(pformat, (uintmax_t) statbuf->st_blocks);
 266        } else if (m == 'o') {
 267                strcat(pformat, "lu");
 268                printf(pformat, (unsigned long) statbuf->st_blksize);
 269        } else if (m == 'x') {
 270                printfs(pformat, human_time(statbuf->st_atime));
 271        } else if (m == 'X') {
 272                strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
 273                printf(pformat, (unsigned long) statbuf->st_atime);
 274        } else if (m == 'y') {
 275                printfs(pformat, human_time(statbuf->st_mtime));
 276        } else if (m == 'Y') {
 277                strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
 278                printf(pformat, (unsigned long) statbuf->st_mtime);
 279        } else if (m == 'z') {
 280                printfs(pformat, human_time(statbuf->st_ctime));
 281        } else if (m == 'Z') {
 282                strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
 283                printf(pformat, (unsigned long) statbuf->st_ctime);
 284#if ENABLE_SELINUX
 285        } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
 286                printfs(pformat, scontext);
 287#endif
 288        } else {
 289                strcatc(pformat, 'c');
 290                printf(pformat, m);
 291        }
 292}
 293
 294static void print_it(const char *masterformat, const char *filename,
 295                void (*print_func) (char*, char, const char*, const void* USE_SELINUX(, security_context_t scontext)),
 296                const void *data
 297                USE_SELINUX(, security_context_t scontext) )
 298{
 299        /* Create a working copy of the format string */
 300        char *format = xstrdup(masterformat);
 301        /* Add 2 to accomodate our conversion of the stat '%s' format string
 302         * to the printf '%llu' one.  */
 303        char *dest = xmalloc(strlen(format) + 2 + 1);
 304        char *b;
 305
 306        b = format;
 307        while (b) {
 308                size_t len;
 309                char *p = strchr(b, '%');
 310                if (!p) {
 311                        /* coreutils 6.3 always prints <cr> at the end */
 312                        /*fputs(b, stdout);*/
 313                        puts(b);
 314                        break;
 315                }
 316                *p++ = '\0';
 317                fputs(b, stdout);
 318
 319                /* dest = "%<modifiers>" */
 320                len = strspn(p, "#-+.I 0123456789");
 321                dest[0] = '%';
 322                memcpy(dest + 1, p, len);
 323                dest[1 + len] = '\0';
 324                p += len;
 325
 326                b = p + 1;
 327                switch (*p) {
 328                case '\0':
 329                        b = NULL;
 330                        /* fall through */
 331                case '%':
 332                        bb_putchar('%');
 333                        break;
 334                default:
 335                        /* Completes "%<modifiers>" with specifier and printfs */
 336                        print_func(dest, *p, filename, data USE_SELINUX(,scontext));
 337                        break;
 338                }
 339        }
 340
 341        free(format);
 342        free(dest);
 343}
 344#endif
 345
 346/* Stat the file system and print what we find.  */
 347#if !ENABLE_FEATURE_STAT_FORMAT
 348#define do_statfs(filename, format) do_statfs(filename)
 349#endif
 350static bool do_statfs(const char *filename, const char *format)
 351{
 352#if !ENABLE_FEATURE_STAT_FORMAT
 353        const char *format;
 354#endif
 355        struct statfs statfsbuf;
 356#if ENABLE_SELINUX
 357        security_context_t scontext = NULL;
 358
 359        if (option_mask32 & OPT_SELINUX) {
 360                if ((option_mask32 & OPT_DEREFERENCE
 361                     ? lgetfilecon(filename, &scontext)
 362                     : getfilecon(filename, &scontext)
 363                    ) < 0
 364                ) {
 365                        bb_perror_msg(filename);
 366                        return 0;
 367                }
 368        }
 369#endif
 370        if (statfs(filename, &statfsbuf) != 0) {
 371                bb_perror_msg("cannot read file system information for '%s'", filename);
 372                return 0;
 373        }
 374
 375#if ENABLE_FEATURE_STAT_FORMAT
 376        if (format == NULL) {
 377#if !ENABLE_SELINUX
 378                format = (option_mask32 & OPT_TERSE
 379                        ? "%n %i %l %t %s %b %f %a %c %d\n"
 380                        : "  File: \"%n\"\n"
 381                          "    ID: %-8i Namelen: %-7l Type: %T\n"
 382                          "Block size: %-10s\n"
 383                          "Blocks: Total: %-10b Free: %-10f Available: %a\n"
 384                          "Inodes: Total: %-10c Free: %d");
 385#else
 386                format = (option_mask32 & OPT_TERSE
 387                        ? (option_mask32 & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n":
 388                        "%n %i %l %t %s %b %f %a %c %d\n")
 389                        : (option_mask32 & OPT_SELINUX ?
 390                        "  File: \"%n\"\n"
 391                        "    ID: %-8i Namelen: %-7l Type: %T\n"
 392                        "Block size: %-10s\n"
 393                        "Blocks: Total: %-10b Free: %-10f Available: %a\n"
 394                        "Inodes: Total: %-10c Free: %d"
 395                        "  S_context: %C\n":
 396                        "  File: \"%n\"\n"
 397                        "    ID: %-8i Namelen: %-7l Type: %T\n"
 398                        "Block size: %-10s\n"
 399                        "Blocks: Total: %-10b Free: %-10f Available: %a\n"
 400                        "Inodes: Total: %-10c Free: %d\n")
 401                        );
 402#endif /* SELINUX */
 403        }
 404        print_it(format, filename, print_statfs, &statfsbuf USE_SELINUX(, scontext));
 405#else /* FEATURE_STAT_FORMAT */
 406        format = (option_mask32 & OPT_TERSE
 407                ? "%s %llx %lu "
 408                : "  File: \"%s\"\n"
 409                  "    ID: %-8Lx Namelen: %-7lu ");
 410        printf(format,
 411               filename,
 412               statfsbuf.f_fsid,
 413               statfsbuf.f_namelen);
 414
 415        if (option_mask32 & OPT_TERSE)
 416                printf("%lx ", (unsigned long) (statfsbuf.f_type));
 417        else
 418                printf("Type: %s\n", human_fstype(statfsbuf.f_type));
 419
 420#if !ENABLE_SELINUX
 421        format = (option_mask32 & OPT_TERSE
 422                ? "%lu %ld %ld %ld %ld %ld\n"
 423                : "Block size: %-10lu\n"
 424                  "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n"
 425                  "Inodes: Total: %-10jd Free: %jd\n");
 426        printf(format,
 427               (unsigned long) (statfsbuf.f_bsize),
 428               (intmax_t) (statfsbuf.f_blocks),
 429               (intmax_t) (statfsbuf.f_bfree),
 430               (intmax_t) (statfsbuf.f_bavail),
 431               (intmax_t) (statfsbuf.f_files),
 432               (intmax_t) (statfsbuf.f_ffree));
 433#else
 434        format = (option_mask32 & OPT_TERSE
 435                ? (option_mask32 & OPT_SELINUX ? "%lu %ld %ld %ld %ld %ld %C\n":
 436                "%lu %ld %ld %ld %ld %ld\n")
 437                : (option_mask32 & OPT_SELINUX ?
 438                "Block size: %-10lu\n"
 439                "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n"
 440                "Inodes: Total: %-10jd Free: %jd"
 441                "S_context: %C\n":
 442                "Block size: %-10lu\n"
 443                "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n"
 444                "Inodes: Total: %-10jd Free: %jd\n"));
 445        printf(format,
 446               (unsigned long) (statfsbuf.f_bsize),
 447               (intmax_t) (statfsbuf.f_blocks),
 448               (intmax_t) (statfsbuf.f_bfree),
 449               (intmax_t) (statfsbuf.f_bavail),
 450               (intmax_t) (statfsbuf.f_files),
 451               (intmax_t) (statfsbuf.f_ffree),
 452                scontext);
 453
 454        if (scontext)
 455                freecon(scontext);
 456#endif
 457#endif  /* FEATURE_STAT_FORMAT */
 458        return 1;
 459}
 460
 461/* stat the file and print what we find */
 462#if !ENABLE_FEATURE_STAT_FORMAT
 463#define do_stat(filename, format) do_stat(filename)
 464#endif
 465static bool do_stat(const char *filename, const char *format)
 466{
 467        struct stat statbuf;
 468#if ENABLE_SELINUX
 469        security_context_t scontext = NULL;
 470
 471        if (option_mask32 & OPT_SELINUX) {
 472                if ((option_mask32 & OPT_DEREFERENCE
 473                     ? lgetfilecon(filename, &scontext)
 474                     : getfilecon(filename, &scontext)
 475                    ) < 0
 476                ) {
 477                        bb_perror_msg(filename);
 478                        return 0;
 479                }
 480        }
 481#endif
 482        if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) {
 483                bb_perror_msg("cannot stat '%s'", filename);
 484                return 0;
 485        }
 486
 487#if ENABLE_FEATURE_STAT_FORMAT
 488        if (format == NULL) {
 489#if !ENABLE_SELINUX
 490                if (option_mask32 & OPT_TERSE) {
 491                        format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
 492                } else {
 493                        if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
 494                                format =
 495                                        "  File: \"%N\"\n"
 496                                        "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 497                                        "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
 498                                        " Device type: %t,%T\n"
 499                                        "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 500                                        "Access: %x\n" "Modify: %y\n" "Change: %z\n";
 501                        } else {
 502                                format =
 503                                        "  File: \"%N\"\n"
 504                                        "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 505                                        "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
 506                                        "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 507                                        "Access: %x\n" "Modify: %y\n" "Change: %z\n";
 508                        }
 509                }
 510#else
 511                if (option_mask32 & OPT_TERSE) {
 512                        format = (option_mask32 & OPT_SELINUX ?
 513                                  "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n":
 514                                  "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n");
 515                } else {
 516                        if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
 517                                format = (option_mask32 & OPT_SELINUX ?
 518                                          "  File: \"%N\"\n"
 519                                          "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 520                                          "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
 521                                          " Device type: %t,%T\n"
 522                                          "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 523                                          "   S_Context: %C\n"
 524                                          "Access: %x\n" "Modify: %y\n" "Change: %z\n":
 525                                          "  File: \"%N\"\n"
 526                                          "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 527                                          "Device: %Dh/%dd\tInode: %-10i  Links: %-5h"
 528                                          " Device type: %t,%T\n"
 529                                          "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 530                                          "Access: %x\n" "Modify: %y\n" "Change: %z\n");
 531                        } else {
 532                                format = (option_mask32 & OPT_SELINUX ?
 533                                          "  File: \"%N\"\n"
 534                                          "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 535                                          "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
 536                                          "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 537                                          "S_Context: %C\n"
 538                                          "Access: %x\n" "Modify: %y\n" "Change: %z\n":
 539                                          "  File: \"%N\"\n"
 540                                          "  Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
 541                                          "Device: %Dh/%dd\tInode: %-10i  Links: %h\n"
 542                                          "Access: (%04a/%10.10A)  Uid: (%5u/%8U)   Gid: (%5g/%8G)\n"
 543                                          "Access: %x\n" "Modify: %y\n" "Change: %z\n");
 544                        }
 545                }
 546#endif
 547        }
 548        print_it(format, filename, print_stat, &statbuf USE_SELINUX(, scontext));
 549#else   /* FEATURE_STAT_FORMAT */
 550        if (option_mask32 & OPT_TERSE) {
 551                printf("%s %ju %ju %lx %lu %lu %jx %ju %lu %lx %lx %lu %lu %lu %lu"
 552                       SKIP_SELINUX("\n"),
 553                       filename,
 554                       (uintmax_t) (statbuf.st_size),
 555                       (uintmax_t) statbuf.st_blocks,
 556                       (unsigned long) statbuf.st_mode,
 557                       (unsigned long) statbuf.st_uid,
 558                       (unsigned long) statbuf.st_gid,
 559                       (uintmax_t) statbuf.st_dev,
 560                       (uintmax_t) statbuf.st_ino,
 561                       (unsigned long) statbuf.st_nlink,
 562                       (unsigned long) major(statbuf.st_rdev),
 563                       (unsigned long) minor(statbuf.st_rdev),
 564                       (unsigned long) statbuf.st_atime,
 565                       (unsigned long) statbuf.st_mtime,
 566                       (unsigned long) statbuf.st_ctime,
 567                       (unsigned long) statbuf.st_blksize
 568                );
 569#if ENABLE_SELINUX
 570                if (option_mask32 & OPT_SELINUX)
 571                        printf(" %lc\n", *scontext);
 572                else
 573                        bb_putchar('\n');
 574#endif
 575        } else {
 576                char *linkname = NULL;
 577
 578                struct passwd *pw_ent;
 579                struct group *gw_ent;
 580                setgrent();
 581                gw_ent = getgrgid(statbuf.st_gid);
 582                setpwent();
 583                pw_ent = getpwuid(statbuf.st_uid);
 584
 585                if (S_ISLNK(statbuf.st_mode))
 586                        linkname = xmalloc_readlink_or_warn(filename);
 587                if (linkname)
 588                        printf("  File: \"%s\" -> \"%s\"\n", filename, linkname);
 589                else
 590                        printf("  File: \"%s\"\n", filename);
 591
 592                printf("  Size: %-10ju\tBlocks: %-10ju IO Block: %-6lu %s\n"
 593                       "Device: %jxh/%jud\tInode: %-10ju  Links: %-5lu",
 594                       (uintmax_t) (statbuf.st_size),
 595                       (uintmax_t) statbuf.st_blocks,
 596                       (unsigned long) statbuf.st_blksize,
 597                       file_type(&statbuf),
 598                       (uintmax_t) statbuf.st_dev,
 599                       (uintmax_t) statbuf.st_dev,
 600                       (uintmax_t) statbuf.st_ino,
 601                       (unsigned long) statbuf.st_nlink);
 602                if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode))
 603                        printf(" Device type: %lx,%lx\n",
 604                               (unsigned long) major(statbuf.st_rdev),
 605                               (unsigned long) minor(statbuf.st_rdev));
 606                else
 607                        bb_putchar('\n');
 608                printf("Access: (%04lo/%10.10s)  Uid: (%5lu/%8s)   Gid: (%5lu/%8s)\n",
 609                       (unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)),
 610                       bb_mode_string(statbuf.st_mode),
 611                       (unsigned long) statbuf.st_uid,
 612                       (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN",
 613                       (unsigned long) statbuf.st_gid,
 614                       (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN");
 615#if ENABLE_SELINUX
 616                printf("   S_Context: %lc\n", *scontext);
 617#endif
 618                printf("Access: %s\n" "Modify: %s\n" "Change: %s\n",
 619                       human_time(statbuf.st_atime),
 620                       human_time(statbuf.st_mtime),
 621                       human_time(statbuf.st_ctime));
 622        }
 623#endif  /* FEATURE_STAT_FORMAT */
 624        return 1;
 625}
 626
 627int stat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 628int stat_main(int argc, char **argv)
 629{
 630        USE_FEATURE_STAT_FORMAT(char *format = NULL;)
 631        int i;
 632        int ok = 1;
 633        statfunc_ptr statfunc = do_stat;
 634
 635        getopt32(argv, "ftL"
 636                USE_SELINUX("Z")
 637                USE_FEATURE_STAT_FORMAT("c:", &format)
 638        );
 639
 640        if (option_mask32 & OPT_FILESYS) /* -f */
 641                statfunc = do_statfs;
 642        if (argc == optind)           /* files */
 643                bb_show_usage();
 644
 645#if ENABLE_SELINUX
 646        if (option_mask32 & OPT_SELINUX) {
 647                selinux_or_die();
 648        }
 649#endif  /* ENABLE_SELINUX */
 650        for (i = optind; i < argc; ++i)
 651                ok &= statfunc(argv[i] USE_FEATURE_STAT_FORMAT(, format));
 652
 653        return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
 654}
 655