@@ -451,7 +451,6 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
if (flags & (BDRV_O_CACHE_WB|BDRV_O_NOCACHE))
bs->enable_write_cache = 1;
- bs->read_only = (flags & BDRV_O_RDWR) == 0;
if (!(flags & BDRV_O_FILE)) {
open_flags = (flags & (BDRV_O_RDWR | BDRV_O_CACHE_MASK|BDRV_O_NATIVE_AIO));
if (bs->is_temporary) { /* snapshot should be writeable */
@@ -466,6 +465,7 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
goto free_and_fail;
}
+ bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR);
if (drv->bdrv_getlength) {
bs->total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
}
@@ -482,13 +482,28 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
filename, bs->backing_file);
if (bs->backing_format[0] != '\0')
back_drv = bdrv_find_format(bs->backing_format);
+
+ open_flags &= ~BDRV_O_RDWR; /* clear RW, then restore from orig */
+ if (bs->is_temporary) {
+ open_flags |= (flags & BDRV_O_RDWR);
+ }
+
ret = bdrv_open2(bs->backing_hd, backing_filename, open_flags,
back_drv);
- bs->backing_hd->read_only = (open_flags & BDRV_O_RDWR) == 0;
+ if (ret < 0) {
+ open_flags &= ~BDRV_O_RDWR; /* Fall-back to read-only for the backing file */
+ ret = bdrv_open2(bs->backing_hd, backing_filename, open_flags,
+ back_drv);
+ }
if (ret < 0) {
bdrv_close(bs);
return ret;
}
+ if (!bs->is_temporary) {
+ bs->backing_hd->keep_read_only = bs->keep_read_only; /* base image inherits from "parent" and open read-only */
+ } else {
+ bs->backing_hd->keep_read_only = !(flags & BDRV_O_RDWR);
+ }
}
if (!bdrv_key_required(bs)) {
@@ -564,19 +579,34 @@ int bdrv_commit(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
int64_t i, total_sectors;
- int n, j;
+ int n, j, ro;
int ret = 0;
unsigned char sector[512];
+ BlockDriverState *bs_rw, *bs_ro;
if (!drv)
return -ENOMEDIUM;
+
+ if (!bs->backing_hd) {
+ return -ENOTSUP;
+ }
- if (bs->read_only) {
+ if (bs->backing_hd->keep_read_only) {
return -EACCES;
}
+
+ ro = bs->backing_hd->read_only;
- if (!bs->backing_hd) {
- return -ENOTSUP;
+ if (ro) { /* re-open as RW */
+ bs_rw = bdrv_new("");
+ ret = bdrv_open2(bs_rw, bs->backing_hd->filename, bs->backing_hd->open_flags | BDRV_O_RDWR, NULL);
+ if (ret < 0) {
+ bdrv_delete(bs_rw);
+ return -EACCES;
+ }
+ bdrv_close(bs->backing_hd);
+ qemu_free(bs->backing_hd);
+ bs->backing_hd = bs_rw;
}
total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
@@ -584,11 +614,13 @@ int bdrv_commit(BlockDriverState *bs)
if (drv->bdrv_is_allocated(bs, i, 65536, &n)) {
for(j = 0; j < n; j++) {
if (bdrv_read(bs, i, sector, 1) != 0) {
- return -EIO;
+ ret = -EIO;
+ goto ro_cleanup;
}
if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) {
- return -EIO;
+ ret = -EIO;
+ goto ro_cleanup;
}
i++;
}
@@ -608,6 +640,22 @@ int bdrv_commit(BlockDriverState *bs)
*/
if (bs->backing_hd)
bdrv_flush(bs->backing_hd);
+
+ro_cleanup:
+
+ if (ro) { /* re-open as RO */
+ bs_ro = bdrv_new("");
+ ret = bdrv_open2(bs_ro, bs->backing_hd->filename, bs->backing_hd->open_flags & ~BDRV_O_RDWR, NULL);
+ if (ret < 0) {
+ bdrv_delete(bs_ro);
+ return -EACCES;
+ }
+ bdrv_close(bs->backing_hd);
+ qemu_free(bs->backing_hd);
+ bs->backing_hd = bs_ro;
+ bs->backing_hd->keep_read_only = 0;
+ }
+
return ret;
}
@@ -130,6 +130,7 @@ struct BlockDriverState {
int64_t total_sectors; /* if we are reading a disk image, give its
size in sectors */
int read_only; /* if true, the media is read only */
+ int keep_read_only; /* if true, the media was requested to stay read only */
int open_flags; /* flags used to open the file */
int removable; /* if true, the media can be removed */
int locked; /* if true, the media cannot temporarily be ejected */
Open backing file for read-only During commit upgrade to read-write and back at end to read-only Signed-off-by: Naphtali Sprei <nsprei@redhat.com> --- block.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++------- block_int.h | 1 + 2 files changed, 57 insertions(+), 8 deletions(-)