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 8 typedef struct Part Part; 9 typedef struct Trip Trip; 10 typedef struct Dbl Dbl; 11 typedef struct Ind Ind; 12 13 /* 14 * with 8192-byte blocks and 4-byte pointers, 15 * double-indirect gets us 34 GB. 16 * triple-indirect gets us 70,368 GB. 17 */ 18 19 enum 20 { 21 LOGBLKSZ = 13, 22 BLKSZ = 1<<LOGBLKSZ, /* 8192 */ 23 LOGNPTR = LOGBLKSZ-2, /* assume sizeof(void*) == 4 */ 24 NPTR = 1<<LOGNPTR, 25 }; 26 27 struct Trip 28 { 29 Dbl *dbl[NPTR]; 30 }; 31 32 struct Dbl 33 { 34 Ind *ind[NPTR]; 35 }; 36 37 struct Ind 38 { 39 uchar *blk[NPTR]; 40 }; 41 42 Trip trip; 43 44 struct Part 45 { 46 int inuse; 47 int vers; 48 ulong mode; 49 char *name; 50 vlong offset; /* in sectors */ 51 vlong length; /* in sectors */ 52 }; 53 54 enum 55 { 56 Qroot = 0, 57 Qdir, 58 Qctl, 59 Qpart, 60 }; 61 62 Part tab[64]; 63 int fd = -1; 64 char *sdname = "sdXX"; 65 ulong ctlmode = 0666; 66 char *inquiry = "aux/disksim hard drive"; 67 vlong nsect, sectsize, c, h, s; 68 ulong time0; 69 int rdonly; 70 71 char* 72 ctlstring(void) 73 { 74 int i; 75 Fmt fmt; 76 77 fmtstrinit(&fmt); 78 fmtprint(&fmt, "inquiry %s\n", inquiry); 79 fmtprint(&fmt, "geometry %lld %lld %lld %lld %lld\n", nsect, sectsize, c, h, s); 80 for(i=0; i<nelem(tab); i++) 81 if(tab[i].inuse) 82 fmtprint(&fmt, "part %s %lld %lld\n", tab[i].name, tab[i].offset, tab[i].length); 83 return fmtstrflush(&fmt); 84 } 85 86 int 87 addpart(char *name, vlong start, vlong end) 88 { 89 int i; 90 91 if(start < 0 || start > end || end > nsect){ 92 werrstr("bad partition boundaries"); 93 return -1; 94 } 95 96 for(i=0; i<nelem(tab); i++) 97 if(tab[i].inuse == 0) 98 break; 99 if(i == nelem(tab)){ 100 werrstr("no free partition slots"); 101 return -1; 102 } 103 104 free(tab[i].name); 105 tab[i].inuse = 1; 106 tab[i].name = estrdup9p(name); 107 tab[i].offset = start; 108 tab[i].length = end - start; 109 tab[i].mode = ctlmode; 110 tab[i].vers++; 111 112 return 0; 113 } 114 115 int 116 delpart(char *s) 117 { 118 int i; 119 120 for(i=0; i<nelem(tab); i++) 121 if(tab[i].inuse && strcmp(tab[i].name, s) == 0) 122 break; 123 if(i==nelem(tab)){ 124 werrstr("partition not found"); 125 return -1; 126 } 127 128 tab[i].inuse = 0; 129 free(tab[i].name); 130 tab[i].name = 0; 131 return 0; 132 } 133 134 void 135 ctlwrite(Req *r) 136 { 137 int i; 138 Cmdbuf *cb; 139 vlong start, end; 140 141 r->ofcall.count = r->ifcall.count; 142 cb = parsecmd(r->ifcall.data, r->ifcall.count); 143 if(cb->nf < 1){ 144 respond(r, "empty control message"); 145 free(cb); 146 return; 147 } 148 149 if(strcmp(cb->f[0], "part") == 0){ 150 if(cb->nf != 4){ 151 respondcmderror(r, cb, "part takes 3 args"); 152 free(cb); 153 return; 154 } 155 start = strtoll(cb->f[2], 0, 0); 156 end = strtoll(cb->f[3], 0, 0); 157 if(addpart(cb->f[1], start, end) < 0){ 158 respondcmderror(r, cb, "%r"); 159 free(cb); 160 return; 161 } 162 } 163 else if(strcmp(cb->f[0], "delpart") == 0){ 164 if(cb->nf != 2){ 165 respondcmderror(r, cb, "delpart takes 1 arg"); 166 free(cb); 167 return; 168 } 169 if(delpart(cb->f[1]) < 0){ 170 respondcmderror(r, cb, "%r"); 171 free(cb); 172 return; 173 } 174 } 175 else if(strcmp(cb->f[0], "inquiry") == 0){ 176 if(cb->nf != 2){ 177 respondcmderror(r, cb, "inquiry takes 1 arg"); 178 free(cb); 179 return; 180 } 181 free(inquiry); 182 inquiry = estrdup9p(cb->f[1]); 183 } 184 else if(strcmp(cb->f[0], "geometry") == 0){ 185 if(cb->nf != 6){ 186 respondcmderror(r, cb, "geometry takes 5 args"); 187 free(cb); 188 return; 189 } 190 nsect = strtoll(cb->f[1], 0, 0); 191 sectsize = strtoll(cb->f[2], 0, 0); 192 c = strtoll(cb->f[3], 0, 0); 193 h = strtoll(cb->f[4], 0, 0); 194 s = strtoll(cb->f[5], 0, 0); 195 if(tab[0].inuse && strcmp(tab[0].name, "data") == 0 && tab[0].vers == 0){ 196 tab[0].offset = 0; 197 tab[0].length = nsect; 198 } 199 for(i=0; i<nelem(tab); i++){ 200 if(tab[i].inuse && tab[i].offset+tab[i].length > nsect){ 201 tab[i].inuse = 0; 202 free(tab[i].name); 203 tab[i].name = 0; 204 } 205 } 206 } 207 else{ 208 respondcmderror(r, cb, "unknown control message"); 209 free(cb); 210 return; 211 } 212 213 free(cb); 214 respond(r, nil); 215 } 216 217 void* 218 allocblk(vlong addr) 219 { 220 uchar *op; 221 static uchar *p; 222 static ulong n; 223 224 if(n == 0){ 225 p = malloc(4*1024*1024); 226 if(p == 0) 227 sysfatal("out of memory"); 228 n = 4*1024*1024; 229 } 230 op = p; 231 p += BLKSZ; 232 n -= BLKSZ; 233 memset(op, 0, BLKSZ); 234 if(fd != -1 && addr != -1) 235 pread(fd, op, BLKSZ, addr); 236 return op; 237 } 238 239 uchar* 240 getblock(vlong addr, int alloc) 241 { 242 static uchar zero[BLKSZ]; 243 Dbl *p2; 244 Ind *p1; 245 uchar *p0; 246 uint i0, i1, i2; 247 vlong oaddr; 248 249 if(fd >= 0) 250 alloc = 1; 251 252 addr >>= LOGBLKSZ; 253 oaddr = addr<<LOGBLKSZ; 254 i0 = addr & (NPTR-1); 255 addr >>= LOGNPTR; 256 i1 = addr & (NPTR-1); 257 addr >>= LOGNPTR; 258 i2 = addr & (NPTR-1); 259 addr >>= LOGNPTR; 260 assert(addr == 0); 261 262 if((p2 = trip.dbl[i2]) == 0){ 263 if(!alloc) 264 return zero; 265 trip.dbl[i2] = p2 = allocblk(-1); 266 } 267 268 if((p1 = p2->ind[i1]) == 0){ 269 if(!alloc) 270 return zero; 271 p2->ind[i1] = p1 = allocblk(-1); 272 } 273 274 if((p0 = p1->blk[i0]) == 0){ 275 if(!alloc) 276 return zero; 277 p1->blk[i0] = p0 = allocblk(oaddr); 278 } 279 return p0; 280 } 281 282 void 283 dirty(vlong addr, uchar *buf) 284 { 285 vlong oaddr; 286 287 if(fd == -1 || rdonly) 288 return; 289 oaddr = addr&~((vlong)BLKSZ-1); 290 if(pwrite(fd, buf, BLKSZ, oaddr) != BLKSZ) 291 sysfatal("write: %r"); 292 } 293 294 int 295 rootgen(int off, Dir *d, void*) 296 { 297 memset(d, 0, sizeof *d); 298 d->atime = time0; 299 d->mtime = time0; 300 if(off == 0){ 301 d->name = estrdup9p(sdname); 302 d->mode = DMDIR|0777; 303 d->qid.path = Qdir; 304 d->qid.type = QTDIR; 305 d->uid = estrdup9p("disksim"); 306 d->gid = estrdup9p("disksim"); 307 d->muid = estrdup9p(""); 308 return 0; 309 } 310 return -1; 311 } 312 313 int 314 dirgen(int off, Dir *d, void*) 315 { 316 int n, j; 317 318 memset(d, 0, sizeof *d); 319 d->atime = time0; 320 d->mtime = time0; 321 if(off == 0){ 322 d->name = estrdup9p("ctl"); 323 d->mode = ctlmode; 324 d->qid.path = Qctl; 325 goto Have; 326 } 327 328 off--; 329 n = 0; 330 for(j=0; j<nelem(tab); j++){ 331 if(tab[j].inuse==0) 332 continue; 333 if(n == off){ 334 d->name = estrdup9p(tab[j].name); 335 d->length = tab[j].length*sectsize; 336 d->mode = tab[j].mode; 337 d->qid.path = Qpart+j; 338 d->qid.vers = tab[j].vers; 339 goto Have; 340 } 341 n++; 342 } 343 return -1; 344 345 Have: 346 d->uid = estrdup9p("disksim"); 347 d->gid = estrdup9p("disksim"); 348 d->muid = estrdup9p(""); 349 return 0; 350 } 351 352 void* 353 evommem(void *a, void *b, ulong n) 354 { 355 return memmove(b, a, n); 356 } 357 358 int 359 rdwrpart(Req *r) 360 { 361 int q; 362 Part *p; 363 vlong offset; 364 long count, tot, n, o; 365 uchar *blk, *dat; 366 void *(*move)(void*, void*, ulong); 367 368 q = r->fid->qid.path-Qpart; 369 if(q < 0 || q > nelem(tab) || !tab[q].inuse || tab[q].vers != r->fid->qid.vers){ 370 respond(r, "unknown partition"); 371 return -1; 372 } 373 374 p = &tab[q]; 375 offset = r->ifcall.offset; 376 count = r->ifcall.count; 377 if(offset < 0){ 378 respond(r, "negative offset"); 379 return -1; 380 } 381 if(count < 0){ 382 respond(r, "negative count"); 383 return -1; 384 } 385 if(offset > p->length*sectsize){ 386 respond(r, "offset past end of partition"); 387 return -1; 388 } 389 if(offset+count > p->length*sectsize) 390 count = p->length*sectsize - offset; 391 offset += p->offset*sectsize; 392 393 if(r->ifcall.type == Tread) 394 move = memmove; 395 else 396 move = evommem; 397 398 tot = 0; 399 if(r->ifcall.type == Tread) 400 dat = (uchar*)r->ofcall.data; 401 else 402 dat = (uchar*)r->ifcall.data; 403 o = offset & (BLKSZ-1); 404 405 /* left fringe block */ 406 if(o && count){ 407 blk = getblock(offset, r->ifcall.type==Twrite); 408 if(blk == nil) 409 abort(); 410 n = BLKSZ - o; 411 if(n > count) 412 n = count; 413 (*move)(dat, blk+o, n); 414 if(r->ifcall.type == Twrite) 415 dirty(offset, blk); 416 tot += n; 417 } 418 /* full and right fringe blocks */ 419 while(tot < count){ 420 blk = getblock(offset+tot, r->ifcall.type==Twrite); 421 if(blk == nil) 422 abort(); 423 n = BLKSZ; 424 if(n > count-tot) 425 n = count-tot; 426 (*move)(dat+tot, blk, n); 427 if(r->ifcall.type == Twrite) 428 dirty(offset+tot, blk); 429 tot += n; 430 } 431 r->ofcall.count = tot; 432 respond(r, nil); 433 return 0; 434 } 435 436 void 437 fsread(Req *r) 438 { 439 char *s; 440 441 switch((int)r->fid->qid.path){ 442 case Qroot: 443 dirread9p(r, rootgen, nil); 444 respond(r, nil); 445 break; 446 447 case Qdir: 448 dirread9p(r, dirgen, nil); 449 respond(r, nil); 450 break; 451 452 case Qctl: 453 s = ctlstring(); 454 readstr(r, s); 455 free(s); 456 respond(r, nil); 457 break; 458 459 default: 460 rdwrpart(r); 461 break; 462 } 463 } 464 465 void 466 fswrite(Req *r) 467 { 468 switch((int)r->fid->qid.path){ 469 case Qroot: 470 case Qdir: 471 respond(r, "write to a directory?"); 472 break; 473 474 case Qctl: 475 ctlwrite(r); 476 break; 477 478 default: 479 rdwrpart(r); 480 break; 481 } 482 } 483 484 void 485 fsopen(Req *r) 486 { 487 if(r->ifcall.mode&ORCLOSE) 488 respond(r, "cannot open ORCLOSE"); 489 490 switch((int)r->fid->qid.path){ 491 case Qroot: 492 case Qdir: 493 if(r->ifcall.mode != OREAD){ 494 respond(r, "bad mode for directory open"); 495 return; 496 } 497 } 498 499 respond(r, nil); 500 } 501 502 void 503 fsstat(Req *r) 504 { 505 int q; 506 Dir *d; 507 Part *p; 508 509 d = &r->d; 510 memset(d, 0, sizeof *d); 511 d->qid = r->fid->qid; 512 d->atime = d->mtime = time0; 513 q = r->fid->qid.path; 514 switch(q){ 515 case Qroot: 516 d->name = estrdup9p("/"); 517 d->mode = DMDIR|0777; 518 break; 519 520 case Qdir: 521 d->name = estrdup9p(sdname); 522 d->mode = DMDIR|0777; 523 break; 524 525 default: 526 q -= Qpart; 527 if(q < 0 || q > nelem(tab) || tab[q].inuse==0 || r->fid->qid.vers != tab[q].vers){ 528 respond(r, "partition no longer exists"); 529 return; 530 } 531 p = &tab[q]; 532 d->name = estrdup9p(p->name); 533 d->length = p->length * sectsize; 534 d->mode = p->mode; 535 break; 536 } 537 538 d->uid = estrdup9p("disksim"); 539 d->gid = estrdup9p("disksim"); 540 d->muid = estrdup9p(""); 541 respond(r, nil); 542 } 543 544 void 545 fsattach(Req *r) 546 { 547 char *spec; 548 549 spec = r->ifcall.aname; 550 if(spec && spec[0]){ 551 respond(r, "invalid attach specifier"); 552 return; 553 } 554 r->ofcall.qid = (Qid){Qroot, 0, QTDIR}; 555 r->fid->qid = r->ofcall.qid; 556 respond(r, nil); 557 } 558 559 char* 560 fswalk1(Fid *fid, char *name, Qid *qid) 561 { 562 int i; 563 switch((int)fid->qid.path){ 564 case Qroot: 565 if(strcmp(name, sdname) == 0){ 566 fid->qid.path = Qdir; 567 fid->qid.type = QTDIR; 568 *qid = fid->qid; 569 return nil; 570 } 571 break; 572 case Qdir: 573 if(strcmp(name, "ctl") == 0){ 574 fid->qid.path = Qctl; 575 fid->qid.vers = 0; 576 fid->qid.type = 0; 577 *qid = fid->qid; 578 return nil; 579 } 580 for(i=0; i<nelem(tab); i++){ 581 if(tab[i].inuse && strcmp(tab[i].name, name) == 0){ 582 fid->qid.path = i+Qpart; 583 fid->qid.vers = tab[i].vers; 584 fid->qid.type = 0; 585 *qid = fid->qid; 586 return nil; 587 } 588 } 589 break; 590 } 591 return "file not found"; 592 } 593 594 Srv fs = { 595 .attach= fsattach, 596 .open= fsopen, 597 .read= fsread, 598 .write= fswrite, 599 .stat= fsstat, 600 .walk1= fswalk1, 601 }; 602 603 char *mtpt = "/dev"; 604 char *srvname; 605 606 void 607 usage(void) 608 { 609 fprint(2, "usage: aux/disksim [-D] [-f file] [-s srvname] [-m mtpt] [sdXX]\n"); 610 fprint(2, "\tdefault mtpt is /dev\n"); 611 exits("usage"); 612 } 613 614 void 615 main(int argc, char **argv) 616 { 617 char *file; 618 619 file = nil; 620 quotefmtinstall(); 621 time0 = time(0); 622 if(NPTR != BLKSZ/sizeof(void*)) 623 sysfatal("unexpected pointer size"); 624 625 ARGBEGIN{ 626 case 'D': 627 chatty9p++; 628 break; 629 case 'f': 630 file = EARGF(usage()); 631 break; 632 case 'r': 633 rdonly = 1; 634 break; 635 case 's': 636 srvname = EARGF(usage()); 637 break; 638 case 'm': 639 mtpt = EARGF(usage()); 640 break; 641 default: 642 usage(); 643 }ARGEND 644 645 if(argc > 1) 646 usage(); 647 if(argc == 1) 648 sdname = argv[0]; 649 650 if(file){ 651 if((fd = open(file, rdonly ? OREAD : ORDWR)) < 0) 652 sysfatal("open %s: %r", file); 653 } 654 655 inquiry = estrdup9p(inquiry); 656 tab[0].name = estrdup9p("data"); 657 tab[0].inuse = 1; 658 tab[0].mode = 0666; 659 660 postmountsrv(&fs, srvname, mtpt, MBEFORE); 661 exits(nil); 662 } 663