diff --git a/core-modules/000QubesVm.py b/core-modules/000QubesVm.py
index ac5976c0..95e037a5 100644
--- a/core-modules/000QubesVm.py
+++ b/core-modules/000QubesVm.py
@@ -347,6 +347,12 @@ class QubesVm(object):
else:
assert self.root_img is not None, "Missing root_img for standalone VM!"
+ self.storage = defaults["storage_class"](self)
+ if hasattr(self, 'kernels_dir'):
+ self.storage.modules_img = os.path.join(self.kernels_dir,
+ "modules.img")
+ self.storage.modules_img_rw = self.kernel is None
+
# fire hooks
for hook in self.hooks_init:
hook(self)
@@ -854,33 +860,17 @@ class QubesVm(object):
return qubes.qubesutils.get_disk_usage(self.private_img)
def get_private_img_sz(self):
- if not os.path.exists(self.private_img):
- return 0
-
- return os.path.getsize(self.private_img)
+ return self.storage.get_private_img_sz()
def resize_private_img(self, size):
assert size >= self.get_private_img_sz(), "Cannot shrink private.img"
- f_private = open (self.private_img, "a+b")
- f_private.truncate (size)
- f_private.close ()
+ # resize the image
+ self.storage.resize_private_img(size)
+ # and then the filesystem
retcode = 0
if self.is_running():
- # find loop device
- p = subprocess.Popen (["sudo", "losetup", "--associated", self.private_img],
- stdout=subprocess.PIPE)
- result = p.communicate()
- m = re.match(r"^(/dev/loop\d+):\s", result[0])
- if m is None:
- raise QubesException("ERROR: Cannot find loop device!")
-
- loop_dev = m.group(1)
-
- # resize loop device
- subprocess.check_call(["sudo", "losetup", "--set-capacity", loop_dev])
-
retcode = self.run("while [ \"`blockdev --getsize64 /dev/xvdb`\" -lt {0} ]; do ".format(size) +
"head /dev/xvdb > /dev/null; sleep 0.2; done; resize2fs /dev/xvdb", user="root", wait=True)
if retcode != 0:
@@ -983,23 +973,6 @@ class QubesVm(object):
for hook in self.hooks_create_xenstore_entries:
hook(self, xid=xid)
- def _format_disk_dev(self, path, script, vdev, rw=True, type="disk", domain=None):
- template = " \n" \
- " \n" \
- " \n" \
- " \n" \
- "{params}" \
- " \n"
- params = ""
- if not rw:
- params += " \n"
- if domain:
- params += " \n" % domain
- if script:
- params += " \n" % script
- return template.format(path=path, vdev=vdev, type=type,
- params=params)
-
def _format_net_dev(self, ip, mac, backend):
template = " \n" \
" \n" \
@@ -1023,17 +996,6 @@ class QubesVm(object):
slot=dev_match.group(2),
fun=dev_match.group(3))
- def get_rootdev(self):
- if self.template:
- return self._format_disk_dev(
- "{dir}/root.img:{dir}/root-cow.img".format(
- dir=self.template.dir_path),
- "block-snapshot", "xvda", False)
- else:
- return self._format_disk_dev(
- "{dir}/root.img".format(dir=self.dir_path),
- None, "xvda", True)
-
def get_config_params(self):
args = {}
args['name'] = self.name
@@ -1070,17 +1032,7 @@ class QubesVm(object):
args['netdev'] = ''
args['disable_network1'] = '';
- args['rootdev'] = self.get_rootdev()
- args['privatedev'] = \
- self._format_disk_dev("{dir}/private.img".format(dir=self.dir_path),
- None, "xvdb", True)
- args['volatiledev'] = \
- self._format_disk_dev("{dir}/volatile.img".format(dir=self.dir_path),
- None, "xvdc", True)
- if hasattr(self, 'kernel'):
- args['otherdevs'] = \
- self._format_disk_dev("{dir}/modules.img".format(dir=self.kernels_dir),
- None, "xvdd", self.kernel is None)
+ args.update(self.storage.get_config_params())
if hasattr(self, 'kernelopts'):
args['kernelopts'] = self.kernelopts
if self.debug:
@@ -1139,37 +1091,9 @@ class QubesVm(object):
if dry_run:
return
- old_umask = os.umask(002)
- if verbose:
- print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path)
- os.mkdir (self.dir_path)
-
- if verbose:
- print >> sys.stderr, "--> Creating the VM config file: {0}".format(self.conf_file)
-
- template_priv = source_template.private_img
- if verbose:
- print >> sys.stderr, "--> Copying the template's private image: {0}".\
- format(template_priv)
-
- # We prefer to use Linux's cp, because it nicely handles sparse files
- retcode = subprocess.call (["cp", template_priv, self.private_img])
- if retcode != 0:
- raise IOError ("Error while copying {0} to {1}".\
- format(template_priv, self.private_img))
+ self.storage.create_on_disk(verbose, source_template)
if self.updateable:
- template_root = source_template.root_img
- if verbose:
- print >> sys.stderr, "--> Copying the template's root image: {0}".\
- format(template_root)
-
- # We prefer to use Linux's cp, because it nicely handles sparse files
- retcode = subprocess.call (["cp", template_root, self.root_img])
- if retcode != 0:
- raise IOError ("Error while copying {0} to {1}".\
- format(template_root, self.root_img))
-
kernels_dir = source_template.kernels_dir
if verbose:
print >> sys.stderr, "--> Copying the kernel (set kernel \"none\" to use it): {0}".\
@@ -1180,15 +1104,10 @@ class QubesVm(object):
shutil.copy(os.path.join(kernels_dir, f),
os.path.join(self.dir_path, vm_files["kernels_subdir"], f))
- # Create volatile.img
- self.reset_volatile_storage(source_template = source_template, verbose=verbose)
-
if verbose:
print >> sys.stderr, "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path)
os.symlink (self.label.icon_path, self.icon_path)
- os.umask(old_umask)
-
# fire hooks
for hook in self.hooks_create_on_disk:
hook(self, verbose, source_template=source_template)
@@ -1224,29 +1143,7 @@ class QubesVm(object):
if src_vm.is_running():
raise QubesException("Attempt to clone a running VM!")
- if verbose:
- print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path)
- os.mkdir (self.dir_path)
-
- if src_vm.private_img is not None and self.private_img is not None:
- if verbose:
- print >> sys.stderr, "--> Copying the private image:\n{0} ==>\n{1}".\
- format(src_vm.private_img, self.private_img)
- # We prefer to use Linux's cp, because it nicely handles sparse files
- retcode = subprocess.call (["cp", src_vm.private_img, self.private_img])
- if retcode != 0:
- raise IOError ("Error while copying {0} to {1}".\
- format(src_vm.private_img, self.private_img))
-
- if src_vm.updateable and src_vm.root_img is not None and self.root_img is not None:
- if verbose:
- print >> sys.stderr, "--> Copying the root image:\n{0} ==>\n{1}".\
- format(src_vm.root_img, self.root_img)
- # We prefer to use Linux's cp, because it nicely handles sparse files
- retcode = subprocess.call (["cp", src_vm.root_img, self.root_img])
- if retcode != 0:
- raise IOError ("Error while copying {0} to {1}".\
- format(src_vm.root_img, self.root_img))
+ self.storage.clone_disk_files(src_vm, verbose)
if src_vm.icon_path is not None and self.icon_path is not None:
if os.path.exists (src_vm.dir_path):
@@ -1268,20 +1165,7 @@ class QubesVm(object):
if dry_run:
return
- if not os.path.exists (self.dir_path):
- raise QubesException (
- "VM directory doesn't exist: {0}".\
- format(self.dir_path))
-
- if self.updateable and not os.path.exists (self.root_img):
- raise QubesException (
- "VM root image file doesn't exist: {0}".\
- format(self.root_img))
-
- if not os.path.exists (self.private_img):
- raise QubesException (
- "VM private image file doesn't exist: {0}".\
- format(self.private_img))
+ self.storage.verify_files()
if not os.path.exists (os.path.join(self.kernels_dir, 'vmlinuz')):
raise QubesException (
@@ -1293,49 +1177,12 @@ class QubesVm(object):
"VM initramfs does not exists: {0}".\
format(os.path.join(self.kernels_dir, 'initramfs')))
- if not os.path.exists (os.path.join(self.kernels_dir, 'modules.img')):
- raise QubesException (
- "VM kernel modules image does not exists: {0}".\
- format(os.path.join(self.kernels_dir, 'modules.img')))
-
# fire hooks
for hook in self.hooks_verify_files:
hook(self)
return True
- def reset_volatile_storage(self, source_template = None, verbose = False):
- assert not self.is_running(), "Attempt to clean volatile image of running VM!"
-
- if source_template is None:
- source_template = self.template
-
- # Only makes sense on template based VM
- if source_template is None:
- # For StandaloneVM create it only if not already exists (eg after backup-restore)
- if not os.path.exists(self.volatile_img):
- if verbose:
- print >> sys.stderr, "--> Creating volatile image: {0}...".format (self.volatile_img)
- f_root = open (self.root_img, "r")
- f_root.seek(0, os.SEEK_END)
- root_size = f_root.tell()
- f_root.close()
- subprocess.check_call([system_path["prepare_volatile_img_cmd"], self.volatile_img, str(root_size / 1024 / 1024)])
- return
-
- if verbose:
- print >> sys.stderr, "--> Cleaning volatile image: {0}...".format (self.volatile_img)
- if dry_run:
- return
- if os.path.exists (self.volatile_img):
- os.remove (self.volatile_img)
-
- if hasattr(source_template, 'clean_volatile_img'):
- retcode = subprocess.call (["tar", "xf", source_template.clean_volatile_img, "-C", self.dir_path])
- if retcode != 0:
- raise IOError ("Error while unpacking {0} to {1}".\
- format(source_template.clean_volatile_img, self.volatile_img))
-
def remove_from_disk(self):
if dry_run:
return
@@ -1344,7 +1191,7 @@ class QubesVm(object):
for hook in self.hooks_remove_from_disk:
hook(self)
- shutil.rmtree (self.dir_path)
+ self.storage.remove_from_disk()
def write_firewall_conf(self, conf):
defaults = self.get_firewall_conf()
@@ -1727,7 +1574,7 @@ class QubesVm(object):
print >> sys.stderr, "--> Starting NetVM {0}...".format(self.netvm.name)
self.netvm.start(verbose = verbose, start_guid = start_guid, notify_function = notify_function)
- self.reset_volatile_storage(verbose=verbose)
+ self.storage.prepare_for_vm_startup(verbose=verbose)
if verbose:
print >> sys.stderr, "--> Loading the VM (type = {0})...".format(self.type)
diff --git a/core-modules/003QubesTemplateVm.py b/core-modules/003QubesTemplateVm.py
index 3ede7fc6..72488db6 100644
--- a/core-modules/003QubesTemplateVm.py
+++ b/core-modules/003QubesTemplateVm.py
@@ -51,6 +51,7 @@ class QubesTemplateVm(QubesVm):
attrs_config['rootcow_img'] = {
'func': lambda x: os.path.join(self.dir_path, vm_files["rootcow_img"]) }
# Clean image for root-cow and swap (AppVM side)
+ # TODO: not used anymore - clean up when all references removed
attrs_config['clean_volatile_img'] = {
'func': lambda x: os.path.join(self.dir_path, vm_files["clean_volatile_img"]) }
@@ -76,35 +77,12 @@ class QubesTemplateVm(QubesVm):
def get_firewall_defaults(self):
return { "rules": list(), "allow": False, "allowDns": False, "allowIcmp": False, "allowYumProxy": True }
- def get_rootdev(self):
- return self._format_disk_dev(
- "{dir}/root.img:{dir}/root-cow.img".format(
- dir=self.dir_path),
- "block-origin", "xvda", True)
-
def clone_disk_files(self, src_vm, verbose):
if dry_run:
return
super(QubesTemplateVm, self).clone_disk_files(src_vm=src_vm, verbose=verbose)
- if verbose:
- print >> sys.stderr, "--> Copying the template's clean volatile image:\n{0} ==>\n{1}".\
- format(src_vm.clean_volatile_img, self.clean_volatile_img)
- # We prefer to use Linux's cp, because it nicely handles sparse files
- retcode = subprocess.call (["cp", src_vm.clean_volatile_img, self.clean_volatile_img])
- if retcode != 0:
- raise IOError ("Error while copying {0} to {1}".\
- format(src_vm.clean_volatile_img, self.clean_volatile_img))
- if verbose:
- print >> sys.stderr, "--> Copying the template's volatile image:\n{0} ==>\n{1}".\
- format(self.clean_volatile_img, self.volatile_img)
- # We prefer to use Linux's cp, because it nicely handles sparse files
- retcode = subprocess.call (["cp", self.clean_volatile_img, self.volatile_img])
- if retcode != 0:
- raise IOError ("Error while copying {0} to {1}".\
- format(self.clean_img, self.volatile_img))
-
# Create root-cow.img
self.commit_changes(verbose=verbose)
@@ -112,42 +90,10 @@ class QubesTemplateVm(QubesVm):
super(QubesTemplateVm, self).post_rename(old_name)
old_dirpath = os.path.join(os.path.dirname(self.dir_path), old_name)
+ # TODO: clean_volatile_img not used anymore
self.clean_volatile_img = self.clean_volatile_img.replace(old_dirpath, self.dir_path)
self.rootcow_img = self.rootcow_img.replace(old_dirpath, self.dir_path)
- def verify_files(self):
- if dry_run:
- return
-
- super(QubesTemplateVm, self).verify_files()
-
- if not os.path.exists (self.volatile_img):
- raise QubesException (
- "VM volatile image file doesn't exist: {0}".\
- format(self.volatile_img))
-
- if not os.path.exists (self.clean_volatile_img):
- raise QubesException (
- "Clean VM volatile image file doesn't exist: {0}".\
- format(self.clean_volatile_img))
-
- return True
-
- def reset_volatile_storage(self, verbose = False):
- assert not self.is_running(), "Attempt to clean volatile image of running Template VM!"
-
- if verbose:
- print >> sys.stderr, "--> Cleaning volatile image: {0}...".format (self.volatile_img)
- if dry_run:
- return
- if os.path.exists (self.volatile_img):
- os.remove (self.volatile_img)
-
- retcode = subprocess.call (["tar", "xf", self.clean_volatile_img, "-C", self.dir_path])
- if retcode != 0:
- raise IOError ("Error while unpacking {0} to {1}".\
- format(self.template.clean_volatile_img, self.volatile_img))
-
def commit_changes (self, verbose = False):
if not vmm.offline_mode:
@@ -158,16 +104,7 @@ class QubesTemplateVm(QubesVm):
if dry_run:
return
- if os.path.exists (self.rootcow_img):
- os.rename (self.rootcow_img, self.rootcow_img + '.old')
- old_umask = os.umask(002)
- f_cow = open (self.rootcow_img, "w")
- f_root = open (self.root_img, "r")
- f_root.seek(0, os.SEEK_END)
- f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img
- f_cow.close ()
- f_root.close()
- os.umask(old_umask)
+ self.storage.commit_template_changes()
register_qubes_vm_class(QubesTemplateVm)
diff --git a/core/Makefile b/core/Makefile
index 41481354..15a73287 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -6,6 +6,7 @@ SETTINGS_SUFFIX = $(BACKEND_VMM)-$(OS)
all:
python -m compileall .
python -O -m compileall .
+ make -C storage all
install:
ifndef PYTHON_SITEPATH
@@ -30,3 +31,4 @@ ifneq ($(BACKEND_VMM),)
test -r settings-$(SETTINGS_SUFFIX).pyo && \
cp settings-$(SETTINGS_SUFFIX).pyo $(DESTDIR)$(PYTHON_QUBESPATH)/settings.pyo
endif
+ make -C storage install
diff --git a/core/qubes.py b/core/qubes.py
index 50fba25f..d2ed5b67 100755
--- a/core/qubes.py
+++ b/core/qubes.py
@@ -94,6 +94,11 @@ defaults = {
'dom0_update_check_interval': 6*3600,
+ 'private_img_size': 2*1024*1024*1024,
+ 'root_img_size': 10*1024*1024*1024,
+
+ 'storage_class': None,
+
# how long (in sec) to wait for VMs to shutdown,
# before killing them (when used qvm-run with --wait option),
'shutdown_counter_max': 60,
diff --git a/core/storage/Makefile b/core/storage/Makefile
new file mode 100644
index 00000000..ec59cc64
--- /dev/null
+++ b/core/storage/Makefile
@@ -0,0 +1,21 @@
+OS ?= Linux
+
+PYTHON_QUBESPATH = $(PYTHON_SITEPATH)/qubes
+
+all:
+ python -m compileall .
+ python -O -m compileall .
+
+install:
+ifndef PYTHON_SITEPATH
+ $(error PYTHON_SITEPATH not defined)
+endif
+ mkdir -p $(DESTDIR)$(PYTHON_QUBESPATH)/storage
+ cp __init__.py $(DESTDIR)$(PYTHON_QUBESPATH)/storage
+ cp __init__.py[co] $(DESTDIR)$(PYTHON_QUBESPATH)/storage
+ifneq ($(BACKEND_VMM),)
+ if [ -r $(BACKEND_VMM).py ]; then \
+ cp $(BACKEND_VMM).py $(DESTDIR)$(PYTHON_QUBESPATH)/storage && \
+ cp $(BACKEND_VMM).py[co] $(DESTDIR)$(PYTHON_QUBESPATH)/storage; \
+ fi
+endif
diff --git a/core/storage/__init__.py b/core/storage/__init__.py
new file mode 100644
index 00000000..e482fcd8
--- /dev/null
+++ b/core/storage/__init__.py
@@ -0,0 +1,182 @@
+#!/usr/bin/python2
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2013 Marek Marczykowski
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+#
+
+from __future__ import absolute_import
+
+import os
+import os.path
+import re
+import shutil
+import subprocess
+import sys
+
+from qubes.qubes import vm_files,system_path,defaults
+from qubes.qubes import QubesException
+import qubes.qubesutils
+
+class QubesVmStorage(object):
+ """
+ Class for handling VM virtual disks. This is base class for all other
+ implementations, mostly with Xen on Linux in mind.
+ """
+
+ def __init__(self, vm,
+ private_img_size = None,
+ root_img_size = None,
+ modules_img = None,
+ modules_img_rw = False):
+ self.vm = vm
+ self.vmdir = vm.dir_path
+ if private_img_size:
+ self.private_img_size = private_img_size
+ else:
+ self.private_img_size = defaults['private_img_size']
+ if root_img_size:
+ self.root_img_size = root_img_size
+ else:
+ self.root_img_size = defaults['root_img_size']
+
+ self.private_img = os.path.join(self.vmdir, vm_files["private_img"])
+ if self.vm.template:
+ self.root_img = self.vm.template.root_img
+ else:
+ self.root_img = os.path.join(self.vmdir, vm_files["root_img"])
+ self.volatile_img = os.path.join(self.vmdir, vm_files["volatile_img"])
+
+ # For now compute this path still in QubesVm
+ self.modules_img = modules_img
+ self.modules_img_rw = modules_img_rw
+
+ def get_config_params(self):
+ raise NotImplementedError
+
+ def _copy_file(self, source, destination):
+ """
+ Effective file copy, preserving sparse files etc.
+ """
+ # TODO: Windows support
+
+ # We prefer to use Linux's cp, because it nicely handles sparse files
+ retcode = subprocess.call (["cp", source, destination])
+ if retcode != 0:
+ raise IOError ("Error while copying {0} to {1}".\
+ format(source, destination))
+
+ def get_disk_utilization(self):
+ return qubes.qubesutils.get_disk_usage(self.vmdir)
+
+ def get_disk_utilization_private_img(self):
+ return qubes.qubesutils.get_disk_usage(self.private_img)
+
+ def get_private_img_sz(self):
+ if not os.path.exists(self.private_img):
+ return 0
+
+ return os.path.getsize(self.private_img)
+
+ def resize_private_img(self, size):
+ raise NotImplementedError
+
+ def create_on_disk_private_img(self, verbose, source_template = None):
+ raise NotImplementedError
+
+ def create_on_disk_root_img(self, verbose, source_template = None):
+ raise NotImplementedError
+
+ def create_on_disk(self, verbose, source_template = None):
+ if source_template is None:
+ source_template = self.vm.template
+
+ old_umask = os.umask(002)
+ if verbose:
+ print >> sys.stderr, "--> Creating directory: {0}".format(self.vmdir)
+ os.mkdir (self.vmdir)
+
+ self.create_on_disk_private_img(verbose, source_template)
+ self.create_on_disk_root_img(verbose, source_template)
+ self.reset_volatile_storage(verbose, source_template)
+
+ os.umask(old_umask)
+
+ def clone_disk_files(self, src_vm, verbose):
+ if verbose:
+ print >> sys.stderr, "--> Creating directory: {0}".format(self.vmdir)
+ os.mkdir (self.vmdir)
+
+ if src_vm.private_img is not None and self.private_img is not None:
+ if verbose:
+ print >> sys.stderr, "--> Copying the private image:\n{0} ==>\n{1}".\
+ format(src_vm.private_img, self.private_img)
+ self._copy_file(src_vm.private_img, self.private_img)
+
+ if src_vm.updateable and src_vm.root_img is not None and self.root_img is not None:
+ if verbose:
+ print >> sys.stderr, "--> Copying the root image:\n{0} ==>\n{1}".\
+ format(src_vm.root_img, self.root_img)
+ self._copy_file(src_vm.root_img, self.root_img)
+
+ # TODO: modules?
+
+ def verify_files(self):
+ if not os.path.exists (self.vmdir):
+ raise QubesException (
+ "VM directory doesn't exist: {0}".\
+ format(self.vmdir))
+
+ if self.vm.updateable and not os.path.exists (self.root_img):
+ raise QubesException (
+ "VM root image file doesn't exist: {0}".\
+ format(self.root_img))
+
+ if not os.path.exists (self.private_img):
+ raise QubesException (
+ "VM private image file doesn't exist: {0}".\
+ format(self.private_img))
+ if self.modules_img is not None:
+ if not os.path.exists(self.modules_img):
+ raise QubesException (
+ "VM kernel modules image does not exists: {0}".\
+ format(self.modules_img))
+
+ def remove_from_disk(self):
+ shutil.rmtree (self.vmdir)
+
+ def reset_volatile_storage(self, verbose = False, source_template = None):
+ if source_template is None:
+ source_template = self.vm.template
+
+ # Re-create only for template based VMs
+ if source_template is not None:
+ if os.path.exists(self.volatile_img):
+ os.remove(self.volatile_img)
+
+ # For StandaloneVM create it only if not already exists (eg after backup-restore)
+ if not os.path.exists(self.volatile_img):
+ if verbose:
+ print >> sys.stderr, "--> Creating volatile image: {0}...".\
+ format(self.volatile_img)
+ subprocess.check_call([system_path["prepare_volatile_img_cmd"],
+ self.volatile_img, str(self.root_img_size / 1024 / 1024)])
+
+ def prepare_for_vm_startup(self, verbose):
+ self.reset_volatile_storage(verbose=verbose)
+ pass
diff --git a/core/storage/xen.py b/core/storage/xen.py
new file mode 100644
index 00000000..130408be
--- /dev/null
+++ b/core/storage/xen.py
@@ -0,0 +1,154 @@
+#!/usr/bin/python2
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2013 Marek Marczykowski
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+#
+
+from __future__ import absolute_import
+
+import os
+import os.path
+import subprocess
+import sys
+
+from qubes.storage import QubesVmStorage
+from qubes.qubes import QubesException
+
+class QubesXenVmStorage(QubesVmStorage):
+ """
+ Class for VM storage of Xen VMs.
+ """
+
+ def __init__(self, vm, **kwargs):
+ super(QubesXenVmStorage, self).__init__(vm, **kwargs)
+
+ self.root_dev = "xvda"
+ self.private_dev = "xvdb"
+ self.volatile_dev = "xvdc"
+ self.modules_dev = "xvdd"
+
+ def _format_disk_dev(self, path, script, vdev, rw=True, type="disk", domain=None):
+ template = " \n" \
+ " \n" \
+ " \n" \
+ " \n" \
+ "{params}" \
+ " \n"
+ params = ""
+ if not rw:
+ params += " \n"
+ if domain:
+ params += " \n" % domain
+ if script:
+ params += " \n" % script
+ return template.format(path=path, vdev=vdev, type=type,
+ params=params)
+
+ def _get_rootdev(self):
+ if self.vm.is_template():
+ return self._format_disk_dev(
+ "{dir}/root.img:{dir}/root-cow.img".format(
+ dir=self.vmdir),
+ "block-origin", self.root_dev, True)
+ elif self.vm.template:
+ return self._format_disk_dev(
+ "{dir}/root.img:{dir}/root-cow.img".format(
+ dir=self.vm.template.vmdir),
+ "block-snapshot", self.root_dev, False)
+ else:
+ return self._format_disk_dev(
+ "{dir}/root.img".format(dir=self.vmdir),
+ None, self.root_dev, True)
+
+ def get_config_params(self):
+ args = {}
+ args['rootdev'] = self._get_rootdev()
+ args['privatedev'] = \
+ self._format_disk_dev(self.private_img,
+ None, self.private_dev, True)
+ args['volatiledev'] = \
+ self._format_disk_dev(self.volatile_img,
+ None, self.volatile_dev, True)
+ if self.modules_img is not None:
+ args['otherdevs'] = \
+ self._format_disk_dev(self.modules_img,
+ None, self.modules_dev, self.modules_img_rw)
+
+ return args
+
+ def create_on_disk_private_img(self, verbose, source_template = None):
+ if source_template:
+ template_priv = source_template.private_img
+ if verbose:
+ print >> sys.stderr, "--> Copying the template's private image: {0}".\
+ format(template_priv)
+ self._copy_file(template_priv, self.private_img)
+ else:
+ f_private = open (self.private_img, "a+b")
+ f_private.truncate (self.private_img_size)
+ f_private.close ()
+
+ def create_on_disk_root_img(self, verbose, source_template = None):
+ if source_template:
+ if not self.vm.updateable:
+ # just use template's disk
+ return
+ else:
+ template_root = source_template.root_img
+ if verbose:
+ print >> sys.stderr, "--> Copying the template's root image: {0}".\
+ format(template_root)
+
+ self._copy_file(template_root, self.root_img)
+ else:
+ f_root = open (self.root_img, "a+b")
+ f_root.truncate (self.root_img_size)
+ f_root.close ()
+
+ def resize_private_img(self, size):
+ f_private = open (self.private_img, "a+b")
+ f_private.truncate (size)
+ f_private.close ()
+
+ # find loop device if any
+ p = subprocess.Popen (["sudo", "losetup", "--associated", self.private_img],
+ stdout=subprocess.PIPE)
+ result = p.communicate()
+ m = re.match(r"^(/dev/loop\d+):\s", result[0])
+ if m is not None:
+ loop_dev = m.group(1)
+
+ # resize loop device
+ subprocess.check_call(["sudo", "losetup", "--set-capacity", loop_dev])
+
+ def commit_template_changes(self):
+ assert self.vm.is_template()
+ # TODO: move rootcow_img to this class; the same for vm.is_outdated()
+ if os.path.exists (self.vm.rootcow_img):
+ os.rename (self.vm.rootcow_img, self.vm.rootcow_img + '.old')
+
+ old_umask = os.umask(002)
+ f_cow = open (self.vm.rootcow_img, "w")
+ f_root = open (self.root_img, "r")
+ f_root.seek(0, os.SEEK_END)
+ f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img
+ f_cow.close ()
+ f_root.close()
+ os.umask(old_umask)
+
diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec
index 7dbc49e3..39970711 100644
--- a/rpm_spec/core-dom0.spec
+++ b/rpm_spec/core-dom0.spec
@@ -197,6 +197,9 @@ fi
%{python_sitearch}/qubes/backup.py
%{python_sitearch}/qubes/backup.pyc
%{python_sitearch}/qubes/backup.pyo
+%{python_sitearch}/qubes/storage/*.py
+%{python_sitearch}/qubes/storage/*.pyc
+%{python_sitearch}/qubes/storage/*.pyo
%{python_sitearch}/qubes/qmemman*.py*
%{python_sitearch}/qubes/modules/0*.py*
%{python_sitearch}/qubes/modules/__init__.py*