xref: /plan9/sys/src/cmd/ip/ftpfs/ftpfs.c (revision 24e2e6554c8f9c73ddd34af1b8d8f160084070b7)
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