mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 12:44:11 +08:00
cifs/smb3 fixes including improvements to mode bit conversion when using cifsacl mount option, new mount options for controlling attribute caching, improvements to crediting and reconnect, improved debugging
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmA4bocACgkQiiy9cAdy T1HuMwv/bmZ53ilDwhCph/UKoLJm/bRyvCp+GBECtLLS/C/4qz5IBLpPPr2yhOyH gmkeCZZWhj0nzGAYxhVDAdBz9IPEA7bae503IaOuk5uXaCXC8htsq/Cd7qpJmHlf 5vCdBTmHBiUt02dUcZ9A3bm855xJLEINHH9YdJM157ysqLgttIibLQB0F/gJ49DR QIIdq7sZNJXcTgRsUzJZNnrWLDi2oVIoUlq5M6d8ypmZC0ArPNfrSafjW6h5rqpj UYBwtUDNwQiS0lgwR4mji4PCen0GGwMFtyVDOpdJLJq3fO995yse2BRk0BFHVH1i xfAskQjkxAHEcfzQC1cM4ouT/WYu8nHaLK1vp/1lVr93mo8KqSX+SW/bLsXfpQkm w//xMy94HdM2pgyM6J1pYnKPb7s/DG19RYPktQ5oYn0fR5qYlqALAmd02JRjO3xV cbjbmWXXzFFsFJc5MJmM6wVLJzRb4a50SN1W37aHNXWi8ktpsaNzz33LGw1pF2OT K6P2DqJe =RQo/ -----END PGP SIGNATURE----- Merge tag '5.12-smb3-part1' of git://git.samba.org/sfrench/cifs-2.6 Pull cifs updates from Steve French: - improvements to mode bit conversion, chmod and chown when using cifsacl mount option - two new mount options for controlling attribute caching - improvements to crediting and reconnect, improved debugging - reconnect fix - add SMB3.1.1 dialect to default dialects for vers=3 * tag '5.12-smb3-part1' of git://git.samba.org/sfrench/cifs-2.6: (27 commits) cifs: update internal version number cifs: use discard iterator to discard unneeded network data more efficiently cifs: introduce helper for finding referral server to improve DFS target resolution cifs: check all path components in resolved dfs target cifs: fix DFS failover cifs: fix nodfs mount option cifs: fix handling of escaped ',' in the password mount argument cifs: Add new parameter "acregmax" for distinct file and directory metadata timeout cifs: convert revalidate of directories to using directory metadata cache timeout cifs: Add new mount parameter "acdirmax" to allow caching directory metadata cifs: If a corrupted DACL is returned by the server, bail out. cifs: minor simplification to smb2_is_network_name_deleted TCON Reconnect during STATUS_NETWORK_NAME_DELETED cifs: cleanup a few le16 vs. le32 uses in cifsacl.c cifs: Change SIDs in ACEs while transferring file ownership. cifs: Retain old ACEs when converting between mode bits and ACL. cifs: Fix cifsacl ACE mask for group and others. cifs: clarify hostname vs ip address in /proc/fs/cifs/DebugData cifs: change confusing field serverName (to ip_addr) cifs: Fix inconsistent IS_ERR and PTR_ERR ...
This commit is contained in:
commit
c19798af2e
@ -5,10 +5,10 @@ Authors
|
||||
Original Author
|
||||
---------------
|
||||
|
||||
Steve French (sfrench@samba.org)
|
||||
Steve French (smfrench@gmail.com, sfrench@samba.org)
|
||||
|
||||
The author wishes to express his appreciation and thanks to:
|
||||
Andrew Tridgell (Samba team) for his early suggestions about smb/cifs VFS
|
||||
Andrew Tridgell (Samba team) for his early suggestions about SMB/CIFS VFS
|
||||
improvements. Thanks to IBM for allowing me time and test resources to pursue
|
||||
this project, to Jim McDonough from IBM (and the Samba Team) for his help, to
|
||||
the IBM Linux JFS team for explaining many esoteric Linux filesystem features.
|
||||
@ -51,7 +51,7 @@ Patch Contributors
|
||||
- Ronnie Sahlberg (for SMB3 xattr work, bug fixes, and lots of great work on compounding)
|
||||
- Shirish Pargaonkar (for many ACL patches over the years)
|
||||
- Sachin Prabhu (many bug fixes, including for reconnect, copy offload and security)
|
||||
- Paulo Alcantara
|
||||
- Paulo Alcantara (for some excellent work in DFS, and in booting from SMB3)
|
||||
- Long Li (some great work on RDMA, SMB Direct)
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@ Changes
|
||||
=======
|
||||
|
||||
See https://wiki.samba.org/index.php/LinuxCIFSKernel for summary
|
||||
information (that may be easier to read than parsing the output of
|
||||
"git log fs/cifs") about fixes/improvements to CIFS/SMB2/SMB3 support (changes
|
||||
information about fixes/improvements to CIFS/SMB2/SMB3 support (changes
|
||||
to cifs.ko module) by kernel version (and cifs internal module version).
|
||||
This may be easier to read than parsing the output of "git log fs/cifs"
|
||||
by release.
|
||||
|
@ -7,19 +7,19 @@ Introduction
|
||||
protocol which was the successor to the Server Message Block
|
||||
(SMB) protocol, the native file sharing mechanism for most early
|
||||
PC operating systems. New and improved versions of CIFS are now
|
||||
called SMB2 and SMB3. Use of SMB3 (and later, including SMB3.1.1)
|
||||
is strongly preferred over using older dialects like CIFS due to
|
||||
security reasons. All modern dialects, including the most recent,
|
||||
SMB3.1.1 are supported by the CIFS VFS module. The SMB3 protocol
|
||||
is implemented and supported by all major file servers
|
||||
such as all modern versions of Windows (including Windows 2016
|
||||
Server), as well as by Samba (which provides excellent
|
||||
CIFS/SMB2/SMB3 server support and tools for Linux and many other
|
||||
operating systems). Apple systems also support SMB3 well, as
|
||||
do most Network Attached Storage vendors, so this network
|
||||
filesystem client can mount to a wide variety of systems.
|
||||
It also supports mounting to the cloud (for example
|
||||
Microsoft Azure), including the necessary security features.
|
||||
called SMB2 and SMB3. Use of SMB3 (and later, including SMB3.1.1
|
||||
the most current dialect) is strongly preferred over using older
|
||||
dialects like CIFS due to security reasons. All modern dialects,
|
||||
including the most recent, SMB3.1.1, are supported by the CIFS VFS
|
||||
module. The SMB3 protocol is implemented and supported by all major
|
||||
file servers such as Windows (including Windows 2019 Server), as
|
||||
well as by Samba (which provides excellent CIFS/SMB2/SMB3 server
|
||||
support and tools for Linux and many other operating systems).
|
||||
Apple systems also support SMB3 well, as do most Network Attached
|
||||
Storage vendors, so this network filesystem client can mount to a
|
||||
wide variety of systems. It also supports mounting to the cloud
|
||||
(for example Microsoft Azure), including the necessary security
|
||||
features.
|
||||
|
||||
The intent of this module is to provide the most advanced network
|
||||
file system function for SMB3 compliant servers, including advanced
|
||||
@ -27,8 +27,8 @@ Introduction
|
||||
POSIX compliance, secure per-user session establishment, encryption,
|
||||
high performance safe distributed caching (leases/oplocks), optional packet
|
||||
signing, large files, Unicode support and other internationalization
|
||||
improvements. Since both Samba server and this filesystem client support
|
||||
the CIFS Unix extensions (and in the future SMB3 POSIX extensions),
|
||||
improvements. Since both Samba server and this filesystem client support the
|
||||
CIFS Unix extensions, and the Linux client also suppors SMB3 POSIX extensions,
|
||||
the combination can provide a reasonable alternative to other network and
|
||||
cluster file systems for fileserving in some Linux to Linux environments,
|
||||
not just in Linux to Windows (or Linux to Mac) environments.
|
||||
|
@ -13,24 +13,26 @@ is a partial list of the known problems and missing features:
|
||||
|
||||
a) SMB3 (and SMB3.1.1) missing optional features:
|
||||
|
||||
- multichannel (started), integration with RDMA
|
||||
- directory leases (improved metadata caching), started (root dir only)
|
||||
- multichannel (partially integrated), integration of multichannel with RDMA
|
||||
- directory leases (improved metadata caching). Currently only implemented for root dir
|
||||
- T10 copy offload ie "ODX" (copy chunk, and "Duplicate Extents" ioctl
|
||||
currently the only two server side copy mechanisms supported)
|
||||
|
||||
b) improved sparse file support (fiemap and SEEK_HOLE are implemented
|
||||
but additional features would be supportable by the protocol).
|
||||
but additional features would be supportable by the protocol such
|
||||
as FALLOC_FL_COLLAPSE_RANGE and FALLOC_FL_INSERT_RANGE)
|
||||
|
||||
c) Directory entry caching relies on a 1 second timer, rather than
|
||||
using Directory Leases, currently only the root file handle is cached longer
|
||||
by leveraging Directory Leases
|
||||
|
||||
d) quota support (needs minor kernel change since quota calls
|
||||
to make it to network filesystems or deviceless filesystems)
|
||||
d) quota support (needs minor kernel change since quota calls otherwise
|
||||
won't make it to network filesystems or deviceless filesystems).
|
||||
|
||||
e) Additional use cases can be optimized to use "compounding" (e.g.
|
||||
open/query/close and open/setinfo/close) to reduce the number of
|
||||
roundtrips to the server and improve performance. Various cases
|
||||
(stat, statfs, create, unlink, mkdir) already have been improved by
|
||||
(stat, statfs, create, unlink, mkdir, xattrs) already have been improved by
|
||||
using compounding but more can be done. In addition we could
|
||||
significantly reduce redundant opens by using deferred close (with
|
||||
handle caching leases) and better using reference counters on file
|
||||
@ -60,7 +62,9 @@ k) Add tools to take advantage of more smb3 specific ioctls and features
|
||||
metadata attributes easier from tools (e.g. extending what was done
|
||||
in smb-info tool).
|
||||
|
||||
l) encrypted file support
|
||||
l) encrypted file support (currently the attribute showing the file is
|
||||
encrypted on the server is reported, but changing the attribute is not
|
||||
supported).
|
||||
|
||||
m) improved stats gathering tools (perhaps integration with nfsometer?)
|
||||
to extend and make easier to use what is currently in /proc/fs/cifs/Stats
|
||||
@ -69,14 +73,13 @@ n) Add support for claims based ACLs ("DAC")
|
||||
|
||||
o) mount helper GUI (to simplify the various configuration options on mount)
|
||||
|
||||
p) Add support for witness protocol (perhaps ioctl to cifs.ko from user space
|
||||
tool listening on witness protocol RPC) to allow for notification of share
|
||||
move, server failover, and server adapter changes. And also improve other
|
||||
failover scenarios, e.g. when client knows multiple DFS entries point to
|
||||
different servers, and the server we are connected to has gone down.
|
||||
p) Expand support for witness protocol to allow for notification of share
|
||||
move, and server network adapter changes. Currently only notifications by
|
||||
the witness protocol for server move is supported by the Linux client.
|
||||
|
||||
q) Allow mount.cifs to be more verbose in reporting errors with dialect
|
||||
or unsupported feature errors.
|
||||
or unsupported feature errors. This would now be easier due to the
|
||||
implementation of the new mount API.
|
||||
|
||||
r) updating cifs documentation, and user guide.
|
||||
|
||||
@ -87,11 +90,10 @@ t) split cifs and smb3 support into separate modules so legacy (and less
|
||||
secure) CIFS dialect can be disabled in environments that don't need it
|
||||
and simplify the code.
|
||||
|
||||
v) POSIX Extensions for SMB3.1.1 (started, create and mkdir support added
|
||||
so far).
|
||||
v) Additional testing of POSIX Extensions for SMB3.1.1
|
||||
|
||||
w) Add support for additional strong encryption types, and additional spnego
|
||||
authentication mechanisms (see MS-SMB2)
|
||||
authentication mechanisms (see MS-SMB2). GCM-256 is now partially implemented.
|
||||
|
||||
x) Finish support for SMB3.1.1 compression
|
||||
|
||||
|
@ -83,7 +83,7 @@ and encrypted shares and stronger signing and authentication algorithms.
|
||||
There are additional mount options that may be helpful for SMB3 to get
|
||||
improved POSIX behavior (NB: can use vers=3.0 to force only SMB3, never 2.1):
|
||||
|
||||
``mfsymlinks`` and ``cifsacl`` and ``idsfromsid``
|
||||
``mfsymlinks`` and either ``cifsacl`` or ``modefromsid`` (usually with ``idsfromsid``)
|
||||
|
||||
Allowing User Mounts
|
||||
====================
|
||||
|
@ -133,11 +133,12 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
|
||||
{
|
||||
struct TCP_Server_Info *server = chan->server;
|
||||
|
||||
seq_printf(m, "\t\tChannel %d Number of credits: %d Dialect 0x%x "
|
||||
"TCP status: %d Instance: %d Local Users To Server: %d "
|
||||
"SecMode: 0x%x Req On Wire: %d In Send: %d "
|
||||
"In MaxReq Wait: %d\n",
|
||||
i+1,
|
||||
seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx"
|
||||
"\n\t\tNumber of credits: %d Dialect 0x%x"
|
||||
"\n\t\tTCP status: %d Instance: %d"
|
||||
"\n\t\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d"
|
||||
"\n\t\tIn Send: %d In MaxReq Wait: %d",
|
||||
i+1, server->conn_id,
|
||||
server->credits,
|
||||
server->dialect,
|
||||
server->tcpStatus,
|
||||
@ -227,7 +228,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
||||
struct TCP_Server_Info *server;
|
||||
struct cifs_ses *ses;
|
||||
struct cifs_tcon *tcon;
|
||||
int i, j;
|
||||
int c, i, j;
|
||||
|
||||
seq_puts(m,
|
||||
"Display Internal CIFS Data Structures for Debugging\n"
|
||||
@ -275,14 +276,25 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
||||
seq_putc(m, '\n');
|
||||
seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize);
|
||||
seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid);
|
||||
seq_printf(m, "Servers:");
|
||||
|
||||
i = 0;
|
||||
seq_printf(m, "\nServers: ");
|
||||
|
||||
c = 0;
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each(tmp1, &cifs_tcp_ses_list) {
|
||||
server = list_entry(tmp1, struct TCP_Server_Info,
|
||||
tcp_ses_list);
|
||||
|
||||
/* channel info will be printed as a part of sessions below */
|
||||
if (server->is_channel)
|
||||
continue;
|
||||
|
||||
c++;
|
||||
seq_printf(m, "\n%d) ConnectionId: 0x%llx ",
|
||||
c, server->conn_id);
|
||||
|
||||
if (server->hostname)
|
||||
seq_printf(m, "Hostname: %s ", server->hostname);
|
||||
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||
if (!server->rdma)
|
||||
goto skip_rdma;
|
||||
@ -362,46 +374,48 @@ skip_rdma:
|
||||
if (server->posix_ext_supported)
|
||||
seq_printf(m, " posix");
|
||||
|
||||
i++;
|
||||
if (server->rdma)
|
||||
seq_printf(m, "\nRDMA ");
|
||||
seq_printf(m, "\nTCP status: %d Instance: %d"
|
||||
"\nLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d",
|
||||
server->tcpStatus,
|
||||
server->reconnect_instance,
|
||||
server->srv_count,
|
||||
server->sec_mode, in_flight(server));
|
||||
|
||||
seq_printf(m, "\nIn Send: %d In MaxReq Wait: %d",
|
||||
atomic_read(&server->in_send),
|
||||
atomic_read(&server->num_waiters));
|
||||
|
||||
seq_printf(m, "\n\n\tSessions: ");
|
||||
i = 0;
|
||||
list_for_each(tmp2, &server->smb_ses_list) {
|
||||
ses = list_entry(tmp2, struct cifs_ses,
|
||||
smb_ses_list);
|
||||
i++;
|
||||
if ((ses->serverDomain == NULL) ||
|
||||
(ses->serverOS == NULL) ||
|
||||
(ses->serverNOS == NULL)) {
|
||||
seq_printf(m, "\n%d) Name: %s Uses: %d Capability: 0x%x\tSession Status: %d ",
|
||||
i, ses->serverName, ses->ses_count,
|
||||
seq_printf(m, "\n\t%d) Address: %s Uses: %d Capability: 0x%x\tSession Status: %d ",
|
||||
i, ses->ip_addr, ses->ses_count,
|
||||
ses->capabilities, ses->status);
|
||||
if (ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST)
|
||||
seq_printf(m, "Guest\t");
|
||||
seq_printf(m, "Guest ");
|
||||
else if (ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
|
||||
seq_printf(m, "Anonymous\t");
|
||||
seq_printf(m, "Anonymous ");
|
||||
} else {
|
||||
seq_printf(m,
|
||||
"\n%d) Name: %s Domain: %s Uses: %d OS:"
|
||||
" %s\n\tNOS: %s\tCapability: 0x%x\n\tSMB"
|
||||
" session status: %d ",
|
||||
i, ses->serverName, ses->serverDomain,
|
||||
"\n\t%d) Name: %s Domain: %s Uses: %d OS: %s "
|
||||
"\n\tNOS: %s\tCapability: 0x%x"
|
||||
"\n\tSMB session status: %d ",
|
||||
i, ses->ip_addr, ses->serverDomain,
|
||||
ses->ses_count, ses->serverOS, ses->serverNOS,
|
||||
ses->capabilities, ses->status);
|
||||
}
|
||||
|
||||
seq_printf(m,"Security type: %s\n",
|
||||
seq_printf(m, "\n\tSecurity type: %s ",
|
||||
get_security_type_str(server->ops->select_sectype(server, ses->sectype)));
|
||||
|
||||
if (server->rdma)
|
||||
seq_printf(m, "RDMA\n\t");
|
||||
seq_printf(m, "TCP status: %d Instance: %d\n\tLocal Users To "
|
||||
"Server: %d SecMode: 0x%x Req On Wire: %d",
|
||||
server->tcpStatus,
|
||||
server->reconnect_instance,
|
||||
server->srv_count,
|
||||
server->sec_mode, in_flight(server));
|
||||
|
||||
seq_printf(m, " In Send: %d In MaxReq Wait: %d",
|
||||
atomic_read(&server->in_send),
|
||||
atomic_read(&server->num_waiters));
|
||||
|
||||
/* dump session id helpful for use with network trace */
|
||||
seq_printf(m, " SessionId: 0x%llx", ses->Suid);
|
||||
if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
|
||||
@ -414,13 +428,13 @@ skip_rdma:
|
||||
from_kuid(&init_user_ns, ses->cred_uid));
|
||||
|
||||
if (ses->chan_count > 1) {
|
||||
seq_printf(m, "\n\n\tExtra Channels: %zu\n",
|
||||
seq_printf(m, "\n\n\tExtra Channels: %zu ",
|
||||
ses->chan_count-1);
|
||||
for (j = 1; j < ses->chan_count; j++)
|
||||
cifs_dump_channel(m, j, &ses->chans[j]);
|
||||
}
|
||||
|
||||
seq_puts(m, "\n\n\tShares:");
|
||||
seq_puts(m, "\n\n\tShares: ");
|
||||
j = 0;
|
||||
|
||||
seq_printf(m, "\n\t%d) IPC: ", j);
|
||||
@ -437,38 +451,43 @@ skip_rdma:
|
||||
cifs_debug_tcon(m, tcon);
|
||||
}
|
||||
|
||||
seq_puts(m, "\n\tMIDs:\n");
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
list_for_each(tmp3, &server->pending_mid_q) {
|
||||
mid_entry = list_entry(tmp3, struct mid_q_entry,
|
||||
qhead);
|
||||
seq_printf(m, "\tState: %d com: %d pid:"
|
||||
" %d cbdata: %p mid %llu\n",
|
||||
mid_entry->mid_state,
|
||||
le16_to_cpu(mid_entry->command),
|
||||
mid_entry->pid,
|
||||
mid_entry->callback_data,
|
||||
mid_entry->mid);
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
|
||||
spin_lock(&ses->iface_lock);
|
||||
if (ses->iface_count)
|
||||
seq_printf(m, "\n\tServer interfaces: %zu\n",
|
||||
seq_printf(m, "\n\n\tServer interfaces: %zu",
|
||||
ses->iface_count);
|
||||
for (j = 0; j < ses->iface_count; j++) {
|
||||
struct cifs_server_iface *iface;
|
||||
|
||||
iface = &ses->iface_list[j];
|
||||
seq_printf(m, "\t%d)", j);
|
||||
seq_printf(m, "\n\t%d)", j+1);
|
||||
cifs_dump_iface(m, iface);
|
||||
if (is_ses_using_iface(ses, iface))
|
||||
seq_puts(m, "\t\t[CONNECTED]\n");
|
||||
}
|
||||
spin_unlock(&ses->iface_lock);
|
||||
}
|
||||
if (i == 0)
|
||||
seq_printf(m, "\n\t\t[NONE]");
|
||||
|
||||
seq_puts(m, "\n\n\tMIDs: ");
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
list_for_each(tmp3, &server->pending_mid_q) {
|
||||
mid_entry = list_entry(tmp3, struct mid_q_entry,
|
||||
qhead);
|
||||
seq_printf(m, "\n\tState: %d com: %d pid:"
|
||||
" %d cbdata: %p mid %llu\n",
|
||||
mid_entry->mid_state,
|
||||
le16_to_cpu(mid_entry->command),
|
||||
mid_entry->pid,
|
||||
mid_entry->callback_data,
|
||||
mid_entry->mid);
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
seq_printf(m, "\n--\n");
|
||||
}
|
||||
if (c == 0)
|
||||
seq_printf(m, "\n\t[NONE]");
|
||||
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
seq_putc(m, '\n');
|
||||
|
||||
|
@ -272,7 +272,7 @@ static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon)
|
||||
if (IS_ERR(share_name)) {
|
||||
int ret;
|
||||
|
||||
ret = PTR_ERR(net_name);
|
||||
ret = PTR_ERR(share_name);
|
||||
cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n",
|
||||
__func__, tcon->treeName, ret);
|
||||
kfree(net_name);
|
||||
|
@ -267,10 +267,11 @@ is_well_known_sid(const struct cifs_sid *psid, uint32_t *puid, bool is_group)
|
||||
return true; /* well known sid found, uid returned */
|
||||
}
|
||||
|
||||
static void
|
||||
static __u16
|
||||
cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
|
||||
{
|
||||
int i;
|
||||
__u16 size = 1 + 1 + 6;
|
||||
|
||||
dst->revision = src->revision;
|
||||
dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES);
|
||||
@ -278,6 +279,9 @@ cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
|
||||
dst->authority[i] = src->authority[i];
|
||||
for (i = 0; i < dst->num_subauth; ++i)
|
||||
dst->sub_auth[i] = src->sub_auth[i];
|
||||
size += (dst->num_subauth * 4);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -521,8 +525,11 @@ exit_cifs_idmap(void)
|
||||
}
|
||||
|
||||
/* copy ntsd, owner sid, and group sid from a security descriptor to another */
|
||||
static void copy_sec_desc(const struct cifs_ntsd *pntsd,
|
||||
struct cifs_ntsd *pnntsd, __u32 sidsoffset)
|
||||
static __u32 copy_sec_desc(const struct cifs_ntsd *pntsd,
|
||||
struct cifs_ntsd *pnntsd,
|
||||
__u32 sidsoffset,
|
||||
struct cifs_sid *pownersid,
|
||||
struct cifs_sid *pgrpsid)
|
||||
{
|
||||
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
|
||||
struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
|
||||
@ -536,19 +543,25 @@ static void copy_sec_desc(const struct cifs_ntsd *pntsd,
|
||||
pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid));
|
||||
|
||||
/* copy owner sid */
|
||||
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
||||
if (pownersid)
|
||||
owner_sid_ptr = pownersid;
|
||||
else
|
||||
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
||||
le32_to_cpu(pntsd->osidoffset));
|
||||
nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
|
||||
cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr);
|
||||
|
||||
/* copy group sid */
|
||||
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
||||
if (pgrpsid)
|
||||
group_sid_ptr = pgrpsid;
|
||||
else
|
||||
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
||||
le32_to_cpu(pntsd->gsidoffset));
|
||||
ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
|
||||
sizeof(struct cifs_sid));
|
||||
cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr);
|
||||
|
||||
return;
|
||||
return sidsoffset + (2 * sizeof(struct cifs_sid));
|
||||
}
|
||||
|
||||
|
||||
@ -663,6 +676,25 @@ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
|
||||
return;
|
||||
}
|
||||
|
||||
static __u16 cifs_copy_ace(struct cifs_ace *dst, struct cifs_ace *src, struct cifs_sid *psid)
|
||||
{
|
||||
__u16 size = 1 + 1 + 2 + 4;
|
||||
|
||||
dst->type = src->type;
|
||||
dst->flags = src->flags;
|
||||
dst->access_req = src->access_req;
|
||||
|
||||
/* Check if there's a replacement sid specified */
|
||||
if (psid)
|
||||
size += cifs_copy_sid(&dst->sid, psid);
|
||||
else
|
||||
size += cifs_copy_sid(&dst->sid, &src->sid);
|
||||
|
||||
dst->size = cpu_to_le16(size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static __u16 fill_ace_for_sid(struct cifs_ace *pntace,
|
||||
const struct cifs_sid *psid, __u64 nmode,
|
||||
umode_t bits, __u8 access_type,
|
||||
@ -907,29 +939,30 @@ unsigned int setup_special_user_owner_ACE(struct cifs_ace *pntace)
|
||||
return ace_size;
|
||||
}
|
||||
|
||||
static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
|
||||
struct cifs_sid *pgrpsid, __u64 *pnmode, bool modefromsid)
|
||||
static void populate_new_aces(char *nacl_base,
|
||||
struct cifs_sid *pownersid,
|
||||
struct cifs_sid *pgrpsid,
|
||||
__u64 *pnmode, u32 *pnum_aces, u16 *pnsize,
|
||||
bool modefromsid)
|
||||
{
|
||||
u16 size = 0;
|
||||
u32 num_aces = 0;
|
||||
struct cifs_acl *pnndacl;
|
||||
__u64 nmode;
|
||||
u32 num_aces = 0;
|
||||
u16 nsize = 0;
|
||||
__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));
|
||||
struct cifs_ace *pnntace = NULL;
|
||||
|
||||
nmode = *pnmode;
|
||||
num_aces = *pnum_aces;
|
||||
nsize = *pnsize;
|
||||
|
||||
if (modefromsid) {
|
||||
struct cifs_ace *pntace =
|
||||
(struct cifs_ace *)((char *)pnndacl + size);
|
||||
|
||||
size += setup_special_mode_ACE(pntace, nmode);
|
||||
pnntace = (struct cifs_ace *) (nacl_base + nsize);
|
||||
nsize += setup_special_mode_ACE(pnntace, nmode);
|
||||
num_aces++;
|
||||
goto set_size;
|
||||
}
|
||||
@ -966,40 +999,170 @@ static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
|
||||
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);
|
||||
pnntace = (struct cifs_ace *) (nacl_base + nsize);
|
||||
nsize += fill_ace_for_sid(pnntace, 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);
|
||||
pnntace = (struct cifs_ace *) (nacl_base + nsize);
|
||||
nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode,
|
||||
0070, ACCESS_DENIED, false);
|
||||
num_aces++;
|
||||
}
|
||||
size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
|
||||
pownersid, user_mode, 0700, ACCESS_ALLOWED, true);
|
||||
|
||||
pnntace = (struct cifs_ace *) (nacl_base + nsize);
|
||||
nsize += fill_ace_for_sid(pnntace, 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);
|
||||
pnntace = (struct cifs_ace *) (nacl_base + nsize);
|
||||
nsize += fill_ace_for_sid(pnntace, 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);
|
||||
|
||||
pnntace = (struct cifs_ace *) (nacl_base + nsize);
|
||||
nsize += fill_ace_for_sid(pnntace, pgrpsid, group_mode,
|
||||
0070, ACCESS_ALLOWED, !sticky_set);
|
||||
num_aces++;
|
||||
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
|
||||
&sid_everyone, other_mode, 0007, ACCESS_ALLOWED, !sticky_set);
|
||||
|
||||
pnntace = (struct cifs_ace *) (nacl_base + nsize);
|
||||
nsize += fill_ace_for_sid(pnntace, &sid_everyone, other_mode,
|
||||
0007, ACCESS_ALLOWED, !sticky_set);
|
||||
num_aces++;
|
||||
|
||||
set_size:
|
||||
*pnum_aces = num_aces;
|
||||
*pnsize = nsize;
|
||||
}
|
||||
|
||||
static __u16 replace_sids_and_copy_aces(struct cifs_acl *pdacl, struct cifs_acl *pndacl,
|
||||
struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
|
||||
struct cifs_sid *pnownersid, struct cifs_sid *pngrpsid)
|
||||
{
|
||||
int i;
|
||||
u16 size = 0;
|
||||
struct cifs_ace *pntace = NULL;
|
||||
char *acl_base = NULL;
|
||||
u32 src_num_aces = 0;
|
||||
u16 nsize = 0;
|
||||
struct cifs_ace *pnntace = NULL;
|
||||
char *nacl_base = NULL;
|
||||
u16 ace_size = 0;
|
||||
|
||||
acl_base = (char *)pdacl;
|
||||
size = sizeof(struct cifs_acl);
|
||||
src_num_aces = le32_to_cpu(pdacl->num_aces);
|
||||
|
||||
nacl_base = (char *)pndacl;
|
||||
nsize = sizeof(struct cifs_acl);
|
||||
|
||||
/* Go through all the ACEs */
|
||||
for (i = 0; i < src_num_aces; ++i) {
|
||||
pntace = (struct cifs_ace *) (acl_base + size);
|
||||
pnntace = (struct cifs_ace *) (nacl_base + nsize);
|
||||
|
||||
if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0)
|
||||
ace_size = cifs_copy_ace(pnntace, pntace, pnownersid);
|
||||
else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0)
|
||||
ace_size = cifs_copy_ace(pnntace, pntace, pngrpsid);
|
||||
else
|
||||
ace_size = cifs_copy_ace(pnntace, pntace, NULL);
|
||||
|
||||
size += le16_to_cpu(pntace->size);
|
||||
nsize += ace_size;
|
||||
}
|
||||
|
||||
return nsize;
|
||||
}
|
||||
|
||||
static int set_chmod_dacl(struct cifs_acl *pdacl, struct cifs_acl *pndacl,
|
||||
struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
|
||||
__u64 *pnmode, bool mode_from_sid)
|
||||
{
|
||||
int i;
|
||||
u16 size = 0;
|
||||
struct cifs_ace *pntace = NULL;
|
||||
char *acl_base = NULL;
|
||||
u32 src_num_aces = 0;
|
||||
u16 nsize = 0;
|
||||
struct cifs_ace *pnntace = NULL;
|
||||
char *nacl_base = NULL;
|
||||
u32 num_aces = 0;
|
||||
__u64 nmode;
|
||||
bool new_aces_set = false;
|
||||
|
||||
/* Assuming that pndacl and pnmode are never NULL */
|
||||
nmode = *pnmode;
|
||||
nacl_base = (char *)pndacl;
|
||||
nsize = sizeof(struct cifs_acl);
|
||||
|
||||
/* If pdacl is NULL, we don't have a src. Simply populate new ACL. */
|
||||
if (!pdacl) {
|
||||
populate_new_aces(nacl_base,
|
||||
pownersid, pgrpsid,
|
||||
pnmode, &num_aces, &nsize,
|
||||
mode_from_sid);
|
||||
goto finalize_dacl;
|
||||
}
|
||||
|
||||
acl_base = (char *)pdacl;
|
||||
size = sizeof(struct cifs_acl);
|
||||
src_num_aces = le32_to_cpu(pdacl->num_aces);
|
||||
|
||||
/* Retain old ACEs which we can retain */
|
||||
for (i = 0; i < src_num_aces; ++i) {
|
||||
pntace = (struct cifs_ace *) (acl_base + size);
|
||||
pnntace = (struct cifs_ace *) (nacl_base + nsize);
|
||||
|
||||
if (!new_aces_set && (pntace->flags & INHERITED_ACE)) {
|
||||
/* Place the new ACEs in between existing explicit and inherited */
|
||||
populate_new_aces(nacl_base,
|
||||
pownersid, pgrpsid,
|
||||
pnmode, &num_aces, &nsize,
|
||||
mode_from_sid);
|
||||
|
||||
new_aces_set = true;
|
||||
}
|
||||
|
||||
/* If it's any one of the ACE we're replacing, skip! */
|
||||
if ((compare_sids(&pntace->sid, &sid_unix_NFS_mode) == 0) ||
|
||||
(compare_sids(&pntace->sid, pownersid) == 0) ||
|
||||
(compare_sids(&pntace->sid, pgrpsid) == 0) ||
|
||||
(compare_sids(&pntace->sid, &sid_everyone) == 0) ||
|
||||
(compare_sids(&pntace->sid, &sid_authusers) == 0)) {
|
||||
goto next_ace;
|
||||
}
|
||||
|
||||
nsize += cifs_copy_ace(pnntace, pntace, NULL);
|
||||
num_aces++;
|
||||
|
||||
next_ace:
|
||||
size += le16_to_cpu(pntace->size);
|
||||
}
|
||||
|
||||
/* If inherited ACEs are not present, place the new ones at the tail */
|
||||
if (!new_aces_set) {
|
||||
populate_new_aces(nacl_base,
|
||||
pownersid, pgrpsid,
|
||||
pnmode, &num_aces, &nsize,
|
||||
mode_from_sid);
|
||||
|
||||
new_aces_set = true;
|
||||
}
|
||||
|
||||
finalize_dacl:
|
||||
pndacl->num_aces = cpu_to_le32(num_aces);
|
||||
pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
|
||||
pndacl->size = cpu_to_le16(nsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
|
||||
{
|
||||
/* BB need to add parm so we can store the SID BB */
|
||||
@ -1094,7 +1257,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 *pnmode, kuid_t uid, kgid_t gid,
|
||||
__u32 secdesclen, __u32 *pnsecdesclen, __u64 *pnmode, kuid_t uid, kgid_t gid,
|
||||
bool mode_from_sid, bool id_from_sid, int *aclflag)
|
||||
{
|
||||
int rc = 0;
|
||||
@ -1102,39 +1265,59 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
|
||||
__u32 ndacloffset;
|
||||
__u32 sidsoffset;
|
||||
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
|
||||
struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
|
||||
struct cifs_sid *nowner_sid_ptr = NULL, *ngroup_sid_ptr = NULL;
|
||||
struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */
|
||||
struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
|
||||
char *end_of_acl = ((char *)pntsd) + secdesclen;
|
||||
u16 size = 0;
|
||||
|
||||
dacloffset = le32_to_cpu(pntsd->dacloffset);
|
||||
if (dacloffset) {
|
||||
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
|
||||
if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
|
||||
cifs_dbg(VFS, "Server returned illegal ACL size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
||||
le32_to_cpu(pntsd->osidoffset));
|
||||
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
|
||||
le32_to_cpu(pntsd->gsidoffset));
|
||||
|
||||
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 +
|
||||
le32_to_cpu(pntsd->gsidoffset));
|
||||
dacloffset = le32_to_cpu(pntsd->dacloffset);
|
||||
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
|
||||
ndacloffset = sizeof(struct cifs_ntsd);
|
||||
ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
|
||||
ndacl_ptr->revision = dacl_ptr->revision;
|
||||
ndacl_ptr->size = 0;
|
||||
ndacl_ptr->num_aces = 0;
|
||||
ndacl_ptr->revision =
|
||||
dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION);
|
||||
|
||||
rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr,
|
||||
ndacl_ptr->size = cpu_to_le16(0);
|
||||
ndacl_ptr->num_aces = cpu_to_le32(0);
|
||||
|
||||
rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr,
|
||||
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);
|
||||
*aclflag = CIFS_ACL_DACL;
|
||||
/* copy the non-dacl portion of secdesc */
|
||||
*pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset,
|
||||
NULL, NULL);
|
||||
|
||||
*aclflag |= CIFS_ACL_DACL;
|
||||
} else {
|
||||
memcpy(pnntsd, pntsd, secdesclen);
|
||||
ndacloffset = sizeof(struct cifs_ntsd);
|
||||
ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
|
||||
ndacl_ptr->revision =
|
||||
dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION);
|
||||
ndacl_ptr->num_aces = dacl_ptr->num_aces;
|
||||
|
||||
if (uid_valid(uid)) { /* chown */
|
||||
uid_t id;
|
||||
owner_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
|
||||
le32_to_cpu(pnntsd->osidoffset));
|
||||
nowner_sid_ptr = kmalloc(sizeof(struct cifs_sid),
|
||||
GFP_KERNEL);
|
||||
if (!nowner_sid_ptr)
|
||||
return -ENOMEM;
|
||||
if (!nowner_sid_ptr) {
|
||||
rc = -ENOMEM;
|
||||
goto chown_chgrp_exit;
|
||||
}
|
||||
id = from_kuid(&init_user_ns, uid);
|
||||
if (id_from_sid) {
|
||||
struct owner_sid *osid = (struct owner_sid *)nowner_sid_ptr;
|
||||
@ -1145,27 +1328,25 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
|
||||
osid->SubAuthorities[0] = cpu_to_le32(88);
|
||||
osid->SubAuthorities[1] = cpu_to_le32(1);
|
||||
osid->SubAuthorities[2] = cpu_to_le32(id);
|
||||
|
||||
} else { /* lookup sid with upcall */
|
||||
rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n",
|
||||
__func__, rc, id);
|
||||
kfree(nowner_sid_ptr);
|
||||
return rc;
|
||||
goto chown_chgrp_exit;
|
||||
}
|
||||
}
|
||||
cifs_copy_sid(owner_sid_ptr, nowner_sid_ptr);
|
||||
kfree(nowner_sid_ptr);
|
||||
*aclflag = CIFS_ACL_OWNER;
|
||||
*aclflag |= CIFS_ACL_OWNER;
|
||||
}
|
||||
if (gid_valid(gid)) { /* chgrp */
|
||||
gid_t id;
|
||||
group_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
|
||||
le32_to_cpu(pnntsd->gsidoffset));
|
||||
ngroup_sid_ptr = kmalloc(sizeof(struct cifs_sid),
|
||||
GFP_KERNEL);
|
||||
if (!ngroup_sid_ptr)
|
||||
return -ENOMEM;
|
||||
if (!ngroup_sid_ptr) {
|
||||
rc = -ENOMEM;
|
||||
goto chown_chgrp_exit;
|
||||
}
|
||||
id = from_kgid(&init_user_ns, gid);
|
||||
if (id_from_sid) {
|
||||
struct owner_sid *gsid = (struct owner_sid *)ngroup_sid_ptr;
|
||||
@ -1176,19 +1357,35 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
|
||||
gsid->SubAuthorities[0] = cpu_to_le32(88);
|
||||
gsid->SubAuthorities[1] = cpu_to_le32(2);
|
||||
gsid->SubAuthorities[2] = cpu_to_le32(id);
|
||||
|
||||
} else { /* lookup sid with upcall */
|
||||
rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n",
|
||||
__func__, rc, id);
|
||||
kfree(ngroup_sid_ptr);
|
||||
return rc;
|
||||
goto chown_chgrp_exit;
|
||||
}
|
||||
}
|
||||
cifs_copy_sid(group_sid_ptr, ngroup_sid_ptr);
|
||||
kfree(ngroup_sid_ptr);
|
||||
*aclflag = CIFS_ACL_GROUP;
|
||||
*aclflag |= CIFS_ACL_GROUP;
|
||||
}
|
||||
|
||||
if (dacloffset) {
|
||||
/* Replace ACEs for old owner with new one */
|
||||
size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr,
|
||||
owner_sid_ptr, group_sid_ptr,
|
||||
nowner_sid_ptr, ngroup_sid_ptr);
|
||||
ndacl_ptr->size = cpu_to_le16(size);
|
||||
}
|
||||
|
||||
sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
|
||||
/* copy the non-dacl portion of secdesc */
|
||||
*pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset,
|
||||
nowner_sid_ptr, ngroup_sid_ptr);
|
||||
|
||||
chown_chgrp_exit:
|
||||
/* errors could jump here. So make sure we return soon after this */
|
||||
kfree(nowner_sid_ptr);
|
||||
kfree(ngroup_sid_ptr);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -1384,6 +1581,9 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
|
||||
int rc = 0;
|
||||
int aclflag = CIFS_ACL_DACL; /* default flag to set */
|
||||
__u32 secdesclen = 0;
|
||||
__u32 nsecdesclen = 0;
|
||||
__u32 dacloffset = 0;
|
||||
struct cifs_acl *dacl_ptr = NULL;
|
||||
struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
|
||||
struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
@ -1414,20 +1614,6 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add three ACEs for owner, group, everyone getting rid of other ACEs
|
||||
* as chmod disables ACEs and set the security descriptor. Allocate
|
||||
* memory for the smb header, set security descriptor request security
|
||||
* descriptor parameters, and secuirty descriptor itself
|
||||
*/
|
||||
secdesclen = max_t(u32, secdesclen, DEFAULT_SEC_DESC_LEN);
|
||||
pnntsd = kmalloc(secdesclen, GFP_KERNEL);
|
||||
if (!pnntsd) {
|
||||
kfree(pntsd);
|
||||
cifs_put_tlink(tlink);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)
|
||||
mode_from_sid = true;
|
||||
else
|
||||
@ -1438,7 +1624,42 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
|
||||
else
|
||||
id_from_sid = false;
|
||||
|
||||
rc = build_sec_desc(pntsd, pnntsd, secdesclen, pnmode, uid, gid,
|
||||
/* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */
|
||||
nsecdesclen = secdesclen;
|
||||
if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
|
||||
if (mode_from_sid)
|
||||
nsecdesclen += sizeof(struct cifs_ace);
|
||||
else /* cifsacl */
|
||||
nsecdesclen += 5 * sizeof(struct cifs_ace);
|
||||
} else { /* chown */
|
||||
/* When ownership changes, changes new owner sid length could be different */
|
||||
nsecdesclen = sizeof(struct cifs_ntsd) + (sizeof(struct cifs_sid) * 2);
|
||||
dacloffset = le32_to_cpu(pntsd->dacloffset);
|
||||
if (dacloffset) {
|
||||
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
|
||||
if (mode_from_sid)
|
||||
nsecdesclen +=
|
||||
le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct cifs_ace);
|
||||
else /* cifsacl */
|
||||
nsecdesclen += le16_to_cpu(dacl_ptr->size);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add three ACEs for owner, group, everyone getting rid of other ACEs
|
||||
* as chmod disables ACEs and set the security descriptor. Allocate
|
||||
* memory for the smb header, set security descriptor request security
|
||||
* descriptor parameters, and secuirty descriptor itself
|
||||
*/
|
||||
nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN);
|
||||
pnntsd = kmalloc(nsecdesclen, GFP_KERNEL);
|
||||
if (!pnntsd) {
|
||||
kfree(pntsd);
|
||||
cifs_put_tlink(tlink);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rc = build_sec_desc(pntsd, pnntsd, secdesclen, &nsecdesclen, pnmode, uid, gid,
|
||||
mode_from_sid, id_from_sid, &aclflag);
|
||||
|
||||
cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc);
|
||||
@ -1448,7 +1669,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
|
||||
|
||||
if (!rc) {
|
||||
/* Set the security descriptor */
|
||||
rc = ops->set_acl(pnntsd, secdesclen, inode, path, aclflag);
|
||||
rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag);
|
||||
cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
|
||||
}
|
||||
cifs_put_tlink(tlink);
|
||||
|
@ -31,8 +31,8 @@
|
||||
#define EXEC_BIT 0x1
|
||||
|
||||
#define ACL_OWNER_MASK 0700
|
||||
#define ACL_GROUP_MASK 0770
|
||||
#define ACL_EVERYONE_MASK 0777
|
||||
#define ACL_GROUP_MASK 0070
|
||||
#define ACL_EVERYONE_MASK 0007
|
||||
|
||||
#define UBITSHIFT 6
|
||||
#define GBITSHIFT 3
|
||||
|
@ -568,15 +568,15 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
/* We use ses->serverName if no domain name available */
|
||||
len = strlen(ses->serverName);
|
||||
/* We use ses->ip_addr if no domain name available */
|
||||
len = strlen(ses->ip_addr);
|
||||
|
||||
server = kmalloc(2 + (len * 2), GFP_KERNEL);
|
||||
if (server == NULL) {
|
||||
rc = -ENOMEM;
|
||||
return rc;
|
||||
}
|
||||
len = cifs_strtoUTF16((__le16 *)server, ses->serverName, len,
|
||||
len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len,
|
||||
nls_cp);
|
||||
rc =
|
||||
crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
|
||||
|
@ -638,8 +638,18 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
|
||||
seq_printf(s, ",snapshot=%llu", tcon->snapshot_time);
|
||||
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->ctx->actimeo / HZ);
|
||||
|
||||
/*
|
||||
* Display file and directory attribute timeout in seconds.
|
||||
* If file and directory attribute timeout the same then actimeo
|
||||
* was likely specified on mount
|
||||
*/
|
||||
if (cifs_sb->ctx->acdirmax == cifs_sb->ctx->acregmax)
|
||||
seq_printf(s, ",actimeo=%lu", cifs_sb->ctx->acregmax / HZ);
|
||||
else {
|
||||
seq_printf(s, ",acdirmax=%lu", cifs_sb->ctx->acdirmax / HZ);
|
||||
seq_printf(s, ",acregmax=%lu", cifs_sb->ctx->acregmax / HZ);
|
||||
}
|
||||
|
||||
if (tcon->ses->chan_max > 1)
|
||||
seq_printf(s, ",multichannel,max_channels=%zu",
|
||||
@ -1526,6 +1536,7 @@ init_cifs(void)
|
||||
*/
|
||||
atomic_set(&sesInfoAllocCount, 0);
|
||||
atomic_set(&tconInfoAllocCount, 0);
|
||||
atomic_set(&tcpSesNextId, 0);
|
||||
atomic_set(&tcpSesAllocCount, 0);
|
||||
atomic_set(&tcpSesReconnectCount, 0);
|
||||
atomic_set(&tconInfoReconnectCount, 0);
|
||||
|
@ -165,5 +165,5 @@ extern struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type,
|
||||
extern const struct export_operations cifs_export_ops;
|
||||
#endif /* CONFIG_CIFS_NFSD_EXPORT */
|
||||
|
||||
#define CIFS_VERSION "2.30"
|
||||
#define CIFS_VERSION "2.31"
|
||||
#endif /* _CIFSFS_H */
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/workqueue.h>
|
||||
@ -504,6 +505,8 @@ struct smb_version_operations {
|
||||
loff_t (*llseek)(struct file *, struct cifs_tcon *, loff_t, int);
|
||||
/* Check for STATUS_IO_TIMEOUT */
|
||||
bool (*is_status_io_timeout)(char *buf);
|
||||
/* Check for STATUS_NETWORK_NAME_DELETED */
|
||||
void (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv);
|
||||
};
|
||||
|
||||
struct smb_version_values {
|
||||
@ -577,6 +580,7 @@ inc_rfc1001_len(void *buf, int count)
|
||||
struct TCP_Server_Info {
|
||||
struct list_head tcp_ses_list;
|
||||
struct list_head smb_ses_list;
|
||||
__u64 conn_id; /* connection identifier (useful for debugging) */
|
||||
int srv_count; /* reference counter */
|
||||
/* 15 character server name + 0x20 16th byte indicating type = srv */
|
||||
char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
|
||||
@ -901,7 +905,7 @@ struct cifs_ses {
|
||||
kuid_t linux_uid; /* overriding owner of files on the mount */
|
||||
kuid_t cred_uid; /* owner of credentials */
|
||||
unsigned int capabilities;
|
||||
char serverName[SERVER_NAME_LEN_WITH_NULL];
|
||||
char ip_addr[INET6_ADDRSTRLEN + 1]; /* Max ipv6 (or v4) addr string len */
|
||||
char *user_name; /* must not be null except during init of sess
|
||||
and after mount option parsing we fill it */
|
||||
char *domainName;
|
||||
@ -1704,7 +1708,9 @@ static inline bool is_retryable_error(int error)
|
||||
#define CIFS_ECHO_OP 0x080 /* echo request */
|
||||
#define CIFS_OBREAK_OP 0x0100 /* oplock break request */
|
||||
#define CIFS_NEG_OP 0x0200 /* negotiate request */
|
||||
#define CIFS_OP_MASK 0x0380 /* mask request type */
|
||||
/* Lower bitmask values are reserved by others below. */
|
||||
#define CIFS_SESS_OP 0x2000 /* session setup request */
|
||||
#define CIFS_OP_MASK 0x2380 /* mask request type */
|
||||
|
||||
#define CIFS_HAS_CREDITS 0x0400 /* already has credits */
|
||||
#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */
|
||||
@ -1844,6 +1850,7 @@ GLOBAL_EXTERN spinlock_t GlobalMid_Lock; /* protects above & list operations */
|
||||
*/
|
||||
GLOBAL_EXTERN atomic_t sesInfoAllocCount;
|
||||
GLOBAL_EXTERN atomic_t tconInfoAllocCount;
|
||||
GLOBAL_EXTERN atomic_t tcpSesNextId;
|
||||
GLOBAL_EXTERN atomic_t tcpSesAllocCount;
|
||||
GLOBAL_EXTERN atomic_t tcpSesReconnectCount;
|
||||
GLOBAL_EXTERN atomic_t tconInfoReconnectCount;
|
||||
|
@ -232,6 +232,8 @@ extern unsigned int setup_special_user_owner_ACE(struct cifs_ace *pace);
|
||||
extern void dequeue_mid(struct mid_q_entry *mid, bool malformed);
|
||||
extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
|
||||
unsigned int to_read);
|
||||
extern ssize_t cifs_discard_from_socket(struct TCP_Server_Info *server,
|
||||
size_t to_read);
|
||||
extern int cifs_read_page_from_socket(struct TCP_Server_Info *server,
|
||||
struct page *page,
|
||||
unsigned int page_offset,
|
||||
|
@ -1451,9 +1451,9 @@ cifs_discard_remaining_data(struct TCP_Server_Info *server)
|
||||
while (remaining > 0) {
|
||||
int length;
|
||||
|
||||
length = cifs_read_from_socket(server, server->bigbuf,
|
||||
min_t(unsigned int, remaining,
|
||||
CIFSMaxBufSize + MAX_HEADER_SIZE(server)));
|
||||
length = cifs_discard_from_socket(server,
|
||||
min_t(size_t, remaining,
|
||||
CIFSMaxBufSize + MAX_HEADER_SIZE(server)));
|
||||
if (length < 0)
|
||||
return length;
|
||||
server->total_read += length;
|
||||
|
@ -242,7 +242,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||
server->max_read = 0;
|
||||
|
||||
cifs_dbg(FYI, "Mark tcp session as need reconnect\n");
|
||||
trace_smb3_reconnect(server->CurrentMid, server->hostname);
|
||||
trace_smb3_reconnect(server->CurrentMid, server->conn_id, server->hostname);
|
||||
|
||||
/* before reconnecting the tcp session, mark the smb session (uid)
|
||||
and the tid bad so they are not used until reconnected */
|
||||
@ -564,6 +564,23 @@ cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
|
||||
return cifs_readv_from_socket(server, &smb_msg);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
cifs_discard_from_socket(struct TCP_Server_Info *server, size_t to_read)
|
||||
{
|
||||
struct msghdr smb_msg;
|
||||
|
||||
/*
|
||||
* iov_iter_discard already sets smb_msg.type and count and iov_offset
|
||||
* and cifs_readv_from_socket sets msg_control and msg_controllen
|
||||
* so little to initialize in struct msghdr
|
||||
*/
|
||||
smb_msg.msg_name = NULL;
|
||||
smb_msg.msg_namelen = 0;
|
||||
iov_iter_discard(&smb_msg.msg_iter, READ, to_read);
|
||||
|
||||
return cifs_readv_from_socket(server, &smb_msg);
|
||||
}
|
||||
|
||||
int
|
||||
cifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page,
|
||||
unsigned int page_offset, unsigned int to_read)
|
||||
@ -846,7 +863,7 @@ static void
|
||||
smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
|
||||
{
|
||||
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer;
|
||||
int scredits = server->credits;
|
||||
int scredits, in_flight;
|
||||
|
||||
/*
|
||||
* SMB1 does not use credits.
|
||||
@ -857,12 +874,14 @@ smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
|
||||
if (shdr->CreditRequest) {
|
||||
spin_lock(&server->req_lock);
|
||||
server->credits += le16_to_cpu(shdr->CreditRequest);
|
||||
scredits = server->credits;
|
||||
in_flight = server->in_flight;
|
||||
spin_unlock(&server->req_lock);
|
||||
wake_up(&server->request_q);
|
||||
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->hostname, scredits,
|
||||
le16_to_cpu(shdr->CreditRequest));
|
||||
server->conn_id, server->hostname, scredits,
|
||||
le16_to_cpu(shdr->CreditRequest), in_flight);
|
||||
cifs_server_dbg(FYI, "%s: added %u credits total=%d\n",
|
||||
__func__, le16_to_cpu(shdr->CreditRequest),
|
||||
scredits);
|
||||
@ -993,6 +1012,10 @@ next_pdu:
|
||||
if (mids[i] != NULL) {
|
||||
mids[i]->resp_buf_size = server->pdu_size;
|
||||
|
||||
if (bufs[i] && server->ops->is_network_name_deleted)
|
||||
server->ops->is_network_name_deleted(bufs[i],
|
||||
server);
|
||||
|
||||
if (!mids[i]->multiRsp || mids[i]->multiEnd)
|
||||
mids[i]->callback(mids[i]);
|
||||
|
||||
@ -1317,6 +1340,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
|
||||
goto out_err_crypto_release;
|
||||
}
|
||||
|
||||
tcp_ses->conn_id = atomic_inc_return(&tcpSesNextId);
|
||||
tcp_ses->noblockcnt = ctx->rootfs;
|
||||
tcp_ses->noblocksnd = ctx->noblocksnd || ctx->rootfs;
|
||||
tcp_ses->noautotune = ctx->noautotune;
|
||||
@ -1838,9 +1862,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
|
||||
/* new SMB session uses our server ref */
|
||||
ses->server = server;
|
||||
if (server->dstaddr.ss_family == AF_INET6)
|
||||
sprintf(ses->serverName, "%pI6", &addr6->sin6_addr);
|
||||
sprintf(ses->ip_addr, "%pI6", &addr6->sin6_addr);
|
||||
else
|
||||
sprintf(ses->serverName, "%pI4", &addr->sin_addr);
|
||||
sprintf(ses->ip_addr, "%pI4", &addr->sin_addr);
|
||||
|
||||
if (ctx->username) {
|
||||
ses->user_name = kstrdup(ctx->username, GFP_KERNEL);
|
||||
@ -2269,7 +2293,9 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
|
||||
if (strcmp(old->local_nls->charset, new->local_nls->charset))
|
||||
return 0;
|
||||
|
||||
if (old->ctx->actimeo != new->ctx->actimeo)
|
||||
if (old->ctx->acregmax != new->ctx->acregmax)
|
||||
return 0;
|
||||
if (old->ctx->acdirmax != new->ctx->acdirmax)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
@ -2911,7 +2937,7 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
/*
|
||||
* cifs_build_path_to_root returns full path to root when we do not have an
|
||||
* exiting connection (tcon)
|
||||
* existing connection (tcon)
|
||||
*/
|
||||
static char *
|
||||
build_unc_path_to_root(const struct smb3_fs_context *ctx,
|
||||
@ -3038,96 +3064,91 @@ static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_dfs_tgt_conn(const char *path, const char *full_path,
|
||||
const struct dfs_cache_tgt_iterator *tgt_it,
|
||||
struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
|
||||
unsigned int *xid, struct TCP_Server_Info **server,
|
||||
struct cifs_ses **ses, struct cifs_tcon **tcon)
|
||||
{
|
||||
int rc;
|
||||
struct dfs_info3_param ref = {0};
|
||||
char *mdata = NULL;
|
||||
struct smb3_fs_context fake_ctx = {NULL};
|
||||
char *fake_devname = NULL;
|
||||
|
||||
cifs_dbg(FYI, "%s: dfs path: %s\n", __func__, path);
|
||||
|
||||
rc = dfs_cache_get_tgt_referral(path, tgt_it, &ref);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options,
|
||||
full_path + 1, &ref,
|
||||
&fake_devname);
|
||||
free_dfs_info_param(&ref);
|
||||
|
||||
if (IS_ERR(mdata)) {
|
||||
rc = PTR_ERR(mdata);
|
||||
mdata = NULL;
|
||||
} else
|
||||
rc = cifs_setup_volume_info(&fake_ctx, mdata, fake_devname);
|
||||
|
||||
kfree(mdata);
|
||||
kfree(fake_devname);
|
||||
|
||||
if (!rc) {
|
||||
/*
|
||||
* We use a 'fake_ctx' here because we need pass it down to the
|
||||
* mount_{get,put} functions to test connection against new DFS
|
||||
* targets.
|
||||
*/
|
||||
mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon);
|
||||
rc = mount_get_conns(&fake_ctx, cifs_sb, xid, server, ses,
|
||||
tcon);
|
||||
if (!rc || (*server && *ses)) {
|
||||
/*
|
||||
* We were able to connect to new target server.
|
||||
* Update current context with new target server.
|
||||
*/
|
||||
rc = update_vol_info(tgt_it, &fake_ctx, ctx);
|
||||
}
|
||||
}
|
||||
smb3_cleanup_fs_context_contents(&fake_ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_dfs_failover(const char *path, const char *full_path, struct cifs_sb_info *cifs_sb,
|
||||
struct smb3_fs_context *ctx, struct cifs_ses *root_ses,
|
||||
unsigned int *xid, struct TCP_Server_Info **server,
|
||||
struct cifs_ses **ses, struct cifs_tcon **tcon)
|
||||
{
|
||||
int rc;
|
||||
struct dfs_cache_tgt_list tgt_list;
|
||||
struct dfs_cache_tgt_list tgt_list = {0};
|
||||
struct dfs_cache_tgt_iterator *tgt_it = NULL;
|
||||
struct smb3_fs_context tmp_ctx = {NULL};
|
||||
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
cifs_dbg(FYI, "%s: path=%s full_path=%s\n", __func__, path, full_path);
|
||||
|
||||
rc = dfs_cache_noreq_find(path, NULL, &tgt_list);
|
||||
if (rc)
|
||||
return rc;
|
||||
/*
|
||||
* We use a 'tmp_ctx' here because we need pass it down to the mount_{get,put} functions to
|
||||
* test connection against new DFS targets.
|
||||
*/
|
||||
rc = smb3_fs_context_dup(&tmp_ctx, ctx);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
for (;;) {
|
||||
struct dfs_info3_param ref = {0};
|
||||
char *fake_devname = NULL, *mdata = NULL;
|
||||
|
||||
/* Get next DFS target server - if any */
|
||||
rc = get_next_dfs_tgt(path, &tgt_list, &tgt_it);
|
||||
if (rc)
|
||||
break;
|
||||
/* Connect to next DFS target */
|
||||
rc = setup_dfs_tgt_conn(path, full_path, tgt_it, cifs_sb, ctx, xid, server, ses,
|
||||
tcon);
|
||||
if (!rc || (*server && *ses))
|
||||
|
||||
rc = dfs_cache_get_tgt_referral(path, tgt_it, &ref);
|
||||
if (rc)
|
||||
break;
|
||||
|
||||
cifs_dbg(FYI, "%s: old ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC,
|
||||
tmp_ctx.prepath);
|
||||
|
||||
mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, &ref,
|
||||
&fake_devname);
|
||||
free_dfs_info_param(&ref);
|
||||
|
||||
if (IS_ERR(mdata)) {
|
||||
rc = PTR_ERR(mdata);
|
||||
mdata = NULL;
|
||||
} else
|
||||
rc = cifs_setup_volume_info(&tmp_ctx, mdata, fake_devname);
|
||||
|
||||
kfree(mdata);
|
||||
kfree(fake_devname);
|
||||
|
||||
if (rc)
|
||||
break;
|
||||
|
||||
cifs_dbg(FYI, "%s: new ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC,
|
||||
tmp_ctx.prepath);
|
||||
|
||||
mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon);
|
||||
rc = mount_get_conns(&tmp_ctx, cifs_sb, xid, server, ses, tcon);
|
||||
if (!rc || (*server && *ses)) {
|
||||
/*
|
||||
* We were able to connect to new target server. Update current context with
|
||||
* new target server.
|
||||
*/
|
||||
rc = update_vol_info(tgt_it, &tmp_ctx, ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!rc) {
|
||||
cifs_dbg(FYI, "%s: final ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC,
|
||||
tmp_ctx.prepath);
|
||||
/*
|
||||
* Update DFS target hint in DFS referral cache with the target
|
||||
* server we successfully reconnected to.
|
||||
* Update DFS target hint in DFS referral cache with the target server we
|
||||
* successfully reconnected to.
|
||||
*/
|
||||
rc = dfs_cache_update_tgthint(*xid, root_ses ? root_ses : *ses,
|
||||
cifs_sb->local_nls,
|
||||
cifs_remap(cifs_sb), path,
|
||||
tgt_it);
|
||||
rc = dfs_cache_update_tgthint(*xid, root_ses ? root_ses : *ses, cifs_sb->local_nls,
|
||||
cifs_remap(cifs_sb), path, tgt_it);
|
||||
}
|
||||
|
||||
out:
|
||||
smb3_cleanup_fs_context_contents(&tmp_ctx);
|
||||
dfs_cache_free_tgts(&tgt_list);
|
||||
return rc;
|
||||
}
|
||||
@ -3285,77 +3306,77 @@ static void put_root_ses(struct cifs_ses *ses)
|
||||
cifs_put_smb_ses(ses);
|
||||
}
|
||||
|
||||
/* Check if a path component is remote and then update @dfs_path accordingly */
|
||||
static int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
|
||||
const unsigned int xid, struct TCP_Server_Info *server,
|
||||
struct cifs_tcon *tcon, char **dfs_path)
|
||||
/* Set up next dfs prefix path in @dfs_path */
|
||||
static int next_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
|
||||
const unsigned int xid, struct TCP_Server_Info *server,
|
||||
struct cifs_tcon *tcon, char **dfs_path)
|
||||
{
|
||||
char *path, *s;
|
||||
char sep = CIFS_DIR_SEP(cifs_sb), tmp;
|
||||
char *npath;
|
||||
int rc = 0;
|
||||
int added_treename = tcon->Flags & SMB_SHARE_IS_IN_DFS;
|
||||
int skip = added_treename;
|
||||
char *path, *npath;
|
||||
int added_treename = is_tcon_dfs(tcon);
|
||||
int rc;
|
||||
|
||||
path = cifs_build_path_to_root(ctx, cifs_sb, tcon, added_treename);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Walk through the path components in @path and check if they're accessible. In case any of
|
||||
* the components is -EREMOTE, then update @dfs_path with the next DFS referral request path
|
||||
* (NOT including the remaining components).
|
||||
*/
|
||||
s = path;
|
||||
do {
|
||||
/* skip separators */
|
||||
while (*s && *s == sep)
|
||||
s++;
|
||||
if (!*s)
|
||||
break;
|
||||
/* next separator */
|
||||
while (*s && *s != sep)
|
||||
s++;
|
||||
/*
|
||||
* if the treename is added, we then have to skip the first
|
||||
* part within the separators
|
||||
*/
|
||||
if (skip) {
|
||||
skip = 0;
|
||||
continue;
|
||||
rc = is_path_remote(cifs_sb, ctx, xid, server, tcon);
|
||||
if (rc == -EREMOTE) {
|
||||
struct smb3_fs_context v = {NULL};
|
||||
/* if @path contains a tree name, skip it in the prefix path */
|
||||
if (added_treename) {
|
||||
rc = smb3_parse_devname(path, &v);
|
||||
if (rc)
|
||||
goto out;
|
||||
npath = build_unc_path_to_root(&v, cifs_sb, true);
|
||||
smb3_cleanup_fs_context_contents(&v);
|
||||
} else {
|
||||
v.UNC = ctx->UNC;
|
||||
v.prepath = path + 1;
|
||||
npath = build_unc_path_to_root(&v, cifs_sb, true);
|
||||
}
|
||||
tmp = *s;
|
||||
*s = 0;
|
||||
rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, path);
|
||||
if (rc && rc == -EREMOTE) {
|
||||
struct smb3_fs_context v = {NULL};
|
||||
/* if @path contains a tree name, skip it in the prefix path */
|
||||
if (added_treename) {
|
||||
rc = smb3_parse_devname(path, &v);
|
||||
if (rc)
|
||||
break;
|
||||
rc = -EREMOTE;
|
||||
npath = build_unc_path_to_root(&v, cifs_sb, true);
|
||||
smb3_cleanup_fs_context_contents(&v);
|
||||
} else {
|
||||
v.UNC = ctx->UNC;
|
||||
v.prepath = path + 1;
|
||||
npath = build_unc_path_to_root(&v, cifs_sb, true);
|
||||
}
|
||||
if (IS_ERR(npath)) {
|
||||
rc = PTR_ERR(npath);
|
||||
break;
|
||||
}
|
||||
kfree(*dfs_path);
|
||||
*dfs_path = npath;
|
||||
}
|
||||
*s = tmp;
|
||||
} while (rc == 0);
|
||||
|
||||
if (IS_ERR(npath)) {
|
||||
rc = PTR_ERR(npath);
|
||||
goto out;
|
||||
}
|
||||
|
||||
kfree(*dfs_path);
|
||||
*dfs_path = npath;
|
||||
rc = -EREMOTE;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(path);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check if resolved targets can handle any DFS referrals */
|
||||
static int is_referral_server(const char *ref_path, struct cifs_tcon *tcon, bool *ref_server)
|
||||
{
|
||||
int rc;
|
||||
struct dfs_info3_param ref = {0};
|
||||
|
||||
if (is_tcon_dfs(tcon)) {
|
||||
*ref_server = true;
|
||||
} else {
|
||||
cifs_dbg(FYI, "%s: ref_path=%s\n", __func__, ref_path);
|
||||
|
||||
rc = dfs_cache_noreq_find(ref_path, &ref, NULL);
|
||||
if (rc) {
|
||||
cifs_dbg(VFS, "%s: dfs_cache_noreq_find: failed (rc=%d)\n", __func__, rc);
|
||||
return rc;
|
||||
}
|
||||
cifs_dbg(FYI, "%s: ref.flags=0x%x\n", __func__, ref.flags);
|
||||
/*
|
||||
* Check if all targets are capable of handling DFS referrals as per
|
||||
* MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL.
|
||||
*/
|
||||
*ref_server = !!(ref.flags & DFSREF_REFERRAL_SERVER);
|
||||
free_dfs_info_param(&ref);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
|
||||
{
|
||||
int rc = 0;
|
||||
@ -3367,18 +3388,19 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
|
||||
char *ref_path = NULL, *full_path = NULL;
|
||||
char *oldmnt = NULL;
|
||||
char *mntdata = NULL;
|
||||
bool ref_server = false;
|
||||
|
||||
rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
|
||||
/*
|
||||
* Unconditionally try to get an DFS referral (even cached) to determine whether it is an
|
||||
* DFS mount.
|
||||
* If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally
|
||||
* try to get an DFS referral (even cached) to determine whether it is an DFS mount.
|
||||
*
|
||||
* Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem
|
||||
* to respond with PATH_NOT_COVERED to requests that include the prefix.
|
||||
*/
|
||||
if (dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), ctx->UNC + 1, NULL,
|
||||
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) ||
|
||||
dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), ctx->UNC + 1, NULL,
|
||||
NULL)) {
|
||||
/* No DFS referral was returned. Looks like a regular share. */
|
||||
if (rc)
|
||||
goto error;
|
||||
/* Check if it is fully accessible and then mount it */
|
||||
@ -3432,13 +3454,18 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
|
||||
break;
|
||||
if (!tcon)
|
||||
continue;
|
||||
|
||||
/* Make sure that requests go through new root servers */
|
||||
if (is_tcon_dfs(tcon)) {
|
||||
rc = is_referral_server(ref_path + 1, tcon, &ref_server);
|
||||
if (rc)
|
||||
break;
|
||||
if (ref_server) {
|
||||
put_root_ses(root_ses);
|
||||
set_root_ses(cifs_sb, ses, &root_ses);
|
||||
}
|
||||
/* Check for remaining path components and then continue chasing them (-EREMOTE) */
|
||||
rc = check_dfs_prepath(cifs_sb, ctx, xid, server, tcon, &ref_path);
|
||||
|
||||
/* Get next dfs path and then continue chasing them if -EREMOTE */
|
||||
rc = next_dfs_prepath(cifs_sb, ctx, xid, server, tcon, &ref_path);
|
||||
/* Prevent recursion on broken link referrals */
|
||||
if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS)
|
||||
rc = -ELOOP;
|
||||
|
@ -37,11 +37,12 @@ struct cache_dfs_tgt {
|
||||
struct cache_entry {
|
||||
struct hlist_node hlist;
|
||||
const char *path;
|
||||
int ttl;
|
||||
int srvtype;
|
||||
int flags;
|
||||
int hdr_flags; /* RESP_GET_DFS_REFERRAL.ReferralHeaderFlags */
|
||||
int ttl; /* DFS_REREFERRAL_V3.TimeToLive */
|
||||
int srvtype; /* DFS_REREFERRAL_V3.ServerType */
|
||||
int ref_flags; /* DFS_REREFERRAL_V3.ReferralEntryFlags */
|
||||
struct timespec64 etime;
|
||||
int path_consumed;
|
||||
int path_consumed; /* RESP_GET_DFS_REFERRAL.PathConsumed */
|
||||
int numtgts;
|
||||
struct list_head tlist;
|
||||
struct cache_dfs_tgt *tgthint;
|
||||
@ -166,14 +167,11 @@ static int dfscache_proc_show(struct seq_file *m, void *v)
|
||||
continue;
|
||||
|
||||
seq_printf(m,
|
||||
"cache entry: path=%s,type=%s,ttl=%d,etime=%ld,"
|
||||
"interlink=%s,path_consumed=%d,expired=%s\n",
|
||||
ce->path,
|
||||
ce->srvtype == DFS_TYPE_ROOT ? "root" : "link",
|
||||
ce->ttl, ce->etime.tv_nsec,
|
||||
IS_INTERLINK_SET(ce->flags) ? "yes" : "no",
|
||||
ce->path_consumed,
|
||||
cache_entry_expired(ce) ? "yes" : "no");
|
||||
"cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n",
|
||||
ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link",
|
||||
ce->ttl, ce->etime.tv_nsec, ce->ref_flags, ce->hdr_flags,
|
||||
IS_INTERLINK_SET(ce->hdr_flags) ? "yes" : "no",
|
||||
ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no");
|
||||
|
||||
list_for_each_entry(t, &ce->tlist, list) {
|
||||
seq_printf(m, " %s%s\n",
|
||||
@ -236,11 +234,12 @@ static inline void dump_tgts(const struct cache_entry *ce)
|
||||
|
||||
static inline void dump_ce(const struct cache_entry *ce)
|
||||
{
|
||||
cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,interlink=%s,path_consumed=%d,expired=%s\n",
|
||||
cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n",
|
||||
ce->path,
|
||||
ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl,
|
||||
ce->etime.tv_nsec,
|
||||
IS_INTERLINK_SET(ce->flags) ? "yes" : "no",
|
||||
ce->hdr_flags, ce->ref_flags,
|
||||
IS_INTERLINK_SET(ce->hdr_flags) ? "yes" : "no",
|
||||
ce->path_consumed,
|
||||
cache_entry_expired(ce) ? "yes" : "no");
|
||||
dump_tgts(ce);
|
||||
@ -381,7 +380,8 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
|
||||
ce->ttl = refs[0].ttl;
|
||||
ce->etime = get_expire_time(ce->ttl);
|
||||
ce->srvtype = refs[0].server_type;
|
||||
ce->flags = refs[0].ref_flag;
|
||||
ce->hdr_flags = refs[0].flags;
|
||||
ce->ref_flags = refs[0].ref_flag;
|
||||
ce->path_consumed = refs[0].path_consumed;
|
||||
|
||||
for (i = 0; i < numrefs; i++) {
|
||||
@ -799,7 +799,8 @@ static int setup_referral(const char *path, struct cache_entry *ce,
|
||||
ref->path_consumed = ce->path_consumed;
|
||||
ref->ttl = ce->ttl;
|
||||
ref->server_type = ce->srvtype;
|
||||
ref->ref_flag = ce->flags;
|
||||
ref->ref_flag = ce->ref_flags;
|
||||
ref->flags = ce->hdr_flags;
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -580,7 +580,7 @@ int cifs_open(struct inode *inode, struct file *file)
|
||||
} else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
|
||||
if (tcon->ses->serverNOS)
|
||||
cifs_dbg(VFS, "server %s of type %s returned unexpected error on SMB posix open, disabling posix open support. Check if server update available.\n",
|
||||
tcon->ses->serverName,
|
||||
tcon->ses->ip_addr,
|
||||
tcon->ses->serverNOS);
|
||||
tcon->broken_posix_open = true;
|
||||
} else if ((rc != -EIO) && (rc != -EREMOTE) &&
|
||||
|
@ -140,6 +140,8 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
|
||||
fsparam_u32("rsize", Opt_rsize),
|
||||
fsparam_u32("wsize", Opt_wsize),
|
||||
fsparam_u32("actimeo", Opt_actimeo),
|
||||
fsparam_u32("acdirmax", Opt_acdirmax),
|
||||
fsparam_u32("acregmax", Opt_acregmax),
|
||||
fsparam_u32("echo_interval", Opt_echo_interval),
|
||||
fsparam_u32("max_credits", Opt_max_credits),
|
||||
fsparam_u32("handletimeout", Opt_handletimeout),
|
||||
@ -397,7 +399,7 @@ cifs_parse_smb_version(char *value, struct smb3_fs_context *ctx, bool is_smb3)
|
||||
ctx->vals = &smb3any_values;
|
||||
break;
|
||||
case Smb_default:
|
||||
ctx->ops = &smb30_operations; /* currently identical with 3.0 */
|
||||
ctx->ops = &smb30_operations;
|
||||
ctx->vals = &smbdefault_values;
|
||||
break;
|
||||
default:
|
||||
@ -542,20 +544,37 @@ static int smb3_fs_context_parse_monolithic(struct fs_context *fc,
|
||||
|
||||
/* BB Need to add support for sep= here TBD */
|
||||
while ((key = strsep(&options, ",")) != NULL) {
|
||||
if (*key) {
|
||||
size_t v_len = 0;
|
||||
char *value = strchr(key, '=');
|
||||
size_t len;
|
||||
char *value;
|
||||
|
||||
if (value) {
|
||||
if (value == key)
|
||||
continue;
|
||||
*value++ = 0;
|
||||
v_len = strlen(value);
|
||||
}
|
||||
ret = vfs_parse_fs_string(fc, key, value, v_len);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (*key == 0)
|
||||
break;
|
||||
|
||||
/* Check if following character is the deliminator If yes,
|
||||
* we have encountered a double deliminator reset the NULL
|
||||
* character to the deliminator
|
||||
*/
|
||||
while (options && options[0] == ',') {
|
||||
len = strlen(key);
|
||||
strcpy(key + len, options);
|
||||
options = strchr(options, ',');
|
||||
if (options)
|
||||
*options++ = 0;
|
||||
}
|
||||
|
||||
|
||||
len = 0;
|
||||
value = strchr(key, '=');
|
||||
if (value) {
|
||||
if (value == key)
|
||||
continue;
|
||||
*value++ = 0;
|
||||
len = strlen(value);
|
||||
}
|
||||
|
||||
ret = vfs_parse_fs_string(fc, key, value, len);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -929,13 +948,32 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
|
||||
ctx->wsize = result.uint_32;
|
||||
ctx->got_wsize = true;
|
||||
break;
|
||||
case Opt_actimeo:
|
||||
ctx->actimeo = HZ * result.uint_32;
|
||||
if (ctx->actimeo > CIFS_MAX_ACTIMEO) {
|
||||
cifs_dbg(VFS, "attribute cache timeout too large\n");
|
||||
case Opt_acregmax:
|
||||
ctx->acregmax = HZ * result.uint_32;
|
||||
if (ctx->acregmax > CIFS_MAX_ACTIMEO) {
|
||||
cifs_dbg(VFS, "acregmax too large\n");
|
||||
goto cifs_parse_mount_err;
|
||||
}
|
||||
break;
|
||||
case Opt_acdirmax:
|
||||
ctx->acdirmax = HZ * result.uint_32;
|
||||
if (ctx->acdirmax > CIFS_MAX_ACTIMEO) {
|
||||
cifs_dbg(VFS, "acdirmax too large\n");
|
||||
goto cifs_parse_mount_err;
|
||||
}
|
||||
break;
|
||||
case Opt_actimeo:
|
||||
if (HZ * result.uint_32 > CIFS_MAX_ACTIMEO) {
|
||||
cifs_dbg(VFS, "timeout too large\n");
|
||||
goto cifs_parse_mount_err;
|
||||
}
|
||||
if ((ctx->acdirmax != CIFS_DEF_ACTIMEO) ||
|
||||
(ctx->acregmax != CIFS_DEF_ACTIMEO)) {
|
||||
cifs_dbg(VFS, "actimeo ignored since acregmax or acdirmax specified\n");
|
||||
break;
|
||||
}
|
||||
ctx->acdirmax = ctx->acregmax = HZ * result.uint_32;
|
||||
break;
|
||||
case Opt_echo_interval:
|
||||
ctx->echo_interval = result.uint_32;
|
||||
break;
|
||||
@ -1361,7 +1399,8 @@ int smb3_init_fs_context(struct fs_context *fc)
|
||||
/* default is to use strict cifs caching semantics */
|
||||
ctx->strict_io = true;
|
||||
|
||||
ctx->actimeo = CIFS_DEF_ACTIMEO;
|
||||
ctx->acregmax = CIFS_DEF_ACTIMEO;
|
||||
ctx->acdirmax = CIFS_DEF_ACTIMEO;
|
||||
|
||||
/* Most clients set timeout to 0, allows server to use its default */
|
||||
ctx->handle_timeout = 0; /* See MS-SMB2 spec section 2.2.14.2.12 */
|
||||
|
@ -118,6 +118,8 @@ enum cifs_param {
|
||||
Opt_rsize,
|
||||
Opt_wsize,
|
||||
Opt_actimeo,
|
||||
Opt_acdirmax,
|
||||
Opt_acregmax,
|
||||
Opt_echo_interval,
|
||||
Opt_max_credits,
|
||||
Opt_snapshot,
|
||||
@ -232,7 +234,9 @@ struct smb3_fs_context {
|
||||
unsigned int wsize;
|
||||
unsigned int min_offload;
|
||||
bool sockopt_tcp_nodelay:1;
|
||||
unsigned long actimeo; /* attribute cache timeout (jiffies) */
|
||||
/* attribute cache timemout for files and directories in jiffies */
|
||||
unsigned long acregmax;
|
||||
unsigned long acdirmax;
|
||||
struct smb_version_operations *ops;
|
||||
struct smb_version_values *vals;
|
||||
char *prepath;
|
||||
|
@ -2199,12 +2199,23 @@ cifs_inode_needs_reval(struct inode *inode)
|
||||
if (!lookupCacheEnabled)
|
||||
return true;
|
||||
|
||||
if (!cifs_sb->ctx->actimeo)
|
||||
return true;
|
||||
|
||||
if (!time_in_range(jiffies, cifs_i->time,
|
||||
cifs_i->time + cifs_sb->ctx->actimeo))
|
||||
return true;
|
||||
/*
|
||||
* depending on inode type, check if attribute caching disabled for
|
||||
* files or directories
|
||||
*/
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
if (!cifs_sb->ctx->acdirmax)
|
||||
return true;
|
||||
if (!time_in_range(jiffies, cifs_i->time,
|
||||
cifs_i->time + cifs_sb->ctx->acdirmax))
|
||||
return true;
|
||||
} else { /* file */
|
||||
if (!cifs_sb->ctx->acregmax)
|
||||
return true;
|
||||
if (!time_in_range(jiffies, cifs_i->time,
|
||||
cifs_i->time + cifs_sb->ctx->acregmax))
|
||||
return true;
|
||||
}
|
||||
|
||||
/* hardlinked files w/ noserverino get "special" treatment */
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) &&
|
||||
|
@ -218,7 +218,7 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
|
||||
/* UNC and paths */
|
||||
/* XXX: Use ses->server->hostname? */
|
||||
sprintf(unc, unc_fmt, ses->serverName);
|
||||
sprintf(unc, unc_fmt, ses->ip_addr);
|
||||
ctx.UNC = unc;
|
||||
ctx.prepath = "";
|
||||
|
||||
|
@ -63,17 +63,19 @@ smb2_add_credits(struct TCP_Server_Info *server,
|
||||
const struct cifs_credits *credits, const int optype)
|
||||
{
|
||||
int *val, rc = -1;
|
||||
int scredits, in_flight;
|
||||
unsigned int add = credits->value;
|
||||
unsigned int instance = credits->instance;
|
||||
bool reconnect_detected = false;
|
||||
bool reconnect_with_invalid_credits = false;
|
||||
|
||||
spin_lock(&server->req_lock);
|
||||
val = server->ops->get_credits_field(server, optype);
|
||||
|
||||
/* eg found case where write overlapping reconnect messed up credits */
|
||||
if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0))
|
||||
trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
|
||||
server->hostname, *val, add);
|
||||
reconnect_with_invalid_credits = true;
|
||||
|
||||
if ((instance == 0) || (instance == server->reconnect_instance))
|
||||
*val += add;
|
||||
else
|
||||
@ -84,7 +86,9 @@ smb2_add_credits(struct TCP_Server_Info *server,
|
||||
pr_warn_once("server overflowed SMB3 credits\n");
|
||||
}
|
||||
server->in_flight--;
|
||||
if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP)
|
||||
if (server->in_flight == 0 &&
|
||||
((optype & CIFS_OP_MASK) != CIFS_NEG_OP) &&
|
||||
((optype & CIFS_OP_MASK) != CIFS_SESS_OP))
|
||||
rc = change_conf(server);
|
||||
/*
|
||||
* Sometimes server returns 0 credits on oplock break ack - we need to
|
||||
@ -97,14 +101,26 @@ smb2_add_credits(struct TCP_Server_Info *server,
|
||||
server->oplock_credits++;
|
||||
}
|
||||
}
|
||||
scredits = *val;
|
||||
in_flight = server->in_flight;
|
||||
spin_unlock(&server->req_lock);
|
||||
wake_up(&server->request_q);
|
||||
|
||||
if (reconnect_detected) {
|
||||
trace_smb3_reconnect_detected(server->CurrentMid,
|
||||
server->conn_id, server->hostname, scredits, add, in_flight);
|
||||
|
||||
cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n",
|
||||
add, instance);
|
||||
}
|
||||
|
||||
if (reconnect_with_invalid_credits) {
|
||||
trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
|
||||
server->conn_id, server->hostname, scredits, add, in_flight);
|
||||
cifs_dbg(FYI, "Negotiate operation when server credits is non-zero. Optype: %d, server credits: %d, credits added: %d\n",
|
||||
optype, scredits, add);
|
||||
}
|
||||
|
||||
if (server->tcpStatus == CifsNeedReconnect
|
||||
|| server->tcpStatus == CifsExiting)
|
||||
return;
|
||||
@ -123,23 +139,30 @@ smb2_add_credits(struct TCP_Server_Info *server,
|
||||
cifs_dbg(FYI, "disabling oplocks\n");
|
||||
break;
|
||||
default:
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->hostname, rc, add);
|
||||
cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, rc);
|
||||
/* change_conf rebalanced credits for different types */
|
||||
break;
|
||||
}
|
||||
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->conn_id, server->hostname, scredits, add, in_flight);
|
||||
cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, scredits);
|
||||
}
|
||||
|
||||
static void
|
||||
smb2_set_credits(struct TCP_Server_Info *server, const int val)
|
||||
{
|
||||
int scredits, in_flight;
|
||||
|
||||
spin_lock(&server->req_lock);
|
||||
server->credits = val;
|
||||
if (val == 1)
|
||||
server->reconnect_instance++;
|
||||
scredits = server->credits;
|
||||
in_flight = server->in_flight;
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
trace_smb3_set_credits(server->CurrentMid,
|
||||
server->hostname, val, val);
|
||||
server->conn_id, server->hostname, scredits, val, in_flight);
|
||||
cifs_dbg(FYI, "%s: set %u credits\n", __func__, val);
|
||||
|
||||
/* don't log while holding the lock */
|
||||
@ -171,7 +194,7 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
|
||||
unsigned int *num, struct cifs_credits *credits)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned int scredits;
|
||||
unsigned int scredits, in_flight;
|
||||
|
||||
spin_lock(&server->req_lock);
|
||||
while (1) {
|
||||
@ -208,17 +231,18 @@ 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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
scredits = server->credits;
|
||||
in_flight = server->in_flight;
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->hostname, scredits, -(credits->value));
|
||||
server->conn_id, server->hostname, scredits, -(credits->value), in_flight);
|
||||
cifs_dbg(FYI, "%s: removed %u credits total=%d\n",
|
||||
__func__, credits->value, scredits);
|
||||
|
||||
@ -231,14 +255,14 @@ 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;
|
||||
int scredits, in_flight;
|
||||
|
||||
if (!credits->value || credits->value == new_val)
|
||||
return 0;
|
||||
|
||||
if (credits->value < new_val) {
|
||||
trace_smb3_too_many_credits(server->CurrentMid,
|
||||
server->hostname, 0, credits->value - new_val);
|
||||
server->conn_id, server->hostname, 0, credits->value - new_val, 0);
|
||||
cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)",
|
||||
credits->value, new_val);
|
||||
|
||||
@ -248,9 +272,13 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
|
||||
spin_lock(&server->req_lock);
|
||||
|
||||
if (server->reconnect_instance != credits->instance) {
|
||||
scredits = server->credits;
|
||||
in_flight = server->in_flight;
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
trace_smb3_reconnect_detected(server->CurrentMid,
|
||||
server->hostname, 0, 0);
|
||||
server->conn_id, server->hostname, scredits,
|
||||
credits->value - new_val, in_flight);
|
||||
cifs_server_dbg(VFS, "trying to return %d credits to old session\n",
|
||||
credits->value - new_val);
|
||||
return -EAGAIN;
|
||||
@ -258,15 +286,18 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
|
||||
|
||||
server->credits += credits->value - new_val;
|
||||
scredits = server->credits;
|
||||
in_flight = server->in_flight;
|
||||
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);
|
||||
server->conn_id, server->hostname, scredits,
|
||||
credits->value - new_val, in_flight);
|
||||
cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n",
|
||||
__func__, credits->value - new_val, scredits);
|
||||
|
||||
credits->value = new_val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2369,7 +2400,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;
|
||||
int scredits, in_flight;
|
||||
|
||||
if (shdr->Status != STATUS_PENDING)
|
||||
return false;
|
||||
@ -2378,11 +2409,13 @@ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
|
||||
spin_lock(&server->req_lock);
|
||||
server->credits += le16_to_cpu(shdr->CreditRequest);
|
||||
scredits = server->credits;
|
||||
in_flight = server->in_flight;
|
||||
spin_unlock(&server->req_lock);
|
||||
wake_up(&server->request_q);
|
||||
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->hostname, scredits, le16_to_cpu(shdr->CreditRequest));
|
||||
server->conn_id, server->hostname, scredits,
|
||||
le16_to_cpu(shdr->CreditRequest), in_flight);
|
||||
cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n",
|
||||
__func__, le16_to_cpu(shdr->CreditRequest), scredits);
|
||||
}
|
||||
@ -2418,6 +2451,34 @@ smb2_is_status_io_timeout(char *buf)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
|
||||
{
|
||||
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
|
||||
struct list_head *tmp, *tmp1;
|
||||
struct cifs_ses *ses;
|
||||
struct cifs_tcon *tcon;
|
||||
|
||||
if (shdr->Status != STATUS_NETWORK_NAME_DELETED)
|
||||
return;
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each(tmp, &server->smb_ses_list) {
|
||||
ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
|
||||
list_for_each(tmp1, &ses->tcon_list) {
|
||||
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
|
||||
if (tcon->tid == shdr->TreeId) {
|
||||
tcon->need_reconnect = true;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
pr_warn_once("Server share %s deleted.\n",
|
||||
tcon->treeName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
|
||||
struct cifsInodeInfo *cinode)
|
||||
@ -4605,6 +4666,10 @@ static void smb2_decrypt_offload(struct work_struct *work)
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
mid->when_received = jiffies;
|
||||
#endif
|
||||
if (dw->server->ops->is_network_name_deleted)
|
||||
dw->server->ops->is_network_name_deleted(dw->buf,
|
||||
dw->server);
|
||||
|
||||
mid->callback(mid);
|
||||
} else {
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
@ -4723,6 +4788,12 @@ non_offloaded_decrypt:
|
||||
rc = handle_read_data(server, *mid, buf,
|
||||
server->vals->read_rsp_size,
|
||||
pages, npages, len, false);
|
||||
if (rc >= 0) {
|
||||
if (server->ops->is_network_name_deleted) {
|
||||
server->ops->is_network_name_deleted(buf,
|
||||
server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_pages:
|
||||
@ -5072,6 +5143,7 @@ struct smb_version_operations smb20_operations = {
|
||||
.fiemap = smb3_fiemap,
|
||||
.llseek = smb3_llseek,
|
||||
.is_status_io_timeout = smb2_is_status_io_timeout,
|
||||
.is_network_name_deleted = smb2_is_network_name_deleted,
|
||||
};
|
||||
|
||||
struct smb_version_operations smb21_operations = {
|
||||
@ -5173,6 +5245,7 @@ struct smb_version_operations smb21_operations = {
|
||||
.fiemap = smb3_fiemap,
|
||||
.llseek = smb3_llseek,
|
||||
.is_status_io_timeout = smb2_is_status_io_timeout,
|
||||
.is_network_name_deleted = smb2_is_network_name_deleted,
|
||||
};
|
||||
|
||||
struct smb_version_operations smb30_operations = {
|
||||
@ -5286,6 +5359,7 @@ struct smb_version_operations smb30_operations = {
|
||||
.fiemap = smb3_fiemap,
|
||||
.llseek = smb3_llseek,
|
||||
.is_status_io_timeout = smb2_is_status_io_timeout,
|
||||
.is_network_name_deleted = smb2_is_network_name_deleted,
|
||||
};
|
||||
|
||||
struct smb_version_operations smb311_operations = {
|
||||
@ -5399,6 +5473,7 @@ struct smb_version_operations smb311_operations = {
|
||||
.fiemap = smb3_fiemap,
|
||||
.llseek = smb3_llseek,
|
||||
.is_status_io_timeout = smb2_is_status_io_timeout,
|
||||
.is_network_name_deleted = smb2_is_network_name_deleted,
|
||||
};
|
||||
|
||||
struct smb_version_values smb20_values = {
|
||||
|
@ -814,8 +814,9 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
SMB3ANY_VERSION_STRING) == 0) {
|
||||
req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
|
||||
req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
|
||||
req->DialectCount = cpu_to_le16(2);
|
||||
total_len += 4;
|
||||
req->Dialects[2] = cpu_to_le16(SMB311_PROT_ID);
|
||||
req->DialectCount = cpu_to_le16(3);
|
||||
total_len += 6;
|
||||
} else if (strcmp(server->vals->version_string,
|
||||
SMBDEFAULT_VERSION_STRING) == 0) {
|
||||
req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
|
||||
@ -848,6 +849,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
memcpy(req->ClientGUID, server->client_guid,
|
||||
SMB2_CLIENT_GUID_SIZE);
|
||||
if ((server->vals->protocol_id == SMB311_PROT_ID) ||
|
||||
(strcmp(server->vals->version_string,
|
||||
SMB3ANY_VERSION_STRING) == 0) ||
|
||||
(strcmp(server->vals->version_string,
|
||||
SMBDEFAULT_VERSION_STRING) == 0))
|
||||
assemble_neg_contexts(req, server, &total_len);
|
||||
@ -883,6 +886,10 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
cifs_server_dbg(VFS,
|
||||
"SMB2.1 dialect returned but not requested\n");
|
||||
return -EIO;
|
||||
} else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
|
||||
/* ops set to 3.0 by default for default so update */
|
||||
server->ops = &smb311_operations;
|
||||
server->vals = &smb311_values;
|
||||
}
|
||||
} else if (strcmp(server->vals->version_string,
|
||||
SMBDEFAULT_VERSION_STRING) == 0) {
|
||||
@ -1042,10 +1049,11 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
SMB3ANY_VERSION_STRING) == 0) {
|
||||
pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
|
||||
pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
|
||||
pneg_inbuf->DialectCount = cpu_to_le16(2);
|
||||
/* structure is big enough for 3 dialects, sending only 2 */
|
||||
pneg_inbuf->Dialects[2] = cpu_to_le16(SMB311_PROT_ID);
|
||||
pneg_inbuf->DialectCount = cpu_to_le16(3);
|
||||
/* SMB 2.1 not included so subtract one dialect from len */
|
||||
inbuflen = sizeof(*pneg_inbuf) -
|
||||
(2 * sizeof(pneg_inbuf->Dialects[0]));
|
||||
(sizeof(pneg_inbuf->Dialects[0]));
|
||||
} else if (strcmp(server->vals->version_string,
|
||||
SMBDEFAULT_VERSION_STRING) == 0) {
|
||||
pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
|
||||
@ -1053,7 +1061,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
pneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
|
||||
pneg_inbuf->Dialects[3] = cpu_to_le16(SMB311_PROT_ID);
|
||||
pneg_inbuf->DialectCount = cpu_to_le16(4);
|
||||
/* structure is big enough for 3 dialects */
|
||||
/* structure is big enough for 4 dialects */
|
||||
inbuflen = sizeof(*pneg_inbuf);
|
||||
} else {
|
||||
/* otherwise specific dialect was requested */
|
||||
@ -1253,7 +1261,7 @@ SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
|
||||
cifs_ses_server(sess_data->ses),
|
||||
&rqst,
|
||||
&sess_data->buf0_type,
|
||||
CIFS_LOG_ERROR | CIFS_NEG_OP, &rsp_iov);
|
||||
CIFS_LOG_ERROR | CIFS_SESS_OP, &rsp_iov);
|
||||
cifs_small_buf_release(sess_data->iov[0].iov_base);
|
||||
memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec));
|
||||
|
||||
|
@ -851,17 +851,21 @@ DEFINE_SMB3_LEASE_ERR_EVENT(lease_err);
|
||||
|
||||
DECLARE_EVENT_CLASS(smb3_reconnect_class,
|
||||
TP_PROTO(__u64 currmid,
|
||||
__u64 conn_id,
|
||||
char *hostname),
|
||||
TP_ARGS(currmid, hostname),
|
||||
TP_ARGS(currmid, conn_id, hostname),
|
||||
TP_STRUCT__entry(
|
||||
__field(__u64, currmid)
|
||||
__field(__u64, conn_id)
|
||||
__field(char *, hostname)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->currmid = currmid;
|
||||
__entry->conn_id = conn_id;
|
||||
__entry->hostname = hostname;
|
||||
),
|
||||
TP_printk("server=%s current_mid=0x%llx",
|
||||
TP_printk("conn_id=0x%llx server=%s current_mid=%llu",
|
||||
__entry->conn_id,
|
||||
__entry->hostname,
|
||||
__entry->currmid)
|
||||
)
|
||||
@ -869,44 +873,56 @@ DECLARE_EVENT_CLASS(smb3_reconnect_class,
|
||||
#define DEFINE_SMB3_RECONNECT_EVENT(name) \
|
||||
DEFINE_EVENT(smb3_reconnect_class, smb3_##name, \
|
||||
TP_PROTO(__u64 currmid, \
|
||||
char *hostname), \
|
||||
TP_ARGS(currmid, hostname))
|
||||
__u64 conn_id, \
|
||||
char *hostname), \
|
||||
TP_ARGS(currmid, conn_id, hostname))
|
||||
|
||||
DEFINE_SMB3_RECONNECT_EVENT(reconnect);
|
||||
DEFINE_SMB3_RECONNECT_EVENT(partial_send_reconnect);
|
||||
|
||||
DECLARE_EVENT_CLASS(smb3_credit_class,
|
||||
TP_PROTO(__u64 currmid,
|
||||
__u64 conn_id,
|
||||
char *hostname,
|
||||
int credits,
|
||||
int credits_to_add),
|
||||
TP_ARGS(currmid, hostname, credits, credits_to_add),
|
||||
int credits_to_add,
|
||||
int in_flight),
|
||||
TP_ARGS(currmid, conn_id, hostname, credits, credits_to_add, in_flight),
|
||||
TP_STRUCT__entry(
|
||||
__field(__u64, currmid)
|
||||
__field(__u64, conn_id)
|
||||
__field(char *, hostname)
|
||||
__field(int, credits)
|
||||
__field(int, credits_to_add)
|
||||
__field(int, in_flight)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->currmid = currmid;
|
||||
__entry->conn_id = conn_id;
|
||||
__entry->hostname = hostname;
|
||||
__entry->credits = credits;
|
||||
__entry->credits_to_add = credits_to_add;
|
||||
__entry->in_flight = in_flight;
|
||||
),
|
||||
TP_printk("server=%s current_mid=0x%llx credits=%d credits_to_add=%d",
|
||||
TP_printk("conn_id=0x%llx server=%s current_mid=%llu "
|
||||
"credits=%d credit_change=%d in_flight=%d",
|
||||
__entry->conn_id,
|
||||
__entry->hostname,
|
||||
__entry->currmid,
|
||||
__entry->credits,
|
||||
__entry->credits_to_add)
|
||||
__entry->credits_to_add,
|
||||
__entry->in_flight)
|
||||
)
|
||||
|
||||
#define DEFINE_SMB3_CREDIT_EVENT(name) \
|
||||
DEFINE_EVENT(smb3_credit_class, smb3_##name, \
|
||||
TP_PROTO(__u64 currmid, \
|
||||
__u64 conn_id, \
|
||||
char *hostname, \
|
||||
int credits, \
|
||||
int credits_to_add), \
|
||||
TP_ARGS(currmid, hostname, credits, credits_to_add))
|
||||
int credits_to_add, \
|
||||
int in_flight), \
|
||||
TP_ARGS(currmid, conn_id, hostname, credits, credits_to_add, in_flight))
|
||||
|
||||
DEFINE_SMB3_CREDIT_EVENT(reconnect_with_invalid_credits);
|
||||
DEFINE_SMB3_CREDIT_EVENT(reconnect_detected);
|
||||
|
@ -445,7 +445,7 @@ unmask:
|
||||
*/
|
||||
server->tcpStatus = CifsNeedReconnect;
|
||||
trace_smb3_partial_send_reconnect(server->CurrentMid,
|
||||
server->hostname);
|
||||
server->conn_id, server->hostname);
|
||||
}
|
||||
smbd_done:
|
||||
if (rc < 0 && rc != -EINTR)
|
||||
@ -527,7 +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;
|
||||
int scredits, in_flight;
|
||||
|
||||
if (timeout < 0)
|
||||
t = MAX_JIFFY_OFFSET;
|
||||
@ -551,23 +551,39 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
|
||||
server->max_in_flight = server->in_flight;
|
||||
*credits -= 1;
|
||||
*instance = server->reconnect_instance;
|
||||
scredits = *credits;
|
||||
in_flight = server->in_flight;
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->conn_id, server->hostname, scredits, -1, in_flight);
|
||||
cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
|
||||
__func__, 1, scredits);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (*credits < num_credits) {
|
||||
scredits = *credits;
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
cifs_num_waiters_inc(server);
|
||||
rc = wait_event_killable_timeout(server->request_q,
|
||||
has_credits(server, credits, num_credits), t);
|
||||
cifs_num_waiters_dec(server);
|
||||
if (!rc) {
|
||||
spin_lock(&server->req_lock);
|
||||
scredits = *credits;
|
||||
in_flight = server->in_flight;
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
trace_smb3_credit_timeout(server->CurrentMid,
|
||||
server->hostname, num_credits, 0);
|
||||
server->conn_id, server->hostname, scredits,
|
||||
num_credits, in_flight);
|
||||
cifs_server_dbg(VFS, "wait timed out after %d ms\n",
|
||||
timeout);
|
||||
return -ENOTSUPP;
|
||||
timeout);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (rc == -ERESTARTSYS)
|
||||
return -ERESTARTSYS;
|
||||
@ -595,6 +611,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
|
||||
server->in_flight > 2 * MAX_COMPOUND &&
|
||||
*credits <= MAX_COMPOUND) {
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
cifs_num_waiters_inc(server);
|
||||
rc = wait_event_killable_timeout(
|
||||
server->request_q,
|
||||
@ -603,13 +620,18 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
|
||||
t);
|
||||
cifs_num_waiters_dec(server);
|
||||
if (!rc) {
|
||||
spin_lock(&server->req_lock);
|
||||
scredits = *credits;
|
||||
in_flight = server->in_flight;
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
trace_smb3_credit_timeout(
|
||||
server->CurrentMid,
|
||||
server->hostname, num_credits,
|
||||
0);
|
||||
server->CurrentMid,
|
||||
server->conn_id, server->hostname,
|
||||
scredits, num_credits, in_flight);
|
||||
cifs_server_dbg(VFS, "wait timed out after %d ms\n",
|
||||
timeout);
|
||||
return -ENOTSUPP;
|
||||
timeout);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (rc == -ERESTARTSYS)
|
||||
return -ERESTARTSYS;
|
||||
@ -625,16 +647,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;
|
||||
}
|
||||
scredits = *credits;
|
||||
in_flight = server->in_flight;
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
trace_smb3_add_credits(server->CurrentMid,
|
||||
server->hostname, scredits, -(num_credits));
|
||||
server->conn_id, server->hostname, scredits,
|
||||
-(num_credits), in_flight);
|
||||
cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
|
||||
__func__, num_credits, scredits);
|
||||
break;
|
||||
@ -656,13 +680,13 @@ wait_for_compound_request(struct TCP_Server_Info *server, int num,
|
||||
const int flags, unsigned int *instance)
|
||||
{
|
||||
int *credits;
|
||||
int scredits, sin_flight;
|
||||
int scredits, in_flight;
|
||||
|
||||
credits = server->ops->get_credits_field(server, flags & CIFS_OP_MASK);
|
||||
|
||||
spin_lock(&server->req_lock);
|
||||
scredits = *credits;
|
||||
sin_flight = server->in_flight;
|
||||
in_flight = server->in_flight;
|
||||
|
||||
if (*credits < num) {
|
||||
/*
|
||||
@ -684,10 +708,11 @@ wait_for_compound_request(struct TCP_Server_Info *server, int num,
|
||||
if (server->in_flight == 0) {
|
||||
spin_unlock(&server->req_lock);
|
||||
trace_smb3_insufficient_credits(server->CurrentMid,
|
||||
server->hostname, scredits, sin_flight);
|
||||
server->conn_id, server->hostname, scredits,
|
||||
num, in_flight);
|
||||
cifs_dbg(FYI, "%s: %d requests in flight, needed %d total=%d\n",
|
||||
__func__, sin_flight, num, scredits);
|
||||
return -ENOTSUPP;
|
||||
__func__, in_flight, num, scredits);
|
||||
return -EDEADLK;
|
||||
}
|
||||
}
|
||||
spin_unlock(&server->req_lock);
|
||||
@ -1171,7 +1196,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||
/*
|
||||
* Compounding is never used during session establish.
|
||||
*/
|
||||
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP))
|
||||
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP))
|
||||
smb311_update_preauth_hash(ses, rqst[0].rq_iov,
|
||||
rqst[0].rq_nvec);
|
||||
|
||||
@ -1236,7 +1261,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||
/*
|
||||
* Compounding is never used during session establish.
|
||||
*/
|
||||
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) {
|
||||
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) {
|
||||
struct kvec iov = {
|
||||
.iov_base = resp_iov[0].iov_base,
|
||||
.iov_len = resp_iov[0].iov_len
|
||||
|
Loading…
Reference in New Issue
Block a user