Skip to content
Snippets Groups Projects
block.c 242 KiB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
/*
 * QEMU System Emulator block driver
Fabrice Bellard's avatar
Fabrice Bellard committed
 * Copyright (c) 2003 Fabrice Bellard
 * Copyright (c) 2020 Virtuozzo International GmbH.
Fabrice Bellard's avatar
Fabrice Bellard committed
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
Peter Maydell's avatar
Peter Maydell committed
#include "qemu/osdep.h"
#include "block/block_int.h"
#include "block/blockjob.h"
#include "block/fuse.h"
#include "block/nbd.h"
#include "block/qdict.h"
#include "qemu/error-report.h"
#include "block/module_block.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qnull.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qobject-output-visitor.h"
#include "qapi/qapi-visit-block-core.h"
#include "sysemu/block-backend.h"
#include "qemu/notify.h"
#include "qemu/coroutine.h"
#include "qemu/timer.h"
#include "qemu/cutils.h"
#include "qemu/id.h"
#include "qemu/range.h"
#include "qemu/rcu.h"
Fabrice Bellard's avatar
Fabrice Bellard committed

#ifdef CONFIG_BSD
#include <sys/ioctl.h>
#include <sys/queue.h>
#if defined(HAVE_SYS_DISK_H)
#include <sys/disk.h>
#endif
#ifdef _WIN32
#include <windows.h>
#endif

#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */

static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states =
    QTAILQ_HEAD_INITIALIZER(graph_bdrv_states);

static QTAILQ_HEAD(, BlockDriverState) all_bdrv_states =
    QTAILQ_HEAD_INITIALIZER(all_bdrv_states);

static QLIST_HEAD(, BlockDriver) bdrv_drivers =
    QLIST_HEAD_INITIALIZER(bdrv_drivers);
static BlockDriverState *bdrv_open_inherit(const char *filename,
                                           const char *reference,
                                           QDict *options, int flags,
                                           BlockDriverState *parent,
                                           const BdrvChildClass *child_class,
                                           BdrvChildRole child_role,
                                           Error **errp);
static bool bdrv_recurse_has_child(BlockDriverState *bs,
                                   BlockDriverState *child);

static void bdrv_child_free(BdrvChild *child);
static void bdrv_replace_child_noperm(BdrvChild **child,
                                      BlockDriverState *new_bs,
                                      bool free_empty_child);
static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
                                              BdrvChild *child,
                                              Transaction *tran);
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
                                            Transaction *tran);
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
                               BlockReopenQueue *queue,
                               Transaction *change_child_tran, Error **errp);
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
static void bdrv_reopen_abort(BDRVReopenState *reopen_state);

static bool bdrv_backing_overridden(BlockDriverState *bs);

/* If non-zero, use only whitelisted block drivers */
static int use_bdrv_whitelist;

#ifdef _WIN32
static int is_windows_drive_prefix(const char *filename)
{
    return (((filename[0] >= 'a' && filename[0] <= 'z') ||
             (filename[0] >= 'A' && filename[0] <= 'Z')) &&
            filename[1] == ':');
}

int is_windows_drive(const char *filename)
{
    if (is_windows_drive_prefix(filename) &&
        filename[2] == '\0')
        return 1;
    if (strstart(filename, "\\\\.\\", NULL) ||
        strstart(filename, "//./", NULL))
        return 1;
    return 0;
}
#endif

size_t bdrv_opt_mem_align(BlockDriverState *bs)
{
    if (!bs || !bs->drv) {
        /* page size or 4k (hdd sector size) should be on the safe side */
        return MAX(4096, qemu_real_host_page_size());
size_t bdrv_min_mem_align(BlockDriverState *bs)
{
    if (!bs || !bs->drv) {
        /* page size or 4k (hdd sector size) should be on the safe side */
        return MAX(4096, qemu_real_host_page_size());

    return bs->bl.min_mem_alignment;
}

/* check if the path starts with "<protocol>:" */
int path_has_protocol(const char *path)
#ifdef _WIN32
    if (is_windows_drive(path) ||
        is_windows_drive_prefix(path)) {
        return 0;
    }
    p = path + strcspn(path, ":/\\");
#else
    p = path + strcspn(path, ":/");
Fabrice Bellard's avatar
Fabrice Bellard committed
int path_is_absolute(const char *path)
#ifdef _WIN32
    /* specific case for names like: "\\.\d:" */
    if (is_windows_drive(path) || is_windows_drive_prefix(path)) {
        return 1;
    }
    return (*path == '/' || *path == '\\');
    return (*path == '/');
/* if filename is absolute, just return its duplicate. Otherwise, build a
Fabrice Bellard's avatar
Fabrice Bellard committed
   path to it by considering it is relative to base_path. URL are
   supported. */
char *path_combine(const char *base_path, const char *filename)
    const char *protocol_stripped = NULL;
Fabrice Bellard's avatar
Fabrice Bellard committed
    const char *p, *p1;
Fabrice Bellard's avatar
Fabrice Bellard committed
    int len;

    if (path_is_absolute(filename)) {
        return g_strdup(filename);
    }
    if (path_has_protocol(base_path)) {
        protocol_stripped = strchr(base_path, ':');
        if (protocol_stripped) {
            protocol_stripped++;
    }
    p = protocol_stripped ?: base_path;
    p1 = strrchr(base_path, '/');
    {
        const char *p2;
        p2 = strrchr(base_path, '\\');
        if (!p1 || p2 > p1) {
            p1 = p2;
    if (p1) {
        p1++;
    } else {
        p1 = base_path;
    }
    if (p1 > p) {
        p = p1;
    len = p - base_path;

    result = g_malloc(len + strlen(filename) + 1);
    memcpy(result, base_path, len);
    strcpy(result + len, filename);

    return result;
}

/*
 * Helper function for bdrv_parse_filename() implementations to remove optional
 * protocol prefixes (especially "file:") from a filename and for putting the
 * stripped filename into the options QDict if there is such a prefix.
 */
void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix,
                                      QDict *options)
{
    if (strstart(filename, prefix, &filename)) {
        /* Stripping the explicit protocol prefix may result in a protocol
         * prefix being (wrongly) detected (if the filename contains a colon) */
        if (path_has_protocol(filename)) {

            /* This means there is some colon before the first slash; therefore,
             * this cannot be an absolute path */
            assert(!path_is_absolute(filename));

            /* And we can thus fix the protocol detection issue by prefixing it
             * by "./" */
            fat_filename = g_string_new("./");
            g_string_append(fat_filename, filename);
            assert(!path_has_protocol(fat_filename->str));
            qdict_put(options, "filename",
                      qstring_from_gstring(fat_filename));
        } else {
            /* If no protocol prefix was detected, we can use the shortened
             * filename as-is */
            qdict_put_str(options, "filename", filename);
        }
    }
}


/* Returns whether the image file is opened as read-only. Note that this can
 * return false and writing to the image file is still not possible because the
 * image is inactivated. */
Jeff Cody's avatar
Jeff Cody committed
bool bdrv_is_read_only(BlockDriverState *bs)
{
    return !(bs->open_flags & BDRV_O_RDWR);
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
                           bool ignore_allow_rdw, Error **errp)
    /* Do not set read_only if copy_on_read is enabled */
    if (bs->copy_on_read && read_only) {
        error_setg(errp, "Can't set node '%s' to r/o with copy-on-read enabled",
                   bdrv_get_device_or_node_name(bs));
        return -EINVAL;
    }

    /* Do not clear read_only if it is prohibited */
    if (!read_only && !(bs->open_flags & BDRV_O_ALLOW_RDWR) &&
        !ignore_allow_rdw)
    {
        error_setg(errp, "Node '%s' is read only",
                   bdrv_get_device_or_node_name(bs));
        return -EPERM;
    }

/*
 * Called by a driver that can only provide a read-only image.
 *
 * Returns 0 if the node is already read-only or it could switch the node to
 * read-only because BDRV_O_AUTO_RDONLY is set.
 *
 * Returns -EACCES if the node is read-write and BDRV_O_AUTO_RDONLY is not set
 * or bdrv_can_set_read_only() forbids making the node read-only. If @errmsg
 * is not NULL, it is used as the error message for the Error object.
 */
int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg,
                              Error **errp)
    if (!(bs->open_flags & BDRV_O_RDWR)) {
        return 0;
    }
    if (!(bs->open_flags & BDRV_O_AUTO_RDONLY)) {
        goto fail;
    ret = bdrv_can_set_read_only(bs, true, false, NULL);
    if (ret < 0) {
        goto fail;
    bs->open_flags &= ~BDRV_O_RDWR;


fail:
    error_setg(errp, "%s", errmsg ?: "Image is read-only");
    return -EACCES;
/*
 * If @backing is empty, this function returns NULL without setting
 * @errp.  In all other cases, NULL will only be returned with @errp
 * set.
 *
 * Therefore, a return value of NULL without @errp set means that
 * there is no backing file; if @errp is set, there is one but its
 * absolute filename cannot be generated.
 */
char *bdrv_get_full_backing_filename_from_filename(const char *backed,
                                                   const char *backing,
                                                   Error **errp)
    if (backing[0] == '\0') {
        return NULL;
    } else if (path_has_protocol(backing) || path_is_absolute(backing)) {
        return g_strdup(backing);
    } else if (backed[0] == '\0' || strstart(backed, "json:", NULL)) {
        error_setg(errp, "Cannot use relative backing file names for '%s'",
                   backed);
        return path_combine(backed, backing);
/*
 * If @filename is empty or NULL, this function returns NULL without
 * setting @errp.  In all other cases, NULL will only be returned with
 * @errp set.
 */
static char *bdrv_make_absolute_filename(BlockDriverState *relative_to,
                                         const char *filename, Error **errp)
    char *dir, *full_name;
    if (!filename || filename[0] == '\0') {
        return NULL;
    } else if (path_has_protocol(filename) || path_is_absolute(filename)) {
        return g_strdup(filename);
    }
    dir = bdrv_dirname(relative_to, errp);
    if (!dir) {
        return NULL;
    }
    full_name = g_strconcat(dir, filename, NULL);
    g_free(dir);
    return full_name;
}

char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp)
{
    return bdrv_make_absolute_filename(bs, bs->backing_file, errp);
void bdrv_register(BlockDriver *bdrv)
{
    QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list);
Fabrice Bellard's avatar
Fabrice Bellard committed
}
BlockDriverState *bdrv_new(void)
{
    BlockDriverState *bs;
    int i;

    bs = g_new0(BlockDriverState, 1);
    QLIST_INIT(&bs->dirty_bitmaps);
    for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
        QLIST_INIT(&bs->op_blockers[i]);
    }
    qemu_co_mutex_init(&bs->reqs_lock);
    qemu_mutex_init(&bs->dirty_bitmap_mutex);
    bs->aio_context = qemu_get_aio_context();
    qemu_co_queue_init(&bs->flush_queue);

    qemu_co_mutex_init(&bs->bsc_modify_lock);
    bs->block_status_cache = g_new0(BdrvBlockStatusCache, 1);

    for (i = 0; i < bdrv_drain_all_count; i++) {
        bdrv_drained_begin(bs);
    }

    QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list);

Fabrice Bellard's avatar
Fabrice Bellard committed
    return bs;
}

static BlockDriver *bdrv_do_find_format(const char *format_name)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    BlockDriver *drv1;
    QLIST_FOREACH(drv1, &bdrv_drivers, list) {
        if (!strcmp(drv1->format_name, format_name)) {
Fabrice Bellard's avatar
Fabrice Bellard committed
            return drv1;
Fabrice Bellard's avatar
Fabrice Bellard committed
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
    return NULL;
}

BlockDriver *bdrv_find_format(const char *format_name)
{
    BlockDriver *drv1;
    int i;

    drv1 = bdrv_do_find_format(format_name);
    if (drv1) {
        return drv1;
    }

    /* The driver isn't registered, maybe we need to load a module */
    for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) {
        if (!strcmp(block_driver_modules[i].format_name, format_name)) {
            block_module_load_one(block_driver_modules[i].library_name);
            break;
        }
    }

    return bdrv_do_find_format(format_name);
}

static int bdrv_format_is_whitelisted(const char *format_name, bool read_only)
    static const char *whitelist_rw[] = {
        CONFIG_BDRV_RW_WHITELIST
    };
    static const char *whitelist_ro[] = {
        CONFIG_BDRV_RO_WHITELIST
    if (!whitelist_rw[0] && !whitelist_ro[0]) {
        return 1;               /* no whitelist, anything goes */
    for (p = whitelist_rw; *p; p++) {
        if (!strcmp(format_name, *p)) {
    if (read_only) {
        for (p = whitelist_ro; *p; p++) {
            if (!strcmp(format_name, *p)) {
int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
{
    return bdrv_format_is_whitelisted(drv->format_name, read_only);
}

bool bdrv_uses_whitelist(void)
{
    return use_bdrv_whitelist;
}

typedef struct CreateCo {
    BlockDriver *drv;
    char *filename;
} CreateCo;

static void coroutine_fn bdrv_create_co_entry(void *opaque)
{
    Error *local_err = NULL;
    int ret;

    CreateCo *cco = opaque;
    assert(cco->drv);
    ret = cco->drv->bdrv_co_create_opts(cco->drv,
                                        cco->filename, cco->opts, &local_err);
    error_propagate(&cco->err, local_err);
    cco->ret = ret;
int bdrv_create(BlockDriver *drv, const char* filename,
                QemuOpts *opts, Error **errp)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    Coroutine *co;
    CreateCo cco = {
        .drv = drv,
        .filename = g_strdup(filename),
        .ret = NOT_DONE,
    if (!drv->bdrv_co_create_opts) {
        error_setg(errp, "Driver '%s' does not support image creation", drv->format_name);
    }

    if (qemu_in_coroutine()) {
        /* Fast-path if already in coroutine context */
        bdrv_create_co_entry(&cco);
    } else {
        co = qemu_coroutine_create(bdrv_create_co_entry, &cco);
        qemu_coroutine_enter(co);
        while (cco.ret == NOT_DONE) {
            aio_poll(qemu_get_aio_context(), true);
    if (ret < 0) {
            error_propagate(errp, cco.err);
        } else {
            error_setg_errno(errp, -ret, "Could not create image");
        }
    }
    return ret;
/**
 * Helper function for bdrv_create_file_fallback(): Resize @blk to at
 * least the given @minimum_size.
 *
 * On success, return @blk's actual length.
 * Otherwise, return -errno.
 */
static int64_t create_file_fallback_truncate(BlockBackend *blk,
                                             int64_t minimum_size, Error **errp)
    Error *local_err = NULL;
    int64_t size;
    ret = blk_truncate(blk, minimum_size, false, PREALLOC_MODE_OFF, 0,
                       &local_err);
    if (ret < 0 && ret != -ENOTSUP) {
        error_propagate(errp, local_err);
        return ret;
    }

    size = blk_getlength(blk);
    if (size < 0) {
        error_free(local_err);
        error_setg_errno(errp, -size,
                         "Failed to inquire the new image file's length");
        return size;
    }

    if (size < minimum_size) {
        /* Need to grow the image, but we failed to do that */
        error_propagate(errp, local_err);
        return -ENOTSUP;
    }

    error_free(local_err);
    local_err = NULL;

    return size;
}

/**
 * Helper function for bdrv_create_file_fallback(): Zero the first
 * sector to remove any potentially pre-existing image header.
 */
static int create_file_fallback_zero_first_sector(BlockBackend *blk,
                                                  int64_t current_size,
                                                  Error **errp)
{
    int64_t bytes_to_clear;
    int ret;

    bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE);
    if (bytes_to_clear) {
        ret = blk_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP);
        if (ret < 0) {
            error_setg_errno(errp, -ret,
                             "Failed to clear the new image's first sector");
            return ret;
        }
    }

    return 0;
}

/**
 * Simple implementation of bdrv_co_create_opts for protocol drivers
 * which only support creation via opening a file
 * (usually existing raw storage device)
 */
int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv,
                                            const char *filename,
                                            QemuOpts *opts,
                                            Error **errp)
{
    BlockBackend *blk;
    int64_t size = 0;
    char *buf = NULL;
    PreallocMode prealloc;
    Error *local_err = NULL;
    int ret;

    size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
    buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
    prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
                               PREALLOC_MODE_OFF, &local_err);
    g_free(buf);
    if (local_err) {
        error_propagate(errp, local_err);
        return -EINVAL;
    }

    if (prealloc != PREALLOC_MODE_OFF) {
        error_setg(errp, "Unsupported preallocation mode '%s'",
                   PreallocMode_str(prealloc));
        return -ENOTSUP;
    }

    options = qdict_new();
    qdict_put_str(options, "driver", drv->format_name);

    blk = blk_new_open(filename, NULL, options,
                       BDRV_O_RDWR | BDRV_O_RESIZE, errp);
    if (!blk) {
        error_prepend(errp, "Protocol driver '%s' does not support image "
                      "creation, and opening the image failed: ",
                      drv->format_name);
        return -EINVAL;
    }

    size = create_file_fallback_truncate(blk, size, errp);
    if (size < 0) {
        ret = size;
        goto out;
    }

    ret = create_file_fallback_zero_first_sector(blk, size, errp);
    if (ret < 0) {
        goto out;
    }

    ret = 0;
out:
    blk_unref(blk);
    return ret;
}

int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
{
    BlockDriver *drv;
    drv = bdrv_find_protocol(filename, true, errp);
    if (!drv->create_opts) {
        error_setg(errp, "Driver '%s' does not support image creation",
                   drv->format_name);
        return -ENOTSUP;
    }

    /*
     * 'opts' contains a QemuOptsList with a combination of format and protocol
     * default values.
     *
     * The format properly removes its options, but the default values remain
     * in 'opts->list'.  So if the protocol has options with the same name
     * (e.g. rbd has 'cluster_size' as qcow2), it will see the default values
     * of the format, since for overlapping options, the format wins.
     *
     * To avoid this issue, lets convert QemuOpts to QDict, in this way we take
     * only the set options, and then convert it back to QemuOpts, using the
     * create_opts of the protocol. So the new QemuOpts, will contain only the
     * protocol defaults.
     */
    qdict = qemu_opts_to_qdict(opts, NULL);
    protocol_opts = qemu_opts_from_qdict(drv->create_opts, qdict, errp);
    if (protocol_opts == NULL) {
        ret = -EINVAL;
        goto out;
    }

    ret = bdrv_create(drv, filename, protocol_opts, errp);
out:
    qemu_opts_del(protocol_opts);
    qobject_unref(qdict);
    return ret;
int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp)
{
    Error *local_err = NULL;
    int ret;

    assert(bs != NULL);

    if (!bs->drv) {
        error_setg(errp, "Block node '%s' is not opened", bs->filename);
        return -ENOMEDIUM;
    }

    if (!bs->drv->bdrv_co_delete_file) {
        error_setg(errp, "Driver '%s' does not support image deletion",
                   bs->drv->format_name);
        return -ENOTSUP;
    }

    ret = bs->drv->bdrv_co_delete_file(bs, &local_err);
    if (ret < 0) {
        error_propagate(errp, local_err);
    }

    return ret;
}

void coroutine_fn bdrv_co_delete_file_noerr(BlockDriverState *bs)
{
    Error *local_err = NULL;
    int ret;

    if (!bs) {
        return;
    }

    ret = bdrv_co_delete_file(bs, &local_err);
    /*
     * ENOTSUP will happen if the block driver doesn't support
     * the 'bdrv_co_delete_file' interface. This is a predictable
     * scenario and shouldn't be reported back to the user.
     */
    if (ret == -ENOTSUP) {
        error_free(local_err);
    } else if (ret < 0) {
        error_report_err(local_err);
    }
}

/**
 * Try to get @bs's logical and physical block size.
 * On success, store them in @bsz struct and return 0.
 * On failure return -errno.
 * @bs must not be empty.
 */
int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
{
    BlockDriver *drv = bs->drv;
    BlockDriverState *filtered = bdrv_filter_bs(bs);

    if (drv && drv->bdrv_probe_blocksizes) {
        return drv->bdrv_probe_blocksizes(bs, bsz);
    } else if (filtered) {
        return bdrv_probe_blocksizes(filtered, bsz);
    }

    return -ENOTSUP;
}

/**
 * Try to get @bs's geometry (cyls, heads, sectors).
 * On success, store them in @geo struct and return 0.
 * On failure return -errno.
 * @bs must not be empty.
 */
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
{
    BlockDriver *drv = bs->drv;
    BlockDriverState *filtered = bdrv_filter_bs(bs);

    if (drv && drv->bdrv_probe_geometry) {
        return drv->bdrv_probe_geometry(bs, geo);
    } else if (filtered) {
        return bdrv_probe_geometry(filtered, geo);
/*
 * Create a uniquely-named empty temporary file.
 * Return 0 upon success, otherwise a negative errno value.
 */
int get_tmp_filename(char *filename, int size)
    char temp_dir[MAX_PATH];
    /* GetTempFileName requires that its output buffer (4th param)
       have length MAX_PATH or greater.  */
    assert(size >= MAX_PATH);
    return (GetTempPath(MAX_PATH, temp_dir)
            && GetTempFileName(temp_dir, "qem", 0, filename)
            ? 0 : -GetLastError());
#else
    tmpdir = getenv("TMPDIR");
    if (!tmpdir) {
        tmpdir = "/var/tmp";
    }
    if (snprintf(filename, size, "%s/vl.XXXXXX", tmpdir) >= size) {
        return -EOVERFLOW;
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
    fd = mkstemp(filename);
    if (fd < 0) {
        return -errno;
    }
    if (close(fd) != 0) {
        unlink(filename);
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed

/*
 * Detect host devices. By convention, /dev/cdrom[N] is always
 * recognized as a host CDROM.
 */
static BlockDriver *find_hdev_driver(const char *filename)
{
    int score_max = 0, score;
    BlockDriver *drv = NULL, *d;

    QLIST_FOREACH(d, &bdrv_drivers, list) {
        if (d->bdrv_probe_device) {
            score = d->bdrv_probe_device(filename);
            if (score > score_max) {
                score_max = score;
                drv = d;
            }
        }
    }

    return drv;
}

static BlockDriver *bdrv_do_find_protocol(const char *protocol)
{
    BlockDriver *drv1;

    QLIST_FOREACH(drv1, &bdrv_drivers, list) {
        if (drv1->protocol_name && !strcmp(drv1->protocol_name, protocol)) {
            return drv1;
        }
    }

    return NULL;
}

BlockDriver *bdrv_find_protocol(const char *filename,
                                bool allow_protocol_prefix,
                                Error **errp)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    BlockDriver *drv1;
    char protocol[128];
Fabrice Bellard's avatar
Fabrice Bellard committed
    const char *p;
    /* TODO Drivers without bdrv_file_open must be specified explicitly */

    /*
     * XXX(hch): we really should not let host device detection
     * override an explicit protocol specification, but moving this
     * later breaks access to device names with colons in them.
     * Thanks to the brain-dead persistent naming schemes on udev-
     * based Linux systems those actually are quite common.
     */
    drv1 = find_hdev_driver(filename);
    if (drv1) {
        return drv1;
    }

    if (!path_has_protocol(filename) || !allow_protocol_prefix) {
    p = strchr(filename, ':');
    assert(p != NULL);
    len = p - filename;
    if (len > sizeof(protocol) - 1)
        len = sizeof(protocol) - 1;
    memcpy(protocol, filename, len);
    protocol[len] = '\0';

    drv1 = bdrv_do_find_protocol(protocol);
    if (drv1) {
        return drv1;
    }

    for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) {
        if (block_driver_modules[i].protocol_name &&
            !strcmp(block_driver_modules[i].protocol_name, protocol)) {
            block_module_load_one(block_driver_modules[i].library_name);
            break;
Fabrice Bellard's avatar
Fabrice Bellard committed
    }
    drv1 = bdrv_do_find_protocol(protocol);
    if (!drv1) {
        error_setg(errp, "Unknown protocol '%s'", protocol);
    }
    return drv1;
/*
 * Guess image format by probing its contents.
 * This is not a good idea when your image is raw (CVE-2008-2004), but
 * we do it anyway for backward compatibility.
 *
 * @buf         contains the image's first @buf_size bytes.
 * @buf_size    is the buffer size in bytes (generally BLOCK_PROBE_BUF_SIZE,
 *              but can be smaller if the image file is smaller)