411 lines · 7.3 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * directory reading * from /sys/src/libc/9sys/dirread.c */#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"../port/error.h"#include	"ureg.h" enum{	DIRSIZE	= STATFIXLEN + 16 * 4		/* enough for encoded stat buf + some reasonable strings */}; Dir*dirchstat(Chan *chan){	Dir *d;	uchar *buf;	int n, nd, i; 	nd = DIRSIZE;	for(i=0; i<2; i++){	/* should work by the second try */		d = malloc(sizeof(Dir) + BIT16SZ + nd);		if(d == nil)			return nil;		buf = (uchar*)&d[1];		n = devtab[chan->type]->stat(chan, buf, BIT16SZ + nd);		if(n < BIT16SZ){			free(d);			return nil;		}		nd = GBIT16((uchar*)buf);	/* upper bound on size of Dir + strings */		if(nd <= n){			convM2D(buf, n, d, (char*)&d[1]);			return d;		}		/* else sizeof(Dir)+BIT16SZ+nd is plenty */		free(d);	}	return nil;} longdirpackage(uchar *buf, long ts, Dir **d){	char *s;	long ss, i, n, nn, m; 	*d = nil;	if(ts <= 0)		return 0; 	/*	 * first find number of all stats, check they look like stats, & size all associated strings	 */	ss = 0;	n = 0;	for(i = 0; i < ts; i += m){		m = BIT16SZ + GBIT16(&buf[i]);		if(statcheck(&buf[i], m) < 0)			break;		ss += m;		n++;	} 	if(i != ts)		return -1; 	*d = malloc(n * sizeof(Dir) + ss);	if(*d == nil)		return -1; 	/*	 * then convert all buffers	 */	s = (char*)*d + n * sizeof(Dir);	nn = 0;	for(i = 0; i < ts; i += m){		m = BIT16SZ + GBIT16((uchar*)&buf[i]);		if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){			free(*d);			*d = nil;			return -1;		}		nn++;		s += m;	} 	return nn;} /* * directory reading, from sysfile.c */ longunionread(Chan *c, void *va, long n){	int i;	long nr;	Mhead *m;	Mount *mount; 	qlock(&c->umqlock);	m = c->umh;	rlock(&m->lock);	mount = m->mount;	/* bring mount in sync with c->uri and c->umc */	for(i = 0; mount != nil && i < c->uri; i++)		mount = mount->next; 	nr = 0;	while(mount != nil){		/* Error causes component of union to be skipped */		if(mount->to && !waserror()){			if(c->umc == nil){				c->umc = cclone(mount->to);				c->umc = devtab[c->umc->type]->open(c->umc, OREAD);			}				nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset);			c->umc->offset += nr;			poperror();		}		if(nr > 0)			break; 		/* Advance to next element */		c->uri++;		if(c->umc){			cclose(c->umc);			c->umc = nil;		}		mount = mount->next;	}	runlock(&m->lock);	qunlock(&c->umqlock);	return nr;} voidunionrewind(Chan *c){	qlock(&c->umqlock);	c->uri = 0;	if(c->umc){		cclose(c->umc);		c->umc = nil;	}	qunlock(&c->umqlock);} static intdirfixed(uchar *p, uchar *e, Dir *d){	int len; 	len = GBIT16(p)+BIT16SZ;	if(p + len > e)		return -1; 	p += BIT16SZ;	/* ignore size */	d->type = devno(GBIT16(p), 1);	p += BIT16SZ;	d->dev = GBIT32(p);	p += BIT32SZ;	d->qid.type = GBIT8(p);	p += BIT8SZ;	d->qid.vers = GBIT32(p);	p += BIT32SZ;	d->qid.path = GBIT64(p);	p += BIT64SZ;	d->mode = GBIT32(p);	p += BIT32SZ;	d->atime = GBIT32(p);	p += BIT32SZ;	d->mtime = GBIT32(p);	p += BIT32SZ;	d->length = GBIT64(p); 	return len;} static char*dirname(uchar *p, int *n){	p += BIT16SZ+BIT16SZ+BIT32SZ+BIT8SZ+BIT32SZ+BIT64SZ		+ BIT32SZ+BIT32SZ+BIT32SZ+BIT64SZ;	*n = GBIT16(p);	return (char*)p+BIT16SZ;} static longdirsetname(char *name, int len, uchar *p, long n, long maxn){	char *oname;	int olen;	long nn; 	if(n == BIT16SZ)		return BIT16SZ; 	oname = dirname(p, &olen); 	nn = n+len-olen;	PBIT16(p, nn-BIT16SZ);	if(nn > maxn)		return BIT16SZ; 	if(len != olen)		memmove(oname+len, oname+olen, p+n-(uchar*)(oname+olen));	PBIT16((uchar*)(oname-2), len);	memmove(oname, name, len);	return nn;} /* * Mountfix might have caused the fixed results of the directory read * to overflow the buffer.  Catch the overflow in c->dirrock. */static voidmountrock(Chan *c, uchar *p, uchar **pe){	uchar *e, *r;	int len, n; 	e = *pe; 	/* find last directory entry */	for(;;){		len = BIT16SZ+GBIT16(p);		if(p+len >= e)			break;		p += len;	} 	/* save it away */	qlock(&c->rockqlock);	if(c->nrock+len > c->mrock){		n = ROUND(c->nrock+len, 1024);		r = smalloc(n);		memmove(r, c->dirrock, c->nrock);		free(c->dirrock);		c->dirrock = r;		c->mrock = n;	}	memmove(c->dirrock+c->nrock, p, len);	c->nrock += len;	qunlock(&c->rockqlock); 	/* drop it */	*pe = p;} /* * Satisfy a directory read with the results saved in c->dirrock. */intmountrockread(Chan *c, uchar *op, long n, long *nn){	long dirlen;	uchar *rp, *erp, *ep, *p; 	/* common case */	if(c->nrock == 0)		return 0; 	/* copy out what we can */	qlock(&c->rockqlock);	rp = c->dirrock;	erp = rp+c->nrock;	p = op;	ep = p+n;	while(rp+BIT16SZ <= erp){		dirlen = BIT16SZ+GBIT16(rp);		if(p+dirlen > ep)			break;		memmove(p, rp, dirlen);		p += dirlen;		rp += dirlen;	} 	if(p == op){		qunlock(&c->rockqlock);		return 0;	} 	/* shift the rest */	if(rp != erp)		memmove(c->dirrock, rp, erp-rp);	c->nrock = erp - rp; 	*nn = p - op;	qunlock(&c->rockqlock);	return 1;} voidmountrewind(Chan *c){	c->nrock = 0;} /* * Rewrite the results of a directory read to reflect current  * name space bindings and mounts.  Specifically, replace * directory entries for bind and mount points with the results * of statting what is mounted there.  Except leave the old names. */longmountfix(Chan *c, uchar *op, long n, long maxn){	char *name;	int nbuf, nname;	Chan *nc;	Mhead *mh;	Mount *m;	uchar *p;	int dirlen, rest;	long l;	uchar *buf, *e;	Dir d; 	p = op;	buf = nil;	nbuf = 0;	for(e=&p[n]; p+BIT16SZ<e; p+=dirlen){		dirlen = dirfixed(p, e, &d);		if(dirlen < 0)			break;		nc = nil;		mh = nil;		if(findmount(&nc, &mh, d.type, d.dev, d.qid)){			/*			 * If it's a union directory and the original is			 * in the union, don't rewrite anything.			 */			for(m=mh->mount; m; m=m->next)				if(eqchantdqid(m->to, d.type, d.dev, d.qid, 1))					goto Norewrite; 			name = dirname(p, &nname);			/*			 * Do the stat but fix the name.  If it fails, leave old entry.			 * BUG: If it fails because there isn't room for the entry,			 * what can we do?  Nothing, really.  Might as well skip it.			 */			if(buf == nil){				buf = smalloc(4096);				nbuf = 4096;			}			if(waserror())				goto Norewrite;			l = devtab[nc->type]->stat(nc, buf, nbuf);			l = dirsetname(name, nname, buf, l, nbuf);			if(l == BIT16SZ)				error("dirsetname");			poperror(); 			/*			 * Shift data in buffer to accomodate new entry,			 * possibly overflowing into rock.			 */			rest = e - (p+dirlen);			if(l > dirlen){				while(p+l+rest > op+maxn){					mountrock(c, p, &e);					if(e == p){						dirlen = 0;						goto Norewrite;					}					rest = e - (p+dirlen);				}			}			if(l != dirlen){				memmove(p+l, p+dirlen, rest);				dirlen = l;				e = p+dirlen+rest;			} 			/*			 * Rewrite directory entry.			 */			memmove(p, buf, l); 		    Norewrite:			cclose(nc);			putmhead(mh);		}	}	if(buf)		free(buf); 	if(p != e)		error("oops in rockfix"); 	return e-op;}