diff options
Diffstat (limited to 'fs/smb/client/dfs.c')
| -rw-r--r-- | fs/smb/client/dfs.c | 96 | 
1 files changed, 26 insertions, 70 deletions
diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c index 2390b2fedd6a..26d14dd0482e 100644 --- a/fs/smb/client/dfs.c +++ b/fs/smb/client/dfs.c @@ -54,39 +54,6 @@ out:  	return rc;  } -/* - * cifs_build_path_to_root returns full path to root when we do not have an - * existing connection (tcon) - */ -static char *build_unc_path_to_root(const struct smb3_fs_context *ctx, -				    const struct cifs_sb_info *cifs_sb, bool useppath) -{ -	char *full_path, *pos; -	unsigned int pplen = useppath && ctx->prepath ? strlen(ctx->prepath) + 1 : 0; -	unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1); - -	if (unc_len > MAX_TREE_SIZE) -		return ERR_PTR(-EINVAL); - -	full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL); -	if (full_path == NULL) -		return ERR_PTR(-ENOMEM); - -	memcpy(full_path, ctx->UNC, unc_len); -	pos = full_path + unc_len; - -	if (pplen) { -		*pos = CIFS_DIR_SEP(cifs_sb); -		memcpy(pos + 1, ctx->prepath, pplen); -		pos += pplen; -	} - -	*pos = '\0'; /* add trailing null */ -	convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb)); -	cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path); -	return full_path; -} -  static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)  {  	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; @@ -179,6 +146,7 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)  	struct TCP_Server_Info *server;  	struct cifs_tcon *tcon;  	char *origin_fullpath = NULL; +	char sep = CIFS_DIR_SEP(cifs_sb);  	int num_links = 0;  	int rc; @@ -186,7 +154,7 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)  	if (IS_ERR(ref_path))  		return PTR_ERR(ref_path); -	full_path = build_unc_path_to_root(ctx, cifs_sb, true); +	full_path = smb3_fs_context_fullpath(ctx, sep);  	if (IS_ERR(full_path)) {  		rc = PTR_ERR(full_path);  		full_path = NULL; @@ -228,7 +196,7 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)  				kfree(full_path);  				ref_path = full_path = NULL; -				full_path = build_unc_path_to_root(ctx, cifs_sb, true); +				full_path = smb3_fs_context_fullpath(ctx, sep);  				if (IS_ERR(full_path)) {  					rc = PTR_ERR(full_path);  					full_path = NULL; @@ -249,14 +217,12 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)  		server = mnt_ctx->server;  		tcon = mnt_ctx->tcon; -		mutex_lock(&server->refpath_lock); -		spin_lock(&server->srv_lock); -		if (!server->origin_fullpath) { -			server->origin_fullpath = origin_fullpath; +		spin_lock(&tcon->tc_lock); +		if (!tcon->origin_fullpath) { +			tcon->origin_fullpath = origin_fullpath;  			origin_fullpath = NULL;  		} -		spin_unlock(&server->srv_lock); -		mutex_unlock(&server->refpath_lock); +		spin_unlock(&tcon->tc_lock);  		if (list_empty(&tcon->dfs_ses_list)) {  			list_replace_init(&mnt_ctx->dfs_ses_list, @@ -279,18 +245,13 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)  {  	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;  	struct cifs_ses *ses; -	char *source = ctx->source;  	bool nodfs = ctx->nodfs;  	int rc;  	*isdfs = false; -	/* Temporarily set @ctx->source to NULL as we're not matching DFS -	 * superblocks yet.  See cifs_match_super() and match_server(). -	 */ -	ctx->source = NULL;  	rc = get_session(mnt_ctx, NULL);  	if (rc) -		goto out; +		return rc;  	ctx->dfs_root_ses = mnt_ctx->ses;  	/* @@ -303,8 +264,9 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)  	if (!nodfs) {  		rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL);  		if (rc) { -			if (rc != -ENOENT && rc != -EOPNOTSUPP && rc != -EIO) -				goto out; +			cifs_dbg(FYI, "%s: no dfs referral for %s: %d\n", +				 __func__, ctx->UNC + 1, rc); +			cifs_dbg(FYI, "%s: assuming non-dfs mount...\n", __func__);  			nodfs = true;  		}  	} @@ -312,7 +274,7 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)  		rc = cifs_mount_get_tcon(mnt_ctx);  		if (!rc)  			rc = cifs_is_path_remote(mnt_ctx); -		goto out; +		return rc;  	}  	*isdfs = true; @@ -328,12 +290,7 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)  	rc = __dfs_mount_share(mnt_ctx);  	if (ses == ctx->dfs_root_ses)  		cifs_put_smb_ses(ses); -out: -	/* -	 * Restore previous value of @ctx->source so DFS superblock can be -	 * matched in cifs_match_super(). -	 */ -	ctx->source = source; +  	return rc;  } @@ -567,11 +524,11 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru  	int rc;  	struct TCP_Server_Info *server = tcon->ses->server;  	const struct smb_version_operations *ops = server->ops; -	struct super_block *sb = NULL; -	struct cifs_sb_info *cifs_sb;  	struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); -	char *tree; +	struct cifs_sb_info *cifs_sb = NULL; +	struct super_block *sb = NULL;  	struct dfs_info3_param ref = {0}; +	char *tree;  	/* only send once per connect */  	spin_lock(&tcon->tc_lock); @@ -603,19 +560,18 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru  		goto out;  	} -	sb = cifs_get_tcp_super(server); -	if (IS_ERR(sb)) { -		rc = PTR_ERR(sb); -		cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc); -		goto out; -	} - -	cifs_sb = CIFS_SB(sb); +	sb = cifs_get_dfs_tcon_super(tcon); +	if (!IS_ERR(sb)) +		cifs_sb = CIFS_SB(sb); -	/* If it is not dfs or there was no cached dfs referral, then reconnect to same share */ -	if (!server->leaf_fullpath || +	/* +	 * Tree connect to last share in @tcon->tree_name whether dfs super or +	 * cached dfs referral was not found. +	 */ +	if (!cifs_sb || !server->leaf_fullpath ||  	    dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) { -		rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls); +		rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, +				       cifs_sb ? cifs_sb->local_nls : nlsc);  		goto out;  	}  |