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