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