diff options
| -rw-r--r-- | fs/cifs/cifsacl.h | 4 | ||||
| -rw-r--r-- | fs/cifs/cifsglob.h | 2 | ||||
| -rw-r--r-- | fs/cifs/cifsproto.h | 9 | ||||
| -rw-r--r-- | fs/cifs/cifssmb.c | 151 | ||||
| -rw-r--r-- | fs/cifs/connect.c | 508 | ||||
| -rw-r--r-- | fs/cifs/dfs_cache.c | 136 | ||||
| -rw-r--r-- | fs/cifs/dfs_cache.h | 7 | ||||
| -rw-r--r-- | fs/cifs/inode.c | 2 | ||||
| -rw-r--r-- | fs/cifs/misc.c | 7 | ||||
| -rw-r--r-- | fs/cifs/netmisc.c | 27 | ||||
| -rw-r--r-- | fs/cifs/sess.c | 4 | ||||
| -rw-r--r-- | fs/cifs/smb1ops.c | 4 | ||||
| -rw-r--r-- | fs/cifs/smb2misc.c | 73 | ||||
| -rw-r--r-- | fs/cifs/smb2pdu.c | 115 | ||||
| -rw-r--r-- | fs/cifs/smb2pdu.h | 2 | ||||
| -rw-r--r-- | fs/cifs/transport.c | 2 | 
16 files changed, 560 insertions, 493 deletions
| diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h index 17562ea00e18..45665ff87b64 100644 --- a/fs/cifs/cifsacl.h +++ b/fs/cifs/cifsacl.h @@ -132,7 +132,7 @@ struct cifs_ace {  /*   * The current SMB3 form of security descriptor is similar to what was used for   * cifs (see above) but some fields are split, and fields in the struct below - * matches names of fields to the the spec, MS-DTYP (see sections 2.4.5 and + * matches names of fields to the spec, MS-DTYP (see sections 2.4.5 and   * 2.4.6). Note that "CamelCase" fields are used in this struct in order to   * match the MS-DTYP and MS-SMB2 specs which define the wire format.   */ @@ -178,7 +178,7 @@ struct smb3_acl {  /*   * Used to store the special 'NFS SIDs' used to persist the POSIX uid and gid - * See See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx   */  struct owner_sid {  	u8 Revision; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index f4b88cd02662..b296964b8afa 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1466,7 +1466,7 @@ struct cifsInodeInfo {  	struct list_head llist;	/* locks helb by this inode */  	/*  	 * NOTE: Some code paths call down_read(lock_sem) twice, so -	 * we must always use use cifs_down_write() instead of down_write() +	 * we must always use cifs_down_write() instead of down_write()  	 * for this semaphore to avoid deadlocks.  	 */  	struct rw_semaphore lock_sem;	/* protect the fields above */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 7a836ec0438e..bb68cbf81074 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -154,6 +154,7 @@ extern int decode_negTokenInit(unsigned char *security_blob, int length,  extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);  extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port);  extern int map_smb_to_linux_error(char *buf, bool logErr); +extern int map_and_check_smb_error(struct mid_q_entry *mid, bool logErr);  extern void header_assemble(struct smb_hdr *, char /* command */ ,  			    const struct cifs_tcon *, int /* length of  			    fixed section (word count) in two byte units */); @@ -271,6 +272,9 @@ extern void cifs_move_llist(struct list_head *source, struct list_head *dest);  extern void cifs_free_llist(struct list_head *llist);  extern void cifs_del_lock_waiters(struct cifsLockInfo *lock); +extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, +			     const struct nls_table *nlsc); +  extern int cifs_negotiate_protocol(const unsigned int xid,  				   struct cifs_ses *ses);  extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, @@ -344,7 +348,7 @@ extern int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,  extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,  			const char *fileName, const FILE_BASIC_INFO *data,  			const struct nls_table *nls_codepage, -			int remap_special_chars); +			struct cifs_sb_info *cifs_sb);  extern int CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,  			const FILE_BASIC_INFO *data, __u16 fid,  			__u32 pid_of_opener); @@ -613,8 +617,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);  void cifs_put_tcp_super(struct super_block *sb); -int update_super_prepath(struct cifs_tcon *tcon, const char *prefix, -			 size_t prefix_len); +int update_super_prepath(struct cifs_tcon *tcon, char *prefix);  #ifdef CONFIG_CIFS_DFS_UPCALL  static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index bf41ee048396..0e763d2dcf16 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -124,116 +124,6 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)  	 */  } -#ifdef CONFIG_CIFS_DFS_UPCALL -static int __cifs_reconnect_tcon(const struct nls_table *nlsc, -				 struct cifs_tcon *tcon) -{ -	int rc; -	struct TCP_Server_Info *server = tcon->ses->server; -	struct dfs_cache_tgt_list tl; -	struct dfs_cache_tgt_iterator *it = NULL; -	char *tree; -	const char *tcp_host; -	size_t tcp_host_len; -	const char *dfs_host; -	size_t dfs_host_len; - -	tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); -	if (!tree) -		return -ENOMEM; - -	if (!tcon->dfs_path) { -		if (tcon->ipc) { -			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", -				  server->hostname); -			rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc); -		} else { -			rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nlsc); -		} -		goto out; -	} - -	rc = dfs_cache_noreq_find(tcon->dfs_path + 1, NULL, &tl); -	if (rc) -		goto out; - -	extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); - -	for (it = dfs_cache_get_tgt_iterator(&tl); it; -	     it = dfs_cache_get_next_tgt(&tl, it)) { -		const char *share, *prefix; -		size_t share_len, prefix_len; -		bool target_match; - -		rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix, -					     &prefix_len); -		if (rc) { -			cifs_dbg(VFS, "%s: failed to parse target share %d\n", -				 __func__, rc); -			continue; -		} - -		extract_unc_hostname(share, &dfs_host, &dfs_host_len); - -		if (dfs_host_len != tcp_host_len -		    || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { -			cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", -				 __func__, -				 (int)dfs_host_len, dfs_host, -				 (int)tcp_host_len, tcp_host); - -			rc = match_target_ip(server, dfs_host, dfs_host_len, -					     &target_match); -			if (rc) { -				cifs_dbg(VFS, "%s: failed to match target ip: %d\n", -					 __func__, rc); -				break; -			} - -			if (!target_match) { -				cifs_dbg(FYI, "%s: skipping target\n", __func__); -				continue; -			} -		} - -		if (tcon->ipc) { -			scnprintf(tree, MAX_TREE_SIZE, "\\\\%.*s\\IPC$", -				  (int)share_len, share); -			rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc); -		} else { -			scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, -				  share); -			rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc); -			if (!rc) { -				rc = update_super_prepath(tcon, prefix, -							  prefix_len); -				break; -			} -		} -		if (rc == -EREMOTE) -			break; -	} - -	if (!rc) { -		if (it) -			rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, -							    it); -		else -			rc = -ENOENT; -	} -	dfs_cache_free_tgts(&tl); -out: -	kfree(tree); -	return rc; -} -#else -static inline int __cifs_reconnect_tcon(const struct nls_table *nlsc, -					struct cifs_tcon *tcon) -{ -	return CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nlsc); -} -#endif -  /* reconnect the socket, tcon, and smb session if needed */  static int  cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) @@ -338,7 +228,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)  	}  	cifs_mark_open_files_invalid(tcon); -	rc = __cifs_reconnect_tcon(nls_codepage, tcon); +	rc = cifs_tree_connect(0, tcon, nls_codepage);  	mutex_unlock(&ses->session_mutex);  	cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); @@ -5913,10 +5803,42 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,  	return rc;  } +static int +CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon, +		     const char *fileName, const FILE_BASIC_INFO *data, +		     const struct nls_table *nls_codepage, +		     struct cifs_sb_info *cifs_sb) +{ +	int oplock = 0; +	struct cifs_open_parms oparms; +	struct cifs_fid fid; +	int rc; + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = GENERIC_WRITE; +	oparms.create_options = cifs_create_options(cifs_sb, 0); +	oparms.disposition = FILE_OPEN; +	oparms.path = fileName; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL); +	if (rc) +		goto out; + +	rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid); +	CIFSSMBClose(xid, tcon, fid.netfid); +out: + +	return rc; +} +  int  CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,  		   const char *fileName, const FILE_BASIC_INFO *data, -		   const struct nls_table *nls_codepage, int remap) +		   const struct nls_table *nls_codepage, +		     struct cifs_sb_info *cifs_sb)  {  	TRANSACTION2_SPI_REQ *pSMB = NULL;  	TRANSACTION2_SPI_RSP *pSMBr = NULL; @@ -5925,6 +5847,7 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,  	int bytes_returned = 0;  	char *data_offset;  	__u16 params, param_offset, offset, byte_count, count; +	int remap = cifs_remap(cifs_sb);  	cifs_dbg(FYI, "In SetTimes\n"); @@ -5987,6 +5910,10 @@ SetTimesRetry:  	if (rc == -EAGAIN)  		goto SetTimesRetry; +	if (rc == -EOPNOTSUPP) +		return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data, +					    nls_codepage, cifs_sb); +  	return rc;  } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a61abde09ffe..7e3e5e2098eb 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -393,15 +393,14 @@ static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)  #ifdef CONFIG_CIFS_DFS_UPCALL  /* These functions must be called with server->srv_mutex held */ -static void reconn_inval_dfs_target(struct TCP_Server_Info *server, -				    struct cifs_sb_info *cifs_sb, -				    struct dfs_cache_tgt_list *tgt_list, -				    struct dfs_cache_tgt_iterator **tgt_it) +static void reconn_set_next_dfs_target(struct TCP_Server_Info *server, +				       struct cifs_sb_info *cifs_sb, +				       struct dfs_cache_tgt_list *tgt_list, +				       struct dfs_cache_tgt_iterator **tgt_it)  {  	const char *name; -	if (!cifs_sb || !cifs_sb->origin_fullpath || !tgt_list || -	    !server->nr_targets) +	if (!cifs_sb || !cifs_sb->origin_fullpath)  		return;  	if (!*tgt_it) { @@ -471,11 +470,13 @@ cifs_reconnect(struct TCP_Server_Info *server)  		sb = NULL;  	} else {  		cifs_sb = CIFS_SB(sb); -  		rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list); -		if (rc && (rc != -EOPNOTSUPP)) { -			cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n", -				 __func__); +		if (rc) { +			cifs_sb = NULL; +			if (rc != -EOPNOTSUPP) { +				cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n", +						__func__); +			}  		} else {  			server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list);  		} @@ -578,7 +579,7 @@ cifs_reconnect(struct TCP_Server_Info *server)  		 * feature is disabled, then we will retry last server we  		 * connected to before.  		 */ -		reconn_inval_dfs_target(server, cifs_sb, &tgt_list, &tgt_it); +		reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);  #endif  		rc = reconn_set_ipaddr(server);  		if (rc) { @@ -4422,11 +4423,11 @@ build_unc_path_to_root(const struct smb_vol *vol,  static int  expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,  		    struct smb_vol *volume_info, struct cifs_sb_info *cifs_sb, -		    int check_prefix) +		    char *ref_path)  {  	int rc;  	struct dfs_info3_param referral = {0}; -	char *full_path = NULL, *ref_path = NULL, *mdata = NULL; +	char *full_path = NULL, *mdata = NULL;  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)  		return -EREMOTE; @@ -4435,9 +4436,6 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,  	if (IS_ERR(full_path))  		return PTR_ERR(full_path); -	/* For DFS paths, skip the first '\' of the UNC */ -	ref_path = check_prefix ? full_path + 1 : volume_info->UNC + 1; -  	rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),  			    ref_path, &referral, NULL);  	if (!rc) { @@ -4500,13 +4498,10 @@ static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it,  	return 0;  } -static int setup_dfs_tgt_conn(const char *path, +static int setup_dfs_tgt_conn(const char *path, const char *full_path,  			      const struct dfs_cache_tgt_iterator *tgt_it, -			      struct cifs_sb_info *cifs_sb, -			      struct smb_vol *vol, -			      unsigned int *xid, -			      struct TCP_Server_Info **server, -			      struct cifs_ses **ses, +			      struct cifs_sb_info *cifs_sb, struct smb_vol *vol, unsigned int *xid, +			      struct TCP_Server_Info **server, struct cifs_ses **ses,  			      struct cifs_tcon **tcon)  {  	int rc; @@ -4520,8 +4515,7 @@ static int setup_dfs_tgt_conn(const char *path,  	if (rc)  		return rc; -	mdata = cifs_compose_mount_options(cifs_sb->mountdata, path, &ref, -					   &fake_devname); +	mdata = cifs_compose_mount_options(cifs_sb->mountdata, full_path + 1, &ref, &fake_devname);  	free_dfs_info_param(&ref);  	if (IS_ERR(mdata)) { @@ -4544,7 +4538,7 @@ static int setup_dfs_tgt_conn(const char *path,  		mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon);  		rc = mount_get_conns(&fake_vol, cifs_sb, xid, server, ses,  				     tcon); -		if (!rc) { +		if (!rc || (*server && *ses)) {  			/*  			 * We were able to connect to new target server.  			 * Update current volume info with new target server. @@ -4556,14 +4550,10 @@ static int setup_dfs_tgt_conn(const char *path,  	return rc;  } -static int mount_do_dfs_failover(const char *path, -				 struct cifs_sb_info *cifs_sb, -				 struct smb_vol *vol, -				 struct cifs_ses *root_ses, -				 unsigned int *xid, -				 struct TCP_Server_Info **server, -				 struct cifs_ses **ses, -				 struct cifs_tcon **tcon) +static int do_dfs_failover(const char *path, const char *full_path, struct cifs_sb_info *cifs_sb, +			   struct smb_vol *vol, struct cifs_ses *root_ses, unsigned int *xid, +			   struct TCP_Server_Info **server, struct cifs_ses **ses, +			   struct cifs_tcon **tcon)  {  	int rc;  	struct dfs_cache_tgt_list tgt_list; @@ -4582,9 +4572,9 @@ static int mount_do_dfs_failover(const char *path,  		if (rc)  			break;  		/* Connect to next DFS target */ -		rc = setup_dfs_tgt_conn(path, tgt_it, cifs_sb, vol, xid, server, -					ses, tcon); -		if (!rc || rc == -EACCES || rc == -EOPNOTSUPP) +		rc = setup_dfs_tgt_conn(path, full_path, tgt_it, cifs_sb, vol, xid, server, ses, +					tcon); +		if (!rc || (*server && *ses))  			break;  	}  	if (!rc) { @@ -4754,207 +4744,210 @@ static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb_vol *vol,  }  #ifdef CONFIG_CIFS_DFS_UPCALL -static inline void set_root_tcon(struct cifs_sb_info *cifs_sb, -				 struct cifs_tcon *tcon, -				 struct cifs_tcon **root) +static void set_root_ses(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, +			 struct cifs_ses **root_ses)  { -	spin_lock(&cifs_tcp_ses_lock); -	tcon->tc_count++; -	tcon->remap = cifs_remap(cifs_sb); -	spin_unlock(&cifs_tcp_ses_lock); -	*root = tcon; +	if (ses) { +		spin_lock(&cifs_tcp_ses_lock); +		ses->ses_count++; +		ses->tcon_ipc->remap = cifs_remap(cifs_sb); +		spin_unlock(&cifs_tcp_ses_lock); +	} +	*root_ses = ses; +} + +static void put_root_ses(struct cifs_ses *ses) +{ +	if (ses) +		cifs_put_smb_ses(ses); +} + +/* Check if a path component is remote and then update @dfs_path accordingly */ +static int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb_vol *vol, +			     const unsigned int xid, struct TCP_Server_Info *server, +			     struct cifs_tcon *tcon, char **dfs_path) +{ +	char *path, *s; +	char sep = CIFS_DIR_SEP(cifs_sb), tmp; +	char *npath; +	int rc = 0; +	int added_treename = tcon->Flags & SMB_SHARE_IS_IN_DFS; +	int skip = added_treename; + +	path = cifs_build_path_to_root(vol, cifs_sb, tcon, added_treename); +	if (!path) +		return -ENOMEM; + +	/* +	 * Walk through the path components in @path and check if they're accessible. In case any of +	 * the components is -EREMOTE, then update @dfs_path with the next DFS referral request path +	 * (NOT including the remaining components). +	 */ +	s = path; +	do { +		/* skip separators */ +		while (*s && *s == sep) +			s++; +		if (!*s) +			break; +		/* next separator */ +		while (*s && *s != sep) +			s++; +		/* +		 * if the treename is added, we then have to skip the first +		 * part within the separators +		 */ +		if (skip) { +			skip = 0; +			continue; +		} +		tmp = *s; +		*s = 0; +		rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, path); +		if (rc && rc == -EREMOTE) { +			struct smb_vol v = {NULL}; +			/* if @path contains a tree name, skip it in the prefix path */ +			if (added_treename) { +				rc = cifs_parse_devname(path, &v); +				if (rc) +					break; +				rc = -EREMOTE; +				npath = build_unc_path_to_root(&v, cifs_sb, true); +				cifs_cleanup_volume_info_contents(&v); +			} else { +				v.UNC = vol->UNC; +				v.prepath = path + 1; +				npath = build_unc_path_to_root(&v, cifs_sb, true); +			} +			if (IS_ERR(npath)) { +				rc = PTR_ERR(npath); +				break; +			} +			kfree(*dfs_path); +			*dfs_path = npath; +		} +		*s = tmp; +	} while (rc == 0); + +	kfree(path); +	return rc;  }  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 *root_tcon = NULL; +	struct TCP_Server_Info *server = NULL; +	struct cifs_ses *ses = NULL, *root_ses = NULL;  	struct cifs_tcon *tcon = NULL; -	struct TCP_Server_Info *server; -	char *root_path = NULL, *full_path = NULL; -	char *old_mountdata, *origin_mountdata = NULL; -	int count; +	int count = 0; +	char *ref_path = NULL, *full_path = NULL; +	char *oldmnt = NULL; +	char *mntdata = NULL;  	rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon); -	if (!rc && tcon) { -		/* If not a standalone DFS root, then check if path is remote */ -		rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, -				    cifs_remap(cifs_sb), vol->UNC + 1, NULL, -				    NULL); -		if (rc) { -			rc = is_path_remote(cifs_sb, vol, xid, server, tcon); -			if (!rc) -				goto out; -			if (rc != -EREMOTE) -				goto error; -		} -	}  	/* -	 * If first DFS target server went offline and we failed to connect it, -	 * server and ses pointers are NULL at this point, though we still have -	 * chance to get a cached DFS referral in expand_dfs_referral() and -	 * retry next target available in it. +	 * Unconditionally try to get an DFS referral (even cached) to determine whether it is an +	 * DFS mount.  	 * -	 * If a NULL ses ptr is passed to dfs_cache_find(), a lookup will be -	 * performed against DFS path and *no* requests will be sent to server -	 * for any new DFS referrals. Hence it's safe to skip checking whether -	 * server or ses ptr is NULL. +	 * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem +	 * to respond with PATH_NOT_COVERED to requests that include the prefix.  	 */ -	if (rc == -EACCES || rc == -EOPNOTSUPP) -		goto error; - -	root_path = build_unc_path_to_root(vol, cifs_sb, false); -	if (IS_ERR(root_path)) { -		rc = PTR_ERR(root_path); -		root_path = NULL; -		goto error; -	} - -	full_path = build_unc_path_to_root(vol, cifs_sb, true); -	if (IS_ERR(full_path)) { -		rc = PTR_ERR(full_path); -		full_path = NULL; -		goto error; -	} -	/* -	 * Perform an unconditional check for whether there are DFS -	 * referrals for this path without prefix, to provide support -	 * for DFS referrals from w2k8 servers which don't seem to respond -	 * with PATH_NOT_COVERED to requests that include the prefix. -	 * Chase the referral if found, otherwise continue normally. -	 */ -	old_mountdata = cifs_sb->mountdata; -	(void)expand_dfs_referral(xid, ses, vol, cifs_sb, false); - -	if (cifs_sb->mountdata == NULL) { -		rc = -ENOENT; -		goto error; +	if (dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), vol->UNC + 1, NULL, +			   NULL)) { +		/* No DFS referral was returned.  Looks like a regular share. */ +		if (rc) +			goto error; +		/* Check if it is fully accessible and then mount it */ +		rc = is_path_remote(cifs_sb, vol, xid, server, tcon); +		if (!rc) +			goto out; +		if (rc != -EREMOTE) +			goto error;  	} - -	/* Save DFS root volume information for DFS refresh worker */ -	origin_mountdata = kstrndup(cifs_sb->mountdata, -				    strlen(cifs_sb->mountdata), GFP_KERNEL); -	if (!origin_mountdata) { +	/* Save mount options */ +	mntdata = kstrndup(cifs_sb->mountdata, strlen(cifs_sb->mountdata), GFP_KERNEL); +	if (!mntdata) {  		rc = -ENOMEM;  		goto error;  	} - -	if (cifs_sb->mountdata != old_mountdata) { -		/* If we were redirected, reconnect to new target server */ -		mount_put_conns(cifs_sb, xid, server, ses, tcon); -		rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon); -	} -	if (rc) { -		if (rc == -EACCES || rc == -EOPNOTSUPP) -			goto error; -		/* Perform DFS failover to any other DFS targets */ -		rc = mount_do_dfs_failover(root_path + 1, cifs_sb, vol, NULL, -					   &xid, &server, &ses, &tcon); -		if (rc) -			goto error; -	} - -	kfree(root_path); -	root_path = build_unc_path_to_root(vol, cifs_sb, false); -	if (IS_ERR(root_path)) { -		rc = PTR_ERR(root_path); -		root_path = NULL; +	/* Get path of DFS root */ +	ref_path = build_unc_path_to_root(vol, cifs_sb, false); +	if (IS_ERR(ref_path)) { +		rc = PTR_ERR(ref_path); +		ref_path = NULL;  		goto error;  	} -	/* Cache out resolved root server */ -	(void)dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), -			     root_path + 1, NULL, NULL); -	kfree(root_path); -	root_path = NULL; - -	set_root_tcon(cifs_sb, tcon, &root_tcon); - -	for (count = 1; ;) { -		if (!rc && tcon) { -			rc = is_path_remote(cifs_sb, vol, xid, server, tcon); -			if (!rc || rc != -EREMOTE) -				break; -		} -		/* -		 * 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). -		 */ -		if (count++ > MAX_NESTED_LINKS) { -			rc = -ELOOP; -			break; -		} +	set_root_ses(cifs_sb, ses, &root_ses); +	do { +		/* Save full path of last DFS path we used to resolve final target server */  		kfree(full_path); -		full_path = build_unc_path_to_root(vol, cifs_sb, true); +		full_path = build_unc_path_to_root(vol, cifs_sb, !!count);  		if (IS_ERR(full_path)) {  			rc = PTR_ERR(full_path); -			full_path = NULL;  			break;  		} - -		old_mountdata = cifs_sb->mountdata; -		rc = expand_dfs_referral(xid, root_tcon->ses, vol, cifs_sb, -					 true); +		/* Chase referral */ +		oldmnt = cifs_sb->mountdata; +		rc = expand_dfs_referral(xid, root_ses, vol, cifs_sb, ref_path + 1);  		if (rc)  			break; - -		if (cifs_sb->mountdata != old_mountdata) { +		/* Connect to new DFS target only if we were redirected */ +		if (oldmnt != cifs_sb->mountdata) {  			mount_put_conns(cifs_sb, xid, server, ses, tcon); -			rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, -					     &tcon); -			/* -			 * Ensure that DFS referrals go through new root server. -			 */ -			if (!rc && tcon && -			    (tcon->share_flags & (SHI1005_FLAGS_DFS | -						  SHI1005_FLAGS_DFS_ROOT))) { -				cifs_put_tcon(root_tcon); -				set_root_tcon(cifs_sb, tcon, &root_tcon); -			} +			rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);  		} -		if (rc) { -			if (rc == -EACCES || rc == -EOPNOTSUPP) -				break; -			/* Perform DFS failover to any other DFS targets */ -			rc = mount_do_dfs_failover(full_path + 1, cifs_sb, vol, -						   root_tcon->ses, &xid, -						   &server, &ses, &tcon); -			if (rc == -EACCES || rc == -EOPNOTSUPP || !server || -			    !ses) -				goto error; +		if (rc && !server && !ses) { +			/* Failed to connect. Try to connect to other targets in the referral. */ +			rc = do_dfs_failover(ref_path + 1, full_path, cifs_sb, vol, root_ses, &xid, +					     &server, &ses, &tcon);  		} -	} -	cifs_put_tcon(root_tcon); +		if (rc == -EACCES || rc == -EOPNOTSUPP || !server || !ses) +			break; +		if (!tcon) +			continue; +		/* Make sure that requests go through new root servers */ +		if (tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT)) { +			put_root_ses(root_ses); +			set_root_ses(cifs_sb, ses, &root_ses); +		} +		/* Check for remaining path components and then continue chasing them (-EREMOTE) */ +		rc = check_dfs_prepath(cifs_sb, vol, xid, server, tcon, &ref_path); +		/* Prevent recursion on broken link referrals */ +		if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS) +			rc = -ELOOP; +	} while (rc == -EREMOTE);  	if (rc)  		goto error; - -	spin_lock(&cifs_tcp_ses_lock); -	if (!tcon->dfs_path) { -		/* Save full path in new tcon to do failover when reconnecting tcons */ -		tcon->dfs_path = full_path; -		full_path = NULL; -		tcon->remap = cifs_remap(cifs_sb); -	} -	cifs_sb->origin_fullpath = kstrndup(tcon->dfs_path, -					    strlen(tcon->dfs_path), -					    GFP_ATOMIC); +	put_root_ses(root_ses); +	root_ses = NULL; +	kfree(ref_path); +	ref_path = NULL; +	/* +	 * Store DFS full path in both superblock and tree connect structures. +	 * +	 * For DFS root mounts, the prefix path (cifs_sb->prepath) is preserved during reconnect so +	 * only the root path is set in cifs_sb->origin_fullpath and tcon->dfs_path. And for DFS +	 * links, the prefix path is included in both and may be changed during reconnect.  See +	 * cifs_tree_connect(). +	 */ +	cifs_sb->origin_fullpath = kstrndup(full_path, strlen(full_path), GFP_KERNEL);  	if (!cifs_sb->origin_fullpath) { -		spin_unlock(&cifs_tcp_ses_lock);  		rc = -ENOMEM;  		goto error;  	} +	spin_lock(&cifs_tcp_ses_lock); +	tcon->dfs_path = full_path; +	full_path = NULL; +	tcon->remap = cifs_remap(cifs_sb);  	spin_unlock(&cifs_tcp_ses_lock); -	rc = dfs_cache_add_vol(origin_mountdata, vol, cifs_sb->origin_fullpath); -	if (rc) { -		kfree(cifs_sb->origin_fullpath); +	/* Add original volume information for DFS cache to be used when refreshing referrals */ +	rc = dfs_cache_add_vol(mntdata, vol, cifs_sb->origin_fullpath); +	if (rc)  		goto error; -	}  	/*  	 * After reconnecting to a different server, unique ids won't  	 * match anymore, so we disable serverino. This prevents @@ -4976,9 +4969,11 @@ out:  	return mount_setup_tlink(cifs_sb, ses, tcon);  error: +	kfree(ref_path);  	kfree(full_path); -	kfree(root_path); -	kfree(origin_mountdata); +	kfree(mntdata); +	kfree(cifs_sb->origin_fullpath); +	put_root_ses(root_ses);  	mount_put_conns(cifs_sb, xid, server, ses, tcon);  	return rc;  } @@ -5114,8 +5109,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,  	bcc_ptr += strlen("?????");  	bcc_ptr += 1;  	count = bcc_ptr - &pSMB->Password[0]; -	pSMB->hdr.smb_buf_length = cpu_to_be32(be32_to_cpu( -					pSMB->hdr.smb_buf_length) + count); +	be32_add_cpu(&pSMB->hdr.smb_buf_length, count);  	pSMB->ByteCount = cpu_to_le16(count);  	rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, @@ -5533,3 +5527,115 @@ cifs_prune_tlinks(struct work_struct *work)  	queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,  				TLINK_IDLE_EXPIRE);  } + +#ifdef CONFIG_CIFS_DFS_UPCALL +int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) +{ +	int rc; +	struct TCP_Server_Info *server = tcon->ses->server; +	const struct smb_version_operations *ops = server->ops; +	struct dfs_cache_tgt_list tl; +	struct dfs_cache_tgt_iterator *it = NULL; +	char *tree; +	const char *tcp_host; +	size_t tcp_host_len; +	const char *dfs_host; +	size_t dfs_host_len; +	char *share = NULL, *prefix = NULL; +	struct dfs_info3_param ref = {0}; +	bool isroot; + +	tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); +	if (!tree) +		return -ENOMEM; + +	if (!tcon->dfs_path) { +		if (tcon->ipc) { +			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); +			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); +		} else { +			rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc); +		} +		goto out; +	} + +	rc = dfs_cache_noreq_find(tcon->dfs_path + 1, &ref, &tl); +	if (rc) +		goto out; +	isroot = ref.server_type == DFS_TYPE_ROOT; +	free_dfs_info_param(&ref); + +	extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); + +	for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) { +		bool target_match; + +		kfree(share); +		kfree(prefix); +		share = NULL; +		prefix = NULL; + +		rc = dfs_cache_get_tgt_share(tcon->dfs_path + 1, it, &share, &prefix); +		if (rc) { +			cifs_dbg(VFS, "%s: failed to parse target share %d\n", +				 __func__, rc); +			continue; +		} + +		extract_unc_hostname(share, &dfs_host, &dfs_host_len); + +		if (dfs_host_len != tcp_host_len +		    || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { +			cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len, +				 dfs_host, (int)tcp_host_len, tcp_host); + +			rc = match_target_ip(server, dfs_host, dfs_host_len, &target_match); +			if (rc) { +				cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); +				break; +			} + +			if (!target_match) { +				cifs_dbg(FYI, "%s: skipping target\n", __func__); +				continue; +			} +		} + +		if (tcon->ipc) { +			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", share); +			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); +		} else { +			scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); +			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); +			/* Only handle prefix paths of DFS link targets */ +			if (!rc && !isroot) { +				rc = update_super_prepath(tcon, prefix); +				break; +			} +		} +		if (rc == -EREMOTE) +			break; +	} + +	kfree(share); +	kfree(prefix); + +	if (!rc) { +		if (it) +			rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, it); +		else +			rc = -ENOENT; +	} +	dfs_cache_free_tgts(&tl); +out: +	kfree(tree); +	return rc; +} +#else +int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) +{ +	const struct smb_version_operations *ops = tcon->ses->server->ops; + +	return ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc); +} +#endif diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c index df81c718d2fa..a44f58bbf7ab 100644 --- a/fs/cifs/dfs_cache.c +++ b/fs/cifs/dfs_cache.c @@ -29,6 +29,7 @@  struct cache_dfs_tgt {  	char *name; +	int path_consumed;  	struct list_head list;  }; @@ -350,7 +351,7 @@ static inline struct timespec64 get_expire_time(int ttl)  }  /* Allocate a new DFS target */ -static struct cache_dfs_tgt *alloc_target(const char *name) +static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed)  {  	struct cache_dfs_tgt *t; @@ -362,6 +363,7 @@ static struct cache_dfs_tgt *alloc_target(const char *name)  		kfree(t);  		return ERR_PTR(-ENOMEM);  	} +	t->path_consumed = path_consumed;  	INIT_LIST_HEAD(&t->list);  	return t;  } @@ -384,7 +386,7 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,  	for (i = 0; i < numrefs; i++) {  		struct cache_dfs_tgt *t; -		t = alloc_target(refs[i].node_name); +		t = alloc_target(refs[i].node_name, refs[i].path_consumed);  		if (IS_ERR(t)) {  			free_tgts(ce);  			return PTR_ERR(t); @@ -490,16 +492,7 @@ static int add_cache_entry(const char *path, unsigned int hash,  	return 0;  } -/* - * Find a DFS cache entry in hash table and optionally check prefix path against - * @path. - * Use whole path components in the match. - * Must be called with htable_rw_lock held. - * - * Return ERR_PTR(-ENOENT) if the entry is not found. - */ -static struct cache_entry *lookup_cache_entry(const char *path, -					      unsigned int *hash) +static struct cache_entry *__lookup_cache_entry(const char *path)  {  	struct cache_entry *ce;  	unsigned int h; @@ -517,9 +510,75 @@ static struct cache_entry *lookup_cache_entry(const char *path,  	if (!found)  		ce = ERR_PTR(-ENOENT); +	return ce; +} + +/* + * Find a DFS cache entry in hash table and optionally check prefix path against + * @path. + * Use whole path components in the match. + * Must be called with htable_rw_lock held. + * + * Return ERR_PTR(-ENOENT) if the entry is not found. + */ +static struct cache_entry *lookup_cache_entry(const char *path, unsigned int *hash) +{ +	struct cache_entry *ce = ERR_PTR(-ENOENT); +	unsigned int h; +	int cnt = 0; +	char *npath; +	char *s, *e; +	char sep; + +	npath = kstrndup(path, strlen(path), GFP_KERNEL); +	if (!npath) +		return ERR_PTR(-ENOMEM); + +	s = npath; +	sep = *npath; +	while ((s = strchr(s, sep)) && ++cnt < 3) +		s++; + +	if (cnt < 3) { +		h = cache_entry_hash(path, strlen(path)); +		ce = __lookup_cache_entry(path); +		goto out; +	} +	/* +	 * Handle paths that have more than two path components and are a complete prefix of the DFS +	 * referral request path (@path). +	 * +	 * See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request". +	 */ +	h = cache_entry_hash(npath, strlen(npath)); +	e = npath + strlen(npath) - 1; +	while (e > s) { +		char tmp; + +		/* skip separators */ +		while (e > s && *e == sep) +			e--; +		if (e == s) +			goto out; + +		tmp = *(e+1); +		*(e+1) = 0; + +		ce = __lookup_cache_entry(npath); +		if (!IS_ERR(ce)) { +			h = cache_entry_hash(npath, strlen(npath)); +			break; +		} + +		*(e+1) = tmp; +		/* backward until separator */ +		while (e > s && *e != sep) +			e--; +	} +out:  	if (hash)  		*hash = h; - +	kfree(npath);  	return ce;  } @@ -773,6 +832,7 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)  			rc = -ENOMEM;  			goto err_free_it;  		} +		it->it_path_consumed = t->path_consumed;  		if (ce->tgthint == t)  			list_add(&it->it_list, head); @@ -1263,23 +1323,26 @@ void dfs_cache_del_vol(const char *fullpath)  /**   * dfs_cache_get_tgt_share - parse a DFS target   * + * @path: DFS full path   * @it: DFS target iterator.   * @share: tree name. - * @share_len: length of tree name.   * @prefix: prefix path. - * @prefix_len: length of prefix path.   *   * Return zero if target was parsed correctly, otherwise non-zero.   */ -int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it, -			    const char **share, size_t *share_len, -			    const char **prefix, size_t *prefix_len) +int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, +			    char **share, char **prefix)  { -	char *s, sep; +	char *s, sep, *p; +	size_t len; +	size_t plen1, plen2; -	if (!it || !share || !share_len || !prefix || !prefix_len) +	if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed)  		return -EINVAL; +	*share = NULL; +	*prefix = NULL; +  	sep = it->it_name[0];  	if (sep != '\\' && sep != '/')  		return -EINVAL; @@ -1288,13 +1351,38 @@ int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,  	if (!s)  		return -EINVAL; +	/* point to prefix in target node */  	s = strchrnul(s + 1, sep); -	*share = it->it_name; -	*share_len = s - it->it_name; -	*prefix = *s ? s + 1 : s; -	*prefix_len = &it->it_name[strlen(it->it_name)] - *prefix; +	/* extract target share */ +	*share = kstrndup(it->it_name, s - it->it_name, GFP_KERNEL); +	if (!*share) +		return -ENOMEM; +	/* skip separator */ +	if (*s) +		s++; +	/* point to prefix in DFS path */ +	p = path + it->it_path_consumed; +	if (*p == sep) +		p++; + +	/* merge prefix paths from DFS path and target node */ +	plen1 = it->it_name + strlen(it->it_name) - s; +	plen2 = path + strlen(path) - p; +	if (plen1 || plen2) { +		len = plen1 + plen2 + 2; +		*prefix = kmalloc(len, GFP_KERNEL); +		if (!*prefix) { +			kfree(*share); +			*share = NULL; +			return -ENOMEM; +		} +		if (plen1) +			scnprintf(*prefix, len, "%.*s%c%.*s", (int)plen1, s, sep, (int)plen2, p); +		else +			strscpy(*prefix, p, len); +	}  	return 0;  } diff --git a/fs/cifs/dfs_cache.h b/fs/cifs/dfs_cache.h index bf94d08cfb5a..3d7c05194536 100644 --- a/fs/cifs/dfs_cache.h +++ b/fs/cifs/dfs_cache.h @@ -19,6 +19,7 @@ struct dfs_cache_tgt_list {  struct dfs_cache_tgt_iterator {  	char *it_name; +	int it_path_consumed;  	struct list_head it_list;  }; @@ -48,10 +49,8 @@ extern int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol,  extern int dfs_cache_update_vol(const char *fullpath,  				struct TCP_Server_Info *server);  extern void dfs_cache_del_vol(const char *fullpath); - -extern int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it, -				   const char **share, size_t *share_len, -				   const char **prefix, size_t *prefix_len); +extern int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, +				   char **share, char **prefix);  static inline struct dfs_cache_tgt_iterator *  dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl, diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index ce95801e9b66..3989d08396ac 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1086,7 +1086,6 @@ smb311_posix_get_inode_info(struct inode **inode,  		    struct super_block *sb, unsigned int xid)  {  	struct cifs_tcon *tcon; -	struct TCP_Server_Info *server;  	struct tcon_link *tlink;  	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);  	bool adjust_tz = false; @@ -1100,7 +1099,6 @@ smb311_posix_get_inode_info(struct inode **inode,  	if (IS_ERR(tlink))  		return PTR_ERR(tlink);  	tcon = tlink_tcon(tlink); -	server = tcon->ses->server;  	/*  	 * 1. Fetch file metadata diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index e44d049142d0..764234803071 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -1164,8 +1164,7 @@ static inline void cifs_put_tcon_super(struct super_block *sb)  }  #endif -int update_super_prepath(struct cifs_tcon *tcon, const char *prefix, -			 size_t prefix_len) +int update_super_prepath(struct cifs_tcon *tcon, char *prefix)  {  	struct super_block *sb;  	struct cifs_sb_info *cifs_sb; @@ -1179,8 +1178,8 @@ int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,  	kfree(cifs_sb->prepath); -	if (*prefix && prefix_len) { -		cifs_sb->prepath = kstrndup(prefix, prefix_len, GFP_ATOMIC); +	if (prefix && *prefix) { +		cifs_sb->prepath = kstrndup(prefix, strlen(prefix), GFP_ATOMIC);  		if (!cifs_sb->prepath) {  			rc = -ENOMEM;  			goto out; diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index b7ca4960d4ca..0e728aac67e9 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -881,6 +881,33 @@ map_smb_to_linux_error(char *buf, bool logErr)  	return rc;  } +int +map_and_check_smb_error(struct mid_q_entry *mid, bool logErr) +{ +	int rc; +	struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf; + +	rc = map_smb_to_linux_error((char *)smb, logErr); +	if (rc == -EACCES && !(smb->Flags2 & SMBFLG2_ERR_STATUS)) { +		/* possible ERRBaduid */ +		__u8 class = smb->Status.DosError.ErrorClass; +		__u16 code = le16_to_cpu(smb->Status.DosError.Error); + +		/* switch can be used to handle different errors */ +		if (class == ERRSRV && code == ERRbaduid) { +			cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n", +				code); +			spin_lock(&GlobalMid_Lock); +			if (mid->server->tcpStatus != CifsExiting) +				mid->server->tcpStatus = CifsNeedReconnect; +			spin_unlock(&GlobalMid_Lock); +		} +	} + +	return rc; +} + +  /*   * calculate the size of the SMB message based on the fixed header   * portion, the number of word parameters and the data portion of the message diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 5d05bd2822d2..69cd5856621b 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -938,8 +938,7 @@ sess_sendreceive(struct sess_data *sess_data)  	struct kvec rsp_iov = { NULL, 0 };  	count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len; -	smb_buf->smb_buf_length = -		cpu_to_be32(be32_to_cpu(smb_buf->smb_buf_length) + count); +	be32_add_cpu(&smb_buf->smb_buf_length, count);  	put_bcc(count, smb_buf);  	rc = SendReceive2(sess_data->xid, sess_data->ses, @@ -1705,7 +1704,6 @@ static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)  #else  		cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");  		return -ENOSYS; -		break;  #endif /* CONFIG_CIFS_UPCALL */  	case RawNTLMSSP:  		sess_data->func = sess_auth_rawntlmssp_negotiate; diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 197ed455e657..80287c26cfac 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -688,7 +688,7 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,  	dosattrs = cifsInode->cifsAttrs|ATTR_READONLY;  	info.Attributes = cpu_to_le32(dosattrs);  	rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls, -				cifs_remap(cifs_sb)); +				cifs_sb);  	if (rc == 0)  		cifsInode->cifsAttrs = dosattrs;  } @@ -783,7 +783,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,  	tcon = tlink_tcon(tlink);  	rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls, -				cifs_remap(cifs_sb)); +				cifs_sb);  	if (rc == 0) {  		cinode->cifsAttrs = le32_to_cpu(buf->Attributes);  		goto out; diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 157992864ce7..d88e2683626e 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -508,15 +508,31 @@ cifs_ses_oplock_break(struct work_struct *work)  	kfree(lw);  } +static void +smb2_queue_pending_open_break(struct tcon_link *tlink, __u8 *lease_key, +			      __le32 new_lease_state) +{ +	struct smb2_lease_break_work *lw; + +	lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); +	if (!lw) { +		cifs_put_tlink(tlink); +		return; +	} + +	INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); +	lw->tlink = tlink; +	lw->lease_state = new_lease_state; +	memcpy(lw->lease_key, lease_key, SMB2_LEASE_KEY_SIZE); +	queue_work(cifsiod_wq, &lw->lease_break); +} +  static bool -smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, -		    struct smb2_lease_break_work *lw) +smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp)  { -	bool found;  	__u8 lease_state;  	struct list_head *tmp;  	struct cifsFileInfo *cfile; -	struct cifs_pending_open *open;  	struct cifsInodeInfo *cinode;  	int ack_req = le32_to_cpu(rsp->Flags &  				  SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); @@ -546,22 +562,29 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,  		cfile->oplock_level = lease_state;  		cifs_queue_oplock_break(cfile); -		kfree(lw);  		return true;  	} -	found = false; +	return false; +} + +static struct cifs_pending_open * +smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon, +				  struct smb2_lease_break *rsp) +{ +	__u8 lease_state = le32_to_cpu(rsp->NewLeaseState); +	int ack_req = le32_to_cpu(rsp->Flags & +				  SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); +	struct cifs_pending_open *open; +	struct cifs_pending_open *found = NULL; +  	list_for_each_entry(open, &tcon->pending_opens, olist) {  		if (memcmp(open->lease_key, rsp->LeaseKey,  			   SMB2_LEASE_KEY_SIZE))  			continue;  		if (!found && ack_req) { -			found = true; -			memcpy(lw->lease_key, open->lease_key, -			       SMB2_LEASE_KEY_SIZE); -			lw->tlink = cifs_get_tlink(open->tlink); -			queue_work(cifsiod_wq, &lw->lease_break); +			found = open;  		}  		cifs_dbg(FYI, "found in the pending open list\n"); @@ -582,14 +605,7 @@ smb2_is_valid_lease_break(char *buffer)  	struct TCP_Server_Info *server;  	struct cifs_ses *ses;  	struct cifs_tcon *tcon; -	struct smb2_lease_break_work *lw; - -	lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); -	if (!lw) -		return false; - -	INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); -	lw->lease_state = rsp->NewLeaseState; +	struct cifs_pending_open *open;  	cifs_dbg(FYI, "Checking for lease break\n"); @@ -607,11 +623,27 @@ smb2_is_valid_lease_break(char *buffer)  				spin_lock(&tcon->open_file_lock);  				cifs_stats_inc(  				    &tcon->stats.cifs_stats.num_oplock_brks); -				if (smb2_tcon_has_lease(tcon, rsp, lw)) { +				if (smb2_tcon_has_lease(tcon, rsp)) {  					spin_unlock(&tcon->open_file_lock);  					spin_unlock(&cifs_tcp_ses_lock);  					return true;  				} +				open = smb2_tcon_find_pending_open_lease(tcon, +									 rsp); +				if (open) { +					__u8 lease_key[SMB2_LEASE_KEY_SIZE]; +					struct tcon_link *tlink; + +					tlink = cifs_get_tlink(open->tlink); +					memcpy(lease_key, open->lease_key, +					       SMB2_LEASE_KEY_SIZE); +					spin_unlock(&tcon->open_file_lock); +					spin_unlock(&cifs_tcp_ses_lock); +					smb2_queue_pending_open_break(tlink, +								      lease_key, +								      rsp->NewLeaseState); +					return true; +				}  				spin_unlock(&tcon->open_file_lock);  				if (tcon->crfid.is_valid && @@ -629,7 +661,6 @@ smb2_is_valid_lease_break(char *buffer)  		}  	}  	spin_unlock(&cifs_tcp_ses_lock); -	kfree(lw);  	cifs_dbg(FYI, "Can not process lease break - no lease matched\n");  	return false;  } diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 2f4cdd290c46..24c2ac360591 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -152,117 +152,6 @@ out:  	return;  } -#ifdef CONFIG_CIFS_DFS_UPCALL -static int __smb2_reconnect(const struct nls_table *nlsc, -			    struct cifs_tcon *tcon) -{ -	int rc; -	struct TCP_Server_Info *server = tcon->ses->server; -	struct dfs_cache_tgt_list tl; -	struct dfs_cache_tgt_iterator *it = NULL; -	char *tree; -	const char *tcp_host; -	size_t tcp_host_len; -	const char *dfs_host; -	size_t dfs_host_len; - -	tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); -	if (!tree) -		return -ENOMEM; - -	if (!tcon->dfs_path) { -		if (tcon->ipc) { -			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", -				  server->hostname); -			rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc); -		} else { -			rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, -				       nlsc); -		} -		goto out; -	} - -	rc = dfs_cache_noreq_find(tcon->dfs_path + 1, NULL, &tl); -	if (rc) -		goto out; - -	extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); - -	for (it = dfs_cache_get_tgt_iterator(&tl); it; -	     it = dfs_cache_get_next_tgt(&tl, it)) { -		const char *share, *prefix; -		size_t share_len, prefix_len; -		bool target_match; - -		rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix, -					     &prefix_len); -		if (rc) { -			cifs_dbg(VFS, "%s: failed to parse target share %d\n", -				 __func__, rc); -			continue; -		} - -		extract_unc_hostname(share, &dfs_host, &dfs_host_len); - -		if (dfs_host_len != tcp_host_len -		    || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { -			cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", -				 __func__, -				 (int)dfs_host_len, dfs_host, -				 (int)tcp_host_len, tcp_host); - -			rc = match_target_ip(server, dfs_host, dfs_host_len, -					     &target_match); -			if (rc) { -				cifs_dbg(VFS, "%s: failed to match target ip: %d\n", -					 __func__, rc); -				break; -			} - -			if (!target_match) { -				cifs_dbg(FYI, "%s: skipping target\n", __func__); -				continue; -			} -		} - -		if (tcon->ipc) { -			scnprintf(tree, MAX_TREE_SIZE, "\\\\%.*s\\IPC$", -				  (int)share_len, share); -			rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc); -		} else { -			scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, -				  share); -			rc = SMB2_tcon(0, tcon->ses, tree, tcon, nlsc); -			if (!rc) { -				rc = update_super_prepath(tcon, prefix, -							  prefix_len); -				break; -			} -		} -		if (rc == -EREMOTE) -			break; -	} - -	if (!rc) { -		if (it) -			rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, -							    it); -		else -			rc = -ENOENT; -	} -	dfs_cache_free_tgts(&tl); -out: -	kfree(tree); -	return rc; -} -#else -static inline int __smb2_reconnect(const struct nls_table *nlsc, -				   struct cifs_tcon *tcon) -{ -	return SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nlsc); -} -#endif -  static int  smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,  	       struct TCP_Server_Info *server) @@ -409,7 +298,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,  	if (tcon->use_persistent)  		tcon->need_reopen_files = true; -	rc = __smb2_reconnect(nls_codepage, tcon); +	rc = cifs_tree_connect(0, tcon, nls_codepage);  	mutex_unlock(&tcon->ses->session_mutex);  	cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); @@ -1387,6 +1276,8 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)  	spnego_key = cifs_get_spnego_key(ses);  	if (IS_ERR(spnego_key)) {  		rc = PTR_ERR(spnego_key); +		if (rc == -ENOKEY) +			cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n");  		spnego_key = NULL;  		goto out;  	} diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 236814d61248..c3f1baf5bde2 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -31,7 +31,7 @@   * Note that, due to trying to use names similar to the protocol specifications,   * there are many mixed case field names in the structures below.  Although   * this does not match typical Linux kernel style, it is necessary to be - * be able to match against the protocol specfication. + * able to match against the protocol specfication.   *   * SMB2 commands   * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 84433d0653f9..ac7632482736 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -936,7 +936,7 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,  	}  	/* BB special case reconnect tid and uid here? */ -	return map_smb_to_linux_error(mid->resp_buf, log_error); +	return map_and_check_smb_error(mid, log_error);  }  struct mid_q_entry * |