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