]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/disksim.c
exec(2): fix prototypes
[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         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 }