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