1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108#include "libbb.h"
109#include "common_bufsiz.h"
110
111enum {
112 OPT_TERSE = (1 << 0),
113 OPT_DEREFERENCE = (1 << 1),
114 OPT_FILESYS = (1 << 2) * ENABLE_FEATURE_STAT_FILESYSTEM,
115 OPT_SELINUX = (1 << (2+ENABLE_FEATURE_STAT_FILESYSTEM)) * ENABLE_SELINUX,
116};
117
118#if ENABLE_FEATURE_STAT_FORMAT
119typedef bool (*statfunc_ptr)(const char *, const char *);
120#else
121typedef bool (*statfunc_ptr)(const char *);
122#endif
123
124static const char *file_type(const struct stat *st)
125{
126
127
128
129
130
131 if (S_ISREG(st->st_mode)) return st->st_size == 0 ? "regular empty file" : "regular file";
132 if (S_ISDIR(st->st_mode)) return "directory";
133 if (S_ISBLK(st->st_mode)) return "block special file";
134 if (S_ISCHR(st->st_mode)) return "character special file";
135 if (S_ISFIFO(st->st_mode)) return "fifo";
136 if (S_ISLNK(st->st_mode)) return "symbolic link";
137 if (S_ISSOCK(st->st_mode)) return "socket";
138#ifdef S_TYPEISMQ
139 if (S_TYPEISMQ(st)) return "message queue";
140#endif
141#ifdef S_TYPEISSEM
142 if (S_TYPEISSEM(st)) return "semaphore";
143#endif
144#ifdef S_TYPEISSHM
145 if (S_TYPEISSHM(st)) return "shared memory object";
146#endif
147#ifdef S_TYPEISTMO
148 if (S_TYPEISTMO(st)) return "typed memory object";
149#endif
150 return "weird file";
151}
152
153static const char *human_time(time_t t)
154{
155
156
157
158
159
160
161
162
163
164#define buf bb_common_bufsiz1
165 setup_common_bufsiz();
166 strcpy(strftime_YYYYMMDDHHMMSS(buf, COMMON_BUFSIZE, &t), ".000000000");
167 return buf;
168#undef buf
169}
170
171#if ENABLE_FEATURE_STAT_FILESYSTEM
172#define FS_TYPE_LIST \
173FS_TYPE(0xADFF, "affs") \
174FS_TYPE(0x1CD1, "devpts") \
175FS_TYPE(0x137D, "ext") \
176FS_TYPE(0xEF51, "ext2") \
177FS_TYPE(0xEF53, "ext2/ext3") \
178FS_TYPE(0x3153464a, "jfs") \
179FS_TYPE(0x58465342, "xfs") \
180FS_TYPE(0xF995E849, "hpfs") \
181FS_TYPE(0x9660, "isofs") \
182FS_TYPE(0x4000, "isofs") \
183FS_TYPE(0x4004, "isofs") \
184FS_TYPE(0x137F, "minix") \
185FS_TYPE(0x138F, "minix (30 char.)") \
186FS_TYPE(0x2468, "minix v2") \
187FS_TYPE(0x2478, "minix v2 (30 char.)") \
188FS_TYPE(0x4d44, "msdos") \
189FS_TYPE(0x4006, "fat") \
190FS_TYPE(0x564c, "novell") \
191FS_TYPE(0x6969, "nfs") \
192FS_TYPE(0x9fa0, "proc") \
193FS_TYPE(0x517B, "smb") \
194FS_TYPE(0x012FF7B4, "xenix") \
195FS_TYPE(0x012FF7B5, "sysv4") \
196FS_TYPE(0x012FF7B6, "sysv2") \
197FS_TYPE(0x012FF7B7, "coh") \
198FS_TYPE(0x00011954, "ufs") \
199FS_TYPE(0x012FD16D, "xia") \
200FS_TYPE(0x5346544e, "ntfs") \
201FS_TYPE(0x1021994, "tmpfs") \
202FS_TYPE(0x52654973, "reiserfs") \
203FS_TYPE(0x28cd3d45, "cramfs") \
204FS_TYPE(0x7275, "romfs") \
205FS_TYPE(0x858458f6, "ramfs") \
206FS_TYPE(0x73717368, "squashfs") \
207FS_TYPE(0x62656572, "sysfs")
208
209
210
211
212
213static const char *human_fstype(uint32_t f_type)
214{
215# define FS_TYPE(type, name) type,
216 static const uint32_t fstype[] = {
217 FS_TYPE_LIST
218 };
219# undef FS_TYPE
220# define FS_TYPE(type, name) name"\0"
221 static const char humanname[] ALIGN1 =
222 FS_TYPE_LIST
223 "UNKNOWN";
224# undef FS_TYPE
225 int i;
226
227 for (i = 0; i < ARRAY_SIZE(fstype); ++i)
228 if (fstype[i] == f_type)
229 break;
230 return nth_string(humanname, i);
231}
232
233
234
235static unsigned long long get_f_fsid(const struct statfs *statfsbuf)
236{
237 const unsigned *p = (const void*) &statfsbuf->f_fsid;
238 unsigned sz = sizeof(statfsbuf->f_fsid) / sizeof(unsigned);
239 unsigned long long r = 0;
240
241 do
242 r = (r << (sizeof(unsigned)*8)) | *p++;
243 while (--sz > 0);
244 return r;
245}
246#endif
247
248#if ENABLE_FEATURE_STAT_FORMAT
249static void strcatc(char *str, char c)
250{
251 int len = strlen(str);
252 str[len++] = c;
253 str[len] = '\0';
254}
255
256static void printfs(char *pformat, const char *msg)
257{
258 strcatc(pformat, 's');
259 printf(pformat, msg);
260}
261
262#if ENABLE_FEATURE_STAT_FILESYSTEM
263
264static void FAST_FUNC print_statfs(char *pformat, const char m,
265 const char *const filename, const void *data
266 IF_SELINUX(, security_context_t scontext))
267{
268 const struct statfs *statfsbuf = data;
269 if (m == 'n') {
270 printfs(pformat, filename);
271 } else if (m == 'i') {
272 strcat(pformat, "llx");
273 printf(pformat, get_f_fsid(statfsbuf));
274 } else if (m == 'l') {
275 strcat(pformat, "lu");
276 printf(pformat, (unsigned long) statfsbuf->f_namelen);
277 } else if (m == 't') {
278 strcat(pformat, "lx");
279 printf(pformat, (unsigned long) statfsbuf->f_type);
280 } else if (m == 'T') {
281 printfs(pformat, human_fstype(statfsbuf->f_type));
282 } else if (m == 'b') {
283 strcat(pformat, "llu");
284 printf(pformat, (unsigned long long) statfsbuf->f_blocks);
285 } else if (m == 'f') {
286 strcat(pformat, "llu");
287 printf(pformat, (unsigned long long) statfsbuf->f_bfree);
288 } else if (m == 'a') {
289 strcat(pformat, "llu");
290 printf(pformat, (unsigned long long) statfsbuf->f_bavail);
291 } else if (m == 's' || m == 'S') {
292 strcat(pformat, "lu");
293 printf(pformat, (unsigned long) statfsbuf->f_bsize);
294 } else if (m == 'c') {
295 strcat(pformat, "llu");
296 printf(pformat, (unsigned long long) statfsbuf->f_files);
297 } else if (m == 'd') {
298 strcat(pformat, "llu");
299 printf(pformat, (unsigned long long) statfsbuf->f_ffree);
300# if ENABLE_SELINUX
301 } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
302 printfs(pformat, scontext);
303# endif
304 } else {
305 strcatc(pformat, 'c');
306 printf(pformat, m);
307 }
308}
309#endif
310
311
312static void FAST_FUNC print_stat(char *pformat, const char m,
313 const char *const filename, const void *data
314 IF_SELINUX(, security_context_t scontext))
315{
316#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
317 struct stat *statbuf = (struct stat *) data;
318 struct passwd *pw_ent;
319 struct group *gw_ent;
320
321 if (m == 'n') {
322 printfs(pformat, filename);
323 } else if (m == 'N') {
324 strcatc(pformat, 's');
325 if (S_ISLNK(statbuf->st_mode)) {
326 char *linkname = xmalloc_readlink_or_warn(filename);
327 if (linkname == NULL)
328 return;
329 printf("'%s' -> '%s'", filename, linkname);
330 free(linkname);
331 } else {
332 printf(pformat, filename);
333 }
334 } else if (m == 'd') {
335 strcat(pformat, "llu");
336 printf(pformat, (unsigned long long) statbuf->st_dev);
337 } else if (m == 'D') {
338 strcat(pformat, "llx");
339 printf(pformat, (unsigned long long) statbuf->st_dev);
340 } else if (m == 'i') {
341 strcat(pformat, "llu");
342 printf(pformat, (unsigned long long) statbuf->st_ino);
343 } else if (m == 'a') {
344 strcat(pformat, "lo");
345 printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)));
346 } else if (m == 'A') {
347 printfs(pformat, bb_mode_string(statbuf->st_mode));
348 } else if (m == 'f') {
349 strcat(pformat, "lx");
350 printf(pformat, (unsigned long) statbuf->st_mode);
351 } else if (m == 'F') {
352 printfs(pformat, file_type(statbuf));
353 } else if (m == 'h') {
354 strcat(pformat, "lu");
355 printf(pformat, (unsigned long) statbuf->st_nlink);
356 } else if (m == 'u') {
357 strcat(pformat, "lu");
358 printf(pformat, (unsigned long) statbuf->st_uid);
359 } else if (m == 'U') {
360 pw_ent = getpwuid(statbuf->st_uid);
361 printfs(pformat, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN");
362 } else if (m == 'g') {
363 strcat(pformat, "lu");
364 printf(pformat, (unsigned long) statbuf->st_gid);
365 } else if (m == 'G') {
366 gw_ent = getgrgid(statbuf->st_gid);
367 printfs(pformat, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN");
368 } else if (m == 't') {
369 strcat(pformat, "lx");
370 printf(pformat, (unsigned long) major(statbuf->st_rdev));
371 } else if (m == 'T') {
372 strcat(pformat, "lx");
373 printf(pformat, (unsigned long) minor(statbuf->st_rdev));
374 } else if (m == 's') {
375 strcat(pformat, "llu");
376 printf(pformat, (unsigned long long) statbuf->st_size);
377 } else if (m == 'B') {
378 strcat(pformat, "lu");
379 printf(pformat, (unsigned long) 512);
380 } else if (m == 'b') {
381 strcat(pformat, "llu");
382 printf(pformat, (unsigned long long) statbuf->st_blocks);
383 } else if (m == 'o') {
384 strcat(pformat, "lu");
385 printf(pformat, (unsigned long) statbuf->st_blksize);
386 } else if (m == 'x') {
387 printfs(pformat, human_time(statbuf->st_atime));
388 } else if (m == 'X') {
389 strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
390
391
392 printf(pformat, (long) statbuf->st_atime);
393 } else if (m == 'y') {
394 printfs(pformat, human_time(statbuf->st_mtime));
395 } else if (m == 'Y') {
396 strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
397 printf(pformat, (long) statbuf->st_mtime);
398 } else if (m == 'z') {
399 printfs(pformat, human_time(statbuf->st_ctime));
400 } else if (m == 'Z') {
401 strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
402 printf(pformat, (long) statbuf->st_ctime);
403# if ENABLE_SELINUX
404 } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
405 printfs(pformat, scontext);
406# endif
407 } else {
408 strcatc(pformat, 'c');
409 printf(pformat, m);
410 }
411}
412
413static void print_it(const char *masterformat,
414 const char *filename,
415 void FAST_FUNC (*print_func)(char*, char, const char*, const void* IF_SELINUX(, security_context_t scontext)),
416 const void *data
417 IF_SELINUX(, security_context_t scontext))
418{
419
420 char *format = xstrdup(masterformat);
421
422
423 char *dest = xmalloc(strlen(format) + 2 + 1);
424 char *b;
425
426 b = format;
427 while (b) {
428
429
430
431 size_t len;
432 char *p = strchr(b, '%');
433 if (!p) {
434
435
436 puts(b);
437 break;
438 }
439
440
441 len = 1 + strspn(p + 1, "#-+.I 0123456789");
442 memcpy(dest, p, len);
443 dest[len] = '\0';
444
445
446 *p = '\0';
447 fputs(b, stdout);
448
449 p += len;
450 b = p + 1;
451 switch (*p) {
452 case '\0':
453 b = NULL;
454
455 case '%':
456 bb_putchar('%');
457 break;
458 default:
459
460 print_func(dest, *p, filename, data IF_SELINUX(,scontext));
461 break;
462 }
463 }
464
465 free(format);
466 free(dest);
467}
468#endif
469
470#if ENABLE_FEATURE_STAT_FILESYSTEM
471
472#if !ENABLE_FEATURE_STAT_FORMAT
473#define do_statfs(filename, format) do_statfs(filename)
474#endif
475static bool do_statfs(const char *filename, const char *format)
476{
477 struct statfs statfsbuf;
478#if !ENABLE_FEATURE_STAT_FORMAT
479 const char *format;
480#endif
481#if ENABLE_SELINUX
482 security_context_t scontext = NULL;
483
484 if (option_mask32 & OPT_SELINUX) {
485 if ((option_mask32 & OPT_DEREFERENCE
486 ? lgetfilecon(filename, &scontext)
487 : getfilecon(filename, &scontext)
488 ) < 0
489 ) {
490 bb_simple_perror_msg(filename);
491 return 0;
492 }
493 }
494#endif
495 if (statfs(filename, &statfsbuf) != 0) {
496 bb_perror_msg("can't read file system information for '%s'", filename);
497 return 0;
498 }
499
500#if ENABLE_FEATURE_STAT_FORMAT
501 if (format == NULL) {
502# if !ENABLE_SELINUX
503 format = (option_mask32 & OPT_TERSE
504 ? "%n %i %l %t %s %b %f %a %c %d\n"
505 : " File: \"%n\"\n"
506 " ID: %-8i Namelen: %-7l Type: %T\n"
507 "Block size: %-10s\n"
508 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
509 "Inodes: Total: %-10c Free: %d");
510# else
511 format = (option_mask32 & OPT_TERSE
512 ? (option_mask32 & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n":
513 "%n %i %l %t %s %b %f %a %c %d\n")
514 : (option_mask32 & OPT_SELINUX ?
515 " File: \"%n\"\n"
516 " ID: %-8i Namelen: %-7l Type: %T\n"
517 "Block size: %-10s\n"
518 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
519 "Inodes: Total: %-10c Free: %d"
520 " S_context: %C\n":
521 " File: \"%n\"\n"
522 " ID: %-8i Namelen: %-7l Type: %T\n"
523 "Block size: %-10s\n"
524 "Blocks: Total: %-10b Free: %-10f Available: %a\n"
525 "Inodes: Total: %-10c Free: %d\n")
526 );
527# endif
528 }
529 print_it(format, filename, print_statfs, &statfsbuf IF_SELINUX(, scontext));
530#else
531 format = (option_mask32 & OPT_TERSE
532 ? "%s %llx %lu "
533 : " File: \"%s\"\n"
534 " ID: %-8llx Namelen: %-7lu ");
535 printf(format,
536 filename,
537 get_f_fsid(&statfsbuf),
538 statfsbuf.f_namelen);
539
540 if (option_mask32 & OPT_TERSE)
541 printf("%lx ", (unsigned long) statfsbuf.f_type);
542 else
543 printf("Type: %s\n", human_fstype(statfsbuf.f_type));
544
545# if !ENABLE_SELINUX
546 format = (option_mask32 & OPT_TERSE
547 ? "%lu %llu %llu %llu %llu %llu\n"
548 : "Block size: %-10lu\n"
549 "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
550 "Inodes: Total: %-10llu Free: %llu\n");
551 printf(format,
552 (unsigned long) statfsbuf.f_bsize,
553 (unsigned long long) statfsbuf.f_blocks,
554 (unsigned long long) statfsbuf.f_bfree,
555 (unsigned long long) statfsbuf.f_bavail,
556 (unsigned long long) statfsbuf.f_files,
557 (unsigned long long) statfsbuf.f_ffree);
558# else
559 format = (option_mask32 & OPT_TERSE
560 ? (option_mask32 & OPT_SELINUX ? "%lu %llu %llu %llu %llu %llu %C\n" : "%lu %llu %llu %llu %llu %llu\n")
561 : (option_mask32 & OPT_SELINUX
562 ? "Block size: %-10lu\n"
563 "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
564 "Inodes: Total: %-10llu Free: %llu"
565 "S_context: %C\n"
566 : "Block size: %-10lu\n"
567 "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
568 "Inodes: Total: %-10llu Free: %llu\n"
569 )
570 );
571 printf(format,
572 (unsigned long) statfsbuf.f_bsize,
573 (unsigned long long) statfsbuf.f_blocks,
574 (unsigned long long) statfsbuf.f_bfree,
575 (unsigned long long) statfsbuf.f_bavail,
576 (unsigned long long) statfsbuf.f_files,
577 (unsigned long long) statfsbuf.f_ffree,
578 scontext);
579
580 if (scontext)
581 freecon(scontext);
582# endif
583#endif
584 return 1;
585}
586#endif
587
588
589#if !ENABLE_FEATURE_STAT_FORMAT
590#define do_stat(filename, format) do_stat(filename)
591#endif
592static bool do_stat(const char *filename, const char *format)
593{
594 struct stat statbuf;
595#if ENABLE_SELINUX
596 security_context_t scontext = NULL;
597
598 if (option_mask32 & OPT_SELINUX) {
599 if ((option_mask32 & OPT_DEREFERENCE
600 ? lgetfilecon(filename, &scontext)
601 : getfilecon(filename, &scontext)
602 ) < 0
603 ) {
604 bb_simple_perror_msg(filename);
605 return 0;
606 }
607 }
608#endif
609 if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) {
610 bb_perror_msg("can't stat '%s'", filename);
611 return 0;
612 }
613
614#if ENABLE_FEATURE_STAT_FORMAT
615 if (format == NULL) {
616# if !ENABLE_SELINUX
617 if (option_mask32 & OPT_TERSE) {
618 format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
619 } else {
620 if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
621 format =
622 " File: %N\n"
623 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
624 "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
625 " Device type: %t,%T\n"
626 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
627 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
628 } else {
629 format =
630 " File: %N\n"
631 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
632 "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
633 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
634 "Access: %x\n" "Modify: %y\n" "Change: %z\n";
635 }
636 }
637# else
638 if (option_mask32 & OPT_TERSE) {
639 format = (option_mask32 & OPT_SELINUX ?
640 "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n"
641 :
642 "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n"
643 );
644 } else {
645 if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
646 format = (option_mask32 & OPT_SELINUX ?
647 " File: %N\n"
648 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
649 "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
650 " Device type: %t,%T\n"
651 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
652 " S_Context: %C\n"
653 "Access: %x\n" "Modify: %y\n" "Change: %z\n"
654 :
655 " File: %N\n"
656 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
657 "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
658 " Device type: %t,%T\n"
659 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
660 "Access: %x\n" "Modify: %y\n" "Change: %z\n"
661 );
662 } else {
663 format = (option_mask32 & OPT_SELINUX ?
664 " File: %N\n"
665 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
666 "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
667 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
668 "S_Context: %C\n"
669 "Access: %x\n" "Modify: %y\n" "Change: %z\n"
670 :
671 " File: %N\n"
672 " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
673 "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
674 "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
675 "Access: %x\n" "Modify: %y\n" "Change: %z\n"
676 );
677 }
678 }
679# endif
680 }
681 print_it(format, filename, print_stat, &statbuf IF_SELINUX(, scontext));
682#else
683 if (option_mask32 & OPT_TERSE) {
684 printf("%s %llu %llu %lx %lu %lu %llx %llu %lu %lx %lx %lu %lu %lu %lu"
685 IF_NOT_SELINUX("\n"),
686 filename,
687 (unsigned long long) statbuf.st_size,
688 (unsigned long long) statbuf.st_blocks,
689 (unsigned long) statbuf.st_mode,
690 (unsigned long) statbuf.st_uid,
691 (unsigned long) statbuf.st_gid,
692 (unsigned long long) statbuf.st_dev,
693 (unsigned long long) statbuf.st_ino,
694 (unsigned long) statbuf.st_nlink,
695 (unsigned long) major(statbuf.st_rdev),
696 (unsigned long) minor(statbuf.st_rdev),
697 (unsigned long) statbuf.st_atime,
698 (unsigned long) statbuf.st_mtime,
699 (unsigned long) statbuf.st_ctime,
700 (unsigned long) statbuf.st_blksize
701 );
702# if ENABLE_SELINUX
703 if (option_mask32 & OPT_SELINUX)
704 printf(" %s\n", scontext);
705 else
706 bb_putchar('\n');
707# endif
708 } else {
709 char *linkname = NULL;
710 struct passwd *pw_ent;
711 struct group *gw_ent;
712
713 gw_ent = getgrgid(statbuf.st_gid);
714 pw_ent = getpwuid(statbuf.st_uid);
715
716 if (S_ISLNK(statbuf.st_mode))
717 linkname = xmalloc_readlink_or_warn(filename);
718 if (linkname) {
719 printf(" File: '%s' -> '%s'\n", filename, linkname);
720 free(linkname);
721 } else {
722 printf(" File: '%s'\n", filename);
723 }
724
725 printf(" Size: %-10llu\tBlocks: %-10llu IO Block: %-6lu %s\n"
726 "Device: %llxh/%llud\tInode: %-10llu Links: %-5lu",
727 (unsigned long long) statbuf.st_size,
728 (unsigned long long) statbuf.st_blocks,
729 (unsigned long) statbuf.st_blksize,
730 file_type(&statbuf),
731 (unsigned long long) statbuf.st_dev,
732 (unsigned long long) statbuf.st_dev,
733 (unsigned long long) statbuf.st_ino,
734 (unsigned long) statbuf.st_nlink);
735 if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode))
736 printf(" Device type: %lx,%lx\n",
737 (unsigned long) major(statbuf.st_rdev),
738 (unsigned long) minor(statbuf.st_rdev));
739 else
740 bb_putchar('\n');
741 printf("Access: (%04lo/%10.10s) Uid: (%5lu/%8s) Gid: (%5lu/%8s)\n",
742 (unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)),
743 bb_mode_string(statbuf.st_mode),
744 (unsigned long) statbuf.st_uid,
745 (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN",
746 (unsigned long) statbuf.st_gid,
747 (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN");
748# if ENABLE_SELINUX
749 if (option_mask32 & OPT_SELINUX)
750 printf(" S_Context: %s\n", scontext);
751# endif
752 printf("Access: %s\n", human_time(statbuf.st_atime));
753 printf("Modify: %s\n", human_time(statbuf.st_mtime));
754 printf("Change: %s\n", human_time(statbuf.st_ctime));
755 }
756#endif
757 return 1;
758}
759
760int stat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
761int stat_main(int argc UNUSED_PARAM, char **argv)
762{
763 IF_FEATURE_STAT_FORMAT(char *format = NULL;)
764 int i;
765 int ok;
766 statfunc_ptr statfunc = do_stat;
767#if ENABLE_FEATURE_STAT_FILESYSTEM || ENABLE_SELINUX
768 unsigned opts;
769
770 opts =
771#endif
772 getopt32(argv, "^"
773 "tL"
774 IF_FEATURE_STAT_FILESYSTEM("f")
775 IF_SELINUX("Z")
776 IF_FEATURE_STAT_FORMAT("c:")
777 "\0" "-1"
778 IF_FEATURE_STAT_FORMAT(,&format)
779 );
780#if ENABLE_FEATURE_STAT_FILESYSTEM
781 if (opts & OPT_FILESYS)
782 statfunc = do_statfs;
783#endif
784#if ENABLE_SELINUX
785 if (opts & OPT_SELINUX) {
786 selinux_or_die();
787 }
788#endif
789 ok = 1;
790 argv += optind;
791 for (i = 0; argv[i]; ++i)
792 ok &= statfunc(argv[i] IF_FEATURE_STAT_FORMAT(, format));
793
794 return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
795}
796