commit - 2f27a08d6204e68291684c6250a7bbc9fe11fb07
commit + 021481cadee5324ef838e632a87746cdabffd5e3
blob - 581c92e6ee98c98e0ef5cb12f4c0f8169d551090
blob + cba83b9e3fbbf1c2a6f00940931a89fca6d44dda
--- 9p.7
+++ 9p.7
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
/*
* 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;
/*
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 *);
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);
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
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);
}
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;
}
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;
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 {
{Tclunk, tclunk},
{Tflush, tflush},
{Twalk, twalk},
+ {Topen, topen},
};
struct np_msg_header hdr;
size_t i;
blob - 20e493776432b52f9d71a48023a3602d0464d402
blob + dc6de158f75a8df9950cb053adb50729cdc63490
--- kamid.h
+++ kamid.h
#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
# ...
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
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
)
}
+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
+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
+}