summaryrefslogtreecommitdiff
path: root/sys/src/cmd/srvold9p
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/srvold9p
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/srvold9p')
-rwxr-xr-xsys/src/cmd/srvold9p/9p1.h127
-rwxr-xr-xsys/src/cmd/srvold9p/9p1lib.c627
-rwxr-xr-xsys/src/cmd/srvold9p/fcall.c150
-rwxr-xr-xsys/src/cmd/srvold9p/mkfile11
-rwxr-xr-xsys/src/cmd/srvold9p/srvold9p.c1123
5 files changed, 2038 insertions, 0 deletions
diff --git a/sys/src/cmd/srvold9p/9p1.h b/sys/src/cmd/srvold9p/9p1.h
new file mode 100755
index 000000000..d783647a4
--- /dev/null
+++ b/sys/src/cmd/srvold9p/9p1.h
@@ -0,0 +1,127 @@
+#include <authsrv.h>
+
+#define DIRREC 116 /* size of a directory ascii record */
+#define ERRREC 64 /* size of a error record */
+#define NAMEREC 28
+
+typedef struct Fcall9p1 Fcall9p1;
+typedef struct Qid9p1 Qid9p1;
+
+struct Qid9p1
+{
+ long path;
+ long version;
+};
+
+struct Fcall9p1
+{
+ char type;
+ ushort fid;
+ short err;
+ short tag;
+ union
+ {
+ struct
+ {
+ short uid; /* T-Userstr */
+ short oldtag; /* T-nFlush */
+ Qid9p1 qid; /* R-Attach, R-Clwalk, R-Walk,
+ * R-Open, R-Create */
+ char rauth[AUTHENTLEN]; /* R-attach */
+ };
+ struct
+ {
+ char uname[NAMEREC]; /* T-nAttach */
+ char aname[NAMEREC]; /* T-nAttach */
+ char ticket[TICKETLEN]; /* T-attach */
+ char auth[AUTHENTLEN]; /* T-attach */
+ };
+ struct
+ {
+ char ename[ERRREC]; /* R-nError */
+ char chal[CHALLEN]; /* T-session, R-session */
+ char authid[NAMEREC]; /* R-session */
+ char authdom[DOMLEN]; /* R-session */
+ };
+ struct
+ {
+ char name[NAMEREC]; /* T-Walk, T-Clwalk, T-Create, T-Remove */
+ long perm; /* T-Create */
+ ushort newfid; /* T-Clone, T-Clwalk */
+ char mode; /* T-Create, T-Open */
+ };
+ struct
+ {
+ long offset; /* T-Read, T-Write */
+ long count; /* T-Read, T-Write, R-Read */
+ char* data; /* T-Write, R-Read */
+ };
+ struct
+ {
+ char stat[DIRREC]; /* T-Wstat, R-Stat */
+ };
+ };
+};
+
+/*
+ * P9 protocol message types
+ */
+enum
+{
+ Tnop9p1 = 50,
+ Rnop9p1,
+ Tosession9p1 = 52,
+ Rosession9p1,
+ Terror9p1 = 54, /* illegal */
+ Rerror9p1,
+ Tflush9p1 = 56,
+ Rflush9p1,
+ Toattach9p1 = 58,
+ Roattach9p1,
+ Tclone9p1 = 60,
+ Rclone9p1,
+ Twalk9p1 = 62,
+ Rwalk9p1,
+ Topen9p1 = 64,
+ Ropen9p1,
+ Tcreate9p1 = 66,
+ Rcreate9p1,
+ Tread9p1 = 68,
+ Rread9p1,
+ Twrite9p1 = 70,
+ Rwrite9p1,
+ Tclunk9p1 = 72,
+ Rclunk9p1,
+ Tremove9p1 = 74,
+ Rremove9p1,
+ Tstat9p1 = 76,
+ Rstat9p1,
+ Twstat9p1 = 78,
+ Rwstat9p1,
+ Tclwalk9p1 = 80,
+ Rclwalk9p1,
+ Tauth9p1 = 82, /* illegal */
+ Rauth9p1, /* illegal */
+ Tsession9p1 = 84,
+ Rsession9p1,
+ Tattach9p1 = 86,
+ Rattach9p1,
+
+ MAXSYSCALL
+};
+
+int convA2M9p1(Authenticator*, char*, char*);
+void convM2A9p1(char*, Authenticator*, char*);
+void convM2T9p1(char*, Ticket*, char*);
+int convD2M9p1(Dir*, char*);
+int convM2D9p1(char*, Dir*);
+int convM2S9p1(char*, Fcall9p1*, int);
+int convS2M9p1(Fcall9p1*, char*);
+int fcallfmt9p1(Fmt*);
+int fcall(int);
+
+#pragma varargck type "F" Fcall*
+#pragma varargck type "G" Fcall9p1*
+#pragma varargck type "D" Dir*
+
+void fatal(char*, ...);
diff --git a/sys/src/cmd/srvold9p/9p1lib.c b/sys/src/cmd/srvold9p/9p1lib.c
new file mode 100755
index 000000000..df68ef5ba
--- /dev/null
+++ b/sys/src/cmd/srvold9p/9p1lib.c
@@ -0,0 +1,627 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include "9p1.h"
+#pragma varargck type "D" Dir* /* from fcall.h */
+
+static void dumpsome(char*, char*, long);
+
+int
+fcallfmt9p1(Fmt *f1)
+{
+ Fcall9p1 *f;
+ int fid, type, tag, n;
+ char buf[512];
+ Dir d;
+
+ f = va_arg(f1->args, Fcall9p1*);
+ type = f->type;
+ fid = f->fid;
+ tag = f->tag;
+ switch(type){
+ case Tnop9p1: /* 50 */
+ sprint(buf, "old Tnop tag %ud", tag);
+ break;
+ case Rnop9p1:
+ sprint(buf, "old Rnop tag %ud", tag);
+ break;
+ case Tsession9p1: /* 52 */
+ sprint(buf, "old Tsession tag %ud", tag);
+ break;
+ case Rsession9p1:
+ sprint(buf, "old Rsession tag %ud", tag);
+ break;
+ case Rerror9p1: /* 55 */
+ sprint(buf, "old Rerror tag %ud error %.64s", tag, f->ename);
+ break;
+ case Tflush9p1: /* 56 */
+ sprint(buf, "old Tflush tag %ud oldtag %d", tag, f->oldtag);
+ break;
+ case Rflush9p1:
+ sprint(buf, "old Rflush tag %ud", tag);
+ break;
+ case Tattach9p1: /* 58 */
+ sprint(buf, "old Tattach tag %ud fid %d uname %.28s aname %.28s auth %.28s",
+ tag, f->fid, f->uname, f->aname, f->auth);
+ break;
+ case Rattach9p1:
+ sprint(buf, "old Rattach tag %ud fid %d qid 0x%lux|0x%lux",
+ tag, fid, f->qid.path, f->qid.version);
+ break;
+ case Tclone9p1: /* 60 */
+ sprint(buf, "old Tclone tag %ud fid %d newfid %d", tag, fid, f->newfid);
+ break;
+ case Rclone9p1:
+ sprint(buf, "old Rclone tag %ud fid %d", tag, fid);
+ break;
+ case Twalk9p1: /* 62 */
+ sprint(buf, "old Twalk tag %ud fid %d name %.28s", tag, fid, f->name);
+ break;
+ case Rwalk9p1:
+ sprint(buf, "old Rwalk tag %ud fid %d qid 0x%lux|0x%lux",
+ tag, fid, f->qid.path, f->qid.version);
+ break;
+ case Topen9p1: /* 64 */
+ sprint(buf, "old Topen tag %ud fid %d mode %d", tag, fid, f->mode);
+ break;
+ case Ropen9p1:
+ sprint(buf, "old Ropen tag %ud fid %d qid 0x%lux|0x%lux",
+ tag, fid, f->qid.path, f->qid.version);
+ break;
+ case Tcreate9p1: /* 66 */
+ sprint(buf, "old Tcreate tag %ud fid %d name %.28s perm 0x%lux mode %d",
+ tag, fid, f->name, f->perm, f->mode);
+ break;
+ case Rcreate9p1:
+ sprint(buf, "old Rcreate tag %ud fid %d qid 0x%lux|0x%lux",
+ tag, fid, f->qid.path, f->qid.version);
+ break;
+ case Tread9p1: /* 68 */
+ sprint(buf, "old Tread tag %ud fid %d offset %ld count %ld",
+ tag, fid, f->offset, f->count);
+ break;
+ case Rread9p1:
+ n = sprint(buf, "old Rread tag %ud fid %d count %ld ", tag, fid, f->count);
+ dumpsome(buf+n, f->data, f->count);
+ break;
+ case Twrite9p1: /* 70 */
+ n = sprint(buf, "old Twrite tag %ud fid %d offset %ld count %ld ",
+ tag, fid, f->offset, f->count);
+ dumpsome(buf+n, f->data, f->count);
+ break;
+ case Rwrite9p1:
+ sprint(buf, "old Rwrite tag %ud fid %d count %ld", tag, fid, f->count);
+ break;
+ case Tclunk9p1: /* 72 */
+ sprint(buf, "old Tclunk tag %ud fid %d", tag, fid);
+ break;
+ case Rclunk9p1:
+ sprint(buf, "old Rclunk tag %ud fid %d", tag, fid);
+ break;
+ case Tremove9p1: /* 74 */
+ sprint(buf, "old Tremove tag %ud fid %d", tag, fid);
+ break;
+ case Rremove9p1:
+ sprint(buf, "old Rremove tag %ud fid %d", tag, fid);
+ break;
+ case Tstat9p1: /* 76 */
+ sprint(buf, "old Tstat tag %ud fid %d", tag, fid);
+ break;
+ case Rstat9p1:
+ convM2D9p1(f->stat, &d);
+ sprint(buf, "old Rstat tag %ud fid %d stat %D", tag, fid, &d);
+ break;
+ case Twstat9p1: /* 78 */
+ convM2D9p1(f->stat, &d);
+ sprint(buf, "old Twstat tag %ud fid %d stat %D", tag, fid, &d);
+ break;
+ case Rwstat9p1:
+ sprint(buf, "old Rwstat tag %ud fid %d", tag, fid);
+ break;
+ case Tclwalk9p1: /* 81 */
+ sprint(buf, "old Tclwalk tag %ud fid %d newfid %d name %.28s",
+ tag, fid, f->newfid, f->name);
+ break;
+ case Rclwalk9p1:
+ sprint(buf, "old Rclwalk tag %ud fid %d qid 0x%lux|0x%lux",
+ tag, fid, f->qid.path, f->qid.version);
+ break;
+ default:
+ sprint(buf, "unknown type %d", type);
+ }
+ return fmtstrcpy(f1, buf);
+}
+
+/*
+ * dump out count (or DUMPL, if count is bigger) bytes from
+ * buf to ans, as a string if they are all printable,
+ * else as a series of hex bytes
+ */
+#define DUMPL 64
+
+static void
+dumpsome(char *ans, char *buf, long count)
+{
+ int i, printable;
+ char *p;
+
+ printable = 1;
+ if(count > DUMPL)
+ count = DUMPL;
+ for(i=0; i<count && printable; i++)
+ if((buf[i]<32 && buf[i] !='\n' && buf[i] !='\t') || (uchar)buf[i]>127)
+ printable = 0;
+ p = ans;
+ *p++ = '\'';
+ if(printable){
+ memmove(p, buf, count);
+ p += count;
+ }else{
+ for(i=0; i<count; i++){
+ if(i>0 && i%4==0)
+ *p++ = ' ';
+ sprint(p, "%2.2ux", buf[i]);
+ p += 2;
+ }
+ }
+ *p++ = '\'';
+ *p = 0;
+}
+
+#define CHAR(x) *p++ = f->x
+#define SHORT(x) { ulong vvv = f->x; p[0] = vvv; p[1] = vvv>>8; p += 2; }
+#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
+#define LONG(x) { ulong vvv = f->x; VLONG(vvv); }
+#define BYTES(x,n) memmove(p, f->x, n); p += n
+#define STRING(x,n) strncpy((char*)p, f->x, n); p += n
+
+int
+convS2M9p1(Fcall9p1 *f, char *ap)
+{
+ uchar *p;
+ int t;
+
+ p = (uchar*)ap;
+ CHAR(type);
+ t = f->type;
+ SHORT(tag);
+ switch(t)
+ {
+ default:
+ fprint(2, "convS2M9p1: bad type: %d\n", t);
+ return 0;
+
+ case Tnop9p1:
+ case Tosession9p1:
+ break;
+
+ case Tsession9p1:
+ BYTES(chal, sizeof(f->chal));
+ break;
+
+ case Tflush9p1:
+ SHORT(oldtag);
+ break;
+
+ case Tattach9p1:
+ SHORT(fid);
+ STRING(uname, sizeof(f->uname));
+ STRING(aname, sizeof(f->aname));
+ BYTES(ticket, sizeof(f->ticket));
+ BYTES(auth, sizeof(f->auth));
+ break;
+
+ case Toattach9p1:
+ SHORT(fid);
+ STRING(uname, sizeof(f->uname));
+ STRING(aname, sizeof(f->aname));
+ BYTES(ticket, NAMEREC);
+ break;
+
+ case Tclone9p1:
+ SHORT(fid);
+ SHORT(newfid);
+ break;
+
+ case Twalk9p1:
+ SHORT(fid);
+ STRING(name, sizeof(f->name));
+ break;
+
+ case Tclwalk9p1:
+ SHORT(fid);
+ SHORT(newfid);
+ STRING(name, sizeof(f->name));
+ break;
+
+ case Topen9p1:
+ SHORT(fid);
+ CHAR(mode);
+ break;
+
+ case Tcreate9p1:
+ SHORT(fid);
+ STRING(name, sizeof(f->name));
+ LONG(perm);
+ CHAR(mode);
+ break;
+
+ case Tread9p1:
+ SHORT(fid);
+ LONG(offset); VLONG(0);
+ SHORT(count);
+ break;
+
+ case Twrite9p1:
+ SHORT(fid);
+ LONG(offset); VLONG(0);
+ SHORT(count);
+ p++;
+ if((uchar*)p == (uchar*)f->data) {
+ p += f->count;
+ break;
+ }
+ BYTES(data, f->count);
+ break;
+
+ case Tclunk9p1:
+ case Tremove9p1:
+ case Tstat9p1:
+ SHORT(fid);
+ break;
+
+ case Twstat9p1:
+ SHORT(fid);
+ BYTES(stat, sizeof(f->stat));
+ break;
+/*
+ */
+ case Rnop9p1:
+ case Rosession9p1:
+ case Rflush9p1:
+ break;
+
+ case Rsession9p1:
+ BYTES(chal, sizeof(f->chal));
+ BYTES(authid, sizeof(f->authid));
+ BYTES(authdom, sizeof(f->authdom));
+ break;
+
+ case Rerror9p1:
+ STRING(ename, sizeof(f->ename));
+ break;
+
+ case Rclone9p1:
+ case Rclunk9p1:
+ case Rremove9p1:
+ case Rwstat9p1:
+ SHORT(fid);
+ break;
+
+ case Rwalk9p1:
+ case Ropen9p1:
+ case Rcreate9p1:
+ case Rclwalk9p1:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.version);
+ break;
+
+ case Rattach9p1:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.version);
+ BYTES(rauth, sizeof(f->rauth));
+ break;
+
+ case Roattach9p1:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.version);
+ break;
+
+ case Rread9p1:
+ SHORT(fid);
+ SHORT(count);
+ p++;
+ if((uchar*)p == (uchar*)f->data) {
+ p += f->count;
+ break;
+ }
+ BYTES(data, f->count);
+ break;
+
+ case Rwrite9p1:
+ SHORT(fid);
+ SHORT(count);
+ break;
+
+ case Rstat9p1:
+ SHORT(fid);
+ BYTES(stat, sizeof(f->stat));
+ break;
+ }
+ return p - (uchar*)ap;
+}
+
+int
+convD2M9p1(Dir *f, char *ap)
+{
+ uchar *p;
+ ulong q;
+
+ p = (uchar*)ap;
+ STRING(name, NAMEREC);
+ STRING(uid, NAMEREC);
+ STRING(gid, NAMEREC);
+
+ q = f->qid.path & ~0x80000000;
+ if(f->qid.type & QTDIR)
+ q |= 0x80000000;
+ VLONG(q);
+ LONG(qid.vers);
+ LONG(mode);
+ LONG(atime);
+ LONG(mtime);
+ LONG(length); VLONG(0);
+ VLONG(0);
+ return p - (uchar*)ap;
+}
+
+int
+convA2M9p1(Authenticator *f, char *ap, char *key)
+{
+ int n;
+ uchar *p;
+
+ p = (uchar*)ap;
+ CHAR(num);
+ STRING(chal, CHALLEN);
+ LONG(id);
+ n = p - (uchar*)ap;
+ if(key)
+ encrypt(key, ap, n);
+ return n;
+}
+
+#undef CHAR
+#undef SHORT
+#undef LONG
+#undef VLONG
+#undef BYTES
+#undef STRING
+
+#define CHAR(x) f->x = *p++
+#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2
+#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
+#define LONG(x) VLONG(f->x)
+#define BYTES(x,n) memmove(f->x, p, n); p += n
+#define STRING(x,n) memmove(f->x, p, n); p += n
+
+int
+convM2S9p1(char *ap, Fcall9p1 *f, int n)
+{
+ uchar *p;
+ int t;
+
+ p = (uchar*)ap;
+ CHAR(type);
+ t = f->type;
+ SHORT(tag);
+ switch(t)
+ {
+ default:
+ fprint(2, "convM2S9p1: bad type: %d\n", f->type);
+ return 0;
+
+ case Tnop9p1:
+ case Tosession9p1:
+ break;
+
+ case Tsession9p1:
+ BYTES(chal, sizeof(f->chal));
+ break;
+
+ case Tflush9p1:
+ SHORT(oldtag);
+ break;
+
+ case Tattach9p1:
+ SHORT(fid);
+ BYTES(uname, sizeof(f->uname));
+ BYTES(aname, sizeof(f->aname));
+ BYTES(ticket, sizeof(f->ticket));
+ BYTES(auth, sizeof(f->auth));
+ break;
+
+ case Toattach9p1:
+ SHORT(fid);
+ BYTES(uname, sizeof(f->uname));
+ BYTES(aname, sizeof(f->aname));
+ BYTES(ticket, NAMEREC);
+ break;
+
+ case Tclone9p1:
+ SHORT(fid);
+ SHORT(newfid);
+ break;
+
+ case Twalk9p1:
+ SHORT(fid);
+ BYTES(name, sizeof(f->name));
+ break;
+
+ case Tclwalk9p1:
+ SHORT(fid);
+ SHORT(newfid);
+ BYTES(name, sizeof(f->name));
+ break;
+
+ case Tremove9p1:
+ SHORT(fid);
+ break;
+
+ case Topen9p1:
+ SHORT(fid);
+ CHAR(mode);
+ break;
+
+ case Tcreate9p1:
+ SHORT(fid);
+ BYTES(name, sizeof(f->name));
+ LONG(perm);
+ CHAR(mode);
+ break;
+
+ case Tread9p1:
+ SHORT(fid);
+ LONG(offset); p += 4;
+ SHORT(count);
+ break;
+
+ case Twrite9p1:
+ SHORT(fid);
+ LONG(offset); p += 4;
+ SHORT(count);
+ p++;
+ f->data = (char*)p; p += f->count;
+ break;
+
+ case Tclunk9p1:
+ case Tstat9p1:
+ SHORT(fid);
+ break;
+
+ case Twstat9p1:
+ SHORT(fid);
+ BYTES(stat, sizeof(f->stat));
+ break;
+
+/*
+ */
+ case Rnop9p1:
+ case Rosession9p1:
+ break;
+
+ case Rsession9p1:
+ BYTES(chal, sizeof(f->chal));
+ BYTES(authid, sizeof(f->authid));
+ BYTES(authdom, sizeof(f->authdom));
+ break;
+
+ case Rerror9p1:
+ BYTES(ename, sizeof(f->ename));
+ break;
+
+ case Rflush9p1:
+ break;
+
+ case Rclone9p1:
+ case Rclunk9p1:
+ case Rremove9p1:
+ case Rwstat9p1:
+ SHORT(fid);
+ break;
+
+ case Rwalk9p1:
+ case Rclwalk9p1:
+ case Ropen9p1:
+ case Rcreate9p1:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.version);
+ break;
+
+ case Rattach9p1:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.version);
+ BYTES(rauth, sizeof(f->rauth));
+ break;
+
+ case Roattach9p1:
+ SHORT(fid);
+ LONG(qid.path);
+ LONG(qid.version);
+ break;
+
+ case Rread9p1:
+ SHORT(fid);
+ SHORT(count);
+ p++;
+ f->data = (char*)p; p += f->count;
+ break;
+
+ case Rwrite9p1:
+ SHORT(fid);
+ SHORT(count);
+ break;
+
+ case Rstat9p1:
+ SHORT(fid);
+ BYTES(stat, sizeof(f->stat));
+ break;
+ }
+ if((uchar*)ap+n == p)
+ return n;
+ return 0;
+}
+
+int
+convM2D9p1(char *ap, Dir *f)
+{
+ uchar *p;
+
+ p = (uchar*)ap;
+ f->name = (char*)p;
+ p += NAMEREC;
+ f->uid = (char*)p;
+ f->muid = (char*)p;
+ p += NAMEREC;
+ f->gid = (char*)p;
+ p += NAMEREC;
+
+ LONG(qid.path);
+ f->qid.path &= ~0x80000000;
+
+ LONG(qid.vers);
+ LONG(mode);
+ f->qid.type = f->mode >> 24;
+ LONG(atime);
+ LONG(mtime);
+ LONG(length); p += 4;
+ p += 4;
+ return p - (uchar*)ap;
+}
+
+void
+convM2A9p1(char *ap, Authenticator *f, char *key)
+{
+ uchar *p;
+
+ if(key)
+ decrypt(key, ap, AUTHENTLEN);
+ p = (uchar*)ap;
+ CHAR(num);
+ STRING(chal, CHALLEN);
+ LONG(id);
+ USED(p);
+}
+
+void
+convM2T9p1(char *ap, Ticket *f, char *key)
+{
+ uchar *p;
+
+ if(key)
+ decrypt(key, ap, TICKETLEN);
+ p = (uchar*)ap;
+ CHAR(num);
+ STRING(chal, CHALLEN);
+ STRING(cuid, NAMEREC);
+ f->cuid[NAMEREC-1] = 0;
+ STRING(suid, NAMEREC);
+ f->suid[NAMEREC-1] = 0;
+ STRING(key, DESKEYLEN);
+ USED(p);
+};
diff --git a/sys/src/cmd/srvold9p/fcall.c b/sys/src/cmd/srvold9p/fcall.c
new file mode 100755
index 000000000..a3e007330
--- /dev/null
+++ b/sys/src/cmd/srvold9p/fcall.c
@@ -0,0 +1,150 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "9p1.h"
+
+#define MAXFDATA (8*1024)
+#define MAXRPC (MAXFDATA+160)
+
+/*
+ * reassemble 9P messages for stream based protocols
+ * interposed between devmnt and the network by srv for tcp connections
+ * fcall expects devmnt on fd0, network fd1
+ */
+uchar msglen[256] =
+{
+ [Tnop9p1] 3,
+ [Rnop9p1] 3,
+ [Tsession9p1] 3+CHALLEN,
+ [Rsession9p1] 3+NAMEREC+DOMLEN+CHALLEN,
+ [Terror9p1] 0,
+ [Rerror9p1] 67,
+ [Tflush9p1] 5,
+ [Rflush9p1] 3,
+ [Tattach9p1] 5+2*NAMEREC+TICKETLEN+AUTHENTLEN,
+ [Rattach9p1] 13+AUTHENTLEN,
+ [Tclone9p1] 7,
+ [Rclone9p1] 5,
+ [Twalk9p1] 33,
+ [Rwalk9p1] 13,
+ [Topen9p1] 6,
+ [Ropen9p1] 13,
+ [Tcreate9p1] 38,
+ [Rcreate9p1] 13,
+ [Tread9p1] 15,
+ [Rread9p1] 8,
+ [Twrite9p1] 16,
+ [Rwrite9p1] 7,
+ [Tclunk9p1] 5,
+ [Rclunk9p1] 5,
+ [Tremove9p1] 5,
+ [Rremove9p1] 5,
+ [Tstat9p1] 5,
+ [Rstat9p1] 121,
+ [Twstat9p1] 121,
+ [Rwstat9p1] 5,
+ [Tclwalk9p1] 35,
+ [Rclwalk9p1] 13,
+};
+
+enum
+{
+ Twritehdr = 16, /* Min bytes for Twrite */
+ Rreadhdr = 8, /* Min bytes for Rread */
+ Twritecnt = 13, /* Offset in byte stream of write count */
+ Rreadcnt = 5, /* Offset for Readcnt */
+};
+
+int
+mntrpclen(uchar *d, int n)
+{
+ uchar t;
+ int len, off;
+
+ if(n < 1)
+ return 0;
+
+ t = d[0];
+ switch(t) { /* This is the type */
+ default:
+ len = msglen[t];
+ if(len == 0) /* Illegal type so consume */
+ return n;
+ if(n < len)
+ return 0;
+ return len;
+ case Twrite9p1: /* Fmt: TGGFFOOOOOOOOCC */
+ len = Twritehdr; /* T = type, G = tag, F = fid */
+ off = Twritecnt; /* O = offset, C = count */
+ break;
+ case Rread9p1: /* Fmt: TGGFFCC */
+ len = Rreadhdr;
+ off = Rreadcnt;
+ break;
+ }
+ if(n < off+2)
+ return 0;
+
+ len += d[off]|(d[off+1]<<8);
+ if(n < len)
+ return 0;
+
+ return len;
+}
+
+int
+fcall(int fd)
+{
+ int i, r, n, l;
+ uchar *p, *buf;
+ int pipefd[2];
+
+ if(pipe(pipefd) < 0)
+ fatal("fcall pipe: %r");
+
+ buf = malloc(MAXRPC);
+ if(buf == nil)
+ fatal("fcall malloc");
+
+ switch(rfork(RFPROC|RFMEM|RFFDG|RFCNAMEG)){
+ default:
+ return pipefd[0]; /* parent returns fd */
+ case 0:
+ break; /* child builds buffers */
+ case -1:
+ fatal("fcall fork: %r");
+ }
+
+ /* close file descriptors */
+ for(i=0; i<20; i++)
+ if(i!=fd && i!=pipefd[1])
+ close(i);
+
+ l = MAXRPC;
+ p = buf;
+ for(;;) {
+ n = read(fd, p, l);
+ if(n < 0)
+ break;
+ p += n;
+ l -= n;
+
+ for(;;) {
+ r = mntrpclen(buf, p - buf);
+ if(r == 0)
+ break;
+
+ if(write(pipefd[1], buf, r) < 0)
+ break;
+
+ n = (p - buf) - r;
+ memmove(buf, buf+r, n);
+ p = buf+n;
+ l = MAXRPC - n;
+ }
+ }
+ close(pipefd[1]);
+ fatal(nil);
+ return -1;
+}
diff --git a/sys/src/cmd/srvold9p/mkfile b/sys/src/cmd/srvold9p/mkfile
new file mode 100755
index 000000000..41e6ceb71
--- /dev/null
+++ b/sys/src/cmd/srvold9p/mkfile
@@ -0,0 +1,11 @@
+</$objtype/mkfile
+BIN=/$objtype/bin
+
+TARG=srvold9p
+OFILES=srvold9p.$O\
+ 9p1lib.$O\
+ fcall.$O\
+
+</sys/src/cmd/mkone
+
+install: $O.out
diff --git a/sys/src/cmd/srvold9p/srvold9p.c b/sys/src/cmd/srvold9p/srvold9p.c
new file mode 100755
index 000000000..32f6b0bf0
--- /dev/null
+++ b/sys/src/cmd/srvold9p/srvold9p.c
@@ -0,0 +1,1123 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <libsec.h>
+#include "9p1.h"
+
+char *user;
+int newfd;
+int roldfd;
+int woldfd;
+int debug;
+int dofcall;
+QLock servelock;
+QLock fidlock;
+QLock taglock;
+int mainpid;
+int ntag;
+int nfork;
+char FLUSHED[] = "FLUSHED";
+
+enum{
+ Maxfdata = 8192
+};
+
+enum{
+ Command,
+ Network,
+ File,
+ Stdio,
+};
+
+typedef struct Tag Tag;
+struct Tag
+{
+ int tag;
+ int flushed;
+ int received;
+ int ref;
+ Tag *next;
+};
+
+typedef struct Message Message;
+struct Message
+{
+ char *data;
+ int n;
+};
+
+typedef struct Fid Fid;
+
+struct Fid
+{
+ short busy;
+ short allocated;
+ int fid;
+ Qid qid;
+ ulong newoffset;
+ ulong oldoffset;
+ Fid *next;
+};
+
+Fid *fids;
+Tag *tags;
+
+char *rflush(Fcall*, Fcall*, char*),
+ *rversion(Fcall*, Fcall*, char*),
+ *rauth(Fcall*, Fcall*, char*),
+ *rattach(Fcall*, Fcall*, char*),
+ *rwalk(Fcall*, Fcall*, char*),
+ *ropen(Fcall*, Fcall*, char*),
+ *rcreate(Fcall*, Fcall*, char*),
+ *rread(Fcall*, Fcall*, char*),
+ *rwrite(Fcall*, Fcall*, char*),
+ *rclunk(Fcall*, Fcall*, char*),
+ *rremove(Fcall*, Fcall*, char*),
+ *rstat(Fcall*, Fcall*, char*),
+ *rwstat(Fcall*, Fcall*, char*);
+
+char *(*fcalls[])(Fcall*, Fcall*, char*) = {
+ [Tversion] rversion,
+ [Tflush] rflush,
+ [Tauth] rauth,
+ [Tattach] rattach,
+ [Twalk] rwalk,
+ [Topen] ropen,
+ [Tcreate] rcreate,
+ [Tread] rread,
+ [Twrite] rwrite,
+ [Tclunk] rclunk,
+ [Tremove] rremove,
+ [Tstat] rstat,
+ [Twstat] rwstat,
+};
+
+char Etoolong[] = "name too long";
+
+void connect(int, char*);
+void post(int, char*);
+void serve(void);
+void demux(void);
+void* emalloc(ulong);
+char* transact9p1(Fcall9p1*, Fcall9p1*, char*);
+Fid* newfid(int);
+
+struct
+{
+ char chal[CHALLEN]; /* my challenge */
+ char rchal[CHALLEN]; /* his challenge */
+ char authid[NAMEREC];
+ char authdom[DOMLEN];
+ int id;
+} ai;
+
+void
+usage(void)
+{
+ fprint(2, "usage: srvold9p [-abcCd] [-u user] [-s | [-m mountpoint]] [-x 'command' | -n network-addr | -f file] [-F] [-p servicename]\n");
+ exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+ int method;
+ char *oldstring;
+ char *mountpoint, *postname;
+ int mountflag, mountfd;
+ int p[2];
+int i;
+
+ fmtinstall('F', fcallfmt);
+ fmtinstall('G', fcallfmt9p1);
+ fmtinstall('D', dirfmt);
+
+ user = getuser();
+ mountpoint = nil;
+ mountflag = 0;
+ postname = nil;
+ oldstring = nil;
+ method = -1;
+ mountfd = -1;
+
+ ARGBEGIN{
+ case 'a':
+ mountflag |= MAFTER;
+ break;
+ case 'b':
+ mountflag |= MBEFORE;
+ break;
+ case 'c':
+ mountflag |= MCREATE;
+ break;
+ case 'C':
+ mountflag |= MCACHE;
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'f':
+ method = File;
+ oldstring = ARGF();
+ break;
+ case 'F':
+ dofcall++;
+ break;
+ case 'm':
+ mountpoint = EARGF(usage());
+ break;
+ case 'n':
+ method = Network;
+ oldstring = ARGF();
+ break;
+ case 'p':
+ postname = ARGF();
+ if(postname == nil)
+ usage();
+ break;
+ case 's':
+ method = Stdio;
+ break;
+ case 'u':
+ user = EARGF(usage());
+ break;
+ case 'x':
+ method = Command;
+ oldstring = ARGF();
+ break;
+ default:
+ usage();
+ }ARGEND;
+
+ if(method == Stdio){
+ if(mountpoint!=nil || argc!=0)
+ usage();
+ }else{
+ if(oldstring == nil || argc != 0 || (mountflag!=0 && mountpoint==nil))
+ usage();
+ }
+
+ rfork(RFNOTEG|RFREND);
+
+ connect(method, oldstring);
+
+ if(method == Stdio)
+ newfd = 0;
+ else{
+ if(pipe(p) < 0)
+ fatal("pipe: %r");
+ if(postname != nil)
+ post(p[0], postname);
+ mountfd = p[0];
+ newfd = p[1];
+ }
+ if(debug)
+ fprint(2, "connected and posted\n");
+
+ switch(rfork(RFPROC|RFMEM|RFNAMEG|RFFDG)){
+ case 0:
+ mainpid = getpid();
+ /* child does all the work */
+ if(mountfd >= 0)
+ close(mountfd);
+ switch(rfork(RFPROC|RFMEM|RFFDG)){
+ case 0:
+ for(i = 0; i < 20; i++)
+ if (i != roldfd) close(i);
+ demux();
+ return;
+ case -1:
+ fatal("fork error: %r");
+ break;
+ }
+ for(i = 0; i < 20; i++)
+ if (i != newfd && i != woldfd && (debug == 0 || i != 2)) close(i);
+ serve();
+ break;
+ case -1:
+ fatal("fork error: %r");
+ break;
+ default:
+ /* parent mounts if required, then exits */
+ if(mountpoint){
+ if(mount(mountfd, -1, mountpoint, mountflag, "") < 0)
+ fatal("can't mount: %r");
+ }
+ break;
+ }
+ exits(nil);
+}
+
+void
+connect(int method, char *oldstring)
+{
+ char *s;
+ char dir[256];
+
+ switch(method){
+ default:
+ roldfd = -1;
+ woldfd = -1;
+ fatal("can't handle method type %d", method);
+ break;
+ case Network:
+ s = netmkaddr(oldstring, 0, "9fs");
+ roldfd = dial(s, 0, dir, 0);
+ if(roldfd < 0)
+ fatal("dial %s: %r", s);
+ woldfd = roldfd;
+ if(dofcall)
+ roldfd = fcall(woldfd);
+ break;
+ case File:
+ roldfd = open(oldstring, ORDWR);
+ if(roldfd < 0)
+ fatal("can't open %s: %r", oldstring);
+ woldfd = roldfd;
+ if(dofcall)
+ roldfd = fcall(woldfd);
+ break;
+ case Stdio:
+ roldfd = fcall(1);
+ woldfd = 1;
+ break;
+ }
+}
+
+void
+post(int fd, char *srv)
+{
+ int f;
+ char buf[128];
+
+ snprint(buf, sizeof buf, "/srv/%s", srv);
+ f = create(buf, OWRITE, 0666);
+ if(f < 0)
+ fatal("can't create %s: %r", buf);
+ sprint(buf, "%d", fd);
+ if(write(f, buf, strlen(buf)) != strlen(buf))
+ fatal("post write: %r");
+ close(f);
+}
+
+Fid *
+newfid(int fid)
+{
+ Fid *f, *ff;
+
+ ff = 0;
+ qlock(&fidlock);
+ for(f = fids; f; f = f->next)
+ if(f->fid == fid){
+ f->allocated = 1;
+ qunlock(&fidlock);
+ return f;
+ }
+ else if(!ff && !f->allocated)
+ ff = f;
+ if(ff){
+ ff->fid = fid;
+ ff->allocated = 1;
+ qunlock(&fidlock);
+ return ff;
+ }
+ f = emalloc(sizeof *f);
+ f->fid = fid;
+ f->next = fids;
+ f->allocated = 1;
+ fids = f;
+ qunlock(&fidlock);
+ return f;
+}
+
+/*
+ * Reads returning 9P1 messages and demultiplexes them.
+ * BUG: assumes one read per message.
+ */
+void
+demux(void)
+{
+ int m, n;
+ char *data;
+ Fcall9p1 r;
+ Message *msg;
+ Tag *t;
+
+ for(;;){
+ data = malloc(IOHDRSZ+Maxfdata); /* no need to clear memory */
+ if(data == nil)
+ fatal("demux malloc: %r");
+ m = read(roldfd, data, IOHDRSZ+Maxfdata);
+ if(m <= 0)
+ fatal("read error talking to old system: %r");
+ n = convM2S9p1(data, &r, m);
+ if(n == 0)
+ fatal("bad conversion receiving from old system");
+ if(debug)
+ fprint(2, "srvold9p:<=%G\n", &r);
+ qlock(&taglock);
+ for(t=tags; t!=nil; t=t->next)
+ if(t->tag == r.tag){
+ t->received = 1;
+ break;
+ }
+ qunlock(&taglock);
+ /*
+ * Fcall9p1 tag is used to rendezvous.
+ * Recipient converts message a second time, but that's OK.
+ */
+ msg = emalloc(sizeof(Message));
+ msg->data = data;
+ msg->n = n;
+ rendezvous((void*)r.tag, msg);
+ }
+}
+
+Tag*
+newtag(int tag)
+{
+ Tag *t;
+
+ t = emalloc(sizeof(Tag));
+ t->tag = tag;
+ t->flushed = 0;
+ t->received = 0;
+ t->ref = 1;
+ qlock(&taglock);
+ t->next = tags;
+ tags = t;
+ qunlock(&taglock);
+ return t;
+}
+
+void
+freetag(Tag *tag) /* called with taglock set */
+{
+ Tag *t, *prev;
+
+ if(tag->ref-- == 1){
+ prev = nil;
+ for(t=tags; t!=nil; t=t->next){
+ if(t == tag){
+ if(prev == nil)
+ tags = t->next;
+ else
+ prev->next = t->next;
+ break;
+ }
+ prev = t;
+ }
+ if(t == nil)
+ sysfatal("freetag");
+ free(tag);
+ }
+}
+
+void
+serve(void)
+{
+ char *err;
+ int n;
+ Fcall thdr;
+ Fcall rhdr;
+ uchar mdata[IOHDRSZ+Maxfdata];
+ char mdata9p1[IOHDRSZ+Maxfdata];
+ Tag *tag;
+
+ for(;;){
+ qlock(&servelock);
+ for(;;){
+ n = read9pmsg(newfd, mdata, sizeof mdata);
+
+ if(n == 0)
+ continue;
+ if(n < 0)
+ break;
+ if(n > 0 && convM2S(mdata, n, &thdr) > 0)
+ break;
+ }
+ if(n>0 && servelock.head==nil) /* no other processes waiting to read */
+ switch(rfork(RFPROC|RFMEM)){
+ case 0:
+ /* child starts serving */
+ continue;
+ break;
+ case -1:
+ fatal("fork error: %r");
+ break;
+ default:
+ break;
+ }
+ qunlock(&servelock);
+
+ if(n < 0)
+ fatal(nil); /* exit quietly; remote end has just hung up */
+
+ if(debug)
+ fprint(2, "srvold9p:<-%F\n", &thdr);
+
+ tag = newtag(thdr.tag);
+
+ if(!fcalls[thdr.type])
+ err = "bad fcall type";
+ else
+ err = (*fcalls[thdr.type])(&thdr, &rhdr, mdata9p1);
+
+ qlock(&taglock);
+ if(tag->flushed){
+ freetag(tag);
+ qunlock(&taglock);
+ continue;
+ }
+ qunlock(&taglock);
+
+ if(err){
+ rhdr.type = Rerror;
+ rhdr.ename = err;
+ }else{
+ rhdr.type = thdr.type + 1;
+ rhdr.fid = thdr.fid;
+ }
+ rhdr.tag = thdr.tag;
+ if(debug)
+ fprint(2, "srvold9p:->%F\n", &rhdr);/**/
+ n = convS2M(&rhdr, mdata, sizeof mdata);
+ if(n == 0)
+ fatal("convS2M error on write");
+ if(write(newfd, mdata, n) != n)
+ fatal("mount write");
+
+ qlock(&taglock);
+ freetag(tag);
+ qunlock(&taglock);
+ }
+}
+
+void
+send9p1(Fcall9p1 *t, char *data)
+{
+ int m, n;
+
+ if(debug)
+ fprint(2, "srvold9p:=>%G\n", t);
+ n = convS2M9p1(t, data);
+ if(n == 0)
+ fatal("bad conversion sending to old system");
+ m = write(woldfd, data, n);
+ if(m != n)
+ fatal("wrote %d to old system; should be %d", m, n);
+}
+
+int
+recv9p1(Fcall9p1 *r, int tag, char *data)
+{
+ int n;
+ Message *msg;
+
+ msg = rendezvous((void*)tag, 0);
+ if(msg == (void*)~0)
+ fatal("rendezvous: %r");
+ if(msg == nil){
+ if(debug)
+ fprint(2, "recv flushed\n");
+ return -1;
+ }
+ /* copy data to local buffer */
+ memmove(data, msg->data, msg->n);
+ n = convM2S9p1(data, r, msg->n);
+ if(n == 0)
+ fatal("bad conversion receiving from old system");
+ free(msg->data);
+ free(msg);
+ return 1;
+}
+
+char*
+transact9p1(Fcall9p1 *t, Fcall9p1 *r, char *mdata9p1)
+{
+ send9p1(t, mdata9p1);
+ if(recv9p1(r, t->tag, mdata9p1) < 0)
+ return FLUSHED;
+ if(r->type == Rerror9p1)
+ return r->ename;
+ if(r->type != t->type+1)
+ fatal("bad message type; expected %d got %d", t->type+1, r->type);
+ return nil;
+}
+
+char*
+rflush(Fcall *t, Fcall *, char *mdata9p1)
+{
+ Fcall9p1 t9, r9;
+ Tag *oldt;
+
+ t9.type = Tflush9p1;
+ t9.tag = t->tag;
+ t9.oldtag = t->oldtag;
+ qlock(&taglock);
+ for(oldt=tags; oldt!=nil; oldt=oldt->next)
+ if(oldt->tag == t->oldtag){
+ oldt->flushed = 1;
+ oldt->ref++;
+ break;
+ }
+ qunlock(&taglock);
+
+ if(oldt == nil){ /* nothing to flush */
+ if(debug)
+ fprint(2, "no such tag to flush\n");
+ return 0;
+ }
+
+ transact9p1(&t9, &r9, mdata9p1); /* can't error */
+
+ qlock(&taglock);
+ if(oldt->received == 0){ /* wake up receiver */
+ if(debug)
+ fprint(2, "wake up receiver\n");
+ oldt->received = 1;
+ rendezvous((void*)t->oldtag, 0);
+ }
+ freetag(oldt);
+ qunlock(&taglock);
+ return 0;
+}
+
+char*
+rversion(Fcall *t, Fcall *r, char*)
+{
+ Fid *f;
+
+ /* just ack; this one doesn't go to old service */
+ if(t->msize > IOHDRSZ+Maxfdata)
+ r->msize = IOHDRSZ+Maxfdata;
+ else
+ r->msize = t->msize;
+ if(strncmp(t->version, "9P2000", 6) != 0)
+ return "unknown 9P version";
+ r->version = "9P2000";
+
+ qlock(&fidlock);
+ for(f = fids; f; f = f->next){
+ f->busy = 0;
+ f->allocated = 0;
+ }
+ qunlock(&fidlock);
+
+ return 0;
+}
+
+char*
+rauth(Fcall *, Fcall *, char *)
+{
+ return "srvold9p: authentication not supported";
+}
+
+#ifdef asdf
+
+void
+memrandom(void *p, int n)
+{
+ ulong *lp;
+ uchar *cp;
+
+ for(lp = p; n >= sizeof(ulong); n -= sizeof(ulong))
+ *lp++ = fastrand();
+ for(cp = (uchar*)lp; n > 0; n--)
+ *cp++ = fastrand();
+}
+
+char*
+rsession(Fcall *t, Fcall *r, char *mdata9p1)
+{
+ char *err;
+ Fcall9p1 t9, r9;
+ Fid *f;
+
+ t9.type = Tsession9p1;
+ t9.tag = t->tag;
+ if(doauth)
+ memrandom(t9.chal, sizeof t9.chal);
+ else
+ memset(t9.chal, 0, sizeof t9.chal);
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err)
+ return err;
+
+ qlock(&fidlock);
+ for(f = fids; f; f = f->next){
+ f->busy = 0;
+ f->allocated = 0;
+ }
+ qunlock(&fidlock);
+
+ if(doauth){
+ memmove(ai.authid, r9.authid, sizeof ai.authid);
+ memmove(ai.authdom, r9.authdom, sizeof ai.authid);
+ memmove(ai.rchal, r9.chal, sizeof ai.rchal);
+ memmove(ai.chal, t9.chal, sizeof ai.chal);
+ r->authid = ai.authid;
+ r->authdom = ai.authdom;
+ r->chal = (uchar*)ai.rchal;
+ r->nchal = CHALLEN;
+ } else {
+ r->authid = "";
+ r->authdom = "";
+ r->nchal = 0;
+ r->chal = nil;
+ }
+ return 0;
+}
+#endif
+
+char*
+rattach(Fcall *t, Fcall *r, char *mdata9p1)
+{
+ char *err;
+ Fcall9p1 t9, r9;
+ Fid *f;
+
+ f = newfid(t->fid);
+ if(f->busy)
+ return "attach: fid in use";
+ /* no authentication! */
+ t9.type = Tattach9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ strncpy(t9.uname, t->uname, NAMEREC);
+ if(strcmp(user, "none") == 0)
+ strncpy(t9.uname, user, NAMEREC);
+ strncpy(t9.aname, t->aname, NAMEREC);
+ memset(t9.ticket, 0, sizeof t9.ticket);
+ memset(t9.auth, 0, sizeof t9.auth);
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err)
+ return err;
+
+ r->qid.path = r9.qid.path & ~0x80000000;
+ r->qid.vers = r9.qid.version;
+ r->qid.type = QTDIR;
+ f->busy = 1;
+ f->qid = r->qid;
+ return 0;
+}
+
+char*
+rwalk(Fcall *t, Fcall *r, char *mdata9p1)
+{
+ char *err;
+ Fcall9p1 t9, r9;
+ int i, fid;
+ Qid *q;
+ Fid *f, *nf;
+
+ f = newfid(t->fid);
+ if(!f->busy)
+ return "walk: bad fid";
+
+ fid = t->fid;
+ nf = nil;
+ if(t->fid != t->newfid){
+ nf = newfid(t->newfid);
+ if(nf->busy)
+ return "walk: newfid in use";
+ t9.type = Tclone9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ t9.newfid = t->newfid;
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err){
+ nf->busy = 0;
+ nf->allocated = 0;
+ return err;
+ }
+ fid = t->newfid;
+ nf->busy = 1;
+ }
+
+ err = nil;
+ r->nwqid = 0;
+ for(i=0; i<t->nwname && err==nil; i++){
+ if(i > MAXWELEM)
+ break;
+ t9.type = Twalk9p1;
+ t9.tag = t->tag;
+ t9.fid = fid;
+ strncpy(t9.name, t->wname[i], NAMEREC);
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err == FLUSHED){
+ i = -1; /* guarantee cleanup */
+ break;
+ }
+ if(err == nil){
+ q = &r->wqid[r->nwqid++];
+ q->type = QTFILE;
+ if(r9.qid.path & 0x80000000)
+ q->type = QTDIR;
+ q->vers = r9.qid.version;
+ q->path = r9.qid.path & ~0x80000000;
+ }
+ }
+
+ if(nf!=nil && (err!=nil || i<t->nwname)){
+ /* clunk the new fid */
+ t9.type = Tclunk9p1;
+ t9.tag = t->tag;
+ t9.fid = t->newfid;
+ transact9p1(&t9, &r9, mdata9p1);
+ /* ignore more errors */
+ nf->busy = 0;
+ nf->allocated = 0;
+ }
+
+ if(i>0 && i==t->nwname && err==nil)
+ f->qid = r->wqid[r->nwqid-1];
+ if(i > 0)
+ return 0;
+ return err;
+}
+
+char*
+ropen(Fcall *t, Fcall *r, char *mdata9p1)
+{
+ char *err;
+ Fcall9p1 t9, r9;
+ Fid *f;
+
+ f = newfid(t->fid);
+ if(!f->busy)
+ return "open: bad fid";
+
+ t9.type = Topen9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ t9.mode = t->mode;
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err)
+ return err;
+
+ r->qid.path = r9.qid.path & ~0x80000000;
+ r->qid.vers = r9.qid.version;
+ r->qid.type = QTFILE;
+ if(r9.qid.path & 0x80000000)
+ r->qid.type = QTDIR;
+ f->qid = r->qid;
+ f->newoffset = 0;
+ f->oldoffset = 0;
+ r->iounit = 0;
+ return 0;
+}
+
+char*
+rcreate(Fcall *t, Fcall *r, char *mdata9p1)
+{
+ char *err;
+ Fcall9p1 t9, r9;
+ Fid *f;
+
+ f = newfid(t->fid);
+ if(!f->busy)
+ return "create: bad fid";
+
+ t9.type = Tcreate9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ if(strlen(t->name)+1 >= NAMEREC)
+ return "file name element too long";
+ strncpy(t9.name, t->name, NAMEREC);
+ t9.perm = t->perm;
+ t9.mode = t->mode;
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err)
+ return err;
+
+ r->qid.path = r9.qid.path & ~0x80000000;
+ r->qid.vers = r9.qid.version;
+ r->qid.type = QTFILE;
+ if(r9.qid.path & 0x80000000)
+ r->qid.type = QTDIR;
+ if(r9.qid.path & 0x40000000)
+ r->qid.type |= QTAPPEND;
+ if(r9.qid.path & 0x20000000)
+ r->qid.type |= QTEXCL;
+ f->qid = r->qid;
+ r->iounit = 0;
+ return 0;
+}
+
+char*
+dirrread(Fcall *t, Fcall *r, char *mdata9p1)
+{
+ char *err;
+ Fcall9p1 t9, r9;
+ Fid *f;
+ int i, ndir, n, count;
+ Dir d;
+ uchar buf[Maxfdata];
+ char *old;
+
+ f = newfid(t->fid);
+ if(!f->busy)
+ return "dirread: bad fid";
+
+ if(f->newoffset != t->offset)
+ return "seek in directory disallowed";
+
+ t9.type = Tread9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ t9.offset = f->oldoffset;
+ t9.count = t->count; /* new directories tend to be smaller, so this may overshoot */
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err)
+ return err;
+
+ ndir = r9.count/DIRREC;
+ old = r9.data;
+ count = 0;
+ for(i=0; i<ndir; i++){
+ if(convM2D9p1(old, &d) != DIRREC)
+ return "bad dir conversion in read";
+ n = convD2M(&d, buf+count, sizeof buf-count);
+ if(n<=BIT16SZ || count+n>t->count)
+ break;
+ old += DIRREC;
+ f->oldoffset += DIRREC;
+ f->newoffset += n;
+ count += n;
+ }
+ memmove(r9.data, buf, count); /* put it back in stable storage */
+ r->data = r9.data;
+ r->count = count;
+ return 0;
+}
+
+char*
+rread(Fcall *t, Fcall *r, char *mdata9p1)
+{
+ char *err;
+ Fcall9p1 t9, r9;
+ Fid *f;
+
+ f = newfid(t->fid);
+ if(!f->busy)
+ return "read: bad fid";
+
+ if(f->qid.type & QTDIR)
+ return dirrread(t, r, mdata9p1);
+
+ t9.type = Tread9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ t9.offset = t->offset;
+ t9.count = t->count;
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err)
+ return err;
+
+ r->count = r9.count;
+ r->data = r9.data; /* points to stable storage */
+ return 0;
+}
+
+char*
+rwrite(Fcall *t, Fcall *r, char *mdata9p1)
+{
+ char *err;
+ Fcall9p1 t9, r9;
+ Fid *f;
+
+ f = newfid(t->fid);
+ if(!f->busy)
+ return "write: bad fid";
+
+ t9.type = Twrite9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ t9.offset = t->offset;
+ t9.count = t->count;
+ t9.data = t->data;
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err)
+ return err;
+
+ r->count = r9.count;
+ return 0;
+}
+
+char*
+rclunk(Fcall *t, Fcall *, char *mdata9p1)
+{
+ Fcall9p1 t9, r9;
+ Fid *f;
+
+ f = newfid(t->fid);
+ if(!f->busy)
+ return "clunk: bad fid";
+ t9.type = Tclunk9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ transact9p1(&t9, &r9, mdata9p1);
+ f->busy = 0;
+ f->allocated = 0;
+ /* disregard error */
+ return 0;
+}
+
+char*
+rremove(Fcall *t, Fcall*, char *mdata9p1)
+{
+ char *err;
+ Fcall9p1 t9, r9;
+ Fid *f;
+
+ f = newfid(t->fid);
+ if(!f->busy)
+ return "remove: bad fid";
+ t9.type = Tremove9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ err = transact9p1(&t9, &r9, mdata9p1);
+ f->busy = 0;
+ f->allocated = 0;
+ return err;
+}
+
+char*
+rstat(Fcall *t, Fcall *r, char *mdata9p1)
+{
+ Fcall9p1 t9, r9;
+ char *err;
+ Fid *f;
+ Dir d;
+ uchar buf[256]; /* big enough; there's no long names */
+
+ f = newfid(t->fid);
+ if(!f->busy)
+ return "stat: bad fid";
+
+ t9.type = Tstat9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err)
+ return err;
+
+ if(convM2D9p1(r9.stat, &d) != DIRREC)
+ return "bad conversion in stat";
+ r->stat = buf;
+ r->nstat = convD2M(&d, buf, sizeof buf);
+ return 0;
+}
+
+int
+anydefault(Dir *d)
+{
+ if(d->name[0] == '\0')
+ return 1;
+ if(d->uid[0] == '\0')
+ return 1;
+ if(d->gid[0] == '\0')
+ return 1;
+ if(d->mode == ~0)
+ return 1;
+ if(d->mtime == ~0)
+ return 1;
+ return 0;
+}
+
+char*
+rwstat(Fcall *t, Fcall *, char *mdata9p1)
+{
+ Fcall9p1 t9, r9;
+ char strs[DIRREC];
+ char *err;
+ Fid *f;
+ Dir d, cd;
+
+ f = newfid(t->fid);
+ if(!f->busy)
+ return "wstat: bad fid";
+
+ convM2D(t->stat, t->nstat, &d, strs);
+ cd = d;
+ if(anydefault(&d)){
+ /* must first stat file so we can copy current values */
+ t9.type = Tstat9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err)
+ return err;
+ if(convM2D9p1(r9.stat, &cd) != DIRREC)
+ return "bad in conversion in wstat";
+
+ /* fill in default values */
+ if(d.name[0] != '\0'){
+ if(strlen(d.name) >= NAMEREC)
+ return Etoolong;
+ cd.name = d.name;
+ }
+ if(d.uid[0] != '\0'){
+ if(strlen(d.uid) >= NAMEREC)
+ return Etoolong;
+ cd.uid = d.uid;
+ }
+ if(d.gid[0] != '\0'){
+ if(strlen(d.gid) >= NAMEREC)
+ return Etoolong;
+ cd.gid = d.gid;
+ }
+ if(d.mode != ~0)
+ cd.mode = d.mode;
+ if(d.mtime != ~0)
+ cd.mtime = d.mtime;
+ if(d.length != ~0LL)
+ cd.length = d.length;
+ }
+
+ if(convD2M9p1(&cd, t9.stat) != DIRREC)
+ return "bad out conversion in wstat";
+
+ t9.type = Twstat9p1;
+ t9.tag = t->tag;
+ t9.fid = t->fid;
+ err = transact9p1(&t9, &r9, mdata9p1);
+ if(err)
+ return err;
+ return 0;
+}
+
+void *
+emalloc(ulong n)
+{
+ void *p;
+
+ p = malloc(n);
+ if(!p)
+ fatal("out of memory: %r");
+ memset(p, 0, n);
+ return p;
+}
+
+void
+fatal(char *fmt, ...)
+{
+ char buf[1024];
+ va_list arg;
+
+ if(fmt){
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ va_end(arg);
+ fprint(2, "%s: (pid %d) %s\n", argv0, getpid(), buf);
+ }else
+ buf[0] = '\0';
+ if(mainpid){
+ /* two hits are sometimes needed */
+ postnote(PNGROUP, mainpid, "die1 - from srvold9p");
+ postnote(PNGROUP, mainpid, "die2 - from srvold9p");
+ }
+ exits(buf);
+}