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*
ctlstring(void)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
addpart(char * name,vlong start,vlong end)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
delpart(char * s)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
ctlwrite(Req * r)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*
allocblk(vlong addr)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*
getblock(vlong addr,int alloc)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
dirty(vlong addr,uchar * buf)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
rootgen(int off,Dir * d,void *)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
dirgen(int off,Dir * d,void *)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*
evommem(void * a,void * b,ulong n)353 evommem(void *a, void *b, ulong n)
354 {
355 return memmove(b, a, n);
356 }
357
358 int
isnonzero(void * v,ulong n)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
rdwrpart(Req * r)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
fsread(Req * r)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
fswrite(Req * r)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
fsopen(Req * r)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
fsstat(Req * r)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
fsattach(Req * r)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*
fswalk1(Fid * fid,char * name,Qid * qid)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
usage(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
main(int argc,char ** argv)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