On Tue, 2017-08-01 at 12:28 +0100, Radostin Stoyanov wrote: > 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) ACK -- Cedric _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list