def _find_volumes(self, volume_system, vstype='detect'): """Finds all volumes based on the pytsk3 library.""" try: # noinspection PyUnresolvedReferences import pytsk3 except ImportError: logger.error("pytsk3 not installed, could not detect volumes") raise ModuleNotFoundError("pytsk3") baseimage = None try: # ewf raw image is now available on base mountpoint # either as ewf1 file or as .dd file raw_path = volume_system.parent.get_raw_path() # noinspection PyBroadException try: baseimage = pytsk3.Img_Info(raw_path) except Exception: logger.error( "Failed retrieving image info (possible empty image).", exc_info=True) return [] try: volumes = pytsk3.Volume_Info( baseimage, getattr(pytsk3, 'TSK_VS_TYPE_' + vstype.upper()), volume_system.parent.offset // volume_system.disk.block_size) volume_system.volume_source = 'multi' return volumes except Exception as e: # some bug in sleuthkit makes detection sometimes difficult, so we hack around it: if "(GPT or DOS at 0)" in str(e) and vstype != 'gpt': volume_system.vstype = 'gpt' # noinspection PyBroadException try: logger.warning( "Error in retrieving volume info: TSK couldn't decide between GPT and DOS, " "choosing GPT for you. Use --vstype=dos to force DOS.", exc_info=True) volumes = pytsk3.Volume_Info( baseimage, getattr(pytsk3, 'TSK_VS_TYPE_GPT')) volume_system.volume_source = 'multi' return volumes except Exception as e: logger.exception( "Failed retrieving image info (possible empty image)." ) raise SubsystemError(e) else: logger.exception( "Failed retrieving image info (possible empty image).") raise SubsystemError(e) finally: if baseimage: baseimage.close() del baseimage
def carve(self, freespace=True): """Call this method to carve the free space of the volume for (deleted) files. Note that photorec has its own interface that temporarily takes over the shell. :param freespace: indicates whether the entire volume should be carved (False) or only the free space (True) :type freespace: bool :return: string to the path where carved data is available :raises CommandNotFoundError: if the underlying command does not exist :raises SubsystemError: if the underlying command fails :raises NoMountpointAvailableError: if there is no mountpoint available :raises NoLoopbackAvailableError: if there is no loopback available (only when volume has no slot number) """ self._make_mountpoint(var_name='carve', suffix="carve", in_paths=True) # if no slot, we need to make a loopback that we can use to carve the volume loopback_was_created_for_carving = False if not self.slot: if not self.loopback: self._find_loopback() # Can't carve if volume has no slot number and can't be mounted on loopback. loopback_was_created_for_carving = True # noinspection PyBroadException try: _util.check_call_([ "photorec", "/d", self._paths['carve'] + os.sep, "/cmd", self.loopback, ("freespace," if freespace else "") + "search" ]) # clean out the loop device if we created it specifically for carving if loopback_was_created_for_carving: # noinspection PyBroadException try: _util.check_call_(['losetup', '-d', self.loopback]) except Exception: pass else: self.loopback = "" return self._paths['carve'] except Exception as e: logger.exception("Failed carving the volume.") raise SubsystemError(e) else: # noinspection PyBroadException try: _util.check_call_([ "photorec", "/d", self._paths['carve'] + os.sep, "/cmd", self.get_raw_path(), str(self.slot) + (",freespace" if freespace else "") + ",search" ]) return self._paths['carve'] except Exception as e: logger.exception("Failed carving the volume.") raise SubsystemError(e)
def _detect_vss_volumes(self, path): """Detect volume shadow copy volumes in the specified path.""" try: volume_info = _util.check_output_([ "vshadowinfo", "-o", str(self.parent.offset), self.parent.get_raw_path() ]) except Exception as e: logger.exception( "Failed obtaining info from the volume shadow copies.") raise SubsystemError(e) current_store = None for line in volume_info.splitlines(): line = line.strip() if line.startswith("Store:"): idx = line.split(":")[-1].strip() current_store = self._make_subvolume( index=self._format_index(idx), flag='alloc', offset=0) current_store._paths['vss_store'] = os.path.join( path, 'vss' + idx) current_store.info['fsdescription'] = 'VSS Store' elif line.startswith("Volume size"): current_store.size = int( line.split(":")[-1].strip().split()[0]) elif line.startswith("Creation time"): current_store.info['creation_time'] = line.split( ":")[-1].strip() return self.volumes
def mount(self): """Based on the file system type as determined by :func:`determine_fs_type`, the proper mount command is executed for this volume. The volume is mounted in a temporary path (or a pretty path if :attr:`pretty` is enabled) in the mountpoint as specified by :attr:`mountpoint`. If the file system type is a LUKS container or LVM, additional methods may be called, adding subvolumes to :attr:`volumes` :raises NotMountedError: if the parent volume/disk is not mounted :raises NoMountpointAvailableError: if no mountpoint was found :raises NoLoopbackAvailableError: if no loopback device was found :raises UnsupportedFilesystemError: if the fstype is not supported for mounting :raises SubsystemError: if one of the underlying commands failed """ if not self.parent.is_mounted: raise NotMountedError(self.parent) self.filesystem = self.determine_fs_type() self._load_fsstat_data() # Prepare mount command try: self.filesystem.mount() self.was_mounted = True self.is_mounted = True except Exception as e: logger.exception("Execution failed due to {} {}".format(type(e), e), exc_info=True) if not isinstance(e, ImageMounterError): raise SubsystemError(e) else: raise
def clean_unmount(cmd, mountpoint, tries=5, rmdir=True): cmd.append(mountpoint) # AVFS mounts are not actually unmountable, but are symlinked. if os.path.exists(os.path.join(mountpoint, 'avfs')): os.remove(os.path.join(mountpoint, 'avfs')) # noinspection PyProtectedMember logger.debug("Removed {}".format(os.path.join(mountpoint, 'avfs'))) elif os.path.islink(mountpoint): pass # if it is a symlink, we can simply skip to removing it else: # Perform unmount # noinspection PyBroadException try: check_call_(cmd) except Exception as e: raise SubsystemError(e) # Remove mountpoint only if needed if not rmdir: return for _ in range(tries): if not os.path.ismount(mountpoint): # Unmount was successful, remove mountpoint if os.path.islink(mountpoint): os.unlink(mountpoint) else: os.rmdir(mountpoint) break else: time.sleep(1) if os.path.isdir(mountpoint): raise CleanupError()
def check_call_(cmd, wrap_error=False, *args, **kwargs): logger.debug('$ {0}'.format(' '.join(cmd))) try: return subprocess.check_call(cmd, *args, **kwargs) except Exception as e: if wrap_error: raise SubsystemError(e) else: raise
def mount(self, volume): """Add the volume to a RAID system. The RAID array is activated as soon as the array can be activated. :raises NoLoopbackAvailableError: if no loopback device was found """ volume._find_loopback() raid_status = None try: # use mdadm to mount the loopback to a md device # incremental and run as soon as available output = _util.check_output_(['mdadm', '-IR', volume.loopback], stderr=subprocess.STDOUT) match = re.findall(r"attached to ([^ ,]+)", output) if match: volume._paths['md'] = os.path.realpath(match[0]) if 'which is already active' in output: logger.info( "RAID is already active in other volume, using %s", volume._paths['md']) raid_status = 'active' elif 'not enough to start' in output: volume._paths['md'] = volume._paths['md'].replace( "/dev/md/", "/dev/md") logger.info( "RAID volume added, but not enough to start %s", volume._paths['md']) raid_status = 'waiting' else: logger.info("RAID started at {0}".format( volume._paths['md'])) raid_status = 'active' except Exception as e: logger.exception("Failed mounting RAID.") volume._free_loopback() raise SubsystemError(e) # search for the RAID volume for v in volume.disk.parser.get_volumes(): if v._paths.get("md") == volume._paths['md'] and v.volumes: logger.debug("Adding existing volume %s to volume %s", v.volumes[0], volume) v.volumes[0].info['raid_status'] = raid_status volume.volumes.volumes.append(v.volumes[0]) return v.volumes[0] else: logger.debug("Creating RAID volume for %s", self) container = volume.volumes._make_single_subvolume(flag='alloc', offset=0, size=volume.size) container.info['fsdescription'] = 'RAID Volume' container.info['raid_status'] = raid_status return container
def mount(self, volume): """Mounts a BDE container. Uses key material provided by the :attr:`keys` attribute. The key material should be provided in the same format as to :cmd:`bdemount`, used as follows: k:full volume encryption and tweak key p:passphrase r:recovery password s:file to startup key (.bek) :return: the Volume contained in the BDE container :raises ArgumentError: if the keys argument is invalid :raises SubsystemError: when the underlying command fails """ volume._paths['bde'] = tempfile.mkdtemp(prefix='image_mounter_bde_') try: if volume.key: t, v = volume.key.split(':', 1) key = ['-' + t, v] else: logger.warning("No key material provided for %s", volume) key = [] except ValueError: logger.exception( "Invalid key material provided (%s) for %s. Expecting [arg]:[value]", volume.key, volume) raise ArgumentError() # noinspection PyBroadException try: cmd = [ "bdemount", volume.get_raw_path(), volume._paths['bde'], '-o', str(volume.offset) ] cmd.extend(key) _util.check_call_(cmd) except Exception as e: del volume._paths['bde'] logger.exception("Failed mounting BDE volume %s.", volume) raise SubsystemError(e) container = volume.volumes._make_single_subvolume(flag='alloc', offset=0, size=volume.size) container.info['fsdescription'] = 'BDE Volume' return container
def detect_volume_shadow_copies(self): """Method to call vshadowmount and mount NTFS volume shadow copies. :return: iterable with the :class:`Volume` objects of the VSS :raises CommandNotFoundError: if the underlying command does not exist :raises SubSystemError: if the underlying command fails :raises NoMountpointAvailableError: if there is no mountpoint available """ self._paths['vss'] = self._make_mountpoint(suffix="vss") try: _util.check_call_(["vshadowmount", "-o", str(self.offset), self.get_raw_path(), self._paths['vss']]) except Exception as e: logger.exception("Failed mounting the volume shadow copies.") raise SubsystemError(e) else: return self.volumes.detect_volumes(vstype='vss')
def bindmount(self, mountpoint): """Bind mounts the volume to another mountpoint. Only works if the volume is already mounted. :raises NotMountedError: when the volume is not yet mounted :raises SubsystemError: when the underlying command failed """ if not self.mountpoint: raise NotMountedError(self) try: _util.check_call_(['mount', '--bind', self.mountpoint, mountpoint], stdout=subprocess.PIPE) if 'bindmounts' in self._paths: self._paths['bindmounts'].append(mountpoint) else: self._paths['bindmounts'] = [mountpoint] return True except Exception as e: logger.exception("Error bind mounting {0}.".format(self)) raise SubsystemError(e)
def unmount(self, allow_lazy=False): if self.mdpath is not None: # MD arrays are a bit complicated, we also check all other volumes that are part of this array and # unmount them as well. logger.debug( "All other volumes that use %s as well will also be unmounted", self.mdpath) for v in self._iter_same_md_volumes(): v.unmount(allow_lazy=allow_lazy) try: _util.check_output_(["mdadm", '--stop', self.mdpath], stderr=subprocess.STDOUT) except Exception as e: raise SubsystemError(e) self.mdpath = None super().unmount(allow_lazy=allow_lazy)
def unmount(self, allow_lazy=False): """Unounts the volume from the filesystem. :raises SubsystemError: if one of the underlying processes fails :raises CleanupError: if the cleanup fails """ for volume in self.volumes: try: volume.unmount(allow_lazy=allow_lazy) except ImageMounterError: pass if self.is_mounted: logger.info("Unmounting volume %s", self) if self._paths.get('vss'): try: _util.clean_unmount(['fusermount', '-u'], self._paths['vss']) except SubsystemError: if not allow_lazy: raise _util.clean_unmount(['fusermount', '-uz'], self._paths['vss']) del self._paths['vss'] if self._paths.get('bindmounts'): for mp in self._paths['bindmounts']: _util.clean_unmount(['umount'], mp, rmdir=False) del self._paths['bindmounts'] if self._paths.get('carve'): try: shutil.rmtree(self._paths['carve']) except OSError as e: raise SubsystemError(e) else: del self._paths['carve'] self.filesystem.unmount(allow_lazy=allow_lazy) self.is_mounted = False
def unmount(self, allow_lazy=False): """Unounts the volume from the filesystem. :raises SubsystemError: if one of the underlying processes fails :raises CleanupError: if the cleanup fails """ for volume in self.volumes: try: volume.unmount(allow_lazy=allow_lazy) except ImageMounterError: pass if self.is_mounted: logger.info("Unmounting volume %s", self) if self.loopback and self.info.get('volume_group'): _util.check_call_( ["lvm", 'vgchange', '-a', 'n', self.info['volume_group']], wrap_error=True, stdout=subprocess.PIPE) self.info['volume_group'] = "" if self.loopback and self._paths.get('luks'): _util.check_call_(['cryptsetup', 'luksClose', self._paths['luks']], wrap_error=True, stdout=subprocess.PIPE) del self._paths['luks'] if self._paths.get('bde'): try: _util.clean_unmount(['fusermount', '-u'], self._paths['bde']) except SubsystemError: if not allow_lazy: raise _util.clean_unmount(['fusermount', '-uz'], self._paths['bde']) del self._paths['bde'] if self._paths.get('md'): md_path = self._paths['md'] del self._paths[ 'md'] # removing it here to ensure we do not enter an infinite loop, will add it back later # MD arrays are a bit complicated, we also check all other volumes that are part of this array and # unmount them as well. logger.debug( "All other volumes that use %s as well will also be unmounted", md_path) for v in self.disk.get_volumes(): if v != self and v._paths.get('md') == md_path: v.unmount(allow_lazy=allow_lazy) try: _util.check_output_(["mdadm", '--stop', md_path], stderr=subprocess.STDOUT) except Exception as e: self._paths['md'] = md_path raise SubsystemError(e) if self._paths.get('vss'): try: _util.clean_unmount(['fusermount', '-u'], self._paths['vss']) except SubsystemError: if not allow_lazy: raise _util.clean_unmount(['fusermount', '-uz'], self._paths['vss']) del self._paths['vss'] if self.loopback: _util.check_call_(['losetup', '-d', self.loopback], wrap_error=True) self.loopback = "" if self._paths.get('bindmounts'): for mp in self._paths['bindmounts']: _util.clean_unmount(['umount'], mp, rmdir=False) del self._paths['bindmounts'] if self.mountpoint: _util.clean_unmount(['umount'], self.mountpoint) self.mountpoint = "" if self._paths.get('carve'): try: shutil.rmtree(self._paths['carve']) except OSError as e: raise SubsystemError(e) else: del self._paths['carve'] self.is_mounted = False
def mount(self, fstype=None): """Based on the file system type as determined by :func:`determine_fs_type`, the proper mount command is executed for this volume. The volume is mounted in a temporary path (or a pretty path if :attr:`pretty` is enabled) in the mountpoint as specified by :attr:`mountpoint`. If the file system type is a LUKS container or LVM, additional methods may be called, adding subvolumes to :attr:`volumes` :raises NotMountedError: if the parent volume/disk is not mounted :raises NoMountpointAvailableError: if no mountpoint was found :raises NoLoopbackAvailableError: if no loopback device was found :raises UnsupportedFilesystemError: if the fstype is not supported for mounting :raises SubsystemError: if one of the underlying commands failed """ if not self.parent.is_mounted: raise NotMountedError(self.parent) raw_path = self.get_raw_path() if fstype is None: fstype = self.determine_fs_type() self._load_fsstat_data() # we need a mountpoint if it is not a lvm or luks volume if fstype not in ('luks', 'lvm', 'bde', 'raid', 'volumesystem') and \ fstype in FILE_SYSTEM_TYPES: self._make_mountpoint() # Prepare mount command try: def call_mount(type, opts): cmd = ['mount', raw_path, self.mountpoint, '-t', type, '-o', opts] if not self.disk.read_write: cmd[-1] += ',ro' _util.check_output_(cmd, stderr=subprocess.STDOUT) if fstype == 'ext': call_mount('ext4', 'noexec,noload,loop,offset=' + str(self.offset)) elif fstype == 'ufs': call_mount('ufs', 'ufstype=ufs2,loop,offset=' + str(self.offset)) elif fstype == 'ntfs': call_mount('ntfs', 'show_sys_files,noexec,force,loop,offset=' + str(self.offset)) elif fstype == 'xfs': call_mount('xfs', 'norecovery,loop,offset=' + str(self.offset)) elif fstype == 'hfs+': call_mount('hfsplus', 'force,loop,offset=' + str(self.offset) + ',sizelimit=' + str(self.size)) elif fstype in ('iso', 'udf', 'squashfs', 'cramfs', 'minix', 'fat', 'hfs'): mnt_type = {'iso': 'iso9660', 'fat': 'vfat'}.get(fstype, fstype) call_mount(mnt_type, 'loop,offset=' + str(self.offset)) elif fstype == 'vmfs': self._find_loopback() _util.check_call_(['vmfs-fuse', self.loopback, self.mountpoint], stdout=subprocess.PIPE) elif fstype == 'unknown': # mounts without specifying the filesystem type cmd = ['mount', raw_path, self.mountpoint, '-o', 'loop,offset=' + str(self.offset)] if not self.disk.read_write: cmd[-1] += ',ro' _util.check_call_(cmd, stdout=subprocess.PIPE) elif fstype == 'jffs2': self._open_jffs2() elif fstype == 'luks': self._open_luks_container() elif fstype == 'bde': self._open_bde_container() elif fstype == 'lvm': self._open_lvm() self.volumes.vstype = 'lvm' for _ in self.volumes.detect_volumes('lvm'): pass elif fstype == 'raid': self._open_raid_volume() elif fstype == 'dir': os.rmdir(self.mountpoint) os.symlink(raw_path, self.mountpoint) elif fstype == 'volumesystem': for _ in self.volumes.detect_volumes(): pass else: try: size = self.size // self.disk.block_size except TypeError: size = self.size logger.warning("Unsupported filesystem {0} (type: {1}, block offset: {2}, length: {3})" .format(self, fstype, self.offset // self.disk.block_size, size)) raise UnsupportedFilesystemError(fstype) self.was_mounted = True self.is_mounted = True self.fstype = fstype except Exception as e: logger.exception("Execution failed due to {} {}".format(type(e), e), exc_info=True) try: if self.mountpoint: os.rmdir(self.mountpoint) self.mountpoint = "" if self.loopback: self.loopback = "" except Exception as e2: logger.exception("Clean-up failed", exc_info=True) if not isinstance(e, ImageMounterError): raise SubsystemError(e) else: raise
def _detect_mmls_volumes(self, vstype='detect'): """Finds and mounts all volumes based on mmls.""" try: cmd = ['mmls'] if self.parent.offset: cmd.extend( ['-o', str(self.parent.offset // self.disk.block_size)]) if vstype != 'detect': cmd.extend(['-t', vstype]) cmd.append(self.parent.get_raw_path()) output = _util.check_output_(cmd, stderr=subprocess.STDOUT) self.volume_source = 'multi' except Exception as e: # some bug in sleuthkit makes detection sometimes difficult, so we hack around it: if hasattr(e, 'output') and "(GPT or DOS at 0)" in e.output.decode( ) and vstype != 'gpt': self.vstype = 'gpt' # noinspection PyBroadException try: logger.warning( "Error in retrieving volume info: mmls couldn't decide between GPT and DOS, " "choosing GPT for you. Use --vstype=dos to force DOS.", exc_info=True) cmd = ['mmls', '-t', 'gpt', self.parent.get_raw_path()] output = _util.check_output_(cmd, stderr=subprocess.STDOUT) self.volume_source = 'multi' except Exception as e: logger.exception("Failed executing mmls command") raise SubsystemError(e) else: logger.exception("Failed executing mmls command") raise SubsystemError(e) output = output.split("Description", 1)[-1] for line in output.splitlines(): if not line: continue # noinspection PyBroadException try: values = line.split(None, 5) # sometimes there are only 5 elements available description = '' index, slot, start, end, length = values[0:5] if len(values) > 5: description = values[5] volume = self._make_subvolume( index=self._format_index(int(index[:-1])), offset=int(start) * self.disk.block_size, size=int(length) * self.disk.block_size) volume.info['fsdescription'] = description except Exception: logger.exception("Error while parsing mmls output") continue if slot.lower() == 'meta': volume.flag = 'meta' logger.info( "Found meta volume: block offset: {0}, length: {1}".format( start, length)) elif slot.lower().startswith('-----'): volume.flag = 'unalloc' logger.info( "Found unallocated space: block offset: {0}, length: {1}". format(start, length)) else: volume.flag = 'alloc' if ":" in slot: volume.slot = _util.determine_slot(*slot.split(':')) else: volume.slot = _util.determine_slot(-1, slot) self._assign_disktype_data(volume) logger.info( "Found allocated {2}: block offset: {0}, length: {1} ". format(start, length, volume.info['fsdescription'])) yield volume
def _detect_parted_volumes(self, vstype='detect'): """Finds and mounts all volumes based on parted.""" # for some reason, parted does not properly return extended volume types in its machine # output, so we need to execute it twice. meta_volumes = [] # noinspection PyBroadException try: output = _util.check_output_( ['parted', self.parent.get_raw_path(), 'print'], stdin=subprocess.PIPE) for line in output.splitlines(): if 'extended' in line: meta_volumes.append(int(line.split()[0])) except Exception: logger.exception("Failed executing parted command.") # skip detection of meta volumes # noinspection PyBroadException try: # parted does not support passing in the vstype. It either works, or it doesn't. cmd = [ 'parted', self.parent.get_raw_path(), '-sm', 'unit s', 'print free' ] output = _util.check_output_(cmd, stdin=subprocess.PIPE) self.volume_source = 'multi' except Exception as e: logger.exception("Failed executing parted command") raise SubsystemError(e) num = 0 for line in output.splitlines(): if line.startswith( "Warning" ) or not line or ':' not in line or line.startswith( self.parent.get_raw_path()): continue line = line[:-1] # remove last ; try: slot, start, end, length, description = line.split(':', 4) if ':' in description: description, label, flags = description.split(':', 2) else: description, label, flags = description, '', '' try: slot = int(slot) except ValueError: continue volume = self._make_subvolume( index=self._format_index(num), offset=int(start[:-1]) * self.disk.block_size, # remove last s size=int(length[:-1]) * self.disk.block_size) volume.info['fsdescription'] = description if label: volume.info['label'] = label if flags: volume.info['parted_flags'] = flags # TODO: detection of meta volumes if description == 'free': volume.flag = 'unalloc' logger.info( "Found unallocated space: block offset: {0}, length: {1}" .format(start[:-1], length[:-1])) elif slot in meta_volumes: volume.flag = 'meta' volume.slot = slot logger.info( "Found meta volume: block offset: {0}, length: {1}". format(start[:-1], length[:-1])) else: volume.flag = 'alloc' volume.slot = slot self._assign_disktype_data(volume) logger.info( "Found allocated {2}: block offset: {0}, length: {1} ". format(start[:-1], length[:-1], volume.info['fsdescription'])) except AttributeError: logger.exception("Error while parsing parted output") continue num += 1 yield volume
def mount(self, volume): """Command that is an alternative to the :func:`mount` command that opens a LUKS container. The opened volume is added to the subvolume set of this volume. Requires the user to enter the key manually. TODO: add support for :attr:`keys` :return: the Volume contained in the LUKS container, or None on failure. :raises NoLoopbackAvailableError: when no free loopback could be found :raises IncorrectFilesystemError: when this is not a LUKS volume :raises SubsystemError: when the underlying command fails """ # Open a loopback device volume._find_loopback() # Check if this is a LUKS device # noinspection PyBroadException try: _util.check_call_(["cryptsetup", "isLuks", volume.loopback], stderr=subprocess.STDOUT) # ret = 0 if isLuks except Exception: logger.warning("Not a LUKS volume") # clean the loopback device, we want this method to be clean as possible # noinspection PyBroadException try: volume._free_loopback() except Exception: pass raise IncorrectFilesystemError() try: extra_args = [] key = None if volume.key: t, v = volume.key.split(':', 1) if t == 'p': # passphrase key = v elif t == 'f': # key-file extra_args = ['--key-file', v] elif t == 'm': # master-key-file extra_args = ['--master-key-file', v] else: logger.warning("No key material provided for %s", volume) except ValueError: logger.exception( "Invalid key material provided (%s) for %s. Expecting [arg]:[value]", volume.key, volume) volume._free_loopback() raise ArgumentError() # Open the LUKS container volume._paths['luks'] = 'image_mounter_luks_' + str( random.randint(10000, 99999)) # noinspection PyBroadException try: cmd = [ "cryptsetup", "luksOpen", volume.loopback, volume._paths['luks'] ] cmd.extend(extra_args) if not volume.disk.read_write: cmd.insert(1, '-r') if key is not None: logger.debug('$ {0}'.format(' '.join(cmd))) # for py 3.2+, we could have used input=, but that doesn't exist in py2.7. p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.communicate(key.encode("utf-8")) p.wait() retcode = p.poll() if retcode: raise KeyInvalidError() else: _util.check_call_(cmd) except ImageMounterError: del volume._paths['luks'] volume._free_loopback() raise except Exception as e: del volume._paths['luks'] volume._free_loopback() raise SubsystemError(e) size = None # noinspection PyBroadException try: result = _util.check_output_( ["cryptsetup", "status", volume._paths['luks']]) for l in result.splitlines(): if "size:" in l and "key" not in l: size = int( l.replace("size:", "").replace( "sectors", "").strip()) * volume.disk.block_size except Exception: pass container = volume.volumes._make_single_subvolume(flag='alloc', offset=0, size=size) container.info['fsdescription'] = 'LUKS Volume' return container