diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-02 15:08:29 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-02 15:08:29 -0500 |
commit | cacf02df4b84d261d76db3d290ccb6b951df28c0 (patch) | |
tree | 5f0655ff89210626854c747566b934e2e562830a | |
parent | 74673fc50babc9be22b32c4ce697fceb51c7671a (diff) | |
parent | fea170804b4dc44cd79f8cb1ce236f3a824951cd (diff) |
Merge tag '4.21-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6
Pull cifs updates from Steve French:
- four fixes for stable
- improvements to DFS including allowing failover to alternate targets
- some small performance improvements
* tag '4.21-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6: (39 commits)
cifs: update internal module version number
cifs: we can not use small padding iovs together with encryption
cifs: Minor Kconfig clarification
cifs: Always resolve hostname before reconnecting
cifs: Add support for failover in cifs_reconnect_tcon()
cifs: Add support for failover in smb2_reconnect()
cifs: Only free DFS target list if we actually got one
cifs: start DFS cache refresher in cifs_mount()
cifs: Use GFP_ATOMIC when a lock is held in cifs_mount()
cifs: Add support for failover in cifs_reconnect()
cifs: Add support for failover in cifs_mount()
cifs: remove set but not used variable 'sep'
cifs: Make use of DFS cache to get new DFS referrals
cifs: minor updates to documentation
cifs: check kzalloc return
cifs: remove set but not used variable 'server'
cifs: Use kzfree() to free password
cifs: Fix to use kmem_cache_free() instead of kfree()
cifs: update for current_kernel_time64() removal
cifs: Add DFS cache routines
...
-rw-r--r-- | Documentation/filesystems/cifs/TODO | 26 | ||||
-rw-r--r-- | fs/cifs/Kconfig | 5 | ||||
-rw-r--r-- | fs/cifs/Makefile | 2 | ||||
-rw-r--r-- | fs/cifs/cifs_debug.c | 12 | ||||
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 138 | ||||
-rw-r--r-- | fs/cifs/cifs_fs_sb.h | 9 | ||||
-rw-r--r-- | fs/cifs/cifsencrypt.c | 13 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 17 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 15 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 28 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 88 | ||||
-rw-r--r-- | fs/cifs/connect.c | 924 | ||||
-rw-r--r-- | fs/cifs/dfs_cache.c | 1367 | ||||
-rw-r--r-- | fs/cifs/dfs_cache.h | 97 | ||||
-rw-r--r-- | fs/cifs/file.c | 12 | ||||
-rw-r--r-- | fs/cifs/inode.c | 44 | ||||
-rw-r--r-- | fs/cifs/misc.c | 68 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 9 | ||||
-rw-r--r-- | fs/cifs/sess.c | 4 | ||||
-rw-r--r-- | fs/cifs/smb1ops.c | 15 | ||||
-rw-r--r-- | fs/cifs/smb2inode.c | 16 | ||||
-rw-r--r-- | fs/cifs/smb2maperror.c | 4 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 322 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 108 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 1 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 15 | ||||
-rw-r--r-- | fs/cifs/transport.c | 8 |
28 files changed, 2862 insertions, 507 deletions
diff --git a/Documentation/filesystems/cifs/TODO b/Documentation/filesystems/cifs/TODO index 852499aed64b..66b3f54aa6dc 100644 --- a/Documentation/filesystems/cifs/TODO +++ b/Documentation/filesystems/cifs/TODO | |||
@@ -1,4 +1,4 @@ | |||
1 | Version 2.11 September 13, 2017 | 1 | Version 2.14 December 21, 2018 |
2 | 2 | ||
3 | A Partial List of Missing Features | 3 | A Partial List of Missing Features |
4 | ================================== | 4 | ================================== |
@@ -7,7 +7,7 @@ Contributions are welcome. There are plenty of opportunities | |||
7 | for visible, important contributions to this module. Here | 7 | for visible, important contributions to this module. Here |
8 | is a partial list of the known problems and missing features: | 8 | is a partial list of the known problems and missing features: |
9 | 9 | ||
10 | a) SMB3 (and SMB3.02) missing optional features: | 10 | a) SMB3 (and SMB3.1.1) missing optional features: |
11 | - multichannel (started), integration with RDMA | 11 | - multichannel (started), integration with RDMA |
12 | - directory leases (improved metadata caching), started (root dir only) | 12 | - directory leases (improved metadata caching), started (root dir only) |
13 | - T10 copy offload ie "ODX" (copy chunk, and "Duplicate Extents" ioctl | 13 | - T10 copy offload ie "ODX" (copy chunk, and "Duplicate Extents" ioctl |
@@ -21,8 +21,9 @@ using Directory Leases, currently only the root file handle is cached longer | |||
21 | d) quota support (needs minor kernel change since quota calls | 21 | d) quota support (needs minor kernel change since quota calls |
22 | to make it to network filesystems or deviceless filesystems) | 22 | to make it to network filesystems or deviceless filesystems) |
23 | 23 | ||
24 | e) Compounding (in progress) to reduce number of roundtrips, and also | 24 | e) Additional use cases where we use "compoounding" (e.g. open/query/close |
25 | better optimize open to reduce redundant opens (using reference counts more). | 25 | and open/setinfo/close) to reduce the number of roundtrips, and also |
26 | open to reduce redundant opens (using deferred close and reference counts more). | ||
26 | 27 | ||
27 | f) Finish inotify support so kde and gnome file list windows | 28 | f) Finish inotify support so kde and gnome file list windows |
28 | will autorefresh (partially complete by Asser). Needs minor kernel | 29 | will autorefresh (partially complete by Asser). Needs minor kernel |
@@ -43,11 +44,13 @@ exists. Also better integration with winbind for resolving SID owners | |||
43 | 44 | ||
44 | k) Add tools to take advantage of more smb3 specific ioctls and features | 45 | k) Add tools to take advantage of more smb3 specific ioctls and features |
45 | (passthrough ioctl/fsctl for sending various SMB3 fsctls to the server | 46 | (passthrough ioctl/fsctl for sending various SMB3 fsctls to the server |
46 | is in progress) | 47 | is in progress, and a passthrough query_info call is already implemented |
48 | in cifs.ko to allow smb3 info levels queries to be sent from userspace) | ||
47 | 49 | ||
48 | l) encrypted file support | 50 | l) encrypted file support |
49 | 51 | ||
50 | m) improved stats gathering, tools (perhaps integration with nfsometer?) | 52 | m) improved stats gathering tools (perhaps integration with nfsometer?) |
53 | to extend and make easier to use what is currently in /proc/fs/cifs/Stats | ||
51 | 54 | ||
52 | n) allow setting more NTFS/SMB3 file attributes remotely (currently limited to compressed | 55 | n) allow setting more NTFS/SMB3 file attributes remotely (currently limited to compressed |
53 | file attribute via chflags) and improve user space tools for managing and | 56 | file attribute via chflags) and improve user space tools for managing and |
@@ -76,6 +79,9 @@ and simplify the code. | |||
76 | v) POSIX Extensions for SMB3.1.1 (started, create and mkdir support added | 79 | v) POSIX Extensions for SMB3.1.1 (started, create and mkdir support added |
77 | so far). | 80 | so far). |
78 | 81 | ||
82 | w) Add support for additional strong encryption types, and additional spnego | ||
83 | authentication mechanisms (see MS-SMB2) | ||
84 | |||
79 | KNOWN BUGS | 85 | KNOWN BUGS |
80 | ==================================== | 86 | ==================================== |
81 | See http://bugzilla.samba.org - search on product "CifsVFS" for | 87 | See http://bugzilla.samba.org - search on product "CifsVFS" for |
@@ -102,3 +108,11 @@ and when signing is disabled to request larger read sizes (larger than | |||
102 | negotiated size) and send larger write sizes to modern servers. | 108 | negotiated size) and send larger write sizes to modern servers. |
103 | 109 | ||
104 | 4) More exhaustively test against less common servers | 110 | 4) More exhaustively test against less common servers |
111 | |||
112 | 5) Continue to extend the smb3 "buildbot" which does automated xfstesting | ||
113 | against Windows, Samba and Azure currently - to add additional tests and | ||
114 | to allow the buildbot to execute the tests faster. | ||
115 | |||
116 | 6) Address various coverity warnings (most are not bugs per-se, but | ||
117 | the more warnings are addressed, the easier it is to spot real | ||
118 | problems that static analyzers will point out in the future). | ||
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 85dadb93c992..f1ddc9d03c10 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig | |||
@@ -190,8 +190,9 @@ config CIFS_DFS_UPCALL | |||
190 | moves to a different server. This feature also enables | 190 | moves to a different server. This feature also enables |
191 | an upcall mechanism for CIFS which contacts userspace helper | 191 | an upcall mechanism for CIFS which contacts userspace helper |
192 | utilities to provide server name resolution (host names to | 192 | utilities to provide server name resolution (host names to |
193 | IP addresses) which is needed for implicit mounts of DFS junction | 193 | IP addresses) which is needed in order to reconnect to |
194 | points. If unsure, say Y. | 194 | servers if their addresses change or for implicit mounts of |
195 | DFS junction points. If unsure, say Y. | ||
195 | 196 | ||
196 | config CIFS_NFSD_EXPORT | 197 | config CIFS_NFSD_EXPORT |
197 | bool "Allow nfsd to export CIFS file system" | 198 | bool "Allow nfsd to export CIFS file system" |
diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 85817991ee68..51af69a1a328 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile | |||
@@ -17,7 +17,7 @@ cifs-$(CONFIG_CIFS_ACL) += cifsacl.o | |||
17 | 17 | ||
18 | cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o | 18 | cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o |
19 | 19 | ||
20 | cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o | 20 | cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o |
21 | 21 | ||
22 | cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o | 22 | cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o |
23 | 23 | ||
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index ba178b09de0b..593fb422d0f3 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c | |||
@@ -30,6 +30,9 @@ | |||
30 | #include "cifsproto.h" | 30 | #include "cifsproto.h" |
31 | #include "cifs_debug.h" | 31 | #include "cifs_debug.h" |
32 | #include "cifsfs.h" | 32 | #include "cifsfs.h" |
33 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
34 | #include "dfs_cache.h" | ||
35 | #endif | ||
33 | #ifdef CONFIG_CIFS_SMB_DIRECT | 36 | #ifdef CONFIG_CIFS_SMB_DIRECT |
34 | #include "smbdirect.h" | 37 | #include "smbdirect.h" |
35 | #endif | 38 | #endif |
@@ -629,6 +632,11 @@ cifs_proc_init(void) | |||
629 | &cifs_security_flags_proc_fops); | 632 | &cifs_security_flags_proc_fops); |
630 | proc_create("LookupCacheEnabled", 0644, proc_fs_cifs, | 633 | proc_create("LookupCacheEnabled", 0644, proc_fs_cifs, |
631 | &cifs_lookup_cache_proc_fops); | 634 | &cifs_lookup_cache_proc_fops); |
635 | |||
636 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
637 | proc_create("dfscache", 0644, proc_fs_cifs, &dfscache_proc_fops); | ||
638 | #endif | ||
639 | |||
632 | #ifdef CONFIG_CIFS_SMB_DIRECT | 640 | #ifdef CONFIG_CIFS_SMB_DIRECT |
633 | proc_create("rdma_readwrite_threshold", 0644, proc_fs_cifs, | 641 | proc_create("rdma_readwrite_threshold", 0644, proc_fs_cifs, |
634 | &cifs_rdma_readwrite_threshold_proc_fops); | 642 | &cifs_rdma_readwrite_threshold_proc_fops); |
@@ -663,6 +671,10 @@ cifs_proc_clean(void) | |||
663 | remove_proc_entry("SecurityFlags", proc_fs_cifs); | 671 | remove_proc_entry("SecurityFlags", proc_fs_cifs); |
664 | remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); | 672 | remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); |
665 | remove_proc_entry("LookupCacheEnabled", proc_fs_cifs); | 673 | remove_proc_entry("LookupCacheEnabled", proc_fs_cifs); |
674 | |||
675 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
676 | remove_proc_entry("dfscache", proc_fs_cifs); | ||
677 | #endif | ||
666 | #ifdef CONFIG_CIFS_SMB_DIRECT | 678 | #ifdef CONFIG_CIFS_SMB_DIRECT |
667 | remove_proc_entry("rdma_readwrite_threshold", proc_fs_cifs); | 679 | remove_proc_entry("rdma_readwrite_threshold", proc_fs_cifs); |
668 | remove_proc_entry("smbd_max_frmr_depth", proc_fs_cifs); | 680 | remove_proc_entry("smbd_max_frmr_depth", proc_fs_cifs); |
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index b97c74efd04a..d9b99abe1243 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include "dns_resolve.h" | 25 | #include "dns_resolve.h" |
26 | #include "cifs_debug.h" | 26 | #include "cifs_debug.h" |
27 | #include "cifs_unicode.h" | 27 | #include "cifs_unicode.h" |
28 | #include "dfs_cache.h" | ||
28 | 29 | ||
29 | static LIST_HEAD(cifs_dfs_automount_list); | 30 | static LIST_HEAD(cifs_dfs_automount_list); |
30 | 31 | ||
@@ -126,7 +127,7 @@ cifs_build_devname(char *nodename, const char *prepath) | |||
126 | * @sb_mountdata: parent/root DFS mount options (template) | 127 | * @sb_mountdata: parent/root DFS mount options (template) |
127 | * @fullpath: full path in UNC format | 128 | * @fullpath: full path in UNC format |
128 | * @ref: server's referral | 129 | * @ref: server's referral |
129 | * @devname: pointer for saving device name | 130 | * @devname: optional pointer for saving device name |
130 | * | 131 | * |
131 | * creates mount options for submount based on template options sb_mountdata | 132 | * creates mount options for submount based on template options sb_mountdata |
132 | * and replacing unc,ip,prefixpath options with ones we've got form ref_unc. | 133 | * and replacing unc,ip,prefixpath options with ones we've got form ref_unc. |
@@ -140,6 +141,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata, | |||
140 | char **devname) | 141 | char **devname) |
141 | { | 142 | { |
142 | int rc; | 143 | int rc; |
144 | char *name; | ||
143 | char *mountdata = NULL; | 145 | char *mountdata = NULL; |
144 | const char *prepath = NULL; | 146 | const char *prepath = NULL; |
145 | int md_len; | 147 | int md_len; |
@@ -158,17 +160,17 @@ char *cifs_compose_mount_options(const char *sb_mountdata, | |||
158 | prepath++; | 160 | prepath++; |
159 | } | 161 | } |
160 | 162 | ||
161 | *devname = cifs_build_devname(ref->node_name, prepath); | 163 | name = cifs_build_devname(ref->node_name, prepath); |
162 | if (IS_ERR(*devname)) { | 164 | if (IS_ERR(name)) { |
163 | rc = PTR_ERR(*devname); | 165 | rc = PTR_ERR(name); |
164 | *devname = NULL; | 166 | name = NULL; |
165 | goto compose_mount_options_err; | 167 | goto compose_mount_options_err; |
166 | } | 168 | } |
167 | 169 | ||
168 | rc = dns_resolve_server_name_to_ip(*devname, &srvIP); | 170 | rc = dns_resolve_server_name_to_ip(name, &srvIP); |
169 | if (rc < 0) { | 171 | if (rc < 0) { |
170 | cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n", | 172 | cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n", |
171 | __func__, *devname, rc); | 173 | __func__, name, rc); |
172 | goto compose_mount_options_err; | 174 | goto compose_mount_options_err; |
173 | } | 175 | } |
174 | 176 | ||
@@ -224,6 +226,9 @@ char *cifs_compose_mount_options(const char *sb_mountdata, | |||
224 | strcat(mountdata, "ip="); | 226 | strcat(mountdata, "ip="); |
225 | strcat(mountdata, srvIP); | 227 | strcat(mountdata, srvIP); |
226 | 228 | ||
229 | if (devname) | ||
230 | *devname = name; | ||
231 | |||
227 | /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/ | 232 | /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/ |
228 | /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/ | 233 | /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/ |
229 | 234 | ||
@@ -234,8 +239,7 @@ compose_mount_options_out: | |||
234 | compose_mount_options_err: | 239 | compose_mount_options_err: |
235 | kfree(mountdata); | 240 | kfree(mountdata); |
236 | mountdata = ERR_PTR(rc); | 241 | mountdata = ERR_PTR(rc); |
237 | kfree(*devname); | 242 | kfree(name); |
238 | *devname = NULL; | ||
239 | goto compose_mount_options_out; | 243 | goto compose_mount_options_out; |
240 | } | 244 | } |
241 | 245 | ||
@@ -251,20 +255,30 @@ static struct vfsmount *cifs_dfs_do_refmount(struct dentry *mntpt, | |||
251 | { | 255 | { |
252 | struct vfsmount *mnt; | 256 | struct vfsmount *mnt; |
253 | char *mountdata; | 257 | char *mountdata; |
254 | char *devname = NULL; | 258 | char *devname; |
259 | |||
260 | /* | ||
261 | * Always pass down the DFS full path to smb3_do_mount() so we | ||
262 | * can use it later for failover. | ||
263 | */ | ||
264 | devname = kstrndup(fullpath, strlen(fullpath), GFP_KERNEL); | ||
265 | if (!devname) | ||
266 | return ERR_PTR(-ENOMEM); | ||
267 | |||
268 | convert_delimiter(devname, '/'); | ||
255 | 269 | ||
256 | /* strip first '\' from fullpath */ | 270 | /* strip first '\' from fullpath */ |
257 | mountdata = cifs_compose_mount_options(cifs_sb->mountdata, | 271 | mountdata = cifs_compose_mount_options(cifs_sb->mountdata, |
258 | fullpath + 1, ref, &devname); | 272 | fullpath + 1, ref, NULL); |
259 | 273 | if (IS_ERR(mountdata)) { | |
260 | if (IS_ERR(mountdata)) | 274 | kfree(devname); |
261 | return (struct vfsmount *)mountdata; | 275 | return (struct vfsmount *)mountdata; |
276 | } | ||
262 | 277 | ||
263 | mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata); | 278 | mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata); |
264 | kfree(mountdata); | 279 | kfree(mountdata); |
265 | kfree(devname); | 280 | kfree(devname); |
266 | return mnt; | 281 | return mnt; |
267 | |||
268 | } | 282 | } |
269 | 283 | ||
270 | static void dump_referral(const struct dfs_info3_param *ref) | 284 | static void dump_referral(const struct dfs_info3_param *ref) |
@@ -282,16 +296,15 @@ static void dump_referral(const struct dfs_info3_param *ref) | |||
282 | */ | 296 | */ |
283 | static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) | 297 | static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) |
284 | { | 298 | { |
285 | struct dfs_info3_param *referrals = NULL; | 299 | struct dfs_info3_param referral = {0}; |
286 | unsigned int num_referrals = 0; | ||
287 | struct cifs_sb_info *cifs_sb; | 300 | struct cifs_sb_info *cifs_sb; |
288 | struct cifs_ses *ses; | 301 | struct cifs_ses *ses; |
289 | char *full_path; | 302 | struct cifs_tcon *tcon; |
303 | char *full_path, *root_path; | ||
290 | unsigned int xid; | 304 | unsigned int xid; |
291 | int i; | 305 | int len; |
292 | int rc; | 306 | int rc; |
293 | struct vfsmount *mnt; | 307 | struct vfsmount *mnt; |
294 | struct tcon_link *tlink; | ||
295 | 308 | ||
296 | cifs_dbg(FYI, "in %s\n", __func__); | 309 | cifs_dbg(FYI, "in %s\n", __func__); |
297 | BUG_ON(IS_ROOT(mntpt)); | 310 | BUG_ON(IS_ROOT(mntpt)); |
@@ -315,48 +328,69 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) | |||
315 | if (full_path == NULL) | 328 | if (full_path == NULL) |
316 | goto cdda_exit; | 329 | goto cdda_exit; |
317 | 330 | ||
318 | tlink = cifs_sb_tlink(cifs_sb); | 331 | cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); |
319 | if (IS_ERR(tlink)) { | 332 | |
320 | mnt = ERR_CAST(tlink); | 333 | if (!cifs_sb_master_tlink(cifs_sb)) { |
334 | cifs_dbg(FYI, "%s: master tlink is NULL\n", __func__); | ||
321 | goto free_full_path; | 335 | goto free_full_path; |
322 | } | 336 | } |
323 | ses = tlink_tcon(tlink)->ses; | ||
324 | 337 | ||
338 | tcon = cifs_sb_master_tcon(cifs_sb); | ||
339 | if (!tcon) { | ||
340 | cifs_dbg(FYI, "%s: master tcon is NULL\n", __func__); | ||
341 | goto free_full_path; | ||
342 | } | ||
343 | |||
344 | root_path = kstrdup(tcon->treeName, GFP_KERNEL); | ||
345 | if (!root_path) { | ||
346 | mnt = ERR_PTR(-ENOMEM); | ||
347 | goto free_full_path; | ||
348 | } | ||
349 | cifs_dbg(FYI, "%s: root path: %s\n", __func__, root_path); | ||
350 | |||
351 | ses = tcon->ses; | ||
325 | xid = get_xid(); | 352 | xid = get_xid(); |
326 | rc = get_dfs_path(xid, ses, full_path + 1, cifs_sb->local_nls, | ||
327 | &num_referrals, &referrals, | ||
328 | cifs_remap(cifs_sb)); | ||
329 | free_xid(xid); | ||
330 | 353 | ||
331 | cifs_put_tlink(tlink); | 354 | /* |
332 | 355 | * If DFS root has been expired, then unconditionally fetch it again to | |
333 | mnt = ERR_PTR(-ENOENT); | 356 | * refresh DFS referral cache. |
334 | for (i = 0; i < num_referrals; i++) { | 357 | */ |
335 | int len; | 358 | rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), |
336 | dump_referral(referrals + i); | 359 | root_path + 1, NULL, NULL); |
337 | /* connect to a node */ | 360 | if (!rc) { |
338 | len = strlen(referrals[i].node_name); | 361 | rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, |
339 | if (len < 2) { | 362 | cifs_remap(cifs_sb), full_path + 1, |
340 | cifs_dbg(VFS, "%s: Net Address path too short: %s\n", | 363 | &referral, NULL); |
341 | __func__, referrals[i].node_name); | ||
342 | mnt = ERR_PTR(-EINVAL); | ||
343 | break; | ||
344 | } | ||
345 | mnt = cifs_dfs_do_refmount(mntpt, cifs_sb, | ||
346 | full_path, referrals + i); | ||
347 | cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n", | ||
348 | __func__, referrals[i].node_name, mnt); | ||
349 | if (!IS_ERR(mnt)) | ||
350 | goto success; | ||
351 | } | 364 | } |
352 | 365 | ||
353 | /* no valid submounts were found; return error from get_dfs_path() by | 366 | free_xid(xid); |
354 | * preference */ | 367 | |
355 | if (rc != 0) | 368 | if (rc) { |
356 | mnt = ERR_PTR(rc); | 369 | mnt = ERR_PTR(rc); |
370 | goto free_root_path; | ||
371 | } | ||
372 | |||
373 | dump_referral(&referral); | ||
357 | 374 | ||
358 | success: | 375 | len = strlen(referral.node_name); |
359 | free_dfs_info_array(referrals, num_referrals); | 376 | if (len < 2) { |
377 | cifs_dbg(VFS, "%s: Net Address path too short: %s\n", | ||
378 | __func__, referral.node_name); | ||
379 | mnt = ERR_PTR(-EINVAL); | ||
380 | goto free_dfs_ref; | ||
381 | } | ||
382 | /* | ||
383 | * cifs_mount() will retry every available node server in case | ||
384 | * of failures. | ||
385 | */ | ||
386 | mnt = cifs_dfs_do_refmount(mntpt, cifs_sb, full_path, &referral); | ||
387 | cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n", __func__, | ||
388 | referral.node_name, mnt); | ||
389 | |||
390 | free_dfs_ref: | ||
391 | free_dfs_info_param(&referral); | ||
392 | free_root_path: | ||
393 | kfree(root_path); | ||
360 | free_full_path: | 394 | free_full_path: |
361 | kfree(full_path); | 395 | kfree(full_path); |
362 | cdda_exit: | 396 | cdda_exit: |
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 63d7530f2e1d..42f0d67f1054 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h | |||
@@ -72,6 +72,15 @@ struct cifs_sb_info { | |||
72 | char *mountdata; /* options received at mount time or via DFS refs */ | 72 | char *mountdata; /* options received at mount time or via DFS refs */ |
73 | struct delayed_work prune_tlinks; | 73 | struct delayed_work prune_tlinks; |
74 | struct rcu_head rcu; | 74 | struct rcu_head rcu; |
75 | |||
76 | /* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */ | ||
75 | char *prepath; | 77 | char *prepath; |
78 | |||
79 | /* | ||
80 | * Path initially provided by the mount call. We might connect | ||
81 | * to something different via DFS but we want to keep it to do | ||
82 | * failover properly. | ||
83 | */ | ||
84 | char *origin_fullpath; /* \\HOST\SHARE\[OPTIONAL PATH] */ | ||
76 | }; | 85 | }; |
77 | #endif /* _CIFS_FS_SB_H */ | 86 | #endif /* _CIFS_FS_SB_H */ |
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 85b31cfa2f3c..d2a05e46d6f5 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c | |||
@@ -224,7 +224,7 @@ int cifs_verify_signature(struct smb_rqst *rqst, | |||
224 | if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) { | 224 | if (cifs_pdu->Command == SMB_COM_LOCKING_ANDX) { |
225 | struct smb_com_lock_req *pSMB = | 225 | struct smb_com_lock_req *pSMB = |
226 | (struct smb_com_lock_req *)cifs_pdu; | 226 | (struct smb_com_lock_req *)cifs_pdu; |
227 | if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE) | 227 | if (pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE) |
228 | return 0; | 228 | return 0; |
229 | } | 229 | } |
230 | 230 | ||
@@ -304,12 +304,17 @@ int setup_ntlm_response(struct cifs_ses *ses, const struct nls_table *nls_cp) | |||
304 | int calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, | 304 | int calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, |
305 | char *lnm_session_key) | 305 | char *lnm_session_key) |
306 | { | 306 | { |
307 | int i; | 307 | int i, len; |
308 | int rc; | 308 | int rc; |
309 | char password_with_pad[CIFS_ENCPWD_SIZE] = {0}; | 309 | char password_with_pad[CIFS_ENCPWD_SIZE] = {0}; |
310 | 310 | ||
311 | if (password) | 311 | if (password) { |
312 | strncpy(password_with_pad, password, CIFS_ENCPWD_SIZE); | 312 | for (len = 0; len < CIFS_ENCPWD_SIZE; len++) |
313 | if (!password[len]) | ||
314 | break; | ||
315 | |||
316 | memcpy(password_with_pad, password, len); | ||
317 | } | ||
313 | 318 | ||
314 | if (!encrypt && global_secflags & CIFSSEC_MAY_PLNTXT) { | 319 | if (!encrypt && global_secflags & CIFSSEC_MAY_PLNTXT) { |
315 | memcpy(lnm_session_key, password_with_pad, | 320 | memcpy(lnm_session_key, password_with_pad, |
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 865706edb307..62d48d486d8f 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -52,6 +52,9 @@ | |||
52 | #include "cifs_spnego.h" | 52 | #include "cifs_spnego.h" |
53 | #include "fscache.h" | 53 | #include "fscache.h" |
54 | #include "smb2pdu.h" | 54 | #include "smb2pdu.h" |
55 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
56 | #include "dfs_cache.h" | ||
57 | #endif | ||
55 | 58 | ||
56 | int cifsFYI = 0; | 59 | int cifsFYI = 0; |
57 | bool traceSMB; | 60 | bool traceSMB; |
@@ -1494,10 +1497,15 @@ init_cifs(void) | |||
1494 | if (rc) | 1497 | if (rc) |
1495 | goto out_destroy_mids; | 1498 | goto out_destroy_mids; |
1496 | 1499 | ||
1500 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
1501 | rc = dfs_cache_init(); | ||
1502 | if (rc) | ||
1503 | goto out_destroy_request_bufs; | ||
1504 | #endif /* CONFIG_CIFS_DFS_UPCALL */ | ||
1497 | #ifdef CONFIG_CIFS_UPCALL | 1505 | #ifdef CONFIG_CIFS_UPCALL |
1498 | rc = init_cifs_spnego(); | 1506 | rc = init_cifs_spnego(); |
1499 | if (rc) | 1507 | if (rc) |
1500 | goto out_destroy_request_bufs; | 1508 | goto out_destroy_dfs_cache; |
1501 | #endif /* CONFIG_CIFS_UPCALL */ | 1509 | #endif /* CONFIG_CIFS_UPCALL */ |
1502 | 1510 | ||
1503 | #ifdef CONFIG_CIFS_ACL | 1511 | #ifdef CONFIG_CIFS_ACL |
@@ -1525,6 +1533,10 @@ out_register_key_type: | |||
1525 | #endif | 1533 | #endif |
1526 | #ifdef CONFIG_CIFS_UPCALL | 1534 | #ifdef CONFIG_CIFS_UPCALL |
1527 | exit_cifs_spnego(); | 1535 | exit_cifs_spnego(); |
1536 | out_destroy_dfs_cache: | ||
1537 | #endif | ||
1538 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
1539 | dfs_cache_destroy(); | ||
1528 | out_destroy_request_bufs: | 1540 | out_destroy_request_bufs: |
1529 | #endif | 1541 | #endif |
1530 | cifs_destroy_request_bufs(); | 1542 | cifs_destroy_request_bufs(); |
@@ -1556,6 +1568,9 @@ exit_cifs(void) | |||
1556 | #ifdef CONFIG_CIFS_UPCALL | 1568 | #ifdef CONFIG_CIFS_UPCALL |
1557 | exit_cifs_spnego(); | 1569 | exit_cifs_spnego(); |
1558 | #endif | 1570 | #endif |
1571 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
1572 | dfs_cache_destroy(); | ||
1573 | #endif | ||
1559 | cifs_destroy_request_bufs(); | 1574 | cifs_destroy_request_bufs(); |
1560 | cifs_destroy_mids(); | 1575 | cifs_destroy_mids(); |
1561 | cifs_destroy_inodecache(); | 1576 | cifs_destroy_inodecache(); |
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 4c3b5cfccc49..26776eddd85d 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h | |||
@@ -150,5 +150,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); | |||
150 | extern const struct export_operations cifs_export_ops; | 150 | extern const struct export_operations cifs_export_ops; |
151 | #endif /* CONFIG_CIFS_NFSD_EXPORT */ | 151 | #endif /* CONFIG_CIFS_NFSD_EXPORT */ |
152 | 152 | ||
153 | #define CIFS_VERSION "2.14" | 153 | #define CIFS_VERSION "2.15" |
154 | #endif /* _CIFSFS_H */ | 154 | #endif /* _CIFSFS_H */ |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 38ab0fca49e1..01ded7038b19 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -701,6 +701,13 @@ struct TCP_Server_Info { | |||
701 | struct delayed_work reconnect; /* reconnect workqueue job */ | 701 | struct delayed_work reconnect; /* reconnect workqueue job */ |
702 | struct mutex reconnect_mutex; /* prevent simultaneous reconnects */ | 702 | struct mutex reconnect_mutex; /* prevent simultaneous reconnects */ |
703 | unsigned long echo_interval; | 703 | unsigned long echo_interval; |
704 | |||
705 | /* | ||
706 | * Number of targets available for reconnect. The more targets | ||
707 | * the more tasks have to wait to let the demultiplex thread | ||
708 | * reconnect. | ||
709 | */ | ||
710 | int nr_targets; | ||
704 | }; | 711 | }; |
705 | 712 | ||
706 | static inline unsigned int | 713 | static inline unsigned int |
@@ -1014,6 +1021,11 @@ struct cifs_tcon { | |||
1014 | struct list_head pending_opens; /* list of incomplete opens */ | 1021 | struct list_head pending_opens; /* list of incomplete opens */ |
1015 | struct cached_fid crfid; /* Cached root fid */ | 1022 | struct cached_fid crfid; /* Cached root fid */ |
1016 | /* BB add field for back pointer to sb struct(s)? */ | 1023 | /* BB add field for back pointer to sb struct(s)? */ |
1024 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
1025 | char *dfs_path; | ||
1026 | int remap:2; | ||
1027 | struct list_head ulist; /* cache update list */ | ||
1028 | #endif | ||
1017 | }; | 1029 | }; |
1018 | 1030 | ||
1019 | /* | 1031 | /* |
@@ -1508,6 +1520,7 @@ struct dfs_info3_param { | |||
1508 | int ref_flag; | 1520 | int ref_flag; |
1509 | char *path_name; | 1521 | char *path_name; |
1510 | char *node_name; | 1522 | char *node_name; |
1523 | int ttl; | ||
1511 | }; | 1524 | }; |
1512 | 1525 | ||
1513 | /* | 1526 | /* |
@@ -1545,7 +1558,6 @@ static inline void free_dfs_info_param(struct dfs_info3_param *param) | |||
1545 | if (param) { | 1558 | if (param) { |
1546 | kfree(param->path_name); | 1559 | kfree(param->path_name); |
1547 | kfree(param->node_name); | 1560 | kfree(param->node_name); |
1548 | kfree(param); | ||
1549 | } | 1561 | } |
1550 | } | 1562 | } |
1551 | 1563 | ||
@@ -1790,6 +1802,7 @@ extern struct smb_version_values smb3any_values; | |||
1790 | extern struct smb_version_operations smb30_operations; | 1802 | extern struct smb_version_operations smb30_operations; |
1791 | extern struct smb_version_values smb30_values; | 1803 | extern struct smb_version_values smb30_values; |
1792 | #define SMB302_VERSION_STRING "3.02" | 1804 | #define SMB302_VERSION_STRING "3.02" |
1805 | #define ALT_SMB302_VERSION_STRING "3.0.2" | ||
1793 | /*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */ | 1806 | /*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */ |
1794 | extern struct smb_version_values smb302_values; | 1807 | extern struct smb_version_values smb302_values; |
1795 | #define SMB311_VERSION_STRING "3.1.1" | 1808 | #define SMB311_VERSION_STRING "3.1.1" |
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index fa361bc00602..336c116995d7 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h | |||
@@ -22,6 +22,9 @@ | |||
22 | #define _CIFSPROTO_H | 22 | #define _CIFSPROTO_H |
23 | #include <linux/nls.h> | 23 | #include <linux/nls.h> |
24 | #include "trace.h" | 24 | #include "trace.h" |
25 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
26 | #include "dfs_cache.h" | ||
27 | #endif | ||
25 | 28 | ||
26 | struct statfs; | 29 | struct statfs; |
27 | struct smb_vol; | 30 | struct smb_vol; |
@@ -213,7 +216,7 @@ extern int cifs_match_super(struct super_block *, void *); | |||
213 | extern void cifs_cleanup_volume_info(struct smb_vol *pvolume_info); | 216 | extern void cifs_cleanup_volume_info(struct smb_vol *pvolume_info); |
214 | extern struct smb_vol *cifs_get_volume_info(char *mount_data, | 217 | extern struct smb_vol *cifs_get_volume_info(char *mount_data, |
215 | const char *devname, bool is_smb3); | 218 | const char *devname, bool is_smb3); |
216 | extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); | 219 | extern int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol); |
217 | extern void cifs_umount(struct cifs_sb_info *); | 220 | extern void cifs_umount(struct cifs_sb_info *); |
218 | extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); | 221 | extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); |
219 | extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon); | 222 | extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon); |
@@ -294,11 +297,6 @@ extern int CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, | |||
294 | unsigned int *num_of_nodes, | 297 | unsigned int *num_of_nodes, |
295 | const struct nls_table *nls_codepage, int remap); | 298 | const struct nls_table *nls_codepage, int remap); |
296 | 299 | ||
297 | extern int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, | ||
298 | const char *old_path, | ||
299 | const struct nls_table *nls_codepage, | ||
300 | unsigned int *num_referrals, | ||
301 | struct dfs_info3_param **referrals, int remap); | ||
302 | extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, | 300 | extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, |
303 | unsigned int *num_of_nodes, | 301 | unsigned int *num_of_nodes, |
304 | struct dfs_info3_param **target_nodes, | 302 | struct dfs_info3_param **target_nodes, |
@@ -524,6 +522,11 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16, | |||
524 | const struct nls_table *codepage); | 522 | const struct nls_table *codepage); |
525 | extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, | 523 | extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, |
526 | unsigned char *p24); | 524 | unsigned char *p24); |
525 | extern void | ||
526 | cifs_cleanup_volume_info_contents(struct smb_vol *volume_info); | ||
527 | |||
528 | extern struct TCP_Server_Info * | ||
529 | cifs_find_tcp_session(struct smb_vol *vol); | ||
527 | 530 | ||
528 | void cifs_readdata_release(struct kref *refcount); | 531 | void cifs_readdata_release(struct kref *refcount); |
529 | int cifs_async_readv(struct cifs_readdata *rdata); | 532 | int cifs_async_readv(struct cifs_readdata *rdata); |
@@ -562,4 +565,17 @@ void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc); | |||
562 | extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page, | 565 | extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page, |
563 | unsigned int *len, unsigned int *offset); | 566 | unsigned int *len, unsigned int *offset); |
564 | 567 | ||
568 | void extract_unc_hostname(const char *unc, const char **h, size_t *len); | ||
569 | |||
570 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
571 | static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, | ||
572 | const char *old_path, | ||
573 | const struct nls_table *nls_codepage, | ||
574 | struct dfs_info3_param *referral, int remap) | ||
575 | { | ||
576 | return dfs_cache_find(xid, ses, nls_codepage, remap, old_path, | ||
577 | referral, NULL); | ||
578 | } | ||
579 | #endif | ||
580 | |||
565 | #endif /* _CIFSPROTO_H */ | 581 | #endif /* _CIFSPROTO_H */ |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f82fd342bca5..b1f49c1c543a 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -44,6 +44,9 @@ | |||
44 | #include "cifs_debug.h" | 44 | #include "cifs_debug.h" |
45 | #include "fscache.h" | 45 | #include "fscache.h" |
46 | #include "smbdirect.h" | 46 | #include "smbdirect.h" |
47 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
48 | #include "dfs_cache.h" | ||
49 | #endif | ||
47 | 50 | ||
48 | #ifdef CONFIG_CIFS_POSIX | 51 | #ifdef CONFIG_CIFS_POSIX |
49 | static struct { | 52 | static struct { |
@@ -118,6 +121,77 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) | |||
118 | */ | 121 | */ |
119 | } | 122 | } |
120 | 123 | ||
124 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
125 | static int __cifs_reconnect_tcon(const struct nls_table *nlsc, | ||
126 | struct cifs_tcon *tcon) | ||
127 | { | ||
128 | int rc; | ||
129 | struct dfs_cache_tgt_list tl; | ||
130 | struct dfs_cache_tgt_iterator *it = NULL; | ||
131 | char tree[MAX_TREE_SIZE + 1]; | ||
132 | const char *tcp_host; | ||
133 | size_t tcp_host_len; | ||
134 | const char *dfs_host; | ||
135 | size_t dfs_host_len; | ||
136 | |||
137 | if (tcon->ipc) { | ||
138 | snprintf(tree, sizeof(tree), "\\\\%s\\IPC$", | ||
139 | tcon->ses->server->hostname); | ||
140 | return CIFSTCon(0, tcon->ses, tree, tcon, nlsc); | ||
141 | } | ||
142 | |||
143 | if (!tcon->dfs_path) | ||
144 | return CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nlsc); | ||
145 | |||
146 | rc = dfs_cache_noreq_find(tcon->dfs_path + 1, NULL, &tl); | ||
147 | if (rc) | ||
148 | return rc; | ||
149 | |||
150 | extract_unc_hostname(tcon->ses->server->hostname, &tcp_host, | ||
151 | &tcp_host_len); | ||
152 | |||
153 | for (it = dfs_cache_get_tgt_iterator(&tl); it; | ||
154 | it = dfs_cache_get_next_tgt(&tl, it)) { | ||
155 | const char *tgt = dfs_cache_get_tgt_name(it); | ||
156 | |||
157 | extract_unc_hostname(tgt, &dfs_host, &dfs_host_len); | ||
158 | |||
159 | if (dfs_host_len != tcp_host_len | ||
160 | || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { | ||
161 | cifs_dbg(FYI, "%s: skipping %.*s, doesn't match %.*s", | ||
162 | __func__, | ||
163 | (int)dfs_host_len, dfs_host, | ||
164 | (int)tcp_host_len, tcp_host); | ||
165 | continue; | ||
166 | } | ||
167 | |||
168 | snprintf(tree, sizeof(tree), "\\%s", tgt); | ||
169 | |||
170 | rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc); | ||
171 | if (!rc) | ||
172 | break; | ||
173 | if (rc == -EREMOTE) | ||
174 | break; | ||
175 | } | ||
176 | |||
177 | if (!rc) { | ||
178 | if (it) | ||
179 | rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, | ||
180 | it); | ||
181 | else | ||
182 | rc = -ENOENT; | ||
183 | } | ||
184 | dfs_cache_free_tgts(&tl); | ||
185 | return rc; | ||
186 | } | ||
187 | #else | ||
188 | static inline int __cifs_reconnect_tcon(const struct nls_table *nlsc, | ||
189 | struct cifs_tcon *tcon) | ||
190 | { | ||
191 | return CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nlsc); | ||
192 | } | ||
193 | #endif | ||
194 | |||
121 | /* reconnect the socket, tcon, and smb session if needed */ | 195 | /* reconnect the socket, tcon, and smb session if needed */ |
122 | static int | 196 | static int |
123 | cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) | 197 | cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) |
@@ -126,6 +200,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) | |||
126 | struct cifs_ses *ses; | 200 | struct cifs_ses *ses; |
127 | struct TCP_Server_Info *server; | 201 | struct TCP_Server_Info *server; |
128 | struct nls_table *nls_codepage; | 202 | struct nls_table *nls_codepage; |
203 | int retries; | ||
129 | 204 | ||
130 | /* | 205 | /* |
131 | * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for | 206 | * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for |
@@ -152,9 +227,12 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) | |||
152 | } | 227 | } |
153 | } | 228 | } |
154 | 229 | ||
230 | retries = server->nr_targets; | ||
231 | |||
155 | /* | 232 | /* |
156 | * Give demultiplex thread up to 10 seconds to reconnect, should be | 233 | * Give demultiplex thread up to 10 seconds to each target available for |
157 | * greater than cifs socket timeout which is 7 seconds | 234 | * reconnect -- should be greater than cifs socket timeout which is 7 |
235 | * seconds. | ||
158 | */ | 236 | */ |
159 | while (server->tcpStatus == CifsNeedReconnect) { | 237 | while (server->tcpStatus == CifsNeedReconnect) { |
160 | rc = wait_event_interruptible_timeout(server->response_q, | 238 | rc = wait_event_interruptible_timeout(server->response_q, |
@@ -170,6 +248,9 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) | |||
170 | if (server->tcpStatus != CifsNeedReconnect) | 248 | if (server->tcpStatus != CifsNeedReconnect) |
171 | break; | 249 | break; |
172 | 250 | ||
251 | if (--retries) | ||
252 | continue; | ||
253 | |||
173 | /* | 254 | /* |
174 | * on "soft" mounts we wait once. Hard mounts keep | 255 | * on "soft" mounts we wait once. Hard mounts keep |
175 | * retrying until process is killed or server comes | 256 | * retrying until process is killed or server comes |
@@ -179,6 +260,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) | |||
179 | cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n"); | 260 | cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n"); |
180 | return -EHOSTDOWN; | 261 | return -EHOSTDOWN; |
181 | } | 262 | } |
263 | retries = server->nr_targets; | ||
182 | } | 264 | } |
183 | 265 | ||
184 | if (!ses->need_reconnect && !tcon->need_reconnect) | 266 | if (!ses->need_reconnect && !tcon->need_reconnect) |
@@ -214,7 +296,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) | |||
214 | } | 296 | } |
215 | 297 | ||
216 | cifs_mark_open_files_invalid(tcon); | 298 | cifs_mark_open_files_invalid(tcon); |
217 | rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage); | 299 | rc = __cifs_reconnect_tcon(nls_codepage, tcon); |
218 | mutex_unlock(&ses->session_mutex); | 300 | mutex_unlock(&ses->session_mutex); |
219 | cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); | 301 | cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); |
220 | 302 | ||
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 6f24f129a751..69b9d5606eba 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -56,6 +56,11 @@ | |||
56 | #include "fscache.h" | 56 | #include "fscache.h" |
57 | #include "smb2proto.h" | 57 | #include "smb2proto.h" |
58 | #include "smbdirect.h" | 58 | #include "smbdirect.h" |
59 | #include "dns_resolve.h" | ||
60 | #include "cifsfs.h" | ||
61 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
62 | #include "dfs_cache.h" | ||
63 | #endif | ||
59 | 64 | ||
60 | extern mempool_t *cifs_req_poolp; | 65 | extern mempool_t *cifs_req_poolp; |
61 | extern bool disable_legacy_dialects; | 66 | extern bool disable_legacy_dialects; |
@@ -304,6 +309,7 @@ static const match_table_t cifs_smb_version_tokens = { | |||
304 | { Smb_21, SMB21_VERSION_STRING }, | 309 | { Smb_21, SMB21_VERSION_STRING }, |
305 | { Smb_30, SMB30_VERSION_STRING }, | 310 | { Smb_30, SMB30_VERSION_STRING }, |
306 | { Smb_302, SMB302_VERSION_STRING }, | 311 | { Smb_302, SMB302_VERSION_STRING }, |
312 | { Smb_302, ALT_SMB302_VERSION_STRING }, | ||
307 | { Smb_311, SMB311_VERSION_STRING }, | 313 | { Smb_311, SMB311_VERSION_STRING }, |
308 | { Smb_311, ALT_SMB311_VERSION_STRING }, | 314 | { Smb_311, ALT_SMB311_VERSION_STRING }, |
309 | { Smb_3any, SMB3ANY_VERSION_STRING }, | 315 | { Smb_3any, SMB3ANY_VERSION_STRING }, |
@@ -317,6 +323,131 @@ static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); | |||
317 | static void cifs_prune_tlinks(struct work_struct *work); | 323 | static void cifs_prune_tlinks(struct work_struct *work); |
318 | static int cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, | 324 | static int cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, |
319 | const char *devname, bool is_smb3); | 325 | const char *devname, bool is_smb3); |
326 | static char *extract_hostname(const char *unc); | ||
327 | |||
328 | /* | ||
329 | * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may | ||
330 | * get their ip addresses changed at some point. | ||
331 | * | ||
332 | * This should be called with server->srv_mutex held. | ||
333 | */ | ||
334 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
335 | static int reconn_set_ipaddr(struct TCP_Server_Info *server) | ||
336 | { | ||
337 | int rc; | ||
338 | int len; | ||
339 | char *unc, *ipaddr = NULL; | ||
340 | |||
341 | if (!server->hostname) | ||
342 | return -EINVAL; | ||
343 | |||
344 | len = strlen(server->hostname) + 3; | ||
345 | |||
346 | unc = kmalloc(len, GFP_KERNEL); | ||
347 | if (!unc) { | ||
348 | cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__); | ||
349 | return -ENOMEM; | ||
350 | } | ||
351 | snprintf(unc, len, "\\\\%s", server->hostname); | ||
352 | |||
353 | rc = dns_resolve_server_name_to_ip(unc, &ipaddr); | ||
354 | kfree(unc); | ||
355 | |||
356 | if (rc < 0) { | ||
357 | cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n", | ||
358 | __func__, server->hostname, rc); | ||
359 | return rc; | ||
360 | } | ||
361 | |||
362 | rc = cifs_convert_address((struct sockaddr *)&server->dstaddr, ipaddr, | ||
363 | strlen(ipaddr)); | ||
364 | kfree(ipaddr); | ||
365 | |||
366 | return !rc ? -1 : 0; | ||
367 | } | ||
368 | #else | ||
369 | static inline int reconn_set_ipaddr(struct TCP_Server_Info *server) | ||
370 | { | ||
371 | return 0; | ||
372 | } | ||
373 | #endif | ||
374 | |||
375 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
376 | struct super_cb_data { | ||
377 | struct TCP_Server_Info *server; | ||
378 | struct cifs_sb_info *cifs_sb; | ||
379 | }; | ||
380 | |||
381 | /* These functions must be called with server->srv_mutex held */ | ||
382 | |||
383 | static void super_cb(struct super_block *sb, void *arg) | ||
384 | { | ||
385 | struct super_cb_data *d = arg; | ||
386 | struct cifs_sb_info *cifs_sb; | ||
387 | struct cifs_tcon *tcon; | ||
388 | |||
389 | if (d->cifs_sb) | ||
390 | return; | ||
391 | |||
392 | cifs_sb = CIFS_SB(sb); | ||
393 | tcon = cifs_sb_master_tcon(cifs_sb); | ||
394 | if (tcon->ses->server == d->server) | ||
395 | d->cifs_sb = cifs_sb; | ||
396 | } | ||
397 | |||
398 | static inline struct cifs_sb_info * | ||
399 | find_super_by_tcp(struct TCP_Server_Info *server) | ||
400 | { | ||
401 | struct super_cb_data d = { | ||
402 | .server = server, | ||
403 | .cifs_sb = NULL, | ||
404 | }; | ||
405 | |||
406 | iterate_supers_type(&cifs_fs_type, super_cb, &d); | ||
407 | return d.cifs_sb ? d.cifs_sb : ERR_PTR(-ENOENT); | ||
408 | } | ||
409 | |||
410 | static void reconn_inval_dfs_target(struct TCP_Server_Info *server, | ||
411 | struct cifs_sb_info *cifs_sb, | ||
412 | struct dfs_cache_tgt_list *tgt_list, | ||
413 | struct dfs_cache_tgt_iterator **tgt_it) | ||
414 | { | ||
415 | const char *name; | ||
416 | |||
417 | if (!cifs_sb || !cifs_sb->origin_fullpath || !tgt_list || | ||
418 | !server->nr_targets) | ||
419 | return; | ||
420 | |||
421 | if (!*tgt_it) { | ||
422 | *tgt_it = dfs_cache_get_tgt_iterator(tgt_list); | ||
423 | } else { | ||
424 | *tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it); | ||
425 | if (!*tgt_it) | ||
426 | *tgt_it = dfs_cache_get_tgt_iterator(tgt_list); | ||
427 | } | ||
428 | |||
429 | cifs_dbg(FYI, "%s: UNC: %s\n", __func__, cifs_sb->origin_fullpath); | ||
430 | |||
431 | name = dfs_cache_get_tgt_name(*tgt_it); | ||
432 | |||
433 | kfree(server->hostname); | ||
434 | |||
435 | server->hostname = extract_hostname(name); | ||
436 | if (!server->hostname) { | ||
437 | cifs_dbg(FYI, "%s: failed to extract hostname from target: %d\n", | ||
438 | __func__, -ENOMEM); | ||
439 | } | ||
440 | } | ||
441 | |||
442 | static inline int reconn_setup_dfs_targets(struct cifs_sb_info *cifs_sb, | ||
443 | struct dfs_cache_tgt_list *tl, | ||
444 | struct dfs_cache_tgt_iterator **it) | ||
445 | { | ||
446 | if (!cifs_sb->origin_fullpath) | ||
447 | return -EOPNOTSUPP; | ||
448 | return dfs_cache_noreq_find(cifs_sb->origin_fullpath + 1, NULL, tl); | ||
449 | } | ||
450 | #endif | ||
320 | 451 | ||
321 | /* | 452 | /* |
322 | * cifs tcp session reconnection | 453 | * cifs tcp session reconnection |
@@ -335,8 +466,33 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
335 | struct cifs_tcon *tcon; | 466 | struct cifs_tcon *tcon; |
336 | struct mid_q_entry *mid_entry; | 467 | struct mid_q_entry *mid_entry; |
337 | struct list_head retry_list; | 468 | struct list_head retry_list; |
469 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
470 | struct cifs_sb_info *cifs_sb = NULL; | ||
471 | struct dfs_cache_tgt_list tgt_list = {0}; | ||
472 | struct dfs_cache_tgt_iterator *tgt_it = NULL; | ||
473 | #endif | ||
338 | 474 | ||
339 | spin_lock(&GlobalMid_Lock); | 475 | spin_lock(&GlobalMid_Lock); |
476 | server->nr_targets = 1; | ||
477 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
478 | cifs_sb = find_super_by_tcp(server); | ||
479 | if (IS_ERR(cifs_sb)) { | ||
480 | rc = PTR_ERR(cifs_sb); | ||
481 | cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n", | ||
482 | __func__, rc); | ||
483 | cifs_sb = NULL; | ||
484 | } else { | ||
485 | rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list, &tgt_it); | ||
486 | if (rc) { | ||
487 | cifs_dbg(VFS, "%s: no target servers for DFS failover\n", | ||
488 | __func__); | ||
489 | } else { | ||
490 | server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list); | ||
491 | } | ||
492 | } | ||
493 | cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__, | ||
494 | server->nr_targets); | ||
495 | #endif | ||
340 | if (server->tcpStatus == CifsExiting) { | 496 | if (server->tcpStatus == CifsExiting) { |
341 | /* the demux thread will exit normally | 497 | /* the demux thread will exit normally |
342 | next time through the loop */ | 498 | next time through the loop */ |
@@ -410,14 +566,27 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
410 | do { | 566 | do { |
411 | try_to_freeze(); | 567 | try_to_freeze(); |
412 | 568 | ||
413 | /* we should try only the port we connected to before */ | ||
414 | mutex_lock(&server->srv_mutex); | 569 | mutex_lock(&server->srv_mutex); |
570 | /* | ||
571 | * Set up next DFS target server (if any) for reconnect. If DFS | ||
572 | * feature is disabled, then we will retry last server we | ||
573 | * connected to before. | ||
574 | */ | ||
415 | if (cifs_rdma_enabled(server)) | 575 | if (cifs_rdma_enabled(server)) |
416 | rc = smbd_reconnect(server); | 576 | rc = smbd_reconnect(server); |
417 | else | 577 | else |
418 | rc = generic_ip_connect(server); | 578 | rc = generic_ip_connect(server); |
419 | if (rc) { | 579 | if (rc) { |
420 | cifs_dbg(FYI, "reconnect error %d\n", rc); | 580 | cifs_dbg(FYI, "reconnect error %d\n", rc); |
581 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
582 | reconn_inval_dfs_target(server, cifs_sb, &tgt_list, | ||
583 | &tgt_it); | ||
584 | #endif | ||
585 | rc = reconn_set_ipaddr(server); | ||
586 | if (rc) { | ||
587 | cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", | ||
588 | __func__, rc); | ||
589 | } | ||
421 | mutex_unlock(&server->srv_mutex); | 590 | mutex_unlock(&server->srv_mutex); |
422 | msleep(3000); | 591 | msleep(3000); |
423 | } else { | 592 | } else { |
@@ -430,6 +599,22 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
430 | } | 599 | } |
431 | } while (server->tcpStatus == CifsNeedReconnect); | 600 | } while (server->tcpStatus == CifsNeedReconnect); |
432 | 601 | ||
602 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
603 | if (tgt_it) { | ||
604 | rc = dfs_cache_noreq_update_tgthint(cifs_sb->origin_fullpath + 1, | ||
605 | tgt_it); | ||
606 | if (rc) { | ||
607 | cifs_dbg(VFS, "%s: failed to update DFS target hint: rc = %d\n", | ||
608 | __func__, rc); | ||
609 | } | ||
610 | rc = dfs_cache_update_vol(cifs_sb->origin_fullpath, server); | ||
611 | if (rc) { | ||
612 | cifs_dbg(VFS, "%s: failed to update vol info in DFS cache: rc = %d\n", | ||
613 | __func__, rc); | ||
614 | } | ||
615 | dfs_cache_free_tgts(&tgt_list); | ||
616 | } | ||
617 | #endif | ||
433 | if (server->tcpStatus == CifsNeedNegotiate) | 618 | if (server->tcpStatus == CifsNeedNegotiate) |
434 | mod_delayed_work(cifsiod_wq, &server->echo, 0); | 619 | mod_delayed_work(cifsiod_wq, &server->echo, 0); |
435 | 620 | ||
@@ -1043,7 +1228,12 @@ extract_hostname(const char *unc) | |||
1043 | 1228 | ||
1044 | /* skip double chars at beginning of string */ | 1229 | /* skip double chars at beginning of string */ |
1045 | /* BB: check validity of these bytes? */ | 1230 | /* BB: check validity of these bytes? */ |
1046 | src = unc + 2; | 1231 | if (strlen(unc) < 3) |
1232 | return ERR_PTR(-EINVAL); | ||
1233 | for (src = unc; *src && *src == '\\'; src++) | ||
1234 | ; | ||
1235 | if (!*src) | ||
1236 | return ERR_PTR(-EINVAL); | ||
1047 | 1237 | ||
1048 | /* delimiter between hostname and sharename is always '\\' now */ | 1238 | /* delimiter between hostname and sharename is always '\\' now */ |
1049 | delim = strchr(src, '\\'); | 1239 | delim = strchr(src, '\\'); |
@@ -1827,7 +2017,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, | |||
1827 | vol->password = NULL; | 2017 | vol->password = NULL; |
1828 | break; | 2018 | break; |
1829 | } | 2019 | } |
1830 | /* Yes it is. Drop down to Opt_pass below.*/ | 2020 | /* Fallthrough - to Opt_pass below.*/ |
1831 | case Opt_pass: | 2021 | case Opt_pass: |
1832 | /* Obtain the value string */ | 2022 | /* Obtain the value string */ |
1833 | value = strchr(data, '='); | 2023 | value = strchr(data, '='); |
@@ -2289,7 +2479,7 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol) | |||
2289 | return 1; | 2479 | return 1; |
2290 | } | 2480 | } |
2291 | 2481 | ||
2292 | static struct TCP_Server_Info * | 2482 | struct TCP_Server_Info * |
2293 | cifs_find_tcp_session(struct smb_vol *vol) | 2483 | cifs_find_tcp_session(struct smb_vol *vol) |
2294 | { | 2484 | { |
2295 | struct TCP_Server_Info *server; | 2485 | struct TCP_Server_Info *server; |
@@ -2461,6 +2651,8 @@ smbd_connected: | |||
2461 | } | 2651 | } |
2462 | tcp_ses->tcpStatus = CifsNeedNegotiate; | 2652 | tcp_ses->tcpStatus = CifsNeedNegotiate; |
2463 | 2653 | ||
2654 | tcp_ses->nr_targets = 1; | ||
2655 | |||
2464 | /* thread spawned, put it on the list */ | 2656 | /* thread spawned, put it on the list */ |
2465 | spin_lock(&cifs_tcp_ses_lock); | 2657 | spin_lock(&cifs_tcp_ses_lock); |
2466 | list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); | 2658 | list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); |
@@ -3256,25 +3448,6 @@ out: | |||
3256 | return rc; | 3448 | return rc; |
3257 | } | 3449 | } |
3258 | 3450 | ||
3259 | int | ||
3260 | get_dfs_path(const unsigned int xid, struct cifs_ses *ses, const char *old_path, | ||
3261 | const struct nls_table *nls_codepage, unsigned int *num_referrals, | ||
3262 | struct dfs_info3_param **referrals, int remap) | ||
3263 | { | ||
3264 | int rc = 0; | ||
3265 | |||
3266 | if (!ses->server->ops->get_dfs_refer) | ||
3267 | return -ENOSYS; | ||
3268 | |||
3269 | *num_referrals = 0; | ||
3270 | *referrals = NULL; | ||
3271 | |||
3272 | rc = ses->server->ops->get_dfs_refer(xid, ses, old_path, | ||
3273 | referrals, num_referrals, | ||
3274 | nls_codepage, remap); | ||
3275 | return rc; | ||
3276 | } | ||
3277 | |||
3278 | #ifdef CONFIG_DEBUG_LOCK_ALLOC | 3451 | #ifdef CONFIG_DEBUG_LOCK_ALLOC |
3279 | static struct lock_class_key cifs_key[2]; | 3452 | static struct lock_class_key cifs_key[2]; |
3280 | static struct lock_class_key cifs_slock_key[2]; | 3453 | static struct lock_class_key cifs_slock_key[2]; |
@@ -3746,8 +3919,8 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, | |||
3746 | return 0; | 3919 | return 0; |
3747 | } | 3920 | } |
3748 | 3921 | ||
3749 | static void | 3922 | void |
3750 | cleanup_volume_info_contents(struct smb_vol *volume_info) | 3923 | cifs_cleanup_volume_info_contents(struct smb_vol *volume_info) |
3751 | { | 3924 | { |
3752 | kfree(volume_info->username); | 3925 | kfree(volume_info->username); |
3753 | kzfree(volume_info->password); | 3926 | kzfree(volume_info->password); |
@@ -3762,10 +3935,136 @@ cifs_cleanup_volume_info(struct smb_vol *volume_info) | |||
3762 | { | 3935 | { |
3763 | if (!volume_info) | 3936 | if (!volume_info) |
3764 | return; | 3937 | return; |
3765 | cleanup_volume_info_contents(volume_info); | 3938 | cifs_cleanup_volume_info_contents(volume_info); |
3766 | kfree(volume_info); | 3939 | kfree(volume_info); |
3767 | } | 3940 | } |
3768 | 3941 | ||
3942 | /* Release all succeed connections */ | ||
3943 | static inline void mount_put_conns(struct cifs_sb_info *cifs_sb, | ||
3944 | unsigned int xid, | ||
3945 | struct TCP_Server_Info *server, | ||
3946 | struct cifs_ses *ses, struct cifs_tcon *tcon) | ||
3947 | { | ||
3948 | int rc = 0; | ||
3949 | |||
3950 | if (tcon) | ||
3951 | cifs_put_tcon(tcon); | ||
3952 | else if (ses) | ||
3953 | cifs_put_smb_ses(ses); | ||
3954 | else if (server) | ||
3955 | cifs_put_tcp_session(server, 0); | ||
3956 | cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; | ||
3957 | free_xid(xid); | ||
3958 | } | ||
3959 | |||
3960 | /* Get connections for tcp, ses and tcon */ | ||
3961 | static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, | ||
3962 | unsigned int *xid, | ||
3963 | struct TCP_Server_Info **nserver, | ||
3964 | struct cifs_ses **nses, struct cifs_tcon **ntcon) | ||
3965 | { | ||
3966 | int rc = 0; | ||
3967 | struct TCP_Server_Info *server; | ||
3968 | struct cifs_ses *ses; | ||
3969 | struct cifs_tcon *tcon; | ||
3970 | |||
3971 | *nserver = NULL; | ||
3972 | *nses = NULL; | ||
3973 | *ntcon = NULL; | ||
3974 | |||
3975 | *xid = get_xid(); | ||
3976 | |||
3977 | /* get a reference to a tcp session */ | ||
3978 | server = cifs_get_tcp_session(vol); | ||
3979 | if (IS_ERR(server)) { | ||
3980 | rc = PTR_ERR(server); | ||
3981 | return rc; | ||
3982 | } | ||
3983 | |||
3984 | *nserver = server; | ||
3985 | |||
3986 | if ((vol->max_credits < 20) || (vol->max_credits > 60000)) | ||
3987 | server->max_credits = SMB2_MAX_CREDITS_AVAILABLE; | ||
3988 | else | ||
3989 | server->max_credits = vol->max_credits; | ||
3990 | |||
3991 | /* get a reference to a SMB session */ | ||
3992 | ses = cifs_get_smb_ses(server, vol); | ||
3993 | if (IS_ERR(ses)) { | ||
3994 | rc = PTR_ERR(ses); | ||
3995 | return rc; | ||
3996 | } | ||
3997 | |||
3998 | *nses = ses; | ||
3999 | |||
4000 | if ((vol->persistent == true) && (!(ses->server->capabilities & | ||
4001 | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) { | ||
4002 | cifs_dbg(VFS, "persistent handles not supported by server\n"); | ||
4003 | return -EOPNOTSUPP; | ||
4004 | } | ||
4005 | |||
4006 | /* search for existing tcon to this server share */ | ||
4007 | tcon = cifs_get_tcon(ses, vol); | ||
4008 | if (IS_ERR(tcon)) { | ||
4009 | rc = PTR_ERR(tcon); | ||
4010 | return rc; | ||
4011 | } | ||
4012 | |||
4013 | *ntcon = tcon; | ||
4014 | |||
4015 | /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ | ||
4016 | if (tcon->posix_extensions) | ||
4017 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; | ||
4018 | |||
4019 | /* tell server which Unix caps we support */ | ||
4020 | if (cap_unix(tcon->ses)) { | ||
4021 | /* | ||
4022 | * reset of caps checks mount to see if unix extensions disabled | ||
4023 | * for just this mount. | ||
4024 | */ | ||
4025 | reset_cifs_unix_caps(*xid, tcon, cifs_sb, vol); | ||
4026 | if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && | ||
4027 | (le64_to_cpu(tcon->fsUnixInfo.Capability) & | ||
4028 | CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) | ||
4029 | return -EACCES; | ||
4030 | } else | ||
4031 | tcon->unix_ext = 0; /* server does not support them */ | ||
4032 | |||
4033 | /* do not care if a following call succeed - informational */ | ||
4034 | if (!tcon->pipe && server->ops->qfs_tcon) | ||
4035 | server->ops->qfs_tcon(*xid, tcon); | ||
4036 | |||
4037 | cifs_sb->wsize = server->ops->negotiate_wsize(tcon, vol); | ||
4038 | cifs_sb->rsize = server->ops->negotiate_rsize(tcon, vol); | ||
4039 | |||
4040 | return 0; | ||
4041 | } | ||
4042 | |||
4043 | static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, | ||
4044 | struct cifs_tcon *tcon) | ||
4045 | { | ||
4046 | struct tcon_link *tlink; | ||
4047 | |||
4048 | /* hang the tcon off of the superblock */ | ||
4049 | tlink = kzalloc(sizeof(*tlink), GFP_KERNEL); | ||
4050 | if (tlink == NULL) | ||
4051 | return -ENOMEM; | ||
4052 | |||
4053 | tlink->tl_uid = ses->linux_uid; | ||
4054 | tlink->tl_tcon = tcon; | ||
4055 | tlink->tl_time = jiffies; | ||
4056 | set_bit(TCON_LINK_MASTER, &tlink->tl_flags); | ||
4057 | set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); | ||
4058 | |||
4059 | cifs_sb->master_tlink = tlink; | ||
4060 | spin_lock(&cifs_sb->tlink_tree_lock); | ||
4061 | tlink_rb_insert(&cifs_sb->tlink_tree, tlink); | ||
4062 | spin_unlock(&cifs_sb->tlink_tree_lock); | ||
4063 | |||
4064 | queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, | ||
4065 | TLINK_IDLE_EXPIRE); | ||
4066 | return 0; | ||
4067 | } | ||
3769 | 4068 | ||
3770 | #ifdef CONFIG_CIFS_DFS_UPCALL | 4069 | #ifdef CONFIG_CIFS_DFS_UPCALL |
3771 | /* | 4070 | /* |
@@ -3774,10 +4073,11 @@ cifs_cleanup_volume_info(struct smb_vol *volume_info) | |||
3774 | */ | 4073 | */ |
3775 | static char * | 4074 | static char * |
3776 | build_unc_path_to_root(const struct smb_vol *vol, | 4075 | build_unc_path_to_root(const struct smb_vol *vol, |
3777 | const struct cifs_sb_info *cifs_sb) | 4076 | const struct cifs_sb_info *cifs_sb, bool useppath) |
3778 | { | 4077 | { |
3779 | char *full_path, *pos; | 4078 | char *full_path, *pos; |
3780 | unsigned int pplen = vol->prepath ? strlen(vol->prepath) + 1 : 0; | 4079 | unsigned int pplen = useppath && vol->prepath ? |
4080 | strlen(vol->prepath) + 1 : 0; | ||
3781 | unsigned int unc_len = strnlen(vol->UNC, MAX_TREE_SIZE + 1); | 4081 | unsigned int unc_len = strnlen(vol->UNC, MAX_TREE_SIZE + 1); |
3782 | 4082 | ||
3783 | full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL); | 4083 | full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL); |
@@ -3799,8 +4099,9 @@ build_unc_path_to_root(const struct smb_vol *vol, | |||
3799 | return full_path; | 4099 | return full_path; |
3800 | } | 4100 | } |
3801 | 4101 | ||
3802 | /* | 4102 | /** |
3803 | * Perform a dfs referral query for a share and (optionally) prefix | 4103 | * expand_dfs_referral - Perform a dfs referral query and update the cifs_sb |
4104 | * | ||
3804 | * | 4105 | * |
3805 | * If a referral is found, cifs_sb->mountdata will be (re-)allocated | 4106 | * If a referral is found, cifs_sb->mountdata will be (re-)allocated |
3806 | * to a string containing updated options for the submount. Otherwise it | 4107 | * to a string containing updated options for the submount. Otherwise it |
@@ -3815,39 +4116,36 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, | |||
3815 | int check_prefix) | 4116 | int check_prefix) |
3816 | { | 4117 | { |
3817 | int rc; | 4118 | int rc; |
3818 | unsigned int num_referrals = 0; | 4119 | struct dfs_info3_param referral = {0}; |
3819 | struct dfs_info3_param *referrals = NULL; | ||
3820 | char *full_path = NULL, *ref_path = NULL, *mdata = NULL; | 4120 | char *full_path = NULL, *ref_path = NULL, *mdata = NULL; |
3821 | 4121 | ||
3822 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) | 4122 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) |
3823 | return -EREMOTE; | 4123 | return -EREMOTE; |
3824 | 4124 | ||
3825 | full_path = build_unc_path_to_root(volume_info, cifs_sb); | 4125 | full_path = build_unc_path_to_root(volume_info, cifs_sb, true); |
3826 | if (IS_ERR(full_path)) | 4126 | if (IS_ERR(full_path)) |
3827 | return PTR_ERR(full_path); | 4127 | return PTR_ERR(full_path); |
3828 | 4128 | ||
3829 | /* For DFS paths, skip the first '\' of the UNC */ | 4129 | /* For DFS paths, skip the first '\' of the UNC */ |
3830 | ref_path = check_prefix ? full_path + 1 : volume_info->UNC + 1; | 4130 | ref_path = check_prefix ? full_path + 1 : volume_info->UNC + 1; |
3831 | 4131 | ||
3832 | rc = get_dfs_path(xid, ses, ref_path, cifs_sb->local_nls, | 4132 | rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), |
3833 | &num_referrals, &referrals, cifs_remap(cifs_sb)); | 4133 | ref_path, &referral, NULL); |
3834 | 4134 | if (!rc) { | |
3835 | if (!rc && num_referrals > 0) { | ||
3836 | char *fake_devname = NULL; | 4135 | char *fake_devname = NULL; |
3837 | 4136 | ||
3838 | mdata = cifs_compose_mount_options(cifs_sb->mountdata, | 4137 | mdata = cifs_compose_mount_options(cifs_sb->mountdata, |
3839 | full_path + 1, referrals, | 4138 | full_path + 1, &referral, |
3840 | &fake_devname); | 4139 | &fake_devname); |
3841 | 4140 | free_dfs_info_param(&referral); | |
3842 | free_dfs_info_array(referrals, num_referrals); | ||
3843 | 4141 | ||
3844 | if (IS_ERR(mdata)) { | 4142 | if (IS_ERR(mdata)) { |
3845 | rc = PTR_ERR(mdata); | 4143 | rc = PTR_ERR(mdata); |
3846 | mdata = NULL; | 4144 | mdata = NULL; |
3847 | } else { | 4145 | } else { |
3848 | cleanup_volume_info_contents(volume_info); | 4146 | cifs_cleanup_volume_info_contents(volume_info); |
3849 | rc = cifs_setup_volume_info(volume_info, mdata, | 4147 | rc = cifs_setup_volume_info(volume_info, mdata, |
3850 | fake_devname, false); | 4148 | fake_devname, false); |
3851 | } | 4149 | } |
3852 | kfree(fake_devname); | 4150 | kfree(fake_devname); |
3853 | kfree(cifs_sb->mountdata); | 4151 | kfree(cifs_sb->mountdata); |
@@ -3856,6 +4154,143 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, | |||
3856 | kfree(full_path); | 4154 | kfree(full_path); |
3857 | return rc; | 4155 | return rc; |
3858 | } | 4156 | } |
4157 | |||
4158 | static inline int get_next_dfs_tgt(const char *path, | ||
4159 | struct dfs_cache_tgt_list *tgt_list, | ||
4160 | struct dfs_cache_tgt_iterator **tgt_it) | ||
4161 | { | ||
4162 | if (!*tgt_it) | ||
4163 | *tgt_it = dfs_cache_get_tgt_iterator(tgt_list); | ||
4164 | else | ||
4165 | *tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it); | ||
4166 | return !*tgt_it ? -EHOSTDOWN : 0; | ||
4167 | } | ||
4168 | |||
4169 | static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it, | ||
4170 | struct smb_vol *fake_vol, struct smb_vol *vol) | ||
4171 | { | ||
4172 | const char *tgt = dfs_cache_get_tgt_name(tgt_it); | ||
4173 | int len = strlen(tgt) + 2; | ||
4174 | char *new_unc; | ||
4175 | |||
4176 | new_unc = kmalloc(len, GFP_KERNEL); | ||
4177 | if (!new_unc) | ||
4178 | return -ENOMEM; | ||
4179 | snprintf(new_unc, len, "\\%s", tgt); | ||
4180 | |||
4181 | kfree(vol->UNC); | ||
4182 | vol->UNC = new_unc; | ||
4183 | |||
4184 | if (fake_vol->prepath) { | ||
4185 | kfree(vol->prepath); | ||
4186 | vol->prepath = fake_vol->prepath; | ||
4187 | fake_vol->prepath = NULL; | ||
4188 | } | ||
4189 | memcpy(&vol->dstaddr, &fake_vol->dstaddr, sizeof(vol->dstaddr)); | ||
4190 | |||
4191 | return 0; | ||
4192 | } | ||
4193 | |||
4194 | static int setup_dfs_tgt_conn(const char *path, | ||
4195 | const struct dfs_cache_tgt_iterator *tgt_it, | ||
4196 | struct cifs_sb_info *cifs_sb, | ||
4197 | struct smb_vol *vol, | ||
4198 | unsigned int *xid, | ||
4199 | struct TCP_Server_Info **server, | ||
4200 | struct cifs_ses **ses, | ||
4201 | struct cifs_tcon **tcon) | ||
4202 | { | ||
4203 | int rc; | ||
4204 | struct dfs_info3_param ref = {0}; | ||
4205 | char *mdata = NULL, *fake_devname = NULL; | ||
4206 | struct smb_vol fake_vol = {0}; | ||
4207 | |||
4208 | cifs_dbg(FYI, "%s: dfs path: %s\n", __func__, path); | ||
4209 | |||
4210 | rc = dfs_cache_get_tgt_referral(path, tgt_it, &ref); | ||
4211 | if (rc) | ||
4212 | return rc; | ||
4213 | |||
4214 | mdata = cifs_compose_mount_options(cifs_sb->mountdata, path, &ref, | ||
4215 | &fake_devname); | ||
4216 | free_dfs_info_param(&ref); | ||
4217 | |||
4218 | if (IS_ERR(mdata)) { | ||
4219 | rc = PTR_ERR(mdata); | ||
4220 | mdata = NULL; | ||
4221 | } else { | ||
4222 | cifs_dbg(FYI, "%s: fake_devname: %s\n", __func__, fake_devname); | ||
4223 | rc = cifs_setup_volume_info(&fake_vol, mdata, fake_devname, | ||
4224 | false); | ||
4225 | } | ||
4226 | kfree(mdata); | ||
4227 | kfree(fake_devname); | ||
4228 | |||
4229 | if (!rc) { | ||
4230 | /* | ||
4231 | * We use a 'fake_vol' here because we need pass it down to the | ||
4232 | * mount_{get,put} functions to test connection against new DFS | ||
4233 | * targets. | ||
4234 | */ | ||
4235 | mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon); | ||
4236 | rc = mount_get_conns(&fake_vol, cifs_sb, xid, server, ses, | ||
4237 | tcon); | ||
4238 | if (!rc) { | ||
4239 | /* | ||
4240 | * We were able to connect to new target server. | ||
4241 | * Update current volume info with new target server. | ||
4242 | */ | ||
4243 | rc = update_vol_info(tgt_it, &fake_vol, vol); | ||
4244 | } | ||
4245 | } | ||
4246 | cifs_cleanup_volume_info_contents(&fake_vol); | ||
4247 | return rc; | ||
4248 | } | ||
4249 | |||
4250 | static int mount_do_dfs_failover(const char *path, | ||
4251 | struct cifs_sb_info *cifs_sb, | ||
4252 | struct smb_vol *vol, | ||
4253 | struct cifs_ses *root_ses, | ||
4254 | unsigned int *xid, | ||
4255 | struct TCP_Server_Info **server, | ||
4256 | struct cifs_ses **ses, | ||
4257 | struct cifs_tcon **tcon) | ||
4258 | { | ||
4259 | int rc; | ||
4260 | struct dfs_cache_tgt_list tgt_list; | ||
4261 | struct dfs_cache_tgt_iterator *tgt_it = NULL; | ||
4262 | |||
4263 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) | ||
4264 | return -EOPNOTSUPP; | ||
4265 | |||
4266 | rc = dfs_cache_noreq_find(path, NULL, &tgt_list); | ||
4267 | if (rc) | ||
4268 | return rc; | ||
4269 | |||
4270 | for (;;) { | ||
4271 | /* Get next DFS target server - if any */ | ||
4272 | rc = get_next_dfs_tgt(path, &tgt_list, &tgt_it); | ||
4273 | if (rc) | ||
4274 | break; | ||
4275 | /* Connect to next DFS target */ | ||
4276 | rc = setup_dfs_tgt_conn(path, tgt_it, cifs_sb, vol, xid, server, | ||
4277 | ses, tcon); | ||
4278 | if (!rc || rc == -EACCES || rc == -EOPNOTSUPP) | ||
4279 | break; | ||
4280 | } | ||
4281 | if (!rc) { | ||
4282 | /* | ||
4283 | * Update DFS target hint in DFS referral cache with the target | ||
4284 | * server we successfully reconnected to. | ||
4285 | */ | ||
4286 | rc = dfs_cache_update_tgthint(*xid, root_ses ? root_ses : *ses, | ||
4287 | cifs_sb->local_nls, | ||
4288 | cifs_remap(cifs_sb), path, | ||
4289 | tgt_it); | ||
4290 | } | ||
4291 | dfs_cache_free_tgts(&tgt_list); | ||
4292 | return rc; | ||
4293 | } | ||
3859 | #endif | 4294 | #endif |
3860 | 4295 | ||
3861 | static int | 4296 | static int |
@@ -3954,107 +4389,108 @@ cifs_are_all_path_components_accessible(struct TCP_Server_Info *server, | |||
3954 | return rc; | 4389 | return rc; |
3955 | } | 4390 | } |
3956 | 4391 | ||
3957 | int | 4392 | /* |
3958 | cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info) | 4393 | * Check if path is remote (e.g. a DFS share). Return -EREMOTE if it is, |
4394 | * otherwise 0. | ||
4395 | */ | ||
4396 | static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb_vol *vol, | ||
4397 | const unsigned int xid, | ||
4398 | struct TCP_Server_Info *server, | ||
4399 | struct cifs_tcon *tcon) | ||
3959 | { | 4400 | { |
3960 | int rc; | 4401 | int rc; |
3961 | unsigned int xid; | 4402 | char *full_path; |
3962 | struct cifs_ses *ses; | ||
3963 | struct cifs_tcon *tcon; | ||
3964 | struct TCP_Server_Info *server; | ||
3965 | char *full_path; | ||
3966 | struct tcon_link *tlink; | ||
3967 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
3968 | int referral_walks_count = 0; | ||
3969 | #endif | ||
3970 | 4403 | ||
3971 | #ifdef CONFIG_CIFS_DFS_UPCALL | 4404 | if (!server->ops->is_path_accessible) |
3972 | try_mount_again: | 4405 | return -EOPNOTSUPP; |
3973 | /* cleanup activities if we're chasing a referral */ | ||
3974 | if (referral_walks_count) { | ||
3975 | if (tcon) | ||
3976 | cifs_put_tcon(tcon); | ||
3977 | else if (ses) | ||
3978 | cifs_put_smb_ses(ses); | ||
3979 | 4406 | ||
3980 | cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; | 4407 | /* |
3981 | 4408 | * cifs_build_path_to_root works only when we have a valid tcon | |
3982 | free_xid(xid); | 4409 | */ |
3983 | } | 4410 | full_path = cifs_build_path_to_root(vol, cifs_sb, tcon, |
3984 | #endif | 4411 | tcon->Flags & SMB_SHARE_IS_IN_DFS); |
3985 | rc = 0; | 4412 | if (full_path == NULL) |
3986 | tcon = NULL; | 4413 | return -ENOMEM; |
3987 | ses = NULL; | ||
3988 | server = NULL; | ||
3989 | full_path = NULL; | ||
3990 | tlink = NULL; | ||
3991 | 4414 | ||
3992 | xid = get_xid(); | 4415 | cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); |
3993 | 4416 | ||
3994 | /* get a reference to a tcp session */ | 4417 | rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, |
3995 | server = cifs_get_tcp_session(volume_info); | 4418 | full_path); |
3996 | if (IS_ERR(server)) { | 4419 | if (rc != 0 && rc != -EREMOTE) { |
3997 | rc = PTR_ERR(server); | 4420 | kfree(full_path); |
3998 | goto out; | 4421 | return rc; |
3999 | } | ||
4000 | if ((volume_info->max_credits < 20) || | ||
4001 | (volume_info->max_credits > 60000)) | ||
4002 | server->max_credits = SMB2_MAX_CREDITS_AVAILABLE; | ||
4003 | else | ||
4004 | server->max_credits = volume_info->max_credits; | ||
4005 | /* get a reference to a SMB session */ | ||
4006 | ses = cifs_get_smb_ses(server, volume_info); | ||
4007 | if (IS_ERR(ses)) { | ||
4008 | rc = PTR_ERR(ses); | ||
4009 | ses = NULL; | ||
4010 | goto mount_fail_check; | ||
4011 | } | 4422 | } |
4012 | 4423 | ||
4013 | if ((volume_info->persistent == true) && ((ses->server->capabilities & | 4424 | if (rc != -EREMOTE) { |
4014 | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == 0)) { | 4425 | rc = cifs_are_all_path_components_accessible(server, xid, tcon, |
4015 | cifs_dbg(VFS, "persistent handles not supported by server\n"); | 4426 | cifs_sb, |
4016 | rc = -EOPNOTSUPP; | 4427 | full_path); |
4017 | goto mount_fail_check; | 4428 | if (rc != 0) { |
4429 | cifs_dbg(VFS, "cannot query dirs between root and final path, " | ||
4430 | "enabling CIFS_MOUNT_USE_PREFIX_PATH\n"); | ||
4431 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; | ||
4432 | rc = 0; | ||
4433 | } | ||
4018 | } | 4434 | } |
4019 | 4435 | ||
4020 | /* search for existing tcon to this server share */ | 4436 | kfree(full_path); |
4021 | tcon = cifs_get_tcon(ses, volume_info); | 4437 | return rc; |
4022 | if (IS_ERR(tcon)) { | 4438 | } |
4023 | rc = PTR_ERR(tcon); | ||
4024 | tcon = NULL; | ||
4025 | if (rc == -EACCES) | ||
4026 | goto mount_fail_check; | ||
4027 | |||
4028 | goto remote_path_check; | ||
4029 | } | ||
4030 | 4439 | ||
4031 | /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ | 4440 | #ifdef CONFIG_CIFS_DFS_UPCALL |
4032 | if (tcon->posix_extensions) | 4441 | int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) |
4033 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; | 4442 | { |
4443 | int rc = 0; | ||
4444 | unsigned int xid; | ||
4445 | struct cifs_ses *ses; | ||
4446 | struct cifs_tcon *root_tcon = NULL; | ||
4447 | struct cifs_tcon *tcon = NULL; | ||
4448 | struct TCP_Server_Info *server; | ||
4449 | char *root_path = NULL, *full_path = NULL; | ||
4450 | char *old_mountdata; | ||
4451 | int count; | ||
4034 | 4452 | ||
4035 | /* tell server which Unix caps we support */ | 4453 | rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon); |
4036 | if (cap_unix(tcon->ses)) { | 4454 | if (!rc && tcon) { |
4037 | /* reset of caps checks mount to see if unix extensions | 4455 | /* If not a standalone DFS root, then check if path is remote */ |
4038 | disabled for just this mount */ | 4456 | rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, |
4039 | reset_cifs_unix_caps(xid, tcon, cifs_sb, volume_info); | 4457 | cifs_remap(cifs_sb), vol->UNC + 1, NULL, |
4040 | if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && | 4458 | NULL); |
4041 | (le64_to_cpu(tcon->fsUnixInfo.Capability) & | 4459 | if (rc) { |
4042 | CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) { | 4460 | rc = is_path_remote(cifs_sb, vol, xid, server, tcon); |
4043 | rc = -EACCES; | 4461 | if (!rc) |
4044 | goto mount_fail_check; | 4462 | goto out; |
4463 | if (rc != -EREMOTE) | ||
4464 | goto error; | ||
4045 | } | 4465 | } |
4046 | } else | 4466 | } |
4047 | tcon->unix_ext = 0; /* server does not support them */ | 4467 | /* |
4048 | 4468 | * If first DFS target server went offline and we failed to connect it, | |
4049 | /* do not care if a following call succeed - informational */ | 4469 | * server and ses pointers are NULL at this point, though we still have |
4050 | if (!tcon->pipe && server->ops->qfs_tcon) | 4470 | * chance to get a cached DFS referral in expand_dfs_referral() and |
4051 | server->ops->qfs_tcon(xid, tcon); | 4471 | * retry next target available in it. |
4052 | 4472 | * | |
4053 | cifs_sb->wsize = server->ops->negotiate_wsize(tcon, volume_info); | 4473 | * If a NULL ses ptr is passed to dfs_cache_find(), a lookup will be |
4054 | cifs_sb->rsize = server->ops->negotiate_rsize(tcon, volume_info); | 4474 | * performed against DFS path and *no* requests will be sent to server |
4475 | * for any new DFS referrals. Hence it's safe to skip checking whether | ||
4476 | * server or ses ptr is NULL. | ||
4477 | */ | ||
4478 | if (rc == -EACCES || rc == -EOPNOTSUPP) | ||
4479 | goto error; | ||
4480 | |||
4481 | root_path = build_unc_path_to_root(vol, cifs_sb, false); | ||
4482 | if (IS_ERR(root_path)) { | ||
4483 | rc = PTR_ERR(root_path); | ||
4484 | root_path = NULL; | ||
4485 | goto error; | ||
4486 | } | ||
4055 | 4487 | ||
4056 | remote_path_check: | 4488 | full_path = build_unc_path_to_root(vol, cifs_sb, true); |
4057 | #ifdef CONFIG_CIFS_DFS_UPCALL | 4489 | if (IS_ERR(full_path)) { |
4490 | rc = PTR_ERR(full_path); | ||
4491 | full_path = NULL; | ||
4492 | goto error; | ||
4493 | } | ||
4058 | /* | 4494 | /* |
4059 | * Perform an unconditional check for whether there are DFS | 4495 | * Perform an unconditional check for whether there are DFS |
4060 | * referrals for this path without prefix, to provide support | 4496 | * referrals for this path without prefix, to provide support |
@@ -4062,119 +4498,173 @@ remote_path_check: | |||
4062 | * with PATH_NOT_COVERED to requests that include the prefix. | 4498 | * with PATH_NOT_COVERED to requests that include the prefix. |
4063 | * Chase the referral if found, otherwise continue normally. | 4499 | * Chase the referral if found, otherwise continue normally. |
4064 | */ | 4500 | */ |
4065 | if (referral_walks_count == 0) { | 4501 | old_mountdata = cifs_sb->mountdata; |
4066 | int refrc = expand_dfs_referral(xid, ses, volume_info, cifs_sb, | 4502 | (void)expand_dfs_referral(xid, ses, vol, cifs_sb, false); |
4067 | false); | 4503 | |
4068 | if (!refrc) { | 4504 | if (cifs_sb->mountdata == NULL) { |
4069 | referral_walks_count++; | 4505 | rc = -ENOENT; |
4070 | goto try_mount_again; | 4506 | goto error; |
4071 | } | ||
4072 | } | 4507 | } |
4073 | #endif | ||
4074 | 4508 | ||
4075 | /* check if a whole path is not remote */ | 4509 | if (cifs_sb->mountdata != old_mountdata) { |
4076 | if (!rc && tcon) { | 4510 | /* If we were redirected, reconnect to new target server */ |
4077 | if (!server->ops->is_path_accessible) { | 4511 | mount_put_conns(cifs_sb, xid, server, ses, tcon); |
4078 | rc = -ENOSYS; | 4512 | rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon); |
4079 | goto mount_fail_check; | 4513 | } |
4514 | if (rc) { | ||
4515 | if (rc == -EACCES || rc == -EOPNOTSUPP) | ||
4516 | goto error; | ||
4517 | /* Perform DFS failover to any other DFS targets */ | ||
4518 | rc = mount_do_dfs_failover(root_path + 1, cifs_sb, vol, NULL, | ||
4519 | &xid, &server, &ses, &tcon); | ||
4520 | if (rc) | ||
4521 | goto error; | ||
4522 | } | ||
4523 | |||
4524 | kfree(root_path); | ||
4525 | root_path = build_unc_path_to_root(vol, cifs_sb, false); | ||
4526 | if (IS_ERR(root_path)) { | ||
4527 | rc = PTR_ERR(root_path); | ||
4528 | root_path = NULL; | ||
4529 | goto error; | ||
4530 | } | ||
4531 | /* Cache out resolved root server */ | ||
4532 | (void)dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), | ||
4533 | root_path + 1, NULL, NULL); | ||
4534 | /* | ||
4535 | * Save root tcon for additional DFS requests to update or create a new | ||
4536 | * DFS cache entry, or even perform DFS failover. | ||
4537 | */ | ||
4538 | spin_lock(&cifs_tcp_ses_lock); | ||
4539 | tcon->tc_count++; | ||
4540 | tcon->dfs_path = root_path; | ||
4541 | root_path = NULL; | ||
4542 | tcon->remap = cifs_remap(cifs_sb); | ||
4543 | spin_unlock(&cifs_tcp_ses_lock); | ||
4544 | |||
4545 | root_tcon = tcon; | ||
4546 | |||
4547 | for (count = 1; ;) { | ||
4548 | if (!rc && tcon) { | ||
4549 | rc = is_path_remote(cifs_sb, vol, xid, server, tcon); | ||
4550 | if (!rc || rc != -EREMOTE) | ||
4551 | break; | ||
4080 | } | 4552 | } |
4081 | /* | 4553 | /* |
4082 | * cifs_build_path_to_root works only when we have a valid tcon | 4554 | * BB: when we implement proper loop detection, |
4555 | * we will remove this check. But now we need it | ||
4556 | * to prevent an indefinite loop if 'DFS tree' is | ||
4557 | * misconfigured (i.e. has loops). | ||
4083 | */ | 4558 | */ |
4084 | full_path = cifs_build_path_to_root(volume_info, cifs_sb, tcon, | 4559 | if (count++ > MAX_NESTED_LINKS) { |
4085 | tcon->Flags & SMB_SHARE_IS_IN_DFS); | 4560 | rc = -ELOOP; |
4086 | if (full_path == NULL) { | 4561 | break; |
4087 | rc = -ENOMEM; | ||
4088 | goto mount_fail_check; | ||
4089 | } | ||
4090 | rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, | ||
4091 | full_path); | ||
4092 | if (rc != 0 && rc != -EREMOTE) { | ||
4093 | kfree(full_path); | ||
4094 | goto mount_fail_check; | ||
4095 | } | 4562 | } |
4096 | 4563 | ||
4097 | if (rc != -EREMOTE) { | ||
4098 | rc = cifs_are_all_path_components_accessible(server, | ||
4099 | xid, tcon, cifs_sb, | ||
4100 | full_path); | ||
4101 | if (rc != 0) { | ||
4102 | cifs_dbg(VFS, "cannot query dirs between root and final path, " | ||
4103 | "enabling CIFS_MOUNT_USE_PREFIX_PATH\n"); | ||
4104 | cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; | ||
4105 | rc = 0; | ||
4106 | } | ||
4107 | } | ||
4108 | kfree(full_path); | 4564 | kfree(full_path); |
4109 | } | 4565 | full_path = build_unc_path_to_root(vol, cifs_sb, true); |
4110 | 4566 | if (IS_ERR(full_path)) { | |
4111 | /* get referral if needed */ | 4567 | rc = PTR_ERR(full_path); |
4112 | if (rc == -EREMOTE) { | 4568 | full_path = NULL; |
4113 | #ifdef CONFIG_CIFS_DFS_UPCALL | 4569 | break; |
4114 | if (referral_walks_count > MAX_NESTED_LINKS) { | ||
4115 | /* | ||
4116 | * BB: when we implement proper loop detection, | ||
4117 | * we will remove this check. But now we need it | ||
4118 | * to prevent an indefinite loop if 'DFS tree' is | ||
4119 | * misconfigured (i.e. has loops). | ||
4120 | */ | ||
4121 | rc = -ELOOP; | ||
4122 | goto mount_fail_check; | ||
4123 | } | 4570 | } |
4124 | 4571 | ||
4125 | rc = expand_dfs_referral(xid, ses, volume_info, cifs_sb, true); | 4572 | old_mountdata = cifs_sb->mountdata; |
4573 | rc = expand_dfs_referral(xid, root_tcon->ses, vol, cifs_sb, | ||
4574 | true); | ||
4575 | if (rc) | ||
4576 | break; | ||
4126 | 4577 | ||
4127 | if (!rc) { | 4578 | if (cifs_sb->mountdata != old_mountdata) { |
4128 | referral_walks_count++; | 4579 | mount_put_conns(cifs_sb, xid, server, ses, tcon); |
4129 | goto try_mount_again; | 4580 | rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, |
4581 | &tcon); | ||
4582 | } | ||
4583 | if (rc) { | ||
4584 | if (rc == -EACCES || rc == -EOPNOTSUPP) | ||
4585 | break; | ||
4586 | /* Perform DFS failover to any other DFS targets */ | ||
4587 | rc = mount_do_dfs_failover(full_path + 1, cifs_sb, vol, | ||
4588 | root_tcon->ses, &xid, | ||
4589 | &server, &ses, &tcon); | ||
4590 | if (rc == -EACCES || rc == -EOPNOTSUPP || !server || | ||
4591 | !ses) | ||
4592 | goto error; | ||
4130 | } | 4593 | } |
4131 | goto mount_fail_check; | ||
4132 | #else /* No DFS support, return error on mount */ | ||
4133 | rc = -EOPNOTSUPP; | ||
4134 | #endif | ||
4135 | } | 4594 | } |
4595 | cifs_put_tcon(root_tcon); | ||
4136 | 4596 | ||
4137 | if (rc) | 4597 | if (rc) |
4138 | goto mount_fail_check; | 4598 | goto error; |
4139 | 4599 | ||
4140 | /* now, hang the tcon off of the superblock */ | 4600 | spin_lock(&cifs_tcp_ses_lock); |
4141 | tlink = kzalloc(sizeof *tlink, GFP_KERNEL); | 4601 | if (!tcon->dfs_path) { |
4142 | if (tlink == NULL) { | 4602 | /* Save full path in new tcon to do failover when reconnecting tcons */ |
4603 | tcon->dfs_path = full_path; | ||
4604 | full_path = NULL; | ||
4605 | tcon->remap = cifs_remap(cifs_sb); | ||
4606 | } | ||
4607 | cifs_sb->origin_fullpath = kstrndup(tcon->dfs_path, | ||
4608 | strlen(tcon->dfs_path), | ||
4609 | GFP_ATOMIC); | ||
4610 | if (!cifs_sb->origin_fullpath) { | ||
4611 | spin_unlock(&cifs_tcp_ses_lock); | ||
4143 | rc = -ENOMEM; | 4612 | rc = -ENOMEM; |
4144 | goto mount_fail_check; | 4613 | goto error; |
4145 | } | 4614 | } |
4615 | spin_unlock(&cifs_tcp_ses_lock); | ||
4146 | 4616 | ||
4147 | tlink->tl_uid = ses->linux_uid; | 4617 | rc = dfs_cache_add_vol(vol, cifs_sb->origin_fullpath); |
4148 | tlink->tl_tcon = tcon; | 4618 | if (rc) { |
4149 | tlink->tl_time = jiffies; | 4619 | kfree(cifs_sb->origin_fullpath); |
4150 | set_bit(TCON_LINK_MASTER, &tlink->tl_flags); | 4620 | goto error; |
4151 | set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); | 4621 | } |
4622 | /* | ||
4623 | * After reconnecting to a different server, unique ids won't | ||
4624 | * match anymore, so we disable serverino. This prevents | ||
4625 | * dentry revalidation to think the dentry are stale (ESTALE). | ||
4626 | */ | ||
4627 | cifs_autodisable_serverino(cifs_sb); | ||
4628 | out: | ||
4629 | free_xid(xid); | ||
4630 | return mount_setup_tlink(cifs_sb, ses, tcon); | ||
4152 | 4631 | ||
4153 | cifs_sb->master_tlink = tlink; | 4632 | error: |
4154 | spin_lock(&cifs_sb->tlink_tree_lock); | 4633 | kfree(full_path); |
4155 | tlink_rb_insert(&cifs_sb->tlink_tree, tlink); | 4634 | kfree(root_path); |
4156 | spin_unlock(&cifs_sb->tlink_tree_lock); | 4635 | mount_put_conns(cifs_sb, xid, server, ses, tcon); |
4636 | return rc; | ||
4637 | } | ||
4638 | #else | ||
4639 | int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) | ||
4640 | { | ||
4641 | int rc = 0; | ||
4642 | unsigned int xid; | ||
4643 | struct cifs_ses *ses; | ||
4644 | struct cifs_tcon *tcon; | ||
4645 | struct TCP_Server_Info *server; | ||
4157 | 4646 | ||
4158 | queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, | 4647 | rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon); |
4159 | TLINK_IDLE_EXPIRE); | 4648 | if (rc) |
4649 | goto error; | ||
4160 | 4650 | ||
4161 | mount_fail_check: | 4651 | if (tcon) { |
4162 | /* on error free sesinfo and tcon struct if needed */ | 4652 | rc = is_path_remote(cifs_sb, vol, xid, server, tcon); |
4163 | if (rc) { | 4653 | if (rc == -EREMOTE) |
4164 | /* If find_unc succeeded then rc == 0 so we can not end */ | 4654 | rc = -EOPNOTSUPP; |
4165 | /* up accidentally freeing someone elses tcon struct */ | 4655 | if (rc) |
4166 | if (tcon) | 4656 | goto error; |
4167 | cifs_put_tcon(tcon); | ||
4168 | else if (ses) | ||
4169 | cifs_put_smb_ses(ses); | ||
4170 | else | ||
4171 | cifs_put_tcp_session(server, 0); | ||
4172 | } | 4657 | } |
4173 | 4658 | ||
4174 | out: | ||
4175 | free_xid(xid); | 4659 | free_xid(xid); |
4660 | |||
4661 | return mount_setup_tlink(cifs_sb, ses, tcon); | ||
4662 | |||
4663 | error: | ||
4664 | mount_put_conns(cifs_sb, xid, server, ses, tcon); | ||
4176 | return rc; | 4665 | return rc; |
4177 | } | 4666 | } |
4667 | #endif | ||
4178 | 4668 | ||
4179 | /* | 4669 | /* |
4180 | * Issue a TREE_CONNECT request. | 4670 | * Issue a TREE_CONNECT request. |
@@ -4370,6 +4860,10 @@ cifs_umount(struct cifs_sb_info *cifs_sb) | |||
4370 | 4860 | ||
4371 | kfree(cifs_sb->mountdata); | 4861 | kfree(cifs_sb->mountdata); |
4372 | kfree(cifs_sb->prepath); | 4862 | kfree(cifs_sb->prepath); |
4863 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
4864 | dfs_cache_del_vol(cifs_sb->origin_fullpath); | ||
4865 | kfree(cifs_sb->origin_fullpath); | ||
4866 | #endif | ||
4373 | call_rcu(&cifs_sb->rcu, delayed_free); | 4867 | call_rcu(&cifs_sb->rcu, delayed_free); |
4374 | } | 4868 | } |
4375 | 4869 | ||
diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c new file mode 100644 index 000000000000..cd63c4a70875 --- /dev/null +++ b/fs/cifs/dfs_cache.c | |||
@@ -0,0 +1,1367 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * DFS referral cache routines | ||
4 | * | ||
5 | * Copyright (c) 2018 Paulo Alcantara <palcantara@suse.de> | ||
6 | */ | ||
7 | |||
8 | #include <linux/rcupdate.h> | ||
9 | #include <linux/rculist.h> | ||
10 | #include <linux/jhash.h> | ||
11 | #include <linux/ktime.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/nls.h> | ||
14 | #include <linux/workqueue.h> | ||
15 | #include "cifsglob.h" | ||
16 | #include "smb2pdu.h" | ||
17 | #include "smb2proto.h" | ||
18 | #include "cifsproto.h" | ||
19 | #include "cifs_debug.h" | ||
20 | #include "cifs_unicode.h" | ||
21 | #include "smb2glob.h" | ||
22 | |||
23 | #include "dfs_cache.h" | ||
24 | |||
25 | #define DFS_CACHE_HTABLE_SIZE 32 | ||
26 | #define DFS_CACHE_MAX_ENTRIES 64 | ||
27 | |||
28 | #define IS_INTERLINK_SET(v) ((v) & (DFSREF_REFERRAL_SERVER | \ | ||
29 | DFSREF_STORAGE_SERVER)) | ||
30 | |||
31 | struct dfs_cache_tgt { | ||
32 | char *t_name; | ||
33 | struct list_head t_list; | ||
34 | }; | ||
35 | |||
36 | struct dfs_cache_entry { | ||
37 | struct hlist_node ce_hlist; | ||
38 | const char *ce_path; | ||
39 | int ce_ttl; | ||
40 | int ce_srvtype; | ||
41 | int ce_flags; | ||
42 | struct timespec64 ce_etime; | ||
43 | int ce_path_consumed; | ||
44 | int ce_numtgts; | ||
45 | struct list_head ce_tlist; | ||
46 | struct dfs_cache_tgt *ce_tgthint; | ||
47 | struct rcu_head ce_rcu; | ||
48 | }; | ||
49 | |||
50 | static struct kmem_cache *dfs_cache_slab __read_mostly; | ||
51 | |||
52 | struct dfs_cache_vol_info { | ||
53 | char *vi_fullpath; | ||
54 | struct smb_vol vi_vol; | ||
55 | struct list_head vi_list; | ||
56 | }; | ||
57 | |||
58 | struct dfs_cache { | ||
59 | struct mutex dc_lock; | ||
60 | struct nls_table *dc_nlsc; | ||
61 | struct list_head dc_vol_list; | ||
62 | int dc_ttl; | ||
63 | struct delayed_work dc_refresh; | ||
64 | }; | ||
65 | |||
66 | static struct dfs_cache dfs_cache; | ||
67 | |||
68 | /* | ||
69 | * Number of entries in the cache | ||
70 | */ | ||
71 | static size_t dfs_cache_count; | ||
72 | |||
73 | static DEFINE_MUTEX(dfs_cache_list_lock); | ||
74 | static struct hlist_head dfs_cache_htable[DFS_CACHE_HTABLE_SIZE]; | ||
75 | |||
76 | static void refresh_cache_worker(struct work_struct *work); | ||
77 | |||
78 | static inline bool is_path_valid(const char *path) | ||
79 | { | ||
80 | return path && (strchr(path + 1, '\\') || strchr(path + 1, '/')); | ||
81 | } | ||
82 | |||
83 | static inline int get_normalized_path(const char *path, char **npath) | ||
84 | { | ||
85 | if (*path == '\\') { | ||
86 | *npath = (char *)path; | ||
87 | } else { | ||
88 | *npath = kstrndup(path, strlen(path), GFP_KERNEL); | ||
89 | if (!*npath) | ||
90 | return -ENOMEM; | ||
91 | convert_delimiter(*npath, '\\'); | ||
92 | } | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static inline void free_normalized_path(const char *path, char *npath) | ||
97 | { | ||
98 | if (path != npath) | ||
99 | kfree(npath); | ||
100 | } | ||
101 | |||
102 | static inline bool cache_entry_expired(const struct dfs_cache_entry *ce) | ||
103 | { | ||
104 | struct timespec64 ts; | ||
105 | |||
106 | ktime_get_coarse_real_ts64(&ts); | ||
107 | return timespec64_compare(&ts, &ce->ce_etime) >= 0; | ||
108 | } | ||
109 | |||
110 | static inline void free_tgts(struct dfs_cache_entry *ce) | ||
111 | { | ||
112 | struct dfs_cache_tgt *t, *n; | ||
113 | |||
114 | list_for_each_entry_safe(t, n, &ce->ce_tlist, t_list) { | ||
115 | list_del(&t->t_list); | ||
116 | kfree(t->t_name); | ||
117 | kfree(t); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static void free_cache_entry(struct rcu_head *rcu) | ||
122 | { | ||
123 | struct dfs_cache_entry *ce = container_of(rcu, struct dfs_cache_entry, | ||
124 | ce_rcu); | ||
125 | kmem_cache_free(dfs_cache_slab, ce); | ||
126 | } | ||
127 | |||
128 | static inline void flush_cache_ent(struct dfs_cache_entry *ce) | ||
129 | { | ||
130 | if (hlist_unhashed(&ce->ce_hlist)) | ||
131 | return; | ||
132 | |||
133 | hlist_del_init_rcu(&ce->ce_hlist); | ||
134 | kfree(ce->ce_path); | ||
135 | free_tgts(ce); | ||
136 | dfs_cache_count--; | ||
137 | call_rcu(&ce->ce_rcu, free_cache_entry); | ||
138 | } | ||
139 | |||
140 | static void flush_cache_ents(void) | ||
141 | { | ||
142 | int i; | ||
143 | |||
144 | rcu_read_lock(); | ||
145 | for (i = 0; i < DFS_CACHE_HTABLE_SIZE; i++) { | ||
146 | struct hlist_head *l = &dfs_cache_htable[i]; | ||
147 | struct dfs_cache_entry *ce; | ||
148 | |||
149 | hlist_for_each_entry_rcu(ce, l, ce_hlist) | ||
150 | flush_cache_ent(ce); | ||
151 | } | ||
152 | rcu_read_unlock(); | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * dfs cache /proc file | ||
157 | */ | ||
158 | static int dfscache_proc_show(struct seq_file *m, void *v) | ||
159 | { | ||
160 | int bucket; | ||
161 | struct dfs_cache_entry *ce; | ||
162 | struct dfs_cache_tgt *t; | ||
163 | |||
164 | seq_puts(m, "DFS cache\n---------\n"); | ||
165 | |||
166 | mutex_lock(&dfs_cache_list_lock); | ||
167 | |||
168 | rcu_read_lock(); | ||
169 | hash_for_each_rcu(dfs_cache_htable, bucket, ce, ce_hlist) { | ||
170 | seq_printf(m, | ||
171 | "cache entry: path=%s,type=%s,ttl=%d,etime=%ld," | ||
172 | "interlink=%s,path_consumed=%d,expired=%s\n", | ||
173 | ce->ce_path, | ||
174 | ce->ce_srvtype == DFS_TYPE_ROOT ? "root" : "link", | ||
175 | ce->ce_ttl, ce->ce_etime.tv_nsec, | ||
176 | IS_INTERLINK_SET(ce->ce_flags) ? "yes" : "no", | ||
177 | ce->ce_path_consumed, | ||
178 | cache_entry_expired(ce) ? "yes" : "no"); | ||
179 | |||
180 | list_for_each_entry(t, &ce->ce_tlist, t_list) { | ||
181 | seq_printf(m, " %s%s\n", | ||
182 | t->t_name, | ||
183 | ce->ce_tgthint == t ? " (target hint)" : ""); | ||
184 | } | ||
185 | |||
186 | } | ||
187 | rcu_read_unlock(); | ||
188 | |||
189 | mutex_unlock(&dfs_cache_list_lock); | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer, | ||
194 | size_t count, loff_t *ppos) | ||
195 | { | ||
196 | char c; | ||
197 | int rc; | ||
198 | |||
199 | rc = get_user(c, buffer); | ||
200 | if (rc) | ||
201 | return rc; | ||
202 | |||
203 | if (c != '0') | ||
204 | return -EINVAL; | ||
205 | |||
206 | cifs_dbg(FYI, "clearing dfs cache"); | ||
207 | mutex_lock(&dfs_cache_list_lock); | ||
208 | flush_cache_ents(); | ||
209 | mutex_unlock(&dfs_cache_list_lock); | ||
210 | |||
211 | return count; | ||
212 | } | ||
213 | |||
214 | static int dfscache_proc_open(struct inode *inode, struct file *file) | ||
215 | { | ||
216 | return single_open(file, dfscache_proc_show, NULL); | ||
217 | } | ||
218 | |||
219 | const struct file_operations dfscache_proc_fops = { | ||
220 | .open = dfscache_proc_open, | ||
221 | .read = seq_read, | ||
222 | .llseek = seq_lseek, | ||
223 | .release = single_release, | ||
224 | .write = dfscache_proc_write, | ||
225 | }; | ||
226 | |||
227 | #ifdef CONFIG_CIFS_DEBUG2 | ||
228 | static inline void dump_tgts(const struct dfs_cache_entry *ce) | ||
229 | { | ||
230 | struct dfs_cache_tgt *t; | ||
231 | |||
232 | cifs_dbg(FYI, "target list:\n"); | ||
233 | list_for_each_entry(t, &ce->ce_tlist, t_list) { | ||
234 | cifs_dbg(FYI, " %s%s\n", t->t_name, | ||
235 | ce->ce_tgthint == t ? " (target hint)" : ""); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | static inline void dump_ce(const struct dfs_cache_entry *ce) | ||
240 | { | ||
241 | cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld," | ||
242 | "interlink=%s,path_consumed=%d,expired=%s\n", ce->ce_path, | ||
243 | ce->ce_srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ce_ttl, | ||
244 | ce->ce_etime.tv_nsec, | ||
245 | IS_INTERLINK_SET(ce->ce_flags) ? "yes" : "no", | ||
246 | ce->ce_path_consumed, | ||
247 | cache_entry_expired(ce) ? "yes" : "no"); | ||
248 | dump_tgts(ce); | ||
249 | } | ||
250 | |||
251 | static inline void dump_refs(const struct dfs_info3_param *refs, int numrefs) | ||
252 | { | ||
253 | int i; | ||
254 | |||
255 | cifs_dbg(FYI, "DFS referrals returned by the server:\n"); | ||
256 | for (i = 0; i < numrefs; i++) { | ||
257 | const struct dfs_info3_param *ref = &refs[i]; | ||
258 | |||
259 | cifs_dbg(FYI, | ||
260 | "\n" | ||
261 | "flags: 0x%x\n" | ||
262 | "path_consumed: %d\n" | ||
263 | "server_type: 0x%x\n" | ||
264 | "ref_flag: 0x%x\n" | ||
265 | "path_name: %s\n" | ||
266 | "node_name: %s\n" | ||
267 | "ttl: %d (%dm)\n", | ||
268 | ref->flags, ref->path_consumed, ref->server_type, | ||
269 | ref->ref_flag, ref->path_name, ref->node_name, | ||
270 | ref->ttl, ref->ttl / 60); | ||
271 | } | ||
272 | } | ||
273 | #else | ||
274 | #define dump_tgts(e) | ||
275 | #define dump_ce(e) | ||
276 | #define dump_refs(r, n) | ||
277 | #endif | ||
278 | |||
279 | /** | ||
280 | * dfs_cache_init - Initialize DFS referral cache. | ||
281 | * | ||
282 | * Return zero if initialized successfully, otherwise non-zero. | ||
283 | */ | ||
284 | int dfs_cache_init(void) | ||
285 | { | ||
286 | int i; | ||
287 | |||
288 | dfs_cache_slab = kmem_cache_create("cifs_dfs_cache", | ||
289 | sizeof(struct dfs_cache_entry), 0, | ||
290 | SLAB_HWCACHE_ALIGN, NULL); | ||
291 | if (!dfs_cache_slab) | ||
292 | return -ENOMEM; | ||
293 | |||
294 | for (i = 0; i < DFS_CACHE_HTABLE_SIZE; i++) | ||
295 | INIT_HLIST_HEAD(&dfs_cache_htable[i]); | ||
296 | |||
297 | INIT_LIST_HEAD(&dfs_cache.dc_vol_list); | ||
298 | mutex_init(&dfs_cache.dc_lock); | ||
299 | INIT_DELAYED_WORK(&dfs_cache.dc_refresh, refresh_cache_worker); | ||
300 | dfs_cache.dc_ttl = -1; | ||
301 | dfs_cache.dc_nlsc = load_nls_default(); | ||
302 | |||
303 | cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__); | ||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | static inline unsigned int cache_entry_hash(const void *data, int size) | ||
308 | { | ||
309 | unsigned int h; | ||
310 | |||
311 | h = jhash(data, size, 0); | ||
312 | return h & (DFS_CACHE_HTABLE_SIZE - 1); | ||
313 | } | ||
314 | |||
315 | /* Check whether second path component of @path is SYSVOL or NETLOGON */ | ||
316 | static inline bool is_sysvol_or_netlogon(const char *path) | ||
317 | { | ||
318 | const char *s; | ||
319 | char sep = path[0]; | ||
320 | |||
321 | s = strchr(path + 1, sep) + 1; | ||
322 | return !strncasecmp(s, "sysvol", strlen("sysvol")) || | ||
323 | !strncasecmp(s, "netlogon", strlen("netlogon")); | ||
324 | } | ||
325 | |||
326 | /* Return target hint of a DFS cache entry */ | ||
327 | static inline char *get_tgt_name(const struct dfs_cache_entry *ce) | ||
328 | { | ||
329 | struct dfs_cache_tgt *t = ce->ce_tgthint; | ||
330 | |||
331 | return t ? t->t_name : ERR_PTR(-ENOENT); | ||
332 | } | ||
333 | |||
334 | /* Return expire time out of a new entry's TTL */ | ||
335 | static inline struct timespec64 get_expire_time(int ttl) | ||
336 | { | ||
337 | struct timespec64 ts = { | ||
338 | .tv_sec = ttl, | ||
339 | .tv_nsec = 0, | ||
340 | }; | ||
341 | struct timespec64 now; | ||
342 | |||
343 | ktime_get_coarse_real_ts64(&now); | ||
344 | return timespec64_add(now, ts); | ||
345 | } | ||
346 | |||
347 | /* Allocate a new DFS target */ | ||
348 | static inline struct dfs_cache_tgt *alloc_tgt(const char *name) | ||
349 | { | ||
350 | struct dfs_cache_tgt *t; | ||
351 | |||
352 | t = kmalloc(sizeof(*t), GFP_KERNEL); | ||
353 | if (!t) | ||
354 | return ERR_PTR(-ENOMEM); | ||
355 | t->t_name = kstrndup(name, strlen(name), GFP_KERNEL); | ||
356 | if (!t->t_name) { | ||
357 | kfree(t); | ||
358 | return ERR_PTR(-ENOMEM); | ||
359 | } | ||
360 | INIT_LIST_HEAD(&t->t_list); | ||
361 | return t; | ||
362 | } | ||
363 | |||
364 | /* | ||
365 | * Copy DFS referral information to a cache entry and conditionally update | ||
366 | * target hint. | ||
367 | */ | ||
368 | static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs, | ||
369 | struct dfs_cache_entry *ce, const char *tgthint) | ||
370 | { | ||
371 | int i; | ||
372 | |||
373 | ce->ce_ttl = refs[0].ttl; | ||
374 | ce->ce_etime = get_expire_time(ce->ce_ttl); | ||
375 | ce->ce_srvtype = refs[0].server_type; | ||
376 | ce->ce_flags = refs[0].ref_flag; | ||
377 | ce->ce_path_consumed = refs[0].path_consumed; | ||
378 | |||
379 | for (i = 0; i < numrefs; i++) { | ||
380 | struct dfs_cache_tgt *t; | ||
381 | |||
382 | t = alloc_tgt(refs[i].node_name); | ||
383 | if (IS_ERR(t)) { | ||
384 | free_tgts(ce); | ||
385 | return PTR_ERR(t); | ||
386 | } | ||
387 | if (tgthint && !strcasecmp(t->t_name, tgthint)) { | ||
388 | list_add(&t->t_list, &ce->ce_tlist); | ||
389 | tgthint = NULL; | ||
390 | } else { | ||
391 | list_add_tail(&t->t_list, &ce->ce_tlist); | ||
392 | } | ||
393 | ce->ce_numtgts++; | ||
394 | } | ||
395 | |||
396 | ce->ce_tgthint = list_first_entry_or_null(&ce->ce_tlist, | ||
397 | struct dfs_cache_tgt, t_list); | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | /* Allocate a new cache entry */ | ||
403 | static struct dfs_cache_entry * | ||
404 | alloc_cache_entry(const char *path, const struct dfs_info3_param *refs, | ||
405 | int numrefs) | ||
406 | { | ||
407 | struct dfs_cache_entry *ce; | ||
408 | int rc; | ||
409 | |||
410 | ce = kmem_cache_zalloc(dfs_cache_slab, GFP_KERNEL); | ||
411 | if (!ce) | ||
412 | return ERR_PTR(-ENOMEM); | ||
413 | |||
414 | ce->ce_path = kstrdup_const(path, GFP_KERNEL); | ||
415 | if (!ce->ce_path) { | ||
416 | kmem_cache_free(dfs_cache_slab, ce); | ||
417 | return ERR_PTR(-ENOMEM); | ||
418 | } | ||
419 | INIT_HLIST_NODE(&ce->ce_hlist); | ||
420 | INIT_LIST_HEAD(&ce->ce_tlist); | ||
421 | |||
422 | rc = copy_ref_data(refs, numrefs, ce, NULL); | ||
423 | if (rc) { | ||
424 | kfree(ce->ce_path); | ||
425 | kmem_cache_free(dfs_cache_slab, ce); | ||
426 | ce = ERR_PTR(rc); | ||
427 | } | ||
428 | return ce; | ||
429 | } | ||
430 | |||
431 | static void remove_oldest_entry(void) | ||
432 | { | ||
433 | int bucket; | ||
434 | struct dfs_cache_entry *ce; | ||
435 | struct dfs_cache_entry *to_del = NULL; | ||
436 | |||
437 | rcu_read_lock(); | ||
438 | hash_for_each_rcu(dfs_cache_htable, bucket, ce, ce_hlist) { | ||
439 | if (!to_del || timespec64_compare(&ce->ce_etime, | ||
440 | &to_del->ce_etime) < 0) | ||
441 | to_del = ce; | ||
442 | } | ||
443 | if (!to_del) { | ||
444 | cifs_dbg(FYI, "%s: no entry to remove", __func__); | ||
445 | goto out; | ||
446 | } | ||
447 | cifs_dbg(FYI, "%s: removing entry", __func__); | ||
448 | dump_ce(to_del); | ||
449 | flush_cache_ent(to_del); | ||
450 | out: | ||
451 | rcu_read_unlock(); | ||
452 | } | ||
453 | |||
454 | /* Add a new DFS cache entry */ | ||
455 | static inline struct dfs_cache_entry * | ||
456 | add_cache_entry(unsigned int hash, const char *path, | ||
457 | const struct dfs_info3_param *refs, int numrefs) | ||
458 | { | ||
459 | struct dfs_cache_entry *ce; | ||
460 | |||
461 | ce = alloc_cache_entry(path, refs, numrefs); | ||
462 | if (IS_ERR(ce)) | ||
463 | return ce; | ||
464 | |||
465 | hlist_add_head_rcu(&ce->ce_hlist, &dfs_cache_htable[hash]); | ||
466 | |||
467 | mutex_lock(&dfs_cache.dc_lock); | ||
468 | if (dfs_cache.dc_ttl < 0) { | ||
469 | dfs_cache.dc_ttl = ce->ce_ttl; | ||
470 | queue_delayed_work(cifsiod_wq, &dfs_cache.dc_refresh, | ||
471 | dfs_cache.dc_ttl * HZ); | ||
472 | } else { | ||
473 | dfs_cache.dc_ttl = min_t(int, dfs_cache.dc_ttl, ce->ce_ttl); | ||
474 | mod_delayed_work(cifsiod_wq, &dfs_cache.dc_refresh, | ||
475 | dfs_cache.dc_ttl * HZ); | ||
476 | } | ||
477 | mutex_unlock(&dfs_cache.dc_lock); | ||
478 | |||
479 | return ce; | ||
480 | } | ||
481 | |||
482 | static struct dfs_cache_entry *__find_cache_entry(unsigned int hash, | ||
483 | const char *path) | ||
484 | { | ||
485 | struct dfs_cache_entry *ce; | ||
486 | bool found = false; | ||
487 | |||
488 | rcu_read_lock(); | ||
489 | hlist_for_each_entry_rcu(ce, &dfs_cache_htable[hash], ce_hlist) { | ||
490 | if (!strcasecmp(path, ce->ce_path)) { | ||
491 | #ifdef CONFIG_CIFS_DEBUG2 | ||
492 | char *name = get_tgt_name(ce); | ||
493 | |||
494 | if (unlikely(IS_ERR(name))) { | ||
495 | rcu_read_unlock(); | ||
496 | return ERR_CAST(name); | ||
497 | } | ||
498 | cifs_dbg(FYI, "%s: cache hit\n", __func__); | ||
499 | cifs_dbg(FYI, "%s: target hint: %s\n", __func__, name); | ||
500 | #endif | ||
501 | found = true; | ||
502 | break; | ||
503 | } | ||
504 | } | ||
505 | rcu_read_unlock(); | ||
506 | return found ? ce : ERR_PTR(-ENOENT); | ||
507 | } | ||
508 | |||
509 | /* | ||
510 | * Find a DFS cache entry in hash table and optionally check prefix path against | ||
511 | * @path. | ||
512 | * Use whole path components in the match. | ||
513 | * Return ERR_PTR(-ENOENT) if the entry is not found. | ||
514 | */ | ||
515 | static inline struct dfs_cache_entry *find_cache_entry(const char *path, | ||
516 | unsigned int *hash) | ||
517 | { | ||
518 | *hash = cache_entry_hash(path, strlen(path)); | ||
519 | return __find_cache_entry(*hash, path); | ||
520 | } | ||
521 | |||
522 | static inline void destroy_slab_cache(void) | ||
523 | { | ||
524 | rcu_barrier(); | ||
525 | kmem_cache_destroy(dfs_cache_slab); | ||
526 | } | ||
527 | |||
528 | static inline void free_vol(struct dfs_cache_vol_info *vi) | ||
529 | { | ||
530 | list_del(&vi->vi_list); | ||
531 | kfree(vi->vi_fullpath); | ||
532 | cifs_cleanup_volume_info_contents(&vi->vi_vol); | ||
533 | kfree(vi); | ||
534 | } | ||
535 | |||
536 | static inline void free_vol_list(void) | ||
537 | { | ||
538 | struct dfs_cache_vol_info *vi, *nvi; | ||
539 | |||
540 | list_for_each_entry_safe(vi, nvi, &dfs_cache.dc_vol_list, vi_list) | ||
541 | free_vol(vi); | ||
542 | } | ||
543 | |||
544 | /** | ||
545 | * dfs_cache_destroy - destroy DFS referral cache | ||
546 | */ | ||
547 | void dfs_cache_destroy(void) | ||
548 | { | ||
549 | cancel_delayed_work_sync(&dfs_cache.dc_refresh); | ||
550 | unload_nls(dfs_cache.dc_nlsc); | ||
551 | free_vol_list(); | ||
552 | mutex_destroy(&dfs_cache.dc_lock); | ||
553 | |||
554 | flush_cache_ents(); | ||
555 | destroy_slab_cache(); | ||
556 | mutex_destroy(&dfs_cache_list_lock); | ||
557 | |||
558 | cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__); | ||
559 | } | ||
560 | |||
561 | static inline struct dfs_cache_entry * | ||
562 | __update_cache_entry(const char *path, const struct dfs_info3_param *refs, | ||
563 | int numrefs) | ||
564 | { | ||
565 | int rc; | ||
566 | unsigned int h; | ||
567 | struct dfs_cache_entry *ce; | ||
568 | char *s, *th = NULL; | ||
569 | |||
570 | ce = find_cache_entry(path, &h); | ||
571 | if (IS_ERR(ce)) | ||
572 | return ce; | ||
573 | |||
574 | if (ce->ce_tgthint) { | ||
575 | s = ce->ce_tgthint->t_name; | ||
576 | th = kstrndup(s, strlen(s), GFP_KERNEL); | ||
577 | if (!th) | ||
578 | return ERR_PTR(-ENOMEM); | ||
579 | } | ||
580 | |||
581 | free_tgts(ce); | ||
582 | ce->ce_numtgts = 0; | ||
583 | |||
584 | rc = copy_ref_data(refs, numrefs, ce, th); | ||
585 | kfree(th); | ||
586 | |||
587 | if (rc) | ||
588 | ce = ERR_PTR(rc); | ||
589 | |||
590 | return ce; | ||
591 | } | ||
592 | |||
593 | /* Update an expired cache entry by getting a new DFS referral from server */ | ||
594 | static struct dfs_cache_entry * | ||
595 | update_cache_entry(const unsigned int xid, struct cifs_ses *ses, | ||
596 | const struct nls_table *nls_codepage, int remap, | ||
597 | const char *path, struct dfs_cache_entry *ce) | ||
598 | { | ||
599 | int rc; | ||
600 | struct dfs_info3_param *refs = NULL; | ||
601 | int numrefs = 0; | ||
602 | |||
603 | cifs_dbg(FYI, "%s: update expired cache entry\n", __func__); | ||
604 | /* | ||
605 | * Check if caller provided enough parameters to update an expired | ||
606 | * entry. | ||
607 | */ | ||
608 | if (!ses || !ses->server || !ses->server->ops->get_dfs_refer) | ||
609 | return ERR_PTR(-ETIME); | ||
610 | if (unlikely(!nls_codepage)) | ||
611 | return ERR_PTR(-ETIME); | ||
612 | |||
613 | cifs_dbg(FYI, "%s: DFS referral request for %s\n", __func__, path); | ||
614 | |||
615 | rc = ses->server->ops->get_dfs_refer(xid, ses, path, &refs, &numrefs, | ||
616 | nls_codepage, remap); | ||
617 | if (rc) | ||
618 | ce = ERR_PTR(rc); | ||
619 | else | ||
620 | ce = __update_cache_entry(path, refs, numrefs); | ||
621 | |||
622 | dump_refs(refs, numrefs); | ||
623 | free_dfs_info_array(refs, numrefs); | ||
624 | |||
625 | return ce; | ||
626 | } | ||
627 | |||
628 | /* | ||
629 | * Find, create or update a DFS cache entry. | ||
630 | * | ||
631 | * If the entry wasn't found, it will create a new one. Or if it was found but | ||
632 | * expired, then it will update the entry accordingly. | ||
633 | * | ||
634 | * For interlinks, __cifs_dfs_mount() and expand_dfs_referral() are supposed to | ||
635 | * handle them properly. | ||
636 | */ | ||
637 | static struct dfs_cache_entry * | ||
638 | do_dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, | ||
639 | const struct nls_table *nls_codepage, int remap, | ||
640 | const char *path, bool noreq) | ||
641 | { | ||
642 | int rc; | ||
643 | unsigned int h; | ||
644 | struct dfs_cache_entry *ce; | ||
645 | struct dfs_info3_param *nrefs; | ||
646 | int numnrefs; | ||
647 | |||
648 | cifs_dbg(FYI, "%s: search path: %s\n", __func__, path); | ||
649 | |||
650 | ce = find_cache_entry(path, &h); | ||
651 | if (IS_ERR(ce)) { | ||
652 | cifs_dbg(FYI, "%s: cache miss\n", __func__); | ||
653 | /* | ||
654 | * If @noreq is set, no requests will be sent to the server for | ||
655 | * either updating or getting a new DFS referral. | ||
656 | */ | ||
657 | if (noreq) | ||
658 | return ce; | ||
659 | /* | ||
660 | * No cache entry was found, so check for valid parameters that | ||
661 | * will be required to get a new DFS referral and then create a | ||
662 | * new cache entry. | ||
663 | */ | ||
664 | if (!ses || !ses->server || !ses->server->ops->get_dfs_refer) { | ||
665 | ce = ERR_PTR(-EOPNOTSUPP); | ||
666 | return ce; | ||
667 | } | ||
668 | if (unlikely(!nls_codepage)) { | ||
669 | ce = ERR_PTR(-EINVAL); | ||
670 | return ce; | ||
671 | } | ||
672 | |||
673 | nrefs = NULL; | ||
674 | numnrefs = 0; | ||
675 | |||
676 | cifs_dbg(FYI, "%s: DFS referral request for %s\n", __func__, | ||
677 | path); | ||
678 | |||
679 | rc = ses->server->ops->get_dfs_refer(xid, ses, path, &nrefs, | ||
680 | &numnrefs, nls_codepage, | ||
681 | remap); | ||
682 | if (rc) { | ||
683 | ce = ERR_PTR(rc); | ||
684 | return ce; | ||
685 | } | ||
686 | |||
687 | dump_refs(nrefs, numnrefs); | ||
688 | |||
689 | cifs_dbg(FYI, "%s: new cache entry\n", __func__); | ||
690 | |||
691 | if (dfs_cache_count >= DFS_CACHE_MAX_ENTRIES) { | ||
692 | cifs_dbg(FYI, "%s: reached max cache size (%d)", | ||
693 | __func__, DFS_CACHE_MAX_ENTRIES); | ||
694 | remove_oldest_entry(); | ||
695 | } | ||
696 | ce = add_cache_entry(h, path, nrefs, numnrefs); | ||
697 | free_dfs_info_array(nrefs, numnrefs); | ||
698 | |||
699 | if (IS_ERR(ce)) | ||
700 | return ce; | ||
701 | |||
702 | dfs_cache_count++; | ||
703 | } | ||
704 | |||
705 | dump_ce(ce); | ||
706 | |||
707 | /* Just return the found cache entry in case @noreq is set */ | ||
708 | if (noreq) | ||
709 | return ce; | ||
710 | |||
711 | if (cache_entry_expired(ce)) { | ||
712 | cifs_dbg(FYI, "%s: expired cache entry\n", __func__); | ||
713 | ce = update_cache_entry(xid, ses, nls_codepage, remap, path, | ||
714 | ce); | ||
715 | if (IS_ERR(ce)) { | ||
716 | cifs_dbg(FYI, "%s: failed to update expired entry\n", | ||
717 | __func__); | ||
718 | } | ||
719 | } | ||
720 | return ce; | ||
721 | } | ||
722 | |||
723 | /* Set up a new DFS referral from a given cache entry */ | ||
724 | static int setup_ref(const char *path, const struct dfs_cache_entry *ce, | ||
725 | struct dfs_info3_param *ref, const char *tgt) | ||
726 | { | ||
727 | int rc; | ||
728 | |||
729 | cifs_dbg(FYI, "%s: set up new ref\n", __func__); | ||
730 | |||
731 | memset(ref, 0, sizeof(*ref)); | ||
732 | |||
733 | ref->path_name = kstrndup(path, strlen(path), GFP_KERNEL); | ||
734 | if (!ref->path_name) | ||
735 | return -ENOMEM; | ||
736 | |||
737 | ref->path_consumed = ce->ce_path_consumed; | ||
738 | |||
739 | ref->node_name = kstrndup(tgt, strlen(tgt), GFP_KERNEL); | ||
740 | if (!ref->node_name) { | ||
741 | rc = -ENOMEM; | ||
742 | goto err_free_path; | ||
743 | } | ||
744 | |||
745 | ref->ttl = ce->ce_ttl; | ||
746 | ref->server_type = ce->ce_srvtype; | ||
747 | ref->ref_flag = ce->ce_flags; | ||
748 | |||
749 | return 0; | ||
750 | |||
751 | err_free_path: | ||
752 | kfree(ref->path_name); | ||
753 | ref->path_name = NULL; | ||
754 | return rc; | ||
755 | } | ||
756 | |||
757 | /* Return target list of a DFS cache entry */ | ||
758 | static int get_tgt_list(const struct dfs_cache_entry *ce, | ||
759 | struct dfs_cache_tgt_list *tl) | ||
760 | { | ||
761 | int rc; | ||
762 | struct list_head *head = &tl->tl_list; | ||
763 | struct dfs_cache_tgt *t; | ||
764 | struct dfs_cache_tgt_iterator *it, *nit; | ||
765 | |||
766 | memset(tl, 0, sizeof(*tl)); | ||
767 | INIT_LIST_HEAD(head); | ||
768 | |||
769 | list_for_each_entry(t, &ce->ce_tlist, t_list) { | ||
770 | it = kzalloc(sizeof(*it), GFP_KERNEL); | ||
771 | if (!it) { | ||
772 | rc = -ENOMEM; | ||
773 | goto err_free_it; | ||
774 | } | ||
775 | |||
776 | it->it_name = kstrndup(t->t_name, strlen(t->t_name), | ||
777 | GFP_KERNEL); | ||
778 | if (!it->it_name) { | ||
779 | rc = -ENOMEM; | ||
780 | goto err_free_it; | ||
781 | } | ||
782 | |||
783 | if (ce->ce_tgthint == t) | ||
784 | list_add(&it->it_list, head); | ||
785 | else | ||
786 | list_add_tail(&it->it_list, head); | ||
787 | } | ||
788 | tl->tl_numtgts = ce->ce_numtgts; | ||
789 | |||
790 | return 0; | ||
791 | |||
792 | err_free_it: | ||
793 | list_for_each_entry_safe(it, nit, head, it_list) { | ||
794 | kfree(it->it_name); | ||
795 | kfree(it); | ||
796 | } | ||
797 | return rc; | ||
798 | } | ||
799 | |||
800 | /** | ||
801 | * dfs_cache_find - find a DFS cache entry | ||
802 | * | ||
803 | * If it doesn't find the cache entry, then it will get a DFS referral | ||
804 | * for @path and create a new entry. | ||
805 | * | ||
806 | * In case the cache entry exists but expired, it will get a DFS referral | ||
807 | * for @path and then update the respective cache entry. | ||
808 | * | ||
809 | * These parameters are passed down to the get_dfs_refer() call if it | ||
810 | * needs to be issued: | ||
811 | * @xid: syscall xid | ||
812 | * @ses: smb session to issue the request on | ||
813 | * @nls_codepage: charset conversion | ||
814 | * @remap: path character remapping type | ||
815 | * @path: path to lookup in DFS referral cache. | ||
816 | * | ||
817 | * @ref: when non-NULL, store single DFS referral result in it. | ||
818 | * @tgt_list: when non-NULL, store complete DFS target list in it. | ||
819 | * | ||
820 | * Return zero if the target was found, otherwise non-zero. | ||
821 | */ | ||
822 | int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, | ||
823 | const struct nls_table *nls_codepage, int remap, | ||
824 | const char *path, struct dfs_info3_param *ref, | ||
825 | struct dfs_cache_tgt_list *tgt_list) | ||
826 | { | ||
827 | int rc; | ||
828 | char *npath; | ||
829 | struct dfs_cache_entry *ce; | ||
830 | |||
831 | if (unlikely(!is_path_valid(path))) | ||
832 | return -EINVAL; | ||
833 | |||
834 | rc = get_normalized_path(path, &npath); | ||
835 | if (rc) | ||
836 | return rc; | ||
837 | |||
838 | mutex_lock(&dfs_cache_list_lock); | ||
839 | ce = do_dfs_cache_find(xid, ses, nls_codepage, remap, npath, false); | ||
840 | if (!IS_ERR(ce)) { | ||
841 | if (ref) | ||
842 | rc = setup_ref(path, ce, ref, get_tgt_name(ce)); | ||
843 | else | ||
844 | rc = 0; | ||
845 | if (!rc && tgt_list) | ||
846 | rc = get_tgt_list(ce, tgt_list); | ||
847 | } else { | ||
848 | rc = PTR_ERR(ce); | ||
849 | } | ||
850 | mutex_unlock(&dfs_cache_list_lock); | ||
851 | free_normalized_path(path, npath); | ||
852 | return rc; | ||
853 | } | ||
854 | |||
855 | /** | ||
856 | * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to | ||
857 | * the currently connected server. | ||
858 | * | ||
859 | * NOTE: This function will neither update a cache entry in case it was | ||
860 | * expired, nor create a new cache entry if @path hasn't been found. It heavily | ||
861 | * relies on an existing cache entry. | ||
862 | * | ||
863 | * @path: path to lookup in the DFS referral cache. | ||
864 | * @ref: when non-NULL, store single DFS referral result in it. | ||
865 | * @tgt_list: when non-NULL, store complete DFS target list in it. | ||
866 | * | ||
867 | * Return 0 if successful. | ||
868 | * Return -ENOENT if the entry was not found. | ||
869 | * Return non-zero for other errors. | ||
870 | */ | ||
871 | int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, | ||
872 | struct dfs_cache_tgt_list *tgt_list) | ||
873 | { | ||
874 | int rc; | ||
875 | char *npath; | ||
876 | struct dfs_cache_entry *ce; | ||
877 | |||
878 | if (unlikely(!is_path_valid(path))) | ||
879 | return -EINVAL; | ||
880 | |||
881 | rc = get_normalized_path(path, &npath); | ||
882 | if (rc) | ||
883 | return rc; | ||
884 | |||
885 | mutex_lock(&dfs_cache_list_lock); | ||
886 | ce = do_dfs_cache_find(0, NULL, NULL, 0, npath, true); | ||
887 | if (IS_ERR(ce)) { | ||
888 | rc = PTR_ERR(ce); | ||
889 | goto out; | ||
890 | } | ||
891 | |||
892 | if (ref) | ||
893 | rc = setup_ref(path, ce, ref, get_tgt_name(ce)); | ||
894 | else | ||
895 | rc = 0; | ||
896 | if (!rc && tgt_list) | ||
897 | rc = get_tgt_list(ce, tgt_list); | ||
898 | out: | ||
899 | mutex_unlock(&dfs_cache_list_lock); | ||
900 | free_normalized_path(path, npath); | ||
901 | return rc; | ||
902 | } | ||
903 | |||
904 | /** | ||
905 | * dfs_cache_update_tgthint - update target hint of a DFS cache entry | ||
906 | * | ||
907 | * If it doesn't find the cache entry, then it will get a DFS referral for @path | ||
908 | * and create a new entry. | ||
909 | * | ||
910 | * In case the cache entry exists but expired, it will get a DFS referral | ||
911 | * for @path and then update the respective cache entry. | ||
912 | * | ||
913 | * @xid: syscall id | ||
914 | * @ses: smb session | ||
915 | * @nls_codepage: charset conversion | ||
916 | * @remap: type of character remapping for paths | ||
917 | * @path: path to lookup in DFS referral cache. | ||
918 | * @it: DFS target iterator | ||
919 | * | ||
920 | * Return zero if the target hint was updated successfully, otherwise non-zero. | ||
921 | */ | ||
922 | int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses, | ||
923 | const struct nls_table *nls_codepage, int remap, | ||
924 | const char *path, | ||
925 | const struct dfs_cache_tgt_iterator *it) | ||
926 | { | ||
927 | int rc; | ||
928 | char *npath; | ||
929 | struct dfs_cache_entry *ce; | ||
930 | struct dfs_cache_tgt *t; | ||
931 | |||
932 | if (unlikely(!is_path_valid(path))) | ||
933 | return -EINVAL; | ||
934 | |||
935 | rc = get_normalized_path(path, &npath); | ||
936 | if (rc) | ||
937 | return rc; | ||
938 | |||
939 | cifs_dbg(FYI, "%s: path: %s\n", __func__, npath); | ||
940 | |||
941 | mutex_lock(&dfs_cache_list_lock); | ||
942 | ce = do_dfs_cache_find(xid, ses, nls_codepage, remap, npath, false); | ||
943 | if (IS_ERR(ce)) { | ||
944 | rc = PTR_ERR(ce); | ||
945 | goto out; | ||
946 | } | ||
947 | |||
948 | rc = 0; | ||
949 | |||
950 | t = ce->ce_tgthint; | ||
951 | |||
952 | if (likely(!strcasecmp(it->it_name, t->t_name))) | ||
953 | goto out; | ||
954 | |||
955 | list_for_each_entry(t, &ce->ce_tlist, t_list) { | ||
956 | if (!strcasecmp(t->t_name, it->it_name)) { | ||
957 | ce->ce_tgthint = t; | ||
958 | cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, | ||
959 | it->it_name); | ||
960 | break; | ||
961 | } | ||
962 | } | ||
963 | |||
964 | out: | ||
965 | mutex_unlock(&dfs_cache_list_lock); | ||
966 | free_normalized_path(path, npath); | ||
967 | return rc; | ||
968 | } | ||
969 | |||
970 | /** | ||
971 | * dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry | ||
972 | * without sending any requests to the currently connected server. | ||
973 | * | ||
974 | * NOTE: This function will neither update a cache entry in case it was | ||
975 | * expired, nor create a new cache entry if @path hasn't been found. It heavily | ||
976 | * relies on an existing cache entry. | ||
977 | * | ||
978 | * @path: path to lookup in DFS referral cache. | ||
979 | * @it: target iterator which contains the target hint to update the cache | ||
980 | * entry with. | ||
981 | * | ||
982 | * Return zero if the target hint was updated successfully, otherwise non-zero. | ||
983 | */ | ||
984 | int dfs_cache_noreq_update_tgthint(const char *path, | ||
985 | const struct dfs_cache_tgt_iterator *it) | ||
986 | { | ||
987 | int rc; | ||
988 | char *npath; | ||
989 | struct dfs_cache_entry *ce; | ||
990 | struct dfs_cache_tgt *t; | ||
991 | |||
992 | if (unlikely(!is_path_valid(path)) || !it) | ||
993 | return -EINVAL; | ||
994 | |||
995 | rc = get_normalized_path(path, &npath); | ||
996 | if (rc) | ||
997 | return rc; | ||
998 | |||
999 | cifs_dbg(FYI, "%s: path: %s\n", __func__, npath); | ||
1000 | |||
1001 | mutex_lock(&dfs_cache_list_lock); | ||
1002 | |||
1003 | ce = do_dfs_cache_find(0, NULL, NULL, 0, npath, true); | ||
1004 | if (IS_ERR(ce)) { | ||
1005 | rc = PTR_ERR(ce); | ||
1006 | goto out; | ||
1007 | } | ||
1008 | |||
1009 | rc = 0; | ||
1010 | |||
1011 | t = ce->ce_tgthint; | ||
1012 | |||
1013 | if (unlikely(!strcasecmp(it->it_name, t->t_name))) | ||
1014 | goto out; | ||
1015 | |||
1016 | list_for_each_entry(t, &ce->ce_tlist, t_list) { | ||
1017 | if (!strcasecmp(t->t_name, it->it_name)) { | ||
1018 | ce->ce_tgthint = t; | ||
1019 | cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, | ||
1020 | it->it_name); | ||
1021 | break; | ||
1022 | } | ||
1023 | } | ||
1024 | |||
1025 | out: | ||
1026 | mutex_unlock(&dfs_cache_list_lock); | ||
1027 | free_normalized_path(path, npath); | ||
1028 | return rc; | ||
1029 | } | ||
1030 | |||
1031 | /** | ||
1032 | * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given | ||
1033 | * target iterator (@it). | ||
1034 | * | ||
1035 | * @path: path to lookup in DFS referral cache. | ||
1036 | * @it: DFS target iterator. | ||
1037 | * @ref: DFS referral pointer to set up the gathered information. | ||
1038 | * | ||
1039 | * Return zero if the DFS referral was set up correctly, otherwise non-zero. | ||
1040 | */ | ||
1041 | int dfs_cache_get_tgt_referral(const char *path, | ||
1042 | const struct dfs_cache_tgt_iterator *it, | ||
1043 | struct dfs_info3_param *ref) | ||
1044 | { | ||
1045 | int rc; | ||
1046 | char *npath; | ||
1047 | struct dfs_cache_entry *ce; | ||
1048 | unsigned int h; | ||
1049 | |||
1050 | if (!it || !ref) | ||
1051 | return -EINVAL; | ||
1052 | if (unlikely(!is_path_valid(path))) | ||
1053 | return -EINVAL; | ||
1054 | |||
1055 | rc = get_normalized_path(path, &npath); | ||
1056 | if (rc) | ||
1057 | return rc; | ||
1058 | |||
1059 | cifs_dbg(FYI, "%s: path: %s\n", __func__, npath); | ||
1060 | |||
1061 | mutex_lock(&dfs_cache_list_lock); | ||
1062 | |||
1063 | ce = find_cache_entry(npath, &h); | ||
1064 | if (IS_ERR(ce)) { | ||
1065 | rc = PTR_ERR(ce); | ||
1066 | goto out; | ||
1067 | } | ||
1068 | |||
1069 | cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name); | ||
1070 | |||
1071 | rc = setup_ref(path, ce, ref, it->it_name); | ||
1072 | |||
1073 | out: | ||
1074 | mutex_unlock(&dfs_cache_list_lock); | ||
1075 | free_normalized_path(path, npath); | ||
1076 | return rc; | ||
1077 | } | ||
1078 | |||
1079 | static int dup_vol(struct smb_vol *vol, struct smb_vol *new) | ||
1080 | { | ||
1081 | memcpy(new, vol, sizeof(*new)); | ||
1082 | |||
1083 | if (vol->username) { | ||
1084 | new->username = kstrndup(vol->username, strlen(vol->username), | ||
1085 | GFP_KERNEL); | ||
1086 | if (!new->username) | ||
1087 | return -ENOMEM; | ||
1088 | } | ||
1089 | if (vol->password) { | ||
1090 | new->password = kstrndup(vol->password, strlen(vol->password), | ||
1091 | GFP_KERNEL); | ||
1092 | if (!new->password) | ||
1093 | goto err_free_username; | ||
1094 | } | ||
1095 | if (vol->UNC) { | ||
1096 | cifs_dbg(FYI, "%s: vol->UNC: %s\n", __func__, vol->UNC); | ||
1097 | new->UNC = kstrndup(vol->UNC, strlen(vol->UNC), GFP_KERNEL); | ||
1098 | if (!new->UNC) | ||
1099 | goto err_free_password; | ||
1100 | } | ||
1101 | if (vol->domainname) { | ||
1102 | new->domainname = kstrndup(vol->domainname, | ||
1103 | strlen(vol->domainname), GFP_KERNEL); | ||
1104 | if (!new->domainname) | ||
1105 | goto err_free_unc; | ||
1106 | } | ||
1107 | if (vol->iocharset) { | ||
1108 | new->iocharset = kstrndup(vol->iocharset, | ||
1109 | strlen(vol->iocharset), GFP_KERNEL); | ||
1110 | if (!new->iocharset) | ||
1111 | goto err_free_domainname; | ||
1112 | } | ||
1113 | if (vol->prepath) { | ||
1114 | cifs_dbg(FYI, "%s: vol->prepath: %s\n", __func__, vol->prepath); | ||
1115 | new->prepath = kstrndup(vol->prepath, strlen(vol->prepath), | ||
1116 | GFP_KERNEL); | ||
1117 | if (!new->prepath) | ||
1118 | goto err_free_iocharset; | ||
1119 | } | ||
1120 | |||
1121 | return 0; | ||
1122 | |||
1123 | err_free_iocharset: | ||
1124 | kfree(new->iocharset); | ||
1125 | err_free_domainname: | ||
1126 | kfree(new->domainname); | ||
1127 | err_free_unc: | ||
1128 | kfree(new->UNC); | ||
1129 | err_free_password: | ||
1130 | kzfree(new->password); | ||
1131 | err_free_username: | ||
1132 | kfree(new->username); | ||
1133 | kfree(new); | ||
1134 | return -ENOMEM; | ||
1135 | } | ||
1136 | |||
1137 | /** | ||
1138 | * dfs_cache_add_vol - add a cifs volume during mount() that will be handled by | ||
1139 | * DFS cache refresh worker. | ||
1140 | * | ||
1141 | * @vol: cifs volume. | ||
1142 | * @fullpath: origin full path. | ||
1143 | * | ||
1144 | * Return zero if volume was set up correctly, otherwise non-zero. | ||
1145 | */ | ||
1146 | int dfs_cache_add_vol(struct smb_vol *vol, const char *fullpath) | ||
1147 | { | ||
1148 | int rc; | ||
1149 | struct dfs_cache_vol_info *vi; | ||
1150 | |||
1151 | if (!vol || !fullpath) | ||
1152 | return -EINVAL; | ||
1153 | |||
1154 | cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath); | ||
1155 | |||
1156 | vi = kzalloc(sizeof(*vi), GFP_KERNEL); | ||
1157 | if (!vi) | ||
1158 | return -ENOMEM; | ||
1159 | |||
1160 | vi->vi_fullpath = kstrndup(fullpath, strlen(fullpath), GFP_KERNEL); | ||
1161 | if (!vi->vi_fullpath) { | ||
1162 | rc = -ENOMEM; | ||
1163 | goto err_free_vi; | ||
1164 | } | ||
1165 | |||
1166 | rc = dup_vol(vol, &vi->vi_vol); | ||
1167 | if (rc) | ||
1168 | goto err_free_fullpath; | ||
1169 | |||
1170 | mutex_lock(&dfs_cache.dc_lock); | ||
1171 | list_add_tail(&vi->vi_list, &dfs_cache.dc_vol_list); | ||
1172 | mutex_unlock(&dfs_cache.dc_lock); | ||
1173 | return 0; | ||
1174 | |||
1175 | err_free_fullpath: | ||
1176 | kfree(vi->vi_fullpath); | ||
1177 | err_free_vi: | ||
1178 | kfree(vi); | ||
1179 | return rc; | ||
1180 | } | ||
1181 | |||
1182 | static inline struct dfs_cache_vol_info *find_vol(const char *fullpath) | ||
1183 | { | ||
1184 | struct dfs_cache_vol_info *vi; | ||
1185 | |||
1186 | list_for_each_entry(vi, &dfs_cache.dc_vol_list, vi_list) { | ||
1187 | cifs_dbg(FYI, "%s: vi->vi_fullpath: %s\n", __func__, | ||
1188 | vi->vi_fullpath); | ||
1189 | if (!strcasecmp(vi->vi_fullpath, fullpath)) | ||
1190 | return vi; | ||
1191 | } | ||
1192 | return ERR_PTR(-ENOENT); | ||
1193 | } | ||
1194 | |||
1195 | /** | ||
1196 | * dfs_cache_update_vol - update vol info in DFS cache after failover | ||
1197 | * | ||
1198 | * @fullpath: fullpath to look up in volume list. | ||
1199 | * @server: TCP ses pointer. | ||
1200 | * | ||
1201 | * Return zero if volume was updated, otherwise non-zero. | ||
1202 | */ | ||
1203 | int dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server) | ||
1204 | { | ||
1205 | int rc; | ||
1206 | struct dfs_cache_vol_info *vi; | ||
1207 | |||
1208 | if (!fullpath || !server) | ||
1209 | return -EINVAL; | ||
1210 | |||
1211 | cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath); | ||
1212 | |||
1213 | mutex_lock(&dfs_cache.dc_lock); | ||
1214 | |||
1215 | vi = find_vol(fullpath); | ||
1216 | if (IS_ERR(vi)) { | ||
1217 | rc = PTR_ERR(vi); | ||
1218 | goto out; | ||
1219 | } | ||
1220 | |||
1221 | cifs_dbg(FYI, "%s: updating volume info\n", __func__); | ||
1222 | memcpy(&vi->vi_vol.dstaddr, &server->dstaddr, | ||
1223 | sizeof(vi->vi_vol.dstaddr)); | ||
1224 | rc = 0; | ||
1225 | |||
1226 | out: | ||
1227 | mutex_unlock(&dfs_cache.dc_lock); | ||
1228 | return rc; | ||
1229 | } | ||
1230 | |||
1231 | /** | ||
1232 | * dfs_cache_del_vol - remove volume info in DFS cache during umount() | ||
1233 | * | ||
1234 | * @fullpath: fullpath to look up in volume list. | ||
1235 | */ | ||
1236 | void dfs_cache_del_vol(const char *fullpath) | ||
1237 | { | ||
1238 | struct dfs_cache_vol_info *vi; | ||
1239 | |||
1240 | if (!fullpath || !*fullpath) | ||
1241 | return; | ||
1242 | |||
1243 | cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath); | ||
1244 | |||
1245 | mutex_lock(&dfs_cache.dc_lock); | ||
1246 | vi = find_vol(fullpath); | ||
1247 | if (!IS_ERR(vi)) | ||
1248 | free_vol(vi); | ||
1249 | mutex_unlock(&dfs_cache.dc_lock); | ||
1250 | } | ||
1251 | |||
1252 | /* Get all tcons that are within a DFS namespace and can be refreshed */ | ||
1253 | static void get_tcons(struct TCP_Server_Info *server, struct list_head *head) | ||
1254 | { | ||
1255 | struct cifs_ses *ses; | ||
1256 | struct cifs_tcon *tcon; | ||
1257 | |||
1258 | INIT_LIST_HEAD(head); | ||
1259 | |||
1260 | spin_lock(&cifs_tcp_ses_lock); | ||
1261 | list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { | ||
1262 | list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { | ||
1263 | if (!tcon->need_reconnect && !tcon->need_reopen_files && | ||
1264 | tcon->dfs_path) { | ||
1265 | tcon->tc_count++; | ||
1266 | list_add_tail(&tcon->ulist, head); | ||
1267 | } | ||
1268 | } | ||
1269 | if (ses->tcon_ipc && !ses->tcon_ipc->need_reconnect && | ||
1270 | ses->tcon_ipc->dfs_path) { | ||
1271 | list_add_tail(&ses->tcon_ipc->ulist, head); | ||
1272 | } | ||
1273 | } | ||
1274 | spin_unlock(&cifs_tcp_ses_lock); | ||
1275 | } | ||
1276 | |||
1277 | /* Refresh DFS cache entry from a given tcon */ | ||
1278 | static void do_refresh_tcon(struct dfs_cache *dc, struct cifs_tcon *tcon) | ||
1279 | { | ||
1280 | int rc = 0; | ||
1281 | unsigned int xid; | ||
1282 | char *path, *npath; | ||
1283 | unsigned int h; | ||
1284 | struct dfs_cache_entry *ce; | ||
1285 | struct dfs_info3_param *refs = NULL; | ||
1286 | int numrefs = 0; | ||
1287 | |||
1288 | xid = get_xid(); | ||
1289 | |||
1290 | path = tcon->dfs_path + 1; | ||
1291 | |||
1292 | rc = get_normalized_path(path, &npath); | ||
1293 | if (rc) | ||
1294 | goto out; | ||
1295 | |||
1296 | mutex_lock(&dfs_cache_list_lock); | ||
1297 | ce = find_cache_entry(npath, &h); | ||
1298 | mutex_unlock(&dfs_cache_list_lock); | ||
1299 | |||
1300 | if (IS_ERR(ce)) { | ||
1301 | rc = PTR_ERR(ce); | ||
1302 | goto out; | ||
1303 | } | ||
1304 | |||
1305 | if (!cache_entry_expired(ce)) | ||
1306 | goto out; | ||
1307 | |||
1308 | if (unlikely(!tcon->ses->server->ops->get_dfs_refer)) { | ||
1309 | rc = -EOPNOTSUPP; | ||
1310 | } else { | ||
1311 | rc = tcon->ses->server->ops->get_dfs_refer(xid, tcon->ses, path, | ||
1312 | &refs, &numrefs, | ||
1313 | dc->dc_nlsc, | ||
1314 | tcon->remap); | ||
1315 | if (!rc) { | ||
1316 | mutex_lock(&dfs_cache_list_lock); | ||
1317 | ce = __update_cache_entry(npath, refs, numrefs); | ||
1318 | mutex_unlock(&dfs_cache_list_lock); | ||
1319 | dump_refs(refs, numrefs); | ||
1320 | free_dfs_info_array(refs, numrefs); | ||
1321 | if (IS_ERR(ce)) | ||
1322 | rc = PTR_ERR(ce); | ||
1323 | } | ||
1324 | } | ||
1325 | if (rc) | ||
1326 | cifs_dbg(FYI, "%s: failed to update expired entry\n", __func__); | ||
1327 | out: | ||
1328 | free_xid(xid); | ||
1329 | free_normalized_path(path, npath); | ||
1330 | } | ||
1331 | |||
1332 | /* | ||
1333 | * Worker that will refresh DFS cache based on lowest TTL value from a DFS | ||
1334 | * referral. | ||
1335 | * | ||
1336 | * FIXME: ensure that all requests are sent to DFS root for refreshing the | ||
1337 | * cache. | ||
1338 | */ | ||
1339 | static void refresh_cache_worker(struct work_struct *work) | ||
1340 | { | ||
1341 | struct dfs_cache *dc = container_of(work, struct dfs_cache, | ||
1342 | dc_refresh.work); | ||
1343 | struct dfs_cache_vol_info *vi; | ||
1344 | struct TCP_Server_Info *server; | ||
1345 | LIST_HEAD(list); | ||
1346 | struct cifs_tcon *tcon, *ntcon; | ||
1347 | |||
1348 | mutex_lock(&dc->dc_lock); | ||
1349 | |||
1350 | list_for_each_entry(vi, &dc->dc_vol_list, vi_list) { | ||
1351 | server = cifs_find_tcp_session(&vi->vi_vol); | ||
1352 | if (IS_ERR_OR_NULL(server)) | ||
1353 | continue; | ||
1354 | if (server->tcpStatus != CifsGood) | ||
1355 | goto next; | ||
1356 | get_tcons(server, &list); | ||
1357 | list_for_each_entry_safe(tcon, ntcon, &list, ulist) { | ||
1358 | do_refresh_tcon(dc, tcon); | ||
1359 | list_del_init(&tcon->ulist); | ||
1360 | cifs_put_tcon(tcon); | ||
1361 | } | ||
1362 | next: | ||
1363 | cifs_put_tcp_session(server, 0); | ||
1364 | } | ||
1365 | queue_delayed_work(cifsiod_wq, &dc->dc_refresh, dc->dc_ttl * HZ); | ||
1366 | mutex_unlock(&dc->dc_lock); | ||
1367 | } | ||
diff --git a/fs/cifs/dfs_cache.h b/fs/cifs/dfs_cache.h new file mode 100644 index 000000000000..22f366514f3a --- /dev/null +++ b/fs/cifs/dfs_cache.h | |||
@@ -0,0 +1,97 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * DFS referral cache routines | ||
4 | * | ||
5 | * Copyright (c) 2018 Paulo Alcantara <palcantara@suse.de> | ||
6 | */ | ||
7 | |||
8 | #ifndef _CIFS_DFS_CACHE_H | ||
9 | #define _CIFS_DFS_CACHE_H | ||
10 | |||
11 | #include <linux/nls.h> | ||
12 | #include <linux/list.h> | ||
13 | #include "cifsglob.h" | ||
14 | |||
15 | struct dfs_cache_tgt_list { | ||
16 | int tl_numtgts; | ||
17 | struct list_head tl_list; | ||
18 | }; | ||
19 | |||
20 | struct dfs_cache_tgt_iterator { | ||
21 | char *it_name; | ||
22 | struct list_head it_list; | ||
23 | }; | ||
24 | |||
25 | extern int dfs_cache_init(void); | ||
26 | extern void dfs_cache_destroy(void); | ||
27 | extern const struct file_operations dfscache_proc_fops; | ||
28 | |||
29 | extern int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, | ||
30 | const struct nls_table *nls_codepage, int remap, | ||
31 | const char *path, struct dfs_info3_param *ref, | ||
32 | struct dfs_cache_tgt_list *tgt_list); | ||
33 | extern int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, | ||
34 | struct dfs_cache_tgt_list *tgt_list); | ||
35 | extern int dfs_cache_update_tgthint(const unsigned int xid, | ||
36 | struct cifs_ses *ses, | ||
37 | const struct nls_table *nls_codepage, | ||
38 | int remap, const char *path, | ||
39 | const struct dfs_cache_tgt_iterator *it); | ||
40 | extern int | ||
41 | dfs_cache_noreq_update_tgthint(const char *path, | ||
42 | const struct dfs_cache_tgt_iterator *it); | ||
43 | extern int dfs_cache_get_tgt_referral(const char *path, | ||
44 | const struct dfs_cache_tgt_iterator *it, | ||
45 | struct dfs_info3_param *ref); | ||
46 | extern int dfs_cache_add_vol(struct smb_vol *vol, const char *fullpath); | ||
47 | extern int dfs_cache_update_vol(const char *fullpath, | ||
48 | struct TCP_Server_Info *server); | ||
49 | extern void dfs_cache_del_vol(const char *fullpath); | ||
50 | |||
51 | static inline struct dfs_cache_tgt_iterator * | ||
52 | dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl, | ||
53 | struct dfs_cache_tgt_iterator *it) | ||
54 | { | ||
55 | if (!tl || list_empty(&tl->tl_list) || !it || | ||
56 | list_is_last(&it->it_list, &tl->tl_list)) | ||
57 | return NULL; | ||
58 | return list_next_entry(it, it_list); | ||
59 | } | ||
60 | |||
61 | static inline struct dfs_cache_tgt_iterator * | ||
62 | dfs_cache_get_tgt_iterator(struct dfs_cache_tgt_list *tl) | ||
63 | { | ||
64 | if (!tl) | ||
65 | return NULL; | ||
66 | return list_first_entry_or_null(&tl->tl_list, | ||
67 | struct dfs_cache_tgt_iterator, | ||
68 | it_list); | ||
69 | } | ||
70 | |||
71 | static inline void dfs_cache_free_tgts(struct dfs_cache_tgt_list *tl) | ||
72 | { | ||
73 | struct dfs_cache_tgt_iterator *it, *nit; | ||
74 | |||
75 | if (!tl || list_empty(&tl->tl_list)) | ||
76 | return; | ||
77 | list_for_each_entry_safe(it, nit, &tl->tl_list, it_list) { | ||
78 | list_del(&it->it_list); | ||
79 | kfree(it->it_name); | ||
80 | kfree(it); | ||
81 | } | ||
82 | tl->tl_numtgts = 0; | ||
83 | } | ||
84 | |||
85 | static inline const char * | ||
86 | dfs_cache_get_tgt_name(const struct dfs_cache_tgt_iterator *it) | ||
87 | { | ||
88 | return it ? it->it_name : NULL; | ||
89 | } | ||
90 | |||
91 | static inline int | ||
92 | dfs_cache_get_nr_tgts(const struct dfs_cache_tgt_list *tl) | ||
93 | { | ||
94 | return tl ? tl->tl_numtgts : 0; | ||
95 | } | ||
96 | |||
97 | #endif /* _CIFS_DFS_CACHE_H */ | ||
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 6706328ce03f..5e405164394a 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -2617,11 +2617,13 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, | |||
2617 | if (rc) | 2617 | if (rc) |
2618 | break; | 2618 | break; |
2619 | 2619 | ||
2620 | cur_len = min_t(const size_t, len, wsize); | ||
2621 | |||
2620 | if (ctx->direct_io) { | 2622 | if (ctx->direct_io) { |
2621 | ssize_t result; | 2623 | ssize_t result; |
2622 | 2624 | ||
2623 | result = iov_iter_get_pages_alloc( | 2625 | result = iov_iter_get_pages_alloc( |
2624 | from, &pagevec, wsize, &start); | 2626 | from, &pagevec, cur_len, &start); |
2625 | if (result < 0) { | 2627 | if (result < 0) { |
2626 | cifs_dbg(VFS, | 2628 | cifs_dbg(VFS, |
2627 | "direct_writev couldn't get user pages " | 2629 | "direct_writev couldn't get user pages " |
@@ -2630,6 +2632,9 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, | |||
2630 | result, from->type, | 2632 | result, from->type, |
2631 | from->iov_offset, from->count); | 2633 | from->iov_offset, from->count); |
2632 | dump_stack(); | 2634 | dump_stack(); |
2635 | |||
2636 | rc = result; | ||
2637 | add_credits_and_wake_if(server, credits, 0); | ||
2633 | break; | 2638 | break; |
2634 | } | 2639 | } |
2635 | cur_len = (size_t)result; | 2640 | cur_len = (size_t)result; |
@@ -3313,13 +3318,16 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, | |||
3313 | cur_len, &start); | 3318 | cur_len, &start); |
3314 | if (result < 0) { | 3319 | if (result < 0) { |
3315 | cifs_dbg(VFS, | 3320 | cifs_dbg(VFS, |
3316 | "couldn't get user pages (cur_len=%zd)" | 3321 | "couldn't get user pages (rc=%zd)" |
3317 | " iter type %d" | 3322 | " iter type %d" |
3318 | " iov_offset %zd count %zd\n", | 3323 | " iov_offset %zd count %zd\n", |
3319 | result, direct_iov.type, | 3324 | result, direct_iov.type, |
3320 | direct_iov.iov_offset, | 3325 | direct_iov.iov_offset, |
3321 | direct_iov.count); | 3326 | direct_iov.count); |
3322 | dump_stack(); | 3327 | dump_stack(); |
3328 | |||
3329 | rc = result; | ||
3330 | add_credits_and_wake_if(server, credits, 0); | ||
3323 | break; | 3331 | break; |
3324 | } | 3332 | } |
3325 | cur_len = (size_t)result; | 3333 | cur_len = (size_t)result; |
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index a81a9df997c1..13fb59aadebc 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
@@ -333,7 +333,7 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb) | |||
333 | fattr->cf_mtime = timespec64_trunc(fattr->cf_mtime, sb->s_time_gran); | 333 | fattr->cf_mtime = timespec64_trunc(fattr->cf_mtime, sb->s_time_gran); |
334 | fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime; | 334 | fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime; |
335 | fattr->cf_nlink = 2; | 335 | fattr->cf_nlink = 2; |
336 | fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL; | 336 | fattr->cf_flags = CIFS_FATTR_DFS_REFERRAL; |
337 | } | 337 | } |
338 | 338 | ||
339 | static int | 339 | static int |
@@ -730,7 +730,6 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, | |||
730 | FILE_ALL_INFO *data, struct super_block *sb, int xid, | 730 | FILE_ALL_INFO *data, struct super_block *sb, int xid, |
731 | const struct cifs_fid *fid) | 731 | const struct cifs_fid *fid) |
732 | { | 732 | { |
733 | bool validinum = false; | ||
734 | __u16 srchflgs; | 733 | __u16 srchflgs; |
735 | int rc = 0, tmprc = ENOSYS; | 734 | int rc = 0, tmprc = ENOSYS; |
736 | struct cifs_tcon *tcon; | 735 | struct cifs_tcon *tcon; |
@@ -821,7 +820,6 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, | |||
821 | (FILE_DIRECTORY_INFO *)data, cifs_sb); | 820 | (FILE_DIRECTORY_INFO *)data, cifs_sb); |
822 | fattr.cf_uniqueid = le64_to_cpu( | 821 | fattr.cf_uniqueid = le64_to_cpu( |
823 | ((SEARCH_ID_FULL_DIR_INFO *)data)->UniqueId); | 822 | ((SEARCH_ID_FULL_DIR_INFO *)data)->UniqueId); |
824 | validinum = true; | ||
825 | 823 | ||
826 | cifs_buf_release(srchinf->ntwrk_buf_start); | 824 | cifs_buf_release(srchinf->ntwrk_buf_start); |
827 | } | 825 | } |
@@ -840,31 +838,29 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, | |||
840 | */ | 838 | */ |
841 | if (*inode == NULL) { | 839 | if (*inode == NULL) { |
842 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { | 840 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { |
843 | if (validinum == false) { | 841 | if (server->ops->get_srv_inum) |
844 | if (server->ops->get_srv_inum) | 842 | tmprc = server->ops->get_srv_inum(xid, |
845 | tmprc = server->ops->get_srv_inum(xid, | 843 | tcon, cifs_sb, full_path, |
846 | tcon, cifs_sb, full_path, | 844 | &fattr.cf_uniqueid, data); |
847 | &fattr.cf_uniqueid, data); | 845 | if (tmprc) { |
848 | if (tmprc) { | 846 | cifs_dbg(FYI, "GetSrvInodeNum rc %d\n", |
849 | cifs_dbg(FYI, "GetSrvInodeNum rc %d\n", | 847 | tmprc); |
850 | tmprc); | 848 | fattr.cf_uniqueid = iunique(sb, ROOT_I); |
851 | fattr.cf_uniqueid = iunique(sb, ROOT_I); | 849 | cifs_autodisable_serverino(cifs_sb); |
852 | cifs_autodisable_serverino(cifs_sb); | 850 | } else if ((fattr.cf_uniqueid == 0) && |
853 | } else if ((fattr.cf_uniqueid == 0) && | 851 | strlen(full_path) == 0) { |
854 | strlen(full_path) == 0) { | 852 | /* some servers ret bad root ino ie 0 */ |
855 | /* some servers ret bad root ino ie 0 */ | 853 | cifs_dbg(FYI, "Invalid (0) inodenum\n"); |
856 | cifs_dbg(FYI, "Invalid (0) inodenum\n"); | 854 | fattr.cf_flags |= |
857 | fattr.cf_flags |= | 855 | CIFS_FATTR_FAKE_ROOT_INO; |
858 | CIFS_FATTR_FAKE_ROOT_INO; | 856 | fattr.cf_uniqueid = |
859 | fattr.cf_uniqueid = | 857 | simple_hashstr(tcon->treeName); |
860 | simple_hashstr(tcon->treeName); | ||
861 | } | ||
862 | } | 858 | } |
863 | } else | 859 | } else |
864 | fattr.cf_uniqueid = iunique(sb, ROOT_I); | 860 | fattr.cf_uniqueid = iunique(sb, ROOT_I); |
865 | } else { | 861 | } else { |
866 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) && | 862 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) |
867 | validinum == false && server->ops->get_srv_inum) { | 863 | && server->ops->get_srv_inum) { |
868 | /* | 864 | /* |
869 | * Pass a NULL tcon to ensure we don't make a round | 865 | * Pass a NULL tcon to ensure we don't make a round |
870 | * trip to the server. This only works for SMB2+. | 866 | * trip to the server. This only works for SMB2+. |
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 8a41f4eba726..bee203055b30 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c | |||
@@ -111,21 +111,27 @@ struct cifs_tcon * | |||
111 | tconInfoAlloc(void) | 111 | tconInfoAlloc(void) |
112 | { | 112 | { |
113 | struct cifs_tcon *ret_buf; | 113 | struct cifs_tcon *ret_buf; |
114 | ret_buf = kzalloc(sizeof(struct cifs_tcon), GFP_KERNEL); | 114 | |
115 | if (ret_buf) { | 115 | ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL); |
116 | atomic_inc(&tconInfoAllocCount); | 116 | if (!ret_buf) |
117 | ret_buf->tidStatus = CifsNew; | 117 | return NULL; |
118 | ++ret_buf->tc_count; | 118 | ret_buf->crfid.fid = kzalloc(sizeof(*ret_buf->crfid.fid), GFP_KERNEL); |
119 | INIT_LIST_HEAD(&ret_buf->openFileList); | 119 | if (!ret_buf->crfid.fid) { |
120 | INIT_LIST_HEAD(&ret_buf->tcon_list); | 120 | kfree(ret_buf); |
121 | spin_lock_init(&ret_buf->open_file_lock); | 121 | return NULL; |
122 | mutex_init(&ret_buf->crfid.fid_mutex); | ||
123 | ret_buf->crfid.fid = kzalloc(sizeof(struct cifs_fid), | ||
124 | GFP_KERNEL); | ||
125 | spin_lock_init(&ret_buf->stat_lock); | ||
126 | atomic_set(&ret_buf->num_local_opens, 0); | ||
127 | atomic_set(&ret_buf->num_remote_opens, 0); | ||
128 | } | 122 | } |
123 | |||
124 | atomic_inc(&tconInfoAllocCount); | ||
125 | ret_buf->tidStatus = CifsNew; | ||
126 | ++ret_buf->tc_count; | ||
127 | INIT_LIST_HEAD(&ret_buf->openFileList); | ||
128 | INIT_LIST_HEAD(&ret_buf->tcon_list); | ||
129 | spin_lock_init(&ret_buf->open_file_lock); | ||
130 | mutex_init(&ret_buf->crfid.fid_mutex); | ||
131 | spin_lock_init(&ret_buf->stat_lock); | ||
132 | atomic_set(&ret_buf->num_local_opens, 0); | ||
133 | atomic_set(&ret_buf->num_remote_opens, 0); | ||
134 | |||
129 | return ret_buf; | 135 | return ret_buf; |
130 | } | 136 | } |
131 | 137 | ||
@@ -140,6 +146,9 @@ tconInfoFree(struct cifs_tcon *buf_to_free) | |||
140 | kfree(buf_to_free->nativeFileSystem); | 146 | kfree(buf_to_free->nativeFileSystem); |
141 | kzfree(buf_to_free->password); | 147 | kzfree(buf_to_free->password); |
142 | kfree(buf_to_free->crfid.fid); | 148 | kfree(buf_to_free->crfid.fid); |
149 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
150 | kfree(buf_to_free->dfs_path); | ||
151 | #endif | ||
143 | kfree(buf_to_free); | 152 | kfree(buf_to_free); |
144 | } | 153 | } |
145 | 154 | ||
@@ -525,9 +534,17 @@ void | |||
525 | cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) | 534 | cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) |
526 | { | 535 | { |
527 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { | 536 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { |
537 | struct cifs_tcon *tcon = NULL; | ||
538 | |||
539 | if (cifs_sb->master_tlink) | ||
540 | tcon = cifs_sb_master_tcon(cifs_sb); | ||
541 | |||
528 | cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; | 542 | cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; |
529 | cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s. This server doesn't seem to support them properly. Hardlinks will not be recognized on this mount. Consider mounting with the \"noserverino\" option to silence this message.\n", | 543 | cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s.\n", |
530 | cifs_sb_master_tcon(cifs_sb)->treeName); | 544 | tcon ? tcon->treeName : "new server"); |
545 | cifs_dbg(VFS, "The server doesn't seem to support them properly or the files might be on different servers (DFS).\n"); | ||
546 | cifs_dbg(VFS, "Hardlinks will not be recognized on this mount. Consider mounting with the \"noserverino\" option to silence this message.\n"); | ||
547 | |||
531 | } | 548 | } |
532 | } | 549 | } |
533 | 550 | ||
@@ -732,6 +749,8 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size, | |||
732 | goto parse_DFS_referrals_exit; | 749 | goto parse_DFS_referrals_exit; |
733 | } | 750 | } |
734 | 751 | ||
752 | node->ttl = le32_to_cpu(ref->TimeToLive); | ||
753 | |||
735 | ref++; | 754 | ref++; |
736 | } | 755 | } |
737 | 756 | ||
@@ -933,3 +952,20 @@ void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page, | |||
933 | else if (page == 0) | 952 | else if (page == 0) |
934 | *len = rqst->rq_pagesz - rqst->rq_offset; | 953 | *len = rqst->rq_pagesz - rqst->rq_offset; |
935 | } | 954 | } |
955 | |||
956 | void extract_unc_hostname(const char *unc, const char **h, size_t *len) | ||
957 | { | ||
958 | const char *end; | ||
959 | |||
960 | /* skip initial slashes */ | ||
961 | while (*unc && (*unc == '\\' || *unc == '/')) | ||
962 | unc++; | ||
963 | |||
964 | end = unc; | ||
965 | |||
966 | while (*end && !(*end == '\\' || *end == '/')) | ||
967 | end++; | ||
968 | |||
969 | *h = unc; | ||
970 | *len = end - unc; | ||
971 | } | ||
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index e169e1a5fd35..3925a7bfc74d 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c | |||
@@ -655,7 +655,14 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, | |||
655 | /* scan and find it */ | 655 | /* scan and find it */ |
656 | int i; | 656 | int i; |
657 | char *cur_ent; | 657 | char *cur_ent; |
658 | char *end_of_smb = cfile->srch_inf.ntwrk_buf_start + | 658 | char *end_of_smb; |
659 | |||
660 | if (cfile->srch_inf.ntwrk_buf_start == NULL) { | ||
661 | cifs_dbg(VFS, "ntwrk_buf_start is NULL during readdir\n"); | ||
662 | return -EIO; | ||
663 | } | ||
664 | |||
665 | end_of_smb = cfile->srch_inf.ntwrk_buf_start + | ||
659 | server->ops->calc_smb_size( | 666 | server->ops->calc_smb_size( |
660 | cfile->srch_inf.ntwrk_buf_start, | 667 | cfile->srch_inf.ntwrk_buf_start, |
661 | server); | 668 | server); |
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index aa23c00367ec..dcd49ad60c83 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c | |||
@@ -534,9 +534,9 @@ cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) | |||
534 | if (global_secflags & CIFSSEC_MAY_NTLM) | 534 | if (global_secflags & CIFSSEC_MAY_NTLM) |
535 | return NTLM; | 535 | return NTLM; |
536 | default: | 536 | default: |
537 | /* Fallthrough to attempt LANMAN authentication next */ | ||
538 | break; | 537 | break; |
539 | } | 538 | } |
539 | /* Fallthrough - to attempt LANMAN authentication next */ | ||
540 | case CIFS_NEGFLAVOR_LANMAN: | 540 | case CIFS_NEGFLAVOR_LANMAN: |
541 | switch (requested) { | 541 | switch (requested) { |
542 | case LANMAN: | 542 | case LANMAN: |
@@ -1154,14 +1154,12 @@ out: | |||
1154 | static int | 1154 | static int |
1155 | _sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data) | 1155 | _sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data) |
1156 | { | 1156 | { |
1157 | struct smb_hdr *smb_buf; | ||
1158 | SESSION_SETUP_ANDX *pSMB; | 1157 | SESSION_SETUP_ANDX *pSMB; |
1159 | struct cifs_ses *ses = sess_data->ses; | 1158 | struct cifs_ses *ses = sess_data->ses; |
1160 | __u32 capabilities; | 1159 | __u32 capabilities; |
1161 | char *bcc_ptr; | 1160 | char *bcc_ptr; |
1162 | 1161 | ||
1163 | pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; | 1162 | pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; |
1164 | smb_buf = (struct smb_hdr *)pSMB; | ||
1165 | 1163 | ||
1166 | capabilities = cifs_ssetup_hdr(ses, pSMB); | 1164 | capabilities = cifs_ssetup_hdr(ses, pSMB); |
1167 | if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) { | 1165 | if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) { |
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 378151e09e91..32a6c020478f 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c | |||
@@ -929,19 +929,18 @@ cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon, | |||
929 | { | 929 | { |
930 | #ifdef CONFIG_CIFS_DFS_UPCALL | 930 | #ifdef CONFIG_CIFS_DFS_UPCALL |
931 | int rc; | 931 | int rc; |
932 | unsigned int num_referrals = 0; | 932 | struct dfs_info3_param referral = {0}; |
933 | struct dfs_info3_param *referrals = NULL; | ||
934 | 933 | ||
935 | rc = get_dfs_path(xid, tcon->ses, searchName, nls_codepage, | 934 | rc = get_dfs_path(xid, tcon->ses, searchName, nls_codepage, &referral, |
936 | &num_referrals, &referrals, 0); | 935 | 0); |
937 | 936 | ||
938 | if (!rc && num_referrals > 0) { | 937 | if (!rc) { |
939 | *symlinkinfo = kstrndup(referrals->node_name, | 938 | *symlinkinfo = kstrndup(referral.node_name, |
940 | strlen(referrals->node_name), | 939 | strlen(referral.node_name), |
941 | GFP_KERNEL); | 940 | GFP_KERNEL); |
941 | free_dfs_info_param(&referral); | ||
942 | if (!*symlinkinfo) | 942 | if (!*symlinkinfo) |
943 | rc = -ENOMEM; | 943 | rc = -ENOMEM; |
944 | free_dfs_info_array(referrals, num_referrals); | ||
945 | } | 944 | } |
946 | return rc; | 945 | return rc; |
947 | #else /* No DFS support */ | 946 | #else /* No DFS support */ |
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index a8999f930b22..f14533da3a93 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c | |||
@@ -49,7 +49,6 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, | |||
49 | struct cifs_open_parms oparms; | 49 | struct cifs_open_parms oparms; |
50 | struct cifs_fid fid; | 50 | struct cifs_fid fid; |
51 | struct cifs_ses *ses = tcon->ses; | 51 | struct cifs_ses *ses = tcon->ses; |
52 | struct TCP_Server_Info *server = ses->server; | ||
53 | int num_rqst = 0; | 52 | int num_rqst = 0; |
54 | struct smb_rqst rqst[3]; | 53 | struct smb_rqst rqst[3]; |
55 | int resp_buftype[3]; | 54 | int resp_buftype[3]; |
@@ -97,7 +96,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, | |||
97 | if (rc) | 96 | if (rc) |
98 | goto finished; | 97 | goto finished; |
99 | 98 | ||
100 | smb2_set_next_command(server, &rqst[num_rqst++], 0); | 99 | smb2_set_next_command(tcon, &rqst[num_rqst++]); |
101 | 100 | ||
102 | /* Operation */ | 101 | /* Operation */ |
103 | switch (command) { | 102 | switch (command) { |
@@ -111,7 +110,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, | |||
111 | SMB2_O_INFO_FILE, 0, | 110 | SMB2_O_INFO_FILE, 0, |
112 | sizeof(struct smb2_file_all_info) + | 111 | sizeof(struct smb2_file_all_info) + |
113 | PATH_MAX * 2, 0, NULL); | 112 | PATH_MAX * 2, 0, NULL); |
114 | smb2_set_next_command(server, &rqst[num_rqst], 0); | 113 | smb2_set_next_command(tcon, &rqst[num_rqst]); |
115 | smb2_set_related(&rqst[num_rqst++]); | 114 | smb2_set_related(&rqst[num_rqst++]); |
116 | break; | 115 | break; |
117 | case SMB2_OP_DELETE: | 116 | case SMB2_OP_DELETE: |
@@ -134,7 +133,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, | |||
134 | COMPOUND_FID, current->tgid, | 133 | COMPOUND_FID, current->tgid, |
135 | FILE_DISPOSITION_INFORMATION, | 134 | FILE_DISPOSITION_INFORMATION, |
136 | SMB2_O_INFO_FILE, 0, data, size); | 135 | SMB2_O_INFO_FILE, 0, data, size); |
137 | smb2_set_next_command(server, &rqst[num_rqst], 1); | 136 | smb2_set_next_command(tcon, &rqst[num_rqst]); |
138 | smb2_set_related(&rqst[num_rqst++]); | 137 | smb2_set_related(&rqst[num_rqst++]); |
139 | break; | 138 | break; |
140 | case SMB2_OP_SET_EOF: | 139 | case SMB2_OP_SET_EOF: |
@@ -149,7 +148,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, | |||
149 | COMPOUND_FID, current->tgid, | 148 | COMPOUND_FID, current->tgid, |
150 | FILE_END_OF_FILE_INFORMATION, | 149 | FILE_END_OF_FILE_INFORMATION, |
151 | SMB2_O_INFO_FILE, 0, data, size); | 150 | SMB2_O_INFO_FILE, 0, data, size); |
152 | smb2_set_next_command(server, &rqst[num_rqst], 0); | 151 | smb2_set_next_command(tcon, &rqst[num_rqst]); |
153 | smb2_set_related(&rqst[num_rqst++]); | 152 | smb2_set_related(&rqst[num_rqst++]); |
154 | break; | 153 | break; |
155 | case SMB2_OP_SET_INFO: | 154 | case SMB2_OP_SET_INFO: |
@@ -165,7 +164,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, | |||
165 | COMPOUND_FID, current->tgid, | 164 | COMPOUND_FID, current->tgid, |
166 | FILE_BASIC_INFORMATION, | 165 | FILE_BASIC_INFORMATION, |
167 | SMB2_O_INFO_FILE, 0, data, size); | 166 | SMB2_O_INFO_FILE, 0, data, size); |
168 | smb2_set_next_command(server, &rqst[num_rqst], 0); | 167 | smb2_set_next_command(tcon, &rqst[num_rqst]); |
169 | smb2_set_related(&rqst[num_rqst++]); | 168 | smb2_set_related(&rqst[num_rqst++]); |
170 | break; | 169 | break; |
171 | case SMB2_OP_RENAME: | 170 | case SMB2_OP_RENAME: |
@@ -189,7 +188,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, | |||
189 | COMPOUND_FID, current->tgid, | 188 | COMPOUND_FID, current->tgid, |
190 | FILE_RENAME_INFORMATION, | 189 | FILE_RENAME_INFORMATION, |
191 | SMB2_O_INFO_FILE, 0, data, size); | 190 | SMB2_O_INFO_FILE, 0, data, size); |
192 | smb2_set_next_command(server, &rqst[num_rqst], 0); | 191 | smb2_set_next_command(tcon, &rqst[num_rqst]); |
193 | smb2_set_related(&rqst[num_rqst++]); | 192 | smb2_set_related(&rqst[num_rqst++]); |
194 | break; | 193 | break; |
195 | case SMB2_OP_HARDLINK: | 194 | case SMB2_OP_HARDLINK: |
@@ -213,7 +212,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, | |||
213 | COMPOUND_FID, current->tgid, | 212 | COMPOUND_FID, current->tgid, |
214 | FILE_LINK_INFORMATION, | 213 | FILE_LINK_INFORMATION, |
215 | SMB2_O_INFO_FILE, 0, data, size); | 214 | SMB2_O_INFO_FILE, 0, data, size); |
216 | smb2_set_next_command(server, &rqst[num_rqst], 0); | 215 | smb2_set_next_command(tcon, &rqst[num_rqst]); |
217 | smb2_set_related(&rqst[num_rqst++]); | 216 | smb2_set_related(&rqst[num_rqst++]); |
218 | break; | 217 | break; |
219 | default: | 218 | default: |
@@ -388,7 +387,6 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, | |||
388 | rc = -ENOMEM; | 387 | rc = -ENOMEM; |
389 | goto smb2_rename_path; | 388 | goto smb2_rename_path; |
390 | } | 389 | } |
391 | |||
392 | rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, | 390 | rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, |
393 | FILE_OPEN, 0, smb2_to_name, command); | 391 | FILE_OPEN, 0, smb2_to_name, command); |
394 | smb2_rename_path: | 392 | smb2_rename_path: |
diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c index d47b7f5dfa6c..924269cec135 100644 --- a/fs/cifs/smb2maperror.c +++ b/fs/cifs/smb2maperror.c | |||
@@ -379,8 +379,8 @@ static const struct status_to_posix_error smb2_error_map_table[] = { | |||
379 | {STATUS_NONEXISTENT_EA_ENTRY, -EIO, "STATUS_NONEXISTENT_EA_ENTRY"}, | 379 | {STATUS_NONEXISTENT_EA_ENTRY, -EIO, "STATUS_NONEXISTENT_EA_ENTRY"}, |
380 | {STATUS_NO_EAS_ON_FILE, -ENODATA, "STATUS_NO_EAS_ON_FILE"}, | 380 | {STATUS_NO_EAS_ON_FILE, -ENODATA, "STATUS_NO_EAS_ON_FILE"}, |
381 | {STATUS_EA_CORRUPT_ERROR, -EIO, "STATUS_EA_CORRUPT_ERROR"}, | 381 | {STATUS_EA_CORRUPT_ERROR, -EIO, "STATUS_EA_CORRUPT_ERROR"}, |
382 | {STATUS_FILE_LOCK_CONFLICT, -EIO, "STATUS_FILE_LOCK_CONFLICT"}, | 382 | {STATUS_FILE_LOCK_CONFLICT, -EACCES, "STATUS_FILE_LOCK_CONFLICT"}, |
383 | {STATUS_LOCK_NOT_GRANTED, -EIO, "STATUS_LOCK_NOT_GRANTED"}, | 383 | {STATUS_LOCK_NOT_GRANTED, -EACCES, "STATUS_LOCK_NOT_GRANTED"}, |
384 | {STATUS_DELETE_PENDING, -ENOENT, "STATUS_DELETE_PENDING"}, | 384 | {STATUS_DELETE_PENDING, -ENOENT, "STATUS_DELETE_PENDING"}, |
385 | {STATUS_CTL_FILE_NOT_SUPPORTED, -ENOSYS, | 385 | {STATUS_CTL_FILE_NOT_SUPPORTED, -ENOSYS, |
386 | "STATUS_CTL_FILE_NOT_SUPPORTED"}, | 386 | "STATUS_CTL_FILE_NOT_SUPPORTED"}, |
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index e25c7aade98a..33100ef74d7f 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -831,72 +831,48 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, | |||
831 | { | 831 | { |
832 | int rc; | 832 | int rc; |
833 | __le16 *utf16_path; | 833 | __le16 *utf16_path; |
834 | __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; | 834 | struct kvec rsp_iov = {NULL, 0}; |
835 | struct cifs_open_parms oparms; | 835 | int buftype = CIFS_NO_BUFFER; |
836 | struct cifs_fid fid; | 836 | struct smb2_query_info_rsp *rsp; |
837 | struct smb2_file_full_ea_info *smb2_data; | 837 | struct smb2_file_full_ea_info *info = NULL; |
838 | int ea_buf_size = SMB2_MIN_EA_BUF; | ||
839 | 838 | ||
840 | utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); | 839 | utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); |
841 | if (!utf16_path) | 840 | if (!utf16_path) |
842 | return -ENOMEM; | 841 | return -ENOMEM; |
843 | 842 | ||
844 | oparms.tcon = tcon; | 843 | rc = smb2_query_info_compound(xid, tcon, utf16_path, |
845 | oparms.desired_access = FILE_READ_EA; | 844 | FILE_READ_EA, |
846 | oparms.disposition = FILE_OPEN; | 845 | FILE_FULL_EA_INFORMATION, |
847 | if (backup_cred(cifs_sb)) | 846 | SMB2_O_INFO_FILE, |
848 | oparms.create_options = CREATE_OPEN_BACKUP_INTENT; | 847 | SMB2_MAX_EA_BUF, |
849 | else | 848 | &rsp_iov, &buftype, cifs_sb); |
850 | oparms.create_options = 0; | ||
851 | oparms.fid = &fid; | ||
852 | oparms.reconnect = false; | ||
853 | |||
854 | rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL); | ||
855 | kfree(utf16_path); | ||
856 | if (rc) { | 849 | if (rc) { |
857 | cifs_dbg(FYI, "open failed rc=%d\n", rc); | 850 | /* |
858 | return rc; | 851 | * If ea_name is NULL (listxattr) and there are no EAs, |
859 | } | 852 | * return 0 as it's not an error. Otherwise, the specified |
860 | 853 | * ea_name was not found. | |
861 | while (1) { | 854 | */ |
862 | smb2_data = kzalloc(ea_buf_size, GFP_KERNEL); | 855 | if (!ea_name && rc == -ENODATA) |
863 | if (smb2_data == NULL) { | 856 | rc = 0; |
864 | SMB2_close(xid, tcon, fid.persistent_fid, | 857 | goto qeas_exit; |
865 | fid.volatile_fid); | ||
866 | return -ENOMEM; | ||
867 | } | ||
868 | |||
869 | rc = SMB2_query_eas(xid, tcon, fid.persistent_fid, | ||
870 | fid.volatile_fid, | ||
871 | ea_buf_size, smb2_data); | ||
872 | |||
873 | if (rc != -E2BIG) | ||
874 | break; | ||
875 | |||
876 | kfree(smb2_data); | ||
877 | ea_buf_size <<= 1; | ||
878 | |||
879 | if (ea_buf_size > SMB2_MAX_EA_BUF) { | ||
880 | cifs_dbg(VFS, "EA size is too large\n"); | ||
881 | SMB2_close(xid, tcon, fid.persistent_fid, | ||
882 | fid.volatile_fid); | ||
883 | return -ENOMEM; | ||
884 | } | ||
885 | } | 858 | } |
886 | 859 | ||
887 | SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); | 860 | rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; |
861 | rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), | ||
862 | le32_to_cpu(rsp->OutputBufferLength), | ||
863 | &rsp_iov, | ||
864 | sizeof(struct smb2_file_full_ea_info)); | ||
865 | if (rc) | ||
866 | goto qeas_exit; | ||
888 | 867 | ||
889 | /* | 868 | info = (struct smb2_file_full_ea_info *)( |
890 | * If ea_name is NULL (listxattr) and there are no EAs, return 0 as it's | 869 | le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); |
891 | * not an error. Otherwise, the specified ea_name was not found. | 870 | rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, |
892 | */ | 871 | le32_to_cpu(rsp->OutputBufferLength), ea_name); |
893 | if (!rc) | ||
894 | rc = move_smb2_ea_to_cifs(ea_data, buf_size, smb2_data, | ||
895 | SMB2_MAX_EA_BUF, ea_name); | ||
896 | else if (!ea_name && rc == -ENODATA) | ||
897 | rc = 0; | ||
898 | 872 | ||
899 | kfree(smb2_data); | 873 | qeas_exit: |
874 | kfree(utf16_path); | ||
875 | free_rsp_buf(buftype, rsp_iov.iov_base); | ||
900 | return rc; | 876 | return rc; |
901 | } | 877 | } |
902 | 878 | ||
@@ -907,14 +883,27 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, | |||
907 | const __u16 ea_value_len, const struct nls_table *nls_codepage, | 883 | const __u16 ea_value_len, const struct nls_table *nls_codepage, |
908 | struct cifs_sb_info *cifs_sb) | 884 | struct cifs_sb_info *cifs_sb) |
909 | { | 885 | { |
910 | int rc; | 886 | struct cifs_ses *ses = tcon->ses; |
911 | __le16 *utf16_path; | 887 | __le16 *utf16_path = NULL; |
912 | __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; | ||
913 | struct cifs_open_parms oparms; | ||
914 | struct cifs_fid fid; | ||
915 | struct smb2_file_full_ea_info *ea; | ||
916 | int ea_name_len = strlen(ea_name); | 888 | int ea_name_len = strlen(ea_name); |
889 | int flags = 0; | ||
917 | int len; | 890 | int len; |
891 | struct smb_rqst rqst[3]; | ||
892 | int resp_buftype[3]; | ||
893 | struct kvec rsp_iov[3]; | ||
894 | struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; | ||
895 | struct cifs_open_parms oparms; | ||
896 | __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; | ||
897 | struct cifs_fid fid; | ||
898 | struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; | ||
899 | unsigned int size[1]; | ||
900 | void *data[1]; | ||
901 | struct smb2_file_full_ea_info *ea = NULL; | ||
902 | struct kvec close_iov[1]; | ||
903 | int rc; | ||
904 | |||
905 | if (smb3_encryption_required(tcon)) | ||
906 | flags |= CIFS_TRANSFORM_REQ; | ||
918 | 907 | ||
919 | if (ea_name_len > 255) | 908 | if (ea_name_len > 255) |
920 | return -EINVAL; | 909 | return -EINVAL; |
@@ -923,6 +912,16 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, | |||
923 | if (!utf16_path) | 912 | if (!utf16_path) |
924 | return -ENOMEM; | 913 | return -ENOMEM; |
925 | 914 | ||
915 | memset(rqst, 0, sizeof(rqst)); | ||
916 | resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; | ||
917 | memset(rsp_iov, 0, sizeof(rsp_iov)); | ||
918 | |||
919 | /* Open */ | ||
920 | memset(&open_iov, 0, sizeof(open_iov)); | ||
921 | rqst[0].rq_iov = open_iov; | ||
922 | rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; | ||
923 | |||
924 | memset(&oparms, 0, sizeof(oparms)); | ||
926 | oparms.tcon = tcon; | 925 | oparms.tcon = tcon; |
927 | oparms.desired_access = FILE_WRITE_EA; | 926 | oparms.desired_access = FILE_WRITE_EA; |
928 | oparms.disposition = FILE_OPEN; | 927 | oparms.disposition = FILE_OPEN; |
@@ -933,18 +932,22 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, | |||
933 | oparms.fid = &fid; | 932 | oparms.fid = &fid; |
934 | oparms.reconnect = false; | 933 | oparms.reconnect = false; |
935 | 934 | ||
936 | rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL); | 935 | rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, utf16_path); |
937 | kfree(utf16_path); | 936 | if (rc) |
938 | if (rc) { | 937 | goto sea_exit; |
939 | cifs_dbg(FYI, "open failed rc=%d\n", rc); | 938 | smb2_set_next_command(tcon, &rqst[0]); |
940 | return rc; | 939 | |
941 | } | 940 | |
941 | /* Set Info */ | ||
942 | memset(&si_iov, 0, sizeof(si_iov)); | ||
943 | rqst[1].rq_iov = si_iov; | ||
944 | rqst[1].rq_nvec = 1; | ||
942 | 945 | ||
943 | len = sizeof(ea) + ea_name_len + ea_value_len + 1; | 946 | len = sizeof(ea) + ea_name_len + ea_value_len + 1; |
944 | ea = kzalloc(len, GFP_KERNEL); | 947 | ea = kzalloc(len, GFP_KERNEL); |
945 | if (ea == NULL) { | 948 | if (ea == NULL) { |
946 | SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); | 949 | rc = -ENOMEM; |
947 | return -ENOMEM; | 950 | goto sea_exit; |
948 | } | 951 | } |
949 | 952 | ||
950 | ea->ea_name_length = ea_name_len; | 953 | ea->ea_name_length = ea_name_len; |
@@ -952,12 +955,36 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, | |||
952 | memcpy(ea->ea_data, ea_name, ea_name_len + 1); | 955 | memcpy(ea->ea_data, ea_name, ea_name_len + 1); |
953 | memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len); | 956 | memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len); |
954 | 957 | ||
955 | rc = SMB2_set_ea(xid, tcon, fid.persistent_fid, fid.volatile_fid, ea, | 958 | size[0] = len; |
956 | len); | 959 | data[0] = ea; |
957 | kfree(ea); | 960 | |
961 | rc = SMB2_set_info_init(tcon, &rqst[1], COMPOUND_FID, | ||
962 | COMPOUND_FID, current->tgid, | ||
963 | FILE_FULL_EA_INFORMATION, | ||
964 | SMB2_O_INFO_FILE, 0, data, size); | ||
965 | smb2_set_next_command(tcon, &rqst[1]); | ||
966 | smb2_set_related(&rqst[1]); | ||
958 | 967 | ||
959 | SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); | ||
960 | 968 | ||
969 | /* Close */ | ||
970 | memset(&close_iov, 0, sizeof(close_iov)); | ||
971 | rqst[2].rq_iov = close_iov; | ||
972 | rqst[2].rq_nvec = 1; | ||
973 | rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID); | ||
974 | smb2_set_related(&rqst[2]); | ||
975 | |||
976 | rc = compound_send_recv(xid, ses, flags, 3, rqst, | ||
977 | resp_buftype, rsp_iov); | ||
978 | |||
979 | sea_exit: | ||
980 | kfree(ea); | ||
981 | kfree(utf16_path); | ||
982 | SMB2_open_free(&rqst[0]); | ||
983 | SMB2_set_info_free(&rqst[1]); | ||
984 | SMB2_close_free(&rqst[2]); | ||
985 | free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); | ||
986 | free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); | ||
987 | free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); | ||
961 | return rc; | 988 | return rc; |
962 | } | 989 | } |
963 | #endif | 990 | #endif |
@@ -1194,7 +1221,7 @@ smb2_ioctl_query_info(const unsigned int xid, | |||
1194 | rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, path); | 1221 | rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, path); |
1195 | if (rc) | 1222 | if (rc) |
1196 | goto iqinf_exit; | 1223 | goto iqinf_exit; |
1197 | smb2_set_next_command(ses->server, &rqst[0], 0); | 1224 | smb2_set_next_command(tcon, &rqst[0]); |
1198 | 1225 | ||
1199 | /* Query */ | 1226 | /* Query */ |
1200 | memset(&qi_iov, 0, sizeof(qi_iov)); | 1227 | memset(&qi_iov, 0, sizeof(qi_iov)); |
@@ -1208,7 +1235,7 @@ smb2_ioctl_query_info(const unsigned int xid, | |||
1208 | qi.output_buffer_length, buffer); | 1235 | qi.output_buffer_length, buffer); |
1209 | if (rc) | 1236 | if (rc) |
1210 | goto iqinf_exit; | 1237 | goto iqinf_exit; |
1211 | smb2_set_next_command(ses->server, &rqst[1], 0); | 1238 | smb2_set_next_command(tcon, &rqst[1]); |
1212 | smb2_set_related(&rqst[1]); | 1239 | smb2_set_related(&rqst[1]); |
1213 | 1240 | ||
1214 | /* Close */ | 1241 | /* Close */ |
@@ -1761,49 +1788,79 @@ smb2_set_related(struct smb_rqst *rqst) | |||
1761 | char smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0}; | 1788 | char smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0}; |
1762 | 1789 | ||
1763 | void | 1790 | void |
1764 | smb2_set_next_command(struct TCP_Server_Info *server, struct smb_rqst *rqst, | 1791 | smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) |
1765 | bool has_space_for_padding) | ||
1766 | { | 1792 | { |
1767 | struct smb2_sync_hdr *shdr; | 1793 | struct smb2_sync_hdr *shdr; |
1794 | struct cifs_ses *ses = tcon->ses; | ||
1795 | struct TCP_Server_Info *server = ses->server; | ||
1768 | unsigned long len = smb_rqst_len(server, rqst); | 1796 | unsigned long len = smb_rqst_len(server, rqst); |
1797 | int i, num_padding; | ||
1769 | 1798 | ||
1770 | /* SMB headers in a compound are 8 byte aligned. */ | 1799 | /* SMB headers in a compound are 8 byte aligned. */ |
1771 | if (len & 7) { | 1800 | |
1772 | if (has_space_for_padding) { | 1801 | /* No padding needed */ |
1773 | len = rqst->rq_iov[rqst->rq_nvec - 1].iov_len; | 1802 | if (!(len & 7)) |
1774 | rqst->rq_iov[rqst->rq_nvec - 1].iov_len = | 1803 | goto finished; |
1775 | (len + 7) & ~7; | 1804 | |
1776 | } else { | 1805 | num_padding = 8 - (len & 7); |
1777 | rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding; | 1806 | if (!smb3_encryption_required(tcon)) { |
1778 | rqst->rq_iov[rqst->rq_nvec].iov_len = 8 - (len & 7); | 1807 | /* |
1779 | rqst->rq_nvec++; | 1808 | * If we do not have encryption then we can just add an extra |
1809 | * iov for the padding. | ||
1810 | */ | ||
1811 | rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding; | ||
1812 | rqst->rq_iov[rqst->rq_nvec].iov_len = num_padding; | ||
1813 | rqst->rq_nvec++; | ||
1814 | len += num_padding; | ||
1815 | } else { | ||
1816 | /* | ||
1817 | * We can not add a small padding iov for the encryption case | ||
1818 | * because the encryption framework can not handle the padding | ||
1819 | * iovs. | ||
1820 | * We have to flatten this into a single buffer and add | ||
1821 | * the padding to it. | ||
1822 | */ | ||
1823 | for (i = 1; i < rqst->rq_nvec; i++) { | ||
1824 | memcpy(rqst->rq_iov[0].iov_base + | ||
1825 | rqst->rq_iov[0].iov_len, | ||
1826 | rqst->rq_iov[i].iov_base, | ||
1827 | rqst->rq_iov[i].iov_len); | ||
1828 | rqst->rq_iov[0].iov_len += rqst->rq_iov[i].iov_len; | ||
1780 | } | 1829 | } |
1781 | len = smb_rqst_len(server, rqst); | 1830 | memset(rqst->rq_iov[0].iov_base + rqst->rq_iov[0].iov_len, |
1831 | 0, num_padding); | ||
1832 | rqst->rq_iov[0].iov_len += num_padding; | ||
1833 | len += num_padding; | ||
1834 | rqst->rq_nvec = 1; | ||
1782 | } | 1835 | } |
1783 | 1836 | ||
1837 | finished: | ||
1784 | shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base); | 1838 | shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base); |
1785 | shdr->NextCommand = cpu_to_le32(len); | 1839 | shdr->NextCommand = cpu_to_le32(len); |
1786 | } | 1840 | } |
1787 | 1841 | ||
1788 | static int | 1842 | /* |
1789 | smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, | 1843 | * Passes the query info response back to the caller on success. |
1790 | struct kstatfs *buf) | 1844 | * Caller need to free this with free_rsp_buf(). |
1845 | */ | ||
1846 | int | ||
1847 | smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, | ||
1848 | __le16 *utf16_path, u32 desired_access, | ||
1849 | u32 class, u32 type, u32 output_len, | ||
1850 | struct kvec *rsp, int *buftype, | ||
1851 | struct cifs_sb_info *cifs_sb) | ||
1791 | { | 1852 | { |
1792 | struct smb2_query_info_rsp *rsp; | 1853 | struct cifs_ses *ses = tcon->ses; |
1793 | struct smb2_fs_full_size_info *info = NULL; | 1854 | int flags = 0; |
1794 | struct smb_rqst rqst[3]; | 1855 | struct smb_rqst rqst[3]; |
1795 | int resp_buftype[3]; | 1856 | int resp_buftype[3]; |
1796 | struct kvec rsp_iov[3]; | 1857 | struct kvec rsp_iov[3]; |
1797 | struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; | 1858 | struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; |
1798 | struct kvec qi_iov[1]; | 1859 | struct kvec qi_iov[1]; |
1799 | struct kvec close_iov[1]; | 1860 | struct kvec close_iov[1]; |
1800 | struct cifs_ses *ses = tcon->ses; | ||
1801 | struct TCP_Server_Info *server = ses->server; | ||
1802 | __le16 srch_path = 0; /* Null - open root of share */ | ||
1803 | u8 oplock = SMB2_OPLOCK_LEVEL_NONE; | 1861 | u8 oplock = SMB2_OPLOCK_LEVEL_NONE; |
1804 | struct cifs_open_parms oparms; | 1862 | struct cifs_open_parms oparms; |
1805 | struct cifs_fid fid; | 1863 | struct cifs_fid fid; |
1806 | int flags = 0; | ||
1807 | int rc; | 1864 | int rc; |
1808 | 1865 | ||
1809 | if (smb3_encryption_required(tcon)) | 1866 | if (smb3_encryption_required(tcon)) |
@@ -1818,29 +1875,31 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, | |||
1818 | rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; | 1875 | rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; |
1819 | 1876 | ||
1820 | oparms.tcon = tcon; | 1877 | oparms.tcon = tcon; |
1821 | oparms.desired_access = FILE_READ_ATTRIBUTES; | 1878 | oparms.desired_access = desired_access; |
1822 | oparms.disposition = FILE_OPEN; | 1879 | oparms.disposition = FILE_OPEN; |
1823 | oparms.create_options = 0; | 1880 | if (cifs_sb && backup_cred(cifs_sb)) |
1881 | oparms.create_options = CREATE_OPEN_BACKUP_INTENT; | ||
1882 | else | ||
1883 | oparms.create_options = 0; | ||
1824 | oparms.fid = &fid; | 1884 | oparms.fid = &fid; |
1825 | oparms.reconnect = false; | 1885 | oparms.reconnect = false; |
1826 | 1886 | ||
1827 | rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, &srch_path); | 1887 | rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, utf16_path); |
1828 | if (rc) | 1888 | if (rc) |
1829 | goto qfs_exit; | 1889 | goto qic_exit; |
1830 | smb2_set_next_command(server, &rqst[0], 0); | 1890 | smb2_set_next_command(tcon, &rqst[0]); |
1831 | 1891 | ||
1832 | memset(&qi_iov, 0, sizeof(qi_iov)); | 1892 | memset(&qi_iov, 0, sizeof(qi_iov)); |
1833 | rqst[1].rq_iov = qi_iov; | 1893 | rqst[1].rq_iov = qi_iov; |
1834 | rqst[1].rq_nvec = 1; | 1894 | rqst[1].rq_nvec = 1; |
1835 | 1895 | ||
1836 | rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID, COMPOUND_FID, | 1896 | rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID, COMPOUND_FID, |
1837 | FS_FULL_SIZE_INFORMATION, | 1897 | class, type, 0, |
1838 | SMB2_O_INFO_FILESYSTEM, 0, | 1898 | output_len, 0, |
1839 | sizeof(struct smb2_fs_full_size_info), 0, | ||
1840 | NULL); | 1899 | NULL); |
1841 | if (rc) | 1900 | if (rc) |
1842 | goto qfs_exit; | 1901 | goto qic_exit; |
1843 | smb2_set_next_command(server, &rqst[1], 0); | 1902 | smb2_set_next_command(tcon, &rqst[1]); |
1844 | smb2_set_related(&rqst[1]); | 1903 | smb2_set_related(&rqst[1]); |
1845 | 1904 | ||
1846 | memset(&close_iov, 0, sizeof(close_iov)); | 1905 | memset(&close_iov, 0, sizeof(close_iov)); |
@@ -1849,32 +1908,61 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, | |||
1849 | 1908 | ||
1850 | rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID); | 1909 | rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID); |
1851 | if (rc) | 1910 | if (rc) |
1852 | goto qfs_exit; | 1911 | goto qic_exit; |
1853 | smb2_set_related(&rqst[2]); | 1912 | smb2_set_related(&rqst[2]); |
1854 | 1913 | ||
1855 | rc = compound_send_recv(xid, ses, flags, 3, rqst, | 1914 | rc = compound_send_recv(xid, ses, flags, 3, rqst, |
1856 | resp_buftype, rsp_iov); | 1915 | resp_buftype, rsp_iov); |
1916 | if (rc) { | ||
1917 | free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); | ||
1918 | goto qic_exit; | ||
1919 | } | ||
1920 | *rsp = rsp_iov[1]; | ||
1921 | *buftype = resp_buftype[1]; | ||
1922 | |||
1923 | qic_exit: | ||
1924 | SMB2_open_free(&rqst[0]); | ||
1925 | SMB2_query_info_free(&rqst[1]); | ||
1926 | SMB2_close_free(&rqst[2]); | ||
1927 | free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); | ||
1928 | free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); | ||
1929 | return rc; | ||
1930 | } | ||
1931 | |||
1932 | static int | ||
1933 | smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, | ||
1934 | struct kstatfs *buf) | ||
1935 | { | ||
1936 | struct smb2_query_info_rsp *rsp; | ||
1937 | struct smb2_fs_full_size_info *info = NULL; | ||
1938 | __le16 utf16_path = 0; /* Null - open root of share */ | ||
1939 | struct kvec rsp_iov = {NULL, 0}; | ||
1940 | int buftype = CIFS_NO_BUFFER; | ||
1941 | int rc; | ||
1942 | |||
1943 | |||
1944 | rc = smb2_query_info_compound(xid, tcon, &utf16_path, | ||
1945 | FILE_READ_ATTRIBUTES, | ||
1946 | FS_FULL_SIZE_INFORMATION, | ||
1947 | SMB2_O_INFO_FILESYSTEM, | ||
1948 | sizeof(struct smb2_fs_full_size_info), | ||
1949 | &rsp_iov, &buftype, NULL); | ||
1857 | if (rc) | 1950 | if (rc) |
1858 | goto qfs_exit; | 1951 | goto qfs_exit; |
1859 | 1952 | ||
1860 | rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; | 1953 | rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; |
1861 | buf->f_type = SMB2_MAGIC_NUMBER; | 1954 | buf->f_type = SMB2_MAGIC_NUMBER; |
1862 | info = (struct smb2_fs_full_size_info *)( | 1955 | info = (struct smb2_fs_full_size_info *)( |
1863 | le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); | 1956 | le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); |
1864 | rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), | 1957 | rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), |
1865 | le32_to_cpu(rsp->OutputBufferLength), | 1958 | le32_to_cpu(rsp->OutputBufferLength), |
1866 | &rsp_iov[1], | 1959 | &rsp_iov, |
1867 | sizeof(struct smb2_fs_full_size_info)); | 1960 | sizeof(struct smb2_fs_full_size_info)); |
1868 | if (!rc) | 1961 | if (!rc) |
1869 | smb2_copy_fs_info_to_kstatfs(info, buf); | 1962 | smb2_copy_fs_info_to_kstatfs(info, buf); |
1870 | 1963 | ||
1871 | qfs_exit: | 1964 | qfs_exit: |
1872 | SMB2_open_free(&rqst[0]); | 1965 | free_rsp_buf(buftype, rsp_iov.iov_base); |
1873 | SMB2_query_info_free(&rqst[1]); | ||
1874 | SMB2_close_free(&rqst[2]); | ||
1875 | free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); | ||
1876 | free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); | ||
1877 | free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); | ||
1878 | return rc; | 1966 | return rc; |
1879 | } | 1967 | } |
1880 | 1968 | ||
@@ -2743,7 +2831,7 @@ init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign) | |||
2743 | smb2_sg_set_buf(&sg[idx++], | 2831 | smb2_sg_set_buf(&sg[idx++], |
2744 | rqst[i].rq_iov[j].iov_base + skip, | 2832 | rqst[i].rq_iov[j].iov_base + skip, |
2745 | rqst[i].rq_iov[j].iov_len - skip); | 2833 | rqst[i].rq_iov[j].iov_len - skip); |
2746 | } | 2834 | } |
2747 | 2835 | ||
2748 | for (j = 0; j < rqst[i].rq_npages; j++) { | 2836 | for (j = 0; j < rqst[i].rq_npages; j++) { |
2749 | unsigned int len, offset; | 2837 | unsigned int len, offset; |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 27f86537a5d1..e283590955cd 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -50,6 +50,9 @@ | |||
50 | #include "cifs_spnego.h" | 50 | #include "cifs_spnego.h" |
51 | #include "smbdirect.h" | 51 | #include "smbdirect.h" |
52 | #include "trace.h" | 52 | #include "trace.h" |
53 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
54 | #include "dfs_cache.h" | ||
55 | #endif | ||
53 | 56 | ||
54 | /* | 57 | /* |
55 | * The following table defines the expected "StructureSize" of SMB2 requests | 58 | * The following table defines the expected "StructureSize" of SMB2 requests |
@@ -152,6 +155,77 @@ out: | |||
152 | return; | 155 | return; |
153 | } | 156 | } |
154 | 157 | ||
158 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
159 | static int __smb2_reconnect(const struct nls_table *nlsc, | ||
160 | struct cifs_tcon *tcon) | ||
161 | { | ||
162 | int rc; | ||
163 | struct dfs_cache_tgt_list tl; | ||
164 | struct dfs_cache_tgt_iterator *it = NULL; | ||
165 | char tree[MAX_TREE_SIZE + 1]; | ||
166 | const char *tcp_host; | ||
167 | size_t tcp_host_len; | ||
168 | const char *dfs_host; | ||
169 | size_t dfs_host_len; | ||
170 | |||
171 | if (tcon->ipc) { | ||
172 | snprintf(tree, sizeof(tree), "\\\\%s\\IPC$", | ||
173 | tcon->ses->server->hostname); | ||
174 | return SMB2_tcon(0, tcon->ses, tree, tcon, nlsc); | ||
175 | } | ||
176 | |||
177 | if (!tcon->dfs_path) | ||
178 | return SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nlsc); | ||
179 | |||
180 | rc = dfs_cache_noreq_find(tcon->dfs_path + 1, NULL, &tl); | ||
181 | if (rc) | ||
182 | return rc; | ||
183 | |||
184 | extract_unc_hostname(tcon->ses->server->hostname, &tcp_host, | ||
185 | &tcp_host_len); | ||
186 | |||
187 | for (it = dfs_cache_get_tgt_iterator(&tl); it; | ||
188 | it = dfs_cache_get_next_tgt(&tl, it)) { | ||
189 | const char *tgt = dfs_cache_get_tgt_name(it); | ||
190 | |||
191 | extract_unc_hostname(tgt, &dfs_host, &dfs_host_len); | ||
192 | |||
193 | if (dfs_host_len != tcp_host_len | ||
194 | || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { | ||
195 | cifs_dbg(FYI, "%s: skipping %.*s, doesn't match %.*s", | ||
196 | __func__, | ||
197 | (int)dfs_host_len, dfs_host, | ||
198 | (int)tcp_host_len, tcp_host); | ||
199 | continue; | ||
200 | } | ||
201 | |||
202 | snprintf(tree, sizeof(tree), "\\%s", tgt); | ||
203 | |||
204 | rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc); | ||
205 | if (!rc) | ||
206 | break; | ||
207 | if (rc == -EREMOTE) | ||
208 | break; | ||
209 | } | ||
210 | |||
211 | if (!rc) { | ||
212 | if (it) | ||
213 | rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, | ||
214 | it); | ||
215 | else | ||
216 | rc = -ENOENT; | ||
217 | } | ||
218 | dfs_cache_free_tgts(&tl); | ||
219 | return rc; | ||
220 | } | ||
221 | #else | ||
222 | static inline int __smb2_reconnect(const struct nls_table *nlsc, | ||
223 | struct cifs_tcon *tcon) | ||
224 | { | ||
225 | return SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nlsc); | ||
226 | } | ||
227 | #endif | ||
228 | |||
155 | static int | 229 | static int |
156 | smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) | 230 | smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) |
157 | { | 231 | { |
@@ -159,6 +233,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) | |||
159 | struct nls_table *nls_codepage; | 233 | struct nls_table *nls_codepage; |
160 | struct cifs_ses *ses; | 234 | struct cifs_ses *ses; |
161 | struct TCP_Server_Info *server; | 235 | struct TCP_Server_Info *server; |
236 | int retries; | ||
162 | 237 | ||
163 | /* | 238 | /* |
164 | * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so | 239 | * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so |
@@ -192,9 +267,12 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) | |||
192 | ses = tcon->ses; | 267 | ses = tcon->ses; |
193 | server = ses->server; | 268 | server = ses->server; |
194 | 269 | ||
270 | retries = server->nr_targets; | ||
271 | |||
195 | /* | 272 | /* |
196 | * Give demultiplex thread up to 10 seconds to reconnect, should be | 273 | * Give demultiplex thread up to 10 seconds to each target available for |
197 | * greater than cifs socket timeout which is 7 seconds | 274 | * reconnect -- should be greater than cifs socket timeout which is 7 |
275 | * seconds. | ||
198 | */ | 276 | */ |
199 | while (server->tcpStatus == CifsNeedReconnect) { | 277 | while (server->tcpStatus == CifsNeedReconnect) { |
200 | /* | 278 | /* |
@@ -225,6 +303,9 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) | |||
225 | if (server->tcpStatus != CifsNeedReconnect) | 303 | if (server->tcpStatus != CifsNeedReconnect) |
226 | break; | 304 | break; |
227 | 305 | ||
306 | if (--retries) | ||
307 | continue; | ||
308 | |||
228 | /* | 309 | /* |
229 | * on "soft" mounts we wait once. Hard mounts keep | 310 | * on "soft" mounts we wait once. Hard mounts keep |
230 | * retrying until process is killed or server comes | 311 | * retrying until process is killed or server comes |
@@ -234,6 +315,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) | |||
234 | cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n"); | 315 | cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n"); |
235 | return -EHOSTDOWN; | 316 | return -EHOSTDOWN; |
236 | } | 317 | } |
318 | retries = server->nr_targets; | ||
237 | } | 319 | } |
238 | 320 | ||
239 | if (!tcon->ses->need_reconnect && !tcon->need_reconnect) | 321 | if (!tcon->ses->need_reconnect && !tcon->need_reconnect) |
@@ -271,7 +353,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) | |||
271 | if (tcon->use_persistent) | 353 | if (tcon->use_persistent) |
272 | tcon->need_reopen_files = true; | 354 | tcon->need_reopen_files = true; |
273 | 355 | ||
274 | rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); | 356 | rc = __smb2_reconnect(nls_codepage, tcon); |
275 | mutex_unlock(&tcon->ses->session_mutex); | 357 | mutex_unlock(&tcon->ses->session_mutex); |
276 | 358 | ||
277 | cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); | 359 | cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); |
@@ -1955,7 +2037,6 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, | |||
1955 | struct smb_rqst rqst; | 2037 | struct smb_rqst rqst; |
1956 | struct smb2_create_req *req; | 2038 | struct smb2_create_req *req; |
1957 | struct smb2_create_rsp *rsp = NULL; | 2039 | struct smb2_create_rsp *rsp = NULL; |
1958 | struct TCP_Server_Info *server; | ||
1959 | struct cifs_ses *ses = tcon->ses; | 2040 | struct cifs_ses *ses = tcon->ses; |
1960 | struct kvec iov[3]; /* make sure at least one for each open context */ | 2041 | struct kvec iov[3]; /* make sure at least one for each open context */ |
1961 | struct kvec rsp_iov = {NULL, 0}; | 2042 | struct kvec rsp_iov = {NULL, 0}; |
@@ -1978,9 +2059,7 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, | |||
1978 | if (!utf16_path) | 2059 | if (!utf16_path) |
1979 | return -ENOMEM; | 2060 | return -ENOMEM; |
1980 | 2061 | ||
1981 | if (ses && (ses->server)) | 2062 | if (!ses || !(ses->server)) { |
1982 | server = ses->server; | ||
1983 | else { | ||
1984 | rc = -EIO; | 2063 | rc = -EIO; |
1985 | goto err_free_path; | 2064 | goto err_free_path; |
1986 | } | 2065 | } |
@@ -2768,18 +2847,6 @@ qinf_exit: | |||
2768 | return rc; | 2847 | return rc; |
2769 | } | 2848 | } |
2770 | 2849 | ||
2771 | int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, | ||
2772 | u64 persistent_fid, u64 volatile_fid, | ||
2773 | int ea_buf_size, struct smb2_file_full_ea_info *data) | ||
2774 | { | ||
2775 | return query_info(xid, tcon, persistent_fid, volatile_fid, | ||
2776 | FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, 0, | ||
2777 | ea_buf_size, | ||
2778 | sizeof(struct smb2_file_full_ea_info), | ||
2779 | (void **)&data, | ||
2780 | NULL); | ||
2781 | } | ||
2782 | |||
2783 | int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, | 2850 | int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, |
2784 | u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data) | 2851 | u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data) |
2785 | { | 2852 | { |
@@ -3994,7 +4061,6 @@ static int | |||
3994 | build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level, | 4061 | build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level, |
3995 | int outbuf_len, u64 persistent_fid, u64 volatile_fid) | 4062 | int outbuf_len, u64 persistent_fid, u64 volatile_fid) |
3996 | { | 4063 | { |
3997 | struct TCP_Server_Info *server; | ||
3998 | int rc; | 4064 | int rc; |
3999 | struct smb2_query_info_req *req; | 4065 | struct smb2_query_info_req *req; |
4000 | unsigned int total_len; | 4066 | unsigned int total_len; |
@@ -4004,8 +4070,6 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level, | |||
4004 | if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) | 4070 | if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) |
4005 | return -EIO; | 4071 | return -EIO; |
4006 | 4072 | ||
4007 | server = tcon->ses->server; | ||
4008 | |||
4009 | rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, (void **) &req, | 4073 | rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, (void **) &req, |
4010 | &total_len); | 4074 | &total_len); |
4011 | if (rc) | 4075 | if (rc) |
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 5671d5ee7f58..05dea6750c33 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h | |||
@@ -1398,7 +1398,6 @@ struct smb2_file_link_info { /* encoding of request for level 11 */ | |||
1398 | char FileName[0]; /* Name to be assigned to new link */ | 1398 | char FileName[0]; /* Name to be assigned to new link */ |
1399 | } __packed; /* level 11 Set */ | 1399 | } __packed; /* level 11 Set */ |
1400 | 1400 | ||
1401 | #define SMB2_MIN_EA_BUF 2048 | ||
1402 | #define SMB2_MAX_EA_BUF 65536 | 1401 | #define SMB2_MAX_EA_BUF 65536 |
1403 | 1402 | ||
1404 | struct smb2_file_full_ea_info { /* encoding of response for level 15 */ | 1403 | struct smb2_file_full_ea_info { /* encoding of response for level 15 */ |
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 2fe78acd7d0c..87733b27a65f 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h | |||
@@ -116,9 +116,8 @@ extern void smb2_reconnect_server(struct work_struct *work); | |||
116 | extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); | 116 | extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); |
117 | extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, | 117 | extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, |
118 | struct smb_rqst *rqst); | 118 | struct smb_rqst *rqst); |
119 | extern void smb2_set_next_command(struct TCP_Server_Info *server, | 119 | extern void smb2_set_next_command(struct cifs_tcon *tcon, |
120 | struct smb_rqst *rqst, | 120 | struct smb_rqst *rqst); |
121 | bool has_space_for_padding); | ||
122 | extern void smb2_set_related(struct smb_rqst *rqst); | 121 | extern void smb2_set_related(struct smb_rqst *rqst); |
123 | 122 | ||
124 | /* | 123 | /* |
@@ -154,10 +153,6 @@ extern int SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, | |||
154 | extern void SMB2_close_free(struct smb_rqst *rqst); | 153 | extern void SMB2_close_free(struct smb_rqst *rqst); |
155 | extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, | 154 | extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, |
156 | u64 persistent_file_id, u64 volatile_file_id); | 155 | u64 persistent_file_id, u64 volatile_file_id); |
157 | extern int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, | ||
158 | u64 persistent_file_id, u64 volatile_file_id, | ||
159 | int ea_buf_size, | ||
160 | struct smb2_file_full_ea_info *data); | ||
161 | extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, | 156 | extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, |
162 | u64 persistent_file_id, u64 volatile_file_id, | 157 | u64 persistent_file_id, u64 volatile_file_id, |
163 | struct smb2_file_all_info *data); | 158 | struct smb2_file_all_info *data); |
@@ -241,4 +236,10 @@ extern void smb2_copy_fs_info_to_kstatfs( | |||
241 | extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server); | 236 | extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server); |
242 | extern int smb311_update_preauth_hash(struct cifs_ses *ses, | 237 | extern int smb311_update_preauth_hash(struct cifs_ses *ses, |
243 | struct kvec *iov, int nvec); | 238 | struct kvec *iov, int nvec); |
239 | extern int smb2_query_info_compound(const unsigned int xid, | ||
240 | struct cifs_tcon *tcon, | ||
241 | __le16 *utf16_path, u32 desired_access, | ||
242 | u32 class, u32 type, u32 output_len, | ||
243 | struct kvec *rsp, int *buftype, | ||
244 | struct cifs_sb_info *cifs_sb); | ||
244 | #endif /* _SMB2PROTO_H */ | 245 | #endif /* _SMB2PROTO_H */ |
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 83ff0c25710d..5be7302853b6 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c | |||
@@ -126,9 +126,11 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) | |||
126 | if ((slow_rsp_threshold != 0) && | 126 | if ((slow_rsp_threshold != 0) && |
127 | time_after(now, midEntry->when_alloc + (slow_rsp_threshold * HZ)) && | 127 | time_after(now, midEntry->when_alloc + (slow_rsp_threshold * HZ)) && |
128 | (midEntry->command != command)) { | 128 | (midEntry->command != command)) { |
129 | /* smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command */ | 129 | /* |
130 | if ((le16_to_cpu(midEntry->command) < NUMBER_OF_SMB2_COMMANDS) && | 130 | * smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command |
131 | (le16_to_cpu(midEntry->command) >= 0)) | 131 | * NB: le16_to_cpu returns unsigned so can not be negative below |
132 | */ | ||
133 | if (le16_to_cpu(midEntry->command) < NUMBER_OF_SMB2_COMMANDS) | ||
132 | cifs_stats_inc(&midEntry->server->smb2slowcmd[le16_to_cpu(midEntry->command)]); | 134 | cifs_stats_inc(&midEntry->server->smb2slowcmd[le16_to_cpu(midEntry->command)]); |
133 | 135 | ||
134 | trace_smb3_slow_rsp(le16_to_cpu(midEntry->command), | 136 | trace_smb3_slow_rsp(le16_to_cpu(midEntry->command), |