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