forked from Minki/linux
3 cifs/smb3 fixes, one for symlink handling and two fix multichannel issues with iterating channels, including for oplock breaks when leases are disabled
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmNnPqoACgkQiiy9cAdy T1FXpgv/fgkH48LGjRH+Pd86oZchCagEMF8Zfy59SKiNWwcuS43B+sjwrotbwT4r Nmq1LYHmGgy0b63L9L+DSwO6mxOEvm3ryJ2vxInsG1Rsebw0oSBxolPOHjYLWHKH +BsMGxLEVHWMHzFzrNJC1Fp9oGgmD6tNmdDBxBw471UK1AURfc7tg/70MmDm7lDx cTLE40Fu+ni3OZ22YL0jYIgHWkk0S1r+/lFNYLvxrZF+D7zRhnVCALbY60L/a4/T /nLViWlHAKp9UUlCJTJOXyfVV2PVkF2JEUCIPcfTvYNvDFMGLH/mLLNd6iSq4EgX HE811XfZ8HrfL+T2oTHcgNo6CkCZBtdw2zV/RivRDojxHYy/soYv0p0B54c37V3i x89/tc1KYxHNR27W+0dxT8D66cRkez0Sb4f/BKdhHfl0WaAmVpfl41XGQ3pihm/C Nb8nj/b16R9lqm27Zgu8Vy3p1LSF0d3tn/UDxIP3unoyQWEHHY7oiBlMJ6uc6qls faQSx8tv =ESTZ -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
90153f928b
@ -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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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,
|
|||||||
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)
|
|||||||
__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)
|
|||||||
|
|
||||||
/* 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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
if (cached_dir_lease_break(tcon, rsp->LeaseKey)) {
|
tlink = cifs_get_tlink(open->tlink);
|
||||||
spin_unlock(&cifs_tcp_ses_lock);
|
memcpy(lease_key, open->lease_key,
|
||||||
return true;
|
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)) {
|
||||||
|
spin_unlock(&cifs_tcp_ses_lock);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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)
|
|||||||
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
|
|||||||
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)
|
|||||||
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
|
|||||||
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 @@ out:
|
|||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user