Commit 90153f92 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '6.1-rc4-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs fixes from Steve French:
 "One symlink handling fix and two fixes foir multichannel issues with
  iterating channels, including for oplock breaks when leases are
  disabled"

* tag '6.1-rc4-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: fix use-after-free on the link name
  cifs: avoid unnecessary iteration of tcp sessions
  cifs: always iterate smb sessions using primary channel
parents 8391aa4b 542228db
...@@ -1143,8 +1143,32 @@ const struct inode_operations cifs_file_inode_ops = { ...@@ -1143,8 +1143,32 @@ const struct inode_operations cifs_file_inode_ops = {
.fiemap = cifs_fiemap, .fiemap = cifs_fiemap,
}; };
const char *cifs_get_link(struct dentry *dentry, struct inode *inode,
struct delayed_call *done)
{
char *target_path;
target_path = kmalloc(PATH_MAX, GFP_KERNEL);
if (!target_path)
return ERR_PTR(-ENOMEM);
spin_lock(&inode->i_lock);
if (likely(CIFS_I(inode)->symlink_target)) {
strscpy(target_path, CIFS_I(inode)->symlink_target, PATH_MAX);
} else {
kfree(target_path);
target_path = ERR_PTR(-EOPNOTSUPP);
}
spin_unlock(&inode->i_lock);
if (!IS_ERR(target_path))
set_delayed_call(done, kfree_link, target_path);
return target_path;
}
const struct inode_operations cifs_symlink_inode_ops = { const struct inode_operations cifs_symlink_inode_ops = {
.get_link = simple_get_link, .get_link = cifs_get_link,
.permission = cifs_permission, .permission = cifs_permission,
.listxattr = cifs_listxattr, .listxattr = cifs_listxattr,
}; };
......
...@@ -215,11 +215,6 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) ...@@ -215,11 +215,6 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
kfree(cifs_i->symlink_target); kfree(cifs_i->symlink_target);
cifs_i->symlink_target = fattr->cf_symlink_target; cifs_i->symlink_target = fattr->cf_symlink_target;
fattr->cf_symlink_target = NULL; fattr->cf_symlink_target = NULL;
if (unlikely(!cifs_i->symlink_target))
inode->i_link = ERR_PTR(-EOPNOTSUPP);
else
inode->i_link = cifs_i->symlink_target;
} }
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
......
...@@ -400,6 +400,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) ...@@ -400,6 +400,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
{ {
struct smb_hdr *buf = (struct smb_hdr *)buffer; struct smb_hdr *buf = (struct smb_hdr *)buffer;
struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
struct TCP_Server_Info *pserver;
struct cifs_ses *ses; struct cifs_ses *ses;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct cifsInodeInfo *pCifsInode; struct cifsInodeInfo *pCifsInode;
...@@ -464,9 +465,12 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) ...@@ -464,9 +465,12 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE)) if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE))
return false; return false;
/* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(srv) ? srv->primary_server : srv;
/* look up tcon based on tid & uid */ /* look up tcon based on tid & uid */
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &srv->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
if (tcon->tid != buf->Tid) if (tcon->tid != buf->Tid)
continue; continue;
......
...@@ -135,6 +135,7 @@ static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len, ...@@ -135,6 +135,7 @@ static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len,
int int
smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
{ {
struct TCP_Server_Info *pserver;
struct smb2_hdr *shdr = (struct smb2_hdr *)buf; struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
struct smb2_pdu *pdu = (struct smb2_pdu *)shdr; struct smb2_pdu *pdu = (struct smb2_pdu *)shdr;
int hdr_size = sizeof(struct smb2_hdr); int hdr_size = sizeof(struct smb2_hdr);
...@@ -143,6 +144,9 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) ...@@ -143,6 +144,9 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
__u32 calc_len; /* calculated length */ __u32 calc_len; /* calculated length */
__u64 mid; __u64 mid;
/* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
/* /*
* Add function to do table lookup of StructureSize by command * Add function to do table lookup of StructureSize by command
* ie Validate the wct via smb2_struct_sizes table above * ie Validate the wct via smb2_struct_sizes table above
...@@ -155,7 +159,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) ...@@ -155,7 +159,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
/* decrypt frame now that it is completely read in */ /* decrypt frame now that it is completely read in */
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(iter, &server->smb_ses_list, smb_ses_list) { list_for_each_entry(iter, &pserver->smb_ses_list, smb_ses_list) {
if (iter->Suid == le64_to_cpu(thdr->SessionId)) { if (iter->Suid == le64_to_cpu(thdr->SessionId)) {
ses = iter; ses = iter;
break; break;
...@@ -608,51 +612,52 @@ smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon, ...@@ -608,51 +612,52 @@ smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon,
} }
static bool static bool
smb2_is_valid_lease_break(char *buffer) smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
{ {
struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer;
struct TCP_Server_Info *server; struct TCP_Server_Info *pserver;
struct cifs_ses *ses; struct cifs_ses *ses;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct cifs_pending_open *open; struct cifs_pending_open *open;
cifs_dbg(FYI, "Checking for lease break\n"); cifs_dbg(FYI, "Checking for lease break\n");
/* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
/* look up tcon based on tid & uid */ /* look up tcon based on tid & uid */
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { spin_lock(&tcon->open_file_lock);
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)) {
if (smb2_tcon_has_lease(tcon, rsp)) {
spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
open = smb2_tcon_find_pending_open_lease(tcon,
rsp);
if (open) {
__u8 lease_key[SMB2_LEASE_KEY_SIZE];
struct tcon_link *tlink;
tlink = cifs_get_tlink(open->tlink);
memcpy(lease_key, open->lease_key,
SMB2_LEASE_KEY_SIZE);
spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
smb2_queue_pending_open_break(tlink,
lease_key,
rsp->NewLeaseState);
return true;
}
spin_unlock(&tcon->open_file_lock); spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
open = smb2_tcon_find_pending_open_lease(tcon,
rsp);
if (open) {
__u8 lease_key[SMB2_LEASE_KEY_SIZE];
struct tcon_link *tlink;
tlink = cifs_get_tlink(open->tlink);
memcpy(lease_key, open->lease_key,
SMB2_LEASE_KEY_SIZE);
spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
smb2_queue_pending_open_break(tlink,
lease_key,
rsp->NewLeaseState);
return true;
}
spin_unlock(&tcon->open_file_lock);
if (cached_dir_lease_break(tcon, rsp->LeaseKey)) { if (cached_dir_lease_break(tcon, rsp->LeaseKey)) {
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return true; return true;
}
} }
} }
} }
...@@ -671,6 +676,7 @@ bool ...@@ -671,6 +676,7 @@ bool
smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
{ {
struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer; struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer;
struct TCP_Server_Info *pserver;
struct cifs_ses *ses; struct cifs_ses *ses;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct cifsInodeInfo *cinode; struct cifsInodeInfo *cinode;
...@@ -684,16 +690,19 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -684,16 +690,19 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
if (rsp->StructureSize != if (rsp->StructureSize !=
smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
if (le16_to_cpu(rsp->StructureSize) == 44) if (le16_to_cpu(rsp->StructureSize) == 44)
return smb2_is_valid_lease_break(buffer); return smb2_is_valid_lease_break(buffer, server);
else else
return false; return false;
} }
cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel); cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel);
/* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
/* look up tcon based on tid & uid */ /* look up tcon based on tid & uid */
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
spin_lock(&tcon->open_file_lock); spin_lock(&tcon->open_file_lock);
......
...@@ -2302,14 +2302,18 @@ static void ...@@ -2302,14 +2302,18 @@ static void
smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server) smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
{ {
struct smb2_hdr *shdr = (struct smb2_hdr *)buf; struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
struct TCP_Server_Info *pserver;
struct cifs_ses *ses; struct cifs_ses *ses;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
if (shdr->Status != STATUS_NETWORK_NAME_DELETED) if (shdr->Status != STATUS_NETWORK_NAME_DELETED)
return; return;
/* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) { if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) {
spin_lock(&tcon->tc_lock); spin_lock(&tcon->tc_lock);
...@@ -4264,21 +4268,23 @@ init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign) ...@@ -4264,21 +4268,23 @@ init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign)
static int static int
smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key) smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key)
{ {
struct TCP_Server_Info *pserver;
struct cifs_ses *ses; struct cifs_ses *ses;
u8 *ses_enc_key; u8 *ses_enc_key;
/* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { if (ses->Suid == ses_id) {
if (ses->Suid == ses_id) { spin_lock(&ses->ses_lock);
spin_lock(&ses->ses_lock); ses_enc_key = enc ? ses->smb3encryptionkey :
ses_enc_key = enc ? ses->smb3encryptionkey : ses->smb3decryptionkey;
ses->smb3decryptionkey; memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE);
memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); spin_unlock(&ses->ses_lock);
spin_unlock(&ses->ses_lock); spin_unlock(&cifs_tcp_ses_lock);
spin_unlock(&cifs_tcp_ses_lock); return 0;
return 0;
}
} }
} }
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
......
...@@ -77,18 +77,19 @@ static ...@@ -77,18 +77,19 @@ static
int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
{ {
struct cifs_chan *chan; struct cifs_chan *chan;
struct TCP_Server_Info *pserver;
struct cifs_ses *ses = NULL; struct cifs_ses *ses = NULL;
struct TCP_Server_Info *it = NULL;
int i; int i;
int rc = 0; int rc = 0;
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(it, &cifs_tcp_ses_list, tcp_ses_list) { /* If server is a channel, select the primary channel */
list_for_each_entry(ses, &it->smb_ses_list, smb_ses_list) { pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
if (ses->Suid == ses_id)
goto found; list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
} if (ses->Suid == ses_id)
goto found;
} }
cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n", cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n",
__func__, ses_id); __func__, ses_id);
...@@ -136,9 +137,13 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) ...@@ -136,9 +137,13 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
static struct cifs_ses * static struct cifs_ses *
smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id) smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
{ {
struct TCP_Server_Info *pserver;
struct cifs_ses *ses; struct cifs_ses *ses;
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { /* If server is a channel, select the primary channel */
pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
if (ses->Suid != ses_id) if (ses->Suid != ses_id)
continue; continue;
++ses->ses_count; ++ses->ses_count;
......
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