xref: /inferno-os/emu/port/devfs-posix.c (revision 90ccc69f5be0b7e26c3607dac07340fe3e271509)
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 uchar 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 			if(waserror()){
779 				qunlock(&idl);
780 				nexterror();
781 			}
782 			r = fsdirconv(c, de->d_name, &stbuf, slop, sizeof(slop), 1);
783 			poperror();
784 			qunlock(&idl);
785 			if(r <= 0) {
786 				FS(c)->offset = n;
787 				return 0;
788 			}
789 			n += r;
790 		}
791 		FS(c)->offset = offset;
792 	}
793 
794 	if(FS(c)->eod)
795 		return 0;
796 
797 	/*
798 	 * Take idl on behalf of id2name.  Stalling attach, which is a
799 	 * rare operation, until the readdir completes is probably
800 	 * preferable to adding lock round-trips.
801 	 */
802 	qlock(&idl);
803 	while(i < count){
804 		de = FS(c)->de;
805 		FS(c)->de = nil;
806 		if(de == nil)
807 			de = readdir(FS(c)->dir);
808 		if(de == nil){
809 			FS(c)->eod = 1;
810 			break;
811 		}
812 
813 		if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
814 			continue;
815 
816 		strecpy(ep, path+sizeof(path), de->d_name);
817 		if(xstat(path, &stbuf) < 0) {
818 			fprint(2, "dir: bad path %s\n", path);
819 			continue;
820 		}
821 		r = fsdirconv(c, de->d_name, &stbuf, va+i, count-i, 1);
822 		if(r <= 0){
823 			FS(c)->de = de;
824 			break;
825 		}
826 		i += r;
827 		FS(c)->offset += r;
828 	}
829 	qunlock(&idl);
830 	return i;
831 }
832 
833 static int
834 fsomode(int m)
835 {
836 	if(m < 0 || m > 3)
837 		error(Ebadarg);
838 	return m == 3? 0: m;
839 }
840 
841 void
842 setid(char *name, int owner)
843 {
844 	User *u;
845 
846 	if(owner && !iseve())
847 		return;
848 	kstrdup(&up->env->user, name);
849 
850 	qlock(&idl);
851 	u = name2user(uidmap, name, newuname);
852 	if(u == nil){
853 		qunlock(&idl);
854 		up->env->uid = -1;
855 		up->env->gid = -1;
856 		return;
857 	}
858 
859 	up->env->uid = u->id;
860 	up->env->gid = u->gid;
861 	qunlock(&idl);
862 }
863 
864 static User**
865 hashuser(User** tab, int id)
866 {
867 	int i;
868 
869 	i = (id>>IDSHIFT) ^ id;
870 	return &tab[i & IDMASK];
871 }
872 
873 /*
874  * the caller of the following functions must hold QLock idl.
875  */
876 
877 /*
878  * we could keep separate maps of user and group names to Users to
879  * speed this up, but the reverse lookup currently isn't common (ie, change group by wstat and setid)
880  */
881 static User*
882 name2user(User **tab, char *name, User* (*get)(char*))
883 {
884 	int i;
885 	User *u, **h;
886 	static User *prevu;
887 	static User **prevtab;
888 
889 	if(prevu != nil && prevtab == tab && strcmp(name, prevu->name) == 0)
890 		return prevu;	/* it's often the one we've just seen */
891 
892 	for(i=0; i<NID; i++)
893 		for(u = tab[i]; u != nil; u = u->next)
894 			if(strcmp(name, u->name) == 0) {
895 				prevtab = tab;
896 				prevu = u;
897 				return u;
898 			}
899 
900 	u = get(name);
901 	if(u == nil)
902 		return nil;
903 	h = hashuser(tab, u->id);
904 	u->next = *h;
905 	*h = u;
906 	prevtab = tab;
907 	prevu = u;
908 	return u;
909 }
910 
911 static void
912 freeuser(User *u)
913 {
914 	if(u != nil){
915 		free(u->name);
916 		free(u->mem);
917 		free(u);
918 	}
919 }
920 
921 static User*
922 newuser(int id, int gid, char *name, int nmem)
923 {
924 	User *u;
925 
926 	u = malloc(sizeof(*u));
927 	if(u == nil)
928 		return nil;
929 	u->name = strdup(name);
930 	if(u->name == nil){
931 		free(u);
932 		return nil;
933 	}
934 	u->nmem = nmem;
935 	if(nmem){
936 		u->mem = malloc(nmem*sizeof(*u->mem));
937 		if(u->mem == nil){
938 			free(u->name);
939 			free(u);
940 			return nil;
941 		}
942 	}else
943 		u->mem = nil;
944 	u->id = id;
945 	u->gid = gid;
946 	u->next = nil;
947 	return u;
948 }
949 
950 static User*
951 newuname(char *name)
952 {
953 	struct passwd *p;
954 
955 	p = getpwnam(name);
956 	if(p == nil)
957 		return nil;
958 	return newuser(p->pw_uid, p->pw_gid, name, 0);
959 }
960 
961 static User*
962 newuid(int id)
963 {
964 	struct passwd *p;
965 
966 	p = getpwuid(id);
967 	if(p == nil)
968 		return nil;
969 	return newuser(p->pw_uid, p->pw_gid, p->pw_name, 0);
970 }
971 
972 static User*
973 newgroup(struct group *g)
974 {
975 	User *u, *gm;
976 	int n, o;
977 
978 	if(g == nil)
979 		return nil;
980 	for(n=0; g->gr_mem[n] != nil; n++)
981 		;
982 	u = newuser(g->gr_gid, g->gr_gid, g->gr_name, n);
983 	if(u == nil)
984 		return nil;
985 	o = 0;
986 	for(n=0; g->gr_mem[n] != nil; n++){
987 		gm = name2user(uidmap, g->gr_mem[n], newuname);
988 		if(gm != nil)
989 			u->mem[o++] = gm->id;
990 		/* ignore names that don't map to IDs */
991 	}
992 	u->nmem = o;
993 	return u;
994 }
995 
996 static User*
997 newgid(int id)
998 {
999 	return newgroup(getgrgid(id));
1000 }
1001 
1002 static User*
1003 newgname(char *name)
1004 {
1005 	return newgroup(getgrnam(name));
1006 }
1007 
1008 static User*
1009 id2user(User **tab, int id, User* (*get)(int))
1010 {
1011 	User *u, **h;
1012 
1013 	h = hashuser(tab, id);
1014 	for(u = *h; u != nil; u = u->next)
1015 		if(u->id == id)
1016 			return u;
1017 	u = get(id);
1018 	if(u == nil)
1019 		return nil;
1020 	u->next = *h;
1021 	*h = u;
1022 	return u;
1023 }
1024 
1025 static int
1026 ingroup(int id, int gid)
1027 {
1028 	int i;
1029 	User *g;
1030 
1031 	g = id2user(gidmap, gid, newgid);
1032 	if(g == nil || g->mem == nil)
1033 		return 0;
1034 	for(i = 0; i < g->nmem; i++)
1035 		if(g->mem[i] == id)
1036 			return 1;
1037 	return 0;
1038 }
1039 
1040 Dev fsdevtab = {
1041 	'U',
1042 	"fs",
1043 
1044 	devinit,
1045 	fsattach,
1046 	fswalk,
1047 	fsstat,
1048 	fsopen,
1049 	fscreate,
1050 	fsclose,
1051 	fsread,
1052 	devbread,
1053 	fswrite,
1054 	devbwrite,
1055 	fsremove,
1056 	fswstat
1057 };
1058