Commit Diff


commit - 2f27a08d6204e68291684c6250a7bbc9fe11fb07
commit + 021481cadee5324ef838e632a87746cdabffd5e3
blob - 581c92e6ee98c98e0ef5cb12f4c0f8169d551090
blob + cba83b9e3fbbf1c2a6f00940931a89fca6d44dda
--- 9p.7
+++ 9p.7
@@ -149,6 +149,12 @@ Traverse a file tree.
 fid[4] newfid[4] nwname[2] nwname*(wname[s])
 nwqid[2] nwqid*(qid[13])
 .Ed
+.It Ic open
+Prepare a fid for I/O
+.Bd -literal
+fid[4] mode[1]
+qid[13] iounit[4]
+.Ed
 .El
 .Sh SEE ALSO
 .Xr utf8 7 ,
blob - 11d9896410ce3e6af920acca7b70261d239a1a11
blob + 14cb21d918481f2613d20751cddbb567cfddba36
--- client.c
+++ client.c
@@ -57,10 +57,9 @@ struct fid {
 
 	/*
 	 * 0 when the fid was not yet opened for I/O otherwise set to
-	 * the bitwise or of KFIO_R for read and KFIO_W for write
+	 * the flags passed to open(2).  O_CLOEXEC means ORCLOSE, that
+	 * is to unlink the file upon Tclunk.
 	 */
-#define KFIO_W	0x02
-#define KFIO_R	0x04
 	int			 iomode;
 
 	/*
@@ -100,6 +99,7 @@ static void		parse_message(const uint8_t *, size_t,
 			    struct np_msg_header *, uint8_t **);
 
 static void		np_write16(uint16_t);
+static void		np_write32(uint32_t);
 static void		np_header(uint32_t, uint8_t, uint16_t);
 static void		np_string(uint16_t, const char *);
 static void		np_qid(struct qid *);
@@ -110,6 +110,7 @@ static void		np_attach(uint16_t, struct qid *);
 static void		np_clunk(uint16_t);
 static void		np_flush(uint16_t);
 static void		np_walk(uint16_t, int, struct qid *);
+static void		np_open(uint16_t, struct qid *, uint32_t);
 static void		np_error(uint16_t, const char *);
 static void		np_errno(uint16_t);
 
@@ -136,6 +137,7 @@ static void	tattach(struct np_msg_header *, const uint
 static void	tclunk(struct np_msg_header *, const uint8_t *, size_t);
 static void	tflush(struct np_msg_header *, const uint8_t *, size_t);
 static void	twalk(struct np_msg_header *, const uint8_t *, size_t);
+static void	topen(struct np_msg_header *, const uint8_t *, size_t);
 static void	handle_message(struct imsg *, size_t);
 
 ATTR_DEAD void
@@ -447,6 +449,13 @@ fid_by_id(uint32_t fid)
 static void
 free_fid(struct fid *f)
 {
+	if (f->fd != -1) {
+		close(f->fd);
+		/* try to honour ORCLOSE if requested */
+		if (f->iomode & O_CLOEXEC)
+			unlinkat(f->qid->fd, f->qid->fpath, 0);
+	}
+
 	qid_decref(f->qid);
 
 	STAILQ_REMOVE(&fids, f, fid, entries);
@@ -491,6 +500,13 @@ np_write16(uint16_t x)
 }
 
 static void
+np_write32(uint32_t x)
+{
+	x = htole32(x);
+	evbuffer_add(evb, &x, sizeof(x));
+}
+
+static void
 np_header(uint32_t len, uint8_t type, uint16_t tag)
 {
 	len += HEADERSIZE;
@@ -595,6 +611,15 @@ np_walk(uint16_t tag, int nwqid, struct qid *wqid)
 }
 
 static void
+np_open(uint16_t tag, struct qid *qid, uint32_t iounit)
+{
+	np_header(QIDSIZE + sizeof(iounit), Ropen, tag);
+	np_qid(qid);
+	np_write32(iounit);
+	do_send();
+}
+
+static void
 np_error(uint16_t tag, const char *errstr)
 {
 	uint16_t l;
@@ -991,9 +1016,87 @@ cantopen:
 err:
 	client_send_listener(IMSG_CLOSE, NULL, 0);
 	client_shutdown();
+}
+
+static inline int
+npmode_to_unix(uint8_t mode, int *flags)
+{
+	switch (mode & 0x0F) {
+	case KOREAD:
+		*flags = O_RDONLY;
+		break;
+	case KOWRITE:
+		*flags = O_WRONLY;
+		break;
+	case KORDWR:
+		*flags = O_RDWR;
+		break;
+	case KOEXEC:
+		log_warnx("tried to open something with KOEXEC");
+		/* fallthrough */
+	default:
+		return -1;
+	}
+
+	if (mode & KOTRUNC)
+		*flags |= O_TRUNC;
+	if (mode & KORCLOSE)
+		*flags |= O_CLOEXEC;
+
+	return 0;
 }
 
 static void
+topen(struct np_msg_header *hdr, const uint8_t *data, size_t len)
+{
+	struct stat	 sb;
+	struct qid	 qid;
+	struct fid	*f;
+	uint32_t	 fid;
+	uint8_t		 mode;
+
+	/* fid[4] mode[1] */
+	if (!NPREAD32("fid", &fid, &data, &len) ||
+	    !NPREAD8("mode", &mode, &data, &len)) {
+		client_send_listener(IMSG_CLOSE, NULL, 0);
+		client_shutdown();
+		return;
+	}
+
+	if ((f = fid_by_id(fid)) == NULL || f->fd != -1) {
+		np_error(hdr->tag, "invalid fid");
+		return;
+	}
+
+	if (f->qid->type & QTDIR) {
+		/*
+	 	 * XXX: real 9P2000 uses reads on directories to list the
+		 * files, but 9P2000.L (and possibly .U too) uses
+		 * Treaddir.  It's my intention to support the 9p-style
+		 * read-on-dir, just not yet.
+		 */
+		np_error(hdr->tag, "can't do I/O on directories yet");
+		return;
+	}
+
+	if (npmode_to_unix(mode, &f->iomode) == -1) {
+		np_error(hdr->tag, "invalid mode");
+		return;
+	}
+
+	if ((f->fd = openat(f->qid->fd, f->qid->fpath, f->iomode)) == -1) {
+		np_error(hdr->tag, strerror(errno));
+		return;
+	}
+
+	if (fstat(f->fd, &sb) == -1)
+		fatal("fstat");
+
+	qid_update_from_sb(&qid, &sb);
+	np_open(hdr->tag, &qid, sb.st_blksize);
+}
+
+static void
 handle_message(struct imsg *imsg, size_t len)
 {
 	struct msg {
@@ -1005,6 +1108,7 @@ handle_message(struct imsg *imsg, size_t len)
 		{Tclunk,	tclunk},
 		{Tflush,	tflush},
 		{Twalk,		twalk},
+		{Topen,		topen},
 	};
 	struct np_msg_header	 hdr;
 	size_t			 i;
blob - 20e493776432b52f9d71a48023a3602d0464d402
blob + dc6de158f75a8df9950cb053adb50729cdc63490
--- kamid.h
+++ kamid.h
@@ -162,6 +162,14 @@ struct np_msg_header {
 #define QTSYMLINK	0x02		/* type bit for symbolic link */
 #define QTFILE		0x00		/* type bits for plain file */
 
+/* Topen mode/flags */
+#define KOREAD		0x00
+#define KOWRITE		0x01
+#define KORDWR		0x02
+#define KOEXEC		0x03
+#define KOTRUNC		0x10
+#define KORCLOSE	0x40
+
 /* 9p message types */
 enum {
 	Tversion =	100,
blob - 02c0f1056e32015f8c1c76e21286a2684cc3d2dd
blob + 5f149ef222c04f85943c9bcd2d100590e8f68d31
--- regress/consts.9ps
+++ regress/consts.9ps
@@ -8,6 +8,13 @@ const (
 	# ...
 	QTFILE	= 0x0
 
+	OREAD	= 0
+	OWRITE	= 1
+	ORDWR	= 2
+	OEXEC	= 3
+	OTRUNC	= 16
+	ORCLOSE	= 64
+
 	Tversion	= 100:u8
 	Rversion	= 101:u8
 	Tauth		= 102:u8
blob - b64ab820b4a598c5be0d0f3edca3079f9536c237
blob + 3f0ab88efea020d89b51d482691cf307a99c9cd3
--- regress/lib.9ps
+++ regress/lib.9ps
@@ -14,6 +14,14 @@ proc walk(fid, newfid, ...) {
 	send(Twalk, iota(), fid:u32, newfid:u32, vargs:u16, ...)
 }
 
+proc open(fid, mode) {
+	send(Topen, iota(), fid:u32, mode:u8)
+}
+
+proc clunk(fid) {
+	send(Tclunk, iota(), fid:u32)
+}
+
 
 
 # useful functions
@@ -38,6 +46,11 @@ proc mount(fid, path) {
 	)
 }
 
+proc walk-ok() {
+	m = recv()
+	assert m.type == Rwalk
+}
+
 proc expect-error() {
 	m = recv()
 	assert m.type == Rerror
blob - /dev/null
blob + fa58b7276325c65d3859238346b4fab028f14090 (mode 644)
--- /dev/null
+++ regress/io-suite.9ps
@@ -0,0 +1,15 @@
+include "lib.9ps"
+
+testing "open + clunk works" dir "./root" {
+	mount(0, "/")
+	walk(0, 1, "dir", "subdir", "file")
+	walk-ok()
+
+	open(1, OREAD)
+	m = recv()
+	assert m.type == Ropen
+
+	clunk(1)
+	m = recv()
+	assert m.type == Rclunk
+}