xref: /plan9/sys/src/cmd/unix/u9fs/u9fs.c (revision dfda52d80255562ef033681626b3a6450d80329d)
19a747e4fSDavid du Colombier /* already in plan9.h #include <sys/types.h> *//* for struct passwd, struct group, struct stat ... */
2f5736e95SDavid du Colombier /* plan9.h is first to get the large file support definitions as early as possible */
3f5736e95SDavid du Colombier #include <plan9.h>
49a747e4fSDavid du Colombier #include <sys/stat.h>	/* for stat, umask */
59a747e4fSDavid du Colombier #include <stdlib.h>	/* for malloc */
69a747e4fSDavid du Colombier #include <string.h>	/* for strcpy, memmove */
79a747e4fSDavid du Colombier #include <pwd.h>	/* for getpwnam, getpwuid */
89a747e4fSDavid du Colombier #include <grp.h>	/* for getgrnam, getgrgid */
99a747e4fSDavid du Colombier #include <unistd.h>	/* for gethostname, pread, pwrite, read, write */
109a747e4fSDavid du Colombier #include <utime.h>	/* for utime */
119a747e4fSDavid du Colombier #include <dirent.h>	/* for readdir */
129a747e4fSDavid du Colombier #include <errno.h>	/* for errno */
139a747e4fSDavid du Colombier #include <stdio.h>	/* for remove [sic] */
149a747e4fSDavid du Colombier #include <fcntl.h>	/* for O_RDONLY, etc. */
153e12c5d1SDavid du Colombier 
169a747e4fSDavid du Colombier #include <sys/socket.h>	/* various networking crud */
17219b2ee8SDavid du Colombier #include <netinet/in.h>
18219b2ee8SDavid du Colombier #include <netdb.h>
199a747e4fSDavid du Colombier 
209a747e4fSDavid du Colombier #include <fcall.h>
219a747e4fSDavid du Colombier #include <oldfcall.h>
229a747e4fSDavid du Colombier #include <u9fs.h>
239a747e4fSDavid du Colombier 
249a747e4fSDavid du Colombier /* #ifndef because can be given in makefile */
259a747e4fSDavid du Colombier #ifndef DEFAULTLOG
269a747e4fSDavid du Colombier #define DEFAULTLOG "/tmp/u9fs.log"
273e12c5d1SDavid du Colombier #endif
283e12c5d1SDavid du Colombier 
299a747e4fSDavid du Colombier char *logfile = DEFAULTLOG;
303e12c5d1SDavid du Colombier 
319a747e4fSDavid du Colombier #define S_ISSPECIAL(m) (S_ISCHR(m) || S_ISBLK(m) || S_ISFIFO(m))
329a747e4fSDavid du Colombier 
339a747e4fSDavid du Colombier enum {
349a747e4fSDavid du Colombier 	Tdot = 1,
359a747e4fSDavid du Colombier 	Tdotdot
363e12c5d1SDavid du Colombier };
373e12c5d1SDavid du Colombier 
389a747e4fSDavid du Colombier enum {
399a747e4fSDavid du Colombier 	P9P1,
409a747e4fSDavid du Colombier 	P9P2000
413e12c5d1SDavid du Colombier };
423e12c5d1SDavid du Colombier 
439a747e4fSDavid du Colombier typedef struct User User;
449a747e4fSDavid du Colombier struct User {
453e12c5d1SDavid du Colombier 	int id;
469a747e4fSDavid du Colombier 	gid_t defaultgid;
473e12c5d1SDavid du Colombier 	char *name;
489a747e4fSDavid du Colombier 	char **mem;	/* group members */
497dd7cddfSDavid du Colombier 	int nmem;
509a747e4fSDavid du Colombier 	User *next;
513e12c5d1SDavid du Colombier };
523e12c5d1SDavid du Colombier 
539a747e4fSDavid du Colombier struct Fid {
549a747e4fSDavid du Colombier 	int fid;
559a747e4fSDavid du Colombier 	char *path;
569a747e4fSDavid du Colombier 	struct stat st;
579a747e4fSDavid du Colombier 	User *u;
589a747e4fSDavid du Colombier 	int omode;
599a747e4fSDavid du Colombier 	DIR *dir;
609a747e4fSDavid du Colombier 	int diroffset;
619a747e4fSDavid du Colombier 	int fd;
629a747e4fSDavid du Colombier 	struct dirent *dirent;
639a747e4fSDavid du Colombier 	Fid *next;
649a747e4fSDavid du Colombier 	Fid *prev;
6550a9bdd4SDavid du Colombier 	int auth;
6650a9bdd4SDavid du Colombier 	void *authmagic;
679a747e4fSDavid du Colombier };
683e12c5d1SDavid du Colombier 
699a747e4fSDavid du Colombier void*	emalloc(size_t);
709a747e4fSDavid du Colombier void*	erealloc(void*, size_t);
713e12c5d1SDavid du Colombier char*	estrdup(char*);
72*dfda52d8SDavid du Colombier char*	estrpath(char*, char*, int);
739a747e4fSDavid du Colombier void	sysfatal(char*, ...);
749a747e4fSDavid du Colombier int	okuser(char*);
753e12c5d1SDavid du Colombier 
769a747e4fSDavid du Colombier void	rversion(Fcall*, Fcall*);
779a747e4fSDavid du Colombier void	rauth(Fcall*, Fcall*);
789a747e4fSDavid du Colombier void	rattach(Fcall*, Fcall*);
799a747e4fSDavid du Colombier void	rflush(Fcall*, Fcall*);
809a747e4fSDavid du Colombier void	rclone(Fcall*, Fcall*);
819a747e4fSDavid du Colombier void	rwalk(Fcall*, Fcall*);
829a747e4fSDavid du Colombier void	ropen(Fcall*, Fcall*);
839a747e4fSDavid du Colombier void	rcreate(Fcall*, Fcall*);
849a747e4fSDavid du Colombier void	rread(Fcall*, Fcall*);
859a747e4fSDavid du Colombier void	rwrite(Fcall*, Fcall*);
869a747e4fSDavid du Colombier void	rclunk(Fcall*, Fcall*);
879a747e4fSDavid du Colombier void	rstat(Fcall*, Fcall*);
889a747e4fSDavid du Colombier void	rwstat(Fcall*, Fcall*);
899a747e4fSDavid du Colombier void	rclwalk(Fcall*, Fcall*);
909a747e4fSDavid du Colombier void	rremove(Fcall*, Fcall*);
919a747e4fSDavid du Colombier 
929a747e4fSDavid du Colombier User*	uname2user(char*);
939a747e4fSDavid du Colombier User*	gname2user(char*);
949a747e4fSDavid du Colombier User*	uid2user(int);
959a747e4fSDavid du Colombier User*	gid2user(int);
969a747e4fSDavid du Colombier 
979a747e4fSDavid du Colombier Fid*	newfid(int, char**);
9850a9bdd4SDavid du Colombier Fid*	oldfidex(int, int, char**);
999a747e4fSDavid du Colombier Fid*	oldfid(int, char**);
1009a747e4fSDavid du Colombier int	fidstat(Fid*, char**);
1019a747e4fSDavid du Colombier void	freefid(Fid*);
1029a747e4fSDavid du Colombier 
1039a747e4fSDavid du Colombier int	userchange(User*, char**);
1049a747e4fSDavid du Colombier int	userwalk(User*, char**, char*, Qid*, char**);
1059a747e4fSDavid du Colombier int	useropen(Fid*, int, char**);
1069a747e4fSDavid du Colombier int	usercreate(Fid*, char*, int, long, char**);
1079a747e4fSDavid du Colombier int	userremove(Fid*, char**);
1089a747e4fSDavid du Colombier int	userperm(User*, char*, int, int);
1099a747e4fSDavid du Colombier int	useringroup(User*, User*);
1109a747e4fSDavid du Colombier 
1119a747e4fSDavid du Colombier Qid	stat2qid(struct stat*);
1129a747e4fSDavid du Colombier 
1139a747e4fSDavid du Colombier void	getfcallold(int, Fcall*, int);
1149a747e4fSDavid du Colombier void	putfcallold(int, Fcall*);
1153e12c5d1SDavid du Colombier 
1163e12c5d1SDavid du Colombier char	Eauth[] =	"authentication failed";
1173e12c5d1SDavid du Colombier char	Ebadfid[] =	"fid unknown or out of range";
1189a747e4fSDavid du Colombier char	Ebadoffset[] =	"bad offset in directory read";
1199a747e4fSDavid du Colombier char	Ebadusefid[] =	"bad use of fid";
1209a747e4fSDavid du Colombier char	Edirchange[] =	"wstat can't convert between files and directories";
1219a747e4fSDavid du Colombier char	Eexist[] =	"file or directory already exists";
1223e12c5d1SDavid du Colombier char	Efidactive[] =	"fid already in use";
1239a747e4fSDavid du Colombier char	Enotdir[] =	"not a directory";
1249a747e4fSDavid du Colombier char	Enotingroup[] =	"not a member of proposed group";
1259a747e4fSDavid du Colombier char	Enotowner[] = 	"only owner can change group in wstat";
1269a747e4fSDavid du Colombier char	Eperm[] =	"permission denied";
1273e12c5d1SDavid du Colombier char	Especial0[] =	"already attached without access to special files";
1283e12c5d1SDavid du Colombier char	Especial1[] =	"already attached with access to special files";
1299a747e4fSDavid du Colombier char	Especial[] =	"no access to special file";
1303e12c5d1SDavid du Colombier char	Etoolarge[] =	"i/o count too large";
1319a747e4fSDavid du Colombier char	Eunknowngroup[] = "unknown group";
1329a747e4fSDavid du Colombier char	Eunknownuser[] = "unknown user";
1339a747e4fSDavid du Colombier char	Ewstatbuffer[] = "bogus wstat buffer";
1349a747e4fSDavid du Colombier 
1359a747e4fSDavid du Colombier ulong	msize = IOHDRSZ+8192;
1369a747e4fSDavid du Colombier uchar*	rxbuf;
1379a747e4fSDavid du Colombier uchar*	txbuf;
1389a747e4fSDavid du Colombier void*	databuf;
1399a747e4fSDavid du Colombier int	connected;
1409a747e4fSDavid du Colombier int	devallowed;
1419a747e4fSDavid du Colombier char*	autharg;
1429a747e4fSDavid du Colombier char*	defaultuser;
1439a747e4fSDavid du Colombier char	hostname[256];
1449a747e4fSDavid du Colombier char	remotehostname[256];
1459a747e4fSDavid du Colombier int	chatty9p = 0;
1469a747e4fSDavid du Colombier int	network = 1;
1479a747e4fSDavid du Colombier int	old9p = -1;
1489a747e4fSDavid du Colombier int	authed;
1499a747e4fSDavid du Colombier User*	none;
1509a747e4fSDavid du Colombier 
1519a747e4fSDavid du Colombier Auth *authmethods[] = {	/* first is default */
1529a747e4fSDavid du Colombier 	&authrhosts,
15350a9bdd4SDavid du Colombier 	&authp9any,
1549a747e4fSDavid du Colombier 	&authnone,
1559a747e4fSDavid du Colombier };
1569a747e4fSDavid du Colombier 
1579a747e4fSDavid du Colombier Auth *auth;
1583e12c5d1SDavid du Colombier 
159*dfda52d8SDavid du Colombier /*
160*dfda52d8SDavid du Colombier  * frogs: characters not valid in plan9
161*dfda52d8SDavid du Colombier  * filenames, keep this list in sync with
162*dfda52d8SDavid du Colombier  * /sys/src/9/port/chan.c:1656
163*dfda52d8SDavid du Colombier  */
164*dfda52d8SDavid du Colombier char isfrog[256]={
165*dfda52d8SDavid du Colombier 	/*NUL*/	1, 1, 1, 1, 1, 1, 1, 1,
166*dfda52d8SDavid du Colombier 	/*BKS*/	1, 1, 1, 1, 1, 1, 1, 1,
167*dfda52d8SDavid du Colombier 	/*DLE*/	1, 1, 1, 1, 1, 1, 1, 1,
168*dfda52d8SDavid du Colombier 	/*CAN*/	1, 1, 1, 1, 1, 1, 1, 1,
169*dfda52d8SDavid du Colombier 	['/']	1,
170*dfda52d8SDavid du Colombier 	[0x7f]	1,
171*dfda52d8SDavid du Colombier };
172*dfda52d8SDavid du Colombier 
1737dd7cddfSDavid du Colombier void
1749a747e4fSDavid du Colombier getfcallnew(int fd, Fcall *fc, int have)
1757dd7cddfSDavid du Colombier {
1769a747e4fSDavid du Colombier 	int len;
1779a747e4fSDavid du Colombier 
1789a747e4fSDavid du Colombier 	if(have > BIT32SZ)
1799a747e4fSDavid du Colombier 		sysfatal("cannot happen");
1809a747e4fSDavid du Colombier 
1819a747e4fSDavid du Colombier 	if(have < BIT32SZ && readn(fd, rxbuf+have, BIT32SZ-have) != BIT32SZ-have)
1829a747e4fSDavid du Colombier 		sysfatal("couldn't read message");
1839a747e4fSDavid du Colombier 
1849a747e4fSDavid du Colombier 	len = GBIT32(rxbuf);
1859a747e4fSDavid du Colombier 	if(len <= BIT32SZ)
1869a747e4fSDavid du Colombier 		sysfatal("bogus message");
1879a747e4fSDavid du Colombier 
1889a747e4fSDavid du Colombier 	len -= BIT32SZ;
1899a747e4fSDavid du Colombier 	if(readn(fd, rxbuf+BIT32SZ, len) != len)
1909a747e4fSDavid du Colombier 		sysfatal("short message");
1919a747e4fSDavid du Colombier 
1929a747e4fSDavid du Colombier 	if(convM2S(rxbuf, len+BIT32SZ, fc) != len+BIT32SZ)
1939a747e4fSDavid du Colombier 		sysfatal("badly sized message type %d", rxbuf[0]);
1949a747e4fSDavid du Colombier }
1959a747e4fSDavid du Colombier 
1969a747e4fSDavid du Colombier void
1979a747e4fSDavid du Colombier getfcallold(int fd, Fcall *fc, int have)
1989a747e4fSDavid du Colombier {
1999a747e4fSDavid du Colombier 	int len, n;
2009a747e4fSDavid du Colombier 
2019a747e4fSDavid du Colombier 	if(have > 3)
2029a747e4fSDavid du Colombier 		sysfatal("cannot happen");
2039a747e4fSDavid du Colombier 
2049a747e4fSDavid du Colombier 	if(have < 3 && readn(fd, rxbuf, 3-have) != 3-have)
2059a747e4fSDavid du Colombier 		sysfatal("couldn't read message");
2069a747e4fSDavid du Colombier 
2079a747e4fSDavid du Colombier 	len = oldhdrsize(rxbuf[0]);
2089a747e4fSDavid du Colombier 	if(len < 3)
2099a747e4fSDavid du Colombier 		sysfatal("bad message %d", rxbuf[0]);
2109a747e4fSDavid du Colombier 	if(len > 3 && readn(fd, rxbuf+3, len-3) != len-3)
2119a747e4fSDavid du Colombier 		sysfatal("couldn't read message");
2129a747e4fSDavid du Colombier 
2139a747e4fSDavid du Colombier 	n = iosize(rxbuf);
2149a747e4fSDavid du Colombier 	if(readn(fd, rxbuf+len, n) != n)
2159a747e4fSDavid du Colombier 		sysfatal("couldn't read message");
2169a747e4fSDavid du Colombier 	len += n;
2179a747e4fSDavid du Colombier 
2189a747e4fSDavid du Colombier 	if(convM2Sold(rxbuf, len, fc) != len)
2199a747e4fSDavid du Colombier 		sysfatal("badly sized message type %d", rxbuf[0]);
2209a747e4fSDavid du Colombier }
2219a747e4fSDavid du Colombier 
2229a747e4fSDavid du Colombier void
2239a747e4fSDavid du Colombier putfcallnew(int wfd, Fcall *tx)
2249a747e4fSDavid du Colombier {
2259a747e4fSDavid du Colombier 	uint n;
2269a747e4fSDavid du Colombier 
2279a747e4fSDavid du Colombier 	if((n = convS2M(tx, txbuf, msize)) == 0)
2289a747e4fSDavid du Colombier 		sysfatal("couldn't format message type %d", tx->type);
2299a747e4fSDavid du Colombier 	if(write(wfd, txbuf, n) != n)
2309a747e4fSDavid du Colombier 		sysfatal("couldn't send message");
2319a747e4fSDavid du Colombier }
2329a747e4fSDavid du Colombier 
2339a747e4fSDavid du Colombier void
2349a747e4fSDavid du Colombier putfcallold(int wfd, Fcall *tx)
2359a747e4fSDavid du Colombier {
2369a747e4fSDavid du Colombier 	uint n;
2379a747e4fSDavid du Colombier 
2389a747e4fSDavid du Colombier 	if((n = convS2Mold(tx, txbuf, msize)) == 0)
2399a747e4fSDavid du Colombier 		sysfatal("couldn't format message type %d", tx->type);
2409a747e4fSDavid du Colombier 	if(write(wfd, txbuf, n) != n)
2419a747e4fSDavid du Colombier 		sysfatal("couldn't send message");
2429a747e4fSDavid du Colombier }
2439a747e4fSDavid du Colombier 
2449a747e4fSDavid du Colombier void
2459a747e4fSDavid du Colombier getfcall(int fd, Fcall *fc)
2469a747e4fSDavid du Colombier {
2479a747e4fSDavid du Colombier 	if(old9p == 1){
2489a747e4fSDavid du Colombier 		getfcallold(fd, fc, 0);
2499a747e4fSDavid du Colombier 		return;
2509a747e4fSDavid du Colombier 	}
2519a747e4fSDavid du Colombier 	if(old9p == 0){
2529a747e4fSDavid du Colombier 		getfcallnew(fd, fc, 0);
2539a747e4fSDavid du Colombier 		return;
2549a747e4fSDavid du Colombier 	}
2559a747e4fSDavid du Colombier 
2569a747e4fSDavid du Colombier 	/* auto-detect */
2579a747e4fSDavid du Colombier 	if(readn(fd, rxbuf, 3) != 3)
2589a747e4fSDavid du Colombier 		sysfatal("couldn't read message");
2599a747e4fSDavid du Colombier 
2609a747e4fSDavid du Colombier 	/* is it an old (9P1) message? */
2619a747e4fSDavid du Colombier 	if(50 <= rxbuf[0] && rxbuf[0] <= 87 && (rxbuf[0]&1)==0 && GBIT16(rxbuf+1) == 0xFFFF){
2629a747e4fSDavid du Colombier 		old9p = 1;
2639a747e4fSDavid du Colombier 		getfcallold(fd, fc, 3);
2649a747e4fSDavid du Colombier 		return;
2659a747e4fSDavid du Colombier 	}
2669a747e4fSDavid du Colombier 
2679a747e4fSDavid du Colombier 	getfcallnew(fd, fc, 3);
2689a747e4fSDavid du Colombier 	old9p = 0;
2699a747e4fSDavid du Colombier }
2709a747e4fSDavid du Colombier 
2719a747e4fSDavid du Colombier void
2729a747e4fSDavid du Colombier seterror(Fcall *f, char *error)
2739a747e4fSDavid du Colombier {
2749a747e4fSDavid du Colombier 	f->type = Rerror;
2759a747e4fSDavid du Colombier 	f->ename = error ? error : "programmer error";
2767dd7cddfSDavid du Colombier }
2777dd7cddfSDavid du Colombier 
2783e12c5d1SDavid du Colombier int
2799a747e4fSDavid du Colombier isowner(User *u, Fid *f)
2803e12c5d1SDavid du Colombier {
2819a747e4fSDavid du Colombier 	return u->id == f->st.st_uid;
2823e12c5d1SDavid du Colombier }
2833e12c5d1SDavid du Colombier 
2849df35464SDavid du Colombier 
2859df35464SDavid du Colombier 
2863e12c5d1SDavid du Colombier void
2879a747e4fSDavid du Colombier serve(int rfd, int wfd)
2883e12c5d1SDavid du Colombier {
2899a747e4fSDavid du Colombier 	Fcall rx, tx;
2903e12c5d1SDavid du Colombier 
2913e12c5d1SDavid du Colombier 	for(;;){
2929a747e4fSDavid du Colombier 		getfcall(rfd, &rx);
2933e12c5d1SDavid du Colombier 
2949a747e4fSDavid du Colombier 		if(chatty9p)
2959a747e4fSDavid du Colombier 			fprint(2, "<- %F\n", &rx);
2969a747e4fSDavid du Colombier 
2979a747e4fSDavid du Colombier 		memset(&tx, 0, sizeof tx);
2989a747e4fSDavid du Colombier 		tx.type = rx.type+1;
2999a747e4fSDavid du Colombier 		tx.tag = rx.tag;
3009a747e4fSDavid du Colombier 		switch(rx.type){
3019a747e4fSDavid du Colombier 		case Tflush:
3023e12c5d1SDavid du Colombier 			break;
3039a747e4fSDavid du Colombier 		case Tversion:
3049a747e4fSDavid du Colombier 			rversion(&rx, &tx);
3059a747e4fSDavid du Colombier 			break;
3069a747e4fSDavid du Colombier 		case Tauth:
3079a747e4fSDavid du Colombier 			rauth(&rx, &tx);
3083e12c5d1SDavid du Colombier 			break;
3093e12c5d1SDavid du Colombier 		case Tattach:
3109a747e4fSDavid du Colombier 			rattach(&rx, &tx);
3113e12c5d1SDavid du Colombier 			break;
3123e12c5d1SDavid du Colombier 		case Twalk:
3139a747e4fSDavid du Colombier 			rwalk(&rx, &tx);
3143e12c5d1SDavid du Colombier 			break;
3153e12c5d1SDavid du Colombier 		case Tstat:
3169a747e4fSDavid du Colombier 			tx.stat = databuf;
3179a747e4fSDavid du Colombier 			rstat(&rx, &tx);
3183e12c5d1SDavid du Colombier 			break;
3193e12c5d1SDavid du Colombier 		case Twstat:
3209a747e4fSDavid du Colombier 			rwstat(&rx, &tx);
3213e12c5d1SDavid du Colombier 			break;
3223e12c5d1SDavid du Colombier 		case Topen:
3239a747e4fSDavid du Colombier 			ropen(&rx, &tx);
3243e12c5d1SDavid du Colombier 			break;
3253e12c5d1SDavid du Colombier 		case Tcreate:
3269a747e4fSDavid du Colombier 			rcreate(&rx, &tx);
3273e12c5d1SDavid du Colombier 			break;
3283e12c5d1SDavid du Colombier 		case Tread:
3299a747e4fSDavid du Colombier 			tx.data = databuf;
3309a747e4fSDavid du Colombier 			rread(&rx, &tx);
3313e12c5d1SDavid du Colombier 			break;
3323e12c5d1SDavid du Colombier 		case Twrite:
3339a747e4fSDavid du Colombier 			rwrite(&rx, &tx);
3343e12c5d1SDavid du Colombier 			break;
3353e12c5d1SDavid du Colombier 		case Tclunk:
3369a747e4fSDavid du Colombier 			rclunk(&rx, &tx);
3373e12c5d1SDavid du Colombier 			break;
3383e12c5d1SDavid du Colombier 		case Tremove:
3399a747e4fSDavid du Colombier 			rremove(&rx, &tx);
3403e12c5d1SDavid du Colombier 			break;
3413e12c5d1SDavid du Colombier 		default:
3429a747e4fSDavid du Colombier 			fprint(2, "unknown message %F\n", &rx);
3439a747e4fSDavid du Colombier 			seterror(&tx, "bad message");
3449a747e4fSDavid du Colombier 			break;
3453e12c5d1SDavid du Colombier 		}
3469a747e4fSDavid du Colombier 
3479a747e4fSDavid du Colombier 		if(chatty9p)
3489a747e4fSDavid du Colombier 			fprint(2, "-> %F\n", &tx);
3499a747e4fSDavid du Colombier 
3509a747e4fSDavid du Colombier 		(old9p ? putfcallold : putfcallnew)(wfd, &tx);
3519a747e4fSDavid du Colombier 	}
3523e12c5d1SDavid du Colombier }
3533e12c5d1SDavid du Colombier 
3543e12c5d1SDavid du Colombier void
3559a747e4fSDavid du Colombier rversion(Fcall *rx, Fcall *tx)
3563e12c5d1SDavid du Colombier {
3579a747e4fSDavid du Colombier 	if(msize > rx->msize)
3589a747e4fSDavid du Colombier 		msize = rx->msize;
3599a747e4fSDavid du Colombier 	tx->msize = msize;
3609a747e4fSDavid du Colombier 	if(strncmp(rx->version, "9P", 2) != 0)
3619a747e4fSDavid du Colombier 		tx->version = "unknown";
3629a747e4fSDavid du Colombier 	else
3639a747e4fSDavid du Colombier 		tx->version = "9P2000";
3643e12c5d1SDavid du Colombier }
3653e12c5d1SDavid du Colombier 
3663e12c5d1SDavid du Colombier void
3679a747e4fSDavid du Colombier rauth(Fcall *rx, Fcall *tx)
3683e12c5d1SDavid du Colombier {
3699a747e4fSDavid du Colombier 	char *e;
3703e12c5d1SDavid du Colombier 
3719a747e4fSDavid du Colombier 	if((e = auth->auth(rx, tx)) != nil)
3729a747e4fSDavid du Colombier 		seterror(tx, e);
3733e12c5d1SDavid du Colombier }
3749a747e4fSDavid du Colombier 
3759a747e4fSDavid du Colombier void
3769a747e4fSDavid du Colombier rattach(Fcall *rx, Fcall *tx)
3779a747e4fSDavid du Colombier {
3789a747e4fSDavid du Colombier 	char *e;
3799a747e4fSDavid du Colombier 	Fid *fid;
3809a747e4fSDavid du Colombier 	User *u;
3819a747e4fSDavid du Colombier 
3829a747e4fSDavid du Colombier 	if(rx->aname == nil)
3839a747e4fSDavid du Colombier 		rx->aname = "";
3849a747e4fSDavid du Colombier 
3859a747e4fSDavid du Colombier 	if(strcmp(rx->aname, "device") == 0){
3869a747e4fSDavid du Colombier 		if(connected && !devallowed){
3879a747e4fSDavid du Colombier 			seterror(tx, Especial0);
3889a747e4fSDavid du Colombier 			return;
3899a747e4fSDavid du Colombier 		}
3903e12c5d1SDavid du Colombier 		devallowed = 1;
3913e12c5d1SDavid du Colombier 	}else{
3929a747e4fSDavid du Colombier 		if(connected && devallowed){
3939a747e4fSDavid du Colombier 			seterror(tx, Especial1);
3949a747e4fSDavid du Colombier 			return;
3953e12c5d1SDavid du Colombier 		}
3969a747e4fSDavid du Colombier 	}
3979a747e4fSDavid du Colombier 
3989a747e4fSDavid du Colombier 	if(strcmp(rx->uname, "none") == 0){
3999a747e4fSDavid du Colombier 		if(authed == 0){
4009a747e4fSDavid du Colombier 			seterror(tx, Eauth);
4019a747e4fSDavid du Colombier 			return;
4029a747e4fSDavid du Colombier 		}
4039a747e4fSDavid du Colombier 	} else {
4049a747e4fSDavid du Colombier 		if((e = auth->attach(rx, tx)) != nil){
4059a747e4fSDavid du Colombier 			seterror(tx, e);
4069a747e4fSDavid du Colombier 			return;
4079a747e4fSDavid du Colombier 		}
4089a747e4fSDavid du Colombier 		authed++;
4099a747e4fSDavid du Colombier 	}
4109a747e4fSDavid du Colombier 
4119a747e4fSDavid du Colombier 	if((fid = newfid(rx->fid, &e)) == nil){
4129a747e4fSDavid du Colombier 		seterror(tx, e);
4139a747e4fSDavid du Colombier 		return;
4149a747e4fSDavid du Colombier 	}
4159a747e4fSDavid du Colombier 	fid->path = estrdup("/");
4169a747e4fSDavid du Colombier 	if(fidstat(fid, &e) < 0){
4179a747e4fSDavid du Colombier 		seterror(tx, e);
4189a747e4fSDavid du Colombier 		freefid(fid);
4199a747e4fSDavid du Colombier 		return;
4209a747e4fSDavid du Colombier 	}
4219a747e4fSDavid du Colombier 
4229a747e4fSDavid du Colombier 	if(defaultuser)
4239a747e4fSDavid du Colombier 		rx->uname = defaultuser;
4249a747e4fSDavid du Colombier 
425fa1160edSDavid du Colombier 	if((u = uname2user(rx->uname)) == nil
426fa1160edSDavid du Colombier 	|| (!defaultuser && u->id == 0)){
4279a747e4fSDavid du Colombier 		/* we don't know anyone named root... */
4289a747e4fSDavid du Colombier 		seterror(tx, Eunknownuser);
4299a747e4fSDavid du Colombier 		freefid(fid);
4309a747e4fSDavid du Colombier 		return;
4319a747e4fSDavid du Colombier 	}
4329a747e4fSDavid du Colombier 
4339a747e4fSDavid du Colombier 	fid->u = u;
4349a747e4fSDavid du Colombier 	tx->qid = stat2qid(&fid->st);
4359a747e4fSDavid du Colombier 	return;
4363e12c5d1SDavid du Colombier }
4373e12c5d1SDavid du Colombier 
4383e12c5d1SDavid du Colombier void
4399a747e4fSDavid du Colombier rwalk(Fcall *rx, Fcall *tx)
4403e12c5d1SDavid du Colombier {
4419a747e4fSDavid du Colombier 	int i;
4429a747e4fSDavid du Colombier 	char *path, *e;
4439a747e4fSDavid du Colombier 	Fid *fid, *nfid;
4443e12c5d1SDavid du Colombier 
4459a747e4fSDavid du Colombier 	e = nil;
4469a747e4fSDavid du Colombier 	if((fid = oldfid(rx->fid, &e)) == nil){
4479a747e4fSDavid du Colombier 		seterror(tx, e);
4489a747e4fSDavid du Colombier 		return;
4493e12c5d1SDavid du Colombier 	}
4503e12c5d1SDavid du Colombier 
4519a747e4fSDavid du Colombier 	if(fid->omode != -1){
4529a747e4fSDavid du Colombier 		seterror(tx, Ebadusefid);
4539a747e4fSDavid du Colombier 		return;
4543e12c5d1SDavid du Colombier 	}
4553e12c5d1SDavid du Colombier 
4569a747e4fSDavid du Colombier 	if(fidstat(fid, &e) < 0){
4579a747e4fSDavid du Colombier 		seterror(tx, e);
4589a747e4fSDavid du Colombier 		return;
4599a747e4fSDavid du Colombier 	}
4603e12c5d1SDavid du Colombier 
4619a747e4fSDavid du Colombier 	if(!S_ISDIR(fid->st.st_mode) && rx->nwname){
4629a747e4fSDavid du Colombier 		seterror(tx, Enotdir);
4639a747e4fSDavid du Colombier 		return;
4649a747e4fSDavid du Colombier 	}
4659a747e4fSDavid du Colombier 
4669a747e4fSDavid du Colombier 	nfid = nil;
4679a747e4fSDavid du Colombier 	if(rx->newfid != rx->fid && (nfid = newfid(rx->newfid, &e)) == nil){
4689a747e4fSDavid du Colombier 		seterror(tx, e);
4699a747e4fSDavid du Colombier 		return;
4709a747e4fSDavid du Colombier 	}
4719a747e4fSDavid du Colombier 
4729a747e4fSDavid du Colombier 	path = estrdup(fid->path);
4739a747e4fSDavid du Colombier 	e = nil;
4749a747e4fSDavid du Colombier 	for(i=0; i<rx->nwname; i++)
4759a747e4fSDavid du Colombier 		if(userwalk(fid->u, &path, rx->wname[i], &tx->wqid[i], &e) < 0)
4763e12c5d1SDavid du Colombier 			break;
4773e12c5d1SDavid du Colombier 
4789a747e4fSDavid du Colombier 	if(i == rx->nwname){		/* successful clone or walk */
4799a747e4fSDavid du Colombier 		tx->nwqid = i;
4809a747e4fSDavid du Colombier 		if(nfid){
4819a747e4fSDavid du Colombier 			nfid->path = path;
4829a747e4fSDavid du Colombier 			nfid->u = fid->u;
4833e12c5d1SDavid du Colombier 		}else{
4849a747e4fSDavid du Colombier 			free(fid->path);
4859a747e4fSDavid du Colombier 			fid->path = path;
4863e12c5d1SDavid du Colombier 		}
4879a747e4fSDavid du Colombier 	}else{
4889a747e4fSDavid du Colombier 		if(i > 0)		/* partial walk? */
4899a747e4fSDavid du Colombier 			tx->nwqid = i;
4909a747e4fSDavid du Colombier 		else
4919a747e4fSDavid du Colombier 			seterror(tx, e);
4929a747e4fSDavid du Colombier 
4939a747e4fSDavid du Colombier 		if(nfid)		/* clone implicit new fid */
4949a747e4fSDavid du Colombier 			freefid(nfid);
4959a747e4fSDavid du Colombier 		free(path);
4963e12c5d1SDavid du Colombier 	}
4979a747e4fSDavid du Colombier 	return;
4983e12c5d1SDavid du Colombier }
4993e12c5d1SDavid du Colombier 
5003e12c5d1SDavid du Colombier void
5019a747e4fSDavid du Colombier ropen(Fcall *rx, Fcall *tx)
5023e12c5d1SDavid du Colombier {
5039a747e4fSDavid du Colombier 	char *e;
5049a747e4fSDavid du Colombier 	Fid *fid;
5053e12c5d1SDavid du Colombier 
5069a747e4fSDavid du Colombier 	if((fid = oldfid(rx->fid, &e)) == nil){
5079a747e4fSDavid du Colombier 		seterror(tx, e);
5089a747e4fSDavid du Colombier 		return;
5093e12c5d1SDavid du Colombier 	}
5109a747e4fSDavid du Colombier 
5119a747e4fSDavid du Colombier 	if(fid->omode != -1){
5129a747e4fSDavid du Colombier 		seterror(tx, Ebadusefid);
5139a747e4fSDavid du Colombier 		return;
5143e12c5d1SDavid du Colombier 	}
5159a747e4fSDavid du Colombier 
5169a747e4fSDavid du Colombier 	if(fidstat(fid, &e) < 0){
5179a747e4fSDavid du Colombier 		seterror(tx, e);
5189a747e4fSDavid du Colombier 		return;
5193e12c5d1SDavid du Colombier 	}
5209a747e4fSDavid du Colombier 
5219a747e4fSDavid du Colombier 	if(!devallowed && S_ISSPECIAL(fid->st.st_mode)){
5229a747e4fSDavid du Colombier 		seterror(tx, Especial);
5239a747e4fSDavid du Colombier 		return;
5243e12c5d1SDavid du Colombier 	}
5259a747e4fSDavid du Colombier 
5269a747e4fSDavid du Colombier 	if(useropen(fid, rx->mode, &e) < 0){
5279a747e4fSDavid du Colombier 		seterror(tx, e);
5289a747e4fSDavid du Colombier 		return;
5293e12c5d1SDavid du Colombier 	}
5309a747e4fSDavid du Colombier 
5319a747e4fSDavid du Colombier 	tx->iounit = 0;
5329a747e4fSDavid du Colombier 	tx->qid = stat2qid(&fid->st);
5333e12c5d1SDavid du Colombier }
5343e12c5d1SDavid du Colombier 
5353e12c5d1SDavid du Colombier void
5369a747e4fSDavid du Colombier rcreate(Fcall *rx, Fcall *tx)
5373e12c5d1SDavid du Colombier {
5389a747e4fSDavid du Colombier 	char *e;
5399a747e4fSDavid du Colombier 	Fid *fid;
5409a747e4fSDavid du Colombier 
5419a747e4fSDavid du Colombier 	if((fid = oldfid(rx->fid, &e)) == nil){
5429a747e4fSDavid du Colombier 		seterror(tx, e);
5439a747e4fSDavid du Colombier 		return;
5449a747e4fSDavid du Colombier 	}
5459a747e4fSDavid du Colombier 
5469a747e4fSDavid du Colombier 	if(fid->omode != -1){
5479a747e4fSDavid du Colombier 		seterror(tx, Ebadusefid);
5489a747e4fSDavid du Colombier 		return;
5499a747e4fSDavid du Colombier 	}
5509a747e4fSDavid du Colombier 
5519a747e4fSDavid du Colombier 	if(fidstat(fid, &e) < 0){
5529a747e4fSDavid du Colombier 		seterror(tx, e);
5539a747e4fSDavid du Colombier 		return;
5549a747e4fSDavid du Colombier 	}
5559a747e4fSDavid du Colombier 
5569a747e4fSDavid du Colombier 	if(!S_ISDIR(fid->st.st_mode)){
5579a747e4fSDavid du Colombier 		seterror(tx, Enotdir);
5589a747e4fSDavid du Colombier 		return;
5599a747e4fSDavid du Colombier 	}
5609a747e4fSDavid du Colombier 
5619a747e4fSDavid du Colombier 	if(usercreate(fid, rx->name, rx->mode, rx->perm, &e) < 0){
5629a747e4fSDavid du Colombier 		seterror(tx, e);
5639a747e4fSDavid du Colombier 		return;
5649a747e4fSDavid du Colombier 	}
5659a747e4fSDavid du Colombier 
5669a747e4fSDavid du Colombier 	if(fidstat(fid, &e) < 0){
5679a747e4fSDavid du Colombier 		seterror(tx, e);
5689a747e4fSDavid du Colombier 		return;
5699a747e4fSDavid du Colombier 	}
5709a747e4fSDavid du Colombier 
5719a747e4fSDavid du Colombier 	tx->iounit = 0;
5729a747e4fSDavid du Colombier 	tx->qid = stat2qid(&fid->st);
5739a747e4fSDavid du Colombier }
5749a747e4fSDavid du Colombier 
5759a747e4fSDavid du Colombier uchar
5769a747e4fSDavid du Colombier modebyte(struct stat *st)
5779a747e4fSDavid du Colombier {
5789a747e4fSDavid du Colombier 	uchar b;
5799a747e4fSDavid du Colombier 
5809a747e4fSDavid du Colombier 	b = 0;
5819a747e4fSDavid du Colombier 
5829a747e4fSDavid du Colombier 	if(S_ISDIR(st->st_mode))
5839a747e4fSDavid du Colombier 		b |= QTDIR;
5849a747e4fSDavid du Colombier 
5859a747e4fSDavid du Colombier 	/* no way to test append-only */
5869a747e4fSDavid du Colombier 	/* no real way to test exclusive use, but mark devices as such */
5879a747e4fSDavid du Colombier 	if(S_ISSPECIAL(st->st_mode))
5889a747e4fSDavid du Colombier 		b |= QTEXCL;
5899a747e4fSDavid du Colombier 
5909a747e4fSDavid du Colombier 	return b;
5919a747e4fSDavid du Colombier }
5929a747e4fSDavid du Colombier 
5939a747e4fSDavid du Colombier ulong
5949a747e4fSDavid du Colombier plan9mode(struct stat *st)
5959a747e4fSDavid du Colombier {
5969a747e4fSDavid du Colombier 	return ((ulong)modebyte(st)<<24) | (st->st_mode & 0777);
5979a747e4fSDavid du Colombier }
5989a747e4fSDavid du Colombier 
5999a747e4fSDavid du Colombier /*
6009a747e4fSDavid du Colombier  * this is for chmod, so don't worry about S_IFDIR
6019a747e4fSDavid du Colombier  */
6029a747e4fSDavid du Colombier mode_t
6039a747e4fSDavid du Colombier unixmode(Dir *d)
6049a747e4fSDavid du Colombier {
6059a747e4fSDavid du Colombier 	return (mode_t)(d->mode&0777);
6069a747e4fSDavid du Colombier }
6079a747e4fSDavid du Colombier 
6089a747e4fSDavid du Colombier Qid
6099a747e4fSDavid du Colombier stat2qid(struct stat *st)
6109a747e4fSDavid du Colombier {
6119a747e4fSDavid du Colombier 	uchar *p, *ep, *q;
6129a747e4fSDavid du Colombier 	Qid qid;
6139a747e4fSDavid du Colombier 
6149a747e4fSDavid du Colombier 	/*
6159a747e4fSDavid du Colombier 	 * For now, ignore the device number.
6169a747e4fSDavid du Colombier 	 */
6179a747e4fSDavid du Colombier 	qid.path = 0;
6189a747e4fSDavid du Colombier 	p = (uchar*)&qid.path;
6199a747e4fSDavid du Colombier 	ep = p+sizeof(qid.path);
6209a747e4fSDavid du Colombier 	q = p+sizeof(ino_t);
6219a747e4fSDavid du Colombier 	if(q > ep){
6229a747e4fSDavid du Colombier 		fprint(2, "warning: inode number too big\n");
6239a747e4fSDavid du Colombier 		q = ep;
6249a747e4fSDavid du Colombier 	}
6259a747e4fSDavid du Colombier 	memmove(p, &st->st_ino, q-p);
6269a747e4fSDavid du Colombier 	q = q+sizeof(dev_t);
6279a747e4fSDavid du Colombier 	if(q > ep){
628*dfda52d8SDavid du Colombier /*
629*dfda52d8SDavid du Colombier  *		fprint(2, "warning: inode number + device number too big %d+%d\n",
630*dfda52d8SDavid du Colombier  *			sizeof(ino_t), sizeof(dev_t));
631*dfda52d8SDavid du Colombier  */
6329a747e4fSDavid du Colombier 		q = ep - sizeof(dev_t);
6339a747e4fSDavid du Colombier 		if(q < p)
6349a747e4fSDavid du Colombier 			fprint(2, "warning: device number too big by itself\n");
6359a747e4fSDavid du Colombier 		else
6369a747e4fSDavid du Colombier 			*(dev_t*)q ^= st->st_dev;
6379a747e4fSDavid du Colombier 	}
6389a747e4fSDavid du Colombier 
6399a747e4fSDavid du Colombier 	qid.vers = st->st_mtime ^ (st->st_size << 8);
6409a747e4fSDavid du Colombier 	qid.type = modebyte(st);
6419a747e4fSDavid du Colombier 	return qid;
6429a747e4fSDavid du Colombier }
6439a747e4fSDavid du Colombier 
644*dfda52d8SDavid du Colombier char *
645*dfda52d8SDavid du Colombier enfrog(char *src)
646*dfda52d8SDavid du Colombier {
647*dfda52d8SDavid du Colombier 	char *d, *dst;
648*dfda52d8SDavid du Colombier 	uchar *s;
649*dfda52d8SDavid du Colombier 
650*dfda52d8SDavid du Colombier 	d = dst = emalloc(strlen(src)*3 + 1);
651*dfda52d8SDavid du Colombier 	for (s = (uchar *)src; *s; s++)
652*dfda52d8SDavid du Colombier 		if(isfrog[*s] || *s == '\\')
653*dfda52d8SDavid du Colombier 			d += sprintf(d, "\\%02x", *s);
654*dfda52d8SDavid du Colombier 		else
655*dfda52d8SDavid du Colombier 			*d++ = *s;
656*dfda52d8SDavid du Colombier 	*d = 0;
657*dfda52d8SDavid du Colombier 	return dst;
658*dfda52d8SDavid du Colombier }
659*dfda52d8SDavid du Colombier 
660*dfda52d8SDavid du Colombier char *
661*dfda52d8SDavid du Colombier defrog(char *s)
662*dfda52d8SDavid du Colombier {
663*dfda52d8SDavid du Colombier 	char *d, *dst, buf[3];
664*dfda52d8SDavid du Colombier 
665*dfda52d8SDavid du Colombier 	d = dst = emalloc(strlen(s) + 1);
666*dfda52d8SDavid du Colombier 	for(; *s; s++)
667*dfda52d8SDavid du Colombier 		if(*s == '\\' && strlen(s) >= 3){
668*dfda52d8SDavid du Colombier 			buf[0] = *++s;			/* skip \ */
669*dfda52d8SDavid du Colombier 			buf[1] = *++s;
670*dfda52d8SDavid du Colombier 			buf[2] = 0;
671*dfda52d8SDavid du Colombier 			*d++ = strtoul(buf, NULL, 16);
672*dfda52d8SDavid du Colombier 		} else
673*dfda52d8SDavid du Colombier 			*d++ = *s;
674*dfda52d8SDavid du Colombier 	*d = 0;
675*dfda52d8SDavid du Colombier 	return dst;
676*dfda52d8SDavid du Colombier }
677*dfda52d8SDavid du Colombier 
6789a747e4fSDavid du Colombier void
6799a747e4fSDavid du Colombier stat2dir(char *path, struct stat *st, Dir *d)
6809a747e4fSDavid du Colombier {
6819a747e4fSDavid du Colombier 	User *u;
682*dfda52d8SDavid du Colombier 	char *q, *p, *npath;
6839a747e4fSDavid du Colombier 
6849a747e4fSDavid du Colombier 	memset(d, 0, sizeof(*d));
6859a747e4fSDavid du Colombier 	d->qid = stat2qid(st);
6869a747e4fSDavid du Colombier 	d->mode = plan9mode(st);
6879a747e4fSDavid du Colombier 	d->atime = st->st_atime;
6889a747e4fSDavid du Colombier 	d->mtime = st->st_mtime;
6899a747e4fSDavid du Colombier 	d->length = st->st_size;
6909a747e4fSDavid du Colombier 
6919a747e4fSDavid du Colombier 	d->uid = (u = uid2user(st->st_uid)) ? u->name : "???";
6929a747e4fSDavid du Colombier 	d->gid = (u = gid2user(st->st_gid)) ? u->name : "???";
6939a747e4fSDavid du Colombier 	d->muid = "";
6949a747e4fSDavid du Colombier 
6959a747e4fSDavid du Colombier 	if((q = strrchr(path, '/')) != nil)
696*dfda52d8SDavid du Colombier 		d->name = enfrog(q+1);
6979a747e4fSDavid du Colombier 	else
698*dfda52d8SDavid du Colombier 		d->name = enfrog(path);
6999a747e4fSDavid du Colombier }
7009a747e4fSDavid du Colombier 
7019a747e4fSDavid du Colombier void
7029a747e4fSDavid du Colombier rread(Fcall *rx, Fcall *tx)
7039a747e4fSDavid du Colombier {
7049a747e4fSDavid du Colombier 	char *e, *path;
7059a747e4fSDavid du Colombier 	uchar *p, *ep;
7069a747e4fSDavid du Colombier 	int n;
7079a747e4fSDavid du Colombier 	Fid *fid;
7083e12c5d1SDavid du Colombier 	Dir d;
7099a747e4fSDavid du Colombier 	struct stat st;
7103e12c5d1SDavid du Colombier 
7119a747e4fSDavid du Colombier 	if(rx->count > msize-IOHDRSZ){
7129a747e4fSDavid du Colombier 		seterror(tx, Etoolarge);
7139a747e4fSDavid du Colombier 		return;
7143e12c5d1SDavid du Colombier 	}
7159a747e4fSDavid du Colombier 
71650a9bdd4SDavid du Colombier 	if((fid = oldfidex(rx->fid, -1, &e)) == nil){
71750a9bdd4SDavid du Colombier 		seterror(tx, e);
71850a9bdd4SDavid du Colombier 		return;
71950a9bdd4SDavid du Colombier 	}
72050a9bdd4SDavid du Colombier 
72150a9bdd4SDavid du Colombier 	if (fid->auth) {
72250a9bdd4SDavid du Colombier 		char *e;
72350a9bdd4SDavid du Colombier 		e = auth->read(rx, tx);
72450a9bdd4SDavid du Colombier 		if (e)
7259a747e4fSDavid du Colombier 			seterror(tx, e);
7269a747e4fSDavid du Colombier 		return;
7273e12c5d1SDavid du Colombier 	}
7289a747e4fSDavid du Colombier 
7299a747e4fSDavid du Colombier 	if(fid->omode == -1 || (fid->omode&3) == OWRITE){
7309a747e4fSDavid du Colombier 		seterror(tx, Ebadusefid);
7319a747e4fSDavid du Colombier 		return;
7329a747e4fSDavid du Colombier 	}
7339a747e4fSDavid du Colombier 
7349a747e4fSDavid du Colombier 	if(fid->dir){
7359a747e4fSDavid du Colombier 		if(rx->offset != fid->diroffset){
7369a747e4fSDavid du Colombier 			if(rx->offset != 0){
7379a747e4fSDavid du Colombier 				seterror(tx, Ebadoffset);
7389a747e4fSDavid du Colombier 				return;
7399a747e4fSDavid du Colombier 			}
7409a747e4fSDavid du Colombier 			rewinddir(fid->dir);
7419a747e4fSDavid du Colombier 			fid->diroffset = 0;
7429a747e4fSDavid du Colombier 		}
7439a747e4fSDavid du Colombier 
7449a747e4fSDavid du Colombier 		p = (uchar*)tx->data;
7459a747e4fSDavid du Colombier 		ep = (uchar*)tx->data+rx->count;
7469a747e4fSDavid du Colombier 		for(;;){
7479a747e4fSDavid du Colombier 			if(p+BIT16SZ >= ep)
7483e12c5d1SDavid du Colombier 				break;
7499a747e4fSDavid du Colombier 			if(fid->dirent == nil)	/* one entry cache for when convD2M fails */
7509a747e4fSDavid du Colombier 				if((fid->dirent = readdir(fid->dir)) == nil)
7519a747e4fSDavid du Colombier 					break;
7529a747e4fSDavid du Colombier 			if(strcmp(fid->dirent->d_name, ".") == 0
7539a747e4fSDavid du Colombier 			|| strcmp(fid->dirent->d_name, "..") == 0){
7549a747e4fSDavid du Colombier 				fid->dirent = nil;
7553e12c5d1SDavid du Colombier 				continue;
7569a747e4fSDavid du Colombier 			}
757*dfda52d8SDavid du Colombier 			path = estrpath(fid->path, fid->dirent->d_name, 0);
7589a747e4fSDavid du Colombier 			memset(&st, 0, sizeof st);
7599a747e4fSDavid du Colombier 			if(stat(path, &st) < 0){
7609a747e4fSDavid du Colombier 				fprint(2, "dirread: stat(%s) failed: %s\n", path, strerror(errno));
7619a747e4fSDavid du Colombier 				fid->dirent = nil;
7629a747e4fSDavid du Colombier 				free(path);
7633e12c5d1SDavid du Colombier 				continue;
7643e12c5d1SDavid du Colombier 			}
7653e12c5d1SDavid du Colombier 			free(path);
7669a747e4fSDavid du Colombier 			stat2dir(fid->dirent->d_name, &st, &d);
7679a747e4fSDavid du Colombier 			if((n=(old9p ? convD2Mold : convD2M)(&d, p, ep-p)) <= BIT16SZ)
7689a747e4fSDavid du Colombier 				break;
7699a747e4fSDavid du Colombier 			p += n;
7709a747e4fSDavid du Colombier 			fid->dirent = nil;
7713e12c5d1SDavid du Colombier 		}
7729a747e4fSDavid du Colombier 		tx->count = p - (uchar*)tx->data;
7739a747e4fSDavid du Colombier 		fid->diroffset += tx->count;
7743e12c5d1SDavid du Colombier 	}else{
7759a747e4fSDavid du Colombier 		if((n = pread(fid->fd, tx->data, rx->count, rx->offset)) < 0){
7769a747e4fSDavid du Colombier 			seterror(tx, strerror(errno));
7779a747e4fSDavid du Colombier 			return;
778219b2ee8SDavid du Colombier 		}
7799a747e4fSDavid du Colombier 		tx->count = n;
7803e12c5d1SDavid du Colombier 	}
7813e12c5d1SDavid du Colombier }
7823e12c5d1SDavid du Colombier 
7833e12c5d1SDavid du Colombier void
7849a747e4fSDavid du Colombier rwrite(Fcall *rx, Fcall *tx)
7853e12c5d1SDavid du Colombier {
7869a747e4fSDavid du Colombier 	char *e;
7879a747e4fSDavid du Colombier 	Fid *fid;
7883e12c5d1SDavid du Colombier 	int n;
7893e12c5d1SDavid du Colombier 
7909a747e4fSDavid du Colombier 	if(rx->count > msize-IOHDRSZ){
7919a747e4fSDavid du Colombier 		seterror(tx, Etoolarge);
7927dd7cddfSDavid du Colombier 		return;
7937dd7cddfSDavid du Colombier 	}
7949a747e4fSDavid du Colombier 
79550a9bdd4SDavid du Colombier 	if((fid = oldfidex(rx->fid, -1, &e)) == nil){
79650a9bdd4SDavid du Colombier 		seterror(tx, e);
79750a9bdd4SDavid du Colombier 		return;
79850a9bdd4SDavid du Colombier 	}
79950a9bdd4SDavid du Colombier 
80050a9bdd4SDavid du Colombier 	if (fid->auth) {
80150a9bdd4SDavid du Colombier 		char *e;
80250a9bdd4SDavid du Colombier 		e = auth->write(rx, tx);
80350a9bdd4SDavid du Colombier 		if (e)
8049a747e4fSDavid du Colombier 			seterror(tx, e);
8059a747e4fSDavid du Colombier 		return;
8063e12c5d1SDavid du Colombier 	}
8079a747e4fSDavid du Colombier 
8089a747e4fSDavid du Colombier 	if(fid->omode == -1 || (fid->omode&3) == OREAD || (fid->omode&3) == OEXEC){
8099a747e4fSDavid du Colombier 		seterror(tx, Ebadusefid);
8109a747e4fSDavid du Colombier 		return;
8119a747e4fSDavid du Colombier 	}
8129a747e4fSDavid du Colombier 
8139a747e4fSDavid du Colombier 	if((n = pwrite(fid->fd, rx->data, rx->count, rx->offset)) < 0){
8149a747e4fSDavid du Colombier 		seterror(tx, strerror(errno));
8159a747e4fSDavid du Colombier 		return;
8169a747e4fSDavid du Colombier 	}
8179a747e4fSDavid du Colombier 	tx->count = n;
8183e12c5d1SDavid du Colombier }
8193e12c5d1SDavid du Colombier 
8203e12c5d1SDavid du Colombier void
8219a747e4fSDavid du Colombier rclunk(Fcall *rx, Fcall *tx)
8223e12c5d1SDavid du Colombier {
8239a747e4fSDavid du Colombier 	char *e;
8249a747e4fSDavid du Colombier 	Fid *fid;
8253e12c5d1SDavid du Colombier 
82650a9bdd4SDavid du Colombier 	if((fid = oldfidex(rx->fid, -1, &e)) == nil){
8279a747e4fSDavid du Colombier 		seterror(tx, e);
8289a747e4fSDavid du Colombier 		return;
8299a747e4fSDavid du Colombier 	}
83050a9bdd4SDavid du Colombier 	if (fid->auth) {
83150a9bdd4SDavid du Colombier 		if (auth->clunk) {
83250a9bdd4SDavid du Colombier 			e = (*auth->clunk)(rx, tx);
83350a9bdd4SDavid du Colombier 			if (e) {
83450a9bdd4SDavid du Colombier 				seterror(tx, e);
83550a9bdd4SDavid du Colombier 				return;
83650a9bdd4SDavid du Colombier 			}
83750a9bdd4SDavid du Colombier 		}
83850a9bdd4SDavid du Colombier 	}
83950a9bdd4SDavid du Colombier 	else if(fid->omode != -1 && fid->omode&ORCLOSE)
8409a747e4fSDavid du Colombier 		remove(fid->path);
8419a747e4fSDavid du Colombier 	freefid(fid);
8423e12c5d1SDavid du Colombier }
843219b2ee8SDavid du Colombier 
8449a747e4fSDavid du Colombier void
8459a747e4fSDavid du Colombier rremove(Fcall *rx, Fcall *tx)
8463e12c5d1SDavid du Colombier {
8479a747e4fSDavid du Colombier 	char *e;
8489a747e4fSDavid du Colombier 	Fid *fid;
8493e12c5d1SDavid du Colombier 
8509a747e4fSDavid du Colombier 	if((fid = oldfid(rx->fid, &e)) == nil){
8519a747e4fSDavid du Colombier 		seterror(tx, e);
8529a747e4fSDavid du Colombier 		return;
8533e12c5d1SDavid du Colombier 	}
8549a747e4fSDavid du Colombier 	if(userremove(fid, &e) < 0)
8559a747e4fSDavid du Colombier 		seterror(tx, e);
8569a747e4fSDavid du Colombier 	freefid(fid);
8579a747e4fSDavid du Colombier }
8589a747e4fSDavid du Colombier 
8599a747e4fSDavid du Colombier void
8609a747e4fSDavid du Colombier rstat(Fcall *rx, Fcall *tx)
8619a747e4fSDavid du Colombier {
8629a747e4fSDavid du Colombier 	char *e;
8639a747e4fSDavid du Colombier 	Fid *fid;
8649a747e4fSDavid du Colombier 	Dir d;
8659a747e4fSDavid du Colombier 
8669a747e4fSDavid du Colombier 	if((fid = oldfid(rx->fid, &e)) == nil){
8679a747e4fSDavid du Colombier 		seterror(tx, e);
8689a747e4fSDavid du Colombier 		return;
8699a747e4fSDavid du Colombier 	}
8709a747e4fSDavid du Colombier 
8719a747e4fSDavid du Colombier 	if(fidstat(fid, &e) < 0){
8729a747e4fSDavid du Colombier 		seterror(tx, e);
8739a747e4fSDavid du Colombier 		return;
8749a747e4fSDavid du Colombier 	}
8759a747e4fSDavid du Colombier 
8769a747e4fSDavid du Colombier 	stat2dir(fid->path, &fid->st, &d);
8779a747e4fSDavid du Colombier 	if((tx->nstat=(old9p ? convD2Mold : convD2M)(&d, tx->stat, msize)) <= BIT16SZ)
8789a747e4fSDavid du Colombier 		seterror(tx, "convD2M fails");
8799a747e4fSDavid du Colombier }
8809a747e4fSDavid du Colombier 
8819a747e4fSDavid du Colombier void
8829a747e4fSDavid du Colombier rwstat(Fcall *rx, Fcall *tx)
8839a747e4fSDavid du Colombier {
8849a747e4fSDavid du Colombier 	char *e;
8859a747e4fSDavid du Colombier 	char *p, *old, *new, *dir;
8869a747e4fSDavid du Colombier 	gid_t gid;
8879a747e4fSDavid du Colombier 	Dir d;
8889a747e4fSDavid du Colombier 	Fid *fid;
8899a747e4fSDavid du Colombier 
8909a747e4fSDavid du Colombier 	if((fid = oldfid(rx->fid, &e)) == nil){
8919a747e4fSDavid du Colombier 		seterror(tx, e);
8929a747e4fSDavid du Colombier 		return;
8939a747e4fSDavid du Colombier 	}
8949a747e4fSDavid du Colombier 
8959a747e4fSDavid du Colombier 	/*
8969a747e4fSDavid du Colombier 	 * wstat is supposed to be atomic.
8979a747e4fSDavid du Colombier 	 * we check all the things we can before trying anything.
8989a747e4fSDavid du Colombier 	 * still, if we are told to truncate a file and rename it and only
8999a747e4fSDavid du Colombier 	 * one works, we're screwed.  in such cases we leave things
9009a747e4fSDavid du Colombier 	 * half broken and return an error.  it's hardly perfect.
9019a747e4fSDavid du Colombier 	 */
9029a747e4fSDavid du Colombier 	if((old9p ? convM2Dold : convM2D)(rx->stat, rx->nstat, &d, (char*)rx->stat) <= BIT16SZ){
9039a747e4fSDavid du Colombier 		seterror(tx, Ewstatbuffer);
9049a747e4fSDavid du Colombier 		return;
9059a747e4fSDavid du Colombier 	}
9069a747e4fSDavid du Colombier 
9079a747e4fSDavid du Colombier 	if(fidstat(fid, &e) < 0){
9089a747e4fSDavid du Colombier 		seterror(tx, e);
9099a747e4fSDavid du Colombier 		return;
9109a747e4fSDavid du Colombier 	}
9119a747e4fSDavid du Colombier 
9129a747e4fSDavid du Colombier 	/*
9139a747e4fSDavid du Colombier 	 * The casting is necessary because d.mode is ulong and might,
9149a747e4fSDavid du Colombier 	 * on some systems, be 64 bits.  We only want to compare the
9159a747e4fSDavid du Colombier 	 * bottom 32 bits, since that's all that gets sent in the protocol.
9169a747e4fSDavid du Colombier 	 *
9179a747e4fSDavid du Colombier 	 * Same situation for d.mtime and d.length (although that last check
9189a747e4fSDavid du Colombier 	 * is admittedly superfluous, given the current lack of 128-bit machines).
9199a747e4fSDavid du Colombier 	 */
9209a747e4fSDavid du Colombier 	gid = (gid_t)-1;
9219a747e4fSDavid du Colombier 	if(d.gid[0] != '\0'){
9229a747e4fSDavid du Colombier 		User *g;
9239a747e4fSDavid du Colombier 
9249a747e4fSDavid du Colombier 		g = gname2user(d.gid);
9259a747e4fSDavid du Colombier 		if(g == nil){
9269a747e4fSDavid du Colombier 			seterror(tx, Eunknowngroup);
9279a747e4fSDavid du Colombier 			return;
9289a747e4fSDavid du Colombier 		}
9299a747e4fSDavid du Colombier 		gid = (gid_t)g->id;
9309a747e4fSDavid du Colombier 
9319a747e4fSDavid du Colombier 		if(groupchange(fid->u, gid2user(gid), &e) < 0){
9329a747e4fSDavid du Colombier 			seterror(tx, e);
9339a747e4fSDavid du Colombier 			return;
9349a747e4fSDavid du Colombier 		}
9359a747e4fSDavid du Colombier 	}
9369a747e4fSDavid du Colombier 
9379a747e4fSDavid du Colombier 	if((u32int)d.mode != (u32int)~0 && (((d.mode&DMDIR)!=0) ^ (S_ISDIR(fid->st.st_mode)!=0))){
9389a747e4fSDavid du Colombier 		seterror(tx, Edirchange);
9399a747e4fSDavid du Colombier 		return;
9409a747e4fSDavid du Colombier 	}
9419a747e4fSDavid du Colombier 
9429a747e4fSDavid du Colombier 	if(strcmp(fid->path, "/") == 0){
9439a747e4fSDavid du Colombier 		seterror(tx, "no wstat of root");
9449a747e4fSDavid du Colombier 		return;
9459a747e4fSDavid du Colombier 	}
9469a747e4fSDavid du Colombier 
9479a747e4fSDavid du Colombier 	/*
9489a747e4fSDavid du Colombier 	 * try things in increasing order of harm to the file.
9499a747e4fSDavid du Colombier 	 * mtime should come after truncate so that if you
9509a747e4fSDavid du Colombier 	 * do both the mtime actually takes effect, but i'd rather
9519a747e4fSDavid du Colombier 	 * leave truncate until last.
9529a747e4fSDavid du Colombier 	 * (see above comment about atomicity).
9539a747e4fSDavid du Colombier 	 */
9549a747e4fSDavid du Colombier 	if((u32int)d.mode != (u32int)~0 && chmod(fid->path, unixmode(&d)) < 0){
9559a747e4fSDavid du Colombier 		if(chatty9p)
9569a747e4fSDavid du Colombier 			fprint(2, "chmod(%s, 0%luo) failed\n", fid->path, unixmode(&d));
9579a747e4fSDavid du Colombier 		seterror(tx, strerror(errno));
9589a747e4fSDavid du Colombier 		return;
9599a747e4fSDavid du Colombier 	}
9609a747e4fSDavid du Colombier 
9619a747e4fSDavid du Colombier 	if((u32int)d.mtime != (u32int)~0){
9629a747e4fSDavid du Colombier 		struct utimbuf t;
9639a747e4fSDavid du Colombier 
9649a747e4fSDavid du Colombier 		t.actime = 0;
9659a747e4fSDavid du Colombier 		t.modtime = d.mtime;
9669a747e4fSDavid du Colombier 		if(utime(fid->path, &t) < 0){
9679a747e4fSDavid du Colombier 			if(chatty9p)
9689a747e4fSDavid du Colombier 				fprint(2, "utime(%s) failed\n", fid->path);
9699a747e4fSDavid du Colombier 			seterror(tx, strerror(errno));
9709a747e4fSDavid du Colombier 			return;
9719a747e4fSDavid du Colombier 		}
9729a747e4fSDavid du Colombier 	}
9739a747e4fSDavid du Colombier 
9749a747e4fSDavid du Colombier 	if(gid != (gid_t)-1 && gid != fid->st.st_gid){
9759a747e4fSDavid du Colombier 		if(chown(fid->path, (uid_t)-1, gid) < 0){
9769a747e4fSDavid du Colombier 			if(chatty9p)
9779a747e4fSDavid du Colombier 				fprint(2, "chgrp(%s, %d) failed\n", fid->path, gid);
9789a747e4fSDavid du Colombier 			seterror(tx, strerror(errno));
9799a747e4fSDavid du Colombier 			return;
9809a747e4fSDavid du Colombier 		}
9819a747e4fSDavid du Colombier 	}
9829a747e4fSDavid du Colombier 
9839a747e4fSDavid du Colombier 	if(d.name[0]){
9849a747e4fSDavid du Colombier 		old = fid->path;
9859a747e4fSDavid du Colombier 		dir = estrdup(fid->path);
9869a747e4fSDavid du Colombier 		if((p = strrchr(dir, '/')) > dir)
9879a747e4fSDavid du Colombier 			*p = '\0';
9883e12c5d1SDavid du Colombier 		else{
9899a747e4fSDavid du Colombier 			seterror(tx, "whoops: can't happen in u9fs");
9909a747e4fSDavid du Colombier 			return;
9913e12c5d1SDavid du Colombier 		}
992*dfda52d8SDavid du Colombier 		new = estrpath(dir, d.name, 1);
9939a747e4fSDavid du Colombier 		if(strcmp(old, new) != 0 && rename(old, new) < 0){
9949a747e4fSDavid du Colombier 			if(chatty9p)
9959a747e4fSDavid du Colombier 				fprint(2, "rename(%s, %s) failed\n", old, new);
9969a747e4fSDavid du Colombier 			seterror(tx, strerror(errno));
9979a747e4fSDavid du Colombier 			free(new);
9989a747e4fSDavid du Colombier 			free(dir);
9999a747e4fSDavid du Colombier 			return;
10009a747e4fSDavid du Colombier 		}
10019a747e4fSDavid du Colombier 		fid->path = new;
10029a747e4fSDavid du Colombier 		free(old);
10039a747e4fSDavid du Colombier 		free(dir);
10049a747e4fSDavid du Colombier 	}
10059a747e4fSDavid du Colombier 
10069a747e4fSDavid du Colombier 	if((u64int)d.length != (u64int)~0 && truncate(fid->path, d.length) < 0){
10079a747e4fSDavid du Colombier 		fprint(2, "truncate(%s, %lld) failed\n", fid->path, d.length);
10089a747e4fSDavid du Colombier 		seterror(tx, strerror(errno));
10099a747e4fSDavid du Colombier 		return;
10109a747e4fSDavid du Colombier 	}
10119a747e4fSDavid du Colombier }
10129a747e4fSDavid du Colombier 
10139a747e4fSDavid du Colombier /*
10149a747e4fSDavid du Colombier  * we keep a table by numeric id.  by name lookups happen infrequently
10159a747e4fSDavid du Colombier  * while by-number lookups happen once for every directory entry read
10169a747e4fSDavid du Colombier  * and every stat request.
10179a747e4fSDavid du Colombier  */
10189a747e4fSDavid du Colombier User *utab[64];
10199a747e4fSDavid du Colombier User *gtab[64];
10209a747e4fSDavid du Colombier 
10219a747e4fSDavid du Colombier User*
10229a747e4fSDavid du Colombier adduser(struct passwd *p)
10233e12c5d1SDavid du Colombier {
10249a747e4fSDavid du Colombier 	User *u;
10253e12c5d1SDavid du Colombier 
10269a747e4fSDavid du Colombier 	u = emalloc(sizeof(*u));
10279a747e4fSDavid du Colombier 	u->id = p->pw_uid;
10289a747e4fSDavid du Colombier 	u->name = estrdup(p->pw_name);
10299a747e4fSDavid du Colombier 	u->next = utab[p->pw_uid%nelem(utab)];
10309a747e4fSDavid du Colombier 	u->defaultgid = p->pw_gid;
10319a747e4fSDavid du Colombier 	utab[p->pw_uid%nelem(utab)] = u;
10329a747e4fSDavid du Colombier 	return u;
10333e12c5d1SDavid du Colombier }
10343e12c5d1SDavid du Colombier 
10353e12c5d1SDavid du Colombier int
10369a747e4fSDavid du Colombier useringroup(User *u, User *g)
10373e12c5d1SDavid du Colombier {
10389a747e4fSDavid du Colombier 	int i;
10399a747e4fSDavid du Colombier 
10409a747e4fSDavid du Colombier 	for(i=0; i<g->nmem; i++)
10419a747e4fSDavid du Colombier 		if(strcmp(g->mem[i], u->name) == 0)
10423e12c5d1SDavid du Colombier 			return 1;
10439a747e4fSDavid du Colombier 
10449a747e4fSDavid du Colombier 	/*
10459a747e4fSDavid du Colombier 	 * Hack around common Unix problem that everyone has
10469a747e4fSDavid du Colombier 	 * default group "user" but /etc/group lists no members.
10479a747e4fSDavid du Colombier 	 */
10489a747e4fSDavid du Colombier 	if(u->defaultgid == g->id)
10499a747e4fSDavid du Colombier 		return 1;
10503e12c5d1SDavid du Colombier 	return 0;
10513e12c5d1SDavid du Colombier }
10523e12c5d1SDavid du Colombier 
10539a747e4fSDavid du Colombier User*
10549a747e4fSDavid du Colombier addgroup(struct group *g)
10553e12c5d1SDavid du Colombier {
10569a747e4fSDavid du Colombier 	User *u;
10579a747e4fSDavid du Colombier 	char **p;
10583e12c5d1SDavid du Colombier 	int n;
10593e12c5d1SDavid du Colombier 
10609a747e4fSDavid du Colombier 	u = emalloc(sizeof(*u));
10619a747e4fSDavid du Colombier 	n = 0;
10629a747e4fSDavid du Colombier 	for(p=g->gr_mem; *p; p++)
10639a747e4fSDavid du Colombier 		n++;
10649a747e4fSDavid du Colombier 	u->mem = emalloc(sizeof(u->mem[0])*n);
10659a747e4fSDavid du Colombier 	n = 0;
10669a747e4fSDavid du Colombier 	for(p=g->gr_mem; *p; p++)
10679a747e4fSDavid du Colombier 		u->mem[n++] = estrdup(*p);
10689a747e4fSDavid du Colombier 	u->nmem = n;
10699a747e4fSDavid du Colombier 	u->id = g->gr_gid;
10709a747e4fSDavid du Colombier 	u->name = estrdup(g->gr_name);
10719a747e4fSDavid du Colombier 	u->next = gtab[g->gr_gid%nelem(gtab)];
10729a747e4fSDavid du Colombier 	gtab[g->gr_gid%nelem(gtab)] = u;
10739a747e4fSDavid du Colombier 	return u;
10743e12c5d1SDavid du Colombier }
10753e12c5d1SDavid du Colombier 
10769a747e4fSDavid du Colombier User*
10779a747e4fSDavid du Colombier uname2user(char *name)
10787dd7cddfSDavid du Colombier {
10797dd7cddfSDavid du Colombier 	int i;
10809a747e4fSDavid du Colombier 	User *u;
10819a747e4fSDavid du Colombier 	struct passwd *p;
10827dd7cddfSDavid du Colombier 
10839a747e4fSDavid du Colombier 	for(i=0; i<nelem(utab); i++)
10849a747e4fSDavid du Colombier 		for(u=utab[i]; u; u=u->next)
10859a747e4fSDavid du Colombier 			if(strcmp(u->name, name) == 0)
10869a747e4fSDavid du Colombier 				return u;
10879a747e4fSDavid du Colombier 
10889a747e4fSDavid du Colombier 	if((p = getpwnam(name)) == nil)
10899a747e4fSDavid du Colombier 		return nil;
10909a747e4fSDavid du Colombier 	return adduser(p);
10917dd7cddfSDavid du Colombier }
10927dd7cddfSDavid du Colombier 
10939a747e4fSDavid du Colombier User*
10949a747e4fSDavid du Colombier uid2user(int id)
10959a747e4fSDavid du Colombier {
10969a747e4fSDavid du Colombier 	User *u;
10979a747e4fSDavid du Colombier 	struct passwd *p;
10989a747e4fSDavid du Colombier 
10999a747e4fSDavid du Colombier 	for(u=utab[id%nelem(utab)]; u; u=u->next)
11009a747e4fSDavid du Colombier 		if(u->id == id)
11019a747e4fSDavid du Colombier 			return u;
11029a747e4fSDavid du Colombier 
11039a747e4fSDavid du Colombier 	if((p = getpwuid(id)) == nil)
11049a747e4fSDavid du Colombier 		return nil;
11059a747e4fSDavid du Colombier 	return adduser(p);
11069a747e4fSDavid du Colombier }
11079a747e4fSDavid du Colombier 
11089a747e4fSDavid du Colombier User*
11099a747e4fSDavid du Colombier gname2user(char *name)
11103e12c5d1SDavid du Colombier {
11113e12c5d1SDavid du Colombier 	int i;
11129a747e4fSDavid du Colombier 	User *u;
11139a747e4fSDavid du Colombier 	struct group *g;
11143e12c5d1SDavid du Colombier 
11159a747e4fSDavid du Colombier 	for(i=0; i<nelem(gtab); i++)
11169a747e4fSDavid du Colombier 		for(u=gtab[i]; u; u=u->next)
11179a747e4fSDavid du Colombier 			if(strcmp(u->name, name) == 0)
11189a747e4fSDavid du Colombier 				return u;
11199a747e4fSDavid du Colombier 
11209a747e4fSDavid du Colombier 	if((g = getgrnam(name)) == nil)
11219a747e4fSDavid du Colombier 		return nil;
11229a747e4fSDavid du Colombier 	return addgroup(g);
11239a747e4fSDavid du Colombier }
11249a747e4fSDavid du Colombier 
11259a747e4fSDavid du Colombier User*
11269a747e4fSDavid du Colombier gid2user(int id)
11279a747e4fSDavid du Colombier {
11289a747e4fSDavid du Colombier 	User *u;
11299a747e4fSDavid du Colombier 	struct group *g;
11309a747e4fSDavid du Colombier 
11319a747e4fSDavid du Colombier 	for(u=gtab[id%nelem(gtab)]; u; u=u->next)
11329a747e4fSDavid du Colombier 		if(u->id == id)
11339a747e4fSDavid du Colombier 			return u;
11349a747e4fSDavid du Colombier 
11359a747e4fSDavid du Colombier 	if((g = getgrgid(id)) == nil)
11369a747e4fSDavid du Colombier 		return nil;
11379a747e4fSDavid du Colombier 	return addgroup(g);
11383e12c5d1SDavid du Colombier }
11393e12c5d1SDavid du Colombier 
11403e12c5d1SDavid du Colombier void
11419a747e4fSDavid du Colombier sysfatal(char *fmt, ...)
11423e12c5d1SDavid du Colombier {
11439a747e4fSDavid du Colombier 	char buf[1024];
11449df35464SDavid du Colombier 	va_list va, temp;
11453e12c5d1SDavid du Colombier 
11469a747e4fSDavid du Colombier 	va_start(va, fmt);
11479df35464SDavid du Colombier 	va_copy(temp, va);
11489df35464SDavid du Colombier 	doprint(buf, buf+sizeof buf, fmt, &temp);
11499df35464SDavid du Colombier 	va_end(temp);
11509a747e4fSDavid du Colombier 	va_end(va);
11519a747e4fSDavid du Colombier 	fprint(2, "u9fs: %s\n", buf);
11529a747e4fSDavid du Colombier 	fprint(2, "last unix error: %s\n", strerror(errno));
11533e12c5d1SDavid du Colombier 	exit(1);
11543e12c5d1SDavid du Colombier }
11553e12c5d1SDavid du Colombier 
11563e12c5d1SDavid du Colombier void*
11579a747e4fSDavid du Colombier emalloc(size_t n)
11589a747e4fSDavid du Colombier {
11599a747e4fSDavid du Colombier 	void *p;
11609a747e4fSDavid du Colombier 
1161d9306527SDavid du Colombier 	if(n == 0)
1162d9306527SDavid du Colombier 		n = 1;
11639a747e4fSDavid du Colombier 	p = malloc(n);
11649a747e4fSDavid du Colombier 	if(p == 0)
11659a747e4fSDavid du Colombier 		sysfatal("malloc(%ld) fails", (long)n);
11669a747e4fSDavid du Colombier 	memset(p, 0, n);
11679a747e4fSDavid du Colombier 	return p;
11689a747e4fSDavid du Colombier }
11699a747e4fSDavid du Colombier 
11709a747e4fSDavid du Colombier void*
11719a747e4fSDavid du Colombier erealloc(void *p, size_t n)
11723e12c5d1SDavid du Colombier {
11733e12c5d1SDavid du Colombier 	if(p == 0)
11743e12c5d1SDavid du Colombier 		p = malloc(n);
11753e12c5d1SDavid du Colombier 	else
11763e12c5d1SDavid du Colombier 		p = realloc(p, n);
11773e12c5d1SDavid du Colombier 	if(p == 0)
11789a747e4fSDavid du Colombier 		sysfatal("realloc(..., %ld) fails", (long)n);
11793e12c5d1SDavid du Colombier 	return p;
11803e12c5d1SDavid du Colombier }
11813e12c5d1SDavid du Colombier 
11823e12c5d1SDavid du Colombier char*
11833e12c5d1SDavid du Colombier estrdup(char *p)
11843e12c5d1SDavid du Colombier {
11853e12c5d1SDavid du Colombier 	p = strdup(p);
11863e12c5d1SDavid du Colombier 	if(p == 0)
11879a747e4fSDavid du Colombier 		sysfatal("strdup(%.20s) fails", p);
11883e12c5d1SDavid du Colombier 	return p;
11893e12c5d1SDavid du Colombier }
1190219b2ee8SDavid du Colombier 
11919a747e4fSDavid du Colombier char*
1192*dfda52d8SDavid du Colombier estrpath(char *p, char *q, int frog)
1193219b2ee8SDavid du Colombier {
11949a747e4fSDavid du Colombier 	char *r, *s;
1195219b2ee8SDavid du Colombier 
11969a747e4fSDavid du Colombier 	if(strcmp(q, "..") == 0){
11979a747e4fSDavid du Colombier 		r = estrdup(p);
11989a747e4fSDavid du Colombier 		if((s = strrchr(r, '/')) && s > r)
11999a747e4fSDavid du Colombier 			*s = '\0';
12009a747e4fSDavid du Colombier 		else if(s == r)
12019a747e4fSDavid du Colombier 			s[1] = '\0';
12029a747e4fSDavid du Colombier 		return r;
1203219b2ee8SDavid du Colombier 	}
12049a747e4fSDavid du Colombier 
1205*dfda52d8SDavid du Colombier 	if(frog)
1206*dfda52d8SDavid du Colombier 		q = defrog(q);
1207*dfda52d8SDavid du Colombier 	else
1208*dfda52d8SDavid du Colombier 		q = strdup(q);
12099a747e4fSDavid du Colombier 	r = emalloc(strlen(p)+1+strlen(q)+1);
12109a747e4fSDavid du Colombier 	strcpy(r, p);
12119a747e4fSDavid du Colombier 	if(r[0]=='\0' || r[strlen(r)-1] != '/')
12129a747e4fSDavid du Colombier 		strcat(r, "/");
12139a747e4fSDavid du Colombier 	strcat(r, q);
1214*dfda52d8SDavid du Colombier 	free(q);
12159a747e4fSDavid du Colombier 	return r;
12169a747e4fSDavid du Colombier }
12179a747e4fSDavid du Colombier 
12189a747e4fSDavid du Colombier Fid *fidtab[1];
12199a747e4fSDavid du Colombier 
12209a747e4fSDavid du Colombier Fid*
12219a747e4fSDavid du Colombier lookupfid(int fid)
12229a747e4fSDavid du Colombier {
12239a747e4fSDavid du Colombier 	Fid *f;
12249a747e4fSDavid du Colombier 
12259a747e4fSDavid du Colombier 	for(f=fidtab[fid%nelem(fidtab)]; f; f=f->next)
12269a747e4fSDavid du Colombier 		if(f->fid == fid)
12279a747e4fSDavid du Colombier 			return f;
12289a747e4fSDavid du Colombier 	return nil;
12299a747e4fSDavid du Colombier }
12309a747e4fSDavid du Colombier 
12319a747e4fSDavid du Colombier Fid*
12329a747e4fSDavid du Colombier newfid(int fid, char **ep)
12339a747e4fSDavid du Colombier {
12349a747e4fSDavid du Colombier 	Fid *f;
12359a747e4fSDavid du Colombier 
12369a747e4fSDavid du Colombier 	if(lookupfid(fid) != nil){
12379a747e4fSDavid du Colombier 		*ep = Efidactive;
12389a747e4fSDavid du Colombier 		return nil;
12399a747e4fSDavid du Colombier 	}
12409a747e4fSDavid du Colombier 
12419a747e4fSDavid du Colombier 	f = emalloc(sizeof(*f));
12429a747e4fSDavid du Colombier 	f->next = fidtab[fid%nelem(fidtab)];
12439a747e4fSDavid du Colombier 	if(f->next)
12449a747e4fSDavid du Colombier 		f->next->prev = f;
12459a747e4fSDavid du Colombier 	fidtab[fid%nelem(fidtab)] = f;
12469a747e4fSDavid du Colombier 	f->fid = fid;
12479a747e4fSDavid du Colombier 	f->fd = -1;
12489a747e4fSDavid du Colombier 	f->omode = -1;
12499a747e4fSDavid du Colombier 	return f;
12509a747e4fSDavid du Colombier }
12519a747e4fSDavid du Colombier 
12529a747e4fSDavid du Colombier Fid*
125350a9bdd4SDavid du Colombier newauthfid(int fid, void *magic, char **ep)
125450a9bdd4SDavid du Colombier {
125550a9bdd4SDavid du Colombier 	Fid *af;
125650a9bdd4SDavid du Colombier 	af = newfid(fid, ep);
125750a9bdd4SDavid du Colombier 	if (af == nil)
125850a9bdd4SDavid du Colombier 		return nil;
125950a9bdd4SDavid du Colombier 	af->auth = 1;
126050a9bdd4SDavid du Colombier 	af->authmagic = magic;
126150a9bdd4SDavid du Colombier 	return af;
126250a9bdd4SDavid du Colombier }
126350a9bdd4SDavid du Colombier 
126450a9bdd4SDavid du Colombier Fid*
126550a9bdd4SDavid du Colombier oldfidex(int fid, int auth, char **ep)
12669a747e4fSDavid du Colombier {
12679a747e4fSDavid du Colombier 	Fid *f;
12689a747e4fSDavid du Colombier 
12699a747e4fSDavid du Colombier 	if((f = lookupfid(fid)) == nil){
12709a747e4fSDavid du Colombier 		*ep = Ebadfid;
12719a747e4fSDavid du Colombier 		return nil;
12729a747e4fSDavid du Colombier 	}
12739a747e4fSDavid du Colombier 
127450a9bdd4SDavid du Colombier 	if (auth != -1 && f->auth != auth) {
127550a9bdd4SDavid du Colombier 		*ep = Ebadfid;
127650a9bdd4SDavid du Colombier 		return nil;
127750a9bdd4SDavid du Colombier 	}
127850a9bdd4SDavid du Colombier 
127950a9bdd4SDavid du Colombier 	if (!f->auth) {
12809a747e4fSDavid du Colombier 		if(userchange(f->u, ep) < 0)
12819a747e4fSDavid du Colombier 			return nil;
128250a9bdd4SDavid du Colombier 	}
12839a747e4fSDavid du Colombier 
12849a747e4fSDavid du Colombier 	return f;
12859a747e4fSDavid du Colombier }
12869a747e4fSDavid du Colombier 
128750a9bdd4SDavid du Colombier Fid*
128850a9bdd4SDavid du Colombier oldfid(int fid, char **ep)
128950a9bdd4SDavid du Colombier {
129050a9bdd4SDavid du Colombier 	return oldfidex(fid, 0, ep);
129150a9bdd4SDavid du Colombier }
129250a9bdd4SDavid du Colombier 
129350a9bdd4SDavid du Colombier Fid*
129450a9bdd4SDavid du Colombier oldauthfid(int fid, void **magic, char **ep)
129550a9bdd4SDavid du Colombier {
129650a9bdd4SDavid du Colombier 	Fid *af;
129750a9bdd4SDavid du Colombier 	af = oldfidex(fid, 1, ep);
129850a9bdd4SDavid du Colombier 	if (af == nil)
129950a9bdd4SDavid du Colombier 		return nil;
130050a9bdd4SDavid du Colombier 	*magic = af->authmagic;
130150a9bdd4SDavid du Colombier 	return af;
130250a9bdd4SDavid du Colombier }
130350a9bdd4SDavid du Colombier 
13049a747e4fSDavid du Colombier void
13059a747e4fSDavid du Colombier freefid(Fid *f)
13069a747e4fSDavid du Colombier {
13079a747e4fSDavid du Colombier 	if(f->prev)
13089a747e4fSDavid du Colombier 		f->prev->next = f->next;
13099a747e4fSDavid du Colombier 	else
13109a747e4fSDavid du Colombier 		fidtab[f->fid%nelem(fidtab)] = f->next;
13119a747e4fSDavid du Colombier 	if(f->next)
13129a747e4fSDavid du Colombier 		f->next->prev = f->prev;
13139a747e4fSDavid du Colombier 	if(f->dir)
13149a747e4fSDavid du Colombier 		closedir(f->dir);
13159a747e4fSDavid du Colombier 	if(f->fd)
13169a747e4fSDavid du Colombier 		close(f->fd);
13179a747e4fSDavid du Colombier 	free(f->path);
13189a747e4fSDavid du Colombier 	free(f);
13199a747e4fSDavid du Colombier }
13209a747e4fSDavid du Colombier 
13219a747e4fSDavid du Colombier int
13229a747e4fSDavid du Colombier fidstat(Fid *fid, char **ep)
13239a747e4fSDavid du Colombier {
13249a747e4fSDavid du Colombier 	if(stat(fid->path, &fid->st) < 0){
13259a747e4fSDavid du Colombier 		fprint(2, "fidstat(%s) failed\n", fid->path);
13269a747e4fSDavid du Colombier 		if(ep)
13279a747e4fSDavid du Colombier 			*ep = strerror(errno);
13289a747e4fSDavid du Colombier 		return -1;
13299a747e4fSDavid du Colombier 	}
13309a747e4fSDavid du Colombier 	if(S_ISDIR(fid->st.st_mode))
13319a747e4fSDavid du Colombier 		fid->st.st_size = 0;
13329a747e4fSDavid du Colombier 	return 0;
13339a747e4fSDavid du Colombier }
13349a747e4fSDavid du Colombier 
13359a747e4fSDavid du Colombier int
13369a747e4fSDavid du Colombier userchange(User *u, char **ep)
13379a747e4fSDavid du Colombier {
1338d9306527SDavid du Colombier 	if(defaultuser)
1339d9306527SDavid du Colombier 		return 0;
1340d9306527SDavid du Colombier 
1341d9306527SDavid du Colombier 	if(setreuid(0, 0) < 0){
13429a747e4fSDavid du Colombier 		fprint(2, "setreuid(0, 0) failed\n");
13439a747e4fSDavid du Colombier 		*ep = "cannot setuid back to root";
13449a747e4fSDavid du Colombier 		return -1;
13459a747e4fSDavid du Colombier 	}
13469a747e4fSDavid du Colombier 
13479a747e4fSDavid du Colombier 	/*
13489a747e4fSDavid du Colombier 	 * Initgroups does not appear to be SUSV standard.
13499a747e4fSDavid du Colombier 	 * But it exists on SGI and on Linux, which makes me
13509a747e4fSDavid du Colombier 	 * think it's standard enough.  We have to do something
13519a747e4fSDavid du Colombier 	 * like this, and the closest other function I can find is
13529a747e4fSDavid du Colombier 	 * setgroups (which initgroups eventually calls).
13539a747e4fSDavid du Colombier 	 * Setgroups is the same as far as standardization though,
13549a747e4fSDavid du Colombier 	 * so we're stuck using a non-SUSV call.  Sigh.
13559a747e4fSDavid du Colombier 	 */
13569a747e4fSDavid du Colombier 	if(initgroups(u->name, u->defaultgid) < 0)
13579a747e4fSDavid du Colombier 		fprint(2, "initgroups(%s) failed: %s\n", u->name, strerror(errno));
13589a747e4fSDavid du Colombier 
13599a747e4fSDavid du Colombier 	if(setreuid(-1, u->id) < 0){
13609a747e4fSDavid du Colombier 		fprint(2, "setreuid(-1, %s) failed\n", u->name);
13619a747e4fSDavid du Colombier 		*ep = strerror(errno);
13629a747e4fSDavid du Colombier 		return -1;
13639a747e4fSDavid du Colombier 	}
13649a747e4fSDavid du Colombier 
13659a747e4fSDavid du Colombier 	return 0;
13669a747e4fSDavid du Colombier }
13679a747e4fSDavid du Colombier 
13689a747e4fSDavid du Colombier /*
13699a747e4fSDavid du Colombier  * We do our own checking here, then switch to root temporarily
13709a747e4fSDavid du Colombier  * to set our gid.  In a perfect world, you'd be allowed to set your
13719a747e4fSDavid du Colombier  * egid to any of the supplemental groups of your euid, but this
13729a747e4fSDavid du Colombier  * is not the case on Linux 2.2.14 (and perhaps others).
13739a747e4fSDavid du Colombier  *
13749a747e4fSDavid du Colombier  * This is a race, of course, but it's a race against processes
13759a747e4fSDavid du Colombier  * that can edit the group lists.  If you can do that, you can
13769a747e4fSDavid du Colombier  * change your own group without our help.
13779a747e4fSDavid du Colombier  */
13789a747e4fSDavid du Colombier int
13799a747e4fSDavid du Colombier groupchange(User *u, User *g, char **ep)
13809a747e4fSDavid du Colombier {
1381dc5a79c1SDavid du Colombier 	if(g == nil)
1382dc5a79c1SDavid du Colombier 		return -1;
13839a747e4fSDavid du Colombier 	if(!useringroup(u, g)){
13849a747e4fSDavid du Colombier 		if(chatty9p)
13859a747e4fSDavid du Colombier 			fprint(2, "%s not in group %s\n", u->name, g->name);
13869a747e4fSDavid du Colombier 		*ep = Enotingroup;
13879a747e4fSDavid du Colombier 		return -1;
13889a747e4fSDavid du Colombier 	}
13899a747e4fSDavid du Colombier 
13909a747e4fSDavid du Colombier 	setreuid(0,0);
13919a747e4fSDavid du Colombier 	if(setregid(-1, g->id) < 0){
13929a747e4fSDavid du Colombier 		fprint(2, "setegid(%s/%d) failed in groupchange\n", g->name, g->id);
13939a747e4fSDavid du Colombier 		*ep = strerror(errno);
13949a747e4fSDavid du Colombier 		return -1;
13959a747e4fSDavid du Colombier 	}
13969a747e4fSDavid du Colombier 	if(userchange(u, ep) < 0)
13979a747e4fSDavid du Colombier 		return -1;
13989a747e4fSDavid du Colombier 
13999a747e4fSDavid du Colombier 	return 0;
14009a747e4fSDavid du Colombier }
14019a747e4fSDavid du Colombier 
14029a747e4fSDavid du Colombier 
14039a747e4fSDavid du Colombier /*
14049a747e4fSDavid du Colombier  * An attempt to enforce permissions by looking at the
14059a747e4fSDavid du Colombier  * file system.  Separation of checking permission and
14069a747e4fSDavid du Colombier  * actually performing the action is a terrible idea, of
14079a747e4fSDavid du Colombier  * course, so we use setreuid for most of the permission
14089a747e4fSDavid du Colombier  * enforcement.  This is here only so we can give errors
14099a747e4fSDavid du Colombier  * on open(ORCLOSE) in some cases.
14109a747e4fSDavid du Colombier  */
14119a747e4fSDavid du Colombier int
14129a747e4fSDavid du Colombier userperm(User *u, char *path, int type, int need)
14139a747e4fSDavid du Colombier {
14149a747e4fSDavid du Colombier 	char *p, *q;
14159a747e4fSDavid du Colombier 	int i, have;
14169a747e4fSDavid du Colombier 	struct stat st;
14179a747e4fSDavid du Colombier 	User *g;
14189a747e4fSDavid du Colombier 
14199a747e4fSDavid du Colombier 	switch(type){
14209a747e4fSDavid du Colombier 	default:
14219a747e4fSDavid du Colombier 		fprint(2, "bad type %d in userperm\n", type);
14229a747e4fSDavid du Colombier 		return -1;
14239a747e4fSDavid du Colombier 	case Tdot:
14249a747e4fSDavid du Colombier 		if(stat(path, &st) < 0){
14259a747e4fSDavid du Colombier 			fprint(2, "userperm: stat(%s) failed\n", path);
14269a747e4fSDavid du Colombier 			return -1;
14279a747e4fSDavid du Colombier 		}
14289a747e4fSDavid du Colombier 		break;
14299a747e4fSDavid du Colombier 	case Tdotdot:
14309a747e4fSDavid du Colombier 		p = estrdup(path);
14319a747e4fSDavid du Colombier 		if((q = strrchr(p, '/'))==nil){
14329a747e4fSDavid du Colombier 			fprint(2, "userperm(%s, ..): bad path\n", p);
14339a747e4fSDavid du Colombier 			free(p);
14349a747e4fSDavid du Colombier 			return -1;
14359a747e4fSDavid du Colombier 		}
14369a747e4fSDavid du Colombier 		if(q > p)
14379a747e4fSDavid du Colombier 			*q = '\0';
14389a747e4fSDavid du Colombier 		else
14399a747e4fSDavid du Colombier 			*(q+1) = '\0';
14409a747e4fSDavid du Colombier 		if(stat(p, &st) < 0){
14419a747e4fSDavid du Colombier 			fprint(2, "userperm: stat(%s) (dotdot of %s) failed\n",
14429a747e4fSDavid du Colombier 				p, path);
14439a747e4fSDavid du Colombier 			free(p);
14449a747e4fSDavid du Colombier 			return -1;
14459a747e4fSDavid du Colombier 		}
14469a747e4fSDavid du Colombier 		free(p);
14479a747e4fSDavid du Colombier 		break;
14489a747e4fSDavid du Colombier 	}
14499a747e4fSDavid du Colombier 
14509a747e4fSDavid du Colombier 	if(u == none){
14519a747e4fSDavid du Colombier 		fprint(2, "userperm: none wants %d in 0%luo\n", need, st.st_mode);
14529a747e4fSDavid du Colombier 		have = st.st_mode&7;
14539a747e4fSDavid du Colombier 		if((have&need)==need)
14549a747e4fSDavid du Colombier 			return 0;
14559a747e4fSDavid du Colombier 		return -1;
14569a747e4fSDavid du Colombier 	}
14579a747e4fSDavid du Colombier 	have = st.st_mode&7;
14589a747e4fSDavid du Colombier 	if((uid_t)u->id == st.st_uid)
14599a747e4fSDavid du Colombier 		have |= (st.st_mode>>6)&7;
14609a747e4fSDavid du Colombier 	if((have&need)==need)
14619a747e4fSDavid du Colombier 		return 0;
14629a747e4fSDavid du Colombier 	if(((have|((st.st_mode>>3)&7))&need) != need)	/* group won't help */
14639a747e4fSDavid du Colombier 		return -1;
14649a747e4fSDavid du Colombier 	g = gid2user(st.st_gid);
14659a747e4fSDavid du Colombier 	for(i=0; i<g->nmem; i++){
14669a747e4fSDavid du Colombier 		if(strcmp(g->mem[i], u->name) == 0){
14679a747e4fSDavid du Colombier 			have |= (st.st_mode>>3)&7;
14689a747e4fSDavid du Colombier 			break;
14699a747e4fSDavid du Colombier 		}
14709a747e4fSDavid du Colombier 	}
14719a747e4fSDavid du Colombier 	if((have&need)==need)
14729a747e4fSDavid du Colombier 		return 0;
14739a747e4fSDavid du Colombier 	return -1;
14749a747e4fSDavid du Colombier }
14759a747e4fSDavid du Colombier 
14769a747e4fSDavid du Colombier int
14779a747e4fSDavid du Colombier userwalk(User *u, char **path, char *elem, Qid *qid, char **ep)
14789a747e4fSDavid du Colombier {
14799a747e4fSDavid du Colombier 	char *npath;
14809a747e4fSDavid du Colombier 	struct stat st;
14819a747e4fSDavid du Colombier 
1482*dfda52d8SDavid du Colombier 	npath = estrpath(*path, elem, 1);
14839a747e4fSDavid du Colombier 	if(stat(npath, &st) < 0){
14849a747e4fSDavid du Colombier 		free(npath);
14859a747e4fSDavid du Colombier 		*ep = strerror(errno);
14869a747e4fSDavid du Colombier 		return -1;
14879a747e4fSDavid du Colombier 	}
14889a747e4fSDavid du Colombier 	*qid = stat2qid(&st);
14899a747e4fSDavid du Colombier 	free(*path);
14909a747e4fSDavid du Colombier 	*path = npath;
14919a747e4fSDavid du Colombier 	return 0;
14929a747e4fSDavid du Colombier }
14939a747e4fSDavid du Colombier 
14949a747e4fSDavid du Colombier int
14959a747e4fSDavid du Colombier useropen(Fid *fid, int omode, char **ep)
14969a747e4fSDavid du Colombier {
14979a747e4fSDavid du Colombier 	int a, o;
14989a747e4fSDavid du Colombier 
14999a747e4fSDavid du Colombier 	/*
15009a747e4fSDavid du Colombier 	 * Check this anyway, to try to head off problems later.
15019a747e4fSDavid du Colombier 	 */
15029a747e4fSDavid du Colombier 	if((omode&ORCLOSE) && userperm(fid->u, fid->path, Tdotdot, W_OK) < 0){
15039a747e4fSDavid du Colombier 		*ep = Eperm;
15049a747e4fSDavid du Colombier 		return -1;
15059a747e4fSDavid du Colombier 	}
15069a747e4fSDavid du Colombier 
15079a747e4fSDavid du Colombier 	switch(omode&3){
15089a747e4fSDavid du Colombier 	default:
15099a747e4fSDavid du Colombier 		*ep = "programmer error";
15109a747e4fSDavid du Colombier 		return -1;
15119a747e4fSDavid du Colombier 	case OREAD:
15121118d624SDavid du Colombier 		a = R_OK;
15139a747e4fSDavid du Colombier 		o = O_RDONLY;
15149a747e4fSDavid du Colombier 		break;
15159a747e4fSDavid du Colombier 	case ORDWR:
15169a747e4fSDavid du Colombier 		a = R_OK|W_OK;
15179a747e4fSDavid du Colombier 		o = O_RDWR;
15189a747e4fSDavid du Colombier 		break;
15199a747e4fSDavid du Colombier 	case OWRITE:
1520e5495c06SDavid du Colombier 		a = W_OK;
15219a747e4fSDavid du Colombier 		o = O_WRONLY;
15229a747e4fSDavid du Colombier 		break;
15239a747e4fSDavid du Colombier 	case OEXEC:
15249a747e4fSDavid du Colombier 		a = X_OK;
15259a747e4fSDavid du Colombier 		o = O_RDONLY;
15269a747e4fSDavid du Colombier 		break;
15279a747e4fSDavid du Colombier 	}
15289a747e4fSDavid du Colombier 	if(omode & OTRUNC){
15299a747e4fSDavid du Colombier 		a |= W_OK;
15309a747e4fSDavid du Colombier 		o |= O_TRUNC;
15319a747e4fSDavid du Colombier 	}
15329a747e4fSDavid du Colombier 
15339a747e4fSDavid du Colombier 	if(S_ISDIR(fid->st.st_mode)){
15349a747e4fSDavid du Colombier 		if(a != R_OK){
15359a747e4fSDavid du Colombier 			fprint(2, "attempt by %s to open dir %d\n", fid->u->name, omode);
15369a747e4fSDavid du Colombier 			*ep = Eperm;
15379a747e4fSDavid du Colombier 			return -1;
15389a747e4fSDavid du Colombier 		}
15399a747e4fSDavid du Colombier 		if((fid->dir = opendir(fid->path)) == nil){
15409a747e4fSDavid du Colombier 			*ep = strerror(errno);
15419a747e4fSDavid du Colombier 			return -1;
15429a747e4fSDavid du Colombier 		}
15439a747e4fSDavid du Colombier 	}else{
15440c0e9c72SDavid du Colombier 		/*
15450c0e9c72SDavid du Colombier 		 * This is wrong because access used the real uid
15460c0e9c72SDavid du Colombier 		 * and not the effective uid.  Let the open sort it out.
15470c0e9c72SDavid du Colombier 		 *
15489a747e4fSDavid du Colombier 		if(access(fid->path, a) < 0){
15499a747e4fSDavid du Colombier 			*ep = strerror(errno);
15509a747e4fSDavid du Colombier 			return -1;
15519a747e4fSDavid du Colombier 		}
15520c0e9c72SDavid du Colombier 		 *
15530c0e9c72SDavid du Colombier 		 */
15549a747e4fSDavid du Colombier 		if((fid->fd = open(fid->path, o)) < 0){
15559a747e4fSDavid du Colombier 			*ep = strerror(errno);
15569a747e4fSDavid du Colombier 			return -1;
15579a747e4fSDavid du Colombier 		}
15589a747e4fSDavid du Colombier 	}
15599a747e4fSDavid du Colombier 	fid->omode = omode;
15609a747e4fSDavid du Colombier 	return 0;
15619a747e4fSDavid du Colombier }
15629a747e4fSDavid du Colombier 
15639a747e4fSDavid du Colombier int
15649a747e4fSDavid du Colombier usercreate(Fid *fid, char *elem, int omode, long perm, char **ep)
15659a747e4fSDavid du Colombier {
15669a747e4fSDavid du Colombier 	int o, m;
15679a747e4fSDavid du Colombier 	char *opath, *npath;
15689a747e4fSDavid du Colombier 	struct stat st, parent;
15699a747e4fSDavid du Colombier 
15709a747e4fSDavid du Colombier 	if(stat(fid->path, &parent) < 0){
15719a747e4fSDavid du Colombier 		*ep = strerror(errno);
15729a747e4fSDavid du Colombier 		return -1;
15739a747e4fSDavid du Colombier 	}
15749a747e4fSDavid du Colombier 
15759a747e4fSDavid du Colombier 	/*
15769a747e4fSDavid du Colombier 	 * Change group so that created file has expected group
15779a747e4fSDavid du Colombier 	 * by Plan 9 semantics.  If that fails, might as well go
15789a747e4fSDavid du Colombier 	 * with the user's default group.
15799a747e4fSDavid du Colombier 	 */
15809a747e4fSDavid du Colombier 	if(groupchange(fid->u, gid2user(parent.st_gid), ep) < 0
15819a747e4fSDavid du Colombier 	&& groupchange(fid->u, gid2user(fid->u->defaultgid), ep) < 0)
15829a747e4fSDavid du Colombier 		return -1;
15839a747e4fSDavid du Colombier 
15849a747e4fSDavid du Colombier 	m = (perm & DMDIR) ? 0777 : 0666;
15859a747e4fSDavid du Colombier 	perm = perm & (~m | (fid->st.st_mode & m));
15869a747e4fSDavid du Colombier 
1587*dfda52d8SDavid du Colombier 	npath = estrpath(fid->path, elem, 1);
15889a747e4fSDavid du Colombier 	if(perm & DMDIR){
15899a747e4fSDavid du Colombier 		if((omode&~ORCLOSE) != OREAD){
15909a747e4fSDavid du Colombier 			*ep = Eperm;
15919a747e4fSDavid du Colombier 			free(npath);
15929a747e4fSDavid du Colombier 			return -1;
15939a747e4fSDavid du Colombier 		}
15949a747e4fSDavid du Colombier 		if(stat(npath, &st) >= 0 || errno != ENOENT){
15959a747e4fSDavid du Colombier 			*ep = Eexist;
15969a747e4fSDavid du Colombier 			free(npath);
15979a747e4fSDavid du Colombier 			return -1;
15989a747e4fSDavid du Colombier 		}
15999a747e4fSDavid du Colombier 		/* race */
16009a747e4fSDavid du Colombier 		if(mkdir(npath, perm&0777) < 0){
16019a747e4fSDavid du Colombier 			*ep = strerror(errno);
16029a747e4fSDavid du Colombier 			free(npath);
16039a747e4fSDavid du Colombier 			return -1;
16049a747e4fSDavid du Colombier 		}
16059a747e4fSDavid du Colombier 		if((fid->dir = opendir(npath)) == nil){
16069a747e4fSDavid du Colombier 			*ep = strerror(errno);
16079a747e4fSDavid du Colombier 			remove(npath);		/* race */
16089a747e4fSDavid du Colombier 			free(npath);
16099a747e4fSDavid du Colombier 			return -1;
16109a747e4fSDavid du Colombier 		}
16119a747e4fSDavid du Colombier 	}else{
16129a747e4fSDavid du Colombier 		o = O_CREAT|O_EXCL;
16139a747e4fSDavid du Colombier 		switch(omode&3){
16149a747e4fSDavid du Colombier 		default:
16159a747e4fSDavid du Colombier 			*ep = "programmer error";
16169a747e4fSDavid du Colombier 			return -1;
16179a747e4fSDavid du Colombier 		case OREAD:
16189a747e4fSDavid du Colombier 		case OEXEC:
16199a747e4fSDavid du Colombier 			o |= O_RDONLY;
16209a747e4fSDavid du Colombier 			break;
16219a747e4fSDavid du Colombier 		case ORDWR:
16229a747e4fSDavid du Colombier 			o |= O_RDWR;
16239a747e4fSDavid du Colombier 			break;
16249a747e4fSDavid du Colombier 		case OWRITE:
16259a747e4fSDavid du Colombier 			o |= O_WRONLY;
16269a747e4fSDavid du Colombier 			break;
16279a747e4fSDavid du Colombier 		}
16289a747e4fSDavid du Colombier 		if(omode & OTRUNC)
16299a747e4fSDavid du Colombier 			o |= O_TRUNC;
16309a747e4fSDavid du Colombier 		if((fid->fd = open(npath, o, perm&0777)) < 0){
16319a747e4fSDavid du Colombier 			if(chatty9p)
16329a747e4fSDavid du Colombier 				fprint(2, "create(%s, 0x%x, 0%o) failed\n", npath, o, perm&0777);
16339a747e4fSDavid du Colombier 			*ep = strerror(errno);
16349a747e4fSDavid du Colombier 			free(npath);
16359a747e4fSDavid du Colombier 			return -1;
16369a747e4fSDavid du Colombier 		}
16379a747e4fSDavid du Colombier 	}
16389a747e4fSDavid du Colombier 
16399a747e4fSDavid du Colombier 	opath = fid->path;
16409a747e4fSDavid du Colombier 	fid->path = npath;
16419a747e4fSDavid du Colombier 	if(fidstat(fid, ep) < 0){
16429a747e4fSDavid du Colombier 		fprint(2, "stat after create on %s failed\n", npath);
16439a747e4fSDavid du Colombier 		remove(npath);	/* race */
16449a747e4fSDavid du Colombier 		free(npath);
16459a747e4fSDavid du Colombier 		fid->path = opath;
16469a747e4fSDavid du Colombier 		if(fid->fd >= 0){
16479a747e4fSDavid du Colombier 			close(fid->fd);
16489a747e4fSDavid du Colombier 			fid->fd = -1;
16499a747e4fSDavid du Colombier 		}else{
16509a747e4fSDavid du Colombier 			closedir(fid->dir);
16519a747e4fSDavid du Colombier 			fid->dir = nil;
16529a747e4fSDavid du Colombier 		}
16539a747e4fSDavid du Colombier 		return -1;
16549a747e4fSDavid du Colombier 	}
16559a747e4fSDavid du Colombier 	fid->omode = omode;
16569a747e4fSDavid du Colombier 	free(opath);
16579a747e4fSDavid du Colombier 	return 0;
16589a747e4fSDavid du Colombier }
16599a747e4fSDavid du Colombier 
16609a747e4fSDavid du Colombier int
16619a747e4fSDavid du Colombier userremove(Fid *fid, char **ep)
16629a747e4fSDavid du Colombier {
16639a747e4fSDavid du Colombier 	if(remove(fid->path) < 0){
16649a747e4fSDavid du Colombier 		*ep = strerror(errno);
16659a747e4fSDavid du Colombier 		return -1;
16669a747e4fSDavid du Colombier 	}
16679a747e4fSDavid du Colombier 	return 0;
16689a747e4fSDavid du Colombier }
16699a747e4fSDavid du Colombier 
16709a747e4fSDavid du Colombier void
16719a747e4fSDavid du Colombier usage(void)
16729a747e4fSDavid du Colombier {
16739a747e4fSDavid du Colombier 	fprint(2, "usage: u9fs [-Dnz] [-a authmethod] [-m msize] [-u user] [root]\n");
16749a747e4fSDavid du Colombier 	exit(1);
16759a747e4fSDavid du Colombier }
16769a747e4fSDavid du Colombier 
16779a747e4fSDavid du Colombier int
16789a747e4fSDavid du Colombier main(int argc, char **argv)
16799a747e4fSDavid du Colombier {
16809a747e4fSDavid du Colombier 	char *authtype;
16819a747e4fSDavid du Colombier 	int i;
16829a747e4fSDavid du Colombier 	int fd;
16839a747e4fSDavid du Colombier 	int logflag;
16849a747e4fSDavid du Colombier 
16859a747e4fSDavid du Colombier 	auth = authmethods[0];
16869a747e4fSDavid du Colombier 	logflag = O_WRONLY|O_APPEND|O_CREAT;
16879a747e4fSDavid du Colombier 	ARGBEGIN{
16889a747e4fSDavid du Colombier 	case 'D':
16899a747e4fSDavid du Colombier 		chatty9p = 1;
16909a747e4fSDavid du Colombier 		break;
16919a747e4fSDavid du Colombier 	case 'a':
16929a747e4fSDavid du Colombier 		authtype = EARGF(usage());
16939a747e4fSDavid du Colombier 		auth = nil;
16949a747e4fSDavid du Colombier 		for(i=0; i<nelem(authmethods); i++)
16959a747e4fSDavid du Colombier 			if(strcmp(authmethods[i]->name, authtype)==0)
16969a747e4fSDavid du Colombier 				auth = authmethods[i];
16979a747e4fSDavid du Colombier 		if(auth == nil)
16989a747e4fSDavid du Colombier 			sysfatal("unknown auth type '%s'", authtype);
16999a747e4fSDavid du Colombier 		break;
17009a747e4fSDavid du Colombier 	case 'A':
17019a747e4fSDavid du Colombier 		autharg = EARGF(usage());
17029a747e4fSDavid du Colombier 		break;
17039a747e4fSDavid du Colombier 	case 'l':
17049a747e4fSDavid du Colombier 		logfile = EARGF(usage());
17059a747e4fSDavid du Colombier 		break;
17069a747e4fSDavid du Colombier 	case 'm':
17079a747e4fSDavid du Colombier 		msize = strtol(EARGF(usage()), 0, 0);
17089a747e4fSDavid du Colombier 		break;
17099a747e4fSDavid du Colombier 	case 'n':
17109a747e4fSDavid du Colombier 		network = 0;
17119a747e4fSDavid du Colombier 		break;
17129a747e4fSDavid du Colombier 	case 'u':
17139a747e4fSDavid du Colombier 		defaultuser = EARGF(usage());
17149a747e4fSDavid du Colombier 		break;
17159a747e4fSDavid du Colombier 	case 'z':
17169a747e4fSDavid du Colombier 		logflag |= O_TRUNC;
17179a747e4fSDavid du Colombier 	}ARGEND
17189a747e4fSDavid du Colombier 
17199a747e4fSDavid du Colombier 	if(argc > 1)
17209a747e4fSDavid du Colombier 		usage();
17219a747e4fSDavid du Colombier 
17229a747e4fSDavid du Colombier 	fd = open(logfile, logflag, 0666);
17239a747e4fSDavid du Colombier 	if(fd < 0)
17249a747e4fSDavid du Colombier 		sysfatal("cannot open log '%s'", logfile);
17259a747e4fSDavid du Colombier 
17269a747e4fSDavid du Colombier 	if(dup2(fd, 2) < 0)
17279a747e4fSDavid du Colombier 		sysfatal("cannot dup fd onto stderr");
17289a747e4fSDavid du Colombier 	fprint(2, "u9fs\nkill %d\n", (int)getpid());
17299a747e4fSDavid du Colombier 
17309a747e4fSDavid du Colombier 	fmtinstall('F', fcallconv);
17319a747e4fSDavid du Colombier 	fmtinstall('D', dirconv);
17329a747e4fSDavid du Colombier 	fmtinstall('M', dirmodeconv);
17339a747e4fSDavid du Colombier 
17349a747e4fSDavid du Colombier 	rxbuf = emalloc(msize);
17359a747e4fSDavid du Colombier 	txbuf = emalloc(msize);
17369a747e4fSDavid du Colombier 	databuf = emalloc(msize);
17379a747e4fSDavid du Colombier 
17389a747e4fSDavid du Colombier 	if(auth->init)
17399a747e4fSDavid du Colombier 		auth->init();
17409a747e4fSDavid du Colombier 
17419a747e4fSDavid du Colombier 	if(network)
17429a747e4fSDavid du Colombier 		getremotehostname(remotehostname, sizeof remotehostname);
17439a747e4fSDavid du Colombier 
17449a747e4fSDavid du Colombier 	if(gethostname(hostname, sizeof hostname) < 0)
17459a747e4fSDavid du Colombier 		strcpy(hostname, "gnot");
17469a747e4fSDavid du Colombier 
17479a747e4fSDavid du Colombier 	umask(0);
17489a747e4fSDavid du Colombier 
17499a747e4fSDavid du Colombier 	if(argc == 1)
17509a747e4fSDavid du Colombier 		if(chroot(argv[0]) < 0)
17519a747e4fSDavid du Colombier 			sysfatal("chroot '%s' failed", argv[0]);
17529a747e4fSDavid du Colombier 
17539a747e4fSDavid du Colombier 	none = uname2user("none");
17549df35464SDavid du Colombier 
17559a747e4fSDavid du Colombier 	serve(0, 1);
17569a747e4fSDavid du Colombier 	return 0;
17579a747e4fSDavid du Colombier }
1758