Browse Source

Got working native 9p, can create/read/write files, more stable on large
transfers than virtio-serial 9p.

Signed-off-by: golubovsky <golubovsky@gmail.com>

golubovsky 7 years ago
parent
commit
ef60e3451a
6 changed files with 293 additions and 129 deletions
  1. 3 1
      sys/src/9/port/chan.c
  2. 2 2
      sys/src/9/port/dev.c
  3. 274 123
      sys/src/9/port/dev9p.c
  4. 5 0
      sys/src/9/port/devcons.c
  5. 1 0
      sys/src/9/port/portfns.h
  6. 8 3
      sys/src/9/port/sysfile.c

+ 3 - 1
sys/src/9/port/chan.c

@@ -14,6 +14,8 @@
 #include	"fns.h"
 #include	"../port/error.h"
 
+int checkdc(int dc);
+
 enum
 {
 	PATHSLOP	= 20,
@@ -1284,7 +1286,7 @@ namec(char *aname, int amode, int omode, int perm)
 		 */
 		n = chartorune(&r, up->genbuf+1)+1;
 		/* actually / is caught by parsing earlier */
-		if(utfrune("M", r))
+		if(checkdc(r))
 			error(Enoattach);
 		if(up->pgrp->noattach && utfrune("|decp", r)==nil)
 			error(Enoattach);

+ 2 - 2
sys/src/9/port/dev.c

@@ -177,8 +177,8 @@ devclone(Chan *c)
 	Chan *nc;
 
 	if(c->flag & COPEN){
-		panic("devclone: file of type %C already open\n",
-			c->dev != nil? c->dev->dc: -1);
+		panic("devclone: file of type %C %s already open\n",
+			c->dev != nil? c->dev->dc: -1, chanpath(c));
 	}
 
 	nc = newchan();

+ 274 - 123
sys/src/9/port/dev9p.c

@@ -29,6 +29,14 @@
 
 char* strdup(char *s);
 
+// Functions from devmnt.c
+
+int32_t	mntrdwr(int, Chan*, void*, int32_t, int64_t);
+
+void mntdirfix(uint8_t *dirbuf, Chan *c);
+
+extern char Esbadstat[];
+
 // We use this version string to communicate with QEMU virtio-9P.
 
 #define    VERSION9PU       "9P2000.u"
@@ -45,8 +53,18 @@ char* strdup(char *s);
 
 #define Edonotcall(f) "Function " #f " should not be called for this device"
 
+// This device's dev methods table
+
 extern Dev v9pdevtab;
 
+// A phantom device's dev methods table
+
+extern Dev phdevtab;
+
+// The 'M' device's dev methods table
+
+static Dev *mdevtab;
+
 // Array of defined 9p mounts and their number
 
 static uint32_t nv9p;
@@ -103,6 +121,7 @@ static struct v9pmnt
 	int pcuse;					// cache usage counter (entering processes)
 	int pchit;					// cache hits counter
 	int pcmiss;					// cache misses counter
+	uint mounted;				// true if mounted
 } *mounts;
 
 // Find a hold buffer structure by PID. Nil is returned if no process found.
@@ -142,50 +161,9 @@ findtag(char *tag)
 		if(mounts[i].tag && (!strcmp(mounts[i].tag, tag)))
 			return i;
 	}
-print("tag not found: %s\n", tag);
 	return -1;
 }
 
-// Emulate bindmount for each mount tag. We attach to devmnt directly, without
-// the user calling mount. The logic of devmnt properly sequences write and read
-// requests, but this cannot be expected from any other client. So 9p virtqueues
-// are not generally accessible, they operate under the hood.
-
-static void
-domountvq(int tidx)
-{
-	struct {
-		Chan    *chan;
-		Chan    *authchan;
-		char    *spec;
-		int     flags;
-	} bogus;
-	int dc = 'M';
-	Dev *dev = devtabget(dc, 0);
-	if(dev == nil)
-		error("no #M device found");
-	bogus.spec = mounts[tidx].tag;
-	bogus.flags = MCACHE;
-	bogus.authchan = nil;
-	bogus.chan = newchan();
-	bogus.chan->dev = &v9pdevtab;
-	bogus.chan->path = newpath(bogus.spec);
-	Chan *c0 = dev->attach((char *)&bogus);
-	Chan *c1 = namec(strdup("/mnt/xxx"), Amount, 0, 0);
-	cmount(&c0, c1, MAFTER, bogus.spec); 
-}
-
-static void 
-mntvq(void)
-{
-	if(initdone)
-		return;
-	initdone = 1;
-	for(int i = 0; i < nv9p; i++) {
-		print("auto mount %d\n", i);
-		domountvq(i);
-	}
-}
 
 static void
 v9pinit(void)
@@ -193,6 +171,11 @@ v9pinit(void)
 	uint32_t nvdev;
 
 	print("virtio-9p initializing\n");
+	mdevtab = devtabget('M', 1);
+	if(mdevtab == nil) {
+		print("no #M device found, cannot initialize virtio-9p");
+		return;
+	}
 	nvdev = getvdevnum();
 	if(nvdev <= 0)
 		return;
@@ -270,7 +253,7 @@ do_request(int gdescr, int tidx, void *inbuf, int32_t inlen, void *outbuf, int32
 // applies based on the extracted message type.
 
 static int32_t
-v9pwrite(Chan *c, void *va, int32_t n, int64_t offset)
+phwrite(Chan *c, void *va, int32_t n, int64_t offset)
 {
 	Proc *up = externup();
 	int tidx = findtag(chanpath(c));
@@ -328,49 +311,81 @@ v9pwrite(Chan *c, void *va, int32_t n, int64_t offset)
 	pm->pidch[up->pid & PIDCMASK].pid = up->pid;
 	pm->pcuse++;
 	unlock(&pm->pclock);
-print("write %s msg %d %d\n", chanpath(c), mtype, lnva);
 	return n;
 }
 
-static int
-v9pgen(Chan *c, char *d, Dirtab* dir, int i, int s, Dir *dp)
-{
-	return -1;
-}
-
+// Override the devmnt's read method. It is necessary to fix the incorrectly packed
+// stat structures when reading from a directory.
 
-int
-statcheckx(uint8_t *buf, uint nbuf)
+static int32_t
+v9pread(Chan *c, void *buf, int32_t n, int64_t off)
 {
-        uint8_t *ebuf;
-        int i;
-
-        ebuf = buf + nbuf;
-
-print("nbuf %d STATFIXLEN %d BIT16SZ + GBIT16(buf) %d\n", nbuf, STATFIXLEN, BIT16SZ + GBIT16(buf));
-
-        if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf))
-                {print("x1\n");return -1;}
-
-        buf += STATFIXLEN - 4 * BIT16SZ;
-
-
-        for(i = 0; i < 4; i++){
-                if(buf + BIT16SZ > ebuf)
-					{print("x2\n");return -1;}
-                buf += BIT16SZ + GBIT16(buf);
-        }
-
-print("buf %X ebuf %X\n", buf, ebuf);
+	uint8_t *p, *e;
+	int nc, cache, isdir;
+	usize dirlen;
+
+	isdir = 0;
+	cache = c->flag & CCACHE;
+	if(c->qid.type & QTDIR) {
+		cache = 0;
+		isdir = 1;
+	}
 
-        if(buf != ebuf)
-                {print("x3\n");return -1;}
+	p = buf;
+	if(cache) {
+		nc = mfcread(c, buf, n, off);
+		if(nc > 0) {
+			n -= nc;
+			if(n == 0)
+				return nc;
+			p += nc;
+			off += nc;
+		}
+		n = mntrdwr(Tread, c, p, n, off);
+		mfcupdate(c, p, n, off);
+		return n + nc;
+	}
 
-        return 0;
+	n = mntrdwr(Tread, c, buf, n, off);
+	if(isdir) {
+		uint8_t *nbuf = malloc(n);
+		if(nbuf == nil)
+			error(Enomem);
+		uint8_t *xnbuf = nbuf;
+		for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
+			dirlen = BIT16SZ+GBIT16(p);
+			if(p+dirlen > e)
+				break;
+			uint8_t *pn = p + 41;
+			uint lstrs = 0;
+			for(int i = 0; i < 4; i++) {
+				int ns = GBIT16(pn);
+				lstrs += ns + 1;
+				pn += ns + BIT16SZ;
+			}
+			{
+				char strs[lstrs];
+				Dir d;
+				convM2D(p, dirlen, &d, strs);
+				d.uid = eve;
+				d.gid = eve;
+				d.muid = eve;
+				uint dms = convD2M(&d, xnbuf, dirlen);
+				validstat(xnbuf, dms);
+				mntdirfix(xnbuf, c);
+				xnbuf = xnbuf + dms;
+			}
+		}
+		if(p != e)
+			error(Esbadstat);
+		memmove(buf, nbuf, (xnbuf - nbuf));
+		n = xnbuf - nbuf;
+	}
+	return n;
 }
 
 
-// We expect only 9p messages be received, and only for a non-empty chan path (mount tag).
+// We expect only 9p messages to be received.
 // Some messages need massaging (like Rversion because QEMU does not support vanilla 9P2000
 // and we have to cheat here about the protocol version). In such case some additional logic
 // applies based on the extracted message type. The function checks for a held write buffer,
@@ -378,10 +393,8 @@ print("buf %X ebuf %X\n", buf, ebuf);
 // 4 bytes of the message in some cases.
 
 static int32_t
-v9pread(Chan *c, void *va, int32_t n, int64_t offset)
+phread(Chan *c, void *va, int32_t n, int64_t offset)
 {
-	if(!strcmp(chanpath(c), "#9"))
-		return devdirread(c, va, n, (Dirtab *)0, 0L, v9pgen);
 	Proc *up = externup();
 	int tidx = findtag(chanpath(c));
 	if(tidx < 0 || tidx >= nv9p)
@@ -417,80 +430,218 @@ v9pread(Chan *c, void *va, int32_t n, int64_t offset)
 		uint nbuf = GBIT16(msg + 9);
 		uint8_t *buf = msg + 9;
 		Dir d;
-		char strs[1024];
-		convM2D(buf, nbuf, &d, strs);
-		d.uid = eve;
-		d.gid = eve;
-		d.muid = eve;
-		uint dms = convD2M(&d, buf, nbuf);
-		PBIT16(msg + 7, dms);
-		mlen = 9 + dms;
-		PBIT32(msg, mlen);
+		uint8_t *pn = buf + 41;
+		uint lstrs = 0;
+		for(int i = 0; i < 4; i++) {
+			int ns = GBIT16(pn);
+			lstrs += ns + 1;
+			pn += ns + BIT16SZ;
+		}
+		{
+			char strs[lstrs];
+			convM2D(buf, nbuf, &d, strs);
+			d.uid = eve;
+			d.gid = eve;
+			d.muid = eve;
+			uint dms = convD2M(&d, buf, nbuf);
+			PBIT16(msg + 7, dms);
+			mlen = 9 + dms;
+			PBIT32(msg, mlen);
+		}
 	default:
 		;
 	}
-print("read %s msg %d %d\n", chanpath(c), mtype, mlen);
 	return mlen;
 }
 
-static int
-v9pversion(int tidx)
-{
-	error(Edonotcall(version));
-	return 0;
-}
-
-// First attach of #9 will force mounting of all defined tags with devmnt. It cannot be done in devinit
-// because error() called in a kernel process without user context causes double fault and kernel crash.
-// So, to have the host shares actually mounted one has to issue something like ls '#9'.
+// Use a command like "mount [-c] -d '#9' /dev/null /mount/point tag".
+// It is "tag" that matters: it should be same as one of the mount tags
+// provided by the host. The server file name may be any existing file name.
+// It will not be used, cf. "mount none" in Linux. Use "-d '#9'" to use
+// proper mount device methods.
 
 static Chan*
 v9pattach(char *spec)
 {
-	mntvq();
-	if(strlen(spec) == 0)
-		return devattach(v9pdevtab.dc, "");
-print("stray attach %s\n", spec);
-	error(Edonotcall(attach));
-	return nil;
+	struct bogus{
+		Chan	*chan;
+		Chan	*authchan;
+		char	*spec;
+		int	flags;
+	}bogus;
+	bogus = *((struct bogus *)spec);
+	int tidx = findtag(bogus.spec);
+	if(tidx < 0)
+		error("tag does not exist");
+	bogus.authchan = nil;
+	Chan *c = bogus.chan;
+	c->dev = &phdevtab;
+	c->path = newpath(bogus.spec);
+	Chan *mc = mdevtab->attach((char *)&bogus);
+	mc->dev = &v9pdevtab;
+	mounts[tidx].mounted = 1;
+	return mc;
 }
 
 static Chan*
 v9popen(Chan *c, int omode)
 {
-	if(!strcmp(chanpath(c), "#9"))
-		return devopen(c, omode, (Dirtab*)0, 0, v9pgen);
-	error(Edonotcall(open));
-	return nil;
+	return mdevtab->open(c, omode);
 }
 
 static Walkqid*
 v9pwalk(Chan* c, Chan *nc, char** name, int nname)
 {
-	if(!strcmp(chanpath(c), "#9"))
-		return devwalk(c, nc, name, nname, (Dirtab *)0, 0, v9pgen);
-print("walk %s\n", chanpath(c));
-	error(Edonotcall(walk));
-	return nil;
+	return mdevtab->walk(c, nc, name, nname);
 }
 
 static int32_t
 v9pstat(Chan* c, uint8_t* dp, int32_t n)
 {
-	if(!strcmp(chanpath(c), "#9"))
-		return devstat(c, dp, n, (Dirtab *)0, 0L, v9pgen);
-	error(Edonotcall(stat));
-	return 0;
+	return mdevtab->stat(c, dp, n);
 }
 
 static void
 v9pclose(Chan* c)
 {
-	if(!strcmp(chanpath(c), "#9"))
-		return;
-	error(Edonotcall(close));
+	int tidx = findtag(chanpath(c));
+	if(tidx >= 0 && tidx < nv9p)
+		mounts[tidx].mounted = 0;
+	mdevtab->close(c);
+}
+
+static void
+v9pcreate(Chan *c, char *name, int omode, int perm)
+{
+	mdevtab->create(c, name, omode, perm);
+}
+
+static void
+v9premove(Chan *c)
+{
+	mdevtab->remove(c);
 }
 
+static int32_t
+v9pwstat(Chan *c, uint8_t *dp, int32_t n)
+{
+	return mdevtab->wstat(c, dp, n);
+}
+
+static int32_t
+v9pwrite(Chan *c, void *va, int32_t n, int64_t offset)
+{
+	return mdevtab->write(c, va, n, offset);
+}
+
+
+// Phantom device. It is used only for read/write operations. It is not registered in the
+// global table or devices, and is not addressable in any other way. It is only needed to
+// pass the reference to the read/write methods to the mount driver. 
+
+static Chan*
+phattach(char *spec)
+{
+	error(Edonotcall(__FUNCTION__));
+	return nil;
+}
+
+static Walkqid*
+phwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+	error(Edonotcall(__FUNCTION__));
+	return nil;
+}
+
+static int32_t
+phstat(Chan* c, uint8_t* dp, int32_t n)
+{
+	error(Edonotcall(__FUNCTION__));
+	return -1;
+}
+
+static int32_t
+phwstat(Chan *c, uint8_t *dp, int32_t n)
+{
+	error(Edonotcall(__FUNCTION__));
+	return -1;
+}
+
+static Chan*
+phopen(Chan *c, int omode)
+{
+	error(Edonotcall(__FUNCTION__));
+	return nil;
+}
+
+static void
+phclose(Chan* c)
+{
+	error(Edonotcall(__FUNCTION__));
+}
+
+static void
+phcreate(Chan *c, char *name, int omode, int perm)
+{
+	error(Edonotcall(__FUNCTION__));
+}
+
+static void
+phremove(Chan *c)
+{
+	error(Edonotcall(__FUNCTION__));
+}
+
+// Read mount tags information as tag:version:msize:pcuse:pchit:pcmiss for mounted tags, and
+// tag:- for non-mounted.
+
+int32_t
+mtagsread(Chan* c, void* buf, int32_t n, int64_t off)
+{
+	Proc *up = externup();
+	int i;
+	char *alloc, *e, *p;
+	alloc = malloc(READSTR);
+	if(alloc == nil)
+		error(Enomem);
+	p = alloc;
+	e = p + READSTR;
+	for(i = 0; i < nv9p; i++) {
+		p = mounts[i].mounted?seprint(p, e, "%s:%s:%d:%d:%d\n", mounts[i].tag, mounts[i].version, mounts[i].msize, mounts[i].pcuse, mounts[i].pchit, mounts[i].pcmiss):
+							  seprint(p, e, "%s:-\n", mounts[i].tag);
+	}
+	if(waserror()) {
+		free(alloc);
+		nexterror();
+	}
+	n = readstr(off, buf, n, alloc);
+	free(alloc);
+	poperror();
+	return n;
+}
+
+Dev phdevtab = {
+	.dc = 2151,			/* 1/9 */
+	.name = "9phantom",
+	
+	.reset = devreset,
+	.init = devinit,
+	.shutdown = devshutdown,
+	.attach = phattach,
+	.walk = phwalk,
+	.stat = phstat,
+	.open = phopen,
+	.create = phcreate,
+	.close = phclose,
+	.read = phread,
+	.bread = devbread,
+	.write = phwrite,
+	.bwrite = devbwrite,
+	.remove = phremove,
+	.wstat = phwstat,
+};
+
+
 Dev v9pdevtab = {
 	.dc = '9',
 	.name = "9p",
@@ -502,12 +653,12 @@ Dev v9pdevtab = {
 	.walk = v9pwalk,
 	.stat = v9pstat,
 	.open = v9popen,
-	.create = devcreate,
+	.create = v9pcreate,
 	.close = v9pclose,
 	.read = v9pread,
 	.bread = devbread,
 	.write = v9pwrite,
 	.bwrite = devbwrite,
-	.remove = devremove,
-	.wstat = devwstat,
+	.remove = v9premove,
+	.wstat = v9pwstat,
 };

+ 5 - 0
sys/src/9/port/devcons.c

@@ -382,6 +382,7 @@ enum{
 	Quser,
 	Qzero,
 	Qsyscall,
+	Qmtags,
 	Qdebug,
 };
 
@@ -416,6 +417,7 @@ static Dirtab consdir[]={
 	"user",		{Quser},	0,		0666,
 	"zero",		{Qzero},	0,		0444,
 	"syscall",	{Qsyscall},	0,		0666,
+	"mtags",	{Qmtags},	0,		0666,
 	"debug",	{Qdebug},	0,		0666,
 };
 
@@ -665,6 +667,9 @@ consread(Chan *c, void *buf, int32_t n, int64_t off)
 	case Qdrivers:
 		return devtabread(c, buf, n, off);
 
+	case Qmtags:
+		return mtagsread(c, buf, n, off);
+
 	case Qzero:
 		memset(buf, 0, n);
 		return n;

+ 1 - 0
sys/src/9/port/portfns.h

@@ -85,6 +85,7 @@ int32_t		devstat(Chan*, unsigned char*, int32_t, Dirtab*, int,
 			       Devgen*);
 Dev*		devtabget(int, int);
 void		devtabinit(void);
+int32_t		mtagsread(Chan*, void*, int32_t, int64_t);
 int32_t		devtabread(Chan*, void*, int32_t, int64_t);
 void		devtabreset(void);
 void		devtabshutdown(void);

+ 8 - 3
sys/src/9/port/sysfile.c

@@ -710,7 +710,6 @@ mountfix(Chan *c, uint8_t *op, int32_t n, int32_t maxn)
 	}
 	if(buf)
 		free(buf);
-
 	if(p != e)
 		error("oops in mountfix");
 
@@ -1169,9 +1168,15 @@ syschdir(Ar0* ar0, ...)
 
 static int dcok[] =  {
 	'M',
-	'N'
+	'N',
+	'9'
 };
-static int checkdc(int dc)
+
+/* to be used in chan.c to disallow attach to these devices
+ */
+
+int 
+checkdc(int dc)
 {
 	int i;
 	/* we check for non-zero in case somebody ever puts a ,