⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 9p.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 2 页
字号:
#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 + -