|
@@ -0,0 +1,566 @@
|
|
|
+/*
|
|
|
+ * partfs - serve an underlying file, with devsd-style partitions
|
|
|
+ */
|
|
|
+#include <u.h>
|
|
|
+#include <libc.h>
|
|
|
+#include <auth.h>
|
|
|
+#include <fcall.h>
|
|
|
+#include <thread.h>
|
|
|
+#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
|
|
|
+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;
|
|
|
+}
|
|
|
+
|
|
|
+void*
|
|
|
+evommem(void *a, void *b, ulong n)
|
|
|
+{
|
|
|
+ return memmove(b, a, n);
|
|
|
+}
|
|
|
+
|
|
|
+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 [-D] [-f file] [-s srvname] [-m mtpt] [sdXX]\n",
|
|
|
+ argv0);
|
|
|
+ fprint(2, "\tdefault mtpt is /dev\n");
|
|
|
+ exits("usage");
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+main(int argc, char **argv)
|
|
|
+{
|
|
|
+ int isdir;
|
|
|
+ char *file, *cname;
|
|
|
+ Dir *dir;
|
|
|
+
|
|
|
+ file = nil;
|
|
|
+ quotefmtinstall();
|
|
|
+ time0 = time(0);
|
|
|
+
|
|
|
+ ARGBEGIN{
|
|
|
+ case 'D':
|
|
|
+ chatty9p++;
|
|
|
+ break;
|
|
|
+ case 'f':
|
|
|
+ file = EARGF(usage());
|
|
|
+ break;
|
|
|
+ case 'm':
|
|
|
+ mtpt = EARGF(usage());
|
|
|
+ break;
|
|
|
+ case 'r':
|
|
|
+ rdonly = 1;
|
|
|
+ break;
|
|
|
+ case 's':
|
|
|
+ srvname = EARGF(usage());
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ usage();
|
|
|
+ }ARGEND
|
|
|
+
|
|
|
+ if(argc > 1)
|
|
|
+ usage();
|
|
|
+ if(argc == 1)
|
|
|
+ sdname = argv[0];
|
|
|
+ if(!file)
|
|
|
+ sysfatal("no underlying file named");
|
|
|
+ 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;
|
|
|
+
|
|
|
+ postmountsrv(&fs, srvname, mtpt, MBEFORE);
|
|
|
+ exits(nil);
|
|
|
+}
|