summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-08-29 15:54:07 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-08-29 15:54:07 -0700
commitdc709bd190c130b299ac19d596594256265c042a (patch)
tree53c74b614d906825d478b62c068e2ed11b883c95 /fs
parentd96299537e43681942ea272e00b0e529aa5b5fa4 (diff)
parentea4c07d780a6f7b7be2d984117bd3e0a2b772e3d (diff)
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] Do not send Query All EAs SMB when mount option nouser_xattr [CIFS] endian errors in lanman protocol support [CIFS] Fix oops in cifs_close due to unitialized lock sem and list in [CIFS] Fix oops when negotiating lanman and no password specified [CIFS] [CIFS] Allow cifsd to suspend if connection is lost [CIFS] Make midState usage more consistent [CIFS] spinlock protect read of last srv response time in timeout path [CIFS] Do not time out posix brl requests when using new posix setfileinfo
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/CHANGES10
-rw-r--r--fs/cifs/README2
-rw-r--r--fs/cifs/cifsencrypt.c3
-rw-r--r--fs/cifs/cifsfs.c6
-rw-r--r--fs/cifs/cifsfs.h2
-rw-r--r--fs/cifs/cifsglob.h18
-rw-r--r--fs/cifs/cifsproto.h4
-rw-r--r--fs/cifs/cifssmb.c28
-rw-r--r--fs/cifs/connect.c32
-rw-r--r--fs/cifs/dir.c4
-rw-r--r--fs/cifs/file.c97
-rw-r--r--fs/cifs/netmisc.c1
-rw-r--r--fs/cifs/readdir.c2
-rw-r--r--fs/cifs/sess.c2
-rw-r--r--fs/cifs/smberr.h1
-rw-r--r--fs/cifs/transport.c618
-rw-r--r--fs/cifs/xattr.c6
17 files changed, 576 insertions, 260 deletions
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index a61d17ed182..0feb3bd49cb 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -1,3 +1,13 @@
+Version 1.45
+------------
+Do not time out lockw calls when using posix extensions. Do not
+time out requests if server still responding reasonably fast
+on requests on other threads. Improve POSIX locking emulation,
+(lock cancel now works, and unlock of merged range works even
+to Windows servers now). Fix oops on mount to lanman servers
+(win9x, os/2 etc.) when null password. Do not send listxattr
+(SMB to query all EAs) if nouser_xattr specified.
+
Version 1.44
------------
Rewritten sessionsetup support, including support for legacy SMB
diff --git a/fs/cifs/README b/fs/cifs/README
index 7986d0d97ac..5f0e1bd64fe 100644
--- a/fs/cifs/README
+++ b/fs/cifs/README
@@ -408,7 +408,7 @@ A partial list of the supported mount options follows:
user_xattr Allow getting and setting user xattrs as OS/2 EAs (extended
attributes) to the server (default) e.g. via setfattr
and getfattr utilities.
- nouser_xattr Do not allow getfattr/setfattr to get/set xattrs
+ nouser_xattr Do not allow getfattr/setfattr to get/set/list xattrs
mapchars Translate six of the seven reserved characters (not backslash)
*?<>|:
to the remap range (above 0xF000), which also
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index a89efaf78a2..4bc250b2d9f 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -277,7 +277,8 @@ void calc_lanman_hash(struct cifsSesInfo * ses, char * lnm_session_key)
return;
memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
- strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
+ if(ses->password)
+ strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
if((ses->server->secMode & SECMODE_PW_ENCRYPT) == 0)
if(extended_security & CIFSSEC_MAY_PLNTXT) {
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index c28ede59994..3cd750029be 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -402,7 +402,6 @@ static struct quotactl_ops cifs_quotactl_ops = {
};
#endif
-#ifdef CONFIG_CIFS_EXPERIMENTAL
static void cifs_umount_begin(struct vfsmount * vfsmnt, int flags)
{
struct cifs_sb_info *cifs_sb;
@@ -422,7 +421,7 @@ static void cifs_umount_begin(struct vfsmount * vfsmnt, int flags)
tcon->tidStatus = CifsExiting;
up(&tcon->tconSem);
- /* cancel_brl_requests(tcon); */
+ /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
/* cancel_notify_requests(tcon); */
if(tcon->ses && tcon->ses->server)
{
@@ -438,7 +437,6 @@ static void cifs_umount_begin(struct vfsmount * vfsmnt, int flags)
return;
}
-#endif
static int cifs_remount(struct super_block *sb, int *flags, char *data)
{
@@ -457,9 +455,7 @@ struct super_operations cifs_super_ops = {
unless later we add lazy close of inodes or unless the kernel forgets to call
us with the same number of releases (closes) as opens */
.show_options = cifs_show_options,
-#ifdef CONFIG_CIFS_EXPERIMENTAL
.umount_begin = cifs_umount_begin,
-#endif
.remount_fs = cifs_remount,
};
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index 8f75c6f2470..39ee8ef3bde 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -100,5 +100,5 @@ extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
extern int cifs_ioctl (struct inode * inode, struct file * filep,
unsigned int command, unsigned long arg);
-#define CIFS_VERSION "1.44"
+#define CIFS_VERSION "1.45"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 6d7cf5f3bc0..b24006c47df 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -3,6 +3,7 @@
*
* Copyright (C) International Business Machines Corp., 2002,2006
* Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
@@ -158,7 +159,8 @@ struct TCP_Server_Info {
/* 16th byte of RFC1001 workstation name is always null */
char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
__u32 sequence_number; /* needed for CIFS PDU signature */
- char mac_signing_key[CIFS_SESS_KEY_SIZE + 16];
+ char mac_signing_key[CIFS_SESS_KEY_SIZE + 16];
+ unsigned long lstrp; /* when we got last response from this server */
};
/*
@@ -266,14 +268,14 @@ struct cifsTconInfo {
};
/*
- * This info hangs off the cifsFileInfo structure. This is used to track
- * byte stream locks on the file
+ * This info hangs off the cifsFileInfo structure, pointed to by llist.
+ * This is used to track byte stream locks on the file
*/
struct cifsLockInfo {
- struct cifsLockInfo *next;
- int start;
- int length;
- int type;
+ struct list_head llist; /* pointer to next cifsLockInfo */
+ __u64 offset;
+ __u64 length;
+ __u8 type;
};
/*
@@ -304,6 +306,8 @@ struct cifsFileInfo {
/* lock scope id (0 if none) */
struct file * pfile; /* needed for writepage */
struct inode * pInode; /* needed for oplock break */
+ struct semaphore lock_sem;
+ struct list_head llist; /* list of byte range locks we have. */
unsigned closePend:1; /* file is marked to close */
unsigned invalidHandle:1; /* file closed via session abend */
atomic_t wrtPending; /* handle in use - defer close */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index a5ddc62d6fe..b35c55c3c8b 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -50,6 +50,10 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *,
extern int SendReceive2(const unsigned int /* xid */ , struct cifsSesInfo *,
struct kvec *, int /* nvec to send */,
int * /* type of buf returned */ , const int long_op);
+extern int SendReceiveBlockingLock(const unsigned int /* xid */ , struct cifsTconInfo *,
+ struct smb_hdr * /* input */ ,
+ struct smb_hdr * /* out */ ,
+ int * /* bytes returned */);
extern int checkSMBhdr(struct smb_hdr *smb, __u16 mid);
extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length);
extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 19678c575df..075d8fb3d37 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -477,7 +477,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
/* BB get server time for time conversions and add
code to use it and timezone since this is not UTC */
- if (rsp->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
+ if (rsp->EncryptionKeyLength == cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
memcpy(server->cryptKey, rsp->EncryptionKey,
CIFS_CRYPTO_KEY_SIZE);
} else if (server->secMode & SECMODE_PW_ENCRYPT) {
@@ -1460,8 +1460,13 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
pSMB->hdr.smb_buf_length += count;
pSMB->ByteCount = cpu_to_le16(count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ if (waitFlag) {
+ rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned);
+ } else {
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
(struct smb_hdr *) pSMBr, &bytes_returned, timeout);
+ }
cifs_stats_inc(&tcon->num_locks);
if (rc) {
cFYI(1, ("Send error in Lock = %d", rc));
@@ -1484,6 +1489,7 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
char *data_offset;
struct cifs_posix_lock *parm_data;
int rc = 0;
+ int timeout = 0;
int bytes_returned = 0;
__u16 params, param_offset, offset, byte_count, count;
@@ -1503,7 +1509,6 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
- pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
offset = param_offset + params;
@@ -1529,8 +1534,13 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
(((char *) &pSMB->hdr.Protocol) + offset);
parm_data->lock_type = cpu_to_le16(lock_type);
- if(waitFlag)
+ if(waitFlag) {
+ timeout = 3; /* blocking operation, no timeout */
parm_data->lock_flags = cpu_to_le16(1);
+ pSMB->Timeout = cpu_to_le32(-1);
+ } else
+ pSMB->Timeout = 0;
+
parm_data->pid = cpu_to_le32(current->tgid);
parm_data->start = cpu_to_le64(pLockData->fl_start);
parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
@@ -1541,8 +1551,14 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
pSMB->Reserved4 = 0;
pSMB->hdr.smb_buf_length += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
- (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (waitFlag) {
+ rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned);
+ } else {
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, timeout);
+ }
+
if (rc) {
cFYI(1, ("Send error in Posix Lock = %d", rc));
} else if (get_flag) {
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 876eb9ef85f..5d394c72686 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -182,6 +182,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
while ((server->tcpStatus != CifsExiting) && (server->tcpStatus != CifsGood))
{
+ try_to_freeze();
if(server->protocolType == IPV6) {
rc = ipv6_connect(&server->addr.sockAddr6,&server->ssocket);
} else {
@@ -612,6 +613,10 @@ multi_t2_fnd:
#ifdef CONFIG_CIFS_STATS2
mid_entry->when_received = jiffies;
#endif
+ /* so we do not time out requests to server
+ which is still responding (since server could
+ be busy but not dead) */
+ server->lstrp = jiffies;
break;
}
}
@@ -1266,33 +1271,35 @@ find_unc(__be32 new_target_ip_addr, char *uncName, char *userName)
read_lock(&GlobalSMBSeslock);
list_for_each(tmp, &GlobalTreeConnectionList) {
- cFYI(1, ("Next tcon - "));
+ cFYI(1, ("Next tcon"));
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
if (tcon->ses) {
if (tcon->ses->server) {
cFYI(1,
- (" old ip addr: %x == new ip %x ?",
+ ("old ip addr: %x == new ip %x ?",
tcon->ses->server->addr.sockAddr.sin_addr.
s_addr, new_target_ip_addr));
if (tcon->ses->server->addr.sockAddr.sin_addr.
s_addr == new_target_ip_addr) {
- /* BB lock tcon and server and tcp session and increment use count here? */
+ /* BB lock tcon, server and tcp session and increment use count here? */
/* found a match on the TCP session */
/* BB check if reconnection needed */
- cFYI(1,("Matched ip, old UNC: %s == new: %s ?",
+ cFYI(1,("IP match, old UNC: %s new: %s",
tcon->treeName, uncName));
if (strncmp
(tcon->treeName, uncName,
MAX_TREE_SIZE) == 0) {
cFYI(1,
- ("Matched UNC, old user: %s == new: %s ?",
+ ("and old usr: %s new: %s",
tcon->treeName, uncName));
if (strncmp
(tcon->ses->userName,
userName,
MAX_USERNAME_SIZE) == 0) {
read_unlock(&GlobalSMBSeslock);
- return tcon;/* also matched user (smb session)*/
+ /* matched smb session
+ (user name */
+ return tcon;
}
}
}
@@ -1969,7 +1976,18 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
}
cFYI(1,("Negotiate caps 0x%x",(int)cap));
-
+#ifdef CONFIG_CIFS_DEBUG2
+ if(cap & CIFS_UNIX_FCNTL_CAP)
+ cFYI(1,("FCNTL cap"));
+ if(cap & CIFS_UNIX_EXTATTR_CAP)
+ cFYI(1,("EXTATTR cap"));
+ if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
+ cFYI(1,("POSIX path cap"));
+ if(cap & CIFS_UNIX_XATTR_CAP)
+ cFYI(1,("XATTR cap"));
+ if(cap & CIFS_UNIX_POSIX_ACL_CAP)
+ cFYI(1,("POSIX ACL cap"));
+#endif /* CIFS_DEBUG2 */
if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
cFYI(1,("setting capabilities failed"));
}
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index ba4cbe9b068..914239d5363 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -267,6 +267,10 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
pCifsFile->invalidHandle = FALSE;
pCifsFile->closePend = FALSE;
init_MUTEX(&pCifsFile->fh_sem);
+ init_MUTEX(&pCifsFile->lock_sem);
+ INIT_LIST_HEAD(&pCifsFile->llist);
+ atomic_set(&pCifsFile->wrtPending,0);
+
/* set the following in open now
pCifsFile->pfile = file; */
write_lock(&GlobalSMBSeslock);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 944d2b9e092..e9c5ba9084f 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -5,6 +5,7 @@
*
* Copyright (C) International Business Machines Corp., 2002,2003
* Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
@@ -47,6 +48,8 @@ static inline struct cifsFileInfo *cifs_init_private(
private_data->netfid = netfid;
private_data->pid = current->tgid;
init_MUTEX(&private_data->fh_sem);
+ init_MUTEX(&private_data->lock_sem);
+ INIT_LIST_HEAD(&private_data->llist);
private_data->pfile = file; /* needed for writepage */
private_data->pInode = inode;
private_data->invalidHandle = FALSE;
@@ -473,6 +476,8 @@ int cifs_close(struct inode *inode, struct file *file)
cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon;
if (pSMBFile) {
+ struct cifsLockInfo *li, *tmp;
+
pSMBFile->closePend = TRUE;
if (pTcon) {
/* no sense reconnecting to close a file that is
@@ -496,6 +501,16 @@ int cifs_close(struct inode *inode, struct file *file)
pSMBFile->netfid);
}
}
+
+ /* Delete any outstanding lock records.
+ We'll lose them when the file is closed anyway. */
+ down(&pSMBFile->lock_sem);
+ list_for_each_entry_safe(li, tmp, &pSMBFile->llist, llist) {
+ list_del(&li->llist);
+ kfree(li);
+ }
+ up(&pSMBFile->lock_sem);
+
write_lock(&GlobalSMBSeslock);
list_del(&pSMBFile->flist);
list_del(&pSMBFile->tlist);
@@ -570,6 +585,21 @@ int cifs_closedir(struct inode *inode, struct file *file)
return rc;
}
+static int store_file_lock(struct cifsFileInfo *fid, __u64 len,
+ __u64 offset, __u8 lockType)
+{
+ struct cifsLockInfo *li = kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL);
+ if (li == NULL)
+ return -ENOMEM;
+ li->offset = offset;
+ li->length = len;
+ li->type = lockType;
+ down(&fid->lock_sem);
+ list_add(&li->llist, &fid->llist);
+ up(&fid->lock_sem);
+ return 0;
+}
+
int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
{
int rc, xid;
@@ -581,6 +611,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
struct cifsTconInfo *pTcon;
__u16 netfid;
__u8 lockType = LOCKING_ANDX_LARGE_FILES;
+ int posix_locking;
length = 1 + pfLock->fl_end - pfLock->fl_start;
rc = -EACCES;
@@ -639,15 +670,14 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
}
netfid = ((struct cifsFileInfo *)file->private_data)->netfid;
+ posix_locking = (cifs_sb->tcon->ses->capabilities & CAP_UNIX) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability));
/* BB add code here to normalize offset and length to
account for negative length which we can not accept over the
wire */
if (IS_GETLK(cmd)) {
- if(experimEnabled &&
- (cifs_sb->tcon->ses->capabilities & CAP_UNIX) &&
- (CIFS_UNIX_FCNTL_CAP &
- le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability))) {
+ if(posix_locking) {
int posix_lock_type;
if(lockType & LOCKING_ANDX_SHARED_LOCK)
posix_lock_type = CIFS_RDLCK;
@@ -683,10 +713,15 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
FreeXid(xid);
return rc;
}
- if (experimEnabled &&
- (cifs_sb->tcon->ses->capabilities & CAP_UNIX) &&
- (CIFS_UNIX_FCNTL_CAP &
- le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability))) {
+
+ if (!numLock && !numUnlock) {
+ /* if no lock or unlock then nothing
+ to do since we do not know what it is */
+ FreeXid(xid);
+ return -EOPNOTSUPP;
+ }
+
+ if (posix_locking) {
int posix_lock_type;
if(lockType & LOCKING_ANDX_SHARED_LOCK)
posix_lock_type = CIFS_RDLCK;
@@ -695,18 +730,46 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
if(numUnlock == 1)
posix_lock_type = CIFS_UNLCK;
- else if(numLock == 0) {
- /* if no lock or unlock then nothing
- to do since we do not know what it is */
- FreeXid(xid);
- return -EOPNOTSUPP;
- }
+
rc = CIFSSMBPosixLock(xid, pTcon, netfid, 0 /* set */,
length, pfLock,
posix_lock_type, wait_flag);
- } else
- rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start,
- numUnlock, numLock, lockType, wait_flag);
+ } else {
+ struct cifsFileInfo *fid = (struct cifsFileInfo *)file->private_data;
+
+ if (numLock) {
+ rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start,
+ 0, numLock, lockType, wait_flag);
+
+ if (rc == 0) {
+ /* For Windows locks we must store them. */
+ rc = store_file_lock(fid, length,
+ pfLock->fl_start, lockType);
+ }
+ } else if (numUnlock) {
+ /* For each stored lock that this unlock overlaps
+ completely, unlock it. */
+ int stored_rc = 0;
+ struct cifsLockInfo *li, *tmp;
+
+ down(&fid->lock_sem);
+ list_for_each_entry_safe(li, tmp, &fid->llist, llist) {
+ if (pfLock->fl_start <= li->offset &&
+ length >= li->length) {
+ stored_rc = CIFSSMBLock(xid, pTcon, netfid,
+ li->length, li->offset,
+ 1, 0, li->type, FALSE);
+ if (stored_rc)
+ rc = stored_rc;
+
+ list_del(&li->llist);
+ kfree(li);
+ }
+ }
+ up(&fid->lock_sem);
+ }
+ }
+
if (pfLock->fl_flags & FL_POSIX)
posix_lock_file_wait(file, pfLock);
FreeXid(xid);
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index b66eff5dc62..ce87550e918 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -72,6 +72,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
{ERRinvlevel,-EOPNOTSUPP},
{ERRdirnotempty, -ENOTEMPTY},
{ERRnotlocked, -ENOLCK},
+ {ERRcancelviolation, -ENOLCK},
{ERRalreadyexists, -EEXIST},
{ERRmoredata, -EOVERFLOW},
{ERReasnotsupported,-EOPNOTSUPP},
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 03bbcb37791..105761e3ba0 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -556,7 +556,7 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
FIND_FILE_STANDARD_INFO * pFindData =
(FIND_FILE_STANDARD_INFO *)current_entry;
filename = &pFindData->FileName[0];
- len = le32_to_cpu(pFindData->FileNameLength);
+ len = pFindData->FileNameLength;
} else {
cFYI(1,("Unknown findfirst level %d",cfile->srch_inf.info_level));
}
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 7202d534ef0..d1705ab8136 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -372,7 +372,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
/* no capabilities flags in old lanman negotiation */
- pSMB->old_req.PasswordLength = CIFS_SESS_KEY_SIZE;
+ pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE);
/* BB calculate hash with password */
/* and copy into bcc */
diff --git a/fs/cifs/smberr.h b/fs/cifs/smberr.h
index cd41c67ff8d..212c3c29640 100644
--- a/fs/cifs/smberr.h
+++ b/fs/cifs/smberr.h
@@ -95,6 +95,7 @@
#define ERRinvlevel 124
#define ERRdirnotempty 145
#define ERRnotlocked 158
+#define ERRcancelviolation 173
#define ERRalreadyexists 183
#define ERRbadpipe 230
#define ERRpipebusy 231
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 17ba329e2b3..48d47b46b1f 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -3,7 +3,8 @@
*
* Copyright (C) International Business Machines Corp., 2002,2005
* Author(s): Steve French (sfrench@us.ibm.com)
- *
+ * Jeremy Allison (jra@samba.org) 2006.
+ *
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
@@ -36,7 +37,7 @@ extern mempool_t *cifs_mid_poolp;
extern kmem_cache_t *cifs_oplock_cachep;
static struct mid_q_entry *
-AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
+AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
{
struct mid_q_entry *temp;
@@ -203,6 +204,10 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
rc = 0;
}
+ /* Don't want to modify the buffer as a
+ side effect of this call. */
+ smb_buffer->smb_buf_length = smb_buf_length;
+
return rc;
}
@@ -217,6 +222,7 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
unsigned int len = iov[0].iov_len;
unsigned int total_len;
int first_vec = 0;
+ unsigned int smb_buf_length = smb_buffer->smb_buf_length;
if(ssocket == NULL)
return -ENOTSOCK; /* BB eventually add reconnect code here */
@@ -293,36 +299,15 @@ smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
} else
rc = 0;
+ /* Don't want to modify the buffer as a
+ side effect of this call. */
+ smb_buffer->smb_buf_length = smb_buf_length;
+
return rc;
}
-int
-SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
- struct kvec *iov, int n_vec, int * pRespBufType /* ret */,
- const int long_op)
+static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op)
{
- int rc = 0;
- unsigned int receive_len;
- unsigned long timeout;
- struct mid_q_entry *midQ;
- struct smb_hdr *in_buf = iov[0].iov_base;
-
- *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */
-
- if ((ses == NULL) || (ses->server == NULL)) {
- cifs_small_buf_release(in_buf);
- cERROR(1,("Null session"));
- return -EIO;
- }
-
- if(ses->server->tcpStatus == CifsExiting) {
- cifs_small_buf_release(in_buf);
- return -ENOENT;
- }
-
- /* Ensure that we do not send more than 50 overlapping requests
- to the same server. We may make this configurable later or
- use ses->maxReq */
if(long_op == -1) {
/* oplock breaks must not be held up */
atomic_inc(&ses->server->inFlight);
@@ -345,53 +330,140 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
} else {
if(ses->server->tcpStatus == CifsExiting) {
spin_unlock(&GlobalMid_Lock);
- cifs_small_buf_release(in_buf);
return -ENOENT;
}
- /* can not count locking commands against total since
- they are allowed to block on server */
+ /* can not count locking commands against total since
+ they are allowed to block on server */
- if(long_op < 3) {
/* update # of requests on the wire to server */
+ if (long_op < 3)
atomic_inc(&ses->server->inFlight);
- }
spin_unlock(&GlobalMid_Lock);
break;
}
}
}
- /* make sure that we sign in the same order that we send on this socket
- and avoid races inside tcp sendmsg code that could cause corruption
- of smb data */
-
- down(&ses->server->tcpSem);
+ return 0;
+}
+static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf,
+ struct mid_q_entry **ppmidQ)
+{
if (ses->server->tcpStatus == CifsExiting) {
- rc = -ENOENT;
- goto out_unlock2;
+ return -ENOENT;
} else if (ses->server->tcpStatus == CifsNeedReconnect) {
cFYI(1,("tcp session dead - return to caller to retry"));
- rc = -EAGAIN;
- goto out_unlock2;
+ return -EAGAIN;
} else if (ses->status != CifsGood) {
/* check if SMB session is bad because we are setting it up */
if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
(in_buf->Command != SMB_COM_NEGOTIATE)) {
- rc = -EAGAIN;
- goto out_unlock2;
+ return -EAGAIN;
} /* else ok - we are setting up session */
}
- midQ = AllocMidQEntry(in_buf, ses);
- if (midQ == NULL) {
+ *ppmidQ = AllocMidQEntry(in_buf, ses);
+ if (*ppmidQ == NULL) {
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int wait_for_response(struct cifsSesInfo *ses,
+ struct mid_q_entry *midQ,
+ unsigned long timeout,
+ unsigned long time_to_wait)
+{
+ unsigned long curr_timeout;
+
+ for (;;) {
+ curr_timeout = timeout + jiffies;
+ wait_event(ses->server->response_q,
+ (!(midQ->midState == MID_REQUEST_SUBMITTED)) ||
+ time_after(jiffies, curr_timeout) ||
+ ((ses->server->tcpStatus != CifsGood) &&
+ (ses->server->tcpStatus != CifsNew)));
+
+ if (time_after(jiffies, curr_timeout) &&
+ (midQ->midState == MID_REQUEST_SUBMITTED) &&
+ ((ses->server->tcpStatus == CifsGood) ||
+ (ses->server->tcpStatus == CifsNew))) {
+
+ unsigned long lrt;
+
+ /* We timed out. Is the server still
+ sending replies ? */
+ spin_lock(&GlobalMid_Lock);
+ lrt = ses->server->lstrp;
+ spin_unlock(&GlobalMid_Lock);
+
+ /* Calculate time_to_wait past last receive time.
+ Although we prefer not to time out if the
+ server is still responding - we will time
+ out if the server takes more than 15 (or 45
+ or 180) seconds to respond to this request
+ and has not responded to any request from
+ other threads on the client within 10 seconds */
+ lrt += time_to_wait;
+ if (time_after(jiffies, lrt)) {
+ /* No replies for time_to_wait. */
+ cERROR(1,("server not responding"));
+ return -1;
+ }
+ } else {
+ return 0;
+ }
+ }
+}
+
+int
+SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
+ struct kvec *iov, int n_vec, int * pRespBufType /* ret */,
+ const int long_op)
+{
+ int rc = 0;
+ unsigned int receive_len;
+ unsigned long timeout;
+ struct mid_q_entry *midQ;
+ struct smb_hdr *in_buf = iov[0].iov_base;
+
+ *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */
+
+ if ((ses == NULL) || (ses->server == NULL)) {
+ cifs_small_buf_release(in_buf);
+ cERROR(1,("Null session"));
+ return -EIO;
+ }
+
+ if(ses->server->tcpStatus == CifsExiting) {
+ cifs_small_buf_release(in_buf);
+ return -ENOENT;
+ }
+
+ /* Ensure that we do not send more than 50 overlapping requests
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+ rc = wait_for_free_request(ses, long_op);
+ if (rc) {
+ cifs_small_buf_release(in_buf);
+ return rc;
+ }
+
+ /* make sure that we sign in the same order that we send on this socket
+ and avoid races inside tcp sendmsg code that could cause corruption
+ of smb data */
+
+ down(&ses->server->tcpSem);
+
+ rc = allocate_mid(ses, in_buf, &midQ);
+ if (rc) {
up(&ses->server->tcpSem);
cifs_small_buf_release(in_buf);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
- return -ENOMEM;
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
+ return rc;
}
rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number);
@@ -406,32 +478,23 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
atomic_dec(&ses->server->inSend);
midQ->when_sent = jiffies;
#endif
- if(rc < 0) {
- DeleteMidQEntry(midQ);
- up(&ses->server->tcpSem);
- cifs_small_buf_release(in_buf);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
- return rc;
- } else {
- up(&ses->server->tcpSem);
- cifs_small_buf_release(in_buf);
- }
+
+ up(&ses->server->tcpSem);
+ cifs_small_buf_release(in_buf);
+
+ if(rc < 0)
+ goto out;
if (long_op == -1)
- goto cifs_no_response_exit2;
+ goto out;
else if (long_op == 2) /* writes past end of file can take loong time */
timeout = 180 * HZ;
else if (long_op == 1)
timeout = 45 * HZ; /* should be greater than
servers oplock break timeout (about 43 seconds) */
- else if (long_op > 2) {
- timeout = MAX_SCHEDULE_TIMEOUT;
- } else
+ else
timeout = 15 * HZ;
+
/* wait for 15 seconds or until woken up due to response arriving or
due to last connection to this server being unmounted */
if (signal_pending(current)) {
@@ -441,19 +504,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
}
/* No user interrupts in wait - wreaks havoc with performance */
- if(timeout != MAX_SCHEDULE_TIMEOUT) {
- timeout += jiffies;
- wait_event(ses->server->response_q,
- (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
- time_after(jiffies, timeout) ||
- ((ses->server->tcpStatus != CifsGood) &&
- (ses->server->tcpStatus != CifsNew)));
- } else {
- wait_event(ses->server->response_q,
- (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
- ((ses->server->tcpStatus != CifsGood) &&
- (ses->server->tcpStatus != CifsNew)));
- }
+ wait_for_response(ses, midQ, timeout, 10 * HZ);
spin_lock(&GlobalMid_Lock);
if (midQ->resp_buf) {
@@ -481,11 +532,9 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
}
spin_unlock(&GlobalMid_Lock);
DeleteMidQEntry(midQ);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
return rc;
}
@@ -536,24 +585,12 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
cFYI(1,("Bad MID state?"));
}
}
-cifs_no_response_exit2:
- DeleteMidQEntry(midQ);
-
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
- return rc;
+out:
-out_unlock2:
- up(&ses->server->tcpSem);
- cifs_small_buf_release(in_buf);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
+ DeleteMidQEntry(midQ);
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
return rc;
}
@@ -583,85 +620,34 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
/* Ensure that we do not send more than 50 overlapping requests
to the same server. We may make this configurable later or
use ses->maxReq */
- if(long_op == -1) {
- /* oplock breaks must not be held up */
- atomic_inc(&ses->server->inFlight);
- } else {
- spin_lock(&GlobalMid_Lock);
- while(1) {
- if(atomic_read(&ses->server->inFlight) >=
- cifs_max_pending){
- spin_unlock(&GlobalMid_Lock);
-#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&ses->server->num_waiters);
-#endif
- wait_event(ses->server->request_q,
- atomic_read(&ses->server->inFlight)
- < cifs_max_pending);
-#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&ses->server->num_waiters);
-#endif
- spin_lock(&GlobalMid_Lock);
- } else {
- if(ses->server->tcpStatus == CifsExiting) {
- spin_unlock(&GlobalMid_Lock);
- return -ENOENT;
- }
- /* can not count locking commands against total since
- they are allowed to block on server */
-
- if(long_op < 3) {
- /* update # of requests on the wire to server */
- atomic_inc(&ses->server->inFlight);
- }
- spin_unlock(&GlobalMid_Lock);
- break;
- }
- }
- }
+ rc = wait_for_free_request(ses, long_op);
+ if (rc)
+ return rc;
+
/* make sure that we sign in the same order that we send on this socket
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
down(&ses->server->tcpSem);
- if (ses->server->tcpStatus == CifsExiting) {
- rc = -ENOENT;
- goto out_unlock;
- } else if (ses->server->tcpStatus == CifsNeedReconnect) {
- cFYI(1,("tcp session dead - return to caller to retry"));
- rc = -EAGAIN;
- goto out_unlock;
- } else if (ses->status != CifsGood) {
- /* check if SMB session is bad because we are setting it up */
- if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
- (in_buf->Command != SMB_COM_NEGOTIATE)) {
- rc = -EAGAIN;
- goto out_unlock;
- } /* else ok - we are setting up session */
- }
- midQ = AllocMidQEntry(in_buf, ses);
- if (midQ == NULL) {
+ rc = allocate_mid(ses, in_buf, &midQ);
+ if (rc) {
up(&ses->server->tcpSem);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
- return -ENOMEM;
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
+ return rc;
}
if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
- up(&ses->server->tcpSem);
cERROR(1, ("Illegal length, greater than maximum frame, %d",
in_buf->smb_buf_length));
DeleteMidQEntry(midQ);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
+ up(&ses->server->tcpSem);
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
return -EIO;
}
@@ -677,27 +663,19 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
atomic_dec(&ses->server->inSend);
midQ->when_sent = jiffies;
#endif
- if(rc < 0) {
- DeleteMidQEntry(midQ);
- up(&ses->server->tcpSem);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
- return rc;
- } else
- up(&ses->server->tcpSem);
+ up(&ses->server->tcpSem);
+
+ if(rc < 0)
+ goto out;
+
if (long_op == -1)
- goto cifs_no_response_exit;
+ goto out;
else if (long_op == 2) /* writes past end of file can take loong time */
timeout = 180 * HZ;
else if (long_op == 1)
timeout = 45 * HZ; /* should be greater than
servers oplock break timeout (about 43 seconds) */
- else if (long_op > 2) {
- timeout = MAX_SCHEDULE_TIMEOUT;
- } else
+ else
timeout = 15 * HZ;
/* wait for 15 seconds or until woken up due to response arriving or
due to last connection to this server being unmounted */
@@ -708,19 +686,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
}
/* No user interrupts in wait - wreaks havoc with performance */
- if(timeout != MAX_SCHEDULE_TIMEOUT) {
- timeout += jiffies;
- wait_event(ses->server->response_q,
- (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
- time_after(jiffies, timeout) ||
- ((ses->server->tcpStatus != CifsGood) &&
- (ses->server->tcpStatus != CifsNew)));
- } else {
- wait_event(ses->server->response_q,
- (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
- ((ses->server->tcpStatus != CifsGood) &&
- (ses->server->tcpStatus != CifsNew)));
- }
+ wait_for_response(ses, midQ, timeout, 10 * HZ);
spin_lock(&GlobalMid_Lock);
if (midQ->resp_buf) {
@@ -748,11 +714,9 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
}
spin_unlock(&GlobalMid_Lock);
DeleteMidQEntry(midQ);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
+ /* Update # of requests on wire to server */
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
return rc;
}
@@ -799,23 +763,253 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
cERROR(1,("Bad MID state?"));
}
}
-cifs_no_response_exit:
+
+out:
+
DeleteMidQEntry(midQ);
+ atomic_dec(&ses->server->inFlight);
+ wake_up(&ses->server->request_q);
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- }
+ return rc;
+}
+
+/* Send an NT_CANCEL SMB to cause the POSIX blocking lock to return. */
+
+static int
+send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf,
+ struct mid_q_entry *midQ)
+{
+ int rc = 0;
+ struct cifsSesInfo *ses = tcon->ses;
+ __u16 mid = in_buf->Mid;
+ header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0);
+ in_buf->Mid = mid;
+ down(&ses->server->tcpSem);
+ rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+ if (rc) {
+ up(&ses->server->tcpSem);
+ return rc;
+ }
+ rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
+ (struct sockaddr *) &(ses->server->addr.sockAddr));
+ up(&ses->server->tcpSem);
return rc;
+}
+
+/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
+ blocking lock to return. */
+
+static int
+send_lock_cancel(const unsigned int xid, struct cifsTconInfo *tcon,
+ struct smb_hdr *in_buf,
+ struct smb_hdr *out_buf)
+{
+ int bytes_returned;
+ struct cifsSesInfo *ses = tcon->ses;
+ LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
+
+ /* We just modify the current in_buf to change
+ the type of lock from LOCKING_ANDX_SHARED_LOCK
+ or LOCKING_ANDX_EXCLUSIVE_LOCK to
+ LOCKING_ANDX_CANCEL_LOCK. */
+
+ pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
+ pSMB->Timeout = 0;
+ pSMB->hdr.Mid = GetNextMid(ses->server);
+
+ return SendReceive(xid, ses, in_buf, out_buf,
+ &bytes_returned, 0);
+}
-out_unlock:
+int
+SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
+ struct smb_hdr *in_buf, struct smb_hdr *out_buf,
+ int *pbytes_returned)
+{
+ int rc = 0;
+ int rstart = 0;
+ unsigned int receive_len;
+ struct mid_q_entry *midQ;
+ struct cifsSesInfo *ses;
+
+ if (tcon == NULL || tcon->ses == NULL) {
+ cERROR(1,("Null smb session"));
+ return -EIO;
+ }
+ ses = tcon->ses;
+
+ if(ses->server == NULL) {
+ cERROR(1,("Null tcp session"));
+ return -EIO;
+ }
+
+ if(ses->server->tcpStatus == CifsExiting)
+ return -ENOENT;
+
+ /* Ensure that we do not send more than 50 overlapping requests
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+ rc = wait_for_free_request(ses, 3);
+ if (rc)
+ return rc;
+
+ /* make sure that we sign in the same order that we send on this socket
+ and avoid races inside tcp sendmsg code that could cause corruption
+ of smb data */
+
+ down(&ses->server->tcpSem);
+
+ rc = allocate_mid(ses, in_buf, &midQ);
+ if (rc) {
+ up(&ses->server->tcpSem);
+ return rc;
+ }
+
+ if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ up(&ses->server->tcpSem);
+ cERROR(1, ("Illegal length, greater than maximum frame, %d",
+ in_buf->smb_buf_length));
+ DeleteMidQEntry(midQ);
+ return -EIO;
+ }
+
+ rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+
+ midQ->midState = MID_REQUEST_SUBMITTED;
+#ifdef CONFIG_CIFS_STATS2
+ atomic_inc(&ses->server->inSend);
+#endif
+ rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
+ (struct sockaddr *) &(ses->server->addr.sockAddr));
+#ifdef CONFIG_CIFS_STATS2
+ atomic_dec(&ses->server->inSend);
+ midQ->when_sent = jiffies;
+#endif
up(&ses->server->tcpSem);
- /* If not lock req, update # of requests on wire to server */
- if(long_op < 3) {
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
+
+ if(rc < 0) {
+ DeleteMidQEntry(midQ);
+ return rc;
+ }
+
+ /* Wait for a reply - allow signals to interrupt. */
+ rc = wait_event_interruptible(ses->server->response_q,
+ (!(midQ->midState == MID_REQUEST_SUBMITTED)) ||
+ ((ses->server->tcpStatus != CifsGood) &&
+ (ses->server->tcpStatus != CifsNew)));
+
+ /* Were we interrupted by a signal ? */
+ if ((rc == -ERESTARTSYS) &&
+ (midQ->midState == MID_REQUEST_SUBMITTED) &&
+ ((ses->server->tcpStatus == CifsGood) ||
+ (ses->server->tcpStatus == CifsNew))) {
+
+ if (in_buf->Command == SMB_COM_TRANSACTION2) {
+ /* POSIX lock. We send a NT_CANCEL SMB to cause the
+ blocking lock to return. */
+
+ rc = send_nt_cancel(tcon, in_buf, midQ);
+ if (rc) {
+ DeleteMidQEntry(midQ);
+ return rc;
+ }
+ } else {
+ /* Windows lock. We send a LOCKINGX_CANCEL_LOCK
+ to cause the blocking lock to return. */
+
+ rc = send_lock_cancel(xid, tcon, in_buf, out_buf);
+
+ /* If we get -ENOLCK back the lock may have
+ already been removed. Don't exit in this case. */
+ if (rc && rc != -ENOLCK) {
+ DeleteMidQEntry(midQ);
+ return rc;
+ }
+ }
+
+ /* Wait 5 seconds for the response. */
+ if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ)==0) {
+ /* We got the response - restart system call. */
+ rstart = 1;
+ }
+ }
+
+ spin_lock(&GlobalMid_Lock);
+ if (midQ->resp_buf) {
+ spin_unlock(&GlobalMid_Lock);
+ receive_len = midQ->resp_buf->smb_buf_length;
+ } else {
+ cERROR(1,("No response for cmd %d mid %d",
+ midQ->command, midQ->mid));
+ if(midQ->midState == MID_REQUEST_SUBMITTED) {
+ if(ses->server->tcpStatus == CifsExiting)
+ rc = -EHOSTDOWN;
+ else {
+ ses->server->tcpStatus = CifsNeedReconnect;
+ midQ->midState = MID_RETRY_NEEDED;
+ }
+ }
+
+ if (rc != -EHOSTDOWN) {
+ if(midQ->midState == MID_RETRY_NEEDED) {
+ rc = -EAGAIN;
+ cFYI(1,("marking request for retry"));
+ } else {
+ rc = -EIO;
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ DeleteMidQEntry(midQ);
+ return rc;
}
+
+ if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cERROR(1, ("Frame too large received. Length: %d Xid: %d",
+ receive_len, xid));
+ rc = -EIO;
+ } else { /* rcvd frame is ok */
+
+ if (midQ->resp_buf && out_buf
+ && (midQ->midState == MID_RESPONSE_RECEIVED)) {
+ out_buf->smb_buf_length = receive_len;
+ memcpy((char *)out_buf + 4,
+ (char *)midQ->resp_buf + 4,
+ receive_len);
+
+ dump_smb(out_buf, 92);
+ /* convert the length into a more usable form */
+ if((receive_len > 24) &&
+ (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+ SECMODE_SIGN_ENABLED))) {
+ rc = cifs_verify_signature(out_buf,
+ ses->server->mac_signing_key,
+ midQ->sequence_number+1);
+ if(rc) {
+ cERROR(1,("Unexpected SMB signature"));
+ /* BB FIXME add code to kill session */
+ }
+ }
+
+ *pbytes_returned = out_buf->smb_buf_length;
+
+ /* BB special case reconnect tid and uid here? */
+ rc = map_smb_to_linux_error(out_buf);
+ /* convert ByteCount if necessary */
+ 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));
+ } else {
+ rc = -EIO;
+ cERROR(1,("Bad MID state?"));
+ }
+ }
+ DeleteMidQEntry(midQ);
+ if (rstart && rc == -EACCES)
+ return -ERESTARTSYS;
return rc;
}
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
index 7754d641775..067648b7179 100644
--- a/fs/cifs/xattr.c
+++ b/fs/cifs/xattr.c
@@ -330,11 +330,15 @@ ssize_t cifs_listxattr(struct dentry * direntry, char * data, size_t buf_size)
sb = direntry->d_inode->i_sb;
if(sb == NULL)
return -EIO;
- xid = GetXid();
cifs_sb = CIFS_SB(sb);
pTcon = cifs_sb->tcon;
+ if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
+ return -EOPNOTSUPP;
+
+ xid = GetXid();
+
full_path = build_path_from_dentry(direntry);
if(full_path == NULL) {
FreeXid(xid);