]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/disk/partfs.c
Import sources from 2011-03-30 iso image - lib
[plan9front.git] / sys / src / cmd / disk / partfs.c
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*
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
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
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
110 ctlwrite0(Req *r, char *msg, Cmdbuf *cb)
111 {
112         vlong start, end;
113         Part *p;
114
115         r->ofcall.count = r->ifcall.count;
116
117         if(cb->nf < 1){
118                 respond(r, "empty control message");
119                 return;
120         }
121
122         if(strcmp(cb->f[0], "part") == 0){
123                 if(cb->nf != 4){
124                         respondcmderror(r, cb, "part takes 3 args");
125                         return;
126                 }
127                 start = strtoll(cb->f[2], 0, 0);
128                 end = strtoll(cb->f[3], 0, 0);
129                 if(addpart(cb->f[1], start, end) < 0){
130                         respondcmderror(r, cb, "%r");
131                         return;
132                 }
133         }
134         else if(strcmp(cb->f[0], "delpart") == 0){
135                 if(cb->nf != 2){
136                         respondcmderror(r, cb, "delpart takes 1 arg");
137                         return;
138                 }
139                 if(delpart(cb->f[1]) < 0){
140                         respondcmderror(r, cb, "%r");
141                         return;
142                 }
143         }
144         else if(strcmp(cb->f[0], "inquiry") == 0){
145                 if(cb->nf != 2){
146                         respondcmderror(r, cb, "inquiry takes 1 arg");
147                         return;
148                 }
149                 free(inquiry);
150                 inquiry = estrdup9p(cb->f[1]);
151         }
152         else if(strcmp(cb->f[0], "geometry") == 0){
153                 if(cb->nf != 3){
154                         respondcmderror(r, cb, "geometry takes 2 args");
155                         return;
156                 }
157                 nsect = strtoll(cb->f[1], 0, 0);
158                 sectsize = strtoll(cb->f[2], 0, 0);
159                 if(tab[0].inuse && strcmp(tab[0].name, "data") == 0 &&
160                     tab[0].vers == 0){
161                         tab[0].offset = 0;
162                         tab[0].length = nsect;
163                 }
164                 for(p = tab; p < tab + nelem(tab); p++)
165                         if(p->inuse && p->offset + p->length > nsect){
166                                 p->inuse = 0;
167                                 free(p->name);
168                                 p->name = nil;
169                         }
170         } else
171                 /* pass through to underlying ctl file, if any */
172                 if (write(ctlfd, msg, r->ifcall.count) != r->ifcall.count) {
173                         respondcmderror(r, cb, "%r");
174                         return;
175                 }
176         respond(r, nil);
177 }
178
179 void
180 ctlwrite(Req *r)
181 {
182         char *msg;
183         Cmdbuf *cb;
184
185         r->ofcall.count = r->ifcall.count;
186
187         msg = emalloc9p(r->ifcall.count+1);
188         memmove(msg, r->ifcall.data, r->ifcall.count);
189         msg[r->ifcall.count] = '\0';
190
191         cb = parsecmd(r->ifcall.data, r->ifcall.count);
192         ctlwrite0(r, msg, cb);
193
194         free(cb);
195         free(msg);
196 }
197
198 int
199 rootgen(int off, Dir *d, void*)
200 {
201         memset(d, 0, sizeof *d);
202         d->atime = time0;
203         d->mtime = time0;
204         if(off == 0){
205                 d->name = estrdup9p(sdname);
206                 d->mode = DMDIR|0777;
207                 d->qid.path = Qdir;
208                 d->qid.type = QTDIR;
209                 d->uid = estrdup9p("partfs");
210                 d->gid = estrdup9p("partfs");
211                 d->muid = estrdup9p("");
212                 return 0;
213         }
214         return -1;
215 }
216
217 int
218 dirgen(int off, Dir *d, void*)
219 {
220         int n;
221         Part *p;
222
223         memset(d, 0, sizeof *d);
224         d->atime = time0;
225         d->mtime = time0;
226         if(off == 0){
227                 d->name = estrdup9p("ctl");
228                 d->mode = ctlmode;
229                 d->qid.path = Qctl;
230                 goto Have;
231         }
232
233         off--;
234         n = 0;
235         for(p = tab; p < tab + nelem(tab); p++, n++){
236                 if(!p->inuse)
237                         continue;
238                 if(n == off){
239                         d->name = estrdup9p(p->name);
240                         d->length = p->length*sectsize;
241                         d->mode = p->mode;
242                         d->qid.path = Qpart + p - tab;
243                         d->qid.vers = p->vers;
244                         goto Have;
245                 }
246         }
247         return -1;
248
249 Have:
250         d->uid = estrdup9p("partfs");
251         d->gid = estrdup9p("partfs");
252         d->muid = estrdup9p("");
253         return 0;
254 }
255
256 void*
257 evommem(void *a, void *b, ulong n)
258 {
259         return memmove(b, a, n);
260 }
261
262 int
263 rdwrpart(Req *r)
264 {
265         int q;
266         long count, tot;
267         vlong offset;
268         uchar *dat;
269         Part *p;
270
271         q = r->fid->qid.path - Qpart;
272         if(q < 0 || q > nelem(tab) || !tab[q].inuse ||
273             tab[q].vers != r->fid->qid.vers){
274                 respond(r, "unknown partition");
275                 return -1;
276         }
277         p = &tab[q];
278
279         offset = r->ifcall.offset;
280         count = r->ifcall.count;
281         if(offset < 0){
282                 respond(r, "negative offset");
283                 return -1;
284         }
285         if(count < 0){
286                 respond(r, "negative count");
287                 return -1;
288         }
289         if(offset > p->length*sectsize){
290                 respond(r, "offset past end of partition");
291                 return -1;
292         }
293         if(offset+count > p->length*sectsize)
294                 count = p->length*sectsize - offset;
295         offset += p->offset*sectsize;
296
297         if(r->ifcall.type == Tread)
298                 dat = (uchar*)r->ofcall.data;
299         else
300                 dat = (uchar*)r->ifcall.data;
301
302         /* pass i/o through to underlying file */
303         seek(fd, offset, 0);
304         if (r->ifcall.type == Twrite) {
305                 tot = write(fd, dat, count);
306                 if (tot != count) {
307                         respond(r, "%r");
308                         return -1;
309                 }
310         } else {
311                 tot = read(fd, dat, count);
312                 if (tot < 0) {
313                         respond(r, "%r");
314                         return -1;
315                 }
316         }
317         r->ofcall.count = tot;
318         respond(r, nil);
319         return 0;
320 }
321
322 void
323 fsread(Req *r)
324 {
325         char *s;
326
327         switch((int)r->fid->qid.path){
328         case Qroot:
329                 dirread9p(r, rootgen, nil);
330                 break;
331         case Qdir:
332                 dirread9p(r, dirgen, nil);
333                 break;
334         case Qctl:
335                 s = ctlstring();
336                 readstr(r, s);
337                 free(s);
338                 break;
339         default:
340                 rdwrpart(r);
341                 return;
342         }
343         respond(r, nil);
344 }
345
346 void
347 fswrite(Req *r)
348 {
349         switch((int)r->fid->qid.path){
350         case Qroot:
351         case Qdir:
352                 respond(r, "write to a directory?");
353                 break;
354         case Qctl:
355                 ctlwrite(r);
356                 break;
357         default:
358                 rdwrpart(r);
359                 break;
360         }
361 }
362
363 void
364 fsopen(Req *r)
365 {
366         if(r->ifcall.mode&ORCLOSE)
367                 respond(r, "cannot open ORCLOSE");
368
369         switch((int)r->fid->qid.path){
370         case Qroot:
371         case Qdir:
372                 if(r->ifcall.mode != OREAD){
373                         respond(r, "bad mode for directory open");
374                         return;
375                 }
376         }
377
378         respond(r, nil);
379 }
380
381 void
382 fsstat(Req *r)
383 {
384         int q;
385         Dir *d;
386         Part *p;
387
388         d = &r->d;
389         memset(d, 0, sizeof *d);
390         d->qid = r->fid->qid;
391         d->atime = d->mtime = time0;
392         q = r->fid->qid.path;
393         switch(q){
394         case Qroot:
395                 d->name = estrdup9p("/");
396                 d->mode = DMDIR|0777;
397                 break;
398
399         case Qdir:
400                 d->name = estrdup9p(sdname);
401                 d->mode = DMDIR|0777;
402                 break;
403
404         case Qctl:
405                 d->name = estrdup9p("ctl");
406                 d->mode = 0666;
407                 break;
408
409         default:
410                 q -= Qpart;
411                 if(q < 0 || q > nelem(tab) || tab[q].inuse == 0 ||
412                     r->fid->qid.vers != tab[q].vers){
413                         respond(r, "partition no longer exists");
414                         return;
415                 }
416                 p = &tab[q];
417                 d->name = estrdup9p(p->name);
418                 d->length = p->length * sectsize;
419                 d->mode = p->mode;
420                 break;
421         }
422
423         d->uid = estrdup9p("partfs");
424         d->gid = estrdup9p("partfs");
425         d->muid = estrdup9p("");
426         respond(r, nil);
427 }
428
429 void
430 fsattach(Req *r)
431 {
432         char *spec;
433
434         spec = r->ifcall.aname;
435         if(spec && spec[0]){
436                 respond(r, "invalid attach specifier");
437                 return;
438         }
439         r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
440         r->fid->qid = r->ofcall.qid;
441         respond(r, nil);
442 }
443
444 char*
445 fswalk1(Fid *fid, char *name, Qid *qid)
446 {
447         Part *p;
448
449         switch((int)fid->qid.path){
450         case Qroot:
451                 if(strcmp(name, sdname) == 0){
452                         fid->qid.path = Qdir;
453                         fid->qid.type = QTDIR;
454                         *qid = fid->qid;
455                         return nil;
456                 }
457                 break;
458         case Qdir:
459                 if(strcmp(name, "ctl") == 0){
460                         fid->qid.path = Qctl;
461                         fid->qid.vers = 0;
462                         fid->qid.type = 0;
463                         *qid = fid->qid;
464                         return nil;
465                 }
466                 for(p = tab; p < tab + nelem(tab); p++)
467                         if(p->inuse && strcmp(p->name, name) == 0){
468                                 fid->qid.path = p - tab + Qpart;
469                                 fid->qid.vers = p->vers;
470                                 fid->qid.type = 0;
471                                 *qid = fid->qid;
472                                 return nil;
473                         }
474                 break;
475         }
476         return "file not found";
477 }
478
479 Srv fs = {
480         .attach=fsattach,
481         .open=  fsopen,
482         .read=  fsread,
483         .write= fswrite,
484         .stat=  fsstat,
485         .walk1= fswalk1,
486 };
487
488 char *mtpt = "/dev";
489 char *srvname;
490
491 void
492 usage(void)
493 {
494         fprint(2, "usage: %s [-Dr] [-d sdname] [-m mtpt] [-s srvname] diskimage\n",
495                 argv0);
496         fprint(2, "\tdefault mtpt is /dev\n");
497         exits("usage");
498 }
499
500 void
501 main(int argc, char **argv)
502 {
503         int isdir;
504         char *file, *cname;
505         Dir *dir;
506
507         quotefmtinstall();
508         time0 = time(0);
509
510         ARGBEGIN{
511         case 'D':
512                 chatty9p++;
513                 break;
514         case 'd':
515                 sdname = EARGF(usage());
516                 break;
517         case 'm':
518                 mtpt = EARGF(usage());
519                 break;
520         case 'r':
521                 rdonly = 1;
522                 break;
523         case 's':
524                 srvname = EARGF(usage());
525                 break;
526         default:
527                 usage();
528         }ARGEND
529
530         if(argc != 1)
531                 usage();
532         file = argv[0];
533         dir = dirstat(file);
534         if(!dir)
535                 sysfatal("%s: %r", file);
536         isdir = (dir->mode & DMDIR) != 0;
537         free(dir);
538
539         if (isdir) {
540                 cname = smprint("%s/ctl", file);
541                 if ((ctlfd = open(cname, ORDWR)) < 0)
542                         sysfatal("open %s: %r", cname);
543                 file = smprint("%s/data", file);
544         }
545         if((fd = open(file, rdonly? OREAD: ORDWR)) < 0)
546                 sysfatal("open %s: %r", file);
547
548         sectsize = 512;                 /* conventional */
549         dir = dirfstat(fd);
550         if (dir)
551                 nsect = dir->length / sectsize;
552         free(dir);
553
554         inquiry = estrdup9p(inquiry);
555         tab[0].inuse = 1;
556         tab[0].name = estrdup9p("data");
557         tab[0].mode = 0666;
558         tab[0].length = nsect;
559
560         postmountsrv(&fs, srvname, mtpt, MBEFORE);
561         exits(nil);
562 }