xref: /inferno-os/emu/port/devfs-posix.c (revision 7b3bf63c0d3f8b97a41a7022310b70c5d40c6fc6)
1 /*
2  * Unix file system interface
3  */
4 #define _LARGEFILE64_SOURCE	1
5 #define _FILE_OFFSET_BITS 64
6 #include	"dat.h"
7 #include	"fns.h"
8 #include	"error.h"
9 
10 #include	<sys/types.h>
11 #include	<sys/stat.h>
12 #include	<sys/fcntl.h>
13 #include	<utime.h>
14 #include	<dirent.h>
15 #include	<stdio.h>
16 #define	__EXTENSIONS__
17 #undef	getwd
18 #include	<unistd.h>
19 #include	<pwd.h>
20 #include	<grp.h>
21 
22 typedef struct Fsinfo Fsinfo;
23 struct Fsinfo
24 {
25 	int	uid;
26 	int	gid;
27 	int	mode;	/* Unix mode */
28 	DIR*	dir;		/* open directory */
29 	struct dirent*	de;	/* directory reading */
30 	int	fd;		/* open files */
31 	ulong	offset;	/* offset when reading directory */
32 	int	eod;	/* end of directory */
33 	QLock	oq;	/* mutex for offset */
34 	char*	spec;
35 	Cname*	name;	/* Unix's name for file */
36 	Qid	rootqid;		/* Plan 9's qid for Inferno's root */
37 };
38 
39 #define	FS(c)	((Fsinfo*)(c)->aux)
40 
41 enum
42 {
43 	IDSHIFT	= 8,
44 	NID	= 1 << IDSHIFT,
45 	IDMASK	= NID - 1,
46 	MAXPATH	= 1024	/* TO DO: eliminate this */
47 };
48 
49 typedef struct User User;
50 struct User
51 {
52 	int	id;		/* might be user or group ID */
53 	int	gid;		/* if it's a user not a group, the group ID (only for setid) */
54 	char*	name;
55 	int	nmem;
56 	int*	mem;	/* member array, if nmem != 0 */
57 	User*	next;
58 };
59 
60 char	rootdir[MAXROOT] = ROOT;
61 
62 static	User*	uidmap[NID];
63 static	User*	gidmap[NID];
64 static	QLock	idl;
65 static	User*	name2user(User**, char*, User* (*get)(char*));
66 static	User*	id2user(User**, int, User* (*get)(int));
67 static	User*	newuid(int);
68 static	User*	newgid(int);
69 static	User*	newuname(char*);
70 static	User*	newgname(char*);
71 
72 static	Qid	fsqid(struct stat *);
73 static	void	fspath(Cname*, char*, char*);
74 static	int	fsdirconv(Chan*, char*, struct stat*, uchar*, int, int);
75 static	Cname*	fswalkpath(Cname*, char*, int);
76 static	char*	fslastelem(Cname*);
77 static	int ingroup(int id, int gid);
78 static	void	fsperm(Chan*, int);
79 static	long	fsdirread(Chan*, uchar*, int, vlong);
80 static	int	fsomode(int);
81 static	void	fsremove(Chan*);
82 
83 /*
84  * this crud is to compensate for invalid symbolic links;
85  * all you can do is delete them, and good riddance
86  */
87 static int
88 xstat(char *f, struct stat *sb)
89 {
90 	if(stat(f, sb) >= 0)
91 		return 0;
92 	/* could possibly generate ->name as rob once suggested */
93 	return lstat(f, sb);
94 }
95 
96 static void
97 fsfree(Chan *c)
98 {
99 	cnameclose(FS(c)->name);
100 	free(FS(c));
101 }
102 
103 Chan*
104 fsattach(char *spec)
105 {
106 	Chan *c;
107 	struct stat stbuf;
108 	static int devno;
109 	static Lock l;
110 
111 	if(!emptystr(spec) && strcmp(spec, "*") != 0)
112 		error(Ebadspec);
113 	if(stat(rootdir, &stbuf) < 0)
114 		oserror();
115 
116 	c = devattach('U', spec);
117 	c->qid = fsqid(&stbuf);
118 	c->aux = smalloc(sizeof(Fsinfo));
119 	FS(c)->dir = nil;
120 	FS(c)->de = nil;
121 	FS(c)->fd = -1;
122 	FS(c)->gid = stbuf.st_gid;
123 	FS(c)->uid = stbuf.st_uid;
124 	FS(c)->mode = stbuf.st_mode;
125 	lock(&l);
126 	c->dev = devno++;
127 	unlock(&l);
128 	if (!emptystr(spec)){
129 		FS(c)->spec = "/";
130 		FS(c)->name = newcname(FS(c)->spec);
131 	}else
132 		FS(c)->name = newcname(rootdir);
133 	FS(c)->rootqid = c->qid;
134 
135 	return c;
136 }
137 
138 Walkqid*
139 fswalk(Chan *c, Chan *nc, char **name, int nname)
140 {
141 	int j;
142 	volatile int alloc;
143 	Walkqid *wq;
144 	struct stat stbuf;
145 	char *n;
146 	Cname *next;
147 	Cname *volatile current;
148 	Qid rootqid;
149 
150 	if(nname > 0)
151 		isdir(c);
152 
153 	alloc = 0;
154 	current = nil;
155 	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
156 	if(waserror()){
157 		if(alloc && wq->clone != nil)
158 			cclose(wq->clone);
159 		cnameclose(current);
160 		free(wq);
161 		return nil;
162 	}
163 	if(nc == nil){
164 		nc = devclone(c);
165 		nc->type = 0;
166 		alloc = 1;
167 	}
168 	wq->clone = nc;
169 	rootqid = FS(c)->rootqid;
170 	current = FS(c)->name;
171 	if(current != nil)
172 		incref(&current->r);
173 	for(j = 0; j < nname; j++){
174 		if(!(nc->qid.type&QTDIR)){
175 			if(j==0)
176 				error(Enotdir);
177 			break;
178 		}
179 		n = name[j];
180 		if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){
181 			next = current;
182 			incref(&next->r);
183 			next = addelem(current, n);
184 			//print("** ufs walk '%s' -> %s [%s]\n", current->s, n, next->s);
185 			if(xstat(next->s, &stbuf) < 0){
186 				cnameclose(next);
187 				if(j == 0)
188 					error(Enonexist);
189 				strcpy(up->env->errstr, Enonexist);
190 				break;
191 			}
192 			nc->qid = fsqid(&stbuf);
193 			cnameclose(current);
194 			current = next;
195 		}
196 		wq->qid[wq->nqid++] = nc->qid;
197 	}
198 	poperror();
199 	if(wq->nqid < nname){
200 		cnameclose(current);
201 		if(alloc)
202 			cclose(wq->clone);
203 		wq->clone = nil;
204 	}else if(wq->clone){
205 		nc->aux = smalloc(sizeof(Fsinfo));
206 		nc->type = c->type;
207 		if (nname) {
208 			FS(nc)->gid = stbuf.st_gid;
209 			FS(nc)->uid = stbuf.st_uid;
210 			FS(nc)->mode = stbuf.st_mode;
211 		} else {
212 			FS(nc)->gid = FS(c)->gid;
213 			FS(nc)->uid = FS(c)->uid;
214 			FS(nc)->mode = FS(c)->mode;
215 		}
216 		FS(nc)->name = current;
217 		FS(nc)->spec = FS(c)->spec;
218 		FS(nc)->rootqid = rootqid;
219 		FS(nc)->fd = -1;
220 		FS(nc)->dir = nil;
221 		FS(nc)->de = nil;
222 	}
223 	return wq;
224 }
225 
226 static int
227 fsstat(Chan *c, uchar *dp, int n)
228 {
229 	struct stat stbuf;
230 	char *p;
231 
232 	if(xstat(FS(c)->name->s, &stbuf) < 0)
233 		oserror();
234 	p = fslastelem(FS(c)->name);
235 	if(*p == 0)
236 		p = "/";
237 	qlock(&idl);
238 	n = fsdirconv(c, p, &stbuf, dp, n, 0);
239 	qunlock(&idl);
240 	return n;
241 }
242 
243 static Chan*
244 fsopen(Chan *c, int mode)
245 {
246 	int m, isdir;
247 
248 	m = mode & (OTRUNC|3);
249 	switch(m) {
250 	case 0:
251 		fsperm(c, 4);
252 		break;
253 	case 1:
254 	case 1|16:
255 		fsperm(c, 2);
256 		break;
257 	case 2:
258 	case 0|16:
259 	case 2|16:
260 		fsperm(c, 4);
261 		fsperm(c, 2);
262 		break;
263 	case 3:
264 		fsperm(c, 1);
265 		break;
266 	default:
267 		error(Ebadarg);
268 	}
269 
270 	isdir = c->qid.type & QTDIR;
271 
272 	if(isdir && mode != OREAD)
273 		error(Eperm);
274 
275 	m = fsomode(m & 3);
276 	c->mode = openmode(mode);
277 
278 	if(isdir) {
279 		FS(c)->dir = opendir(FS(c)->name->s);
280 		if(FS(c)->dir == 0)
281 			oserror();
282 		FS(c)->eod = 0;
283 	}
284 	else {
285 		if(mode & OTRUNC)
286 			m |= O_TRUNC;
287 		FS(c)->fd = open(FS(c)->name->s, m, 0666);
288 		if(FS(c)->fd < 0)
289 			oserror();
290 	}
291 
292 	c->offset = 0;
293 	FS(c)->offset = 0;
294 	c->flag |= COPEN;
295 	return c;
296 }
297 
298 static void
299 fscreate(Chan *c, char *name, int mode, ulong perm)
300 {
301 	int fd, m, o;
302 	struct stat stbuf;
303 	Cname *n;
304 
305 	fsperm(c, 2);
306 
307 	m = fsomode(mode&3);
308 	openmode(mode);	/* get the errors out of the way */
309 
310 	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
311 		error(Efilename);
312 	n = fswalkpath(FS(c)->name, name, 1);
313 	if(waserror()){
314 		cnameclose(n);
315 		nexterror();
316 	}
317 	if(perm & DMDIR) {
318 		if(m)
319 			error(Eperm);
320 
321 		perm &= ~0777 | (FS(c)->mode & 0777);
322 		if(mkdir(n->s, perm) < 0)
323 			oserror();
324 
325 		fd = open(n->s, 0);
326 		if(fd < 0)
327 			oserror();
328 		fchmod(fd, perm);
329 		fchown(fd, up->env->uid, FS(c)->gid);
330 		if(fstat(fd, &stbuf) <0){
331 			close(fd);
332 			oserror();
333 		}
334 		close(fd);
335 		FS(c)->dir = opendir(n->s);
336 		if(FS(c)->dir == nil)
337 			oserror();
338 		FS(c)->eod = 0;
339 	} else {
340 		o = (O_CREAT | O_EXCL) | (mode&3);
341 		if(mode & OTRUNC)
342 			o |= O_TRUNC;
343 		perm &= ~0666 | (FS(c)->mode & 0666);
344 		fd = open(n->s, o, perm);
345 		if(fd < 0)
346 			oserror();
347 		fchmod(fd, perm);
348 		fchown(fd, up->env->uid, FS(c)->gid);
349 		if(fstat(fd, &stbuf) < 0){
350 			close(fd);
351 			oserror();
352 		}
353 		FS(c)->fd = fd;
354 	}
355 	cnameclose(FS(c)->name);
356 	FS(c)->name = n;
357 	poperror();
358 
359 	c->qid = fsqid(&stbuf);
360 	FS(c)->gid = stbuf.st_gid;
361 	FS(c)->uid = stbuf.st_uid;
362 	FS(c)->mode = stbuf.st_mode;
363 	c->mode = openmode(mode);
364 	c->offset = 0;
365 	FS(c)->offset = 0;
366 	c->flag |= COPEN;
367 }
368 
369 static void
370 fsclose(Chan *c)
371 {
372 	if((c->flag & COPEN) != 0){
373 		if(c->qid.type & QTDIR)
374 			closedir(FS(c)->dir);
375 		else
376 			close(FS(c)->fd);
377 	}
378 	if(c->flag & CRCLOSE) {
379 		if(!waserror()) {
380 			fsremove(c);
381 			poperror();
382 		}
383 		return;
384 	}
385 	fsfree(c);
386 }
387 
388 static long
389 fsread(Chan *c, void *va, long n, vlong offset)
390 {
391 	long r;
392 
393 	if(c->qid.type & QTDIR){
394 		qlock(&FS(c)->oq);
395 		if(waserror()) {
396 			qunlock(&FS(c)->oq);
397 			nexterror();
398 		}
399 		r = fsdirread(c, va, n, offset);
400 		poperror();
401 		qunlock(&FS(c)->oq);
402 	}else{
403 		r = pread(FS(c)->fd, va, n, offset);
404 		if(r < 0){
405 			if(errno == ESPIPE || errno == EPIPE){
406 				r = read(FS(c)->fd, va, n);
407 				if(r >= 0)
408 					return r;
409 			}
410 			oserror();
411 		}
412 	}
413 	return r;
414 }
415 
416 static long
417 fswrite(Chan *c, void *va, long n, vlong offset)
418 {
419 	long r;
420 
421 	r = pwrite(FS(c)->fd, va, n, offset);
422 	if(r < 0 && (errno == ESPIPE || errno == EPIPE)){
423 		r = write(FS(c)->fd, va, n);
424 		if(r < 0)
425 			oserror();
426 	}
427 	return r;
428 }
429 
430 static void
431 fswchk(Cname *c)
432 {
433 	struct stat stbuf;
434 
435 	if(stat(c->s, &stbuf) < 0)
436 		oserror();
437 
438 	if(stbuf.st_uid == up->env->uid)
439 		stbuf.st_mode >>= 6;
440 	else
441 	if(stbuf.st_gid == up->env->gid || ingroup(up->env->uid, stbuf.st_gid))
442 		stbuf.st_mode >>= 3;
443 
444 	if(stbuf.st_mode & S_IWOTH)
445 		return;
446 
447 	error(Eperm);
448 }
449 
450 static void
451 fsremove(Chan *c)
452 {
453 	int n;
454 	volatile struct { Cname *dir; } dir;
455 
456 	dir.dir = fswalkpath(FS(c)->name, "..", 1);
457 	if(waserror()){
458 		if(dir.dir != nil)
459 			cnameclose(dir.dir);
460 		fsfree(c);
461 		nexterror();
462 	}
463 	fswchk(dir.dir);
464 	cnameclose(dir.dir);
465 	dir.dir = nil;
466 	if(c->qid.type & QTDIR)
467 		n = rmdir(FS(c)->name->s);
468 	else
469 		n = remove(FS(c)->name->s);
470 	if(n < 0)
471 		oserror();
472 	poperror();
473 	fsfree(c);
474 }
475 
476 static int
477 fswstat(Chan *c, uchar *buf, int nb)
478 {
479 	Dir *d;
480 	User *p;
481 	volatile struct { Cname *ph; } ph;
482 	struct stat stbuf;
483 	struct utimbuf utbuf;
484 	int tsync;
485 
486 	if(FS(c)->fd >= 0){
487 		if(fstat(FS(c)->fd, &stbuf) < 0)
488 			oserror();
489 	}else{
490 		if(stat(FS(c)->name->s, &stbuf) < 0)
491 			oserror();
492 	}
493 	d = malloc(sizeof(*d)+nb);
494 	if(d == nil)
495 		error(Enomem);
496 	if(waserror()){
497 		free(d);
498 		nexterror();
499 	}
500 	tsync = 1;
501 	nb = convM2D(buf, nb, d, (char*)&d[1]);
502 	if(nb == 0)
503 		error(Eshortstat);
504 	if(!emptystr(d->name) && strcmp(d->name, fslastelem(FS(c)->name)) != 0) {
505 		tsync = 0;
506 		validname(d->name, 0);
507 		ph.ph = fswalkpath(FS(c)->name, "..", 1);
508 		if(waserror()){
509 			cnameclose(ph.ph);
510 			nexterror();
511 		}
512 		fswchk(ph.ph);
513 		ph.ph = fswalkpath(ph.ph, d->name, 0);
514 		if(rename(FS(c)->name->s, ph.ph->s) < 0)
515 			oserror();
516 		cnameclose(FS(c)->name);
517 		poperror();
518 		FS(c)->name = ph.ph;
519 	}
520 
521 	if(d->mode != ~0 && (d->mode&0777) != (stbuf.st_mode&0777)) {
522 		tsync = 0;
523 		if(up->env->uid != stbuf.st_uid)
524 			error(Eowner);
525 		if(FS(c)->fd >= 0){
526 			if(fchmod(FS(c)->fd, d->mode&0777) < 0)
527 				oserror();
528 		}else{
529 			if(chmod(FS(c)->name->s, d->mode&0777) < 0)
530 				oserror();
531 		}
532 		FS(c)->mode &= ~0777;
533 		FS(c)->mode |= d->mode&0777;
534 	}
535 
536 	if(d->atime != ~0 && d->atime != stbuf.st_atime
537 		|| d->mtime != ~0 && d->mtime != stbuf.st_mtime) {
538 		tsync = 0;
539 		if(up->env->uid != stbuf.st_uid)
540 			error(Eowner);
541 		if(d->mtime != ~0)
542 			utbuf.modtime = d->mtime;
543 		else
544 			utbuf.modtime = stbuf.st_mtime;
545 		if(d->atime != ~0)
546 			utbuf.actime  = d->atime;
547 		else
548 			utbuf.actime = stbuf.st_atime;
549 		if(utime(FS(c)->name->s, &utbuf) < 0)	/* TO DO: futimes isn't portable */
550 			oserror();
551 	}
552 
553 	if(*d->gid){
554 		tsync = 0;
555 		qlock(&idl);
556 		if(waserror()){
557 			qunlock(&idl);
558 			nexterror();
559 		}
560 		p = name2user(gidmap, d->gid, newgname);
561 		if(p == 0)
562 			error(Eunknown);
563 		if(p->id != stbuf.st_gid) {
564 			if(up->env->uid != stbuf.st_uid)
565 				error(Eowner);
566 			if(FS(c)->fd >= 0){
567 				if(fchown(FS(c)->fd, stbuf.st_uid, p->id) < 0)
568 					oserror();
569 			}else{
570 				if(chown(FS(c)->name->s, stbuf.st_uid, p->id) < 0)
571 					oserror();
572 			}
573 			FS(c)->gid = p->id;
574 		}
575 		poperror();
576 		qunlock(&idl);
577 	}
578 
579 	if(d->length != ~(uvlong)0){
580 		tsync = 0;
581 		if(FS(c)->fd >= 0){
582 			fsperm(c, 2);
583 			if(ftruncate(FS(c)->fd, d->length) < 0)
584 				oserror();
585 		}else{
586 			fswchk(FS(c)->name);
587 			if(truncate(FS(c)->name->s, d->length) < 0)
588 				oserror();
589 		}
590 	}
591 
592 	poperror();
593 	free(d);
594 	if(tsync && FS(c)->fd >= 0 && fsync(FS(c)->fd) < 0)
595 		oserror();
596 	return nb;
597 }
598 
599 #define	QDEVBITS 4	/* 16 devices should be plenty */
600 #define MAXDEV (1<<QDEVBITS)
601 #define	QDEVSHIFT	(64-QDEVBITS)
602 #define	QINOMASK	(((uvlong)1<<QDEVSHIFT)-1)
603 #define QPATH(d,i)      (((uvlong)(d)<<QDEVSHIFT)|((uvlong)(i)&QINOMASK))
604 
605 static Qid
606 fsqid(struct stat *st)
607 {
608 	Qid q;
609 	ulong dev;
610 	int idev;
611 	static int nqdev = 0;
612 	static ulong qdev[MAXDEV];
613 	static Lock l;
614 
615 	q.type = QTFILE;
616 	if(S_ISDIR(st->st_mode))
617 		q.type = QTDIR;
618 
619 	dev = st->st_dev;
620 	lock(&l);
621 	for(idev = 0; idev < nqdev; idev++)
622 		if(qdev[idev] == dev)
623 			break;
624 	if(idev == nqdev) {
625 		if(nqdev == MAXDEV) {
626 			unlock(&l);
627 			error("too many devices");
628 		}
629 		qdev[nqdev++] = dev;
630 	}
631 	unlock(&l);
632 
633 	if(0)	/* we'll just let it be masked off */
634 	if((uvlong)st->st_ino & ~QINOMASK)
635 		error("inode number too large");
636 
637 	q.path = QPATH(idev, st->st_ino);
638 	q.vers = st->st_mtime;
639 
640 	return q;
641 }
642 
643 static void
644 fspath(Cname *c, char *name, char *path)
645 {
646 	int n;
647 
648 	if(c->len+strlen(name) >= MAXPATH)
649 		panic("fspath: name too long");
650 	memmove(path, c->s, c->len);
651 	n = c->len;
652 	if(path[n-1] != '/')
653 		path[n++] = '/';
654 	strcpy(path+n, name);
655 	if(isdotdot(name))
656 		cleanname(path);
657 /*print("->%s\n", path);*/
658 }
659 
660 static Cname *
661 fswalkpath(Cname *c, char *name, int dup)
662 {
663 	if(dup)
664 		c = newcname(c->s);
665 	c = addelem(c, name);
666 	if(isdotdot(name))
667 		cleancname(c);
668 	return c;
669 }
670 
671 static char *
672 fslastelem(Cname *c)
673 {
674 	char *p;
675 
676 	p = c->s + c->len;
677 	while(p > c->s && p[-1] != '/')
678 		p--;
679 	return p;
680 }
681 
682 static void
683 fsperm(Chan *c, int mask)
684 {
685 	int m;
686 
687 	m = FS(c)->mode;
688 /*
689 	print("fsperm: %o %o uuid %d ugid %d cuid %d cgid %d\n",
690 		m, mask, up->env->uid, up->env->gid, FS(c)->uid, FS(c)->gid);
691 */
692 	if(FS(c)->uid == up->env->uid)
693 		m >>= 6;
694 	else
695 	if(FS(c)->gid == up->env->gid || ingroup(up->env->uid, FS(c)->gid))
696 		m >>= 3;
697 
698 	m &= mask;
699 	if(m == 0)
700 		error(Eperm);
701 }
702 
703 static int
704 isdots(char *name)
705 {
706 	return name[0] == '.' && (name[1] == '\0' || name[1] == '.' && name[2] == '\0');
707 }
708 
709 static int
710 fsdirconv(Chan *c, char *name, struct stat *s, uchar *va, int nb, int indir)
711 {
712 	Dir d;
713 	char uidbuf[NUMSIZE], gidbuf[NUMSIZE];
714 	User *u;
715 
716 	memset(&d, 0, sizeof(d));
717 	d.name = name;
718 	u = id2user(uidmap, s->st_uid, newuid);
719 	if(u == nil){
720 		snprint(uidbuf, sizeof(uidbuf), "#%lud", (long)s->st_uid);
721 		d.uid = uidbuf;
722 	}else
723 		d.uid = u->name;
724 	u = id2user(gidmap, s->st_gid, newgid);
725 	if(u == nil){
726 		snprint(gidbuf, sizeof(gidbuf), "#%lud", (long)s->st_gid);
727 		d.gid = gidbuf;
728 	}else
729 		d.gid = u->name;
730 	d.muid = "";
731 	d.qid = fsqid(s);
732 	d.mode = (d.qid.type<<24)|(s->st_mode&0777);
733 	d.atime = s->st_atime;
734 	d.mtime = s->st_mtime;
735 	d.length = s->st_size;
736 	if(d.mode&DMDIR)
737 		d.length = 0;
738 	d.type = 'U';
739 	d.dev = c->dev;
740 	if(indir && sizeD2M(&d) > nb)
741 		return -1;	/* directory reader needs to know it didn't fit */
742 	return convD2M(&d, va, nb);
743 }
744 
745 static long
746 fsdirread(Chan *c, uchar *va, int count, vlong offset)
747 {
748 	int i;
749 	long n, r;
750 	struct stat stbuf;
751 	char path[MAXPATH], *ep;
752 	struct dirent *de;
753 	static char slop[8192];
754 
755 	i = 0;
756 	fspath(FS(c)->name, "", path);
757 	ep = path+strlen(path);
758 	if(FS(c)->offset != offset) {
759 		seekdir(FS(c)->dir, 0);
760 		FS(c)->de = nil;
761 		FS(c)->eod = 0;
762 		for(n=0; n<offset; ) {
763 			de = readdir(FS(c)->dir);
764 			if(de == 0) {
765 				/* EOF, so stash offset and return 0 */
766 				FS(c)->offset = n;
767 				FS(c)->eod = 1;
768 				return 0;
769 			}
770 			if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
771 				continue;
772 			strecpy(ep, path+sizeof(path), de->d_name);
773 			if(xstat(path, &stbuf) < 0) {
774 				fprint(2, "dir: bad path %s\n", path);
775 				continue;
776 			}
777 			qlock(&idl);
778 			r = fsdirconv(c, de->d_name, &stbuf, slop, sizeof(slop), 1);
779 			qunlock(&idl);
780 			if(r <= 0) {
781 				FS(c)->offset = n;
782 				return 0;
783 			}
784 			n += r;
785 		}
786 		FS(c)->offset = offset;
787 	}
788 
789 	if(FS(c)->eod)
790 		return 0;
791 
792 	/*
793 	 * Take idl on behalf of id2name.  Stalling attach, which is a
794 	 * rare operation, until the readdir completes is probably
795 	 * preferable to adding lock round-trips.
796 	 */
797 	qlock(&idl);
798 	while(i < count){
799 		de = FS(c)->de;
800 		FS(c)->de = nil;
801 		if(de == nil)
802 			de = readdir(FS(c)->dir);
803 		if(de == nil){
804 			FS(c)->eod = 1;
805 			break;
806 		}
807 
808 		if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
809 			continue;
810 
811 		strecpy(ep, path+sizeof(path), de->d_name);
812 		if(xstat(path, &stbuf) < 0) {
813 			fprint(2, "dir: bad path %s\n", path);
814 			continue;
815 		}
816 		r = fsdirconv(c, de->d_name, &stbuf, va+i, count-i, 1);
817 		if(r <= 0){
818 			FS(c)->de = de;
819 			break;
820 		}
821 		i += r;
822 		FS(c)->offset += r;
823 	}
824 	qunlock(&idl);
825 	return i;
826 }
827 
828 static int
829 fsomode(int m)
830 {
831 	if(m < 0 || m > 3)
832 		error(Ebadarg);
833 	return m == 3? 0: m;
834 }
835 
836 void
837 setid(char *name, int owner)
838 {
839 	User *u;
840 
841 	if(owner && !iseve())
842 		return;
843 	kstrdup(&up->env->user, name);
844 
845 	qlock(&idl);
846 	u = name2user(uidmap, name, newuname);
847 	if(u == nil){
848 		qunlock(&idl);
849 		up->env->uid = -1;
850 		up->env->gid = -1;
851 		return;
852 	}
853 
854 	up->env->uid = u->id;
855 	up->env->gid = u->gid;
856 	qunlock(&idl);
857 }
858 
859 static User**
860 hashuser(User** tab, int id)
861 {
862 	int i;
863 
864 	i = (id>>IDSHIFT) ^ id;
865 	return &tab[i & IDMASK];
866 }
867 
868 /*
869  * the caller of the following functions must hold QLock idl.
870  */
871 
872 /*
873  * we could keep separate maps of user and group names to Users to
874  * speed this up, but the reverse lookup currently isn't common (ie, change group by wstat and setid)
875  */
876 static User*
877 name2user(User **tab, char *name, User* (*get)(char*))
878 {
879 	int i;
880 	User *u, **h;
881 	static User *prevu;
882 	static User **prevtab;
883 
884 	if(prevu != nil && prevtab == tab && strcmp(name, prevu->name) == 0)
885 		return prevu;	/* it's often the one we've just seen */
886 
887 	for(i=0; i<NID; i++)
888 		for(u = tab[i]; u != nil; u = u->next)
889 			if(strcmp(name, u->name) == 0) {
890 				prevtab = tab;
891 				prevu = u;
892 				return u;
893 			}
894 
895 	u = get(name);
896 	if(u == nil)
897 		return nil;
898 	h = hashuser(tab, u->id);
899 	u->next = *h;
900 	*h = u;
901 	prevtab = tab;
902 	prevu = u;
903 	return u;
904 }
905 
906 static void
907 freeuser(User *u)
908 {
909 	if(u != nil){
910 		free(u->name);
911 		free(u->mem);
912 		free(u);
913 	}
914 }
915 
916 static User*
917 newuser(int id, int gid, char *name, int nmem)
918 {
919 	User *u;
920 
921 	u = malloc(sizeof(*u));
922 	if(u == nil)
923 		return nil;
924 	u->name = strdup(name);
925 	if(u->name == nil){
926 		free(u);
927 		return nil;
928 	}
929 	u->nmem = nmem;
930 	if(nmem){
931 		u->mem = malloc(nmem*sizeof(*u->mem));
932 		if(u->mem == nil){
933 			free(u->name);
934 			free(u);
935 			return nil;
936 		}
937 	}else
938 		u->mem = nil;
939 	u->id = id;
940 	u->gid = gid;
941 	u->next = nil;
942 	return u;
943 }
944 
945 static User*
946 newuname(char *name)
947 {
948 	struct passwd *p;
949 	User *u;
950 
951 	p = getpwnam(name);
952 	if(p == nil)
953 		return nil;
954 	return newuser(p->pw_uid, p->pw_gid, name, 0);
955 }
956 
957 static User*
958 newuid(int id)
959 {
960 	struct passwd *p;
961 	User *u;
962 
963 	p = getpwuid(id);
964 	if(p == nil)
965 		return nil;
966 	return newuser(p->pw_uid, p->pw_gid, p->pw_name, 0);
967 }
968 
969 static User*
970 newgroup(struct group *g)
971 {
972 	User *u, *gm;
973 	int n, o;
974 
975 	if(g == nil)
976 		return nil;
977 	for(n=0; g->gr_mem[n] != nil; n++)
978 		;
979 	u = newuser(g->gr_gid, g->gr_gid, g->gr_name, n);
980 	if(u == nil)
981 		return nil;
982 	o = 0;
983 	for(n=0; g->gr_mem[n] != nil; n++){
984 		gm = name2user(uidmap, g->gr_mem[n], newuname);
985 		if(gm != nil)
986 			u->mem[o++] = gm->id;
987 		/* ignore names that don't map to IDs */
988 	}
989 	u->nmem = o;
990 	return u;
991 }
992 
993 static User*
994 newgid(int id)
995 {
996 	return newgroup(getgrgid(id));
997 }
998 
999 static User*
1000 newgname(char *name)
1001 {
1002 	return newgroup(getgrnam(name));
1003 }
1004 
1005 static User*
1006 id2user(User **tab, int id, User* (*get)(int))
1007 {
1008 	int i;
1009 	User *u, **h;
1010 
1011 	h = hashuser(tab, id);
1012 	for(u = *h; u != nil; u = u->next)
1013 		if(u->id == id)
1014 			return u;
1015 	u = get(id);
1016 	if(u == nil)
1017 		return nil;
1018 	u->next = *h;
1019 	*h = u;
1020 	return u;
1021 }
1022 
1023 static int
1024 ingroup(int id, int gid)
1025 {
1026 	int i;
1027 	User *g;
1028 
1029 	g = id2user(gidmap, gid, newgid);
1030 	if(g == nil || g->mem == nil)
1031 		return 0;
1032 	for(i = 0; i < g->nmem; i++)
1033 		if(g->mem[i] == id)
1034 			return 1;
1035 	return 0;
1036 }
1037 
1038 Dev fsdevtab = {
1039 	'U',
1040 	"fs",
1041 
1042 	devinit,
1043 	fsattach,
1044 	fswalk,
1045 	fsstat,
1046 	fsopen,
1047 	fscreate,
1048 	fsclose,
1049 	fsread,
1050 	devbread,
1051 	fswrite,
1052 	devbwrite,
1053 	fsremove,
1054 	fswstat
1055 };
1056