xref: /plan9/sys/src/cmd/disk/partfs.c (revision 23173ec17955c609738f37acefe0c6e72a169a75)
1 /*
2  * partfs - serve an underlying file, with devsd-style partitions
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <auth.h>
7 #include <fcall.h>
8 #include <thread.h>
9 #include <9p.h>
10 
11 typedef struct Part Part;
12 struct Part
13 {
14 	int	inuse;
15 	int	vers;
16 	ulong	mode;
17 	char	*name;
18 	vlong	offset;		/* in sectors */
19 	vlong	length;		/* in sectors */
20 };
21 
22 enum
23 {
24 	Qroot = 0,
25 	Qdir,
26 	Qctl,
27 	Qpart,
28 };
29 
30 int fd = -1, ctlfd = -1;
31 int rdonly;
32 ulong ctlmode = 0666;
33 ulong time0;
34 vlong nsect, sectsize;
35 
36 char *inquiry = "partfs hard drive";
37 char *sdname = "sdXX";
38 Part tab[64];
39 
40 char*
ctlstring(void)41 ctlstring(void)
42 {
43 	Part *p;
44 	Fmt fmt;
45 
46 	fmtstrinit(&fmt);
47 	fmtprint(&fmt, "inquiry %s\n", inquiry);
48 	fmtprint(&fmt, "geometry %lld %lld\n", nsect, sectsize);
49 	for (p = tab; p < tab + nelem(tab); p++)
50 		if (p->inuse)
51 			fmtprint(&fmt, "part %s %lld %lld\n",
52 				p->name, p->offset, p->length);
53 	return fmtstrflush(&fmt);
54 }
55 
56 int
addpart(char * name,vlong start,vlong end)57 addpart(char *name, vlong start, vlong end)
58 {
59 	Part *p;
60 
61 	if(start < 0 || start > end || end > nsect){
62 		werrstr("bad partition boundaries");
63 		return -1;
64 	}
65 
66 	if (strcmp(name, "ctl") == 0 || strcmp(name, "data") == 0) {
67 		werrstr("partition name already in use");
68 		return -1;
69 	}
70 	for (p = tab; p < tab + nelem(tab) && p->inuse; p++)
71 		if (strcmp(p->name, name) == 0) {
72 			werrstr("partition name already in use");
73 			return -1;
74 		}
75 	if(p == tab + nelem(tab)){
76 		werrstr("no free partition slots");
77 		return -1;
78 	}
79 
80 	p->inuse = 1;
81 	free(p->name);
82 	p->name = estrdup9p(name);
83 	p->offset = start;
84 	p->length = end - start;
85 	p->mode = ctlmode;
86 	p->vers++;
87 	return 0;
88 }
89 
90 int
delpart(char * s)91 delpart(char *s)
92 {
93 	Part *p;
94 
95 	for (p = tab; p < tab + nelem(tab); p++)
96 		if(p->inuse && strcmp(p->name, s) == 0)
97 			break;
98 	if(p == tab + nelem(tab)){
99 		werrstr("partition not found");
100 		return -1;
101 	}
102 
103 	p->inuse = 0;
104 	free(p->name);
105 	p->name = nil;
106 	return 0;
107 }
108 
109 static void
addparts(char * buf)110 addparts(char *buf)
111 {
112 	char *f[4], *p, *q;
113 
114 	/*
115 	 * Use partitions passed from boot program,
116 	 * e.g.
117 	 *	sdC0part=dos 63 123123/plan9 123123 456456
118 	 * This happens before /boot sets hostname so the
119 	 * partitions will have the null-string for user.
120 	 */
121 	for(p = buf; p != nil; p = q){
122 		if(q = strchr(p, '/'))
123 			*q++ = '\0';
124 		if(tokenize(p, f, nelem(f)) >= 3 &&
125 		    addpart(f[0], strtoull(f[1], 0, 0), strtoull(f[2], 0, 0)) < 0)
126 			fprint(2, "%s: addpart %s: %r\n", argv0, f[0]);
127 	}
128 }
129 
130 static void
ctlwrite0(Req * r,char * msg,Cmdbuf * cb)131 ctlwrite0(Req *r, char *msg, Cmdbuf *cb)
132 {
133 	vlong start, end;
134 	Part *p;
135 
136 	r->ofcall.count = r->ifcall.count;
137 
138 	if(cb->nf < 1){
139 		respond(r, "empty control message");
140 		return;
141 	}
142 
143 	if(strcmp(cb->f[0], "part") == 0){
144 		if(cb->nf != 4){
145 			respondcmderror(r, cb, "part takes 3 args");
146 			return;
147 		}
148 		start = strtoll(cb->f[2], 0, 0);
149 		end = strtoll(cb->f[3], 0, 0);
150 		if(addpart(cb->f[1], start, end) < 0){
151 			respondcmderror(r, cb, "%r");
152 			return;
153 		}
154 	}
155 	else if(strcmp(cb->f[0], "delpart") == 0){
156 		if(cb->nf != 2){
157 			respondcmderror(r, cb, "delpart takes 1 arg");
158 			return;
159 		}
160 		if(delpart(cb->f[1]) < 0){
161 			respondcmderror(r, cb, "%r");
162 			return;
163 		}
164 	}
165 	else if(strcmp(cb->f[0], "inquiry") == 0){
166 		if(cb->nf != 2){
167 			respondcmderror(r, cb, "inquiry takes 1 arg");
168 			return;
169 		}
170 		free(inquiry);
171 		inquiry = estrdup9p(cb->f[1]);
172 	}
173 	else if(strcmp(cb->f[0], "geometry") == 0){
174 		if(cb->nf != 3){
175 			respondcmderror(r, cb, "geometry takes 2 args");
176 			return;
177 		}
178 		nsect = strtoll(cb->f[1], 0, 0);
179 		sectsize = strtoll(cb->f[2], 0, 0);
180 		if(tab[0].inuse && strcmp(tab[0].name, "data") == 0 &&
181 		    tab[0].vers == 0){
182 			tab[0].offset = 0;
183 			tab[0].length = nsect;
184 		}
185 		for(p = tab; p < tab + nelem(tab); p++)
186 			if(p->inuse && p->offset + p->length > nsect){
187 				p->inuse = 0;
188 				free(p->name);
189 				p->name = nil;
190 			}
191 	} else
192 		/* pass through to underlying ctl file, if any */
193 		if (write(ctlfd, msg, r->ifcall.count) != r->ifcall.count) {
194 			respondcmderror(r, cb, "%r");
195 			return;
196 		}
197 	respond(r, nil);
198 }
199 
200 void
ctlwrite(Req * r)201 ctlwrite(Req *r)
202 {
203 	char *msg;
204 	Cmdbuf *cb;
205 
206 	r->ofcall.count = r->ifcall.count;
207 
208 	msg = emalloc9p(r->ifcall.count+1);
209 	memmove(msg, r->ifcall.data, r->ifcall.count);
210 	msg[r->ifcall.count] = '\0';
211 
212 	cb = parsecmd(r->ifcall.data, r->ifcall.count);
213 	ctlwrite0(r, msg, cb);
214 
215 	free(cb);
216 	free(msg);
217 }
218 
219 int
rootgen(int off,Dir * d,void *)220 rootgen(int off, Dir *d, void*)
221 {
222 	memset(d, 0, sizeof *d);
223 	d->atime = time0;
224 	d->mtime = time0;
225 	if(off == 0){
226 		d->name = estrdup9p(sdname);
227 		d->mode = DMDIR|0777;
228 		d->qid.path = Qdir;
229 		d->qid.type = QTDIR;
230 		d->uid = estrdup9p("partfs");
231 		d->gid = estrdup9p("partfs");
232 		d->muid = estrdup9p("");
233 		return 0;
234 	}
235 	return -1;
236 }
237 
238 int
dirgen(int off,Dir * d,void *)239 dirgen(int off, Dir *d, void*)
240 {
241 	int n;
242 	Part *p;
243 
244 	memset(d, 0, sizeof *d);
245 	d->atime = time0;
246 	d->mtime = time0;
247 	if(off == 0){
248 		d->name = estrdup9p("ctl");
249 		d->mode = ctlmode;
250 		d->qid.path = Qctl;
251 		goto Have;
252 	}
253 
254 	off--;
255 	n = 0;
256 	for(p = tab; p < tab + nelem(tab); p++, n++){
257 		if(!p->inuse)
258 			continue;
259 		if(n == off){
260 			d->name = estrdup9p(p->name);
261 			d->length = p->length*sectsize;
262 			d->mode = p->mode;
263 			d->qid.path = Qpart + p - tab;
264 			d->qid.vers = p->vers;
265 			goto Have;
266 		}
267 	}
268 	return -1;
269 
270 Have:
271 	d->uid = estrdup9p("partfs");
272 	d->gid = estrdup9p("partfs");
273 	d->muid = estrdup9p("");
274 	return 0;
275 }
276 
277 int
rdwrpart(Req * r)278 rdwrpart(Req *r)
279 {
280 	int q;
281 	long count, tot;
282 	vlong offset;
283 	uchar *dat;
284 	Part *p;
285 
286 	q = r->fid->qid.path - Qpart;
287 	if(q < 0 || q > nelem(tab) || !tab[q].inuse ||
288 	    tab[q].vers != r->fid->qid.vers){
289 		respond(r, "unknown partition");
290 		return -1;
291 	}
292 	p = &tab[q];
293 
294 	offset = r->ifcall.offset;
295 	count = r->ifcall.count;
296 	if(offset < 0){
297 		respond(r, "negative offset");
298 		return -1;
299 	}
300 	if(count < 0){
301 		respond(r, "negative count");
302 		return -1;
303 	}
304 	if(offset > p->length*sectsize){
305 		respond(r, "offset past end of partition");
306 		return -1;
307 	}
308 	if(offset+count > p->length*sectsize)
309 		count = p->length*sectsize - offset;
310 	offset += p->offset*sectsize;
311 
312 	if(r->ifcall.type == Tread)
313 		dat = (uchar*)r->ofcall.data;
314 	else
315 		dat = (uchar*)r->ifcall.data;
316 
317 	/* pass i/o through to underlying file */
318 	seek(fd, offset, 0);
319 	if (r->ifcall.type == Twrite) {
320 		tot = write(fd, dat, count);
321 		if (tot != count) {
322 			respond(r, "%r");
323 			return -1;
324 		}
325 	} else {
326 		tot = read(fd, dat, count);
327 		if (tot < 0) {
328 			respond(r, "%r");
329 			return -1;
330 		}
331 	}
332 	r->ofcall.count = tot;
333 	respond(r, nil);
334 	return 0;
335 }
336 
337 void
fsread(Req * r)338 fsread(Req *r)
339 {
340 	char *s;
341 
342 	switch((int)r->fid->qid.path){
343 	case Qroot:
344 		dirread9p(r, rootgen, nil);
345 		break;
346 	case Qdir:
347 		dirread9p(r, dirgen, nil);
348 		break;
349 	case Qctl:
350 		s = ctlstring();
351 		readstr(r, s);
352 		free(s);
353 		break;
354 	default:
355 		rdwrpart(r);
356 		return;
357 	}
358 	respond(r, nil);
359 }
360 
361 void
fswrite(Req * r)362 fswrite(Req *r)
363 {
364 	switch((int)r->fid->qid.path){
365 	case Qroot:
366 	case Qdir:
367 		respond(r, "write to a directory?");
368 		break;
369 	case Qctl:
370 		ctlwrite(r);
371 		break;
372 	default:
373 		rdwrpart(r);
374 		break;
375 	}
376 }
377 
378 void
fsopen(Req * r)379 fsopen(Req *r)
380 {
381 	if(r->ifcall.mode&ORCLOSE)
382 		respond(r, "cannot open ORCLOSE");
383 
384 	switch((int)r->fid->qid.path){
385 	case Qroot:
386 	case Qdir:
387 		if(r->ifcall.mode != OREAD){
388 			respond(r, "bad mode for directory open");
389 			return;
390 		}
391 	}
392 
393 	respond(r, nil);
394 }
395 
396 void
fsstat(Req * r)397 fsstat(Req *r)
398 {
399 	int q;
400 	Dir *d;
401 	Part *p;
402 
403 	d = &r->d;
404 	memset(d, 0, sizeof *d);
405 	d->qid = r->fid->qid;
406 	d->atime = d->mtime = time0;
407 	q = r->fid->qid.path;
408 	switch(q){
409 	case Qroot:
410 		d->name = estrdup9p("/");
411 		d->mode = DMDIR|0777;
412 		break;
413 
414 	case Qdir:
415 		d->name = estrdup9p(sdname);
416 		d->mode = DMDIR|0777;
417 		break;
418 
419 	case Qctl:
420 		d->name = estrdup9p("ctl");
421 		d->mode = 0666;
422 		break;
423 
424 	default:
425 		q -= Qpart;
426 		if(q < 0 || q > nelem(tab) || tab[q].inuse == 0 ||
427 		    r->fid->qid.vers != tab[q].vers){
428 			respond(r, "partition no longer exists");
429 			return;
430 		}
431 		p = &tab[q];
432 		d->name = estrdup9p(p->name);
433 		d->length = p->length * sectsize;
434 		d->mode = p->mode;
435 		break;
436 	}
437 
438 	d->uid = estrdup9p("partfs");
439 	d->gid = estrdup9p("partfs");
440 	d->muid = estrdup9p("");
441 	respond(r, nil);
442 }
443 
444 void
fsattach(Req * r)445 fsattach(Req *r)
446 {
447 	char *spec;
448 
449 	spec = r->ifcall.aname;
450 	if(spec && spec[0]){
451 		respond(r, "invalid attach specifier");
452 		return;
453 	}
454 	r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
455 	r->fid->qid = r->ofcall.qid;
456 	respond(r, nil);
457 }
458 
459 char*
fswalk1(Fid * fid,char * name,Qid * qid)460 fswalk1(Fid *fid, char *name, Qid *qid)
461 {
462 	Part *p;
463 
464 	switch((int)fid->qid.path){
465 	case Qroot:
466 		if(strcmp(name, sdname) == 0){
467 			fid->qid.path = Qdir;
468 			fid->qid.type = QTDIR;
469 			*qid = fid->qid;
470 			return nil;
471 		}
472 		break;
473 	case Qdir:
474 		if(strcmp(name, "ctl") == 0){
475 			fid->qid.path = Qctl;
476 			fid->qid.vers = 0;
477 			fid->qid.type = 0;
478 			*qid = fid->qid;
479 			return nil;
480 		}
481 		for(p = tab; p < tab + nelem(tab); p++)
482 			if(p->inuse && strcmp(p->name, name) == 0){
483 				fid->qid.path = p - tab + Qpart;
484 				fid->qid.vers = p->vers;
485 				fid->qid.type = 0;
486 				*qid = fid->qid;
487 				return nil;
488 			}
489 		break;
490 	}
491 	return "file not found";
492 }
493 
494 Srv fs = {
495 	.attach=fsattach,
496 	.open=	fsopen,
497 	.read=	fsread,
498 	.write=	fswrite,
499 	.stat=	fsstat,
500 	.walk1=	fswalk1,
501 };
502 
503 char *mtpt = "/dev";
504 char *srvname;
505 
506 void
usage(void)507 usage(void)
508 {
509 	fprint(2, "usage: %s [-Dr] [-d sdname] [-m mtpt] [-p 9parts] "
510 		"[-s srvname] diskimage\n", argv0);
511 	fprint(2, "\tdefault mtpt is /dev\n");
512 	exits("usage");
513 }
514 
515 void
main(int argc,char ** argv)516 main(int argc, char **argv)
517 {
518 	int isdir;
519 	char *file, *cname, *parts;
520 	Dir *dir;
521 
522 	quotefmtinstall();
523 	time0 = time(0);
524 	parts = nil;
525 
526 	ARGBEGIN{
527 	case 'D':
528 		chatty9p++;
529 		break;
530 	case 'd':
531 		sdname = EARGF(usage());
532 		break;
533 	case 'm':
534 		mtpt = EARGF(usage());
535 		break;
536 	case 'p':
537 		parts = EARGF(usage());
538 		break;
539 	case 'r':
540 		rdonly = 1;
541 		break;
542 	case 's':
543 		srvname = EARGF(usage());
544 		break;
545 	default:
546 		usage();
547 	}ARGEND
548 
549 	if(argc != 1)
550 		usage();
551 	file = argv[0];
552 	dir = dirstat(file);
553 	if(!dir)
554 		sysfatal("%s: %r", file);
555 	isdir = (dir->mode & DMDIR) != 0;
556 	free(dir);
557 
558 	if (isdir) {
559 		cname = smprint("%s/ctl", file);
560 		if ((ctlfd = open(cname, ORDWR)) < 0)
561 			sysfatal("open %s: %r", cname);
562 		file = smprint("%s/data", file);
563 	}
564 	if((fd = open(file, rdonly? OREAD: ORDWR)) < 0)
565 		sysfatal("open %s: %r", file);
566 
567 	sectsize = 512;			/* conventional */
568 	dir = dirfstat(fd);
569 	if (dir)
570 		nsect = dir->length / sectsize;
571 	free(dir);
572 
573 	inquiry = estrdup9p(inquiry);
574 	tab[0].inuse = 1;
575 	tab[0].name = estrdup9p("data");
576 	tab[0].mode = 0666;
577 	tab[0].length = nsect;
578 
579 	/*
580 	 * hack for booting from usb: add 9load partitions.
581 	 */
582 	if(parts != nil)
583 		addparts(parts);
584 
585 	postmountsrv(&fs, srvname, mtpt, MBEFORE);
586 	exits(nil);
587 }
588