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

Merge tag 'v6.17-rc-part1-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client updates from Steve French:

 - Fix network namespace refcount leak

 - Multichannel reconnect fix

 - Perf improvement to not do unneeded EA query on native symlinks

 - Performance improvement for directory leases to allow extending lease
   for actively queried directories

 - Improve debugging of directory leases by adding pseudofile to show
   them

 - Five minor mount cleanup patches

 - Minor directory lease cleanup patch

 - Allow creating special files via reparse points over SMB1

 - Two minor improvements to FindFirst over SMB1

 - Two NTLMSSP session setup fixes

* tag 'v6.17-rc-part1-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb3 client: add way to show directory leases for improved debugging
  smb: client: get rid of kstrdup() when parsing iocharset mount option
  smb: client: get rid of kstrdup() when parsing domain mount option
  smb: client: get rid of kstrdup() when parsing pass2 mount option
  smb: client: get rid of kstrdup() when parsing pass mount option
  smb: client: get rid of kstrdup() when parsing user mount option
  cifs: Add support for creating reparse points over SMB1
  cifs: Do not query WSL EAs for native SMB symlink
  cifs: Optimize CIFSFindFirst() response when not searching
  cifs: Fix calling CIFSFindFirst() for root path without msearch
  smb: client: fix session setup against servers that require SPN
  smb: client: allow parsing zero-length AV pairs
  cifs: add new field to track the last access time of cfid
  smb: change return type of cached_dir_lease_break() to bool
  cifs: reset iface weights when we cannot find a candidate
  smb: client: fix netns refcount leak after net_passive changes
parents f2d282e1 844e5c0e
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -195,6 +195,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
	 * from @cfids->entries.  Caller will put last reference if the latter.
	 */
	if (cfid->has_lease && cfid->time) {
		cfid->last_access_time = jiffies;
		spin_unlock(&cfids->cfid_list_lock);
		*ret_cfid = cfid;
		kfree(utf16_path);
@@ -363,6 +364,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
		cfid->file_all_info_is_valid = true;

	cfid->time = jiffies;
	cfid->last_access_time = jiffies;
	spin_unlock(&cfids->cfid_list_lock);
	/* At this point the directory handle is fully cached */
	rc = 0;
@@ -617,7 +619,7 @@ static void cached_dir_put_work(struct work_struct *work)
	queue_work(serverclose_wq, &cfid->close_work);
}

int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
{
	struct cached_fids *cfids = tcon->cfids;
	struct cached_fid *cfid;
@@ -730,8 +732,8 @@ static void cfids_laundromat_worker(struct work_struct *work)

	spin_lock(&cfids->cfid_list_lock);
	list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
		if (cfid->time &&
		    time_after(jiffies, cfid->time + HZ * dir_cache_timeout)) {
		if (cfid->last_access_time &&
		    time_after(jiffies, cfid->last_access_time + HZ * dir_cache_timeout)) {
			cfid->on_list = false;
			list_move(&cfid->entry, &entry);
			cfids->num_entries--;
+2 −2
Original line number Diff line number Diff line
@@ -14,7 +14,6 @@ struct cached_dirent {
	char *name;
	int namelen;
	loff_t pos;

	struct cifs_fattr fattr;
};

@@ -39,6 +38,7 @@ struct cached_fid {
	bool on_list:1;
	bool file_all_info_is_valid:1;
	unsigned long time; /* jiffies of when lease was taken */
	unsigned long last_access_time; /* jiffies of when last accessed */
	struct kref refcount;
	struct cifs_fid fid;
	spinlock_t fid_lock;
@@ -80,6 +80,6 @@ extern void drop_cached_dir_by_name(const unsigned int xid,
				    struct cifs_sb_info *cifs_sb);
extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
extern bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);

#endif			/* _CACHED_DIR_H */
+53 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include "smbdirect.h"
#endif
#include "cifs_swn.h"
#include "cached_dir.h"

void
cifs_dump_mem(char *label, void *data, int length)
@@ -280,6 +281,54 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
	return 0;
}

static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
{
	struct list_head *stmp, *tmp, *tmp1;
	struct TCP_Server_Info *server;
	struct cifs_ses *ses;
	struct cifs_tcon *tcon;
	struct cached_fids *cfids;
	struct cached_fid *cfid;
	LIST_HEAD(entry);

	seq_puts(m, "# Version:1\n");
	seq_puts(m, "# Format:\n");
	seq_puts(m, "# <tree id> <sess id> <persistent fid> <path>\n");

	spin_lock(&cifs_tcp_ses_lock);
	list_for_each(stmp, &cifs_tcp_ses_list) {
		server = list_entry(stmp, struct TCP_Server_Info,
				    tcp_ses_list);
		list_for_each(tmp, &server->smb_ses_list) {
			ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
			list_for_each(tmp1, &ses->tcon_list) {
				tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
				cfids = tcon->cfids;
				spin_lock(&cfids->cfid_list_lock); /* check lock ordering */
				seq_printf(m, "Num entries: %d\n", cfids->num_entries);
				list_for_each_entry(cfid, &cfids->entries, entry) {
					seq_printf(m, "0x%x 0x%llx 0x%llx     %s",
						tcon->tid,
						ses->Suid,
						cfid->fid.persistent_fid,
						cfid->path);
					if (cfid->file_all_info_is_valid)
						seq_printf(m, "\tvalid file info");
					if (cfid->dirents.is_valid)
						seq_printf(m, ", valid dirents");
					seq_printf(m, "\n");
				}
				spin_unlock(&cfids->cfid_list_lock);


			}
		}
	}
	spin_unlock(&cifs_tcp_ses_lock);
	seq_putc(m, '\n');
	return 0;
}

static __always_inline const char *compression_alg_str(__le16 alg)
{
	switch (alg) {
@@ -863,6 +912,9 @@ cifs_proc_init(void)
	proc_create_single("open_files", 0400, proc_fs_cifs,
			cifs_debug_files_proc_show);

	proc_create_single("open_dirs", 0400, proc_fs_cifs,
			cifs_debug_dirs_proc_show);

	proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_ops);
	proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_ops);
	proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_ops);
@@ -907,6 +959,7 @@ cifs_proc_clean(void)

	remove_proc_entry("DebugData", proc_fs_cifs);
	remove_proc_entry("open_files", proc_fs_cifs);
	remove_proc_entry("open_dirs", proc_fs_cifs);
	remove_proc_entry("cifsFYI", proc_fs_cifs);
	remove_proc_entry("traceSMB", proc_fs_cifs);
	remove_proc_entry("Stats", proc_fs_cifs);
+63 −20
Original line number Diff line number Diff line
@@ -343,7 +343,7 @@ static struct ntlmssp2_name *find_next_av(struct cifs_ses *ses,
	len = AV_LEN(av);
	if (AV_TYPE(av) == NTLMSSP_AV_EOL)
		return NULL;
	if (!len || (u8 *)av + sizeof(*av) + len > end)
	if ((u8 *)av + sizeof(*av) + len > end)
		return NULL;
	return av;
}
@@ -363,7 +363,7 @@ static int find_av_name(struct cifs_ses *ses, u16 type, char **name, u16 maxlen)

	av_for_each_entry(ses, av) {
		len = AV_LEN(av);
		if (AV_TYPE(av) != type)
		if (AV_TYPE(av) != type || !len)
			continue;
		if (!IS_ALIGNED(len, sizeof(__le16))) {
			cifs_dbg(VFS | ONCE, "%s: bad length(%u) for type %u\n",
@@ -532,17 +532,67 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash, struct shash_
	return rc;
}

/*
 * Set up NTLMv2 response blob with SPN (cifs/<hostname>) appended to the
 * existing list of AV pairs.
 */
static int set_auth_key_response(struct cifs_ses *ses)
{
	size_t baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp);
	size_t len, spnlen, tilen = 0, num_avs = 2 /* SPN + EOL */;
	struct TCP_Server_Info *server = ses->server;
	char *spn __free(kfree) = NULL;
	struct ntlmssp2_name *av;
	char *rsp = NULL;
	int rc;

	spnlen = strlen(server->hostname);
	len = sizeof("cifs/") + spnlen;
	spn = kmalloc(len, GFP_KERNEL);
	if (!spn) {
		rc = -ENOMEM;
		goto out;
	}

	spnlen = scnprintf(spn, len, "cifs/%.*s",
			   (int)spnlen, server->hostname);

	av_for_each_entry(ses, av)
		tilen += sizeof(*av) + AV_LEN(av);

	len = baselen + tilen + spnlen * sizeof(__le16) + num_avs * sizeof(*av);
	rsp = kmalloc(len, GFP_KERNEL);
	if (!rsp) {
		rc = -ENOMEM;
		goto out;
	}

	memcpy(rsp + baselen, ses->auth_key.response, tilen);
	av = (void *)(rsp + baselen + tilen);
	av->type = cpu_to_le16(NTLMSSP_AV_TARGET_NAME);
	av->length = cpu_to_le16(spnlen * sizeof(__le16));
	cifs_strtoUTF16((__le16 *)av->data, spn, spnlen, ses->local_nls);
	av = (void *)((__u8 *)av + sizeof(*av) + AV_LEN(av));
	av->type = cpu_to_le16(NTLMSSP_AV_EOL);
	av->length = 0;

	rc = 0;
	ses->auth_key.len = len;
out:
	ses->auth_key.response = rsp;
	return rc;
}

int
setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
{
	struct shash_desc *hmacmd5 = NULL;
	int rc;
	int baselen;
	unsigned int tilen;
	unsigned char *tiblob = NULL; /* target info blob */
	struct ntlmv2_resp *ntlmv2;
	char ntlmv2_hash[16];
	unsigned char *tiblob = NULL; /* target info blob */
	__le64 rsp_timestamp;
	__u64 cc;
	int rc;

	if (nls_cp == NULL) {
		cifs_dbg(VFS, "%s called with nls_cp==NULL\n", __func__);
@@ -588,32 +638,25 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
	 * (as Windows 7 does)
	 */
	rsp_timestamp = find_timestamp(ses);
	get_random_bytes(&cc, sizeof(cc));

	baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp);
	tilen = ses->auth_key.len;
	tiblob = ses->auth_key.response;
	cifs_server_lock(ses->server);

	ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL);
	if (!ses->auth_key.response) {
		rc = -ENOMEM;
	tiblob = ses->auth_key.response;
	rc = set_auth_key_response(ses);
	if (rc) {
		ses->auth_key.len = 0;
		goto setup_ntlmv2_rsp_ret;
		goto unlock;
	}
	ses->auth_key.len += baselen;

	ntlmv2 = (struct ntlmv2_resp *)
			(ses->auth_key.response + CIFS_SESS_KEY_SIZE);
	ntlmv2->blob_signature = cpu_to_le32(0x00000101);
	ntlmv2->reserved = 0;
	ntlmv2->time = rsp_timestamp;

	get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal));
	ntlmv2->client_chal = cc;
	ntlmv2->reserved2 = 0;

	memcpy(ses->auth_key.response + baselen, tiblob, tilen);

	cifs_server_lock(ses->server);

	rc = cifs_alloc_hash("hmac(md5)", &hmacmd5);
	if (rc) {
		cifs_dbg(VFS, "Could not allocate HMAC-MD5, rc=%d\n", rc);
+8 −6
Original line number Diff line number Diff line
@@ -627,12 +627,14 @@ struct smb_version_operations {
	bool (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv);
	struct reparse_data_buffer * (*get_reparse_point_buffer)(const struct kvec *rsp_iov,
								 u32 *plen);
	int (*create_reparse_symlink)(const unsigned int xid,
				      struct inode *inode,
				      struct dentry *dentry,
	struct inode * (*create_reparse_inode)(struct cifs_open_info_data *data,
					       struct super_block *sb,
					       const unsigned int xid,
					       struct cifs_tcon *tcon,
					       const char *full_path,
				      const char *symname);
					       bool directory,
					       struct kvec *reparse_iov,
					       struct kvec *xattr_iov);
};

struct smb_version_values {
Loading