]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/disksim.c
3d261ea54d70b0e6d8e84e7910a7acefe28fac4b
[plan9front.git] / sys / src / cmd / aux / disksim.c
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         NPTR = BLKSZ/sizeof(void*),
24 };
25 static uchar zero[BLKSZ];
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         Dbl *p2;
243         Ind *p1;
244         uchar *p0;
245         uint i0, i1, i2;
246         vlong oaddr;
247
248         if(fd >= 0)
249                 alloc = 1;
250
251         addr >>= LOGBLKSZ;
252         oaddr = addr<<LOGBLKSZ;
253         i0 = addr % NPTR;
254         addr /= NPTR;
255         i1 = addr % NPTR;
256         addr /= NPTR;
257         i2 = addr % NPTR;
258         addr /= NPTR;
259         assert(addr == 0);
260
261         if((p2 = trip.dbl[i2]) == 0){
262                 if(!alloc)
263                         return zero;
264                 trip.dbl[i2] = p2 = allocblk(-1);
265         }
266
267         if((p1 = p2->ind[i1]) == 0){
268                 if(!alloc)
269                         return zero;
270                 p2->ind[i1] = p1 = allocblk(-1);
271         }
272
273         if((p0 = p1->blk[i0]) == 0){
274                 if(!alloc)
275                         return zero;
276                 p1->blk[i0] = p0 = allocblk(oaddr);
277         }
278         return p0;
279 }
280
281 void
282 dirty(vlong addr, uchar *buf)
283 {
284         vlong oaddr;
285
286         if(fd == -1 || rdonly)
287                 return;
288         oaddr = addr&~((vlong)BLKSZ-1);
289         if(pwrite(fd, buf, BLKSZ, oaddr) != BLKSZ)
290                 sysfatal("write: %r");
291 }
292         
293 int
294 rootgen(int off, Dir *d, void*)
295 {
296         memset(d, 0, sizeof *d);
297         d->atime = time0;
298         d->mtime = time0;
299         if(off == 0){
300                 d->name = estrdup9p(sdname);
301                 d->mode = DMDIR|0777;
302                 d->qid.path = Qdir;
303                 d->qid.type = QTDIR;
304                 d->uid = estrdup9p("disksim");
305                 d->gid = estrdup9p("disksim");
306                 d->muid = estrdup9p("");
307                 return 0;
308         }
309         return -1;
310 }       
311                 
312 int
313 dirgen(int off, Dir *d, void*)
314 {
315         int n, j;
316
317         memset(d, 0, sizeof *d);
318         d->atime = time0;
319         d->mtime = time0;
320         if(off == 0){
321                 d->name = estrdup9p("ctl");
322                 d->mode = ctlmode;
323                 d->qid.path = Qctl;
324                 goto Have;
325         }
326
327         off--;
328         n = 0;
329         for(j=0; j<nelem(tab); j++){
330                 if(tab[j].inuse==0)
331                         continue;
332                 if(n == off){
333                         d->name = estrdup9p(tab[j].name);
334                         d->length = tab[j].length*sectsize;
335                         d->mode = tab[j].mode;
336                         d->qid.path = Qpart+j;
337                         d->qid.vers = tab[j].vers;
338                         goto Have;
339                 }
340                 n++;
341         }
342         return -1;
343
344 Have:
345         d->uid = estrdup9p("disksim");
346         d->gid = estrdup9p("disksim");
347         d->muid = estrdup9p("");
348         return 0;
349 }
350
351 void*
352 evommem(void *a, void *b, ulong n)
353 {
354         return memmove(b, a, n);
355 }
356
357 int
358 isnonzero(void *v, ulong n)
359 {
360         uchar *a, *ea;
361         
362         a = v;
363         ea = a+n;
364         for(; a<ea; a++)
365                 if(*a)
366                         return 1;
367         return 0;
368 }
369
370 int
371 rdwrpart(Req *r)
372 {
373         int q, nonzero;
374         Part *p;
375         vlong offset;
376         long count, tot, n, o;
377         uchar *blk, *dat;
378         void *(*move)(void*, void*, ulong);
379
380         q = r->fid->qid.path-Qpart;
381         if(q < 0 || q > nelem(tab) || !tab[q].inuse || tab[q].vers != r->fid->qid.vers){
382                 respond(r, "unknown partition");
383                 return -1;
384         }
385
386         p = &tab[q];
387         offset = r->ifcall.offset;
388         count = r->ifcall.count;
389         if(offset < 0){
390                 respond(r, "negative offset");
391                 return -1;
392         }
393         if(count < 0){
394                 respond(r, "negative count");
395                 return -1;
396         }
397         if(offset > p->length*sectsize){
398                 respond(r, "offset past end of partition");
399                 return -1;
400         }
401         if(offset+count > p->length*sectsize)
402                 count = p->length*sectsize - offset;
403         offset += p->offset*sectsize;
404
405         if(r->ifcall.type == Tread)
406                 move = memmove;
407         else
408                 move = evommem;
409
410         tot = 0;
411         nonzero = 1;
412         if(r->ifcall.type == Tread)
413                 dat = (uchar*)r->ofcall.data;
414         else{
415                 dat = (uchar*)r->ifcall.data;
416                 nonzero = isnonzero(dat, r->ifcall.count);
417         }
418         o = offset & (BLKSZ-1);
419
420         /* left fringe block */
421         if(o && count){
422                 blk = getblock(offset, r->ifcall.type==Twrite && nonzero);
423                 n = BLKSZ - o;
424                 if(n > count)
425                         n = count;
426                 if(r->ifcall.type != Twrite || blk != zero)
427                         (*move)(dat, blk+o, n);
428                 if(r->ifcall.type == Twrite)
429                         dirty(offset, blk);
430                 tot += n;
431         }
432         /* full and right fringe blocks */
433         while(tot < count){
434                 blk = getblock(offset+tot, r->ifcall.type==Twrite && nonzero);
435                 n = BLKSZ;
436                 if(n > count-tot)
437                         n = count-tot;
438                 if(r->ifcall.type != Twrite || blk != zero)
439                         (*move)(dat+tot, blk, n);
440                 if(r->ifcall.type == Twrite)
441                         dirty(offset+tot, blk);
442                 tot += n;
443         }
444         r->ofcall.count = tot;
445         respond(r, nil);
446         return 0;
447 }
448
449 void
450 fsread(Req *r)
451 {
452         char *s;
453
454         switch((int)r->fid->qid.path){
455         case Qroot:
456                 dirread9p(r, rootgen, nil);
457                 respond(r, nil);
458                 break;
459
460         case Qdir:
461                 dirread9p(r, dirgen, nil);
462                 respond(r, nil);
463                 break;
464
465         case Qctl:
466                 s = ctlstring();
467                 readstr(r, s);
468                 free(s);
469                 respond(r, nil);
470                 break;
471
472         default:
473                 rdwrpart(r);
474                 break;
475         }
476 }
477
478 void
479 fswrite(Req *r)
480 {
481         switch((int)r->fid->qid.path){
482         case Qroot:
483         case Qdir:
484                 respond(r, "write to a directory?");
485                 break;
486
487         case Qctl:
488                 ctlwrite(r);
489                 break;
490
491         default:
492                 rdwrpart(r);
493                 break;
494         }
495 }
496
497 void
498 fsopen(Req *r)
499 {
500         if(r->ifcall.mode&ORCLOSE)
501                 respond(r, "cannot open ORCLOSE");
502
503         switch((int)r->fid->qid.path){
504         case Qroot:
505         case Qdir:
506                 if(r->ifcall.mode != OREAD){
507                         respond(r, "bad mode for directory open");
508                         return;
509                 }
510         }
511
512         respond(r, nil);
513 }
514
515 void
516 fsstat(Req *r)
517 {
518         int q;
519         Dir *d;
520         Part *p;
521
522         d = &r->d;
523         memset(d, 0, sizeof *d);
524         d->qid = r->fid->qid;
525         d->atime = d->mtime = time0;
526         q = r->fid->qid.path;
527         switch(q){
528         case Qroot:
529                 d->name = estrdup9p("/");
530                 d->mode = DMDIR|0777;
531                 break;
532
533         case Qdir:
534                 d->name = estrdup9p(sdname);
535                 d->mode = DMDIR|0777;
536                 break;
537
538         default:
539                 q -= Qpart;
540                 if(q < 0 || q > nelem(tab) || tab[q].inuse==0 || r->fid->qid.vers != tab[q].vers){
541                         respond(r, "partition no longer exists");
542                         return;
543                 }
544                 p = &tab[q];
545                 d->name = estrdup9p(p->name);
546                 d->length = p->length * sectsize;
547                 d->mode = p->mode;
548                 break;
549         }
550                 
551         d->uid = estrdup9p("disksim");
552         d->gid = estrdup9p("disksim");
553         d->muid = estrdup9p("");
554         respond(r, nil);
555 }
556
557 void
558 fsattach(Req *r)
559 {
560         char *spec;
561
562         spec = r->ifcall.aname;
563         if(spec && spec[0]){
564                 respond(r, "invalid attach specifier");
565                 return;
566         }
567         r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
568         r->fid->qid = r->ofcall.qid;
569         respond(r, nil);
570 }
571
572 char*
573 fswalk1(Fid *fid, char *name, Qid *qid)
574 {
575         int i;
576         switch((int)fid->qid.path){
577         case Qroot:
578                 if(strcmp(name, sdname) == 0){
579                         fid->qid.path = Qdir;
580                         fid->qid.type = QTDIR;
581                         *qid = fid->qid;
582                         return nil;
583                 }
584                 break;
585         case Qdir:
586                 if(strcmp(name, "ctl") == 0){
587                         fid->qid.path = Qctl;
588                         fid->qid.vers = 0;
589                         fid->qid.type = 0;
590                         *qid = fid->qid;
591                         return nil;
592                 }
593                 for(i=0; i<nelem(tab); i++){
594                         if(tab[i].inuse && strcmp(tab[i].name, name) == 0){
595                                 fid->qid.path = i+Qpart;
596                                 fid->qid.vers = tab[i].vers;
597                                 fid->qid.type = 0;
598                                 *qid = fid->qid;
599                                 return nil;
600                         }
601                 }
602                 break;
603         }
604         return "file not found";
605 }
606
607 Srv fs = {
608         .attach=        fsattach,
609         .open=  fsopen,
610         .read=  fsread,
611         .write= fswrite,
612         .stat=  fsstat,
613         .walk1= fswalk1,
614 };
615
616 char *mtpt = "/dev";
617 char *srvname;
618
619 void
620 usage(void)
621 {
622         fprint(2, "usage: aux/disksim [-D] [-f file] [-s srvname] [-m mtpt] [sdXX]\n");
623         fprint(2, "\tdefault mtpt is /dev\n");
624         exits("usage");
625 }
626
627 void
628 main(int argc, char **argv)
629 {
630         char *file;
631
632         file = nil;
633         quotefmtinstall();
634         time0 = time(0);
635
636         ARGBEGIN{
637         case 'D':
638                 chatty9p++;
639                 break;
640         case 'f':
641                 file = EARGF(usage());
642                 break;
643         case 'r':
644                 rdonly = 1;
645                 break;
646         case 's':
647                 srvname = EARGF(usage());
648                 break;
649         case 'm':
650                 mtpt = EARGF(usage());
651                 break;
652         default:
653                 usage();
654         }ARGEND
655
656         if(argc > 1)
657                 usage();
658         if(argc == 1)
659                 sdname = argv[0];
660
661         if(file){
662                 if((fd = open(file, rdonly ? OREAD : ORDWR)) < 0)
663                         sysfatal("open %s: %r", file);
664         }
665
666         inquiry = estrdup9p(inquiry);
667         tab[0].name = estrdup9p("data");
668         tab[0].inuse = 1;
669         tab[0].mode = 0666;
670
671         postmountsrv(&fs, srvname, mtpt, MBEFORE);
672         exits(nil);
673 }