ref: c12022fd8c434860accb237b9bad9bd7cd9ed2db
parent: c7c0ff5db6137662e435d66338cfa4e68c64598e
author: Jacob Moody <moody@posixcafe.org>
date: Wed Jun 15 02:42:05 EDT 2022
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'.
--- a/sys/man/3/skel
+++ /dev/null
@@ -1,35 +1,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
--- /dev/null
+++ b/sys/man/4/skelfs
@@ -1,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
--- a/sys/src/9/pc/pc
+++ b/sys/src/9/pc/pc
@@ -18,7 +18,6 @@
kprof
fs
dtracy
- skel
ether netif
bridge netif log
--- a/sys/src/9/pc64/pc64
+++ b/sys/src/9/pc64/pc64
@@ -41,7 +41,6 @@
segment
vmx
dtracy
- skel
link
# devpccard pci
--- a/sys/src/9/port/devskel.c
+++ /dev/null
@@ -1,239 +1,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,
-};
--- a/sys/src/cmd/auth/box.c
+++ b/sys/src/cmd/auth/box.c
@@ -29,7 +29,7 @@
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 @@
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 @@
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);
}
@@ -109,6 +109,31 @@
}
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)
{
fprint(2, "usage %s: [ -d ] [ -r file ] [ -c dir ] [ -e devs ] cmd args...\n", argv0);
@@ -129,8 +154,10 @@
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 @@
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 @@
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)
--- /dev/null
+++ b/sys/src/cmd/skelfs.c
@@ -1,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);
+}