]> git.proxmox.com Git - pve-container.git/blobdiff - src/PVE/LXC.pm
fix #5160: fix move_mount regression for mount point hotplug
[pve-container.git] / src / PVE / LXC.pm
index c2397158e6edd7e0776e111da680c1e0bca006e6..7db48335200d9a9e319ddaddf8d09f6376fdac89 100644 (file)
@@ -4,8 +4,8 @@ use strict;
 use warnings;
 
 use Cwd qw();
-use Errno qw(ELOOP ENOTDIR EROFS ECONNREFUSED EEXIST);
-use Fcntl qw(O_RDONLY O_WRONLY O_NOFOLLOW O_DIRECTORY);
+use Errno qw(ELOOP ENOENT ENOTDIR EROFS ECONNREFUSED EEXIST);
+use Fcntl qw(O_RDONLY O_WRONLY O_NOFOLLOW O_DIRECTORY :mode);
 use File::Path;
 use File::Spec;
 use IO::Poll qw(POLLIN POLLHUP);
@@ -640,6 +640,29 @@ sub update_lxc_config {
        $raw .= "lxc.mount.auto = sys:mixed\n";
     }
 
+    PVE::LXC::Config->foreach_passthrough_device($conf, sub {
+       my ($key, $device) = @_;
+
+       die "Path is not defined for passthrough device $key"
+           unless (defined($device->{path}));
+
+       my $absolute_path = $device->{path};
+       my ($mode, $rdev) = (stat($absolute_path))[2, 6];
+
+       die "Device $absolute_path does not exist\n" if $! == ENOENT;
+
+       die "Error accessing device $absolute_path\n"
+           if (!defined($mode) || !defined($rdev));
+
+       die "$absolute_path is not a device\n"
+           if (!S_ISBLK($mode) && !S_ISCHR($mode));
+
+       my $major = PVE::Tools::dev_t_major($rdev);
+       my $minor = PVE::Tools::dev_t_minor($rdev);
+       my $device_type_char = S_ISBLK($mode) ? 'b' : 'c';
+       $raw .= "lxc.cgroup2.devices.allow = $device_type_char $major:$minor rw\n";
+    });
+
     # WARNING: DO NOT REMOVE this without making sure that loop device nodes
     # cannot be exposed to the container with r/w access (cgroup perms).
     # When this is enabled mounts will still remain in the monitor's namespace
@@ -970,7 +993,7 @@ sub update_net {
                warn $@ if $@;
 
                PVE::Network::SDN::Vnets::add_next_free_cidr($newnet->{bridge}, $conf->{hostname}, $newnet->{hwaddr}, $vmid, undef, 1);
-               PVE::Network::SDN::Vnets::add_dhcp_mapping($newnet->{bridge}, $newnet->{hwaddr});
+               PVE::Network::SDN::Vnets::add_dhcp_mapping($newnet->{bridge}, $newnet->{hwaddr}, $vmid, $conf->{hostname});
            }
 
            delete $conf->{$opt};
@@ -1021,8 +1044,10 @@ sub update_net {
            PVE::LXC::Config->write_config($vmid, $conf);
        }
     } else {
-       PVE::Network::SDN::Vnets::add_next_free_cidr($newnet->{bridge}, $conf->{hostname}, $newnet->{hwaddr}, $vmid, undef, 1);
-       PVE::Network::SDN::Vnets::add_dhcp_mapping($newnet->{bridge}, $newnet->{hwaddr});
+       if ($have_sdn) {
+           PVE::Network::SDN::Vnets::add_next_free_cidr($newnet->{bridge}, $conf->{hostname}, $newnet->{hwaddr}, $vmid, undef, 1);
+           PVE::Network::SDN::Vnets::add_dhcp_mapping($newnet->{bridge}, $newnet->{hwaddr}, $vmid, $conf->{hostname});
+       }
 
        hotplug_net($vmid, $conf, $opt, $newnet, $netid);
     }
@@ -1370,6 +1395,8 @@ sub check_ct_modify_config_perm {
            $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
            check_bridge_access($rpcenv, $authuser, $oldconf->{$opt}) if $oldconf->{$opt};
            check_bridge_access($rpcenv, $authuser, $newconf->{$opt}) if $newconf->{$opt};
+       } elsif ($opt =~ m/^dev\d+$/) {
+           raise_perm_exc("configuring device passthrough is only allowed for root\@pam");
        } elsif ($opt eq 'nameserver' || $opt eq 'searchdomain' || $opt eq 'hostname') {
            $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
        } elsif ($opt eq 'features') {
@@ -1947,7 +1974,7 @@ sub mountpoint_hotplug :prototype($$$$$) {
        my $dir = get_staging_mount_path($opt);
 
        # Now switch our apparmor profile before mounting:
-       my $data = 'changeprofile /usr/bin/lxc-start';
+       my $data = 'changeprofile pve-container-mounthotplug';
        if (syswrite($aa_fd, $data, length($data)) != length($data)) {
            die "failed to change apparmor profile: $!\n";
        }
@@ -2419,6 +2446,34 @@ sub validate_id_maps {
     }
 }
 
+sub map_ct_id_to_host {
+    my ($id, $id_map, $id_type) = @_;
+
+    for my $mapping (@$id_map) {
+       my ($type, $ct, $host, $length) = @$mapping;
+
+       next if ($type ne $id_type);
+
+       if ($id >= $ct && $id < ($ct + $length)) {
+           return $host - $ct + $id;
+       }
+    }
+
+    return $id;
+}
+
+sub map_ct_uid_to_host {
+    my ($uid, $id_map) = @_;
+
+    return map_ct_id_to_host($uid, $id_map, 'u');
+}
+
+sub map_ct_gid_to_host {
+    my ($gid, $id_map) = @_;
+
+    return map_ct_id_to_host($gid, $id_map, 'g');
+}
+
 sub userns_command {
     my ($id_map) = @_;
     if (@$id_map) {
@@ -2771,9 +2826,9 @@ sub create_ifaces_ipams_ips {
 
     for my $opt (keys %$conf) {
        next if $opt !~ m/^net(\d+)$/;
-       my $net = PVE::QemuServer::parse_net($conf->{$opt});
+       my $net = PVE::LXC::Config->parse_lxc_network($conf->{$opt});
        next if $net->{type} ne 'veth';
-        PVE::Network::SDN::Vnets::add_next_free_cidr($net->{bridge}, $conf->{hostname}, $net->{hwaddr}, $vmid, undef, 1);
+       PVE::Network::SDN::Vnets::add_next_free_cidr($net->{bridge}, $conf->{hostname}, $net->{hwaddr}, $vmid, undef, 1);
     }
 }
 
@@ -2784,7 +2839,7 @@ sub delete_ifaces_ipams_ips {
 
     for my $opt (keys %$conf) {
        next if $opt !~ m/^net(\d+)$/;
-       my $net = PVE::QemuServer::parse_net($conf->{$opt});
+       my $net = PVE::LXC::Config->parse_lxc_network($conf->{$opt});
        eval { PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, $net->{hwaddr}, $conf->{hostname}) };
        warn $@ if $@;
     }