19a747e4fSDavid du Colombier #include <u.h> 29a747e4fSDavid du Colombier #include <libc.h> 39a747e4fSDavid du Colombier #include <auth.h> 49a747e4fSDavid du Colombier #include <fcall.h> 59a747e4fSDavid du Colombier #include <String.h> 69a747e4fSDavid du Colombier #include "ftpfs.h" 79a747e4fSDavid du Colombier 89a747e4fSDavid du Colombier /* an active fid */ 99a747e4fSDavid du Colombier typedef struct Fid Fid; 109a747e4fSDavid du Colombier struct Fid 119a747e4fSDavid du Colombier { 129a747e4fSDavid du Colombier int fid; 139a747e4fSDavid du Colombier Node *node; /* path to remote file */ 149a747e4fSDavid du Colombier int busy; 159a747e4fSDavid du Colombier Fid *next; 169a747e4fSDavid du Colombier int open; 179a747e4fSDavid du Colombier }; 189a747e4fSDavid du Colombier 199a747e4fSDavid du Colombier Fid *fids; /* linked list of fids */ 209a747e4fSDavid du Colombier char errstring[128]; /* error to return */ 219a747e4fSDavid du Colombier int mfd; /* fd for 9fs */ 229a747e4fSDavid du Colombier int messagesize = 4*1024*IOHDRSZ; 239a747e4fSDavid du Colombier uchar mdata[8*1024*IOHDRSZ]; 249a747e4fSDavid du Colombier uchar mbuf[8*1024*IOHDRSZ]; 259a747e4fSDavid du Colombier Fcall rhdr; 269a747e4fSDavid du Colombier Fcall thdr; 279a747e4fSDavid du Colombier int debug; 289a747e4fSDavid du Colombier int usenlst; 299a747e4fSDavid du Colombier char *ext; 309a747e4fSDavid du Colombier int quiet; 319a747e4fSDavid du Colombier int kapid = -1; 329a747e4fSDavid du Colombier int dying; /* set when any process decides to die */ 339a747e4fSDavid du Colombier int dokeepalive; 349a747e4fSDavid du Colombier 359a747e4fSDavid du Colombier char *rflush(Fid*), *rnop(Fid*), *rversion(Fid*), 369a747e4fSDavid du Colombier *rattach(Fid*), *rclone(Fid*), *rwalk(Fid*), 379a747e4fSDavid du Colombier *rclwalk(Fid*), *ropen(Fid*), *rcreate(Fid*), 389a747e4fSDavid du Colombier *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*), 399a747e4fSDavid du Colombier *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*), 409a747e4fSDavid du Colombier *rauth(Fid*);; 419a747e4fSDavid du Colombier void mountinit(char*); 429a747e4fSDavid du Colombier void io(void); 439a747e4fSDavid du Colombier int readpdir(Node*); 449a747e4fSDavid du Colombier 459a747e4fSDavid du Colombier char *(*fcalls[])(Fid*) = { 469a747e4fSDavid du Colombier [Tflush] rflush, 479a747e4fSDavid du Colombier [Tversion] rversion, 489a747e4fSDavid du Colombier [Tattach] rattach, 499a747e4fSDavid du Colombier [Tauth] rauth, 509a747e4fSDavid du Colombier [Twalk] rwalk, 519a747e4fSDavid du Colombier [Topen] ropen, 529a747e4fSDavid du Colombier [Tcreate] rcreate, 539a747e4fSDavid du Colombier [Tread] rread, 549a747e4fSDavid du Colombier [Twrite] rwrite, 559a747e4fSDavid du Colombier [Tclunk] rclunk, 569a747e4fSDavid du Colombier [Tremove] rremove, 579a747e4fSDavid du Colombier [Tstat] rstat, 589a747e4fSDavid du Colombier [Twstat] rwstat, 599a747e4fSDavid du Colombier }; 609a747e4fSDavid du Colombier 619a747e4fSDavid du Colombier OS oslist[] = { 629a747e4fSDavid du Colombier { Plan9, "Plan 9", }, 639a747e4fSDavid du Colombier { Plan9, "Plan9", }, 649a747e4fSDavid du Colombier { Plan9, "UNIX Type: L8 Version: Plan 9", }, 659a747e4fSDavid du Colombier { Unix, "SUN", }, 669a747e4fSDavid du Colombier { Unix, "UNIX", }, 679a747e4fSDavid du Colombier { VMS, "VMS", }, 689a747e4fSDavid du Colombier { VM, "VM", }, 699a747e4fSDavid du Colombier { Tops, "TOPS", }, 709a747e4fSDavid du Colombier { MVS, "MVS", }, 719a747e4fSDavid du Colombier { NetWare, "NetWare", }, 725d459b5aSDavid du Colombier { NetWare, "NETWARE", }, 739a747e4fSDavid du Colombier { OS½, "OS/2", }, 749a747e4fSDavid du Colombier { TSO, "TSO", }, 759a747e4fSDavid du Colombier { NT, "Windows_NT", }, /* DOS like interface */ 769a747e4fSDavid du Colombier { NT, "WINDOWS_NT", }, /* Unix like interface */ 779a747e4fSDavid du Colombier { Unknown, 0 }, 789a747e4fSDavid du Colombier }; 799a747e4fSDavid du Colombier 809a747e4fSDavid du Colombier char *nouid = "?uid?"; 819a747e4fSDavid du Colombier 829a747e4fSDavid du Colombier #define S2P(x) (((ulong)(x)) & 0xffffff) 839a747e4fSDavid du Colombier 849a747e4fSDavid du Colombier char *nosuchfile = "file does not exist"; 85eaa278a2SDavid du Colombier char *keyspec = ""; 869a747e4fSDavid du Colombier 879a747e4fSDavid du Colombier void 889a747e4fSDavid du Colombier usage(void) 899a747e4fSDavid du Colombier { 909a747e4fSDavid du Colombier fprint(2, "ftpfs [-/dqn] [-a passwd] [-m mountpoint] [-e ext] [-o os] [-r root] [net!]address\n"); 919a747e4fSDavid du Colombier exits("usage"); 929a747e4fSDavid du Colombier } 939a747e4fSDavid du Colombier 949a747e4fSDavid du Colombier void 959a747e4fSDavid du Colombier main(int argc, char *argv[]) 969a747e4fSDavid du Colombier { 979a747e4fSDavid du Colombier char *mountroot = 0; 989a747e4fSDavid du Colombier char *mountpoint = "/n/ftp"; 999a747e4fSDavid du Colombier char *cpassword = 0; 1009a747e4fSDavid du Colombier char *cp; 1019a747e4fSDavid du Colombier int p[2]; 1029a747e4fSDavid du Colombier OS *o; 1039a747e4fSDavid du Colombier 1049a747e4fSDavid du Colombier defos = Unix; 1059a747e4fSDavid du Colombier user = strdup(getuser()); 1069a747e4fSDavid du Colombier 1079a747e4fSDavid du Colombier ARGBEGIN { 1089a747e4fSDavid du Colombier case '/': 1099a747e4fSDavid du Colombier mountroot = "/"; 1109a747e4fSDavid du Colombier break; 1119a747e4fSDavid du Colombier case 'a': 1129a747e4fSDavid du Colombier cpassword = ARGF(); 1139a747e4fSDavid du Colombier break; 1149a747e4fSDavid du Colombier case 'd': 1159a747e4fSDavid du Colombier debug = 1; 1169a747e4fSDavid du Colombier break; 1179a747e4fSDavid du Colombier case 'k': 118eaa278a2SDavid du Colombier keyspec = EARGF(usage()); 119eaa278a2SDavid du Colombier break; 120eaa278a2SDavid du Colombier case 'K': 1219a747e4fSDavid du Colombier dokeepalive = 1; 1229a747e4fSDavid du Colombier break; 1239a747e4fSDavid du Colombier case 'm': 1249a747e4fSDavid du Colombier mountpoint = ARGF(); 1259a747e4fSDavid du Colombier break; 1269a747e4fSDavid du Colombier case 'n': 1279a747e4fSDavid du Colombier usenlst = 1; 1289a747e4fSDavid du Colombier break; 1299a747e4fSDavid du Colombier case 'e': 1309a747e4fSDavid du Colombier ext = ARGF(); 1319a747e4fSDavid du Colombier break; 1329a747e4fSDavid du Colombier case 'o': 1339a747e4fSDavid du Colombier cp = ARGF(); 1349a747e4fSDavid du Colombier for(o = oslist; o->os != Unknown; o++) 1359a747e4fSDavid du Colombier if(strncmp(cp, o->name, strlen(o->name)) == 0){ 1369a747e4fSDavid du Colombier defos = o->os; 1379a747e4fSDavid du Colombier break; 1389a747e4fSDavid du Colombier } 1399a747e4fSDavid du Colombier break; 1409a747e4fSDavid du Colombier case 'r': 1419a747e4fSDavid du Colombier mountroot = ARGF(); 1429a747e4fSDavid du Colombier break; 1439a747e4fSDavid du Colombier case 'q': 1449a747e4fSDavid du Colombier quiet = 1; 1459a747e4fSDavid du Colombier break; 1469a747e4fSDavid du Colombier } ARGEND 1479a747e4fSDavid du Colombier if(argc != 1) 1489a747e4fSDavid du Colombier usage(); 1499a747e4fSDavid du Colombier 1509a747e4fSDavid du Colombier /* get a pipe to mount and run 9fs on */ 1519a747e4fSDavid du Colombier if(pipe(p) < 0) 1529a747e4fSDavid du Colombier fatal("pipe failed: %r"); 1539a747e4fSDavid du Colombier mfd = p[0]; 1549a747e4fSDavid du Colombier 1559a747e4fSDavid du Colombier /* initial handshakes with remote side */ 1569a747e4fSDavid du Colombier hello(*argv); 1579a747e4fSDavid du Colombier if(cpassword == 0) 158eaa278a2SDavid du Colombier rlogin(*argv, keyspec); 1599a747e4fSDavid du Colombier else 1609a747e4fSDavid du Colombier clogin("anonymous", cpassword); 1619a747e4fSDavid du Colombier preamble(mountroot); 1629a747e4fSDavid du Colombier 1639a747e4fSDavid du Colombier /* start the 9fs protocol */ 1649a747e4fSDavid du Colombier switch(rfork(RFPROC|RFNAMEG|RFENVG|RFFDG|RFNOTEG|RFREND)){ 1659a747e4fSDavid du Colombier case -1: 1669a747e4fSDavid du Colombier fatal("fork: %r"); 1679a747e4fSDavid du Colombier case 0: 1689a747e4fSDavid du Colombier /* seal off standard input/output */ 1699a747e4fSDavid du Colombier close(0); 1709a747e4fSDavid du Colombier open("/dev/null", OREAD); 1719a747e4fSDavid du Colombier close(1); 1729a747e4fSDavid du Colombier open("/dev/null", OWRITE); 1739a747e4fSDavid du Colombier 1749a747e4fSDavid du Colombier close(p[1]); 1759a747e4fSDavid du Colombier fmtinstall('F', fcallfmt); /* debugging */ 1769a747e4fSDavid du Colombier io(); 1779a747e4fSDavid du Colombier quit(); 1789a747e4fSDavid du Colombier break; 1799a747e4fSDavid du Colombier default: 1809a747e4fSDavid du Colombier close(p[0]); 1819a747e4fSDavid du Colombier if(mount(p[1], -1, mountpoint, MREPL|MCREATE, "") < 0) 1829a747e4fSDavid du Colombier fatal("mount failed: %r"); 1839a747e4fSDavid du Colombier } 1849a747e4fSDavid du Colombier exits(0); 1859a747e4fSDavid du Colombier } 1869a747e4fSDavid du Colombier 1879a747e4fSDavid du Colombier /* 1889a747e4fSDavid du Colombier * lookup an fid. if not found, create a new one. 1899a747e4fSDavid du Colombier */ 1909a747e4fSDavid du Colombier Fid * 1919a747e4fSDavid du Colombier newfid(int fid) 1929a747e4fSDavid du Colombier { 1939a747e4fSDavid du Colombier Fid *f, *ff; 1949a747e4fSDavid du Colombier 1959a747e4fSDavid du Colombier ff = 0; 1969a747e4fSDavid du Colombier for(f = fids; f; f = f->next){ 1979a747e4fSDavid du Colombier if(f->fid == fid){ 1989a747e4fSDavid du Colombier if(f->busy) 1999a747e4fSDavid du Colombier return f; 2009a747e4fSDavid du Colombier else{ 2019a747e4fSDavid du Colombier ff = f; 2029a747e4fSDavid du Colombier break; 2039a747e4fSDavid du Colombier } 2049a747e4fSDavid du Colombier } else if(!ff && !f->busy) 2059a747e4fSDavid du Colombier ff = f; 2069a747e4fSDavid du Colombier } 2079a747e4fSDavid du Colombier if(ff == 0){ 2089a747e4fSDavid du Colombier ff = mallocz(sizeof(*f), 1); 2099a747e4fSDavid du Colombier ff->next = fids; 2109a747e4fSDavid du Colombier fids = ff; 2119a747e4fSDavid du Colombier } 2129a747e4fSDavid du Colombier ff->node = nil; 2139a747e4fSDavid du Colombier ff->fid = fid; 2149a747e4fSDavid du Colombier return ff; 2159a747e4fSDavid du Colombier } 2169a747e4fSDavid du Colombier 2179a747e4fSDavid du Colombier /* 2189a747e4fSDavid du Colombier * a process that sends keep alive messages to 2199a747e4fSDavid du Colombier * keep the server from shutting down the connection 2209a747e4fSDavid du Colombier */ 2219a747e4fSDavid du Colombier int 2229a747e4fSDavid du Colombier kaproc(void) 2239a747e4fSDavid du Colombier { 2249a747e4fSDavid du Colombier int pid; 2259a747e4fSDavid du Colombier 2269a747e4fSDavid du Colombier if(!dokeepalive) 2279a747e4fSDavid du Colombier return -1; 2289a747e4fSDavid du Colombier 2299a747e4fSDavid du Colombier switch(pid = rfork(RFPROC|RFMEM)){ 2309a747e4fSDavid du Colombier case -1: 2319a747e4fSDavid du Colombier return -1; 2329a747e4fSDavid du Colombier case 0: 2339a747e4fSDavid du Colombier break; 2349a747e4fSDavid du Colombier default: 2359a747e4fSDavid du Colombier return pid; 2369a747e4fSDavid du Colombier } 2379a747e4fSDavid du Colombier 2389a747e4fSDavid du Colombier while(!dying){ 2399a747e4fSDavid du Colombier sleep(5000); 2409a747e4fSDavid du Colombier nop(); 2419a747e4fSDavid du Colombier } 2429a747e4fSDavid du Colombier 2439a747e4fSDavid du Colombier _exits(0); 2449a747e4fSDavid du Colombier return -1; 2459a747e4fSDavid du Colombier } 2469a747e4fSDavid du Colombier 2479a747e4fSDavid du Colombier void 2489a747e4fSDavid du Colombier io(void) 2499a747e4fSDavid du Colombier { 250*24e2e655SDavid du Colombier char *err, buf[ERRMAX]; 2519a747e4fSDavid du Colombier int n; 2529a747e4fSDavid du Colombier 2539a747e4fSDavid du Colombier kapid = kaproc(); 2549a747e4fSDavid du Colombier 2559a747e4fSDavid du Colombier while(!dying){ 2569a747e4fSDavid du Colombier n = read9pmsg(mfd, mdata, messagesize); 257*24e2e655SDavid du Colombier if(n <= 0){ 258*24e2e655SDavid du Colombier errstr(buf, sizeof buf); 259*24e2e655SDavid du Colombier if(buf[0]=='\0' || strstr(buf, "hungup")) 260*24e2e655SDavid du Colombier exits(""); 261*24e2e655SDavid du Colombier fatal("mount read: %s\n", buf); 262*24e2e655SDavid du Colombier } 2639a747e4fSDavid du Colombier if(convM2S(mdata, n, &thdr) == 0) 2649a747e4fSDavid du Colombier continue; 2659a747e4fSDavid du Colombier 2669a747e4fSDavid du Colombier if(debug) 2679a747e4fSDavid du Colombier fprint(2, "<-%F\n", &thdr);/**/ 2689a747e4fSDavid du Colombier 2699a747e4fSDavid du Colombier if(!fcalls[thdr.type]) 2709a747e4fSDavid du Colombier err = "bad fcall type"; 2719a747e4fSDavid du Colombier else 2729a747e4fSDavid du Colombier err = (*fcalls[thdr.type])(newfid(thdr.fid)); 2739a747e4fSDavid du Colombier if(err){ 2749a747e4fSDavid du Colombier rhdr.type = Rerror; 2759a747e4fSDavid du Colombier rhdr.ename = err; 2769a747e4fSDavid du Colombier }else{ 2779a747e4fSDavid du Colombier rhdr.type = thdr.type + 1; 2789a747e4fSDavid du Colombier rhdr.fid = thdr.fid; 2799a747e4fSDavid du Colombier } 2809a747e4fSDavid du Colombier rhdr.tag = thdr.tag; 2819a747e4fSDavid du Colombier if(debug) 2829a747e4fSDavid du Colombier fprint(2, "->%F\n", &rhdr);/**/ 2839a747e4fSDavid du Colombier n = convS2M(&rhdr, mdata, messagesize); 2849a747e4fSDavid du Colombier if(write(mfd, mdata, n) != n) 2859a747e4fSDavid du Colombier fatal("mount write"); 2869a747e4fSDavid du Colombier } 2879a747e4fSDavid du Colombier } 2889a747e4fSDavid du Colombier 2899a747e4fSDavid du Colombier char* 2909a747e4fSDavid du Colombier rnop(Fid *f) 2919a747e4fSDavid du Colombier { 2929a747e4fSDavid du Colombier USED(f); 2939a747e4fSDavid du Colombier return 0; 2949a747e4fSDavid du Colombier } 2959a747e4fSDavid du Colombier 2969a747e4fSDavid du Colombier char* 2979a747e4fSDavid du Colombier rversion(Fid*) 2989a747e4fSDavid du Colombier { 2999a747e4fSDavid du Colombier if(thdr.msize > sizeof(mdata)) 3009a747e4fSDavid du Colombier rhdr.msize = messagesize; 3019a747e4fSDavid du Colombier else 3029a747e4fSDavid du Colombier rhdr.msize = thdr.msize; 3039a747e4fSDavid du Colombier messagesize = thdr.msize; 3049a747e4fSDavid du Colombier 3059a747e4fSDavid du Colombier if(strncmp(thdr.version, "9P2000", 6) != 0) 3069a747e4fSDavid du Colombier return "unknown 9P version"; 3079a747e4fSDavid du Colombier rhdr.version = "9P2000"; 3089a747e4fSDavid du Colombier return nil; 3099a747e4fSDavid du Colombier } 3109a747e4fSDavid du Colombier 3119a747e4fSDavid du Colombier char* 3129a747e4fSDavid du Colombier rflush(Fid*) 3139a747e4fSDavid du Colombier { 3149a747e4fSDavid du Colombier return 0; 3159a747e4fSDavid du Colombier } 3169a747e4fSDavid du Colombier 3179a747e4fSDavid du Colombier char* 3189a747e4fSDavid du Colombier rauth(Fid*) 3199a747e4fSDavid du Colombier { 3209a747e4fSDavid du Colombier return "auth unimplemented"; 3219a747e4fSDavid du Colombier } 3229a747e4fSDavid du Colombier 3239a747e4fSDavid du Colombier char* 3249a747e4fSDavid du Colombier rattach(Fid *f) 3259a747e4fSDavid du Colombier { 3269a747e4fSDavid du Colombier f->busy = 1; 3279a747e4fSDavid du Colombier f->node = remroot; 3289a747e4fSDavid du Colombier rhdr.qid = f->node->d->qid; 3299a747e4fSDavid du Colombier return 0; 3309a747e4fSDavid du Colombier } 3319a747e4fSDavid du Colombier 3329a747e4fSDavid du Colombier char* 3339a747e4fSDavid du Colombier rwalk(Fid *f) 3349a747e4fSDavid du Colombier { 3359a747e4fSDavid du Colombier Node *np; 3369a747e4fSDavid du Colombier Fid *nf; 3379a747e4fSDavid du Colombier char **elems; 3389a747e4fSDavid du Colombier int i, nelems; 3399a747e4fSDavid du Colombier char *err; 3409a747e4fSDavid du Colombier Node *node; 3419a747e4fSDavid du Colombier 3429a747e4fSDavid du Colombier /* clone fid */ 3439a747e4fSDavid du Colombier nf = nil; 3449a747e4fSDavid du Colombier if(thdr.newfid != thdr.fid){ 3459a747e4fSDavid du Colombier nf = newfid(thdr.newfid); 3469a747e4fSDavid du Colombier if(nf->busy) 3479a747e4fSDavid du Colombier return "newfid in use"; 3489a747e4fSDavid du Colombier nf->busy = 1; 3499a747e4fSDavid du Colombier nf->node = f->node; 3509a747e4fSDavid du Colombier f = nf; 3519a747e4fSDavid du Colombier } 3529a747e4fSDavid du Colombier 3539a747e4fSDavid du Colombier err = nil; 3549a747e4fSDavid du Colombier elems = thdr.wname; 3559a747e4fSDavid du Colombier nelems = thdr.nwname; 3569a747e4fSDavid du Colombier node = f->node; 3579a747e4fSDavid du Colombier rhdr.nwqid = 0; 3589a747e4fSDavid du Colombier if(nelems > 0){ 3599a747e4fSDavid du Colombier /* walk fid */ 3609a747e4fSDavid du Colombier for(i=0; i<nelems && i<MAXWELEM; i++){ 3619a747e4fSDavid du Colombier if((node->d->qid.type & QTDIR) == 0){ 3629a747e4fSDavid du Colombier err = "not a directory"; 3639a747e4fSDavid du Colombier break; 3649a747e4fSDavid du Colombier } 3659a747e4fSDavid du Colombier if(strcmp(elems[i], ".") == 0){ 3669a747e4fSDavid du Colombier Found: 3679a747e4fSDavid du Colombier rhdr.wqid[i] = node->d->qid; 3689a747e4fSDavid du Colombier rhdr.nwqid++; 3699a747e4fSDavid du Colombier continue; 3709a747e4fSDavid du Colombier } 3719a747e4fSDavid du Colombier if(strcmp(elems[i], "..") == 0){ 3729a747e4fSDavid du Colombier node = node->parent; 3739a747e4fSDavid du Colombier goto Found; 3749a747e4fSDavid du Colombier } 3759a747e4fSDavid du Colombier if(strcmp(elems[i], ".flush.ftpfs") == 0){ 3769a747e4fSDavid du Colombier /* hack to flush the cache */ 3779a747e4fSDavid du Colombier invalidate(node); 3789a747e4fSDavid du Colombier readdir(node); 3799a747e4fSDavid du Colombier goto Found; 3809a747e4fSDavid du Colombier } 3819a747e4fSDavid du Colombier 3829a747e4fSDavid du Colombier /* some top level names are special */ 3839a747e4fSDavid du Colombier if((os == Tops || os == VM || os == VMS) && node == remroot){ 3849a747e4fSDavid du Colombier np = newtopsdir(elems[i]); 3859a747e4fSDavid du Colombier if(np){ 3869a747e4fSDavid du Colombier node = np; 3879a747e4fSDavid du Colombier goto Found; 3889a747e4fSDavid du Colombier } else { 3899a747e4fSDavid du Colombier err = nosuchfile; 3909a747e4fSDavid du Colombier break; 3919a747e4fSDavid du Colombier } 3929a747e4fSDavid du Colombier } 3939a747e4fSDavid du Colombier 3949a747e4fSDavid du Colombier /* everything else */ 3959a747e4fSDavid du Colombier node = extendpath(node, s_copy(elems[i])); 3969a747e4fSDavid du Colombier if(ISCACHED(node->parent)){ 3979a747e4fSDavid du Colombier /* the cache of the parent is good, believe it */ 3989a747e4fSDavid du Colombier if(!ISVALID(node)){ 3999a747e4fSDavid du Colombier err = nosuchfile; 4009a747e4fSDavid du Colombier break; 4019a747e4fSDavid du Colombier } 4029a747e4fSDavid du Colombier if(node->parent->chdirunknown || (node->d->mode & DMSYML)) 4039a747e4fSDavid du Colombier fixsymbolic(node); 4049a747e4fSDavid du Colombier } else if(!ISVALID(node)){ 4059a747e4fSDavid du Colombier /* this isn't a valid node, try cd'ing */ 4069a747e4fSDavid du Colombier if(changedir(node) == 0){ 4079a747e4fSDavid du Colombier node->d->qid.type = QTDIR; 4089a747e4fSDavid du Colombier node->d->mode |= DMDIR; 4099a747e4fSDavid du Colombier }else{ 4109a747e4fSDavid du Colombier node->d->qid.type = QTFILE; 4119a747e4fSDavid du Colombier node->d->mode &= ~DMDIR; 4129a747e4fSDavid du Colombier } 4139a747e4fSDavid du Colombier } 4149a747e4fSDavid du Colombier goto Found; 4159a747e4fSDavid du Colombier } 4169a747e4fSDavid du Colombier if(i == 0 && err == 0) 4179a747e4fSDavid du Colombier err = "file does not exist"; 4189a747e4fSDavid du Colombier } 4199a747e4fSDavid du Colombier 4209a747e4fSDavid du Colombier /* clunk a newly cloned fid if the walk didn't succeed */ 4219a747e4fSDavid du Colombier if(nf != nil && (err != nil || rhdr.nwqid<nelems)){ 4229a747e4fSDavid du Colombier nf->busy = 0; 4239a747e4fSDavid du Colombier nf->fid = 0; 4249a747e4fSDavid du Colombier } 4259a747e4fSDavid du Colombier 4269a747e4fSDavid du Colombier /* if it all worked, point the fid to the enw node */ 4279a747e4fSDavid du Colombier if(err == nil) 4289a747e4fSDavid du Colombier f->node = node; 4299a747e4fSDavid du Colombier 4309a747e4fSDavid du Colombier return err; 4319a747e4fSDavid du Colombier } 4329a747e4fSDavid du Colombier 4339a747e4fSDavid du Colombier char * 4349a747e4fSDavid du Colombier ropen(Fid *f) 4359a747e4fSDavid du Colombier { 4369a747e4fSDavid du Colombier int mode; 4379a747e4fSDavid du Colombier 4389a747e4fSDavid du Colombier mode = thdr.mode; 4399a747e4fSDavid du Colombier if(f->node->d->qid.type & QTDIR) 4409a747e4fSDavid du Colombier if(mode != OREAD) 4419a747e4fSDavid du Colombier return "permission denied"; 4429a747e4fSDavid du Colombier 4439a747e4fSDavid du Colombier if(mode & OTRUNC){ 4449a747e4fSDavid du Colombier uncache(f->node); 4459a747e4fSDavid du Colombier uncache(f->node->parent); 4469a747e4fSDavid du Colombier filedirty(f->node); 4479a747e4fSDavid du Colombier } else { 4489a747e4fSDavid du Colombier /* read the remote file or directory */ 4499a747e4fSDavid du Colombier if(!ISCACHED(f->node)){ 4509a747e4fSDavid du Colombier filefree(f->node); 4519a747e4fSDavid du Colombier if(f->node->d->qid.type & QTDIR){ 4529a747e4fSDavid du Colombier invalidate(f->node); 4539a747e4fSDavid du Colombier if(readdir(f->node) < 0) 4549a747e4fSDavid du Colombier return nosuchfile; 4559a747e4fSDavid du Colombier } else { 4569a747e4fSDavid du Colombier if(readfile(f->node) < 0) 4579a747e4fSDavid du Colombier return errstring; 4589a747e4fSDavid du Colombier } 4599a747e4fSDavid du Colombier CACHED(f->node); 4609a747e4fSDavid du Colombier } 4619a747e4fSDavid du Colombier } 4629a747e4fSDavid du Colombier 4639a747e4fSDavid du Colombier rhdr.qid = f->node->d->qid; 4649a747e4fSDavid du Colombier f->open = 1; 4659a747e4fSDavid du Colombier f->node->opens++; 4669a747e4fSDavid du Colombier return 0; 4679a747e4fSDavid du Colombier } 4689a747e4fSDavid du Colombier 4699a747e4fSDavid du Colombier char* 4709a747e4fSDavid du Colombier rcreate(Fid *f) 4719a747e4fSDavid du Colombier { 4729a747e4fSDavid du Colombier char *name; 4739a747e4fSDavid du Colombier 4749a747e4fSDavid du Colombier if((f->node->d->qid.type&QTDIR) == 0) 4759a747e4fSDavid du Colombier return "not a directory"; 4769a747e4fSDavid du Colombier 4779a747e4fSDavid du Colombier name = thdr.name; 4789a747e4fSDavid du Colombier f->node = extendpath(f->node, s_copy(name)); 4799a747e4fSDavid du Colombier uncache(f->node); 4805d459b5aSDavid du Colombier if(thdr.perm & DMDIR){ 4819a747e4fSDavid du Colombier if(createdir(f->node) < 0) 4829a747e4fSDavid du Colombier return "permission denied"; 4839a747e4fSDavid du Colombier } else 4849a747e4fSDavid du Colombier filedirty(f->node); 4859a747e4fSDavid du Colombier invalidate(f->node->parent); 4869a747e4fSDavid du Colombier uncache(f->node->parent); 4879a747e4fSDavid du Colombier 4889a747e4fSDavid du Colombier rhdr.qid = f->node->d->qid; 4899a747e4fSDavid du Colombier f->open = 1; 4909a747e4fSDavid du Colombier f->node->opens++; 4919a747e4fSDavid du Colombier return 0; 4929a747e4fSDavid du Colombier } 4939a747e4fSDavid du Colombier 4949a747e4fSDavid du Colombier char* 4959a747e4fSDavid du Colombier rread(Fid *f) 4969a747e4fSDavid du Colombier { 4979a747e4fSDavid du Colombier long off; 4989a747e4fSDavid du Colombier int n, cnt, rv; 4999a747e4fSDavid du Colombier Node *np; 5009a747e4fSDavid du Colombier 5019a747e4fSDavid du Colombier rhdr.count = 0; 5029a747e4fSDavid du Colombier off = thdr.offset; 5039a747e4fSDavid du Colombier cnt = thdr.count; 5049a747e4fSDavid du Colombier if(cnt > messagesize-IOHDRSZ) 5059a747e4fSDavid du Colombier cnt = messagesize-IOHDRSZ; 5069a747e4fSDavid du Colombier 5079a747e4fSDavid du Colombier if(f->node->d->qid.type & QTDIR){ 5089a747e4fSDavid du Colombier rv = 0; 5099a747e4fSDavid du Colombier for(np = f->node->children; np != nil; np = np->sibs){ 5109a747e4fSDavid du Colombier if(!ISVALID(np)) 5119a747e4fSDavid du Colombier continue; 5129a747e4fSDavid du Colombier if(off <= BIT16SZ) 5139a747e4fSDavid du Colombier break; 5149a747e4fSDavid du Colombier n = convD2M(np->d, mbuf, messagesize-IOHDRSZ); 5159a747e4fSDavid du Colombier off -= n; 5169a747e4fSDavid du Colombier } 5179a747e4fSDavid du Colombier for(; rv < cnt && np != nil; np = np->sibs){ 5189a747e4fSDavid du Colombier if(!ISVALID(np)) 5199a747e4fSDavid du Colombier continue; 5209a747e4fSDavid du Colombier if(np->d->mode & DMSYML) 5219a747e4fSDavid du Colombier fixsymbolic(np); 5229a747e4fSDavid du Colombier n = convD2M(np->d, mbuf + rv, cnt-rv); 5239a747e4fSDavid du Colombier if(n <= BIT16SZ) 5249a747e4fSDavid du Colombier break; 5259a747e4fSDavid du Colombier rv += n; 5269a747e4fSDavid du Colombier } 5279a747e4fSDavid du Colombier } else { 5289a747e4fSDavid du Colombier /* reread file if it's fallen out of the cache */ 5299a747e4fSDavid du Colombier if(!ISCACHED(f->node)) 5309a747e4fSDavid du Colombier if(readfile(f->node) < 0) 5319a747e4fSDavid du Colombier return errstring; 5329a747e4fSDavid du Colombier CACHED(f->node); 5339a747e4fSDavid du Colombier rv = fileread(f->node, (char*)mbuf, off, cnt); 5349a747e4fSDavid du Colombier if(rv < 0) 5359a747e4fSDavid du Colombier return errstring; 5369a747e4fSDavid du Colombier } 5379a747e4fSDavid du Colombier 5389a747e4fSDavid du Colombier rhdr.data = (char*)mbuf; 5399a747e4fSDavid du Colombier rhdr.count = rv; 5409a747e4fSDavid du Colombier return 0; 5419a747e4fSDavid du Colombier } 5429a747e4fSDavid du Colombier 5439a747e4fSDavid du Colombier char* 5449a747e4fSDavid du Colombier rwrite(Fid *f) 5459a747e4fSDavid du Colombier { 5469a747e4fSDavid du Colombier long off; 5479a747e4fSDavid du Colombier int cnt; 5489a747e4fSDavid du Colombier 5499a747e4fSDavid du Colombier if(f->node->d->qid.type & QTDIR) 5509a747e4fSDavid du Colombier return "directories are not writable"; 5519a747e4fSDavid du Colombier 5529a747e4fSDavid du Colombier rhdr.count = 0; 5539a747e4fSDavid du Colombier off = thdr.offset; 5549a747e4fSDavid du Colombier cnt = thdr.count; 5559a747e4fSDavid du Colombier cnt = filewrite(f->node, thdr.data, off, cnt); 5569a747e4fSDavid du Colombier if(cnt < 0) 5579a747e4fSDavid du Colombier return errstring; 5589a747e4fSDavid du Colombier filedirty(f->node); 5599a747e4fSDavid du Colombier rhdr.count = cnt; 5609a747e4fSDavid du Colombier return 0; 5619a747e4fSDavid du Colombier } 5629a747e4fSDavid du Colombier 5639a747e4fSDavid du Colombier char * 5649a747e4fSDavid du Colombier rclunk(Fid *f) 5659a747e4fSDavid du Colombier { 5669a747e4fSDavid du Colombier if(fileisdirty(f->node)){ 5679a747e4fSDavid du Colombier if(createfile(f->node) < 0) 5689a747e4fSDavid du Colombier fprint(2, "ftpfs: couldn't create %s\n", f->node->d->name); 5699a747e4fSDavid du Colombier fileclean(f->node); 5709a747e4fSDavid du Colombier uncache(f->node); 5719a747e4fSDavid du Colombier } 5729a747e4fSDavid du Colombier if(f->open){ 5739a747e4fSDavid du Colombier f->open = 0; 5749a747e4fSDavid du Colombier f->node->opens--; 5759a747e4fSDavid du Colombier } 5769a747e4fSDavid du Colombier f->busy = 0; 5779a747e4fSDavid du Colombier return 0; 5789a747e4fSDavid du Colombier } 5799a747e4fSDavid du Colombier 5809a747e4fSDavid du Colombier /* 5819a747e4fSDavid du Colombier * remove is an implicit clunk 5829a747e4fSDavid du Colombier */ 5839a747e4fSDavid du Colombier char * 5849a747e4fSDavid du Colombier rremove(Fid *f) 5859a747e4fSDavid du Colombier { 586c0eadb1cSDavid du Colombier char *e; 587c0eadb1cSDavid du Colombier e = nil; 5889a747e4fSDavid du Colombier if(QTDIR & f->node->d->qid.type){ 5899a747e4fSDavid du Colombier if(removedir(f->node) < 0) 590c0eadb1cSDavid du Colombier e = errstring; 5919a747e4fSDavid du Colombier } else { 5929a747e4fSDavid du Colombier if(removefile(f->node) < 0) 593c0eadb1cSDavid du Colombier e = errstring; 5949a747e4fSDavid du Colombier } 5959a747e4fSDavid du Colombier uncache(f->node->parent); 5969a747e4fSDavid du Colombier uncache(f->node); 597c0eadb1cSDavid du Colombier if(e == nil) 5989a747e4fSDavid du Colombier INVALID(f->node); 5999a747e4fSDavid du Colombier f->busy = 0; 600c0eadb1cSDavid du Colombier return e; 6019a747e4fSDavid du Colombier } 6029a747e4fSDavid du Colombier 6039a747e4fSDavid du Colombier char * 6049a747e4fSDavid du Colombier rstat(Fid *f) 6059a747e4fSDavid du Colombier { 6069a747e4fSDavid du Colombier Node *p; 6079a747e4fSDavid du Colombier 6089a747e4fSDavid du Colombier p = f->node->parent; 6099a747e4fSDavid du Colombier if(!ISCACHED(p)){ 6109a747e4fSDavid du Colombier invalidate(p); 6119a747e4fSDavid du Colombier readdir(p); 6129a747e4fSDavid du Colombier CACHED(p); 6139a747e4fSDavid du Colombier } 6149a747e4fSDavid du Colombier if(!ISVALID(f->node)) 6159a747e4fSDavid du Colombier return nosuchfile; 6169a747e4fSDavid du Colombier if(p->chdirunknown) 6179a747e4fSDavid du Colombier fixsymbolic(f->node); 6189a747e4fSDavid du Colombier rhdr.nstat = convD2M(f->node->d, mbuf, messagesize-IOHDRSZ); 6199a747e4fSDavid du Colombier rhdr.stat = mbuf; 6209a747e4fSDavid du Colombier return 0; 6219a747e4fSDavid du Colombier } 6229a747e4fSDavid du Colombier 6239a747e4fSDavid du Colombier char * 6249a747e4fSDavid du Colombier rwstat(Fid *f) 6259a747e4fSDavid du Colombier { 6269a747e4fSDavid du Colombier USED(f); 6279a747e4fSDavid du Colombier return "wstat not implemented"; 6289a747e4fSDavid du Colombier } 6299a747e4fSDavid du Colombier 6309a747e4fSDavid du Colombier /* 6319a747e4fSDavid du Colombier * print message and die 6329a747e4fSDavid du Colombier */ 6339a747e4fSDavid du Colombier void 6349a747e4fSDavid du Colombier fatal(char *fmt, ...) 6359a747e4fSDavid du Colombier { 6369a747e4fSDavid du Colombier va_list arg; 6379a747e4fSDavid du Colombier char buf[8*1024]; 6389a747e4fSDavid du Colombier 6399a747e4fSDavid du Colombier dying = 1; 6409a747e4fSDavid du Colombier 6419a747e4fSDavid du Colombier va_start(arg, fmt); 6429a747e4fSDavid du Colombier vseprint(buf, buf + (sizeof(buf)-1) / sizeof(*buf), fmt, arg); 6439a747e4fSDavid du Colombier va_end(arg); 6449a747e4fSDavid du Colombier 6459a747e4fSDavid du Colombier fprint(2, "ftpfs: %s\n", buf); 6469a747e4fSDavid du Colombier if(kapid > 0) 6479a747e4fSDavid du Colombier postnote(PNGROUP, kapid, "die"); 6489a747e4fSDavid du Colombier exits(buf); 6499a747e4fSDavid du Colombier } 6509a747e4fSDavid du Colombier 6519a747e4fSDavid du Colombier /* 6529a747e4fSDavid du Colombier * like strncpy but make sure there's a terminating null 6539a747e4fSDavid du Colombier */ 6549a747e4fSDavid du Colombier void* 6559a747e4fSDavid du Colombier safecpy(void *to, void *from, int n) 6569a747e4fSDavid du Colombier { 6579a747e4fSDavid du Colombier char *a = ((char*)to) + n - 1; 6589a747e4fSDavid du Colombier 6599a747e4fSDavid du Colombier strncpy(to, from, n); 6609a747e4fSDavid du Colombier *a = 0; 6619a747e4fSDavid du Colombier return to; 6629a747e4fSDavid du Colombier } 6639a747e4fSDavid du Colombier 6649a747e4fSDavid du Colombier /* 6659a747e4fSDavid du Colombier * set the error string 6669a747e4fSDavid du Colombier */ 6679a747e4fSDavid du Colombier int 6689a747e4fSDavid du Colombier seterr(char *fmt, ...) 6699a747e4fSDavid du Colombier { 6709a747e4fSDavid du Colombier va_list arg; 6719a747e4fSDavid du Colombier 6729a747e4fSDavid du Colombier va_start(arg, fmt); 6739a747e4fSDavid du Colombier vsnprint(errstring, sizeof errstring, fmt, arg); 6749a747e4fSDavid du Colombier va_end(arg); 6759a747e4fSDavid du Colombier return -1; 6769a747e4fSDavid du Colombier } 6779a747e4fSDavid du Colombier 6789a747e4fSDavid du Colombier /* 6799a747e4fSDavid du Colombier * create a new node 6809a747e4fSDavid du Colombier */ 6819a747e4fSDavid du Colombier Node* 6829a747e4fSDavid du Colombier newnode(Node *parent, String *name) 6839a747e4fSDavid du Colombier { 6849a747e4fSDavid du Colombier Node *np; 6859a747e4fSDavid du Colombier static ulong path; 6869a747e4fSDavid du Colombier Dir d; 6879a747e4fSDavid du Colombier 6889a747e4fSDavid du Colombier np = mallocz(sizeof(Node), 1); 6899a747e4fSDavid du Colombier if(np == 0) 6909a747e4fSDavid du Colombier fatal("out of memory"); 6919a747e4fSDavid du Colombier 6929a747e4fSDavid du Colombier np->children = 0; 6939a747e4fSDavid du Colombier if(parent){ 6949a747e4fSDavid du Colombier np->parent = parent; 6959a747e4fSDavid du Colombier np->sibs = parent->children; 6969a747e4fSDavid du Colombier parent->children = np; 6979a747e4fSDavid du Colombier np->depth = parent->depth + 1; 6989a747e4fSDavid du Colombier d.dev = 0; /* not stat'd */ 6999a747e4fSDavid du Colombier } else { 7009a747e4fSDavid du Colombier /* the root node */ 7019a747e4fSDavid du Colombier np->parent = np; 7029a747e4fSDavid du Colombier np->sibs = 0; 7039a747e4fSDavid du Colombier np->depth = 0; 7049a747e4fSDavid du Colombier d.dev = 1; 7059a747e4fSDavid du Colombier } 7069a747e4fSDavid du Colombier np->remname = name; 7079a747e4fSDavid du Colombier d.name = s_to_c(name); 7089a747e4fSDavid du Colombier d.atime = time(0); 7099a747e4fSDavid du Colombier d.mtime = d.atime; 7109a747e4fSDavid du Colombier d.uid = nouid; 7119a747e4fSDavid du Colombier d.gid = nouid; 7129a747e4fSDavid du Colombier d.muid = nouid; 7139a747e4fSDavid du Colombier np->fp = 0; 7149a747e4fSDavid du Colombier d.qid.path = ++path; 7159a747e4fSDavid du Colombier d.qid.vers = 0; 7169a747e4fSDavid du Colombier d.qid.type = QTFILE; 7179a747e4fSDavid du Colombier d.type = 0; 7189a747e4fSDavid du Colombier np->d = reallocdir(&d, 0); 7199a747e4fSDavid du Colombier 7209a747e4fSDavid du Colombier return np; 7219a747e4fSDavid du Colombier } 7229a747e4fSDavid du Colombier 7239a747e4fSDavid du Colombier /* 7249a747e4fSDavid du Colombier * walk one down the local mirror of the remote directory tree 7259a747e4fSDavid du Colombier */ 7269a747e4fSDavid du Colombier Node* 7279a747e4fSDavid du Colombier extendpath(Node *parent, String *elem) 7289a747e4fSDavid du Colombier { 7299a747e4fSDavid du Colombier Node *np; 7309a747e4fSDavid du Colombier 7319a747e4fSDavid du Colombier for(np = parent->children; np; np = np->sibs) 7329a747e4fSDavid du Colombier if(strcmp(s_to_c(np->remname), s_to_c(elem)) == 0){ 7339a747e4fSDavid du Colombier s_free(elem); 7349a747e4fSDavid du Colombier return np; 7359a747e4fSDavid du Colombier } 7369a747e4fSDavid du Colombier 7379a747e4fSDavid du Colombier return newnode(parent, elem); 7389a747e4fSDavid du Colombier } 7399a747e4fSDavid du Colombier 7409a747e4fSDavid du Colombier /* 7419a747e4fSDavid du Colombier * flush the cached file, write it back if it's dirty 7429a747e4fSDavid du Colombier */ 7439a747e4fSDavid du Colombier void 7449a747e4fSDavid du Colombier uncache(Node *np) 7459a747e4fSDavid du Colombier { 7469a747e4fSDavid du Colombier if(fileisdirty(np)) 7479a747e4fSDavid du Colombier createfile(np); 7489a747e4fSDavid du Colombier filefree(np); 7499a747e4fSDavid du Colombier UNCACHED(np); 7509a747e4fSDavid du Colombier } 7519a747e4fSDavid du Colombier 7529a747e4fSDavid du Colombier /* 7539a747e4fSDavid du Colombier * invalidate all children of a node 7549a747e4fSDavid du Colombier */ 7559a747e4fSDavid du Colombier void 7569a747e4fSDavid du Colombier invalidate(Node *node) 7579a747e4fSDavid du Colombier { 7589a747e4fSDavid du Colombier Node *np; 7599a747e4fSDavid du Colombier 7609a747e4fSDavid du Colombier if(node->opens) 7619a747e4fSDavid du Colombier return; /* don't invalidate something that's open */ 7629a747e4fSDavid du Colombier 7639a747e4fSDavid du Colombier uncachedir(node, 0); 7649a747e4fSDavid du Colombier 7659a747e4fSDavid du Colombier /* invalidate children */ 7669a747e4fSDavid du Colombier for(np = node->children; np; np = np->sibs){ 7679a747e4fSDavid du Colombier if(np->opens) 7689a747e4fSDavid du Colombier continue; /* don't invalidate something that's open */ 7699a747e4fSDavid du Colombier UNCACHED(np); 7709a747e4fSDavid du Colombier invalidate(np); 7719a747e4fSDavid du Colombier np->d->dev = 0; 7729a747e4fSDavid du Colombier } 7739a747e4fSDavid du Colombier } 7749a747e4fSDavid du Colombier 7759a747e4fSDavid du Colombier /* 7769a747e4fSDavid du Colombier * make a top level tops-20 directory. They are automaticly valid. 7779a747e4fSDavid du Colombier */ 7789a747e4fSDavid du Colombier Node* 7799a747e4fSDavid du Colombier newtopsdir(char *name) 7809a747e4fSDavid du Colombier { 7819a747e4fSDavid du Colombier Node *np; 7829a747e4fSDavid du Colombier 7839a747e4fSDavid du Colombier np = extendpath(remroot, s_copy(name)); 7849a747e4fSDavid du Colombier if(!ISVALID(np)){ 7859a747e4fSDavid du Colombier np->d->qid.type = QTDIR; 7869a747e4fSDavid du Colombier np->d->atime = time(0); 7879a747e4fSDavid du Colombier np->d->mtime = np->d->atime; 7889a747e4fSDavid du Colombier np->d->uid = "?uid?"; 7899a747e4fSDavid du Colombier np->d->gid = "?uid?"; 7909a747e4fSDavid du Colombier np->d->muid = "?uid?"; 7919a747e4fSDavid du Colombier np->d->mode = DMDIR|0777; 7929a747e4fSDavid du Colombier np->d->length = 0; 7939a747e4fSDavid du Colombier np->d = reallocdir(np->d, 1); 7949a747e4fSDavid du Colombier 7959a747e4fSDavid du Colombier if(changedir(np) >= 0) 7969a747e4fSDavid du Colombier VALID(np); 7979a747e4fSDavid du Colombier } 7989a747e4fSDavid du Colombier return np; 7999a747e4fSDavid du Colombier } 8009a747e4fSDavid du Colombier 8019a747e4fSDavid du Colombier /* 8029a747e4fSDavid du Colombier * figure out if a symbolic link is to a directory or a file 8039a747e4fSDavid du Colombier */ 8049a747e4fSDavid du Colombier void 8059a747e4fSDavid du Colombier fixsymbolic(Node *node) 8069a747e4fSDavid du Colombier { 8079a747e4fSDavid du Colombier if(changedir(node) == 0){ 8089a747e4fSDavid du Colombier node->d->mode |= DMDIR; 8099a747e4fSDavid du Colombier node->d->qid.type = QTDIR; 8109a747e4fSDavid du Colombier } else 8119a747e4fSDavid du Colombier node->d->qid.type = QTFILE; 8129a747e4fSDavid du Colombier node->d->mode &= ~DMSYML; 8139a747e4fSDavid du Colombier } 814