From 8105fad6468cdd26c26bb89765cfb0c7d9d99781 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Mon, 26 Mar 2012 20:29:49 +0200 Subject: [PATCH] dom0+vm: qvm-block --attach-file Allow to attach disk image from different VM as block device. File attached with qvm-block -A will be visible as loopX device and as such can be detached. File path will be in device description. --- dom0/qvm-core/qubesutils.py | 27 ++++++++++++++++++++++----- dom0/qvm-tools/qvm-block | 21 ++++++++++++++++----- misc/block_add_change | 10 ++++++++++ misc/qubes_block.rules | 3 --- 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/dom0/qvm-core/qubesutils.py b/dom0/qvm-core/qubesutils.py index 7b8e5add..3e87ec72 100644 --- a/dom0/qvm-core/qubesutils.py +++ b/dom0/qvm-core/qubesutils.py @@ -142,6 +142,9 @@ def block_name_to_majorminor(name): elif name.startswith("sr"): disk = False major = 11 + elif name.startswith("loop"): + disk = False + major = 7 elif name.startswith("md"): disk = False major = 9 @@ -242,25 +245,34 @@ def block_check_attached(backend_vm, device, backend_xid = None): vm_list = xs.ls('', '/local/domain/%d/backend/vbd' % backend_xid) if vm_list is None: return None - device_majorminor = block_name_to_majorminor(device) + device_majorminor = None + try: + device_majorminor = block_name_to_majorminor(device) + except: + # Unknown devices will be compared directly - perhaps it is a filename? + pass for vm_xid in vm_list: for devid in xs.ls('', '/local/domain/%d/backend/vbd/%s' % (backend_xid, vm_xid)): (tmp_major, tmp_minor) = (0, 0) phys_device = xs.read('', '/local/domain/%d/backend/vbd/%s/%s/physical-device' % (backend_xid, vm_xid, devid)) + dev_params = xs.read('', '/local/domain/%d/backend/vbd/%s/%s/params' % (backend_xid, vm_xid, devid)) if phys_device and phys_device.find(':'): (tmp_major, tmp_minor) = phys_device.split(":") tmp_major = int(tmp_major, 16) tmp_minor = int(tmp_minor, 16) else: # perhaps not ready yet - check params - dev_params = xs.read('', '/local/domain/%d/backend/vbd/%s/%s/params' % (backend_xid, vm_xid, devid)) - if not dev_params or not dev_params.startswith('/dev/'): + if not dev_params: # Skip not-phy devices continue + elif not dev_params.startswith('/dev/'): + # will compare params directly + pass else: (tmp_major, tmp_minor) = block_name_to_majorminor(dev_params.lstrip('/dev/')) - if (tmp_major, tmp_minor) == device_majorminor: + if (device_majorminor and (tmp_major, tmp_minor) == device_majorminor) or \ + (device_majorminor is None and dev_params == device): vm_name = xl_ctx.domid_to_name(int(vm_xid)) frontend = block_devid_to_name(int(devid)) return {"xid":int(vm_xid), "frontend": frontend, "devid": int(devid), "vm": vm_name} @@ -290,7 +302,12 @@ def block_attach(vm, backend_vm, device, frontend=None, mode="w", auto_detach=Fa else: raise QubesException("Device %s from %s already connected to VM %s as %s" % (device, backend_vm.name, attached_vm['vm'], attached_vm['frontend'])) - xl_cmd = [ '/usr/sbin/xl', 'block-attach', vm.name, 'phy:/dev/' + device, frontend, mode, str(backend_vm.xid) ] + if device.startswith('/'): + backend_dev = 'script:file:' + device + else: + backend_dev = 'phy:/dev/' + device + + xl_cmd = [ '/usr/sbin/xl', 'block-attach', vm.name, backend_dev, frontend, mode, str(backend_vm.xid) ] subprocess.check_call(xl_cmd) def block_detach(vm, frontend = "xvdi", vm_xid = None): diff --git a/dom0/qvm-tools/qvm-block b/dom0/qvm-tools/qvm-block index bcdb8eac..e46d9438 100755 --- a/dom0/qvm-tools/qvm-block +++ b/dom0/qvm-tools/qvm-block @@ -37,6 +37,8 @@ def main(): parser = OptionParser (usage) parser.add_option ("-l", "--list", action="store_true", dest="do_list", default=False) + parser.add_option ("-A", "--attach-file", action="store", dest="file_path", default=None, + help="Attach specified file instead of physical device (syntax: qvm-block -A file backend-VM frontend-VM)") parser.add_option ("-a", "--attach", action="store_true", dest="do_attach", default=False) parser.add_option ("-d", "--detach", action="store_true", dest="do_detach", default=False) parser.add_option ("-f", "--frontend", dest="frontend", @@ -48,8 +50,11 @@ def main(): (options, args) = parser.parse_args () + if options.file_path: + options.do_attach = True + if options.do_list + options.do_attach + options.do_detach > 1: - print >> sys.stderr, "Only one of -l -a -d is allowed!" + print >> sys.stderr, "Only one of -l -a/-A -d is allowed!" exit (1) if options.do_attach or options.do_detach: @@ -64,10 +69,16 @@ def main(): vm = qvm_collection.get_vm_by_name(args[1]) if vm is None: parser.error ("Invalid VM name: %s" % args[1]) - dev_list = block_list() - if not args[0] in dev_list.keys(): - parser.error ("Invalid device name: %s" % args[0]) - dev = dev_list[args[0]] + if options.file_path: + dev = {} + dev['vm'] = args[0] + dev['device'] = options.file_path + dev['mode'] = 'w' + else: + dev_list = block_list() + if not args[0] in dev_list.keys(): + parser.error ("Invalid device name: %s" % args[0]) + dev = dev_list[args[0]] backend_vm = qvm_collection.get_vm_by_name(dev['vm']) assert backend_vm is not None kwargs = {} diff --git a/misc/block_add_change b/misc/block_add_change index 3d675aa1..e1b25ca9 100755 --- a/misc/block_add_change +++ b/misc/block_add_change @@ -16,6 +16,11 @@ if [ -n "`ls -A /sys/$DEVPATH/holders 2> /dev/null`" ]; then xenstore-rm "$XS_KEY" exit 0 fi +# ... and "empty" loop devices +if [ "$MAJOR" -eq 7 -a ! -d /sys/$DEVPATH/loop ]; then + xenstore-rm "$XS_KEY" + exit 0 +fi # Special case for CD if [ "$ID_TYPE" = "cd" ]; then @@ -26,6 +31,11 @@ if [ "$ID_TYPE" = "cd" ]; then fi MODE=r fi + +# Special description for loop devices +if [ -d /sys/$DEVPATH/loop ]; then + DESC=$(cat /sys/$DEVPATH/loop/backing_file) +fi xenstore-write "$XS_KEY/desc" "$DESC" "$XS_KEY/size" "$SIZE" "$XS_KEY/mode" "$MODE" # Make sure that block backend is loaded diff --git a/misc/qubes_block.rules b/misc/qubes_block.rules index 1a0864e0..343553f5 100644 --- a/misc/qubes_block.rules +++ b/misc/qubes_block.rules @@ -6,9 +6,6 @@ SUBSYSTEM!="block", GOTO="qubes_block_end" # Skip xen-blkfront devices ENV{MAJOR}=="202", GOTO="qubes_block_end" -# Skip loop devices -ENV{MAJOR}=="7", GOTO="qubes_block_end" - # Skip device-mapper devices ENV{MAJOR}=="253", GOTO="qubes_block_end"