Skip to content

Commit 0a92481

Browse files
committed
Merge tag '6.2-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6
Pull cifs fixes from Steve French: "cifs/smb3 client fixes, mostly related to reconnect and/or DFS: - two important reconnect fixes: cases where status of recently connected IPCs and shares were not being updated leaving them in an incorrect state - fix for older Windows servers that would return STATUS_OBJECT_NAME_INVALID to query info requests on DFS links in a namespace that contained non-ASCII characters, reducing number of wasted roundtrips. - fix for leaked -ENOMEM to userspace when cifs.ko couldn't perform I/O due to a disconnected server, expired or deleted session. - removal of all unneeded DFS related mount option string parsing (now using fs_context for automounts) - improve clarity/readability, moving various DFS related functions out of fs/cifs/connect.c (which was getting too big to be readable) to new file. - Fix problem when large number of DFS connections. Allow sharing of DFS connections and fix how the referral paths are matched - Referral caching fix: Instead of looking up ipc connections to refresh cached referrals, store direct dfs root server's IPC pointer in new sessions so it can simply be accessed to either refresh or create a new referral that such connections belong to. - Fix to allow dfs root server's connections to also failover - Optimized reconnect of nested DFS links - Set correct status of IPC connections marked for reconnect" * tag '6.2-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6: cifs: update internal module number cifs: don't leak -ENOMEM in smb2_open_file() cifs: use origin fullpath for automounts cifs: set correct status of tcon ipc when reconnecting cifs: optimize reconnect of nested links cifs: fix source pathname comparison of dfs supers cifs: fix confusing debug message cifs: don't block in dfs_cache_noreq_update_tgthint() cifs: refresh root referrals cifs: fix refresh of cached referrals cifs: don't refresh cached referrals from unactive mounts cifs: share dfs connections and supers cifs: split out ses and tcon retrieval from mount_get_conns() cifs: set resolved ip in sockaddr cifs: remove unused smb3_fs_context::mount_options cifs: get rid of mount options string parsing cifs: use fs_context for automounts cifs: reduce roundtrips on create/qinfo requests cifs: set correct ipc status after initial tree connect cifs: set correct tcon status after initial tree connect
2 parents 6022ec6 + aacfc93 commit 0a92481

22 files changed

+991
-1338
lines changed

fs/cifs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ cifs-$(CONFIG_CIFS_XATTR) += xattr.o
2121

2222
cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
2323

24-
cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o
24+
cifs-$(CONFIG_CIFS_DFS_UPCALL) += cifs_dfs_ref.o dfs_cache.o dfs.o
2525

2626
cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o
2727

fs/cifs/cifs_debug.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,14 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
372372
seq_printf(m, "\nIn Send: %d In MaxReq Wait: %d",
373373
atomic_read(&server->in_send),
374374
atomic_read(&server->num_waiters));
375+
if (IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)) {
376+
if (server->origin_fullpath)
377+
seq_printf(m, "\nDFS origin full path: %s",
378+
server->origin_fullpath);
379+
if (server->leaf_fullpath)
380+
seq_printf(m, "\nDFS leaf full path: %s",
381+
server->leaf_fullpath);
382+
}
375383

376384
seq_printf(m, "\n\n\tSessions: ");
377385
i = 0;

fs/cifs/cifs_dfs_ref.c

Lines changed: 56 additions & 199 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@
2121
#include "cifsfs.h"
2222
#include "dns_resolve.h"
2323
#include "cifs_debug.h"
24-
#include "cifs_unicode.h"
25-
#include "dfs_cache.h"
24+
#include "dfs.h"
2625
#include "fs_context.h"
2726

2827
static LIST_HEAD(cifs_dfs_automount_list);
@@ -60,7 +59,7 @@ void cifs_dfs_release_automount_timer(void)
6059
* Returns pointer to the built string, or a ERR_PTR. Caller is responsible
6160
* for freeing the returned string.
6261
*/
63-
static char *
62+
char *
6463
cifs_build_devname(char *nodename, const char *prepath)
6564
{
6665
size_t pplen;
@@ -119,200 +118,34 @@ cifs_build_devname(char *nodename, const char *prepath)
119118
return dev;
120119
}
121120

122-
123-
/**
124-
* cifs_compose_mount_options - creates mount options for referral
125-
* @sb_mountdata: parent/root DFS mount options (template)
126-
* @fullpath: full path in UNC format
127-
* @ref: optional server's referral
128-
* @devname: return the built cifs device name if passed pointer not NULL
129-
* creates mount options for submount based on template options sb_mountdata
130-
* and replacing unc,ip,prefixpath options with ones we've got form ref_unc.
131-
*
132-
* Returns: pointer to new mount options or ERR_PTR.
133-
* Caller is responsible for freeing returned value if it is not error.
134-
*/
135-
char *cifs_compose_mount_options(const char *sb_mountdata,
136-
const char *fullpath,
137-
const struct dfs_info3_param *ref,
138-
char **devname)
121+
static int set_dest_addr(struct smb3_fs_context *ctx, const char *full_path)
139122
{
123+
struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
140124
int rc;
141-
char *name;
142-
char *mountdata = NULL;
143-
const char *prepath = NULL;
144-
int md_len;
145-
char *tkn_e;
146-
char *srvIP = NULL;
147-
char sep = ',';
148-
int off, noff;
149-
150-
if (sb_mountdata == NULL)
151-
return ERR_PTR(-EINVAL);
152-
153-
if (ref) {
154-
if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0))
155-
return ERR_PTR(-EINVAL);
156-
157-
if (strlen(fullpath) - ref->path_consumed) {
158-
prepath = fullpath + ref->path_consumed;
159-
/* skip initial delimiter */
160-
if (*prepath == '/' || *prepath == '\\')
161-
prepath++;
162-
}
163-
164-
name = cifs_build_devname(ref->node_name, prepath);
165-
if (IS_ERR(name)) {
166-
rc = PTR_ERR(name);
167-
name = NULL;
168-
goto compose_mount_options_err;
169-
}
170-
} else {
171-
name = cifs_build_devname((char *)fullpath, NULL);
172-
if (IS_ERR(name)) {
173-
rc = PTR_ERR(name);
174-
name = NULL;
175-
goto compose_mount_options_err;
176-
}
177-
}
178-
179-
rc = dns_resolve_server_name_to_ip(name, &srvIP, NULL);
180-
if (rc < 0) {
181-
cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n",
182-
__func__, name, rc);
183-
goto compose_mount_options_err;
184-
}
185-
186-
/*
187-
* In most cases, we'll be building a shorter string than the original,
188-
* but we do have to assume that the address in the ip= option may be
189-
* much longer than the original. Add the max length of an address
190-
* string to the length of the original string to allow for worst case.
191-
*/
192-
md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN;
193-
mountdata = kzalloc(md_len + sizeof("ip=") + 1, GFP_KERNEL);
194-
if (mountdata == NULL) {
195-
rc = -ENOMEM;
196-
goto compose_mount_options_err;
197-
}
198-
199-
/* copy all options except of unc,ip,prefixpath */
200-
off = 0;
201-
if (strncmp(sb_mountdata, "sep=", 4) == 0) {
202-
sep = sb_mountdata[4];
203-
strncpy(mountdata, sb_mountdata, 5);
204-
off += 5;
205-
}
206-
207-
do {
208-
tkn_e = strchr(sb_mountdata + off, sep);
209-
if (tkn_e == NULL)
210-
noff = strlen(sb_mountdata + off);
211-
else
212-
noff = tkn_e - (sb_mountdata + off) + 1;
213-
214-
if (strncasecmp(sb_mountdata + off, "cruid=", 6) == 0) {
215-
off += noff;
216-
continue;
217-
}
218-
if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) {
219-
off += noff;
220-
continue;
221-
}
222-
if (strncasecmp(sb_mountdata + off, "ip=", 3) == 0) {
223-
off += noff;
224-
continue;
225-
}
226-
if (strncasecmp(sb_mountdata + off, "prefixpath=", 11) == 0) {
227-
off += noff;
228-
continue;
229-
}
230-
strncat(mountdata, sb_mountdata + off, noff);
231-
off += noff;
232-
} while (tkn_e);
233-
strcat(mountdata, sb_mountdata + off);
234-
mountdata[md_len] = '\0';
235-
236-
/* copy new IP and ref share name */
237-
if (mountdata[strlen(mountdata) - 1] != sep)
238-
strncat(mountdata, &sep, 1);
239-
strcat(mountdata, "ip=");
240-
strcat(mountdata, srvIP);
241-
242-
if (devname)
243-
*devname = name;
244-
else
245-
kfree(name);
246-
247-
/*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/
248-
/*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/
249125

250-
compose_mount_options_out:
251-
kfree(srvIP);
252-
return mountdata;
253-
254-
compose_mount_options_err:
255-
kfree(mountdata);
256-
mountdata = ERR_PTR(rc);
257-
kfree(name);
258-
goto compose_mount_options_out;
259-
}
260-
261-
/**
262-
* cifs_dfs_do_mount - mounts specified path using DFS full path
263-
*
264-
* Always pass down @fullpath to smb3_do_mount() so we can use the root server
265-
* to perform failover in case we failed to connect to the first target in the
266-
* referral.
267-
*
268-
* @mntpt: directory entry for the path we are trying to automount
269-
* @cifs_sb: parent/root superblock
270-
* @fullpath: full path in UNC format
271-
*/
272-
static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
273-
struct cifs_sb_info *cifs_sb,
274-
const char *fullpath)
275-
{
276-
struct vfsmount *mnt;
277-
char *mountdata;
278-
char *devname;
279-
280-
devname = kstrdup(fullpath, GFP_KERNEL);
281-
if (!devname)
282-
return ERR_PTR(-ENOMEM);
283-
284-
convert_delimiter(devname, '/');
285-
286-
/* TODO: change to call fs_context_for_mount(), fill in context directly, call fc_mount */
287-
288-
/* See afs_mntpt_do_automount in fs/afs/mntpt.c for an example */
289-
290-
/* strip first '\' from fullpath */
291-
mountdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options,
292-
fullpath + 1, NULL, NULL);
293-
if (IS_ERR(mountdata)) {
294-
kfree(devname);
295-
return (struct vfsmount *)mountdata;
296-
}
297-
298-
mnt = vfs_submount(mntpt, &cifs_fs_type, devname, mountdata);
299-
kfree(mountdata);
300-
kfree(devname);
301-
return mnt;
126+
rc = dns_resolve_server_name_to_ip(full_path, addr, NULL);
127+
if (!rc)
128+
cifs_set_port(addr, ctx->port);
129+
return rc;
302130
}
303131

304132
/*
305133
* Create a vfsmount that we can automount
306134
*/
307-
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
135+
static struct vfsmount *cifs_dfs_do_automount(struct path *path)
308136
{
137+
int rc;
138+
struct dentry *mntpt = path->dentry;
139+
struct fs_context *fc;
309140
struct cifs_sb_info *cifs_sb;
310-
void *page;
141+
void *page = NULL;
142+
struct smb3_fs_context *ctx, *cur_ctx;
143+
struct smb3_fs_context tmp;
311144
char *full_path;
312145
struct vfsmount *mnt;
313146

314-
cifs_dbg(FYI, "in %s\n", __func__);
315-
BUG_ON(IS_ROOT(mntpt));
147+
if (IS_ROOT(mntpt))
148+
return ERR_PTR(-ESTALE);
316149

317150
/*
318151
* The MSDFS spec states that paths in DFS referral requests and
@@ -321,29 +154,53 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
321154
* gives us the latter, so we must adjust the result.
322155
*/
323156
cifs_sb = CIFS_SB(mntpt->d_sb);
324-
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) {
325-
mnt = ERR_PTR(-EREMOTE);
326-
goto cdda_exit;
327-
}
157+
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)
158+
return ERR_PTR(-EREMOTE);
159+
160+
cur_ctx = cifs_sb->ctx;
161+
162+
fc = fs_context_for_submount(path->mnt->mnt_sb->s_type, mntpt);
163+
if (IS_ERR(fc))
164+
return ERR_CAST(fc);
165+
166+
ctx = smb3_fc2context(fc);
328167

329168
page = alloc_dentry_path();
330-
/* always use tree name prefix */
331-
full_path = build_path_from_dentry_optional_prefix(mntpt, page, true);
169+
full_path = dfs_get_automount_devname(mntpt, page);
332170
if (IS_ERR(full_path)) {
333171
mnt = ERR_CAST(full_path);
334-
goto free_full_path;
172+
goto out;
335173
}
336174

337-
convert_delimiter(full_path, '\\');
175+
convert_delimiter(full_path, '/');
338176
cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
339177

340-
mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path);
341-
cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt);
178+
tmp = *cur_ctx;
179+
tmp.source = full_path;
180+
tmp.leaf_fullpath = NULL;
181+
tmp.UNC = tmp.prepath = NULL;
182+
183+
rc = smb3_fs_context_dup(ctx, &tmp);
184+
if (rc) {
185+
mnt = ERR_PTR(rc);
186+
goto out;
187+
}
188+
189+
rc = set_dest_addr(ctx, full_path);
190+
if (rc) {
191+
mnt = ERR_PTR(rc);
192+
goto out;
193+
}
194+
195+
rc = smb3_parse_devname(full_path, ctx);
196+
if (!rc)
197+
mnt = fc_mount(fc);
198+
else
199+
mnt = ERR_PTR(rc);
342200

343-
free_full_path:
201+
out:
202+
put_fs_context(fc);
344203
free_dentry_path(page);
345-
cdda_exit:
346-
cifs_dbg(FYI, "leaving %s\n" , __func__);
347204
return mnt;
348205
}
349206

@@ -354,9 +211,9 @@ struct vfsmount *cifs_dfs_d_automount(struct path *path)
354211
{
355212
struct vfsmount *newmnt;
356213

357-
cifs_dbg(FYI, "in %s\n", __func__);
214+
cifs_dbg(FYI, "%s: %pd\n", __func__, path->dentry);
358215

359-
newmnt = cifs_dfs_do_automount(path->dentry);
216+
newmnt = cifs_dfs_do_automount(path);
360217
if (IS_ERR(newmnt)) {
361218
cifs_dbg(FYI, "leaving %s [automount failed]\n" , __func__);
362219
return newmnt;

fs/cifs/cifsfs.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -896,12 +896,6 @@ cifs_smb3_do_mount(struct file_system_type *fs_type,
896896
goto out;
897897
}
898898

899-
rc = cifs_setup_volume_info(cifs_sb->ctx, NULL, NULL);
900-
if (rc) {
901-
root = ERR_PTR(rc);
902-
goto out;
903-
}
904-
905899
rc = cifs_setup_cifs_sb(cifs_sb);
906900
if (rc) {
907901
root = ERR_PTR(rc);

fs/cifs/cifsfs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,6 @@ extern const struct export_operations cifs_export_ops;
153153
#endif /* CONFIG_CIFS_NFSD_EXPORT */
154154

155155
/* when changing internal version - update following two lines at same time */
156-
#define SMB3_PRODUCT_BUILD 40
157-
#define CIFS_VERSION "2.40"
156+
#define SMB3_PRODUCT_BUILD 41
157+
#define CIFS_VERSION "2.41"
158158
#endif /* _CIFSFS_H */

0 commit comments

Comments
 (0)