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