xref: /plan9/sys/src/cmd/aux/disksim.c (revision 81cf874224a4620dc66ce2d894718f8bb735f81d)
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 
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 
70 char*
71 ctlstring(void)
72 {
73 	int i;
74 	Fmt fmt;
75 
76 	fmtstrinit(&fmt);
77 	fmtprint(&fmt, "inquiry %s\n", inquiry);
78 	fmtprint(&fmt, "geometry %lld %lld %lld %lld %lld\n", nsect, sectsize, c, h, s);
79 	for(i=0; i<nelem(tab); i++)
80 		if(tab[i].inuse)
81 			fmtprint(&fmt, "part %s %lld %lld\n", tab[i].name, tab[i].offset, tab[i].length);
82 	return fmtstrflush(&fmt);
83 }
84 
85 int
86 addpart(char *name, vlong start, vlong end)
87 {
88 	int i;
89 
90 	if(start < 0 || start > end || end > nsect){
91 		werrstr("bad partition boundaries");
92 		return -1;
93 	}
94 
95 	for(i=0; i<nelem(tab); i++)
96 		if(tab[i].inuse == 0)
97 			break;
98 	if(i == nelem(tab)){
99 		werrstr("no free partition slots");
100 		return -1;
101 	}
102 
103 	free(tab[i].name);
104 	tab[i].inuse = 1;
105 	tab[i].name = estrdup9p(name);
106 	tab[i].offset = start;
107 	tab[i].length = end - start;
108 	tab[i].mode = ctlmode;
109 	tab[i].vers++;
110 
111 	return 0;
112 }
113 
114 int
115 delpart(char *s)
116 {
117 	int i;
118 
119 	for(i=0; i<nelem(tab); i++)
120 		if(tab[i].inuse && strcmp(tab[i].name, s) == 0)
121 			break;
122 	if(i==nelem(tab)){
123 		werrstr("partition not found");
124 		return -1;
125 	}
126 
127 	tab[i].inuse = 0;
128 	free(tab[i].name);
129 	tab[i].name = 0;
130 	return 0;
131 }
132 
133 void
134 ctlwrite(Req *r)
135 {
136 	int i;
137 	Cmdbuf *cb;
138 	vlong start, end;
139 
140 	r->ofcall.count = r->ifcall.count;
141 	cb = parsecmd(r->ifcall.data, r->ifcall.count);
142 	if(cb->nf < 1){
143 		respond(r, "empty control message");
144 		free(cb);
145 		return;
146 	}
147 
148 	if(strcmp(cb->f[0], "part") == 0){
149 		if(cb->nf != 4){
150 			respondcmderror(r, cb, "part takes 3 args");
151 			free(cb);
152 			return;
153 		}
154 		start = strtoll(cb->f[2], 0, 0);
155 		end = strtoll(cb->f[3], 0, 0);
156 		if(addpart(cb->f[1], start, end) < 0){
157 			respondcmderror(r, cb, "%r");
158 			free(cb);
159 			return;
160 		}
161 	}
162 	else if(strcmp(cb->f[0], "delpart") == 0){
163 		if(cb->nf != 2){
164 			respondcmderror(r, cb, "delpart takes 1 arg");
165 			free(cb);
166 			return;
167 		}
168 		if(delpart(cb->f[1]) < 0){
169 			respondcmderror(r, cb, "%r");
170 			free(cb);
171 			return;
172 		}
173 	}
174 	else if(strcmp(cb->f[0], "inquiry") == 0){
175 		if(cb->nf != 2){
176 			respondcmderror(r, cb, "inquiry takes 1 arg");
177 			free(cb);
178 			return;
179 		}
180 		free(inquiry);
181 		inquiry = estrdup9p(cb->f[1]);
182 	}
183 	else if(strcmp(cb->f[0], "geometry") == 0){
184 		if(cb->nf != 6){
185 			respondcmderror(r, cb, "geometry takes 5 args");
186 			free(cb);
187 			return;
188 		}
189 		nsect = strtoll(cb->f[1], 0, 0);
190 		sectsize = strtoll(cb->f[2], 0, 0);
191 		c = strtoll(cb->f[3], 0, 0);
192 		h = strtoll(cb->f[4], 0, 0);
193 		s = strtoll(cb->f[5], 0, 0);
194 		if(tab[0].inuse && strcmp(tab[0].name, "data") == 0 && tab[0].vers == 0){
195 			tab[0].offset = 0;
196 			tab[0].length = nsect;
197 		}
198 		for(i=0; i<nelem(tab); i++){
199 			if(tab[i].inuse && tab[i].offset+tab[i].length > nsect){
200 				tab[i].inuse = 0;
201 				free(tab[i].name);
202 				tab[i].name = 0;
203 			}
204 		}
205 	}
206 	else{
207 		respondcmderror(r, cb, "unknown control message");
208 		free(cb);
209 		return;
210 	}
211 
212 	free(cb);
213 	respond(r, nil);
214 }
215 
216 void*
217 allocblk(void)
218 {
219 	uchar *op;
220 	static uchar *p;
221 	static ulong n;
222 
223 	if(n == 0){
224 		p = malloc(4*1024*1024);
225 		if(p == 0)
226 			sysfatal("out of memory");
227 		n = 4*1024*1024;
228 	}
229 	op = p;
230 	p += BLKSZ;
231 	n -= BLKSZ;
232 	memset(op, 0, BLKSZ);
233 	return op;
234 }
235 
236 uchar*
237 getblock(vlong addr, int alloc)
238 {
239 	static uchar zero[BLKSZ];
240  	Dbl *p2;
241 	Ind *p1;
242 	uchar *p0;
243 	uint i0, i1, i2;
244 
245 	addr >>= LOGBLKSZ;
246 	i0 = addr & (NPTR-1);
247 	addr >>= LOGNPTR;
248 	i1 = addr & (NPTR-1);
249 	addr >>= LOGNPTR;
250 	i2 = addr & (NPTR-1);
251 	addr >>= LOGNPTR;
252 	assert(addr == 0);
253 
254 	if((p2 = trip.dbl[i2]) == 0){
255 		if(!alloc)
256 			return zero;
257 		trip.dbl[i2] = p2 = allocblk();
258 	}
259 
260 	if((p1 = p2->ind[i1]) == 0){
261 		if(!alloc)
262 			return zero;
263 		p2->ind[i1] = p1 = allocblk();
264 	}
265 
266 	if((p0 = p1->blk[i0]) == 0){
267 		if(!alloc)
268 			return zero;
269 		p1->blk[i0] = p0 = allocblk();
270 	}
271 	return p0;
272 }
273 
274 int
275 rootgen(int off, Dir *d, void*)
276 {
277 	memset(d, 0, sizeof *d);
278 	d->atime = time0;
279 	d->mtime = time0;
280 	if(off == 0){
281 		d->name = estrdup9p(sdname);
282 		d->mode = DMDIR|0777;
283 		d->qid.path = Qdir;
284 		d->qid.type = QTDIR;
285 		d->uid = estrdup9p("disksim");
286 		d->gid = estrdup9p("disksim");
287 		d->muid = estrdup9p("");
288 		return 0;
289 	}
290 	return -1;
291 }
292 
293 int
294 dirgen(int off, Dir *d, void*)
295 {
296 	int n, j;
297 
298 	memset(d, 0, sizeof *d);
299 	d->atime = time0;
300 	d->mtime = time0;
301 	if(off == 0){
302 		d->name = estrdup9p("ctl");
303 		d->mode = ctlmode;
304 		d->qid.path = Qctl;
305 		goto Have;
306 	}
307 
308 	off--;
309 	n = 0;
310 	for(j=0; j<nelem(tab); j++){
311 		if(tab[j].inuse==0)
312 			continue;
313 		if(n == off){
314 			d->name = estrdup9p(tab[j].name);
315 			d->length = tab[j].length*sectsize;
316 			d->mode = tab[j].mode;
317 			d->qid.path = Qpart+j;
318 			d->qid.vers = tab[j].vers;
319 			goto Have;
320 		}
321 		n++;
322 	}
323 	return -1;
324 
325 Have:
326 	d->uid = estrdup9p("disksim");
327 	d->gid = estrdup9p("disksim");
328 	d->muid = estrdup9p("");
329 	return 0;
330 }
331 
332 void*
333 evommem(void *a, void *b, ulong n)
334 {
335 	return memmove(b, a, n);
336 }
337 
338 int
339 rdwrpart(Req *r)
340 {
341 	int q;
342 	Part *p;
343 	vlong offset;
344 	long count, tot, n, o;
345 	uchar *blk, *dat;
346 	void *(*move)(void*, void*, ulong);
347 
348 	q = r->fid->qid.path-Qpart;
349 	if(q < 0 || q > nelem(tab) || !tab[q].inuse || tab[q].vers != r->fid->qid.vers){
350 		respond(r, "unknown partition");
351 		return -1;
352 	}
353 
354 	p = &tab[q];
355 	offset = r->ifcall.offset;
356 	count = r->ifcall.count;
357 	if(offset < 0){
358 		respond(r, "negative offset");
359 		return -1;
360 	}
361 	if(count < 0){
362 		respond(r, "negative count");
363 		return -1;
364 	}
365 	if(offset > p->length*sectsize){
366 		respond(r, "offset past end of partition");
367 		return -1;
368 	}
369 	if(offset+count > p->length*sectsize)
370 		count = p->length*sectsize - offset;
371 	offset += p->offset*sectsize;
372 
373 	if(r->ifcall.type == Tread)
374 		move = memmove;
375 	else
376 		move = evommem;
377 
378 	tot = 0;
379 	if(r->ifcall.type == Tread)
380 		dat = (uchar*)r->ofcall.data;
381 	else
382 		dat = (uchar*)r->ifcall.data;
383 	o = offset & (BLKSZ-1);
384 
385 	/* left fringe block */
386 	if(o && count){
387 		blk = getblock(offset, r->ifcall.type==Twrite);
388 		if(blk == nil)
389 			abort();
390 		n = BLKSZ - o;
391 		if(n > count)
392 			n = count;
393 		(*move)(dat, blk+o, n);
394 		tot += n;
395 	}
396 	/* full and right fringe blocks */
397 	while(tot < count){
398 		blk = getblock(offset+tot, r->ifcall.type==Twrite);
399 		if(blk == nil)
400 			abort();
401 		n = BLKSZ;
402 		if(n > count)
403 			n = count;
404 		(*move)(dat+tot, blk, n);
405 		tot += n;
406 	}
407 	r->ofcall.count = tot;
408 	respond(r, nil);
409 	return 0;
410 }
411 
412 void
413 fsread(Req *r)
414 {
415 	char *s;
416 
417 	switch((int)r->fid->qid.path){
418 	case Qroot:
419 		dirread9p(r, rootgen, nil);
420 		respond(r, nil);
421 		break;
422 
423 	case Qdir:
424 		dirread9p(r, dirgen, nil);
425 		respond(r, nil);
426 		break;
427 
428 	case Qctl:
429 		s = ctlstring();
430 		readstr(r, s);
431 		free(s);
432 		respond(r, nil);
433 		break;
434 
435 	default:
436 		rdwrpart(r);
437 		break;
438 	}
439 }
440 
441 void
442 fswrite(Req *r)
443 {
444 	switch((int)r->fid->qid.path){
445 	case Qroot:
446 	case Qdir:
447 		respond(r, "write to a directory?");
448 		break;
449 
450 	case Qctl:
451 		ctlwrite(r);
452 		break;
453 
454 	default:
455 		rdwrpart(r);
456 		break;
457 	}
458 }
459 
460 void
461 fsopen(Req *r)
462 {
463 	if(r->ifcall.mode&ORCLOSE)
464 		respond(r, "cannot open ORCLOSE");
465 
466 	switch((int)r->fid->qid.path){
467 	case Qroot:
468 	case Qdir:
469 		if(r->ifcall.mode != OREAD){
470 			respond(r, "bad mode for directory open");
471 			return;
472 		}
473 	}
474 
475 	respond(r, nil);
476 }
477 
478 void
479 fsstat(Req *r)
480 {
481 	int q;
482 	Dir *d;
483 	Part *p;
484 
485 	d = &r->d;
486 	memset(d, 0, sizeof *d);
487 	d->qid = r->fid->qid;
488 	d->atime = d->mtime = time0;
489 	q = r->fid->qid.path;
490 	switch(q){
491 	case Qroot:
492 		d->name = estrdup9p("/");
493 		d->mode = DMDIR|0777;
494 		break;
495 
496 	case Qdir:
497 		d->name = estrdup9p(sdname);
498 		d->mode = DMDIR|0777;
499 		break;
500 
501 	default:
502 		q -= Qpart;
503 		if(q < 0 || q > nelem(tab) || tab[q].inuse==0 || r->fid->qid.vers != tab[q].vers){
504 			respond(r, "partition no longer exists");
505 			return;
506 		}
507 		p = &tab[q];
508 		d->name = estrdup9p(p->name);
509 		d->length = p->length * sectsize;
510 		d->mode = p->mode;
511 		break;
512 	}
513 
514 	d->uid = estrdup9p("disksim");
515 	d->gid = estrdup9p("disksim");
516 	d->muid = estrdup9p("");
517 	respond(r, nil);
518 }
519 
520 void
521 fsattach(Req *r)
522 {
523 	char *spec;
524 
525 	spec = r->ifcall.aname;
526 	if(spec && spec[0]){
527 		respond(r, "invalid attach specifier");
528 		return;
529 	}
530 	r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
531 	r->fid->qid = r->ofcall.qid;
532 	respond(r, nil);
533 }
534 
535 char*
536 fswalk1(Fid *fid, char *name, Qid *qid)
537 {
538 	int i;
539 	switch((int)fid->qid.path){
540 	case Qroot:
541 		if(strcmp(name, sdname) == 0){
542 			fid->qid.path = Qdir;
543 			fid->qid.type = QTDIR;
544 			*qid = fid->qid;
545 			return nil;
546 		}
547 		break;
548 	case Qdir:
549 		if(strcmp(name, "ctl") == 0){
550 			fid->qid.path = Qctl;
551 			fid->qid.vers = 0;
552 			fid->qid.type = 0;
553 			*qid = fid->qid;
554 			return nil;
555 		}
556 		for(i=0; i<nelem(tab); i++){
557 			if(tab[i].inuse && strcmp(tab[i].name, name) == 0){
558 				fid->qid.path = i+Qpart;
559 				fid->qid.vers = tab[i].vers;
560 				fid->qid.type = 0;
561 				*qid = fid->qid;
562 				return nil;
563 			}
564 		}
565 		break;
566 	}
567 	return "file not found";
568 }
569 
570 Srv fs = {
571 	.attach=	fsattach,
572 	.open=	fsopen,
573 	.read=	fsread,
574 	.write=	fswrite,
575 	.stat=	fsstat,
576 	.walk1=	fswalk1,
577 };
578 
579 char *mtpt = "/dev";
580 char *srvname;
581 
582 void
583 usage(void)
584 {
585 	fprint(2, "usage: aux/disksim [-D] [-s srvname] [-m mtpt] [sdXX]\n");
586 	fprint(2, "\tdefault mtpt is /dev\n");
587 	exits("usage");
588 }
589 
590 void
591 main(int argc, char **argv)
592 {
593 	quotefmtinstall();
594 	time0 = time(0);
595 	if(NPTR != BLKSZ/sizeof(void*))
596 		sysfatal("unexpected pointer size");
597 
598 	ARGBEGIN{
599 	case 'D':
600 		chatty9p++;
601 		break;
602 	case 's':
603 		srvname = EARGF(usage());
604 		break;
605 	case 'm':
606 		mtpt = EARGF(usage());
607 		break;
608 	default:
609 		usage();
610 	}ARGEND
611 
612 	if(argc > 1)
613 		usage();
614 	if(argc == 1)
615 		sdname = argv[0];
616 
617 	inquiry = estrdup9p(inquiry);
618 	tab[0].name = estrdup9p("data");
619 	tab[0].inuse = 1;
620 	tab[0].mode = 0666;
621 
622 	postmountsrv(&fs, srvname, mtpt, MBEFORE);
623 	exits(nil);
624 }
625