main.c 11 KB

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