Commit 9093ba53 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6:
  cifs: fix up CIFSSMBEcho for unaligned access
  cifs: fix unaligned accesses in cifsConvertToUCS
  cifs: clean up unaligned accesses in cifs_unicode.c
  cifs: fix unaligned access in check2ndT2 and coalesce_t2
  cifs: clean up unaligned accesses in validate_t2
  cifs: use get/put_unaligned functions to access ByteCount
  cifs: move time field in cifsInodeInfo
  cifs: TCP_Server_Info diet
  CIFS: Implement cifs_strict_readv (try #4)
  CIFS: Implement cifs_file_strict_mmap (try #2)
  CIFS: Implement cifs_strict_fsync
  CIFS: Make cifsFileInfo_put work with strict cache mode
parents ebe0d805 99d86c8f
......@@ -40,6 +40,7 @@
#define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */
#define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */
#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */
#define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */
struct cifs_sb_info {
struct rb_root tlink_tree;
......
......@@ -44,10 +44,14 @@ cifs_ucs2_bytes(const __le16 *from, int maxbytes,
int charlen, outlen = 0;
int maxwords = maxbytes / 2;
char tmp[NLS_MAX_CHARSET_SIZE];
__u16 ftmp;
for (i = 0; i < maxwords && from[i]; i++) {
charlen = codepage->uni2char(le16_to_cpu(from[i]), tmp,
NLS_MAX_CHARSET_SIZE);
for (i = 0; i < maxwords; i++) {
ftmp = get_unaligned_le16(&from[i]);
if (ftmp == 0)
break;
charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE);
if (charlen > 0)
outlen += charlen;
else
......@@ -58,9 +62,9 @@ cifs_ucs2_bytes(const __le16 *from, int maxbytes,
}
/*
* cifs_mapchar - convert a little-endian char to proper char in codepage
* cifs_mapchar - convert a host-endian char to proper char in codepage
* @target - where converted character should be copied
* @src_char - 2 byte little-endian source character
* @src_char - 2 byte host-endian source character
* @cp - codepage to which character should be converted
* @mapchar - should character be mapped according to mapchars mount option?
*
......@@ -69,7 +73,7 @@ cifs_ucs2_bytes(const __le16 *from, int maxbytes,
* enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE).
*/
static int
cifs_mapchar(char *target, const __le16 src_char, const struct nls_table *cp,
cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
bool mapchar)
{
int len = 1;
......@@ -82,7 +86,7 @@ cifs_mapchar(char *target, const __le16 src_char, const struct nls_table *cp,
* build_path_from_dentry are modified, as they use slash as
* separator.
*/
switch (le16_to_cpu(src_char)) {
switch (src_char) {
case UNI_COLON:
*target = ':';
break;
......@@ -109,8 +113,7 @@ cifs_mapchar(char *target, const __le16 src_char, const struct nls_table *cp,
return len;
cp_convert:
len = cp->uni2char(le16_to_cpu(src_char), target,
NLS_MAX_CHARSET_SIZE);
len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
if (len <= 0) {
*target = '?';
len = 1;
......@@ -149,6 +152,7 @@ cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen,
int nullsize = nls_nullsize(codepage);
int fromwords = fromlen / 2;
char tmp[NLS_MAX_CHARSET_SIZE];
__u16 ftmp;
/*
* because the chars can be of varying widths, we need to take care
......@@ -158,19 +162,23 @@ cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen,
*/
safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize);
for (i = 0; i < fromwords && from[i]; i++) {
for (i = 0; i < fromwords; i++) {
ftmp = get_unaligned_le16(&from[i]);
if (ftmp == 0)
break;
/*
* check to see if converting this character might make the
* conversion bleed into the null terminator
*/
if (outlen >= safelen) {
charlen = cifs_mapchar(tmp, from[i], codepage, mapchar);
charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar);
if ((outlen + charlen) > (tolen - nullsize))
break;
}
/* put converted char into 'to' buffer */
charlen = cifs_mapchar(&to[outlen], from[i], codepage, mapchar);
charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar);
outlen += charlen;
}
......@@ -193,24 +201,21 @@ cifs_strtoUCS(__le16 *to, const char *from, int len,
{
int charlen;
int i;
wchar_t *wchar_to = (wchar_t *)to; /* needed to quiet sparse */
wchar_t wchar_to; /* needed to quiet sparse */
for (i = 0; len && *from; i++, from += charlen, len -= charlen) {
/* works for 2.4.0 kernel or later */
charlen = codepage->char2uni(from, len, &wchar_to[i]);
charlen = codepage->char2uni(from, len, &wchar_to);
if (charlen < 1) {
cERROR(1, "strtoUCS: char2uni of %d returned %d",
(int)*from, charlen);
cERROR(1, "strtoUCS: char2uni of 0x%x returned %d",
*from, charlen);
/* A question mark */
to[i] = cpu_to_le16(0x003f);
wchar_to = 0x003f;
charlen = 1;
} else
to[i] = cpu_to_le16(wchar_to[i]);
}
put_unaligned_le16(wchar_to, &to[i]);
}
to[i] = 0;
put_unaligned_le16(0, &to[i]);
return i;
}
......@@ -252,3 +257,79 @@ cifs_strndup_from_ucs(const char *src, const int maxlen, const bool is_unicode,
return dst;
}
/*
* Convert 16 bit Unicode pathname to wire format from string in current code
* page. Conversion may involve remapping up the six characters that are
* only legal in POSIX-like OS (if they are present in the string). Path
* names are little endian 16 bit Unicode on the wire
*/
int
cifsConvertToUCS(__le16 *target, const char *source, int maxlen,
const struct nls_table *cp, int mapChars)
{
int i, j, charlen;
int len_remaining = maxlen;
char src_char;
__u16 temp;
if (!mapChars)
return cifs_strtoUCS(target, source, PATH_MAX, cp);
for (i = 0, j = 0; i < maxlen; j++) {
src_char = source[i];
switch (src_char) {
case 0:
put_unaligned_le16(0, &target[j]);
goto ctoUCS_out;
case ':':
temp = UNI_COLON;
break;
case '*':
temp = UNI_ASTERIK;
break;
case '?':
temp = UNI_QUESTION;
break;
case '<':
temp = UNI_LESSTHAN;
break;
case '>':
temp = UNI_GRTRTHAN;
break;
case '|':
temp = UNI_PIPE;
break;
/*
* FIXME: We can not handle remapping backslash (UNI_SLASH)
* until all the calls to build_path_from_dentry are modified,
* as they use backslash as separator.
*/
default:
charlen = cp->char2uni(source+i, len_remaining,
&temp);
/*
* if no match, use question mark, which at least in
* some cases serves as wild card
*/
if (charlen < 1) {
temp = 0x003f;
charlen = 1;
}
len_remaining -= charlen;
/*
* character may take more than one byte in the source
* string, but will take exactly two bytes in the
* target string
*/
i += charlen;
continue;
}
put_unaligned_le16(temp, &target[j]);
i++; /* move to next char in source string */
len_remaining--;
}
ctoUCS_out:
return i;
}
......@@ -733,6 +733,25 @@ const struct file_operations cifs_file_ops = {
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_strict_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_strict_readv,
.aio_write = cifs_file_aio_write,
.open = cifs_open,
.release = cifs_close,
.lock = cifs_lock,
.fsync = cifs_strict_fsync,
.flush = cifs_flush,
.mmap = cifs_file_strict_mmap,
.splice_read = generic_file_splice_read,
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_direct_ops = {
/* no aio, no readv -
BB reevaluate whether they can be done with directio, no cache */
......@@ -751,6 +770,7 @@ const struct file_operations cifs_file_direct_ops = {
.llseek = cifs_llseek,
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_nobrl_ops = {
.read = do_sync_read,
.write = do_sync_write,
......@@ -769,6 +789,24 @@ const struct file_operations cifs_file_nobrl_ops = {
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_strict_nobrl_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_strict_readv,
.aio_write = cifs_file_aio_write,
.open = cifs_open,
.release = cifs_close,
.fsync = cifs_strict_fsync,
.flush = cifs_flush,
.mmap = cifs_file_strict_mmap,
.splice_read = generic_file_splice_read,
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_direct_nobrl_ops = {
/* no mmap, no aio, no readv -
BB reevaluate whether they can be done with directio, no cache */
......
......@@ -61,6 +61,7 @@ extern int cifs_rename(struct inode *, struct dentry *, struct inode *,
struct dentry *);
extern int cifs_revalidate_file(struct file *filp);
extern int cifs_revalidate_dentry(struct dentry *);
extern void cifs_invalidate_mapping(struct inode *inode);
extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int cifs_setattr(struct dentry *, struct iattr *);
......@@ -72,19 +73,25 @@ extern const struct inode_operations cifs_dfs_referral_inode_operations;
/* Functions related to files and directories */
extern const struct file_operations cifs_file_ops;
extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */
extern const struct file_operations cifs_file_nobrl_ops;
extern const struct file_operations cifs_file_direct_nobrl_ops; /* no brlocks */
extern const struct file_operations cifs_file_strict_ops; /* if strictio mnt */
extern const struct file_operations cifs_file_nobrl_ops; /* no brlocks */
extern const struct file_operations cifs_file_direct_nobrl_ops;
extern const struct file_operations cifs_file_strict_nobrl_ops;
extern int cifs_open(struct inode *inode, struct file *file);
extern int cifs_close(struct inode *inode, struct file *file);
extern int cifs_closedir(struct inode *inode, struct file *file);
extern ssize_t cifs_user_read(struct file *file, char __user *read_data,
size_t read_size, loff_t *poffset);
extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
extern ssize_t cifs_user_write(struct file *file, const char __user *write_data,
size_t write_size, loff_t *poffset);
extern int cifs_lock(struct file *, int, struct file_lock *);
extern int cifs_fsync(struct file *, int);
extern int cifs_strict_fsync(struct file *, int);
extern int cifs_flush(struct file *, fl_owner_t id);
extern int cifs_file_mmap(struct file * , struct vm_area_struct *);
extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *);
extern const struct file_operations cifs_dir_ops;
extern int cifs_dir_open(struct inode *inode, struct file *file);
extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir);
......
......@@ -161,6 +161,7 @@ struct TCP_Server_Info {
int srv_count; /* reference counter */
/* 15 character server name + 0x20 16th byte indicating type = srv */
char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
enum statusEnum tcpStatus; /* what we think the status is */
char *hostname; /* hostname portion of UNC string */
struct socket *ssocket;
struct sockaddr_storage dstaddr;
......@@ -168,25 +169,16 @@ struct TCP_Server_Info {
wait_queue_head_t response_q;
wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
struct list_head pending_mid_q;
void *Server_NlsInfo; /* BB - placeholder for future NLS info */
unsigned short server_codepage; /* codepage for the server */
enum protocolEnum protocolType;
char versionMajor;
char versionMinor;
bool svlocal:1; /* local server or remote */
bool noblocksnd; /* use blocking sendmsg */
bool noautotune; /* do not autotune send buf sizes */
bool tcp_nodelay;
atomic_t inFlight; /* number of requests on the wire to server */
#ifdef CONFIG_CIFS_STATS2
atomic_t inSend; /* requests trying to send */
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
#endif
enum statusEnum tcpStatus; /* what we think the status is */
struct mutex srv_mutex;
struct task_struct *tsk;
char server_GUID[16];
char secMode;
bool session_estab; /* mark when very first sess is established */
u16 dialect; /* dialect index that server chose */
enum securityEnum secType;
unsigned int maxReq; /* Clients should submit no more */
/* than maxReq distinct unanswered SMBs to the server when using */
......@@ -199,8 +191,6 @@ struct TCP_Server_Info {
unsigned int max_vcs; /* maximum number of smb sessions, at least
those that can be specified uniquely with
vcnumbers */
char sessid[4]; /* unique token id for this session */
/* (returned on Negotiate */
int capabilities; /* allow selective disabling of caps by smb sess */
int timeAdj; /* Adjust for difference in server time zone in sec */
__u16 CurrentMid; /* multiplex id - rotating counter */
......@@ -210,18 +200,20 @@ struct TCP_Server_Info {
__u32 sequence_number; /* for signing, protected by srv_mutex */
struct session_key session_key;
unsigned long lstrp; /* when we got last response from this server */
u16 dialect; /* dialect index that server chose */
struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */
/* extended security flavors that server supports */
bool sec_ntlmssp; /* supports NTLMSSP */
bool sec_kerberosu2u; /* supports U2U Kerberos */
bool sec_kerberos; /* supports plain Kerberos */
bool sec_mskerberos; /* supports legacy MS Kerberos */
bool sec_kerberosu2u; /* supports U2U Kerberos */
bool sec_ntlmssp; /* supports NTLMSSP */
bool session_estab; /* mark when very first sess is established */
struct delayed_work echo; /* echo ping workqueue job */
#ifdef CONFIG_CIFS_FSCACHE
struct fscache_cookie *fscache; /* client index cache cookie */
#endif
#ifdef CONFIG_CIFS_STATS2
atomic_t inSend; /* requests trying to send */
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
#endif
};
/*
......@@ -447,11 +439,11 @@ struct cifsInodeInfo {
/* BB add in lists for dirty pages i.e. write caching info for oplock */
struct list_head openFileList;
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
unsigned long time; /* jiffies of last update/check of inode */
bool clientCanCacheRead:1; /* read oplock */
bool clientCanCacheAll:1; /* read and writebehind oplock */
bool delete_pending:1; /* DELETE_ON_CLOSE is set */
bool invalid_mapping:1; /* pagecache is invalid */
bool clientCanCacheRead; /* read oplock */
bool clientCanCacheAll; /* read and writebehind oplock */
bool delete_pending; /* DELETE_ON_CLOSE is set */
bool invalid_mapping; /* pagecache is invalid */
unsigned long time; /* jiffies of last update of inode */
u64 server_eof; /* current file size on server */
u64 uniqueid; /* server inode number */
u64 createtime; /* creation time on server */
......
......@@ -23,6 +23,7 @@
#define _CIFSPDU_H
#include <net/sock.h>
#include <asm/unaligned.h>
#include "smbfsctl.h"
#ifdef CONFIG_CIFS_WEAK_PW_HASH
......@@ -426,11 +427,49 @@ struct smb_hdr {
__u16 Mid;
__u8 WordCount;
} __attribute__((packed));
/* given a pointer to an smb_hdr retrieve the value of byte count */
#define BCC(smb_var) (*(__u16 *)((char *)(smb_var) + sizeof(struct smb_hdr) + (2 * (smb_var)->WordCount)))
#define BCC_LE(smb_var) (*(__le16 *)((char *)(smb_var) + sizeof(struct smb_hdr) + (2 * (smb_var)->WordCount)))
/* given a pointer to an smb_hdr retrieve a char pointer to the byte count */
#define BCC(smb_var) ((unsigned char *)(smb_var) + sizeof(struct smb_hdr) + \
(2 * (smb_var)->WordCount))
/* given a pointer to an smb_hdr retrieve the pointer to the byte area */
#define pByteArea(smb_var) ((unsigned char *)(smb_var) + sizeof(struct smb_hdr) + (2 * (smb_var)->WordCount) + 2)
#define pByteArea(smb_var) (BCC(smb_var) + 2)
/* get the converted ByteCount for a SMB packet and return it */
static inline __u16
get_bcc(struct smb_hdr *hdr)
{
__u16 *bc_ptr = (__u16 *)BCC(hdr);
return get_unaligned(bc_ptr);
}
/* get the unconverted ByteCount for a SMB packet and return it */
static inline __u16
get_bcc_le(struct smb_hdr *hdr)
{
__le16 *bc_ptr = (__le16 *)BCC(hdr);
return get_unaligned_le16(bc_ptr);
}
/* set the ByteCount for a SMB packet in host-byte order */
static inline void
put_bcc(__u16 count, struct smb_hdr *hdr)
{
__u16 *bc_ptr = (__u16 *)BCC(hdr);
put_unaligned(count, bc_ptr);
}
/* set the ByteCount for a SMB packet in little-endian */
static inline void
put_bcc_le(__u16 count, struct smb_hdr *hdr)
{
__le16 *bc_ptr = (__le16 *)BCC(hdr);
put_unaligned_le16(count, bc_ptr);
}
/*
* Computer Name Length (since Netbios name was length 16 with last byte 0x20)
......
......@@ -331,37 +331,35 @@ smb_init_no_reconnect(int smb_command, int wct, struct cifsTconInfo *tcon,
static int validate_t2(struct smb_t2_rsp *pSMB)
{
int rc = -EINVAL;
int total_size;
char *pBCC;
unsigned int total_size;
/* check for plausible wct */
if (pSMB->hdr.WordCount < 10)
goto vt2_err;
/* check for plausible wct, bcc and t2 data and parm sizes */
/* check for parm and data offset going beyond end of smb */
if (pSMB->hdr.WordCount >= 10) {
if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
(le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 ||
get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024)
goto vt2_err;
/* check that bcc is at least as big as parms + data */
/* check that bcc is less than negotiated smb buffer */
total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
if (total_size < 512) {
total_size +=
le16_to_cpu(pSMB->t2_rsp.DataCount);
/* BCC le converted in SendReceive */
pBCC = (pSMB->hdr.WordCount * 2) +
sizeof(struct smb_hdr) +
(char *)pSMB;
if ((total_size <= (*(u16 *)pBCC)) &&
(total_size <
CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount);
if (total_size >= 512)
goto vt2_err;
total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount);
if (total_size > get_bcc(&pSMB->hdr) ||
total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE)
goto vt2_err;
return 0;
}
}
}
}
vt2_err:
cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
sizeof(struct smb_t2_rsp) + 16);
return rc;
return -EINVAL;
}
int
CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
{
......@@ -452,7 +450,6 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
(__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
/* even though we do not use raw we might as well set this
accurately, in case we ever find a need for it */
if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
......@@ -566,7 +563,6 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
(__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
cFYI(DBG2, "Max buf = %d", ses->server->maxBuf);
GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
server->capabilities = le32_to_cpu(pSMBr->Capabilities);
server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
server->timeAdj *= 60;
......@@ -737,9 +733,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
/* set up echo request */
smb->hdr.Tid = cpu_to_le16(0xffff);
smb->hdr.WordCount = cpu_to_le16(1);
smb->EchoCount = cpu_to_le16(1);
smb->ByteCount = cpu_to_le16(1);
smb->hdr.WordCount = 1;
put_unaligned_le16(1, &smb->EchoCount);
put_bcc_le(1, &smb->hdr);
smb->Data[0] = 'a';
smb->hdr.smb_buf_length += 3;
......@@ -5611,7 +5607,7 @@ CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
}
/* make sure list_len doesn't go past end of SMB */
end_of_smb = (char *)pByteArea(&pSMBr->hdr) + BCC(&pSMBr->hdr);
end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr);
if ((char *)ea_response_data + list_len > end_of_smb) {
cFYI(1, "EA list appears to go beyond SMB");
rc = -EIO;
......
......@@ -232,9 +232,8 @@ cifs_reconnect(struct TCP_Server_Info *server)
static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize)
{
struct smb_t2_rsp *pSMBt;
int total_data_size;
int data_in_this_rsp;
int remaining;
__u16 total_data_size, data_in_this_rsp;
if (pSMB->Command != SMB_COM_TRANSACTION2)
return 0;
......@@ -248,8 +247,8 @@ static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize)
pSMBt = (struct smb_t2_rsp *)pSMB;
total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);
data_in_this_rsp = le16_to_cpu(pSMBt->t2_rsp.DataCount);
total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount);
data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount);
remaining = total_data_size - data_in_this_rsp;
......@@ -275,21 +274,18 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB)
{
struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond;
struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB;
int total_data_size;
int total_in_buf;
int remaining;
int total_in_buf2;
char *data_area_of_target;
char *data_area_of_buf2;
__u16 byte_count;
int remaining;
__u16 byte_count, total_data_size, total_in_buf, total_in_buf2;
total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);
total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount);
if (total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) {
if (total_data_size !=
get_unaligned_le16(&pSMB2->t2_rsp.TotalDataCount))
cFYI(1, "total data size of primary and secondary t2 differ");
}
total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount);
total_in_buf = get_unaligned_le16(&pSMBt->t2_rsp.DataCount);
remaining = total_data_size - total_in_buf;
......@@ -299,28 +295,28 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB)
if (remaining == 0) /* nothing to do, ignore */
return 0;
total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount);
total_in_buf2 = get_unaligned_le16(&pSMB2->t2_rsp.DataCount);
if (remaining < total_in_buf2) {
cFYI(1, "transact2 2nd response contains too much data");
}
/* find end of first SMB data area */
data_area_of_target = (char *)&pSMBt->hdr.Protocol +
le16_to_cpu(pSMBt->t2_rsp.DataOffset);
get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
/* validate target area */
data_area_of_buf2 = (char *) &pSMB2->hdr.Protocol +
le16_to_cpu(pSMB2->t2_rsp.DataOffset);
data_area_of_buf2 = (char *)&pSMB2->hdr.Protocol +
get_unaligned_le16(&pSMB2->t2_rsp.DataOffset);
data_area_of_target += total_in_buf;
/* copy second buffer into end of first buffer */
memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2);
total_in_buf += total_in_buf2;
pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf);
byte_count = le16_to_cpu(BCC_LE(pTargetSMB));
put_unaligned_le16(total_in_buf, &pSMBt->t2_rsp.DataCount);
byte_count = get_bcc_le(pTargetSMB);
byte_count += total_in_buf2;
BCC_LE(pTargetSMB) = cpu_to_le16(byte_count);
put_bcc_le(byte_count, pTargetSMB);
byte_count = pTargetSMB->smb_buf_length;
byte_count += total_in_buf2;
......@@ -334,7 +330,6 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB)
return 0; /* we are done */
} else /* more responses to go */
return 1;
}
static void
......@@ -2937,8 +2932,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
TCONX_RSP *pSMBr;
unsigned char *bcc_ptr;
int rc = 0;
int length, bytes_left;
__u16 count;
int length;
__u16 bytes_left, count;
if (ses == NULL)
return -EIO;
......@@ -3032,7 +3027,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
tcon->need_reconnect = false;
tcon->tid = smb_buffer_response->Tid;
bcc_ptr = pByteArea(smb_buffer_response);
bytes_left = BCC(smb_buffer_response);
bytes_left = get_bcc(smb_buffer_response);
length = strnlen(bcc_ptr, bytes_left - 2);
if (smb_buffer->Flags2 & SMBFLG2_UNICODE)
is_unicode = true;
......
......@@ -287,6 +287,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
struct inode *inode = cifs_file->dentry->d_inode;
struct cifsTconInfo *tcon = tlink_tcon(cifs_file->tlink);
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsLockInfo *li, *tmp;
spin_lock(&cifs_file_list_lock);
......@@ -302,6 +303,13 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
if (list_empty(&cifsi->openFileList)) {
cFYI(1, "closing last open instance for inode %p",
cifs_file->dentry->d_inode);
/* in strict cache mode we need invalidate mapping on the last
close because it may cause a error when we open this file
again and get at least level II oplock */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
CIFS_I(inode)->invalid_mapping = true;
cifs_set_oplock_level(cifsi, 0);
}
spin_unlock(&cifs_file_list_lock);
......@@ -1520,27 +1528,47 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
return rc;
}
int cifs_fsync(struct file *file, int datasync)
int cifs_strict_fsync(struct file *file, int datasync)
{
int xid;
int rc = 0;
struct cifsTconInfo *tcon;
struct cifsFileInfo *smbfile = file->private_data;
struct inode *inode = file->f_path.dentry->d_inode;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
xid = GetXid();
cFYI(1, "Sync file - name: %s datasync: 0x%x",
file->f_path.dentry->d_name.name, datasync);
rc = filemap_write_and_wait(inode->i_mapping);
if (rc == 0) {
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
if (!CIFS_I(inode)->clientCanCacheRead)
cifs_invalidate_mapping(inode);
tcon = tlink_tcon(smbfile->tlink);
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
FreeXid(xid);
return rc;
}
int cifs_fsync(struct file *file, int datasync)
{
int xid;
int rc = 0;
struct cifsTconInfo *tcon;
struct cifsFileInfo *smbfile = file->private_data;
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
xid = GetXid();
cFYI(1, "Sync file - name: %s datasync: 0x%x",
file->f_path.dentry->d_name.name, datasync);
tcon = tlink_tcon(smbfile->tlink);
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
}
FreeXid(xid);
return rc;
......@@ -1591,42 +1619,42 @@ int cifs_flush(struct file *file, fl_owner_t id)
return rc;
}
ssize_t cifs_user_read(struct file *file, char __user *read_data,
size_t read_size, loff_t *poffset)
static ssize_t
cifs_iovec_read(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *poffset)
{
int rc = -EACCES;
unsigned int bytes_read = 0;
unsigned int total_read = 0;
unsigned int current_read_size;
int rc;
int xid;
unsigned int total_read, bytes_read = 0;
size_t len, cur_len;
int iov_offset = 0;
struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon;
int xid;
struct cifsFileInfo *open_file;
char *smb_read_data;
char __user *current_offset;
struct smb_com_read_rsp *pSMBr;
char *read_data;
if (!nr_segs)
return 0;
len = iov_length(iov, nr_segs);
if (!len)
return 0;
xid = GetXid();
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
if (file->private_data == NULL) {
rc = -EBADF;
FreeXid(xid);
return rc;
}
open_file = file->private_data;
pTcon = tlink_tcon(open_file->tlink);
if ((file->f_flags & O_ACCMODE) == O_WRONLY)
cFYI(1, "attempting read on write only file instance");
for (total_read = 0, current_offset = read_data;
read_size > total_read;
total_read += bytes_read, current_offset += bytes_read) {
current_read_size = min_t(const int, read_size - total_read,
cifs_sb->rsize);
for (total_read = 0; total_read < len; total_read += bytes_read) {
cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize);
rc = -EAGAIN;
smb_read_data = NULL;
read_data = NULL;
while (rc == -EAGAIN) {
int buf_type = CIFS_NO_BUFFER;
if (open_file->invalidHandle) {
......@@ -1634,27 +1662,25 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
if (rc != 0)
break;
}
rc = CIFSSMBRead(xid, pTcon,
open_file->netfid,
current_read_size, *poffset,
&bytes_read, &smb_read_data,
&buf_type);
pSMBr = (struct smb_com_read_rsp *)smb_read_data;
if (smb_read_data) {
if (copy_to_user(current_offset,
smb_read_data +
4 /* RFC1001 length field */ +
le16_to_cpu(pSMBr->DataOffset),
bytes_read))
rc = CIFSSMBRead(xid, pTcon, open_file->netfid,
cur_len, *poffset, &bytes_read,
&read_data, &buf_type);
pSMBr = (struct smb_com_read_rsp *)read_data;
if (read_data) {
char *data_offset = read_data + 4 +
le16_to_cpu(pSMBr->DataOffset);
if (memcpy_toiovecend(iov, data_offset,
iov_offset, bytes_read))
rc = -EFAULT;
if (buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(smb_read_data);
cifs_small_buf_release(read_data);
else if (buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(smb_read_data);
smb_read_data = NULL;
cifs_buf_release(read_data);
read_data = NULL;
iov_offset += bytes_read;
}
}
if (rc || (bytes_read == 0)) {
if (total_read) {
break;
......@@ -1667,10 +1693,54 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
*poffset += bytes_read;
}
}
FreeXid(xid);
return total_read;
}
ssize_t cifs_user_read(struct file *file, char __user *read_data,
size_t read_size, loff_t *poffset)
{
struct iovec iov;
iov.iov_base = read_data;
iov.iov_len = read_size;
return cifs_iovec_read(file, &iov, 1, poffset);
}
static ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
ssize_t read;
read = cifs_iovec_read(iocb->ki_filp, iov, nr_segs, &pos);
if (read > 0)
iocb->ki_pos = pos;
return read;
}
ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct inode *inode;
inode = iocb->ki_filp->f_path.dentry->d_inode;
if (CIFS_I(inode)->clientCanCacheRead)
return generic_file_aio_read(iocb, iov, nr_segs, pos);
/*
* In strict cache mode we need to read from the server all the time
* if we don't have level II oplock because the server can delay mtime
* change - so we can't make a decision about inode invalidating.
* And we can also fail with pagereading if there are mandatory locks
* on pages affected by this read but not on the region from pos to
* pos+len-1.
*/
return cifs_user_readv(iocb, iov, nr_segs, pos);
}
static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
loff_t *poffset)
......@@ -1741,6 +1811,21 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
return total_read;
}
int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma)
{
int rc, xid;
struct inode *inode = file->f_path.dentry->d_inode;
xid = GetXid();
if (!CIFS_I(inode)->clientCanCacheRead)
cifs_invalidate_mapping(inode);
rc = generic_file_mmap(file, vma);
FreeXid(xid);
return rc;
}
int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
{
int rc, xid;
......
......@@ -44,13 +44,17 @@ static void cifs_set_ops(struct inode *inode)
inode->i_fop = &cifs_file_direct_nobrl_ops;
else
inode->i_fop = &cifs_file_direct_ops;
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
inode->i_fop = &cifs_file_strict_nobrl_ops;
else
inode->i_fop = &cifs_file_strict_ops;
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
inode->i_fop = &cifs_file_nobrl_ops;
else { /* not direct, send byte range locks */
inode->i_fop = &cifs_file_ops;
}
/* check if server can support readpages */
if (cifs_sb_master_tcon(cifs_sb)->ses->server->maxBuf <
PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)
......@@ -1679,7 +1683,7 @@ cifs_inode_needs_reval(struct inode *inode)
/*
* Zap the cache. Called when invalid_mapping flag is set.
*/
static void
void
cifs_invalidate_mapping(struct inode *inode)
{
int rc;
......
......@@ -637,77 +637,6 @@ dump_smb(struct smb_hdr *smb_buf, int smb_buf_length)
return;
}
/* Convert 16 bit Unicode pathname to wire format from string in current code
page. Conversion may involve remapping up the seven characters that are
only legal in POSIX-like OS (if they are present in the string). Path
names are little endian 16 bit Unicode on the wire */
int
cifsConvertToUCS(__le16 *target, const char *source, int maxlen,
const struct nls_table *cp, int mapChars)
{
int i, j, charlen;
int len_remaining = maxlen;
char src_char;
__u16 temp;
if (!mapChars)
return cifs_strtoUCS(target, source, PATH_MAX, cp);
for (i = 0, j = 0; i < maxlen; j++) {
src_char = source[i];
switch (src_char) {
case 0:
target[j] = 0;
goto ctoUCS_out;
case ':':
target[j] = cpu_to_le16(UNI_COLON);
break;
case '*':
target[j] = cpu_to_le16(UNI_ASTERIK);
break;
case '?':
target[j] = cpu_to_le16(UNI_QUESTION);
break;
case '<':
target[j] = cpu_to_le16(UNI_LESSTHAN);
break;
case '>':
target[j] = cpu_to_le16(UNI_GRTRTHAN);
break;
case '|':
target[j] = cpu_to_le16(UNI_PIPE);
break;
/* BB We can not handle remapping slash until
all the calls to build_path_from_dentry
are modified, as they use slash as separator BB */
/* case '\\':
target[j] = cpu_to_le16(UNI_SLASH);
break;*/
default:
charlen = cp->char2uni(source+i,
len_remaining, &temp);
/* if no match, use question mark, which
at least in some cases servers as wild card */
if (charlen < 1) {
target[j] = cpu_to_le16(0x003f);
charlen = 1;
} else
target[j] = cpu_to_le16(temp);
len_remaining -= charlen;
/* character may take more than one byte in the
the source string, but will take exactly two
bytes in the target string */
i += charlen;
continue;
}
i++; /* move to next char in source string */
len_remaining--;
}
ctoUCS_out:
return i;
}
void
cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
{
......
......@@ -916,14 +916,14 @@ unsigned int
smbCalcSize(struct smb_hdr *ptr)
{
return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
2 /* size of the bcc field */ + BCC(ptr));
2 /* size of the bcc field */ + get_bcc(ptr));
}
unsigned int
smbCalcSize_LE(struct smb_hdr *ptr)
{
return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
2 /* size of the bcc field */ + le16_to_cpu(BCC_LE(ptr)));
2 /* size of the bcc field */ + get_bcc_le(ptr));
}
/* The following are taken from fs/ntfs/util.c */
......
......@@ -277,7 +277,7 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses,
}
static void
decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses,
decode_unicode_ssetup(char **pbcc_area, __u16 bleft, struct cifsSesInfo *ses,
const struct nls_table *nls_cp)
{
int len;
......@@ -323,7 +323,7 @@ decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses,
return;
}
static int decode_ascii_ssetup(char **pbcc_area, int bleft,
static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft,
struct cifsSesInfo *ses,
const struct nls_table *nls_cp)
{
......@@ -575,12 +575,11 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
char *str_area;
SESSION_SETUP_ANDX *pSMB;
__u32 capabilities;
int count;
__u16 count;
int resp_buf_type;
struct kvec iov[3];
enum securityEnum type;
__u16 action;
int bytes_remaining;
__u16 action, bytes_remaining;
struct key *spnego_key = NULL;
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
u16 blob_len;
......@@ -876,7 +875,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
count = iov[1].iov_len + iov[2].iov_len;
smb_buf->smb_buf_length += count;
BCC_LE(smb_buf) = cpu_to_le16(count);
put_bcc_le(count, smb_buf);
rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type,
CIFS_LOG_ERROR);
......@@ -910,7 +909,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
cFYI(1, "UID = %d ", ses->Suid);
/* response can have either 3 or 4 word count - Samba sends 3 */
/* and lanman response is 3 */
bytes_remaining = BCC(smb_buf);
bytes_remaining = get_bcc(smb_buf);
bcc_ptr = pByteArea(smb_buf);
if (smb_buf->WordCount == 4) {
......
......@@ -484,7 +484,7 @@ send_nt_cancel(struct TCP_Server_Info *server, struct smb_hdr *in_buf,
in_buf->smb_buf_length = sizeof(struct smb_hdr) - 4 + 2;
in_buf->Command = SMB_COM_NT_CANCEL;
in_buf->WordCount = 0;
BCC_LE(in_buf) = 0;
put_bcc_le(0, in_buf);
mutex_lock(&server->srv_mutex);
rc = cifs_sign_smb(in_buf, server, &mid->sequence_number);
......@@ -632,8 +632,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
if (receive_len >= sizeof(struct smb_hdr) - 4
/* do not count RFC1001 header */ +
(2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
BCC(midQ->resp_buf) =
le16_to_cpu(BCC_LE(midQ->resp_buf));
put_bcc(get_bcc_le(midQ->resp_buf), midQ->resp_buf);
if ((flags & CIFS_NO_RESP) == 0)
midQ->resp_buf = NULL; /* mark it so buf will
not be freed by
......@@ -776,7 +775,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
if (receive_len >= sizeof(struct smb_hdr) - 4
/* do not count RFC1001 header */ +
(2 * out_buf->WordCount) + 2 /* bcc */ )
BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
put_bcc(get_bcc_le(midQ->resp_buf), midQ->resp_buf);
} else {
rc = -EIO;
cERROR(1, "Bad MID state?");
......@@ -977,7 +976,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
if (receive_len >= sizeof(struct smb_hdr) - 4
/* do not count RFC1001 header */ +
(2 * out_buf->WordCount) + 2 /* bcc */ )
BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
put_bcc(get_bcc_le(out_buf), out_buf);
out:
delete_mid(midQ);
......
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