skel(3) → skelfs(4)
The original intention was to put devskel in to the kernel to detach what it provides from devsrv. That is not a good reason, just move it to userspace. auth/box has been changed to exec skelfs instead of relying on '#z'.
This commit is contained in:
parent
c7c0ff5db6
commit
c12022fd8c
7 changed files with 351 additions and 283 deletions
|
@ -1,35 +0,0 @@
|
|||
.TH SKEL 3
|
||||
.SH NAME
|
||||
skel \- skeleton builder
|
||||
.SH SYNOPSIS
|
||||
.B bind #z/\fIskel\fR
|
||||
.I dir
|
||||
.PP
|
||||
.B bind #zd/\fIskel\fR
|
||||
.I dir
|
||||
.PP
|
||||
.B bind #ze/
|
||||
.I dir
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
This device serves a single child directory with a single empty child
|
||||
file. The name of these files is determined by the first walk away
|
||||
from the root. After which, the hierarchy for that attached session
|
||||
becomes stable. The
|
||||
.B d
|
||||
attach option causes the child file to be an empty directory. The
|
||||
.B e
|
||||
attach option presents a completly empty root directory.
|
||||
.SH EXAMPLES
|
||||
Skeleton files can be used for constructing arbitrary bind targets.
|
||||
.EX
|
||||
.IP
|
||||
bind -b '#zd/newroot' /
|
||||
bind '#zd/bin' /newroot
|
||||
bind '#z/walk' /newroot/bin
|
||||
bind /bin/walk /newroot/bin/walk
|
||||
bind /newroot /
|
||||
walk /
|
||||
.EE
|
||||
.SH SOURCE
|
||||
.B /sys/src/9/port/devskel.c
|
65
sys/man/4/skelfs
Normal file
65
sys/man/4/skelfs
Normal file
|
@ -0,0 +1,65 @@
|
|||
.TH SKELFS 4
|
||||
.SH NAME
|
||||
skelfs \- build directory skeletons
|
||||
.SH SYNOPSIS
|
||||
.B skelfs
|
||||
[
|
||||
.B -i
|
||||
]
|
||||
[
|
||||
.B -t
|
||||
.I mode
|
||||
]
|
||||
[
|
||||
.B -s
|
||||
.I service
|
||||
]
|
||||
[
|
||||
.I mnt
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
.I Skelfs
|
||||
generates directory skeletons
|
||||
to assist in building namespaces.
|
||||
Skeletons are generated on demand
|
||||
by walking through the root directory.
|
||||
A skeleon is a directory containing a single empty child.
|
||||
The name of this child is defined by the first walk taken
|
||||
away from the root. For example the hierarchy for a skeleton
|
||||
named 'echo' would be:
|
||||
.PP
|
||||
.EX
|
||||
/
|
||||
/echo/
|
||||
/echo/echo
|
||||
.EE
|
||||
.PP
|
||||
The
|
||||
.I mode
|
||||
dictates what form the innermost child file takes. The
|
||||
.B file
|
||||
and
|
||||
.B dir
|
||||
modes cause the child to be an empty file or directory
|
||||
respecively. The
|
||||
.B empty
|
||||
mode instead serves no skeletons, causing the root
|
||||
directory to always be empty.
|
||||
A client may override the mode by providing
|
||||
its own selection as an attach option. If a
|
||||
mode is not provided,
|
||||
.B file
|
||||
is assumed.
|
||||
.PP
|
||||
The skeletons generated by
|
||||
.I skelfs
|
||||
are anonmyous. Clients will never see the
|
||||
skeletons of other clients, nor can a client revisit
|
||||
a previous skeleton.
|
||||
.PP
|
||||
.SH "SEE ALSO"
|
||||
.B auth/box
|
||||
in
|
||||
.IR auth (8).
|
||||
.SH SOURCE
|
||||
.B /sys/src/cmd/skelfs.c
|
|
@ -18,7 +18,6 @@ dev
|
|||
kprof
|
||||
fs
|
||||
dtracy
|
||||
skel
|
||||
|
||||
ether netif
|
||||
bridge netif log
|
||||
|
|
|
@ -41,7 +41,6 @@ dev
|
|||
segment
|
||||
vmx
|
||||
dtracy
|
||||
skel
|
||||
|
||||
link
|
||||
# devpccard pci
|
||||
|
|
|
@ -1,239 +0,0 @@
|
|||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "../port/error.h"
|
||||
|
||||
#include "netif.h"
|
||||
|
||||
typedef struct Skel Skel;
|
||||
struct Skel {
|
||||
RWlock;
|
||||
int ref;
|
||||
char name[KNAMELEN];
|
||||
char mode;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
QLock;
|
||||
ulong path;
|
||||
} skelalloc;
|
||||
|
||||
enum{
|
||||
Qroot,
|
||||
Qdir,
|
||||
Qskel,
|
||||
};
|
||||
|
||||
static Chan*
|
||||
skelattach(char *spec)
|
||||
{
|
||||
Chan *c;
|
||||
Skel *f;
|
||||
uvlong path;
|
||||
|
||||
c = devattach('z', spec);
|
||||
|
||||
f = mallocz(sizeof *f, 1);
|
||||
if(f == nil)
|
||||
exhausted("memory");
|
||||
if(waserror()){
|
||||
free(f);
|
||||
nexterror();
|
||||
}
|
||||
|
||||
if(spec == nil)
|
||||
f->mode = 'f';
|
||||
else
|
||||
f->mode = spec[0];
|
||||
|
||||
eqlock(&skelalloc);
|
||||
path = skelalloc.path++;
|
||||
qunlock(&skelalloc);
|
||||
|
||||
poperror();
|
||||
mkqid(&c->qid, NETQID(path, Qroot), path, QTDIR);
|
||||
f->ref = 1;
|
||||
c->aux = f;
|
||||
return c;
|
||||
}
|
||||
|
||||
static int
|
||||
step(Chan *c, Dir *dp, int direction)
|
||||
{
|
||||
Skel *f;
|
||||
Qid qid;
|
||||
ulong perm;
|
||||
int path;
|
||||
char *name;
|
||||
|
||||
perm = 0555|DMDIR;
|
||||
path = NETTYPE(c->qid.path);
|
||||
f = c->aux;
|
||||
rlock(f);
|
||||
if(waserror()){
|
||||
runlock(f);
|
||||
return -1;
|
||||
}
|
||||
name = f->name;
|
||||
|
||||
path += direction;
|
||||
if(!f->name[0] && path > Qroot)
|
||||
error(Enonexist);
|
||||
|
||||
switch(path){
|
||||
case Qroot-1:
|
||||
case Qroot:
|
||||
mkqid(&qid, Qroot, 0, QTDIR);
|
||||
name = "#z";
|
||||
break;
|
||||
case Qdir:
|
||||
mkqid(&qid, Qdir, 0, QTDIR);
|
||||
break;
|
||||
case Qskel:
|
||||
switch(f->mode){
|
||||
case 'd':
|
||||
mkqid(&qid, Qskel, 0, QTDIR);
|
||||
break;
|
||||
case 'f':
|
||||
default:
|
||||
mkqid(&qid, Qskel, 0, QTFILE);
|
||||
perm = 0666;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error(Enonexist);
|
||||
}
|
||||
|
||||
qid.vers = NETID(c->qid.path);
|
||||
qid.path = NETQID(qid.vers, qid.path);
|
||||
devdir(c, qid, name, 0, eve, perm, dp);
|
||||
runlock(f);
|
||||
poperror();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
skelgen(Chan *c, char *name, Dirtab*, int, int s, Dir *dp)
|
||||
{
|
||||
Skel *f;
|
||||
|
||||
switch(s){
|
||||
case DEVDOTDOT:
|
||||
break;
|
||||
case 0:
|
||||
s++;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
f = c->aux;
|
||||
if(name && NETTYPE(c->qid.path) == Qroot){
|
||||
wlock(f);
|
||||
if(!f->name[0] && f->mode != 'e')
|
||||
utfecpy(f->name, &f->name[sizeof f->name-1], name);
|
||||
wunlock(f);
|
||||
}
|
||||
|
||||
return step(c, dp, s);
|
||||
}
|
||||
|
||||
static Walkqid*
|
||||
skelwalk(Chan *c, Chan *nc, char **name, int nname)
|
||||
{
|
||||
Walkqid *wq;
|
||||
Skel *f;
|
||||
|
||||
wq = devwalk(c, nc, name, nname, nil, 0, skelgen);
|
||||
if(wq == nil || wq->clone == nil)
|
||||
return wq;
|
||||
|
||||
f = c->aux;
|
||||
wlock(f);
|
||||
if(f->ref <= 0)
|
||||
panic("devskel ref");
|
||||
f->ref++;
|
||||
wunlock(f);
|
||||
return wq;
|
||||
}
|
||||
|
||||
static Chan*
|
||||
skelopen(Chan *c, int omode)
|
||||
{
|
||||
if(!(c->qid.type & QTDIR))
|
||||
error(Eperm);
|
||||
if(omode != OREAD)
|
||||
error(Ebadarg);
|
||||
|
||||
c->mode = omode;
|
||||
c->flag |= COPEN;
|
||||
c->offset = 0;
|
||||
return c;
|
||||
}
|
||||
|
||||
static void
|
||||
skelclose(Chan *c)
|
||||
{
|
||||
Skel *f;
|
||||
|
||||
f = c->aux;
|
||||
wlock(f);
|
||||
f->ref--;
|
||||
if(f->ref == 0){
|
||||
wunlock(f);
|
||||
free(f);
|
||||
} else
|
||||
wunlock(f);
|
||||
}
|
||||
|
||||
static long
|
||||
skelread(Chan *c, void *va, long n, vlong)
|
||||
{
|
||||
return devdirread(c, va, n, nil, 0, skelgen);
|
||||
}
|
||||
|
||||
static long
|
||||
skelwrite(Chan*, void*, long, vlong)
|
||||
{
|
||||
error(Ebadusefd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
skelstat(Chan *c, uchar *db, int n)
|
||||
{
|
||||
Dir dir;
|
||||
|
||||
if(step(c, &dir, 0) < 0)
|
||||
error(Enonexist);
|
||||
|
||||
n = convD2M(&dir, db, n);
|
||||
if(n < BIT16SZ)
|
||||
error(Eshortstat);
|
||||
return n;
|
||||
}
|
||||
|
||||
Dev skeldevtab = {
|
||||
'z',
|
||||
"skel",
|
||||
|
||||
devreset,
|
||||
devinit,
|
||||
devshutdown,
|
||||
skelattach,
|
||||
skelwalk,
|
||||
skelstat,
|
||||
skelopen,
|
||||
devcreate,
|
||||
skelclose,
|
||||
skelread,
|
||||
devbread,
|
||||
skelwrite,
|
||||
devbwrite,
|
||||
devremove,
|
||||
devwstat,
|
||||
};
|
|
@ -29,7 +29,7 @@ binderr(char *new, char *old, int flag)
|
|||
dash[1] = 'a';
|
||||
break;
|
||||
}
|
||||
print("bind %s %s %s\n", dash, new, old);
|
||||
fprint(2, "bind %s %s %s\n", dash, new, old);
|
||||
}
|
||||
if(bind(new, old, flag) < 0)
|
||||
sysfatal("bind: %r");
|
||||
|
@ -72,10 +72,10 @@ sandbox(char **names, int *flags, int nname)
|
|||
Dir *d;
|
||||
int i, j, n;
|
||||
|
||||
snprint(rootskel, sizeof rootskel, "#zd/newroot.%d", getpid());
|
||||
snprint(rootskel, sizeof rootskel, "/mnt/d/newroot.%d", getpid());
|
||||
binderr(rootskel, "/", MBEFORE);
|
||||
|
||||
newroot = rootskel + strlen("#zd");
|
||||
newroot = rootskel + strlen("/mnt/d");
|
||||
|
||||
for(j = 0; j < nname; j++){
|
||||
if(names[j] == nil)
|
||||
|
@ -97,9 +97,9 @@ sandbox(char **names, int *flags, int nname)
|
|||
if(d == nil)
|
||||
continue;
|
||||
if(d->mode & DMDIR)
|
||||
snprint(skel, sizeof skel, "#zd/%s", parts[i]);
|
||||
snprint(skel, sizeof skel, "/mnt/d/%s", parts[i]);
|
||||
else
|
||||
snprint(skel, sizeof skel, "#zf/%s", parts[i]);
|
||||
snprint(skel, sizeof skel, "/mnt/f/%s", parts[i]);
|
||||
free(d);
|
||||
binderr(skel, dir, MBEFORE);
|
||||
}
|
||||
|
@ -108,6 +108,31 @@ sandbox(char **names, int *flags, int nname)
|
|||
binderr(newroot, "/", MREPL);
|
||||
}
|
||||
|
||||
void
|
||||
skelfs(void)
|
||||
{
|
||||
int p[2];
|
||||
int dfd;
|
||||
|
||||
pipe(p);
|
||||
switch(rfork(RFFDG|RFREND|RFPROC|RFNAMEG)){
|
||||
case -1:
|
||||
sysfatal("fork");
|
||||
case 0:
|
||||
close(p[1]);
|
||||
dup(p[0], 0);
|
||||
dup(p[0], 1);
|
||||
execl("/bin/skelfs", "skelfs", debug > 1 ? "-Di" : "-i", nil);
|
||||
sysfatal("exec /bin/skelfs: %r");
|
||||
}
|
||||
close(p[0]);
|
||||
dfd = dup(p[1], -1);
|
||||
if(mount(p[1], -1, "/mnt/f", MREPL, "file") < 0)
|
||||
sysfatal("/mnt/f mount setup: %r");
|
||||
if(mount(dfd, -1, "/mnt/d", MREPL, "dir") < 0)
|
||||
sysfatal("/mnt/d mount setup: %r");
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
|
@ -129,8 +154,10 @@ main(int argc, char **argv)
|
|||
nparts = 0;
|
||||
memset(devs, 0, sizeof devs);
|
||||
ARGBEGIN{
|
||||
case 'D':
|
||||
debug++;
|
||||
case 'd':
|
||||
debug = 1;
|
||||
debug++;
|
||||
break;
|
||||
case 'r':
|
||||
parts[nparts] = EARGF(usage());
|
||||
|
@ -164,6 +191,7 @@ main(int argc, char **argv)
|
|||
argv[0] = b;
|
||||
|
||||
rfork(RFNAMEG|RFFDG);
|
||||
skelfs();
|
||||
dfd = open("/dev/drivers", OWRITE|OCEXEC);
|
||||
if(dfd < 0)
|
||||
sysfatal("could not /dev/drivers: %r");
|
||||
|
@ -172,7 +200,7 @@ main(int argc, char **argv)
|
|||
sandbox(parts, mflags, nparts);
|
||||
|
||||
if(debug)
|
||||
print("chdev %s\n", devs);
|
||||
fprint(2, "chdev %s\n", devs);
|
||||
|
||||
if(devs[0] != '\0'){
|
||||
if(fprint(dfd, "chdev & %s", devs) <= 0)
|
||||
|
|
251
sys/src/cmd/skelfs.c
Normal file
251
sys/src/cmd/skelfs.c
Normal file
|
@ -0,0 +1,251 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <fcall.h>
|
||||
#include <thread.h>
|
||||
#include <9p.h>
|
||||
|
||||
typedef struct Skel Skel;
|
||||
struct Skel {
|
||||
char name[64];
|
||||
char mode;
|
||||
};
|
||||
|
||||
static uvlong sessions;
|
||||
static char defmode;
|
||||
|
||||
enum{
|
||||
Qroot,
|
||||
Qdir,
|
||||
Qskel,
|
||||
};
|
||||
|
||||
#define qtype(x) (((ulong)x)&0x1f)
|
||||
#define qsess(x) ((((ulong)x))>>5)
|
||||
#define mkqid(i,t) ((((ulong)i)<<5)|(t))
|
||||
|
||||
static int
|
||||
step(Fid *f, int way, Qid *res, Dir *dp)
|
||||
{
|
||||
Skel *s;
|
||||
int path;
|
||||
char *name;
|
||||
ulong perm;
|
||||
|
||||
s = f->aux;
|
||||
name = s->name;
|
||||
perm = 0550|DMDIR;
|
||||
path = qtype(f->qid.path) + way;
|
||||
if(!name[0] && way > 0)
|
||||
return -1;
|
||||
|
||||
if(path < 0)
|
||||
goto Root;
|
||||
switch(path){
|
||||
Root:
|
||||
case Qroot:
|
||||
name = "/";
|
||||
/* fallthrough */
|
||||
case Qdir:
|
||||
res->type = QTDIR;
|
||||
break;
|
||||
case Qskel:
|
||||
switch(s->mode){
|
||||
case 'd':
|
||||
res->type = QTDIR;
|
||||
break;
|
||||
case 'f':
|
||||
default:
|
||||
res->type = QTFILE;
|
||||
perm = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
res->vers = qsess(f->qid.path);
|
||||
res->path = mkqid(res->vers, path);
|
||||
if(dp){
|
||||
dp->mode = perm;
|
||||
dp->name = estrdup9p(name);
|
||||
dp->uid = estrdup9p("sys");
|
||||
dp->gid = estrdup9p("sys");
|
||||
dp->qid = *res;
|
||||
dp->length = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
dirgen(int i, Dir *d, void *a)
|
||||
{
|
||||
Fid *f;
|
||||
Qid q;
|
||||
|
||||
if(i > 0)
|
||||
return -1;
|
||||
f = a;
|
||||
return step(f, 1, &q, d);
|
||||
}
|
||||
|
||||
static void
|
||||
fidclunk(Fid *fid)
|
||||
{
|
||||
free(fid->aux);
|
||||
}
|
||||
|
||||
static char*
|
||||
fsclone(Fid *old, Fid *new, void*)
|
||||
{
|
||||
Skel *s, *s2;
|
||||
|
||||
s = old->aux;
|
||||
s2 = emalloc9p(sizeof *s2);
|
||||
if(s2 == nil)
|
||||
return "out of memory";
|
||||
memset(s2, 0, sizeof *s2);
|
||||
|
||||
s2->mode = s->mode;
|
||||
utfecpy(s2->name, &s2->name[sizeof s2->name-1], s->name);
|
||||
new->aux = s2;
|
||||
return nil;
|
||||
}
|
||||
|
||||
static char*
|
||||
fswalk1(Fid *old, char *name, void*)
|
||||
{
|
||||
Skel *s;
|
||||
|
||||
if(strcmp("..", name) == 0){
|
||||
step(old, -1, &old->qid, nil);
|
||||
return nil;
|
||||
}
|
||||
|
||||
s = old->aux;
|
||||
if(!s->name[0] && qtype(old->qid.path) == Qroot && s->mode != 'e'){
|
||||
utfecpy(s->name, &s->name[sizeof s->name-1], name);
|
||||
old->qid.vers = sessions++;
|
||||
old->qid.path = mkqid(old->qid.vers, qtype(old->qid.path));
|
||||
} else if(strcmp(name, s->name) != 0)
|
||||
return "does not exist";
|
||||
|
||||
if(step(old, 1, &old->qid, nil) < 0)
|
||||
return "does not exist";
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
fswalk(Req *r)
|
||||
{
|
||||
walkandclone(r, fswalk1, fsclone, nil);
|
||||
}
|
||||
|
||||
static void
|
||||
fsattach(Req *r)
|
||||
{
|
||||
Skel s;
|
||||
Fid root;
|
||||
char *spec;
|
||||
Qid *q;
|
||||
|
||||
spec = r->ifcall.aname;
|
||||
if(spec && spec[0] != '\0')
|
||||
s.mode = spec[0];
|
||||
else
|
||||
s.mode = defmode;
|
||||
|
||||
q = &r->fid->qid;
|
||||
q->vers = sessions++;
|
||||
q->path = mkqid(q->vers, Qroot);
|
||||
q->type = QTDIR;
|
||||
r->ofcall.qid = *q;
|
||||
|
||||
s.name[0] = '\0';
|
||||
root.aux = &s;
|
||||
respond(r, fsclone(&root, r->fid, nil));
|
||||
}
|
||||
|
||||
static void
|
||||
fsstat(Req *r)
|
||||
{
|
||||
Qid q;
|
||||
|
||||
if(step(r->fid, 0, &q, &r->d) < 0)
|
||||
respond(r, "does not exist");
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
static void
|
||||
fsread(Req *r)
|
||||
{
|
||||
dirread9p(r, dirgen, r->fid);
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
static void
|
||||
fsopen(Req *r)
|
||||
{
|
||||
r->ofcall.mode = r->ifcall.mode;
|
||||
if(r->ifcall.mode != OREAD)
|
||||
respond(r, "permission denied");
|
||||
else
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
Srv fs=
|
||||
{
|
||||
.attach= fsattach,
|
||||
.open= fsopen,
|
||||
.read= fsread,
|
||||
.stat= fsstat,
|
||||
.walk= fswalk,
|
||||
.destroyfid= fidclunk
|
||||
};
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s [ -Di ] [ -s service ] [ -t mode ] [ mntpt ]\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *s, *mode;
|
||||
int stdio;
|
||||
|
||||
s = nil;
|
||||
stdio = 0;
|
||||
defmode = 'f';
|
||||
ARGBEGIN{
|
||||
case 'D':
|
||||
chatty9p++;
|
||||
break;
|
||||
case 's':
|
||||
s = EARGF(usage());
|
||||
break;
|
||||
case 'i':
|
||||
stdio = 1;
|
||||
break;
|
||||
case 't':
|
||||
mode = EARGF(usage());
|
||||
defmode = mode[0];
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
if(argc > 1)
|
||||
usage();
|
||||
|
||||
if(stdio == 0){
|
||||
postmountsrv(&fs, s, argc ? argv[0] : "/mnt/skel", MREPL);
|
||||
exits(nil);
|
||||
}
|
||||
fs.infd = 0;
|
||||
fs.outfd = 1;
|
||||
srv(&fs);
|
||||
exits(nil);
|
||||
}
|
Loading…
Reference in a new issue