xref: /inferno-os/emu/port/devfs-posix.c (revision 0195c4e25fc394097552c7f5ebf3085ec2d201f5)
137da2899SCharles.Forsyth /*
237da2899SCharles.Forsyth  * Unix file system interface
337da2899SCharles.Forsyth  */
437da2899SCharles.Forsyth #define _LARGEFILE64_SOURCE	1
537da2899SCharles.Forsyth #define _FILE_OFFSET_BITS 64
637da2899SCharles.Forsyth #include	"dat.h"
737da2899SCharles.Forsyth #include	"fns.h"
837da2899SCharles.Forsyth #include	"error.h"
937da2899SCharles.Forsyth 
1037da2899SCharles.Forsyth #include	<sys/types.h>
1137da2899SCharles.Forsyth #include	<sys/stat.h>
12*3f109ae8SYaroslav Kolomiiets #include	<fcntl.h>
1335a6f3e1Sforsyth #include	<sys/socket.h>
1435a6f3e1Sforsyth #include	<sys/un.h>
1537da2899SCharles.Forsyth #include	<utime.h>
1637da2899SCharles.Forsyth #include	<dirent.h>
1737da2899SCharles.Forsyth #include	<stdio.h>
1837da2899SCharles.Forsyth #define	__EXTENSIONS__
1937da2899SCharles.Forsyth #undef	getwd
2037da2899SCharles.Forsyth #include	<unistd.h>
2137da2899SCharles.Forsyth #include	<pwd.h>
2237da2899SCharles.Forsyth #include	<grp.h>
2337da2899SCharles.Forsyth 
2437da2899SCharles.Forsyth typedef struct Fsinfo Fsinfo;
2537da2899SCharles.Forsyth struct Fsinfo
2637da2899SCharles.Forsyth {
2737da2899SCharles.Forsyth 	int	uid;
2837da2899SCharles.Forsyth 	int	gid;
2937da2899SCharles.Forsyth 	int	mode;	/* Unix mode */
3037da2899SCharles.Forsyth 	DIR*	dir;		/* open directory */
3137da2899SCharles.Forsyth 	struct dirent*	de;	/* directory reading */
3237da2899SCharles.Forsyth 	int	fd;		/* open files */
3337da2899SCharles.Forsyth 	ulong	offset;	/* offset when reading directory */
3423babd45SCharles.Forsyth 	int	eod;	/* end of directory */
3535a6f3e1Sforsyth 	int	issocket;
3637da2899SCharles.Forsyth 	QLock	oq;	/* mutex for offset */
3737da2899SCharles.Forsyth 	char*	spec;
3837da2899SCharles.Forsyth 	Cname*	name;	/* Unix's name for file */
3937da2899SCharles.Forsyth 	Qid	rootqid;		/* Plan 9's qid for Inferno's root */
4037da2899SCharles.Forsyth };
4137da2899SCharles.Forsyth 
4237da2899SCharles.Forsyth #define	FS(c)	((Fsinfo*)(c)->aux)
4337da2899SCharles.Forsyth 
4437da2899SCharles.Forsyth enum
4537da2899SCharles.Forsyth {
4637da2899SCharles.Forsyth 	IDSHIFT	= 8,
4737da2899SCharles.Forsyth 	NID	= 1 << IDSHIFT,
4837da2899SCharles.Forsyth 	IDMASK	= NID - 1,
4937da2899SCharles.Forsyth 	MAXPATH	= 1024	/* TO DO: eliminate this */
5037da2899SCharles.Forsyth };
5137da2899SCharles.Forsyth 
52184c64d4SCharles.Forsyth typedef struct User User;
53184c64d4SCharles.Forsyth struct User
5437da2899SCharles.Forsyth {
55184c64d4SCharles.Forsyth 	int	id;		/* might be user or group ID */
56184c64d4SCharles.Forsyth 	int	gid;		/* if it's a user not a group, the group ID (only for setid) */
5737da2899SCharles.Forsyth 	char*	name;
58184c64d4SCharles.Forsyth 	int	nmem;
59184c64d4SCharles.Forsyth 	int*	mem;	/* member array, if nmem != 0 */
60184c64d4SCharles.Forsyth 	User*	next;
6137da2899SCharles.Forsyth };
6237da2899SCharles.Forsyth 
6337da2899SCharles.Forsyth char	rootdir[MAXROOT] = ROOT;
6437da2899SCharles.Forsyth 
65184c64d4SCharles.Forsyth static	User*	uidmap[NID];
66184c64d4SCharles.Forsyth static	User*	gidmap[NID];
67184c64d4SCharles.Forsyth static	QLock	idl;
68184c64d4SCharles.Forsyth static	User*	name2user(User**, char*, User* (*get)(char*));
69184c64d4SCharles.Forsyth static	User*	id2user(User**, int, User* (*get)(int));
70184c64d4SCharles.Forsyth static	User*	newuid(int);
71184c64d4SCharles.Forsyth static	User*	newgid(int);
72184c64d4SCharles.Forsyth static	User*	newuname(char*);
73184c64d4SCharles.Forsyth static	User*	newgname(char*);
7437da2899SCharles.Forsyth 
7537da2899SCharles.Forsyth static	Qid	fsqid(struct stat *);
7637da2899SCharles.Forsyth static	void	fspath(Cname*, char*, char*);
7748cb024aSforsyth static	int	fsdirconv(Chan*, char*, char*, struct stat*, uchar*, int, int);
7837da2899SCharles.Forsyth static	Cname*	fswalkpath(Cname*, char*, int);
7937da2899SCharles.Forsyth static	char*	fslastelem(Cname*);
8037da2899SCharles.Forsyth static	int ingroup(int id, int gid);
8137da2899SCharles.Forsyth static	void	fsperm(Chan*, int);
8237da2899SCharles.Forsyth static	long	fsdirread(Chan*, uchar*, int, vlong);
8337da2899SCharles.Forsyth static	int	fsomode(int);
8437da2899SCharles.Forsyth static	void	fsremove(Chan*);
8548cb024aSforsyth static	vlong osdisksize(int);	/* defined by including file */
8637da2899SCharles.Forsyth 
8737da2899SCharles.Forsyth /*
8835a6f3e1Sforsyth  * make invalid symbolic links visible; less confusing, and at least you can then delete them.
8937da2899SCharles.Forsyth  */
9037da2899SCharles.Forsyth static int
xstat(char * f,struct stat * sb)9137da2899SCharles.Forsyth xstat(char *f, struct stat *sb)
9237da2899SCharles.Forsyth {
9337da2899SCharles.Forsyth 	if(stat(f, sb) >= 0)
9437da2899SCharles.Forsyth 		return 0;
95184c64d4SCharles.Forsyth 	/* could possibly generate ->name as rob once suggested */
9637da2899SCharles.Forsyth 	return lstat(f, sb);
9737da2899SCharles.Forsyth }
9837da2899SCharles.Forsyth 
9937da2899SCharles.Forsyth static void
fsfree(Chan * c)10037da2899SCharles.Forsyth fsfree(Chan *c)
10137da2899SCharles.Forsyth {
10237da2899SCharles.Forsyth 	cnameclose(FS(c)->name);
10337da2899SCharles.Forsyth 	free(FS(c));
10437da2899SCharles.Forsyth }
10537da2899SCharles.Forsyth 
10637da2899SCharles.Forsyth Chan*
fsattach(char * spec)10737da2899SCharles.Forsyth fsattach(char *spec)
10837da2899SCharles.Forsyth {
10937da2899SCharles.Forsyth 	Chan *c;
11035a6f3e1Sforsyth 	struct stat st;
11137da2899SCharles.Forsyth 	static int devno;
11237da2899SCharles.Forsyth 	static Lock l;
11337da2899SCharles.Forsyth 
11437da2899SCharles.Forsyth 	if(!emptystr(spec) && strcmp(spec, "*") != 0)
11537da2899SCharles.Forsyth 		error(Ebadspec);
11635a6f3e1Sforsyth 	if(stat(rootdir, &st) < 0)
11737da2899SCharles.Forsyth 		oserror();
11835a6f3e1Sforsyth 	if(!S_ISDIR(st.st_mode))
11935a6f3e1Sforsyth 		error(Enotdir);
12037da2899SCharles.Forsyth 
12137da2899SCharles.Forsyth 	c = devattach('U', spec);
12235a6f3e1Sforsyth 	c->qid = fsqid(&st);
12337da2899SCharles.Forsyth 	c->aux = smalloc(sizeof(Fsinfo));
12437da2899SCharles.Forsyth 	FS(c)->dir = nil;
12537da2899SCharles.Forsyth 	FS(c)->de = nil;
12637da2899SCharles.Forsyth 	FS(c)->fd = -1;
12735a6f3e1Sforsyth 	FS(c)->issocket = 0;
12835a6f3e1Sforsyth 	FS(c)->gid = st.st_gid;
12935a6f3e1Sforsyth 	FS(c)->uid = st.st_uid;
13035a6f3e1Sforsyth 	FS(c)->mode = st.st_mode;
13137da2899SCharles.Forsyth 	lock(&l);
13237da2899SCharles.Forsyth 	c->dev = devno++;
13337da2899SCharles.Forsyth 	unlock(&l);
13437da2899SCharles.Forsyth 	if(!emptystr(spec)){
13537da2899SCharles.Forsyth 		FS(c)->spec = "/";
13637da2899SCharles.Forsyth 		FS(c)->name = newcname(FS(c)->spec);
13737da2899SCharles.Forsyth 	}else
13837da2899SCharles.Forsyth 		FS(c)->name = newcname(rootdir);
13937da2899SCharles.Forsyth 	FS(c)->rootqid = c->qid;
14037da2899SCharles.Forsyth 
14137da2899SCharles.Forsyth 	return c;
14237da2899SCharles.Forsyth }
14337da2899SCharles.Forsyth 
14437da2899SCharles.Forsyth Walkqid*
fswalk(Chan * c,Chan * nc,char ** name,int nname)14537da2899SCharles.Forsyth fswalk(Chan *c, Chan *nc, char **name, int nname)
14637da2899SCharles.Forsyth {
14737da2899SCharles.Forsyth 	int j;
14837da2899SCharles.Forsyth 	volatile int alloc;
14937da2899SCharles.Forsyth 	Walkqid *wq;
15035a6f3e1Sforsyth 	struct stat st;
15137da2899SCharles.Forsyth 	char *n;
15237da2899SCharles.Forsyth 	Cname *next;
15337da2899SCharles.Forsyth 	Cname *volatile current;
15437da2899SCharles.Forsyth 	Qid rootqid;
15537da2899SCharles.Forsyth 
15637da2899SCharles.Forsyth 	if(nname > 0)
15737da2899SCharles.Forsyth 		isdir(c);
15837da2899SCharles.Forsyth 
15937da2899SCharles.Forsyth 	alloc = 0;
16037da2899SCharles.Forsyth 	current = nil;
16137da2899SCharles.Forsyth 	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
16237da2899SCharles.Forsyth 	if(waserror()){
16337da2899SCharles.Forsyth 		if(alloc && wq->clone != nil)
16437da2899SCharles.Forsyth 			cclose(wq->clone);
16537da2899SCharles.Forsyth 		cnameclose(current);
16637da2899SCharles.Forsyth 		free(wq);
16737da2899SCharles.Forsyth 		return nil;
16837da2899SCharles.Forsyth 	}
16937da2899SCharles.Forsyth 	if(nc == nil){
17037da2899SCharles.Forsyth 		nc = devclone(c);
17137da2899SCharles.Forsyth 		nc->type = 0;
17237da2899SCharles.Forsyth 		alloc = 1;
17337da2899SCharles.Forsyth 	}
17437da2899SCharles.Forsyth 	wq->clone = nc;
17537da2899SCharles.Forsyth 	rootqid = FS(c)->rootqid;
17637da2899SCharles.Forsyth 	current = FS(c)->name;
17737da2899SCharles.Forsyth 	if(current != nil)
17837da2899SCharles.Forsyth 		incref(&current->r);
17937da2899SCharles.Forsyth 	for(j = 0; j < nname; j++){
18037da2899SCharles.Forsyth 		if(!(nc->qid.type&QTDIR)){
18137da2899SCharles.Forsyth 			if(j==0)
18237da2899SCharles.Forsyth 				error(Enotdir);
18337da2899SCharles.Forsyth 			break;
18437da2899SCharles.Forsyth 		}
18537da2899SCharles.Forsyth 		n = name[j];
18637da2899SCharles.Forsyth 		if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){
18737da2899SCharles.Forsyth 			next = current;
18837da2899SCharles.Forsyth 			incref(&next->r);
18937da2899SCharles.Forsyth 			next = addelem(current, n);
19037da2899SCharles.Forsyth 			//print("** ufs walk '%s' -> %s [%s]\n", current->s, n, next->s);
19135a6f3e1Sforsyth 			if(xstat(next->s, &st) < 0){
19237da2899SCharles.Forsyth 				cnameclose(next);
19337da2899SCharles.Forsyth 				if(j == 0)
19437da2899SCharles.Forsyth 					error(Enonexist);
19537da2899SCharles.Forsyth 				strcpy(up->env->errstr, Enonexist);
19637da2899SCharles.Forsyth 				break;
19737da2899SCharles.Forsyth 			}
19835a6f3e1Sforsyth 			nc->qid = fsqid(&st);
19937da2899SCharles.Forsyth 			cnameclose(current);
20037da2899SCharles.Forsyth 			current = next;
20137da2899SCharles.Forsyth 		}
20237da2899SCharles.Forsyth 		wq->qid[wq->nqid++] = nc->qid;
20337da2899SCharles.Forsyth 	}
20437da2899SCharles.Forsyth 	poperror();
20537da2899SCharles.Forsyth 	if(wq->nqid < nname){
20637da2899SCharles.Forsyth 		cnameclose(current);
20737da2899SCharles.Forsyth 		if(alloc)
20837da2899SCharles.Forsyth 			cclose(wq->clone);
20937da2899SCharles.Forsyth 		wq->clone = nil;
21037da2899SCharles.Forsyth 	}else if(wq->clone){
21137da2899SCharles.Forsyth 		nc->aux = smalloc(sizeof(Fsinfo));
21237da2899SCharles.Forsyth 		nc->type = c->type;
21335a6f3e1Sforsyth 		if(nname > 0) {
21435a6f3e1Sforsyth 			FS(nc)->gid = st.st_gid;
21535a6f3e1Sforsyth 			FS(nc)->uid = st.st_uid;
21635a6f3e1Sforsyth 			FS(nc)->mode = st.st_mode;
21735a6f3e1Sforsyth 			FS(nc)->issocket = S_ISSOCK(st.st_mode);
21837da2899SCharles.Forsyth 		} else {
21937da2899SCharles.Forsyth 			FS(nc)->gid = FS(c)->gid;
22037da2899SCharles.Forsyth 			FS(nc)->uid = FS(c)->uid;
22137da2899SCharles.Forsyth 			FS(nc)->mode = FS(c)->mode;
22235a6f3e1Sforsyth 			FS(nc)->issocket = FS(c)->issocket;
22337da2899SCharles.Forsyth 		}
22437da2899SCharles.Forsyth 		FS(nc)->name = current;
22537da2899SCharles.Forsyth 		FS(nc)->spec = FS(c)->spec;
22637da2899SCharles.Forsyth 		FS(nc)->rootqid = rootqid;
22737da2899SCharles.Forsyth 		FS(nc)->fd = -1;
22837da2899SCharles.Forsyth 		FS(nc)->dir = nil;
22937da2899SCharles.Forsyth 		FS(nc)->de = nil;
23037da2899SCharles.Forsyth 	}
23137da2899SCharles.Forsyth 	return wq;
23237da2899SCharles.Forsyth }
23337da2899SCharles.Forsyth 
23437da2899SCharles.Forsyth static int
fsstat(Chan * c,uchar * dp,int n)23537da2899SCharles.Forsyth fsstat(Chan *c, uchar *dp, int n)
23637da2899SCharles.Forsyth {
23735a6f3e1Sforsyth 	struct stat st;
23837da2899SCharles.Forsyth 	char *p;
23937da2899SCharles.Forsyth 
24048cb024aSforsyth 	if(FS(c)->fd >= 0){
24148cb024aSforsyth 		if(fstat(FS(c)->fd, &st) < 0)
24248cb024aSforsyth 			oserror();
24348cb024aSforsyth 	}else{
24435a6f3e1Sforsyth 		if(xstat(FS(c)->name->s, &st) < 0)
24537da2899SCharles.Forsyth 			oserror();
24648cb024aSforsyth 	}
24737da2899SCharles.Forsyth 	p = fslastelem(FS(c)->name);
24837da2899SCharles.Forsyth 	if(*p == 0)
24937da2899SCharles.Forsyth 		p = "/";
250184c64d4SCharles.Forsyth 	qlock(&idl);
25148cb024aSforsyth 	n = fsdirconv(c, FS(c)->name->s, p, &st, dp, n, 0);
252184c64d4SCharles.Forsyth 	qunlock(&idl);
25337da2899SCharles.Forsyth 	return n;
25437da2899SCharles.Forsyth }
25537da2899SCharles.Forsyth 
25635a6f3e1Sforsyth static int
opensocket(char * path)25735a6f3e1Sforsyth opensocket(char *path)
25835a6f3e1Sforsyth {
25935a6f3e1Sforsyth 	int fd;
26035a6f3e1Sforsyth 	struct sockaddr_un su;
26135a6f3e1Sforsyth 
26235a6f3e1Sforsyth 	memset(&su, 0, sizeof su);
26335a6f3e1Sforsyth 	su.sun_family = AF_UNIX;
26435a6f3e1Sforsyth 	if(strlen(path)+1 > sizeof su.sun_path)
26535a6f3e1Sforsyth 		error("unix socket name too long");
26635a6f3e1Sforsyth 	strcpy(su.sun_path, path);
26735a6f3e1Sforsyth 	if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
26835a6f3e1Sforsyth 		return -1;
26935a6f3e1Sforsyth 	if(connect(fd, (struct sockaddr*)&su, sizeof su) >= 0)
27035a6f3e1Sforsyth 		return fd;
27135a6f3e1Sforsyth 	close(fd);
27248cb024aSforsyth 	if((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
27348cb024aSforsyth 		return -1;
27448cb024aSforsyth 	if(connect(fd, (struct sockaddr*)&su, sizeof su) >= 0)
27548cb024aSforsyth 		return fd;
27648cb024aSforsyth 	close(fd);
27735a6f3e1Sforsyth 	return -1;
27835a6f3e1Sforsyth }
27935a6f3e1Sforsyth 
28037da2899SCharles.Forsyth static Chan*
fsopen(Chan * c,int mode)28137da2899SCharles.Forsyth fsopen(Chan *c, int mode)
28237da2899SCharles.Forsyth {
28337da2899SCharles.Forsyth 	int m, isdir;
28437da2899SCharles.Forsyth 
28537da2899SCharles.Forsyth 	m = mode & (OTRUNC|3);
28637da2899SCharles.Forsyth 	switch(m) {
28737da2899SCharles.Forsyth 	case 0:
28837da2899SCharles.Forsyth 		fsperm(c, 4);
28937da2899SCharles.Forsyth 		break;
29037da2899SCharles.Forsyth 	case 1:
29137da2899SCharles.Forsyth 	case 1|16:
29237da2899SCharles.Forsyth 		fsperm(c, 2);
29337da2899SCharles.Forsyth 		break;
29437da2899SCharles.Forsyth 	case 2:
29537da2899SCharles.Forsyth 	case 0|16:
29637da2899SCharles.Forsyth 	case 2|16:
29737da2899SCharles.Forsyth 		fsperm(c, 4);
29837da2899SCharles.Forsyth 		fsperm(c, 2);
29937da2899SCharles.Forsyth 		break;
30037da2899SCharles.Forsyth 	case 3:
30137da2899SCharles.Forsyth 		fsperm(c, 1);
30237da2899SCharles.Forsyth 		break;
30337da2899SCharles.Forsyth 	default:
30437da2899SCharles.Forsyth 		error(Ebadarg);
30537da2899SCharles.Forsyth 	}
30637da2899SCharles.Forsyth 
30737da2899SCharles.Forsyth 	isdir = c->qid.type & QTDIR;
30837da2899SCharles.Forsyth 
30937da2899SCharles.Forsyth 	if(isdir && mode != OREAD)
31037da2899SCharles.Forsyth 		error(Eperm);
31137da2899SCharles.Forsyth 
31237da2899SCharles.Forsyth 	m = fsomode(m & 3);
31337da2899SCharles.Forsyth 	c->mode = openmode(mode);
31437da2899SCharles.Forsyth 
31537da2899SCharles.Forsyth 	if(isdir) {
31637da2899SCharles.Forsyth 		FS(c)->dir = opendir(FS(c)->name->s);
31735a6f3e1Sforsyth 		if(FS(c)->dir == nil)
31837da2899SCharles.Forsyth 			oserror();
31923babd45SCharles.Forsyth 		FS(c)->eod = 0;
32037da2899SCharles.Forsyth 	}
32137da2899SCharles.Forsyth 	else {
32235a6f3e1Sforsyth 		if(!FS(c)->issocket){
32337da2899SCharles.Forsyth 			if(mode & OTRUNC)
32437da2899SCharles.Forsyth 				m |= O_TRUNC;
32537da2899SCharles.Forsyth 			FS(c)->fd = open(FS(c)->name->s, m, 0666);
32635a6f3e1Sforsyth 		}else
32735a6f3e1Sforsyth 			FS(c)->fd = opensocket(FS(c)->name->s);
32837da2899SCharles.Forsyth 		if(FS(c)->fd < 0)
32937da2899SCharles.Forsyth 			oserror();
33037da2899SCharles.Forsyth 	}
33137da2899SCharles.Forsyth 
33237da2899SCharles.Forsyth 	c->offset = 0;
33337da2899SCharles.Forsyth 	FS(c)->offset = 0;
33437da2899SCharles.Forsyth 	c->flag |= COPEN;
33537da2899SCharles.Forsyth 	return c;
33637da2899SCharles.Forsyth }
33737da2899SCharles.Forsyth 
33837da2899SCharles.Forsyth static void
fscreate(Chan * c,char * name,int mode,ulong perm)33937da2899SCharles.Forsyth fscreate(Chan *c, char *name, int mode, ulong perm)
34037da2899SCharles.Forsyth {
34137da2899SCharles.Forsyth 	int fd, m, o;
34235a6f3e1Sforsyth 	struct stat st;
34337da2899SCharles.Forsyth 	Cname *n;
34437da2899SCharles.Forsyth 
34537da2899SCharles.Forsyth 	fsperm(c, 2);
34637da2899SCharles.Forsyth 
34737da2899SCharles.Forsyth 	m = fsomode(mode&3);
34837da2899SCharles.Forsyth 	openmode(mode);	/* get the errors out of the way */
34937da2899SCharles.Forsyth 
35037da2899SCharles.Forsyth 	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
35137da2899SCharles.Forsyth 		error(Efilename);
35237da2899SCharles.Forsyth 	n = fswalkpath(FS(c)->name, name, 1);
35337da2899SCharles.Forsyth 	if(waserror()){
35437da2899SCharles.Forsyth 		cnameclose(n);
35537da2899SCharles.Forsyth 		nexterror();
35637da2899SCharles.Forsyth 	}
35737da2899SCharles.Forsyth 	if(perm & DMDIR) {
35837da2899SCharles.Forsyth 		if(m)
35937da2899SCharles.Forsyth 			error(Eperm);
36037da2899SCharles.Forsyth 
36137da2899SCharles.Forsyth 		perm &= ~0777 | (FS(c)->mode & 0777);
36237da2899SCharles.Forsyth 		if(mkdir(n->s, perm) < 0)
36337da2899SCharles.Forsyth 			oserror();
36437da2899SCharles.Forsyth 
36537da2899SCharles.Forsyth 		fd = open(n->s, 0);
36637da2899SCharles.Forsyth 		if(fd < 0)
36737da2899SCharles.Forsyth 			oserror();
36837da2899SCharles.Forsyth 		fchmod(fd, perm);
36937da2899SCharles.Forsyth 		fchown(fd, up->env->uid, FS(c)->gid);
37035a6f3e1Sforsyth 		if(fstat(fd, &st) <0){
37137da2899SCharles.Forsyth 			close(fd);
37237da2899SCharles.Forsyth 			oserror();
37337da2899SCharles.Forsyth 		}
37437da2899SCharles.Forsyth 		close(fd);
37537da2899SCharles.Forsyth 		FS(c)->dir = opendir(n->s);
37637da2899SCharles.Forsyth 		if(FS(c)->dir == nil)
37737da2899SCharles.Forsyth 			oserror();
37823babd45SCharles.Forsyth 		FS(c)->eod = 0;
37937da2899SCharles.Forsyth 	} else {
38037da2899SCharles.Forsyth 		o = (O_CREAT | O_EXCL) | (mode&3);
38137da2899SCharles.Forsyth 		if(mode & OTRUNC)
38237da2899SCharles.Forsyth 			o |= O_TRUNC;
38337da2899SCharles.Forsyth 		perm &= ~0666 | (FS(c)->mode & 0666);
38437da2899SCharles.Forsyth 		fd = open(n->s, o, perm);
38537da2899SCharles.Forsyth 		if(fd < 0)
38637da2899SCharles.Forsyth 			oserror();
38737da2899SCharles.Forsyth 		fchmod(fd, perm);
38837da2899SCharles.Forsyth 		fchown(fd, up->env->uid, FS(c)->gid);
38935a6f3e1Sforsyth 		if(fstat(fd, &st) < 0){
39037da2899SCharles.Forsyth 			close(fd);
39137da2899SCharles.Forsyth 			oserror();
39237da2899SCharles.Forsyth 		}
39337da2899SCharles.Forsyth 		FS(c)->fd = fd;
39437da2899SCharles.Forsyth 	}
39537da2899SCharles.Forsyth 	cnameclose(FS(c)->name);
39637da2899SCharles.Forsyth 	FS(c)->name = n;
39737da2899SCharles.Forsyth 	poperror();
39837da2899SCharles.Forsyth 
39935a6f3e1Sforsyth 	c->qid = fsqid(&st);
40035a6f3e1Sforsyth 	FS(c)->gid = st.st_gid;
40135a6f3e1Sforsyth 	FS(c)->uid = st.st_uid;
40235a6f3e1Sforsyth 	FS(c)->mode = st.st_mode;
40337da2899SCharles.Forsyth 	c->mode = openmode(mode);
40437da2899SCharles.Forsyth 	c->offset = 0;
40537da2899SCharles.Forsyth 	FS(c)->offset = 0;
40635a6f3e1Sforsyth 	FS(c)->issocket = 0;
40737da2899SCharles.Forsyth 	c->flag |= COPEN;
40837da2899SCharles.Forsyth }
40937da2899SCharles.Forsyth 
41037da2899SCharles.Forsyth static void
fsclose(Chan * c)41137da2899SCharles.Forsyth fsclose(Chan *c)
41237da2899SCharles.Forsyth {
41337da2899SCharles.Forsyth 	if((c->flag & COPEN) != 0){
41437da2899SCharles.Forsyth 		if(c->qid.type & QTDIR)
41537da2899SCharles.Forsyth 			closedir(FS(c)->dir);
41637da2899SCharles.Forsyth 		else
41737da2899SCharles.Forsyth 			close(FS(c)->fd);
41837da2899SCharles.Forsyth 	}
41937da2899SCharles.Forsyth 	if(c->flag & CRCLOSE) {
42037da2899SCharles.Forsyth 		if(!waserror()) {
42137da2899SCharles.Forsyth 			fsremove(c);
42237da2899SCharles.Forsyth 			poperror();
42337da2899SCharles.Forsyth 		}
42437da2899SCharles.Forsyth 		return;
42537da2899SCharles.Forsyth 	}
42637da2899SCharles.Forsyth 	fsfree(c);
42737da2899SCharles.Forsyth }
42837da2899SCharles.Forsyth 
42937da2899SCharles.Forsyth static long
fsread(Chan * c,void * va,long n,vlong offset)43037da2899SCharles.Forsyth fsread(Chan *c, void *va, long n, vlong offset)
43137da2899SCharles.Forsyth {
43237da2899SCharles.Forsyth 	long r;
43337da2899SCharles.Forsyth 
43437da2899SCharles.Forsyth 	if(c->qid.type & QTDIR){
43537da2899SCharles.Forsyth 		qlock(&FS(c)->oq);
43637da2899SCharles.Forsyth 		if(waserror()) {
43737da2899SCharles.Forsyth 			qunlock(&FS(c)->oq);
43837da2899SCharles.Forsyth 			nexterror();
43937da2899SCharles.Forsyth 		}
44037da2899SCharles.Forsyth 		r = fsdirread(c, va, n, offset);
44137da2899SCharles.Forsyth 		poperror();
44237da2899SCharles.Forsyth 		qunlock(&FS(c)->oq);
44337da2899SCharles.Forsyth 	}else{
44435a6f3e1Sforsyth 		if(!FS(c)->issocket){
44537da2899SCharles.Forsyth 			r = pread(FS(c)->fd, va, n, offset);
446b18a52b7SCharles.Forsyth 			if(r >= 0)
447b18a52b7SCharles.Forsyth 				return r;
44835a6f3e1Sforsyth 			if(errno != ESPIPE && errno != EPIPE)
44937da2899SCharles.Forsyth 				oserror();
45037da2899SCharles.Forsyth 		}
45135a6f3e1Sforsyth 		r = read(FS(c)->fd, va, n);
45235a6f3e1Sforsyth 		if(r < 0)
45335a6f3e1Sforsyth 			oserror();
45437da2899SCharles.Forsyth 	}
45537da2899SCharles.Forsyth 	return r;
45637da2899SCharles.Forsyth }
45737da2899SCharles.Forsyth 
45837da2899SCharles.Forsyth static long
fswrite(Chan * c,void * va,long n,vlong offset)45937da2899SCharles.Forsyth fswrite(Chan *c, void *va, long n, vlong offset)
46037da2899SCharles.Forsyth {
46137da2899SCharles.Forsyth 	long r;
46237da2899SCharles.Forsyth 
46335a6f3e1Sforsyth 	if(!FS(c)->issocket){
46437da2899SCharles.Forsyth 		r = pwrite(FS(c)->fd, va, n, offset);
46535a6f3e1Sforsyth 		if(r >= 0)
46635a6f3e1Sforsyth 			return r;
46735a6f3e1Sforsyth 		if(errno != ESPIPE && errno != EPIPE)
46835a6f3e1Sforsyth 			oserror();
46935a6f3e1Sforsyth 	}
47037da2899SCharles.Forsyth 	r = write(FS(c)->fd, va, n);
47137da2899SCharles.Forsyth 	if(r < 0)
47237da2899SCharles.Forsyth 		oserror();
47337da2899SCharles.Forsyth 	return r;
47437da2899SCharles.Forsyth }
47537da2899SCharles.Forsyth 
47637da2899SCharles.Forsyth static void
fswchk(Cname * c)47737da2899SCharles.Forsyth fswchk(Cname *c)
47837da2899SCharles.Forsyth {
47935a6f3e1Sforsyth 	struct stat st;
48037da2899SCharles.Forsyth 
48135a6f3e1Sforsyth 	if(stat(c->s, &st) < 0)
48237da2899SCharles.Forsyth 		oserror();
48337da2899SCharles.Forsyth 
48435a6f3e1Sforsyth 	if(st.st_uid == up->env->uid)
48535a6f3e1Sforsyth 		st.st_mode >>= 6;
48635a6f3e1Sforsyth 	else if(st.st_gid == up->env->gid || ingroup(up->env->uid, st.st_gid))
48735a6f3e1Sforsyth 		st.st_mode >>= 3;
48837da2899SCharles.Forsyth 
48935a6f3e1Sforsyth 	if(st.st_mode & S_IWOTH)
49037da2899SCharles.Forsyth 		return;
49137da2899SCharles.Forsyth 
49237da2899SCharles.Forsyth 	error(Eperm);
49337da2899SCharles.Forsyth }
49437da2899SCharles.Forsyth 
49537da2899SCharles.Forsyth static void
fsremove(Chan * c)49637da2899SCharles.Forsyth fsremove(Chan *c)
49737da2899SCharles.Forsyth {
49837da2899SCharles.Forsyth 	int n;
49935a6f3e1Sforsyth 	Cname *volatile dir;
50037da2899SCharles.Forsyth 
50137da2899SCharles.Forsyth 	if(waserror()){
50237da2899SCharles.Forsyth 		fsfree(c);
50337da2899SCharles.Forsyth 		nexterror();
50437da2899SCharles.Forsyth 	}
50535a6f3e1Sforsyth 	dir = fswalkpath(FS(c)->name, "..", 1);
50635a6f3e1Sforsyth 	if(waserror()){
50735a6f3e1Sforsyth 		cnameclose(dir);
50835a6f3e1Sforsyth 		nexterror();
50935a6f3e1Sforsyth 	}
51035a6f3e1Sforsyth 	fswchk(dir);
51135a6f3e1Sforsyth 	cnameclose(dir);
51235a6f3e1Sforsyth 	poperror();
51337da2899SCharles.Forsyth 	if(c->qid.type & QTDIR)
51437da2899SCharles.Forsyth 		n = rmdir(FS(c)->name->s);
51537da2899SCharles.Forsyth 	else
51637da2899SCharles.Forsyth 		n = remove(FS(c)->name->s);
51737da2899SCharles.Forsyth 	if(n < 0)
51837da2899SCharles.Forsyth 		oserror();
51937da2899SCharles.Forsyth 	poperror();
52037da2899SCharles.Forsyth 	fsfree(c);
52137da2899SCharles.Forsyth }
52237da2899SCharles.Forsyth 
52337da2899SCharles.Forsyth static int
fswstat(Chan * c,uchar * buf,int nb)52437da2899SCharles.Forsyth fswstat(Chan *c, uchar *buf, int nb)
52537da2899SCharles.Forsyth {
52637da2899SCharles.Forsyth 	Dir *d;
527184c64d4SCharles.Forsyth 	User *p;
52835a6f3e1Sforsyth 	Cname *volatile ph;
52935a6f3e1Sforsyth 	struct stat st;
53037da2899SCharles.Forsyth 	struct utimbuf utbuf;
53137da2899SCharles.Forsyth 	int tsync;
53237da2899SCharles.Forsyth 
53337da2899SCharles.Forsyth 	if(FS(c)->fd >= 0){
53435a6f3e1Sforsyth 		if(fstat(FS(c)->fd, &st) < 0)
53537da2899SCharles.Forsyth 			oserror();
53637da2899SCharles.Forsyth 	}else{
53735a6f3e1Sforsyth 		if(stat(FS(c)->name->s, &st) < 0)
53837da2899SCharles.Forsyth 			oserror();
53937da2899SCharles.Forsyth 	}
54037da2899SCharles.Forsyth 	d = malloc(sizeof(*d)+nb);
54137da2899SCharles.Forsyth 	if(d == nil)
54237da2899SCharles.Forsyth 		error(Enomem);
54337da2899SCharles.Forsyth 	if(waserror()){
54437da2899SCharles.Forsyth 		free(d);
54537da2899SCharles.Forsyth 		nexterror();
54637da2899SCharles.Forsyth 	}
54737da2899SCharles.Forsyth 	tsync = 1;
54837da2899SCharles.Forsyth 	nb = convM2D(buf, nb, d, (char*)&d[1]);
54937da2899SCharles.Forsyth 	if(nb == 0)
55037da2899SCharles.Forsyth 		error(Eshortstat);
55137da2899SCharles.Forsyth 	if(!emptystr(d->name) && strcmp(d->name, fslastelem(FS(c)->name)) != 0) {
55237da2899SCharles.Forsyth 		tsync = 0;
55337da2899SCharles.Forsyth 		validname(d->name, 0);
55435a6f3e1Sforsyth 		ph = fswalkpath(FS(c)->name, "..", 1);
55537da2899SCharles.Forsyth 		if(waserror()){
55635a6f3e1Sforsyth 			cnameclose(ph);
55737da2899SCharles.Forsyth 			nexterror();
55837da2899SCharles.Forsyth 		}
55935a6f3e1Sforsyth 		fswchk(ph);
56035a6f3e1Sforsyth 		ph = fswalkpath(ph, d->name, 0);
56135a6f3e1Sforsyth 		if(rename(FS(c)->name->s, ph->s) < 0)
56237da2899SCharles.Forsyth 			oserror();
56337da2899SCharles.Forsyth 		cnameclose(FS(c)->name);
56437da2899SCharles.Forsyth 		poperror();
56535a6f3e1Sforsyth 		FS(c)->name = ph;
56637da2899SCharles.Forsyth 	}
56737da2899SCharles.Forsyth 
56835a6f3e1Sforsyth 	if(d->mode != ~0 && (d->mode&0777) != (st.st_mode&0777)) {
56937da2899SCharles.Forsyth 		tsync = 0;
57035a6f3e1Sforsyth 		if(up->env->uid != st.st_uid)
57137da2899SCharles.Forsyth 			error(Eowner);
57237da2899SCharles.Forsyth 		if(FS(c)->fd >= 0){
57337da2899SCharles.Forsyth 			if(fchmod(FS(c)->fd, d->mode&0777) < 0)
57437da2899SCharles.Forsyth 				oserror();
57537da2899SCharles.Forsyth 		}else{
57637da2899SCharles.Forsyth 			if(chmod(FS(c)->name->s, d->mode&0777) < 0)
57737da2899SCharles.Forsyth 				oserror();
57837da2899SCharles.Forsyth 		}
57937da2899SCharles.Forsyth 		FS(c)->mode &= ~0777;
58037da2899SCharles.Forsyth 		FS(c)->mode |= d->mode&0777;
58137da2899SCharles.Forsyth 	}
58237da2899SCharles.Forsyth 
58335a6f3e1Sforsyth 	if(d->atime != ~0 && d->atime != st.st_atime ||
58435a6f3e1Sforsyth 	   d->mtime != ~0 && d->mtime != st.st_mtime) {
58537da2899SCharles.Forsyth 		tsync = 0;
58635a6f3e1Sforsyth 		if(up->env->uid != st.st_uid)
58737da2899SCharles.Forsyth 			error(Eowner);
58837da2899SCharles.Forsyth 		if(d->mtime != ~0)
58937da2899SCharles.Forsyth 			utbuf.modtime = d->mtime;
59037da2899SCharles.Forsyth 		else
59135a6f3e1Sforsyth 			utbuf.modtime = st.st_mtime;
59237da2899SCharles.Forsyth 		if(d->atime != ~0)
59337da2899SCharles.Forsyth 			utbuf.actime  = d->atime;
59437da2899SCharles.Forsyth 		else
59535a6f3e1Sforsyth 			utbuf.actime = st.st_atime;
59637da2899SCharles.Forsyth 		if(utime(FS(c)->name->s, &utbuf) < 0)	/* TO DO: futimes isn't portable */
59737da2899SCharles.Forsyth 			oserror();
59837da2899SCharles.Forsyth 	}
59937da2899SCharles.Forsyth 
60037da2899SCharles.Forsyth 	if(*d->gid){
60137da2899SCharles.Forsyth 		tsync = 0;
602184c64d4SCharles.Forsyth 		qlock(&idl);
60337da2899SCharles.Forsyth 		if(waserror()){
604184c64d4SCharles.Forsyth 			qunlock(&idl);
60537da2899SCharles.Forsyth 			nexterror();
60637da2899SCharles.Forsyth 		}
607184c64d4SCharles.Forsyth 		p = name2user(gidmap, d->gid, newgname);
60837da2899SCharles.Forsyth 		if(p == 0)
60937da2899SCharles.Forsyth 			error(Eunknown);
61035a6f3e1Sforsyth 		if(p->id != st.st_gid) {
61135a6f3e1Sforsyth 			if(up->env->uid != st.st_uid)
61237da2899SCharles.Forsyth 				error(Eowner);
61337da2899SCharles.Forsyth 			if(FS(c)->fd >= 0){
61435a6f3e1Sforsyth 				if(fchown(FS(c)->fd, st.st_uid, p->id) < 0)
61537da2899SCharles.Forsyth 					oserror();
61637da2899SCharles.Forsyth 			}else{
61735a6f3e1Sforsyth 				if(chown(FS(c)->name->s, st.st_uid, p->id) < 0)
61837da2899SCharles.Forsyth 					oserror();
61937da2899SCharles.Forsyth 			}
62037da2899SCharles.Forsyth 			FS(c)->gid = p->id;
62137da2899SCharles.Forsyth 		}
62237da2899SCharles.Forsyth 		poperror();
623184c64d4SCharles.Forsyth 		qunlock(&idl);
62437da2899SCharles.Forsyth 	}
62537da2899SCharles.Forsyth 
62637da2899SCharles.Forsyth 	if(d->length != ~(uvlong)0){
62737da2899SCharles.Forsyth 		tsync = 0;
62837da2899SCharles.Forsyth 		if(FS(c)->fd >= 0){
62937da2899SCharles.Forsyth 			fsperm(c, 2);
63037da2899SCharles.Forsyth 			if(ftruncate(FS(c)->fd, d->length) < 0)
63137da2899SCharles.Forsyth 				oserror();
63237da2899SCharles.Forsyth 		}else{
63337da2899SCharles.Forsyth 			fswchk(FS(c)->name);
63437da2899SCharles.Forsyth 			if(truncate(FS(c)->name->s, d->length) < 0)
63537da2899SCharles.Forsyth 				oserror();
63637da2899SCharles.Forsyth 		}
63737da2899SCharles.Forsyth 	}
63837da2899SCharles.Forsyth 
63937da2899SCharles.Forsyth 	poperror();
64037da2899SCharles.Forsyth 	free(d);
64137da2899SCharles.Forsyth 	if(tsync && FS(c)->fd >= 0 && fsync(FS(c)->fd) < 0)
64237da2899SCharles.Forsyth 		oserror();
64337da2899SCharles.Forsyth 	return nb;
64437da2899SCharles.Forsyth }
64537da2899SCharles.Forsyth 
64637da2899SCharles.Forsyth static Qid
fsqid(struct stat * st)64737da2899SCharles.Forsyth fsqid(struct stat *st)
64837da2899SCharles.Forsyth {
64937da2899SCharles.Forsyth 	Qid q;
650c594916bSforsyth 	u16int dev;
65137da2899SCharles.Forsyth 
65237da2899SCharles.Forsyth 	q.type = QTFILE;
65337da2899SCharles.Forsyth 	if(S_ISDIR(st->st_mode))
65437da2899SCharles.Forsyth 		q.type = QTDIR;
65537da2899SCharles.Forsyth 
656c594916bSforsyth 	dev = (u16int)st->st_dev;
657c594916bSforsyth 	if(dev & 0x8000){
6582aba3aa7SCharles Forsyth 		static int aware = 1;
659c594916bSforsyth 		if(aware==0){
660c594916bSforsyth 			aware = 1;
661c594916bSforsyth 			fprint(2, "fs: fsqid: top-bit dev: %#4.4ux\n", dev);
66237da2899SCharles.Forsyth 		}
663c594916bSforsyth 		dev ^= 0x8080;
66437da2899SCharles.Forsyth 	}
66537da2899SCharles.Forsyth 
666c594916bSforsyth 	q.path = (uvlong)dev<<48;
667c594916bSforsyth 	q.path ^= st->st_ino;
66837da2899SCharles.Forsyth 	q.vers = st->st_mtime;
66937da2899SCharles.Forsyth 
67037da2899SCharles.Forsyth 	return q;
67137da2899SCharles.Forsyth }
67237da2899SCharles.Forsyth 
67337da2899SCharles.Forsyth static void
fspath(Cname * c,char * name,char * path)67437da2899SCharles.Forsyth fspath(Cname *c, char *name, char *path)
67537da2899SCharles.Forsyth {
67637da2899SCharles.Forsyth 	int n;
67737da2899SCharles.Forsyth 
67837da2899SCharles.Forsyth 	if(c->len+strlen(name) >= MAXPATH)
67937da2899SCharles.Forsyth 		panic("fspath: name too long");
68037da2899SCharles.Forsyth 	memmove(path, c->s, c->len);
68137da2899SCharles.Forsyth 	n = c->len;
68237da2899SCharles.Forsyth 	if(path[n-1] != '/')
68337da2899SCharles.Forsyth 		path[n++] = '/';
68437da2899SCharles.Forsyth 	strcpy(path+n, name);
68537da2899SCharles.Forsyth 	if(isdotdot(name))
68637da2899SCharles.Forsyth 		cleanname(path);
68737da2899SCharles.Forsyth /*print("->%s\n", path);*/
68837da2899SCharles.Forsyth }
68937da2899SCharles.Forsyth 
69037da2899SCharles.Forsyth static Cname *
fswalkpath(Cname * c,char * name,int dup)69137da2899SCharles.Forsyth fswalkpath(Cname *c, char *name, int dup)
69237da2899SCharles.Forsyth {
69337da2899SCharles.Forsyth 	if(dup)
69437da2899SCharles.Forsyth 		c = newcname(c->s);
69537da2899SCharles.Forsyth 	c = addelem(c, name);
69637da2899SCharles.Forsyth 	if(isdotdot(name))
69737da2899SCharles.Forsyth 		cleancname(c);
69837da2899SCharles.Forsyth 	return c;
69937da2899SCharles.Forsyth }
70037da2899SCharles.Forsyth 
70137da2899SCharles.Forsyth static char *
fslastelem(Cname * c)70237da2899SCharles.Forsyth fslastelem(Cname *c)
70337da2899SCharles.Forsyth {
70437da2899SCharles.Forsyth 	char *p;
70537da2899SCharles.Forsyth 
70637da2899SCharles.Forsyth 	p = c->s + c->len;
70737da2899SCharles.Forsyth 	while(p > c->s && p[-1] != '/')
70837da2899SCharles.Forsyth 		p--;
70937da2899SCharles.Forsyth 	return p;
71037da2899SCharles.Forsyth }
71137da2899SCharles.Forsyth 
71237da2899SCharles.Forsyth static void
fsperm(Chan * c,int mask)71337da2899SCharles.Forsyth fsperm(Chan *c, int mask)
71437da2899SCharles.Forsyth {
71537da2899SCharles.Forsyth 	int m;
71637da2899SCharles.Forsyth 
71737da2899SCharles.Forsyth 	m = FS(c)->mode;
71837da2899SCharles.Forsyth /*
71937da2899SCharles.Forsyth 	print("fsperm: %o %o uuid %d ugid %d cuid %d cgid %d\n",
72037da2899SCharles.Forsyth 		m, mask, up->env->uid, up->env->gid, FS(c)->uid, FS(c)->gid);
72137da2899SCharles.Forsyth */
72237da2899SCharles.Forsyth 	if(FS(c)->uid == up->env->uid)
72337da2899SCharles.Forsyth 		m >>= 6;
72435a6f3e1Sforsyth 	else if(FS(c)->gid == up->env->gid || ingroup(up->env->uid, FS(c)->gid))
72537da2899SCharles.Forsyth 		m >>= 3;
72637da2899SCharles.Forsyth 
72737da2899SCharles.Forsyth 	m &= mask;
72837da2899SCharles.Forsyth 	if(m == 0)
72937da2899SCharles.Forsyth 		error(Eperm);
73037da2899SCharles.Forsyth }
73137da2899SCharles.Forsyth 
73237da2899SCharles.Forsyth static int
isdots(char * name)73337da2899SCharles.Forsyth isdots(char *name)
73437da2899SCharles.Forsyth {
73537da2899SCharles.Forsyth 	return name[0] == '.' && (name[1] == '\0' || name[1] == '.' && name[2] == '\0');
73637da2899SCharles.Forsyth }
73737da2899SCharles.Forsyth 
73837da2899SCharles.Forsyth static int
fsdirconv(Chan * c,char * path,char * name,struct stat * s,uchar * va,int nb,int indir)73948cb024aSforsyth fsdirconv(Chan *c, char *path, char *name, struct stat *s, uchar *va, int nb, int indir)
74037da2899SCharles.Forsyth {
74137da2899SCharles.Forsyth 	Dir d;
742184c64d4SCharles.Forsyth 	char uidbuf[NUMSIZE], gidbuf[NUMSIZE];
743184c64d4SCharles.Forsyth 	User *u;
74448cb024aSforsyth 	int fd;
74537da2899SCharles.Forsyth 
74637da2899SCharles.Forsyth 	memset(&d, 0, sizeof(d));
74737da2899SCharles.Forsyth 	d.name = name;
748184c64d4SCharles.Forsyth 	u = id2user(uidmap, s->st_uid, newuid);
749184c64d4SCharles.Forsyth 	if(u == nil){
750184c64d4SCharles.Forsyth 		snprint(uidbuf, sizeof(uidbuf), "#%lud", (long)s->st_uid);
751184c64d4SCharles.Forsyth 		d.uid = uidbuf;
752184c64d4SCharles.Forsyth 	}else
753184c64d4SCharles.Forsyth 		d.uid = u->name;
754184c64d4SCharles.Forsyth 	u = id2user(gidmap, s->st_gid, newgid);
755184c64d4SCharles.Forsyth 	if(u == nil){
756184c64d4SCharles.Forsyth 		snprint(gidbuf, sizeof(gidbuf), "#%lud", (long)s->st_gid);
757184c64d4SCharles.Forsyth 		d.gid = gidbuf;
758184c64d4SCharles.Forsyth 	}else
759184c64d4SCharles.Forsyth 		d.gid = u->name;
76037da2899SCharles.Forsyth 	d.muid = "";
76137da2899SCharles.Forsyth 	d.qid = fsqid(s);
76237da2899SCharles.Forsyth 	d.mode = (d.qid.type<<24)|(s->st_mode&0777);
76337da2899SCharles.Forsyth 	d.atime = s->st_atime;
76437da2899SCharles.Forsyth 	d.mtime = s->st_mtime;
76537da2899SCharles.Forsyth 	d.length = s->st_size;
76637da2899SCharles.Forsyth 	if(d.mode&DMDIR)
76737da2899SCharles.Forsyth 		d.length = 0;
76848cb024aSforsyth 	else if(S_ISBLK(s->st_mode) && s->st_size == 0){
76948cb024aSforsyth 		fd = open(path, O_RDONLY);
77048cb024aSforsyth 		if(fd >= 0){
77148cb024aSforsyth 			d.length = osdisksize(fd);
77248cb024aSforsyth 			close(fd);
77348cb024aSforsyth 		}
77448cb024aSforsyth 	}
77537da2899SCharles.Forsyth 	d.type = 'U';
77637da2899SCharles.Forsyth 	d.dev = c->dev;
77737da2899SCharles.Forsyth 	if(indir && sizeD2M(&d) > nb)
77837da2899SCharles.Forsyth 		return -1;	/* directory reader needs to know it didn't fit */
77937da2899SCharles.Forsyth 	return convD2M(&d, va, nb);
78037da2899SCharles.Forsyth }
78137da2899SCharles.Forsyth 
78237da2899SCharles.Forsyth static long
fsdirread(Chan * c,uchar * va,int count,vlong offset)78337da2899SCharles.Forsyth fsdirread(Chan *c, uchar *va, int count, vlong offset)
78437da2899SCharles.Forsyth {
78537da2899SCharles.Forsyth 	int i;
78637da2899SCharles.Forsyth 	long n, r;
78735a6f3e1Sforsyth 	struct stat st;
78837da2899SCharles.Forsyth 	char path[MAXPATH], *ep;
78937da2899SCharles.Forsyth 	struct dirent *de;
790aee7f58dSforsyth 	static uchar slop[8192];
79137da2899SCharles.Forsyth 
79237da2899SCharles.Forsyth 	i = 0;
79337da2899SCharles.Forsyth 	fspath(FS(c)->name, "", path);
79437da2899SCharles.Forsyth 	ep = path+strlen(path);
79537da2899SCharles.Forsyth 	if(FS(c)->offset != offset) {
79637da2899SCharles.Forsyth 		seekdir(FS(c)->dir, 0);
79737da2899SCharles.Forsyth 		FS(c)->de = nil;
79823babd45SCharles.Forsyth 		FS(c)->eod = 0;
79937da2899SCharles.Forsyth 		for(n=0; n<offset; ) {
80037da2899SCharles.Forsyth 			de = readdir(FS(c)->dir);
80137da2899SCharles.Forsyth 			if(de == 0) {
80237da2899SCharles.Forsyth 				/* EOF, so stash offset and return 0 */
80337da2899SCharles.Forsyth 				FS(c)->offset = n;
80423babd45SCharles.Forsyth 				FS(c)->eod = 1;
80537da2899SCharles.Forsyth 				return 0;
80637da2899SCharles.Forsyth 			}
80737da2899SCharles.Forsyth 			if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
80837da2899SCharles.Forsyth 				continue;
80937da2899SCharles.Forsyth 			strecpy(ep, path+sizeof(path), de->d_name);
81035a6f3e1Sforsyth 			if(xstat(path, &st) < 0) {
81137da2899SCharles.Forsyth 				fprint(2, "dir: bad path %s\n", path);
81237da2899SCharles.Forsyth 				continue;
81337da2899SCharles.Forsyth 			}
814184c64d4SCharles.Forsyth 			qlock(&idl);
8151d6f6b43Sforsyth 			if(waserror()){
8161d6f6b43Sforsyth 				qunlock(&idl);
8171d6f6b43Sforsyth 				nexterror();
8181d6f6b43Sforsyth 			}
81948cb024aSforsyth 			r = fsdirconv(c, path, de->d_name, &st, slop, sizeof(slop), 1);
8201d6f6b43Sforsyth 			poperror();
821184c64d4SCharles.Forsyth 			qunlock(&idl);
82237da2899SCharles.Forsyth 			if(r <= 0) {
82337da2899SCharles.Forsyth 				FS(c)->offset = n;
82437da2899SCharles.Forsyth 				return 0;
82537da2899SCharles.Forsyth 			}
82637da2899SCharles.Forsyth 			n += r;
82737da2899SCharles.Forsyth 		}
82837da2899SCharles.Forsyth 		FS(c)->offset = offset;
82937da2899SCharles.Forsyth 	}
83037da2899SCharles.Forsyth 
83123babd45SCharles.Forsyth 	if(FS(c)->eod)
83223babd45SCharles.Forsyth 		return 0;
83323babd45SCharles.Forsyth 
83437da2899SCharles.Forsyth 	/*
83537da2899SCharles.Forsyth 	 * Take idl on behalf of id2name.  Stalling attach, which is a
83637da2899SCharles.Forsyth 	 * rare operation, until the readdir completes is probably
83737da2899SCharles.Forsyth 	 * preferable to adding lock round-trips.
83837da2899SCharles.Forsyth 	 */
839184c64d4SCharles.Forsyth 	qlock(&idl);
84037da2899SCharles.Forsyth 	while(i < count){
84137da2899SCharles.Forsyth 		de = FS(c)->de;
84237da2899SCharles.Forsyth 		FS(c)->de = nil;
84337da2899SCharles.Forsyth 		if(de == nil)
84437da2899SCharles.Forsyth 			de = readdir(FS(c)->dir);
84523babd45SCharles.Forsyth 		if(de == nil){
84623babd45SCharles.Forsyth 			FS(c)->eod = 1;
84737da2899SCharles.Forsyth 			break;
84823babd45SCharles.Forsyth 		}
84937da2899SCharles.Forsyth 
85037da2899SCharles.Forsyth 		if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
85137da2899SCharles.Forsyth 			continue;
85237da2899SCharles.Forsyth 
85337da2899SCharles.Forsyth 		strecpy(ep, path+sizeof(path), de->d_name);
85435a6f3e1Sforsyth 		if(xstat(path, &st) < 0) {
85537da2899SCharles.Forsyth 			fprint(2, "dir: bad path %s\n", path);
85637da2899SCharles.Forsyth 			continue;
85737da2899SCharles.Forsyth 		}
85848cb024aSforsyth 		r = fsdirconv(c, path, de->d_name, &st, va+i, count-i, 1);
85937da2899SCharles.Forsyth 		if(r <= 0){
86037da2899SCharles.Forsyth 			FS(c)->de = de;
86137da2899SCharles.Forsyth 			break;
86237da2899SCharles.Forsyth 		}
86337da2899SCharles.Forsyth 		i += r;
86437da2899SCharles.Forsyth 		FS(c)->offset += r;
86537da2899SCharles.Forsyth 	}
866184c64d4SCharles.Forsyth 	qunlock(&idl);
86737da2899SCharles.Forsyth 	return i;
86837da2899SCharles.Forsyth }
86937da2899SCharles.Forsyth 
87037da2899SCharles.Forsyth static int
fsomode(int m)87137da2899SCharles.Forsyth fsomode(int m)
87237da2899SCharles.Forsyth {
87337da2899SCharles.Forsyth 	if(m < 0 || m > 3)
87437da2899SCharles.Forsyth 		error(Ebadarg);
87537da2899SCharles.Forsyth 	return m == 3? 0: m;
87637da2899SCharles.Forsyth }
87737da2899SCharles.Forsyth 
87837da2899SCharles.Forsyth void
setid(char * name,int owner)87937da2899SCharles.Forsyth setid(char *name, int owner)
88037da2899SCharles.Forsyth {
881184c64d4SCharles.Forsyth 	User *u;
88237da2899SCharles.Forsyth 
88337da2899SCharles.Forsyth 	if(owner && !iseve())
88437da2899SCharles.Forsyth 		return;
88537da2899SCharles.Forsyth 	kstrdup(&up->env->user, name);
88637da2899SCharles.Forsyth 
887184c64d4SCharles.Forsyth 	qlock(&idl);
888184c64d4SCharles.Forsyth 	u = name2user(uidmap, name, newuname);
889184c64d4SCharles.Forsyth 	if(u == nil){
890184c64d4SCharles.Forsyth 		qunlock(&idl);
89137da2899SCharles.Forsyth 		up->env->uid = -1;
89237da2899SCharles.Forsyth 		up->env->gid = -1;
89337da2899SCharles.Forsyth 		return;
89437da2899SCharles.Forsyth 	}
89537da2899SCharles.Forsyth 
896184c64d4SCharles.Forsyth 	up->env->uid = u->id;
897184c64d4SCharles.Forsyth 	up->env->gid = u->gid;
898184c64d4SCharles.Forsyth 	qunlock(&idl);
899184c64d4SCharles.Forsyth }
900184c64d4SCharles.Forsyth 
901184c64d4SCharles.Forsyth static User**
hashuser(User ** tab,int id)902184c64d4SCharles.Forsyth hashuser(User** tab, int id)
903184c64d4SCharles.Forsyth {
904184c64d4SCharles.Forsyth 	int i;
905184c64d4SCharles.Forsyth 
906184c64d4SCharles.Forsyth 	i = (id>>IDSHIFT) ^ id;
907184c64d4SCharles.Forsyth 	return &tab[i & IDMASK];
908184c64d4SCharles.Forsyth }
909184c64d4SCharles.Forsyth 
910184c64d4SCharles.Forsyth /*
911184c64d4SCharles.Forsyth  * the caller of the following functions must hold QLock idl.
912184c64d4SCharles.Forsyth  */
913184c64d4SCharles.Forsyth 
914184c64d4SCharles.Forsyth /*
915184c64d4SCharles.Forsyth  * we could keep separate maps of user and group names to Users to
916184c64d4SCharles.Forsyth  * speed this up, but the reverse lookup currently isn't common (ie, change group by wstat and setid)
917184c64d4SCharles.Forsyth  */
918184c64d4SCharles.Forsyth static User*
name2user(User ** tab,char * name,User * (* get)(char *))919184c64d4SCharles.Forsyth name2user(User **tab, char *name, User* (*get)(char*))
920184c64d4SCharles.Forsyth {
921184c64d4SCharles.Forsyth 	int i;
922184c64d4SCharles.Forsyth 	User *u, **h;
923184c64d4SCharles.Forsyth 	static User *prevu;
924184c64d4SCharles.Forsyth 	static User **prevtab;
925184c64d4SCharles.Forsyth 
926184c64d4SCharles.Forsyth 	if(prevu != nil && prevtab == tab && strcmp(name, prevu->name) == 0)
927184c64d4SCharles.Forsyth 		return prevu;	/* it's often the one we've just seen */
928184c64d4SCharles.Forsyth 
929184c64d4SCharles.Forsyth 	for(i=0; i<NID; i++)
930184c64d4SCharles.Forsyth 		for(u = tab[i]; u != nil; u = u->next)
931184c64d4SCharles.Forsyth 			if(strcmp(name, u->name) == 0) {
932184c64d4SCharles.Forsyth 				prevtab = tab;
933184c64d4SCharles.Forsyth 				prevu = u;
934184c64d4SCharles.Forsyth 				return u;
935184c64d4SCharles.Forsyth 			}
936184c64d4SCharles.Forsyth 
937184c64d4SCharles.Forsyth 	u = get(name);
938184c64d4SCharles.Forsyth 	if(u == nil)
939184c64d4SCharles.Forsyth 		return nil;
940184c64d4SCharles.Forsyth 	h = hashuser(tab, u->id);
941184c64d4SCharles.Forsyth 	u->next = *h;
942184c64d4SCharles.Forsyth 	*h = u;
943184c64d4SCharles.Forsyth 	prevtab = tab;
944184c64d4SCharles.Forsyth 	prevu = u;
945184c64d4SCharles.Forsyth 	return u;
946184c64d4SCharles.Forsyth }
947184c64d4SCharles.Forsyth 
948184c64d4SCharles.Forsyth static void
freeuser(User * u)949184c64d4SCharles.Forsyth freeuser(User *u)
950184c64d4SCharles.Forsyth {
951184c64d4SCharles.Forsyth 	if(u != nil){
952184c64d4SCharles.Forsyth 		free(u->name);
953184c64d4SCharles.Forsyth 		free(u->mem);
954184c64d4SCharles.Forsyth 		free(u);
955184c64d4SCharles.Forsyth 	}
956184c64d4SCharles.Forsyth }
957184c64d4SCharles.Forsyth 
958184c64d4SCharles.Forsyth static User*
newuser(int id,int gid,char * name,int nmem)959184c64d4SCharles.Forsyth newuser(int id, int gid, char *name, int nmem)
960184c64d4SCharles.Forsyth {
961184c64d4SCharles.Forsyth 	User *u;
962184c64d4SCharles.Forsyth 
963184c64d4SCharles.Forsyth 	u = malloc(sizeof(*u));
964184c64d4SCharles.Forsyth 	if(u == nil)
965184c64d4SCharles.Forsyth 		return nil;
966184c64d4SCharles.Forsyth 	u->name = strdup(name);
967184c64d4SCharles.Forsyth 	if(u->name == nil){
968184c64d4SCharles.Forsyth 		free(u);
969184c64d4SCharles.Forsyth 		return nil;
970184c64d4SCharles.Forsyth 	}
971184c64d4SCharles.Forsyth 	u->nmem = nmem;
972184c64d4SCharles.Forsyth 	if(nmem){
973184c64d4SCharles.Forsyth 		u->mem = malloc(nmem*sizeof(*u->mem));
974184c64d4SCharles.Forsyth 		if(u->mem == nil){
975184c64d4SCharles.Forsyth 			free(u->name);
976184c64d4SCharles.Forsyth 			free(u);
977184c64d4SCharles.Forsyth 			return nil;
978184c64d4SCharles.Forsyth 		}
979184c64d4SCharles.Forsyth 	}else
980184c64d4SCharles.Forsyth 		u->mem = nil;
981184c64d4SCharles.Forsyth 	u->id = id;
982184c64d4SCharles.Forsyth 	u->gid = gid;
983184c64d4SCharles.Forsyth 	u->next = nil;
984184c64d4SCharles.Forsyth 	return u;
985184c64d4SCharles.Forsyth }
986184c64d4SCharles.Forsyth 
987184c64d4SCharles.Forsyth static User*
newuname(char * name)988184c64d4SCharles.Forsyth newuname(char *name)
989184c64d4SCharles.Forsyth {
990184c64d4SCharles.Forsyth 	struct passwd *p;
991184c64d4SCharles.Forsyth 
992184c64d4SCharles.Forsyth 	p = getpwnam(name);
993184c64d4SCharles.Forsyth 	if(p == nil)
994184c64d4SCharles.Forsyth 		return nil;
995184c64d4SCharles.Forsyth 	return newuser(p->pw_uid, p->pw_gid, name, 0);
996184c64d4SCharles.Forsyth }
997184c64d4SCharles.Forsyth 
998184c64d4SCharles.Forsyth static User*
newuid(int id)999184c64d4SCharles.Forsyth newuid(int id)
1000184c64d4SCharles.Forsyth {
1001184c64d4SCharles.Forsyth 	struct passwd *p;
1002184c64d4SCharles.Forsyth 
1003184c64d4SCharles.Forsyth 	p = getpwuid(id);
1004184c64d4SCharles.Forsyth 	if(p == nil)
1005184c64d4SCharles.Forsyth 		return nil;
1006184c64d4SCharles.Forsyth 	return newuser(p->pw_uid, p->pw_gid, p->pw_name, 0);
1007184c64d4SCharles.Forsyth }
1008184c64d4SCharles.Forsyth 
1009184c64d4SCharles.Forsyth static User*
newgroup(struct group * g)1010184c64d4SCharles.Forsyth newgroup(struct group *g)
1011184c64d4SCharles.Forsyth {
1012184c64d4SCharles.Forsyth 	User *u, *gm;
1013184c64d4SCharles.Forsyth 	int n, o;
1014184c64d4SCharles.Forsyth 
1015184c64d4SCharles.Forsyth 	if(g == nil)
1016184c64d4SCharles.Forsyth 		return nil;
1017184c64d4SCharles.Forsyth 	for(n=0; g->gr_mem[n] != nil; n++)
1018184c64d4SCharles.Forsyth 		;
1019184c64d4SCharles.Forsyth 	u = newuser(g->gr_gid, g->gr_gid, g->gr_name, n);
1020184c64d4SCharles.Forsyth 	if(u == nil)
1021184c64d4SCharles.Forsyth 		return nil;
1022184c64d4SCharles.Forsyth 	o = 0;
1023184c64d4SCharles.Forsyth 	for(n=0; g->gr_mem[n] != nil; n++){
1024184c64d4SCharles.Forsyth 		gm = name2user(uidmap, g->gr_mem[n], newuname);
1025184c64d4SCharles.Forsyth 		if(gm != nil)
1026184c64d4SCharles.Forsyth 			u->mem[o++] = gm->id;
1027184c64d4SCharles.Forsyth 		/* ignore names that don't map to IDs */
1028184c64d4SCharles.Forsyth 	}
1029184c64d4SCharles.Forsyth 	u->nmem = o;
1030184c64d4SCharles.Forsyth 	return u;
1031184c64d4SCharles.Forsyth }
1032184c64d4SCharles.Forsyth 
1033184c64d4SCharles.Forsyth static User*
newgid(int id)1034184c64d4SCharles.Forsyth newgid(int id)
1035184c64d4SCharles.Forsyth {
1036184c64d4SCharles.Forsyth 	return newgroup(getgrgid(id));
1037184c64d4SCharles.Forsyth }
1038184c64d4SCharles.Forsyth 
1039184c64d4SCharles.Forsyth static User*
newgname(char * name)1040184c64d4SCharles.Forsyth newgname(char *name)
1041184c64d4SCharles.Forsyth {
1042184c64d4SCharles.Forsyth 	return newgroup(getgrnam(name));
1043184c64d4SCharles.Forsyth }
1044184c64d4SCharles.Forsyth 
1045184c64d4SCharles.Forsyth static User*
id2user(User ** tab,int id,User * (* get)(int))1046184c64d4SCharles.Forsyth id2user(User **tab, int id, User* (*get)(int))
1047184c64d4SCharles.Forsyth {
1048184c64d4SCharles.Forsyth 	User *u, **h;
1049184c64d4SCharles.Forsyth 
1050184c64d4SCharles.Forsyth 	h = hashuser(tab, id);
1051184c64d4SCharles.Forsyth 	for(u = *h; u != nil; u = u->next)
1052184c64d4SCharles.Forsyth 		if(u->id == id)
1053184c64d4SCharles.Forsyth 			return u;
1054184c64d4SCharles.Forsyth 	u = get(id);
1055184c64d4SCharles.Forsyth 	if(u == nil)
1056184c64d4SCharles.Forsyth 		return nil;
1057184c64d4SCharles.Forsyth 	u->next = *h;
1058184c64d4SCharles.Forsyth 	*h = u;
1059184c64d4SCharles.Forsyth 	return u;
1060184c64d4SCharles.Forsyth }
1061184c64d4SCharles.Forsyth 
1062184c64d4SCharles.Forsyth static int
ingroup(int id,int gid)1063184c64d4SCharles.Forsyth ingroup(int id, int gid)
1064184c64d4SCharles.Forsyth {
1065184c64d4SCharles.Forsyth 	int i;
1066184c64d4SCharles.Forsyth 	User *g;
1067184c64d4SCharles.Forsyth 
1068184c64d4SCharles.Forsyth 	g = id2user(gidmap, gid, newgid);
1069184c64d4SCharles.Forsyth 	if(g == nil || g->mem == nil)
1070184c64d4SCharles.Forsyth 		return 0;
1071184c64d4SCharles.Forsyth 	for(i = 0; i < g->nmem; i++)
1072184c64d4SCharles.Forsyth 		if(g->mem[i] == id)
1073184c64d4SCharles.Forsyth 			return 1;
1074184c64d4SCharles.Forsyth 	return 0;
107537da2899SCharles.Forsyth }
107637da2899SCharles.Forsyth 
107737da2899SCharles.Forsyth Dev fsdevtab = {
107837da2899SCharles.Forsyth 	'U',
107937da2899SCharles.Forsyth 	"fs",
108037da2899SCharles.Forsyth 
108137da2899SCharles.Forsyth 	devinit,
108237da2899SCharles.Forsyth 	fsattach,
108337da2899SCharles.Forsyth 	fswalk,
108437da2899SCharles.Forsyth 	fsstat,
108537da2899SCharles.Forsyth 	fsopen,
108637da2899SCharles.Forsyth 	fscreate,
108737da2899SCharles.Forsyth 	fsclose,
108837da2899SCharles.Forsyth 	fsread,
108937da2899SCharles.Forsyth 	devbread,
109037da2899SCharles.Forsyth 	fswrite,
109137da2899SCharles.Forsyth 	devbwrite,
109237da2899SCharles.Forsyth 	fsremove,
109337da2899SCharles.Forsyth 	fswstat
109437da2899SCharles.Forsyth };
1095