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 /* full and right fringe blocks */ 434 while(tot < count){ 435 blk = getblock(offset+tot, r->ifcall.type==Twrite && nonzero); 436 n = BLKSZ; 437 if(n > count-tot) 438 n = count-tot; 439 if(r->ifcall.type != Twrite || blk != zero) 440 (*move)(dat+tot, blk, n); 441 if(r->ifcall.type == Twrite) 442 dirty(offset+tot, blk); 443 tot += n; 444 } 445 r->ofcall.count = tot; 446 respond(r, nil); 447 return 0; 448 } 449 450 void 451 fsread(Req *r) 452 { 453 char *s; 454 455 switch((int)r->fid->qid.path){ 456 case Qroot: 457 dirread9p(r, rootgen, nil); 458 respond(r, nil); 459 break; 460 461 case Qdir: 462 dirread9p(r, dirgen, nil); 463 respond(r, nil); 464 break; 465 466 case Qctl: 467 s = ctlstring(); 468 readstr(r, s); 469 free(s); 470 respond(r, nil); 471 break; 472 473 default: 474 rdwrpart(r); 475 break; 476 } 477 } 478 479 void 480 fswrite(Req *r) 481 { 482 switch((int)r->fid->qid.path){ 483 case Qroot: 484 case Qdir: 485 respond(r, "write to a directory?"); 486 break; 487 488 case Qctl: 489 ctlwrite(r); 490 break; 491 492 default: 493 rdwrpart(r); 494 break; 495 } 496 } 497 498 void 499 fsopen(Req *r) 500 { 501 if(r->ifcall.mode&ORCLOSE) 502 respond(r, "cannot open ORCLOSE"); 503 504 switch((int)r->fid->qid.path){ 505 case Qroot: 506 case Qdir: 507 if(r->ifcall.mode != OREAD){ 508 respond(r, "bad mode for directory open"); 509 return; 510 } 511 } 512 513 respond(r, nil); 514 } 515 516 void 517 fsstat(Req *r) 518 { 519 int q; 520 Dir *d; 521 Part *p; 522 523 d = &r->d; 524 memset(d, 0, sizeof *d); 525 d->qid = r->fid->qid; 526 d->atime = d->mtime = time0; 527 q = r->fid->qid.path; 528 switch(q){ 529 case Qroot: 530 d->name = estrdup9p("/"); 531 d->mode = DMDIR|0777; 532 break; 533 534 case Qdir: 535 d->name = estrdup9p(sdname); 536 d->mode = DMDIR|0777; 537 break; 538 539 default: 540 q -= Qpart; 541 if(q < 0 || q > nelem(tab) || tab[q].inuse==0 || r->fid->qid.vers != tab[q].vers){ 542 respond(r, "partition no longer exists"); 543 return; 544 } 545 p = &tab[q]; 546 d->name = estrdup9p(p->name); 547 d->length = p->length * sectsize; 548 d->mode = p->mode; 549 break; 550 } 551 552 d->uid = estrdup9p("disksim"); 553 d->gid = estrdup9p("disksim"); 554 d->muid = estrdup9p(""); 555 respond(r, nil); 556 } 557 558 void 559 fsattach(Req *r) 560 { 561 char *spec; 562 563 spec = r->ifcall.aname; 564 if(spec && spec[0]){ 565 respond(r, "invalid attach specifier"); 566 return; 567 } 568 r->ofcall.qid = (Qid){Qroot, 0, QTDIR}; 569 r->fid->qid = r->ofcall.qid; 570 respond(r, nil); 571 } 572 573 char* 574 fswalk1(Fid *fid, char *name, Qid *qid) 575 { 576 int i; 577 switch((int)fid->qid.path){ 578 case Qroot: 579 if(strcmp(name, sdname) == 0){ 580 fid->qid.path = Qdir; 581 fid->qid.type = QTDIR; 582 *qid = fid->qid; 583 return nil; 584 } 585 break; 586 case Qdir: 587 if(strcmp(name, "ctl") == 0){ 588 fid->qid.path = Qctl; 589 fid->qid.vers = 0; 590 fid->qid.type = 0; 591 *qid = fid->qid; 592 return nil; 593 } 594 for(i=0; i<nelem(tab); i++){ 595 if(tab[i].inuse && strcmp(tab[i].name, name) == 0){ 596 fid->qid.path = i+Qpart; 597 fid->qid.vers = tab[i].vers; 598 fid->qid.type = 0; 599 *qid = fid->qid; 600 return nil; 601 } 602 } 603 break; 604 } 605 return "file not found"; 606 } 607 608 Srv fs = { 609 .attach= fsattach, 610 .open= fsopen, 611 .read= fsread, 612 .write= fswrite, 613 .stat= fsstat, 614 .walk1= fswalk1, 615 }; 616 617 char *mtpt = "/dev"; 618 char *srvname; 619 620 void 621 usage(void) 622 { 623 fprint(2, "usage: aux/disksim [-D] [-f file] [-s srvname] [-m mtpt] [sdXX]\n"); 624 fprint(2, "\tdefault mtpt is /dev\n"); 625 exits("usage"); 626 } 627 628 void 629 main(int argc, char **argv) 630 { 631 char *file; 632 633 file = nil; 634 quotefmtinstall(); 635 time0 = time(0); 636 if(NPTR != BLKSZ/sizeof(void*)) 637 sysfatal("unexpected pointer size"); 638 639 ARGBEGIN{ 640 case 'D': 641 chatty9p++; 642 break; 643 case 'f': 644 file = EARGF(usage()); 645 break; 646 case 'r': 647 rdonly = 1; 648 break; 649 case 's': 650 srvname = EARGF(usage()); 651 break; 652 case 'm': 653 mtpt = EARGF(usage()); 654 break; 655 default: 656 usage(); 657 }ARGEND 658 659 if(argc > 1) 660 usage(); 661 if(argc == 1) 662 sdname = argv[0]; 663 664 if(file){ 665 if((fd = open(file, rdonly ? OREAD : ORDWR)) < 0) 666 sysfatal("open %s: %r", file); 667 } 668 669 inquiry = estrdup9p(inquiry); 670 tab[0].name = estrdup9p("data"); 671 tab[0].inuse = 1; 672 tab[0].mode = 0666; 673 674 postmountsrv(&fs, srvname, mtpt, MBEFORE); 675 exits(nil); 676 } 677