]> git.proxmox.com Git - pve-container.git/blobdiff - src/PVE/LXC/Tools.pm
fix invalid device passthrough being added to config
[pve-container.git] / src / PVE / LXC / Tools.pm
index 62cdbc14b8159b722225a3508399dfcfa82de1ce..7e3e530f033b96ae9eddcf921259d24bbfb6c126 100644 (file)
@@ -4,6 +4,8 @@ package PVE::LXC::Tools;
 use strict;
 use warnings;
 
+use POSIX qw(ENOENT S_ISBLK S_ISCHR);
+
 use PVE::SafeSyslog;
 
 # LXC introduced an `lxc.hook.version` property which allows hooks to be executed in different
@@ -81,10 +83,9 @@ sub lxc_hook($$&) {
     $code->($ct_name, $common_vars, $namespaces, $args);
 }
 
-sub for_current_devices($&) {
-    my ($vmid, $code) = @_;
+sub for_devices {
+    my ($devlist_file, $vmid, $code) = @_;
 
-    my $devlist_file = "/var/lib/lxc/$vmid/devices";
     my $fd;
 
     if (! open $fd, '<', $devlist_file) {
@@ -93,8 +94,8 @@ sub for_current_devices($&) {
     }
 
     while (defined(my $line = <$fd>)) {
-       if ($line !~ m@^(b):(\d+):(\d+):/dev/(\S+)\s*$@) {
-           warn "invalid .pve-devices entry: $line\n";
+       if ($line !~ m@^(b|c):(\d+):(\d+):/dev/(\S+)\s*$@) {
+           warn "invalid $devlist_file entry: $line\n";
            next;
        }
 
@@ -117,6 +118,27 @@ sub for_current_devices($&) {
     close $fd;
 }
 
+sub for_current_passthrough_mounts($&) {
+    my ($vmid, $code) = @_;
+
+    my $devlist_file = "/var/lib/lxc/$vmid/passthrough/mounts";
+
+    if (-e $devlist_file) {
+       for_devices($devlist_file, $vmid, $code);
+    } else {
+       # Fallback to the old device list file in case a package upgrade
+       # occurs between lxc-pve-prestart-hook and now.
+       for_devices("/var/lib/lxc/$vmid/devices", $vmid, $code);
+    }
+}
+
+sub for_current_passthrough_devices($&) {
+    my ($vmid, $code) = @_;
+
+    my $passthrough_devlist_file = "/var/lib/lxc/$vmid/passthrough/devices";
+    for_devices($passthrough_devlist_file, $vmid, $code);
+}
+
 sub cgroup_do_write($$) {
     my ($path, $value) = @_;
     my $fd;
@@ -132,4 +154,67 @@ sub cgroup_do_write($$) {
     return 1;
 }
 
+sub get_device_mode_and_rdev($) {
+    my ($path) = @_;
+
+    die "Path is not defined\n" if !defined($path);
+
+    my ($mode, $rdev) = (stat($path))[2, 6];
+    die "Device $path does not exist\n" if $! == ENOENT;
+    die "Error accessing device $path\n" if !defined($mode) || !defined($rdev);
+    die "$path is not a device\n" if !S_ISBLK($mode) && !S_ISCHR($mode);
+
+    return ($mode, $rdev);
+}
+
+# Tries to the architecture of an executable file based on its ELF header.
+sub detect_elf_architecture {
+    my ($elf_fn) = @_;
+
+    # see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+
+    my $supported_elf_machine = {
+       0x03 => 'i386',
+       0x3e => 'amd64',
+       0x28 => 'armhf',
+       0xb7 => 'arm64',
+       0xf3 => 'riscv',
+    };
+
+    my $detect_arch = sub {
+       open(my $fh, "<", $elf_fn) or die "open '$elf_fn' failed: $!\n";
+       binmode($fh);
+
+       my $length = read($fh, my $data, 20) or die "read failed: $!\n";
+
+       # 4 bytes ELF magic number and 1 byte ELF class, padding, machine
+       my ($magic, $class, undef, $machine) = unpack("A4CA12n", $data);
+
+       die "'$elf_fn' does not resolve to an ELF!\n"
+           if (!defined($class) || !defined($magic) || $magic ne "\177ELF");
+
+       my $arch = $supported_elf_machine->{$machine};
+       die "'$elf_fn' has unknown ELF machine '$machine'!\n"
+           if !defined($arch);
+
+       if ($arch eq 'riscv') {
+           if ($class eq 1) {
+               $arch = 'riscv32';
+           } elsif ($class eq 2) {
+               $arch = 'riscv64';
+           } else {
+               die "'$elf_fn' has invalid class '$class'!\n";
+           }
+       }
+
+       return $arch;
+    };
+
+    my $arch = eval { PVE::Tools::run_fork_with_timeout(10, $detect_arch); };
+    my $err = $@ // "timeout\n";
+    die $err if !defined($arch);
+
+    return $arch;
+}
+
 1;