Commit 0688d963 authored by Vitaly Lipatov's avatar Vitaly Lipatov

update 4.4 up to v4.4.113

parent ac75de6b
...@@ -152,6 +152,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -152,6 +152,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
list_for_each(tmp1, &cifs_tcp_ses_list) { list_for_each(tmp1, &cifs_tcp_ses_list) {
server = list_entry(tmp1, struct TCP_Server_Info, server = list_entry(tmp1, struct TCP_Server_Info,
tcp_ses_list); tcp_ses_list);
seq_printf(m, "\nNumber of credits: %d", server->credits);
i++; i++;
list_for_each(tmp2, &server->smb_ses_list) { list_for_each(tmp2, &server->smb_ses_list) {
ses = list_entry(tmp2, struct cifs_ses, ses = list_entry(tmp2, struct cifs_ses,
...@@ -255,7 +256,6 @@ static const struct file_operations cifs_debug_data_proc_fops = { ...@@ -255,7 +256,6 @@ static const struct file_operations cifs_debug_data_proc_fops = {
static ssize_t cifs_stats_proc_write(struct file *file, static ssize_t cifs_stats_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos) const char __user *buffer, size_t count, loff_t *ppos)
{ {
char c;
bool bv; bool bv;
int rc; int rc;
struct list_head *tmp1, *tmp2, *tmp3; struct list_head *tmp1, *tmp2, *tmp3;
...@@ -263,11 +263,8 @@ static ssize_t cifs_stats_proc_write(struct file *file, ...@@ -263,11 +263,8 @@ static ssize_t cifs_stats_proc_write(struct file *file,
struct cifs_ses *ses; struct cifs_ses *ses;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
rc = get_user(c, buffer); rc = kstrtobool_from_user(buffer, count, &bv);
if (rc) if (rc == 0) {
return rc;
if (strtobool(&c, &bv) == 0) {
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
atomic_set(&totBufAllocCount, 0); atomic_set(&totBufAllocCount, 0);
atomic_set(&totSmBufAllocCount, 0); atomic_set(&totSmBufAllocCount, 0);
...@@ -290,6 +287,8 @@ static ssize_t cifs_stats_proc_write(struct file *file, ...@@ -290,6 +287,8 @@ static ssize_t cifs_stats_proc_write(struct file *file,
} }
} }
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
} else {
return rc;
} }
return count; return count;
...@@ -433,17 +432,17 @@ static int cifsFYI_proc_open(struct inode *inode, struct file *file) ...@@ -433,17 +432,17 @@ static int cifsFYI_proc_open(struct inode *inode, struct file *file)
static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer, static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
char c; char c[2] = { '\0' };
bool bv; bool bv;
int rc; int rc;
rc = get_user(c, buffer); rc = get_user(c[0], buffer);
if (rc) if (rc)
return rc; return rc;
if (strtobool(&c, &bv) == 0) if (strtobool(c, &bv) == 0)
cifsFYI = bv; cifsFYI = bv;
else if ((c > '1') && (c <= '9')) else if ((c[0] > '1') && (c[0] <= '9'))
cifsFYI = (int) (c - '0'); /* see cifs_debug.h for meanings */ cifsFYI = (int) (c[0] - '0'); /* see cifs_debug.h for meanings */
return count; return count;
} }
...@@ -471,20 +470,12 @@ static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file) ...@@ -471,20 +470,12 @@ static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file)
static ssize_t cifs_linux_ext_proc_write(struct file *file, static ssize_t cifs_linux_ext_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos) const char __user *buffer, size_t count, loff_t *ppos)
{ {
char c;
bool bv;
int rc; int rc;
rc = get_user(c, buffer); rc = kstrtobool_from_user(buffer, count, &linuxExtEnabled);
if (rc) if (rc)
return rc; return rc;
rc = strtobool(&c, &bv);
if (rc)
return rc;
linuxExtEnabled = bv;
return count; return count;
} }
...@@ -511,20 +502,12 @@ static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file) ...@@ -511,20 +502,12 @@ static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file)
static ssize_t cifs_lookup_cache_proc_write(struct file *file, static ssize_t cifs_lookup_cache_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos) const char __user *buffer, size_t count, loff_t *ppos)
{ {
char c;
bool bv;
int rc; int rc;
rc = get_user(c, buffer); rc = kstrtobool_from_user(buffer, count, &lookupCacheEnabled);
if (rc) if (rc)
return rc; return rc;
rc = strtobool(&c, &bv);
if (rc)
return rc;
lookupCacheEnabled = bv;
return count; return count;
} }
...@@ -551,20 +534,12 @@ static int traceSMB_proc_open(struct inode *inode, struct file *file) ...@@ -551,20 +534,12 @@ static int traceSMB_proc_open(struct inode *inode, struct file *file)
static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer, static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
char c;
bool bv;
int rc; int rc;
rc = get_user(c, buffer); rc = kstrtobool_from_user(buffer, count, &traceSMB);
if (rc) if (rc)
return rc; return rc;
rc = strtobool(&c, &bv);
if (rc)
return rc;
traceSMB = bv;
return count; return count;
} }
...@@ -622,7 +597,6 @@ static ssize_t cifs_security_flags_proc_write(struct file *file, ...@@ -622,7 +597,6 @@ static ssize_t cifs_security_flags_proc_write(struct file *file,
int rc; int rc;
unsigned int flags; unsigned int flags;
char flags_string[12]; char flags_string[12];
char c;
bool bv; bool bv;
if ((count < 1) || (count > 11)) if ((count < 1) || (count > 11))
...@@ -635,11 +609,10 @@ static ssize_t cifs_security_flags_proc_write(struct file *file, ...@@ -635,11 +609,10 @@ static ssize_t cifs_security_flags_proc_write(struct file *file,
if (count < 3) { if (count < 3) {
/* single char or single char followed by null */ /* single char or single char followed by null */
c = flags_string[0]; if (strtobool(flags_string, &bv) == 0) {
if (strtobool(&c, &bv) == 0) {
global_secflags = bv ? CIFSSEC_MAX : CIFSSEC_DEF; global_secflags = bv ? CIFSSEC_MAX : CIFSSEC_DEF;
return count; return count;
} else if (!isdigit(c)) { } else if (!isdigit(flags_string[0])) {
cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", cifs_dbg(VFS, "Invalid SecurityFlags: %s\n",
flags_string); flags_string);
return -EINVAL; return -EINVAL;
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
void cifs_dump_mem(char *label, void *data, int length); void cifs_dump_mem(char *label, void *data, int length);
void cifs_dump_detail(void *); void cifs_dump_detail(void *);
void cifs_dump_mids(struct TCP_Server_Info *); void cifs_dump_mids(struct TCP_Server_Info *);
extern int traceSMB; /* flag which enables the function below */ extern bool traceSMB; /* flag which enables the function below */
void dump_smb(void *, int); void dump_smb(void *, int);
#define CIFS_INFO 0x01 #define CIFS_INFO 0x01
#define CIFS_RC 0x02 #define CIFS_RC 0x02
......
...@@ -46,6 +46,9 @@ ...@@ -46,6 +46,9 @@
#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */ #define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */
#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */ #define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */
#define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */ #define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */
#define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible
* root mountable
*/
struct cifs_sb_info { struct cifs_sb_info {
struct rb_root tlink_tree; struct rb_root tlink_tree;
...@@ -67,5 +70,6 @@ struct cifs_sb_info { ...@@ -67,5 +70,6 @@ struct cifs_sb_info {
struct backing_dev_info bdi; struct backing_dev_info bdi;
struct delayed_work prune_tlinks; struct delayed_work prune_tlinks;
struct rcu_head rcu; struct rcu_head rcu;
char *prepath;
}; };
#endif /* _CIFS_FS_SB_H */ #endif /* _CIFS_FS_SB_H */
...@@ -83,6 +83,9 @@ convert_sfm_char(const __u16 src_char, char *target) ...@@ -83,6 +83,9 @@ convert_sfm_char(const __u16 src_char, char *target)
case SFM_COLON: case SFM_COLON:
*target = ':'; *target = ':';
break; break;
case SFM_DOUBLEQUOTE:
*target = '"';
break;
case SFM_ASTERISK: case SFM_ASTERISK:
*target = '*'; *target = '*';
break; break;
...@@ -101,6 +104,12 @@ convert_sfm_char(const __u16 src_char, char *target) ...@@ -101,6 +104,12 @@ convert_sfm_char(const __u16 src_char, char *target)
case SFM_SLASH: case SFM_SLASH:
*target = '\\'; *target = '\\';
break; break;
case SFM_SPACE:
*target = ' ';
break;
case SFM_PERIOD:
*target = '.';
break;
default: default:
return false; return false;
} }
...@@ -404,7 +413,7 @@ static __le16 convert_to_sfu_char(char src_char) ...@@ -404,7 +413,7 @@ static __le16 convert_to_sfu_char(char src_char)
return dest_char; return dest_char;
} }
static __le16 convert_to_sfm_char(char src_char) static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
{ {
__le16 dest_char; __le16 dest_char;
...@@ -412,6 +421,9 @@ static __le16 convert_to_sfm_char(char src_char) ...@@ -412,6 +421,9 @@ static __le16 convert_to_sfm_char(char src_char)
case ':': case ':':
dest_char = cpu_to_le16(SFM_COLON); dest_char = cpu_to_le16(SFM_COLON);
break; break;
case '"':
dest_char = cpu_to_le16(SFM_DOUBLEQUOTE);
break;
case '*': case '*':
dest_char = cpu_to_le16(SFM_ASTERISK); dest_char = cpu_to_le16(SFM_ASTERISK);
break; break;
...@@ -427,6 +439,18 @@ static __le16 convert_to_sfm_char(char src_char) ...@@ -427,6 +439,18 @@ static __le16 convert_to_sfm_char(char src_char)
case '|': case '|':
dest_char = cpu_to_le16(SFM_PIPE); dest_char = cpu_to_le16(SFM_PIPE);
break; break;
case '.':
if (end_of_string)
dest_char = cpu_to_le16(SFM_PERIOD);
else
dest_char = 0;
break;
case ' ':
if (end_of_string)
dest_char = cpu_to_le16(SFM_SPACE);
else
dest_char = 0;
break;
default: default:
dest_char = 0; dest_char = 0;
} }
...@@ -469,9 +493,16 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen, ...@@ -469,9 +493,16 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
/* see if we must remap this char */ /* see if we must remap this char */
if (map_chars == SFU_MAP_UNI_RSVD) if (map_chars == SFU_MAP_UNI_RSVD)
dst_char = convert_to_sfu_char(src_char); dst_char = convert_to_sfu_char(src_char);
else if (map_chars == SFM_MAP_UNI_RSVD) else if (map_chars == SFM_MAP_UNI_RSVD) {
dst_char = convert_to_sfm_char(src_char); bool end_of_string;
else
if (i == srclen - 1)
end_of_string = true;
else
end_of_string = false;
dst_char = convert_to_sfm_char(src_char, end_of_string);
} else
dst_char = 0; dst_char = 0;
/* /*
* FIXME: We can not handle remapping backslash (UNI_SLASH) * FIXME: We can not handle remapping backslash (UNI_SLASH)
......
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
* not conflict (although almost does) with the mapping above. * not conflict (although almost does) with the mapping above.
*/ */
#define SFM_DOUBLEQUOTE ((__u16) 0xF020)
#define SFM_ASTERISK ((__u16) 0xF021) #define SFM_ASTERISK ((__u16) 0xF021)
#define SFM_QUESTION ((__u16) 0xF025) #define SFM_QUESTION ((__u16) 0xF025)
#define SFM_COLON ((__u16) 0xF022) #define SFM_COLON ((__u16) 0xF022)
...@@ -64,6 +65,8 @@ ...@@ -64,6 +65,8 @@
#define SFM_LESSTHAN ((__u16) 0xF023) #define SFM_LESSTHAN ((__u16) 0xF023)
#define SFM_PIPE ((__u16) 0xF027) #define SFM_PIPE ((__u16) 0xF027)
#define SFM_SLASH ((__u16) 0xF026) #define SFM_SLASH ((__u16) 0xF026)
#define SFM_SPACE ((__u16) 0xF028)
#define SFM_PERIOD ((__u16) 0xF029)
/* /*
* Mapping mechanism to use when one of the seven reserved characters is * Mapping mechanism to use when one of the seven reserved characters is
......
...@@ -731,24 +731,26 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) ...@@ -731,24 +731,26 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
memcpy(ses->auth_key.response + baselen, tiblob, tilen); memcpy(ses->auth_key.response + baselen, tiblob, tilen);
mutex_lock(&ses->server->srv_mutex);
rc = crypto_hmacmd5_alloc(ses->server); rc = crypto_hmacmd5_alloc(ses->server);
if (rc) { if (rc) {
cifs_dbg(VFS, "could not crypto alloc hmacmd5 rc %d\n", rc); cifs_dbg(VFS, "could not crypto alloc hmacmd5 rc %d\n", rc);
goto setup_ntlmv2_rsp_ret; goto unlock;
} }
/* calculate ntlmv2_hash */ /* calculate ntlmv2_hash */
rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp); rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp);
if (rc) { if (rc) {
cifs_dbg(VFS, "could not get v2 hash rc %d\n", rc); cifs_dbg(VFS, "could not get v2 hash rc %d\n", rc);
goto setup_ntlmv2_rsp_ret; goto unlock;
} }
/* calculate first part of the client response (CR1) */ /* calculate first part of the client response (CR1) */
rc = CalcNTLMv2_response(ses, ntlmv2_hash); rc = CalcNTLMv2_response(ses, ntlmv2_hash);
if (rc) { if (rc) {
cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", rc); cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", rc);
goto setup_ntlmv2_rsp_ret; goto unlock;
} }
/* now calculate the session key for NTLMv2 */ /* now calculate the session key for NTLMv2 */
...@@ -757,13 +759,13 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) ...@@ -757,13 +759,13 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n",
__func__); __func__);
goto setup_ntlmv2_rsp_ret; goto unlock;
} }
rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash); rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__); cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__);
goto setup_ntlmv2_rsp_ret; goto unlock;
} }
rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
...@@ -771,7 +773,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) ...@@ -771,7 +773,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
CIFS_HMAC_MD5_HASH_SIZE); CIFS_HMAC_MD5_HASH_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__);
goto setup_ntlmv2_rsp_ret; goto unlock;
} }
rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
...@@ -779,6 +781,8 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) ...@@ -779,6 +781,8 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
if (rc) if (rc)
cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
unlock:
mutex_unlock(&ses->server->srv_mutex);
setup_ntlmv2_rsp_ret: setup_ntlmv2_rsp_ret:
kfree(tiblob); kfree(tiblob);
......
...@@ -54,10 +54,10 @@ ...@@ -54,10 +54,10 @@
#endif #endif
int cifsFYI = 0; int cifsFYI = 0;
int traceSMB = 0; bool traceSMB;
bool enable_oplocks = true; bool enable_oplocks = true;
unsigned int linuxExtEnabled = 1; bool linuxExtEnabled = true;
unsigned int lookupCacheEnabled = 1; bool lookupCacheEnabled = true;
unsigned int global_secflags = CIFSSEC_DEF; unsigned int global_secflags = CIFSSEC_DEF;
/* unsigned int ntlmv2_support = 0; */ /* unsigned int ntlmv2_support = 0; */
unsigned int sign_CIFS_PDUs = 1; unsigned int sign_CIFS_PDUs = 1;
...@@ -268,7 +268,7 @@ cifs_alloc_inode(struct super_block *sb) ...@@ -268,7 +268,7 @@ cifs_alloc_inode(struct super_block *sb)
cifs_inode->createtime = 0; cifs_inode->createtime = 0;
cifs_inode->epoch = 0; cifs_inode->epoch = 0;
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE); generate_random_uuid(cifs_inode->lease_key);
#endif #endif
/* /*
* Can not set i_flags here - they get immediately overwritten to zero * Can not set i_flags here - they get immediately overwritten to zero
...@@ -689,6 +689,14 @@ cifs_do_mount(struct file_system_type *fs_type, ...@@ -689,6 +689,14 @@ cifs_do_mount(struct file_system_type *fs_type,
goto out_cifs_sb; goto out_cifs_sb;
} }
if (volume_info->prepath) {
cifs_sb->prepath = kstrdup(volume_info->prepath, GFP_KERNEL);
if (cifs_sb->prepath == NULL) {
root = ERR_PTR(-ENOMEM);
goto out_cifs_sb;
}
}
cifs_setup_cifs_sb(volume_info, cifs_sb); cifs_setup_cifs_sb(volume_info, cifs_sb);
rc = cifs_mount(cifs_sb, volume_info); rc = cifs_mount(cifs_sb, volume_info);
...@@ -727,7 +735,11 @@ cifs_do_mount(struct file_system_type *fs_type, ...@@ -727,7 +735,11 @@ cifs_do_mount(struct file_system_type *fs_type,
sb->s_flags |= MS_ACTIVE; sb->s_flags |= MS_ACTIVE;
} }
root = cifs_get_root(volume_info, sb); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
root = dget(sb->s_root);
else
root = cifs_get_root(volume_info, sb);
if (IS_ERR(root)) if (IS_ERR(root))
goto out_super; goto out_super;
...@@ -1201,7 +1213,6 @@ init_cifs(void) ...@@ -1201,7 +1213,6 @@ init_cifs(void)
GlobalTotalActiveXid = 0; GlobalTotalActiveXid = 0;
GlobalMaxActiveXid = 0; GlobalMaxActiveXid = 0;
spin_lock_init(&cifs_tcp_ses_lock); spin_lock_init(&cifs_tcp_ses_lock);
spin_lock_init(&cifs_file_list_lock);
spin_lock_init(&GlobalMid_Lock); spin_lock_init(&GlobalMid_Lock);
if (cifs_max_pending < 2) { if (cifs_max_pending < 2) {
......
...@@ -227,6 +227,7 @@ struct smb_version_operations { ...@@ -227,6 +227,7 @@ struct smb_version_operations {
/* verify the message */ /* verify the message */
int (*check_message)(char *, unsigned int); int (*check_message)(char *, unsigned int);
bool (*is_oplock_break)(char *, struct TCP_Server_Info *); bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
int (*handle_cancelled_mid)(char *, struct TCP_Server_Info *);
void (*downgrade_oplock)(struct TCP_Server_Info *, void (*downgrade_oplock)(struct TCP_Server_Info *,
struct cifsInodeInfo *, bool); struct cifsInodeInfo *, bool);
/* process transaction2 response */ /* process transaction2 response */
...@@ -350,6 +351,8 @@ struct smb_version_operations { ...@@ -350,6 +351,8 @@ struct smb_version_operations {
unsigned int (*calc_smb_size)(void *); unsigned int (*calc_smb_size)(void *);
/* check for STATUS_PENDING and process it in a positive case */ /* check for STATUS_PENDING and process it in a positive case */
bool (*is_status_pending)(char *, struct TCP_Server_Info *, int); bool (*is_status_pending)(char *, struct TCP_Server_Info *, int);
/* check for STATUS_NETWORK_SESSION_EXPIRED */
bool (*is_session_expired)(char *);
/* send oplock break response */ /* send oplock break response */
int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *, int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *,
struct cifsInodeInfo *); struct cifsInodeInfo *);
...@@ -627,6 +630,8 @@ struct TCP_Server_Info { ...@@ -627,6 +630,8 @@ struct TCP_Server_Info {
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
unsigned int max_read; unsigned int max_read;
unsigned int max_write; unsigned int max_write;
struct delayed_work reconnect; /* reconnect workqueue job */
struct mutex reconnect_mutex; /* prevent simultaneous reconnects */
#endif /* CONFIG_CIFS_SMB2 */ #endif /* CONFIG_CIFS_SMB2 */
}; };
...@@ -826,7 +831,9 @@ cap_unix(struct cifs_ses *ses) ...@@ -826,7 +831,9 @@ cap_unix(struct cifs_ses *ses)
struct cifs_tcon { struct cifs_tcon {
struct list_head tcon_list; struct list_head tcon_list;
int tc_count; int tc_count;
struct list_head rlist; /* reconnect list */
struct list_head openFileList; struct list_head openFileList;
spinlock_t open_file_lock; /* protects list above */
struct cifs_ses *ses; /* pointer to session associated with */ struct cifs_ses *ses; /* pointer to session associated with */
char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
char *nativeFileSystem; char *nativeFileSystem;
...@@ -883,7 +890,7 @@ struct cifs_tcon { ...@@ -883,7 +890,7 @@ struct cifs_tcon {
#endif /* CONFIG_CIFS_STATS2 */ #endif /* CONFIG_CIFS_STATS2 */
__u64 bytes_read; __u64 bytes_read;
__u64 bytes_written; __u64 bytes_written;
spinlock_t stat_lock; spinlock_t stat_lock; /* protects the two fields above */
#endif /* CONFIG_CIFS_STATS */ #endif /* CONFIG_CIFS_STATS */
FILE_SYSTEM_DEVICE_INFO fsDevInfo; FILE_SYSTEM_DEVICE_INFO fsDevInfo;
FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */
...@@ -902,7 +909,6 @@ struct cifs_tcon { ...@@ -902,7 +909,6 @@ struct cifs_tcon {
bool use_persistent:1; /* use persistent instead of durable handles */ bool use_persistent:1; /* use persistent instead of durable handles */
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
bool print:1; /* set if connection to printer share */ bool print:1; /* set if connection to printer share */
bool bad_network_name:1; /* set if ret status STATUS_BAD_NETWORK_NAME */
__le32 capabilities; __le32 capabilities;
__u32 share_flags; __u32 share_flags;
__u32 maximal_access; __u32 maximal_access;
...@@ -1035,8 +1041,10 @@ struct cifs_fid_locks { ...@@ -1035,8 +1041,10 @@ struct cifs_fid_locks {
}; };
struct cifsFileInfo { struct cifsFileInfo {
/* following two lists are protected by tcon->open_file_lock */
struct list_head tlist; /* pointer to next fid owned by tcon */ struct list_head tlist; /* pointer to next fid owned by tcon */
struct list_head flist; /* next fid (file instance) for this inode */ struct list_head flist; /* next fid (file instance) for this inode */
/* lock list below protected by cifsi->lock_sem */
struct cifs_fid_locks *llist; /* brlocks held by this fid */ struct cifs_fid_locks *llist; /* brlocks held by this fid */
kuid_t uid; /* allows finding which FileInfo structure */ kuid_t uid; /* allows finding which FileInfo structure */
__u32 pid; /* process id who opened file */ __u32 pid; /* process id who opened file */
...@@ -1044,11 +1052,12 @@ struct cifsFileInfo { ...@@ -1044,11 +1052,12 @@ struct cifsFileInfo {
/* BB add lock scope info here if needed */ ; /* BB add lock scope info here if needed */ ;
/* lock scope id (0 if none) */ /* lock scope id (0 if none) */
struct dentry *dentry; struct dentry *dentry;
unsigned int f_flags;
struct tcon_link *tlink; struct tcon_link *tlink;
unsigned int f_flags;
bool invalidHandle:1; /* file closed via session abend */ bool invalidHandle:1; /* file closed via session abend */
bool oplock_break_cancelled:1; bool oplock_break_cancelled:1;
int count; /* refcount protected by cifs_file_list_lock */ int count;
spinlock_t file_info_lock; /* protects four flag/count fields above */
struct mutex fh_mutex; /* prevents reopen race after dead ses*/ struct mutex fh_mutex; /* prevents reopen race after dead ses*/
struct cifs_search_info srch_inf; struct cifs_search_info srch_inf;
struct work_struct oplock_break; /* work for oplock breaks */ struct work_struct oplock_break; /* work for oplock breaks */
...@@ -1122,7 +1131,7 @@ struct cifs_writedata { ...@@ -1122,7 +1131,7 @@ struct cifs_writedata {
/* /*
* Take a reference on the file private data. Must be called with * Take a reference on the file private data. Must be called with
* cifs_file_list_lock held. * cfile->file_info_lock held.
*/ */
static inline void static inline void
cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
...@@ -1291,12 +1300,19 @@ struct mid_q_entry { ...@@ -1291,12 +1300,19 @@ struct mid_q_entry {
void *callback_data; /* general purpose pointer for callback */ void *callback_data; /* general purpose pointer for callback */
void *resp_buf; /* pointer to received SMB header */ void *resp_buf; /* pointer to received SMB header */
int mid_state; /* wish this were enum but can not pass to wait_event */ int mid_state; /* wish this were enum but can not pass to wait_event */
unsigned int mid_flags;
__le16 command; /* smb command code */ __le16 command; /* smb command code */
bool large_buf:1; /* if valid response, is pointer to large buf */ bool large_buf:1; /* if valid response, is pointer to large buf */
bool multiRsp:1; /* multiple trans2 responses for one request */ bool multiRsp:1; /* multiple trans2 responses for one request */
bool multiEnd:1; /* both received */ bool multiEnd:1; /* both received */
}; };
struct close_cancelled_open {
struct cifs_fid fid;
struct cifs_tcon *tcon;
struct work_struct work;
};
/* Make code in transport.c a little cleaner by moving /* Make code in transport.c a little cleaner by moving
update of optional stats into function below */ update of optional stats into function below */
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
...@@ -1428,6 +1444,9 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param, ...@@ -1428,6 +1444,9 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param,
#define MID_RESPONSE_MALFORMED 0x10 #define MID_RESPONSE_MALFORMED 0x10
#define MID_SHUTDOWN 0x20 #define MID_SHUTDOWN 0x20
/* Flags */
#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */
/* Types of response buffer returned from SendReceive2 */ /* Types of response buffer returned from SendReceive2 */
#define CIFS_NO_BUFFER 0 /* Response buffer not returned */ #define CIFS_NO_BUFFER 0 /* Response buffer not returned */
#define CIFS_SMALL_BUFFER 1 #define CIFS_SMALL_BUFFER 1
...@@ -1516,8 +1535,10 @@ require use of the stronger protocol */ ...@@ -1516,8 +1535,10 @@ require use of the stronger protocol */
* GlobalMid_Lock protects: * GlobalMid_Lock protects:
* list operations on pending_mid_q and oplockQ * list operations on pending_mid_q and oplockQ
* updates to XID counters, multiplex id and SMB sequence numbers * updates to XID counters, multiplex id and SMB sequence numbers
* cifs_file_list_lock protects: * tcp_ses_lock protects:
* list operations on tcp and SMB session lists and tCon lists * list operations on tcp and SMB session lists
* tcon->open_file_lock protects the list of open files hanging off the tcon
* cfile->file_info_lock protects counters and fields in cifs file struct
* f_owner.lock protects certain per file struct operations * f_owner.lock protects certain per file struct operations
* mapping->page_lock protects certain per page operations * mapping->page_lock protects certain per page operations
* *
...@@ -1549,18 +1570,12 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list; ...@@ -1549,18 +1570,12 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list;
* tcp session, and the list of tcon's per smb session. It also protects * tcp session, and the list of tcon's per smb session. It also protects
* the reference counters for the server, smb session, and tcon. Finally, * the reference counters for the server, smb session, and tcon. Finally,
* changes to the tcon->tidStatus should be done while holding this lock. * changes to the tcon->tidStatus should be done while holding this lock.
* generally the locks should be taken in order tcp_ses_lock before
* tcon->open_file_lock and that before file->file_info_lock since the
* structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file
*/ */
GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock; GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock;
/*
* This lock protects the cifs_file->llist and cifs_file->flist
* list operations, and updates to some flags (cifs_file->invalidHandle)
* It will be moved to either use the tcon->stat_lock or equivalent later.
* If cifs_tcp_ses_lock and the lock below are both needed to be held, then
* the cifs_tcp_ses_lock must be grabbed first and released last.
*/
GLOBAL_EXTERN spinlock_t cifs_file_list_lock;
#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */ #ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */
/* Outstanding dir notify requests */ /* Outstanding dir notify requests */
GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
...@@ -1596,11 +1611,11 @@ GLOBAL_EXTERN atomic_t midCount; ...@@ -1596,11 +1611,11 @@ GLOBAL_EXTERN atomic_t midCount;
/* Misc globals */ /* Misc globals */
GLOBAL_EXTERN bool enable_oplocks; /* enable or disable oplocks */ GLOBAL_EXTERN bool enable_oplocks; /* enable or disable oplocks */
GLOBAL_EXTERN unsigned int lookupCacheEnabled; GLOBAL_EXTERN bool lookupCacheEnabled;
GLOBAL_EXTERN unsigned int global_secflags; /* if on, session setup sent GLOBAL_EXTERN unsigned int global_secflags; /* if on, session setup sent
with more secure ntlmssp2 challenge/resp */ with more secure ntlmssp2 challenge/resp */
GLOBAL_EXTERN unsigned int sign_CIFS_PDUs; /* enable smb packet signing */ GLOBAL_EXTERN unsigned int sign_CIFS_PDUs; /* enable smb packet signing */
GLOBAL_EXTERN unsigned int linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/ GLOBAL_EXTERN bool linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/
GLOBAL_EXTERN unsigned int CIFSMaxBufSize; /* max size not including hdr */ GLOBAL_EXTERN unsigned int CIFSMaxBufSize; /* max size not including hdr */
GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */ GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */
GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */ GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */
......
...@@ -205,6 +205,9 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid, ...@@ -205,6 +205,9 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
struct tcon_link *tlink, struct tcon_link *tlink,
struct cifs_pending_open *open); struct cifs_pending_open *open);
extern void cifs_del_pending_open(struct cifs_pending_open *open); extern void cifs_del_pending_open(struct cifs_pending_open *open);
extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
int from_reconnect);
extern void cifs_put_tcon(struct cifs_tcon *tcon);
#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) #if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
extern void cifs_dfs_release_automount_timer(void); extern void cifs_dfs_release_automount_timer(void);
......
...@@ -98,13 +98,13 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) ...@@ -98,13 +98,13 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
struct list_head *tmp1; struct list_head *tmp1;
/* list all files open on tree connection and mark them invalid */ /* list all files open on tree connection and mark them invalid */
spin_lock(&cifs_file_list_lock); spin_lock(&tcon->open_file_lock);
list_for_each_safe(tmp, tmp1, &tcon->openFileList) { list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
open_file = list_entry(tmp, struct cifsFileInfo, tlist); open_file = list_entry(tmp, struct cifsFileInfo, tlist);
open_file->invalidHandle = true; open_file->invalidHandle = true;
open_file->oplock_break_cancelled = true; open_file->oplock_break_cancelled = true;
} }
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
/* /*
* BB Add call to invalidate_inodes(sb) for all superblocks mounted * BB Add call to invalidate_inodes(sb) for all superblocks mounted
* to this tcon. * to this tcon.
...@@ -717,6 +717,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server) ...@@ -717,6 +717,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
if (rc) if (rc)
return rc; return rc;
if (server->capabilities & CAP_UNICODE)
smb->hdr.Flags2 |= SMBFLG2_UNICODE;
/* set up echo request */ /* set up echo request */
smb->hdr.Tid = 0xffff; smb->hdr.Tid = 0xffff;
smb->hdr.WordCount = 1; smb->hdr.WordCount = 1;
...@@ -1431,6 +1434,8 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1431,6 +1434,8 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
length = discard_remaining_data(server); length = discard_remaining_data(server);
dequeue_mid(mid, rdata->result); dequeue_mid(mid, rdata->result);
mid->resp_buf = server->smallbuf;
server->smallbuf = NULL;
return length; return length;
} }
...@@ -1462,6 +1467,13 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1462,6 +1467,13 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return length; return length;
server->total_read += length; server->total_read += length;
if (server->ops->is_session_expired &&
server->ops->is_session_expired(buf)) {
cifs_reconnect(server);
wake_up(&server->response_q);
return -1;
}
if (server->ops->is_status_pending && if (server->ops->is_status_pending &&
server->ops->is_status_pending(buf, server, 0)) { server->ops->is_status_pending(buf, server, 0)) {
discard_remaining_data(server); discard_remaining_data(server);
...@@ -1545,6 +1557,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1545,6 +1557,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return cifs_readv_discard(server, mid); return cifs_readv_discard(server, mid);
dequeue_mid(mid, false); dequeue_mid(mid, false);
mid->resp_buf = server->smallbuf;
server->smallbuf = NULL;
return length; return length;
} }
......
...@@ -52,6 +52,9 @@ ...@@ -52,6 +52,9 @@
#include "nterr.h" #include "nterr.h"
#include "rfc1002pdu.h" #include "rfc1002pdu.h"
#include "fscache.h" #include "fscache.h"
#ifdef CONFIG_CIFS_SMB2
#include "smb2proto.h"
#endif
#define CIFS_PORT 445 #define CIFS_PORT 445
#define RFC1001_PORT 139 #define RFC1001_PORT 139
...@@ -410,6 +413,9 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -410,6 +413,9 @@ cifs_reconnect(struct TCP_Server_Info *server)
} }
} while (server->tcpStatus == CifsNeedReconnect); } while (server->tcpStatus == CifsNeedReconnect);
if (server->tcpStatus == CifsNeedNegotiate)
mod_delayed_work(cifsiod_wq, &server->echo, 0);
return rc; return rc;
} }
...@@ -419,16 +425,27 @@ cifs_echo_request(struct work_struct *work) ...@@ -419,16 +425,27 @@ cifs_echo_request(struct work_struct *work)
int rc; int rc;
struct TCP_Server_Info *server = container_of(work, struct TCP_Server_Info *server = container_of(work,
struct TCP_Server_Info, echo.work); struct TCP_Server_Info, echo.work);
unsigned long echo_interval;
/*
* If we need to renegotiate, set echo interval to zero to
* immediately call echo service where we can renegotiate.
*/
if (server->tcpStatus == CifsNeedNegotiate)
echo_interval = 0;
else
echo_interval = SMB_ECHO_INTERVAL;
/* /*
* We cannot send an echo if it is disabled or until the * We cannot send an echo if it is disabled.
* NEGOTIATE_PROTOCOL request is done, which is indicated by * Also, no need to ping if we got a response recently.
* server->ops->need_neg() == true. Also, no need to ping if
* we got a response recently.
*/ */
if (!server->ops->need_neg || server->ops->need_neg(server) ||
if (server->tcpStatus == CifsNeedReconnect ||
server->tcpStatus == CifsExiting ||
server->tcpStatus == CifsNew ||
(server->ops->can_echo && !server->ops->can_echo(server)) || (server->ops->can_echo && !server->ops->can_echo(server)) ||
time_before(jiffies, server->lstrp + SMB_ECHO_INTERVAL - HZ)) time_before(jiffies, server->lstrp + echo_interval - HZ))
goto requeue_echo; goto requeue_echo;
rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS; rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS;
...@@ -834,6 +851,13 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -834,6 +851,13 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
cifs_dump_mem("Bad SMB: ", buf, cifs_dump_mem("Bad SMB: ", buf,
min_t(unsigned int, server->total_read, 48)); min_t(unsigned int, server->total_read, 48));
if (server->ops->is_session_expired &&
server->ops->is_session_expired(buf)) {
cifs_reconnect(server);
wake_up(&server->response_q);
return -1;
}
if (server->ops->is_status_pending && if (server->ops->is_status_pending &&
server->ops->is_status_pending(buf, server, length)) server->ops->is_status_pending(buf, server, length))
return -1; return -1;
...@@ -920,10 +944,19 @@ cifs_demultiplex_thread(void *p) ...@@ -920,10 +944,19 @@ cifs_demultiplex_thread(void *p)
server->lstrp = jiffies; server->lstrp = jiffies;
if (mid_entry != NULL) { if (mid_entry != NULL) {
if ((mid_entry->mid_flags & MID_WAIT_CANCELLED) &&
mid_entry->mid_state == MID_RESPONSE_RECEIVED &&
server->ops->handle_cancelled_mid)
server->ops->handle_cancelled_mid(
mid_entry->resp_buf,
server);
if (!mid_entry->multiRsp || mid_entry->multiEnd) if (!mid_entry->multiRsp || mid_entry->multiEnd)
mid_entry->callback(mid_entry); mid_entry->callback(mid_entry);
} else if (!server->ops->is_oplock_break || } else if (server->ops->is_oplock_break &&
!server->ops->is_oplock_break(buf, server)) { server->ops->is_oplock_break(buf, server)) {
cifs_dbg(FYI, "Received oplock break\n");
} else {
cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n", cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n",
atomic_read(&midCount)); atomic_read(&midCount));
cifs_dump_mem("Received Data is: ", buf, cifs_dump_mem("Received Data is: ", buf,
...@@ -2116,8 +2149,8 @@ cifs_find_tcp_session(struct smb_vol *vol) ...@@ -2116,8 +2149,8 @@ cifs_find_tcp_session(struct smb_vol *vol)
return NULL; return NULL;
} }
static void void
cifs_put_tcp_session(struct TCP_Server_Info *server) cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
{ {
struct task_struct *task; struct task_struct *task;
...@@ -2134,6 +2167,19 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) ...@@ -2134,6 +2167,19 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
cancel_delayed_work_sync(&server->echo); cancel_delayed_work_sync(&server->echo);
#ifdef CONFIG_CIFS_SMB2
if (from_reconnect)
/*
* Avoid deadlock here: reconnect work calls
* cifs_put_tcp_session() at its end. Need to be sure
* that reconnect work does nothing with server pointer after
* that step.
*/
cancel_delayed_work(&server->reconnect);
else
cancel_delayed_work_sync(&server->reconnect);
#endif
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting; server->tcpStatus = CifsExiting;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
...@@ -2198,12 +2244,16 @@ cifs_get_tcp_session(struct smb_vol *volume_info) ...@@ -2198,12 +2244,16 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
INIT_LIST_HEAD(&tcp_ses->smb_ses_list); INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request); INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
#ifdef CONFIG_CIFS_SMB2
INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
mutex_init(&tcp_ses->reconnect_mutex);
#endif
memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr, memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
sizeof(tcp_ses->srcaddr)); sizeof(tcp_ses->srcaddr));
memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr, memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
sizeof(tcp_ses->dstaddr)); sizeof(tcp_ses->dstaddr));
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
get_random_bytes(tcp_ses->client_guid, SMB2_CLIENT_GUID_SIZE); generate_random_uuid(tcp_ses->client_guid);
#endif #endif
/* /*
* at this point we are the only ones with the pointer * at this point we are the only ones with the pointer
...@@ -2350,7 +2400,7 @@ cifs_put_smb_ses(struct cifs_ses *ses) ...@@ -2350,7 +2400,7 @@ cifs_put_smb_ses(struct cifs_ses *ses)
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
sesInfoFree(ses); sesInfoFree(ses);
cifs_put_tcp_session(server); cifs_put_tcp_session(server, 0);
} }
#ifdef CONFIG_KEYS #ifdef CONFIG_KEYS
...@@ -2524,7 +2574,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) ...@@ -2524,7 +2574,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
mutex_unlock(&ses->session_mutex); mutex_unlock(&ses->session_mutex);
/* existing SMB ses has a server reference already */ /* existing SMB ses has a server reference already */
cifs_put_tcp_session(server); cifs_put_tcp_session(server, 0);
free_xid(xid); free_xid(xid);
return ses; return ses;
} }
...@@ -2614,7 +2664,7 @@ cifs_find_tcon(struct cifs_ses *ses, const char *unc) ...@@ -2614,7 +2664,7 @@ cifs_find_tcon(struct cifs_ses *ses, const char *unc)
return NULL; return NULL;
} }
static void void
cifs_put_tcon(struct cifs_tcon *tcon) cifs_put_tcon(struct cifs_tcon *tcon)
{ {
unsigned int xid; unsigned int xid;
...@@ -3520,6 +3570,44 @@ cifs_get_volume_info(char *mount_data, const char *devname) ...@@ -3520,6 +3570,44 @@ cifs_get_volume_info(char *mount_data, const char *devname)
return volume_info; return volume_info;
} }
static int
cifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
unsigned int xid,
struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
char *full_path)
{
int rc;
char *s;
char sep, tmp;
sep = CIFS_DIR_SEP(cifs_sb);
s = full_path;
rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, "");
while (rc == 0) {
/* skip separators */
while (*s == sep)
s++;
if (!*s)
break;
/* next separator */
while (*s && *s != sep)
s++;
/*
* temporarily null-terminate the path at the end of
* the current component
*/
tmp = *s;
*s = 0;
rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
full_path);
*s = tmp;
}
return rc;
}
int int
cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info) cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
{ {
...@@ -3657,6 +3745,18 @@ remote_path_check: ...@@ -3657,6 +3745,18 @@ remote_path_check:
kfree(full_path); kfree(full_path);
goto mount_fail_check; goto mount_fail_check;
} }
if (rc != -EREMOTE) {
rc = cifs_are_all_path_components_accessible(server,
xid, tcon, cifs_sb,
full_path);
if (rc != 0) {
cifs_dbg(VFS, "cannot query dirs between root and final path, "
"enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
rc = 0;
}
}
kfree(full_path); kfree(full_path);
} }
...@@ -3720,7 +3820,7 @@ mount_fail_check: ...@@ -3720,7 +3820,7 @@ mount_fail_check:
else if (ses) else if (ses)
cifs_put_smb_ses(ses); cifs_put_smb_ses(ses);
else else
cifs_put_tcp_session(server); cifs_put_tcp_session(server, 0);
bdi_destroy(&cifs_sb->bdi); bdi_destroy(&cifs_sb->bdi);
} }
...@@ -3926,6 +4026,7 @@ cifs_umount(struct cifs_sb_info *cifs_sb) ...@@ -3926,6 +4026,7 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
bdi_destroy(&cifs_sb->bdi); bdi_destroy(&cifs_sb->bdi);
kfree(cifs_sb->mountdata); kfree(cifs_sb->mountdata);
kfree(cifs_sb->prepath);
call_rcu(&cifs_sb->rcu, delayed_free); call_rcu(&cifs_sb->rcu, delayed_free);
} }
...@@ -3971,6 +4072,14 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, ...@@ -3971,6 +4072,14 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n", cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
server->sec_mode, server->capabilities, server->timeAdj); server->sec_mode, server->capabilities, server->timeAdj);
if (ses->auth_key.response) {
cifs_dbg(VFS, "Free previous auth_key.response = %p\n",
ses->auth_key.response);
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
ses->auth_key.len = 0;
}
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);
...@@ -4030,7 +4139,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) ...@@ -4030,7 +4139,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info); ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info);
if (IS_ERR(ses)) { if (IS_ERR(ses)) {
tcon = (struct cifs_tcon *)ses; tcon = (struct cifs_tcon *)ses;
cifs_put_tcp_session(master_tcon->ses->server); cifs_put_tcp_session(master_tcon->ses->server, 0);
goto out; goto out;
} }
......
...@@ -84,6 +84,7 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -84,6 +84,7 @@ build_path_from_dentry(struct dentry *direntry)
struct dentry *temp; struct dentry *temp;
int namelen; int namelen;
int dfsplen; int dfsplen;
int pplen = 0;
char *full_path; char *full_path;
char dirsep; char dirsep;
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
...@@ -95,8 +96,12 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -95,8 +96,12 @@ build_path_from_dentry(struct dentry *direntry)
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1); dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
else else
dfsplen = 0; dfsplen = 0;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0;
cifs_bp_rename_retry: cifs_bp_rename_retry:
namelen = dfsplen; namelen = dfsplen + pplen;
seq = read_seqbegin(&rename_lock); seq = read_seqbegin(&rename_lock);
rcu_read_lock(); rcu_read_lock();
for (temp = direntry; !IS_ROOT(temp);) { for (temp = direntry; !IS_ROOT(temp);) {
...@@ -137,7 +142,7 @@ cifs_bp_rename_retry: ...@@ -137,7 +142,7 @@ cifs_bp_rename_retry:
} }
} }
rcu_read_unlock(); rcu_read_unlock();
if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) { if (namelen != dfsplen + pplen || read_seqretry(&rename_lock, seq)) {
cifs_dbg(FYI, "did not end path lookup where expected. namelen=%ddfsplen=%d\n", cifs_dbg(FYI, "did not end path lookup where expected. namelen=%ddfsplen=%d\n",
namelen, dfsplen); namelen, dfsplen);
/* presumably this is only possible if racing with a rename /* presumably this is only possible if racing with a rename
...@@ -153,6 +158,17 @@ cifs_bp_rename_retry: ...@@ -153,6 +158,17 @@ cifs_bp_rename_retry:
those safely to '/' if any are found in the middle of the prepath */ those safely to '/' if any are found in the middle of the prepath */
/* BB test paths to Windows with '/' in the midst of prepath */ /* BB test paths to Windows with '/' in the midst of prepath */
if (pplen) {
int i;
cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath);
memcpy(full_path+dfsplen+1, cifs_sb->prepath, pplen-1);
full_path[dfsplen] = '\\';
for (i = 0; i < pplen-1; i++)
if (full_path[dfsplen+1+i] == '/')
full_path[dfsplen+1+i] = CIFS_DIR_SEP(cifs_sb);
}
if (dfsplen) { if (dfsplen) {
strncpy(full_path, tcon->treeName, dfsplen); strncpy(full_path, tcon->treeName, dfsplen);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
...@@ -167,15 +183,21 @@ cifs_bp_rename_retry: ...@@ -167,15 +183,21 @@ cifs_bp_rename_retry:
} }
/* /*
* Don't allow path components longer than the server max.
* Don't allow the separator character in a path component. * Don't allow the separator character in a path component.
* The VFS will not allow "/", but "\" is allowed by posix. * The VFS will not allow "/", but "\" is allowed by posix.
*/ */
static int static int
check_name(struct dentry *direntry) check_name(struct dentry *direntry, struct cifs_tcon *tcon)
{ {
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
int i; int i;
if (unlikely(tcon->fsAttrInfo.MaxPathNameComponentLength &&
direntry->d_name.len >
le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength)))
return -ENAMETOOLONG;
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) { if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
for (i = 0; i < direntry->d_name.len; i++) { for (i = 0; i < direntry->d_name.len; i++) {
if (direntry->d_name.name[i] == '\\') { if (direntry->d_name.name[i] == '\\') {
...@@ -231,6 +253,13 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, ...@@ -231,6 +253,13 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
goto cifs_create_get_file_info; goto cifs_create_get_file_info;
} }
if (S_ISDIR(newinode->i_mode)) {
CIFSSMBClose(xid, tcon, fid->netfid);
iput(newinode);
rc = -EISDIR;
goto out;
}
if (!S_ISREG(newinode->i_mode)) { if (!S_ISREG(newinode->i_mode)) {
/* /*
* The server may allow us to open things like * The server may allow us to open things like
...@@ -404,10 +433,14 @@ cifs_create_set_dentry: ...@@ -404,10 +433,14 @@ cifs_create_set_dentry:
if (rc != 0) { if (rc != 0) {
cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n", cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n",
rc); rc);
if (server->ops->close) goto out_err;
server->ops->close(xid, tcon, fid);
goto out;
} }
if (S_ISDIR(newinode->i_mode)) {
rc = -EISDIR;
goto out_err;
}
d_drop(direntry); d_drop(direntry);
d_add(direntry, newinode); d_add(direntry, newinode);
...@@ -415,6 +448,13 @@ out: ...@@ -415,6 +448,13 @@ out:
kfree(buf); kfree(buf);
kfree(full_path); kfree(full_path);
return rc; return rc;
out_err:
if (server->ops->close)
server->ops->close(xid, tcon, fid);
if (newinode)
iput(newinode);
goto out;
} }
int int
...@@ -460,10 +500,6 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, ...@@ -460,10 +500,6 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
return finish_no_open(file, res); return finish_no_open(file, res);
} }
rc = check_name(direntry);
if (rc)
return rc;
xid = get_xid(); xid = get_xid();
cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n", cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
...@@ -476,6 +512,11 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, ...@@ -476,6 +512,11 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
} }
tcon = tlink_tcon(tlink); tcon = tlink_tcon(tlink);
rc = check_name(direntry, tcon);
if (rc)
goto out;
server = tcon->ses->server; server = tcon->ses->server;
if (server->ops->new_lease_key) if (server->ops->new_lease_key)
...@@ -736,7 +777,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -736,7 +777,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
} }
pTcon = tlink_tcon(tlink); pTcon = tlink_tcon(tlink);
rc = check_name(direntry); rc = check_name(direntry, pTcon);
if (rc) if (rc)
goto lookup_out; goto lookup_out;
......
...@@ -226,6 +226,13 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, ...@@ -226,6 +226,13 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
if (backup_cred(cifs_sb)) if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT; create_options |= CREATE_OPEN_BACKUP_INTENT;
/* O_SYNC also has bit for O_DSYNC so following check picks up either */
if (f_flags & O_SYNC)
create_options |= CREATE_WRITE_THROUGH;
if (f_flags & O_DIRECT)
create_options |= CREATE_NO_BUFFER;
oparms.tcon = tcon; oparms.tcon = tcon;
oparms.cifs_sb = cifs_sb; oparms.cifs_sb = cifs_sb;
oparms.desired_access = desired_access; oparms.desired_access = desired_access;
...@@ -308,6 +315,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, ...@@ -308,6 +315,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
cfile->tlink = cifs_get_tlink(tlink); cfile->tlink = cifs_get_tlink(tlink);
INIT_WORK(&cfile->oplock_break, cifs_oplock_break); INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
mutex_init(&cfile->fh_mutex); mutex_init(&cfile->fh_mutex);
spin_lock_init(&cfile->file_info_lock);
cifs_sb_active(inode->i_sb); cifs_sb_active(inode->i_sb);
...@@ -320,7 +328,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, ...@@ -320,7 +328,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
oplock = 0; oplock = 0;
} }
spin_lock(&cifs_file_list_lock); spin_lock(&tcon->open_file_lock);
if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock)
oplock = fid->pending_open->oplock; oplock = fid->pending_open->oplock;
list_del(&fid->pending_open->olist); list_del(&fid->pending_open->olist);
...@@ -329,12 +337,13 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, ...@@ -329,12 +337,13 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
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);
/* if readable file instance put first in list*/ /* if readable file instance put first in list*/
if (file->f_mode & FMODE_READ) if (file->f_mode & FMODE_READ)
list_add(&cfile->flist, &cinode->openFileList); list_add(&cfile->flist, &cinode->openFileList);
else else
list_add_tail(&cfile->flist, &cinode->openFileList); list_add_tail(&cfile->flist, &cinode->openFileList);
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
if (fid->purge_cache) if (fid->purge_cache)
cifs_zap_mapping(inode); cifs_zap_mapping(inode);
...@@ -346,16 +355,16 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, ...@@ -346,16 +355,16 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
struct cifsFileInfo * struct cifsFileInfo *
cifsFileInfo_get(struct cifsFileInfo *cifs_file) cifsFileInfo_get(struct cifsFileInfo *cifs_file)
{ {
spin_lock(&cifs_file_list_lock); spin_lock(&cifs_file->file_info_lock);
cifsFileInfo_get_locked(cifs_file); cifsFileInfo_get_locked(cifs_file);
spin_unlock(&cifs_file_list_lock); spin_unlock(&cifs_file->file_info_lock);
return cifs_file; return cifs_file;
} }
/* /*
* Release a reference on the file private data. This may involve closing * Release a reference on the file private data. This may involve closing
* the filehandle out on the server. Must be called without holding * the filehandle out on the server. Must be called without holding
* cifs_file_list_lock. * tcon->open_file_lock and cifs_file->file_info_lock.
*/ */
void cifsFileInfo_put(struct cifsFileInfo *cifs_file) void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
{ {
...@@ -370,11 +379,15 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) ...@@ -370,11 +379,15 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
struct cifs_pending_open open; struct cifs_pending_open open;
bool oplock_break_cancelled; bool oplock_break_cancelled;
spin_lock(&cifs_file_list_lock); spin_lock(&tcon->open_file_lock);
spin_lock(&cifs_file->file_info_lock);
if (--cifs_file->count > 0) { if (--cifs_file->count > 0) {
spin_unlock(&cifs_file_list_lock); spin_unlock(&cifs_file->file_info_lock);
spin_unlock(&tcon->open_file_lock);
return; return;
} }
spin_unlock(&cifs_file->file_info_lock);
if (server->ops->get_lease_key) if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &fid); server->ops->get_lease_key(inode, &fid);
...@@ -398,7 +411,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) ...@@ -398,7 +411,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags); set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags);
cifs_set_oplock_level(cifsi, 0); cifs_set_oplock_level(cifsi, 0);
} }
spin_unlock(&cifs_file_list_lock);
spin_unlock(&tcon->open_file_lock);
oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break); oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break);
...@@ -780,10 +794,10 @@ int cifs_closedir(struct inode *inode, struct file *file) ...@@ -780,10 +794,10 @@ int cifs_closedir(struct inode *inode, struct file *file)
server = tcon->ses->server; server = tcon->ses->server;
cifs_dbg(FYI, "Freeing private data in close dir\n"); cifs_dbg(FYI, "Freeing private data in close dir\n");
spin_lock(&cifs_file_list_lock); spin_lock(&cfile->file_info_lock);
if (server->ops->dir_needs_close(cfile)) { if (server->ops->dir_needs_close(cfile)) {
cfile->invalidHandle = true; cfile->invalidHandle = true;
spin_unlock(&cifs_file_list_lock); spin_unlock(&cfile->file_info_lock);
if (server->ops->close_dir) if (server->ops->close_dir)
rc = server->ops->close_dir(xid, tcon, &cfile->fid); rc = server->ops->close_dir(xid, tcon, &cfile->fid);
else else
...@@ -792,7 +806,7 @@ int cifs_closedir(struct inode *inode, struct file *file) ...@@ -792,7 +806,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
/* not much we can do if it fails anyway, ignore rc */ /* not much we can do if it fails anyway, ignore rc */
rc = 0; rc = 0;
} else } else
spin_unlock(&cifs_file_list_lock); spin_unlock(&cfile->file_info_lock);
buf = cfile->srch_inf.ntwrk_buf_start; buf = cfile->srch_inf.ntwrk_buf_start;
if (buf) { if (buf) {
...@@ -1728,12 +1742,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, ...@@ -1728,12 +1742,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
{ {
struct cifsFileInfo *open_file = NULL; struct cifsFileInfo *open_file = NULL;
struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
/* only filter by fsuid on multiuser mounts */ /* only filter by fsuid on multiuser mounts */
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
fsuid_only = false; fsuid_only = false;
spin_lock(&cifs_file_list_lock); spin_lock(&tcon->open_file_lock);
/* we could simply get the first_list_entry since write-only entries /* we could simply get the first_list_entry since write-only entries
are always at the end of the list but since the first entry might are always at the end of the list but since the first entry might
have a close pending, we go through the whole list */ have a close pending, we go through the whole list */
...@@ -1744,8 +1759,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, ...@@ -1744,8 +1759,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
if (!open_file->invalidHandle) { if (!open_file->invalidHandle) {
/* found a good file */ /* found a good file */
/* lock it so it will not be closed on us */ /* lock it so it will not be closed on us */
cifsFileInfo_get_locked(open_file); cifsFileInfo_get(open_file);
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
return open_file; return open_file;
} /* else might as well continue, and look for } /* else might as well continue, and look for
another, or simply have the caller reopen it another, or simply have the caller reopen it
...@@ -1753,7 +1768,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, ...@@ -1753,7 +1768,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
} else /* write only file */ } else /* write only file */
break; /* write only files are last so must be done */ break; /* write only files are last so must be done */
} }
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
return NULL; return NULL;
} }
...@@ -1762,6 +1777,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, ...@@ -1762,6 +1777,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
{ {
struct cifsFileInfo *open_file, *inv_file = NULL; struct cifsFileInfo *open_file, *inv_file = NULL;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifs_tcon *tcon;
bool any_available = false; bool any_available = false;
int rc; int rc;
unsigned int refind = 0; unsigned int refind = 0;
...@@ -1777,15 +1793,16 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, ...@@ -1777,15 +1793,16 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
} }
cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
tcon = cifs_sb_master_tcon(cifs_sb);
/* only filter by fsuid on multiuser mounts */ /* only filter by fsuid on multiuser mounts */
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
fsuid_only = false; fsuid_only = false;
spin_lock(&cifs_file_list_lock); spin_lock(&tcon->open_file_lock);
refind_writable: refind_writable:
if (refind > MAX_REOPEN_ATT) { if (refind > MAX_REOPEN_ATT) {
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
return NULL; return NULL;
} }
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
...@@ -1796,8 +1813,8 @@ refind_writable: ...@@ -1796,8 +1813,8 @@ refind_writable:
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
if (!open_file->invalidHandle) { if (!open_file->invalidHandle) {
/* found a good writable file */ /* found a good writable file */
cifsFileInfo_get_locked(open_file); cifsFileInfo_get(open_file);
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
return open_file; return open_file;
} else { } else {
if (!inv_file) if (!inv_file)
...@@ -1813,24 +1830,24 @@ refind_writable: ...@@ -1813,24 +1830,24 @@ refind_writable:
if (inv_file) { if (inv_file) {
any_available = false; any_available = false;
cifsFileInfo_get_locked(inv_file); cifsFileInfo_get(inv_file);
} }
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
if (inv_file) { if (inv_file) {
rc = cifs_reopen_file(inv_file, false); rc = cifs_reopen_file(inv_file, false);
if (!rc) if (!rc)
return inv_file; return inv_file;
else { else {
spin_lock(&cifs_file_list_lock); spin_lock(&tcon->open_file_lock);
list_move_tail(&inv_file->flist, list_move_tail(&inv_file->flist,
&cifs_inode->openFileList); &cifs_inode->openFileList);
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
cifsFileInfo_put(inv_file); cifsFileInfo_put(inv_file);
spin_lock(&cifs_file_list_lock);
++refind; ++refind;
inv_file = NULL; inv_file = NULL;
spin_lock(&tcon->open_file_lock);
goto refind_writable; goto refind_writable;
} }
} }
...@@ -2543,7 +2560,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, ...@@ -2543,7 +2560,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
wdata->credits = credits; wdata->credits = credits;
if (!wdata->cfile->invalidHandle || if (!wdata->cfile->invalidHandle ||
!cifs_reopen_file(wdata->cfile, false)) !(rc = cifs_reopen_file(wdata->cfile, false)))
rc = server->ops->async_writev(wdata, rc = server->ops->async_writev(wdata,
cifs_uncached_writedata_release); cifs_uncached_writedata_release);
if (rc) { if (rc) {
...@@ -2956,7 +2973,7 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file, ...@@ -2956,7 +2973,7 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
rdata->credits = credits; rdata->credits = credits;
if (!rdata->cfile->invalidHandle || if (!rdata->cfile->invalidHandle ||
!cifs_reopen_file(rdata->cfile, true)) !(rc = cifs_reopen_file(rdata->cfile, true)))
rc = server->ops->async_readv(rdata); rc = server->ops->async_readv(rdata);
error: error:
if (rc) { if (rc) {
...@@ -3542,7 +3559,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, ...@@ -3542,7 +3559,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
} }
if (!rdata->cfile->invalidHandle || if (!rdata->cfile->invalidHandle ||
!cifs_reopen_file(rdata->cfile, true)) !(rc = cifs_reopen_file(rdata->cfile, true)))
rc = server->ops->async_readv(rdata); rc = server->ops->async_readv(rdata);
if (rc) { if (rc) {
add_credits_and_wake_if(server, rdata->credits, 0); add_credits_and_wake_if(server, rdata->credits, 0);
...@@ -3640,15 +3657,17 @@ static int cifs_readpage(struct file *file, struct page *page) ...@@ -3640,15 +3657,17 @@ static int cifs_readpage(struct file *file, struct page *page)
static int is_inode_writable(struct cifsInodeInfo *cifs_inode) static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
{ {
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
struct cifs_tcon *tcon =
cifs_sb_master_tcon(CIFS_SB(cifs_inode->vfs_inode.i_sb));
spin_lock(&cifs_file_list_lock); spin_lock(&tcon->open_file_lock);
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
return 1; return 1;
} }
} }
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
return 0; return 0;
} }
......
...@@ -983,10 +983,26 @@ struct inode *cifs_root_iget(struct super_block *sb) ...@@ -983,10 +983,26 @@ struct inode *cifs_root_iget(struct super_block *sb)
struct inode *inode = NULL; struct inode *inode = NULL;
long rc; long rc;
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
char *path = NULL;
int len;
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
&& cifs_sb->prepath) {
len = strlen(cifs_sb->prepath);
path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL);
if (path == NULL)
return ERR_PTR(-ENOMEM);
path[0] = '/';
memcpy(path+1, cifs_sb->prepath, len);
} else {
path = kstrdup("", GFP_KERNEL);
if (path == NULL)
return ERR_PTR(-ENOMEM);
}
xid = get_xid(); xid = get_xid();
if (tcon->unix_ext) { if (tcon->unix_ext) {
rc = cifs_get_inode_info_unix(&inode, "", sb, xid); rc = cifs_get_inode_info_unix(&inode, path, sb, xid);
/* some servers mistakenly claim POSIX support */ /* some servers mistakenly claim POSIX support */
if (rc != -EOPNOTSUPP) if (rc != -EOPNOTSUPP)
goto iget_no_retry; goto iget_no_retry;
...@@ -994,7 +1010,8 @@ struct inode *cifs_root_iget(struct super_block *sb) ...@@ -994,7 +1010,8 @@ struct inode *cifs_root_iget(struct super_block *sb)
tcon->unix_ext = false; tcon->unix_ext = false;
} }
rc = cifs_get_inode_info(&inode, "", NULL, sb, xid, NULL); convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL);
iget_no_retry: iget_no_retry:
if (!inode) { if (!inode) {
...@@ -1023,6 +1040,7 @@ iget_no_retry: ...@@ -1023,6 +1040,7 @@ iget_no_retry:
} }
out: out:
kfree(path);
/* can not call macro free_xid here since in a void func /* can not call macro free_xid here since in a void func
* TODO: This is no longer true * TODO: This is no longer true
*/ */
......
...@@ -272,6 +272,8 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) ...@@ -272,6 +272,8 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
break; break;
case CIFS_IOC_GET_MNT_INFO: case CIFS_IOC_GET_MNT_INFO:
if (pSMBFile == NULL)
break;
tcon = tlink_tcon(pSMBFile->tlink); tcon = tlink_tcon(pSMBFile->tlink);
rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg); rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
break; break;
......
...@@ -120,6 +120,7 @@ tconInfoAlloc(void) ...@@ -120,6 +120,7 @@ tconInfoAlloc(void)
++ret_buf->tc_count; ++ret_buf->tc_count;
INIT_LIST_HEAD(&ret_buf->openFileList); INIT_LIST_HEAD(&ret_buf->openFileList);
INIT_LIST_HEAD(&ret_buf->tcon_list); INIT_LIST_HEAD(&ret_buf->tcon_list);
spin_lock_init(&ret_buf->open_file_lock);
#ifdef CONFIG_CIFS_STATS #ifdef CONFIG_CIFS_STATS
spin_lock_init(&ret_buf->stat_lock); spin_lock_init(&ret_buf->stat_lock);
#endif #endif
...@@ -465,7 +466,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) ...@@ -465,7 +466,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
continue; continue;
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
spin_lock(&cifs_file_list_lock); spin_lock(&tcon->open_file_lock);
list_for_each(tmp2, &tcon->openFileList) { list_for_each(tmp2, &tcon->openFileList) {
netfile = list_entry(tmp2, struct cifsFileInfo, netfile = list_entry(tmp2, struct cifsFileInfo,
tlist); tlist);
...@@ -495,11 +496,11 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) ...@@ -495,11 +496,11 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
&netfile->oplock_break); &netfile->oplock_break);
netfile->oplock_break_cancelled = false; netfile->oplock_break_cancelled = false;
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return true; return true;
} }
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "No matching file for oplock break\n"); cifs_dbg(FYI, "No matching file for oplock break\n");
return true; return true;
...@@ -613,9 +614,9 @@ backup_cred(struct cifs_sb_info *cifs_sb) ...@@ -613,9 +614,9 @@ backup_cred(struct cifs_sb_info *cifs_sb)
void void
cifs_del_pending_open(struct cifs_pending_open *open) cifs_del_pending_open(struct cifs_pending_open *open)
{ {
spin_lock(&cifs_file_list_lock); spin_lock(&tlink_tcon(open->tlink)->open_file_lock);
list_del(&open->olist); list_del(&open->olist);
spin_unlock(&cifs_file_list_lock); spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
} }
void void
...@@ -635,7 +636,7 @@ void ...@@ -635,7 +636,7 @@ void
cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
struct cifs_pending_open *open) struct cifs_pending_open *open)
{ {
spin_lock(&cifs_file_list_lock); spin_lock(&tlink_tcon(tlink)->open_file_lock);
cifs_add_pending_open_locked(fid, tlink, open); cifs_add_pending_open_locked(fid, tlink, open);
spin_unlock(&cifs_file_list_lock); spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
} }
...@@ -133,6 +133,6 @@ typedef struct _AUTHENTICATE_MESSAGE { ...@@ -133,6 +133,6 @@ typedef struct _AUTHENTICATE_MESSAGE {
int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses);
void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses); void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses);
int build_ntlmssp_auth_blob(unsigned char *pbuffer, u16 *buflen, int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
struct cifs_ses *ses, struct cifs_ses *ses,
const struct nls_table *nls_cp); const struct nls_table *nls_cp);
...@@ -282,6 +282,7 @@ initiate_cifs_search(const unsigned int xid, struct file *file) ...@@ -282,6 +282,7 @@ initiate_cifs_search(const unsigned int xid, struct file *file)
rc = -ENOMEM; rc = -ENOMEM;
goto error_exit; goto error_exit;
} }
spin_lock_init(&cifsFile->file_info_lock);
file->private_data = cifsFile; file->private_data = cifsFile;
cifsFile->tlink = cifs_get_tlink(tlink); cifsFile->tlink = cifs_get_tlink(tlink);
tcon = tlink_tcon(tlink); tcon = tlink_tcon(tlink);
...@@ -594,14 +595,14 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, ...@@ -594,14 +595,14 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) { is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
/* close and restart search */ /* close and restart search */
cifs_dbg(FYI, "search backing up - close and restart search\n"); cifs_dbg(FYI, "search backing up - close and restart search\n");
spin_lock(&cifs_file_list_lock); spin_lock(&cfile->file_info_lock);
if (server->ops->dir_needs_close(cfile)) { if (server->ops->dir_needs_close(cfile)) {
cfile->invalidHandle = true; cfile->invalidHandle = true;
spin_unlock(&cifs_file_list_lock); spin_unlock(&cfile->file_info_lock);
if (server->ops->close_dir) if (server->ops->close_dir)
server->ops->close_dir(xid, tcon, &cfile->fid); server->ops->close_dir(xid, tcon, &cfile->fid);
} else } else
spin_unlock(&cifs_file_list_lock); spin_unlock(&cfile->file_info_lock);
if (cfile->srch_inf.ntwrk_buf_start) { if (cfile->srch_inf.ntwrk_buf_start) {
cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n"); cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n");
if (cfile->srch_inf.smallBuf) if (cfile->srch_inf.smallBuf)
......
...@@ -364,19 +364,43 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, ...@@ -364,19 +364,43 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
sec_blob->DomainName.MaximumLength = 0; sec_blob->DomainName.MaximumLength = 0;
} }
/* We do not malloc the blob, it is passed in pbuffer, because its static int size_of_ntlmssp_blob(struct cifs_ses *ses)
maximum possible size is fixed and small, making this approach cleaner. {
This function returns the length of the data in the blob */ int sz = sizeof(AUTHENTICATE_MESSAGE) + ses->auth_key.len
int build_ntlmssp_auth_blob(unsigned char *pbuffer, - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2;
if (ses->domainName)
sz += 2 * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
else
sz += 2;
if (ses->user_name)
sz += 2 * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);
else
sz += 2;
return sz;
}
int build_ntlmssp_auth_blob(unsigned char **pbuffer,
u16 *buflen, u16 *buflen,
struct cifs_ses *ses, struct cifs_ses *ses,
const struct nls_table *nls_cp) const struct nls_table *nls_cp)
{ {
int rc; int rc;
AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer; AUTHENTICATE_MESSAGE *sec_blob;
__u32 flags; __u32 flags;
unsigned char *tmp; unsigned char *tmp;
rc = setup_ntlmv2_rsp(ses, nls_cp);
if (rc) {
cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);
*buflen = 0;
goto setup_ntlmv2_ret;
}
*pbuffer = kmalloc(size_of_ntlmssp_blob(ses), GFP_KERNEL);
sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer;
memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
sec_blob->MessageType = NtLmAuthenticate; sec_blob->MessageType = NtLmAuthenticate;
...@@ -391,7 +415,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -391,7 +415,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
flags |= NTLMSSP_NEGOTIATE_KEY_XCH; flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
} }
tmp = pbuffer + sizeof(AUTHENTICATE_MESSAGE); tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
sec_blob->NegotiateFlags = cpu_to_le32(flags); sec_blob->NegotiateFlags = cpu_to_le32(flags);
sec_blob->LmChallengeResponse.BufferOffset = sec_blob->LmChallengeResponse.BufferOffset =
...@@ -399,13 +423,9 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -399,13 +423,9 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
sec_blob->LmChallengeResponse.Length = 0; sec_blob->LmChallengeResponse.Length = 0;
sec_blob->LmChallengeResponse.MaximumLength = 0; sec_blob->LmChallengeResponse.MaximumLength = 0;
sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->NtChallengeResponse.BufferOffset =
cpu_to_le32(tmp - *pbuffer);
if (ses->user_name != NULL) { if (ses->user_name != NULL) {
rc = setup_ntlmv2_rsp(ses, nls_cp);
if (rc) {
cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);
goto setup_ntlmv2_ret;
}
memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
ses->auth_key.len - CIFS_SESS_KEY_SIZE); ses->auth_key.len - CIFS_SESS_KEY_SIZE);
tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
...@@ -423,7 +443,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -423,7 +443,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
} }
if (ses->domainName == NULL) { if (ses->domainName == NULL) {
sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->DomainName.Length = 0; sec_blob->DomainName.Length = 0;
sec_blob->DomainName.MaximumLength = 0; sec_blob->DomainName.MaximumLength = 0;
tmp += 2; tmp += 2;
...@@ -432,14 +452,14 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -432,14 +452,14 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName, len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName,
CIFS_MAX_USERNAME_LEN, 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);
sec_blob->DomainName.MaximumLength = cpu_to_le16(len); sec_blob->DomainName.MaximumLength = cpu_to_le16(len);
tmp += len; tmp += len;
} }
if (ses->user_name == NULL) { if (ses->user_name == NULL) {
sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->UserName.Length = 0; sec_blob->UserName.Length = 0;
sec_blob->UserName.MaximumLength = 0; sec_blob->UserName.MaximumLength = 0;
tmp += 2; tmp += 2;
...@@ -448,13 +468,13 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -448,13 +468,13 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name, len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name,
CIFS_MAX_USERNAME_LEN, 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);
sec_blob->UserName.MaximumLength = cpu_to_le16(len); sec_blob->UserName.MaximumLength = cpu_to_le16(len);
tmp += len; tmp += len;
} }
sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->WorkstationName.Length = 0; sec_blob->WorkstationName.Length = 0;
sec_blob->WorkstationName.MaximumLength = 0; sec_blob->WorkstationName.MaximumLength = 0;
tmp += 2; tmp += 2;
...@@ -463,19 +483,19 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -463,19 +483,19 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
(ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) (ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC))
&& !calc_seckey(ses)) { && !calc_seckey(ses)) {
memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);
sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE); sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE);
sec_blob->SessionKey.MaximumLength = sec_blob->SessionKey.MaximumLength =
cpu_to_le16(CIFS_CPHTXT_SIZE); cpu_to_le16(CIFS_CPHTXT_SIZE);
tmp += CIFS_CPHTXT_SIZE; tmp += CIFS_CPHTXT_SIZE;
} else { } else {
sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->SessionKey.Length = 0; sec_blob->SessionKey.Length = 0;
sec_blob->SessionKey.MaximumLength = 0; sec_blob->SessionKey.MaximumLength = 0;
} }
*buflen = tmp - *pbuffer;
setup_ntlmv2_ret: setup_ntlmv2_ret:
*buflen = tmp - pbuffer;
return rc; return rc;
} }
...@@ -1266,7 +1286,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) ...@@ -1266,7 +1286,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
__u16 bytes_remaining; __u16 bytes_remaining;
char *bcc_ptr; char *bcc_ptr;
char *ntlmsspblob = NULL; unsigned char *ntlmsspblob = NULL;
u16 blob_len; u16 blob_len;
cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n"); cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n");
...@@ -1279,19 +1299,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) ...@@ -1279,19 +1299,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
/* Build security blob before we assemble the request */ /* Build security blob before we assemble the request */
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
smb_buf = (struct smb_hdr *)pSMB; smb_buf = (struct smb_hdr *)pSMB;
/* rc = build_ntlmssp_auth_blob(&ntlmsspblob,
* 5 is an empirical value, large enough to hold
* authenticate message plus max 10 of av paris,
* domain, user, workstation names, flags, etc.
*/
ntlmsspblob = kzalloc(5*sizeof(struct _AUTHENTICATE_MESSAGE),
GFP_KERNEL);
if (!ntlmsspblob) {
rc = -ENOMEM;
goto out;
}
rc = build_ntlmssp_auth_blob(ntlmsspblob,
&blob_len, ses, sess_data->nls_cp); &blob_len, ses, sess_data->nls_cp);
if (rc) if (rc)
goto out_free_ntlmsspblob; goto out_free_ntlmsspblob;
......
...@@ -851,8 +851,13 @@ cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -851,8 +851,13 @@ cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid *fid, __u16 search_flags, struct cifs_fid *fid, __u16 search_flags,
struct cifs_search_info *srch_inf) struct cifs_search_info *srch_inf)
{ {
return CIFSFindFirst(xid, tcon, path, cifs_sb, int rc;
&fid->netfid, search_flags, srch_inf, true);
rc = CIFSFindFirst(xid, tcon, path, cifs_sb,
&fid->netfid, search_flags, srch_inf, true);
if (rc)
cifs_dbg(FYI, "find first failed=%d\n", rc);
return rc;
} }
static int static int
...@@ -1018,6 +1023,15 @@ cifs_dir_needs_close(struct cifsFileInfo *cfile) ...@@ -1018,6 +1023,15 @@ cifs_dir_needs_close(struct cifsFileInfo *cfile)
return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle; return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle;
} }
static bool
cifs_can_echo(struct TCP_Server_Info *server)
{
if (server->tcpStatus == CifsGood)
return true;
return false;
}
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,
...@@ -1052,6 +1066,7 @@ struct smb_version_operations smb1_operations = { ...@@ -1052,6 +1066,7 @@ struct smb_version_operations smb1_operations = {
.get_dfs_refer = CIFSGetDFSRefer, .get_dfs_refer = CIFSGetDFSRefer,
.qfs_tcon = cifs_qfs_tcon, .qfs_tcon = cifs_qfs_tcon,
.is_path_accessible = cifs_is_path_accessible, .is_path_accessible = cifs_is_path_accessible,
.can_echo = cifs_can_echo,
.query_path_info = cifs_query_path_info, .query_path_info = cifs_query_path_info,
.query_file_info = cifs_query_file_info, .query_file_info = cifs_query_file_info,
.get_srv_inum = cifs_get_srv_inum, .get_srv_inum = cifs_get_srv_inum,
......
...@@ -260,7 +260,7 @@ smb2_push_mandatory_locks(struct cifsFileInfo *cfile) ...@@ -260,7 +260,7 @@ smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
* and check it for zero before using. * and check it for zero before using.
*/ */
max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf; max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf;
if (!max_buf) { if (max_buf < sizeof(struct smb2_lock_element)) {
free_xid(xid); free_xid(xid);
return -EINVAL; return -EINVAL;
} }
......
...@@ -61,4 +61,14 @@ ...@@ -61,4 +61,14 @@
/* Maximum buffer size value we can send with 1 credit */ /* Maximum buffer size value we can send with 1 credit */
#define SMB2_MAX_BUFFER_SIZE 65536 #define SMB2_MAX_BUFFER_SIZE 65536
/*
* Maximum number of credits to keep available.
* This value is chosen somewhat arbitrarily. The Windows client
* defaults to 128 credits, the Windows server allows clients up to
* 512 credits, and the NetApp server does not limit clients at all.
* Choose a high enough value such that the client shouldn't limit
* performance.
*/
#define SMB2_MAX_CREDITS_AVAILABLE 32000
#endif /* _SMB2_GLOB_H */ #endif /* _SMB2_GLOB_H */
...@@ -267,9 +267,15 @@ smb2_set_file_info(struct inode *inode, const char *full_path, ...@@ -267,9 +267,15 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
struct tcon_link *tlink; struct tcon_link *tlink;
int rc; int rc;
if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
(buf->LastWriteTime == 0) && (buf->ChangeTime) &&
(buf->Attributes == 0))
return 0; /* would be a no op, no sense sending this */
tlink = cifs_sb_tlink(cifs_sb); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) if (IS_ERR(tlink))
return PTR_ERR(tlink); return PTR_ERR(tlink);
rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path, rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path,
FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf, FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf,
SMB2_OP_SET_INFO); SMB2_OP_SET_INFO);
......
...@@ -525,19 +525,19 @@ smb2_is_valid_lease_break(char *buffer) ...@@ -525,19 +525,19 @@ smb2_is_valid_lease_break(char *buffer)
list_for_each(tmp1, &server->smb_ses_list) { list_for_each(tmp1, &server->smb_ses_list) {
ses = list_entry(tmp1, struct cifs_ses, smb_ses_list); ses = list_entry(tmp1, struct cifs_ses, smb_ses_list);
spin_lock(&cifs_file_list_lock);
list_for_each(tmp2, &ses->tcon_list) { list_for_each(tmp2, &ses->tcon_list) {
tcon = list_entry(tmp2, struct cifs_tcon, tcon = list_entry(tmp2, struct cifs_tcon,
tcon_list); tcon_list);
spin_lock(&tcon->open_file_lock);
cifs_stats_inc( cifs_stats_inc(
&tcon->stats.cifs_stats.num_oplock_brks); &tcon->stats.cifs_stats.num_oplock_brks);
if (smb2_tcon_has_lease(tcon, rsp, lw)) { if (smb2_tcon_has_lease(tcon, rsp, lw)) {
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return true; return true;
} }
spin_unlock(&tcon->open_file_lock);
} }
spin_unlock(&cifs_file_list_lock);
} }
} }
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
...@@ -579,7 +579,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -579,7 +579,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
spin_lock(&cifs_file_list_lock); spin_lock(&tcon->open_file_lock);
list_for_each(tmp2, &tcon->openFileList) { list_for_each(tmp2, &tcon->openFileList) {
cfile = list_entry(tmp2, struct cifsFileInfo, cfile = list_entry(tmp2, struct cifsFileInfo,
tlist); tlist);
...@@ -591,7 +591,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -591,7 +591,7 @@ 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(d_inode(cfile->dentry)); cinode = CIFS_I(d_inode(cfile->dentry));
spin_lock(&cfile->file_info_lock);
if (!CIFS_CACHE_WRITE(cinode) && 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;
...@@ -613,14 +613,14 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -613,14 +613,14 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
clear_bit( clear_bit(
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags); &cinode->flags);
spin_unlock(&cfile->file_info_lock);
queue_work(cifsiod_wq, &cfile->oplock_break); queue_work(cifsiod_wq, &cfile->oplock_break);
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return true; return true;
} }
spin_unlock(&cifs_file_list_lock); spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "No matching file for oplock break\n"); cifs_dbg(FYI, "No matching file for oplock break\n");
return true; return true;
...@@ -630,3 +630,47 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -630,3 +630,47 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n");
return false; return false;
} }
void
smb2_cancelled_close_fid(struct work_struct *work)
{
struct close_cancelled_open *cancelled = container_of(work,
struct close_cancelled_open, work);
cifs_dbg(VFS, "Close unmatched open\n");
SMB2_close(0, cancelled->tcon, cancelled->fid.persistent_fid,
cancelled->fid.volatile_fid);
cifs_put_tcon(cancelled->tcon);
kfree(cancelled);
}
int
smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
{
struct smb2_hdr *hdr = (struct smb2_hdr *)buffer;
struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
struct cifs_tcon *tcon;
struct close_cancelled_open *cancelled;
if (hdr->Command != SMB2_CREATE || hdr->Status != STATUS_SUCCESS)
return 0;
cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
if (!cancelled)
return -ENOMEM;
tcon = smb2_find_smb_tcon(server, hdr->SessionId, hdr->TreeId);
if (!tcon) {
kfree(cancelled);
return -ENOENT;
}
cancelled->fid.persistent_fid = rsp->PersistentFileId;
cancelled->fid.volatile_fid = rsp->VolatileFileId;
cancelled->tcon = tcon;
INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
queue_work(cifsiod_wq, &cancelled->work);
return 0;
}
...@@ -282,7 +282,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -282,7 +282,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
cifs_dbg(FYI, "Link Speed %lld\n", cifs_dbg(FYI, "Link Speed %lld\n",
le64_to_cpu(out_buf->LinkSpeed)); le64_to_cpu(out_buf->LinkSpeed));
} }
kfree(out_buf);
return rc; return rc;
} }
#endif /* STATS2 */ #endif /* STATS2 */
...@@ -539,6 +539,7 @@ smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) ...@@ -539,6 +539,7 @@ smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
server->ops->set_oplock_level(cinode, oplock, fid->epoch, server->ops->set_oplock_level(cinode, oplock, fid->epoch,
&fid->purge_cache); &fid->purge_cache);
cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
memcpy(cfile->fid.create_guid, fid->create_guid, 16);
} }
static void static void
...@@ -697,6 +698,7 @@ smb2_clone_range(const unsigned int xid, ...@@ -697,6 +698,7 @@ smb2_clone_range(const unsigned int xid,
cchunk_out: cchunk_out:
kfree(pcchunk); kfree(pcchunk);
kfree(retbuf);
return rc; return rc;
} }
...@@ -821,7 +823,6 @@ smb2_duplicate_extents(const unsigned int xid, ...@@ -821,7 +823,6 @@ smb2_duplicate_extents(const unsigned int xid,
{ {
int rc; int rc;
unsigned int ret_data_len; unsigned int ret_data_len;
char *retbuf = NULL;
struct duplicate_extents_to_file dup_ext_buf; struct duplicate_extents_to_file dup_ext_buf;
struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink); struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
...@@ -847,7 +848,7 @@ smb2_duplicate_extents(const unsigned int xid, ...@@ -847,7 +848,7 @@ smb2_duplicate_extents(const unsigned int xid,
FSCTL_DUPLICATE_EXTENTS_TO_FILE, FSCTL_DUPLICATE_EXTENTS_TO_FILE,
true /* is_fsctl */, (char *)&dup_ext_buf, true /* is_fsctl */, (char *)&dup_ext_buf,
sizeof(struct duplicate_extents_to_file), sizeof(struct duplicate_extents_to_file),
(char **)&retbuf, NULL,
&ret_data_len); &ret_data_len);
if (ret_data_len > 0) if (ret_data_len > 0)
...@@ -870,7 +871,6 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -870,7 +871,6 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile) struct cifsFileInfo *cfile)
{ {
struct fsctl_set_integrity_information_req integr_info; struct fsctl_set_integrity_information_req integr_info;
char *retbuf = NULL;
unsigned int ret_data_len; unsigned int ret_data_len;
integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED); integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED);
...@@ -882,7 +882,7 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -882,7 +882,7 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
FSCTL_SET_INTEGRITY_INFORMATION, FSCTL_SET_INTEGRITY_INFORMATION,
true /* is_fsctl */, (char *)&integr_info, true /* is_fsctl */, (char *)&integr_info,
sizeof(struct fsctl_set_integrity_information_req), sizeof(struct fsctl_set_integrity_information_req),
(char **)&retbuf, NULL,
&ret_data_len); &ret_data_len);
} }
...@@ -913,7 +913,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -913,7 +913,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL); rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
kfree(utf16_path); kfree(utf16_path);
if (rc) { if (rc) {
cifs_dbg(VFS, "open dir failed\n"); cifs_dbg(FYI, "open dir failed rc=%d\n", rc);
return rc; return rc;
} }
...@@ -923,7 +923,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -923,7 +923,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
rc = SMB2_query_directory(xid, tcon, fid->persistent_fid, rc = SMB2_query_directory(xid, tcon, fid->persistent_fid,
fid->volatile_fid, 0, srch_inf); fid->volatile_fid, 0, srch_inf);
if (rc) { if (rc) {
cifs_dbg(VFS, "query directory failed\n"); cifs_dbg(FYI, "query directory failed rc=%d\n", rc);
SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
} }
return rc; return rc;
...@@ -967,6 +967,18 @@ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length) ...@@ -967,6 +967,18 @@ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length)
return true; return true;
} }
static bool
smb2_is_session_expired(char *buf)
{
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
if (hdr->Status != STATUS_NETWORK_SESSION_EXPIRED)
return false;
cifs_dbg(FYI, "Session expired\n");
return true;
}
static int static int
smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
struct cifsInodeInfo *cinode) struct cifsInodeInfo *cinode)
...@@ -1041,9 +1053,12 @@ smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid) ...@@ -1041,9 +1053,12 @@ smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid)
static void static void
smb2_new_lease_key(struct cifs_fid *fid) smb2_new_lease_key(struct cifs_fid *fid)
{ {
get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE); generate_random_uuid(fid->lease_key);
} }
#define SMB2_SYMLINK_STRUCT_SIZE \
(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
static int static int
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
const char *full_path, char **target_path, const char *full_path, char **target_path,
...@@ -1056,7 +1071,10 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1056,7 +1071,10 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid fid; struct cifs_fid fid;
struct smb2_err_rsp *err_buf = NULL; struct smb2_err_rsp *err_buf = NULL;
struct smb2_symlink_err_rsp *symlink; struct smb2_symlink_err_rsp *symlink;
unsigned int sub_len, sub_offset; unsigned int sub_len;
unsigned int sub_offset;
unsigned int print_len;
unsigned int print_offset;
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
...@@ -1078,11 +1096,33 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1078,11 +1096,33 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
kfree(utf16_path); kfree(utf16_path);
return -ENOENT; return -ENOENT;
} }
if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) ||
get_rfc1002_length(err_buf) + 4 < SMB2_SYMLINK_STRUCT_SIZE) {
kfree(utf16_path);
return -ENOENT;
}
/* open must fail on symlink - reset rc */ /* open must fail on symlink - reset rc */
rc = 0; rc = 0;
symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData; symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData;
sub_len = le16_to_cpu(symlink->SubstituteNameLength); sub_len = le16_to_cpu(symlink->SubstituteNameLength);
sub_offset = le16_to_cpu(symlink->SubstituteNameOffset); sub_offset = le16_to_cpu(symlink->SubstituteNameOffset);
print_len = le16_to_cpu(symlink->PrintNameLength);
print_offset = le16_to_cpu(symlink->PrintNameOffset);
if (get_rfc1002_length(err_buf) + 4 <
SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) {
kfree(utf16_path);
return -ENOENT;
}
if (get_rfc1002_length(err_buf) + 4 <
SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) {
kfree(utf16_path);
return -ENOENT;
}
*target_path = cifs_strndup_from_utf16( *target_path = cifs_strndup_from_utf16(
(char *)symlink->PathBuffer + sub_offset, (char *)symlink->PathBuffer + sub_offset,
sub_len, true, cifs_sb->local_nls); sub_len, true, cifs_sb->local_nls);
...@@ -1489,6 +1529,7 @@ struct smb_version_operations smb20_operations = { ...@@ -1489,6 +1529,7 @@ struct smb_version_operations smb20_operations = {
.clear_stats = smb2_clear_stats, .clear_stats = smb2_clear_stats,
.print_stats = smb2_print_stats, .print_stats = smb2_print_stats,
.is_oplock_break = smb2_is_valid_oplock_break, .is_oplock_break = smb2_is_valid_oplock_break,
.handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb2_downgrade_oplock, .downgrade_oplock = smb2_downgrade_oplock,
.need_neg = smb2_need_neg, .need_neg = smb2_need_neg,
.negotiate = smb2_negotiate, .negotiate = smb2_negotiate,
...@@ -1529,6 +1570,7 @@ struct smb_version_operations smb20_operations = { ...@@ -1529,6 +1570,7 @@ struct smb_version_operations smb20_operations = {
.close_dir = smb2_close_dir, .close_dir = smb2_close_dir,
.calc_smb_size = smb2_calc_size, .calc_smb_size = smb2_calc_size,
.is_status_pending = smb2_is_status_pending, .is_status_pending = smb2_is_status_pending,
.is_session_expired = smb2_is_session_expired,
.oplock_response = smb2_oplock_response, .oplock_response = smb2_oplock_response,
.queryfs = smb2_queryfs, .queryfs = smb2_queryfs,
.mand_lock = smb2_mand_lock, .mand_lock = smb2_mand_lock,
...@@ -1567,6 +1609,7 @@ struct smb_version_operations smb21_operations = { ...@@ -1567,6 +1609,7 @@ struct smb_version_operations smb21_operations = {
.clear_stats = smb2_clear_stats, .clear_stats = smb2_clear_stats,
.print_stats = smb2_print_stats, .print_stats = smb2_print_stats,
.is_oplock_break = smb2_is_valid_oplock_break, .is_oplock_break = smb2_is_valid_oplock_break,
.handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb2_downgrade_oplock, .downgrade_oplock = smb2_downgrade_oplock,
.need_neg = smb2_need_neg, .need_neg = smb2_need_neg,
.negotiate = smb2_negotiate, .negotiate = smb2_negotiate,
...@@ -1609,6 +1652,7 @@ struct smb_version_operations smb21_operations = { ...@@ -1609,6 +1652,7 @@ struct smb_version_operations smb21_operations = {
.close_dir = smb2_close_dir, .close_dir = smb2_close_dir,
.calc_smb_size = smb2_calc_size, .calc_smb_size = smb2_calc_size,
.is_status_pending = smb2_is_status_pending, .is_status_pending = smb2_is_status_pending,
.is_session_expired = smb2_is_session_expired,
.oplock_response = smb2_oplock_response, .oplock_response = smb2_oplock_response,
.queryfs = smb2_queryfs, .queryfs = smb2_queryfs,
.mand_lock = smb2_mand_lock, .mand_lock = smb2_mand_lock,
...@@ -1648,6 +1692,7 @@ struct smb_version_operations smb30_operations = { ...@@ -1648,6 +1692,7 @@ struct smb_version_operations smb30_operations = {
.print_stats = smb2_print_stats, .print_stats = smb2_print_stats,
.dump_share_caps = smb2_dump_share_caps, .dump_share_caps = smb2_dump_share_caps,
.is_oplock_break = smb2_is_valid_oplock_break, .is_oplock_break = smb2_is_valid_oplock_break,
.handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb2_downgrade_oplock, .downgrade_oplock = smb2_downgrade_oplock,
.need_neg = smb2_need_neg, .need_neg = smb2_need_neg,
.negotiate = smb2_negotiate, .negotiate = smb2_negotiate,
...@@ -1690,6 +1735,7 @@ struct smb_version_operations smb30_operations = { ...@@ -1690,6 +1735,7 @@ struct smb_version_operations smb30_operations = {
.close_dir = smb2_close_dir, .close_dir = smb2_close_dir,
.calc_smb_size = smb2_calc_size, .calc_smb_size = smb2_calc_size,
.is_status_pending = smb2_is_status_pending, .is_status_pending = smb2_is_status_pending,
.is_session_expired = smb2_is_session_expired,
.oplock_response = smb2_oplock_response, .oplock_response = smb2_oplock_response,
.queryfs = smb2_queryfs, .queryfs = smb2_queryfs,
.mand_lock = smb2_mand_lock, .mand_lock = smb2_mand_lock,
...@@ -1735,6 +1781,7 @@ struct smb_version_operations smb311_operations = { ...@@ -1735,6 +1781,7 @@ struct smb_version_operations smb311_operations = {
.print_stats = smb2_print_stats, .print_stats = smb2_print_stats,
.dump_share_caps = smb2_dump_share_caps, .dump_share_caps = smb2_dump_share_caps,
.is_oplock_break = smb2_is_valid_oplock_break, .is_oplock_break = smb2_is_valid_oplock_break,
.handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb2_downgrade_oplock, .downgrade_oplock = smb2_downgrade_oplock,
.need_neg = smb2_need_neg, .need_neg = smb2_need_neg,
.negotiate = smb2_negotiate, .negotiate = smb2_negotiate,
...@@ -1777,6 +1824,7 @@ struct smb_version_operations smb311_operations = { ...@@ -1777,6 +1824,7 @@ struct smb_version_operations smb311_operations = {
.close_dir = smb2_close_dir, .close_dir = smb2_close_dir,
.calc_smb_size = smb2_calc_size, .calc_smb_size = smb2_calc_size,
.is_status_pending = smb2_is_status_pending, .is_status_pending = smb2_is_status_pending,
.is_session_expired = smb2_is_session_expired,
.oplock_response = smb2_oplock_response, .oplock_response = smb2_oplock_response,
.queryfs = smb2_queryfs, .queryfs = smb2_queryfs,
.mand_lock = smb2_mand_lock, .mand_lock = smb2_mand_lock,
......
...@@ -103,7 +103,21 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , ...@@ -103,7 +103,21 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
hdr->ProtocolId[3] = 'B'; hdr->ProtocolId[3] = 'B';
hdr->StructureSize = cpu_to_le16(64); hdr->StructureSize = cpu_to_le16(64);
hdr->Command = smb2_cmd; hdr->Command = smb2_cmd;
hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */ if (tcon && tcon->ses && tcon->ses->server) {
struct TCP_Server_Info *server = tcon->ses->server;
spin_lock(&server->req_lock);
/* Request up to 2 credits but don't go over the limit. */
if (server->credits >= SMB2_MAX_CREDITS_AVAILABLE)
hdr->CreditRequest = cpu_to_le16(0);
else
hdr->CreditRequest = cpu_to_le16(
min_t(int, SMB2_MAX_CREDITS_AVAILABLE -
server->credits, 2));
spin_unlock(&server->req_lock);
} else {
hdr->CreditRequest = cpu_to_le16(2);
}
hdr->ProcessId = cpu_to_le32((__u16)current->tgid); hdr->ProcessId = cpu_to_le32((__u16)current->tgid);
if (!tcon) if (!tcon)
...@@ -264,7 +278,7 @@ out: ...@@ -264,7 +278,7 @@ out:
case SMB2_CHANGE_NOTIFY: case SMB2_CHANGE_NOTIFY:
case SMB2_QUERY_INFO: case SMB2_QUERY_INFO:
case SMB2_SET_INFO: case SMB2_SET_INFO:
return -EAGAIN; rc = -EAGAIN;
} }
unload_nls(nls_codepage); unload_nls(nls_codepage);
return rc; return rc;
...@@ -347,7 +361,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req) ...@@ -347,7 +361,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req)
build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt); build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt);
req->NegotiateContextOffset = cpu_to_le32(OFFSET_OF_NEG_CONTEXT); req->NegotiateContextOffset = cpu_to_le32(OFFSET_OF_NEG_CONTEXT);
req->NegotiateContextCount = cpu_to_le16(2); req->NegotiateContextCount = cpu_to_le16(2);
inc_rfc1001_len(req, 4 + sizeof(struct smb2_preauth_neg_context) + 2 inc_rfc1001_len(req, 4 + sizeof(struct smb2_preauth_neg_context)
+ sizeof(struct smb2_encryption_neg_context)); /* calculate hash */ + sizeof(struct smb2_encryption_neg_context)); /* calculate hash */
} }
#else #else
...@@ -512,14 +526,21 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -512,14 +526,21 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
/* /*
* validation ioctl must be signed, so no point sending this if we * validation ioctl must be signed, so no point sending this if we
* can not sign it. We could eventually change this to selectively * can not sign it (ie are not known user). Even if signing is not
* required (enabled but not negotiated), in those cases we selectively
* sign just this, the first and only signed request on a connection. * sign just this, the first and only signed request on a connection.
* This is good enough for now since a user who wants better security * Having validation of negotiate info helps reduce attack vectors.
* would also enable signing on the mount. Having validation of
* negotiate info for signed connections helps reduce attack vectors
*/ */
if (tcon->ses->server->sign == false) if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST)
return 0; /* validation requires signing */
if (tcon->ses->user_name == NULL) {
cifs_dbg(FYI, "Can't validate negotiate: null user mount\n");
return 0; /* validation requires signing */ return 0; /* validation requires signing */
}
if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
cifs_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n");
vneg_inbuf.Capabilities = vneg_inbuf.Capabilities =
cpu_to_le32(tcon->ses->server->vals->req_capabilities); cpu_to_le32(tcon->ses->server->vals->req_capabilities);
...@@ -550,8 +571,12 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -550,8 +571,12 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
} }
if (rsplen != sizeof(struct validate_negotiate_info_rsp)) { if (rsplen != sizeof(struct validate_negotiate_info_rsp)) {
cifs_dbg(VFS, "invalid size of protocol negotiate response\n"); cifs_dbg(VFS, "invalid protocol negotiate response size: %d\n",
return -EIO; rsplen);
/* relax check since Mac returns max bufsize allowed on ioctl */
if (rsplen > CIFSMaxBufSize)
return -EIO;
} }
/* check validate negotiate info response matches what we got earlier */ /* check validate negotiate info response matches what we got earlier */
...@@ -591,8 +616,9 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, ...@@ -591,8 +616,9 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
u16 blob_length = 0; u16 blob_length = 0;
struct key *spnego_key = NULL; struct key *spnego_key = NULL;
char *security_blob = NULL; char *security_blob = NULL;
char *ntlmssp_blob = NULL; unsigned char *ntlmssp_blob = NULL;
bool use_spnego = false; /* else use raw ntlmssp */ bool use_spnego = false; /* else use raw ntlmssp */
u64 previous_session = ses->Suid;
cifs_dbg(FYI, "Session Setup\n"); cifs_dbg(FYI, "Session Setup\n");
...@@ -630,6 +656,10 @@ ssetup_ntlmssp_authenticate: ...@@ -630,6 +656,10 @@ ssetup_ntlmssp_authenticate:
return rc; return rc;
req->hdr.SessionId = 0; /* First session, not a reauthenticate */ req->hdr.SessionId = 0; /* First session, not a reauthenticate */
/* if reconnect, we need to send previous sess id, otherwise it is 0 */
req->PreviousSessionId = previous_session;
req->Flags = 0; /* MBZ */ req->Flags = 0; /* MBZ */
/* to enable echos and oplocks */ /* to enable echos and oplocks */
req->hdr.CreditRequest = cpu_to_le16(3); req->hdr.CreditRequest = cpu_to_le16(3);
...@@ -716,13 +746,7 @@ ssetup_ntlmssp_authenticate: ...@@ -716,13 +746,7 @@ ssetup_ntlmssp_authenticate:
iov[1].iov_len = blob_length; iov[1].iov_len = blob_length;
} else if (phase == NtLmAuthenticate) { } else if (phase == NtLmAuthenticate) {
req->hdr.SessionId = ses->Suid; req->hdr.SessionId = ses->Suid;
ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500, rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
GFP_KERNEL);
if (ntlmssp_blob == NULL) {
rc = -ENOMEM;
goto ssetup_exit;
}
rc = build_ntlmssp_auth_blob(ntlmssp_blob, &blob_length, ses,
nls_cp); nls_cp);
if (rc) { if (rc) {
cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n",
...@@ -919,9 +943,6 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, ...@@ -919,9 +943,6 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
else else
return -EIO; return -EIO;
if (tcon && tcon->bad_network_name)
return -ENOENT;
if ((tcon && tcon->seal) && if ((tcon && tcon->seal) &&
((ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) == 0)) { ((ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) == 0)) {
cifs_dbg(VFS, "encryption requested but no server support"); cifs_dbg(VFS, "encryption requested but no server support");
...@@ -939,6 +960,10 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, ...@@ -939,6 +960,10 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
return -EINVAL; return -EINVAL;
} }
/* SMB2 TREE_CONNECT request must be called with TreeId == 0 */
if (tcon)
tcon->tid = 0;
rc = small_smb2_init(SMB2_TREE_CONNECT, tcon, (void **) &req); rc = small_smb2_init(SMB2_TREE_CONNECT, tcon, (void **) &req);
if (rc) { if (rc) {
kfree(unc_path); kfree(unc_path);
...@@ -1019,8 +1044,6 @@ tcon_exit: ...@@ -1019,8 +1044,6 @@ tcon_exit:
tcon_error_exit: tcon_error_exit:
if (rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) { if (rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) {
cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree);
if (tcon)
tcon->bad_network_name = true;
} }
goto tcon_exit; goto tcon_exit;
} }
...@@ -1173,7 +1196,7 @@ create_durable_v2_buf(struct cifs_fid *pfid) ...@@ -1173,7 +1196,7 @@ create_durable_v2_buf(struct cifs_fid *pfid)
buf->dcontext.Timeout = 0; /* Should this be configurable by workload */ buf->dcontext.Timeout = 0; /* Should this be configurable by workload */
buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
get_random_bytes(buf->dcontext.CreateGuid, 16); generate_random_uuid(buf->dcontext.CreateGuid);
memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
...@@ -1506,8 +1529,12 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, ...@@ -1506,8 +1529,12 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
* than one credit. Windows typically sets this smaller, but for some * than one credit. Windows typically sets this smaller, but for some
* ioctls it may be useful to allow server to send more. No point * ioctls it may be useful to allow server to send more. No point
* limiting what the server can send as long as fits in one credit * limiting what the server can send as long as fits in one credit
* Unfortunately - we can not handle more than CIFS_MAX_MSG_SIZE
* (by default, note that it can be overridden to make max larger)
* in responses (except for read responses which can be bigger.
* We may want to bump this limit up
*/ */
req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */ req->MaxOutputResponse = cpu_to_le32(CIFSMaxBufSize);
if (is_fsctl) if (is_fsctl)
req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
...@@ -1809,6 +1836,54 @@ smb2_echo_callback(struct mid_q_entry *mid) ...@@ -1809,6 +1836,54 @@ smb2_echo_callback(struct mid_q_entry *mid)
add_credits(server, credits_received, CIFS_ECHO_OP); add_credits(server, credits_received, CIFS_ECHO_OP);
} }
void smb2_reconnect_server(struct work_struct *work)
{
struct TCP_Server_Info *server = container_of(work,
struct TCP_Server_Info, reconnect.work);
struct cifs_ses *ses;
struct cifs_tcon *tcon, *tcon2;
struct list_head tmp_list;
int tcon_exist = false;
/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
mutex_lock(&server->reconnect_mutex);
INIT_LIST_HEAD(&tmp_list);
cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n");
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
if (tcon->need_reconnect) {
tcon->tc_count++;
list_add_tail(&tcon->rlist, &tmp_list);
tcon_exist = true;
}
}
}
/*
* Get the reference to server struct to be sure that the last call of
* cifs_put_tcon() in the loop below won't release the server pointer.
*/
if (tcon_exist)
server->srv_count++;
spin_unlock(&cifs_tcp_ses_lock);
list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
smb2_reconnect(SMB2_ECHO, tcon);
list_del_init(&tcon->rlist);
cifs_put_tcon(tcon);
}
cifs_dbg(FYI, "Reconnecting tcons finished\n");
mutex_unlock(&server->reconnect_mutex);
/* now we can safely release srv struct */
if (tcon_exist)
cifs_put_tcp_session(server, 1);
}
int int
SMB2_echo(struct TCP_Server_Info *server) SMB2_echo(struct TCP_Server_Info *server)
{ {
...@@ -1820,6 +1895,12 @@ SMB2_echo(struct TCP_Server_Info *server) ...@@ -1820,6 +1895,12 @@ SMB2_echo(struct TCP_Server_Info *server)
cifs_dbg(FYI, "In echo request\n"); cifs_dbg(FYI, "In echo request\n");
if (server->tcpStatus == CifsNeedNegotiate) {
/* No need to send echo on newly established connections */
queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
return rc;
}
rc = small_smb2_init(SMB2_ECHO, NULL, (void **)&req); rc = small_smb2_init(SMB2_ECHO, NULL, (void **)&req);
if (rc) if (rc)
return rc; return rc;
...@@ -2038,6 +2119,7 @@ smb2_async_readv(struct cifs_readdata *rdata) ...@@ -2038,6 +2119,7 @@ smb2_async_readv(struct cifs_readdata *rdata)
if (rdata->credits) { if (rdata->credits) {
buf->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, buf->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
SMB2_MAX_BUFFER_SIZE)); SMB2_MAX_BUFFER_SIZE));
buf->CreditRequest = buf->CreditCharge;
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
server->credits += rdata->credits - server->credits += rdata->credits -
le16_to_cpu(buf->CreditCharge); le16_to_cpu(buf->CreditCharge);
...@@ -2224,6 +2306,7 @@ smb2_async_writev(struct cifs_writedata *wdata, ...@@ -2224,6 +2306,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
if (wdata->credits) { if (wdata->credits) {
req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
SMB2_MAX_BUFFER_SIZE)); SMB2_MAX_BUFFER_SIZE));
req->hdr.CreditRequest = req->hdr.CreditCharge;
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
server->credits += wdata->credits - server->credits += wdata->credits -
le16_to_cpu(req->hdr.CreditCharge); le16_to_cpu(req->hdr.CreditCharge);
...@@ -2692,8 +2775,8 @@ copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, ...@@ -2692,8 +2775,8 @@ copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) * kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) *
le32_to_cpu(pfs_inf->SectorsPerAllocationUnit); le32_to_cpu(pfs_inf->SectorsPerAllocationUnit);
kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits); kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits);
kst->f_bfree = le64_to_cpu(pfs_inf->ActualAvailableAllocationUnits); kst->f_bfree = kst->f_bavail =
kst->f_bavail = le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits); le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits);
return; return;
} }
......
...@@ -82,8 +82,8 @@ ...@@ -82,8 +82,8 @@
#define NUMBER_OF_SMB2_COMMANDS 0x0013 #define NUMBER_OF_SMB2_COMMANDS 0x0013
/* BB FIXME - analyze following length BB */ /* 4 len + 52 transform hdr + 64 hdr + 56 create rsp */
#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ #define MAX_SMB2_HDR_SIZE 0x00b0
#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) #define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe)
...@@ -276,7 +276,7 @@ struct smb2_sess_setup_req { ...@@ -276,7 +276,7 @@ struct smb2_sess_setup_req {
__le32 Channel; __le32 Channel;
__le16 SecurityBufferOffset; __le16 SecurityBufferOffset;
__le16 SecurityBufferLength; __le16 SecurityBufferLength;
__le64 PreviousSessionId; __u64 PreviousSessionId;
__u8 Buffer[1]; /* variable length GSS security buffer */ __u8 Buffer[1]; /* variable length GSS security buffer */
} __packed; } __packed;
......
...@@ -47,6 +47,10 @@ extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses, ...@@ -47,6 +47,10 @@ extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
struct smb_rqst *rqst); struct smb_rqst *rqst);
extern struct mid_q_entry *smb2_setup_async_request( extern struct mid_q_entry *smb2_setup_async_request(
struct TCP_Server_Info *server, struct smb_rqst *rqst); struct TCP_Server_Info *server, struct smb_rqst *rqst);
extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server,
__u64 ses_id);
extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server,
__u64 ses_id, __u32 tid);
extern int smb2_calc_signature(struct smb_rqst *rqst, extern int smb2_calc_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server); struct TCP_Server_Info *server);
extern int smb3_calc_signature(struct smb_rqst *rqst, extern int smb3_calc_signature(struct smb_rqst *rqst,
...@@ -95,6 +99,7 @@ extern int smb2_open_file(const unsigned int xid, ...@@ -95,6 +99,7 @@ extern int smb2_open_file(const unsigned int xid,
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);
extern void smb2_reconnect_server(struct work_struct *work);
/* /*
* SMB2 Worker functions - most of protocol specific implementation details * SMB2 Worker functions - most of protocol specific implementation details
...@@ -156,6 +161,9 @@ extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -156,6 +161,9 @@ extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
const u64 persistent_fid, const u64 volatile_fid, const u64 persistent_fid, const u64 volatile_fid,
const __u8 oplock_level); const __u8 oplock_level);
extern int smb2_handle_cancelled_mid(char *buffer,
struct TCP_Server_Info *server);
void smb2_cancelled_close_fid(struct work_struct *work);
extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id, u64 persistent_file_id, u64 volatile_file_id,
struct kstatfs *FSData); struct kstatfs *FSData);
......
...@@ -115,22 +115,68 @@ smb3_crypto_shash_allocate(struct TCP_Server_Info *server) ...@@ -115,22 +115,68 @@ smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
} }
static struct cifs_ses * static struct cifs_ses *
smb2_find_smb_ses(struct smb2_hdr *smb2hdr, struct TCP_Server_Info *server) smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
{ {
struct cifs_ses *ses; struct cifs_ses *ses;
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
if (ses->Suid != smb2hdr->SessionId) if (ses->Suid != ses_id)
continue; continue;
spin_unlock(&cifs_tcp_ses_lock);
return ses; return ses;
} }
return NULL;
}
struct cifs_ses *
smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id)
{
struct cifs_ses *ses;
spin_lock(&cifs_tcp_ses_lock);
ses = smb2_find_smb_ses_unlocked(server, ses_id);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return ses;
}
static struct cifs_tcon *
smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid)
{
struct cifs_tcon *tcon;
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
if (tcon->tid != tid)
continue;
++tcon->tc_count;
return tcon;
}
return NULL; return NULL;
} }
/*
* Obtain tcon corresponding to the tid in the given
* cifs_ses
*/
struct cifs_tcon *
smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid)
{
struct cifs_ses *ses;
struct cifs_tcon *tcon;
spin_lock(&cifs_tcp_ses_lock);
ses = smb2_find_smb_ses_unlocked(server, ses_id);
if (!ses) {
spin_unlock(&cifs_tcp_ses_lock);
return NULL;
}
tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid);
spin_unlock(&cifs_tcp_ses_lock);
return tcon;
}
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)
...@@ -143,7 +189,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -143,7 +189,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
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; struct cifs_ses *ses;
ses = smb2_find_smb_ses(smb2_pdu, server); ses = smb2_find_smb_ses(server, smb2_pdu->SessionId);
if (!ses) { if (!ses) {
cifs_dbg(VFS, "%s: Could not find session\n", __func__); cifs_dbg(VFS, "%s: Could not find session\n", __func__);
return 0; return 0;
...@@ -314,7 +360,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -314,7 +360,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
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; struct cifs_ses *ses;
ses = smb2_find_smb_ses(smb2_pdu, server); ses = smb2_find_smb_ses(server, smb2_pdu->SessionId);
if (!ses) { if (!ses) {
cifs_dbg(VFS, "%s: Could not find session\n", __func__); cifs_dbg(VFS, "%s: Could not find session\n", __func__);
return 0; return 0;
......
...@@ -786,9 +786,11 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, ...@@ -786,9 +786,11 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
rc = wait_for_response(ses->server, midQ); rc = wait_for_response(ses->server, midQ);
if (rc != 0) { if (rc != 0) {
cifs_dbg(FYI, "Cancelling wait for mid %llu\n", midQ->mid);
send_cancel(ses->server, buf, midQ); send_cancel(ses->server, buf, midQ);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (midQ->mid_state == MID_REQUEST_SUBMITTED) { if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
midQ->mid_flags |= MID_WAIT_CANCELLED;
midQ->callback = DeleteMidQEntry; midQ->callback = DeleteMidQEntry;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
cifs_small_buf_release(buf); cifs_small_buf_release(buf);
......
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