Add the open request of the 9pfs protocol.
Signed-off-by: Juergen Gross <jgross@suse.com>
Acked-by: Anthony PERARD <anthony.perard@citrix.com>
---
V2:
- don't allow to open symbolic link
V3:
- use openat() (Jason Andryuk)
- use common error handling in p9_open()
---
tools/xen-9pfsd/io.c | 137 ++++++++++++++++++++++++++++++++++++
tools/xen-9pfsd/xen-9pfsd.h | 4 ++
2 files changed, 141 insertions(+)
diff --git a/tools/xen-9pfsd/io.c b/tools/xen-9pfsd/io.c
index 5b827706b7..2e2275e8ab 100644
--- a/tools/xen-9pfsd/io.c
+++ b/tools/xen-9pfsd/io.c
@@ -19,6 +19,8 @@
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
#include <xenctrl.h> /* For cpu barriers. */
#include <xen-tools/common-macros.h>
@@ -29,6 +31,15 @@
#define P9_CMD_ATTACH 104
#define P9_CMD_ERROR 107
#define P9_CMD_WALK 110
+#define P9_CMD_OPEN 112
+
+/* P9 protocol open flags. */
+#define P9_OREAD 0 /* read */
+#define P9_OWRITE 1 /* write */
+#define P9_ORDWR 2 /* read and write */
+#define P9_OMODEMASK 0x03
+#define P9_OTRUNC 0x10 /* or'ed in, truncate file first */
+#define P9_OREMOVE 0x40 /* or'ed in, remove file after clunk */
#define P9_MIN_MSIZE 2048
#define P9_VERSION "9P2000.u"
@@ -829,6 +840,128 @@ static void p9_walk(struct ring *ring, struct p9_header *hdr)
free(names);
}
+static int open_flags_from_mode(uint8_t mode)
+{
+ int flags;
+
+ switch ( mode & P9_OMODEMASK )
+ {
+ case P9_OREAD:
+ flags = O_RDONLY;
+ break;
+
+ case P9_OWRITE:
+ flags = O_WRONLY;
+ break;
+
+ case P9_ORDWR:
+ flags = O_RDWR;
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ( mode & P9_OTRUNC )
+ flags |= O_TRUNC;
+
+ return flags;
+}
+
+static unsigned int get_iounit(struct ring *ring, struct stat *st)
+{
+ return (ring->max_size - st->st_blksize) & ~(st->st_blksize - 1);
+}
+
+static void p9_open(struct ring *ring, struct p9_header *hdr)
+{
+ device *device = ring->device;
+ uint32_t fid;
+ uint8_t mode;
+ struct p9_fid *fidp;
+ struct stat st;
+ struct p9_qid qid;
+ uint32_t iounit;
+ int flags;
+ int ret;
+
+ ret = fill_data(ring, "Ub", &fid, &mode);
+ if ( ret != 2 )
+ {
+ p9_error(ring, hdr->tag, EINVAL);
+ return;
+ }
+ if ( mode & ~(P9_OMODEMASK | P9_OTRUNC | P9_OREMOVE) )
+ {
+ p9_error(ring, hdr->tag, EINVAL);
+ return;
+ }
+
+ fidp = get_fid_ref(device, fid);
+ if ( !fidp )
+ {
+ p9_error(ring, hdr->tag, ENOENT);
+ return;
+ }
+ if ( fidp->opened )
+ {
+ errno = EINVAL;
+ goto err;
+ }
+
+ if ( fstatat(device->root_fd, fidp->path, &st, 0) < 0 )
+ {
+ errno = ENOENT;
+ goto err;
+ }
+
+ if ( S_ISLNK(st.st_mode) )
+ {
+ errno = EMLINK;
+ goto err;
+ }
+
+ fidp->isdir = S_ISDIR(st.st_mode);
+ fidp->mode = mode;
+ if ( fidp->isdir )
+ {
+ if ( mode != P9_OREAD )
+ {
+ errno = EINVAL;
+ goto err;
+ }
+ fidp->fd = openat(device->root_fd, fidp->path, O_RDONLY);
+ if ( fidp->fd < 0 )
+ goto err;
+ fidp->data = fdopendir(fidp->fd);
+ if ( !fidp->data )
+ goto err;
+ }
+ else
+ {
+ flags = open_flags_from_mode(mode);
+ if ( flags < 0 )
+ goto err;
+
+ fidp->fd = openat(device->root_fd, fidp->path, flags);
+ if ( fidp->fd < 0 )
+ goto err;
+ }
+
+ fill_qid(device, fidp->path, &qid, &st);
+ iounit = get_iounit(ring, &st);
+ fidp->opened = true;
+
+ fill_buffer(ring, hdr->cmd + 1, hdr->tag, "QU", &qid, &iounit);
+
+ return;
+
+ err:
+ free_fid(device, fidp);
+ p9_error(ring, hdr->tag, errno);
+}
+
void *io_thread(void *arg)
{
struct ring *ring = arg;
@@ -896,6 +1029,10 @@ void *io_thread(void *arg)
p9_walk(ring, &hdr);
break;
+ case P9_CMD_OPEN:
+ p9_open(ring, &hdr);
+ break;
+
default:
syslog(LOG_DEBUG, "%u.%u sent unhandled command %u\n",
ring->device->domid, ring->device->devid, hdr.cmd);
diff --git a/tools/xen-9pfsd/xen-9pfsd.h b/tools/xen-9pfsd/xen-9pfsd.h
index f01fffb0bb..757be2da4b 100644
--- a/tools/xen-9pfsd/xen-9pfsd.h
+++ b/tools/xen-9pfsd/xen-9pfsd.h
@@ -25,7 +25,11 @@ struct p9_fid {
XEN_TAILQ_ENTRY(struct p9_fid) list;
unsigned int fid;
unsigned int ref;
+ int fd;
+ uint8_t mode;
bool opened;
+ bool isdir;
+ void *data; /* File type specific. */
char path[];
};
--
2.35.3