Commit 56c762eb authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French

cifs: Refactor out cifs_mount()

* Split and refactor the very large function cifs_mount() in multiple
  functions:

- tcp, ses and tcon setup to mount_get_conns()
- tcp, ses and tcon cleanup in mount_put_conns()
- tcon tlink setup to mount_setup_tlink()
- remote path checking to is_path_remote()

* Implement 2 version of cifs_mount() for DFS-enabled builds and
  non-DFS-enabled builds (CONFIG_CIFS_DFS_UPCALL).

In preparation for DFS failover support.
Signed-off-by: default avatarPaulo Alcantara <palcantara@suse.de>
Reviewed-by: default avatarAurelien Aptel <aaptel@suse.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 9a596f5b
...@@ -213,7 +213,7 @@ extern int cifs_match_super(struct super_block *, void *); ...@@ -213,7 +213,7 @@ extern int cifs_match_super(struct super_block *, void *);
extern void cifs_cleanup_volume_info(struct smb_vol *pvolume_info); extern void cifs_cleanup_volume_info(struct smb_vol *pvolume_info);
extern struct smb_vol *cifs_get_volume_info(char *mount_data, extern struct smb_vol *cifs_get_volume_info(char *mount_data,
const char *devname, bool is_smb3); const char *devname, bool is_smb3);
extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); extern int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol);
extern void cifs_umount(struct cifs_sb_info *); extern void cifs_umount(struct cifs_sb_info *);
extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon); extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon);
...@@ -524,6 +524,8 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16, ...@@ -524,6 +524,8 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
const struct nls_table *codepage); const struct nls_table *codepage);
extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
unsigned char *p24); unsigned char *p24);
extern void
cifs_cleanup_volume_info_contents(struct smb_vol *volume_info);
void cifs_readdata_release(struct kref *refcount); void cifs_readdata_release(struct kref *refcount);
int cifs_async_readv(struct cifs_readdata *rdata); int cifs_async_readv(struct cifs_readdata *rdata);
......
...@@ -3747,8 +3747,8 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info, ...@@ -3747,8 +3747,8 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
return 0; return 0;
} }
static void void
cleanup_volume_info_contents(struct smb_vol *volume_info) cifs_cleanup_volume_info_contents(struct smb_vol *volume_info)
{ {
kfree(volume_info->username); kfree(volume_info->username);
kzfree(volume_info->password); kzfree(volume_info->password);
...@@ -3763,10 +3763,136 @@ cifs_cleanup_volume_info(struct smb_vol *volume_info) ...@@ -3763,10 +3763,136 @@ cifs_cleanup_volume_info(struct smb_vol *volume_info)
{ {
if (!volume_info) if (!volume_info)
return; return;
cleanup_volume_info_contents(volume_info); cifs_cleanup_volume_info_contents(volume_info);
kfree(volume_info); kfree(volume_info);
} }
/* Release all succeed connections */
static inline void mount_put_conns(struct cifs_sb_info *cifs_sb,
unsigned int xid,
struct TCP_Server_Info *server,
struct cifs_ses *ses, struct cifs_tcon *tcon)
{
int rc = 0;
if (tcon)
cifs_put_tcon(tcon);
else if (ses)
cifs_put_smb_ses(ses);
else if (server)
cifs_put_tcp_session(server, 0);
cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS;
free_xid(xid);
}
/* Get connections for tcp, ses and tcon */
static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
unsigned int *xid,
struct TCP_Server_Info **nserver,
struct cifs_ses **nses, struct cifs_tcon **ntcon)
{
int rc = 0;
struct TCP_Server_Info *server;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
*nserver = NULL;
*nses = NULL;
*ntcon = NULL;
*xid = get_xid();
/* get a reference to a tcp session */
server = cifs_get_tcp_session(vol);
if (IS_ERR(server)) {
rc = PTR_ERR(server);
return rc;
}
*nserver = server;
if ((vol->max_credits < 20) || (vol->max_credits > 60000))
server->max_credits = SMB2_MAX_CREDITS_AVAILABLE;
else
server->max_credits = vol->max_credits;
/* get a reference to a SMB session */
ses = cifs_get_smb_ses(server, vol);
if (IS_ERR(ses)) {
rc = PTR_ERR(ses);
return rc;
}
*nses = ses;
if ((vol->persistent == true) && (!(ses->server->capabilities &
SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) {
cifs_dbg(VFS, "persistent handles not supported by server\n");
return -EOPNOTSUPP;
}
/* search for existing tcon to this server share */
tcon = cifs_get_tcon(ses, vol);
if (IS_ERR(tcon)) {
rc = PTR_ERR(tcon);
return rc;
}
*ntcon = tcon;
/* if new SMB3.11 POSIX extensions are supported do not remap / and \ */
if (tcon->posix_extensions)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS;
/* tell server which Unix caps we support */
if (cap_unix(tcon->ses)) {
/*
* reset of caps checks mount to see if unix extensions disabled
* for just this mount.
*/
reset_cifs_unix_caps(*xid, tcon, cifs_sb, vol);
if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) &&
(le64_to_cpu(tcon->fsUnixInfo.Capability) &
CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP))
return -EACCES;
} else
tcon->unix_ext = 0; /* server does not support them */
/* do not care if a following call succeed - informational */
if (!tcon->pipe && server->ops->qfs_tcon)
server->ops->qfs_tcon(*xid, tcon);
cifs_sb->wsize = server->ops->negotiate_wsize(tcon, vol);
cifs_sb->rsize = server->ops->negotiate_rsize(tcon, vol);
return 0;
}
static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
struct cifs_tcon *tcon)
{
struct tcon_link *tlink;
/* hang the tcon off of the superblock */
tlink = kzalloc(sizeof(*tlink), GFP_KERNEL);
if (tlink == NULL)
return -ENOMEM;
tlink->tl_uid = ses->linux_uid;
tlink->tl_tcon = tcon;
tlink->tl_time = jiffies;
set_bit(TCON_LINK_MASTER, &tlink->tl_flags);
set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
cifs_sb->master_tlink = tlink;
spin_lock(&cifs_sb->tlink_tree_lock);
tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
spin_unlock(&cifs_sb->tlink_tree_lock);
queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
TLINK_IDLE_EXPIRE);
return 0;
}
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
/* /*
...@@ -3846,7 +3972,7 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, ...@@ -3846,7 +3972,7 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
rc = PTR_ERR(mdata); rc = PTR_ERR(mdata);
mdata = NULL; mdata = NULL;
} else { } else {
cleanup_volume_info_contents(volume_info); cifs_cleanup_volume_info_contents(volume_info);
rc = cifs_setup_volume_info(volume_info, mdata, rc = cifs_setup_volume_info(volume_info, mdata,
fake_devname, false); fake_devname, false);
} }
...@@ -3955,107 +4081,77 @@ cifs_are_all_path_components_accessible(struct TCP_Server_Info *server, ...@@ -3955,107 +4081,77 @@ cifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
return rc; return rc;
} }
int /*
cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info) * Check if path is remote (e.g. a DFS share). Return -EREMOTE if it is,
* otherwise 0.
*/
static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb_vol *vol,
const unsigned int xid,
struct TCP_Server_Info *server,
struct cifs_tcon *tcon)
{ {
int rc; int rc;
unsigned int xid; char *full_path;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
char *full_path;
struct tcon_link *tlink;
#ifdef CONFIG_CIFS_DFS_UPCALL
int referral_walks_count = 0;
#endif
#ifdef CONFIG_CIFS_DFS_UPCALL
try_mount_again:
/* cleanup activities if we're chasing a referral */
if (referral_walks_count) {
if (tcon)
cifs_put_tcon(tcon);
else if (ses)
cifs_put_smb_ses(ses);
cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; if (!server->ops->is_path_accessible)
return -EOPNOTSUPP;
free_xid(xid); /*
} * cifs_build_path_to_root works only when we have a valid tcon
#endif */
rc = 0; full_path = cifs_build_path_to_root(vol, cifs_sb, tcon,
tcon = NULL; tcon->Flags & SMB_SHARE_IS_IN_DFS);
ses = NULL; if (full_path == NULL)
server = NULL; return -ENOMEM;
full_path = NULL;
tlink = NULL;
xid = get_xid(); cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
/* get a reference to a tcp session */ rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
server = cifs_get_tcp_session(volume_info); full_path);
if (IS_ERR(server)) { if (rc != 0 && rc != -EREMOTE) {
rc = PTR_ERR(server); kfree(full_path);
goto out; return rc;
}
if ((volume_info->max_credits < 20) ||
(volume_info->max_credits > 60000))
server->max_credits = SMB2_MAX_CREDITS_AVAILABLE;
else
server->max_credits = volume_info->max_credits;
/* get a reference to a SMB session */
ses = cifs_get_smb_ses(server, volume_info);
if (IS_ERR(ses)) {
rc = PTR_ERR(ses);
ses = NULL;
goto mount_fail_check;
}
if ((volume_info->persistent == true) && ((ses->server->capabilities &
SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == 0)) {
cifs_dbg(VFS, "persistent handles not supported by server\n");
rc = -EOPNOTSUPP;
goto mount_fail_check;
} }
/* search for existing tcon to this server share */ if (rc != -EREMOTE) {
tcon = cifs_get_tcon(ses, volume_info); rc = cifs_are_all_path_components_accessible(server, xid, tcon,
if (IS_ERR(tcon)) { cifs_sb,
rc = PTR_ERR(tcon); full_path);
tcon = NULL; if (rc != 0) {
if (rc == -EACCES) cifs_dbg(VFS, "cannot query dirs between root and final path, "
goto mount_fail_check; "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
goto remote_path_check; rc = 0;
}
} }
/* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ kfree(full_path);
if (tcon->posix_extensions) return rc;
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; }
/* tell server which Unix caps we support */ #ifdef CONFIG_CIFS_DFS_UPCALL
if (cap_unix(tcon->ses)) { int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
/* reset of caps checks mount to see if unix extensions {
disabled for just this mount */ int rc = 0;
reset_cifs_unix_caps(xid, tcon, cifs_sb, volume_info); unsigned int xid;
if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && struct cifs_ses *ses;
(le64_to_cpu(tcon->fsUnixInfo.Capability) & struct cifs_tcon *tcon = NULL;
CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) { struct TCP_Server_Info *server;
rc = -EACCES; char *old_mountdata;
goto mount_fail_check; int count;
}
} else
tcon->unix_ext = 0; /* server does not support them */
/* do not care if a following call succeed - informational */ rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
if (!tcon->pipe && server->ops->qfs_tcon) if (!rc && tcon) {
server->ops->qfs_tcon(xid, tcon); rc = is_path_remote(cifs_sb, vol, xid, server, tcon);
if (!rc)
goto out;
if (rc != -EREMOTE)
goto error;
}
if ((rc == -EACCES) || (rc == -EOPNOTSUPP) || (ses == NULL) || (server == NULL))
goto error;
cifs_sb->wsize = server->ops->negotiate_wsize(tcon, volume_info);
cifs_sb->rsize = server->ops->negotiate_rsize(tcon, volume_info);
remote_path_check:
#ifdef CONFIG_CIFS_DFS_UPCALL
/* /*
* Perform an unconditional check for whether there are DFS * Perform an unconditional check for whether there are DFS
* referrals for this path without prefix, to provide support * referrals for this path without prefix, to provide support
...@@ -4063,119 +4159,100 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info) ...@@ -4063,119 +4159,100 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
* with PATH_NOT_COVERED to requests that include the prefix. * with PATH_NOT_COVERED to requests that include the prefix.
* Chase the referral if found, otherwise continue normally. * Chase the referral if found, otherwise continue normally.
*/ */
if (referral_walks_count == 0) { old_mountdata = cifs_sb->mountdata;
int refrc = expand_dfs_referral(xid, ses, volume_info, cifs_sb, (void)expand_dfs_referral(xid, ses, vol, cifs_sb, false);
false);
if (!refrc) { if (cifs_sb->mountdata == NULL) {
referral_walks_count++; rc = -ENOENT;
goto try_mount_again; goto error;
}
} }
#endif
/* check if a whole path is not remote */ if (cifs_sb->mountdata != old_mountdata) {
if (!rc && tcon) { /* If we were redirected, reconnect to new target server */
if (!server->ops->is_path_accessible) { mount_put_conns(cifs_sb, xid, server, ses, tcon);
rc = -ENOSYS; rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
goto mount_fail_check; }
if (rc) {
if (rc == -EACCES || rc == -EOPNOTSUPP)
goto error;
}
for (count = 1; ;) {
if (!rc && tcon) {
rc = is_path_remote(cifs_sb, vol, xid, server, tcon);
if (!rc || rc != -EREMOTE)
break;
} }
/* /*
* cifs_build_path_to_root works only when we have a valid tcon * BB: when we implement proper loop detection,
* we will remove this check. But now we need it
* to prevent an indefinite loop if 'DFS tree' is
* misconfigured (i.e. has loops).
*/ */
full_path = cifs_build_path_to_root(volume_info, cifs_sb, tcon, if (count++ > MAX_NESTED_LINKS) {
tcon->Flags & SMB_SHARE_IS_IN_DFS);
if (full_path == NULL) {
rc = -ENOMEM;
goto mount_fail_check;
}
rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
full_path);
if (rc != 0 && rc != -EREMOTE) {
kfree(full_path);
goto mount_fail_check;
}
if (rc != -EREMOTE) {
rc = cifs_are_all_path_components_accessible(server,
xid, tcon, cifs_sb,
full_path);
if (rc != 0) {
cifs_dbg(VFS, "cannot query dirs between root and final path, "
"enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
rc = 0;
}
}
kfree(full_path);
}
/* get referral if needed */
if (rc == -EREMOTE) {
#ifdef CONFIG_CIFS_DFS_UPCALL
if (referral_walks_count > MAX_NESTED_LINKS) {
/*
* BB: when we implement proper loop detection,
* we will remove this check. But now we need it
* to prevent an indefinite loop if 'DFS tree' is
* misconfigured (i.e. has loops).
*/
rc = -ELOOP; rc = -ELOOP;
goto mount_fail_check; break;
} }
rc = expand_dfs_referral(xid, ses, volume_info, cifs_sb, true); old_mountdata = cifs_sb->mountdata;
rc = expand_dfs_referral(xid, tcon->ses, vol, cifs_sb,
true);
if (rc)
break;
if (!rc) { if (cifs_sb->mountdata != old_mountdata) {
referral_walks_count++; mount_put_conns(cifs_sb, xid, server, ses, tcon);
goto try_mount_again; rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses,
&tcon);
}
if (rc) {
if (rc == -EACCES || rc == -EOPNOTSUPP || !server ||
!ses)
goto error;
} }
goto mount_fail_check;
#else /* No DFS support, return error on mount */
rc = -EOPNOTSUPP;
#endif
} }
if (rc) if (rc)
goto mount_fail_check; goto error;
/* now, hang the tcon off of the superblock */
tlink = kzalloc(sizeof *tlink, GFP_KERNEL);
if (tlink == NULL) {
rc = -ENOMEM;
goto mount_fail_check;
}
tlink->tl_uid = ses->linux_uid; out:
tlink->tl_tcon = tcon; free_xid(xid);
tlink->tl_time = jiffies; return mount_setup_tlink(cifs_sb, ses, tcon);
set_bit(TCON_LINK_MASTER, &tlink->tl_flags);
set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
cifs_sb->master_tlink = tlink; error:
spin_lock(&cifs_sb->tlink_tree_lock); mount_put_conns(cifs_sb, xid, server, ses, tcon);
tlink_rb_insert(&cifs_sb->tlink_tree, tlink); return rc;
spin_unlock(&cifs_sb->tlink_tree_lock); }
#else
int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
{
int rc = 0;
unsigned int xid;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks, rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
TLINK_IDLE_EXPIRE); if (rc)
goto error;
mount_fail_check: if (tcon) {
/* on error free sesinfo and tcon struct if needed */ rc = is_path_remote(cifs_sb, vol, xid, server, tcon);
if (rc) { if (rc == -EREMOTE)
/* If find_unc succeeded then rc == 0 so we can not end */ rc = -EOPNOTSUPP;
/* up accidentally freeing someone elses tcon struct */ if (rc)
if (tcon) goto error;
cifs_put_tcon(tcon);
else if (ses)
cifs_put_smb_ses(ses);
else
cifs_put_tcp_session(server, 0);
} }
out:
free_xid(xid); free_xid(xid);
return mount_setup_tlink(cifs_sb, ses, tcon);
error:
mount_put_conns(cifs_sb, xid, server, ses, tcon);
return rc; return rc;
} }
#endif
/* /*
* Issue a TREE_CONNECT request. * Issue a TREE_CONNECT request.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment