volume => {
type => 'string',
default_key => 1,
+ format => 'pve-lxc-mp-string',
format_description => 'volume',
description => 'Volume, device or directory to mount into the container.',
},
description => 'Volume size (read only value).',
optional => 1,
},
+ acl => {
+ type => 'boolean',
+ format_description => 'acl',
+ description => 'Explicitly enable or disable ACL support.',
+ optional => 1,
+ },
+ ro => {
+ type => 'boolean',
+ format_description => 'ro',
+ description => 'Read-only mountpoint (not supported with bind mounts)',
+ optional => 1,
+ },
};
PVE::JSONSchema::register_standard_option('pve-ct-rootfs', {
'lxc.mount' => 1,
'lxc.mount.entry' => 1,
'lxc.mount.auto' => 1,
- 'lxc.rootfs' => 1,
+ 'lxc.rootfs' => 'lxc.rootfs is auto generated from rootfs',
'lxc.rootfs.mount' => 1,
- 'lxc.rootfs.options' => 1,
+ 'lxc.rootfs.options' => 'lxc.rootfs.options is not supported' .
+ ', please use mountpoint options in the "rootfs" key',
# lxc.cgroup.*
'lxc.cap.drop' => 1,
'lxc.cap.keep' => 1,
};
}
+PVE::JSONSchema::register_format('pve-lxc-mp-string', \&verify_lxc_mp_string);
+sub verify_lxc_mp_string{
+ my ($mp, $noerr) = @_;
+
+ # do not allow:
+ # /./ or /../
+ # /. or /.. at the end
+ # ../ at the beginning
+
+ if($mp =~ m@/\.\.?/@ ||
+ $mp =~ m@/\.\.?$@ ||
+ $mp =~ m@^\.\./@){
+ return undef if $noerr;
+ die "$mp contains illegal character sequences\n";
+ }
+ return $mp;
+}
+
my $mp_desc = {
%$rootfs_desc,
mp => {
type => 'string',
+ format => 'pve-lxc-mp-string',
format_description => 'Path',
description => 'Path to the mountpoint as seen from inside the container.',
},
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\./) {
+ my $validity = $valid_lxc_conf_keys->{$key} || 0;
+ if ($validity eq 1 || $key =~ m/^lxc\.cgroup\./) {
push @{$conf->{lxc}}, [$key, $value];
+ } elsif (my $errmsg = $validity) {
+ warn "vm $vmid - $key: $errmsg\n";
} else {
warn "vm $vmid - unable to parse config: $line\n";
}
return "$lockdir/pve-config-${vmid}.lock";
}
-sub lock_aquire {
- my ($vmid, $timeout) = @_;
+sub lock_container {
+ my ($vmid, $timeout, $code, @param) = @_;
$timeout = 10 if !$timeout;
- my $mode = LOCK_EX;
my $filename = lock_filename($vmid);
mkdir $lockdir if !-d $lockdir;
- my $lock_func = sub {
- if (!$lock_handles->{$$}->{$filename}) {
- my $fh = new IO::File(">>$filename") ||
- die "can't open file - $!\n";
- $lock_handles->{$$}->{$filename} = { fh => $fh, refcount => 0};
- }
-
- if (!flock($lock_handles->{$$}->{$filename}->{fh}, $mode |LOCK_NB)) {
- print STDERR "trying to aquire lock...";
- my $success;
- while(1) {
- $success = flock($lock_handles->{$$}->{$filename}->{fh}, $mode);
- # try again on EINTR (see bug #273)
- if ($success || ($! != EINTR)) {
- last;
- }
- }
- if (!$success) {
- print STDERR " failed\n";
- die "can't aquire lock - $!\n";
- }
-
- print STDERR " OK\n";
- }
-
- $lock_handles->{$$}->{$filename}->{refcount}++;
- };
+ my $res = PVE::Tools::lock_file_full($filename, $timeout, 0, $code, @param);
- eval { PVE::Tools::run_with_timeout($timeout, $lock_func); };
- my $err = $@;
- if ($err) {
- die "can't lock file '$filename' - $err";
- }
-}
-
-sub lock_release {
- my ($vmid) = @_;
-
- my $filename = lock_filename($vmid);
-
- if (my $fh = $lock_handles->{$$}->{$filename}->{fh}) {
- my $refcount = --$lock_handles->{$$}->{$filename}->{refcount};
- if ($refcount <= 0) {
- $lock_handles->{$$}->{$filename} = undef;
- close ($fh);
- }
- }
-}
-
-sub lock_container {
- my ($vmid, $timeout, $code, @param) = @_;
-
- my $res;
-
- lock_aquire($vmid, $timeout);
- eval { $res = &$code(@param) };
- my $err = $@;
- lock_release($vmid);
-
- die $err if $err;
+ die $@ if $@;
return $res;
}
my $wanted_swap = PVE::Tools::extract_param($param, 'swap');
if (defined($wanted_memory) || defined($wanted_swap)) {
- $wanted_memory //= ($conf->{memory} || 512);
- $wanted_swap //= ($conf->{swap} || 0);
+ my $old_memory = ($conf->{memory} || 512);
+ my $old_swap = ($conf->{swap} || 0);
+
+ $wanted_memory //= $old_memory;
+ $wanted_swap //= $old_swap;
my $total = $wanted_memory + $wanted_swap;
if ($running) {
- write_cgroup_value("memory", $vmid, "memory.limit_in_bytes", int($wanted_memory*1024*1024));
- write_cgroup_value("memory", $vmid, "memory.memsw.limit_in_bytes", int($total*1024*1024));
+ my $old_total = $old_memory + $old_swap;
+ if ($total > $old_total) {
+ write_cgroup_value("memory", $vmid, "memory.memsw.limit_in_bytes", int($total*1024*1024));
+ write_cgroup_value("memory", $vmid, "memory.limit_in_bytes", int($wanted_memory*1024*1024));
+ } else {
+ write_cgroup_value("memory", $vmid, "memory.limit_in_bytes", int($wanted_memory*1024*1024));
+ write_cgroup_value("memory", $vmid, "memory.memsw.limit_in_bytes", int($total*1024*1024));
+ }
}
$conf->{memory} = $wanted_memory;
$conf->{swap} = $wanted_swap;
if defined($conf->{snapshots}->{$snapname});
my $storecfg = PVE::Storage::config();
- die "snapshot feature is not available\n" if !has_feature('snapshot', $conf, $storecfg);
+ my $feature = $snapname eq 'vzdump' ? 'vzdump' : 'snapshot';
+ die "snapshot feature is not available\n" if !has_feature($feature, $conf, $storecfg);
$snap = $conf->{snapshots}->{$snapname} = {};
my ($feature, $conf, $storecfg, $snapname) = @_;
my $err;
+ my $vzdump = $feature eq 'vzdump';
+ $feature = 'snapshot' if $vzdump;
foreach_mountpoint($conf, sub {
my ($ms, $mountpoint) = @_;
return if $err; # skip further test
+ return if $vzdump && $ms ne 'rootfs' && !$mountpoint->{backup};
$err = 1 if !PVE::Storage::volume_has_feature($storecfg, $feature, $mountpoint->{volume}, $snapname);
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) = @_;
my $mountpoint = $key eq 'rootfs' ? parse_ct_rootfs($value, 1) : parse_ct_mountpoint($value, 1);
next if !defined($mountpoint);
- $mountpoint->{mp} = sanitize_mountpoint($mountpoint->{mp});
-
- my $path = $mountpoint->{volume};
- $mountpoint->{volume} = sanitize_mountpoint($path) if $path =~ m|^/|;
-
&$func($key, $mountpoint);
}
}
die "unknown snapshot path for '$volid'" if !$storage && defined($snapname);
+ my $optstring = '';
+ if (defined($mountpoint->{acl})) {
+ $optstring .= ($mountpoint->{acl} ? 'acl' : 'noacl');
+ }
+ if ($mountpoint->{ro}) {
+ $optstring .= ',' if $optstring;
+ $optstring .= 'ro';
+ }
+
+ my @extra_opts = ('-o', $optstring);
+
if ($storage) {
my $scfg = PVE::Storage::storage_config($storage_cfg, $storage);
if ($scfg->{type} eq 'zfspool') {
my $path_arg = $path;
$path_arg =~ s!^/+!!;
- PVE::Tools::run_command(['mount', '-o', 'ro', '-t', 'zfs', $path_arg, $mount_path]);
+ PVE::Tools::run_command(['mount', '-o', 'ro', @extra_opts, '-t', 'zfs', $path_arg, $mount_path]);
} else {
die "cannot mount subvol snapshots for storage type '$scfg->{type}'\n";
}
} else {
- PVE::Tools::run_command(['mount', '-o', 'bind', $path, $mount_path]);
+ if ($mountpoint->{ro}) {
+ die "read-only bind mounts not supported\n";
+ }
+ PVE::Tools::run_command(['mount', '-o', 'bind', @extra_opts, $path, $mount_path]);
}
}
return wantarray ? ($path, 0) : $path;
} elsif ($format eq 'raw' || $format eq 'iso') {
my $use_loopdev = 0;
- my @extra_opts;
if ($scfg->{path}) {
push @extra_opts, '-o', 'loop';
$use_loopdev = 1;
die "unsupported image format '$format'\n";
}
} elsif ($type eq 'device') {
- PVE::Tools::run_command(['mount', $volid, $mount_path]) if $mount_path;
+ PVE::Tools::run_command(['mount', @extra_opts, $volid, $mount_path]) if $mount_path;
return wantarray ? ($volid, 0) : $volid;
} elsif ($type eq 'bind') {
+ if ($mountpoint->{ro}) {
+ die "read-only bind mounts not supported\n";
+ # Theoretically we'd have to execute both:
+ # mount -o bind $a $b
+ # mount -o bind,remount,ro $a $b
+ }
die "directory '$volid' does not exist\n" if ! -d $volid;
&$check_mount_path($volid);
- PVE::Tools::run_command(['mount', '-o', 'bind', $volid, $mount_path]) if $mount_path;
+ PVE::Tools::run_command(['mount', '-o', 'bind', @extra_opts, $volid, $mount_path]) if $mount_path;
return wantarray ? ($volid, 0) : $volid;
}