1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <auth.h> 5 #include <ip.h> 6 #include <libsec.h> 7 #include <String.h> 8 9 #include "glob.h" 10 11 enum 12 { 13 /* telnet control character */ 14 Iac= 255, 15 16 /* representation types */ 17 Tascii= 0, 18 Timage= 1, 19 20 /* transmission modes */ 21 Mstream= 0, 22 Mblock= 1, 23 Mpage= 2, 24 25 /* file structure */ 26 Sfile= 0, 27 Sblock= 1, 28 Scompressed= 2, 29 30 /* read/write buffer size */ 31 Nbuf= 4096, 32 33 /* maximum ms we'll wait for a command */ 34 Maxwait= 1000*60*30, /* inactive for 30 minutes, we hang up */ 35 36 Maxerr= 128, 37 Maxpath= 512, 38 }; 39 40 int abortcmd(char*); 41 int appendcmd(char*); 42 int cdupcmd(char*); 43 int cwdcmd(char*); 44 int delcmd(char*); 45 int helpcmd(char*); 46 int listcmd(char*); 47 int mdtmcmd(char*); 48 int mkdircmd(char*); 49 int modecmd(char*); 50 int namelistcmd(char*); 51 int nopcmd(char*); 52 int passcmd(char*); 53 int pasvcmd(char*); 54 int portcmd(char*); 55 int pwdcmd(char*); 56 int quitcmd(char*); 57 int rnfrcmd(char*); 58 int rntocmd(char*); 59 int reply(char*, ...); 60 int restartcmd(char*); 61 int retrievecmd(char*); 62 int sizecmd(char*); 63 int storecmd(char*); 64 int storeucmd(char*); 65 int structcmd(char*); 66 int systemcmd(char*); 67 int typecmd(char*); 68 int usercmd(char*); 69 70 int dialdata(void); 71 char* abspath(char*); 72 int crlfwrite(int, char*, int); 73 int sodoff(void); 74 int accessok(char*); 75 76 typedef struct Cmd Cmd; 77 struct Cmd 78 { 79 char *name; 80 int (*f)(char*); 81 int needlogin; 82 }; 83 84 Cmd cmdtab[] = 85 { 86 { "abor", abortcmd, 0, }, 87 { "appe", appendcmd, 1, }, 88 { "cdup", cdupcmd, 1, }, 89 { "cwd", cwdcmd, 1, }, 90 { "dele", delcmd, 1, }, 91 { "help", helpcmd, 0, }, 92 { "list", listcmd, 1, }, 93 { "mdtm", mdtmcmd, 1, }, 94 { "mkd", mkdircmd, 1, }, 95 { "mode", modecmd, 0, }, 96 { "nlst", namelistcmd, 1, }, 97 { "noop", nopcmd, 0, }, 98 { "pass", passcmd, 0, }, 99 { "pasv", pasvcmd, 1, }, 100 { "pwd", pwdcmd, 0, }, 101 { "port", portcmd, 1, }, 102 { "quit", quitcmd, 0, }, 103 { "rest", restartcmd, 1, }, 104 { "retr", retrievecmd, 1, }, 105 { "rmd", delcmd, 1, }, 106 { "rnfr", rnfrcmd, 1, }, 107 { "rnto", rntocmd, 1, }, 108 { "size", sizecmd, 1, }, 109 { "stor", storecmd, 1, }, 110 { "stou", storeucmd, 1, }, 111 { "stru", structcmd, 1, }, 112 { "syst", systemcmd, 0, }, 113 { "type", typecmd, 0, }, 114 { "user", usercmd, 0, }, 115 { 0, 0, 0 }, 116 }; 117 118 #define NONENS "/lib/namespace.ftp" /* default ns for none */ 119 120 char user[Maxpath]; /* logged in user */ 121 char curdir[Maxpath]; /* current directory path */ 122 Chalstate *ch; 123 int loggedin; 124 int type; /* transmission type */ 125 int mode; /* transmission mode */ 126 int structure; /* file structure */ 127 char data[64]; /* data address */ 128 int pid; /* transfer process */ 129 int encryption; /* encryption state */ 130 int isnone, anon_ok, anon_only; 131 char cputype[Maxpath]; /* the environment variable of the same name */ 132 char bindir[Maxpath]; /* bin directory for this architecture */ 133 char mailaddr[Maxpath]; 134 char *namespace = NONENS; 135 int debug; 136 NetConnInfo *nci; 137 int createperm = 0660; 138 int isnoworld; 139 vlong offset; /* from restart command */ 140 141 ulong id; 142 143 typedef struct Passive Passive; 144 struct Passive 145 { 146 int inuse; 147 char adir[40]; 148 int afd; 149 int port; 150 uchar ipaddr[IPaddrlen]; 151 } passive; 152 153 #define FTPLOG "ftp" 154 155 void 156 logit(char *fmt, ...) 157 { 158 char buf[8192]; 159 va_list arg; 160 char errstr[128]; 161 162 rerrstr(errstr, sizeof errstr); 163 va_start(arg, fmt); 164 vseprint(buf, buf+sizeof(buf), fmt, arg); 165 va_end(arg); 166 syslog(0, FTPLOG, "%s.%s %s", nci->rsys, nci->rserv, buf); 167 werrstr(errstr, sizeof errstr); 168 } 169 170 /* 171 * read commands from the control stream and dispatch 172 */ 173 void 174 main(int argc, char **argv) 175 { 176 char *cmd; 177 char *arg; 178 char *p; 179 Cmd *t; 180 Biobuf in; 181 int i; 182 183 ARGBEGIN{ 184 case 'd': 185 debug++; 186 break; 187 case 'a': /* anonymous OK */ 188 anon_ok = 1; 189 break; 190 case 'A': 191 anon_ok = 1; 192 anon_only = 1; 193 break; 194 case 'n': 195 namespace = ARGF(); 196 break; 197 }ARGEND 198 199 /* open log file before doing a newns */ 200 syslog(0, FTPLOG, nil); 201 202 /* find out who is calling */ 203 if(argc < 1) 204 nci = getnetconninfo(nil, 0); 205 else 206 nci = getnetconninfo(argv[argc-1], 0); 207 if(nci == nil) 208 sysfatal("ftpd needs a network address"); 209 210 strcpy(mailaddr, "?"); 211 id = getpid(); 212 213 /* figure out which binaries to bind in later */ 214 arg = getenv("cputype"); 215 if(arg) 216 strcpy(cputype, arg); 217 else 218 strcpy(cputype, "mips"); 219 snprint(bindir, sizeof(bindir), "/bin/%s/bin", cputype); 220 221 Binit(&in, 0, OREAD); 222 reply("220 Plan 9 FTP server ready"); 223 alarm(Maxwait); 224 while(cmd = Brdline(&in, '\n')){ 225 alarm(0); 226 227 /* 228 * strip out trailing cr's & lf and delimit with null 229 */ 230 i = Blinelen(&in)-1; 231 cmd[i] = 0; 232 if(debug) 233 logit("%s", cmd); 234 while(i > 0 && cmd[i-1] == '\r') 235 cmd[--i] = 0; 236 237 /* 238 * hack for GatorFTP+, look for a 0x10 used as a delimiter 239 */ 240 p = strchr(cmd, 0x10); 241 if(p) 242 *p = 0; 243 244 /* 245 * get rid of telnet control sequences (we don't need them) 246 */ 247 while(*cmd && *cmd == Iac){ 248 cmd++; 249 if(*cmd) 250 cmd++; 251 } 252 253 /* 254 * parse the message (command arg) 255 */ 256 arg = strchr(cmd, ' '); 257 if(arg){ 258 *arg++ = 0; 259 while(*arg == ' ') 260 arg++; 261 } 262 263 /* 264 * ignore blank commands 265 */ 266 if(*cmd == 0) 267 continue; 268 269 /* 270 * lookup the command and do it 271 */ 272 for(p = cmd; *p; p++) 273 *p = tolower(*p); 274 for(t = cmdtab; t->name; t++) 275 if(strcmp(cmd, t->name) == 0){ 276 if(t->needlogin && !loggedin) 277 sodoff(); 278 else if((*t->f)(arg) < 0) 279 exits(0); 280 break; 281 } 282 if(t->f != restartcmd){ 283 /* 284 * the file offset is set to zero following 285 * all commands except the restart command 286 */ 287 offset = 0; 288 } 289 if(t->name == 0){ 290 /* 291 * the OOB bytes preceding an abort from UCB machines 292 * comes out as something unrecognizable instead of 293 * IAC's. Certainly a Plan 9 bug but I can't find it. 294 * This is a major hack to avoid the problem. -- presotto 295 */ 296 i = strlen(cmd); 297 if(i > 4 && strcmp(cmd+i-4, "abor") == 0){ 298 abortcmd(0); 299 } else{ 300 logit("%s (%s) command not implemented", cmd, arg?arg:""); 301 reply("502 %s command not implemented", cmd); 302 } 303 } 304 alarm(Maxwait); 305 } 306 if(pid) 307 postnote(PNGROUP, pid, "kill"); 308 } 309 310 /* 311 * reply to a command 312 */ 313 int 314 reply(char *fmt, ...) 315 { 316 va_list arg; 317 char buf[8192], *s; 318 319 va_start(arg, fmt); 320 s = vseprint(buf, buf+sizeof(buf), fmt, arg); 321 va_end(arg); 322 if(debug){ 323 *s = 0; 324 logit("%s", buf); 325 } 326 *s++ = '\r'; 327 *s++ = '\n'; 328 write(1, buf, s - buf); 329 return 0; 330 } 331 332 int 333 sodoff(void) 334 { 335 return reply("530 Sod off, service requires login"); 336 } 337 338 /* 339 * run a command in a separate process 340 */ 341 int 342 asproc(void (*f)(char*, int), char *arg, int arg2) 343 { 344 int i; 345 346 if(pid){ 347 /* wait for previous command to finish */ 348 for(;;){ 349 i = waitpid(); 350 if(i == pid || i < 0) 351 break; 352 } 353 } 354 355 switch(pid = rfork(RFFDG|RFPROC|RFNOTEG)){ 356 case -1: 357 return reply("450 Out of processes: %r"); 358 case 0: 359 (*f)(arg, arg2); 360 exits(0); 361 default: 362 break; 363 } 364 return 0; 365 } 366 367 /* 368 * run a command to filter a tail 369 */ 370 int 371 transfer(char *cmd, char *a1, char *a2, char *a3, int image) 372 { 373 int n, dfd, fd, bytes, eofs, pid; 374 int pfd[2]; 375 char buf[Nbuf], *p; 376 Waitmsg *w; 377 378 reply("150 Opening data connection for %s (%s)", cmd, data); 379 dfd = dialdata(); 380 if(dfd < 0) 381 return reply("425 Error opening data connection: %r"); 382 383 if(pipe(pfd) < 0) 384 return reply("520 Internal Error: %r"); 385 386 bytes = 0; 387 switch(pid = rfork(RFFDG|RFPROC|RFNAMEG)){ 388 case -1: 389 return reply("450 Out of processes: %r"); 390 case 0: 391 logit("running tar %d\n", getpid()); 392 close(pfd[1]); 393 close(dfd); 394 dup(pfd[0], 1); 395 dup(pfd[0], 2); 396 if(isnone){ 397 fd = open("#s/boot", ORDWR); 398 if(fd < 0 399 || bind("#/", "/", MAFTER) < 0 400 || amount(fd, "/bin", MREPL, "") < 0 401 || bind("#c", "/dev", MAFTER) < 0 402 || bind(bindir, "/bin", MREPL) < 0) 403 exits("building name space"); 404 close(fd); 405 } 406 execl(cmd, cmd, a1, a2, a3, 0); 407 exits(cmd); 408 default: 409 close(pfd[0]); 410 eofs = 0; 411 while((n = read(pfd[1], buf, sizeof buf)) >= 0){ 412 if(n == 0){ 413 if(eofs++ > 5) 414 break; 415 else 416 continue; 417 } 418 eofs = 0; 419 p = buf; 420 if(offset > 0){ 421 if(n > offset){ 422 p = buf+offset; 423 n -= offset; 424 offset = 0; 425 } else { 426 offset -= n; 427 continue; 428 } 429 } 430 if(!image) 431 n = crlfwrite(dfd, p, n); 432 else 433 n = write(dfd, p, n); 434 if(n < 0){ 435 postnote(PNPROC, pid, "kill"); 436 bytes = -1; 437 break; 438 } 439 bytes += n; 440 } 441 close(pfd[1]); 442 close(dfd); 443 break; 444 } 445 446 /* wait for this command to finish */ 447 for(;;){ 448 w = wait(); 449 if(w == nil || w->pid == pid) 450 break; 451 free(w); 452 } 453 if(w != nil && w->msg != nil){ 454 bytes = -1; 455 logit("%s", w->msg); 456 logit("%s %s %s %s failed %s", cmd, a1?a1:"", a2?a2:"" , a3?a3:"", w->msg); 457 } 458 free(w); 459 reply("226 Transfer complete"); 460 return bytes; 461 } 462 463 464 /* 465 * just reply OK 466 */ 467 int 468 nopcmd(char *arg) 469 { 470 USED(arg); 471 reply("510 Plan 9 FTP daemon still alive"); 472 return 0; 473 } 474 475 /* 476 * login as user 477 */ 478 int 479 loginuser(char *user, char *nsfile, int gotoslash) 480 { 481 logit("login %s %s %s %s", user, mailaddr, nci->rsys, nsfile); 482 if(nsfile != nil && newns(user, nsfile) < 0){ 483 logit("namespace file %s does not exist", nsfile); 484 return reply("530 Not logged in: login out of service"); 485 return -1; 486 } 487 getwd(curdir, sizeof(curdir)); 488 if(gotoslash){ 489 chdir("/"); 490 strcpy(curdir, "/"); 491 } 492 putenv("service", "ftp"); 493 loggedin = 1; 494 if(debug == 0) 495 reply("230- If you have problems, send mail to 'postmaster'."); 496 return reply("230 Logged in"); 497 } 498 499 /* 500 * get a user id, reply with a challenge. The users 'anonymous' 501 * and 'ftp' are equivalent to 'none'. The user 'none' requires 502 * no challenge. 503 */ 504 int 505 usercmd(char *name) 506 { 507 logit("user %s %s", name, nci->rsys); 508 if(loggedin) 509 return reply("530 Already logged in as %s", user); 510 if(name == 0 || *name == 0) 511 return reply("530 user command needs user name"); 512 isnoworld = 0; 513 if(*name == ':'){ 514 debug = 1; 515 name++; 516 } 517 strncpy(user, name, sizeof(user)); 518 if(debug) 519 logit("debugging"); 520 user[sizeof(user)-1] = 0; 521 if(strcmp(user, "anonymous") == 0 || strcmp(user, "ftp") == 0) 522 strcpy(user, "none"); 523 if(strcmp(user, "*none") == 0){ 524 if(!anon_ok) 525 return reply("530 Not logged in: anonymous disallowed"); 526 return loginuser("none", namespace, 1); 527 } 528 if(strcmp(user, "none") == 0){ 529 if(!anon_ok) 530 return reply("530 Not logged in: anonymous disallowed"); 531 return reply("331 Send email address as password"); 532 } 533 if(anon_only) 534 return reply("530 Not logged in: anonymous access only"); 535 isnoworld = noworld(name); 536 if(isnoworld) 537 return reply("331 OK"); 538 if(ch) 539 auth_freechal(ch); 540 if((ch = auth_challenge("proto=p9cr role=server user=%q", user)) == nil) 541 return reply("421 %r"); 542 return reply("331 encrypt challenge, %s, as a password", ch->chal); 543 } 544 545 /* 546 * get a password, set up user if it works. 547 */ 548 int 549 passcmd(char *response) 550 { 551 char namefile[128]; 552 AuthInfo *ai; 553 554 if(response == nil) 555 response = ""; 556 557 if(strcmp(user, "none") == 0 || strcmp(user, "*none") == 0){ 558 /* for none, accept anything as a password */ 559 isnone = 1; 560 strncpy(mailaddr, response, sizeof(mailaddr)-1); 561 return loginuser("none", namespace, 1); 562 } 563 564 if(isnoworld){ 565 /* noworld gets a password in the clear */ 566 if(login(user, response, "/lib/namespace.noworld") < 0) 567 return reply("530 Not logged in"); 568 createperm = 0664; 569 /* login has already setup the namespace */ 570 return loginuser(user, nil, 0); 571 } else { 572 /* for everyone else, do challenge response */ 573 if(ch == nil) 574 return reply("531 Send user id before encrypted challenge"); 575 ch->resp = response; 576 ch->nresp = strlen(response); 577 ai = auth_response(ch); 578 if(ai == nil) 579 return reply("530 Not logged in: %r"); 580 if(auth_chuid(ai, nil) < 0) 581 return reply("530 Not logged in: %r"); 582 auth_freechal(ch); 583 ch = nil; 584 585 /* if the user has specified a namespace for ftp, use it */ 586 snprint(namefile, sizeof(namefile), "/usr/%s/lib/namespace.ftp", user); 587 strcpy(mailaddr, user); 588 createperm = 0660; 589 if(access(namefile, 0) == 0) 590 return loginuser(user, namefile, 0); 591 else 592 return loginuser(user, "/lib/namespace", 0); 593 } 594 } 595 596 /* 597 * print working directory 598 */ 599 int 600 pwdcmd(char *arg) 601 { 602 if(arg) 603 return reply("550 Pwd takes no argument"); 604 return reply("257 \"%s\" is the current directory", curdir); 605 } 606 607 /* 608 * chdir 609 */ 610 int 611 cwdcmd(char *dir) 612 { 613 char *rp; 614 char buf[Maxpath]; 615 616 /* shell cd semantics */ 617 if(dir == 0 || *dir == 0){ 618 if(isnone) 619 rp = "/"; 620 else { 621 snprint(buf, sizeof buf, "/usr/%s", user); 622 rp = buf; 623 } 624 if(accessok(rp) == 0) 625 rp = nil; 626 } else 627 rp = abspath(dir); 628 629 if(rp == nil) 630 return reply("550 Permission denied"); 631 632 if(chdir(rp) < 0) 633 return reply("550 Cwd failed: %r"); 634 strcpy(curdir, rp); 635 return reply("250 directory changed to %s", curdir); 636 } 637 638 /* 639 * chdir .. 640 */ 641 int 642 cdupcmd(char *dp) 643 { 644 USED(dp); 645 return cwdcmd(".."); 646 } 647 648 int 649 quitcmd(char *arg) 650 { 651 USED(arg); 652 reply("200 Bye"); 653 if(pid) 654 postnote(PNGROUP, pid, "kill"); 655 return -1; 656 } 657 658 int 659 typecmd(char *arg) 660 { 661 int c; 662 char *x; 663 664 x = arg; 665 if(arg == 0) 666 return reply("501 Type command needs arguments"); 667 668 while(c = *arg++){ 669 switch(tolower(c)){ 670 case 'a': 671 type = Tascii; 672 break; 673 case 'i': 674 case 'l': 675 type = Timage; 676 break; 677 case '8': 678 case ' ': 679 case 'n': 680 case 't': 681 case 'c': 682 break; 683 default: 684 return reply("501 Unimplemented type %s", x); 685 break; 686 } 687 } 688 return reply("200 Type %s", type==Tascii ? "Ascii" : "Image"); 689 } 690 691 int 692 modecmd(char *arg) 693 { 694 if(arg == 0) 695 return reply("501 Mode command needs arguments"); 696 while(*arg){ 697 switch(tolower(*arg)){ 698 case 's': 699 mode = Mstream; 700 break; 701 default: 702 return reply("501 Unimplemented mode %c", *arg); 703 break; 704 } 705 arg++; 706 } 707 return reply("200 Stream mode"); 708 } 709 710 int 711 structcmd(char *arg) 712 { 713 if(arg == 0) 714 return reply("501 Struct command needs arguments"); 715 for(; *arg; arg++){ 716 switch(tolower(*arg)){ 717 case 'f': 718 structure = Sfile; 719 break; 720 default: 721 return reply("501 Unimplemented structure %c", *arg); 722 break; 723 } 724 } 725 return reply("200 File structure"); 726 } 727 728 int 729 portcmd(char *arg) 730 { 731 char *field[7]; 732 int n; 733 734 if(arg == 0) 735 return reply("501 Port command needs arguments"); 736 n = getfields(arg, field, 7, 0, ", "); 737 if(n != 6) 738 return reply("501 Incorrect port specification"); 739 snprint(data, sizeof data, "tcp!%.3s.%.3s.%.3s.%.3s!%d", field[0], field[1], field[2], 740 field[3], atoi(field[4])*256 + atoi(field[5])); 741 return reply("200 Data port is %s", data); 742 } 743 744 int 745 mountnet(void) 746 { 747 int rv; 748 749 rv = 0; 750 751 if(bind("#/", "/", MAFTER) < 0){ 752 logit("can't bind #/ to /: %r"); 753 return reply("500 can't bind #/ to /: %r"); 754 } 755 756 if(bind(nci->spec, "/net", MBEFORE) < 0){ 757 logit("can't bind %s to /net: %r", nci->spec); 758 rv = reply("500 can't bind %s to /net: %r", nci->spec); 759 unmount("#/", "/"); 760 } 761 762 return rv; 763 } 764 765 void 766 unmountnet(void) 767 { 768 unmount(0, "/net"); 769 unmount("#/", "/"); 770 } 771 772 int 773 pasvcmd(char *arg) 774 { 775 NetConnInfo *nnci; 776 Passive *p; 777 778 USED(arg); 779 p = &passive; 780 781 if(p->inuse){ 782 close(p->afd); 783 p->inuse = 0; 784 } 785 786 if(mountnet() < 0) 787 return 0; 788 789 p->afd = announce("tcp!*!0", passive.adir); 790 if(p->afd < 0){ 791 unmountnet(); 792 return reply("500 No free ports"); 793 } 794 nnci = getnetconninfo(p->adir, -1); 795 unmountnet(); 796 797 /* parse the local address */ 798 if(debug) 799 logit("local sys is %s", nci->lsys); 800 parseip(p->ipaddr, nci->lsys); 801 if(ipcmp(p->ipaddr, v4prefix) == 0 || ipcmp(p->ipaddr, IPnoaddr) == 0) 802 parseip(p->ipaddr, nci->lsys); 803 p->port = atoi(nnci->lserv); 804 805 freenetconninfo(nnci); 806 p->inuse = 1; 807 808 return reply("227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)", 809 p->ipaddr[IPv4off+0], p->ipaddr[IPv4off+1], p->ipaddr[IPv4off+2], p->ipaddr[IPv4off+3], 810 p->port>>8, p->port&0xff); 811 } 812 813 enum 814 { 815 Narg=32, 816 }; 817 int Cflag, rflag, tflag, Rflag; 818 int maxnamelen; 819 int col; 820 821 char* 822 mode2asc(int m) 823 { 824 static char asc[12]; 825 char *p; 826 827 strcpy(asc, "----------"); 828 if(DMDIR & m) 829 asc[0] = 'd'; 830 if(DMAPPEND & m) 831 asc[0] = 'a'; 832 else if(DMEXCL & m) 833 asc[3] = 'l'; 834 835 for(p = asc+1; p < asc + 10; p += 3, m<<=3){ 836 if(m & 0400) 837 p[0] = 'r'; 838 if(m & 0200) 839 p[1] = 'w'; 840 if(m & 0100) 841 p[2] = 'x'; 842 } 843 return asc; 844 } 845 void 846 listfile(Biobufhdr *b, char *name, int lflag, char *dname) 847 { 848 char ts[32]; 849 char buf[256]; 850 int n, links; 851 long now; 852 char *x; 853 Dir *d; 854 855 x = abspath(name); 856 if(x == nil) 857 return; 858 d = dirstat(x); 859 if(d == nil) 860 return; 861 if(isnone){ 862 if(strncmp(x, "/incoming/", sizeof("/incoming/")-1) != 0) 863 d->mode &= ~0222; 864 strcpy(d->uid, "none"); 865 strcpy(d->gid, "none"); 866 } 867 868 strcpy(ts, ctime(d->mtime)); 869 ts[16] = 0; 870 now = time(0); 871 if(now - d->mtime > 6*30*24*60*60) 872 memmove(ts+11, ts+23, 5); 873 if(lflag){ 874 /* Unix style long listing */ 875 if(DMDIR&d->mode){ 876 links = 2; 877 d->length = 512; 878 } else 879 links = 1; 880 881 n = snprint(buf, sizeof(buf), "%s %3d %-8s %-8s %7lld %s ", mode2asc(d->mode), links, 882 d->uid, d->gid, d->length, ts+4); 883 } else 884 n = 0; 885 if(dname) 886 n += snprint(buf+n, sizeof(buf)-n, "%s/", dname); 887 n += snprint(buf+n, sizeof(buf)-n, "%s", name); 888 if(Cflag && col + maxnamelen + 1 < 40){ 889 if(n < maxnamelen+1){ 890 memset(buf+n, ' ', maxnamelen+1-n); 891 buf[maxnamelen+1] = 0; 892 } 893 col += maxnamelen + 1; 894 } else { 895 n += snprint(buf+n, sizeof(buf)-n, "\r\n"); 896 col = 0; 897 } 898 Bwrite(b, buf, n); 899 free(d); 900 } 901 int 902 dircomp(void *va, void *vb) 903 { 904 int rv; 905 Dir *a, *b; 906 907 a = va; 908 b = vb; 909 910 if(tflag) 911 rv = b->mtime - a->mtime; 912 else 913 rv = strcmp(a->name, b->name); 914 return (rflag?-1:1)*rv; 915 } 916 void 917 listdir(char *name, Biobufhdr *b, int lflag, int *printname, Globlist *gl) 918 { 919 Dir *p; 920 int fd, n, i; 921 char *dname; 922 uvlong total; 923 924 col = 0; 925 926 fd = open(name, OREAD); 927 if(fd < 0){ 928 Bprint(b, "can't read %s: %r\r\n", name); 929 return; 930 } 931 dname = 0; 932 if(*printname){ 933 if(Rflag || lflag) 934 Bprint(b, "\r\n%s:\r\n", name); 935 else 936 dname = name; 937 } 938 n = dirreadall(fd, &p); 939 close(fd); 940 if(Cflag){ 941 for(i = 0; i < n; i++) 942 if(strlen(p[i].name) > maxnamelen) 943 maxnamelen = strlen(p[i].name); 944 } 945 946 /* Unix style total line */ 947 if(lflag){ 948 total = 0; 949 for(i = 0; i < n; i++){ 950 if(p[i].qid.type & QTDIR) 951 total += 512; 952 else 953 total += p[i].length; 954 } 955 Bprint(b, "total %ulld\r\n", total/512); 956 } 957 958 qsort(p, n, sizeof(Dir), dircomp); 959 for(i = 0; i < n; i++){ 960 if(Rflag && (p[i].qid.type & QTDIR)){ 961 *printname = 1; 962 globadd(gl, name, p[i].name); 963 } 964 listfile(b, p[i].name, lflag, dname); 965 } 966 free(p); 967 } 968 void 969 list(char *arg, int lflag) 970 { 971 Dir *d; 972 Globlist *gl; 973 Glob *g; 974 int dfd, printname; 975 int i, n, argc; 976 char *alist[Narg]; 977 char path[Maxpath]; 978 char **argv; 979 Biobufhdr bh; 980 uchar buf[512]; 981 char *p; 982 983 if(arg == 0) 984 arg = ""; 985 986 if(debug) 987 logit("ls %s (. = %s)", arg, curdir); 988 989 /* process arguments, understand /bin/ls -l option */ 990 argv = alist; 991 argv[0] = "/bin/ls"; 992 argc = getfields(arg, argv+1, Narg-2, 1, " \t") + 1; 993 argv[argc] = 0; 994 rflag = 0; 995 tflag = 0; 996 Rflag = 0; 997 Cflag = 0; 998 col = 0; 999 ARGBEGIN{ 1000 case 'l': 1001 lflag++; 1002 break; 1003 case 'R': 1004 Rflag++; 1005 break; 1006 case 'C': 1007 Cflag++; 1008 break; 1009 case 'r': 1010 rflag++; 1011 break; 1012 case 't': 1013 tflag++; 1014 break; 1015 }ARGEND; 1016 if(Cflag) 1017 lflag = 0; 1018 1019 dfd = dialdata(); 1020 if(dfd < 0){ 1021 reply("425 Error opening data connection:%r"); 1022 return; 1023 } 1024 reply("150 Opened data connection (%s)", data); 1025 1026 Binits(&bh, dfd, OWRITE, buf, sizeof(buf)); 1027 if(argc == 0){ 1028 argc = 1; 1029 argv = alist; 1030 argv[0] = "."; 1031 } 1032 for(i = 0; i < argc; i++){ 1033 chdir(curdir); 1034 strncpy(path, argv[i], sizeof(path)); 1035 path[sizeof(path)-1] = 0; 1036 gl = glob(path); 1037 if(gl == nil) 1038 continue; 1039 1040 printname = gl->first != nil && gl->first->next != nil; 1041 maxnamelen = 8; 1042 1043 if(Cflag) 1044 for(g = gl->first; g; g = g->next) 1045 if(g->glob && (n = strlen(s_to_c(g->glob))) > maxnamelen) 1046 maxnamelen = n; 1047 1048 for(g = gl->first; g; g = g->next){ 1049 if(debug) 1050 logit("glob %s", s_to_c(g->glob)); 1051 p = abspath(s_to_c(g->glob)); 1052 if(p == nil) 1053 continue; 1054 d = dirstat(p); 1055 if(d == nil) 1056 continue; 1057 if(d->qid.type & QTDIR) 1058 listdir(s_to_c(g->glob), &bh, lflag, &printname, gl); 1059 else 1060 listfile(&bh, s_to_c(g->glob), lflag, 0); 1061 free(d); 1062 } 1063 globlistfree(gl); 1064 } 1065 if(Cflag) 1066 Bprint(&bh, "\r\n"); 1067 Bflush(&bh); 1068 close(dfd); 1069 1070 reply("226 Transfer complete (list %s)", arg); 1071 } 1072 int 1073 namelistcmd(char *arg) 1074 { 1075 return asproc(list, arg, 0); 1076 } 1077 int 1078 listcmd(char *arg) 1079 { 1080 return asproc(list, arg, 1); 1081 } 1082 1083 /* 1084 * return the size of the file 1085 */ 1086 int 1087 sizecmd(char *arg) 1088 { 1089 Dir *d; 1090 int rv; 1091 1092 if(arg == 0) 1093 return reply("501 Size command requires pathname"); 1094 arg = abspath(arg); 1095 d = dirstat(arg); 1096 if(d == nil) 1097 return reply("501 %r accessing %s", arg); 1098 rv = reply("213 %lld", d->length); 1099 free(d); 1100 return rv; 1101 } 1102 1103 /* 1104 * return the modify time of the file 1105 */ 1106 int 1107 mdtmcmd(char *arg) 1108 { 1109 Dir *d; 1110 Tm *t; 1111 int rv; 1112 1113 if(arg == 0) 1114 return reply("501 Mdtm command requires pathname"); 1115 if(arg == 0) 1116 return reply("550 Permission denied"); 1117 d = dirstat(arg); 1118 if(d == nil) 1119 return reply("501 %r accessing %s", arg); 1120 t = gmtime(d->mtime); 1121 rv = reply("213 %4.4d%2.2d%2.2d%2.2d%2.2d%2.2d", 1122 t->year+1900, t->mon+1, t->mday, 1123 t->hour, t->min, t->sec); 1124 free(d); 1125 return rv; 1126 } 1127 1128 /* 1129 * set an offset to start reading a file from 1130 * only lasts for one command 1131 */ 1132 int 1133 restartcmd(char *arg) 1134 { 1135 if(arg == 0) 1136 return reply("501 Restart command requires offset"); 1137 offset = atoll(arg); 1138 if(offset < 0){ 1139 offset = 0; 1140 return reply("501 Bad offset"); 1141 } 1142 1143 return reply("350 Restarting at %lld. Send STORE or RETRIEVE", offset); 1144 } 1145 1146 /* 1147 * send a file to the user 1148 */ 1149 int 1150 crlfwrite(int fd, char *p, int n) 1151 { 1152 char *ep, *np; 1153 char buf[2*Nbuf]; 1154 1155 for(np = buf, ep = p + n; p < ep; p++){ 1156 if(*p == '\n') 1157 *np++ = '\r'; 1158 *np++ = *p; 1159 } 1160 if(write(fd, buf, np - buf) == np - buf) 1161 return n; 1162 else 1163 return -1; 1164 } 1165 void 1166 retrievedir(char *arg) 1167 { 1168 int n; 1169 char *p; 1170 char file[Maxpath]; 1171 1172 if(type != Timage){ 1173 reply("550 This file requires type binary/image"); 1174 return; 1175 } 1176 1177 strcpy(file, arg); 1178 p = strrchr(arg, '/'); 1179 if(p != arg){ 1180 *p++ = 0; 1181 chdir(arg); 1182 } else { 1183 chdir("/"); 1184 p = arg+1; 1185 } 1186 1187 n = transfer("/bin/tar", "c", p, 0, 1); 1188 if(n < 0) 1189 logit("get %s failed", file); 1190 else 1191 logit("get %s OK %d", file, n); 1192 } 1193 void 1194 retrieve(char *arg, int arg2) 1195 { 1196 int dfd, fd, n, i, bytes; 1197 Dir *d; 1198 char buf[Nbuf]; 1199 char *p, *ep; 1200 1201 USED(arg2); 1202 1203 p = strchr(arg, '\r'); 1204 if(p){ 1205 logit("cr in file name", arg); 1206 *p = 0; 1207 } 1208 1209 fd = open(arg, OREAD); 1210 if(fd == -1){ 1211 n = strlen(arg); 1212 if(n > 4 && strcmp(arg+n-4, ".tar") == 0){ 1213 *(arg+n-4) = 0; 1214 d = dirstat(arg); 1215 if(d != nil){ 1216 if(d->qid.type & QTDIR){ 1217 retrievedir(arg); 1218 free(d); 1219 return; 1220 } 1221 free(d); 1222 } 1223 } 1224 logit("get %s failed", arg); 1225 reply("550 Error opening %s: %r", arg); 1226 return; 1227 } 1228 if(offset != 0) 1229 if(seek(fd, offset, 0) < 0){ 1230 reply("550 %s: seek to %lld failed", arg, offset); 1231 close(fd); 1232 return; 1233 } 1234 d = dirfstat(fd); 1235 if(d != nil){ 1236 if(d->qid.type & QTDIR){ 1237 reply("550 %s: not a plain file.", arg); 1238 close(fd); 1239 free(d); 1240 return; 1241 } 1242 free(d); 1243 } 1244 1245 n = read(fd, buf, sizeof(buf)); 1246 if(n < 0){ 1247 logit("get %s failed", arg, mailaddr, nci->rsys); 1248 reply("550 Error reading %s: %r", arg); 1249 close(fd); 1250 return; 1251 } 1252 1253 if(type != Timage) 1254 for(p = buf, ep = &buf[n]; p < ep; p++) 1255 if(*p & 0x80){ 1256 close(fd); 1257 reply("550 This file requires type binary/image"); 1258 return; 1259 } 1260 1261 reply("150 Opening data connection for %s (%s)", arg, data); 1262 dfd = dialdata(); 1263 if(dfd < 0){ 1264 reply("425 Error opening data connection:%r"); 1265 close(fd); 1266 return; 1267 } 1268 1269 bytes = 0; 1270 do { 1271 switch(type){ 1272 case Timage: 1273 i = write(dfd, buf, n); 1274 break; 1275 default: 1276 i = crlfwrite(dfd, buf, n); 1277 break; 1278 } 1279 if(i != n){ 1280 close(fd); 1281 close(dfd); 1282 logit("get %s %r to data connection after %d", arg, bytes); 1283 reply("550 Error writing to data connection: %r"); 1284 return; 1285 } 1286 bytes += n; 1287 } while((n = read(fd, buf, sizeof(buf))) > 0); 1288 1289 if(n < 0) 1290 logit("get %s %r after %d", arg, bytes); 1291 1292 close(fd); 1293 close(dfd); 1294 reply("226 Transfer complete"); 1295 logit("get %s OK %d", arg, bytes); 1296 } 1297 int 1298 retrievecmd(char *arg) 1299 { 1300 if(arg == 0) 1301 return reply("501 Retrieve command requires an argument"); 1302 arg = abspath(arg); 1303 if(arg == 0) 1304 return reply("550 Permission denied"); 1305 1306 return asproc(retrieve, arg, 0); 1307 } 1308 1309 /* 1310 * get a file from the user 1311 */ 1312 int 1313 lfwrite(int fd, char *p, int n) 1314 { 1315 char *ep, *np; 1316 char buf[Nbuf]; 1317 1318 for(np = buf, ep = p + n; p < ep; p++){ 1319 if(*p != '\r') 1320 *np++ = *p; 1321 } 1322 if(write(fd, buf, np - buf) == np - buf) 1323 return n; 1324 else 1325 return -1; 1326 } 1327 void 1328 store(char *arg, int fd) 1329 { 1330 int dfd, n, i; 1331 char buf[Nbuf]; 1332 1333 reply("150 Opening data connection for %s (%s)", arg, data); 1334 dfd = dialdata(); 1335 if(dfd < 0){ 1336 reply("425 Error opening data connection:%r"); 1337 close(fd); 1338 return; 1339 } 1340 1341 while((n = read(dfd, buf, sizeof(buf))) > 0){ 1342 switch(type){ 1343 case Timage: 1344 i = write(fd, buf, n); 1345 break; 1346 default: 1347 i = lfwrite(fd, buf, n); 1348 break; 1349 } 1350 if(i != n){ 1351 close(fd); 1352 close(dfd); 1353 reply("550 Error writing file"); 1354 return; 1355 } 1356 } 1357 close(fd); 1358 close(dfd); 1359 logit("put %s OK", arg); 1360 reply("226 Transfer complete"); 1361 } 1362 int 1363 storecmd(char *arg) 1364 { 1365 int fd, rv; 1366 1367 if(arg == 0) 1368 return reply("501 Store command requires an argument"); 1369 arg = abspath(arg); 1370 if(arg == 0) 1371 return reply("550 Permission denied"); 1372 if(isnone && strncmp(arg, "/incoming/", sizeof("/incoming/")-1)) 1373 return reply("550 Permission denied"); 1374 if(offset){ 1375 fd = open(arg, OWRITE); 1376 if(fd == -1) 1377 return reply("550 Error opening %s: %r", arg); 1378 if(seek(fd, offset, 0) == -1) 1379 return reply("550 Error seeking %s to %d: %r", 1380 arg, offset); 1381 } else { 1382 fd = create(arg, OWRITE, createperm); 1383 if(fd == -1) 1384 return reply("550 Error creating %s: %r", arg); 1385 } 1386 1387 rv = asproc(store, arg, fd); 1388 close(fd); 1389 return rv; 1390 } 1391 int 1392 appendcmd(char *arg) 1393 { 1394 int fd, rv; 1395 1396 if(arg == 0) 1397 return reply("501 Append command requires an argument"); 1398 if(isnone) 1399 return reply("550 Permission denied"); 1400 arg = abspath(arg); 1401 if(arg == 0) 1402 return reply("550 Error creating %s: Permission denied", arg); 1403 fd = open(arg, OWRITE); 1404 if(fd == -1){ 1405 fd = create(arg, OWRITE, createperm); 1406 if(fd == -1) 1407 return reply("550 Error creating %s: %r", arg); 1408 } 1409 seek(fd, 0, 2); 1410 1411 rv = asproc(store, arg, fd); 1412 close(fd); 1413 return rv; 1414 } 1415 int 1416 storeucmd(char *arg) 1417 { 1418 int fd, rv; 1419 char name[Maxpath]; 1420 1421 USED(arg); 1422 if(isnone) 1423 return reply("550 Permission denied"); 1424 strcpy(name, "ftpXXXXXXXXXXX"); 1425 mktemp(name); 1426 fd = create(name, OWRITE, createperm); 1427 if(fd == -1) 1428 return reply("550 Error creating %s: %r", name); 1429 1430 rv = asproc(store, name, fd); 1431 close(fd); 1432 return rv; 1433 } 1434 1435 int 1436 mkdircmd(char *name) 1437 { 1438 int fd; 1439 1440 if(name == 0) 1441 return reply("501 Mkdir command requires an argument"); 1442 if(isnone) 1443 return reply("550 Permission denied"); 1444 name = abspath(name); 1445 if(name == 0) 1446 return reply("550 Permission denied"); 1447 fd = create(name, OREAD, DMDIR|0775); 1448 if(fd < 0) 1449 return reply("550 Can't create %s: %r", name); 1450 close(fd); 1451 return reply("226 %s created", name); 1452 } 1453 1454 int 1455 delcmd(char *name) 1456 { 1457 if(name == 0) 1458 return reply("501 Rmdir/delete command requires an argument"); 1459 if(isnone) 1460 return reply("550 Permission denied"); 1461 name = abspath(name); 1462 if(name == 0) 1463 return reply("550 Permission denied"); 1464 if(remove(name) < 0) 1465 return reply("550 Can't remove %s: %r", name); 1466 else 1467 return reply("226 %s removed", name); 1468 } 1469 1470 /* 1471 * kill off the last transfer (if the process still exists) 1472 */ 1473 int 1474 abortcmd(char *arg) 1475 { 1476 USED(arg); 1477 if(pid && postnote(PNGROUP, pid, "kill") == 0) 1478 reply("426 Command aborted"); 1479 return reply("226 Abort processed"); 1480 } 1481 1482 int 1483 systemcmd(char *arg) 1484 { 1485 USED(arg); 1486 return reply("215 UNIX Type: L8 Version: Plan 9"); 1487 } 1488 1489 int 1490 helpcmd(char *arg) 1491 { 1492 int i; 1493 char buf[80]; 1494 char *p, *e; 1495 1496 USED(arg); 1497 reply("214- the following commands are implemented:"); 1498 p = buf; 1499 e = buf+sizeof buf; 1500 for(i = 0; cmdtab[i].name; i++){ 1501 if((i%8) == 0){ 1502 reply("214-%s", buf); 1503 p = buf; 1504 } 1505 p = seprint(p, e, " %-5.5s", cmdtab[i].name); 1506 } 1507 if(p != buf) 1508 reply("214-%s", buf); 1509 reply("214 "); 1510 return 0; 1511 } 1512 1513 /* 1514 * renaming a file takes two commands 1515 */ 1516 static char filepath[256]; 1517 1518 int 1519 rnfrcmd(char *from) 1520 { 1521 if(isnone) 1522 return reply("550 Permission denied"); 1523 if(from == 0) 1524 return reply("501 Rename command requires an argument"); 1525 from = abspath(from); 1526 if(from == 0) 1527 return reply("550 Permission denied"); 1528 strncpy(filepath, from, sizeof(filepath)); 1529 filepath[sizeof(filepath)-1] = 0; 1530 return reply("350 Rename %s to ...", filepath); 1531 } 1532 int 1533 rntocmd(char *to) 1534 { 1535 Dir *d; 1536 char *fp; 1537 char *tp; 1538 1539 if(isnone) 1540 return reply("550 Permission denied"); 1541 if(to == 0) 1542 return reply("501 Rename command requires an argument"); 1543 to = abspath(to); 1544 if(to == 0) 1545 return reply("550 Permission denied"); 1546 if(*filepath == 0) 1547 return reply("503 Rnto must be preceeded by an rnfr"); 1548 1549 tp = strrchr(to, '/'); 1550 fp = strrchr(filepath, '/'); 1551 if((tp && fp == 0) || (fp && tp == 0) 1552 || (fp && tp && (fp-filepath != tp-to || memcmp(filepath, to, tp-to)))) 1553 return reply("550 Rename can't change directory"); 1554 if(tp) 1555 to = tp+1; 1556 1557 d = dirstat(filepath); 1558 if(d == nil) 1559 return reply("550 Can't rename %s: %r\n", filepath); 1560 d->name = to; 1561 if(dirwstat(filepath, d) < 0){ 1562 free(d); 1563 return reply("550 Can't rename %s to %s: %r\n", filepath, to); 1564 } 1565 free(d); 1566 1567 filepath[0] = 0; 1568 return reply("250 %s now %s", filepath, to); 1569 } 1570 1571 /* 1572 * to dial out we need the network file system in our 1573 * name space. 1574 */ 1575 int 1576 dialdata(void) 1577 { 1578 int fd, cfd; 1579 char ldir[40]; 1580 char err[Maxerr]; 1581 1582 if(mountnet() < 0) 1583 return -1; 1584 1585 if(!passive.inuse){ 1586 fd = dial(data, "20", 0, 0); 1587 errstr(err, sizeof err); 1588 } else { 1589 alarm(5*60*1000); 1590 cfd = listen(passive.adir, ldir); 1591 alarm(0); 1592 errstr(err, sizeof err); 1593 if(cfd < 0) 1594 return -1; 1595 fd = accept(cfd, ldir); 1596 errstr(err, sizeof err); 1597 close(cfd); 1598 } 1599 if(fd < 0) 1600 logit("can't dial %s: %s", data, err); 1601 1602 unmountnet(); 1603 werrstr(err, sizeof err); 1604 return fd; 1605 } 1606 1607 /* 1608 * to circumscribe the accessible files we have to eliminate ..'s 1609 * and resolve all names from the root. We also remove any /bin/rc 1610 * special characters to avoid later problems with executed commands. 1611 */ 1612 char *special = "`;| "; 1613 1614 char* 1615 abspath(char *origpath) 1616 { 1617 int n, c; 1618 char *p, *sp, *path; 1619 char work[Maxpath]; 1620 static char rpath[Maxpath]; 1621 1622 if(origpath == 0) 1623 *work = 0; 1624 else 1625 strncpy(work, origpath, sizeof(work)); 1626 path = work; 1627 1628 for(sp = special; *sp; sp++){ 1629 p = strchr(path, *sp); 1630 if(p) 1631 *p = 0; 1632 } 1633 1634 if(*path == '-') 1635 return path; 1636 1637 if(*path == '/') 1638 rpath[0] = 0; 1639 else 1640 strcpy(rpath, curdir); 1641 n = strlen(rpath); 1642 1643 while(*path && n < Maxpath-2){ 1644 p = strchr(path, '/'); 1645 if(p) 1646 *p++ = 0; 1647 if(strcmp(path, "..") == 0){ 1648 while(n > 1){ 1649 n--; 1650 c = rpath[n]; 1651 rpath[n] = 0; 1652 if(c == '/') 1653 break; 1654 } 1655 } else if(strcmp(path, ".") == 0) 1656 n = n; 1657 else if(n == 1) 1658 n += snprint(rpath+n, Maxpath - n, "%s", path); 1659 else 1660 n += snprint(rpath+n, Maxpath - n - 1, "/%s", path); 1661 if(p) 1662 path = p; 1663 else 1664 break; 1665 } 1666 1667 if(!accessok(rpath)) 1668 return nil; 1669 1670 return rpath; 1671 } 1672 1673 int 1674 postnote(int group, int pid, char *note) 1675 { 1676 char file[128]; 1677 int f, r; 1678 1679 switch(group) { 1680 case PNPROC: 1681 sprint(file, "#p/%d/note", pid); 1682 break; 1683 case PNGROUP: 1684 sprint(file, "#p/%d/notepg", pid); 1685 break; 1686 default: 1687 return -1; 1688 } 1689 1690 f = open(file, OWRITE); 1691 if(f < 0) 1692 return -1; 1693 1694 r = strlen(note); 1695 if(write(f, note, r) != r) { 1696 close(f); 1697 return -1; 1698 } 1699 close(f); 1700 return 0; 1701 } 1702 1703 static char *pfile = "/.httplogin"; 1704 1705 typedef struct Path Path; 1706 struct Path { 1707 Path *next; 1708 char *path; 1709 int inuse; 1710 int ok; 1711 }; 1712 1713 enum 1714 { 1715 Maxlevel = 16, 1716 Maxperlevel= 8, 1717 }; 1718 1719 Path *pathlevel[Maxlevel]; 1720 1721 Path* 1722 unlinkpath(char *path, int level) 1723 { 1724 Path **l, *p; 1725 int n; 1726 1727 n = 0; 1728 for(l = &pathlevel[level]; *l; l = &(*l)->next){ 1729 p = *l; 1730 if(strcmp(p->path, path) == 0){ 1731 *l = p->next; 1732 p->next = nil; 1733 return p; 1734 } 1735 if(++n >= Maxperlevel - 1 || p->next == nil) 1736 break; 1737 } 1738 if(*l){ 1739 /* reuse */ 1740 p = *l; 1741 *l = p->next; 1742 free(p->path); 1743 memset(p, 0, sizeof *p); 1744 1745 } else { 1746 /* allocate */ 1747 p = mallocz(sizeof *p, 1); 1748 } 1749 p->path = strdup(path); 1750 return p; 1751 } 1752 1753 void 1754 linkpath(Path *p, int level) 1755 { 1756 p->next = pathlevel[level]; 1757 pathlevel[level] = p; 1758 p->inuse = 1; 1759 } 1760 1761 void 1762 addpath(Path *p, int level, int ok) 1763 { 1764 p->ok = ok; 1765 p->next = pathlevel[level]; 1766 pathlevel[level] = p; 1767 } 1768 1769 int 1770 _accessok(char *path, int level) 1771 { 1772 Path *p; 1773 char *cp; 1774 int lvl; 1775 1776 if(level < 0) 1777 return 1; 1778 lvl = level; 1779 if(lvl >= Maxlevel) 1780 lvl = Maxlevel - 1; 1781 1782 p = unlinkpath(path, lvl); 1783 if(p->inuse){ 1784 /* move to front */ 1785 linkpath(p, lvl); 1786 return p->ok; 1787 } 1788 cp = strrchr(path, '/'); 1789 if(cp == nil) 1790 cp = path; 1791 p->path = strdup(path); 1792 strcat(path, pfile); 1793 if(access(path, AEXIST) == 0){ 1794 addpath(p, lvl, 0); 1795 return 0; 1796 } 1797 *cp = 0; 1798 addpath(p, lvl, _accessok(path, level-1)); 1799 return p->ok; 1800 } 1801 1802 /* 1803 * work down from the root 1804 */ 1805 int 1806 accessok(char *path) 1807 { 1808 int level, n; 1809 char *p; 1810 char npath[Maxpath+1]; 1811 1812 strcpy(npath, path); 1813 n = strlen(path)-1; 1814 if(npath[n] == '/') 1815 npath[n] = 0; 1816 p = npath+1; 1817 for(level = 1; p >= path && level < Maxlevel; level++){ 1818 p = strchr(p, '/'); 1819 if(p == nil) 1820 break; 1821 p++; 1822 } 1823 return _accessok(npath, level-1); 1824 } 1825