/* check if the path starts with "<protocol>:" */
static int path_has_protocol(const char *path)
{
+ const char *p;
+
#ifdef _WIN32
if (is_windows_drive(path) ||
is_windows_drive_prefix(path)) {
return 0;
}
+ p = path + strcspn(path, ":/\\");
+#else
+ p = path + strcspn(path, ":/");
#endif
- return strchr(path, ':') != NULL;
+ return *p == ':';
}
int path_is_absolute(const char *path)
{
- const char *p;
#ifdef _WIN32
/* specific case for names like: "\\.\d:" */
- if (*path == '/' || *path == '\\')
+ if (is_windows_drive(path) || is_windows_drive_prefix(path)) {
return 1;
-#endif
- p = strchr(path, ':');
- if (p)
- p++;
- else
- p = path;
-#ifdef _WIN32
- return (*p == '/' || *p == '\\');
+ }
+ return (*path == '/' || *path == '\\');
#else
- return (*p == '/');
+ return (*path == '/');
#endif
}
}
}
+void bdrv_get_full_backing_filename(BlockDriverState *bs, char *dest, size_t sz)
+{
+ if (bs->backing_file[0] == '\0' || path_has_protocol(bs->backing_file)) {
+ pstrcpy(dest, sz, bs->backing_file);
+ } else {
+ path_combine(dest, sz, bs->filename, bs->backing_file);
+ }
+}
+
void bdrv_register(BlockDriver *bdrv)
{
/* Block drivers without coroutine functions need emulation */
return bdrv_create(drv, filename, options);
}
-#ifdef _WIN32
-void get_tmp_filename(char *filename, int size)
+/*
+ * Create a uniquely-named empty temporary file.
+ * Return 0 upon success, otherwise a negative errno value.
+ */
+int get_tmp_filename(char *filename, int size)
{
+#ifdef _WIN32
char temp_dir[MAX_PATH];
-
- GetTempPath(MAX_PATH, temp_dir);
- GetTempFileName(temp_dir, "qem", 0, filename);
-}
+ /* GetTempFileName requires that its output buffer (4th param)
+ have length MAX_PATH or greater. */
+ assert(size >= MAX_PATH);
+ return (GetTempPath(MAX_PATH, temp_dir)
+ && GetTempFileName(temp_dir, "qem", 0, filename)
+ ? 0 : -GetLastError());
#else
-void get_tmp_filename(char *filename, int size)
-{
int fd;
const char *tmpdir;
- /* XXX: race condition possible */
tmpdir = getenv("TMPDIR");
if (!tmpdir)
tmpdir = "/tmp";
- snprintf(filename, size, "%s/vl.XXXXXX", tmpdir);
+ if (snprintf(filename, size, "%s/vl.XXXXXX", tmpdir) >= size) {
+ return -EOVERFLOW;
+ }
fd = mkstemp(filename);
- close(fd);
-}
+ if (fd < 0 || close(fd)) {
+ return -errno;
+ }
+ return 0;
#endif
+}
/*
* Detect host devices. By convention, /dev/cdrom[N] is always
int ret, open_flags;
assert(drv != NULL);
+ assert(bs->file == NULL);
trace_bdrv_open_common(bs, filename, flags, drv->format_name);
- bs->file = NULL;
- bs->total_sectors = 0;
- bs->encrypted = 0;
- bs->valid_key = 0;
- bs->sg = 0;
bs->open_flags = flags;
- bs->growable = 0;
bs->buffer_alignment = 512;
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
}
pstrcpy(bs->filename, sizeof(bs->filename), filename);
- bs->backing_file[0] = '\0';
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) {
return -ENOTSUP;
bdrv_delete(bs1);
- get_tmp_filename(tmp_filename, sizeof(tmp_filename));
+ ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename));
+ if (ret < 0) {
+ return ret;
+ }
/* Real path is meaningless for protocols */
if (is_protocol)
BlockDriver *back_drv = NULL;
bs->backing_hd = bdrv_new("");
-
- if (path_has_protocol(bs->backing_file)) {
- pstrcpy(backing_filename, sizeof(backing_filename),
- bs->backing_file);
- } else {
- path_combine(backing_filename, sizeof(backing_filename),
- filename, bs->backing_file);
- }
+ bdrv_get_full_backing_filename(bs, backing_filename,
+ sizeof(backing_filename));
if (bs->backing_format[0] != '\0') {
back_drv = bdrv_find_format(bs->backing_format);
bs->opaque = NULL;
bs->drv = NULL;
bs->copy_on_read = 0;
+ bs->backing_file[0] = '\0';
+ bs->backing_format[0] = '\0';
+ bs->total_sectors = 0;
+ bs->encrypted = 0;
+ bs->valid_key = 0;
+ bs->sg = 0;
+ bs->growable = 0;
if (bs->file != NULL) {
- bdrv_close(bs->file);
+ bdrv_delete(bs->file);
+ bs->file = NULL;
}
bdrv_dev_change_media_cb(bs, false);
bs->device_name[0] = '\0';
}
+static void bdrv_rebind(BlockDriverState *bs)
+{
+ if (bs->drv && bs->drv->bdrv_rebind) {
+ bs->drv->bdrv_rebind(bs);
+ }
+}
+
/*
* Add new bs contents at the top of an image chain while the chain is
* live, while keeping required fields on the top layer.
tmp = *bs_new;
/* there are some fields that need to stay on the top layer: */
+ tmp.open_flags = bs_top->open_flags;
/* dev info */
tmp.dev_ops = bs_top->dev_ops;
bs_new->slice_time = 0;
bs_new->slice_start = 0;
bs_new->slice_end = 0;
+
+ bdrv_rebind(bs_new);
+ bdrv_rebind(bs_top);
}
void bdrv_delete(BlockDriverState *bs)
bdrv_make_anon(bs);
bdrv_close(bs);
- if (bs->file != NULL) {
- bdrv_delete(bs->file);
- }
assert(bs != bs_snapshots);
g_free(bs);
return bdrv_rw_co(bs, sector_num, buf, nb_sectors, false);
}
+#define BITS_PER_LONG (sizeof(unsigned long) * 8)
+
static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int dirty)
{
end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK;
for (; start <= end; start++) {
- idx = start / (sizeof(unsigned long) * 8);
- bit = start % (sizeof(unsigned long) * 8);
+ idx = start / BITS_PER_LONG;
+ bit = start % BITS_PER_LONG;
val = bs->dirty_bitmap[idx];
if (dirty) {
if (!(val & (1UL << bit))) {
if (enable) {
if (!bs->dirty_bitmap) {
bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) +
- BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
- bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8;
+ BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG - 1;
+ bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG;
- bs->dirty_bitmap = g_malloc0(bitmap_size);
+ bs->dirty_bitmap = g_new0(unsigned long, bitmap_size);
}
} else {
if (bs->dirty_bitmap) {
job->bs = bs;
job->cb = cb;
job->opaque = opaque;
+ job->busy = true;
bs->job = job;
/* Only set speed when necessary to avoid NotSupported error */
void block_job_cancel(BlockJob *job)
{
job->cancelled = true;
+ if (job->co && !job->busy) {
+ qemu_coroutine_enter(job->co, NULL);
+ }
}
bool block_job_is_cancelled(BlockJob *job)
return job->cancelled;
}
-void block_job_cancel_sync(BlockJob *job)
+struct BlockCancelData {
+ BlockJob *job;
+ BlockDriverCompletionFunc *cb;
+ void *opaque;
+ bool cancelled;
+ int ret;
+};
+
+static void block_job_cancel_cb(void *opaque, int ret)
+{
+ struct BlockCancelData *data = opaque;
+
+ data->cancelled = block_job_is_cancelled(data->job);
+ data->ret = ret;
+ data->cb(data->opaque, ret);
+}
+
+int block_job_cancel_sync(BlockJob *job)
{
+ struct BlockCancelData data;
BlockDriverState *bs = job->bs;
assert(bs->job == job);
+
+ /* Set up our own callback to store the result and chain to
+ * the original callback.
+ */
+ data.job = job;
+ data.cb = job->cb;
+ data.opaque = job->opaque;
+ data.ret = -EINPROGRESS;
+ job->cb = block_job_cancel_cb;
+ job->opaque = &data;
block_job_cancel(job);
- while (bs->job != NULL && bs->job->busy) {
+ while (data.ret == -EINPROGRESS) {
qemu_aio_wait();
}
+ return (data.cancelled && data.ret == 0) ? -ECANCELED : data.ret;
+}
+
+void block_job_sleep_ns(BlockJob *job, QEMUClock *clock, int64_t ns)
+{
+ /* Check cancellation *before* setting busy = false, too! */
+ if (!block_job_is_cancelled(job)) {
+ job->busy = false;
+ co_sleep_ns(clock, ns);
+ job->busy = true;
+ }
}