use File::Path;
use File::Spec;
use Cwd qw();
-use Fcntl qw(O_RDONLY :flock);
+use Fcntl qw(O_RDONLY);
use PVE::Cluster qw(cfs_register_file cfs_read_file);
use PVE::Storage;
use PVE::SafeSyslog;
use PVE::INotify;
use PVE::JSONSchema qw(get_standard_option);
-use PVE::Tools qw($IPV6RE $IPV4RE dir_glob_foreach);
+use PVE::Tools qw($IPV6RE $IPV4RE dir_glob_foreach lock_file lock_file_full);
use PVE::Network;
use PVE::AccessControl;
use PVE::ProcFSTools;
my $lock_handles = {};
my $lockdir = "/run/lock/lxc";
-sub lock_filename {
+sub config_file_lock {
my ($vmid) = @_;
return "$lockdir/pve-config-${vmid}.lock";
}
-sub lock_container {
+sub lock_config_full {
my ($vmid, $timeout, $code, @param) = @_;
- $timeout = 10 if !$timeout;
+ my $filename = config_file_lock($vmid);
- my $filename = lock_filename($vmid);
+ mkdir $lockdir if !-d $lockdir;
+
+ my $res = lock_file($filename, $timeout, $code, @param);
+
+ die $@ if $@;
+
+ return $res;
+}
+
+sub lock_config_mode {
+ my ($vmid, $timeout, $shared, $code, @param) = @_;
+
+ my $filename = config_file_lock($vmid);
mkdir $lockdir if !-d $lockdir;
- my $res = PVE::Tools::lock_file_full($filename, $timeout, 0, $code, @param);
+ my $res = lock_file_full($filename, $timeout, $shared, $code, @param);
die $@ if $@;
return $res;
}
+sub lock_config {
+ my ($vmid, $code, @param) = @_;
+
+ return lock_config_full($vmid, 10, $code, @param);
+}
+
sub option_exists {
my ($name) = @_;
return $prop;
}
-sub json_config_properties_no_rootfs {
- my $prop = shift;
-
- foreach my $opt (keys %$confdesc) {
- next if $prop->{$opt};
- next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'rootfs';
- $prop->{$opt} = $confdesc->{$opt};
- }
-
- return $prop;
-}
-
# container status helpers
sub list_active_containers {
write_config($vmid, $conf);
};
- lock_container($vmid, 10, $updatefn);
+ lock_config($vmid, $updatefn);
return $snap;
};
write_config($vmid, $conf);
};
- lock_container($vmid, 10 ,$updatefn);
+ lock_config($vmid ,$updatefn);
};
sub has_feature {
write_config($vmid, $conf);
};
- lock_container($vmid, 10, $updatefn);
+ lock_config($vmid, $updatefn);
my $storecfg = PVE::Storage::config();
my $err = $@;
if(!$err || ($err && $force)) {
- lock_container($vmid, 10, $del_snap);
+ lock_config($vmid, $del_snap);
if ($err) {
die "Can't delete snapshot: $vmid $snapname $err\n";
}
write_config($vmid, $conf);
};
- lock_container($vmid, 10, $updatefn);
+ lock_config($vmid, $updatefn);
PVE::Storage::volume_snapshot_rollback($storecfg, $volid, $snapname);
- lock_container($vmid, 5, $unlockfn);
+ lock_config($vmid, $unlockfn);
}
sub template_create {
return $device;
}
+sub bindmount {
+ my ($dir, $dest, $ro, @extra_opts) = @_;
+ PVE::Tools::run_command(['mount', '-o', 'bind', @extra_opts, $dir, $dest]);
+ if ($ro) {
+ eval { PVE::Tools::run_command(['mount', '-o', 'bind,remount,ro', $dest]); };
+ if (my $err = $@) {
+ warn "bindmount error\n";
+ # don't leave writable bind-mounts behind...
+ PVE::Tools::run_command(['umount', $dest]);
+ die $err;
+ }
+ }
+}
+
# use $rootdir = undef to just return the corresponding mount path
sub mountpoint_mount {
my ($mountpoint, $rootdir, $storage_cfg, $snapname) = @_;
if (defined($mountpoint->{acl})) {
$optstring .= ($mountpoint->{acl} ? 'acl' : 'noacl');
}
- if ($mountpoint->{ro}) {
- $optstring .= ',' if $optstring;
- $optstring .= 'ro';
- }
+ my $readonly = $mountpoint->{ro};
my @extra_opts = ('-o', $optstring);
die "cannot mount subvol snapshots for storage type '$scfg->{type}'\n";
}
} else {
- if ($mountpoint->{ro}) {
- die "read-only bind mounts not supported\n";
- }
- PVE::Tools::run_command(['mount', '-o', 'bind', @extra_opts, $path, $mount_path]);
+ bindmount($path, $mount_path, $readonly, @extra_opts);
warn "cannot enable quota control for bind mounted subvolumes\n" if $quota;
}
}
if ($quota) {
push @extra_opts, '-o', 'usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv0';
}
+ push @extra_opts, '-o', 'ro' if $readonly;
PVE::Tools::run_command(['mount', @extra_opts, $path, $mount_path]);
}
}
die "unsupported image format '$format'\n";
}
} elsif ($type eq 'device') {
+ push @extra_opts, '-o', 'ro' if $readonly;
PVE::Tools::run_command(['mount', @extra_opts, $volid, $mount_path]) if $mount_path;
return wantarray ? ($volid, 0, $volid) : $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', @extra_opts, $volid, $mount_path]) if $mount_path;
+ bindmount($volid, $mount_path, $readonly, @extra_opts) if $mount_path;
warn "cannot enable quota control for bind mounts\n" if $quota;
return wantarray ? ($volid, 0, undef) : $volid;
}