xref: /plan9/sys/src/cmd/aux/disksim.c (revision bbd061d49f2d27022e38642b8a1e5862b40931a7)
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 next:
434 	/* full and right fringe blocks */
435 	while(tot < count){
436 		blk = getblock(offset+tot, r->ifcall.type==Twrite && nonzero);
437 		n = BLKSZ;
438 		if(n > count-tot)
439 			n = count-tot;
440 		if(r->ifcall.type != Twrite || blk != zero)
441 			(*move)(dat+tot, blk, n);
442 		if(r->ifcall.type == Twrite)
443 			dirty(offset+tot, blk);
444 		tot += n;
445 	}
446 	r->ofcall.count = tot;
447 	respond(r, nil);
448 	return 0;
449 }
450 
451 void
452 fsread(Req *r)
453 {
454 	char *s;
455 
456 	switch((int)r->fid->qid.path){
457 	case Qroot:
458 		dirread9p(r, rootgen, nil);
459 		respond(r, nil);
460 		break;
461 
462 	case Qdir:
463 		dirread9p(r, dirgen, nil);
464 		respond(r, nil);
465 		break;
466 
467 	case Qctl:
468 		s = ctlstring();
469 		readstr(r, s);
470 		free(s);
471 		respond(r, nil);
472 		break;
473 
474 	default:
475 		rdwrpart(r);
476 		break;
477 	}
478 }
479 
480 void
481 fswrite(Req *r)
482 {
483 	switch((int)r->fid->qid.path){
484 	case Qroot:
485 	case Qdir:
486 		respond(r, "write to a directory?");
487 		break;
488 
489 	case Qctl:
490 		ctlwrite(r);
491 		break;
492 
493 	default:
494 		rdwrpart(r);
495 		break;
496 	}
497 }
498 
499 void
500 fsopen(Req *r)
501 {
502 	if(r->ifcall.mode&ORCLOSE)
503 		respond(r, "cannot open ORCLOSE");
504 
505 	switch((int)r->fid->qid.path){
506 	case Qroot:
507 	case Qdir:
508 		if(r->ifcall.mode != OREAD){
509 			respond(r, "bad mode for directory open");
510 			return;
511 		}
512 	}
513 
514 	respond(r, nil);
515 }
516 
517 void
518 fsstat(Req *r)
519 {
520 	int q;
521 	Dir *d;
522 	Part *p;
523 
524 	d = &r->d;
525 	memset(d, 0, sizeof *d);
526 	d->qid = r->fid->qid;
527 	d->atime = d->mtime = time0;
528 	q = r->fid->qid.path;
529 	switch(q){
530 	case Qroot:
531 		d->name = estrdup9p("/");
532 		d->mode = DMDIR|0777;
533 		break;
534 
535 	case Qdir:
536 		d->name = estrdup9p(sdname);
537 		d->mode = DMDIR|0777;
538 		break;
539 
540 	default:
541 		q -= Qpart;
542 		if(q < 0 || q > nelem(tab) || tab[q].inuse==0 || r->fid->qid.vers != tab[q].vers){
543 			respond(r, "partition no longer exists");
544 			return;
545 		}
546 		p = &tab[q];
547 		d->name = estrdup9p(p->name);
548 		d->length = p->length * sectsize;
549 		d->mode = p->mode;
550 		break;
551 	}
552 
553 	d->uid = estrdup9p("disksim");
554 	d->gid = estrdup9p("disksim");
555 	d->muid = estrdup9p("");
556 	respond(r, nil);
557 }
558 
559 void
560 fsattach(Req *r)
561 {
562 	char *spec;
563 
564 	spec = r->ifcall.aname;
565 	if(spec && spec[0]){
566 		respond(r, "invalid attach specifier");
567 		return;
568 	}
569 	r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
570 	r->fid->qid = r->ofcall.qid;
571 	respond(r, nil);
572 }
573 
574 char*
575 fswalk1(Fid *fid, char *name, Qid *qid)
576 {
577 	int i;
578 	switch((int)fid->qid.path){
579 	case Qroot:
580 		if(strcmp(name, sdname) == 0){
581 			fid->qid.path = Qdir;
582 			fid->qid.type = QTDIR;
583 			*qid = fid->qid;
584 			return nil;
585 		}
586 		break;
587 	case Qdir:
588 		if(strcmp(name, "ctl") == 0){
589 			fid->qid.path = Qctl;
590 			fid->qid.vers = 0;
591 			fid->qid.type = 0;
592 			*qid = fid->qid;
593 			return nil;
594 		}
595 		for(i=0; i<nelem(tab); i++){
596 			if(tab[i].inuse && strcmp(tab[i].name, name) == 0){
597 				fid->qid.path = i+Qpart;
598 				fid->qid.vers = tab[i].vers;
599 				fid->qid.type = 0;
600 				*qid = fid->qid;
601 				return nil;
602 			}
603 		}
604 		break;
605 	}
606 	return "file not found";
607 }
608 
609 Srv fs = {
610 	.attach=	fsattach,
611 	.open=	fsopen,
612 	.read=	fsread,
613 	.write=	fswrite,
614 	.stat=	fsstat,
615 	.walk1=	fswalk1,
616 };
617 
618 char *mtpt = "/dev";
619 char *srvname;
620 
621 void
622 usage(void)
623 {
624 	fprint(2, "usage: aux/disksim [-D] [-f file] [-s srvname] [-m mtpt] [sdXX]\n");
625 	fprint(2, "\tdefault mtpt is /dev\n");
626 	exits("usage");
627 }
628 
629 void
630 main(int argc, char **argv)
631 {
632 	char *file;
633 
634 	file = nil;
635 	quotefmtinstall();
636 	time0 = time(0);
637 	if(NPTR != BLKSZ/sizeof(void*))
638 		sysfatal("unexpected pointer size");
639 
640 	ARGBEGIN{
641 	case 'D':
642 		chatty9p++;
643 		break;
644 	case 'f':
645 		file = EARGF(usage());
646 		break;
647 	case 'r':
648 		rdonly = 1;
649 		break;
650 	case 's':
651 		srvname = EARGF(usage());
652 		break;
653 	case 'm':
654 		mtpt = EARGF(usage());
655 		break;
656 	default:
657 		usage();
658 	}ARGEND
659 
660 	if(argc > 1)
661 		usage();
662 	if(argc == 1)
663 		sdname = argv[0];
664 
665 	if(file){
666 		if((fd = open(file, rdonly ? OREAD : ORDWR)) < 0)
667 			sysfatal("open %s: %r", file);
668 	}
669 
670 	inquiry = estrdup9p(inquiry);
671 	tab[0].name = estrdup9p("data");
672 	tab[0].inuse = 1;
673 	tab[0].mode = 0666;
674 
675 	postmountsrv(&fs, srvname, mtpt, MBEFORE);
676 	exits(nil);
677 }
678