Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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, *laysfx;
211
 
212
	rw = laysfx = "";
213
	switch (drive->mmctype) {
214
	case Mmccd:
215
		type = "cd-";
216
		break;
217
	case Mmcdvdminus:
218
	case Mmcdvdplus:
219
		type = drive->dvdtype;
220
		break;
221
	case Mmcbd:
222
		type = "bd-";
223
		if (drive->laysfx)
224
			laysfx = drive->laysfx;
225
		break;
226
	case Mmcnone:
227
		type = "no-disc";
228
		break;
229
	default:
230
		type = "**GOK**";		/* traditional */
231
		break;
232
	}
233
	if (drive->mmctype != Mmcnone && drive->dvdtype == nil)
234
		if (drive->erasable == Yes)
235
			rw = drive->mmctype == Mmcbd? "re": "rw";
236
		else if (drive->recordable == Yes)
237
			rw = "r";
238
		else
239
			rw = "rom";
240
	return smprint("%s%s%s", type, rw, laysfx);
241
}
242
 
243
int
244
fillstat(ulong qid, Dir *d)
245
{
246
	char *ty;
247
	Track *t;
248
	static char buf[32];
249
 
250
	nulldir(d);
251
	d->type = L'M';
252
	d->dev = 1;
253
	d->length = 0;
254
	ty = disctype(drive);
255
	strncpy(buf, ty, sizeof buf);
256
	free(ty);
257
	d->uid = d->gid = buf;
258
	d->muid = "";
259
	d->qid = (Qid){qid, drive->nchange, 0};
260
	d->atime = time(0);
261
	d->mtime = drive->changetime;
262
 
263
	switch(qid){
264
	case Qdir:
265
		d->name = "/";
266
		d->qid.type = QTDIR;
267
		d->mode = DMDIR|0777;
268
		break;
269
 
270
	case Qctl:
271
		d->name = "ctl";
272
		d->mode = 0666;
273
		break;
274
 
275
	case Qwa:
276
		if(drive->writeok == No ||
277
		    drive->mmctype != Mmcnone &&
278
		    drive->mmctype != Mmccd)
279
			return 0;
280
		d->name = "wa";
281
		d->qid.type = QTDIR;
282
		d->mode = DMDIR|0777;
283
		break;
284
 
285
	case Qwd:
286
		if(drive->writeok == No)
287
			return 0;
288
		d->name = "wd";
289
		d->qid.type = QTDIR;
290
		d->mode = DMDIR|0777;
291
		break;
292
 
293
	default:
294
		if(qid-Qtrack >= drive->ntrack)
295
			return 0;
296
		t = &drive->track[qid-Qtrack];
297
		if(strcmp(t->name, "") == 0)
298
			return 0;
299
		d->name = t->name;
300
		d->mode = t->mode;
301
		d->length = t->size;
302
		break;
303
	}
304
	return 1;
305
}
306
 
307
static ulong 
308
cddb_sum(int n)
309
{
310
	int ret;
311
	ret = 0;
312
	while(n > 0) {
313
		ret += n%10;
314
		n /= 10;
315
	}
316
	return ret;
317
}
318
 
319
static ulong
320
diskid(Drive *d)
321
{
322
	int i, n;
323
	ulong tmp;
324
	Msf *ms, *me;
325
 
326
	n = 0;
327
	for(i=0; i < d->ntrack; i++)
328
		n += cddb_sum(d->track[i].mbeg.m*60+d->track[i].mbeg.s);
329
 
330
	ms = &d->track[0].mbeg;
331
	me = &d->track[d->ntrack].mbeg;
332
	tmp = (me->m*60+me->s) - (ms->m*60+ms->s);
333
 
334
	/*
335
	 * the spec says n%0xFF rather than n&0xFF.  it's unclear which is
336
	 * correct.  most CDs are in the database under both entries.
337
	 */
338
	return ((n % 0xFF) << 24 | (tmp << 8) | d->ntrack);
339
}
340
 
341
static void
342
readctl(Req *r)
343
{
344
	int i, isaudio;
345
	ulong nwa;
346
	char *p, *e, *ty;
347
	char s[1024];
348
	Msf *m;
349
 
350
	isaudio = 0;
351
	for(i=0; i<drive->ntrack; i++)
352
		if(drive->track[i].type == TypeAudio)
353
			isaudio = 1;
354
 
355
	p = s;
356
	e = s + sizeof s;
357
	*p = '\0';
358
	if(isaudio){
359
		p = seprint(p, e, "aux/cddb query %8.8lux %d", diskid(drive),
360
			drive->ntrack);
361
		for(i=0; i<drive->ntrack; i++){
362
			m = &drive->track[i].mbeg;
363
			p = seprint(p, e, " %d", (m->m*60 + m->s)*75 + m->f);
364
		}
365
		m = &drive->track[drive->ntrack].mbeg;
366
		p = seprint(p, e, " %d\n", m->m*60 + m->s);
367
	}
368
 
369
	if(drive->readspeed == drive->writespeed)
370
		p = seprint(p, e, "speed %d\n", drive->readspeed);
371
	else
372
		p = seprint(p, e, "speed read %d write %d\n",
373
			drive->readspeed, drive->writespeed);
374
	p = seprint(p, e, "maxspeed read %d write %d\n",
375
		drive->maxreadspeed, drive->maxwritespeed);
376
 
377
	if (drive->Scsi.changetime != 0 && drive->ntrack != 0) { /* have disc? */
378
		ty = disctype(drive);
379
		p = seprint(p, e, "%s", ty);
380
		free(ty);
381
		if (drive->mmctype != Mmcnone) {
382
			nwa = getnwa(drive);
383
			p = seprint(p, e, " next writable sector ");
384
			if (nwa == ~0ul)
385
				p = seprint(p, e, "none; disc full");
386
			else
387
				p = seprint(p, e, "%lud", nwa);
388
		}
389
		seprint(p, e, "\n");
390
	}
391
	readstr(r, s);
392
}
393
 
394
static void
395
fsread(Req *r)
396
{
397
	int j, n, m;
398
	uchar *p, *ep;
399
	Dir d;
400
	Fid *fid;
401
	Otrack *o;
402
	vlong offset;
403
	void *buf;
404
	long count;
405
	Aux *a;
406
 
407
	fid = r->fid;
408
	offset = r->ifcall.offset;
409
	buf = r->ofcall.data;
410
	count = r->ifcall.count;
411
 
412
	switch((ulong)fid->qid.path) {
413
	case Qdir:
414
		checktoc(drive);
415
		p = buf;
416
		ep = p+count;
417
		m = Qtrack+drive->ntrack;
418
		a = fid->aux;
419
		if(offset == 0)
420
			a->doff = 1;	/* skip root */
421
 
422
		for(j=a->doff; j<m; j++) {
423
			if(fillstat(j, &d)) {
424
				if((n = convD2M(&d, p, ep-p)) <= BIT16SZ)
425
					break;
426
				p += n;
427
			}
428
		}
429
		a->doff = j;
430
 
431
		r->ofcall.count = p - (uchar*)buf;
432
		break;
433
	case Qwa:
434
	case Qwd:
435
		r->ofcall.count = 0;
436
		break;
437
	case Qctl:
438
		readctl(r);
439
		break;
440
	default:
441
		/* a disk track; we can only call read for whole blocks */
442
		o = ((Aux*)fid->aux)->o;
443
		if((count = o->drive->read(o, buf, count, offset)) < 0) {
444
			respond(r, geterrstr());
445
			return;
446
		}
447
		r->ofcall.count = count;
448
		break;
449
	}
450
	respond(r, nil);
451
}
452
 
453
static char Ebadmsg[] = "bad cdfs control message";
454
 
455
static char*
456
writectl(void *v, long count)
457
{
458
	char buf[256];
459
	char *f[10], *p;
460
	int i, nf, n, r, w, what;
461
 
462
	if(count >= sizeof(buf))
463
		count = sizeof(buf)-1;
464
	memmove(buf, v, count);
465
	buf[count] = '\0';
466
 
467
	nf = tokenize(buf, f, nelem(f));
468
	if(nf == 0)
469
		return Ebadmsg;
470
 
471
	if(strcmp(f[0], "speed") == 0){
472
		what = 0;
473
		r = w = -1;
474
		if(nf == 1)
475
			return Ebadmsg;
476
		for(i=1; i<nf; i++){
477
			if(strcmp(f[i], "read") == 0 || strcmp(f[i], "write") == 0){
478
				if(what!=0 && what!='?')
479
					return Ebadmsg;
480
				what = f[i][0];
481
			}else{
482
				if (strcmp(f[i], "best") == 0)
483
					n = (1<<16) - 1;
484
				else {
485
					n = strtol(f[i], &p, 0);
486
					if(*p != '\0' || n <= 0)
487
						return Ebadmsg;
488
				}
489
				switch(what){
490
				case 0:
491
					if(r >= 0 || w >= 0)
492
						return Ebadmsg;
493
					r = w = n;
494
					break;
495
				case 'r':
496
					if(r >= 0)
497
						return Ebadmsg;
498
					r = n;
499
					break;
500
				case 'w':
501
					if(w >= 0)
502
						return Ebadmsg;
503
					w = n;
504
					break;
505
				default:
506
					return Ebadmsg;
507
				}
508
				what = '?';
509
			}
510
		}
511
		if(what != '?')
512
			return Ebadmsg;
513
		return drive->setspeed(drive, r, w);
514
	}
515
	return drive->ctl(drive, nf, f);
516
}
517
 
518
static void
519
fswrite(Req *r)
520
{
521
	Otrack *o;
522
	Fid *fid;
523
 
524
	fid = r->fid;
525
	r->ofcall.count = r->ifcall.count;
526
	if(fid->qid.path == Qctl) {
527
		respond(r, writectl(r->ifcall.data, r->ifcall.count));
528
		return;
529
	}
530
 
531
	if((o = ((Aux*)fid->aux)->o) == nil || o->omode != OWRITE) {
532
		respond(r, "permission denied");
533
		return;
534
	}
535
 
536
	if(o->drive->write(o, r->ifcall.data, r->ifcall.count) < 0)
537
		respond(r, geterrstr());
538
	else
539
		respond(r, nil);
540
}
541
 
542
static void
543
fsstat(Req *r)
544
{
545
	fillstat((ulong)r->fid->qid.path, &r->d);
546
	r->d.name = estrdup9p(r->d.name);
547
	r->d.uid = estrdup9p(r->d.uid);
548
	r->d.gid = estrdup9p(r->d.gid);
549
	r->d.muid = estrdup9p(r->d.muid);
550
	respond(r, nil);
551
}
552
 
553
static void
554
fsopen(Req *r)
555
{
556
	int omode;
557
	Fid *fid;
558
	Otrack *o;
559
 
560
	fid = r->fid;
561
	omode = r->ifcall.mode;
562
	checktoc(drive);
563
	r->ofcall.qid = (Qid){fid->qid.path, drive->nchange, fid->qid.vers};
564
 
565
	switch((ulong)fid->qid.path){
566
	case Qdir:
567
	case Qwa:
568
	case Qwd:
569
		if(omode != OREAD) {
570
			respond(r, "permission denied");
571
			return;
572
		}
573
		break;
574
	case Qctl:
575
		if(omode & ~(OTRUNC|OREAD|OWRITE|ORDWR)) {
576
			respond(r, "permission denied");
577
			return;
578
		}
579
		break;
580
	default:
581
		if(fid->qid.path >= Qtrack+drive->ntrack) {
582
			respond(r, "file no longer exists");
583
			return;
584
		}
585
 
586
		/*
587
		 * allow the open with OWRITE or ORDWR if the
588
		 * drive and disc are both capable?
589
		 */
590
		if(omode != OREAD ||
591
		    (o = drive->openrd(drive, fid->qid.path-Qtrack)) == nil) {
592
			respond(r, "permission denied");
593
			return;
594
		}
595
 
596
		o->nref = 1;
597
		((Aux*)fid->aux)->o = o;
598
		break;
599
	}
600
	respond(r, nil);
601
}
602
 
603
static void
604
fsdestroyfid(Fid *fid)
605
{
606
	Aux *aux;
607
	Otrack *o;
608
 
609
	aux = fid->aux;
610
	if(aux == nil)
611
		return;
612
	o = aux->o;
613
	if(o && --o->nref == 0) {
614
		bterm(o->buf);
615
		drive->close(o);
616
		checktoc(drive);
617
	}
618
}
619
 
620
static void
621
checktoc(Drive *drive)
622
{
623
	int i;
624
	Track *t;
625
 
626
	drive->gettoc(drive);
627
	if(drive->nameok)
628
		return;
629
 
630
	for(i=0; i<drive->ntrack; i++) {
631
		t = &drive->track[i];
632
		if(t->size == 0)	/* being created */
633
			t->mode = 0;
634
		else
635
			t->mode = 0444;
636
		sprint(t->name, "?%.3d", i);
637
		switch(t->type){
638
		case TypeNone:
639
			t->name[0] = 'u';
640
//			t->mode = 0;
641
			break;
642
		case TypeData:
643
			t->name[0] = 'd';
644
			break;
645
		case TypeAudio:
646
			t->name[0] = 'a';
647
			break;
648
		case TypeBlank:
649
			t->name[0] = '\0';
650
			break;
651
		default:
652
			print("unknown track type %d\n", t->type);
653
			break;
654
		}
655
	}
656
 
657
	drive->nameok = 1;
658
}
659
 
660
long
661
bufread(Otrack *t, void *v, long n, vlong off)
662
{
663
	return bread(t->buf, v, n, off);
664
}
665
 
666
long
667
bufwrite(Otrack *t, void *v, long n)
668
{
669
	return bwrite(t->buf, v, n);
670
}
671
 
672
Srv fs = {
673
.attach=	fsattach,
674
.destroyfid=	fsdestroyfid,
675
.clone=		fsclone,
676
.walk1=		fswalk1,
677
.open=		fsopen,
678
.read=		fsread,
679
.write=		fswrite,
680
.create=	fscreate,
681
.remove=	fsremove,
682
.stat=		fsstat,
683
};
684
 
685
void
686
usage(void)
687
{
688
	fprint(2, "usage: cdfs [-Dv] [-d /dev/sdC0] [-m mtpt]\n");
689
	exits("usage");
690
}
691
 
692
void
693
main(int argc, char **argv)
694
{
695
	Scsi *s;
696
	int fd;
697
	char *dev, *mtpt;
698
 
699
	dev = "/dev/sdD0";
700
	mtpt = "/mnt/cd";
701
 
702
	ARGBEGIN{
703
	case 'D':
704
		chatty9p++;
705
		break;
706
	case 'd':
707
		dev = EARGF(usage());
708
		break;
709
	case 'm':
710
		mtpt = EARGF(usage());
711
		break;
712
	case 'v':
713
		if((fd = create("/tmp/cdfs.log", OWRITE, 0666)) >= 0) {
714
			dup(fd, 2);
715
			dup(fd, 1);
716
			if(fd != 1 && fd != 2)
717
				close(fd);
718
			vflag++;
719
			scsiverbose = 2; /* verbose but no Readtoc errs */
720
		} else
721
			fprint(2, "%s: can't open /tmp/cdfs.log: %r\n", argv0);
722
		break;
723
	default:
724
		usage();
725
	}ARGEND
726
 
727
	if(dev == nil || mtpt == nil || argc > 0)
728
		usage();
729
 
730
	werrstr("");
731
	if((s = openscsi(dev)) == nil)
732
		sysfatal("openscsi '%s': %r", dev);
733
	if((drive = mmcprobe(s)) == nil)
734
		sysfatal("mmcprobe '%s': %r", dev);
735
	checktoc(drive);
736
 
737
	postmountsrv(&fs, nil, mtpt, MREPL|MCREATE);
738
	exits(nil);
739
}