xref: /inferno-os/emu/port/devfs-posix.c (revision 965322b69e0bdf60e8bc7e3be6050d61687c3eb6)
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