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