Commit c7bb3fbc authored by Linus Torvalds's avatar Linus Torvalds

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
...@@ -32,10 +32,9 @@ int __cifs_calc_signature(struct smb_rqst *rqst, ...@@ -32,10 +32,9 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
int rc; int rc;
struct kvec *iov = rqst->rq_iov; struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec; 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+ */ /* 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) if (iov[0].iov_len <= 4)
return -EIO; return -EIO;
i = 0; i = 0;
......
...@@ -557,6 +557,8 @@ struct smb_version_values { ...@@ -557,6 +557,8 @@ struct smb_version_values {
#define HEADER_SIZE(server) (server->vals->header_size) #define HEADER_SIZE(server) (server->vals->header_size)
#define MAX_HEADER_SIZE(server) (server->vals->max_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 * CIFS superblock mount flags (mnt_cifs_flags) to consider when
...@@ -750,6 +752,11 @@ struct TCP_Server_Info { ...@@ -750,6 +752,11 @@ struct TCP_Server_Info {
#endif #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) static inline void cifs_server_lock(struct TCP_Server_Info *server)
{ {
unsigned int nofs_flag = memalloc_nofs_save(); unsigned int nofs_flag = memalloc_nofs_save();
......
...@@ -871,7 +871,7 @@ smb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) ...@@ -871,7 +871,7 @@ smb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
/* /*
* SMB1 does not use credits. * SMB1 does not use credits.
*/ */
if (server->vals->header_preamble_size) if (is_smb1(server))
return 0; return 0;
return le16_to_cpu(shdr->CreditRequest); return le16_to_cpu(shdr->CreditRequest);
...@@ -1050,7 +1050,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -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 */ /* make sure this will fit in a large buffer */
if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - 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_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
cifs_reconnect(server, true); cifs_reconnect(server, true);
return -ECONNABORTED; return -ECONNABORTED;
...@@ -1065,8 +1065,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1065,8 +1065,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
/* now read the rest */ /* now read the rest */
length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
pdu_length - HEADER_SIZE(server) + 1 pdu_length - MID_HEADER_SIZE(server));
+ server->vals->header_preamble_size);
if (length < 0) if (length < 0)
return length; return length;
...@@ -1122,7 +1121,7 @@ smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) ...@@ -1122,7 +1121,7 @@ smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
/* /*
* SMB1 does not use credits. * SMB1 does not use credits.
*/ */
if (server->vals->header_preamble_size) if (is_smb1(server))
return; return;
if (shdr->CreditRequest) { if (shdr->CreditRequest) {
...@@ -1180,10 +1179,10 @@ cifs_demultiplex_thread(void *p) ...@@ -1180,10 +1179,10 @@ cifs_demultiplex_thread(void *p)
if (length < 0) if (length < 0)
continue; continue;
if (server->vals->header_preamble_size == 0) if (is_smb1(server))
server->total_read = 0;
else
server->total_read = length; server->total_read = length;
else
server->total_read = 0;
/* /*
* The right amount was read from socket - 4 bytes, * The right amount was read from socket - 4 bytes,
...@@ -1198,8 +1197,7 @@ cifs_demultiplex_thread(void *p) ...@@ -1198,8 +1197,7 @@ cifs_demultiplex_thread(void *p)
server->pdu_size = pdu_length; server->pdu_size = pdu_length;
/* make sure we have enough to get to the MID */ /* make sure we have enough to get to the MID */
if (server->pdu_size < HEADER_SIZE(server) - 1 - if (server->pdu_size < MID_HEADER_SIZE(server)) {
server->vals->header_preamble_size) {
cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n", cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n",
server->pdu_size); server->pdu_size);
cifs_reconnect(server, true); cifs_reconnect(server, true);
...@@ -1208,9 +1206,8 @@ cifs_demultiplex_thread(void *p) ...@@ -1208,9 +1206,8 @@ cifs_demultiplex_thread(void *p)
/* read down to the MID */ /* read down to the MID */
length = cifs_read_from_socket(server, length = cifs_read_from_socket(server,
buf + server->vals->header_preamble_size, buf + HEADER_PREAMBLE_SIZE(server),
HEADER_SIZE(server) - 1 MID_HEADER_SIZE(server));
- server->vals->header_preamble_size);
if (length < 0) if (length < 0)
continue; continue;
server->total_read += length; server->total_read += length;
......
...@@ -3307,26 +3307,43 @@ get_smb2_acl(struct cifs_sb_info *cifs_sb, ...@@ -3307,26 +3307,43 @@ get_smb2_acl(struct cifs_sb_info *cifs_sb,
return pntsd; 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, static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
loff_t offset, loff_t len, bool keep_size) loff_t offset, loff_t len, bool keep_size)
{ {
struct cifs_ses *ses = tcon->ses; struct cifs_ses *ses = tcon->ses;
struct inode *inode; struct inode *inode = file_inode(file);
struct cifsInodeInfo *cifsi; struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifsFileInfo *cfile = file->private_data; struct cifsFileInfo *cfile = file->private_data;
struct file_zero_data_information fsctl_buf;
long rc; long rc;
unsigned int xid; unsigned int xid;
__le64 eof; __le64 eof;
xid = get_xid(); xid = get_xid();
inode = d_inode(cfile->dentry);
cifsi = CIFS_I(inode);
trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
ses->Suid, offset, len); 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 * We zero the range through ioctl, so we need remove the page caches
* first, otherwise the data may be inconsistent with the server. * 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, ...@@ -3334,26 +3351,12 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
truncate_pagecache_range(inode, offset, offset + len - 1); truncate_pagecache_range(inode, offset, offset + len - 1);
/* if file not oplocked can't be sure whether asking to extend size */ /* if file not oplocked can't be sure whether asking to extend size */
if (!CIFS_CACHE_READ(cifsi)) rc = -EOPNOTSUPP;
if (keep_size == false) { if (keep_size == false && !CIFS_CACHE_READ(cifsi))
rc = -EOPNOTSUPP; goto zero_range_exit;
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);
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, rc = smb3_zero_data(file, tcon, offset, len, xid);
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, if (rc < 0)
(char *)&fsctl_buf,
sizeof(struct file_zero_data_information),
0, NULL, NULL);
if (rc)
goto zero_range_exit; goto zero_range_exit;
/* /*
...@@ -3366,6 +3369,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, ...@@ -3366,6 +3369,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
} }
zero_range_exit: zero_range_exit:
filemap_invalidate_unlock(inode->i_mapping);
inode_unlock(inode);
free_xid(xid); free_xid(xid);
if (rc) if (rc)
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid, 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, ...@@ -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, static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
loff_t offset, loff_t len) loff_t offset, loff_t len)
{ {
struct inode *inode; struct inode *inode = file_inode(file);
struct cifsFileInfo *cfile = file->private_data; struct cifsFileInfo *cfile = file->private_data;
struct file_zero_data_information fsctl_buf; struct file_zero_data_information fsctl_buf;
long rc; long rc;
...@@ -3388,14 +3393,12 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, ...@@ -3388,14 +3393,12 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
xid = get_xid(); xid = get_xid();
inode = d_inode(cfile->dentry); inode_lock(inode);
/* Need to make file sparse, if not already, before freeing range. */ /* Need to make file sparse, if not already, before freeing range. */
/* Consider adding equivalent for compressed since it could also work */ /* Consider adding equivalent for compressed since it could also work */
if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) { if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) {
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
free_xid(xid); goto out;
return rc;
} }
filemap_invalidate_lock(inode->i_mapping); filemap_invalidate_lock(inode->i_mapping);
...@@ -3415,8 +3418,10 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, ...@@ -3415,8 +3418,10 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
(char *)&fsctl_buf, (char *)&fsctl_buf,
sizeof(struct file_zero_data_information), sizeof(struct file_zero_data_information),
CIFSMaxBufSize, NULL, NULL); CIFSMaxBufSize, NULL, NULL);
free_xid(xid);
filemap_invalidate_unlock(inode->i_mapping); filemap_invalidate_unlock(inode->i_mapping);
out:
inode_unlock(inode);
free_xid(xid);
return rc; return rc;
} }
......
...@@ -2572,19 +2572,15 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, ...@@ -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); path_len = UniStrnlen((wchar_t *)path, PATH_MAX);
/* /* make room for one path separator only if @path isn't empty */
* make room for one path separator between the treename and *out_len = treename_len + (path[0] ? 1 : 0) + path_len;
* path
*/
*out_len = treename_len + 1 + path_len;
/* /*
* final path needs to be null-terminated UTF16 with a * final path needs to be 8-byte aligned as specified in
* size aligned to 8 * MS-SMB2 2.2.13 SMB2 CREATE Request.
*/ */
*out_size = roundup(*out_len * sizeof(__le16), 8);
*out_size = roundup((*out_len+1)*2, 8); *out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL);
*out_path = kzalloc(*out_size, GFP_KERNEL);
if (!*out_path) if (!*out_path)
return -ENOMEM; return -ENOMEM;
......
...@@ -261,8 +261,8 @@ smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst) ...@@ -261,8 +261,8 @@ smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst)
int nvec; int nvec;
unsigned long buflen = 0; unsigned long buflen = 0;
if (server->vals->header_preamble_size == 0 && if (!is_smb1(server) && rqst->rq_nvec >= 2 &&
rqst->rq_nvec >= 2 && rqst->rq_iov[0].iov_len == 4) { rqst->rq_iov[0].iov_len == 4) {
iov = &rqst->rq_iov[1]; iov = &rqst->rq_iov[1];
nvec = rqst->rq_nvec - 1; nvec = rqst->rq_nvec - 1;
} else { } else {
...@@ -346,7 +346,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, ...@@ -346,7 +346,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
sigprocmask(SIG_BLOCK, &mask, &oldmask); sigprocmask(SIG_BLOCK, &mask, &oldmask);
/* Generate a rfc1002 marker for SMB2+ */ /* Generate a rfc1002 marker for SMB2+ */
if (server->vals->header_preamble_size == 0) { if (!is_smb1(server)) {
struct kvec hiov = { struct kvec hiov = {
.iov_base = &rfc1002_marker, .iov_base = &rfc1002_marker,
.iov_len = 4 .iov_len = 4
...@@ -1238,7 +1238,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -1238,7 +1238,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
buf = (char *)midQ[i]->resp_buf; buf = (char *)midQ[i]->resp_buf;
resp_iov[i].iov_base = buf; resp_iov[i].iov_base = buf;
resp_iov[i].iov_len = midQ[i]->resp_buf_size + resp_iov[i].iov_len = midQ[i]->resp_buf_size +
server->vals->header_preamble_size; HEADER_PREAMBLE_SIZE(server);
if (midQ[i]->large_buf) if (midQ[i]->large_buf)
resp_buf_type[i] = CIFS_LARGE_BUFFER; resp_buf_type[i] = CIFS_LARGE_BUFFER;
...@@ -1643,7 +1643,7 @@ int ...@@ -1643,7 +1643,7 @@ int
cifs_discard_remaining_data(struct TCP_Server_Info *server) cifs_discard_remaining_data(struct TCP_Server_Info *server)
{ {
unsigned int rfclen = server->pdu_size; unsigned int rfclen = server->pdu_size;
int remaining = rfclen + server->vals->header_preamble_size - int remaining = rfclen + HEADER_PREAMBLE_SIZE(server) -
server->total_read; server->total_read;
while (remaining > 0) { while (remaining > 0) {
...@@ -1689,8 +1689,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1689,8 +1689,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
unsigned int data_offset, data_len; unsigned int data_offset, data_len;
struct cifs_readdata *rdata = mid->callback_data; struct cifs_readdata *rdata = mid->callback_data;
char *buf = server->smallbuf; char *buf = server->smallbuf;
unsigned int buflen = server->pdu_size + unsigned int buflen = server->pdu_size + HEADER_PREAMBLE_SIZE(server);
server->vals->header_preamble_size;
bool use_rdma_mr = false; bool use_rdma_mr = false;
cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%u\n", cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%u\n",
...@@ -1724,10 +1723,10 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1724,10 +1723,10 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
/* set up first two iov for signature check and to get credits */ /* set up first two iov for signature check and to get credits */
rdata->iov[0].iov_base = buf; rdata->iov[0].iov_base = buf;
rdata->iov[0].iov_len = server->vals->header_preamble_size; rdata->iov[0].iov_len = HEADER_PREAMBLE_SIZE(server);
rdata->iov[1].iov_base = buf + server->vals->header_preamble_size; rdata->iov[1].iov_base = buf + HEADER_PREAMBLE_SIZE(server);
rdata->iov[1].iov_len = rdata->iov[1].iov_len =
server->total_read - server->vals->header_preamble_size; server->total_read - HEADER_PREAMBLE_SIZE(server);
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
rdata->iov[0].iov_base, rdata->iov[0].iov_len); rdata->iov[0].iov_base, rdata->iov[0].iov_len);
cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
...@@ -1752,7 +1751,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1752,7 +1751,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
} }
data_offset = server->ops->read_data_offset(buf) + data_offset = server->ops->read_data_offset(buf) +
server->vals->header_preamble_size; HEADER_PREAMBLE_SIZE(server);
if (data_offset < server->total_read) { if (data_offset < server->total_read) {
/* /*
* win2k8 sometimes sends an offset of 0 when the read * win2k8 sometimes sends an offset of 0 when the read
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment