📄 9p2.c
字号:
} else if(file->fs->dev->type == Devro){ error = Eronly; goto out; } if ((p = getbuf(file->fs->dev, file->addr, Bread|Bmod)) == nil || (d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)) { error = Ealloc; goto out; } if(error = mkqidcmp(&file->qid, d)) goto out; if(t = file->tlock) { tim = toytime(); if(t->time < tim || t->file != file){ error = Ebroken; goto out; } /* renew the lock */ t->time = tim + TLOCK; } accessdir(p, d, FWRITE, file->uid); if(d->mode & DAPND) offset = d->size; if(offset+count > d->size) d->size = offset+count; while(count > 0){ if(p == nil){ p = getbuf(file->fs->dev, file->addr, Bread|Bmod); if(p == nil){ error = Ealloc; goto out; } d = getdir(p, file->slot); if(d == nil || !(d->mode & DALLOC)){ error = 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, file->uid); p = nil; if(p1 == nil) { error = Efull; goto out; } if(checktag(p1, Tfile, qpath)){ putbuf(p1); error = Ephase; goto out; } memmove(p1->iobuf+o, f->data+nwrite, n); p1->flags |= Bmod; putbuf(p1); count -= n; nwrite += n; offset += n; }out: if(p != nil) putbuf(p); if(file != nil) qunlock(file); r->count = nwrite; return error;}static int_clunk(File* file, int remove, int wok){ Tlock *t; int error; error = 0; if(t = file->tlock){ if(t->file == file) t->time = 0; /* free the lock */ file->tlock = 0; } if(remove && (file->qid.type & QTAUTH) == 0) error = doremove(file, wok); file->open = 0; freewp(file->wpath); authfree(file->auth); freefp(file); qunlock(file); return error;}static intclunk(Chan* chan, Fcall* f, Fcall*){ File *file; if((file = filep(chan, f->fid, 0)) == nil) return Efid; _clunk(file, file->open & FREMOV, 0); return 0;}static intremove(Chan* chan, Fcall* f, Fcall*){ File *file; if((file = filep(chan, f->fid, 0)) == nil) return Efid; return _clunk(file, 1, chan == cons.chan);}static intstat(Chan* chan, Fcall* f, Fcall* r, uchar* data){ Dir dir; Iobuf *p; Dentry *d, dentry; File *file; int error, len; error = 0; p = nil; if((file = filep(chan, f->fid, 0)) == nil) return Efid; if(file->qid.type & QTAUTH){ memset(&dentry, 0, sizeof dentry); d = &dentry; mkqid9p1(&d->qid, &file->qid); strcpy(d->name, "#¿"); d->uid = authuid(file->auth); d->gid = d->uid; d->muid = d->uid; d->atime = time(); d->mtime = d->atime; d->size = 0; } else { p = getbuf(file->fs->dev, file->addr, Bread); if(p == nil || checktag(p, Tdir, QPNONE)){ error = Edir1; goto out; } d = getdir(p, file->slot); if(d == nil || !(d->mode & DALLOC)){ error = Ealloc; goto out; } if(error = mkqidcmp(&file->qid, d)) goto out; if(d->qid.path == QPROOT) /* stat of root gives time */ d->atime = time(); } len = mkdir9p2(&dir, d, data); data += len; if((r->nstat = convD2M(&dir, data, chan->msize - len)) == 0) error = Eedge; r->stat = data;out: if(p != nil) putbuf(p); if(file != nil) qunlock(file); return error;}static intwstat(Chan* chan, Fcall* f, Fcall*, char* strs){ Iobuf *p, *p1; Dentry *d, *d1; File *file; int error, err, gid, gl, muid, op, slot, tsync, uid; long addr; Dir dir; if(convM2D(f->stat, f->nstat, &dir, strs) == 0) return Econvert; /* * Get the file. * If user 'none' (uid == 0), can't do anything; * if filesystem is read-only, can't change anything. */ if((file = filep(chan, f->fid, 0)) == nil) return Efid; p = p1 = nil; if(file->uid == 0){ error = Eaccess; goto out; } if(file->fs->dev->type == Devro){ error = Eronly; goto out; } if(file->qid.type & QTAUTH){ error = Emode; goto out; } /* * Get the current entry and check it is still valid. */ p = getbuf(file->fs->dev, file->addr, Bread); if(p == nil || checktag(p, Tdir, QPNONE)){ error = Ealloc; goto out; } d = getdir(p, file->slot); if(d == nil || !(d->mode & DALLOC)){ error = Ealloc; goto out; } if(error = mkqidcmp(&file->qid, d)) goto out; /* * Run through each of the (sub-)fields in the provided Dir * checking for validity and whether it's a default: * .type, .dev and .atime are completely ignored and not checked; * .qid.path, .qid.vers and .muid are checked for validity but * any attempt to change them is an error. * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can * possibly be changed. * * 'Op' flags there are changed fields, i.e. it's not a no-op. * 'Tsync' flags all fields are defaulted. * * Wstatallow and writeallow are set to allow changes during the * fileserver bootstrap phase. */ tsync = 1; if(dir.qid.path != ~0){ if(dir.qid.path != file->qid.path){ error = Ewstatp; goto out; } tsync = 0; } if(dir.qid.vers != ~0){ if(dir.qid.vers != file->qid.vers){ error = Ewstatv; goto out; } tsync = 0; } if(dir.muid != nil && *dir.muid != '\0'){ muid = strtouid(dir.muid); if(muid != d->muid && !wstatallow){ error = Ewstatm; goto out; } tsync = 0; } /* * .qid.type and .mode have some bits in common. Only .mode * is currently needed for comparisons with the old mode but * if there are changes to the bits also encoded in .qid.type * then file->qid must be updated appropriately later. */ if(dir.qid.type == (uchar)~0){ if(dir.mode == ~0) dir.qid.type = mktype9p2(d->mode); else dir.qid.type = dir.mode>>24; } else tsync = 0; if(dir.mode == ~0) dir.mode = mkmode9p2(d->mode); else tsync = 0; /* * Check dir.qid.type and dir.mode agree, check for any unknown * type/mode bits, check for an attempt to change the directory bit. */ if(dir.qid.type != ((dir.mode>>24) & 0xFF)){ error = Ewstatq; goto out; } if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|0777)){ error = Ewstatb; goto out; } op = dir.mode^mkmode9p2(d->mode); if(op & DMDIR){ error = Ewstatd; goto out; } if(dir.mtime != ~0){ if(dir.mtime != d->mtime) op = 1; tsync = 0; } else dir.mtime = d->mtime; if(dir.length == ~(Off)0) dir.length = d->size; else { if (dir.length < 0) { error = Ewstatl; goto out; } else if(dir.length != d->size) op = 1; tsync = 0; } /* * Check for permission to change .mode, .mtime or .length, * must be owner or leader of either group, for which test gid * is needed; permission checks on gid will be done later. * 'Gl' counts whether neither, one or both groups are led. */ if(dir.gid != nil && *dir.gid != '\0'){ gid = strtouid(dir.gid); tsync = 0; } else gid = d->gid; gl = leadgroup(file->uid, gid) != 0; gl += leadgroup(file->uid, d->gid) != 0; if(op && !wstatallow && d->uid != file->uid && !gl){ error = Ewstato; goto out; } /* * Rename. * Check .name is valid and different to the current. */ if(dir.name != nil && *dir.name != '\0'){ if(error = checkname9p2(dir.name)) goto out; if(strncmp(dir.name, d->name, NAMELEN)) op = 1; else dir.name = d->name; tsync = 0; } else dir.name = d->name; /* * If the name is really to be changed check it's unique * and there is write permission in the parent. */ if(dir.name != d->name){ /* * First get parent. * Must drop current entry to prevent * deadlock when searching that new name * already exists below. */ putbuf(p); p = nil; if(file->wpath == nil){ error = Ephase; goto out; } p1 = getbuf(file->fs->dev, file->wpath->addr, Bread); if(p1 == nil || checktag(p1, Tdir, QPNONE)){ error = Ephase; goto out; } d1 = getdir(p1, file->wpath->slot); if(d1 == nil || !(d1->mode & DALLOC)){ error = Ephase; goto out; } /* * Check entries in parent for new name. */ for(addr = 0; ; addr++){ if((p = dnodebuf(p1, d1, addr, 0, file->uid)) == nil) 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) || strncmp(dir.name, d->name, sizeof d->name)) continue; error = Eexist; goto out; } putbuf(p); } /* * Reacquire entry and check it's still OK. */ p = getbuf(file->fs->dev, file->addr, Bread); if(p == nil || checktag(p, Tdir, QPNONE)){ error = Ephase; goto out; } d = getdir(p, file->slot); if(d == nil || !(d->mode & DALLOC)){ error = Ephase; goto out; } /* * Check write permission in the parent. */ if(!wstatallow && !writeallow && iaccess(file, d1, DWRITE)){ error = Eaccess; goto out; } } /* * Check for permission to change owner - must be god. */ if(dir.uid != nil && *dir.uid != '\0'){ uid = strtouid(dir.uid); if(uid != d->uid){ if(!wstatallow){ error = Ewstatu; goto out; } op = 1; } tsync = 0; } else uid = d->uid; /* * Check for permission to change group, must be * either owner and in new group or leader of both groups. */ if(gid != d->gid){ if(!(wstatallow || writeallow) && !(d->uid == file->uid && ingroup(file->uid, gid)) && !(gl == 2)){ error = Ewstatg; goto out; } op = 1; } /* * Checks all done, update if necessary. */ if(op){ d->mode = mkmode9p1(dir.mode); file->qid.type = mktype9p2(d->mode); d->mtime = dir.mtime; if (dir.length < d->size) { err = dtrunclen(p, d, dir.length, uid); if (error == 0) error = err; } d->size = dir.length; if(dir.name != d->name) strncpy(d->name, dir.name, sizeof(d->name)); d->uid = uid; d->gid = gid; } if(!tsync) accessdir(p, d, FREAD, file->uid);out: if(p != nil) putbuf(p); if(p1 != nil) putbuf(p1); qunlock(file); return error;}intserve9p2(Msgbuf* mb){ Chan *chan; Fcall f, r; Msgbuf *data, *rmb; char ename[64]; int error, n, type; static int once; if(once == 0){ fmtinstall('F', fcallfmt); once = 1; } /* * 0 return means i don't understand this message, * 1 return means i dealt with it, including error * replies. */ if(convM2S(mb->data, mb->count, &f) != mb->count){print("didn't like %d byte message\n", mb->count); return 0;} type = f.type; if(type < Tversion || type >= Tmax || (type & 1) || type == Terror) return 0; chan = mb->chan; if(CHAT(chan)) print("9p2: f %F\n", &f); r.type = type+1; r.tag = f.tag; error = 0; data = nil; switch(type){ default: r.type = Rerror; snprint(ename, sizeof(ename), "unknown message: %F", &f); r.ename = ename; break; case Tversion: error = version(chan, &f, &r); break; case Tauth: error = auth(chan, &f, &r); break; case Tattach: error = attach(chan, &f, &r); break; case Tflush: error = flush(chan, &f, &r); break; case Twalk: error = walk(chan, &f, &r); break; case Topen: error = open(chan, &f, &r); break; case Tcreate: error = create(chan, &f, &r); break; case Tread: data = mballoc(chan->msize, chan, Mbreply1); error = read(chan, &f, &r, data->data); break; case Twrite: error = write(chan, &f, &r); break; case Tclunk: error = clunk(chan, &f, &r); break; case Tremove: error = remove(chan, &f, &r); break; case Tstat: data = mballoc(chan->msize, chan, Mbreply1); error = stat(chan, &f, &r, data->data); break; case Twstat: data = mballoc(chan->msize, chan, Mbreply1); error = wstat(chan, &f, &r, (char*)data->data); break; } if(error != 0){ r.type = Rerror; if(error >= MAXERR){ snprint(ename, sizeof(ename), "error %d", error); r.ename = ename; } else r.ename = errstr9p[error]; } if(CHAT(chan)) print("9p2: r %F\n", &r); rmb = mballoc(chan->msize, chan, Mbreply2); n = convS2M(&r, rmb->data, chan->msize); if(data != nil) mbfree(data); if(n == 0){ type = r.type; r.type = Rerror; /* * If a Tversion has not been seen on the chan then * chan->msize will be 0. In that case craft a special * Rerror message. It's fortunate that the mballoc above * for rmb will have returned a Msgbuf of MAXMSG size * when given a request with count of 0... */ if(chan->msize == 0){ r.ename = "Tversion not seen"; n = convS2M(&r, rmb->data, MAXMSG); } else{ snprint(ename, sizeof(ename), "9p2: convS2M: type %d", type); r.ename = ename; n = convS2M(&r, rmb->data, chan->msize); } print("%s\n", r.ename); if(n == 0){ /* * What to do here, the failure notification failed? */ mbfree(rmb); return 1; } } rmb->count = n; rmb->param = mb->param; send(chan->reply, rmb); return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -