return &$complete_ctid_full(1);
}
+sub parse_id_maps {
+ my ($conf) = @_;
+
+ my $id_map = [];
+ my $rootuid = 0;
+ my $rootgid = 0;
+
+ my $lxc = $conf->{lxc};
+ foreach my $entry (@$lxc) {
+ my ($key, $value) = @$entry;
+ next if $key ne 'lxc.id_map';
+ if ($value =~ /^([ug])\s+(\d+)\s+(\d+)\s+(\d+)\s*$/) {
+ my ($type, $ct, $host, $length) = ($1, $2, $3, $4);
+ push @$id_map, [$type, $ct, $host, $length];
+ if ($ct == 0) {
+ $rootuid = $host if $type eq 'u';
+ $rootgid = $host if $type eq 'g';
+ }
+ } else {
+ die "failed to parse id_map: $value\n";
+ }
+ }
+
+ if (!@$id_map && $conf->{unprivileged}) {
+ # Should we read them from /etc/subuid?
+ $id_map = [ ['u', '0', '100000', '65536'],
+ ['g', '0', '100000', '65536'] ];
+ $rootuid = $rootgid = 100000;
+ }
+
+ return ($id_map, $rootuid, $rootgid);
+}
+
1;
my $userns_cmd = [];
-# we always use the same mapping: 'b:0:100000:65536'
-# if ($conf->{'lxc.id_map'}) {
-# $userns_cmd = ['lxc-usernsexec', '-m', 'b:0:100000:65536', '--'];
-# PVE::Tools::run_command(['chown', '-R', '100000:100000', $rootdir]);
-# }
+ my ($id_map, $rootuid, $rootgid) = PVE::LXC::parse_id_maps($conf);
+ if (@$id_map) {
+ $userns_cmd = ['lxc-usernsexec', (map { ('-m', join(':', @$_)) } @$id_map), '--'];
+ PVE::Tools::run_command(['chown', '-R', "$rootuid:$rootgid", $rootdir]);
+ }
my $cmd = [@$userns_cmd, 'tar', 'xpf', $archive, '--totals',
@$PVE::LXC::COMMON_TAR_FLAGS,
# Cache some host files we need access to:
$plugin->{host_resolv_conf} = PVE::INotify::read_file('resolvconf');
+
+ # pass on user namespace information:
+ my ($id_map, $rootuid, $rootgid) = PVE::LXC::parse_id_maps($conf);
+ if (@$id_map) {
+ $plugin->{id_map} = $id_map;
+ $plugin->{rootuid} = $rootuid;
+ $plugin->{rootgid} = $rootgid;
+ }
return $self;
}
# File access wrappers for container setup code.
# For user-namespace support these might need to take uid and gid maps into account.
+sub ct_reset_ownership {
+ my ($self, @files) = @_;
+ my $conf = $self->{conf};
+ return if !$self->{id_map};
+ my $uid = $self->{rootuid};
+ my $gid = $self->{rootgid};
+ chown($uid, $gid, @files);
+}
+
sub ct_mkdir {
my ($self, $file, $mask) = @_;
# mkdir goes by parameter count - an `undef' mode acts like a mode of 0000
- return CORE::mkdir($file, $mask) if defined ($mask);
- return CORE::mkdir($file);
+ if (defined($mask)) {
+ return CORE::mkdir($file, $mask) && $self->ct_reset_ownership($file);
+ } else {
+ return CORE::mkdir($file) && $self->ct_reset_ownership($file);
+ }
}
sub ct_unlink {
sub ct_open_file_write {
my $self = shift;
my $file = shift;
- return IO::File->new($file, O_WRONLY | O_CREAT, @_);
+ my $fh = IO::File->new($file, O_WRONLY | O_CREAT, @_);
+ $self->ct_reset_ownership($fh);
+ return $fh;
}
sub ct_make_path {
my $self = shift;
- File::Path::make_path(@_);
+ if ($self->{id_map}) {
+ my $opts = pop;
+ if (ref($opts) eq 'HASH') {
+ $opts->{owner} = $self->{rootuid} if !defined($self->{owner});
+ $opts->{group} = $self->{rootgid} if !defined($self->{group});
+ }
+ File::Path::make_path(@_, $opts);
+ } else {
+ File::Path::make_path(@_);
+ }
}
sub ct_symlink {
sub ct_file_set_contents {
my ($self, $file, $data, $perms) = @_;
- return PVE::Tools::file_set_contents($file, $data, $perms);
+ PVE::Tools::file_set_contents($file, $data, $perms);
+ $self->ct_reset_ownership($file);
}
1;