diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/affs/Changes | 2 | ||||
| -rw-r--r-- | fs/cifs/cifsglob.h | 1 | ||||
| -rw-r--r-- | fs/cifs/ioctl.c | 6 | ||||
| -rw-r--r-- | fs/cifs/smb2ops.c | 99 | ||||
| -rw-r--r-- | fs/cifs/smb2pdu.c | 92 | ||||
| -rw-r--r-- | fs/cifs/smb2pdu.h | 12 | ||||
| -rw-r--r-- | fs/cifs/smb2proto.h | 1 | ||||
| -rw-r--r-- | fs/cifs/smbfsctl.h | 2 | ||||
| -rw-r--r-- | fs/namei.c | 3 | ||||
| -rw-r--r-- | fs/pipe.c | 39 | ||||
| -rw-r--r-- | fs/squashfs/file_direct.c | 5 | ||||
| -rw-r--r-- | fs/sysfs/file.c | 22 | 
12 files changed, 234 insertions, 50 deletions
| diff --git a/fs/affs/Changes b/fs/affs/Changes index a29409c1ffe..b41c2c9792f 100644 --- a/fs/affs/Changes +++ b/fs/affs/Changes @@ -91,7 +91,7 @@ more 2.4 fixes: [Roman Zippel]  Version 3.11  ------------ -- Converted to use 2.3.x page cache [Dave Jones <dave@powertweak.com>] +- Converted to use 2.3.x page cache [Dave Jones]  - Corruption in truncate() bugfix [Ken Tyler <kent@werple.net.au>]  Version 3.10 diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d9ea7ada137..f918a998a08 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -384,6 +384,7 @@ struct smb_version_operations {  	int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file,  			struct cifsFileInfo *target_file, u64 src_off, u64 len,  			u64 dest_off); +	int (*validate_negotiate)(const unsigned int, struct cifs_tcon *);  };  struct smb_version_values { diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 409b45eefe7..77492301cc2 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -26,13 +26,15 @@  #include <linux/mount.h>  #include <linux/mm.h>  #include <linux/pagemap.h> -#include <linux/btrfs.h>  #include "cifspdu.h"  #include "cifsglob.h"  #include "cifsproto.h"  #include "cifs_debug.h"  #include "cifsfs.h" +#define CIFS_IOCTL_MAGIC	0xCF +#define CIFS_IOC_COPYCHUNK_FILE	_IOW(CIFS_IOCTL_MAGIC, 3, int) +  static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,  			unsigned long srcfd, u64 off, u64 len, u64 destoff)  { @@ -213,7 +215,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)  				cifs_dbg(FYI, "set compress flag rc %d\n", rc);  			}  			break; -		case BTRFS_IOC_CLONE: +		case CIFS_IOC_COPYCHUNK_FILE:  			rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0);  			break;  		default: diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 11dde4b24f8..757da3e54d3 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -532,7 +532,10 @@ smb2_clone_range(const unsigned int xid,  	int rc;  	unsigned int ret_data_len;  	struct copychunk_ioctl *pcchunk; -	char *retbuf = NULL; +	struct copychunk_ioctl_rsp *retbuf = NULL; +	struct cifs_tcon *tcon; +	int chunks_copied = 0; +	bool chunk_sizes_updated = false;  	pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); @@ -547,27 +550,96 @@ smb2_clone_range(const unsigned int xid,  	/* Note: request_res_key sets res_key null only if rc !=0 */  	if (rc) -		return rc; +		goto cchunk_out;  	/* For now array only one chunk long, will make more flexible later */  	pcchunk->ChunkCount = __constant_cpu_to_le32(1);  	pcchunk->Reserved = 0; -	pcchunk->SourceOffset = cpu_to_le64(src_off); -	pcchunk->TargetOffset = cpu_to_le64(dest_off); -	pcchunk->Length = cpu_to_le32(len);  	pcchunk->Reserved2 = 0; -	/* Request that server copy to target from src file identified by key */ -	rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink), -			trgtfile->fid.persistent_fid, -			trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, -			true /* is_fsctl */, (char *)pcchunk, -			sizeof(struct copychunk_ioctl),	&retbuf, &ret_data_len); +	tcon = tlink_tcon(trgtfile->tlink); -	/* BB need to special case rc = EINVAL to alter chunk size */ +	while (len > 0) { +		pcchunk->SourceOffset = cpu_to_le64(src_off); +		pcchunk->TargetOffset = cpu_to_le64(dest_off); +		pcchunk->Length = +			cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk)); -	cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len); +		/* Request server copy to target from src identified by key */ +		rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, +			trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, +			true /* is_fsctl */, (char *)pcchunk, +			sizeof(struct copychunk_ioctl),	(char **)&retbuf, +			&ret_data_len); +		if (rc == 0) { +			if (ret_data_len != +					sizeof(struct copychunk_ioctl_rsp)) { +				cifs_dbg(VFS, "invalid cchunk response size\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			if (retbuf->TotalBytesWritten == 0) { +				cifs_dbg(FYI, "no bytes copied\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			/* +			 * Check if server claimed to write more than we asked +			 */ +			if (le32_to_cpu(retbuf->TotalBytesWritten) > +			    le32_to_cpu(pcchunk->Length)) { +				cifs_dbg(VFS, "invalid copy chunk response\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			if (le32_to_cpu(retbuf->ChunksWritten) != 1) { +				cifs_dbg(VFS, "invalid num chunks written\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			chunks_copied++; + +			src_off += le32_to_cpu(retbuf->TotalBytesWritten); +			dest_off += le32_to_cpu(retbuf->TotalBytesWritten); +			len -= le32_to_cpu(retbuf->TotalBytesWritten); + +			cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n", +				le32_to_cpu(retbuf->ChunksWritten), +				le32_to_cpu(retbuf->ChunkBytesWritten), +				le32_to_cpu(retbuf->TotalBytesWritten)); +		} else if (rc == -EINVAL) { +			if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) +				goto cchunk_out; + +			cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", +				le32_to_cpu(retbuf->ChunksWritten), +				le32_to_cpu(retbuf->ChunkBytesWritten), +				le32_to_cpu(retbuf->TotalBytesWritten)); + +			/* +			 * Check if this is the first request using these sizes, +			 * (ie check if copy succeed once with original sizes +			 * and check if the server gave us different sizes after +			 * we already updated max sizes on previous request). +			 * if not then why is the server returning an error now +			 */ +			if ((chunks_copied != 0) || chunk_sizes_updated) +				goto cchunk_out; + +			/* Check that server is not asking us to grow size */ +			if (le32_to_cpu(retbuf->ChunkBytesWritten) < +					tcon->max_bytes_chunk) +				tcon->max_bytes_chunk = +					le32_to_cpu(retbuf->ChunkBytesWritten); +			else +				goto cchunk_out; /* server gave us bogus size */ + +			/* No need to change MaxChunks since already set to 1 */ +			chunk_sizes_updated = true; +		} +	} +cchunk_out:  	kfree(pcchunk);  	return rc;  } @@ -1247,6 +1319,7 @@ struct smb_version_operations smb30_operations = {  	.create_lease_buf = smb3_create_lease_buf,  	.parse_lease_buf = smb3_parse_lease_buf,  	.clone_range = smb2_clone_range, +	.validate_negotiate = smb3_validate_negotiate,  };  struct smb_version_values smb20_values = { diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index d65270c290a..2013234b73a 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -454,6 +454,81 @@ neg_exit:  	return rc;  } +int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) +{ +	int rc = 0; +	struct validate_negotiate_info_req vneg_inbuf; +	struct validate_negotiate_info_rsp *pneg_rsp; +	u32 rsplen; + +	cifs_dbg(FYI, "validate negotiate\n"); + +	/* +	 * validation ioctl must be signed, so no point sending this if we +	 * can not sign it.  We could eventually change this to selectively +	 * sign just this, the first and only signed request on a connection. +	 * This is good enough for now since a user who wants better security +	 * would also enable signing on the mount. Having validation of +	 * negotiate info for signed connections helps reduce attack vectors +	 */ +	if (tcon->ses->server->sign == false) +		return 0; /* validation requires signing */ + +	vneg_inbuf.Capabilities = +			cpu_to_le32(tcon->ses->server->vals->req_capabilities); +	memcpy(vneg_inbuf.Guid, cifs_client_guid, SMB2_CLIENT_GUID_SIZE); + +	if (tcon->ses->sign) +		vneg_inbuf.SecurityMode = +			cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); +	else if (global_secflags & CIFSSEC_MAY_SIGN) +		vneg_inbuf.SecurityMode = +			cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); +	else +		vneg_inbuf.SecurityMode = 0; + +	vneg_inbuf.DialectCount = cpu_to_le16(1); +	vneg_inbuf.Dialects[0] = +		cpu_to_le16(tcon->ses->server->vals->protocol_id); + +	rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, +		FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */, +		(char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req), +		(char **)&pneg_rsp, &rsplen); + +	if (rc != 0) { +		cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); +		return -EIO; +	} + +	if (rsplen != sizeof(struct validate_negotiate_info_rsp)) { +		cifs_dbg(VFS, "invalid size of protocol negotiate response\n"); +		return -EIO; +	} + +	/* check validate negotiate info response matches what we got earlier */ +	if (pneg_rsp->Dialect != +			cpu_to_le16(tcon->ses->server->vals->protocol_id)) +		goto vneg_out; + +	if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode)) +		goto vneg_out; + +	/* do not validate server guid because not saved at negprot time yet */ + +	if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | +	      SMB2_LARGE_FILES) != tcon->ses->server->capabilities) +		goto vneg_out; + +	/* validate negotiate successful */ +	cifs_dbg(FYI, "validate negotiate info successful\n"); +	return 0; + +vneg_out: +	cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n"); +	return -EIO; +} +  int  SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,  		const struct nls_table *nls_cp) @@ -829,6 +904,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,  	    ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))  		cifs_dbg(VFS, "DFS capability contradicts DFS flag\n");  	init_copy_chunk_defaults(tcon); +	if (tcon->ses->server->ops->validate_negotiate) +		rc = tcon->ses->server->ops->validate_negotiate(xid, tcon);  tcon_exit:  	free_rsp_buf(resp_buftype, rsp);  	kfree(unc_path); @@ -1214,10 +1291,17 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,  	rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);  	rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; -	if (rc != 0) { +	if ((rc != 0) && (rc != -EINVAL)) {  		if (tcon)  			cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);  		goto ioctl_exit; +	} else if (rc == -EINVAL) { +		if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && +		    (opcode != FSCTL_SRV_COPYCHUNK)) { +			if (tcon) +				cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); +			goto ioctl_exit; +		}  	}  	/* check if caller wants to look at return data or just return rc */ @@ -2154,11 +2238,9 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,  	rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0);  	rsp = (struct smb2_set_info_rsp *)iov[0].iov_base; -	if (rc != 0) { +	if (rc != 0)  		cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); -		goto out; -	} -out: +  	free_rsp_buf(resp_buftype, rsp);  	kfree(iov);  	return rc; diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index f88320bbb47..2022c542ea3 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -577,13 +577,19 @@ struct copychunk_ioctl_rsp {  	__le32 TotalBytesWritten;  } __packed; -/* Response and Request are the same format */ -struct validate_negotiate_info { +struct validate_negotiate_info_req {  	__le32 Capabilities;  	__u8   Guid[SMB2_CLIENT_GUID_SIZE];  	__le16 SecurityMode;  	__le16 DialectCount; -	__le16 Dialect[1]; +	__le16 Dialects[1]; /* dialect (someday maybe list) client asked for */ +} __packed; + +struct validate_negotiate_info_rsp { +	__le32 Capabilities; +	__u8   Guid[SMB2_CLIENT_GUID_SIZE]; +	__le16 SecurityMode; +	__le16 Dialect; /* Dialect in use for the connection */  } __packed;  #define RSS_CAPABLE	0x00000001 diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index b4eea105b08..93adc64666f 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -162,5 +162,6 @@ extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,  		      struct smb2_lock_element *buf);  extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,  			    __u8 *lease_key, const __le32 lease_state); +extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);  #endif			/* _SMB2PROTO_H */ diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h index a4b2391fe66..0e538b5c962 100644 --- a/fs/cifs/smbfsctl.h +++ b/fs/cifs/smbfsctl.h @@ -90,7 +90,7 @@  #define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */  #define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */  #define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ -#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 /* BB add struct */ +#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204  /* Perform server-side data movement */  #define FSCTL_SRV_COPYCHUNK 0x001440F2  #define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 diff --git a/fs/namei.c b/fs/namei.c index 8f77a8cea28..c53d3a9547f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -513,8 +513,7 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)  	if (!lockref_get_not_dead(&parent->d_lockref)) {  		nd->path.dentry = NULL;	 -		rcu_read_unlock(); -		return -ECHILD; +		goto out;  	}  	/* diff --git a/fs/pipe.c b/fs/pipe.c index d2c45e14e6d..0e0752ef271 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -726,11 +726,25 @@ pipe_poll(struct file *filp, poll_table *wait)  	return mask;  } +static void put_pipe_info(struct inode *inode, struct pipe_inode_info *pipe) +{ +	int kill = 0; + +	spin_lock(&inode->i_lock); +	if (!--pipe->files) { +		inode->i_pipe = NULL; +		kill = 1; +	} +	spin_unlock(&inode->i_lock); + +	if (kill) +		free_pipe_info(pipe); +} +  static int  pipe_release(struct inode *inode, struct file *file)  { -	struct pipe_inode_info *pipe = inode->i_pipe; -	int kill = 0; +	struct pipe_inode_info *pipe = file->private_data;  	__pipe_lock(pipe);  	if (file->f_mode & FMODE_READ) @@ -743,17 +757,9 @@ pipe_release(struct inode *inode, struct file *file)  		kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);  		kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);  	} -	spin_lock(&inode->i_lock); -	if (!--pipe->files) { -		inode->i_pipe = NULL; -		kill = 1; -	} -	spin_unlock(&inode->i_lock);  	__pipe_unlock(pipe); -	if (kill) -		free_pipe_info(pipe); - +	put_pipe_info(inode, pipe);  	return 0;  } @@ -1014,7 +1020,6 @@ static int fifo_open(struct inode *inode, struct file *filp)  {  	struct pipe_inode_info *pipe;  	bool is_pipe = inode->i_sb->s_magic == PIPEFS_MAGIC; -	int kill = 0;  	int ret;  	filp->f_version = 0; @@ -1130,15 +1135,9 @@ err_wr:  	goto err;  err: -	spin_lock(&inode->i_lock); -	if (!--pipe->files) { -		inode->i_pipe = NULL; -		kill = 1; -	} -	spin_unlock(&inode->i_lock);  	__pipe_unlock(pipe); -	if (kill) -		free_pipe_info(pipe); + +	put_pipe_info(inode, pipe);  	return ret;  } diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c index 2943b2bfae4..62a0de6632e 100644 --- a/fs/squashfs/file_direct.c +++ b/fs/squashfs/file_direct.c @@ -84,6 +84,9 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)  		 */  		res = squashfs_read_cache(target_page, block, bsize, pages,  								page); +		if (res < 0) +			goto mark_errored; +  		goto out;  	} @@ -119,7 +122,7 @@ mark_errored:  	 * dealt with by the caller  	 */  	for (i = 0; i < pages; i++) { -		if (page[i] == target_page) +		if (page[i] == NULL || page[i] == target_page)  			continue;  		flush_dcache_page(page[i]);  		SetPageError(page[i]); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 79b5da2acbe..b94f9368509 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -609,7 +609,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file)  	struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;  	struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;  	struct sysfs_open_file *of; -	bool has_read, has_write; +	bool has_read, has_write, has_mmap;  	int error = -EACCES;  	/* need attr_sd for attr and ops, its parent for kobj */ @@ -621,6 +621,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file)  		has_read = battr->read || battr->mmap;  		has_write = battr->write || battr->mmap; +		has_mmap = battr->mmap;  	} else {  		const struct sysfs_ops *ops = sysfs_file_ops(attr_sd); @@ -632,6 +633,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file)  		has_read = ops->show;  		has_write = ops->store; +		has_mmap = false;  	}  	/* check perms and supported operations */ @@ -649,7 +651,23 @@ static int sysfs_open_file(struct inode *inode, struct file *file)  	if (!of)  		goto err_out; -	mutex_init(&of->mutex); +	/* +	 * The following is done to give a different lockdep key to +	 * @of->mutex for files which implement mmap.  This is a rather +	 * crude way to avoid false positive lockdep warning around +	 * mm->mmap_sem - mmap nests @of->mutex under mm->mmap_sem and +	 * reading /sys/block/sda/trace/act_mask grabs sr_mutex, under +	 * which mm->mmap_sem nests, while holding @of->mutex.  As each +	 * open file has a separate mutex, it's okay as long as those don't +	 * happen on the same file.  At this point, we can't easily give +	 * each file a separate locking class.  Let's differentiate on +	 * whether the file has mmap or not for now. +	 */ +	if (has_mmap) +		mutex_init(&of->mutex); +	else +		mutex_init(&of->mutex); +  	of->sd = attr_sd;  	of->file = file; | 
