commit dff9050a6b7e40cd34596aff2625c2ff61278cb4 Author: Eric Harney Date: Thu Aug 28 11:44:45 2014 -0400 Refuse invalid qcow2 backing files Don't allow qcow2 files that are pointing to backing files outside of: volume- volume-. volume-.tmp-snap- (optionally prefixed with /mnt/path) Change-Id: I465d9e804c78bcdccb3784a1601274d2b2c01851 diff --git a/cinder/tests/test_glusterfs.py b/cinder/tests/test_glusterfs.py index 3d1e857..236a074 100644 --- a/cinder/tests/test_glusterfs.py +++ b/cinder/tests/test_glusterfs.py @@ -83,6 +83,7 @@ class GlusterFsDriverTestCase(test.TestCase): TEST_SHARES_CONFIG_FILE = '/etc/cinder/test-shares.conf' TEST_TMP_FILE = '/tmp/tempfile' VOLUME_UUID = 'abcdefab-cdef-abcd-efab-cdefabcdefab' + VOLUME_NAME = 'volume-%s' % VOLUME_UUID SNAP_UUID = 'bacadaca-baca-daca-baca-dacadacadaca' SNAP_UUID_2 = 'bebedede-bebe-dede-bebe-dedebebedede' @@ -1795,7 +1796,8 @@ class GlusterFsDriverTestCase(test.TestCase): stale_snapshot['volume']) mock_read_info_file.assert_called_once_with(info_path, empty_if_missing=True) - mock_qemu_img_info.assert_called_once_with(stale_snap_path) + mock_qemu_img_info.assert_called_once_with(stale_snap_path, + volume['name']) mock_get_active_image.assert_called_once_with( stale_snapshot['volume']) mock_delete_stale_snap.assert_called_once_with(stale_snapshot) @@ -1878,8 +1880,8 @@ class GlusterFsDriverTestCase(test.TestCase): volume = self._simple_volume() vol_filename = volume['name'] - vol_filename_2 = volume['name'] + '.asdfjkl' - vol_filename_3 = volume['name'] + 'qwertyuiop' + vol_filename_2 = volume['name'] + '.abcd' + vol_filename_3 = volume['name'] + '.efef' hashed = drv._get_hash_str(self.TEST_EXPORT1) vol_dir = '%s/%s' % (self.TEST_MNT_POINT_BASE, hashed) vol_path = '%s/%s' % (vol_dir, vol_filename) @@ -2088,7 +2090,7 @@ class GlusterFsDriverTestCase(test.TestCase): info = imageutils.QemuImgInfo() info.file_format = 'raw' - drv._qemu_img_info(IgnoreArg()).AndReturn(info) + drv._qemu_img_info(IgnoreArg(), IgnoreArg()).AndReturn(info) base_driver.VolumeDriver.backup_volume(IgnoreArg(), IgnoreArg(), @@ -2124,7 +2126,7 @@ class GlusterFsDriverTestCase(test.TestCase): info = imageutils.QemuImgInfo() info.file_format = 'raw' - drv._qemu_img_info(IgnoreArg()).AndReturn(info) + drv._qemu_img_info(IgnoreArg(), IgnoreArg()).AndReturn(info) base_driver.VolumeDriver.backup_volume(IgnoreArg(), IgnoreArg(), @@ -2178,7 +2180,7 @@ class GlusterFsDriverTestCase(test.TestCase): info.file_format = 'raw' info.backing_file = 'file1' - drv._qemu_img_info(IgnoreArg()).AndReturn(info) + drv._qemu_img_info(IgnoreArg(), IgnoreArg()).AndReturn(info) mox.ReplayAll() @@ -2207,7 +2209,7 @@ class GlusterFsDriverTestCase(test.TestCase): info.file_format = 'qcow2' drv.db.volume_get(ctxt, volume['id']).AndReturn(volume) - drv._qemu_img_info(IgnoreArg()).AndReturn(info) + drv._qemu_img_info(IgnoreArg(), IgnoreArg()).AndReturn(info) mox.ReplayAll() diff --git a/cinder/volume/drivers/glusterfs.py b/cinder/volume/drivers/glusterfs.py index 602b359..22aa542 100644 --- a/cinder/volume/drivers/glusterfs.py +++ b/cinder/volume/drivers/glusterfs.py @@ -272,7 +272,8 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver): # Find the file which backs this file, which represents the point # when this snapshot was created. - img_info = self._qemu_img_info(forward_path) + img_info = self._qemu_img_info(forward_path, + snapshot['volume']['name']) path_to_snap_img = os.path.join(vol_path, img_info.backing_file) path_to_new_vol = self._local_path_volume(volume) @@ -510,7 +511,8 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver): 'backing_file=%s' % backing_path_full_path, new_snap_path] self._execute(*command, run_as_root=True) - info = self._qemu_img_info(backing_path_full_path) + info = self._qemu_img_info(backing_path_full_path, + snapshot['volume']['name']) backing_fmt = info.file_format command = ['qemu-img', 'rebase', '-u', @@ -598,7 +600,9 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver): snapshot_path = '%s/%s' % (self._local_volume_dir(snapshot['volume']), snapshot_file) - snapshot_path_img_info = self._qemu_img_info(snapshot_path) + snapshot_path_img_info = self._qemu_img_info( + snapshot_path, + snapshot['volume']['name']) vol_path = self._local_volume_dir(snapshot['volume']) @@ -624,7 +628,8 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver): base_path = os.path.join( self._local_volume_dir(snapshot['volume']), base_file) - base_file_img_info = self._qemu_img_info(base_path) + base_file_img_info = self._qemu_img_info( + base_path, snapshot['volume']['name']) new_base_file = base_file_img_info.backing_file base_id = None @@ -721,7 +726,8 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver): self._qemu_img_commit(higher_file_path) if highest_file is not None: highest_file_path = '%s/%s' % (vol_path, highest_file) - info = self._qemu_img_info(snapshot_path) + info = self._qemu_img_info(snapshot_path, + snapshot['volume']['name']) snapshot_file_fmt = info.file_format backing_fmt = ('-F', snapshot_file_fmt) @@ -856,6 +862,45 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver): del(snap_info[snapshot['id']]) self._write_info_file(info_path, snap_info) + def _get_backing_chain_for_path(self, volume, path): + """Returns list of dicts containing backing-chain information. + + Includes 'filename', and 'backing-filename' for each + applicable entry. + + Consider converting this to use --backing-chain and --output=json + when environment supports qemu-img 1.5.0. + + :param volume: volume reference + :param path: path to image file at top of chain + + """ + + output = [] + + info = self._qemu_img_info(path, volume['name']) + new_info = {} + new_info['filename'] = os.path.basename(path) + new_info['backing-filename'] = info.backing_file + + output.append(new_info) + + while new_info['backing-filename']: + filename = new_info['backing-filename'] + path = os.path.join(self._local_volume_dir(volume), filename) + info = self._qemu_img_info(path, volume['name']) + backing_filename = info.backing_file + new_info = {} + new_info['filename'] = filename + new_info['backing-filename'] = backing_filename + + output.append(new_info) + + return output + + def _qemu_img_commit(self, path): + return self._execute('qemu-img', 'commit', path, run_as_root=True) + def ensure_export(self, ctx, volume): """Synchronously recreates an export for a logical volume.""" @@ -889,7 +934,7 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver): data['options'] = self.shares[volume['provider_location']] # Test file for raw vs. qcow2 format - info = self._qemu_img_info(path) + info = self._qemu_img_info(path, volume['name']) data['format'] = info.file_format if data['format'] not in ['raw', 'qcow2']: msg = _('%s must be a valid raw or qcow2 image.') % path @@ -914,7 +959,7 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver): active_file = self.get_active_image_from_info(volume) active_file_path = '%s/%s' % (self._local_volume_dir(volume), active_file) - info = self._qemu_img_info(active_file_path) + info = self._qemu_img_info(active_file_path, volume['name']) backing_file = info.backing_file if backing_file: snapshots_exist = True @@ -954,7 +999,7 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver): ' driver when no snapshots exist.') raise exception.InvalidVolume(msg) - info = self._qemu_img_info(volume_path) + info = self._qemu_img_info(volume_path, volume['name']) backing_fmt = info.file_format if backing_fmt not in ['raw', 'qcow2']: @@ -1086,7 +1131,7 @@ class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver): volume_dir, self.get_active_image_from_info(volume)) - info = self._qemu_img_info(active_file_path) + info = self._qemu_img_info(active_file_path, volume['name']) if info.backing_file is not None: msg = _('No snapshots found in database, but ' diff --git a/cinder/volume/drivers/remotefs.py b/cinder/volume/drivers/remotefs.py index 85e625f..2f124bc 100644 --- a/cinder/volume/drivers/remotefs.py +++ b/cinder/volume/drivers/remotefs.py @@ -407,7 +407,7 @@ class RemoteFSSnapDriver(RemoteFSDriver): with open(info_path, 'w') as f: json.dump(snap_info, f, indent=1, sort_keys=True) - def _qemu_img_info(self, path): + def _qemu_img_info(self, path, volume_name): """Sanitize image_utils' qemu_img_info. This code expects to deal only with relative filenames. @@ -417,6 +417,19 @@ class RemoteFSSnapDriver(RemoteFSDriver): if info.image: info.image = os.path.basename(info.image) if info.backing_file: + basedir = self.configuration.glusterfs_mount_point_base + backing_file_template = \ + "(%(basedir)s/[0-9a-f]+/)?%" \ + "(volname)s(.(tmp-snap-)?[0-9a-f-]+)?$" % { + 'basedir': basedir, + 'volname': volume_name + } + if not re.match(backing_file_template, info.backing_file): + msg = _("File %(path)s has invalid backing file " + "%(bfile)s, aborting.") % {'path': path, + 'bfile': info.backing_file} + raise exception.GlusterfsException(msg) + info.backing_file = os.path.basename(info.backing_file) return info