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