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(unsigned mode)36 static char mode2kind(unsigned 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
mode2str(unsigned mode,char * out)50 static void mode2str(unsigned 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(unsigned uid,char * out)78 static void user2str(unsigned uid, char *out)
79 {
80 struct passwd *pw = getpwuid(uid);
81 if(pw) {
82 strcpy(out, pw->pw_name);
83 } else {
84 sprintf(out, "%d", uid);
85 }
86 }
87
group2str(unsigned gid,char * out)88 static void group2str(unsigned gid, char *out)
89 {
90 struct group *gr = getgrgid(gid);
91 if(gr) {
92 strcpy(out, gr->gr_name);
93 } else {
94 sprintf(out, "%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 ", 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[16];
168 char group[16];
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 mode2str(s->st_mode, mode);
184 if (flags & LIST_LONG_NUMERIC) {
185 sprintf(user, "%ld", s->st_uid);
186 sprintf(group, "%ld", s->st_gid);
187 } else {
188 user2str(s->st_uid, user);
189 group2str(s->st_gid, 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, s->st_size, date, name);
209 break;
210 case S_IFLNK: {
211 char linkto[256];
212 int 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,int flags)238 static int listfile_maclabel(const char *path, struct stat *s, int flags)
239 {
240 char mode[16];
241 char user[16];
242 char group[16];
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 mode2str(s->st_mode, mode);
264 user2str(s->st_uid, user);
265 group2str(s->st_gid, 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 return -1;
320 }
321
322 if(flags & LIST_INODE) {
323 printf("%8llu ", s.st_ino);
324 }
325
326 if ((flags & LIST_MACLABEL) != 0) {
327 return listfile_maclabel(pathname, &s, flags);
328 } else if ((flags & LIST_LONG) != 0) {
329 return listfile_long(pathname, &s, flags);
330 } else /*((flags & LIST_SIZE) != 0)*/ {
331 return listfile_size(pathname, filename, &s, flags);
332 }
333 }
334
listdir(const char * name,int flags)335 static int listdir(const char *name, int flags)
336 {
337 char tmp[4096];
338 DIR *d;
339 struct dirent *de;
340 strlist_t files = STRLIST_INITIALIZER;
341
342 d = opendir(name);
343 if(d == 0) {
344 fprintf(stderr, "opendir failed, %s\n", strerror(errno));
345 return -1;
346 }
347
348 if ((flags & LIST_SIZE) != 0) {
349 show_total_size(name, d, flags);
350 }
351
352 while((de = readdir(d)) != 0){
353 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
354 if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
355
356 strlist_append_dup(&files, de->d_name);
357 }
358
359 strlist_sort(&files);
360 STRLIST_FOREACH(&files, filename, listfile(name, filename, flags));
361 strlist_done(&files);
362
363 if (flags & LIST_RECURSIVE) {
364 strlist_t subdirs = STRLIST_INITIALIZER;
365
366 rewinddir(d);
367
368 while ((de = readdir(d)) != 0) {
369 struct stat s;
370 int err;
371
372 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
373 continue;
374 if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
375 continue;
376
377 if (!strcmp(name, "/"))
378 snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
379 else
380 snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name);
381
382 /*
383 * If the name ends in a '/', use stat() so we treat it like a
384 * directory even if it's a symlink.
385 */
386 if (tmp[strlen(tmp)-1] == '/')
387 err = stat(tmp, &s);
388 else
389 err = lstat(tmp, &s);
390
391 if (err < 0) {
392 perror(tmp);
393 closedir(d);
394 return -1;
395 }
396
397 if (S_ISDIR(s.st_mode)) {
398 strlist_append_dup(&subdirs, tmp);
399 }
400 }
401 strlist_sort(&subdirs);
402 STRLIST_FOREACH(&subdirs, path, {
403 printf("\n%s:\n", path);
404 listdir(path, flags);
405 });
406 strlist_done(&subdirs);
407 }
408
409 closedir(d);
410 return 0;
411 }
412
listpath(const char * name,int flags)413 static int listpath(const char *name, int flags)
414 {
415 struct stat s;
416 int err;
417
418 /*
419 * If the name ends in a '/', use stat() so we treat it like a
420 * directory even if it's a symlink.
421 */
422 if (name[strlen(name)-1] == '/')
423 err = stat(name, &s);
424 else
425 err = lstat(name, &s);
426
427 if (err < 0) {
428 perror(name);
429 return -1;
430 }
431
432 if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) {
433 if (flags & LIST_RECURSIVE)
434 printf("\n%s:\n", name);
435 return listdir(name, flags);
436 } else {
437 /* yeah this calls stat() again*/
438 return listfile(NULL, name, flags);
439 }
440 }
441
ls_main(int argc,char ** argv)442 int ls_main(int argc, char **argv)
443 {
444 int flags = 0;
445 int listed = 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