summaryrefslogtreecommitdiff
path: root/tools/libstyx
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 20:52:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 20:52:35 +0000
commit46439007cf417cbd9ac8049bb4122c890097a0fa (patch)
tree6fdb25e5f3a2b6d5657eb23b35774b631d4d97e4 /tools/libstyx
parent37da2899f40661e3e9631e497da8dc59b971cbd0 (diff)
20060303-partial
Diffstat (limited to 'tools/libstyx')
-rw-r--r--tools/libstyx/Nt.c172
-rw-r--r--tools/libstyx/Plan9.c314
-rw-r--r--tools/libstyx/Posix.c164
-rw-r--r--tools/libstyx/mkfile11
-rw-r--r--tools/libstyx/styxaux.h14
-rw-r--r--tools/libstyx/styxserver.c1067
-rw-r--r--tools/libstyx/styxserver.h97
7 files changed, 1839 insertions, 0 deletions
diff --git a/tools/libstyx/Nt.c b/tools/libstyx/Nt.c
new file mode 100644
index 00000000..682eca6b
--- /dev/null
+++ b/tools/libstyx/Nt.c
@@ -0,0 +1,172 @@
+#include <windows.h>
+#include <lib9.h>
+#include "styxserver.h"
+#include "styxaux.h"
+
+typedef struct Fdset Fdset;
+
+struct Fdset
+{
+ fd_set infds, outfds, excfds, r_infds, r_outfds, r_excfds;
+};
+
+int
+styxinitsocket(void)
+{
+ WSADATA wsaData;
+ WORD wVersionRequired=MAKEWORD(1,1);
+
+ int rv = WSAStartup(wVersionRequired, &wsaData);
+
+ if(rv != 0){
+ fprint(2, "Unable to Find winsock.dll");
+ return -1;
+ }
+ if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1 ){
+ fprint(2, "Unable to find winsock.dll V1.1 or later");
+ return -1;
+ }
+ return 0;
+}
+
+void
+styxendsocket(void)
+{
+ WSACleanup( );
+}
+
+void
+styxclosesocket(int fd)
+{
+ closesocket(fd);
+}
+
+int
+styxannounce(Styxserver *server, char *port)
+{
+ struct sockaddr_in sin;
+ int s, one;
+
+ USED(server);
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if(s < 0)
+ return s;
+ one = 1;
+ if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0)
+ fprint(2, "setsockopt failed\n");
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = 0;
+ sin.sin_port = htons(atoi(port));
+ if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
+ close(s);
+ return -1;
+ }
+ if(listen(s, 20) < 0){
+ close(s);
+ return -1;
+ }
+ return s;
+}
+
+int
+styxaccept(Styxserver *server)
+{
+ struct sockaddr_in sin;
+ int len, s;
+
+ len = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ s = accept(server->connfd, (struct sockaddr *)&sin, &len);
+ if(s < 0){
+ if(errno != EINTR)
+ fprint(2, "error in accept: %s\n", strerror(errno));
+ }
+ return s;
+}
+
+void
+styxinitwait(Styxserver *server)
+{
+ Fdset *fs;
+
+ server->priv = fs = malloc(sizeof(Fdset));
+ FD_ZERO(&fs->infds);
+ FD_ZERO(&fs->outfds);
+ FD_ZERO(&fs->excfds);
+ FD_SET(server->connfd, &fs->infds);
+}
+
+int
+styxnewcall(Styxserver *server)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ return FD_ISSET(server->connfd, &fs->r_infds);
+}
+
+void
+styxnewclient(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ FD_SET(s, &fs->infds);
+}
+
+void
+styxfreeclient(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ FD_CLR(s, &fs->infds);
+}
+
+int
+styxnewmsg(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ return FD_ISSET(s, &fs->r_infds) || FD_ISSET(s, &fs->r_excfds);
+}
+
+char*
+styxwaitmsg(Styxserver *server)
+{
+ struct timeval seltime;
+ int nfds;
+ Fdset *fs;
+
+ fs = server->priv;
+ fs->r_infds = fs->infds;
+ fs->r_outfds = fs->outfds;
+ fs->r_excfds = fs->excfds;
+ seltime.tv_sec = 10;
+ seltime.tv_usec = 0L;
+ nfds = select(sizeof(fd_set)*8, &fs->r_infds, &fs->r_outfds, &fs->r_excfds, &seltime);
+ if(nfds < 0 && errno != EINTR)
+ return"error in select";
+ return nil;
+}
+
+int
+styxrecv(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ return recv(fd, buf, n, m);
+}
+
+int
+styxsend(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ return send(fd, buf, n, m);
+}
+
+void
+styxexit(int n)
+{
+ exit(n);
+}
diff --git a/tools/libstyx/Plan9.c b/tools/libstyx/Plan9.c
new file mode 100644
index 00000000..3e92d443
--- /dev/null
+++ b/tools/libstyx/Plan9.c
@@ -0,0 +1,314 @@
+#include <lib9.h>
+#include "styxserver.h"
+#include "styxaux.h"
+
+typedef struct Listener Listener;
+typedef struct Reader Reader;
+typedef struct Union Union;
+
+struct Listener
+{
+ int fd;
+ char *dir;
+ Listener *next;
+};
+
+struct Reader
+{
+ int pid;
+ int fd;
+ int n;
+ char buf[MSGMAX];
+ char rbuf[MSGMAX];
+ Reader *next;
+};
+
+struct Union
+{
+ int pid;
+ Lock lock;
+ Listener *lr;
+ Reader *rr;
+};
+
+void xlock(Lock *l){ lock(l); }
+void xunlock(Lock *l){ unlock(l); }
+
+static Reader*
+findr(Styxserver *server, int fd)
+{
+ Reader *r;
+ Union *u;
+
+ u = server->priv;
+ xlock(&u->lock);
+ for(r = u->rr; r != nil; r = r->next)
+ if(r->fd == fd)
+ break;
+ xunlock(&u->lock);
+ return r;
+}
+
+int
+styxinitsocket(void)
+{
+ return 0;
+}
+
+void
+styxendsocket(void)
+{
+}
+
+void
+styxclosesocket(int fd)
+{
+ close(fd);
+}
+
+static void
+listener(Styxserver *server, int afd, char *adir)
+{
+ int s;
+ Listener *l;
+ Union *u;
+ char ld[40];
+
+ USED(afd);
+ u = server->priv;
+ for(;;){
+ s = listen(adir, ld);
+ /* fprint(2, "listen %d %s %s\n", s, adir, ld); */
+ if(s < 0){
+ u->pid = -1;
+ break;
+ }
+ l = malloc(sizeof(Listener));
+ l->fd = s;
+ l->dir = strdup(ld);
+ xlock(&u->lock);
+ l->next = u->lr;
+ u->lr = l;
+ xunlock(&u->lock);
+ }
+}
+
+int
+styxannounce(Styxserver *server, char *port)
+{
+ int s, pid;
+ Union *u;
+ char adr[32], adir[40];
+
+ server->priv = u = malloc(sizeof(Union));
+ u->lock.val = 0;
+ u->lr = nil;
+ u->rr = nil;
+ sprint(adr, "tcp!*!%s", port);
+ s = announce(adr, adir);
+ /* fprint(2, "announce %d %s %s\n", s, adr, adir); */
+ if(s < 0)
+ return s;
+ switch((pid = rfork(RFPROC|RFREND|RFMEM))){
+ case 0:
+ listener(server, s, strdup(adir));
+ break;
+ default:
+ u->pid = pid;
+ break;
+ }
+ return s;
+}
+
+static void
+reader(Styxserver *server, Reader *r)
+{
+ int m, n, s;
+ Union *u;
+
+ u = server->priv;
+ s = r->fd;
+ for(;;){
+ n = r->n;
+ if(n < 0){
+ r->pid = -1;
+ exits(nil);
+ }
+ m = read(s, r->rbuf, MSGMAX-n);
+ xlock(&u->lock);
+ n = r->n;
+ if(m < 0)
+ r->n = n == 0 ? m : n;
+ else{
+ memmove(r->buf+n, r->rbuf, m);
+ r->n = m+n;
+ }
+ xunlock(&u->lock);
+ }
+}
+
+int
+styxaccept(Styxserver *server)
+{
+ int s, fd, pid;
+ Reader *r;
+ Listener *l;
+ Union *u;
+ char *dir;
+
+ u = server->priv;
+ xlock(&u->lock);
+ if((l = u->lr) == nil){
+ xunlock(&u->lock);
+ return -1;
+ }
+ u->lr = l->next;
+ xunlock(&u->lock);
+ fd = l->fd;
+ dir = l->dir;
+ free(l);
+ s = accept(fd, dir);
+ /* fprint(2, "accept %d\n", s); */
+ free(dir);
+ if(s < 0)
+ return s;
+ r = malloc(sizeof(struct Reader));
+ r->fd = s;
+ r->n = 0;
+ xlock(&u->lock);
+ r->next = u->rr;
+ u->rr = r;
+ xunlock(&u->lock);
+ switch((pid = rfork(RFPROC|RFREND|RFMEM))){
+ case 0:
+ reader(server, r);
+ break;
+ case 1:
+ r->pid = pid;
+ break;
+ }
+ return s;
+}
+
+void
+styxinitwait(Styxserver *server)
+{
+ USED(server);
+}
+
+int
+styxnewcall(Styxserver *server)
+{
+ Union *u;
+
+ u = server->priv;
+ return u->lr != nil;
+}
+
+int
+styxnewmsg(Styxserver *server, int fd)
+{
+ Reader *r;
+
+ r = findr(server, fd);
+ return r != nil && r->n != 0;
+}
+
+void
+styxnewclient(Styxserver *server, int fd)
+{
+ USED(server);
+ USED(fd);
+}
+
+void
+styxfreeclient(Styxserver *server, int fd)
+{
+ Reader *r, **rp;
+ Union *u;
+
+ u = server->priv;
+ r = findr(server, fd);
+ if(r == nil)
+ return;
+ xlock(&u->lock);
+ for(rp = &u->rr; *rp != nil; rp = &(*rp)->next)
+ if(r == *rp){
+ *rp = r->next;
+ break;
+ }
+ xunlock(&u->lock);
+ if(r->pid >= 0)
+ postnote(PNPROC, r->pid, "kill");
+ free(r);
+}
+
+char*
+styxwaitmsg(Styxserver *server)
+{
+ int i;
+ Reader *r;
+ Union *u;
+
+ u = server->priv;
+ for(i = 0; i < 100; i++){
+ if(u->lr != nil)
+ return nil;
+ xlock(&u->lock);
+ for(r = u->rr; r != nil; r = r->next)
+ if(r->n != 0){
+ xunlock(&u->lock);
+ return nil;
+ }
+ xunlock(&u->lock);
+ sleep(100);
+ }
+ return nil;
+}
+
+int
+styxrecv(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ Reader *r;
+ Union *u;
+ int rn;
+ char *rbuf;
+
+ USED(m);
+ r = findr(server, fd);
+ if(r == nil)
+ return -1;
+ u = server->priv;
+ xlock(&u->lock);
+ rn = r->n;
+ rbuf = r->buf;
+ if(rn < 0){
+ xunlock(&u->lock);
+ return rn;
+ }
+ if(n > rn)
+ n = rn;
+ memmove(buf, rbuf, n);
+ rn -= n;
+ memmove(rbuf, rbuf+n, rn);
+ r->n = rn;
+ xunlock(&u->lock);
+ return n;
+}
+
+int
+styxsend(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ USED(server);
+ USED(m);
+ return write(fd, buf, n);
+}
+
+void
+styxexit(int n)
+{
+ if(n)
+ exits("error");
+ else
+ exits(nil);
+}
diff --git a/tools/libstyx/Posix.c b/tools/libstyx/Posix.c
new file mode 100644
index 00000000..dbadfd9f
--- /dev/null
+++ b/tools/libstyx/Posix.c
@@ -0,0 +1,164 @@
+#define __EXTENSIONS__
+#define _BSD_COMPAT
+#include <lib9.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "styxserver.h"
+#include "styxaux.h"
+
+typedef struct Fdset Fdset;
+
+struct Fdset
+{
+ fd_set infds, outfds, excfds, r_infds, r_outfds, r_excfds;
+};
+
+int
+styxinitsocket(void)
+{
+ return 0;
+}
+
+void
+styxendsocket(void)
+{
+}
+
+void
+styxclosesocket(int fd)
+{
+ close(fd);
+}
+
+int
+styxannounce(Styxserver *server, char *port)
+{
+ struct sockaddr_in sin;
+ int s, one;
+
+ USED(server);
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if(s < 0)
+ return s;
+ one = 1;
+ if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0)
+ fprint(2, "setsockopt failed\n");
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = 0;
+ sin.sin_port = htons(atoi(port));
+ if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0){
+ close(s);
+ return -1;
+ }
+ if(listen(s, 20) < 0){
+ close(s);
+ return -1;
+ }
+ return s;
+}
+
+int
+styxaccept(Styxserver *server)
+{
+ struct sockaddr_in sin;
+ int len, s;
+
+ len = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ s = accept(server->connfd, (struct sockaddr *)&sin, &len);
+ if(s < 0){
+ if(errno != EINTR)
+ fprint(2, "error in accept: %s\n", strerror(errno));
+ }
+ return s;
+}
+
+void
+styxinitwait(Styxserver *server)
+{
+ Fdset *fs;
+
+ server->priv = fs = malloc(sizeof(Fdset));
+ FD_ZERO(&fs->infds);
+ FD_ZERO(&fs->outfds);
+ FD_ZERO(&fs->excfds);
+ FD_SET(server->connfd, &fs->infds);
+}
+
+int
+styxnewcall(Styxserver *server)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ return FD_ISSET(server->connfd, &fs->r_infds);
+}
+
+void
+styxnewclient(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ FD_SET(s, &fs->infds);
+}
+
+void
+styxfreeclient(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ FD_CLR(s, &fs->infds);
+}
+
+int
+styxnewmsg(Styxserver *server, int s)
+{
+ Fdset *fs;
+
+ fs = server->priv;
+ return FD_ISSET(s, &fs->r_infds) || FD_ISSET(s, &fs->r_excfds);
+}
+
+char*
+styxwaitmsg(Styxserver *server)
+{
+ struct timeval seltime;
+ int nfds;
+ Fdset *fs;
+
+ fs = server->priv;
+ fs->r_infds = fs->infds;
+ fs->r_outfds = fs->outfds;
+ fs->r_excfds = fs->excfds;
+ seltime.tv_sec = 10;
+ seltime.tv_usec = 0L;
+ nfds = select(sizeof(fd_set)*8, &fs->r_infds, &fs->r_outfds, &fs->r_excfds, &seltime);
+ if(nfds < 0 && errno != EINTR)
+ return"error in select";
+ return nil;
+}
+
+int
+styxrecv(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ return recv(fd, buf, n, m);
+}
+
+int
+styxsend(Styxserver *server, int fd, char *buf, int n, int m)
+{
+ return send(fd, buf, n, m);
+}
+
+void
+styxexit(int n)
+{
+ exit(n);
+}
diff --git a/tools/libstyx/mkfile b/tools/libstyx/mkfile
new file mode 100644
index 00000000..1840bd2f
--- /dev/null
+++ b/tools/libstyx/mkfile
@@ -0,0 +1,11 @@
+<../../mkconfig
+
+LIB=libstyx.a
+
+OFILES=\
+ styxserver.$O\
+ $TARGMODEL.$O\
+
+HFILES=\
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
diff --git a/tools/libstyx/styxaux.h b/tools/libstyx/styxaux.h
new file mode 100644
index 00000000..030a49fb
--- /dev/null
+++ b/tools/libstyx/styxaux.h
@@ -0,0 +1,14 @@
+int styxinitsocket(void);
+void styxendsocket(void);
+void styxclosesocket(int);
+int styxannounce(Styxserver*, char *);
+void styxinitwait(Styxserver*);
+int styxnewcall(Styxserver*);
+int styxnewmsg(Styxserver*, int);
+int styxaccept(Styxserver *server);
+void styxnewclient(Styxserver*, int);
+void styxfreeclient(Styxserver*, int);
+char* styxwaitmsg(Styxserver*);
+int styxrecv(Styxserver*, int, char*, int, int);
+int styxsend(Styxserver*, int, char*, int, int);
+void styxexit(int);
diff --git a/tools/libstyx/styxserver.c b/tools/libstyx/styxserver.c
new file mode 100644
index 00000000..d0bf1c2c
--- /dev/null
+++ b/tools/libstyx/styxserver.c
@@ -0,0 +1,1067 @@
+#include <lib9.h>
+#include <styx.h>
+#include "styxserver.h"
+#include "styxaux.h"
+
+#define MAXSTAT 512
+#define EMSGLEN 256 /* %r */
+
+#define TABSZ 32 /* power of 2 */
+
+static unsigned long boottime;
+static char* eve = "inferno";
+static int Debug = 0;
+
+char Enomem[] = "out of memory";
+char Eperm[] = "permission denied";
+char Enodev[] = "no free devices";
+char Ehungup[] = "write to hungup channel";
+char Eexist[] = "file exists";
+char Enonexist[] = "file does not exist";
+char Ebadcmd[] = "bad command";
+char Ebadarg[] = "bad arg in system call";
+char Enofid[] = "no such fid";
+char Enotdir[] = "not a directory";
+char Eopen[] = "already open";
+char Ebadfid[] = "bad fid";
+
+/* client state */
+enum{
+ CDISC = 01,
+ CNREAD = 02,
+ CRECV = 04,
+};
+
+typedef struct Walkqid Walkqid;
+
+struct Fid
+{
+ Client *client;
+ Fid *next;
+ short fid;
+ ushort open;
+ ushort mode; /* read/write */
+ ulong offset; /* in file */
+ int dri; /* dirread index */
+ Qid qid;
+};
+
+struct Walkqid
+{
+ Fid *clone;
+ int nqid;
+ Qid qid[1];
+};
+
+#define ASSERT(A,B) styxassert((int)A,B)
+
+static int hash(Path);
+static void deletefids(Client *);
+
+static void
+styxfatal(char *fmt, ...)
+{
+ char buf[1024], *out;
+ va_list arg;
+ out = seprint(buf, buf+sizeof(buf), "Fatal error: ");
+ va_start(arg, fmt);
+ out = vseprint(out, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ write(2, buf, out-buf);
+ styxexit(1);
+}
+
+static void
+styxassert(int true, char *reason)
+{
+ if(!true)
+ styxfatal("assertion failed: %s\n", reason);
+}
+
+void *
+styxmalloc(int bytes)
+{
+ char *m = malloc(bytes);
+ if(m == nil)
+ styxfatal(Enomem);
+ memset(m, 0, bytes);
+ return m;
+}
+
+void
+styxfree(void *p)
+{
+ free(p);
+}
+
+void
+styxdebug()
+{
+ Debug = 1;
+}
+
+static Client *
+newclient(Styxserver *server, int fd)
+{
+ Client *c = (Client *)styxmalloc(sizeof(Client));
+
+ if(Debug)
+ fprint(2, "New client at %lux\n", (ulong)c);
+ c->server = server;
+ c->fd = fd;
+ c->nread = 0;
+ c->nc = 0;
+ c->state = 0;
+ c->fids = nil;
+ c->uname = strdup(eve);
+ c->aname = strdup(eve);
+ c->next = server->clients;
+ server->clients = c;
+ if(server->ops->newclient)
+ server->ops->newclient(c);
+ return c;
+}
+
+static void
+freeclient(Client *c)
+{
+ Client **p;
+ Styxserver *server;
+
+ if(Debug)
+ fprint(2, "Freeing client at %lux\n", (ulong)c);
+ server = c->server;
+ if(server->ops->freeclient)
+ server->ops->freeclient(c);
+ for(p = &server->clients; *p; p = &(*p)->next)
+ if(*p == c){
+ styxclosesocket(c->fd);
+ *p = c->next;
+ deletefids(c);
+ free(c->uname);
+ free(c->aname);
+ styxfree(c);
+ return;
+ }
+}
+
+static int
+nbread(Client *c, int nr)
+{
+ int nb;
+
+ if(c->state&CDISC)
+ return -1;
+ nb = styxrecv(c->server, c->fd, c->msg + c->nread, nr, 0);
+ if(nb <= 0){
+ c->nread = 0;
+ c->state |= CDISC;
+ return -1;
+ }
+ c->nread += nb;
+ return 0;
+}
+
+static int
+rd(Client *c, Fcall *r)
+{
+ if(c->nc > 0){ /* last convM2S consumed nc bytes */
+ c->nread -= c->nc;
+ if(c->nread < 0){
+ r->ename = "negative size in rd";
+ return -1;
+ }
+ memmove(c->msg, c->msg+c->nc, c->nread);
+ c->nc = 0;
+ }
+ if(c->state&CRECV){
+ if(nbread(c, MSGMAX - c->nread) != 0){
+ r->ename = "unexpected EOF";
+ return -1;
+ }
+ c->state &= ~CRECV;
+ }
+ c->nc = convM2S((uchar*)(c->msg), c->nread, r);
+ if(c->nc < 0){
+ r->ename = "bad message format";
+ return -1;
+ }
+ if(c->nc == 0 && c->nread > 0){
+ c->nread = 0;
+ c->state &= ~CNREAD;
+ return 0;
+ }
+ if(c->nread > c->nc)
+ c->state |= CNREAD;
+ else
+ c->state &= ~CNREAD;
+ if(c->nc == 0)
+ return 0;
+ /* fprint(2, "rd: %F\n", r); */
+ return 1;
+}
+
+static int
+wr(Client *c, Fcall *r)
+{
+ int n;
+ char buf[MSGMAX];
+
+ n = convS2M(r, (uchar*)buf, sizeof(buf));
+ if(n < 0){
+ r->ename = "bad message type in wr";
+ return -1;
+ }
+ /* fprint(2, "wr: %F\n", r); */
+ return styxsend(c->server, c->fd, buf, n, 0);
+}
+
+static void
+sremove(Styxserver *server, Styxfile *f)
+{
+ Styxfile *s, *next, **p;
+
+ if(f == nil)
+ return;
+ if(Debug)
+ fprint(2, "Remove file %s Qid=%llx\n", f->d.name, f->d.qid.path);
+ if(f->d.qid.type&QTDIR)
+ for(s = f->child; s != nil; s = next){
+ next = s->sibling;
+ sremove(server, s);
+ }
+ for(p = &server->ftab[hash(f->d.qid.path)]; *p; p = &(*p)->next)
+ if(*p == f){
+ *p = f->next;
+ break;
+ }
+ for(p = &f->parent->child; *p; p = &(*p)->sibling)
+ if(*p == f){
+ *p = f->sibling;
+ break;
+ }
+ styxfree(f->d.name);
+ styxfree(f->d.uid);
+ styxfree(f->d.gid);
+ styxfree(f);
+}
+
+int
+styxrmfile(Styxserver *server, Path qid)
+{
+ Styxfile *f;
+
+ f = styxfindfile(server, qid);
+ if(f != nil){
+ if(f->parent == nil)
+ return -1;
+ sremove(server, f);
+ return 0;
+ }
+ return -1;
+}
+
+static void
+incref(Styxfile *f)
+{
+ if(f != nil)
+ f->ref++;
+}
+
+static void
+decref(Styxfile *f)
+{
+ if(f != nil)
+ --f->ref;
+}
+
+static void
+increff(Fid *f)
+{
+ incref(styxfindfile(f->client->server, f->qid.path));
+}
+
+static void
+decreff(Fid *f)
+{
+ decref(styxfindfile(f->client->server, f->qid.path));
+}
+
+static void
+incopen(Fid *f)
+{
+ Styxfile *file;
+
+ if(f->open && (file = styxfindfile(f->client->server, f->qid.path)) != nil)
+ file->open++;
+}
+
+static void
+decopen(Fid *f)
+{
+ Styxfile *file;
+
+ if(f->open && (file = styxfindfile(f->client->server, f->qid.path)) != nil)
+ file->open--;
+}
+
+int
+styxperm(Styxfile *f, char *uid, int mode)
+{
+ int m, p;
+
+ p = 0;
+ switch(mode&3){
+ case OREAD: p = AREAD; break;
+ case OWRITE: p = AWRITE; break;
+ case ORDWR: p = AREAD+AWRITE; break;
+ case OEXEC: p = AEXEC; break;
+ }
+ if(mode&OTRUNC)
+ p |= AWRITE;
+ m = f->d.mode&7;
+ if((p&m) == p)
+ return 1;
+ if(strcmp(f->d.uid, uid) == 0){
+ m |= (f->d.mode>>6)&7;
+ if((p&m) == p)
+ return 1;
+ }
+ if(strcmp(f->d.gid, uid) == 0){
+ m |= (f->d.mode>>3)&7;
+ if((p&m) == p)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+hash(Path path)
+{
+ return path&(TABSZ-1);
+}
+
+Styxfile *
+styxfindfile(Styxserver *server, Path path)
+{
+ Styxfile *f;
+
+ for(f = server->ftab[hash(path)]; f != nil; f = f->next){
+ if(f->d.qid.path == path)
+ return f;
+ }
+ return nil;
+}
+
+static Fid *
+findfid(Client *c, short fid)
+{
+ Fid *f;
+ for(f = c->fids; f && f->fid != fid; f = f->next)
+ ;
+ return f;
+}
+
+static void
+deletefid(Client *c, Fid *d)
+{
+ /* TODO: end any outstanding reads on this fid */
+ Fid **f;
+
+ for(f = &c->fids; *f; f = &(*f)->next)
+ if(*f == d){
+ decreff(d);
+ decopen(d);
+ *f = d->next;
+ styxfree(d);
+ return;
+ }
+}
+
+static void
+deletefids(Client *c)
+{
+ Fid *f, *g;
+
+ for(f = c->fids; f; f = g){
+ decreff(f);
+ decopen(f);
+ g = f->next;
+ styxfree(f);
+ }
+}
+
+Fid *
+newfid(Client *c, short fid, Qid qid){
+ Fid *f;
+
+ f = styxmalloc(sizeof(Fid));
+ ASSERT(f, "newfid");
+ f->client = c;
+ f->fid = fid;
+ f->open = 0;
+ f->dri = 0;
+ f->qid = qid;
+ f->next = c->fids;
+ c->fids = f;
+ increff(f);
+ return f;
+}
+
+static void
+flushtag(int oldtag)
+{
+ USED(oldtag);
+}
+
+int
+eqqid(Qid a, Qid b)
+{
+ return a.path == b.path && a.vers == b.vers;
+}
+
+static Fid *
+fidclone(Fid *old, short fid)
+{
+ Fid *new;
+
+ new = newfid(old->client, fid, old->qid);
+ return new;
+}
+
+static Walkqid*
+devwalk(Client *c, Styxfile *file, Fid *fp, Fid *nfp, char **name, int nname, char **err)
+{
+ Styxserver *server;
+ long j;
+ Walkqid *wq;
+ char *n;
+ Styxfile *p, *f;
+ Styxops *ops;
+ Qid qid;
+
+ *err = nil;
+ server = c->server;
+ ops = server->ops;
+
+ wq = styxmalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ wq->nqid = 0;
+
+ p = file;
+ qid = p != nil ? p->d.qid : fp->qid;
+ for(j = 0; j < nname; j++){
+ if(!(qid.type&QTDIR)){
+ if(j == 0)
+ styxfatal("devwalk error");
+ *err = Enotdir;
+ goto Done;
+ }
+ if(p != nil && !styxperm(p, c->uname, OEXEC)){
+ *err = Eperm;
+ goto Done;
+ }
+ n = name[j];
+ if(strcmp(n, ".") == 0){
+ Accept:
+ wq->qid[wq->nqid++] = nfp->qid;
+ continue;
+ }
+ if(p != nil && strcmp(n, "..") == 0 && p->parent){
+ decref(p);
+ nfp->qid.path = p->parent->d.qid.path;
+ nfp->qid.type = p->parent->d.qid.type;
+ nfp->qid.vers = 0;
+ incref(p->parent);
+ p = p->parent;
+ qid = p->d.qid;
+ goto Accept;
+ }
+
+ if(ops->walk != nil){
+ char *e;
+
+ e = ops->walk(&qid, n);
+ if(e == nil){
+ decreff(nfp);
+ nfp->qid = qid;
+ increff(nfp);
+ p = styxfindfile(server, qid.path);
+ if(server->needfile && p == nil)
+ goto Done;
+ qid = p != nil ? p->d.qid : nfp->qid;
+ goto Accept;
+ }
+ }
+
+ if(p != nil)
+ for(f = p->child; f != nil; f = f->sibling){
+ if(strcmp(n, f->d.name) == 0){
+ decref(p);
+ nfp->qid.path = f->d.qid.path;
+ nfp->qid.type = f->d.qid.type;
+ nfp->qid.vers = 0;
+ incref(f);
+ p = f;
+ qid = p->d.qid;
+ goto Accept;
+ }
+ }
+ if(j == 0 && *err == nil)
+ *err = Enonexist;
+ goto Done;
+ }
+Done:
+ if(*err != nil){
+ styxfree(wq);
+ return nil;
+ }
+ return wq;
+}
+
+static long
+devdirread(Fid *fp, Styxfile *file, char *d, long n)
+{
+ long dsz, m;
+ Styxfile *f;
+ int i;
+
+ struct{
+ Dir d;
+ char slop[100]; /* TO DO */
+ }dir;
+
+ f = file->child;
+ for(i = 0; i < fp->dri; i++)
+ if(f == 0)
+ return 0;
+ else
+ f = f->sibling;
+ for(m = 0; m < n; fp->dri++){
+ if(f == nil)
+ break;
+ dir.d = f->d;
+ dsz = convD2M(&dir.d, (uchar*)d, n-m);
+ m += dsz;
+ d += dsz;
+ f = f->sibling;
+ }
+
+ return m;
+}
+
+static char*
+nilconv(char *s)
+{
+ if(s != nil && s[0] == '\0')
+ return nil;
+ return s;
+}
+
+static Styxfile *
+newfile(Styxserver *server, Styxfile *parent, int isdir, Path qid, char *name, int mode, char *owner)
+{
+ Styxfile *file;
+ Dir d;
+ int h;
+
+ if(qid == -1)
+ qid = server->qidgen++;
+ file = styxfindfile(server, qid);
+ if(file != nil)
+ return nil;
+ if(parent != nil){
+ for(file = parent->child; file != nil; file = file->sibling)
+ if(strcmp(name, file->d.name) == 0)
+ return nil;
+ }
+ file = (Styxfile *)styxmalloc(sizeof(Styxfile));
+ file->parent = parent;
+ file->child = nil;
+ h = hash(qid);
+ file->next = server->ftab[h];
+ server->ftab[h] = file;
+ if(parent){
+ file->sibling = parent->child;
+ parent->child = file;
+ }else
+ file->sibling = nil;
+
+ d.type = 'X';
+ d.dev = 'x';
+ d.qid.path = qid;
+ d.qid.type = 0;
+ d.qid.vers = 0;
+ d.mode = mode;
+ d.atime = time(0);
+ d.mtime = boottime;
+ d.length = 0;
+ d.name = strdup(name);
+ d.uid = strdup(owner);
+ d.gid = strdup(eve);
+ d.muid = "";
+
+ if(isdir){
+ d.qid.type |= QTDIR;
+ d.mode |= DMDIR;
+ }
+ else{
+ d.qid.type &= ~QTDIR;
+ d.mode &= ~DMDIR;
+ }
+
+ file->d = d;
+ file->ref = 0;
+ file->open = 0;
+ if(Debug)
+ fprint(2, "New file %s Qid=%llx\n", name, qid);
+ return file;
+}
+
+static void
+run(Client *c)
+{
+ Fcall f;
+ Fid *fp, *nfp;
+ int i, open, mode;
+ char ebuf[EMSGLEN];
+ Walkqid *wq;
+ Styxfile *file;
+ Dir dir;
+ Qid qid;
+ Styxops *ops;
+ char strs[128];
+
+ ebuf[0] = 0;
+ if(rd(c, &f) <= 0)
+ return;
+ if(f.type == Tflush){
+ flushtag(f.oldtag);
+ f.type = Rflush;
+ wr(c, &f);
+ return;
+ }
+ ops = c->server->ops;
+ file = nil;
+ fp = findfid(c, f.fid);
+ if(f.type != Tversion && f.type != Tauth && f.type != Tattach){
+ if(fp == nil){
+ f.type = Rerror;
+ f.ename = Enofid;
+ wr(c, &f);
+ return;
+ }
+ else{
+ file = styxfindfile(c->server, fp->qid.path);
+ if(c->server->needfile && file == nil){
+ f.type = Rerror;
+ f.ename = Enonexist;
+ wr(c, &f);
+ return;
+ }
+ }
+ }
+ /* if(fp == nil) fprint(2, "fid not found for %d\n", f.fid); */
+ switch(f.type){
+ case Twalk:
+ if(Debug){
+ fprint(2, "Twalk %d %d", f.fid, f.newfid);
+ for(i = 0; i < f.nwname; i++)
+ fprint(2, " %s", f.wname[i]);
+ fprint(2, "\n");
+ }
+ nfp = findfid(c, f.newfid);
+ f.type = Rerror;
+ if(nfp){
+ deletefid(c, nfp);
+ nfp = nil;
+ }
+ if(nfp){
+ f.ename = "fid in use";
+ if(Debug) fprint(2, "walk: %s\n", f.ename);
+ wr(c, &f);
+ break;
+ }else if(fp->open){
+ f.ename = "can't clone";
+ wr(c, &f);
+ break;
+ }
+ if(f.newfid != f.fid)
+ nfp = fidclone(fp, f.newfid);
+ else
+ nfp = fp;
+ if((wq = devwalk(c, file, fp, nfp, f.wname, f.nwname, &f.ename)) == nil){
+ if(nfp != fp)
+ deletefid(c, nfp);
+ f.type = Rerror;
+ }else{
+ if(nfp != fp){
+ if(wq->nqid != f.nwname)
+ deletefid(c, nfp);
+ }
+ f.type = Rwalk;
+ f.nwqid = wq->nqid;
+ for(i = 0; i < wq->nqid; i++)
+ f.wqid[i] = wq->qid[i];
+ styxfree(wq);
+ }
+ wr(c, &f);
+ break;
+ case Topen:
+ if(Debug)
+ fprint(2, "Topen %d\n", f.fid);
+ f.ename = nil;
+ if(fp->open)
+ f.ename = Eopen;
+ else if((fp->qid.type&QTDIR) && (f.mode&(OWRITE|OTRUNC|ORCLOSE)))
+ f.ename = Eperm;
+ else if(file != nil && !styxperm(file, c->uname, f.mode))
+ f.ename = Eperm;
+ else if((f.mode&ORCLOSE) && file != nil && file->parent != nil && !styxperm(file->parent, c->uname, OWRITE))
+ f.ename = Eperm;
+ if(f.ename != nil){
+ f.type = Rerror;
+ wr(c, &f);
+ break;
+ }
+ f.ename = Enonexist;
+ decreff(fp);
+ if(ops->open == nil || (f.ename = ops->open(&fp->qid, f.mode)) == nil){
+ f.type = Ropen;
+ f.qid = fp->qid;
+ fp->mode = f.mode;
+ fp->open = 1;
+ fp->offset = 0;
+ incopen(fp);
+ }
+ else
+ f.type = Rerror;
+ increff(fp);
+ wr(c, &f);
+ break;
+ case Tcreate:
+ if(Debug)
+ fprint(2, "Tcreate %d %s\n", f.fid, f.name);
+ f.ename = nil;
+ if(fp->open)
+ f.ename = Eopen;
+ else if(!(fp->qid.type&QTDIR))
+ f.ename = Enotdir;
+ else if((f.perm&DMDIR) && (f.mode&(OWRITE|OTRUNC|ORCLOSE)))
+ f.ename = Eperm;
+ else if(file != nil && !styxperm(file, c->uname, OWRITE))
+ f.ename = Eperm;
+ if(f.ename != nil){
+ f.type = Rerror;
+ wr(c, &f);
+ break;
+ }
+ f.ename = Eperm;
+ decreff(fp);
+ if(file != nil){
+ if(f.perm&DMDIR)
+ f.perm = (f.perm&~0777) | (file->d.mode&f.perm&0777) | DMDIR;
+ else
+ f.perm = (f.perm&(~0777|0111)) | (file->d.mode&f.perm&0666);
+ }
+ if(ops->create && (f.ename = ops->create(&fp->qid, f.name, f.perm, f.mode)) == nil){
+ f.type = Rcreate;
+ f.qid = fp->qid;
+ fp->mode = f.mode;
+ fp->open = 1;
+ fp->offset = 0;
+ incopen(fp);
+ }
+ else
+ f.type = Rerror;
+ increff(fp);
+ wr(c, &f);
+ break;
+ case Tread:
+ if(Debug)
+ fprint(2, "Tread %d\n", f.fid);
+ if(!fp->open){
+ f.type = Rerror;
+ f.ename = Ebadfid;
+ wr(c, &f);
+ break;
+ }
+ if(fp->qid.type&QTDIR || (file != nil && file->d.qid.type&QTDIR)){
+ f.type = Rread;
+ if(file == nil){
+ f.ename = Eperm;
+ if(ops->read && (f.ename = ops->read(fp->qid, c->data, (ulong*)(&f.count), fp->dri)) == nil){
+ f.data = c->data;
+ }
+ else
+ f.type = Rerror;
+ }
+ else{
+ f.count = devdirread(fp, file, c->data, f.count);
+ f.data = c->data;
+ }
+ }else{
+ f.ename = Eperm;
+ f.type = Rerror;
+ if(ops->read && (f.ename = ops->read(fp->qid, c->data, (ulong*)(&f.count), f.offset)) == nil){
+ f.type = Rread;
+ f.data = c->data;
+ }
+ }
+ wr(c, &f);
+ break;
+ case Twrite:
+ if(Debug)
+ fprint(2, "Twrite %d\n", f.fid);
+ if(!fp->open){
+ f.type = Rerror;
+ f.ename = Ebadfid;
+ wr(c, &f);
+ break;
+ }
+ f.ename = Eperm;
+ f.type = Rerror;
+ if(ops->write && (f.ename = ops->write(fp->qid, f.data, (ulong*)(&f.count), f.offset)) == nil){
+ f.type = Rwrite;
+ }
+ wr(c, &f);
+ break;
+ case Tclunk:
+ if(Debug)
+ fprint(2, "Tclunk %d\n", f.fid);
+ open = fp->open;
+ mode = fp->mode;
+ qid = fp->qid;
+ deletefid(c, fp);
+ f.type = Rclunk;
+ if(open && ops->close && (f.ename = ops->close(qid, mode)) != nil)
+ f.type = Rerror;
+ wr(c, &f);
+ break;
+ case Tremove:
+ if(Debug)
+ fprint(2, "Tremove %d\n", f.fid);
+ if(file != nil && file->parent != nil && !styxperm(file->parent, c->uname, OWRITE)){
+ f.type = Rerror;
+ f.ename = Eperm;
+ deletefid(c, fp);
+ wr(c, &f);
+ break;
+ }
+ f.ename = Eperm;
+ if(ops->remove && (f.ename = ops->remove(fp->qid)) == nil)
+ f.type = Rremove;
+ else
+ f.type = Rerror;
+ deletefid(c, fp);
+ wr(c, &f);
+ break;
+ case Tstat:
+ if(Debug)
+ fprint(2, "Tstat %d qid=%llx\n", f.fid, fp->qid.path);
+ f.stat = styxmalloc(MAXSTAT);
+ f.ename = "stat error";
+ if(ops->stat == nil && file != nil){
+ f.type = Rstat;
+ f.nstat = convD2M(&file->d, f.stat, MAXSTAT);
+ }
+ else if(ops->stat && (f.ename = ops->stat(fp->qid, &dir)) == nil){
+ f.type = Rstat;
+ f.nstat = convD2M(&dir, f.stat, MAXSTAT);
+ }
+ else
+ f.type = Rerror;
+ wr(c, &f);
+ styxfree(f.stat);
+ break;
+ case Twstat:
+ if(Debug)
+ fprint(2, "Twstat %d\n", f.fid);
+ f.ename = Eperm;
+ convM2D(f.stat, f.nstat, &dir, strs);
+ dir.name = nilconv(dir.name);
+ dir.uid = nilconv(dir.uid);
+ dir.gid = nilconv(dir.gid);
+ dir.muid = nilconv(dir.muid);
+ if(ops->wstat && (f.ename = ops->wstat(fp->qid, &dir)) == nil)
+ f.type = Rwstat;
+ else
+ f.type = Rerror;
+ wr(c, &f);
+ break;
+ case Tversion:
+ if(Debug)
+ fprint(2, "Tversion\n");
+ f.type = Rversion;
+ f.tag = NOTAG;
+ wr(c, &f);
+ break;
+ case Tauth:
+ if(Debug)
+ fprint(2, "Tauth\n");
+ f.type = Rauth;
+ wr(c, &f);
+ break;
+ case Tattach:
+ if(Debug)
+ fprint(2, "Tattach %d %s\n", f.fid, f.uname[0] ? f.uname : c->uname);
+ if(fp){
+ f.type = Rerror;
+ f.ename = "fid in use";
+ }else{
+ Qid q;
+
+ if(f.uname[0]){
+ free(c->uname);
+ c->uname = strdup(f.uname);
+ }
+ if(f.aname[0]){
+ free(c->aname);
+ c->aname = strdup(f.aname);
+ }
+ q.path = Qroot;
+ q.type = QTDIR;
+ q.vers = 0;
+ fp = newfid(c, f.fid, q);
+ f.type = Rattach;
+ f.fid = fp->fid;
+ f.qid = q;
+ if(ops->attach && (f.ename = ops->attach(c->uname, c->aname)) != nil)
+ f.type = Rerror;
+ }
+ wr(c, &f);
+ break;
+ }
+}
+
+char *
+styxinit(Styxserver *server, Styxops *ops, char *port, int perm, int needfile)
+{
+ int i;
+
+ if(Debug)
+ fprint(2, "Initialising Styx server on port %s\n", port);
+ if(perm == -1)
+ perm = 0555;
+ server->ops = ops;
+ server->clients = nil;
+ server->root = nil;
+ server->ftab = (Styxfile**)malloc(TABSZ*sizeof(Styxfile*));
+ for(i = 0; i < TABSZ; i++)
+ server->ftab[i] = nil;
+ server->qidgen = Qroot+1;
+ if(styxinitsocket() < 0)
+ return "styxinitsocket failed";
+ server->connfd = styxannounce(server, port);
+ if(server->connfd < 0)
+ return "can't announce on network port";
+ styxinitwait(server);
+ server->root = newfile(server, nil, 1, Qroot, "/", perm|DMDIR, eve);
+ server->needfile = needfile;
+ return nil;
+}
+
+char*
+styxend(Styxserver *server)
+{
+ USED(server);
+ styxendsocket();
+ return nil;
+}
+
+char *
+styxwait(Styxserver *server)
+{
+ return styxwaitmsg(server);
+}
+
+char *
+styxprocess(Styxserver *server)
+{
+ Client *c;
+ int s;
+
+ if(styxnewcall(server)){
+ s = styxaccept(server);
+ if(s >= 0){
+ newclient(server, s);
+ styxnewclient(server, s);
+ }
+ }
+ for(c = server->clients; c != nil; ){
+ Client *next = c->next;
+
+ server->curc = c;
+ if(c->fd >= 0 && styxnewmsg(server, c->fd))
+ c->state |= CRECV;
+ if(c->state&(CNREAD|CRECV)){
+ if(c->state&CDISC){
+ styxfreeclient(server, c->fd);
+ freeclient(c);
+ }else
+ do
+ run(c);
+ while(c->state&CNREAD);
+ }
+ c = next;
+ }
+
+ return nil;
+}
+
+Client*
+styxclient(Styxserver *server)
+{
+ return server->curc;
+}
+
+Styxfile*
+styxaddfile(Styxserver *server, Path pqid, Path qid, char *name, int mode, char *owner)
+{
+ Styxfile *f, *parent;
+
+ parent = styxfindfile(server, pqid);
+ if(parent == nil || (parent->d.qid.type&QTDIR) == 0)
+ return nil;
+ f = newfile(server, parent, 0, qid, name, mode, owner);
+ return f;
+}
+
+Styxfile*
+styxadddir(Styxserver *server, Path pqid, Path qid, char *name, int mode, char *owner)
+{
+ Styxfile *f, *parent;
+
+ parent = styxfindfile(server, pqid);
+ if(parent == nil || (parent->d.qid.type&QTDIR) == 0)
+ return nil;
+ f = newfile(server, parent, 1, qid, name, mode|DMDIR, owner);
+ return f;
+}
+
+long
+styxreadstr(ulong off, char *buf, ulong n, char *str)
+{
+ int size;
+
+ size = strlen(str);
+ if(off >= size)
+ return 0;
+ if(off+n > size)
+ n = size-off;
+ memmove(buf, str+off, n);
+ return n;
+}
+
+Qid
+styxqid(int path, int isdir)
+{
+ Qid q;
+
+ q.path = path;
+ q.vers = 0;
+ if(isdir)
+ q.type = QTDIR;
+ else
+ q.type = 0;
+ return q;
+}
diff --git a/tools/libstyx/styxserver.h b/tools/libstyx/styxserver.h
new file mode 100644
index 00000000..3aac9eee
--- /dev/null
+++ b/tools/libstyx/styxserver.h
@@ -0,0 +1,97 @@
+
+#define Qroot 0
+
+#define MSGMAX ((((8192+128)*2)+3) & ~3)
+
+extern char Enomem[]; /* out of memory */
+extern char Eperm[]; /* permission denied */
+extern char Enodev[]; /* no free devices */
+extern char Ehungup[]; /* i/o on hungup channel */
+extern char Eexist[]; /* file exists */
+extern char Enonexist[]; /* file does not exist */
+extern char Ebadcmd[]; /* bad command */
+extern char Ebadarg[]; /* bad arguments */
+
+typedef uvlong Path;
+typedef struct Styxserver Styxserver;
+typedef struct Styxops Styxops;
+typedef struct Styxfile Styxfile;
+typedef struct Client Client;
+typedef struct Fid Fid;
+
+struct Styxserver
+{
+ Styxops *ops;
+ Path qidgen;
+ int connfd;
+ int needfile;
+ Client *clients;
+ Client *curc;
+ Styxfile *root;
+ Styxfile **ftab;
+ void *priv; /* private */
+};
+
+struct Client
+{
+ Styxserver *server;
+ Client *next;
+ int fd;
+ char msg[MSGMAX];
+ uint nread; /* valid bytes in msg (including nc)*/
+ int nc; /* bytes consumed from front of msg by convM2S */
+ char data[MSGMAX]; /* Tread/Rread data */
+ int state;
+ Fid *fids;
+ char *uname; /* uid */
+ char *aname; /* attach name */
+ void *u;
+};
+
+struct Styxops
+{
+ char *(*newclient)(Client *c);
+ char *(*freeclient)(Client *c);
+
+ char *(*attach)(char *uname, char *aname);
+ char *(*walk)(Qid *qid, char *name);
+ char *(*open)(Qid *qid, int mode);
+ char *(*create)(Qid *qid, char *name, int perm, int mode);
+ char *(*read)(Qid qid, char *buf, ulong *n, vlong offset);
+ char *(*write)(Qid qid, char *buf, ulong *n, vlong offset);
+ char *(*close)(Qid qid, int mode);
+ char *(*remove)(Qid qid);
+ char *(*stat)(Qid qid, Dir *d);
+ char *(*wstat)(Qid qid, Dir *d);
+};
+
+struct Styxfile
+{
+ Dir d;
+ Styxfile *parent;
+ Styxfile *child;
+ Styxfile *sibling;
+ Styxfile *next;
+ int ref;
+ int open;
+ void *u;
+};
+
+char *styxinit(Styxserver *server, Styxops *ops, char *port, int perm, int needfile);
+char *styxwait(Styxserver *server);
+char *styxprocess(Styxserver *server);
+char *styxend(Styxserver *server);
+
+Client *styxclient(Styxserver *server);
+
+Styxfile *styxaddfile(Styxserver *server, Path pqid, Path qid, char *name, int mode, char *owner);
+Styxfile *styxadddir(Styxserver *server, Path pqid, Path qid, char *name, int mode, char *owner);
+int styxrmfile(Styxserver *server, Path qid);
+Styxfile *styxfindfile(Styxserver *server, Path qid);
+
+int styxperm(Styxfile *file, char *uid, int mode);
+long styxreadstr(ulong off, char *buf, ulong n, char *str);
+Qid styxqid(int path, int isdir);
+void *styxmalloc(int n);
+void styxfree(void *p);
+void styxdebug(void);