]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/disk/kfs/9p1.c
Add Erik Quanstrom's smart tool for ATA SMART.
[plan9front.git] / sys / src / cmd / disk / kfs / 9p1.c
1 #include        "all.h"
2 #include        "9p1.h"
3
4 /*
5  * buggery to give false qid for
6  * the top 2 levels of the dump fs
7  */
8 void
9 mkqid(Qid* qid, Dentry *d, int buggery)
10 {
11         int c;
12
13         if(buggery && d->qid.path == QPROOT && (d->qid.path & QPDIR)){
14                 c = d->name[0];
15                 if(c >= '0' && c <= '9'){
16                         qid->path = 3;
17                         qid->vers = d->qid.version;
18                         qid->type = QTDIR;
19
20                         c = (c-'0')*10 + (d->name[1]-'0');
21                         if(c >= 1 && c <= 12)
22                                 qid->path = 4;
23                         return;
24                 }
25         }
26
27         mkqid9p2(qid, &d->qid, d->mode);
28 }
29
30 int
31 mkqidcmp(Qid* qid, Dentry *d)
32 {
33         Qid tmp;
34
35         mkqid(&tmp, d, 1);
36         if(qid->path==tmp.path && (qid->type&QTDIR)==(tmp.type&QTDIR))
37                 return 0;
38         return Eqid;
39 }
40
41 void
42 f_nop(Chan *cp, Oldfcall *in, Oldfcall *ou)
43 {
44
45         USED(in);
46         USED(ou);
47         if(CHAT(cp))
48                 print("c_nop %d\n", cp->chan);
49 }
50
51 void
52 f_flush(Chan *cp, Oldfcall *in, Oldfcall *ou)
53 {
54
55         USED(in);
56         USED(ou);
57         if(CHAT(cp))
58                 print("c_flush %d\n", cp->chan);
59         runlock(&cp->reflock);
60         wlock(&cp->reflock);
61         wunlock(&cp->reflock);
62         rlock(&cp->reflock);
63 }
64
65 void
66 f_session(Chan *cp, Oldfcall *in, Oldfcall *ou)
67 {
68         if(CHAT(cp))
69                 print("c_session %d\n", cp->chan);
70
71         memmove(cp->rchal, in->chal, sizeof(cp->rchal));
72         if(wstatallow || cp == cons.srvchan){
73                 memset(ou->chal, 0, sizeof(ou->chal));
74                 memset(ou->authid, 0, sizeof(ou->authid));
75         }else{
76                 mkchallenge(cp);
77                 memmove(ou->chal, cp->chal, sizeof(ou->chal));
78                 memmove(ou->authid, nvr.authid, sizeof(ou->authid));
79         }
80         sprint(ou->authdom, "%s.%s", service, nvr.authdom);
81         fileinit(cp);
82 }
83
84 void
85 f_attach(Chan *cp, Oldfcall *in, Oldfcall *ou)
86 {
87         Iobuf *p;
88         Dentry *d;
89         File *f;
90         int u;
91         Filsys *fs;
92         long raddr;
93
94         if(CHAT(cp)) {
95                 print("c_attach %d\n", cp->chan);
96                 print(" fid = %d\n", in->fid);
97                 print(" uid = %s\n", in->uname);
98                 print(" arg = %s\n", in->aname);
99         }
100
101         ou->qid = QID9P1(0,0);
102         ou->fid = in->fid;
103         if(!in->aname[0])       /* default */
104                 strncpy(in->aname, filesys[0].name, sizeof(in->aname));
105         p = 0;
106         f = filep(cp, in->fid, 1);
107         if(!f) {
108                 ou->err = Efid;
109                 goto out;
110         }
111         u = -1;
112         if(cp != cons.chan){
113                 if(authorize(cp, in, ou) == 0 || strcmp(in->uname, "adm") == 0){
114                         ou->err = Eauth;
115                         goto out;
116                 }
117                 u = strtouid(in->uname);
118                 if(u < 0){
119                         ou->err = Ebadu;
120                         goto out;
121                 }
122         }
123
124         fs = fsstr(in->aname);
125         if(fs == 0) {
126                 ou->err = Ebadspc;
127                 goto out;
128         }
129         raddr = getraddr(fs->dev);
130         p = getbuf(fs->dev, raddr, Bread);
131         d = getdir(p, 0);
132         if(!d || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)) {
133                 ou->err = Ealloc;
134                 goto out;
135         }
136         f->uid = u;
137         if(iaccess(f, d, DREAD)) {
138                 ou->err = Eaccess;
139                 goto out;
140         }
141         accessdir(p, d, FREAD);
142         mkqid(&f->qid, d, 1);
143         f->fs = fs;
144         f->addr = raddr;
145         f->slot = 0;
146         f->open = 0;
147         freewp(f->wpath);
148         f->wpath = 0;
149
150         mkqid9p1(&ou->qid, &f->qid);
151
152 out:
153         if(p)
154                 putbuf(p);
155         if(f) {
156                 qunlock(f);
157                 if(ou->err)
158                         freefp(f);
159         }
160 }
161
162 void
163 f_clone(Chan *cp, Oldfcall *in, Oldfcall *ou)
164 {
165         File *f1, *f2;
166         int fid, fid1;
167
168         if(CHAT(cp)) {
169                 print("c_clone %d\n", cp->chan);
170                 print(" old fid = %d\n", in->fid);
171                 print(" new fid = %d\n", in->newfid);
172         }
173
174         fid = in->fid;
175         fid1 = in->newfid;
176
177         f1 = 0;
178         f2 = 0;
179         if(fid < fid1) {
180                 f1 = filep(cp, fid, 0);
181                 f2 = filep(cp, fid1, 1);
182         } else
183         if(fid1 < fid) {
184                 f2 = filep(cp, fid1, 1);
185                 f1 = filep(cp, fid, 0);
186         }
187         if(!f1 || !f2) {
188                 ou->err = Efid;
189                 goto out;
190         }
191
192
193         f2->fs = f1->fs;
194         f2->addr = f1->addr;
195         f2->open = f1->open & ~FREMOV;
196         f2->uid = f1->uid;
197         f2->slot = f1->slot;
198         f2->qid = f1->qid;
199
200         freewp(f2->wpath);
201         f2->wpath = getwp(f1->wpath);
202
203 out:
204         ou->fid = fid;
205         if(f1)
206                 qunlock(f1);
207         if(f2)
208                 qunlock(f2);
209 }
210
211 void
212 f_walk(Chan *cp, Oldfcall *in, Oldfcall *ou)
213 {
214         Iobuf *p, *p1;
215         Dentry *d, *d1;
216         File *f;
217         Wpath *w, *ow;
218         int slot;
219         long addr;
220
221         if(CHAT(cp)) {
222                 print("c_walk %d\n", cp->chan);
223                 print(" fid = %d\n", in->fid);
224                 print(" name = %s\n", in->name);
225         }
226
227         ou->fid = in->fid;
228         ou->qid = QID9P1(0,0);
229         p = 0;
230         f = filep(cp, in->fid, 0);
231         if(!f) {
232                 ou->err = Efid;
233                 goto out;
234         }
235         p = getbuf(f->fs->dev, f->addr, Bread);
236         d = getdir(p, f->slot);
237         if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
238                 ou->err = Ealloc;
239                 goto out;
240         }
241         if(!(d->mode & DDIR)) {
242                 ou->err = Edir1;
243                 goto out;
244         }
245         if(ou->err = mkqidcmp(&f->qid, d))
246                 goto out;
247         if(cp != cons.chan && iaccess(f, d, DEXEC)) {
248                 ou->err = Eaccess;
249                 goto out;
250         }
251         accessdir(p, d, FREAD);
252         if(strcmp(in->name, ".") == 0)
253                 goto setdot;
254         if(strcmp(in->name, "..") == 0) {
255                 if(f->wpath == 0)
256                         goto setdot;
257                 putbuf(p);
258                 p = 0;
259                 addr = f->wpath->addr;
260                 slot = f->wpath->slot;
261                 p1 = getbuf(f->fs->dev, addr, Bread);
262                 d1 = getdir(p1, slot);
263                 if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
264                         if(p1)
265                                 putbuf(p1);
266                         ou->err = Ephase;
267                         goto out;
268                 }
269                 ow = f->wpath;
270                 f->wpath = ow->up;
271                 putwp(ow);
272                 goto found;
273         }
274         for(addr=0;; addr++) {
275                 p1 = dnodebuf(p, d, addr, 0);
276                 if(!p1 || checktag(p1, Tdir, d->qid.path) ) {
277                         if(p1)
278                                 putbuf(p1);
279                         ou->err = Eentry;
280                         goto out;
281                 }
282                 for(slot=0; slot<DIRPERBUF; slot++) {
283                         d1 = getdir(p1, slot);
284                         if(!(d1->mode & DALLOC))
285                                 continue;
286                         if(strncmp(in->name, d1->name, sizeof(in->name)))
287                                 continue;
288                         /*
289                          * update walk path
290                          */
291                         w = newwp();
292                         if(!w) {
293                                 ou->err = Ewalk;
294                                 putbuf(p1);
295                                 goto out;
296                         }
297                         w->addr = f->addr;
298                         w->slot = f->slot;
299                         w->up = f->wpath;
300                         f->wpath = w;
301                         slot += DIRPERBUF*addr;
302                         goto found;
303                 }
304                 putbuf(p1);
305         }
306
307 found:
308         f->addr = p1->addr;
309         mkqid(&f->qid, d1, 1);
310         putbuf(p1);
311         f->slot = slot;
312
313 setdot:
314         mkqid9p1(&ou->qid, &f->qid);
315         f->open = 0;
316
317 out:
318         if(p)
319                 putbuf(p);
320         if(f)
321                 qunlock(f);
322 }
323
324 void
325 f_clunk(Chan *cp, Oldfcall *in, Oldfcall *ou)
326 {
327         File *f;
328         Tlock *t;
329         long tim;
330
331         if(CHAT(cp)) {
332                 print("c_clunk %d\n", cp->chan);
333                 print(" fid = %d\n", in->fid);
334         }
335
336         f = filep(cp, in->fid, 0);
337         if(!f) {
338                 print("%p\n", f);
339                 ou->err = Efid;
340                 goto out;
341         }
342         if(t = f->tlock) {
343                 tim = time(0);
344                 if(t->time < tim || t->file != f)
345                         ou->err = Ebroken;
346                 t->time = 0;    /* free the lock */
347                 f->tlock = 0;
348         }
349         if(f->open & FREMOV)
350                 ou->err = doremove(f, 0);
351         f->open = 0;
352         freewp(f->wpath);
353         freefp(f);
354
355 out:
356         if(f)
357                 qunlock(f);
358         ou->fid = in->fid;
359 }
360
361 void
362 f_clwalk(Chan *cp, Oldfcall *in, Oldfcall *ou)
363 {
364         int er, fid;
365
366         if(CHAT(cp))
367                 print("c_clwalk macro\n");
368
369         f_clone(cp, in, ou);            /* sets tag, fid */
370         if(ou->err)
371                 return;
372         fid = in->fid;
373         in->fid = in->newfid;
374         f_walk(cp, in, ou);             /* sets tag, fid, qid */
375         er = ou->err;
376         if(er == Eentry) {
377                 /*
378                  * if error is "no entry"
379                  * return non error and fid
380                  */
381                 ou->err = 0;
382                 f_clunk(cp, in, ou);    /* sets tag, fid */
383                 ou->err = 0;
384                 ou->fid = fid;
385                 if(CHAT(cp)) 
386                         print(" error: %s\n", errstring[er]);
387                 return;
388         }
389         if(er) {
390                 /*
391                  * if any other error
392                  * return an error
393                  */
394                 ou->err = 0;
395                 f_clunk(cp, in, ou);    /* sets tag, fid */
396                 ou->err = er;
397                 return;
398         }
399         /*
400          * non error
401          * return newfid
402          */
403 }
404
405 void
406 f_open(Chan *cp, Oldfcall *in, Oldfcall *ou)
407 {
408         Iobuf *p;
409         Dentry *d;
410         File *f;
411         Tlock *t;
412         Qid qid;
413         int ro, fmod;
414
415         if(CHAT(cp)) {
416                 print("c_open %d\n", cp->chan);
417                 print(" fid = %d\n", in->fid);
418                 print(" mode = %o\n", in->mode);
419         }
420
421         p = 0;
422         f = filep(cp, in->fid, 0);
423         if(!f) {
424                 ou->err = Efid;
425                 goto out;
426         }
427
428         /*
429          * if remove on close, check access here
430          */
431         ro = isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup));
432         if(in->mode & MRCLOSE) {
433                 if(ro) {
434                         ou->err = Eronly;
435                         goto out;
436                 }
437                 /*
438                  * check on parent directory of file to be deleted
439                  */
440                 if(f->wpath == 0 || f->wpath->addr == f->addr) {
441                         ou->err = Ephase;
442                         goto out;
443                 }
444                 p = getbuf(f->fs->dev, f->wpath->addr, Bread);
445                 d = getdir(p, f->wpath->slot);
446                 if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
447                         ou->err = Ephase;
448                         goto out;
449                 }
450                 if(iaccess(f, d, DWRITE)) {
451                         ou->err = Eaccess;
452                         goto out;
453                 }
454                 putbuf(p);
455         }
456         p = getbuf(f->fs->dev, f->addr, Bread);
457         d = getdir(p, f->slot);
458         if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
459                 ou->err = Ealloc;
460                 goto out;
461         }
462         if(ou->err = mkqidcmp(&f->qid, d))
463                 goto out;
464         mkqid(&qid, d, 1);
465         switch(in->mode & 7) {
466
467         case MREAD:
468                 if(iaccess(f, d, DREAD) && !writeallow)
469                         goto badaccess;
470                 fmod = FREAD;
471                 break;
472
473         case MWRITE:
474                 if((d->mode & DDIR) ||
475                    (iaccess(f, d, DWRITE) && !writeallow))
476                         goto badaccess;
477                 if(ro) {
478                         ou->err = Eronly;
479                         goto out;
480                 }
481                 fmod = FWRITE;
482                 break;
483
484         case MBOTH:
485                 if((d->mode & DDIR) ||
486                    (iaccess(f, d, DREAD) && !writeallow) ||
487                    (iaccess(f, d, DWRITE) && !writeallow))
488                         goto badaccess;
489                 if(ro) {
490                         ou->err = Eronly;
491                         goto out;
492                 }
493                 fmod = FREAD+FWRITE;
494                 break;
495
496         case MEXEC:
497                 if((d->mode & DDIR) ||
498                    iaccess(f, d, DEXEC))
499                         goto badaccess;
500                 fmod = FREAD;
501                 break;
502
503         default:
504                 ou->err = Emode;
505                 goto out;
506         }
507         if(in->mode & MTRUNC) {
508                 if((d->mode & DDIR) ||
509                    (iaccess(f, d, DWRITE) && !writeallow))
510                         goto badaccess;
511                 if(ro) {
512                         ou->err = Eronly;
513                         goto out;
514                 }
515         }
516         t = 0;
517         if(d->mode & DLOCK) {
518                 t = tlocked(p, d);
519                 if(t == 0) {
520                         ou->err = Elocked;
521                         goto out;
522                 }
523                 t->file = f;
524         }
525         if(in->mode & MRCLOSE)
526                 fmod |= FREMOV;
527         f->open = fmod;
528         if(in->mode & MTRUNC)
529                 if(!(d->mode & DAPND))
530                         dtrunc(p, d);
531         f->tlock = t;
532         f->lastra = 0;
533         mkqid9p1(&ou->qid, &qid);
534         goto out;
535
536 badaccess:
537         ou->err = Eaccess;
538         f->open = 0;
539
540 out:
541         if(p)
542                 putbuf(p);
543         if(f)
544                 qunlock(f);
545         ou->fid = in->fid;
546 }
547
548 void
549 f_create(Chan *cp, Oldfcall *in, Oldfcall *ou)
550 {
551         Iobuf *p, *p1;
552         Dentry *d, *d1;
553         File *f;
554         int slot, slot1, fmod;
555         long addr, addr1, path;
556         Qid qid;
557         Tlock *t;
558         Wpath *w;
559
560         if(CHAT(cp)) {
561                 print("c_create %d\n", cp->chan);
562                 print(" fid = %d\n", in->fid);
563                 print(" name = %s\n", in->name);
564                 print(" perm = %lx+%lo\n", (in->perm>>28)&0xf,
565                                 in->perm&0777);
566                 print(" mode = %d\n", in->mode);
567         }
568
569         p = 0;
570         f = filep(cp, in->fid, 0);
571         if(!f) {
572                 ou->err = Efid;
573                 goto out;
574         }
575         if(isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) {
576                 ou->err = Eronly;
577                 goto out;
578         }
579
580         p = getbuf(f->fs->dev, f->addr, Bread);
581         d = getdir(p, f->slot);
582         if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
583                 ou->err = Ealloc;
584                 goto out;
585         }
586         if(ou->err = mkqidcmp(&f->qid, d))
587                 goto out;
588         if(!(d->mode & DDIR)) {
589                 ou->err = Edir2;
590                 goto out;
591         }
592         if(cp != cons.chan && iaccess(f, d, DWRITE) && !writeallow) {
593                 ou->err = Eaccess;
594                 goto out;
595         }
596         accessdir(p, d, FREAD);
597         if(!strncmp(in->name, ".", sizeof(in->name)) ||
598            !strncmp(in->name, "..", sizeof(in->name))) {
599                 ou->err = Edot;
600                 goto out;
601         }
602         if(checkname(in->name)) {
603                 ou->err = Ename;
604                 goto out;
605         }
606         addr1 = 0;
607         slot1 = 0;      /* set */
608         for(addr=0;; addr++) {
609                 p1 = dnodebuf(p, d, addr, 0);
610                 if(!p1) {
611                         if(addr1)
612                                 break;
613                         p1 = dnodebuf(p, d, addr, Tdir);
614                 }
615                 if(p1 == 0) {
616                         ou->err = Efull;
617                         goto out;
618                 }
619                 if(checktag(p1, Tdir, d->qid.path)) {
620                         putbuf(p1);
621                         goto phase;
622                 }
623                 for(slot=0; slot<DIRPERBUF; slot++) {
624                         d1 = getdir(p1, slot);
625                         if(!(d1->mode & DALLOC)) {
626                                 if(!addr1) {
627                                         addr1 = p1->addr;
628                                         slot1 = slot + addr*DIRPERBUF;
629                                 }
630                                 continue;
631                         }
632                         if(!strncmp(in->name, d1->name, sizeof(in->name))) {
633                                 putbuf(p1);
634                                 ou->err = Eexist;
635                                 goto out;
636                         }
637                 }
638                 putbuf(p1);
639         }
640         switch(in->mode & 7) {
641         case MEXEC:
642         case MREAD:             /* seems only useful to make directories */
643                 fmod = FREAD;
644                 break;
645
646         case MWRITE:
647                 fmod = FWRITE;
648                 break;
649
650         case MBOTH:
651                 fmod = FREAD+FWRITE;
652                 break;
653
654         default:
655                 ou->err = Emode;
656                 goto out;
657         }
658         if(in->perm & PDIR)
659                 if((in->mode & MTRUNC) || (in->perm & PAPND) || (fmod & FWRITE))
660                         goto badaccess;
661         /*
662          * do it
663          */
664         path = qidpathgen(&f->fs->dev);
665         p1 = getbuf(f->fs->dev, addr1, Bread|Bimm|Bmod);
666         d1 = getdir(p1, slot1);
667         if(!d1 || checktag(p1, Tdir, d->qid.path)) {
668                 if(p1)
669                         putbuf(p1);
670                 goto phase;
671         }
672         if(d1->mode & DALLOC) {
673                 putbuf(p1);
674                 goto phase;
675         }
676
677         strncpy(d1->name, in->name, sizeof(in->name));
678         /*
679          * bogus argument passing -- see console.c
680          */
681         if(cp == cons.chan) {
682                 d1->uid = cons.uid;
683                 d1->gid = cons.gid;
684         } else {
685                 d1->uid = f->uid;
686                 d1->gid = d->gid;
687                 in->perm &= d->mode | ~0666;
688                 if(in->perm & PDIR)
689                         in->perm &= d->mode | ~0777;
690         }
691         d1->qid.path = path;
692         d1->qid.version = 0;
693         d1->mode = DALLOC | (in->perm & 0777);
694         if(in->perm & PDIR) {
695                 d1->mode |= DDIR;
696                 d1->qid.path |= QPDIR;
697         }
698         if(in->perm & PAPND)
699                 d1->mode |= DAPND;
700         t = 0;
701         if(in->perm & PLOCK) {
702                 d1->mode |= DLOCK;
703                 t = tlocked(p1, d1);
704         }
705         accessdir(p1, d1, FWRITE);
706         mkqid(&qid, d1, 0);
707         putbuf(p1);
708         accessdir(p, d, FWRITE);
709
710         /*
711          * do a walk to new directory entry
712          */
713         w = newwp();
714         if(!w) {
715                 ou->err = Ewalk;
716                 goto out;
717         }
718         w->addr = f->addr;
719         w->slot = f->slot;
720         w->up = f->wpath;
721         f->wpath = w;
722         f->qid = qid;
723         f->tlock = t;
724         f->lastra = 0;
725         if(in->mode & MRCLOSE)
726                 fmod |= FREMOV;
727         f->open = fmod;
728         f->addr = addr1;
729         f->slot = slot1;
730         if(t)
731                 t->file = f;
732         mkqid9p1(&ou->qid, &qid);
733         goto out;
734
735 badaccess:
736         ou->err = Eaccess;
737         goto out;
738
739 phase:
740         ou->err = Ephase;
741
742 out:
743         if(p)
744                 putbuf(p);
745         if(f)
746                 qunlock(f);
747         ou->fid = in->fid;
748 }
749
750 void
751 f_read(Chan *cp, Oldfcall *in, Oldfcall *ou)
752 {
753         Iobuf *p, *p1;
754         File *f;
755         Dentry *d, *d1;
756         Tlock *t;
757         long addr, offset, tim;
758         int nread, count, n, o, slot;
759
760         if(CHAT(cp)) {
761                 print("c_read %d\n", cp->chan);
762                 print(" fid = %d\n", in->fid);
763                 print(" offset = %ld\n", in->offset);
764                 print(" count = %ld\n", in->count);
765         }
766
767         p = 0;
768         count = in->count;
769         offset = in->offset;
770         nread = 0;
771         f = filep(cp, in->fid, 0);
772         if(!f) {
773                 ou->err = Efid;
774                 goto out;
775         }
776         if(!(f->open & FREAD)) {
777                 ou->err = Eopen;
778                 goto out;
779         }
780         if(count < 0 || count > MAXDAT) {
781                 ou->err = Ecount;
782                 goto out;
783         }
784         if(offset < 0) {
785                 ou->err = Eoffset;
786                 goto out;
787         }
788         p = getbuf(f->fs->dev, f->addr, Bread);
789         d = getdir(p, f->slot);
790         if(!d || !(d->mode & DALLOC)) {
791                 ou->err = Ealloc;
792                 goto out;
793         }
794         if(ou->err = mkqidcmp(&f->qid, d))
795                 goto out;
796         if(t = f->tlock) {
797                 tim = time(0);
798                 if(t->time < tim || t->file != f) {
799                         ou->err = Ebroken;
800                         goto out;
801                 }
802                 /* renew the lock */
803                 t->time = tim + TLOCK;
804         }
805         accessdir(p, d, FREAD);
806         if(d->mode & DDIR) {
807                 addr = 0;
808                 goto dread;
809         }
810         if(offset >= d->size)
811                 count = 0;
812         else if(offset+count > d->size)
813                 count = d->size - offset;
814         while(count > 0) {
815                 addr = offset / BUFSIZE;
816                 if(addr == f->lastra+1)
817                         dbufread(p, d, addr+1);
818                 f->lastra = addr;
819                 o = offset % BUFSIZE;
820                 n = BUFSIZE - o;
821                 if(n > count)
822                         n = count;
823                 p1 = dnodebuf(p, d, addr, 0);
824                 if(p1) {
825                         if(checktag(p1, Tfile, QPNONE)) {
826                                 ou->err = Ephase;
827                                 putbuf(p1);
828                                 goto out;
829                         }
830                         memmove(ou->data+nread, p1->iobuf+o, n);
831                         putbuf(p1);
832                 } else
833                         memset(ou->data+nread, 0, n);
834                 count -= n;
835                 nread += n;
836                 offset += n;
837         }
838         goto out;
839
840 dread:
841         p1 = dnodebuf(p, d, addr, 0);
842         if(!p1)
843                 goto out;
844         if(checktag(p1, Tdir, QPNONE)) {
845                 ou->err = Ephase;
846                 putbuf(p1);
847                 goto out;
848         }
849         n = DIRREC;
850         for(slot=0; slot<DIRPERBUF; slot++) {
851                 d1 = getdir(p1, slot);
852                 if(!(d1->mode & DALLOC))
853                         continue;
854                 if(offset >= n) {
855                         offset -= n;
856                         continue;
857                 }
858                 if(count < n) {
859                         putbuf(p1);
860                         goto out;
861                 }
862                 if(convD2M9p1(d1, ou->data+nread) != n)
863                         print("dirread convD2M\n");
864                 nread += n;
865                 count -= n;
866         }
867         putbuf(p1);
868         addr++;
869         goto dread;
870
871 out:
872         count = in->count - nread;
873         if(count > 0)
874                 memset(ou->data+nread, 0, count);
875         if(p)
876                 putbuf(p);
877         if(f)
878                 qunlock(f);
879         ou->fid = in->fid;
880         ou->count = nread;
881         if(CHAT(cp))
882                 print(" nread = %d\n", nread);
883 }
884
885 void
886 f_write(Chan *cp, Oldfcall *in, Oldfcall *ou)
887 {
888         Iobuf *p, *p1;
889         Dentry *d;
890         File *f;
891         Tlock *t;
892         long offset, addr, tim;
893         int count, nwrite, o, n;
894
895         if(CHAT(cp)) {
896                 print("c_write %d\n", cp->chan);
897                 print(" fid = %d\n", in->fid);
898                 print(" offset = %ld\n", in->offset);
899                 print(" count = %ld\n", in->count);
900         }
901
902         offset = in->offset;
903         count = in->count;
904         nwrite = 0;
905         p = 0;
906         f = filep(cp, in->fid, 0);
907         if(!f) {
908                 ou->err = Efid;
909                 goto out;
910         }
911         if(!(f->open & FWRITE)) {
912                 ou->err = Eopen;
913                 goto out;
914         }
915         if(isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) {
916                 ou->err = Eronly;
917                 goto out;
918         }
919         if(count < 0 || count > MAXDAT) {
920                 ou->err = Ecount;
921                 goto out;
922         }
923         if(offset < 0) {
924                 ou->err = Eoffset;
925                 goto out;
926         }
927         p = getbuf(f->fs->dev, f->addr, Bread|Bmod);
928         d = getdir(p, f->slot);
929         if(!d || !(d->mode & DALLOC)) {
930                 ou->err = Ealloc;
931                 goto out;
932         }
933         if(ou->err = mkqidcmp(&f->qid, d))
934                 goto out;
935         if(t = f->tlock) {
936                 tim = time(0);
937                 if(t->time < tim || t->file != f) {
938                         ou->err = Ebroken;
939                         goto out;
940                 }
941                 /* renew the lock */
942                 t->time = tim + TLOCK;
943         }
944         accessdir(p, d, FWRITE);
945         if(d->mode & DAPND)
946                 offset = d->size;
947         if(offset+count > d->size)
948                 d->size = offset+count;
949         while(count > 0) {
950                 addr = offset / BUFSIZE;
951                 o = offset % BUFSIZE;
952                 n = BUFSIZE - o;
953                 if(n > count)
954                         n = count;
955                 p1 = dnodebuf(p, d, addr, Tfile);
956                 if(p1 == 0) {
957                         ou->err = Efull;
958                         goto out;
959                 }
960                 if(checktag(p1, Tfile, d->qid.path)) {
961                         putbuf(p1);
962                         ou->err = Ephase;
963                         goto out;
964                 }
965                 memmove(p1->iobuf+o, in->data+nwrite, n);
966                 p1->flags |= Bmod;
967                 putbuf(p1);
968                 count -= n;
969                 nwrite += n;
970                 offset += n;
971         }
972         if(CHAT(cp))
973                 print(" nwrite = %d\n", nwrite);
974
975 out:
976         if(p)
977                 putbuf(p);
978         if(f)
979                 qunlock(f);
980         ou->fid = in->fid;
981         ou->count = nwrite;
982 }
983
984 int
985 doremove(File *f, int iscon)
986 {
987         Iobuf *p, *p1;
988         Dentry *d, *d1;
989         long addr;
990         int slot, err;
991
992         p = 0;
993         p1 = 0;
994         if(isro(f->fs->dev) || (f->cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) {
995                 err = Eronly;
996                 goto out;
997         }
998         /*
999          * check on parent directory of file to be deleted
1000          */
1001         if(f->wpath == 0 || f->wpath->addr == f->addr) {
1002                 err = Ephase;
1003                 goto out;
1004         }
1005         p1 = getbuf(f->fs->dev, f->wpath->addr, Bread);
1006         d1 = getdir(p1, f->wpath->slot);
1007         if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
1008                 err = Ephase;
1009                 goto out;
1010         }
1011         if(!iscon && iaccess(f, d1, DWRITE)) {
1012                 err = Eaccess;
1013                 goto out;
1014         }
1015         accessdir(p1, d1, FWRITE);
1016         putbuf(p1);
1017         p1 = 0;
1018
1019         /*
1020          * check on file to be deleted
1021          */
1022         p = getbuf(f->fs->dev, f->addr, Bread);
1023         d = getdir(p, f->slot);
1024         if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1025                 err = Ealloc;
1026                 goto out;
1027         }
1028         if(err = mkqidcmp(&f->qid, d))
1029                 goto out;
1030
1031         /*
1032          * if deleting a directory, make sure it is empty
1033          */
1034         if((d->mode & DDIR))
1035         for(addr=0;; addr++) {
1036                 p1 = dnodebuf(p, d, addr, 0);
1037                 if(!p1)
1038                         break;
1039                 if(checktag(p1, Tdir, d->qid.path)) {
1040                         err = Ephase;
1041                         goto out;
1042                 }
1043                 for(slot=0; slot<DIRPERBUF; slot++) {
1044                         d1 = getdir(p1, slot);
1045                         if(!(d1->mode & DALLOC))
1046                                 continue;
1047                         err = Eempty;
1048                         goto out;
1049                 }
1050                 putbuf(p1);
1051         }
1052
1053         /*
1054          * do it
1055          */
1056         dtrunc(p, d);
1057         memset(d, 0, sizeof(Dentry));
1058         settag(p, Tdir, QPNONE);
1059
1060 out:
1061         if(p1)
1062                 putbuf(p1);
1063         if(p)
1064                 putbuf(p);
1065         return err;
1066 }
1067
1068 void
1069 f_remove(Chan *cp, Oldfcall *in, Oldfcall *ou)
1070 {
1071         File *f;
1072
1073         if(CHAT(cp)) {
1074                 print("c_remove %d\n", cp->chan);
1075                 print(" fid = %d\n", in->fid);
1076         }
1077
1078         f = filep(cp, in->fid, 0);
1079         if(!f) {
1080                 ou->err = Efid;
1081                 goto out;
1082         }
1083         ou->err = doremove(f, cp==cons.chan);
1084
1085 out:
1086         ou->fid = in->fid;
1087         if(f)
1088                 qunlock(f);
1089 }
1090
1091 void
1092 f_stat(Chan *cp, Oldfcall *in, Oldfcall *ou)
1093 {
1094         Iobuf *p;
1095         Dentry *d;
1096         File *f;
1097
1098         if(CHAT(cp)) {
1099                 print("c_stat %d\n", cp->chan);
1100                 print(" fid = %d\n", in->fid);
1101         }
1102
1103         p = 0;
1104         memset(ou->stat, 0, sizeof(ou->stat));
1105         f = filep(cp, in->fid, 0);
1106         if(!f) {
1107                 ou->err = Efid;
1108                 goto out;
1109         }
1110         p = getbuf(f->fs->dev, f->addr, Bread);
1111         d = getdir(p, f->slot);
1112         if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1113                 ou->err = Ealloc;
1114                 goto out;
1115         }
1116         if(ou->err = mkqidcmp(&f->qid, d))
1117                 goto out;
1118         if(d->qid.path == QPROOT)       /* stat of root gives time */
1119                 d->atime = time(0);
1120         if(convD2M9p1(d, ou->stat) != DIRREC)
1121                 print("stat convD2M\n");
1122
1123 out:
1124         if(p)
1125                 putbuf(p);
1126         if(f)
1127                 qunlock(f);
1128         ou->fid = in->fid;
1129 }
1130
1131 void
1132 f_wstat(Chan *cp, Oldfcall *in, Oldfcall *ou)
1133 {
1134         Iobuf *p, *p1;
1135         Dentry *d, *d1, xd;
1136         File *f;
1137         int slot;
1138         long addr;
1139
1140         if(CHAT(cp)) {
1141                 print("c_wstat %d\n", cp->chan);
1142                 print(" fid = %d\n", in->fid);
1143         }
1144
1145         p = 0;
1146         p1 = 0;
1147         d1 = 0;
1148         f = filep(cp, in->fid, 0);
1149         if(!f) {
1150                 ou->err = Efid;
1151                 goto out;
1152         }
1153         if(isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) {
1154                 ou->err = Eronly;
1155                 goto out;
1156         }
1157
1158         /*
1159          * first get parent
1160          */
1161         if(f->wpath) {
1162                 p1 = getbuf(f->fs->dev, f->wpath->addr, Bread);
1163                 d1 = getdir(p1, f->wpath->slot);
1164                 if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
1165                         ou->err = Ephase;
1166                         goto out;
1167                 }
1168         }
1169
1170         p = getbuf(f->fs->dev, f->addr, Bread);
1171         d = getdir(p, f->slot);
1172         if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1173                 ou->err = Ealloc;
1174                 goto out;
1175         }
1176         if(ou->err = mkqidcmp(&f->qid, d))
1177                 goto out;
1178
1179         convM2D9p1(in->stat, &xd);
1180         if(CHAT(cp)) {
1181                 print(" d.name = %s\n", xd.name);
1182                 print(" d.uid  = %d\n", xd.uid);
1183                 print(" d.gid  = %d\n", xd.gid);
1184                 print(" d.mode = %.4x\n", xd.mode);
1185         }
1186
1187         /*
1188          * if chown,
1189          * must be god
1190          */
1191         while(xd.uid != d->uid) {
1192                 if(wstatallow)                  /* set to allow chown during boot */
1193                         break;
1194                 ou->err = Enotu;
1195                 goto out;
1196         }
1197
1198         /*
1199          * if chgroup,
1200          * must be either
1201          *      a) owner and in new group
1202          *      b) leader of both groups
1203          */
1204         while(xd.gid != d->gid) {
1205                 if(wstatallow || writeallow)            /* set to allow chgrp during boot */
1206                         break;
1207                 if(d->uid == f->uid && ingroup(f->uid, xd.gid))
1208                         break;
1209                 if(leadgroup(f->uid, xd.gid))
1210                         if(leadgroup(f->uid, d->gid))
1211                                 break;
1212                 ou->err = Enotg;
1213                 goto out;
1214         }
1215
1216         /*
1217          * if rename,
1218          * must have write permission in parent
1219          */
1220         if(xd.name[0] == 0)
1221                 strncpy(xd.name, d->name, sizeof(xd.name));
1222         while(strncmp(d->name, xd.name, sizeof(d->name)) != 0) {
1223                 if(checkname(xd.name)) {
1224                         ou->err = Ename;
1225                         goto out;
1226                 }
1227
1228                 if(strcmp(xd.name, ".") == 0 || strcmp(xd.name, "..") == 0) {
1229                         ou->err = Ename;
1230                         goto out;
1231                 }
1232
1233                 /*
1234                  * drop entry to prevent lock, then
1235                  * check that destination name is unique,
1236                  */
1237                 putbuf(p);
1238                 for(addr=0;; addr++) {
1239                         p = dnodebuf(p1, d1, addr, 0);
1240                         if(!p)
1241                                 break;
1242                         if(checktag(p, Tdir, d1->qid.path)) {
1243                                 putbuf(p);
1244                                 continue;
1245                         }
1246                         for(slot=0; slot<DIRPERBUF; slot++) {
1247                                 d = getdir(p, slot);
1248                                 if(!(d->mode & DALLOC))
1249                                         continue;
1250                                 if(!strncmp(xd.name, d->name, sizeof(xd.name))) {
1251                                         ou->err = Eexist;
1252                                         goto out;
1253                                 }
1254                         }
1255                         putbuf(p);
1256                 }
1257
1258                 /*
1259                  * reacquire entry
1260                  */
1261                 p = getbuf(f->fs->dev, f->addr, Bread);
1262                 d = getdir(p, f->slot);
1263                 if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1264                         ou->err = Ephase;
1265                         goto out;
1266                 }
1267
1268                 if(wstatallow || writeallow) /* set to allow rename during boot */
1269                         break;
1270                 if(!d1 || iaccess(f, d1, DWRITE)) {
1271                         ou->err = Eaccess;
1272                         goto out;
1273                 }
1274                 break;
1275         }
1276
1277         /*
1278          * if mode/time, either
1279          *      a) owner
1280          *      b) leader of either group
1281          */
1282         while(d->mtime != xd.mtime ||
1283              ((d->mode^xd.mode) & (DAPND|DLOCK|0777))) {
1284                 if(wstatallow)                  /* set to allow chmod during boot */
1285                         break;
1286                 if(d->uid == f->uid)
1287                         break;
1288                 if(leadgroup(f->uid, xd.gid))
1289                         break;
1290                 if(leadgroup(f->uid, d->gid))
1291                         break;
1292                 ou->err = Enotu;
1293                 goto out;
1294         }
1295         d->mtime = xd.mtime;
1296         d->uid = xd.uid;
1297         d->gid = xd.gid;
1298         d->mode = (xd.mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR));
1299
1300         strncpy(d->name, xd.name, sizeof(d->name));
1301         if(wstatallow) {
1302                 p->flags |= Bmod;
1303                 if(xd.atime)
1304                         d->atime = xd.atime;
1305                 if(xd.mtime)
1306                         d->mtime = xd.mtime;
1307         } else
1308                 accessdir(p, d, FWSTAT);
1309
1310 out:
1311         if(p)
1312                 putbuf(p);
1313         if(p1)
1314                 putbuf(p1);
1315         if(f)
1316                 qunlock(f);
1317         ou->fid = in->fid;
1318 }
1319
1320 void
1321 (*call9p1[MAXSYSCALL])(Chan*, Oldfcall*, Oldfcall*) =
1322 {
1323         [Tnop9p1]               f_nop,
1324         [Tosession9p1]  f_session,
1325         [Tsession9p1]   f_session,
1326         [Tflush9p1]     f_flush,
1327         [Toattach9p1]   f_attach,
1328         [Tattach9p1]    f_attach,
1329         [Tclone9p1]     f_clone,
1330         [Twalk9p1]              f_walk,
1331         [Topen9p1]              f_open,
1332         [Tcreate9p1]    f_create,
1333         [Tread9p1]              f_read,
1334         [Twrite9p1]     f_write,
1335         [Tclunk9p1]     f_clunk,
1336         [Tremove9p1]    f_remove,
1337         [Tstat9p1]              f_stat,
1338         [Twstat9p1]     f_wstat,
1339         [Tclwalk9p1]    f_clwalk,
1340 };
1341
1342 static void
1343 send(Chan *c, uchar *buf, int n)
1344 {
1345         int fd, m;
1346
1347         fd = c->chan;
1348         m = write(fd, buf, n);
1349         if(m == n)
1350                 return;
1351         panic("write failed");
1352 }
1353
1354 void
1355 error9p1(Chan *c, uchar *buf)
1356 {
1357         buf[0] = Rnop9p1;
1358         buf[1] = ~0;
1359         buf[2] = ~0;
1360
1361         send(c, buf, 3);
1362 }
1363
1364 void
1365 serve9p1(Chan *chan, uchar *ib, int nib)
1366 {
1367         int n, t;
1368         uchar inbuf[MAXMSG+MAXDAT], outbuf[MAXMSG+MAXDAT];
1369         Oldfcall fi, fo;
1370
1371         for(;;){
1372                 if(nib){
1373                         memmove(inbuf, ib, nib);
1374                         n = nib;
1375                         nib = 0;
1376                 }else
1377                         n = read(chan->chan, inbuf, sizeof inbuf);
1378                 if(chat)
1379                         print("read msg %d\n", n);
1380                 if(n == 0 && (chan == cons.srvchan || chan == cons.chan))
1381                         continue;
1382                 if(n <= 0)
1383                         return;
1384                 if(convM2S9p1(inbuf, &fi, n) != n){
1385                         error9p1(chan, outbuf);
1386                         continue;
1387                 }
1388
1389                 t = fi.type;
1390                 if(t < 0 || t >= MAXSYSCALL || (t&1) || !call9p1[t]) {
1391                         print("9p1: bad message type\n");
1392                         error9p1(chan, outbuf);
1393                         continue;
1394                 }
1395
1396                 if(CHAT(chan))
1397                         print("9p1: fi %O\n", &fi);
1398
1399                 /*
1400                  * set up reply message
1401                  */
1402                 fo.err = 0;
1403                 if(t == Tread9p1)
1404                         fo.data = (char*)outbuf + 8;
1405         
1406                 /*
1407                  * call the file system
1408                  */
1409                 cons.work.count++;
1410                 cons.rate.count += n;
1411
1412                 /*
1413                  * call the file system
1414                  */
1415                 rlock(&mainlock);
1416                 rlock(&chan->reflock);
1417         
1418                 (*call9p1[t])(chan, &fi, &fo);
1419         
1420                 runlock(&chan->reflock);
1421                 runlock(&mainlock);
1422         
1423                 fo.type = t+1;
1424                 fo.tag = fi.tag;
1425         
1426                 if(chat)
1427                         print("9p1: fo %O\n", &fo);
1428
1429                 if(fo.err) {
1430                         strcpy(fo.ename, errstring[fo.err]);
1431                         if(CHAT(cp))
1432                                 print(" error: %s\n", fo.ename);
1433                         fo.type = Terror9p1+1;
1434                 }
1435         
1436                 n = convS2M9p1(&fo, outbuf);
1437                 if(n == 0) {
1438                         print("9p1: bad S2M conversion\n");
1439                         error9p1(chan, outbuf);
1440                         continue;
1441                 }
1442
1443                 cons.rate.count += n;
1444                 send(chan, outbuf, n);
1445         }
1446 }