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

Merge tag '6.0-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull cfis fixes from Steve French:

 - two locking fixes (zero range, punch hole)

 - DFS 9 fix (padding), affecting some servers

 - three minor cleanup changes

* tag '6.0-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: Add helper function to check smb1+ server
  cifs: Use help macro to get the mid header size
  cifs: Use help macro to get the header preamble size
  cifs: skip extra NULL byte in filenames
  smb3: missing inode locks in punch hole
  smb3: missing inode locks in zero range
parents 2f23a7c9 d291e703
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -32,10 +32,9 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
	int rc;
	struct kvec *iov = rqst->rq_iov;
	int n_vec = rqst->rq_nvec;
	int is_smb2 = server->vals->header_preamble_size == 0;

	/* iov[0] is actual data and not the rfc1002 length for SMB2+ */
	if (is_smb2) {
	if (!is_smb1(server)) {
		if (iov[0].iov_len <= 4)
			return -EIO;
		i = 0;
+7 −0
Original line number Diff line number Diff line
@@ -557,6 +557,8 @@ struct smb_version_values {

#define HEADER_SIZE(server) (server->vals->header_size)
#define MAX_HEADER_SIZE(server) (server->vals->max_header_size)
#define HEADER_PREAMBLE_SIZE(server) (server->vals->header_preamble_size)
#define MID_HEADER_SIZE(server) (HEADER_SIZE(server) - 1 - HEADER_PREAMBLE_SIZE(server))

/**
 * CIFS superblock mount flags (mnt_cifs_flags) to consider when
@@ -750,6 +752,11 @@ struct TCP_Server_Info {
#endif
};

static inline bool is_smb1(struct TCP_Server_Info *server)
{
	return HEADER_PREAMBLE_SIZE(server) != 0;
}

static inline void cifs_server_lock(struct TCP_Server_Info *server)
{
	unsigned int nofs_flag = memalloc_nofs_save();
+10 −13
Original line number Diff line number Diff line
@@ -871,7 +871,7 @@ smb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
	/*
	 * SMB1 does not use credits.
	 */
	if (server->vals->header_preamble_size)
	if (is_smb1(server))
		return 0;

	return le16_to_cpu(shdr->CreditRequest);
@@ -1050,7 +1050,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)

	/* make sure this will fit in a large buffer */
	if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
		server->vals->header_preamble_size) {
	    HEADER_PREAMBLE_SIZE(server)) {
		cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
		cifs_reconnect(server, true);
		return -ECONNABORTED;
@@ -1065,8 +1065,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)

	/* now read the rest */
	length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
				       pdu_length - HEADER_SIZE(server) + 1
				       + server->vals->header_preamble_size);
				       pdu_length - MID_HEADER_SIZE(server));

	if (length < 0)
		return length;
@@ -1122,7 +1121,7 @@ smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
	/*
	 * SMB1 does not use credits.
	 */
	if (server->vals->header_preamble_size)
	if (is_smb1(server))
		return;

	if (shdr->CreditRequest) {
@@ -1180,10 +1179,10 @@ cifs_demultiplex_thread(void *p)
		if (length < 0)
			continue;

		if (server->vals->header_preamble_size == 0)
			server->total_read = 0;
		else
		if (is_smb1(server))
			server->total_read = length;
		else
			server->total_read = 0;

		/*
		 * The right amount was read from socket - 4 bytes,
@@ -1198,8 +1197,7 @@ cifs_demultiplex_thread(void *p)
		server->pdu_size = pdu_length;

		/* make sure we have enough to get to the MID */
		if (server->pdu_size < HEADER_SIZE(server) - 1 -
		    server->vals->header_preamble_size) {
		if (server->pdu_size < MID_HEADER_SIZE(server)) {
			cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n",
				 server->pdu_size);
			cifs_reconnect(server, true);
@@ -1208,9 +1206,8 @@ cifs_demultiplex_thread(void *p)

		/* read down to the MID */
		length = cifs_read_from_socket(server,
			     buf + server->vals->header_preamble_size,
			     HEADER_SIZE(server) - 1
			     - server->vals->header_preamble_size);
			     buf + HEADER_PREAMBLE_SIZE(server),
			     MID_HEADER_SIZE(server));
		if (length < 0)
			continue;
		server->total_read += length;
+36 −31
Original line number Diff line number Diff line
@@ -3307,26 +3307,43 @@ get_smb2_acl(struct cifs_sb_info *cifs_sb,
	return pntsd;
}

static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon,
			     loff_t offset, loff_t len, unsigned int xid)
{
	struct cifsFileInfo *cfile = file->private_data;
	struct file_zero_data_information fsctl_buf;

	cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);

	fsctl_buf.FileOffset = cpu_to_le64(offset);
	fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);

	return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
			  cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
			  (char *)&fsctl_buf,
			  sizeof(struct file_zero_data_information),
			  0, NULL, NULL);
}

static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
			    loff_t offset, loff_t len, bool keep_size)
{
	struct cifs_ses *ses = tcon->ses;
	struct inode *inode;
	struct cifsInodeInfo *cifsi;
	struct inode *inode = file_inode(file);
	struct cifsInodeInfo *cifsi = CIFS_I(inode);
	struct cifsFileInfo *cfile = file->private_data;
	struct file_zero_data_information fsctl_buf;
	long rc;
	unsigned int xid;
	__le64 eof;

	xid = get_xid();

	inode = d_inode(cfile->dentry);
	cifsi = CIFS_I(inode);

	trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
			      ses->Suid, offset, len);

	inode_lock(inode);
	filemap_invalidate_lock(inode->i_mapping);

	/*
	 * We zero the range through ioctl, so we need remove the page caches
	 * first, otherwise the data may be inconsistent with the server.
@@ -3334,26 +3351,12 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
	truncate_pagecache_range(inode, offset, offset + len - 1);

	/* if file not oplocked can't be sure whether asking to extend size */
	if (!CIFS_CACHE_READ(cifsi))
		if (keep_size == false) {
	rc = -EOPNOTSUPP;
			trace_smb3_zero_err(xid, cfile->fid.persistent_fid,
				tcon->tid, ses->Suid, offset, len, rc);
			free_xid(xid);
			return rc;
		}

	cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);

	fsctl_buf.FileOffset = cpu_to_le64(offset);
	fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
	if (keep_size == false && !CIFS_CACHE_READ(cifsi))
		goto zero_range_exit;

	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
			cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
			(char *)&fsctl_buf,
			sizeof(struct file_zero_data_information),
			0, NULL, NULL);
	if (rc)
	rc = smb3_zero_data(file, tcon, offset, len, xid);
	if (rc < 0)
		goto zero_range_exit;

	/*
@@ -3366,6 +3369,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
	}

 zero_range_exit:
	filemap_invalidate_unlock(inode->i_mapping);
	inode_unlock(inode);
	free_xid(xid);
	if (rc)
		trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
@@ -3379,7 +3384,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
			    loff_t offset, loff_t len)
{
	struct inode *inode;
	struct inode *inode = file_inode(file);
	struct cifsFileInfo *cfile = file->private_data;
	struct file_zero_data_information fsctl_buf;
	long rc;
@@ -3388,14 +3393,12 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,

	xid = get_xid();

	inode = d_inode(cfile->dentry);

	inode_lock(inode);
	/* Need to make file sparse, if not already, before freeing range. */
	/* Consider adding equivalent for compressed since it could also work */
	if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) {
		rc = -EOPNOTSUPP;
		free_xid(xid);
		return rc;
		goto out;
	}

	filemap_invalidate_lock(inode->i_mapping);
@@ -3415,8 +3418,10 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
			(char *)&fsctl_buf,
			sizeof(struct file_zero_data_information),
			CIFSMaxBufSize, NULL, NULL);
	free_xid(xid);
	filemap_invalidate_unlock(inode->i_mapping);
out:
	inode_unlock(inode);
	free_xid(xid);
	return rc;
}

+6 −10
Original line number Diff line number Diff line
@@ -2572,19 +2572,15 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,

	path_len = UniStrnlen((wchar_t *)path, PATH_MAX);

	/*
	 * make room for one path separator between the treename and
	 * path
	 */
	*out_len = treename_len + 1 + path_len;
	/* make room for one path separator only if @path isn't empty */
	*out_len = treename_len + (path[0] ? 1 : 0) + path_len;

	/*
	 * final path needs to be null-terminated UTF16 with a
	 * size aligned to 8
	 * final path needs to be 8-byte aligned as specified in
	 * MS-SMB2 2.2.13 SMB2 CREATE Request.
	 */

	*out_size = roundup((*out_len+1)*2, 8);
	*out_path = kzalloc(*out_size, GFP_KERNEL);
	*out_size = roundup(*out_len * sizeof(__le16), 8);
	*out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL);
	if (!*out_path)
		return -ENOMEM;

Loading