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