xref: /plan9/sys/src/cmd/aux/disksim.c (revision ec46fab06dcae3e636b775c4eaa679036316e1d8)
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