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:
Peter Maydell 2020-12-12 00:20:45 +00:00
commit ad717e6da3
113 changed files with 1844 additions and 268 deletions

View File

@ -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
View File

@ -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,

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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
View 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,
};

View File

@ -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'))

View File

@ -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) {

View File

@ -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");

View File

@ -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);
}

View File

@ -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;

View File

@ -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
View File

@ -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"

View File

@ -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.
*

View File

@ -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
View 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

View File

@ -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)

View File

@ -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'],

View File

@ -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);

View File

@ -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)' }
} }
##

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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' ""

View File

@ -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

View File

@ -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]\|$\)\)'

View File

@ -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" \

View File

@ -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

View File

@ -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" \

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 =="

View File

@ -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)"

View File

@ -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

View File

@ -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

View File

@ -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

View 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
_supported_os Linux
# - Internal snapshots are (currently) impossible with refcount_bits=1,
# and generally impossible with external data files

View File

@ -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' \

View File

@ -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

View File

@ -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

View 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

View 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
size_smaller=5M
size_larger=100M

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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 =="

View File

@ -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

View File

@ -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

View 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

View 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
_supported_os Linux
# v2 images can't mark clusters as zero

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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()
{

View File

@ -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

View File

@ -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)

View File

@ -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'

View File

@ -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 })

View File

@ -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": {}}

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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": {}}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View 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

View File

@ -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 ---'

View 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
echo
echo "== Required alignment larger than cluster size =="

View File

@ -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

View File

@ -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

View File

@ -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" \

View File

@ -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

View 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

View File

@ -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