]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cdfs/main.c
exec(2): fix prototypes
[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 = No;    
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 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 == Yes)
233                         rw = drive->mmctype == Mmcbd? "re": "rw";
234                 else if (drive->recordable == Yes)
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 == No ||
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 == No)
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         ulong nwa;
344         char *p, *e, *ty;
345         char s[1024];
346         Msf *m;
347
348         isaudio = 0;
349         for(i=0; i<drive->ntrack; i++)
350                 if(drive->track[i].type == TypeAudio)
351                         isaudio = 1;
352
353         p = s;
354         e = s + sizeof s;
355         *p = '\0';
356         if(isaudio){
357                 p = seprint(p, e, "aux/cddb query %8.8lux %d", diskid(drive),
358                         drive->ntrack);
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);
362                 }
363                 m = &drive->track[drive->ntrack].mbeg;
364                 p = seprint(p, e, " %d\n", m->m*60 + m->s);
365         }
366
367         if(drive->readspeed == drive->writespeed)
368                 p = seprint(p, e, "speed %d\n", drive->readspeed);
369         else
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);
374
375         if (drive->Scsi.changetime != 0 && drive->ntrack != 0) { /* have disc? */
376                 ty = disctype(drive);
377                 p = seprint(p, e, "%s", ty);
378                 free(ty);
379                 if (drive->mmctype != Mmcnone) {
380                         nwa = getnwa(drive);
381                         p = seprint(p, e, " next writable sector ");
382                         if (nwa == ~0ul)
383                                 p = seprint(p, e, "none; disc full");
384                         else
385                                 p = seprint(p, e, "%lud", nwa);
386                 }
387                 seprint(p, e, "\n");
388         }
389         readstr(r, s);
390 }
391
392 static void
393 fsread(Req *r)
394 {
395         int j, n, m;
396         uchar *p, *ep;
397         Dir d;
398         Fid *fid;
399         Otrack *o;
400         vlong offset;
401         void *buf;
402         long count;
403         Aux *a;
404
405         fid = r->fid;
406         offset = r->ifcall.offset;
407         buf = r->ofcall.data;
408         count = r->ifcall.count;
409
410         switch((ulong)fid->qid.path) {
411         case Qdir:
412                 checktoc(drive);
413                 p = buf;
414                 ep = p+count;
415                 m = Qtrack+drive->ntrack;
416                 a = fid->aux;
417                 if(offset == 0)
418                         a->doff = 1;    /* skip root */
419
420                 for(j=a->doff; j<m; j++) {
421                         if(fillstat(j, &d)) {
422                                 if((n = convD2M(&d, p, ep-p)) <= BIT16SZ)
423                                         break;
424                                 p += n;
425                         }
426                 }
427                 a->doff = j;
428
429                 r->ofcall.count = p - (uchar*)buf;
430                 break;
431         case Qwa:
432         case Qwd:
433                 r->ofcall.count = 0;
434                 break;
435         case Qctl:
436                 readctl(r);
437                 break;
438         default:
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());
443                         return;
444                 }
445                 r->ofcall.count = count;
446                 break;
447         }
448         respond(r, nil);
449 }
450
451 static char Ebadmsg[] = "bad cdfs control message";
452
453 static char*
454 writectl(void *v, long count)
455 {
456         char buf[256];
457         char *f[10], *p;
458         int i, nf, n, r, w, what;
459
460         if(count >= sizeof(buf))
461                 count = sizeof(buf)-1;
462         memmove(buf, v, count);
463         buf[count] = '\0';
464
465         nf = tokenize(buf, f, nelem(f));
466         if(nf == 0)
467                 return Ebadmsg;
468
469         if(strcmp(f[0], "speed") == 0){
470                 what = 0;
471                 r = w = -1;
472                 if(nf == 1)
473                         return Ebadmsg;
474                 for(i=1; i<nf; i++){
475                         if(strcmp(f[i], "read") == 0 || strcmp(f[i], "write") == 0){
476                                 if(what!=0 && what!='?')
477                                         return Ebadmsg;
478                                 what = f[i][0];
479                         }else{
480                                 if (strcmp(f[i], "best") == 0)
481                                         n = (1<<16) - 1;
482                                 else {
483                                         n = strtol(f[i], &p, 0);
484                                         if(*p != '\0' || n <= 0)
485                                                 return Ebadmsg;
486                                 }
487                                 switch(what){
488                                 case 0:
489                                         if(r >= 0 || w >= 0)
490                                                 return Ebadmsg;
491                                         r = w = n;
492                                         break;
493                                 case 'r':
494                                         if(r >= 0)
495                                                 return Ebadmsg;
496                                         r = n;
497                                         break;
498                                 case 'w':
499                                         if(w >= 0)
500                                                 return Ebadmsg;
501                                         w = n;
502                                         break;
503                                 default:
504                                         return Ebadmsg;
505                                 }
506                                 what = '?';
507                         }
508                 }
509                 if(what != '?')
510                         return Ebadmsg;
511                 return drive->setspeed(drive, r, w);
512         }
513         return drive->ctl(drive, nf, f);
514 }
515
516 static void
517 fswrite(Req *r)
518 {
519         Otrack *o;
520         Fid *fid;
521
522         fid = r->fid;
523         r->ofcall.count = r->ifcall.count;
524         if(fid->qid.path == Qctl) {
525                 respond(r, writectl(r->ifcall.data, r->ifcall.count));
526                 return;
527         }
528
529         if((o = ((Aux*)fid->aux)->o) == nil || o->omode != OWRITE) {
530                 respond(r, "permission denied");
531                 return;
532         }
533
534         if(o->drive->write(o, r->ifcall.data, r->ifcall.count) < 0)
535                 respond(r, geterrstr());
536         else
537                 respond(r, nil);
538 }
539
540 static void
541 fsstat(Req *r)
542 {
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);
548         respond(r, nil);
549 }
550
551 static void
552 fsopen(Req *r)
553 {
554         int omode;
555         Fid *fid;
556         Otrack *o;
557
558         fid = r->fid;
559         omode = r->ifcall.mode;
560         checktoc(drive);
561         r->ofcall.qid = (Qid){fid->qid.path, drive->nchange, fid->qid.vers};
562
563         switch((ulong)fid->qid.path){
564         case Qdir:
565         case Qwa:
566         case Qwd:
567                 if(omode != OREAD) {
568                         respond(r, "permission denied");
569                         return;
570                 }
571                 break;
572         case Qctl:
573                 if(omode & ~(OTRUNC|OREAD|OWRITE|ORDWR)) {
574                         respond(r, "permission denied");
575                         return;
576                 }
577                 break;
578         default:
579                 if(fid->qid.path >= Qtrack+drive->ntrack) {
580                         respond(r, "file no longer exists");
581                         return;
582                 }
583
584                 /*
585                  * allow the open with OWRITE or ORDWR if the
586                  * drive and disc are both capable?
587                  */
588                 if(omode != OREAD ||
589                     (o = drive->openrd(drive, fid->qid.path-Qtrack)) == nil) {
590                         respond(r, "permission denied");
591                         return;
592                 }
593
594                 o->nref = 1;
595                 ((Aux*)fid->aux)->o = o;
596                 break;
597         }
598         respond(r, nil);
599 }
600
601 static void
602 fsdestroyfid(Fid *fid)
603 {
604         Aux *aux;
605         Otrack *o;
606
607         aux = fid->aux;
608         if(aux == nil)
609                 return;
610         o = aux->o;
611         if(o && --o->nref == 0) {
612                 bterm(o->buf);
613                 drive->close(o);
614                 checktoc(drive);
615         }
616 }
617
618 static void
619 checktoc(Drive *drive)
620 {
621         int i;
622         Track *t;
623
624         drive->gettoc(drive);
625         if(drive->nameok)
626                 return;
627
628         for(i=0; i<drive->ntrack; i++) {
629                 t = &drive->track[i];
630                 if(t->size == 0)        /* being created */
631                         t->mode = 0;
632                 else
633                         t->mode = 0444;
634                 sprint(t->name, "?%.3d", i);
635                 switch(t->type){
636                 case TypeNone:
637                         t->name[0] = 'u';
638 //                      t->mode = 0;
639                         break;
640                 case TypeData:
641                         t->name[0] = 'd';
642                         break;
643                 case TypeAudio:
644                         t->name[0] = 'a';
645                         break;
646                 case TypeBlank:
647                         t->name[0] = '\0';
648                         break;
649                 default:
650                         print("unknown track type %d\n", t->type);
651                         break;
652                 }
653         }
654
655         drive->nameok = 1;
656 }
657
658 long
659 bufread(Otrack *t, void *v, long n, vlong off)
660 {
661         return bread(t->buf, v, n, off);
662 }
663
664 long
665 bufwrite(Otrack *t, void *v, long n)
666 {
667         return bwrite(t->buf, v, n);
668 }
669
670 Srv fs = {
671 .attach=        fsattach,
672 .destroyfid=    fsdestroyfid,
673 .clone=         fsclone,
674 .walk1=         fswalk1,
675 .open=          fsopen,
676 .read=          fsread,
677 .write=         fswrite,
678 .create=        fscreate,
679 .remove=        fsremove,
680 .stat=          fsstat,
681 };
682
683 void
684 usage(void)
685 {
686         fprint(2, "usage: cdfs [-Dv] [-d /dev/sdC0] [-m mtpt]\n");
687         exits("usage");
688 }
689
690 void
691 main(int argc, char **argv)
692 {
693         Scsi *s;
694         int fd;
695         char *dev, *mtpt;
696
697         dev = "/dev/sdD0";
698         mtpt = "/mnt/cd";
699
700         ARGBEGIN{
701         case 'D':
702                 chatty9p++;
703                 break;
704         case 'd':
705                 dev = EARGF(usage());
706                 break;
707         case 'm':
708                 mtpt = EARGF(usage());
709                 break;
710         case 'v':
711                 if((fd = create("/tmp/cdfs.log", OWRITE, 0666)) >= 0) {
712                         dup(fd, 2);
713                         dup(fd, 1);
714                         if(fd != 1 && fd != 2)
715                                 close(fd);
716                         vflag++;
717                         scsiverbose = 2; /* verbose but no Readtoc errs */
718                 }
719                 break;
720         default:
721                 usage();
722         }ARGEND
723
724         if(dev == nil || mtpt == nil || argc > 0)
725                 usage();
726
727         werrstr("");
728         if((s = openscsi(dev)) == nil)
729                 sysfatal("openscsi '%s': %r", dev);
730         if((drive = mmcprobe(s)) == nil)
731                 sysfatal("mmcprobe '%s': %r", dev);
732         checktoc(drive);
733
734         postmountsrv(&fs, nil, mtpt, MREPL|MCREATE);
735         exits(nil);
736 }