1 /* 2 * Framework for USB devices that provide a file tree. 3 * The main process (usbd or the driver's main proc) 4 * calls fsinit() to start FS operation. 5 * 6 * One or more drivers call fsstart/fsend to register 7 * or unregister their operations for their subtrees. 8 * 9 * root dir has qids with 0 in high 32 bits. 10 * for other files we keep the device id in there. 11 * The low 32 bits for directories at / must be 0. 12 */ 13 #include <u.h> 14 #include <libc.h> 15 #include <thread.h> 16 #include <fcall.h> 17 #include "usb.h" 18 #include "usbfs.h" 19 20 #undef dprint 21 #define dprint if(usbfsdebug)fprint 22 23 typedef struct Rpc Rpc; 24 25 enum 26 { 27 Nproc = 3, /* max nb. of cached FS procs */ 28 29 Nofid = ~0, /* null value for fid number */ 30 Notag = ~0, /* null value for tags */ 31 Dietag = 0xdead, /* fake tag to ask outproc to die */ 32 33 Stack = 16 * 1024, 34 35 /* Fsproc requests */ 36 Run = 0, /* call f(r) */ 37 Exit, /* terminate */ 38 39 }; 40 41 struct Rpc 42 { 43 Fcall t; 44 Fcall r; 45 Fid* fid; 46 int flushed; 47 Rpc* next; 48 char data[Bufsize]; 49 }; 50 51 int usbfsdebug; 52 53 char Enotfound[] = "file not found"; 54 char Etoosmall[] = "parameter too small"; 55 char Eio[] = "i/o error"; 56 char Eperm[] = "permission denied"; 57 char Ebadcall[] = "unknown fs call"; 58 char Ebadfid[] = "fid not found"; 59 char Einuse[] = "fid already in use"; 60 char Eisopen[] = "it is already open"; 61 char Ebadctl[] = "unknown control request"; 62 63 static char *user; 64 static ulong epoch; 65 static ulong msgsize = Msgsize; 66 static int fsfd = -1; 67 static Channel *outc; /* of Rpc* */ 68 69 static QLock rpclck; /* protect vars in this block */ 70 static Fid *freefids; 71 static Fid *fids; 72 static Rpc *freerpcs; 73 static Rpc *rpcs; 74 75 static Channel*procc; 76 static Channel*endc; 77 78 static Usbfs* fsops; 79 80 static void fsioproc(void*); 81 82 static void 83 schedproc(void*) 84 { 85 Channel *proc[Nproc]; 86 int nproc; 87 Channel *p; 88 89 Alt a[] = 90 { 91 {procc, &proc[0], CHANSND}, 92 {endc, &p, CHANRCV}, 93 {nil, nil, CHANEND} 94 }; 95 memset(proc, 0, sizeof(proc)); 96 nproc = 0; 97 for(;;){ 98 if(nproc == 0){ 99 proc[0] = chancreate(sizeof(Rpc*), 0); 100 proccreate(fsioproc, proc[0], Stack); 101 nproc++; 102 } 103 switch(alt(a)){ 104 case 0: 105 proc[0] = nil; 106 if(nproc > 1){ 107 proc[0] = proc[nproc-1]; 108 proc[nproc-1] = nil; 109 } 110 nproc--; 111 break; 112 case 1: 113 if(nproc < nelem(proc)) 114 proc[nproc++] = p; 115 else 116 sendp(p, nil); 117 break; 118 default: 119 sysfatal("alt"); 120 } 121 } 122 } 123 124 static void 125 dump(void) 126 { 127 Rpc *rpc; 128 Fid *fid; 129 130 qlock(&rpclck); 131 fprint(2, "dump:\n"); 132 for(rpc = rpcs; rpc != nil; rpc = rpc->next) 133 fprint(2, "rpc %#p %F next %#p\n", rpc, &rpc->t, rpc->next); 134 for(fid = fids; fid != nil; fid = fid->next) 135 fprint(2, "fid %d qid %#llux omode %d aux %#p\n", 136 fid->fid, fid->qid.path, fid->omode, fid->aux); 137 fprint(2, "\n"); 138 qunlock(&rpclck); 139 } 140 141 static Rpc* 142 newrpc(void) 143 { 144 Rpc *r; 145 146 qlock(&rpclck); 147 r = freerpcs; 148 if(r != nil) 149 freerpcs = r->next; 150 else 151 r = emallocz(sizeof(Rpc), 0); 152 r->next = rpcs; 153 rpcs = r; 154 r->t.tag = r->r.tag = Notag; 155 r->t.fid = r->r.fid = Nofid; 156 r->t.type = r->r.type = 0; 157 r->flushed = 0; 158 r->fid = nil; 159 r->r.data = (char*)r->data; 160 qunlock(&rpclck); 161 return r; 162 } 163 164 static void 165 freerpc(Rpc *r) 166 { 167 Rpc **l; 168 if(r == nil) 169 return; 170 qlock(&rpclck); 171 for(l = &rpcs; *l != nil && *l != r; l = &(*l)->next) 172 ; 173 assert(*l == r); 174 *l = r->next; 175 r->next = freerpcs; 176 freerpcs = r; 177 r->t.type = 0; 178 r->t.tag = 0x77777777; 179 qunlock(&rpclck); 180 } 181 182 static void 183 flushrpc(int tag) 184 { 185 Rpc *r; 186 187 qlock(&rpclck); 188 for(r = rpcs; r != nil; r = r->next) 189 if(r->t.tag == tag){ 190 r->flushed = 1; 191 break; 192 } 193 qunlock(&rpclck); 194 } 195 196 static Fid* 197 getfid(int fid, int alloc) 198 { 199 Fid *f; 200 201 qlock(&rpclck); 202 for(f = fids; f != nil && f->fid != fid; f = f->next) 203 ; 204 if(f != nil && alloc != 0){ /* fid in use */ 205 qunlock(&rpclck); 206 return nil; 207 } 208 if(f == nil && alloc != 0){ 209 if(freefids != nil){ 210 f = freefids; 211 freefids = freefids->next; 212 }else 213 f = emallocz(sizeof(Fid), 1); 214 f->fid = fid; 215 f->aux = nil; 216 f->omode = ONONE; 217 f->next = fids; 218 fids = f; 219 } 220 qunlock(&rpclck); 221 return f; 222 } 223 224 static void 225 freefid(Fid *f) 226 { 227 Fid **l; 228 229 if(f == nil) 230 return; 231 if(fsops->clunk != nil) 232 fsops->clunk(fsops, f); 233 qlock(&rpclck); 234 for(l = &fids; *l != nil && *l != f; l = &(*l)->next) 235 ; 236 assert(*l == f); 237 *l = f->next; 238 f->next = freefids; 239 freefids = f; 240 qunlock(&rpclck); 241 } 242 243 static Rpc* 244 fserror(Rpc *rpc, char* fmt, ...) 245 { 246 va_list arg; 247 char *c; 248 249 va_start(arg, fmt); 250 c = (char*)rpc->data; 251 vseprint(c, c+sizeof(rpc->data), fmt, arg); 252 va_end(arg); 253 rpc->r.type = Rerror; 254 rpc->r.ename = (char*)rpc->data; 255 return rpc; 256 } 257 258 static Rpc* 259 fsversion(Rpc *r) 260 { 261 if(r->t.msize < 256) 262 return fserror(r, Etoosmall); 263 if(strncmp(r->t.version, "9P2000", 6) != 0) 264 return fserror(r, "wrong version"); 265 if(r->t.msize < msgsize) 266 msgsize = r->t.msize; 267 r->r.msize = msgsize; 268 r->r.version = "9P2000"; 269 return r; 270 } 271 272 static Rpc* 273 fsattach(Rpc *r) 274 { 275 static int already; 276 277 /* Reload user because at boot it could be still none */ 278 user=getuser(); 279 if(already++ > 0 && strcmp(r->t.uname, user) != 0) 280 return fserror(r, Eperm); 281 if(r->fid == nil) 282 return fserror(r, Einuse); 283 284 r->r.qid.type = QTDIR; 285 r->r.qid.path = fsops->qid; 286 r->r.qid.vers = 0; 287 r->fid->qid = r->r.qid; 288 return r; 289 } 290 291 static Rpc* 292 fswalk(Rpc *r) 293 { 294 int i; 295 Fid *nfid, *ofid; 296 297 if(r->fid->omode != ONONE) 298 return fserror(r, Eisopen); 299 300 nfid = nil; 301 ofid = r->fid; 302 if(r->t.newfid != r->t.fid){ 303 nfid = getfid(r->t.newfid, 1); 304 if(nfid == nil) 305 return fserror(r, Einuse); 306 nfid->qid = r->fid->qid; 307 if(fsops->clone != nil) 308 fsops->clone(fsops, ofid, nfid); 309 else 310 nfid->aux = r->fid->aux; 311 r->fid = nfid; 312 } 313 r->r.nwqid = 0; 314 for(i = 0; i < r->t.nwname; i++) 315 if(fsops->walk(fsops, r->fid, r->t.wname[i]) < 0) 316 break; 317 else 318 r->r.wqid[i] = r->fid->qid; 319 r->r.nwqid = i; 320 if(i != r->t.nwname && r->t.nwname > 0){ 321 if(nfid != nil) 322 freefid(nfid); 323 r->fid = ofid; 324 } 325 if(i == 0 && r->t.nwname > 0) 326 return fserror(r, "%r"); 327 return r; 328 } 329 330 static void 331 fsioproc(void* a) 332 { 333 long rc; 334 Channel *p = a; 335 Rpc *rpc; 336 Fcall *t, *r; 337 Fid *fid; 338 339 dprint(2, "%s: fsioproc pid %d\n", argv0, getpid()); 340 while((rpc = recvp(p)) != nil){ 341 t = &rpc->t; 342 r = &rpc->r; 343 fid = rpc->fid; 344 rc = -1; 345 dprint(2, "%s: fsioproc pid %d: req %d\n", argv0, getpid(), t->type); 346 switch(t->type){ 347 case Topen: 348 rc = fsops->open(fsops, fid, t->mode); 349 if(rc >= 0){ 350 r->iounit = 0; 351 r->qid = fid->qid; 352 fid->omode = t->mode & 3; 353 } 354 break; 355 case Tread: 356 rc = fsops->read(fsops, fid, r->data, t->count, t->offset); 357 if(rc >= 0){ 358 if(rc > t->count) 359 print("%s: bug: read %ld bytes > %ud wanted\n", 360 argv0, rc, t->count); 361 r->count = rc; 362 } 363 /* 364 * TODO: if we encounter a long run of continuous read 365 * errors, we should do something more drastic so that 366 * our caller doesn't just spin its wheels forever. 367 */ 368 break; 369 case Twrite: 370 rc = fsops->write(fsops, fid, t->data, t->count, t->offset); 371 r->count = rc; 372 break; 373 default: 374 sysfatal("fsioproc: bad type"); 375 } 376 if(rc < 0) 377 sendp(outc, fserror(rpc, "%r")); 378 else 379 sendp(outc, rpc); 380 sendp(endc, p); 381 } 382 chanfree(p); 383 dprint(2, "%s: fsioproc %d exiting\n", argv0, getpid()); 384 threadexits(nil); 385 } 386 387 static Rpc* 388 fsopen(Rpc *r) 389 { 390 Channel *p; 391 392 if(r->fid->omode != ONONE) 393 return fserror(r, Eisopen); 394 if((r->t.mode & 3) != OREAD && (r->fid->qid.type & QTDIR) != 0) 395 return fserror(r, Eperm); 396 p = recvp(procc); 397 sendp(p, r); 398 return nil; 399 } 400 401 int 402 usbdirread(Usbfs*f, Qid q, char *data, long cnt, vlong off, Dirgen gen, void *arg) 403 { 404 int i, n, nd; 405 char name[Namesz]; 406 Dir d; 407 408 memset(&d, 0, sizeof(d)); 409 d.name = name; 410 d.uid = d.gid = d.muid = user; 411 d.atime = time(nil); 412 d.mtime = epoch; 413 d.length = 0; 414 for(i = n = 0; gen(f, q, i, &d, arg) >= 0; i++){ 415 if(usbfsdebug > 1) 416 fprint(2, "%s: dir %d q %#llux: %D\n", argv0, i, q.path, &d); 417 nd = convD2M(&d, (uchar*)data+n, cnt-n); 418 if(nd <= BIT16SZ) 419 break; 420 if(off > 0) 421 off -= nd; 422 else 423 n += nd; 424 d.name = name; 425 d.uid = d.gid = d.muid = user; 426 d.atime = time(nil); 427 d.mtime = epoch; 428 d.length = 0; 429 } 430 return n; 431 } 432 433 long 434 usbreadbuf(void *data, long count, vlong offset, void *buf, long n) 435 { 436 if(offset >= n) 437 return 0; 438 if(offset + count > n) 439 count = n - offset; 440 memmove(data, (char*)buf + offset, count); 441 return count; 442 } 443 444 static Rpc* 445 fsread(Rpc *r) 446 { 447 Channel *p; 448 449 if(r->fid->omode != OREAD && r->fid->omode != ORDWR) 450 return fserror(r, Eperm); 451 p = recvp(procc); 452 sendp(p, r); 453 return nil; 454 } 455 456 static Rpc* 457 fswrite(Rpc *r) 458 { 459 Channel *p; 460 461 if(r->fid->omode != OWRITE && r->fid->omode != ORDWR) 462 return fserror(r, Eperm); 463 p = recvp(procc); 464 sendp(p, r); 465 return nil; 466 } 467 468 static Rpc* 469 fsclunk(Rpc *r) 470 { 471 freefid(r->fid); 472 return r; 473 } 474 475 static Rpc* 476 fsno(Rpc *r) 477 { 478 return fserror(r, Eperm); 479 } 480 481 static Rpc* 482 fsstat(Rpc *r) 483 { 484 Dir d; 485 char name[Namesz]; 486 487 memset(&d, 0, sizeof(d)); 488 d.name = name; 489 d.uid = d.gid = d.muid = user; 490 d.atime = time(nil); 491 d.mtime = epoch; 492 d.length = 0; 493 if(fsops->stat(fsops, r->fid->qid, &d) < 0) 494 return fserror(r, "%r"); 495 r->r.stat = (uchar*)r->data; 496 r->r.nstat = convD2M(&d, (uchar*)r->data, msgsize); 497 return r; 498 } 499 500 static Rpc* 501 fsflush(Rpc *r) 502 { 503 /* 504 * Flag it as flushed and respond. 505 * Either outproc will reply to the flushed request 506 * before responding to flush, or it will never reply to it. 507 * Note that we do NOT abort the ongoing I/O. 508 * That might leave the affected endpoints in a failed 509 * state. Instead, we pretend the request is aborted. 510 * 511 * Only open, read, and write are processed 512 * by auxiliary processes and other requests wil never be 513 * flushed in practice. 514 */ 515 flushrpc(r->t.oldtag); 516 return r; 517 } 518 519 Rpc* (*fscalls[])(Rpc*) = { 520 [Tversion] fsversion, 521 [Tauth] fsno, 522 [Tattach] fsattach, 523 [Twalk] fswalk, 524 [Topen] fsopen, 525 [Tcreate] fsno, 526 [Tread] fsread, 527 [Twrite] fswrite, 528 [Tclunk] fsclunk, 529 [Tremove] fsno, 530 [Tstat] fsstat, 531 [Twstat] fsno, 532 [Tflush] fsflush, 533 }; 534 535 static void 536 outproc(void*) 537 { 538 static uchar buf[Bufsize]; 539 Rpc *rpc; 540 int nw; 541 static int once = 0; 542 543 if(once++ != 0) 544 sysfatal("more than one outproc"); 545 for(;;){ 546 do 547 rpc = recvp(outc); 548 while(rpc == nil); /* a delayed reply */ 549 if(rpc->t.tag == Dietag) 550 break; 551 if(rpc->flushed){ 552 dprint(2, "outproc: tag %d flushed\n", rpc->t.tag); 553 freerpc(rpc); 554 continue; 555 } 556 dprint(2, "-> %F\n", &rpc->r); 557 nw = convS2M(&rpc->r, buf, sizeof(buf)); 558 if(nw == sizeof(buf)) 559 fprint(2, "%s: outproc: buffer is too small\n", argv0); 560 if(nw <= BIT16SZ) 561 fprint(2, "%s: conS2M failed\n", argv0); 562 else if(write(fsfd, buf, nw) != nw){ 563 fprint(2, "%s: outproc: write: %r", argv0); 564 /* continue and let the reader abort us */ 565 } 566 if(usbfsdebug > 1) 567 dump(); 568 freerpc(rpc); 569 } 570 dprint(2, "%s: outproc: exiting\n", argv0); 571 } 572 573 static void 574 usbfs(void*) 575 { 576 Rpc *rpc; 577 int nr; 578 static int once = 0; 579 580 if(once++ != 0) 581 sysfatal("more than one usbfs proc"); 582 583 outc = chancreate(sizeof(Rpc*), 1); 584 procc = chancreate(sizeof(Channel*), 0); 585 endc = chancreate(sizeof(Channel*), 0); 586 if(outc == nil || procc == nil || endc == nil) 587 sysfatal("chancreate: %r"); 588 threadcreate(schedproc, nil, Stack); 589 proccreate(outproc, nil, Stack); 590 for(;;){ 591 rpc = newrpc(); 592 do{ 593 nr = read9pmsg(fsfd, rpc->data, sizeof(rpc->data)); 594 }while(nr == 0); 595 if(nr < 0){ 596 dprint(2, "%s: usbfs: read: '%r'", argv0); 597 if(fsops->end != nil) 598 fsops->end(fsops); 599 else 600 closedev(fsops->dev); 601 rpc->t.tag = Dietag; 602 sendp(outc, rpc); 603 break; 604 } 605 if(convM2S((uchar*)rpc->data, nr, &rpc->t) <=0){ 606 dprint(2, "%s: convM2S failed\n", argv0); 607 freerpc(rpc); 608 continue; 609 } 610 dprint(2, "<- %F\n", &rpc->t); 611 rpc->r.tag = rpc->t.tag; 612 rpc->r.type = rpc->t.type + 1; 613 rpc->r.fid = rpc->t.fid; 614 if(fscalls[rpc->t.type] == nil){ 615 sendp(outc, fserror(rpc, Ebadcall)); 616 continue; 617 } 618 if(rpc->t.fid != Nofid){ 619 if(rpc->t.type == Tattach) 620 rpc->fid = getfid(rpc->t.fid, 1); 621 else 622 rpc->fid = getfid(rpc->t.fid, 0); 623 if(rpc->fid == nil){ 624 sendp(outc, fserror(rpc, Ebadfid)); 625 continue; 626 } 627 } 628 sendp(outc, fscalls[rpc->t.type](rpc)); 629 } 630 dprint(2, "%s: ubfs: eof: exiting\n", argv0); 631 } 632 633 void 634 usbfsinit(char* srv, char *mnt, Usbfs *f, int flag) 635 { 636 int fd[2]; 637 int sfd; 638 int afd; 639 char sfile[40]; 640 641 fsops = f; 642 if(pipe(fd) < 0) 643 sysfatal("pipe: %r"); 644 user = getuser(); 645 epoch = time(nil); 646 647 fmtinstall('D', dirfmt); 648 fmtinstall('M', dirmodefmt); 649 fmtinstall('F', fcallfmt); 650 fsfd = fd[1]; 651 procrfork(usbfs, nil, Stack, RFNAMEG); /* no RFFDG */ 652 if(srv != nil){ 653 snprint(sfile, sizeof(sfile), "#s/%s", srv); 654 remove(sfile); 655 sfd = create(sfile, OWRITE, 0660); 656 if(sfd < 0) 657 sysfatal("post: %r"); 658 snprint(sfile, sizeof(sfile), "%d", fd[0]); 659 if(write(sfd, sfile, strlen(sfile)) != strlen(sfile)) 660 sysfatal("post: %r"); 661 close(sfd); 662 } 663 if(mnt != nil){ 664 sfd = dup(fd[0], -1); /* debug */ 665 afd = fauth(sfd, ""); 666 if(afd >= 0) 667 sysfatal("authentication required??"); 668 if(mount(sfd, -1, mnt, flag, "") < 0) 669 sysfatal("mount: %r"); 670 } 671 close(fd[0]); 672 } 673 674