]> git.proxmox.com Git - pve-storage.git/blob - PVE/Storage/CIFSPlugin.pm
Add CIFS Storage Plugin.
[pve-storage.git] / PVE / Storage / CIFSPlugin.pm
1 package PVE::Storage::CIFSPlugin;
2
3 use strict;
4 use warnings;
5 use Net::IP;
6 use PVE::Tools qw(run_command);
7 use PVE::ProcFSTools;
8 use File::Path;
9 use PVE::Storage::Plugin;
10 use PVE::JSONSchema qw(get_standard_option);
11
12 use base qw(PVE::Storage::Plugin);
13
14 # CIFS helper functions
15
16 sub cifs_is_mounted {
17 my ($server, $share, $mountpoint, $mountdata) = @_;
18
19 $server = "[$server]" if Net::IP::ip_is_ipv6($server);
20 my $source = "//${server}/$share";
21 $mountdata = PVE::ProcFSTools::parse_proc_mounts() if !$mountdata;
22
23 return $mountpoint if grep {
24 $_->[2] =~ /^cifs/ &&
25 $_->[0] =~ m|^\Q$source\E/?$| &&
26 $_->[1] eq $mountpoint
27 } @$mountdata;
28 return undef;
29 }
30
31 sub get_cred_file {
32 my ($storeid) = @_;
33
34 my $cred_file = '/etc/pve/priv/'.$storeid.'.cred';
35
36 if (-e $cred_file) {
37 return $cred_file;
38 }
39 return undef;
40 }
41
42 sub cifs_mount {
43 my ($server, $share, $mountpoint, $storeid, $smbver, $user, $domain) = @_;
44
45 $server = "[$server]" if Net::IP::ip_is_ipv6($server);
46 my $source = "//${server}/$share";
47
48 my $cmd = ['/bin/mount', '-t', 'cifs', $source, $mountpoint, '-o', 'soft', '-o'];
49
50 if (my $cred_file = get_cred_file($storeid)) {
51 push @$cmd, "username=$user", '-o', "credentials=$cred_file";
52 push @$cmd, '-o', "domain=$domain" if defined($domain);
53 } else {
54 push @$cmd, 'guest,username=guest';
55 }
56
57 push @$cmd, '-o', defined($smbver) ? "vers=$smbver" : "vers=3.0";
58
59 run_command($cmd, errmsg => "mount error");
60 }
61
62 # Configuration
63
64 sub type {
65 return 'cifs';
66 }
67
68 sub plugindata {
69 return {
70 content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1,
71 backup => 1}, { images => 1 }],
72 format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
73 };
74 }
75
76 sub properties {
77 return {
78 share => {
79 description => "CIFS share.",
80 type => 'string',
81 },
82 password => {
83 description => "Password for CIFS share.",
84 type => 'string',
85 maxLength => 256,
86 },
87 domain => {
88 description => "CIFS domain.",
89 type => 'string',
90 optional => 1,
91 maxLength => 256,
92 },
93 smbversion => {
94 description => "",
95 type => 'string',
96 optional => 1,
97 },
98 };
99 }
100
101 sub options {
102 return {
103 path => { fixed => 1 },
104 server => { fixed => 1 },
105 share => { fixed => 1 },
106 nodes => { optional => 1 },
107 disable => { optional => 1 },
108 maxfiles => { optional => 1 },
109 content => { optional => 1 },
110 format => { optional => 1 },
111 username => { optional => 1 },
112 password => { optional => 1},
113 domain => { optional => 1},
114 smbversion => { optional => 1},
115 };
116 }
117
118
119 sub check_config {
120 my ($class, $sectionId, $config, $create, $skipSchemaCheck) = @_;
121
122 $config->{path} = "/mnt/pve/$sectionId" if $create && !$config->{path};
123
124 return $class->SUPER::check_config($sectionId, $config, $create, $skipSchemaCheck);
125 }
126
127 # Storage implementation
128
129 sub status {
130 my ($class, $storeid, $scfg, $cache) = @_;
131
132 $cache->{mountdata} = PVE::ProcFSTools::parse_proc_mounts()
133 if !$cache->{mountdata};
134
135 my $path = $scfg->{path};
136 my $server = $scfg->{server};
137 my $share = $scfg->{share};
138
139 return undef
140 if !cifs_is_mounted($server, $share, $path, $cache->{mountdata});
141
142 return $class->SUPER::status($storeid, $scfg, $cache);
143 }
144
145 sub activate_storage {
146 my ($class, $storeid, $scfg, $cache) = @_;
147
148 $cache->{mountdata} = PVE::ProcFSTools::parse_proc_mounts()
149 if !$cache->{mountdata};
150
151 my $path = $scfg->{path};
152 my $server = $scfg->{server};
153 my $share = $scfg->{share};
154
155 if (!cifs_is_mounted($server, $share, $path, $cache->{mountdata})) {
156
157 mkpath $path;
158
159 die "unable to activate storage '$storeid' - " .
160 "directory '$path' does not exist\n" if ! -d $path;
161
162 cifs_mount($server, $share, $path, $storeid, $scfg->{smbversion},
163 $scfg->{username}, $scfg->{domain});
164 }
165
166 $class->SUPER::activate_storage($storeid, $scfg, $cache);
167 }
168
169 sub deactivate_storage {
170 my ($class, $storeid, $scfg, $cache) = @_;
171
172 $cache->{mountdata} = PVE::ProcFSTools::parse_proc_mounts()
173 if !$cache->{mountdata};
174
175 my $path = $scfg->{path};
176 my $server = $scfg->{server};
177 my $share = $scfg->{share};
178
179 if (cifs_is_mounted($server, $share, $path, $cache->{mountdata})) {
180 my $cmd = ['/bin/umount', $path];
181 run_command($cmd, errmsg => 'umount error');
182 }
183 }
184
185 sub check_connection {
186 my ($class, $storeid, $scfg) = @_;
187
188 my $server = $scfg->{server};
189
190 my $cmd = ['/usr/bin/smbclient', '-L', $server, '-d', '0', '-m'];
191
192 push @$cmd, $scfg->{smbversion} ? "smb".int($scfg->{smbversion}) : 'smb3';
193
194 if (my $cred_file = get_cred_file($storeid)) {
195 push @$cmd, '-U', $scfg->{username}, '-A', $cred_file;
196 push @$cmd, '-W', $scfg->{domain} if defined($scfg->{domain});
197 } else {
198 push @$cmd, '-U', 'Guest','-N';
199 }
200
201 my $out_str;
202 eval {
203 run_command($cmd, timeout => 2, outfunc => sub {$out_str .= shift;},
204 errfunc => sub {});
205 };
206
207 if (my $err = $@) {
208 die "$out_str\n" if ($out_str =~ m/NT_STATUS_ACCESS_DENIED/);
209 return 0;
210 }
211
212 return 1;
213 }
214
215 1;