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