]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/9p2.c
rio: fix goodrect() bug (thanks mike)
[plan9front.git] / sys / src / cmd / cwfs / 9p2.c
1 #include "all.h"
2 #include <fcall.h>
3
4 enum { MSIZE = MAXDAT+MAXMSG };
5
6 static int
7 mkmode9p1(ulong mode9p2)
8 {
9         int mode;
10
11         /*
12          * Assume this is for an allocated entry.
13          */
14         mode = DALLOC|(mode9p2 & 0777);
15         if(mode9p2 & DMEXCL)
16                 mode |= DLOCK;
17         if(mode9p2 & DMAPPEND)
18                 mode |= DAPND;
19         if(mode9p2 & DMDIR)
20                 mode |= DDIR;
21         if(mode9p2 & DMTMP)
22                 mode |= DTMP;
23
24         return mode;
25 }
26
27 void
28 mkqid9p1(Qid9p1* qid9p1, Qid* qid)
29 {
30         qid9p1->path = qid->path;
31         if(qid->type & QTDIR)
32                 qid9p1->path ^= QPDIR;
33         qid9p1->version = qid->vers;
34 }
35
36 static int
37 mktype9p2(int mode9p1)
38 {
39         int type;
40
41         type = 0;
42         if(mode9p1 & DLOCK)
43                 type |= QTEXCL;
44         if(mode9p1 & DAPND)
45                 type |= QTAPPEND;
46         if(mode9p1 & DDIR)
47                 type |= QTDIR;
48         if(mode9p1 & DTMP)
49                 type |= QTTMP;
50
51         return type;
52 }
53
54 static ulong
55 mkmode9p2(int mode9p1)
56 {
57         ulong mode;
58
59         mode = mode9p1 & 0777;
60         if(mode9p1 & DLOCK)
61                 mode |= DMEXCL;
62         if(mode9p1 & DAPND)
63                 mode |= DMAPPEND;
64         if(mode9p1 & DDIR)
65                 mode |= DMDIR;
66         if(mode9p1 & DTMP)
67                 mode |= DMTMP;
68
69         return mode;
70 }
71
72 void
73 mkqid9p2(Qid* qid, Qid9p1* qid9p1, int mode9p1)
74 {
75         qid->path = qid9p1->path;
76         if(mode9p1 & DDIR)
77                 qid->path ^= QPDIR;
78         qid->vers = qid9p1->version;
79         qid->type = mktype9p2(mode9p1);
80 }
81
82 static int
83 mkdir9p2(Dir* dir, Dentry* dentry, void* strs)
84 {
85         char *op, *p;
86
87         memset(dir, 0, sizeof(Dir));
88         mkqid(&dir->qid, dentry, 1);
89         dir->mode = mkmode9p2(dentry->mode);
90         dir->atime = dentry->atime;
91         dir->mtime = dentry->mtime;
92         dir->length = dentry->size;
93
94         op = p = strs;
95         dir->name = p;
96         strncpy(p, dentry->name, NAMELEN);
97         p[NAMELEN-1] = 0;
98         p += strlen(p)+1;
99
100         dir->uid = p;
101         uidtostr(p, dentry->uid, 1);
102         p += strlen(p)+1;
103
104         dir->gid = p;
105         uidtostr(p, dentry->gid, 1);
106         p += strlen(p)+1;
107
108         dir->muid = p;
109         uidtostr(p, dentry->muid, 1);
110         p += strlen(p)+1;
111
112         return p-op;
113 }
114
115 /*
116  * buggery to give false qid for
117  * the top 2 levels of the dump fs
118  */
119 void
120 mkqid(Qid* qid, Dentry *d, int buggery)
121 {
122         int c;
123
124         if(buggery && d->qid.path == (QPDIR|QPROOT)){
125                 c = d->name[0];
126                 if(isascii(c) && isdigit(c)){
127                         qid->path = 3;
128                         qid->vers = d->qid.version;
129                         qid->type = QTDIR;
130
131                         c = (c-'0')*10 + (d->name[1]-'0');
132                         if(c >= 1 && c <= 12)
133                                 qid->path = 4;
134                         return;
135                 }
136         }
137
138         mkqid9p2(qid, &d->qid, d->mode);
139 }
140
141 int
142 mkqidcmp(Qid* qid, Dentry *d)
143 {
144         Qid tmp;
145
146         mkqid(&tmp, d, 1);
147         if(qid->path == tmp.path && qid->type == tmp.type)
148                 return 0;
149         return Eqid;
150 }
151
152 int
153 version(Chan* chan, Fcall* f, Fcall* r)
154 {
155         if(chan->protocol != nil || f->msize < 256)
156                 return Eversion;
157
158         if(f->msize < MSIZE)
159                 r->msize = f->msize;
160         else
161                 r->msize = MSIZE;
162
163         /*
164          * Should check the '.' stuff here.
165          */
166         if(strcmp(f->version, VERSION9P) == 0){
167                 r->version = VERSION9P;
168                 chan->protocol = serve9p2;
169                 chan->msize = r->msize;
170         } else
171                 r->version = "unknown";
172
173         fileinit(chan);
174         return 0;
175 }
176
177
178 static int
179 auth(Chan* chan, Fcall* f, Fcall* r)
180 {
181         static struct {
182                 Lock;
183                 ulong   hi;
184         } authpath;
185         char *aname;
186         File *file;
187         Filsys *fs;
188         int error;
189
190         if(noauth)
191                 return Eauthdisabled;
192
193         error = 0;
194         aname = f->aname;
195
196         if(strcmp(f->uname, "none") == 0)
197                 return Eauthnone;
198
199         if(!aname[0])   /* default */
200                 aname = "main";
201         file = filep(chan, f->afid, 1);
202         if(file == nil){
203                 error = Efidinuse;
204                 goto out;
205         }
206         fs = fsstr(aname);
207         if(fs == nil){
208                 error = Ebadspc;
209                 goto out;
210         }
211         lock(&authpath);
212         file->qid.path = authpath.hi++;
213         unlock(&authpath);
214         file->qid.type = QTAUTH;
215         file->qid.vers = 0;
216         file->fs = fs;
217         file->open = FREAD+FWRITE;
218         freewp(file->wpath);
219         file->wpath = 0;
220         file->uid = -1;
221         if((file->auth = authnew()) == nil){
222                 error = Eauthfile;
223                 goto out;
224         }
225         r->aqid = file->qid;
226 out:
227         if(file != nil){
228                 qunlock(file);
229                 if(error)
230                         freefp(file);
231         }
232         return error;
233 }
234
235 static int
236 authorize(Chan* chan, Fcall* f)
237 {
238         File* af;
239         int db, uid;
240
241         db = cons.flags & authdebugflag;
242
243         if(noauth){
244                 uid = strtouid(f->uname);
245                 if(db)
246                         fprint(2, "permission granted by noauth uid %s = %d\n",
247                                 f->uname, uid);
248                 return uid;
249         }
250
251         if(f->afid == NOFID && (!nonone || chan->authok)){
252                 uid = strtouid(f->uname);
253                 if(db)
254                         fprint(2, "permission granted to none: uid %s = %d\n",
255                                 f->uname, uid);
256                 return 0; /* none */
257         }
258
259         af = filep(chan, f->afid, 0);
260         if(af == nil){
261                 if(db)
262                         fprint(2, "authorize: af == nil\n");
263                 return -1;
264         }
265
266         /* fake read to get auth info */
267         authread(af, nil, 0);
268         uid = af->uid;
269         if(db)
270                 fprint(2, "authorize: uid is %d\n", uid);
271         qunlock(af);
272
273         if(uid > 0)
274                 chan->authok = 1;
275
276         return uid;
277 }
278
279 int
280 attach(Chan* chan, Fcall* f, Fcall* r)
281 {
282         char *aname;
283         Iobuf *p;
284         Dentry *d;
285         File *file;
286         Filsys *fs;
287         Off raddr;
288         int error, u;
289
290         aname = f->aname;
291         if(!aname[0])   /* default */
292                 aname = "main";
293         p = nil;
294         error = 0;
295         file = filep(chan, f->fid, 1);
296         if(file == nil){
297                 error = Efidinuse;
298                 goto out;
299         }
300
301         u = -1;
302         if(chan != cons.chan){
303                 if(noattach && strcmp(f->uname, "none")) {
304                         error = Enoattach;
305                         goto out;
306                 }
307                 u = authorize(chan, f);
308                 if(u < 0){
309                         error = Ebadu;
310                         goto out;
311                 }
312                 chan->err[0] = 0;
313         }
314         file->uid = u;
315
316         fs = fsstr(aname);
317         if(fs == nil){
318                 error = Ebadspc;
319                 goto out;
320         }
321         raddr = getraddr(fs->dev);
322         p = getbuf(fs->dev, raddr, Brd);
323         if(p == nil || checktag(p, Tdir, QPROOT)){
324                 error = Ealloc;
325                 goto out;
326         }
327         d = getdir(p, 0);
328         if(d == nil || !(d->mode & DALLOC)){
329                 error = Ealloc;
330                 goto out;
331         }
332         if(iaccess(file, d, DEXEC) ||
333             file->uid == 0 && fs->dev->type == Devro) {
334                 /*
335                  * 'none' not allowed on dump
336                  */
337                 error = Eaccess;
338                 goto out;
339         }
340         accessdir(p, d, FREAD, file->uid);
341         mkqid(&file->qid, d, 1);
342         file->fs = fs;
343         file->addr = raddr;
344         file->slot = 0;
345         file->open = 0;
346         freewp(file->wpath);
347         file->wpath = 0;
348
349         r->qid = file->qid;
350
351         snprint(chan->whoname, sizeof(chan->whoname), "%s", f->uname);
352         chan->whotime = time(nil);
353 out:
354         if(p != nil)
355                 putbuf(p);
356         if(file != nil){
357                 qunlock(file);
358                 if(error)
359                         freefp(file);
360         }
361         return error;
362 }
363
364 static int
365 flush(Chan* chan, Fcall*, Fcall*)
366 {
367         runlock(&chan->reflock);
368         wlock(&chan->reflock);
369         wunlock(&chan->reflock);
370         rlock(&chan->reflock);
371
372         return 0;
373 }
374
375 static void
376 clone(File* nfile, File* file)
377 {
378         Wpath *wpath;
379
380         nfile->qid = file->qid;
381
382         lock(&wpathlock);
383         nfile->wpath = file->wpath;
384         for(wpath = nfile->wpath; wpath != nil; wpath = wpath->up)
385                 wpath->refs++;
386         unlock(&wpathlock);
387
388         nfile->fs = file->fs;
389         nfile->addr = file->addr;
390         nfile->slot = file->slot;
391         nfile->uid = file->uid;
392         nfile->open = file->open & ~FREMOV;
393 }
394
395 static int
396 walkname(File* file, char* wname, Qid* wqid)
397 {
398         Wpath *w;
399         Iobuf *p, *p1;
400         Dentry *d, *d1;
401         int error, slot, mask;
402         Off addr;
403
404         p = p1 = nil;
405
406         /*
407          * File must not have been opened for I/O by an open
408          * or create message and must represent a directory.
409          */
410         if(file->open != 0){
411                 error = Emode;
412                 goto out;
413         }
414
415         p = getbuf(file->fs->dev, file->addr, Brd);
416         if(p == nil || checktag(p, Tdir, QPNONE)){
417                 error = Edir1;
418                 goto out;
419         }
420         d = getdir(p, file->slot);
421         if(d == nil || !(d->mode & DALLOC)){
422                 error = Ealloc;
423                 goto out;
424         }
425         if(!(d->mode & DDIR)){
426                 error = Edir1;
427                 goto out;
428         }
429         if(error = mkqidcmp(&file->qid, d))
430                 goto out;
431
432         /*
433          * For walked elements the implied user must
434          * have permission to search the directory.
435          */
436         if(iaccess(file, d, DEXEC)){
437                 error = Eaccess;
438                 goto out;
439         }
440         accessdir(p, d, FREAD, file->uid);
441
442         if(strcmp(wname, ".") == 0){
443 setdot:
444                 if(wqid != nil)
445                         *wqid = file->qid;
446                 goto out;
447         }
448         if(strcmp(wname, "..") == 0){
449                 if(file->wpath == 0)
450                         goto setdot;
451                 putbuf(p);
452                 p = nil;
453                 addr = file->wpath->addr;
454                 slot = file->wpath->slot;
455                 p1 = getbuf(file->fs->dev, addr, Brd);
456                 if(p1 == nil || checktag(p1, Tdir, QPNONE)){
457                         error = Edir1;
458                         goto out;
459                 }
460                 d1 = getdir(p1, slot);
461                 if(d == nil || !(d1->mode & DALLOC)){
462                         error = Ephase;
463                         goto out;
464                 }
465                 lock(&wpathlock);
466                 file->wpath->refs--;
467                 file->wpath = file->wpath->up;
468                 unlock(&wpathlock);
469                 goto found;
470         }
471
472         for(addr = 0; ; addr++){
473                 if(p == nil){
474                         p = getbuf(file->fs->dev, file->addr, Brd);
475                         if(p == nil || checktag(p, Tdir, QPNONE)){
476                                 error = Ealloc;
477                                 goto out;
478                         }
479                         d = getdir(p, file->slot);
480                         if(d == nil || !(d->mode & DALLOC)){
481                                 error = Ealloc;
482                                 goto out;
483                         }
484                 }
485                 p1 = dnodebuf1(p, d, addr, 0, file->uid);
486                 p = nil;
487                 if(p1 == nil || checktag(p1, Tdir, d->qid.path ^ QPDIR)){
488                         error = Eentry;
489                         goto out;
490                 }
491                 mask = DALLOC;
492                 if(file->fs->dev->type == Devro)
493                         mask |= DTMP;
494                 for(slot = 0; slot < DIRPERBUF; slot++){
495                         d1 = getdir(p1, slot);
496                         if ((d1->mode & mask) != DALLOC ||
497                             strncmp(wname, d1->name, NAMELEN) != 0)
498                                 continue;
499                         /*
500                          * update walk path
501                          */
502                         if((w = newwp()) == nil){
503                                 error = Ewalk;
504                                 goto out;
505                         }
506                         w->addr = file->addr;
507                         w->slot = file->slot;
508                         w->up = file->wpath;
509                         file->wpath = w;
510                         slot += DIRPERBUF*addr;
511                         goto found;
512                 }
513                 putbuf(p1);
514                 p1 = nil;
515         }
516
517 found:
518         file->addr = p1->addr;
519         mkqid(&file->qid, d1, 1);
520         putbuf(p1);
521         p1 = nil;
522         file->slot = slot;
523         if(wqid != nil)
524                 *wqid = file->qid;
525
526 out:
527         if(p1 != nil)
528                 putbuf(p1);
529         if(p != nil)
530                 putbuf(p);
531
532         return error;
533 }
534
535 int
536 walk(Chan* chan, Fcall* f, Fcall* r)
537 {
538         int error, nwname;
539         File *file, *nfile, tfile;
540
541         /*
542          * The file identified by f->fid must be valid in the
543          * current session and must not have been opened for I/O
544          * by an open or create message.
545          */
546         if((file = filep(chan, f->fid, 0)) == nil)
547                 return Efid;
548         if(file->open != 0){
549                 qunlock(file);
550                 return Emode;
551         }
552
553         /*
554          * If newfid is not the same as fid, allocate a new file;
555          * a side effect is checking newfid is not already in use (error);
556          * if there are no names to walk this will be equivalent to a
557          * simple 'clone' operation.
558          * Otherwise, fid and newfid are the same and if there are names
559          * to walk make a copy of 'file' to be used during the walk as
560          * 'file' must only be updated on success.
561          * Finally, it's a no-op if newfid is the same as fid and f->nwname
562          * is 0.
563          */
564         r->nwqid = 0;
565         if(f->newfid != f->fid){
566                 if((nfile = filep(chan, f->newfid, 1)) == nil){
567                         qunlock(file);
568                         return Efidinuse;
569                 }
570         } else if(f->nwname != 0){
571                 nfile = &tfile;
572                 memset(nfile, 0, sizeof(File));
573                 nfile->cp = chan;
574                 nfile->fid = ~0;
575         } else {
576                 qunlock(file);
577                 return 0;
578         }
579         clone(nfile, file);
580
581         /*
582          * Should check name is not too long.
583          */
584         error = 0;
585         for(nwname = 0; nwname < f->nwname; nwname++){
586                 error = walkname(nfile, f->wname[nwname], &r->wqid[r->nwqid]);
587                 if(error != 0 || ++r->nwqid >= MAXDAT/sizeof(Qid))
588                         break;
589         }
590
591         if(f->nwname == 0){
592                 /*
593                  * Newfid must be different to fid (see above)
594                  * so this is a simple 'clone' operation - there's
595                  * nothing to do except unlock unless there's
596                  * an error.
597                  */
598                 if(error){
599                         freewp(nfile->wpath);
600                         qunlock(nfile);
601                         freefp(nfile);
602                 } else
603                         qunlock(nfile);
604         } else if(r->nwqid < f->nwname){
605                 /*
606                  * Didn't walk all elements, 'clunk' nfile
607                  * and leave 'file' alone.
608                  * Clear error if some of the elements were
609                  * walked OK.
610                  */
611                 freewp(nfile->wpath);
612                 if(nfile != &tfile){
613                         qunlock(nfile);
614                         freefp(nfile);
615                 }
616                 if(r->nwqid != 0)
617                         error = 0;
618         } else {
619                 /*
620                  * Walked all elements. If newfid is the same
621                  * as fid must update 'file' from the temporary
622                  * copy used during the walk.
623                  * Otherwise just unlock (when using tfile there's
624                  * no need to unlock as it's a local).
625                  */
626                 if(nfile == &tfile){
627                         file->qid = nfile->qid;
628                         freewp(file->wpath);
629                         file->wpath = nfile->wpath;
630                         file->addr = nfile->addr;
631                         file->slot = nfile->slot;
632                 } else
633                         qunlock(nfile);
634         }
635         qunlock(file);
636
637         return error;
638 }
639
640 int
641 fs_open(Chan* chan, Fcall* f, Fcall* r)
642 {
643         Iobuf *p;
644         Dentry *d;
645         File *file;
646         Tlock *t;
647         Qid qid;
648         int error, ro, fmod;
649
650         p = nil;
651         if((file = filep(chan, f->fid, 0)) == nil){
652                 error = Efid;
653                 goto out;
654         }
655         if(file->open != 0){
656                 error = Emode;
657                 goto out;
658         }
659
660         /*
661          * if remove on close, check access here
662          */
663         ro = file->fs->dev->type == Devro;
664         if(f->mode & ORCLOSE){
665                 if(ro){
666                         error = Eronly;
667                         goto out;
668                 }
669                 /*
670                  * check on parent directory of file to be deleted
671                  */
672                 if(file->wpath == 0 || file->wpath->addr == file->addr){
673                         error = Ephase;
674                         goto out;
675                 }
676                 p = getbuf(file->fs->dev, file->wpath->addr, Brd);
677                 if(p == nil || checktag(p, Tdir, QPNONE)){
678                         error = Ephase;
679                         goto out;
680                 }
681                 d = getdir(p, file->wpath->slot);
682                 if(d == nil || !(d->mode & DALLOC)){
683                         error = Ephase;
684                         goto out;
685                 }
686                 if(iaccess(file, d, DWRITE)){
687                         error = Eaccess;
688                         goto out;
689                 }
690                 putbuf(p);
691         }
692         p = getbuf(file->fs->dev, file->addr, Brd);
693         if(p == nil || checktag(p, Tdir, QPNONE)){
694                 error = Ealloc;
695                 goto out;
696         }
697         d = getdir(p, file->slot);
698         if(d == nil || !(d->mode & DALLOC)){
699                 error = Ealloc;
700                 goto out;
701         }
702         if(error = mkqidcmp(&file->qid, d))
703                 goto out;
704         mkqid(&qid, d, 1);
705         switch(f->mode & 7){
706
707         case OREAD:
708                 if(iaccess(file, d, DREAD))
709                         goto badaccess;
710                 fmod = FREAD;
711                 break;
712
713         case OWRITE:
714                 if((d->mode & DDIR) || iaccess(file, d, DWRITE))
715                         goto badaccess;
716                 if(ro){
717                         error = Eronly;
718                         goto out;
719                 }
720                 fmod = FWRITE;
721                 break;
722
723         case ORDWR:
724                 if((d->mode & DDIR)
725                 || iaccess(file, d, DREAD)
726                 || iaccess(file, d, DWRITE))
727                         goto badaccess;
728                 if(ro){
729                         error = Eronly;
730                         goto out;
731                 }
732                 fmod = FREAD+FWRITE;
733                 break;
734
735         case OEXEC:
736                 if((d->mode & DDIR) || iaccess(file, d, DEXEC))
737                         goto badaccess;
738                 fmod = FREAD;
739                 break;
740
741         default:
742                 error = Emode;
743                 goto out;
744         }
745         if(f->mode & OTRUNC){
746                 if((d->mode & DDIR) || iaccess(file, d, DWRITE))
747                         goto badaccess;
748                 if(ro){
749                         error = Eronly;
750                         goto out;
751                 }
752         }
753         t = 0;
754         if(d->mode & DLOCK){
755                 if((t = tlocked(p, d)) == nil){
756                         error = Elocked;
757                         goto out;
758                 }
759         }
760         if(f->mode & ORCLOSE)
761                 fmod |= FREMOV;
762         file->open = fmod;
763         if((f->mode & OTRUNC) && !(d->mode & DAPND)){
764                 dtrunc(p, d, file->uid);
765                 qid.vers = d->qid.version;
766         }
767         r->qid = qid;
768         file->tlock = t;
769         if(t != nil)
770                 t->file = file;
771         file->lastra = 1;
772         goto out;
773
774 badaccess:
775         error = Eaccess;
776         file->open = 0;
777
778 out:
779         if(p != nil)
780                 putbuf(p);
781         if(file != nil)
782                 qunlock(file);
783
784         r->iounit = chan->msize-IOHDRSZ;
785
786         return error;
787 }
788
789 int
790 fs_create(Chan* chan, Fcall* f, Fcall* r)
791 {
792         Iobuf *p, *p1;
793         Dentry *d, *d1;
794         File *file;
795         int error, slot, slot1, fmod;
796         Off addr, addr1, path;
797         Tlock *t;
798         Wpath *w;
799
800         p = nil;
801
802         if((file = filep(chan, f->fid, 0)) == nil){
803                 error = Efid;
804                 goto out;
805         }
806         if(file->fs->dev->type == Devro){
807                 error = Eronly;
808                 goto out;
809         }
810         if(file->qid.type & QTAUTH){
811                 error = Emode;
812                 goto out;
813         }
814
815         p = getbuf(file->fs->dev, file->addr, Brd);
816         if(p == nil || checktag(p, Tdir, QPNONE)){
817                 error = Ealloc;
818                 goto out;
819         }
820         d = getdir(p, file->slot);
821         if(d == nil || !(d->mode & DALLOC)){
822                 error = Ealloc;
823                 goto out;
824         }
825         if(error = mkqidcmp(&file->qid, d))
826                 goto out;
827         if(!(d->mode & DDIR)){
828                 error = Edir2;
829                 goto out;
830         }
831         if(iaccess(file, d, DWRITE)) {
832                 error = Eaccess;
833                 goto out;
834         }
835         accessdir(p, d, FREAD, file->uid);
836
837         /*
838          * Check the name is valid (and will fit in an old
839          * directory entry for the moment).
840          */
841         if(error = checkname(f->name))
842                 goto out;
843
844         addr1 = 0;
845         slot1 = 0;      /* set */
846         for(addr = 0; ; addr++){
847                 if((p1 = dnodebuf(p, d, addr, 0, file->uid)) == nil){
848                         if(addr1 != 0)
849                                 break;
850                         p1 = dnodebuf(p, d, addr, Tdir, file->uid);
851                 }
852                 if(p1 == nil){
853                         error = Efull;
854                         goto out;
855                 }
856                 if(checktag(p1, Tdir, d->qid.path ^ QPDIR)){
857                         putbuf(p1);
858                         goto phase;
859                 }
860                 for(slot = 0; slot < DIRPERBUF; slot++){
861                         d1 = getdir(p1, slot);
862                         if(!(d1->mode & DALLOC)){
863                                 if(addr1 == 0){
864                                         addr1 = p1->addr;
865                                         slot1 = slot + addr*DIRPERBUF;
866                                 }
867                                 continue;
868                         }
869                         if(strncmp(f->name, d1->name, sizeof(d1->name)) == 0){
870                                 putbuf(p1);
871                                 error = Eexist;
872                                 goto out;
873                         }
874                 }
875                 putbuf(p1);
876         }
877
878         switch(f->mode & 7){
879         case OEXEC:
880         case OREAD:             /* seems only useful to make directories */
881                 fmod = FREAD;
882                 break;
883
884         case OWRITE:
885                 fmod = FWRITE;
886                 break;
887
888         case ORDWR:
889                 fmod = FREAD+FWRITE;
890                 break;
891
892         default:
893                 error = Emode;
894                 goto out;
895         }
896         if(f->perm & DMDIR)
897                 if((f->mode & OTRUNC) || (f->perm & DMAPPEND) || (fmod & FWRITE))
898                         goto badaccess;
899         /*
900          * do it
901          */
902         path = qidpathgen(file->fs->dev);
903         if((p1 = getbuf(file->fs->dev, addr1, Brd|Bimm|Bmod)) == nil)
904                 goto phase;
905         d1 = getdir(p1, slot1);
906         if(d1 == nil || checktag(p1, Tdir, d->qid.path ^ QPDIR)) {
907                 putbuf(p1);
908                 goto phase;
909         }
910         if(d1->mode & DALLOC){
911                 putbuf(p1);
912                 goto phase;
913         }
914
915         strncpy(d1->name, f->name, NAMELEN);
916         if(chan == cons.chan){
917                 d1->uid = cons.uid;
918                 d1->gid = cons.gid;
919         } else {
920                 d1->uid = file->uid;
921                 d1->gid = d->gid;
922                 f->perm &= d->mode | ~0666;
923                 if(f->perm & DMDIR)
924                         f->perm &= d->mode | ~0777;
925         }
926         d1->qid.path = path;
927         d1->qid.version = 0;
928         d1->mode = DALLOC | (f->perm & 0777);
929         if(f->perm & DMDIR) {
930                 d1->mode |= DDIR;
931                 d1->qid.path ^= QPDIR;
932         }
933         if(f->perm & DMAPPEND)
934                 d1->mode |= DAPND;
935         if(f->perm & DMTMP)
936                 d1->mode |= DTMP;
937         t = nil;
938         if(f->perm & DMEXCL){
939                 d1->mode |= DLOCK;
940                 t = tlocked(p1, d1);
941                 /* if nil, out of tlock structures */
942         }
943         accessdir(p1, d1, FWRITE, file->uid);
944         mkqid(&r->qid, d1, 0);
945         putbuf(p1);
946         accessdir(p, d, FWRITE, file->uid);
947
948         /*
949          * do a walk to new directory entry
950          */
951         if((w = newwp()) == nil){
952                 error = Ewalk;
953                 goto out;
954         }
955         w->addr = file->addr;
956         w->slot = file->slot;
957         w->up = file->wpath;
958         file->wpath = w;
959         file->qid = r->qid;
960         file->tlock = t;
961         if(t != nil)
962                 t->file = file;
963         file->lastra = 1;
964         if(f->mode & ORCLOSE)
965                 fmod |= FREMOV;
966         file->open = fmod;
967         file->addr = addr1;
968         file->slot = slot1;
969         goto out;
970
971 badaccess:
972         error = Eaccess;
973         goto out;
974
975 phase:
976         error = Ephase;
977
978 out:
979         if(p != nil)
980                 putbuf(p);
981         if(file != nil)
982                 qunlock(file);
983
984         r->iounit = chan->msize-IOHDRSZ;
985
986         return error;
987 }
988
989 int
990 fs_read(Chan* chan, Fcall* f, Fcall* r, uchar* data)
991 {
992         Iobuf *p, *p1;
993         File *file;
994         Dentry *d, *d1;
995         Tlock *t;
996         Off addr, offset, start;
997         Timet tim;
998         int error, iounit, nread, count, mask, n, o, slot;
999         Msgbuf *dmb;
1000         Dir dir;
1001
1002         p = nil;
1003
1004         error = 0;
1005         count = f->count;
1006         offset = f->offset;
1007         nread = 0;
1008         if((file = filep(chan, f->fid, 0)) == nil){
1009                 error = Efid;
1010                 goto out;
1011         }
1012         if(!(file->open & FREAD)){
1013                 error = Eopen;
1014                 goto out;
1015         }
1016         iounit = chan->msize-IOHDRSZ;
1017         if(count < 0 || count > iounit){
1018                 error = Ecount;
1019                 goto out;
1020         }
1021         if(offset < 0){
1022                 error = Eoffset;
1023                 goto out;
1024         }
1025         if(file->qid.type & QTAUTH){
1026                 nread = authread(file, (uchar*)data, count);
1027                 if(nread < 0)
1028                         error = Eauth2;
1029                 goto out;
1030         }
1031         p = getbuf(file->fs->dev, file->addr, Brd);
1032         if(p == nil || checktag(p, Tdir, QPNONE)){
1033                 error = Ealloc;
1034                 goto out;
1035         }
1036         d = getdir(p, file->slot);
1037         if(d == nil || !(d->mode & DALLOC)){
1038                 error = Ealloc;
1039                 goto out;
1040         }
1041         if(error = mkqidcmp(&file->qid, d))
1042                 goto out;
1043         if(t = file->tlock){
1044                 tim = toytime();
1045                 if(t->time < tim || t->file != file){
1046                         error = Ebroken;
1047                         goto out;
1048                 }
1049                 /* renew the lock */
1050                 t->time = tim + TLOCK;
1051         }
1052         accessdir(p, d, FREAD, file->uid);
1053         if(d->mode & DDIR)
1054                 goto dread;
1055         if(offset >= d->size)
1056                 count = 0;
1057         else if(offset+count > d->size)
1058                 count = d->size - offset;
1059         while(count > 0){
1060                 if(p == nil){
1061                         p = getbuf(file->fs->dev, file->addr, Brd);
1062                         if(p == nil || checktag(p, Tdir, QPNONE)){
1063                                 error = Ealloc;
1064                                 goto out;
1065                         }
1066                         d = getdir(p, file->slot);
1067                         if(d == nil || !(d->mode & DALLOC)){
1068                                 error = Ealloc;
1069                                 goto out;
1070                         }
1071                 }
1072                 addr = offset / BUFSIZE;
1073                 file->lastra = dbufread(p, d, addr, file->lastra, file->uid);
1074                 o = offset % BUFSIZE;
1075                 n = BUFSIZE - o;
1076                 if(n > count)
1077                         n = count;
1078                 p1 = dnodebuf1(p, d, addr, 0, file->uid);
1079                 p = nil;
1080                 if(p1 != nil){
1081                         if(checktag(p1, Tfile, QPNONE)){
1082                                 error = Ephase;
1083                                 putbuf(p1);
1084                                 goto out;
1085                         }
1086                         memmove(data+nread, p1->iobuf+o, n);
1087                         putbuf(p1);
1088                 } else
1089                         memset(data+nread, 0, n);
1090                 count -= n;
1091                 nread += n;
1092                 offset += n;
1093         }
1094         goto out;
1095
1096 dread:
1097         /*
1098          * Pick up where we left off last time if nothing has changed,
1099          * otherwise must scan from the beginning.
1100          */
1101         if(offset == file->doffset /*&& file->qid.vers == file->dvers*/){
1102                 addr = file->dslot/DIRPERBUF;
1103                 slot = file->dslot%DIRPERBUF;
1104                 start = offset;
1105         } else {
1106                 addr = 0;
1107                 slot = 0;
1108                 start = 0;
1109         }
1110
1111         dmb = mballoc(iounit, chan, Mbreply1);
1112         for (;;) {
1113                 if(p == nil){
1114                         /*
1115                          * This is just a check to ensure the entry hasn't
1116                          * gone away during the read of each directory block.
1117                          */
1118                         p = getbuf(file->fs->dev, file->addr, Brd);
1119                         if(p == nil || checktag(p, Tdir, QPNONE)){
1120                                 error = Ealloc;
1121                                 goto out1;
1122                         }
1123                         d = getdir(p, file->slot);
1124                         if(d == nil || !(d->mode & DALLOC)){
1125                                 error = Ealloc;
1126                                 goto out1;
1127                         }
1128                 }
1129                 p1 = dnodebuf1(p, d, addr, 0, file->uid);
1130                 p = nil;
1131                 if(p1 == nil)
1132                         goto out1;
1133                 if(checktag(p1, Tdir, QPNONE)){
1134                         error = Ephase;
1135                         putbuf(p1);
1136                         goto out1;
1137                 }
1138
1139                 mask = DALLOC;
1140                 if(file->fs->dev->type == Devro)
1141                         mask |= DTMP;
1142                 for(; slot < DIRPERBUF; slot++){
1143                         d1 = getdir(p1, slot);
1144                         if((d1->mode & mask) != DALLOC)
1145                                 continue;
1146                         mkdir9p2(&dir, d1, dmb->data);
1147                         n = convD2M(&dir, data+nread, iounit - nread);
1148                         if(n <= BIT16SZ){
1149                                 putbuf(p1);
1150                                 goto out1;
1151                         }
1152                         start += n;
1153                         if(start < offset)
1154                                 continue;
1155                         if(count < n){
1156                                 putbuf(p1);
1157                                 goto out1;
1158                         }
1159                         count -= n;
1160                         nread += n;
1161                         offset += n;
1162                 }
1163                 putbuf(p1);
1164                 slot = 0;
1165                 addr++;
1166         }
1167 out1:
1168         mbfree(dmb);
1169         if(error == 0){
1170                 file->doffset = offset;
1171                 file->dvers = file->qid.vers;
1172                 file->dslot = slot+DIRPERBUF*addr;
1173         }
1174
1175 out:
1176         /*
1177          * Do we need this any more?
1178         count = f->count - nread;
1179         if(count > 0)
1180                 memset(data+nread, 0, count);
1181          */
1182         if(p != nil)
1183                 putbuf(p);
1184         if(file != nil)
1185                 qunlock(file);
1186         r->count = nread;
1187         r->data = (char*)data;
1188
1189         return error;
1190 }
1191
1192 int
1193 fs_write(Chan* chan, Fcall* f, Fcall* r)
1194 {
1195         Iobuf *p, *p1;
1196         Dentry *d;
1197         File *file;
1198         Tlock *t;
1199         Off offset, addr, qpath;
1200         Timet tim;
1201         int count, error, nwrite, o, n;
1202
1203         error = 0;
1204         offset = f->offset;
1205         count = f->count;
1206
1207         nwrite = 0;
1208         p = nil;
1209
1210         if((file = filep(chan, f->fid, 0)) == nil){
1211                 error = Efid;
1212                 goto out;
1213         }
1214         if(!(file->open & FWRITE)){
1215                 error = Eopen;
1216                 goto out;
1217         }
1218         if(count < 0 || count > chan->msize-IOHDRSZ){
1219                 error = Ecount;
1220                 goto out;
1221         }
1222         if(offset < 0) {
1223                 error = Eoffset;
1224                 goto out;
1225         }
1226
1227         if(file->qid.type & QTAUTH){
1228                 nwrite = authwrite(file, (uchar*)f->data, count);
1229                 if(nwrite < 0)
1230                         error = Eauth2;
1231                 goto out;
1232         } else if(file->fs->dev->type == Devro){
1233                 error = Eronly;
1234                 goto out;
1235         }
1236
1237         if ((p = getbuf(file->fs->dev, file->addr, Brd|Bmod)) == nil ||
1238             (d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)) {
1239                 error = Ealloc;
1240                 goto out;
1241         }
1242         if(error = mkqidcmp(&file->qid, d))
1243                 goto out;
1244         if(t = file->tlock) {
1245                 tim = toytime();
1246                 if(t->time < tim || t->file != file){
1247                         error = Ebroken;
1248                         goto out;
1249                 }
1250                 /* renew the lock */
1251                 t->time = tim + TLOCK;
1252         }
1253         accessdir(p, d, FWRITE, file->uid);
1254         if(d->mode & DAPND)
1255                 offset = d->size;
1256         if(offset+count > d->size)
1257                 d->size = offset+count;
1258         while(count > 0){
1259                 if(p == nil){
1260                         p = getbuf(file->fs->dev, file->addr, Brd|Bmod);
1261                         if(p == nil){
1262                                 error = Ealloc;
1263                                 goto out;
1264                         }
1265                         d = getdir(p, file->slot);
1266                         if(d == nil || !(d->mode & DALLOC)){
1267                                 error = Ealloc;
1268                                 goto out;
1269                         }
1270                 }
1271                 addr = offset / BUFSIZE;
1272                 o = offset % BUFSIZE;
1273                 n = BUFSIZE - o;
1274                 if(n > count)
1275                         n = count;
1276                 qpath = d->qid.path;
1277                 p1 = dnodebuf1(p, d, addr, Tfile, file->uid);
1278                 p = nil;
1279                 if(p1 == nil) {
1280                         error = Efull;
1281                         goto out;
1282                 }
1283                 if(checktag(p1, Tfile, qpath)){
1284                         putbuf(p1);
1285                         error = Ephase;
1286                         goto out;
1287                 }
1288                 memmove(p1->iobuf+o, f->data+nwrite, n);
1289                 p1->flags |= Bmod;
1290                 putbuf(p1);
1291                 count -= n;
1292                 nwrite += n;
1293                 offset += n;
1294         }
1295
1296 out:
1297         if(p != nil)
1298                 putbuf(p);
1299         if(file != nil)
1300                 qunlock(file);
1301         r->count = nwrite;
1302
1303         return error;
1304 }
1305
1306 int
1307 doremove(File *f)
1308 {
1309         Iobuf *p, *p1;
1310         Dentry *d, *d1;
1311         Off addr;
1312         int slot, err;
1313
1314         p = 0;
1315         p1 = 0;
1316         if(f->fs->dev->type == Devro) {
1317                 err = Eronly;
1318                 goto out;
1319         }
1320         /*
1321          * check on parent directory of file to be deleted
1322          */
1323         if(f->wpath == 0 || f->wpath->addr == f->addr) {
1324                 err = Ephase;
1325                 goto out;
1326         }
1327         p1 = getbuf(f->fs->dev, f->wpath->addr, Brd);
1328         d1 = getdir(p1, f->wpath->slot);
1329         if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
1330                 err = Ephase;
1331                 goto out;
1332         }
1333         if(iaccess(f, d1, DWRITE)) {
1334                 err = Eaccess;
1335                 goto out;
1336         }
1337         accessdir(p1, d1, FWRITE, f->uid);
1338         putbuf(p1);
1339         p1 = 0;
1340
1341         /*
1342          * check on file to be deleted
1343          */
1344         p = getbuf(f->fs->dev, f->addr, Brd);
1345         d = getdir(p, f->slot);
1346         if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1347                 err = Ealloc;
1348                 goto out;
1349         }
1350         if(err = mkqidcmp(&f->qid, d))
1351                 goto out;
1352
1353         /*
1354          * if deleting a directory, make sure it is empty
1355          */
1356         if((d->mode & DDIR))
1357         for(addr=0;; addr++) {
1358                 p1 = dnodebuf(p, d, addr, 0, f->uid);
1359                 if(!p1)
1360                         break;
1361                 if(checktag(p1, Tdir, d->qid.path ^ QPDIR)) {
1362                         err = Ephase;
1363                         goto out;
1364                 }
1365                 for(slot=0; slot<DIRPERBUF; slot++) {
1366                         d1 = getdir(p1, slot);
1367                         if(!(d1->mode & DALLOC))
1368                                 continue;
1369                         err = Eempty;
1370                         goto out;
1371                 }
1372                 putbuf(p1);
1373         }
1374
1375         /*
1376          * do it
1377          */
1378         dtrunc(p, d, f->uid);
1379         memset(d, 0, sizeof(Dentry));
1380         settag(p, Tdir, QPNONE);
1381
1382 out:
1383         if(p1)
1384                 putbuf(p1);
1385         if(p)
1386                 putbuf(p);
1387         return err;
1388 }
1389
1390 static int
1391 _clunk(File* file, int remove)
1392 {
1393         Tlock *t;
1394         int error;
1395
1396         error = 0;
1397         if(t = file->tlock){
1398                 if(t->file == file)
1399                         t->time = 0;            /* free the lock */
1400                 file->tlock = 0;
1401         }
1402         if(remove && (file->qid.type & QTAUTH) == 0)
1403                 error = doremove(file);
1404         file->open = 0;
1405         freewp(file->wpath);
1406         authfree(file->auth);
1407         file->auth = 0;
1408         freefp(file);
1409         qunlock(file);
1410
1411         return error;
1412 }
1413
1414 static int
1415 clunk(Chan* chan, Fcall* f, Fcall*)
1416 {
1417         File *file;
1418
1419         if((file = filep(chan, f->fid, 0)) == nil)
1420                 return Efid;
1421
1422         _clunk(file, file->open & FREMOV);
1423         return 0;
1424 }
1425
1426 int
1427 fs_remove(Chan* chan, Fcall* f, Fcall*)
1428 {
1429         File *file;
1430
1431         if((file = filep(chan, f->fid, 0)) == nil)
1432                 return Efid;
1433         return _clunk(file, 1);
1434 }
1435
1436 int
1437 fs_stat(Chan* chan, Fcall* f, Fcall* r, uchar* data)
1438 {
1439         Dir dir;
1440         Iobuf *p;
1441         Dentry *d, dentry;
1442         File *file;
1443         int error, len;
1444
1445         error = 0;
1446         p = nil;
1447         if((file = filep(chan, f->fid, 0)) == nil)
1448                 return Efid;
1449         if(file->qid.type & QTAUTH){
1450                 memset(&dentry, 0, sizeof dentry);
1451                 d = &dentry;
1452                 mkqid9p1(&d->qid, &file->qid);
1453                 strcpy(d->name, "#¿");
1454                 d->uid = file->uid;
1455                 d->gid = d->uid;
1456                 d->muid = d->uid;
1457                 d->atime = time(nil);
1458                 d->mtime = d->atime;
1459                 d->size = 0;
1460         } else {
1461                 p = getbuf(file->fs->dev, file->addr, Brd);
1462                 if(p == nil || checktag(p, Tdir, QPNONE)){
1463                         error = Edir1;
1464                         goto out;
1465                 }
1466                 d = getdir(p, file->slot);
1467                 if(d == nil || !(d->mode & DALLOC)){
1468                         error = Ealloc;
1469                         goto out;
1470                 }
1471                 if(error = mkqidcmp(&file->qid, d))
1472                         goto out;
1473
1474                 if(d->qid.path == (QPROOT|QPDIR))       /* stat of root gives time */
1475                         d->atime = time(nil);
1476         }
1477         len = mkdir9p2(&dir, d, data);
1478         data += len;
1479
1480         if((r->nstat = convD2M(&dir, data, chan->msize - len)) == 0)
1481                 error = Eedge;
1482         r->stat = data;
1483
1484 out:
1485         if(p != nil)
1486                 putbuf(p);
1487         if(file != nil)
1488                 qunlock(file);
1489
1490         return error;
1491 }
1492
1493 static int
1494 fs_wstat(Chan* chan, Fcall* f, Fcall*, char* strs)
1495 {
1496         Iobuf *p, *p1;
1497         Dentry *d, *d1;
1498         File *file;
1499         int error, err, gid, gl, muid, op, slot, tsync, uid;
1500         long addr;
1501         Dir dir;
1502
1503         if(convM2D(f->stat, f->nstat, &dir, strs) == 0)
1504                 return Econvert;
1505
1506         /*
1507          * Get the file.
1508          * if filesystem is read-only, can't change anything.
1509          */
1510         if((file = filep(chan, f->fid, 0)) == nil)
1511                 return Efid;
1512         p = p1 = nil;
1513         if(file->fs->dev->type == Devro){
1514                 error = Eronly;
1515                 goto out;
1516         }
1517         if(file->qid.type & QTAUTH){
1518                 error = Emode;
1519                 goto out;
1520         }
1521
1522         /*
1523          * Get the current entry and check it is still valid.
1524          */
1525         p = getbuf(file->fs->dev, file->addr, Brd);
1526         if(p == nil || checktag(p, Tdir, QPNONE)){
1527                 error = Ealloc;
1528                 goto out;
1529         }
1530         d = getdir(p, file->slot);
1531         if(d == nil || !(d->mode & DALLOC)){
1532                 error = Ealloc;
1533                 goto out;
1534         }
1535         if(error = mkqidcmp(&file->qid, d))
1536                 goto out;
1537
1538         /*
1539          * Run through each of the (sub-)fields in the provided Dir
1540          * checking for validity and whether it's a default:
1541          * .type, .dev and .atime are completely ignored and not checked;
1542          * .qid.path, .qid.vers and .muid are checked for validity but
1543          * any attempt to change them is an error.
1544          * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can
1545          * possibly be changed (and .muid iff is god).
1546          *
1547          * 'Op' flags there are changed fields, i.e. it's not a no-op.
1548          * 'Tsync' flags all fields are defaulted.
1549          */
1550         tsync = 1;
1551         if(dir.qid.path != ~0){
1552                 if(dir.qid.path != file->qid.path){
1553                         error = Ewstatp;
1554                         goto out;
1555                 }
1556                 tsync = 0;
1557         }
1558         if(dir.qid.vers != ~0){
1559                 if(dir.qid.vers != file->qid.vers){
1560                         error = Ewstatv;
1561                         goto out;
1562                 }
1563                 tsync = 0;
1564         }
1565
1566         /*
1567          * .qid.type and .mode have some bits in common. Only .mode
1568          * is currently needed for comparisons with the old mode but
1569          * if there are changes to the bits also encoded in .qid.type
1570          * then file->qid must be updated appropriately later.
1571          */
1572         if(dir.qid.type == (uchar)~0){
1573                 if(dir.mode == ~0)
1574                         dir.qid.type = mktype9p2(d->mode);
1575                 else
1576                         dir.qid.type = dir.mode>>24;
1577         } else
1578                 tsync = 0;
1579         if(dir.mode == ~0)
1580                 dir.mode = mkmode9p2(d->mode);
1581         else
1582                 tsync = 0;
1583
1584         /*
1585          * Check dir.qid.type and dir.mode agree, check for any unknown
1586          * type/mode bits, check for an attempt to change the directory bit.
1587          */
1588         if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
1589                 error = Ewstatq;
1590                 goto out;
1591         }
1592         if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){
1593                 error = Ewstatb;
1594                 goto out;
1595         }
1596
1597         op = dir.mode^mkmode9p2(d->mode);
1598         if(op & DMDIR){
1599                 error = Ewstatd;
1600                 goto out;
1601         }
1602
1603         if(dir.mtime != ~0){
1604                 if(dir.mtime != d->mtime)
1605                         op = 1;
1606                 tsync = 0;
1607         } else
1608                 dir.mtime = d->mtime;
1609
1610         if(dir.length == ~(Off)0)
1611                 dir.length = d->size;
1612         else {
1613                 if (dir.length < 0) {
1614                         error = Ewstatl;
1615                         goto out;
1616                 } else if(dir.length != d->size)
1617                         op = 1;
1618                 tsync = 0;
1619         }
1620
1621         /*
1622          * Check for permission to change .mode, .mtime or .length,
1623          * must be owner or leader of either group, for which test gid
1624          * is needed; permission checks on gid will be done later.
1625          * 'Gl' counts whether neither, one or both groups are led.
1626          */
1627         if(dir.gid != nil && *dir.gid != '\0'){
1628                 gid = strtouid(dir.gid);
1629                 tsync = 0;
1630         } else
1631                 gid = d->gid;
1632         gl = leadgroup(file->uid, gid) != 0;
1633         gl += leadgroup(file->uid, d->gid) != 0;
1634
1635         if(op && !isallowed(file) && d->uid != file->uid && !gl){
1636                 error = Ewstato;
1637                 goto out;
1638         }
1639
1640         /*
1641          * Rename.
1642          * Check .name is valid and different to the current.
1643          */
1644         if(dir.name != nil && *dir.name != '\0'){
1645                 if(error = checkname(dir.name))
1646                         goto out;
1647                 if(strncmp(dir.name, d->name, NAMELEN))
1648                         op = 1;
1649                 else
1650                         dir.name = d->name;
1651                 tsync = 0;
1652         } else
1653                 dir.name = d->name;
1654
1655         /*
1656          * If the name is really to be changed check it's unique
1657          * and there is write permission in the parent.
1658          */
1659         if(dir.name != d->name){
1660                 /*
1661                  * First get parent.
1662                  * Must drop current entry to prevent
1663                  * deadlock when searching that new name
1664                  * already exists below.
1665                  */
1666                 putbuf(p);
1667                 p = nil;
1668
1669                 if(file->wpath == nil){
1670                         error = Ephase;
1671                         goto out;
1672                 }
1673                 p1 = getbuf(file->fs->dev, file->wpath->addr, Brd);
1674                 if(p1 == nil || checktag(p1, Tdir, QPNONE)){
1675                         error = Ephase;
1676                         goto out;
1677                 }
1678                 d1 = getdir(p1, file->wpath->slot);
1679                 if(d1 == nil || !(d1->mode & DALLOC)){
1680                         error = Ephase;
1681                         goto out;
1682                 }
1683
1684                 /*
1685                  * Check entries in parent for new name.
1686                  */
1687                 for(addr = 0; ; addr++){
1688                         if((p = dnodebuf(p1, d1, addr, 0, file->uid)) == nil)
1689                                 break;
1690                         if(checktag(p, Tdir, d1->qid.path ^ QPDIR)){
1691                                 putbuf(p);
1692                                 continue;
1693                         }
1694                         for(slot = 0; slot < DIRPERBUF; slot++){
1695                                 d = getdir(p, slot);
1696                                 if(!(d->mode & DALLOC) ||
1697                                    strncmp(dir.name, d->name, sizeof d->name))
1698                                         continue;
1699                                 error = Eexist;
1700                                 goto out;
1701                         }
1702                         putbuf(p);
1703                 }
1704
1705                 /*
1706                  * Reacquire entry and check it's still OK.
1707                  */
1708                 p = getbuf(file->fs->dev, file->addr, Brd);
1709                 if(p == nil || checktag(p, Tdir, QPNONE)){
1710                         error = Ephase;
1711                         goto out;
1712                 }
1713                 d = getdir(p, file->slot);
1714                 if(d == nil || !(d->mode & DALLOC)){
1715                         error = Ephase;
1716                         goto out;
1717                 }
1718
1719                 /*
1720                  * Check write permission in the parent.
1721                  */
1722                 if(iaccess(file, d1, DWRITE)){
1723                         error = Eaccess;
1724                         goto out;
1725                 }
1726         }
1727
1728         /*
1729          * Check for permission to change owner - must be god.
1730          */
1731         if(dir.uid != nil && *dir.uid != '\0'){
1732                 uid = strtouid(dir.uid);
1733                 if(uid != d->uid){
1734                         if(!isallowed(file)){
1735                                 error = Ewstatu;
1736                                 goto out;
1737                         }
1738                         op = 1;
1739                 }
1740                 tsync = 0;
1741         } else
1742                 uid = d->uid;
1743         if(dir.muid != nil && *dir.muid != '\0'){
1744                 muid = strtouid(dir.muid);
1745                 if(muid != d->muid){
1746                         if(!isallowed(file)){
1747                                 error = Ewstatm;
1748                                 goto out;
1749                         }
1750                         op = 1;
1751                 }
1752                 tsync = 0;
1753         } else
1754                 muid = d->muid;
1755
1756         /*
1757          * Check for permission to change group, must be
1758          * either owner and in new group or leader of both groups.
1759          */
1760         if(gid != d->gid){
1761                 if(!isallowed(file)
1762                 && !(d->uid == file->uid && ingroup(file->uid, gid))
1763                 && !(gl == 2)){
1764                         error = Ewstatg;
1765                         goto out;
1766                 }
1767                 op = 1;
1768         }
1769
1770         /*
1771          * Checks all done, update if necessary.
1772          */
1773         if(op){
1774                 d->mode = mkmode9p1(dir.mode);
1775                 file->qid.type = mktype9p2(d->mode);
1776                 d->mtime = dir.mtime;
1777                 if (dir.length < d->size) {
1778                         err = dtrunclen(p, d, dir.length, uid);
1779                         if (error == 0)
1780                                 error = err;
1781                 }
1782                 d->size = dir.length;
1783                 if(dir.name != d->name)
1784                         strncpy(d->name, dir.name, NAMELEN);
1785                 d->uid = uid;
1786                 d->gid = gid;
1787                 d->muid = muid;
1788                 p->flags |= Bmod;
1789         }
1790         if(!tsync)
1791                 accessdir(p, d, FREAD, file->uid);
1792
1793 out:
1794         if(p != nil)
1795                 putbuf(p);
1796         if(p1 != nil)
1797                 putbuf(p1);
1798         qunlock(file);
1799
1800         return error;
1801 }
1802
1803 int
1804 serve9p2(Msgbuf* mb)
1805 {
1806         Chan *chan;
1807         Fcall f, r;
1808         Msgbuf *data, *rmb;
1809         char ename[64];
1810         int error, n, type;
1811         static int once;
1812
1813         if(once == 0){
1814                 fmtinstall('F', fcallfmt);
1815                 once = 1;
1816         }
1817
1818         /*
1819          * 0 return means i don't understand this message,
1820          * 1 return means i dealt with it, including error
1821          * replies.
1822          */
1823         if(convM2S(mb->data, mb->count, &f) != mb->count){
1824                 fprint(2, "didn't like %d byte message\n", mb->count);
1825                 return 0;
1826         }
1827         type = f.type;
1828         if(type < Tversion || type >= Tmax || (type & 1) || type == Terror)
1829                 return 0;
1830
1831         chan = mb->chan;
1832         if(CHAT(chan))
1833                 fprint(2, "9p2: f %F\n", &f);
1834         r.type = type+1;
1835         r.tag = f.tag;
1836         error = 0;
1837         data = nil;
1838         chan->err[0] = 0;
1839
1840         switch(type){
1841         default:
1842                 r.type = Rerror;
1843                 snprint(ename, sizeof(ename), "unknown message: %F", &f);
1844                 r.ename = ename;
1845                 break;
1846         case Tversion:
1847                 error = version(chan, &f, &r);
1848                 break;
1849         case Tauth:
1850                 error = auth(chan, &f, &r);
1851                 break;
1852         case Tattach:
1853                 error = attach(chan, &f, &r);
1854                 break;
1855         case Tflush:
1856                 error = flush(chan, &f, &r);
1857                 break;
1858         case Twalk:
1859                 error = walk(chan, &f, &r);
1860                 break;
1861         case Topen:
1862                 error = fs_open(chan, &f, &r);
1863                 break;
1864         case Tcreate:
1865                 error = fs_create(chan, &f, &r);
1866                 break;
1867         case Tread:
1868                 data = mballoc(chan->msize, chan, Mbreply1);
1869                 error = fs_read(chan, &f, &r, data->data);
1870                 break;
1871         case Twrite:
1872                 error = fs_write(chan, &f, &r);
1873                 break;
1874         case Tclunk:
1875                 error = clunk(chan, &f, &r);
1876                 break;
1877         case Tremove:
1878                 error = fs_remove(chan, &f, &r);
1879                 break;
1880         case Tstat:
1881                 data = mballoc(chan->msize, chan, Mbreply1);
1882                 error = fs_stat(chan, &f, &r, data->data);
1883                 break;
1884         case Twstat:
1885                 data = mballoc(chan->msize, chan, Mbreply1);
1886                 error = fs_wstat(chan, &f, &r, (char*)data->data);
1887                 break;
1888         }
1889
1890         if(error != 0){
1891                 r.type = Rerror;
1892                 if(chan->err[0])
1893                         r.ename = chan->err;
1894                 else if(error >= MAXERR){
1895                         snprint(ename, sizeof(ename), "error %d", error);
1896                         r.ename = ename;
1897                 } else
1898                         r.ename = errstr9p[error];
1899         }
1900         if(CHAT(chan))
1901                 fprint(2, "9p2: r %F\n", &r);
1902
1903         rmb = mballoc(chan->msize, chan, Mbreply2);
1904         n = convS2M(&r, rmb->data, chan->msize);
1905         if(data != nil)
1906                 mbfree(data);
1907         if(n == 0){
1908                 type = r.type;
1909                 r.type = Rerror;
1910
1911                 /*
1912                  * If a Tversion has not been seen on the chan then
1913                  * chan->msize will be 0. In that case craft a special
1914                  * Rerror message. It's fortunate that the mballoc above
1915                  * for rmb will have returned a Msgbuf of MAXMSG size
1916                  * when given a request with count of 0...
1917                  */
1918                 if(chan->msize == 0){
1919                         r.ename = "Tversion not seen";
1920                         n = convS2M(&r, rmb->data, MAXMSG);
1921                 } else {
1922                         snprint(ename, sizeof(ename), "9p2: convS2M: type %d",
1923                                 type);
1924                         r.ename = ename;
1925                         n = convS2M(&r, rmb->data, chan->msize);
1926                 }
1927                 fprint(2, "%s\n", r.ename);
1928                 if(n == 0){
1929                         /*
1930                          * What to do here, the failure notification failed?
1931                          */
1932                         mbfree(rmb);
1933                         return 1;
1934                 }
1935         }
1936         rmb->count = n;
1937         rmb->param = mb->param;
1938
1939         /* done 9P processing, write reply to network */
1940         fs_send(chan->reply, rmb);
1941
1942         return 1;
1943 }