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 = 0;
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)
233 rw = drive->mmctype == Mmcbd? "re": "rw";
234 else if (drive->recordable)
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 == 0 ||
275 drive->mmctype != Mmcnone &&
276 drive->mmctype != Mmccd)
280 d->mode = DMDIR|0777;
284 if(drive->writeok == 0)
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);
348 for(i=0; i<drive->ntrack; i++)
349 if(drive->track[i].type == TypeAudio)
356 p = seprint(p, e, "aux/cddb query %8.8lux %d", diskid(drive),
358 for(i=0; i<drive->ntrack; i++){
359 m = &drive->track[i].mbeg;
360 p = seprint(p, e, " %d", (m->m*60 + m->s)*75 + m->f);
362 m = &drive->track[drive->ntrack].mbeg;
363 p = seprint(p, e, " %d\n", m->m*60 + m->s);
366 if(drive->readspeed == drive->writespeed)
367 p = seprint(p, e, "speed %d\n", drive->readspeed);
369 p = seprint(p, e, "speed read %d write %d\n",
370 drive->readspeed, drive->writespeed);
371 p = seprint(p, e, "maxspeed read %d write %d\n",
372 drive->maxreadspeed, drive->maxwritespeed);
374 if (drive->Scsi.changetime != 0 && drive->ntrack != 0) { /* have disc? */
375 ty = disctype(drive);
376 p = seprint(p, e, "%s", ty);
378 if (drive->mmctype != Mmcnone)
379 p = seprint(p, e, " next writable sector %lud",
400 offset = r->ifcall.offset;
401 buf = r->ofcall.data;
402 count = r->ifcall.count;
404 switch((ulong)fid->qid.path) {
409 m = Qtrack+drive->ntrack;
412 a->doff = 1; /* skip root */
414 for(j=a->doff; j<m; j++) {
415 if(fillstat(j, &d)) {
416 if((n = convD2M(&d, p, ep-p)) <= BIT16SZ)
423 r->ofcall.count = p - (uchar*)buf;
433 /* a disk track; we can only call read for whole blocks */
434 o = ((Aux*)fid->aux)->o;
435 if((count = o->drive->read(o, buf, count, offset)) < 0) {
436 respond(r, geterrstr());
439 r->ofcall.count = count;
445 static char Ebadmsg[] = "bad cdfs control message";
448 writectl(void *v, long count)
452 int i, nf, n, r, w, what;
454 if(count >= sizeof(buf))
455 count = sizeof(buf)-1;
456 memmove(buf, v, count);
459 nf = tokenize(buf, f, nelem(f));
463 if(strcmp(f[0], "speed") == 0){
469 if(strcmp(f[i], "read") == 0 || strcmp(f[i], "write") == 0){
470 if(what!=0 && what!='?')
474 if (strcmp(f[i], "best") == 0)
477 n = strtol(f[i], &p, 0);
478 if(*p != '\0' || n <= 0)
505 return drive->setspeed(drive, r, w);
507 return drive->ctl(drive, nf, f);
517 r->ofcall.count = r->ifcall.count;
518 if(fid->qid.path == Qctl) {
519 respond(r, writectl(r->ifcall.data, r->ifcall.count));
523 if((o = ((Aux*)fid->aux)->o) == nil || o->omode != OWRITE) {
524 respond(r, "permission denied");
528 if(o->drive->write(o, r->ifcall.data, r->ifcall.count) < 0)
529 respond(r, geterrstr());
537 fillstat((ulong)r->fid->qid.path, &r->d);
538 r->d.name = estrdup9p(r->d.name);
539 r->d.uid = estrdup9p(r->d.uid);
540 r->d.gid = estrdup9p(r->d.gid);
541 r->d.muid = estrdup9p(r->d.muid);
553 omode = r->ifcall.mode;
555 r->ofcall.qid = (Qid){fid->qid.path, drive->nchange, fid->qid.vers};
557 switch((ulong)fid->qid.path){
562 respond(r, "permission denied");
567 if(omode & ~(OTRUNC|OREAD|OWRITE|ORDWR)) {
568 respond(r, "permission denied");
573 if(fid->qid.path >= Qtrack+drive->ntrack) {
574 respond(r, "file no longer exists");
579 * allow the open with OWRITE or ORDWR if the
580 * drive and disc are both capable?
583 (o = drive->openrd(drive, fid->qid.path-Qtrack)) == nil) {
584 respond(r, "permission denied");
589 ((Aux*)fid->aux)->o = o;
596 fsdestroyfid(Fid *fid)
605 if(o && --o->nref == 0) {
613 checktoc(Drive *drive)
618 drive->gettoc(drive);
622 for(i=0; i<drive->ntrack; i++) {
623 t = &drive->track[i];
624 if(t->size == 0) /* being created */
628 sprint(t->name, "?%.3d", i);
644 print("unknown track type %d\n", t->type);
653 bufread(Otrack *t, void *v, long n, vlong off)
655 return bread(t->buf, v, n, off);
659 bufwrite(Otrack *t, void *v, long n)
661 return bwrite(t->buf, v, n);
666 .destroyfid= fsdestroyfid,
680 fprint(2, "usage: cdfs [-Dv] [-d /dev/sdC0] [-m mtpt]\n");
685 main(int argc, char **argv)
699 dev = EARGF(usage());
702 mtpt = EARGF(usage());
705 if((fd = create("/tmp/cdfs.log", OWRITE, 0666)) >= 0) {
708 if(fd != 1 && fd != 2)
711 scsiverbose = 2; /* verbose but no Readtoc errs */
718 if(dev == nil || mtpt == nil || argc > 0)
721 if((s = openscsi(dev)) == nil)
722 sysfatal("openscsi '%s': %r", dev);
723 if((drive = mmcprobe(s)) == nil)
724 sysfatal("mmcprobe '%s': %r", dev);
727 postmountsrv(&fs, nil, mtpt, MREPL|MCREATE);