/* * 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; } long dirpackage(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 */ long unionread(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; } void unionrewind(Chan *c) { qlock(&c->umqlock); c->uri = 0; if(c->umc){ cclose(c->umc); c->umc = nil; } qunlock(&c->umqlock); } static int dirfixed(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 long dirsetname(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 void mountrock(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. */ int mountrockread(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; } void mountrewind(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. */ long mountfix(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+BIT16SZmount; 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; }