commit 021481cadee5324ef838e632a87746cdabffd5e3 from: Omar Polo date: Mon Dec 13 20:22:36 2021 UTC Topen implemented Implement Topen plus some basic testing. ORCLOSE (remove file when the fid is clunked) is mapped to O_CLOEXEC and tried to be honoured on fid_free. "vanilla" 9P2000 uses reads on directories to list the entries while 9P2000.L (and .U too possibly) introduces an explicit Treaddir. I'm planning to support 9P2000-style read-on-dir but not yet. 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 +}