1/* 2 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com> 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13*/ 14 15#ifdef HAVE_CONFIG_H 16#include "config.h" 17#endif /* HAVE_CONFIG_H */ 18 19#include <unistd.h> 20#include <sys/types.h> 21#include <stdlib.h> 22#include <stdio.h> 23#include <stdarg.h> 24#include <limits.h> 25#include <string.h> 26#include <errno.h> 27#include <dirent.h> 28#include <fcntl.h> 29#include <ctype.h> 30#include <pwd.h> 31#include <grp.h> 32#include <time.h> 33 34#include <atalk/adouble.h> 35#include <atalk/cnid.h> 36#include "ad.h" 37 38#define ADv2_DIRNAME ".AppleDouble" 39 40#define DIR_DOT_OR_DOTDOT(a) \ 41 ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0)) 42 43static volatile sig_atomic_t alarmed; 44 45/* ls options */ 46static int ls_a; 47static int ls_l; 48static int ls_R; 49static int ls_d; 50static int ls_u; 51 52/* Used for pretty printing */ 53static int first = 1; 54static int recursion; 55 56static char *netatalk_dirs[] = { 57 ADv2_DIRNAME, 58 ".AppleDB", 59 ".AppleDesktop", 60 NULL 61}; 62 63static char *labels[] = { 64 "---", 65 "gry", 66 "gre", 67 "vio", 68 "blu", 69 "yel", 70 "red", 71 "ora" 72}; 73 74/* 75 SIGNAL handling: 76 catch SIGINT and SIGTERM which cause clean exit. Ignore anything else. 77*/ 78 79static void sig_handler(int signo) 80{ 81 alarmed = 1; 82 return; 83} 84 85static void set_signal(void) 86{ 87 struct sigaction sv; 88 89 sv.sa_handler = sig_handler; 90 sv.sa_flags = SA_RESTART; 91 sigemptyset(&sv.sa_mask); 92 if (sigaction(SIGTERM, &sv, NULL) < 0) 93 ERROR("error in sigaction(SIGTERM): %s", strerror(errno)); 94 95 if (sigaction(SIGINT, &sv, NULL) < 0) 96 ERROR("error in sigaction(SIGINT): %s", strerror(errno)); 97 98 memset(&sv, 0, sizeof(struct sigaction)); 99 sv.sa_handler = SIG_IGN; 100 sigemptyset(&sv.sa_mask); 101 102 if (sigaction(SIGABRT, &sv, NULL) < 0) 103 ERROR("error in sigaction(SIGABRT): %s", strerror(errno)); 104 105 if (sigaction(SIGHUP, &sv, NULL) < 0) 106 ERROR("error in sigaction(SIGHUP): %s", strerror(errno)); 107 108 if (sigaction(SIGQUIT, &sv, NULL) < 0) 109 ERROR("error in sigaction(SIGQUIT): %s", strerror(errno)); 110} 111 112/* 113 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" 114 Returns pointer to name or NULL. 115*/ 116static const char *check_netatalk_dirs(const char *name) 117{ 118 int c; 119 120 for (c=0; netatalk_dirs[c]; c++) { 121 if ((strcmp(name, netatalk_dirs[c])) == 0) 122 return netatalk_dirs[c]; 123 } 124 return NULL; 125} 126 127 128static void usage_ls(void) 129{ 130 printf( 131 "Usage: ad ls [-dRl[u]] [file|dir, ...]\n\n" 132 " -l Long Output [-u: unix info]:\n" 133 " <unixinfo ...> <FinderFlags> <AFPAttributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>\n\n" 134 " FinderFlags (valid for (f)ile and/or (d)irectory):\n" 135 " d = On Desktop (f/d)\n" 136 " e = Hidden extension (f/d)\n" 137 " m = Shared (can run multiple times) (f)\n" 138 " n = No INIT resources (f)\n" 139 " i = Inited (f/d)\n" 140 " c = Custom icon (f/d)\n" 141 " t = Stationery (f)\n" 142 " s = Name locked (f/d)\n" 143 " b = Bundle (f/d)\n" 144 " v = Invisible (f/d)\n" 145 " a = Alias file (f/d)\n\n" 146 " AFPAttributes:\n" 147 " y = System (f/d)\n" 148 " w = No write (f)\n" 149 " p = Needs backup (f/d)\n" 150 " r = No rename (f/d)\n" 151 " l = No delete (f/d)\n" 152 " o = No copy (f)\n\n" 153 " Note: any letter appearing in uppercase means the flag is set\n" 154 " but it's a directory for which the flag is not allowed.\n" 155 ); 156} 157 158static void print_numlinks(const struct stat *statp) 159{ 160 printf("%5ld", (long)statp->st_nlink); 161} 162 163static void print_owner(const struct stat *statp) 164{ 165 struct passwd *pwd = getpwuid(statp->st_uid); 166 167 if (pwd == NULL) 168 printf(" %-8ld", (long)statp->st_uid); 169 else 170 printf(" %-8s", pwd->pw_name); 171} 172 173static void print_group(const struct stat *statp) 174{ 175 struct group *grp = getgrgid(statp->st_gid); 176 177 if (grp == NULL) 178 printf(" %-8ld", (long)statp->st_gid); 179 else 180 printf(" %-8s", grp->gr_name); 181} 182 183static void print_size(const struct stat *statp) 184{ 185 switch (statp->st_mode & S_IFMT) { 186 case S_IFCHR: 187 case S_IFBLK: 188 printf("%4u,%4u", (unsigned)(statp->st_rdev >> 8), 189 (unsigned)(statp->st_rdev & 0xFF)); 190 break; 191 default: 192 printf("%9lu", (unsigned long)statp->st_size); 193 } 194} 195 196static void print_date(const struct stat *statp) 197{ 198 time_t now; 199 double diff; 200 char buf[100], *fmt; 201 202 if (time(&now) == -1) { 203 printf(" ????????????"); 204 return; 205 } 206 diff = difftime(now, statp->st_mtime); 207 if (diff < 0 || diff > 60 * 60 * 24 * 182.5) 208 fmt = "%b %e %Y"; 209 else 210 fmt = "%b %e %H:%M"; 211 strftime(buf, sizeof(buf), fmt, localtime(&statp->st_mtime)); 212 printf(" %s", buf); 213} 214 215static void print_flags(char *path, afpvol_t *vol, const struct stat *st) 216{ 217 int adflags = 0; 218 struct adouble ad; 219 char *FinderInfo; 220 uint16_t FinderFlags; 221 uint16_t AFPattributes; 222 char type[5] = "----"; 223 char creator[5] = "----"; 224 int i; 225 uint32_t cnid; 226 227 if (S_ISDIR(st->st_mode)) 228 adflags = ADFLAGS_DIR; 229 230 if (vol->vol == NULL || vol->vol->v_path == NULL) 231 return; 232 233 ad_init(&ad, vol->vol); 234 235 if ( ad_metadata(path, adflags, &ad) < 0 ) 236 return; 237 238 FinderInfo = ad_entry(&ad, ADEID_FINDERI); 239 240 memcpy(&FinderFlags, FinderInfo + 8, 2); 241 FinderFlags = ntohs(FinderFlags); 242 243 memcpy(type, FinderInfo, 4); 244 memcpy(creator, FinderInfo + 4, 4); 245 246 ad_getattr(&ad, &AFPattributes); 247 AFPattributes = ntohs(AFPattributes); 248 249 /* 250 Finder flags. Lowercase means valid, uppercase means invalid because 251 object is a dir and flag is only valid for files. 252 */ 253 putchar(' '); 254 if (FinderFlags & FINDERINFO_ISONDESK) 255 putchar('d'); 256 else 257 putchar('-'); 258 259 if (FinderFlags & FINDERINFO_HIDEEXT) 260 putchar('e'); 261 else 262 putchar('-'); 263 264 if (FinderFlags & FINDERINFO_ISHARED) { 265 if (adflags & ADFLAGS_DIR) 266 putchar('M'); 267 else 268 putchar('m'); 269 } else 270 putchar('-'); 271 272 if (FinderFlags & FINDERINFO_HASNOINITS) { 273 if (adflags & ADFLAGS_DIR) 274 putchar('N'); 275 else 276 putchar('n'); 277 } else 278 putchar('-'); 279 280 if (FinderFlags & FINDERINFO_HASBEENINITED) 281 putchar('i'); 282 else 283 putchar('-'); 284 285 if (FinderFlags & FINDERINFO_HASCUSTOMICON) 286 putchar('c'); 287 else 288 putchar('-'); 289 290 if (FinderFlags & FINDERINFO_ISSTATIONNERY) { 291 if (adflags & ADFLAGS_DIR) 292 putchar('T'); 293 else 294 putchar('t'); 295 } else 296 putchar('-'); 297 298 if (FinderFlags & FINDERINFO_NAMELOCKED) 299 putchar('s'); 300 else 301 putchar('-'); 302 303 if (FinderFlags & FINDERINFO_HASBUNDLE) 304 putchar('b'); 305 else 306 putchar('-'); 307 308 if (FinderFlags & FINDERINFO_INVISIBLE) 309 putchar('v'); 310 else 311 putchar('-'); 312 313 if (FinderFlags & FINDERINFO_ISALIAS) 314 putchar('a'); 315 else 316 putchar('-'); 317 318 putchar(' '); 319 320 /* AFP attributes */ 321 if (AFPattributes & ATTRBIT_SYSTEM) 322 putchar('y'); 323 else 324 putchar('-'); 325 326 if (AFPattributes & ATTRBIT_NOWRITE) { 327 if (adflags & ADFLAGS_DIR) 328 putchar('W'); 329 else 330 putchar('w'); 331 } else 332 putchar('-'); 333 334 if (AFPattributes & ATTRBIT_BACKUP) 335 putchar('p'); 336 else 337 putchar('-'); 338 339 if (AFPattributes & ATTRBIT_NORENAME) 340 putchar('r'); 341 else 342 putchar('-'); 343 344 if (AFPattributes & ATTRBIT_NODELETE) 345 putchar('l'); 346 else 347 putchar('-'); 348 349 if (AFPattributes & ATTRBIT_NOCOPY) { 350 if (adflags & ADFLAGS_DIR) 351 putchar('O'); 352 else 353 putchar('o'); 354 } else 355 putchar('-'); 356 357 /* Color */ 358 printf(" %s ", labels[(FinderFlags & FINDERINFO_COLOR) >> 1]); 359 360 /* Type & Creator */ 361 for(i=0; i<4; i++) { 362 if (isalnum(type[i])) 363 putchar(type[i]); 364 else 365 putchar('-'); 366 } 367 putchar(' '); 368 for(i=0; i<4; i++) { 369 if (isalnum(creator[i])) 370 putchar(creator[i]); 371 else 372 putchar('-'); 373 } 374 putchar(' '); 375 376 /* CNID */ 377 cnid = ad_forcegetid(&ad); 378 if (cnid) 379 printf(" %10u ", ntohl(cnid)); 380 else 381 printf(" !ADVOL_CACHE "); 382 383 ad_close(&ad, ADFLAGS_HF); 384} 385 386#define TYPE(b) ((st->st_mode & (S_IFMT)) == (b)) 387#define MODE(b) ((st->st_mode & (b)) == (b)) 388 389static void print_mode(const struct stat *st) 390{ 391 if (TYPE(S_IFBLK)) 392 putchar('b'); 393 else if (TYPE(S_IFCHR)) 394 putchar('c'); 395 else if (TYPE(S_IFDIR)) 396 putchar('d'); 397 else if (TYPE(S_IFIFO)) 398 putchar('p'); 399 else if (TYPE(S_IFREG)) 400 putchar('-'); 401 else if (TYPE(S_IFLNK)) 402 putchar('l'); 403 else if (TYPE(S_IFSOCK)) 404 putchar('s'); 405 else 406 putchar('?'); 407 putchar(MODE(S_IRUSR) ? 'r' : '-'); 408 putchar(MODE(S_IWUSR) ? 'w' : '-'); 409 if (MODE(S_ISUID)) { 410 if (MODE(S_IXUSR)) 411 putchar('s'); 412 else 413 putchar('S'); 414 } 415 else if (MODE(S_IXUSR)) 416 putchar('x'); 417 else 418 putchar('-'); 419 putchar(MODE(S_IRGRP) ? 'r' : '-'); 420 putchar(MODE(S_IWGRP) ? 'w' : '-'); 421 if (MODE(S_ISGID)) { 422 if (MODE(S_IXGRP)) 423 putchar('s'); 424 else 425 putchar('S'); 426 } 427 else if (MODE(S_IXGRP)) 428 putchar('x'); 429 else 430 putchar('-'); 431 putchar(MODE(S_IROTH) ? 'r' : '-'); 432 putchar(MODE(S_IWOTH) ? 'w' : '-'); 433 if (MODE(S_IFDIR) && MODE(S_ISVTX)) { 434 if (MODE(S_IXOTH)) 435 putchar('t'); 436 else 437 putchar('T'); 438 } 439 else if (MODE(S_IXOTH)) 440 putchar('x'); 441 else 442 putchar('-'); 443} 444#undef TYPE 445#undef MODE 446 447static int ad_print(char *path, const struct stat *st, afpvol_t *vol) 448{ 449 if ( ! ls_l) { 450 printf("%s ", path); 451 if (ls_d) 452 printf("\n"); 453 return 0; 454 } 455 456 /* Long output */ 457 if (ls_u) { 458 print_mode(st); 459 print_numlinks(st); 460 print_owner(st); 461 print_group(st); 462 print_size(st); 463 print_date(st); 464 } 465 print_flags(path, vol, st); 466 printf(" %s\n", path); 467 468 469 return 0; 470} 471 472static int ad_ls_r(char *path, afpvol_t *vol) 473{ 474 int ret = 0, cwd, dirprinted = 0, dirempty; 475 const char *name; 476 char *tmp; 477 static char cwdpath[MAXPATHLEN+1]; 478 DIR *dp; 479 struct dirent *ep; 480 static struct stat st; /* Save some stack space */ 481 482 if ( first) 483 cwdpath[0] = 0; 484 else 485 strcat(cwdpath, "/"); 486 487 strcat(cwdpath, path); 488 first = 0; 489 490 if (lstat(path, &st) < 0) { 491 perror("Can't stat"); 492 return -1; 493 } 494 /* If its a file or a dir with -d option call ad_print and return */ 495 if (S_ISREG(st.st_mode) || ls_d) 496 return ad_print(path, &st, vol); 497 498 /* Its a dir: chdir to it remembering where we started */ 499 if ((cwd = open(".", O_RDONLY)) == -1) { 500 perror("Cant open ."); 501 return -1; 502 } 503 if (chdir(path) != 0) { 504 perror("Cant chdir"); 505 close(cwd); 506 return -1; 507 } 508 509 if ((dp = opendir (".")) == NULL) { 510 perror("Couldn't opendir ."); 511 return -1; 512 } 513 514 /* First run: print everything */ 515 dirempty = 1; 516 while ((ep = readdir (dp))) { 517 if (alarmed) { 518 ret = -1; 519 goto exit; 520 } 521 522 /* Check if its "." or ".." */ 523 if (DIR_DOT_OR_DOTDOT(ep->d_name)) 524 continue; 525 526 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */ 527 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) 528 continue; 529 530 if ((ep->d_name[0] == '.') && ! ls_a) 531 continue; 532 533 dirempty = 0; 534 535 if (recursion && ! dirprinted) { 536 printf("\n%s:\n", cwdpath); 537 dirprinted = 1; 538 } 539 540 if (lstat(ep->d_name, &st) < 0) { 541 perror("Can't stat"); 542 return -1; 543 } 544 545 ret = ad_print(ep->d_name, &st, vol); 546 if (ret != 0) 547 goto exit; 548 } 549 550 if (! ls_l && ! dirempty) 551 printf("\n"); 552 553 /* Second run: recurse to dirs */ 554 if (ls_R) { 555 rewinddir(dp); 556 while ((ep = readdir (dp))) { 557 if (alarmed) { 558 ret = -1; 559 goto exit; 560 } 561 562 /* Check if its "." or ".." */ 563 if (DIR_DOT_OR_DOTDOT(ep->d_name)) 564 continue; 565 566 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */ 567 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) 568 continue; 569 570 if ((ret = lstat(ep->d_name, &st)) < 0) { 571 perror("Can't stat"); 572 goto exit; 573 } 574 575 /* Recursion */ 576 if (S_ISDIR(st.st_mode)) { 577 recursion = 1; 578 ret = ad_ls_r(ep->d_name, vol); 579 } 580 if (ret != 0) 581 goto exit; 582 } 583 } 584 585exit: 586 closedir(dp); 587 fchdir(cwd); 588 close(cwd); 589 590 tmp = strrchr(cwdpath, '/'); 591 if (tmp) 592 *tmp = 0; 593 594 return ret; 595} 596 597int ad_ls(int argc, char **argv, AFPObj *obj) 598{ 599 int c, firstarg; 600 afpvol_t vol; 601 struct stat st; 602 603 while ((c = getopt(argc, argv, ":adlRu")) != -1) { 604 switch(c) { 605 case 'a': 606 ls_a = 1; 607 break; 608 case 'd': 609 ls_d = 1; 610 break; 611 case 'l': 612 ls_l = 1; 613 break; 614 case 'R': 615 ls_R = 1; 616 break; 617 case 'u': 618 ls_u = 1; 619 break; 620 case ':': 621 case '?': 622 usage_ls(); 623 return -1; 624 break; 625 } 626 627 } 628 629 set_signal(); 630 cnid_init(); 631 632 if ((argc - optind) == 0) { 633 openvol(obj, ".", &vol); 634 ad_ls_r(".", &vol); 635 closevol(&vol); 636 } 637 else { 638 int havefile = 0; 639 640 firstarg = optind; 641 642 /* First run: only print files from argv paths */ 643 while(optind < argc) { 644 if (stat(argv[optind], &st) != 0) 645 goto next; 646 if (S_ISDIR(st.st_mode)) 647 goto next; 648 649 havefile = 1; 650 first = 1; 651 recursion = 0; 652 653 openvol(obj, argv[optind], &vol); 654 ad_ls_r(argv[optind], &vol); 655 closevol(&vol); 656 next: 657 optind++; 658 } 659 if (havefile && (! ls_l)) 660 printf("\n"); 661 662 /* Second run: print dirs */ 663 optind = firstarg; 664 while(optind < argc) { 665 if (stat(argv[optind], &st) != 0) 666 goto next2; 667 if ( ! S_ISDIR(st.st_mode)) 668 goto next2; 669 if ((optind > firstarg) || havefile) 670 printf("\n%s:\n", argv[optind]); 671 672 first = 1; 673 recursion = 0; 674 675 openvol(obj, argv[optind], &vol); 676 ad_ls_r(argv[optind], &vol); 677 closevol(&vol); 678 679 next2: 680 optind++; 681 } 682 683 684 } 685 686 return 0; 687} 688