📄 9p.c
字号:
#include "stdinc.h"#include "9.h"enum { OMODE = 0x7, /* Topen/Tcreate mode */};enum { PermX = 1, PermW = 2, PermR = 4,};static char EPermission[] = "permission denied";static intpermFile(File* file, Fid* fid, int perm){ char *u; DirEntry de; if(!fileGetDir(file, &de)) return -1; /* * User none only gets other permissions. */ if(strcmp(fid->uname, unamenone) != 0){ /* * There is only one uid<->uname mapping * and it's already cached in the Fid, but * it might have changed during the lifetime * if this Fid. */ if((u = unameByUid(de.uid)) != nil){ if(strcmp(fid->uname, u) == 0 && ((perm<<6) & de.mode)){ vtMemFree(u); deCleanup(&de); return 1; } vtMemFree(u); } if(groupMember(de.gid, fid->uname) && ((perm<<3) & de.mode)){ deCleanup(&de); return 1; } } if(perm & de.mode){ if(perm == PermX && (de.mode & ModeDir)){ deCleanup(&de); return 1; } if(!groupMember(uidnoworld, fid->uname)){ deCleanup(&de); return 1; } } if(fsysNoPermCheck(fid->fsys) || (fid->con->flags&ConNoPermCheck)){ deCleanup(&de); return 1; } vtSetError(EPermission); deCleanup(&de); return 0;}static intpermFid(Fid* fid, int p){ return permFile(fid->file, fid, p);}static intpermParent(Fid* fid, int p){ int r; File *parent; parent = fileGetParent(fid->file); r = permFile(parent, fid, p); fileDecRef(parent); return r;}intvalidFileName(char* name){ char *p; if(name == nil || name[0] == '\0'){ vtSetError("no file name"); return 0; } if(name[0] == '.'){ if(name[1] == '\0' || (name[1] == '.' && name[2] == '\0')){ vtSetError(". and .. illegal as file name"); return 0; } } for(p = name; *p != '\0'; p++){ if((*p & 0xFF) < 040){ vtSetError("bad character in file name"); return 0; } } return 1;}static intrTwstat(Msg* m){ Dir dir; Fid *fid; ulong mode, oldmode; DirEntry de; char *gid, *strs, *uid; int gl, op, retval, tsync, wstatallow; if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil) return 0; gid = uid = nil; retval = 0; if(strcmp(fid->uname, unamenone) == 0 || (fid->qid.type & QTAUTH)){ vtSetError(EPermission); goto error0; } if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){ vtSetError("read-only filesystem"); goto error0; } if(!fileGetDir(fid->file, &de)) goto error0; strs = vtMemAlloc(m->t.nstat); if(convM2D(m->t.stat, m->t.nstat, &dir, strs) == 0){ vtSetError("wstat -- protocol botch"); goto error; } /* * 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. */ tsync = 1; if(dir.qid.path != ~0){ if(dir.qid.path != de.qid){ vtSetError("wstat -- attempt to change qid.path"); goto error; } tsync = 0; } if(dir.qid.vers != ~0){ if(dir.qid.vers != de.mcount){ vtSetError("wstat -- attempt to change qid.vers"); goto error; } tsync = 0; } if(dir.muid != nil && *dir.muid != '\0'){ if((uid = uidByUname(dir.muid)) == nil){ vtSetError("wstat -- unknown muid"); goto error; } if(strcmp(uid, de.mid) != 0){ vtSetError("wstat -- attempt to change muid"); goto error; } vtMemFree(uid); uid = nil; tsync = 0; } /* * Check .qid.type and .mode agree if neither is defaulted. */ if(dir.qid.type != (uchar)~0 && dir.mode != ~0){ if(dir.qid.type != ((dir.mode>>24) & 0xFF)){ vtSetError("wstat -- qid.type/mode mismatch"); goto error; } } op = 0; oldmode = de.mode; if(dir.qid.type != (uchar)~0 || dir.mode != ~0){ /* * .qid.type or .mode isn't defaulted, check for unknown bits. */ if(dir.mode == ~0) dir.mode = (dir.qid.type<<24)|(de.mode & 0777); if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){ vtSetError("wstat -- unknown bits in qid.type/mode"); goto error; } /* * Synthesise a mode to check against the current settings. */ mode = dir.mode & 0777; if(dir.mode & DMEXCL) mode |= ModeExclusive; if(dir.mode & DMAPPEND) mode |= ModeAppend; if(dir.mode & DMDIR) mode |= ModeDir; if(dir.mode & DMTMP) mode |= ModeTemporary; if((de.mode^mode) & ModeDir){ vtSetError("wstat -- attempt to change directory bit"); goto error; } if((de.mode & (ModeAppend|ModeExclusive|ModeTemporary|0777)) != mode){ de.mode &= ~(ModeAppend|ModeExclusive|ModeTemporary|0777); de.mode |= mode; op = 1; } tsync = 0; } if(dir.mtime != ~0){ if(dir.mtime != de.mtime){ de.mtime = dir.mtime; op = 1; } tsync = 0; } if(dir.length != ~0){ if(dir.length != de.size){ /* * Cannot change length on append-only files. * If we're changing the append bit, it's okay. */ if(de.mode & oldmode & ModeAppend){ vtSetError("wstat -- attempt to change length of append-only file"); goto error; } if(de.mode & ModeDir){ vtSetError("wstat -- attempt to change length of directory"); goto error; } de.size = dir.length; 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. */ if(dir.gid != nil && *dir.gid != '\0'){ if((gid = uidByUname(dir.gid)) == nil){ vtSetError("wstat -- unknown gid"); goto error; } tsync = 0; } else gid = vtStrDup(de.gid); wstatallow = (fsysWstatAllow(fid->fsys) || (m->con->flags&ConWstatAllow)); /* * 'Gl' counts whether neither, one or both groups are led. */ gl = groupLeader(gid, fid->uname) != 0; gl += groupLeader(de.gid, fid->uname) != 0; if(op && !wstatallow){ if(strcmp(fid->uid, de.uid) != 0 && !gl){ vtSetError("wstat -- not owner or group leader"); goto error; } } /* * Check for permission to change group, must be * either owner and in new group or leader of both groups. * If gid is nil here then */ if(strcmp(gid, de.gid) != 0){ if(!wstatallow && !(strcmp(fid->uid, de.uid) == 0 && groupMember(gid, fid->uname)) && !(gl == 2)){ vtSetError("wstat -- not owner and not group leaders"); goto error; } vtMemFree(de.gid); de.gid = gid; gid = nil; op = 1; tsync = 0; } /* * Rename. * Check .name is valid and different to the current. * If so, check write permission in parent. */ if(dir.name != nil && *dir.name != '\0'){ if(!validFileName(dir.name)) goto error; if(strcmp(dir.name, de.elem) != 0){ if(permParent(fid, PermW) <= 0) goto error; vtMemFree(de.elem); de.elem = vtStrDup(dir.name); op = 1; } tsync = 0; } /* * Check for permission to change owner - must be god. */ if(dir.uid != nil && *dir.uid != '\0'){ if((uid = uidByUname(dir.uid)) == nil){ vtSetError("wstat -- unknown uid"); goto error; } if(strcmp(uid, de.uid) != 0){ if(!wstatallow){ vtSetError("wstat -- not owner"); goto error; } if(strcmp(uid, uidnoworld) == 0){ vtSetError(EPermission); goto error; } vtMemFree(de.uid); de.uid = uid; uid = nil; op = 1; } tsync = 0; } if(op) retval = fileSetDir(fid->file, &de, fid->uid); else retval = 1; if(tsync){ /* * All values were defaulted, * make the state of the file exactly what it * claims to be before returning... */ USED(tsync); }error: deCleanup(&de); vtMemFree(strs); if(gid != nil) vtMemFree(gid); if(uid != nil) vtMemFree(uid);error0: fidPut(fid); return retval;};static intrTstat(Msg* m){ Dir dir; Fid *fid; DirEntry de; if((fid = fidGet(m->con, m->t.fid, 0)) == nil) return 0; if(fid->qid.type & QTAUTH){ memset(&dir, 0, sizeof(Dir)); dir.qid = fid->qid; dir.mode = DMAUTH; dir.atime = time(0L); dir.mtime = dir.atime; dir.length = 0; dir.name = "#¿"; dir.uid = fid->uname; dir.gid = fid->uname; dir.muid = fid->uname; if((m->r.nstat = convD2M(&dir, m->data, m->con->msize)) == 0){ vtSetError("stat QTAUTH botch"); fidPut(fid); return 0; } m->r.stat = m->data; fidPut(fid); return 1; } if(!fileGetDir(fid->file, &de)){ fidPut(fid); return 0; } fidPut(fid); /* * TODO: optimise this copy (in convS2M) away somehow. * This pettifoggery with m->data will do for the moment. */ m->r.nstat = dirDe2M(&de, m->data, m->con->msize); m->r.stat = m->data; deCleanup(&de); return 1;}static int_rTclunk(Fid* fid, int remove){ int rok; if(fid->excl) exclFree(fid); rok = 1; if(remove && !(fid->qid.type & QTAUTH)){ if((rok = permParent(fid, PermW)) > 0) rok = fileRemove(fid->file, fid->uid); } fidClunk(fid); return rok;}static intrTremove(Msg* m){ Fid *fid; if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil) return 0; return _rTclunk(fid, 1);}static intrTclunk(Msg* m){ Fid *fid; if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil) return 0; _rTclunk(fid, (fid->open & FidORclose)); return 1;}static intrTwrite(Msg* m){ Fid *fid; int count, n; if((fid = fidGet(m->con, m->t.fid, 0)) == nil) return 0; if(!(fid->open & FidOWrite)){ vtSetError("fid not open for write"); goto error; } count = m->t.count; if(count < 0 || count > m->con->msize-IOHDRSZ){ vtSetError("write count too big"); goto error; } if(m->t.offset < 0){ vtSetError("write offset negative"); goto error; } if(fid->excl != nil && !exclUpdate(fid)) goto error; if(fid->qid.type & QTDIR){ vtSetError("is a directory"); goto error; } else if(fid->qid.type & QTAUTH) n = authWrite(fid, m->t.data, count); else n = fileWrite(fid->file, m->t.data, count, m->t.offset, fid->uid); if(n < 0) goto error; m->r.count = n; fidPut(fid); return 1;error: fidPut(fid); return 0;}static intrTread(Msg* m){ Fid *fid; uchar *data; int count, n; if((fid = fidGet(m->con, m->t.fid, 0)) == nil) return 0; if(!(fid->open & FidORead)){ vtSetError("fid not open for read"); goto error; } count = m->t.count; if(count < 0 || count > m->con->msize-IOHDRSZ){ vtSetError("read count too big"); goto error; } if(m->t.offset < 0){ vtSetError("read offset negative"); goto error; } if(fid->excl != nil && !exclUpdate(fid)) goto error; /* * TODO: optimise this copy (in convS2M) away somehow. * This pettifoggery with m->data will do for the moment. */ data = m->data+IOHDRSZ; if(fid->qid.type & QTDIR) n = dirRead(fid, data, count, m->t.offset); else if(fid->qid.type & QTAUTH) n = authRead(fid, data, count); else n = fileRead(fid->file, data, count, m->t.offset); if(n < 0) goto error; m->r.count = n; m->r.data = (char*)data; fidPut(fid); return 1;error: fidPut(fid); return 0;}static intrTcreate(Msg* m){ Fid *fid; File *file; ulong mode; int omode, open, perm; if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil) return 0; if(fid->open){ vtSetError("fid open for I/O"); goto error; } if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){ vtSetError("read-only filesystem"); goto error; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -