block/raw-posix: detect readonly Linux block devices using BLKROGET

Linux block devices can be set read-only with "blockdev --setro
<device>".  The same thing can be done for LVM volumes using "lvchange
--permission r <volume>".  This read-only setting is independent of
device node permissions.  Therefore the device can still be opened
O_RDWR but actual writes will fail.

This results in odd behavior for QEMU.  bdrv_open() is supposed to fail
if a read-only image is being opened with BDRV_O_RDWR.  By not failing
for Linux block devices, the guest boots up but every write produces an
I/O error.

This patch checks whether the block device is read-only so that Linux
block devices behave like regular files.

Reported-by: Sibiao Luo <sluo@redhat.com>
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2013-02-05 12:28:33 +01:00
parent 58fa432522
commit da888d37b0

View File

@ -1257,9 +1257,43 @@ static int hdev_probe_device(const char *filename)
return 0;
}
static int check_hdev_writable(BDRVRawState *s)
{
#if defined(BLKROGET)
/* Linux block devices can be configured "read-only" using blockdev(8).
* This is independent of device node permissions and therefore open(2)
* with O_RDWR succeeds. Actual writes fail with EPERM.
*
* bdrv_open() is supposed to fail if the disk is read-only. Explicitly
* check for read-only block devices so that Linux block devices behave
* properly.
*/
struct stat st;
int readonly = 0;
if (fstat(s->fd, &st)) {
return -errno;
}
if (!S_ISBLK(st.st_mode)) {
return 0;
}
if (ioctl(s->fd, BLKROGET, &readonly) < 0) {
return -errno;
}
if (readonly) {
return -EACCES;
}
#endif /* defined(BLKROGET) */
return 0;
}
static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
{
BDRVRawState *s = bs->opaque;
int ret;
#if defined(__APPLE__) && defined(__MACH__)
if (strstart(filename, "/dev/cdrom", NULL)) {
@ -1300,7 +1334,20 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
}
#endif
return raw_open_common(bs, filename, flags, 0);
ret = raw_open_common(bs, filename, flags, 0);
if (ret < 0) {
return ret;
}
if (flags & BDRV_O_RDWR) {
ret = check_hdev_writable(s);
if (ret < 0) {
raw_close(bs);
return ret;
}
}
return ret;
}
#if defined(__linux__)