Block layer patches:
- Support for FUSE exports - Fix deadlock in bdrv_co_yield_to_drain() - Use lock guard macros - Some preparational patches for 64 bit block layer - file-posix: Fix request extension to INT64_MAX in raw_do_pwrite_zeroes() -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAl/TpwsRHGt3b2xmQHJl ZGhhdC5jb20ACgkQfwmycsiPL9alQxAAxBbtKSakFvRLRsJ/zoKSGF/8r/O7CSJ3 zvD3LetdRRn98y2rLIKde/82kMmdjF62+KAoz5QboOf/3FO+1QJfSDO16hDR4rDQ iXLoJoMULNsugU0fJzJSbPt0CQ1din+zrr96VZcP5wkYEzRx3l7ANq0ZmD42C7rN FNc4r7FTB3JuCb1QHS19Ql1XTJh50zWoO4CluQbxTv23YX5RrBoN0xCbmq67PCAs nuJiA5D1J907nlh+xzfM2qOpuILfNULoch+dT0f0rYrmpANHzJX+VmjsTwQvkDdz 7CdVQsyXd1YmU0JMM41acQAe3WCxmIps2WaFRiVFRVrGWo29NJourb5OEYY7XN0T DEKwLbQ9nXPLN6FnedwMBZ9r4FNThsu5tdjbW+MEw8xSukxfnUjLYiBKHroRckDv 7n5gBu9T9x3DI+SpFo1YVhdeS+W3eHtZeyYoP2nahuTbMjhRCYwpqEtrnMldAcFa mYea9lqDeuAEPo5XBkQK7XFT8gpJp9CByr0jPkf/ZRa0TJhgA0TLmEdEEDScdk6M RMkhvfcyOxVj30rw8yy/BjZ+boJ9eVAHkUUfbB0ZD9pcrYwMzEgw++aC1hHUGzxB B1XPWk1ueOtGXPKG+6fvwVv9yrqW55eJ/8DSwjos+bvhzX6yGitpBiEFiHJIpcjY e17e7M3ocf4= =ISxg -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block layer patches: - Support for FUSE exports - Fix deadlock in bdrv_co_yield_to_drain() - Use lock guard macros - Some preparational patches for 64 bit block layer - file-posix: Fix request extension to INT64_MAX in raw_do_pwrite_zeroes() # gpg: Signature made Fri 11 Dec 2020 17:06:19 GMT # gpg: using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6 # gpg: issuer "kwolf@redhat.com" # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full] # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * remotes/kevin/tags/for-upstream: (34 commits) block: Fix deadlock in bdrv_co_yield_to_drain() block: Fix locking in qmp_block_resize() block: Simplify qmp_block_resize() error paths block: introduce BDRV_MAX_LENGTH block/io: bdrv_check_byte_request(): drop bdrv_is_inserted() block/io: bdrv_refresh_limits(): use ERRP_GUARD block/file-posix: fix workaround in raw_do_pwrite_zeroes() can-host: Fix crash when 'canbus' property is not set iotests/221: Discard image before qemu-img map file-posix: check the use_lock before setting the file lock iotests/308: Add test for FUSE exports iotests: Enable fuse for many tests iotests: Allow testing FUSE exports iotests: Give access to the qemu-storage-daemon storage-daemon: Call bdrv_close_all() on exit iotests/287: Clean up subshell test image iotests: Let _make_test_img guess $TEST_IMG_FILE iotests: Restrict some Python tests to file iotests/091: Use _cleanup_qemu instad of "wait" iotests: Derive image names from $TEST_IMG ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
ad717e6da3
@ -3115,6 +3115,12 @@ F: include/qemu/vhost-user-server.h
|
||||
F: tests/qtest/libqos/vhost-user-blk.c
|
||||
F: util/vhost-user-server.c
|
||||
|
||||
FUSE block device exports
|
||||
M: Max Reitz <mreitz@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/export/fuse.c
|
||||
|
||||
Replication
|
||||
M: Wen Congyang <wencongyang2@huawei.com>
|
||||
M: Xie Changlong <xiechanglong.d@gmail.com>
|
||||
|
18
block.c
18
block.c
@ -26,6 +26,7 @@
|
||||
#include "block/trace.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"
|
||||
@ -961,6 +962,11 @@ int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
|
||||
}
|
||||
|
||||
bs->total_sectors = hint;
|
||||
|
||||
if (bs->total_sectors * BDRV_SECTOR_SIZE > BDRV_MAX_LENGTH) {
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -5534,6 +5540,7 @@ void bdrv_get_backing_filename(BlockDriverState *bs,
|
||||
|
||||
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
int ret;
|
||||
BlockDriver *drv = bs->drv;
|
||||
/* if bs->drv == NULL, bs is closed, so there's nothing to do here */
|
||||
if (!drv) {
|
||||
@ -5547,7 +5554,16 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
return -ENOTSUP;
|
||||
}
|
||||
memset(bdi, 0, sizeof(*bdi));
|
||||
return drv->bdrv_get_info(bs, bdi);
|
||||
ret = drv->bdrv_get_info(bs, bdi);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bdi->cluster_size > BDRV_MAX_ALIGNMENT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs,
|
||||
|
@ -199,29 +199,27 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&stats->lock);
|
||||
WITH_QEMU_LOCK_GUARD(&stats->lock) {
|
||||
if (failed) {
|
||||
stats->failed_ops[cookie->type]++;
|
||||
} else {
|
||||
stats->nr_bytes[cookie->type] += cookie->bytes;
|
||||
stats->nr_ops[cookie->type]++;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
stats->failed_ops[cookie->type]++;
|
||||
} else {
|
||||
stats->nr_bytes[cookie->type] += cookie->bytes;
|
||||
stats->nr_ops[cookie->type]++;
|
||||
}
|
||||
block_latency_histogram_account(&stats->latency_histogram[cookie->type],
|
||||
latency_ns);
|
||||
|
||||
block_latency_histogram_account(&stats->latency_histogram[cookie->type],
|
||||
latency_ns);
|
||||
if (!failed || stats->account_failed) {
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
||||
if (!failed || stats->account_failed) {
|
||||
stats->total_time_ns[cookie->type] += latency_ns;
|
||||
stats->last_access_time_ns = time_ns;
|
||||
|
||||
QSLIST_FOREACH(s, &stats->intervals, entries) {
|
||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
||||
QSLIST_FOREACH(s, &stats->intervals, entries) {
|
||||
timed_average_account(&s->latency[cookie->type], latency_ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&stats->lock);
|
||||
|
||||
cookie->type = BLOCK_ACCT_NONE;
|
||||
}
|
||||
|
||||
|
28
block/curl.c
28
block/curl.c
@ -564,23 +564,23 @@ static void curl_detach_aio_context(BlockDriverState *bs)
|
||||
BDRVCURLState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
qemu_mutex_lock(&s->mutex);
|
||||
for (i = 0; i < CURL_NUM_STATES; i++) {
|
||||
if (s->states[i].in_use) {
|
||||
curl_clean_state(&s->states[i]);
|
||||
WITH_QEMU_LOCK_GUARD(&s->mutex) {
|
||||
for (i = 0; i < CURL_NUM_STATES; i++) {
|
||||
if (s->states[i].in_use) {
|
||||
curl_clean_state(&s->states[i]);
|
||||
}
|
||||
if (s->states[i].curl) {
|
||||
curl_easy_cleanup(s->states[i].curl);
|
||||
s->states[i].curl = NULL;
|
||||
}
|
||||
g_free(s->states[i].orig_buf);
|
||||
s->states[i].orig_buf = NULL;
|
||||
}
|
||||
if (s->states[i].curl) {
|
||||
curl_easy_cleanup(s->states[i].curl);
|
||||
s->states[i].curl = NULL;
|
||||
if (s->multi) {
|
||||
curl_multi_cleanup(s->multi);
|
||||
s->multi = NULL;
|
||||
}
|
||||
g_free(s->states[i].orig_buf);
|
||||
s->states[i].orig_buf = NULL;
|
||||
}
|
||||
if (s->multi) {
|
||||
curl_multi_cleanup(s->multi);
|
||||
s->multi = NULL;
|
||||
}
|
||||
qemu_mutex_unlock(&s->mutex);
|
||||
|
||||
timer_del(&s->timer);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/iothread.h"
|
||||
#include "block/export.h"
|
||||
#include "block/fuse.h"
|
||||
#include "block/nbd.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-commands-block-export.h"
|
||||
@ -31,6 +32,9 @@ static const BlockExportDriver *blk_exp_drivers[] = {
|
||||
#ifdef CONFIG_VHOST_USER_BLK_SERVER
|
||||
&blk_exp_vhost_user_blk,
|
||||
#endif
|
||||
#ifdef CONFIG_FUSE
|
||||
&blk_exp_fuse,
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Only accessed from the main thread */
|
||||
|
726
block/export/fuse.c
Normal file
726
block/export/fuse.c
Normal file
@ -0,0 +1,726 @@
|
||||
/*
|
||||
* Present a block device as a raw image through FUSE
|
||||
*
|
||||
* Copyright (c) 2020 Max Reitz <mreitz@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; under version 2 or later of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define FUSE_USE_VERSION 31
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/aio.h"
|
||||
#include "block/block.h"
|
||||
#include "block/export.h"
|
||||
#include "block/fuse.h"
|
||||
#include "block/qapi.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-commands-block.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
||||
#include <fuse.h>
|
||||
#include <fuse_lowlevel.h>
|
||||
|
||||
|
||||
/* Prevent overly long bounce buffer allocations */
|
||||
#define FUSE_MAX_BOUNCE_BYTES (MIN(BDRV_REQUEST_MAX_BYTES, 64 * 1024 * 1024))
|
||||
|
||||
|
||||
typedef struct FuseExport {
|
||||
BlockExport common;
|
||||
|
||||
struct fuse_session *fuse_session;
|
||||
struct fuse_buf fuse_buf;
|
||||
bool mounted, fd_handler_set_up;
|
||||
|
||||
char *mountpoint;
|
||||
bool writable;
|
||||
bool growable;
|
||||
} FuseExport;
|
||||
|
||||
static GHashTable *exports;
|
||||
static const struct fuse_lowlevel_ops fuse_ops;
|
||||
|
||||
static void fuse_export_shutdown(BlockExport *exp);
|
||||
static void fuse_export_delete(BlockExport *exp);
|
||||
|
||||
static void init_exports_table(void);
|
||||
|
||||
static int setup_fuse_export(FuseExport *exp, const char *mountpoint,
|
||||
Error **errp);
|
||||
static void read_from_fuse_export(void *opaque);
|
||||
|
||||
static bool is_regular_file(const char *path, Error **errp);
|
||||
|
||||
|
||||
static int fuse_export_create(BlockExport *blk_exp,
|
||||
BlockExportOptions *blk_exp_args,
|
||||
Error **errp)
|
||||
{
|
||||
FuseExport *exp = container_of(blk_exp, FuseExport, common);
|
||||
BlockExportOptionsFuse *args = &blk_exp_args->u.fuse;
|
||||
int ret;
|
||||
|
||||
assert(blk_exp_args->type == BLOCK_EXPORT_TYPE_FUSE);
|
||||
|
||||
/* For growable exports, take the RESIZE permission */
|
||||
if (args->growable) {
|
||||
uint64_t blk_perm, blk_shared_perm;
|
||||
|
||||
blk_get_perm(exp->common.blk, &blk_perm, &blk_shared_perm);
|
||||
|
||||
ret = blk_set_perm(exp->common.blk, blk_perm | BLK_PERM_RESIZE,
|
||||
blk_shared_perm, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
init_exports_table();
|
||||
|
||||
/*
|
||||
* It is important to do this check before calling is_regular_file() --
|
||||
* that function will do a stat(), which we would have to handle if we
|
||||
* already exported something on @mountpoint. But we cannot, because
|
||||
* we are currently caught up here.
|
||||
* (Note that ideally we would want to resolve relative paths here,
|
||||
* but bdrv_make_absolute_filename() might do the wrong thing for
|
||||
* paths that contain colons, and realpath() would resolve symlinks,
|
||||
* which we do not want: The mount point is not going to be the
|
||||
* symlink's destination, but the link itself.)
|
||||
* So this will not catch all potential clashes, but hopefully at
|
||||
* least the most common one of specifying exactly the same path
|
||||
* string twice.
|
||||
*/
|
||||
if (g_hash_table_contains(exports, args->mountpoint)) {
|
||||
error_setg(errp, "There already is a FUSE export on '%s'",
|
||||
args->mountpoint);
|
||||
ret = -EEXIST;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!is_regular_file(args->mountpoint, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
exp->mountpoint = g_strdup(args->mountpoint);
|
||||
exp->writable = blk_exp_args->writable;
|
||||
exp->growable = args->growable;
|
||||
|
||||
ret = setup_fuse_export(exp, args->mountpoint, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
fuse_export_delete(blk_exp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates the global @exports hash table.
|
||||
*/
|
||||
static void init_exports_table(void)
|
||||
{
|
||||
if (exports) {
|
||||
return;
|
||||
}
|
||||
|
||||
exports = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create exp->fuse_session and mount it.
|
||||
*/
|
||||
static int setup_fuse_export(FuseExport *exp, const char *mountpoint,
|
||||
Error **errp)
|
||||
{
|
||||
const char *fuse_argv[4];
|
||||
char *mount_opts;
|
||||
struct fuse_args fuse_args;
|
||||
int ret;
|
||||
|
||||
/* Needs to match what fuse_init() sets. Only max_read must be supplied. */
|
||||
mount_opts = g_strdup_printf("max_read=%zu", FUSE_MAX_BOUNCE_BYTES);
|
||||
|
||||
fuse_argv[0] = ""; /* Dummy program name */
|
||||
fuse_argv[1] = "-o";
|
||||
fuse_argv[2] = mount_opts;
|
||||
fuse_argv[3] = NULL;
|
||||
fuse_args = (struct fuse_args)FUSE_ARGS_INIT(3, (char **)fuse_argv);
|
||||
|
||||
exp->fuse_session = fuse_session_new(&fuse_args, &fuse_ops,
|
||||
sizeof(fuse_ops), exp);
|
||||
g_free(mount_opts);
|
||||
if (!exp->fuse_session) {
|
||||
error_setg(errp, "Failed to set up FUSE session");
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = fuse_session_mount(exp->fuse_session, mountpoint);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to mount FUSE session to export");
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
exp->mounted = true;
|
||||
|
||||
g_hash_table_insert(exports, g_strdup(mountpoint), NULL);
|
||||
|
||||
aio_set_fd_handler(exp->common.ctx,
|
||||
fuse_session_fd(exp->fuse_session), true,
|
||||
read_from_fuse_export, NULL, NULL, exp);
|
||||
exp->fd_handler_set_up = true;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
fuse_export_shutdown(&exp->common);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the FUSE session FD can be read from.
|
||||
* (This is basically the FUSE event loop.)
|
||||
*/
|
||||
static void read_from_fuse_export(void *opaque)
|
||||
{
|
||||
FuseExport *exp = opaque;
|
||||
int ret;
|
||||
|
||||
blk_exp_ref(&exp->common);
|
||||
|
||||
do {
|
||||
ret = fuse_session_receive_buf(exp->fuse_session, &exp->fuse_buf);
|
||||
} while (ret == -EINTR);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
fuse_session_process_buf(exp->fuse_session, &exp->fuse_buf);
|
||||
|
||||
out:
|
||||
blk_exp_unref(&exp->common);
|
||||
}
|
||||
|
||||
static void fuse_export_shutdown(BlockExport *blk_exp)
|
||||
{
|
||||
FuseExport *exp = container_of(blk_exp, FuseExport, common);
|
||||
|
||||
if (exp->fuse_session) {
|
||||
fuse_session_exit(exp->fuse_session);
|
||||
|
||||
if (exp->fd_handler_set_up) {
|
||||
aio_set_fd_handler(exp->common.ctx,
|
||||
fuse_session_fd(exp->fuse_session), true,
|
||||
NULL, NULL, NULL, NULL);
|
||||
exp->fd_handler_set_up = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (exp->mountpoint) {
|
||||
/*
|
||||
* Safe to drop now, because we will not handle any requests
|
||||
* for this export anymore anyway.
|
||||
*/
|
||||
g_hash_table_remove(exports, exp->mountpoint);
|
||||
}
|
||||
}
|
||||
|
||||
static void fuse_export_delete(BlockExport *blk_exp)
|
||||
{
|
||||
FuseExport *exp = container_of(blk_exp, FuseExport, common);
|
||||
|
||||
if (exp->fuse_session) {
|
||||
if (exp->mounted) {
|
||||
fuse_session_unmount(exp->fuse_session);
|
||||
}
|
||||
|
||||
fuse_session_destroy(exp->fuse_session);
|
||||
}
|
||||
|
||||
free(exp->fuse_buf.mem);
|
||||
g_free(exp->mountpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether @path points to a regular file. If not, put an
|
||||
* appropriate message into *errp.
|
||||
*/
|
||||
static bool is_regular_file(const char *path, Error **errp)
|
||||
{
|
||||
struct stat statbuf;
|
||||
int ret;
|
||||
|
||||
ret = stat(path, &statbuf);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, errno, "Failed to stat '%s'", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!S_ISREG(statbuf.st_mode)) {
|
||||
error_setg(errp, "'%s' is not a regular file", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A chance to set change some parameters supplied to FUSE_INIT.
|
||||
*/
|
||||
static void fuse_init(void *userdata, struct fuse_conn_info *conn)
|
||||
{
|
||||
/*
|
||||
* MIN_NON_ZERO() would not be wrong here, but what we set here
|
||||
* must equal what has been passed to fuse_session_new().
|
||||
* Therefore, as long as max_read must be passed as a mount option
|
||||
* (which libfuse claims will be changed at some point), we have
|
||||
* to set max_read to a fixed value here.
|
||||
*/
|
||||
conn->max_read = FUSE_MAX_BOUNCE_BYTES;
|
||||
|
||||
conn->max_write = MIN_NON_ZERO(BDRV_REQUEST_MAX_BYTES, conn->max_write);
|
||||
}
|
||||
|
||||
/**
|
||||
* Let clients look up files. Always return ENOENT because we only
|
||||
* care about the mountpoint itself.
|
||||
*/
|
||||
static void fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
|
||||
{
|
||||
fuse_reply_err(req, ENOENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Let clients get file attributes (i.e., stat() the file).
|
||||
*/
|
||||
static void fuse_getattr(fuse_req_t req, fuse_ino_t inode,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
struct stat statbuf;
|
||||
int64_t length, allocated_blocks;
|
||||
time_t now = time(NULL);
|
||||
FuseExport *exp = fuse_req_userdata(req);
|
||||
mode_t mode;
|
||||
|
||||
length = blk_getlength(exp->common.blk);
|
||||
if (length < 0) {
|
||||
fuse_reply_err(req, -length);
|
||||
return;
|
||||
}
|
||||
|
||||
allocated_blocks = bdrv_get_allocated_file_size(blk_bs(exp->common.blk));
|
||||
if (allocated_blocks <= 0) {
|
||||
allocated_blocks = DIV_ROUND_UP(length, 512);
|
||||
} else {
|
||||
allocated_blocks = DIV_ROUND_UP(allocated_blocks, 512);
|
||||
}
|
||||
|
||||
mode = S_IFREG | S_IRUSR;
|
||||
if (exp->writable) {
|
||||
mode |= S_IWUSR;
|
||||
}
|
||||
|
||||
statbuf = (struct stat) {
|
||||
.st_ino = inode,
|
||||
.st_mode = mode,
|
||||
.st_nlink = 1,
|
||||
.st_uid = getuid(),
|
||||
.st_gid = getgid(),
|
||||
.st_size = length,
|
||||
.st_blksize = blk_bs(exp->common.blk)->bl.request_alignment,
|
||||
.st_blocks = allocated_blocks,
|
||||
.st_atime = now,
|
||||
.st_mtime = now,
|
||||
.st_ctime = now,
|
||||
};
|
||||
|
||||
fuse_reply_attr(req, &statbuf, 1.);
|
||||
}
|
||||
|
||||
static int fuse_do_truncate(const FuseExport *exp, int64_t size,
|
||||
bool req_zero_write, PreallocMode prealloc)
|
||||
{
|
||||
uint64_t blk_perm, blk_shared_perm;
|
||||
BdrvRequestFlags truncate_flags = 0;
|
||||
int ret;
|
||||
|
||||
if (req_zero_write) {
|
||||
truncate_flags |= BDRV_REQ_ZERO_WRITE;
|
||||
}
|
||||
|
||||
/* Growable exports have a permanent RESIZE permission */
|
||||
if (!exp->growable) {
|
||||
blk_get_perm(exp->common.blk, &blk_perm, &blk_shared_perm);
|
||||
|
||||
ret = blk_set_perm(exp->common.blk, blk_perm | BLK_PERM_RESIZE,
|
||||
blk_shared_perm, NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = blk_truncate(exp->common.blk, size, true, prealloc,
|
||||
truncate_flags, NULL);
|
||||
|
||||
if (!exp->growable) {
|
||||
/* Must succeed, because we are only giving up the RESIZE permission */
|
||||
blk_set_perm(exp->common.blk, blk_perm, blk_shared_perm, &error_abort);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Let clients set file attributes. Only resizing is supported.
|
||||
*/
|
||||
static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *statbuf,
|
||||
int to_set, struct fuse_file_info *fi)
|
||||
{
|
||||
FuseExport *exp = fuse_req_userdata(req);
|
||||
int ret;
|
||||
|
||||
if (!exp->writable) {
|
||||
fuse_reply_err(req, EACCES);
|
||||
return;
|
||||
}
|
||||
|
||||
if (to_set & ~FUSE_SET_ATTR_SIZE) {
|
||||
fuse_reply_err(req, ENOTSUP);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = fuse_do_truncate(exp, statbuf->st_size, true, PREALLOC_MODE_OFF);
|
||||
if (ret < 0) {
|
||||
fuse_reply_err(req, -ret);
|
||||
return;
|
||||
}
|
||||
|
||||
fuse_getattr(req, inode, fi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Let clients open a file (i.e., the exported image).
|
||||
*/
|
||||
static void fuse_open(fuse_req_t req, fuse_ino_t inode,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
fuse_reply_open(req, fi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client reads from the exported image.
|
||||
*/
|
||||
static void fuse_read(fuse_req_t req, fuse_ino_t inode,
|
||||
size_t size, off_t offset, struct fuse_file_info *fi)
|
||||
{
|
||||
FuseExport *exp = fuse_req_userdata(req);
|
||||
int64_t length;
|
||||
void *buf;
|
||||
int ret;
|
||||
|
||||
/* Limited by max_read, should not happen */
|
||||
if (size > FUSE_MAX_BOUNCE_BYTES) {
|
||||
fuse_reply_err(req, EINVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clients will expect short reads at EOF, so we have to limit
|
||||
* offset+size to the image length.
|
||||
*/
|
||||
length = blk_getlength(exp->common.blk);
|
||||
if (length < 0) {
|
||||
fuse_reply_err(req, -length);
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset + size > length) {
|
||||
size = length - offset;
|
||||
}
|
||||
|
||||
buf = qemu_try_blockalign(blk_bs(exp->common.blk), size);
|
||||
if (!buf) {
|
||||
fuse_reply_err(req, ENOMEM);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = blk_pread(exp->common.blk, offset, buf, size);
|
||||
if (ret >= 0) {
|
||||
fuse_reply_buf(req, buf, size);
|
||||
} else {
|
||||
fuse_reply_err(req, -ret);
|
||||
}
|
||||
|
||||
qemu_vfree(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client writes to the exported image.
|
||||
*/
|
||||
static void fuse_write(fuse_req_t req, fuse_ino_t inode, const char *buf,
|
||||
size_t size, off_t offset, struct fuse_file_info *fi)
|
||||
{
|
||||
FuseExport *exp = fuse_req_userdata(req);
|
||||
int64_t length;
|
||||
int ret;
|
||||
|
||||
/* Limited by max_write, should not happen */
|
||||
if (size > BDRV_REQUEST_MAX_BYTES) {
|
||||
fuse_reply_err(req, EINVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!exp->writable) {
|
||||
fuse_reply_err(req, EACCES);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clients will expect short writes at EOF, so we have to limit
|
||||
* offset+size to the image length.
|
||||
*/
|
||||
length = blk_getlength(exp->common.blk);
|
||||
if (length < 0) {
|
||||
fuse_reply_err(req, -length);
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset + size > length) {
|
||||
if (exp->growable) {
|
||||
ret = fuse_do_truncate(exp, offset + size, true, PREALLOC_MODE_OFF);
|
||||
if (ret < 0) {
|
||||
fuse_reply_err(req, -ret);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
size = length - offset;
|
||||
}
|
||||
}
|
||||
|
||||
ret = blk_pwrite(exp->common.blk, offset, buf, size, 0);
|
||||
if (ret >= 0) {
|
||||
fuse_reply_write(req, size);
|
||||
} else {
|
||||
fuse_reply_err(req, -ret);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Let clients perform various fallocate() operations.
|
||||
*/
|
||||
static void fuse_fallocate(fuse_req_t req, fuse_ino_t inode, int mode,
|
||||
off_t offset, off_t length,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
FuseExport *exp = fuse_req_userdata(req);
|
||||
int64_t blk_len;
|
||||
int ret;
|
||||
|
||||
if (!exp->writable) {
|
||||
fuse_reply_err(req, EACCES);
|
||||
return;
|
||||
}
|
||||
|
||||
blk_len = blk_getlength(exp->common.blk);
|
||||
if (blk_len < 0) {
|
||||
fuse_reply_err(req, -blk_len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode & FALLOC_FL_KEEP_SIZE) {
|
||||
length = MIN(length, blk_len - offset);
|
||||
}
|
||||
|
||||
if (mode & FALLOC_FL_PUNCH_HOLE) {
|
||||
if (!(mode & FALLOC_FL_KEEP_SIZE)) {
|
||||
fuse_reply_err(req, EINVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
int size = MIN(length, BDRV_REQUEST_MAX_BYTES);
|
||||
|
||||
ret = blk_pdiscard(exp->common.blk, offset, size);
|
||||
offset += size;
|
||||
length -= size;
|
||||
} while (ret == 0 && length > 0);
|
||||
} else if (mode & FALLOC_FL_ZERO_RANGE) {
|
||||
if (!(mode & FALLOC_FL_KEEP_SIZE) && offset + length > blk_len) {
|
||||
/* No need for zeroes, we are going to write them ourselves */
|
||||
ret = fuse_do_truncate(exp, offset + length, false,
|
||||
PREALLOC_MODE_OFF);
|
||||
if (ret < 0) {
|
||||
fuse_reply_err(req, -ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
int size = MIN(length, BDRV_REQUEST_MAX_BYTES);
|
||||
|
||||
ret = blk_pwrite_zeroes(exp->common.blk,
|
||||
offset, size, 0);
|
||||
offset += size;
|
||||
length -= size;
|
||||
} while (ret == 0 && length > 0);
|
||||
} else if (!mode) {
|
||||
/* We can only fallocate at the EOF with a truncate */
|
||||
if (offset < blk_len) {
|
||||
fuse_reply_err(req, EOPNOTSUPP);
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset > blk_len) {
|
||||
/* No preallocation needed here */
|
||||
ret = fuse_do_truncate(exp, offset, true, PREALLOC_MODE_OFF);
|
||||
if (ret < 0) {
|
||||
fuse_reply_err(req, -ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ret = fuse_do_truncate(exp, offset + length, true,
|
||||
PREALLOC_MODE_FALLOC);
|
||||
} else {
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
fuse_reply_err(req, ret < 0 ? -ret : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Let clients fsync the exported image.
|
||||
*/
|
||||
static void fuse_fsync(fuse_req_t req, fuse_ino_t inode, int datasync,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
FuseExport *exp = fuse_req_userdata(req);
|
||||
int ret;
|
||||
|
||||
ret = blk_flush(exp->common.blk);
|
||||
fuse_reply_err(req, ret < 0 ? -ret : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before an FD to the exported image is closed. (libfuse
|
||||
* notes this to be a way to return last-minute errors.)
|
||||
*/
|
||||
static void fuse_flush(fuse_req_t req, fuse_ino_t inode,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
fuse_fsync(req, inode, 1, fi);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FUSE_LSEEK
|
||||
/**
|
||||
* Let clients inquire allocation status.
|
||||
*/
|
||||
static void fuse_lseek(fuse_req_t req, fuse_ino_t inode, off_t offset,
|
||||
int whence, struct fuse_file_info *fi)
|
||||
{
|
||||
FuseExport *exp = fuse_req_userdata(req);
|
||||
|
||||
if (whence != SEEK_HOLE && whence != SEEK_DATA) {
|
||||
fuse_reply_err(req, EINVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
int64_t pnum;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_block_status_above(blk_bs(exp->common.blk), NULL,
|
||||
offset, INT64_MAX, &pnum, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
fuse_reply_err(req, -ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pnum && (ret & BDRV_BLOCK_EOF)) {
|
||||
int64_t blk_len;
|
||||
|
||||
/*
|
||||
* If blk_getlength() rounds (e.g. by sectors), then the
|
||||
* export length will be rounded, too. However,
|
||||
* bdrv_block_status_above() may return EOF at unaligned
|
||||
* offsets. We must not let this become visible and thus
|
||||
* always simulate a hole between @offset (the real EOF)
|
||||
* and @blk_len (the client-visible EOF).
|
||||
*/
|
||||
|
||||
blk_len = blk_getlength(exp->common.blk);
|
||||
if (blk_len < 0) {
|
||||
fuse_reply_err(req, -blk_len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset > blk_len || whence == SEEK_DATA) {
|
||||
fuse_reply_err(req, ENXIO);
|
||||
} else {
|
||||
fuse_reply_lseek(req, offset);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret & BDRV_BLOCK_DATA) {
|
||||
if (whence == SEEK_DATA) {
|
||||
fuse_reply_lseek(req, offset);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (whence == SEEK_HOLE) {
|
||||
fuse_reply_lseek(req, offset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Safety check against infinite loops */
|
||||
if (!pnum) {
|
||||
fuse_reply_err(req, ENXIO);
|
||||
return;
|
||||
}
|
||||
|
||||
offset += pnum;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct fuse_lowlevel_ops fuse_ops = {
|
||||
.init = fuse_init,
|
||||
.lookup = fuse_lookup,
|
||||
.getattr = fuse_getattr,
|
||||
.setattr = fuse_setattr,
|
||||
.open = fuse_open,
|
||||
.read = fuse_read,
|
||||
.write = fuse_write,
|
||||
.fallocate = fuse_fallocate,
|
||||
.flush = fuse_flush,
|
||||
.fsync = fuse_fsync,
|
||||
#ifdef CONFIG_FUSE_LSEEK
|
||||
.lseek = fuse_lseek,
|
||||
#endif
|
||||
};
|
||||
|
||||
const BlockExportDriver blk_exp_fuse = {
|
||||
.type = BLOCK_EXPORT_TYPE_FUSE,
|
||||
.instance_size = sizeof(FuseExport),
|
||||
.create = fuse_export_create,
|
||||
.delete = fuse_export_delete,
|
||||
.request_shutdown = fuse_export_shutdown,
|
||||
};
|
@ -3,3 +3,5 @@ blockdev_ss.add(files('export.c'))
|
||||
if have_vhost_user_blk_server
|
||||
blockdev_ss.add(files('vhost-user-blk-server.c'))
|
||||
endif
|
||||
|
||||
blockdev_ss.add(when: fuse, if_true: files('fuse.c'))
|
||||
|
@ -2926,7 +2926,6 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
|
||||
#ifdef CONFIG_FALLOCATE
|
||||
if (offset + bytes > bs->total_sectors * BDRV_SECTOR_SIZE) {
|
||||
BdrvTrackedRequest *req;
|
||||
uint64_t end;
|
||||
|
||||
/*
|
||||
* This is a workaround for a bug in the Linux XFS driver,
|
||||
@ -2950,9 +2949,9 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
|
||||
assert(req->offset <= offset);
|
||||
assert(req->offset + req->bytes >= offset + bytes);
|
||||
|
||||
end = INT64_MAX & -(uint64_t)bs->bl.request_alignment;
|
||||
req->bytes = end - req->offset;
|
||||
req->overlap_bytes = req->bytes;
|
||||
req->bytes = BDRV_MAX_LENGTH - req->offset;
|
||||
|
||||
assert(bdrv_check_request(req->offset, req->bytes) == 0);
|
||||
|
||||
bdrv_mark_request_serialising(req, bs->bl.request_alignment);
|
||||
}
|
||||
@ -3104,7 +3103,7 @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
|
||||
}
|
||||
|
||||
/* Copy locks to the new fd */
|
||||
if (s->perm_change_fd) {
|
||||
if (s->perm_change_fd && s->use_lock) {
|
||||
ret = raw_apply_lock_bytes(NULL, s->perm_change_fd, perm, ~shared,
|
||||
false, errp);
|
||||
if (ret < 0) {
|
||||
|
110
block/io.c
110
block/io.c
@ -135,10 +135,10 @@ static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
|
||||
|
||||
void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BlockDriver *drv = bs->drv;
|
||||
BdrvChild *c;
|
||||
bool have_limits;
|
||||
Error *local_err = NULL;
|
||||
|
||||
memset(&bs->bl, 0, sizeof(bs->bl));
|
||||
|
||||
@ -156,9 +156,8 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
QLIST_FOREACH(c, &bs->children, next) {
|
||||
if (c->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED | BDRV_CHILD_COW))
|
||||
{
|
||||
bdrv_refresh_limits(c->bs, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
bdrv_refresh_limits(c->bs, errp);
|
||||
if (*errp) {
|
||||
return;
|
||||
}
|
||||
bdrv_merge_limits(&bs->bl, &c->bs->bl);
|
||||
@ -177,6 +176,13 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
/* Then let the driver override it */
|
||||
if (drv->bdrv_refresh_limits) {
|
||||
drv->bdrv_refresh_limits(bs, errp);
|
||||
if (*errp) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (bs->bl.request_alignment > BDRV_MAX_ALIGNMENT) {
|
||||
error_setg(errp, "Driver requires too large request alignment");
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,17 +312,7 @@ static void bdrv_co_drain_bh_cb(void *opaque)
|
||||
|
||||
if (bs) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
AioContext *co_ctx = qemu_coroutine_get_aio_context(co);
|
||||
|
||||
/*
|
||||
* When the coroutine yielded, the lock for its home context was
|
||||
* released, so we need to re-acquire it here. If it explicitly
|
||||
* acquired a different context, the lock is still held and we don't
|
||||
* want to lock it a second time (or AIO_WAIT_WHILE() would hang).
|
||||
*/
|
||||
if (ctx == co_ctx) {
|
||||
aio_context_acquire(ctx);
|
||||
}
|
||||
aio_context_acquire(ctx);
|
||||
bdrv_dec_in_flight(bs);
|
||||
if (data->begin) {
|
||||
assert(!data->drained_end_counter);
|
||||
@ -328,9 +324,7 @@ static void bdrv_co_drain_bh_cb(void *opaque)
|
||||
data->ignore_bds_parents,
|
||||
data->drained_end_counter);
|
||||
}
|
||||
if (ctx == co_ctx) {
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
aio_context_release(ctx);
|
||||
} else {
|
||||
assert(data->begin);
|
||||
bdrv_drain_all_begin();
|
||||
@ -348,13 +342,16 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
|
||||
int *drained_end_counter)
|
||||
{
|
||||
BdrvCoDrainData data;
|
||||
Coroutine *self = qemu_coroutine_self();
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
AioContext *co_ctx = qemu_coroutine_get_aio_context(self);
|
||||
|
||||
/* Calling bdrv_drain() from a BH ensures the current coroutine yields and
|
||||
* other coroutines run if they were queued by aio_co_enter(). */
|
||||
|
||||
assert(qemu_in_coroutine());
|
||||
data = (BdrvCoDrainData) {
|
||||
.co = qemu_coroutine_self(),
|
||||
.co = self,
|
||||
.bs = bs,
|
||||
.done = false,
|
||||
.begin = begin,
|
||||
@ -368,13 +365,29 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
|
||||
if (bs) {
|
||||
bdrv_inc_in_flight(bs);
|
||||
}
|
||||
replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs),
|
||||
bdrv_co_drain_bh_cb, &data);
|
||||
|
||||
/*
|
||||
* Temporarily drop the lock across yield or we would get deadlocks.
|
||||
* bdrv_co_drain_bh_cb() reaquires the lock as needed.
|
||||
*
|
||||
* When we yield below, the lock for the current context will be
|
||||
* released, so if this is actually the lock that protects bs, don't drop
|
||||
* it a second time.
|
||||
*/
|
||||
if (ctx != co_ctx) {
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
replay_bh_schedule_oneshot_event(ctx, bdrv_co_drain_bh_cb, &data);
|
||||
|
||||
qemu_coroutine_yield();
|
||||
/* If we are resumed from some other event (such as an aio completion or a
|
||||
* timer callback), it is a bug in the caller that should be fixed. */
|
||||
assert(data.done);
|
||||
|
||||
/* Reaquire the AioContext of bs if we dropped it */
|
||||
if (ctx != co_ctx) {
|
||||
aio_context_acquire(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
|
||||
@ -885,18 +898,31 @@ static bool coroutine_fn bdrv_wait_serialising_requests(BdrvTrackedRequest *self
|
||||
return waited;
|
||||
}
|
||||
|
||||
static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
|
||||
size_t size)
|
||||
int bdrv_check_request(int64_t offset, int64_t bytes)
|
||||
{
|
||||
if (size > BDRV_REQUEST_MAX_BYTES) {
|
||||
if (offset < 0 || bytes < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!bdrv_is_inserted(bs)) {
|
||||
return -ENOMEDIUM;
|
||||
if (bytes > BDRV_MAX_LENGTH) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
if (offset > BDRV_MAX_LENGTH - bytes) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bdrv_check_request32(int64_t offset, int64_t bytes)
|
||||
{
|
||||
int ret = bdrv_check_request(offset, bytes);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bytes > BDRV_REQUEST_MAX_BYTES) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -1643,7 +1669,11 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child,
|
||||
|
||||
trace_bdrv_co_preadv(bs, offset, bytes, flags);
|
||||
|
||||
ret = bdrv_check_byte_request(bs, offset, bytes);
|
||||
if (!bdrv_is_inserted(bs)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
ret = bdrv_check_request32(offset, bytes);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@ -2055,11 +2085,11 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child,
|
||||
|
||||
trace_bdrv_co_pwritev(child->bs, offset, bytes, flags);
|
||||
|
||||
if (!bs->drv) {
|
||||
if (!bdrv_is_inserted(bs)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
ret = bdrv_check_byte_request(bs, offset, bytes);
|
||||
ret = bdrv_check_request32(offset, bytes);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@ -2789,8 +2819,9 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset,
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (offset < 0 || bytes < 0 || bytes > INT64_MAX - offset) {
|
||||
return -EIO;
|
||||
ret = bdrv_check_request(offset, bytes);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do nothing if disabled. */
|
||||
@ -3046,10 +3077,10 @@ static int coroutine_fn bdrv_co_copy_range_internal(
|
||||
assert(!(read_flags & BDRV_REQ_NO_FALLBACK));
|
||||
assert(!(write_flags & BDRV_REQ_NO_FALLBACK));
|
||||
|
||||
if (!dst || !dst->bs) {
|
||||
if (!dst || !dst->bs || !bdrv_is_inserted(dst->bs)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
ret = bdrv_check_byte_request(dst->bs, dst_offset, bytes);
|
||||
ret = bdrv_check_request32(dst_offset, bytes);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
@ -3057,10 +3088,10 @@ static int coroutine_fn bdrv_co_copy_range_internal(
|
||||
return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, write_flags);
|
||||
}
|
||||
|
||||
if (!src || !src->bs) {
|
||||
if (!src || !src->bs || !bdrv_is_inserted(src->bs)) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
ret = bdrv_check_byte_request(src->bs, src_offset, bytes);
|
||||
ret = bdrv_check_request32(src_offset, bytes);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
@ -3190,6 +3221,13 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = bdrv_check_request(offset, 0);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Required too big image size, it must be not greater "
|
||||
"than %" PRId64, BDRV_MAX_LENGTH);
|
||||
return ret;
|
||||
}
|
||||
|
||||
old_size = bdrv_getlength(bs);
|
||||
if (old_size < 0) {
|
||||
error_setg_errno(errp, -old_size, "Failed to get old image size");
|
||||
|
@ -322,25 +322,23 @@ iscsi_aio_cancel(BlockAIOCB *blockacb)
|
||||
IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
|
||||
IscsiLun *iscsilun = acb->iscsilun;
|
||||
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
WITH_QEMU_LOCK_GUARD(&iscsilun->mutex) {
|
||||
|
||||
/* If it was cancelled or completed already, our work is done here */
|
||||
if (acb->cancelled || acb->status != -EINPROGRESS) {
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
return;
|
||||
/* If it was cancelled or completed already, our work is done here */
|
||||
if (acb->cancelled || acb->status != -EINPROGRESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
acb->cancelled = true;
|
||||
|
||||
qemu_aio_ref(acb); /* released in iscsi_abort_task_cb() */
|
||||
|
||||
/* send a task mgmt call to the target to cancel the task on the target */
|
||||
if (iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
|
||||
iscsi_abort_task_cb, acb) < 0) {
|
||||
qemu_aio_unref(acb); /* since iscsi_abort_task_cb() won't be called */
|
||||
}
|
||||
}
|
||||
|
||||
acb->cancelled = true;
|
||||
|
||||
qemu_aio_ref(acb); /* released in iscsi_abort_task_cb() */
|
||||
|
||||
/* send a task mgmt call to the target to cancel the task on the target */
|
||||
if (iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
|
||||
iscsi_abort_task_cb, acb) < 0) {
|
||||
qemu_aio_unref(acb); /* since iscsi_abort_task_cb() won't be called */
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
}
|
||||
|
||||
static const AIOCBInfo iscsi_aiocb_info = {
|
||||
@ -375,22 +373,22 @@ static void iscsi_timed_check_events(void *opaque)
|
||||
{
|
||||
IscsiLun *iscsilun = opaque;
|
||||
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
WITH_QEMU_LOCK_GUARD(&iscsilun->mutex) {
|
||||
/* check for timed out requests */
|
||||
iscsi_service(iscsilun->iscsi, 0);
|
||||
|
||||
/* check for timed out requests */
|
||||
iscsi_service(iscsilun->iscsi, 0);
|
||||
if (iscsilun->request_timed_out) {
|
||||
iscsilun->request_timed_out = false;
|
||||
iscsi_reconnect(iscsilun->iscsi);
|
||||
}
|
||||
|
||||
if (iscsilun->request_timed_out) {
|
||||
iscsilun->request_timed_out = false;
|
||||
iscsi_reconnect(iscsilun->iscsi);
|
||||
/*
|
||||
* newer versions of libiscsi may return zero events. Ensure we are
|
||||
* able to return to service once this situation changes.
|
||||
*/
|
||||
iscsi_set_events(iscsilun);
|
||||
}
|
||||
|
||||
/* newer versions of libiscsi may return zero events. Ensure we are able
|
||||
* to return to service once this situation changes. */
|
||||
iscsi_set_events(iscsilun);
|
||||
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
|
||||
timer_mod(iscsilun->event_timer,
|
||||
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL);
|
||||
}
|
||||
|
@ -546,7 +546,7 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
|
||||
tgm->aio_context = ctx;
|
||||
qatomic_set(&tgm->restart_pending, 0);
|
||||
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
QEMU_LOCK_GUARD(&tg->lock);
|
||||
/* If the ThrottleGroup is new set this ThrottleGroupMember as the token */
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!tg->tokens[i]) {
|
||||
@ -565,8 +565,6 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
|
||||
qemu_co_mutex_init(&tgm->throttled_reqs_lock);
|
||||
qemu_co_queue_init(&tgm->throttled_reqs[0]);
|
||||
qemu_co_queue_init(&tgm->throttled_reqs[1]);
|
||||
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
|
||||
/* Unregister a ThrottleGroupMember from its group, removing it from the list,
|
||||
@ -594,25 +592,25 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
|
||||
/* Wait for throttle_group_restart_queue_entry() coroutines to finish */
|
||||
AIO_WAIT_WHILE(tgm->aio_context, qatomic_read(&tgm->restart_pending) > 0);
|
||||
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
for (i = 0; i < 2; i++) {
|
||||
assert(tgm->pending_reqs[i] == 0);
|
||||
assert(qemu_co_queue_empty(&tgm->throttled_reqs[i]));
|
||||
assert(!timer_pending(tgm->throttle_timers.timers[i]));
|
||||
if (tg->tokens[i] == tgm) {
|
||||
token = throttle_group_next_tgm(tgm);
|
||||
/* Take care of the case where this is the last tgm in the group */
|
||||
if (token == tgm) {
|
||||
token = NULL;
|
||||
WITH_QEMU_LOCK_GUARD(&tg->lock) {
|
||||
for (i = 0; i < 2; i++) {
|
||||
assert(tgm->pending_reqs[i] == 0);
|
||||
assert(qemu_co_queue_empty(&tgm->throttled_reqs[i]));
|
||||
assert(!timer_pending(tgm->throttle_timers.timers[i]));
|
||||
if (tg->tokens[i] == tgm) {
|
||||
token = throttle_group_next_tgm(tgm);
|
||||
/* Take care of the case where this is the last tgm in the group */
|
||||
if (token == tgm) {
|
||||
token = NULL;
|
||||
}
|
||||
tg->tokens[i] = token;
|
||||
}
|
||||
tg->tokens[i] = token;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove the current tgm from the list */
|
||||
QLIST_REMOVE(tgm, round_robin);
|
||||
throttle_timers_destroy(&tgm->throttle_timers);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
/* remove the current tgm from the list */
|
||||
QLIST_REMOVE(tgm, round_robin);
|
||||
throttle_timers_destroy(&tgm->throttle_timers);
|
||||
}
|
||||
|
||||
throttle_group_unref(&tg->ts);
|
||||
tgm->throttle_state = NULL;
|
||||
@ -638,14 +636,14 @@ void throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
|
||||
assert(qemu_co_queue_empty(&tgm->throttled_reqs[1]));
|
||||
|
||||
/* Kick off next ThrottleGroupMember, if necessary */
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (timer_pending(tt->timers[i])) {
|
||||
tg->any_timer_armed[i] = false;
|
||||
schedule_next_request(tgm, i);
|
||||
WITH_QEMU_LOCK_GUARD(&tg->lock) {
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (timer_pending(tt->timers[i])) {
|
||||
tg->any_timer_armed[i] = false;
|
||||
schedule_next_request(tgm, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
|
||||
throttle_timers_detach_aio_context(tt);
|
||||
tgm->aio_context = NULL;
|
||||
|
14
blockdev.c
14
blockdev.c
@ -2454,7 +2454,7 @@ void coroutine_fn qmp_block_resize(bool has_device, const char *device,
|
||||
int64_t size, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
BlockBackend *blk = NULL;
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
AioContext *old_ctx;
|
||||
|
||||
@ -2468,27 +2468,29 @@ void coroutine_fn qmp_block_resize(bool has_device, const char *device,
|
||||
|
||||
if (size < 0) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size");
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) {
|
||||
error_setg(errp, QERR_DEVICE_IN_USE, device);
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp);
|
||||
if (!blk) {
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_co_lock(bs);
|
||||
bdrv_drained_begin(bs);
|
||||
bdrv_co_unlock(bs);
|
||||
|
||||
old_ctx = bdrv_co_enter(bs);
|
||||
blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
|
||||
bdrv_co_leave(bs, old_ctx);
|
||||
bdrv_drained_end(bs);
|
||||
|
||||
out:
|
||||
bdrv_co_lock(bs);
|
||||
bdrv_drained_end(bs);
|
||||
blk_unref(blk);
|
||||
bdrv_co_unlock(bs);
|
||||
}
|
||||
|
13
configure
vendored
13
configure
vendored
@ -449,6 +449,8 @@ meson=""
|
||||
ninja=""
|
||||
skip_meson=no
|
||||
gettext=""
|
||||
fuse="auto"
|
||||
fuse_lseek="auto"
|
||||
|
||||
bogus_os="no"
|
||||
malloc_trim="auto"
|
||||
@ -1525,6 +1527,14 @@ for opt do
|
||||
;;
|
||||
--disable-libdaxctl) libdaxctl=no
|
||||
;;
|
||||
--enable-fuse) fuse="enabled"
|
||||
;;
|
||||
--disable-fuse) fuse="disabled"
|
||||
;;
|
||||
--enable-fuse-lseek) fuse_lseek="enabled"
|
||||
;;
|
||||
--disable-fuse-lseek) fuse_lseek="disabled"
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unknown option $opt"
|
||||
echo "Try '$0 --help' for more information"
|
||||
@ -1850,6 +1860,8 @@ disabled with --disable-FEATURE, default is enabled if available:
|
||||
xkbcommon xkbcommon support
|
||||
rng-none dummy RNG, avoid using /dev/(u)random and getrandom()
|
||||
libdaxctl libdaxctl support
|
||||
fuse FUSE block device export
|
||||
fuse-lseek SEEK_HOLE/SEEK_DATA support for FUSE exports
|
||||
|
||||
NOTE: The object files are built at the place where configure is launched
|
||||
EOF
|
||||
@ -7014,6 +7026,7 @@ NINJA=$ninja $meson setup \
|
||||
-Diconv=$iconv -Dcurses=$curses -Dlibudev=$libudev\
|
||||
-Ddocs=$docs -Dsphinx_build=$sphinx_build -Dinstall_blobs=$blobs \
|
||||
-Dvhost_user_blk_server=$vhost_user_blk_server \
|
||||
-Dfuse=$fuse -Dfuse_lseek=$fuse_lseek \
|
||||
$cross_arg \
|
||||
"$PWD" "$source_path"
|
||||
|
||||
|
@ -142,6 +142,16 @@ typedef struct HDGeometry {
|
||||
INT_MAX >> BDRV_SECTOR_BITS)
|
||||
#define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS)
|
||||
|
||||
/*
|
||||
* We want allow aligning requests and disk length up to any 32bit alignment
|
||||
* and don't afraid of overflow.
|
||||
* To achieve it, and in the same time use some pretty number as maximum disk
|
||||
* size, let's define maximum "length" (a limit for any offset/bytes request and
|
||||
* for disk size) to be the greatest power of 2 less than INT64_MAX.
|
||||
*/
|
||||
#define BDRV_MAX_ALIGNMENT (1L << 30)
|
||||
#define BDRV_MAX_LENGTH (QEMU_ALIGN_DOWN(INT64_MAX, BDRV_MAX_ALIGNMENT))
|
||||
|
||||
/*
|
||||
* Allocation status flags for bdrv_block_status() and friends.
|
||||
*
|
||||
|
@ -70,6 +70,12 @@ enum BdrvTrackedRequestType {
|
||||
BDRV_TRACKED_TRUNCATE,
|
||||
};
|
||||
|
||||
/*
|
||||
* That is not quite good that BdrvTrackedRequest structure is public,
|
||||
* as block/io.c is very careful about incoming offset/bytes being
|
||||
* correct. Be sure to assert bdrv_check_request() succeeded after any
|
||||
* modification of BdrvTrackedRequest object out of block/io.c
|
||||
*/
|
||||
typedef struct BdrvTrackedRequest {
|
||||
BlockDriverState *bs;
|
||||
int64_t offset;
|
||||
@ -87,6 +93,8 @@ typedef struct BdrvTrackedRequest {
|
||||
struct BdrvTrackedRequest *waiting_for;
|
||||
} BdrvTrackedRequest;
|
||||
|
||||
int bdrv_check_request(int64_t offset, int64_t bytes);
|
||||
|
||||
struct BlockDriver {
|
||||
const char *format_name;
|
||||
int instance_size;
|
||||
|
30
include/block/fuse.h
Normal file
30
include/block/fuse.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Present a block device as a raw image through FUSE
|
||||
*
|
||||
* Copyright (c) 2020 Max Reitz <mreitz@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; under version 2 or later of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLOCK_FUSE_H
|
||||
#define BLOCK_FUSE_H
|
||||
|
||||
#ifdef CONFIG_FUSE
|
||||
|
||||
#include "block/export.h"
|
||||
|
||||
extern const BlockExportDriver blk_exp_fuse;
|
||||
|
||||
#endif /* CONFIG_FUSE */
|
||||
|
||||
#endif
|
26
meson.build
26
meson.build
@ -773,6 +773,28 @@ elif get_option('vhost_user_blk_server').disabled() or not have_system
|
||||
have_vhost_user_blk_server = false
|
||||
endif
|
||||
|
||||
if get_option('fuse').disabled() and get_option('fuse_lseek').enabled()
|
||||
error('Cannot enable fuse-lseek while fuse is disabled')
|
||||
endif
|
||||
|
||||
fuse = dependency('fuse3', required: get_option('fuse'),
|
||||
version: '>=3.1', method: 'pkg-config',
|
||||
static: enable_static)
|
||||
|
||||
fuse_lseek = not_found
|
||||
if not get_option('fuse_lseek').disabled()
|
||||
if fuse.version().version_compare('>=3.8')
|
||||
# Dummy dependency
|
||||
fuse_lseek = declare_dependency()
|
||||
elif get_option('fuse_lseek').enabled()
|
||||
if fuse.found()
|
||||
error('fuse-lseek requires libfuse >=3.8, found ' + fuse.version())
|
||||
else
|
||||
error('fuse-lseek requires libfuse, which was not found')
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
#################
|
||||
# config-host.h #
|
||||
#################
|
||||
@ -807,6 +829,8 @@ config_host_data.set('CONFIG_KEYUTILS', keyutils.found())
|
||||
config_host_data.set('CONFIG_GETTID', has_gettid)
|
||||
config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim)
|
||||
config_host_data.set('CONFIG_STATX', has_statx)
|
||||
config_host_data.set('CONFIG_FUSE', fuse.found())
|
||||
config_host_data.set('CONFIG_FUSE_LSEEK', fuse_lseek.found())
|
||||
config_host_data.set('QEMU_VERSION', '"@0@"'.format(meson.project_version()))
|
||||
config_host_data.set('QEMU_VERSION_MAJOR', meson.project_version().split('.')[0])
|
||||
config_host_data.set('QEMU_VERSION_MINOR', meson.project_version().split('.')[1])
|
||||
@ -2208,6 +2232,8 @@ endif
|
||||
summary_info += {'thread sanitizer': config_host.has_key('CONFIG_TSAN')}
|
||||
summary_info += {'rng-none': config_host.has_key('CONFIG_RNG_NONE')}
|
||||
summary_info += {'Linux keyring': config_host.has_key('CONFIG_SECRET_KEYRING')}
|
||||
summary_info += {'FUSE exports': fuse.found()}
|
||||
summary_info += {'FUSE lseek': fuse_lseek.found()}
|
||||
summary(summary_info, bool_yn: true)
|
||||
|
||||
if not supported_cpus.contains(cpu)
|
||||
|
@ -66,6 +66,10 @@ option('virtiofsd', type: 'feature', value: 'auto',
|
||||
description: 'build virtiofs daemon (virtiofsd)')
|
||||
option('vhost_user_blk_server', type: 'feature', value: 'auto',
|
||||
description: 'build vhost-user-blk server')
|
||||
option('fuse', type: 'feature', value: 'auto',
|
||||
description: 'FUSE block device export')
|
||||
option('fuse_lseek', type : 'feature', value : 'auto',
|
||||
description: 'SEEK_HOLE/SEEK_DATA support for FUSE exports')
|
||||
|
||||
option('capstone', type: 'combo', value: 'auto',
|
||||
choices: ['disabled', 'enabled', 'auto', 'system', 'internal'],
|
||||
|
@ -53,6 +53,11 @@ static void can_host_connect(CanHostState *ch, Error **errp)
|
||||
CanHostClass *chc = CAN_HOST_GET_CLASS(ch);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (ch->bus == NULL) {
|
||||
error_setg(errp, "'canbus' property not set");
|
||||
return;
|
||||
}
|
||||
|
||||
chc->connect(ch, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
|
@ -120,6 +120,25 @@
|
||||
'*logical-block-size': 'size',
|
||||
'*num-queues': 'uint16'} }
|
||||
|
||||
##
|
||||
# @BlockExportOptionsFuse:
|
||||
#
|
||||
# Options for exporting a block graph node on some (file) mountpoint
|
||||
# as a raw image.
|
||||
#
|
||||
# @mountpoint: Path on which to export the block device via FUSE.
|
||||
# This must point to an existing regular file.
|
||||
#
|
||||
# @growable: Whether writes beyond the EOF should grow the block node
|
||||
# accordingly. (default: false)
|
||||
#
|
||||
# Since: 6.0
|
||||
##
|
||||
{ 'struct': 'BlockExportOptionsFuse',
|
||||
'data': { 'mountpoint': 'str',
|
||||
'*growable': 'bool' },
|
||||
'if': 'defined(CONFIG_FUSE)' }
|
||||
|
||||
##
|
||||
# @NbdServerAddOptions:
|
||||
#
|
||||
@ -222,11 +241,13 @@
|
||||
#
|
||||
# @nbd: NBD export
|
||||
# @vhost-user-blk: vhost-user-blk export (since 5.2)
|
||||
# @fuse: FUSE export (since: 6.0)
|
||||
#
|
||||
# Since: 4.2
|
||||
##
|
||||
{ 'enum': 'BlockExportType',
|
||||
'data': [ 'nbd', 'vhost-user-blk' ] }
|
||||
'data': [ 'nbd', 'vhost-user-blk',
|
||||
{ 'name': 'fuse', 'if': 'defined(CONFIG_FUSE)' } ] }
|
||||
|
||||
##
|
||||
# @BlockExportOptions:
|
||||
@ -267,7 +288,9 @@
|
||||
'discriminator': 'type',
|
||||
'data': {
|
||||
'nbd': 'BlockExportOptionsNbd',
|
||||
'vhost-user-blk': 'BlockExportOptionsVhostUserBlk'
|
||||
'vhost-user-blk': 'BlockExportOptionsVhostUserBlk',
|
||||
'fuse': { 'type': 'BlockExportOptionsFuse',
|
||||
'if': 'defined(CONFIG_FUSE)' }
|
||||
} }
|
||||
|
||||
##
|
||||
|
@ -314,6 +314,9 @@ int main(int argc, char *argv[])
|
||||
main_loop_wait(false);
|
||||
}
|
||||
|
||||
bdrv_drain_all_begin();
|
||||
bdrv_close_all();
|
||||
|
||||
monitor_cleanup();
|
||||
qemu_chr_cleanup();
|
||||
user_creatable_cleanup();
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.pattern
|
||||
|
||||
_supported_fmt raw qcow2 qed luks
|
||||
_supported_proto file sheepdog rbd nfs
|
||||
_supported_proto file sheepdog rbd nfs fuse
|
||||
|
||||
echo "=== Creating image"
|
||||
echo
|
||||
|
@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# Currently only qcow2 supports rebasing
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_default_cache_mode writethrough
|
||||
_supported_cache_modes writethrough none
|
||||
# The refcount table tests expect a certain minimum width for refcount entries
|
||||
|
@ -46,7 +46,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
# Any format supporting backing files except vmdk and qcow which do not support
|
||||
# smaller backing files.
|
||||
_supported_fmt qcow2 qed
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
# Choose a size that is not necessarily a cluster size multiple for image
|
||||
@ -116,16 +116,10 @@ else
|
||||
QEMU_COMM_TIMEOUT=1
|
||||
fi
|
||||
|
||||
# Silence output since it contains the disk image path and QEMU's readline
|
||||
# character echoing makes it very hard to filter the output. Plus, there
|
||||
# is no telling how many times the command will repeat before succeeding.
|
||||
# (Note that creating the image results in a "Formatting..." message over
|
||||
# stdout, which is the same channel the monitor uses. We cannot reliably
|
||||
# wait for it because the monitor output may interact with it in such a
|
||||
# way that _timed_wait_for cannot read it. However, once the block job is
|
||||
# done, we know that the "Formatting..." message must have appeared
|
||||
# already, so the output is still deterministic.)
|
||||
silent=y _send_qemu_cmd $h "drive_backup disk ${TEST_IMG}.copy" "(qemu)"
|
||||
TEST_IMG="$TEST_IMG.copy" _make_test_img $image_size
|
||||
_send_qemu_cmd $h "drive_backup -n disk ${TEST_IMG}.copy" "(qemu)" \
|
||||
| _filter_imgfmt
|
||||
|
||||
silent=y qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs"
|
||||
_send_qemu_cmd $h "info block-jobs" "No active jobs"
|
||||
_send_qemu_cmd $h 'quit' ""
|
||||
|
@ -468,6 +468,9 @@ No errors were found on the image.
|
||||
|
||||
block-backup
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT.copy', fmt=IMGFMT size=4294968832
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) drive_backup -n disk TEST_DIR/t.IMGFMT.copy
|
||||
(qemu) info block-jobs
|
||||
No active jobs
|
||||
=== IO: pattern 195
|
||||
|
@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# This tests qcow2-specific low-level functionality
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
# We want to test compat=0.10, which does not support external data
|
||||
# files or refcount widths other than 16
|
||||
_unsupported_imgopts data_file 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow qcow2 vmdk qed
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
_unsupported_imgopts "subformat=monolithicFlat" \
|
||||
"subformat=twoGbMaxExtentFlat" \
|
||||
|
@ -42,7 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# This tests qcow2-specific low-level functionality
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
# Only qcow2v3 and later supports feature bits;
|
||||
# qcow2.py does not support external data files;
|
||||
# this test requires a cluster size large enough for the feature table
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow qcow2 vmdk qed
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_unsupported_imgopts "subformat=monolithicFlat" \
|
||||
"subformat=twoGbMaxExtentFlat" \
|
||||
"subformat=twoGbMaxExtentSparse" \
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2 qed
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
CLUSTER_SIZE=2M
|
||||
|
@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
_default_cache_mode writethrough
|
||||
_supported_cache_modes writethrough
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
# data_file does not support compressed clusters
|
||||
_unsupported_imgopts data_file
|
||||
|
||||
@ -47,6 +47,8 @@ size=128M
|
||||
echo
|
||||
echo "== creating backing file for COW tests =="
|
||||
|
||||
TEST_IMG_SAVE=$TEST_IMG
|
||||
TEST_IMG="$TEST_IMG.base"
|
||||
_make_test_img $size
|
||||
|
||||
backing_io()
|
||||
@ -67,8 +69,7 @@ backing_io()
|
||||
|
||||
backing_io 0 32 write | $QEMU_IO "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
mv "$TEST_IMG" "$TEST_IMG.base"
|
||||
|
||||
TEST_IMG=$TEST_IMG_SAVE
|
||||
_make_test_img -b "$TEST_IMG.base" -F $IMGFMT 6G
|
||||
|
||||
echo
|
||||
|
@ -1,7 +1,7 @@
|
||||
QA output created by 046
|
||||
|
||||
== creating backing file for COW tests ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 65536/65536 bytes at offset 65536
|
||||
|
@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2 qed
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
echo
|
||||
echo "== Creating images =="
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
echo
|
||||
echo "creating too large image (1 EB)"
|
||||
|
@ -46,7 +46,7 @@ _filter_io_error()
|
||||
|
||||
# This tests qcow2-specific low-level functionality
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
# These tests only work for compat=1.1 images without an external
|
||||
# data file with refcount_bits=16
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_require_drivers blkdebug blkverify
|
||||
# blkdebug can only inject errors on bs->file, not on the data_file,
|
||||
# so thie test does not work with external data files
|
||||
@ -61,8 +61,17 @@ echo
|
||||
echo "=== Testing blkverify through filename ==="
|
||||
echo
|
||||
|
||||
TEST_IMG="$TEST_IMG.base" IMGFMT="raw" _make_test_img --no-opts $IMG_SIZE |\
|
||||
_filter_imgfmt
|
||||
# _make_test_img may set variables that we need to retain. Everything
|
||||
# in a pipe is executed in a subshell, so doing so would throw away
|
||||
# all changes. Therefore, we have to store the output in some temp
|
||||
# file and filter that.
|
||||
scratch_out="$TEST_DIR/img-create.out"
|
||||
|
||||
TEST_IMG="$TEST_IMG.base" IMGFMT="raw" _make_test_img --no-opts $IMG_SIZE \
|
||||
>"$scratch_out"
|
||||
_filter_imgfmt <"$scratch_out"
|
||||
rm -f "$scratch_out"
|
||||
|
||||
_make_test_img $IMG_SIZE
|
||||
$QEMU_IO -c "open -o driver=raw,file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \
|
||||
-c 'read 0 512' -c 'write -P 42 0x38000 512' -c 'read -P 42 0x38000 512' | _filter_qemu_io
|
||||
@ -76,8 +85,10 @@ echo
|
||||
echo "=== Testing blkverify through file blockref ==="
|
||||
echo
|
||||
|
||||
TEST_IMG="$TEST_IMG.base" IMGFMT="raw" _make_test_img --no-opts $IMG_SIZE |\
|
||||
_filter_imgfmt
|
||||
TEST_IMG="$TEST_IMG.base" IMGFMT="raw" _make_test_img --no-opts $IMG_SIZE \
|
||||
>"$scratch_out"
|
||||
_filter_imgfmt <"$scratch_out"
|
||||
|
||||
_make_test_img $IMG_SIZE
|
||||
$QEMU_IO -c "open -o driver=raw,file.driver=blkverify,file.raw.filename=$TEST_IMG.base,file.test.driver=$IMGFMT,file.test.file.filename=$TEST_IMG" \
|
||||
-c 'read 0 512' -c 'write -P 42 0x38000 512' -c 'read -P 42 0x38000 512' | _filter_qemu_io
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file nfs
|
||||
_supported_proto file nfs fuse
|
||||
|
||||
# Some containers (e.g. non-x86 on Travis) do not allow large files
|
||||
_require_large_file 4G
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
# - Internal snapshots are (currently) impossible with refcount_bits=1,
|
||||
# and generally impossible with external data files
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
# Because anything other than 16 would change the output of qemu_io -c info
|
||||
_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
|
||||
|
||||
@ -62,7 +62,8 @@ TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE
|
||||
$QEMU_IO -c 'write -P 42 0 512' -c 'write -P 23 512 512' \
|
||||
-c 'write -P 66 1024 512' "$TEST_IMG.base" | _filter_qemu_io
|
||||
|
||||
$QEMU_IMG convert -f raw -O $IMGFMT "$TEST_IMG.base" "$TEST_IMG"
|
||||
_make_test_img $IMG_SIZE
|
||||
$QEMU_IMG convert -f raw -O $IMGFMT -n "$TEST_IMG.base" "$TEST_IMG"
|
||||
|
||||
$QEMU_IO_PROG --cache $CACHEMODE --aio $AIOMODE \
|
||||
-c 'read -P 42 0 512' -c 'read -P 23 512 512' \
|
||||
|
@ -9,6 +9,7 @@ wrote 512/512 bytes at offset 512
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote 512/512 bytes at offset 1024
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
read 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 512/512 bytes at offset 512
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file nfs
|
||||
_supported_proto file nfs fuse
|
||||
# External data files do not support compressed clusters
|
||||
_unsupported_imgopts data_file
|
||||
|
||||
|
@ -44,7 +44,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
_supported_cache_modes writethrough none writeback
|
||||
_default_cache_mode none writeback
|
||||
@ -96,7 +96,8 @@ _send_qemu_cmd $h2 'qemu-io disk flush' "(qemu)"
|
||||
_send_qemu_cmd $h2 'quit' ""
|
||||
_send_qemu_cmd $h1 'quit' ""
|
||||
|
||||
wait
|
||||
wait=yes _cleanup_qemu >/dev/null
|
||||
|
||||
echo "Check image pattern"
|
||||
${QEMU_IO} -c "read -P 0x22 0 4M" "${TEST_IMG}" | _filter_testdir | _filter_qemu_io
|
||||
|
||||
|
@ -44,7 +44,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
size_smaller=5M
|
||||
size_larger=100M
|
||||
|
@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# Any format supporting backing files and bdrv_make_empty
|
||||
_supported_fmt qcow qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
|
||||
|
@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.pattern
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
# The code path we want to test here only works for compat=1.1 images;
|
||||
# blkdebug can only inject errors on bs->file, so external data files
|
||||
# do not work with this test
|
||||
|
@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
IMG_SIZE=64K
|
||||
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file nfs
|
||||
_supported_proto file nfs fuse
|
||||
# Internal snapshots are (currently) impossible with refcount_bits=1,
|
||||
# and generally impossible with external data files
|
||||
_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt raw
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
# in kB
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file nfs
|
||||
_supported_proto file nfs fuse
|
||||
|
||||
|
||||
IMG_SIZE=64K
|
||||
|
@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# This tests qcow2-specific low-level functionality
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
# This test directly modifies a refblock so it relies on refcount_bits being 16;
|
||||
# and the low-level modification it performs are not tuned for external data
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qed qcow qcow2 vmdk
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
|
||||
|
||||
_make_test_img -b "$TEST_IMG.inexistent"
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# This tests qcow2-specific low-level functionality
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
# This test will set refcount_bits on its own which would conflict with the
|
||||
# manual setting; compat will be overridden as well;
|
||||
# and external data files do not work well with our refcount testing
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
# This test relies on refcounts being 64 bits wide (which does not work with
|
||||
# compat=0.10)
|
||||
_unsupported_imgopts 'refcount_bits=\([^6]\|.\([^4]\|$\)\)' 'compat=0.10'
|
||||
|
@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
_make_test_img 64k
|
||||
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt generic
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_unsupported_fmt luks
|
||||
_require_drivers raw
|
||||
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
# Refcount structures are used much differently with external data
|
||||
# files
|
||||
|
@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
_require_devices virtio-scsi scsi-hd
|
||||
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img 64M
|
||||
_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
# We are going to use lazy-refcounts
|
||||
_unsupported_imgopts 'compat=0.10'
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# This tests qcow2-specific low-level functionality
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
# With an external data file, data clusters are not refcounted
|
||||
# (so qemu-img check would not do much);
|
||||
|
@ -44,7 +44,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt generic
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
_make_test_img 64k
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
CLUSTER_SIZE=4k
|
||||
|
@ -42,15 +42,15 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# Any format implementing BlockDriver.bdrv_change_backing_file
|
||||
_supported_fmt qcow2 qed
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
IMG_SIZE=1M
|
||||
|
||||
# Create the images
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE | _filter_imgfmt
|
||||
TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT | _filter_imgfmt
|
||||
_make_test_img -b "$TEST_IMG.int" -F $IMGFMT -F $IMGFMT | _filter_imgfmt
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE
|
||||
TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT
|
||||
_make_test_img -b "$TEST_IMG.int" -F $IMGFMT -F $IMGFMT
|
||||
|
||||
# First test: reopen $TEST.IMG changing the detect-zeroes option on
|
||||
# its backing file ($TEST_IMG.int).
|
||||
@ -105,9 +105,9 @@ echo
|
||||
echo "*** Commit and then change an option on the backing file"
|
||||
echo
|
||||
# Create the images again
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE | _filter_imgfmt
|
||||
TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT| _filter_imgfmt
|
||||
_make_test_img -b "$TEST_IMG.int" -F $IMGFMT | _filter_imgfmt
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE
|
||||
TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT
|
||||
_make_test_img -b "$TEST_IMG.int" -F $IMGFMT
|
||||
|
||||
_launch_qemu -drive if=none,file="${TEST_IMG}"
|
||||
_send_qemu_cmd $QEMU_HANDLE \
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt raw
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
|
||||
|
@ -40,7 +40,15 @@ _unsupported_fmt raw
|
||||
|
||||
|
||||
size=256K
|
||||
IMGFMT=raw IMGKEYSECRET= _make_test_img --no-opts $size | _filter_imgfmt
|
||||
|
||||
# _make_test_img may set variables that we need to retain. Everything
|
||||
# in a pipe is executed in a subshell, so doing so would throw away
|
||||
# all changes. Therefore, we have to store the output in some temp
|
||||
# file and filter that.
|
||||
scratch_out="$TEST_DIR/img-create.out"
|
||||
IMGFMT=raw IMGKEYSECRET= _make_test_img --no-opts $size >"$scratch_out"
|
||||
_filter_imgfmt <"$scratch_out"
|
||||
rm -f "$scratch_out"
|
||||
|
||||
echo
|
||||
echo "== reading wrong format should fail =="
|
||||
|
@ -71,7 +71,7 @@ EOF
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt raw
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
_default_cache_mode none
|
||||
@ -89,20 +89,20 @@ min_blocks=$(stat -c '%b' "$TEST_DIR/empty")
|
||||
|
||||
echo
|
||||
echo "== creating image with default preallocation =="
|
||||
_make_test_img -o extent_size_hint=0 $size | _filter_imgfmt
|
||||
_make_test_img -o extent_size_hint=0 $size
|
||||
stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size
|
||||
|
||||
for mode in off full falloc; do
|
||||
echo
|
||||
echo "== creating image with preallocation $mode =="
|
||||
_make_test_img -o preallocation=$mode,extent_size_hint=0 $size | _filter_imgfmt
|
||||
_make_test_img -o preallocation=$mode,extent_size_hint=0 $size
|
||||
stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size
|
||||
done
|
||||
|
||||
for new_size in 4096 1048576; do
|
||||
echo
|
||||
echo "== resize empty image with block_resize =="
|
||||
_make_test_img -o extent_size_hint=0 0 | _filter_imgfmt
|
||||
_make_test_img -o extent_size_hint=0 0
|
||||
_block_resize $TEST_IMG $new_size >/dev/null
|
||||
stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $new_size
|
||||
done
|
||||
|
@ -45,7 +45,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# This test is specific to qcow2
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
# Persistent dirty bitmaps require compat=1.1;
|
||||
# Internal snapshots forbid using an external data file
|
||||
|
@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
# tests specific to compat=1.1.
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
CLUSTER_SIZE=1M
|
||||
size=128M
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
# v2 images can't mark clusters as zero
|
||||
|
@ -44,7 +44,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
_supported_os Linux FreeBSD NetBSD
|
||||
_supported_fmt qcow2 raw qed quorum
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
size=64M
|
||||
_make_test_img $size
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_require_drivers null-co
|
||||
|
||||
if [ "$QEMU_DEFAULT_MACHINE" != "pc" ]; then
|
||||
|
@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
size=64M
|
||||
_make_test_img $size
|
||||
|
@ -42,7 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
# An external data file would change the query-named-block-nodes output
|
||||
_unsupported_imgopts data_file
|
||||
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
do_run_qemu()
|
||||
{
|
||||
|
@ -42,10 +42,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2 qed
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
BACKING_IMG="${TEST_DIR}/backing.img"
|
||||
TEST_IMG="${TEST_DIR}/test.img"
|
||||
BACKING_IMG="$TEST_IMG.base"
|
||||
|
||||
TEST_IMG="$BACKING_IMG" _make_test_img 512M
|
||||
_make_test_img -F $IMGFMT -b "$BACKING_IMG" 512M
|
||||
|
@ -1,6 +1,6 @@
|
||||
QA output created by 200
|
||||
Formatting 'TEST_DIR/backing.img', fmt=IMGFMT size=536870912
|
||||
Formatting 'TEST_DIR/test.img', fmt=IMGFMT size=536870912 backing_file=TEST_DIR/backing.img backing_fmt=IMGFMT
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=536870912
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=536870912 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
|
||||
wrote 314572800/314572800 bytes at offset 512
|
||||
300 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
# This test assumes that discard leaves zero clusters; see test 177 for
|
||||
# other tests that also work in older images
|
||||
_unsupported_imgopts 'compat=0.10'
|
||||
|
@ -23,7 +23,8 @@
|
||||
import iotests
|
||||
from iotests import imgfmt
|
||||
|
||||
iotests.script_initialize(supported_fmts=['qcow2'])
|
||||
iotests.script_initialize(supported_fmts=['qcow2'],
|
||||
supported_protocols=['file'])
|
||||
iotests.verify_working_luks()
|
||||
|
||||
with iotests.FilePath('t.qcow2') as disk_path, \
|
||||
@ -201,7 +202,7 @@ with iotests.FilePath('t.qcow2') as disk_path, \
|
||||
|
||||
vm.launch()
|
||||
for size in [ 1234, 18446744073709551104, 9223372036854775808,
|
||||
9223372036854775296 ]:
|
||||
9223372036854775296, 9223372035781033984 ]:
|
||||
vm.blockdev_create({ 'driver': imgfmt,
|
||||
'file': 'node0',
|
||||
'size': size })
|
||||
|
@ -180,6 +180,12 @@ Job failed: Could not resize image: Image size cannot be negative
|
||||
|
||||
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775296}}}
|
||||
{"return": {}}
|
||||
Job failed: Could not resize image: Required too big image size, it must be not greater than 9223372035781033984
|
||||
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
|
||||
{"return": {}}
|
||||
|
||||
{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372035781033984}}}
|
||||
{"return": {}}
|
||||
Job failed: Could not resize image: Failed to grow the L1 table: File too large
|
||||
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
|
||||
{"return": {}}
|
||||
|
@ -35,7 +35,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
# Repairing the corrupted image requires qemu-img check to store a
|
||||
# refcount up to 3, which requires at least two refcount bits.
|
||||
|
@ -36,7 +36,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# This test is specific to qcow2
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
# This test needs clusters with at least a refcount of 2 so that
|
||||
# OFLAG_COPIED is not set. refcount_bits=1 is therefore unsupported.
|
||||
|
@ -35,7 +35,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.pattern
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
# To use a different refcount width but 16 bits we need compat=1.1,
|
||||
# and external data files do not support compressed clusters.
|
||||
|
@ -35,7 +35,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt raw
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
_default_cache_mode writeback
|
||||
@ -46,6 +46,13 @@ echo "=== Check mapping of unaligned raw image ==="
|
||||
echo
|
||||
|
||||
_make_test_img 65537 # qemu-img create rounds size up
|
||||
|
||||
# file-posix allocates the first block of any images when it is created;
|
||||
# the size of this block depends on the host page size and the file
|
||||
# system block size, none of which are constant. Discard the whole
|
||||
# image so we will not see this allocation in qemu-img map's output.
|
||||
$QEMU_IO -c 'discard 0 65537' "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
|
||||
|
||||
truncate --size=65537 "$TEST_IMG" # so we resize it and check again
|
||||
|
@ -3,18 +3,16 @@ QA output created by 221
|
||||
=== Check mapping of unaligned raw image ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65537
|
||||
[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 4096, "length": 61952, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 4096, "length": 61952, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
discard 65537/65537 bytes at offset 0
|
||||
64.001 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 66048, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
[{ "start": 0, "length": 66048, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
wrote 1/1 bytes at offset 65536
|
||||
1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 4096, "length": 61440, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
[{ "start": 0, "length": 65536, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 65536, "length": 1, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 65537, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 4096, "length": 61440, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
[{ "start": 0, "length": 65536, "depth": 0, "zero": true, "data": false, "offset": OFFSET},
|
||||
{ "start": 65536, "length": 1, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 65537, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
*** done
|
||||
|
@ -44,15 +44,14 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# Needs backing file and backing format support
|
||||
_supported_fmt qcow2 qed
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
# blkdebug can only inject errors on bs->file, so external data files
|
||||
# do not work with this test
|
||||
_unsupported_imgopts data_file
|
||||
|
||||
|
||||
DEST_IMG="$TEST_DIR/d.$IMGFMT"
|
||||
TEST_IMG="$TEST_DIR/b.$IMGFMT"
|
||||
DEST_IMG="$TEST_IMG.dest"
|
||||
BLKDEBUG_CONF="$TEST_DIR/blkdebug.conf"
|
||||
|
||||
_make_test_img 2M
|
||||
|
@ -1,6 +1,6 @@
|
||||
QA output created by 229
|
||||
Formatting 'TEST_DIR/b.IMGFMT', fmt=IMGFMT size=2097152
|
||||
Formatting 'TEST_DIR/d.IMGFMT', fmt=IMGFMT size=2097152
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2097152
|
||||
Formatting 'TEST_DIR/t.IMGFMT.dest', fmt=IMGFMT size=2097152
|
||||
wrote 2097152/2097152 bytes at offset 0
|
||||
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
{'execute': 'qmp_capabilities'}
|
||||
@ -8,7 +8,7 @@ wrote 2097152/2097152 bytes at offset 0
|
||||
|
||||
=== Starting drive-mirror, causing error & stop ===
|
||||
|
||||
{'execute': 'drive-mirror', 'arguments': {'device': 'testdisk', 'format': 'IMGFMT', 'target': 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/d.IMGFMT', 'sync': 'full', 'mode': 'existing', 'on-source-error': 'stop', 'on-target-error': 'stop' }}
|
||||
{'execute': 'drive-mirror', 'arguments': {'device': 'testdisk', 'format': 'IMGFMT', 'target': 'blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT.dest', 'sync': 'full', 'mode': 'existing', 'on-source-error': 'stop', 'on-target-error': 'stop' }}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "testdisk"}}
|
||||
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "testdisk"}}
|
||||
{"return": {}}
|
||||
|
@ -24,7 +24,8 @@ import struct
|
||||
from iotests import qemu_img_create, qemu_io, qemu_img_pipe, \
|
||||
file_path, img_info_log, log, filter_qemu_io
|
||||
|
||||
iotests.script_initialize(supported_fmts=['qcow2'])
|
||||
iotests.script_initialize(supported_fmts=['qcow2'],
|
||||
supported_protocols=['file'])
|
||||
|
||||
disk = file_path('disk')
|
||||
chunk = 256 * 1024
|
||||
|
@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# Requires backing files and .bdrv_change_backing_file support
|
||||
_supported_fmt qcow2 qed
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
size=128M
|
||||
|
||||
|
@ -42,15 +42,15 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# Any format implementing BlockDriver.bdrv_change_backing_file
|
||||
_supported_fmt qcow2 qed
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
IMG_SIZE=1M
|
||||
|
||||
# Create the images: base <- int <- active
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE | _filter_imgfmt
|
||||
TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT | _filter_imgfmt
|
||||
_make_test_img -b "$TEST_IMG.int" -F $IMGFMT | _filter_imgfmt
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE
|
||||
TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT
|
||||
_make_test_img -b "$TEST_IMG.int" -F $IMGFMT
|
||||
|
||||
# Launch QEMU with these two drives:
|
||||
# none0: base (read-only)
|
||||
|
@ -37,7 +37,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
# This test does not make much sense with external data files
|
||||
_unsupported_imgopts data_file
|
||||
|
@ -42,7 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
# zero cluster support
|
||||
_supported_fmt qcow2
|
||||
_unsupported_imgopts 'compat=0.10'
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
CLUSTER_SIZE=65536
|
||||
|
@ -35,7 +35,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# qcow2-specific test
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
|
||||
echo '--- Writing to the image ---'
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
echo
|
||||
echo "== Required alignment larger than cluster size =="
|
||||
|
@ -35,7 +35,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# This is a qcow2 regression test
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
|
||||
# External data files do not support compression;
|
||||
# We need an exact cluster size (2M) and refcount width (2) so we can
|
||||
|
@ -35,7 +35,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# This is a qcow2 regression test
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
# External data files would add nodes to the block graph, so it would
|
||||
# not match the reference output
|
||||
|
@ -36,7 +36,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# Backing files are required...
|
||||
_supported_fmt qcow qcow2 vmdk qed
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
_unsupported_imgopts "subformat=monolithicFlat" \
|
||||
"subformat=twoGbMaxExtentFlat" \
|
||||
|
@ -35,7 +35,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.qemu
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
# Internal snapshots are (currently) impossible with refcount_bits=1,
|
||||
# and generally impossible with external data files
|
||||
_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file
|
||||
|
@ -32,7 +32,7 @@ status=1 # failure is the default!
|
||||
|
||||
# This tests qocw2-specific low-level functionality
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
_supported_os Linux
|
||||
_unsupported_imgopts 'compat=0.10' data_file
|
||||
|
||||
@ -51,8 +51,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
CLUSTER_SIZE=65536
|
||||
|
||||
# Check if we can run this test.
|
||||
if IMGOPTS='compression_type=zstd' _make_test_img 64M |
|
||||
grep "Invalid parameter 'zstd'"; then
|
||||
output=$(_make_test_img -o 'compression_type=zstd' 64M; _cleanup_test_img)
|
||||
if echo "$output" | grep -q "Invalid parameter 'zstd'"; then
|
||||
_notrun "ZSTD is disabled"
|
||||
fi
|
||||
|
||||
|
@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
. ./common.pattern
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_proto file fuse
|
||||
# This is a v3-exclusive test;
|
||||
# As for data_file, error paths often very much depend on whether
|
||||
# there is an external data file or not; so we create one exactly when
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user