]> git.proxmox.com Git - pve-container.git/blobdiff - src/PVE/LXC.pm
specify data format for hostname, searchdomain and nameserver
[pve-container.git] / src / PVE / LXC.pm
index fc1500c7c6b7c4ac1d9abcec0bf869d230e84922..3e1e5aa8c543abcda3331b418bc748e57c9cf7c3 100644 (file)
@@ -5,6 +5,8 @@ use warnings;
 use POSIX qw(EINTR);
 
 use File::Path;
+use File::Spec;
+use Cwd qw();
 use Fcntl ':flock';
 
 use PVE::Cluster qw(cfs_register_file cfs_read_file);
@@ -138,7 +140,7 @@ my $confdesc = {
     hostname => {
        optional => 1,
        description => "Set a host name for the container.",
-       type => 'string',
+       type => 'string', format => 'dns-name',
        maxLength => 255,
     },
     description => {
@@ -148,12 +150,12 @@ my $confdesc = {
     },
     searchdomain => {
        optional => 1,
-       type => 'string',
+       type => 'string', format => 'dns-name-list',
        description => "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver.",
     },
     nameserver => {
        optional => 1,
-       type => 'string',
+       type => 'string', format => 'address-list',
        description => "Sets DNS server IP address for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver.",
     },
     rootfs => get_standard_option('pve-ct-rootfs'),
@@ -290,7 +292,9 @@ sub write_pct_config {
        foreach my $key (sort keys %$conf) {
            next if $key eq 'digest' || $key eq 'description' || $key eq 'pending' || 
                $key eq 'snapshots' || $key eq 'snapname' || $key eq 'lxc';
-           $raw .= "$key: $conf->{$key}\n";
+           my $value = $conf->{$key};
+           die "detected invalid newline inside property '$key'\n" if $value =~ m/\n/;
+           $raw .= "$key: $value\n";
        }
 
        if (my $lxcconf = $conf->{lxc}) {
@@ -385,7 +389,7 @@ sub parse_pct_config {
            next;
        }
 
-       if ($line =~ m/^(lxc\.[a-z0-9_\.]+)(:|\s*=)\s*(.*?)\s*$/) {
+       if ($line =~ m/^(lxc\.[a-z0-9_\-\.]+)(:|\s*=)\s*(.*?)\s*$/) {
            my $key = $1;
            my $value = $3;
            if ($valid_lxc_conf_keys->{$key} || $key =~ m/^lxc\.cgroup\./) {
@@ -1818,6 +1822,19 @@ sub mountpoint_names {
     return $reverse ? reverse @names : @names;
 }
 
+# The container might have *different* symlinks than the host. realpath/abs_path
+# use the actual filesystem to resolve links.
+sub sanitize_mountpoint {
+    my ($mp) = @_;
+    $mp = '/' . $mp; # we always start with a slash
+    $mp =~ s@/{2,}@/@g; # collapse sequences of slashes
+    $mp =~ s@/\./@@g; # collapse /./
+    $mp =~ s@/\.(/)?$@$1@; # collapse a trailing /. or /./
+    $mp =~ s@(.*)/[^/]+/\.\./@$1/@g; # collapse /../ without regard for symlinks
+    $mp =~ s@/\.\.(/)?$@$1@; # collapse trailing /.. or /../ disregarding symlinks
+    return $mp;
+}
+
 sub foreach_mountpoint_full {
     my ($conf, $reverse, $func) = @_;
 
@@ -1825,7 +1842,14 @@ sub foreach_mountpoint_full {
        my $value = $conf->{$key};
        next if !defined($value);
        my $mountpoint = parse_ct_mountpoint($value);
-       $mountpoint->{mp} = '/' if $key eq 'rootfs'; # just to be sure
+
+       # just to be sure: rootfs is /
+       my $path = $key eq 'rootfs' ? '/' : $mountpoint->{mp};
+       $mountpoint->{mp} = sanitize_mountpoint($path);
+
+       $path = $mountpoint->{volume};
+       $mountpoint->{volume} = sanitize_mountpoint($path) if $path =~ m|^/|;
+
        &$func($key, $mountpoint);
     }
 }
@@ -1851,7 +1875,7 @@ sub check_ct_modify_config_perm {
 
        if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') {
            $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
-       } elsif ($opt eq 'disk') {
+       } elsif ($opt eq 'rootfs' || $opt =~ /^mp\d+$/) {
            $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
        } elsif ($opt eq 'memory' || $opt eq 'swap') {
            $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
@@ -1940,6 +1964,15 @@ sub mountpoint_mount_path {
     return mountpoint_mount($mountpoint, undef, $storage_cfg, $snapname);
 }
 
+my $check_mount_path = sub {
+    my ($path) = @_;
+    $path = File::Spec->canonpath($path);
+    my $real = Cwd::realpath($path);
+    if ($real ne $path) {
+       die "mount path modified by symlink: $path != $real";
+    }
+};
+
 # use $rootdir = undef to just return the corresponding mount path
 sub mountpoint_mount {
     my ($mountpoint, $rootdir, $storage_cfg, $snapname) = @_;
@@ -1955,6 +1988,7 @@ sub mountpoint_mount {
        $rootdir =~ s!/+$!!;
        $mount_path = "$rootdir/$mount";
        $mount_path =~ s!/+!/!g;
+       &$check_mount_path($mount_path);
        File::Path::mkpath($mount_path);
     }
     
@@ -2011,6 +2045,7 @@ sub mountpoint_mount {
        PVE::Tools::run_command(['mount', $volid, $mount_path]) if $mount_path;
        return wantarray ? ($volid, 0) : $volid;
     } elsif ($volid !~ m|^/dev/.+| && $volid =~ m|^/.+| && -d $volid) {
+       &$check_mount_path($volid);
        PVE::Tools::run_command(['mount', '-o', 'bind', $volid, $mount_path]) if $mount_path;
        return wantarray ? ($volid, 0) : $volid;
     }