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