]> git.proxmox.com Git - pve-storage.git/blobdiff - PVE/Storage/CIFSPlugin.pm
feature: enable 'content-dirs' property for cephfs
[pve-storage.git] / PVE / Storage / CIFSPlugin.pm
index 6115a967c069ce2de734c99f3c3817db9bad8571..e03226df756bc042d1a618a77cf763c58ccd1ace 100644 (file)
@@ -13,11 +13,14 @@ use base qw(PVE::Storage::Plugin);
 
 # CIFS helper functions
 
-sub cifs_is_mounted {
-    my ($server, $share, $mountpoint, $mountdata) = @_;
+sub cifs_is_mounted : prototype($$) {
+    my ($scfg, $mountdata) = @_;
+
+    my ($mountpoint, $server, $share) = $scfg->@{'path', 'server', 'share'};
+    my $subdir = $scfg->{subdir} // '';
 
     $server = "[$server]" if Net::IP::ip_is_ipv6($server);
-    my $source = "//${server}/$share";
+    my $source = "//${server}/$share$subdir";
     $mountdata = PVE::ProcFSTools::parse_proc_mounts() if !$mountdata;
 
     return $mountpoint if grep {
@@ -30,14 +33,22 @@ sub cifs_is_mounted {
 
 sub cifs_cred_file_name {
     my ($storeid) = @_;
+    return "/etc/pve/priv/storage/${storeid}.pw";
+}
 
-    return "/etc/pve/priv/${storeid}.cred";
+sub cifs_delete_credentials {
+    my ($storeid) = @_;
+
+    if (my $cred_file = get_cred_file($storeid)) {
+       unlink($cred_file) or warn "removing cifs credientials '$cred_file' failed: $!\n";
+    }
 }
 
 sub cifs_set_credentials {
     my ($password, $storeid) = @_;
 
     my $cred_file = cifs_cred_file_name($storeid);
+    mkdir "/etc/pve/priv/storage";
 
     PVE::Tools::file_set_contents($cred_file, "password=$password\n");
 
@@ -49,14 +60,20 @@ sub get_cred_file {
 
     my $cred_file = cifs_cred_file_name($storeid);
 
-    return -e $cred_file ? $cred_file : undef;
+    if (-e $cred_file) {
+       return $cred_file;
+    }
+    return undef;
 }
 
-sub cifs_mount {
-    my ($server, $share, $mountpoint, $storeid, $smbver, $user, $domain) = @_;
+sub cifs_mount : prototype($$$$$) {
+    my ($scfg, $storeid, $smbver, $user, $domain) = @_;
+
+    my ($mountpoint, $server, $share) = $scfg->@{'path', 'server', 'share'};
+    my $subdir = $scfg->{subdir} // '';
 
     $server = "[$server]" if Net::IP::ip_is_ipv6($server);
-    my $source = "//${server}/$share";
+    my $source = "//${server}/$share$subdir";
 
     my $cmd = ['/bin/mount', '-t', 'cifs', $source, $mountpoint, '-o', 'soft', '-o'];
 
@@ -67,7 +84,7 @@ sub cifs_mount {
        push @$cmd, 'guest,username=guest';
     }
 
-    push @$cmd, '-o', defined($smbver) ? "vers=$smbver" : "vers=3.0";
+    push @$cmd, '-o', defined($smbver) ? "vers=$smbver" : "vers=default";
 
     run_command($cmd, errmsg => "mount error");
 }
@@ -93,7 +110,7 @@ sub properties {
            type => 'string',
        },
        password => {
-           description => "Password for CIFS share.",
+           description => "Password for accessing the share/datastore.",
            type => 'string',
            maxLength => 256,
        },
@@ -104,9 +121,11 @@ sub properties {
            maxLength => 256,
        },
        smbversion => {
-           description => "SMB protocol version",
+           description => "SMB protocol version. 'default' if not set, negotiates the highest SMB2+"
+               ." version supported by both the client and server.",
            type => 'string',
-           enum => ['2.0', '2.1', '3.0'],
+           default => 'default',
+           enum => ['default', '2.0', '2.1', '3', '3.0', '3.11'],
            optional => 1,
        },
     };
@@ -115,11 +134,15 @@ sub properties {
 sub options {
     return {
        path => { fixed => 1 },
+       'content-dirs' => { optional => 1 },
        server => { fixed => 1 },
        share => { fixed => 1 },
+       subdir => { optional => 1 },
        nodes => { optional => 1 },
        disable => { optional => 1 },
        maxfiles => { optional => 1 },
+       'prune-backups' => { optional => 1 },
+       'max-protected-backups' => { optional => 1 },
        content => { optional => 1 },
        format => { optional => 1 },
        username => { optional => 1 },
@@ -128,6 +151,7 @@ sub options {
        smbversion => { optional => 1},
        mkdir => { optional => 1 },
        bwlimit => { optional => 1 },
+       preallocation => { optional => 1 },
     };
 }
 
@@ -143,20 +167,43 @@ sub check_config {
 # Storage implementation
 
 sub on_add_hook {
-    my ($class, $storeid, $scfg, %param) = @_;
+    my ($class, $storeid, $scfg, %sensitive) = @_;
 
-    if (my $password = $param{password}) {
-       cifs_set_credentials($password, $storeid);
+    if (defined($sensitive{password})) {
+       cifs_set_credentials($sensitive{password}, $storeid);
+       if (!exists($scfg->{username})) {
+           warn "storage $storeid: ignoring password parameter, no user set\n";
+       }
+    } else {
+       cifs_delete_credentials($storeid);
     }
+
+    return;
+}
+
+sub on_update_hook {
+    my ($class, $storeid, $scfg, %sensitive) = @_;
+
+    return if !exists($sensitive{password});
+
+    if (defined($sensitive{password})) {
+       cifs_set_credentials($sensitive{password}, $storeid);
+       if (!exists($scfg->{username})) {
+           warn "storage $storeid: ignoring password parameter, no user set\n";
+       }
+    } else {
+       cifs_delete_credentials($storeid);
+    }
+
+    return;
 }
 
 sub on_delete_hook {
     my ($class, $storeid, $scfg) = @_;
 
-    my $cred_file = cifs_cred_file_name($storeid);
-    if (-f $cred_file) {
-       unlink($cred_file) or warn "removing cifs credientials '$cred_file' failed: $!\n";
-    }
+    cifs_delete_credentials($storeid);
+
+    return;
 }
 
 sub status {
@@ -165,12 +212,8 @@ sub status {
     $cache->{mountdata} = PVE::ProcFSTools::parse_proc_mounts()
        if !$cache->{mountdata};
 
-    my $path = $scfg->{path};
-    my $server = $scfg->{server};
-    my $share = $scfg->{share};
-
     return undef
-       if !cifs_is_mounted($server, $share, $path, $cache->{mountdata});
+       if !cifs_is_mounted($scfg, $cache->{mountdata});
 
     return $class->SUPER::status($storeid, $scfg, $cache);
 }
@@ -182,17 +225,15 @@ sub activate_storage {
        if !$cache->{mountdata};
 
     my $path = $scfg->{path};
-    my $server = $scfg->{server};
-    my $share = $scfg->{share};
 
-    if (!cifs_is_mounted($server, $share, $path, $cache->{mountdata})) {
+    if (!cifs_is_mounted($scfg, $cache->{mountdata})) {
 
        mkpath $path if !(defined($scfg->{mkdir}) && !$scfg->{mkdir});
 
        die "unable to activate storage '$storeid' - " .
            "directory '$path' does not exist\n" if ! -d $path;
 
-       cifs_mount($server, $share, $path, $storeid, $scfg->{smbversion},
+       cifs_mount($scfg, $storeid, $scfg->{smbversion},
            $scfg->{username}, $scfg->{domain});
     }
 
@@ -206,10 +247,8 @@ sub deactivate_storage {
        if !$cache->{mountdata};
 
     my $path = $scfg->{path};
-    my $server = $scfg->{server};
-    my $share = $scfg->{share};
 
-    if (cifs_is_mounted($server, $share, $path, $cache->{mountdata})) {
+    if (cifs_is_mounted($scfg, $cache->{mountdata})) {
        my $cmd = ['/bin/umount', $path];
        run_command($cmd, errmsg => 'umount error');
     }
@@ -220,9 +259,12 @@ sub check_connection {
 
     my $servicename = '//'.$scfg->{server}.'/'.$scfg->{share};
 
-    my $cmd = ['/usr/bin/smbclient', $servicename, '-d', '0', '-m'];
+    my $cmd = ['/usr/bin/smbclient', $servicename, '-d', '0'];
 
-    push @$cmd, $scfg->{smbversion} ? "smb".int($scfg->{smbversion}) : 'smb3';
+    if (defined($scfg->{smbversion}) && $scfg->{smbversion} ne 'default') {
+       # max-protocol version, so basically only relevant for smb2 vs smb3
+       push @$cmd, '-m', "smb" . int($scfg->{smbversion});
+    }
 
     if (my $cred_file = get_cred_file($storeid)) {
        push @$cmd, '-U', $scfg->{username}, '-A', $cred_file;
@@ -230,22 +272,42 @@ sub check_connection {
     } else {
        push @$cmd, '-U', 'Guest','-N';
     }
-
     push @$cmd, '-c', 'echo 1 0';
 
     my $out_str;
-    eval {
-       run_command($cmd, timeout => 2, outfunc => sub {$out_str .= shift;},
-                   errfunc => sub {});
-    };
+    my $out = sub { $out_str .= shift };
+
+    eval { run_command($cmd, timeout => 10, outfunc => $out, errfunc => sub {}) };
 
     if (my $err = $@) {
        die "$out_str\n" if defined($out_str) &&
-           ($out_str =~ m/NT_STATUS_ACCESS_DENIED/);
+           ($out_str =~ m/NT_STATUS_(ACCESS_DENIED|LOGON_FAILURE)/);
        return 0;
     }
 
     return 1;
 }
 
+# FIXME remove on the next APIAGE reset.
+# Deprecated, use get_volume_attribute instead.
+sub get_volume_notes {
+    my $class = shift;
+    PVE::Storage::DirPlugin::get_volume_notes($class, @_);
+}
+
+# FIXME remove on the next APIAGE reset.
+# Deprecated, use update_volume_attribute instead.
+sub update_volume_notes {
+    my $class = shift;
+    PVE::Storage::DirPlugin::update_volume_notes($class, @_);
+}
+
+sub get_volume_attribute {
+    return PVE::Storage::DirPlugin::get_volume_attribute(@_);
+}
+
+sub update_volume_attribute {
+    return PVE::Storage::DirPlugin::update_volume_attribute(@_);
+}
+
 1;