1
0
Fork 0

cifs/smb3 changes, the largest part are for support of the newer mount API, also includes addition of support for the SMB3 witness protocol which can provide important notifications from the server on address or export or network changes, and three patches for stable

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAl/bxgoACgkQiiy9cAdy
 T1HrVwv9E3lSCo8xvipqU4vto+I/29zVa7yjf2oY06LyHkgXEBzPYjhWtLN3KyF+
 H6gnFUusyw4prteeFXUYatXYBhT3tK1fxeshUT7B+zk9eFGQca8WYockjEIiDci3
 WcieEHH1xvDuwyPAomjpJFEQ70h+vXBREvtLZOAPxisKID+gKjncP0jncAFeFvhM
 Cln/s88YmG7vJZ78S1yIJqiD0PvUJOdYLrDz/Zmwh+m5UIs9N9clB1MVuLw+5vMe
 E5vG6Aeh3hCvwyVmGxTx4IEixV9hFR20XBvxTSnhyGQ4s+7DfcHmDDcmPHEnTcZ8
 7U2Y1xUslx6nvoej9hlZDB1bEyMPNlI6GIYjDb6RiU18D3crgygat4SmgbPUV1up
 P6hqIy1NebskZjLEpVDIDNV7JWqucHzfJYfPW9B/LwiH72KMNhwQhZ9H1PqkzS7q
 WCeVKdc5vGhLyHEZJjHu3qOhCQQQ3cDh4akL5gWiYmwNDGQZjpVfhE4tzcMYWtSn
 WkxqLVA3
 =YiLC
 -----END PGP SIGNATURE-----

Merge tag '5.11-rc-smb3' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs updates from Steve French:
 "The largest part are for support of the newer mount API which has been
  needed for cifs/smb3 mounts for a long time due to the new API's
  better handling of remount, and better error reporting. There are
  three additional small cleanup patches for this being tested, that are
  not included yet.

  This series also includes addition of support for the SMB3 witness
  protocol which can provide important notifications from the server to
  client on server address or export or network changes. This can be
  useful for example in order to be notified before the failure - when a
  server's IP address changes (in the future it will allow us to support
  server notifications of when a share is moved).

  It also includes three patches for stable e.g. some that better handle
  some confusing error messages during session establishment"

* tag '5.11-rc-smb3' of git://git.samba.org/sfrench/cifs-2.6: (55 commits)
  cifs: update internal module version number
  cifs: Fix support for remount when not changing rsize/wsize
  cifs: handle "guest" mount parameter
  cifs: correct four aliased mount parms to allow use of previous names
  cifs: Tracepoints and logs for tracing credit changes.
  cifs: fix use after free in cifs_smb3_do_mount()
  cifs: fix rsize/wsize to be negotiated values
  cifs: Fix some error pointers handling detected by static checker
  smb3: remind users that witness protocol is experimental
  cifs: update super_operations to show_devname
  cifs: fix uninitialized variable in smb3_fs_context_parse_param
  cifs: update mnt_cifs_flags during reconfigure
  cifs: move update of flags into a separate function
  cifs: remove ctx argument from cifs_setup_cifs_sb
  cifs: do not allow changing posix_paths during remount
  cifs: uncomplicate printing the iocharset parameter
  cifs: don't create a temp nls in cifs_setup_ipc
  cifs: simplify handling of cifs_sb/ctx->local_nls
  cifs: we do not allow changing username/password/unc/... during remount
  cifs: add initial reconfigure support
  ...
zero-sugar-mainline-defconfig
Linus Torvalds 2020-12-17 17:41:37 -08:00
commit e13300bdaa
41 changed files with 3621 additions and 2352 deletions

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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;
}

View File

@ -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;

View File

@ -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 100644
View 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(&reg->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(&reg->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 100644
View 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 */

View File

@ -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);

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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 */

View File

@ -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
};
/*

View File

@ -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)

View File

@ -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,

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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 100644
View 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 100644
View 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 */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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)

View File

@ -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);
}

View File

@ -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");

View File

@ -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 */

View File

@ -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;

View File

@ -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 */

View File

@ -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 100644
View 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;
}

View 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 */