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:
Jacob Moody 2022-06-15 06:42:05 +00:00
parent c7c0ff5db6
commit c12022fd8c
7 changed files with 351 additions and 283 deletions

View file

@ -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
View 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

View file

@ -18,7 +18,6 @@ dev
kprof
fs
dtracy
skel
ether netif
bridge netif log

View file

@ -41,7 +41,6 @@ dev
segment
vmx
dtracy
skel
link
# devpccard pci

View file

@ -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,
};

View file

@ -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
View 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);
}