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