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