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