1 /* 2 * Copyright (c) 1999 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of KTH nor the names of its contributors may be 18 * used to endorse or promote products derived from this software without 19 * specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY 22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 32 33 #include "ftpd_locl.h" 34 35 RCSID("$Id: ls.c,v 1.14 2000/01/05 13:48:58 joda Exp $"); 36 37 struct fileinfo { 38 struct stat st; 39 int inode; 40 int bsize; 41 char mode[11]; 42 int n_link; 43 char *user; 44 char *group; 45 char *size; 46 char *major; 47 char *minor; 48 char *date; 49 char *filename; 50 char *link; 51 }; 52 53 static void 54 free_fileinfo(struct fileinfo *f) 55 { 56 free(f->user); 57 free(f->group); 58 free(f->size); 59 free(f->major); 60 free(f->minor); 61 free(f->date); 62 free(f->filename); 63 free(f->link); 64 } 65 66 #define LS_DIRS 1 67 #define LS_IGNORE_DOT 2 68 #define LS_SORT_MODE 12 69 #define SORT_MODE(f) ((f) & LS_SORT_MODE) 70 #define LS_SORT_NAME 4 71 #define LS_SORT_MTIME 8 72 #define LS_SORT_SIZE 12 73 #define LS_SORT_REVERSE 16 74 75 #define LS_SIZE 32 76 #define LS_INODE 64 77 78 #ifndef S_ISTXT 79 #define S_ISTXT S_ISVTX 80 #endif 81 82 #ifndef S_ISSOCK 83 #define S_ISSOCK(mode) (((mode) & _S_IFMT) == S_IFSOCK) 84 #endif 85 86 #ifndef S_ISLNK 87 #define S_ISLNK(mode) (((mode) & _S_IFMT) == S_IFLNK) 88 #endif 89 90 static void 91 make_fileinfo(const char *filename, struct fileinfo *file, int flags) 92 { 93 char buf[128]; 94 struct stat *st = &file->st; 95 96 file->inode = st->st_ino; 97 #ifdef S_BLKSIZE 98 file->bsize = st->st_blocks * S_BLKSIZE / 1024; 99 #else 100 file->bsize = st->st_blocks * 512 / 1024; 101 #endif 102 103 if(S_ISDIR(st->st_mode)) 104 file->mode[0] = 'd'; 105 else if(S_ISCHR(st->st_mode)) 106 file->mode[0] = 'c'; 107 else if(S_ISBLK(st->st_mode)) 108 file->mode[0] = 'b'; 109 else if(S_ISREG(st->st_mode)) 110 file->mode[0] = '-'; 111 else if(S_ISFIFO(st->st_mode)) 112 file->mode[0] = 'p'; 113 else if(S_ISLNK(st->st_mode)) 114 file->mode[0] = 'l'; 115 else if(S_ISSOCK(st->st_mode)) 116 file->mode[0] = 's'; 117 #ifdef S_ISWHT 118 else if(S_ISWHT(st->st_mode)) 119 file->mode[0] = 'w'; 120 #endif 121 else 122 file->mode[0] = '?'; 123 { 124 char *x[] = { "---", "--x", "-w-", "-wx", 125 "r--", "r-x", "rw-", "rwx" }; 126 strcpy(file->mode + 1, x[(st->st_mode & S_IRWXU) >> 6]); 127 strcpy(file->mode + 4, x[(st->st_mode & S_IRWXG) >> 3]); 128 strcpy(file->mode + 7, x[(st->st_mode & S_IRWXO) >> 0]); 129 if((st->st_mode & S_ISUID)) { 130 if((st->st_mode & S_IXUSR)) 131 file->mode[3] = 's'; 132 else 133 file->mode[3] = 'S'; 134 } 135 if((st->st_mode & S_ISGID)) { 136 if((st->st_mode & S_IXGRP)) 137 file->mode[6] = 's'; 138 else 139 file->mode[6] = 'S'; 140 } 141 if((st->st_mode & S_ISTXT)) { 142 if((st->st_mode & S_IXOTH)) 143 file->mode[9] = 't'; 144 else 145 file->mode[9] = 'T'; 146 } 147 } 148 file->n_link = st->st_nlink; 149 { 150 struct passwd *pwd; 151 pwd = getpwuid(st->st_uid); 152 if(pwd == NULL) 153 asprintf(&file->user, "%u", (unsigned)st->st_uid); 154 else 155 file->user = strdup(pwd->pw_name); 156 } 157 { 158 struct group *grp; 159 grp = getgrgid(st->st_gid); 160 if(grp == NULL) 161 asprintf(&file->group, "%u", (unsigned)st->st_gid); 162 else 163 file->group = strdup(grp->gr_name); 164 } 165 166 if(S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { 167 #if defined(major) && defined(minor) 168 asprintf(&file->major, "%u", (unsigned)major(st->st_rdev)); 169 asprintf(&file->minor, "%u", (unsigned)minor(st->st_rdev)); 170 #else 171 /* Don't want to use the DDI/DKI crap. */ 172 asprintf(&file->major, "%u", (unsigned)st->st_rdev); 173 asprintf(&file->minor, "%u", 0); 174 #endif 175 } else 176 asprintf(&file->size, "%lu", (unsigned long)st->st_size); 177 178 { 179 time_t t = time(NULL); 180 struct tm *tm = localtime(&st->st_mtime); 181 if((t - st->st_mtime > 6*30*24*60*60) || 182 (st->st_mtime - t > 6*30*24*60*60)) 183 strftime(buf, sizeof(buf), "%b %e %Y", tm); 184 else 185 strftime(buf, sizeof(buf), "%b %e %H:%M", tm); 186 file->date = strdup(buf); 187 } 188 { 189 const char *p = strrchr(filename, '/'); 190 if(p) 191 p++; 192 else 193 p = filename; 194 file->filename = strdup(p); 195 } 196 if(S_ISLNK(st->st_mode)) { 197 int n; 198 n = readlink((char *)filename, buf, sizeof(buf)); 199 if(n >= 0) { 200 buf[n] = '\0'; 201 file->link = strdup(buf); 202 } else 203 warn("%s: readlink", filename); 204 } 205 } 206 207 static void 208 print_file(FILE *out, 209 int flags, 210 struct fileinfo *f, 211 int max_inode, 212 int max_bsize, 213 int max_n_link, 214 int max_user, 215 int max_group, 216 int max_size, 217 int max_major, 218 int max_minor, 219 int max_date) 220 { 221 if(f->filename == NULL) 222 return; 223 224 if(flags & LS_INODE) { 225 sec_fprintf2(out, "%*d", max_inode, f->inode); 226 sec_fprintf2(out, " "); 227 } 228 if(flags & LS_SIZE) { 229 sec_fprintf2(out, "%*d", max_bsize, f->bsize); 230 sec_fprintf2(out, " "); 231 } 232 sec_fprintf2(out, "%s", f->mode); 233 sec_fprintf2(out, " "); 234 sec_fprintf2(out, "%*d", max_n_link, f->n_link); 235 sec_fprintf2(out, " "); 236 sec_fprintf2(out, "%-*s", max_user, f->user); 237 sec_fprintf2(out, " "); 238 sec_fprintf2(out, "%-*s", max_group, f->group); 239 sec_fprintf2(out, " "); 240 if(f->major != NULL && f->minor != NULL) 241 sec_fprintf2(out, "%*s, %*s", max_major, f->major, max_minor, f->minor); 242 else 243 sec_fprintf2(out, "%*s", max_size, f->size); 244 sec_fprintf2(out, " "); 245 sec_fprintf2(out, "%*s", max_date, f->date); 246 sec_fprintf2(out, " "); 247 sec_fprintf2(out, "%s", f->filename); 248 if(f->link) 249 sec_fprintf2(out, " -> %s", f->link); 250 sec_fprintf2(out, "\r\n"); 251 } 252 253 static int 254 compare_filename(struct fileinfo *a, struct fileinfo *b) 255 { 256 if(a->filename == NULL) 257 return 1; 258 if(b->filename == NULL) 259 return -1; 260 return strcmp(a->filename, b->filename); 261 } 262 263 static int 264 compare_mtime(struct fileinfo *a, struct fileinfo *b) 265 { 266 if(a->filename == NULL) 267 return 1; 268 if(b->filename == NULL) 269 return -1; 270 return a->st.st_mtime - b->st.st_mtime; 271 } 272 273 static int 274 compare_size(struct fileinfo *a, struct fileinfo *b) 275 { 276 if(a->filename == NULL) 277 return 1; 278 if(b->filename == NULL) 279 return -1; 280 return a->st.st_size - b->st.st_size; 281 } 282 283 static void 284 list_dir(FILE *out, const char *directory, int flags); 285 286 static int 287 log10(int num) 288 { 289 int i = 1; 290 while(num > 10) { 291 i++; 292 num /= 10; 293 } 294 return i; 295 } 296 297 /* 298 * Operate as lstat but fake up entries for AFS mount points so we don't 299 * have to fetch them. 300 */ 301 302 static int 303 lstat_file (const char *file, struct stat *sb) 304 { 305 #ifdef KRB4 306 if (k_hasafs() 307 && strcmp(file, ".") 308 && strcmp(file, "..")) 309 { 310 struct ViceIoctl a_params; 311 char *last; 312 char *path_bkp; 313 static ino_t ino_counter = 0, ino_last = 0; 314 int ret; 315 const int maxsize = 2048; 316 317 path_bkp = strdup (file); 318 if (path_bkp == NULL) 319 return -1; 320 321 a_params.out = malloc (maxsize); 322 if (a_params.out == NULL) { 323 free (path_bkp); 324 return -1; 325 } 326 327 /* If path contains more than the filename alone - split it */ 328 329 last = strrchr (path_bkp, '/'); 330 if (last != NULL) { 331 *last = '\0'; 332 a_params.in = last + 1; 333 } else 334 a_params.in = (char *)file; 335 336 a_params.in_size = strlen (a_params.in) + 1; 337 a_params.out_size = maxsize; 338 339 ret = k_pioctl (last ? path_bkp : "." , 340 VIOC_AFS_STAT_MT_PT, &a_params, 0); 341 free (a_params.out); 342 if (ret < 0) { 343 free (path_bkp); 344 345 if (errno != EINVAL) 346 return ret; 347 else 348 /* if we get EINVAL this is probably not a mountpoint */ 349 return lstat (file, sb); 350 } 351 352 /* 353 * wow this was a mountpoint, lets cook the struct stat 354 * use . as a prototype 355 */ 356 357 ret = lstat (path_bkp, sb); 358 free (path_bkp); 359 if (ret < 0) 360 return ret; 361 362 if (ino_last == sb->st_ino) 363 ino_counter++; 364 else { 365 ino_last = sb->st_ino; 366 ino_counter = 0; 367 } 368 sb->st_ino += ino_counter; 369 sb->st_nlink = 3; 370 371 return 0; 372 } 373 #endif /* KRB4 */ 374 return lstat (file, sb); 375 } 376 377 static void 378 list_files(FILE *out, const char **files, int n_files, int flags) 379 { 380 struct fileinfo *fi; 381 int i; 382 383 fi = calloc(n_files, sizeof(*fi)); 384 if (fi == NULL) { 385 sec_fprintf2(out, "ouf of memory\r\n"); 386 return; 387 } 388 for(i = 0; i < n_files; i++) { 389 if(lstat_file(files[i], &fi[i].st) < 0) { 390 sec_fprintf2(out, "%s: %s\r\n", files[i], strerror(errno)); 391 fi[i].filename = NULL; 392 } else { 393 if((flags & LS_DIRS) == 0 && S_ISDIR(fi[i].st.st_mode)) { 394 if(n_files > 1) 395 sec_fprintf2(out, "%s:\r\n", files[i]); 396 list_dir(out, files[i], flags); 397 } else { 398 make_fileinfo(files[i], &fi[i], flags); 399 } 400 } 401 } 402 switch(SORT_MODE(flags)) { 403 case LS_SORT_NAME: 404 qsort(fi, n_files, sizeof(*fi), 405 (int (*)(const void*, const void*))compare_filename); 406 break; 407 case LS_SORT_MTIME: 408 qsort(fi, n_files, sizeof(*fi), 409 (int (*)(const void*, const void*))compare_mtime); 410 break; 411 case LS_SORT_SIZE: 412 qsort(fi, n_files, sizeof(*fi), 413 (int (*)(const void*, const void*))compare_size); 414 break; 415 } 416 { 417 int max_inode = 0; 418 int max_bsize = 0; 419 int max_n_link = 0; 420 int max_user = 0; 421 int max_group = 0; 422 int max_size = 0; 423 int max_major = 0; 424 int max_minor = 0; 425 int max_date = 0; 426 for(i = 0; i < n_files; i++) { 427 if(fi[i].filename == NULL) 428 continue; 429 if(fi[i].inode > max_inode) 430 max_inode = fi[i].inode; 431 if(fi[i].bsize > max_bsize) 432 max_bsize = fi[i].bsize; 433 if(fi[i].n_link > max_n_link) 434 max_n_link = fi[i].n_link; 435 if(strlen(fi[i].user) > max_user) 436 max_user = strlen(fi[i].user); 437 if(strlen(fi[i].group) > max_group) 438 max_group = strlen(fi[i].group); 439 if(fi[i].major != NULL && strlen(fi[i].major) > max_major) 440 max_major = strlen(fi[i].major); 441 if(fi[i].minor != NULL && strlen(fi[i].minor) > max_minor) 442 max_minor = strlen(fi[i].minor); 443 if(fi[i].size != NULL && strlen(fi[i].size) > max_size) 444 max_size = strlen(fi[i].size); 445 if(strlen(fi[i].date) > max_date) 446 max_date = strlen(fi[i].date); 447 } 448 if(max_size < max_major + max_minor + 2) 449 max_size = max_major + max_minor + 2; 450 else if(max_size - max_minor - 2 > max_major) 451 max_major = max_size - max_minor - 2; 452 max_inode = log10(max_inode); 453 max_bsize = log10(max_bsize); 454 max_n_link = log10(max_n_link); 455 456 if(flags & LS_SORT_REVERSE) 457 for(i = n_files - 1; i >= 0; i--) 458 print_file(out, 459 flags, 460 &fi[i], 461 max_inode, 462 max_bsize, 463 max_n_link, 464 max_user, 465 max_group, 466 max_size, 467 max_major, 468 max_minor, 469 max_date); 470 else 471 for(i = 0; i < n_files; i++) 472 print_file(out, 473 flags, 474 &fi[i], 475 max_inode, 476 max_bsize, 477 max_n_link, 478 max_user, 479 max_group, 480 max_size, 481 max_major, 482 max_minor, 483 max_date); 484 for(i = 0; i < n_files; i++) 485 free_fileinfo(&fi[i]); 486 free(fi); 487 } 488 } 489 490 static void 491 free_files (char **files, int n) 492 { 493 int i; 494 495 for (i = 0; i < n; ++i) 496 free (files[i]); 497 free (files); 498 } 499 500 static void 501 list_dir(FILE *out, const char *directory, int flags) 502 { 503 DIR *d = opendir(directory); 504 struct dirent *ent; 505 char **files = NULL; 506 int n_files = 0; 507 508 if(d == NULL) { 509 sec_fprintf2(out, "%s: %s\r\n", directory, strerror(errno)); 510 return; 511 } 512 while((ent = readdir(d)) != NULL) { 513 void *tmp; 514 515 if(ent->d_name[0] == '.') { 516 if (flags & LS_IGNORE_DOT) 517 continue; 518 if (ent->d_name[1] == 0) /* Ignore . */ 519 continue; 520 if (ent->d_name[1] == '.' && ent->d_name[2] == 0) /* Ignore .. */ 521 continue; 522 } 523 tmp = realloc(files, (n_files + 1) * sizeof(*files)); 524 if (tmp == NULL) { 525 sec_fprintf2(out, "%s: out of memory\r\n", directory); 526 free_files (files, n_files); 527 closedir (d); 528 return; 529 } 530 files = tmp; 531 asprintf(&files[n_files], "%s/%s", directory, ent->d_name); 532 if (files[n_files] == NULL) { 533 sec_fprintf2(out, "%s: out of memory\r\n", directory); 534 free_files (files, n_files); 535 closedir (d); 536 return; 537 } 538 ++n_files; 539 } 540 closedir(d); 541 list_files(out, (const char**)files, n_files, flags | LS_DIRS); 542 } 543 544 void 545 builtin_ls(FILE *out, const char *file) 546 { 547 int flags = LS_SORT_NAME; 548 549 if(*file == '-') { 550 const char *p; 551 for(p = file + 1; *p; p++) { 552 switch(*p) { 553 case 'a': 554 case 'A': 555 flags &= ~LS_IGNORE_DOT; 556 break; 557 case 'C': 558 break; 559 case 'd': 560 flags |= LS_DIRS; 561 break; 562 case 'f': 563 flags = (flags & ~LS_SORT_MODE); 564 break; 565 case 'i': 566 flags |= flags | LS_INODE; 567 break; 568 case 'l': 569 break; 570 case 't': 571 flags = (flags & ~LS_SORT_MODE) | LS_SORT_MTIME; 572 break; 573 case 's': 574 flags |= LS_SIZE; 575 break; 576 case 'S': 577 flags = (flags & ~LS_SORT_MODE) | LS_SORT_SIZE; 578 break; 579 case 'r': 580 flags |= LS_SORT_REVERSE; 581 break; 582 } 583 } 584 file = "."; 585 } 586 list_files(out, &file, 1, flags); 587 sec_fflush(out); 588 } 589