Commit c8103c27 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag '5.16-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull more cifs updates from Steve French:

 - improvements to reconnect and multichannel

 - a performance improvement (additional use of SMB3 compounding)

 - DFS code cleanup and improvements

 - various trivial Coverity fixes

 - two fscache fixes

 - an fsync fix

* tag '5.16-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: (23 commits)
  cifs: do not duplicate fscache cookie for secondary channels
  cifs: connect individual channel servers to primary channel server
  cifs: protect session channel fields with chan_lock
  cifs: do not negotiate session if session already exists
  smb3: do not setup the fscache_super_cookie until fsinfo initialized
  cifs: fix potential use-after-free bugs
  cifs: fix memory leak of smb3_fs_context_dup::server_hostname
  smb3: add additional null check in SMB311_posix_mkdir
  cifs: release lock earlier in dequeue_mid error case
  smb3: add additional null check in SMB2_tcon
  smb3: add additional null check in SMB2_open
  smb3: add additional null check in SMB2_ioctl
  smb3: remove trivial dfs compile warning
  cifs: support nested dfs links over reconnect
  smb3: do not error on fsync when readonly
  cifs: for compound requests, use open handle if possible
  cifs: set a minimum of 120s for next dns resolution
  cifs: split out dfs code from cifs_reconnect()
  cifs: convert list_for_each to entry variant
  cifs: introduce new helper for cifs_reconnect()
  ...
parents d0b51bfb 46bb1b94
Loading
Loading
Loading
Loading
+6 −1
Original line number Original line Diff line number Diff line
@@ -271,7 +271,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
	c = 0;
	c = 0;
	spin_lock(&cifs_tcp_ses_lock);
	spin_lock(&cifs_tcp_ses_lock);
	list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
	list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
		if (server->is_channel)
		/* channel info will be printed as a part of sessions below */
		if (CIFS_SERVER_IS_CHAN(server))
			continue;
			continue;


		c++;
		c++;
@@ -358,6 +359,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
			seq_printf(m, " signed");
			seq_printf(m, " signed");
		if (server->posix_ext_supported)
		if (server->posix_ext_supported)
			seq_printf(m, " posix");
			seq_printf(m, " posix");
		if (server->nosharesock)
			seq_printf(m, " nosharesock");


		if (server->rdma)
		if (server->rdma)
			seq_printf(m, "\nRDMA ");
			seq_printf(m, "\nRDMA ");
@@ -412,12 +415,14 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
				   from_kuid(&init_user_ns, ses->linux_uid),
				   from_kuid(&init_user_ns, ses->linux_uid),
				   from_kuid(&init_user_ns, ses->cred_uid));
				   from_kuid(&init_user_ns, ses->cred_uid));


			spin_lock(&ses->chan_lock);
			if (ses->chan_count > 1) {
			if (ses->chan_count > 1) {
				seq_printf(m, "\n\n\tExtra Channels: %zu ",
				seq_printf(m, "\n\n\tExtra Channels: %zu ",
					   ses->chan_count-1);
					   ses->chan_count-1);
				for (j = 1; j < ses->chan_count; j++)
				for (j = 1; j < ses->chan_count; j++)
					cifs_dump_channel(m, j, &ses->chans[j]);
					cifs_dump_channel(m, j, &ses->chans[j]);
			}
			}
			spin_unlock(&ses->chan_lock);


			seq_puts(m, "\n\n\tShares: ");
			seq_puts(m, "\n\n\tShares: ");
			j = 0;
			j = 0;
+2 −57
Original line number Original line Diff line number Diff line
@@ -307,12 +307,8 @@ static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
{
{
	struct cifs_sb_info *cifs_sb;
	struct cifs_sb_info *cifs_sb;
	struct cifs_ses *ses;
	struct cifs_tcon *tcon;
	void *page;
	void *page;
	char *full_path, *root_path;
	char *full_path;
	unsigned int xid;
	int rc;
	struct vfsmount *mnt;
	struct vfsmount *mnt;


	cifs_dbg(FYI, "in %s\n", __func__);
	cifs_dbg(FYI, "in %s\n", __func__);
@@ -324,8 +320,6 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
	 * the double backslashes usually used in the UNC. This function
	 * the double backslashes usually used in the UNC. This function
	 * gives us the latter, so we must adjust the result.
	 * gives us the latter, so we must adjust the result.
	 */
	 */
	mnt = ERR_PTR(-ENOMEM);

	cifs_sb = CIFS_SB(mntpt->d_sb);
	cifs_sb = CIFS_SB(mntpt->d_sb);
	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) {
	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) {
		mnt = ERR_PTR(-EREMOTE);
		mnt = ERR_PTR(-EREMOTE);
@@ -341,60 +335,11 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
	}
	}


	convert_delimiter(full_path, '\\');
	convert_delimiter(full_path, '\\');

	cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
	cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);


	if (!cifs_sb_master_tlink(cifs_sb)) {
		cifs_dbg(FYI, "%s: master tlink is NULL\n", __func__);
		goto free_full_path;
	}

	tcon = cifs_sb_master_tcon(cifs_sb);
	if (!tcon) {
		cifs_dbg(FYI, "%s: master tcon is NULL\n", __func__);
		goto free_full_path;
	}

	root_path = kstrdup(tcon->treeName, GFP_KERNEL);
	if (!root_path) {
		mnt = ERR_PTR(-ENOMEM);
		goto free_full_path;
	}
	cifs_dbg(FYI, "%s: root path: %s\n", __func__, root_path);

	ses = tcon->ses;
	xid = get_xid();

	/*
	 * If DFS root has been expired, then unconditionally fetch it again to
	 * refresh DFS referral cache.
	 */
	rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
			    root_path + 1, NULL, NULL);
	if (!rc) {
		rc = dfs_cache_find(xid, ses, cifs_sb->local_nls,
				    cifs_remap(cifs_sb), full_path + 1,
				    NULL, NULL);
	}

	free_xid(xid);

	if (rc) {
		mnt = ERR_PTR(rc);
		goto free_root_path;
	}
	/*
	 * OK - we were able to get and cache a referral for @full_path.
	 *
	 * Now, pass it down to cifs_mount() and it will retry every available
	 * node server in case of failures - no need to do it here.
	 */
	mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path);
	mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path);
	cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__,
	cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt);
		 full_path + 1, mnt);


free_root_path:
	kfree(root_path);
free_full_path:
free_full_path:
	free_dentry_path(page);
	free_dentry_path(page);
cdda_exit:
cdda_exit:
+0 −5
Original line number Original line Diff line number Diff line
@@ -61,11 +61,6 @@ struct cifs_sb_info {
	/* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */
	/* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */
	char *prepath;
	char *prepath;


	/*
	 * Canonical DFS path initially provided by the mount call. We might connect to something
	 * different via DFS but we want to keep it to do failover properly.
	 */
	char *origin_fullpath; /* \\HOST\SHARE\[OPTIONAL PATH] */
	/* randomly generated 128-bit number for indexing dfs mount groups in referral cache */
	/* randomly generated 128-bit number for indexing dfs mount groups in referral cache */
	uuid_t dfs_mount_id;
	uuid_t dfs_mount_id;
	/*
	/*
+44 −3
Original line number Original line Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/mempool.h>
#include <linux/mempool.h>
#include <linux/workqueue.h>
#include <linux/workqueue.h>
#include <linux/utsname.h>
#include "cifs_fs_sb.h"
#include "cifs_fs_sb.h"
#include "cifsacl.h"
#include "cifsacl.h"
#include <crypto/internal/hash.h>
#include <crypto/internal/hash.h>
@@ -75,7 +76,8 @@
#define SMB_ECHO_INTERVAL_MAX 600
#define SMB_ECHO_INTERVAL_MAX 600
#define SMB_ECHO_INTERVAL_DEFAULT 60
#define SMB_ECHO_INTERVAL_DEFAULT 60


/* dns resolution interval in seconds */
/* dns resolution intervals in seconds */
#define SMB_DNS_RESOLVE_INTERVAL_MIN     120
#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600
#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600


/* maximum number of PDUs in one compound */
/* maximum number of PDUs in one compound */
@@ -99,6 +101,8 @@
#define XATTR_DOS_ATTRIB "user.DOSATTRIB"
#define XATTR_DOS_ATTRIB "user.DOSATTRIB"
#endif
#endif


#define CIFS_MAX_WORKSTATION_LEN  (__NEW_UTS_LEN + 1)  /* reasonable max for client */

/*
/*
 * CIFS vfs client Status information (based on what we know.)
 * CIFS vfs client Status information (based on what we know.)
 */
 */
@@ -592,6 +596,7 @@ struct TCP_Server_Info {
	struct list_head pending_mid_q;
	struct list_head pending_mid_q;
	bool noblocksnd;		/* use blocking sendmsg */
	bool noblocksnd;		/* use blocking sendmsg */
	bool noautotune;		/* do not autotune send buf sizes */
	bool noautotune;		/* do not autotune send buf sizes */
	bool nosharesock;
	bool tcp_nodelay;
	bool tcp_nodelay;
	unsigned int credits;  /* send no more requests at once */
	unsigned int credits;  /* send no more requests at once */
	unsigned int max_credits; /* can override large 32000 default at mnt */
	unsigned int max_credits; /* can override large 32000 default at mnt */
@@ -685,13 +690,34 @@ struct TCP_Server_Info {
	 */
	 */
	int nr_targets;
	int nr_targets;
	bool noblockcnt; /* use non-blocking connect() */
	bool noblockcnt; /* use non-blocking connect() */
	bool is_channel; /* if a session channel */

	/*
	 * If this is a session channel,
	 * primary_server holds the ref-counted
	 * pointer to primary channel connection for the session.
	 */
#define CIFS_SERVER_IS_CHAN(server)	(!!(server)->primary_server)
	struct TCP_Server_Info *primary_server;

#ifdef CONFIG_CIFS_SWN_UPCALL
#ifdef CONFIG_CIFS_SWN_UPCALL
	bool use_swn_dstaddr;
	bool use_swn_dstaddr;
	struct sockaddr_storage swn_dstaddr;
	struct sockaddr_storage swn_dstaddr;
#endif
#endif
#ifdef CONFIG_CIFS_DFS_UPCALL
#ifdef CONFIG_CIFS_DFS_UPCALL
	bool is_dfs_conn; /* if a dfs connection */
	bool is_dfs_conn; /* if a dfs connection */
	struct mutex refpath_lock; /* protects leaf_fullpath */
	/*
	 * Canonical DFS full paths that were used to chase referrals in mount and reconnect.
	 *
	 * origin_fullpath: first or original referral path
	 * leaf_fullpath: last referral path (might be changed due to nested links in reconnect)
	 *
	 * current_fullpath: pointer to either origin_fullpath or leaf_fullpath
	 * NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect()
	 *
	 * format: \\HOST\SHARE\[OPTIONAL PATH]
	 */
	char *origin_fullpath, *leaf_fullpath, *current_fullpath;
#endif
#endif
};
};


@@ -908,6 +934,7 @@ struct cifs_ses {
				   and after mount option parsing we fill it */
				   and after mount option parsing we fill it */
	char *domainName;
	char *domainName;
	char *password;
	char *password;
	char *workstation_name;
	struct session_key auth_key;
	struct session_key auth_key;
	struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
	struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
	enum securityEnum sectype; /* what security flavor was specified? */
	enum securityEnum sectype; /* what security flavor was specified? */
@@ -933,16 +960,21 @@ struct cifs_ses {
	 * iface_lock should be taken when accessing any of these fields
	 * iface_lock should be taken when accessing any of these fields
	 */
	 */
	spinlock_t iface_lock;
	spinlock_t iface_lock;
	/* ========= begin: protected by iface_lock ======== */
	struct cifs_server_iface *iface_list;
	struct cifs_server_iface *iface_list;
	size_t iface_count;
	size_t iface_count;
	unsigned long iface_last_update; /* jiffies */
	unsigned long iface_last_update; /* jiffies */
	/* ========= end: protected by iface_lock ======== */


	spinlock_t chan_lock;
	/* ========= begin: protected by chan_lock ======== */
#define CIFS_MAX_CHANNELS 16
#define CIFS_MAX_CHANNELS 16
	struct cifs_chan chans[CIFS_MAX_CHANNELS];
	struct cifs_chan chans[CIFS_MAX_CHANNELS];
	struct cifs_chan *binding_chan;
	struct cifs_chan *binding_chan;
	size_t chan_count;
	size_t chan_count;
	size_t chan_max;
	size_t chan_max;
	atomic_t chan_seq; /* round robin state */
	atomic_t chan_seq; /* round robin state */
	/* ========= end: protected by chan_lock ======== */
};
};


/*
/*
@@ -1091,7 +1123,6 @@ struct cifs_tcon {
	struct cached_fid crfid; /* Cached root fid */
	struct cached_fid crfid; /* Cached root fid */
	/* BB add field for back pointer to sb struct(s)? */
	/* BB add field for back pointer to sb struct(s)? */
#ifdef CONFIG_CIFS_DFS_UPCALL
#ifdef CONFIG_CIFS_DFS_UPCALL
	char *dfs_path; /* canonical DFS path */
	struct list_head ulist; /* cache update list */
	struct list_head ulist; /* cache update list */
#endif
#endif
};
};
@@ -1942,4 +1973,14 @@ static inline bool is_tcon_dfs(struct cifs_tcon *tcon)
		tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT);
		tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT);
}
}


static inline bool cifs_is_referral_server(struct cifs_tcon *tcon,
					   const struct dfs_info3_param *ref)
{
	/*
	 * Check if all targets are capable of handling DFS referrals as per
	 * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL.
	 */
	return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER));
}

#endif	/* _CIFS_GLOB_H */
#endif	/* _CIFS_GLOB_H */
+7 −3
Original line number Original line Diff line number Diff line
@@ -269,8 +269,9 @@ extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon);


extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
				const char *path);
				const char *path);

extern struct TCP_Server_Info *
extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx);
cifs_get_tcp_session(struct smb3_fs_context *ctx,
		     struct TCP_Server_Info *primary_server);
extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
				 int from_reconnect);
				 int from_reconnect);
extern void cifs_put_tcon(struct cifs_tcon *tcon);
extern void cifs_put_tcon(struct cifs_tcon *tcon);
@@ -607,7 +608,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,


struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
void cifs_put_tcp_super(struct super_block *sb);
void cifs_put_tcp_super(struct super_block *sb);
int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix);
char *extract_hostname(const char *unc);
char *extract_hostname(const char *unc);
char *extract_sharename(const char *unc);
char *extract_sharename(const char *unc);


@@ -634,4 +635,7 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
		return options;
		return options;
}
}


struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
void cifs_put_tcon_super(struct super_block *sb);

#endif			/* _CIFSPROTO_H */
#endif			/* _CIFSPROTO_H */
Loading