xref: /inferno-os/emu/port/devfs-posix.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
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 && (errno == ESPIPE || errno == EPIPE)){
405 			r = read(FS(c)->fd, va, n);
406 			if(r < 0)
407 				oserror();
408 		}
409 	}
410 	return r;
411 }
412 
413 static long
414 fswrite(Chan *c, void *va, long n, vlong offset)
415 {
416 	long r;
417 
418 	r = pwrite(FS(c)->fd, va, n, offset);
419 	if(r < 0 && (errno == ESPIPE || errno == EPIPE)){
420 		r = write(FS(c)->fd, va, n);
421 		if(r < 0)
422 			oserror();
423 	}
424 	return r;
425 }
426 
427 static void
428 fswchk(Cname *c)
429 {
430 	struct stat stbuf;
431 
432 	if(stat(c->s, &stbuf) < 0)
433 		oserror();
434 
435 	if(stbuf.st_uid == up->env->uid)
436 		stbuf.st_mode >>= 6;
437 	else
438 	if(stbuf.st_gid == up->env->gid || ingroup(up->env->uid, stbuf.st_gid))
439 		stbuf.st_mode >>= 3;
440 
441 	if(stbuf.st_mode & S_IWOTH)
442 		return;
443 
444 	error(Eperm);
445 }
446 
447 static void
448 fsremove(Chan *c)
449 {
450 	int n;
451 	volatile struct { Cname *dir; } dir;
452 
453 	dir.dir = fswalkpath(FS(c)->name, "..", 1);
454 	if(waserror()){
455 		if(dir.dir != nil)
456 			cnameclose(dir.dir);
457 		fsfree(c);
458 		nexterror();
459 	}
460 	fswchk(dir.dir);
461 	cnameclose(dir.dir);
462 	dir.dir = nil;
463 	if(c->qid.type & QTDIR)
464 		n = rmdir(FS(c)->name->s);
465 	else
466 		n = remove(FS(c)->name->s);
467 	if(n < 0)
468 		oserror();
469 	poperror();
470 	fsfree(c);
471 }
472 
473 static int
474 fswstat(Chan *c, uchar *buf, int nb)
475 {
476 	Dir *d;
477 	User *p;
478 	volatile struct { Cname *ph; } ph;
479 	struct stat stbuf;
480 	struct utimbuf utbuf;
481 	int tsync;
482 
483 	if(FS(c)->fd >= 0){
484 		if(fstat(FS(c)->fd, &stbuf) < 0)
485 			oserror();
486 	}else{
487 		if(stat(FS(c)->name->s, &stbuf) < 0)
488 			oserror();
489 	}
490 	d = malloc(sizeof(*d)+nb);
491 	if(d == nil)
492 		error(Enomem);
493 	if(waserror()){
494 		free(d);
495 		nexterror();
496 	}
497 	tsync = 1;
498 	nb = convM2D(buf, nb, d, (char*)&d[1]);
499 	if(nb == 0)
500 		error(Eshortstat);
501 	if(!emptystr(d->name) && strcmp(d->name, fslastelem(FS(c)->name)) != 0) {
502 		tsync = 0;
503 		validname(d->name, 0);
504 		ph.ph = fswalkpath(FS(c)->name, "..", 1);
505 		if(waserror()){
506 			cnameclose(ph.ph);
507 			nexterror();
508 		}
509 		fswchk(ph.ph);
510 		ph.ph = fswalkpath(ph.ph, d->name, 0);
511 		if(rename(FS(c)->name->s, ph.ph->s) < 0)
512 			oserror();
513 		cnameclose(FS(c)->name);
514 		poperror();
515 		FS(c)->name = ph.ph;
516 	}
517 
518 	if(d->mode != ~0 && (d->mode&0777) != (stbuf.st_mode&0777)) {
519 		tsync = 0;
520 		if(up->env->uid != stbuf.st_uid)
521 			error(Eowner);
522 		if(FS(c)->fd >= 0){
523 			if(fchmod(FS(c)->fd, d->mode&0777) < 0)
524 				oserror();
525 		}else{
526 			if(chmod(FS(c)->name->s, d->mode&0777) < 0)
527 				oserror();
528 		}
529 		FS(c)->mode &= ~0777;
530 		FS(c)->mode |= d->mode&0777;
531 	}
532 
533 	if(d->atime != ~0 && d->atime != stbuf.st_atime
534 		|| d->mtime != ~0 && d->mtime != stbuf.st_mtime) {
535 		tsync = 0;
536 		if(up->env->uid != stbuf.st_uid)
537 			error(Eowner);
538 		if(d->mtime != ~0)
539 			utbuf.modtime = d->mtime;
540 		else
541 			utbuf.modtime = stbuf.st_mtime;
542 		if(d->atime != ~0)
543 			utbuf.actime  = d->atime;
544 		else
545 			utbuf.actime = stbuf.st_atime;
546 		if(utime(FS(c)->name->s, &utbuf) < 0)	/* TO DO: futimes isn't portable */
547 			oserror();
548 	}
549 
550 	if(*d->gid){
551 		tsync = 0;
552 		qlock(&idl);
553 		if(waserror()){
554 			qunlock(&idl);
555 			nexterror();
556 		}
557 		p = name2user(gidmap, d->gid, newgname);
558 		if(p == 0)
559 			error(Eunknown);
560 		if(p->id != stbuf.st_gid) {
561 			if(up->env->uid != stbuf.st_uid)
562 				error(Eowner);
563 			if(FS(c)->fd >= 0){
564 				if(fchown(FS(c)->fd, stbuf.st_uid, p->id) < 0)
565 					oserror();
566 			}else{
567 				if(chown(FS(c)->name->s, stbuf.st_uid, p->id) < 0)
568 					oserror();
569 			}
570 			FS(c)->gid = p->id;
571 		}
572 		poperror();
573 		qunlock(&idl);
574 	}
575 
576 	if(d->length != ~(uvlong)0){
577 		tsync = 0;
578 		if(FS(c)->fd >= 0){
579 			fsperm(c, 2);
580 			if(ftruncate(FS(c)->fd, d->length) < 0)
581 				oserror();
582 		}else{
583 			fswchk(FS(c)->name);
584 			if(truncate(FS(c)->name->s, d->length) < 0)
585 				oserror();
586 		}
587 	}
588 
589 	poperror();
590 	free(d);
591 	if(tsync && FS(c)->fd >= 0 && fsync(FS(c)->fd) < 0)
592 		oserror();
593 	return nb;
594 }
595 
596 #define	QDEVBITS 4	/* 16 devices should be plenty */
597 #define MAXDEV (1<<QDEVBITS)
598 #define	QDEVSHIFT	(64-QDEVBITS)
599 #define	QINOMASK	(((uvlong)1<<QDEVSHIFT)-1)
600 #define QPATH(d,i)      (((uvlong)(d)<<QDEVSHIFT)|((uvlong)(i)&QINOMASK))
601 
602 static Qid
603 fsqid(struct stat *st)
604 {
605 	Qid q;
606 	ulong dev;
607 	int idev;
608 	static int nqdev = 0;
609 	static ulong qdev[MAXDEV];
610 	static Lock l;
611 
612 	q.type = QTFILE;
613 	if(S_ISDIR(st->st_mode))
614 		q.type = QTDIR;
615 
616 	dev = st->st_dev;
617 	lock(&l);
618 	for(idev = 0; idev < nqdev; idev++)
619 		if(qdev[idev] == dev)
620 			break;
621 	if(idev == nqdev) {
622 		if(nqdev == MAXDEV) {
623 			unlock(&l);
624 			error("too many devices");
625 		}
626 		qdev[nqdev++] = dev;
627 	}
628 	unlock(&l);
629 
630 	if(0)	/* we'll just let it be masked off */
631 	if((uvlong)st->st_ino & ~QINOMASK)
632 		error("inode number too large");
633 
634 	q.path = QPATH(idev, st->st_ino);
635 	q.vers = st->st_mtime;
636 
637 	return q;
638 }
639 
640 static void
641 fspath(Cname *c, char *name, char *path)
642 {
643 	int n;
644 
645 	if(c->len+strlen(name) >= MAXPATH)
646 		panic("fspath: name too long");
647 	memmove(path, c->s, c->len);
648 	n = c->len;
649 	if(path[n-1] != '/')
650 		path[n++] = '/';
651 	strcpy(path+n, name);
652 	if(isdotdot(name))
653 		cleanname(path);
654 /*print("->%s\n", path);*/
655 }
656 
657 static Cname *
658 fswalkpath(Cname *c, char *name, int dup)
659 {
660 	if(dup)
661 		c = newcname(c->s);
662 	c = addelem(c, name);
663 	if(isdotdot(name))
664 		cleancname(c);
665 	return c;
666 }
667 
668 static char *
669 fslastelem(Cname *c)
670 {
671 	char *p;
672 
673 	p = c->s + c->len;
674 	while(p > c->s && p[-1] != '/')
675 		p--;
676 	return p;
677 }
678 
679 static void
680 fsperm(Chan *c, int mask)
681 {
682 	int m;
683 
684 	m = FS(c)->mode;
685 /*
686 	print("fsperm: %o %o uuid %d ugid %d cuid %d cgid %d\n",
687 		m, mask, up->env->uid, up->env->gid, FS(c)->uid, FS(c)->gid);
688 */
689 	if(FS(c)->uid == up->env->uid)
690 		m >>= 6;
691 	else
692 	if(FS(c)->gid == up->env->gid || ingroup(up->env->uid, FS(c)->gid))
693 		m >>= 3;
694 
695 	m &= mask;
696 	if(m == 0)
697 		error(Eperm);
698 }
699 
700 static int
701 isdots(char *name)
702 {
703 	return name[0] == '.' && (name[1] == '\0' || name[1] == '.' && name[2] == '\0');
704 }
705 
706 static int
707 fsdirconv(Chan *c, char *name, struct stat *s, uchar *va, int nb, int indir)
708 {
709 	Dir d;
710 	char uidbuf[NUMSIZE], gidbuf[NUMSIZE];
711 	User *u;
712 
713 	memset(&d, 0, sizeof(d));
714 	d.name = name;
715 	u = id2user(uidmap, s->st_uid, newuid);
716 	if(u == nil){
717 		snprint(uidbuf, sizeof(uidbuf), "#%lud", (long)s->st_uid);
718 		d.uid = uidbuf;
719 	}else
720 		d.uid = u->name;
721 	u = id2user(gidmap, s->st_gid, newgid);
722 	if(u == nil){
723 		snprint(gidbuf, sizeof(gidbuf), "#%lud", (long)s->st_gid);
724 		d.gid = gidbuf;
725 	}else
726 		d.gid = u->name;
727 	d.muid = "";
728 	d.qid = fsqid(s);
729 	d.mode = (d.qid.type<<24)|(s->st_mode&0777);
730 	d.atime = s->st_atime;
731 	d.mtime = s->st_mtime;
732 	d.length = s->st_size;
733 	if(d.mode&DMDIR)
734 		d.length = 0;
735 	d.type = 'U';
736 	d.dev = c->dev;
737 	if(indir && sizeD2M(&d) > nb)
738 		return -1;	/* directory reader needs to know it didn't fit */
739 	return convD2M(&d, va, nb);
740 }
741 
742 static long
743 fsdirread(Chan *c, uchar *va, int count, vlong offset)
744 {
745 	int i;
746 	long n, r;
747 	struct stat stbuf;
748 	char path[MAXPATH], *ep;
749 	struct dirent *de;
750 	static char slop[8192];
751 
752 	i = 0;
753 	fspath(FS(c)->name, "", path);
754 	ep = path+strlen(path);
755 	if(FS(c)->offset != offset) {
756 		seekdir(FS(c)->dir, 0);
757 		FS(c)->de = nil;
758 		FS(c)->eod = 0;
759 		for(n=0; n<offset; ) {
760 			de = readdir(FS(c)->dir);
761 			if(de == 0) {
762 				/* EOF, so stash offset and return 0 */
763 				FS(c)->offset = n;
764 				FS(c)->eod = 1;
765 				return 0;
766 			}
767 			if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
768 				continue;
769 			strecpy(ep, path+sizeof(path), de->d_name);
770 			if(xstat(path, &stbuf) < 0) {
771 				fprint(2, "dir: bad path %s\n", path);
772 				continue;
773 			}
774 			qlock(&idl);
775 			r = fsdirconv(c, de->d_name, &stbuf, slop, sizeof(slop), 1);
776 			qunlock(&idl);
777 			if(r <= 0) {
778 				FS(c)->offset = n;
779 				return 0;
780 			}
781 			n += r;
782 		}
783 		FS(c)->offset = offset;
784 	}
785 
786 	if(FS(c)->eod)
787 		return 0;
788 
789 	/*
790 	 * Take idl on behalf of id2name.  Stalling attach, which is a
791 	 * rare operation, until the readdir completes is probably
792 	 * preferable to adding lock round-trips.
793 	 */
794 	qlock(&idl);
795 	while(i < count){
796 		de = FS(c)->de;
797 		FS(c)->de = nil;
798 		if(de == nil)
799 			de = readdir(FS(c)->dir);
800 		if(de == nil){
801 			FS(c)->eod = 1;
802 			break;
803 		}
804 
805 		if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
806 			continue;
807 
808 		strecpy(ep, path+sizeof(path), de->d_name);
809 		if(xstat(path, &stbuf) < 0) {
810 			fprint(2, "dir: bad path %s\n", path);
811 			continue;
812 		}
813 		r = fsdirconv(c, de->d_name, &stbuf, va+i, count-i, 1);
814 		if(r <= 0){
815 			FS(c)->de = de;
816 			break;
817 		}
818 		i += r;
819 		FS(c)->offset += r;
820 	}
821 	qunlock(&idl);
822 	return i;
823 }
824 
825 static int
826 fsomode(int m)
827 {
828 	if(m < 0 || m > 3)
829 		error(Ebadarg);
830 	return m == 3? 0: m;
831 }
832 
833 void
834 setid(char *name, int owner)
835 {
836 	User *u;
837 
838 	if(owner && !iseve())
839 		return;
840 	kstrdup(&up->env->user, name);
841 
842 	qlock(&idl);
843 	u = name2user(uidmap, name, newuname);
844 	if(u == nil){
845 		qunlock(&idl);
846 		up->env->uid = -1;
847 		up->env->gid = -1;
848 		return;
849 	}
850 
851 	up->env->uid = u->id;
852 	up->env->gid = u->gid;
853 	qunlock(&idl);
854 }
855 
856 static User**
857 hashuser(User** tab, int id)
858 {
859 	int i;
860 
861 	i = (id>>IDSHIFT) ^ id;
862 	return &tab[i & IDMASK];
863 }
864 
865 /*
866  * the caller of the following functions must hold QLock idl.
867  */
868 
869 /*
870  * we could keep separate maps of user and group names to Users to
871  * speed this up, but the reverse lookup currently isn't common (ie, change group by wstat and setid)
872  */
873 static User*
874 name2user(User **tab, char *name, User* (*get)(char*))
875 {
876 	int i;
877 	User *u, **h;
878 	static User *prevu;
879 	static User **prevtab;
880 
881 	if(prevu != nil && prevtab == tab && strcmp(name, prevu->name) == 0)
882 		return prevu;	/* it's often the one we've just seen */
883 
884 	for(i=0; i<NID; i++)
885 		for(u = tab[i]; u != nil; u = u->next)
886 			if(strcmp(name, u->name) == 0) {
887 				prevtab = tab;
888 				prevu = u;
889 				return u;
890 			}
891 
892 	u = get(name);
893 	if(u == nil)
894 		return nil;
895 	h = hashuser(tab, u->id);
896 	u->next = *h;
897 	*h = u;
898 	prevtab = tab;
899 	prevu = u;
900 	return u;
901 }
902 
903 static void
904 freeuser(User *u)
905 {
906 	if(u != nil){
907 		free(u->name);
908 		free(u->mem);
909 		free(u);
910 	}
911 }
912 
913 static User*
914 newuser(int id, int gid, char *name, int nmem)
915 {
916 	User *u;
917 
918 	u = malloc(sizeof(*u));
919 	if(u == nil)
920 		return nil;
921 	u->name = strdup(name);
922 	if(u->name == nil){
923 		free(u);
924 		return nil;
925 	}
926 	u->nmem = nmem;
927 	if(nmem){
928 		u->mem = malloc(nmem*sizeof(*u->mem));
929 		if(u->mem == nil){
930 			free(u->name);
931 			free(u);
932 			return nil;
933 		}
934 	}else
935 		u->mem = nil;
936 	u->id = id;
937 	u->gid = gid;
938 	u->next = nil;
939 	return u;
940 }
941 
942 static User*
943 newuname(char *name)
944 {
945 	struct passwd *p;
946 	User *u;
947 
948 	p = getpwnam(name);
949 	if(p == nil)
950 		return nil;
951 	return newuser(p->pw_uid, p->pw_gid, name, 0);
952 }
953 
954 static User*
955 newuid(int id)
956 {
957 	struct passwd *p;
958 	User *u;
959 
960 	p = getpwuid(id);
961 	if(p == nil)
962 		return nil;
963 	return newuser(p->pw_uid, p->pw_gid, p->pw_name, 0);
964 }
965 
966 static User*
967 newgroup(struct group *g)
968 {
969 	User *u, *gm;
970 	int n, o;
971 
972 	if(g == nil)
973 		return nil;
974 	for(n=0; g->gr_mem[n] != nil; n++)
975 		;
976 	u = newuser(g->gr_gid, g->gr_gid, g->gr_name, n);
977 	if(u == nil)
978 		return nil;
979 	o = 0;
980 	for(n=0; g->gr_mem[n] != nil; n++){
981 		gm = name2user(uidmap, g->gr_mem[n], newuname);
982 		if(gm != nil)
983 			u->mem[o++] = gm->id;
984 		/* ignore names that don't map to IDs */
985 	}
986 	u->nmem = o;
987 	return u;
988 }
989 
990 static User*
991 newgid(int id)
992 {
993 	return newgroup(getgrgid(id));
994 }
995 
996 static User*
997 newgname(char *name)
998 {
999 	return newgroup(getgrnam(name));
1000 }
1001 
1002 static User*
1003 id2user(User **tab, int id, User* (*get)(int))
1004 {
1005 	int i;
1006 	User *u, **h;
1007 
1008 	h = hashuser(tab, id);
1009 	for(u = *h; u != nil; u = u->next)
1010 		if(u->id == id)
1011 			return u;
1012 	u = get(id);
1013 	if(u == nil)
1014 		return nil;
1015 	u->next = *h;
1016 	*h = u;
1017 	return u;
1018 }
1019 
1020 static int
1021 ingroup(int id, int gid)
1022 {
1023 	int i;
1024 	User *g;
1025 
1026 	g = id2user(gidmap, gid, newgid);
1027 	if(g == nil || g->mem == nil)
1028 		return 0;
1029 	for(i = 0; i < g->nmem; i++)
1030 		if(g->mem[i] == id)
1031 			return 1;
1032 	return 0;
1033 }
1034 
1035 Dev fsdevtab = {
1036 	'U',
1037 	"fs",
1038 
1039 	devinit,
1040 	fsattach,
1041 	fswalk,
1042 	fsstat,
1043 	fsopen,
1044 	fscreate,
1045 	fsclose,
1046 	fsread,
1047 	devbread,
1048 	fswrite,
1049 	devbwrite,
1050 	fsremove,
1051 	fswstat
1052 };
1053