Commit 23dece19 authored by Kevin Wolf's avatar Kevin Wolf
Browse files

file-posix: Make auto-read-only dynamic



Until now, with auto-read-only=on we tried to open the file read-write
first and if that failed, read-only was tried. This is actually not good
enough for libvirt, which gives QEMU SELinux permissions for read-write
only as soon as it actually intends to write to the image. So we need to
be able to switch between read-only and read-write at runtime.

This patch makes auto-read-only dynamic, i.e. the file is opened
read-only as long as no user of the node has requested write
permissions, but it is automatically reopened read-write as soon as the
first writer is attached. Conversely, if the last writer goes away, the
file is reopened read-only again.

bs->read_only is no longer set for auto-read-only=on files even if the
file descriptor is opened read-only because it will be transparently
upgraded as soon as a writer is attached. This changes the output of
qemu-iotests 232.

Signed-off-by: default avatarKevin Wolf <kwolf@redhat.com>
parent 6ceabe6f
Loading
Loading
Loading
Loading
+17 −19
Original line number Diff line number Diff line
@@ -376,13 +376,21 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp)
    }
}

static void raw_parse_flags(int bdrv_flags, int *open_flags)
static void raw_parse_flags(int bdrv_flags, int *open_flags, bool has_writers)
{
    bool read_write = false;
    assert(open_flags != NULL);

    *open_flags |= O_BINARY;
    *open_flags &= ~O_ACCMODE;
    if (bdrv_flags & BDRV_O_RDWR) {

    if (bdrv_flags & BDRV_O_AUTO_RDONLY) {
        read_write = has_writers;
    } else if (bdrv_flags & BDRV_O_RDWR) {
        read_write = true;
    }

    if (read_write) {
        *open_flags |= O_RDWR;
    } else {
        *open_flags |= O_RDONLY;
@@ -518,24 +526,12 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
                                               false);

    s->open_flags = open_flags;
    raw_parse_flags(bdrv_flags, &s->open_flags);
    raw_parse_flags(bdrv_flags, &s->open_flags, false);

    s->fd = -1;
    fd = qemu_open(filename, s->open_flags, 0644);
    ret = fd < 0 ? -errno : 0;

    if (ret == -EACCES || ret == -EROFS) {
        /* Try to degrade to read-only, but if it doesn't work, still use the
         * normal error message. */
        if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
            bdrv_flags &= ~BDRV_O_RDWR;
            raw_parse_flags(bdrv_flags, &s->open_flags);
            assert(!(s->open_flags & O_CREAT));
            fd = qemu_open(filename, s->open_flags);
            ret = fd < 0 ? -errno : 0;
        }
    }

    if (ret < 0) {
        error_setg_errno(errp, -ret, "Could not open '%s'", filename);
        if (ret == -EROFS) {
@@ -846,12 +842,14 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
}

static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
                                 int *open_flags, bool force_dup,
                                 int *open_flags, uint64_t perm, bool force_dup,
                                 Error **errp)
{
    BDRVRawState *s = bs->opaque;
    int fd = -1;
    int ret;
    bool has_writers = perm &
        (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_RESIZE);
    int fcntl_flags = O_APPEND | O_NONBLOCK;
#ifdef O_NOATIME
    fcntl_flags |= O_NOATIME;
@@ -862,7 +860,7 @@ static int raw_reconfigure_getfd(BlockDriverState *bs, int flags,
        *open_flags |= O_NONBLOCK;
    }

    raw_parse_flags(flags, open_flags);
    raw_parse_flags(flags, open_flags, has_writers);

#ifdef O_ASYNC
    /* Not all operating systems have O_ASYNC, and those that don't
@@ -942,7 +940,7 @@ static int raw_reopen_prepare(BDRVReopenState *state,
    qemu_opts_to_qdict(opts, state->options);

    rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
                                   true, &local_err);
                                   state->perm, true, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        ret = -1;
@@ -2720,7 +2718,7 @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
        s->perm_change_fd = rs->fd;
    } else {
        /* We may need a new fd if auto-read-only switches the mode */
        ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags,
        ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, perm,
                                    false, errp);
        if (ret < 0) {
            return ret;
+6 −6
Original line number Diff line number Diff line
@@ -22,12 +22,12 @@ NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)

QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
NODE_NAME: TEST_DIR/t.IMGFMT (file)
NODE_NAME: TEST_DIR/t.IMGFMT (file)

QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
NODE_NAME: TEST_DIR/t.IMGFMT (file)
NODE_NAME: TEST_DIR/t.IMGFMT (file)

=== -blockdev with read-write image: read-only/auto-read-only combinations ===

@@ -50,11 +50,11 @@ node0: TEST_DIR/t.IMGFMT (file, read-only)
node0: TEST_DIR/t.IMGFMT (file, read-only)

QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
node0: TEST_DIR/t.IMGFMT (file, read-only)
node0: TEST_DIR/t.IMGFMT (file)
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied

QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
node0: TEST_DIR/t.IMGFMT (file, read-only)
node0: TEST_DIR/t.IMGFMT (file)
QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied

=== Try commit to backing file with auto-read-only ===