2021-06-18 13:31:49 +08:00
|
|
|
// SPDX-License-Identifier: LGPL-2.1
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
*
|
2008-02-08 07:25:02 +08:00
|
|
|
* Copyright (C) International Business Machines Corp., 2002,2008
|
2005-04-17 06:20:36 +08:00
|
|
|
* Author(s): Steve French (sfrench@us.ibm.com)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/ctype.h>
|
|
|
|
#include <linux/mempool.h>
|
2017-04-26 02:52:29 +08:00
|
|
|
#include <linux/vmalloc.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "cifspdu.h"
|
|
|
|
#include "cifsglob.h"
|
|
|
|
#include "cifsproto.h"
|
|
|
|
#include "cifs_debug.h"
|
|
|
|
#include "smberr.h"
|
|
|
|
#include "nterr.h"
|
2005-04-29 13:41:06 +08:00
|
|
|
#include "cifs_unicode.h"
|
2012-01-13 02:40:50 +08:00
|
|
|
#include "smb2pdu.h"
|
2020-02-21 06:49:34 +08:00
|
|
|
#include "cifsfs.h"
|
2020-05-20 02:38:28 +08:00
|
|
|
#ifdef CONFIG_CIFS_DFS_UPCALL
|
|
|
|
#include "dns_resolve.h"
|
|
|
|
#endif
|
2020-12-13 03:40:50 +08:00
|
|
|
#include "fs_context.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
extern mempool_t *cifs_sm_req_poolp;
|
|
|
|
extern mempool_t *cifs_req_poolp;
|
|
|
|
|
2007-07-10 09:16:18 +08:00
|
|
|
/* The xid serves as a useful identifier for each incoming vfs request,
|
|
|
|
in a similar way to the mid which is useful to track each sent smb,
|
|
|
|
and CurrentXid can also provide a running counter (although it
|
|
|
|
will eventually wrap past zero) of the total vfs operations handled
|
2005-04-17 06:20:36 +08:00
|
|
|
since the cifs fs was mounted */
|
|
|
|
|
|
|
|
unsigned int
|
2012-06-20 15:21:16 +08:00
|
|
|
_get_xid(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned int xid;
|
|
|
|
|
|
|
|
spin_lock(&GlobalMid_Lock);
|
|
|
|
GlobalTotalActiveXid++;
|
2007-07-13 08:33:32 +08:00
|
|
|
|
|
|
|
/* keep high water mark for number of simultaneous ops in filesystem */
|
2005-04-17 06:20:36 +08:00
|
|
|
if (GlobalTotalActiveXid > GlobalMaxActiveXid)
|
2007-07-13 08:33:32 +08:00
|
|
|
GlobalMaxActiveXid = GlobalTotalActiveXid;
|
2007-07-08 03:25:05 +08:00
|
|
|
if (GlobalTotalActiveXid > 65000)
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "warning: more than 65000 requests active\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
xid = GlobalCurrentXid++;
|
|
|
|
spin_unlock(&GlobalMid_Lock);
|
|
|
|
return xid;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-06-20 15:21:16 +08:00
|
|
|
_free_xid(unsigned int xid)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
spin_lock(&GlobalMid_Lock);
|
2007-07-08 03:25:05 +08:00
|
|
|
/* if (GlobalTotalActiveXid == 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
BUG(); */
|
|
|
|
GlobalTotalActiveXid--;
|
|
|
|
spin_unlock(&GlobalMid_Lock);
|
|
|
|
}
|
|
|
|
|
2011-05-27 12:34:02 +08:00
|
|
|
struct cifs_ses *
|
2005-04-17 06:20:36 +08:00
|
|
|
sesInfoAlloc(void)
|
|
|
|
{
|
2011-05-27 12:34:02 +08:00
|
|
|
struct cifs_ses *ret_buf;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-05-27 12:34:02 +08:00
|
|
|
ret_buf = kzalloc(sizeof(struct cifs_ses), GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ret_buf) {
|
|
|
|
atomic_inc(&sesInfoAllocCount);
|
2022-04-07 21:15:49 +08:00
|
|
|
ret_buf->ses_status = SES_NEW;
|
2008-11-15 02:53:46 +08:00
|
|
|
++ret_buf->ses_count;
|
|
|
|
INIT_LIST_HEAD(&ret_buf->smb_ses_list);
|
2008-11-16 00:12:47 +08:00
|
|
|
INIT_LIST_HEAD(&ret_buf->tcon_list);
|
2010-02-25 13:36:46 +08:00
|
|
|
mutex_init(&ret_buf->session_mutex);
|
2018-06-14 21:43:18 +08:00
|
|
|
spin_lock_init(&ret_buf->iface_lock);
|
2021-07-19 18:54:46 +08:00
|
|
|
spin_lock_init(&ret_buf->chan_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return ret_buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-05-27 12:34:02 +08:00
|
|
|
sesInfoFree(struct cifs_ses *buf_to_free)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (buf_to_free == NULL) {
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "Null buffer passed to sesInfoFree\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
atomic_dec(&sesInfoAllocCount);
|
2005-11-07 17:01:34 +08:00
|
|
|
kfree(buf_to_free->serverOS);
|
|
|
|
kfree(buf_to_free->serverDomain);
|
|
|
|
kfree(buf_to_free->serverNOS);
|
2020-08-07 14:18:13 +08:00
|
|
|
kfree_sensitive(buf_to_free->password);
|
2011-02-25 15:11:56 +08:00
|
|
|
kfree(buf_to_free->user_name);
|
2006-06-01 06:40:51 +08:00
|
|
|
kfree(buf_to_free->domainName);
|
2020-08-07 14:18:13 +08:00
|
|
|
kfree_sensitive(buf_to_free->auth_key.response);
|
2018-06-14 21:43:18 +08:00
|
|
|
kfree(buf_to_free->iface_list);
|
2020-08-07 14:18:13 +08:00
|
|
|
kfree_sensitive(buf_to_free);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-05-27 12:34:02 +08:00
|
|
|
struct cifs_tcon *
|
2005-04-17 06:20:36 +08:00
|
|
|
tconInfoAlloc(void)
|
|
|
|
{
|
2011-05-27 12:34:02 +08:00
|
|
|
struct cifs_tcon *ret_buf;
|
2018-12-21 13:50:48 +08:00
|
|
|
|
|
|
|
ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
|
|
|
|
if (!ret_buf)
|
|
|
|
return NULL;
|
|
|
|
ret_buf->crfid.fid = kzalloc(sizeof(*ret_buf->crfid.fid), GFP_KERNEL);
|
|
|
|
if (!ret_buf->crfid.fid) {
|
|
|
|
kfree(ret_buf);
|
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2022-05-10 07:42:07 +08:00
|
|
|
INIT_LIST_HEAD(&ret_buf->crfid.dirents.entries);
|
|
|
|
mutex_init(&ret_buf->crfid.dirents.de_mutex);
|
2018-12-21 13:50:48 +08:00
|
|
|
|
|
|
|
atomic_inc(&tconInfoAllocCount);
|
smb3: cleanup and clarify status of tree connections
Currently the way the tid (tree connection) status is tracked
is confusing. The same enum is used for structs cifs_tcon
and cifs_ses and TCP_Server_info, but each of these three has
different states that they transition among. The current
code also unnecessarily uses camelCase.
Convert from use of statusEnum to a new tid_status_enum for
tree connections. The valid states for a tid are:
TID_NEW = 0,
TID_GOOD,
TID_EXITING,
TID_NEED_RECON,
TID_NEED_TCON,
TID_IN_TCON,
TID_NEED_FILES_INVALIDATE, /* unused, considering removing in future */
TID_IN_FILES_INVALIDATE
It also removes CifsNeedTcon, CifsInTcon, CifsNeedFilesInvalidate and
CifsInFilesInvalidate from the statusEnum used for session and
TCP_Server_Info since they are not relevant for those.
A follow on patch will fix the places where we use the
tcon->need_reconnect flag to be more consistent with the tid->status.
Also fixes a bug that was:
Reported-by: kernel test robot <lkp@intel.com>
Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
2022-03-28 05:07:30 +08:00
|
|
|
ret_buf->status = TID_NEW;
|
2018-12-21 13:50:48 +08:00
|
|
|
++ret_buf->tc_count;
|
|
|
|
INIT_LIST_HEAD(&ret_buf->openFileList);
|
|
|
|
INIT_LIST_HEAD(&ret_buf->tcon_list);
|
|
|
|
spin_lock_init(&ret_buf->open_file_lock);
|
|
|
|
mutex_init(&ret_buf->crfid.fid_mutex);
|
|
|
|
spin_lock_init(&ret_buf->stat_lock);
|
|
|
|
atomic_set(&ret_buf->num_local_opens, 0);
|
|
|
|
atomic_set(&ret_buf->num_remote_opens, 0);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret_buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-05-27 12:34:02 +08:00
|
|
|
tconInfoFree(struct cifs_tcon *buf_to_free)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (buf_to_free == NULL) {
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
atomic_dec(&tconInfoAllocCount);
|
2005-11-07 17:01:34 +08:00
|
|
|
kfree(buf_to_free->nativeFileSystem);
|
2020-08-07 14:18:13 +08:00
|
|
|
kfree_sensitive(buf_to_free->password);
|
2018-06-14 04:48:35 +08:00
|
|
|
kfree(buf_to_free->crfid.fid);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(buf_to_free);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct smb_hdr *
|
|
|
|
cifs_buf_get(void)
|
|
|
|
{
|
|
|
|
struct smb_hdr *ret_buf = NULL;
|
2012-01-13 02:40:50 +08:00
|
|
|
/*
|
|
|
|
* SMB2 header is bigger than CIFS one - no problems to clean some
|
|
|
|
* more bytes for CIFS.
|
|
|
|
*/
|
2021-11-05 07:39:01 +08:00
|
|
|
size_t buf_size = sizeof(struct smb2_hdr);
|
2017-07-09 07:48:15 +08:00
|
|
|
|
2012-01-13 02:40:50 +08:00
|
|
|
/*
|
|
|
|
* We could use negotiated size instead of max_msgsize -
|
|
|
|
* but it may be more efficient to always alloc same size
|
|
|
|
* albeit slightly larger than necessary and maxbuffersize
|
|
|
|
* defaults to this and can not be bigger.
|
|
|
|
*/
|
2008-09-15 18:22:54 +08:00
|
|
|
ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* clear the first few header bytes */
|
|
|
|
/* for most paths, more is cleared in header_assemble */
|
2017-04-10 10:08:53 +08:00
|
|
|
memset(ret_buf, 0, buf_size + 3);
|
|
|
|
atomic_inc(&bufAllocCount);
|
2005-12-04 05:58:57 +08:00
|
|
|
#ifdef CONFIG_CIFS_STATS2
|
2017-04-10 10:08:53 +08:00
|
|
|
atomic_inc(&totBufAllocCount);
|
2005-12-04 05:58:57 +08:00
|
|
|
#endif /* CONFIG_CIFS_STATS2 */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return ret_buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cifs_buf_release(void *buf_to_free)
|
|
|
|
{
|
|
|
|
if (buf_to_free == NULL) {
|
2013-05-05 11:12:25 +08:00
|
|
|
/* cifs_dbg(FYI, "Null buffer passed to cifs_buf_release\n");*/
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
2007-07-10 09:16:18 +08:00
|
|
|
mempool_free(buf_to_free, cifs_req_poolp);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
atomic_dec(&bufAllocCount);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct smb_hdr *
|
|
|
|
cifs_small_buf_get(void)
|
|
|
|
{
|
|
|
|
struct smb_hdr *ret_buf = NULL;
|
|
|
|
|
2007-07-10 09:16:18 +08:00
|
|
|
/* We could use negotiated size instead of max_msgsize -
|
|
|
|
but it may be more efficient to always alloc same size
|
|
|
|
albeit slightly larger than necessary and maxbuffersize
|
2005-04-17 06:20:36 +08:00
|
|
|
defaults to this and can not be bigger */
|
2008-09-15 18:22:54 +08:00
|
|
|
ret_buf = mempool_alloc(cifs_sm_req_poolp, GFP_NOFS);
|
2005-04-17 06:20:36 +08:00
|
|
|
/* No need to clear memory here, cleared in header assemble */
|
|
|
|
/* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/
|
2017-04-10 10:08:53 +08:00
|
|
|
atomic_inc(&smBufAllocCount);
|
2005-12-04 05:58:57 +08:00
|
|
|
#ifdef CONFIG_CIFS_STATS2
|
2017-04-10 10:08:53 +08:00
|
|
|
atomic_inc(&totSmBufAllocCount);
|
2005-12-04 05:58:57 +08:00
|
|
|
#endif /* CONFIG_CIFS_STATS2 */
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret_buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cifs_small_buf_release(void *buf_to_free)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (buf_to_free == NULL) {
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "Null buffer passed to cifs_small_buf_release\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
2007-07-10 09:16:18 +08:00
|
|
|
mempool_free(buf_to_free, cifs_sm_req_poolp);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
atomic_dec(&smBufAllocCount);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-16 22:35:24 +08:00
|
|
|
void
|
|
|
|
free_rsp_buf(int resp_buftype, void *rsp)
|
|
|
|
{
|
|
|
|
if (resp_buftype == CIFS_SMALL_BUFFER)
|
|
|
|
cifs_small_buf_release(rsp);
|
|
|
|
else if (resp_buftype == CIFS_LARGE_BUFFER)
|
|
|
|
cifs_buf_release(rsp);
|
|
|
|
}
|
|
|
|
|
2005-08-18 03:38:22 +08:00
|
|
|
/* NB: MID can not be set if treeCon not passed in, in that
|
|
|
|
case it is responsbility of caller to set the mid */
|
2005-04-17 06:20:36 +08:00
|
|
|
void
|
|
|
|
header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
2011-05-27 12:34:02 +08:00
|
|
|
const struct cifs_tcon *treeCon, int word_count
|
2005-04-17 06:20:36 +08:00
|
|
|
/* length of fixed section (word count) in two byte units */)
|
|
|
|
{
|
|
|
|
char *temp = (char *) buffer;
|
|
|
|
|
2007-07-10 09:16:18 +08:00
|
|
|
memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-04-29 13:40:20 +08:00
|
|
|
buffer->smb_buf_length = cpu_to_be32(
|
2007-10-26 05:17:17 +08:00
|
|
|
(2 * word_count) + sizeof(struct smb_hdr) -
|
2005-04-17 06:20:36 +08:00
|
|
|
4 /* RFC 1001 length field does not count */ +
|
2011-04-29 13:40:20 +08:00
|
|
|
2 /* for bcc field itself */) ;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
buffer->Protocol[0] = 0xFF;
|
|
|
|
buffer->Protocol[1] = 'S';
|
|
|
|
buffer->Protocol[2] = 'M';
|
|
|
|
buffer->Protocol[3] = 'B';
|
|
|
|
buffer->Command = smb_command;
|
|
|
|
buffer->Flags = 0x00; /* case sensitive */
|
|
|
|
buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES;
|
|
|
|
buffer->Pid = cpu_to_le16((__u16)current->tgid);
|
|
|
|
buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
|
|
|
|
if (treeCon) {
|
|
|
|
buffer->Tid = treeCon->tid;
|
|
|
|
if (treeCon->ses) {
|
|
|
|
if (treeCon->ses->capabilities & CAP_UNICODE)
|
|
|
|
buffer->Flags2 |= SMBFLG2_UNICODE;
|
2008-02-08 07:25:02 +08:00
|
|
|
if (treeCon->ses->capabilities & CAP_STATUS32)
|
2005-04-17 06:20:36 +08:00
|
|
|
buffer->Flags2 |= SMBFLG2_ERR_STATUS;
|
2008-02-08 07:25:02 +08:00
|
|
|
|
2005-08-18 03:38:22 +08:00
|
|
|
/* Uid is not converted */
|
|
|
|
buffer->Uid = treeCon->ses->Suid;
|
2021-09-24 07:52:40 +08:00
|
|
|
if (treeCon->ses->server)
|
|
|
|
buffer->Mid = get_next_mid(treeCon->ses->server);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)
|
|
|
|
buffer->Flags2 |= SMBFLG2_DFS;
|
2005-08-20 02:04:29 +08:00
|
|
|
if (treeCon->nocase)
|
|
|
|
buffer->Flags |= SMBFLG_CASELESS;
|
2007-07-08 03:25:05 +08:00
|
|
|
if ((treeCon->ses) && (treeCon->ses->server))
|
2013-05-26 19:01:00 +08:00
|
|
|
if (treeCon->ses->server->sign)
|
2005-04-17 06:20:36 +08:00
|
|
|
buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* endian conversion of flags is now done just before sending */
|
|
|
|
buffer->WordCount = (char) word_count;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-09-29 03:43:08 +08:00
|
|
|
static int
|
2013-10-16 23:09:49 +08:00
|
|
|
check_smb_hdr(struct smb_hdr *smb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-01-29 04:05:42 +08:00
|
|
|
/* does it have the right SMB "signature" ? */
|
|
|
|
if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) {
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n",
|
|
|
|
*(unsigned int *)smb->Protocol);
|
2011-01-29 04:05:42 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if it's a response then accept */
|
|
|
|
if (smb->Flags & SMBFLG_RESPONSE)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* only one valid case where server sends us request */
|
|
|
|
if (smb->Command == SMB_COM_LOCKING_ANDX)
|
|
|
|
return 0;
|
|
|
|
|
2013-11-03 01:50:34 +08:00
|
|
|
cifs_dbg(VFS, "Server sent request, not response. mid=%u\n",
|
|
|
|
get_mid(smb));
|
2005-04-17 06:20:36 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2015-12-19 03:05:30 +08:00
|
|
|
checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2012-03-24 02:28:02 +08:00
|
|
|
struct smb_hdr *smb = (struct smb_hdr *)buf;
|
2011-10-11 18:41:32 +08:00
|
|
|
__u32 rfclen = be32_to_cpu(smb->smb_buf_length);
|
2005-10-11 02:48:26 +08:00
|
|
|
__u32 clc_len; /* calculated length */
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n",
|
|
|
|
total_read, rfclen);
|
2006-10-13 01:49:24 +08:00
|
|
|
|
2011-10-11 18:41:32 +08:00
|
|
|
/* is this frame too small to even get to a BCC? */
|
|
|
|
if (total_read < 2 + sizeof(struct smb_hdr)) {
|
|
|
|
if ((total_read >= sizeof(struct smb_hdr) - 1)
|
2005-04-17 06:20:36 +08:00
|
|
|
&& (smb->Status.CifsError != 0)) {
|
2011-10-11 18:41:32 +08:00
|
|
|
/* it's an error return */
|
2006-10-13 01:49:24 +08:00
|
|
|
smb->WordCount = 0;
|
|
|
|
/* some error cases do not return wct and bcc */
|
|
|
|
return 0;
|
2011-10-11 18:41:32 +08:00
|
|
|
} else if ((total_read == sizeof(struct smb_hdr) + 1) &&
|
2006-10-13 01:49:24 +08:00
|
|
|
(smb->WordCount == 0)) {
|
2007-07-10 09:16:18 +08:00
|
|
|
char *tmp = (char *)smb;
|
2006-10-13 01:49:24 +08:00
|
|
|
/* Need to work around a bug in two servers here */
|
|
|
|
/* First, check if the part of bcc they sent was zero */
|
|
|
|
if (tmp[sizeof(struct smb_hdr)] == 0) {
|
|
|
|
/* some servers return only half of bcc
|
|
|
|
* on simple responses (wct, bcc both zero)
|
|
|
|
* in particular have seen this on
|
|
|
|
* ulogoffX and FindClose. This leaves
|
|
|
|
* one byte of bcc potentially unitialized
|
|
|
|
*/
|
|
|
|
/* zero rest of bcc */
|
|
|
|
tmp[sizeof(struct smb_hdr)+1] = 0;
|
2006-03-02 08:07:08 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n");
|
2006-10-13 01:49:24 +08:00
|
|
|
} else {
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(VFS, "Length less than smb header size\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-10-11 18:41:32 +08:00
|
|
|
return -EIO;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-10-11 18:41:32 +08:00
|
|
|
/* otherwise, there is enough to get to the BCC */
|
2013-10-16 23:09:49 +08:00
|
|
|
if (check_smb_hdr(smb))
|
2011-10-11 18:41:32 +08:00
|
|
|
return -EIO;
|
2018-04-23 05:30:12 +08:00
|
|
|
clc_len = smbCalcSize(smb, server);
|
2006-02-24 14:15:11 +08:00
|
|
|
|
2011-10-11 18:41:32 +08:00
|
|
|
if (4 + rfclen != total_read) {
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n",
|
|
|
|
rfclen);
|
2011-10-11 18:41:32 +08:00
|
|
|
return -EIO;
|
2006-02-24 14:15:11 +08:00
|
|
|
}
|
|
|
|
|
2011-10-11 18:41:32 +08:00
|
|
|
if (4 + rfclen != clc_len) {
|
2013-11-03 01:50:34 +08:00
|
|
|
__u16 mid = get_mid(smb);
|
2006-02-24 14:15:11 +08:00
|
|
|
/* check if bcc wrapped around for large read responses */
|
2011-10-11 18:41:32 +08:00
|
|
|
if ((rfclen > 64 * 1024) && (rfclen > clc_len)) {
|
2006-02-24 14:15:11 +08:00
|
|
|
/* check if lengths match mod 64K */
|
2011-10-11 18:41:32 +08:00
|
|
|
if (((4 + rfclen) & 0xFFFF) == (clc_len & 0xFFFF))
|
2007-07-10 09:16:18 +08:00
|
|
|
return 0; /* bcc wrapped */
|
2006-02-24 14:15:11 +08:00
|
|
|
}
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n",
|
2013-11-03 01:50:34 +08:00
|
|
|
clc_len, 4 + rfclen, mid);
|
2011-01-31 22:14:17 +08:00
|
|
|
|
2011-10-11 18:41:32 +08:00
|
|
|
if (4 + rfclen < clc_len) {
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n",
|
2013-11-03 01:50:34 +08:00
|
|
|
rfclen, mid);
|
2011-10-11 18:41:32 +08:00
|
|
|
return -EIO;
|
|
|
|
} else if (rfclen > clc_len + 512) {
|
2011-01-31 22:14:17 +08:00
|
|
|
/*
|
|
|
|
* Some servers (Windows XP in particular) send more
|
|
|
|
* data than the lengths in the SMB packet would
|
|
|
|
* indicate on certain calls (byte range locks and
|
|
|
|
* trans2 find first calls in particular). While the
|
|
|
|
* client can handle such a frame by ignoring the
|
|
|
|
* trailing data, we choose limit the amount of extra
|
|
|
|
* data to 512 bytes.
|
|
|
|
*/
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n",
|
2013-11-03 01:50:34 +08:00
|
|
|
rfclen, mid);
|
2011-10-11 18:41:32 +08:00
|
|
|
return -EIO;
|
2006-03-02 08:07:08 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2005-09-22 13:05:57 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-04-29 08:06:05 +08:00
|
|
|
|
|
|
|
bool
|
2012-03-24 02:28:02 +08:00
|
|
|
is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
|
2007-07-10 09:16:18 +08:00
|
|
|
{
|
2012-03-24 02:28:02 +08:00
|
|
|
struct smb_hdr *buf = (struct smb_hdr *)buffer;
|
2007-07-10 09:16:18 +08:00
|
|
|
struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
|
2008-11-16 00:12:47 +08:00
|
|
|
struct list_head *tmp, *tmp1, *tmp2;
|
2011-05-27 12:34:02 +08:00
|
|
|
struct cifs_ses *ses;
|
|
|
|
struct cifs_tcon *tcon;
|
2008-11-16 00:12:47 +08:00
|
|
|
struct cifsInodeInfo *pCifsInode;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct cifsFileInfo *netfile;
|
|
|
|
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "Checking for oplock break or dnotify response\n");
|
2007-07-08 03:25:05 +08:00
|
|
|
if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
|
2005-04-17 06:20:36 +08:00
|
|
|
(pSMB->hdr.Flags & SMBFLG_RESPONSE)) {
|
2007-07-10 09:16:18 +08:00
|
|
|
struct smb_com_transaction_change_notify_rsp *pSMBr =
|
2005-04-17 06:20:36 +08:00
|
|
|
(struct smb_com_transaction_change_notify_rsp *)buf;
|
2007-07-10 09:16:18 +08:00
|
|
|
struct file_notify_information *pnotify;
|
2005-04-17 06:20:36 +08:00
|
|
|
__u32 data_offset = 0;
|
2018-09-06 17:47:01 +08:00
|
|
|
size_t len = srv->total_read - sizeof(pSMBr->hdr.smb_buf_length);
|
|
|
|
|
2011-05-04 20:05:26 +08:00
|
|
|
if (get_bcc(buf) > sizeof(struct file_notify_information)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
data_offset = le32_to_cpu(pSMBr->DataOffset);
|
|
|
|
|
2018-09-06 17:47:01 +08:00
|
|
|
if (data_offset >
|
|
|
|
len - sizeof(struct file_notify_information)) {
|
2020-04-15 13:42:53 +08:00
|
|
|
cifs_dbg(FYI, "Invalid data_offset %u\n",
|
2018-09-06 17:47:01 +08:00
|
|
|
data_offset);
|
|
|
|
return true;
|
|
|
|
}
|
2006-06-01 06:40:51 +08:00
|
|
|
pnotify = (struct file_notify_information *)
|
|
|
|
((char *)&pSMBr->hdr.Protocol + data_offset);
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "dnotify on %s Action: 0x%x\n",
|
2010-04-21 11:50:45 +08:00
|
|
|
pnotify->FileName, pnotify->Action);
|
2007-07-10 09:16:18 +08:00
|
|
|
/* cifs_dump_mem("Rcvd notify Data: ",buf,
|
2006-06-01 06:40:51 +08:00
|
|
|
sizeof(struct smb_hdr)+60); */
|
2008-04-29 08:06:05 +08:00
|
|
|
return true;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-07-08 03:25:05 +08:00
|
|
|
if (pSMBr->hdr.Status.CifsError) {
|
2014-08-03 10:16:48 +08:00
|
|
|
cifs_dbg(FYI, "notify err 0x%x\n",
|
2013-05-05 11:12:25 +08:00
|
|
|
pSMBr->hdr.Status.CifsError);
|
2008-04-29 08:06:05 +08:00
|
|
|
return true;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-04-29 08:06:05 +08:00
|
|
|
return false;
|
2007-07-10 09:16:18 +08:00
|
|
|
}
|
2007-07-08 03:25:05 +08:00
|
|
|
if (pSMB->hdr.Command != SMB_COM_LOCKING_ANDX)
|
2008-04-29 08:06:05 +08:00
|
|
|
return false;
|
2007-07-08 03:25:05 +08:00
|
|
|
if (pSMB->hdr.Flags & SMBFLG_RESPONSE) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/* no sense logging error on invalid handle on oplock
|
|
|
|
break - harmless race between close request and oplock
|
|
|
|
break response is expected from time to time writing out
|
|
|
|
large dirty files cached on the client */
|
2007-07-10 09:16:18 +08:00
|
|
|
if ((NT_STATUS_INVALID_HANDLE) ==
|
|
|
|
le32_to_cpu(pSMB->hdr.Status.CifsError)) {
|
2020-04-15 13:42:53 +08:00
|
|
|
cifs_dbg(FYI, "Invalid handle on oplock break\n");
|
2008-04-29 08:06:05 +08:00
|
|
|
return true;
|
2007-07-10 09:16:18 +08:00
|
|
|
} else if (ERRbadfid ==
|
2005-04-17 06:20:36 +08:00
|
|
|
le16_to_cpu(pSMB->hdr.Status.DosError.Error)) {
|
2008-04-29 08:06:05 +08:00
|
|
|
return true;
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
2008-04-29 08:06:05 +08:00
|
|
|
return false; /* on valid oplock brk we get "request" */
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2007-07-08 03:25:05 +08:00
|
|
|
if (pSMB->hdr.WordCount != 8)
|
2008-04-29 08:06:05 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-08-03 10:16:48 +08:00
|
|
|
cifs_dbg(FYI, "oplock type 0x%x level 0x%x\n",
|
2010-04-21 11:50:45 +08:00
|
|
|
pSMB->LockType, pSMB->OplockLevel);
|
2007-07-08 03:25:05 +08:00
|
|
|
if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE))
|
2008-04-29 08:06:05 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* look up tcon based on tid & uid */
|
2010-10-19 01:59:37 +08:00
|
|
|
spin_lock(&cifs_tcp_ses_lock);
|
2008-11-16 00:12:47 +08:00
|
|
|
list_for_each(tmp, &srv->smb_ses_list) {
|
2011-05-27 12:34:02 +08:00
|
|
|
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
|
2008-11-16 00:12:47 +08:00
|
|
|
list_for_each(tmp1, &ses->tcon_list) {
|
2011-05-27 12:34:02 +08:00
|
|
|
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
|
2008-11-16 00:12:47 +08:00
|
|
|
if (tcon->tid != buf->Tid)
|
|
|
|
continue;
|
|
|
|
|
2012-05-28 18:16:31 +08:00
|
|
|
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
|
2016-09-23 07:58:16 +08:00
|
|
|
spin_lock(&tcon->open_file_lock);
|
2008-11-16 00:12:47 +08:00
|
|
|
list_for_each(tmp2, &tcon->openFileList) {
|
|
|
|
netfile = list_entry(tmp2, struct cifsFileInfo,
|
2005-04-29 13:41:10 +08:00
|
|
|
tlist);
|
2012-09-19 07:20:26 +08:00
|
|
|
if (pSMB->Fid != netfile->fid.netfid)
|
2008-11-16 00:12:47 +08:00
|
|
|
continue;
|
|
|
|
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "file id match, oplock break\n");
|
2015-03-18 06:25:59 +08:00
|
|
|
pCifsInode = CIFS_I(d_inode(netfile->dentry));
|
2010-07-21 04:09:02 +08:00
|
|
|
|
2014-03-12 00:11:47 +08:00
|
|
|
set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK,
|
|
|
|
&pCifsInode->flags);
|
|
|
|
|
2019-10-30 07:51:19 +08:00
|
|
|
netfile->oplock_epoch = 0;
|
|
|
|
netfile->oplock_level = pSMB->OplockLevel;
|
2010-07-21 04:09:02 +08:00
|
|
|
netfile->oplock_break_cancelled = false;
|
2019-10-30 07:51:19 +08:00
|
|
|
cifs_queue_oplock_break(netfile);
|
2010-07-21 04:09:02 +08:00
|
|
|
|
2016-09-23 07:58:16 +08:00
|
|
|
spin_unlock(&tcon->open_file_lock);
|
2010-10-19 01:59:37 +08:00
|
|
|
spin_unlock(&cifs_tcp_ses_lock);
|
2008-11-16 00:12:47 +08:00
|
|
|
return true;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2016-09-23 07:58:16 +08:00
|
|
|
spin_unlock(&tcon->open_file_lock);
|
2010-10-19 01:59:37 +08:00
|
|
|
spin_unlock(&cifs_tcp_ses_lock);
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "No matching file for oplock break\n");
|
2008-04-29 08:06:05 +08:00
|
|
|
return true;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2010-10-19 01:59:37 +08:00
|
|
|
spin_unlock(&cifs_tcp_ses_lock);
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n");
|
2008-04-29 08:06:05 +08:00
|
|
|
return true;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-03-24 02:28:02 +08:00
|
|
|
dump_smb(void *buf, int smb_buf_length)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (traceSMB == 0)
|
|
|
|
return;
|
|
|
|
|
2014-08-27 21:49:43 +08:00
|
|
|
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, 8, 2, buf,
|
|
|
|
smb_buf_length, true);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2005-04-29 13:41:05 +08:00
|
|
|
|
2009-11-07 03:18:29 +08:00
|
|
|
void
|
|
|
|
cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
|
|
|
|
{
|
|
|
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
|
2018-11-16 23:13:25 +08:00
|
|
|
struct cifs_tcon *tcon = NULL;
|
|
|
|
|
|
|
|
if (cifs_sb->master_tlink)
|
|
|
|
tcon = cifs_sb_master_tcon(cifs_sb);
|
|
|
|
|
2009-11-16 14:33:16 +08:00
|
|
|
cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
|
2019-06-19 03:16:02 +08:00
|
|
|
cifs_sb->mnt_cifs_serverino_autodisabled = true;
|
2020-04-15 13:42:53 +08:00
|
|
|
cifs_dbg(VFS, "Autodisabling the use of server inode numbers on %s\n",
|
2018-11-16 23:13:25 +08:00
|
|
|
tcon ? tcon->treeName : "new server");
|
2020-04-15 13:42:53 +08:00
|
|
|
cifs_dbg(VFS, "The server doesn't seem to support them properly or the files might be on different servers (DFS)\n");
|
2018-11-16 23:13:25 +08:00
|
|
|
cifs_dbg(VFS, "Hardlinks will not be recognized on this mount. Consider mounting with the \"noserverino\" option to silence this message.\n");
|
|
|
|
|
2009-11-07 03:18:29 +08:00
|
|
|
}
|
|
|
|
}
|
2010-11-02 17:00:42 +08:00
|
|
|
|
2010-11-03 15:58:57 +08:00
|
|
|
void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
|
2010-11-02 17:00:42 +08:00
|
|
|
{
|
2010-11-03 15:58:57 +08:00
|
|
|
oplock &= 0xF;
|
2010-11-02 17:00:42 +08:00
|
|
|
|
2010-11-03 15:58:57 +08:00
|
|
|
if (oplock == OPLOCK_EXCLUSIVE) {
|
2013-09-05 17:01:06 +08:00
|
|
|
cinode->oplock = CIFS_CACHE_WRITE_FLG | CIFS_CACHE_READ_FLG;
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n",
|
netfs: Fix gcc-12 warning by embedding vfs inode in netfs_i_context
While randstruct was satisfied with using an open-coded "void *" offset
cast for the netfs_i_context <-> inode casting, __builtin_object_size() as
used by FORTIFY_SOURCE was not as easily fooled. This was causing the
following complaint[1] from gcc v12:
In file included from include/linux/string.h:253,
from include/linux/ceph/ceph_debug.h:7,
from fs/ceph/inode.c:2:
In function 'fortify_memset_chk',
inlined from 'netfs_i_context_init' at include/linux/netfs.h:326:2,
inlined from 'ceph_alloc_inode' at fs/ceph/inode.c:463:2:
include/linux/fortify-string.h:242:25: warning: call to '__write_overflow_field' declared with attribute warning: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Wattribute-warning]
242 | __write_overflow_field(p_size_field, size);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fix this by embedding a struct inode into struct netfs_i_context (which
should perhaps be renamed to struct netfs_inode). The struct inode
vfs_inode fields are then removed from the 9p, afs, ceph and cifs inode
structs and vfs_inode is then simply changed to "netfs.inode" in those
filesystems.
Further, rename netfs_i_context to netfs_inode, get rid of the
netfs_inode() function that converted a netfs_i_context pointer to an
inode pointer (that can now be done with &ctx->inode) and rename the
netfs_i_context() function to netfs_inode() (which is now a wrapper
around container_of()).
Most of the changes were done with:
perl -p -i -e 's/vfs_inode/netfs.inode/'g \
`git grep -l 'vfs_inode' -- fs/{9p,afs,ceph,cifs}/*.[ch]`
Kees suggested doing it with a pair structure[2] and a special
declarator to insert that into the network filesystem's inode
wrapper[3], but I think it's cleaner to embed it - and then it doesn't
matter if struct randomisation reorders things.
Dave Chinner suggested using a filesystem-specific VFS_I() function in
each filesystem to convert that filesystem's own inode wrapper struct
into the VFS inode struct[4].
Version #2:
- Fix a couple of missed name changes due to a disabled cifs option.
- Rename nfs_i_context to nfs_inode
- Use "netfs" instead of "nic" as the member name in per-fs inode wrapper
structs.
[ This also undoes commit 507160f46c55 ("netfs: gcc-12: temporarily
disable '-Wattribute-warning' for now") that is no longer needed ]
Fixes: bc899ee1c898 ("netfs: Add a netfs inode context")
Reported-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Xiubo Li <xiubli@redhat.com>
cc: Jonathan Corbet <corbet@lwn.net>
cc: Eric Van Hensbergen <ericvh@gmail.com>
cc: Latchesar Ionkov <lucho@ionkov.net>
cc: Dominique Martinet <asmadeus@codewreck.org>
cc: Christian Schoenebeck <linux_oss@crudebyte.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Ilya Dryomov <idryomov@gmail.com>
cc: Steve French <smfrench@gmail.com>
cc: William Kucharski <william.kucharski@oracle.com>
cc: "Matthew Wilcox (Oracle)" <willy@infradead.org>
cc: Dave Chinner <david@fromorbit.com>
cc: linux-doc@vger.kernel.org
cc: v9fs-developer@lists.sourceforge.net
cc: linux-afs@lists.infradead.org
cc: ceph-devel@vger.kernel.org
cc: linux-cifs@vger.kernel.org
cc: samba-technical@lists.samba.org
cc: linux-fsdevel@vger.kernel.org
cc: linux-hardening@vger.kernel.org
Link: https://lore.kernel.org/r/d2ad3a3d7bdd794c6efb562d2f2b655fb67756b9.camel@kernel.org/ [1]
Link: https://lore.kernel.org/r/20220517210230.864239-1-keescook@chromium.org/ [2]
Link: https://lore.kernel.org/r/20220518202212.2322058-1-keescook@chromium.org/ [3]
Link: https://lore.kernel.org/r/20220524101205.GI2306852@dread.disaster.area/ [4]
Link: https://lore.kernel.org/r/165296786831.3591209.12111293034669289733.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/165305805651.4094995.7763502506786714216.stgit@warthog.procyon.org.uk # v2
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2022-06-10 04:46:04 +08:00
|
|
|
&cinode->netfs.inode);
|
2010-11-03 15:58:57 +08:00
|
|
|
} else if (oplock == OPLOCK_READ) {
|
2013-09-05 17:01:06 +08:00
|
|
|
cinode->oplock = CIFS_CACHE_READ_FLG;
|
2013-05-05 11:12:25 +08:00
|
|
|
cifs_dbg(FYI, "Level II Oplock granted on inode %p\n",
|
netfs: Fix gcc-12 warning by embedding vfs inode in netfs_i_context
While randstruct was satisfied with using an open-coded "void *" offset
cast for the netfs_i_context <-> inode casting, __builtin_object_size() as
used by FORTIFY_SOURCE was not as easily fooled. This was causing the
following complaint[1] from gcc v12:
In file included from include/linux/string.h:253,
from include/linux/ceph/ceph_debug.h:7,
from fs/ceph/inode.c:2:
In function 'fortify_memset_chk',
inlined from 'netfs_i_context_init' at include/linux/netfs.h:326:2,
inlined from 'ceph_alloc_inode' at fs/ceph/inode.c:463:2:
include/linux/fortify-string.h:242:25: warning: call to '__write_overflow_field' declared with attribute warning: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Wattribute-warning]
242 | __write_overflow_field(p_size_field, size);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fix this by embedding a struct inode into struct netfs_i_context (which
should perhaps be renamed to struct netfs_inode). The struct inode
vfs_inode fields are then removed from the 9p, afs, ceph and cifs inode
structs and vfs_inode is then simply changed to "netfs.inode" in those
filesystems.
Further, rename netfs_i_context to netfs_inode, get rid of the
netfs_inode() function that converted a netfs_i_context pointer to an
inode pointer (that can now be done with &ctx->inode) and rename the
netfs_i_context() function to netfs_inode() (which is now a wrapper
around container_of()).
Most of the changes were done with:
perl -p -i -e 's/vfs_inode/netfs.inode/'g \
`git grep -l 'vfs_inode' -- fs/{9p,afs,ceph,cifs}/*.[ch]`
Kees suggested doing it with a pair structure[2] and a special
declarator to insert that into the network filesystem's inode
wrapper[3], but I think it's cleaner to embed it - and then it doesn't
matter if struct randomisation reorders things.
Dave Chinner suggested using a filesystem-specific VFS_I() function in
each filesystem to convert that filesystem's own inode wrapper struct
into the VFS inode struct[4].
Version #2:
- Fix a couple of missed name changes due to a disabled cifs option.
- Rename nfs_i_context to nfs_inode
- Use "netfs" instead of "nic" as the member name in per-fs inode wrapper
structs.
[ This also undoes commit 507160f46c55 ("netfs: gcc-12: temporarily
disable '-Wattribute-warning' for now") that is no longer needed ]
Fixes: bc899ee1c898 ("netfs: Add a netfs inode context")
Reported-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Xiubo Li <xiubli@redhat.com>
cc: Jonathan Corbet <corbet@lwn.net>
cc: Eric Van Hensbergen <ericvh@gmail.com>
cc: Latchesar Ionkov <lucho@ionkov.net>
cc: Dominique Martinet <asmadeus@codewreck.org>
cc: Christian Schoenebeck <linux_oss@crudebyte.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Ilya Dryomov <idryomov@gmail.com>
cc: Steve French <smfrench@gmail.com>
cc: William Kucharski <william.kucharski@oracle.com>
cc: "Matthew Wilcox (Oracle)" <willy@infradead.org>
cc: Dave Chinner <david@fromorbit.com>
cc: linux-doc@vger.kernel.org
cc: v9fs-developer@lists.sourceforge.net
cc: linux-afs@lists.infradead.org
cc: ceph-devel@vger.kernel.org
cc: linux-cifs@vger.kernel.org
cc: samba-technical@lists.samba.org
cc: linux-fsdevel@vger.kernel.org
cc: linux-hardening@vger.kernel.org
Link: https://lore.kernel.org/r/d2ad3a3d7bdd794c6efb562d2f2b655fb67756b9.camel@kernel.org/ [1]
Link: https://lore.kernel.org/r/20220517210230.864239-1-keescook@chromium.org/ [2]
Link: https://lore.kernel.org/r/20220518202212.2322058-1-keescook@chromium.org/ [3]
Link: https://lore.kernel.org/r/20220524101205.GI2306852@dread.disaster.area/ [4]
Link: https://lore.kernel.org/r/165296786831.3591209.12111293034669289733.stgit@warthog.procyon.org.uk/ # v1
Link: https://lore.kernel.org/r/165305805651.4094995.7763502506786714216.stgit@warthog.procyon.org.uk # v2
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2022-06-10 04:46:04 +08:00
|
|
|
&cinode->netfs.inode);
|
2013-09-05 17:01:06 +08:00
|
|
|
} else
|
|
|
|
cinode->oplock = 0;
|
2010-11-02 17:00:42 +08:00
|
|
|
}
|
2011-09-26 22:56:44 +08:00
|
|
|
|
2014-03-12 00:11:47 +08:00
|
|
|
/*
|
|
|
|
* We wait for oplock breaks to be processed before we attempt to perform
|
|
|
|
* writes.
|
|
|
|
*/
|
|
|
|
int cifs_get_writer(struct cifsInodeInfo *cinode)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
start:
|
|
|
|
rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK,
|
sched: Remove proliferation of wait_on_bit() action functions
The current "wait_on_bit" interface requires an 'action'
function to be provided which does the actual waiting.
There are over 20 such functions, many of them identical.
Most cases can be satisfied by one of just two functions, one
which uses io_schedule() and one which just uses schedule().
So:
Rename wait_on_bit and wait_on_bit_lock to
wait_on_bit_action and wait_on_bit_lock_action
to make it explicit that they need an action function.
Introduce new wait_on_bit{,_lock} and wait_on_bit{,_lock}_io
which are *not* given an action function but implicitly use
a standard one.
The decision to error-out if a signal is pending is now made
based on the 'mode' argument rather than being encoded in the action
function.
All instances of the old wait_on_bit and wait_on_bit_lock which
can use the new version have been changed accordingly and their
action functions have been discarded.
wait_on_bit{_lock} does not return any specific error code in the
event of a signal so the caller must check for non-zero and
interpolate their own error code as appropriate.
The wait_on_bit() call in __fscache_wait_on_invalidate() was
ambiguous as it specified TASK_UNINTERRUPTIBLE but used
fscache_wait_bit_interruptible as an action function.
David Howells confirms this should be uniformly
"uninterruptible"
The main remaining user of wait_on_bit{,_lock}_action is NFS
which needs to use a freezer-aware schedule() call.
A comment in fs/gfs2/glock.c notes that having multiple 'action'
functions is useful as they display differently in the 'wchan'
field of 'ps'. (and /proc/$PID/wchan).
As the new bit_wait{,_io} functions are tagged "__sched", they
will not show up at all, but something higher in the stack. So
the distinction will still be visible, only with different
function names (gds2_glock_wait versus gfs2_glock_dq_wait in the
gfs2/glock.c case).
Since first version of this patch (against 3.15) two new action
functions appeared, on in NFS and one in CIFS. CIFS also now
uses an action function that makes the same freezer aware
schedule call as NFS.
Signed-off-by: NeilBrown <neilb@suse.de>
Acked-by: David Howells <dhowells@redhat.com> (fscache, keys)
Acked-by: Steven Whitehouse <swhiteho@redhat.com> (gfs2)
Acked-by: Peter Zijlstra <peterz@infradead.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Steve French <sfrench@samba.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: http://lkml.kernel.org/r/20140707051603.28027.72349.stgit@notabene.brown
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2014-07-07 13:16:04 +08:00
|
|
|
TASK_KILLABLE);
|
2014-03-12 00:11:47 +08:00
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
spin_lock(&cinode->writers_lock);
|
|
|
|
if (!cinode->writers)
|
|
|
|
set_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags);
|
|
|
|
cinode->writers++;
|
|
|
|
/* Check to see if we have started servicing an oplock break */
|
|
|
|
if (test_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags)) {
|
|
|
|
cinode->writers--;
|
|
|
|
if (cinode->writers == 0) {
|
|
|
|
clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags);
|
|
|
|
wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS);
|
|
|
|
}
|
|
|
|
spin_unlock(&cinode->writers_lock);
|
|
|
|
goto start;
|
|
|
|
}
|
|
|
|
spin_unlock(&cinode->writers_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cifs_put_writer(struct cifsInodeInfo *cinode)
|
|
|
|
{
|
|
|
|
spin_lock(&cinode->writers_lock);
|
|
|
|
cinode->writers--;
|
|
|
|
if (cinode->writers == 0) {
|
|
|
|
clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags);
|
|
|
|
wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS);
|
|
|
|
}
|
|
|
|
spin_unlock(&cinode->writers_lock);
|
|
|
|
}
|
|
|
|
|
2019-03-29 17:49:12 +08:00
|
|
|
/**
|
|
|
|
* cifs_queue_oplock_break - queue the oplock break handler for cfile
|
2021-09-20 20:14:15 +08:00
|
|
|
* @cfile: The file to break the oplock on
|
2019-03-29 17:49:12 +08:00
|
|
|
*
|
|
|
|
* This function is called from the demultiplex thread when it
|
|
|
|
* receives an oplock break for @cfile.
|
|
|
|
*
|
|
|
|
* Assumes the tcon->open_file_lock is held.
|
|
|
|
* Assumes cfile->file_info_lock is NOT held.
|
|
|
|
*/
|
|
|
|
void cifs_queue_oplock_break(struct cifsFileInfo *cfile)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Bump the handle refcount now while we hold the
|
|
|
|
* open_file_lock to enforce the validity of it for the oplock
|
|
|
|
* break handler. The matching put is done at the end of the
|
|
|
|
* handler.
|
|
|
|
*/
|
|
|
|
cifsFileInfo_get(cfile);
|
|
|
|
|
|
|
|
queue_work(cifsoplockd_wq, &cfile->oplock_break);
|
|
|
|
}
|
|
|
|
|
2014-03-12 00:11:47 +08:00
|
|
|
void cifs_done_oplock_break(struct cifsInodeInfo *cinode)
|
|
|
|
{
|
|
|
|
clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
|
|
|
|
wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK);
|
|
|
|
}
|
|
|
|
|
2011-09-26 22:56:44 +08:00
|
|
|
bool
|
|
|
|
backup_cred(struct cifs_sb_info *cifs_sb)
|
|
|
|
{
|
|
|
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) {
|
2020-12-13 03:40:50 +08:00
|
|
|
if (uid_eq(cifs_sb->ctx->backupuid, current_fsuid()))
|
2011-09-26 22:56:44 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) {
|
2020-12-13 03:40:50 +08:00
|
|
|
if (in_group_p(cifs_sb->ctx->backupgid))
|
2011-09-26 22:56:44 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2012-09-19 21:22:45 +08:00
|
|
|
|
|
|
|
void
|
|
|
|
cifs_del_pending_open(struct cifs_pending_open *open)
|
|
|
|
{
|
2016-09-23 07:58:16 +08:00
|
|
|
spin_lock(&tlink_tcon(open->tlink)->open_file_lock);
|
2012-09-19 21:22:45 +08:00
|
|
|
list_del(&open->olist);
|
2016-09-23 07:58:16 +08:00
|
|
|
spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
|
2012-09-19 21:22:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink,
|
|
|
|
struct cifs_pending_open *open)
|
|
|
|
{
|
|
|
|
memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
|
|
|
|
open->oplock = CIFS_OPLOCK_NO_CHANGE;
|
|
|
|
open->tlink = tlink;
|
|
|
|
fid->pending_open = open;
|
|
|
|
list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
|
|
|
|
struct cifs_pending_open *open)
|
|
|
|
{
|
2016-09-23 07:58:16 +08:00
|
|
|
spin_lock(&tlink_tcon(tlink)->open_file_lock);
|
2012-09-19 21:22:45 +08:00
|
|
|
cifs_add_pending_open_locked(fid, tlink, open);
|
2016-09-23 07:58:16 +08:00
|
|
|
spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
|
2012-09-19 21:22:45 +08:00
|
|
|
}
|
2017-02-13 23:03:47 +08:00
|
|
|
|
2021-05-05 18:56:47 +08:00
|
|
|
/*
|
|
|
|
* Critical section which runs after acquiring deferred_lock.
|
2021-05-21 00:45:01 +08:00
|
|
|
* As there is no reference count on cifs_deferred_close, pdclose
|
|
|
|
* should not be used outside deferred_lock.
|
2021-05-05 18:56:47 +08:00
|
|
|
*/
|
2021-04-13 13:26:42 +08:00
|
|
|
bool
|
|
|
|
cifs_is_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close **pdclose)
|
|
|
|
{
|
|
|
|
struct cifs_deferred_close *dclose;
|
|
|
|
|
|
|
|
list_for_each_entry(dclose, &CIFS_I(d_inode(cfile->dentry))->deferred_closes, dlist) {
|
|
|
|
if ((dclose->netfid == cfile->fid.netfid) &&
|
|
|
|
(dclose->persistent_fid == cfile->fid.persistent_fid) &&
|
|
|
|
(dclose->volatile_fid == cfile->fid.volatile_fid)) {
|
|
|
|
*pdclose = dclose;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-05-05 18:56:47 +08:00
|
|
|
/*
|
|
|
|
* Critical section which runs after acquiring deferred_lock.
|
|
|
|
*/
|
2021-04-13 13:26:42 +08:00
|
|
|
void
|
|
|
|
cifs_add_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close *dclose)
|
|
|
|
{
|
|
|
|
bool is_deferred = false;
|
|
|
|
struct cifs_deferred_close *pdclose;
|
|
|
|
|
|
|
|
is_deferred = cifs_is_deferred_close(cfile, &pdclose);
|
|
|
|
if (is_deferred) {
|
|
|
|
kfree(dclose);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dclose->tlink = cfile->tlink;
|
|
|
|
dclose->netfid = cfile->fid.netfid;
|
|
|
|
dclose->persistent_fid = cfile->fid.persistent_fid;
|
|
|
|
dclose->volatile_fid = cfile->fid.volatile_fid;
|
|
|
|
list_add_tail(&dclose->dlist, &CIFS_I(d_inode(cfile->dentry))->deferred_closes);
|
|
|
|
}
|
|
|
|
|
2021-05-05 18:56:47 +08:00
|
|
|
/*
|
|
|
|
* Critical section which runs after acquiring deferred_lock.
|
|
|
|
*/
|
2021-04-13 13:26:42 +08:00
|
|
|
void
|
|
|
|
cifs_del_deferred_close(struct cifsFileInfo *cfile)
|
|
|
|
{
|
|
|
|
bool is_deferred = false;
|
|
|
|
struct cifs_deferred_close *dclose;
|
|
|
|
|
|
|
|
is_deferred = cifs_is_deferred_close(cfile, &dclose);
|
|
|
|
if (!is_deferred)
|
|
|
|
return;
|
|
|
|
list_del(&dclose->dlist);
|
|
|
|
kfree(dclose);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
|
|
|
|
{
|
|
|
|
struct cifsFileInfo *cfile = NULL;
|
2021-08-09 17:32:46 +08:00
|
|
|
struct file_list *tmp_list, *tmp_next_list;
|
|
|
|
struct list_head file_head;
|
2021-07-29 15:45:29 +08:00
|
|
|
|
|
|
|
if (cifs_inode == NULL)
|
|
|
|
return;
|
2021-04-13 13:26:42 +08:00
|
|
|
|
2021-08-09 17:32:46 +08:00
|
|
|
INIT_LIST_HEAD(&file_head);
|
|
|
|
spin_lock(&cifs_inode->open_file_lock);
|
2021-04-13 13:26:42 +08:00
|
|
|
list_for_each_entry(cfile, &cifs_inode->openFileList, flist) {
|
2021-07-29 15:45:29 +08:00
|
|
|
if (delayed_work_pending(&cfile->deferred)) {
|
2021-08-09 17:32:46 +08:00
|
|
|
if (cancel_delayed_work(&cfile->deferred)) {
|
|
|
|
tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
|
|
|
|
if (tmp_list == NULL)
|
2021-09-18 01:29:42 +08:00
|
|
|
break;
|
2021-08-09 17:32:46 +08:00
|
|
|
tmp_list->cfile = cfile;
|
|
|
|
list_add_tail(&tmp_list->list, &file_head);
|
|
|
|
}
|
2021-07-29 15:45:29 +08:00
|
|
|
}
|
2021-04-13 13:26:42 +08:00
|
|
|
}
|
2021-08-09 17:32:46 +08:00
|
|
|
spin_unlock(&cifs_inode->open_file_lock);
|
|
|
|
|
|
|
|
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
|
|
|
|
_cifsFileInfo_put(tmp_list->cfile, true, false);
|
|
|
|
list_del(&tmp_list->list);
|
|
|
|
kfree(tmp_list);
|
|
|
|
}
|
2021-04-13 13:26:42 +08:00
|
|
|
}
|
|
|
|
|
2021-04-20 03:02:03 +08:00
|
|
|
void
|
|
|
|
cifs_close_all_deferred_files(struct cifs_tcon *tcon)
|
|
|
|
{
|
|
|
|
struct cifsFileInfo *cfile;
|
|
|
|
struct list_head *tmp;
|
2021-08-09 17:32:46 +08:00
|
|
|
struct file_list *tmp_list, *tmp_next_list;
|
|
|
|
struct list_head file_head;
|
2021-04-20 03:02:03 +08:00
|
|
|
|
2021-08-09 17:32:46 +08:00
|
|
|
INIT_LIST_HEAD(&file_head);
|
2021-04-20 03:02:03 +08:00
|
|
|
spin_lock(&tcon->open_file_lock);
|
|
|
|
list_for_each(tmp, &tcon->openFileList) {
|
|
|
|
cfile = list_entry(tmp, struct cifsFileInfo, tlist);
|
2021-05-21 00:45:01 +08:00
|
|
|
if (delayed_work_pending(&cfile->deferred)) {
|
2021-08-09 17:32:46 +08:00
|
|
|
if (cancel_delayed_work(&cfile->deferred)) {
|
|
|
|
tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
|
|
|
|
if (tmp_list == NULL)
|
2021-09-18 01:29:42 +08:00
|
|
|
break;
|
2021-08-09 17:32:46 +08:00
|
|
|
tmp_list->cfile = cfile;
|
|
|
|
list_add_tail(&tmp_list->list, &file_head);
|
|
|
|
}
|
2021-05-21 00:45:01 +08:00
|
|
|
}
|
2021-04-20 03:02:03 +08:00
|
|
|
}
|
|
|
|
spin_unlock(&tcon->open_file_lock);
|
2021-08-09 17:32:46 +08:00
|
|
|
|
|
|
|
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
|
|
|
|
_cifsFileInfo_put(tmp_list->cfile, true, false);
|
|
|
|
list_del(&tmp_list->list);
|
|
|
|
kfree(tmp_list);
|
|
|
|
}
|
2021-04-20 03:02:03 +08:00
|
|
|
}
|
2021-09-18 02:14:26 +08:00
|
|
|
void
|
|
|
|
cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)
|
|
|
|
{
|
|
|
|
struct cifsFileInfo *cfile;
|
|
|
|
struct list_head *tmp;
|
|
|
|
struct file_list *tmp_list, *tmp_next_list;
|
|
|
|
struct list_head file_head;
|
|
|
|
void *page;
|
|
|
|
const char *full_path;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&file_head);
|
|
|
|
page = alloc_dentry_path();
|
|
|
|
spin_lock(&tcon->open_file_lock);
|
|
|
|
list_for_each(tmp, &tcon->openFileList) {
|
|
|
|
cfile = list_entry(tmp, struct cifsFileInfo, tlist);
|
|
|
|
full_path = build_path_from_dentry(cfile->dentry, page);
|
|
|
|
if (strstr(full_path, path)) {
|
|
|
|
if (delayed_work_pending(&cfile->deferred)) {
|
|
|
|
if (cancel_delayed_work(&cfile->deferred)) {
|
|
|
|
tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
|
|
|
|
if (tmp_list == NULL)
|
|
|
|
break;
|
|
|
|
tmp_list->cfile = cfile;
|
|
|
|
list_add_tail(&tmp_list->list, &file_head);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock(&tcon->open_file_lock);
|
|
|
|
|
|
|
|
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
|
|
|
|
_cifsFileInfo_put(tmp_list->cfile, true, false);
|
|
|
|
list_del(&tmp_list->list);
|
|
|
|
kfree(tmp_list);
|
|
|
|
}
|
|
|
|
free_dentry_path(page);
|
|
|
|
}
|
2021-04-20 03:02:03 +08:00
|
|
|
|
2017-02-13 23:03:47 +08:00
|
|
|
/* parses DFS refferal V3 structure
|
|
|
|
* caller is responsible for freeing target_nodes
|
|
|
|
* returns:
|
|
|
|
* - on success - 0
|
|
|
|
* - on failure - errno
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
|
|
|
|
unsigned int *num_of_nodes,
|
|
|
|
struct dfs_info3_param **target_nodes,
|
|
|
|
const struct nls_table *nls_codepage, int remap,
|
|
|
|
const char *searchName, bool is_unicode)
|
|
|
|
{
|
|
|
|
int i, rc = 0;
|
|
|
|
char *data_end;
|
|
|
|
struct dfs_referral_level_3 *ref;
|
|
|
|
|
|
|
|
*num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals);
|
|
|
|
|
|
|
|
if (*num_of_nodes < 1) {
|
|
|
|
cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n",
|
|
|
|
*num_of_nodes);
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto parse_DFS_referrals_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
ref = (struct dfs_referral_level_3 *) &(rsp->referrals);
|
|
|
|
if (ref->VersionNumber != cpu_to_le16(3)) {
|
|
|
|
cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n",
|
|
|
|
le16_to_cpu(ref->VersionNumber));
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto parse_DFS_referrals_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get the upper boundary of the resp buffer */
|
|
|
|
data_end = (char *)rsp + rsp_size;
|
|
|
|
|
|
|
|
cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n",
|
|
|
|
*num_of_nodes, le32_to_cpu(rsp->DFSFlags));
|
|
|
|
|
|
|
|
*target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (*target_nodes == NULL) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto parse_DFS_referrals_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* collect necessary data from referrals */
|
|
|
|
for (i = 0; i < *num_of_nodes; i++) {
|
|
|
|
char *temp;
|
|
|
|
int max_len;
|
|
|
|
struct dfs_info3_param *node = (*target_nodes)+i;
|
|
|
|
|
|
|
|
node->flags = le32_to_cpu(rsp->DFSFlags);
|
|
|
|
if (is_unicode) {
|
|
|
|
__le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (tmp == NULL) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto parse_DFS_referrals_exit;
|
|
|
|
}
|
|
|
|
cifsConvertToUTF16((__le16 *) tmp, searchName,
|
|
|
|
PATH_MAX, nls_codepage, remap);
|
|
|
|
node->path_consumed = cifs_utf16_bytes(tmp,
|
|
|
|
le16_to_cpu(rsp->PathConsumed),
|
|
|
|
nls_codepage);
|
|
|
|
kfree(tmp);
|
|
|
|
} else
|
|
|
|
node->path_consumed = le16_to_cpu(rsp->PathConsumed);
|
|
|
|
|
|
|
|
node->server_type = le16_to_cpu(ref->ServerType);
|
|
|
|
node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
|
|
|
|
|
|
|
|
/* copy DfsPath */
|
|
|
|
temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
|
|
|
|
max_len = data_end - temp;
|
|
|
|
node->path_name = cifs_strndup_from_utf16(temp, max_len,
|
|
|
|
is_unicode, nls_codepage);
|
|
|
|
if (!node->path_name) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto parse_DFS_referrals_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy link target UNC */
|
|
|
|
temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
|
|
|
|
max_len = data_end - temp;
|
|
|
|
node->node_name = cifs_strndup_from_utf16(temp, max_len,
|
|
|
|
is_unicode, nls_codepage);
|
|
|
|
if (!node->node_name) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto parse_DFS_referrals_exit;
|
|
|
|
}
|
|
|
|
|
2018-11-15 01:38:51 +08:00
|
|
|
node->ttl = le32_to_cpu(ref->TimeToLive);
|
|
|
|
|
2017-02-13 23:03:47 +08:00
|
|
|
ref++;
|
|
|
|
}
|
|
|
|
|
|
|
|
parse_DFS_referrals_exit:
|
|
|
|
if (rc) {
|
|
|
|
free_dfs_info_array(*target_nodes, *num_of_nodes);
|
|
|
|
*target_nodes = NULL;
|
|
|
|
*num_of_nodes = 0;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
2017-04-26 02:52:29 +08:00
|
|
|
|
|
|
|
struct cifs_aio_ctx *
|
|
|
|
cifs_aio_ctx_alloc(void)
|
|
|
|
{
|
|
|
|
struct cifs_aio_ctx *ctx;
|
|
|
|
|
2019-04-11 03:37:47 +08:00
|
|
|
/*
|
|
|
|
* Must use kzalloc to initialize ctx->bv to NULL and ctx->direct_io
|
|
|
|
* to false so that we know when we have to unreference pages within
|
|
|
|
* cifs_aio_ctx_release()
|
|
|
|
*/
|
2017-04-26 02:52:29 +08:00
|
|
|
ctx = kzalloc(sizeof(struct cifs_aio_ctx), GFP_KERNEL);
|
|
|
|
if (!ctx)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&ctx->list);
|
|
|
|
mutex_init(&ctx->aio_mutex);
|
|
|
|
init_completion(&ctx->done);
|
|
|
|
kref_init(&ctx->refcount);
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cifs_aio_ctx_release(struct kref *refcount)
|
|
|
|
{
|
|
|
|
struct cifs_aio_ctx *ctx = container_of(refcount,
|
|
|
|
struct cifs_aio_ctx, refcount);
|
|
|
|
|
|
|
|
cifsFileInfo_put(ctx->cfile);
|
2019-04-11 03:37:47 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ctx->bv is only set if setup_aio_ctx_iter() was call successfuly
|
|
|
|
* which means that iov_iter_get_pages() was a success and thus that
|
|
|
|
* we have taken reference on pages.
|
|
|
|
*/
|
|
|
|
if (ctx->bv) {
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
for (i = 0; i < ctx->npages; i++) {
|
|
|
|
if (ctx->should_dirty)
|
|
|
|
set_page_dirty(ctx->bv[i].bv_page);
|
|
|
|
put_page(ctx->bv[i].bv_page);
|
|
|
|
}
|
|
|
|
kvfree(ctx->bv);
|
|
|
|
}
|
|
|
|
|
2017-04-26 02:52:29 +08:00
|
|
|
kfree(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CIFS_AIO_KMALLOC_LIMIT (1024 * 1024)
|
|
|
|
|
|
|
|
int
|
|
|
|
setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw)
|
|
|
|
{
|
|
|
|
ssize_t rc;
|
|
|
|
unsigned int cur_npages;
|
|
|
|
unsigned int npages = 0;
|
|
|
|
unsigned int i;
|
|
|
|
size_t len;
|
|
|
|
size_t count = iov_iter_count(iter);
|
|
|
|
unsigned int saved_len;
|
|
|
|
size_t start;
|
|
|
|
unsigned int max_pages = iov_iter_npages(iter, INT_MAX);
|
|
|
|
struct page **pages = NULL;
|
|
|
|
struct bio_vec *bv = NULL;
|
|
|
|
|
2018-10-22 20:07:28 +08:00
|
|
|
if (iov_iter_is_kvec(iter)) {
|
2020-06-16 06:41:12 +08:00
|
|
|
memcpy(&ctx->iter, iter, sizeof(*iter));
|
2017-04-26 02:52:29 +08:00
|
|
|
ctx->len = count;
|
|
|
|
iov_iter_advance(iter, count);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-16 06:41:12 +08:00
|
|
|
if (array_size(max_pages, sizeof(*bv)) <= CIFS_AIO_KMALLOC_LIMIT)
|
|
|
|
bv = kmalloc_array(max_pages, sizeof(*bv), GFP_KERNEL);
|
2017-04-26 02:52:29 +08:00
|
|
|
|
|
|
|
if (!bv) {
|
2020-06-16 06:41:12 +08:00
|
|
|
bv = vmalloc(array_size(max_pages, sizeof(*bv)));
|
2017-04-26 02:52:29 +08:00
|
|
|
if (!bv)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2020-06-16 06:41:12 +08:00
|
|
|
if (array_size(max_pages, sizeof(*pages)) <= CIFS_AIO_KMALLOC_LIMIT)
|
|
|
|
pages = kmalloc_array(max_pages, sizeof(*pages), GFP_KERNEL);
|
2017-04-26 02:52:29 +08:00
|
|
|
|
|
|
|
if (!pages) {
|
2020-06-16 06:41:12 +08:00
|
|
|
pages = vmalloc(array_size(max_pages, sizeof(*pages)));
|
2017-05-18 02:24:15 +08:00
|
|
|
if (!pages) {
|
2017-04-26 02:52:29 +08:00
|
|
|
kvfree(bv);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
saved_len = count;
|
|
|
|
|
|
|
|
while (count && npages < max_pages) {
|
|
|
|
rc = iov_iter_get_pages(iter, pages, count, max_pages, &start);
|
|
|
|
if (rc < 0) {
|
2020-04-15 13:42:53 +08:00
|
|
|
cifs_dbg(VFS, "Couldn't get user pages (rc=%zd)\n", rc);
|
2017-04-26 02:52:29 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc > count) {
|
|
|
|
cifs_dbg(VFS, "get pages rc=%zd more than %zu\n", rc,
|
|
|
|
count);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
iov_iter_advance(iter, rc);
|
|
|
|
count -= rc;
|
|
|
|
rc += start;
|
|
|
|
cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE);
|
|
|
|
|
|
|
|
if (npages + cur_npages > max_pages) {
|
|
|
|
cifs_dbg(VFS, "out of vec array capacity (%u vs %u)\n",
|
|
|
|
npages + cur_npages, max_pages);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < cur_npages; i++) {
|
|
|
|
len = rc > PAGE_SIZE ? PAGE_SIZE : rc;
|
|
|
|
bv[npages + i].bv_page = pages[i];
|
|
|
|
bv[npages + i].bv_offset = start;
|
|
|
|
bv[npages + i].bv_len = len - start;
|
|
|
|
rc -= len;
|
|
|
|
start = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
npages += cur_npages;
|
|
|
|
}
|
|
|
|
|
|
|
|
kvfree(pages);
|
|
|
|
ctx->bv = bv;
|
|
|
|
ctx->len = saved_len - count;
|
|
|
|
ctx->npages = npages;
|
2018-10-20 07:57:56 +08:00
|
|
|
iov_iter_bvec(&ctx->iter, rw, ctx->bv, npages, ctx->len);
|
2017-04-26 02:52:29 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2018-02-17 02:19:27 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* cifs_alloc_hash - allocate hash and hash context together
|
2021-09-20 20:14:15 +08:00
|
|
|
* @name: The name of the crypto hash algo
|
|
|
|
* @shash: Where to put the pointer to the hash algo
|
|
|
|
* @sdesc: Where to put the pointer to the hash descriptor
|
2018-02-17 02:19:27 +08:00
|
|
|
*
|
|
|
|
* The caller has to make sure @sdesc is initialized to either NULL or
|
|
|
|
* a valid context. Both can be freed via cifs_free_hash().
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
cifs_alloc_hash(const char *name,
|
|
|
|
struct crypto_shash **shash, struct sdesc **sdesc)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
if (*sdesc != NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
*shash = crypto_alloc_shash(name, 0, 0);
|
|
|
|
if (IS_ERR(*shash)) {
|
2020-04-15 13:42:53 +08:00
|
|
|
cifs_dbg(VFS, "Could not allocate crypto %s\n", name);
|
2018-02-17 02:19:27 +08:00
|
|
|
rc = PTR_ERR(*shash);
|
|
|
|
*shash = NULL;
|
|
|
|
*sdesc = NULL;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = sizeof(struct shash_desc) + crypto_shash_descsize(*shash);
|
|
|
|
*sdesc = kmalloc(size, GFP_KERNEL);
|
|
|
|
if (*sdesc == NULL) {
|
|
|
|
cifs_dbg(VFS, "no memory left to allocate crypto %s\n", name);
|
|
|
|
crypto_free_shash(*shash);
|
|
|
|
*shash = NULL;
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*sdesc)->shash.tfm = *shash;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* cifs_free_hash - free hash and hash context together
|
2021-09-20 20:14:15 +08:00
|
|
|
* @shash: Where to find the pointer to the hash algo
|
|
|
|
* @sdesc: Where to find the pointer to the hash descriptor
|
2018-02-17 02:19:27 +08:00
|
|
|
*
|
|
|
|
* Freeing a NULL hash or context is safe.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc)
|
|
|
|
{
|
|
|
|
kfree(*sdesc);
|
|
|
|
*sdesc = NULL;
|
|
|
|
if (*shash)
|
|
|
|
crypto_free_shash(*shash);
|
|
|
|
*shash = NULL;
|
|
|
|
}
|
2018-05-31 03:47:58 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* rqst_page_get_length - obtain the length and offset for a page in smb_rqst
|
2021-09-20 20:14:15 +08:00
|
|
|
* @rqst: The request descriptor
|
|
|
|
* @page: The index of the page to query
|
|
|
|
* @len: Where to store the length for this page:
|
|
|
|
* @offset: Where to store the offset for this page
|
2018-05-31 03:47:58 +08:00
|
|
|
*/
|
|
|
|
void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
|
|
|
|
unsigned int *len, unsigned int *offset)
|
|
|
|
{
|
|
|
|
*len = rqst->rq_pagesz;
|
|
|
|
*offset = (page == 0) ? rqst->rq_offset : 0;
|
|
|
|
|
|
|
|
if (rqst->rq_npages == 1 || page == rqst->rq_npages-1)
|
|
|
|
*len = rqst->rq_tailsz;
|
|
|
|
else if (page == 0)
|
|
|
|
*len = rqst->rq_pagesz - rqst->rq_offset;
|
|
|
|
}
|
2018-11-15 03:20:31 +08:00
|
|
|
|
|
|
|
void extract_unc_hostname(const char *unc, const char **h, size_t *len)
|
|
|
|
{
|
|
|
|
const char *end;
|
|
|
|
|
|
|
|
/* skip initial slashes */
|
|
|
|
while (*unc && (*unc == '\\' || *unc == '/'))
|
|
|
|
unc++;
|
|
|
|
|
|
|
|
end = unc;
|
|
|
|
|
|
|
|
while (*end && !(*end == '\\' || *end == '/'))
|
|
|
|
end++;
|
|
|
|
|
|
|
|
*h = unc;
|
|
|
|
*len = end - unc;
|
|
|
|
}
|
2019-08-27 07:30:14 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* copy_path_name - copy src path to dst, possibly truncating
|
2021-09-20 20:14:15 +08:00
|
|
|
* @dst: The destination buffer
|
|
|
|
* @src: The source name
|
2019-08-27 07:30:14 +08:00
|
|
|
*
|
|
|
|
* returns number of bytes written (including trailing nul)
|
|
|
|
*/
|
|
|
|
int copy_path_name(char *dst, const char *src)
|
|
|
|
{
|
|
|
|
int name_len;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PATH_MAX includes nul, so if strlen(src) >= PATH_MAX it
|
|
|
|
* will truncate and strlen(dst) will be PATH_MAX-1
|
|
|
|
*/
|
|
|
|
name_len = strscpy(dst, src, PATH_MAX);
|
|
|
|
if (WARN_ON_ONCE(name_len < 0))
|
|
|
|
name_len = PATH_MAX-1;
|
|
|
|
|
|
|
|
/* we count the trailing nul */
|
|
|
|
name_len++;
|
|
|
|
return name_len;
|
|
|
|
}
|
2020-02-21 06:49:34 +08:00
|
|
|
|
|
|
|
struct super_cb_data {
|
2020-04-21 06:43:04 +08:00
|
|
|
void *data;
|
2020-02-21 06:49:34 +08:00
|
|
|
struct super_block *sb;
|
|
|
|
};
|
|
|
|
|
2020-04-21 06:43:04 +08:00
|
|
|
static void tcp_super_cb(struct super_block *sb, void *arg)
|
2020-02-21 06:49:34 +08:00
|
|
|
{
|
2020-04-21 06:43:04 +08:00
|
|
|
struct super_cb_data *sd = arg;
|
|
|
|
struct TCP_Server_Info *server = sd->data;
|
2020-02-21 06:49:34 +08:00
|
|
|
struct cifs_sb_info *cifs_sb;
|
|
|
|
struct cifs_tcon *tcon;
|
|
|
|
|
2020-04-21 06:43:04 +08:00
|
|
|
if (sd->sb)
|
2020-02-21 06:49:34 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
cifs_sb = CIFS_SB(sb);
|
|
|
|
tcon = cifs_sb_master_tcon(cifs_sb);
|
2020-04-21 06:43:04 +08:00
|
|
|
if (tcon->ses->server == server)
|
|
|
|
sd->sb = sb;
|
2020-02-21 06:49:34 +08:00
|
|
|
}
|
|
|
|
|
2020-04-21 06:43:04 +08:00
|
|
|
static struct super_block *__cifs_get_super(void (*f)(struct super_block *, void *),
|
|
|
|
void *data)
|
2020-02-21 06:49:34 +08:00
|
|
|
{
|
2020-04-21 06:43:04 +08:00
|
|
|
struct super_cb_data sd = {
|
|
|
|
.data = data,
|
2020-02-21 06:49:34 +08:00
|
|
|
.sb = NULL,
|
|
|
|
};
|
2022-06-06 06:54:26 +08:00
|
|
|
struct file_system_type **fs_type = (struct file_system_type *[]) {
|
|
|
|
&cifs_fs_type, &smb3_fs_type, NULL,
|
|
|
|
};
|
2020-02-21 06:49:34 +08:00
|
|
|
|
2022-06-06 06:54:26 +08:00
|
|
|
for (; *fs_type; fs_type++) {
|
|
|
|
iterate_supers_type(*fs_type, f, &sd);
|
|
|
|
if (sd.sb) {
|
|
|
|
/*
|
|
|
|
* Grab an active reference in order to prevent automounts (DFS links)
|
|
|
|
* of expiring and then freeing up our cifs superblock pointer while
|
|
|
|
* we're doing failover.
|
|
|
|
*/
|
|
|
|
cifs_sb_active(sd.sb);
|
|
|
|
return sd.sb;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ERR_PTR(-EINVAL);
|
2020-02-21 06:49:34 +08:00
|
|
|
}
|
|
|
|
|
2020-04-21 06:43:04 +08:00
|
|
|
static void __cifs_put_super(struct super_block *sb)
|
2020-02-21 06:49:34 +08:00
|
|
|
{
|
|
|
|
if (!IS_ERR_OR_NULL(sb))
|
|
|
|
cifs_sb_deactive(sb);
|
|
|
|
}
|
|
|
|
|
2020-04-21 06:43:04 +08:00
|
|
|
struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server)
|
|
|
|
{
|
|
|
|
return __cifs_get_super(tcp_super_cb, server);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cifs_put_tcp_super(struct super_block *sb)
|
|
|
|
{
|
|
|
|
__cifs_put_super(sb);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_CIFS_DFS_UPCALL
|
2020-05-20 02:38:28 +08:00
|
|
|
int match_target_ip(struct TCP_Server_Info *server,
|
|
|
|
const char *share, size_t share_len,
|
|
|
|
bool *result)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
char *target, *tip = NULL;
|
|
|
|
struct sockaddr tipaddr;
|
|
|
|
|
|
|
|
*result = false;
|
|
|
|
|
|
|
|
target = kzalloc(share_len + 3, GFP_KERNEL);
|
|
|
|
if (!target) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
scnprintf(target, share_len + 3, "\\\\%.*s", (int)share_len, share);
|
|
|
|
|
|
|
|
cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2);
|
|
|
|
|
2021-05-18 23:05:50 +08:00
|
|
|
rc = dns_resolve_server_name_to_ip(target, &tip, NULL);
|
2020-05-20 02:38:28 +08:00
|
|
|
if (rc < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
cifs_dbg(FYI, "%s: target ip: %s\n", __func__, tip);
|
|
|
|
|
|
|
|
if (!cifs_convert_address(&tipaddr, tip, strlen(tip))) {
|
|
|
|
cifs_dbg(VFS, "%s: failed to convert target ip address\n",
|
|
|
|
__func__);
|
|
|
|
rc = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr,
|
|
|
|
&tipaddr);
|
|
|
|
cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result);
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(target);
|
|
|
|
kfree(tip);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2021-11-04 00:53:29 +08:00
|
|
|
int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix)
|
2020-04-21 06:43:04 +08:00
|
|
|
{
|
2020-02-21 06:49:34 +08:00
|
|
|
kfree(cifs_sb->prepath);
|
|
|
|
|
2020-07-21 20:36:42 +08:00
|
|
|
if (prefix && *prefix) {
|
2021-03-06 04:02:34 +08:00
|
|
|
cifs_sb->prepath = kstrdup(prefix, GFP_ATOMIC);
|
2021-11-04 00:53:29 +08:00
|
|
|
if (!cifs_sb->prepath)
|
|
|
|
return -ENOMEM;
|
2020-02-21 06:49:34 +08:00
|
|
|
|
|
|
|
convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
|
|
|
|
} else
|
|
|
|
cifs_sb->prepath = NULL;
|
|
|
|
|
|
|
|
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
|
2021-11-04 00:53:29 +08:00
|
|
|
return 0;
|
2020-02-21 06:49:34 +08:00
|
|
|
}
|
2022-01-15 03:53:40 +08:00
|
|
|
|
|
|
|
/** cifs_dfs_query_info_nonascii_quirk
|
|
|
|
* Handle weird Windows SMB server behaviour. It responds with
|
|
|
|
* STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
|
|
|
|
* for "\<server>\<dfsname>\<linkpath>" DFS reference,
|
|
|
|
* where <dfsname> contains non-ASCII unicode symbols.
|
|
|
|
*
|
2022-05-18 22:41:05 +08:00
|
|
|
* Check such DFS reference.
|
2022-01-15 03:53:40 +08:00
|
|
|
*/
|
|
|
|
int cifs_dfs_query_info_nonascii_quirk(const unsigned int xid,
|
|
|
|
struct cifs_tcon *tcon,
|
|
|
|
struct cifs_sb_info *cifs_sb,
|
|
|
|
const char *linkpath)
|
|
|
|
{
|
|
|
|
char *treename, *dfspath, sep;
|
|
|
|
int treenamelen, linkpathlen, rc;
|
|
|
|
|
|
|
|
treename = tcon->treeName;
|
|
|
|
/* MS-DFSC: All paths in REQ_GET_DFS_REFERRAL and RESP_GET_DFS_REFERRAL
|
|
|
|
* messages MUST be encoded with exactly one leading backslash, not two
|
|
|
|
* leading backslashes.
|
|
|
|
*/
|
|
|
|
sep = CIFS_DIR_SEP(cifs_sb);
|
|
|
|
if (treename[0] == sep && treename[1] == sep)
|
|
|
|
treename++;
|
|
|
|
linkpathlen = strlen(linkpath);
|
|
|
|
treenamelen = strnlen(treename, MAX_TREE_SIZE + 1);
|
|
|
|
dfspath = kzalloc(treenamelen + linkpathlen + 1, GFP_KERNEL);
|
|
|
|
if (!dfspath)
|
|
|
|
return -ENOMEM;
|
|
|
|
if (treenamelen)
|
|
|
|
memcpy(dfspath, treename, treenamelen);
|
|
|
|
memcpy(dfspath + treenamelen, linkpath, linkpathlen);
|
|
|
|
rc = dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls,
|
|
|
|
cifs_remap(cifs_sb), dfspath, NULL, NULL);
|
|
|
|
if (rc == 0) {
|
|
|
|
cifs_dbg(FYI, "DFS ref '%s' is found, emulate -EREMOTE\n",
|
|
|
|
dfspath);
|
|
|
|
rc = -EREMOTE;
|
|
|
|
} else {
|
|
|
|
cifs_dbg(FYI, "%s: dfs_cache_find returned %d\n", __func__, rc);
|
|
|
|
}
|
|
|
|
kfree(dfspath);
|
|
|
|
return rc;
|
|
|
|
}
|
2021-11-04 00:53:29 +08:00
|
|
|
#endif
|