9p.c
来自「这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易」· C语言 代码 · 共 1,175 行 · 第 1/2 页
C
1,175 行
if(!fileIsDir(fid->file)){ vtSetError("not a directory"); goto error; } if(permFid(fid, PermW) <= 0) goto error; if(!validFileName(m->t.name)) goto error; if(strcmp(fid->uid, uidnoworld) == 0){ vtSetError(EPermission); goto error; } omode = m->t.mode & OMODE; open = 0; if(omode == OREAD || omode == ORDWR || omode == OEXEC) open |= FidORead; if(omode == OWRITE || omode == ORDWR) open |= FidOWrite; if((open & (FidOWrite|FidORead)) == 0){ vtSetError("unknown mode"); goto error; } if(m->t.perm & DMDIR){ if((m->t.mode & (ORCLOSE|OTRUNC)) || (open & FidOWrite)){ vtSetError("illegal mode"); goto error; } if(m->t.perm & DMAPPEND){ vtSetError("illegal perm"); goto error; } } mode = fileGetMode(fid->file); perm = m->t.perm; if(m->t.perm & DMDIR) perm &= ~0777|(mode & 0777); else perm &= ~0666|(mode & 0666); mode = perm & 0777; if(m->t.perm & DMDIR) mode |= ModeDir; if(m->t.perm & DMAPPEND) mode |= ModeAppend; if(m->t.perm & DMEXCL) mode |= ModeExclusive; if(m->t.perm & DMTMP) mode |= ModeTemporary; if((file = fileCreate(fid->file, m->t.name, mode, fid->uid)) == nil){ fidPut(fid); return 0; } fileDecRef(fid->file); fid->qid.vers = fileGetMcount(file); fid->qid.path = fileGetId(file); fid->file = file; mode = fileGetMode(fid->file); if(mode & ModeDir) fid->qid.type = QTDIR; else fid->qid.type = QTFILE; if(mode & ModeAppend) fid->qid.type |= QTAPPEND; if(mode & ModeExclusive){ fid->qid.type |= QTEXCL; assert(exclAlloc(fid) != 0); } if(m->t.mode & ORCLOSE) open |= FidORclose; fid->open = open; m->r.qid = fid->qid; m->r.iounit = m->con->msize-IOHDRSZ; fidPut(fid); return 1;error: fidPut(fid); return 0;}static intrTopen(Msg* m){ Fid *fid; int isdir, mode, omode, open, rofs; if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil) return 0; if(fid->open){ vtSetError("fid open for I/O"); goto error; } isdir = fileIsDir(fid->file); open = 0; rofs = fileIsRoFs(fid->file) || !groupWriteMember(fid->uname); if(m->t.mode & ORCLOSE){ if(isdir){ vtSetError("is a directory"); goto error; } if(rofs){ vtSetError("read-only filesystem"); goto error; } if(permParent(fid, PermW) <= 0) goto error; open |= FidORclose; } omode = m->t.mode & OMODE; if(omode == OREAD || omode == ORDWR){ if(permFid(fid, PermR) <= 0) goto error; open |= FidORead; } if(omode == OWRITE || omode == ORDWR || (m->t.mode & OTRUNC)){ if(isdir){ vtSetError("is a directory"); goto error; } if(rofs){ vtSetError("read-only filesystem"); goto error; } if(permFid(fid, PermW) <= 0) goto error; open |= FidOWrite; } if(omode == OEXEC){ if(isdir){ vtSetError("is a directory"); goto error; } if(permFid(fid, PermX) <= 0) goto error; open |= FidORead; } if((open & (FidOWrite|FidORead)) == 0){ vtSetError("unknown mode"); goto error; } mode = fileGetMode(fid->file); if((mode & ModeExclusive) && exclAlloc(fid) == 0) goto error; /* * Everything checks out, try to commit any changes. */ if((m->t.mode & OTRUNC) && !(mode & ModeAppend)) if(!fileTruncate(fid->file, fid->uid)) goto error; if(isdir && fid->db != nil){ dirBufFree(fid->db); fid->db = nil; } fid->qid.vers = fileGetMcount(fid->file); m->r.qid = fid->qid; m->r.iounit = m->con->msize-IOHDRSZ; fid->open = open; fidPut(fid); return 1;error: if(fid->excl != nil) exclFree(fid); fidPut(fid); return 0;}static intrTwalk(Msg* m){ Qid qid; Fcall *r, *t; int nwname, wlock; File *file, *nfile; Fid *fid, *ofid, *nfid; t = &m->t; if(t->fid == t->newfid) wlock = FidFWlock; else wlock = 0; /* * The file identified by t->fid must be valid in the * current session and must not have been opened for I/O * by an open or create message. */ if((ofid = fidGet(m->con, t->fid, wlock)) == nil) return 0; if(ofid->open){ vtSetError("file open for I/O"); fidPut(ofid); return 0; } /* * If newfid is not the same as fid, allocate a new file; * a side effect is checking newfid is not already in use (error); * if there are no names to walk this will be equivalent to a * simple 'clone' operation. * It's a no-op if newfid is the same as fid and t->nwname is 0. */ nfid = nil; if(t->fid != t->newfid){ nfid = fidGet(m->con, t->newfid, FidFWlock|FidFCreate); if(nfid == nil){ vtSetError("fid in use"); fidPut(ofid); return 0; } nfid->open = ofid->open & ~FidORclose; nfid->file = fileIncRef(ofid->file); nfid->qid = ofid->qid; nfid->uid = vtStrDup(ofid->uid); nfid->uname = vtStrDup(ofid->uname); nfid->fsys = fsysIncRef(ofid->fsys); fid = nfid; } else fid = ofid; r = &m->r; r->nwqid = 0; if(t->nwname == 0){ if(nfid != nil) fidPut(nfid); fidPut(ofid); return 1; } file = fid->file; fileIncRef(file); qid = fid->qid; for(nwname = 0; nwname < t->nwname; nwname++){ /* * Walked elements must represent a directory and * the implied user must have permission to search * the directory. Walking .. is always allowed, so that * you can't walk into a directory and then not be able * to walk out of it. */ if(!(qid.type & QTDIR)){ vtSetError("not a directory"); break; } switch(permFile(file, fid, PermX)){ case 1: break; case 0: if(strcmp(t->wname[nwname], "..") == 0) break; case -1: goto Out; } if((nfile = fileWalk(file, t->wname[nwname])) == nil) break; fileDecRef(file); file = nfile; qid.type = QTFILE; if(fileIsDir(file)) qid.type = QTDIR; qid.vers = fileGetMcount(file); qid.path = fileGetId(file); r->wqid[r->nwqid++] = qid; } if(nwname == t->nwname){ /* * Walked all elements. Update the target fid * from the temporary qid used during the walk, * and tidy up. */ fid->qid = r->wqid[r->nwqid-1]; fileDecRef(fid->file); fid->file = file; if(nfid != nil) fidPut(nfid); fidPut(ofid); return 1; }Out: /* * Didn't walk all elements, 'clunk' nfid if it exists * and leave fid untouched. * It's not an error if some of the elements were walked OK. */ fileDecRef(file); if(nfid != nil) fidClunk(nfid); fidPut(ofid); if(nwname == 0) return 0; return 1;}static intrTflush(Msg* m){ if(m->t.oldtag != NOTAG) msgFlush(m); return 1;}static voidparseAname(char *aname, char **fsname, char **path){ char *s; if(aname && aname[0]) s = vtStrDup(aname); else s = vtStrDup("main/active"); *fsname = s; if((*path = strchr(s, '/')) != nil) *(*path)++ = '\0'; else *path = "";}/* * Check remote IP address against /mnt/ipok. * Sources.cs.bell-labs.com uses this to disallow * network connections from Sudan, Libya, etc., * following U.S. cryptography export regulations. */static intconIPCheck(Con* con){ char ok[256], *p; int fd; if(con->flags&ConIPCheck){ if(con->remote[0] == 0){ vtSetError("cannot verify unknown remote address"); return 0; } if(access("/mnt/ipok/ok", AEXIST) < 0){ /* mount closes the fd on success */ if((fd = open("/srv/ipok", ORDWR)) >= 0 && mount(fd, -1, "/mnt/ipok", MREPL, "") < 0) close(fd); if(access("/mnt/ipok/ok", AEXIST) < 0){ vtSetError("cannot verify remote address"); return 0; } } snprint(ok, sizeof ok, "/mnt/ipok/ok/%s", con->remote); if((p = strchr(ok, '!')) != nil) *p = 0; if(access(ok, AEXIST) < 0){ vtSetError("restricted remote address"); return 0; } } return 1;}static intrTattach(Msg* m){ Fid *fid; Fsys *fsys; char *fsname, *path; if((fid = fidGet(m->con, m->t.fid, FidFWlock|FidFCreate)) == nil) return 0; parseAname(m->t.aname, &fsname, &path); if((fsys = fsysGet(fsname)) == nil){ fidClunk(fid); vtMemFree(fsname); return 0; } fid->fsys = fsys; if(m->t.uname[0] != '\0') fid->uname = vtStrDup(m->t.uname); else fid->uname = vtStrDup(unamenone); if((fid->con->flags&ConIPCheck) && !conIPCheck(fid->con)){ consPrint("reject %s from %s: %R\n", fid->uname, fid->con->remote); fidClunk(fid); vtMemFree(fsname); return 0; } if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){ if((fid->uid = uidByUname(fid->uname)) == nil) fid->uid = vtStrDup(unamenone); } else if(!authCheck(&m->t, fid, fsys)){ fidClunk(fid); vtMemFree(fsname); return 0; } fsysFsRlock(fsys); if((fid->file = fsysGetRoot(fsys, path)) == nil){ fsysFsRUnlock(fsys); fidClunk(fid); vtMemFree(fsname); return 0; } fsysFsRUnlock(fsys); vtMemFree(fsname); fid->qid = (Qid){fileGetId(fid->file), 0, QTDIR}; m->r.qid = fid->qid; fidPut(fid); return 1;}static intrTauth(Msg* m){ int afd; Con *con; Fid *afid; Fsys *fsys; char *fsname, *path; parseAname(m->t.aname, &fsname, &path); if((fsys = fsysGet(fsname)) == nil){ vtMemFree(fsname); return 0; } vtMemFree(fsname); if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){ m->con->aok = 1; vtSetError("authentication disabled"); fsysPut(fsys); return 0; } if(strcmp(m->t.uname, unamenone) == 0){ vtSetError("user 'none' requires no authentication"); fsysPut(fsys); return 0; } con = m->con; if((afid = fidGet(con, m->t.afid, FidFWlock|FidFCreate)) == nil){ fsysPut(fsys); return 0; } afid->fsys = fsys; if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){ vtSetError("can't open \"/mnt/factotum/rpc\""); fidClunk(afid); return 0; } if((afid->rpc = auth_allocrpc(afd)) == nil){ close(afd); vtSetError("can't auth_allocrpc"); fidClunk(afid); return 0; } if(auth_rpc(afid->rpc, "start", "proto=p9any role=server", 23) != ARok){ vtSetError("can't auth_rpc"); fidClunk(afid); return 0; } afid->open = FidOWrite|FidORead; afid->qid.type = QTAUTH; afid->qid.path = m->t.afid; afid->uname = vtStrDup(m->t.uname); m->r.qid = afid->qid; fidPut(afid); return 1;}static intrTversion(Msg* m){ int v; Con *con; Fcall *r, *t; t = &m->t; r = &m->r; con = m->con; vtLock(con->lock); if(con->state != ConInit){ vtUnlock(con->lock); vtSetError("Tversion: down"); return 0; } con->state = ConNew; /* * Release the karma of past lives and suffering. * Should this be done before or after checking the * validity of the Tversion? */ fidClunkAll(con); if(t->tag != NOTAG){ vtUnlock(con->lock); vtSetError("Tversion: invalid tag"); return 0; } if(t->msize < 256){ vtUnlock(con->lock); vtSetError("Tversion: message size too small"); return 0; } if(t->msize < con->msize) r->msize = t->msize; else r->msize = con->msize; r->version = "unknown"; if(t->version[0] == '9' && t->version[1] == 'P'){ /* * Currently, the only defined version * is "9P2000"; ignore any later versions. */ v = strtol(&t->version[2], 0, 10); if(v >= 2000){ r->version = VERSION9P; con->msize = r->msize; con->state = ConUp; } else if(strcmp(t->version, "9PEoF") == 0){ r->version = "9PEoF"; con->msize = r->msize; con->state = ConMoribund; /* * Don't want to attempt to write this * message as the connection may be already * closed. */ m->state = MsgF; } } vtUnlock(con->lock); return 1;}int (*rFcall[Tmax])(Msg*) = { [Tversion] = rTversion, [Tauth] = rTauth, [Tattach] = rTattach, [Tflush] = rTflush, [Twalk] = rTwalk, [Topen] = rTopen, [Tcreate] = rTcreate, [Tread] = rTread, [Twrite] = rTwrite, [Tclunk] = rTclunk, [Tremove] = rTremove, [Tstat] = rTstat, [Twstat] = rTwstat,};
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?