1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <dirent.h>
6 #include <errno.h>
7
8 #include <selinux/selinux.h>
9
10 #include <sys/stat.h>
11 #include <unistd.h>
12 #include <time.h>
13
14 #include <pwd.h>
15 #include <grp.h>
16
17 #include <linux/kdev_t.h>
18 #include <limits.h>
19
20 #include "dynarray.h"
21
22 // bits for flags argument
23 #define LIST_LONG (1 << 0)
24 #define LIST_ALL (1 << 1)
25 #define LIST_RECURSIVE (1 << 2)
26 #define LIST_DIRECTORIES (1 << 3)
27 #define LIST_SIZE (1 << 4)
28 #define LIST_LONG_NUMERIC (1 << 5)
29 #define LIST_CLASSIFY (1 << 6)
30 #define LIST_MACLABEL (1 << 7)
31 #define LIST_INODE (1 << 8)
32
33 // fwd
34 static int listpath(const char *name, int flags);
35
mode2kind(mode_t mode)36 static char mode2kind(mode_t mode)
37 {
38 switch(mode & S_IFMT){
39 case S_IFSOCK: return 's';
40 case S_IFLNK: return 'l';
41 case S_IFREG: return '-';
42 case S_IFDIR: return 'd';
43 case S_IFBLK: return 'b';
44 case S_IFCHR: return 'c';
45 case S_IFIFO: return 'p';
46 default: return '?';
47 }
48 }
49
strmode(mode_t mode,char * out)50 void strmode(mode_t mode, char *out)
51 {
52 *out++ = mode2kind(mode);
53
54 *out++ = (mode & 0400) ? 'r' : '-';
55 *out++ = (mode & 0200) ? 'w' : '-';
56 if(mode & 04000) {
57 *out++ = (mode & 0100) ? 's' : 'S';
58 } else {
59 *out++ = (mode & 0100) ? 'x' : '-';
60 }
61 *out++ = (mode & 040) ? 'r' : '-';
62 *out++ = (mode & 020) ? 'w' : '-';
63 if(mode & 02000) {
64 *out++ = (mode & 010) ? 's' : 'S';
65 } else {
66 *out++ = (mode & 010) ? 'x' : '-';
67 }
68 *out++ = (mode & 04) ? 'r' : '-';
69 *out++ = (mode & 02) ? 'w' : '-';
70 if(mode & 01000) {
71 *out++ = (mode & 01) ? 't' : 'T';
72 } else {
73 *out++ = (mode & 01) ? 'x' : '-';
74 }
75 *out = 0;
76 }
77
user2str(uid_t uid,char * out,size_t out_size)78 static void user2str(uid_t uid, char *out, size_t out_size)
79 {
80 struct passwd *pw = getpwuid(uid);
81 if(pw) {
82 strlcpy(out, pw->pw_name, out_size);
83 } else {
84 snprintf(out, out_size, "%d", uid);
85 }
86 }
87
group2str(gid_t gid,char * out,size_t out_size)88 static void group2str(gid_t gid, char *out, size_t out_size)
89 {
90 struct group *gr = getgrgid(gid);
91 if(gr) {
92 strlcpy(out, gr->gr_name, out_size);
93 } else {
94 snprintf(out, out_size, "%d", gid);
95 }
96 }
97
show_total_size(const char * dirname,DIR * d,int flags)98 static int show_total_size(const char *dirname, DIR *d, int flags)
99 {
100 struct dirent *de;
101 char tmp[1024];
102 struct stat s;
103 int sum = 0;
104
105 /* run through the directory and sum up the file block sizes */
106 while ((de = readdir(d)) != 0) {
107 if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
108 continue;
109 if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
110 continue;
111
112 if (strcmp(dirname, "/") == 0)
113 snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
114 else
115 snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name);
116
117 if (lstat(tmp, &s) < 0) {
118 fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno));
119 rewinddir(d);
120 return -1;
121 }
122
123 sum += s.st_blocks / 2;
124 }
125
126 printf("total %d\n", sum);
127 rewinddir(d);
128 return 0;
129 }
130
listfile_size(const char * path,const char * filename,struct stat * s,int flags)131 static int listfile_size(const char *path, const char *filename, struct stat *s,
132 int flags)
133 {
134 if(!s || !path) {
135 return -1;
136 }
137
138 /* blocks are 512 bytes, we want output to be KB */
139 if ((flags & LIST_SIZE) != 0) {
140 printf("%lld ", (long long)s->st_blocks / 2);
141 }
142
143 if ((flags & LIST_CLASSIFY) != 0) {
144 char filetype = mode2kind(s->st_mode);
145 if (filetype != 'l') {
146 printf("%c ", filetype);
147 } else {
148 struct stat link_dest;
149 if (!stat(path, &link_dest)) {
150 printf("l%c ", mode2kind(link_dest.st_mode));
151 } else {
152 fprintf(stderr, "stat '%s' failed: %s\n", path, strerror(errno));
153 printf("l? ");
154 }
155 }
156 }
157
158 printf("%s\n", filename);
159
160 return 0;
161 }
162
listfile_long(const char * path,struct stat * s,int flags)163 static int listfile_long(const char *path, struct stat *s, int flags)
164 {
165 char date[32];
166 char mode[16];
167 char user[32];
168 char group[32];
169 const char *name;
170
171 if(!s || !path) {
172 return -1;
173 }
174
175 /* name is anything after the final '/', or the whole path if none*/
176 name = strrchr(path, '/');
177 if(name == 0) {
178 name = path;
179 } else {
180 name++;
181 }
182
183 strmode(s->st_mode, mode);
184 if (flags & LIST_LONG_NUMERIC) {
185 snprintf(user, sizeof(user), "%u", s->st_uid);
186 snprintf(group, sizeof(group), "%u", s->st_gid);
187 } else {
188 user2str(s->st_uid, user, sizeof(user));
189 group2str(s->st_gid, group, sizeof(group));
190 }
191
192 strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s->st_mtime));
193 date[31] = 0;
194
195 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890
196 // MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
197
198 switch(s->st_mode & S_IFMT) {
199 case S_IFBLK:
200 case S_IFCHR:
201 printf("%s %-8s %-8s %3d, %3d %s %s\n",
202 mode, user, group,
203 (int) MAJOR(s->st_rdev), (int) MINOR(s->st_rdev),
204 date, name);
205 break;
206 case S_IFREG:
207 printf("%s %-8s %-8s %8lld %s %s\n",
208 mode, user, group, (long long)s->st_size, date, name);
209 break;
210 case S_IFLNK: {
211 char linkto[256];
212 ssize_t len;
213
214 len = readlink(path, linkto, 256);
215 if(len < 0) return -1;
216
217 if(len > 255) {
218 linkto[252] = '.';
219 linkto[253] = '.';
220 linkto[254] = '.';
221 linkto[255] = 0;
222 } else {
223 linkto[len] = 0;
224 }
225
226 printf("%s %-8s %-8s %s %s -> %s\n",
227 mode, user, group, date, name, linkto);
228 break;
229 }
230 default:
231 printf("%s %-8s %-8s %s %s\n",
232 mode, user, group, date, name);
233
234 }
235 return 0;
236 }
237
listfile_maclabel(const char * path,struct stat * s)238 static int listfile_maclabel(const char *path, struct stat *s)
239 {
240 char mode[16];
241 char user[32];
242 char group[32];
243 char *maclabel = NULL;
244 const char *name;
245
246 if(!s || !path) {
247 return -1;
248 }
249
250 /* name is anything after the final '/', or the whole path if none*/
251 name = strrchr(path, '/');
252 if(name == 0) {
253 name = path;
254 } else {
255 name++;
256 }
257
258 lgetfilecon(path, &maclabel);
259 if (!maclabel) {
260 return -1;
261 }
262
263 strmode(s->st_mode, mode);
264 user2str(s->st_uid, user, sizeof(user));
265 group2str(s->st_gid, group, sizeof(group));
266
267 switch(s->st_mode & S_IFMT) {
268 case S_IFLNK: {
269 char linkto[256];
270 ssize_t len;
271
272 len = readlink(path, linkto, sizeof(linkto));
273 if(len < 0) return -1;
274
275 if((size_t)len > sizeof(linkto)-1) {
276 linkto[sizeof(linkto)-4] = '.';
277 linkto[sizeof(linkto)-3] = '.';
278 linkto[sizeof(linkto)-2] = '.';
279 linkto[sizeof(linkto)-1] = 0;
280 } else {
281 linkto[len] = 0;
282 }
283
284 printf("%s %-8s %-8s %s %s -> %s\n",
285 mode, user, group, maclabel, name, linkto);
286 break;
287 }
288 default:
289 printf("%s %-8s %-8s %s %s\n",
290 mode, user, group, maclabel, name);
291
292 }
293
294 free(maclabel);
295
296 return 0;
297 }
298
listfile(const char * dirname,const char * filename,int flags)299 static int listfile(const char *dirname, const char *filename, int flags)
300 {
301 struct stat s;
302
303 if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL | LIST_INODE)) == 0) {
304 printf("%s\n", filename);
305 return 0;
306 }
307
308 char tmp[4096];
309 const char* pathname = filename;
310
311 if (dirname != NULL) {
312 snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename);
313 pathname = tmp;
314 } else {
315 pathname = filename;
316 }
317
318 if(lstat(pathname, &s) < 0) {
319 fprintf(stderr, "lstat '%s' failed: %s\n", pathname, strerror(errno));
320 return -1;
321 }
322
323 if(flags & LIST_INODE) {
324 printf("%8llu ", (unsigned long long)s.st_ino);
325 }
326
327 if ((flags & LIST_MACLABEL) != 0) {
328 return listfile_maclabel(pathname, &s);
329 } else if ((flags & LIST_LONG) != 0) {
330 return listfile_long(pathname, &s, flags);
331 } else /*((flags & LIST_SIZE) != 0)*/ {
332 return listfile_size(pathname, filename, &s, flags);
333 }
334 }
335
listdir(const char * name,int flags)336 static int listdir(const char *name, int flags)
337 {
338 char tmp[4096];
339 DIR *d;
340 struct dirent *de;
341 strlist_t files = STRLIST_INITIALIZER;
342
343 d = opendir(name);
344 if(d == 0) {
345 fprintf(stderr, "opendir failed, %s\n", strerror(errno));
346 return -1;
347 }
348
349 if ((flags & LIST_SIZE) != 0) {
350 show_total_size(name, d, flags);
351 }
352
353 while((de = readdir(d)) != 0){
354 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
355 if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
356
357 strlist_append_dup(&files, de->d_name);
358 }
359
360 strlist_sort(&files);
361 STRLIST_FOREACH(&files, filename, listfile(name, filename, flags));
362 strlist_done(&files);
363
364 if (flags & LIST_RECURSIVE) {
365 strlist_t subdirs = STRLIST_INITIALIZER;
366
367 rewinddir(d);
368
369 while ((de = readdir(d)) != 0) {
370 struct stat s;
371 int err;
372
373 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
374 continue;
375 if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
376 continue;
377
378 if (!strcmp(name, "/"))
379 snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
380 else
381 snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name);
382
383 /*
384 * If the name ends in a '/', use stat() so we treat it like a
385 * directory even if it's a symlink.
386 */
387 if (tmp[strlen(tmp)-1] == '/')
388 err = stat(tmp, &s);
389 else
390 err = lstat(tmp, &s);
391
392 if (err < 0) {
393 perror(tmp);
394 closedir(d);
395 return -1;
396 }
397
398 if (S_ISDIR(s.st_mode)) {
399 strlist_append_dup(&subdirs, tmp);
400 }
401 }
402 strlist_sort(&subdirs);
403 STRLIST_FOREACH(&subdirs, path, {
404 printf("\n%s:\n", path);
405 listdir(path, flags);
406 });
407 strlist_done(&subdirs);
408 }
409
410 closedir(d);
411 return 0;
412 }
413
listpath(const char * name,int flags)414 static int listpath(const char *name, int flags)
415 {
416 struct stat s;
417 int err;
418
419 /*
420 * If the name ends in a '/', use stat() so we treat it like a
421 * directory even if it's a symlink.
422 */
423 if (name[strlen(name)-1] == '/')
424 err = stat(name, &s);
425 else
426 err = lstat(name, &s);
427
428 if (err < 0) {
429 perror(name);
430 return -1;
431 }
432
433 if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) {
434 if (flags & LIST_RECURSIVE)
435 printf("\n%s:\n", name);
436 return listdir(name, flags);
437 } else {
438 /* yeah this calls stat() again*/
439 return listfile(NULL, name, flags);
440 }
441 }
442
ls_main(int argc,char ** argv)443 int ls_main(int argc, char **argv)
444 {
445 int flags = 0;
446
447 if(argc > 1) {
448 int i;
449 int err = 0;
450 strlist_t files = STRLIST_INITIALIZER;
451
452 for (i = 1; i < argc; i++) {
453 if (argv[i][0] == '-') {
454 /* an option ? */
455 const char *arg = argv[i]+1;
456 while (arg[0]) {
457 switch (arg[0]) {
458 case 'l': flags |= LIST_LONG; break;
459 case 'n': flags |= LIST_LONG | LIST_LONG_NUMERIC; break;
460 case 's': flags |= LIST_SIZE; break;
461 case 'R': flags |= LIST_RECURSIVE; break;
462 case 'd': flags |= LIST_DIRECTORIES; break;
463 case 'Z': flags |= LIST_MACLABEL; break;
464 case 'a': flags |= LIST_ALL; break;
465 case 'F': flags |= LIST_CLASSIFY; break;
466 case 'i': flags |= LIST_INODE; break;
467 default:
468 fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]);
469 exit(1);
470 }
471 arg++;
472 }
473 } else {
474 /* not an option ? */
475 strlist_append_dup(&files, argv[i]);
476 }
477 }
478
479 if (files.count > 0) {
480 STRLIST_FOREACH(&files, path, {
481 if (listpath(path, flags) != 0) {
482 err = EXIT_FAILURE;
483 }
484 });
485 strlist_done(&files);
486 return err;
487 }
488 }
489
490 // list working directory if no files or directories were specified
491 return listpath(".", flags);
492 }
493