Commit adb696f3 authored by Corey Bryant's avatar Corey Bryant Committed by Kevin Wolf
Browse files

block: Enable qemu_open/close to work with fd sets



When qemu_open is passed a filename of the "/dev/fdset/nnn"
format (where nnn is the fdset ID), an fd with matching access
mode flags will be searched for within the specified monitor
fd set.  If the fd is found, a dup of the fd will be returned
from qemu_open.

Signed-off-by: default avatarCorey Bryant <coreyb@linux.vnet.ibm.com>
Signed-off-by: default avatarKevin Wolf <kwolf@redhat.com>
parent 2e1e79da
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -148,9 +148,6 @@ install-libcacard: libcacard.la
	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libcacard V="$(V)" TARGET_DIR="$*/" install-libcacard,)
endif

vscclient$(EXESUF): $(libcacard-y) $(oslib-obj-y) $(trace-obj-y) qemu-timer-common.o libcacard/vscclient.o
	$(call quiet-command,$(CC) $(LDFLAGS) -o $@ $^ $(libcacard_libs) $(LIBS),"  LINK  $@")

######################################################################

qemu-img.o: qemu-img-cmds.h
@@ -166,6 +163,9 @@ qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y)

qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o

vscclient$(EXESUF): $(libcacard-y) $(oslib-obj-y) $(trace-obj-y) $(tools-obj-y) qemu-timer-common.o libcacard/vscclient.o
	$(call quiet-command,$(CC) $(LDFLAGS) -o $@ $^ $(libcacard_libs) $(LIBS),"  LINK  $@")

fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o oslib-posix.o $(trace-obj-y)
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap

+5 −0
Original line number Diff line number Diff line
@@ -383,6 +383,11 @@ int qemu_parse_fd(const char *param)
    return fd;
}

int qemu_parse_fdset(const char *param)
{
    return qemu_parse_fd(param);
}

/* round down to the nearest power of 2*/
int64_t pow2floor(int64_t value)
{
+85 −1
Original line number Diff line number Diff line
@@ -154,6 +154,7 @@ typedef struct MonFdset MonFdset;
struct MonFdset {
    int64_t id;
    QLIST_HEAD(, MonFdsetFd) fds;
    QLIST_HEAD(, MonFdsetFd) dup_fds;
    QLIST_ENTRY(MonFdset) next;
};

@@ -2398,7 +2399,7 @@ static void monitor_fdset_cleanup(MonFdset *mon_fdset)
        }
    }

    if (QLIST_EMPTY(&mon_fdset->fds)) {
    if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) {
        QLIST_REMOVE(mon_fdset, next);
        g_free(mon_fdset);
    }
@@ -2555,6 +2556,89 @@ FdsetInfoList *qmp_query_fdsets(Error **errp)
    return fdset_list;
}

int monitor_fdset_get_fd(int64_t fdset_id, int flags)
{
    MonFdset *mon_fdset;
    MonFdsetFd *mon_fdset_fd;
    int mon_fd_flags;

#ifndef _WIN32
    QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
        if (mon_fdset->id != fdset_id) {
            continue;
        }
        QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
            mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL);
            if (mon_fd_flags == -1) {
                return -1;
            }

            if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) {
                return mon_fdset_fd->fd;
            }
        }
        errno = EACCES;
        return -1;
    }
#endif

    errno = ENOENT;
    return -1;
}

int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd)
{
    MonFdset *mon_fdset;
    MonFdsetFd *mon_fdset_fd_dup;

    QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
        if (mon_fdset->id != fdset_id) {
            continue;
        }
        QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) {
            if (mon_fdset_fd_dup->fd == dup_fd) {
                return -1;
            }
        }
        mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup));
        mon_fdset_fd_dup->fd = dup_fd;
        QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next);
        return 0;
    }
    return -1;
}

static int monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove)
{
    MonFdset *mon_fdset;
    MonFdsetFd *mon_fdset_fd_dup;

    QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
        QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) {
            if (mon_fdset_fd_dup->fd == dup_fd) {
                if (remove) {
                    QLIST_REMOVE(mon_fdset_fd_dup, next);
                    if (QLIST_EMPTY(&mon_fdset->dup_fds)) {
                        monitor_fdset_cleanup(mon_fdset);
                    }
                }
                return mon_fdset->id;
            }
        }
    }
    return -1;
}

int monitor_fdset_dup_fd_find(int dup_fd)
{
    return monitor_fdset_dup_fd_find_remove(dup_fd, false);
}

int monitor_fdset_dup_fd_remove(int dup_fd)
{
    return monitor_fdset_dup_fd_find_remove(dup_fd, true);
}

/* mon_cmds and info_cmds would be sorted at runtime */
static mon_cmd_t mon_cmds[] = {
#include "hmp-commands.h"
+5 −0
Original line number Diff line number Diff line
@@ -87,4 +87,9 @@ int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret);

int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret);

int monitor_fdset_get_fd(int64_t fdset_id, int flags);
int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd);
int monitor_fdset_dup_fd_remove(int dup_fd);
int monitor_fdset_dup_fd_find(int dup_fd);

#endif /* !MONITOR_H */
+111 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ extern int madvise(caddr_t, size_t, int);
#include "qemu-common.h"
#include "trace.h"
#include "qemu_socket.h"
#include "monitor.h"

static bool fips_enabled = false;

@@ -78,6 +79,66 @@ int qemu_madvise(void *addr, size_t len, int advice)
#endif
}

#ifndef _WIN32
/*
 * Dups an fd and sets the flags
 */
static int qemu_dup_flags(int fd, int flags)
{
    int ret;
    int serrno;
    int dup_flags;
    int setfl_flags;

#ifdef F_DUPFD_CLOEXEC
    ret = fcntl(fd, F_DUPFD_CLOEXEC, 0);
#else
    ret = dup(fd);
    if (ret != -1) {
        qemu_set_cloexec(ret);
    }
#endif
    if (ret == -1) {
        goto fail;
    }

    dup_flags = fcntl(ret, F_GETFL);
    if (dup_flags == -1) {
        goto fail;
    }

    if ((flags & O_SYNC) != (dup_flags & O_SYNC)) {
        errno = EINVAL;
        goto fail;
    }

    /* Set/unset flags that we can with fcntl */
    setfl_flags = O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NONBLOCK;
    dup_flags &= ~setfl_flags;
    dup_flags |= (flags & setfl_flags);
    if (fcntl(ret, F_SETFL, dup_flags) == -1) {
        goto fail;
    }

    /* Truncate the file in the cases that open() would truncate it */
    if (flags & O_TRUNC ||
            ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))) {
        if (ftruncate(ret, 0) == -1) {
            goto fail;
        }
    }

    return ret;

fail:
    serrno = errno;
    if (ret != -1) {
        close(ret);
    }
    errno = serrno;
    return -1;
}
#endif

/*
 * Opens a file with FD_CLOEXEC set
@@ -87,6 +148,41 @@ int qemu_open(const char *name, int flags, ...)
    int ret;
    int mode = 0;

#ifndef _WIN32
    const char *fdset_id_str;

    /* Attempt dup of fd from fd set */
    if (strstart(name, "/dev/fdset/", &fdset_id_str)) {
        int64_t fdset_id;
        int fd, dupfd;

        fdset_id = qemu_parse_fdset(fdset_id_str);
        if (fdset_id == -1) {
            errno = EINVAL;
            return -1;
        }

        fd = monitor_fdset_get_fd(fdset_id, flags);
        if (fd == -1) {
            return -1;
        }

        dupfd = qemu_dup_flags(fd, flags);
        if (dupfd == -1) {
            return -1;
        }

        ret = monitor_fdset_dup_fd_add(fdset_id, dupfd);
        if (ret == -1) {
            close(dupfd);
            errno = EINVAL;
            return -1;
        }

        return dupfd;
    }
#endif

    if (flags & O_CREAT) {
        va_list ap;

@@ -109,6 +205,21 @@ int qemu_open(const char *name, int flags, ...)

int qemu_close(int fd)
{
    int64_t fdset_id;

    /* Close fd that was dup'd from an fdset */
    fdset_id = monitor_fdset_dup_fd_find(fd);
    if (fdset_id != -1) {
        int ret;

        ret = close(fd);
        if (ret == 0) {
            monitor_fdset_dup_fd_remove(fd);
        }

        return ret;
    }

    return close(fd);
}

Loading