xref: /plan9-contrib/sys/src/cmd/cdfs/main.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
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 static void checktoc(Drive*);
12 
13 int vflag;
14 
15 Drive *drive;
16 int nchange;
17 
18 enum {
19 	Qdir = 0|CHDIR,
20 	Qctl = 0,
21 	Qwa = 1|CHDIR,
22 	Qwd = 2|CHDIR,
23 	Qtrack = 3,
24 };
25 
26 char*
27 geterrstr(void)
28 {
29 	static char errbuf[ERRLEN];
30 
31 	errbuf[0] = 0;
32 	errstr(errbuf);
33 	return errbuf;
34 }
35 
36 static void
37 cdattach(Req *r, Fid*, char *spec, Qid *qid)
38 {
39 	if(spec && spec[0]) {
40 		respond(r, "invalid attach specifier");
41 		return;
42 	}
43 
44 	checktoc(drive);
45 	*qid = (Qid){Qdir, drive->nchange};
46 	respond(r, nil);
47 }
48 
49 static void
50 cdclone(Req *r, Fid *old, Fid *new)
51 {
52 	Otrack *aux;
53 
54 	if(aux = old->aux)
55 		aux->nref++;
56 	new->aux = aux;
57 
58 	respond(r, nil);
59 }
60 
61 static void
62 cdwalk(Req *r, Fid *fid, char *name, Qid *qid)
63 {
64 	int i;
65 
66 	checktoc(drive);
67 	switch(fid->qid.path) {
68 	case Qdir:
69 		if(strcmp(name, "..") == 0) {
70 			*qid = (Qid){Qdir, drive->nchange};
71 			respond(r, nil);
72 			return;
73 		}
74 		if(strcmp(name, "ctl") == 0) {
75 			*qid = (Qid){Qctl, 0};
76 			respond(r, nil);
77 			return;
78 		}
79 		if(strcmp(name, "wa") == 0 && drive->writeok) {
80 			*qid = (Qid){Qwa, drive->nchange};
81 			respond(r, nil);
82 			return;
83 		}
84 		if(strcmp(name, "wd") == 0 && drive->writeok) {
85 			*qid = (Qid){Qwd, drive->nchange};
86 			respond(r, nil);
87 			return;
88 		}
89 		for(i=0; i<drive->ntrack; i++)
90 			if(strcmp(drive->track[i].name, name) == 0)
91 				break;
92 		if(i == drive->ntrack) {
93 			respond(r, "file not found");
94 			return;
95 		}
96 		*qid = (Qid){Qtrack+i, 0};
97 		respond(r, nil);
98 		return;
99 
100 	case Qwa:
101 	case Qwd:
102 		if(strcmp(name, "..") == 0) {
103 			*qid = (Qid){Qdir, drive->nchange};
104 			respond(r, nil);
105 			return;
106 		}
107 		respond(r, "file not found");
108 		return;
109 	default:	/* bug: lib9p could handle this */
110 		respond(r, "walk in non-directory");
111 		return;
112 	}
113 }
114 
115 static void
116 cdcreate(Req *r, Fid *fid, char*, int omode, ulong, Qid *qid)
117 {
118 	int type;
119 	Otrack *o;
120 
121 	if(omode != OWRITE) {
122 		respond(r, "bad mode (use OWRITE)");
123 		return;
124 	}
125 
126 	switch(fid->qid.path) {
127 	case Qdir:
128 	default:
129 		respond(r, "permission denied");
130 		return;
131 
132 	case Qwa:
133 		type = TypeAudio;
134 		break;
135 
136 	case Qwd:
137 		type = TypeData;
138 		break;
139 	}
140 
141 	if((drive->cap & Cwrite) == 0) {
142 		respond(r, "drive does not write");
143 		return;
144 	}
145 
146 	o = drive->create(drive, type);
147 	if(o == nil) {
148 		respond(r, geterrstr());
149 		return;
150 	}
151 	drive->nchange = -1;
152 	checktoc(drive);	/* update directory info */
153 	o->nref = 1;
154 	fid->aux = o;
155 
156 	*qid = (Qid){Qtrack+(o->track - drive->track), drive->nchange};
157 	respond(r, nil);
158 }
159 
160 static void
161 cdremove(Req *r, Fid *fid)
162 {
163 	switch(fid->qid.path){
164 	case Qwa:
165 	case Qwd:
166 		if(drive->fixate(drive) < 0)
167 			respond(r, geterrstr());
168 // let's see if it can figure this out		drive->writeok = 0;
169 		else
170 			respond(r, nil);
171 		checktoc(drive);
172 		break;
173 	default:
174 		respond(r, "permission denied");
175 		break;
176 	}
177 }
178 
179 int
180 fillstat(int qid, Dir *d)
181 {
182 	Track *t;
183 
184 	memset(d, 0, sizeof(Dir));
185 	strcpy(d->uid, "cd");
186 	strcpy(d->gid, "cd");
187 	d->qid = (Qid){qid, drive->nchange};
188 	d->atime = time(0);
189 	d->mtime = drive->changetime;
190 
191 	switch(qid){
192 	case Qdir:
193 		strcpy(d->name, "/");
194 		d->mode = CHDIR|0777;
195 		break;
196 
197 	case Qctl:
198 		strcpy(d->name, "ctl");
199 		d->mode = 0666;
200 		break;
201 
202 	case Qwa&~CHDIR:
203 	case Qwa:
204 		if(drive->writeok == 0)
205 			return 0;
206 		strcpy(d->name, "wa");
207 		d->mode = CHDIR|0777;
208 		break;
209 
210 	case Qwd&~CHDIR:
211 	case Qwd:
212 		if(drive->writeok == 0)
213 			return 0;
214 		strcpy(d->name, "wd");
215 		d->mode = CHDIR|0777;
216 		break;
217 
218 	default:
219 		if(qid-Qtrack >= drive->ntrack)
220 			return 0;
221 		t = &drive->track[qid-Qtrack];
222 		if(strcmp(t->name, "") == 0)
223 			return 0;
224 		strcpy(d->name, t->name);
225 		d->mode = t->mode;
226 		d->length = t->size;
227 		break;
228 	}
229 	return 1;
230 }
231 
232 static int
233 readctl(void*, long, long)
234 {
235 	return 0;
236 }
237 
238 static void
239 cdread(Req *r, Fid *fid, void *buf, long *count, vlong offset)
240 {
241 	int i, j, off, n, m;
242 	char *p;
243 	Dir d;
244 	Otrack *o;
245 
246 	switch(fid->qid.path) {
247 	case Qdir:
248 		checktoc(drive);
249 		p = buf;
250 		m = Qtrack+drive->ntrack;
251 		n = *count/DIRLEN;
252 		off = offset/DIRLEN;
253 		for(i=0, j=0; j<m && i<off+n; j++) {
254 			if(fillstat(j, &d)) {
255 				if(off<=i && i<off+n) {
256 					convD2M(&d, p);
257 					p += DIRLEN;
258 				}
259 				i++;
260 			}
261 		}
262 		*count = (i-off)*DIRLEN;
263 		respond(r, nil);
264 		return;
265 
266 	case Qwa:
267 	case Qwd:
268 		*count = 0;
269 		respond(r, nil);
270 		return;
271 
272 	case Qctl:
273 		*count = readctl(buf, *count, offset);
274 		respond(r, nil);
275 		return;
276 	}
277 
278 	/* a disk track; we can only call read for whole blocks */
279 	o = fid->aux;
280 
281 	if((*count = o->drive->read(o, buf, *count, offset)) < 0)
282 		respond(r, geterrstr());
283 	else
284 		respond(r, nil);
285 
286 	return;
287 }
288 
289 static char*
290 writectl(void *v, long count)
291 {
292 	char buf[256];
293 	char *f[10];
294 	int nf;
295 
296 	if(count >= sizeof(buf))
297 		count = sizeof(buf)-1;
298 	memmove(buf, v, count);
299 	buf[count] = '\0';
300 
301 	nf = tokenize(buf, f, nelem(f));
302 	return drive->ctl(drive, nf, f);
303 }
304 
305 static void
306 cdwrite(Req *r, Fid *fid, void *buf, long *count, vlong)
307 {
308 	Otrack *o;
309 
310 	if(fid->qid.path == Qctl) {
311 		respond(r, writectl(buf, *count));
312 		return;
313 	}
314 
315 	if((o = fid->aux) == nil || o->omode != OWRITE) {
316 		respond(r, "permission denied");
317 		return;
318 	}
319 
320 	if(o->drive->write(o, buf, *count) < 0)
321 		respond(r, geterrstr());
322 	else
323 		respond(r, nil);
324 }
325 
326 static void
327 cdstat(Req *r, Fid *fid, Dir *d)
328 {
329 	fillstat(fid->qid.path, d);
330 	respond(r, nil);
331 }
332 
333 static void
334 cdopen(Req *r, Fid *fid, int omode, Qid *qid)
335 {
336 	Otrack *o;
337 
338 	checktoc(drive);
339 	*qid = (Qid){fid->qid.path, drive->nchange};
340 
341 	switch(fid->qid.path){
342 	case Qdir:
343 	case Qwa:
344 	case Qwd:
345 		if(omode == OREAD)
346 			respond(r, nil);
347 		else
348 			respond(r, "permission denied");
349 		return;
350 
351 	case Qctl:
352 		if(omode&~(OTRUNC|OREAD|OWRITE|ORDWR))
353 			respond(r, "permission denied");
354 		else
355 			respond(r, nil);
356 		return;
357 
358 	default:
359 		if(fid->qid.path >= Qtrack+drive->ntrack) {
360 			respond(r, "file no longer exists");
361 			return;
362 		}
363 
364 		if(omode != OREAD || (o = drive->openrd(drive, fid->qid.path-Qtrack)) == nil) {
365 			respond(r, "permission denied");
366 			return;
367 		}
368 
369 		o->nref = 1;
370 		fid->aux = o;
371 		respond(r, nil);
372 	}
373 }
374 
375 static uchar zero[BScdda];
376 
377 static void
378 cdclunkaux(Fid *fid)
379 {
380 	Otrack *o;
381 
382 	o = fid->aux;
383 	if(o && --o->nref == 0) {
384 		bterm(o->buf);
385 		drive->close(o);
386 		checktoc(drive);
387 	}
388 }
389 
390 static void
391 checktoc(Drive *drive)
392 {
393 	int i;
394 	Track *t;
395 
396 	drive->gettoc(drive);
397 	if(drive->nameok)
398 		return;
399 
400 	for(i=0; i<drive->ntrack; i++) {
401 		t = &drive->track[i];
402 		if(t->size == 0)	/* being created */
403 			t->mode = 0;
404 		else
405 			t->mode = 0444;
406 		sprint(t->name, "?%.3d", i);
407 		switch(t->type){
408 		case TypeNone:
409 			t->name[0] = 'u';
410 			t->mode = 0;
411 			break;
412 		case TypeData:
413 			t->name[0] = 'd';
414 			break;
415 		case TypeAudio:
416 			t->name[0] = 'a';
417 			break;
418 		case TypeBlank:
419 			t->name[0] = '\0';
420 			break;
421 		default:
422 			print("unknown type %d\n", t->type);
423 			break;
424 		}
425 	}
426 
427 	drive->nameok = 1;
428 }
429 
430 long
431 bufread(Otrack *t, void *v, long n, long off)
432 {
433 	return bread(t->buf, v, n, off);
434 }
435 
436 long
437 bufwrite(Otrack *t, void *v, long n)
438 {
439 	return bwrite(t->buf, v, n);
440 }
441 
442 Srv cdsrv = {
443 .attach=	cdattach,
444 .clone=	cdclone,
445 .clunkaux=	cdclunkaux,
446 .walk=	cdwalk,
447 .open=	cdopen,
448 .read=	cdread,
449 .write=	cdwrite,
450 .create=	cdcreate,
451 .remove=	cdremove,
452 .stat=	cdstat,
453 };
454 
455 void
456 usage(void)
457 {
458 	fprint(2, "usage: cdfs [-v] [-d /dev/sdC0] [-m mtpt]\n");
459 	exits("usage");
460 }
461 
462 void
463 main(int argc, char **argv)
464 {
465 	Scsi *s;
466 	int fd;
467 	char *dev, *mtpt;
468 
469 	dev = "/dev/sdD0";
470 	mtpt = "/mnt/cd";
471 
472 	ARGBEGIN{
473 	case 'd':
474 		dev = ARGF();
475 		break;
476 	case 'm':
477 		mtpt = ARGF();
478 		break;
479 	case 'v':
480 		if((fd = create("/tmp/cdfs.log", OWRITE, 0666)) >= 0) {
481 			dup(fd, 2);
482 			dup(fd, 1);
483 			if(fd != 1 && fd != 2)
484 				close(fd);
485 			vflag++;
486 			scsiverbose++;
487 		}
488 		break;
489 	case 'V':
490 		lib9p_chatty++;
491 		break;
492 	default:
493 		usage();
494 	}ARGEND
495 
496 	if(dev == nil || mtpt == nil || argc > 0)
497 		usage();
498 
499 	if((s = openscsi(dev)) == nil) {
500 		fprint(2, "openscsi '%s': %r\n", dev);
501 		exits("openscsi");
502 	}
503 
504 	if((drive = mmcprobe(s)) == nil) {
505 		fprint(2, "mmcprobe '%s': %r\n", dev);
506 		exits("mmcprobe");
507 	}
508 
509 	checktoc(drive);
510 
511 	postmountsrv(&cdsrv, nil, mtpt, MREPL|MCREATE);
512 }
513