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