xref: /plan9/sys/src/cmd/aux/disksim.c (revision 11e1fb05e6e53d365d5a9dab7cd6360fed28781a)
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 
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 	static uchar zero[BLKSZ];
243  	Dbl *p2;
244 	Ind *p1;
245 	uchar *p0;
246 	uint i0, i1, i2;
247 	vlong oaddr;
248 
249 	if(fd)
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 rdwrpart(Req *r)
360 {
361 	int q;
362 	Part *p;
363 	vlong offset;
364 	long count, tot, n, o;
365 	uchar *blk, *dat;
366 	void *(*move)(void*, void*, ulong);
367 
368 	q = r->fid->qid.path-Qpart;
369 	if(q < 0 || q > nelem(tab) || !tab[q].inuse || tab[q].vers != r->fid->qid.vers){
370 		respond(r, "unknown partition");
371 		return -1;
372 	}
373 
374 	p = &tab[q];
375 	offset = r->ifcall.offset;
376 	count = r->ifcall.count;
377 	if(offset < 0){
378 		respond(r, "negative offset");
379 		return -1;
380 	}
381 	if(count < 0){
382 		respond(r, "negative count");
383 		return -1;
384 	}
385 	if(offset > p->length*sectsize){
386 		respond(r, "offset past end of partition");
387 		return -1;
388 	}
389 	if(offset+count > p->length*sectsize)
390 		count = p->length*sectsize - offset;
391 	offset += p->offset*sectsize;
392 
393 	if(r->ifcall.type == Tread)
394 		move = memmove;
395 	else
396 		move = evommem;
397 
398 	tot = 0;
399 	if(r->ifcall.type == Tread)
400 		dat = (uchar*)r->ofcall.data;
401 	else
402 		dat = (uchar*)r->ifcall.data;
403 	o = offset & (BLKSZ-1);
404 
405 	/* left fringe block */
406 	if(o && count){
407 		blk = getblock(offset, r->ifcall.type==Twrite);
408 		if(blk == nil)
409 			abort();
410 		n = BLKSZ - o;
411 		if(n > count)
412 			n = count;
413 		(*move)(dat, blk+o, n);
414 		if(r->ifcall.type == Twrite)
415 			dirty(offset, blk);
416 		tot += n;
417 	}
418 	/* full and right fringe blocks */
419 	while(tot < count){
420 		blk = getblock(offset+tot, r->ifcall.type==Twrite);
421 		if(blk == nil)
422 			abort();
423 		n = BLKSZ;
424 		if(n > count-tot)
425 			n = count-tot;
426 		(*move)(dat+tot, blk, n);
427 		if(r->ifcall.type == Twrite)
428 			dirty(offset+tot, blk);
429 		tot += n;
430 	}
431 	r->ofcall.count = tot;
432 	respond(r, nil);
433 	return 0;
434 }
435 
436 void
437 fsread(Req *r)
438 {
439 	char *s;
440 
441 	switch((int)r->fid->qid.path){
442 	case Qroot:
443 		dirread9p(r, rootgen, nil);
444 		respond(r, nil);
445 		break;
446 
447 	case Qdir:
448 		dirread9p(r, dirgen, nil);
449 		respond(r, nil);
450 		break;
451 
452 	case Qctl:
453 		s = ctlstring();
454 		readstr(r, s);
455 		free(s);
456 		respond(r, nil);
457 		break;
458 
459 	default:
460 		rdwrpart(r);
461 		break;
462 	}
463 }
464 
465 void
466 fswrite(Req *r)
467 {
468 	switch((int)r->fid->qid.path){
469 	case Qroot:
470 	case Qdir:
471 		respond(r, "write to a directory?");
472 		break;
473 
474 	case Qctl:
475 		ctlwrite(r);
476 		break;
477 
478 	default:
479 		rdwrpart(r);
480 		break;
481 	}
482 }
483 
484 void
485 fsopen(Req *r)
486 {
487 	if(r->ifcall.mode&ORCLOSE)
488 		respond(r, "cannot open ORCLOSE");
489 
490 	switch((int)r->fid->qid.path){
491 	case Qroot:
492 	case Qdir:
493 		if(r->ifcall.mode != OREAD){
494 			respond(r, "bad mode for directory open");
495 			return;
496 		}
497 	}
498 
499 	respond(r, nil);
500 }
501 
502 void
503 fsstat(Req *r)
504 {
505 	int q;
506 	Dir *d;
507 	Part *p;
508 
509 	d = &r->d;
510 	memset(d, 0, sizeof *d);
511 	d->qid = r->fid->qid;
512 	d->atime = d->mtime = time0;
513 	q = r->fid->qid.path;
514 	switch(q){
515 	case Qroot:
516 		d->name = estrdup9p("/");
517 		d->mode = DMDIR|0777;
518 		break;
519 
520 	case Qdir:
521 		d->name = estrdup9p(sdname);
522 		d->mode = DMDIR|0777;
523 		break;
524 
525 	default:
526 		q -= Qpart;
527 		if(q < 0 || q > nelem(tab) || tab[q].inuse==0 || r->fid->qid.vers != tab[q].vers){
528 			respond(r, "partition no longer exists");
529 			return;
530 		}
531 		p = &tab[q];
532 		d->name = estrdup9p(p->name);
533 		d->length = p->length * sectsize;
534 		d->mode = p->mode;
535 		break;
536 	}
537 
538 	d->uid = estrdup9p("disksim");
539 	d->gid = estrdup9p("disksim");
540 	d->muid = estrdup9p("");
541 	respond(r, nil);
542 }
543 
544 void
545 fsattach(Req *r)
546 {
547 	char *spec;
548 
549 	spec = r->ifcall.aname;
550 	if(spec && spec[0]){
551 		respond(r, "invalid attach specifier");
552 		return;
553 	}
554 	r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
555 	r->fid->qid = r->ofcall.qid;
556 	respond(r, nil);
557 }
558 
559 char*
560 fswalk1(Fid *fid, char *name, Qid *qid)
561 {
562 	int i;
563 	switch((int)fid->qid.path){
564 	case Qroot:
565 		if(strcmp(name, sdname) == 0){
566 			fid->qid.path = Qdir;
567 			fid->qid.type = QTDIR;
568 			*qid = fid->qid;
569 			return nil;
570 		}
571 		break;
572 	case Qdir:
573 		if(strcmp(name, "ctl") == 0){
574 			fid->qid.path = Qctl;
575 			fid->qid.vers = 0;
576 			fid->qid.type = 0;
577 			*qid = fid->qid;
578 			return nil;
579 		}
580 		for(i=0; i<nelem(tab); i++){
581 			if(tab[i].inuse && strcmp(tab[i].name, name) == 0){
582 				fid->qid.path = i+Qpart;
583 				fid->qid.vers = tab[i].vers;
584 				fid->qid.type = 0;
585 				*qid = fid->qid;
586 				return nil;
587 			}
588 		}
589 		break;
590 	}
591 	return "file not found";
592 }
593 
594 Srv fs = {
595 	.attach=	fsattach,
596 	.open=	fsopen,
597 	.read=	fsread,
598 	.write=	fswrite,
599 	.stat=	fsstat,
600 	.walk1=	fswalk1,
601 };
602 
603 char *mtpt = "/dev";
604 char *srvname;
605 
606 void
607 usage(void)
608 {
609 	fprint(2, "usage: aux/disksim [-D] [-f file] [-s srvname] [-m mtpt] [sdXX]\n");
610 	fprint(2, "\tdefault mtpt is /dev\n");
611 	exits("usage");
612 }
613 
614 void
615 main(int argc, char **argv)
616 {
617 	char *file;
618 
619 	file = nil;
620 	quotefmtinstall();
621 	time0 = time(0);
622 	if(NPTR != BLKSZ/sizeof(void*))
623 		sysfatal("unexpected pointer size");
624 
625 	ARGBEGIN{
626 	case 'D':
627 		chatty9p++;
628 		break;
629 	case 'f':
630 		file = EARGF(usage());
631 		break;
632 	case 'r':
633 		rdonly = 1;
634 		break;
635 	case 's':
636 		srvname = EARGF(usage());
637 		break;
638 	case 'm':
639 		mtpt = EARGF(usage());
640 		break;
641 	default:
642 		usage();
643 	}ARGEND
644 
645 	if(argc > 1)
646 		usage();
647 	if(argc == 1)
648 		sdname = argv[0];
649 
650 	if(file){
651 		if((fd = open(file, rdonly ? OREAD : ORDWR)) < 0)
652 			sysfatal("open %s: %r", file);
653 	}
654 
655 	inquiry = estrdup9p(inquiry);
656 	tab[0].name = estrdup9p("data");
657 	tab[0].inuse = 1;
658 	tab[0].mode = 0666;
659 
660 	postmountsrv(&fs, srvname, mtpt, MBEFORE);
661 	exits(nil);
662 }
663