diff --git a/docs/overview.rst b/docs/overview.rst
index e7356e45e6a74b702f5e1b7a41b1673265625d0d..f15bb67657f980f52a4fd2c972aaeb57dafa8079 100644
--- a/docs/overview.rst
+++ b/docs/overview.rst
@@ -2,8 +2,12 @@ Overview
 ^^^^^^^^
 
 snf-image-creator is a simple command-line tool for creating OS images. The
-original media the image is created from, can be a block device, a regular
-file that represents a hard disk or the host system itself.
+original media, the image is created from, can be:
+
+ * a block device, representing a hard disk
+ * a disk image file, representing a hard disk (supports all image file formats
+   supported by QEMU)
+ * the host system itself
 
 Snapshotting
 ============
diff --git a/docs/usage.rst b/docs/usage.rst
index 3fbead5eef9d5b462fbb80ff53553018eccef9a8..3baafed1e5960110811bdd61d2dba11a2afcb6fc 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -318,102 +318,17 @@ Choosing *YES* will create and upload the image to your cloud account.
 Working with different image formats
 ====================================
 
-*snf-image-creator* works on raw image files. If you have an image file with a
-different image format you can either convert it to raw using
-*qemu-img convert* command or use the *blktap* toolkit that provides a
-user-level disk I/O interface and use the exposed *tapdev* block device as
-input on *snf-image-creator*.
+*snf-image-creator* is able to work with the most popular disk image formats.
+It has been successfully tested with:
 
-Converting images to raw
-------------------------
-
-Converting between images with *qemu-img convert* is generally straightforward.
-All you need to provide is the output format (*-O raw*) and an output filename.
-You may use the *-f* option to define the input format, but in most cases this
-is guessed automatically. The table below shows a list of supported image
-formats and the equivalent argument you may pass to the *-f* flag.
-
-+--------------------------+-----------+
-|Image Format              |-f argument|
-+==========================+===========+
-|qcow2 (QEMU Copy On Write)|qcow2      |
-+--------------------------+-----------+
-|VHD (Mircosoft Hyper-V)   |vpc        |
-+--------------------------+-----------+
-|VMDK (VMware)             |vmdk       |
-+--------------------------+-----------+
-
-With the following commands we demonstrate how to download and convert an
-official Ubuntu 14.04 *qcow2* image to raw:
-
-.. code-block:: console
-
-   $ wget http://uec-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img
-   $ qemu-img convert -f qcow2 -O raw trusty-server-cloudimg-amd64-disk1.img ubuntu.raw
-
-Working on .vhd disk images using blktap/tapdisk
-------------------------------------------------
-
-If the source image format is *Microsoft VHD* [#f1]_, we can use blktap/tapdisk
-to connect it to a block device and use this block device as input in
-*snf-image-creator* without having to convert the image to raw format.
-
-Assuming that you work on a recent Debian GNU/Linux, you can install the
-needed tools by giving the following command:
-
-.. code-block:: console
-
-   # apt-get install blktap-utils
-   # modprobe blktap
-
-Please refer to your distribution's documentation on how to install the blktap
-user-space tools and the corresponding kernel module.
-
-After you have successfully installed blktap, do the following to attack the
-source image (*/tmp/Centos-6.2-x86_64-minimal-dist.vhd*) to a block device:
-
-Allocate a minor number in the kernel:
-
-.. code-block:: console
-
-   # tap-ctl allocate
-   /dev/xen/blktap-2/tapdev0
-
-Then, spawn a tapdisk process:
-
-.. code-block:: console
-
-   # tap-ctl spawn
-   tapdisk spawned with pid 14879
-
-Now, attach them together:
-
-.. code-block:: console
-
-   # tap-ctl attach -m 0 -p 14879
-
-And finally, open the VHD image:
-
-.. code-block:: console
-
-   # tap-clt open -m 0 -p 14879 -a vhd:/tmp/Centos-6.2-x86_64-minimal-dist.vhd
-
-Now you can open the associated block device with *snf-image-creator* like
-this:
-
-.. code-block:: console
-
-   # snf-image-creator /dev/xen/blktap-2/tapdev
-
-When done, you may release the allocated resources by giving the following
-commands:
-
-.. code-block:: console
-
-   # tap-ctl close -m 0 -p 14879
-   # tap-ctl detach -m 0 -p 14879
-   # tap-ctl free -m 0
+* Raw disk images
+* VMDK (VMware)
+* VHD (Microsoft Hyper-V)
+* VDI (VirtualBox)
+* qcow2 (QEMU)
 
+It can support any image format QEMU supports as long as it represents a
+bootable hard drive.
 
 Limitations
 ===========
diff --git a/image_creator/dialog_main.py b/image_creator/dialog_main.py
index 67345af204939b09fe747086256fb875b9c60b32..2177aaf96b43d19fe4c28871128940eb08be8e7d 100644
--- a/image_creator/dialog_main.py
+++ b/image_creator/dialog_main.py
@@ -46,7 +46,7 @@ from image_creator.dialog_util import WIDTH, confirm_exit, Reset, \
 PROGNAME = os.path.basename(sys.argv[0])
 
 
-def create_image(d, media, out, tmp):
+def create_image(d, media, out, tmp, snapshot):
     """Create an image out of `media'"""
     d.setBackgroundTitle('snf-image-creator')
 
@@ -61,9 +61,8 @@ def create_image(d, media, out, tmp):
     signal.signal(signal.SIGINT, signal_handler)
     signal.signal(signal.SIGTERM, signal_handler)
     try:
-        # There is no need to snapshot the media if it was created by the Disk
-        # instance as a temporary object.
-        device = disk.device if disk.source == '/' else disk.snapshot()
+
+        device = disk.file if not snapshot else disk.snapshot()
 
         image = disk.get_image(device)
 
@@ -172,7 +171,7 @@ def _dialog_form(self, text, height=20, width=60, form_height=15, fields=[],
     return (code, output.splitlines())
 
 
-def dialog_main(media, logfile, tmpdir):
+def dialog_main(media, logfile, tmpdir, snapshot):
 
     # In openSUSE dialog is buggy under xterm
     if os.environ['TERM'] == 'xterm':
@@ -214,7 +213,7 @@ def dialog_main(media, logfile, tmpdir):
             try:
                 out = CompositeOutput([log])
                 out.output("Starting %s v%s ..." % (PROGNAME, version))
-                return create_image(d, media, out, tmpdir)
+                return create_image(d, media, out, tmpdir, snapshot)
             except Reset:
                 log.output("Resetting everything ...")
                 continue
@@ -235,6 +234,10 @@ def main():
     parser.add_option("-l", "--logfile", type="string", dest="logfile",
                       default=None, help="log all messages to FILE",
                       metavar="FILE")
+    parser.add_option("--no-snapshot", dest="snapshot", default=True,
+                      help="don't snapshot the input media. (THIS IS "
+                      "DANGEROUS AS IT WILL ALTER THE ORIGINAL MEDIA!!!)",
+                      action="store_false")
     parser.add_option("--tmpdir", type="string", dest="tmp", default=None,
                       help="create large temporary image files under DIR",
                       metavar="DIR")
@@ -260,7 +263,7 @@ def main():
         # Save the terminal attributes
         attr = termios.tcgetattr(sys.stdin.fileno())
         try:
-            ret = dialog_main(media, logfile, opts.tmp)
+            ret = dialog_main(media, logfile, opts.tmp, opts.snapshot)
         finally:
             # Restore the terminal attributes. If an error occurs make sure
             # that the terminal turns back to normal.
diff --git a/image_creator/dialog_menu.py b/image_creator/dialog_menu.py
index 847fb0a424167874099e242fb25b0d06eb148136..1322cb2933b978f5e46a822c704b37afa4fe6a75 100644
--- a/image_creator/dialog_menu.py
+++ b/image_creator/dialog_menu.py
@@ -27,7 +27,7 @@ import re
 import time
 
 from image_creator import __version__ as version
-from image_creator.util import MD5, FatalError, virtio_versions
+from image_creator.util import FatalError, virtio_versions
 from image_creator.output.dialog import GaugeOutput, InfoBoxOutput
 from image_creator.kamaki_wrapper import Kamaki, ClientError
 from image_creator.help import get_help_file
@@ -149,16 +149,16 @@ def upload_image(session):
         kamaki.out = out
         try:
             if 'checksum' not in session:
-                md5 = MD5(out)
-                session['checksum'] = md5.compute(image.device, image.size)
+                session['checksum'] = image.md5()
 
             try:
                 # Upload image file
-                with open(image.device, 'rb') as f:
-                    session["pithos_uri"] = \
-                        kamaki.upload(f, image.size, filename,
-                                      "Calculating block hashes",
-                                      "Uploading missing blocks")
+                with image.raw_device() as raw:
+                    with open(raw, 'rb') as f:
+                        session["pithos_uri"] = \
+                            kamaki.upload(f, image.size, filename,
+                                          "Calculating block hashes",
+                                          "Uploading missing blocks")
                 # Upload md5sum file
                 out.output("Uploading md5sum file ...")
                 md5str = "%s %s\n" % (session['checksum'], filename)
diff --git a/image_creator/dialog_util.py b/image_creator/dialog_util.py
index 2b64b91be178fd7e8afbddb9f9bb654c29b75d0a..a757a963fbf687b9dacf260df10e5c657288e328 100644
--- a/image_creator/dialog_util.py
+++ b/image_creator/dialog_util.py
@@ -24,7 +24,6 @@ import stat
 import re
 import json
 from image_creator.output.dialog import GaugeOutput
-from image_creator.util import MD5
 from image_creator.kamaki_wrapper import Kamaki
 
 SMALL_WIDTH = 60
@@ -198,8 +197,7 @@ def extract_image(session):
             out.add(gauge)
             try:
                 if "checksum" not in session:
-                    md5 = MD5(out)
-                    session['checksum'] = md5.compute(image.device, image.size)
+                    session['checksum'] = image.md5()
 
                 # Extract image file
                 image.dump(path)
diff --git a/image_creator/dialog_wizard.py b/image_creator/dialog_wizard.py
index 7ce24cb9a442f2884a6ba435154e41ce54fcaba9..2c4e5d62f611b0c2eba448fe9a7ba515ec0e897e 100644
--- a/image_creator/dialog_wizard.py
+++ b/image_creator/dialog_wizard.py
@@ -25,7 +25,7 @@ import json
 import re
 
 from image_creator.kamaki_wrapper import Kamaki, ClientError
-from image_creator.util import MD5, FatalError, virtio_versions
+from image_creator.util import FatalError, virtio_versions
 from image_creator.output.cli import OutputWthProgress
 from image_creator.dialog_util import extract_image, update_background_title, \
     add_cloud, edit_cloud, update_sysprep_param
@@ -461,7 +461,7 @@ def create_image(session, answers):
         metadata['DESCRIPTION'] = answers['ImageDescription']
 
         # MD5
-        session['checksum'] = MD5(image.out).compute(image.device, image.size)
+        session['checksum'] = image.md5()
 
         image.out.output()
         try:
@@ -472,10 +472,11 @@ def create_image(session, answers):
 
             name = "%s-%s.diskdump" % (answers['ImageName'],
                                        time.strftime("%Y%m%d%H%M"))
-            with open(image.device, 'rb') as device:
-                remote = kamaki.upload(device, image.size, name,
-                                       "(1/3)  Calculating block hashes",
-                                       "(2/3)  Uploading image blocks")
+            with image.raw_device() as raw:
+                with open(raw, 'rb') as device:
+                    remote = kamaki.upload(device, image.size, name,
+                                           "(1/3)  Calculating block hashes",
+                                           "(2/3)  Uploading image blocks")
 
             image.out.output("(3/3)  Uploading md5sum file ...", False)
             md5sumstr = '%s %s\n' % (session['checksum'], name)
diff --git a/image_creator/disk.py b/image_creator/disk.py
index 2dc24e23c8a237484f3c26f3adf8cf3ff3d5636f..0b02d0504006dcb36afa4f064536dc57452ba015 100644
--- a/image_creator/disk.py
+++ b/image_creator/disk.py
@@ -17,10 +17,8 @@
 
 """Module hosting the Disk class."""
 
-from image_creator.util import get_command
-from image_creator.util import try_fail_repeat
-from image_creator.util import free_space
-from image_creator.util import FatalError
+from image_creator.util import get_command, try_fail_repeat, free_space, \
+    FatalError, create_snapshot
 from image_creator.bundle_volume import BundleVolume
 from image_creator.image import Image
 
@@ -72,7 +70,7 @@ class Disk(object):
         """
         self._cleanup_jobs = []
         self._images = []
-        self._device = None
+        self._file = None
         self.source = source
         self.out = output
         self.meta = {}
@@ -98,15 +96,16 @@ class Disk(object):
         """Create a disk out of a directory"""
         if self.source == '/':
             bundle = BundleVolume(self.out, self.meta)
-            image = '%s/%s.diskdump' % (self.tmp, uuid.uuid4().hex)
+            image = '%s/%s.raw' % (self.tmp, uuid.uuid4().hex)
 
             def check_unlink(path):
+                """Unlinks file if exists"""
                 if os.path.exists(path):
                     os.unlink(path)
 
             self._add_cleanup(check_unlink, image)
             bundle.create_image(image)
-            return self._losetup(image)
+            return image
         raise FatalError("Using a directory as media source is supported")
 
     def cleanup(self):
@@ -125,49 +124,63 @@ class Disk(object):
                 job(*args)
 
     @property
-    def device(self):
-        """Convert the source media into a block device"""
+    def file(self):
+        """Convert the source media into a file"""
 
-        if self._device is not None:
-            return self._device
+        if self._file is not None:
+            return self._file
 
         self.out.output("Examining source media `%s' ..." % self.source, False)
         mode = os.stat(self.source).st_mode
         if stat.S_ISDIR(mode):
             self.out.success('looks like a directory')
-            self._device = self._dir_to_disk()
+            self._file = self._dir_to_disk()
         elif stat.S_ISREG(mode):
             self.out.success('looks like an image file')
-            self._device = self._losetup(self.source)
+            self._file = self.source
         elif not stat.S_ISBLK(mode):
             raise FatalError("Invalid media source. Only block devices, "
                              "regular files and directories are supported.")
         else:
             self.out.success('looks like a block device')
-            self._device = self.source
+            self._file = self.source
 
-        return self._device
+        return self._file
 
     def snapshot(self):
         """Creates a snapshot of the original source media of the Disk
         instance.
         """
-        size = blockdev('--getsz', self.device)
+
+        if self.source == '/':
+            self.out.warn("Snapshotting ignored for host bundling mode.")
+            return self.file
+
         self.out.output("Snapshotting media source ...", False)
+
+        # Create a qcow2 snapshot for image files
+        if not stat.S_ISBLK(os.stat(self.file).st_mode):
+            snapshot = create_snapshot(self.file, self.tmp)
+            self._add_cleanup(os.unlink, snapshot)
+            self.out.success('done')
+            return snapshot
+
+        # Create a device-mapper snapshot for block devices
+        size = int(blockdev('--getsz', self.file))
+
         cowfd, cow = tempfile.mkstemp(dir=self.tmp)
         os.close(cowfd)
         self._add_cleanup(os.unlink, cow)
         # Create cow sparse file
-        dd('if=/dev/null', 'of=%s' % cow, 'bs=512', 'seek=%d' % int(size))
+        dd('if=/dev/null', 'of=%s' % cow, 'bs=512', 'seek=%d' % size)
         cowdev = self._losetup(cow)
 
-        snapshot = uuid.uuid4().hex
+        snapshot = 'snf-image-creator-snapshot-%s' % uuid.uuid4().hex
         tablefd, table = tempfile.mkstemp()
         try:
             try:
-                os.write(tablefd,
-                         "0 %d snapshot %s %s n 8" %
-                         (int(size), self.device, cowdev))
+                os.write(tablefd, "0 %d snapshot %s %s n 8\n" %
+                         (size, self.file, cowdev))
             finally:
                 os.close(tablefd)
 
diff --git a/image_creator/image.py b/image_creator/image.py
index ab142176c0f478c00549b5cceb2bea58e8364182..da3777df927ee34c828b19044972863e2514ebe9 100644
--- a/image_creator/image.py
+++ b/image_creator/image.py
@@ -15,12 +15,13 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from image_creator.util import FatalError
+from image_creator.util import FatalError, QemuNBD, image_info
 from image_creator.gpt import GPTPartitionTable
 from image_creator.os_type import os_cls
 
 import re
 import guestfs
+import hashlib
 from sendfile import sendfile
 
 
@@ -32,6 +33,7 @@ class Image(object):
 
         self.device = device
         self.out = output
+        self.info = image_info(device)
 
         self.meta = kargs['meta'] if 'meta' in kargs else {}
         self.sysprep_params = \
@@ -45,6 +47,9 @@ class Image(object):
         self.guestfs_enabled = False
         self.guestfs_version = self.g.version()
 
+        # This is needed if the image format is not raw
+        self.nbd = QemuNBD(device)
+
     def check_guestfs_version(self, major, minor, release):
         """Checks if the version of the used libguestfs is smaller, equal or
         greater than the one specified by the major, minor and release triplet
@@ -124,7 +129,7 @@ class Image(object):
         if self.check_guestfs_version(1, 18, 4) < 0:
             self.g = guestfs.GuestFS()
 
-        self.g.add_drive_opts(self.device, readonly=0, format="raw")
+        self.g.add_drive_opts(self.device, readonly=0)
 
         # Before version 1.17.14 the recovery process, which is a fork of the
         # original process that called libguestfs, did not close its inherited
@@ -201,6 +206,31 @@ class Image(object):
 
         return self._os
 
+    def raw_device(self, readonly=True):
+        """Returns a context manager that exports the raw image device. If
+        readonly is true, the block device that is returned is read only.
+        """
+
+        if self.guestfs_enabled:
+            self.g.umount_all()
+            self.g.sync()
+            self.g.drop_caches(3)  # drop everything
+
+        # Self gets overwritten
+        img = self
+
+        class RawImage:
+            """The RawImage context manager"""
+            def __enter__(self):
+                return img.device if img.info['format'] == 'raw' else \
+                    img.nbd.connect(readonly)
+
+            def __exit__(self, exc_type, exc_value, traceback):
+                if img.info['format'] != 'raw':
+                    img.nbd.disconnect()
+
+        return RawImage()
+
     def destroy(self):
         """Destroy this Image instance."""
 
@@ -373,8 +403,9 @@ class Image(object):
         assert (new_size <= self.size)
 
         if self.meta['PARTITION_TABLE'] == 'gpt':
-            ptable = GPTPartitionTable(self.device)
-            self.size = ptable.shrink(new_size, self.size)
+            with self.raw_device(readonly=False) as raw:
+                ptable = GPTPartitionTable(raw)
+                self.size = ptable.shrink(new_size, self.size)
         else:
             self.size = min(new_size + 2048 * sector_size, self.size)
 
@@ -391,30 +422,57 @@ class Image(object):
         partition table. Empty space in the end of the device will be ignored.
         """
         MB = 2 ** 20
-        blocksize = 4 * MB  # 4MB
-        size = self.size
-        progr_size = (size + MB - 1) // MB  # in MB
+        blocksize = 2 ** 22  # 4MB
+        progr_size = (self.size + MB - 1) // MB  # in MB
         progressbar = self.out.Progress(progr_size, "Dumping image file", 'mb')
 
-        with open(self.device, 'r') as src:
-            with open(outfile, "w") as dst:
-                left = size
-                offset = 0
-                progressbar.next()
+        with self.raw_device() as raw:
+            with open(raw, 'rb') as src:
+                with open(outfile, "wb") as dst:
+                    left = self.size
+                    offset = 0
+                    progressbar.next()
+                    while left > 0:
+                        length = min(left, blocksize)
+                        sent = sendfile(dst.fileno(), src.fileno(), offset,
+                                        length)
+
+                        # Workaround for python-sendfile API change. In
+                        # python-sendfile 1.2.x (py-sendfile) the returning
+                        # value of sendfile is a tuple, where in version 2.x
+                        # (pysendfile) it is just a single integer.
+                        if isinstance(sent, tuple):
+                            sent = sent[1]
+
+                        offset += sent
+                        left -= sent
+                        progressbar.goto((self.size - left) // MB)
+
+        progressbar.success('image file %s was successfully created' % outfile)
+
+    def md5(self):
+        """Computes the MD5 checksum of the image"""
+
+        MB = 2 ** 20
+        blocksize = 2 ** 22  # 4MB
+        progr_size = ((self.size + MB - 1) // MB)  # in MB
+        progressbar = self.out.Progress(progr_size, "Calculating md5sum", 'mb')
+        md5 = hashlib.md5()
+
+        with self.raw_device() as raw:
+            with open(raw, "rb") as src:
+                left = self.size
                 while left > 0:
                     length = min(left, blocksize)
-                    sent = sendfile(dst.fileno(), src.fileno(), offset, length)
-
-                    # Workaround for python-sendfile API change. In
-                    # python-sendfile 1.2.x (py-sendfile) the returning value
-                    # of sendfile is a tuple, where in version 2.x (pysendfile)
-                    # it is just a single integer.
-                    if isinstance(sent, tuple):
-                        sent = sent[1]
-
-                    offset += sent
-                    left -= sent
-                    progressbar.goto((size - left) // MB)
-        progressbar.success('image file %s was successfully created' % outfile)
+                    data = src.read(length)
+                    md5.update(data)
+                    left -= length
+                    progressbar.goto((self.size - left) // MB)
+
+        checksum = md5.hexdigest()
+        progressbar.success(checksum)
+
+        return checksum
+
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
diff --git a/image_creator/main.py b/image_creator/main.py
index 02d8c9ba9ef3220fa4d1e26eda23a3986d5124dc..4dffc5c46c3a8e0165da1aee9ca1738eb8cc47a0 100644
--- a/image_creator/main.py
+++ b/image_creator/main.py
@@ -22,7 +22,7 @@ snf-image-creator program.
 
 from image_creator import __version__ as version
 from image_creator.disk import Disk
-from image_creator.util import FatalError, MD5
+from image_creator.util import FatalError
 from image_creator.output.cli import SilentOutput, SimpleOutput, \
     OutputWthProgress
 from image_creator.kamaki_wrapper import Kamaki, ClientError
@@ -120,6 +120,11 @@ def parse_options(input_args):
                       help="don't perform any system preparation operation",
                       action="store_false")
 
+    parser.add_option("--no-snapshot", dest="snapshot", default=True,
+                      help="don't snapshot the input media. (THIS IS "
+                      "DANGEROUS AS IT WILL ALTER THE ORIGINAL MEDIA!!!)",
+                      action="store_false")
+
     parser.add_option("--public", dest="public", default=False,
                       help="register image with the cloud as public",
                       action="store_true")
@@ -263,7 +268,7 @@ def image_creator():
     try:
         # There is no need to snapshot the media if it was created by the Disk
         # instance as a temporary object.
-        device = disk.device if disk.source == '/' else disk.snapshot()
+        device = disk.file if not options.snapshot else disk.snapshot()
         image = disk.get_image(device, sysprep_params=options.sysprep_params)
 
         if image.is_unsupported() and not options.allow_unsupported:
@@ -309,8 +314,7 @@ def image_creator():
         # Add command line metadata to the collected ones...
         metadata.update(options.metadata)
 
-        md5 = MD5(out)
-        checksum = md5.compute(image.device, image.size)
+        checksum = image.md5()
 
         metastring = unicode(json.dumps(
             {'properties': metadata,
@@ -330,19 +334,17 @@ def image_creator():
                                      os.path.basename(options.outfile)))
             out.success('done')
 
-        # Destroy the image instance. We only need the disk device from now on
-        disk.destroy_image(image)
-
         out.output()
         try:
-            uploaded_obj = ""
             if options.upload:
                 out.output("Uploading image to the storage service:")
-                with open(device, 'rb') as f:
-                    uploaded_obj = kamaki.upload(
-                        f, image.size, options.upload,
-                        "(1/3)  Calculating block hashes",
-                        "(2/3)  Uploading missing blocks")
+                with image.raw_device() as raw:
+                    with open(raw, 'rb') as f:
+                        remote = kamaki.upload(
+                            f, image.size, options.upload,
+                            "(1/3)  Calculating block hashes",
+                            "(2/3)  Uploading missing blocks")
+
                 out.output("(3/3)  Uploading md5sum file ...", False)
                 md5sumstr = '%s %s\n' % (checksum,
                                          os.path.basename(options.upload))
@@ -356,7 +358,7 @@ def image_creator():
                 img_type = 'public' if options.public else 'private'
                 out.output('Registering %s image with the compute service ...'
                            % img_type, False)
-                result = kamaki.register(options.register, uploaded_obj,
+                result = kamaki.register(options.register, remote,
                                          metadata, options.public)
                 out.success('done')
                 out.output("Uploading metadata file ...", False)
diff --git a/image_creator/os_type/__init__.py b/image_creator/os_type/__init__.py
index 1d648a568b37430ac255269b3ed16f3453493cf3..fa2fc92716676008cc2b51efcaaa773b14b4436d 100644
--- a/image_creator/os_type/__init__.py
+++ b/image_creator/os_type/__init__.py
@@ -420,7 +420,7 @@ class OSBase(object):
 
         self.out.output()
 
-    @sysprep('Shrinking image', nomount=True)
+    @sysprep('Shrinking image (may take a while)', nomount=True)
     def _shrink(self):
         """Shrink the last file system and update the partition table"""
         self.image.shrink()
diff --git a/image_creator/os_type/windows/__init__.py b/image_creator/os_type/windows/__init__.py
index a664c464ea9ef4f4f6cca88b22589cc0218cb9bd..002260e4700b7f98ed4b5a286436ed663e31d95e 100644
--- a/image_creator/os_type/windows/__init__.py
+++ b/image_creator/os_type/windows/__init__.py
@@ -701,12 +701,16 @@ class Windows(OSBase):
         """Check if winexe works on the Windows VM"""
 
         retries = self.sysprep_params['connection_retries'].value
+        timeout = [2]
+        for i in xrange(1, retries - 1):
+            timeout.insert(0, timeout[0] * 2)
+
         # If the connection_retries parameter is set to 0 disable the
         # connectivity check
         if retries == 0:
             return True
 
-        for i in range(retries):
+        for i in xrange(retries):
             (stdout, stderr, rc) = self.vm.rexec('cmd /C', fatal=False,
                                                  debug=True)
             if rc == 0:
@@ -721,6 +725,7 @@ class Windows(OSBase):
             self.out.output("failed! See: `%s' for the full output" % log.name)
             if i < retries - 1:
                 self.out.output("retrying ...", False)
+                time.sleep(timeout.pop())
 
         raise FatalError("Connection to the Windows VM failed after %d retries"
                          % retries)
@@ -866,8 +871,17 @@ class Windows(OSBase):
 
             tmp = uuid.uuid4().hex
             self.image.g.mkdir_p("%s/%s" % (self.systemroot, tmp))
-            self._add_cleanup('virtio', self.image.g.rm_rf,
-                              "%s/%s" % (self.systemroot, tmp))
+
+            # This is a hack. We create a function here and pass it to
+            # _add_cleanup because self.image.g may change and the _add_cleanup
+            # will cache it which is wrong. For older versions of the guestfs
+            # library we recreate the g handler in enable_guestfs() and the
+            # program will crash if cleanup retains an older value for the
+            # guestfs handler.
+            def remove_tmp():
+                self.image.g.rm_rf("%s/%s" % (self.systemroot, tmp))
+
+            self._add_cleanup('virtio', remove_tmp)
 
             for fname in os.listdir(dirname):
                 full_path = os.path.join(dirname, fname)
@@ -929,38 +943,43 @@ class Windows(OSBase):
     def _boot_virtio_vm(self):
         """Boot the media and install the VirtIO drivers"""
 
-        timeout = self.sysprep_params['boot_timeout'].value
-        shutdown_timeout = self.sysprep_params['shutdown_timeout'].value
-        virtio_timeout = self.sysprep_params['virtio_timeout'].value
-        self.out.output("Starting Windows VM ...", False)
-        booted = False
+        old_windows = self.check_version(6, 1) <= 0
+        self.image.disable_guestfs()
         try:
-            if self.check_version(6, 1) <= 0:
-                self.vm.start()
-            else:
-                self.vm.interface = 'ide'
-                self.vm.start(extra_disk=('/dev/null', 'virtio'))
-                self.vm.interface = 'virtio'
-
-            self.out.success("started (console on VNC display: %d)" %
-                             self.vm.display)
-            self.out.output("Waiting for Windows to boot ...", False)
-            if not self.vm.wait_on_serial(timeout):
-                raise FatalError("Windows VM booting timed out!")
-            self.out.success('done')
-            booted = True
-            self.out.output("Installing new drivers ...", False)
-            if not self.vm.wait_on_serial(virtio_timeout):
-                raise FatalError("Windows VirtIO installation timed out!")
-            self.out.success('done')
-            self.out.output('Shutting down ...', False)
-            (_, stderr, rc) = self.vm.wait(shutdown_timeout)
-            if rc != 0 or "terminating on signal" in stderr:
-                raise FatalError("Windows VM died unexpectedly!\n\n"
-                                 "(rc=%d)\n%s" % (rc, stderr))
-            self.out.success('done')
+            timeout = self.sysprep_params['boot_timeout'].value
+            shutdown_timeout = self.sysprep_params['shutdown_timeout'].value
+            virtio_timeout = self.sysprep_params['virtio_timeout'].value
+            self.out.output("Starting Windows VM ...", False)
+            booted = False
+            try:
+                if old_windows:
+                    self.vm.start()
+                else:
+                    self.vm.interface = 'ide'
+                    self.vm.start(extra_disk=('/dev/null', 'virtio'))
+                    self.vm.interface = 'virtio'
+
+                self.out.success("started (console on VNC display: %d)" %
+                                 self.vm.display)
+                self.out.output("Waiting for Windows to boot ...", False)
+                if not self.vm.wait_on_serial(timeout):
+                    raise FatalError("Windows VM booting timed out!")
+                self.out.success('done')
+                booted = True
+                self.out.output("Installing new drivers ...", False)
+                if not self.vm.wait_on_serial(virtio_timeout):
+                    raise FatalError("Windows VirtIO installation timed out!")
+                self.out.success('done')
+                self.out.output('Shutting down ...', False)
+                (_, stderr, rc) = self.vm.wait(shutdown_timeout)
+                if rc != 0 or "terminating on signal" in stderr:
+                    raise FatalError("Windows VM died unexpectedly!\n\n"
+                                     "(rc=%d)\n%s" % (rc, stderr))
+                self.out.success('done')
+            finally:
+                self.vm.stop(shutdown_timeout if booted else 1, fatal=False)
         finally:
-            self.vm.stop(shutdown_timeout if booted else 1, fatal=False)
+            self.image.enable_guestfs()
 
         with self.mount(readonly=True, silent=True):
             self.virtio_state = self.compute_virtio_state()
diff --git a/image_creator/util.py b/image_creator/util.py
index d5370ffd06f9daa10e9a003719a680c1bdbeafda..dfae024be68350b58352b8c00e491ef9905e75d7 100644
--- a/image_creator/util.py
+++ b/image_creator/util.py
@@ -20,10 +20,14 @@ the package.
 """
 
 import sh
-import hashlib
 import time
 import os
 import re
+import json
+import tempfile
+from sh import qemu_img
+from sh import qemu_nbd
+from sh import modprobe
 
 
 class FatalError(Exception):
@@ -31,6 +35,22 @@ class FatalError(Exception):
     pass
 
 
+def image_info(image):
+    """Returns information about an image file"""
+    info = qemu_img('info', '--output', 'json', image)
+    return json.loads(str(info))
+
+
+def create_snapshot(source, target_dir):
+    """Returns a qcow2 snapshot of an image file"""
+
+    snapfd, snap = tempfile.mkstemp(prefix='snapshot-', dir=target_dir)
+    os.close(snapfd)
+    qemu_img('create', '-f', 'qcow2', '-o',
+             'backing_file=%s' % os.path.abspath(source), snap)
+    return snap
+
+
 def get_command(command):
     """Return a file system binary command"""
     def find_sbin_command(command, exception):
@@ -90,35 +110,6 @@ def free_space(dirname):
     return stat.f_bavail * stat.f_frsize
 
 
-class MD5:
-    """Represents MD5 computations"""
-    def __init__(self, output):
-        """Create an MD5 instance"""
-        self.out = output
-
-    def compute(self, filename, size):
-        """Compute the MD5 checksum of a file"""
-        MB = 2 ** 20
-        BLOCKSIZE = 4 * MB  # 4MB
-
-        prog_size = ((size + MB - 1) // MB)  # in MB
-        progressbar = self.out.Progress(prog_size, "Calculating md5sum", 'mb')
-        md5 = hashlib.md5()
-        with open(filename, "r") as src:
-            left = size
-            while left > 0:
-                length = min(left, BLOCKSIZE)
-                data = src.read(length)
-                md5.update(data)
-                left -= length
-                progressbar.goto((size - left) // MB)
-
-        checksum = md5.hexdigest()
-        progressbar.success(checksum)
-
-        return checksum
-
-
 def virtio_versions(virtio_state):
     """Returns the versions of the drivers defined by the virtio state"""
 
@@ -131,4 +122,59 @@ def virtio_versions(virtio_state):
 
     return ret
 
+
+class QemuNBD(object):
+    """Wrapper class for the qemu-nbd tool"""
+
+    def __init__(self, image):
+        """Initialize an instance"""
+        self.image = image
+        self.device = None
+        self.pattern = re.compile('^nbd\d+$')
+
+    def _list_devices(self):
+        """Returns all the NBD block devices"""
+        return set([d for d in os.listdir('/dev/') if self.pattern.match(d)])
+
+    def connect(self, ro=True):
+        """Connect the image to a free NBD device"""
+        devs = self._list_devices()
+
+        if len(devs) == 0:  # Is nbd module loaded?
+            modprobe('nbd', 'max_part=16')
+            # Wait a second for /dev to be populated
+            time.sleep(1)
+            devs = self._list_devices()
+            if len(devs) == 0:
+                raise FatalError("/dev/nbd* devices not present!")
+
+        # Ignore the nbd block devices that are in use
+        with open('/proc/partitions') as partitions:
+            for line in iter(partitions):
+                entry = line.split()
+                if len(entry) != 4:
+                    continue
+                if entry[3] in devs:
+                    devs.remove(entry[3])
+
+        if len(devs) == 0:
+            raise FatalError("All NBD block devices are busy!")
+
+        device = '/dev/%s' % devs.pop()
+        args = ['-c', device]
+        if ro:
+            args.append('-r')
+        args.append(self.image)
+
+        qemu_nbd(*args)
+        self.device = device
+        return device
+
+    def disconnect(self):
+        """Disconnect the image from the connected device"""
+        assert self.device is not None, "No device connected"
+
+        qemu_nbd('-d', self.device)
+        self.device = None
+
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :