1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include <thread.h> 6 #include <9p.h> 7 #include <disk.h> 8 #include "dat.h" 9 #include "fns.h" 10 11 typedef struct Aux Aux; 12 struct Aux { 13 int doff; 14 Otrack *o; 15 }; 16 17 static void checktoc(Drive*); 18 19 int vflag; 20 21 Drive *drive; 22 int nchange; 23 24 enum { 25 Qdir = 0, 26 Qctl = 1, 27 Qwa = 2, 28 Qwd = 3, 29 Qtrack = 4, 30 }; 31 32 char* 33 geterrstr(void) 34 { 35 static char errbuf[ERRMAX]; 36 37 rerrstr(errbuf, sizeof errbuf); 38 return errbuf; 39 } 40 41 void* 42 emalloc(ulong sz) 43 { 44 void *v; 45 46 v = malloc(sz); 47 if(v == nil) 48 sysfatal("malloc %lud fails\n", sz); 49 memset(v, 0, sz); 50 return v; 51 } 52 53 static void 54 fsattach(Req *r) 55 { 56 char *spec; 57 58 spec = r->ifcall.aname; 59 if(spec && spec[0]) { 60 respond(r, "invalid attach specifier"); 61 return; 62 } 63 64 checktoc(drive); 65 r->fid->qid = (Qid){Qdir, drive->nchange, QTDIR}; 66 r->ofcall.qid = r->fid->qid; 67 r->fid->aux = emalloc(sizeof(Aux)); 68 respond(r, nil); 69 } 70 71 static char* 72 fsclone(Fid *old, Fid *new) 73 { 74 Aux *na; 75 76 na = emalloc(sizeof(Aux)); 77 *na = *((Aux*)old->aux); 78 if(na->o) 79 na->o->nref++; 80 new->aux = na; 81 return nil; 82 } 83 84 static char* 85 fswalk1(Fid *fid, char *name, Qid *qid) 86 { 87 int i; 88 89 checktoc(drive); 90 switch((ulong)fid->qid.path) { 91 case Qdir: 92 if(strcmp(name, "..") == 0) { 93 *qid = (Qid){Qdir, drive->nchange, QTDIR}; 94 return nil; 95 } 96 if(strcmp(name, "ctl") == 0) { 97 *qid = (Qid){Qctl, 0, 0}; 98 return nil; 99 } 100 if(strcmp(name, "wa") == 0 && drive->writeok) { 101 *qid = (Qid){Qwa, drive->nchange, QTDIR}; 102 return nil; 103 } 104 if(strcmp(name, "wd") == 0 && drive->writeok) { 105 *qid = (Qid){Qwd, drive->nchange, QTDIR}; 106 return nil; 107 } 108 for(i=0; i<drive->ntrack; i++) 109 if(strcmp(drive->track[i].name, name) == 0) 110 break; 111 if(i == drive->ntrack) { 112 return "file not found"; 113 } 114 *qid = (Qid){Qtrack+i, 0, 0}; 115 return nil; 116 117 case Qwa: 118 case Qwd: 119 if(strcmp(name, "..") == 0) { 120 *qid = (Qid){Qdir, drive->nchange, QTDIR}; 121 return nil; 122 } 123 return "file not found"; 124 default: /* bug: lib9p could handle this */ 125 return "walk in non-directory"; 126 } 127 } 128 129 static void 130 fscreate(Req *r) 131 { 132 int omode, type; 133 Otrack *o; 134 Fid *fid; 135 136 fid = r->fid; 137 omode = r->ifcall.mode; 138 139 if(omode != OWRITE) { 140 respond(r, "bad mode (use OWRITE)"); 141 return; 142 } 143 144 switch((ulong)fid->qid.path) { 145 case Qdir: 146 default: 147 respond(r, "permission denied"); 148 return; 149 150 case Qwa: 151 type = TypeAudio; 152 break; 153 154 case Qwd: 155 type = TypeData; 156 break; 157 } 158 159 if((drive->cap & Cwrite) == 0) { 160 respond(r, "drive does not write"); 161 return; 162 } 163 164 o = drive->create(drive, type); 165 if(o == nil) { 166 respond(r, geterrstr()); 167 return; 168 } 169 drive->nchange = -1; 170 checktoc(drive); /* update directory info */ 171 o->nref = 1; 172 ((Aux*)fid->aux)->o = o; 173 174 fid->qid = (Qid){Qtrack+(o->track - drive->track), drive->nchange, 0}; 175 r->ofcall.qid = fid->qid; 176 respond(r, nil); 177 } 178 179 static void 180 fsremove(Req *r) 181 { 182 switch((ulong)r->fid->qid.path){ 183 case Qwa: 184 case Qwd: 185 if(drive->fixate(drive) < 0) 186 respond(r, geterrstr()); 187 // let's see if it can figure this out drive->writeok = 0; 188 else 189 respond(r, nil); 190 checktoc(drive); 191 break; 192 default: 193 respond(r, "permission denied"); 194 break; 195 } 196 } 197 198 int 199 fillstat(ulong qid, Dir *d) 200 { 201 Track *t; 202 203 memset(d, 0, sizeof(Dir)); 204 d->uid = "cd"; 205 d->gid = "cd"; 206 d->muid = ""; 207 d->qid = (Qid){qid, drive->nchange, 0}; 208 d->atime = time(0); 209 d->atime = drive->changetime; 210 211 switch(qid){ 212 case Qdir: 213 d->name = "/"; 214 d->qid.type = QTDIR; 215 d->mode = DMDIR|0777; 216 break; 217 218 case Qctl: 219 d->name = "ctl"; 220 d->mode = 0666; 221 break; 222 223 case Qwa: 224 if(drive->writeok == 0) 225 return 0; 226 d->name = "wa"; 227 d->qid.type = QTDIR; 228 d->mode = DMDIR|0777; 229 break; 230 231 case Qwd: 232 if(drive->writeok == 0) 233 return 0; 234 d->name = "wd"; 235 d->qid.type = QTDIR; 236 d->mode = DMDIR|0777; 237 break; 238 239 default: 240 if(qid-Qtrack >= drive->ntrack) 241 return 0; 242 t = &drive->track[qid-Qtrack]; 243 if(strcmp(t->name, "") == 0) 244 return 0; 245 d->name = t->name; 246 d->mode = t->mode; 247 d->length = t->size; 248 break; 249 } 250 return 1; 251 } 252 253 static ulong 254 cddb_sum(int n) 255 { 256 int ret; 257 ret = 0; 258 while(n > 0) { 259 ret += n%10; 260 n /= 10; 261 } 262 return ret; 263 } 264 265 static ulong 266 diskid(Drive *d) 267 { 268 int i, n; 269 ulong tmp; 270 Msf *ms, *me; 271 272 n = 0; 273 for(i=0; i < d->ntrack; i++) 274 n += cddb_sum(d->track[i].mbeg.m*60+d->track[i].mbeg.s); 275 276 ms = &d->track[0].mbeg; 277 me = &d->track[d->ntrack].mbeg; 278 tmp = (me->m*60+me->s) - (ms->m*60+ms->s); 279 280 /* 281 * the spec says n%0xFF rather than n&0xFF. it's unclear which is correct. 282 * most CDs are in the database under both entries. 283 */ 284 return ((n % 0xFF) << 24 | (tmp << 8) | d->ntrack); 285 } 286 287 static void 288 readctl(Req *r) 289 { 290 int i, isaudio; 291 char s[1024]; 292 Msf *m; 293 294 strcpy(s, ""); 295 296 isaudio = 0; 297 for(i=0; i<drive->ntrack; i++) 298 if(drive->track[i].type == TypeAudio) 299 isaudio = 1; 300 301 if(isaudio){ 302 sprint(s, "aux/cddb query %8.8lux %d", diskid(drive), drive->ntrack); 303 for(i=0; i<drive->ntrack; i++){ 304 m = &drive->track[i].mbeg; 305 sprint(s+strlen(s), " %d", (m->m*60+m->s)*75+m->f); 306 } 307 m = &drive->track[drive->ntrack].mbeg; 308 sprint(s+strlen(s), " %d\n", m->m*60+m->s); 309 } 310 311 if(drive->readspeed == drive->writespeed) 312 sprint(s+strlen(s), "speed %d\n", drive->readspeed); 313 else 314 sprint(s+strlen(s), "speed read %d write %d\n", drive->readspeed, drive->writespeed); 315 sprint(s+strlen(s), "maxspeed read %d write %d\n", drive->maxreadspeed, drive->maxwritespeed); 316 readstr(r, s); 317 } 318 319 static void 320 fsread(Req *r) 321 { 322 int j, n, m; 323 uchar *p, *ep; 324 Dir d; 325 Fid *fid; 326 Otrack *o; 327 vlong offset; 328 void *buf; 329 long count; 330 Aux *a; 331 332 fid = r->fid; 333 offset = r->ifcall.offset; 334 buf = r->ofcall.data; 335 count = r->ifcall.count; 336 337 switch((ulong)fid->qid.path) { 338 case Qdir: 339 checktoc(drive); 340 p = buf; 341 ep = p+count; 342 m = Qtrack+drive->ntrack; 343 a = fid->aux; 344 if(offset == 0) 345 a->doff = 1; /* skip root */ 346 347 for(j=a->doff; j<m; j++) { 348 if(fillstat(j, &d)) { 349 if((n = convD2M(&d, p, ep-p)) <= BIT16SZ) 350 break; 351 p += n; 352 } 353 } 354 a->doff = j; 355 356 r->ofcall.count = p-(uchar*)buf; 357 respond(r, nil); 358 return; 359 360 case Qwa: 361 case Qwd: 362 r->ofcall.count = 0; 363 respond(r, nil); 364 return; 365 366 case Qctl: 367 readctl(r); 368 respond(r, nil); 369 return; 370 } 371 372 /* a disk track; we can only call read for whole blocks */ 373 o = ((Aux*)fid->aux)->o; 374 if((count = o->drive->read(o, buf, count, offset)) < 0) 375 respond(r, geterrstr()); 376 else{ 377 r->ofcall.count = count; 378 respond(r, nil); 379 } 380 return; 381 } 382 383 static char *Ebadmsg = "bad cdfs control message"; 384 static char* 385 writectl(void *v, long count) 386 { 387 char buf[256]; 388 char *f[10], *p; 389 int i, nf, n, r, w, what; 390 391 if(count >= sizeof(buf)) 392 count = sizeof(buf)-1; 393 memmove(buf, v, count); 394 buf[count] = '\0'; 395 396 nf = tokenize(buf, f, nelem(f)); 397 if(nf == 0) 398 return Ebadmsg; 399 400 if(strcmp(f[0], "speed") == 0){ 401 what = 0; 402 r = w = -1; 403 if(nf == 1) 404 return Ebadmsg; 405 for(i=1; i<nf; i++){ 406 if(strcmp(f[i], "read") == 0 || strcmp(f[i], "write") == 0){ 407 if(what!=0 && what!='?') 408 return Ebadmsg; 409 what = f[i][0]; 410 }else{ 411 n = strtol(f[i], &p, 0); 412 if(*p != '\0' || n <= 0) 413 return Ebadmsg; 414 switch(what){ 415 case 0: 416 if(r >= 0 || w >= 0) 417 return Ebadmsg; 418 r = w = n; 419 what = '?'; 420 break; 421 case 'r': 422 if(r >= 0) 423 return Ebadmsg; 424 r = n; 425 what = '?'; 426 break; 427 case 'w': 428 if(w >= 0) 429 return Ebadmsg; 430 w = n; 431 what = '?'; 432 break; 433 default: 434 return Ebadmsg; 435 } 436 } 437 } 438 if(what != '?') 439 return Ebadmsg; 440 return drive->setspeed(drive, r, w); 441 } 442 return drive->ctl(drive, nf, f); 443 } 444 445 static void 446 fswrite(Req *r) 447 { 448 Otrack *o; 449 Fid *fid; 450 451 fid = r->fid; 452 r->ofcall.count = r->ifcall.count; 453 if(fid->qid.path == Qctl) { 454 respond(r, writectl(r->ifcall.data, r->ifcall.count)); 455 return; 456 } 457 458 if((o = ((Aux*)fid->aux)->o) == nil || o->omode != OWRITE) { 459 respond(r, "permission denied"); 460 return; 461 } 462 463 if(o->drive->write(o, r->ifcall.data, r->ifcall.count) < 0) 464 respond(r, geterrstr()); 465 else 466 respond(r, nil); 467 } 468 469 static void 470 fsstat(Req *r) 471 { 472 fillstat((ulong)r->fid->qid.path, &r->d); 473 r->d.name = estrdup9p(r->d.name); 474 r->d.uid = estrdup9p(r->d.uid); 475 r->d.gid = estrdup9p(r->d.gid); 476 r->d.muid = estrdup9p(r->d.muid); 477 respond(r, nil); 478 } 479 480 static void 481 fsopen(Req *r) 482 { 483 int omode; 484 Fid *fid; 485 Otrack *o; 486 487 fid = r->fid; 488 omode = r->ifcall.mode; 489 checktoc(drive); 490 r->ofcall.qid = (Qid){fid->qid.path, drive->nchange, fid->qid.vers}; 491 492 switch((ulong)fid->qid.path){ 493 case Qdir: 494 case Qwa: 495 case Qwd: 496 if(omode == OREAD) 497 respond(r, nil); 498 else 499 respond(r, "permission denied"); 500 return; 501 502 case Qctl: 503 if(omode&~(OTRUNC|OREAD|OWRITE|ORDWR)) 504 respond(r, "permission denied"); 505 else 506 respond(r, nil); 507 return; 508 509 default: 510 if(fid->qid.path >= Qtrack+drive->ntrack) { 511 respond(r, "file no longer exists"); 512 return; 513 } 514 515 if(omode != OREAD || (o = drive->openrd(drive, fid->qid.path-Qtrack)) == nil) { 516 respond(r, "permission denied"); 517 return; 518 } 519 520 o->nref = 1; 521 ((Aux*)fid->aux)->o = o; 522 respond(r, nil); 523 } 524 } 525 526 static uchar zero[BScdda]; 527 528 static void 529 fsdestroyfid(Fid *fid) 530 { 531 Aux *aux; 532 Otrack *o; 533 534 aux = fid->aux; 535 if(aux == nil) 536 return; 537 o = aux->o; 538 if(o && --o->nref == 0) { 539 bterm(o->buf); 540 drive->close(o); 541 checktoc(drive); 542 } 543 } 544 545 static void 546 checktoc(Drive *drive) 547 { 548 int i; 549 Track *t; 550 551 drive->gettoc(drive); 552 if(drive->nameok) 553 return; 554 555 for(i=0; i<drive->ntrack; i++) { 556 t = &drive->track[i]; 557 if(t->size == 0) /* being created */ 558 t->mode = 0; 559 else 560 t->mode = 0444; 561 sprint(t->name, "?%.3d", i); 562 switch(t->type){ 563 case TypeNone: 564 t->name[0] = 'u'; 565 t->mode = 0; 566 break; 567 case TypeData: 568 t->name[0] = 'd'; 569 break; 570 case TypeAudio: 571 t->name[0] = 'a'; 572 break; 573 case TypeBlank: 574 t->name[0] = '\0'; 575 break; 576 default: 577 print("unknown type %d\n", t->type); 578 break; 579 } 580 } 581 582 drive->nameok = 1; 583 } 584 585 long 586 bufread(Otrack *t, void *v, long n, long off) 587 { 588 return bread(t->buf, v, n, off); 589 } 590 591 long 592 bufwrite(Otrack *t, void *v, long n) 593 { 594 return bwrite(t->buf, v, n); 595 } 596 597 Srv fs = { 598 .attach= fsattach, 599 .destroyfid= fsdestroyfid, 600 .clone= fsclone, 601 .walk1= fswalk1, 602 .open= fsopen, 603 .read= fsread, 604 .write= fswrite, 605 .create= fscreate, 606 .remove= fsremove, 607 .stat= fsstat, 608 }; 609 610 void 611 usage(void) 612 { 613 fprint(2, "usage: cdfs [-Dv] [-d /dev/sdC0] [-m mtpt]\n"); 614 exits("usage"); 615 } 616 617 void 618 main(int argc, char **argv) 619 { 620 Scsi *s; 621 int fd; 622 char *dev, *mtpt; 623 624 dev = "/dev/sdD0"; 625 mtpt = "/mnt/cd"; 626 627 ARGBEGIN{ 628 case 'D': 629 chatty9p++; 630 break; 631 case 'd': 632 dev = ARGF(); 633 break; 634 case 'm': 635 mtpt = ARGF(); 636 break; 637 case 'v': 638 if((fd = create("/tmp/cdfs.log", OWRITE, 0666)) >= 0) { 639 dup(fd, 2); 640 dup(fd, 1); 641 if(fd != 1 && fd != 2) 642 close(fd); 643 vflag++; 644 scsiverbose++; 645 } 646 break; 647 default: 648 usage(); 649 }ARGEND 650 651 if(dev == nil || mtpt == nil || argc > 0) 652 usage(); 653 654 if((s = openscsi(dev)) == nil) { 655 fprint(2, "openscsi '%s': %r\n", dev); 656 exits("openscsi"); 657 } 658 659 if((drive = mmcprobe(s)) == nil) { 660 fprint(2, "mmcprobe '%s': %r\n", dev); 661 exits("mmcprobe"); 662 } 663 664 checktoc(drive); 665 666 postmountsrv(&fs, nil, mtpt, MREPL|MCREATE); 667 exits(nil); 668 } 669