t9p.c - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       t9p.c (23094B)
       ---
            1 #include "stdinc.h"
            2 
            3 #include "9.h"
            4 
            5 enum {
            6         OMODE                = 0x7,                /* Topen/Tcreate mode */
            7 };
            8 
            9 enum {
           10         PermX                = 1,
           11         PermW                = 2,
           12         PermR                = 4,
           13 };
           14 
           15 static char EPermission[] = "permission denied";
           16 
           17 static int
           18 permFile(File* file, Fid* fid, int perm)
           19 {
           20         char *u;
           21         DirEntry de;
           22 
           23         if(!fileGetDir(file, &de))
           24                 return -1;
           25 
           26         /*
           27          * User none only gets other permissions.
           28          */
           29         if(strcmp(fid->uname, unamenone) != 0){
           30                 /*
           31                  * There is only one uid<->uname mapping
           32                  * and it's already cached in the Fid, but
           33                  * it might have changed during the lifetime
           34                  * if this Fid.
           35                  */
           36                 if((u = unameByUid(de.uid)) != nil){
           37                         if(strcmp(fid->uname, u) == 0 && ((perm<<6) & de.mode)){
           38                                 vtfree(u);
           39                                 deCleanup(&de);
           40                                 return 1;
           41                         }
           42                         vtfree(u);
           43                 }
           44                 if(groupMember(de.gid, fid->uname) && ((perm<<3) & de.mode)){
           45                         deCleanup(&de);
           46                         return 1;
           47                 }
           48         }
           49         if(perm & de.mode){
           50                 if(perm == PermX && (de.mode & ModeDir)){
           51                         deCleanup(&de);
           52                         return 1;
           53                 }
           54                 if(!groupMember(uidnoworld, fid->uname)){
           55                         deCleanup(&de);
           56                         return 1;
           57                 }
           58         }
           59         if(fsysNoPermCheck(fid->fsys) || (fid->con->flags&ConNoPermCheck)){
           60                 deCleanup(&de);
           61                 return 1;
           62         }
           63         werrstr(EPermission);
           64 
           65         deCleanup(&de);
           66         return 0;
           67 }
           68 
           69 static int
           70 permFid(Fid* fid, int p)
           71 {
           72         return permFile(fid->file, fid, p);
           73 }
           74 
           75 static int
           76 permParent(Fid* fid, int p)
           77 {
           78         int r;
           79         File *parent;
           80 
           81         parent = fileGetParent(fid->file);
           82         r = permFile(parent, fid, p);
           83         fileDecRef(parent);
           84 
           85         return r;
           86 }
           87 
           88 int
           89 validFileName(char* name)
           90 {
           91         char *p;
           92 
           93         if(name == nil || name[0] == '\0'){
           94                 werrstr("no file name");
           95                 return 0;
           96         }
           97         if(name[0] == '.'){
           98                 if(name[1] == '\0' || (name[1] == '.' && name[2] == '\0')){
           99                         werrstr(". and .. illegal as file name");
          100                         return 0;
          101                 }
          102         }
          103 
          104         for(p = name; *p != '\0'; p++){
          105                 if((*p & 0xFF) < 040){
          106                         werrstr("bad character in file name");
          107                         return 0;
          108                 }
          109         }
          110 
          111         return 1;
          112 }
          113 
          114 static int
          115 rTwstat(Msg* m)
          116 {
          117         Dir dir;
          118         Fid *fid;
          119         ulong mode, oldmode;
          120         DirEntry de;
          121         char *gid, *strs, *uid;
          122         int gl, op, retval, tsync, wstatallow;
          123 
          124         if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
          125                 return 0;
          126 
          127         gid = uid = nil;
          128         retval = 0;
          129 
          130         if(strcmp(fid->uname, unamenone) == 0 || (fid->qid.type & QTAUTH)){
          131                 werrstr(EPermission);
          132                 goto error0;
          133         }
          134         if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
          135                 werrstr("read-only filesystem");
          136                 goto error0;
          137         }
          138 
          139         if(!fileGetDir(fid->file, &de))
          140                 goto error0;
          141 
          142         strs = vtmalloc(m->t.nstat);
          143         if(convM2D(m->t.stat, m->t.nstat, &dir, strs) == 0){
          144                 werrstr("wstat -- protocol botch");
          145                 goto error;
          146         }
          147 
          148         /*
          149          * Run through each of the (sub-)fields in the provided Dir
          150          * checking for validity and whether it's a default:
          151          * .type, .dev and .atime are completely ignored and not checked;
          152          * .qid.path, .qid.vers and .muid are checked for validity but
          153          * any attempt to change them is an error.
          154          * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can
          155          * possibly be changed.
          156          *
          157          * 'Op' flags there are changed fields, i.e. it's not a no-op.
          158          * 'Tsync' flags all fields are defaulted.
          159          */
          160         tsync = 1;
          161         if(dir.qid.path != ~0){
          162                 if(dir.qid.path != de.qid){
          163                         werrstr("wstat -- attempt to change qid.path");
          164                         goto error;
          165                 }
          166                 tsync = 0;
          167         }
          168         if(dir.qid.vers != (u32int)~0){
          169                 if(dir.qid.vers != de.mcount){
          170                         werrstr("wstat -- attempt to change qid.vers");
          171                         goto error;
          172                 }
          173                 tsync = 0;
          174         }
          175         if(dir.muid != nil && *dir.muid != '\0'){
          176                 if((uid = uidByUname(dir.muid)) == nil){
          177                         werrstr("wstat -- unknown muid");
          178                         goto error;
          179                 }
          180                 if(strcmp(uid, de.mid) != 0){
          181                         werrstr("wstat -- attempt to change muid");
          182                         goto error;
          183                 }
          184                 vtfree(uid);
          185                 uid = nil;
          186                 tsync = 0;
          187         }
          188 
          189         /*
          190          * Check .qid.type and .mode agree if neither is defaulted.
          191          */
          192         if(dir.qid.type != (uchar)~0 && dir.mode != (u32int)~0){
          193                 if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
          194                         werrstr("wstat -- qid.type/mode mismatch");
          195                         goto error;
          196                 }
          197         }
          198 
          199         op = 0;
          200 
          201         oldmode = de.mode;
          202         if(dir.qid.type != (uchar)~0 || dir.mode != (u32int)~0){
          203                 /*
          204                  * .qid.type or .mode isn't defaulted, check for unknown bits.
          205                  */
          206                 if(dir.mode == ~0)
          207                         dir.mode = (dir.qid.type<<24)|(de.mode & 0777);
          208                 if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){
          209                         werrstr("wstat -- unknown bits in qid.type/mode");
          210                         goto error;
          211                 }
          212 
          213                 /*
          214                  * Synthesise a mode to check against the current settings.
          215                  */
          216                 mode = dir.mode & 0777;
          217                 if(dir.mode & DMEXCL)
          218                         mode |= ModeExclusive;
          219                 if(dir.mode & DMAPPEND)
          220                         mode |= ModeAppend;
          221                 if(dir.mode & DMDIR)
          222                         mode |= ModeDir;
          223                 if(dir.mode & DMTMP)
          224                         mode |= ModeTemporary;
          225 
          226                 if((de.mode^mode) & ModeDir){
          227                         werrstr("wstat -- attempt to change directory bit");
          228                         goto error;
          229                 }
          230 
          231                 if((de.mode & (ModeAppend|ModeExclusive|ModeTemporary|0777)) != mode){
          232                         de.mode &= ~(ModeAppend|ModeExclusive|ModeTemporary|0777);
          233                         de.mode |= mode;
          234                         op = 1;
          235                 }
          236                 tsync = 0;
          237         }
          238 
          239         if(dir.mtime != (u32int)~0){
          240                 if(dir.mtime != de.mtime){
          241                         de.mtime = dir.mtime;
          242                         op = 1;
          243                 }
          244                 tsync = 0;
          245         }
          246 
          247         if(dir.length != ~0){
          248                 if(dir.length != de.size){
          249                         /*
          250                          * Cannot change length on append-only files.
          251                          * If we're changing the append bit, it's okay.
          252                          */
          253                         if(de.mode & oldmode & ModeAppend){
          254                                 werrstr("wstat -- attempt to change length of append-only file");
          255                                 goto error;
          256                         }
          257                         if(de.mode & ModeDir){
          258                                 werrstr("wstat -- attempt to change length of directory");
          259                                 goto error;
          260                         }
          261                         de.size = dir.length;
          262                         op = 1;
          263                 }
          264                 tsync = 0;
          265         }
          266 
          267         /*
          268          * Check for permission to change .mode, .mtime or .length,
          269          * must be owner or leader of either group, for which test gid
          270          * is needed; permission checks on gid will be done later.
          271          */
          272         if(dir.gid != nil && *dir.gid != '\0'){
          273                 if((gid = uidByUname(dir.gid)) == nil){
          274                         werrstr("wstat -- unknown gid");
          275                         goto error;
          276                 }
          277                 tsync = 0;
          278         }
          279         else
          280                 gid = vtstrdup(de.gid);
          281 
          282         wstatallow = (fsysWstatAllow(fid->fsys) || (m->con->flags&ConWstatAllow));
          283 
          284         /*
          285          * 'Gl' counts whether neither, one or both groups are led.
          286          */
          287         gl = groupLeader(gid, fid->uname) != 0;
          288         gl += groupLeader(de.gid, fid->uname) != 0;
          289 
          290         if(op && !wstatallow){
          291                 if(strcmp(fid->uid, de.uid) != 0 && !gl){
          292                         werrstr("wstat -- not owner or group leader");
          293                         goto error;
          294                 }
          295         }
          296 
          297         /*
          298          * Check for permission to change group, must be
          299          * either owner and in new group or leader of both groups.
          300          * If gid is nil here then
          301          */
          302         if(strcmp(gid, de.gid) != 0){
          303                 if(!wstatallow
          304                 && !(strcmp(fid->uid, de.uid) == 0 && groupMember(gid, fid->uname))
          305                 && !(gl == 2)){
          306                         werrstr("wstat -- not owner and not group leaders");
          307                         goto error;
          308                 }
          309                 vtfree(de.gid);
          310                 de.gid = gid;
          311                 gid = nil;
          312                 op = 1;
          313                 tsync = 0;
          314         }
          315 
          316         /*
          317          * Rename.
          318          * Check .name is valid and different to the current.
          319          * If so, check write permission in parent.
          320          */
          321         if(dir.name != nil && *dir.name != '\0'){
          322                 if(!validFileName(dir.name))
          323                         goto error;
          324                 if(strcmp(dir.name, de.elem) != 0){
          325                         if(permParent(fid, PermW) <= 0)
          326                                 goto error;
          327                         vtfree(de.elem);
          328                         de.elem = vtstrdup(dir.name);
          329                         op = 1;
          330                 }
          331                 tsync = 0;
          332         }
          333 
          334         /*
          335          * Check for permission to change owner - must be god.
          336          */
          337         if(dir.uid != nil && *dir.uid != '\0'){
          338                 if((uid = uidByUname(dir.uid)) == nil){
          339                         werrstr("wstat -- unknown uid");
          340                         goto error;
          341                 }
          342                 if(strcmp(uid, de.uid) != 0){
          343                         if(!wstatallow){
          344                                 werrstr("wstat -- not owner");
          345                                 goto error;
          346                         }
          347                         if(strcmp(uid, uidnoworld) == 0){
          348                                 werrstr(EPermission);
          349                                 goto error;
          350                         }
          351                         vtfree(de.uid);
          352                         de.uid = uid;
          353                         uid = nil;
          354                         op = 1;
          355                 }
          356                 tsync = 0;
          357         }
          358 
          359         if(op)
          360                 retval = fileSetDir(fid->file, &de, fid->uid);
          361         else
          362                 retval = 1;
          363 
          364         if(tsync){
          365                 /*
          366                  * All values were defaulted,
          367                  * make the state of the file exactly what it
          368                  * claims to be before returning...
          369                  */
          370                 USED(tsync);
          371         }
          372 
          373 error:
          374         deCleanup(&de);
          375         vtfree(strs);
          376         if(gid != nil)
          377                 vtfree(gid);
          378         if(uid != nil)
          379                 vtfree(uid);
          380 error0:
          381         fidPut(fid);
          382         return retval;
          383 };
          384 
          385 static int
          386 rTstat(Msg* m)
          387 {
          388         Dir dir;
          389         Fid *fid;
          390         DirEntry de;
          391 
          392         if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
          393                 return 0;
          394         if(fid->qid.type & QTAUTH){
          395                 memset(&dir, 0, sizeof(Dir));
          396                 dir.qid = fid->qid;
          397                 dir.mode = DMAUTH;
          398                 dir.atime = time(0L);
          399                 dir.mtime = dir.atime;
          400                 dir.length = 0;
          401                 dir.name = "#¿";
          402                 dir.uid = fid->uname;
          403                 dir.gid = fid->uname;
          404                 dir.muid = fid->uname;
          405 
          406                 if((m->r.nstat = convD2M(&dir, m->data, m->con->msize)) == 0){
          407                         werrstr("stat QTAUTH botch");
          408                         fidPut(fid);
          409                         return 0;
          410                 }
          411                 m->r.stat = m->data;
          412 
          413                 fidPut(fid);
          414                 return 1;
          415         }
          416         if(!fileGetDir(fid->file, &de)){
          417                 fidPut(fid);
          418                 return 0;
          419         }
          420         fidPut(fid);
          421 
          422         /*
          423          * TODO: optimise this copy (in convS2M) away somehow.
          424          * This pettifoggery with m->data will do for the moment.
          425          */
          426         m->r.nstat = dirDe2M(&de, m->data, m->con->msize);
          427         m->r.stat = m->data;
          428         deCleanup(&de);
          429 
          430         return 1;
          431 }
          432 
          433 static int
          434 _rTclunk(Fid* fid, int remove)
          435 {
          436         int rok;
          437 
          438         if(fid->excl)
          439                 exclFree(fid);
          440 
          441         rok = 1;
          442         if(remove && !(fid->qid.type & QTAUTH)){
          443                 if((rok = permParent(fid, PermW)) > 0)
          444                         rok = fileRemove(fid->file, fid->uid);
          445         }
          446         fidClunk(fid);
          447 
          448         return rok;
          449 }
          450 
          451 static int
          452 rTremove(Msg* m)
          453 {
          454         Fid *fid;
          455 
          456         if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
          457                 return 0;
          458         return _rTclunk(fid, 1);
          459 }
          460 
          461 static int
          462 rTclunk(Msg* m)
          463 {
          464         Fid *fid;
          465 
          466         if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
          467                 return 0;
          468         _rTclunk(fid, (fid->open & FidORclose));
          469 
          470         return 1;
          471 }
          472 
          473 static int
          474 rTwrite(Msg* m)
          475 {
          476         Fid *fid;
          477         int count, n;
          478 
          479         if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
          480                 return 0;
          481         if(!(fid->open & FidOWrite)){
          482                 werrstr("fid not open for write");
          483                 goto error;
          484         }
          485 
          486         count = m->t.count;
          487         if(count < 0 || count > m->con->msize-IOHDRSZ){
          488                 werrstr("write count too big");
          489                 goto error;
          490         }
          491         if(m->t.offset < 0){
          492                 werrstr("write offset negative");
          493                 goto error;
          494         }
          495         if(fid->excl != nil && !exclUpdate(fid))
          496                 goto error;
          497 
          498         if(fid->qid.type & QTDIR){
          499                 werrstr("is a directory");
          500                 goto error;
          501         }
          502         else if(fid->qid.type & QTAUTH)
          503                 n = authWrite(fid, m->t.data, count);
          504         else
          505                 n = fileWrite(fid->file, m->t.data, count, m->t.offset, fid->uid);
          506         if(n < 0)
          507                 goto error;
          508 
          509 
          510         m->r.count = n;
          511 
          512         fidPut(fid);
          513         return 1;
          514 
          515 error:
          516         fidPut(fid);
          517         return 0;
          518 }
          519 
          520 static int
          521 rTread(Msg* m)
          522 {
          523         Fid *fid;
          524         uchar *data;
          525         int count, n;
          526 
          527         if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
          528                 return 0;
          529         if(!(fid->open & FidORead)){
          530                 werrstr("fid not open for read");
          531                 goto error;
          532         }
          533 
          534         count = m->t.count;
          535         if(count < 0 || count > m->con->msize-IOHDRSZ){
          536                 werrstr("read count too big");
          537                 goto error;
          538         }
          539         if(m->t.offset < 0){
          540                 werrstr("read offset negative");
          541                 goto error;
          542         }
          543         if(fid->excl != nil && !exclUpdate(fid))
          544                 goto error;
          545 
          546         /*
          547          * TODO: optimise this copy (in convS2M) away somehow.
          548          * This pettifoggery with m->data will do for the moment.
          549          */
          550         data = m->data+IOHDRSZ;
          551         if(fid->qid.type & QTDIR)
          552                 n = dirRead(fid, data, count, m->t.offset);
          553         else if(fid->qid.type & QTAUTH)
          554                 n = authRead(fid, data, count);
          555         else
          556                 n = fileRead(fid->file, data, count, m->t.offset);
          557         if(n < 0)
          558                 goto error;
          559 
          560         m->r.count = n;
          561         m->r.data = (char*)data;
          562 
          563         fidPut(fid);
          564         return 1;
          565 
          566 error:
          567         fidPut(fid);
          568         return 0;
          569 }
          570 
          571 static int
          572 rTcreate(Msg* m)
          573 {
          574         Fid *fid;
          575         File *file;
          576         ulong mode;
          577         int omode, open, perm;
          578 
          579         if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
          580                 return 0;
          581         if(fid->open){
          582                 werrstr("fid open for I/O");
          583                 goto error;
          584         }
          585         if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
          586                 werrstr("read-only filesystem");
          587                 goto error;
          588         }
          589         if(!fileIsDir(fid->file)){
          590                 werrstr("not a directory");
          591                 goto error;
          592         }
          593         if(permFid(fid, PermW) <= 0)
          594                 goto error;
          595         if(!validFileName(m->t.name))
          596                 goto error;
          597         if(strcmp(fid->uid, uidnoworld) == 0){
          598                 werrstr(EPermission);
          599                 goto error;
          600         }
          601 
          602         omode = m->t.mode & OMODE;
          603         open = 0;
          604 
          605         if(omode == OREAD || omode == ORDWR || omode == OEXEC)
          606                 open |= FidORead;
          607         if(omode == OWRITE || omode == ORDWR)
          608                 open |= FidOWrite;
          609         if((open & (FidOWrite|FidORead)) == 0){
          610                 werrstr("unknown mode");
          611                 goto error;
          612         }
          613         if(m->t.perm & DMDIR){
          614                 if((m->t.mode & (ORCLOSE|OTRUNC)) || (open & FidOWrite)){
          615                         werrstr("illegal mode");
          616                         goto error;
          617                 }
          618                 if(m->t.perm & DMAPPEND){
          619                         werrstr("illegal perm");
          620                         goto error;
          621                 }
          622         }
          623 
          624         mode = fileGetMode(fid->file);
          625         perm = m->t.perm;
          626         if(m->t.perm & DMDIR)
          627                 perm &= ~0777|(mode & 0777);
          628         else
          629                 perm &= ~0666|(mode & 0666);
          630         mode = perm & 0777;
          631         if(m->t.perm & DMDIR)
          632                 mode |= ModeDir;
          633         if(m->t.perm & DMAPPEND)
          634                 mode |= ModeAppend;
          635         if(m->t.perm & DMEXCL)
          636                 mode |= ModeExclusive;
          637         if(m->t.perm & DMTMP)
          638                 mode |= ModeTemporary;
          639 
          640         if((file = fileCreate(fid->file, m->t.name, mode, fid->uid)) == nil){
          641                 fidPut(fid);
          642                 return 0;
          643         }
          644         fileDecRef(fid->file);
          645 
          646         fid->qid.vers = fileGetMcount(file);
          647         fid->qid.path = fileGetId(file);
          648         fid->file = file;
          649         mode = fileGetMode(fid->file);
          650         if(mode & ModeDir)
          651                 fid->qid.type = QTDIR;
          652         else
          653                 fid->qid.type = QTFILE;
          654         if(mode & ModeAppend)
          655                 fid->qid.type |= QTAPPEND;
          656         if(mode & ModeExclusive){
          657                 fid->qid.type |= QTEXCL;
          658                 assert(exclAlloc(fid) != 0);
          659         }
          660         if(m->t.mode & ORCLOSE)
          661                 open |= FidORclose;
          662         fid->open = open;
          663 
          664         m->r.qid = fid->qid;
          665         m->r.iounit = m->con->msize-IOHDRSZ;
          666 
          667         fidPut(fid);
          668         return 1;
          669 
          670 error:
          671         fidPut(fid);
          672         return 0;
          673 }
          674 
          675 static int
          676 rTopen(Msg* m)
          677 {
          678         Fid *fid;
          679         int isdir, mode, omode, open, rofs;
          680 
          681         if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
          682                 return 0;
          683         if(fid->open){
          684                 werrstr("fid open for I/O");
          685                 goto error;
          686         }
          687 
          688         isdir = fileIsDir(fid->file);
          689         open = 0;
          690         rofs = fileIsRoFs(fid->file) || !groupWriteMember(fid->uname);
          691 
          692         if(m->t.mode & ORCLOSE){
          693                 if(isdir){
          694                         werrstr("is a directory");
          695                         goto error;
          696                 }
          697                 if(rofs){
          698                         werrstr("read-only filesystem");
          699                         goto error;
          700                 }
          701                 if(permParent(fid, PermW) <= 0)
          702                         goto error;
          703 
          704                 open |= FidORclose;
          705         }
          706 
          707         omode = m->t.mode & OMODE;
          708         if(omode == OREAD || omode == ORDWR){
          709                 if(permFid(fid, PermR) <= 0)
          710                         goto error;
          711                 open |= FidORead;
          712         }
          713         if(omode == OWRITE || omode == ORDWR || (m->t.mode & OTRUNC)){
          714                 if(isdir){
          715                         werrstr("is a directory");
          716                         goto error;
          717                 }
          718                 if(rofs){
          719                         werrstr("read-only filesystem");
          720                         goto error;
          721                 }
          722                 if(permFid(fid, PermW) <= 0)
          723                         goto error;
          724                 open |= FidOWrite;
          725         }
          726         if(omode == OEXEC){
          727                 if(isdir){
          728                         werrstr("is a directory");
          729                         goto error;
          730                 }
          731                 if(permFid(fid, PermX) <= 0)
          732                         goto error;
          733                 open |= FidORead;
          734         }
          735         if((open & (FidOWrite|FidORead)) == 0){
          736                 werrstr("unknown mode");
          737                 goto error;
          738         }
          739 
          740         mode = fileGetMode(fid->file);
          741         if((mode & ModeExclusive) && exclAlloc(fid) == 0)
          742                 goto error;
          743 
          744         /*
          745          * Everything checks out, try to commit any changes.
          746          */
          747         if((m->t.mode & OTRUNC) && !(mode & ModeAppend))
          748                 if(!fileTruncate(fid->file, fid->uid))
          749                         goto error;
          750 
          751         if(isdir && fid->db != nil){
          752                 dirBufFree(fid->db);
          753                 fid->db = nil;
          754         }
          755 
          756         fid->qid.vers = fileGetMcount(fid->file);
          757         m->r.qid = fid->qid;
          758         m->r.iounit = m->con->msize-IOHDRSZ;
          759 
          760         fid->open = open;
          761 
          762         fidPut(fid);
          763         return 1;
          764 
          765 error:
          766         if(fid->excl != nil)
          767                 exclFree(fid);
          768         fidPut(fid);
          769         return 0;
          770 }
          771 
          772 static int
          773 rTwalk(Msg* m)
          774 {
          775         Qid qid;
          776         Fcall *r, *t;
          777         int nwname, wlock;
          778         File *file, *nfile;
          779         Fid *fid, *ofid, *nfid;
          780 
          781         t = &m->t;
          782         if(t->fid == t->newfid)
          783                 wlock = FidFWlock;
          784         else
          785                 wlock = 0;
          786 
          787         /*
          788          * The file identified by t->fid must be valid in the
          789          * current session and must not have been opened for I/O
          790          * by an open or create message.
          791          */
          792         if((ofid = fidGet(m->con, t->fid, wlock)) == nil)
          793                 return 0;
          794         if(ofid->open){
          795                 werrstr("file open for I/O");
          796                 fidPut(ofid);
          797                 return 0;
          798         }
          799 
          800         /*
          801          * If newfid is not the same as fid, allocate a new file;
          802          * a side effect is checking newfid is not already in use (error);
          803          * if there are no names to walk this will be equivalent to a
          804          * simple 'clone' operation.
          805          * It's a no-op if newfid is the same as fid and t->nwname is 0.
          806          */
          807         nfid = nil;
          808         if(t->fid != t->newfid){
          809                 nfid = fidGet(m->con, t->newfid, FidFWlock|FidFCreate);
          810                 if(nfid == nil){
          811                         werrstr("%s: walk: newfid 0x%ud in use",
          812                                 argv0, t->newfid);
          813                         fidPut(ofid);
          814                         return 0;
          815                 }
          816                 nfid->open = ofid->open & ~FidORclose;
          817                 nfid->file = fileIncRef(ofid->file);
          818                 nfid->qid = ofid->qid;
          819                 nfid->uid = vtstrdup(ofid->uid);
          820                 nfid->uname = vtstrdup(ofid->uname);
          821                 nfid->fsys = fsysIncRef(ofid->fsys);
          822                 fid = nfid;
          823         }
          824         else
          825                 fid = ofid;
          826 
          827         r = &m->r;
          828         r->nwqid = 0;
          829 
          830         if(t->nwname == 0){
          831                 if(nfid != nil)
          832                         fidPut(nfid);
          833                 fidPut(ofid);
          834 
          835                 return 1;
          836         }
          837 
          838         file = fid->file;
          839         fileIncRef(file);
          840         qid = fid->qid;
          841 
          842         for(nwname = 0; nwname < t->nwname; nwname++){
          843                 /*
          844                  * Walked elements must represent a directory and
          845                  * the implied user must have permission to search
          846                  * the directory.  Walking .. is always allowed, so that
          847                  * you can't walk into a directory and then not be able
          848                  * to walk out of it.
          849                  */
          850                 if(!(qid.type & QTDIR)){
          851                         werrstr("not a directory");
          852                         break;
          853                 }
          854                 switch(permFile(file, fid, PermX)){
          855                 case 1:
          856                         break;
          857                 case 0:
          858                         if(strcmp(t->wname[nwname], "..") == 0)
          859                                 break;
          860                 case -1:
          861                         goto Out;
          862                 }
          863                 if((nfile = fileWalk(file, t->wname[nwname])) == nil)
          864                         break;
          865                 fileDecRef(file);
          866                 file = nfile;
          867                 qid.type = QTFILE;
          868                 if(fileIsDir(file))
          869                         qid.type = QTDIR;
          870                 if(fileIsAppend(file))
          871                         qid.type |= QTAPPEND;
          872                 if(fileIsTemporary(file))
          873                         qid.type |= QTTMP;
          874                 if(fileIsExclusive(file))
          875                         qid.type |= QTEXCL;
          876                 qid.vers = fileGetMcount(file);
          877                 qid.path = fileGetId(file);
          878                 r->wqid[r->nwqid++] = qid;
          879         }
          880 
          881         if(nwname == t->nwname){
          882                 /*
          883                  * Walked all elements. Update the target fid
          884                  * from the temporary qid used during the walk,
          885                  * and tidy up.
          886                  */
          887                 fid->qid = r->wqid[r->nwqid-1];
          888                 fileDecRef(fid->file);
          889                 fid->file = file;
          890 
          891                 if(nfid != nil)
          892                         fidPut(nfid);
          893 
          894                 fidPut(ofid);
          895                 return 1;
          896         }
          897 
          898 Out:
          899         /*
          900          * Didn't walk all elements, 'clunk' nfid if it exists
          901          * and leave fid untouched.
          902          * It's not an error if some of the elements were walked OK.
          903          */
          904         fileDecRef(file);
          905         if(nfid != nil)
          906                 fidClunk(nfid);
          907 
          908         fidPut(ofid);
          909         if(nwname == 0)
          910                 return 0;
          911         return 1;
          912 }
          913 
          914 static int
          915 rTflush(Msg* m)
          916 {
          917         if(m->t.oldtag != NOTAG)
          918                 msgFlush(m);
          919         return 1;
          920 }
          921 
          922 static void
          923 parseAname(char *aname, char **fsname, char **path)
          924 {
          925         char *s;
          926 
          927         if(aname && aname[0])
          928                 s = vtstrdup(aname);
          929         else
          930                 s = vtstrdup("main/active");
          931         *fsname = s;
          932         if((*path = strchr(s, '/')) != nil)
          933                 *(*path)++ = '\0';
          934         else
          935                 *path = "";
          936 }
          937 
          938 #ifndef PLAN9PORT
          939 /*
          940  * Check remote IP address against /mnt/ipok.
          941  * Sources.cs.bell-labs.com uses this to disallow
          942  * network connections from Sudan, Libya, etc.,
          943  * following U.S. cryptography export regulations.
          944  */
          945 static int
          946 conIPCheck(Con* con)
          947 {
          948         char ok[256], *p;
          949         int fd;
          950 
          951         if(con->flags&ConIPCheck){
          952                 if(con->remote[0] == 0){
          953                         werrstr("cannot verify unknown remote address");
          954                         return 0;
          955                 }
          956                 if(access("/mnt/ipok/ok", AEXIST) < 0){
          957                         /* mount closes the fd on success */
          958                         if((fd = open("/srv/ipok", ORDWR)) >= 0
          959                         && mount(fd, -1, "/mnt/ipok", MREPL, "") < 0)
          960                                 close(fd);
          961                         if(access("/mnt/ipok/ok", AEXIST) < 0){
          962                                 werrstr("cannot verify remote address");
          963                                 return 0;
          964                         }
          965                 }
          966                 snprint(ok, sizeof ok, "/mnt/ipok/ok/%s", con->remote);
          967                 if((p = strchr(ok, '!')) != nil)
          968                         *p = 0;
          969                 if(access(ok, AEXIST) < 0){
          970                         werrstr("restricted remote address");
          971                         return 0;
          972                 }
          973         }
          974         return 1;
          975 }
          976 #endif
          977 
          978 static int
          979 rTattach(Msg* m)
          980 {
          981         Fid *fid;
          982         Fsys *fsys;
          983         char *fsname, *path;
          984 
          985         if((fid = fidGet(m->con, m->t.fid, FidFWlock|FidFCreate)) == nil)
          986                 return 0;
          987 
          988         parseAname(m->t.aname, &fsname, &path);
          989         if((fsys = fsysGet(fsname)) == nil){
          990                 fidClunk(fid);
          991                 vtfree(fsname);
          992                 return 0;
          993         }
          994         fid->fsys = fsys;
          995 
          996         if(m->t.uname[0] != '\0')
          997                 fid->uname = vtstrdup(m->t.uname);
          998         else
          999                 fid->uname = vtstrdup(unamenone);
         1000 
         1001 #ifndef PLAN9PORT
         1002         if((fid->con->flags&ConIPCheck) && !conIPCheck(fid->con)){
         1003                 consPrint("reject %s from %s: %r\n", fid->uname, fid->con->remote);
         1004                 fidClunk(fid);
         1005                 vtfree(fsname);
         1006                 return 0;
         1007         }
         1008 #endif
         1009         if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
         1010                 if((fid->uid = uidByUname(fid->uname)) == nil)
         1011                         fid->uid = vtstrdup(unamenone);
         1012         }
         1013         else if(!authCheck(&m->t, fid, fsys)){
         1014                 fidClunk(fid);
         1015                 vtfree(fsname);
         1016                 return 0;
         1017         }
         1018 
         1019         fsysFsRlock(fsys);
         1020         if((fid->file = fsysGetRoot(fsys, path)) == nil){
         1021                 fsysFsRUnlock(fsys);
         1022                 fidClunk(fid);
         1023                 vtfree(fsname);
         1024                 return 0;
         1025         }
         1026         fsysFsRUnlock(fsys);
         1027         vtfree(fsname);
         1028 
         1029         fid->qid = (Qid){fileGetId(fid->file), 0, QTDIR};
         1030         m->r.qid = fid->qid;
         1031 
         1032         fidPut(fid);
         1033         return 1;
         1034 }
         1035 
         1036 static int
         1037 rTauth(Msg* m)
         1038 {
         1039 #ifndef PLAN9PORT
         1040         int afd;
         1041 #endif
         1042         Con *con;
         1043         Fid *afid;
         1044         Fsys *fsys;
         1045         char *fsname, *path;
         1046 
         1047         parseAname(m->t.aname, &fsname, &path);
         1048         if((fsys = fsysGet(fsname)) == nil){
         1049                 vtfree(fsname);
         1050                 return 0;
         1051         }
         1052         vtfree(fsname);
         1053 
         1054         if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
         1055                 m->con->aok = 1;
         1056                 werrstr("authentication disabled");
         1057                 fsysPut(fsys);
         1058                 return 0;
         1059         }
         1060         if(strcmp(m->t.uname, unamenone) == 0){
         1061                 werrstr("user 'none' requires no authentication");
         1062                 fsysPut(fsys);
         1063                 return 0;
         1064         }
         1065 
         1066         con = m->con;
         1067         if((afid = fidGet(con, m->t.afid, FidFWlock|FidFCreate)) == nil){
         1068                 fsysPut(fsys);
         1069                 return 0;
         1070         }
         1071         afid->fsys = fsys;
         1072 
         1073 #ifndef PLAN9PORT
         1074         if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
         1075                 werrstr("can't open \"/mnt/factotum/rpc\"");
         1076                 fidClunk(afid);
         1077                 return 0;
         1078         }
         1079 #endif
         1080 
         1081 #ifdef PLAN9PORT
         1082         if((afid->rpc = auth_allocrpc()) == nil){
         1083 #else
         1084         if((afid->rpc = auth_allocrpc(afd)) == nil){
         1085                 close(afd);
         1086 #endif
         1087                 werrstr("can't auth_allocrpc");
         1088                 fidClunk(afid);
         1089                 return 0;
         1090         }
         1091         if(auth_rpc(afid->rpc, "start", "proto=p9any role=server", 23) != ARok){
         1092                 werrstr("can't auth_rpc");
         1093                 fidClunk(afid);
         1094                 return 0;
         1095         }
         1096 
         1097         afid->open = FidOWrite|FidORead;
         1098         afid->qid.type = QTAUTH;
         1099         afid->qid.path = m->t.afid;
         1100         afid->uname = vtstrdup(m->t.uname);
         1101 
         1102         m->r.qid = afid->qid;
         1103 
         1104         fidPut(afid);
         1105         return 1;
         1106 }
         1107 
         1108 static int
         1109 rTversion(Msg* m)
         1110 {
         1111         int v;
         1112         Con *con;
         1113         Fcall *r, *t;
         1114 
         1115         t = &m->t;
         1116         r = &m->r;
         1117         con = m->con;
         1118 
         1119         qlock(&con->lock);
         1120         if(con->state != ConInit){
         1121                 qunlock(&con->lock);
         1122                 werrstr("Tversion: down");
         1123                 return 0;
         1124         }
         1125         con->state = ConNew;
         1126 
         1127         /*
         1128          * Release the karma of past lives and suffering.
         1129          * Should this be done before or after checking the
         1130          * validity of the Tversion?
         1131          */
         1132         fidClunkAll(con);
         1133 
         1134         if(t->tag != NOTAG){
         1135                 qunlock(&con->lock);
         1136                 werrstr("Tversion: invalid tag");
         1137                 return 0;
         1138         }
         1139 
         1140         if(t->msize < 256){
         1141                 qunlock(&con->lock);
         1142                 werrstr("Tversion: message size too small");
         1143                 return 0;
         1144         }
         1145         if(t->msize < con->msize)
         1146                 r->msize = t->msize;
         1147         else
         1148                 r->msize = con->msize;
         1149 
         1150         r->version = "unknown";
         1151         if(t->version[0] == '9' && t->version[1] == 'P'){
         1152                 /*
         1153                  * Currently, the only defined version
         1154                  * is "9P2000"; ignore any later versions.
         1155                  */
         1156                 v = strtol(&t->version[2], 0, 10);
         1157                 if(v >= 2000){
         1158                         r->version = VERSION9P;
         1159                         con->msize = r->msize;
         1160                         con->state = ConUp;
         1161                 }
         1162                 else if(strcmp(t->version, "9PEoF") == 0){
         1163                         r->version = "9PEoF";
         1164                         con->msize = r->msize;
         1165                         con->state = ConMoribund;
         1166 
         1167                         /*
         1168                          * Don't want to attempt to write this
         1169                          * message as the connection may be already
         1170                          * closed.
         1171                          */
         1172                         m->state = MsgF;
         1173                 }
         1174         }
         1175         qunlock(&con->lock);
         1176 
         1177         return 1;
         1178 }
         1179 
         1180 int (*rFcall[Tmax])(Msg*) = {
         1181         [Tversion]        = rTversion,
         1182         [Tauth]                = rTauth,
         1183         [Tattach]        = rTattach,
         1184         [Tflush]        = rTflush,
         1185         [Twalk]                = rTwalk,
         1186         [Topen]                = rTopen,
         1187         [Tcreate]        = rTcreate,
         1188         [Tread]                = rTread,
         1189         [Twrite]        = rTwrite,
         1190         [Tclunk]        = rTclunk,
         1191         [Tremove]        = rTremove,
         1192         [Tstat]                = rTstat,
         1193         [Twstat]        = rTwstat,
         1194 };