Implement UID/GID file ownership mapping for qcow2 images. The implementation consist of: For each layer: 1. Create qcow2 image with backing chain 2. Tar-in the content form tarball 2. Get a list of all members (stored files) of this tarball 3. For each member get the UID/GID 4. Apply the new ownership for this member in the qcow2 image --- src/virtBootstrap/sources/docker_source.py | 5 ++++- src/virtBootstrap/sources/file_source.py | 6 +++-- src/virtBootstrap/utils.py | 35 +++++++++++++++++++++++++++++- src/virtBootstrap/virt_bootstrap.py | 2 ++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/virtBootstrap/sources/docker_source.py b/src/virtBootstrap/sources/docker_source.py index 9220e13..0ca3b20 100644 --- a/src/virtBootstrap/sources/docker_source.py +++ b/src/virtBootstrap/sources/docker_source.py @@ -58,6 +58,8 @@ class DockerSource(object): self.url = self.gen_valid_uri(kwargs['uri']) self.username = kwargs['username'] self.password = kwargs['password'] + self.uid_map = kwargs['uid_map'] + self.gid_map = kwargs['gid_map'] self.output_format = kwargs['fmt'] self.insecure = kwargs['not_secure'] self.no_cache = kwargs['no_cache'] @@ -262,7 +264,8 @@ class DockerSource(object): elif self.output_format == 'qcow2': self.progress("Extracting container layers into qcow2 images", value=50, logger=logger) - utils.Build_QCOW2_Image(self.tar_files, dest, self.progress) + utils.Build_QCOW2_Image(self.tar_files, dest, self.progress, + self.uid_map, self.gid_map) else: raise Exception("Unknown format:" + self.output_format) diff --git a/src/virtBootstrap/sources/file_source.py b/src/virtBootstrap/sources/file_source.py index 87589f6..748181f 100644 --- a/src/virtBootstrap/sources/file_source.py +++ b/src/virtBootstrap/sources/file_source.py @@ -44,6 +44,8 @@ class FileSource(object): """ self.path = kwargs['uri'].path self.output_format = kwargs['fmt'] + self.uid_map = kwargs['uid_map'] + self.gid_map = kwargs['gid_map'] self.progress = kwargs['progress'].update_progress def unpack(self, dest): @@ -60,11 +62,11 @@ class FileSource(object): self.progress("Extracting files into destination directory", value=0, logger=logger) utils.safe_untar(self.path, dest) - elif self.output_format == 'qcow2': self.progress("Extracting files into qcow2 image", value=0, logger=logger) - utils.Build_QCOW2_Image([self.path], dest, self.progress) + utils.Build_QCOW2_Image([self.path], dest, self.progress, + self.uid_map, self.gid_map) else: raise Exception("Unknown format:" + self.output_format) diff --git a/src/virtBootstrap/utils.py b/src/virtBootstrap/utils.py index cf5becc..c69b5da 100644 --- a/src/virtBootstrap/utils.py +++ b/src/virtBootstrap/utils.py @@ -28,6 +28,7 @@ import hashlib import json import os import sys +import tarfile import tempfile import logging import re @@ -56,7 +57,7 @@ class Build_QCOW2_Image(object): """ Create qcow2 image with backing chains from list of tar files. """ - def __init__(self, tar_files, dest, progress): + def __init__(self, tar_files, dest, progress, uid_map=None, gid_map=None): """ Initialize guestfs """ @@ -65,6 +66,8 @@ class Build_QCOW2_Image(object): self.tar_files = tar_files self.nlayers = len(tar_files) self.progress = progress + self.uid_map = uid_map + self.gid_map = gid_map self.fmt = 'qcow2' self.qcow2_files = [os.path.join(dest, 'layer-%s.qcow2' % i) for i in range(self.nlayers)] @@ -100,6 +103,14 @@ class Build_QCOW2_Image(object): # from tar file. self.g.tar_in(tar_file, '/', get_compression_type(tar_file), xattrs=True, selinux=True, acls=True) + + # UID/GID Mapping + if self.uid_map or self.gid_map: + tar_members = tarfile.open(tar_file).getmembers() + balance_uid_gid_maps(self.uid_map, self.gid_map) + for uid, gid in zip(self.uid_map, self.gid_map): + self.map_id(tar_members, uid, gid) + # Shutdown guestfs instance to avoid hot-plugging of devices. self.g.umount('/') @@ -139,6 +150,28 @@ class Build_QCOW2_Image(object): self.tar_in(tar_file, devices[i]) + def map_id(self, tar_members, map_uid, map_gid): + """ + Remapping ownership of all files inside image. + + map_gid and map_uid: Contain integers in a list with format: + [<start>, <target>, <count>] + """ + if map_uid: + uid_opts = get_mapping_opts(map_uid) + if map_gid: + gid_opts = get_mapping_opts(map_gid) + + for member in tar_members: + old_uid = member.uid + old_gid = member.gid + + new_uid = get_map_id(old_uid, uid_opts) if map_uid else -1 + new_gid = get_map_id(old_gid, gid_opts) if map_gid else -1 + if new_uid != -1 or new_gid != -1: + self.g.lchown(new_uid, new_gid, os.path.join('/', member.name)) + + def get_compression_type(tar_file): """ Get compression type of tar file. diff --git a/src/virtBootstrap/virt_bootstrap.py b/src/virtBootstrap/virt_bootstrap.py index 4459ba0..54025d2 100755 --- a/src/virtBootstrap/virt_bootstrap.py +++ b/src/virtBootstrap/virt_bootstrap.py @@ -124,6 +124,8 @@ def bootstrap(uri, dest, fmt=fmt, username=username, password=password, + uid_map=uid_map, + gid_map=gid_map, not_secure=not_secure, no_cache=no_cache, progress=prog).unpack(dest) -- 2.13.3 _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list