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