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
permFile(File * file,Fid * fid,int perm)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))
24f48db99cSDavid du Colombier return -1;
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)){
38d7aba6c3SDavid du Colombier vtfree(u);
395e96a66cSDavid du Colombier deCleanup(&de);
405e96a66cSDavid du Colombier return 1;
415e96a66cSDavid du Colombier }
42d7aba6c3SDavid du Colombier vtfree(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 }
592cca75a1SDavid du Colombier if(fsysNoPermCheck(fid->fsys) || (fid->con->flags&ConNoPermCheck)){
605e96a66cSDavid du Colombier deCleanup(&de);
615e96a66cSDavid du Colombier return 1;
625e96a66cSDavid du Colombier }
63d7aba6c3SDavid du Colombier werrstr(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
permFid(Fid * fid,int p)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
permParent(Fid * fid,int p)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
validFileName(char * name)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'){
94d7aba6c3SDavid du Colombier werrstr("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')){
99d7aba6c3SDavid du Colombier werrstr(". 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){
106d7aba6c3SDavid du Colombier werrstr("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
rTwstat(Msg * m)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;
1222cca75a1SDavid du Colombier int gl, op, retval, tsync, wstatallow;
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)){
131d7aba6c3SDavid du Colombier werrstr(EPermission);
1325e96a66cSDavid du Colombier goto error0;
1335e96a66cSDavid du Colombier }
1345e96a66cSDavid du Colombier if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
135d7aba6c3SDavid du Colombier werrstr("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
142d7aba6c3SDavid du Colombier strs = vtmalloc(m->t.nstat);
1435e96a66cSDavid du Colombier if(convM2D(m->t.stat, m->t.nstat, &dir, strs) == 0){
144d7aba6c3SDavid du Colombier werrstr("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){
163d7aba6c3SDavid du Colombier werrstr("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){
170d7aba6c3SDavid du Colombier werrstr("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){
177d7aba6c3SDavid du Colombier werrstr("wstat -- unknown muid");
1785e96a66cSDavid du Colombier goto error;
1795e96a66cSDavid du Colombier }
1805e96a66cSDavid du Colombier if(strcmp(uid, de.mid) != 0){
181d7aba6c3SDavid du Colombier werrstr("wstat -- attempt to change muid");
1825e96a66cSDavid du Colombier goto error;
1835e96a66cSDavid du Colombier }
184d7aba6c3SDavid du Colombier vtfree(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)){
194d7aba6c3SDavid du Colombier werrstr("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);
208fe853e23SDavid du Colombier if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){
209d7aba6c3SDavid du Colombier werrstr("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;
223fe853e23SDavid du Colombier if(dir.mode & DMTMP)
224fe853e23SDavid du Colombier mode |= ModeTemporary;
2255e96a66cSDavid du Colombier
2265e96a66cSDavid du Colombier if((de.mode^mode) & ModeDir){
227d7aba6c3SDavid du Colombier werrstr("wstat -- attempt to change directory bit");
2285e96a66cSDavid du Colombier goto error;
2295e96a66cSDavid du Colombier }
2305e96a66cSDavid du Colombier
231fe853e23SDavid du Colombier if((de.mode & (ModeAppend|ModeExclusive|ModeTemporary|0777)) != mode){
232fe853e23SDavid 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){
254d7aba6c3SDavid du Colombier werrstr("wstat -- attempt to change length of append-only file");
2550827824dSDavid du Colombier goto error;
2560827824dSDavid du Colombier }
2575e96a66cSDavid du Colombier if(de.mode & ModeDir){
258d7aba6c3SDavid du Colombier werrstr("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){
274d7aba6c3SDavid du Colombier werrstr("wstat -- unknown gid");
2755e96a66cSDavid du Colombier goto error;
2765e96a66cSDavid du Colombier }
2775e96a66cSDavid du Colombier tsync = 0;
2785e96a66cSDavid du Colombier }
2795e96a66cSDavid du Colombier else
280d7aba6c3SDavid du Colombier gid = vtstrdup(de.gid);
2815e96a66cSDavid du Colombier
2822cca75a1SDavid du Colombier wstatallow = (fsysWstatAllow(fid->fsys) || (m->con->flags&ConWstatAllow));
2832cca75a1SDavid du Colombier
2845e96a66cSDavid du Colombier /*
2855e96a66cSDavid du Colombier * 'Gl' counts whether neither, one or both groups are led.
2865e96a66cSDavid du Colombier */
2875e96a66cSDavid du Colombier gl = groupLeader(gid, fid->uname) != 0;
2885e96a66cSDavid du Colombier gl += groupLeader(de.gid, fid->uname) != 0;
2895e96a66cSDavid du Colombier
2902cca75a1SDavid du Colombier if(op && !wstatallow){
2915e96a66cSDavid du Colombier if(strcmp(fid->uid, de.uid) != 0 && !gl){
292d7aba6c3SDavid du Colombier werrstr("wstat -- not owner or group leader");
2935e96a66cSDavid du Colombier goto error;
2945e96a66cSDavid du Colombier }
2955e96a66cSDavid du Colombier }
2965e96a66cSDavid du Colombier
2975e96a66cSDavid du Colombier /*
2985e96a66cSDavid du Colombier * Check for permission to change group, must be
2995e96a66cSDavid du Colombier * either owner and in new group or leader of both groups.
3005e96a66cSDavid du Colombier * If gid is nil here then
3015e96a66cSDavid du Colombier */
3025e96a66cSDavid du Colombier if(strcmp(gid, de.gid) != 0){
3032cca75a1SDavid du Colombier if(!wstatallow
3045e96a66cSDavid du Colombier && !(strcmp(fid->uid, de.uid) == 0 && groupMember(gid, fid->uname))
3055e96a66cSDavid du Colombier && !(gl == 2)){
306d7aba6c3SDavid du Colombier werrstr("wstat -- not owner and not group leaders");
3075e96a66cSDavid du Colombier goto error;
3085e96a66cSDavid du Colombier }
309d7aba6c3SDavid du Colombier vtfree(de.gid);
3105e96a66cSDavid du Colombier de.gid = gid;
3115e96a66cSDavid du Colombier gid = nil;
3125e96a66cSDavid du Colombier op = 1;
313fb7f0c93SDavid du Colombier tsync = 0;
3145e96a66cSDavid du Colombier }
3155e96a66cSDavid du Colombier
3165e96a66cSDavid du Colombier /*
3175e96a66cSDavid du Colombier * Rename.
3185e96a66cSDavid du Colombier * Check .name is valid and different to the current.
3195e96a66cSDavid du Colombier * If so, check write permission in parent.
3205e96a66cSDavid du Colombier */
3215e96a66cSDavid du Colombier if(dir.name != nil && *dir.name != '\0'){
3225e96a66cSDavid du Colombier if(!validFileName(dir.name))
3235e96a66cSDavid du Colombier goto error;
3245e96a66cSDavid du Colombier if(strcmp(dir.name, de.elem) != 0){
325f48db99cSDavid du Colombier if(permParent(fid, PermW) <= 0)
3265e96a66cSDavid du Colombier goto error;
327d7aba6c3SDavid du Colombier vtfree(de.elem);
328d7aba6c3SDavid du Colombier de.elem = vtstrdup(dir.name);
3295e96a66cSDavid du Colombier op = 1;
3305e96a66cSDavid du Colombier }
3315e96a66cSDavid du Colombier tsync = 0;
3325e96a66cSDavid du Colombier }
3335e96a66cSDavid du Colombier
3345e96a66cSDavid du Colombier /*
3355e96a66cSDavid du Colombier * Check for permission to change owner - must be god.
3365e96a66cSDavid du Colombier */
3375e96a66cSDavid du Colombier if(dir.uid != nil && *dir.uid != '\0'){
3385e96a66cSDavid du Colombier if((uid = uidByUname(dir.uid)) == nil){
339d7aba6c3SDavid du Colombier werrstr("wstat -- unknown uid");
3405e96a66cSDavid du Colombier goto error;
3415e96a66cSDavid du Colombier }
3425e96a66cSDavid du Colombier if(strcmp(uid, de.uid) != 0){
3432cca75a1SDavid du Colombier if(!wstatallow){
344d7aba6c3SDavid du Colombier werrstr("wstat -- not owner");
3455e96a66cSDavid du Colombier goto error;
3465e96a66cSDavid du Colombier }
3475e96a66cSDavid du Colombier if(strcmp(uid, uidnoworld) == 0){
348d7aba6c3SDavid du Colombier werrstr(EPermission);
3495e96a66cSDavid du Colombier goto error;
3505e96a66cSDavid du Colombier }
351d7aba6c3SDavid du Colombier vtfree(de.uid);
3525e96a66cSDavid du Colombier de.uid = uid;
3535e96a66cSDavid du Colombier uid = nil;
3545e96a66cSDavid du Colombier op = 1;
3555e96a66cSDavid du Colombier }
3565e96a66cSDavid du Colombier tsync = 0;
3575e96a66cSDavid du Colombier }
3585e96a66cSDavid du Colombier
3595e96a66cSDavid du Colombier if(op)
3605e96a66cSDavid du Colombier retval = fileSetDir(fid->file, &de, fid->uid);
3615e96a66cSDavid du Colombier else
3625e96a66cSDavid du Colombier retval = 1;
3635e96a66cSDavid du Colombier
364*addada52SDavid du Colombier fid->qid.vers = fileGetMcount(fid->file);
365*addada52SDavid du Colombier m->r.qid = fid->qid;
366*addada52SDavid du Colombier m->r.iounit = m->con->msize-IOHDRSZ;
367*addada52SDavid du Colombier
3685e96a66cSDavid du Colombier if(tsync){
3695e96a66cSDavid du Colombier /*
3705e96a66cSDavid du Colombier * All values were defaulted,
3715e96a66cSDavid du Colombier * make the state of the file exactly what it
3725e96a66cSDavid du Colombier * claims to be before returning...
3735e96a66cSDavid du Colombier */
3745e96a66cSDavid du Colombier USED(tsync);
3755e96a66cSDavid du Colombier }
3765e96a66cSDavid du Colombier
3775e96a66cSDavid du Colombier error:
3785e96a66cSDavid du Colombier deCleanup(&de);
379d7aba6c3SDavid du Colombier vtfree(strs);
3805e96a66cSDavid du Colombier if(gid != nil)
381d7aba6c3SDavid du Colombier vtfree(gid);
3825e96a66cSDavid du Colombier if(uid != nil)
383d7aba6c3SDavid du Colombier vtfree(uid);
3845e96a66cSDavid du Colombier error0:
3855e96a66cSDavid du Colombier fidPut(fid);
3865e96a66cSDavid du Colombier return retval;
3875e96a66cSDavid du Colombier };
3885e96a66cSDavid du Colombier
3895e96a66cSDavid du Colombier static int
rTstat(Msg * m)3905e96a66cSDavid du Colombier rTstat(Msg* m)
3915e96a66cSDavid du Colombier {
3925e96a66cSDavid du Colombier Dir dir;
3935e96a66cSDavid du Colombier Fid *fid;
3945e96a66cSDavid du Colombier DirEntry de;
3955e96a66cSDavid du Colombier
3965e96a66cSDavid du Colombier if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
3975e96a66cSDavid du Colombier return 0;
3985e96a66cSDavid du Colombier if(fid->qid.type & QTAUTH){
3995e96a66cSDavid du Colombier memset(&dir, 0, sizeof(Dir));
4005e96a66cSDavid du Colombier dir.qid = fid->qid;
4015e96a66cSDavid du Colombier dir.mode = DMAUTH;
4025e96a66cSDavid du Colombier dir.atime = time(0L);
4035e96a66cSDavid du Colombier dir.mtime = dir.atime;
4045e96a66cSDavid du Colombier dir.length = 0;
4055e96a66cSDavid du Colombier dir.name = "#¿";
4065e96a66cSDavid du Colombier dir.uid = fid->uname;
4075e96a66cSDavid du Colombier dir.gid = fid->uname;
4085e96a66cSDavid du Colombier dir.muid = fid->uname;
4095e96a66cSDavid du Colombier
4105e96a66cSDavid du Colombier if((m->r.nstat = convD2M(&dir, m->data, m->con->msize)) == 0){
411d7aba6c3SDavid du Colombier werrstr("stat QTAUTH botch");
4125e96a66cSDavid du Colombier fidPut(fid);
4135e96a66cSDavid du Colombier return 0;
4145e96a66cSDavid du Colombier }
4155e96a66cSDavid du Colombier m->r.stat = m->data;
4165e96a66cSDavid du Colombier
4175e96a66cSDavid du Colombier fidPut(fid);
4185e96a66cSDavid du Colombier return 1;
4195e96a66cSDavid du Colombier }
4205e96a66cSDavid du Colombier if(!fileGetDir(fid->file, &de)){
4215e96a66cSDavid du Colombier fidPut(fid);
4225e96a66cSDavid du Colombier return 0;
4235e96a66cSDavid du Colombier }
4245e96a66cSDavid du Colombier fidPut(fid);
4255e96a66cSDavid du Colombier
4265e96a66cSDavid du Colombier /*
4275e96a66cSDavid du Colombier * TODO: optimise this copy (in convS2M) away somehow.
4285e96a66cSDavid du Colombier * This pettifoggery with m->data will do for the moment.
4295e96a66cSDavid du Colombier */
4305e96a66cSDavid du Colombier m->r.nstat = dirDe2M(&de, m->data, m->con->msize);
4315e96a66cSDavid du Colombier m->r.stat = m->data;
4325e96a66cSDavid du Colombier deCleanup(&de);
4335e96a66cSDavid du Colombier
4345e96a66cSDavid du Colombier return 1;
4355e96a66cSDavid du Colombier }
4365e96a66cSDavid du Colombier
4375e96a66cSDavid du Colombier static int
_rTclunk(Fid * fid,int remove)4385e96a66cSDavid du Colombier _rTclunk(Fid* fid, int remove)
4395e96a66cSDavid du Colombier {
4405e96a66cSDavid du Colombier int rok;
4415e96a66cSDavid du Colombier
4425e96a66cSDavid du Colombier if(fid->excl)
4435e96a66cSDavid du Colombier exclFree(fid);
4445e96a66cSDavid du Colombier
4455e96a66cSDavid du Colombier rok = 1;
4465e96a66cSDavid du Colombier if(remove && !(fid->qid.type & QTAUTH)){
447f48db99cSDavid du Colombier if((rok = permParent(fid, PermW)) > 0)
4485e96a66cSDavid du Colombier rok = fileRemove(fid->file, fid->uid);
4495e96a66cSDavid du Colombier }
4505e96a66cSDavid du Colombier fidClunk(fid);
4515e96a66cSDavid du Colombier
4525e96a66cSDavid du Colombier return rok;
4535e96a66cSDavid du Colombier }
4545e96a66cSDavid du Colombier
4555e96a66cSDavid du Colombier static int
rTremove(Msg * m)4565e96a66cSDavid du Colombier rTremove(Msg* m)
4575e96a66cSDavid du Colombier {
4585e96a66cSDavid du Colombier Fid *fid;
4595e96a66cSDavid du Colombier
4605e96a66cSDavid du Colombier if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
4615e96a66cSDavid du Colombier return 0;
4625e96a66cSDavid du Colombier return _rTclunk(fid, 1);
4635e96a66cSDavid du Colombier }
4645e96a66cSDavid du Colombier
4655e96a66cSDavid du Colombier static int
rTclunk(Msg * m)4665e96a66cSDavid du Colombier rTclunk(Msg* m)
4675e96a66cSDavid du Colombier {
4685e96a66cSDavid du Colombier Fid *fid;
4695e96a66cSDavid du Colombier
4705e96a66cSDavid du Colombier if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
4715e96a66cSDavid du Colombier return 0;
4725e96a66cSDavid du Colombier _rTclunk(fid, (fid->open & FidORclose));
4735e96a66cSDavid du Colombier
4745e96a66cSDavid du Colombier return 1;
4755e96a66cSDavid du Colombier }
4765e96a66cSDavid du Colombier
4775e96a66cSDavid du Colombier static int
rTwrite(Msg * m)4785e96a66cSDavid du Colombier rTwrite(Msg* m)
4795e96a66cSDavid du Colombier {
4805e96a66cSDavid du Colombier Fid *fid;
4815e96a66cSDavid du Colombier int count, n;
4825e96a66cSDavid du Colombier
4835e96a66cSDavid du Colombier if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
4845e96a66cSDavid du Colombier return 0;
4855e96a66cSDavid du Colombier if(!(fid->open & FidOWrite)){
486d7aba6c3SDavid du Colombier werrstr("fid not open for write");
4875e96a66cSDavid du Colombier goto error;
4885e96a66cSDavid du Colombier }
4895e96a66cSDavid du Colombier
4905e96a66cSDavid du Colombier count = m->t.count;
4915e96a66cSDavid du Colombier if(count < 0 || count > m->con->msize-IOHDRSZ){
492d7aba6c3SDavid du Colombier werrstr("write count too big");
4935e96a66cSDavid du Colombier goto error;
4945e96a66cSDavid du Colombier }
4955e96a66cSDavid du Colombier if(m->t.offset < 0){
496d7aba6c3SDavid du Colombier werrstr("write offset negative");
4975e96a66cSDavid du Colombier goto error;
4985e96a66cSDavid du Colombier }
4995e96a66cSDavid du Colombier if(fid->excl != nil && !exclUpdate(fid))
5005e96a66cSDavid du Colombier goto error;
5015e96a66cSDavid du Colombier
5025e96a66cSDavid du Colombier if(fid->qid.type & QTDIR){
503d7aba6c3SDavid du Colombier werrstr("is a directory");
5045e96a66cSDavid du Colombier goto error;
5055e96a66cSDavid du Colombier }
5065e96a66cSDavid du Colombier else if(fid->qid.type & QTAUTH)
5075e96a66cSDavid du Colombier n = authWrite(fid, m->t.data, count);
5085e96a66cSDavid du Colombier else
5095e96a66cSDavid du Colombier n = fileWrite(fid->file, m->t.data, count, m->t.offset, fid->uid);
5105e96a66cSDavid du Colombier if(n < 0)
5115e96a66cSDavid du Colombier goto error;
5125e96a66cSDavid du Colombier
5135e96a66cSDavid du Colombier
5145e96a66cSDavid du Colombier m->r.count = n;
5155e96a66cSDavid du Colombier
5165e96a66cSDavid du Colombier fidPut(fid);
5175e96a66cSDavid du Colombier return 1;
5185e96a66cSDavid du Colombier
5195e96a66cSDavid du Colombier error:
5205e96a66cSDavid du Colombier fidPut(fid);
5215e96a66cSDavid du Colombier return 0;
5225e96a66cSDavid du Colombier }
5235e96a66cSDavid du Colombier
5245e96a66cSDavid du Colombier static int
rTread(Msg * m)5255e96a66cSDavid du Colombier rTread(Msg* m)
5265e96a66cSDavid du Colombier {
5275e96a66cSDavid du Colombier Fid *fid;
5285e96a66cSDavid du Colombier uchar *data;
5295e96a66cSDavid du Colombier int count, n;
5305e96a66cSDavid du Colombier
5315e96a66cSDavid du Colombier if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
5325e96a66cSDavid du Colombier return 0;
5335e96a66cSDavid du Colombier if(!(fid->open & FidORead)){
534d7aba6c3SDavid du Colombier werrstr("fid not open for read");
5355e96a66cSDavid du Colombier goto error;
5365e96a66cSDavid du Colombier }
5375e96a66cSDavid du Colombier
5385e96a66cSDavid du Colombier count = m->t.count;
5395e96a66cSDavid du Colombier if(count < 0 || count > m->con->msize-IOHDRSZ){
540d7aba6c3SDavid du Colombier werrstr("read count too big");
5415e96a66cSDavid du Colombier goto error;
5425e96a66cSDavid du Colombier }
5435e96a66cSDavid du Colombier if(m->t.offset < 0){
544d7aba6c3SDavid du Colombier werrstr("read offset negative");
5455e96a66cSDavid du Colombier goto error;
5465e96a66cSDavid du Colombier }
5475e96a66cSDavid du Colombier if(fid->excl != nil && !exclUpdate(fid))
5485e96a66cSDavid du Colombier goto error;
5495e96a66cSDavid du Colombier
5505e96a66cSDavid du Colombier /*
5515e96a66cSDavid du Colombier * TODO: optimise this copy (in convS2M) away somehow.
5525e96a66cSDavid du Colombier * This pettifoggery with m->data will do for the moment.
5535e96a66cSDavid du Colombier */
5545e96a66cSDavid du Colombier data = m->data+IOHDRSZ;
5555e96a66cSDavid du Colombier if(fid->qid.type & QTDIR)
5565e96a66cSDavid du Colombier n = dirRead(fid, data, count, m->t.offset);
5575e96a66cSDavid du Colombier else if(fid->qid.type & QTAUTH)
5585e96a66cSDavid du Colombier n = authRead(fid, data, count);
5595e96a66cSDavid du Colombier else
5605e96a66cSDavid du Colombier n = fileRead(fid->file, data, count, m->t.offset);
5615e96a66cSDavid du Colombier if(n < 0)
5625e96a66cSDavid du Colombier goto error;
5635e96a66cSDavid du Colombier
5645e96a66cSDavid du Colombier m->r.count = n;
5655e96a66cSDavid du Colombier m->r.data = (char*)data;
5665e96a66cSDavid du Colombier
5675e96a66cSDavid du Colombier fidPut(fid);
5685e96a66cSDavid du Colombier return 1;
5695e96a66cSDavid du Colombier
5705e96a66cSDavid du Colombier error:
5715e96a66cSDavid du Colombier fidPut(fid);
5725e96a66cSDavid du Colombier return 0;
5735e96a66cSDavid du Colombier }
5745e96a66cSDavid du Colombier
5755e96a66cSDavid du Colombier static int
rTcreate(Msg * m)5765e96a66cSDavid du Colombier rTcreate(Msg* m)
5775e96a66cSDavid du Colombier {
5785e96a66cSDavid du Colombier Fid *fid;
5795e96a66cSDavid du Colombier File *file;
5805e96a66cSDavid du Colombier ulong mode;
5815e96a66cSDavid du Colombier int omode, open, perm;
5825e96a66cSDavid du Colombier
5835e96a66cSDavid du Colombier if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
5845e96a66cSDavid du Colombier return 0;
5855e96a66cSDavid du Colombier if(fid->open){
586d7aba6c3SDavid du Colombier werrstr("fid open for I/O");
5875e96a66cSDavid du Colombier goto error;
5885e96a66cSDavid du Colombier }
5895e96a66cSDavid du Colombier if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
590d7aba6c3SDavid du Colombier werrstr("read-only filesystem");
5915e96a66cSDavid du Colombier goto error;
5925e96a66cSDavid du Colombier }
5935e96a66cSDavid du Colombier if(!fileIsDir(fid->file)){
594d7aba6c3SDavid du Colombier werrstr("not a directory");
5955e96a66cSDavid du Colombier goto error;
5965e96a66cSDavid du Colombier }
597f48db99cSDavid du Colombier if(permFid(fid, PermW) <= 0)
5985e96a66cSDavid du Colombier goto error;
5995e96a66cSDavid du Colombier if(!validFileName(m->t.name))
6005e96a66cSDavid du Colombier goto error;
6015e96a66cSDavid du Colombier if(strcmp(fid->uid, uidnoworld) == 0){
602d7aba6c3SDavid du Colombier werrstr(EPermission);
6035e96a66cSDavid du Colombier goto error;
6045e96a66cSDavid du Colombier }
6055e96a66cSDavid du Colombier
6065e96a66cSDavid du Colombier omode = m->t.mode & OMODE;
6075e96a66cSDavid du Colombier open = 0;
6085e96a66cSDavid du Colombier
6095e96a66cSDavid du Colombier if(omode == OREAD || omode == ORDWR || omode == OEXEC)
6105e96a66cSDavid du Colombier open |= FidORead;
6115e96a66cSDavid du Colombier if(omode == OWRITE || omode == ORDWR)
6125e96a66cSDavid du Colombier open |= FidOWrite;
6135e96a66cSDavid du Colombier if((open & (FidOWrite|FidORead)) == 0){
614d7aba6c3SDavid du Colombier werrstr("unknown mode");
6155e96a66cSDavid du Colombier goto error;
6165e96a66cSDavid du Colombier }
6175e96a66cSDavid du Colombier if(m->t.perm & DMDIR){
6185e96a66cSDavid du Colombier if((m->t.mode & (ORCLOSE|OTRUNC)) || (open & FidOWrite)){
619d7aba6c3SDavid du Colombier werrstr("illegal mode");
6205e96a66cSDavid du Colombier goto error;
6215e96a66cSDavid du Colombier }
6225e96a66cSDavid du Colombier if(m->t.perm & DMAPPEND){
623d7aba6c3SDavid du Colombier werrstr("illegal perm");
6245e96a66cSDavid du Colombier goto error;
6255e96a66cSDavid du Colombier }
6265e96a66cSDavid du Colombier }
6275e96a66cSDavid du Colombier
6285e96a66cSDavid du Colombier mode = fileGetMode(fid->file);
6295e96a66cSDavid du Colombier perm = m->t.perm;
6305e96a66cSDavid du Colombier if(m->t.perm & DMDIR)
6315e96a66cSDavid du Colombier perm &= ~0777|(mode & 0777);
6325e96a66cSDavid du Colombier else
6335e96a66cSDavid du Colombier perm &= ~0666|(mode & 0666);
6345e96a66cSDavid du Colombier mode = perm & 0777;
6355e96a66cSDavid du Colombier if(m->t.perm & DMDIR)
6365e96a66cSDavid du Colombier mode |= ModeDir;
6375e96a66cSDavid du Colombier if(m->t.perm & DMAPPEND)
6385e96a66cSDavid du Colombier mode |= ModeAppend;
6395e96a66cSDavid du Colombier if(m->t.perm & DMEXCL)
6405e96a66cSDavid du Colombier mode |= ModeExclusive;
641fe853e23SDavid du Colombier if(m->t.perm & DMTMP)
642fe853e23SDavid du Colombier mode |= ModeTemporary;
6435e96a66cSDavid du Colombier
6445e96a66cSDavid du Colombier if((file = fileCreate(fid->file, m->t.name, mode, fid->uid)) == nil){
6455e96a66cSDavid du Colombier fidPut(fid);
6465e96a66cSDavid du Colombier return 0;
6475e96a66cSDavid du Colombier }
6485e96a66cSDavid du Colombier fileDecRef(fid->file);
6495e96a66cSDavid du Colombier
65034e04225SDavid du Colombier fid->qid.vers = fileGetMcount(file);
65134e04225SDavid du Colombier fid->qid.path = fileGetId(file);
6525e96a66cSDavid du Colombier fid->file = file;
6535e96a66cSDavid du Colombier mode = fileGetMode(fid->file);
6545e96a66cSDavid du Colombier if(mode & ModeDir)
6555e96a66cSDavid du Colombier fid->qid.type = QTDIR;
6565e96a66cSDavid du Colombier else
6575e96a66cSDavid du Colombier fid->qid.type = QTFILE;
6585e96a66cSDavid du Colombier if(mode & ModeAppend)
6595e96a66cSDavid du Colombier fid->qid.type |= QTAPPEND;
6605e96a66cSDavid du Colombier if(mode & ModeExclusive){
6615e96a66cSDavid du Colombier fid->qid.type |= QTEXCL;
6625e96a66cSDavid du Colombier assert(exclAlloc(fid) != 0);
6635e96a66cSDavid du Colombier }
6645e96a66cSDavid du Colombier if(m->t.mode & ORCLOSE)
6655e96a66cSDavid du Colombier open |= FidORclose;
6665e96a66cSDavid du Colombier fid->open = open;
6675e96a66cSDavid du Colombier
6685e96a66cSDavid du Colombier m->r.qid = fid->qid;
6695e96a66cSDavid du Colombier m->r.iounit = m->con->msize-IOHDRSZ;
6705e96a66cSDavid du Colombier
6715e96a66cSDavid du Colombier fidPut(fid);
6725e96a66cSDavid du Colombier return 1;
6735e96a66cSDavid du Colombier
6745e96a66cSDavid du Colombier error:
6755e96a66cSDavid du Colombier fidPut(fid);
6765e96a66cSDavid du Colombier return 0;
6775e96a66cSDavid du Colombier }
6785e96a66cSDavid du Colombier
6795e96a66cSDavid du Colombier static int
rTopen(Msg * m)6805e96a66cSDavid du Colombier rTopen(Msg* m)
6815e96a66cSDavid du Colombier {
6825e96a66cSDavid du Colombier Fid *fid;
6835e96a66cSDavid du Colombier int isdir, mode, omode, open, rofs;
6845e96a66cSDavid du Colombier
6855e96a66cSDavid du Colombier if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
6865e96a66cSDavid du Colombier return 0;
6875e96a66cSDavid du Colombier if(fid->open){
688d7aba6c3SDavid du Colombier werrstr("fid open for I/O");
6895e96a66cSDavid du Colombier goto error;
6905e96a66cSDavid du Colombier }
6915e96a66cSDavid du Colombier
6925e96a66cSDavid du Colombier isdir = fileIsDir(fid->file);
6935e96a66cSDavid du Colombier open = 0;
6945e96a66cSDavid du Colombier rofs = fileIsRoFs(fid->file) || !groupWriteMember(fid->uname);
6955e96a66cSDavid du Colombier
6965e96a66cSDavid du Colombier if(m->t.mode & ORCLOSE){
6975e96a66cSDavid du Colombier if(isdir){
698d7aba6c3SDavid du Colombier werrstr("is a directory");
6995e96a66cSDavid du Colombier goto error;
7005e96a66cSDavid du Colombier }
7015e96a66cSDavid du Colombier if(rofs){
702d7aba6c3SDavid du Colombier werrstr("read-only filesystem");
7035e96a66cSDavid du Colombier goto error;
7045e96a66cSDavid du Colombier }
705f48db99cSDavid du Colombier if(permParent(fid, PermW) <= 0)
7065e96a66cSDavid du Colombier goto error;
7075e96a66cSDavid du Colombier
7085e96a66cSDavid du Colombier open |= FidORclose;
7095e96a66cSDavid du Colombier }
7105e96a66cSDavid du Colombier
7115e96a66cSDavid du Colombier omode = m->t.mode & OMODE;
7125e96a66cSDavid du Colombier if(omode == OREAD || omode == ORDWR){
713f48db99cSDavid du Colombier if(permFid(fid, PermR) <= 0)
7145e96a66cSDavid du Colombier goto error;
7155e96a66cSDavid du Colombier open |= FidORead;
7165e96a66cSDavid du Colombier }
7175e96a66cSDavid du Colombier if(omode == OWRITE || omode == ORDWR || (m->t.mode & OTRUNC)){
7185e96a66cSDavid du Colombier if(isdir){
719d7aba6c3SDavid du Colombier werrstr("is a directory");
7205e96a66cSDavid du Colombier goto error;
7215e96a66cSDavid du Colombier }
7225e96a66cSDavid du Colombier if(rofs){
723d7aba6c3SDavid du Colombier werrstr("read-only filesystem");
7245e96a66cSDavid du Colombier goto error;
7255e96a66cSDavid du Colombier }
726f48db99cSDavid du Colombier if(permFid(fid, PermW) <= 0)
7275e96a66cSDavid du Colombier goto error;
7285e96a66cSDavid du Colombier open |= FidOWrite;
7295e96a66cSDavid du Colombier }
7305e96a66cSDavid du Colombier if(omode == OEXEC){
7315e96a66cSDavid du Colombier if(isdir){
732d7aba6c3SDavid du Colombier werrstr("is a directory");
7335e96a66cSDavid du Colombier goto error;
7345e96a66cSDavid du Colombier }
735f48db99cSDavid du Colombier if(permFid(fid, PermX) <= 0)
7365e96a66cSDavid du Colombier goto error;
7375e96a66cSDavid du Colombier open |= FidORead;
7385e96a66cSDavid du Colombier }
7395e96a66cSDavid du Colombier if((open & (FidOWrite|FidORead)) == 0){
740d7aba6c3SDavid du Colombier werrstr("unknown mode");
7415e96a66cSDavid du Colombier goto error;
7425e96a66cSDavid du Colombier }
7435e96a66cSDavid du Colombier
7445e96a66cSDavid du Colombier mode = fileGetMode(fid->file);
7455e96a66cSDavid du Colombier if((mode & ModeExclusive) && exclAlloc(fid) == 0)
7465e96a66cSDavid du Colombier goto error;
7475e96a66cSDavid du Colombier
7485e96a66cSDavid du Colombier /*
7495e96a66cSDavid du Colombier * Everything checks out, try to commit any changes.
7505e96a66cSDavid du Colombier */
75163da9eacSDavid du Colombier if((m->t.mode & OTRUNC) && !(mode & ModeAppend))
7525e96a66cSDavid du Colombier if(!fileTruncate(fid->file, fid->uid))
7535e96a66cSDavid du Colombier goto error;
75463da9eacSDavid du Colombier
7555e96a66cSDavid du Colombier if(isdir && fid->db != nil){
7565e96a66cSDavid du Colombier dirBufFree(fid->db);
7575e96a66cSDavid du Colombier fid->db = nil;
7585e96a66cSDavid du Colombier }
7595e96a66cSDavid du Colombier
76063da9eacSDavid du Colombier fid->qid.vers = fileGetMcount(fid->file);
7615e96a66cSDavid du Colombier m->r.qid = fid->qid;
7625e96a66cSDavid du Colombier m->r.iounit = m->con->msize-IOHDRSZ;
7635e96a66cSDavid du Colombier
7645e96a66cSDavid du Colombier fid->open = open;
7655e96a66cSDavid du Colombier
7665e96a66cSDavid du Colombier fidPut(fid);
7675e96a66cSDavid du Colombier return 1;
7685e96a66cSDavid du Colombier
7695e96a66cSDavid du Colombier error:
7705e96a66cSDavid du Colombier if(fid->excl != nil)
7715e96a66cSDavid du Colombier exclFree(fid);
7725e96a66cSDavid du Colombier fidPut(fid);
7735e96a66cSDavid du Colombier return 0;
7745e96a66cSDavid du Colombier }
7755e96a66cSDavid du Colombier
7765e96a66cSDavid du Colombier static int
rTwalk(Msg * m)7775e96a66cSDavid du Colombier rTwalk(Msg* m)
7785e96a66cSDavid du Colombier {
7795e96a66cSDavid du Colombier Qid qid;
7805e96a66cSDavid du Colombier Fcall *r, *t;
7815e96a66cSDavid du Colombier int nwname, wlock;
7825e96a66cSDavid du Colombier File *file, *nfile;
7835e96a66cSDavid du Colombier Fid *fid, *ofid, *nfid;
7845e96a66cSDavid du Colombier
7855e96a66cSDavid du Colombier t = &m->t;
7865e96a66cSDavid du Colombier if(t->fid == t->newfid)
7875e96a66cSDavid du Colombier wlock = FidFWlock;
7885e96a66cSDavid du Colombier else
7895e96a66cSDavid du Colombier wlock = 0;
7905e96a66cSDavid du Colombier
7915e96a66cSDavid du Colombier /*
7925e96a66cSDavid du Colombier * The file identified by t->fid must be valid in the
7935e96a66cSDavid du Colombier * current session and must not have been opened for I/O
7945e96a66cSDavid du Colombier * by an open or create message.
7955e96a66cSDavid du Colombier */
7965e96a66cSDavid du Colombier if((ofid = fidGet(m->con, t->fid, wlock)) == nil)
7975e96a66cSDavid du Colombier return 0;
7985e96a66cSDavid du Colombier if(ofid->open){
799d7aba6c3SDavid du Colombier werrstr("file open for I/O");
8005e96a66cSDavid du Colombier fidPut(ofid);
8015e96a66cSDavid du Colombier return 0;
8025e96a66cSDavid du Colombier }
8035e96a66cSDavid du Colombier
8045e96a66cSDavid du Colombier /*
8055e96a66cSDavid du Colombier * If newfid is not the same as fid, allocate a new file;
8065e96a66cSDavid du Colombier * a side effect is checking newfid is not already in use (error);
8075e96a66cSDavid du Colombier * if there are no names to walk this will be equivalent to a
8085e96a66cSDavid du Colombier * simple 'clone' operation.
8095e96a66cSDavid du Colombier * It's a no-op if newfid is the same as fid and t->nwname is 0.
8105e96a66cSDavid du Colombier */
8115e96a66cSDavid du Colombier nfid = nil;
8125e96a66cSDavid du Colombier if(t->fid != t->newfid){
8135e96a66cSDavid du Colombier nfid = fidGet(m->con, t->newfid, FidFWlock|FidFCreate);
8145e96a66cSDavid du Colombier if(nfid == nil){
815d7aba6c3SDavid du Colombier werrstr("%s: walk: newfid 0x%ud in use",
81636066be0SDavid du Colombier argv0, t->newfid);
8175e96a66cSDavid du Colombier fidPut(ofid);
8185e96a66cSDavid du Colombier return 0;
8195e96a66cSDavid du Colombier }
8205e96a66cSDavid du Colombier nfid->open = ofid->open & ~FidORclose;
8215e96a66cSDavid du Colombier nfid->file = fileIncRef(ofid->file);
8225e96a66cSDavid du Colombier nfid->qid = ofid->qid;
823d7aba6c3SDavid du Colombier nfid->uid = vtstrdup(ofid->uid);
824d7aba6c3SDavid du Colombier nfid->uname = vtstrdup(ofid->uname);
8255e96a66cSDavid du Colombier nfid->fsys = fsysIncRef(ofid->fsys);
8265e96a66cSDavid du Colombier fid = nfid;
8275e96a66cSDavid du Colombier }
8285e96a66cSDavid du Colombier else
8295e96a66cSDavid du Colombier fid = ofid;
8305e96a66cSDavid du Colombier
8315e96a66cSDavid du Colombier r = &m->r;
8325e96a66cSDavid du Colombier r->nwqid = 0;
8335e96a66cSDavid du Colombier
8345e96a66cSDavid du Colombier if(t->nwname == 0){
8355e96a66cSDavid du Colombier if(nfid != nil)
8365e96a66cSDavid du Colombier fidPut(nfid);
8375e96a66cSDavid du Colombier fidPut(ofid);
8385e96a66cSDavid du Colombier
8395e96a66cSDavid du Colombier return 1;
8405e96a66cSDavid du Colombier }
8415e96a66cSDavid du Colombier
8425e96a66cSDavid du Colombier file = fid->file;
8435e96a66cSDavid du Colombier fileIncRef(file);
8445e96a66cSDavid du Colombier qid = fid->qid;
8455e96a66cSDavid du Colombier
8465e96a66cSDavid du Colombier for(nwname = 0; nwname < t->nwname; nwname++){
8475e96a66cSDavid du Colombier /*
8485e96a66cSDavid du Colombier * Walked elements must represent a directory and
8495e96a66cSDavid du Colombier * the implied user must have permission to search
8505e96a66cSDavid du Colombier * the directory. Walking .. is always allowed, so that
8515e96a66cSDavid du Colombier * you can't walk into a directory and then not be able
8525e96a66cSDavid du Colombier * to walk out of it.
8535e96a66cSDavid du Colombier */
8545e96a66cSDavid du Colombier if(!(qid.type & QTDIR)){
855d7aba6c3SDavid du Colombier werrstr("not a directory");
8565e96a66cSDavid du Colombier break;
8575e96a66cSDavid du Colombier }
858f48db99cSDavid du Colombier switch(permFile(file, fid, PermX)){
859f48db99cSDavid du Colombier case 1:
8605e96a66cSDavid du Colombier break;
861f48db99cSDavid du Colombier case 0:
862f48db99cSDavid du Colombier if(strcmp(t->wname[nwname], "..") == 0)
863f48db99cSDavid du Colombier break;
864f48db99cSDavid du Colombier case -1:
865f48db99cSDavid du Colombier goto Out;
866f48db99cSDavid du Colombier }
8675e96a66cSDavid du Colombier if((nfile = fileWalk(file, t->wname[nwname])) == nil)
8685e96a66cSDavid du Colombier break;
8695e96a66cSDavid du Colombier fileDecRef(file);
8705e96a66cSDavid du Colombier file = nfile;
8715e96a66cSDavid du Colombier qid.type = QTFILE;
8725e96a66cSDavid du Colombier if(fileIsDir(file))
8735e96a66cSDavid du Colombier qid.type = QTDIR;
8746bbfed0dSDavid du Colombier if(fileIsAppend(file))
8756bbfed0dSDavid du Colombier qid.type |= QTAPPEND;
8766bbfed0dSDavid du Colombier if(fileIsTemporary(file))
8776bbfed0dSDavid du Colombier qid.type |= QTTMP;
8786bbfed0dSDavid du Colombier if(fileIsExclusive(file))
8796bbfed0dSDavid du Colombier qid.type |= QTEXCL;
8805e96a66cSDavid du Colombier qid.vers = fileGetMcount(file);
8815e96a66cSDavid du Colombier qid.path = fileGetId(file);
8825e96a66cSDavid du Colombier r->wqid[r->nwqid++] = qid;
8835e96a66cSDavid du Colombier }
8845e96a66cSDavid du Colombier
8855e96a66cSDavid du Colombier if(nwname == t->nwname){
8865e96a66cSDavid du Colombier /*
8875e96a66cSDavid du Colombier * Walked all elements. Update the target fid
8885e96a66cSDavid du Colombier * from the temporary qid used during the walk,
8895e96a66cSDavid du Colombier * and tidy up.
8905e96a66cSDavid du Colombier */
8915e96a66cSDavid du Colombier fid->qid = r->wqid[r->nwqid-1];
8925e96a66cSDavid du Colombier fileDecRef(fid->file);
8935e96a66cSDavid du Colombier fid->file = file;
8945e96a66cSDavid du Colombier
8955e96a66cSDavid du Colombier if(nfid != nil)
8965e96a66cSDavid du Colombier fidPut(nfid);
8975e96a66cSDavid du Colombier
8985e96a66cSDavid du Colombier fidPut(ofid);
8995e96a66cSDavid du Colombier return 1;
9005e96a66cSDavid du Colombier }
9015e96a66cSDavid du Colombier
902f48db99cSDavid du Colombier Out:
9035e96a66cSDavid du Colombier /*
9045e96a66cSDavid du Colombier * Didn't walk all elements, 'clunk' nfid if it exists
9055e96a66cSDavid du Colombier * and leave fid untouched.
9065e96a66cSDavid du Colombier * It's not an error if some of the elements were walked OK.
9075e96a66cSDavid du Colombier */
9085e96a66cSDavid du Colombier fileDecRef(file);
9095e96a66cSDavid du Colombier if(nfid != nil)
9105e96a66cSDavid du Colombier fidClunk(nfid);
9115e96a66cSDavid du Colombier
9125e96a66cSDavid du Colombier fidPut(ofid);
9135e96a66cSDavid du Colombier if(nwname == 0)
9145e96a66cSDavid du Colombier return 0;
9155e96a66cSDavid du Colombier return 1;
9165e96a66cSDavid du Colombier }
9175e96a66cSDavid du Colombier
9185e96a66cSDavid du Colombier static int
rTflush(Msg * m)9195e96a66cSDavid du Colombier rTflush(Msg* m)
9205e96a66cSDavid du Colombier {
92134e04225SDavid du Colombier if(m->t.oldtag != NOTAG)
92234e04225SDavid du Colombier msgFlush(m);
9235e96a66cSDavid du Colombier return 1;
9245e96a66cSDavid du Colombier }
9255e96a66cSDavid du Colombier
9265e96a66cSDavid du Colombier static void
parseAname(char * aname,char ** fsname,char ** path)9275e96a66cSDavid du Colombier parseAname(char *aname, char **fsname, char **path)
9285e96a66cSDavid du Colombier {
9295e96a66cSDavid du Colombier char *s;
9305e96a66cSDavid du Colombier
9315e96a66cSDavid du Colombier if(aname && aname[0])
932d7aba6c3SDavid du Colombier s = vtstrdup(aname);
9335e96a66cSDavid du Colombier else
934d7aba6c3SDavid du Colombier s = vtstrdup("main/active");
9355e96a66cSDavid du Colombier *fsname = s;
9365e96a66cSDavid du Colombier if((*path = strchr(s, '/')) != nil)
9375e96a66cSDavid du Colombier *(*path)++ = '\0';
9385e96a66cSDavid du Colombier else
9395e96a66cSDavid du Colombier *path = "";
9405e96a66cSDavid du Colombier }
9415e96a66cSDavid du Colombier
9422cca75a1SDavid du Colombier /*
9432cca75a1SDavid du Colombier * Check remote IP address against /mnt/ipok.
9442cca75a1SDavid du Colombier * Sources.cs.bell-labs.com uses this to disallow
9452cca75a1SDavid du Colombier * network connections from Sudan, Libya, etc.,
9462cca75a1SDavid du Colombier * following U.S. cryptography export regulations.
9472cca75a1SDavid du Colombier */
9482cca75a1SDavid du Colombier static int
conIPCheck(Con * con)9492cca75a1SDavid du Colombier conIPCheck(Con* con)
9502cca75a1SDavid du Colombier {
9512cca75a1SDavid du Colombier char ok[256], *p;
9522cca75a1SDavid du Colombier int fd;
9532cca75a1SDavid du Colombier
9542cca75a1SDavid du Colombier if(con->flags&ConIPCheck){
9552cca75a1SDavid du Colombier if(con->remote[0] == 0){
956d7aba6c3SDavid du Colombier werrstr("cannot verify unknown remote address");
9572cca75a1SDavid du Colombier return 0;
9582cca75a1SDavid du Colombier }
9592cca75a1SDavid du Colombier if(access("/mnt/ipok/ok", AEXIST) < 0){
9602cca75a1SDavid du Colombier /* mount closes the fd on success */
9612cca75a1SDavid du Colombier if((fd = open("/srv/ipok", ORDWR)) >= 0
9622cca75a1SDavid du Colombier && mount(fd, -1, "/mnt/ipok", MREPL, "") < 0)
9632cca75a1SDavid du Colombier close(fd);
9642cca75a1SDavid du Colombier if(access("/mnt/ipok/ok", AEXIST) < 0){
965d7aba6c3SDavid du Colombier werrstr("cannot verify remote address");
9662cca75a1SDavid du Colombier return 0;
9672cca75a1SDavid du Colombier }
9682cca75a1SDavid du Colombier }
9692cca75a1SDavid du Colombier snprint(ok, sizeof ok, "/mnt/ipok/ok/%s", con->remote);
9702cca75a1SDavid du Colombier if((p = strchr(ok, '!')) != nil)
9712cca75a1SDavid du Colombier *p = 0;
9722cca75a1SDavid du Colombier if(access(ok, AEXIST) < 0){
973d7aba6c3SDavid du Colombier werrstr("restricted remote address");
9742cca75a1SDavid du Colombier return 0;
9752cca75a1SDavid du Colombier }
9762cca75a1SDavid du Colombier }
9772cca75a1SDavid du Colombier return 1;
9782cca75a1SDavid du Colombier }
9792cca75a1SDavid du Colombier
9805e96a66cSDavid du Colombier static int
rTattach(Msg * m)9815e96a66cSDavid du Colombier rTattach(Msg* m)
9825e96a66cSDavid du Colombier {
9835e96a66cSDavid du Colombier Fid *fid;
9845e96a66cSDavid du Colombier Fsys *fsys;
9855e96a66cSDavid du Colombier char *fsname, *path;
9865e96a66cSDavid du Colombier
9875e96a66cSDavid du Colombier if((fid = fidGet(m->con, m->t.fid, FidFWlock|FidFCreate)) == nil)
9885e96a66cSDavid du Colombier return 0;
9895e96a66cSDavid du Colombier
9905e96a66cSDavid du Colombier parseAname(m->t.aname, &fsname, &path);
9915e96a66cSDavid du Colombier if((fsys = fsysGet(fsname)) == nil){
9925e96a66cSDavid du Colombier fidClunk(fid);
993d7aba6c3SDavid du Colombier vtfree(fsname);
9945e96a66cSDavid du Colombier return 0;
9955e96a66cSDavid du Colombier }
9965e96a66cSDavid du Colombier fid->fsys = fsys;
9975e96a66cSDavid du Colombier
9985e96a66cSDavid du Colombier if(m->t.uname[0] != '\0')
999d7aba6c3SDavid du Colombier fid->uname = vtstrdup(m->t.uname);
10005e96a66cSDavid du Colombier else
1001d7aba6c3SDavid du Colombier fid->uname = vtstrdup(unamenone);
10025e96a66cSDavid du Colombier
10032cca75a1SDavid du Colombier if((fid->con->flags&ConIPCheck) && !conIPCheck(fid->con)){
1004d7aba6c3SDavid du Colombier consPrint("reject %s from %s: %r\n", fid->uname, fid->con->remote);
10052cca75a1SDavid du Colombier fidClunk(fid);
1006d7aba6c3SDavid du Colombier vtfree(fsname);
10072cca75a1SDavid du Colombier return 0;
10082cca75a1SDavid du Colombier }
10092cca75a1SDavid du Colombier if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
10105e96a66cSDavid du Colombier if((fid->uid = uidByUname(fid->uname)) == nil)
1011d7aba6c3SDavid du Colombier fid->uid = vtstrdup(unamenone);
10125e96a66cSDavid du Colombier }
10135e96a66cSDavid du Colombier else if(!authCheck(&m->t, fid, fsys)){
10145e96a66cSDavid du Colombier fidClunk(fid);
1015d7aba6c3SDavid du Colombier vtfree(fsname);
10165e96a66cSDavid du Colombier return 0;
10175e96a66cSDavid du Colombier }
10185e96a66cSDavid du Colombier
10195e96a66cSDavid du Colombier fsysFsRlock(fsys);
10205e96a66cSDavid du Colombier if((fid->file = fsysGetRoot(fsys, path)) == nil){
10215e96a66cSDavid du Colombier fsysFsRUnlock(fsys);
10225e96a66cSDavid du Colombier fidClunk(fid);
1023d7aba6c3SDavid du Colombier vtfree(fsname);
10245e96a66cSDavid du Colombier return 0;
10255e96a66cSDavid du Colombier }
10265e96a66cSDavid du Colombier fsysFsRUnlock(fsys);
1027d7aba6c3SDavid du Colombier vtfree(fsname);
10285e96a66cSDavid du Colombier
10295e96a66cSDavid du Colombier fid->qid = (Qid){fileGetId(fid->file), 0, QTDIR};
10305e96a66cSDavid du Colombier m->r.qid = fid->qid;
10315e96a66cSDavid du Colombier
10325e96a66cSDavid du Colombier fidPut(fid);
10335e96a66cSDavid du Colombier return 1;
10345e96a66cSDavid du Colombier }
10355e96a66cSDavid du Colombier
10365e96a66cSDavid du Colombier static int
rTauth(Msg * m)10375e96a66cSDavid du Colombier rTauth(Msg* m)
10385e96a66cSDavid du Colombier {
10395e96a66cSDavid du Colombier int afd;
10405e96a66cSDavid du Colombier Con *con;
10415e96a66cSDavid du Colombier Fid *afid;
10425e96a66cSDavid du Colombier Fsys *fsys;
10435e96a66cSDavid du Colombier char *fsname, *path;
10445e96a66cSDavid du Colombier
10455e96a66cSDavid du Colombier parseAname(m->t.aname, &fsname, &path);
10465e96a66cSDavid du Colombier if((fsys = fsysGet(fsname)) == nil){
1047d7aba6c3SDavid du Colombier vtfree(fsname);
10485e96a66cSDavid du Colombier return 0;
10495e96a66cSDavid du Colombier }
1050d7aba6c3SDavid du Colombier vtfree(fsname);
10515e96a66cSDavid du Colombier
10522cca75a1SDavid du Colombier if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
10535e96a66cSDavid du Colombier m->con->aok = 1;
1054d7aba6c3SDavid du Colombier werrstr("authentication disabled");
10555e96a66cSDavid du Colombier fsysPut(fsys);
10565e96a66cSDavid du Colombier return 0;
10575e96a66cSDavid du Colombier }
10585e96a66cSDavid du Colombier if(strcmp(m->t.uname, unamenone) == 0){
1059d7aba6c3SDavid du Colombier werrstr("user 'none' requires no authentication");
10605e96a66cSDavid du Colombier fsysPut(fsys);
10615e96a66cSDavid du Colombier return 0;
10625e96a66cSDavid du Colombier }
10635e96a66cSDavid du Colombier
10645e96a66cSDavid du Colombier con = m->con;
10655e96a66cSDavid du Colombier if((afid = fidGet(con, m->t.afid, FidFWlock|FidFCreate)) == nil){
10665e96a66cSDavid du Colombier fsysPut(fsys);
10675e96a66cSDavid du Colombier return 0;
10685e96a66cSDavid du Colombier }
10695e96a66cSDavid du Colombier afid->fsys = fsys;
10705e96a66cSDavid du Colombier
10715e96a66cSDavid du Colombier if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
1072d7aba6c3SDavid du Colombier werrstr("can't open \"/mnt/factotum/rpc\"");
10735e96a66cSDavid du Colombier fidClunk(afid);
10745e96a66cSDavid du Colombier return 0;
10755e96a66cSDavid du Colombier }
10765e96a66cSDavid du Colombier if((afid->rpc = auth_allocrpc(afd)) == nil){
10775e96a66cSDavid du Colombier close(afd);
1078d7aba6c3SDavid du Colombier werrstr("can't auth_allocrpc");
10795e96a66cSDavid du Colombier fidClunk(afid);
10805e96a66cSDavid du Colombier return 0;
10815e96a66cSDavid du Colombier }
10825e96a66cSDavid du Colombier if(auth_rpc(afid->rpc, "start", "proto=p9any role=server", 23) != ARok){
1083d7aba6c3SDavid du Colombier werrstr("can't auth_rpc");
10845e96a66cSDavid du Colombier fidClunk(afid);
10855e96a66cSDavid du Colombier return 0;
10865e96a66cSDavid du Colombier }
10875e96a66cSDavid du Colombier
10885e96a66cSDavid du Colombier afid->open = FidOWrite|FidORead;
10895e96a66cSDavid du Colombier afid->qid.type = QTAUTH;
10905e96a66cSDavid du Colombier afid->qid.path = m->t.afid;
1091d7aba6c3SDavid du Colombier afid->uname = vtstrdup(m->t.uname);
10925e96a66cSDavid du Colombier
10935e96a66cSDavid du Colombier m->r.qid = afid->qid;
10945e96a66cSDavid du Colombier
10955e96a66cSDavid du Colombier fidPut(afid);
10965e96a66cSDavid du Colombier return 1;
10975e96a66cSDavid du Colombier }
10985e96a66cSDavid du Colombier
10995e96a66cSDavid du Colombier static int
rTversion(Msg * m)11005e96a66cSDavid du Colombier rTversion(Msg* m)
11015e96a66cSDavid du Colombier {
11025e96a66cSDavid du Colombier int v;
11035e96a66cSDavid du Colombier Con *con;
11045e96a66cSDavid du Colombier Fcall *r, *t;
11055e96a66cSDavid du Colombier
11065e96a66cSDavid du Colombier t = &m->t;
11075e96a66cSDavid du Colombier r = &m->r;
11085e96a66cSDavid du Colombier con = m->con;
11095e96a66cSDavid du Colombier
1110d7aba6c3SDavid du Colombier qlock(&con->lock);
111134e04225SDavid du Colombier if(con->state != ConInit){
1112d7aba6c3SDavid du Colombier qunlock(&con->lock);
1113d7aba6c3SDavid du Colombier werrstr("Tversion: down");
11145e96a66cSDavid du Colombier return 0;
11155e96a66cSDavid du Colombier }
111634e04225SDavid du Colombier con->state = ConNew;
11175e96a66cSDavid du Colombier
11185e96a66cSDavid du Colombier /*
11195e96a66cSDavid du Colombier * Release the karma of past lives and suffering.
112034e04225SDavid du Colombier * Should this be done before or after checking the
112134e04225SDavid du Colombier * validity of the Tversion?
11225e96a66cSDavid du Colombier */
112334e04225SDavid du Colombier fidClunkAll(con);
11245e96a66cSDavid du Colombier
11255e96a66cSDavid du Colombier if(t->tag != NOTAG){
1126d7aba6c3SDavid du Colombier qunlock(&con->lock);
1127d7aba6c3SDavid du Colombier werrstr("Tversion: invalid tag");
11285e96a66cSDavid du Colombier return 0;
11295e96a66cSDavid du Colombier }
11305e96a66cSDavid du Colombier
11315e96a66cSDavid du Colombier if(t->msize < 256){
1132d7aba6c3SDavid du Colombier qunlock(&con->lock);
1133d7aba6c3SDavid du Colombier werrstr("Tversion: message size too small");
11345e96a66cSDavid du Colombier return 0;
11355e96a66cSDavid du Colombier }
11365e96a66cSDavid du Colombier if(t->msize < con->msize)
11375e96a66cSDavid du Colombier r->msize = t->msize;
11385e96a66cSDavid du Colombier else
11395e96a66cSDavid du Colombier r->msize = con->msize;
11405e96a66cSDavid du Colombier
11415e96a66cSDavid du Colombier r->version = "unknown";
11425e96a66cSDavid du Colombier if(t->version[0] == '9' && t->version[1] == 'P'){
11435e96a66cSDavid du Colombier /*
11445e96a66cSDavid du Colombier * Currently, the only defined version
11455e96a66cSDavid du Colombier * is "9P2000"; ignore any later versions.
11465e96a66cSDavid du Colombier */
11475e96a66cSDavid du Colombier v = strtol(&t->version[2], 0, 10);
11485e96a66cSDavid du Colombier if(v >= 2000){
11495e96a66cSDavid du Colombier r->version = VERSION9P;
11505e96a66cSDavid du Colombier con->msize = r->msize;
115134e04225SDavid du Colombier con->state = ConUp;
11525e96a66cSDavid du Colombier }
11535e96a66cSDavid du Colombier else if(strcmp(t->version, "9PEoF") == 0){
11545e96a66cSDavid du Colombier r->version = "9PEoF";
11555e96a66cSDavid du Colombier con->msize = r->msize;
115634e04225SDavid du Colombier con->state = ConMoribund;
1157ceddb3d7SDavid du Colombier
1158ceddb3d7SDavid du Colombier /*
1159ceddb3d7SDavid du Colombier * Don't want to attempt to write this
1160ceddb3d7SDavid du Colombier * message as the connection may be already
1161ceddb3d7SDavid du Colombier * closed.
1162ceddb3d7SDavid du Colombier */
1163ceddb3d7SDavid du Colombier m->state = MsgF;
11645e96a66cSDavid du Colombier }
11655e96a66cSDavid du Colombier }
1166d7aba6c3SDavid du Colombier qunlock(&con->lock);
11675e96a66cSDavid du Colombier
11685e96a66cSDavid du Colombier return 1;
11695e96a66cSDavid du Colombier }
11705e96a66cSDavid du Colombier
11715e96a66cSDavid du Colombier int (*rFcall[Tmax])(Msg*) = {
11725e96a66cSDavid du Colombier [Tversion] = rTversion,
11735e96a66cSDavid du Colombier [Tauth] = rTauth,
11745e96a66cSDavid du Colombier [Tattach] = rTattach,
11755e96a66cSDavid du Colombier [Tflush] = rTflush,
11765e96a66cSDavid du Colombier [Twalk] = rTwalk,
11775e96a66cSDavid du Colombier [Topen] = rTopen,
11785e96a66cSDavid du Colombier [Tcreate] = rTcreate,
11795e96a66cSDavid du Colombier [Tread] = rTread,
11805e96a66cSDavid du Colombier [Twrite] = rTwrite,
11815e96a66cSDavid du Colombier [Tclunk] = rTclunk,
11825e96a66cSDavid du Colombier [Tremove] = rTremove,
11835e96a66cSDavid du Colombier [Tstat] = rTstat,
11845e96a66cSDavid du Colombier [Twstat] = rTwstat,
11855e96a66cSDavid du Colombier };
1186