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:
Linus Torvalds 2021-02-26 14:09:41 -08:00
commit c19798af2e
26 changed files with 886 additions and 417 deletions

View File

@ -5,10 +5,10 @@ Authors
Original Author 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: 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 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 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. 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) - Ronnie Sahlberg (for SMB3 xattr work, bug fixes, and lots of great work on compounding)
- Shirish Pargaonkar (for many ACL patches over the years) - Shirish Pargaonkar (for many ACL patches over the years)
- Sachin Prabhu (many bug fixes, including for reconnect, copy offload and security) - 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) - Long Li (some great work on RDMA, SMB Direct)

View File

@ -3,6 +3,7 @@ Changes
======= =======
See https://wiki.samba.org/index.php/LinuxCIFSKernel for summary See https://wiki.samba.org/index.php/LinuxCIFSKernel for summary
information (that may be easier to read than parsing the output of information about fixes/improvements to CIFS/SMB2/SMB3 support (changes
"git log fs/cifs") about fixes/improvements to CIFS/SMB2/SMB3 support (changes
to cifs.ko module) by kernel version (and cifs internal module version). 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.

View File

@ -7,19 +7,19 @@ Introduction
protocol which was the successor to the Server Message Block protocol which was the successor to the Server Message Block
(SMB) protocol, the native file sharing mechanism for most early (SMB) protocol, the native file sharing mechanism for most early
PC operating systems. New and improved versions of CIFS are now PC operating systems. New and improved versions of CIFS are now
called SMB2 and SMB3. Use of SMB3 (and later, including SMB3.1.1) called SMB2 and SMB3. Use of SMB3 (and later, including SMB3.1.1
is strongly preferred over using older dialects like CIFS due to the most current dialect) is strongly preferred over using older
security reasons. All modern dialects, including the most recent, dialects like CIFS due to security reasons. All modern dialects,
SMB3.1.1 are supported by the CIFS VFS module. The SMB3 protocol including the most recent, SMB3.1.1, are supported by the CIFS VFS
is implemented and supported by all major file servers module. The SMB3 protocol is implemented and supported by all major
such as all modern versions of Windows (including Windows 2016 file servers such as Windows (including Windows 2019 Server), as
Server), as well as by Samba (which provides excellent well as by Samba (which provides excellent CIFS/SMB2/SMB3 server
CIFS/SMB2/SMB3 server support and tools for Linux and many other support and tools for Linux and many other operating systems).
operating systems). Apple systems also support SMB3 well, as Apple systems also support SMB3 well, as do most Network Attached
do most Network Attached Storage vendors, so this network Storage vendors, so this network filesystem client can mount to a
filesystem client can mount to a wide variety of systems. wide variety of systems. It also supports mounting to the cloud
It also supports mounting to the cloud (for example (for example Microsoft Azure), including the necessary security
Microsoft Azure), including the necessary security features. features.
The intent of this module is to provide the most advanced network The intent of this module is to provide the most advanced network
file system function for SMB3 compliant servers, including advanced file system function for SMB3 compliant servers, including advanced
@ -27,8 +27,8 @@ Introduction
POSIX compliance, secure per-user session establishment, encryption, POSIX compliance, secure per-user session establishment, encryption,
high performance safe distributed caching (leases/oplocks), optional packet high performance safe distributed caching (leases/oplocks), optional packet
signing, large files, Unicode support and other internationalization signing, large files, Unicode support and other internationalization
improvements. Since both Samba server and this filesystem client support improvements. Since both Samba server and this filesystem client support the
the CIFS Unix extensions (and in the future SMB3 POSIX extensions), CIFS Unix extensions, and the Linux client also suppors SMB3 POSIX extensions,
the combination can provide a reasonable alternative to other network and the combination can provide a reasonable alternative to other network and
cluster file systems for fileserving in some Linux to Linux environments, cluster file systems for fileserving in some Linux to Linux environments,
not just in Linux to Windows (or Linux to Mac) environments. not just in Linux to Windows (or Linux to Mac) environments.

View File

@ -13,24 +13,26 @@ is a partial list of the known problems and missing features:
a) SMB3 (and SMB3.1.1) missing optional features: a) SMB3 (and SMB3.1.1) missing optional features:
- multichannel (started), integration with RDMA - multichannel (partially integrated), integration of multichannel with RDMA
- directory leases (improved metadata caching), started (root dir only) - directory leases (improved metadata caching). Currently only implemented for root dir
- T10 copy offload ie "ODX" (copy chunk, and "Duplicate Extents" ioctl - T10 copy offload ie "ODX" (copy chunk, and "Duplicate Extents" ioctl
currently the only two server side copy mechanisms supported) currently the only two server side copy mechanisms supported)
b) improved sparse file support (fiemap and SEEK_HOLE are implemented 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 c) Directory entry caching relies on a 1 second timer, rather than
using Directory Leases, currently only the root file handle is cached longer 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 d) quota support (needs minor kernel change since quota calls otherwise
to make it to network filesystems or deviceless filesystems) won't make it to network filesystems or deviceless filesystems).
e) Additional use cases can be optimized to use "compounding" (e.g. e) Additional use cases can be optimized to use "compounding" (e.g.
open/query/close and open/setinfo/close) to reduce the number of open/query/close and open/setinfo/close) to reduce the number of
roundtrips to the server and improve performance. Various cases 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 using compounding but more can be done. In addition we could
significantly reduce redundant opens by using deferred close (with significantly reduce redundant opens by using deferred close (with
handle caching leases) and better using reference counters on file 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 metadata attributes easier from tools (e.g. extending what was done
in smb-info tool). 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?) m) improved stats gathering tools (perhaps integration with nfsometer?)
to extend and make easier to use what is currently in /proc/fs/cifs/Stats 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) 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 p) Expand support for witness protocol to allow for notification of share
tool listening on witness protocol RPC) to allow for notification of share move, and server network adapter changes. Currently only notifications by
move, server failover, and server adapter changes. And also improve other the witness protocol for server move is supported by the Linux client.
failover scenarios, e.g. when client knows multiple DFS entries point to
different servers, and the server we are connected to has gone down.
q) Allow mount.cifs to be more verbose in reporting errors with dialect 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. 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 secure) CIFS dialect can be disabled in environments that don't need it
and simplify the code. and simplify the code.
v) POSIX Extensions for SMB3.1.1 (started, create and mkdir support added v) Additional testing of POSIX Extensions for SMB3.1.1
so far).
w) Add support for additional strong encryption types, and additional spnego 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 x) Finish support for SMB3.1.1 compression

View File

@ -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 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): 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 Allowing User Mounts
==================== ====================

View File

@ -133,11 +133,12 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
{ {
struct TCP_Server_Info *server = chan->server; struct TCP_Server_Info *server = chan->server;
seq_printf(m, "\t\tChannel %d Number of credits: %d Dialect 0x%x " seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx"
"TCP status: %d Instance: %d Local Users To Server: %d " "\n\t\tNumber of credits: %d Dialect 0x%x"
"SecMode: 0x%x Req On Wire: %d In Send: %d " "\n\t\tTCP status: %d Instance: %d"
"In MaxReq Wait: %d\n", "\n\t\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d"
i+1, "\n\t\tIn Send: %d In MaxReq Wait: %d",
i+1, server->conn_id,
server->credits, server->credits,
server->dialect, server->dialect,
server->tcpStatus, 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 TCP_Server_Info *server;
struct cifs_ses *ses; struct cifs_ses *ses;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
int i, j; int c, i, j;
seq_puts(m, seq_puts(m,
"Display Internal CIFS Data Structures for Debugging\n" "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_putc(m, '\n');
seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize); seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize);
seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid); 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); spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp1, &cifs_tcp_ses_list) { list_for_each(tmp1, &cifs_tcp_ses_list) {
server = list_entry(tmp1, struct TCP_Server_Info, server = list_entry(tmp1, struct TCP_Server_Info,
tcp_ses_list); 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 #ifdef CONFIG_CIFS_SMB_DIRECT
if (!server->rdma) if (!server->rdma)
goto skip_rdma; goto skip_rdma;
@ -362,46 +374,48 @@ skip_rdma:
if (server->posix_ext_supported) if (server->posix_ext_supported)
seq_printf(m, " posix"); seq_printf(m, " posix");
i++;
list_for_each(tmp2, &server->smb_ses_list) {
ses = list_entry(tmp2, struct cifs_ses,
smb_ses_list);
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,
ses->capabilities, ses->status);
if (ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST)
seq_printf(m, "Guest\t");
else if (ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
seq_printf(m, "Anonymous\t");
} 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,
ses->ses_count, ses->serverOS, ses->serverNOS,
ses->capabilities, ses->status);
}
seq_printf(m,"Security type: %s\n",
get_security_type_str(server->ops->select_sectype(server, ses->sectype)));
if (server->rdma) if (server->rdma)
seq_printf(m, "RDMA\n\t"); seq_printf(m, "\nRDMA ");
seq_printf(m, "TCP status: %d Instance: %d\n\tLocal Users To " seq_printf(m, "\nTCP status: %d Instance: %d"
"Server: %d SecMode: 0x%x Req On Wire: %d", "\nLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d",
server->tcpStatus, server->tcpStatus,
server->reconnect_instance, server->reconnect_instance,
server->srv_count, server->srv_count,
server->sec_mode, in_flight(server)); server->sec_mode, in_flight(server));
seq_printf(m, " In Send: %d In MaxReq Wait: %d", seq_printf(m, "\nIn Send: %d In MaxReq Wait: %d",
atomic_read(&server->in_send), atomic_read(&server->in_send),
atomic_read(&server->num_waiters)); 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\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 ");
else if (ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
seq_printf(m, "Anonymous ");
} else {
seq_printf(m,
"\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, "\n\tSecurity type: %s ",
get_security_type_str(server->ops->select_sectype(server, ses->sectype)));
/* dump session id helpful for use with network trace */ /* dump session id helpful for use with network trace */
seq_printf(m, " SessionId: 0x%llx", ses->Suid); seq_printf(m, " SessionId: 0x%llx", ses->Suid);
if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
@ -414,7 +428,7 @@ skip_rdma:
from_kuid(&init_user_ns, ses->cred_uid)); from_kuid(&init_user_ns, ses->cred_uid));
if (ses->chan_count > 1) { 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); ses->chan_count-1);
for (j = 1; j < ses->chan_count; j++) for (j = 1; j < ses->chan_count; j++)
cifs_dump_channel(m, j, &ses->chans[j]); cifs_dump_channel(m, j, &ses->chans[j]);
@ -437,13 +451,30 @@ skip_rdma:
cifs_debug_tcon(m, tcon); cifs_debug_tcon(m, tcon);
} }
seq_puts(m, "\n\tMIDs:\n"); spin_lock(&ses->iface_lock);
if (ses->iface_count)
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, "\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); spin_lock(&GlobalMid_Lock);
list_for_each(tmp3, &server->pending_mid_q) { list_for_each(tmp3, &server->pending_mid_q) {
mid_entry = list_entry(tmp3, struct mid_q_entry, mid_entry = list_entry(tmp3, struct mid_q_entry,
qhead); qhead);
seq_printf(m, "\tState: %d com: %d pid:" seq_printf(m, "\n\tState: %d com: %d pid:"
" %d cbdata: %p mid %llu\n", " %d cbdata: %p mid %llu\n",
mid_entry->mid_state, mid_entry->mid_state,
le16_to_cpu(mid_entry->command), le16_to_cpu(mid_entry->command),
@ -452,23 +483,11 @@ skip_rdma:
mid_entry->mid); mid_entry->mid);
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
seq_printf(m, "\n--\n");
}
if (c == 0)
seq_printf(m, "\n\t[NONE]");
spin_lock(&ses->iface_lock);
if (ses->iface_count)
seq_printf(m, "\n\tServer interfaces: %zu\n",
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);
cifs_dump_iface(m, iface);
if (is_ses_using_iface(ses, iface))
seq_puts(m, "\t\t[CONNECTED]\n");
}
spin_unlock(&ses->iface_lock);
}
}
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
seq_putc(m, '\n'); seq_putc(m, '\n');

View File

@ -272,7 +272,7 @@ static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon)
if (IS_ERR(share_name)) { if (IS_ERR(share_name)) {
int ret; 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", cifs_dbg(VFS, "%s: failed to extract share name from target '%s': %d\n",
__func__, tcon->treeName, ret); __func__, tcon->treeName, ret);
kfree(net_name); kfree(net_name);

View File

@ -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 */ return true; /* well known sid found, uid returned */
} }
static void static __u16
cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src) cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
{ {
int i; int i;
__u16 size = 1 + 1 + 6;
dst->revision = src->revision; dst->revision = src->revision;
dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); 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]; dst->authority[i] = src->authority[i];
for (i = 0; i < dst->num_subauth; ++i) for (i = 0; i < dst->num_subauth; ++i)
dst->sub_auth[i] = src->sub_auth[i]; dst->sub_auth[i] = src->sub_auth[i];
size += (dst->num_subauth * 4);
return size;
} }
static int static int
@ -521,8 +525,11 @@ exit_cifs_idmap(void)
} }
/* copy ntsd, owner sid, and group sid from a security descriptor to another */ /* copy ntsd, owner sid, and group sid from a security descriptor to another */
static void copy_sec_desc(const struct cifs_ntsd *pntsd, static __u32 copy_sec_desc(const struct cifs_ntsd *pntsd,
struct cifs_ntsd *pnntsd, __u32 sidsoffset) 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 *owner_sid_ptr, *group_sid_ptr;
struct cifs_sid *nowner_sid_ptr, *ngroup_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)); pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid));
/* copy owner sid */ /* copy owner sid */
if (pownersid)
owner_sid_ptr = pownersid;
else
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->osidoffset)); le32_to_cpu(pntsd->osidoffset));
nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset); nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr); cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr);
/* copy group sid */ /* copy group sid */
if (pgrpsid)
group_sid_ptr = pgrpsid;
else
group_sid_ptr = (struct cifs_sid *)((char *)pntsd + group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset)); le32_to_cpu(pntsd->gsidoffset));
ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset + ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
sizeof(struct cifs_sid)); sizeof(struct cifs_sid));
cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr); 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; 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, static __u16 fill_ace_for_sid(struct cifs_ace *pntace,
const struct cifs_sid *psid, __u64 nmode, const struct cifs_sid *psid, __u64 nmode,
umode_t bits, __u8 access_type, umode_t bits, __u8 access_type,
@ -907,29 +939,30 @@ unsigned int setup_special_user_owner_ACE(struct cifs_ace *pntace)
return ace_size; return ace_size;
} }
static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid, static void populate_new_aces(char *nacl_base,
struct cifs_sid *pgrpsid, __u64 *pnmode, bool modefromsid) 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; __u64 nmode;
u32 num_aces = 0;
u16 nsize = 0;
__u64 user_mode; __u64 user_mode;
__u64 group_mode; __u64 group_mode;
__u64 other_mode; __u64 other_mode;
__u64 deny_user_mode = 0; __u64 deny_user_mode = 0;
__u64 deny_group_mode = 0; __u64 deny_group_mode = 0;
bool sticky_set = false; bool sticky_set = false;
struct cifs_ace *pnntace = NULL;
pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
nmode = *pnmode; nmode = *pnmode;
num_aces = *pnum_aces;
nsize = *pnsize;
if (modefromsid) { if (modefromsid) {
struct cifs_ace *pntace = pnntace = (struct cifs_ace *) (nacl_base + nsize);
(struct cifs_ace *)((char *)pnndacl + size); nsize += setup_special_mode_ACE(pnntace, nmode);
size += setup_special_mode_ACE(pntace, nmode);
num_aces++; num_aces++;
goto set_size; goto set_size;
} }
@ -966,40 +999,170 @@ static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
sticky_set = true; sticky_set = true;
if (deny_user_mode) { if (deny_user_mode) {
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), pnntace = (struct cifs_ace *) (nacl_base + nsize);
pownersid, deny_user_mode, 0700, ACCESS_DENIED, false); nsize += fill_ace_for_sid(pnntace, pownersid, deny_user_mode,
0700, ACCESS_DENIED, false);
num_aces++; num_aces++;
} }
/* Group DENY ACE does not conflict with owner ALLOW ACE. Keep in preferred order*/ /* Group DENY ACE does not conflict with owner ALLOW ACE. Keep in preferred order*/
if (deny_group_mode && !(deny_group_mode & (user_mode >> 3))) { if (deny_group_mode && !(deny_group_mode & (user_mode >> 3))) {
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), pnntace = (struct cifs_ace *) (nacl_base + nsize);
pgrpsid, deny_group_mode, 0070, ACCESS_DENIED, false); nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode,
0070, ACCESS_DENIED, false);
num_aces++; 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++; num_aces++;
/* Group DENY ACE conflicts with owner ALLOW ACE. So keep it after. */ /* Group DENY ACE conflicts with owner ALLOW ACE. So keep it after. */
if (deny_group_mode && (deny_group_mode & (user_mode >> 3))) { if (deny_group_mode && (deny_group_mode & (user_mode >> 3))) {
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), pnntace = (struct cifs_ace *) (nacl_base + nsize);
pgrpsid, deny_group_mode, 0070, ACCESS_DENIED, false); nsize += fill_ace_for_sid(pnntace, pgrpsid, deny_group_mode,
0070, ACCESS_DENIED, false);
num_aces++; 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++; 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++; num_aces++;
set_size: 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->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; return 0;
} }
static int parse_sid(struct cifs_sid *psid, char *end_of_acl) static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
{ {
/* BB need to add parm so we can store the SID BB */ /* 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 */ /* Convert permission bits from mode to equivalent CIFS ACL */
static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, 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) bool mode_from_sid, bool id_from_sid, int *aclflag)
{ {
int rc = 0; int rc = 0;
@ -1102,39 +1265,59 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
__u32 ndacloffset; __u32 ndacloffset;
__u32 sidsoffset; __u32 sidsoffset;
struct cifs_sid *owner_sid_ptr, *group_sid_ptr; 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 *dacl_ptr = NULL; /* no need for SACL ptr */
struct cifs_acl *ndacl_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;
}
}
if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->osidoffset)); le32_to_cpu(pntsd->osidoffset));
group_sid_ptr = (struct cifs_sid *)((char *)pntsd + group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset)); le32_to_cpu(pntsd->gsidoffset));
dacloffset = le32_to_cpu(pntsd->dacloffset);
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
ndacloffset = sizeof(struct cifs_ntsd); ndacloffset = sizeof(struct cifs_ntsd);
ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
ndacl_ptr->revision = dacl_ptr->revision; ndacl_ptr->revision =
ndacl_ptr->size = 0; dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION);
ndacl_ptr->num_aces = 0;
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); pnmode, mode_from_sid);
sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
/* copy sec desc control portion & owner and group sids */ /* copy the non-dacl portion of secdesc */
copy_sec_desc(pntsd, pnntsd, sidsoffset); *pnsecdesclen = copy_sec_desc(pntsd, pnntsd, sidsoffset,
*aclflag = CIFS_ACL_DACL; NULL, NULL);
*aclflag |= CIFS_ACL_DACL;
} else { } 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 */ if (uid_valid(uid)) { /* chown */
uid_t id; uid_t id;
owner_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
le32_to_cpu(pnntsd->osidoffset));
nowner_sid_ptr = kmalloc(sizeof(struct cifs_sid), nowner_sid_ptr = kmalloc(sizeof(struct cifs_sid),
GFP_KERNEL); GFP_KERNEL);
if (!nowner_sid_ptr) if (!nowner_sid_ptr) {
return -ENOMEM; rc = -ENOMEM;
goto chown_chgrp_exit;
}
id = from_kuid(&init_user_ns, uid); id = from_kuid(&init_user_ns, uid);
if (id_from_sid) { if (id_from_sid) {
struct owner_sid *osid = (struct owner_sid *)nowner_sid_ptr; 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[0] = cpu_to_le32(88);
osid->SubAuthorities[1] = cpu_to_le32(1); osid->SubAuthorities[1] = cpu_to_le32(1);
osid->SubAuthorities[2] = cpu_to_le32(id); osid->SubAuthorities[2] = cpu_to_le32(id);
} else { /* lookup sid with upcall */ } else { /* lookup sid with upcall */
rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr); rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr);
if (rc) { if (rc) {
cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n", cifs_dbg(FYI, "%s: Mapping error %d for owner id %d\n",
__func__, rc, id); __func__, rc, id);
kfree(nowner_sid_ptr); goto chown_chgrp_exit;
return rc;
} }
} }
cifs_copy_sid(owner_sid_ptr, nowner_sid_ptr); *aclflag |= CIFS_ACL_OWNER;
kfree(nowner_sid_ptr);
*aclflag = CIFS_ACL_OWNER;
} }
if (gid_valid(gid)) { /* chgrp */ if (gid_valid(gid)) { /* chgrp */
gid_t id; gid_t id;
group_sid_ptr = (struct cifs_sid *)((char *)pnntsd +
le32_to_cpu(pnntsd->gsidoffset));
ngroup_sid_ptr = kmalloc(sizeof(struct cifs_sid), ngroup_sid_ptr = kmalloc(sizeof(struct cifs_sid),
GFP_KERNEL); GFP_KERNEL);
if (!ngroup_sid_ptr) if (!ngroup_sid_ptr) {
return -ENOMEM; rc = -ENOMEM;
goto chown_chgrp_exit;
}
id = from_kgid(&init_user_ns, gid); id = from_kgid(&init_user_ns, gid);
if (id_from_sid) { if (id_from_sid) {
struct owner_sid *gsid = (struct owner_sid *)ngroup_sid_ptr; 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[0] = cpu_to_le32(88);
gsid->SubAuthorities[1] = cpu_to_le32(2); gsid->SubAuthorities[1] = cpu_to_le32(2);
gsid->SubAuthorities[2] = cpu_to_le32(id); gsid->SubAuthorities[2] = cpu_to_le32(id);
} else { /* lookup sid with upcall */ } else { /* lookup sid with upcall */
rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr); rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr);
if (rc) { if (rc) {
cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n", cifs_dbg(FYI, "%s: Mapping error %d for group id %d\n",
__func__, rc, id); __func__, rc, id);
goto chown_chgrp_exit;
}
}
*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); kfree(ngroup_sid_ptr);
return rc;
}
}
cifs_copy_sid(group_sid_ptr, ngroup_sid_ptr);
kfree(ngroup_sid_ptr);
*aclflag = CIFS_ACL_GROUP;
}
} }
return rc; return rc;
@ -1384,6 +1581,9 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
int rc = 0; int rc = 0;
int aclflag = CIFS_ACL_DACL; /* default flag to set */ int aclflag = CIFS_ACL_DACL; /* default flag to set */
__u32 secdesclen = 0; __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 *pntsd = NULL; /* acl obtained from server */
struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 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; 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) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)
mode_from_sid = true; mode_from_sid = true;
else else
@ -1438,7 +1624,42 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
else else
id_from_sid = false; 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); mode_from_sid, id_from_sid, &aclflag);
cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); 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) { if (!rc) {
/* Set the security descriptor */ /* 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_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
} }
cifs_put_tlink(tlink); cifs_put_tlink(tlink);

View File

@ -31,8 +31,8 @@
#define EXEC_BIT 0x1 #define EXEC_BIT 0x1
#define ACL_OWNER_MASK 0700 #define ACL_OWNER_MASK 0700
#define ACL_GROUP_MASK 0770 #define ACL_GROUP_MASK 0070
#define ACL_EVERYONE_MASK 0777 #define ACL_EVERYONE_MASK 0007
#define UBITSHIFT 6 #define UBITSHIFT 6
#define GBITSHIFT 3 #define GBITSHIFT 3

View File

@ -568,15 +568,15 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
return rc; return rc;
} }
} else { } else {
/* We use ses->serverName if no domain name available */ /* We use ses->ip_addr if no domain name available */
len = strlen(ses->serverName); len = strlen(ses->ip_addr);
server = kmalloc(2 + (len * 2), GFP_KERNEL); server = kmalloc(2 + (len * 2), GFP_KERNEL);
if (server == NULL) { if (server == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
return rc; return rc;
} }
len = cifs_strtoUTF16((__le16 *)server, ses->serverName, len, len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len,
nls_cp); nls_cp);
rc = rc =
crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,

View File

@ -638,8 +638,18 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
seq_printf(s, ",snapshot=%llu", tcon->snapshot_time); seq_printf(s, ",snapshot=%llu", tcon->snapshot_time);
if (tcon->handle_timeout) if (tcon->handle_timeout)
seq_printf(s, ",handletimeout=%u", 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) if (tcon->ses->chan_max > 1)
seq_printf(s, ",multichannel,max_channels=%zu", seq_printf(s, ",multichannel,max_channels=%zu",
@ -1526,6 +1536,7 @@ init_cifs(void)
*/ */
atomic_set(&sesInfoAllocCount, 0); atomic_set(&sesInfoAllocCount, 0);
atomic_set(&tconInfoAllocCount, 0); atomic_set(&tconInfoAllocCount, 0);
atomic_set(&tcpSesNextId, 0);
atomic_set(&tcpSesAllocCount, 0); atomic_set(&tcpSesAllocCount, 0);
atomic_set(&tcpSesReconnectCount, 0); atomic_set(&tcpSesReconnectCount, 0);
atomic_set(&tconInfoReconnectCount, 0); atomic_set(&tconInfoReconnectCount, 0);

View File

@ -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; extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */ #endif /* CONFIG_CIFS_NFSD_EXPORT */
#define CIFS_VERSION "2.30" #define CIFS_VERSION "2.31"
#endif /* _CIFSFS_H */ #endif /* _CIFSFS_H */

View File

@ -21,6 +21,7 @@
#include <linux/in.h> #include <linux/in.h>
#include <linux/in6.h> #include <linux/in6.h>
#include <linux/inet.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mempool.h> #include <linux/mempool.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
@ -504,6 +505,8 @@ struct smb_version_operations {
loff_t (*llseek)(struct file *, struct cifs_tcon *, loff_t, int); loff_t (*llseek)(struct file *, struct cifs_tcon *, loff_t, int);
/* Check for STATUS_IO_TIMEOUT */ /* Check for STATUS_IO_TIMEOUT */
bool (*is_status_io_timeout)(char *buf); 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 { struct smb_version_values {
@ -577,6 +580,7 @@ inc_rfc1001_len(void *buf, int count)
struct TCP_Server_Info { struct TCP_Server_Info {
struct list_head tcp_ses_list; struct list_head tcp_ses_list;
struct list_head smb_ses_list; struct list_head smb_ses_list;
__u64 conn_id; /* connection identifier (useful for debugging) */
int srv_count; /* reference counter */ int srv_count; /* reference counter */
/* 15 character server name + 0x20 16th byte indicating type = srv */ /* 15 character server name + 0x20 16th byte indicating type = srv */
char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; 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 linux_uid; /* overriding owner of files on the mount */
kuid_t cred_uid; /* owner of credentials */ kuid_t cred_uid; /* owner of credentials */
unsigned int capabilities; 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 char *user_name; /* must not be null except during init of sess
and after mount option parsing we fill it */ and after mount option parsing we fill it */
char *domainName; char *domainName;
@ -1704,7 +1708,9 @@ static inline bool is_retryable_error(int error)
#define CIFS_ECHO_OP 0x080 /* echo request */ #define CIFS_ECHO_OP 0x080 /* echo request */
#define CIFS_OBREAK_OP 0x0100 /* oplock break request */ #define CIFS_OBREAK_OP 0x0100 /* oplock break request */
#define CIFS_NEG_OP 0x0200 /* negotiate 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_HAS_CREDITS 0x0400 /* already has credits */
#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */ #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 sesInfoAllocCount;
GLOBAL_EXTERN atomic_t tconInfoAllocCount; GLOBAL_EXTERN atomic_t tconInfoAllocCount;
GLOBAL_EXTERN atomic_t tcpSesNextId;
GLOBAL_EXTERN atomic_t tcpSesAllocCount; GLOBAL_EXTERN atomic_t tcpSesAllocCount;
GLOBAL_EXTERN atomic_t tcpSesReconnectCount; GLOBAL_EXTERN atomic_t tcpSesReconnectCount;
GLOBAL_EXTERN atomic_t tconInfoReconnectCount; GLOBAL_EXTERN atomic_t tconInfoReconnectCount;

View File

@ -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 void dequeue_mid(struct mid_q_entry *mid, bool malformed);
extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
unsigned int to_read); 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, extern int cifs_read_page_from_socket(struct TCP_Server_Info *server,
struct page *page, struct page *page,
unsigned int page_offset, unsigned int page_offset,

View File

@ -1451,8 +1451,8 @@ cifs_discard_remaining_data(struct TCP_Server_Info *server)
while (remaining > 0) { while (remaining > 0) {
int length; int length;
length = cifs_read_from_socket(server, server->bigbuf, length = cifs_discard_from_socket(server,
min_t(unsigned int, remaining, min_t(size_t, remaining,
CIFSMaxBufSize + MAX_HEADER_SIZE(server))); CIFSMaxBufSize + MAX_HEADER_SIZE(server)));
if (length < 0) if (length < 0)
return length; return length;

View File

@ -242,7 +242,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
server->max_read = 0; server->max_read = 0;
cifs_dbg(FYI, "Mark tcp session as need reconnect\n"); 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) /* before reconnecting the tcp session, mark the smb session (uid)
and the tid bad so they are not used until reconnected */ 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); 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 int
cifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page, cifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page,
unsigned int page_offset, unsigned int to_read) 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) smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
{ {
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer;
int scredits = server->credits; int scredits, in_flight;
/* /*
* SMB1 does not use credits. * SMB1 does not use credits.
@ -857,12 +874,14 @@ smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
if (shdr->CreditRequest) { if (shdr->CreditRequest) {
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
server->credits += le16_to_cpu(shdr->CreditRequest); server->credits += le16_to_cpu(shdr->CreditRequest);
scredits = server->credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
wake_up(&server->request_q); wake_up(&server->request_q);
trace_smb3_add_credits(server->CurrentMid, trace_smb3_add_credits(server->CurrentMid,
server->hostname, scredits, server->conn_id, server->hostname, scredits,
le16_to_cpu(shdr->CreditRequest)); le16_to_cpu(shdr->CreditRequest), in_flight);
cifs_server_dbg(FYI, "%s: added %u credits total=%d\n", cifs_server_dbg(FYI, "%s: added %u credits total=%d\n",
__func__, le16_to_cpu(shdr->CreditRequest), __func__, le16_to_cpu(shdr->CreditRequest),
scredits); scredits);
@ -993,6 +1012,10 @@ next_pdu:
if (mids[i] != NULL) { if (mids[i] != NULL) {
mids[i]->resp_buf_size = server->pdu_size; 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) if (!mids[i]->multiRsp || mids[i]->multiEnd)
mids[i]->callback(mids[i]); mids[i]->callback(mids[i]);
@ -1317,6 +1340,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
goto out_err_crypto_release; goto out_err_crypto_release;
} }
tcp_ses->conn_id = atomic_inc_return(&tcpSesNextId);
tcp_ses->noblockcnt = ctx->rootfs; tcp_ses->noblockcnt = ctx->rootfs;
tcp_ses->noblocksnd = ctx->noblocksnd || ctx->rootfs; tcp_ses->noblocksnd = ctx->noblocksnd || ctx->rootfs;
tcp_ses->noautotune = ctx->noautotune; 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 */ /* new SMB session uses our server ref */
ses->server = server; ses->server = server;
if (server->dstaddr.ss_family == AF_INET6) if (server->dstaddr.ss_family == AF_INET6)
sprintf(ses->serverName, "%pI6", &addr6->sin6_addr); sprintf(ses->ip_addr, "%pI6", &addr6->sin6_addr);
else else
sprintf(ses->serverName, "%pI4", &addr->sin_addr); sprintf(ses->ip_addr, "%pI4", &addr->sin_addr);
if (ctx->username) { if (ctx->username) {
ses->user_name = kstrdup(ctx->username, GFP_KERNEL); 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)) if (strcmp(old->local_nls->charset, new->local_nls->charset))
return 0; 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 0;
return 1; 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 #ifdef CONFIG_CIFS_DFS_UPCALL
/* /*
* cifs_build_path_to_root returns full path to root when we do not have an * cifs_build_path_to_root returns full path to root when we do not have an
* exiting connection (tcon) * existing connection (tcon)
*/ */
static char * static char *
build_unc_path_to_root(const struct smb3_fs_context *ctx, build_unc_path_to_root(const struct smb3_fs_context *ctx,
@ -3038,26 +3064,49 @@ static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it,
return 0; return 0;
} }
static int setup_dfs_tgt_conn(const char *path, const char *full_path, static int do_dfs_failover(const char *path, const char *full_path, struct cifs_sb_info *cifs_sb,
const struct dfs_cache_tgt_iterator *tgt_it, struct smb3_fs_context *ctx, struct cifs_ses *root_ses,
struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
unsigned int *xid, struct TCP_Server_Info **server, unsigned int *xid, struct TCP_Server_Info **server,
struct cifs_ses **ses, struct cifs_tcon **tcon) struct cifs_ses **ses, struct cifs_tcon **tcon)
{ {
int rc; int rc;
struct dfs_info3_param ref = {0}; struct dfs_cache_tgt_list tgt_list = {0};
char *mdata = NULL; struct dfs_cache_tgt_iterator *tgt_it = NULL;
struct smb3_fs_context fake_ctx = {NULL}; struct smb3_fs_context tmp_ctx = {NULL};
char *fake_devname = NULL;
cifs_dbg(FYI, "%s: dfs path: %s\n", __func__, path); 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;
rc = dfs_cache_get_tgt_referral(path, tgt_it, &ref); rc = dfs_cache_get_tgt_referral(path, tgt_it, &ref);
if (rc) if (rc)
return rc; break;
mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, cifs_dbg(FYI, "%s: old ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC,
full_path + 1, &ref, tmp_ctx.prepath);
mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, &ref,
&fake_devname); &fake_devname);
free_dfs_info_param(&ref); free_dfs_info_param(&ref);
@ -3065,69 +3114,41 @@ static int setup_dfs_tgt_conn(const char *path, const char *full_path,
rc = PTR_ERR(mdata); rc = PTR_ERR(mdata);
mdata = NULL; mdata = NULL;
} else } else
rc = cifs_setup_volume_info(&fake_ctx, mdata, fake_devname); rc = cifs_setup_volume_info(&tmp_ctx, mdata, fake_devname);
kfree(mdata); kfree(mdata);
kfree(fake_devname); kfree(fake_devname);
if (!rc) { if (rc)
/* break;
* We use a 'fake_ctx' here because we need pass it down to the
* mount_{get,put} functions to test connection against new DFS cifs_dbg(FYI, "%s: new ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC,
* targets. tmp_ctx.prepath);
*/
mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon); mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon);
rc = mount_get_conns(&fake_ctx, cifs_sb, xid, server, ses, rc = mount_get_conns(&tmp_ctx, cifs_sb, xid, server, ses, tcon);
tcon);
if (!rc || (*server && *ses)) { if (!rc || (*server && *ses)) {
/* /*
* We were able to connect to new target server. * We were able to connect to new target server. Update current context with
* Update current context with new target server. * new target server.
*/ */
rc = update_vol_info(tgt_it, &fake_ctx, ctx); rc = update_vol_info(tgt_it, &tmp_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_iterator *tgt_it = NULL;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)
return -EOPNOTSUPP;
rc = dfs_cache_noreq_find(path, NULL, &tgt_list);
if (rc)
return rc;
for (;;) {
/* 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))
break; break;
} }
}
if (!rc) { 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 * Update DFS target hint in DFS referral cache with the target server we
* server we successfully reconnected to. * successfully reconnected to.
*/ */
rc = dfs_cache_update_tgthint(*xid, root_ses ? root_ses : *ses, rc = dfs_cache_update_tgthint(*xid, root_ses ? root_ses : *ses, cifs_sb->local_nls,
cifs_sb->local_nls, cifs_remap(cifs_sb), path, tgt_it);
cifs_remap(cifs_sb), path,
tgt_it);
} }
out:
smb3_cleanup_fs_context_contents(&tmp_ctx);
dfs_cache_free_tgts(&tgt_list); dfs_cache_free_tgts(&tgt_list);
return rc; return rc;
} }
@ -3285,56 +3306,27 @@ static void put_root_ses(struct cifs_ses *ses)
cifs_put_smb_ses(ses); cifs_put_smb_ses(ses);
} }
/* Check if a path component is remote and then update @dfs_path accordingly */ /* Set up next dfs prefix path in @dfs_path */
static int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx, 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, const unsigned int xid, struct TCP_Server_Info *server,
struct cifs_tcon *tcon, char **dfs_path) struct cifs_tcon *tcon, char **dfs_path)
{ {
char *path, *s; char *path, *npath;
char sep = CIFS_DIR_SEP(cifs_sb), tmp; int added_treename = is_tcon_dfs(tcon);
char *npath; int rc;
int rc = 0;
int added_treename = tcon->Flags & SMB_SHARE_IS_IN_DFS;
int skip = added_treename;
path = cifs_build_path_to_root(ctx, cifs_sb, tcon, added_treename); path = cifs_build_path_to_root(ctx, cifs_sb, tcon, added_treename);
if (!path) if (!path)
return -ENOMEM; return -ENOMEM;
/* rc = is_path_remote(cifs_sb, ctx, xid, server, tcon);
* Walk through the path components in @path and check if they're accessible. In case any of if (rc == -EREMOTE) {
* 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;
}
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}; struct smb3_fs_context v = {NULL};
/* if @path contains a tree name, skip it in the prefix path */ /* if @path contains a tree name, skip it in the prefix path */
if (added_treename) { if (added_treename) {
rc = smb3_parse_devname(path, &v); rc = smb3_parse_devname(path, &v);
if (rc) if (rc)
break; goto out;
rc = -EREMOTE;
npath = build_unc_path_to_root(&v, cifs_sb, true); npath = build_unc_path_to_root(&v, cifs_sb, true);
smb3_cleanup_fs_context_contents(&v); smb3_cleanup_fs_context_contents(&v);
} else { } else {
@ -3342,20 +3334,49 @@ static int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_contex
v.prepath = path + 1; v.prepath = path + 1;
npath = build_unc_path_to_root(&v, cifs_sb, true); npath = build_unc_path_to_root(&v, cifs_sb, true);
} }
if (IS_ERR(npath)) { if (IS_ERR(npath)) {
rc = PTR_ERR(npath); rc = PTR_ERR(npath);
break; goto out;
} }
kfree(*dfs_path); kfree(*dfs_path);
*dfs_path = npath; *dfs_path = npath;
rc = -EREMOTE;
} }
*s = tmp;
} while (rc == 0);
out:
kfree(path); kfree(path);
return rc; 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 cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
{ {
int rc = 0; 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 *ref_path = NULL, *full_path = NULL;
char *oldmnt = NULL; char *oldmnt = NULL;
char *mntdata = NULL; char *mntdata = NULL;
bool ref_server = false;
rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); 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 * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally
* DFS mount. * 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 * 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. * 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)) { NULL)) {
/* No DFS referral was returned. Looks like a regular share. */
if (rc) if (rc)
goto error; goto error;
/* Check if it is fully accessible and then mount it */ /* 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; break;
if (!tcon) if (!tcon)
continue; continue;
/* Make sure that requests go through new root servers */ /* 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); put_root_ses(root_ses);
set_root_ses(cifs_sb, 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 */ /* Prevent recursion on broken link referrals */
if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS) if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS)
rc = -ELOOP; rc = -ELOOP;

View File

@ -37,11 +37,12 @@ struct cache_dfs_tgt {
struct cache_entry { struct cache_entry {
struct hlist_node hlist; struct hlist_node hlist;
const char *path; const char *path;
int ttl; int hdr_flags; /* RESP_GET_DFS_REFERRAL.ReferralHeaderFlags */
int srvtype; int ttl; /* DFS_REREFERRAL_V3.TimeToLive */
int flags; int srvtype; /* DFS_REREFERRAL_V3.ServerType */
int ref_flags; /* DFS_REREFERRAL_V3.ReferralEntryFlags */
struct timespec64 etime; struct timespec64 etime;
int path_consumed; int path_consumed; /* RESP_GET_DFS_REFERRAL.PathConsumed */
int numtgts; int numtgts;
struct list_head tlist; struct list_head tlist;
struct cache_dfs_tgt *tgthint; struct cache_dfs_tgt *tgthint;
@ -166,14 +167,11 @@ static int dfscache_proc_show(struct seq_file *m, void *v)
continue; continue;
seq_printf(m, seq_printf(m,
"cache entry: path=%s,type=%s,ttl=%d,etime=%ld," "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",
"interlink=%s,path_consumed=%d,expired=%s\n", ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link",
ce->path, ce->ttl, ce->etime.tv_nsec, ce->ref_flags, ce->hdr_flags,
ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", IS_INTERLINK_SET(ce->hdr_flags) ? "yes" : "no",
ce->ttl, ce->etime.tv_nsec, ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no");
IS_INTERLINK_SET(ce->flags) ? "yes" : "no",
ce->path_consumed,
cache_entry_expired(ce) ? "yes" : "no");
list_for_each_entry(t, &ce->tlist, list) { list_for_each_entry(t, &ce->tlist, list) {
seq_printf(m, " %s%s\n", 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) 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->path,
ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl,
ce->etime.tv_nsec, 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, ce->path_consumed,
cache_entry_expired(ce) ? "yes" : "no"); cache_entry_expired(ce) ? "yes" : "no");
dump_tgts(ce); 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->ttl = refs[0].ttl;
ce->etime = get_expire_time(ce->ttl); ce->etime = get_expire_time(ce->ttl);
ce->srvtype = refs[0].server_type; 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; ce->path_consumed = refs[0].path_consumed;
for (i = 0; i < numrefs; i++) { 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->path_consumed = ce->path_consumed;
ref->ttl = ce->ttl; ref->ttl = ce->ttl;
ref->server_type = ce->srvtype; ref->server_type = ce->srvtype;
ref->ref_flag = ce->flags; ref->ref_flag = ce->ref_flags;
ref->flags = ce->hdr_flags;
return 0; return 0;

View File

@ -580,7 +580,7 @@ int cifs_open(struct inode *inode, struct file *file)
} else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { } else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
if (tcon->ses->serverNOS) 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", 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->ses->serverNOS);
tcon->broken_posix_open = true; tcon->broken_posix_open = true;
} else if ((rc != -EIO) && (rc != -EREMOTE) && } else if ((rc != -EIO) && (rc != -EREMOTE) &&

View File

@ -140,6 +140,8 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
fsparam_u32("rsize", Opt_rsize), fsparam_u32("rsize", Opt_rsize),
fsparam_u32("wsize", Opt_wsize), fsparam_u32("wsize", Opt_wsize),
fsparam_u32("actimeo", Opt_actimeo), fsparam_u32("actimeo", Opt_actimeo),
fsparam_u32("acdirmax", Opt_acdirmax),
fsparam_u32("acregmax", Opt_acregmax),
fsparam_u32("echo_interval", Opt_echo_interval), fsparam_u32("echo_interval", Opt_echo_interval),
fsparam_u32("max_credits", Opt_max_credits), fsparam_u32("max_credits", Opt_max_credits),
fsparam_u32("handletimeout", Opt_handletimeout), 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; ctx->vals = &smb3any_values;
break; break;
case Smb_default: case Smb_default:
ctx->ops = &smb30_operations; /* currently identical with 3.0 */ ctx->ops = &smb30_operations;
ctx->vals = &smbdefault_values; ctx->vals = &smbdefault_values;
break; break;
default: default:
@ -542,21 +544,38 @@ static int smb3_fs_context_parse_monolithic(struct fs_context *fc,
/* BB Need to add support for sep= here TBD */ /* BB Need to add support for sep= here TBD */
while ((key = strsep(&options, ",")) != NULL) { while ((key = strsep(&options, ",")) != NULL) {
if (*key) { size_t len;
size_t v_len = 0; char *value;
char *value = strchr(key, '=');
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) {
if (value == key) if (value == key)
continue; continue;
*value++ = 0; *value++ = 0;
v_len = strlen(value); len = strlen(value);
} }
ret = vfs_parse_fs_string(fc, key, value, v_len);
ret = vfs_parse_fs_string(fc, key, value, len);
if (ret < 0) if (ret < 0)
break; break;
} }
}
return ret; return ret;
} }
@ -929,13 +948,32 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
ctx->wsize = result.uint_32; ctx->wsize = result.uint_32;
ctx->got_wsize = true; ctx->got_wsize = true;
break; break;
case Opt_actimeo: case Opt_acregmax:
ctx->actimeo = HZ * result.uint_32; ctx->acregmax = HZ * result.uint_32;
if (ctx->actimeo > CIFS_MAX_ACTIMEO) { if (ctx->acregmax > CIFS_MAX_ACTIMEO) {
cifs_dbg(VFS, "attribute cache timeout too large\n"); cifs_dbg(VFS, "acregmax too large\n");
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
break; 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: case Opt_echo_interval:
ctx->echo_interval = result.uint_32; ctx->echo_interval = result.uint_32;
break; break;
@ -1361,7 +1399,8 @@ int smb3_init_fs_context(struct fs_context *fc)
/* default is to use strict cifs caching semantics */ /* default is to use strict cifs caching semantics */
ctx->strict_io = true; 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 */ /* 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 */ ctx->handle_timeout = 0; /* See MS-SMB2 spec section 2.2.14.2.12 */

View File

@ -118,6 +118,8 @@ enum cifs_param {
Opt_rsize, Opt_rsize,
Opt_wsize, Opt_wsize,
Opt_actimeo, Opt_actimeo,
Opt_acdirmax,
Opt_acregmax,
Opt_echo_interval, Opt_echo_interval,
Opt_max_credits, Opt_max_credits,
Opt_snapshot, Opt_snapshot,
@ -232,7 +234,9 @@ struct smb3_fs_context {
unsigned int wsize; unsigned int wsize;
unsigned int min_offload; unsigned int min_offload;
bool sockopt_tcp_nodelay:1; 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_operations *ops;
struct smb_version_values *vals; struct smb_version_values *vals;
char *prepath; char *prepath;

View File

@ -2199,12 +2199,23 @@ cifs_inode_needs_reval(struct inode *inode)
if (!lookupCacheEnabled) if (!lookupCacheEnabled)
return true; return true;
if (!cifs_sb->ctx->actimeo) /*
* 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; return true;
if (!time_in_range(jiffies, cifs_i->time, if (!time_in_range(jiffies, cifs_i->time,
cifs_i->time + cifs_sb->ctx->actimeo)) cifs_i->time + cifs_sb->ctx->acdirmax))
return true; 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 */ /* hardlinked files w/ noserverino get "special" treatment */
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) && if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) &&

View File

@ -218,7 +218,7 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
/* UNC and paths */ /* UNC and paths */
/* XXX: Use ses->server->hostname? */ /* XXX: Use ses->server->hostname? */
sprintf(unc, unc_fmt, ses->serverName); sprintf(unc, unc_fmt, ses->ip_addr);
ctx.UNC = unc; ctx.UNC = unc;
ctx.prepath = ""; ctx.prepath = "";

View File

@ -63,17 +63,19 @@ smb2_add_credits(struct TCP_Server_Info *server,
const struct cifs_credits *credits, const int optype) const struct cifs_credits *credits, const int optype)
{ {
int *val, rc = -1; int *val, rc = -1;
int scredits, in_flight;
unsigned int add = credits->value; unsigned int add = credits->value;
unsigned int instance = credits->instance; unsigned int instance = credits->instance;
bool reconnect_detected = false; bool reconnect_detected = false;
bool reconnect_with_invalid_credits = false;
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
val = server->ops->get_credits_field(server, optype); val = server->ops->get_credits_field(server, optype);
/* eg found case where write overlapping reconnect messed up credits */ /* eg found case where write overlapping reconnect messed up credits */
if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0)) if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0))
trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, reconnect_with_invalid_credits = true;
server->hostname, *val, add);
if ((instance == 0) || (instance == server->reconnect_instance)) if ((instance == 0) || (instance == server->reconnect_instance))
*val += add; *val += add;
else else
@ -84,7 +86,9 @@ smb2_add_credits(struct TCP_Server_Info *server,
pr_warn_once("server overflowed SMB3 credits\n"); pr_warn_once("server overflowed SMB3 credits\n");
} }
server->in_flight--; 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); rc = change_conf(server);
/* /*
* Sometimes server returns 0 credits on oplock break ack - we need to * 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++; server->oplock_credits++;
} }
} }
scredits = *val;
in_flight = server->in_flight;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
wake_up(&server->request_q); wake_up(&server->request_q);
if (reconnect_detected) { 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", cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n",
add, instance); 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 if (server->tcpStatus == CifsNeedReconnect
|| server->tcpStatus == CifsExiting) || server->tcpStatus == CifsExiting)
return; return;
@ -123,23 +139,30 @@ smb2_add_credits(struct TCP_Server_Info *server,
cifs_dbg(FYI, "disabling oplocks\n"); cifs_dbg(FYI, "disabling oplocks\n");
break; break;
default: default:
trace_smb3_add_credits(server->CurrentMid, /* change_conf rebalanced credits for different types */
server->hostname, rc, add); break;
cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, rc);
} }
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 static void
smb2_set_credits(struct TCP_Server_Info *server, const int val) smb2_set_credits(struct TCP_Server_Info *server, const int val)
{ {
int scredits, in_flight;
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
server->credits = val; server->credits = val;
if (val == 1) if (val == 1)
server->reconnect_instance++; server->reconnect_instance++;
scredits = server->credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
trace_smb3_set_credits(server->CurrentMid, 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); cifs_dbg(FYI, "%s: set %u credits\n", __func__, val);
/* don't log while holding the lock */ /* 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) unsigned int *num, struct cifs_credits *credits)
{ {
int rc = 0; int rc = 0;
unsigned int scredits; unsigned int scredits, in_flight;
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
while (1) { 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); DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE);
credits->instance = server->reconnect_instance; credits->instance = server->reconnect_instance;
server->credits -= credits->value; server->credits -= credits->value;
scredits = server->credits;
server->in_flight++; server->in_flight++;
if (server->in_flight > server->max_in_flight) if (server->in_flight > server->max_in_flight)
server->max_in_flight = server->in_flight; server->max_in_flight = server->in_flight;
break; break;
} }
} }
scredits = server->credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
trace_smb3_add_credits(server->CurrentMid, 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", cifs_dbg(FYI, "%s: removed %u credits total=%d\n",
__func__, credits->value, scredits); __func__, credits->value, scredits);
@ -231,14 +255,14 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
const unsigned int payload_size) const unsigned int payload_size)
{ {
int new_val = DIV_ROUND_UP(payload_size, SMB2_MAX_BUFFER_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) if (!credits->value || credits->value == new_val)
return 0; return 0;
if (credits->value < new_val) { if (credits->value < new_val) {
trace_smb3_too_many_credits(server->CurrentMid, 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)", cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)",
credits->value, new_val); credits->value, new_val);
@ -248,9 +272,13 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
if (server->reconnect_instance != credits->instance) { if (server->reconnect_instance != credits->instance) {
scredits = server->credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
trace_smb3_reconnect_detected(server->CurrentMid, 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", cifs_server_dbg(VFS, "trying to return %d credits to old session\n",
credits->value - new_val); credits->value - new_val);
return -EAGAIN; return -EAGAIN;
@ -258,15 +286,18 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
server->credits += credits->value - new_val; server->credits += credits->value - new_val;
scredits = server->credits; scredits = server->credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
wake_up(&server->request_q); wake_up(&server->request_q);
credits->value = new_val;
trace_smb3_add_credits(server->CurrentMid, 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", cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n",
__func__, credits->value - new_val, scredits); __func__, credits->value - new_val, scredits);
credits->value = new_val;
return 0; return 0;
} }
@ -2369,7 +2400,7 @@ static bool
smb2_is_status_pending(char *buf, struct TCP_Server_Info *server) smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
{ {
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
int scredits; int scredits, in_flight;
if (shdr->Status != STATUS_PENDING) if (shdr->Status != STATUS_PENDING)
return false; return false;
@ -2378,11 +2409,13 @@ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
server->credits += le16_to_cpu(shdr->CreditRequest); server->credits += le16_to_cpu(shdr->CreditRequest);
scredits = server->credits; scredits = server->credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
wake_up(&server->request_q); wake_up(&server->request_q);
trace_smb3_add_credits(server->CurrentMid, 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", cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n",
__func__, le16_to_cpu(shdr->CreditRequest), scredits); __func__, le16_to_cpu(shdr->CreditRequest), scredits);
} }
@ -2418,6 +2451,34 @@ smb2_is_status_io_timeout(char *buf)
return false; 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 static int
smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
struct cifsInodeInfo *cinode) struct cifsInodeInfo *cinode)
@ -4605,6 +4666,10 @@ static void smb2_decrypt_offload(struct work_struct *work)
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
mid->when_received = jiffies; mid->when_received = jiffies;
#endif #endif
if (dw->server->ops->is_network_name_deleted)
dw->server->ops->is_network_name_deleted(dw->buf,
dw->server);
mid->callback(mid); mid->callback(mid);
} else { } else {
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
@ -4723,6 +4788,12 @@ non_offloaded_decrypt:
rc = handle_read_data(server, *mid, buf, rc = handle_read_data(server, *mid, buf,
server->vals->read_rsp_size, server->vals->read_rsp_size,
pages, npages, len, false); pages, npages, len, false);
if (rc >= 0) {
if (server->ops->is_network_name_deleted) {
server->ops->is_network_name_deleted(buf,
server);
}
}
} }
free_pages: free_pages:
@ -5072,6 +5143,7 @@ struct smb_version_operations smb20_operations = {
.fiemap = smb3_fiemap, .fiemap = smb3_fiemap,
.llseek = smb3_llseek, .llseek = smb3_llseek,
.is_status_io_timeout = smb2_is_status_io_timeout, .is_status_io_timeout = smb2_is_status_io_timeout,
.is_network_name_deleted = smb2_is_network_name_deleted,
}; };
struct smb_version_operations smb21_operations = { struct smb_version_operations smb21_operations = {
@ -5173,6 +5245,7 @@ struct smb_version_operations smb21_operations = {
.fiemap = smb3_fiemap, .fiemap = smb3_fiemap,
.llseek = smb3_llseek, .llseek = smb3_llseek,
.is_status_io_timeout = smb2_is_status_io_timeout, .is_status_io_timeout = smb2_is_status_io_timeout,
.is_network_name_deleted = smb2_is_network_name_deleted,
}; };
struct smb_version_operations smb30_operations = { struct smb_version_operations smb30_operations = {
@ -5286,6 +5359,7 @@ struct smb_version_operations smb30_operations = {
.fiemap = smb3_fiemap, .fiemap = smb3_fiemap,
.llseek = smb3_llseek, .llseek = smb3_llseek,
.is_status_io_timeout = smb2_is_status_io_timeout, .is_status_io_timeout = smb2_is_status_io_timeout,
.is_network_name_deleted = smb2_is_network_name_deleted,
}; };
struct smb_version_operations smb311_operations = { struct smb_version_operations smb311_operations = {
@ -5399,6 +5473,7 @@ struct smb_version_operations smb311_operations = {
.fiemap = smb3_fiemap, .fiemap = smb3_fiemap,
.llseek = smb3_llseek, .llseek = smb3_llseek,
.is_status_io_timeout = smb2_is_status_io_timeout, .is_status_io_timeout = smb2_is_status_io_timeout,
.is_network_name_deleted = smb2_is_network_name_deleted,
}; };
struct smb_version_values smb20_values = { struct smb_version_values smb20_values = {

View File

@ -814,8 +814,9 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
SMB3ANY_VERSION_STRING) == 0) { SMB3ANY_VERSION_STRING) == 0) {
req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
req->DialectCount = cpu_to_le16(2); req->Dialects[2] = cpu_to_le16(SMB311_PROT_ID);
total_len += 4; req->DialectCount = cpu_to_le16(3);
total_len += 6;
} else if (strcmp(server->vals->version_string, } else if (strcmp(server->vals->version_string,
SMBDEFAULT_VERSION_STRING) == 0) { SMBDEFAULT_VERSION_STRING) == 0) {
req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); 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, memcpy(req->ClientGUID, server->client_guid,
SMB2_CLIENT_GUID_SIZE); SMB2_CLIENT_GUID_SIZE);
if ((server->vals->protocol_id == SMB311_PROT_ID) || if ((server->vals->protocol_id == SMB311_PROT_ID) ||
(strcmp(server->vals->version_string,
SMB3ANY_VERSION_STRING) == 0) ||
(strcmp(server->vals->version_string, (strcmp(server->vals->version_string,
SMBDEFAULT_VERSION_STRING) == 0)) SMBDEFAULT_VERSION_STRING) == 0))
assemble_neg_contexts(req, server, &total_len); 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, cifs_server_dbg(VFS,
"SMB2.1 dialect returned but not requested\n"); "SMB2.1 dialect returned but not requested\n");
return -EIO; 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, } else if (strcmp(server->vals->version_string,
SMBDEFAULT_VERSION_STRING) == 0) { SMBDEFAULT_VERSION_STRING) == 0) {
@ -1042,10 +1049,11 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
SMB3ANY_VERSION_STRING) == 0) { SMB3ANY_VERSION_STRING) == 0) {
pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
pneg_inbuf->DialectCount = cpu_to_le16(2); pneg_inbuf->Dialects[2] = cpu_to_le16(SMB311_PROT_ID);
/* structure is big enough for 3 dialects, sending only 2 */ pneg_inbuf->DialectCount = cpu_to_le16(3);
/* SMB 2.1 not included so subtract one dialect from len */
inbuflen = sizeof(*pneg_inbuf) - inbuflen = sizeof(*pneg_inbuf) -
(2 * sizeof(pneg_inbuf->Dialects[0])); (sizeof(pneg_inbuf->Dialects[0]));
} else if (strcmp(server->vals->version_string, } else if (strcmp(server->vals->version_string,
SMBDEFAULT_VERSION_STRING) == 0) { SMBDEFAULT_VERSION_STRING) == 0) {
pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); 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[2] = cpu_to_le16(SMB302_PROT_ID);
pneg_inbuf->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); pneg_inbuf->Dialects[3] = cpu_to_le16(SMB311_PROT_ID);
pneg_inbuf->DialectCount = cpu_to_le16(4); 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); inbuflen = sizeof(*pneg_inbuf);
} else { } else {
/* otherwise specific dialect was requested */ /* otherwise specific dialect was requested */
@ -1253,7 +1261,7 @@ SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
cifs_ses_server(sess_data->ses), cifs_ses_server(sess_data->ses),
&rqst, &rqst,
&sess_data->buf0_type, &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); cifs_small_buf_release(sess_data->iov[0].iov_base);
memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec));

View File

@ -851,17 +851,21 @@ DEFINE_SMB3_LEASE_ERR_EVENT(lease_err);
DECLARE_EVENT_CLASS(smb3_reconnect_class, DECLARE_EVENT_CLASS(smb3_reconnect_class,
TP_PROTO(__u64 currmid, TP_PROTO(__u64 currmid,
__u64 conn_id,
char *hostname), char *hostname),
TP_ARGS(currmid, hostname), TP_ARGS(currmid, conn_id, hostname),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(__u64, currmid) __field(__u64, currmid)
__field(__u64, conn_id)
__field(char *, hostname) __field(char *, hostname)
), ),
TP_fast_assign( TP_fast_assign(
__entry->currmid = currmid; __entry->currmid = currmid;
__entry->conn_id = conn_id;
__entry->hostname = hostname; __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->hostname,
__entry->currmid) __entry->currmid)
) )
@ -869,44 +873,56 @@ DECLARE_EVENT_CLASS(smb3_reconnect_class,
#define DEFINE_SMB3_RECONNECT_EVENT(name) \ #define DEFINE_SMB3_RECONNECT_EVENT(name) \
DEFINE_EVENT(smb3_reconnect_class, smb3_##name, \ DEFINE_EVENT(smb3_reconnect_class, smb3_##name, \
TP_PROTO(__u64 currmid, \ TP_PROTO(__u64 currmid, \
__u64 conn_id, \
char *hostname), \ char *hostname), \
TP_ARGS(currmid, hostname)) TP_ARGS(currmid, conn_id, hostname))
DEFINE_SMB3_RECONNECT_EVENT(reconnect); DEFINE_SMB3_RECONNECT_EVENT(reconnect);
DEFINE_SMB3_RECONNECT_EVENT(partial_send_reconnect); DEFINE_SMB3_RECONNECT_EVENT(partial_send_reconnect);
DECLARE_EVENT_CLASS(smb3_credit_class, DECLARE_EVENT_CLASS(smb3_credit_class,
TP_PROTO(__u64 currmid, TP_PROTO(__u64 currmid,
__u64 conn_id,
char *hostname, char *hostname,
int credits, int credits,
int credits_to_add), int credits_to_add,
TP_ARGS(currmid, hostname, credits, credits_to_add), int in_flight),
TP_ARGS(currmid, conn_id, hostname, credits, credits_to_add, in_flight),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(__u64, currmid) __field(__u64, currmid)
__field(__u64, conn_id)
__field(char *, hostname) __field(char *, hostname)
__field(int, credits) __field(int, credits)
__field(int, credits_to_add) __field(int, credits_to_add)
__field(int, in_flight)
), ),
TP_fast_assign( TP_fast_assign(
__entry->currmid = currmid; __entry->currmid = currmid;
__entry->conn_id = conn_id;
__entry->hostname = hostname; __entry->hostname = hostname;
__entry->credits = credits; __entry->credits = credits;
__entry->credits_to_add = credits_to_add; __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->hostname,
__entry->currmid, __entry->currmid,
__entry->credits, __entry->credits,
__entry->credits_to_add) __entry->credits_to_add,
__entry->in_flight)
) )
#define DEFINE_SMB3_CREDIT_EVENT(name) \ #define DEFINE_SMB3_CREDIT_EVENT(name) \
DEFINE_EVENT(smb3_credit_class, smb3_##name, \ DEFINE_EVENT(smb3_credit_class, smb3_##name, \
TP_PROTO(__u64 currmid, \ TP_PROTO(__u64 currmid, \
__u64 conn_id, \
char *hostname, \ char *hostname, \
int credits, \ int credits, \
int credits_to_add), \ int credits_to_add, \
TP_ARGS(currmid, hostname, credits, 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_with_invalid_credits);
DEFINE_SMB3_CREDIT_EVENT(reconnect_detected); DEFINE_SMB3_CREDIT_EVENT(reconnect_detected);

View File

@ -445,7 +445,7 @@ unmask:
*/ */
server->tcpStatus = CifsNeedReconnect; server->tcpStatus = CifsNeedReconnect;
trace_smb3_partial_send_reconnect(server->CurrentMid, trace_smb3_partial_send_reconnect(server->CurrentMid,
server->hostname); server->conn_id, server->hostname);
} }
smbd_done: smbd_done:
if (rc < 0 && rc != -EINTR) 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 *credits;
int optype; int optype;
long int t; long int t;
int scredits = server->credits; int scredits, in_flight;
if (timeout < 0) if (timeout < 0)
t = MAX_JIFFY_OFFSET; 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; server->max_in_flight = server->in_flight;
*credits -= 1; *credits -= 1;
*instance = server->reconnect_instance; *instance = server->reconnect_instance;
scredits = *credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock); 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; return 0;
} }
while (1) { while (1) {
if (*credits < num_credits) { if (*credits < num_credits) {
scredits = *credits;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
cifs_num_waiters_inc(server); cifs_num_waiters_inc(server);
rc = wait_event_killable_timeout(server->request_q, rc = wait_event_killable_timeout(server->request_q,
has_credits(server, credits, num_credits), t); has_credits(server, credits, num_credits), t);
cifs_num_waiters_dec(server); cifs_num_waiters_dec(server);
if (!rc) { 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, 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", cifs_server_dbg(VFS, "wait timed out after %d ms\n",
timeout); timeout);
return -ENOTSUPP; return -EBUSY;
} }
if (rc == -ERESTARTSYS) if (rc == -ERESTARTSYS)
return -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 && server->in_flight > 2 * MAX_COMPOUND &&
*credits <= MAX_COMPOUND) { *credits <= MAX_COMPOUND) {
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
cifs_num_waiters_inc(server); cifs_num_waiters_inc(server);
rc = wait_event_killable_timeout( rc = wait_event_killable_timeout(
server->request_q, server->request_q,
@ -603,13 +620,18 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
t); t);
cifs_num_waiters_dec(server); cifs_num_waiters_dec(server);
if (!rc) { if (!rc) {
spin_lock(&server->req_lock);
scredits = *credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_credit_timeout( trace_smb3_credit_timeout(
server->CurrentMid, server->CurrentMid,
server->hostname, num_credits, server->conn_id, server->hostname,
0); scredits, num_credits, in_flight);
cifs_server_dbg(VFS, "wait timed out after %d ms\n", cifs_server_dbg(VFS, "wait timed out after %d ms\n",
timeout); timeout);
return -ENOTSUPP; return -EBUSY;
} }
if (rc == -ERESTARTSYS) if (rc == -ERESTARTSYS)
return -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 */ /* update # of requests on the wire to server */
if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) { if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) {
*credits -= num_credits; *credits -= num_credits;
scredits = *credits;
server->in_flight += num_credits; server->in_flight += num_credits;
if (server->in_flight > server->max_in_flight) if (server->in_flight > server->max_in_flight)
server->max_in_flight = server->in_flight; server->max_in_flight = server->in_flight;
*instance = server->reconnect_instance; *instance = server->reconnect_instance;
} }
scredits = *credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
trace_smb3_add_credits(server->CurrentMid, 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", cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
__func__, num_credits, scredits); __func__, num_credits, scredits);
break; break;
@ -656,13 +680,13 @@ wait_for_compound_request(struct TCP_Server_Info *server, int num,
const int flags, unsigned int *instance) const int flags, unsigned int *instance)
{ {
int *credits; int *credits;
int scredits, sin_flight; int scredits, in_flight;
credits = server->ops->get_credits_field(server, flags & CIFS_OP_MASK); credits = server->ops->get_credits_field(server, flags & CIFS_OP_MASK);
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
scredits = *credits; scredits = *credits;
sin_flight = server->in_flight; in_flight = server->in_flight;
if (*credits < num) { if (*credits < num) {
/* /*
@ -684,10 +708,11 @@ wait_for_compound_request(struct TCP_Server_Info *server, int num,
if (server->in_flight == 0) { if (server->in_flight == 0) {
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
trace_smb3_insufficient_credits(server->CurrentMid, 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", cifs_dbg(FYI, "%s: %d requests in flight, needed %d total=%d\n",
__func__, sin_flight, num, scredits); __func__, in_flight, num, scredits);
return -ENOTSUPP; return -EDEADLK;
} }
} }
spin_unlock(&server->req_lock); 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. * 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, smb311_update_preauth_hash(ses, rqst[0].rq_iov,
rqst[0].rq_nvec); 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. * 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 = { struct kvec iov = {
.iov_base = resp_iov[0].iov_base, .iov_base = resp_iov[0].iov_base,
.iov_len = resp_iov[0].iov_len .iov_len = resp_iov[0].iov_len