5 |
* Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation. |
* Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation. |
6 |
* Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org> |
* Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org> |
7 |
* Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org> |
* 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 |
* Written by Michael Meskes |
11 |
* Taken from coreutils and turned into a busybox applet by Mike Frysinger |
* Taken from coreutils and turned into a busybox applet by Mike Frysinger |
13 |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
14 |
*/ |
*/ |
15 |
|
|
16 |
#include "busybox.h" |
#include "libbb.h" |
17 |
|
|
18 |
/* vars to control behavior */ |
/* vars to control behavior */ |
19 |
#define OPT_TERSE 2 |
#define OPT_FILESYS (1 << 0) |
20 |
#define OPT_DEREFERENCE 4 |
#define OPT_TERSE (1 << 1) |
21 |
static long flags; |
#define OPT_DEREFERENCE (1 << 2) |
22 |
|
#define OPT_SELINUX (1 << 3) |
23 |
|
|
24 |
static char const *file_type(struct stat const *st) |
#if ENABLE_FEATURE_STAT_FORMAT |
25 |
|
typedef bool (*statfunc_ptr)(const char *, const char *); |
26 |
|
#else |
27 |
|
typedef bool (*statfunc_ptr)(const char *); |
28 |
|
#endif |
29 |
|
|
30 |
|
static const char *file_type(const struct stat *st) |
31 |
{ |
{ |
32 |
/* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107 |
/* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107 |
33 |
* for some of these formats. |
* for some of these formats. |
50 |
return "weird file"; |
return "weird file"; |
51 |
} |
} |
52 |
|
|
53 |
static char const *human_time(time_t t) |
static const char *human_time(time_t t) |
54 |
{ |
{ |
55 |
/* Old |
/* Old |
56 |
static char *str; |
static char *str; |
59 |
return str; |
return str; |
60 |
*/ |
*/ |
61 |
/* coreutils 6.3 compat: */ |
/* coreutils 6.3 compat: */ |
62 |
static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")]; |
|
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)); |
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.000000000", localtime(&t)); |
67 |
return buf; |
return buf; |
68 |
|
#undef buf |
69 |
} |
} |
70 |
|
|
71 |
/* Return the type of the specified file system. |
/* Return the type of the specified file system. |
73 |
* Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2) |
* Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2) |
74 |
* Still others have neither and have to get by with f_type (Linux). |
* Still others have neither and have to get by with f_type (Linux). |
75 |
*/ |
*/ |
76 |
static char const *human_fstype(long f_type) |
static const char *human_fstype(uint32_t f_type) |
77 |
{ |
{ |
|
int i; |
|
78 |
static const struct types { |
static const struct types { |
79 |
long type; |
uint32_t type; |
80 |
const char *fs; |
const char *const fs; |
81 |
} humantypes[] = { |
} humantypes[] = { |
82 |
{ 0xADFF, "affs" }, |
{ 0xADFF, "affs" }, |
83 |
{ 0x1Cd1, "devpts" }, |
{ 0x1Cd1, "devpts" }, |
116 |
{ 0x62656572, "sysfs" }, |
{ 0x62656572, "sysfs" }, |
117 |
{ 0, "UNKNOWN" } |
{ 0, "UNKNOWN" } |
118 |
}; |
}; |
119 |
for (i=0; humantypes[i].type; ++i) |
|
120 |
|
int i; |
121 |
|
|
122 |
|
for (i = 0; humantypes[i].type; ++i) |
123 |
if (humantypes[i].type == f_type) |
if (humantypes[i].type == f_type) |
124 |
break; |
break; |
125 |
return humantypes[i].fs; |
return humantypes[i].fs; |
126 |
} |
} |
127 |
|
|
128 |
#ifdef CONFIG_FEATURE_STAT_FORMAT |
#if ENABLE_FEATURE_STAT_FORMAT |
129 |
/* print statfs info */ |
static void strcatc(char *str, char c) |
|
static void print_statfs(char *pformat, size_t buf_len, char m, |
|
|
char const *filename, void const *data) |
|
130 |
{ |
{ |
131 |
struct statfs const *statfsbuf = data; |
int len = strlen(str); |
132 |
|
str[len++] = c; |
133 |
|
str[len] = '\0'; |
134 |
|
} |
135 |
|
|
136 |
switch (m) { |
static void printfs(char *pformat, const char *msg) |
137 |
case 'n': |
{ |
138 |
strncat(pformat, "s", buf_len); |
strcatc(pformat, 's'); |
139 |
printf(pformat, filename); |
printf(pformat, msg); |
140 |
break; |
} |
141 |
case 'i': |
|
142 |
strncat(pformat, "Lx", buf_len); |
/* print statfs info */ |
143 |
|
static 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); |
printf(pformat, statfsbuf->f_fsid); |
153 |
break; |
} else if (m == 'l') { |
154 |
case 'l': |
strcat(pformat, "lu"); |
|
strncat(pformat, "lu", buf_len); |
|
155 |
printf(pformat, statfsbuf->f_namelen); |
printf(pformat, statfsbuf->f_namelen); |
156 |
break; |
} else if (m == 't') { |
157 |
case 't': |
strcat(pformat, "lx"); |
158 |
strncat(pformat, "lx", buf_len); |
printf(pformat, (unsigned long) (statfsbuf->f_type)); /* no equiv */ |
159 |
printf(pformat, (unsigned long int) (statfsbuf->f_type)); /* no equiv. */ |
} else if (m == 'T') { |
160 |
break; |
printfs(pformat, human_fstype(statfsbuf->f_type)); |
161 |
case 'T': |
} else if (m == 'b') { |
162 |
strncat(pformat, "s", buf_len); |
strcat(pformat, "jd"); |
|
printf(pformat, human_fstype(statfsbuf->f_type)); |
|
|
break; |
|
|
case 'b': |
|
|
strncat(pformat, "jd", buf_len); |
|
163 |
printf(pformat, (intmax_t) (statfsbuf->f_blocks)); |
printf(pformat, (intmax_t) (statfsbuf->f_blocks)); |
164 |
break; |
} else if (m == 'f') { |
165 |
case 'f': |
strcat(pformat, "jd"); |
|
strncat(pformat, "jd", buf_len); |
|
166 |
printf(pformat, (intmax_t) (statfsbuf->f_bfree)); |
printf(pformat, (intmax_t) (statfsbuf->f_bfree)); |
167 |
break; |
} else if (m == 'a') { |
168 |
case 'a': |
strcat(pformat, "jd"); |
|
strncat(pformat, "jd", buf_len); |
|
169 |
printf(pformat, (intmax_t) (statfsbuf->f_bavail)); |
printf(pformat, (intmax_t) (statfsbuf->f_bavail)); |
170 |
break; |
} else if (m == 's' || m == 'S') { |
171 |
case 'S': |
strcat(pformat, "lu"); |
172 |
case 's': |
printf(pformat, (unsigned long) (statfsbuf->f_bsize)); |
173 |
strncat(pformat, "lu", buf_len); |
} else if (m == 'c') { |
174 |
printf(pformat, (unsigned long int) (statfsbuf->f_bsize)); |
strcat(pformat, "jd"); |
|
break; |
|
|
case 'c': |
|
|
strncat(pformat, "jd", buf_len); |
|
175 |
printf(pformat, (intmax_t) (statfsbuf->f_files)); |
printf(pformat, (intmax_t) (statfsbuf->f_files)); |
176 |
break; |
} else if (m == 'd') { |
177 |
case 'd': |
strcat(pformat, "jd"); |
|
strncat(pformat, "jd", buf_len); |
|
178 |
printf(pformat, (intmax_t) (statfsbuf->f_ffree)); |
printf(pformat, (intmax_t) (statfsbuf->f_ffree)); |
179 |
break; |
#if ENABLE_SELINUX |
180 |
default: |
} else if (m == 'C' && (option_mask32 & OPT_SELINUX)) { |
181 |
strncat(pformat, "c", buf_len); |
printfs(pformat, scontext); |
182 |
|
#endif |
183 |
|
} else { |
184 |
|
strcatc(pformat, 'c'); |
185 |
printf(pformat, m); |
printf(pformat, m); |
|
break; |
|
186 |
} |
} |
187 |
} |
} |
188 |
|
|
189 |
/* print stat info */ |
/* print stat info */ |
190 |
static void print_stat(char *pformat, size_t buf_len, char m, |
static void print_stat(char *pformat, const char m, |
191 |
char const *filename, void const *data) |
const char *const filename, const void *data |
192 |
|
USE_SELINUX(, security_context_t scontext)) |
193 |
{ |
{ |
194 |
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) |
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) |
195 |
struct stat *statbuf = (struct stat *) data; |
struct stat *statbuf = (struct stat *) data; |
196 |
struct passwd *pw_ent; |
struct passwd *pw_ent; |
197 |
struct group *gw_ent; |
struct group *gw_ent; |
198 |
|
|
199 |
switch (m) { |
if (m == 'n') { |
200 |
case 'n': |
printfs(pformat, filename); |
201 |
strncat(pformat, "s", buf_len); |
} else if (m == 'N') { |
202 |
printf(pformat, filename); |
strcatc(pformat, 's'); |
|
break; |
|
|
case 'N': |
|
|
strncat(pformat, "s", buf_len); |
|
203 |
if (S_ISLNK(statbuf->st_mode)) { |
if (S_ISLNK(statbuf->st_mode)) { |
204 |
char *linkname = xreadlink(filename); |
char *linkname = xmalloc_readlink_or_warn(filename); |
205 |
if (linkname == NULL) { |
if (linkname == NULL) |
|
bb_perror_msg("cannot read symbolic link '%s'", filename); |
|
206 |
return; |
return; |
|
} |
|
207 |
/*printf("\"%s\" -> \"%s\"", filename, linkname); */ |
/*printf("\"%s\" -> \"%s\"", filename, linkname); */ |
208 |
printf(pformat, filename); |
printf(pformat, filename); |
209 |
printf(" -> "); |
printf(" -> "); |
210 |
printf(pformat, linkname); |
printf(pformat, linkname); |
211 |
|
free(linkname); |
212 |
} else { |
} else { |
213 |
printf(pformat, filename); |
printf(pformat, filename); |
214 |
} |
} |
215 |
break; |
} else if (m == 'd') { |
216 |
case 'd': |
strcat(pformat, "ju"); |
|
strncat(pformat, "ju", buf_len); |
|
217 |
printf(pformat, (uintmax_t) statbuf->st_dev); |
printf(pformat, (uintmax_t) statbuf->st_dev); |
218 |
break; |
} else if (m == 'D') { |
219 |
case 'D': |
strcat(pformat, "jx"); |
|
strncat(pformat, "jx", buf_len); |
|
220 |
printf(pformat, (uintmax_t) statbuf->st_dev); |
printf(pformat, (uintmax_t) statbuf->st_dev); |
221 |
break; |
} else if (m == 'i') { |
222 |
case 'i': |
strcat(pformat, "ju"); |
|
strncat(pformat, "ju", buf_len); |
|
223 |
printf(pformat, (uintmax_t) statbuf->st_ino); |
printf(pformat, (uintmax_t) statbuf->st_ino); |
224 |
break; |
} else if (m == 'a') { |
225 |
case 'a': |
strcat(pformat, "lo"); |
226 |
strncat(pformat, "lo", buf_len); |
printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO))); |
227 |
printf(pformat, (unsigned long int) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO))); |
} else if (m == 'A') { |
228 |
break; |
printfs(pformat, bb_mode_string(statbuf->st_mode)); |
229 |
case 'A': |
} else if (m == 'f') { |
230 |
strncat(pformat, "s", buf_len); |
strcat(pformat, "lx"); |
231 |
printf(pformat, bb_mode_string(statbuf->st_mode)); |
printf(pformat, (unsigned long) statbuf->st_mode); |
232 |
break; |
} else if (m == 'F') { |
233 |
case 'f': |
printfs(pformat, file_type(statbuf)); |
234 |
strncat(pformat, "lx", buf_len); |
} else if (m == 'h') { |
235 |
printf(pformat, (unsigned long int) statbuf->st_mode); |
strcat(pformat, "lu"); |
236 |
break; |
printf(pformat, (unsigned long) statbuf->st_nlink); |
237 |
case 'F': |
} else if (m == 'u') { |
238 |
strncat(pformat, "s", buf_len); |
strcat(pformat, "lu"); |
239 |
printf(pformat, file_type(statbuf)); |
printf(pformat, (unsigned long) statbuf->st_uid); |
240 |
break; |
} else if (m == 'U') { |
|
case 'h': |
|
|
strncat(pformat, "lu", buf_len); |
|
|
printf(pformat, (unsigned long int) statbuf->st_nlink); |
|
|
break; |
|
|
case 'u': |
|
|
strncat(pformat, "lu", buf_len); |
|
|
printf(pformat, (unsigned long int) statbuf->st_uid); |
|
|
break; |
|
|
case 'U': |
|
|
strncat(pformat, "s", buf_len); |
|
241 |
setpwent(); |
setpwent(); |
242 |
pw_ent = getpwuid(statbuf->st_uid); |
pw_ent = getpwuid(statbuf->st_uid); |
243 |
printf(pformat, (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN"); |
printfs(pformat, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN"); |
244 |
break; |
} else if (m == 'g') { |
245 |
case 'g': |
strcat(pformat, "lu"); |
246 |
strncat(pformat, "lu", buf_len); |
printf(pformat, (unsigned long) statbuf->st_gid); |
247 |
printf(pformat, (unsigned long int) statbuf->st_gid); |
} else if (m == 'G') { |
|
break; |
|
|
case 'G': |
|
|
strncat(pformat, "s", buf_len); |
|
248 |
setgrent(); |
setgrent(); |
249 |
gw_ent = getgrgid(statbuf->st_gid); |
gw_ent = getgrgid(statbuf->st_gid); |
250 |
printf(pformat, (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN"); |
printfs(pformat, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN"); |
251 |
break; |
} else if (m == 't') { |
252 |
case 't': |
strcat(pformat, "lx"); |
253 |
strncat(pformat, "lx", buf_len); |
printf(pformat, (unsigned long) major(statbuf->st_rdev)); |
254 |
printf(pformat, (unsigned long int) major(statbuf->st_rdev)); |
} else if (m == 'T') { |
255 |
break; |
strcat(pformat, "lx"); |
256 |
case 'T': |
printf(pformat, (unsigned long) minor(statbuf->st_rdev)); |
257 |
strncat(pformat, "lx", buf_len); |
} else if (m == 's') { |
258 |
printf(pformat, (unsigned long int) minor(statbuf->st_rdev)); |
strcat(pformat, "ju"); |
|
break; |
|
|
case 's': |
|
|
strncat(pformat, "ju", buf_len); |
|
259 |
printf(pformat, (uintmax_t) (statbuf->st_size)); |
printf(pformat, (uintmax_t) (statbuf->st_size)); |
260 |
break; |
} else if (m == 'B') { |
261 |
case 'B': |
strcat(pformat, "lu"); |
262 |
strncat(pformat, "lu", buf_len); |
printf(pformat, (unsigned long) 512); //ST_NBLOCKSIZE |
263 |
printf(pformat, (unsigned long int) 512); //ST_NBLOCKSIZE |
} else if (m == 'b') { |
264 |
break; |
strcat(pformat, "ju"); |
|
case 'b': |
|
|
strncat(pformat, "ju", buf_len); |
|
265 |
printf(pformat, (uintmax_t) statbuf->st_blocks); |
printf(pformat, (uintmax_t) statbuf->st_blocks); |
266 |
break; |
} else if (m == 'o') { |
267 |
case 'o': |
strcat(pformat, "lu"); |
268 |
strncat(pformat, "lu", buf_len); |
printf(pformat, (unsigned long) statbuf->st_blksize); |
269 |
printf(pformat, (unsigned long int) statbuf->st_blksize); |
} else if (m == 'x') { |
270 |
break; |
printfs(pformat, human_time(statbuf->st_atime)); |
271 |
case 'x': |
} else if (m == 'X') { |
272 |
strncat(pformat, "s", buf_len); |
strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); |
273 |
printf(pformat, human_time(statbuf->st_atime)); |
printf(pformat, (unsigned long) statbuf->st_atime); |
274 |
break; |
} else if (m == 'y') { |
275 |
case 'X': |
printfs(pformat, human_time(statbuf->st_mtime)); |
276 |
strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len); |
} else if (m == 'Y') { |
277 |
printf(pformat, (unsigned long int) statbuf->st_atime); |
strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); |
278 |
break; |
printf(pformat, (unsigned long) statbuf->st_mtime); |
279 |
case 'y': |
} else if (m == 'z') { |
280 |
strncat(pformat, "s", buf_len); |
printfs(pformat, human_time(statbuf->st_ctime)); |
281 |
printf(pformat, human_time(statbuf->st_mtime)); |
} else if (m == 'Z') { |
282 |
break; |
strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); |
283 |
case 'Y': |
printf(pformat, (unsigned long) statbuf->st_ctime); |
284 |
strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len); |
#if ENABLE_SELINUX |
285 |
printf(pformat, (unsigned long int) statbuf->st_mtime); |
} else if (m == 'C' && (option_mask32 & OPT_SELINUX)) { |
286 |
break; |
printfs(pformat, scontext); |
287 |
case 'z': |
#endif |
288 |
strncat(pformat, "s", buf_len); |
} else { |
289 |
printf(pformat, human_time(statbuf->st_ctime)); |
strcatc(pformat, 'c'); |
|
break; |
|
|
case 'Z': |
|
|
strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len); |
|
|
printf(pformat, (unsigned long int) statbuf->st_ctime); |
|
|
break; |
|
|
default: |
|
|
strncat(pformat, "c", buf_len); |
|
290 |
printf(pformat, m); |
printf(pformat, m); |
|
break; |
|
291 |
} |
} |
292 |
} |
} |
293 |
|
|
294 |
static void print_it(char const *masterformat, char const *filename, |
static void print_it(const char *masterformat, const char *filename, |
295 |
void (*print_func) (char *, size_t, char, char const *, void const *), |
void (*print_func) (char*, char, const char*, const void* USE_SELINUX(, security_context_t scontext)), |
296 |
void const *data) |
const void *data |
297 |
|
USE_SELINUX(, security_context_t scontext) ) |
298 |
{ |
{ |
299 |
char *b; |
/* Create a working copy of the format string */ |
|
|
|
|
/* create a working copy of the format string */ |
|
300 |
char *format = xstrdup(masterformat); |
char *format = xstrdup(masterformat); |
|
|
|
301 |
/* Add 2 to accomodate our conversion of the stat '%s' format string |
/* Add 2 to accomodate our conversion of the stat '%s' format string |
302 |
* to the printf '%llu' one. */ |
* to the printf '%llu' one. */ |
303 |
size_t n_alloc = strlen(format) + 2 + 1; |
char *dest = xmalloc(strlen(format) + 2 + 1); |
304 |
char *dest = xmalloc(n_alloc); |
char *b; |
305 |
|
|
306 |
b = format; |
b = format; |
307 |
while (b) { |
while (b) { |
308 |
size_t len; |
size_t len; |
309 |
char *p = strchr(b, '%'); |
char *p = strchr(b, '%'); |
310 |
if (!p) { |
if (!p) { |
311 |
/* coreutils 6.3 always print <cr> at the end */ |
/* coreutils 6.3 always prints <cr> at the end */ |
312 |
/*fputs(b, stdout);*/ |
/*fputs(b, stdout);*/ |
313 |
puts(b); |
puts(b); |
314 |
break; |
break; |
316 |
*p++ = '\0'; |
*p++ = '\0'; |
317 |
fputs(b, stdout); |
fputs(b, stdout); |
318 |
|
|
319 |
|
/* dest = "%<modifiers>" */ |
320 |
len = strspn(p, "#-+.I 0123456789"); |
len = strspn(p, "#-+.I 0123456789"); |
321 |
dest[0] = '%'; |
dest[0] = '%'; |
322 |
memcpy(dest + 1, p, len); |
memcpy(dest + 1, p, len); |
323 |
dest[1 + len] = 0; |
dest[1 + len] = '\0'; |
324 |
p += len; |
p += len; |
325 |
|
|
326 |
b = p + 1; |
b = p + 1; |
329 |
b = NULL; |
b = NULL; |
330 |
/* fall through */ |
/* fall through */ |
331 |
case '%': |
case '%': |
332 |
putchar('%'); |
bb_putchar('%'); |
333 |
break; |
break; |
334 |
default: |
default: |
335 |
print_func(dest, n_alloc, *p, filename, data); |
/* Completes "%<modifiers>" with specifier and printfs */ |
336 |
|
print_func(dest, *p, filename, data USE_SELINUX(,scontext)); |
337 |
break; |
break; |
338 |
} |
} |
339 |
} |
} |
344 |
#endif |
#endif |
345 |
|
|
346 |
/* Stat the file system and print what we find. */ |
/* Stat the file system and print what we find. */ |
347 |
static int do_statfs(char const *filename, char const *format) |
#if !ENABLE_FEATURE_STAT_FORMAT |
348 |
|
#define do_statfs(filename, format) do_statfs(filename) |
349 |
|
#endif |
350 |
|
static 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; |
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) { |
if (statfs(filename, &statfsbuf) != 0) { |
371 |
bb_perror_msg("cannot read file system information for '%s'", filename); |
bb_perror_msg("cannot read file system information for '%s'", filename); |
372 |
return 0; |
return 0; |
373 |
} |
} |
374 |
|
|
375 |
#ifdef CONFIG_FEATURE_STAT_FORMAT |
#if ENABLE_FEATURE_STAT_FORMAT |
376 |
if (format == NULL) |
if (format == NULL) { |
377 |
format = (flags & OPT_TERSE |
#if !ENABLE_SELINUX |
378 |
|
format = (option_mask32 & OPT_TERSE |
379 |
? "%n %i %l %t %s %b %f %a %c %d\n" |
? "%n %i %l %t %s %b %f %a %c %d\n" |
380 |
: " File: \"%n\"\n" |
: " File: \"%n\"\n" |
381 |
" ID: %-8i Namelen: %-7l Type: %T\n" |
" ID: %-8i Namelen: %-7l Type: %T\n" |
382 |
"Block size: %-10s\n" |
"Block size: %-10s\n" |
383 |
"Blocks: Total: %-10b Free: %-10f Available: %a\n" |
"Blocks: Total: %-10b Free: %-10f Available: %a\n" |
384 |
"Inodes: Total: %-10c Free: %d"); |
"Inodes: Total: %-10c Free: %d"); |
|
print_it(format, filename, print_statfs, &statfsbuf); |
|
385 |
#else |
#else |
386 |
|
format = (option_mask32 & OPT_TERSE |
387 |
format = (flags & OPT_TERSE |
? (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 " |
? "%s %llx %lu " |
408 |
: " File: \"%s\"\n" |
: " File: \"%s\"\n" |
409 |
" ID: %-8Lx Namelen: %-7lu "); |
" ID: %-8Lx Namelen: %-7lu "); |
412 |
statfsbuf.f_fsid, |
statfsbuf.f_fsid, |
413 |
statfsbuf.f_namelen); |
statfsbuf.f_namelen); |
414 |
|
|
415 |
if (flags & OPT_TERSE) |
if (option_mask32 & OPT_TERSE) |
416 |
printf("%lx ", (unsigned long int) (statfsbuf.f_type)); |
printf("%lx ", (unsigned long) (statfsbuf.f_type)); |
417 |
else |
else |
418 |
printf("Type: %s\n", human_fstype(statfsbuf.f_type)); |
printf("Type: %s\n", human_fstype(statfsbuf.f_type)); |
419 |
|
|
420 |
format = (flags & OPT_TERSE |
#if !ENABLE_SELINUX |
421 |
|
format = (option_mask32 & OPT_TERSE |
422 |
? "%lu %ld %ld %ld %ld %ld\n" |
? "%lu %ld %ld %ld %ld %ld\n" |
423 |
: "Block size: %-10lu\n" |
: "Block size: %-10lu\n" |
424 |
"Blocks: Total: %-10jd Free: %-10jd Available: %jd\n" |
"Blocks: Total: %-10jd Free: %-10jd Available: %jd\n" |
425 |
"Inodes: Total: %-10jd Free: %jd\n"); |
"Inodes: Total: %-10jd Free: %jd\n"); |
426 |
printf(format, |
printf(format, |
427 |
(unsigned long int) (statfsbuf.f_bsize), |
(unsigned long) (statfsbuf.f_bsize), |
428 |
(intmax_t) (statfsbuf.f_blocks), |
(intmax_t) (statfsbuf.f_blocks), |
429 |
(intmax_t) (statfsbuf.f_bfree), |
(intmax_t) (statfsbuf.f_bfree), |
430 |
(intmax_t) (statfsbuf.f_bavail), |
(intmax_t) (statfsbuf.f_bavail), |
431 |
(intmax_t) (statfsbuf.f_files), |
(intmax_t) (statfsbuf.f_files), |
432 |
(intmax_t) (statfsbuf.f_ffree)); |
(intmax_t) (statfsbuf.f_ffree)); |
433 |
#endif |
#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; |
return 1; |
459 |
} |
} |
460 |
|
|
461 |
/* stat the file and print what we find */ |
/* stat the file and print what we find */ |
462 |
static int do_stat(char const *filename, char const *format) |
#if !ENABLE_FEATURE_STAT_FORMAT |
463 |
|
#define do_stat(filename, format) do_stat(filename) |
464 |
|
#endif |
465 |
|
static bool do_stat(const char *filename, const char *format) |
466 |
{ |
{ |
467 |
struct stat statbuf; |
struct stat statbuf; |
468 |
|
#if ENABLE_SELINUX |
469 |
|
security_context_t scontext = NULL; |
470 |
|
|
471 |
if ((flags & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) { |
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); |
bb_perror_msg("cannot stat '%s'", filename); |
484 |
return 0; |
return 0; |
485 |
} |
} |
486 |
|
|
487 |
#ifdef CONFIG_FEATURE_STAT_FORMAT |
#if ENABLE_FEATURE_STAT_FORMAT |
488 |
if (format == NULL) { |
if (format == NULL) { |
489 |
if (flags & OPT_TERSE) { |
#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"; |
format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; |
492 |
} else { |
} else { |
493 |
if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { |
if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { |
507 |
"Access: %x\n" "Modify: %y\n" "Change: %z\n"; |
"Access: %x\n" "Modify: %y\n" "Change: %z\n"; |
508 |
} |
} |
509 |
} |
} |
|
} |
|
|
print_it(format, filename, print_stat, &statbuf); |
|
510 |
#else |
#else |
511 |
if (flags & OPT_TERSE) { |
if (option_mask32 & OPT_TERSE) { |
512 |
printf("%s %ju %ju %lx %lu %lu %jx %ju %lu %lx %lx %lu %lu %lu %lu\n", |
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, |
filename, |
554 |
(uintmax_t) (statbuf.st_size), |
(uintmax_t) (statbuf.st_size), |
555 |
(uintmax_t) statbuf.st_blocks, |
(uintmax_t) statbuf.st_blocks, |
556 |
(unsigned long int) statbuf.st_mode, |
(unsigned long) statbuf.st_mode, |
557 |
(unsigned long int) statbuf.st_uid, |
(unsigned long) statbuf.st_uid, |
558 |
(unsigned long int) statbuf.st_gid, |
(unsigned long) statbuf.st_gid, |
559 |
(uintmax_t) statbuf.st_dev, |
(uintmax_t) statbuf.st_dev, |
560 |
(uintmax_t) statbuf.st_ino, |
(uintmax_t) statbuf.st_ino, |
561 |
(unsigned long int) statbuf.st_nlink, |
(unsigned long) statbuf.st_nlink, |
562 |
(unsigned long int) major(statbuf.st_rdev), |
(unsigned long) major(statbuf.st_rdev), |
563 |
(unsigned long int) minor(statbuf.st_rdev), |
(unsigned long) minor(statbuf.st_rdev), |
564 |
(unsigned long int) statbuf.st_atime, |
(unsigned long) statbuf.st_atime, |
565 |
(unsigned long int) statbuf.st_mtime, |
(unsigned long) statbuf.st_mtime, |
566 |
(unsigned long int) statbuf.st_ctime, |
(unsigned long) statbuf.st_ctime, |
567 |
(unsigned long int) statbuf.st_blksize |
(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 { |
} else { |
576 |
char *linkname = NULL; |
char *linkname = NULL; |
577 |
|
|
583 |
pw_ent = getpwuid(statbuf.st_uid); |
pw_ent = getpwuid(statbuf.st_uid); |
584 |
|
|
585 |
if (S_ISLNK(statbuf.st_mode)) |
if (S_ISLNK(statbuf.st_mode)) |
586 |
linkname = xreadlink(filename); |
linkname = xmalloc_readlink_or_warn(filename); |
587 |
if (linkname) |
if (linkname) |
588 |
printf(" File: \"%s\" -> \"%s\"\n", filename, linkname); |
printf(" File: \"%s\" -> \"%s\"\n", filename, linkname); |
589 |
else |
else |
593 |
"Device: %jxh/%jud\tInode: %-10ju Links: %-5lu", |
"Device: %jxh/%jud\tInode: %-10ju Links: %-5lu", |
594 |
(uintmax_t) (statbuf.st_size), |
(uintmax_t) (statbuf.st_size), |
595 |
(uintmax_t) statbuf.st_blocks, |
(uintmax_t) statbuf.st_blocks, |
596 |
(unsigned long int) statbuf.st_blksize, |
(unsigned long) statbuf.st_blksize, |
597 |
file_type(&statbuf), |
file_type(&statbuf), |
598 |
(uintmax_t) statbuf.st_dev, |
(uintmax_t) statbuf.st_dev, |
599 |
(uintmax_t) statbuf.st_dev, |
(uintmax_t) statbuf.st_dev, |
600 |
(uintmax_t) statbuf.st_ino, |
(uintmax_t) statbuf.st_ino, |
601 |
(unsigned long int) statbuf.st_nlink); |
(unsigned long) statbuf.st_nlink); |
602 |
if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) |
if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) |
603 |
printf(" Device type: %lx,%lx\n", |
printf(" Device type: %lx,%lx\n", |
604 |
(unsigned long int) major(statbuf.st_rdev), |
(unsigned long) major(statbuf.st_rdev), |
605 |
(unsigned long int) minor(statbuf.st_rdev)); |
(unsigned long) minor(statbuf.st_rdev)); |
606 |
else |
else |
607 |
putchar('\n'); |
bb_putchar('\n'); |
608 |
printf("Access: (%04lo/%10.10s) Uid: (%5lu/%8s) Gid: (%5lu/%8s)\n" |
printf("Access: (%04lo/%10.10s) Uid: (%5lu/%8s) Gid: (%5lu/%8s)\n", |
609 |
"Access: %s\n" "Modify: %s\n" "Change: %s\n", |
(unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)), |
|
(unsigned long int) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)), |
|
610 |
bb_mode_string(statbuf.st_mode), |
bb_mode_string(statbuf.st_mode), |
611 |
(unsigned long int) statbuf.st_uid, |
(unsigned long) statbuf.st_uid, |
612 |
(pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN", |
(pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN", |
613 |
(unsigned long int) statbuf.st_gid, |
(unsigned long) statbuf.st_gid, |
614 |
(gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN", |
(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), |
human_time(statbuf.st_atime), |
620 |
human_time(statbuf.st_mtime), |
human_time(statbuf.st_mtime), |
621 |
human_time(statbuf.st_ctime)); |
human_time(statbuf.st_ctime)); |
622 |
} |
} |
623 |
#endif |
#endif /* FEATURE_STAT_FORMAT */ |
624 |
return 1; |
return 1; |
625 |
} |
} |
626 |
|
|
627 |
|
int stat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
628 |
int stat_main(int argc, char **argv) |
int stat_main(int argc, char **argv) |
629 |
{ |
{ |
630 |
|
USE_FEATURE_STAT_FORMAT(char *format = NULL;) |
631 |
int i; |
int i; |
|
char *format = NULL; |
|
632 |
int ok = 1; |
int ok = 1; |
633 |
int (*statfunc)(char const *, char const *) = do_stat; |
statfunc_ptr statfunc = do_stat; |
634 |
|
|
635 |
flags = getopt32(argc, argv, "ftL" |
getopt32(argv, "ftL" |
636 |
|
USE_SELINUX("Z") |
637 |
USE_FEATURE_STAT_FORMAT("c:", &format) |
USE_FEATURE_STAT_FORMAT("c:", &format) |
638 |
); |
); |
639 |
|
|
640 |
if (flags & 1) /* -f */ |
if (option_mask32 & OPT_FILESYS) /* -f */ |
641 |
statfunc = do_statfs; |
statfunc = do_statfs; |
642 |
if (argc == optind) /* files */ |
if (argc == optind) /* files */ |
643 |
bb_show_usage(); |
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) |
for (i = optind; i < argc; ++i) |
651 |
ok &= statfunc(argv[i], format); |
ok &= statfunc(argv[i] USE_FEATURE_STAT_FORMAT(, format)); |
652 |
|
|
653 |
return (ok ? EXIT_SUCCESS : EXIT_FAILURE); |
return (ok ? EXIT_SUCCESS : EXIT_FAILURE); |
654 |
} |
} |