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*);
37 static char errbuf[ERRMAX];
39 rerrstr(errbuf, sizeof errbuf);
50 sysfatal("malloc %lud fails", sz);
59 spec = r->ifcall.aname;
61 respond(r, "invalid attach specifier");
66 r->fid->qid = (Qid){Qdir, drive->nchange, QTDIR};
67 r->ofcall.qid = r->fid->qid;
68 r->fid->aux = emalloc(sizeof(Aux));
73 fsclone(Fid *old, Fid *new)
77 na = emalloc(sizeof(Aux));
78 *na = *((Aux*)old->aux);
86 fswalk1(Fid *fid, char *name, Qid *qid)
91 switch((ulong)fid->qid.path) {
93 if(strcmp(name, "..") == 0) {
94 *qid = (Qid){Qdir, drive->nchange, QTDIR};
97 if(strcmp(name, "ctl") == 0) {
98 *qid = (Qid){Qctl, 0, 0};
101 if(strcmp(name, "wa") == 0 && drive->writeok &&
102 (drive->mmctype == Mmcnone ||
103 drive->mmctype == Mmccd)) {
104 *qid = (Qid){Qwa, drive->nchange, QTDIR};
107 if(strcmp(name, "wd") == 0 && drive->writeok) {
108 *qid = (Qid){Qwd, drive->nchange, QTDIR};
111 for(i=0; i<drive->ntrack; i++)
112 if(strcmp(drive->track[i].name, name) == 0)
114 if(i == drive->ntrack)
115 return "file not found";
116 *qid = (Qid){Qtrack+i, 0, 0};
121 if(strcmp(name, "..") == 0) {
122 *qid = (Qid){Qdir, drive->nchange, QTDIR};
125 return "file not found";
126 default: /* bug: lib9p could handle this */
127 return "walk in non-directory";
139 omode = r->ifcall.mode;
141 if(omode != OWRITE) {
142 respond(r, "bad mode (use OWRITE)");
146 switch((ulong)fid->qid.path) {
149 respond(r, "permission denied");
153 if (drive->mmctype != Mmcnone &&
154 drive->mmctype != Mmccd) {
155 respond(r, "audio supported only on cd");
166 if((drive->cap & Cwrite) == 0) {
167 respond(r, "drive does not write");
171 o = drive->create(drive, type);
173 respond(r, geterrstr());
177 checktoc(drive); /* update directory info */
179 ((Aux*)fid->aux)->o = o;
181 fid->qid = (Qid){Qtrack+(o->track - drive->track), drive->nchange, 0};
182 r->ofcall.qid = fid->qid;
189 switch((ulong)r->fid->qid.path){
192 if(drive->fixate(drive) < 0)
193 respond(r, geterrstr());
194 // let us see if it can figure this out: drive->writeok = No;
200 respond(r, "permission denied");
205 /* result is one word, so it can be used as a uid in Dir structs */
207 disctype(Drive *drive)
211 switch (drive->mmctype) {
217 type = drive->dvdtype;
226 type = "**GOK**"; /* traditional */
230 if (drive->mmctype != Mmcnone && drive->dvdtype == nil)
231 if (drive->erasable == Yes)
232 rw = drive->mmctype == Mmcbd? "re": "rw";
233 else if (drive->recordable == Yes)
237 return smprint("%s%s", type, rw);
241 fillstat(ulong qid, Dir *d)
251 ty = disctype(drive);
252 strncpy(buf, ty, sizeof buf);
254 d->uid = d->gid = buf;
256 d->qid = (Qid){qid, drive->nchange, 0};
258 d->mtime = drive->changetime;
264 d->mode = DMDIR|0777;
273 if(drive->writeok == No ||
274 drive->mmctype != Mmcnone &&
275 drive->mmctype != Mmccd)
279 d->mode = DMDIR|0777;
283 if(drive->writeok == No)
287 d->mode = DMDIR|0777;
291 if(qid-Qtrack >= drive->ntrack)
293 t = &drive->track[qid-Qtrack];
294 if(strcmp(t->name, "") == 0)
324 for(i=0; i < d->ntrack; i++)
325 n += cddb_sum(d->track[i].mbeg.m*60+d->track[i].mbeg.s);
327 ms = &d->track[0].mbeg;
328 me = &d->track[d->ntrack].mbeg;
329 tmp = (me->m*60+me->s) - (ms->m*60+ms->s);
332 * the spec says n%0xFF rather than n&0xFF. it's unclear which is
333 * correct. most CDs are in the database under both entries.
335 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) {
380 p = seprint(p, e, " next writable sector ");
382 p = seprint(p, e, "none; disc full");
384 p = seprint(p, e, "%lud", nwa);
405 offset = r->ifcall.offset;
406 buf = r->ofcall.data;
407 count = r->ifcall.count;
409 switch((ulong)fid->qid.path) {
414 m = Qtrack+drive->ntrack;
417 a->doff = 1; /* skip root */
419 for(j=a->doff; j<m; j++) {
420 if(fillstat(j, &d)) {
421 if((n = convD2M(&d, p, ep-p)) <= BIT16SZ)
428 r->ofcall.count = p - (uchar*)buf;
438 /* a disk track; we can only call read for whole blocks */
439 o = ((Aux*)fid->aux)->o;
440 if((count = o->drive->read(o, buf, count, offset)) < 0) {
441 respond(r, geterrstr());
444 r->ofcall.count = count;
450 static char Ebadmsg[] = "bad cdfs control message";
453 writectl(void *v, long count)
457 int i, nf, n, r, w, what;
459 if(count >= sizeof(buf))
460 count = sizeof(buf)-1;
461 memmove(buf, v, count);
464 nf = tokenize(buf, f, nelem(f));
468 if(strcmp(f[0], "speed") == 0){
474 if(strcmp(f[i], "read") == 0 || strcmp(f[i], "write") == 0){
475 if(what!=0 && what!='?')
479 if (strcmp(f[i], "best") == 0)
482 n = strtol(f[i], &p, 0);
483 if(*p != '\0' || n <= 0)
510 return drive->setspeed(drive, r, w);
512 return drive->ctl(drive, nf, f);
522 r->ofcall.count = r->ifcall.count;
523 if(fid->qid.path == Qctl) {
524 respond(r, writectl(r->ifcall.data, r->ifcall.count));
528 if((o = ((Aux*)fid->aux)->o) == nil || o->omode != OWRITE) {
529 respond(r, "permission denied");
533 if(o->drive->write(o, r->ifcall.data, r->ifcall.count) < 0)
534 respond(r, geterrstr());
542 fillstat((ulong)r->fid->qid.path, &r->d);
543 r->d.name = estrdup9p(r->d.name);
544 r->d.uid = estrdup9p(r->d.uid);
545 r->d.gid = estrdup9p(r->d.gid);
546 r->d.muid = estrdup9p(r->d.muid);
558 omode = r->ifcall.mode;
560 r->ofcall.qid = (Qid){fid->qid.path, drive->nchange, fid->qid.vers};
562 switch((ulong)fid->qid.path){
567 respond(r, "permission denied");
572 if(omode & ~(OTRUNC|OREAD|OWRITE|ORDWR)) {
573 respond(r, "permission denied");
578 if(fid->qid.path >= Qtrack+drive->ntrack) {
579 respond(r, "file no longer exists");
584 * allow the open with OWRITE or ORDWR if the
585 * drive and disc are both capable?
588 (o = drive->openrd(drive, fid->qid.path-Qtrack)) == nil) {
589 respond(r, "permission denied");
594 ((Aux*)fid->aux)->o = o;
601 fsdestroyfid(Fid *fid)
610 if(o && --o->nref == 0) {
618 checktoc(Drive *drive)
623 drive->gettoc(drive);
627 for(i=0; i<drive->ntrack; i++) {
628 t = &drive->track[i];
629 if(t->size == 0) /* being created */
633 sprint(t->name, "?%.3d", i);
649 print("unknown track type %d\n", t->type);
658 bufread(Otrack *t, void *v, long n, vlong off)
660 return bread(t->buf, v, n, off);
664 bufwrite(Otrack *t, void *v, long n)
666 return bwrite(t->buf, v, n);
671 .destroyfid= fsdestroyfid,
685 fprint(2, "usage: cdfs [-Dv] [-d /dev/sdC0] [-m mtpt]\n");
690 main(int argc, char **argv)
704 dev = EARGF(usage());
707 mtpt = EARGF(usage());
710 if((fd = create("/tmp/cdfs.log", OWRITE, 0666)) >= 0) {
713 if(fd != 1 && fd != 2)
716 scsiverbose = 2; /* verbose but no Readtoc errs */
723 if(dev == nil || mtpt == nil || argc > 0)
727 if((s = openscsi(dev)) == nil)
728 sysfatal("openscsi '%s': %r", dev);
729 if((drive = mmcprobe(s)) == nil)
730 sysfatal("mmcprobe '%s': %r", dev);
733 postmountsrv(&fs, nil, mtpt, MREPL|MCREATE);