Commit ec637e3f authored by Steve French's avatar Steve French

[CIFS] Avoid extra large buffer allocation (and memcpy) in cifs_readpages

Signed-off-by: default avatarSteve French <sfrench@us.ibm.com>
parent c89a86bb
...@@ -401,8 +401,8 @@ static read_proc_t ntlmv2_enabled_read; ...@@ -401,8 +401,8 @@ static read_proc_t ntlmv2_enabled_read;
static write_proc_t ntlmv2_enabled_write; static write_proc_t ntlmv2_enabled_write;
static read_proc_t packet_signing_enabled_read; static read_proc_t packet_signing_enabled_read;
static write_proc_t packet_signing_enabled_write; static write_proc_t packet_signing_enabled_write;
static read_proc_t quotaEnabled_read; static read_proc_t experimEnabled_read;
static write_proc_t quotaEnabled_write; static write_proc_t experimEnabled_write;
static read_proc_t linuxExtensionsEnabled_read; static read_proc_t linuxExtensionsEnabled_read;
static write_proc_t linuxExtensionsEnabled_write; static write_proc_t linuxExtensionsEnabled_write;
...@@ -442,9 +442,9 @@ cifs_proc_init(void) ...@@ -442,9 +442,9 @@ cifs_proc_init(void)
pde->write_proc = oplockEnabled_write; pde->write_proc = oplockEnabled_write;
pde = create_proc_read_entry("Experimental", 0, proc_fs_cifs, pde = create_proc_read_entry("Experimental", 0, proc_fs_cifs,
quotaEnabled_read, NULL); experimEnabled_read, NULL);
if (pde) if (pde)
pde->write_proc = quotaEnabled_write; pde->write_proc = experimEnabled_write;
pde = create_proc_read_entry("LinuxExtensionsEnabled", 0, proc_fs_cifs, pde = create_proc_read_entry("LinuxExtensionsEnabled", 0, proc_fs_cifs,
linuxExtensionsEnabled_read, NULL); linuxExtensionsEnabled_read, NULL);
...@@ -586,14 +586,13 @@ oplockEnabled_write(struct file *file, const char __user *buffer, ...@@ -586,14 +586,13 @@ oplockEnabled_write(struct file *file, const char __user *buffer,
} }
static int static int
quotaEnabled_read(char *page, char **start, off_t off, experimEnabled_read(char *page, char **start, off_t off,
int count, int *eof, void *data) int count, int *eof, void *data)
{ {
int len; int len;
len = sprintf(page, "%d\n", experimEnabled); len = sprintf(page, "%d\n", experimEnabled);
/* could also check if quotas are enabled in kernel
as a whole first */
len -= off; len -= off;
*start = page + off; *start = page + off;
...@@ -608,21 +607,23 @@ quotaEnabled_read(char *page, char **start, off_t off, ...@@ -608,21 +607,23 @@ quotaEnabled_read(char *page, char **start, off_t off,
return len; return len;
} }
static int static int
quotaEnabled_write(struct file *file, const char __user *buffer, experimEnabled_write(struct file *file, const char __user *buffer,
unsigned long count, void *data) unsigned long count, void *data)
{ {
char c; char c;
int rc; int rc;
rc = get_user(c, buffer); rc = get_user(c, buffer);
if (rc) if (rc)
return rc; return rc;
if (c == '0' || c == 'n' || c == 'N') if (c == '0' || c == 'n' || c == 'N')
experimEnabled = 0; experimEnabled = 0;
else if (c == '1' || c == 'y' || c == 'Y') else if (c == '1' || c == 'y' || c == 'Y')
experimEnabled = 1; experimEnabled = 1;
else if (c == '2')
experimEnabled = 2;
return count; return count;
} }
static int static int
...@@ -632,8 +633,6 @@ linuxExtensionsEnabled_read(char *page, char **start, off_t off, ...@@ -632,8 +633,6 @@ linuxExtensionsEnabled_read(char *page, char **start, off_t off,
int len; int len;
len = sprintf(page, "%d\n", linuxExtEnabled); len = sprintf(page, "%d\n", linuxExtEnabled);
/* could also check if quotas are enabled in kernel
as a whole first */
len -= off; len -= off;
*start = page + off; *start = page + off;
......
...@@ -733,7 +733,7 @@ cifs_init_request_bufs(void) ...@@ -733,7 +733,7 @@ cifs_init_request_bufs(void)
kmem_cache_destroy(cifs_req_cachep); kmem_cache_destroy(cifs_req_cachep);
return -ENOMEM; return -ENOMEM;
} }
/* 256 (MAX_CIFS_HDR_SIZE bytes is enough for most SMB responses and /* MAX_CIFS_SMALL_BUFFER_SIZE bytes is enough for most SMB responses and
almost all handle based requests (but not write response, nor is it almost all handle based requests (but not write response, nor is it
sufficient for path based requests). A smaller size would have sufficient for path based requests). A smaller size would have
been more efficient (compacting multiple slab items on one 4k page) been more efficient (compacting multiple slab items on one 4k page)
...@@ -742,7 +742,8 @@ cifs_init_request_bufs(void) ...@@ -742,7 +742,8 @@ cifs_init_request_bufs(void)
efficient to alloc 1 per page off the slab compared to 17K (5page) efficient to alloc 1 per page off the slab compared to 17K (5page)
alloc of large cifs buffers even when page debugging is on */ alloc of large cifs buffers even when page debugging is on */
cifs_sm_req_cachep = kmem_cache_create("cifs_small_rq", cifs_sm_req_cachep = kmem_cache_create("cifs_small_rq",
MAX_CIFS_HDR_SIZE, 0, SLAB_HWCACHE_ALIGN, NULL, NULL); MAX_CIFS_SMALL_BUFFER_SIZE, 0, SLAB_HWCACHE_ALIGN,
NULL, NULL);
if (cifs_sm_req_cachep == NULL) { if (cifs_sm_req_cachep == NULL) {
mempool_destroy(cifs_req_poolp); mempool_destroy(cifs_req_poolp);
kmem_cache_destroy(cifs_req_cachep); kmem_cache_destroy(cifs_req_cachep);
......
...@@ -285,6 +285,7 @@ struct cifs_search_info { ...@@ -285,6 +285,7 @@ struct cifs_search_info {
unsigned endOfSearch:1; unsigned endOfSearch:1;
unsigned emptyDir:1; unsigned emptyDir:1;
unsigned unicode:1; unsigned unicode:1;
unsigned smallBuf:1; /* so we know which buf_release function to call */
}; };
struct cifsFileInfo { struct cifsFileInfo {
...@@ -420,7 +421,12 @@ struct dir_notify_req { ...@@ -420,7 +421,12 @@ struct dir_notify_req {
#define MID_RESPONSE_RECEIVED 4 #define MID_RESPONSE_RECEIVED 4
#define MID_RETRY_NEEDED 8 /* session closed while this request out */ #define MID_RETRY_NEEDED 8 /* session closed while this request out */
#define MID_NO_RESP_NEEDED 0x10 #define MID_NO_RESP_NEEDED 0x10
#define MID_SMALL_BUFFER 0x20 /* 112 byte response buffer instead of 4K */
/* Types of response buffer returned from SendReceive2 */
#define CIFS_NO_BUFFER 0 /* Response buffer not returned */
#define CIFS_SMALL_BUFFER 1
#define CIFS_LARGE_BUFFER 2
#define CIFS_IOVEC 4 /* array of response buffers */
/* /*
***************************************************************** *****************************************************************
......
...@@ -80,7 +80,11 @@ ...@@ -80,7 +80,11 @@
#define NT_TRANSACT_GET_USER_QUOTA 0x07 #define NT_TRANSACT_GET_USER_QUOTA 0x07
#define NT_TRANSACT_SET_USER_QUOTA 0x08 #define NT_TRANSACT_SET_USER_QUOTA 0x08
#define MAX_CIFS_HDR_SIZE 256 /* is future chained NTCreateXReadX bigger? */ #define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */
/* among the requests (NTCreateX response is bigger with wct of 34) */
#define MAX_CIFS_HDR_SIZE 0x58 /* 4 len + 32 hdr + (2*24 wct) + 2 bct + 2 pad */
#define CIFS_SMALL_PATH 120 /* allows for (448-88)/3 */
/* internal cifs vfs structures */ /* internal cifs vfs structures */
/***************************************************************** /*****************************************************************
......
...@@ -49,7 +49,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, ...@@ -49,7 +49,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
int * /* bytes returned */ , const int long_op); int * /* bytes returned */ , const int long_op);
extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *, extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *,
struct kvec *, int /* nvec to send */, struct kvec *, int /* nvec to send */,
int * /* bytes returned */ , const int long_op); int * /* type of buf returned */ , const int long_op);
extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid); extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid);
extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length);
extern int is_valid_oplock_break(struct smb_hdr *smb); extern int is_valid_oplock_break(struct smb_hdr *smb);
...@@ -93,11 +93,12 @@ extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, ...@@ -93,11 +93,12 @@ extern int CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
const struct nls_table *); const struct nls_table *);
extern int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon, extern int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
const char *searchName, const struct nls_table *nls_codepage, const char *searchName, const struct nls_table *nls_codepage,
__u16 *searchHandle, struct cifs_search_info * psrch_inf, int map, const char dirsep); __u16 *searchHandle, struct cifs_search_info * psrch_inf,
int map, const char dirsep);
extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
__u16 searchHandle, struct cifs_search_info * psrch_inf); __u16 searchHandle, struct cifs_search_info * psrch_inf);
extern int CIFSFindClose(const int, struct cifsTconInfo *tcon, extern int CIFSFindClose(const int, struct cifsTconInfo *tcon,
const __u16 search_handle); const __u16 search_handle);
...@@ -230,8 +231,9 @@ extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, ...@@ -230,8 +231,9 @@ extern int CIFSSMBClose(const int xid, struct cifsTconInfo *tcon,
const int smb_file_id); const int smb_file_id);
extern int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, extern int CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
const int netfid, unsigned int count, const int netfid, unsigned int count,
const __u64 lseek, unsigned int *nbytes, char **buf); const __u64 lseek, unsigned int *nbytes, char **buf,
int * return_buf_type);
extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, extern int CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
const int netfid, const unsigned int count, const int netfid, const unsigned int count,
const __u64 lseek, unsigned int *nbytes, const __u64 lseek, unsigned int *nbytes,
......
...@@ -958,21 +958,19 @@ CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon, ...@@ -958,21 +958,19 @@ CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
return rc; return rc;
} }
/* If no buffer passed in, then caller wants to do the copy
as in the case of readpages so the SMB buffer must be
freed by the caller */
int int
CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
const int netfid, const unsigned int count, const int netfid, const unsigned int count,
const __u64 lseek, unsigned int *nbytes, char **buf) const __u64 lseek, unsigned int *nbytes, char **buf,
int * pbuf_type)
{ {
int rc = -EACCES; int rc = -EACCES;
READ_REQ *pSMB = NULL; READ_REQ *pSMB = NULL;
READ_RSP *pSMBr = NULL; READ_RSP *pSMBr = NULL;
char *pReadData = NULL; char *pReadData = NULL;
int bytes_returned;
int wct; int wct;
int resp_buf_type = 0;
struct kvec iov[1];
cFYI(1,("Reading %d bytes on fid %d",count,netfid)); cFYI(1,("Reading %d bytes on fid %d",count,netfid));
if(tcon->ses->capabilities & CAP_LARGE_FILES) if(tcon->ses->capabilities & CAP_LARGE_FILES)
...@@ -981,8 +979,7 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, ...@@ -981,8 +979,7 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
wct = 10; /* old style read */ wct = 10; /* old style read */
*nbytes = 0; *nbytes = 0;
rc = smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB, rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
(void **) &pSMBr);
if (rc) if (rc)
return rc; return rc;
...@@ -990,13 +987,13 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, ...@@ -990,13 +987,13 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
if (tcon->ses->server == NULL) if (tcon->ses->server == NULL)
return -ECONNABORTED; return -ECONNABORTED;
pSMB->AndXCommand = 0xFF; /* none */ pSMB->AndXCommand = 0xFF; /* none */
pSMB->Fid = netfid; pSMB->Fid = netfid;
pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF); pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
if(wct == 12) if(wct == 12)
pSMB->OffsetHigh = cpu_to_le32(lseek >> 32); pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
else if((lseek >> 32) > 0) /* can not handle this big offset for old */ else if((lseek >> 32) > 0) /* can not handle this big offset for old */
return -EIO; return -EIO;
pSMB->Remaining = 0; pSMB->Remaining = 0;
pSMB->MaxCount = cpu_to_le16(count & 0xFFFF); pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
...@@ -1005,14 +1002,18 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, ...@@ -1005,14 +1002,18 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
pSMB->ByteCount = 0; /* no need to do le conversion since 0 */ pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
else { else {
/* old style read */ /* old style read */
struct smb_com_readx_req * pSMBW = struct smb_com_readx_req * pSMBW =
(struct smb_com_readx_req *)pSMB; (struct smb_com_readx_req *)pSMB;
pSMBW->ByteCount = 0; pSMBW->ByteCount = 0;
} }
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, iov[0].iov_base = (char *)pSMB;
(struct smb_hdr *) pSMBr, &bytes_returned, 0); iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
rc = SendReceive2(xid, tcon->ses, iov,
1 /* num iovecs */,
&resp_buf_type, 0);
cifs_stats_inc(&tcon->num_reads); cifs_stats_inc(&tcon->num_reads);
pSMBr = (READ_RSP *)iov[0].iov_base;
if (rc) { if (rc) {
cERROR(1, ("Send error in read = %d", rc)); cERROR(1, ("Send error in read = %d", rc));
} else { } else {
...@@ -1022,33 +1023,43 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, ...@@ -1022,33 +1023,43 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon,
*nbytes = data_length; *nbytes = data_length;
/*check that DataLength would not go beyond end of SMB */ /*check that DataLength would not go beyond end of SMB */
if ((data_length > CIFSMaxBufSize) if ((data_length > CIFSMaxBufSize)
|| (data_length > count)) { || (data_length > count)) {
cFYI(1,("bad length %d for count %d",data_length,count)); cFYI(1,("bad length %d for count %d",data_length,count));
rc = -EIO; rc = -EIO;
*nbytes = 0; *nbytes = 0;
} else { } else {
pReadData = pReadData = (char *) (&pSMBr->hdr.Protocol) +
(char *) (&pSMBr->hdr.Protocol) +
le16_to_cpu(pSMBr->DataOffset); le16_to_cpu(pSMBr->DataOffset);
/* if(rc = copy_to_user(buf, pReadData, data_length)) { /* if(rc = copy_to_user(buf, pReadData, data_length)) {
cERROR(1,("Faulting on read rc = %d",rc)); cERROR(1,("Faulting on read rc = %d",rc));
rc = -EFAULT; rc = -EFAULT;
}*/ /* can not use copy_to_user when using page cache*/ }*/ /* can not use copy_to_user when using page cache*/
if(*buf) if(*buf)
memcpy(*buf,pReadData,data_length); memcpy(*buf,pReadData,data_length);
} }
} }
if(*buf)
cifs_buf_release(pSMB);
else
*buf = (char *)pSMB;
/* Note: On -EAGAIN error only caller can retry on handle based calls cifs_small_buf_release(pSMB);
if(*buf) {
if(resp_buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(iov[0].iov_base);
else if(resp_buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(iov[0].iov_base);
} else /* return buffer to caller to free */ /* BB FIXME how do we tell caller if it is not a large buffer */ {
*buf = iov[0].iov_base;
if(resp_buf_type == CIFS_SMALL_BUFFER)
*pbuf_type = CIFS_SMALL_BUFFER;
else if(resp_buf_type == CIFS_LARGE_BUFFER)
*pbuf_type = CIFS_LARGE_BUFFER;
}
/* Note: On -EAGAIN error only caller can retry on handle based calls
since file handle passed in no longer valid */ since file handle passed in no longer valid */
return rc; return rc;
} }
int int
CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon, CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
const int netfid, const unsigned int count, const int netfid, const unsigned int count,
...@@ -1163,10 +1174,10 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, ...@@ -1163,10 +1174,10 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
{ {
int rc = -EACCES; int rc = -EACCES;
WRITE_REQ *pSMB = NULL; WRITE_REQ *pSMB = NULL;
int bytes_returned, wct; int wct;
int smb_hdr_len; int smb_hdr_len;
int resp_buf_type = 0;
/* BB removeme BB */
cFYI(1,("write2 at %lld %d bytes", (long long)offset, count)); cFYI(1,("write2 at %lld %d bytes", (long long)offset, count));
if(tcon->ses->capabilities & CAP_LARGE_FILES) if(tcon->ses->capabilities & CAP_LARGE_FILES)
...@@ -1209,22 +1220,34 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon, ...@@ -1209,22 +1220,34 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
pSMBW->ByteCount = cpu_to_le16(count + 5); pSMBW->ByteCount = cpu_to_le16(count + 5);
} }
iov[0].iov_base = pSMB; iov[0].iov_base = pSMB;
iov[0].iov_len = smb_hdr_len + 4; if(wct == 14)
iov[0].iov_len = smb_hdr_len + 4;
else /* wct == 12 pad bigger by four bytes */
iov[0].iov_len = smb_hdr_len + 8;
rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &bytes_returned, rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
long_op); long_op);
cifs_stats_inc(&tcon->num_writes); cifs_stats_inc(&tcon->num_writes);
if (rc) { if (rc) {
cFYI(1, ("Send error Write2 = %d", rc)); cFYI(1, ("Send error Write2 = %d", rc));
*nbytes = 0; *nbytes = 0;
} else if(resp_buf_type == 0) {
/* presumably this can not happen, but best to be safe */
rc = -EIO;
*nbytes = 0;
} else { } else {
WRITE_RSP * pSMBr = (WRITE_RSP *)pSMB; WRITE_RSP * pSMBr = (WRITE_RSP *)iov[0].iov_base;
*nbytes = le16_to_cpu(pSMBr->CountHigh); *nbytes = le16_to_cpu(pSMBr->CountHigh);
*nbytes = (*nbytes) << 16; *nbytes = (*nbytes) << 16;
*nbytes += le16_to_cpu(pSMBr->Count); *nbytes += le16_to_cpu(pSMBr->Count);
} }
cifs_small_buf_release(pSMB); cifs_small_buf_release(pSMB);
if(resp_buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(iov[0].iov_base);
else if(resp_buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(iov[0].iov_base);
/* Note: On -EAGAIN error only caller can retry on handle based calls /* Note: On -EAGAIN error only caller can retry on handle based calls
since file handle passed in no longer valid */ since file handle passed in no longer valid */
......
...@@ -514,7 +514,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -514,7 +514,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
/* else length ok */ /* else length ok */
reconnect = 0; reconnect = 0;
if(pdu_length > MAX_CIFS_HDR_SIZE - 4) { if(pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
isLargeBuf = TRUE; isLargeBuf = TRUE;
memcpy(bigbuf, smallbuf, 4); memcpy(bigbuf, smallbuf, 4);
smb_buffer = bigbuf; smb_buffer = bigbuf;
......
...@@ -555,13 +555,13 @@ int cifs_closedir(struct inode *inode, struct file *file) ...@@ -555,13 +555,13 @@ int cifs_closedir(struct inode *inode, struct file *file)
} }
ptmp = pCFileStruct->srch_inf.ntwrk_buf_start; ptmp = pCFileStruct->srch_inf.ntwrk_buf_start;
if (ptmp) { if (ptmp) {
/* BB removeme BB */ cFYI(1, ("freeing smb buf in srch struct in closedir")); cFYI(1, ("closedir free smb buf in srch struct"));
pCFileStruct->srch_inf.ntwrk_buf_start = NULL; pCFileStruct->srch_inf.ntwrk_buf_start = NULL;
cifs_buf_release(ptmp); cifs_buf_release(ptmp);
} }
ptmp = pCFileStruct->search_resume_name; ptmp = pCFileStruct->search_resume_name;
if (ptmp) { if (ptmp) {
/* BB removeme BB */ cFYI(1, ("freeing resume name in closedir")); cFYI(1, ("closedir free resume name"));
pCFileStruct->search_resume_name = NULL; pCFileStruct->search_resume_name = NULL;
kfree(ptmp); kfree(ptmp);
} }
...@@ -871,8 +871,8 @@ static ssize_t cifs_write(struct file *file, const char *write_data, ...@@ -871,8 +871,8 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
break; break;
} }
/* BB FIXME We can not sign across two buffers yet */ /* BB FIXME We can not sign across two buffers yet */
if((experimEnabled) && ((pTcon->ses->server->secMode & if((pTcon->ses->server->secMode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0)) { (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0) {
struct kvec iov[2]; struct kvec iov[2];
unsigned int len; unsigned int len;
...@@ -1424,6 +1424,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, ...@@ -1424,6 +1424,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
rc = -EAGAIN; rc = -EAGAIN;
smb_read_data = NULL; smb_read_data = NULL;
while (rc == -EAGAIN) { while (rc == -EAGAIN) {
int buf_type = CIFS_NO_BUFFER;
if ((open_file->invalidHandle) && if ((open_file->invalidHandle) &&
(!open_file->closePend)) { (!open_file->closePend)) {
rc = cifs_reopen_file(file->f_dentry->d_inode, rc = cifs_reopen_file(file->f_dentry->d_inode,
...@@ -1432,20 +1433,22 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, ...@@ -1432,20 +1433,22 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
break; break;
} }
rc = CIFSSMBRead(xid, pTcon, rc = CIFSSMBRead(xid, pTcon,
open_file->netfid, open_file->netfid,
current_read_size, *poffset, current_read_size, *poffset,
&bytes_read, &smb_read_data); &bytes_read, &smb_read_data,
&buf_type);
pSMBr = (struct smb_com_read_rsp *)smb_read_data; pSMBr = (struct smb_com_read_rsp *)smb_read_data;
if (copy_to_user(current_offset, if (copy_to_user(current_offset,
smb_read_data + 4 /* RFC1001 hdr */ smb_read_data + 4 /* RFC1001 hdr */
+ le16_to_cpu(pSMBr->DataOffset), + le16_to_cpu(pSMBr->DataOffset),
bytes_read)) { bytes_read)) {
rc = -EFAULT; rc = -EFAULT;
FreeXid(xid); }
return rc;
}
if (smb_read_data) { if (smb_read_data) {
cifs_buf_release(smb_read_data); if(buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(smb_read_data);
else if(buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(smb_read_data);
smb_read_data = NULL; smb_read_data = NULL;
} }
} }
...@@ -1478,6 +1481,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, ...@@ -1478,6 +1481,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
int xid; int xid;
char *current_offset; char *current_offset;
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
int buf_type = CIFS_NO_BUFFER;
xid = GetXid(); xid = GetXid();
cifs_sb = CIFS_SB(file->f_dentry->d_sb); cifs_sb = CIFS_SB(file->f_dentry->d_sb);
...@@ -1514,9 +1518,10 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, ...@@ -1514,9 +1518,10 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
break; break;
} }
rc = CIFSSMBRead(xid, pTcon, rc = CIFSSMBRead(xid, pTcon,
open_file->netfid, open_file->netfid,
current_read_size, *poffset, current_read_size, *poffset,
&bytes_read, &current_offset); &bytes_read, &current_offset,
&buf_type);
} }
if (rc || (bytes_read == 0)) { if (rc || (bytes_read == 0)) {
if (total_read) { if (total_read) {
...@@ -1614,6 +1619,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, ...@@ -1614,6 +1619,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
struct smb_com_read_rsp *pSMBr; struct smb_com_read_rsp *pSMBr;
struct pagevec lru_pvec; struct pagevec lru_pvec;
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
int buf_type = CIFS_NO_BUFFER;
xid = GetXid(); xid = GetXid();
if (file->private_data == NULL) { if (file->private_data == NULL) {
...@@ -1670,14 +1676,17 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, ...@@ -1670,14 +1676,17 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
} }
rc = CIFSSMBRead(xid, pTcon, rc = CIFSSMBRead(xid, pTcon,
open_file->netfid, open_file->netfid,
read_size, offset, read_size, offset,
&bytes_read, &smb_read_data); &bytes_read, &smb_read_data,
&buf_type);
/* BB more RC checks ? */ /* BB more RC checks ? */
if (rc== -EAGAIN) { if (rc== -EAGAIN) {
if (smb_read_data) { if (smb_read_data) {
cifs_buf_release(smb_read_data); if(buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(smb_read_data);
else if(buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(smb_read_data);
smb_read_data = NULL; smb_read_data = NULL;
} }
} }
...@@ -1734,7 +1743,10 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, ...@@ -1734,7 +1743,10 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
break; break;
} }
if (smb_read_data) { if (smb_read_data) {
cifs_buf_release(smb_read_data); if(buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(smb_read_data);
else if(buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(smb_read_data);
smb_read_data = NULL; smb_read_data = NULL;
} }
bytes_read = 0; bytes_read = 0;
......
...@@ -229,11 +229,12 @@ static int decode_sfu_inode(struct inode * inode, __u64 size, ...@@ -229,11 +229,12 @@ static int decode_sfu_inode(struct inode * inode, __u64 size,
cifs_sb->mnt_cifs_flags & cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc==0) { if (rc==0) {
int buf_type = CIFS_NO_BUFFER;
/* Read header */ /* Read header */
rc = CIFSSMBRead(xid, pTcon, rc = CIFSSMBRead(xid, pTcon,
netfid, netfid,
24 /* length */, 0 /* offset */, 24 /* length */, 0 /* offset */,
&bytes_read, &pbuf); &bytes_read, &pbuf, &buf_type);
if((rc == 0) && (bytes_read >= 8)) { if((rc == 0) && (bytes_read >= 8)) {
if(memcmp("IntxBLK", pbuf, 8) == 0) { if(memcmp("IntxBLK", pbuf, 8) == 0) {
cFYI(1,("Block device")); cFYI(1,("Block device"));
...@@ -267,7 +268,7 @@ static int decode_sfu_inode(struct inode * inode, __u64 size, ...@@ -267,7 +268,7 @@ static int decode_sfu_inode(struct inode * inode, __u64 size,
} else { } else {
inode->i_mode |= S_IFREG; /* then it is a file */ inode->i_mode |= S_IFREG; /* then it is a file */
rc = -EOPNOTSUPP; /* or some unknown SFU type */ rc = -EOPNOTSUPP; /* or some unknown SFU type */
} }
CIFSSMBClose(xid, pTcon, netfid); CIFSSMBClose(xid, pTcon, netfid);
} }
return rc; return rc;
......
...@@ -299,7 +299,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , ...@@ -299,7 +299,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
struct cifsSesInfo * ses; struct cifsSesInfo * ses;
char *temp = (char *) buffer; char *temp = (char *) buffer;
memset(temp,0,MAX_CIFS_HDR_SIZE); memset(temp,0,256); /* bigger than MAX_CIFS_HDR_SIZE */
buffer->smb_buf_length = buffer->smb_buf_length =
(2 * word_count) + sizeof (struct smb_hdr) - (2 * word_count) + sizeof (struct smb_hdr) -
......
...@@ -298,7 +298,7 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, ...@@ -298,7 +298,7 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
int int
SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
struct kvec *iov, int n_vec, int *pbytes_returned, struct kvec *iov, int n_vec, int * pRespBufType /* ret */,
const int long_op) const int long_op)
{ {
int rc = 0; int rc = 0;
...@@ -306,6 +306,8 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, ...@@ -306,6 +306,8 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
unsigned long timeout; unsigned long timeout;
struct mid_q_entry *midQ; struct mid_q_entry *midQ;
struct smb_hdr *in_buf = iov[0].iov_base; struct smb_hdr *in_buf = iov[0].iov_base;
*pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */
if (ses == NULL) { if (ses == NULL) {
cERROR(1,("Null smb session")); cERROR(1,("Null smb session"));
...@@ -491,23 +493,20 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, ...@@ -491,23 +493,20 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
if (midQ->resp_buf && if (midQ->resp_buf &&
(midQ->midState == MID_RESPONSE_RECEIVED)) { (midQ->midState == MID_RESPONSE_RECEIVED)) {
in_buf->smb_buf_length = receive_len; iov[0].iov_base = (char *)midQ->resp_buf;
if(receive_len > 500) { if(midQ->largeBuf)
/* use multiple buffers on way out */ *pRespBufType = CIFS_LARGE_BUFFER;
} else { else
memcpy((char *)in_buf + 4, *pRespBufType = CIFS_SMALL_BUFFER;
(char *)midQ->resp_buf + 4, iov[0].iov_len = receive_len + 4;
receive_len); iov[1].iov_len = 0;
iov[0].iov_len = receive_len + 4;
iov[1].iov_len = 0;
}
dump_smb(in_buf, 80); dump_smb(midQ->resp_buf, 80);
/* convert the length into a more usable form */ /* convert the length into a more usable form */
if((receive_len > 24) && if((receive_len > 24) &&
(ses->server->secMode & (SECMODE_SIGN_REQUIRED | (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
SECMODE_SIGN_ENABLED))) { SECMODE_SIGN_ENABLED))) {
rc = cifs_verify_signature(in_buf, rc = cifs_verify_signature(midQ->resp_buf,
ses->server->mac_signing_key, ses->server->mac_signing_key,
midQ->sequence_number+1); midQ->sequence_number+1);
if(rc) { if(rc) {
...@@ -516,18 +515,19 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, ...@@ -516,18 +515,19 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
} }
} }
*pbytes_returned = in_buf->smb_buf_length;
/* BB special case reconnect tid and uid here? */ /* BB special case reconnect tid and uid here? */
/* BB special case Errbadpassword and pwdexpired here */ /* BB special case Errbadpassword and pwdexpired here */
rc = map_smb_to_linux_error(in_buf); rc = map_smb_to_linux_error(midQ->resp_buf);
/* convert ByteCount if necessary */ /* convert ByteCount if necessary */
if (receive_len >= if (receive_len >=
sizeof (struct smb_hdr) - sizeof (struct smb_hdr) -
4 /* do not count RFC1001 header */ + 4 /* do not count RFC1001 header */ +
(2 * in_buf->WordCount) + 2 /* bcc */ ) (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
BCC(in_buf) = le16_to_cpu(BCC_LE(in_buf)); BCC(midQ->resp_buf) =
le16_to_cpu(BCC_LE(midQ->resp_buf));
midQ->resp_buf = NULL; /* mark it so will not be freed
by DeleteMidQEntry */
} else { } else {
rc = -EIO; rc = -EIO;
cFYI(1,("Bad MID state?")); cFYI(1,("Bad MID state?"));
...@@ -793,7 +793,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, ...@@ -793,7 +793,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
} else { } else {
rc = -EIO; rc = -EIO;
cERROR(1,("Bad MID state? ")); cERROR(1,("Bad MID state?"));
} }
} }
cifs_no_response_exit: cifs_no_response_exit:
......
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