9
#include <selinux/selinux.h>
19
#include <linux/kdev_t.h>
24
// bits for flags argument
25
#define LIST_LONG (1 << 0)
26
#define LIST_ALL (1 << 1)
27
#define LIST_RECURSIVE (1 << 2)
28
#define LIST_DIRECTORIES (1 << 3)
29
#define LIST_SIZE (1 << 4)
30
#define LIST_LONG_NUMERIC (1 << 5)
31
#define LIST_CLASSIFY (1 << 6)
32
#define LIST_MACLABEL (1 << 7)
35
static int listpath(const char *name, int flags);
37
static char mode2kind(unsigned mode)
39
switch(mode & S_IFMT){
40
case S_IFSOCK: return 's';
41
case S_IFLNK: return 'l';
42
case S_IFREG: return '-';
43
case S_IFDIR: return 'd';
44
case S_IFBLK: return 'b';
45
case S_IFCHR: return 'c';
46
case S_IFIFO: return 'p';
51
static void mode2str(unsigned mode, char *out)
53
*out++ = mode2kind(mode);
55
*out++ = (mode & 0400) ? 'r' : '-';
56
*out++ = (mode & 0200) ? 'w' : '-';
58
*out++ = (mode & 0100) ? 's' : 'S';
60
*out++ = (mode & 0100) ? 'x' : '-';
62
*out++ = (mode & 040) ? 'r' : '-';
63
*out++ = (mode & 020) ? 'w' : '-';
65
*out++ = (mode & 010) ? 's' : 'S';
67
*out++ = (mode & 010) ? 'x' : '-';
69
*out++ = (mode & 04) ? 'r' : '-';
70
*out++ = (mode & 02) ? 'w' : '-';
72
*out++ = (mode & 01) ? 't' : 'T';
74
*out++ = (mode & 01) ? 'x' : '-';
79
static void user2str(unsigned uid, char *out)
81
struct passwd *pw = getpwuid(uid);
83
strcpy(out, pw->pw_name);
85
sprintf(out, "%d", uid);
89
static void group2str(unsigned gid, char *out)
91
struct group *gr = getgrgid(gid);
93
strcpy(out, gr->gr_name);
95
sprintf(out, "%d", gid);
99
static int show_total_size(const char *dirname, DIR *d, int flags)
106
/* run through the directory and sum up the file block sizes */
107
while ((de = readdir(d)) != 0) {
108
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
110
if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
113
if (strcmp(dirname, "/") == 0)
114
snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
116
snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name);
118
if (lstat(tmp, &s) < 0) {
119
fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno));
124
sum += s.st_blocks / 2;
127
printf("total %d\n", sum);
132
static int listfile_size(const char *path, const char *filename, int flags)
136
if (lstat(path, &s) < 0) {
137
fprintf(stderr, "lstat '%s' failed: %s\n", path, strerror(errno));
141
/* blocks are 512 bytes, we want output to be KB */
142
if ((flags & LIST_SIZE) != 0) {
143
printf("%lld ", s.st_blocks / 2);
146
if ((flags & LIST_CLASSIFY) != 0) {
147
char filetype = mode2kind(s.st_mode);
148
if (filetype != 'l') {
149
printf("%c ", filetype);
151
struct stat link_dest;
152
if (!stat(path, &link_dest)) {
153
printf("l%c ", mode2kind(link_dest.st_mode));
155
fprintf(stderr, "stat '%s' failed: %s\n", path, strerror(errno));
161
printf("%s\n", filename);
166
static int listfile_long(const char *path, int flags)
175
/* name is anything after the final '/', or the whole path if none*/
176
name = strrchr(path, '/');
183
if(lstat(path, &s) < 0) {
187
mode2str(s.st_mode, mode);
188
if (flags & LIST_LONG_NUMERIC) {
189
sprintf(user, "%ld", s.st_uid);
190
sprintf(group, "%ld", s.st_gid);
192
user2str(s.st_uid, user);
193
group2str(s.st_gid, group);
196
strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime));
199
// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
200
// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
202
switch(s.st_mode & S_IFMT) {
205
printf("%s %-8s %-8s %3d, %3d %s %s\n",
207
(int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev),
211
printf("%s %-8s %-8s %8lld %s %s\n",
212
mode, user, group, s.st_size, date, name);
218
len = readlink(path, linkto, 256);
219
if(len < 0) return -1;
230
printf("%s %-8s %-8s %s %s -> %s\n",
231
mode, user, group, date, name, linkto);
235
printf("%s %-8s %-8s %s %s\n",
236
mode, user, group, date, name);
242
static int listfile_maclabel(const char *path, int flags)
248
char *maclabel = NULL;
251
/* name is anything after the final '/', or the whole path if none*/
252
name = strrchr(path, '/');
259
if(lstat(path, &s) < 0) {
264
lgetfilecon(path, &maclabel);
266
maclabel = strdup("-");
272
mode2str(s.st_mode, mode);
273
user2str(s.st_uid, user);
274
group2str(s.st_gid, group);
276
switch(s.st_mode & S_IFMT) {
281
len = readlink(path, linkto, sizeof(linkto));
282
if(len < 0) return -1;
284
if(len > sizeof(linkto)-1) {
285
linkto[sizeof(linkto)-4] = '.';
286
linkto[sizeof(linkto)-3] = '.';
287
linkto[sizeof(linkto)-2] = '.';
288
linkto[sizeof(linkto)-1] = 0;
293
printf("%s %-8s %-8s %s %s -> %s\n",
294
mode, user, group, maclabel, name, linkto);
298
printf("%s %-8s %-8s %s %s\n",
299
mode, user, group, maclabel, name);
308
static int listfile(const char *dirname, const char *filename, int flags)
310
if ((flags & LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL) == 0) {
311
printf("%s\n", filename);
316
const char* pathname = filename;
318
if (dirname != NULL) {
319
snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename);
325
if ((flags & LIST_MACLABEL) != 0) {
326
return listfile_maclabel(pathname, flags);
327
} else if ((flags & LIST_LONG) != 0) {
328
return listfile_long(pathname, flags);
329
} else /*((flags & LIST_SIZE) != 0)*/ {
330
return listfile_size(pathname, filename, flags);
334
static int listdir(const char *name, int flags)
339
strlist_t files = STRLIST_INITIALIZER;
343
fprintf(stderr, "opendir failed, %s\n", strerror(errno));
347
if ((flags & LIST_SIZE) != 0) {
348
show_total_size(name, d, flags);
351
while((de = readdir(d)) != 0){
352
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
353
if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
355
strlist_append_dup(&files, de->d_name);
358
strlist_sort(&files);
359
STRLIST_FOREACH(&files, filename, listfile(name, filename, flags));
360
strlist_done(&files);
362
if (flags & LIST_RECURSIVE) {
363
strlist_t subdirs = STRLIST_INITIALIZER;
367
while ((de = readdir(d)) != 0) {
371
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
373
if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
376
if (!strcmp(name, "/"))
377
snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
379
snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name);
382
* If the name ends in a '/', use stat() so we treat it like a
383
* directory even if it's a symlink.
385
if (tmp[strlen(tmp)-1] == '/')
388
err = lstat(tmp, &s);
396
if (S_ISDIR(s.st_mode)) {
397
strlist_append_dup(&subdirs, tmp);
400
strlist_sort(&subdirs);
401
STRLIST_FOREACH(&subdirs, path, {
402
printf("\n%s:\n", path);
403
listdir(path, flags);
405
strlist_done(&subdirs);
412
static int listpath(const char *name, int flags)
418
* If the name ends in a '/', use stat() so we treat it like a
419
* directory even if it's a symlink.
421
if (name[strlen(name)-1] == '/')
422
err = stat(name, &s);
424
err = lstat(name, &s);
431
if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) {
432
if (flags & LIST_RECURSIVE)
433
printf("\n%s:\n", name);
434
return listdir(name, flags);
436
/* yeah this calls stat() again*/
437
return listfile(NULL, name, flags);
441
int ls_main(int argc, char **argv)
449
strlist_t files = STRLIST_INITIALIZER;
451
for (i = 1; i < argc; i++) {
452
if (argv[i][0] == '-') {
454
const char *arg = argv[i]+1;
457
case 'l': flags |= LIST_LONG; break;
458
case 'n': flags |= LIST_LONG | LIST_LONG_NUMERIC; break;
459
case 's': flags |= LIST_SIZE; break;
460
case 'R': flags |= LIST_RECURSIVE; break;
461
case 'd': flags |= LIST_DIRECTORIES; break;
462
case 'Z': flags |= LIST_MACLABEL; break;
463
case 'a': flags |= LIST_ALL; break;
464
case 'F': flags |= LIST_CLASSIFY; break;
466
fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]);
472
/* not an option ? */
473
strlist_append_dup(&files, argv[i]);
477
if (files.count > 0) {
478
STRLIST_FOREACH(&files, path, {
479
if (listpath(path, flags) != 0) {
483
strlist_done(&files);
488
// list working directory if no files or directories were specified
489
return listpath(".", flags);