]> git.proxmox.com Git - pve-container.git/blobdiff - src/PVE/LXC.pm
lxc: read-only bind mounts
[pve-container.git] / src / PVE / LXC.pm
index 6545df57543a4e8f8a3978a4eefbd09aaff3ea98..a737fc0d3ab4195f4aa2d05053e9eddd0135de43 100644 (file)
@@ -9,14 +9,14 @@ use Socket;
 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;
@@ -646,28 +646,46 @@ sub write_config {
 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) = @_;
 
@@ -687,18 +705,6 @@ sub json_config_properties {
     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 {
@@ -1744,7 +1750,7 @@ my $snapshot_prepare = sub {
        write_config($vmid, $conf);
     };
 
-    lock_container($vmid, 10, $updatefn);
+    lock_config($vmid, $updatefn);
 
     return $snap;
 };
@@ -1773,7 +1779,7 @@ my $snapshot_commit = sub {
        write_config($vmid, $conf);
     };
 
-    lock_container($vmid, 10 ,$updatefn);
+    lock_config($vmid ,$updatefn);
 };
 
 sub has_feature {
@@ -1939,7 +1945,7 @@ sub snapshot_delete {
        write_config($vmid, $conf);
     };
 
-    lock_container($vmid, 10, $updatefn);
+    lock_config($vmid, $updatefn);
 
     my $storecfg = PVE::Storage::config();
 
@@ -1988,7 +1994,7 @@ sub snapshot_delete {
     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";
        }
@@ -2047,11 +2053,11 @@ sub snapshot_rollback {
        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 {
@@ -2250,6 +2256,20 @@ sub run_with_loopdev {
     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) = @_;
@@ -2280,10 +2300,7 @@ sub mountpoint_mount {
     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);
 
@@ -2308,10 +2325,7 @@ sub mountpoint_mount {
                        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;
                }
            }
@@ -2328,6 +2342,7 @@ sub mountpoint_mount {
                        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]);
                    }
                }
@@ -2348,18 +2363,13 @@ sub mountpoint_mount {
            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;
     }