/* * partfs - serve an underlying file, with devsd-style partitions */ #include #include #include #include #include #include <9p.h> typedef struct Part Part; struct Part { int inuse; int vers; ulong mode; char *name; vlong offset; /* in sectors */ vlong length; /* in sectors */ }; enum { Qroot = 0, Qdir, Qctl, Qpart, }; int fd = -1, ctlfd = -1; int rdonly; ulong ctlmode = 0666; ulong time0; vlong nsect, sectsize; char *inquiry = "partfs hard drive"; char *sdname = "sdXX"; Part tab[64]; char* ctlstring(void) { Part *p; Fmt fmt; fmtstrinit(&fmt); fmtprint(&fmt, "inquiry %s\n", inquiry); fmtprint(&fmt, "geometry %lld %lld\n", nsect, sectsize); for (p = tab; p < tab + nelem(tab); p++) if (p->inuse) fmtprint(&fmt, "part %s %lld %lld\n", p->name, p->offset, p->length); return fmtstrflush(&fmt); } int addpart(char *name, vlong start, vlong end) { Part *p; if(start < 0 || start > end || end > nsect){ werrstr("bad partition boundaries"); return -1; } if (strcmp(name, "ctl") == 0 || strcmp(name, "data") == 0) { werrstr("partition name already in use"); return -1; } for (p = tab; p < tab + nelem(tab) && p->inuse; p++) if (strcmp(p->name, name) == 0) { werrstr("partition name already in use"); return -1; } if(p == tab + nelem(tab)){ werrstr("no free partition slots"); return -1; } p->inuse = 1; free(p->name); p->name = estrdup9p(name); p->offset = start; p->length = end - start; p->mode = ctlmode; p->vers++; return 0; } int delpart(char *s) { Part *p; for (p = tab; p < tab + nelem(tab); p++) if(p->inuse && strcmp(p->name, s) == 0) break; if(p == tab + nelem(tab)){ werrstr("partition not found"); return -1; } p->inuse = 0; free(p->name); p->name = nil; return 0; } static void addparts(char *buf) { char *f[4], *p, *q; /* * Use partitions passed from boot program, * e.g. * sdC0part=dos 63 123123/plan9 123123 456456 * This happens before /boot sets hostname so the * partitions will have the null-string for user. */ for(p = buf; p != nil; p = q){ if(q = strchr(p, '/')) *q++ = '\0'; if(tokenize(p, f, nelem(f)) >= 3 && addpart(f[0], strtoull(f[1], 0, 0), strtoull(f[2], 0, 0)) < 0) fprint(2, "%s: addpart %s: %r\n", argv0, f[0]); } } static void ctlwrite0(Req *r, char *msg, Cmdbuf *cb) { vlong start, end; Part *p; r->ofcall.count = r->ifcall.count; if(cb->nf < 1){ respond(r, "empty control message"); return; } if(strcmp(cb->f[0], "part") == 0){ if(cb->nf != 4){ respondcmderror(r, cb, "part takes 3 args"); return; } start = strtoll(cb->f[2], 0, 0); end = strtoll(cb->f[3], 0, 0); if(addpart(cb->f[1], start, end) < 0){ respondcmderror(r, cb, "%r"); return; } } else if(strcmp(cb->f[0], "delpart") == 0){ if(cb->nf != 2){ respondcmderror(r, cb, "delpart takes 1 arg"); return; } if(delpart(cb->f[1]) < 0){ respondcmderror(r, cb, "%r"); return; } } else if(strcmp(cb->f[0], "inquiry") == 0){ if(cb->nf != 2){ respondcmderror(r, cb, "inquiry takes 1 arg"); return; } free(inquiry); inquiry = estrdup9p(cb->f[1]); } else if(strcmp(cb->f[0], "geometry") == 0){ if(cb->nf != 3){ respondcmderror(r, cb, "geometry takes 2 args"); return; } nsect = strtoll(cb->f[1], 0, 0); sectsize = strtoll(cb->f[2], 0, 0); if(tab[0].inuse && strcmp(tab[0].name, "data") == 0 && tab[0].vers == 0){ tab[0].offset = 0; tab[0].length = nsect; } for(p = tab; p < tab + nelem(tab); p++) if(p->inuse && p->offset + p->length > nsect){ p->inuse = 0; free(p->name); p->name = nil; } } else /* pass through to underlying ctl file, if any */ if (write(ctlfd, msg, r->ifcall.count) != r->ifcall.count) { respondcmderror(r, cb, "%r"); return; } respond(r, nil); } void ctlwrite(Req *r) { char *msg; Cmdbuf *cb; r->ofcall.count = r->ifcall.count; msg = emalloc9p(r->ifcall.count+1); memmove(msg, r->ifcall.data, r->ifcall.count); msg[r->ifcall.count] = '\0'; cb = parsecmd(r->ifcall.data, r->ifcall.count); ctlwrite0(r, msg, cb); free(cb); free(msg); } int rootgen(int off, Dir *d, void*) { memset(d, 0, sizeof *d); d->atime = time0; d->mtime = time0; if(off == 0){ d->name = estrdup9p(sdname); d->mode = DMDIR|0777; d->qid.path = Qdir; d->qid.type = QTDIR; d->uid = estrdup9p("partfs"); d->gid = estrdup9p("partfs"); d->muid = estrdup9p(""); return 0; } return -1; } int dirgen(int off, Dir *d, void*) { int n; Part *p; memset(d, 0, sizeof *d); d->atime = time0; d->mtime = time0; if(off == 0){ d->name = estrdup9p("ctl"); d->mode = ctlmode; d->qid.path = Qctl; goto Have; } off--; n = 0; for(p = tab; p < tab + nelem(tab); p++, n++){ if(!p->inuse) continue; if(n == off){ d->name = estrdup9p(p->name); d->length = p->length*sectsize; d->mode = p->mode; d->qid.path = Qpart + p - tab; d->qid.vers = p->vers; goto Have; } } return -1; Have: d->uid = estrdup9p("partfs"); d->gid = estrdup9p("partfs"); d->muid = estrdup9p(""); return 0; } int rdwrpart(Req *r) { int q; long count, tot; vlong offset; uchar *dat; Part *p; q = r->fid->qid.path - Qpart; if(q < 0 || q > nelem(tab) || !tab[q].inuse || tab[q].vers != r->fid->qid.vers){ respond(r, "unknown partition"); return -1; } p = &tab[q]; offset = r->ifcall.offset; count = r->ifcall.count; if(offset < 0){ respond(r, "negative offset"); return -1; } if(count < 0){ respond(r, "negative count"); return -1; } if(offset > p->length*sectsize){ respond(r, "offset past end of partition"); return -1; } if(offset+count > p->length*sectsize) count = p->length*sectsize - offset; offset += p->offset*sectsize; if(r->ifcall.type == Tread) dat = (uchar*)r->ofcall.data; else dat = (uchar*)r->ifcall.data; /* pass i/o through to underlying file */ seek(fd, offset, 0); if (r->ifcall.type == Twrite) { tot = write(fd, dat, count); if (tot != count) { respond(r, "%r"); return -1; } } else { tot = read(fd, dat, count); if (tot < 0) { respond(r, "%r"); return -1; } } r->ofcall.count = tot; respond(r, nil); return 0; } void fsread(Req *r) { char *s; switch((int)r->fid->qid.path){ case Qroot: dirread9p(r, rootgen, nil); break; case Qdir: dirread9p(r, dirgen, nil); break; case Qctl: s = ctlstring(); readstr(r, s); free(s); break; default: rdwrpart(r); return; } respond(r, nil); } void fswrite(Req *r) { switch((int)r->fid->qid.path){ case Qroot: case Qdir: respond(r, "write to a directory?"); break; case Qctl: ctlwrite(r); break; default: rdwrpart(r); break; } } void fsopen(Req *r) { if(r->ifcall.mode&ORCLOSE) respond(r, "cannot open ORCLOSE"); switch((int)r->fid->qid.path){ case Qroot: case Qdir: if(r->ifcall.mode != OREAD){ respond(r, "bad mode for directory open"); return; } } respond(r, nil); } void fsstat(Req *r) { int q; Dir *d; Part *p; d = &r->d; memset(d, 0, sizeof *d); d->qid = r->fid->qid; d->atime = d->mtime = time0; q = r->fid->qid.path; switch(q){ case Qroot: d->name = estrdup9p("/"); d->mode = DMDIR|0777; break; case Qdir: d->name = estrdup9p(sdname); d->mode = DMDIR|0777; break; case Qctl: d->name = estrdup9p("ctl"); d->mode = 0666; break; default: q -= Qpart; if(q < 0 || q > nelem(tab) || tab[q].inuse == 0 || r->fid->qid.vers != tab[q].vers){ respond(r, "partition no longer exists"); return; } p = &tab[q]; d->name = estrdup9p(p->name); d->length = p->length * sectsize; d->mode = p->mode; break; } d->uid = estrdup9p("partfs"); d->gid = estrdup9p("partfs"); d->muid = estrdup9p(""); respond(r, nil); } void fsattach(Req *r) { char *spec; spec = r->ifcall.aname; if(spec && spec[0]){ respond(r, "invalid attach specifier"); return; } r->ofcall.qid = (Qid){Qroot, 0, QTDIR}; r->fid->qid = r->ofcall.qid; respond(r, nil); } char* fswalk1(Fid *fid, char *name, Qid *qid) { Part *p; switch((int)fid->qid.path){ case Qroot: if(strcmp(name, sdname) == 0){ fid->qid.path = Qdir; fid->qid.type = QTDIR; *qid = fid->qid; return nil; } break; case Qdir: if(strcmp(name, "ctl") == 0){ fid->qid.path = Qctl; fid->qid.vers = 0; fid->qid.type = 0; *qid = fid->qid; return nil; } for(p = tab; p < tab + nelem(tab); p++) if(p->inuse && strcmp(p->name, name) == 0){ fid->qid.path = p - tab + Qpart; fid->qid.vers = p->vers; fid->qid.type = 0; *qid = fid->qid; return nil; } break; } return "file not found"; } Srv fs = { .attach=fsattach, .open= fsopen, .read= fsread, .write= fswrite, .stat= fsstat, .walk1= fswalk1, }; char *mtpt = "/dev"; char *srvname; void usage(void) { fprint(2, "usage: %s [-Dr] [-d sdname] [-m mtpt] [-p 9parts] " "[-s srvname] diskimage\n", argv0); fprint(2, "\tdefault mtpt is /dev\n"); exits("usage"); } void main(int argc, char **argv) { int isdir; char *file, *cname, *parts; Dir *dir; quotefmtinstall(); time0 = time(0); parts = nil; ARGBEGIN{ case 'D': chatty9p++; break; case 'd': sdname = EARGF(usage()); break; case 'm': mtpt = EARGF(usage()); break; case 'p': parts = EARGF(usage()); break; case 'r': rdonly = 1; break; case 's': srvname = EARGF(usage()); break; default: usage(); }ARGEND if(argc != 1) usage(); file = argv[0]; dir = dirstat(file); if(!dir) sysfatal("%s: %r", file); isdir = (dir->mode & DMDIR) != 0; free(dir); if (isdir) { cname = smprint("%s/ctl", file); if ((ctlfd = open(cname, ORDWR)) < 0) sysfatal("open %s: %r", cname); file = smprint("%s/data", file); } if((fd = open(file, rdonly? OREAD: ORDWR)) < 0) sysfatal("open %s: %r", file); sectsize = 512; /* conventional */ dir = dirfstat(fd); if (dir) nsect = dir->length / sectsize; free(dir); inquiry = estrdup9p(inquiry); tab[0].inuse = 1; tab[0].name = estrdup9p("data"); tab[0].mode = 0666; tab[0].length = nsect; /* * hack for booting from usb: add 9load partitions. */ if(parts != nil) addparts(parts); postmountsrv(&fs, srvname, mtpt, MBEFORE); exits(nil); }