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 + -
显示快捷键?