Merge branch 'for-linus' of git://git.samba.org/sfrench/cifs-2.6
Pull CIFS updates from Steve French: "This patchset is the final section of the SMB2.1 support merge for cifs.ko. It also includes improvements to the cifs socket handling from Jeff, and also fixes a few cifs bug fixes. It adds SMB2 support for file and inode operations as well as moves some existing cifs code to use ops server struct of protocol specific callbacks. Most of this code is SMB2 specific. When enabled SMB2.1 does pass various functional tests including most of the connectathon test suite, For SMB2.1, Connectathon test 4 and some related tests fail due to not updating mode bits remotely (cifsacl support where mode bits are approximated with the cifs acl is not enable for smb2), and test8 (symlink) support is not completed for SMB2 yet (note that we will likely have a "Unix Extensions" eventually, at least for Samba, so in the long run posix locks won't have to be emulated when mounting Linux to Linux, but for most NAS and for Windows mounts posix lock emulation will still used for SMB2 in a similar fashion as we do for cifs). SMB2.1 dialect is supported. Although additional fixes to enable smb2 (the original smb2.02) dialect and to add various optional features of the smb3 dialect are expected to be added in the future as testing progresses, currently mounting with the "vers=2.1" is supported (in order to mount using SMB2.1 to servers like Samba 4, and Windows 7, Windows 2008R2)." * 'for-linus' of git://git.samba.org/sfrench/cifs-2.6: (82 commits) [CIFS] Fix indentation of fs/cifs/Kconfig entries [CIFS] Fix SMB2 negotiation support to select only one dialect (based on vers=) cifs: obtain file access during backup intent lookup (resend) CIFS: Fix possible freed pointer dereference in CIFS_SessSetup CIFS: Fix possible freed pointer dereference in SMB2_sess_setup CIFS: Make ops->close return void cifs: change DOS/NT/POSIX mapping of ERRnoresource cifs: remove support for deprecated "forcedirectio" and "strictcache" mount options cifs: remove support for CIFS_IOC_CHECKUMOUNT ioctl CIFS: Fix possible memory leaks in SMB2 code CIFS: Fix endian conversion of IndexNumber Trivial endian fixes MARK SMB2 support EXPERIMENTAL Update cifs version number cifs: add FL_CLOSE to fl_flags mask in cifs_read_flock cifs: Mangle string used for unc in /proc/mounts cifs: cleanups for cifs_mkdir_qinfo CIFS: Fix fast lease break after open problem CIFS: Add SMB2.1 lease break support CIFS: Fix cache coherency for read oplock case ...
This commit is contained in:
commit
797b9e5ae9
|
@ -9,13 +9,14 @@ config CIFS
|
||||||
select CRYPTO_ARC4
|
select CRYPTO_ARC4
|
||||||
select CRYPTO_ECB
|
select CRYPTO_ECB
|
||||||
select CRYPTO_DES
|
select CRYPTO_DES
|
||||||
|
select CRYPTO_SHA256
|
||||||
help
|
help
|
||||||
This is the client VFS module for the Common Internet File System
|
This is the client VFS module for the Common Internet File System
|
||||||
(CIFS) protocol which is the successor to the Server Message Block
|
(CIFS) protocol which is the successor to the Server Message Block
|
||||||
(SMB) protocol, the native file sharing mechanism for most early
|
(SMB) protocol, the native file sharing mechanism for most early
|
||||||
PC operating systems. The CIFS protocol is fully supported by
|
PC operating systems. The CIFS protocol is fully supported by
|
||||||
file servers such as Windows 2000 (including Windows 2003, NT 4
|
file servers such as Windows 2000 (including Windows 2003, Windows 2008,
|
||||||
and Windows XP) as well by Samba (which provides excellent CIFS
|
NT 4 and Windows XP) as well by Samba (which provides excellent CIFS
|
||||||
server support for Linux and many other operating systems). Limited
|
server support for Linux and many other operating systems). Limited
|
||||||
support for OS/2 and Windows ME and similar servers is provided as
|
support for OS/2 and Windows ME and similar servers is provided as
|
||||||
well.
|
well.
|
||||||
|
@ -114,6 +115,13 @@ config CIFS_POSIX
|
||||||
(such as Samba 3.10 and later) which can negotiate
|
(such as Samba 3.10 and later) which can negotiate
|
||||||
CIFS POSIX ACL support. If unsure, say N.
|
CIFS POSIX ACL support. If unsure, say N.
|
||||||
|
|
||||||
|
config CIFS_ACL
|
||||||
|
bool "Provide CIFS ACL support"
|
||||||
|
depends on CIFS_XATTR && KEYS
|
||||||
|
help
|
||||||
|
Allows fetching CIFS/NTFS ACL from the server. The DACL blob
|
||||||
|
is handed over to the application/caller.
|
||||||
|
|
||||||
config CIFS_DEBUG2
|
config CIFS_DEBUG2
|
||||||
bool "Enable additional CIFS debugging routines"
|
bool "Enable additional CIFS debugging routines"
|
||||||
depends on CIFS
|
depends on CIFS
|
||||||
|
@ -138,21 +146,6 @@ config CIFS_DFS_UPCALL
|
||||||
IP addresses) which is needed for implicit mounts of DFS junction
|
IP addresses) which is needed for implicit mounts of DFS junction
|
||||||
points. If unsure, say N.
|
points. If unsure, say N.
|
||||||
|
|
||||||
config CIFS_FSCACHE
|
|
||||||
bool "Provide CIFS client caching support"
|
|
||||||
depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y
|
|
||||||
help
|
|
||||||
Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
|
|
||||||
to be cached locally on disk through the general filesystem cache
|
|
||||||
manager. If unsure, say N.
|
|
||||||
|
|
||||||
config CIFS_ACL
|
|
||||||
bool "Provide CIFS ACL support"
|
|
||||||
depends on CIFS_XATTR && KEYS
|
|
||||||
help
|
|
||||||
Allows to fetch CIFS/NTFS ACL from the server. The DACL blob
|
|
||||||
is handed over to the application/caller.
|
|
||||||
|
|
||||||
config CIFS_NFSD_EXPORT
|
config CIFS_NFSD_EXPORT
|
||||||
bool "Allow nfsd to export CIFS file system (EXPERIMENTAL)"
|
bool "Allow nfsd to export CIFS file system (EXPERIMENTAL)"
|
||||||
depends on CIFS && EXPERIMENTAL && BROKEN
|
depends on CIFS && EXPERIMENTAL && BROKEN
|
||||||
|
@ -161,7 +154,7 @@ config CIFS_NFSD_EXPORT
|
||||||
|
|
||||||
config CIFS_SMB2
|
config CIFS_SMB2
|
||||||
bool "SMB2 network file system support (EXPERIMENTAL)"
|
bool "SMB2 network file system support (EXPERIMENTAL)"
|
||||||
depends on EXPERIMENTAL && INET && BROKEN
|
depends on CIFS && EXPERIMENTAL && INET
|
||||||
select NLS
|
select NLS
|
||||||
select KEYS
|
select KEYS
|
||||||
select FSCACHE
|
select FSCACHE
|
||||||
|
@ -178,3 +171,12 @@ config CIFS_SMB2
|
||||||
(compared to cifs) due to protocol improvements.
|
(compared to cifs) due to protocol improvements.
|
||||||
|
|
||||||
Unless you are a developer or tester, say N.
|
Unless you are a developer or tester, say N.
|
||||||
|
|
||||||
|
config CIFS_FSCACHE
|
||||||
|
bool "Provide CIFS client caching support"
|
||||||
|
depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y
|
||||||
|
help
|
||||||
|
Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
|
||||||
|
to be cached locally on disk through the general filesystem cache
|
||||||
|
manager. If unsure, say N.
|
||||||
|
|
||||||
|
|
|
@ -17,4 +17,4 @@ cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o
|
||||||
cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
|
cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
|
||||||
|
|
||||||
cifs-$(CONFIG_CIFS_SMB2) += smb2ops.o smb2maperror.o smb2transport.o \
|
cifs-$(CONFIG_CIFS_SMB2) += smb2ops.o smb2maperror.o smb2transport.o \
|
||||||
smb2misc.o smb2pdu.o smb2inode.o
|
smb2misc.o smb2pdu.o smb2inode.o smb2file.o
|
||||||
|
|
|
@ -1222,7 +1222,7 @@ struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
|
||||||
if (!open_file)
|
if (!open_file)
|
||||||
return get_cifs_acl_by_path(cifs_sb, path, pacllen);
|
return get_cifs_acl_by_path(cifs_sb, path, pacllen);
|
||||||
|
|
||||||
pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->netfid, pacllen);
|
pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->fid.netfid, pacllen);
|
||||||
cifsFileInfo_put(open_file);
|
cifsFileInfo_put(open_file);
|
||||||
return pntsd;
|
return pntsd;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "ntlmssp.h"
|
#include "ntlmssp.h"
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
|
#include <linux/highmem.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate and return the CIFS signature based on the mac key and SMB PDU.
|
* Calculate and return the CIFS signature based on the mac key and SMB PDU.
|
||||||
|
@ -37,11 +38,13 @@
|
||||||
* the sequence number before this function is called. Also, this function
|
* the sequence number before this function is called. Also, this function
|
||||||
* should be called with the server->srv_mutex held.
|
* should be called with the server->srv_mutex held.
|
||||||
*/
|
*/
|
||||||
static int cifs_calc_signature(const struct kvec *iov, int n_vec,
|
static int cifs_calc_signature(struct smb_rqst *rqst,
|
||||||
struct TCP_Server_Info *server, char *signature)
|
struct TCP_Server_Info *server, char *signature)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int rc;
|
int rc;
|
||||||
|
struct kvec *iov = rqst->rq_iov;
|
||||||
|
int n_vec = rqst->rq_nvec;
|
||||||
|
|
||||||
if (iov == NULL || signature == NULL || server == NULL)
|
if (iov == NULL || signature == NULL || server == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -91,6 +94,16 @@ static int cifs_calc_signature(const struct kvec *iov, int n_vec,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* now hash over the rq_pages array */
|
||||||
|
for (i = 0; i < rqst->rq_npages; i++) {
|
||||||
|
struct kvec p_iov;
|
||||||
|
|
||||||
|
cifs_rqst_page_to_kvec(rqst, i, &p_iov);
|
||||||
|
crypto_shash_update(&server->secmech.sdescmd5->shash,
|
||||||
|
p_iov.iov_base, p_iov.iov_len);
|
||||||
|
kunmap(rqst->rq_pages[i]);
|
||||||
|
}
|
||||||
|
|
||||||
rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature);
|
rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature);
|
||||||
if (rc)
|
if (rc)
|
||||||
cERROR(1, "%s: Could not generate md5 hash", __func__);
|
cERROR(1, "%s: Could not generate md5 hash", __func__);
|
||||||
|
@ -99,12 +112,12 @@ static int cifs_calc_signature(const struct kvec *iov, int n_vec,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* must be called with server->srv_mutex held */
|
/* must be called with server->srv_mutex held */
|
||||||
int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
|
int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
|
||||||
__u32 *pexpected_response_sequence_number)
|
__u32 *pexpected_response_sequence_number)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
char smb_signature[20];
|
char smb_signature[20];
|
||||||
struct smb_hdr *cifs_pdu = (struct smb_hdr *)iov[0].iov_base;
|
struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
|
||||||
|
|
||||||
if ((cifs_pdu == NULL) || (server == NULL))
|
if ((cifs_pdu == NULL) || (server == NULL))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -125,7 +138,7 @@ int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
|
||||||
*pexpected_response_sequence_number = server->sequence_number++;
|
*pexpected_response_sequence_number = server->sequence_number++;
|
||||||
server->sequence_number++;
|
server->sequence_number++;
|
||||||
|
|
||||||
rc = cifs_calc_signature(iov, n_vec, server, smb_signature);
|
rc = cifs_calc_signature(rqst, server, smb_signature);
|
||||||
if (rc)
|
if (rc)
|
||||||
memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
|
memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
|
||||||
else
|
else
|
||||||
|
@ -134,6 +147,15 @@ int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
|
||||||
|
__u32 *pexpected_response_sequence)
|
||||||
|
{
|
||||||
|
struct smb_rqst rqst = { .rq_iov = iov,
|
||||||
|
.rq_nvec = n_vec };
|
||||||
|
|
||||||
|
return cifs_sign_rqst(&rqst, server, pexpected_response_sequence);
|
||||||
|
}
|
||||||
|
|
||||||
/* must be called with server->srv_mutex held */
|
/* must be called with server->srv_mutex held */
|
||||||
int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
|
int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
|
||||||
__u32 *pexpected_response_sequence_number)
|
__u32 *pexpected_response_sequence_number)
|
||||||
|
@ -147,14 +169,14 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
|
||||||
pexpected_response_sequence_number);
|
pexpected_response_sequence_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cifs_verify_signature(struct kvec *iov, unsigned int nr_iov,
|
int cifs_verify_signature(struct smb_rqst *rqst,
|
||||||
struct TCP_Server_Info *server,
|
struct TCP_Server_Info *server,
|
||||||
__u32 expected_sequence_number)
|
__u32 expected_sequence_number)
|
||||||
{
|
{
|
||||||
unsigned int rc;
|
unsigned int rc;
|
||||||
char server_response_sig[8];
|
char server_response_sig[8];
|
||||||
char what_we_think_sig_should_be[20];
|
char what_we_think_sig_should_be[20];
|
||||||
struct smb_hdr *cifs_pdu = (struct smb_hdr *)iov[0].iov_base;
|
struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
|
||||||
|
|
||||||
if (cifs_pdu == NULL || server == NULL)
|
if (cifs_pdu == NULL || server == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -186,8 +208,7 @@ int cifs_verify_signature(struct kvec *iov, unsigned int nr_iov,
|
||||||
cifs_pdu->Signature.Sequence.Reserved = 0;
|
cifs_pdu->Signature.Sequence.Reserved = 0;
|
||||||
|
|
||||||
mutex_lock(&server->srv_mutex);
|
mutex_lock(&server->srv_mutex);
|
||||||
rc = cifs_calc_signature(iov, nr_iov, server,
|
rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be);
|
||||||
what_we_think_sig_should_be);
|
|
||||||
mutex_unlock(&server->srv_mutex);
|
mutex_unlock(&server->srv_mutex);
|
||||||
|
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -686,12 +707,17 @@ calc_seckey(struct cifs_ses *ses)
|
||||||
void
|
void
|
||||||
cifs_crypto_shash_release(struct TCP_Server_Info *server)
|
cifs_crypto_shash_release(struct TCP_Server_Info *server)
|
||||||
{
|
{
|
||||||
|
if (server->secmech.hmacsha256)
|
||||||
|
crypto_free_shash(server->secmech.hmacsha256);
|
||||||
|
|
||||||
if (server->secmech.md5)
|
if (server->secmech.md5)
|
||||||
crypto_free_shash(server->secmech.md5);
|
crypto_free_shash(server->secmech.md5);
|
||||||
|
|
||||||
if (server->secmech.hmacmd5)
|
if (server->secmech.hmacmd5)
|
||||||
crypto_free_shash(server->secmech.hmacmd5);
|
crypto_free_shash(server->secmech.hmacmd5);
|
||||||
|
|
||||||
|
kfree(server->secmech.sdeschmacsha256);
|
||||||
|
|
||||||
kfree(server->secmech.sdeschmacmd5);
|
kfree(server->secmech.sdeschmacmd5);
|
||||||
|
|
||||||
kfree(server->secmech.sdescmd5);
|
kfree(server->secmech.sdescmd5);
|
||||||
|
@ -716,6 +742,13 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
|
||||||
goto crypto_allocate_md5_fail;
|
goto crypto_allocate_md5_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0);
|
||||||
|
if (IS_ERR(server->secmech.hmacsha256)) {
|
||||||
|
cERROR(1, "could not allocate crypto hmacsha256\n");
|
||||||
|
rc = PTR_ERR(server->secmech.hmacsha256);
|
||||||
|
goto crypto_allocate_hmacsha256_fail;
|
||||||
|
}
|
||||||
|
|
||||||
size = sizeof(struct shash_desc) +
|
size = sizeof(struct shash_desc) +
|
||||||
crypto_shash_descsize(server->secmech.hmacmd5);
|
crypto_shash_descsize(server->secmech.hmacmd5);
|
||||||
server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
|
server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
|
||||||
|
@ -727,7 +760,6 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
|
||||||
server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
|
server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
|
||||||
server->secmech.sdeschmacmd5->shash.flags = 0x0;
|
server->secmech.sdeschmacmd5->shash.flags = 0x0;
|
||||||
|
|
||||||
|
|
||||||
size = sizeof(struct shash_desc) +
|
size = sizeof(struct shash_desc) +
|
||||||
crypto_shash_descsize(server->secmech.md5);
|
crypto_shash_descsize(server->secmech.md5);
|
||||||
server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
|
server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
|
||||||
|
@ -739,12 +771,29 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
|
||||||
server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
|
server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
|
||||||
server->secmech.sdescmd5->shash.flags = 0x0;
|
server->secmech.sdescmd5->shash.flags = 0x0;
|
||||||
|
|
||||||
|
size = sizeof(struct shash_desc) +
|
||||||
|
crypto_shash_descsize(server->secmech.hmacsha256);
|
||||||
|
server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL);
|
||||||
|
if (!server->secmech.sdeschmacsha256) {
|
||||||
|
cERROR(1, "%s: Can't alloc hmacsha256\n", __func__);
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto crypto_allocate_hmacsha256_sdesc_fail;
|
||||||
|
}
|
||||||
|
server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
|
||||||
|
server->secmech.sdeschmacsha256->shash.flags = 0x0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
crypto_allocate_hmacsha256_sdesc_fail:
|
||||||
|
kfree(server->secmech.sdescmd5);
|
||||||
|
|
||||||
crypto_allocate_md5_sdesc_fail:
|
crypto_allocate_md5_sdesc_fail:
|
||||||
kfree(server->secmech.sdeschmacmd5);
|
kfree(server->secmech.sdeschmacmd5);
|
||||||
|
|
||||||
crypto_allocate_hmacmd5_sdesc_fail:
|
crypto_allocate_hmacmd5_sdesc_fail:
|
||||||
|
crypto_free_shash(server->secmech.hmacsha256);
|
||||||
|
|
||||||
|
crypto_allocate_hmacsha256_fail:
|
||||||
crypto_free_shash(server->secmech.md5);
|
crypto_free_shash(server->secmech.md5);
|
||||||
|
|
||||||
crypto_allocate_md5_fail:
|
crypto_allocate_md5_fail:
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
#include <linux/freezer.h>
|
#include <linux/freezer.h>
|
||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
|
#include <linux/random.h>
|
||||||
#include <net/ipv6.h>
|
#include <net/ipv6.h>
|
||||||
#include "cifsfs.h"
|
#include "cifsfs.h"
|
||||||
#include "cifspdu.h"
|
#include "cifspdu.h"
|
||||||
|
@ -51,7 +52,6 @@
|
||||||
#ifdef CONFIG_CIFS_SMB2
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
#include "smb2pdu.h"
|
#include "smb2pdu.h"
|
||||||
#endif
|
#endif
|
||||||
#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
|
|
||||||
|
|
||||||
int cifsFYI = 0;
|
int cifsFYI = 0;
|
||||||
int cifsERROR = 1;
|
int cifsERROR = 1;
|
||||||
|
@ -89,6 +89,10 @@ extern mempool_t *cifs_mid_poolp;
|
||||||
|
|
||||||
struct workqueue_struct *cifsiod_wq;
|
struct workqueue_struct *cifsiod_wq;
|
||||||
|
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
|
__u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
|
||||||
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cifs_read_super(struct super_block *sb)
|
cifs_read_super(struct super_block *sb)
|
||||||
{
|
{
|
||||||
|
@ -160,13 +164,12 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||||
struct super_block *sb = dentry->d_sb;
|
struct super_block *sb = dentry->d_sb;
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||||
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||||
int rc = -EOPNOTSUPP;
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
xid = get_xid();
|
xid = get_xid();
|
||||||
|
|
||||||
buf->f_type = CIFS_MAGIC_NUMBER;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PATH_MAX may be too long - it would presumably be total path,
|
* PATH_MAX may be too long - it would presumably be total path,
|
||||||
* but note that some servers (includinng Samba 3) have a shorter
|
* but note that some servers (includinng Samba 3) have a shorter
|
||||||
|
@ -178,27 +181,8 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||||
buf->f_files = 0; /* undefined */
|
buf->f_files = 0; /* undefined */
|
||||||
buf->f_ffree = 0; /* unlimited */
|
buf->f_ffree = 0; /* unlimited */
|
||||||
|
|
||||||
/*
|
if (server->ops->queryfs)
|
||||||
* We could add a second check for a QFS Unix capability bit
|
rc = server->ops->queryfs(xid, tcon, buf);
|
||||||
*/
|
|
||||||
if ((tcon->ses->capabilities & CAP_UNIX) &&
|
|
||||||
(CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability)))
|
|
||||||
rc = CIFSSMBQFSPosixInfo(xid, tcon, buf);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only need to call the old QFSInfo if failed on newer one,
|
|
||||||
* e.g. by OS/2.
|
|
||||||
**/
|
|
||||||
if (rc && (tcon->ses->capabilities & CAP_NT_SMBS))
|
|
||||||
rc = CIFSSMBQFSInfo(xid, tcon, buf);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Some old Windows servers also do not support level 103, retry with
|
|
||||||
* older level one if old server failed the previous call or we
|
|
||||||
* bypassed it because we detected that this was an older LANMAN sess
|
|
||||||
*/
|
|
||||||
if (rc)
|
|
||||||
rc = SMBOldQFSInfo(xid, tcon, buf);
|
|
||||||
|
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -239,9 +223,10 @@ cifs_alloc_inode(struct super_block *sb)
|
||||||
return NULL;
|
return NULL;
|
||||||
cifs_inode->cifsAttrs = 0x20; /* default */
|
cifs_inode->cifsAttrs = 0x20; /* default */
|
||||||
cifs_inode->time = 0;
|
cifs_inode->time = 0;
|
||||||
/* Until the file is open and we have gotten oplock
|
/*
|
||||||
info back from the server, can not assume caching of
|
* Until the file is open and we have gotten oplock info back from the
|
||||||
file data or metadata */
|
* server, can not assume caching of file data or metadata.
|
||||||
|
*/
|
||||||
cifs_set_oplock_level(cifs_inode, 0);
|
cifs_set_oplock_level(cifs_inode, 0);
|
||||||
cifs_inode->delete_pending = false;
|
cifs_inode->delete_pending = false;
|
||||||
cifs_inode->invalid_mapping = false;
|
cifs_inode->invalid_mapping = false;
|
||||||
|
@ -249,11 +234,16 @@ cifs_alloc_inode(struct super_block *sb)
|
||||||
cifs_inode->server_eof = 0;
|
cifs_inode->server_eof = 0;
|
||||||
cifs_inode->uniqueid = 0;
|
cifs_inode->uniqueid = 0;
|
||||||
cifs_inode->createtime = 0;
|
cifs_inode->createtime = 0;
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
/* Can not set i_flags here - they get immediately overwritten
|
get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
to zero by the VFS */
|
#endif
|
||||||
/* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME;*/
|
/*
|
||||||
|
* Can not set i_flags here - they get immediately overwritten to zero
|
||||||
|
* by the VFS.
|
||||||
|
*/
|
||||||
|
/* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME; */
|
||||||
INIT_LIST_HEAD(&cifs_inode->openFileList);
|
INIT_LIST_HEAD(&cifs_inode->openFileList);
|
||||||
|
INIT_LIST_HEAD(&cifs_inode->llist);
|
||||||
return &cifs_inode->vfs_inode;
|
return &cifs_inode->vfs_inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +350,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||||
cifs_show_security(s, tcon->ses->server);
|
cifs_show_security(s, tcon->ses->server);
|
||||||
cifs_show_cache_flavor(s, cifs_sb);
|
cifs_show_cache_flavor(s, cifs_sb);
|
||||||
|
|
||||||
seq_printf(s, ",unc=%s", tcon->treeName);
|
seq_printf(s, ",unc=");
|
||||||
|
seq_escape(s, tcon->treeName, " \t\n\\");
|
||||||
|
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)
|
||||||
seq_printf(s, ",multiuser");
|
seq_printf(s, ",multiuser");
|
||||||
|
@ -957,7 +948,7 @@ cifs_init_once(void *inode)
|
||||||
struct cifsInodeInfo *cifsi = inode;
|
struct cifsInodeInfo *cifsi = inode;
|
||||||
|
|
||||||
inode_init_once(&cifsi->vfs_inode);
|
inode_init_once(&cifsi->vfs_inode);
|
||||||
mutex_init(&cifsi->lock_mutex);
|
init_rwsem(&cifsi->lock_sem);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1127,6 +1118,10 @@ init_cifs(void)
|
||||||
spin_lock_init(&cifs_file_list_lock);
|
spin_lock_init(&cifs_file_list_lock);
|
||||||
spin_lock_init(&GlobalMid_Lock);
|
spin_lock_init(&GlobalMid_Lock);
|
||||||
|
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
|
get_random_bytes(cifs_client_guid, SMB2_CLIENT_GUID_SIZE);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (cifs_max_pending < 2) {
|
if (cifs_max_pending < 2) {
|
||||||
cifs_max_pending = 2;
|
cifs_max_pending = 2;
|
||||||
cFYI(1, "cifs_max_pending set to min of 2");
|
cFYI(1, "cifs_max_pending set to min of 2");
|
||||||
|
|
|
@ -128,5 +128,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
|
||||||
extern const struct export_operations cifs_export_ops;
|
extern const struct export_operations cifs_export_ops;
|
||||||
#endif /* CONFIG_CIFS_NFSD_EXPORT */
|
#endif /* CONFIG_CIFS_NFSD_EXPORT */
|
||||||
|
|
||||||
#define CIFS_VERSION "1.78"
|
#define CIFS_VERSION "2.0"
|
||||||
#endif /* _CIFSFS_H */
|
#endif /* _CIFSFS_H */
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
#include "smb2pdu.h"
|
#include "smb2pdu.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The sizes of various internal tables and strings
|
* The sizes of various internal tables and strings
|
||||||
*/
|
*/
|
||||||
|
@ -128,8 +130,10 @@ struct sdesc {
|
||||||
struct cifs_secmech {
|
struct cifs_secmech {
|
||||||
struct crypto_shash *hmacmd5; /* hmac-md5 hash function */
|
struct crypto_shash *hmacmd5; /* hmac-md5 hash function */
|
||||||
struct crypto_shash *md5; /* md5 hash function */
|
struct crypto_shash *md5; /* md5 hash function */
|
||||||
|
struct crypto_shash *hmacsha256; /* hmac-sha256 hash function */
|
||||||
struct sdesc *sdeschmacmd5; /* ctxt to generate ntlmv2 hash, CR1 */
|
struct sdesc *sdeschmacmd5; /* ctxt to generate ntlmv2 hash, CR1 */
|
||||||
struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */
|
struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */
|
||||||
|
struct sdesc *sdeschmacsha256; /* ctxt to generate smb2 signature */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* per smb session structure/fields */
|
/* per smb session structure/fields */
|
||||||
|
@ -158,9 +162,24 @@ struct cifs_cred {
|
||||||
*****************************************************************
|
*****************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A smb_rqst represents a complete request to be issued to a server. It's
|
||||||
|
* formed by a kvec array, followed by an array of pages. Page data is assumed
|
||||||
|
* to start at the beginning of the first page.
|
||||||
|
*/
|
||||||
|
struct smb_rqst {
|
||||||
|
struct kvec *rq_iov; /* array of kvecs */
|
||||||
|
unsigned int rq_nvec; /* number of kvecs in array */
|
||||||
|
struct page **rq_pages; /* pointer to array of page ptrs */
|
||||||
|
unsigned int rq_npages; /* number pages in array */
|
||||||
|
unsigned int rq_pagesz; /* page size to use */
|
||||||
|
unsigned int rq_tailsz; /* length of last page */
|
||||||
|
};
|
||||||
|
|
||||||
enum smb_version {
|
enum smb_version {
|
||||||
Smb_1 = 1,
|
Smb_1 = 1,
|
||||||
Smb_21,
|
Smb_21,
|
||||||
|
Smb_30,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mid_q_entry;
|
struct mid_q_entry;
|
||||||
|
@ -171,17 +190,23 @@ struct cifs_tcon;
|
||||||
struct dfs_info3_param;
|
struct dfs_info3_param;
|
||||||
struct cifs_fattr;
|
struct cifs_fattr;
|
||||||
struct smb_vol;
|
struct smb_vol;
|
||||||
|
struct cifs_fid;
|
||||||
|
struct cifs_readdata;
|
||||||
|
struct cifs_writedata;
|
||||||
|
struct cifs_io_parms;
|
||||||
|
struct cifs_search_info;
|
||||||
|
struct cifsInodeInfo;
|
||||||
|
|
||||||
struct smb_version_operations {
|
struct smb_version_operations {
|
||||||
int (*send_cancel)(struct TCP_Server_Info *, void *,
|
int (*send_cancel)(struct TCP_Server_Info *, void *,
|
||||||
struct mid_q_entry *);
|
struct mid_q_entry *);
|
||||||
bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
|
bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
|
||||||
/* setup request: allocate mid, sign message */
|
/* setup request: allocate mid, sign message */
|
||||||
int (*setup_request)(struct cifs_ses *, struct kvec *, unsigned int,
|
struct mid_q_entry *(*setup_request)(struct cifs_ses *,
|
||||||
struct mid_q_entry **);
|
struct smb_rqst *);
|
||||||
/* setup async request: allocate mid, sign message */
|
/* setup async request: allocate mid, sign message */
|
||||||
int (*setup_async_request)(struct TCP_Server_Info *, struct kvec *,
|
struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *,
|
||||||
unsigned int, struct mid_q_entry **);
|
struct smb_rqst *);
|
||||||
/* check response: verify signature, map error */
|
/* check response: verify signature, map error */
|
||||||
int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *,
|
int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *,
|
||||||
bool);
|
bool);
|
||||||
|
@ -212,6 +237,10 @@ struct smb_version_operations {
|
||||||
bool (*need_neg)(struct TCP_Server_Info *);
|
bool (*need_neg)(struct TCP_Server_Info *);
|
||||||
/* negotiate to the server */
|
/* negotiate to the server */
|
||||||
int (*negotiate)(const unsigned int, struct cifs_ses *);
|
int (*negotiate)(const unsigned int, struct cifs_ses *);
|
||||||
|
/* set negotiated write size */
|
||||||
|
unsigned int (*negotiate_wsize)(struct cifs_tcon *, struct smb_vol *);
|
||||||
|
/* set negotiated read size */
|
||||||
|
unsigned int (*negotiate_rsize)(struct cifs_tcon *, struct smb_vol *);
|
||||||
/* setup smb sessionn */
|
/* setup smb sessionn */
|
||||||
int (*sess_setup)(const unsigned int, struct cifs_ses *,
|
int (*sess_setup)(const unsigned int, struct cifs_ses *,
|
||||||
const struct nls_table *);
|
const struct nls_table *);
|
||||||
|
@ -235,10 +264,22 @@ struct smb_version_operations {
|
||||||
int (*query_path_info)(const unsigned int, struct cifs_tcon *,
|
int (*query_path_info)(const unsigned int, struct cifs_tcon *,
|
||||||
struct cifs_sb_info *, const char *,
|
struct cifs_sb_info *, const char *,
|
||||||
FILE_ALL_INFO *, bool *);
|
FILE_ALL_INFO *, bool *);
|
||||||
|
/* query file data from the server */
|
||||||
|
int (*query_file_info)(const unsigned int, struct cifs_tcon *,
|
||||||
|
struct cifs_fid *, FILE_ALL_INFO *);
|
||||||
/* get server index number */
|
/* get server index number */
|
||||||
int (*get_srv_inum)(const unsigned int, struct cifs_tcon *,
|
int (*get_srv_inum)(const unsigned int, struct cifs_tcon *,
|
||||||
struct cifs_sb_info *, const char *,
|
struct cifs_sb_info *, const char *,
|
||||||
u64 *uniqueid, FILE_ALL_INFO *);
|
u64 *uniqueid, FILE_ALL_INFO *);
|
||||||
|
/* set size by path */
|
||||||
|
int (*set_path_size)(const unsigned int, struct cifs_tcon *,
|
||||||
|
const char *, __u64, struct cifs_sb_info *, bool);
|
||||||
|
/* set size by file handle */
|
||||||
|
int (*set_file_size)(const unsigned int, struct cifs_tcon *,
|
||||||
|
struct cifsFileInfo *, __u64, bool);
|
||||||
|
/* set attributes */
|
||||||
|
int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *,
|
||||||
|
const unsigned int);
|
||||||
/* build a full path to the root of the mount */
|
/* build a full path to the root of the mount */
|
||||||
char * (*build_path_to_root)(struct smb_vol *, struct cifs_sb_info *,
|
char * (*build_path_to_root)(struct smb_vol *, struct cifs_sb_info *,
|
||||||
struct cifs_tcon *);
|
struct cifs_tcon *);
|
||||||
|
@ -256,10 +297,84 @@ struct smb_version_operations {
|
||||||
/* remove directory */
|
/* remove directory */
|
||||||
int (*rmdir)(const unsigned int, struct cifs_tcon *, const char *,
|
int (*rmdir)(const unsigned int, struct cifs_tcon *, const char *,
|
||||||
struct cifs_sb_info *);
|
struct cifs_sb_info *);
|
||||||
|
/* unlink file */
|
||||||
|
int (*unlink)(const unsigned int, struct cifs_tcon *, const char *,
|
||||||
|
struct cifs_sb_info *);
|
||||||
|
/* open, rename and delete file */
|
||||||
|
int (*rename_pending_delete)(const char *, struct dentry *,
|
||||||
|
const unsigned int);
|
||||||
|
/* send rename request */
|
||||||
|
int (*rename)(const unsigned int, struct cifs_tcon *, const char *,
|
||||||
|
const char *, struct cifs_sb_info *);
|
||||||
|
/* send create hardlink request */
|
||||||
|
int (*create_hardlink)(const unsigned int, struct cifs_tcon *,
|
||||||
|
const char *, const char *,
|
||||||
|
struct cifs_sb_info *);
|
||||||
|
/* open a file for non-posix mounts */
|
||||||
|
int (*open)(const unsigned int, struct cifs_tcon *, const char *, int,
|
||||||
|
int, int, struct cifs_fid *, __u32 *, FILE_ALL_INFO *,
|
||||||
|
struct cifs_sb_info *);
|
||||||
|
/* set fid protocol-specific info */
|
||||||
|
void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
|
||||||
|
/* close a file */
|
||||||
|
void (*close)(const unsigned int, struct cifs_tcon *,
|
||||||
|
struct cifs_fid *);
|
||||||
|
/* send a flush request to the server */
|
||||||
|
int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *);
|
||||||
|
/* async read from the server */
|
||||||
|
int (*async_readv)(struct cifs_readdata *);
|
||||||
|
/* async write to the server */
|
||||||
|
int (*async_writev)(struct cifs_writedata *);
|
||||||
|
/* sync read from the server */
|
||||||
|
int (*sync_read)(const unsigned int, struct cifsFileInfo *,
|
||||||
|
struct cifs_io_parms *, unsigned int *, char **,
|
||||||
|
int *);
|
||||||
|
/* sync write to the server */
|
||||||
|
int (*sync_write)(const unsigned int, struct cifsFileInfo *,
|
||||||
|
struct cifs_io_parms *, unsigned int *, struct kvec *,
|
||||||
|
unsigned long);
|
||||||
|
/* open dir, start readdir */
|
||||||
|
int (*query_dir_first)(const unsigned int, struct cifs_tcon *,
|
||||||
|
const char *, struct cifs_sb_info *,
|
||||||
|
struct cifs_fid *, __u16,
|
||||||
|
struct cifs_search_info *);
|
||||||
|
/* continue readdir */
|
||||||
|
int (*query_dir_next)(const unsigned int, struct cifs_tcon *,
|
||||||
|
struct cifs_fid *,
|
||||||
|
__u16, struct cifs_search_info *srch_inf);
|
||||||
|
/* close dir */
|
||||||
|
int (*close_dir)(const unsigned int, struct cifs_tcon *,
|
||||||
|
struct cifs_fid *);
|
||||||
|
/* calculate a size of SMB message */
|
||||||
|
unsigned int (*calc_smb_size)(void *);
|
||||||
|
/* check for STATUS_PENDING and process it in a positive case */
|
||||||
|
bool (*is_status_pending)(char *, struct TCP_Server_Info *, int);
|
||||||
|
/* send oplock break response */
|
||||||
|
int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *,
|
||||||
|
struct cifsInodeInfo *);
|
||||||
|
/* query remote filesystem */
|
||||||
|
int (*queryfs)(const unsigned int, struct cifs_tcon *,
|
||||||
|
struct kstatfs *);
|
||||||
|
/* send mandatory brlock to the server */
|
||||||
|
int (*mand_lock)(const unsigned int, struct cifsFileInfo *, __u64,
|
||||||
|
__u64, __u32, int, int, bool);
|
||||||
|
/* unlock range of mandatory locks */
|
||||||
|
int (*mand_unlock_range)(struct cifsFileInfo *, struct file_lock *,
|
||||||
|
const unsigned int);
|
||||||
|
/* push brlocks from the cache to the server */
|
||||||
|
int (*push_mand_locks)(struct cifsFileInfo *);
|
||||||
|
/* get lease key of the inode */
|
||||||
|
void (*get_lease_key)(struct inode *, struct cifs_fid *fid);
|
||||||
|
/* set lease key of the inode */
|
||||||
|
void (*set_lease_key)(struct inode *, struct cifs_fid *fid);
|
||||||
|
/* generate new lease key */
|
||||||
|
void (*new_lease_key)(struct cifs_fid *fid);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values {
|
struct smb_version_values {
|
||||||
char *version_string;
|
char *version_string;
|
||||||
|
__u16 protocol_id;
|
||||||
|
__u32 req_capabilities;
|
||||||
__u32 large_lock_type;
|
__u32 large_lock_type;
|
||||||
__u32 exclusive_lock_type;
|
__u32 exclusive_lock_type;
|
||||||
__u32 shared_lock_type;
|
__u32 shared_lock_type;
|
||||||
|
@ -495,6 +610,51 @@ get_next_mid(struct TCP_Server_Info *server)
|
||||||
return server->ops->get_next_mid(server);
|
return server->ops->get_next_mid(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the server supports very large reads and writes via POSIX extensions,
|
||||||
|
* we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not
|
||||||
|
* including the RFC1001 length.
|
||||||
|
*
|
||||||
|
* Note that this might make for "interesting" allocation problems during
|
||||||
|
* writeback however as we have to allocate an array of pointers for the
|
||||||
|
* pages. A 16M write means ~32kb page array with PAGE_CACHE_SIZE == 4096.
|
||||||
|
*
|
||||||
|
* For reads, there is a similar problem as we need to allocate an array
|
||||||
|
* of kvecs to handle the receive, though that should only need to be done
|
||||||
|
* once.
|
||||||
|
*/
|
||||||
|
#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4)
|
||||||
|
#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the server doesn't allow large posix writes, only allow a rsize/wsize
|
||||||
|
* of 2^17-1 minus the size of the call header. That allows for a read or
|
||||||
|
* write up to the maximum size described by RFC1002.
|
||||||
|
*/
|
||||||
|
#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4)
|
||||||
|
#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The default wsize is 1M. find_get_pages seems to return a maximum of 256
|
||||||
|
* pages in a single call. With PAGE_CACHE_SIZE == 4k, this means we can fill
|
||||||
|
* a single wsize request with a single call.
|
||||||
|
*/
|
||||||
|
#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Windows only supports a max of 60kb reads and 65535 byte writes. Default to
|
||||||
|
* those values when posix extensions aren't in force. In actuality here, we
|
||||||
|
* use 65536 to allow for a write that is a multiple of 4k. Most servers seem
|
||||||
|
* to be ok with the extra byte even though Windows doesn't send writes that
|
||||||
|
* are that large.
|
||||||
|
*
|
||||||
|
* Citation:
|
||||||
|
*
|
||||||
|
* http://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx
|
||||||
|
*/
|
||||||
|
#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024)
|
||||||
|
#define CIFS_DEFAULT_NON_POSIX_WSIZE (65536)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Macros to allow the TCP_Server_Info->net field and related code to drop out
|
* Macros to allow the TCP_Server_Info->net field and related code to drop out
|
||||||
* when CONFIG_NET_NS isn't set.
|
* when CONFIG_NET_NS isn't set.
|
||||||
|
@ -559,6 +719,7 @@ struct cifs_ses {
|
||||||
__u16 session_flags;
|
__u16 session_flags;
|
||||||
#endif /* CONFIG_CIFS_SMB2 */
|
#endif /* CONFIG_CIFS_SMB2 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* no more than one of the following three session flags may be set */
|
/* no more than one of the following three session flags may be set */
|
||||||
#define CIFS_SES_NT4 1
|
#define CIFS_SES_NT4 1
|
||||||
#define CIFS_SES_OS2 2
|
#define CIFS_SES_OS2 2
|
||||||
|
@ -665,6 +826,7 @@ struct cifs_tcon {
|
||||||
u64 resource_id; /* server resource id */
|
u64 resource_id; /* server resource id */
|
||||||
struct fscache_cookie *fscache; /* cookie for share */
|
struct fscache_cookie *fscache; /* cookie for share */
|
||||||
#endif
|
#endif
|
||||||
|
struct list_head pending_opens; /* list of incomplete opens */
|
||||||
/* BB add field for back pointer to sb struct(s)? */
|
/* BB add field for back pointer to sb struct(s)? */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -707,6 +869,15 @@ cifs_get_tlink(struct tcon_link *tlink)
|
||||||
/* This function is always expected to succeed */
|
/* This function is always expected to succeed */
|
||||||
extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb);
|
extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb);
|
||||||
|
|
||||||
|
#define CIFS_OPLOCK_NO_CHANGE 0xfe
|
||||||
|
|
||||||
|
struct cifs_pending_open {
|
||||||
|
struct list_head olist;
|
||||||
|
struct tcon_link *tlink;
|
||||||
|
__u8 lease_key[16];
|
||||||
|
__u32 oplock;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This info hangs off the cifsFileInfo structure, pointed to by llist.
|
* This info hangs off the cifsFileInfo structure, pointed to by llist.
|
||||||
* This is used to track byte stream locks on the file
|
* This is used to track byte stream locks on the file
|
||||||
|
@ -740,16 +911,29 @@ struct cifs_search_info {
|
||||||
bool smallBuf:1; /* so we know which buf_release function to call */
|
bool smallBuf:1; /* so we know which buf_release function to call */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct cifs_fid {
|
||||||
|
__u16 netfid;
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
|
__u64 persistent_fid; /* persist file id for smb2 */
|
||||||
|
__u64 volatile_fid; /* volatile file id for smb2 */
|
||||||
|
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */
|
||||||
|
#endif
|
||||||
|
struct cifs_pending_open *pending_open;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cifs_fid_locks {
|
||||||
|
struct list_head llist;
|
||||||
|
struct cifsFileInfo *cfile; /* fid that owns locks */
|
||||||
|
struct list_head locks; /* locks held by fid above */
|
||||||
|
};
|
||||||
|
|
||||||
struct cifsFileInfo {
|
struct cifsFileInfo {
|
||||||
struct list_head tlist; /* pointer to next fid owned by tcon */
|
struct list_head tlist; /* pointer to next fid owned by tcon */
|
||||||
struct list_head flist; /* next fid (file instance) for this inode */
|
struct list_head flist; /* next fid (file instance) for this inode */
|
||||||
struct list_head llist; /*
|
struct cifs_fid_locks *llist; /* brlocks held by this fid */
|
||||||
* brlocks held by this fid, protected by
|
|
||||||
* lock_mutex from cifsInodeInfo structure
|
|
||||||
*/
|
|
||||||
unsigned int uid; /* allows finding which FileInfo structure */
|
unsigned int uid; /* allows finding which FileInfo structure */
|
||||||
__u32 pid; /* process id who opened file */
|
__u32 pid; /* process id who opened file */
|
||||||
__u16 netfid; /* file id from remote */
|
struct cifs_fid fid; /* file id from remote */
|
||||||
/* BB add lock scope info here if needed */ ;
|
/* BB add lock scope info here if needed */ ;
|
||||||
/* lock scope id (0 if none) */
|
/* lock scope id (0 if none) */
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
|
@ -765,12 +949,60 @@ struct cifsFileInfo {
|
||||||
|
|
||||||
struct cifs_io_parms {
|
struct cifs_io_parms {
|
||||||
__u16 netfid;
|
__u16 netfid;
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
|
__u64 persistent_fid; /* persist file id for smb2 */
|
||||||
|
__u64 volatile_fid; /* volatile file id for smb2 */
|
||||||
|
#endif
|
||||||
__u32 pid;
|
__u32 pid;
|
||||||
__u64 offset;
|
__u64 offset;
|
||||||
unsigned int length;
|
unsigned int length;
|
||||||
struct cifs_tcon *tcon;
|
struct cifs_tcon *tcon;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct cifs_readdata;
|
||||||
|
|
||||||
|
/* asynchronous read support */
|
||||||
|
struct cifs_readdata {
|
||||||
|
struct kref refcount;
|
||||||
|
struct list_head list;
|
||||||
|
struct completion done;
|
||||||
|
struct cifsFileInfo *cfile;
|
||||||
|
struct address_space *mapping;
|
||||||
|
__u64 offset;
|
||||||
|
unsigned int bytes;
|
||||||
|
pid_t pid;
|
||||||
|
int result;
|
||||||
|
struct work_struct work;
|
||||||
|
int (*read_into_pages)(struct TCP_Server_Info *server,
|
||||||
|
struct cifs_readdata *rdata,
|
||||||
|
unsigned int len);
|
||||||
|
struct kvec iov;
|
||||||
|
unsigned int pagesz;
|
||||||
|
unsigned int tailsz;
|
||||||
|
unsigned int nr_pages;
|
||||||
|
struct page *pages[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cifs_writedata;
|
||||||
|
|
||||||
|
/* asynchronous write support */
|
||||||
|
struct cifs_writedata {
|
||||||
|
struct kref refcount;
|
||||||
|
struct list_head list;
|
||||||
|
struct completion done;
|
||||||
|
enum writeback_sync_modes sync_mode;
|
||||||
|
struct work_struct work;
|
||||||
|
struct cifsFileInfo *cfile;
|
||||||
|
__u64 offset;
|
||||||
|
pid_t pid;
|
||||||
|
unsigned int bytes;
|
||||||
|
int result;
|
||||||
|
unsigned int pagesz;
|
||||||
|
unsigned int tailsz;
|
||||||
|
unsigned int nr_pages;
|
||||||
|
struct page *pages[1];
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Take a reference on the file private data. Must be called with
|
* Take a reference on the file private data. Must be called with
|
||||||
* cifs_file_list_lock held.
|
* cifs_file_list_lock held.
|
||||||
|
@ -790,11 +1022,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
|
||||||
|
|
||||||
struct cifsInodeInfo {
|
struct cifsInodeInfo {
|
||||||
bool can_cache_brlcks;
|
bool can_cache_brlcks;
|
||||||
struct mutex lock_mutex; /*
|
struct list_head llist; /* locks helb by this inode */
|
||||||
* protect the field above and llist
|
struct rw_semaphore lock_sem; /* protect the fields above */
|
||||||
* from every cifsFileInfo structure
|
|
||||||
* from openFileList
|
|
||||||
*/
|
|
||||||
/* BB add in lists for dirty pages i.e. write caching info for oplock */
|
/* BB add in lists for dirty pages i.e. write caching info for oplock */
|
||||||
struct list_head openFileList;
|
struct list_head openFileList;
|
||||||
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
|
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
|
||||||
|
@ -806,6 +1035,9 @@ struct cifsInodeInfo {
|
||||||
u64 server_eof; /* current file size on server -- protected by i_lock */
|
u64 server_eof; /* current file size on server -- protected by i_lock */
|
||||||
u64 uniqueid; /* server inode number */
|
u64 uniqueid; /* server inode number */
|
||||||
u64 createtime; /* creation time on server */
|
u64 createtime; /* creation time on server */
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
|
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_CIFS_FSCACHE
|
#ifdef CONFIG_CIFS_FSCACHE
|
||||||
struct fscache_cookie *fscache;
|
struct fscache_cookie *fscache;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1130,7 +1362,7 @@ require use of the stronger protocol */
|
||||||
#define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */
|
#define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */
|
||||||
#define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */
|
#define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */
|
||||||
|
|
||||||
#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2)
|
#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP)
|
||||||
#define CIFSSEC_MAX (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2)
|
#define CIFSSEC_MAX (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2)
|
||||||
#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)
|
#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)
|
||||||
/*
|
/*
|
||||||
|
@ -1267,7 +1499,13 @@ extern mempool_t *cifs_mid_poolp;
|
||||||
#define SMB1_VERSION_STRING "1.0"
|
#define SMB1_VERSION_STRING "1.0"
|
||||||
extern struct smb_version_operations smb1_operations;
|
extern struct smb_version_operations smb1_operations;
|
||||||
extern struct smb_version_values smb1_values;
|
extern struct smb_version_values smb1_values;
|
||||||
|
#define SMB20_VERSION_STRING "2.0"
|
||||||
|
/*extern struct smb_version_operations smb20_operations; */ /* not needed yet */
|
||||||
|
extern struct smb_version_values smb20_values;
|
||||||
#define SMB21_VERSION_STRING "2.1"
|
#define SMB21_VERSION_STRING "2.1"
|
||||||
extern struct smb_version_operations smb21_operations;
|
extern struct smb_version_operations smb21_operations;
|
||||||
extern struct smb_version_values smb21_values;
|
extern struct smb_version_values smb21_values;
|
||||||
|
#define SMB30_VERSION_STRING "3.0"
|
||||||
|
/*extern struct smb_version_operations smb30_operations; */ /* not needed yet */
|
||||||
|
extern struct smb_version_values smb30_values;
|
||||||
#endif /* _CIFS_GLOB_H */
|
#endif /* _CIFS_GLOB_H */
|
||||||
|
|
|
@ -2210,7 +2210,7 @@ typedef struct { /* data block encoding of response to level 263 QPathInfo */
|
||||||
__u8 DeletePending;
|
__u8 DeletePending;
|
||||||
__u8 Directory;
|
__u8 Directory;
|
||||||
__u16 Pad2;
|
__u16 Pad2;
|
||||||
__u64 IndexNumber;
|
__le64 IndexNumber;
|
||||||
__le32 EASize;
|
__le32 EASize;
|
||||||
__le32 AccessFlags;
|
__le32 AccessFlags;
|
||||||
__u64 IndexNumber1;
|
__u64 IndexNumber1;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
struct statfs;
|
struct statfs;
|
||||||
struct smb_vol;
|
struct smb_vol;
|
||||||
|
struct smb_rqst;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*****************************************************************
|
*****************************************************************
|
||||||
|
@ -35,6 +36,8 @@ extern struct smb_hdr *cifs_buf_get(void);
|
||||||
extern void cifs_buf_release(void *);
|
extern void cifs_buf_release(void *);
|
||||||
extern struct smb_hdr *cifs_small_buf_get(void);
|
extern struct smb_hdr *cifs_small_buf_get(void);
|
||||||
extern void cifs_small_buf_release(void *);
|
extern void cifs_small_buf_release(void *);
|
||||||
|
extern void cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx,
|
||||||
|
struct kvec *iov);
|
||||||
extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *,
|
extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *,
|
||||||
unsigned int /* length */);
|
unsigned int /* length */);
|
||||||
extern unsigned int _get_xid(void);
|
extern unsigned int _get_xid(void);
|
||||||
|
@ -65,21 +68,22 @@ extern char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||||
extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer,
|
extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer,
|
||||||
struct TCP_Server_Info *server);
|
struct TCP_Server_Info *server);
|
||||||
extern void DeleteMidQEntry(struct mid_q_entry *midEntry);
|
extern void DeleteMidQEntry(struct mid_q_entry *midEntry);
|
||||||
|
extern void cifs_delete_mid(struct mid_q_entry *mid);
|
||||||
extern void cifs_wake_up_task(struct mid_q_entry *mid);
|
extern void cifs_wake_up_task(struct mid_q_entry *mid);
|
||||||
extern int cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
|
extern int cifs_call_async(struct TCP_Server_Info *server,
|
||||||
unsigned int nvec, mid_receive_t *receive,
|
struct smb_rqst *rqst,
|
||||||
mid_callback_t *callback, void *cbdata,
|
mid_receive_t *receive, mid_callback_t *callback,
|
||||||
const int flags);
|
void *cbdata, const int flags);
|
||||||
extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
|
extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
|
||||||
struct smb_hdr * /* input */ ,
|
struct smb_hdr * /* input */ ,
|
||||||
struct smb_hdr * /* out */ ,
|
struct smb_hdr * /* out */ ,
|
||||||
int * /* bytes returned */ , const int);
|
int * /* bytes returned */ , const int);
|
||||||
extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
|
extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
|
||||||
char *in_buf, int flags);
|
char *in_buf, int flags);
|
||||||
extern int cifs_setup_request(struct cifs_ses *, struct kvec *, unsigned int,
|
extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *,
|
||||||
struct mid_q_entry **);
|
struct smb_rqst *);
|
||||||
extern int cifs_setup_async_request(struct TCP_Server_Info *, struct kvec *,
|
extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *,
|
||||||
unsigned int, struct mid_q_entry **);
|
struct smb_rqst *);
|
||||||
extern int cifs_check_receive(struct mid_q_entry *mid,
|
extern int cifs_check_receive(struct mid_q_entry *mid,
|
||||||
struct TCP_Server_Info *server, bool log_error);
|
struct TCP_Server_Info *server, bool log_error);
|
||||||
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
|
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
|
||||||
|
@ -99,7 +103,7 @@ extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
|
||||||
unsigned int bytes_written);
|
unsigned int bytes_written);
|
||||||
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
|
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
|
||||||
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
|
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
|
||||||
extern unsigned int smbCalcSize(struct smb_hdr *ptr);
|
extern unsigned int smbCalcSize(void *buf);
|
||||||
extern int decode_negTokenInit(unsigned char *security_blob, int length,
|
extern int decode_negTokenInit(unsigned char *security_blob, int length,
|
||||||
struct TCP_Server_Info *server);
|
struct TCP_Server_Info *server);
|
||||||
extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
|
extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
|
||||||
|
@ -120,10 +124,14 @@ extern u64 cifs_UnixTimeToNT(struct timespec);
|
||||||
extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
|
extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
|
||||||
int offset);
|
int offset);
|
||||||
extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
|
extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
|
||||||
|
extern int cifs_unlock_range(struct cifsFileInfo *cfile,
|
||||||
|
struct file_lock *flock, const unsigned int xid);
|
||||||
|
extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
|
||||||
|
|
||||||
extern struct cifsFileInfo *cifs_new_fileinfo(__u16 fileHandle,
|
extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid,
|
||||||
struct file *file, struct tcon_link *tlink,
|
struct file *file,
|
||||||
__u32 oplock);
|
struct tcon_link *tlink,
|
||||||
|
__u32 oplock);
|
||||||
extern int cifs_posix_open(char *full_path, struct inode **inode,
|
extern int cifs_posix_open(char *full_path, struct inode **inode,
|
||||||
struct super_block *sb, int mode,
|
struct super_block *sb, int mode,
|
||||||
unsigned int f_flags, __u32 *oplock, __u16 *netfid,
|
unsigned int f_flags, __u32 *oplock, __u16 *netfid,
|
||||||
|
@ -132,18 +140,23 @@ void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr);
|
||||||
extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
|
extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
|
||||||
FILE_UNIX_BASIC_INFO *info,
|
FILE_UNIX_BASIC_INFO *info,
|
||||||
struct cifs_sb_info *cifs_sb);
|
struct cifs_sb_info *cifs_sb);
|
||||||
|
extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *,
|
||||||
|
struct cifs_sb_info *);
|
||||||
extern void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
|
extern void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
|
||||||
extern struct inode *cifs_iget(struct super_block *sb,
|
extern struct inode *cifs_iget(struct super_block *sb,
|
||||||
struct cifs_fattr *fattr);
|
struct cifs_fattr *fattr);
|
||||||
|
|
||||||
extern int cifs_get_file_info(struct file *filp);
|
|
||||||
extern int cifs_get_inode_info(struct inode **inode, const char *full_path,
|
extern int cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||||
FILE_ALL_INFO *data, struct super_block *sb,
|
FILE_ALL_INFO *data, struct super_block *sb,
|
||||||
int xid, const __u16 *fid);
|
int xid, const __u16 *fid);
|
||||||
extern int cifs_get_file_info_unix(struct file *filp);
|
|
||||||
extern int cifs_get_inode_info_unix(struct inode **pinode,
|
extern int cifs_get_inode_info_unix(struct inode **pinode,
|
||||||
const unsigned char *search_path,
|
const unsigned char *search_path,
|
||||||
struct super_block *sb, unsigned int xid);
|
struct super_block *sb, unsigned int xid);
|
||||||
|
extern int cifs_set_file_info(struct inode *inode, struct iattr *attrs,
|
||||||
|
unsigned int xid, char *full_path, __u32 dosattr);
|
||||||
|
extern int cifs_rename_pending_delete(const char *full_path,
|
||||||
|
struct dentry *dentry,
|
||||||
|
const unsigned int xid);
|
||||||
extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
|
extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
|
||||||
struct cifs_fattr *fattr, struct inode *inode,
|
struct cifs_fattr *fattr, struct inode *inode,
|
||||||
const char *path, const __u16 *pfid);
|
const char *path, const __u16 *pfid);
|
||||||
|
@ -169,6 +182,17 @@ extern struct smb_vol *cifs_get_volume_info(char *mount_data,
|
||||||
extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
|
extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
|
||||||
extern void cifs_umount(struct cifs_sb_info *);
|
extern void cifs_umount(struct cifs_sb_info *);
|
||||||
extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
|
extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
|
||||||
|
extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
|
||||||
|
__u64 length, __u8 type,
|
||||||
|
struct cifsLockInfo **conf_lock,
|
||||||
|
bool rw_check);
|
||||||
|
extern void cifs_add_pending_open(struct cifs_fid *fid,
|
||||||
|
struct tcon_link *tlink,
|
||||||
|
struct cifs_pending_open *open);
|
||||||
|
extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
|
||||||
|
struct tcon_link *tlink,
|
||||||
|
struct cifs_pending_open *open);
|
||||||
|
extern void cifs_del_pending_open(struct cifs_pending_open *open);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
|
#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
|
||||||
extern void cifs_dfs_release_automount_timer(void);
|
extern void cifs_dfs_release_automount_timer(void);
|
||||||
|
@ -179,6 +203,10 @@ extern void cifs_dfs_release_automount_timer(void);
|
||||||
void cifs_proc_init(void);
|
void cifs_proc_init(void);
|
||||||
void cifs_proc_clean(void);
|
void cifs_proc_clean(void);
|
||||||
|
|
||||||
|
extern void cifs_move_llist(struct list_head *source, struct list_head *dest);
|
||||||
|
extern void cifs_free_llist(struct list_head *llist);
|
||||||
|
extern void cifs_del_lock_waiters(struct cifsLockInfo *lock);
|
||||||
|
|
||||||
extern int cifs_negotiate_protocol(const unsigned int xid,
|
extern int cifs_negotiate_protocol(const unsigned int xid,
|
||||||
struct cifs_ses *ses);
|
struct cifs_ses *ses);
|
||||||
extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
|
extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
|
||||||
|
@ -190,10 +218,10 @@ extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
|
||||||
const struct nls_table *);
|
const struct nls_table *);
|
||||||
|
|
||||||
extern int CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *searchName, const struct nls_table *nls_codepage,
|
const char *searchName, struct cifs_sb_info *cifs_sb,
|
||||||
__u16 *searchHandle, __u16 search_flags,
|
__u16 *searchHandle, __u16 search_flags,
|
||||||
struct cifs_search_info *psrch_inf,
|
struct cifs_search_info *psrch_inf,
|
||||||
int map, const char dirsep);
|
bool msearch);
|
||||||
|
|
||||||
extern int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
__u16 searchHandle, __u16 search_flags,
|
__u16 searchHandle, __u16 search_flags,
|
||||||
|
@ -265,13 +293,11 @@ extern int CIFSSMBSetAttrLegacy(unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const struct nls_table *nls_codepage);
|
const struct nls_table *nls_codepage);
|
||||||
#endif /* possibly unneeded function */
|
#endif /* possibly unneeded function */
|
||||||
extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *fileName, __u64 size,
|
const char *file_name, __u64 size,
|
||||||
bool setAllocationSizeFlag,
|
struct cifs_sb_info *cifs_sb, bool set_allocation);
|
||||||
const struct nls_table *nls_codepage,
|
|
||||||
int remap_special_chars);
|
|
||||||
extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
__u64 size, __u16 fileHandle, __u32 opener_pid,
|
struct cifsFileInfo *cfile, __u64 size,
|
||||||
bool AllocSizeFlag);
|
bool set_allocation);
|
||||||
|
|
||||||
struct cifs_unix_set_info_args {
|
struct cifs_unix_set_info_args {
|
||||||
__u64 ctime;
|
__u64 ctime;
|
||||||
|
@ -303,22 +329,17 @@ extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const struct nls_table *nls_codepage,
|
const struct nls_table *nls_codepage,
|
||||||
int remap_special_chars);
|
int remap_special_chars);
|
||||||
extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *name,
|
const char *name, struct cifs_sb_info *cifs_sb);
|
||||||
const struct nls_table *nls_codepage,
|
|
||||||
int remap_special_chars);
|
|
||||||
extern int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *fromName, const char *toName,
|
const char *from_name, const char *to_name,
|
||||||
const struct nls_table *nls_codepage,
|
struct cifs_sb_info *cifs_sb);
|
||||||
int remap_special_chars);
|
|
||||||
extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
int netfid, const char *target_name,
|
int netfid, const char *target_name,
|
||||||
const struct nls_table *nls_codepage,
|
const struct nls_table *nls_codepage,
|
||||||
int remap_special_chars);
|
int remap_special_chars);
|
||||||
extern int CIFSCreateHardLink(const unsigned int xid,
|
extern int CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
struct cifs_tcon *tcon,
|
const char *from_name, const char *to_name,
|
||||||
const char *fromName, const char *toName,
|
struct cifs_sb_info *cifs_sb);
|
||||||
const struct nls_table *nls_codepage,
|
|
||||||
int remap_special_chars);
|
|
||||||
extern int CIFSUnixCreateHardLink(const unsigned int xid,
|
extern int CIFSUnixCreateHardLink(const unsigned int xid,
|
||||||
struct cifs_tcon *tcon,
|
struct cifs_tcon *tcon,
|
||||||
const char *fromName, const char *toName,
|
const char *fromName, const char *toName,
|
||||||
|
@ -367,8 +388,7 @@ extern int CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
|
||||||
unsigned int *nbytes, const char *buf,
|
unsigned int *nbytes, const char *buf,
|
||||||
const char __user *ubuf, const int long_op);
|
const char __user *ubuf, const int long_op);
|
||||||
extern int CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
|
extern int CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
|
||||||
unsigned int *nbytes, struct kvec *iov, const int nvec,
|
unsigned int *nbytes, struct kvec *iov, const int nvec);
|
||||||
const int long_op);
|
|
||||||
extern int CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *search_name, __u64 *inode_number,
|
const char *search_name, __u64 *inode_number,
|
||||||
const struct nls_table *nls_codepage,
|
const struct nls_table *nls_codepage,
|
||||||
|
@ -397,10 +417,12 @@ extern void sesInfoFree(struct cifs_ses *);
|
||||||
extern struct cifs_tcon *tconInfoAlloc(void);
|
extern struct cifs_tcon *tconInfoAlloc(void);
|
||||||
extern void tconInfoFree(struct cifs_tcon *);
|
extern void tconInfoFree(struct cifs_tcon *);
|
||||||
|
|
||||||
extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *);
|
extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
|
||||||
|
__u32 *pexpected_response_sequence_number);
|
||||||
extern int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
|
extern int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
|
||||||
__u32 *);
|
__u32 *);
|
||||||
extern int cifs_verify_signature(struct kvec *iov, unsigned int nr_iov,
|
extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *);
|
||||||
|
extern int cifs_verify_signature(struct smb_rqst *rqst,
|
||||||
struct TCP_Server_Info *server,
|
struct TCP_Server_Info *server,
|
||||||
__u32 expected_sequence_number);
|
__u32 expected_sequence_number);
|
||||||
extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *,
|
extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *,
|
||||||
|
@ -462,45 +484,9 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
|
||||||
extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
|
extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
|
||||||
unsigned char *p24);
|
unsigned char *p24);
|
||||||
|
|
||||||
/* asynchronous read support */
|
|
||||||
struct cifs_readdata {
|
|
||||||
struct kref refcount;
|
|
||||||
struct list_head list;
|
|
||||||
struct completion done;
|
|
||||||
struct cifsFileInfo *cfile;
|
|
||||||
struct address_space *mapping;
|
|
||||||
__u64 offset;
|
|
||||||
unsigned int bytes;
|
|
||||||
pid_t pid;
|
|
||||||
int result;
|
|
||||||
struct list_head pages;
|
|
||||||
struct work_struct work;
|
|
||||||
int (*marshal_iov) (struct cifs_readdata *rdata,
|
|
||||||
unsigned int remaining);
|
|
||||||
unsigned int nr_iov;
|
|
||||||
struct kvec iov[1];
|
|
||||||
};
|
|
||||||
|
|
||||||
void cifs_readdata_release(struct kref *refcount);
|
void cifs_readdata_release(struct kref *refcount);
|
||||||
int cifs_async_readv(struct cifs_readdata *rdata);
|
int cifs_async_readv(struct cifs_readdata *rdata);
|
||||||
|
int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);
|
||||||
/* asynchronous write support */
|
|
||||||
struct cifs_writedata {
|
|
||||||
struct kref refcount;
|
|
||||||
struct list_head list;
|
|
||||||
struct completion done;
|
|
||||||
enum writeback_sync_modes sync_mode;
|
|
||||||
struct work_struct work;
|
|
||||||
struct cifsFileInfo *cfile;
|
|
||||||
__u64 offset;
|
|
||||||
pid_t pid;
|
|
||||||
unsigned int bytes;
|
|
||||||
int result;
|
|
||||||
void (*marshal_iov) (struct kvec *iov,
|
|
||||||
struct cifs_writedata *wdata);
|
|
||||||
unsigned int nr_pages;
|
|
||||||
struct page *pages[1];
|
|
||||||
};
|
|
||||||
|
|
||||||
int cifs_async_writev(struct cifs_writedata *wdata);
|
int cifs_async_writev(struct cifs_writedata *wdata);
|
||||||
void cifs_writev_complete(struct work_struct *work);
|
void cifs_writev_complete(struct work_struct *work);
|
||||||
|
|
|
@ -86,32 +86,6 @@ static struct {
|
||||||
#endif /* CONFIG_CIFS_WEAK_PW_HASH */
|
#endif /* CONFIG_CIFS_WEAK_PW_HASH */
|
||||||
#endif /* CIFS_POSIX */
|
#endif /* CIFS_POSIX */
|
||||||
|
|
||||||
#ifdef CONFIG_HIGHMEM
|
|
||||||
/*
|
|
||||||
* On arches that have high memory, kmap address space is limited. By
|
|
||||||
* serializing the kmap operations on those arches, we ensure that we don't
|
|
||||||
* end up with a bunch of threads in writeback with partially mapped page
|
|
||||||
* arrays, stuck waiting for kmap to come back. That situation prevents
|
|
||||||
* progress and can deadlock.
|
|
||||||
*/
|
|
||||||
static DEFINE_MUTEX(cifs_kmap_mutex);
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
cifs_kmap_lock(void)
|
|
||||||
{
|
|
||||||
mutex_lock(&cifs_kmap_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
cifs_kmap_unlock(void)
|
|
||||||
{
|
|
||||||
mutex_unlock(&cifs_kmap_mutex);
|
|
||||||
}
|
|
||||||
#else /* !CONFIG_HIGHMEM */
|
|
||||||
#define cifs_kmap_lock() do { ; } while(0)
|
|
||||||
#define cifs_kmap_unlock() do { ; } while(0)
|
|
||||||
#endif /* CONFIG_HIGHMEM */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mark as invalid, all open files on tree connections since they
|
* Mark as invalid, all open files on tree connections since they
|
||||||
* were closed when session to server was lost.
|
* were closed when session to server was lost.
|
||||||
|
@ -751,6 +725,8 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
|
||||||
ECHO_REQ *smb;
|
ECHO_REQ *smb;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct kvec iov;
|
struct kvec iov;
|
||||||
|
struct smb_rqst rqst = { .rq_iov = &iov,
|
||||||
|
.rq_nvec = 1 };
|
||||||
|
|
||||||
cFYI(1, "In echo request");
|
cFYI(1, "In echo request");
|
||||||
|
|
||||||
|
@ -768,7 +744,7 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
|
||||||
iov.iov_base = smb;
|
iov.iov_base = smb;
|
||||||
iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
|
iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
|
||||||
|
|
||||||
rc = cifs_call_async(server, &iov, 1, NULL, cifs_echo_callback,
|
rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback,
|
||||||
server, CIFS_ASYNC_OP | CIFS_ECHO_OP);
|
server, CIFS_ASYNC_OP | CIFS_ECHO_OP);
|
||||||
if (rc)
|
if (rc)
|
||||||
cFYI(1, "Echo request failed: %d", rc);
|
cFYI(1, "Echo request failed: %d", rc);
|
||||||
|
@ -902,15 +878,15 @@ PsxDelete:
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon,
|
CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
|
||||||
const char *fileName, const struct nls_table *nls_codepage,
|
struct cifs_sb_info *cifs_sb)
|
||||||
int remap)
|
|
||||||
{
|
{
|
||||||
DELETE_FILE_REQ *pSMB = NULL;
|
DELETE_FILE_REQ *pSMB = NULL;
|
||||||
DELETE_FILE_RSP *pSMBr = NULL;
|
DELETE_FILE_RSP *pSMBr = NULL;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int bytes_returned;
|
int bytes_returned;
|
||||||
int name_len;
|
int name_len;
|
||||||
|
int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
|
||||||
|
|
||||||
DelFileRetry:
|
DelFileRetry:
|
||||||
rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
|
rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
|
||||||
|
@ -919,15 +895,15 @@ DelFileRetry:
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
|
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
|
||||||
name_len =
|
name_len = cifsConvertToUTF16((__le16 *) pSMB->fileName, name,
|
||||||
cifsConvertToUTF16((__le16 *) pSMB->fileName, fileName,
|
PATH_MAX, cifs_sb->local_nls,
|
||||||
PATH_MAX, nls_codepage, remap);
|
remap);
|
||||||
name_len++; /* trailing null */
|
name_len++; /* trailing null */
|
||||||
name_len *= 2;
|
name_len *= 2;
|
||||||
} else { /* BB improve check for buffer overruns BB */
|
} else { /* BB improve check for buffer overruns BB */
|
||||||
name_len = strnlen(fileName, PATH_MAX);
|
name_len = strnlen(name, PATH_MAX);
|
||||||
name_len++; /* trailing null */
|
name_len++; /* trailing null */
|
||||||
strncpy(pSMB->fileName, fileName, name_len);
|
strncpy(pSMB->fileName, name, name_len);
|
||||||
}
|
}
|
||||||
pSMB->SearchAttributes =
|
pSMB->SearchAttributes =
|
||||||
cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
|
cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
|
||||||
|
@ -1440,7 +1416,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||||
{
|
{
|
||||||
int length, len;
|
int length, len;
|
||||||
|
@ -1460,10 +1436,10 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||||
len = min_t(unsigned int, buflen, server->vals->read_rsp_size) -
|
len = min_t(unsigned int, buflen, server->vals->read_rsp_size) -
|
||||||
HEADER_SIZE(server) + 1;
|
HEADER_SIZE(server) + 1;
|
||||||
|
|
||||||
rdata->iov[0].iov_base = buf + HEADER_SIZE(server) - 1;
|
rdata->iov.iov_base = buf + HEADER_SIZE(server) - 1;
|
||||||
rdata->iov[0].iov_len = len;
|
rdata->iov.iov_len = len;
|
||||||
|
|
||||||
length = cifs_readv_from_socket(server, rdata->iov, 1, len);
|
length = cifs_readv_from_socket(server, &rdata->iov, 1, len);
|
||||||
if (length < 0)
|
if (length < 0)
|
||||||
return length;
|
return length;
|
||||||
server->total_read += length;
|
server->total_read += length;
|
||||||
|
@ -1509,19 +1485,19 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||||
len = data_offset - server->total_read;
|
len = data_offset - server->total_read;
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
/* read any junk before data into the rest of smallbuf */
|
/* read any junk before data into the rest of smallbuf */
|
||||||
rdata->iov[0].iov_base = buf + server->total_read;
|
rdata->iov.iov_base = buf + server->total_read;
|
||||||
rdata->iov[0].iov_len = len;
|
rdata->iov.iov_len = len;
|
||||||
length = cifs_readv_from_socket(server, rdata->iov, 1, len);
|
length = cifs_readv_from_socket(server, &rdata->iov, 1, len);
|
||||||
if (length < 0)
|
if (length < 0)
|
||||||
return length;
|
return length;
|
||||||
server->total_read += length;
|
server->total_read += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set up first iov for signature check */
|
/* set up first iov for signature check */
|
||||||
rdata->iov[0].iov_base = buf;
|
rdata->iov.iov_base = buf;
|
||||||
rdata->iov[0].iov_len = server->total_read;
|
rdata->iov.iov_len = server->total_read;
|
||||||
cFYI(1, "0: iov_base=%p iov_len=%zu",
|
cFYI(1, "0: iov_base=%p iov_len=%zu",
|
||||||
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
|
rdata->iov.iov_base, rdata->iov.iov_len);
|
||||||
|
|
||||||
/* how much data is in the response? */
|
/* how much data is in the response? */
|
||||||
data_len = server->ops->read_data_length(buf);
|
data_len = server->ops->read_data_length(buf);
|
||||||
|
@ -1531,23 +1507,11 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||||
return cifs_readv_discard(server, mid);
|
return cifs_readv_discard(server, mid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* marshal up the page array */
|
length = rdata->read_into_pages(server, rdata, data_len);
|
||||||
cifs_kmap_lock();
|
if (length < 0)
|
||||||
len = rdata->marshal_iov(rdata, data_len);
|
return length;
|
||||||
cifs_kmap_unlock();
|
|
||||||
data_len -= len;
|
|
||||||
|
|
||||||
/* issue the read if we have any iovecs left to fill */
|
|
||||||
if (rdata->nr_iov > 1) {
|
|
||||||
length = cifs_readv_from_socket(server, &rdata->iov[1],
|
|
||||||
rdata->nr_iov - 1, len);
|
|
||||||
if (length < 0)
|
|
||||||
return length;
|
|
||||||
server->total_read += length;
|
|
||||||
} else {
|
|
||||||
length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
server->total_read += length;
|
||||||
rdata->bytes = length;
|
rdata->bytes = length;
|
||||||
|
|
||||||
cFYI(1, "total_read=%u buflen=%u remaining=%u", server->total_read,
|
cFYI(1, "total_read=%u buflen=%u remaining=%u", server->total_read,
|
||||||
|
@ -1567,6 +1531,12 @@ cifs_readv_callback(struct mid_q_entry *mid)
|
||||||
struct cifs_readdata *rdata = mid->callback_data;
|
struct cifs_readdata *rdata = mid->callback_data;
|
||||||
struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
|
struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
|
||||||
struct TCP_Server_Info *server = tcon->ses->server;
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
struct smb_rqst rqst = { .rq_iov = &rdata->iov,
|
||||||
|
.rq_nvec = 1,
|
||||||
|
.rq_pages = rdata->pages,
|
||||||
|
.rq_npages = rdata->nr_pages,
|
||||||
|
.rq_pagesz = rdata->pagesz,
|
||||||
|
.rq_tailsz = rdata->tailsz };
|
||||||
|
|
||||||
cFYI(1, "%s: mid=%llu state=%d result=%d bytes=%u", __func__,
|
cFYI(1, "%s: mid=%llu state=%d result=%d bytes=%u", __func__,
|
||||||
mid->mid, mid->mid_state, rdata->result, rdata->bytes);
|
mid->mid, mid->mid_state, rdata->result, rdata->bytes);
|
||||||
|
@ -1578,9 +1548,8 @@ cifs_readv_callback(struct mid_q_entry *mid)
|
||||||
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
|
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
rc = cifs_verify_signature(rdata->iov, rdata->nr_iov,
|
rc = cifs_verify_signature(&rqst, server,
|
||||||
server,
|
mid->sequence_number + 1);
|
||||||
mid->sequence_number + 1);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
cERROR(1, "SMB signature verification returned "
|
cERROR(1, "SMB signature verification returned "
|
||||||
"error = %d", rc);
|
"error = %d", rc);
|
||||||
|
@ -1610,6 +1579,8 @@ cifs_async_readv(struct cifs_readdata *rdata)
|
||||||
READ_REQ *smb = NULL;
|
READ_REQ *smb = NULL;
|
||||||
int wct;
|
int wct;
|
||||||
struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
|
struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
|
||||||
|
struct smb_rqst rqst = { .rq_iov = &rdata->iov,
|
||||||
|
.rq_nvec = 1 };
|
||||||
|
|
||||||
cFYI(1, "%s: offset=%llu bytes=%u", __func__,
|
cFYI(1, "%s: offset=%llu bytes=%u", __func__,
|
||||||
rdata->offset, rdata->bytes);
|
rdata->offset, rdata->bytes);
|
||||||
|
@ -1632,7 +1603,7 @@ cifs_async_readv(struct cifs_readdata *rdata)
|
||||||
smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16));
|
smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16));
|
||||||
|
|
||||||
smb->AndXCommand = 0xFF; /* none */
|
smb->AndXCommand = 0xFF; /* none */
|
||||||
smb->Fid = rdata->cfile->netfid;
|
smb->Fid = rdata->cfile->fid.netfid;
|
||||||
smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF);
|
smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF);
|
||||||
if (wct == 12)
|
if (wct == 12)
|
||||||
smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32);
|
smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32);
|
||||||
|
@ -1649,13 +1620,12 @@ cifs_async_readv(struct cifs_readdata *rdata)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 4 for RFC1001 length + 1 for BCC */
|
/* 4 for RFC1001 length + 1 for BCC */
|
||||||
rdata->iov[0].iov_base = smb;
|
rdata->iov.iov_base = smb;
|
||||||
rdata->iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
|
rdata->iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
|
||||||
|
|
||||||
kref_get(&rdata->refcount);
|
kref_get(&rdata->refcount);
|
||||||
rc = cifs_call_async(tcon->ses->server, rdata->iov, 1,
|
rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive,
|
||||||
cifs_readv_receive, cifs_readv_callback,
|
cifs_readv_callback, rdata, 0);
|
||||||
rdata, 0);
|
|
||||||
|
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
cifs_stats_inc(&tcon->stats.cifs_stats.num_reads);
|
cifs_stats_inc(&tcon->stats.cifs_stats.num_reads);
|
||||||
|
@ -1926,6 +1896,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
|
||||||
{
|
{
|
||||||
int i, rc;
|
int i, rc;
|
||||||
struct inode *inode = wdata->cfile->dentry->d_inode;
|
struct inode *inode = wdata->cfile->dentry->d_inode;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
|
|
||||||
for (i = 0; i < wdata->nr_pages; i++) {
|
for (i = 0; i < wdata->nr_pages; i++) {
|
||||||
lock_page(wdata->pages[i]);
|
lock_page(wdata->pages[i]);
|
||||||
|
@ -1933,7 +1904,8 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
rc = cifs_async_writev(wdata);
|
server = tlink_tcon(wdata->cfile->tlink)->ses->server;
|
||||||
|
rc = server->ops->async_writev(wdata);
|
||||||
} while (rc == -EAGAIN);
|
} while (rc == -EAGAIN);
|
||||||
|
|
||||||
for (i = 0; i < wdata->nr_pages; i++) {
|
for (i = 0; i < wdata->nr_pages; i++) {
|
||||||
|
@ -2053,11 +2025,12 @@ cifs_writev_callback(struct mid_q_entry *mid)
|
||||||
int
|
int
|
||||||
cifs_async_writev(struct cifs_writedata *wdata)
|
cifs_async_writev(struct cifs_writedata *wdata)
|
||||||
{
|
{
|
||||||
int i, rc = -EACCES;
|
int rc = -EACCES;
|
||||||
WRITE_REQ *smb = NULL;
|
WRITE_REQ *smb = NULL;
|
||||||
int wct;
|
int wct;
|
||||||
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
|
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
|
||||||
struct kvec *iov = NULL;
|
struct kvec iov;
|
||||||
|
struct smb_rqst rqst = { };
|
||||||
|
|
||||||
if (tcon->ses->capabilities & CAP_LARGE_FILES) {
|
if (tcon->ses->capabilities & CAP_LARGE_FILES) {
|
||||||
wct = 14;
|
wct = 14;
|
||||||
|
@ -2073,18 +2046,11 @@ cifs_async_writev(struct cifs_writedata *wdata)
|
||||||
if (rc)
|
if (rc)
|
||||||
goto async_writev_out;
|
goto async_writev_out;
|
||||||
|
|
||||||
/* 1 iov per page + 1 for header */
|
|
||||||
iov = kzalloc((wdata->nr_pages + 1) * sizeof(*iov), GFP_NOFS);
|
|
||||||
if (iov == NULL) {
|
|
||||||
rc = -ENOMEM;
|
|
||||||
goto async_writev_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid);
|
smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid);
|
||||||
smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16));
|
smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16));
|
||||||
|
|
||||||
smb->AndXCommand = 0xFF; /* none */
|
smb->AndXCommand = 0xFF; /* none */
|
||||||
smb->Fid = wdata->cfile->netfid;
|
smb->Fid = wdata->cfile->fid.netfid;
|
||||||
smb->OffsetLow = cpu_to_le32(wdata->offset & 0xFFFFFFFF);
|
smb->OffsetLow = cpu_to_le32(wdata->offset & 0xFFFFFFFF);
|
||||||
if (wct == 14)
|
if (wct == 14)
|
||||||
smb->OffsetHigh = cpu_to_le32(wdata->offset >> 32);
|
smb->OffsetHigh = cpu_to_le32(wdata->offset >> 32);
|
||||||
|
@ -2096,18 +2062,15 @@ cifs_async_writev(struct cifs_writedata *wdata)
|
||||||
cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
|
cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
|
||||||
|
|
||||||
/* 4 for RFC1001 length + 1 for BCC */
|
/* 4 for RFC1001 length + 1 for BCC */
|
||||||
iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1;
|
iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1;
|
||||||
iov[0].iov_base = smb;
|
iov.iov_base = smb;
|
||||||
|
|
||||||
/*
|
rqst.rq_iov = &iov;
|
||||||
* This function should marshal up the page array into the kvec
|
rqst.rq_nvec = 1;
|
||||||
* array, reserving [0] for the header. It should kmap the pages
|
rqst.rq_pages = wdata->pages;
|
||||||
* and set the iov_len properly for each one. It may also set
|
rqst.rq_npages = wdata->nr_pages;
|
||||||
* wdata->bytes too.
|
rqst.rq_pagesz = wdata->pagesz;
|
||||||
*/
|
rqst.rq_tailsz = wdata->tailsz;
|
||||||
cifs_kmap_lock();
|
|
||||||
wdata->marshal_iov(iov, wdata);
|
|
||||||
cifs_kmap_unlock();
|
|
||||||
|
|
||||||
cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes);
|
cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes);
|
||||||
|
|
||||||
|
@ -2123,32 +2086,26 @@ cifs_async_writev(struct cifs_writedata *wdata)
|
||||||
(struct smb_com_writex_req *)smb;
|
(struct smb_com_writex_req *)smb;
|
||||||
inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5);
|
inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5);
|
||||||
put_bcc(wdata->bytes + 5, &smbw->hdr);
|
put_bcc(wdata->bytes + 5, &smbw->hdr);
|
||||||
iov[0].iov_len += 4; /* pad bigger by four bytes */
|
iov.iov_len += 4; /* pad bigger by four bytes */
|
||||||
}
|
}
|
||||||
|
|
||||||
kref_get(&wdata->refcount);
|
kref_get(&wdata->refcount);
|
||||||
rc = cifs_call_async(tcon->ses->server, iov, wdata->nr_pages + 1,
|
rc = cifs_call_async(tcon->ses->server, &rqst, NULL,
|
||||||
NULL, cifs_writev_callback, wdata, 0);
|
cifs_writev_callback, wdata, 0);
|
||||||
|
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
|
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
|
||||||
else
|
else
|
||||||
kref_put(&wdata->refcount, cifs_writedata_release);
|
kref_put(&wdata->refcount, cifs_writedata_release);
|
||||||
|
|
||||||
/* send is done, unmap pages */
|
|
||||||
for (i = 0; i < wdata->nr_pages; i++)
|
|
||||||
kunmap(wdata->pages[i]);
|
|
||||||
|
|
||||||
async_writev_out:
|
async_writev_out:
|
||||||
cifs_small_buf_release(smb);
|
cifs_small_buf_release(smb);
|
||||||
kfree(iov);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
|
CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
|
||||||
unsigned int *nbytes, struct kvec *iov, int n_vec,
|
unsigned int *nbytes, struct kvec *iov, int n_vec)
|
||||||
const int long_op)
|
|
||||||
{
|
{
|
||||||
int rc = -EACCES;
|
int rc = -EACCES;
|
||||||
WRITE_REQ *pSMB = NULL;
|
WRITE_REQ *pSMB = NULL;
|
||||||
|
@ -2219,8 +2176,7 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
|
||||||
iov[0].iov_len = smb_hdr_len + 8;
|
iov[0].iov_len = smb_hdr_len + 8;
|
||||||
|
|
||||||
|
|
||||||
rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
|
rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0);
|
||||||
long_op);
|
|
||||||
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
|
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
cFYI(1, "Send error Write2 = %d", rc);
|
cFYI(1, "Send error Write2 = %d", rc);
|
||||||
|
@ -2557,8 +2513,8 @@ CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)
|
||||||
|
|
||||||
int
|
int
|
||||||
CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
|
CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *fromName, const char *toName,
|
const char *from_name, const char *to_name,
|
||||||
const struct nls_table *nls_codepage, int remap)
|
struct cifs_sb_info *cifs_sb)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
RENAME_REQ *pSMB = NULL;
|
RENAME_REQ *pSMB = NULL;
|
||||||
|
@ -2566,6 +2522,7 @@ CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
int bytes_returned;
|
int bytes_returned;
|
||||||
int name_len, name_len2;
|
int name_len, name_len2;
|
||||||
__u16 count;
|
__u16 count;
|
||||||
|
int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
|
||||||
|
|
||||||
cFYI(1, "In CIFSSMBRename");
|
cFYI(1, "In CIFSSMBRename");
|
||||||
renameRetry:
|
renameRetry:
|
||||||
|
@ -2580,9 +2537,9 @@ renameRetry:
|
||||||
ATTR_DIRECTORY);
|
ATTR_DIRECTORY);
|
||||||
|
|
||||||
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
|
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
|
||||||
name_len =
|
name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName,
|
||||||
cifsConvertToUTF16((__le16 *) pSMB->OldFileName, fromName,
|
from_name, PATH_MAX,
|
||||||
PATH_MAX, nls_codepage, remap);
|
cifs_sb->local_nls, remap);
|
||||||
name_len++; /* trailing null */
|
name_len++; /* trailing null */
|
||||||
name_len *= 2;
|
name_len *= 2;
|
||||||
pSMB->OldFileName[name_len] = 0x04; /* pad */
|
pSMB->OldFileName[name_len] = 0x04; /* pad */
|
||||||
|
@ -2590,17 +2547,18 @@ renameRetry:
|
||||||
pSMB->OldFileName[name_len + 1] = 0x00;
|
pSMB->OldFileName[name_len + 1] = 0x00;
|
||||||
name_len2 =
|
name_len2 =
|
||||||
cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
|
cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
|
||||||
toName, PATH_MAX, nls_codepage, remap);
|
to_name, PATH_MAX, cifs_sb->local_nls,
|
||||||
|
remap);
|
||||||
name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
|
name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
|
||||||
name_len2 *= 2; /* convert to bytes */
|
name_len2 *= 2; /* convert to bytes */
|
||||||
} else { /* BB improve the check for buffer overruns BB */
|
} else { /* BB improve the check for buffer overruns BB */
|
||||||
name_len = strnlen(fromName, PATH_MAX);
|
name_len = strnlen(from_name, PATH_MAX);
|
||||||
name_len++; /* trailing null */
|
name_len++; /* trailing null */
|
||||||
strncpy(pSMB->OldFileName, fromName, name_len);
|
strncpy(pSMB->OldFileName, from_name, name_len);
|
||||||
name_len2 = strnlen(toName, PATH_MAX);
|
name_len2 = strnlen(to_name, PATH_MAX);
|
||||||
name_len2++; /* trailing null */
|
name_len2++; /* trailing null */
|
||||||
pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
|
pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
|
||||||
strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
|
strncpy(&pSMB->OldFileName[name_len + 1], to_name, name_len2);
|
||||||
name_len2++; /* trailing null */
|
name_len2++; /* trailing null */
|
||||||
name_len2++; /* signature byte */
|
name_len2++; /* signature byte */
|
||||||
}
|
}
|
||||||
|
@ -2948,8 +2906,8 @@ createHardLinkRetry:
|
||||||
|
|
||||||
int
|
int
|
||||||
CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
|
CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *fromName, const char *toName,
|
const char *from_name, const char *to_name,
|
||||||
const struct nls_table *nls_codepage, int remap)
|
struct cifs_sb_info *cifs_sb)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
NT_RENAME_REQ *pSMB = NULL;
|
NT_RENAME_REQ *pSMB = NULL;
|
||||||
|
@ -2957,6 +2915,7 @@ CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
int bytes_returned;
|
int bytes_returned;
|
||||||
int name_len, name_len2;
|
int name_len, name_len2;
|
||||||
__u16 count;
|
__u16 count;
|
||||||
|
int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
|
||||||
|
|
||||||
cFYI(1, "In CIFSCreateHardLink");
|
cFYI(1, "In CIFSCreateHardLink");
|
||||||
winCreateHardLinkRetry:
|
winCreateHardLinkRetry:
|
||||||
|
@ -2976,8 +2935,8 @@ winCreateHardLinkRetry:
|
||||||
|
|
||||||
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
|
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
|
||||||
name_len =
|
name_len =
|
||||||
cifsConvertToUTF16((__le16 *) pSMB->OldFileName, fromName,
|
cifsConvertToUTF16((__le16 *) pSMB->OldFileName, from_name,
|
||||||
PATH_MAX, nls_codepage, remap);
|
PATH_MAX, cifs_sb->local_nls, remap);
|
||||||
name_len++; /* trailing null */
|
name_len++; /* trailing null */
|
||||||
name_len *= 2;
|
name_len *= 2;
|
||||||
|
|
||||||
|
@ -2986,17 +2945,18 @@ winCreateHardLinkRetry:
|
||||||
pSMB->OldFileName[name_len + 1] = 0x00; /* pad */
|
pSMB->OldFileName[name_len + 1] = 0x00; /* pad */
|
||||||
name_len2 =
|
name_len2 =
|
||||||
cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
|
cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
|
||||||
toName, PATH_MAX, nls_codepage, remap);
|
to_name, PATH_MAX, cifs_sb->local_nls,
|
||||||
|
remap);
|
||||||
name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
|
name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
|
||||||
name_len2 *= 2; /* convert to bytes */
|
name_len2 *= 2; /* convert to bytes */
|
||||||
} else { /* BB improve the check for buffer overruns BB */
|
} else { /* BB improve the check for buffer overruns BB */
|
||||||
name_len = strnlen(fromName, PATH_MAX);
|
name_len = strnlen(from_name, PATH_MAX);
|
||||||
name_len++; /* trailing null */
|
name_len++; /* trailing null */
|
||||||
strncpy(pSMB->OldFileName, fromName, name_len);
|
strncpy(pSMB->OldFileName, from_name, name_len);
|
||||||
name_len2 = strnlen(toName, PATH_MAX);
|
name_len2 = strnlen(to_name, PATH_MAX);
|
||||||
name_len2++; /* trailing null */
|
name_len2++; /* trailing null */
|
||||||
pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
|
pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
|
||||||
strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
|
strncpy(&pSMB->OldFileName[name_len + 1], to_name, name_len2);
|
||||||
name_len2++; /* trailing null */
|
name_len2++; /* trailing null */
|
||||||
name_len2++; /* signature byte */
|
name_len2++; /* signature byte */
|
||||||
}
|
}
|
||||||
|
@ -4254,10 +4214,9 @@ UnixQPathInfoRetry:
|
||||||
/* xid, tcon, searchName and codepage are input parms, rest are returned */
|
/* xid, tcon, searchName and codepage are input parms, rest are returned */
|
||||||
int
|
int
|
||||||
CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
|
CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *searchName,
|
const char *searchName, struct cifs_sb_info *cifs_sb,
|
||||||
const struct nls_table *nls_codepage,
|
|
||||||
__u16 *pnetfid, __u16 search_flags,
|
__u16 *pnetfid, __u16 search_flags,
|
||||||
struct cifs_search_info *psrch_inf, int remap, const char dirsep)
|
struct cifs_search_info *psrch_inf, bool msearch)
|
||||||
{
|
{
|
||||||
/* level 257 SMB_ */
|
/* level 257 SMB_ */
|
||||||
TRANSACTION2_FFIRST_REQ *pSMB = NULL;
|
TRANSACTION2_FFIRST_REQ *pSMB = NULL;
|
||||||
|
@ -4265,8 +4224,9 @@ CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
T2_FFIRST_RSP_PARMS *parms;
|
T2_FFIRST_RSP_PARMS *parms;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int bytes_returned = 0;
|
int bytes_returned = 0;
|
||||||
int name_len;
|
int name_len, remap;
|
||||||
__u16 params, byte_count;
|
__u16 params, byte_count;
|
||||||
|
struct nls_table *nls_codepage;
|
||||||
|
|
||||||
cFYI(1, "In FindFirst for %s", searchName);
|
cFYI(1, "In FindFirst for %s", searchName);
|
||||||
|
|
||||||
|
@ -4276,6 +4236,9 @@ findFirstRetry:
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
nls_codepage = cifs_sb->local_nls;
|
||||||
|
remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
|
||||||
|
|
||||||
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
|
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
|
||||||
name_len =
|
name_len =
|
||||||
cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName,
|
cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName,
|
||||||
|
@ -4284,24 +4247,29 @@ findFirstRetry:
|
||||||
it got remapped to 0xF03A as if it were part of the
|
it got remapped to 0xF03A as if it were part of the
|
||||||
directory name instead of a wildcard */
|
directory name instead of a wildcard */
|
||||||
name_len *= 2;
|
name_len *= 2;
|
||||||
pSMB->FileName[name_len] = dirsep;
|
if (msearch) {
|
||||||
pSMB->FileName[name_len+1] = 0;
|
pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb);
|
||||||
pSMB->FileName[name_len+2] = '*';
|
pSMB->FileName[name_len+1] = 0;
|
||||||
pSMB->FileName[name_len+3] = 0;
|
pSMB->FileName[name_len+2] = '*';
|
||||||
name_len += 4; /* now the trailing null */
|
pSMB->FileName[name_len+3] = 0;
|
||||||
pSMB->FileName[name_len] = 0; /* null terminate just in case */
|
name_len += 4; /* now the trailing null */
|
||||||
pSMB->FileName[name_len+1] = 0;
|
/* null terminate just in case */
|
||||||
name_len += 2;
|
pSMB->FileName[name_len] = 0;
|
||||||
|
pSMB->FileName[name_len+1] = 0;
|
||||||
|
name_len += 2;
|
||||||
|
}
|
||||||
} else { /* BB add check for overrun of SMB buf BB */
|
} else { /* BB add check for overrun of SMB buf BB */
|
||||||
name_len = strnlen(searchName, PATH_MAX);
|
name_len = strnlen(searchName, PATH_MAX);
|
||||||
/* BB fix here and in unicode clause above ie
|
/* BB fix here and in unicode clause above ie
|
||||||
if (name_len > buffersize-header)
|
if (name_len > buffersize-header)
|
||||||
free buffer exit; BB */
|
free buffer exit; BB */
|
||||||
strncpy(pSMB->FileName, searchName, name_len);
|
strncpy(pSMB->FileName, searchName, name_len);
|
||||||
pSMB->FileName[name_len] = dirsep;
|
if (msearch) {
|
||||||
pSMB->FileName[name_len+1] = '*';
|
pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb);
|
||||||
pSMB->FileName[name_len+2] = 0;
|
pSMB->FileName[name_len+1] = '*';
|
||||||
name_len += 3;
|
pSMB->FileName[name_len+2] = 0;
|
||||||
|
name_len += 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params = 12 + name_len /* includes null */ ;
|
params = 12 + name_len /* includes null */ ;
|
||||||
|
@ -4389,7 +4357,8 @@ findFirstRetry:
|
||||||
psrch_inf->last_entry = psrch_inf->srch_entries_start +
|
psrch_inf->last_entry = psrch_inf->srch_entries_start +
|
||||||
lnoff;
|
lnoff;
|
||||||
|
|
||||||
*pnetfid = parms->SearchHandle;
|
if (pnetfid)
|
||||||
|
*pnetfid = parms->SearchHandle;
|
||||||
} else {
|
} else {
|
||||||
cifs_buf_release(pSMB);
|
cifs_buf_release(pSMB);
|
||||||
}
|
}
|
||||||
|
@ -5417,16 +5386,16 @@ QFSPosixRetry:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* We can not use write of zero bytes trick to
|
/*
|
||||||
set file size due to need for large file support. Also note that
|
* We can not use write of zero bytes trick to set file size due to need for
|
||||||
this SetPathInfo is preferred to SetFileInfo based method in next
|
* large file support. Also note that this SetPathInfo is preferred to
|
||||||
routine which is only needed to work around a sharing violation bug
|
* SetFileInfo based method in next routine which is only needed to work around
|
||||||
in Samba which this routine can run into */
|
* a sharing violation bugin Samba which this routine can run into.
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
|
CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *fileName, __u64 size, bool SetAllocation,
|
const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb,
|
||||||
const struct nls_table *nls_codepage, int remap)
|
bool set_allocation)
|
||||||
{
|
{
|
||||||
struct smb_com_transaction2_spi_req *pSMB = NULL;
|
struct smb_com_transaction2_spi_req *pSMB = NULL;
|
||||||
struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
|
struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
|
||||||
|
@ -5434,6 +5403,8 @@ CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
int name_len;
|
int name_len;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int bytes_returned = 0;
|
int bytes_returned = 0;
|
||||||
|
int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
|
||||||
|
|
||||||
__u16 params, byte_count, data_count, param_offset, offset;
|
__u16 params, byte_count, data_count, param_offset, offset;
|
||||||
|
|
||||||
cFYI(1, "In SetEOF");
|
cFYI(1, "In SetEOF");
|
||||||
|
@ -5445,14 +5416,14 @@ SetEOFRetry:
|
||||||
|
|
||||||
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
|
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
|
||||||
name_len =
|
name_len =
|
||||||
cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
|
cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name,
|
||||||
PATH_MAX, nls_codepage, remap);
|
PATH_MAX, cifs_sb->local_nls, remap);
|
||||||
name_len++; /* trailing null */
|
name_len++; /* trailing null */
|
||||||
name_len *= 2;
|
name_len *= 2;
|
||||||
} else { /* BB improve the check for buffer overruns BB */
|
} else { /* BB improve the check for buffer overruns BB */
|
||||||
name_len = strnlen(fileName, PATH_MAX);
|
name_len = strnlen(file_name, PATH_MAX);
|
||||||
name_len++; /* trailing null */
|
name_len++; /* trailing null */
|
||||||
strncpy(pSMB->FileName, fileName, name_len);
|
strncpy(pSMB->FileName, file_name, name_len);
|
||||||
}
|
}
|
||||||
params = 6 + name_len;
|
params = 6 + name_len;
|
||||||
data_count = sizeof(struct file_end_of_file_info);
|
data_count = sizeof(struct file_end_of_file_info);
|
||||||
|
@ -5466,7 +5437,7 @@ SetEOFRetry:
|
||||||
param_offset = offsetof(struct smb_com_transaction2_spi_req,
|
param_offset = offsetof(struct smb_com_transaction2_spi_req,
|
||||||
InformationLevel) - 4;
|
InformationLevel) - 4;
|
||||||
offset = param_offset + params;
|
offset = param_offset + params;
|
||||||
if (SetAllocation) {
|
if (set_allocation) {
|
||||||
if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
|
if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
|
||||||
pSMB->InformationLevel =
|
pSMB->InformationLevel =
|
||||||
cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
|
cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
|
||||||
|
@ -5513,8 +5484,8 @@ SetEOFRetry:
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, __u64 size,
|
CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
__u16 fid, __u32 pid_of_opener, bool SetAllocation)
|
struct cifsFileInfo *cfile, __u64 size, bool set_allocation)
|
||||||
{
|
{
|
||||||
struct smb_com_transaction2_sfi_req *pSMB = NULL;
|
struct smb_com_transaction2_sfi_req *pSMB = NULL;
|
||||||
struct file_end_of_file_info *parm_data;
|
struct file_end_of_file_info *parm_data;
|
||||||
|
@ -5528,8 +5499,8 @@ CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, __u64 size,
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
|
pSMB->hdr.Pid = cpu_to_le16((__u16)cfile->pid);
|
||||||
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
|
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(cfile->pid >> 16));
|
||||||
|
|
||||||
params = 6;
|
params = 6;
|
||||||
pSMB->MaxSetupCount = 0;
|
pSMB->MaxSetupCount = 0;
|
||||||
|
@ -5558,8 +5529,8 @@ CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, __u64 size,
|
||||||
+ offset);
|
+ offset);
|
||||||
pSMB->DataOffset = cpu_to_le16(offset);
|
pSMB->DataOffset = cpu_to_le16(offset);
|
||||||
parm_data->FileSize = cpu_to_le64(size);
|
parm_data->FileSize = cpu_to_le64(size);
|
||||||
pSMB->Fid = fid;
|
pSMB->Fid = cfile->fid.netfid;
|
||||||
if (SetAllocation) {
|
if (set_allocation) {
|
||||||
if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
|
if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
|
||||||
pSMB->InformationLevel =
|
pSMB->InformationLevel =
|
||||||
cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
|
cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
|
||||||
|
|
|
@ -82,8 +82,7 @@ enum {
|
||||||
Opt_serverino, Opt_noserverino,
|
Opt_serverino, Opt_noserverino,
|
||||||
Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl,
|
Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl,
|
||||||
Opt_acl, Opt_noacl, Opt_locallease,
|
Opt_acl, Opt_noacl, Opt_locallease,
|
||||||
Opt_sign, Opt_seal, Opt_direct,
|
Opt_sign, Opt_seal, Opt_noac,
|
||||||
Opt_strictcache, Opt_noac,
|
|
||||||
Opt_fsc, Opt_mfsymlinks,
|
Opt_fsc, Opt_mfsymlinks,
|
||||||
Opt_multiuser, Opt_sloppy,
|
Opt_multiuser, Opt_sloppy,
|
||||||
|
|
||||||
|
@ -160,10 +159,6 @@ static const match_table_t cifs_mount_option_tokens = {
|
||||||
{ Opt_locallease, "locallease" },
|
{ Opt_locallease, "locallease" },
|
||||||
{ Opt_sign, "sign" },
|
{ Opt_sign, "sign" },
|
||||||
{ Opt_seal, "seal" },
|
{ Opt_seal, "seal" },
|
||||||
{ Opt_direct, "direct" },
|
|
||||||
{ Opt_direct, "directio" },
|
|
||||||
{ Opt_direct, "forcedirectio" },
|
|
||||||
{ Opt_strictcache, "strictcache" },
|
|
||||||
{ Opt_noac, "noac" },
|
{ Opt_noac, "noac" },
|
||||||
{ Opt_fsc, "fsc" },
|
{ Opt_fsc, "fsc" },
|
||||||
{ Opt_mfsymlinks, "mfsymlinks" },
|
{ Opt_mfsymlinks, "mfsymlinks" },
|
||||||
|
@ -277,6 +272,7 @@ static const match_table_t cifs_cacheflavor_tokens = {
|
||||||
static const match_table_t cifs_smb_version_tokens = {
|
static const match_table_t cifs_smb_version_tokens = {
|
||||||
{ Smb_1, SMB1_VERSION_STRING },
|
{ Smb_1, SMB1_VERSION_STRING },
|
||||||
{ Smb_21, SMB21_VERSION_STRING },
|
{ Smb_21, SMB21_VERSION_STRING },
|
||||||
|
{ Smb_30, SMB30_VERSION_STRING },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ip_connect(struct TCP_Server_Info *server);
|
static int ip_connect(struct TCP_Server_Info *server);
|
||||||
|
@ -819,6 +815,10 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||||
cifs_dump_mem("Bad SMB: ", buf,
|
cifs_dump_mem("Bad SMB: ", buf,
|
||||||
min_t(unsigned int, server->total_read, 48));
|
min_t(unsigned int, server->total_read, 48));
|
||||||
|
|
||||||
|
if (server->ops->is_status_pending &&
|
||||||
|
server->ops->is_status_pending(buf, server, length))
|
||||||
|
return -1;
|
||||||
|
|
||||||
if (!mid)
|
if (!mid)
|
||||||
return length;
|
return length;
|
||||||
|
|
||||||
|
@ -1075,6 +1075,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
|
||||||
vol->ops = &smb21_operations;
|
vol->ops = &smb21_operations;
|
||||||
vol->vals = &smb21_values;
|
vol->vals = &smb21_values;
|
||||||
break;
|
break;
|
||||||
|
case Smb_30:
|
||||||
|
vol->ops = &smb21_operations; /* currently identical with 2.1 */
|
||||||
|
vol->vals = &smb30_values;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
cERROR(1, "Unknown vers= option specified: %s", value);
|
cERROR(1, "Unknown vers= option specified: %s", value);
|
||||||
|
@ -1101,8 +1105,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||||
char *string = NULL;
|
char *string = NULL;
|
||||||
char *tmp_end, *value;
|
char *tmp_end, *value;
|
||||||
char delim;
|
char delim;
|
||||||
bool cache_specified = false;
|
|
||||||
static bool cache_warned = false;
|
|
||||||
|
|
||||||
separator[0] = ',';
|
separator[0] = ',';
|
||||||
separator[1] = 0;
|
separator[1] = 0;
|
||||||
|
@ -1134,6 +1136,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||||
/* default to using server inode numbers where available */
|
/* default to using server inode numbers where available */
|
||||||
vol->server_ino = 1;
|
vol->server_ino = 1;
|
||||||
|
|
||||||
|
/* default is to use strict cifs caching semantics */
|
||||||
|
vol->strict_io = true;
|
||||||
|
|
||||||
vol->actimeo = CIFS_DEF_ACTIMEO;
|
vol->actimeo = CIFS_DEF_ACTIMEO;
|
||||||
|
|
||||||
/* FIXME: add autonegotiation -- for now, SMB1 is default */
|
/* FIXME: add autonegotiation -- for now, SMB1 is default */
|
||||||
|
@ -1317,22 +1322,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||||
*/
|
*/
|
||||||
vol->seal = 1;
|
vol->seal = 1;
|
||||||
break;
|
break;
|
||||||
case Opt_direct:
|
|
||||||
cache_specified = true;
|
|
||||||
vol->direct_io = true;
|
|
||||||
vol->strict_io = false;
|
|
||||||
cERROR(1, "The \"directio\" option will be removed in "
|
|
||||||
"3.7. Please switch to the \"cache=none\" "
|
|
||||||
"option.");
|
|
||||||
break;
|
|
||||||
case Opt_strictcache:
|
|
||||||
cache_specified = true;
|
|
||||||
vol->direct_io = false;
|
|
||||||
vol->strict_io = true;
|
|
||||||
cERROR(1, "The \"strictcache\" option will be removed "
|
|
||||||
"in 3.7. Please switch to the \"cache=strict\" "
|
|
||||||
"option.");
|
|
||||||
break;
|
|
||||||
case Opt_noac:
|
case Opt_noac:
|
||||||
printk(KERN_WARNING "CIFS: Mount option noac not "
|
printk(KERN_WARNING "CIFS: Mount option noac not "
|
||||||
"supported. Instead set "
|
"supported. Instead set "
|
||||||
|
@ -1676,8 +1665,13 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||||
if (string == NULL)
|
if (string == NULL)
|
||||||
goto out_nomem;
|
goto out_nomem;
|
||||||
|
|
||||||
if (strnicmp(string, "TCP_NODELAY", 11) == 0)
|
if (strnicmp(string, "TCP_NODELAY", 11) == 0) {
|
||||||
|
printk(KERN_WARNING "CIFS: the "
|
||||||
|
"sockopt=TCP_NODELAY option has been "
|
||||||
|
"deprecated and will be removed "
|
||||||
|
"in 3.9\n");
|
||||||
vol->sockopt_tcp_nodelay = 1;
|
vol->sockopt_tcp_nodelay = 1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Opt_netbiosname:
|
case Opt_netbiosname:
|
||||||
string = match_strdup(args);
|
string = match_strdup(args);
|
||||||
|
@ -1762,7 +1756,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||||
goto cifs_parse_mount_err;
|
goto cifs_parse_mount_err;
|
||||||
break;
|
break;
|
||||||
case Opt_cache:
|
case Opt_cache:
|
||||||
cache_specified = true;
|
|
||||||
string = match_strdup(args);
|
string = match_strdup(args);
|
||||||
if (string == NULL)
|
if (string == NULL)
|
||||||
goto out_nomem;
|
goto out_nomem;
|
||||||
|
@ -1813,14 +1806,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
||||||
printk(KERN_NOTICE "CIFS: ignoring forcegid mount option "
|
printk(KERN_NOTICE "CIFS: ignoring forcegid mount option "
|
||||||
"specified with no gid= option.\n");
|
"specified with no gid= option.\n");
|
||||||
|
|
||||||
/* FIXME: remove this block in 3.7 */
|
|
||||||
if (!cache_specified && !cache_warned) {
|
|
||||||
cache_warned = true;
|
|
||||||
printk(KERN_NOTICE "CIFS: no cache= option specified, using "
|
|
||||||
"\"cache=loose\". This default will change "
|
|
||||||
"to \"cache=strict\" in 3.7.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree(mountdata_copy);
|
kfree(mountdata_copy);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -2636,6 +2621,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
|
||||||
tcon->retry = volume_info->retry;
|
tcon->retry = volume_info->retry;
|
||||||
tcon->nocase = volume_info->nocase;
|
tcon->nocase = volume_info->nocase;
|
||||||
tcon->local_lease = volume_info->local_lease;
|
tcon->local_lease = volume_info->local_lease;
|
||||||
|
INIT_LIST_HEAD(&tcon->pending_opens);
|
||||||
|
|
||||||
spin_lock(&cifs_tcp_ses_lock);
|
spin_lock(&cifs_tcp_ses_lock);
|
||||||
list_add(&tcon->tcon_list, &ses->tcon_list);
|
list_add(&tcon->tcon_list, &ses->tcon_list);
|
||||||
|
@ -3261,146 +3247,6 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
|
||||||
"mount option supported");
|
"mount option supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* When the server supports very large reads and writes via POSIX extensions,
|
|
||||||
* we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not
|
|
||||||
* including the RFC1001 length.
|
|
||||||
*
|
|
||||||
* Note that this might make for "interesting" allocation problems during
|
|
||||||
* writeback however as we have to allocate an array of pointers for the
|
|
||||||
* pages. A 16M write means ~32kb page array with PAGE_CACHE_SIZE == 4096.
|
|
||||||
*
|
|
||||||
* For reads, there is a similar problem as we need to allocate an array
|
|
||||||
* of kvecs to handle the receive, though that should only need to be done
|
|
||||||
* once.
|
|
||||||
*/
|
|
||||||
#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4)
|
|
||||||
#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When the server doesn't allow large posix writes, only allow a rsize/wsize
|
|
||||||
* of 2^17-1 minus the size of the call header. That allows for a read or
|
|
||||||
* write up to the maximum size described by RFC1002.
|
|
||||||
*/
|
|
||||||
#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4)
|
|
||||||
#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The default wsize is 1M. find_get_pages seems to return a maximum of 256
|
|
||||||
* pages in a single call. With PAGE_CACHE_SIZE == 4k, this means we can fill
|
|
||||||
* a single wsize request with a single call.
|
|
||||||
*/
|
|
||||||
#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Windows only supports a max of 60kb reads and 65535 byte writes. Default to
|
|
||||||
* those values when posix extensions aren't in force. In actuality here, we
|
|
||||||
* use 65536 to allow for a write that is a multiple of 4k. Most servers seem
|
|
||||||
* to be ok with the extra byte even though Windows doesn't send writes that
|
|
||||||
* are that large.
|
|
||||||
*
|
|
||||||
* Citation:
|
|
||||||
*
|
|
||||||
* http://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx
|
|
||||||
*/
|
|
||||||
#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024)
|
|
||||||
#define CIFS_DEFAULT_NON_POSIX_WSIZE (65536)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* On hosts with high memory, we can't currently support wsize/rsize that are
|
|
||||||
* larger than we can kmap at once. Cap the rsize/wsize at
|
|
||||||
* LAST_PKMAP * PAGE_SIZE. We'll never be able to fill a read or write request
|
|
||||||
* larger than that anyway.
|
|
||||||
*/
|
|
||||||
#ifdef CONFIG_HIGHMEM
|
|
||||||
#define CIFS_KMAP_SIZE_LIMIT (LAST_PKMAP * PAGE_CACHE_SIZE)
|
|
||||||
#else /* CONFIG_HIGHMEM */
|
|
||||||
#define CIFS_KMAP_SIZE_LIMIT (1<<24)
|
|
||||||
#endif /* CONFIG_HIGHMEM */
|
|
||||||
|
|
||||||
static unsigned int
|
|
||||||
cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info)
|
|
||||||
{
|
|
||||||
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
|
||||||
struct TCP_Server_Info *server = tcon->ses->server;
|
|
||||||
unsigned int wsize;
|
|
||||||
|
|
||||||
/* start with specified wsize, or default */
|
|
||||||
if (pvolume_info->wsize)
|
|
||||||
wsize = pvolume_info->wsize;
|
|
||||||
else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
|
|
||||||
wsize = CIFS_DEFAULT_IOSIZE;
|
|
||||||
else
|
|
||||||
wsize = CIFS_DEFAULT_NON_POSIX_WSIZE;
|
|
||||||
|
|
||||||
/* can server support 24-bit write sizes? (via UNIX extensions) */
|
|
||||||
if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
|
|
||||||
wsize = min_t(unsigned int, wsize, CIFS_MAX_RFC1002_WSIZE);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set?
|
|
||||||
* Limit it to max buffer offered by the server, minus the size of the
|
|
||||||
* WRITEX header, not including the 4 byte RFC1001 length.
|
|
||||||
*/
|
|
||||||
if (!(server->capabilities & CAP_LARGE_WRITE_X) ||
|
|
||||||
(!(server->capabilities & CAP_UNIX) &&
|
|
||||||
(server->sec_mode & (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED))))
|
|
||||||
wsize = min_t(unsigned int, wsize,
|
|
||||||
server->maxBuf - sizeof(WRITE_REQ) + 4);
|
|
||||||
|
|
||||||
/* limit to the amount that we can kmap at once */
|
|
||||||
wsize = min_t(unsigned int, wsize, CIFS_KMAP_SIZE_LIMIT);
|
|
||||||
|
|
||||||
/* hard limit of CIFS_MAX_WSIZE */
|
|
||||||
wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE);
|
|
||||||
|
|
||||||
return wsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int
|
|
||||||
cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info)
|
|
||||||
{
|
|
||||||
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
|
||||||
struct TCP_Server_Info *server = tcon->ses->server;
|
|
||||||
unsigned int rsize, defsize;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set default value...
|
|
||||||
*
|
|
||||||
* HACK alert! Ancient servers have very small buffers. Even though
|
|
||||||
* MS-CIFS indicates that servers are only limited by the client's
|
|
||||||
* bufsize for reads, testing against win98se shows that it throws
|
|
||||||
* INVALID_PARAMETER errors if you try to request too large a read.
|
|
||||||
* OS/2 just sends back short reads.
|
|
||||||
*
|
|
||||||
* If the server doesn't advertise CAP_LARGE_READ_X, then assume that
|
|
||||||
* it can't handle a read request larger than its MaxBufferSize either.
|
|
||||||
*/
|
|
||||||
if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP))
|
|
||||||
defsize = CIFS_DEFAULT_IOSIZE;
|
|
||||||
else if (server->capabilities & CAP_LARGE_READ_X)
|
|
||||||
defsize = CIFS_DEFAULT_NON_POSIX_RSIZE;
|
|
||||||
else
|
|
||||||
defsize = server->maxBuf - sizeof(READ_RSP);
|
|
||||||
|
|
||||||
rsize = pvolume_info->rsize ? pvolume_info->rsize : defsize;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to
|
|
||||||
* the client's MaxBufferSize.
|
|
||||||
*/
|
|
||||||
if (!(server->capabilities & CAP_LARGE_READ_X))
|
|
||||||
rsize = min_t(unsigned int, CIFSMaxBufSize, rsize);
|
|
||||||
|
|
||||||
/* limit to the amount that we can kmap at once */
|
|
||||||
rsize = min_t(unsigned int, rsize, CIFS_KMAP_SIZE_LIMIT);
|
|
||||||
|
|
||||||
/* hard limit of CIFS_MAX_RSIZE */
|
|
||||||
rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE);
|
|
||||||
|
|
||||||
return rsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cleanup_volume_info_contents(struct smb_vol *volume_info)
|
cleanup_volume_info_contents(struct smb_vol *volume_info)
|
||||||
{
|
{
|
||||||
|
@ -3651,8 +3497,8 @@ try_mount_again:
|
||||||
if (!tcon->ipc && server->ops->qfs_tcon)
|
if (!tcon->ipc && server->ops->qfs_tcon)
|
||||||
server->ops->qfs_tcon(xid, tcon);
|
server->ops->qfs_tcon(xid, tcon);
|
||||||
|
|
||||||
cifs_sb->wsize = cifs_negotiate_wsize(tcon, volume_info);
|
cifs_sb->wsize = server->ops->negotiate_wsize(tcon, volume_info);
|
||||||
cifs_sb->rsize = cifs_negotiate_rsize(tcon, volume_info);
|
cifs_sb->rsize = server->ops->negotiate_rsize(tcon, volume_info);
|
||||||
|
|
||||||
/* tune readahead according to rsize */
|
/* tune readahead according to rsize */
|
||||||
cifs_sb->bdi.ra_pages = cifs_sb->rsize / PAGE_CACHE_SIZE;
|
cifs_sb->bdi.ra_pages = cifs_sb->rsize / PAGE_CACHE_SIZE;
|
||||||
|
|
130
fs/cifs/dir.c
130
fs/cifs/dir.c
|
@ -160,17 +160,18 @@ check_name(struct dentry *direntry)
|
||||||
static int
|
static int
|
||||||
cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||||
struct tcon_link *tlink, unsigned oflags, umode_t mode,
|
struct tcon_link *tlink, unsigned oflags, umode_t mode,
|
||||||
__u32 *oplock, __u16 *fileHandle, int *created)
|
__u32 *oplock, struct cifs_fid *fid, int *created)
|
||||||
{
|
{
|
||||||
int rc = -ENOENT;
|
int rc = -ENOENT;
|
||||||
int create_options = CREATE_NOT_DIR;
|
int create_options = CREATE_NOT_DIR;
|
||||||
int desiredAccess;
|
int desired_access;
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
struct cifs_tcon *tcon = tlink_tcon(tlink);
|
struct cifs_tcon *tcon = tlink_tcon(tlink);
|
||||||
char *full_path = NULL;
|
char *full_path = NULL;
|
||||||
FILE_ALL_INFO *buf = NULL;
|
FILE_ALL_INFO *buf = NULL;
|
||||||
struct inode *newinode = NULL;
|
struct inode *newinode = NULL;
|
||||||
int disposition;
|
int disposition;
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
|
||||||
*oplock = 0;
|
*oplock = 0;
|
||||||
if (tcon->ses->server->oplocks)
|
if (tcon->ses->server->oplocks)
|
||||||
|
@ -185,8 +186,8 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||||
if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
|
if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
|
||||||
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
||||||
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
||||||
rc = cifs_posix_open(full_path, &newinode,
|
rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode,
|
||||||
inode->i_sb, mode, oflags, oplock, fileHandle, xid);
|
oflags, oplock, &fid->netfid, xid);
|
||||||
switch (rc) {
|
switch (rc) {
|
||||||
case 0:
|
case 0:
|
||||||
if (newinode == NULL) {
|
if (newinode == NULL) {
|
||||||
|
@ -202,7 +203,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||||
* close it and proceed as if it were a normal
|
* close it and proceed as if it were a normal
|
||||||
* lookup.
|
* lookup.
|
||||||
*/
|
*/
|
||||||
CIFSSMBClose(xid, tcon, *fileHandle);
|
CIFSSMBClose(xid, tcon, fid->netfid);
|
||||||
goto cifs_create_get_file_info;
|
goto cifs_create_get_file_info;
|
||||||
}
|
}
|
||||||
/* success, no need to query */
|
/* success, no need to query */
|
||||||
|
@ -244,11 +245,11 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
desiredAccess = 0;
|
desired_access = 0;
|
||||||
if (OPEN_FMODE(oflags) & FMODE_READ)
|
if (OPEN_FMODE(oflags) & FMODE_READ)
|
||||||
desiredAccess |= GENERIC_READ; /* is this too little? */
|
desired_access |= GENERIC_READ; /* is this too little? */
|
||||||
if (OPEN_FMODE(oflags) & FMODE_WRITE)
|
if (OPEN_FMODE(oflags) & FMODE_WRITE)
|
||||||
desiredAccess |= GENERIC_WRITE;
|
desired_access |= GENERIC_WRITE;
|
||||||
|
|
||||||
disposition = FILE_OVERWRITE_IF;
|
disposition = FILE_OVERWRITE_IF;
|
||||||
if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
|
if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
|
||||||
|
@ -260,8 +261,15 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||||
else
|
else
|
||||||
cFYI(1, "Create flag not set in create function");
|
cFYI(1, "Create flag not set in create function");
|
||||||
|
|
||||||
/* BB add processing to set equivalent of mode - e.g. via CreateX with
|
/*
|
||||||
ACLs */
|
* BB add processing to set equivalent of mode - e.g. via CreateX with
|
||||||
|
* ACLs
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!server->ops->open) {
|
||||||
|
rc = -ENOSYS;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||||
if (buf == NULL) {
|
if (buf == NULL) {
|
||||||
|
@ -279,28 +287,18 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||||
if (backup_cred(cifs_sb))
|
if (backup_cred(cifs_sb))
|
||||||
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
||||||
|
|
||||||
if (tcon->ses->capabilities & CAP_NT_SMBS)
|
rc = server->ops->open(xid, tcon, full_path, disposition,
|
||||||
rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
|
desired_access, create_options, fid, oplock,
|
||||||
desiredAccess, create_options,
|
buf, cifs_sb);
|
||||||
fileHandle, oplock, buf, cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
else
|
|
||||||
rc = -EIO; /* no NT SMB support fall into legacy open below */
|
|
||||||
|
|
||||||
if (rc == -EIO) {
|
|
||||||
/* old server, retry the open legacy style */
|
|
||||||
rc = SMBLegacyOpen(xid, tcon, full_path, disposition,
|
|
||||||
desiredAccess, create_options,
|
|
||||||
fileHandle, oplock, buf, cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
}
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
cFYI(1, "cifs_create returned 0x%x", rc);
|
cFYI(1, "cifs_create returned 0x%x", rc);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If Open reported that we actually created a file
|
/*
|
||||||
then we now have to set the mode if possible */
|
* If Open reported that we actually created a file then we now have to
|
||||||
|
* set the mode if possible.
|
||||||
|
*/
|
||||||
if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) {
|
if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) {
|
||||||
struct cifs_unix_set_info_args args = {
|
struct cifs_unix_set_info_args args = {
|
||||||
.mode = mode,
|
.mode = mode,
|
||||||
|
@ -321,11 +319,13 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||||
args.uid = NO_CHANGE_64;
|
args.uid = NO_CHANGE_64;
|
||||||
args.gid = NO_CHANGE_64;
|
args.gid = NO_CHANGE_64;
|
||||||
}
|
}
|
||||||
CIFSSMBUnixSetFileInfo(xid, tcon, &args, *fileHandle,
|
CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid,
|
||||||
current->tgid);
|
current->tgid);
|
||||||
} else {
|
} else {
|
||||||
/* BB implement mode setting via Windows security
|
/*
|
||||||
descriptors e.g. */
|
* BB implement mode setting via Windows security
|
||||||
|
* descriptors e.g.
|
||||||
|
*/
|
||||||
/* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
|
/* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
|
||||||
|
|
||||||
/* Could set r/o dos attribute if mode & 0222 == 0 */
|
/* Could set r/o dos attribute if mode & 0222 == 0 */
|
||||||
|
@ -334,12 +334,14 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
|
||||||
cifs_create_get_file_info:
|
cifs_create_get_file_info:
|
||||||
/* server might mask mode so we have to query for it */
|
/* server might mask mode so we have to query for it */
|
||||||
if (tcon->unix_ext)
|
if (tcon->unix_ext)
|
||||||
rc = cifs_get_inode_info_unix(&newinode, full_path,
|
rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb,
|
||||||
inode->i_sb, xid);
|
xid);
|
||||||
else {
|
else {
|
||||||
rc = cifs_get_inode_info(&newinode, full_path, buf,
|
rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb,
|
||||||
inode->i_sb, xid, fileHandle);
|
xid, &fid->netfid);
|
||||||
if (newinode) {
|
if (newinode) {
|
||||||
|
if (server->ops->set_lease_key)
|
||||||
|
server->ops->set_lease_key(newinode, fid);
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
|
||||||
newinode->i_mode = mode;
|
newinode->i_mode = mode;
|
||||||
if ((*oplock & CIFS_CREATE_ACTION) &&
|
if ((*oplock & CIFS_CREATE_ACTION) &&
|
||||||
|
@ -356,7 +358,8 @@ cifs_create_get_file_info:
|
||||||
cifs_create_set_dentry:
|
cifs_create_set_dentry:
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
cFYI(1, "Create worked, get_inode_info failed rc = %d", rc);
|
cFYI(1, "Create worked, get_inode_info failed rc = %d", rc);
|
||||||
CIFSSMBClose(xid, tcon, *fileHandle);
|
if (server->ops->close)
|
||||||
|
server->ops->close(xid, tcon, fid);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
d_drop(direntry);
|
d_drop(direntry);
|
||||||
|
@ -377,11 +380,14 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
struct tcon_link *tlink;
|
struct tcon_link *tlink;
|
||||||
struct cifs_tcon *tcon;
|
struct cifs_tcon *tcon;
|
||||||
__u16 fileHandle;
|
struct TCP_Server_Info *server;
|
||||||
|
struct cifs_fid fid;
|
||||||
|
struct cifs_pending_open open;
|
||||||
__u32 oplock;
|
__u32 oplock;
|
||||||
struct cifsFileInfo *pfile_info;
|
struct cifsFileInfo *file_info;
|
||||||
|
|
||||||
/* Posix open is only called (at lookup time) for file create now. For
|
/*
|
||||||
|
* Posix open is only called (at lookup time) for file create now. For
|
||||||
* opens (rather than creates), because we do not know if it is a file
|
* opens (rather than creates), because we do not know if it is a file
|
||||||
* or directory yet, and current Samba no longer allows us to do posix
|
* or directory yet, and current Samba no longer allows us to do posix
|
||||||
* open on dirs, we could end up wasting an open call on what turns out
|
* open on dirs, we could end up wasting an open call on what turns out
|
||||||
|
@ -413,22 +419,34 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
|
||||||
goto out_free_xid;
|
goto out_free_xid;
|
||||||
|
|
||||||
tcon = tlink_tcon(tlink);
|
tcon = tlink_tcon(tlink);
|
||||||
|
server = tcon->ses->server;
|
||||||
|
|
||||||
|
if (server->ops->new_lease_key)
|
||||||
|
server->ops->new_lease_key(&fid);
|
||||||
|
|
||||||
|
cifs_add_pending_open(&fid, tlink, &open);
|
||||||
|
|
||||||
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
|
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
|
||||||
&oplock, &fileHandle, opened);
|
&oplock, &fid, opened);
|
||||||
|
|
||||||
if (rc)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
rc = finish_open(file, direntry, generic_file_open, opened);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
CIFSSMBClose(xid, tcon, fileHandle);
|
cifs_del_pending_open(&open);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
pfile_info = cifs_new_fileinfo(fileHandle, file, tlink, oplock);
|
rc = finish_open(file, direntry, generic_file_open, opened);
|
||||||
if (pfile_info == NULL) {
|
if (rc) {
|
||||||
CIFSSMBClose(xid, tcon, fileHandle);
|
if (server->ops->close)
|
||||||
|
server->ops->close(xid, tcon, &fid);
|
||||||
|
cifs_del_pending_open(&open);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_info = cifs_new_fileinfo(&fid, file, tlink, oplock);
|
||||||
|
if (file_info == NULL) {
|
||||||
|
if (server->ops->close)
|
||||||
|
server->ops->close(xid, tcon, &fid);
|
||||||
|
cifs_del_pending_open(&open);
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,7 +471,9 @@ int cifs_create(struct inode *inode, struct dentry *direntry, umode_t mode,
|
||||||
*/
|
*/
|
||||||
unsigned oflags = O_EXCL | O_CREAT | O_RDWR;
|
unsigned oflags = O_EXCL | O_CREAT | O_RDWR;
|
||||||
struct tcon_link *tlink;
|
struct tcon_link *tlink;
|
||||||
__u16 fileHandle;
|
struct cifs_tcon *tcon;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
|
struct cifs_fid fid;
|
||||||
__u32 oplock;
|
__u32 oplock;
|
||||||
int created = FILE_CREATED;
|
int created = FILE_CREATED;
|
||||||
|
|
||||||
|
@ -465,10 +485,16 @@ int cifs_create(struct inode *inode, struct dentry *direntry, umode_t mode,
|
||||||
if (IS_ERR(tlink))
|
if (IS_ERR(tlink))
|
||||||
goto out_free_xid;
|
goto out_free_xid;
|
||||||
|
|
||||||
|
tcon = tlink_tcon(tlink);
|
||||||
|
server = tcon->ses->server;
|
||||||
|
|
||||||
|
if (server->ops->new_lease_key)
|
||||||
|
server->ops->new_lease_key(&fid);
|
||||||
|
|
||||||
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
|
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
|
||||||
&oplock, &fileHandle, &created);
|
&oplock, &fid, &created);
|
||||||
if (!rc)
|
if (!rc && server->ops->close)
|
||||||
CIFSSMBClose(xid, tlink_tcon(tlink), fileHandle);
|
server->ops->close(xid, tcon, &fid);
|
||||||
|
|
||||||
cifs_put_tlink(tlink);
|
cifs_put_tlink(tlink);
|
||||||
out_free_xid:
|
out_free_xid:
|
||||||
|
|
1051
fs/cifs/file.c
1051
fs/cifs/file.c
File diff suppressed because it is too large
Load Diff
443
fs/cifs/inode.c
443
fs/cifs/inode.c
|
@ -282,7 +282,8 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
|
||||||
fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;
|
fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cifs_get_file_info_unix(struct file *filp)
|
static int
|
||||||
|
cifs_get_file_info_unix(struct file *filp)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
|
@ -294,7 +295,7 @@ int cifs_get_file_info_unix(struct file *filp)
|
||||||
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
||||||
|
|
||||||
xid = get_xid();
|
xid = get_xid();
|
||||||
rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data);
|
rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
|
cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
|
||||||
} else if (rc == -EREMOTE) {
|
} else if (rc == -EREMOTE) {
|
||||||
|
@ -550,7 +551,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
||||||
fattr->cf_gid = cifs_sb->mnt_gid;
|
fattr->cf_gid = cifs_sb->mnt_gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cifs_get_file_info(struct file *filp)
|
static int
|
||||||
|
cifs_get_file_info(struct file *filp)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
|
@ -560,9 +562,13 @@ int cifs_get_file_info(struct file *filp)
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
struct cifsFileInfo *cfile = filp->private_data;
|
struct cifsFileInfo *cfile = filp->private_data;
|
||||||
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
|
||||||
|
if (!server->ops->query_file_info)
|
||||||
|
return -ENOSYS;
|
||||||
|
|
||||||
xid = get_xid();
|
xid = get_xid();
|
||||||
rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data);
|
rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data);
|
||||||
switch (rc) {
|
switch (rc) {
|
||||||
case 0:
|
case 0:
|
||||||
cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false);
|
cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false);
|
||||||
|
@ -601,7 +607,9 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||||
FILE_ALL_INFO *data, struct super_block *sb, int xid,
|
FILE_ALL_INFO *data, struct super_block *sb, int xid,
|
||||||
const __u16 *fid)
|
const __u16 *fid)
|
||||||
{
|
{
|
||||||
int rc = 0, tmprc;
|
bool validinum = false;
|
||||||
|
__u16 srchflgs;
|
||||||
|
int rc = 0, tmprc = ENOSYS;
|
||||||
struct cifs_tcon *tcon;
|
struct cifs_tcon *tcon;
|
||||||
struct TCP_Server_Info *server;
|
struct TCP_Server_Info *server;
|
||||||
struct tcon_link *tlink;
|
struct tcon_link *tlink;
|
||||||
|
@ -609,6 +617,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||||
char *buf = NULL;
|
char *buf = NULL;
|
||||||
bool adjust_tz = false;
|
bool adjust_tz = false;
|
||||||
struct cifs_fattr fattr;
|
struct cifs_fattr fattr;
|
||||||
|
struct cifs_search_info *srchinf = NULL;
|
||||||
|
|
||||||
tlink = cifs_sb_tlink(cifs_sb);
|
tlink = cifs_sb_tlink(cifs_sb);
|
||||||
if (IS_ERR(tlink))
|
if (IS_ERR(tlink))
|
||||||
|
@ -647,9 +656,38 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||||
} else if (rc == -EREMOTE) {
|
} else if (rc == -EREMOTE) {
|
||||||
cifs_create_dfs_fattr(&fattr, sb);
|
cifs_create_dfs_fattr(&fattr, sb);
|
||||||
rc = 0;
|
rc = 0;
|
||||||
} else {
|
} else if (rc == -EACCES && backup_cred(cifs_sb)) {
|
||||||
|
srchinf = kzalloc(sizeof(struct cifs_search_info),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (srchinf == NULL) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto cgii_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
srchinf->endOfSearch = false;
|
||||||
|
srchinf->info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
|
||||||
|
|
||||||
|
srchflgs = CIFS_SEARCH_CLOSE_ALWAYS |
|
||||||
|
CIFS_SEARCH_CLOSE_AT_END |
|
||||||
|
CIFS_SEARCH_BACKUP_SEARCH;
|
||||||
|
|
||||||
|
rc = CIFSFindFirst(xid, tcon, full_path,
|
||||||
|
cifs_sb, NULL, srchflgs, srchinf, false);
|
||||||
|
if (!rc) {
|
||||||
|
data =
|
||||||
|
(FILE_ALL_INFO *)srchinf->srch_entries_start;
|
||||||
|
|
||||||
|
cifs_dir_info_to_fattr(&fattr,
|
||||||
|
(FILE_DIRECTORY_INFO *)data, cifs_sb);
|
||||||
|
fattr.cf_uniqueid = le64_to_cpu(
|
||||||
|
((SEARCH_ID_FULL_DIR_INFO *)data)->UniqueId);
|
||||||
|
validinum = true;
|
||||||
|
|
||||||
|
cifs_buf_release(srchinf->ntwrk_buf_start);
|
||||||
|
}
|
||||||
|
kfree(srchinf);
|
||||||
|
} else
|
||||||
goto cgii_exit;
|
goto cgii_exit;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If an inode wasn't passed in, then get the inode number
|
* If an inode wasn't passed in, then get the inode number
|
||||||
|
@ -660,23 +698,21 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
|
||||||
*/
|
*/
|
||||||
if (*inode == NULL) {
|
if (*inode == NULL) {
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
|
||||||
if (server->ops->get_srv_inum)
|
if (validinum == false) {
|
||||||
tmprc = server->ops->get_srv_inum(xid, tcon,
|
if (server->ops->get_srv_inum)
|
||||||
cifs_sb, full_path, &fattr.cf_uniqueid,
|
tmprc = server->ops->get_srv_inum(xid,
|
||||||
data);
|
tcon, cifs_sb, full_path,
|
||||||
else
|
&fattr.cf_uniqueid, data);
|
||||||
tmprc = -ENOSYS;
|
if (tmprc) {
|
||||||
if (tmprc || !fattr.cf_uniqueid) {
|
cFYI(1, "GetSrvInodeNum rc %d", tmprc);
|
||||||
cFYI(1, "GetSrvInodeNum rc %d", tmprc);
|
fattr.cf_uniqueid = iunique(sb, ROOT_I);
|
||||||
fattr.cf_uniqueid = iunique(sb, ROOT_I);
|
cifs_autodisable_serverino(cifs_sb);
|
||||||
cifs_autodisable_serverino(cifs_sb);
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else
|
||||||
fattr.cf_uniqueid = iunique(sb, ROOT_I);
|
fattr.cf_uniqueid = iunique(sb, ROOT_I);
|
||||||
}
|
} else
|
||||||
} else {
|
|
||||||
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
|
||||||
}
|
|
||||||
|
|
||||||
/* query for SFU type info if supported and needed */
|
/* query for SFU type info if supported and needed */
|
||||||
if (fattr.cf_cifsattrs & ATTR_SYSTEM &&
|
if (fattr.cf_cifsattrs & ATTR_SYSTEM &&
|
||||||
|
@ -876,25 +912,22 @@ out:
|
||||||
return inode;
|
return inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid,
|
cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid,
|
||||||
char *full_path, __u32 dosattr)
|
char *full_path, __u32 dosattr)
|
||||||
{
|
{
|
||||||
int rc;
|
|
||||||
int oplock = 0;
|
|
||||||
__u16 netfid;
|
|
||||||
__u32 netpid;
|
|
||||||
bool set_time = false;
|
bool set_time = false;
|
||||||
struct cifsFileInfo *open_file;
|
|
||||||
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
struct tcon_link *tlink = NULL;
|
struct TCP_Server_Info *server;
|
||||||
struct cifs_tcon *pTcon;
|
|
||||||
FILE_BASIC_INFO info_buf;
|
FILE_BASIC_INFO info_buf;
|
||||||
|
|
||||||
if (attrs == NULL)
|
if (attrs == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
server = cifs_sb_master_tcon(cifs_sb)->ses->server;
|
||||||
|
if (!server->ops->set_file_info)
|
||||||
|
return -ENOSYS;
|
||||||
|
|
||||||
if (attrs->ia_valid & ATTR_ATIME) {
|
if (attrs->ia_valid & ATTR_ATIME) {
|
||||||
set_time = true;
|
set_time = true;
|
||||||
info_buf.LastAccessTime =
|
info_buf.LastAccessTime =
|
||||||
|
@ -925,81 +958,17 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid,
|
||||||
info_buf.CreationTime = 0; /* don't change */
|
info_buf.CreationTime = 0; /* don't change */
|
||||||
info_buf.Attributes = cpu_to_le32(dosattr);
|
info_buf.Attributes = cpu_to_le32(dosattr);
|
||||||
|
|
||||||
/*
|
return server->ops->set_file_info(inode, full_path, &info_buf, xid);
|
||||||
* If the file is already open for write, just use that fileid
|
|
||||||
*/
|
|
||||||
open_file = find_writable_file(cifsInode, true);
|
|
||||||
if (open_file) {
|
|
||||||
netfid = open_file->netfid;
|
|
||||||
netpid = open_file->pid;
|
|
||||||
pTcon = tlink_tcon(open_file->tlink);
|
|
||||||
goto set_via_filehandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
tlink = cifs_sb_tlink(cifs_sb);
|
|
||||||
if (IS_ERR(tlink)) {
|
|
||||||
rc = PTR_ERR(tlink);
|
|
||||||
tlink = NULL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
pTcon = tlink_tcon(tlink);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* NT4 apparently returns success on this call, but it doesn't
|
|
||||||
* really work.
|
|
||||||
*/
|
|
||||||
if (!(pTcon->ses->flags & CIFS_SES_NT4)) {
|
|
||||||
rc = CIFSSMBSetPathInfo(xid, pTcon, full_path,
|
|
||||||
&info_buf, cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
if (rc == 0) {
|
|
||||||
cifsInode->cifsAttrs = dosattr;
|
|
||||||
goto out;
|
|
||||||
} else if (rc != -EOPNOTSUPP && rc != -EINVAL)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
cFYI(1, "calling SetFileInfo since SetPathInfo for "
|
|
||||||
"times not supported by this server");
|
|
||||||
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
|
|
||||||
SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
|
|
||||||
CREATE_NOT_DIR, &netfid, &oplock,
|
|
||||||
NULL, cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
|
|
||||||
if (rc != 0) {
|
|
||||||
if (rc == -EIO)
|
|
||||||
rc = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
netpid = current->tgid;
|
|
||||||
|
|
||||||
set_via_filehandle:
|
|
||||||
rc = CIFSSMBSetFileInfo(xid, pTcon, &info_buf, netfid, netpid);
|
|
||||||
if (!rc)
|
|
||||||
cifsInode->cifsAttrs = dosattr;
|
|
||||||
|
|
||||||
if (open_file == NULL)
|
|
||||||
CIFSSMBClose(xid, pTcon, netfid);
|
|
||||||
else
|
|
||||||
cifsFileInfo_put(open_file);
|
|
||||||
out:
|
|
||||||
if (tlink != NULL)
|
|
||||||
cifs_put_tlink(tlink);
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* open the given file (if it isn't already), set the DELETE_ON_CLOSE bit
|
* Open the given file (if it isn't already), set the DELETE_ON_CLOSE bit
|
||||||
* and rename it to a random name that hopefully won't conflict with
|
* and rename it to a random name that hopefully won't conflict with
|
||||||
* anything else.
|
* anything else.
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
cifs_rename_pending_delete(char *full_path, struct dentry *dentry,
|
cifs_rename_pending_delete(const char *full_path, struct dentry *dentry,
|
||||||
unsigned int xid)
|
const unsigned int xid)
|
||||||
{
|
{
|
||||||
int oplock = 0;
|
int oplock = 0;
|
||||||
int rc;
|
int rc;
|
||||||
|
@ -1136,6 +1105,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||||
struct tcon_link *tlink;
|
struct tcon_link *tlink;
|
||||||
struct cifs_tcon *tcon;
|
struct cifs_tcon *tcon;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
struct iattr *attrs = NULL;
|
struct iattr *attrs = NULL;
|
||||||
__u32 dosattr = 0, origattr = 0;
|
__u32 dosattr = 0, origattr = 0;
|
||||||
|
|
||||||
|
@ -1145,6 +1115,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
if (IS_ERR(tlink))
|
if (IS_ERR(tlink))
|
||||||
return PTR_ERR(tlink);
|
return PTR_ERR(tlink);
|
||||||
tcon = tlink_tcon(tlink);
|
tcon = tlink_tcon(tlink);
|
||||||
|
server = tcon->ses->server;
|
||||||
|
|
||||||
xid = get_xid();
|
xid = get_xid();
|
||||||
|
|
||||||
|
@ -1167,8 +1138,12 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
}
|
}
|
||||||
|
|
||||||
retry_std_delete:
|
retry_std_delete:
|
||||||
rc = CIFSSMBDelFile(xid, tcon, full_path, cifs_sb->local_nls,
|
if (!server->ops->unlink) {
|
||||||
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
rc = -ENOSYS;
|
||||||
|
goto psx_del_no_retry;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = server->ops->unlink(xid, tcon, full_path, cifs_sb);
|
||||||
|
|
||||||
psx_del_no_retry:
|
psx_del_no_retry:
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
|
@ -1177,9 +1152,14 @@ psx_del_no_retry:
|
||||||
} else if (rc == -ENOENT) {
|
} else if (rc == -ENOENT) {
|
||||||
d_drop(dentry);
|
d_drop(dentry);
|
||||||
} else if (rc == -ETXTBSY) {
|
} else if (rc == -ETXTBSY) {
|
||||||
rc = cifs_rename_pending_delete(full_path, dentry, xid);
|
if (server->ops->rename_pending_delete) {
|
||||||
if (rc == 0)
|
rc = server->ops->rename_pending_delete(full_path,
|
||||||
cifs_drop_nlink(inode);
|
dentry, xid);
|
||||||
|
if (rc == 0)
|
||||||
|
cifs_drop_nlink(inode);
|
||||||
|
}
|
||||||
|
if (rc == -ETXTBSY)
|
||||||
|
rc = -EBUSY;
|
||||||
} else if ((rc == -EACCES) && (dosattr == 0) && inode) {
|
} else if ((rc == -EACCES) && (dosattr == 0) && inode) {
|
||||||
attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
|
attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
|
||||||
if (attrs == NULL) {
|
if (attrs == NULL) {
|
||||||
|
@ -1227,34 +1207,33 @@ unlink_out:
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cifs_mkdir_qinfo(struct inode *inode, struct dentry *dentry, umode_t mode,
|
cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
|
||||||
const char *full_path, struct cifs_sb_info *cifs_sb,
|
const char *full_path, struct cifs_sb_info *cifs_sb,
|
||||||
struct cifs_tcon *tcon, const unsigned int xid)
|
struct cifs_tcon *tcon, const unsigned int xid)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct inode *newinode = NULL;
|
struct inode *inode = NULL;
|
||||||
|
|
||||||
if (tcon->unix_ext)
|
if (tcon->unix_ext)
|
||||||
rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb,
|
rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb,
|
||||||
xid);
|
xid);
|
||||||
else
|
else
|
||||||
rc = cifs_get_inode_info(&newinode, full_path, NULL,
|
rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb,
|
||||||
inode->i_sb, xid, NULL);
|
xid, NULL);
|
||||||
|
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
d_instantiate(dentry, newinode);
|
|
||||||
/*
|
/*
|
||||||
* setting nlink not necessary except in cases where we failed to get it
|
* setting nlink not necessary except in cases where we failed to get it
|
||||||
* from the server or was set bogus
|
* from the server or was set bogus. Also, since this is a brand new
|
||||||
|
* inode, no need to grab the i_lock before setting the i_nlink.
|
||||||
*/
|
*/
|
||||||
spin_lock(&dentry->d_inode->i_lock);
|
if (inode->i_nlink < 2)
|
||||||
if ((dentry->d_inode) && (dentry->d_inode->i_nlink < 2))
|
set_nlink(inode, 2);
|
||||||
set_nlink(dentry->d_inode, 2);
|
|
||||||
spin_unlock(&dentry->d_inode->i_lock);
|
|
||||||
mode &= ~current_umask();
|
mode &= ~current_umask();
|
||||||
/* must turn on setgid bit if parent dir has it */
|
/* must turn on setgid bit if parent dir has it */
|
||||||
if (inode->i_mode & S_ISGID)
|
if (parent->i_mode & S_ISGID)
|
||||||
mode |= S_ISGID;
|
mode |= S_ISGID;
|
||||||
|
|
||||||
if (tcon->unix_ext) {
|
if (tcon->unix_ext) {
|
||||||
|
@ -1267,8 +1246,8 @@ cifs_mkdir_qinfo(struct inode *inode, struct dentry *dentry, umode_t mode,
|
||||||
};
|
};
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
|
||||||
args.uid = (__u64)current_fsuid();
|
args.uid = (__u64)current_fsuid();
|
||||||
if (inode->i_mode & S_ISGID)
|
if (parent->i_mode & S_ISGID)
|
||||||
args.gid = (__u64)inode->i_gid;
|
args.gid = (__u64)parent->i_gid;
|
||||||
else
|
else
|
||||||
args.gid = (__u64)current_fsgid();
|
args.gid = (__u64)current_fsgid();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1283,22 +1262,20 @@ cifs_mkdir_qinfo(struct inode *inode, struct dentry *dentry, umode_t mode,
|
||||||
struct TCP_Server_Info *server = tcon->ses->server;
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
|
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
|
||||||
(mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo)
|
(mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo)
|
||||||
server->ops->mkdir_setinfo(newinode, full_path, cifs_sb,
|
server->ops->mkdir_setinfo(inode, full_path, cifs_sb,
|
||||||
tcon, xid);
|
tcon, xid);
|
||||||
if (dentry->d_inode) {
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
|
inode->i_mode = (mode | S_IFDIR);
|
||||||
dentry->d_inode->i_mode = (mode | S_IFDIR);
|
|
||||||
|
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
|
||||||
dentry->d_inode->i_uid = current_fsuid();
|
inode->i_uid = current_fsuid();
|
||||||
if (inode->i_mode & S_ISGID)
|
if (inode->i_mode & S_ISGID)
|
||||||
dentry->d_inode->i_gid = inode->i_gid;
|
inode->i_gid = parent->i_gid;
|
||||||
else
|
else
|
||||||
dentry->d_inode->i_gid =
|
inode->i_gid = current_fsgid();
|
||||||
current_fsgid();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
d_instantiate(dentry, inode);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1495,29 +1472,32 @@ rmdir_exit:
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cifs_do_rename(unsigned int xid, struct dentry *from_dentry,
|
cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
|
||||||
const char *fromPath, struct dentry *to_dentry,
|
const char *from_path, struct dentry *to_dentry,
|
||||||
const char *toPath)
|
const char *to_path)
|
||||||
{
|
{
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb);
|
||||||
struct tcon_link *tlink;
|
struct tcon_link *tlink;
|
||||||
struct cifs_tcon *pTcon;
|
struct cifs_tcon *tcon;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
__u16 srcfid;
|
__u16 srcfid;
|
||||||
int oplock, rc;
|
int oplock, rc;
|
||||||
|
|
||||||
tlink = cifs_sb_tlink(cifs_sb);
|
tlink = cifs_sb_tlink(cifs_sb);
|
||||||
if (IS_ERR(tlink))
|
if (IS_ERR(tlink))
|
||||||
return PTR_ERR(tlink);
|
return PTR_ERR(tlink);
|
||||||
pTcon = tlink_tcon(tlink);
|
tcon = tlink_tcon(tlink);
|
||||||
|
server = tcon->ses->server;
|
||||||
|
|
||||||
|
if (!server->ops->rename)
|
||||||
|
return -ENOSYS;
|
||||||
|
|
||||||
/* try path-based rename first */
|
/* try path-based rename first */
|
||||||
rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls,
|
rc = server->ops->rename(xid, tcon, from_path, to_path, cifs_sb);
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* don't bother with rename by filehandle unless file is busy and
|
* Don't bother with rename by filehandle unless file is busy and
|
||||||
* source Note that cross directory moves do not work with
|
* source. Note that cross directory moves do not work with
|
||||||
* rename by filehandle to various Windows servers.
|
* rename by filehandle to various Windows servers.
|
||||||
*/
|
*/
|
||||||
if (rc == 0 || rc != -ETXTBSY)
|
if (rc == 0 || rc != -ETXTBSY)
|
||||||
|
@ -1528,29 +1508,28 @@ cifs_do_rename(unsigned int xid, struct dentry *from_dentry,
|
||||||
goto do_rename_exit;
|
goto do_rename_exit;
|
||||||
|
|
||||||
/* open the file to be renamed -- we need DELETE perms */
|
/* open the file to be renamed -- we need DELETE perms */
|
||||||
rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE,
|
rc = CIFSSMBOpen(xid, tcon, from_path, FILE_OPEN, DELETE,
|
||||||
CREATE_NOT_DIR, &srcfid, &oplock, NULL,
|
CREATE_NOT_DIR, &srcfid, &oplock, NULL,
|
||||||
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
|
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
rc = CIFSSMBRenameOpenFile(xid, pTcon, srcfid,
|
rc = CIFSSMBRenameOpenFile(xid, tcon, srcfid,
|
||||||
(const char *) to_dentry->d_name.name,
|
(const char *) to_dentry->d_name.name,
|
||||||
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
|
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
CIFSSMBClose(xid, tcon, srcfid);
|
||||||
CIFSSMBClose(xid, pTcon, srcfid);
|
|
||||||
}
|
}
|
||||||
do_rename_exit:
|
do_rename_exit:
|
||||||
cifs_put_tlink(tlink);
|
cifs_put_tlink(tlink);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
|
int
|
||||||
struct inode *target_dir, struct dentry *target_dentry)
|
cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
|
||||||
|
struct inode *target_dir, struct dentry *target_dentry)
|
||||||
{
|
{
|
||||||
char *fromName = NULL;
|
char *from_name = NULL;
|
||||||
char *toName = NULL;
|
char *to_name = NULL;
|
||||||
struct cifs_sb_info *cifs_sb;
|
struct cifs_sb_info *cifs_sb;
|
||||||
struct tcon_link *tlink;
|
struct tcon_link *tlink;
|
||||||
struct cifs_tcon *tcon;
|
struct cifs_tcon *tcon;
|
||||||
|
@ -1571,25 +1550,25 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
|
||||||
* we already have the rename sem so we do not need to
|
* we already have the rename sem so we do not need to
|
||||||
* grab it again here to protect the path integrity
|
* grab it again here to protect the path integrity
|
||||||
*/
|
*/
|
||||||
fromName = build_path_from_dentry(source_dentry);
|
from_name = build_path_from_dentry(source_dentry);
|
||||||
if (fromName == NULL) {
|
if (from_name == NULL) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto cifs_rename_exit;
|
goto cifs_rename_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
toName = build_path_from_dentry(target_dentry);
|
to_name = build_path_from_dentry(target_dentry);
|
||||||
if (toName == NULL) {
|
if (to_name == NULL) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto cifs_rename_exit;
|
goto cifs_rename_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = cifs_do_rename(xid, source_dentry, fromName,
|
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
|
||||||
target_dentry, toName);
|
to_name);
|
||||||
|
|
||||||
if (rc == -EEXIST && tcon->unix_ext) {
|
if (rc == -EEXIST && tcon->unix_ext) {
|
||||||
/*
|
/*
|
||||||
* Are src and dst hardlinks of same inode? We can
|
* Are src and dst hardlinks of same inode? We can only tell
|
||||||
* only tell with unix extensions enabled
|
* with unix extensions enabled.
|
||||||
*/
|
*/
|
||||||
info_buf_source =
|
info_buf_source =
|
||||||
kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
|
kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
|
||||||
|
@ -1600,19 +1579,19 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
|
||||||
}
|
}
|
||||||
|
|
||||||
info_buf_target = info_buf_source + 1;
|
info_buf_target = info_buf_source + 1;
|
||||||
tmprc = CIFSSMBUnixQPathInfo(xid, tcon, fromName,
|
tmprc = CIFSSMBUnixQPathInfo(xid, tcon, from_name,
|
||||||
info_buf_source,
|
info_buf_source,
|
||||||
cifs_sb->local_nls,
|
cifs_sb->local_nls,
|
||||||
cifs_sb->mnt_cifs_flags &
|
cifs_sb->mnt_cifs_flags &
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
if (tmprc != 0)
|
if (tmprc != 0)
|
||||||
goto unlink_target;
|
goto unlink_target;
|
||||||
|
|
||||||
tmprc = CIFSSMBUnixQPathInfo(xid, tcon, toName,
|
tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name,
|
||||||
info_buf_target,
|
info_buf_target,
|
||||||
cifs_sb->local_nls,
|
cifs_sb->local_nls,
|
||||||
cifs_sb->mnt_cifs_flags &
|
cifs_sb->mnt_cifs_flags &
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
|
||||||
if (tmprc == 0 && (info_buf_source->UniqueId ==
|
if (tmprc == 0 && (info_buf_source->UniqueId ==
|
||||||
info_buf_target->UniqueId)) {
|
info_buf_target->UniqueId)) {
|
||||||
|
@ -1620,8 +1599,11 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
|
||||||
rc = 0;
|
rc = 0;
|
||||||
goto cifs_rename_exit;
|
goto cifs_rename_exit;
|
||||||
}
|
}
|
||||||
} /* else ... BB we could add the same check for Windows by
|
}
|
||||||
checking the UniqueId via FILE_INTERNAL_INFO */
|
/*
|
||||||
|
* else ... BB we could add the same check for Windows by
|
||||||
|
* checking the UniqueId via FILE_INTERNAL_INFO
|
||||||
|
*/
|
||||||
|
|
||||||
unlink_target:
|
unlink_target:
|
||||||
/* Try unlinking the target dentry if it's not negative */
|
/* Try unlinking the target dentry if it's not negative */
|
||||||
|
@ -1629,15 +1611,14 @@ unlink_target:
|
||||||
tmprc = cifs_unlink(target_dir, target_dentry);
|
tmprc = cifs_unlink(target_dir, target_dentry);
|
||||||
if (tmprc)
|
if (tmprc)
|
||||||
goto cifs_rename_exit;
|
goto cifs_rename_exit;
|
||||||
|
rc = cifs_do_rename(xid, source_dentry, from_name,
|
||||||
rc = cifs_do_rename(xid, source_dentry, fromName,
|
target_dentry, to_name);
|
||||||
target_dentry, toName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cifs_rename_exit:
|
cifs_rename_exit:
|
||||||
kfree(info_buf_source);
|
kfree(info_buf_source);
|
||||||
kfree(fromName);
|
kfree(from_name);
|
||||||
kfree(toName);
|
kfree(to_name);
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
cifs_put_tlink(tlink);
|
cifs_put_tlink(tlink);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1862,7 +1843,8 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
|
||||||
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
struct tcon_link *tlink = NULL;
|
struct tcon_link *tlink = NULL;
|
||||||
struct cifs_tcon *pTcon = NULL;
|
struct cifs_tcon *tcon = NULL;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
struct cifs_io_parms io_parms;
|
struct cifs_io_parms io_parms;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1876,19 +1858,21 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
|
||||||
*/
|
*/
|
||||||
open_file = find_writable_file(cifsInode, true);
|
open_file = find_writable_file(cifsInode, true);
|
||||||
if (open_file) {
|
if (open_file) {
|
||||||
__u16 nfid = open_file->netfid;
|
tcon = tlink_tcon(open_file->tlink);
|
||||||
__u32 npid = open_file->pid;
|
server = tcon->ses->server;
|
||||||
pTcon = tlink_tcon(open_file->tlink);
|
if (server->ops->set_file_size)
|
||||||
rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid,
|
rc = server->ops->set_file_size(xid, tcon, open_file,
|
||||||
npid, false);
|
attrs->ia_size, false);
|
||||||
|
else
|
||||||
|
rc = -ENOSYS;
|
||||||
cifsFileInfo_put(open_file);
|
cifsFileInfo_put(open_file);
|
||||||
cFYI(1, "SetFSize for attrs rc = %d", rc);
|
cFYI(1, "SetFSize for attrs rc = %d", rc);
|
||||||
if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
|
if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
|
||||||
unsigned int bytes_written;
|
unsigned int bytes_written;
|
||||||
|
|
||||||
io_parms.netfid = nfid;
|
io_parms.netfid = open_file->fid.netfid;
|
||||||
io_parms.pid = npid;
|
io_parms.pid = open_file->pid;
|
||||||
io_parms.tcon = pTcon;
|
io_parms.tcon = tcon;
|
||||||
io_parms.offset = 0;
|
io_parms.offset = 0;
|
||||||
io_parms.length = attrs->ia_size;
|
io_parms.length = attrs->ia_size;
|
||||||
rc = CIFSSMBWrite(xid, &io_parms, &bytes_written,
|
rc = CIFSSMBWrite(xid, &io_parms, &bytes_written,
|
||||||
|
@ -1898,52 +1882,55 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
|
||||||
} else
|
} else
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
|
|
||||||
if (rc != 0) {
|
if (!rc)
|
||||||
if (pTcon == NULL) {
|
goto set_size_out;
|
||||||
tlink = cifs_sb_tlink(cifs_sb);
|
|
||||||
if (IS_ERR(tlink))
|
|
||||||
return PTR_ERR(tlink);
|
|
||||||
pTcon = tlink_tcon(tlink);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set file size by pathname rather than by handle
|
if (tcon == NULL) {
|
||||||
either because no valid, writeable file handle for
|
tlink = cifs_sb_tlink(cifs_sb);
|
||||||
it was found or because there was an error setting
|
if (IS_ERR(tlink))
|
||||||
it by handle */
|
return PTR_ERR(tlink);
|
||||||
rc = CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size,
|
tcon = tlink_tcon(tlink);
|
||||||
false, cifs_sb->local_nls,
|
server = tcon->ses->server;
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
cFYI(1, "SetEOF by path (setattrs) rc = %d", rc);
|
|
||||||
if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
|
|
||||||
__u16 netfid;
|
|
||||||
int oplock = 0;
|
|
||||||
|
|
||||||
rc = SMBLegacyOpen(xid, pTcon, full_path,
|
|
||||||
FILE_OPEN, GENERIC_WRITE,
|
|
||||||
CREATE_NOT_DIR, &netfid, &oplock, NULL,
|
|
||||||
cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
if (rc == 0) {
|
|
||||||
unsigned int bytes_written;
|
|
||||||
|
|
||||||
io_parms.netfid = netfid;
|
|
||||||
io_parms.pid = current->tgid;
|
|
||||||
io_parms.tcon = pTcon;
|
|
||||||
io_parms.offset = 0;
|
|
||||||
io_parms.length = attrs->ia_size;
|
|
||||||
rc = CIFSSMBWrite(xid, &io_parms,
|
|
||||||
&bytes_written,
|
|
||||||
NULL, NULL, 1);
|
|
||||||
cFYI(1, "wrt seteof rc %d", rc);
|
|
||||||
CIFSSMBClose(xid, pTcon, netfid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tlink)
|
|
||||||
cifs_put_tlink(tlink);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set file size by pathname rather than by handle either because no
|
||||||
|
* valid, writeable file handle for it was found or because there was
|
||||||
|
* an error setting it by handle.
|
||||||
|
*/
|
||||||
|
if (server->ops->set_path_size)
|
||||||
|
rc = server->ops->set_path_size(xid, tcon, full_path,
|
||||||
|
attrs->ia_size, cifs_sb, false);
|
||||||
|
else
|
||||||
|
rc = -ENOSYS;
|
||||||
|
cFYI(1, "SetEOF by path (setattrs) rc = %d", rc);
|
||||||
|
if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
|
||||||
|
__u16 netfid;
|
||||||
|
int oplock = 0;
|
||||||
|
|
||||||
|
rc = SMBLegacyOpen(xid, tcon, full_path, FILE_OPEN,
|
||||||
|
GENERIC_WRITE, CREATE_NOT_DIR, &netfid,
|
||||||
|
&oplock, NULL, cifs_sb->local_nls,
|
||||||
|
cifs_sb->mnt_cifs_flags &
|
||||||
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
if (rc == 0) {
|
||||||
|
unsigned int bytes_written;
|
||||||
|
|
||||||
|
io_parms.netfid = netfid;
|
||||||
|
io_parms.pid = current->tgid;
|
||||||
|
io_parms.tcon = tcon;
|
||||||
|
io_parms.offset = 0;
|
||||||
|
io_parms.length = attrs->ia_size;
|
||||||
|
rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, NULL,
|
||||||
|
NULL, 1);
|
||||||
|
cFYI(1, "wrt seteof rc %d", rc);
|
||||||
|
CIFSSMBClose(xid, tcon, netfid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tlink)
|
||||||
|
cifs_put_tlink(tlink);
|
||||||
|
|
||||||
|
set_size_out:
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
cifsInode->server_eof = attrs->ia_size;
|
cifsInode->server_eof = attrs->ia_size;
|
||||||
cifs_setsize(inode, attrs->ia_size);
|
cifs_setsize(inode, attrs->ia_size);
|
||||||
|
@ -2050,7 +2037,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
|
||||||
args->device = 0;
|
args->device = 0;
|
||||||
open_file = find_writable_file(cifsInode, true);
|
open_file = find_writable_file(cifsInode, true);
|
||||||
if (open_file) {
|
if (open_file) {
|
||||||
u16 nfid = open_file->netfid;
|
u16 nfid = open_file->fid.netfid;
|
||||||
u32 npid = open_file->pid;
|
u32 npid = open_file->pid;
|
||||||
pTcon = tlink_tcon(open_file->tlink);
|
pTcon = tlink_tcon(open_file->tlink);
|
||||||
rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid);
|
rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid);
|
||||||
|
|
|
@ -28,8 +28,6 @@
|
||||||
#include "cifs_debug.h"
|
#include "cifs_debug.h"
|
||||||
#include "cifsfs.h"
|
#include "cifsfs.h"
|
||||||
|
|
||||||
#define CIFS_IOC_CHECKUMOUNT _IO(0xCF, 2)
|
|
||||||
|
|
||||||
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||||
{
|
{
|
||||||
struct inode *inode = filep->f_dentry->d_inode;
|
struct inode *inode = filep->f_dentry->d_inode;
|
||||||
|
@ -51,23 +49,6 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||||
cifs_sb = CIFS_SB(inode->i_sb);
|
cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
static bool warned = false;
|
|
||||||
case CIFS_IOC_CHECKUMOUNT:
|
|
||||||
if (!warned) {
|
|
||||||
warned = true;
|
|
||||||
cERROR(1, "the CIFS_IOC_CHECKMOUNT ioctl will "
|
|
||||||
"be deprecated in 3.7. Please "
|
|
||||||
"migrate away from the use of "
|
|
||||||
"umount.cifs");
|
|
||||||
}
|
|
||||||
cFYI(1, "User unmount attempted");
|
|
||||||
if (cifs_sb->mnt_uid == current_uid())
|
|
||||||
rc = 0;
|
|
||||||
else {
|
|
||||||
rc = -EACCES;
|
|
||||||
cFYI(1, "uids do not match");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#ifdef CONFIG_CIFS_POSIX
|
#ifdef CONFIG_CIFS_POSIX
|
||||||
case FS_IOC_GETFLAGS:
|
case FS_IOC_GETFLAGS:
|
||||||
if (pSMBFile == NULL)
|
if (pSMBFile == NULL)
|
||||||
|
@ -75,8 +56,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||||
tcon = tlink_tcon(pSMBFile->tlink);
|
tcon = tlink_tcon(pSMBFile->tlink);
|
||||||
caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
||||||
if (CIFS_UNIX_EXTATTR_CAP & caps) {
|
if (CIFS_UNIX_EXTATTR_CAP & caps) {
|
||||||
rc = CIFSGetExtAttr(xid, tcon, pSMBFile->netfid,
|
rc = CIFSGetExtAttr(xid, tcon,
|
||||||
&ExtAttrBits, &ExtAttrMask);
|
pSMBFile->fid.netfid,
|
||||||
|
&ExtAttrBits, &ExtAttrMask);
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
rc = put_user(ExtAttrBits &
|
rc = put_user(ExtAttrBits &
|
||||||
FS_FL_USER_VISIBLE,
|
FS_FL_USER_VISIBLE,
|
||||||
|
@ -94,8 +76,12 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
||||||
rc = -EFAULT;
|
rc = -EFAULT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* rc= CIFSGetExtAttr(xid,tcon,pSMBFile->netfid,
|
/*
|
||||||
extAttrBits, &ExtAttrMask);*/
|
* rc = CIFSGetExtAttr(xid, tcon,
|
||||||
|
* pSMBFile->fid.netfid,
|
||||||
|
* extAttrBits,
|
||||||
|
* &ExtAttrMask);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
cFYI(1, "set flags not implemented yet");
|
cFYI(1, "set flags not implemented yet");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -391,72 +391,86 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
|
||||||
{
|
{
|
||||||
int rc = -EACCES;
|
int rc = -EACCES;
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
char *fromName = NULL;
|
char *from_name = NULL;
|
||||||
char *toName = NULL;
|
char *to_name = NULL;
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
struct tcon_link *tlink;
|
struct tcon_link *tlink;
|
||||||
struct cifs_tcon *pTcon;
|
struct cifs_tcon *tcon;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
struct cifsInodeInfo *cifsInode;
|
struct cifsInodeInfo *cifsInode;
|
||||||
|
|
||||||
tlink = cifs_sb_tlink(cifs_sb);
|
tlink = cifs_sb_tlink(cifs_sb);
|
||||||
if (IS_ERR(tlink))
|
if (IS_ERR(tlink))
|
||||||
return PTR_ERR(tlink);
|
return PTR_ERR(tlink);
|
||||||
pTcon = tlink_tcon(tlink);
|
tcon = tlink_tcon(tlink);
|
||||||
|
|
||||||
xid = get_xid();
|
xid = get_xid();
|
||||||
|
|
||||||
fromName = build_path_from_dentry(old_file);
|
from_name = build_path_from_dentry(old_file);
|
||||||
toName = build_path_from_dentry(direntry);
|
to_name = build_path_from_dentry(direntry);
|
||||||
if ((fromName == NULL) || (toName == NULL)) {
|
if ((from_name == NULL) || (to_name == NULL)) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto cifs_hl_exit;
|
goto cifs_hl_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pTcon->unix_ext)
|
if (tcon->unix_ext)
|
||||||
rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
|
rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name,
|
||||||
cifs_sb->local_nls,
|
cifs_sb->local_nls,
|
||||||
cifs_sb->mnt_cifs_flags &
|
cifs_sb->mnt_cifs_flags &
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
else {
|
else {
|
||||||
rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
|
server = tcon->ses->server;
|
||||||
cifs_sb->local_nls,
|
if (!server->ops->create_hardlink)
|
||||||
cifs_sb->mnt_cifs_flags &
|
return -ENOSYS;
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
rc = server->ops->create_hardlink(xid, tcon, from_name, to_name,
|
||||||
|
cifs_sb);
|
||||||
if ((rc == -EIO) || (rc == -EINVAL))
|
if ((rc == -EIO) || (rc == -EINVAL))
|
||||||
rc = -EOPNOTSUPP;
|
rc = -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
d_drop(direntry); /* force new lookup from server of target */
|
d_drop(direntry); /* force new lookup from server of target */
|
||||||
|
|
||||||
/* if source file is cached (oplocked) revalidate will not go to server
|
/*
|
||||||
until the file is closed or oplock broken so update nlinks locally */
|
* if source file is cached (oplocked) revalidate will not go to server
|
||||||
|
* until the file is closed or oplock broken so update nlinks locally
|
||||||
|
*/
|
||||||
if (old_file->d_inode) {
|
if (old_file->d_inode) {
|
||||||
cifsInode = CIFS_I(old_file->d_inode);
|
cifsInode = CIFS_I(old_file->d_inode);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
spin_lock(&old_file->d_inode->i_lock);
|
spin_lock(&old_file->d_inode->i_lock);
|
||||||
inc_nlink(old_file->d_inode);
|
inc_nlink(old_file->d_inode);
|
||||||
spin_unlock(&old_file->d_inode->i_lock);
|
spin_unlock(&old_file->d_inode->i_lock);
|
||||||
/* BB should we make this contingent on superblock flag NOATIME? */
|
/*
|
||||||
/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
|
* BB should we make this contingent on superblock flag
|
||||||
/* parent dir timestamps will update from srv
|
* NOATIME?
|
||||||
within a second, would it really be worth it
|
*/
|
||||||
to set the parent dir cifs inode time to zero
|
/* old_file->d_inode->i_ctime = CURRENT_TIME; */
|
||||||
to force revalidate (faster) for it too? */
|
/*
|
||||||
|
* parent dir timestamps will update from srv within a
|
||||||
|
* second, would it really be worth it to set the parent
|
||||||
|
* dir cifs inode time to zero to force revalidate
|
||||||
|
* (faster) for it too?
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
/* if not oplocked will force revalidate to get info
|
/*
|
||||||
on source file from srv */
|
* if not oplocked will force revalidate to get info on source
|
||||||
|
* file from srv
|
||||||
|
*/
|
||||||
cifsInode->time = 0;
|
cifsInode->time = 0;
|
||||||
|
|
||||||
/* Will update parent dir timestamps from srv within a second.
|
/*
|
||||||
Would it really be worth it to set the parent dir (cifs
|
* Will update parent dir timestamps from srv within a second.
|
||||||
inode) time field to zero to force revalidate on parent
|
* Would it really be worth it to set the parent dir (cifs
|
||||||
directory faster ie
|
* inode) time field to zero to force revalidate on parent
|
||||||
CIFS_I(inode)->time = 0; */
|
* directory faster ie
|
||||||
|
*
|
||||||
|
* CIFS_I(inode)->time = 0;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
cifs_hl_exit:
|
cifs_hl_exit:
|
||||||
kfree(fromName);
|
kfree(from_name);
|
||||||
kfree(toName);
|
kfree(to_name);
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
cifs_put_tlink(tlink);
|
cifs_put_tlink(tlink);
|
||||||
return rc;
|
return rc;
|
||||||
|
|
|
@ -466,7 +466,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
|
||||||
list_for_each(tmp2, &tcon->openFileList) {
|
list_for_each(tmp2, &tcon->openFileList) {
|
||||||
netfile = list_entry(tmp2, struct cifsFileInfo,
|
netfile = list_entry(tmp2, struct cifsFileInfo,
|
||||||
tlist);
|
tlist);
|
||||||
if (pSMB->Fid != netfile->netfid)
|
if (pSMB->Fid != netfile->fid.netfid)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
cFYI(1, "file id match, oplock break");
|
cFYI(1, "file id match, oplock break");
|
||||||
|
@ -579,3 +579,33 @@ backup_cred(struct cifs_sb_info *cifs_sb)
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cifs_del_pending_open(struct cifs_pending_open *open)
|
||||||
|
{
|
||||||
|
spin_lock(&cifs_file_list_lock);
|
||||||
|
list_del(&open->olist);
|
||||||
|
spin_unlock(&cifs_file_list_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink,
|
||||||
|
struct cifs_pending_open *open)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
|
memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
|
#endif
|
||||||
|
open->oplock = CIFS_OPLOCK_NO_CHANGE;
|
||||||
|
open->tlink = tlink;
|
||||||
|
fid->pending_open = open;
|
||||||
|
list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
|
||||||
|
struct cifs_pending_open *open)
|
||||||
|
{
|
||||||
|
spin_lock(&cifs_file_list_lock);
|
||||||
|
cifs_add_pending_open_locked(fid, tlink, open);
|
||||||
|
spin_unlock(&cifs_file_list_lock);
|
||||||
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ static const struct smb_to_posix_error mapping_table_ERRSRV[] = {
|
||||||
{ERRnoroom, -ENOSPC},
|
{ERRnoroom, -ENOSPC},
|
||||||
{ERRrmuns, -EUSERS},
|
{ERRrmuns, -EUSERS},
|
||||||
{ERRtimeout, -ETIME},
|
{ERRtimeout, -ETIME},
|
||||||
{ERRnoresource, -ENOBUFS},
|
{ERRnoresource, -EREMOTEIO},
|
||||||
{ERRtoomanyuids, -EUSERS},
|
{ERRtoomanyuids, -EUSERS},
|
||||||
{ERRbaduid, -EACCES},
|
{ERRbaduid, -EACCES},
|
||||||
{ERRusempx, -EIO},
|
{ERRusempx, -EIO},
|
||||||
|
@ -412,7 +412,7 @@ static const struct {
|
||||||
from NT_STATUS_INSUFFICIENT_RESOURCES to
|
from NT_STATUS_INSUFFICIENT_RESOURCES to
|
||||||
NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */
|
NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */
|
||||||
{
|
{
|
||||||
ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES}, {
|
ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, {
|
||||||
ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, {
|
ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, {
|
||||||
ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, {
|
ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, {
|
||||||
ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, {
|
ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, {
|
||||||
|
@ -682,7 +682,7 @@ static const struct {
|
||||||
ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, {
|
ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, {
|
||||||
ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, {
|
ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, {
|
||||||
ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, {
|
ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, {
|
||||||
ERRDOS, ERRnomem, NT_STATUS_INSUFF_SERVER_RESOURCES}, {
|
ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, {
|
||||||
ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, {
|
ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, {
|
||||||
ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, {
|
ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, {
|
||||||
ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, {
|
ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, {
|
||||||
|
@ -913,8 +913,9 @@ map_smb_to_linux_error(char *buf, bool logErr)
|
||||||
* portion, the number of word parameters and the data portion of the message
|
* portion, the number of word parameters and the data portion of the message
|
||||||
*/
|
*/
|
||||||
unsigned int
|
unsigned int
|
||||||
smbCalcSize(struct smb_hdr *ptr)
|
smbCalcSize(void *buf)
|
||||||
{
|
{
|
||||||
|
struct smb_hdr *ptr = (struct smb_hdr *)buf;
|
||||||
return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
|
return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
|
||||||
2 /* size of the bcc field */ + get_bcc(ptr));
|
2 /* size of the bcc field */ + get_bcc(ptr));
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
|
cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
|
||||||
struct cifs_sb_info *cifs_sb)
|
struct cifs_sb_info *cifs_sb)
|
||||||
{
|
{
|
||||||
|
@ -220,7 +220,8 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int initiate_cifs_search(const unsigned int xid, struct file *file)
|
static int
|
||||||
|
initiate_cifs_search(const unsigned int xid, struct file *file)
|
||||||
{
|
{
|
||||||
__u16 search_flags;
|
__u16 search_flags;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
@ -229,6 +230,7 @@ static int initiate_cifs_search(const unsigned int xid, struct file *file)
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
||||||
struct tcon_link *tlink = NULL;
|
struct tcon_link *tlink = NULL;
|
||||||
struct cifs_tcon *tcon;
|
struct cifs_tcon *tcon;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
|
|
||||||
if (file->private_data == NULL) {
|
if (file->private_data == NULL) {
|
||||||
tlink = cifs_sb_tlink(cifs_sb);
|
tlink = cifs_sb_tlink(cifs_sb);
|
||||||
|
@ -248,6 +250,13 @@ static int initiate_cifs_search(const unsigned int xid, struct file *file)
|
||||||
tcon = tlink_tcon(cifsFile->tlink);
|
tcon = tlink_tcon(cifsFile->tlink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server = tcon->ses->server;
|
||||||
|
|
||||||
|
if (!server->ops->query_dir_first) {
|
||||||
|
rc = -ENOSYS;
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
cifsFile->invalidHandle = true;
|
cifsFile->invalidHandle = true;
|
||||||
cifsFile->srch_inf.endOfSearch = false;
|
cifsFile->srch_inf.endOfSearch = false;
|
||||||
|
|
||||||
|
@ -278,10 +287,10 @@ ffirst_retry:
|
||||||
if (backup_cred(cifs_sb))
|
if (backup_cred(cifs_sb))
|
||||||
search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
|
search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
|
||||||
|
|
||||||
rc = CIFSFindFirst(xid, tcon, full_path, cifs_sb->local_nls,
|
rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb,
|
||||||
&cifsFile->netfid, search_flags, &cifsFile->srch_inf,
|
&cifsFile->fid, search_flags,
|
||||||
cifs_sb->mnt_cifs_flags &
|
&cifsFile->srch_inf);
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb));
|
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
cifsFile->invalidHandle = false;
|
cifsFile->invalidHandle = false;
|
||||||
/* BB add following call to handle readdir on new NTFS symlink errors
|
/* BB add following call to handle readdir on new NTFS symlink errors
|
||||||
|
@ -501,62 +510,67 @@ static int cifs_save_resume_key(const char *current_entry,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* find the corresponding entry in the search */
|
/*
|
||||||
/* Note that the SMB server returns search entries for . and .. which
|
* Find the corresponding entry in the search. Note that the SMB server returns
|
||||||
complicates logic here if we choose to parse for them and we do not
|
* search entries for . and .. which complicates logic here if we choose to
|
||||||
assume that they are located in the findfirst return buffer.*/
|
* parse for them and we do not assume that they are located in the findfirst
|
||||||
/* We start counting in the buffer with entry 2 and increment for every
|
* return buffer. We start counting in the buffer with entry 2 and increment for
|
||||||
entry (do not increment for . or .. entry) */
|
* every entry (do not increment for . or .. entry).
|
||||||
static int find_cifs_entry(const unsigned int xid, struct cifs_tcon *pTcon,
|
*/
|
||||||
struct file *file, char **ppCurrentEntry, int *num_to_ret)
|
static int
|
||||||
|
find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct file *file, char **current_entry, int *num_to_ret)
|
||||||
{
|
{
|
||||||
__u16 search_flags;
|
__u16 search_flags;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int pos_in_buf = 0;
|
int pos_in_buf = 0;
|
||||||
loff_t first_entry_in_buffer;
|
loff_t first_entry_in_buffer;
|
||||||
loff_t index_to_find = file->f_pos;
|
loff_t index_to_find = file->f_pos;
|
||||||
struct cifsFileInfo *cifsFile = file->private_data;
|
struct cifsFileInfo *cfile = file->private_data;
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
/* check if index in the buffer */
|
/* check if index in the buffer */
|
||||||
|
|
||||||
if ((cifsFile == NULL) || (ppCurrentEntry == NULL) ||
|
if (!server->ops->query_dir_first || !server->ops->query_dir_next)
|
||||||
(num_to_ret == NULL))
|
return -ENOSYS;
|
||||||
|
|
||||||
|
if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL))
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
*ppCurrentEntry = NULL;
|
*current_entry = NULL;
|
||||||
first_entry_in_buffer =
|
first_entry_in_buffer = cfile->srch_inf.index_of_last_entry -
|
||||||
cifsFile->srch_inf.index_of_last_entry -
|
cfile->srch_inf.entries_in_buffer;
|
||||||
cifsFile->srch_inf.entries_in_buffer;
|
|
||||||
|
|
||||||
/* if first entry in buf is zero then is first buffer
|
/*
|
||||||
in search response data which means it is likely . and ..
|
* If first entry in buf is zero then is first buffer
|
||||||
will be in this buffer, although some servers do not return
|
* in search response data which means it is likely . and ..
|
||||||
. and .. for the root of a drive and for those we need
|
* will be in this buffer, although some servers do not return
|
||||||
to start two entries earlier */
|
* . and .. for the root of a drive and for those we need
|
||||||
|
* to start two entries earlier.
|
||||||
|
*/
|
||||||
|
|
||||||
dump_cifs_file_struct(file, "In fce ");
|
dump_cifs_file_struct(file, "In fce ");
|
||||||
if (((index_to_find < cifsFile->srch_inf.index_of_last_entry) &&
|
if (((index_to_find < cfile->srch_inf.index_of_last_entry) &&
|
||||||
is_dir_changed(file)) ||
|
is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
|
||||||
(index_to_find < first_entry_in_buffer)) {
|
|
||||||
/* close and restart search */
|
/* close and restart search */
|
||||||
cFYI(1, "search backing up - close and restart search");
|
cFYI(1, "search backing up - close and restart search");
|
||||||
spin_lock(&cifs_file_list_lock);
|
spin_lock(&cifs_file_list_lock);
|
||||||
if (!cifsFile->srch_inf.endOfSearch &&
|
if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) {
|
||||||
!cifsFile->invalidHandle) {
|
cfile->invalidHandle = true;
|
||||||
cifsFile->invalidHandle = true;
|
|
||||||
spin_unlock(&cifs_file_list_lock);
|
spin_unlock(&cifs_file_list_lock);
|
||||||
CIFSFindClose(xid, pTcon, cifsFile->netfid);
|
if (server->ops->close)
|
||||||
|
server->ops->close(xid, tcon, &cfile->fid);
|
||||||
} else
|
} else
|
||||||
spin_unlock(&cifs_file_list_lock);
|
spin_unlock(&cifs_file_list_lock);
|
||||||
if (cifsFile->srch_inf.ntwrk_buf_start) {
|
if (cfile->srch_inf.ntwrk_buf_start) {
|
||||||
cFYI(1, "freeing SMB ff cache buf on search rewind");
|
cFYI(1, "freeing SMB ff cache buf on search rewind");
|
||||||
if (cifsFile->srch_inf.smallBuf)
|
if (cfile->srch_inf.smallBuf)
|
||||||
cifs_small_buf_release(cifsFile->srch_inf.
|
cifs_small_buf_release(cfile->srch_inf.
|
||||||
ntwrk_buf_start);
|
ntwrk_buf_start);
|
||||||
else
|
else
|
||||||
cifs_buf_release(cifsFile->srch_inf.
|
cifs_buf_release(cfile->srch_inf.
|
||||||
ntwrk_buf_start);
|
ntwrk_buf_start);
|
||||||
cifsFile->srch_inf.ntwrk_buf_start = NULL;
|
cfile->srch_inf.ntwrk_buf_start = NULL;
|
||||||
}
|
}
|
||||||
rc = initiate_cifs_search(xid, file);
|
rc = initiate_cifs_search(xid, file);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
@ -565,65 +579,64 @@ static int find_cifs_entry(const unsigned int xid, struct cifs_tcon *pTcon,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
/* FindFirst/Next set last_entry to NULL on malformed reply */
|
/* FindFirst/Next set last_entry to NULL on malformed reply */
|
||||||
if (cifsFile->srch_inf.last_entry)
|
if (cfile->srch_inf.last_entry)
|
||||||
cifs_save_resume_key(cifsFile->srch_inf.last_entry,
|
cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
|
||||||
cifsFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
|
search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
|
||||||
if (backup_cred(cifs_sb))
|
if (backup_cred(cifs_sb))
|
||||||
search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
|
search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
|
||||||
|
|
||||||
while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) &&
|
while ((index_to_find >= cfile->srch_inf.index_of_last_entry) &&
|
||||||
(rc == 0) && !cifsFile->srch_inf.endOfSearch) {
|
(rc == 0) && !cfile->srch_inf.endOfSearch) {
|
||||||
cFYI(1, "calling findnext2");
|
cFYI(1, "calling findnext2");
|
||||||
rc = CIFSFindNext(xid, pTcon, cifsFile->netfid, search_flags,
|
rc = server->ops->query_dir_next(xid, tcon, &cfile->fid,
|
||||||
&cifsFile->srch_inf);
|
search_flags,
|
||||||
|
&cfile->srch_inf);
|
||||||
/* FindFirst/Next set last_entry to NULL on malformed reply */
|
/* FindFirst/Next set last_entry to NULL on malformed reply */
|
||||||
if (cifsFile->srch_inf.last_entry)
|
if (cfile->srch_inf.last_entry)
|
||||||
cifs_save_resume_key(cifsFile->srch_inf.last_entry,
|
cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
|
||||||
cifsFile);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
if (index_to_find < cifsFile->srch_inf.index_of_last_entry) {
|
if (index_to_find < cfile->srch_inf.index_of_last_entry) {
|
||||||
/* we found the buffer that contains the entry */
|
/* we found the buffer that contains the entry */
|
||||||
/* scan and find it */
|
/* scan and find it */
|
||||||
int i;
|
int i;
|
||||||
char *current_entry;
|
char *cur_ent;
|
||||||
char *end_of_smb = cifsFile->srch_inf.ntwrk_buf_start +
|
char *end_of_smb = cfile->srch_inf.ntwrk_buf_start +
|
||||||
smbCalcSize((struct smb_hdr *)
|
server->ops->calc_smb_size(
|
||||||
cifsFile->srch_inf.ntwrk_buf_start);
|
cfile->srch_inf.ntwrk_buf_start);
|
||||||
|
|
||||||
current_entry = cifsFile->srch_inf.srch_entries_start;
|
cur_ent = cfile->srch_inf.srch_entries_start;
|
||||||
first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry
|
first_entry_in_buffer = cfile->srch_inf.index_of_last_entry
|
||||||
- cifsFile->srch_inf.entries_in_buffer;
|
- cfile->srch_inf.entries_in_buffer;
|
||||||
pos_in_buf = index_to_find - first_entry_in_buffer;
|
pos_in_buf = index_to_find - first_entry_in_buffer;
|
||||||
cFYI(1, "found entry - pos_in_buf %d", pos_in_buf);
|
cFYI(1, "found entry - pos_in_buf %d", pos_in_buf);
|
||||||
|
|
||||||
for (i = 0; (i < (pos_in_buf)) && (current_entry != NULL); i++) {
|
for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) {
|
||||||
/* go entry by entry figuring out which is first */
|
/* go entry by entry figuring out which is first */
|
||||||
current_entry = nxt_dir_entry(current_entry, end_of_smb,
|
cur_ent = nxt_dir_entry(cur_ent, end_of_smb,
|
||||||
cifsFile->srch_inf.info_level);
|
cfile->srch_inf.info_level);
|
||||||
}
|
}
|
||||||
if ((current_entry == NULL) && (i < pos_in_buf)) {
|
if ((cur_ent == NULL) && (i < pos_in_buf)) {
|
||||||
/* BB fixme - check if we should flag this error */
|
/* BB fixme - check if we should flag this error */
|
||||||
cERROR(1, "reached end of buf searching for pos in buf"
|
cERROR(1, "reached end of buf searching for pos in buf"
|
||||||
" %d index to find %lld rc %d",
|
" %d index to find %lld rc %d", pos_in_buf,
|
||||||
pos_in_buf, index_to_find, rc);
|
index_to_find, rc);
|
||||||
}
|
}
|
||||||
rc = 0;
|
rc = 0;
|
||||||
*ppCurrentEntry = current_entry;
|
*current_entry = cur_ent;
|
||||||
} else {
|
} else {
|
||||||
cFYI(1, "index not in buffer - could not findnext into it");
|
cFYI(1, "index not in buffer - could not findnext into it");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) {
|
if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) {
|
||||||
cFYI(1, "can not return entries pos_in_buf beyond last");
|
cFYI(1, "can not return entries pos_in_buf beyond last");
|
||||||
*num_to_ret = 0;
|
*num_to_ret = 0;
|
||||||
} else
|
} else
|
||||||
*num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf;
|
*num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf;
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -723,7 +736,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
int i;
|
int i;
|
||||||
struct cifs_tcon *pTcon;
|
struct cifs_tcon *tcon;
|
||||||
struct cifsFileInfo *cifsFile = NULL;
|
struct cifsFileInfo *cifsFile = NULL;
|
||||||
char *current_entry;
|
char *current_entry;
|
||||||
int num_to_fill = 0;
|
int num_to_fill = 0;
|
||||||
|
@ -781,12 +794,12 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
||||||
}
|
}
|
||||||
} /* else {
|
} /* else {
|
||||||
cifsFile->invalidHandle = true;
|
cifsFile->invalidHandle = true;
|
||||||
CIFSFindClose(xid, pTcon, cifsFile->netfid);
|
tcon->ses->server->close(xid, tcon, &cifsFile->fid);
|
||||||
} */
|
} */
|
||||||
|
|
||||||
pTcon = tlink_tcon(cifsFile->tlink);
|
tcon = tlink_tcon(cifsFile->tlink);
|
||||||
rc = find_cifs_entry(xid, pTcon, file,
|
rc = find_cifs_entry(xid, tcon, file, ¤t_entry,
|
||||||
¤t_entry, &num_to_fill);
|
&num_to_fill);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
cFYI(1, "fce error %d", rc);
|
cFYI(1, "fce error %d", rc);
|
||||||
goto rddir2_exit;
|
goto rddir2_exit;
|
||||||
|
@ -798,7 +811,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
||||||
}
|
}
|
||||||
cFYI(1, "loop through %d times filling dir for net buf %p",
|
cFYI(1, "loop through %d times filling dir for net buf %p",
|
||||||
num_to_fill, cifsFile->srch_inf.ntwrk_buf_start);
|
num_to_fill, cifsFile->srch_inf.ntwrk_buf_start);
|
||||||
max_len = smbCalcSize((struct smb_hdr *)
|
max_len = tcon->ses->server->ops->calc_smb_size(
|
||||||
cifsFile->srch_inf.ntwrk_buf_start);
|
cifsFile->srch_inf.ntwrk_buf_start);
|
||||||
end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;
|
end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;
|
||||||
|
|
||||||
|
@ -815,10 +828,12 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
||||||
num_to_fill, i);
|
num_to_fill, i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* if buggy server returns . and .. late do
|
/*
|
||||||
we want to check for that here? */
|
* if buggy server returns . and .. late do we want to
|
||||||
rc = cifs_filldir(current_entry, file,
|
* check for that here?
|
||||||
filldir, direntry, tmp_buf, max_len);
|
*/
|
||||||
|
rc = cifs_filldir(current_entry, file, filldir,
|
||||||
|
direntry, tmp_buf, max_len);
|
||||||
if (rc == -EOVERFLOW) {
|
if (rc == -EOVERFLOW) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -876,7 +876,8 @@ ssetup_ntlmssp_authenticate:
|
||||||
pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;
|
pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;
|
||||||
smb_buf = (struct smb_hdr *)iov[0].iov_base;
|
smb_buf = (struct smb_hdr *)iov[0].iov_base;
|
||||||
|
|
||||||
if ((type == RawNTLMSSP) && (smb_buf->Status.CifsError ==
|
if ((type == RawNTLMSSP) && (resp_buf_type != CIFS_NO_BUFFER) &&
|
||||||
|
(smb_buf->Status.CifsError ==
|
||||||
cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))) {
|
cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))) {
|
||||||
if (phase != NtLmNegotiate) {
|
if (phase != NtLmNegotiate) {
|
||||||
cERROR(1, "Unexpected more processing error");
|
cERROR(1, "Unexpected more processing error");
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/pagemap.h>
|
||||||
|
#include <linux/vfs.h>
|
||||||
#include "cifsglob.h"
|
#include "cifsglob.h"
|
||||||
#include "cifsproto.h"
|
#include "cifsproto.h"
|
||||||
#include "cifs_debug.h"
|
#include "cifs_debug.h"
|
||||||
|
@ -63,7 +65,7 @@ send_nt_cancel(struct TCP_Server_Info *server, void *buf,
|
||||||
static bool
|
static bool
|
||||||
cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
|
cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
|
||||||
{
|
{
|
||||||
return ob1->netfid == ob2->netfid;
|
return ob1->fid.netfid == ob2->fid.netfid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int
|
static unsigned int
|
||||||
|
@ -410,6 +412,83 @@ cifs_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||||
|
{
|
||||||
|
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
unsigned int wsize;
|
||||||
|
|
||||||
|
/* start with specified wsize, or default */
|
||||||
|
if (volume_info->wsize)
|
||||||
|
wsize = volume_info->wsize;
|
||||||
|
else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
|
||||||
|
wsize = CIFS_DEFAULT_IOSIZE;
|
||||||
|
else
|
||||||
|
wsize = CIFS_DEFAULT_NON_POSIX_WSIZE;
|
||||||
|
|
||||||
|
/* can server support 24-bit write sizes? (via UNIX extensions) */
|
||||||
|
if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
|
||||||
|
wsize = min_t(unsigned int, wsize, CIFS_MAX_RFC1002_WSIZE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set?
|
||||||
|
* Limit it to max buffer offered by the server, minus the size of the
|
||||||
|
* WRITEX header, not including the 4 byte RFC1001 length.
|
||||||
|
*/
|
||||||
|
if (!(server->capabilities & CAP_LARGE_WRITE_X) ||
|
||||||
|
(!(server->capabilities & CAP_UNIX) &&
|
||||||
|
(server->sec_mode & (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED))))
|
||||||
|
wsize = min_t(unsigned int, wsize,
|
||||||
|
server->maxBuf - sizeof(WRITE_REQ) + 4);
|
||||||
|
|
||||||
|
/* hard limit of CIFS_MAX_WSIZE */
|
||||||
|
wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE);
|
||||||
|
|
||||||
|
return wsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||||
|
{
|
||||||
|
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
unsigned int rsize, defsize;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set default value...
|
||||||
|
*
|
||||||
|
* HACK alert! Ancient servers have very small buffers. Even though
|
||||||
|
* MS-CIFS indicates that servers are only limited by the client's
|
||||||
|
* bufsize for reads, testing against win98se shows that it throws
|
||||||
|
* INVALID_PARAMETER errors if you try to request too large a read.
|
||||||
|
* OS/2 just sends back short reads.
|
||||||
|
*
|
||||||
|
* If the server doesn't advertise CAP_LARGE_READ_X, then assume that
|
||||||
|
* it can't handle a read request larger than its MaxBufferSize either.
|
||||||
|
*/
|
||||||
|
if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP))
|
||||||
|
defsize = CIFS_DEFAULT_IOSIZE;
|
||||||
|
else if (server->capabilities & CAP_LARGE_READ_X)
|
||||||
|
defsize = CIFS_DEFAULT_NON_POSIX_RSIZE;
|
||||||
|
else
|
||||||
|
defsize = server->maxBuf - sizeof(READ_RSP);
|
||||||
|
|
||||||
|
rsize = volume_info->rsize ? volume_info->rsize : defsize;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to
|
||||||
|
* the client's MaxBufferSize.
|
||||||
|
*/
|
||||||
|
if (!(server->capabilities & CAP_LARGE_READ_X))
|
||||||
|
rsize = min_t(unsigned int, CIFSMaxBufSize, rsize);
|
||||||
|
|
||||||
|
/* hard limit of CIFS_MAX_RSIZE */
|
||||||
|
rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE);
|
||||||
|
|
||||||
|
return rsize;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cifs_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
|
cifs_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
|
||||||
{
|
{
|
||||||
|
@ -489,6 +568,13 @@ cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct cifs_fid *fid, FILE_ALL_INFO *data)
|
||||||
|
{
|
||||||
|
return CIFSSMBQFileInfo(xid, tcon, fid->netfid, data);
|
||||||
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
|
cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
|
||||||
struct cifs_tcon *tcon)
|
struct cifs_tcon *tcon)
|
||||||
|
@ -607,6 +693,219 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
|
||||||
cifsInode->cifsAttrs = dosattrs;
|
cifsInode->cifsAttrs = dosattrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path,
|
||||||
|
int disposition, int desired_access, int create_options,
|
||||||
|
struct cifs_fid *fid, __u32 *oplock, FILE_ALL_INFO *buf,
|
||||||
|
struct cifs_sb_info *cifs_sb)
|
||||||
|
{
|
||||||
|
if (!(tcon->ses->capabilities & CAP_NT_SMBS))
|
||||||
|
return SMBLegacyOpen(xid, tcon, path, disposition,
|
||||||
|
desired_access, create_options,
|
||||||
|
&fid->netfid, oplock, buf,
|
||||||
|
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
|
||||||
|
& CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
return CIFSSMBOpen(xid, tcon, path, disposition, desired_access,
|
||||||
|
create_options, &fid->netfid, oplock, buf,
|
||||||
|
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
|
||||||
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
|
||||||
|
{
|
||||||
|
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
|
||||||
|
cfile->fid.netfid = fid->netfid;
|
||||||
|
cifs_set_oplock_level(cinode, oplock);
|
||||||
|
cinode->can_cache_brlcks = cinode->clientCanCacheAll;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
CIFSSMBClose(xid, tcon, fid->netfid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
return CIFSSMBFlush(xid, tcon, fid->netfid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_sync_read(const unsigned int xid, struct cifsFileInfo *cfile,
|
||||||
|
struct cifs_io_parms *parms, unsigned int *bytes_read,
|
||||||
|
char **buf, int *buf_type)
|
||||||
|
{
|
||||||
|
parms->netfid = cfile->fid.netfid;
|
||||||
|
return CIFSSMBRead(xid, parms, bytes_read, buf, buf_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_sync_write(const unsigned int xid, struct cifsFileInfo *cfile,
|
||||||
|
struct cifs_io_parms *parms, unsigned int *written,
|
||||||
|
struct kvec *iov, unsigned long nr_segs)
|
||||||
|
{
|
||||||
|
|
||||||
|
parms->netfid = cfile->fid.netfid;
|
||||||
|
return CIFSSMBWrite2(xid, parms, written, iov, nr_segs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb_set_file_info(struct inode *inode, const char *full_path,
|
||||||
|
FILE_BASIC_INFO *buf, const unsigned int xid)
|
||||||
|
{
|
||||||
|
int oplock = 0;
|
||||||
|
int rc;
|
||||||
|
__u16 netfid;
|
||||||
|
__u32 netpid;
|
||||||
|
struct cifsFileInfo *open_file;
|
||||||
|
struct cifsInodeInfo *cinode = CIFS_I(inode);
|
||||||
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
|
struct tcon_link *tlink = NULL;
|
||||||
|
struct cifs_tcon *tcon;
|
||||||
|
FILE_BASIC_INFO info_buf;
|
||||||
|
|
||||||
|
/* if the file is already open for write, just use that fileid */
|
||||||
|
open_file = find_writable_file(cinode, true);
|
||||||
|
if (open_file) {
|
||||||
|
netfid = open_file->fid.netfid;
|
||||||
|
netpid = open_file->pid;
|
||||||
|
tcon = tlink_tcon(open_file->tlink);
|
||||||
|
goto set_via_filehandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
tlink = cifs_sb_tlink(cifs_sb);
|
||||||
|
if (IS_ERR(tlink)) {
|
||||||
|
rc = PTR_ERR(tlink);
|
||||||
|
tlink = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
tcon = tlink_tcon(tlink);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NT4 apparently returns success on this call, but it doesn't really
|
||||||
|
* work.
|
||||||
|
*/
|
||||||
|
if (!(tcon->ses->flags & CIFS_SES_NT4)) {
|
||||||
|
rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf,
|
||||||
|
cifs_sb->local_nls,
|
||||||
|
cifs_sb->mnt_cifs_flags &
|
||||||
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
if (rc == 0) {
|
||||||
|
cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
|
||||||
|
goto out;
|
||||||
|
} else if (rc != -EOPNOTSUPP && rc != -EINVAL)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
cFYI(1, "calling SetFileInfo since SetPathInfo for times not supported "
|
||||||
|
"by this server");
|
||||||
|
rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
|
||||||
|
SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR,
|
||||||
|
&netfid, &oplock, NULL, cifs_sb->local_nls,
|
||||||
|
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
if (rc == -EIO)
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
netpid = current->tgid;
|
||||||
|
|
||||||
|
set_via_filehandle:
|
||||||
|
rc = CIFSSMBSetFileInfo(xid, tcon, &info_buf, netfid, netpid);
|
||||||
|
if (!rc)
|
||||||
|
cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
|
||||||
|
|
||||||
|
if (open_file == NULL)
|
||||||
|
CIFSSMBClose(xid, tcon, netfid);
|
||||||
|
else
|
||||||
|
cifsFileInfo_put(open_file);
|
||||||
|
out:
|
||||||
|
if (tlink != NULL)
|
||||||
|
cifs_put_tlink(tlink);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *path, struct cifs_sb_info *cifs_sb,
|
||||||
|
struct cifs_fid *fid, __u16 search_flags,
|
||||||
|
struct cifs_search_info *srch_inf)
|
||||||
|
{
|
||||||
|
return CIFSFindFirst(xid, tcon, path, cifs_sb,
|
||||||
|
&fid->netfid, search_flags, srch_inf, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct cifs_fid *fid, __u16 search_flags,
|
||||||
|
struct cifs_search_info *srch_inf)
|
||||||
|
{
|
||||||
|
return CIFSFindNext(xid, tcon, fid->netfid, search_flags, srch_inf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
return CIFSFindClose(xid, tcon, fid->netfid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
|
||||||
|
struct cifsInodeInfo *cinode)
|
||||||
|
{
|
||||||
|
return CIFSSMBLock(0, tcon, fid->netfid, current->tgid, 0, 0, 0, 0,
|
||||||
|
LOCKING_ANDX_OPLOCK_RELEASE, false,
|
||||||
|
cinode->clientCanCacheRead ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct kstatfs *buf)
|
||||||
|
{
|
||||||
|
int rc = -EOPNOTSUPP;
|
||||||
|
|
||||||
|
buf->f_type = CIFS_MAGIC_NUMBER;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We could add a second check for a QFS Unix capability bit
|
||||||
|
*/
|
||||||
|
if ((tcon->ses->capabilities & CAP_UNIX) &&
|
||||||
|
(CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability)))
|
||||||
|
rc = CIFSSMBQFSPosixInfo(xid, tcon, buf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only need to call the old QFSInfo if failed on newer one,
|
||||||
|
* e.g. by OS/2.
|
||||||
|
**/
|
||||||
|
if (rc && (tcon->ses->capabilities & CAP_NT_SMBS))
|
||||||
|
rc = CIFSSMBQFSInfo(xid, tcon, buf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some old Windows servers also do not support level 103, retry with
|
||||||
|
* older level one if old server failed the previous call or we
|
||||||
|
* bypassed it because we detected that this was an older LANMAN sess
|
||||||
|
*/
|
||||||
|
if (rc)
|
||||||
|
rc = SMBOldQFSInfo(xid, tcon, buf);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
|
||||||
|
__u64 length, __u32 type, int lock, int unlock, bool wait)
|
||||||
|
{
|
||||||
|
return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->fid.netfid,
|
||||||
|
current->tgid, length, offset, unlock, lock,
|
||||||
|
(__u8)type, wait, 0);
|
||||||
|
}
|
||||||
|
|
||||||
struct smb_version_operations smb1_operations = {
|
struct smb_version_operations smb1_operations = {
|
||||||
.send_cancel = send_nt_cancel,
|
.send_cancel = send_nt_cancel,
|
||||||
.compare_fids = cifs_compare_fids,
|
.compare_fids = cifs_compare_fids,
|
||||||
|
@ -630,6 +929,8 @@ struct smb_version_operations smb1_operations = {
|
||||||
.check_trans2 = cifs_check_trans2,
|
.check_trans2 = cifs_check_trans2,
|
||||||
.need_neg = cifs_need_neg,
|
.need_neg = cifs_need_neg,
|
||||||
.negotiate = cifs_negotiate,
|
.negotiate = cifs_negotiate,
|
||||||
|
.negotiate_wsize = cifs_negotiate_wsize,
|
||||||
|
.negotiate_rsize = cifs_negotiate_rsize,
|
||||||
.sess_setup = CIFS_SessSetup,
|
.sess_setup = CIFS_SessSetup,
|
||||||
.logoff = CIFSSMBLogoff,
|
.logoff = CIFSSMBLogoff,
|
||||||
.tree_connect = CIFSTCon,
|
.tree_connect = CIFSTCon,
|
||||||
|
@ -638,12 +939,37 @@ struct smb_version_operations smb1_operations = {
|
||||||
.qfs_tcon = cifs_qfs_tcon,
|
.qfs_tcon = cifs_qfs_tcon,
|
||||||
.is_path_accessible = cifs_is_path_accessible,
|
.is_path_accessible = cifs_is_path_accessible,
|
||||||
.query_path_info = cifs_query_path_info,
|
.query_path_info = cifs_query_path_info,
|
||||||
|
.query_file_info = cifs_query_file_info,
|
||||||
.get_srv_inum = cifs_get_srv_inum,
|
.get_srv_inum = cifs_get_srv_inum,
|
||||||
|
.set_path_size = CIFSSMBSetEOF,
|
||||||
|
.set_file_size = CIFSSMBSetFileSize,
|
||||||
|
.set_file_info = smb_set_file_info,
|
||||||
.build_path_to_root = cifs_build_path_to_root,
|
.build_path_to_root = cifs_build_path_to_root,
|
||||||
.echo = CIFSSMBEcho,
|
.echo = CIFSSMBEcho,
|
||||||
.mkdir = CIFSSMBMkDir,
|
.mkdir = CIFSSMBMkDir,
|
||||||
.mkdir_setinfo = cifs_mkdir_setinfo,
|
.mkdir_setinfo = cifs_mkdir_setinfo,
|
||||||
.rmdir = CIFSSMBRmDir,
|
.rmdir = CIFSSMBRmDir,
|
||||||
|
.unlink = CIFSSMBDelFile,
|
||||||
|
.rename_pending_delete = cifs_rename_pending_delete,
|
||||||
|
.rename = CIFSSMBRename,
|
||||||
|
.create_hardlink = CIFSCreateHardLink,
|
||||||
|
.open = cifs_open_file,
|
||||||
|
.set_fid = cifs_set_fid,
|
||||||
|
.close = cifs_close_file,
|
||||||
|
.flush = cifs_flush_file,
|
||||||
|
.async_readv = cifs_async_readv,
|
||||||
|
.async_writev = cifs_async_writev,
|
||||||
|
.sync_read = cifs_sync_read,
|
||||||
|
.sync_write = cifs_sync_write,
|
||||||
|
.query_dir_first = cifs_query_dir_first,
|
||||||
|
.query_dir_next = cifs_query_dir_next,
|
||||||
|
.close_dir = cifs_close_dir,
|
||||||
|
.calc_smb_size = smbCalcSize,
|
||||||
|
.oplock_response = cifs_oplock_response,
|
||||||
|
.queryfs = cifs_queryfs,
|
||||||
|
.mand_lock = cifs_mand_lock,
|
||||||
|
.mand_unlock_range = cifs_unlock_range,
|
||||||
|
.push_mand_locks = cifs_push_mandatory_locks,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values smb1_values = {
|
struct smb_version_values smb1_values = {
|
||||||
|
|
|
@ -0,0 +1,302 @@
|
||||||
|
/*
|
||||||
|
* fs/cifs/smb2file.c
|
||||||
|
*
|
||||||
|
* Copyright (C) International Business Machines Corp., 2002, 2011
|
||||||
|
* Author(s): Steve French (sfrench@us.ibm.com),
|
||||||
|
* Pavel Shilovsky ((pshilovsky@samba.org) 2012
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||||
|
* the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/stat.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/pagemap.h>
|
||||||
|
#include <asm/div64.h>
|
||||||
|
#include "cifsfs.h"
|
||||||
|
#include "cifspdu.h"
|
||||||
|
#include "cifsglob.h"
|
||||||
|
#include "cifsproto.h"
|
||||||
|
#include "cifs_debug.h"
|
||||||
|
#include "cifs_fs_sb.h"
|
||||||
|
#include "cifs_unicode.h"
|
||||||
|
#include "fscache.h"
|
||||||
|
#include "smb2proto.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
|
||||||
|
{
|
||||||
|
oplock &= 0xFF;
|
||||||
|
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
|
||||||
|
return;
|
||||||
|
if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
|
||||||
|
cinode->clientCanCacheAll = true;
|
||||||
|
cinode->clientCanCacheRead = true;
|
||||||
|
cFYI(1, "Exclusive Oplock granted on inode %p",
|
||||||
|
&cinode->vfs_inode);
|
||||||
|
} else if (oplock == SMB2_OPLOCK_LEVEL_II) {
|
||||||
|
cinode->clientCanCacheAll = false;
|
||||||
|
cinode->clientCanCacheRead = true;
|
||||||
|
cFYI(1, "Level II Oplock granted on inode %p",
|
||||||
|
&cinode->vfs_inode);
|
||||||
|
} else {
|
||||||
|
cinode->clientCanCacheAll = false;
|
||||||
|
cinode->clientCanCacheRead = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path,
|
||||||
|
int disposition, int desired_access, int create_options,
|
||||||
|
struct cifs_fid *fid, __u32 *oplock, FILE_ALL_INFO *buf,
|
||||||
|
struct cifs_sb_info *cifs_sb)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
__le16 *smb2_path;
|
||||||
|
struct smb2_file_all_info *smb2_data = NULL;
|
||||||
|
__u8 smb2_oplock[17];
|
||||||
|
|
||||||
|
smb2_path = cifs_convert_path_to_utf16(path, cifs_sb);
|
||||||
|
if (smb2_path == NULL) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (smb2_data == NULL) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
desired_access |= FILE_READ_ATTRIBUTES;
|
||||||
|
*smb2_oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
|
||||||
|
|
||||||
|
if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
|
||||||
|
memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
|
|
||||||
|
rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid,
|
||||||
|
&fid->volatile_fid, desired_access, disposition,
|
||||||
|
0, 0, smb2_oplock, smb2_data);
|
||||||
|
if (rc)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (buf) {
|
||||||
|
/* open response does not have IndexNumber field - get it */
|
||||||
|
rc = SMB2_get_srv_num(xid, tcon, fid->persistent_fid,
|
||||||
|
fid->volatile_fid,
|
||||||
|
&smb2_data->IndexNumber);
|
||||||
|
if (rc) {
|
||||||
|
/* let get_inode_info disable server inode numbers */
|
||||||
|
smb2_data->IndexNumber = 0;
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
move_smb2_info_to_cifs(buf, smb2_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
*oplock = *smb2_oplock;
|
||||||
|
out:
|
||||||
|
kfree(smb2_data);
|
||||||
|
kfree(smb2_path);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
|
||||||
|
const unsigned int xid)
|
||||||
|
{
|
||||||
|
int rc = 0, stored_rc;
|
||||||
|
unsigned int max_num, num = 0, max_buf;
|
||||||
|
struct smb2_lock_element *buf, *cur;
|
||||||
|
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
||||||
|
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
|
||||||
|
struct cifsLockInfo *li, *tmp;
|
||||||
|
__u64 length = 1 + flock->fl_end - flock->fl_start;
|
||||||
|
struct list_head tmp_llist;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&tmp_llist);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Accessing maxBuf is racy with cifs_reconnect - need to store value
|
||||||
|
* and check it for zero before using.
|
||||||
|
*/
|
||||||
|
max_buf = tcon->ses->server->maxBuf;
|
||||||
|
if (!max_buf)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
max_num = max_buf / sizeof(struct smb2_lock_element);
|
||||||
|
buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
cur = buf;
|
||||||
|
|
||||||
|
down_write(&cinode->lock_sem);
|
||||||
|
list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
|
||||||
|
if (flock->fl_start > li->offset ||
|
||||||
|
(flock->fl_start + length) <
|
||||||
|
(li->offset + li->length))
|
||||||
|
continue;
|
||||||
|
if (current->tgid != li->pid)
|
||||||
|
continue;
|
||||||
|
if (cinode->can_cache_brlcks) {
|
||||||
|
/*
|
||||||
|
* We can cache brlock requests - simply remove a lock
|
||||||
|
* from the file's list.
|
||||||
|
*/
|
||||||
|
list_del(&li->llist);
|
||||||
|
cifs_del_lock_waiters(li);
|
||||||
|
kfree(li);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cur->Length = cpu_to_le64(li->length);
|
||||||
|
cur->Offset = cpu_to_le64(li->offset);
|
||||||
|
cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK);
|
||||||
|
/*
|
||||||
|
* We need to save a lock here to let us add it again to the
|
||||||
|
* file's list if the unlock range request fails on the server.
|
||||||
|
*/
|
||||||
|
list_move(&li->llist, &tmp_llist);
|
||||||
|
if (++num == max_num) {
|
||||||
|
stored_rc = smb2_lockv(xid, tcon,
|
||||||
|
cfile->fid.persistent_fid,
|
||||||
|
cfile->fid.volatile_fid,
|
||||||
|
current->tgid, num, buf);
|
||||||
|
if (stored_rc) {
|
||||||
|
/*
|
||||||
|
* We failed on the unlock range request - add
|
||||||
|
* all locks from the tmp list to the head of
|
||||||
|
* the file's list.
|
||||||
|
*/
|
||||||
|
cifs_move_llist(&tmp_llist,
|
||||||
|
&cfile->llist->locks);
|
||||||
|
rc = stored_rc;
|
||||||
|
} else
|
||||||
|
/*
|
||||||
|
* The unlock range request succeed - free the
|
||||||
|
* tmp list.
|
||||||
|
*/
|
||||||
|
cifs_free_llist(&tmp_llist);
|
||||||
|
cur = buf;
|
||||||
|
num = 0;
|
||||||
|
} else
|
||||||
|
cur++;
|
||||||
|
}
|
||||||
|
if (num) {
|
||||||
|
stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid,
|
||||||
|
cfile->fid.volatile_fid, current->tgid,
|
||||||
|
num, buf);
|
||||||
|
if (stored_rc) {
|
||||||
|
cifs_move_llist(&tmp_llist, &cfile->llist->locks);
|
||||||
|
rc = stored_rc;
|
||||||
|
} else
|
||||||
|
cifs_free_llist(&tmp_llist);
|
||||||
|
}
|
||||||
|
up_write(&cinode->lock_sem);
|
||||||
|
|
||||||
|
kfree(buf);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_push_mand_fdlocks(struct cifs_fid_locks *fdlocks, const unsigned int xid,
|
||||||
|
struct smb2_lock_element *buf, unsigned int max_num)
|
||||||
|
{
|
||||||
|
int rc = 0, stored_rc;
|
||||||
|
struct cifsFileInfo *cfile = fdlocks->cfile;
|
||||||
|
struct cifsLockInfo *li;
|
||||||
|
unsigned int num = 0;
|
||||||
|
struct smb2_lock_element *cur = buf;
|
||||||
|
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
||||||
|
|
||||||
|
list_for_each_entry(li, &fdlocks->locks, llist) {
|
||||||
|
cur->Length = cpu_to_le64(li->length);
|
||||||
|
cur->Offset = cpu_to_le64(li->offset);
|
||||||
|
cur->Flags = cpu_to_le32(li->type |
|
||||||
|
SMB2_LOCKFLAG_FAIL_IMMEDIATELY);
|
||||||
|
if (++num == max_num) {
|
||||||
|
stored_rc = smb2_lockv(xid, tcon,
|
||||||
|
cfile->fid.persistent_fid,
|
||||||
|
cfile->fid.volatile_fid,
|
||||||
|
current->tgid, num, buf);
|
||||||
|
if (stored_rc)
|
||||||
|
rc = stored_rc;
|
||||||
|
cur = buf;
|
||||||
|
num = 0;
|
||||||
|
} else
|
||||||
|
cur++;
|
||||||
|
}
|
||||||
|
if (num) {
|
||||||
|
stored_rc = smb2_lockv(xid, tcon,
|
||||||
|
cfile->fid.persistent_fid,
|
||||||
|
cfile->fid.volatile_fid,
|
||||||
|
current->tgid, num, buf);
|
||||||
|
if (stored_rc)
|
||||||
|
rc = stored_rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
|
||||||
|
{
|
||||||
|
int rc = 0, stored_rc;
|
||||||
|
unsigned int xid;
|
||||||
|
unsigned int max_num, max_buf;
|
||||||
|
struct smb2_lock_element *buf;
|
||||||
|
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
|
||||||
|
struct cifs_fid_locks *fdlocks;
|
||||||
|
|
||||||
|
xid = get_xid();
|
||||||
|
/* we are going to update can_cache_brlcks here - need a write access */
|
||||||
|
down_write(&cinode->lock_sem);
|
||||||
|
if (!cinode->can_cache_brlcks) {
|
||||||
|
up_write(&cinode->lock_sem);
|
||||||
|
free_xid(xid);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Accessing maxBuf is racy with cifs_reconnect - need to store value
|
||||||
|
* and check it for zero before using.
|
||||||
|
*/
|
||||||
|
max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf;
|
||||||
|
if (!max_buf) {
|
||||||
|
up_write(&cinode->lock_sem);
|
||||||
|
free_xid(xid);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
max_num = max_buf / sizeof(struct smb2_lock_element);
|
||||||
|
buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL);
|
||||||
|
if (!buf) {
|
||||||
|
up_write(&cinode->lock_sem);
|
||||||
|
free_xid(xid);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(fdlocks, &cinode->llist, llist) {
|
||||||
|
stored_rc = smb2_push_mand_fdlocks(fdlocks, xid, buf, max_num);
|
||||||
|
if (stored_rc)
|
||||||
|
rc = stored_rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cinode->can_cache_brlcks = false;
|
||||||
|
kfree(buf);
|
||||||
|
|
||||||
|
up_write(&cinode->lock_sem);
|
||||||
|
free_xid(xid);
|
||||||
|
return rc;
|
||||||
|
}
|
|
@ -23,6 +23,8 @@
|
||||||
#ifndef _SMB2_GLOB_H
|
#ifndef _SMB2_GLOB_H
|
||||||
#define _SMB2_GLOB_H
|
#define _SMB2_GLOB_H
|
||||||
|
|
||||||
|
#define SMB2_MAGIC_NUMBER 0xFE534D42
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*****************************************************************
|
*****************************************************************
|
||||||
* Constants go here
|
* Constants go here
|
||||||
|
@ -40,5 +42,17 @@
|
||||||
#define SMB2_OP_MKDIR 5
|
#define SMB2_OP_MKDIR 5
|
||||||
#define SMB2_OP_RENAME 6
|
#define SMB2_OP_RENAME 6
|
||||||
#define SMB2_OP_DELETE 7
|
#define SMB2_OP_DELETE 7
|
||||||
|
#define SMB2_OP_HARDLINK 8
|
||||||
|
#define SMB2_OP_SET_EOF 9
|
||||||
|
|
||||||
|
/* Used when constructing chained read requests. */
|
||||||
|
#define CHAINED_REQUEST 1
|
||||||
|
#define START_OF_CHAIN 2
|
||||||
|
#define END_OF_CHAIN 4
|
||||||
|
#define RELATED_REQUEST 8
|
||||||
|
|
||||||
|
#define SMB2_SIGNATURE_SIZE (16)
|
||||||
|
#define SMB2_NTLMV2_SESSKEY_SIZE (16)
|
||||||
|
#define SMB2_HMACSHA256_SIZE (32)
|
||||||
|
|
||||||
#endif /* _SMB2_GLOB_H */
|
#endif /* _SMB2_GLOB_H */
|
||||||
|
|
|
@ -47,6 +47,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
int rc, tmprc = 0;
|
int rc, tmprc = 0;
|
||||||
u64 persistent_fid, volatile_fid;
|
u64 persistent_fid, volatile_fid;
|
||||||
__le16 *utf16_path;
|
__le16 *utf16_path;
|
||||||
|
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||||
|
|
||||||
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
|
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
|
||||||
if (!utf16_path)
|
if (!utf16_path)
|
||||||
|
@ -54,7 +55,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
|
||||||
rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
|
rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
|
||||||
desired_access, create_disposition, file_attributes,
|
desired_access, create_disposition, file_attributes,
|
||||||
create_options);
|
create_options, &oplock, NULL);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
kfree(utf16_path);
|
kfree(utf16_path);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -74,6 +75,22 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
* SMB2_open() call.
|
* SMB2_open() call.
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
|
case SMB2_OP_RENAME:
|
||||||
|
tmprc = SMB2_rename(xid, tcon, persistent_fid, volatile_fid,
|
||||||
|
(__le16 *)data);
|
||||||
|
break;
|
||||||
|
case SMB2_OP_HARDLINK:
|
||||||
|
tmprc = SMB2_set_hardlink(xid, tcon, persistent_fid,
|
||||||
|
volatile_fid, (__le16 *)data);
|
||||||
|
break;
|
||||||
|
case SMB2_OP_SET_EOF:
|
||||||
|
tmprc = SMB2_set_eof(xid, tcon, persistent_fid, volatile_fid,
|
||||||
|
current->tgid, (__le64 *)data);
|
||||||
|
break;
|
||||||
|
case SMB2_OP_SET_INFO:
|
||||||
|
tmprc = SMB2_set_info(xid, tcon, persistent_fid, volatile_fid,
|
||||||
|
(FILE_BASIC_INFO *)data);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
cERROR(1, "Invalid command");
|
cERROR(1, "Invalid command");
|
||||||
break;
|
break;
|
||||||
|
@ -86,7 +103,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
|
move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
|
||||||
{
|
{
|
||||||
memcpy(dst, src, (size_t)(&src->CurrentByteOffset) - (size_t)src);
|
memcpy(dst, src, (size_t)(&src->CurrentByteOffset) - (size_t)src);
|
||||||
|
@ -161,3 +178,80 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
|
||||||
0, CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE,
|
0, CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE,
|
||||||
NULL, SMB2_OP_DELETE);
|
NULL, SMB2_OP_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
|
||||||
|
struct cifs_sb_info *cifs_sb)
|
||||||
|
{
|
||||||
|
return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
|
||||||
|
0, CREATE_DELETE_ON_CLOSE, NULL,
|
||||||
|
SMB2_OP_DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *from_name, const char *to_name,
|
||||||
|
struct cifs_sb_info *cifs_sb, __u32 access, int command)
|
||||||
|
{
|
||||||
|
__le16 *smb2_to_name = NULL;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb);
|
||||||
|
if (smb2_to_name == NULL) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto smb2_rename_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = smb2_open_op_close(xid, tcon, cifs_sb, from_name, access,
|
||||||
|
FILE_OPEN, 0, 0, smb2_to_name, command);
|
||||||
|
smb2_rename_path:
|
||||||
|
kfree(smb2_to_name);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *from_name, const char *to_name,
|
||||||
|
struct cifs_sb_info *cifs_sb)
|
||||||
|
{
|
||||||
|
return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
|
||||||
|
DELETE, SMB2_OP_RENAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *from_name, const char *to_name,
|
||||||
|
struct cifs_sb_info *cifs_sb)
|
||||||
|
{
|
||||||
|
return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
|
||||||
|
FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *full_path, __u64 size,
|
||||||
|
struct cifs_sb_info *cifs_sb, bool set_alloc)
|
||||||
|
{
|
||||||
|
__le64 eof = cpu_to_le64(size);
|
||||||
|
return smb2_open_op_close(xid, tcon, cifs_sb, full_path,
|
||||||
|
FILE_WRITE_DATA, FILE_OPEN, 0, 0, &eof,
|
||||||
|
SMB2_OP_SET_EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
smb2_set_file_info(struct inode *inode, const char *full_path,
|
||||||
|
FILE_BASIC_INFO *buf, const unsigned int xid)
|
||||||
|
{
|
||||||
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
|
struct tcon_link *tlink;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
tlink = cifs_sb_tlink(cifs_sb);
|
||||||
|
if (IS_ERR(tlink))
|
||||||
|
return PTR_ERR(tlink);
|
||||||
|
rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path,
|
||||||
|
FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, 0, buf,
|
||||||
|
SMB2_OP_SET_INFO);
|
||||||
|
cifs_put_tlink(tlink);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
|
@ -453,7 +453,8 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
|
||||||
{STATUS_FILE_INVALID, -EIO, "STATUS_FILE_INVALID"},
|
{STATUS_FILE_INVALID, -EIO, "STATUS_FILE_INVALID"},
|
||||||
{STATUS_ALLOTTED_SPACE_EXCEEDED, -EIO,
|
{STATUS_ALLOTTED_SPACE_EXCEEDED, -EIO,
|
||||||
"STATUS_ALLOTTED_SPACE_EXCEEDED"},
|
"STATUS_ALLOTTED_SPACE_EXCEEDED"},
|
||||||
{STATUS_INSUFFICIENT_RESOURCES, -EIO, "STATUS_INSUFFICIENT_RESOURCES"},
|
{STATUS_INSUFFICIENT_RESOURCES, -EREMOTEIO,
|
||||||
|
"STATUS_INSUFFICIENT_RESOURCES"},
|
||||||
{STATUS_DFS_EXIT_PATH_FOUND, -EIO, "STATUS_DFS_EXIT_PATH_FOUND"},
|
{STATUS_DFS_EXIT_PATH_FOUND, -EIO, "STATUS_DFS_EXIT_PATH_FOUND"},
|
||||||
{STATUS_DEVICE_DATA_ERROR, -EIO, "STATUS_DEVICE_DATA_ERROR"},
|
{STATUS_DEVICE_DATA_ERROR, -EIO, "STATUS_DEVICE_DATA_ERROR"},
|
||||||
{STATUS_DEVICE_NOT_CONNECTED, -EIO, "STATUS_DEVICE_NOT_CONNECTED"},
|
{STATUS_DEVICE_NOT_CONNECTED, -EIO, "STATUS_DEVICE_NOT_CONNECTED"},
|
||||||
|
@ -2455,7 +2456,8 @@ map_smb2_to_linux_error(char *buf, bool log_err)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* mask facility */
|
/* mask facility */
|
||||||
if (log_err && (smb2err != (STATUS_MORE_PROCESSING_REQUIRED)))
|
if (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) &&
|
||||||
|
(smb2err != STATUS_END_OF_FILE))
|
||||||
smb2_print_status(smb2err);
|
smb2_print_status(smb2err);
|
||||||
else if (cifsFYI & CIFS_RC)
|
else if (cifsFYI & CIFS_RC)
|
||||||
smb2_print_status(smb2err);
|
smb2_print_status(smb2err);
|
||||||
|
|
|
@ -142,12 +142,19 @@ smb2_check_message(char *buf, unsigned int length)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) {
|
if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) {
|
||||||
if (hdr->Status == 0 ||
|
if (command != SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0 ||
|
||||||
pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2) {
|
pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2)) {
|
||||||
/* error packets have 9 byte structure size */
|
/* error packets have 9 byte structure size */
|
||||||
cERROR(1, "Illegal response size %u for command %d",
|
cERROR(1, "Illegal response size %u for command %d",
|
||||||
le16_to_cpu(pdu->StructureSize2), command);
|
le16_to_cpu(pdu->StructureSize2), command);
|
||||||
return 1;
|
return 1;
|
||||||
|
} else if (command == SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0)
|
||||||
|
&& (le16_to_cpu(pdu->StructureSize2) != 44)
|
||||||
|
&& (le16_to_cpu(pdu->StructureSize2) != 36)) {
|
||||||
|
/* special case for SMB2.1 lease break message */
|
||||||
|
cERROR(1, "Illegal response size %d for oplock break",
|
||||||
|
le16_to_cpu(pdu->StructureSize2));
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +169,9 @@ smb2_check_message(char *buf, unsigned int length)
|
||||||
if (4 + len != clc_len) {
|
if (4 + len != clc_len) {
|
||||||
cFYI(1, "Calculated size %u length %u mismatch mid %llu",
|
cFYI(1, "Calculated size %u length %u mismatch mid %llu",
|
||||||
clc_len, 4 + len, mid);
|
clc_len, 4 + len, mid);
|
||||||
|
/* Windows 7 server returns 24 bytes more */
|
||||||
|
if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE)
|
||||||
|
return 0;
|
||||||
/* server can return one byte more */
|
/* server can return one byte more */
|
||||||
if (clc_len == 4 + len + 1)
|
if (clc_len == 4 + len + 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -244,7 +254,15 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
|
||||||
((struct smb2_query_info_rsp *)hdr)->OutputBufferLength);
|
((struct smb2_query_info_rsp *)hdr)->OutputBufferLength);
|
||||||
break;
|
break;
|
||||||
case SMB2_READ:
|
case SMB2_READ:
|
||||||
|
*off = ((struct smb2_read_rsp *)hdr)->DataOffset;
|
||||||
|
*len = le32_to_cpu(((struct smb2_read_rsp *)hdr)->DataLength);
|
||||||
|
break;
|
||||||
case SMB2_QUERY_DIRECTORY:
|
case SMB2_QUERY_DIRECTORY:
|
||||||
|
*off = le16_to_cpu(
|
||||||
|
((struct smb2_query_directory_rsp *)hdr)->OutputBufferOffset);
|
||||||
|
*len = le32_to_cpu(
|
||||||
|
((struct smb2_query_directory_rsp *)hdr)->OutputBufferLength);
|
||||||
|
break;
|
||||||
case SMB2_IOCTL:
|
case SMB2_IOCTL:
|
||||||
case SMB2_CHANGE_NOTIFY:
|
case SMB2_CHANGE_NOTIFY:
|
||||||
default:
|
default:
|
||||||
|
@ -287,8 +305,9 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
|
||||||
* portion, the number of word parameters and the data portion of the message.
|
* portion, the number of word parameters and the data portion of the message.
|
||||||
*/
|
*/
|
||||||
unsigned int
|
unsigned int
|
||||||
smb2_calc_size(struct smb2_hdr *hdr)
|
smb2_calc_size(void *buf)
|
||||||
{
|
{
|
||||||
|
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
|
||||||
struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
|
struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
|
||||||
int offset; /* the offset from the beginning of SMB to data area */
|
int offset; /* the offset from the beginning of SMB to data area */
|
||||||
int data_length; /* the length of the variable length data area */
|
int data_length; /* the length of the variable length data area */
|
||||||
|
@ -347,3 +366,218 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__le32
|
||||||
|
smb2_get_lease_state(struct cifsInodeInfo *cinode)
|
||||||
|
{
|
||||||
|
if (cinode->clientCanCacheAll)
|
||||||
|
return SMB2_LEASE_WRITE_CACHING | SMB2_LEASE_READ_CACHING;
|
||||||
|
else if (cinode->clientCanCacheRead)
|
||||||
|
return SMB2_LEASE_READ_CACHING;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__u8 smb2_map_lease_to_oplock(__le32 lease_state)
|
||||||
|
{
|
||||||
|
if (lease_state & SMB2_LEASE_WRITE_CACHING) {
|
||||||
|
if (lease_state & SMB2_LEASE_HANDLE_CACHING)
|
||||||
|
return SMB2_OPLOCK_LEVEL_BATCH;
|
||||||
|
else
|
||||||
|
return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
|
||||||
|
} else if (lease_state & SMB2_LEASE_READ_CACHING)
|
||||||
|
return SMB2_OPLOCK_LEVEL_II;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct smb2_lease_break_work {
|
||||||
|
struct work_struct lease_break;
|
||||||
|
struct tcon_link *tlink;
|
||||||
|
__u8 lease_key[16];
|
||||||
|
__le32 lease_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
cifs_ses_oplock_break(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct smb2_lease_break_work *lw = container_of(work,
|
||||||
|
struct smb2_lease_break_work, lease_break);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key,
|
||||||
|
lw->lease_state);
|
||||||
|
cFYI(1, "Lease release rc %d", rc);
|
||||||
|
cifs_put_tlink(lw->tlink);
|
||||||
|
kfree(lw);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
|
||||||
|
{
|
||||||
|
struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer;
|
||||||
|
struct list_head *tmp, *tmp1, *tmp2;
|
||||||
|
struct cifs_ses *ses;
|
||||||
|
struct cifs_tcon *tcon;
|
||||||
|
struct cifsInodeInfo *cinode;
|
||||||
|
struct cifsFileInfo *cfile;
|
||||||
|
struct cifs_pending_open *open;
|
||||||
|
struct smb2_lease_break_work *lw;
|
||||||
|
bool found;
|
||||||
|
int ack_req = le32_to_cpu(rsp->Flags &
|
||||||
|
SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
|
||||||
|
|
||||||
|
lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
|
||||||
|
if (!lw) {
|
||||||
|
cERROR(1, "Memory allocation failed during lease break check");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
|
||||||
|
lw->lease_state = rsp->NewLeaseState;
|
||||||
|
|
||||||
|
cFYI(1, "Checking for lease break");
|
||||||
|
|
||||||
|
/* look up tcon based on tid & uid */
|
||||||
|
spin_lock(&cifs_tcp_ses_lock);
|
||||||
|
list_for_each(tmp, &server->smb_ses_list) {
|
||||||
|
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
|
||||||
|
|
||||||
|
spin_lock(&cifs_file_list_lock);
|
||||||
|
list_for_each(tmp1, &ses->tcon_list) {
|
||||||
|
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
|
||||||
|
|
||||||
|
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
|
||||||
|
list_for_each(tmp2, &tcon->openFileList) {
|
||||||
|
cfile = list_entry(tmp2, struct cifsFileInfo,
|
||||||
|
tlist);
|
||||||
|
cinode = CIFS_I(cfile->dentry->d_inode);
|
||||||
|
|
||||||
|
if (memcmp(cinode->lease_key, rsp->LeaseKey,
|
||||||
|
SMB2_LEASE_KEY_SIZE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cFYI(1, "found in the open list");
|
||||||
|
cFYI(1, "lease key match, lease break 0x%d",
|
||||||
|
le32_to_cpu(rsp->NewLeaseState));
|
||||||
|
|
||||||
|
smb2_set_oplock_level(cinode,
|
||||||
|
smb2_map_lease_to_oplock(rsp->NewLeaseState));
|
||||||
|
|
||||||
|
if (ack_req)
|
||||||
|
cfile->oplock_break_cancelled = false;
|
||||||
|
else
|
||||||
|
cfile->oplock_break_cancelled = true;
|
||||||
|
|
||||||
|
queue_work(cifsiod_wq, &cfile->oplock_break);
|
||||||
|
|
||||||
|
spin_unlock(&cifs_file_list_lock);
|
||||||
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
found = false;
|
||||||
|
list_for_each_entry(open, &tcon->pending_opens, olist) {
|
||||||
|
if (memcmp(open->lease_key, rsp->LeaseKey,
|
||||||
|
SMB2_LEASE_KEY_SIZE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!found && ack_req) {
|
||||||
|
found = true;
|
||||||
|
memcpy(lw->lease_key, open->lease_key,
|
||||||
|
SMB2_LEASE_KEY_SIZE);
|
||||||
|
lw->tlink = cifs_get_tlink(open->tlink);
|
||||||
|
queue_work(cifsiod_wq,
|
||||||
|
&lw->lease_break);
|
||||||
|
}
|
||||||
|
|
||||||
|
cFYI(1, "found in the pending open list");
|
||||||
|
cFYI(1, "lease key match, lease break 0x%d",
|
||||||
|
le32_to_cpu(rsp->NewLeaseState));
|
||||||
|
|
||||||
|
open->oplock =
|
||||||
|
smb2_map_lease_to_oplock(rsp->NewLeaseState);
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
spin_unlock(&cifs_file_list_lock);
|
||||||
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&cifs_file_list_lock);
|
||||||
|
}
|
||||||
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
kfree(lw);
|
||||||
|
cFYI(1, "Can not process lease break - no lease matched");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
||||||
|
{
|
||||||
|
struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer;
|
||||||
|
struct list_head *tmp, *tmp1, *tmp2;
|
||||||
|
struct cifs_ses *ses;
|
||||||
|
struct cifs_tcon *tcon;
|
||||||
|
struct cifsInodeInfo *cinode;
|
||||||
|
struct cifsFileInfo *cfile;
|
||||||
|
|
||||||
|
cFYI(1, "Checking for oplock break");
|
||||||
|
|
||||||
|
if (rsp->hdr.Command != SMB2_OPLOCK_BREAK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (rsp->StructureSize !=
|
||||||
|
smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
|
||||||
|
if (le16_to_cpu(rsp->StructureSize) == 44)
|
||||||
|
return smb2_is_valid_lease_break(buffer, server);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cFYI(1, "oplock level 0x%d", rsp->OplockLevel);
|
||||||
|
|
||||||
|
/* look up tcon based on tid & uid */
|
||||||
|
spin_lock(&cifs_tcp_ses_lock);
|
||||||
|
list_for_each(tmp, &server->smb_ses_list) {
|
||||||
|
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
|
||||||
|
list_for_each(tmp1, &ses->tcon_list) {
|
||||||
|
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
|
||||||
|
|
||||||
|
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
|
||||||
|
spin_lock(&cifs_file_list_lock);
|
||||||
|
list_for_each(tmp2, &tcon->openFileList) {
|
||||||
|
cfile = list_entry(tmp2, struct cifsFileInfo,
|
||||||
|
tlist);
|
||||||
|
if (rsp->PersistentFid !=
|
||||||
|
cfile->fid.persistent_fid ||
|
||||||
|
rsp->VolatileFid !=
|
||||||
|
cfile->fid.volatile_fid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cFYI(1, "file id match, oplock break");
|
||||||
|
cinode = CIFS_I(cfile->dentry->d_inode);
|
||||||
|
|
||||||
|
if (!cinode->clientCanCacheAll &&
|
||||||
|
rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE)
|
||||||
|
cfile->oplock_break_cancelled = true;
|
||||||
|
else
|
||||||
|
cfile->oplock_break_cancelled = false;
|
||||||
|
|
||||||
|
smb2_set_oplock_level(cinode,
|
||||||
|
rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0);
|
||||||
|
|
||||||
|
queue_work(cifsiod_wq, &cfile->oplock_break);
|
||||||
|
|
||||||
|
spin_unlock(&cifs_file_list_lock);
|
||||||
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
spin_unlock(&cifs_file_list_lock);
|
||||||
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
cFYI(1, "No matching file for oplock break");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
cFYI(1, "Can not process oplock break for non-existent connection");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -17,11 +17,15 @@
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/pagemap.h>
|
||||||
|
#include <linux/vfs.h>
|
||||||
#include "cifsglob.h"
|
#include "cifsglob.h"
|
||||||
#include "smb2pdu.h"
|
#include "smb2pdu.h"
|
||||||
#include "smb2proto.h"
|
#include "smb2proto.h"
|
||||||
#include "cifsproto.h"
|
#include "cifsproto.h"
|
||||||
#include "cifs_debug.h"
|
#include "cifs_debug.h"
|
||||||
|
#include "smb2status.h"
|
||||||
|
#include "smb2glob.h"
|
||||||
|
|
||||||
static int
|
static int
|
||||||
change_conf(struct TCP_Server_Info *server)
|
change_conf(struct TCP_Server_Info *server)
|
||||||
|
@ -63,6 +67,17 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
|
||||||
server->in_flight--;
|
server->in_flight--;
|
||||||
if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP)
|
if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP)
|
||||||
rc = change_conf(server);
|
rc = change_conf(server);
|
||||||
|
/*
|
||||||
|
* Sometimes server returns 0 credits on oplock break ack - we need to
|
||||||
|
* rebalance credits in this case.
|
||||||
|
*/
|
||||||
|
else if (server->in_flight > 0 && server->oplock_credits == 0 &&
|
||||||
|
server->oplocks) {
|
||||||
|
if (server->credits > 1) {
|
||||||
|
server->credits--;
|
||||||
|
server->oplock_credits++;
|
||||||
|
}
|
||||||
|
}
|
||||||
spin_unlock(&server->req_lock);
|
spin_unlock(&server->req_lock);
|
||||||
wake_up(&server->request_q);
|
wake_up(&server->request_q);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -157,6 +172,42 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||||
|
{
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
unsigned int wsize;
|
||||||
|
|
||||||
|
/* start with specified wsize, or default */
|
||||||
|
wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE;
|
||||||
|
wsize = min_t(unsigned int, wsize, server->max_write);
|
||||||
|
/*
|
||||||
|
* limit write size to 2 ** 16, because we don't support multicredit
|
||||||
|
* requests now.
|
||||||
|
*/
|
||||||
|
wsize = min_t(unsigned int, wsize, 2 << 15);
|
||||||
|
|
||||||
|
return wsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||||
|
{
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
unsigned int rsize;
|
||||||
|
|
||||||
|
/* start with specified rsize, or default */
|
||||||
|
rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE;
|
||||||
|
rsize = min_t(unsigned int, rsize, server->max_read);
|
||||||
|
/*
|
||||||
|
* limit write size to 2 ** 16, because we don't support multicredit
|
||||||
|
* requests now.
|
||||||
|
*/
|
||||||
|
rsize = min_t(unsigned int, rsize, 2 << 15);
|
||||||
|
|
||||||
|
return rsize;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
|
smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
struct cifs_sb_info *cifs_sb, const char *full_path)
|
struct cifs_sb_info *cifs_sb, const char *full_path)
|
||||||
|
@ -164,13 +215,14 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
int rc;
|
int rc;
|
||||||
__u64 persistent_fid, volatile_fid;
|
__u64 persistent_fid, volatile_fid;
|
||||||
__le16 *utf16_path;
|
__le16 *utf16_path;
|
||||||
|
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||||
|
|
||||||
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
|
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
|
||||||
if (!utf16_path)
|
if (!utf16_path)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
|
rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
|
||||||
FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0);
|
FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
kfree(utf16_path);
|
kfree(utf16_path);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -190,6 +242,26 @@ smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct cifs_fid *fid, FILE_ALL_INFO *data)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct smb2_file_all_info *smb2_data;
|
||||||
|
|
||||||
|
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (smb2_data == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rc = SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid,
|
||||||
|
smb2_data);
|
||||||
|
if (!rc)
|
||||||
|
move_smb2_info_to_cifs(data, smb2_data);
|
||||||
|
kfree(smb2_data);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
smb2_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
|
smb2_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
|
||||||
struct cifs_tcon *tcon)
|
struct cifs_tcon *tcon)
|
||||||
|
@ -292,7 +364,221 @@ smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
|
||||||
|
{
|
||||||
|
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
|
||||||
|
cfile->fid.persistent_fid = fid->persistent_fid;
|
||||||
|
cfile->fid.volatile_fid = fid->volatile_fid;
|
||||||
|
smb2_set_oplock_level(cinode, oplock);
|
||||||
|
cinode->can_cache_brlcks = cinode->clientCanCacheAll;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
smb2_read_data_offset(char *buf)
|
||||||
|
{
|
||||||
|
struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf;
|
||||||
|
return rsp->DataOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
smb2_read_data_length(char *buf)
|
||||||
|
{
|
||||||
|
struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf;
|
||||||
|
return le32_to_cpu(rsp->DataLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_sync_read(const unsigned int xid, struct cifsFileInfo *cfile,
|
||||||
|
struct cifs_io_parms *parms, unsigned int *bytes_read,
|
||||||
|
char **buf, int *buf_type)
|
||||||
|
{
|
||||||
|
parms->persistent_fid = cfile->fid.persistent_fid;
|
||||||
|
parms->volatile_fid = cfile->fid.volatile_fid;
|
||||||
|
return SMB2_read(xid, parms, bytes_read, buf, buf_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_sync_write(const unsigned int xid, struct cifsFileInfo *cfile,
|
||||||
|
struct cifs_io_parms *parms, unsigned int *written,
|
||||||
|
struct kvec *iov, unsigned long nr_segs)
|
||||||
|
{
|
||||||
|
|
||||||
|
parms->persistent_fid = cfile->fid.persistent_fid;
|
||||||
|
parms->volatile_fid = cfile->fid.volatile_fid;
|
||||||
|
return SMB2_write(xid, parms, written, iov, nr_segs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct cifsFileInfo *cfile, __u64 size, bool set_alloc)
|
||||||
|
{
|
||||||
|
__le64 eof = cpu_to_le64(size);
|
||||||
|
return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||||
|
cfile->fid.volatile_fid, cfile->pid, &eof);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *path, struct cifs_sb_info *cifs_sb,
|
||||||
|
struct cifs_fid *fid, __u16 search_flags,
|
||||||
|
struct cifs_search_info *srch_inf)
|
||||||
|
{
|
||||||
|
__le16 *utf16_path;
|
||||||
|
int rc;
|
||||||
|
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||||
|
__u64 persistent_fid, volatile_fid;
|
||||||
|
|
||||||
|
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
|
||||||
|
if (!utf16_path)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
|
||||||
|
FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_OPEN, 0, 0,
|
||||||
|
&oplock, NULL);
|
||||||
|
kfree(utf16_path);
|
||||||
|
if (rc) {
|
||||||
|
cERROR(1, "open dir failed");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
srch_inf->entries_in_buffer = 0;
|
||||||
|
srch_inf->index_of_last_entry = 0;
|
||||||
|
fid->persistent_fid = persistent_fid;
|
||||||
|
fid->volatile_fid = volatile_fid;
|
||||||
|
|
||||||
|
rc = SMB2_query_directory(xid, tcon, persistent_fid, volatile_fid, 0,
|
||||||
|
srch_inf);
|
||||||
|
if (rc) {
|
||||||
|
cERROR(1, "query directory failed");
|
||||||
|
SMB2_close(xid, tcon, persistent_fid, volatile_fid);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct cifs_fid *fid, __u16 search_flags,
|
||||||
|
struct cifs_search_info *srch_inf)
|
||||||
|
{
|
||||||
|
return SMB2_query_directory(xid, tcon, fid->persistent_fid,
|
||||||
|
fid->volatile_fid, 0, srch_inf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we negotiate SMB2 protocol and get STATUS_PENDING - update
|
||||||
|
* the number of credits and return true. Otherwise - return false.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length)
|
||||||
|
{
|
||||||
|
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
|
||||||
|
|
||||||
|
if (hdr->Status != STATUS_PENDING)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!length) {
|
||||||
|
spin_lock(&server->req_lock);
|
||||||
|
server->credits += le16_to_cpu(hdr->CreditRequest);
|
||||||
|
spin_unlock(&server->req_lock);
|
||||||
|
wake_up(&server->request_q);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
|
||||||
|
struct cifsInodeInfo *cinode)
|
||||||
|
{
|
||||||
|
if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
|
||||||
|
return SMB2_lease_break(0, tcon, cinode->lease_key,
|
||||||
|
smb2_get_lease_state(cinode));
|
||||||
|
|
||||||
|
return SMB2_oplock_break(0, tcon, fid->persistent_fid,
|
||||||
|
fid->volatile_fid,
|
||||||
|
cinode->clientCanCacheRead ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
struct kstatfs *buf)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u64 persistent_fid, volatile_fid;
|
||||||
|
__le16 srch_path = 0; /* Null - open root of share */
|
||||||
|
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||||
|
|
||||||
|
rc = SMB2_open(xid, tcon, &srch_path, &persistent_fid, &volatile_fid,
|
||||||
|
FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
buf->f_type = SMB2_MAGIC_NUMBER;
|
||||||
|
rc = SMB2_QFS_info(xid, tcon, persistent_fid, volatile_fid, buf);
|
||||||
|
SMB2_close(xid, tcon, persistent_fid, volatile_fid);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
smb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
|
||||||
|
{
|
||||||
|
return ob1->fid.persistent_fid == ob2->fid.persistent_fid &&
|
||||||
|
ob1->fid.volatile_fid == ob2->fid.volatile_fid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
|
||||||
|
__u64 length, __u32 type, int lock, int unlock, bool wait)
|
||||||
|
{
|
||||||
|
if (unlock && !lock)
|
||||||
|
type = SMB2_LOCKFLAG_UNLOCK;
|
||||||
|
return SMB2_lock(xid, tlink_tcon(cfile->tlink),
|
||||||
|
cfile->fid.persistent_fid, cfile->fid.volatile_fid,
|
||||||
|
current->tgid, length, offset, type, wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smb2_get_lease_key(struct inode *inode, struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smb2_new_lease_key(struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
struct smb_version_operations smb21_operations = {
|
struct smb_version_operations smb21_operations = {
|
||||||
|
.compare_fids = smb2_compare_fids,
|
||||||
.setup_request = smb2_setup_request,
|
.setup_request = smb2_setup_request,
|
||||||
.setup_async_request = smb2_setup_async_request,
|
.setup_async_request = smb2_setup_async_request,
|
||||||
.check_receive = smb2_check_receive,
|
.check_receive = smb2_check_receive,
|
||||||
|
@ -301,13 +587,19 @@ struct smb_version_operations smb21_operations = {
|
||||||
.get_credits_field = smb2_get_credits_field,
|
.get_credits_field = smb2_get_credits_field,
|
||||||
.get_credits = smb2_get_credits,
|
.get_credits = smb2_get_credits,
|
||||||
.get_next_mid = smb2_get_next_mid,
|
.get_next_mid = smb2_get_next_mid,
|
||||||
|
.read_data_offset = smb2_read_data_offset,
|
||||||
|
.read_data_length = smb2_read_data_length,
|
||||||
|
.map_error = map_smb2_to_linux_error,
|
||||||
.find_mid = smb2_find_mid,
|
.find_mid = smb2_find_mid,
|
||||||
.check_message = smb2_check_message,
|
.check_message = smb2_check_message,
|
||||||
.dump_detail = smb2_dump_detail,
|
.dump_detail = smb2_dump_detail,
|
||||||
.clear_stats = smb2_clear_stats,
|
.clear_stats = smb2_clear_stats,
|
||||||
.print_stats = smb2_print_stats,
|
.print_stats = smb2_print_stats,
|
||||||
|
.is_oplock_break = smb2_is_valid_oplock_break,
|
||||||
.need_neg = smb2_need_neg,
|
.need_neg = smb2_need_neg,
|
||||||
.negotiate = smb2_negotiate,
|
.negotiate = smb2_negotiate,
|
||||||
|
.negotiate_wsize = smb2_negotiate_wsize,
|
||||||
|
.negotiate_rsize = smb2_negotiate_rsize,
|
||||||
.sess_setup = SMB2_sess_setup,
|
.sess_setup = SMB2_sess_setup,
|
||||||
.logoff = SMB2_logoff,
|
.logoff = SMB2_logoff,
|
||||||
.tree_connect = SMB2_tcon,
|
.tree_connect = SMB2_tcon,
|
||||||
|
@ -317,16 +609,68 @@ struct smb_version_operations smb21_operations = {
|
||||||
.echo = SMB2_echo,
|
.echo = SMB2_echo,
|
||||||
.query_path_info = smb2_query_path_info,
|
.query_path_info = smb2_query_path_info,
|
||||||
.get_srv_inum = smb2_get_srv_inum,
|
.get_srv_inum = smb2_get_srv_inum,
|
||||||
|
.query_file_info = smb2_query_file_info,
|
||||||
|
.set_path_size = smb2_set_path_size,
|
||||||
|
.set_file_size = smb2_set_file_size,
|
||||||
|
.set_file_info = smb2_set_file_info,
|
||||||
.build_path_to_root = smb2_build_path_to_root,
|
.build_path_to_root = smb2_build_path_to_root,
|
||||||
.mkdir = smb2_mkdir,
|
.mkdir = smb2_mkdir,
|
||||||
.mkdir_setinfo = smb2_mkdir_setinfo,
|
.mkdir_setinfo = smb2_mkdir_setinfo,
|
||||||
.rmdir = smb2_rmdir,
|
.rmdir = smb2_rmdir,
|
||||||
|
.unlink = smb2_unlink,
|
||||||
|
.rename = smb2_rename_path,
|
||||||
|
.create_hardlink = smb2_create_hardlink,
|
||||||
|
.open = smb2_open_file,
|
||||||
|
.set_fid = smb2_set_fid,
|
||||||
|
.close = smb2_close_file,
|
||||||
|
.flush = smb2_flush_file,
|
||||||
|
.async_readv = smb2_async_readv,
|
||||||
|
.async_writev = smb2_async_writev,
|
||||||
|
.sync_read = smb2_sync_read,
|
||||||
|
.sync_write = smb2_sync_write,
|
||||||
|
.query_dir_first = smb2_query_dir_first,
|
||||||
|
.query_dir_next = smb2_query_dir_next,
|
||||||
|
.close_dir = smb2_close_dir,
|
||||||
|
.calc_smb_size = smb2_calc_size,
|
||||||
|
.is_status_pending = smb2_is_status_pending,
|
||||||
|
.oplock_response = smb2_oplock_response,
|
||||||
|
.queryfs = smb2_queryfs,
|
||||||
|
.mand_lock = smb2_mand_lock,
|
||||||
|
.mand_unlock_range = smb2_unlock_range,
|
||||||
|
.push_mand_locks = smb2_push_mandatory_locks,
|
||||||
|
.get_lease_key = smb2_get_lease_key,
|
||||||
|
.set_lease_key = smb2_set_lease_key,
|
||||||
|
.new_lease_key = smb2_new_lease_key,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values smb21_values = {
|
struct smb_version_values smb21_values = {
|
||||||
.version_string = SMB21_VERSION_STRING,
|
.version_string = SMB21_VERSION_STRING,
|
||||||
|
.protocol_id = SMB21_PROT_ID,
|
||||||
|
.req_capabilities = 0, /* MBZ on negotiate req until SMB3 dialect */
|
||||||
|
.large_lock_type = 0,
|
||||||
|
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
|
||||||
|
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
|
||||||
|
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
||||||
.header_size = sizeof(struct smb2_hdr),
|
.header_size = sizeof(struct smb2_hdr),
|
||||||
.max_header_size = MAX_SMB2_HDR_SIZE,
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
||||||
|
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
||||||
|
.lock_cmd = SMB2_LOCK,
|
||||||
|
.cap_unix = 0,
|
||||||
|
.cap_nt_find = SMB2_NT_FIND,
|
||||||
|
.cap_large_files = SMB2_LARGE_FILES,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct smb_version_values smb30_values = {
|
||||||
|
.version_string = SMB30_VERSION_STRING,
|
||||||
|
.protocol_id = SMB30_PROT_ID,
|
||||||
|
.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU,
|
||||||
|
.large_lock_type = 0,
|
||||||
|
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
|
||||||
|
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
|
||||||
|
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
||||||
|
.header_size = sizeof(struct smb2_hdr),
|
||||||
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
||||||
|
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
||||||
.lock_cmd = SMB2_LOCK,
|
.lock_cmd = SMB2_LOCK,
|
||||||
.cap_unix = 0,
|
.cap_unix = 0,
|
||||||
.cap_nt_find = SMB2_NT_FIND,
|
.cap_nt_find = SMB2_NT_FIND,
|
||||||
|
|
1235
fs/cifs/smb2pdu.c
1235
fs/cifs/smb2pdu.c
File diff suppressed because it is too large
Load Diff
|
@ -150,6 +150,10 @@ struct smb2_err_rsp {
|
||||||
__u8 ErrorData[1]; /* variable length */
|
__u8 ErrorData[1]; /* variable length */
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define SMB2_CLIENT_GUID_SIZE 16
|
||||||
|
|
||||||
|
extern __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
|
||||||
|
|
||||||
struct smb2_negotiate_req {
|
struct smb2_negotiate_req {
|
||||||
struct smb2_hdr hdr;
|
struct smb2_hdr hdr;
|
||||||
__le16 StructureSize; /* Must be 36 */
|
__le16 StructureSize; /* Must be 36 */
|
||||||
|
@ -157,11 +161,17 @@ struct smb2_negotiate_req {
|
||||||
__le16 SecurityMode;
|
__le16 SecurityMode;
|
||||||
__le16 Reserved; /* MBZ */
|
__le16 Reserved; /* MBZ */
|
||||||
__le32 Capabilities;
|
__le32 Capabilities;
|
||||||
__u8 ClientGUID[16]; /* MBZ */
|
__u8 ClientGUID[SMB2_CLIENT_GUID_SIZE];
|
||||||
__le64 ClientStartTime; /* MBZ */
|
__le64 ClientStartTime; /* MBZ */
|
||||||
__le16 Dialects[2]; /* variable length */
|
__le16 Dialects[1]; /* One dialect (vers=) at a time for now */
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
/* Dialects */
|
||||||
|
#define SMB20_PROT_ID 0x0202
|
||||||
|
#define SMB21_PROT_ID 0x0210
|
||||||
|
#define SMB30_PROT_ID 0x0300
|
||||||
|
#define BAD_PROT_ID 0xFFFF
|
||||||
|
|
||||||
/* SecurityMode flags */
|
/* SecurityMode flags */
|
||||||
#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x0001
|
#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x0001
|
||||||
#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002
|
#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002
|
||||||
|
@ -169,6 +179,10 @@ struct smb2_negotiate_req {
|
||||||
#define SMB2_GLOBAL_CAP_DFS 0x00000001
|
#define SMB2_GLOBAL_CAP_DFS 0x00000001
|
||||||
#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */
|
#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */
|
||||||
#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */
|
#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */
|
||||||
|
#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */
|
||||||
|
#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */
|
||||||
|
#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */
|
||||||
|
#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */
|
||||||
/* Internal types */
|
/* Internal types */
|
||||||
#define SMB2_NT_FIND 0x00100000
|
#define SMB2_NT_FIND 0x00100000
|
||||||
#define SMB2_LARGE_FILES 0x00200000
|
#define SMB2_LARGE_FILES 0x00200000
|
||||||
|
@ -307,6 +321,8 @@ struct smb2_tree_disconnect_rsp {
|
||||||
#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
|
#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
|
||||||
#define SMB2_OPLOCK_LEVEL_BATCH 0x09
|
#define SMB2_OPLOCK_LEVEL_BATCH 0x09
|
||||||
#define SMB2_OPLOCK_LEVEL_LEASE 0xFF
|
#define SMB2_OPLOCK_LEVEL_LEASE 0xFF
|
||||||
|
/* Non-spec internal type */
|
||||||
|
#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99
|
||||||
|
|
||||||
/* Desired Access Flags */
|
/* Desired Access Flags */
|
||||||
#define FILE_READ_DATA_LE cpu_to_le32(0x00000001)
|
#define FILE_READ_DATA_LE cpu_to_le32(0x00000001)
|
||||||
|
@ -404,7 +420,7 @@ struct smb2_create_req {
|
||||||
__le16 NameLength;
|
__le16 NameLength;
|
||||||
__le32 CreateContextsOffset;
|
__le32 CreateContextsOffset;
|
||||||
__le32 CreateContextsLength;
|
__le32 CreateContextsLength;
|
||||||
__u8 Buffer[1];
|
__u8 Buffer[8];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
struct smb2_create_rsp {
|
struct smb2_create_rsp {
|
||||||
|
@ -428,6 +444,39 @@ struct smb2_create_rsp {
|
||||||
__u8 Buffer[1];
|
__u8 Buffer[1];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct create_context {
|
||||||
|
__le32 Next;
|
||||||
|
__le16 NameOffset;
|
||||||
|
__le16 NameLength;
|
||||||
|
__le16 Reserved;
|
||||||
|
__le16 DataOffset;
|
||||||
|
__le32 DataLength;
|
||||||
|
__u8 Buffer[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define SMB2_LEASE_NONE __constant_cpu_to_le32(0x00)
|
||||||
|
#define SMB2_LEASE_READ_CACHING __constant_cpu_to_le32(0x01)
|
||||||
|
#define SMB2_LEASE_HANDLE_CACHING __constant_cpu_to_le32(0x02)
|
||||||
|
#define SMB2_LEASE_WRITE_CACHING __constant_cpu_to_le32(0x04)
|
||||||
|
|
||||||
|
#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS __constant_cpu_to_le32(0x02)
|
||||||
|
|
||||||
|
#define SMB2_LEASE_KEY_SIZE 16
|
||||||
|
|
||||||
|
struct lease_context {
|
||||||
|
__le64 LeaseKeyLow;
|
||||||
|
__le64 LeaseKeyHigh;
|
||||||
|
__le32 LeaseState;
|
||||||
|
__le32 LeaseFlags;
|
||||||
|
__le64 LeaseDuration;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct create_lease {
|
||||||
|
struct create_context ccontext;
|
||||||
|
__u8 Name[8];
|
||||||
|
struct lease_context lcontext;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/* Currently defined values for close flags */
|
/* Currently defined values for close flags */
|
||||||
#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001)
|
#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001)
|
||||||
struct smb2_close_req {
|
struct smb2_close_req {
|
||||||
|
@ -453,6 +502,108 @@ struct smb2_close_rsp {
|
||||||
__le32 Attributes;
|
__le32 Attributes;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_flush_req {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 24 */
|
||||||
|
__le16 Reserved1;
|
||||||
|
__le32 Reserved2;
|
||||||
|
__u64 PersistentFileId; /* opaque endianness */
|
||||||
|
__u64 VolatileFileId; /* opaque endianness */
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_flush_rsp {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize;
|
||||||
|
__le16 Reserved;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_read_req {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 49 */
|
||||||
|
__u8 Padding; /* offset from start of SMB2 header to place read */
|
||||||
|
__u8 Reserved;
|
||||||
|
__le32 Length;
|
||||||
|
__le64 Offset;
|
||||||
|
__u64 PersistentFileId; /* opaque endianness */
|
||||||
|
__u64 VolatileFileId; /* opaque endianness */
|
||||||
|
__le32 MinimumCount;
|
||||||
|
__le32 Channel; /* Reserved MBZ */
|
||||||
|
__le32 RemainingBytes;
|
||||||
|
__le16 ReadChannelInfoOffset; /* Reserved MBZ */
|
||||||
|
__le16 ReadChannelInfoLength; /* Reserved MBZ */
|
||||||
|
__u8 Buffer[1];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_read_rsp {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 17 */
|
||||||
|
__u8 DataOffset;
|
||||||
|
__u8 Reserved;
|
||||||
|
__le32 DataLength;
|
||||||
|
__le32 DataRemaining;
|
||||||
|
__u32 Reserved2;
|
||||||
|
__u8 Buffer[1];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
/* For write request Flags field below the following flag is defined: */
|
||||||
|
#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001
|
||||||
|
|
||||||
|
struct smb2_write_req {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 49 */
|
||||||
|
__le16 DataOffset; /* offset from start of SMB2 header to write data */
|
||||||
|
__le32 Length;
|
||||||
|
__le64 Offset;
|
||||||
|
__u64 PersistentFileId; /* opaque endianness */
|
||||||
|
__u64 VolatileFileId; /* opaque endianness */
|
||||||
|
__le32 Channel; /* Reserved MBZ */
|
||||||
|
__le32 RemainingBytes;
|
||||||
|
__le16 WriteChannelInfoOffset; /* Reserved MBZ */
|
||||||
|
__le16 WriteChannelInfoLength; /* Reserved MBZ */
|
||||||
|
__le32 Flags;
|
||||||
|
__u8 Buffer[1];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_write_rsp {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 17 */
|
||||||
|
__u8 DataOffset;
|
||||||
|
__u8 Reserved;
|
||||||
|
__le32 DataLength;
|
||||||
|
__le32 DataRemaining;
|
||||||
|
__u32 Reserved2;
|
||||||
|
__u8 Buffer[1];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define SMB2_LOCKFLAG_SHARED_LOCK 0x0001
|
||||||
|
#define SMB2_LOCKFLAG_EXCLUSIVE_LOCK 0x0002
|
||||||
|
#define SMB2_LOCKFLAG_UNLOCK 0x0004
|
||||||
|
#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010
|
||||||
|
|
||||||
|
struct smb2_lock_element {
|
||||||
|
__le64 Offset;
|
||||||
|
__le64 Length;
|
||||||
|
__le32 Flags;
|
||||||
|
__le32 Reserved;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_lock_req {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 48 */
|
||||||
|
__le16 LockCount;
|
||||||
|
__le32 Reserved;
|
||||||
|
__u64 PersistentFileId; /* opaque endianness */
|
||||||
|
__u64 VolatileFileId; /* opaque endianness */
|
||||||
|
/* Followed by at least one */
|
||||||
|
struct smb2_lock_element locks[1];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_lock_rsp {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 4 */
|
||||||
|
__le16 Reserved;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
struct smb2_echo_req {
|
struct smb2_echo_req {
|
||||||
struct smb2_hdr hdr;
|
struct smb2_hdr hdr;
|
||||||
__le16 StructureSize; /* Must be 4 */
|
__le16 StructureSize; /* Must be 4 */
|
||||||
|
@ -465,6 +616,34 @@ struct smb2_echo_rsp {
|
||||||
__u16 Reserved;
|
__u16 Reserved;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
/* search (query_directory) Flags field */
|
||||||
|
#define SMB2_RESTART_SCANS 0x01
|
||||||
|
#define SMB2_RETURN_SINGLE_ENTRY 0x02
|
||||||
|
#define SMB2_INDEX_SPECIFIED 0x04
|
||||||
|
#define SMB2_REOPEN 0x10
|
||||||
|
|
||||||
|
struct smb2_query_directory_req {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 33 */
|
||||||
|
__u8 FileInformationClass;
|
||||||
|
__u8 Flags;
|
||||||
|
__le32 FileIndex;
|
||||||
|
__u64 PersistentFileId; /* opaque endianness */
|
||||||
|
__u64 VolatileFileId; /* opaque endianness */
|
||||||
|
__le16 FileNameOffset;
|
||||||
|
__le16 FileNameLength;
|
||||||
|
__le32 OutputBufferLength;
|
||||||
|
__u8 Buffer[1];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_query_directory_rsp {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 9 */
|
||||||
|
__le16 OutputBufferOffset;
|
||||||
|
__le32 OutputBufferLength;
|
||||||
|
__u8 Buffer[1];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/* Possible InfoType values */
|
/* Possible InfoType values */
|
||||||
#define SMB2_O_INFO_FILE 0x01
|
#define SMB2_O_INFO_FILE 0x01
|
||||||
#define SMB2_O_INFO_FILESYSTEM 0x02
|
#define SMB2_O_INFO_FILESYSTEM 0x02
|
||||||
|
@ -495,11 +674,84 @@ struct smb2_query_info_rsp {
|
||||||
__u8 Buffer[1];
|
__u8 Buffer[1];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_set_info_req {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 33 */
|
||||||
|
__u8 InfoType;
|
||||||
|
__u8 FileInfoClass;
|
||||||
|
__le32 BufferLength;
|
||||||
|
__le16 BufferOffset;
|
||||||
|
__u16 Reserved;
|
||||||
|
__le32 AdditionalInformation;
|
||||||
|
__u64 PersistentFileId; /* opaque endianness */
|
||||||
|
__u64 VolatileFileId; /* opaque endianness */
|
||||||
|
__u8 Buffer[1];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_set_info_rsp {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 2 */
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_oplock_break {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 24 */
|
||||||
|
__u8 OplockLevel;
|
||||||
|
__u8 Reserved;
|
||||||
|
__le32 Reserved2;
|
||||||
|
__u64 PersistentFid;
|
||||||
|
__u64 VolatileFid;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01)
|
||||||
|
|
||||||
|
struct smb2_lease_break {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 44 */
|
||||||
|
__le16 Reserved;
|
||||||
|
__le32 Flags;
|
||||||
|
__u8 LeaseKey[16];
|
||||||
|
__le32 CurrentLeaseState;
|
||||||
|
__le32 NewLeaseState;
|
||||||
|
__le32 BreakReason;
|
||||||
|
__le32 AccessMaskHint;
|
||||||
|
__le32 ShareMaskHint;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct smb2_lease_ack {
|
||||||
|
struct smb2_hdr hdr;
|
||||||
|
__le16 StructureSize; /* Must be 36 */
|
||||||
|
__le16 Reserved;
|
||||||
|
__le32 Flags;
|
||||||
|
__u8 LeaseKey[16];
|
||||||
|
__le32 LeaseState;
|
||||||
|
__le64 LeaseDuration;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PDU infolevel structure definitions
|
* PDU infolevel structure definitions
|
||||||
* BB consider moving to a different header
|
* BB consider moving to a different header
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* File System Information Classes */
|
||||||
|
#define FS_VOLUME_INFORMATION 1 /* Query */
|
||||||
|
#define FS_LABEL_INFORMATION 2 /* Set */
|
||||||
|
#define FS_SIZE_INFORMATION 3 /* Query */
|
||||||
|
#define FS_DEVICE_INFORMATION 4 /* Query */
|
||||||
|
#define FS_ATTRIBUTE_INFORMATION 5 /* Query */
|
||||||
|
#define FS_CONTROL_INFORMATION 6 /* Query, Set */
|
||||||
|
#define FS_FULL_SIZE_INFORMATION 7 /* Query */
|
||||||
|
#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */
|
||||||
|
#define FS_DRIVER_PATH_INFORMATION 9 /* Query */
|
||||||
|
|
||||||
|
struct smb2_fs_full_size_info {
|
||||||
|
__le64 TotalAllocationUnits;
|
||||||
|
__le64 CallerAvailableAllocationUnits;
|
||||||
|
__le64 ActualAvailableAllocationUnits;
|
||||||
|
__le32 SectorsPerAllocationUnit;
|
||||||
|
__le32 BytesPerSector;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/* partial list of QUERY INFO levels */
|
/* partial list of QUERY INFO levels */
|
||||||
#define FILE_DIRECTORY_INFORMATION 1
|
#define FILE_DIRECTORY_INFORMATION 1
|
||||||
#define FILE_FULL_DIRECTORY_INFORMATION 2
|
#define FILE_FULL_DIRECTORY_INFORMATION 2
|
||||||
|
@ -548,6 +800,28 @@ struct smb2_query_info_rsp {
|
||||||
#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50
|
#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50
|
||||||
#define FILE_STANDARD_LINK_INFORMATION 54
|
#define FILE_STANDARD_LINK_INFORMATION 54
|
||||||
|
|
||||||
|
struct smb2_file_internal_info {
|
||||||
|
__le64 IndexNumber;
|
||||||
|
} __packed; /* level 6 Query */
|
||||||
|
|
||||||
|
struct smb2_file_rename_info { /* encoding of request for level 10 */
|
||||||
|
__u8 ReplaceIfExists; /* 1 = replace existing target with new */
|
||||||
|
/* 0 = fail if target already exists */
|
||||||
|
__u8 Reserved[7];
|
||||||
|
__u64 RootDirectory; /* MBZ for network operations (why says spec?) */
|
||||||
|
__le32 FileNameLength;
|
||||||
|
char FileName[0]; /* New name to be assigned */
|
||||||
|
} __packed; /* level 10 Set */
|
||||||
|
|
||||||
|
struct smb2_file_link_info { /* encoding of request for level 11 */
|
||||||
|
__u8 ReplaceIfExists; /* 1 = replace existing link with new */
|
||||||
|
/* 0 = fail if link already exists */
|
||||||
|
__u8 Reserved[7];
|
||||||
|
__u64 RootDirectory; /* MBZ for network operations (why says spec?) */
|
||||||
|
__le32 FileNameLength;
|
||||||
|
char FileName[0]; /* Name to be assigned to new link */
|
||||||
|
} __packed; /* level 11 Set */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This level 18, although with struct with same name is different from cifs
|
* This level 18, although with struct with same name is different from cifs
|
||||||
* level 0x107. Level 0x107 has an extra u64 between AccessFlags and
|
* level 0x107. Level 0x107 has an extra u64 between AccessFlags and
|
||||||
|
@ -576,4 +850,8 @@ struct smb2_file_all_info { /* data block encoding of response to level 18 */
|
||||||
char FileName[1];
|
char FileName[1];
|
||||||
} __packed; /* level 18 Query */
|
} __packed; /* level 18 Query */
|
||||||
|
|
||||||
|
struct smb2_file_eof_info { /* encoding of request for level 10 */
|
||||||
|
__le64 EndOfFile; /* new end of file value */
|
||||||
|
} __packed; /* level 20 Set */
|
||||||
|
|
||||||
#endif /* _SMB2PDU_H */
|
#endif /* _SMB2PDU_H */
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <linux/key-type.h>
|
#include <linux/key-type.h>
|
||||||
|
|
||||||
struct statfs;
|
struct statfs;
|
||||||
|
struct smb_rqst;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*****************************************************************
|
*****************************************************************
|
||||||
|
@ -34,24 +35,35 @@ struct statfs;
|
||||||
*/
|
*/
|
||||||
extern int map_smb2_to_linux_error(char *buf, bool log_err);
|
extern int map_smb2_to_linux_error(char *buf, bool log_err);
|
||||||
extern int smb2_check_message(char *buf, unsigned int length);
|
extern int smb2_check_message(char *buf, unsigned int length);
|
||||||
extern unsigned int smb2_calc_size(struct smb2_hdr *hdr);
|
extern unsigned int smb2_calc_size(void *buf);
|
||||||
extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr);
|
extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr);
|
||||||
extern __le16 *cifs_convert_path_to_utf16(const char *from,
|
extern __le16 *cifs_convert_path_to_utf16(const char *from,
|
||||||
struct cifs_sb_info *cifs_sb);
|
struct cifs_sb_info *cifs_sb);
|
||||||
|
|
||||||
|
extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *);
|
||||||
extern int smb2_check_receive(struct mid_q_entry *mid,
|
extern int smb2_check_receive(struct mid_q_entry *mid,
|
||||||
struct TCP_Server_Info *server, bool log_error);
|
struct TCP_Server_Info *server, bool log_error);
|
||||||
extern int smb2_setup_request(struct cifs_ses *ses, struct kvec *iov,
|
extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
|
||||||
unsigned int nvec, struct mid_q_entry **ret_mid);
|
struct smb_rqst *rqst);
|
||||||
extern int smb2_setup_async_request(struct TCP_Server_Info *server,
|
extern struct mid_q_entry *smb2_setup_async_request(
|
||||||
struct kvec *iov, unsigned int nvec,
|
struct TCP_Server_Info *server, struct smb_rqst *rqst);
|
||||||
struct mid_q_entry **ret_mid);
|
|
||||||
extern void smb2_echo_request(struct work_struct *work);
|
extern void smb2_echo_request(struct work_struct *work);
|
||||||
|
extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode);
|
||||||
|
extern __u8 smb2_map_lease_to_oplock(__le32 lease_state);
|
||||||
|
extern bool smb2_is_valid_oplock_break(char *buffer,
|
||||||
|
struct TCP_Server_Info *srv);
|
||||||
|
|
||||||
|
extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
|
||||||
|
struct smb2_file_all_info *src);
|
||||||
extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
struct cifs_sb_info *cifs_sb,
|
struct cifs_sb_info *cifs_sb,
|
||||||
const char *full_path, FILE_ALL_INFO *data,
|
const char *full_path, FILE_ALL_INFO *data,
|
||||||
bool *adjust_tz);
|
bool *adjust_tz);
|
||||||
|
extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *full_path, __u64 size,
|
||||||
|
struct cifs_sb_info *cifs_sb, bool set_alloc);
|
||||||
|
extern int smb2_set_file_info(struct inode *inode, const char *full_path,
|
||||||
|
FILE_BASIC_INFO *buf, const unsigned int xid);
|
||||||
extern int smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *name, struct cifs_sb_info *cifs_sb);
|
const char *name, struct cifs_sb_info *cifs_sb);
|
||||||
extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path,
|
extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path,
|
||||||
|
@ -59,6 +71,24 @@ extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path,
|
||||||
struct cifs_tcon *tcon, const unsigned int xid);
|
struct cifs_tcon *tcon, const unsigned int xid);
|
||||||
extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *name, struct cifs_sb_info *cifs_sb);
|
const char *name, struct cifs_sb_info *cifs_sb);
|
||||||
|
extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *name, struct cifs_sb_info *cifs_sb);
|
||||||
|
extern int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *from_name, const char *to_name,
|
||||||
|
struct cifs_sb_info *cifs_sb);
|
||||||
|
extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *from_name, const char *to_name,
|
||||||
|
struct cifs_sb_info *cifs_sb);
|
||||||
|
|
||||||
|
extern int smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *full_path, int disposition,
|
||||||
|
int desired_access, int create_options,
|
||||||
|
struct cifs_fid *fid, __u32 *oplock,
|
||||||
|
FILE_ALL_INFO *buf, struct cifs_sb_info *cifs_sb);
|
||||||
|
extern void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
|
||||||
|
extern int smb2_unlock_range(struct cifsFileInfo *cfile,
|
||||||
|
struct file_lock *flock, const unsigned int xid);
|
||||||
|
extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SMB2 Worker functions - most of protocol specific implementation details
|
* SMB2 Worker functions - most of protocol specific implementation details
|
||||||
|
@ -75,12 +105,55 @@ extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
|
||||||
extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
__le16 *path, u64 *persistent_fid, u64 *volatile_fid,
|
__le16 *path, u64 *persistent_fid, u64 *volatile_fid,
|
||||||
__u32 desired_access, __u32 create_disposition,
|
__u32 desired_access, __u32 create_disposition,
|
||||||
__u32 file_attributes, __u32 create_options);
|
__u32 file_attributes, __u32 create_options,
|
||||||
|
__u8 *oplock, struct smb2_file_all_info *buf);
|
||||||
extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
u64 persistent_file_id, u64 volatile_file_id);
|
u64 persistent_file_id, u64 volatile_file_id);
|
||||||
|
extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
u64 persistent_file_id, u64 volatile_file_id);
|
||||||
extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
u64 persistent_file_id, u64 volatile_file_id,
|
u64 persistent_file_id, u64 volatile_file_id,
|
||||||
struct smb2_file_all_info *data);
|
struct smb2_file_all_info *data);
|
||||||
|
extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
u64 persistent_fid, u64 volatile_fid,
|
||||||
|
__le64 *uniqueid);
|
||||||
|
extern int smb2_async_readv(struct cifs_readdata *rdata);
|
||||||
|
extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
|
||||||
|
unsigned int *nbytes, char **buf, int *buf_type);
|
||||||
|
extern int smb2_async_writev(struct cifs_writedata *wdata);
|
||||||
|
extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
|
||||||
|
unsigned int *nbytes, struct kvec *iov, int n_vec);
|
||||||
extern int SMB2_echo(struct TCP_Server_Info *server);
|
extern int SMB2_echo(struct TCP_Server_Info *server);
|
||||||
|
extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
u64 persistent_fid, u64 volatile_fid, int index,
|
||||||
|
struct cifs_search_info *srch_inf);
|
||||||
|
extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
u64 persistent_fid, u64 volatile_fid,
|
||||||
|
__le16 *target_file);
|
||||||
|
extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
u64 persistent_fid, u64 volatile_fid,
|
||||||
|
__le16 *target_file);
|
||||||
|
extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
u64 persistent_fid, u64 volatile_fid, u32 pid,
|
||||||
|
__le64 *eof);
|
||||||
|
extern int SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
u64 persistent_fid, u64 volatile_fid,
|
||||||
|
FILE_BASIC_INFO *buf);
|
||||||
|
extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const u64 persistent_fid, const u64 volatile_fid,
|
||||||
|
const __u8 oplock_level);
|
||||||
|
extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
u64 persistent_file_id, u64 volatile_file_id,
|
||||||
|
struct kstatfs *FSData);
|
||||||
|
extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const __u64 persist_fid, const __u64 volatile_fid,
|
||||||
|
const __u32 pid, const __u64 length, const __u64 offset,
|
||||||
|
const __u32 lockFlags, const bool wait);
|
||||||
|
extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const __u64 persist_fid, const __u64 volatile_fid,
|
||||||
|
const __u32 pid, const __u32 num_lock,
|
||||||
|
struct smb2_lock_element *buf);
|
||||||
|
extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
__u8 *lease_key, const __le32 lease_state);
|
||||||
|
|
||||||
#endif /* _SMB2PROTO_H */
|
#endif /* _SMB2PROTO_H */
|
||||||
|
|
|
@ -30,12 +30,156 @@
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include <linux/mempool.h>
|
#include <linux/mempool.h>
|
||||||
|
#include <linux/highmem.h>
|
||||||
#include "smb2pdu.h"
|
#include "smb2pdu.h"
|
||||||
#include "cifsglob.h"
|
#include "cifsglob.h"
|
||||||
#include "cifsproto.h"
|
#include "cifsproto.h"
|
||||||
#include "smb2proto.h"
|
#include "smb2proto.h"
|
||||||
#include "cifs_debug.h"
|
#include "cifs_debug.h"
|
||||||
#include "smb2status.h"
|
#include "smb2status.h"
|
||||||
|
#include "smb2glob.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||||
|
{
|
||||||
|
int i, rc;
|
||||||
|
unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
|
||||||
|
unsigned char *sigptr = smb2_signature;
|
||||||
|
struct kvec *iov = rqst->rq_iov;
|
||||||
|
int n_vec = rqst->rq_nvec;
|
||||||
|
struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
|
||||||
|
|
||||||
|
memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
|
||||||
|
memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
|
||||||
|
|
||||||
|
rc = crypto_shash_setkey(server->secmech.hmacsha256,
|
||||||
|
server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
|
||||||
|
if (rc) {
|
||||||
|
cERROR(1, "%s: Could not update with response\n", __func__);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
|
||||||
|
if (rc) {
|
||||||
|
cERROR(1, "%s: Could not init md5\n", __func__);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < n_vec; i++) {
|
||||||
|
if (iov[i].iov_len == 0)
|
||||||
|
continue;
|
||||||
|
if (iov[i].iov_base == NULL) {
|
||||||
|
cERROR(1, "null iovec entry");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* The first entry includes a length field (which does not get
|
||||||
|
* signed that occupies the first 4 bytes before the header).
|
||||||
|
*/
|
||||||
|
if (i == 0) {
|
||||||
|
if (iov[0].iov_len <= 8) /* cmd field at offset 9 */
|
||||||
|
break; /* nothing to sign or corrupt header */
|
||||||
|
rc =
|
||||||
|
crypto_shash_update(
|
||||||
|
&server->secmech.sdeschmacsha256->shash,
|
||||||
|
iov[i].iov_base + 4, iov[i].iov_len - 4);
|
||||||
|
} else {
|
||||||
|
rc =
|
||||||
|
crypto_shash_update(
|
||||||
|
&server->secmech.sdeschmacsha256->shash,
|
||||||
|
iov[i].iov_base, iov[i].iov_len);
|
||||||
|
}
|
||||||
|
if (rc) {
|
||||||
|
cERROR(1, "%s: Could not update with payload\n",
|
||||||
|
__func__);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now hash over the rq_pages array */
|
||||||
|
for (i = 0; i < rqst->rq_npages; i++) {
|
||||||
|
struct kvec p_iov;
|
||||||
|
|
||||||
|
cifs_rqst_page_to_kvec(rqst, i, &p_iov);
|
||||||
|
crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
|
||||||
|
p_iov.iov_base, p_iov.iov_len);
|
||||||
|
kunmap(rqst->rq_pages[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
|
||||||
|
sigptr);
|
||||||
|
if (rc)
|
||||||
|
cERROR(1, "%s: Could not generate sha256 hash\n", __func__);
|
||||||
|
|
||||||
|
memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* must be called with server->srv_mutex held */
|
||||||
|
static int
|
||||||
|
smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
struct smb2_hdr *smb2_pdu = rqst->rq_iov[0].iov_base;
|
||||||
|
|
||||||
|
if (!(smb2_pdu->Flags & SMB2_FLAGS_SIGNED) ||
|
||||||
|
server->tcpStatus == CifsNeedNegotiate)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (!server->session_estab) {
|
||||||
|
strncpy(smb2_pdu->Signature, "BSRSPYL", 8);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = smb2_calc_signature(rqst, server);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||||
|
{
|
||||||
|
unsigned int rc;
|
||||||
|
char server_response_sig[16];
|
||||||
|
struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
|
||||||
|
|
||||||
|
if ((smb2_pdu->Command == SMB2_NEGOTIATE) ||
|
||||||
|
(smb2_pdu->Command == SMB2_OPLOCK_BREAK) ||
|
||||||
|
(!server->session_estab))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BB what if signatures are supposed to be on for session but
|
||||||
|
* server does not send one? BB
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Do not need to verify session setups with signature "BSRSPYL " */
|
||||||
|
if (memcmp(smb2_pdu->Signature, "BSRSPYL ", 8) == 0)
|
||||||
|
cFYI(1, "dummy signature received for smb command 0x%x",
|
||||||
|
smb2_pdu->Command);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save off the origiginal signature so we can modify the smb and check
|
||||||
|
* our calculated signature against what the server sent.
|
||||||
|
*/
|
||||||
|
memcpy(server_response_sig, smb2_pdu->Signature, SMB2_SIGNATURE_SIZE);
|
||||||
|
|
||||||
|
memset(smb2_pdu->Signature, 0, SMB2_SIGNATURE_SIZE);
|
||||||
|
|
||||||
|
mutex_lock(&server->srv_mutex);
|
||||||
|
rc = smb2_calc_signature(rqst, server);
|
||||||
|
mutex_unlock(&server->srv_mutex);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (memcmp(server_response_sig, smb2_pdu->Signature,
|
||||||
|
SMB2_SIGNATURE_SIZE))
|
||||||
|
return -EACCES;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set message id for the request. Should be called after wait_for_free_request
|
* Set message id for the request. Should be called after wait_for_free_request
|
||||||
|
@ -115,58 +259,66 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
|
||||||
bool log_error)
|
bool log_error)
|
||||||
{
|
{
|
||||||
unsigned int len = get_rfc1002_length(mid->resp_buf);
|
unsigned int len = get_rfc1002_length(mid->resp_buf);
|
||||||
|
struct kvec iov;
|
||||||
|
struct smb_rqst rqst = { .rq_iov = &iov,
|
||||||
|
.rq_nvec = 1 };
|
||||||
|
|
||||||
|
iov.iov_base = (char *)mid->resp_buf;
|
||||||
|
iov.iov_len = get_rfc1002_length(mid->resp_buf) + 4;
|
||||||
|
|
||||||
dump_smb(mid->resp_buf, min_t(u32, 80, len));
|
dump_smb(mid->resp_buf, min_t(u32, 80, len));
|
||||||
/* convert the length into a more usable form */
|
/* convert the length into a more usable form */
|
||||||
/* BB - uncomment with SMB2 signing implementation */
|
if ((len > 24) &&
|
||||||
/* if ((len > 24) &&
|
|
||||||
(server->sec_mode & (SECMODE_SIGN_REQUIRED|SECMODE_SIGN_ENABLED))) {
|
(server->sec_mode & (SECMODE_SIGN_REQUIRED|SECMODE_SIGN_ENABLED))) {
|
||||||
if (smb2_verify_signature(mid->resp_buf, server))
|
int rc;
|
||||||
cERROR(1, "Unexpected SMB signature");
|
|
||||||
} */
|
rc = smb2_verify_signature(&rqst, server);
|
||||||
|
if (rc)
|
||||||
|
cERROR(1, "SMB signature verification returned error = "
|
||||||
|
"%d", rc);
|
||||||
|
}
|
||||||
|
|
||||||
return map_smb2_to_linux_error(mid->resp_buf, log_error);
|
return map_smb2_to_linux_error(mid->resp_buf, log_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
struct mid_q_entry *
|
||||||
smb2_setup_request(struct cifs_ses *ses, struct kvec *iov,
|
smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
|
||||||
unsigned int nvec, struct mid_q_entry **ret_mid)
|
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct smb2_hdr *hdr = (struct smb2_hdr *)iov[0].iov_base;
|
struct smb2_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
|
||||||
struct mid_q_entry *mid;
|
struct mid_q_entry *mid;
|
||||||
|
|
||||||
smb2_seq_num_into_buf(ses->server, hdr);
|
smb2_seq_num_into_buf(ses->server, hdr);
|
||||||
|
|
||||||
rc = smb2_get_mid_entry(ses, hdr, &mid);
|
rc = smb2_get_mid_entry(ses, hdr, &mid);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return ERR_PTR(rc);
|
||||||
/* rc = smb2_sign_smb2(iov, nvec, ses->server);
|
rc = smb2_sign_rqst(rqst, ses->server);
|
||||||
if (rc)
|
if (rc) {
|
||||||
delete_mid(mid); */
|
cifs_delete_mid(mid);
|
||||||
*ret_mid = mid;
|
return ERR_PTR(rc);
|
||||||
return rc;
|
}
|
||||||
|
return mid;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
struct mid_q_entry *
|
||||||
smb2_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov,
|
smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
||||||
unsigned int nvec, struct mid_q_entry **ret_mid)
|
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc;
|
||||||
struct smb2_hdr *hdr = (struct smb2_hdr *)iov[0].iov_base;
|
struct smb2_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
|
||||||
struct mid_q_entry *mid;
|
struct mid_q_entry *mid;
|
||||||
|
|
||||||
smb2_seq_num_into_buf(server, hdr);
|
smb2_seq_num_into_buf(server, hdr);
|
||||||
|
|
||||||
mid = smb2_mid_entry_alloc(hdr, server);
|
mid = smb2_mid_entry_alloc(hdr, server);
|
||||||
if (mid == NULL)
|
if (mid == NULL)
|
||||||
return -ENOMEM;
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
/* rc = smb2_sign_smb2(iov, nvec, server);
|
rc = smb2_sign_rqst(rqst, server);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
DeleteMidQEntry(mid);
|
DeleteMidQEntry(mid);
|
||||||
return rc;
|
return ERR_PTR(rc);
|
||||||
}*/
|
}
|
||||||
*ret_mid = mid;
|
|
||||||
return rc;
|
return mid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
#include <linux/net.h>
|
#include <linux/net.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/freezer.h>
|
#include <linux/freezer.h>
|
||||||
|
#include <linux/tcp.h>
|
||||||
|
#include <linux/highmem.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include <linux/mempool.h>
|
#include <linux/mempool.h>
|
||||||
|
@ -109,8 +111,8 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
|
||||||
mempool_free(midEntry, cifs_mid_poolp);
|
mempool_free(midEntry, cifs_mid_poolp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
delete_mid(struct mid_q_entry *mid)
|
cifs_delete_mid(struct mid_q_entry *mid)
|
||||||
{
|
{
|
||||||
spin_lock(&GlobalMid_Lock);
|
spin_lock(&GlobalMid_Lock);
|
||||||
list_del(&mid->qhead);
|
list_del(&mid->qhead);
|
||||||
|
@ -119,18 +121,29 @@ delete_mid(struct mid_q_entry *mid)
|
||||||
DeleteMidQEntry(mid);
|
DeleteMidQEntry(mid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* smb_send_kvec - send an array of kvecs to the server
|
||||||
|
* @server: Server to send the data to
|
||||||
|
* @iov: Pointer to array of kvecs
|
||||||
|
* @n_vec: length of kvec array
|
||||||
|
* @sent: amount of data sent on socket is stored here
|
||||||
|
*
|
||||||
|
* Our basic "send data to server" function. Should be called with srv_mutex
|
||||||
|
* held. The caller is responsible for handling the results.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
|
smb_send_kvec(struct TCP_Server_Info *server, struct kvec *iov, size_t n_vec,
|
||||||
|
size_t *sent)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
struct msghdr smb_msg;
|
struct msghdr smb_msg;
|
||||||
unsigned int len = iov[0].iov_len;
|
unsigned int remaining;
|
||||||
unsigned int total_len;
|
size_t first_vec = 0;
|
||||||
int first_vec = 0;
|
|
||||||
unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base);
|
|
||||||
struct socket *ssocket = server->ssocket;
|
struct socket *ssocket = server->ssocket;
|
||||||
|
|
||||||
|
*sent = 0;
|
||||||
|
|
||||||
if (ssocket == NULL)
|
if (ssocket == NULL)
|
||||||
return -ENOTSOCK; /* BB eventually add reconnect code here */
|
return -ENOTSOCK; /* BB eventually add reconnect code here */
|
||||||
|
|
||||||
|
@ -143,56 +156,60 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
|
||||||
else
|
else
|
||||||
smb_msg.msg_flags = MSG_NOSIGNAL;
|
smb_msg.msg_flags = MSG_NOSIGNAL;
|
||||||
|
|
||||||
total_len = 0;
|
remaining = 0;
|
||||||
for (i = 0; i < n_vec; i++)
|
for (i = 0; i < n_vec; i++)
|
||||||
total_len += iov[i].iov_len;
|
remaining += iov[i].iov_len;
|
||||||
|
|
||||||
cFYI(1, "Sending smb: total_len %d", total_len);
|
|
||||||
dump_smb(iov[0].iov_base, len);
|
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
while (total_len) {
|
while (remaining) {
|
||||||
|
/*
|
||||||
|
* If blocking send, we try 3 times, since each can block
|
||||||
|
* for 5 seconds. For nonblocking we have to try more
|
||||||
|
* but wait increasing amounts of time allowing time for
|
||||||
|
* socket to clear. The overall time we wait in either
|
||||||
|
* case to send on the socket is about 15 seconds.
|
||||||
|
* Similarly we wait for 15 seconds for a response from
|
||||||
|
* the server in SendReceive[2] for the server to send
|
||||||
|
* a response back for most types of requests (except
|
||||||
|
* SMB Write past end of file which can be slow, and
|
||||||
|
* blocking lock operations). NFS waits slightly longer
|
||||||
|
* than CIFS, but this can make it take longer for
|
||||||
|
* nonresponsive servers to be detected and 15 seconds
|
||||||
|
* is more than enough time for modern networks to
|
||||||
|
* send a packet. In most cases if we fail to send
|
||||||
|
* after the retries we will kill the socket and
|
||||||
|
* reconnect which may clear the network problem.
|
||||||
|
*/
|
||||||
rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
|
rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
|
||||||
n_vec - first_vec, total_len);
|
n_vec - first_vec, remaining);
|
||||||
if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
|
if (rc == -ENOSPC || rc == -EAGAIN) {
|
||||||
i++;
|
i++;
|
||||||
/*
|
if (i >= 14 || (!server->noblocksnd && (i > 2))) {
|
||||||
* If blocking send we try 3 times, since each can block
|
cERROR(1, "sends on sock %p stuck for 15 "
|
||||||
* for 5 seconds. For nonblocking we have to try more
|
"seconds", ssocket);
|
||||||
* but wait increasing amounts of time allowing time for
|
|
||||||
* socket to clear. The overall time we wait in either
|
|
||||||
* case to send on the socket is about 15 seconds.
|
|
||||||
* Similarly we wait for 15 seconds for a response from
|
|
||||||
* the server in SendReceive[2] for the server to send
|
|
||||||
* a response back for most types of requests (except
|
|
||||||
* SMB Write past end of file which can be slow, and
|
|
||||||
* blocking lock operations). NFS waits slightly longer
|
|
||||||
* than CIFS, but this can make it take longer for
|
|
||||||
* nonresponsive servers to be detected and 15 seconds
|
|
||||||
* is more than enough time for modern networks to
|
|
||||||
* send a packet. In most cases if we fail to send
|
|
||||||
* after the retries we will kill the socket and
|
|
||||||
* reconnect which may clear the network problem.
|
|
||||||
*/
|
|
||||||
if ((i >= 14) || (!server->noblocksnd && (i > 2))) {
|
|
||||||
cERROR(1, "sends on sock %p stuck for 15 seconds",
|
|
||||||
ssocket);
|
|
||||||
rc = -EAGAIN;
|
rc = -EAGAIN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
msleep(1 << i);
|
msleep(1 << i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (rc == total_len) {
|
/* send was at least partially successful */
|
||||||
total_len = 0;
|
*sent += rc;
|
||||||
break;
|
|
||||||
} else if (rc > total_len) {
|
if (rc == remaining) {
|
||||||
cERROR(1, "sent %d requested %d", rc, total_len);
|
remaining = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rc > remaining) {
|
||||||
|
cERROR(1, "sent %d requested %d", rc, remaining);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
/* should never happen, letting socket clear before
|
/* should never happen, letting socket clear before
|
||||||
retrying is our only obvious option here */
|
retrying is our only obvious option here */
|
||||||
|
@ -200,7 +217,9 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
|
||||||
msleep(500);
|
msleep(500);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
total_len -= rc;
|
|
||||||
|
remaining -= rc;
|
||||||
|
|
||||||
/* the line below resets i */
|
/* the line below resets i */
|
||||||
for (i = first_vec; i < n_vec; i++) {
|
for (i = first_vec; i < n_vec; i++) {
|
||||||
if (iov[i].iov_len) {
|
if (iov[i].iov_len) {
|
||||||
|
@ -215,16 +234,97 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 0; /* in case we get ENOSPC on the next send */
|
i = 0; /* in case we get ENOSPC on the next send */
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rqst_page_to_kvec - Turn a slot in the smb_rqst page array into a kvec
|
||||||
|
* @rqst: pointer to smb_rqst
|
||||||
|
* @idx: index into the array of the page
|
||||||
|
* @iov: pointer to struct kvec that will hold the result
|
||||||
|
*
|
||||||
|
* Helper function to convert a slot in the rqst->rq_pages array into a kvec.
|
||||||
|
* The page will be kmapped and the address placed into iov_base. The length
|
||||||
|
* will then be adjusted according to the ptailoff.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx,
|
||||||
|
struct kvec *iov)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* FIXME: We could avoid this kmap altogether if we used
|
||||||
|
* kernel_sendpage instead of kernel_sendmsg. That will only
|
||||||
|
* work if signing is disabled though as sendpage inlines the
|
||||||
|
* page directly into the fraglist. If userspace modifies the
|
||||||
|
* page after we calculate the signature, then the server will
|
||||||
|
* reject it and may break the connection. kernel_sendmsg does
|
||||||
|
* an extra copy of the data and avoids that issue.
|
||||||
|
*/
|
||||||
|
iov->iov_base = kmap(rqst->rq_pages[idx]);
|
||||||
|
|
||||||
|
/* if last page, don't send beyond this offset into page */
|
||||||
|
if (idx == (rqst->rq_npages - 1))
|
||||||
|
iov->iov_len = rqst->rq_tailsz;
|
||||||
|
else
|
||||||
|
iov->iov_len = rqst->rq_pagesz;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct kvec *iov = rqst->rq_iov;
|
||||||
|
int n_vec = rqst->rq_nvec;
|
||||||
|
unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base);
|
||||||
|
unsigned int i;
|
||||||
|
size_t total_len = 0, sent;
|
||||||
|
struct socket *ssocket = server->ssocket;
|
||||||
|
int val = 1;
|
||||||
|
|
||||||
|
cFYI(1, "Sending smb: smb_len=%u", smb_buf_length);
|
||||||
|
dump_smb(iov[0].iov_base, iov[0].iov_len);
|
||||||
|
|
||||||
|
/* cork the socket */
|
||||||
|
kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
|
||||||
|
(char *)&val, sizeof(val));
|
||||||
|
|
||||||
|
rc = smb_send_kvec(server, iov, n_vec, &sent);
|
||||||
|
if (rc < 0)
|
||||||
|
goto uncork;
|
||||||
|
|
||||||
|
total_len += sent;
|
||||||
|
|
||||||
|
/* now walk the page array and send each page in it */
|
||||||
|
for (i = 0; i < rqst->rq_npages; i++) {
|
||||||
|
struct kvec p_iov;
|
||||||
|
|
||||||
|
cifs_rqst_page_to_kvec(rqst, i, &p_iov);
|
||||||
|
rc = smb_send_kvec(server, &p_iov, 1, &sent);
|
||||||
|
kunmap(rqst->rq_pages[i]);
|
||||||
|
if (rc < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
total_len += sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uncork:
|
||||||
|
/* uncork it */
|
||||||
|
val = 0;
|
||||||
|
kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
|
||||||
|
(char *)&val, sizeof(val));
|
||||||
|
|
||||||
if ((total_len > 0) && (total_len != smb_buf_length + 4)) {
|
if ((total_len > 0) && (total_len != smb_buf_length + 4)) {
|
||||||
cFYI(1, "partial send (%d remaining), terminating session",
|
cFYI(1, "partial send (wanted=%u sent=%zu): terminating "
|
||||||
total_len);
|
"session", smb_buf_length + 4, total_len);
|
||||||
/* If we have only sent part of an SMB then the next SMB
|
/*
|
||||||
could be taken as the remainder of this one. We need
|
* If we have only sent part of an SMB then the next SMB could
|
||||||
to kill the socket so the server throws away the partial
|
* be taken as the remainder of this one. We need to kill the
|
||||||
SMB */
|
* socket so the server throws away the partial SMB
|
||||||
|
*/
|
||||||
server->tcpStatus = CifsNeedReconnect;
|
server->tcpStatus = CifsNeedReconnect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +336,15 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
|
||||||
|
{
|
||||||
|
struct smb_rqst rqst = { .rq_iov = iov,
|
||||||
|
.rq_nvec = n_vec };
|
||||||
|
|
||||||
|
return smb_send_rqst(server, &rqst);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
|
smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
|
||||||
unsigned int smb_buf_length)
|
unsigned int smb_buf_length)
|
||||||
|
@ -345,12 +454,11 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
struct mid_q_entry *
|
||||||
cifs_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov,
|
cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
||||||
unsigned int nvec, struct mid_q_entry **ret_mid)
|
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base;
|
struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
|
||||||
struct mid_q_entry *mid;
|
struct mid_q_entry *mid;
|
||||||
|
|
||||||
/* enable signing if server requires it */
|
/* enable signing if server requires it */
|
||||||
|
@ -359,16 +467,15 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov,
|
||||||
|
|
||||||
mid = AllocMidQEntry(hdr, server);
|
mid = AllocMidQEntry(hdr, server);
|
||||||
if (mid == NULL)
|
if (mid == NULL)
|
||||||
return -ENOMEM;
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
rc = cifs_sign_smbv(iov, nvec, server, &mid->sequence_number);
|
rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
DeleteMidQEntry(mid);
|
DeleteMidQEntry(mid);
|
||||||
return rc;
|
return ERR_PTR(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
*ret_mid = mid;
|
return mid;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -376,9 +483,9 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov,
|
||||||
* the result. Caller is responsible for dealing with timeouts.
|
* the result. Caller is responsible for dealing with timeouts.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
|
cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
|
||||||
unsigned int nvec, mid_receive_t *receive,
|
mid_receive_t *receive, mid_callback_t *callback,
|
||||||
mid_callback_t *callback, void *cbdata, const int flags)
|
void *cbdata, const int flags)
|
||||||
{
|
{
|
||||||
int rc, timeout, optype;
|
int rc, timeout, optype;
|
||||||
struct mid_q_entry *mid;
|
struct mid_q_entry *mid;
|
||||||
|
@ -391,12 +498,12 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
mutex_lock(&server->srv_mutex);
|
mutex_lock(&server->srv_mutex);
|
||||||
rc = server->ops->setup_async_request(server, iov, nvec, &mid);
|
mid = server->ops->setup_async_request(server, rqst);
|
||||||
if (rc) {
|
if (IS_ERR(mid)) {
|
||||||
mutex_unlock(&server->srv_mutex);
|
mutex_unlock(&server->srv_mutex);
|
||||||
add_credits(server, 1, optype);
|
add_credits(server, 1, optype);
|
||||||
wake_up(&server->request_q);
|
wake_up(&server->request_q);
|
||||||
return rc;
|
return PTR_ERR(mid);
|
||||||
}
|
}
|
||||||
|
|
||||||
mid->receive = receive;
|
mid->receive = receive;
|
||||||
|
@ -411,7 +518,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
|
||||||
|
|
||||||
|
|
||||||
cifs_in_send_inc(server);
|
cifs_in_send_inc(server);
|
||||||
rc = smb_sendv(server, iov, nvec);
|
rc = smb_send_rqst(server, rqst);
|
||||||
cifs_in_send_dec(server);
|
cifs_in_send_dec(server);
|
||||||
cifs_save_when_sent(mid);
|
cifs_save_when_sent(mid);
|
||||||
mutex_unlock(&server->srv_mutex);
|
mutex_unlock(&server->srv_mutex);
|
||||||
|
@ -419,7 +526,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
delete_mid(mid);
|
cifs_delete_mid(mid);
|
||||||
add_credits(server, 1, optype);
|
add_credits(server, 1, optype);
|
||||||
wake_up(&server->request_q);
|
wake_up(&server->request_q);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -504,11 +611,13 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
|
||||||
if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
|
if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
|
||||||
struct kvec iov;
|
struct kvec iov;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
struct smb_rqst rqst = { .rq_iov = &iov,
|
||||||
|
.rq_nvec = 1 };
|
||||||
|
|
||||||
iov.iov_base = mid->resp_buf;
|
iov.iov_base = mid->resp_buf;
|
||||||
iov.iov_len = len;
|
iov.iov_len = len;
|
||||||
/* FIXME: add code to kill session */
|
/* FIXME: add code to kill session */
|
||||||
rc = cifs_verify_signature(&iov, 1, server,
|
rc = cifs_verify_signature(&rqst, server,
|
||||||
mid->sequence_number + 1);
|
mid->sequence_number + 1);
|
||||||
if (rc)
|
if (rc)
|
||||||
cERROR(1, "SMB signature verification returned error = "
|
cERROR(1, "SMB signature verification returned error = "
|
||||||
|
@ -519,22 +628,22 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
|
||||||
return map_smb_to_linux_error(mid->resp_buf, log_error);
|
return map_smb_to_linux_error(mid->resp_buf, log_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
struct mid_q_entry *
|
||||||
cifs_setup_request(struct cifs_ses *ses, struct kvec *iov,
|
cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
|
||||||
unsigned int nvec, struct mid_q_entry **ret_mid)
|
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base;
|
struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
|
||||||
struct mid_q_entry *mid;
|
struct mid_q_entry *mid;
|
||||||
|
|
||||||
rc = allocate_mid(ses, hdr, &mid);
|
rc = allocate_mid(ses, hdr, &mid);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return ERR_PTR(rc);
|
||||||
rc = cifs_sign_smbv(iov, nvec, ses->server, &mid->sequence_number);
|
rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number);
|
||||||
if (rc)
|
if (rc) {
|
||||||
delete_mid(mid);
|
cifs_delete_mid(mid);
|
||||||
*ret_mid = mid;
|
return ERR_PTR(rc);
|
||||||
return rc;
|
}
|
||||||
|
return mid;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -547,6 +656,8 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
|
||||||
struct mid_q_entry *midQ;
|
struct mid_q_entry *midQ;
|
||||||
char *buf = iov[0].iov_base;
|
char *buf = iov[0].iov_base;
|
||||||
unsigned int credits = 1;
|
unsigned int credits = 1;
|
||||||
|
struct smb_rqst rqst = { .rq_iov = iov,
|
||||||
|
.rq_nvec = n_vec };
|
||||||
|
|
||||||
timeout = flags & CIFS_TIMEOUT_MASK;
|
timeout = flags & CIFS_TIMEOUT_MASK;
|
||||||
optype = flags & CIFS_OP_MASK;
|
optype = flags & CIFS_OP_MASK;
|
||||||
|
@ -584,13 +695,13 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
|
||||||
|
|
||||||
mutex_lock(&ses->server->srv_mutex);
|
mutex_lock(&ses->server->srv_mutex);
|
||||||
|
|
||||||
rc = ses->server->ops->setup_request(ses, iov, n_vec, &midQ);
|
midQ = ses->server->ops->setup_request(ses, &rqst);
|
||||||
if (rc) {
|
if (IS_ERR(midQ)) {
|
||||||
mutex_unlock(&ses->server->srv_mutex);
|
mutex_unlock(&ses->server->srv_mutex);
|
||||||
cifs_small_buf_release(buf);
|
cifs_small_buf_release(buf);
|
||||||
/* Update # of requests on wire to server */
|
/* Update # of requests on wire to server */
|
||||||
add_credits(ses->server, 1, optype);
|
add_credits(ses->server, 1, optype);
|
||||||
return rc;
|
return PTR_ERR(midQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
midQ->mid_state = MID_REQUEST_SUBMITTED;
|
midQ->mid_state = MID_REQUEST_SUBMITTED;
|
||||||
|
@ -652,11 +763,11 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
|
||||||
rc = ses->server->ops->check_receive(midQ, ses->server,
|
rc = ses->server->ops->check_receive(midQ, ses->server,
|
||||||
flags & CIFS_LOG_ERROR);
|
flags & CIFS_LOG_ERROR);
|
||||||
|
|
||||||
/* mark it so buf will not be freed by delete_mid */
|
/* mark it so buf will not be freed by cifs_delete_mid */
|
||||||
if ((flags & CIFS_NO_RESP) == 0)
|
if ((flags & CIFS_NO_RESP) == 0)
|
||||||
midQ->resp_buf = NULL;
|
midQ->resp_buf = NULL;
|
||||||
out:
|
out:
|
||||||
delete_mid(midQ);
|
cifs_delete_mid(midQ);
|
||||||
add_credits(ses->server, credits, optype);
|
add_credits(ses->server, credits, optype);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -762,7 +873,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
||||||
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
|
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
|
||||||
rc = cifs_check_receive(midQ, ses->server, 0);
|
rc = cifs_check_receive(midQ, ses->server, 0);
|
||||||
out:
|
out:
|
||||||
delete_mid(midQ);
|
cifs_delete_mid(midQ);
|
||||||
add_credits(ses->server, 1, 0);
|
add_credits(ses->server, 1, 0);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -846,7 +957,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
|
||||||
rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
|
rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
delete_mid(midQ);
|
cifs_delete_mid(midQ);
|
||||||
mutex_unlock(&ses->server->srv_mutex);
|
mutex_unlock(&ses->server->srv_mutex);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -859,7 +970,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
mutex_unlock(&ses->server->srv_mutex);
|
mutex_unlock(&ses->server->srv_mutex);
|
||||||
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
delete_mid(midQ);
|
cifs_delete_mid(midQ);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -880,7 +991,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
blocking lock to return. */
|
blocking lock to return. */
|
||||||
rc = send_cancel(ses->server, in_buf, midQ);
|
rc = send_cancel(ses->server, in_buf, midQ);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
delete_mid(midQ);
|
cifs_delete_mid(midQ);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -892,7 +1003,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
/* If we get -ENOLCK back the lock may have
|
/* If we get -ENOLCK back the lock may have
|
||||||
already been removed. Don't exit in this case. */
|
already been removed. Don't exit in this case. */
|
||||||
if (rc && rc != -ENOLCK) {
|
if (rc && rc != -ENOLCK) {
|
||||||
delete_mid(midQ);
|
cifs_delete_mid(midQ);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -929,7 +1040,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
|
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
|
||||||
rc = cifs_check_receive(midQ, ses->server, 0);
|
rc = cifs_check_receive(midQ, ses->server, 0);
|
||||||
out:
|
out:
|
||||||
delete_mid(midQ);
|
cifs_delete_mid(midQ);
|
||||||
if (rstart && rc == -EACCES)
|
if (rstart && rc == -EACCES)
|
||||||
return -ERESTARTSYS;
|
return -ERESTARTSYS;
|
||||||
return rc;
|
return rc;
|
||||||
|
|
Loading…
Reference in New Issue