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