mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 10:58:48 +09:00
Merge e13300bdaa ("Merge tag '5.11-rc-smb3' of git://git.samba.org/sfrench/cifs-2.6") into android-mainline
Steps on the way to 5.11-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I00b4c83be7470ba30b99bc2c67d4e4ea7d4d815b
This commit is contained in:
@@ -60,9 +60,9 @@ config CIFS_STATS2
|
||||
Enabling this option will allow more detailed statistics on SMB
|
||||
request timing to be displayed in /proc/fs/cifs/DebugData and also
|
||||
allow optional logging of slow responses to dmesg (depending on the
|
||||
value of /proc/fs/cifs/cifsFYI, see fs/cifs/README for more details).
|
||||
These additional statistics may have a minor effect on performance
|
||||
and memory utilization.
|
||||
value of /proc/fs/cifs/cifsFYI). See Documentation/admin-guide/cifs/usage.rst
|
||||
for more details. These additional statistics may have a minor effect
|
||||
on performance and memory utilization.
|
||||
|
||||
Unless you are a developer or are doing network performance analysis
|
||||
or tuning, say N.
|
||||
@@ -102,10 +102,10 @@ config CIFS_WEAK_PW_HASH
|
||||
is enabled in the kernel build, LANMAN authentication will not be
|
||||
used automatically. At runtime LANMAN mounts are disabled but
|
||||
can be set to required (or optional) either in
|
||||
/proc/fs/cifs (see fs/cifs/README for more detail) or via an
|
||||
option on the mount command. This support is disabled by
|
||||
default in order to reduce the possibility of a downgrade
|
||||
attack.
|
||||
/proc/fs/cifs (see Documentation/admin-guide/cifs/usage.rst for
|
||||
more detail) or via an option on the mount command. This support
|
||||
is disabled by default in order to reduce the possibility of a
|
||||
downgrade attack.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
@@ -190,6 +190,17 @@ config CIFS_DFS_UPCALL
|
||||
servers if their addresses change or for implicit mounts of
|
||||
DFS junction points. If unsure, say Y.
|
||||
|
||||
config CIFS_SWN_UPCALL
|
||||
bool "SWN feature support"
|
||||
depends on CIFS
|
||||
help
|
||||
The Service Witness Protocol (SWN) is used to get notifications
|
||||
from a highly available server of resource state changes. This
|
||||
feature enables an upcall mechanism for CIFS which contacts a
|
||||
userspace daemon to establish the DCE/RPC connection to retrieve
|
||||
the cluster available interfaces and resource change notifications.
|
||||
If unsure, say Y.
|
||||
|
||||
config CIFS_NFSD_EXPORT
|
||||
bool "Allow nfsd to export CIFS file system"
|
||||
depends on CIFS && BROKEN
|
||||
|
||||
@@ -8,7 +8,7 @@ obj-$(CONFIG_CIFS) += cifs.o
|
||||
cifs-y := trace.o cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o \
|
||||
inode.o link.o misc.o netmisc.o smbencrypt.o transport.o asn1.o \
|
||||
cifs_unicode.o nterr.o cifsencrypt.o \
|
||||
readdir.o ioctl.o sess.o export.o smb1ops.o winucase.o \
|
||||
readdir.o ioctl.o sess.o export.o smb1ops.o unc.o winucase.o \
|
||||
smb2ops.o smb2maperror.o smb2transport.o \
|
||||
smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o
|
||||
|
||||
@@ -18,6 +18,8 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
|
||||
|
||||
cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o
|
||||
|
||||
cifs-$(CONFIG_CIFS_SWN_UPCALL) += netlink.o cifs_swn.o
|
||||
|
||||
cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
|
||||
|
||||
cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
|
||||
|
||||
@@ -53,30 +53,6 @@ const struct fscache_cookie_def cifs_fscache_server_index_def = {
|
||||
.type = FSCACHE_COOKIE_TYPE_INDEX,
|
||||
};
|
||||
|
||||
char *extract_sharename(const char *treename)
|
||||
{
|
||||
const char *src;
|
||||
char *delim, *dst;
|
||||
int len;
|
||||
|
||||
/* skip double chars at the beginning */
|
||||
src = treename + 2;
|
||||
|
||||
/* share name is always preceded by '\\' now */
|
||||
delim = strchr(src, '\\');
|
||||
if (!delim)
|
||||
return ERR_PTR(-EINVAL);
|
||||
delim++;
|
||||
len = strlen(delim);
|
||||
|
||||
/* caller has to free the memory */
|
||||
dst = kstrndup(delim, len, GFP_KERNEL);
|
||||
if (!dst)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
static enum
|
||||
fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,
|
||||
const void *data,
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||
#include "smbdirect.h"
|
||||
#endif
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
#include "cifs_swn.h"
|
||||
#endif
|
||||
|
||||
void
|
||||
cifs_dump_mem(char *label, void *data, int length)
|
||||
@@ -115,6 +118,10 @@ static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon)
|
||||
seq_printf(m, " POSIX Extensions");
|
||||
if (tcon->ses->server->ops->dump_share_caps)
|
||||
tcon->ses->server->ops->dump_share_caps(m, tcon);
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
if (tcon->use_witness)
|
||||
seq_puts(m, " Witness");
|
||||
#endif
|
||||
|
||||
if (tcon->need_reconnect)
|
||||
seq_puts(m, "\tDISCONNECTED ");
|
||||
@@ -262,6 +269,9 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
||||
seq_printf(m, ",XATTR");
|
||||
#endif
|
||||
seq_printf(m, ",ACL");
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
seq_puts(m, ",WITNESS");
|
||||
#endif
|
||||
seq_putc(m, '\n');
|
||||
seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize);
|
||||
seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid);
|
||||
@@ -462,6 +472,9 @@ skip_rdma:
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
seq_putc(m, '\n');
|
||||
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
cifs_swn_dump(m);
|
||||
#endif
|
||||
/* BB add code to dump additional info such as TCP session info now */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "cifs_debug.h"
|
||||
#include "cifs_unicode.h"
|
||||
#include "dfs_cache.h"
|
||||
#include "fs_context.h"
|
||||
|
||||
static LIST_HEAD(cifs_dfs_automount_list);
|
||||
|
||||
@@ -124,7 +125,6 @@ cifs_build_devname(char *nodename, const char *prepath)
|
||||
* @sb_mountdata: parent/root DFS mount options (template)
|
||||
* @fullpath: full path in UNC format
|
||||
* @ref: optional server's referral
|
||||
* @devname: optional pointer for saving device name
|
||||
*
|
||||
* creates mount options for submount based on template options sb_mountdata
|
||||
* and replacing unc,ip,prefixpath options with ones we've got form ref_unc.
|
||||
@@ -134,8 +134,7 @@ cifs_build_devname(char *nodename, const char *prepath)
|
||||
*/
|
||||
char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||
const char *fullpath,
|
||||
const struct dfs_info3_param *ref,
|
||||
char **devname)
|
||||
const struct dfs_info3_param *ref)
|
||||
{
|
||||
int rc;
|
||||
char *name;
|
||||
@@ -232,10 +231,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||
strcat(mountdata, "ip=");
|
||||
strcat(mountdata, srvIP);
|
||||
|
||||
if (devname)
|
||||
*devname = name;
|
||||
else
|
||||
kfree(name);
|
||||
kfree(name);
|
||||
|
||||
/*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/
|
||||
/*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/
|
||||
@@ -258,6 +254,7 @@ compose_mount_options_err:
|
||||
* to perform failover in case we failed to connect to the first target in the
|
||||
* referral.
|
||||
*
|
||||
* @mntpt: directory entry for the path we are trying to automount
|
||||
* @cifs_sb: parent/root superblock
|
||||
* @fullpath: full path in UNC format
|
||||
*/
|
||||
@@ -275,9 +272,13 @@ static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
|
||||
|
||||
convert_delimiter(devname, '/');
|
||||
|
||||
/* TODO: change to call fs_context_for_mount(), fill in context directly, call fc_mount */
|
||||
|
||||
/* See afs_mntpt_do_automount in fs/afs/mntpt.c for an example */
|
||||
|
||||
/* strip first '\' from fullpath */
|
||||
mountdata = cifs_compose_mount_options(cifs_sb->mountdata,
|
||||
fullpath + 1, NULL, NULL);
|
||||
mountdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options,
|
||||
fullpath + 1, NULL);
|
||||
if (IS_ERR(mountdata)) {
|
||||
kfree(devname);
|
||||
return (struct vfsmount *)mountdata;
|
||||
|
||||
@@ -61,19 +61,9 @@ struct cifs_sb_info {
|
||||
spinlock_t tlink_tree_lock;
|
||||
struct tcon_link *master_tlink;
|
||||
struct nls_table *local_nls;
|
||||
unsigned int bsize;
|
||||
unsigned int rsize;
|
||||
unsigned int wsize;
|
||||
unsigned long actimeo; /* attribute cache timeout (jiffies) */
|
||||
struct smb3_fs_context *ctx;
|
||||
atomic_t active;
|
||||
kuid_t mnt_uid;
|
||||
kgid_t mnt_gid;
|
||||
kuid_t mnt_backupuid;
|
||||
kgid_t mnt_backupgid;
|
||||
umode_t mnt_file_mode;
|
||||
umode_t mnt_dir_mode;
|
||||
unsigned int mnt_cifs_flags;
|
||||
char *mountdata; /* options received at mount time or via DFS refs */
|
||||
struct delayed_work prune_tlinks;
|
||||
struct rcu_head rcu;
|
||||
|
||||
|
||||
694
fs/cifs/cifs_swn.c
Normal file
694
fs/cifs/cifs_swn.c
Normal file
@@ -0,0 +1,694 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Witness Service client for CIFS
|
||||
*
|
||||
* Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
|
||||
*/
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <uapi/linux/cifs/cifs_netlink.h>
|
||||
|
||||
#include "cifs_swn.h"
|
||||
#include "cifsglob.h"
|
||||
#include "cifsproto.h"
|
||||
#include "fscache.h"
|
||||
#include "cifs_debug.h"
|
||||
#include "netlink.h"
|
||||
|
||||
static DEFINE_IDR(cifs_swnreg_idr);
|
||||
static DEFINE_MUTEX(cifs_swnreg_idr_mutex);
|
||||
|
||||
struct cifs_swn_reg {
|
||||
int id;
|
||||
struct kref ref_count;
|
||||
|
||||
const char *net_name;
|
||||
const char *share_name;
|
||||
bool net_name_notify;
|
||||
bool share_name_notify;
|
||||
bool ip_notify;
|
||||
|
||||
struct cifs_tcon *tcon;
|
||||
};
|
||||
|
||||
static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_KRB_AUTH);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cifs_swn_auth_info_ntlm(struct cifs_tcon *tcon, struct sk_buff *skb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (tcon->ses->user_name != NULL) {
|
||||
ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_USER_NAME, tcon->ses->user_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (tcon->ses->password != NULL) {
|
||||
ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_PASSWORD, tcon->ses->password);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (tcon->ses->domainName != NULL) {
|
||||
ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_DOMAIN_NAME, tcon->ses->domainName);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends a register message to the userspace daemon based on the registration.
|
||||
* The authentication information to connect to the witness service is bundled
|
||||
* into the message.
|
||||
*/
|
||||
static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct genlmsghdr *hdr;
|
||||
enum securityEnum authtype;
|
||||
struct sockaddr_storage *addr;
|
||||
int ret;
|
||||
|
||||
skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (skb == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER);
|
||||
if (hdr == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto nlmsg_fail;
|
||||
}
|
||||
|
||||
ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
|
||||
ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
|
||||
ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
|
||||
/*
|
||||
* If there is an address stored use it instead of the server address, because we are
|
||||
* in the process of reconnecting to it after a share has been moved or we have been
|
||||
* told to switch to it (client move message). In these cases we unregister from the
|
||||
* server address and register to the new address when we receive the notification.
|
||||
*/
|
||||
if (swnreg->tcon->ses->server->use_swn_dstaddr)
|
||||
addr = &swnreg->tcon->ses->server->swn_dstaddr;
|
||||
else
|
||||
addr = &swnreg->tcon->ses->server->dstaddr;
|
||||
|
||||
ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
|
||||
if (swnreg->net_name_notify) {
|
||||
ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
}
|
||||
|
||||
if (swnreg->share_name_notify) {
|
||||
ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
}
|
||||
|
||||
if (swnreg->ip_notify) {
|
||||
ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
}
|
||||
|
||||
authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype);
|
||||
switch (authtype) {
|
||||
case Kerberos:
|
||||
ret = cifs_swn_auth_info_krb(swnreg->tcon, skb);
|
||||
if (ret < 0) {
|
||||
cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret);
|
||||
goto nlmsg_fail;
|
||||
}
|
||||
break;
|
||||
case LANMAN:
|
||||
case NTLM:
|
||||
case NTLMv2:
|
||||
case RawNTLMSSP:
|
||||
ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb);
|
||||
if (ret < 0) {
|
||||
cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret);
|
||||
goto nlmsg_fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cifs_dbg(VFS, "%s: secType %d not supported!\n", __func__, authtype);
|
||||
ret = -EINVAL;
|
||||
goto nlmsg_fail;
|
||||
}
|
||||
|
||||
genlmsg_end(skb, hdr);
|
||||
genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
|
||||
|
||||
cifs_dbg(FYI, "%s: Message to register for network name %s with id %d sent\n", __func__,
|
||||
swnreg->net_name, swnreg->id);
|
||||
|
||||
return 0;
|
||||
|
||||
nlmsg_fail:
|
||||
genlmsg_cancel(skb, hdr);
|
||||
nlmsg_free(skb);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends an uregister message to the userspace daemon based on the registration
|
||||
*/
|
||||
static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct genlmsghdr *hdr;
|
||||
int ret;
|
||||
|
||||
skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_UNREGISTER);
|
||||
if (hdr == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto nlmsg_fail;
|
||||
}
|
||||
|
||||
ret = nla_put_u32(skb, CIFS_GENL_ATTR_SWN_REGISTRATION_ID, swnreg->id);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
|
||||
ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_NET_NAME, swnreg->net_name);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
|
||||
ret = nla_put_string(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME, swnreg->share_name);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
|
||||
ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage),
|
||||
&swnreg->tcon->ses->server->dstaddr);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
|
||||
if (swnreg->net_name_notify) {
|
||||
ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
}
|
||||
|
||||
if (swnreg->share_name_notify) {
|
||||
ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
}
|
||||
|
||||
if (swnreg->ip_notify) {
|
||||
ret = nla_put_flag(skb, CIFS_GENL_ATTR_SWN_IP_NOTIFY);
|
||||
if (ret < 0)
|
||||
goto nlmsg_fail;
|
||||
}
|
||||
|
||||
genlmsg_end(skb, hdr);
|
||||
genlmsg_multicast(&cifs_genl_family, skb, 0, CIFS_GENL_MCGRP_SWN, GFP_ATOMIC);
|
||||
|
||||
cifs_dbg(FYI, "%s: Message to unregister for network name %s with id %d sent\n", __func__,
|
||||
swnreg->net_name, swnreg->id);
|
||||
|
||||
return 0;
|
||||
|
||||
nlmsg_fail:
|
||||
genlmsg_cancel(skb, hdr);
|
||||
nlmsg_free(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to find a matching registration for the tcon's server name and share name.
|
||||
* Calls to this funciton must be protected by cifs_swnreg_idr_mutex.
|
||||
* TODO Try to avoid memory allocations
|
||||
*/
|
||||
static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon)
|
||||
{
|
||||
struct cifs_swn_reg *swnreg;
|
||||
int id;
|
||||
const char *share_name;
|
||||
const char *net_name;
|
||||
|
||||
net_name = extract_hostname(tcon->treeName);
|
||||
if (IS_ERR(net_name)) {
|
||||
int ret;
|
||||
|
||||
ret = PTR_ERR(net_name);
|
||||
cifs_dbg(VFS, "%s: failed to extract host name from target '%s': %d\n",
|
||||
__func__, tcon->treeName, ret);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
share_name = extract_sharename(tcon->treeName);
|
||||
if (IS_ERR(share_name)) {
|
||||
int ret;
|
||||
|
||||
ret = PTR_ERR(net_name);
|
||||
cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n",
|
||||
__func__, tcon->treeName, ret);
|
||||
kfree(net_name);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
|
||||
if (strcasecmp(swnreg->net_name, net_name) != 0
|
||||
|| strcasecmp(swnreg->share_name, share_name) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
|
||||
cifs_dbg(FYI, "Existing swn registration for %s:%s found\n", swnreg->net_name,
|
||||
swnreg->share_name);
|
||||
|
||||
kfree(net_name);
|
||||
kfree(share_name);
|
||||
|
||||
return swnreg;
|
||||
}
|
||||
|
||||
kfree(net_name);
|
||||
kfree(share_name);
|
||||
|
||||
return ERR_PTR(-EEXIST);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a registration for the tcon's server and share name, allocating a new one if it does not
|
||||
* exists
|
||||
*/
|
||||
static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
|
||||
{
|
||||
struct cifs_swn_reg *reg = NULL;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&cifs_swnreg_idr_mutex);
|
||||
|
||||
/* Check if we are already registered for this network and share names */
|
||||
reg = cifs_find_swn_reg(tcon);
|
||||
if (!IS_ERR(reg)) {
|
||||
kref_get(®->ref_count);
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
return reg;
|
||||
} else if (PTR_ERR(reg) != -EEXIST) {
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
return reg;
|
||||
}
|
||||
|
||||
reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC);
|
||||
if (reg == NULL) {
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
kref_init(®->ref_count);
|
||||
|
||||
reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC);
|
||||
if (reg->id < 0) {
|
||||
cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__);
|
||||
ret = reg->id;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
reg->net_name = extract_hostname(tcon->treeName);
|
||||
if (IS_ERR(reg->net_name)) {
|
||||
ret = PTR_ERR(reg->net_name);
|
||||
cifs_dbg(VFS, "%s: failed to extract host name from target: %d\n", __func__, ret);
|
||||
goto fail_idr;
|
||||
}
|
||||
|
||||
reg->share_name = extract_sharename(tcon->treeName);
|
||||
if (IS_ERR(reg->share_name)) {
|
||||
ret = PTR_ERR(reg->share_name);
|
||||
cifs_dbg(VFS, "%s: failed to extract share name from target: %d\n", __func__, ret);
|
||||
goto fail_net_name;
|
||||
}
|
||||
|
||||
reg->net_name_notify = true;
|
||||
reg->share_name_notify = true;
|
||||
reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
|
||||
|
||||
reg->tcon = tcon;
|
||||
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
|
||||
return reg;
|
||||
|
||||
fail_net_name:
|
||||
kfree(reg->net_name);
|
||||
fail_idr:
|
||||
idr_remove(&cifs_swnreg_idr, reg->id);
|
||||
fail:
|
||||
kfree(reg);
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void cifs_swn_reg_release(struct kref *ref)
|
||||
{
|
||||
struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count);
|
||||
int ret;
|
||||
|
||||
ret = cifs_swn_send_unregister_message(swnreg);
|
||||
if (ret < 0)
|
||||
cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret);
|
||||
|
||||
idr_remove(&cifs_swnreg_idr, swnreg->id);
|
||||
kfree(swnreg->net_name);
|
||||
kfree(swnreg->share_name);
|
||||
kfree(swnreg);
|
||||
}
|
||||
|
||||
static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg)
|
||||
{
|
||||
mutex_lock(&cifs_swnreg_idr_mutex);
|
||||
kref_put(&swnreg->ref_count, cifs_swn_reg_release);
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
}
|
||||
|
||||
static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state)
|
||||
{
|
||||
int i;
|
||||
|
||||
switch (state) {
|
||||
case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE:
|
||||
cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name);
|
||||
for (i = 0; i < swnreg->tcon->ses->chan_count; i++) {
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (swnreg->tcon->ses->chans[i].server->tcpStatus != CifsExiting)
|
||||
swnreg->tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
break;
|
||||
case CIFS_SWN_RESOURCE_STATE_AVAILABLE:
|
||||
cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name);
|
||||
for (i = 0; i < swnreg->tcon->ses->chan_count; i++) {
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (swnreg->tcon->ses->chans[i].server->tcpStatus != CifsExiting)
|
||||
swnreg->tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
break;
|
||||
case CIFS_SWN_RESOURCE_STATE_UNKNOWN:
|
||||
cifs_dbg(FYI, "%s: resource name '%s' changed to unknown state\n", __func__, name);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool cifs_sockaddr_equal(struct sockaddr_storage *addr1, struct sockaddr_storage *addr2)
|
||||
{
|
||||
if (addr1->ss_family != addr2->ss_family)
|
||||
return false;
|
||||
|
||||
if (addr1->ss_family == AF_INET) {
|
||||
return (memcmp(&((const struct sockaddr_in *)addr1)->sin_addr,
|
||||
&((const struct sockaddr_in *)addr2)->sin_addr,
|
||||
sizeof(struct in_addr)) == 0);
|
||||
}
|
||||
|
||||
if (addr1->ss_family == AF_INET6) {
|
||||
return (memcmp(&((const struct sockaddr_in6 *)addr1)->sin6_addr,
|
||||
&((const struct sockaddr_in6 *)addr2)->sin6_addr,
|
||||
sizeof(struct in6_addr)) == 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int cifs_swn_store_swn_addr(const struct sockaddr_storage *new,
|
||||
const struct sockaddr_storage *old,
|
||||
struct sockaddr_storage *dst)
|
||||
{
|
||||
__be16 port;
|
||||
|
||||
if (old->ss_family == AF_INET) {
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)old;
|
||||
|
||||
port = ipv4->sin_port;
|
||||
}
|
||||
|
||||
if (old->ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)old;
|
||||
|
||||
port = ipv6->sin6_port;
|
||||
}
|
||||
|
||||
if (new->ss_family == AF_INET) {
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)new;
|
||||
|
||||
ipv4->sin_port = port;
|
||||
}
|
||||
|
||||
if (new->ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)new;
|
||||
|
||||
ipv6->sin6_port = port;
|
||||
}
|
||||
|
||||
*dst = *new;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *addr)
|
||||
{
|
||||
/* Store the reconnect address */
|
||||
mutex_lock(&tcon->ses->server->srv_mutex);
|
||||
if (!cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr)) {
|
||||
int ret;
|
||||
|
||||
ret = cifs_swn_store_swn_addr(addr, &tcon->ses->server->dstaddr,
|
||||
&tcon->ses->server->swn_dstaddr);
|
||||
if (ret < 0) {
|
||||
cifs_dbg(VFS, "%s: failed to store address: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
tcon->ses->server->use_swn_dstaddr = true;
|
||||
|
||||
/*
|
||||
* Unregister to stop receiving notifications for the old IP address.
|
||||
*/
|
||||
ret = cifs_swn_unregister(tcon);
|
||||
if (ret < 0) {
|
||||
cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* And register to receive notifications for the new IP address now that we have
|
||||
* stored the new address.
|
||||
*/
|
||||
ret = cifs_swn_register(tcon);
|
||||
if (ret < 0) {
|
||||
cifs_dbg(VFS, "%s: Failed to register for witness notifications: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (tcon->ses->server->tcpStatus != CifsExiting)
|
||||
tcon->ses->server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
mutex_unlock(&tcon->ses->server->srv_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr)
|
||||
{
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr;
|
||||
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr;
|
||||
|
||||
if (addr->ss_family == AF_INET)
|
||||
cifs_dbg(FYI, "%s: move to %pI4\n", __func__, &ipv4->sin_addr);
|
||||
else if (addr->ss_family == AF_INET6)
|
||||
cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr);
|
||||
|
||||
return cifs_swn_reconnect(swnreg->tcon, addr);
|
||||
}
|
||||
|
||||
int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cifs_swn_reg *swnreg;
|
||||
char name[256];
|
||||
int type;
|
||||
|
||||
if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) {
|
||||
int swnreg_id;
|
||||
|
||||
swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]);
|
||||
mutex_lock(&cifs_swnreg_idr_mutex);
|
||||
swnreg = idr_find(&cifs_swnreg_idr, swnreg_id);
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
if (swnreg == NULL) {
|
||||
cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) {
|
||||
type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]);
|
||||
} else {
|
||||
cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE: {
|
||||
int state;
|
||||
|
||||
if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME]) {
|
||||
nla_strscpy(name, info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_NAME],
|
||||
sizeof(name));
|
||||
} else {
|
||||
cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) {
|
||||
state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]);
|
||||
} else {
|
||||
cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
return cifs_swn_resource_state_changed(swnreg, name, state);
|
||||
}
|
||||
case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: {
|
||||
struct sockaddr_storage addr;
|
||||
|
||||
if (info->attrs[CIFS_GENL_ATTR_SWN_IP]) {
|
||||
nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr));
|
||||
} else {
|
||||
cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
return cifs_swn_client_move(swnreg, &addr);
|
||||
}
|
||||
default:
|
||||
cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cifs_swn_register(struct cifs_tcon *tcon)
|
||||
{
|
||||
struct cifs_swn_reg *swnreg;
|
||||
int ret;
|
||||
|
||||
swnreg = cifs_get_swn_reg(tcon);
|
||||
if (IS_ERR(swnreg))
|
||||
return PTR_ERR(swnreg);
|
||||
|
||||
ret = cifs_swn_send_register_message(swnreg);
|
||||
if (ret < 0) {
|
||||
cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret);
|
||||
/* Do not put the swnreg or return error, the echo task will retry */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cifs_swn_unregister(struct cifs_tcon *tcon)
|
||||
{
|
||||
struct cifs_swn_reg *swnreg;
|
||||
|
||||
mutex_lock(&cifs_swnreg_idr_mutex);
|
||||
|
||||
swnreg = cifs_find_swn_reg(tcon);
|
||||
if (IS_ERR(swnreg)) {
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
return PTR_ERR(swnreg);
|
||||
}
|
||||
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
|
||||
cifs_put_swn_reg(swnreg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cifs_swn_dump(struct seq_file *m)
|
||||
{
|
||||
struct cifs_swn_reg *swnreg;
|
||||
struct sockaddr_in *sa;
|
||||
struct sockaddr_in6 *sa6;
|
||||
int id;
|
||||
|
||||
seq_puts(m, "Witness registrations:");
|
||||
|
||||
mutex_lock(&cifs_swnreg_idr_mutex);
|
||||
idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
|
||||
seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ",
|
||||
id, kref_read(&swnreg->ref_count),
|
||||
swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)",
|
||||
swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)");
|
||||
switch (swnreg->tcon->ses->server->dstaddr.ss_family) {
|
||||
case AF_INET:
|
||||
sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr;
|
||||
seq_printf(m, "%pI4", &sa->sin_addr.s_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr;
|
||||
seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr);
|
||||
if (sa6->sin6_scope_id)
|
||||
seq_printf(m, "%%%u", sa6->sin6_scope_id);
|
||||
break;
|
||||
default:
|
||||
seq_puts(m, "(unknown)");
|
||||
}
|
||||
seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)");
|
||||
}
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
seq_puts(m, "\n");
|
||||
}
|
||||
|
||||
void cifs_swn_check(void)
|
||||
{
|
||||
struct cifs_swn_reg *swnreg;
|
||||
int id;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&cifs_swnreg_idr_mutex);
|
||||
idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) {
|
||||
ret = cifs_swn_send_register_message(swnreg);
|
||||
if (ret < 0)
|
||||
cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret);
|
||||
}
|
||||
mutex_unlock(&cifs_swnreg_idr_mutex);
|
||||
}
|
||||
25
fs/cifs/cifs_swn.h
Normal file
25
fs/cifs/cifs_swn.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Witness Service client for CIFS
|
||||
*
|
||||
* Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
|
||||
*/
|
||||
|
||||
#ifndef _CIFS_SWN_H
|
||||
#define _CIFS_SWN_H
|
||||
|
||||
struct cifs_tcon;
|
||||
struct sk_buff;
|
||||
struct genl_info;
|
||||
|
||||
extern int cifs_swn_register(struct cifs_tcon *tcon);
|
||||
|
||||
extern int cifs_swn_unregister(struct cifs_tcon *tcon);
|
||||
|
||||
extern int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info);
|
||||
|
||||
extern void cifs_swn_dump(struct seq_file *m);
|
||||
|
||||
extern void cifs_swn_check(void);
|
||||
|
||||
#endif /* _CIFS_SWN_H */
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "cifsacl.h"
|
||||
#include "cifsproto.h"
|
||||
#include "cifs_debug.h"
|
||||
#include "fs_context.h"
|
||||
|
||||
/* security id for everyone/world system group */
|
||||
static const struct cifs_sid sid_everyone = {
|
||||
@@ -346,8 +347,8 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
|
||||
struct key *sidkey;
|
||||
char *sidstr;
|
||||
const struct cred *saved_cred;
|
||||
kuid_t fuid = cifs_sb->mnt_uid;
|
||||
kgid_t fgid = cifs_sb->mnt_gid;
|
||||
kuid_t fuid = cifs_sb->ctx->linux_uid;
|
||||
kgid_t fgid = cifs_sb->ctx->linux_gid;
|
||||
|
||||
/*
|
||||
* If we have too many subauthorities, then something is really wrong.
|
||||
@@ -448,7 +449,7 @@ out_revert_creds:
|
||||
|
||||
/*
|
||||
* Note that we return 0 here unconditionally. If the mapping
|
||||
* fails then we just fall back to using the mnt_uid/mnt_gid.
|
||||
* fails then we just fall back to using the ctx->linux_uid/linux_gid.
|
||||
*/
|
||||
got_valid_id:
|
||||
rc = 0;
|
||||
@@ -557,30 +558,37 @@ static void copy_sec_desc(const struct cifs_ntsd *pntsd,
|
||||
bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007
|
||||
*/
|
||||
static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode,
|
||||
umode_t *pbits_to_set)
|
||||
umode_t *pdenied, umode_t mask)
|
||||
{
|
||||
__u32 flags = le32_to_cpu(ace_flags);
|
||||
/* the order of ACEs is important. The canonical order is to begin with
|
||||
DENY entries followed by ALLOW, otherwise an allow entry could be
|
||||
encountered first, making the subsequent deny entry like "dead code"
|
||||
which would be superflous since Windows stops when a match is made
|
||||
for the operation you are trying to perform for your user */
|
||||
/*
|
||||
* Do not assume "preferred" or "canonical" order.
|
||||
* The first DENY or ALLOW ACE which matches perfectly is
|
||||
* the permission to be used. Once allowed or denied, same
|
||||
* permission in later ACEs do not matter.
|
||||
*/
|
||||
|
||||
/* For deny ACEs we change the mask so that subsequent allow access
|
||||
control entries do not turn on the bits we are denying */
|
||||
/* If not already allowed, deny these bits */
|
||||
if (type == ACCESS_DENIED) {
|
||||
if (flags & GENERIC_ALL)
|
||||
*pbits_to_set &= ~S_IRWXUGO;
|
||||
if (flags & GENERIC_ALL &&
|
||||
!(*pmode & mask & 0777))
|
||||
*pdenied |= mask & 0777;
|
||||
|
||||
if (((flags & GENERIC_WRITE) ||
|
||||
((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) &&
|
||||
!(*pmode & mask & 0222))
|
||||
*pdenied |= mask & 0222;
|
||||
|
||||
if (((flags & GENERIC_READ) ||
|
||||
((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) &&
|
||||
!(*pmode & mask & 0444))
|
||||
*pdenied |= mask & 0444;
|
||||
|
||||
if (((flags & GENERIC_EXECUTE) ||
|
||||
((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) &&
|
||||
!(*pmode & mask & 0111))
|
||||
*pdenied |= mask & 0111;
|
||||
|
||||
if ((flags & GENERIC_WRITE) ||
|
||||
((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
|
||||
*pbits_to_set &= ~S_IWUGO;
|
||||
if ((flags & GENERIC_READ) ||
|
||||
((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
|
||||
*pbits_to_set &= ~S_IRUGO;
|
||||
if ((flags & GENERIC_EXECUTE) ||
|
||||
((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
|
||||
*pbits_to_set &= ~S_IXUGO;
|
||||
return;
|
||||
} else if (type != ACCESS_ALLOWED) {
|
||||
cifs_dbg(VFS, "unknown access control type %d\n", type);
|
||||
@@ -588,20 +596,38 @@ static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode,
|
||||
}
|
||||
/* else ACCESS_ALLOWED type */
|
||||
|
||||
if (flags & GENERIC_ALL) {
|
||||
*pmode |= (S_IRWXUGO & (*pbits_to_set));
|
||||
if ((flags & GENERIC_ALL) &&
|
||||
!(*pdenied & mask & 0777)) {
|
||||
*pmode |= mask & 0777;
|
||||
cifs_dbg(NOISY, "all perms\n");
|
||||
return;
|
||||
}
|
||||
if ((flags & GENERIC_WRITE) ||
|
||||
((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
|
||||
*pmode |= (S_IWUGO & (*pbits_to_set));
|
||||
if ((flags & GENERIC_READ) ||
|
||||
((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
|
||||
*pmode |= (S_IRUGO & (*pbits_to_set));
|
||||
if ((flags & GENERIC_EXECUTE) ||
|
||||
((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
|
||||
*pmode |= (S_IXUGO & (*pbits_to_set));
|
||||
|
||||
if (((flags & GENERIC_WRITE) ||
|
||||
((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) &&
|
||||
!(*pdenied & mask & 0222))
|
||||
*pmode |= mask & 0222;
|
||||
|
||||
if (((flags & GENERIC_READ) ||
|
||||
((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) &&
|
||||
!(*pdenied & mask & 0444))
|
||||
*pmode |= mask & 0444;
|
||||
|
||||
if (((flags & GENERIC_EXECUTE) ||
|
||||
((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) &&
|
||||
!(*pdenied & mask & 0111))
|
||||
*pmode |= mask & 0111;
|
||||
|
||||
/* If DELETE_CHILD is set only on an owner ACE, set sticky bit */
|
||||
if (flags & FILE_DELETE_CHILD) {
|
||||
if (mask == ACL_OWNER_MASK) {
|
||||
if (!(*pdenied & 01000))
|
||||
*pmode |= 01000;
|
||||
} else if (!(*pdenied & 01000)) {
|
||||
*pmode &= ~01000;
|
||||
*pdenied |= 01000;
|
||||
}
|
||||
}
|
||||
|
||||
cifs_dbg(NOISY, "access flags 0x%x mode now %04o\n", flags, *pmode);
|
||||
return;
|
||||
@@ -638,17 +664,26 @@ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
|
||||
}
|
||||
|
||||
static __u16 fill_ace_for_sid(struct cifs_ace *pntace,
|
||||
const struct cifs_sid *psid, __u64 nmode, umode_t bits)
|
||||
const struct cifs_sid *psid, __u64 nmode,
|
||||
umode_t bits, __u8 access_type,
|
||||
bool allow_delete_child)
|
||||
{
|
||||
int i;
|
||||
__u16 size = 0;
|
||||
__u32 access_req = 0;
|
||||
|
||||
pntace->type = ACCESS_ALLOWED;
|
||||
pntace->type = access_type;
|
||||
pntace->flags = 0x0;
|
||||
mode_to_access_flags(nmode, bits, &access_req);
|
||||
if (!access_req)
|
||||
|
||||
if (access_type == ACCESS_ALLOWED && allow_delete_child)
|
||||
access_req |= FILE_DELETE_CHILD;
|
||||
|
||||
if (access_type == ACCESS_ALLOWED && !access_req)
|
||||
access_req = SET_MINIMUM_RIGHTS;
|
||||
else if (access_type == ACCESS_DENIED)
|
||||
access_req &= ~SET_MINIMUM_RIGHTS;
|
||||
|
||||
pntace->access_req = cpu_to_le32(access_req);
|
||||
|
||||
pntace->sid.revision = psid->revision;
|
||||
@@ -716,7 +751,7 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
|
||||
if (!pdacl) {
|
||||
/* no DACL in the security descriptor, set
|
||||
all the permissions for user/group/other */
|
||||
fattr->cf_mode |= S_IRWXUGO;
|
||||
fattr->cf_mode |= 0777;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -733,16 +768,14 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
|
||||
/* reset rwx permissions for user/group/other.
|
||||
Also, if num_aces is 0 i.e. DACL has no ACEs,
|
||||
user/group/other have no permissions */
|
||||
fattr->cf_mode &= ~(S_IRWXUGO);
|
||||
fattr->cf_mode &= ~(0777);
|
||||
|
||||
acl_base = (char *)pdacl;
|
||||
acl_size = sizeof(struct cifs_acl);
|
||||
|
||||
num_aces = le32_to_cpu(pdacl->num_aces);
|
||||
if (num_aces > 0) {
|
||||
umode_t user_mask = S_IRWXU;
|
||||
umode_t group_mask = S_IRWXG;
|
||||
umode_t other_mask = S_IRWXU | S_IRWXG | S_IRWXO;
|
||||
umode_t denied_mode = 0;
|
||||
|
||||
if (num_aces > ULONG_MAX / sizeof(struct cifs_ace *))
|
||||
return;
|
||||
@@ -768,26 +801,28 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
|
||||
fattr->cf_mode |=
|
||||
le32_to_cpu(ppace[i]->sid.sub_auth[2]);
|
||||
break;
|
||||
} else if (compare_sids(&(ppace[i]->sid), pownersid) == 0)
|
||||
access_flags_to_mode(ppace[i]->access_req,
|
||||
ppace[i]->type,
|
||||
&fattr->cf_mode,
|
||||
&user_mask);
|
||||
else if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0)
|
||||
access_flags_to_mode(ppace[i]->access_req,
|
||||
ppace[i]->type,
|
||||
&fattr->cf_mode,
|
||||
&group_mask);
|
||||
else if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0)
|
||||
access_flags_to_mode(ppace[i]->access_req,
|
||||
ppace[i]->type,
|
||||
&fattr->cf_mode,
|
||||
&other_mask);
|
||||
else if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)
|
||||
access_flags_to_mode(ppace[i]->access_req,
|
||||
ppace[i]->type,
|
||||
&fattr->cf_mode,
|
||||
&other_mask);
|
||||
} else {
|
||||
if (compare_sids(&(ppace[i]->sid), pownersid) == 0) {
|
||||
access_flags_to_mode(ppace[i]->access_req,
|
||||
ppace[i]->type,
|
||||
&fattr->cf_mode,
|
||||
&denied_mode,
|
||||
ACL_OWNER_MASK);
|
||||
} else if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) {
|
||||
access_flags_to_mode(ppace[i]->access_req,
|
||||
ppace[i]->type,
|
||||
&fattr->cf_mode,
|
||||
&denied_mode,
|
||||
ACL_GROUP_MASK);
|
||||
} else if ((compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) ||
|
||||
(compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)) {
|
||||
access_flags_to_mode(ppace[i]->access_req,
|
||||
ppace[i]->type,
|
||||
&fattr->cf_mode,
|
||||
&denied_mode,
|
||||
ACL_EVERYONE_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* memcpy((void *)(&(cifscred->aces[i])),
|
||||
@@ -873,32 +908,91 @@ unsigned int setup_special_user_owner_ACE(struct cifs_ace *pntace)
|
||||
}
|
||||
|
||||
static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
|
||||
struct cifs_sid *pgrpsid, __u64 nmode, bool modefromsid)
|
||||
struct cifs_sid *pgrpsid, __u64 *pnmode, bool modefromsid)
|
||||
{
|
||||
u16 size = 0;
|
||||
u32 num_aces = 0;
|
||||
struct cifs_acl *pnndacl;
|
||||
__u64 nmode;
|
||||
__u64 user_mode;
|
||||
__u64 group_mode;
|
||||
__u64 other_mode;
|
||||
__u64 deny_user_mode = 0;
|
||||
__u64 deny_group_mode = 0;
|
||||
bool sticky_set = false;
|
||||
|
||||
pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
|
||||
|
||||
nmode = *pnmode;
|
||||
|
||||
if (modefromsid) {
|
||||
struct cifs_ace *pntace =
|
||||
(struct cifs_ace *)((char *)pnndacl + size);
|
||||
|
||||
size += setup_special_mode_ACE(pntace, nmode);
|
||||
num_aces++;
|
||||
goto set_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* We'll try to keep the mode as requested by the user.
|
||||
* But in cases where we cannot meaningfully convert that
|
||||
* into ACL, return back the updated mode, so that it is
|
||||
* updated in the inode.
|
||||
*/
|
||||
|
||||
if (!memcmp(pownersid, pgrpsid, sizeof(struct cifs_sid))) {
|
||||
/*
|
||||
* Case when owner and group SIDs are the same.
|
||||
* Set the more restrictive of the two modes.
|
||||
*/
|
||||
user_mode = nmode & (nmode << 3) & 0700;
|
||||
group_mode = nmode & (nmode >> 3) & 0070;
|
||||
} else {
|
||||
user_mode = nmode & 0700;
|
||||
group_mode = nmode & 0070;
|
||||
}
|
||||
|
||||
other_mode = nmode & 0007;
|
||||
|
||||
/* We need DENY ACE when the perm is more restrictive than the next sets. */
|
||||
deny_user_mode = ~(user_mode) & ((group_mode << 3) | (other_mode << 6)) & 0700;
|
||||
deny_group_mode = ~(group_mode) & (other_mode << 3) & 0070;
|
||||
|
||||
*pnmode = user_mode | group_mode | other_mode | (nmode & ~0777);
|
||||
|
||||
/* This tells if we should allow delete child for group and everyone. */
|
||||
if (nmode & 01000)
|
||||
sticky_set = true;
|
||||
|
||||
if (deny_user_mode) {
|
||||
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
|
||||
pownersid, deny_user_mode, 0700, ACCESS_DENIED, false);
|
||||
num_aces++;
|
||||
}
|
||||
/* Group DENY ACE does not conflict with owner ALLOW ACE. Keep in preferred order*/
|
||||
if (deny_group_mode && !(deny_group_mode & (user_mode >> 3))) {
|
||||
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
|
||||
pgrpsid, deny_group_mode, 0070, ACCESS_DENIED, false);
|
||||
num_aces++;
|
||||
}
|
||||
size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
|
||||
pownersid, nmode, S_IRWXU);
|
||||
pownersid, user_mode, 0700, ACCESS_ALLOWED, true);
|
||||
num_aces++;
|
||||
/* Group DENY ACE conflicts with owner ALLOW ACE. So keep it after. */
|
||||
if (deny_group_mode && (deny_group_mode & (user_mode >> 3))) {
|
||||
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
|
||||
pgrpsid, deny_group_mode, 0070, ACCESS_DENIED, false);
|
||||
num_aces++;
|
||||
}
|
||||
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
|
||||
pgrpsid, group_mode, 0070, ACCESS_ALLOWED, !sticky_set);
|
||||
num_aces++;
|
||||
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
|
||||
pgrpsid, nmode, S_IRWXG);
|
||||
num_aces++;
|
||||
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
|
||||
&sid_everyone, nmode, S_IRWXO);
|
||||
&sid_everyone, other_mode, 0007, ACCESS_ALLOWED, !sticky_set);
|
||||
num_aces++;
|
||||
|
||||
set_size:
|
||||
pndacl->num_aces = cpu_to_le32(num_aces);
|
||||
pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
|
||||
|
||||
@@ -1000,7 +1094,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
|
||||
|
||||
/* Convert permission bits from mode to equivalent CIFS ACL */
|
||||
static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
|
||||
__u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid,
|
||||
__u32 secdesclen, __u64 *pnmode, kuid_t uid, kgid_t gid,
|
||||
bool mode_from_sid, bool id_from_sid, int *aclflag)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -1012,7 +1106,7 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
|
||||
struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */
|
||||
struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
|
||||
|
||||
if (nmode != NO_CHANGE_64) { /* chmod */
|
||||
if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
|
||||
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
||||
le32_to_cpu(pntsd->osidoffset));
|
||||
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
||||
@@ -1026,7 +1120,7 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
|
||||
ndacl_ptr->num_aces = 0;
|
||||
|
||||
rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr,
|
||||
nmode, mode_from_sid);
|
||||
pnmode, mode_from_sid);
|
||||
sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
|
||||
/* copy sec desc control portion & owner and group sids */
|
||||
copy_sec_desc(pntsd, pnntsd, sidsoffset);
|
||||
@@ -1282,7 +1376,7 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
|
||||
|
||||
/* Convert mode bits to an ACL so we can update the ACL on the server */
|
||||
int
|
||||
id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,
|
||||
id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
|
||||
kuid_t uid, kgid_t gid)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -1341,7 +1435,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,
|
||||
else
|
||||
id_from_sid = false;
|
||||
|
||||
rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
|
||||
rc = build_sec_desc(pntsd, pnntsd, secdesclen, pnmode, uid, gid,
|
||||
mode_from_sid, id_from_sid, &aclflag);
|
||||
|
||||
cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc);
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
#define WRITE_BIT 0x2
|
||||
#define EXEC_BIT 0x1
|
||||
|
||||
#define ACL_OWNER_MASK 0700
|
||||
#define ACL_GROUP_MASK 0770
|
||||
#define ACL_EVERYONE_MASK 0777
|
||||
|
||||
#define UBITSHIFT 6
|
||||
#define GBITSHIFT 3
|
||||
|
||||
|
||||
@@ -661,6 +661,11 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
|
||||
unsigned char *tiblob = NULL; /* target info blob */
|
||||
__le64 rsp_timestamp;
|
||||
|
||||
if (nls_cp == NULL) {
|
||||
cifs_dbg(VFS, "%s called with nls_cp==NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) {
|
||||
if (!ses->domainName) {
|
||||
if (ses->domainAuto) {
|
||||
|
||||
174
fs/cifs/cifsfs.c
174
fs/cifs/cifsfs.c
@@ -55,6 +55,10 @@
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
#include "dfs_cache.h"
|
||||
#endif
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
#include "netlink.h"
|
||||
#endif
|
||||
#include "fs_context.h"
|
||||
|
||||
/*
|
||||
* DOS dates from 1980/1/1 through 2107/12/31
|
||||
@@ -214,7 +218,7 @@ cifs_read_super(struct super_block *sb)
|
||||
if (rc)
|
||||
goto out_no_root;
|
||||
/* tune readahead according to rsize */
|
||||
sb->s_bdi->ra_pages = cifs_sb->rsize / PAGE_SIZE;
|
||||
sb->s_bdi->ra_pages = cifs_sb->ctx->rsize / PAGE_SIZE;
|
||||
|
||||
sb->s_blocksize = CIFS_MAX_MSGSIZE;
|
||||
sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */
|
||||
@@ -458,16 +462,23 @@ cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb)
|
||||
seq_puts(s, "loose");
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_show_nls(struct seq_file *s, struct nls_table *cur)
|
||||
/*
|
||||
* cifs_show_devname() is used so we show the mount device name with correct
|
||||
* format (e.g. forward slashes vs. back slashes) in /proc/mounts
|
||||
*/
|
||||
static int cifs_show_devname(struct seq_file *m, struct dentry *root)
|
||||
{
|
||||
struct nls_table *def;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
|
||||
char *devname = kstrdup(cifs_sb->ctx->UNC, GFP_KERNEL);
|
||||
|
||||
/* Display iocharset= option if it's not default charset */
|
||||
def = load_nls_default();
|
||||
if (def != cur)
|
||||
seq_printf(s, ",iocharset=%s", cur->charset);
|
||||
unload_nls(def);
|
||||
if (devname == NULL)
|
||||
seq_puts(m, "none");
|
||||
else {
|
||||
convert_delimiter(devname, '/');
|
||||
seq_puts(m, devname);
|
||||
kfree(devname);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -489,7 +500,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||
|
||||
if (tcon->no_lease)
|
||||
seq_puts(s, ",nolease");
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)
|
||||
if (cifs_sb->ctx->multiuser)
|
||||
seq_puts(s, ",multiuser");
|
||||
else if (tcon->ses->user_name)
|
||||
seq_show_option(s, "username", tcon->ses->user_name);
|
||||
@@ -514,14 +525,14 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||
}
|
||||
|
||||
seq_printf(s, ",uid=%u",
|
||||
from_kuid_munged(&init_user_ns, cifs_sb->mnt_uid));
|
||||
from_kuid_munged(&init_user_ns, cifs_sb->ctx->linux_uid));
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
|
||||
seq_puts(s, ",forceuid");
|
||||
else
|
||||
seq_puts(s, ",noforceuid");
|
||||
|
||||
seq_printf(s, ",gid=%u",
|
||||
from_kgid_munged(&init_user_ns, cifs_sb->mnt_gid));
|
||||
from_kgid_munged(&init_user_ns, cifs_sb->ctx->linux_gid));
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
|
||||
seq_puts(s, ",forcegid");
|
||||
else
|
||||
@@ -531,11 +542,10 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||
|
||||
if (!tcon->unix_ext)
|
||||
seq_printf(s, ",file_mode=0%ho,dir_mode=0%ho",
|
||||
cifs_sb->mnt_file_mode,
|
||||
cifs_sb->mnt_dir_mode);
|
||||
|
||||
cifs_show_nls(s, cifs_sb->local_nls);
|
||||
|
||||
cifs_sb->ctx->file_mode,
|
||||
cifs_sb->ctx->dir_mode);
|
||||
if (cifs_sb->ctx->iocharset)
|
||||
seq_printf(s, ",iocharset=%s", cifs_sb->ctx->iocharset);
|
||||
if (tcon->seal)
|
||||
seq_puts(s, ",seal");
|
||||
else if (tcon->ses->server->ignore_signature)
|
||||
@@ -605,15 +615,15 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID)
|
||||
seq_printf(s, ",backupuid=%u",
|
||||
from_kuid_munged(&init_user_ns,
|
||||
cifs_sb->mnt_backupuid));
|
||||
cifs_sb->ctx->backupuid));
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID)
|
||||
seq_printf(s, ",backupgid=%u",
|
||||
from_kgid_munged(&init_user_ns,
|
||||
cifs_sb->mnt_backupgid));
|
||||
cifs_sb->ctx->backupgid));
|
||||
|
||||
seq_printf(s, ",rsize=%u", cifs_sb->rsize);
|
||||
seq_printf(s, ",wsize=%u", cifs_sb->wsize);
|
||||
seq_printf(s, ",bsize=%u", cifs_sb->bsize);
|
||||
seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize);
|
||||
seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize);
|
||||
seq_printf(s, ",bsize=%u", cifs_sb->ctx->bsize);
|
||||
if (tcon->ses->server->min_offload)
|
||||
seq_printf(s, ",esize=%u", tcon->ses->server->min_offload);
|
||||
seq_printf(s, ",echo_interval=%lu",
|
||||
@@ -628,12 +638,17 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||
if (tcon->handle_timeout)
|
||||
seq_printf(s, ",handletimeout=%u", tcon->handle_timeout);
|
||||
/* convert actimeo and display it in seconds */
|
||||
seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ);
|
||||
seq_printf(s, ",actimeo=%lu", cifs_sb->ctx->actimeo / HZ);
|
||||
|
||||
if (tcon->ses->chan_max > 1)
|
||||
seq_printf(s, ",multichannel,max_channels=%zu",
|
||||
tcon->ses->chan_max);
|
||||
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
if (tcon->use_witness)
|
||||
seq_puts(s, ",witness");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -681,13 +696,6 @@ static int cifs_show_stats(struct seq_file *s, struct dentry *root)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int cifs_remount(struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
sync_filesystem(sb);
|
||||
*flags |= SB_NODIRATIME;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cifs_drop_inode(struct inode *inode)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
@@ -703,13 +711,14 @@ static const struct super_operations cifs_super_ops = {
|
||||
.free_inode = cifs_free_inode,
|
||||
.drop_inode = cifs_drop_inode,
|
||||
.evict_inode = cifs_evict_inode,
|
||||
/* .show_path = cifs_show_path, */ /* Would we ever need show path? */
|
||||
.show_devname = cifs_show_devname,
|
||||
/* .delete_inode = cifs_delete_inode, */ /* Do not need above
|
||||
function unless later we add lazy close of inodes or unless the
|
||||
kernel forgets to call us with the same number of releases (closes)
|
||||
as opens */
|
||||
.show_options = cifs_show_options,
|
||||
.umount_begin = cifs_umount_begin,
|
||||
.remount_fs = cifs_remount,
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
.show_stats = cifs_show_stats,
|
||||
#endif
|
||||
@@ -720,7 +729,7 @@ static const struct super_operations cifs_super_ops = {
|
||||
* Return dentry with refcount + 1 on success and NULL otherwise.
|
||||
*/
|
||||
static struct dentry *
|
||||
cifs_get_root(struct smb_vol *vol, struct super_block *sb)
|
||||
cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
@@ -731,7 +740,7 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
|
||||
return dget(sb->s_root);
|
||||
|
||||
full_path = cifs_build_path_to_root(vol, cifs_sb,
|
||||
full_path = cifs_build_path_to_root(ctx, cifs_sb,
|
||||
cifs_sb_master_tcon(cifs_sb), 0);
|
||||
if (full_path == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@@ -777,14 +786,13 @@ static int cifs_set_super(struct super_block *sb, void *data)
|
||||
return set_anon_super(sb, NULL);
|
||||
}
|
||||
|
||||
static struct dentry *
|
||||
struct dentry *
|
||||
cifs_smb3_do_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data, bool is_smb3)
|
||||
int flags, struct smb3_fs_context *old_ctx)
|
||||
{
|
||||
int rc;
|
||||
struct super_block *sb;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct smb_vol *volume_info;
|
||||
struct cifs_sb_info *cifs_sb = NULL;
|
||||
struct cifs_mnt_data mnt_data;
|
||||
struct dentry *root;
|
||||
|
||||
@@ -793,42 +801,49 @@ cifs_smb3_do_mount(struct file_system_type *fs_type,
|
||||
* If CIFS_DEBUG && cifs_FYI
|
||||
*/
|
||||
if (cifsFYI)
|
||||
cifs_dbg(FYI, "Devname: %s flags: %d\n", dev_name, flags);
|
||||
cifs_dbg(FYI, "Devname: %s flags: %d\n", old_ctx->UNC, flags);
|
||||
else
|
||||
cifs_info("Attempting to mount %s\n", dev_name);
|
||||
|
||||
volume_info = cifs_get_volume_info((char *)data, dev_name, is_smb3);
|
||||
if (IS_ERR(volume_info))
|
||||
return ERR_CAST(volume_info);
|
||||
cifs_info("Attempting to mount %s\n", old_ctx->UNC);
|
||||
|
||||
cifs_sb = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL);
|
||||
if (cifs_sb == NULL) {
|
||||
root = ERR_PTR(-ENOMEM);
|
||||
goto out_nls;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cifs_sb->mountdata = kstrndup(data, PAGE_SIZE, GFP_KERNEL);
|
||||
if (cifs_sb->mountdata == NULL) {
|
||||
cifs_sb->ctx = kzalloc(sizeof(struct smb3_fs_context), GFP_KERNEL);
|
||||
if (!cifs_sb->ctx) {
|
||||
root = ERR_PTR(-ENOMEM);
|
||||
goto out_free;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = cifs_setup_cifs_sb(volume_info, cifs_sb);
|
||||
rc = smb3_fs_context_dup(cifs_sb->ctx, old_ctx);
|
||||
if (rc) {
|
||||
root = ERR_PTR(rc);
|
||||
goto out_free;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = cifs_mount(cifs_sb, volume_info);
|
||||
rc = cifs_setup_volume_info(cifs_sb->ctx);
|
||||
if (rc) {
|
||||
root = ERR_PTR(rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = cifs_setup_cifs_sb(cifs_sb);
|
||||
if (rc) {
|
||||
root = ERR_PTR(rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = cifs_mount(cifs_sb, cifs_sb->ctx);
|
||||
if (rc) {
|
||||
if (!(flags & SB_SILENT))
|
||||
cifs_dbg(VFS, "cifs_mount failed w/return code = %d\n",
|
||||
rc);
|
||||
root = ERR_PTR(rc);
|
||||
goto out_free;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mnt_data.vol = volume_info;
|
||||
mnt_data.ctx = cifs_sb->ctx;
|
||||
mnt_data.cifs_sb = cifs_sb;
|
||||
mnt_data.flags = flags;
|
||||
|
||||
@@ -839,12 +854,14 @@ cifs_smb3_do_mount(struct file_system_type *fs_type,
|
||||
if (IS_ERR(sb)) {
|
||||
root = ERR_CAST(sb);
|
||||
cifs_umount(cifs_sb);
|
||||
cifs_sb = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sb->s_root) {
|
||||
cifs_dbg(FYI, "Use existing superblock\n");
|
||||
cifs_umount(cifs_sb);
|
||||
cifs_sb = NULL;
|
||||
} else {
|
||||
rc = cifs_read_super(sb);
|
||||
if (rc) {
|
||||
@@ -855,41 +872,24 @@ cifs_smb3_do_mount(struct file_system_type *fs_type,
|
||||
sb->s_flags |= SB_ACTIVE;
|
||||
}
|
||||
|
||||
root = cifs_get_root(volume_info, sb);
|
||||
root = cifs_get_root(cifs_sb ? cifs_sb->ctx : old_ctx, sb);
|
||||
if (IS_ERR(root))
|
||||
goto out_super;
|
||||
|
||||
cifs_dbg(FYI, "dentry root is: %p\n", root);
|
||||
goto out;
|
||||
return root;
|
||||
|
||||
out_super:
|
||||
deactivate_locked_super(sb);
|
||||
out:
|
||||
cifs_cleanup_volume_info(volume_info);
|
||||
if (cifs_sb) {
|
||||
kfree(cifs_sb->prepath);
|
||||
smb3_cleanup_fs_context(cifs_sb->ctx);
|
||||
kfree(cifs_sb);
|
||||
}
|
||||
return root;
|
||||
|
||||
out_free:
|
||||
kfree(cifs_sb->prepath);
|
||||
kfree(cifs_sb->mountdata);
|
||||
kfree(cifs_sb);
|
||||
out_nls:
|
||||
unload_nls(volume_info->local_nls);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static struct dentry *
|
||||
smb3_do_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
{
|
||||
return cifs_smb3_do_mount(fs_type, flags, dev_name, data, true);
|
||||
}
|
||||
|
||||
static struct dentry *
|
||||
cifs_do_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
{
|
||||
return cifs_smb3_do_mount(fs_type, flags, dev_name, data, false);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||
@@ -1026,7 +1026,8 @@ cifs_setlease(struct file *file, long arg, struct file_lock **lease, void **priv
|
||||
struct file_system_type cifs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cifs",
|
||||
.mount = cifs_do_mount,
|
||||
.init_fs_context = smb3_init_fs_context,
|
||||
.parameters = smb3_fs_parameters,
|
||||
.kill_sb = cifs_kill_sb,
|
||||
.fs_flags = FS_RENAME_DOES_D_MOVE,
|
||||
};
|
||||
@@ -1035,7 +1036,8 @@ MODULE_ALIAS_FS("cifs");
|
||||
static struct file_system_type smb3_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "smb3",
|
||||
.mount = smb3_do_mount,
|
||||
.init_fs_context = smb3_init_fs_context,
|
||||
.parameters = smb3_fs_parameters,
|
||||
.kill_sb = cifs_kill_sb,
|
||||
.fs_flags = FS_RENAME_DOES_D_MOVE,
|
||||
};
|
||||
@@ -1617,10 +1619,15 @@ init_cifs(void)
|
||||
if (rc)
|
||||
goto out_destroy_dfs_cache;
|
||||
#endif /* CONFIG_CIFS_UPCALL */
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
rc = cifs_genl_init();
|
||||
if (rc)
|
||||
goto out_register_key_type;
|
||||
#endif /* CONFIG_CIFS_SWN_UPCALL */
|
||||
|
||||
rc = init_cifs_idmap();
|
||||
if (rc)
|
||||
goto out_register_key_type;
|
||||
goto out_cifs_swn_init;
|
||||
|
||||
rc = register_filesystem(&cifs_fs_type);
|
||||
if (rc)
|
||||
@@ -1636,7 +1643,11 @@ init_cifs(void)
|
||||
|
||||
out_init_cifs_idmap:
|
||||
exit_cifs_idmap();
|
||||
out_cifs_swn_init:
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
cifs_genl_exit();
|
||||
out_register_key_type:
|
||||
#endif
|
||||
#ifdef CONFIG_CIFS_UPCALL
|
||||
exit_cifs_spnego();
|
||||
out_destroy_dfs_cache:
|
||||
@@ -1673,6 +1684,9 @@ exit_cifs(void)
|
||||
unregister_filesystem(&smb3_fs_type);
|
||||
cifs_dfs_release_automount_timer();
|
||||
exit_cifs_idmap();
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
cifs_genl_exit();
|
||||
#endif
|
||||
#ifdef CONFIG_CIFS_UPCALL
|
||||
exit_cifs_spnego();
|
||||
#endif
|
||||
|
||||
@@ -152,9 +152,13 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
|
||||
extern void cifs_setsize(struct inode *inode, loff_t offset);
|
||||
extern int cifs_truncate_page(struct address_space *mapping, loff_t from);
|
||||
|
||||
struct smb3_fs_context;
|
||||
extern struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type,
|
||||
int flags, struct smb3_fs_context *ctx);
|
||||
|
||||
#ifdef CONFIG_CIFS_NFSD_EXPORT
|
||||
extern const struct export_operations cifs_export_ops;
|
||||
#endif /* CONFIG_CIFS_NFSD_EXPORT */
|
||||
|
||||
#define CIFS_VERSION "2.29"
|
||||
#define CIFS_VERSION "2.30"
|
||||
#endif /* _CIFSFS_H */
|
||||
|
||||
@@ -202,7 +202,7 @@ struct cifs_ses;
|
||||
struct cifs_tcon;
|
||||
struct dfs_info3_param;
|
||||
struct cifs_fattr;
|
||||
struct smb_vol;
|
||||
struct smb3_fs_context;
|
||||
struct cifs_fid;
|
||||
struct cifs_readdata;
|
||||
struct cifs_writedata;
|
||||
@@ -268,9 +268,9 @@ struct smb_version_operations {
|
||||
/* negotiate to the server */
|
||||
int (*negotiate)(const unsigned int, struct cifs_ses *);
|
||||
/* set negotiated write size */
|
||||
unsigned int (*negotiate_wsize)(struct cifs_tcon *, struct smb_vol *);
|
||||
unsigned int (*negotiate_wsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx);
|
||||
/* set negotiated read size */
|
||||
unsigned int (*negotiate_rsize)(struct cifs_tcon *, struct smb_vol *);
|
||||
unsigned int (*negotiate_rsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx);
|
||||
/* setup smb sessionn */
|
||||
int (*sess_setup)(const unsigned int, struct cifs_ses *,
|
||||
const struct nls_table *);
|
||||
@@ -530,97 +530,6 @@ struct smb_version_values {
|
||||
#define HEADER_SIZE(server) (server->vals->header_size)
|
||||
#define MAX_HEADER_SIZE(server) (server->vals->max_header_size)
|
||||
|
||||
struct smb_vol {
|
||||
char *username;
|
||||
char *password;
|
||||
char *domainname;
|
||||
char *UNC;
|
||||
char *iocharset; /* local code page for mapping to and from Unicode */
|
||||
char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */
|
||||
char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */
|
||||
kuid_t cred_uid;
|
||||
kuid_t linux_uid;
|
||||
kgid_t linux_gid;
|
||||
kuid_t backupuid;
|
||||
kgid_t backupgid;
|
||||
umode_t file_mode;
|
||||
umode_t dir_mode;
|
||||
enum securityEnum sectype; /* sectype requested via mnt opts */
|
||||
bool sign; /* was signing requested via mnt opts? */
|
||||
bool ignore_signature:1;
|
||||
bool retry:1;
|
||||
bool intr:1;
|
||||
bool setuids:1;
|
||||
bool setuidfromacl:1;
|
||||
bool override_uid:1;
|
||||
bool override_gid:1;
|
||||
bool dynperm:1;
|
||||
bool noperm:1;
|
||||
bool nodelete:1;
|
||||
bool mode_ace:1;
|
||||
bool no_psx_acl:1; /* set if posix acl support should be disabled */
|
||||
bool cifs_acl:1;
|
||||
bool backupuid_specified; /* mount option backupuid is specified */
|
||||
bool backupgid_specified; /* mount option backupgid is specified */
|
||||
bool no_xattr:1; /* set if xattr (EA) support should be disabled*/
|
||||
bool server_ino:1; /* use inode numbers from server ie UniqueId */
|
||||
bool direct_io:1;
|
||||
bool strict_io:1; /* strict cache behavior */
|
||||
bool cache_ro:1;
|
||||
bool cache_rw:1;
|
||||
bool remap:1; /* set to remap seven reserved chars in filenames */
|
||||
bool sfu_remap:1; /* remap seven reserved chars ala SFU */
|
||||
bool posix_paths:1; /* unset to not ask for posix pathnames. */
|
||||
bool no_linux_ext:1;
|
||||
bool linux_ext:1;
|
||||
bool sfu_emul:1;
|
||||
bool nullauth:1; /* attempt to authenticate with null user */
|
||||
bool nocase:1; /* request case insensitive filenames */
|
||||
bool nobrl:1; /* disable sending byte range locks to srv */
|
||||
bool nohandlecache:1; /* disable caching dir handles if srvr probs */
|
||||
bool mand_lock:1; /* send mandatory not posix byte range lock reqs */
|
||||
bool seal:1; /* request transport encryption on share */
|
||||
bool nodfs:1; /* Do not request DFS, even if available */
|
||||
bool local_lease:1; /* check leases only on local system, not remote */
|
||||
bool noblocksnd:1;
|
||||
bool noautotune:1;
|
||||
bool nostrictsync:1; /* do not force expensive SMBflush on every sync */
|
||||
bool no_lease:1; /* disable requesting leases */
|
||||
bool fsc:1; /* enable fscache */
|
||||
bool mfsymlinks:1; /* use Minshall+French Symlinks */
|
||||
bool multiuser:1;
|
||||
bool rwpidforward:1; /* pid forward for read/write operations */
|
||||
bool nosharesock:1;
|
||||
bool persistent:1;
|
||||
bool nopersistent:1;
|
||||
bool resilient:1; /* noresilient not required since not fored for CA */
|
||||
bool domainauto:1;
|
||||
bool rdma:1;
|
||||
bool multichannel:1;
|
||||
bool use_client_guid:1;
|
||||
/* reuse existing guid for multichannel */
|
||||
u8 client_guid[SMB2_CLIENT_GUID_SIZE];
|
||||
unsigned int bsize;
|
||||
unsigned int rsize;
|
||||
unsigned int wsize;
|
||||
unsigned int min_offload;
|
||||
bool sockopt_tcp_nodelay:1;
|
||||
unsigned long actimeo; /* attribute cache timeout (jiffies) */
|
||||
struct smb_version_operations *ops;
|
||||
struct smb_version_values *vals;
|
||||
char *prepath;
|
||||
struct sockaddr_storage dstaddr; /* destination address */
|
||||
struct sockaddr_storage srcaddr; /* allow binding to a local IP */
|
||||
struct nls_table *local_nls;
|
||||
unsigned int echo_interval; /* echo interval in secs */
|
||||
__u64 snapshot_time; /* needed for timewarp tokens */
|
||||
__u32 handle_timeout; /* persistent and durable handle timeout in ms */
|
||||
unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
|
||||
unsigned int max_channels;
|
||||
__u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
|
||||
bool rootfs:1; /* if it's a SMB root file system */
|
||||
};
|
||||
|
||||
/**
|
||||
* CIFS superblock mount flags (mnt_cifs_flags) to consider when
|
||||
* trying to reuse existing superblock for a new mount
|
||||
@@ -649,7 +558,7 @@ struct smb_vol {
|
||||
|
||||
struct cifs_mnt_data {
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct smb_vol *vol;
|
||||
struct smb3_fs_context *ctx;
|
||||
int flags;
|
||||
};
|
||||
|
||||
@@ -778,6 +687,10 @@ struct TCP_Server_Info {
|
||||
int nr_targets;
|
||||
bool noblockcnt; /* use non-blocking connect() */
|
||||
bool is_channel; /* if a session channel */
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
bool use_swn_dstaddr;
|
||||
struct sockaddr_storage swn_dstaddr;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct cifs_credits {
|
||||
@@ -1177,6 +1090,9 @@ struct cifs_tcon {
|
||||
int remap:2;
|
||||
struct list_head ulist; /* cache update list */
|
||||
#endif
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
bool use_witness:1; /* use witness protocol */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -262,7 +262,7 @@
|
||||
| WRITE_OWNER | SYNCHRONIZE)
|
||||
#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
|
||||
| FILE_READ_EA | FILE_WRITE_EA \
|
||||
| FILE_DELETE_CHILD | FILE_READ_ATTRIBUTES \
|
||||
| FILE_READ_ATTRIBUTES \
|
||||
| FILE_WRITE_ATTRIBUTES \
|
||||
| DELETE | READ_CONTROL | WRITE_DAC \
|
||||
| WRITE_OWNER | SYNCHRONIZE)
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
#endif
|
||||
|
||||
struct statfs;
|
||||
struct smb_vol;
|
||||
struct smb_rqst;
|
||||
struct smb3_fs_context;
|
||||
|
||||
/*
|
||||
*****************************************************************
|
||||
@@ -72,14 +72,13 @@ extern void exit_cifs_spnego(void);
|
||||
extern char *build_path_from_dentry(struct dentry *);
|
||||
extern char *build_path_from_dentry_optional_prefix(struct dentry *direntry,
|
||||
bool prefix);
|
||||
extern char *cifs_build_path_to_root(struct smb_vol *vol,
|
||||
extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx,
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
struct cifs_tcon *tcon,
|
||||
int add_treename);
|
||||
extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
|
||||
extern char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||
const char *fullpath, const struct dfs_info3_param *ref,
|
||||
char **devname);
|
||||
const char *fullpath, const struct dfs_info3_param *ref);
|
||||
/* extern void renew_parental_timestamps(struct dentry *direntry);*/
|
||||
extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer,
|
||||
struct TCP_Server_Info *server);
|
||||
@@ -89,6 +88,7 @@ extern void cifs_mid_q_entry_release(struct mid_q_entry *midEntry);
|
||||
extern void cifs_wake_up_task(struct mid_q_entry *mid);
|
||||
extern int cifs_handle_standard(struct TCP_Server_Info *server,
|
||||
struct mid_q_entry *mid);
|
||||
extern int smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx);
|
||||
extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs);
|
||||
extern int cifs_discard_remaining_data(struct TCP_Server_Info *server);
|
||||
extern int cifs_call_async(struct TCP_Server_Info *server,
|
||||
@@ -215,8 +215,8 @@ extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
|
||||
struct cifs_fattr *fattr, struct inode *inode,
|
||||
bool get_mode_from_special_sid,
|
||||
const char *path, const struct cifs_fid *pfid);
|
||||
extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64,
|
||||
kuid_t, kgid_t);
|
||||
extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
|
||||
kuid_t uid, kgid_t gid);
|
||||
extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *,
|
||||
const char *, u32 *);
|
||||
extern struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *,
|
||||
@@ -234,13 +234,9 @@ extern int cifs_read_page_from_socket(struct TCP_Server_Info *server,
|
||||
struct page *page,
|
||||
unsigned int page_offset,
|
||||
unsigned int to_read);
|
||||
extern int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
extern int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb);
|
||||
extern int cifs_match_super(struct super_block *, void *);
|
||||
extern void cifs_cleanup_volume_info(struct smb_vol *pvolume_info);
|
||||
extern struct smb_vol *cifs_get_volume_info(char *mount_data,
|
||||
const char *devname, bool is_smb3);
|
||||
extern int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol);
|
||||
extern int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx);
|
||||
extern void cifs_umount(struct cifs_sb_info *);
|
||||
extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
|
||||
extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon);
|
||||
@@ -256,7 +252,7 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
|
||||
struct tcon_link *tlink,
|
||||
struct cifs_pending_open *open);
|
||||
extern void cifs_del_pending_open(struct cifs_pending_open *open);
|
||||
extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb_vol *vol);
|
||||
extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx);
|
||||
extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
|
||||
int from_reconnect);
|
||||
extern void cifs_put_tcon(struct cifs_tcon *tcon);
|
||||
@@ -332,7 +328,7 @@ extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
|
||||
const char *searchName, bool is_unicode);
|
||||
extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
struct smb_vol *vol);
|
||||
struct smb3_fs_context *ctx);
|
||||
extern int CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct kstatfs *FSData);
|
||||
extern int SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
@@ -553,18 +549,15 @@ extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
|
||||
unsigned char *p24);
|
||||
|
||||
extern int
|
||||
cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
|
||||
const char *devname, bool is_smb3);
|
||||
extern void
|
||||
cifs_cleanup_volume_info_contents(struct smb_vol *volume_info);
|
||||
cifs_setup_volume_info(struct smb3_fs_context *ctx);
|
||||
|
||||
extern struct TCP_Server_Info *
|
||||
cifs_find_tcp_session(struct smb_vol *vol);
|
||||
cifs_find_tcp_session(struct smb3_fs_context *ctx);
|
||||
|
||||
extern void cifs_put_smb_ses(struct cifs_ses *ses);
|
||||
|
||||
extern struct cifs_ses *
|
||||
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info);
|
||||
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx);
|
||||
|
||||
void cifs_readdata_release(struct kref *refcount);
|
||||
int cifs_async_readv(struct cifs_readdata *rdata);
|
||||
@@ -604,9 +597,7 @@ extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
|
||||
unsigned int *len, unsigned int *offset);
|
||||
struct cifs_chan *
|
||||
cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server);
|
||||
int cifs_try_adding_channels(struct cifs_ses *ses);
|
||||
int cifs_ses_add_channel(struct cifs_ses *ses,
|
||||
struct cifs_server_iface *iface);
|
||||
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses);
|
||||
bool is_server_using_iface(struct TCP_Server_Info *server,
|
||||
struct cifs_server_iface *iface);
|
||||
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
|
||||
@@ -620,6 +611,8 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
|
||||
struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
|
||||
void cifs_put_tcp_super(struct super_block *sb);
|
||||
int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
|
||||
char *extract_hostname(const char *unc);
|
||||
char *extract_sharename(const char *unc);
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
|
||||
|
||||
2121
fs/cifs/connect.c
2121
fs/cifs/connect.c
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@
|
||||
#include "cifs_debug.h"
|
||||
#include "cifs_unicode.h"
|
||||
#include "smb2glob.h"
|
||||
#include "fs_context.h"
|
||||
|
||||
#include "dfs_cache.h"
|
||||
|
||||
@@ -48,8 +49,8 @@ struct cache_entry {
|
||||
|
||||
struct vol_info {
|
||||
char *fullpath;
|
||||
spinlock_t smb_vol_lock;
|
||||
struct smb_vol smb_vol;
|
||||
spinlock_t ctx_lock;
|
||||
struct smb3_fs_context ctx;
|
||||
char *mntdata;
|
||||
struct list_head list;
|
||||
struct list_head rlist;
|
||||
@@ -586,7 +587,7 @@ static void __vol_release(struct vol_info *vi)
|
||||
{
|
||||
kfree(vi->fullpath);
|
||||
kfree(vi->mntdata);
|
||||
cifs_cleanup_volume_info_contents(&vi->smb_vol);
|
||||
smb3_cleanup_fs_context_contents(&vi->ctx);
|
||||
kfree(vi);
|
||||
}
|
||||
|
||||
@@ -1140,80 +1141,22 @@ out_unlock:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dup_vol(struct smb_vol *vol, struct smb_vol *new)
|
||||
{
|
||||
memcpy(new, vol, sizeof(*new));
|
||||
|
||||
if (vol->username) {
|
||||
new->username = kstrndup(vol->username, strlen(vol->username),
|
||||
GFP_KERNEL);
|
||||
if (!new->username)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (vol->password) {
|
||||
new->password = kstrndup(vol->password, strlen(vol->password),
|
||||
GFP_KERNEL);
|
||||
if (!new->password)
|
||||
goto err_free_username;
|
||||
}
|
||||
if (vol->UNC) {
|
||||
cifs_dbg(FYI, "%s: vol->UNC: %s\n", __func__, vol->UNC);
|
||||
new->UNC = kstrndup(vol->UNC, strlen(vol->UNC), GFP_KERNEL);
|
||||
if (!new->UNC)
|
||||
goto err_free_password;
|
||||
}
|
||||
if (vol->domainname) {
|
||||
new->domainname = kstrndup(vol->domainname,
|
||||
strlen(vol->domainname), GFP_KERNEL);
|
||||
if (!new->domainname)
|
||||
goto err_free_unc;
|
||||
}
|
||||
if (vol->iocharset) {
|
||||
new->iocharset = kstrndup(vol->iocharset,
|
||||
strlen(vol->iocharset), GFP_KERNEL);
|
||||
if (!new->iocharset)
|
||||
goto err_free_domainname;
|
||||
}
|
||||
if (vol->prepath) {
|
||||
cifs_dbg(FYI, "%s: vol->prepath: %s\n", __func__, vol->prepath);
|
||||
new->prepath = kstrndup(vol->prepath, strlen(vol->prepath),
|
||||
GFP_KERNEL);
|
||||
if (!new->prepath)
|
||||
goto err_free_iocharset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_iocharset:
|
||||
kfree(new->iocharset);
|
||||
err_free_domainname:
|
||||
kfree(new->domainname);
|
||||
err_free_unc:
|
||||
kfree(new->UNC);
|
||||
err_free_password:
|
||||
kfree_sensitive(new->password);
|
||||
err_free_username:
|
||||
kfree(new->username);
|
||||
kfree(new);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* dfs_cache_add_vol - add a cifs volume during mount() that will be handled by
|
||||
* dfs_cache_add_vol - add a cifs context during mount() that will be handled by
|
||||
* DFS cache refresh worker.
|
||||
*
|
||||
* @mntdata: mount data.
|
||||
* @vol: cifs volume.
|
||||
* @ctx: cifs context.
|
||||
* @fullpath: origin full path.
|
||||
*
|
||||
* Return zero if volume was set up correctly, otherwise non-zero.
|
||||
* Return zero if context was set up correctly, otherwise non-zero.
|
||||
*/
|
||||
int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, const char *fullpath)
|
||||
int dfs_cache_add_vol(char *mntdata, struct smb3_fs_context *ctx, const char *fullpath)
|
||||
{
|
||||
int rc;
|
||||
struct vol_info *vi;
|
||||
|
||||
if (!vol || !fullpath || !mntdata)
|
||||
if (!ctx || !fullpath || !mntdata)
|
||||
return -EINVAL;
|
||||
|
||||
cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
|
||||
@@ -1228,12 +1171,12 @@ int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, const char *fullpath)
|
||||
goto err_free_vi;
|
||||
}
|
||||
|
||||
rc = dup_vol(vol, &vi->smb_vol);
|
||||
rc = smb3_fs_context_dup(&vi->ctx, ctx);
|
||||
if (rc)
|
||||
goto err_free_fullpath;
|
||||
|
||||
vi->mntdata = mntdata;
|
||||
spin_lock_init(&vi->smb_vol_lock);
|
||||
spin_lock_init(&vi->ctx_lock);
|
||||
kref_init(&vi->refcnt);
|
||||
|
||||
spin_lock(&vol_list_lock);
|
||||
@@ -1289,10 +1232,10 @@ int dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server)
|
||||
spin_unlock(&vol_list_lock);
|
||||
|
||||
cifs_dbg(FYI, "%s: updating volume info\n", __func__);
|
||||
spin_lock(&vi->smb_vol_lock);
|
||||
memcpy(&vi->smb_vol.dstaddr, &server->dstaddr,
|
||||
sizeof(vi->smb_vol.dstaddr));
|
||||
spin_unlock(&vi->smb_vol_lock);
|
||||
spin_lock(&vi->ctx_lock);
|
||||
memcpy(&vi->ctx.dstaddr, &server->dstaddr,
|
||||
sizeof(vi->ctx.dstaddr));
|
||||
spin_unlock(&vi->ctx_lock);
|
||||
|
||||
kref_put(&vi->refcnt, vol_release);
|
||||
|
||||
@@ -1445,11 +1388,11 @@ static inline void put_tcp_server(struct TCP_Server_Info *server)
|
||||
cifs_put_tcp_session(server, 0);
|
||||
}
|
||||
|
||||
static struct TCP_Server_Info *get_tcp_server(struct smb_vol *vol)
|
||||
static struct TCP_Server_Info *get_tcp_server(struct smb3_fs_context *ctx)
|
||||
{
|
||||
struct TCP_Server_Info *server;
|
||||
|
||||
server = cifs_find_tcp_session(vol);
|
||||
server = cifs_find_tcp_session(ctx);
|
||||
if (IS_ERR_OR_NULL(server))
|
||||
return NULL;
|
||||
|
||||
@@ -1473,10 +1416,10 @@ static struct cifs_ses *find_root_ses(struct vol_info *vi,
|
||||
int rc;
|
||||
struct cache_entry *ce;
|
||||
struct dfs_info3_param ref = {0};
|
||||
char *mdata = NULL, *devname = NULL;
|
||||
char *mdata = NULL;
|
||||
struct TCP_Server_Info *server;
|
||||
struct cifs_ses *ses;
|
||||
struct smb_vol vol = {NULL};
|
||||
struct smb3_fs_context ctx = {NULL};
|
||||
|
||||
rpath = get_dfs_root(path);
|
||||
if (IS_ERR(rpath))
|
||||
@@ -1500,8 +1443,7 @@ static struct cifs_ses *find_root_ses(struct vol_info *vi,
|
||||
|
||||
up_read(&htable_rw_lock);
|
||||
|
||||
mdata = cifs_compose_mount_options(vi->mntdata, rpath, &ref,
|
||||
&devname);
|
||||
mdata = cifs_compose_mount_options(vi->mntdata, rpath, &ref);
|
||||
free_dfs_info_param(&ref);
|
||||
|
||||
if (IS_ERR(mdata)) {
|
||||
@@ -1510,24 +1452,23 @@ static struct cifs_ses *find_root_ses(struct vol_info *vi,
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = cifs_setup_volume_info(&vol, mdata, devname, false);
|
||||
kfree(devname);
|
||||
rc = cifs_setup_volume_info(&ctx);
|
||||
|
||||
if (rc) {
|
||||
ses = ERR_PTR(rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
server = get_tcp_server(&vol);
|
||||
server = get_tcp_server(&ctx);
|
||||
if (!server) {
|
||||
ses = ERR_PTR(-EHOSTDOWN);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ses = cifs_get_smb_ses(server, &vol);
|
||||
ses = cifs_get_smb_ses(server, &ctx);
|
||||
|
||||
out:
|
||||
cifs_cleanup_volume_info_contents(&vol);
|
||||
smb3_cleanup_fs_context_contents(&ctx);
|
||||
kfree(mdata);
|
||||
kfree(rpath);
|
||||
|
||||
@@ -1619,7 +1560,7 @@ static void refresh_cache_worker(struct work_struct *work)
|
||||
*/
|
||||
spin_lock(&vol_list_lock);
|
||||
list_for_each_entry(vi, &vol_list, list) {
|
||||
server = get_tcp_server(&vi->smb_vol);
|
||||
server = get_tcp_server(&vi->ctx);
|
||||
if (!server)
|
||||
continue;
|
||||
|
||||
@@ -1631,9 +1572,9 @@ static void refresh_cache_worker(struct work_struct *work)
|
||||
|
||||
/* Walk through all TCONs and refresh any expired cache entry */
|
||||
list_for_each_entry_safe(vi, nvi, &vols, rlist) {
|
||||
spin_lock(&vi->smb_vol_lock);
|
||||
server = get_tcp_server(&vi->smb_vol);
|
||||
spin_unlock(&vi->smb_vol_lock);
|
||||
spin_lock(&vi->ctx_lock);
|
||||
server = get_tcp_server(&vi->ctx);
|
||||
spin_unlock(&vi->ctx_lock);
|
||||
|
||||
if (!server)
|
||||
goto next_vol;
|
||||
|
||||
@@ -44,7 +44,7 @@ dfs_cache_noreq_update_tgthint(const char *path,
|
||||
extern int dfs_cache_get_tgt_referral(const char *path,
|
||||
const struct dfs_cache_tgt_iterator *it,
|
||||
struct dfs_info3_param *ref);
|
||||
extern int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol,
|
||||
extern int dfs_cache_add_vol(char *mntdata, struct smb3_fs_context *ctx,
|
||||
const char *fullpath);
|
||||
extern int dfs_cache_update_vol(const char *fullpath,
|
||||
struct TCP_Server_Info *server);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "cifs_debug.h"
|
||||
#include "cifs_fs_sb.h"
|
||||
#include "cifs_unicode.h"
|
||||
#include "fs_context.h"
|
||||
|
||||
static void
|
||||
renew_parental_timestamps(struct dentry *direntry)
|
||||
@@ -46,10 +47,10 @@ renew_parental_timestamps(struct dentry *direntry)
|
||||
}
|
||||
|
||||
char *
|
||||
cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
|
||||
cifs_build_path_to_root(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb,
|
||||
struct cifs_tcon *tcon, int add_treename)
|
||||
{
|
||||
int pplen = vol->prepath ? strlen(vol->prepath) + 1 : 0;
|
||||
int pplen = ctx->prepath ? strlen(ctx->prepath) + 1 : 0;
|
||||
int dfsplen;
|
||||
char *full_path = NULL;
|
||||
|
||||
@@ -71,7 +72,7 @@ cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
|
||||
if (dfsplen)
|
||||
memcpy(full_path, tcon->treeName, dfsplen);
|
||||
full_path[dfsplen] = CIFS_DIR_SEP(cifs_sb);
|
||||
memcpy(full_path + dfsplen + 1, vol->prepath, pplen);
|
||||
memcpy(full_path + dfsplen + 1, ctx->prepath, pplen);
|
||||
convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb));
|
||||
return full_path;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "cifs_fs_sb.h"
|
||||
#include "fscache.h"
|
||||
#include "smbdirect.h"
|
||||
#include "fs_context.h"
|
||||
|
||||
static inline int cifs_convert_flags(unsigned int flags)
|
||||
{
|
||||
@@ -416,6 +417,8 @@ static void cifsFileInfo_put_work(struct work_struct *work)
|
||||
* cifsFileInfo_put - release a reference of file priv data
|
||||
*
|
||||
* Always potentially wait for oplock handler. See _cifsFileInfo_put().
|
||||
*
|
||||
* @cifs_file: cifs/smb3 specific info (eg refcounts) for an open file
|
||||
*/
|
||||
void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
||||
{
|
||||
@@ -431,8 +434,11 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
||||
*
|
||||
* If @wait_for_oplock_handler is true and we are releasing the last
|
||||
* reference, wait for any running oplock break handler of the file
|
||||
* and cancel any pending one. If calling this function from the
|
||||
* oplock break handler, you need to pass false.
|
||||
* and cancel any pending one.
|
||||
*
|
||||
* @cifs_file: cifs/smb3 specific info (eg refcounts) for an open file
|
||||
* @wait_oplock_handler: must be false if called from oplock_break_handler
|
||||
* @offload: not offloaded on close and oplock breaks
|
||||
*
|
||||
*/
|
||||
void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
|
||||
@@ -566,7 +572,7 @@ int cifs_open(struct inode *inode, struct file *file)
|
||||
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
||||
/* can not refresh inode info since size could be stale */
|
||||
rc = cifs_posix_open(full_path, &inode, inode->i_sb,
|
||||
cifs_sb->mnt_file_mode /* ignored */,
|
||||
cifs_sb->ctx->file_mode /* ignored */,
|
||||
file->f_flags, &oplock, &fid.netfid, xid);
|
||||
if (rc == 0) {
|
||||
cifs_dbg(FYI, "posix open succeeded\n");
|
||||
@@ -735,7 +741,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
|
||||
~(O_CREAT | O_EXCL | O_TRUNC);
|
||||
|
||||
rc = cifs_posix_open(full_path, NULL, inode->i_sb,
|
||||
cifs_sb->mnt_file_mode /* ignored */,
|
||||
cifs_sb->ctx->file_mode /* ignored */,
|
||||
oflags, &oplock, &cfile->fid.netfid, xid);
|
||||
if (rc == 0) {
|
||||
cifs_dbg(FYI, "posix reopen succeeded\n");
|
||||
@@ -2330,7 +2336,7 @@ static int cifs_writepages(struct address_space *mapping,
|
||||
* If wsize is smaller than the page cache size, default to writing
|
||||
* one page at a time via cifs_writepage
|
||||
*/
|
||||
if (cifs_sb->wsize < PAGE_SIZE)
|
||||
if (cifs_sb->ctx->wsize < PAGE_SIZE)
|
||||
return generic_writepages(mapping, wbc);
|
||||
|
||||
xid = get_xid();
|
||||
@@ -2363,7 +2369,7 @@ retry:
|
||||
if (rc)
|
||||
get_file_rc = rc;
|
||||
|
||||
rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
|
||||
rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize,
|
||||
&wsize, credits);
|
||||
if (rc != 0) {
|
||||
done = true;
|
||||
@@ -2905,7 +2911,7 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
|
||||
break;
|
||||
}
|
||||
|
||||
rc = server->ops->wait_mtu_credits(server, cifs_sb->wsize,
|
||||
rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->wsize,
|
||||
&wsize, credits);
|
||||
if (rc)
|
||||
break;
|
||||
@@ -3636,7 +3642,7 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
|
||||
break;
|
||||
}
|
||||
|
||||
rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
|
||||
rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize,
|
||||
&rsize, credits);
|
||||
if (rc)
|
||||
break;
|
||||
@@ -4022,7 +4028,7 @@ cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
|
||||
cifs_sb = CIFS_FILE_SB(file);
|
||||
|
||||
/* FIXME: set up handlers for larger reads and/or convert to async */
|
||||
rsize = min_t(unsigned int, cifs_sb->rsize, CIFSMaxBufSize);
|
||||
rsize = min_t(unsigned int, cifs_sb->ctx->rsize, CIFSMaxBufSize);
|
||||
|
||||
if (file->private_data == NULL) {
|
||||
rc = -EBADF;
|
||||
@@ -4407,7 +4413,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
|
||||
break;
|
||||
}
|
||||
|
||||
rc = server->ops->wait_mtu_credits(server, cifs_sb->rsize,
|
||||
rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize,
|
||||
&rsize, credits);
|
||||
if (rc)
|
||||
break;
|
||||
|
||||
1529
fs/cifs/fs_context.c
1529
fs/cifs/fs_context.c
File diff suppressed because it is too large
Load Diff
@@ -9,8 +9,11 @@
|
||||
#ifndef _FS_CONTEXT_H
|
||||
#define _FS_CONTEXT_H
|
||||
|
||||
#include <linux/parser.h>
|
||||
#include "cifsglob.h"
|
||||
#include <linux/parser.h>
|
||||
#include <linux/fs_parser.h>
|
||||
|
||||
#define cifs_invalf(fc, fmt, ...) invalf(fc, fmt, ## __VA_ARGS__)
|
||||
|
||||
enum smb_version {
|
||||
Smb_1 = 1,
|
||||
@@ -24,8 +27,6 @@ enum smb_version {
|
||||
Smb_version_err
|
||||
};
|
||||
|
||||
int cifs_parse_smb_version(char *value, struct smb_vol *vol, bool is_smb3);
|
||||
|
||||
enum {
|
||||
Opt_cache_loose,
|
||||
Opt_cache_strict,
|
||||
@@ -35,8 +36,6 @@ enum {
|
||||
Opt_cache_err
|
||||
};
|
||||
|
||||
int cifs_parse_cache_flavor(char *value, struct smb_vol *vol);
|
||||
|
||||
enum cifs_sec_param {
|
||||
Opt_sec_krb5,
|
||||
Opt_sec_krb5i,
|
||||
@@ -53,6 +52,220 @@ enum cifs_sec_param {
|
||||
Opt_sec_err
|
||||
};
|
||||
|
||||
int cifs_parse_security_flavors(char *value, struct smb_vol *vol);
|
||||
enum cifs_param {
|
||||
/* Mount options that take no arguments */
|
||||
Opt_user_xattr,
|
||||
Opt_forceuid,
|
||||
Opt_forcegid,
|
||||
Opt_noblocksend,
|
||||
Opt_noautotune,
|
||||
Opt_nolease,
|
||||
Opt_hard,
|
||||
Opt_soft,
|
||||
Opt_perm,
|
||||
Opt_nodelete,
|
||||
Opt_mapposix,
|
||||
Opt_mapchars,
|
||||
Opt_nomapchars,
|
||||
Opt_sfu,
|
||||
Opt_nodfs,
|
||||
Opt_posixpaths,
|
||||
Opt_unix,
|
||||
Opt_nocase,
|
||||
Opt_brl,
|
||||
Opt_handlecache,
|
||||
Opt_forcemandatorylock,
|
||||
Opt_setuidfromacl,
|
||||
Opt_setuids,
|
||||
Opt_dynperm,
|
||||
Opt_intr,
|
||||
Opt_strictsync,
|
||||
Opt_serverino,
|
||||
Opt_rwpidforward,
|
||||
Opt_cifsacl,
|
||||
Opt_acl,
|
||||
Opt_locallease,
|
||||
Opt_sign,
|
||||
Opt_ignore_signature,
|
||||
Opt_seal,
|
||||
Opt_noac,
|
||||
Opt_fsc,
|
||||
Opt_mfsymlinks,
|
||||
Opt_multiuser,
|
||||
Opt_sloppy,
|
||||
Opt_nosharesock,
|
||||
Opt_persistent,
|
||||
Opt_resilient,
|
||||
Opt_domainauto,
|
||||
Opt_rdma,
|
||||
Opt_modesid,
|
||||
Opt_rootfs,
|
||||
Opt_multichannel,
|
||||
Opt_compress,
|
||||
Opt_witness,
|
||||
|
||||
/* Mount options which take numeric value */
|
||||
Opt_backupuid,
|
||||
Opt_backupgid,
|
||||
Opt_uid,
|
||||
Opt_cruid,
|
||||
Opt_gid,
|
||||
Opt_port,
|
||||
Opt_file_mode,
|
||||
Opt_dirmode,
|
||||
Opt_min_enc_offload,
|
||||
Opt_blocksize,
|
||||
Opt_rsize,
|
||||
Opt_wsize,
|
||||
Opt_actimeo,
|
||||
Opt_echo_interval,
|
||||
Opt_max_credits,
|
||||
Opt_snapshot,
|
||||
Opt_max_channels,
|
||||
Opt_handletimeout,
|
||||
|
||||
/* Mount options which take string value */
|
||||
Opt_source,
|
||||
Opt_user,
|
||||
Opt_pass,
|
||||
Opt_ip,
|
||||
Opt_domain,
|
||||
Opt_srcaddr,
|
||||
Opt_iocharset,
|
||||
Opt_netbiosname,
|
||||
Opt_servern,
|
||||
Opt_ver,
|
||||
Opt_vers,
|
||||
Opt_sec,
|
||||
Opt_cache,
|
||||
|
||||
/* Mount options to be ignored */
|
||||
Opt_ignore,
|
||||
|
||||
Opt_err
|
||||
};
|
||||
|
||||
struct smb3_fs_context {
|
||||
bool uid_specified;
|
||||
bool gid_specified;
|
||||
bool sloppy;
|
||||
bool got_ip;
|
||||
bool got_version;
|
||||
bool got_rsize;
|
||||
bool got_wsize;
|
||||
bool got_bsize;
|
||||
unsigned short port;
|
||||
|
||||
char *username;
|
||||
char *password;
|
||||
char *domainname;
|
||||
char *UNC;
|
||||
char *nodename;
|
||||
char *iocharset; /* local code page for mapping to and from Unicode */
|
||||
char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */
|
||||
char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */
|
||||
kuid_t cred_uid;
|
||||
kuid_t linux_uid;
|
||||
kgid_t linux_gid;
|
||||
kuid_t backupuid;
|
||||
kgid_t backupgid;
|
||||
umode_t file_mode;
|
||||
umode_t dir_mode;
|
||||
enum securityEnum sectype; /* sectype requested via mnt opts */
|
||||
bool sign; /* was signing requested via mnt opts? */
|
||||
bool ignore_signature:1;
|
||||
bool retry:1;
|
||||
bool intr:1;
|
||||
bool setuids:1;
|
||||
bool setuidfromacl:1;
|
||||
bool override_uid:1;
|
||||
bool override_gid:1;
|
||||
bool dynperm:1;
|
||||
bool noperm:1;
|
||||
bool nodelete:1;
|
||||
bool mode_ace:1;
|
||||
bool no_psx_acl:1; /* set if posix acl support should be disabled */
|
||||
bool cifs_acl:1;
|
||||
bool backupuid_specified; /* mount option backupuid is specified */
|
||||
bool backupgid_specified; /* mount option backupgid is specified */
|
||||
bool no_xattr:1; /* set if xattr (EA) support should be disabled*/
|
||||
bool server_ino:1; /* use inode numbers from server ie UniqueId */
|
||||
bool direct_io:1;
|
||||
bool strict_io:1; /* strict cache behavior */
|
||||
bool cache_ro:1;
|
||||
bool cache_rw:1;
|
||||
bool remap:1; /* set to remap seven reserved chars in filenames */
|
||||
bool sfu_remap:1; /* remap seven reserved chars ala SFU */
|
||||
bool posix_paths:1; /* unset to not ask for posix pathnames. */
|
||||
bool no_linux_ext:1;
|
||||
bool linux_ext:1;
|
||||
bool sfu_emul:1;
|
||||
bool nullauth:1; /* attempt to authenticate with null user */
|
||||
bool nocase:1; /* request case insensitive filenames */
|
||||
bool nobrl:1; /* disable sending byte range locks to srv */
|
||||
bool nohandlecache:1; /* disable caching dir handles if srvr probs */
|
||||
bool mand_lock:1; /* send mandatory not posix byte range lock reqs */
|
||||
bool seal:1; /* request transport encryption on share */
|
||||
bool nodfs:1; /* Do not request DFS, even if available */
|
||||
bool local_lease:1; /* check leases only on local system, not remote */
|
||||
bool noblocksnd:1;
|
||||
bool noautotune:1;
|
||||
bool nostrictsync:1; /* do not force expensive SMBflush on every sync */
|
||||
bool no_lease:1; /* disable requesting leases */
|
||||
bool fsc:1; /* enable fscache */
|
||||
bool mfsymlinks:1; /* use Minshall+French Symlinks */
|
||||
bool multiuser:1;
|
||||
bool rwpidforward:1; /* pid forward for read/write operations */
|
||||
bool nosharesock:1;
|
||||
bool persistent:1;
|
||||
bool nopersistent:1;
|
||||
bool resilient:1; /* noresilient not required since not fored for CA */
|
||||
bool domainauto:1;
|
||||
bool rdma:1;
|
||||
bool multichannel:1;
|
||||
bool use_client_guid:1;
|
||||
/* reuse existing guid for multichannel */
|
||||
u8 client_guid[SMB2_CLIENT_GUID_SIZE];
|
||||
unsigned int bsize;
|
||||
unsigned int rsize;
|
||||
unsigned int wsize;
|
||||
unsigned int min_offload;
|
||||
bool sockopt_tcp_nodelay:1;
|
||||
unsigned long actimeo; /* attribute cache timeout (jiffies) */
|
||||
struct smb_version_operations *ops;
|
||||
struct smb_version_values *vals;
|
||||
char *prepath;
|
||||
struct sockaddr_storage dstaddr; /* destination address */
|
||||
struct sockaddr_storage srcaddr; /* allow binding to a local IP */
|
||||
struct nls_table *local_nls; /* This is a copy of the pointer in cifs_sb */
|
||||
unsigned int echo_interval; /* echo interval in secs */
|
||||
__u64 snapshot_time; /* needed for timewarp tokens */
|
||||
__u32 handle_timeout; /* persistent and durable handle timeout in ms */
|
||||
unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
|
||||
unsigned int max_channels;
|
||||
__u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
|
||||
bool rootfs:1; /* if it's a SMB root file system */
|
||||
bool witness:1; /* use witness protocol */
|
||||
|
||||
char *mount_options;
|
||||
};
|
||||
|
||||
extern const struct fs_parameter_spec smb3_fs_parameters[];
|
||||
|
||||
extern int cifs_parse_cache_flavor(char *value,
|
||||
struct smb3_fs_context *ctx);
|
||||
extern int cifs_parse_security_flavors(char *value,
|
||||
struct smb3_fs_context *ctx);
|
||||
extern int smb3_init_fs_context(struct fs_context *fc);
|
||||
extern void smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx);
|
||||
extern void smb3_cleanup_fs_context(struct smb3_fs_context *ctx);
|
||||
|
||||
static inline struct smb3_fs_context *smb3_fc2context(const struct fs_context *fc)
|
||||
{
|
||||
return fc->fs_private;
|
||||
}
|
||||
|
||||
extern int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx);
|
||||
extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "cifsglob.h"
|
||||
#include "cifs_debug.h"
|
||||
#include "cifs_fs_sb.h"
|
||||
#include "cifsproto.h"
|
||||
|
||||
/*
|
||||
* Key layout of CIFS server cache index object
|
||||
|
||||
@@ -57,7 +57,6 @@ extern const struct fscache_cookie_def cifs_fscache_inode_object_def;
|
||||
|
||||
extern int cifs_fscache_register(void);
|
||||
extern void cifs_fscache_unregister(void);
|
||||
extern char *extract_sharename(const char *);
|
||||
|
||||
/*
|
||||
* fscache.c
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "cifs_fs_sb.h"
|
||||
#include "cifs_unicode.h"
|
||||
#include "fscache.h"
|
||||
#include "fs_context.h"
|
||||
|
||||
|
||||
static void cifs_set_ops(struct inode *inode)
|
||||
@@ -294,7 +295,7 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,
|
||||
break;
|
||||
}
|
||||
|
||||
fattr->cf_uid = cifs_sb->mnt_uid;
|
||||
fattr->cf_uid = cifs_sb->ctx->linux_uid;
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) {
|
||||
u64 id = le64_to_cpu(info->Uid);
|
||||
if (id < ((uid_t)-1)) {
|
||||
@@ -304,7 +305,7 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,
|
||||
}
|
||||
}
|
||||
|
||||
fattr->cf_gid = cifs_sb->mnt_gid;
|
||||
fattr->cf_gid = cifs_sb->ctx->linux_gid;
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) {
|
||||
u64 id = le64_to_cpu(info->Gid);
|
||||
if (id < ((gid_t)-1)) {
|
||||
@@ -333,8 +334,8 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
|
||||
|
||||
memset(fattr, 0, sizeof(*fattr));
|
||||
fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU;
|
||||
fattr->cf_uid = cifs_sb->mnt_uid;
|
||||
fattr->cf_gid = cifs_sb->mnt_gid;
|
||||
fattr->cf_uid = cifs_sb->ctx->linux_uid;
|
||||
fattr->cf_gid = cifs_sb->ctx->linux_gid;
|
||||
ktime_get_coarse_real_ts64(&fattr->cf_mtime);
|
||||
fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime;
|
||||
fattr->cf_nlink = 2;
|
||||
@@ -644,8 +645,8 @@ smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *
|
||||
}
|
||||
/* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */
|
||||
|
||||
fattr->cf_uid = cifs_sb->mnt_uid; /* TODO: map uid and gid from SID */
|
||||
fattr->cf_gid = cifs_sb->mnt_gid;
|
||||
fattr->cf_uid = cifs_sb->ctx->linux_uid; /* TODO: map uid and gid from SID */
|
||||
fattr->cf_gid = cifs_sb->ctx->linux_gid;
|
||||
|
||||
cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n",
|
||||
fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
|
||||
@@ -685,25 +686,25 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
||||
|
||||
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
|
||||
if (reparse_tag == IO_REPARSE_TAG_LX_SYMLINK) {
|
||||
fattr->cf_mode |= S_IFLNK | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
} else if (reparse_tag == IO_REPARSE_TAG_LX_FIFO) {
|
||||
fattr->cf_mode |= S_IFIFO | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_FIFO;
|
||||
} else if (reparse_tag == IO_REPARSE_TAG_AF_UNIX) {
|
||||
fattr->cf_mode |= S_IFSOCK | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_SOCK;
|
||||
} else if (reparse_tag == IO_REPARSE_TAG_LX_CHR) {
|
||||
fattr->cf_mode |= S_IFCHR | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_CHR;
|
||||
} else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) {
|
||||
fattr->cf_mode |= S_IFBLK | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_BLK;
|
||||
} else if (symlink) { /* TODO add more reparse tag checks */
|
||||
fattr->cf_mode = S_IFLNK;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
|
||||
fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
|
||||
fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode;
|
||||
fattr->cf_dtype = DT_DIR;
|
||||
/*
|
||||
* Server can return wrong NumberOfLinks value for directories
|
||||
@@ -712,7 +713,7 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
||||
if (!tcon->unix_ext)
|
||||
fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
|
||||
} else {
|
||||
fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_REG;
|
||||
|
||||
/* clear write bits if ATTR_READONLY is set */
|
||||
@@ -731,8 +732,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
||||
}
|
||||
}
|
||||
|
||||
fattr->cf_uid = cifs_sb->mnt_uid;
|
||||
fattr->cf_gid = cifs_sb->mnt_gid;
|
||||
fattr->cf_uid = cifs_sb->ctx->linux_uid;
|
||||
fattr->cf_gid = cifs_sb->ctx->linux_gid;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -771,6 +772,7 @@ cifs_get_file_info(struct file *filp)
|
||||
*/
|
||||
rc = 0;
|
||||
CIFS_I(inode)->time = 0;
|
||||
goto cgfi_exit;
|
||||
default:
|
||||
goto cgfi_exit;
|
||||
}
|
||||
@@ -803,11 +805,15 @@ static __u64 simple_hashstr(const char *str)
|
||||
* cifs_backup_query_path_info - SMB1 fallback code to get ino
|
||||
*
|
||||
* Fallback code to get file metadata when we don't have access to
|
||||
* @full_path (EACCES) and have backup creds.
|
||||
* full_path (EACCES) and have backup creds.
|
||||
*
|
||||
* @data will be set to search info result buffer
|
||||
* @resp_buf will be set to cifs resp buf and needs to be freed with
|
||||
* cifs_buf_release() when done with @data.
|
||||
* @xid: transaction id used to identify original request in logs
|
||||
* @tcon: information about the server share we have mounted
|
||||
* @sb: the superblock stores info such as disk space available
|
||||
* @full_path: name of the file we are getting the metadata for
|
||||
* @resp_buf: will be set to cifs resp buf and needs to be freed with
|
||||
* cifs_buf_release() when done with @data
|
||||
* @data: will be set to search info result buffer
|
||||
*/
|
||||
static int
|
||||
cifs_backup_query_path_info(int xid,
|
||||
@@ -1386,8 +1392,8 @@ iget_no_retry:
|
||||
set_nlink(inode, 2);
|
||||
inode->i_op = &cifs_ipc_inode_ops;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
inode->i_uid = cifs_sb->mnt_uid;
|
||||
inode->i_gid = cifs_sb->mnt_gid;
|
||||
inode->i_uid = cifs_sb->ctx->linux_uid;
|
||||
inode->i_gid = cifs_sb->ctx->linux_gid;
|
||||
spin_unlock(&inode->i_lock);
|
||||
} else if (rc) {
|
||||
iget_failed(inode);
|
||||
@@ -2192,11 +2198,11 @@ cifs_inode_needs_reval(struct inode *inode)
|
||||
if (!lookupCacheEnabled)
|
||||
return true;
|
||||
|
||||
if (!cifs_sb->actimeo)
|
||||
if (!cifs_sb->ctx->actimeo)
|
||||
return true;
|
||||
|
||||
if (!time_in_range(jiffies, cifs_i->time,
|
||||
cifs_i->time + cifs_sb->actimeo))
|
||||
cifs_i->time + cifs_sb->ctx->actimeo))
|
||||
return true;
|
||||
|
||||
/* hardlinked files w/ noserverino get "special" treatment */
|
||||
@@ -2228,7 +2234,9 @@ cifs_invalidate_mapping(struct inode *inode)
|
||||
|
||||
/**
|
||||
* cifs_wait_bit_killable - helper for functions that are sleeping on bit locks
|
||||
* @word: long word containing the bit lock
|
||||
*
|
||||
* @key: currently unused
|
||||
* @mode: the task state to sleep in
|
||||
*/
|
||||
static int
|
||||
cifs_wait_bit_killable(struct wait_bit_key *key, int mode)
|
||||
@@ -2401,7 +2409,7 @@ int cifs_getattr(const struct path *path, struct kstat *stat,
|
||||
}
|
||||
|
||||
generic_fillattr(inode, stat);
|
||||
stat->blksize = cifs_sb->bsize;
|
||||
stat->blksize = cifs_sb->ctx->bsize;
|
||||
stat->ino = CIFS_I(inode)->uniqueid;
|
||||
|
||||
/* old CIFS Unix Extensions doesn't return create time */
|
||||
@@ -2812,7 +2820,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
|
||||
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) ||
|
||||
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) {
|
||||
if (uid_valid(uid) || gid_valid(gid)) {
|
||||
rc = id_mode_to_cifs_acl(inode, full_path, NO_CHANGE_64,
|
||||
mode = NO_CHANGE_64;
|
||||
rc = id_mode_to_cifs_acl(inode, full_path, &mode,
|
||||
uid, gid);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: Setting id failed with error: %d\n",
|
||||
@@ -2833,13 +2842,20 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
|
||||
rc = 0;
|
||||
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) ||
|
||||
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) {
|
||||
rc = id_mode_to_cifs_acl(inode, full_path, mode,
|
||||
rc = id_mode_to_cifs_acl(inode, full_path, &mode,
|
||||
INVALID_UID, INVALID_GID);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: Setting ACL failed with error: %d\n",
|
||||
__func__, rc);
|
||||
goto cifs_setattr_exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* In case of CIFS_MOUNT_CIFS_ACL, we cannot support all modes.
|
||||
* Pick up the actual mode bits that were set.
|
||||
*/
|
||||
if (mode != attrs->ia_mode)
|
||||
attrs->ia_mode = mode;
|
||||
} else
|
||||
if (((mode & S_IWUGO) == 0) &&
|
||||
(cifsInode->cifsAttrs & ATTR_READONLY) == 0) {
|
||||
@@ -2862,10 +2878,10 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
|
||||
attrs->ia_mode &= ~(S_IALLUGO);
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
attrs->ia_mode |=
|
||||
cifs_sb->mnt_dir_mode;
|
||||
cifs_sb->ctx->dir_mode;
|
||||
else
|
||||
attrs->ia_mode |=
|
||||
cifs_sb->mnt_file_mode;
|
||||
cifs_sb->ctx->file_mode;
|
||||
}
|
||||
} else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) {
|
||||
/* ignore mode change - ATTR_READONLY hasn't changed */
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
#include "dns_resolve.h"
|
||||
#endif
|
||||
#include "fs_context.h"
|
||||
|
||||
extern mempool_t *cifs_sm_req_poolp;
|
||||
extern mempool_t *cifs_req_poolp;
|
||||
@@ -632,11 +633,11 @@ bool
|
||||
backup_cred(struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) {
|
||||
if (uid_eq(cifs_sb->mnt_backupuid, current_fsuid()))
|
||||
if (uid_eq(cifs_sb->ctx->backupuid, current_fsuid()))
|
||||
return true;
|
||||
}
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) {
|
||||
if (in_group_p(cifs_sb->mnt_backupgid))
|
||||
if (in_group_p(cifs_sb->ctx->backupgid))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
89
fs/cifs/netlink.c
Normal file
89
fs/cifs/netlink.c
Normal file
@@ -0,0 +1,89 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Netlink routines for CIFS
|
||||
*
|
||||
* Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
|
||||
*/
|
||||
|
||||
#include <net/genetlink.h>
|
||||
#include <uapi/linux/cifs/cifs_netlink.h>
|
||||
|
||||
#include "netlink.h"
|
||||
#include "cifsglob.h"
|
||||
#include "cifs_debug.h"
|
||||
#include "cifs_swn.h"
|
||||
|
||||
static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = {
|
||||
[CIFS_GENL_ATTR_SWN_REGISTRATION_ID] = { .type = NLA_U32 },
|
||||
[CIFS_GENL_ATTR_SWN_NET_NAME] = { .type = NLA_STRING },
|
||||
[CIFS_GENL_ATTR_SWN_SHARE_NAME] = { .type = NLA_STRING },
|
||||
[CIFS_GENL_ATTR_SWN_IP] = { .len = sizeof(struct sockaddr_storage) },
|
||||
[CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY] = { .type = NLA_FLAG },
|
||||
[CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY] = { .type = NLA_FLAG },
|
||||
[CIFS_GENL_ATTR_SWN_IP_NOTIFY] = { .type = NLA_FLAG },
|
||||
[CIFS_GENL_ATTR_SWN_KRB_AUTH] = { .type = NLA_FLAG },
|
||||
[CIFS_GENL_ATTR_SWN_USER_NAME] = { .type = NLA_STRING },
|
||||
[CIFS_GENL_ATTR_SWN_PASSWORD] = { .type = NLA_STRING },
|
||||
[CIFS_GENL_ATTR_SWN_DOMAIN_NAME] = { .type = NLA_STRING },
|
||||
[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE] = { .type = NLA_U32 },
|
||||
[CIFS_GENL_ATTR_SWN_RESOURCE_STATE] = { .type = NLA_U32 },
|
||||
[CIFS_GENL_ATTR_SWN_RESOURCE_NAME] = { .type = NLA_STRING},
|
||||
};
|
||||
|
||||
static struct genl_ops cifs_genl_ops[] = {
|
||||
{
|
||||
.cmd = CIFS_GENL_CMD_SWN_NOTIFY,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = cifs_swn_notify,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct genl_multicast_group cifs_genl_mcgrps[] = {
|
||||
[CIFS_GENL_MCGRP_SWN] = { .name = CIFS_GENL_MCGRP_SWN_NAME },
|
||||
};
|
||||
|
||||
struct genl_family cifs_genl_family = {
|
||||
.name = CIFS_GENL_NAME,
|
||||
.version = CIFS_GENL_VERSION,
|
||||
.hdrsize = 0,
|
||||
.maxattr = CIFS_GENL_ATTR_MAX,
|
||||
.module = THIS_MODULE,
|
||||
.policy = cifs_genl_policy,
|
||||
.ops = cifs_genl_ops,
|
||||
.n_ops = ARRAY_SIZE(cifs_genl_ops),
|
||||
.mcgrps = cifs_genl_mcgrps,
|
||||
.n_mcgrps = ARRAY_SIZE(cifs_genl_mcgrps),
|
||||
};
|
||||
|
||||
/**
|
||||
* cifs_genl_init - Register generic netlink family
|
||||
*
|
||||
* Return zero if initialized successfully, otherwise non-zero.
|
||||
*/
|
||||
int cifs_genl_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = genl_register_family(&cifs_genl_family);
|
||||
if (ret < 0) {
|
||||
cifs_dbg(VFS, "%s: failed to register netlink family\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cifs_genl_exit - Unregister generic netlink family
|
||||
*/
|
||||
void cifs_genl_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = genl_unregister_family(&cifs_genl_family);
|
||||
if (ret < 0) {
|
||||
cifs_dbg(VFS, "%s: failed to unregister netlink family\n",
|
||||
__func__);
|
||||
}
|
||||
}
|
||||
16
fs/cifs/netlink.h
Normal file
16
fs/cifs/netlink.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Netlink routines for CIFS
|
||||
*
|
||||
* Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
|
||||
*/
|
||||
|
||||
#ifndef _CIFS_NETLINK_H
|
||||
#define _CIFS_NETLINK_H
|
||||
|
||||
extern struct genl_family cifs_genl_family;
|
||||
|
||||
extern int cifs_genl_init(void);
|
||||
extern void cifs_genl_exit(void);
|
||||
|
||||
#endif /* _CIFS_NETLINK_H */
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "cifs_fs_sb.h"
|
||||
#include "cifsfs.h"
|
||||
#include "smb2proto.h"
|
||||
#include "fs_context.h"
|
||||
|
||||
/*
|
||||
* To be safe - for UCS to UTF-8 with strings loaded with the rare long
|
||||
@@ -165,8 +166,8 @@ static bool reparse_file_needs_reval(const struct cifs_fattr *fattr)
|
||||
static void
|
||||
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
fattr->cf_uid = cifs_sb->mnt_uid;
|
||||
fattr->cf_gid = cifs_sb->mnt_gid;
|
||||
fattr->cf_uid = cifs_sb->ctx->linux_uid;
|
||||
fattr->cf_gid = cifs_sb->ctx->linux_gid;
|
||||
|
||||
/*
|
||||
* The IO_REPARSE_TAG_LX_ tags originally were used by WSL but they
|
||||
@@ -177,25 +178,25 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
|
||||
* reasonably map some of them to directories vs. files vs. symlinks
|
||||
*/
|
||||
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
|
||||
fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
|
||||
fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode;
|
||||
fattr->cf_dtype = DT_DIR;
|
||||
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_SYMLINK) {
|
||||
fattr->cf_mode |= S_IFLNK | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_FIFO) {
|
||||
fattr->cf_mode |= S_IFIFO | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_FIFO;
|
||||
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_AF_UNIX) {
|
||||
fattr->cf_mode |= S_IFSOCK | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_SOCK;
|
||||
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_CHR) {
|
||||
fattr->cf_mode |= S_IFCHR | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_CHR;
|
||||
} else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_BLK) {
|
||||
fattr->cf_mode |= S_IFBLK | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_BLK;
|
||||
} else { /* TODO: should we mark some other reparse points (like DFSR) as directories? */
|
||||
fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
|
||||
fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_dtype = DT_REG;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
#include <linux/slab.h>
|
||||
#include "cifs_spnego.h"
|
||||
#include "smb2proto.h"
|
||||
#include "fs_context.h"
|
||||
|
||||
static int
|
||||
cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
struct cifs_server_iface *iface);
|
||||
|
||||
bool
|
||||
is_server_using_iface(struct TCP_Server_Info *server,
|
||||
@@ -70,7 +75,7 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
|
||||
}
|
||||
|
||||
/* returns number of channels added */
|
||||
int cifs_try_adding_channels(struct cifs_ses *ses)
|
||||
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
|
||||
{
|
||||
int old_chan_count = ses->chan_count;
|
||||
int left = ses->chan_max - ses->chan_count;
|
||||
@@ -133,7 +138,7 @@ int cifs_try_adding_channels(struct cifs_ses *ses)
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = cifs_ses_add_channel(ses, iface);
|
||||
rc = cifs_ses_add_channel(cifs_sb, ses, iface);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "failed to open extra channel on iface#%d rc=%d\n",
|
||||
i, rc);
|
||||
@@ -166,11 +171,12 @@ cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
cifs_ses_add_channel(struct cifs_ses *ses, struct cifs_server_iface *iface)
|
||||
static int
|
||||
cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
struct cifs_server_iface *iface)
|
||||
{
|
||||
struct cifs_chan *chan;
|
||||
struct smb_vol vol = {NULL};
|
||||
struct smb3_fs_context ctx = {NULL};
|
||||
static const char unc_fmt[] = "\\%s\\foo";
|
||||
char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0};
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
|
||||
@@ -188,67 +194,62 @@ cifs_ses_add_channel(struct cifs_ses *ses, struct cifs_server_iface *iface)
|
||||
&ipv6->sin6_addr);
|
||||
|
||||
/*
|
||||
* Setup a smb_vol with mostly the same info as the existing
|
||||
* Setup a ctx with mostly the same info as the existing
|
||||
* session and overwrite it with the requested iface data.
|
||||
*
|
||||
* We need to setup at least the fields used for negprot and
|
||||
* sesssetup.
|
||||
*
|
||||
* We only need the volume here, so we can reuse memory from
|
||||
* We only need the ctx here, so we can reuse memory from
|
||||
* the session and server without caring about memory
|
||||
* management.
|
||||
*/
|
||||
|
||||
/* Always make new connection for now (TODO?) */
|
||||
vol.nosharesock = true;
|
||||
ctx.nosharesock = true;
|
||||
|
||||
/* Auth */
|
||||
vol.domainauto = ses->domainAuto;
|
||||
vol.domainname = ses->domainName;
|
||||
vol.username = ses->user_name;
|
||||
vol.password = ses->password;
|
||||
vol.sectype = ses->sectype;
|
||||
vol.sign = ses->sign;
|
||||
ctx.domainauto = ses->domainAuto;
|
||||
ctx.domainname = ses->domainName;
|
||||
ctx.username = ses->user_name;
|
||||
ctx.password = ses->password;
|
||||
ctx.sectype = ses->sectype;
|
||||
ctx.sign = ses->sign;
|
||||
|
||||
/* UNC and paths */
|
||||
/* XXX: Use ses->server->hostname? */
|
||||
sprintf(unc, unc_fmt, ses->serverName);
|
||||
vol.UNC = unc;
|
||||
vol.prepath = "";
|
||||
ctx.UNC = unc;
|
||||
ctx.prepath = "";
|
||||
|
||||
/* Reuse same version as master connection */
|
||||
vol.vals = ses->server->vals;
|
||||
vol.ops = ses->server->ops;
|
||||
ctx.vals = ses->server->vals;
|
||||
ctx.ops = ses->server->ops;
|
||||
|
||||
vol.noblocksnd = ses->server->noblocksnd;
|
||||
vol.noautotune = ses->server->noautotune;
|
||||
vol.sockopt_tcp_nodelay = ses->server->tcp_nodelay;
|
||||
vol.echo_interval = ses->server->echo_interval / HZ;
|
||||
ctx.noblocksnd = ses->server->noblocksnd;
|
||||
ctx.noautotune = ses->server->noautotune;
|
||||
ctx.sockopt_tcp_nodelay = ses->server->tcp_nodelay;
|
||||
ctx.echo_interval = ses->server->echo_interval / HZ;
|
||||
|
||||
/*
|
||||
* This will be used for encoding/decoding user/domain/pw
|
||||
* during sess setup auth.
|
||||
*
|
||||
* XXX: We use the default for simplicity but the proper way
|
||||
* would be to use the one that ses used, which is not
|
||||
* stored. This might break when dealing with non-ascii
|
||||
* strings.
|
||||
*/
|
||||
vol.local_nls = load_nls_default();
|
||||
ctx.local_nls = cifs_sb->local_nls;
|
||||
|
||||
/* Use RDMA if possible */
|
||||
vol.rdma = iface->rdma_capable;
|
||||
memcpy(&vol.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage));
|
||||
ctx.rdma = iface->rdma_capable;
|
||||
memcpy(&ctx.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage));
|
||||
|
||||
/* reuse master con client guid */
|
||||
memcpy(&vol.client_guid, ses->server->client_guid,
|
||||
memcpy(&ctx.client_guid, ses->server->client_guid,
|
||||
SMB2_CLIENT_GUID_SIZE);
|
||||
vol.use_client_guid = true;
|
||||
ctx.use_client_guid = true;
|
||||
|
||||
mutex_lock(&ses->session_mutex);
|
||||
|
||||
chan = ses->binding_chan = &ses->chans[ses->chan_count];
|
||||
chan->server = cifs_get_tcp_session(&vol);
|
||||
chan->server = cifs_get_tcp_session(&ctx);
|
||||
if (IS_ERR(chan->server)) {
|
||||
rc = PTR_ERR(chan->server);
|
||||
chan->server = NULL;
|
||||
@@ -274,7 +275,7 @@ cifs_ses_add_channel(struct cifs_ses *ses, struct cifs_server_iface *iface)
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = cifs_setup_session(xid, ses, vol.local_nls);
|
||||
rc = cifs_setup_session(xid, ses, cifs_sb->local_nls);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
@@ -297,7 +298,6 @@ out:
|
||||
|
||||
if (rc && chan->server)
|
||||
cifs_put_tcp_session(chan->server, 0);
|
||||
unload_nls(vol.local_nls);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -812,6 +812,7 @@ cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
|
||||
return NTLMv2;
|
||||
if (global_secflags & CIFSSEC_MAY_NTLM)
|
||||
return NTLM;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "cifs_debug.h"
|
||||
#include "cifspdu.h"
|
||||
#include "cifs_unicode.h"
|
||||
#include "fs_context.h"
|
||||
|
||||
/*
|
||||
* An NT cancel request header looks just like the original request except:
|
||||
@@ -428,15 +429,15 @@ cifs_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
|
||||
{
|
||||
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
unsigned int wsize;
|
||||
|
||||
/* start with specified wsize, or default */
|
||||
if (volume_info->wsize)
|
||||
wsize = volume_info->wsize;
|
||||
if (ctx->wsize)
|
||||
wsize = ctx->wsize;
|
||||
else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
|
||||
wsize = CIFS_DEFAULT_IOSIZE;
|
||||
else
|
||||
@@ -463,7 +464,7 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
|
||||
{
|
||||
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
@@ -488,7 +489,7 @@ cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
else
|
||||
defsize = server->maxBuf - sizeof(READ_RSP);
|
||||
|
||||
rsize = volume_info->rsize ? volume_info->rsize : defsize;
|
||||
rsize = ctx->rsize ? ctx->rsize : defsize;
|
||||
|
||||
/*
|
||||
* no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to
|
||||
@@ -1005,7 +1006,7 @@ cifs_is_read_op(__u32 oplock)
|
||||
static unsigned int
|
||||
cifs_wp_retry_size(struct inode *inode)
|
||||
{
|
||||
return CIFS_SB(inode->i_sb)->wsize;
|
||||
return CIFS_SB(inode->i_sb)->ctx->wsize;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
||||
@@ -94,6 +94,8 @@ static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
|
||||
/* SMB2_OPLOCK_BREAK */ cpu_to_le16(24)
|
||||
};
|
||||
|
||||
#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_sync_hdr) + sizeof(struct smb2_negotiate_rsp))
|
||||
|
||||
static __u32 get_neg_ctxt_len(struct smb2_sync_hdr *hdr, __u32 len,
|
||||
__u32 non_ctxlen)
|
||||
{
|
||||
@@ -107,13 +109,28 @@ static __u32 get_neg_ctxt_len(struct smb2_sync_hdr *hdr, __u32 len,
|
||||
(pneg_rsp->DialectRevision != cpu_to_le16(SMB311_PROT_ID)))
|
||||
return 0;
|
||||
|
||||
/* Make sure that negotiate contexts start after gss security blob */
|
||||
/*
|
||||
* if SPNEGO blob present (ie the RFC2478 GSS info which indicates
|
||||
* which security mechanisms the server supports) make sure that
|
||||
* the negotiate contexts start after it
|
||||
*/
|
||||
nc_offset = le32_to_cpu(pneg_rsp->NegotiateContextOffset);
|
||||
if (nc_offset < non_ctxlen) {
|
||||
pr_warn_once("Invalid negotiate context offset\n");
|
||||
/*
|
||||
* non_ctxlen is at least shdr->StructureSize + pdu->StructureSize2
|
||||
* and the latter is 1 byte bigger than the fix-sized area of the
|
||||
* NEGOTIATE response
|
||||
*/
|
||||
if (nc_offset + 1 < non_ctxlen) {
|
||||
pr_warn_once("Invalid negotiate context offset %d\n", nc_offset);
|
||||
return 0;
|
||||
}
|
||||
size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen;
|
||||
} else if (nc_offset + 1 == non_ctxlen) {
|
||||
cifs_dbg(FYI, "no SPNEGO security blob in negprot rsp\n");
|
||||
size_of_pad_before_neg_ctxts = 0;
|
||||
} else if (non_ctxlen == SMB311_NEGPROT_BASE_SIZE)
|
||||
/* has padding, but no SPNEGO blob */
|
||||
size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen + 1;
|
||||
else
|
||||
size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen;
|
||||
|
||||
/* Verify that at least minimal negotiate contexts fit within frame */
|
||||
if (len < nc_offset + (neg_count * sizeof(struct smb2_neg_context))) {
|
||||
@@ -859,6 +876,10 @@ smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
|
||||
*
|
||||
* Assumes @iov does not contain the rfc1002 length and iov[0] has the
|
||||
* SMB2 header.
|
||||
*
|
||||
* @ses: server session structure
|
||||
* @iov: array containing the SMB request we will send to the server
|
||||
* @nvec: number of array entries for the iov
|
||||
*/
|
||||
int
|
||||
smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "smb2glob.h"
|
||||
#include "cifs_ioctl.h"
|
||||
#include "smbdirect.h"
|
||||
#include "fs_context.h"
|
||||
|
||||
/* Change credits for different ops and return the total number of credits */
|
||||
static int
|
||||
@@ -99,9 +100,10 @@ smb2_add_credits(struct TCP_Server_Info *server,
|
||||
spin_unlock(&server->req_lock);
|
||||
wake_up(&server->request_q);
|
||||
|
||||
if (reconnect_detected)
|
||||
if (reconnect_detected) {
|
||||
cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n",
|
||||
add, instance);
|
||||
}
|
||||
|
||||
if (server->tcpStatus == CifsNeedReconnect
|
||||
|| server->tcpStatus == CifsExiting)
|
||||
@@ -123,7 +125,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
|
||||
default:
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->hostname, rc, add);
|
||||
cifs_dbg(FYI, "add %u credits total=%d\n", add, rc);
|
||||
cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, rc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +137,11 @@ smb2_set_credits(struct TCP_Server_Info *server, const int val)
|
||||
if (val == 1)
|
||||
server->reconnect_instance++;
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
trace_smb3_set_credits(server->CurrentMid,
|
||||
server->hostname, val, val);
|
||||
cifs_dbg(FYI, "%s: set %u credits\n", __func__, val);
|
||||
|
||||
/* don't log while holding the lock */
|
||||
if (val == 1)
|
||||
cifs_dbg(FYI, "set credits to 1 due to smb2 reconnect\n");
|
||||
@@ -201,6 +208,7 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
|
||||
DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE);
|
||||
credits->instance = server->reconnect_instance;
|
||||
server->credits -= credits->value;
|
||||
scredits = server->credits;
|
||||
server->in_flight++;
|
||||
if (server->in_flight > server->max_in_flight)
|
||||
server->max_in_flight = server->in_flight;
|
||||
@@ -208,6 +216,12 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
|
||||
}
|
||||
}
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->hostname, scredits, -(credits->value));
|
||||
cifs_dbg(FYI, "%s: removed %u credits total=%d\n",
|
||||
__func__, credits->value, scredits);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -217,13 +231,17 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
|
||||
const unsigned int payload_size)
|
||||
{
|
||||
int new_val = DIV_ROUND_UP(payload_size, SMB2_MAX_BUFFER_SIZE);
|
||||
int scredits;
|
||||
|
||||
if (!credits->value || credits->value == new_val)
|
||||
return 0;
|
||||
|
||||
if (credits->value < new_val) {
|
||||
WARN_ONCE(1, "request has less credits (%d) than required (%d)",
|
||||
credits->value, new_val);
|
||||
trace_smb3_too_many_credits(server->CurrentMid,
|
||||
server->hostname, 0, credits->value - new_val);
|
||||
cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)",
|
||||
credits->value, new_val);
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
@@ -231,15 +249,24 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
|
||||
|
||||
if (server->reconnect_instance != credits->instance) {
|
||||
spin_unlock(&server->req_lock);
|
||||
trace_smb3_reconnect_detected(server->CurrentMid,
|
||||
server->hostname, 0, 0);
|
||||
cifs_server_dbg(VFS, "trying to return %d credits to old session\n",
|
||||
credits->value - new_val);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
server->credits += credits->value - new_val;
|
||||
scredits = server->credits;
|
||||
spin_unlock(&server->req_lock);
|
||||
wake_up(&server->request_q);
|
||||
credits->value = new_val;
|
||||
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->hostname, scredits, credits->value - new_val);
|
||||
cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n",
|
||||
__func__, credits->value - new_val, scredits);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -339,13 +366,13 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
|
||||
{
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
unsigned int wsize;
|
||||
|
||||
/* start with specified wsize, or default */
|
||||
wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE;
|
||||
wsize = ctx->wsize ? ctx->wsize : CIFS_DEFAULT_IOSIZE;
|
||||
wsize = min_t(unsigned int, wsize, server->max_write);
|
||||
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
|
||||
wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
|
||||
@@ -354,13 +381,13 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
|
||||
{
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
unsigned int wsize;
|
||||
|
||||
/* start with specified wsize, or default */
|
||||
wsize = volume_info->wsize ? volume_info->wsize : SMB3_DEFAULT_IOSIZE;
|
||||
wsize = ctx->wsize ? ctx->wsize : SMB3_DEFAULT_IOSIZE;
|
||||
wsize = min_t(unsigned int, wsize, server->max_write);
|
||||
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||
if (server->rdma) {
|
||||
@@ -386,13 +413,13 @@ smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
|
||||
{
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
unsigned int rsize;
|
||||
|
||||
/* start with specified rsize, or default */
|
||||
rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE;
|
||||
rsize = ctx->rsize ? ctx->rsize : CIFS_DEFAULT_IOSIZE;
|
||||
rsize = min_t(unsigned int, rsize, server->max_read);
|
||||
|
||||
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
|
||||
@@ -402,13 +429,13 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
||||
smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
|
||||
{
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
unsigned int rsize;
|
||||
|
||||
/* start with specified rsize, or default */
|
||||
rsize = volume_info->rsize ? volume_info->rsize : SMB3_DEFAULT_IOSIZE;
|
||||
rsize = ctx->rsize ? ctx->rsize : SMB3_DEFAULT_IOSIZE;
|
||||
rsize = min_t(unsigned int, rsize, server->max_read);
|
||||
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||
if (server->rdma) {
|
||||
@@ -477,7 +504,8 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bytes_left || p->Next)
|
||||
/* Azure rounds the buffer size up 8, to a 16 byte boundary */
|
||||
if ((bytes_left > 8) || p->Next)
|
||||
cifs_dbg(VFS, "%s: incomplete interface info\n", __func__);
|
||||
|
||||
|
||||
@@ -2341,6 +2369,7 @@ static bool
|
||||
smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
|
||||
{
|
||||
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
|
||||
int scredits;
|
||||
|
||||
if (shdr->Status != STATUS_PENDING)
|
||||
return false;
|
||||
@@ -2348,8 +2377,14 @@ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
|
||||
if (shdr->CreditRequest) {
|
||||
spin_lock(&server->req_lock);
|
||||
server->credits += le16_to_cpu(shdr->CreditRequest);
|
||||
scredits = server->credits;
|
||||
spin_unlock(&server->req_lock);
|
||||
wake_up(&server->request_q);
|
||||
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->hostname, scredits, le16_to_cpu(shdr->CreditRequest));
|
||||
cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n",
|
||||
__func__, le16_to_cpu(shdr->CreditRequest), scredits);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -3949,7 +3984,7 @@ smb3_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key)
|
||||
static unsigned int
|
||||
smb2_wp_retry_size(struct inode *inode)
|
||||
{
|
||||
return min_t(unsigned int, CIFS_SB(inode->i_sb)->wsize,
|
||||
return min_t(unsigned int, CIFS_SB(inode->i_sb)->ctx->wsize,
|
||||
SMB2_MAX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
|
||||
@@ -427,8 +427,8 @@ build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt)
|
||||
pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES;
|
||||
pneg_ctxt->DataLength = cpu_to_le16(38);
|
||||
pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1);
|
||||
pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE);
|
||||
get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE);
|
||||
pneg_ctxt->SaltLength = cpu_to_le16(SMB311_LINUX_CLIENT_SALT_SIZE);
|
||||
get_random_bytes(pneg_ctxt->Salt, SMB311_LINUX_CLIENT_SALT_SIZE);
|
||||
pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512;
|
||||
}
|
||||
|
||||
@@ -566,6 +566,9 @@ static void decode_preauth_context(struct smb2_preauth_neg_context *ctxt)
|
||||
if (len < MIN_PREAUTH_CTXT_DATA_LEN) {
|
||||
pr_warn_once("server sent bad preauth context\n");
|
||||
return;
|
||||
} else if (len < MIN_PREAUTH_CTXT_DATA_LEN + le16_to_cpu(ctxt->SaltLength)) {
|
||||
pr_warn_once("server sent invalid SaltLength\n");
|
||||
return;
|
||||
}
|
||||
if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1)
|
||||
pr_warn_once("Invalid SMB3 hash algorithm count\n");
|
||||
|
||||
@@ -333,12 +333,20 @@ struct smb2_neg_context {
|
||||
/* Followed by array of data */
|
||||
} __packed;
|
||||
|
||||
#define SMB311_SALT_SIZE 32
|
||||
#define SMB311_LINUX_CLIENT_SALT_SIZE 32
|
||||
/* Hash Algorithm Types */
|
||||
#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001)
|
||||
#define SMB2_PREAUTH_HASH_SIZE 64
|
||||
|
||||
#define MIN_PREAUTH_CTXT_DATA_LEN (SMB311_SALT_SIZE + 6)
|
||||
/*
|
||||
* SaltLength that the server send can be zero, so the only three required
|
||||
* fields (all __le16) end up six bytes total, so the minimum context data len
|
||||
* in the response is six bytes which accounts for
|
||||
*
|
||||
* HashAlgorithmCount, SaltLength, and 1 HashAlgorithm.
|
||||
*/
|
||||
#define MIN_PREAUTH_CTXT_DATA_LEN 6
|
||||
|
||||
struct smb2_preauth_neg_context {
|
||||
__le16 ContextType; /* 1 */
|
||||
__le16 DataLength;
|
||||
@@ -346,7 +354,7 @@ struct smb2_preauth_neg_context {
|
||||
__le16 HashAlgorithmCount; /* 1 */
|
||||
__le16 SaltLength;
|
||||
__le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */
|
||||
__u8 Salt[SMB311_SALT_SIZE];
|
||||
__u8 Salt[SMB311_LINUX_CLIENT_SALT_SIZE];
|
||||
} __packed;
|
||||
|
||||
/* Encryption Algorithms Ciphers */
|
||||
|
||||
@@ -246,6 +246,7 @@ smbd_qp_async_error_upcall(struct ib_event *event, void *context)
|
||||
case IB_EVENT_CQ_ERR:
|
||||
case IB_EVENT_QP_FATAL:
|
||||
smbd_disconnect_rdma_connection(info);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -909,8 +909,12 @@ DEFINE_EVENT(smb3_credit_class, smb3_##name, \
|
||||
TP_ARGS(currmid, hostname, credits, credits_to_add))
|
||||
|
||||
DEFINE_SMB3_CREDIT_EVENT(reconnect_with_invalid_credits);
|
||||
DEFINE_SMB3_CREDIT_EVENT(reconnect_detected);
|
||||
DEFINE_SMB3_CREDIT_EVENT(credit_timeout);
|
||||
DEFINE_SMB3_CREDIT_EVENT(insufficient_credits);
|
||||
DEFINE_SMB3_CREDIT_EVENT(too_many_credits);
|
||||
DEFINE_SMB3_CREDIT_EVENT(add_credits);
|
||||
DEFINE_SMB3_CREDIT_EVENT(set_credits);
|
||||
|
||||
#endif /* _CIFS_TRACE_H */
|
||||
|
||||
|
||||
@@ -527,6 +527,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
|
||||
int *credits;
|
||||
int optype;
|
||||
long int t;
|
||||
int scredits = server->credits;
|
||||
|
||||
if (timeout < 0)
|
||||
t = MAX_JIFFY_OFFSET;
|
||||
@@ -624,12 +625,18 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
|
||||
/* update # of requests on the wire to server */
|
||||
if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) {
|
||||
*credits -= num_credits;
|
||||
scredits = *credits;
|
||||
server->in_flight += num_credits;
|
||||
if (server->in_flight > server->max_in_flight)
|
||||
server->max_in_flight = server->in_flight;
|
||||
*instance = server->reconnect_instance;
|
||||
}
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->hostname, scredits, -(num_credits));
|
||||
cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
|
||||
__func__, num_credits, scredits);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -649,10 +656,14 @@ wait_for_compound_request(struct TCP_Server_Info *server, int num,
|
||||
const int flags, unsigned int *instance)
|
||||
{
|
||||
int *credits;
|
||||
int scredits, sin_flight;
|
||||
|
||||
credits = server->ops->get_credits_field(server, flags & CIFS_OP_MASK);
|
||||
|
||||
spin_lock(&server->req_lock);
|
||||
scredits = *credits;
|
||||
sin_flight = server->in_flight;
|
||||
|
||||
if (*credits < num) {
|
||||
/*
|
||||
* Return immediately if not too many requests in flight since
|
||||
@@ -660,6 +671,10 @@ wait_for_compound_request(struct TCP_Server_Info *server, int num,
|
||||
*/
|
||||
if (server->in_flight < num - *credits) {
|
||||
spin_unlock(&server->req_lock);
|
||||
trace_smb3_insufficient_credits(server->CurrentMid,
|
||||
server->hostname, scredits, sin_flight);
|
||||
cifs_dbg(FYI, "%s: %d requests in flight, needed %d total=%d\n",
|
||||
__func__, sin_flight, num, scredits);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
71
fs/cifs/unc.c
Normal file
71
fs/cifs/unc.c
Normal file
@@ -0,0 +1,71 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2020, Microsoft Corporation.
|
||||
*
|
||||
* Author(s): Steve French <stfrench@microsoft.com>
|
||||
* Suresh Jayaraman <sjayaraman@suse.de>
|
||||
* Jeff Layton <jlayton@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/ctype.h>
|
||||
#include "cifsglob.h"
|
||||
#include "cifsproto.h"
|
||||
|
||||
/* extract the host portion of the UNC string */
|
||||
char *extract_hostname(const char *unc)
|
||||
{
|
||||
const char *src;
|
||||
char *dst, *delim;
|
||||
unsigned int len;
|
||||
|
||||
/* skip double chars at beginning of string */
|
||||
/* BB: check validity of these bytes? */
|
||||
if (strlen(unc) < 3)
|
||||
return ERR_PTR(-EINVAL);
|
||||
for (src = unc; *src && *src == '\\'; src++)
|
||||
;
|
||||
if (!*src)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* delimiter between hostname and sharename is always '\\' now */
|
||||
delim = strchr(src, '\\');
|
||||
if (!delim)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
len = delim - src;
|
||||
dst = kmalloc((len + 1), GFP_KERNEL);
|
||||
if (dst == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(dst, src, len);
|
||||
dst[len] = '\0';
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
char *extract_sharename(const char *unc)
|
||||
{
|
||||
const char *src;
|
||||
char *delim, *dst;
|
||||
int len;
|
||||
|
||||
/* skip double chars at the beginning */
|
||||
src = unc + 2;
|
||||
|
||||
/* share name is always preceded by '\\' now */
|
||||
delim = strchr(src, '\\');
|
||||
if (!delim)
|
||||
return ERR_PTR(-EINVAL);
|
||||
delim++;
|
||||
len = strlen(delim);
|
||||
|
||||
/* caller has to free the memory */
|
||||
dst = kstrndup(delim, len, GFP_KERNEL);
|
||||
if (!dst)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
return dst;
|
||||
}
|
||||
63
include/uapi/linux/cifs/cifs_netlink.h
Normal file
63
include/uapi/linux/cifs/cifs_netlink.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ WITH Linux-syscall-note */
|
||||
/*
|
||||
* Netlink routines for CIFS
|
||||
*
|
||||
* Copyright (c) 2020 Samuel Cabrero <scabrero@suse.de>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _UAPILINUX_CIFS_NETLINK_H
|
||||
#define _UAPILINUX_CIFS_NETLINK_H
|
||||
|
||||
#define CIFS_GENL_NAME "cifs"
|
||||
#define CIFS_GENL_VERSION 0x1
|
||||
|
||||
#define CIFS_GENL_MCGRP_SWN_NAME "cifs_mcgrp_swn"
|
||||
|
||||
enum cifs_genl_multicast_groups {
|
||||
CIFS_GENL_MCGRP_SWN,
|
||||
};
|
||||
|
||||
enum cifs_genl_attributes {
|
||||
CIFS_GENL_ATTR_UNSPEC,
|
||||
CIFS_GENL_ATTR_SWN_REGISTRATION_ID,
|
||||
CIFS_GENL_ATTR_SWN_NET_NAME,
|
||||
CIFS_GENL_ATTR_SWN_SHARE_NAME,
|
||||
CIFS_GENL_ATTR_SWN_IP,
|
||||
CIFS_GENL_ATTR_SWN_NET_NAME_NOTIFY,
|
||||
CIFS_GENL_ATTR_SWN_SHARE_NAME_NOTIFY,
|
||||
CIFS_GENL_ATTR_SWN_IP_NOTIFY,
|
||||
CIFS_GENL_ATTR_SWN_KRB_AUTH,
|
||||
CIFS_GENL_ATTR_SWN_USER_NAME,
|
||||
CIFS_GENL_ATTR_SWN_PASSWORD,
|
||||
CIFS_GENL_ATTR_SWN_DOMAIN_NAME,
|
||||
CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE,
|
||||
CIFS_GENL_ATTR_SWN_RESOURCE_STATE,
|
||||
CIFS_GENL_ATTR_SWN_RESOURCE_NAME,
|
||||
__CIFS_GENL_ATTR_MAX,
|
||||
};
|
||||
#define CIFS_GENL_ATTR_MAX (__CIFS_GENL_ATTR_MAX - 1)
|
||||
|
||||
enum cifs_genl_commands {
|
||||
CIFS_GENL_CMD_UNSPEC,
|
||||
CIFS_GENL_CMD_SWN_REGISTER,
|
||||
CIFS_GENL_CMD_SWN_UNREGISTER,
|
||||
CIFS_GENL_CMD_SWN_NOTIFY,
|
||||
__CIFS_GENL_CMD_MAX
|
||||
};
|
||||
#define CIFS_GENL_CMD_MAX (__CIFS_GENL_CMD_MAX - 1)
|
||||
|
||||
enum cifs_swn_notification_type {
|
||||
CIFS_SWN_NOTIFICATION_RESOURCE_CHANGE = 0x01,
|
||||
CIFS_SWN_NOTIFICATION_CLIENT_MOVE = 0x02,
|
||||
CIFS_SWN_NOTIFICATION_SHARE_MOVE = 0x03,
|
||||
CIFS_SWN_NOTIFICATION_IP_CHANGE = 0x04,
|
||||
};
|
||||
|
||||
enum cifs_swn_resource_state {
|
||||
CIFS_SWN_RESOURCE_STATE_UNKNOWN = 0x00,
|
||||
CIFS_SWN_RESOURCE_STATE_AVAILABLE = 0x01,
|
||||
CIFS_SWN_RESOURCE_STATE_UNAVAILABLE = 0xFF
|
||||
};
|
||||
|
||||
#endif /* _UAPILINUX_CIFS_NETLINK_H */
|
||||
Reference in New Issue
Block a user