]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/9p2.c
webfs(4): document -d and -D flags
[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         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, qpath;
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                 qpath = d->qid.path;
486                 p1 = dnodebuf1(p, d, addr, 0, file->uid);
487                 p = nil;
488                 if(p1 == nil || checktag(p1, Tdir, qpath)){
489                         error = Eentry;
490                         goto out;
491                 }
492                 mask = DALLOC;
493                 if(file->fs->dev->type == Devro)
494                         mask |= DTMP;
495                 for(slot = 0; slot < DIRPERBUF; slot++){
496                         d1 = getdir(p1, slot);
497                         if ((d1->mode & mask) != DALLOC ||
498                             strncmp(wname, d1->name, NAMELEN) != 0)
499                                 continue;
500                         /*
501                          * update walk path
502                          */
503                         if((w = newwp()) == nil){
504                                 error = Ewalk;
505                                 goto out;
506                         }
507                         w->addr = file->addr;
508                         w->slot = file->slot;
509                         w->up = file->wpath;
510                         file->wpath = w;
511                         slot += DIRPERBUF*addr;
512                         goto found;
513                 }
514                 putbuf(p1);
515                 p1 = nil;
516         }
517
518 found:
519         file->addr = p1->addr;
520         mkqid(&file->qid, d1, 1);
521         putbuf(p1);
522         p1 = nil;
523         file->slot = slot;
524         if(wqid != nil)
525                 *wqid = file->qid;
526
527 out:
528         if(p1 != nil)
529                 putbuf(p1);
530         if(p != nil)
531                 putbuf(p);
532
533         return error;
534 }
535
536 int
537 walk(Chan* chan, Fcall* f, Fcall* r)
538 {
539         int error, nwname;
540         File *file, *nfile, tfile;
541
542         /*
543          * The file identified by f->fid must be valid in the
544          * current session and must not have been opened for I/O
545          * by an open or create message.
546          */
547         if((file = filep(chan, f->fid, 0)) == nil)
548                 return Efid;
549         if(file->open != 0){
550                 qunlock(file);
551                 return Emode;
552         }
553
554         /*
555          * If newfid is not the same as fid, allocate a new file;
556          * a side effect is checking newfid is not already in use (error);
557          * if there are no names to walk this will be equivalent to a
558          * simple 'clone' operation.
559          * Otherwise, fid and newfid are the same and if there are names
560          * to walk make a copy of 'file' to be used during the walk as
561          * 'file' must only be updated on success.
562          * Finally, it's a no-op if newfid is the same as fid and f->nwname
563          * is 0.
564          */
565         r->nwqid = 0;
566         if(f->newfid != f->fid){
567                 if((nfile = filep(chan, f->newfid, 1)) == nil){
568                         qunlock(file);
569                         return Efidinuse;
570                 }
571         } else if(f->nwname != 0){
572                 nfile = &tfile;
573                 memset(nfile, 0, sizeof(File));
574                 nfile->cp = chan;
575                 nfile->fid = ~0;
576         } else {
577                 qunlock(file);
578                 return 0;
579         }
580         clone(nfile, file);
581
582         /*
583          * Should check name is not too long.
584          */
585         error = 0;
586         for(nwname = 0; nwname < f->nwname; nwname++){
587                 error = walkname(nfile, f->wname[nwname], &r->wqid[r->nwqid]);
588                 if(error != 0 || ++r->nwqid >= MAXDAT/sizeof(Qid))
589                         break;
590         }
591
592         if(f->nwname == 0){
593                 /*
594                  * Newfid must be different to fid (see above)
595                  * so this is a simple 'clone' operation - there's
596                  * nothing to do except unlock unless there's
597                  * an error.
598                  */
599                 if(error){
600                         freewp(nfile->wpath);
601                         qunlock(nfile);
602                         freefp(nfile);
603                 } else
604                         qunlock(nfile);
605         } else if(r->nwqid < f->nwname){
606                 /*
607                  * Didn't walk all elements, 'clunk' nfile
608                  * and leave 'file' alone.
609                  * Clear error if some of the elements were
610                  * walked OK.
611                  */
612                 freewp(nfile->wpath);
613                 if(nfile != &tfile){
614                         qunlock(nfile);
615                         freefp(nfile);
616                 }
617                 if(r->nwqid != 0)
618                         error = 0;
619         } else {
620                 /*
621                  * Walked all elements. If newfid is the same
622                  * as fid must update 'file' from the temporary
623                  * copy used during the walk.
624                  * Otherwise just unlock (when using tfile there's
625                  * no need to unlock as it's a local).
626                  */
627                 if(nfile == &tfile){
628                         file->qid = nfile->qid;
629                         freewp(file->wpath);
630                         file->wpath = nfile->wpath;
631                         file->addr = nfile->addr;
632                         file->slot = nfile->slot;
633                 } else
634                         qunlock(nfile);
635         }
636         qunlock(file);
637
638         return error;
639 }
640
641 int
642 fs_open(Chan* chan, Fcall* f, Fcall* r)
643 {
644         Iobuf *p;
645         Dentry *d;
646         File *file;
647         Tlock *t;
648         Qid qid;
649         int error, ro, fmod;
650
651         p = nil;
652         if((file = filep(chan, f->fid, 0)) == nil){
653                 error = Efid;
654                 goto out;
655         }
656         if(file->open != 0){
657                 error = Emode;
658                 goto out;
659         }
660
661         /*
662          * if remove on close, check access here
663          */
664         ro = file->fs->dev->type == Devro;
665         if(f->mode & ORCLOSE){
666                 if(ro){
667                         error = Eronly;
668                         goto out;
669                 }
670                 /*
671                  * check on parent directory of file to be deleted
672                  */
673                 if(file->wpath == 0 || file->wpath->addr == file->addr){
674                         error = Ephase;
675                         goto out;
676                 }
677                 p = getbuf(file->fs->dev, file->wpath->addr, Brd);
678                 if(p == nil || checktag(p, Tdir, QPNONE)){
679                         error = Ephase;
680                         goto out;
681                 }
682                 d = getdir(p, file->wpath->slot);
683                 if(d == nil || !(d->mode & DALLOC)){
684                         error = Ephase;
685                         goto out;
686                 }
687                 if(iaccess(file, d, DWRITE)){
688                         error = Eaccess;
689                         goto out;
690                 }
691                 putbuf(p);
692         }
693         p = getbuf(file->fs->dev, file->addr, Brd);
694         if(p == nil || checktag(p, Tdir, QPNONE)){
695                 error = Ealloc;
696                 goto out;
697         }
698         d = getdir(p, file->slot);
699         if(d == nil || !(d->mode & DALLOC)){
700                 error = Ealloc;
701                 goto out;
702         }
703         if(error = mkqidcmp(&file->qid, d))
704                 goto out;
705         mkqid(&qid, d, 1);
706         switch(f->mode & 7){
707
708         case OREAD:
709                 if(iaccess(file, d, DREAD))
710                         goto badaccess;
711                 fmod = FREAD;
712                 break;
713
714         case OWRITE:
715                 if((d->mode & DDIR) || iaccess(file, d, DWRITE))
716                         goto badaccess;
717                 if(ro){
718                         error = Eronly;
719                         goto out;
720                 }
721                 fmod = FWRITE;
722                 break;
723
724         case ORDWR:
725                 if((d->mode & DDIR)
726                 || iaccess(file, d, DREAD)
727                 || iaccess(file, d, DWRITE))
728                         goto badaccess;
729                 if(ro){
730                         error = Eronly;
731                         goto out;
732                 }
733                 fmod = FREAD+FWRITE;
734                 break;
735
736         case OEXEC:
737                 if((d->mode & DDIR) || iaccess(file, d, DEXEC))
738                         goto badaccess;
739                 fmod = FREAD;
740                 break;
741
742         default:
743                 error = Emode;
744                 goto out;
745         }
746         if(f->mode & OTRUNC){
747                 if((d->mode & DDIR) || iaccess(file, d, DWRITE))
748                         goto badaccess;
749                 if(ro){
750                         error = Eronly;
751                         goto out;
752                 }
753         }
754         t = 0;
755         if(d->mode & DLOCK){
756                 if((t = tlocked(p, d)) == nil){
757                         error = Elocked;
758                         goto out;
759                 }
760         }
761         if(f->mode & ORCLOSE)
762                 fmod |= FREMOV;
763         file->open = fmod;
764         if((f->mode & OTRUNC) && !(d->mode & DAPND)){
765                 dtrunc(p, d, file->uid);
766                 qid.vers = d->qid.version;
767         }
768         r->qid = qid;
769         file->tlock = t;
770         if(t != nil)
771                 t->file = file;
772         file->lastra = 1;
773         goto out;
774
775 badaccess:
776         error = Eaccess;
777         file->open = 0;
778
779 out:
780         if(p != nil)
781                 putbuf(p);
782         if(file != nil)
783                 qunlock(file);
784
785         r->iounit = chan->msize-IOHDRSZ;
786
787         return error;
788 }
789
790 int
791 fs_create(Chan* chan, Fcall* f, Fcall* r)
792 {
793         Iobuf *p, *p1;
794         Dentry *d, *d1;
795         File *file;
796         int error, slot, slot1, fmod;
797         Off addr, addr1, path;
798         Tlock *t;
799         Wpath *w;
800
801         p = nil;
802
803         if((file = filep(chan, f->fid, 0)) == nil){
804                 error = Efid;
805                 goto out;
806         }
807         if(file->fs->dev->type == Devro){
808                 error = Eronly;
809                 goto out;
810         }
811         if(file->qid.type & QTAUTH){
812                 error = Emode;
813                 goto out;
814         }
815
816         p = getbuf(file->fs->dev, file->addr, Brd);
817         if(p == nil || checktag(p, Tdir, QPNONE)){
818                 error = Ealloc;
819                 goto out;
820         }
821         d = getdir(p, file->slot);
822         if(d == nil || !(d->mode & DALLOC)){
823                 error = Ealloc;
824                 goto out;
825         }
826         if(error = mkqidcmp(&file->qid, d))
827                 goto out;
828         if(!(d->mode & DDIR)){
829                 error = Edir2;
830                 goto out;
831         }
832         if(iaccess(file, d, DWRITE)) {
833                 error = Eaccess;
834                 goto out;
835         }
836         accessdir(p, d, FREAD, file->uid);
837
838         /*
839          * Check the name is valid (and will fit in an old
840          * directory entry for the moment).
841          */
842         if(error = checkname(f->name))
843                 goto out;
844
845         addr1 = 0;
846         slot1 = 0;      /* set */
847         for(addr = 0; ; addr++){
848                 if((p1 = dnodebuf(p, d, addr, 0, file->uid)) == nil){
849                         if(addr1 != 0)
850                                 break;
851                         p1 = dnodebuf(p, d, addr, Tdir, file->uid);
852                 }
853                 if(p1 == nil){
854                         error = Efull;
855                         goto out;
856                 }
857                 if(checktag(p1, Tdir, d->qid.path)){
858                         putbuf(p1);
859                         goto phase;
860                 }
861                 for(slot = 0; slot < DIRPERBUF; slot++){
862                         d1 = getdir(p1, slot);
863                         if(!(d1->mode & DALLOC)){
864                                 if(addr1 == 0){
865                                         addr1 = p1->addr;
866                                         slot1 = slot + addr*DIRPERBUF;
867                                 }
868                                 continue;
869                         }
870                         if(strncmp(f->name, d1->name, sizeof(d1->name)) == 0){
871                                 putbuf(p1);
872                                 error = Eexist;
873                                 goto out;
874                         }
875                 }
876                 putbuf(p1);
877         }
878
879         switch(f->mode & 7){
880         case OEXEC:
881         case OREAD:             /* seems only useful to make directories */
882                 fmod = FREAD;
883                 break;
884
885         case OWRITE:
886                 fmod = FWRITE;
887                 break;
888
889         case ORDWR:
890                 fmod = FREAD+FWRITE;
891                 break;
892
893         default:
894                 error = Emode;
895                 goto out;
896         }
897         if(f->perm & DMDIR)
898                 if((f->mode & OTRUNC) || (f->perm & DMAPPEND) || (fmod & FWRITE))
899                         goto badaccess;
900         /*
901          * do it
902          */
903         path = qidpathgen(file->fs->dev);
904         if((p1 = getbuf(file->fs->dev, addr1, Brd|Bimm|Bmod)) == nil)
905                 goto phase;
906         d1 = getdir(p1, slot1);
907         if(d1 == nil || checktag(p1, Tdir, d->qid.path)) {
908                 putbuf(p1);
909                 goto phase;
910         }
911         if(d1->mode & DALLOC){
912                 putbuf(p1);
913                 goto phase;
914         }
915
916         strncpy(d1->name, f->name, NAMELEN);
917         if(chan == cons.chan){
918                 d1->uid = cons.uid;
919                 d1->gid = cons.gid;
920         } else {
921                 d1->uid = file->uid;
922                 d1->gid = d->gid;
923                 f->perm &= d->mode | ~0666;
924                 if(f->perm & DMDIR)
925                         f->perm &= d->mode | ~0777;
926         }
927         d1->qid.path = path;
928         d1->qid.version = 0;
929         d1->mode = DALLOC | (f->perm & 0777);
930         if(f->perm & DMDIR) {
931                 d1->mode |= DDIR;
932                 d1->qid.path |= QPDIR;
933         }
934         if(f->perm & DMAPPEND)
935                 d1->mode |= DAPND;
936         if(f->perm & DMTMP)
937                 d1->mode |= DTMP;
938         t = nil;
939         if(f->perm & DMEXCL){
940                 d1->mode |= DLOCK;
941                 t = tlocked(p1, d1);
942                 /* if nil, out of tlock structures */
943         }
944         accessdir(p1, d1, FWRITE, file->uid);
945         mkqid(&r->qid, d1, 0);
946         putbuf(p1);
947         accessdir(p, d, FWRITE, file->uid);
948
949         /*
950          * do a walk to new directory entry
951          */
952         if((w = newwp()) == nil){
953                 error = Ewalk;
954                 goto out;
955         }
956         w->addr = file->addr;
957         w->slot = file->slot;
958         w->up = file->wpath;
959         file->wpath = w;
960         file->qid = r->qid;
961         file->tlock = t;
962         if(t != nil)
963                 t->file = file;
964         file->lastra = 1;
965         if(f->mode & ORCLOSE)
966                 fmod |= FREMOV;
967         file->open = fmod;
968         file->addr = addr1;
969         file->slot = slot1;
970         goto out;
971
972 badaccess:
973         error = Eaccess;
974         goto out;
975
976 phase:
977         error = Ephase;
978
979 out:
980         if(p != nil)
981                 putbuf(p);
982         if(file != nil)
983                 qunlock(file);
984
985         r->iounit = chan->msize-IOHDRSZ;
986
987         return error;
988 }
989
990 int
991 fs_read(Chan* chan, Fcall* f, Fcall* r, uchar* data)
992 {
993         Iobuf *p, *p1;
994         File *file;
995         Dentry *d, *d1;
996         Tlock *t;
997         Off addr, offset, start;
998         Timet tim;
999         int error, iounit, nread, count, mask, n, o, slot;
1000         Msgbuf *dmb;
1001         Dir dir;
1002
1003         p = nil;
1004
1005         error = 0;
1006         count = f->count;
1007         offset = f->offset;
1008         nread = 0;
1009         if((file = filep(chan, f->fid, 0)) == nil){
1010                 error = Efid;
1011                 goto out;
1012         }
1013         if(!(file->open & FREAD)){
1014                 error = Eopen;
1015                 goto out;
1016         }
1017         iounit = chan->msize-IOHDRSZ;
1018         if(count < 0 || count > iounit){
1019                 error = Ecount;
1020                 goto out;
1021         }
1022         if(offset < 0){
1023                 error = Eoffset;
1024                 goto out;
1025         }
1026         if(file->qid.type & QTAUTH){
1027                 nread = authread(file, (uchar*)data, count);
1028                 if(nread < 0)
1029                         error = Eauth2;
1030                 goto out;
1031         }
1032         p = getbuf(file->fs->dev, file->addr, Brd);
1033         if(p == nil || checktag(p, Tdir, QPNONE)){
1034                 error = Ealloc;
1035                 goto out;
1036         }
1037         d = getdir(p, file->slot);
1038         if(d == nil || !(d->mode & DALLOC)){
1039                 error = Ealloc;
1040                 goto out;
1041         }
1042         if(error = mkqidcmp(&file->qid, d))
1043                 goto out;
1044         if(t = file->tlock){
1045                 tim = toytime();
1046                 if(t->time < tim || t->file != file){
1047                         error = Ebroken;
1048                         goto out;
1049                 }
1050                 /* renew the lock */
1051                 t->time = tim + TLOCK;
1052         }
1053         accessdir(p, d, FREAD, file->uid);
1054         if(d->mode & DDIR)
1055                 goto dread;
1056         if(offset >= d->size)
1057                 count = 0;
1058         else if(offset+count > d->size)
1059                 count = d->size - offset;
1060         while(count > 0){
1061                 if(p == nil){
1062                         p = getbuf(file->fs->dev, file->addr, Brd);
1063                         if(p == nil || checktag(p, Tdir, QPNONE)){
1064                                 error = Ealloc;
1065                                 goto out;
1066                         }
1067                         d = getdir(p, file->slot);
1068                         if(d == nil || !(d->mode & DALLOC)){
1069                                 error = Ealloc;
1070                                 goto out;
1071                         }
1072                 }
1073                 addr = offset / BUFSIZE;
1074                 file->lastra = dbufread(p, d, addr, file->lastra, file->uid);
1075                 o = offset % BUFSIZE;
1076                 n = BUFSIZE - o;
1077                 if(n > count)
1078                         n = count;
1079                 p1 = dnodebuf1(p, d, addr, 0, file->uid);
1080                 p = nil;
1081                 if(p1 != nil){
1082                         if(checktag(p1, Tfile, QPNONE)){
1083                                 error = Ephase;
1084                                 putbuf(p1);
1085                                 goto out;
1086                         }
1087                         memmove(data+nread, p1->iobuf+o, n);
1088                         putbuf(p1);
1089                 } else
1090                         memset(data+nread, 0, n);
1091                 count -= n;
1092                 nread += n;
1093                 offset += n;
1094         }
1095         goto out;
1096
1097 dread:
1098         /*
1099          * Pick up where we left off last time if nothing has changed,
1100          * otherwise must scan from the beginning.
1101          */
1102         if(offset == file->doffset /*&& file->qid.vers == file->dvers*/){
1103                 addr = file->dslot/DIRPERBUF;
1104                 slot = file->dslot%DIRPERBUF;
1105                 start = offset;
1106         } else {
1107                 addr = 0;
1108                 slot = 0;
1109                 start = 0;
1110         }
1111
1112         dmb = mballoc(iounit, chan, Mbreply1);
1113         for (;;) {
1114                 if(p == nil){
1115                         /*
1116                          * This is just a check to ensure the entry hasn't
1117                          * gone away during the read of each directory block.
1118                          */
1119                         p = getbuf(file->fs->dev, file->addr, Brd);
1120                         if(p == nil || checktag(p, Tdir, QPNONE)){
1121                                 error = Ealloc;
1122                                 goto out1;
1123                         }
1124                         d = getdir(p, file->slot);
1125                         if(d == nil || !(d->mode & DALLOC)){
1126                                 error = Ealloc;
1127                                 goto out1;
1128                         }
1129                 }
1130                 p1 = dnodebuf1(p, d, addr, 0, file->uid);
1131                 p = nil;
1132                 if(p1 == nil)
1133                         goto out1;
1134                 if(checktag(p1, Tdir, QPNONE)){
1135                         error = Ephase;
1136                         putbuf(p1);
1137                         goto out1;
1138                 }
1139
1140                 mask = DALLOC;
1141                 if(file->fs->dev->type == Devro)
1142                         mask |= DTMP;
1143                 for(; slot < DIRPERBUF; slot++){
1144                         d1 = getdir(p1, slot);
1145                         if((d1->mode & mask) != DALLOC)
1146                                 continue;
1147                         mkdir9p2(&dir, d1, dmb->data);
1148                         n = convD2M(&dir, data+nread, iounit - nread);
1149                         if(n <= BIT16SZ){
1150                                 putbuf(p1);
1151                                 goto out1;
1152                         }
1153                         start += n;
1154                         if(start < offset)
1155                                 continue;
1156                         if(count < n){
1157                                 putbuf(p1);
1158                                 goto out1;
1159                         }
1160                         count -= n;
1161                         nread += n;
1162                         offset += n;
1163                 }
1164                 putbuf(p1);
1165                 slot = 0;
1166                 addr++;
1167         }
1168 out1:
1169         mbfree(dmb);
1170         if(error == 0){
1171                 file->doffset = offset;
1172                 file->dvers = file->qid.vers;
1173                 file->dslot = slot+DIRPERBUF*addr;
1174         }
1175
1176 out:
1177         /*
1178          * Do we need this any more?
1179         count = f->count - nread;
1180         if(count > 0)
1181                 memset(data+nread, 0, count);
1182          */
1183         if(p != nil)
1184                 putbuf(p);
1185         if(file != nil)
1186                 qunlock(file);
1187         r->count = nread;
1188         r->data = (char*)data;
1189
1190         return error;
1191 }
1192
1193 int
1194 fs_write(Chan* chan, Fcall* f, Fcall* r)
1195 {
1196         Iobuf *p, *p1;
1197         Dentry *d;
1198         File *file;
1199         Tlock *t;
1200         Off offset, addr, qpath;
1201         Timet tim;
1202         int count, error, nwrite, o, n;
1203
1204         error = 0;
1205         offset = f->offset;
1206         count = f->count;
1207
1208         nwrite = 0;
1209         p = nil;
1210
1211         if((file = filep(chan, f->fid, 0)) == nil){
1212                 error = Efid;
1213                 goto out;
1214         }
1215         if(!(file->open & FWRITE)){
1216                 error = Eopen;
1217                 goto out;
1218         }
1219         if(count < 0 || count > chan->msize-IOHDRSZ){
1220                 error = Ecount;
1221                 goto out;
1222         }
1223         if(offset < 0) {
1224                 error = Eoffset;
1225                 goto out;
1226         }
1227
1228         if(file->qid.type & QTAUTH){
1229                 nwrite = authwrite(file, (uchar*)f->data, count);
1230                 if(nwrite < 0)
1231                         error = Eauth2;
1232                 goto out;
1233         } else if(file->fs->dev->type == Devro){
1234                 error = Eronly;
1235                 goto out;
1236         }
1237
1238         if ((p = getbuf(file->fs->dev, file->addr, Brd|Bmod)) == nil ||
1239             (d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)) {
1240                 error = Ealloc;
1241                 goto out;
1242         }
1243         if(error = mkqidcmp(&file->qid, d))
1244                 goto out;
1245         if(t = file->tlock) {
1246                 tim = toytime();
1247                 if(t->time < tim || t->file != file){
1248                         error = Ebroken;
1249                         goto out;
1250                 }
1251                 /* renew the lock */
1252                 t->time = tim + TLOCK;
1253         }
1254         accessdir(p, d, FWRITE, file->uid);
1255         if(d->mode & DAPND)
1256                 offset = d->size;
1257         if(offset+count > d->size)
1258                 d->size = offset+count;
1259         while(count > 0){
1260                 if(p == nil){
1261                         p = getbuf(file->fs->dev, file->addr, Brd|Bmod);
1262                         if(p == nil){
1263                                 error = Ealloc;
1264                                 goto out;
1265                         }
1266                         d = getdir(p, file->slot);
1267                         if(d == nil || !(d->mode & DALLOC)){
1268                                 error = Ealloc;
1269                                 goto out;
1270                         }
1271                 }
1272                 addr = offset / BUFSIZE;
1273                 o = offset % BUFSIZE;
1274                 n = BUFSIZE - o;
1275                 if(n > count)
1276                         n = count;
1277                 qpath = d->qid.path;
1278                 p1 = dnodebuf1(p, d, addr, Tfile, file->uid);
1279                 p = nil;
1280                 if(p1 == nil) {
1281                         error = Efull;
1282                         goto out;
1283                 }
1284                 if(checktag(p1, Tfile, qpath)){
1285                         putbuf(p1);
1286                         error = Ephase;
1287                         goto out;
1288                 }
1289                 memmove(p1->iobuf+o, f->data+nwrite, n);
1290                 p1->flags |= Bmod;
1291                 putbuf(p1);
1292                 count -= n;
1293                 nwrite += n;
1294                 offset += n;
1295         }
1296
1297 out:
1298         if(p != nil)
1299                 putbuf(p);
1300         if(file != nil)
1301                 qunlock(file);
1302         r->count = nwrite;
1303
1304         return error;
1305 }
1306
1307 int
1308 doremove(File *f)
1309 {
1310         Iobuf *p, *p1;
1311         Dentry *d, *d1;
1312         Off addr;
1313         int slot, err;
1314
1315         p = 0;
1316         p1 = 0;
1317         if(f->fs->dev->type == Devro) {
1318                 err = Eronly;
1319                 goto out;
1320         }
1321         /*
1322          * check on parent directory of file to be deleted
1323          */
1324         if(f->wpath == 0 || f->wpath->addr == f->addr) {
1325                 err = Ephase;
1326                 goto out;
1327         }
1328         p1 = getbuf(f->fs->dev, f->wpath->addr, Brd);
1329         d1 = getdir(p1, f->wpath->slot);
1330         if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
1331                 err = Ephase;
1332                 goto out;
1333         }
1334         if(iaccess(f, d1, DWRITE)) {
1335                 err = Eaccess;
1336                 goto out;
1337         }
1338         accessdir(p1, d1, FWRITE, f->uid);
1339         putbuf(p1);
1340         p1 = 0;
1341
1342         /*
1343          * check on file to be deleted
1344          */
1345         p = getbuf(f->fs->dev, f->addr, Brd);
1346         d = getdir(p, f->slot);
1347         if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1348                 err = Ealloc;
1349                 goto out;
1350         }
1351         if(err = mkqidcmp(&f->qid, d))
1352                 goto out;
1353
1354         /*
1355          * if deleting a directory, make sure it is empty
1356          */
1357         if((d->mode & DDIR))
1358         for(addr=0;; addr++) {
1359                 p1 = dnodebuf(p, d, addr, 0, f->uid);
1360                 if(!p1)
1361                         break;
1362                 if(checktag(p1, Tdir, d->qid.path)) {
1363                         err = Ephase;
1364                         goto out;
1365                 }
1366                 for(slot=0; slot<DIRPERBUF; slot++) {
1367                         d1 = getdir(p1, slot);
1368                         if(!(d1->mode & DALLOC))
1369                                 continue;
1370                         err = Eempty;
1371                         goto out;
1372                 }
1373                 putbuf(p1);
1374         }
1375
1376         /*
1377          * do it
1378          */
1379         dtrunc(p, d, f->uid);
1380         memset(d, 0, sizeof(Dentry));
1381         settag(p, Tdir, QPNONE);
1382
1383 out:
1384         if(p1)
1385                 putbuf(p1);
1386         if(p)
1387                 putbuf(p);
1388         return err;
1389 }
1390
1391 static int
1392 _clunk(File* file, int remove)
1393 {
1394         Tlock *t;
1395         int error;
1396
1397         error = 0;
1398         if(t = file->tlock){
1399                 if(t->file == file)
1400                         t->time = 0;            /* free the lock */
1401                 file->tlock = 0;
1402         }
1403         if(remove && (file->qid.type & QTAUTH) == 0)
1404                 error = doremove(file);
1405         file->open = 0;
1406         freewp(file->wpath);
1407         authfree(file->auth);
1408         file->auth = 0;
1409         freefp(file);
1410         qunlock(file);
1411
1412         return error;
1413 }
1414
1415 static int
1416 clunk(Chan* chan, Fcall* f, Fcall*)
1417 {
1418         File *file;
1419
1420         if((file = filep(chan, f->fid, 0)) == nil)
1421                 return Efid;
1422
1423         _clunk(file, file->open & FREMOV);
1424         return 0;
1425 }
1426
1427 int
1428 fs_remove(Chan* chan, Fcall* f, Fcall*)
1429 {
1430         File *file;
1431
1432         if((file = filep(chan, f->fid, 0)) == nil)
1433                 return Efid;
1434         return _clunk(file, 1);
1435 }
1436
1437 int
1438 fs_stat(Chan* chan, Fcall* f, Fcall* r, uchar* data)
1439 {
1440         Dir dir;
1441         Iobuf *p;
1442         Dentry *d, dentry;
1443         File *file;
1444         int error, len;
1445
1446         error = 0;
1447         p = nil;
1448         if((file = filep(chan, f->fid, 0)) == nil)
1449                 return Efid;
1450         if(file->qid.type & QTAUTH){
1451                 memset(&dentry, 0, sizeof dentry);
1452                 d = &dentry;
1453                 mkqid9p1(&d->qid, &file->qid);
1454                 strcpy(d->name, "#¿");
1455                 d->uid = file->uid;
1456                 d->gid = d->uid;
1457                 d->muid = d->uid;
1458                 d->atime = time(nil);
1459                 d->mtime = d->atime;
1460                 d->size = 0;
1461         } else {
1462                 p = getbuf(file->fs->dev, file->addr, Brd);
1463                 if(p == nil || checktag(p, Tdir, QPNONE)){
1464                         error = Edir1;
1465                         goto out;
1466                 }
1467                 d = getdir(p, file->slot);
1468                 if(d == nil || !(d->mode & DALLOC)){
1469                         error = Ealloc;
1470                         goto out;
1471                 }
1472                 if(error = mkqidcmp(&file->qid, d))
1473                         goto out;
1474
1475                 if(d->qid.path == QPROOT)       /* stat of root gives time */
1476                         d->atime = time(nil);
1477         }
1478         len = mkdir9p2(&dir, d, data);
1479         data += len;
1480
1481         if((r->nstat = convD2M(&dir, data, chan->msize - len)) == 0)
1482                 error = Eedge;
1483         r->stat = data;
1484
1485 out:
1486         if(p != nil)
1487                 putbuf(p);
1488         if(file != nil)
1489                 qunlock(file);
1490
1491         return error;
1492 }
1493
1494 static int
1495 fs_wstat(Chan* chan, Fcall* f, Fcall*, char* strs)
1496 {
1497         Iobuf *p, *p1;
1498         Dentry *d, *d1;
1499         File *file;
1500         int error, err, gid, gl, muid, op, slot, tsync, uid;
1501         long addr;
1502         Dir dir;
1503
1504         if(convM2D(f->stat, f->nstat, &dir, strs) == 0)
1505                 return Econvert;
1506
1507         /*
1508          * Get the file.
1509          * if filesystem is read-only, can't change anything.
1510          */
1511         if((file = filep(chan, f->fid, 0)) == nil)
1512                 return Efid;
1513         p = p1 = nil;
1514         if(file->fs->dev->type == Devro){
1515                 error = Eronly;
1516                 goto out;
1517         }
1518         if(file->qid.type & QTAUTH){
1519                 error = Emode;
1520                 goto out;
1521         }
1522
1523         /*
1524          * Get the current entry and check it is still valid.
1525          */
1526         p = getbuf(file->fs->dev, file->addr, Brd);
1527         if(p == nil || checktag(p, Tdir, QPNONE)){
1528                 error = Ealloc;
1529                 goto out;
1530         }
1531         d = getdir(p, file->slot);
1532         if(d == nil || !(d->mode & DALLOC)){
1533                 error = Ealloc;
1534                 goto out;
1535         }
1536         if(error = mkqidcmp(&file->qid, d))
1537                 goto out;
1538
1539         /*
1540          * Run through each of the (sub-)fields in the provided Dir
1541          * checking for validity and whether it's a default:
1542          * .type, .dev and .atime are completely ignored and not checked;
1543          * .qid.path, .qid.vers and .muid are checked for validity but
1544          * any attempt to change them is an error.
1545          * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can
1546          * possibly be changed (and .muid iff is god).
1547          *
1548          * 'Op' flags there are changed fields, i.e. it's not a no-op.
1549          * 'Tsync' flags all fields are defaulted.
1550          */
1551         tsync = 1;
1552         if(dir.qid.path != ~0){
1553                 if(dir.qid.path != file->qid.path){
1554                         error = Ewstatp;
1555                         goto out;
1556                 }
1557                 tsync = 0;
1558         }
1559         if(dir.qid.vers != ~0){
1560                 if(dir.qid.vers != file->qid.vers){
1561                         error = Ewstatv;
1562                         goto out;
1563                 }
1564                 tsync = 0;
1565         }
1566
1567         /*
1568          * .qid.type and .mode have some bits in common. Only .mode
1569          * is currently needed for comparisons with the old mode but
1570          * if there are changes to the bits also encoded in .qid.type
1571          * then file->qid must be updated appropriately later.
1572          */
1573         if(dir.qid.type == (uchar)~0){
1574                 if(dir.mode == ~0)
1575                         dir.qid.type = mktype9p2(d->mode);
1576                 else
1577                         dir.qid.type = dir.mode>>24;
1578         } else
1579                 tsync = 0;
1580         if(dir.mode == ~0)
1581                 dir.mode = mkmode9p2(d->mode);
1582         else
1583                 tsync = 0;
1584
1585         /*
1586          * Check dir.qid.type and dir.mode agree, check for any unknown
1587          * type/mode bits, check for an attempt to change the directory bit.
1588          */
1589         if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
1590                 error = Ewstatq;
1591                 goto out;
1592         }
1593         if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){
1594                 error = Ewstatb;
1595                 goto out;
1596         }
1597
1598         op = dir.mode^mkmode9p2(d->mode);
1599         if(op & DMDIR){
1600                 error = Ewstatd;
1601                 goto out;
1602         }
1603
1604         if(dir.mtime != ~0){
1605                 if(dir.mtime != d->mtime)
1606                         op = 1;
1607                 tsync = 0;
1608         } else
1609                 dir.mtime = d->mtime;
1610
1611         if(dir.length == ~(Off)0)
1612                 dir.length = d->size;
1613         else {
1614                 if (dir.length < 0) {
1615                         error = Ewstatl;
1616                         goto out;
1617                 } else if(dir.length != d->size)
1618                         op = 1;
1619                 tsync = 0;
1620         }
1621
1622         /*
1623          * Check for permission to change .mode, .mtime or .length,
1624          * must be owner or leader of either group, for which test gid
1625          * is needed; permission checks on gid will be done later.
1626          * 'Gl' counts whether neither, one or both groups are led.
1627          */
1628         if(dir.gid != nil && *dir.gid != '\0'){
1629                 gid = strtouid(dir.gid);
1630                 tsync = 0;
1631         } else
1632                 gid = d->gid;
1633         gl = leadgroup(file->uid, gid) != 0;
1634         gl += leadgroup(file->uid, d->gid) != 0;
1635
1636         if(op && !isallowed(file) && d->uid != file->uid && !gl){
1637                 error = Ewstato;
1638                 goto out;
1639         }
1640
1641         /*
1642          * Rename.
1643          * Check .name is valid and different to the current.
1644          */
1645         if(dir.name != nil && *dir.name != '\0'){
1646                 if(error = checkname(dir.name))
1647                         goto out;
1648                 if(strncmp(dir.name, d->name, NAMELEN))
1649                         op = 1;
1650                 else
1651                         dir.name = d->name;
1652                 tsync = 0;
1653         } else
1654                 dir.name = d->name;
1655
1656         /*
1657          * If the name is really to be changed check it's unique
1658          * and there is write permission in the parent.
1659          */
1660         if(dir.name != d->name){
1661                 /*
1662                  * First get parent.
1663                  * Must drop current entry to prevent
1664                  * deadlock when searching that new name
1665                  * already exists below.
1666                  */
1667                 putbuf(p);
1668                 p = nil;
1669
1670                 if(file->wpath == nil){
1671                         error = Ephase;
1672                         goto out;
1673                 }
1674                 p1 = getbuf(file->fs->dev, file->wpath->addr, Brd);
1675                 if(p1 == nil || checktag(p1, Tdir, QPNONE)){
1676                         error = Ephase;
1677                         goto out;
1678                 }
1679                 d1 = getdir(p1, file->wpath->slot);
1680                 if(d1 == nil || !(d1->mode & DALLOC)){
1681                         error = Ephase;
1682                         goto out;
1683                 }
1684
1685                 /*
1686                  * Check entries in parent for new name.
1687                  */
1688                 for(addr = 0; ; addr++){
1689                         if((p = dnodebuf(p1, d1, addr, 0, file->uid)) == nil)
1690                                 break;
1691                         if(checktag(p, Tdir, d1->qid.path)){
1692                                 putbuf(p);
1693                                 continue;
1694                         }
1695                         for(slot = 0; slot < DIRPERBUF; slot++){
1696                                 d = getdir(p, slot);
1697                                 if(!(d->mode & DALLOC) ||
1698                                    strncmp(dir.name, d->name, sizeof d->name))
1699                                         continue;
1700                                 error = Eexist;
1701                                 goto out;
1702                         }
1703                         putbuf(p);
1704                 }
1705
1706                 /*
1707                  * Reacquire entry and check it's still OK.
1708                  */
1709                 p = getbuf(file->fs->dev, file->addr, Brd);
1710                 if(p == nil || checktag(p, Tdir, QPNONE)){
1711                         error = Ephase;
1712                         goto out;
1713                 }
1714                 d = getdir(p, file->slot);
1715                 if(d == nil || !(d->mode & DALLOC)){
1716                         error = Ephase;
1717                         goto out;
1718                 }
1719
1720                 /*
1721                  * Check write permission in the parent.
1722                  */
1723                 if(iaccess(file, d1, DWRITE)){
1724                         error = Eaccess;
1725                         goto out;
1726                 }
1727         }
1728
1729         /*
1730          * Check for permission to change owner - must be god.
1731          */
1732         if(dir.uid != nil && *dir.uid != '\0'){
1733                 uid = strtouid(dir.uid);
1734                 if(uid != d->uid){
1735                         if(!isallowed(file)){
1736                                 error = Ewstatu;
1737                                 goto out;
1738                         }
1739                         op = 1;
1740                 }
1741                 tsync = 0;
1742         } else
1743                 uid = d->uid;
1744         if(dir.muid != nil && *dir.muid != '\0'){
1745                 muid = strtouid(dir.muid);
1746                 if(muid != d->muid){
1747                         if(!isallowed(file)){
1748                                 error = Ewstatm;
1749                                 goto out;
1750                         }
1751                         op = 1;
1752                 }
1753                 tsync = 0;
1754         } else
1755                 muid = d->muid;
1756
1757         /*
1758          * Check for permission to change group, must be
1759          * either owner and in new group or leader of both groups.
1760          */
1761         if(gid != d->gid){
1762                 if(!isallowed(file)
1763                 && !(d->uid == file->uid && ingroup(file->uid, gid))
1764                 && !(gl == 2)){
1765                         error = Ewstatg;
1766                         goto out;
1767                 }
1768                 op = 1;
1769         }
1770
1771         /*
1772          * Checks all done, update if necessary.
1773          */
1774         if(op){
1775                 d->mode = mkmode9p1(dir.mode);
1776                 file->qid.type = mktype9p2(d->mode);
1777                 d->mtime = dir.mtime;
1778                 if (dir.length < d->size) {
1779                         err = dtrunclen(p, d, dir.length, uid);
1780                         if (error == 0)
1781                                 error = err;
1782                 }
1783                 d->size = dir.length;
1784                 if(dir.name != d->name)
1785                         strncpy(d->name, dir.name, NAMELEN);
1786                 d->uid = uid;
1787                 d->gid = gid;
1788                 d->muid = muid;
1789                 p->flags |= Bmod;
1790         }
1791         if(!tsync)
1792                 accessdir(p, d, FREAD, file->uid);
1793
1794 out:
1795         if(p != nil)
1796                 putbuf(p);
1797         if(p1 != nil)
1798                 putbuf(p1);
1799         qunlock(file);
1800
1801         return error;
1802 }
1803
1804 int
1805 serve9p2(Msgbuf* mb)
1806 {
1807         Chan *chan;
1808         Fcall f, r;
1809         Msgbuf *data, *rmb;
1810         char ename[64];
1811         int error, n, type;
1812         static int once;
1813
1814         if(once == 0){
1815                 fmtinstall('F', fcallfmt);
1816                 once = 1;
1817         }
1818
1819         /*
1820          * 0 return means i don't understand this message,
1821          * 1 return means i dealt with it, including error
1822          * replies.
1823          */
1824         if(convM2S(mb->data, mb->count, &f) != mb->count){
1825                 fprint(2, "didn't like %d byte message\n", mb->count);
1826                 return 0;
1827         }
1828         type = f.type;
1829         if(type < Tversion || type >= Tmax || (type & 1) || type == Terror)
1830                 return 0;
1831
1832         chan = mb->chan;
1833         if(CHAT(chan))
1834                 fprint(2, "9p2: f %F\n", &f);
1835         r.type = type+1;
1836         r.tag = f.tag;
1837         error = 0;
1838         data = nil;
1839         chan->err[0] = 0;
1840
1841         switch(type){
1842         default:
1843                 r.type = Rerror;
1844                 snprint(ename, sizeof(ename), "unknown message: %F", &f);
1845                 r.ename = ename;
1846                 break;
1847         case Tversion:
1848                 error = version(chan, &f, &r);
1849                 break;
1850         case Tauth:
1851                 error = auth(chan, &f, &r);
1852                 break;
1853         case Tattach:
1854                 error = attach(chan, &f, &r);
1855                 break;
1856         case Tflush:
1857                 error = flush(chan, &f, &r);
1858                 break;
1859         case Twalk:
1860                 error = walk(chan, &f, &r);
1861                 break;
1862         case Topen:
1863                 error = fs_open(chan, &f, &r);
1864                 break;
1865         case Tcreate:
1866                 error = fs_create(chan, &f, &r);
1867                 break;
1868         case Tread:
1869                 data = mballoc(chan->msize, chan, Mbreply1);
1870                 error = fs_read(chan, &f, &r, data->data);
1871                 break;
1872         case Twrite:
1873                 error = fs_write(chan, &f, &r);
1874                 break;
1875         case Tclunk:
1876                 error = clunk(chan, &f, &r);
1877                 break;
1878         case Tremove:
1879                 error = fs_remove(chan, &f, &r);
1880                 break;
1881         case Tstat:
1882                 data = mballoc(chan->msize, chan, Mbreply1);
1883                 error = fs_stat(chan, &f, &r, data->data);
1884                 break;
1885         case Twstat:
1886                 data = mballoc(chan->msize, chan, Mbreply1);
1887                 error = fs_wstat(chan, &f, &r, (char*)data->data);
1888                 break;
1889         }
1890
1891         if(error != 0){
1892                 r.type = Rerror;
1893                 if(chan->err[0])
1894                         r.ename = chan->err;
1895                 else if(error >= MAXERR){
1896                         snprint(ename, sizeof(ename), "error %d", error);
1897                         r.ename = ename;
1898                 } else
1899                         r.ename = errstr9p[error];
1900         }
1901         if(CHAT(chan))
1902                 fprint(2, "9p2: r %F\n", &r);
1903
1904         rmb = mballoc(chan->msize, chan, Mbreply2);
1905         n = convS2M(&r, rmb->data, chan->msize);
1906         if(data != nil)
1907                 mbfree(data);
1908         if(n == 0){
1909                 type = r.type;
1910                 r.type = Rerror;
1911
1912                 /*
1913                  * If a Tversion has not been seen on the chan then
1914                  * chan->msize will be 0. In that case craft a special
1915                  * Rerror message. It's fortunate that the mballoc above
1916                  * for rmb will have returned a Msgbuf of MAXMSG size
1917                  * when given a request with count of 0...
1918                  */
1919                 if(chan->msize == 0){
1920                         r.ename = "Tversion not seen";
1921                         n = convS2M(&r, rmb->data, MAXMSG);
1922                 } else {
1923                         snprint(ename, sizeof(ename), "9p2: convS2M: type %d",
1924                                 type);
1925                         r.ename = ename;
1926                         n = convS2M(&r, rmb->data, chan->msize);
1927                 }
1928                 fprint(2, "%s\n", r.ename);
1929                 if(n == 0){
1930                         /*
1931                          * What to do here, the failure notification failed?
1932                          */
1933                         mbfree(rmb);
1934                         return 1;
1935                 }
1936         }
1937         rmb->count = n;
1938         rmb->param = mb->param;
1939
1940         /* done 9P processing, write reply to network */
1941         fs_send(chan->reply, rmb);
1942
1943         return 1;
1944 }