Commit 7b7a2f0a authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6

Pull CIFS fixes from Steve French:
 "CIFS update including case insensitive file name matching improvements
  for UTF-8 to Unicode, various small cifs fixes, SMB2/SMB3 leasing
  improvements, support for following SMB2 symlinks, SMB3 packet signing
  improvements"

* 'for-next' of git://git.samba.org/sfrench/cifs-2.6: (25 commits)
  CIFS: Respect epoch value from create lease context v2
  CIFS: Add create lease v2 context for SMB3
  CIFS: Move parsing lease buffer to ops struct
  CIFS: Move creating lease buffer to ops struct
  CIFS: Store lease state itself rather than a mapped oplock value
  CIFS: Replace clientCanCache* bools with an integer
  [CIFS] quiet sparse compile warning
  cifs: Start using per session key for smb2/3 for signature generation
  cifs: Add a variable specific to NTLMSSP for key exchange.
  cifs: Process post session setup code in respective dialect functions.
  CIFS: convert to use le32_add_cpu()
  CIFS: Fix missing lease break
  CIFS: Fix a memory leak when a lease break comes
  cifs: add winucase_convert.pl to Documentation/ directory
  cifs: convert case-insensitive dentry ops to use new case conversion routines
  cifs: add new case-insensitive conversion routines that are based on wchar_t's
  [CIFS] Add Scott to list of cifs contributors
  cifs: Move and expand MAX_SERVER_SIZE definition
  cifs: Expand max share name length to 256
  cifs: Move string length definitions to uapi
  ...
parents c3567f8a 42873b0a
...@@ -39,6 +39,7 @@ Shaggy (Dave Kleikamp) for innumerable small fs suggestions and some good cleanu ...@@ -39,6 +39,7 @@ Shaggy (Dave Kleikamp) for innumerable small fs suggestions and some good cleanu
Gunter Kukkukk (testing and suggestions for support of old servers) Gunter Kukkukk (testing and suggestions for support of old servers)
Igor Mammedov (DFS support) Igor Mammedov (DFS support)
Jeff Layton (many, many fixes, as well as great work on the cifs Kerberos code) Jeff Layton (many, many fixes, as well as great work on the cifs Kerberos code)
Scott Lovenberg
Test case and Bug Report contributors Test case and Bug Report contributors
------------------------------------- -------------------------------------
......
#!/usr/bin/perl -w
#
# winucase_convert.pl -- convert "Windows 8 Upper Case Mapping Table.txt" to
# a two-level set of C arrays.
#
# Copyright 2013: Jeff Layton <jlayton@redhat.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
while(<>) {
next if (!/^0x(..)(..)\t0x(....)\t/);
$firstchar = hex($1);
$secondchar = hex($2);
$uppercase = hex($3);
$top[$firstchar][$secondchar] = $uppercase;
}
for ($i = 0; $i < 256; $i++) {
next if (!$top[$i]);
printf("static const wchar_t t2_%2.2x[256] = {", $i);
for ($j = 0; $j < 256; $j++) {
if (($j % 8) == 0) {
print "\n\t";
} else {
print " ";
}
printf("0x%4.4x,", $top[$i][$j] ? $top[$i][$j] : 0);
}
print "\n};\n\n";
}
printf("static const wchar_t *const toplevel[256] = {", $i);
for ($i = 0; $i < 256; $i++) {
if (($i % 8) == 0) {
print "\n\t";
} elsif ($top[$i]) {
print " ";
} else {
print " ";
}
if ($top[$i]) {
printf("t2_%2.2x,", $i);
} else {
print "NULL,";
}
}
print "\n};\n\n";
...@@ -6,7 +6,7 @@ obj-$(CONFIG_CIFS) += cifs.o ...@@ -6,7 +6,7 @@ obj-$(CONFIG_CIFS) += cifs.o
cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \ cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \
link.o misc.o netmisc.o smbencrypt.o transport.o asn1.o \ link.o misc.o netmisc.o smbencrypt.o transport.o asn1.o \
cifs_unicode.o nterr.o xattr.o cifsencrypt.o \ cifs_unicode.o nterr.o xattr.o cifsencrypt.o \
readdir.o ioctl.o sess.o export.o smb1ops.o readdir.o ioctl.o sess.o export.o smb1ops.o winucase.o
cifs-$(CONFIG_CIFS_ACL) += cifsacl.o cifs-$(CONFIG_CIFS_ACL) += cifsacl.o
......
...@@ -91,6 +91,8 @@ extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen, ...@@ -91,6 +91,8 @@ extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen,
#endif /* CONFIG_CIFS_SMB2 */ #endif /* CONFIG_CIFS_SMB2 */
#endif #endif
wchar_t cifs_toupper(wchar_t in);
/* /*
* UniStrcat: Concatenate the second string to the first * UniStrcat: Concatenate the second string to the first
* *
......
...@@ -255,6 +255,7 @@ cifs_alloc_inode(struct super_block *sb) ...@@ -255,6 +255,7 @@ 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;
cifs_inode->epoch = 0;
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE); get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
#endif #endif
...@@ -357,6 +358,18 @@ cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb) ...@@ -357,6 +358,18 @@ cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb)
seq_printf(s, "loose"); seq_printf(s, "loose");
} }
static void
cifs_show_nls(struct seq_file *s, struct nls_table *cur)
{
struct nls_table *def;
/* Display iocharset= option if it's not default charset */
def = load_nls_default();
if (def != cur)
seq_printf(s, ",iocharset=%s", cur->charset);
unload_nls(def);
}
/* /*
* cifs_show_options() is for displaying mount options in /proc/mounts. * cifs_show_options() is for displaying mount options in /proc/mounts.
* Not all settable options are displayed but most of the important * Not all settable options are displayed but most of the important
...@@ -418,6 +431,9 @@ cifs_show_options(struct seq_file *s, struct dentry *root) ...@@ -418,6 +431,9 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
seq_printf(s, ",file_mode=0%ho,dir_mode=0%ho", seq_printf(s, ",file_mode=0%ho,dir_mode=0%ho",
cifs_sb->mnt_file_mode, cifs_sb->mnt_file_mode,
cifs_sb->mnt_dir_mode); cifs_sb->mnt_dir_mode);
cifs_show_nls(s, cifs_sb->local_nls);
if (tcon->seal) if (tcon->seal)
seq_printf(s, ",seal"); seq_printf(s, ",seal");
if (tcon->nocase) if (tcon->nocase)
...@@ -718,7 +734,7 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -718,7 +734,7 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
written = generic_file_aio_write(iocb, iov, nr_segs, pos); written = generic_file_aio_write(iocb, iov, nr_segs, pos);
if (CIFS_I(inode)->clientCanCacheAll) if (CIFS_CACHE_WRITE(CIFS_I(inode)))
return written; return written;
rc = filemap_fdatawrite(inode->i_mapping); rc = filemap_fdatawrite(inode->i_mapping);
...@@ -743,7 +759,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int whence) ...@@ -743,7 +759,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int whence)
* We need to be sure that all dirty pages are written and the * We need to be sure that all dirty pages are written and the
* server has the newest file length. * server has the newest file length.
*/ */
if (!CIFS_I(inode)->clientCanCacheRead && inode->i_mapping && if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping &&
inode->i_mapping->nrpages != 0) { inode->i_mapping->nrpages != 0) {
rc = filemap_fdatawait(inode->i_mapping); rc = filemap_fdatawait(inode->i_mapping);
if (rc) { if (rc) {
...@@ -767,8 +783,10 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int whence) ...@@ -767,8 +783,10 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int whence)
static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
{ {
/* note that this is called by vfs setlease with i_lock held /*
to protect *lease from going away */ * Note that this is called by vfs setlease with i_lock held to
* protect *lease from going away.
*/
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct cifsFileInfo *cfile = file->private_data; struct cifsFileInfo *cfile = file->private_data;
...@@ -776,20 +794,19 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) ...@@ -776,20 +794,19 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
return -EINVAL; return -EINVAL;
/* check if file is oplocked */ /* check if file is oplocked */
if (((arg == F_RDLCK) && if (((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) ||
(CIFS_I(inode)->clientCanCacheRead)) || ((arg == F_WRLCK) && CIFS_CACHE_WRITE(CIFS_I(inode))))
((arg == F_WRLCK) &&
(CIFS_I(inode)->clientCanCacheAll)))
return generic_setlease(file, arg, lease); return generic_setlease(file, arg, lease);
else if (tlink_tcon(cfile->tlink)->local_lease && else if (tlink_tcon(cfile->tlink)->local_lease &&
!CIFS_I(inode)->clientCanCacheRead) !CIFS_CACHE_READ(CIFS_I(inode)))
/* If the server claims to support oplock on this /*
file, then we still need to check oplock even * If the server claims to support oplock on this file, then we
if the local_lease mount option is set, but there * still need to check oplock even if the local_lease mount
are servers which do not support oplock for which * option is set, but there are servers which do not support
this mount option may be useful if the user * oplock for which this mount option may be useful if the user
knows that the file won't be changed on the server * knows that the file won't be changed on the server by anyone
by anyone else */ * else.
*/
return generic_setlease(file, arg, lease); return generic_setlease(file, arg, lease);
else else
return -EAGAIN; return -EAGAIN;
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "cifsacl.h" #include "cifsacl.h"
#include <crypto/internal/hash.h> #include <crypto/internal/hash.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <uapi/linux/cifs/cifs_mount.h>
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
#include "smb2pdu.h" #include "smb2pdu.h"
#endif #endif
...@@ -41,12 +42,7 @@ ...@@ -41,12 +42,7 @@
#define MAX_SES_INFO 2 #define MAX_SES_INFO 2
#define MAX_TCON_INFO 4 #define MAX_TCON_INFO 4
#define MAX_TREE_SIZE (2 + MAX_SERVER_SIZE + 1 + MAX_SHARE_SIZE + 1) #define MAX_TREE_SIZE (2 + CIFS_NI_MAXHOST + 1 + CIFS_MAX_SHARE_LEN + 1)
#define MAX_SERVER_SIZE 15
#define MAX_SHARE_SIZE 80
#define CIFS_MAX_DOMAINNAME_LEN 256 /* max domain name length */
#define MAX_USERNAME_SIZE 256 /* reasonable maximum for current servers */
#define MAX_PASSWORD_SIZE 512 /* max for windows seems to be 256 wide chars */
#define CIFS_MIN_RCV_POOL 4 #define CIFS_MIN_RCV_POOL 4
...@@ -135,6 +131,7 @@ struct cifs_secmech { ...@@ -135,6 +131,7 @@ struct cifs_secmech {
/* per smb session structure/fields */ /* per smb session structure/fields */
struct ntlmssp_auth { struct ntlmssp_auth {
bool sesskey_per_smbsess; /* whether session key is per smb session */
__u32 client_flags; /* sent by client in type 1 ntlmsssp exchange */ __u32 client_flags; /* sent by client in type 1 ntlmsssp exchange */
__u32 server_flags; /* sent by server in type 2 ntlmssp exchange */ __u32 server_flags; /* sent by server in type 2 ntlmssp exchange */
unsigned char ciphertext[CIFS_CPHTXT_SIZE]; /* sent to server */ unsigned char ciphertext[CIFS_CPHTXT_SIZE]; /* sent to server */
...@@ -308,6 +305,9 @@ struct smb_version_operations { ...@@ -308,6 +305,9 @@ struct smb_version_operations {
int (*create_hardlink)(const unsigned int, struct cifs_tcon *, int (*create_hardlink)(const unsigned int, struct cifs_tcon *,
const char *, const char *, const char *, const char *,
struct cifs_sb_info *); struct cifs_sb_info *);
/* query symlink target */
int (*query_symlink)(const unsigned int, struct cifs_tcon *,
const char *, char **, struct cifs_sb_info *);
/* open a file for non-posix mounts */ /* open a file for non-posix mounts */
int (*open)(const unsigned int, struct cifs_open_parms *, int (*open)(const unsigned int, struct cifs_open_parms *,
__u32 *, FILE_ALL_INFO *); __u32 *, FILE_ALL_INFO *);
...@@ -361,18 +361,24 @@ struct smb_version_operations { ...@@ -361,18 +361,24 @@ struct smb_version_operations {
/* push brlocks from the cache to the server */ /* push brlocks from the cache to the server */
int (*push_mand_locks)(struct cifsFileInfo *); int (*push_mand_locks)(struct cifsFileInfo *);
/* get lease key of the inode */ /* get lease key of the inode */
void (*get_lease_key)(struct inode *, struct cifs_fid *fid); void (*get_lease_key)(struct inode *, struct cifs_fid *);
/* set lease key of the inode */ /* set lease key of the inode */
void (*set_lease_key)(struct inode *, struct cifs_fid *fid); void (*set_lease_key)(struct inode *, struct cifs_fid *);
/* generate new lease key */ /* generate new lease key */
void (*new_lease_key)(struct cifs_fid *fid); void (*new_lease_key)(struct cifs_fid *);
/* The next two functions will need to be changed to per smb session */ int (*generate_signingkey)(struct cifs_ses *);
void (*generate_signingkey)(struct TCP_Server_Info *server); int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *);
int (*calc_signature)(struct smb_rqst *rqst, int (*query_mf_symlink)(const unsigned char *, char *, unsigned int *,
struct TCP_Server_Info *server); struct cifs_sb_info *, unsigned int);
int (*query_mf_symlink)(const unsigned char *path, char *pbuf, /* if we can do cache read operations */
unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb, bool (*is_read_op)(__u32);
unsigned int xid); /* set oplock level for the inode */
void (*set_oplock_level)(struct cifsInodeInfo *, __u32, unsigned int,
bool *);
/* create lease context buffer for CREATE request */
char * (*create_lease_buf)(u8 *, u8);
/* parse lease context buffer and return oplock/epoch info */
__u8 (*parse_lease_buf)(void *, unsigned int *);
}; };
struct smb_version_values { struct smb_version_values {
...@@ -390,9 +396,9 @@ struct smb_version_values { ...@@ -390,9 +396,9 @@ struct smb_version_values {
unsigned int cap_unix; unsigned int cap_unix;
unsigned int cap_nt_find; unsigned int cap_nt_find;
unsigned int cap_large_files; unsigned int cap_large_files;
unsigned int oplock_read;
__u16 signing_enabled; __u16 signing_enabled;
__u16 signing_required; __u16 signing_required;
size_t create_lease_size;
}; };
#define HEADER_SIZE(server) (server->vals->header_size) #define HEADER_SIZE(server) (server->vals->header_size)
...@@ -548,7 +554,6 @@ struct TCP_Server_Info { ...@@ -548,7 +554,6 @@ struct TCP_Server_Info {
int timeAdj; /* Adjust for difference in server time zone in sec */ int timeAdj; /* Adjust for difference in server time zone in sec */
__u64 CurrentMid; /* multiplex id - rotating counter */ __u64 CurrentMid; /* multiplex id - rotating counter */
char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */ char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */
char smb3signingkey[SMB3_SIGN_KEY_SIZE]; /* for signing smb3 packets */
/* 16th byte of RFC1001 workstation name is always null */ /* 16th byte of RFC1001 workstation name is always null */
char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
__u32 sequence_number; /* for signing, protected by srv_mutex */ __u32 sequence_number; /* for signing, protected by srv_mutex */
...@@ -731,6 +736,7 @@ struct cifs_ses { ...@@ -731,6 +736,7 @@ struct cifs_ses {
bool need_reconnect:1; /* connection reset, uid now invalid */ bool need_reconnect:1; /* connection reset, uid now invalid */
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
__u16 session_flags; __u16 session_flags;
char smb3signingkey[SMB3_SIGN_KEY_SIZE]; /* for signing smb3 packets */
#endif /* CONFIG_CIFS_SMB2 */ #endif /* CONFIG_CIFS_SMB2 */
}; };
...@@ -935,6 +941,8 @@ struct cifs_fid { ...@@ -935,6 +941,8 @@ struct cifs_fid {
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */ __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */
#endif #endif
struct cifs_pending_open *pending_open; struct cifs_pending_open *pending_open;
unsigned int epoch;
bool purge_cache;
}; };
struct cifs_fid_locks { struct cifs_fid_locks {
...@@ -1032,6 +1040,17 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) ...@@ -1032,6 +1040,17 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file); struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file);
void cifsFileInfo_put(struct cifsFileInfo *cifs_file); void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
#define CIFS_CACHE_READ_FLG 1
#define CIFS_CACHE_HANDLE_FLG 2
#define CIFS_CACHE_RH_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_HANDLE_FLG)
#define CIFS_CACHE_WRITE_FLG 4
#define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG)
#define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG)
#define CIFS_CACHE_READ(cinode) (cinode->oplock & CIFS_CACHE_READ_FLG)
#define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG)
#define CIFS_CACHE_WRITE(cinode) (cinode->oplock & CIFS_CACHE_WRITE_FLG)
/* /*
* One of these for each file inode * One of these for each file inode
*/ */
...@@ -1043,8 +1062,8 @@ struct cifsInodeInfo { ...@@ -1043,8 +1062,8 @@ struct cifsInodeInfo {
/* 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 */
bool clientCanCacheRead; /* read oplock */ unsigned int oplock; /* oplock/lease level we have */
bool clientCanCacheAll; /* read and writebehind oplock */ unsigned int epoch; /* used to track lease state changes */
bool delete_pending; /* DELETE_ON_CLOSE is set */ bool delete_pending; /* DELETE_ON_CLOSE is set */
bool invalid_mapping; /* pagecache is invalid */ bool invalid_mapping; /* pagecache is invalid */
unsigned long time; /* jiffies of last update of inode */ unsigned long time; /* jiffies of last update of inode */
...@@ -1502,7 +1521,7 @@ extern mempool_t *cifs_mid_poolp; ...@@ -1502,7 +1521,7 @@ extern mempool_t *cifs_mid_poolp;
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" #define SMB20_VERSION_STRING "2.0"
/*extern struct smb_version_operations smb20_operations; */ /* not needed yet */ extern struct smb_version_operations smb20_operations;
extern struct smb_version_values smb20_values; 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;
......
...@@ -1495,11 +1495,12 @@ struct reparse_data { ...@@ -1495,11 +1495,12 @@ struct reparse_data {
__u32 ReparseTag; __u32 ReparseTag;
__u16 ReparseDataLength; __u16 ReparseDataLength;
__u16 Reserved; __u16 Reserved;
__u16 AltNameOffset; __u16 SubstituteNameOffset;
__u16 AltNameLen; __u16 SubstituteNameLength;
__u16 TargetNameOffset; __u16 PrintNameOffset;
__u16 TargetNameLen; __u16 PrintNameLength;
char LinkNamesBuf[1]; __u32 Flags;
char PathBuffer[0];
} __attribute__((packed)); } __attribute__((packed));
struct cifs_quota_data { struct cifs_quota_data {
......
...@@ -357,13 +357,9 @@ extern int CIFSSMBUnixQuerySymLink(const unsigned int xid, ...@@ -357,13 +357,9 @@ extern int CIFSSMBUnixQuerySymLink(const unsigned int xid,
struct cifs_tcon *tcon, struct cifs_tcon *tcon,
const unsigned char *searchName, char **syminfo, const unsigned char *searchName, char **syminfo,
const struct nls_table *nls_codepage); const struct nls_table *nls_codepage);
#ifdef CONFIG_CIFS_SYMLINK_EXPERIMENTAL extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
extern int CIFSSMBQueryReparseLinkInfo(const unsigned int xid, __u16 fid, char **symlinkinfo,
struct cifs_tcon *tcon, const struct nls_table *nls_codepage);
const unsigned char *searchName,
char *symlinkinfo, const int buflen, __u16 fid,
const struct nls_table *nls_codepage);
#endif /* temporarily unused until cifs_symlink fixed */
extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon, extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const int disposition, const char *fileName, const int disposition,
const int access_flags, const int omode, const int access_flags, const int omode,
...@@ -435,7 +431,7 @@ extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *); ...@@ -435,7 +431,7 @@ extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *);
extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
extern void cifs_crypto_shash_release(struct TCP_Server_Info *); extern void cifs_crypto_shash_release(struct TCP_Server_Info *);
extern int calc_seckey(struct cifs_ses *); extern int calc_seckey(struct cifs_ses *);
extern void generate_smb3signingkey(struct TCP_Server_Info *); extern int generate_smb3signingkey(struct cifs_ses *);
#ifdef CONFIG_CIFS_WEAK_PW_HASH #ifdef CONFIG_CIFS_WEAK_PW_HASH
extern int calc_lanman_hash(const char *password, const char *cryptkey, extern int calc_lanman_hash(const char *password, const char *cryptkey,
......
...@@ -3067,7 +3067,6 @@ CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -3067,7 +3067,6 @@ CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
#ifdef CONFIG_CIFS_SYMLINK_EXPERIMENTAL
/* /*
* Recent Windows versions now create symlinks more frequently * Recent Windows versions now create symlinks more frequently
* and they use the "reparse point" mechanism below. We can of course * and they use the "reparse point" mechanism below. We can of course
...@@ -3079,18 +3078,22 @@ CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -3079,18 +3078,22 @@ CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
* it is not compiled in by default until callers fixed up and more tested. * it is not compiled in by default until callers fixed up and more tested.
*/ */
int int
CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon, CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
const unsigned char *searchName, __u16 fid, char **symlinkinfo,
char *symlinkinfo, const int buflen, __u16 fid, const struct nls_table *nls_codepage)
const struct nls_table *nls_codepage)
{ {
int rc = 0; int rc = 0;
int bytes_returned; int bytes_returned;
struct smb_com_transaction_ioctl_req *pSMB; struct smb_com_transaction_ioctl_req *pSMB;
struct smb_com_transaction_ioctl_rsp *pSMBr; struct smb_com_transaction_ioctl_rsp *pSMBr;
bool is_unicode;
unsigned int sub_len;
char *sub_start;
struct reparse_data *reparse_buf;
__u32 data_offset, data_count;
char *end_of_smb;
cifs_dbg(FYI, "In Windows reparse style QueryLink for path %s\n", cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid);
searchName);
rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
(void **) &pSMBr); (void **) &pSMBr);
if (rc) if (rc)
...@@ -3119,66 +3122,55 @@ CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -3119,66 +3122,55 @@ CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon,
(struct smb_hdr *) pSMBr, &bytes_returned, 0); (struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) { if (rc) {
cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc); cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc);
} else { /* decode response */ goto qreparse_out;
__u32 data_offset = le32_to_cpu(pSMBr->DataOffset); }
__u32 data_count = le32_to_cpu(pSMBr->DataCount);
if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) {
/* BB also check enough total bytes returned */
rc = -EIO; /* bad smb */
goto qreparse_out;
}
if (data_count && (data_count < 2048)) {
char *end_of_smb = 2 /* sizeof byte count */ +
get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount;
struct reparse_data *reparse_buf =
(struct reparse_data *)
((char *)&pSMBr->hdr.Protocol
+ data_offset);
if ((char *)reparse_buf >= end_of_smb) {
rc = -EIO;
goto qreparse_out;
}
if ((reparse_buf->LinkNamesBuf +
reparse_buf->TargetNameOffset +
reparse_buf->TargetNameLen) > end_of_smb) {
cifs_dbg(FYI, "reparse buf beyond SMB\n");
rc = -EIO;
goto qreparse_out;
}
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { data_offset = le32_to_cpu(pSMBr->DataOffset);
cifs_from_ucs2(symlinkinfo, (__le16 *) data_count = le32_to_cpu(pSMBr->DataCount);
(reparse_buf->LinkNamesBuf + if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) {
reparse_buf->TargetNameOffset), /* BB also check enough total bytes returned */
buflen, rc = -EIO; /* bad smb */
reparse_buf->TargetNameLen, goto qreparse_out;
nls_codepage, 0); }
} else { /* ASCII names */ if (!data_count || (data_count > 2048)) {
strncpy(symlinkinfo, rc = -EIO;
reparse_buf->LinkNamesBuf + cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n");
reparse_buf->TargetNameOffset, goto qreparse_out;
min_t(const int, buflen, }
reparse_buf->TargetNameLen)); end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount;
} reparse_buf = (struct reparse_data *)
} else { ((char *)&pSMBr->hdr.Protocol + data_offset);
rc = -EIO; if ((char *)reparse_buf >= end_of_smb) {
cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); rc = -EIO;
} goto qreparse_out;
symlinkinfo[buflen] = 0; /* just in case so the caller
does not go off the end of the buffer */
cifs_dbg(FYI, "readlink result - %s\n", symlinkinfo);
} }
if ((reparse_buf->PathBuffer + reparse_buf->PrintNameOffset +
reparse_buf->PrintNameLength) > end_of_smb) {
cifs_dbg(FYI, "reparse buf beyond SMB\n");
rc = -EIO;
goto qreparse_out;
}
sub_start = reparse_buf->SubstituteNameOffset + reparse_buf->PathBuffer;
sub_len = reparse_buf->SubstituteNameLength;
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
is_unicode = true;
else
is_unicode = false;
/* BB FIXME investigate remapping reserved chars here */
*symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode,
nls_codepage);
if (!*symlinkinfo)
rc = -ENOMEM;
qreparse_out: qreparse_out:
cifs_buf_release(pSMB); cifs_buf_release(pSMB);
/* Note: On -EAGAIN error only caller can retry on handle based calls /*
since file handle passed in no longer valid */ * Note: On -EAGAIN error only caller can retry on handle based calls
* since file handle passed in no longer valid.
*/
return rc; return rc;
} }
#endif /* CIFS_SYMLINK_EXPERIMENTAL */ /* BB temporarily unused */
#ifdef CONFIG_CIFS_POSIX #ifdef CONFIG_CIFS_POSIX
......
...@@ -379,6 +379,7 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -379,6 +379,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
try_to_freeze(); try_to_freeze();
/* we should try only the port we connected to before */ /* we should try only the port we connected to before */
mutex_lock(&server->srv_mutex);
rc = generic_ip_connect(server); rc = generic_ip_connect(server);
if (rc) { if (rc) {
cifs_dbg(FYI, "reconnect error %d\n", rc); cifs_dbg(FYI, "reconnect error %d\n", rc);
...@@ -390,6 +391,7 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -390,6 +391,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
server->tcpStatus = CifsNeedNegotiate; server->tcpStatus = CifsNeedNegotiate;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
} }
mutex_unlock(&server->srv_mutex);
} while (server->tcpStatus == CifsNeedReconnect); } while (server->tcpStatus == CifsNeedReconnect);
return rc; return rc;
...@@ -1114,7 +1116,7 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol) ...@@ -1114,7 +1116,7 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
break; break;
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
case Smb_20: case Smb_20:
vol->ops = &smb21_operations; /* currently identical with 2.1 */ vol->ops = &smb20_operations;
vol->vals = &smb20_values; vol->vals = &smb20_values;
break; break;
case Smb_21: case Smb_21:
...@@ -1575,8 +1577,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -1575,8 +1577,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
if (string == NULL) if (string == NULL)
goto out_nomem; goto out_nomem;
if (strnlen(string, MAX_USERNAME_SIZE) > if (strnlen(string, CIFS_MAX_USERNAME_LEN) >
MAX_USERNAME_SIZE) { CIFS_MAX_USERNAME_LEN) {
printk(KERN_WARNING "CIFS: username too long\n"); printk(KERN_WARNING "CIFS: username too long\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
...@@ -2221,13 +2223,13 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol) ...@@ -2221,13 +2223,13 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol)
/* anything else takes username/password */ /* anything else takes username/password */
if (strncmp(ses->user_name, if (strncmp(ses->user_name,
vol->username ? vol->username : "", vol->username ? vol->username : "",
MAX_USERNAME_SIZE)) CIFS_MAX_USERNAME_LEN))
return 0; return 0;
if (strlen(vol->username) != 0 && if (strlen(vol->username) != 0 &&
ses->password != NULL && ses->password != NULL &&
strncmp(ses->password, strncmp(ses->password,
vol->password ? vol->password : "", vol->password ? vol->password : "",
MAX_PASSWORD_SIZE)) CIFS_MAX_PASSWORD_LEN))
return 0; return 0;
} }
return 1; return 1;
...@@ -2352,7 +2354,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) ...@@ -2352,7 +2354,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses)
} }
len = delim - payload; len = delim - payload;
if (len > MAX_USERNAME_SIZE || len <= 0) { if (len > CIFS_MAX_USERNAME_LEN || len <= 0) {
cifs_dbg(FYI, "Bad value from username search (len=%zd)\n", cifs_dbg(FYI, "Bad value from username search (len=%zd)\n",
len); len);
rc = -EINVAL; rc = -EINVAL;
...@@ -2369,7 +2371,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) ...@@ -2369,7 +2371,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses)
cifs_dbg(FYI, "%s: username=%s\n", __func__, vol->username); cifs_dbg(FYI, "%s: username=%s\n", __func__, vol->username);
len = key->datalen - (len + 1); len = key->datalen - (len + 1);
if (len > MAX_PASSWORD_SIZE || len <= 0) { if (len > CIFS_MAX_PASSWORD_LEN || len <= 0) {
cifs_dbg(FYI, "Bad len for password search (len=%zd)\n", len); cifs_dbg(FYI, "Bad len for password search (len=%zd)\n", len);
rc = -EINVAL; rc = -EINVAL;
kfree(vol->username); kfree(vol->username);
...@@ -3826,33 +3828,8 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, ...@@ -3826,33 +3828,8 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
if (server->ops->sess_setup) if (server->ops->sess_setup)
rc = server->ops->sess_setup(xid, ses, nls_info); rc = server->ops->sess_setup(xid, ses, nls_info);
if (rc) { if (rc)
cifs_dbg(VFS, "Send error in SessSetup = %d\n", rc); cifs_dbg(VFS, "Send error in SessSetup = %d\n", rc);
} else {
mutex_lock(&server->srv_mutex);
if (!server->session_estab) {
server->session_key.response = ses->auth_key.response;
server->session_key.len = ses->auth_key.len;
server->sequence_number = 0x2;
server->session_estab = true;
ses->auth_key.response = NULL;
if (server->ops->generate_signingkey)
server->ops->generate_signingkey(server);
}
mutex_unlock(&server->srv_mutex);
cifs_dbg(FYI, "CIFS Session Established successfully\n");
spin_lock(&GlobalMid_Lock);
ses->status = CifsGood;
ses->need_reconnect = false;
spin_unlock(&GlobalMid_Lock);
}
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
ses->auth_key.len = 0;
kfree(ses->ntlmssp);
ses->ntlmssp = NULL;
return rc; return rc;
} }
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "cifsproto.h" #include "cifsproto.h"
#include "cifs_debug.h" #include "cifs_debug.h"
#include "cifs_fs_sb.h" #include "cifs_fs_sb.h"
#include "cifs_unicode.h"
static void static void
renew_parental_timestamps(struct dentry *direntry) renew_parental_timestamps(struct dentry *direntry)
...@@ -834,12 +835,17 @@ static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q) ...@@ -834,12 +835,17 @@ static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q)
{ {
struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls;
unsigned long hash; unsigned long hash;
int i; wchar_t c;
int i, charlen;
hash = init_name_hash(); hash = init_name_hash();
for (i = 0; i < q->len; i++) for (i = 0; i < q->len; i += charlen) {
hash = partial_name_hash(nls_tolower(codepage, q->name[i]), charlen = codepage->char2uni(&q->name[i], q->len - i, &c);
hash); /* error out if we can't convert the character */
if (unlikely(charlen < 0))
return charlen;
hash = partial_name_hash(cifs_toupper(c), hash);
}
q->hash = end_name_hash(hash); q->hash = end_name_hash(hash);
return 0; return 0;
...@@ -849,11 +855,47 @@ static int cifs_ci_compare(const struct dentry *parent, const struct dentry *den ...@@ -849,11 +855,47 @@ static int cifs_ci_compare(const struct dentry *parent, const struct dentry *den
unsigned int len, const char *str, const struct qstr *name) unsigned int len, const char *str, const struct qstr *name)
{ {
struct nls_table *codepage = CIFS_SB(parent->d_sb)->local_nls; struct nls_table *codepage = CIFS_SB(parent->d_sb)->local_nls;
wchar_t c1, c2;
int i, l1, l2;
if ((name->len == len) && /*
(nls_strnicmp(codepage, name->name, str, len) == 0)) * We make the assumption here that uppercase characters in the local
return 0; * codepage are always the same length as their lowercase counterparts.
return 1; *
* If that's ever not the case, then this will fail to match it.
*/
if (name->len != len)
return 1;
for (i = 0; i < len; i += l1) {
/* Convert characters in both strings to UTF-16. */
l1 = codepage->char2uni(&str[i], len - i, &c1);
l2 = codepage->char2uni(&name->name[i], name->len - i, &c2);
/*
* If we can't convert either character, just declare it to
* be 1 byte long and compare the original byte.
*/
if (unlikely(l1 < 0 && l2 < 0)) {
if (str[i] != name->name[i])
return 1;
l1 = 1;
continue;
}
/*
* Here, we again ass|u|me that upper/lowercase versions of
* a character are the same length in the local NLS.
*/
if (l1 != l2)
return 1;
/* Now compare uppercase versions of these characters */
if (cifs_toupper(c1) != cifs_toupper(c2))
return 1;
}
return 0;
} }
const struct dentry_operations cifs_ci_dentry_ops = { const struct dentry_operations cifs_ci_dentry_ops = {
......
...@@ -313,8 +313,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, ...@@ -313,8 +313,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
* If the server returned a read oplock and we have mandatory brlocks, * If the server returned a read oplock and we have mandatory brlocks,
* set oplock level to None. * set oplock level to None.
*/ */
if (oplock == server->vals->oplock_read && if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) {
cifs_has_mand_locks(cinode)) {
cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n"); cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n");
oplock = 0; oplock = 0;
} }
...@@ -324,6 +323,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, ...@@ -324,6 +323,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
oplock = fid->pending_open->oplock; oplock = fid->pending_open->oplock;
list_del(&fid->pending_open->olist); list_del(&fid->pending_open->olist);
fid->purge_cache = false;
server->ops->set_fid(cfile, fid, oplock); server->ops->set_fid(cfile, fid, oplock);
list_add(&cfile->tlist, &tcon->openFileList); list_add(&cfile->tlist, &tcon->openFileList);
...@@ -334,6 +334,9 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, ...@@ -334,6 +334,9 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
list_add_tail(&cfile->flist, &cinode->openFileList); list_add_tail(&cfile->flist, &cinode->openFileList);
spin_unlock(&cifs_file_list_lock); spin_unlock(&cifs_file_list_lock);
if (fid->purge_cache)
cifs_invalidate_mapping(inode);
file->private_data = cfile; file->private_data = cfile;
return cfile; return cfile;
} }
...@@ -1524,12 +1527,12 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, ...@@ -1524,12 +1527,12 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
* read won't conflict with non-overlapted locks due to * read won't conflict with non-overlapted locks due to
* pagereading. * pagereading.
*/ */
if (!CIFS_I(inode)->clientCanCacheAll && if (!CIFS_CACHE_WRITE(CIFS_I(inode)) &&
CIFS_I(inode)->clientCanCacheRead) { CIFS_CACHE_READ(CIFS_I(inode))) {
cifs_invalidate_mapping(inode); cifs_invalidate_mapping(inode);
cifs_dbg(FYI, "Set no oplock for inode=%p due to mand locks\n", cifs_dbg(FYI, "Set no oplock for inode=%p due to mand locks\n",
inode); inode);
CIFS_I(inode)->clientCanCacheRead = false; CIFS_I(inode)->oplock = 0;
} }
rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
...@@ -2213,7 +2216,7 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, ...@@ -2213,7 +2216,7 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
cifs_dbg(FYI, "Sync file - name: %s datasync: 0x%x\n", cifs_dbg(FYI, "Sync file - name: %s datasync: 0x%x\n",
file->f_path.dentry->d_name.name, datasync); file->f_path.dentry->d_name.name, datasync);
if (!CIFS_I(inode)->clientCanCacheRead) { if (!CIFS_CACHE_READ(CIFS_I(inode))) {
rc = cifs_invalidate_mapping(inode); rc = cifs_invalidate_mapping(inode);
if (rc) { if (rc) {
cifs_dbg(FYI, "rc: %d during invalidate phase\n", rc); cifs_dbg(FYI, "rc: %d during invalidate phase\n", rc);
...@@ -2577,7 +2580,7 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, ...@@ -2577,7 +2580,7 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
ssize_t written; ssize_t written;
if (cinode->clientCanCacheAll) { if (CIFS_CACHE_WRITE(cinode)) {
if (cap_unix(tcon->ses) && if (cap_unix(tcon->ses) &&
(CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))
&& ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
...@@ -2591,7 +2594,7 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, ...@@ -2591,7 +2594,7 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
* these pages but not on the region from pos to ppos+len-1. * these pages but not on the region from pos to ppos+len-1.
*/ */
written = cifs_user_writev(iocb, iov, nr_segs, pos); written = cifs_user_writev(iocb, iov, nr_segs, pos);
if (written > 0 && cinode->clientCanCacheRead) { if (written > 0 && CIFS_CACHE_READ(cinode)) {
/* /*
* Windows 7 server can delay breaking level2 oplock if a write * Windows 7 server can delay breaking level2 oplock if a write
* request comes - break it on the client to prevent reading * request comes - break it on the client to prevent reading
...@@ -2600,7 +2603,7 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, ...@@ -2600,7 +2603,7 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
cifs_invalidate_mapping(inode); cifs_invalidate_mapping(inode);
cifs_dbg(FYI, "Set no oplock for inode=%p after a write operation\n", cifs_dbg(FYI, "Set no oplock for inode=%p after a write operation\n",
inode); inode);
cinode->clientCanCacheRead = false; cinode->oplock = 0;
} }
return written; return written;
} }
...@@ -2957,7 +2960,7 @@ cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, ...@@ -2957,7 +2960,7 @@ cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
* on pages affected by this read but not on the region from pos to * on pages affected by this read but not on the region from pos to
* pos+len-1. * pos+len-1.
*/ */
if (!cinode->clientCanCacheRead) if (!CIFS_CACHE_READ(cinode))
return cifs_user_readv(iocb, iov, nr_segs, pos); return cifs_user_readv(iocb, iov, nr_segs, pos);
if (cap_unix(tcon->ses) && if (cap_unix(tcon->ses) &&
...@@ -3093,7 +3096,7 @@ int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -3093,7 +3096,7 @@ int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma)
xid = get_xid(); xid = get_xid();
if (!CIFS_I(inode)->clientCanCacheRead) { if (!CIFS_CACHE_READ(CIFS_I(inode))) {
rc = cifs_invalidate_mapping(inode); rc = cifs_invalidate_mapping(inode);
if (rc) if (rc)
return rc; return rc;
...@@ -3526,7 +3529,7 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping, ...@@ -3526,7 +3529,7 @@ static int cifs_write_begin(struct file *file, struct address_space *mapping,
* is, when the page lies beyond the EOF, or straddles the EOF * is, when the page lies beyond the EOF, or straddles the EOF
* and the write will cover all of the existing data. * and the write will cover all of the existing data.
*/ */
if (CIFS_I(mapping->host)->clientCanCacheRead) { if (CIFS_CACHE_READ(CIFS_I(mapping->host))) {
i_size = i_size_read(mapping->host); i_size = i_size_read(mapping->host);
if (page_start >= i_size || if (page_start >= i_size ||
(offset == 0 && (pos + len) >= i_size)) { (offset == 0 && (pos + len) >= i_size)) {
...@@ -3609,20 +3612,20 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -3609,20 +3612,20 @@ void cifs_oplock_break(struct work_struct *work)
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
int rc = 0; int rc = 0;
if (!cinode->clientCanCacheAll && cinode->clientCanCacheRead && if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) &&
cifs_has_mand_locks(cinode)) { cifs_has_mand_locks(cinode)) {
cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n", cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n",
inode); inode);
cinode->clientCanCacheRead = false; cinode->oplock = 0;
} }
if (inode && S_ISREG(inode->i_mode)) { if (inode && S_ISREG(inode->i_mode)) {
if (cinode->clientCanCacheRead) if (CIFS_CACHE_READ(cinode))
break_lease(inode, O_RDONLY); break_lease(inode, O_RDONLY);
else else
break_lease(inode, O_WRONLY); break_lease(inode, O_WRONLY);
rc = filemap_fdatawrite(inode->i_mapping); rc = filemap_fdatawrite(inode->i_mapping);
if (cinode->clientCanCacheRead == 0) { if (!CIFS_CACHE_READ(cinode)) {
rc = filemap_fdatawait(inode->i_mapping); rc = filemap_fdatawait(inode->i_mapping);
mapping_set_error(inode->i_mapping, rc); mapping_set_error(inode->i_mapping, rc);
cifs_invalidate_mapping(inode); cifs_invalidate_mapping(inode);
......
...@@ -101,7 +101,7 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) ...@@ -101,7 +101,7 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
} }
/* don't bother with revalidation if we have an oplock */ /* don't bother with revalidation if we have an oplock */
if (cifs_i->clientCanCacheRead) { if (CIFS_CACHE_READ(cifs_i)) {
cifs_dbg(FYI, "%s: inode %llu is oplocked\n", cifs_dbg(FYI, "%s: inode %llu is oplocked\n",
__func__, cifs_i->uniqueid); __func__, cifs_i->uniqueid);
return; return;
...@@ -549,6 +549,10 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, ...@@ -549,6 +549,10 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
* when Unix extensions are disabled - fake it. * when Unix extensions are disabled - fake it.
*/ */
fattr->cf_nlink = 2; fattr->cf_nlink = 2;
} else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
fattr->cf_mode = S_IFLNK;
fattr->cf_dtype = DT_LNK;
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
} else { } else {
fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
fattr->cf_dtype = DT_REG; fattr->cf_dtype = DT_REG;
...@@ -646,7 +650,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, ...@@ -646,7 +650,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
cifs_dbg(FYI, "Getting info on %s\n", full_path); cifs_dbg(FYI, "Getting info on %s\n", full_path);
if ((data == NULL) && (*inode != NULL)) { if ((data == NULL) && (*inode != NULL)) {
if (CIFS_I(*inode)->clientCanCacheRead) { if (CIFS_CACHE_READ(CIFS_I(*inode))) {
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
goto cgii_exit; goto cgii_exit;
} }
...@@ -1657,7 +1661,7 @@ cifs_inode_needs_reval(struct inode *inode) ...@@ -1657,7 +1661,7 @@ cifs_inode_needs_reval(struct inode *inode)
struct cifsInodeInfo *cifs_i = CIFS_I(inode); struct cifsInodeInfo *cifs_i = 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);
if (cifs_i->clientCanCacheRead) if (CIFS_CACHE_READ(cifs_i))
return false; return false;
if (!lookupCacheEnabled) if (!lookupCacheEnabled)
...@@ -1800,7 +1804,7 @@ int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, ...@@ -1800,7 +1804,7 @@ int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
* We need to be sure that all dirty pages are written and the server * We need to be sure that all dirty pages are written and the server
* has actual ctime, mtime and file length. * has actual ctime, mtime and file length.
*/ */
if (!CIFS_I(inode)->clientCanCacheRead && inode->i_mapping && if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping &&
inode->i_mapping->nrpages != 0) { inode->i_mapping->nrpages != 0) {
rc = filemap_fdatawait(inode->i_mapping); rc = filemap_fdatawait(inode->i_mapping);
if (rc) { if (rc) {
......
...@@ -509,6 +509,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) ...@@ -509,6 +509,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
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 *tcon; struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
xid = get_xid(); xid = get_xid();
...@@ -519,25 +520,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) ...@@ -519,25 +520,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
goto out; goto out;
} }
tcon = tlink_tcon(tlink); tcon = tlink_tcon(tlink);
server = tcon->ses->server;
/*
* For now, we just handle symlinks with unix extensions enabled.
* Eventually we should handle NTFS reparse points, and MacOS
* symlink support. For instance...
*
* rc = CIFSSMBQueryReparseLinkInfo(...)
*
* For now, just return -EACCES when the server doesn't support posix
* extensions. Note that we still allow querying symlinks when posix
* extensions are manually disabled. We could disable these as well
* but there doesn't seem to be any harm in allowing the client to
* read them.
*/
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) &&
!cap_unix(tcon->ses)) {
rc = -EACCES;
goto out;
}
full_path = build_path_from_dentry(direntry); full_path = build_path_from_dentry(direntry);
if (!full_path) if (!full_path)
...@@ -559,6 +542,9 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) ...@@ -559,6 +542,9 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
if ((rc != 0) && cap_unix(tcon->ses)) if ((rc != 0) && cap_unix(tcon->ses))
rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path,
cifs_sb->local_nls); cifs_sb->local_nls);
else if (rc != 0 && server->ops->query_symlink)
rc = server->ops->query_symlink(xid, tcon, full_path,
&target_path, cifs_sb);
kfree(full_path); kfree(full_path);
out: out:
......
...@@ -105,6 +105,7 @@ sesInfoFree(struct cifs_ses *buf_to_free) ...@@ -105,6 +105,7 @@ sesInfoFree(struct cifs_ses *buf_to_free)
} }
kfree(buf_to_free->user_name); kfree(buf_to_free->user_name);
kfree(buf_to_free->domainName); kfree(buf_to_free->domainName);
kfree(buf_to_free->auth_key.response);
kfree(buf_to_free); kfree(buf_to_free);
} }
...@@ -545,19 +546,15 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) ...@@ -545,19 +546,15 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
oplock &= 0xF; oplock &= 0xF;
if (oplock == OPLOCK_EXCLUSIVE) { if (oplock == OPLOCK_EXCLUSIVE) {
cinode->clientCanCacheAll = true; cinode->oplock = CIFS_CACHE_WRITE_FLG | CIFS_CACHE_READ_FLG;
cinode->clientCanCacheRead = true;
cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n",
&cinode->vfs_inode); &cinode->vfs_inode);
} else if (oplock == OPLOCK_READ) { } else if (oplock == OPLOCK_READ) {
cinode->clientCanCacheAll = false; cinode->oplock = CIFS_CACHE_READ_FLG;
cinode->clientCanCacheRead = true;
cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", cifs_dbg(FYI, "Level II Oplock granted on inode %p\n",
&cinode->vfs_inode); &cinode->vfs_inode);
} else { } else
cinode->clientCanCacheAll = false; cinode->oplock = 0;
cinode->clientCanCacheRead = false;
}
} }
bool bool
......
...@@ -172,6 +172,9 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) ...@@ -172,6 +172,9 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
if (cifs_dfs_is_possible(cifs_sb) && if (cifs_dfs_is_possible(cifs_sb) &&
(fattr->cf_cifsattrs & ATTR_REPARSE)) (fattr->cf_cifsattrs & ATTR_REPARSE))
fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
} else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
fattr->cf_mode = S_IFLNK;
fattr->cf_dtype = DT_LNK;
} else { } else {
fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
fattr->cf_dtype = DT_REG; fattr->cf_dtype = DT_REG;
......
...@@ -226,7 +226,7 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, ...@@ -226,7 +226,7 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
*(bcc_ptr+1) = 0; *(bcc_ptr+1) = 0;
} else { } else {
bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name, bytes_ret = cifs_strtoUTF16((__le16 *) bcc_ptr, ses->user_name,
MAX_USERNAME_SIZE, nls_cp); CIFS_MAX_USERNAME_LEN, nls_cp);
} }
bcc_ptr += 2 * bytes_ret; bcc_ptr += 2 * bytes_ret;
bcc_ptr += 2; /* account for null termination */ bcc_ptr += 2; /* account for null termination */
...@@ -246,8 +246,8 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, ...@@ -246,8 +246,8 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
/* BB what about null user mounts - check that we do this BB */ /* BB what about null user mounts - check that we do this BB */
/* copy user */ /* copy user */
if (ses->user_name != NULL) { if (ses->user_name != NULL) {
strncpy(bcc_ptr, ses->user_name, MAX_USERNAME_SIZE); strncpy(bcc_ptr, ses->user_name, CIFS_MAX_USERNAME_LEN);
bcc_ptr += strnlen(ses->user_name, MAX_USERNAME_SIZE); bcc_ptr += strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);
} }
/* else null user mount */ /* else null user mount */
*bcc_ptr = 0; *bcc_ptr = 0;
...@@ -428,7 +428,8 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, ...@@ -428,7 +428,8 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC; NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC;
if (ses->server->sign) { if (ses->server->sign) {
flags |= NTLMSSP_NEGOTIATE_SIGN; flags |= NTLMSSP_NEGOTIATE_SIGN;
if (!ses->server->session_estab) if (!ses->server->session_estab ||
ses->ntlmssp->sesskey_per_smbsess)
flags |= NTLMSSP_NEGOTIATE_KEY_XCH; flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
} }
...@@ -466,7 +467,8 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -466,7 +467,8 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC; NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC;
if (ses->server->sign) { if (ses->server->sign) {
flags |= NTLMSSP_NEGOTIATE_SIGN; flags |= NTLMSSP_NEGOTIATE_SIGN;
if (!ses->server->session_estab) if (!ses->server->session_estab ||
ses->ntlmssp->sesskey_per_smbsess)
flags |= NTLMSSP_NEGOTIATE_KEY_XCH; flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
} }
...@@ -501,7 +503,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -501,7 +503,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
} else { } else {
int len; int len;
len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName, len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName,
MAX_USERNAME_SIZE, nls_cp); CIFS_MAX_USERNAME_LEN, nls_cp);
len *= 2; /* unicode is 2 bytes each */ len *= 2; /* unicode is 2 bytes each */
sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
sec_blob->DomainName.Length = cpu_to_le16(len); sec_blob->DomainName.Length = cpu_to_le16(len);
...@@ -517,7 +519,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -517,7 +519,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
} else { } else {
int len; int len;
len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name, len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name,
MAX_USERNAME_SIZE, nls_cp); CIFS_MAX_USERNAME_LEN, nls_cp);
len *= 2; /* unicode is 2 bytes each */ len *= 2; /* unicode is 2 bytes each */
sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);
sec_blob->UserName.Length = cpu_to_le16(len); sec_blob->UserName.Length = cpu_to_le16(len);
...@@ -629,7 +631,8 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, ...@@ -629,7 +631,8 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
type = select_sectype(ses->server, ses->sectype); type = select_sectype(ses->server, ses->sectype);
cifs_dbg(FYI, "sess setup type %d\n", type); cifs_dbg(FYI, "sess setup type %d\n", type);
if (type == Unspecified) { if (type == Unspecified) {
cifs_dbg(VFS, "Unable to select appropriate authentication method!"); cifs_dbg(VFS,
"Unable to select appropriate authentication method!");
return -EINVAL; return -EINVAL;
} }
...@@ -640,6 +643,8 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, ...@@ -640,6 +643,8 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
if (!ses->ntlmssp) if (!ses->ntlmssp)
return -ENOMEM; return -ENOMEM;
ses->ntlmssp->sesskey_per_smbsess = false;
} }
ssetup_ntlmssp_authenticate: ssetup_ntlmssp_authenticate:
...@@ -815,8 +820,9 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, ...@@ -815,8 +820,9 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
GFP_KERNEL); GFP_KERNEL);
if (!ses->auth_key.response) { if (!ses->auth_key.response) {
cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory", cifs_dbg(VFS,
msg->sesskey_len); "Kerberos can't allocate (%u bytes) memory",
msg->sesskey_len);
rc = -ENOMEM; rc = -ENOMEM;
goto ssetup_exit; goto ssetup_exit;
} }
...@@ -1005,5 +1011,37 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, ...@@ -1005,5 +1011,37 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
if ((phase == NtLmChallenge) && (rc == 0)) if ((phase == NtLmChallenge) && (rc == 0))
goto ssetup_ntlmssp_authenticate; goto ssetup_ntlmssp_authenticate;
if (!rc) {
mutex_lock(&ses->server->srv_mutex);
if (!ses->server->session_estab) {
if (ses->server->sign) {
ses->server->session_key.response =
kmemdup(ses->auth_key.response,
ses->auth_key.len, GFP_KERNEL);
if (!ses->server->session_key.response) {
rc = -ENOMEM;
mutex_unlock(&ses->server->srv_mutex);
goto keycp_exit;
}
ses->server->session_key.len =
ses->auth_key.len;
}
ses->server->sequence_number = 0x2;
ses->server->session_estab = true;
}
mutex_unlock(&ses->server->srv_mutex);
cifs_dbg(FYI, "CIFS session established successfully\n");
spin_lock(&GlobalMid_Lock);
ses->status = CifsGood;
ses->need_reconnect = false;
spin_unlock(&GlobalMid_Lock);
}
keycp_exit:
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
kfree(ses->ntlmssp);
return rc; return rc;
} }
...@@ -700,7 +700,7 @@ cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) ...@@ -700,7 +700,7 @@ cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
cfile->fid.netfid = fid->netfid; cfile->fid.netfid = fid->netfid;
cifs_set_oplock_level(cinode, oplock); cifs_set_oplock_level(cinode, oplock);
cinode->can_cache_brlcks = cinode->clientCanCacheAll; cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
} }
static void static void
...@@ -837,7 +837,7 @@ cifs_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, ...@@ -837,7 +837,7 @@ cifs_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
{ {
return CIFSSMBLock(0, tcon, fid->netfid, current->tgid, 0, 0, 0, 0, return CIFSSMBLock(0, tcon, fid->netfid, current->tgid, 0, 0, 0, 0,
LOCKING_ANDX_OPLOCK_RELEASE, false, LOCKING_ANDX_OPLOCK_RELEASE, false,
cinode->clientCanCacheRead ? 1 : 0); CIFS_CACHE_READ(cinode) ? 1 : 0);
} }
static int static int
...@@ -881,6 +881,43 @@ cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, ...@@ -881,6 +881,43 @@ cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
(__u8)type, wait, 0); (__u8)type, wait, 0);
} }
static int
cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
const char *full_path, char **target_path,
struct cifs_sb_info *cifs_sb)
{
int rc;
int oplock = 0;
__u16 netfid;
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
FILE_READ_ATTRIBUTES, OPEN_REPARSE_POINT, &netfid,
&oplock, NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc)
return rc;
rc = CIFSSMBQuerySymLink(xid, tcon, netfid, target_path,
cifs_sb->local_nls);
if (rc) {
CIFSSMBClose(xid, tcon, netfid);
return rc;
}
convert_delimiter(*target_path, '/');
CIFSSMBClose(xid, tcon, netfid);
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
return rc;
}
static bool
cifs_is_read_op(__u32 oplock)
{
return oplock == OPLOCK_READ;
}
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,
...@@ -927,6 +964,7 @@ struct smb_version_operations smb1_operations = { ...@@ -927,6 +964,7 @@ struct smb_version_operations smb1_operations = {
.rename_pending_delete = cifs_rename_pending_delete, .rename_pending_delete = cifs_rename_pending_delete,
.rename = CIFSSMBRename, .rename = CIFSSMBRename,
.create_hardlink = CIFSCreateHardLink, .create_hardlink = CIFSCreateHardLink,
.query_symlink = cifs_query_symlink,
.open = cifs_open_file, .open = cifs_open_file,
.set_fid = cifs_set_fid, .set_fid = cifs_set_fid,
.close = cifs_close_file, .close = cifs_close_file,
...@@ -945,6 +983,7 @@ struct smb_version_operations smb1_operations = { ...@@ -945,6 +983,7 @@ struct smb_version_operations smb1_operations = {
.mand_unlock_range = cifs_unlock_range, .mand_unlock_range = cifs_unlock_range,
.push_mand_locks = cifs_push_mandatory_locks, .push_mand_locks = cifs_push_mandatory_locks,
.query_mf_symlink = open_query_close_cifs_symlink, .query_mf_symlink = open_query_close_cifs_symlink,
.is_read_op = cifs_is_read_op,
}; };
struct smb_version_values smb1_values = { struct smb_version_values smb1_values = {
...@@ -960,7 +999,6 @@ struct smb_version_values smb1_values = { ...@@ -960,7 +999,6 @@ struct smb_version_values smb1_values = {
.cap_unix = CAP_UNIX, .cap_unix = CAP_UNIX,
.cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND,
.cap_large_files = CAP_LARGE_FILES, .cap_large_files = CAP_LARGE_FILES,
.oplock_read = OPLOCK_READ,
.signing_enabled = SECMODE_SIGN_ENABLED, .signing_enabled = SECMODE_SIGN_ENABLED,
.signing_required = SECMODE_SIGN_REQUIRED, .signing_required = SECMODE_SIGN_REQUIRED,
}; };
...@@ -34,29 +34,6 @@ ...@@ -34,29 +34,6 @@
#include "fscache.h" #include "fscache.h"
#include "smb2proto.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 ||
oplock == SMB2_OPLOCK_LEVEL_BATCH) {
cinode->clientCanCacheAll = true;
cinode->clientCanCacheRead = true;
cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n",
&cinode->vfs_inode);
} else if (oplock == SMB2_OPLOCK_LEVEL_II) {
cinode->clientCanCacheAll = false;
cinode->clientCanCacheRead = true;
cifs_dbg(FYI, "Level II Oplock granted on inode %p\n",
&cinode->vfs_inode);
} else {
cinode->clientCanCacheAll = false;
cinode->clientCanCacheRead = false;
}
}
int int
smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
__u32 *oplock, FILE_ALL_INFO *buf) __u32 *oplock, FILE_ALL_INFO *buf)
...@@ -86,7 +63,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, ...@@ -86,7 +63,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE); memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE);
rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data); rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data, NULL);
if (rc) if (rc)
goto out; goto out;
......
...@@ -60,7 +60,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -60,7 +60,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
if (rc) { if (rc) {
kfree(utf16_path); kfree(utf16_path);
return rc; return rc;
...@@ -136,7 +136,8 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -136,7 +136,8 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
return -ENOMEM; return -ENOMEM;
rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN, 0, smb2_data, FILE_READ_ATTRIBUTES, FILE_OPEN,
OPEN_REPARSE_POINT, smb2_data,
SMB2_OP_QUERY_INFO); SMB2_OP_QUERY_INFO);
if (rc) if (rc)
goto out; goto out;
...@@ -191,8 +192,8 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, ...@@ -191,8 +192,8 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
CREATE_DELETE_ON_CLOSE, NULL, CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
SMB2_OP_DELETE); NULL, SMB2_OP_DELETE);
} }
static int static int
......
...@@ -171,6 +171,10 @@ smb2_check_message(char *buf, unsigned int length) ...@@ -171,6 +171,10 @@ smb2_check_message(char *buf, unsigned int length)
if (4 + len != clc_len) { if (4 + len != clc_len) {
cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n", cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n",
clc_len, 4 + len, mid); clc_len, 4 + len, mid);
/* create failed on symlink */
if (command == SMB2_CREATE_HE &&
hdr->Status == STATUS_STOPPED_ON_SYMLINK)
return 0;
/* Windows 7 server returns 24 bytes more */ /* Windows 7 server returns 24 bytes more */
if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE) if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE)
return 0; return 0;
...@@ -376,23 +380,15 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) ...@@ -376,23 +380,15 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
__le32 __le32
smb2_get_lease_state(struct cifsInodeInfo *cinode) smb2_get_lease_state(struct cifsInodeInfo *cinode)
{ {
if (cinode->clientCanCacheAll) __le32 lease = 0;
return SMB2_LEASE_WRITE_CACHING | SMB2_LEASE_READ_CACHING;
else if (cinode->clientCanCacheRead) if (CIFS_CACHE_WRITE(cinode))
return SMB2_LEASE_READ_CACHING; lease |= SMB2_LEASE_WRITE_CACHING;
return 0; if (CIFS_CACHE_HANDLE(cinode))
} lease |= SMB2_LEASE_HANDLE_CACHING;
if (CIFS_CACHE_READ(cinode))
__u8 smb2_map_lease_to_oplock(__le32 lease_state) lease |= SMB2_LEASE_READ_CACHING;
{ return lease;
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 smb2_lease_break_work {
...@@ -417,96 +413,109 @@ cifs_ses_oplock_break(struct work_struct *work) ...@@ -417,96 +413,109 @@ cifs_ses_oplock_break(struct work_struct *work)
} }
static bool static bool
smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
struct smb2_lease_break_work *lw)
{ {
struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; bool found;
struct list_head *tmp, *tmp1, *tmp2; __u8 lease_state;
struct cifs_ses *ses; struct list_head *tmp;
struct cifs_tcon *tcon;
struct cifsInodeInfo *cinode;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_pending_open *open; struct cifs_pending_open *open;
struct smb2_lease_break_work *lw; struct cifsInodeInfo *cinode;
bool found;
int ack_req = le32_to_cpu(rsp->Flags & int ack_req = le32_to_cpu(rsp->Flags &
SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); lease_state = le32_to_cpu(rsp->NewLeaseState);
if (!lw)
return false;
INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); list_for_each(tmp, &tcon->openFileList) {
lw->lease_state = rsp->NewLeaseState; cfile = list_entry(tmp, struct cifsFileInfo, tlist);
cinode = CIFS_I(cfile->dentry->d_inode);
cifs_dbg(FYI, "Checking for lease break\n"); if (memcmp(cinode->lease_key, rsp->LeaseKey,
SMB2_LEASE_KEY_SIZE))
continue;
/* look up tcon based on tid & uid */ cifs_dbg(FYI, "found in the open list\n");
spin_lock(&cifs_tcp_ses_lock); cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
list_for_each(tmp, &server->smb_ses_list) { le32_to_cpu(rsp->NewLeaseState));
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
spin_lock(&cifs_file_list_lock); server->ops->set_oplock_level(cinode, lease_state, 0, NULL);
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); if (ack_req)
list_for_each(tmp2, &tcon->openFileList) { cfile->oplock_break_cancelled = false;
cfile = list_entry(tmp2, struct cifsFileInfo, else
tlist); cfile->oplock_break_cancelled = true;
cinode = CIFS_I(cfile->dentry->d_inode);
if (memcmp(cinode->lease_key, rsp->LeaseKey, queue_work(cifsiod_wq, &cfile->oplock_break);
SMB2_LEASE_KEY_SIZE)) kfree(lw);
continue; return true;
}
cifs_dbg(FYI, "found in the open list\n"); found = false;
cifs_dbg(FYI, "lease key match, lease break 0x%d\n", list_for_each_entry(open, &tcon->pending_opens, olist) {
le32_to_cpu(rsp->NewLeaseState)); 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);
}
smb2_set_oplock_level(cinode, cifs_dbg(FYI, "found in the pending open list\n");
smb2_map_lease_to_oplock(rsp->NewLeaseState)); cifs_dbg(FYI, "lease key match, lease break 0x%d\n",
le32_to_cpu(rsp->NewLeaseState));
if (ack_req) open->oplock = lease_state;
cfile->oplock_break_cancelled = false; }
else return found;
cfile->oplock_break_cancelled = true; }
queue_work(cifsiod_wq, &cfile->oplock_break); static bool
smb2_is_valid_lease_break(char *buffer)
{
struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer;
struct list_head *tmp, *tmp1, *tmp2;
struct TCP_Server_Info *server;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
struct smb2_lease_break_work *lw;
spin_unlock(&cifs_file_list_lock); lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
spin_unlock(&cifs_tcp_ses_lock); if (!lw)
return true; return false;
}
found = false; INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
list_for_each_entry(open, &tcon->pending_opens, olist) { lw->lease_state = rsp->NewLeaseState;
if (memcmp(open->lease_key, rsp->LeaseKey,
SMB2_LEASE_KEY_SIZE))
continue;
if (!found && ack_req) { cifs_dbg(FYI, "Checking for lease break\n");
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);
}
cifs_dbg(FYI, "found in the pending open list\n"); /* look up tcon based on tid & uid */
cifs_dbg(FYI, "lease key match, lease break 0x%d\n", spin_lock(&cifs_tcp_ses_lock);
le32_to_cpu(rsp->NewLeaseState)); list_for_each(tmp, &cifs_tcp_ses_list) {
server = list_entry(tmp, struct TCP_Server_Info, tcp_ses_list);
open->oplock = list_for_each(tmp1, &server->smb_ses_list) {
smb2_map_lease_to_oplock(rsp->NewLeaseState); ses = list_entry(tmp1, struct cifs_ses, smb_ses_list);
}
if (found) { spin_lock(&cifs_file_list_lock);
spin_unlock(&cifs_file_list_lock); list_for_each(tmp2, &ses->tcon_list) {
spin_unlock(&cifs_tcp_ses_lock); tcon = list_entry(tmp2, struct cifs_tcon,
return true; tcon_list);
cifs_stats_inc(
&tcon->stats.cifs_stats.num_oplock_brks);
if (smb2_tcon_has_lease(tcon, rsp, lw)) {
spin_unlock(&cifs_file_list_lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
} }
spin_unlock(&cifs_file_list_lock);
} }
spin_unlock(&cifs_file_list_lock);
} }
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
kfree(lw); kfree(lw);
...@@ -532,7 +541,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -532,7 +541,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
if (rsp->StructureSize != if (rsp->StructureSize !=
smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
if (le16_to_cpu(rsp->StructureSize) == 44) if (le16_to_cpu(rsp->StructureSize) == 44)
return smb2_is_valid_lease_break(buffer, server); return smb2_is_valid_lease_break(buffer);
else else
return false; return false;
} }
...@@ -560,14 +569,15 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -560,14 +569,15 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
cifs_dbg(FYI, "file id match, oplock break\n"); cifs_dbg(FYI, "file id match, oplock break\n");
cinode = CIFS_I(cfile->dentry->d_inode); cinode = CIFS_I(cfile->dentry->d_inode);
if (!cinode->clientCanCacheAll && if (!CIFS_CACHE_WRITE(cinode) &&
rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE)
cfile->oplock_break_cancelled = true; cfile->oplock_break_cancelled = true;
else else
cfile->oplock_break_cancelled = false; cfile->oplock_break_cancelled = false;
smb2_set_oplock_level(cinode, server->ops->set_oplock_level(cinode,
rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0); rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0,
0, NULL);
queue_work(cifsiod_wq, &cfile->oplock_break); queue_work(cifsiod_wq, &cfile->oplock_break);
......
This diff is collapsed.
...@@ -477,6 +477,13 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, ...@@ -477,6 +477,13 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
return -EIO; return -EIO;
} }
/*
* If we are here due to reconnect, free per-smb session key
* in case signing was required.
*/
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
/* /*
* If memory allocation is successful, caller of this function * If memory allocation is successful, caller of this function
* frees it. * frees it.
...@@ -484,6 +491,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, ...@@ -484,6 +491,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
if (!ses->ntlmssp) if (!ses->ntlmssp)
return -ENOMEM; return -ENOMEM;
ses->ntlmssp->sesskey_per_smbsess = true;
/* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */ /* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */
ses->sectype = RawNTLMSSP; ses->sectype = RawNTLMSSP;
...@@ -628,6 +636,40 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, ...@@ -628,6 +636,40 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
/* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */ /* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
if ((phase == NtLmChallenge) && (rc == 0)) if ((phase == NtLmChallenge) && (rc == 0))
goto ssetup_ntlmssp_authenticate; goto ssetup_ntlmssp_authenticate;
if (!rc) {
mutex_lock(&server->srv_mutex);
if (server->sign && server->ops->generate_signingkey) {
rc = server->ops->generate_signingkey(ses);
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
if (rc) {
cifs_dbg(FYI,
"SMB3 session key generation failed\n");
mutex_unlock(&server->srv_mutex);
goto keygen_exit;
}
}
if (!server->session_estab) {
server->sequence_number = 0x2;
server->session_estab = true;
}
mutex_unlock(&server->srv_mutex);
cifs_dbg(FYI, "SMB2/3 session established successfully\n");
spin_lock(&GlobalMid_Lock);
ses->status = CifsGood;
ses->need_reconnect = false;
spin_unlock(&GlobalMid_Lock);
}
keygen_exit:
if (!server->sign) {
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
}
kfree(ses->ntlmssp);
return rc; return rc;
} }
...@@ -813,39 +855,6 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -813,39 +855,6 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
return rc; return rc;
} }
static struct create_lease *
create_lease_buf(u8 *lease_key, u8 oplock)
{
struct create_lease *buf;
buf = kzalloc(sizeof(struct create_lease), GFP_KERNEL);
if (!buf)
return NULL;
buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key));
buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8)));
if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE)
buf->lcontext.LeaseState = SMB2_LEASE_WRITE_CACHING |
SMB2_LEASE_READ_CACHING;
else if (oplock == SMB2_OPLOCK_LEVEL_II)
buf->lcontext.LeaseState = SMB2_LEASE_READ_CACHING;
else if (oplock == SMB2_OPLOCK_LEVEL_BATCH)
buf->lcontext.LeaseState = SMB2_LEASE_HANDLE_CACHING |
SMB2_LEASE_READ_CACHING |
SMB2_LEASE_WRITE_CACHING;
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_lease, lcontext));
buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context));
buf->ccontext.NameOffset = cpu_to_le16(offsetof
(struct create_lease, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
buf->Name[0] = 'R';
buf->Name[1] = 'q';
buf->Name[2] = 'L';
buf->Name[3] = 's';
return buf;
}
static struct create_durable * static struct create_durable *
create_durable_buf(void) create_durable_buf(void)
...@@ -894,55 +903,49 @@ create_reconnect_durable_buf(struct cifs_fid *fid) ...@@ -894,55 +903,49 @@ create_reconnect_durable_buf(struct cifs_fid *fid)
} }
static __u8 static __u8
parse_lease_state(struct smb2_create_rsp *rsp) parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp,
unsigned int *epoch)
{ {
char *data_offset; char *data_offset;
struct create_lease *lc; struct create_context *cc;
bool found = false;
unsigned int next = 0; unsigned int next = 0;
char *name; char *name;
data_offset = (char *)rsp + 4 + le32_to_cpu(rsp->CreateContextsOffset); data_offset = (char *)rsp + 4 + le32_to_cpu(rsp->CreateContextsOffset);
lc = (struct create_lease *)data_offset; cc = (struct create_context *)data_offset;
do { do {
lc = (struct create_lease *)((char *)lc + next); cc = (struct create_context *)((char *)cc + next);
name = le16_to_cpu(lc->ccontext.NameOffset) + (char *)lc; name = le16_to_cpu(cc->NameOffset) + (char *)cc;
if (le16_to_cpu(lc->ccontext.NameLength) != 4 || if (le16_to_cpu(cc->NameLength) != 4 ||
strncmp(name, "RqLs", 4)) { strncmp(name, "RqLs", 4)) {
next = le32_to_cpu(lc->ccontext.Next); next = le32_to_cpu(cc->Next);
continue; continue;
} }
if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) return server->ops->parse_lease_buf(cc, epoch);
return SMB2_OPLOCK_LEVEL_NOCHANGE;
found = true;
break;
} while (next != 0); } while (next != 0);
if (!found) return 0;
return 0;
return smb2_map_lease_to_oplock(lc->lcontext.LeaseState);
} }
static int static int
add_lease_context(struct kvec *iov, unsigned int *num_iovec, __u8 *oplock) add_lease_context(struct TCP_Server_Info *server, struct kvec *iov,
unsigned int *num_iovec, __u8 *oplock)
{ {
struct smb2_create_req *req = iov[0].iov_base; struct smb2_create_req *req = iov[0].iov_base;
unsigned int num = *num_iovec; unsigned int num = *num_iovec;
iov[num].iov_base = create_lease_buf(oplock+1, *oplock); iov[num].iov_base = server->ops->create_lease_buf(oplock+1, *oplock);
if (iov[num].iov_base == NULL) if (iov[num].iov_base == NULL)
return -ENOMEM; return -ENOMEM;
iov[num].iov_len = sizeof(struct create_lease); iov[num].iov_len = server->vals->create_lease_size;
req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
if (!req->CreateContextsOffset) if (!req->CreateContextsOffset)
req->CreateContextsOffset = cpu_to_le32( req->CreateContextsOffset = cpu_to_le32(
sizeof(struct smb2_create_req) - 4 + sizeof(struct smb2_create_req) - 4 +
iov[num - 1].iov_len); iov[num - 1].iov_len);
req->CreateContextsLength = cpu_to_le32( le32_add_cpu(&req->CreateContextsLength,
le32_to_cpu(req->CreateContextsLength) + server->vals->create_lease_size);
sizeof(struct create_lease)); inc_rfc1001_len(&req->hdr, server->vals->create_lease_size);
inc_rfc1001_len(&req->hdr, sizeof(struct create_lease));
*num_iovec = num + 1; *num_iovec = num + 1;
return 0; return 0;
} }
...@@ -967,9 +970,7 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec, ...@@ -967,9 +970,7 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec,
req->CreateContextsOffset = req->CreateContextsOffset =
cpu_to_le32(sizeof(struct smb2_create_req) - 4 + cpu_to_le32(sizeof(struct smb2_create_req) - 4 +
iov[1].iov_len); iov[1].iov_len);
req->CreateContextsLength = le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable));
cpu_to_le32(le32_to_cpu(req->CreateContextsLength) +
sizeof(struct create_durable));
inc_rfc1001_len(&req->hdr, sizeof(struct create_durable)); inc_rfc1001_len(&req->hdr, sizeof(struct create_durable));
*num_iovec = num + 1; *num_iovec = num + 1;
return 0; return 0;
...@@ -977,7 +978,8 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec, ...@@ -977,7 +978,8 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec,
int int
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
__u8 *oplock, struct smb2_file_all_info *buf) __u8 *oplock, struct smb2_file_all_info *buf,
struct smb2_err_rsp **err_buf)
{ {
struct smb2_create_req *req; struct smb2_create_req *req;
struct smb2_create_rsp *rsp; struct smb2_create_rsp *rsp;
...@@ -1048,11 +1050,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, ...@@ -1048,11 +1050,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
if (!server->oplocks) if (!server->oplocks)
*oplock = SMB2_OPLOCK_LEVEL_NONE; *oplock = SMB2_OPLOCK_LEVEL_NONE;
if (!(tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) || if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) ||
*oplock == SMB2_OPLOCK_LEVEL_NONE) *oplock == SMB2_OPLOCK_LEVEL_NONE)
req->RequestedOplockLevel = *oplock; req->RequestedOplockLevel = *oplock;
else { else {
rc = add_lease_context(iov, &num_iovecs, oplock); rc = add_lease_context(server, iov, &num_iovecs, oplock);
if (rc) { if (rc) {
cifs_small_buf_release(req); cifs_small_buf_release(req);
kfree(copy_path); kfree(copy_path);
...@@ -1062,11 +1064,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, ...@@ -1062,11 +1064,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) {
/* need to set Next field of lease context if we request it */ /* need to set Next field of lease context if we request it */
if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) { if (server->capabilities & SMB2_GLOBAL_CAP_LEASING) {
struct create_context *ccontext = struct create_context *ccontext =
(struct create_context *)iov[num_iovecs-1].iov_base; (struct create_context *)iov[num_iovecs-1].iov_base;
ccontext->Next = ccontext->Next =
cpu_to_le32(sizeof(struct create_lease)); cpu_to_le32(server->vals->create_lease_size);
} }
rc = add_durable_context(iov, &num_iovecs, oparms); rc = add_durable_context(iov, &num_iovecs, oparms);
if (rc) { if (rc) {
...@@ -1082,6 +1084,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, ...@@ -1082,6 +1084,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
if (rc != 0) { if (rc != 0) {
cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
if (err_buf)
*err_buf = kmemdup(rsp, get_rfc1002_length(rsp) + 4,
GFP_KERNEL);
goto creat_exit; goto creat_exit;
} }
...@@ -1098,7 +1103,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, ...@@ -1098,7 +1103,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
} }
if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE)
*oplock = parse_lease_state(rsp); *oplock = parse_lease_state(server, rsp, &oparms->fid->epoch);
else else
*oplock = rsp->OplockLevel; *oplock = rsp->OplockLevel;
creat_exit: creat_exit:
......
...@@ -150,6 +150,20 @@ struct smb2_err_rsp { ...@@ -150,6 +150,20 @@ struct smb2_err_rsp {
__u8 ErrorData[1]; /* variable length */ __u8 ErrorData[1]; /* variable length */
} __packed; } __packed;
struct smb2_symlink_err_rsp {
__le32 SymLinkLength;
__le32 SymLinkErrorTag;
__le32 ReparseTag;
__le16 ReparseDataLength;
__le16 UnparsedPathLength;
__le16 SubstituteNameOffset;
__le16 SubstituteNameLength;
__le16 PrintNameOffset;
__le16 PrintNameLength;
__le32 Flags;
__u8 PathBuffer[0];
} __packed;
#define SMB2_CLIENT_GUID_SIZE 16 #define SMB2_CLIENT_GUID_SIZE 16
extern __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE]; extern __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
...@@ -462,6 +476,10 @@ struct create_context { ...@@ -462,6 +476,10 @@ struct create_context {
__u8 Buffer[0]; __u8 Buffer[0];
} __packed; } __packed;
#define SMB2_LEASE_READ_CACHING_HE 0x01
#define SMB2_LEASE_HANDLE_CACHING_HE 0x02
#define SMB2_LEASE_WRITE_CACHING_HE 0x04
#define SMB2_LEASE_NONE __constant_cpu_to_le32(0x00) #define SMB2_LEASE_NONE __constant_cpu_to_le32(0x00)
#define SMB2_LEASE_READ_CACHING __constant_cpu_to_le32(0x01) #define SMB2_LEASE_READ_CACHING __constant_cpu_to_le32(0x01)
#define SMB2_LEASE_HANDLE_CACHING __constant_cpu_to_le32(0x02) #define SMB2_LEASE_HANDLE_CACHING __constant_cpu_to_le32(0x02)
...@@ -479,12 +497,31 @@ struct lease_context { ...@@ -479,12 +497,31 @@ struct lease_context {
__le64 LeaseDuration; __le64 LeaseDuration;
} __packed; } __packed;
struct lease_context_v2 {
__le64 LeaseKeyLow;
__le64 LeaseKeyHigh;
__le32 LeaseState;
__le32 LeaseFlags;
__le64 LeaseDuration;
__le64 ParentLeaseKeyLow;
__le64 ParentLeaseKeyHigh;
__le16 Epoch;
__le16 Reserved;
} __packed;
struct create_lease { struct create_lease {
struct create_context ccontext; struct create_context ccontext;
__u8 Name[8]; __u8 Name[8];
struct lease_context lcontext; struct lease_context lcontext;
} __packed; } __packed;
struct create_lease_v2 {
struct create_context ccontext;
__u8 Name[8];
struct lease_context_v2 lcontext;
__u8 Pad[4];
} __packed;
struct create_durable { struct create_durable {
struct create_context ccontext; struct create_context ccontext;
__u8 Name[8]; __u8 Name[8];
......
...@@ -53,7 +53,6 @@ extern int smb3_calc_signature(struct smb_rqst *rqst, ...@@ -53,7 +53,6 @@ extern int smb3_calc_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server); struct TCP_Server_Info *server);
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 __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, extern bool smb2_is_valid_oplock_break(char *buffer,
struct TCP_Server_Info *srv); struct TCP_Server_Info *srv);
...@@ -87,7 +86,6 @@ extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -87,7 +86,6 @@ extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
extern int smb2_open_file(const unsigned int xid, extern int smb2_open_file(const unsigned int xid,
struct cifs_open_parms *oparms, struct cifs_open_parms *oparms,
__u32 *oplock, FILE_ALL_INFO *buf); __u32 *oplock, FILE_ALL_INFO *buf);
extern void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
extern int smb2_unlock_range(struct cifsFileInfo *cfile, extern int smb2_unlock_range(struct cifsFileInfo *cfile,
struct file_lock *flock, const unsigned int xid); struct file_lock *flock, const unsigned int xid);
extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
...@@ -106,7 +104,8 @@ extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, ...@@ -106,7 +104,8 @@ extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses,
extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
__le16 *path, __u8 *oplock, __le16 *path, __u8 *oplock,
struct smb2_file_all_info *buf); struct smb2_file_all_info *buf,
struct smb2_err_rsp **err_buf);
extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 opcode, u64 persistent_fid, u64 volatile_fid, u32 opcode,
bool is_fsctl, char *in_data, u32 indatalen, bool is_fsctl, char *in_data, u32 indatalen,
......
...@@ -114,6 +114,23 @@ smb3_crypto_shash_allocate(struct TCP_Server_Info *server) ...@@ -114,6 +114,23 @@ smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
return 0; return 0;
} }
static struct cifs_ses *
smb2_find_smb_ses(struct smb2_hdr *smb2hdr, struct TCP_Server_Info *server)
{
struct cifs_ses *ses;
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
if (ses->Suid != smb2hdr->SessionId)
continue;
spin_unlock(&cifs_tcp_ses_lock);
return ses;
}
spin_unlock(&cifs_tcp_ses_lock);
return NULL;
}
int int
smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
...@@ -124,6 +141,13 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -124,6 +141,13 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
struct kvec *iov = rqst->rq_iov; struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec; int n_vec = rqst->rq_nvec;
struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
struct cifs_ses *ses;
ses = smb2_find_smb_ses(smb2_pdu, server);
if (!ses) {
cifs_dbg(VFS, "%s: Could not find session\n", __func__);
return 0;
}
memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE); memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
...@@ -135,7 +159,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -135,7 +159,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
} }
rc = crypto_shash_setkey(server->secmech.hmacsha256, rc = crypto_shash_setkey(server->secmech.hmacsha256,
server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE); ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with response\n", __func__); cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
return rc; return rc;
...@@ -198,8 +222,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -198,8 +222,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return rc; return rc;
} }
void int
generate_smb3signingkey(struct TCP_Server_Info *server) generate_smb3signingkey(struct cifs_ses *ses)
{ {
unsigned char zero = 0x0; unsigned char zero = 0x0;
__u8 i[4] = {0, 0, 0, 1}; __u8 i[4] = {0, 0, 0, 1};
...@@ -209,90 +233,99 @@ generate_smb3signingkey(struct TCP_Server_Info *server) ...@@ -209,90 +233,99 @@ generate_smb3signingkey(struct TCP_Server_Info *server)
unsigned char *hashptr = prfhash; unsigned char *hashptr = prfhash;
memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
memset(server->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE); memset(ses->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE);
rc = smb3_crypto_shash_allocate(server); rc = smb3_crypto_shash_allocate(ses->server);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_setkey(server->secmech.hmacsha256, rc = crypto_shash_setkey(ses->server->secmech.hmacsha256,
server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE); ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not set with session key\n", __func__); cifs_dbg(VFS, "%s: Could not set with session key\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash); rc = crypto_shash_init(&ses->server->secmech.sdeschmacsha256->shash);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not init sign hmac\n", __func__); cifs_dbg(VFS, "%s: Could not init sign hmac\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
i, 4); i, 4);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with n\n", __func__); cifs_dbg(VFS, "%s: Could not update with n\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
"SMB2AESCMAC", 12); "SMB2AESCMAC", 12);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with label\n", __func__); cifs_dbg(VFS, "%s: Could not update with label\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
&zero, 1); &zero, 1);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with zero\n", __func__); cifs_dbg(VFS, "%s: Could not update with zero\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
"SmbSign", 8); "SmbSign", 8);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with context\n", __func__); cifs_dbg(VFS, "%s: Could not update with context\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash,
L, 4); L, 4);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with L\n", __func__); cifs_dbg(VFS, "%s: Could not update with L\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash, rc = crypto_shash_final(&ses->server->secmech.sdeschmacsha256->shash,
hashptr); hashptr);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
memcpy(server->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE); memcpy(ses->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE);
smb3signkey_ret: smb3signkey_ret:
return; return rc;
} }
int int
smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{ {
int i, rc; int i;
int rc = 0;
unsigned char smb3_signature[SMB2_CMACAES_SIZE]; unsigned char smb3_signature[SMB2_CMACAES_SIZE];
unsigned char *sigptr = smb3_signature; unsigned char *sigptr = smb3_signature;
struct kvec *iov = rqst->rq_iov; struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec; int n_vec = rqst->rq_nvec;
struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
struct cifs_ses *ses;
ses = smb2_find_smb_ses(smb2_pdu, server);
if (!ses) {
cifs_dbg(VFS, "%s: Could not find session\n", __func__);
return 0;
}
memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE); memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
rc = crypto_shash_setkey(server->secmech.cmacaes, rc = crypto_shash_setkey(server->secmech.cmacaes,
server->smb3signingkey, SMB2_CMACAES_SIZE); ses->smb3signingkey, SMB2_CMACAES_SIZE);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); cifs_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
return rc; return rc;
...@@ -389,6 +422,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -389,6 +422,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
if ((smb2_pdu->Command == SMB2_NEGOTIATE) || if ((smb2_pdu->Command == SMB2_NEGOTIATE) ||
(smb2_pdu->Command == SMB2_SESSION_SETUP) ||
(smb2_pdu->Command == SMB2_OPLOCK_BREAK) || (smb2_pdu->Command == SMB2_OPLOCK_BREAK) ||
(!server->session_estab)) (!server->session_estab))
return 0; return 0;
......
This diff is collapsed.
/*
* include/uapi/linux/cifs/cifs_mount.h
*
* Author(s): Scott Lovenberg (scott.lovenberg@gmail.com)
*
* 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.
*/
#ifndef _CIFS_MOUNT_H
#define _CIFS_MOUNT_H
/* Max string lengths for cifs mounting options. */
#define CIFS_MAX_DOMAINNAME_LEN 256 /* max fully qualified domain name */
#define CIFS_MAX_USERNAME_LEN 256 /* reasonable max for current servers */
#define CIFS_MAX_PASSWORD_LEN 512 /* Windows max seems to be 256 wide chars */
#define CIFS_MAX_SHARE_LEN 256 /* reasonable max share name length */
#define CIFS_NI_MAXHOST 1024 /* max host name length (256 * 4 bytes) */
#endif /* _CIFS_MOUNT_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment