def extract(archive_location, extracted_location, skip_symlinks=False): """ Extract the image archive tarball at ``archive_location`` to ``extracted_location``. Skip symlinks and links if ``skip_symlinks`` is True. """ utils.extract_tar( location=archive_location, target_dir=extracted_location, skip_symlinks=skip_symlinks, )
def get_images_from_tarball(location, target_dir, force_extract=False): """ Yield Image objects found in the tarball at `location` that will be extracted to `target_dir`. The tarball must be in the format of a "docker save" command tarball. If `force_extract` is False, do not extract to target_dir if target_dir already exists """ if force_extract or not os.path.exists(target_dir): utils.extract_tar(location, target_dir) return Image.get_images_from_dir(target_dir)
def extract(self, extracted_location, skip_symlinks=True): """ Extract this layer archive in the `extracted_location` directory and set this Layer ``extracted_location`` attribute to ``extracted_location``. """ self.extracted_location = extracted_location utils.extract_tar( location=self.archive_location, target_dir=extracted_location, skip_symlinks=skip_symlinks, )
def extract(self, target_dir, use_layer_id=True, force_extract=False): """ Extract layer tarball to `target_dir` directory. If `use_layer_id` is True, extract in a dir named ``target_dir/layer_id/`` Cache the location where this layer was last extracted in the ``self.extracted_to_location`` attribute. If `force_extract` is False, do not extract if self.extracted_to_location already exists """ if use_layer_id: self.extracted_to_location = path.join(target_dir, self.layer_id) if force_extract or not os.path.exists(self.extracted_to_location): utils.extract_tar(self.layer_location, self.extracted_to_location)
def test_extract_tree_with_colon_in_filenames(self): expected = ( 'colon/libc6:amd64.list', ) test_dir = self.get_test_loc('tar/colon.tar.xz') temp_dir = self.get_temp_dir() errors = utils.extract_tar(location=test_dir, target_dir=temp_dir) check_files(temp_dir, expected) assert not errors
def test_extract_tar_absolute(self): expected = ( 'tmp/subdir/a.txt', 'tmp/subdir/b.txt', ) test_dir = self.get_test_loc('tar/absolute_path.tar') temp_dir = self.get_temp_dir() errors = utils.extract_tar(location=test_dir, target_dir=temp_dir) check_files(temp_dir, expected) assert not errors
def test_extract_tar_relative(self): expected = () test_dir = self.get_test_loc('tar/tar_relative.tar') temp_dir = self.get_temp_dir() errors = utils.extract_tar(location=test_dir, target_dir=temp_dir) check_files(temp_dir, expected) assert errors for error in errors: assert 'skipping unsupported' in error assert 'with relative path' in error
def rebuild_rootfs(img, target_dir): """ Extract and merge all layers of the `image` Image in `target_dir`. Extraction is done in sequence from bottom (root) to top (latest layer) and the "whiteout" unionfs/overlayfs procedure is applied at each step as per the OCI spec: https://github.com/opencontainers/image-spec/blob/master/layer.md#whiteouts Return a list of deleted "whiteout" files. The extraction process consists of these steps: - extract the layer in a temp directory - find whiteouts in that layer temp dir - remove files/directories corresponding to these whiteouts in the target directory - remove whiteouts special marker files or dirs in the tempdirectory - move layer to the target directory, overwriting existing files See also some related implementations and links: https://github.com/moby/moby/blob/d1f470946/pkg/archive/whiteouts.go https://github.com/virt-manager/virt-bootstrap/commit/8a7e752d6614f8425874adbbdab16443ee0b1d9b https://github.com/goldmann/docker-squash https://github.com/moby/moby/blob/master/image/spec/v1.md https://github.com/moby/moby/blob/master/image/spec/v1.1.md https://github.com/moby/moby/blob/master/image/spec/v1.2.md """ assert path.isdir(target_dir) assert path.exists(target_dir) extract_errors = [] # log deletions deletions = [] for layer_num, layer in enumerate(img.layers): layer_id = layer.layer_id layer_tarball = path.join(img.base_location, layer_id, LAYER_TAR_FILE) logger.debug( 'Extracting layer {layer_num} tarball: {layer_tarball}'.format( **locals())) temp_target = tempfile.mkdtemp('container_inspector-docker') # 1. extract a layer to temp. # Note that we are not preserving any special file and any file permission utils.extract_tar(location=layer_tarball, target_dir=temp_target) logger.debug(' Extracted layer to: {}'.format(temp_target)) # 2. find whiteouts in that layer. layer_whiteouts = list(find_whiteouts(temp_target)) logger.debug( ' Merging extracted layers and applying AUFS whiteouts/deletes') logger.debug(' Whiteouts:\n' + ' \n'.join(map(repr, layer_whiteouts))) # 3. remove whiteouts in the previous layer stack (e.g. the WIP rootfs) for layer_whiteout_marker, target_whiteable_path in layer_whiteouts: logger.debug( ' Deleting whiteout dir or file: {target_whiteable_path}'. format(**locals())) whiteable = path.join(target_dir, target_whiteable_path) utils.delete(whiteable) # also delete the whiteout marker file utils.delete(layer_whiteout_marker) deletions.extend(target_whiteable_path) # 4. finall copy/overwrite the extracted layer over the WIP rootfs logger.debug( ' Moving extracted layer from: {temp_target} to: {target_dir}'. format(**locals())) utils.copytree(temp_target, target_dir) logger.debug(' Moved layer to: {}'.format(target_dir)) utils.delete(temp_target) return deletions