1 /* cdfs - CD, DVD and BD reader and writer file system */
12 typedef struct Aux Aux;
18 ulong getnwa(Drive *);
20 static void checktoc(Drive*);
38 static char errbuf[ERRMAX];
40 rerrstr(errbuf, sizeof errbuf);
51 sysfatal("malloc %lud fails", sz);
60 spec = r->ifcall.aname;
62 respond(r, "invalid attach specifier");
67 r->fid->qid = (Qid){Qdir, drive->nchange, QTDIR};
68 r->ofcall.qid = r->fid->qid;
69 r->fid->aux = emalloc(sizeof(Aux));
74 fsclone(Fid *old, Fid *new)
78 na = emalloc(sizeof(Aux));
79 *na = *((Aux*)old->aux);
87 fswalk1(Fid *fid, char *name, Qid *qid)
92 switch((ulong)fid->qid.path) {
94 if(strcmp(name, "..") == 0) {
95 *qid = (Qid){Qdir, drive->nchange, QTDIR};
98 if(strcmp(name, "ctl") == 0) {
99 *qid = (Qid){Qctl, 0, 0};
102 if(strcmp(name, "wa") == 0 && drive->writeok &&
103 (drive->mmctype == Mmcnone ||
104 drive->mmctype == Mmccd)) {
105 *qid = (Qid){Qwa, drive->nchange, QTDIR};
108 if(strcmp(name, "wd") == 0 && drive->writeok) {
109 *qid = (Qid){Qwd, drive->nchange, QTDIR};
112 for(i=0; i<drive->ntrack; i++)
113 if(strcmp(drive->track[i].name, name) == 0)
115 if(i == drive->ntrack)
116 return "file not found";
117 *qid = (Qid){Qtrack+i, 0, 0};
122 if(strcmp(name, "..") == 0) {
123 *qid = (Qid){Qdir, drive->nchange, QTDIR};
126 return "file not found";
127 default: /* bug: lib9p could handle this */
128 return "walk in non-directory";
140 omode = r->ifcall.mode;
142 if(omode != OWRITE) {
143 respond(r, "bad mode (use OWRITE)");
147 switch((ulong)fid->qid.path) {
150 respond(r, "permission denied");
154 if (drive->mmctype != Mmcnone &&
155 drive->mmctype != Mmccd) {
156 respond(r, "audio supported only on cd");
167 if((drive->cap & Cwrite) == 0) {
168 respond(r, "drive does not write");
172 o = drive->create(drive, type);
174 respond(r, geterrstr());
178 checktoc(drive); /* update directory info */
180 ((Aux*)fid->aux)->o = o;
182 fid->qid = (Qid){Qtrack+(o->track - drive->track), drive->nchange, 0};
183 r->ofcall.qid = fid->qid;
190 switch((ulong)r->fid->qid.path){
193 if(drive->fixate(drive) < 0)
194 respond(r, geterrstr());
195 // let us see if it can figure this out: drive->writeok = No;
201 respond(r, "permission denied");
206 /* result is one word, so it can be used as a uid in Dir structs */
208 disctype(Drive *drive)
212 switch (drive->mmctype) {
218 type = drive->dvdtype;
227 type = "**GOK**"; /* traditional */
231 if (drive->mmctype != Mmcnone && drive->dvdtype == nil)
232 if (drive->erasable == Yes)
233 rw = drive->mmctype == Mmcbd? "re": "rw";
234 else if (drive->recordable == Yes)
238 return smprint("%s%s", type, rw);
242 fillstat(ulong qid, Dir *d)
252 ty = disctype(drive);
253 strncpy(buf, ty, sizeof buf);
255 d->uid = d->gid = buf;
257 d->qid = (Qid){qid, drive->nchange, 0};
259 d->mtime = drive->changetime;
265 d->mode = DMDIR|0777;
274 if(drive->writeok == No ||
275 drive->mmctype != Mmcnone &&
276 drive->mmctype != Mmccd)
280 d->mode = DMDIR|0777;
284 if(drive->writeok == No)
288 d->mode = DMDIR|0777;
292 if(qid-Qtrack >= drive->ntrack)
294 t = &drive->track[qid-Qtrack];
295 if(strcmp(t->name, "") == 0)
325 for(i=0; i < d->ntrack; i++)
326 n += cddb_sum(d->track[i].mbeg.m*60+d->track[i].mbeg.s);
328 ms = &d->track[0].mbeg;
329 me = &d->track[d->ntrack].mbeg;
330 tmp = (me->m*60+me->s) - (ms->m*60+ms->s);
333 * the spec says n%0xFF rather than n&0xFF. it's unclear which is
334 * correct. most CDs are in the database under both entries.
336 return ((n % 0xFF) << 24 | (tmp << 8) | d->ntrack);
349 for(i=0; i<drive->ntrack; i++)
350 if(drive->track[i].type == TypeAudio)
357 p = seprint(p, e, "aux/cddb query %8.8lux %d", diskid(drive),
359 for(i=0; i<drive->ntrack; i++){
360 m = &drive->track[i].mbeg;
361 p = seprint(p, e, " %d", (m->m*60 + m->s)*75 + m->f);
363 m = &drive->track[drive->ntrack].mbeg;
364 p = seprint(p, e, " %d\n", m->m*60 + m->s);
367 if(drive->readspeed == drive->writespeed)
368 p = seprint(p, e, "speed %d\n", drive->readspeed);
370 p = seprint(p, e, "speed read %d write %d\n",
371 drive->readspeed, drive->writespeed);
372 p = seprint(p, e, "maxspeed read %d write %d\n",
373 drive->maxreadspeed, drive->maxwritespeed);
375 if (drive->Scsi.changetime != 0 && drive->ntrack != 0) { /* have disc? */
376 ty = disctype(drive);
377 p = seprint(p, e, "%s", ty);
379 if (drive->mmctype != Mmcnone) {
381 p = seprint(p, e, " next writable sector ");
383 p = seprint(p, e, "none; disc full");
385 p = seprint(p, e, "%lud", nwa);
406 offset = r->ifcall.offset;
407 buf = r->ofcall.data;
408 count = r->ifcall.count;
410 switch((ulong)fid->qid.path) {
415 m = Qtrack+drive->ntrack;
418 a->doff = 1; /* skip root */
420 for(j=a->doff; j<m; j++) {
421 if(fillstat(j, &d)) {
422 if((n = convD2M(&d, p, ep-p)) <= BIT16SZ)
429 r->ofcall.count = p - (uchar*)buf;
439 /* a disk track; we can only call read for whole blocks */
440 o = ((Aux*)fid->aux)->o;
441 if((count = o->drive->read(o, buf, count, offset)) < 0) {
442 respond(r, geterrstr());
445 r->ofcall.count = count;
451 static char Ebadmsg[] = "bad cdfs control message";
454 writectl(void *v, long count)
458 int i, nf, n, r, w, what;
460 if(count >= sizeof(buf))
461 count = sizeof(buf)-1;
462 memmove(buf, v, count);
465 nf = tokenize(buf, f, nelem(f));
469 if(strcmp(f[0], "speed") == 0){
475 if(strcmp(f[i], "read") == 0 || strcmp(f[i], "write") == 0){
476 if(what!=0 && what!='?')
480 if (strcmp(f[i], "best") == 0)
483 n = strtol(f[i], &p, 0);
484 if(*p != '\0' || n <= 0)
511 return drive->setspeed(drive, r, w);
513 return drive->ctl(drive, nf, f);
523 r->ofcall.count = r->ifcall.count;
524 if(fid->qid.path == Qctl) {
525 respond(r, writectl(r->ifcall.data, r->ifcall.count));
529 if((o = ((Aux*)fid->aux)->o) == nil || o->omode != OWRITE) {
530 respond(r, "permission denied");
534 if(o->drive->write(o, r->ifcall.data, r->ifcall.count) < 0)
535 respond(r, geterrstr());
543 fillstat((ulong)r->fid->qid.path, &r->d);
544 r->d.name = estrdup9p(r->d.name);
545 r->d.uid = estrdup9p(r->d.uid);
546 r->d.gid = estrdup9p(r->d.gid);
547 r->d.muid = estrdup9p(r->d.muid);
559 omode = r->ifcall.mode;
561 r->ofcall.qid = (Qid){fid->qid.path, drive->nchange, fid->qid.vers};
563 switch((ulong)fid->qid.path){
568 respond(r, "permission denied");
573 if(omode & ~(OTRUNC|OREAD|OWRITE|ORDWR)) {
574 respond(r, "permission denied");
579 if(fid->qid.path >= Qtrack+drive->ntrack) {
580 respond(r, "file no longer exists");
585 * allow the open with OWRITE or ORDWR if the
586 * drive and disc are both capable?
589 (o = drive->openrd(drive, fid->qid.path-Qtrack)) == nil) {
590 respond(r, "permission denied");
595 ((Aux*)fid->aux)->o = o;
602 fsdestroyfid(Fid *fid)
611 if(o && --o->nref == 0) {
619 checktoc(Drive *drive)
624 drive->gettoc(drive);
628 for(i=0; i<drive->ntrack; i++) {
629 t = &drive->track[i];
630 if(t->size == 0) /* being created */
634 sprint(t->name, "?%.3d", i);
650 print("unknown track type %d\n", t->type);
659 bufread(Otrack *t, void *v, long n, vlong off)
661 return bread(t->buf, v, n, off);
665 bufwrite(Otrack *t, void *v, long n)
667 return bwrite(t->buf, v, n);
672 .destroyfid= fsdestroyfid,
686 fprint(2, "usage: cdfs [-Dv] [-d /dev/sdC0] [-m mtpt]\n");
691 main(int argc, char **argv)
705 dev = EARGF(usage());
708 mtpt = EARGF(usage());
711 if((fd = create("/tmp/cdfs.log", OWRITE, 0666)) >= 0) {
714 if(fd != 1 && fd != 2)
717 scsiverbose = 2; /* verbose but no Readtoc errs */
724 if(dev == nil || mtpt == nil || argc > 0)
728 if((s = openscsi(dev)) == nil)
729 sysfatal("openscsi '%s': %r", dev);
730 if((drive = mmcprobe(s)) == nil)
731 sysfatal("mmcprobe '%s': %r", dev);
734 postmountsrv(&fs, nil, mtpt, MREPL|MCREATE);