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 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 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 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 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 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 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 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 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 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 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 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 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