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