diff --git a/dom0/qvm-core/qubesutils.py b/dom0/qvm-core/qubesutils.py index 7b8e5add..957d01a2 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 @@ -185,7 +188,7 @@ def block_find_unused_frontend(vm = None): return block_devid_to_name(major << 8 | minor) return None -def block_list(vm = None): +def block_list(vm = None, system_disks = False): device_re = re.compile(r"^[a-z0-9]{1,8}$") # FIXME: any better idea of desc_re? desc_re = re.compile(r"^.{1,255}$") @@ -216,6 +219,9 @@ def block_list(vm = None): device_desc = xs.read('', '/local/domain/%s/qubes-block-devices/%s/desc' % (xid, device)) device_mode = xs.read('', '/local/domain/%s/qubes-block-devices/%s/mode' % (xid, device)) + if device_size is None or device_desc is None or device_mode is None: + print >> sys.stderr, "Missing field in %s device parameters" % device + continue if not device_size.isdigit(): print >> sys.stderr, "Invalid %s device size in VM '%s'" % (device, vm_name) continue @@ -229,6 +235,11 @@ def block_list(vm = None): if block_name_to_majorminor(device) == (0, 0): print >> sys.stderr, "Unsupported device %s:%s" % (vm_name, device) continue + + if not system_disks: + if xid == '0' and device_desc.startswith(qubes_base_dir): + continue + visible_name = "%s:%s" % (vm_name, device) devices_list[visible_name] = {"name": visible_name, "xid":int(xid), "vm": vm_name, "device":device, "size":int(device_size), @@ -242,25 +253,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} @@ -286,11 +306,16 @@ def block_attach(vm, backend_vm, device, frontend=None, mode="w", auto_detach=Fa attached_vm = block_check_attached(backend_vm, device) if attached_vm: if auto_detach: - block_detach(None, attached_vm['devid'], vm_xid=attached_vm['vm_xid']) + block_detach(None, attached_vm['devid'], vm_xid=attached_vm['xid']) 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..ed8de53d 100755 --- a/dom0/qvm-tools/qvm-block +++ b/dom0/qvm-tools/qvm-block @@ -30,13 +30,16 @@ import os def main(): usage = "usage: %prog -l [options]\n"\ - "usage: %prog -a [options] \n"\ - "usage: %prog -d [options] \n"\ + "usage: %prog -a [options] :\n"\ + "usage: %prog -A [options] :\n"\ + "usage: %prog -d [options] :\n"\ "usage: %prog -d [options] \n"\ "List/set VM PCI devices." parser = OptionParser (usage) parser.add_option ("-l", "--list", action="store_true", dest="do_list", default=False) + parser.add_option ("-A", "--attach-file", action="store_true", dest="do_file_attach", default=False, + help="Attach specified file instead of physical device") 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", @@ -45,11 +48,16 @@ def main(): help="Force read-only mode") parser.add_option ("--no-auto-detach", dest="auto_detach", action="store_false", default=True, help="Fail when device already connected to other VM") + parser.add_option ("--show-system-disks", dest="system_disks", action="store_true", default=False, + help="List also system disks") (options, args) = parser.parse_args () + if options.do_file_attach: + 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: @@ -61,13 +69,21 @@ def main(): if options.do_attach: if (len (args) < 2): parser.error ("You must provide device and vm name!") - vm = qvm_collection.get_vm_by_name(args[1]) + vm = qvm_collection.get_vm_by_name(args[0]) 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]] + parser.error ("Invalid VM name: %s" % args[0]) + # FIXME: here we assume that device is always in form "domain:dev", which can be changed in the future + if args[1].find(":") < 0: + parser.error ("Invalid device syntax" % args[1]) + if options.do_file_attach: + dev = {} + (dev['vm'], dev['device']) = args[1].split(":") + dev['mode'] = 'w' + else: + dev_list = block_list() + if not args[1] in dev_list.keys(): + parser.error ("Invalid device name: %s" % args[1]) + dev = dev_list[args[1]] backend_vm = qvm_collection.get_vm_by_name(dev['vm']) assert backend_vm is not None kwargs = {} @@ -102,7 +118,9 @@ def main(): block_detach(None, attached_to['devid'], vm_xid=attached_to['xid']) else: # do_list - for dev in block_list().values(): + kwargs = {} + kwargs['system_disks'] = options.system_disks + for dev in block_list(**kwargs).values(): attached_to = block_check_attached(None, dev['device'], backend_xid = dev['xid']) attached_to_str = "" if attached_to: 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"