diff mbox

[V2,04/12] hw/9pfs: Open and create files

Message ID 1321358265-10924-5-git-send-email-mohan@in.ibm.com
State New
Headers show

Commit Message

Mohan Kumar M Nov. 15, 2011, 11:57 a.m. UTC
Add interfaces to open and create files for proxy file system driver.

Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
---
 fsdev/virtfs-proxy-helper.c |  136 +++++++++++++++++++++++++++++++-
 hw/9pfs/virtio-9p-proxy.c   |  180 +++++++++++++++++++++++++++++++++++++++++--
 hw/9pfs/virtio-9p-proxy.h   |    9 ++
 3 files changed, 314 insertions(+), 11 deletions(-)

Comments

Stefan Hajnoczi Nov. 17, 2011, 3:46 p.m. UTC | #1
On Tue, Nov 15, 2011 at 11:57 AM, M. Mohan Kumar <mohan@in.ibm.com> wrote:
> +static void send_fd(int sockfd, int fd)
> +{
> +    struct msghdr msg = { };
> +    struct iovec iov;
> +    struct cmsghdr *cmsg;
> +    int retval, data;
> +    union MsgControl msg_control;
> +
> +    iov.iov_base = &data;
> +    iov.iov_len = sizeof(data);
> +
> +    memset(&msg, 0, sizeof(msg));
> +    msg.msg_iov = &iov;
> +    msg.msg_iovlen = 1;
> +    /* No ancillary data on error */
> +    if (fd < 0) {
> +        /*
> +         * fd is really negative errno if the request failed. Or simply
> +         * zero if the request is successful and it doesn't need a file
> +         * descriptor.
> +         */

It cannot be zero because the if statement is fd < 0.  The comment is confusing.

> +/*
> + * create a file and send fd on success
> + * return -errno on error
> + */
> +static int do_create(struct iovec *iovec)
> +{
> +    V9fsString path;
> +    int flags, fd, mode, uid, gid, cur_uid, cur_gid;
> +    proxy_unmarshal(iovec, 1, HDR_SZ, "sdddd",
> +                   &path, &flags, &mode, &uid, &gid);

Unmarshalling can fail if the iovec size does not match what the
format string describes.  We should fail here rather than continuing
on.  If execution continues some of the variables may be
uninitialized.

Stefan
diff mbox

Patch

diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c
index 69daf7c..68d27f1 100644
--- a/fsdev/virtfs-proxy-helper.c
+++ b/fsdev/virtfs-proxy-helper.c
@@ -30,6 +30,8 @@ 
 #include "qemu-common.h"
 #include "virtio-9p-marshal.h"
 #include "hw/9pfs/virtio-9p-proxy.h"
+#include "fsdev/virtio-9p-marshal.h"
+
 
 #define PROGNAME "virtfs-proxy-helper"
 
@@ -148,20 +150,125 @@  static int read_request(int sockfd, struct iovec *iovec)
     ProxyHeader header;
 
     /* read the header */
-    retval = socket_read(sockfd, iovec->iov_base, sizeof(header));
-    if (retval != sizeof(header)) {
+    retval = socket_read(sockfd, iovec->iov_base, HDR_SZ);
+    if (retval != HDR_SZ) {
         return -EIO;
     }
     /* unmarshal header */
     proxy_unmarshal(iovec, 1, 0, "dd", &header.type, &header.size);
     /* read the request */
-    retval = socket_read(sockfd, iovec->iov_base + sizeof(header), header.size);
+    retval = socket_read(sockfd, iovec->iov_base + HDR_SZ, header.size);
     if (retval != header.size) {
         return -EIO;
     }
     return header.type;
 }
 
+static void send_fd(int sockfd, int fd)
+{
+    struct msghdr msg = { };
+    struct iovec iov;
+    struct cmsghdr *cmsg;
+    int retval, data;
+    union MsgControl msg_control;
+
+    iov.iov_base = &data;
+    iov.iov_len = sizeof(data);
+
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    /* No ancillary data on error */
+    if (fd < 0) {
+        /*
+         * fd is really negative errno if the request failed. Or simply
+         * zero if the request is successful and it doesn't need a file
+         * descriptor.
+         */
+        data = fd;
+    } else {
+        data = V9FS_FD_VALID;
+        msg.msg_control = &msg_control;
+        msg.msg_controllen = sizeof(msg_control);
+
+        cmsg = &msg_control.cmsg;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+    }
+
+    do {
+        retval = sendmsg(sockfd, &msg, 0);
+    } while (retval < 0 && errno == EINTR);
+    if (retval < 0) {
+        do_perror("sendmsg");
+        exit(1);
+    }
+    if (fd >= 0) {
+        close(fd);
+    }
+}
+
+/*
+ * from man 7 capabilities, section
+ * Effect of User ID Changes on Capabilities:
+ * 4. If the file system user ID is changed from 0 to nonzero (see setfsuid(2))
+ * then the following capabilities are cleared from the effective set:
+ * CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH,  CAP_FOWNER, CAP_FSETID,
+ * CAP_LINUX_IMMUTABLE  (since  Linux 2.2.30), CAP_MAC_OVERRIDE, and CAP_MKNOD
+ * (since Linux 2.2.30). If the file system UID is changed from nonzero to 0,
+ * then any of these capabilities that are enabled in the permitted set
+ * are enabled in the effective set.
+ */
+static int setfsugid(int uid, int gid)
+{
+    setfsgid(gid);
+    setfsuid(uid);
+    return cap_set();
+}
+
+/*
+ * create a file and send fd on success
+ * return -errno on error
+ */
+static int do_create(struct iovec *iovec)
+{
+    V9fsString path;
+    int flags, fd, mode, uid, gid, cur_uid, cur_gid;
+    proxy_unmarshal(iovec, 1, HDR_SZ, "sdddd",
+                   &path, &flags, &mode, &uid, &gid);
+    cur_uid = geteuid();
+    cur_gid = getegid();
+    if (setfsugid(uid, gid) < 0) {
+        return -EPERM;
+    }
+    fd = open(path.data, flags, mode);
+    if (fd < 0) {
+        fd = -errno;
+    }
+    v9fs_string_free(&path);
+    setfsugid(cur_uid, cur_gid);
+    return fd;
+}
+
+/*
+ * open a file and send fd on success
+ * return -errno on error
+ */
+static int do_open(struct iovec *iovec)
+{
+    V9fsString path;
+    int flags, fd;
+    proxy_unmarshal(iovec, 1, HDR_SZ, "sd", &path, &flags);
+    fd = open(path.data, flags);
+    if (fd < 0) {
+        fd = -errno;
+    }
+    v9fs_string_free(&path);
+    return fd;
+}
+
 static void usage(char *prog)
 {
     fprintf(stderr, "usage: %s\n"
@@ -173,15 +280,34 @@  static void usage(char *prog)
 
 static int process_requests(int sock)
 {
-    int type;
+    int type, retval = 0;
     struct iovec iovec;
 
     iovec.iov_base = g_malloc(BUFF_SZ);
     iovec.iov_len = BUFF_SZ;
     while (1) {
         type = read_request(sock, &iovec);
-        if (type <= 0) {
+        switch (type) {
+        case T_OPEN:
+            retval = do_open(&iovec);
+            break;
+        case T_CREATE:
+            retval = do_create(&iovec);
+            break;
+        default:
+            goto error;
+            break;
+        }
+
+        /* Send response */
+        switch (type) {
+        case T_OPEN:
+        case T_CREATE:
+            send_fd(sock, retval);
+            break;
+        default:
             goto error;
+            break;
         }
     }
     (void)socket_write;
diff --git a/hw/9pfs/virtio-9p-proxy.c b/hw/9pfs/virtio-9p-proxy.c
index 0e539e3..8cc55d6 100644
--- a/hw/9pfs/virtio-9p-proxy.c
+++ b/hw/9pfs/virtio-9p-proxy.c
@@ -22,6 +22,144 @@  typedef struct V9fsProxy {
     struct iovec iovec;
 } V9fsProxy;
 
+/*
+ * Return received file descriptor on success and -errno on failure.
+ * sock_error is set to 1 whenever there is error in socket IO
+ */
+static int v9fs_receivefd(int sockfd, int *sock_error)
+{
+    struct msghdr msg = { };
+    struct iovec iov;
+    union MsgControl msg_control;
+    struct cmsghdr *cmsg;
+    int retval, data, fd;
+
+    iov.iov_base = &data;
+    iov.iov_len = sizeof(data);
+
+    *sock_error = 0;
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = &msg_control;
+    msg.msg_controllen = sizeof(msg_control);
+
+    do {
+        retval = recvmsg(sockfd, &msg, 0);
+    } while (retval < 0 && errno == EINTR);
+    if (retval <= 0) {
+        *sock_error = 1;
+        return -EIO;
+    }
+
+    /*
+     * data is set to V9FS_FD_VALID, if ancillary data is sent.  If this
+     * request doesn't need ancillary data (fd) or an error occurred,
+     * data is set to negative errno value.
+     */
+    if (data != V9FS_FD_VALID) {
+        return data;
+    }
+
+    /*
+     * File descriptor (fd) is sent in the ancillary data. Check if we
+     * indeed received it. One of the reasons to fail to receive it is if
+     * we exceeded the maximum number of file descriptors!
+     */
+    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+        if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
+            cmsg->cmsg_level != SOL_SOCKET ||
+            cmsg->cmsg_type != SCM_RIGHTS) {
+            continue;
+        }
+        fd = *((int *)CMSG_DATA(cmsg));
+        return fd;
+    }
+
+    return -ENFILE; /* Ancillary data sent but not received */
+}
+
+/*
+ * Proxy->header and proxy->request written to socket by QEMU process.
+ * This request read by proxy helper process
+ * returns 0 on success and -errno on error
+ */
+static int v9fs_request(V9fsProxy *proxy, int type,
+                        void *response, const char *fmt, ...)
+{
+    int retval;
+    ProxyHeader header;
+    va_list ap;
+    V9fsString *path;
+    int sock_error, flags, mode, uid, gid;
+    struct iovec *iovec = NULL;
+
+    qemu_mutex_lock(&proxy->mutex);
+
+    if (proxy->sockfd == -1) {
+        goto error;
+    }
+    iovec = &proxy->iovec;
+
+    va_start(ap, fmt);
+    switch (type) {
+    case T_OPEN:
+        path = va_arg(ap, V9fsString *);
+        flags = va_arg(ap, int);
+        header.size = proxy_marshal(iovec, 1, HDR_SZ,
+                                   "sd", path, flags);
+        header.type = T_OPEN;
+        proxy_marshal(iovec, 1, 0, "dd", header.type, header.size);
+        header.size += HDR_SZ;
+        break;
+    case T_CREATE:
+        path = va_arg(ap, V9fsString *);
+        flags = va_arg(ap, int);
+        mode = va_arg(ap, int);
+        uid = va_arg(ap, int);
+        gid = va_arg(ap, int);
+        header.size = proxy_marshal(iovec, 1, HDR_SZ, "sdddd", path,
+                                   flags, mode, uid, gid);
+        header.type = T_CREATE;
+        proxy_marshal(iovec, 1, 0, "dd", header.type, header.size);
+        header.size += HDR_SZ;
+        break;
+    default:
+        error_report("Invalid type %d\n", type);
+        va_end(ap);
+        goto close_error;
+        break;
+    }
+    va_end(ap);
+
+    retval = qemu_write_full(proxy->sockfd, iovec->iov_base, header.size);
+    if (retval != header.size) {
+        goto close_error;
+    }
+
+    switch (type) {
+    case T_OPEN:
+    case T_CREATE:
+        /*
+         * A file descriptor is returned as response for
+         * T_OPEN,T_CREATE on success
+         */
+        retval = v9fs_receivefd(proxy->sockfd, &sock_error);
+        if (sock_error) {
+            goto close_error;
+        }
+        break;
+    }
+    qemu_mutex_unlock(&proxy->mutex);
+    return retval;
+close_error:
+    close(proxy->sockfd);
+    proxy->sockfd = -1;
+error:
+    qemu_mutex_unlock(&proxy->mutex);
+    return -EIO;
+}
+
 static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
 {
     errno = EOPNOTSUPP;
@@ -48,16 +186,35 @@  static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs)
 static int proxy_open(FsContext *ctx, V9fsPath *fs_path,
                       int flags, V9fsFidOpenState *fs)
 {
-    fs->fd = -1;
+    fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd",
+                          fs_path, flags);
+    if (fs->fd < 0) {
+        errno = -fs->fd;
+        fs->fd = -1;
+    }
     return fs->fd;
 }
 
 static int proxy_opendir(FsContext *ctx,
                          V9fsPath *fs_path, V9fsFidOpenState *fs)
 {
+    int serrno, fd;
+
     fs->dir = NULL;
-    errno = EOPNOTSUPP;
-    return -1;
+    fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd",
+                      fs_path, O_DIRECTORY);
+    if (fd < 0) {
+        errno = -fd;
+        return -1;
+    }
+    fs->dir = fdopendir(fd);
+    if (!fs->dir) {
+        serrno = errno;
+        close(fd);
+        errno = serrno;
+        return -1;
+    }
+    return 0;
 }
 
 static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
@@ -159,9 +316,20 @@  static int proxy_fstat(FsContext *fs_ctx,
 static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
                        int flags, FsCred *credp, V9fsFidOpenState *fs)
 {
-    fs->fd = -1;
-    errno = EOPNOTSUPP;
-    return -1;
+    V9fsString fullname;
+
+    v9fs_string_init(&fullname);
+    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+
+    fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, "sdddd",
+                          &fullname, flags, credp->fc_mode,
+                          credp->fc_uid, credp->fc_gid);
+    v9fs_string_free(&fullname);
+    if (fs->fd < 0) {
+        errno = -fs->fd;
+        fs->fd = -1;
+    }
+    return fs->fd;
 }
 
 
diff --git a/hw/9pfs/virtio-9p-proxy.h b/hw/9pfs/virtio-9p-proxy.h
index 120e940..f30e367 100644
--- a/hw/9pfs/virtio-9p-proxy.h
+++ b/hw/9pfs/virtio-9p-proxy.h
@@ -2,6 +2,7 @@ 
 #define _QEMU_VIRTIO_9P_PROXY_H
 
 #define BUFF_SZ (4 * 1024)
+#define V9FS_FD_VALID INT_MAX
 
 #define proxy_unmarshal(in_sg, in_elem, offset, fmt, args...) \
     v9fs_unmarshal(in_sg, in_elem, offset, 0 /* convert */, fmt, ##args)
@@ -17,4 +18,12 @@  typedef struct {
     int type;
     int size;
 } ProxyHeader;
+
+#define HDR_SZ (sizeof(ProxyHeader))
+
+enum {
+    T_OPEN = 1,
+    T_CREATE,
+};
+
 #endif