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