Home | History | Annotate | Download | only in toolbox
      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 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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