xref: /plan9/sys/src/cmd/fossil/9p.c (revision fe853e2326f51910bb38886e9bfc22ecdef993d7)
15e96a66cSDavid du Colombier #include "stdinc.h"
25e96a66cSDavid du Colombier 
35e96a66cSDavid du Colombier #include "9.h"
45e96a66cSDavid du Colombier 
55e96a66cSDavid du Colombier enum {
65e96a66cSDavid du Colombier 	OMODE		= 0x7,		/* Topen/Tcreate mode */
75e96a66cSDavid du Colombier };
85e96a66cSDavid du Colombier 
95e96a66cSDavid du Colombier enum {
105e96a66cSDavid du Colombier 	PermX		= 1,
115e96a66cSDavid du Colombier 	PermW		= 2,
125e96a66cSDavid du Colombier 	PermR		= 4,
135e96a66cSDavid du Colombier };
145e96a66cSDavid du Colombier 
155e96a66cSDavid du Colombier static char EPermission[] = "permission denied";
165e96a66cSDavid du Colombier 
175e96a66cSDavid du Colombier static int
185e96a66cSDavid du Colombier permFile(File* file, Fid* fid, int perm)
195e96a66cSDavid du Colombier {
205e96a66cSDavid du Colombier 	char *u;
215e96a66cSDavid du Colombier 	DirEntry de;
225e96a66cSDavid du Colombier 
235e96a66cSDavid du Colombier 	if(!fileGetDir(file, &de))
245e96a66cSDavid du Colombier 		return 0;
255e96a66cSDavid du Colombier 
265e96a66cSDavid du Colombier 	/*
275e96a66cSDavid du Colombier 	 * User none only gets other permissions.
285e96a66cSDavid du Colombier 	 */
295e96a66cSDavid du Colombier 	if(strcmp(fid->uname, unamenone) != 0){
305e96a66cSDavid du Colombier 		/*
315e96a66cSDavid du Colombier 		 * There is only one uid<->uname mapping
325e96a66cSDavid du Colombier 		 * and it's already cached in the Fid, but
335e96a66cSDavid du Colombier 		 * it might have changed during the lifetime
345e96a66cSDavid du Colombier 		 * if this Fid.
355e96a66cSDavid du Colombier 		 */
365e96a66cSDavid du Colombier 		if((u = unameByUid(de.uid)) != nil){
375e96a66cSDavid du Colombier 			if(strcmp(fid->uname, u) == 0 && ((perm<<6) & de.mode)){
385e96a66cSDavid du Colombier 				vtMemFree(u);
395e96a66cSDavid du Colombier 				deCleanup(&de);
405e96a66cSDavid du Colombier 				return 1;
415e96a66cSDavid du Colombier 			}
425e96a66cSDavid du Colombier 			vtMemFree(u);
435e96a66cSDavid du Colombier 		}
445e96a66cSDavid du Colombier 		if(groupMember(de.gid, fid->uname) && ((perm<<3) & de.mode)){
455e96a66cSDavid du Colombier 			deCleanup(&de);
465e96a66cSDavid du Colombier 			return 1;
475e96a66cSDavid du Colombier 		}
485e96a66cSDavid du Colombier 	}
495e96a66cSDavid du Colombier 	if(perm & de.mode){
505e96a66cSDavid du Colombier 		if(perm == PermX && (de.mode & ModeDir)){
515e96a66cSDavid du Colombier 			deCleanup(&de);
525e96a66cSDavid du Colombier 			return 1;
535e96a66cSDavid du Colombier 		}
545e96a66cSDavid du Colombier 		if(!groupMember(uidnoworld, fid->uname)){
555e96a66cSDavid du Colombier 			deCleanup(&de);
565e96a66cSDavid du Colombier 			return 1;
575e96a66cSDavid du Colombier 		}
585e96a66cSDavid du Colombier 	}
59dc5a79c1SDavid du Colombier 	if(fsysNoPermCheck(fid->fsys) || fid->con->noperm){
605e96a66cSDavid du Colombier 		deCleanup(&de);
615e96a66cSDavid du Colombier 		return 1;
625e96a66cSDavid du Colombier 	}
635e96a66cSDavid du Colombier 	vtSetError(EPermission);
645e96a66cSDavid du Colombier 
655e96a66cSDavid du Colombier 	deCleanup(&de);
665e96a66cSDavid du Colombier 	return 0;
675e96a66cSDavid du Colombier }
685e96a66cSDavid du Colombier 
695e96a66cSDavid du Colombier static int
705e96a66cSDavid du Colombier permFid(Fid* fid, int p)
715e96a66cSDavid du Colombier {
725e96a66cSDavid du Colombier 	return permFile(fid->file, fid, p);
735e96a66cSDavid du Colombier }
745e96a66cSDavid du Colombier 
755e96a66cSDavid du Colombier static int
765e96a66cSDavid du Colombier permParent(Fid* fid, int p)
775e96a66cSDavid du Colombier {
785e96a66cSDavid du Colombier 	int r;
795e96a66cSDavid du Colombier 	File *parent;
805e96a66cSDavid du Colombier 
815e96a66cSDavid du Colombier 	parent = fileGetParent(fid->file);
825e96a66cSDavid du Colombier 	r = permFile(parent, fid, p);
835e96a66cSDavid du Colombier 	fileDecRef(parent);
845e96a66cSDavid du Colombier 
855e96a66cSDavid du Colombier 	return r;
865e96a66cSDavid du Colombier }
875e96a66cSDavid du Colombier 
885e96a66cSDavid du Colombier int
895e96a66cSDavid du Colombier validFileName(char* name)
905e96a66cSDavid du Colombier {
915e96a66cSDavid du Colombier 	char *p;
925e96a66cSDavid du Colombier 
935e96a66cSDavid du Colombier 	if(name == nil || name[0] == '\0'){
945e96a66cSDavid du Colombier 		vtSetError("no file name");
955e96a66cSDavid du Colombier 		return 0;
965e96a66cSDavid du Colombier 	}
975e96a66cSDavid du Colombier 	if(name[0] == '.'){
985e96a66cSDavid du Colombier 		if(name[1] == '\0' || (name[1] == '.' && name[2] == '\0')){
995e96a66cSDavid du Colombier 			vtSetError(". and .. illegal as file name");
1005e96a66cSDavid du Colombier 			return 0;
1015e96a66cSDavid du Colombier 		}
1025e96a66cSDavid du Colombier 	}
1035e96a66cSDavid du Colombier 
1045e96a66cSDavid du Colombier 	for(p = name; *p != '\0'; p++){
1055e96a66cSDavid du Colombier 		if((*p & 0xFF) < 040){
1065e96a66cSDavid du Colombier 			vtSetError("bad character in file name");
1075e96a66cSDavid du Colombier 			return 0;
1085e96a66cSDavid du Colombier 		}
1095e96a66cSDavid du Colombier 	}
1105e96a66cSDavid du Colombier 
1115e96a66cSDavid du Colombier 	return 1;
1125e96a66cSDavid du Colombier }
1135e96a66cSDavid du Colombier 
1145e96a66cSDavid du Colombier static int
1155e96a66cSDavid du Colombier rTwstat(Msg* m)
1165e96a66cSDavid du Colombier {
1175e96a66cSDavid du Colombier 	Dir dir;
1185e96a66cSDavid du Colombier 	Fid *fid;
1190827824dSDavid du Colombier 	ulong mode, oldmode;
1205e96a66cSDavid du Colombier 	DirEntry de;
1215e96a66cSDavid du Colombier 	char *gid, *strs, *uid;
1225e96a66cSDavid du Colombier 	int gl, op, retval, tsync;
1235e96a66cSDavid du Colombier 
1245e96a66cSDavid du Colombier 	if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
1255e96a66cSDavid du Colombier 		return 0;
1265e96a66cSDavid du Colombier 
1275e96a66cSDavid du Colombier 	gid = uid = nil;
1285e96a66cSDavid du Colombier 	retval = 0;
1295e96a66cSDavid du Colombier 
1305e96a66cSDavid du Colombier 	if(strcmp(fid->uname, unamenone) == 0 || (fid->qid.type & QTAUTH)){
1315e96a66cSDavid du Colombier 		vtSetError(EPermission);
1325e96a66cSDavid du Colombier 		goto error0;
1335e96a66cSDavid du Colombier 	}
1345e96a66cSDavid du Colombier 	if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
1355e96a66cSDavid du Colombier 		vtSetError("read-only filesystem");
1365e96a66cSDavid du Colombier 		goto error0;
1375e96a66cSDavid du Colombier 	}
1385e96a66cSDavid du Colombier 
1395e96a66cSDavid du Colombier 	if(!fileGetDir(fid->file, &de))
1405e96a66cSDavid du Colombier 		goto error0;
1415e96a66cSDavid du Colombier 
1425e96a66cSDavid du Colombier 	strs = vtMemAlloc(m->t.nstat);
1435e96a66cSDavid du Colombier 	if(convM2D(m->t.stat, m->t.nstat, &dir, strs) == 0){
1445e96a66cSDavid du Colombier 		vtSetError("wstat -- protocol botch");
1455e96a66cSDavid du Colombier 		goto error;
1465e96a66cSDavid du Colombier 	}
1475e96a66cSDavid du Colombier 
1485e96a66cSDavid du Colombier 	/*
1495e96a66cSDavid du Colombier 	 * Run through each of the (sub-)fields in the provided Dir
1505e96a66cSDavid du Colombier 	 * checking for validity and whether it's a default:
1515e96a66cSDavid du Colombier 	 * .type, .dev and .atime are completely ignored and not checked;
1525e96a66cSDavid du Colombier 	 * .qid.path, .qid.vers and .muid are checked for validity but
1535e96a66cSDavid du Colombier 	 * any attempt to change them is an error.
1545e96a66cSDavid du Colombier 	 * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can
1555e96a66cSDavid du Colombier 	 * possibly be changed.
1565e96a66cSDavid du Colombier 	 *
1575e96a66cSDavid du Colombier 	 * 'Op' flags there are changed fields, i.e. it's not a no-op.
1585e96a66cSDavid du Colombier 	 * 'Tsync' flags all fields are defaulted.
1595e96a66cSDavid du Colombier 	 */
1605e96a66cSDavid du Colombier 	tsync = 1;
1615e96a66cSDavid du Colombier 	if(dir.qid.path != ~0){
1625e96a66cSDavid du Colombier 		if(dir.qid.path != de.qid){
1635e96a66cSDavid du Colombier 			vtSetError("wstat -- attempt to change qid.path");
1645e96a66cSDavid du Colombier 			goto error;
1655e96a66cSDavid du Colombier 		}
1665e96a66cSDavid du Colombier 		tsync = 0;
1675e96a66cSDavid du Colombier 	}
1685e96a66cSDavid du Colombier 	if(dir.qid.vers != ~0){
1695e96a66cSDavid du Colombier 		if(dir.qid.vers != de.mcount){
1705e96a66cSDavid du Colombier 			vtSetError("wstat -- attempt to change qid.vers");
1715e96a66cSDavid du Colombier 			goto error;
1725e96a66cSDavid du Colombier 		}
1735e96a66cSDavid du Colombier 		tsync = 0;
1745e96a66cSDavid du Colombier 	}
1755e96a66cSDavid du Colombier 	if(dir.muid != nil && *dir.muid != '\0'){
1765e96a66cSDavid du Colombier 		if((uid = uidByUname(dir.muid)) == nil){
1775e96a66cSDavid du Colombier 			vtSetError("wstat -- unknown muid");
1785e96a66cSDavid du Colombier 			goto error;
1795e96a66cSDavid du Colombier 		}
1805e96a66cSDavid du Colombier 		if(strcmp(uid, de.mid) != 0){
1815e96a66cSDavid du Colombier 			vtSetError("wstat -- attempt to change muid");
1825e96a66cSDavid du Colombier 			goto error;
1835e96a66cSDavid du Colombier 		}
1845e96a66cSDavid du Colombier 		vtMemFree(uid);
1855e96a66cSDavid du Colombier 		uid = nil;
1865e96a66cSDavid du Colombier 		tsync = 0;
1875e96a66cSDavid du Colombier 	}
1885e96a66cSDavid du Colombier 
1895e96a66cSDavid du Colombier 	/*
1905e96a66cSDavid du Colombier 	 * Check .qid.type and .mode agree if neither is defaulted.
1915e96a66cSDavid du Colombier 	 */
1925e96a66cSDavid du Colombier 	if(dir.qid.type != (uchar)~0 && dir.mode != ~0){
1935e96a66cSDavid du Colombier 		if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
1945e96a66cSDavid du Colombier 			vtSetError("wstat -- qid.type/mode mismatch");
1955e96a66cSDavid du Colombier 			goto error;
1965e96a66cSDavid du Colombier 		}
1975e96a66cSDavid du Colombier 	}
1985e96a66cSDavid du Colombier 
1995e96a66cSDavid du Colombier 	op = 0;
2005e96a66cSDavid du Colombier 
201fb7f0c93SDavid du Colombier 	oldmode = de.mode;
2025e96a66cSDavid du Colombier 	if(dir.qid.type != (uchar)~0 || dir.mode != ~0){
2035e96a66cSDavid du Colombier 		/*
2045e96a66cSDavid du Colombier 		 * .qid.type or .mode isn't defaulted, check for unknown bits.
2055e96a66cSDavid du Colombier 		 */
2065e96a66cSDavid du Colombier 		if(dir.mode == ~0)
2075e96a66cSDavid du Colombier 			dir.mode = (dir.qid.type<<24)|(de.mode & 0777);
208*fe853e23SDavid du Colombier 		if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){
2095e96a66cSDavid du Colombier 			vtSetError("wstat -- unknown bits in qid.type/mode");
2105e96a66cSDavid du Colombier 			goto error;
2115e96a66cSDavid du Colombier 		}
2125e96a66cSDavid du Colombier 
2135e96a66cSDavid du Colombier 		/*
2145e96a66cSDavid du Colombier 		 * Synthesise a mode to check against the current settings.
2155e96a66cSDavid du Colombier 		 */
2165e96a66cSDavid du Colombier 		mode = dir.mode & 0777;
2175e96a66cSDavid du Colombier 		if(dir.mode & DMEXCL)
2185e96a66cSDavid du Colombier 			mode |= ModeExclusive;
2195e96a66cSDavid du Colombier 		if(dir.mode & DMAPPEND)
2205e96a66cSDavid du Colombier 			mode |= ModeAppend;
2215e96a66cSDavid du Colombier 		if(dir.mode & DMDIR)
2225e96a66cSDavid du Colombier 			mode |= ModeDir;
223*fe853e23SDavid du Colombier 		if(dir.mode & DMTMP)
224*fe853e23SDavid du Colombier 			mode |= ModeTemporary;
2255e96a66cSDavid du Colombier 
2265e96a66cSDavid du Colombier 		if((de.mode^mode) & ModeDir){
2275e96a66cSDavid du Colombier 			vtSetError("wstat -- attempt to change directory bit");
2285e96a66cSDavid du Colombier 			goto error;
2295e96a66cSDavid du Colombier 		}
2305e96a66cSDavid du Colombier 
231*fe853e23SDavid du Colombier 		if((de.mode & (ModeAppend|ModeExclusive|ModeTemporary|0777)) != mode){
232*fe853e23SDavid du Colombier 			de.mode &= ~(ModeAppend|ModeExclusive|ModeTemporary|0777);
2335e96a66cSDavid du Colombier 			de.mode |= mode;
2345e96a66cSDavid du Colombier 			op = 1;
2355e96a66cSDavid du Colombier 		}
2365e96a66cSDavid du Colombier 		tsync = 0;
2375e96a66cSDavid du Colombier 	}
2385e96a66cSDavid du Colombier 
2395e96a66cSDavid du Colombier 	if(dir.mtime != ~0){
2405e96a66cSDavid du Colombier 		if(dir.mtime != de.mtime){
2415e96a66cSDavid du Colombier 			de.mtime = dir.mtime;
2425e96a66cSDavid du Colombier 			op = 1;
2435e96a66cSDavid du Colombier 		}
2445e96a66cSDavid du Colombier 		tsync = 0;
2455e96a66cSDavid du Colombier 	}
2465e96a66cSDavid du Colombier 
2475e96a66cSDavid du Colombier 	if(dir.length != ~0){
248f5736e95SDavid du Colombier 		if(dir.length != de.size){
2490827824dSDavid du Colombier 			/*
2500827824dSDavid du Colombier 			 * Cannot change length on append-only files.
2510827824dSDavid du Colombier 			 * If we're changing the append bit, it's okay.
2520827824dSDavid du Colombier 			 */
253fb7f0c93SDavid du Colombier 			if(de.mode & oldmode & ModeAppend){
2540827824dSDavid du Colombier 				vtSetError("wstat -- attempt to change length of append-only file");
2550827824dSDavid du Colombier 				goto error;
2560827824dSDavid du Colombier 			}
2575e96a66cSDavid du Colombier 			if(de.mode & ModeDir){
2585e96a66cSDavid du Colombier 				vtSetError("wstat -- attempt to change length of directory");
2595e96a66cSDavid du Colombier 				goto error;
2605e96a66cSDavid du Colombier 			}
2615e96a66cSDavid du Colombier 			de.size = dir.length;
2625e96a66cSDavid du Colombier 			op = 1;
2635e96a66cSDavid du Colombier 		}
2645e96a66cSDavid du Colombier 		tsync = 0;
2655e96a66cSDavid du Colombier 	}
2665e96a66cSDavid du Colombier 
2675e96a66cSDavid du Colombier 	/*
2685e96a66cSDavid du Colombier 	 * Check for permission to change .mode, .mtime or .length,
2695e96a66cSDavid du Colombier 	 * must be owner or leader of either group, for which test gid
2705e96a66cSDavid du Colombier 	 * is needed; permission checks on gid will be done later.
2715e96a66cSDavid du Colombier 	 */
2725e96a66cSDavid du Colombier 	if(dir.gid != nil && *dir.gid != '\0'){
2735e96a66cSDavid du Colombier 		if((gid = uidByUname(dir.gid)) == nil){
2745e96a66cSDavid du Colombier 			vtSetError("wstat -- unknown gid");
2755e96a66cSDavid du Colombier 			goto error;
2765e96a66cSDavid du Colombier 		}
2775e96a66cSDavid du Colombier 		tsync = 0;
2785e96a66cSDavid du Colombier 	}
2795e96a66cSDavid du Colombier 	else
2805e96a66cSDavid du Colombier 		gid = vtStrDup(de.gid);
2815e96a66cSDavid du Colombier 
2825e96a66cSDavid du Colombier 	/*
2835e96a66cSDavid du Colombier 	 * 'Gl' counts whether neither, one or both groups are led.
2845e96a66cSDavid du Colombier 	 */
2855e96a66cSDavid du Colombier 	gl = groupLeader(gid, fid->uname) != 0;
2865e96a66cSDavid du Colombier 	gl += groupLeader(de.gid, fid->uname) != 0;
2875e96a66cSDavid du Colombier 
288d58da526SDavid du Colombier 	if(op && !(fsysWstatAllow(fid->fsys) || m->con->wstatallow)){
2895e96a66cSDavid du Colombier 		if(strcmp(fid->uid, de.uid) != 0 && !gl){
2905e96a66cSDavid du Colombier 			vtSetError("wstat -- not owner or group leader");
2915e96a66cSDavid du Colombier 			goto error;
2925e96a66cSDavid du Colombier 		}
2935e96a66cSDavid du Colombier 	}
2945e96a66cSDavid du Colombier 
2955e96a66cSDavid du Colombier 	/*
2965e96a66cSDavid du Colombier 	 * Check for permission to change group, must be
2975e96a66cSDavid du Colombier 	 * either owner and in new group or leader of both groups.
2985e96a66cSDavid du Colombier 	 * If gid is nil here then
2995e96a66cSDavid du Colombier 	 */
3005e96a66cSDavid du Colombier 	if(strcmp(gid, de.gid) != 0){
301d58da526SDavid du Colombier 		if(!(fsysWstatAllow(fid->fsys) || m->con->wstatallow)
3025e96a66cSDavid du Colombier 		&& !(strcmp(fid->uid, de.uid) == 0 && groupMember(gid, fid->uname))
3035e96a66cSDavid du Colombier 		&& !(gl == 2)){
3045e96a66cSDavid du Colombier 			vtSetError("wstat -- not owner and not group leaders");
3055e96a66cSDavid du Colombier 			goto error;
3065e96a66cSDavid du Colombier 		}
3075e96a66cSDavid du Colombier 		vtMemFree(de.gid);
3085e96a66cSDavid du Colombier 		de.gid = gid;
3095e96a66cSDavid du Colombier 		gid = nil;
3105e96a66cSDavid du Colombier 		op = 1;
311fb7f0c93SDavid du Colombier 		tsync = 0;
3125e96a66cSDavid du Colombier 	}
3135e96a66cSDavid du Colombier 
3145e96a66cSDavid du Colombier 	/*
3155e96a66cSDavid du Colombier 	 * Rename.
3165e96a66cSDavid du Colombier 	 * Check .name is valid and different to the current.
3175e96a66cSDavid du Colombier 	 * If so, check write permission in parent.
3185e96a66cSDavid du Colombier 	 */
3195e96a66cSDavid du Colombier 	if(dir.name != nil && *dir.name != '\0'){
3205e96a66cSDavid du Colombier 		if(!validFileName(dir.name))
3215e96a66cSDavid du Colombier 			goto error;
3225e96a66cSDavid du Colombier 		if(strcmp(dir.name, de.elem) != 0){
3235e96a66cSDavid du Colombier 			if(!permParent(fid, PermW))
3245e96a66cSDavid du Colombier 				goto error;
3255e96a66cSDavid du Colombier 			vtMemFree(de.elem);
3265e96a66cSDavid du Colombier 			de.elem = vtStrDup(dir.name);
3275e96a66cSDavid du Colombier 			op = 1;
3285e96a66cSDavid du Colombier 		}
3295e96a66cSDavid du Colombier 		tsync = 0;
3305e96a66cSDavid du Colombier 	}
3315e96a66cSDavid du Colombier 
3325e96a66cSDavid du Colombier 	/*
3335e96a66cSDavid du Colombier 	 * Check for permission to change owner - must be god.
3345e96a66cSDavid du Colombier 	 */
3355e96a66cSDavid du Colombier 	if(dir.uid != nil && *dir.uid != '\0'){
3365e96a66cSDavid du Colombier 		if((uid = uidByUname(dir.uid)) == nil){
3375e96a66cSDavid du Colombier 			vtSetError("wstat -- unknown uid");
3385e96a66cSDavid du Colombier 			goto error;
3395e96a66cSDavid du Colombier 		}
3405e96a66cSDavid du Colombier 		if(strcmp(uid, de.uid) != 0){
341d58da526SDavid du Colombier 			if(!(fsysWstatAllow(fid->fsys) || m->con->wstatallow)){
3425e96a66cSDavid du Colombier 				vtSetError("wstat -- not owner");
3435e96a66cSDavid du Colombier 				goto error;
3445e96a66cSDavid du Colombier 			}
3455e96a66cSDavid du Colombier 			if(strcmp(uid, uidnoworld) == 0){
3465e96a66cSDavid du Colombier 				vtSetError(EPermission);
3475e96a66cSDavid du Colombier 				goto error;
3485e96a66cSDavid du Colombier 			}
3495e96a66cSDavid du Colombier 			vtMemFree(de.uid);
3505e96a66cSDavid du Colombier 			de.uid = uid;
3515e96a66cSDavid du Colombier 			uid = nil;
3525e96a66cSDavid du Colombier 			op = 1;
3535e96a66cSDavid du Colombier 		}
3545e96a66cSDavid du Colombier 		tsync = 0;
3555e96a66cSDavid du Colombier 	}
3565e96a66cSDavid du Colombier 
3575e96a66cSDavid du Colombier 	if(op)
3585e96a66cSDavid du Colombier 		retval = fileSetDir(fid->file, &de, fid->uid);
3595e96a66cSDavid du Colombier 	else
3605e96a66cSDavid du Colombier 		retval = 1;
3615e96a66cSDavid du Colombier 
3625e96a66cSDavid du Colombier 	if(tsync){
3635e96a66cSDavid du Colombier 		/*
3645e96a66cSDavid du Colombier 		 * All values were defaulted,
3655e96a66cSDavid du Colombier 		 * make the state of the file exactly what it
3665e96a66cSDavid du Colombier 		 * claims to be before returning...
3675e96a66cSDavid du Colombier 		 */
3685e96a66cSDavid du Colombier 		USED(tsync);
3695e96a66cSDavid du Colombier 	}
3705e96a66cSDavid du Colombier 
3715e96a66cSDavid du Colombier error:
3725e96a66cSDavid du Colombier 	deCleanup(&de);
3735e96a66cSDavid du Colombier 	vtMemFree(strs);
3745e96a66cSDavid du Colombier 	if(gid != nil)
3755e96a66cSDavid du Colombier 		vtMemFree(gid);
3765e96a66cSDavid du Colombier 	if(uid != nil)
3775e96a66cSDavid du Colombier 		vtMemFree(uid);
3785e96a66cSDavid du Colombier error0:
3795e96a66cSDavid du Colombier 	fidPut(fid);
3805e96a66cSDavid du Colombier 	return retval;
3815e96a66cSDavid du Colombier };
3825e96a66cSDavid du Colombier 
3835e96a66cSDavid du Colombier static int
3845e96a66cSDavid du Colombier rTstat(Msg* m)
3855e96a66cSDavid du Colombier {
3865e96a66cSDavid du Colombier 	Dir dir;
3875e96a66cSDavid du Colombier 	Fid *fid;
3885e96a66cSDavid du Colombier 	DirEntry de;
3895e96a66cSDavid du Colombier 
3905e96a66cSDavid du Colombier 	if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
3915e96a66cSDavid du Colombier 		return 0;
3925e96a66cSDavid du Colombier 	if(fid->qid.type & QTAUTH){
3935e96a66cSDavid du Colombier 		memset(&dir, 0, sizeof(Dir));
3945e96a66cSDavid du Colombier 		dir.qid = fid->qid;
3955e96a66cSDavid du Colombier 		dir.mode = DMAUTH;
3965e96a66cSDavid du Colombier 		dir.atime = time(0L);
3975e96a66cSDavid du Colombier 		dir.mtime = dir.atime;
3985e96a66cSDavid du Colombier 		dir.length = 0;
3995e96a66cSDavid du Colombier 		dir.name = "#¿";
4005e96a66cSDavid du Colombier 		dir.uid = fid->uname;
4015e96a66cSDavid du Colombier 		dir.gid = fid->uname;
4025e96a66cSDavid du Colombier 		dir.muid = fid->uname;
4035e96a66cSDavid du Colombier 
4045e96a66cSDavid du Colombier 		if((m->r.nstat = convD2M(&dir, m->data, m->con->msize)) == 0){
4055e96a66cSDavid du Colombier 			vtSetError("stat QTAUTH botch");
4065e96a66cSDavid du Colombier 			fidPut(fid);
4075e96a66cSDavid du Colombier 			return 0;
4085e96a66cSDavid du Colombier 		}
4095e96a66cSDavid du Colombier 		m->r.stat = m->data;
4105e96a66cSDavid du Colombier 
4115e96a66cSDavid du Colombier 		fidPut(fid);
4125e96a66cSDavid du Colombier 		return 1;
4135e96a66cSDavid du Colombier 	}
4145e96a66cSDavid du Colombier 	if(!fileGetDir(fid->file, &de)){
4155e96a66cSDavid du Colombier 		fidPut(fid);
4165e96a66cSDavid du Colombier 		return 0;
4175e96a66cSDavid du Colombier 	}
4185e96a66cSDavid du Colombier 	fidPut(fid);
4195e96a66cSDavid du Colombier 
4205e96a66cSDavid du Colombier 	/*
4215e96a66cSDavid du Colombier 	 * TODO: optimise this copy (in convS2M) away somehow.
4225e96a66cSDavid du Colombier 	 * This pettifoggery with m->data will do for the moment.
4235e96a66cSDavid du Colombier 	 */
4245e96a66cSDavid du Colombier 	m->r.nstat = dirDe2M(&de, m->data, m->con->msize);
4255e96a66cSDavid du Colombier 	m->r.stat = m->data;
4265e96a66cSDavid du Colombier 	deCleanup(&de);
4275e96a66cSDavid du Colombier 
4285e96a66cSDavid du Colombier 	return 1;
4295e96a66cSDavid du Colombier }
4305e96a66cSDavid du Colombier 
4315e96a66cSDavid du Colombier static int
4325e96a66cSDavid du Colombier _rTclunk(Fid* fid, int remove)
4335e96a66cSDavid du Colombier {
4345e96a66cSDavid du Colombier 	int rok;
4355e96a66cSDavid du Colombier 
4365e96a66cSDavid du Colombier 	if(fid->excl)
4375e96a66cSDavid du Colombier 		exclFree(fid);
4385e96a66cSDavid du Colombier 
4395e96a66cSDavid du Colombier 	rok = 1;
4405e96a66cSDavid du Colombier 	if(remove && !(fid->qid.type & QTAUTH)){
4415e96a66cSDavid du Colombier 		if((rok = permParent(fid, PermW)) != 0)
4425e96a66cSDavid du Colombier 			rok = fileRemove(fid->file, fid->uid);
4435e96a66cSDavid du Colombier 	}
4445e96a66cSDavid du Colombier 	fidClunk(fid);
4455e96a66cSDavid du Colombier 
4465e96a66cSDavid du Colombier 	return rok;
4475e96a66cSDavid du Colombier }
4485e96a66cSDavid du Colombier 
4495e96a66cSDavid du Colombier static int
4505e96a66cSDavid du Colombier rTremove(Msg* m)
4515e96a66cSDavid du Colombier {
4525e96a66cSDavid du Colombier 	Fid *fid;
4535e96a66cSDavid du Colombier 
4545e96a66cSDavid du Colombier 	if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
4555e96a66cSDavid du Colombier 		return 0;
4565e96a66cSDavid du Colombier 	return _rTclunk(fid, 1);
4575e96a66cSDavid du Colombier }
4585e96a66cSDavid du Colombier 
4595e96a66cSDavid du Colombier static int
4605e96a66cSDavid du Colombier rTclunk(Msg* m)
4615e96a66cSDavid du Colombier {
4625e96a66cSDavid du Colombier 	Fid *fid;
4635e96a66cSDavid du Colombier 
4645e96a66cSDavid du Colombier 	if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
4655e96a66cSDavid du Colombier 		return 0;
4665e96a66cSDavid du Colombier 	_rTclunk(fid, (fid->open & FidORclose));
4675e96a66cSDavid du Colombier 
4685e96a66cSDavid du Colombier 	return 1;
4695e96a66cSDavid du Colombier }
4705e96a66cSDavid du Colombier 
4715e96a66cSDavid du Colombier static int
4725e96a66cSDavid du Colombier rTwrite(Msg* m)
4735e96a66cSDavid du Colombier {
4745e96a66cSDavid du Colombier 	Fid *fid;
4755e96a66cSDavid du Colombier 	int count, n;
4765e96a66cSDavid du Colombier 
4775e96a66cSDavid du Colombier 	if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
4785e96a66cSDavid du Colombier 		return 0;
4795e96a66cSDavid du Colombier 	if(!(fid->open & FidOWrite)){
4805e96a66cSDavid du Colombier 		vtSetError("fid not open for write");
4815e96a66cSDavid du Colombier 		goto error;
4825e96a66cSDavid du Colombier 	}
4835e96a66cSDavid du Colombier 
4845e96a66cSDavid du Colombier 	count = m->t.count;
4855e96a66cSDavid du Colombier 	if(count < 0 || count > m->con->msize-IOHDRSZ){
4865e96a66cSDavid du Colombier 		vtSetError("write count too big");
4875e96a66cSDavid du Colombier 		goto error;
4885e96a66cSDavid du Colombier 	}
4895e96a66cSDavid du Colombier 	if(m->t.offset < 0){
4905e96a66cSDavid du Colombier 		vtSetError("write offset negative");
4915e96a66cSDavid du Colombier 		goto error;
4925e96a66cSDavid du Colombier 	}
4935e96a66cSDavid du Colombier 	if(fid->excl != nil && !exclUpdate(fid))
4945e96a66cSDavid du Colombier 		goto error;
4955e96a66cSDavid du Colombier 
4965e96a66cSDavid du Colombier 	if(fid->qid.type & QTDIR){
4975e96a66cSDavid du Colombier 		vtSetError("is a directory");
4985e96a66cSDavid du Colombier 		goto error;
4995e96a66cSDavid du Colombier 	}
5005e96a66cSDavid du Colombier 	else if(fid->qid.type & QTAUTH)
5015e96a66cSDavid du Colombier 		n = authWrite(fid, m->t.data, count);
5025e96a66cSDavid du Colombier 	else
5035e96a66cSDavid du Colombier 		n = fileWrite(fid->file, m->t.data, count, m->t.offset, fid->uid);
5045e96a66cSDavid du Colombier 	if(n < 0)
5055e96a66cSDavid du Colombier 		goto error;
5065e96a66cSDavid du Colombier 
5075e96a66cSDavid du Colombier 
5085e96a66cSDavid du Colombier 	m->r.count = n;
5095e96a66cSDavid du Colombier 
5105e96a66cSDavid du Colombier 	fidPut(fid);
5115e96a66cSDavid du Colombier 	return 1;
5125e96a66cSDavid du Colombier 
5135e96a66cSDavid du Colombier error:
5145e96a66cSDavid du Colombier 	fidPut(fid);
5155e96a66cSDavid du Colombier 	return 0;
5165e96a66cSDavid du Colombier }
5175e96a66cSDavid du Colombier 
5185e96a66cSDavid du Colombier static int
5195e96a66cSDavid du Colombier rTread(Msg* m)
5205e96a66cSDavid du Colombier {
5215e96a66cSDavid du Colombier 	Fid *fid;
5225e96a66cSDavid du Colombier 	uchar *data;
5235e96a66cSDavid du Colombier 	int count, n;
5245e96a66cSDavid du Colombier 
5255e96a66cSDavid du Colombier 	if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
5265e96a66cSDavid du Colombier 		return 0;
5275e96a66cSDavid du Colombier 	if(!(fid->open & FidORead)){
5285e96a66cSDavid du Colombier 		vtSetError("fid not open for read");
5295e96a66cSDavid du Colombier 		goto error;
5305e96a66cSDavid du Colombier 	}
5315e96a66cSDavid du Colombier 
5325e96a66cSDavid du Colombier 	count = m->t.count;
5335e96a66cSDavid du Colombier 	if(count < 0 || count > m->con->msize-IOHDRSZ){
5345e96a66cSDavid du Colombier 		vtSetError("read count too big");
5355e96a66cSDavid du Colombier 		goto error;
5365e96a66cSDavid du Colombier 	}
5375e96a66cSDavid du Colombier 	if(m->t.offset < 0){
5385e96a66cSDavid du Colombier 		vtSetError("read offset negative");
5395e96a66cSDavid du Colombier 		goto error;
5405e96a66cSDavid du Colombier 	}
5415e96a66cSDavid du Colombier 	if(fid->excl != nil && !exclUpdate(fid))
5425e96a66cSDavid du Colombier 		goto error;
5435e96a66cSDavid du Colombier 
5445e96a66cSDavid du Colombier 	/*
5455e96a66cSDavid du Colombier 	 * TODO: optimise this copy (in convS2M) away somehow.
5465e96a66cSDavid du Colombier 	 * This pettifoggery with m->data will do for the moment.
5475e96a66cSDavid du Colombier 	 */
5485e96a66cSDavid du Colombier 	data = m->data+IOHDRSZ;
5495e96a66cSDavid du Colombier 	if(fid->qid.type & QTDIR)
5505e96a66cSDavid du Colombier 		n = dirRead(fid, data, count, m->t.offset);
5515e96a66cSDavid du Colombier 	else if(fid->qid.type & QTAUTH)
5525e96a66cSDavid du Colombier 		n = authRead(fid, data, count);
5535e96a66cSDavid du Colombier 	else
5545e96a66cSDavid du Colombier 		n = fileRead(fid->file, data, count, m->t.offset);
5555e96a66cSDavid du Colombier 	if(n < 0)
5565e96a66cSDavid du Colombier 		goto error;
5575e96a66cSDavid du Colombier 
5585e96a66cSDavid du Colombier 	m->r.count = n;
5595e96a66cSDavid du Colombier 	m->r.data = (char*)data;
5605e96a66cSDavid du Colombier 
5615e96a66cSDavid du Colombier 	fidPut(fid);
5625e96a66cSDavid du Colombier 	return 1;
5635e96a66cSDavid du Colombier 
5645e96a66cSDavid du Colombier error:
5655e96a66cSDavid du Colombier 	fidPut(fid);
5665e96a66cSDavid du Colombier 	return 0;
5675e96a66cSDavid du Colombier }
5685e96a66cSDavid du Colombier 
5695e96a66cSDavid du Colombier static int
5705e96a66cSDavid du Colombier rTcreate(Msg* m)
5715e96a66cSDavid du Colombier {
5725e96a66cSDavid du Colombier 	Fid *fid;
5735e96a66cSDavid du Colombier 	File *file;
5745e96a66cSDavid du Colombier 	ulong mode;
5755e96a66cSDavid du Colombier 	int omode, open, perm;
5765e96a66cSDavid du Colombier 
5775e96a66cSDavid du Colombier 	if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
5785e96a66cSDavid du Colombier 		return 0;
5795e96a66cSDavid du Colombier 	if(fid->open){
5805e96a66cSDavid du Colombier 		vtSetError("fid open for I/O");
5815e96a66cSDavid du Colombier 		goto error;
5825e96a66cSDavid du Colombier 	}
5835e96a66cSDavid du Colombier 	if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
5845e96a66cSDavid du Colombier 		vtSetError("read-only filesystem");
5855e96a66cSDavid du Colombier 		goto error;
5865e96a66cSDavid du Colombier 	}
5875e96a66cSDavid du Colombier 	if(!fileIsDir(fid->file)){
5885e96a66cSDavid du Colombier 		vtSetError("not a directory");
5895e96a66cSDavid du Colombier 		goto error;
5905e96a66cSDavid du Colombier 	}
5915e96a66cSDavid du Colombier 	if(!permFid(fid, PermW))
5925e96a66cSDavid du Colombier 		goto error;
5935e96a66cSDavid du Colombier 	if(!validFileName(m->t.name))
5945e96a66cSDavid du Colombier 		goto error;
5955e96a66cSDavid du Colombier 	if(strcmp(fid->uid, uidnoworld) == 0){
5965e96a66cSDavid du Colombier 		vtSetError(EPermission);
5975e96a66cSDavid du Colombier 		goto error;
5985e96a66cSDavid du Colombier 	}
5995e96a66cSDavid du Colombier 
6005e96a66cSDavid du Colombier 	omode = m->t.mode & OMODE;
6015e96a66cSDavid du Colombier 	open = 0;
6025e96a66cSDavid du Colombier 
6035e96a66cSDavid du Colombier 	if(omode == OREAD || omode == ORDWR || omode == OEXEC)
6045e96a66cSDavid du Colombier 		open |= FidORead;
6055e96a66cSDavid du Colombier 	if(omode == OWRITE || omode == ORDWR)
6065e96a66cSDavid du Colombier 		open |= FidOWrite;
6075e96a66cSDavid du Colombier 	if((open & (FidOWrite|FidORead)) == 0){
6085e96a66cSDavid du Colombier 		vtSetError("unknown mode");
6095e96a66cSDavid du Colombier 		goto error;
6105e96a66cSDavid du Colombier 	}
6115e96a66cSDavid du Colombier 	if(m->t.perm & DMDIR){
6125e96a66cSDavid du Colombier 		if((m->t.mode & (ORCLOSE|OTRUNC)) || (open & FidOWrite)){
6135e96a66cSDavid du Colombier 			vtSetError("illegal mode");
6145e96a66cSDavid du Colombier 			goto error;
6155e96a66cSDavid du Colombier 		}
6165e96a66cSDavid du Colombier 		if(m->t.perm & DMAPPEND){
6175e96a66cSDavid du Colombier 			vtSetError("illegal perm");
6185e96a66cSDavid du Colombier 			goto error;
6195e96a66cSDavid du Colombier 		}
6205e96a66cSDavid du Colombier 	}
6215e96a66cSDavid du Colombier 
6225e96a66cSDavid du Colombier 	mode = fileGetMode(fid->file);
6235e96a66cSDavid du Colombier 	perm = m->t.perm;
6245e96a66cSDavid du Colombier 	if(m->t.perm & DMDIR)
6255e96a66cSDavid du Colombier 		perm &= ~0777|(mode & 0777);
6265e96a66cSDavid du Colombier 	else
6275e96a66cSDavid du Colombier 		perm &= ~0666|(mode & 0666);
6285e96a66cSDavid du Colombier 	mode = perm & 0777;
6295e96a66cSDavid du Colombier 	if(m->t.perm & DMDIR)
6305e96a66cSDavid du Colombier 		mode |= ModeDir;
6315e96a66cSDavid du Colombier 	if(m->t.perm & DMAPPEND)
6325e96a66cSDavid du Colombier 		mode |= ModeAppend;
6335e96a66cSDavid du Colombier 	if(m->t.perm & DMEXCL)
6345e96a66cSDavid du Colombier 		mode |= ModeExclusive;
635*fe853e23SDavid du Colombier 	if(m->t.perm & DMTMP)
636*fe853e23SDavid du Colombier 		mode |= ModeTemporary;
6375e96a66cSDavid du Colombier 
6385e96a66cSDavid du Colombier 	if((file = fileCreate(fid->file, m->t.name, mode, fid->uid)) == nil){
6395e96a66cSDavid du Colombier 		fidPut(fid);
6405e96a66cSDavid du Colombier 		return 0;
6415e96a66cSDavid du Colombier 	}
6425e96a66cSDavid du Colombier 	fileDecRef(fid->file);
6435e96a66cSDavid du Colombier 
64434e04225SDavid du Colombier 	fid->qid.vers = fileGetMcount(file);
64534e04225SDavid du Colombier 	fid->qid.path = fileGetId(file);
6465e96a66cSDavid du Colombier 	fid->file = file;
6475e96a66cSDavid du Colombier 	mode = fileGetMode(fid->file);
6485e96a66cSDavid du Colombier 	if(mode & ModeDir)
6495e96a66cSDavid du Colombier 		fid->qid.type = QTDIR;
6505e96a66cSDavid du Colombier 	else
6515e96a66cSDavid du Colombier 		fid->qid.type = QTFILE;
6525e96a66cSDavid du Colombier 	if(mode & ModeAppend)
6535e96a66cSDavid du Colombier 		fid->qid.type |= QTAPPEND;
6545e96a66cSDavid du Colombier 	if(mode & ModeExclusive){
6555e96a66cSDavid du Colombier 		fid->qid.type |= QTEXCL;
6565e96a66cSDavid du Colombier 		assert(exclAlloc(fid) != 0);
6575e96a66cSDavid du Colombier 	}
6585e96a66cSDavid du Colombier 	if(m->t.mode & ORCLOSE)
6595e96a66cSDavid du Colombier 		open |= FidORclose;
6605e96a66cSDavid du Colombier 	fid->open = open;
6615e96a66cSDavid du Colombier 
6625e96a66cSDavid du Colombier 	m->r.qid = fid->qid;
6635e96a66cSDavid du Colombier 	m->r.iounit = m->con->msize-IOHDRSZ;
6645e96a66cSDavid du Colombier 
6655e96a66cSDavid du Colombier 	fidPut(fid);
6665e96a66cSDavid du Colombier 	return 1;
6675e96a66cSDavid du Colombier 
6685e96a66cSDavid du Colombier error:
6695e96a66cSDavid du Colombier 	fidPut(fid);
6705e96a66cSDavid du Colombier 	return 0;
6715e96a66cSDavid du Colombier }
6725e96a66cSDavid du Colombier 
6735e96a66cSDavid du Colombier static int
6745e96a66cSDavid du Colombier rTopen(Msg* m)
6755e96a66cSDavid du Colombier {
6765e96a66cSDavid du Colombier 	Fid *fid;
6775e96a66cSDavid du Colombier 	int isdir, mode, omode, open, rofs;
6785e96a66cSDavid du Colombier 
6795e96a66cSDavid du Colombier 	if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
6805e96a66cSDavid du Colombier 		return 0;
6815e96a66cSDavid du Colombier 	if(fid->open){
6825e96a66cSDavid du Colombier 		vtSetError("fid open for I/O");
6835e96a66cSDavid du Colombier 		goto error;
6845e96a66cSDavid du Colombier 	}
6855e96a66cSDavid du Colombier 
6865e96a66cSDavid du Colombier 	isdir = fileIsDir(fid->file);
6875e96a66cSDavid du Colombier 	open = 0;
6885e96a66cSDavid du Colombier 	rofs = fileIsRoFs(fid->file) || !groupWriteMember(fid->uname);
6895e96a66cSDavid du Colombier 
6905e96a66cSDavid du Colombier 	if(m->t.mode & ORCLOSE){
6915e96a66cSDavid du Colombier 		if(isdir){
6925e96a66cSDavid du Colombier 			vtSetError("is a directory");
6935e96a66cSDavid du Colombier 			goto error;
6945e96a66cSDavid du Colombier 		}
6955e96a66cSDavid du Colombier 		if(rofs){
6965e96a66cSDavid du Colombier 			vtSetError("read-only filesystem");
6975e96a66cSDavid du Colombier 			goto error;
6985e96a66cSDavid du Colombier 		}
6995e96a66cSDavid du Colombier 		if(!permParent(fid, PermW))
7005e96a66cSDavid du Colombier 			goto error;
7015e96a66cSDavid du Colombier 
7025e96a66cSDavid du Colombier 		open |= FidORclose;
7035e96a66cSDavid du Colombier 	}
7045e96a66cSDavid du Colombier 
7055e96a66cSDavid du Colombier 	omode = m->t.mode & OMODE;
7065e96a66cSDavid du Colombier 	if(omode == OREAD || omode == ORDWR){
7075e96a66cSDavid du Colombier 		if(!permFid(fid, PermR))
7085e96a66cSDavid du Colombier 			goto error;
7095e96a66cSDavid du Colombier 		open |= FidORead;
7105e96a66cSDavid du Colombier 	}
7115e96a66cSDavid du Colombier 	if(omode == OWRITE || omode == ORDWR || (m->t.mode & OTRUNC)){
7125e96a66cSDavid du Colombier 		if(isdir){
7135e96a66cSDavid du Colombier 			vtSetError("is a directory");
7145e96a66cSDavid du Colombier 			goto error;
7155e96a66cSDavid du Colombier 		}
7165e96a66cSDavid du Colombier 		if(rofs){
7175e96a66cSDavid du Colombier 			vtSetError("read-only filesystem");
7185e96a66cSDavid du Colombier 			goto error;
7195e96a66cSDavid du Colombier 		}
7205e96a66cSDavid du Colombier 		if(!permFid(fid, PermW))
7215e96a66cSDavid du Colombier 			goto error;
7225e96a66cSDavid du Colombier 		open |= FidOWrite;
7235e96a66cSDavid du Colombier 	}
7245e96a66cSDavid du Colombier 	if(omode == OEXEC){
7255e96a66cSDavid du Colombier 		if(isdir){
7265e96a66cSDavid du Colombier 			vtSetError("is a directory");
7275e96a66cSDavid du Colombier 			goto error;
7285e96a66cSDavid du Colombier 		}
7295e96a66cSDavid du Colombier 		if(!permFid(fid, PermX))
7305e96a66cSDavid du Colombier 			goto error;
7315e96a66cSDavid du Colombier 		open |= FidORead;
7325e96a66cSDavid du Colombier 	}
7335e96a66cSDavid du Colombier 	if((open & (FidOWrite|FidORead)) == 0){
7345e96a66cSDavid du Colombier 		vtSetError("unknown mode");
7355e96a66cSDavid du Colombier 		goto error;
7365e96a66cSDavid du Colombier 	}
7375e96a66cSDavid du Colombier 
7385e96a66cSDavid du Colombier 	mode = fileGetMode(fid->file);
7395e96a66cSDavid du Colombier 	if((mode & ModeExclusive) && exclAlloc(fid) == 0)
7405e96a66cSDavid du Colombier 		goto error;
7415e96a66cSDavid du Colombier 
7425e96a66cSDavid du Colombier 	/*
7435e96a66cSDavid du Colombier 	 * Everything checks out, try to commit any changes.
7445e96a66cSDavid du Colombier 	 */
7455e96a66cSDavid du Colombier 	if((m->t.mode & OTRUNC) && !(mode & ModeAppend)){
7465e96a66cSDavid du Colombier 		if(!fileTruncate(fid->file, fid->uid))
7475e96a66cSDavid du Colombier 			goto error;
7485e96a66cSDavid du Colombier 		fid->qid.vers = fileGetMcount(fid->file);
7495e96a66cSDavid du Colombier 	}
7505e96a66cSDavid du Colombier 	if(isdir && fid->db != nil){
7515e96a66cSDavid du Colombier 		dirBufFree(fid->db);
7525e96a66cSDavid du Colombier 		fid->db = nil;
7535e96a66cSDavid du Colombier 	}
7545e96a66cSDavid du Colombier 
7555e96a66cSDavid du Colombier 	m->r.qid = fid->qid;
7565e96a66cSDavid du Colombier 	m->r.iounit = m->con->msize-IOHDRSZ;
7575e96a66cSDavid du Colombier 
7585e96a66cSDavid du Colombier 	fid->open = open;
7595e96a66cSDavid du Colombier 
7605e96a66cSDavid du Colombier 	fidPut(fid);
7615e96a66cSDavid du Colombier 	return 1;
7625e96a66cSDavid du Colombier 
7635e96a66cSDavid du Colombier error:
7645e96a66cSDavid du Colombier 	if(fid->excl != nil)
7655e96a66cSDavid du Colombier 		exclFree(fid);
7665e96a66cSDavid du Colombier 	fidPut(fid);
7675e96a66cSDavid du Colombier 	return 0;
7685e96a66cSDavid du Colombier }
7695e96a66cSDavid du Colombier 
7705e96a66cSDavid du Colombier static int
7715e96a66cSDavid du Colombier rTwalk(Msg* m)
7725e96a66cSDavid du Colombier {
7735e96a66cSDavid du Colombier 	Qid qid;
7745e96a66cSDavid du Colombier 	Fcall *r, *t;
7755e96a66cSDavid du Colombier 	int nwname, wlock;
7765e96a66cSDavid du Colombier 	File *file, *nfile;
7775e96a66cSDavid du Colombier 	Fid *fid, *ofid, *nfid;
7785e96a66cSDavid du Colombier 
7795e96a66cSDavid du Colombier 	t = &m->t;
7805e96a66cSDavid du Colombier 	if(t->fid == t->newfid)
7815e96a66cSDavid du Colombier 		wlock = FidFWlock;
7825e96a66cSDavid du Colombier 	else
7835e96a66cSDavid du Colombier 		wlock = 0;
7845e96a66cSDavid du Colombier 
7855e96a66cSDavid du Colombier 	/*
7865e96a66cSDavid du Colombier 	 * The file identified by t->fid must be valid in the
7875e96a66cSDavid du Colombier 	 * current session and must not have been opened for I/O
7885e96a66cSDavid du Colombier 	 * by an open or create message.
7895e96a66cSDavid du Colombier 	 */
7905e96a66cSDavid du Colombier 	if((ofid = fidGet(m->con, t->fid, wlock)) == nil)
7915e96a66cSDavid du Colombier 		return 0;
7925e96a66cSDavid du Colombier 	if(ofid->open){
7935e96a66cSDavid du Colombier 		vtSetError("file open for I/O");
7945e96a66cSDavid du Colombier 		fidPut(ofid);
7955e96a66cSDavid du Colombier 		return 0;
7965e96a66cSDavid du Colombier 	}
7975e96a66cSDavid du Colombier 
7985e96a66cSDavid du Colombier 	/*
7995e96a66cSDavid du Colombier 	 * If newfid is not the same as fid, allocate a new file;
8005e96a66cSDavid du Colombier 	 * a side effect is checking newfid is not already in use (error);
8015e96a66cSDavid du Colombier 	 * if there are no names to walk this will be equivalent to a
8025e96a66cSDavid du Colombier 	 * simple 'clone' operation.
8035e96a66cSDavid du Colombier 	 * It's a no-op if newfid is the same as fid and t->nwname is 0.
8045e96a66cSDavid du Colombier 	 */
8055e96a66cSDavid du Colombier 	nfid = nil;
8065e96a66cSDavid du Colombier 	if(t->fid != t->newfid){
8075e96a66cSDavid du Colombier 		nfid = fidGet(m->con, t->newfid, FidFWlock|FidFCreate);
8085e96a66cSDavid du Colombier 		if(nfid == nil){
809d58da526SDavid du Colombier 			vtSetError("fid in use");
8105e96a66cSDavid du Colombier 			fidPut(ofid);
8115e96a66cSDavid du Colombier 			return 0;
8125e96a66cSDavid du Colombier 		}
8135e96a66cSDavid du Colombier 		nfid->open = ofid->open & ~FidORclose;
8145e96a66cSDavid du Colombier 		nfid->file = fileIncRef(ofid->file);
8155e96a66cSDavid du Colombier 		nfid->qid = ofid->qid;
8165e96a66cSDavid du Colombier 		nfid->uid = vtStrDup(ofid->uid);
8175e96a66cSDavid du Colombier 		nfid->uname = vtStrDup(ofid->uname);
8185e96a66cSDavid du Colombier 		nfid->fsys = fsysIncRef(ofid->fsys);
8195e96a66cSDavid du Colombier 		fid = nfid;
8205e96a66cSDavid du Colombier 	}
8215e96a66cSDavid du Colombier 	else
8225e96a66cSDavid du Colombier 		fid = ofid;
8235e96a66cSDavid du Colombier 
8245e96a66cSDavid du Colombier 	r = &m->r;
8255e96a66cSDavid du Colombier 	r->nwqid = 0;
8265e96a66cSDavid du Colombier 
8275e96a66cSDavid du Colombier 	if(t->nwname == 0){
8285e96a66cSDavid du Colombier 		if(nfid != nil)
8295e96a66cSDavid du Colombier 			fidPut(nfid);
8305e96a66cSDavid du Colombier 		fidPut(ofid);
8315e96a66cSDavid du Colombier 
8325e96a66cSDavid du Colombier 		return 1;
8335e96a66cSDavid du Colombier 	}
8345e96a66cSDavid du Colombier 
8355e96a66cSDavid du Colombier 	file = fid->file;
8365e96a66cSDavid du Colombier 	fileIncRef(file);
8375e96a66cSDavid du Colombier 	qid = fid->qid;
8385e96a66cSDavid du Colombier 
8395e96a66cSDavid du Colombier 	for(nwname = 0; nwname < t->nwname; nwname++){
8405e96a66cSDavid du Colombier 		/*
8415e96a66cSDavid du Colombier 		 * Walked elements must represent a directory and
8425e96a66cSDavid du Colombier 		 * the implied user must have permission to search
8435e96a66cSDavid du Colombier 		 * the directory.  Walking .. is always allowed, so that
8445e96a66cSDavid du Colombier 		 * you can't walk into a directory and then not be able
8455e96a66cSDavid du Colombier 		 * to walk out of it.
8465e96a66cSDavid du Colombier 		 */
8475e96a66cSDavid du Colombier 		if(!(qid.type & QTDIR)){
8485e96a66cSDavid du Colombier 			vtSetError("not a directory");
8495e96a66cSDavid du Colombier 			break;
8505e96a66cSDavid du Colombier 		}
8515e96a66cSDavid du Colombier 		if(!permFile(file, fid, PermX) && strcmp(t->wname[nwname], "..") != 0)
8525e96a66cSDavid du Colombier 			break;
8535e96a66cSDavid du Colombier 		if((nfile = fileWalk(file, t->wname[nwname])) == nil)
8545e96a66cSDavid du Colombier 			break;
8555e96a66cSDavid du Colombier 		fileDecRef(file);
8565e96a66cSDavid du Colombier 		file = nfile;
8575e96a66cSDavid du Colombier 		qid.type = QTFILE;
8585e96a66cSDavid du Colombier 		if(fileIsDir(file))
8595e96a66cSDavid du Colombier 			qid.type = QTDIR;
8605e96a66cSDavid du Colombier 		qid.vers = fileGetMcount(file);
8615e96a66cSDavid du Colombier 		qid.path = fileGetId(file);
8625e96a66cSDavid du Colombier 		r->wqid[r->nwqid++] = qid;
8635e96a66cSDavid du Colombier 	}
8645e96a66cSDavid du Colombier 
8655e96a66cSDavid du Colombier 	if(nwname == t->nwname){
8665e96a66cSDavid du Colombier 		/*
8675e96a66cSDavid du Colombier 		 * Walked all elements. Update the target fid
8685e96a66cSDavid du Colombier 		 * from the temporary qid used during the walk,
8695e96a66cSDavid du Colombier 		 * and tidy up.
8705e96a66cSDavid du Colombier 		 */
8715e96a66cSDavid du Colombier 		fid->qid = r->wqid[r->nwqid-1];
8725e96a66cSDavid du Colombier 		fileDecRef(fid->file);
8735e96a66cSDavid du Colombier 		fid->file = file;
8745e96a66cSDavid du Colombier 
8755e96a66cSDavid du Colombier 		if(nfid != nil)
8765e96a66cSDavid du Colombier 			fidPut(nfid);
8775e96a66cSDavid du Colombier 
8785e96a66cSDavid du Colombier 		fidPut(ofid);
8795e96a66cSDavid du Colombier 		return 1;
8805e96a66cSDavid du Colombier 	}
8815e96a66cSDavid du Colombier 
8825e96a66cSDavid du Colombier 	/*
8835e96a66cSDavid du Colombier 	 * Didn't walk all elements, 'clunk' nfid if it exists
8845e96a66cSDavid du Colombier 	 * and leave fid untouched.
8855e96a66cSDavid du Colombier 	 * It's not an error if some of the elements were walked OK.
8865e96a66cSDavid du Colombier 	 */
8875e96a66cSDavid du Colombier 	fileDecRef(file);
8885e96a66cSDavid du Colombier 	if(nfid != nil)
8895e96a66cSDavid du Colombier 		fidClunk(nfid);
8905e96a66cSDavid du Colombier 
8915e96a66cSDavid du Colombier 	fidPut(ofid);
8925e96a66cSDavid du Colombier 	if(nwname == 0)
8935e96a66cSDavid du Colombier 		return 0;
8945e96a66cSDavid du Colombier 	return 1;
8955e96a66cSDavid du Colombier }
8965e96a66cSDavid du Colombier 
8975e96a66cSDavid du Colombier static int
8985e96a66cSDavid du Colombier rTflush(Msg* m)
8995e96a66cSDavid du Colombier {
90034e04225SDavid du Colombier 	if(m->t.oldtag != NOTAG)
90134e04225SDavid du Colombier 		msgFlush(m);
9025e96a66cSDavid du Colombier 	return 1;
9035e96a66cSDavid du Colombier }
9045e96a66cSDavid du Colombier 
9055e96a66cSDavid du Colombier static void
9065e96a66cSDavid du Colombier parseAname(char *aname, char **fsname, char **path)
9075e96a66cSDavid du Colombier {
9085e96a66cSDavid du Colombier 	char *s;
9095e96a66cSDavid du Colombier 
9105e96a66cSDavid du Colombier 	if(aname && aname[0])
9115e96a66cSDavid du Colombier 		s = vtStrDup(aname);
9125e96a66cSDavid du Colombier 	else
9135e96a66cSDavid du Colombier 		s = vtStrDup("main/active");
9145e96a66cSDavid du Colombier 	*fsname = s;
9155e96a66cSDavid du Colombier 	if((*path = strchr(s, '/')) != nil)
9165e96a66cSDavid du Colombier 		*(*path)++ = '\0';
9175e96a66cSDavid du Colombier 	else
9185e96a66cSDavid du Colombier 		*path = "";
9195e96a66cSDavid du Colombier }
9205e96a66cSDavid du Colombier 
9215e96a66cSDavid du Colombier static int
9225e96a66cSDavid du Colombier rTattach(Msg* m)
9235e96a66cSDavid du Colombier {
9245e96a66cSDavid du Colombier 	Fid *fid;
9255e96a66cSDavid du Colombier 	Fsys *fsys;
9265e96a66cSDavid du Colombier 	char *fsname, *path;
9275e96a66cSDavid du Colombier 
9285e96a66cSDavid du Colombier 	if((fid = fidGet(m->con, m->t.fid, FidFWlock|FidFCreate)) == nil)
9295e96a66cSDavid du Colombier 		return 0;
9305e96a66cSDavid du Colombier 
9315e96a66cSDavid du Colombier 	parseAname(m->t.aname, &fsname, &path);
9325e96a66cSDavid du Colombier 	if((fsys = fsysGet(fsname)) == nil){
9335e96a66cSDavid du Colombier 		fidClunk(fid);
9345e96a66cSDavid du Colombier 		vtMemFree(fsname);
9355e96a66cSDavid du Colombier 		return 0;
9365e96a66cSDavid du Colombier 	}
9375e96a66cSDavid du Colombier 	fid->fsys = fsys;
9385e96a66cSDavid du Colombier 
9395e96a66cSDavid du Colombier 	if(m->t.uname[0] != '\0')
9405e96a66cSDavid du Colombier 		fid->uname = vtStrDup(m->t.uname);
9415e96a66cSDavid du Colombier 	else
9425e96a66cSDavid du Colombier 		fid->uname = vtStrDup(unamenone);
9435e96a66cSDavid du Colombier 
944dc5a79c1SDavid du Colombier 	if(fsysNoAuthCheck(fsys) || m->con->noauth){
9455e96a66cSDavid du Colombier 		if((fid->uid = uidByUname(fid->uname)) == nil)
9465e96a66cSDavid du Colombier 			fid->uid = vtStrDup(unamenone);
9475e96a66cSDavid du Colombier 	}
9485e96a66cSDavid du Colombier 	else if(!authCheck(&m->t, fid, fsys)){
9495e96a66cSDavid du Colombier 		fidClunk(fid);
9505e96a66cSDavid du Colombier 		vtMemFree(fsname);
9515e96a66cSDavid du Colombier 		vtSetError("authentication failed");
9525e96a66cSDavid du Colombier 		return 0;
9535e96a66cSDavid du Colombier 	}
9545e96a66cSDavid du Colombier 
9555e96a66cSDavid du Colombier 	fsysFsRlock(fsys);
9565e96a66cSDavid du Colombier 	if((fid->file = fsysGetRoot(fsys, path)) == nil){
9575e96a66cSDavid du Colombier 		fsysFsRUnlock(fsys);
9585e96a66cSDavid du Colombier 		fidClunk(fid);
9595e96a66cSDavid du Colombier 		vtMemFree(fsname);
9605e96a66cSDavid du Colombier 		return 0;
9615e96a66cSDavid du Colombier 	}
9625e96a66cSDavid du Colombier 	fsysFsRUnlock(fsys);
9635e96a66cSDavid du Colombier 	vtMemFree(fsname);
9645e96a66cSDavid du Colombier 
9655e96a66cSDavid du Colombier 	fid->qid = (Qid){fileGetId(fid->file), 0, QTDIR};
9665e96a66cSDavid du Colombier 	m->r.qid = fid->qid;
9675e96a66cSDavid du Colombier 
9685e96a66cSDavid du Colombier 	fidPut(fid);
9695e96a66cSDavid du Colombier 	return 1;
9705e96a66cSDavid du Colombier }
9715e96a66cSDavid du Colombier 
9725e96a66cSDavid du Colombier static int
9735e96a66cSDavid du Colombier rTauth(Msg* m)
9745e96a66cSDavid du Colombier {
9755e96a66cSDavid du Colombier 	int afd;
9765e96a66cSDavid du Colombier 	Con *con;
9775e96a66cSDavid du Colombier 	Fid *afid;
9785e96a66cSDavid du Colombier 	Fsys *fsys;
9795e96a66cSDavid du Colombier 	char *fsname, *path;
9805e96a66cSDavid du Colombier 
9815e96a66cSDavid du Colombier 	parseAname(m->t.aname, &fsname, &path);
9825e96a66cSDavid du Colombier 	if((fsys = fsysGet(fsname)) == nil){
9835e96a66cSDavid du Colombier 		vtMemFree(fsname);
9845e96a66cSDavid du Colombier 		return 0;
9855e96a66cSDavid du Colombier 	}
9865e96a66cSDavid du Colombier 	vtMemFree(fsname);
9875e96a66cSDavid du Colombier 
988dc5a79c1SDavid du Colombier 	if(fsysNoAuthCheck(fsys) || m->con->noauth){
9895e96a66cSDavid du Colombier 		m->con->aok = 1;
9905e96a66cSDavid du Colombier 		vtSetError("authentication disabled");
9915e96a66cSDavid du Colombier 		fsysPut(fsys);
9925e96a66cSDavid du Colombier 		return 0;
9935e96a66cSDavid du Colombier 	}
9945e96a66cSDavid du Colombier 	if(strcmp(m->t.uname, unamenone) == 0){
9955e96a66cSDavid du Colombier 		vtSetError("user 'none' requires no authentication");
9965e96a66cSDavid du Colombier 		fsysPut(fsys);
9975e96a66cSDavid du Colombier 		return 0;
9985e96a66cSDavid du Colombier 	}
9995e96a66cSDavid du Colombier 
10005e96a66cSDavid du Colombier 	con = m->con;
10015e96a66cSDavid du Colombier 	if((afid = fidGet(con, m->t.afid, FidFWlock|FidFCreate)) == nil){
10025e96a66cSDavid du Colombier 		fsysPut(fsys);
10035e96a66cSDavid du Colombier 		return 0;
10045e96a66cSDavid du Colombier 	}
10055e96a66cSDavid du Colombier 	afid->fsys = fsys;
10065e96a66cSDavid du Colombier 
10075e96a66cSDavid du Colombier 	if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
10085e96a66cSDavid du Colombier 		vtSetError("can't open \"/mnt/factotum/rpc\"");
10095e96a66cSDavid du Colombier 		fidClunk(afid);
10105e96a66cSDavid du Colombier 		return 0;
10115e96a66cSDavid du Colombier 	}
10125e96a66cSDavid du Colombier 	if((afid->rpc = auth_allocrpc(afd)) == nil){
10135e96a66cSDavid du Colombier 		close(afd);
10145e96a66cSDavid du Colombier 		vtSetError("can't auth_allocrpc");
10155e96a66cSDavid du Colombier 		fidClunk(afid);
10165e96a66cSDavid du Colombier 		return 0;
10175e96a66cSDavid du Colombier 	}
10185e96a66cSDavid du Colombier 	if(auth_rpc(afid->rpc, "start", "proto=p9any role=server", 23) != ARok){
10195e96a66cSDavid du Colombier 		vtSetError("can't auth_rpc");
10205e96a66cSDavid du Colombier 		fidClunk(afid);
10215e96a66cSDavid du Colombier 		return 0;
10225e96a66cSDavid du Colombier 	}
10235e96a66cSDavid du Colombier 
10245e96a66cSDavid du Colombier 	afid->open = FidOWrite|FidORead;
10255e96a66cSDavid du Colombier 	afid->qid.type = QTAUTH;
10265e96a66cSDavid du Colombier 	afid->qid.path = m->t.afid;
10275e96a66cSDavid du Colombier 	afid->uname = vtStrDup(m->t.uname);
10285e96a66cSDavid du Colombier 
10295e96a66cSDavid du Colombier 	m->r.qid = afid->qid;
10305e96a66cSDavid du Colombier 
10315e96a66cSDavid du Colombier 	fidPut(afid);
10325e96a66cSDavid du Colombier 	return 1;
10335e96a66cSDavid du Colombier }
10345e96a66cSDavid du Colombier 
10355e96a66cSDavid du Colombier static int
10365e96a66cSDavid du Colombier rTversion(Msg* m)
10375e96a66cSDavid du Colombier {
10385e96a66cSDavid du Colombier 	int v;
10395e96a66cSDavid du Colombier 	Con *con;
10405e96a66cSDavid du Colombier 	Fcall *r, *t;
10415e96a66cSDavid du Colombier 
10425e96a66cSDavid du Colombier 	t = &m->t;
10435e96a66cSDavid du Colombier 	r = &m->r;
10445e96a66cSDavid du Colombier 	con = m->con;
10455e96a66cSDavid du Colombier 
10465e96a66cSDavid du Colombier 	vtLock(con->lock);
104734e04225SDavid du Colombier 	if(con->state != ConInit){
10485e96a66cSDavid du Colombier 		vtUnlock(con->lock);
10495e96a66cSDavid du Colombier 		vtSetError("Tversion: down");
10505e96a66cSDavid du Colombier 		return 0;
10515e96a66cSDavid du Colombier 	}
105234e04225SDavid du Colombier 	con->state = ConNew;
10535e96a66cSDavid du Colombier 
10545e96a66cSDavid du Colombier 	/*
10555e96a66cSDavid du Colombier 	 * Release the karma of past lives and suffering.
105634e04225SDavid du Colombier 	 * Should this be done before or after checking the
105734e04225SDavid du Colombier 	 * validity of the Tversion?
10585e96a66cSDavid du Colombier 	 */
105934e04225SDavid du Colombier 	fidClunkAll(con);
10605e96a66cSDavid du Colombier 
10615e96a66cSDavid du Colombier 	if(t->tag != NOTAG){
10625e96a66cSDavid du Colombier 		vtUnlock(con->lock);
10635e96a66cSDavid du Colombier 		vtSetError("Tversion: invalid tag");
10645e96a66cSDavid du Colombier 		return 0;
10655e96a66cSDavid du Colombier 	}
10665e96a66cSDavid du Colombier 
10675e96a66cSDavid du Colombier 	if(t->msize < 256){
10685e96a66cSDavid du Colombier 		vtUnlock(con->lock);
10695e96a66cSDavid du Colombier 		vtSetError("Tversion: message size too small");
10705e96a66cSDavid du Colombier 		return 0;
10715e96a66cSDavid du Colombier 	}
10725e96a66cSDavid du Colombier 	if(t->msize < con->msize)
10735e96a66cSDavid du Colombier 		r->msize = t->msize;
10745e96a66cSDavid du Colombier 	else
10755e96a66cSDavid du Colombier 		r->msize = con->msize;
10765e96a66cSDavid du Colombier 
10775e96a66cSDavid du Colombier 	r->version = "unknown";
10785e96a66cSDavid du Colombier 	if(t->version[0] == '9' && t->version[1] == 'P'){
10795e96a66cSDavid du Colombier 		/*
10805e96a66cSDavid du Colombier 		 * Currently, the only defined version
10815e96a66cSDavid du Colombier 		 * is "9P2000"; ignore any later versions.
10825e96a66cSDavid du Colombier           	 */
10835e96a66cSDavid du Colombier 		v = strtol(&t->version[2], 0, 10);
10845e96a66cSDavid du Colombier 		if(v >= 2000){
10855e96a66cSDavid du Colombier 			r->version = VERSION9P;
10865e96a66cSDavid du Colombier 			con->msize = r->msize;
108734e04225SDavid du Colombier 			con->state = ConUp;
10885e96a66cSDavid du Colombier 		}
10895e96a66cSDavid du Colombier 		else if(strcmp(t->version, "9PEoF") == 0){
10905e96a66cSDavid du Colombier 			r->version = "9PEoF";
10915e96a66cSDavid du Colombier 			con->msize = r->msize;
109234e04225SDavid du Colombier 			con->state = ConMoribund;
10935e96a66cSDavid du Colombier 		}
10945e96a66cSDavid du Colombier 	}
10955e96a66cSDavid du Colombier 	vtUnlock(con->lock);
10965e96a66cSDavid du Colombier 
10975e96a66cSDavid du Colombier 	return 1;
10985e96a66cSDavid du Colombier }
10995e96a66cSDavid du Colombier 
11005e96a66cSDavid du Colombier int (*rFcall[Tmax])(Msg*) = {
11015e96a66cSDavid du Colombier 	[Tversion]	= rTversion,
11025e96a66cSDavid du Colombier 	[Tauth]		= rTauth,
11035e96a66cSDavid du Colombier 	[Tattach]	= rTattach,
11045e96a66cSDavid du Colombier 	[Tflush]	= rTflush,
11055e96a66cSDavid du Colombier 	[Twalk]		= rTwalk,
11065e96a66cSDavid du Colombier 	[Topen]		= rTopen,
11075e96a66cSDavid du Colombier 	[Tcreate]	= rTcreate,
11085e96a66cSDavid du Colombier 	[Tread]		= rTread,
11095e96a66cSDavid du Colombier 	[Twrite]	= rTwrite,
11105e96a66cSDavid du Colombier 	[Tclunk]	= rTclunk,
11115e96a66cSDavid du Colombier 	[Tremove]	= rTremove,
11125e96a66cSDavid du Colombier 	[Tstat]		= rTstat,
11135e96a66cSDavid du Colombier 	[Twstat]	= rTwstat,
11145e96a66cSDavid du Colombier };
1115