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