ref: 7d7650dffc20c3853eb1acd551c9a02f73751ae3
dir: /sys/src/cmd/cwfs/9p1.c/
#include "all.h" #include "9p1.h" extern Nvrsafe nvr; typedef struct { uchar chal[CHALLEN]; /* locally generated challenge */ uchar rchal[CHALLEN]; /* remotely generated challenge */ Lock idlock; ulong idoffset; /* offset of id vector */ ulong idvec; /* vector of acceptable id's */ } Authinfo; static void f_nop(Chan *cp, Fcall*, Fcall*) { if(CHAT(cp)) fprint(2, "c_nop %d\n", cp->chan); } static void f_flush(Chan *cp, Fcall*, Fcall*) { if(CHAT(cp)) fprint(2, "c_flush %d\n", cp->chan); runlock(&cp->reflock); wlock(&cp->reflock); wunlock(&cp->reflock); rlock(&cp->reflock); } /* * create a challenge for a fid space */ static void mkchallenge(Authinfo *aip) { int i; srand((uintptr)aip + time(nil)); for(i = 0; i < CHALLEN; i++) aip->chal[i] = nrand(256); aip->idoffset = 0; aip->idvec = 0; } static void f_session(Chan *cp, Fcall *in, Fcall *ou) { Authinfo *aip; aip = (Authinfo*)cp->authinfo; if(CHAT(cp)) fprint(2, "c_session %d\n", cp->chan); memmove(aip->rchal, in->chal, sizeof(aip->rchal)); mkchallenge(aip); memmove(ou->chal, aip->chal, sizeof(ou->chal)); if(noauth) memset(ou->authid, 0, sizeof(ou->authid)); else memmove(ou->authid, nvr.authid, sizeof(ou->authid)); sprint(ou->authdom, "%s.%s", service, nvr.authdom); fileinit(cp); } /* * match a challenge from an attach */ static int authorize(Chan *cp, Fcall *in, Fcall *ou) { Ticket t; Authenticator a; int x; ulong bit; Authinfo *aip; if(noauth) return 1; if(strcmp(in->uname, "none") == 0) return !nonone || cp->authok; if(in->type == Toattach) return 0; /* decrypt and unpack ticket */ convM2T9p1(in->ticket, &t, nvr.machkey); if(t.num != AuthTs){ fprint(2, "9p1: bad AuthTs num\n"); return 0; } /* decrypt and unpack authenticator */ convM2A9p1(in->auth, &a, t.key); if(a.num != AuthAc){ fprint(2, "9p1: bad AuthAc num\n"); return 0; } /* challenges must match */ aip = (Authinfo*)cp->authinfo; if(memcmp(a.chal, aip->chal, sizeof(a.chal)) != 0){ fprint(2, "9p1: bad challenge\n"); return 0; } /* * the id must be in a valid range. the range is specified by a * lower bound (idoffset) and a bit vector (idvec) where a * bit set to 1 means unusable */ lock(&aip->idlock); x = a.id - aip->idoffset; bit = 1<<x; if(x < 0 || x > 31 || (bit&aip->idvec)){ unlock(&aip->idlock); fprint(2, "9p1: id out of range: idoff %ld idvec %lux id %ld\n", aip->idoffset, aip->idvec, a.id); return 0; } aip->idvec |= bit; /* normalize the vector */ while(aip->idvec&0xffff0001){ aip->idvec >>= 1; aip->idoffset++; } unlock(&aip->idlock); /* ticket name and attach name must match */ if(memcmp(in->uname, t.cuid, sizeof(in->uname)) != 0){ fprint(2, "9p1: names don't match\n"); return 0; } /* copy translated name into input record */ memmove(in->uname, t.suid, sizeof(in->uname)); /* craft a reply */ a.num = AuthAs; memmove(a.chal, aip->rchal, CHALLEN); convA2M9p1(&a, ou->rauth, t.key); cp->authok = 1; return 1; } /* * buggery to give false qid for * the top 2 levels of the dump fs */ void mkqid(Qid* qid, Dentry *d, int buggery) { int c; if(buggery && d->qid.path == (QPDIR|QPROOT)){ c = d->name[0]; if(isascii(c) && isdigit(c)){ qid->path = 3; qid->vers = d->qid.version; qid->type = QTDIR; c = (c-'0')*10 + (d->name[1]-'0'); if(c >= 1 && c <= 12) qid->path = 4; return; } } mkqid9p2(qid, &d->qid, d->mode); } int mkqidcmp(Qid* qid, Dentry *d) { Qid tmp; mkqid(&tmp, d, 1); if(qid->path == tmp.path && qid->type == tmp.type) return 0; return Eqid; } static void f_attach(Chan *cp, Fcall *in, Fcall *ou) { Iobuf *p; Dentry *d; File *f; int u; Filsys *fs; Off raddr; if(CHAT(cp)) { fprint(2, "c_attach %d\n", cp->chan); fprint(2, "\tfid = %d\n", in->fid); fprint(2, "\tuid = %s\n", in->uname); fprint(2, "\targ = %s\n", in->aname); } ou->qid = QID9P1(0,0); ou->fid = in->fid; if(!in->aname[0]) /* default */ strncpy(in->aname, "main", sizeof(in->aname)); p = 0; f = filep(cp, in->fid, 1); if(!f) { ou->err = Efid; goto out; } u = -1; if(cp != cons.chan) { if(noattach && strcmp(in->uname, "none")) { ou->err = Enoattach; goto out; } if(authorize(cp, in, ou) == 0 || strcmp(in->uname, "adm") == 0) { ou->err = Eauth; goto out; } u = strtouid(in->uname); if(u < 0) { ou->err = Ebadu; goto out; } } f->uid = u; fs = fsstr(in->aname); if(fs == 0) { ou->err = Ebadspc; goto out; } raddr = getraddr(fs->dev); p = getbuf(fs->dev, raddr, Brd); d = getdir(p, 0); if(!d || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } if(iaccess(f, d, DEXEC) || f->uid == 0 && fs->dev->type == Devro) { /* * 'none' not allowed on dump */ ou->err = Eaccess; goto out; } accessdir(p, d, FREAD, f->uid); mkqid(&f->qid, d, 1); f->fs = fs; f->addr = raddr; f->slot = 0; f->open = 0; freewp(f->wpath); f->wpath = 0; mkqid9p1(&ou->qid, &f->qid); strncpy(cp->whoname, in->uname, sizeof(cp->whoname)); cp->whotime = time(nil); out: if(p) putbuf(p); if(f) { qunlock(f); if(ou->err) freefp(f); } } static void f_clone(Chan *cp, Fcall *in, Fcall *ou) { File *f1, *f2; Wpath *p; int fid, fid1; if(CHAT(cp)) { fprint(2, "c_clone %d\n", cp->chan); fprint(2, "\told fid = %d\n", in->fid); fprint(2, "\tnew fid = %d\n", in->newfid); } fid = in->fid; fid1 = in->newfid; f1 = 0; f2 = 0; if(fid < fid1) { f1 = filep(cp, fid, 0); f2 = filep(cp, fid1, 1); } else if(fid1 < fid) { f2 = filep(cp, fid1, 1); f1 = filep(cp, fid, 0); } if(!f1 || !f2) { ou->err = Efid; goto out; } f2->fs = f1->fs; f2->addr = f1->addr; f2->open = f1->open & ~FREMOV; f2->uid = f1->uid; f2->slot = f1->slot; f2->qid = f1->qid; freewp(f2->wpath); lock(&wpathlock); f2->wpath = f1->wpath; for(p = f2->wpath; p; p = p->up) p->refs++; unlock(&wpathlock); out: ou->fid = fid; if(f1) qunlock(f1); if(f2) { qunlock(f2); if(ou->err) freefp(f2); } } static void f_walk(Chan *cp, Fcall *in, Fcall *ou) { Iobuf *p, *p1; Dentry *d, *d1; File *f; Wpath *w; int slot, mask; Off addr, qpath; if(CHAT(cp)) { fprint(2, "c_walk %d\n", cp->chan); fprint(2, "\tfid = %d\n", in->fid); fprint(2, "\tname = %s\n", in->name); } ou->fid = in->fid; ou->qid = QID9P1(0,0); p = 0; f = filep(cp, in->fid, 0); if(!f) { ou->err = Efid; goto out; } p = getbuf(f->fs->dev, f->addr, Brd); d = getdir(p, f->slot); if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } if(!(d->mode & DDIR)) { ou->err = Edir1; goto out; } if(ou->err = mkqidcmp(&f->qid, d)) goto out; if(iaccess(f, d, DEXEC)) { ou->err = Eaccess; goto out; } accessdir(p, d, FREAD, f->uid); if(strcmp(in->name, ".") == 0) goto setdot; if(strcmp(in->name, "..") == 0) { if(f->wpath == 0) goto setdot; putbuf(p); p = 0; addr = f->wpath->addr; slot = f->wpath->slot; p1 = getbuf(f->fs->dev, addr, Brd); d1 = getdir(p1, slot); if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) { if(p1) putbuf(p1); ou->err = Ephase; goto out; } lock(&wpathlock); f->wpath->refs--; f->wpath = f->wpath->up; unlock(&wpathlock); goto found; } for(addr=0;; addr++) { if(p == 0) { p = getbuf(f->fs->dev, f->addr, Brd); d = getdir(p, f->slot); if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } } qpath = d->qid.path; p1 = dnodebuf1(p, d, addr, 0, f->uid); p = 0; if(!p1 || checktag(p1, Tdir, qpath) ) { if(p1) putbuf(p1); ou->err = Eentry; goto out; } mask = DALLOC; if(f->fs->dev->type == Devro) mask |= DTMP; for(slot=0; slot<DIRPERBUF; slot++) { d1 = getdir(p1, slot); if((d1->mode & mask) != DALLOC) continue; if(strncmp(in->name, d1->name, sizeof(in->name)) != 0) continue; /* * update walk path */ w = newwp(); if(!w) { ou->err = Ewalk; putbuf(p1); goto out; } w->addr = f->addr; w->slot = f->slot; w->up = f->wpath; f->wpath = w; slot += DIRPERBUF*addr; goto found; } putbuf(p1); } found: f->addr = p1->addr; mkqid(&f->qid, d1, 1); putbuf(p1); f->slot = slot; setdot: mkqid9p1(&ou->qid, &f->qid); f->open = 0; out: if(p) putbuf(p); if(f) qunlock(f); } static void f_open(Chan *cp, Fcall *in, Fcall *ou) { Iobuf *p; Dentry *d; File *f; Tlock *t; Qid qid; int ro, fmod; if(CHAT(cp)) { fprint(2, "c_open %d\n", cp->chan); fprint(2, "\tfid = %d\n", in->fid); fprint(2, "\tmode = %o\n", in->mode); } p = 0; f = filep(cp, in->fid, 0); if(!f) { ou->err = Efid; goto out; } /* * if remove on close, check access here */ ro = f->fs->dev->type == Devro; if(in->mode & ORCLOSE) { if(ro) { ou->err = Eronly; goto out; } /* * check on parent directory of file to be deleted */ if(f->wpath == 0 || f->wpath->addr == f->addr) { ou->err = Ephase; goto out; } p = getbuf(f->fs->dev, f->wpath->addr, Brd); d = getdir(p, f->wpath->slot); if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { ou->err = Ephase; goto out; } if(iaccess(f, d, DWRITE)) { ou->err = Eaccess; goto out; } putbuf(p); } p = getbuf(f->fs->dev, f->addr, Brd); d = getdir(p, f->slot); if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } if(ou->err = mkqidcmp(&f->qid, d)) goto out; mkqid(&qid, d, 1); switch(in->mode & 7) { case OREAD: if(iaccess(f, d, DREAD)) goto badaccess; fmod = FREAD; break; case OWRITE: if((d->mode & DDIR) || iaccess(f, d, DWRITE)) goto badaccess; if(ro) { ou->err = Eronly; goto out; } fmod = FWRITE; break; case ORDWR: if((d->mode & DDIR) || iaccess(f, d, DREAD) || iaccess(f, d, DWRITE)) goto badaccess; if(ro) { ou->err = Eronly; goto out; } fmod = FREAD+FWRITE; break; case OEXEC: if((d->mode & DDIR) || iaccess(f, d, DEXEC)) goto badaccess; fmod = FREAD; break; default: ou->err = Emode; goto out; } if(in->mode & OTRUNC) { if((d->mode & DDIR) || iaccess(f, d, DWRITE)) goto badaccess; if(ro) { ou->err = Eronly; goto out; } } t = 0; if(d->mode & DLOCK) { t = tlocked(p, d); if(t == nil) { ou->err = Elocked; goto out; } } if(in->mode & ORCLOSE) fmod |= FREMOV; f->open = fmod; if(in->mode & OTRUNC) if(!(d->mode & DAPND)) { dtrunc(p, d, f->uid); qid.vers = d->qid.version; } f->tlock = t; if(t) t->file = f; f->lastra = 1; mkqid9p1(&ou->qid, &qid); goto out; badaccess: ou->err = Eaccess; f->open = 0; out: if(p) putbuf(p); if(f) qunlock(f); ou->fid = in->fid; } static void f_create(Chan *cp, Fcall *in, Fcall *ou) { Iobuf *p, *p1; Dentry *d, *d1; File *f; int slot, slot1, fmod; Off addr, addr1, path; Qid qid; Tlock *t; Wpath *w; if(CHAT(cp)) { fprint(2, "c_create %d\n", cp->chan); fprint(2, "\tfid = %d\n", in->fid); fprint(2, "\tname = %s\n", in->name); fprint(2, "\tperm = %lx+%lo\n", (in->perm>>28)&0xf, in->perm&0777); fprint(2, "\tmode = %o\n", in->mode); } p = 0; f = filep(cp, in->fid, 0); if(!f) { ou->err = Efid; goto out; } if(f->fs->dev->type == Devro) { ou->err = Eronly; goto out; } p = getbuf(f->fs->dev, f->addr, Brd); d = getdir(p, f->slot); if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } if(ou->err = mkqidcmp(&f->qid, d)) goto out; if(!(d->mode & DDIR)) { ou->err = Edir2; goto out; } if(iaccess(f, d, DWRITE)) { ou->err = Eaccess; goto out; } accessdir(p, d, FREAD, f->uid); if(ou->err = checkname(in->name)) goto out; addr1 = 0; slot1 = 0; /* set */ for(addr=0;; addr++) { p1 = dnodebuf(p, d, addr, 0, f->uid); if(!p1) { if(addr1) break; p1 = dnodebuf(p, d, addr, Tdir, f->uid); } if(p1 == 0) { ou->err = Efull; goto out; } if(checktag(p1, Tdir, d->qid.path)) { putbuf(p1); goto phase; } for(slot=0; slot<DIRPERBUF; slot++) { d1 = getdir(p1, slot); if(!(d1->mode & DALLOC)) { if(!addr1) { addr1 = p1->addr; slot1 = slot + addr*DIRPERBUF; } continue; } if(!strncmp(in->name, d1->name, sizeof(in->name))) { putbuf(p1); ou->err = Eexist; goto out; } } putbuf(p1); } switch(in->mode & 7) { case OEXEC: case OREAD: /* seems only useful to make directories */ fmod = FREAD; break; case OWRITE: fmod = FWRITE; break; case ORDWR: fmod = FREAD+FWRITE; break; default: ou->err = Emode; goto out; } if(in->perm & PDIR) if((in->mode & OTRUNC) || (in->perm & PAPND) || (fmod & FWRITE)) goto badaccess; /* * do it */ path = qidpathgen(f->fs->dev); p1 = getbuf(f->fs->dev, addr1, Brd|Bimm|Bmod); d1 = getdir(p1, slot1); if(!d1 || checktag(p1, Tdir, d->qid.path)) { if(p1) putbuf(p1); goto phase; } if(d1->mode & DALLOC) { putbuf(p1); goto phase; } strncpy(d1->name, in->name, sizeof(in->name)); if(cp == cons.chan) { d1->uid = cons.uid; d1->gid = cons.gid; } else { d1->uid = f->uid; d1->gid = d->gid; in->perm &= d->mode | ~0666; if(in->perm & PDIR) in->perm &= d->mode | ~0777; } d1->qid.path = path; d1->qid.version = 0; d1->mode = DALLOC | (in->perm & 0777); if(in->perm & PDIR) { d1->mode |= DDIR; d1->qid.path |= QPDIR; } if(in->perm & PAPND) d1->mode |= DAPND; t = 0; if(in->perm & PLOCK) { d1->mode |= DLOCK; t = tlocked(p1, d1); /* if nil, out of tlock structures */ } accessdir(p1, d1, FWRITE, f->uid); mkqid(&qid, d1, 0); putbuf(p1); accessdir(p, d, FWRITE, f->uid); /* * do a walk to new directory entry */ w = newwp(); if(!w) { ou->err = Ewalk; goto out; } w->addr = f->addr; w->slot = f->slot; w->up = f->wpath; f->wpath = w; f->qid = qid; f->tlock = t; if(t) t->file = f; f->lastra = 1; if(in->mode & ORCLOSE) fmod |= FREMOV; f->open = fmod; f->addr = addr1; f->slot = slot1; mkqid9p1(&ou->qid, &qid); goto out; badaccess: ou->err = Eaccess; goto out; phase: ou->err = Ephase; out: if(p) putbuf(p); if(f) qunlock(f); ou->fid = in->fid; } static void f_read(Chan *cp, Fcall *in, Fcall *ou) { Iobuf *p, *p1; File *f; Dentry *d, *d1; Tlock *t; Off addr, offset; Timet tim; int nread, count, mask, n, o, slot; if(CHAT(cp)) { fprint(2, "c_read %d\n", cp->chan); fprint(2, "\tfid = %d\n", in->fid); fprint(2, "\toffset = %lld\n", (Wideoff)in->offset); fprint(2, "\tcount = %ld\n", in->count); } p = 0; count = in->count; offset = in->offset; nread = 0; f = filep(cp, in->fid, 0); if(!f) { ou->err = Efid; goto out; } if(!(f->open & FREAD)) { ou->err = Eopen; goto out; } if(count < 0 || count > MAXDAT) { ou->err = Ecount; goto out; } if(offset < 0) { ou->err = Eoffset; goto out; } p = getbuf(f->fs->dev, f->addr, Brd); d = getdir(p, f->slot); if(!d || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } if(ou->err = mkqidcmp(&f->qid, d)) goto out; if(t = f->tlock) { tim = toytime(); if(t->time < tim || t->file != f) { ou->err = Ebroken; goto out; } /* renew the lock */ t->time = tim + TLOCK; } accessdir(p, d, FREAD, f->uid); if(d->mode & DDIR) { addr = 0; goto dread; } /* XXXX terrible hack to get at raw data XXXX */ if(rawreadok && strncmp(d->name, "--raw--", 7) == 0) { Device *dev; Devsize boff, bsize; dev = p->dev; putbuf(p); p = 0; boff = number(d->name + 7, 0, 10) * 100000; if(boff < 0) boff = 0; if(boff > devsize(dev)) boff = devsize(dev); bsize = devsize(dev) - boff; if(offset+count >= 100000*RBUFSIZE) count = 100000*RBUFSIZE - offset; if((offset+count)/RBUFSIZE >= bsize) /* will not overflow */ count = bsize*RBUFSIZE - offset; while(count > 0) { addr = offset / RBUFSIZE; addr += boff; o = offset % RBUFSIZE; n = RBUFSIZE - o; if(n > count) n = count; p1 = getbuf(dev, addr, Brd); if(p1) { memmove(ou->data+nread, p1->iobuf+o, n); putbuf(p1); } else memset(ou->data+nread, 0, n); count -= n; nread += n; offset += n; } goto out; } if(offset >= d->size) count = 0; else if(offset+count > d->size) count = d->size - offset; while(count > 0) { if(p == 0) { p = getbuf(f->fs->dev, f->addr, Brd); d = getdir(p, f->slot); if(!d || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } } addr = offset / BUFSIZE; f->lastra = dbufread(p, d, addr, f->lastra, f->uid); o = offset % BUFSIZE; n = BUFSIZE - o; if(n > count) n = count; p1 = dnodebuf1(p, d, addr, 0, f->uid); p = 0; if(p1) { if(checktag(p1, Tfile, QPNONE)) { ou->err = Ephase; putbuf(p1); goto out; } memmove(ou->data+nread, p1->iobuf+o, n); putbuf(p1); } else memset(ou->data+nread, 0, n); count -= n; nread += n; offset += n; } goto out; dread: for (;;) { if(p == 0) { p = getbuf(f->fs->dev, f->addr, Brd); d = getdir(p, f->slot); if(!d || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } } p1 = dnodebuf1(p, d, addr, 0, f->uid); p = 0; if(!p1) goto out; if(checktag(p1, Tdir, QPNONE)) { ou->err = Ephase; putbuf(p1); goto out; } n = DIRREC; mask = DALLOC; if(f->fs->dev->type == Devro) mask |= DTMP; for(slot=0; slot<DIRPERBUF; slot++) { d1 = getdir(p1, slot); if((d1->mode & mask) != DALLOC) continue; if(offset >= n) { offset -= n; continue; } if(count < n) { putbuf(p1); goto out; } if(convD2M9p1(d1, ou->data+nread) != n) fprint(2, "9p1: dirread convD2M1990\n"); nread += n; count -= n; } putbuf(p1); addr++; } out: count = in->count - nread; if(count > 0) memset(ou->data+nread, 0, count); if(p) putbuf(p); if(f) qunlock(f); ou->fid = in->fid; ou->count = nread; if(CHAT(cp)) fprint(2, "\tnread = %d\n", nread); } static void f_write(Chan *cp, Fcall *in, Fcall *ou) { Iobuf *p, *p1; Dentry *d; File *f; Tlock *t; Off offset, addr, qpath; Timet tim; int count, nwrite, o, n; if(CHAT(cp)) { fprint(2, "c_write %d\n", cp->chan); fprint(2, "\tfid = %d\n", in->fid); fprint(2, "\toffset = %lld\n", (Wideoff)in->offset); fprint(2, "\tcount = %ld\n", in->count); } offset = in->offset; count = in->count; nwrite = 0; p = 0; f = filep(cp, in->fid, 0); if(!f) { ou->err = Efid; goto out; } if(!(f->open & FWRITE)) { ou->err = Eopen; goto out; } if(f->fs->dev->type == Devro) { ou->err = Eronly; goto out; } if(count < 0 || count > MAXDAT) { ou->err = Ecount; goto out; } if(offset < 0) { ou->err = Eoffset; goto out; } p = getbuf(f->fs->dev, f->addr, Brd|Bmod); d = getdir(p, f->slot); if(!d || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } if(ou->err = mkqidcmp(&f->qid, d)) goto out; if(t = f->tlock) { tim = toytime(); if(t->time < tim || t->file != f) { ou->err = Ebroken; goto out; } /* renew the lock */ t->time = tim + TLOCK; } accessdir(p, d, FWRITE, f->uid); if(d->mode & DAPND) offset = d->size; if(offset+count > d->size) d->size = offset+count; while(count > 0) { if(p == 0) { p = getbuf(f->fs->dev, f->addr, Brd|Bmod); d = getdir(p, f->slot); if(!d || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } } addr = offset / BUFSIZE; o = offset % BUFSIZE; n = BUFSIZE - o; if(n > count) n = count; qpath = d->qid.path; p1 = dnodebuf1(p, d, addr, Tfile, f->uid); p = 0; if(p1 == 0) { ou->err = Efull; goto out; } if(checktag(p1, Tfile, qpath)) { putbuf(p1); ou->err = Ephase; goto out; } memmove(p1->iobuf+o, in->data+nwrite, n); p1->flags |= Bmod; putbuf(p1); count -= n; nwrite += n; offset += n; } if(CHAT(cp)) fprint(2, "\tnwrite = %d\n", nwrite); out: if(p) putbuf(p); if(f) qunlock(f); ou->fid = in->fid; ou->count = nwrite; } int doremove(File *f) { Iobuf *p, *p1; Dentry *d, *d1; Off addr; int slot, err; p = 0; p1 = 0; if(f->fs->dev->type == Devro) { err = Eronly; goto out; } /* * check on parent directory of file to be deleted */ if(f->wpath == 0 || f->wpath->addr == f->addr) { err = Ephase; goto out; } p1 = getbuf(f->fs->dev, f->wpath->addr, Brd); d1 = getdir(p1, f->wpath->slot); if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) { err = Ephase; goto out; } if(iaccess(f, d1, DWRITE)) { err = Eaccess; goto out; } accessdir(p1, d1, FWRITE, f->uid); putbuf(p1); p1 = 0; /* * check on file to be deleted */ p = getbuf(f->fs->dev, f->addr, Brd); d = getdir(p, f->slot); if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { err = Ealloc; goto out; } if(err = mkqidcmp(&f->qid, d)) goto out; /* * if deleting a directory, make sure it is empty */ if((d->mode & DDIR)) for(addr=0;; addr++) { p1 = dnodebuf(p, d, addr, 0, f->uid); if(!p1) break; if(checktag(p1, Tdir, d->qid.path)) { err = Ephase; goto out; } for(slot=0; slot<DIRPERBUF; slot++) { d1 = getdir(p1, slot); if(!(d1->mode & DALLOC)) continue; err = Eempty; goto out; } putbuf(p1); } /* * do it */ dtrunc(p, d, f->uid); memset(d, 0, sizeof(Dentry)); settag(p, Tdir, QPNONE); out: if(p1) putbuf(p1); if(p) putbuf(p); return err; } static int doclunk(File* f, int remove) { Tlock *t; int err; err = 0; if(t = f->tlock) { if(t->file == f) t->time = 0; /* free the lock */ f->tlock = 0; } if(remove) err = doremove(f); f->open = 0; freewp(f->wpath); freefp(f); return err; } static void f_clunk(Chan *cp, Fcall *in, Fcall *ou) { File *f; if(CHAT(cp)) { fprint(2, "c_clunk %d\n", cp->chan); fprint(2, "\tfid = %d\n", in->fid); } f = filep(cp, in->fid, 0); if(!f) ou->err = Efid; else { doclunk(f, f->open & FREMOV); qunlock(f); } ou->fid = in->fid; } static void f_remove(Chan *cp, Fcall *in, Fcall *ou) { File *f; if(CHAT(cp)) { fprint(2, "c_remove %d\n", cp->chan); fprint(2, "\tfid = %d\n", in->fid); } f = filep(cp, in->fid, 0); if(!f) ou->err = Efid; else { ou->err = doclunk(f, 1); qunlock(f); } ou->fid = in->fid; } static void f_stat(Chan *cp, Fcall *in, Fcall *ou) { Iobuf *p; Dentry *d; File *f; if(CHAT(cp)) { fprint(2, "c_stat %d\n", cp->chan); fprint(2, "\tfid = %d\n", in->fid); } p = 0; memset(ou->stat, 0, sizeof(ou->stat)); f = filep(cp, in->fid, 0); if(!f) { ou->err = Efid; goto out; } p = getbuf(f->fs->dev, f->addr, Brd); d = getdir(p, f->slot); if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } if(ou->err = mkqidcmp(&f->qid, d)) goto out; if(d->qid.path == QPROOT) /* stat of root gives time */ d->atime = time(nil); if(convD2M9p1(d, ou->stat) != DIRREC) fprint(2, "9p1: stat convD2M\n"); out: if(p) putbuf(p); if(f) qunlock(f); ou->fid = in->fid; } static void f_wstat(Chan *cp, Fcall *in, Fcall *ou) { Iobuf *p, *p1; Dentry *d, *d1, xd; File *f; int slot; Off addr; if(CHAT(cp)) { fprint(2, "c_wstat %d\n", cp->chan); fprint(2, "\tfid = %d\n", in->fid); } p = 0; p1 = 0; d1 = 0; f = filep(cp, in->fid, 0); if(!f) { ou->err = Efid; goto out; } if(f->fs->dev->type == Devro) { ou->err = Eronly; goto out; } /* * first get parent */ if(f->wpath) { p1 = getbuf(f->fs->dev, f->wpath->addr, Brd); d1 = getdir(p1, f->wpath->slot); if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) { ou->err = Ephase; goto out; } } p = getbuf(f->fs->dev, f->addr, Brd); d = getdir(p, f->slot); if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { ou->err = Ealloc; goto out; } if(ou->err = mkqidcmp(&f->qid, d)) goto out; convM2D9p1(in->stat, &xd); if(CHAT(cp)) { fprint(2, "\td.name = %s\n", xd.name); fprint(2, "\td.uid = %d\n", xd.uid); fprint(2, "\td.gid = %d\n", xd.gid); fprint(2, "\td.mode = %o\n", xd.mode); } /* * if user none, * cant do anything */ if(f->uid == 0) { ou->err = Eaccess; goto out; } /* * if chown, * must be god */ if(xd.uid != d->uid && !isallowed(f)) { ou->err = Ewstatu; goto out; } /* * if chgroup, * must be either * a) owner and in new group * b) leader of both groups */ if (xd.gid != d->gid && !isallowed(f) && (d->uid != f->uid || !ingroup(f->uid, xd.gid)) && (!leadgroup(f->uid, xd.gid) || !leadgroup(f->uid, d->gid))) { ou->err = Ewstatg; goto out; } /* * if rename, * must have write permission in parent */ if (strncmp(d->name, xd.name, sizeof(d->name)) != 0) { if(ou->err = checkname(xd.name)) goto out; /* rename root? */ if(!d1){ ou->err = Ename; goto out; } /* * drop entry to prevent lock, then * check that destination name is unique, */ putbuf(p); for(addr=0;; addr++) { p = dnodebuf(p1, d1, addr, 0, f->uid); if(!p) break; if(checktag(p, Tdir, d1->qid.path)) { putbuf(p); continue; } for(slot=0; slot<DIRPERBUF; slot++) { d = getdir(p, slot); if(!(d->mode & DALLOC)) continue; if(!strncmp(xd.name, d->name, sizeof(xd.name))) { ou->err = Eexist; goto out; } } putbuf(p); } /* * reacquire entry */ p = getbuf(f->fs->dev, f->addr, Brd); d = getdir(p, f->slot); if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { ou->err = Ephase; goto out; } if(iaccess(f, d1, DWRITE)) { ou->err = Eaccess; goto out; } } /* * if mode/time, either * a) owner * b) leader of either group */ if (d->mtime != xd.mtime || ((d->mode^xd.mode) & (DAPND|DLOCK|0777))) if (d->uid != f->uid && !isallowed(f) && !leadgroup(f->uid, xd.gid) && !leadgroup(f->uid, d->gid)) { ou->err = Ewstatu; goto out; } d->mtime = xd.mtime; d->uid = xd.uid; d->gid = xd.gid; d->mode = (xd.mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR)); strncpy(d->name, xd.name, sizeof(d->name)); accessdir(p, d, FREAD, f->uid); out: if(p) putbuf(p); if(p1) putbuf(p1); if(f) qunlock(f); ou->fid = in->fid; } static void f_clwalk(Chan *cp, Fcall *in, Fcall *ou) { int er, fid; if(CHAT(cp)) fprint(2, "c_clwalk macro\n"); f_clone(cp, in, ou); /* sets tag, fid */ if(ou->err) return; fid = in->fid; in->fid = in->newfid; f_walk(cp, in, ou); /* sets tag, fid, qid */ er = ou->err; if(er == Eentry) { /* * if error is "no entry" * return non error and fid */ ou->err = 0; f_clunk(cp, in, ou); /* sets tag, fid */ ou->err = 0; ou->fid = fid; if(CHAT(cp)) fprint(2, "\terror: %s\n", errstr9p[er]); } else if(er) { /* * if any other error * return an error */ ou->err = 0; f_clunk(cp, in, ou); /* sets tag, fid */ ou->err = er; } /* * non error * return newfid */ } void (*call9p1[MAXSYSCALL])(Chan*, Fcall*, Fcall*) = { [Tnop] f_nop, [Tosession] f_session, [Tsession] f_session, [Tflush] f_flush, [Toattach] f_attach, [Tattach] f_attach, [Tclone] f_clone, [Twalk] f_walk, [Topen] f_open, [Tcreate] f_create, [Tread] f_read, [Twrite] f_write, [Tclunk] f_clunk, [Tremove] f_remove, [Tstat] f_stat, [Twstat] f_wstat, [Tclwalk] f_clwalk, }; int error9p1(Chan* cp, Msgbuf* mb) { Msgbuf *mb1; fprint(2, "type=%d count=%d\n", mb->data[0], mb->count); hexdump(mb->data, 12); mb1 = mballoc(3, cp, Mbreply4); mb1->data[0] = Rnop; /* your nop was ok */ mb1->data[1] = ~0; mb1->data[2] = ~0; mb1->count = 3; mb1->param = mb->param; fs_send(cp->reply, mb1); return 1; } int serve9p1(Msgbuf* mb) { int t, n; Chan *cp; Msgbuf *mb1; Fcall fi, fo; assert(mb != nil); cp = mb->chan; assert(mb->data != nil); if(convM2S9p1(mb->data, &fi, mb->count) == 0){ assert(cp != nil); if(cp->protocol == nil) return 0; fprint(2, "9p1: bad M2S conversion\n"); return error9p1(cp, mb); } t = fi.type; if(t < 0 || t >= MAXSYSCALL || (t&1) || !call9p1[t]) { fprint(2, "9p1: bad message type\n"); return error9p1(cp, mb); } /* * allocate reply message */ if(t == Tread) { mb1 = mballoc(MAXMSG+MAXDAT, cp, Mbreply2); fo.data = (char*)(mb1->data + 8); } else mb1 = mballoc(MAXMSG, cp, Mbreply3); /* * call the file system */ assert(cp != nil); fo.err = 0; (*call9p1[t])(cp, &fi, &fo); fo.type = t+1; fo.tag = fi.tag; if(fo.err) { if(cons.flags&errorflag) fprint(2, "\ttype %d: error: %s\n", t, errstr9p[fo.err]); if(CHAT(cp)) fprint(2, "\terror: %s\n", errstr9p[fo.err]); fo.type = Rerror; strncpy(fo.ename, errstr9p[fo.err], sizeof(fo.ename)); } n = convS2M9p1(&fo, mb1->data); if(n == 0) { fprint(2, "9p1: bad S2M conversion\n"); mbfree(mb1); return error9p1(cp, mb); } mb1->count = n; mb1->param = mb->param; fs_send(cp->reply, mb1); return 1; }