smb3/cifs fixes (including 8 for stable). Improved tracing, stats, snapshots (previous version mounts work now), performance (compounding enabled for statfs)

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAltwwVkACgkQiiy9cAdy
 T1H06gv/d+b52w2X05YlLBB1GboJjv1dl3sVrFisJrMrCCr6Hotk+4+RtC8CJHh6
 /9Joq6iY8B/XLl7HrWr61vJhn8vlWOGhOQaUmHCBGeiIMX93RbaP8KvkPfHAKQiJ
 T6+7v70z/WL7mUQA2TEQ3Iz6kpCuP2y/XT6eQglTFXasqsWHy08TbsbnmP9yX2i4
 E1B6zMwpn6m4PFxEURg14eBJTrQomb/UBSLHNwxwwOQjnKzsmeH3pyiFCJSPJNFF
 Xv/lyBibgvQAsuWtLTi82fqei+c60as2LVrsFLdoWXeD6JMlx1O+SHSz0NNJL/Lk
 oxMNx9NI+LsYmekIBHgoX01PzVgCpcn6ThfoQv3JLHtqiZuk3Wlai7bPvJMIApoM
 8fgHGR9xvhRCfHgYvxx4MmP3e7xewajSe7fh8/y8BqreO+fLeoyqho5G9zDpn6bU
 wGFPtVYW/fgTEM5E3NOjsuC1zE35bjwU7U40jqjGHP1VsTs3hkLFy9fZNNq4EP1M
 ytfJPlTG
 =gzZK
 -----END PGP SIGNATURE-----

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

Pull cifs updates from Steve French:
 "smb3/cifs fixes (including 8 for stable).

  Other improvements include:

   - improved tracing, improved stats

   - snapshots (previous version mounts work now over SMB3)

   - performance (compounding enabled for statfs, ~40% faster).

   - security (make it possible to build cifs.ko with insecure vers=1.0
     disabled in Kconfig)"

* tag '4.19-smb3' of git://git.samba.org/sfrench/cifs-2.6: (43 commits)
  smb3: create smb3 equivalent alias for cifs pseudo-xattrs
  smb3: allow previous versions to be mounted with snapshot= mount parm
  cifs: don't show domain= in mount output when domain is empty
  cifs: add missing support for ACLs in SMB 3.11
  smb3: enumerating snapshots was leaving part of the data off end
  cifs: update smb2_queryfs() to use compounding
  cifs: update receive_encrypted_standard to handle compounded responses
  cifs: create SMB2_open_init()/SMB2_open_free() helpers.
  cifs: add SMB2_query_info_[init|free]()
  cifs: add SMB2_close_init()/SMB2_close_free()
  smb3: display stats counters for number of slow commands
  CIFS: fix uninitialized ptr deref in smb2 signing
  smb3: Do not send SMB3 SET_INFO if nothing changed
  smb3: fix minor debug output for CONFIG_CIFS_STATS
  smb3: add tracepoint for slow responses
  cifs: add compound_send_recv()
  cifs: make smb_send_rqst take an array of requests
  cifs: update init_sg, crypt_message to take an array of rqst
  smb3: update readme to correct information about /proc/fs/cifs/Stats
  smb3: fix reset of bytes read and written stats
  ...
This commit is contained in:
Linus Torvalds 2018-08-13 22:32:11 -07:00
commit 3bb37da509
28 changed files with 1288 additions and 1762 deletions

File diff suppressed because it is too large Load Diff

View File

@ -603,8 +603,7 @@ DebugData Displays information about active CIFS sessions and
shares, features enabled as well as the cifs.ko shares, features enabled as well as the cifs.ko
version. version.
Stats Lists summary resource usage information as well as per Stats Lists summary resource usage information as well as per
share statistics, if CONFIG_CIFS_STATS in enabled share statistics.
in the kernel configuration.
Configuration pseudo-files: Configuration pseudo-files:
SecurityFlags Flags which control security negotiation and SecurityFlags Flags which control security negotiation and
@ -687,23 +686,22 @@ cifsFYI functions as a bit mask. Setting it to 1 enables additional kernel
logging of various informational messages. 2 enables logging of non-zero logging of various informational messages. 2 enables logging of non-zero
SMB return codes while 4 enables logging of requests that take longer SMB return codes while 4 enables logging of requests that take longer
than one second to complete (except for byte range lock requests). than one second to complete (except for byte range lock requests).
Setting it to 4 requires defining CONFIG_CIFS_STATS2 manually in the Setting it to 4 requires CONFIG_CIFS_STATS2 to be set in kernel configuration
source code (typically by setting it in the beginning of cifsglob.h), (.config). Setting it to seven enables all three. Finally, tracing
and setting it to seven enables all three. Finally, tracing
the start of smb requests and responses can be enabled via: the start of smb requests and responses can be enabled via:
echo 1 > /proc/fs/cifs/traceSMB echo 1 > /proc/fs/cifs/traceSMB
Per share (per client mount) statistics are available in /proc/fs/cifs/Stats Per share (per client mount) statistics are available in /proc/fs/cifs/Stats.
if the kernel was configured with cifs statistics enabled. The statistics Additional information is available if CONFIG_CIFS_STATS2 is enabled in the
represent the number of successful (ie non-zero return code from the server) kernel configuration (.config). The statistics returned include counters which
SMB responses to some of the more common commands (open, delete, mkdir etc.). represent the number of attempted and failed (ie non-zero return code from the
server) SMB3 (or cifs) requests grouped by request type (read, write, close etc.).
Also recorded is the total bytes read and bytes written to the server for Also recorded is the total bytes read and bytes written to the server for
that share. Note that due to client caching effects this can be less than the that share. Note that due to client caching effects this can be less than the
number of bytes read and written by the application running on the client. number of bytes read and written by the application running on the client.
The statistics for the number of total SMBs and oplock breaks are different in Statistics can be reset to zero by "echo 0 > /proc/fs/cifs/Stats" which may be
that they represent all for that share, not just those for which the server useful if comparing performance of two different scenarios.
returned success.
Also note that "cat /proc/fs/cifs/DebugData" will display information about Also note that "cat /proc/fs/cifs/DebugData" will display information about
the active sessions and the shares that are mounted. the active sessions and the shares that are mounted.

View File

@ -16,24 +16,28 @@ config CIFS
select CRYPTO_DES select CRYPTO_DES
help help
This is the client VFS module for the SMB3 family of NAS protocols, This is the client VFS module for the SMB3 family of NAS protocols,
as well as for earlier dialects such as SMB2.1, SMB2 and the (including support for the most recent, most secure dialect SMB3.1.1)
as well as for earlier dialects such as SMB2.1, SMB2 and the older
Common Internet File System (CIFS) protocol. CIFS was the successor Common Internet File System (CIFS) protocol. CIFS was the successor
to the original dialect, the Server Message Block (SMB) protocol, the to the original dialect, the Server Message Block (SMB) protocol, the
native file sharing mechanism for most early PC operating systems. native file sharing mechanism for most early PC operating systems.
The SMB3 protocol is supported by most modern operating systems and The SMB3 protocol is supported by most modern operating systems
NAS appliances (e.g. Samba, Windows 8, Windows 2012, MacOS). and NAS appliances (e.g. Samba, Windows 10, Windows Server 2016,
MacOS) and even in the cloud (e.g. Microsoft Azure).
The older CIFS protocol was included in Windows NT4, 2000 and XP (and The older CIFS protocol was included in Windows NT4, 2000 and XP (and
later) as well by Samba (which provides excellent CIFS and SMB3 later) as well by Samba (which provides excellent CIFS and SMB3
server support for Linux and many other operating systems). Limited server support for Linux and many other operating systems). Use of
support for OS/2 and Windows ME and similar very old servers is dialects older than SMB2.1 is often discouraged on public networks.
provided as well. This module also provides limited support for OS/2 and Windows ME
and similar very old servers.
The cifs module provides an advanced network file system client This module provides an advanced network file system client
for mounting to SMB3 (and CIFS) compliant servers. It includes for mounting to SMB3 (and CIFS) compliant servers. It includes
support for DFS (hierarchical name space), secure per-user support for DFS (hierarchical name space), secure per-user
session establishment via Kerberos or NTLM or NTLMv2, session establishment via Kerberos or NTLM or NTLMv2, RDMA
safe distributed caching (oplock), optional packet (smbdirect), advanced security features, per-share encryption,
directory leases, safe distributed caching (oplock), optional packet
signing, Unicode and other internationalization improvements. signing, Unicode and other internationalization improvements.
In general, the default dialects, SMB3 and later, enable better In general, the default dialects, SMB3 and later, enable better
@ -43,18 +47,11 @@ config CIFS
than SMB3 mounts. SMB2/SMB3 mount options are also than SMB3 mounts. SMB2/SMB3 mount options are also
slightly simpler (compared to CIFS) due to protocol improvements. slightly simpler (compared to CIFS) due to protocol improvements.
If you need to mount to Samba, Macs or Windows from this machine, say Y. If you need to mount to Samba, Azure, Macs or Windows from this machine, say Y.
config CIFS_STATS
bool "CIFS statistics"
depends on CIFS
help
Enabling this option will cause statistics for each server share
mounted by the cifs client to be displayed in /proc/fs/cifs/Stats
config CIFS_STATS2 config CIFS_STATS2
bool "Extended statistics" bool "Extended statistics"
depends on CIFS_STATS depends on CIFS
help help
Enabling this option will allow more detailed statistics on SMB Enabling this option will allow more detailed statistics on SMB
request timing to be displayed in /proc/fs/cifs/DebugData and also request timing to be displayed in /proc/fs/cifs/DebugData and also
@ -66,9 +63,24 @@ config CIFS_STATS2
Unless you are a developer or are doing network performance analysis Unless you are a developer or are doing network performance analysis
or tuning, say N. or tuning, say N.
config CIFS_ALLOW_INSECURE_LEGACY
bool "Support legacy servers which use less secure dialects"
depends on CIFS
default y
help
Modern dialects, SMB2.1 and later (including SMB3 and 3.1.1), have
additional security features, including protection against
man-in-the-middle attacks and stronger crypto hashes, so the use
of legacy dialects (SMB1/CIFS and SMB2.0) is discouraged.
Disabling this option prevents users from using vers=1.0 or vers=2.0
on mounts with cifs.ko
If unsure, say Y.
config CIFS_WEAK_PW_HASH config CIFS_WEAK_PW_HASH
bool "Support legacy servers which use weaker LANMAN security" bool "Support legacy servers which use weaker LANMAN security"
depends on CIFS depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY
help help
Modern CIFS servers including Samba and most Windows versions Modern CIFS servers including Samba and most Windows versions
(since 1997) support stronger NTLM (and even NTLMv2 and Kerberos) (since 1997) support stronger NTLM (and even NTLMv2 and Kerberos)
@ -186,15 +198,6 @@ config CIFS_NFSD_EXPORT
help help
Allows NFS server to export a CIFS mounted share (nfsd over cifs) Allows NFS server to export a CIFS mounted share (nfsd over cifs)
config CIFS_SMB311
bool "SMB3.1.1 network file system support"
depends on CIFS
select CRYPTO_SHA512
help
This enables support for the newest, and most secure dialect, SMB3.11.
If unsure, say Y
config CIFS_SMB_DIRECT config CIFS_SMB_DIRECT
bool "SMB Direct support (Experimental)" bool "SMB Direct support (Experimental)"
depends on CIFS=m && INFINIBAND && INFINIBAND_ADDR_TRANS || CIFS=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y depends on CIFS=m && INFINIBAND && INFINIBAND_ADDR_TRANS || CIFS=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y

View File

@ -128,8 +128,10 @@ fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data,
memset(&auxdata, 0, sizeof(auxdata)); memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof; auxdata.eof = cifsi->server_eof;
auxdata.last_write_time = timespec64_to_timespec(cifsi->vfs_inode.i_mtime); auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec;
auxdata.last_change_time = timespec64_to_timespec(cifsi->vfs_inode.i_ctime); auxdata.last_change_time_sec = cifsi->vfs_inode.i_ctime.tv_sec;
auxdata.last_write_time_nsec = cifsi->vfs_inode.i_mtime.tv_nsec;
auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec;
if (memcmp(data, &auxdata, datalen) != 0) if (memcmp(data, &auxdata, datalen) != 0)
return FSCACHE_CHECKAUX_OBSOLETE; return FSCACHE_CHECKAUX_OBSOLETE;

View File

@ -160,25 +160,41 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, "CIFS Version %s\n", CIFS_VERSION); seq_printf(m, "CIFS Version %s\n", CIFS_VERSION);
seq_printf(m, "Features:"); seq_printf(m, "Features:");
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
seq_printf(m, " dfs"); seq_printf(m, " DFS");
#endif #endif
#ifdef CONFIG_CIFS_FSCACHE #ifdef CONFIG_CIFS_FSCACHE
seq_printf(m, " fscache"); seq_printf(m, ",FSCACHE");
#endif
#ifdef CONFIG_CIFS_SMB_DIRECT
seq_printf(m, ",SMB_DIRECT");
#endif
#ifdef CONFIG_CIFS_STATS2
seq_printf(m, ",STATS2");
#else
seq_printf(m, ",STATS");
#endif
#ifdef CONFIG_CIFS_DEBUG2
seq_printf(m, ",DEBUG2");
#elif defined(CONFIG_CIFS_DEBUG)
seq_printf(m, ",DEBUG");
#endif
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
seq_printf(m, ",ALLOW_INSECURE_LEGACY");
#endif #endif
#ifdef CONFIG_CIFS_WEAK_PW_HASH #ifdef CONFIG_CIFS_WEAK_PW_HASH
seq_printf(m, " lanman"); seq_printf(m, ",WEAK_PW_HASH");
#endif #endif
#ifdef CONFIG_CIFS_POSIX #ifdef CONFIG_CIFS_POSIX
seq_printf(m, " posix"); seq_printf(m, ",CIFS_POSIX");
#endif #endif
#ifdef CONFIG_CIFS_UPCALL #ifdef CONFIG_CIFS_UPCALL
seq_printf(m, " spnego"); seq_printf(m, ",UPCALL(SPNEGO)");
#endif #endif
#ifdef CONFIG_CIFS_XATTR #ifdef CONFIG_CIFS_XATTR
seq_printf(m, " xattr"); seq_printf(m, ",XATTR");
#endif #endif
#ifdef CONFIG_CIFS_ACL #ifdef CONFIG_CIFS_ACL
seq_printf(m, " acl"); seq_printf(m, ",ACL");
#endif #endif
seq_putc(m, '\n'); seq_putc(m, '\n');
seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid); seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid);
@ -259,10 +275,9 @@ skip_rdma:
server->credits, server->dialect); server->credits, server->dialect);
if (server->sign) if (server->sign)
seq_printf(m, " signed"); seq_printf(m, " signed");
#ifdef CONFIG_CIFS_SMB311
if (server->posix_ext_supported) if (server->posix_ext_supported)
seq_printf(m, " posix"); seq_printf(m, " posix");
#endif /* 3.1.1 */
i++; i++;
list_for_each(tmp2, &server->smb_ses_list) { list_for_each(tmp2, &server->smb_ses_list) {
ses = list_entry(tmp2, struct cifs_ses, ses = list_entry(tmp2, struct cifs_ses,
@ -350,7 +365,6 @@ skip_rdma:
return 0; return 0;
} }
#ifdef CONFIG_CIFS_STATS
static ssize_t cifs_stats_proc_write(struct file *file, static ssize_t cifs_stats_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos) const char __user *buffer, size_t count, loff_t *ppos)
{ {
@ -364,13 +378,23 @@ static ssize_t cifs_stats_proc_write(struct file *file,
rc = kstrtobool_from_user(buffer, count, &bv); rc = kstrtobool_from_user(buffer, count, &bv);
if (rc == 0) { if (rc == 0) {
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
int i;
atomic_set(&totBufAllocCount, 0); atomic_set(&totBufAllocCount, 0);
atomic_set(&totSmBufAllocCount, 0); atomic_set(&totSmBufAllocCount, 0);
#endif /* CONFIG_CIFS_STATS2 */ #endif /* CONFIG_CIFS_STATS2 */
spin_lock(&GlobalMid_Lock);
GlobalMaxActiveXid = 0;
GlobalCurrentXid = 0;
spin_unlock(&GlobalMid_Lock);
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);
#ifdef CONFIG_CIFS_STATS2
for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++)
atomic_set(&server->smb2slowcmd[i], 0);
#endif /* CONFIG_CIFS_STATS2 */
list_for_each(tmp2, &server->smb_ses_list) { list_for_each(tmp2, &server->smb_ses_list) {
ses = list_entry(tmp2, struct cifs_ses, ses = list_entry(tmp2, struct cifs_ses,
smb_ses_list); smb_ses_list);
@ -379,6 +403,10 @@ static ssize_t cifs_stats_proc_write(struct file *file,
struct cifs_tcon, struct cifs_tcon,
tcon_list); tcon_list);
atomic_set(&tcon->num_smbs_sent, 0); atomic_set(&tcon->num_smbs_sent, 0);
spin_lock(&tcon->stat_lock);
tcon->bytes_read = 0;
tcon->bytes_written = 0;
spin_unlock(&tcon->stat_lock);
if (server->ops->clear_stats) if (server->ops->clear_stats)
server->ops->clear_stats(tcon); server->ops->clear_stats(tcon);
} }
@ -395,13 +423,15 @@ static ssize_t cifs_stats_proc_write(struct file *file,
static int cifs_stats_proc_show(struct seq_file *m, void *v) static int cifs_stats_proc_show(struct seq_file *m, void *v)
{ {
int i; int i;
#ifdef CONFIG_CIFS_STATS2
int j;
#endif /* STATS2 */
struct list_head *tmp1, *tmp2, *tmp3; struct list_head *tmp1, *tmp2, *tmp3;
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;
seq_printf(m, seq_printf(m, "Resources in use\nCIFS Session: %d\n",
"Resources in use\nCIFS Session: %d\n",
sesInfoAllocCount.counter); sesInfoAllocCount.counter);
seq_printf(m, "Share (unique mount targets): %d\n", seq_printf(m, "Share (unique mount targets): %d\n",
tconInfoAllocCount.counter); tconInfoAllocCount.counter);
@ -430,6 +460,13 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
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);
#ifdef CONFIG_CIFS_STATS2
for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++)
if (atomic_read(&server->smb2slowcmd[j]))
seq_printf(m, "%d slow responses from %s for command %d\n",
atomic_read(&server->smb2slowcmd[j]),
server->hostname, j);
#endif /* STATS2 */
list_for_each(tmp2, &server->smb_ses_list) { list_for_each(tmp2, &server->smb_ses_list) {
ses = list_entry(tmp2, struct cifs_ses, ses = list_entry(tmp2, struct cifs_ses,
smb_ses_list); smb_ses_list);
@ -466,7 +503,6 @@ static const struct file_operations cifs_stats_proc_fops = {
.release = single_release, .release = single_release,
.write = cifs_stats_proc_write, .write = cifs_stats_proc_write,
}; };
#endif /* STATS */
#ifdef CONFIG_CIFS_SMB_DIRECT #ifdef CONFIG_CIFS_SMB_DIRECT
#define PROC_FILE_DEFINE(name) \ #define PROC_FILE_DEFINE(name) \
@ -524,9 +560,7 @@ cifs_proc_init(void)
proc_create_single("DebugData", 0, proc_fs_cifs, proc_create_single("DebugData", 0, proc_fs_cifs,
cifs_debug_data_proc_show); cifs_debug_data_proc_show);
#ifdef CONFIG_CIFS_STATS
proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_fops); proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_fops);
#endif /* STATS */
proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_fops); proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_fops);
proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_fops); proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_fops);
proc_create("LinuxExtensionsEnabled", 0644, proc_fs_cifs, proc_create("LinuxExtensionsEnabled", 0644, proc_fs_cifs,
@ -564,9 +598,7 @@ cifs_proc_clean(void)
remove_proc_entry("DebugData", proc_fs_cifs); remove_proc_entry("DebugData", proc_fs_cifs);
remove_proc_entry("cifsFYI", proc_fs_cifs); remove_proc_entry("cifsFYI", proc_fs_cifs);
remove_proc_entry("traceSMB", proc_fs_cifs); remove_proc_entry("traceSMB", proc_fs_cifs);
#ifdef CONFIG_CIFS_STATS
remove_proc_entry("Stats", proc_fs_cifs); remove_proc_entry("Stats", proc_fs_cifs);
#endif
remove_proc_entry("SecurityFlags", proc_fs_cifs); remove_proc_entry("SecurityFlags", proc_fs_cifs);
remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs);
remove_proc_entry("LookupCacheEnabled", proc_fs_cifs); remove_proc_entry("LookupCacheEnabled", proc_fs_cifs);

View File

@ -83,7 +83,13 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
kaddr = (char *) kmap(rqst->rq_pages[i]) + offset; kaddr = (char *) kmap(rqst->rq_pages[i]) + offset;
crypto_shash_update(shash, kaddr, len); rc = crypto_shash_update(shash, kaddr, len);
if (rc) {
cifs_dbg(VFS, "%s: Could not update with payload\n",
__func__);
kunmap(rqst->rq_pages[i]);
return rc;
}
kunmap(rqst->rq_pages[i]); kunmap(rqst->rq_pages[i]);
} }
@ -452,7 +458,7 @@ find_timestamp(struct cifs_ses *ses)
unsigned char *blobptr; unsigned char *blobptr;
unsigned char *blobend; unsigned char *blobend;
struct ntlmssp2_name *attrptr; struct ntlmssp2_name *attrptr;
struct timespec ts; struct timespec64 ts;
if (!ses->auth_key.len || !ses->auth_key.response) if (!ses->auth_key.len || !ses->auth_key.response)
return 0; return 0;
@ -477,7 +483,7 @@ find_timestamp(struct cifs_ses *ses)
blobptr += attrsize; /* advance attr value */ blobptr += attrsize; /* advance attr value */
} }
ktime_get_real_ts(&ts); ktime_get_real_ts64(&ts);
return cpu_to_le64(cifs_UnixTimeToNT(ts)); return cpu_to_le64(cifs_UnixTimeToNT(ts));
} }

View File

@ -139,6 +139,9 @@ cifs_read_super(struct super_block *sb)
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL)
sb->s_flags |= SB_POSIXACL; sb->s_flags |= SB_POSIXACL;
if (tcon->snapshot_time)
sb->s_flags |= SB_RDONLY;
if (tcon->ses->capabilities & tcon->ses->server->vals->cap_large_files) if (tcon->ses->capabilities & tcon->ses->server->vals->cap_large_files)
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE;
else else
@ -209,14 +212,16 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
xid = get_xid(); xid = get_xid();
/* if (le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength) > 0)
* PATH_MAX may be too long - it would presumably be total path, buf->f_namelen =
* but note that some servers (includinng Samba 3) have a shorter le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength);
* maximum path. else
* buf->f_namelen = PATH_MAX;
* Instead could get the real value via SMB_QUERY_FS_ATTRIBUTE_INFO.
*/ buf->f_fsid.val[0] = tcon->vol_serial_number;
buf->f_namelen = PATH_MAX; /* are using part of create time for more randomness, see man statfs */
buf->f_fsid.val[1] = (int)le64_to_cpu(tcon->vol_create_time);
buf->f_files = 0; /* undefined */ buf->f_files = 0; /* undefined */
buf->f_ffree = 0; /* unlimited */ buf->f_ffree = 0; /* unlimited */
@ -427,7 +432,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
else if (tcon->ses->user_name) else if (tcon->ses->user_name)
seq_show_option(s, "username", tcon->ses->user_name); seq_show_option(s, "username", tcon->ses->user_name);
if (tcon->ses->domainName) if (tcon->ses->domainName && tcon->ses->domainName[0] != 0)
seq_show_option(s, "domain", tcon->ses->domainName); seq_show_option(s, "domain", tcon->ses->domainName);
if (srcaddr->sa_family != AF_UNSPEC) { if (srcaddr->sa_family != AF_UNSPEC) {
@ -481,20 +486,12 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
seq_puts(s, ",persistenthandles"); seq_puts(s, ",persistenthandles");
else if (tcon->use_resilient) else if (tcon->use_resilient)
seq_puts(s, ",resilienthandles"); seq_puts(s, ",resilienthandles");
#ifdef CONFIG_CIFS_SMB311
if (tcon->posix_extensions) if (tcon->posix_extensions)
seq_puts(s, ",posix"); seq_puts(s, ",posix");
else if (tcon->unix_ext) else if (tcon->unix_ext)
seq_puts(s, ",unix"); seq_puts(s, ",unix");
else else
seq_puts(s, ",nounix"); seq_puts(s, ",nounix");
#else
if (tcon->unix_ext)
seq_puts(s, ",unix");
else
seq_puts(s, ",nounix");
#endif /* SMB311 */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
seq_puts(s, ",posixpaths"); seq_puts(s, ",posixpaths");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)
@ -546,6 +543,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
seq_printf(s, ",wsize=%u", cifs_sb->wsize); seq_printf(s, ",wsize=%u", cifs_sb->wsize);
seq_printf(s, ",echo_interval=%lu", seq_printf(s, ",echo_interval=%lu",
tcon->ses->server->echo_interval / HZ); tcon->ses->server->echo_interval / HZ);
if (tcon->snapshot_time)
seq_printf(s, ",snapshot=%llu", tcon->snapshot_time);
/* convert actimeo and display it in seconds */ /* convert actimeo and display it in seconds */
seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ); seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ);

View File

@ -76,6 +76,9 @@
#define SMB_ECHO_INTERVAL_MAX 600 #define SMB_ECHO_INTERVAL_MAX 600
#define SMB_ECHO_INTERVAL_DEFAULT 60 #define SMB_ECHO_INTERVAL_DEFAULT 60
/* maximum number of PDUs in one compound */
#define MAX_COMPOUND 5
/* /*
* Default number of credits to keep available for SMB3. * Default number of credits to keep available for SMB3.
* This value is chosen somewhat arbitrarily. The Windows client * This value is chosen somewhat arbitrarily. The Windows client
@ -191,9 +194,7 @@ enum smb_version {
Smb_21, Smb_21,
Smb_30, Smb_30,
Smb_302, Smb_302,
#ifdef CONFIG_CIFS_SMB311
Smb_311, Smb_311,
#endif /* SMB311 */
Smb_3any, Smb_3any,
Smb_default, Smb_default,
Smb_version_err Smb_version_err
@ -456,13 +457,11 @@ struct smb_version_operations {
long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t, long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t,
loff_t); loff_t);
/* init transform request - used for encryption for now */ /* init transform request - used for encryption for now */
int (*init_transform_rq)(struct TCP_Server_Info *, struct smb_rqst *, int (*init_transform_rq)(struct TCP_Server_Info *, int num_rqst,
struct smb_rqst *); struct smb_rqst *, struct smb_rqst *);
/* free transform request */
void (*free_transform_rq)(struct smb_rqst *);
int (*is_transform_hdr)(void *buf); int (*is_transform_hdr)(void *buf);
int (*receive_transform)(struct TCP_Server_Info *, int (*receive_transform)(struct TCP_Server_Info *,
struct mid_q_entry **); struct mid_q_entry **, char **, int *);
enum securityEnum (*select_sectype)(struct TCP_Server_Info *, enum securityEnum (*select_sectype)(struct TCP_Server_Info *,
enum securityEnum); enum securityEnum);
int (*next_header)(char *); int (*next_header)(char *);
@ -684,15 +683,14 @@ struct TCP_Server_Info {
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
atomic_t in_send; /* requests trying to send */ atomic_t in_send; /* requests trying to send */
atomic_t num_waiters; /* blocked waiting to get in sendrecv */ atomic_t num_waiters; /* blocked waiting to get in sendrecv */
#endif atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */
#endif /* STATS2 */
unsigned int max_read; unsigned int max_read;
unsigned int max_write; unsigned int max_write;
#ifdef CONFIG_CIFS_SMB311
__le16 cipher_type; __le16 cipher_type;
/* save initital negprot hash */ /* save initital negprot hash */
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
bool posix_ext_supported; bool posix_ext_supported;
#endif /* 3.1.1 */
struct delayed_work reconnect; /* reconnect workqueue job */ struct delayed_work reconnect; /* reconnect workqueue job */
struct mutex reconnect_mutex; /* prevent simultaneous reconnects */ struct mutex reconnect_mutex; /* prevent simultaneous reconnects */
unsigned long echo_interval; unsigned long echo_interval;
@ -886,9 +884,7 @@ struct cifs_ses {
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
#ifdef CONFIG_CIFS_SMB311
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
#endif /* 3.1.1 */
/* /*
* Network interfaces available on the server this session is * Network interfaces available on the server this session is
@ -913,6 +909,7 @@ cap_unix(struct cifs_ses *ses)
struct cached_fid { struct cached_fid {
bool is_valid:1; /* Do we have a useable root fid */ bool is_valid:1; /* Do we have a useable root fid */
struct kref refcount;
struct cifs_fid *fid; struct cifs_fid *fid;
struct mutex fid_mutex; struct mutex fid_mutex;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
@ -936,7 +933,6 @@ struct cifs_tcon {
__u32 tid; /* The 4 byte tree id */ __u32 tid; /* The 4 byte tree id */
__u16 Flags; /* optional support bits */ __u16 Flags; /* optional support bits */
enum statusEnum tidStatus; enum statusEnum tidStatus;
#ifdef CONFIG_CIFS_STATS
atomic_t num_smbs_sent; atomic_t num_smbs_sent;
union { union {
struct { struct {
@ -967,24 +963,9 @@ struct cifs_tcon {
atomic_t smb2_com_failed[NUMBER_OF_SMB2_COMMANDS]; atomic_t smb2_com_failed[NUMBER_OF_SMB2_COMMANDS];
} smb2_stats; } smb2_stats;
} stats; } stats;
#ifdef CONFIG_CIFS_STATS2
unsigned long long time_writes;
unsigned long long time_reads;
unsigned long long time_opens;
unsigned long long time_deletes;
unsigned long long time_closes;
unsigned long long time_mkdirs;
unsigned long long time_rmdirs;
unsigned long long time_renames;
unsigned long long time_t2renames;
unsigned long long time_ffirst;
unsigned long long time_fnext;
unsigned long long time_fclose;
#endif /* CONFIG_CIFS_STATS2 */
__u64 bytes_read; __u64 bytes_read;
__u64 bytes_written; __u64 bytes_written;
spinlock_t stat_lock; /* protects the two fields above */ spinlock_t stat_lock; /* protects the two fields above */
#endif /* CONFIG_CIFS_STATS */
FILE_SYSTEM_DEVICE_INFO fsDevInfo; FILE_SYSTEM_DEVICE_INFO fsDevInfo;
FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */
FILE_SYSTEM_UNIX_INFO fsUnixInfo; FILE_SYSTEM_UNIX_INFO fsUnixInfo;
@ -997,9 +978,7 @@ struct cifs_tcon {
bool seal:1; /* transport encryption for this mounted share */ bool seal:1; /* transport encryption for this mounted share */
bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol
for this mount even if server would support */ for this mount even if server would support */
#ifdef CONFIG_CIFS_SMB311
bool posix_extensions; /* if true SMB3.11 posix extensions enabled */ bool posix_extensions; /* if true SMB3.11 posix extensions enabled */
#endif /* CIFS_311 */
bool local_lease:1; /* check leases (only) on local system not remote */ bool local_lease:1; /* check leases (only) on local system not remote */
bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */
bool broken_sparse_sup; /* if server or share does not support sparse */ bool broken_sparse_sup; /* if server or share does not support sparse */
@ -1046,6 +1025,7 @@ struct tcon_link {
}; };
extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb); extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb);
extern void smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst);
static inline struct cifs_tcon * static inline struct cifs_tcon *
tlink_tcon(struct tcon_link *tlink) tlink_tcon(struct tcon_link *tlink)
@ -1352,7 +1332,6 @@ convert_delimiter(char *path, char delim)
*pos = delim; *pos = delim;
} }
#ifdef CONFIG_CIFS_STATS
#define cifs_stats_inc atomic_inc #define cifs_stats_inc atomic_inc
static inline void cifs_stats_bytes_written(struct cifs_tcon *tcon, static inline void cifs_stats_bytes_written(struct cifs_tcon *tcon,
@ -1372,13 +1351,6 @@ static inline void cifs_stats_bytes_read(struct cifs_tcon *tcon,
tcon->bytes_read += bytes; tcon->bytes_read += bytes;
spin_unlock(&tcon->stat_lock); spin_unlock(&tcon->stat_lock);
} }
#else
#define cifs_stats_inc(field) do {} while (0)
#define cifs_stats_bytes_written(tcon, bytes) do {} while (0)
#define cifs_stats_bytes_read(tcon, bytes) do {} while (0)
#endif
/* /*
@ -1544,9 +1516,9 @@ struct cifs_fattr {
dev_t cf_rdev; dev_t cf_rdev;
unsigned int cf_nlink; unsigned int cf_nlink;
unsigned int cf_dtype; unsigned int cf_dtype;
struct timespec cf_atime; struct timespec64 cf_atime;
struct timespec cf_mtime; struct timespec64 cf_mtime;
struct timespec cf_ctime; struct timespec64 cf_ctime;
}; };
static inline void free_dfs_info_param(struct dfs_info3_param *param) static inline void free_dfs_info_param(struct dfs_info3_param *param)

View File

@ -94,6 +94,10 @@ extern int cifs_call_async(struct TCP_Server_Info *server,
extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
struct smb_rqst *rqst, int *resp_buf_type, struct smb_rqst *rqst, int *resp_buf_type,
const int flags, struct kvec *resp_iov); const int flags, struct kvec *resp_iov);
extern int compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
const int flags, const int num_rqst,
struct smb_rqst *rqst, int *resp_buf_type,
struct kvec *resp_iov);
extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *, extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
struct smb_hdr * /* input */ , struct smb_hdr * /* input */ ,
struct smb_hdr * /* out */ , struct smb_hdr * /* out */ ,
@ -143,9 +147,9 @@ extern enum securityEnum select_sectype(struct TCP_Server_Info *server,
enum securityEnum requested); enum securityEnum requested);
extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
const struct nls_table *nls_cp); const struct nls_table *nls_cp);
extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
extern u64 cifs_UnixTimeToNT(struct timespec); extern u64 cifs_UnixTimeToNT(struct timespec64);
extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, extern struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
int offset); int offset);
extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
extern int cifs_get_writer(struct cifsInodeInfo *cinode); extern int cifs_get_writer(struct cifsInodeInfo *cinode);

View File

@ -508,13 +508,13 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
* this requirement. * this requirement.
*/ */
int val, seconds, remain, result; int val, seconds, remain, result;
struct timespec ts; struct timespec64 ts;
unsigned long utc = ktime_get_real_seconds(); time64_t utc = ktime_get_real_seconds();
ts = cnvrtDosUnixTm(rsp->SrvTime.Date, ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
rsp->SrvTime.Time, 0); rsp->SrvTime.Time, 0);
cifs_dbg(FYI, "SrvTime %d sec since 1970 (utc: %d) diff: %d\n", cifs_dbg(FYI, "SrvTime %lld sec since 1970 (utc: %lld) diff: %lld\n",
(int)ts.tv_sec, (int)utc, ts.tv_sec, utc,
(int)(utc - ts.tv_sec)); utc - ts.tv_sec);
val = (int)(utc - ts.tv_sec); val = (int)(utc - ts.tv_sec);
seconds = abs(val); seconds = abs(val);
result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ; result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
@ -4082,7 +4082,7 @@ QInfRetry:
if (rc) { if (rc) {
cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc); cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc);
} else if (data) { } else if (data) {
struct timespec ts; struct timespec64 ts;
__u32 time = le32_to_cpu(pSMBr->last_write_time); __u32 time = le32_to_cpu(pSMBr->last_write_time);
/* decode response */ /* decode response */

View File

@ -303,10 +303,8 @@ static const match_table_t cifs_smb_version_tokens = {
{ Smb_21, SMB21_VERSION_STRING }, { Smb_21, SMB21_VERSION_STRING },
{ Smb_30, SMB30_VERSION_STRING }, { Smb_30, SMB30_VERSION_STRING },
{ Smb_302, SMB302_VERSION_STRING }, { Smb_302, SMB302_VERSION_STRING },
#ifdef CONFIG_CIFS_SMB311
{ Smb_311, SMB311_VERSION_STRING }, { Smb_311, SMB311_VERSION_STRING },
{ Smb_311, ALT_SMB311_VERSION_STRING }, { Smb_311, ALT_SMB311_VERSION_STRING },
#endif /* SMB311 */
{ Smb_3any, SMB3ANY_VERSION_STRING }, { Smb_3any, SMB3ANY_VERSION_STRING },
{ Smb_default, SMBDEFAULT_VERSION_STRING }, { Smb_default, SMBDEFAULT_VERSION_STRING },
{ Smb_version_err, NULL } { Smb_version_err, NULL }
@ -350,6 +348,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
server->max_read = 0; server->max_read = 0;
cifs_dbg(FYI, "Reconnecting tcp session\n"); cifs_dbg(FYI, "Reconnecting tcp session\n");
trace_smb3_reconnect(server->CurrentMid, 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 */
@ -851,13 +850,14 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
static int static int
cifs_demultiplex_thread(void *p) cifs_demultiplex_thread(void *p)
{ {
int length; int i, num_mids, length;
struct TCP_Server_Info *server = p; struct TCP_Server_Info *server = p;
unsigned int pdu_length; unsigned int pdu_length;
unsigned int next_offset; unsigned int next_offset;
char *buf = NULL; char *buf = NULL;
struct task_struct *task_to_wake = NULL; struct task_struct *task_to_wake = NULL;
struct mid_q_entry *mid_entry; struct mid_q_entry *mids[MAX_COMPOUND];
char *bufs[MAX_COMPOUND];
current->flags |= PF_MEMALLOC; current->flags |= PF_MEMALLOC;
cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current)); cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current));
@ -924,58 +924,75 @@ next_pdu:
server->pdu_size = next_offset; server->pdu_size = next_offset;
} }
mid_entry = NULL; memset(mids, 0, sizeof(mids));
memset(bufs, 0, sizeof(bufs));
num_mids = 0;
if (server->ops->is_transform_hdr && if (server->ops->is_transform_hdr &&
server->ops->receive_transform && server->ops->receive_transform &&
server->ops->is_transform_hdr(buf)) { server->ops->is_transform_hdr(buf)) {
length = server->ops->receive_transform(server, length = server->ops->receive_transform(server,
&mid_entry); mids,
bufs,
&num_mids);
} else { } else {
mid_entry = server->ops->find_mid(server, buf); mids[0] = server->ops->find_mid(server, buf);
bufs[0] = buf;
if (mids[0])
num_mids = 1;
if (!mid_entry || !mid_entry->receive) if (!mids[0] || !mids[0]->receive)
length = standard_receive3(server, mid_entry); length = standard_receive3(server, mids[0]);
else else
length = mid_entry->receive(server, mid_entry); length = mids[0]->receive(server, mids[0]);
} }
if (length < 0) { if (length < 0) {
if (mid_entry) for (i = 0; i < num_mids; i++)
cifs_mid_q_entry_release(mid_entry); if (mids[i])
cifs_mid_q_entry_release(mids[i]);
continue; continue;
} }
if (server->large_buf) if (server->large_buf)
buf = server->bigbuf; buf = server->bigbuf;
server->lstrp = jiffies; server->lstrp = jiffies;
if (mid_entry != NULL) {
mid_entry->resp_buf_size = server->pdu_size; for (i = 0; i < num_mids; i++) {
if ((mid_entry->mid_flags & MID_WAIT_CANCELLED) && if (mids[i] != NULL) {
mid_entry->mid_state == MID_RESPONSE_RECEIVED && mids[i]->resp_buf_size = server->pdu_size;
server->ops->handle_cancelled_mid) if ((mids[i]->mid_flags & MID_WAIT_CANCELLED) &&
server->ops->handle_cancelled_mid( mids[i]->mid_state == MID_RESPONSE_RECEIVED &&
mid_entry->resp_buf, server->ops->handle_cancelled_mid)
server->ops->handle_cancelled_mid(
mids[i]->resp_buf,
server); server);
if (!mid_entry->multiRsp || mid_entry->multiEnd) if (!mids[i]->multiRsp || mids[i]->multiEnd)
mid_entry->callback(mid_entry); mids[i]->callback(mids[i]);
cifs_mid_q_entry_release(mid_entry); cifs_mid_q_entry_release(mids[i]);
} else if (server->ops->is_oplock_break && } else if (server->ops->is_oplock_break &&
server->ops->is_oplock_break(buf, server)) { server->ops->is_oplock_break(bufs[i],
cifs_dbg(FYI, "Received oplock break\n"); server)) {
} else { cifs_dbg(FYI, "Received oplock break\n");
cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n", } else {
atomic_read(&midCount)); cifs_dbg(VFS, "No task to wake, unknown frame "
cifs_dump_mem("Received Data is: ", buf, "received! NumMids %d\n",
HEADER_SIZE(server)); atomic_read(&midCount));
cifs_dump_mem("Received Data is: ", bufs[i],
HEADER_SIZE(server));
#ifdef CONFIG_CIFS_DEBUG2 #ifdef CONFIG_CIFS_DEBUG2
if (server->ops->dump_detail) if (server->ops->dump_detail)
server->ops->dump_detail(buf, server); server->ops->dump_detail(bufs[i],
cifs_dump_mids(server); server);
cifs_dump_mids(server);
#endif /* CIFS_DEBUG2 */ #endif /* CIFS_DEBUG2 */
}
} }
if (pdu_length > server->pdu_size) { if (pdu_length > server->pdu_size) {
if (!allocate_buffers(server)) if (!allocate_buffers(server))
continue; continue;
@ -1174,6 +1191,7 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol, bool is_smb3)
substring_t args[MAX_OPT_ARGS]; substring_t args[MAX_OPT_ARGS];
switch (match_token(value, cifs_smb_version_tokens, args)) { switch (match_token(value, cifs_smb_version_tokens, args)) {
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
case Smb_1: case Smb_1:
if (disable_legacy_dialects) { if (disable_legacy_dialects) {
cifs_dbg(VFS, "mount with legacy dialect disabled\n"); cifs_dbg(VFS, "mount with legacy dialect disabled\n");
@ -1198,6 +1216,14 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol, bool is_smb3)
vol->ops = &smb20_operations; vol->ops = &smb20_operations;
vol->vals = &smb20_values; vol->vals = &smb20_values;
break; break;
#else
case Smb_1:
cifs_dbg(VFS, "vers=1.0 (cifs) mount not permitted when legacy dialects disabled\n");
return 1;
case Smb_20:
cifs_dbg(VFS, "vers=2.0 mount not permitted when legacy dialects disabled\n");
return 1;
#endif /* CIFS_ALLOW_INSECURE_LEGACY */
case Smb_21: case Smb_21:
vol->ops = &smb21_operations; vol->ops = &smb21_operations;
vol->vals = &smb21_values; vol->vals = &smb21_values;
@ -1210,12 +1236,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol, bool is_smb3)
vol->ops = &smb30_operations; /* currently identical with 3.0 */ vol->ops = &smb30_operations; /* currently identical with 3.0 */
vol->vals = &smb302_values; vol->vals = &smb302_values;
break; break;
#ifdef CONFIG_CIFS_SMB311
case Smb_311: case Smb_311:
vol->ops = &smb311_operations; vol->ops = &smb311_operations;
vol->vals = &smb311_values; vol->vals = &smb311_values;
break; break;
#endif /* SMB311 */
case Smb_3any: case Smb_3any:
vol->ops = &smb30_operations; /* currently identical with 3.0 */ vol->ops = &smb30_operations; /* currently identical with 3.0 */
vol->vals = &smb3any_values; vol->vals = &smb3any_values;
@ -3030,15 +3054,17 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
} }
} }
#ifdef CONFIG_CIFS_SMB311 if (volume_info->linux_ext) {
if ((volume_info->linux_ext) && (ses->server->posix_ext_supported)) { if (ses->server->posix_ext_supported) {
if (ses->server->vals->protocol_id == SMB311_PROT_ID) {
tcon->posix_extensions = true; tcon->posix_extensions = true;
printk_once(KERN_WARNING printk_once(KERN_WARNING
"SMB3.11 POSIX Extensions are experimental\n"); "SMB3.11 POSIX Extensions are experimental\n");
} else {
cifs_dbg(VFS, "Server does not support mounting with posix SMB3.11 extensions.\n");
rc = -EOPNOTSUPP;
goto out_fail;
} }
} }
#endif /* 311 */
/* /*
* BB Do we need to wrap session_mutex around this TCon call and Unix * BB Do we need to wrap session_mutex around this TCon call and Unix
@ -3992,11 +4018,9 @@ try_mount_again:
goto remote_path_check; goto remote_path_check;
} }
#ifdef CONFIG_CIFS_SMB311
/* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */
if (tcon->posix_extensions) if (tcon->posix_extensions)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS;
#endif /* SMB3.11 */
/* tell server which Unix caps we support */ /* tell server which Unix caps we support */
if (cap_unix(tcon->ses)) { if (cap_unix(tcon->ses)) {
@ -4459,11 +4483,10 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
goto out; goto out;
} }
#ifdef CONFIG_CIFS_SMB311
/* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */
if (tcon->posix_extensions) if (tcon->posix_extensions)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS;
#endif /* SMB3.11 */
if (cap_unix(ses)) if (cap_unix(ses))
reset_cifs_unix_caps(0, tcon, NULL, vol_info); reset_cifs_unix_caps(0, tcon, NULL, vol_info);

View File

@ -129,8 +129,10 @@ static void cifs_fscache_acquire_inode_cookie(struct cifsInodeInfo *cifsi,
memset(&auxdata, 0, sizeof(auxdata)); memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof; auxdata.eof = cifsi->server_eof;
auxdata.last_write_time = timespec64_to_timespec(cifsi->vfs_inode.i_mtime); auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec;
auxdata.last_change_time = timespec64_to_timespec(cifsi->vfs_inode.i_ctime); auxdata.last_change_time_sec = cifsi->vfs_inode.i_ctime.tv_sec;
auxdata.last_write_time_nsec = cifsi->vfs_inode.i_mtime.tv_nsec;
auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec;
cifsi->fscache = cifsi->fscache =
fscache_acquire_cookie(tcon->fscache, fscache_acquire_cookie(tcon->fscache,
@ -166,8 +168,10 @@ void cifs_fscache_release_inode_cookie(struct inode *inode)
if (cifsi->fscache) { if (cifsi->fscache) {
memset(&auxdata, 0, sizeof(auxdata)); memset(&auxdata, 0, sizeof(auxdata));
auxdata.eof = cifsi->server_eof; auxdata.eof = cifsi->server_eof;
auxdata.last_write_time = timespec64_to_timespec(cifsi->vfs_inode.i_mtime); auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec;
auxdata.last_change_time = timespec64_to_timespec(cifsi->vfs_inode.i_ctime); auxdata.last_change_time_sec = cifsi->vfs_inode.i_ctime.tv_sec;
auxdata.last_write_time_nsec = cifsi->vfs_inode.i_mtime.tv_nsec;
auxdata.last_change_time_nsec = cifsi->vfs_inode.i_ctime.tv_nsec;
cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache); cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache);
fscache_relinquish_cookie(cifsi->fscache, &auxdata, false); fscache_relinquish_cookie(cifsi->fscache, &auxdata, false);

View File

@ -31,9 +31,11 @@
* Auxiliary data attached to CIFS inode within the cache * Auxiliary data attached to CIFS inode within the cache
*/ */
struct cifs_fscache_inode_auxdata { struct cifs_fscache_inode_auxdata {
struct timespec last_write_time; u64 last_write_time_sec;
struct timespec last_change_time; u64 last_change_time_sec;
u64 eof; u32 last_write_time_nsec;
u32 last_change_time_nsec;
u64 eof;
}; };
/* /*

View File

@ -95,7 +95,6 @@ static void
cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
{ {
struct cifsInodeInfo *cifs_i = CIFS_I(inode); struct cifsInodeInfo *cifs_i = CIFS_I(inode);
struct timespec ts;
cifs_dbg(FYI, "%s: revalidating inode %llu\n", cifs_dbg(FYI, "%s: revalidating inode %llu\n",
__func__, cifs_i->uniqueid); __func__, cifs_i->uniqueid);
@ -114,8 +113,7 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
} }
/* revalidate if mtime or size have changed */ /* revalidate if mtime or size have changed */
ts = timespec64_to_timespec(inode->i_mtime); if (timespec64_equal(&inode->i_mtime, &fattr->cf_mtime) &&
if (timespec_equal(&ts, &fattr->cf_mtime) &&
cifs_i->server_eof == fattr->cf_eof) { cifs_i->server_eof == fattr->cf_eof) {
cifs_dbg(FYI, "%s: inode %llu is unchanged\n", cifs_dbg(FYI, "%s: inode %llu is unchanged\n",
__func__, cifs_i->uniqueid); __func__, cifs_i->uniqueid);
@ -164,9 +162,9 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
cifs_revalidate_cache(inode, fattr); cifs_revalidate_cache(inode, fattr);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
inode->i_atime = timespec_to_timespec64(fattr->cf_atime); inode->i_atime = fattr->cf_atime;
inode->i_mtime = timespec_to_timespec64(fattr->cf_mtime); inode->i_mtime = fattr->cf_mtime;
inode->i_ctime = timespec_to_timespec64(fattr->cf_ctime); inode->i_ctime = fattr->cf_ctime;
inode->i_rdev = fattr->cf_rdev; inode->i_rdev = fattr->cf_rdev;
cifs_nlink_fattr_to_inode(inode, fattr); cifs_nlink_fattr_to_inode(inode, fattr);
inode->i_uid = fattr->cf_uid; inode->i_uid = fattr->cf_uid;
@ -327,8 +325,8 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU; fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU;
fattr->cf_uid = cifs_sb->mnt_uid; fattr->cf_uid = cifs_sb->mnt_uid;
fattr->cf_gid = cifs_sb->mnt_gid; fattr->cf_gid = cifs_sb->mnt_gid;
ktime_get_real_ts(&fattr->cf_mtime); ktime_get_real_ts64(&fattr->cf_mtime);
fattr->cf_mtime = timespec_trunc(fattr->cf_mtime, sb->s_time_gran); fattr->cf_mtime = timespec64_trunc(fattr->cf_mtime, sb->s_time_gran);
fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime; fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime;
fattr->cf_nlink = 2; fattr->cf_nlink = 2;
fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL; fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;
@ -604,8 +602,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
if (info->LastAccessTime) if (info->LastAccessTime)
fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
else { else {
ktime_get_real_ts(&fattr->cf_atime); ktime_get_real_ts64(&fattr->cf_atime);
fattr->cf_atime = timespec_trunc(fattr->cf_atime, sb->s_time_gran); fattr->cf_atime = timespec64_trunc(fattr->cf_atime, sb->s_time_gran);
} }
fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
@ -1122,17 +1120,19 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid,
if (!server->ops->set_file_info) if (!server->ops->set_file_info)
return -ENOSYS; return -ENOSYS;
info_buf.Pad = 0;
if (attrs->ia_valid & ATTR_ATIME) { if (attrs->ia_valid & ATTR_ATIME) {
set_time = true; set_time = true;
info_buf.LastAccessTime = info_buf.LastAccessTime =
cpu_to_le64(cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_atime))); cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime));
} else } else
info_buf.LastAccessTime = 0; info_buf.LastAccessTime = 0;
if (attrs->ia_valid & ATTR_MTIME) { if (attrs->ia_valid & ATTR_MTIME) {
set_time = true; set_time = true;
info_buf.LastWriteTime = info_buf.LastWriteTime =
cpu_to_le64(cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_mtime))); cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
} else } else
info_buf.LastWriteTime = 0; info_buf.LastWriteTime = 0;
@ -1145,7 +1145,7 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid,
if (set_time && (attrs->ia_valid & ATTR_CTIME)) { if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
cifs_dbg(FYI, "CIFS - CTIME changed\n"); cifs_dbg(FYI, "CIFS - CTIME changed\n");
info_buf.ChangeTime = info_buf.ChangeTime =
cpu_to_le64(cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_ctime))); cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
} else } else
info_buf.ChangeTime = 0; info_buf.ChangeTime = 0;
@ -1577,14 +1577,12 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode)
server = tcon->ses->server; server = tcon->ses->server;
#ifdef CONFIG_CIFS_SMB311
if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) { if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) {
rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path, rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path,
cifs_sb); cifs_sb);
d_drop(direntry); /* for time being always refresh inode info */ d_drop(direntry); /* for time being always refresh inode info */
goto mkdir_out; goto mkdir_out;
} }
#endif /* SMB311 */
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) { le64_to_cpu(tcon->fsUnixInfo.Capability))) {
@ -2071,8 +2069,8 @@ int cifs_getattr(const struct path *path, struct kstat *stat,
/* old CIFS Unix Extensions doesn't return create time */ /* old CIFS Unix Extensions doesn't return create time */
if (CIFS_I(inode)->createtime) { if (CIFS_I(inode)->createtime) {
stat->result_mask |= STATX_BTIME; stat->result_mask |= STATX_BTIME;
stat->btime = timespec_to_timespec64( stat->btime =
cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime))); cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime));
} }
stat->attributes_mask |= (STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED); stat->attributes_mask |= (STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED);
@ -2278,17 +2276,17 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
args->gid = INVALID_GID; /* no change */ args->gid = INVALID_GID; /* no change */
if (attrs->ia_valid & ATTR_ATIME) if (attrs->ia_valid & ATTR_ATIME)
args->atime = cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_atime)); args->atime = cifs_UnixTimeToNT(attrs->ia_atime);
else else
args->atime = NO_CHANGE_64; args->atime = NO_CHANGE_64;
if (attrs->ia_valid & ATTR_MTIME) if (attrs->ia_valid & ATTR_MTIME)
args->mtime = cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_mtime)); args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime);
else else
args->mtime = NO_CHANGE_64; args->mtime = NO_CHANGE_64;
if (attrs->ia_valid & ATTR_CTIME) if (attrs->ia_valid & ATTR_CTIME)
args->ctime = cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_ctime)); args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime);
else else
args->ctime = NO_CHANGE_64; args->ctime = NO_CHANGE_64;

View File

@ -396,7 +396,7 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_io_parms io_parms; struct cifs_io_parms io_parms;
int buf_type = CIFS_NO_BUFFER; int buf_type = CIFS_NO_BUFFER;
__le16 *utf16_path; __le16 *utf16_path;
__u8 oplock = SMB2_OPLOCK_LEVEL_II; __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
struct smb2_file_all_info *pfile_info = NULL; struct smb2_file_all_info *pfile_info = NULL;
oparms.tcon = tcon; oparms.tcon = tcon;
@ -459,7 +459,7 @@ smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_io_parms io_parms; struct cifs_io_parms io_parms;
int create_options = CREATE_NOT_DIR; int create_options = CREATE_NOT_DIR;
__le16 *utf16_path; __le16 *utf16_path;
__u8 oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE; __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
struct kvec iov[2]; struct kvec iov[2];
if (backup_cred(cifs_sb)) if (backup_cred(cifs_sb))

View File

@ -122,9 +122,7 @@ tconInfoAlloc(void)
mutex_init(&ret_buf->crfid.fid_mutex); mutex_init(&ret_buf->crfid.fid_mutex);
ret_buf->crfid.fid = kzalloc(sizeof(struct cifs_fid), ret_buf->crfid.fid = kzalloc(sizeof(struct cifs_fid),
GFP_KERNEL); GFP_KERNEL);
#ifdef CONFIG_CIFS_STATS
spin_lock_init(&ret_buf->stat_lock); spin_lock_init(&ret_buf->stat_lock);
#endif
} }
return ret_buf; return ret_buf;
} }

View File

@ -918,10 +918,10 @@ smbCalcSize(void *buf, struct TCP_Server_Info *server)
* Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
* into Unix UTC (based 1970-01-01, in seconds). * into Unix UTC (based 1970-01-01, in seconds).
*/ */
struct timespec struct timespec64
cifs_NTtimeToUnix(__le64 ntutc) cifs_NTtimeToUnix(__le64 ntutc)
{ {
struct timespec ts; struct timespec64 ts;
/* BB what about the timezone? BB */ /* BB what about the timezone? BB */
/* Subtract the NTFS time offset, then convert to 1s intervals. */ /* Subtract the NTFS time offset, then convert to 1s intervals. */
@ -935,12 +935,12 @@ cifs_NTtimeToUnix(__le64 ntutc)
*/ */
if (t < 0) { if (t < 0) {
abs_t = -t; abs_t = -t;
ts.tv_nsec = (long)(do_div(abs_t, 10000000) * 100); ts.tv_nsec = (time64_t)(do_div(abs_t, 10000000) * 100);
ts.tv_nsec = -ts.tv_nsec; ts.tv_nsec = -ts.tv_nsec;
ts.tv_sec = -abs_t; ts.tv_sec = -abs_t;
} else { } else {
abs_t = t; abs_t = t;
ts.tv_nsec = (long)do_div(abs_t, 10000000) * 100; ts.tv_nsec = (time64_t)do_div(abs_t, 10000000) * 100;
ts.tv_sec = abs_t; ts.tv_sec = abs_t;
} }
@ -949,7 +949,7 @@ cifs_NTtimeToUnix(__le64 ntutc)
/* Convert the Unix UTC into NT UTC. */ /* Convert the Unix UTC into NT UTC. */
u64 u64
cifs_UnixTimeToNT(struct timespec t) cifs_UnixTimeToNT(struct timespec64 t)
{ {
/* Convert to 100ns intervals and then add the NTFS time offset. */ /* Convert to 100ns intervals and then add the NTFS time offset. */
return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET; return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET;
@ -959,10 +959,11 @@ static const int total_days_of_prev_months[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
}; };
struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset) struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
{ {
struct timespec ts; struct timespec64 ts;
int sec, min, days, month, year; time64_t sec;
int min, days, month, year;
u16 date = le16_to_cpu(le_date); u16 date = le16_to_cpu(le_date);
u16 time = le16_to_cpu(le_time); u16 time = le16_to_cpu(le_time);
SMB_TIME *st = (SMB_TIME *)&time; SMB_TIME *st = (SMB_TIME *)&time;
@ -973,7 +974,7 @@ struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
sec = 2 * st->TwoSeconds; sec = 2 * st->TwoSeconds;
min = st->Minutes; min = st->Minutes;
if ((sec > 59) || (min > 59)) if ((sec > 59) || (min > 59))
cifs_dbg(VFS, "illegal time min %d sec %d\n", min, sec); cifs_dbg(VFS, "illegal time min %d sec %lld\n", min, sec);
sec += (min * 60); sec += (min * 60);
sec += 60 * 60 * st->Hours; sec += 60 * 60 * st->Hours;
if (st->Hours > 24) if (st->Hours > 24)

View File

@ -624,7 +624,6 @@ cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
static void static void
cifs_clear_stats(struct cifs_tcon *tcon) cifs_clear_stats(struct cifs_tcon *tcon)
{ {
#ifdef CONFIG_CIFS_STATS
atomic_set(&tcon->stats.cifs_stats.num_writes, 0); atomic_set(&tcon->stats.cifs_stats.num_writes, 0);
atomic_set(&tcon->stats.cifs_stats.num_reads, 0); atomic_set(&tcon->stats.cifs_stats.num_reads, 0);
atomic_set(&tcon->stats.cifs_stats.num_flushes, 0); atomic_set(&tcon->stats.cifs_stats.num_flushes, 0);
@ -646,13 +645,11 @@ cifs_clear_stats(struct cifs_tcon *tcon)
atomic_set(&tcon->stats.cifs_stats.num_locks, 0); atomic_set(&tcon->stats.cifs_stats.num_locks, 0);
atomic_set(&tcon->stats.cifs_stats.num_acl_get, 0); atomic_set(&tcon->stats.cifs_stats.num_acl_get, 0);
atomic_set(&tcon->stats.cifs_stats.num_acl_set, 0); atomic_set(&tcon->stats.cifs_stats.num_acl_set, 0);
#endif
} }
static void static void
cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon) cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
{ {
#ifdef CONFIG_CIFS_STATS
seq_printf(m, " Oplocks breaks: %d", seq_printf(m, " Oplocks breaks: %d",
atomic_read(&tcon->stats.cifs_stats.num_oplock_brks)); atomic_read(&tcon->stats.cifs_stats.num_oplock_brks));
seq_printf(m, "\nReads: %d Bytes: %llu", seq_printf(m, "\nReads: %d Bytes: %llu",
@ -684,7 +681,6 @@ cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
atomic_read(&tcon->stats.cifs_stats.num_ffirst), atomic_read(&tcon->stats.cifs_stats.num_ffirst),
atomic_read(&tcon->stats.cifs_stats.num_fnext), atomic_read(&tcon->stats.cifs_stats.num_fnext),
atomic_read(&tcon->stats.cifs_stats.num_fclose)); atomic_read(&tcon->stats.cifs_stats.num_fclose));
#endif
} }
static void static void

View File

@ -120,7 +120,9 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
break; break;
} }
if (use_cached_root_handle == false) if (use_cached_root_handle)
close_shroot(&tcon->crfid);
else
rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
if (tmprc) if (tmprc)
rc = tmprc; rc = tmprc;
@ -281,7 +283,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
int rc; int rc;
if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
(buf->LastWriteTime == 0) && (buf->ChangeTime) && (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) &&
(buf->Attributes == 0)) (buf->Attributes == 0))
return 0; /* would be a no op, no sense sending this */ return 0; /* would be a no op, no sense sending this */

View File

@ -93,7 +93,6 @@ static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
/* SMB2_OPLOCK_BREAK */ cpu_to_le16(24) /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24)
}; };
#ifdef CONFIG_CIFS_SMB311
static __u32 get_neg_ctxt_len(struct smb2_sync_hdr *hdr, __u32 len, static __u32 get_neg_ctxt_len(struct smb2_sync_hdr *hdr, __u32 len,
__u32 non_ctxlen) __u32 non_ctxlen)
{ {
@ -127,7 +126,6 @@ static __u32 get_neg_ctxt_len(struct smb2_sync_hdr *hdr, __u32 len,
/* length of negcontexts including pad from end of sec blob to them */ /* length of negcontexts including pad from end of sec blob to them */
return (len - nc_offset) + size_of_pad_before_neg_ctxts; return (len - nc_offset) + size_of_pad_before_neg_ctxts;
} }
#endif /* CIFS_SMB311 */
int int
smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr) smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr)
@ -222,10 +220,9 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr)
clc_len = smb2_calc_size(buf, srvr); clc_len = smb2_calc_size(buf, srvr);
#ifdef CONFIG_CIFS_SMB311
if (shdr->Command == SMB2_NEGOTIATE) if (shdr->Command == SMB2_NEGOTIATE)
clc_len += get_neg_ctxt_len(shdr, len, clc_len); clc_len += get_neg_ctxt_len(shdr, len, clc_len);
#endif /* SMB311 */
if (len != clc_len) { if (len != clc_len) {
cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n", cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n",
clc_len, len, mid); clc_len, len, mid);
@ -451,15 +448,13 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
/* Windows doesn't allow paths beginning with \ */ /* Windows doesn't allow paths beginning with \ */
if (from[0] == '\\') if (from[0] == '\\')
start_of_path = from + 1; start_of_path = from + 1;
#ifdef CONFIG_CIFS_SMB311
/* SMB311 POSIX extensions paths do not include leading slash */ /* SMB311 POSIX extensions paths do not include leading slash */
else if (cifs_sb_master_tlink(cifs_sb) && else if (cifs_sb_master_tlink(cifs_sb) &&
cifs_sb_master_tcon(cifs_sb)->posix_extensions && cifs_sb_master_tcon(cifs_sb)->posix_extensions &&
(from[0] == '/')) { (from[0] == '/')) {
start_of_path = from + 1; start_of_path = from + 1;
} } else
#endif /* 311 */
else
start_of_path = from; start_of_path = from;
to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len, to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len,
@ -759,7 +754,6 @@ smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
return 0; return 0;
} }
#ifdef CONFIG_CIFS_SMB311
/** /**
* smb311_update_preauth_hash - update @ses hash with the packet data in @iov * smb311_update_preauth_hash - update @ses hash with the packet data in @iov
* *
@ -821,4 +815,3 @@ smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec)
return 0; return 0;
} }
#endif

View File

@ -444,7 +444,11 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */, FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
NULL /* no data input */, 0 /* no data input */, NULL /* no data input */, 0 /* no data input */,
(char **)&out_buf, &ret_data_len); (char **)&out_buf, &ret_data_len);
if (rc != 0) { if (rc == -EOPNOTSUPP) {
cifs_dbg(FYI,
"server does not support query network interfaces\n");
goto out;
} else if (rc != 0) {
cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc); cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc);
goto out; goto out;
} }
@ -466,21 +470,36 @@ out:
return rc; return rc;
} }
void static void
smb2_cached_lease_break(struct work_struct *work) smb2_close_cached_fid(struct kref *ref)
{ {
struct cached_fid *cfid = container_of(work, struct cached_fid *cfid = container_of(ref, struct cached_fid,
struct cached_fid, lease_break); refcount);
mutex_lock(&cfid->fid_mutex);
if (cfid->is_valid) { if (cfid->is_valid) {
cifs_dbg(FYI, "clear cached root file handle\n"); cifs_dbg(FYI, "clear cached root file handle\n");
SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid, SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
cfid->fid->volatile_fid); cfid->fid->volatile_fid);
cfid->is_valid = false; cfid->is_valid = false;
} }
}
void close_shroot(struct cached_fid *cfid)
{
mutex_lock(&cfid->fid_mutex);
kref_put(&cfid->refcount, smb2_close_cached_fid);
mutex_unlock(&cfid->fid_mutex); mutex_unlock(&cfid->fid_mutex);
} }
void
smb2_cached_lease_break(struct work_struct *work)
{
struct cached_fid *cfid = container_of(work,
struct cached_fid, lease_break);
close_shroot(cfid);
}
/* /*
* Open the directory at the root of a share * Open the directory at the root of a share
*/ */
@ -495,6 +514,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
if (tcon->crfid.is_valid) { if (tcon->crfid.is_valid) {
cifs_dbg(FYI, "found a cached root file handle\n"); cifs_dbg(FYI, "found a cached root file handle\n");
memcpy(pfid, tcon->crfid.fid, sizeof(struct cifs_fid)); memcpy(pfid, tcon->crfid.fid, sizeof(struct cifs_fid));
kref_get(&tcon->crfid.refcount);
mutex_unlock(&tcon->crfid.fid_mutex); mutex_unlock(&tcon->crfid.fid_mutex);
return 0; return 0;
} }
@ -511,6 +531,8 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid)); memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
tcon->crfid.tcon = tcon; tcon->crfid.tcon = tcon;
tcon->crfid.is_valid = true; tcon->crfid.is_valid = true;
kref_init(&tcon->crfid.refcount);
kref_get(&tcon->crfid.refcount);
} }
mutex_unlock(&tcon->crfid.fid_mutex); mutex_unlock(&tcon->crfid.fid_mutex);
return rc; return rc;
@ -548,10 +570,15 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
FS_ATTRIBUTE_INFORMATION); FS_ATTRIBUTE_INFORMATION);
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
FS_DEVICE_INFORMATION); FS_DEVICE_INFORMATION);
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
FS_VOLUME_INFORMATION);
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */ FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */
if (no_cached_open) if (no_cached_open)
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
else
close_shroot(&tcon->crfid);
return; return;
} }
@ -873,13 +900,11 @@ smb2_can_echo(struct TCP_Server_Info *server)
static void static void
smb2_clear_stats(struct cifs_tcon *tcon) smb2_clear_stats(struct cifs_tcon *tcon)
{ {
#ifdef CONFIG_CIFS_STATS
int i; int i;
for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) {
atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0); atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0);
atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0); atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0);
} }
#endif
} }
static void static void
@ -918,67 +943,58 @@ smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon)
static void static void
smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
{ {
#ifdef CONFIG_CIFS_STATS
atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent; atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent;
atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed; atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed;
seq_printf(m, "\nNegotiates: %d sent %d failed",
atomic_read(&sent[SMB2_NEGOTIATE_HE]), /*
atomic_read(&failed[SMB2_NEGOTIATE_HE])); * Can't display SMB2_NEGOTIATE, SESSION_SETUP, LOGOFF, CANCEL and ECHO
seq_printf(m, "\nSessionSetups: %d sent %d failed", * totals (requests sent) since those SMBs are per-session not per tcon
atomic_read(&sent[SMB2_SESSION_SETUP_HE]), */
atomic_read(&failed[SMB2_SESSION_SETUP_HE])); seq_printf(m, "\nBytes read: %llu Bytes written: %llu",
seq_printf(m, "\nLogoffs: %d sent %d failed", (long long)(tcon->bytes_read),
atomic_read(&sent[SMB2_LOGOFF_HE]), (long long)(tcon->bytes_written));
atomic_read(&failed[SMB2_LOGOFF_HE])); seq_printf(m, "\nTreeConnects: %d total %d failed",
seq_printf(m, "\nTreeConnects: %d sent %d failed",
atomic_read(&sent[SMB2_TREE_CONNECT_HE]), atomic_read(&sent[SMB2_TREE_CONNECT_HE]),
atomic_read(&failed[SMB2_TREE_CONNECT_HE])); atomic_read(&failed[SMB2_TREE_CONNECT_HE]));
seq_printf(m, "\nTreeDisconnects: %d sent %d failed", seq_printf(m, "\nTreeDisconnects: %d total %d failed",
atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]), atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]),
atomic_read(&failed[SMB2_TREE_DISCONNECT_HE])); atomic_read(&failed[SMB2_TREE_DISCONNECT_HE]));
seq_printf(m, "\nCreates: %d sent %d failed", seq_printf(m, "\nCreates: %d total %d failed",
atomic_read(&sent[SMB2_CREATE_HE]), atomic_read(&sent[SMB2_CREATE_HE]),
atomic_read(&failed[SMB2_CREATE_HE])); atomic_read(&failed[SMB2_CREATE_HE]));
seq_printf(m, "\nCloses: %d sent %d failed", seq_printf(m, "\nCloses: %d total %d failed",
atomic_read(&sent[SMB2_CLOSE_HE]), atomic_read(&sent[SMB2_CLOSE_HE]),
atomic_read(&failed[SMB2_CLOSE_HE])); atomic_read(&failed[SMB2_CLOSE_HE]));
seq_printf(m, "\nFlushes: %d sent %d failed", seq_printf(m, "\nFlushes: %d total %d failed",
atomic_read(&sent[SMB2_FLUSH_HE]), atomic_read(&sent[SMB2_FLUSH_HE]),
atomic_read(&failed[SMB2_FLUSH_HE])); atomic_read(&failed[SMB2_FLUSH_HE]));
seq_printf(m, "\nReads: %d sent %d failed", seq_printf(m, "\nReads: %d total %d failed",
atomic_read(&sent[SMB2_READ_HE]), atomic_read(&sent[SMB2_READ_HE]),
atomic_read(&failed[SMB2_READ_HE])); atomic_read(&failed[SMB2_READ_HE]));
seq_printf(m, "\nWrites: %d sent %d failed", seq_printf(m, "\nWrites: %d total %d failed",
atomic_read(&sent[SMB2_WRITE_HE]), atomic_read(&sent[SMB2_WRITE_HE]),
atomic_read(&failed[SMB2_WRITE_HE])); atomic_read(&failed[SMB2_WRITE_HE]));
seq_printf(m, "\nLocks: %d sent %d failed", seq_printf(m, "\nLocks: %d total %d failed",
atomic_read(&sent[SMB2_LOCK_HE]), atomic_read(&sent[SMB2_LOCK_HE]),
atomic_read(&failed[SMB2_LOCK_HE])); atomic_read(&failed[SMB2_LOCK_HE]));
seq_printf(m, "\nIOCTLs: %d sent %d failed", seq_printf(m, "\nIOCTLs: %d total %d failed",
atomic_read(&sent[SMB2_IOCTL_HE]), atomic_read(&sent[SMB2_IOCTL_HE]),
atomic_read(&failed[SMB2_IOCTL_HE])); atomic_read(&failed[SMB2_IOCTL_HE]));
seq_printf(m, "\nCancels: %d sent %d failed", seq_printf(m, "\nQueryDirectories: %d total %d failed",
atomic_read(&sent[SMB2_CANCEL_HE]),
atomic_read(&failed[SMB2_CANCEL_HE]));
seq_printf(m, "\nEchos: %d sent %d failed",
atomic_read(&sent[SMB2_ECHO_HE]),
atomic_read(&failed[SMB2_ECHO_HE]));
seq_printf(m, "\nQueryDirectories: %d sent %d failed",
atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]), atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]),
atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE])); atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE]));
seq_printf(m, "\nChangeNotifies: %d sent %d failed", seq_printf(m, "\nChangeNotifies: %d total %d failed",
atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]), atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]),
atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE])); atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE]));
seq_printf(m, "\nQueryInfos: %d sent %d failed", seq_printf(m, "\nQueryInfos: %d total %d failed",
atomic_read(&sent[SMB2_QUERY_INFO_HE]), atomic_read(&sent[SMB2_QUERY_INFO_HE]),
atomic_read(&failed[SMB2_QUERY_INFO_HE])); atomic_read(&failed[SMB2_QUERY_INFO_HE]));
seq_printf(m, "\nSetInfos: %d sent %d failed", seq_printf(m, "\nSetInfos: %d total %d failed",
atomic_read(&sent[SMB2_SET_INFO_HE]), atomic_read(&sent[SMB2_SET_INFO_HE]),
atomic_read(&failed[SMB2_SET_INFO_HE])); atomic_read(&failed[SMB2_SET_INFO_HE]));
seq_printf(m, "\nOplockBreaks: %d sent %d failed", seq_printf(m, "\nOplockBreaks: %d sent %d failed",
atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]), atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]),
atomic_read(&failed[SMB2_OPLOCK_BREAK_HE])); atomic_read(&failed[SMB2_OPLOCK_BREAK_HE]));
#endif
} }
static void static void
@ -1353,6 +1369,13 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
} }
/* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */
#define GMT_TOKEN_SIZE 50
/*
* Input buffer contains (empty) struct smb_snapshot array with size filled in
* For output see struct SRV_SNAPSHOT_ARRAY in MS-SMB2 section 2.2.32.2
*/
static int static int
smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile, void __user *ioc_buf) struct cifsFileInfo *cfile, void __user *ioc_buf)
@ -1382,14 +1405,27 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
kfree(retbuf); kfree(retbuf);
return rc; return rc;
} }
if (snapshot_in.snapshot_array_size < sizeof(struct smb_snapshot_array)) {
rc = -ERANGE;
kfree(retbuf);
return rc;
}
if (ret_data_len > snapshot_in.snapshot_array_size) /*
ret_data_len = snapshot_in.snapshot_array_size; * Check for min size, ie not large enough to fit even one GMT
* token (snapshot). On the first ioctl some users may pass in
* smaller size (or zero) to simply get the size of the array
* so the user space caller can allocate sufficient memory
* and retry the ioctl again with larger array size sufficient
* to hold all of the snapshot GMT tokens on the second try.
*/
if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE)
ret_data_len = sizeof(struct smb_snapshot_array);
/*
* We return struct SRV_SNAPSHOT_ARRAY, followed by
* the snapshot array (of 50 byte GMT tokens) each
* representing an available previous version of the data
*/
if (ret_data_len > (snapshot_in.snapshot_array_size +
sizeof(struct smb_snapshot_array)))
ret_data_len = snapshot_in.snapshot_array_size +
sizeof(struct smb_snapshot_array);
if (copy_to_user(ioc_buf, retbuf, ret_data_len)) if (copy_to_user(ioc_buf, retbuf, ret_data_len))
rc = -EFAULT; rc = -EFAULT;
@ -1487,7 +1523,11 @@ smb2_is_session_expired(char *buf)
shdr->Status != STATUS_USER_SESSION_DELETED) shdr->Status != STATUS_USER_SESSION_DELETED)
return false; return false;
trace_smb3_ses_expired(shdr->TreeId, shdr->SessionId,
le16_to_cpu(shdr->Command),
le64_to_cpu(shdr->MessageId));
cifs_dbg(FYI, "Session expired or deleted\n"); cifs_dbg(FYI, "Session expired or deleted\n");
return true; return true;
} }
@ -1504,9 +1544,130 @@ smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
CIFS_CACHE_READ(cinode) ? 1 : 0); CIFS_CACHE_READ(cinode) ? 1 : 0);
} }
static void
smb2_set_related(struct smb_rqst *rqst)
{
struct smb2_sync_hdr *shdr;
shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base);
shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS;
}
char smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0};
static void
smb2_set_next_command(struct TCP_Server_Info *server, struct smb_rqst *rqst)
{
struct smb2_sync_hdr *shdr;
unsigned long len = smb_rqst_len(server, rqst);
/* SMB headers in a compound are 8 byte aligned. */
if (len & 7) {
rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding;
rqst->rq_iov[rqst->rq_nvec].iov_len = 8 - (len & 7);
rqst->rq_nvec++;
len = smb_rqst_len(server, rqst);
}
shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base);
shdr->NextCommand = cpu_to_le32(len);
}
static int static int
smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
struct kstatfs *buf) struct kstatfs *buf)
{
struct smb2_query_info_rsp *rsp;
struct smb2_fs_full_size_info *info = NULL;
struct smb_rqst rqst[3];
int resp_buftype[3];
struct kvec rsp_iov[3];
struct kvec open_iov[5]; /* 4 + potential padding. */
struct kvec qi_iov[1];
struct kvec close_iov[1];
struct cifs_ses *ses = tcon->ses;
struct TCP_Server_Info *server = ses->server;
__le16 srch_path = 0; /* Null - open root of share */
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
struct cifs_open_parms oparms;
struct cifs_fid fid;
int flags = 0;
int rc;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
memset(rqst, 0, sizeof(rqst));
memset(resp_buftype, 0, sizeof(resp_buftype));
memset(rsp_iov, 0, sizeof(rsp_iov));
memset(&open_iov, 0, sizeof(open_iov));
rqst[0].rq_iov = open_iov;
rqst[0].rq_nvec = 4;
oparms.tcon = tcon;
oparms.desired_access = FILE_READ_ATTRIBUTES;
oparms.disposition = FILE_OPEN;
oparms.create_options = 0;
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, &srch_path);
if (rc)
goto qfs_exit;
smb2_set_next_command(server, &rqst[0]);
memset(&qi_iov, 0, sizeof(qi_iov));
rqst[1].rq_iov = qi_iov;
rqst[1].rq_nvec = 1;
rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID, COMPOUND_FID,
FS_FULL_SIZE_INFORMATION,
SMB2_O_INFO_FILESYSTEM, 0,
sizeof(struct smb2_fs_full_size_info));
if (rc)
goto qfs_exit;
smb2_set_next_command(server, &rqst[1]);
smb2_set_related(&rqst[1]);
memset(&close_iov, 0, sizeof(close_iov));
rqst[2].rq_iov = close_iov;
rqst[2].rq_nvec = 1;
rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID);
if (rc)
goto qfs_exit;
smb2_set_related(&rqst[2]);
rc = compound_send_recv(xid, ses, flags, 3, rqst,
resp_buftype, rsp_iov);
if (rc)
goto qfs_exit;
rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
buf->f_type = SMB2_MAGIC_NUMBER;
info = (struct smb2_fs_full_size_info *)(
le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp);
rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength),
&rsp_iov[1],
sizeof(struct smb2_fs_full_size_info));
if (!rc)
smb2_copy_fs_info_to_kstatfs(info, buf);
qfs_exit:
SMB2_open_free(&rqst[0]);
SMB2_query_info_free(&rqst[1]);
SMB2_close_free(&rqst[2]);
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
return rc;
}
static int
smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
struct kstatfs *buf)
{ {
int rc; int rc;
__le16 srch_path = 0; /* Null - open root of share */ __le16 srch_path = 0; /* Null - open root of share */
@ -1514,6 +1675,9 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_open_parms oparms; struct cifs_open_parms oparms;
struct cifs_fid fid; struct cifs_fid fid;
if (!tcon->posix_extensions)
return smb2_queryfs(xid, tcon, buf);
oparms.tcon = tcon; oparms.tcon = tcon;
oparms.desired_access = FILE_READ_ATTRIBUTES; oparms.desired_access = FILE_READ_ATTRIBUTES;
oparms.disposition = FILE_OPEN; oparms.disposition = FILE_OPEN;
@ -1524,9 +1688,10 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, NULL); rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, NULL);
if (rc) if (rc)
return rc; return rc;
rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid,
fid.volatile_fid, buf);
buf->f_type = SMB2_MAGIC_NUMBER; buf->f_type = SMB2_MAGIC_NUMBER;
rc = SMB2_QFS_info(xid, tcon, fid.persistent_fid, fid.volatile_fid,
buf);
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
return rc; return rc;
} }
@ -1700,7 +1865,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
&resp_buftype); &resp_buftype);
if (!rc || !err_iov.iov_base) { if (!rc || !err_iov.iov_base) {
rc = -ENOENT; rc = -ENOENT;
goto querty_exit; goto free_path;
} }
err_buf = err_iov.iov_base; err_buf = err_iov.iov_base;
@ -1741,6 +1906,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
querty_exit: querty_exit:
free_rsp_buf(resp_buftype, err_buf); free_rsp_buf(resp_buftype, err_buf);
free_path:
kfree(utf16_path); kfree(utf16_path);
return rc; return rc;
} }
@ -2326,35 +2492,51 @@ static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf,
sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf)); sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
} }
/* Assumes: /* Assumes the first rqst has a transform header as the first iov.
* rqst->rq_iov[0] is transform header * I.e.
* rqst->rq_iov[1+] data to be encrypted/decrypted * rqst[0].rq_iov[0] is transform header
* rqst[0].rq_iov[1+] data to be encrypted/decrypted
* rqst[1+].rq_iov[0+] data to be encrypted/decrypted
*/ */
static struct scatterlist * static struct scatterlist *
init_sg(struct smb_rqst *rqst, u8 *sign) init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign)
{ {
unsigned int sg_len = rqst->rq_nvec + rqst->rq_npages + 1; unsigned int sg_len;
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
struct scatterlist *sg; struct scatterlist *sg;
unsigned int i; unsigned int i;
unsigned int j; unsigned int j;
unsigned int idx = 0;
int skip;
sg_len = 1;
for (i = 0; i < num_rqst; i++)
sg_len += rqst[i].rq_nvec + rqst[i].rq_npages;
sg = kmalloc_array(sg_len, sizeof(struct scatterlist), GFP_KERNEL); sg = kmalloc_array(sg_len, sizeof(struct scatterlist), GFP_KERNEL);
if (!sg) if (!sg)
return NULL; return NULL;
sg_init_table(sg, sg_len); sg_init_table(sg, sg_len);
smb2_sg_set_buf(&sg[0], rqst->rq_iov[0].iov_base + 20, assoc_data_len); for (i = 0; i < num_rqst; i++) {
for (i = 1; i < rqst->rq_nvec; i++) for (j = 0; j < rqst[i].rq_nvec; j++) {
smb2_sg_set_buf(&sg[i], rqst->rq_iov[i].iov_base, /*
rqst->rq_iov[i].iov_len); * The first rqst has a transform header where the
for (j = 0; i < sg_len - 1; i++, j++) { * first 20 bytes are not part of the encrypted blob
unsigned int len, offset; */
skip = (i == 0) && (j == 0) ? 20 : 0;
smb2_sg_set_buf(&sg[idx++],
rqst[i].rq_iov[j].iov_base + skip,
rqst[i].rq_iov[j].iov_len - skip);
}
rqst_page_get_length(rqst, j, &len, &offset); for (j = 0; j < rqst[i].rq_npages; j++) {
sg_set_page(&sg[i], rqst->rq_pages[j], len, offset); unsigned int len, offset;
rqst_page_get_length(&rqst[i], j, &len, &offset);
sg_set_page(&sg[idx++], rqst[i].rq_pages[j], len, offset);
}
} }
smb2_sg_set_buf(&sg[sg_len - 1], sign, SMB2_SIGNATURE_SIZE); smb2_sg_set_buf(&sg[idx], sign, SMB2_SIGNATURE_SIZE);
return sg; return sg;
} }
@ -2386,10 +2568,11 @@ smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key)
* untouched. * untouched.
*/ */
static int static int
crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc) crypt_message(struct TCP_Server_Info *server, int num_rqst,
struct smb_rqst *rqst, int enc)
{ {
struct smb2_transform_hdr *tr_hdr = struct smb2_transform_hdr *tr_hdr =
(struct smb2_transform_hdr *)rqst->rq_iov[0].iov_base; (struct smb2_transform_hdr *)rqst[0].rq_iov[0].iov_base;
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
int rc = 0; int rc = 0;
struct scatterlist *sg; struct scatterlist *sg;
@ -2440,7 +2623,7 @@ crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
crypt_len += SMB2_SIGNATURE_SIZE; crypt_len += SMB2_SIGNATURE_SIZE;
} }
sg = init_sg(rqst, sign); sg = init_sg(num_rqst, rqst, sign);
if (!sg) { if (!sg) {
cifs_dbg(VFS, "%s: Failed to init sg", __func__); cifs_dbg(VFS, "%s: Failed to init sg", __func__);
rc = -ENOMEM; rc = -ENOMEM;
@ -2477,101 +2660,96 @@ free_req:
return rc; return rc;
} }
static int void
smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst)
struct smb_rqst *old_rq)
{ {
struct kvec *iov; int i, j;
struct page **pages;
struct smb2_transform_hdr *tr_hdr;
unsigned int npages = old_rq->rq_npages;
unsigned int orig_len;
int i;
int rc = -ENOMEM;
pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); for (i = 0; i < num_rqst; i++) {
if (!pages) if (rqst[i].rq_pages) {
return rc; for (j = rqst[i].rq_npages - 1; j >= 0; j--)
put_page(rqst[i].rq_pages[j]);
new_rq->rq_pages = pages; kfree(rqst[i].rq_pages);
new_rq->rq_offset = old_rq->rq_offset; }
new_rq->rq_npages = old_rq->rq_npages;
new_rq->rq_pagesz = old_rq->rq_pagesz;
new_rq->rq_tailsz = old_rq->rq_tailsz;
for (i = 0; i < npages; i++) {
pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
if (!pages[i])
goto err_free_pages;
} }
iov = kmalloc_array(old_rq->rq_nvec + 1, sizeof(struct kvec),
GFP_KERNEL);
if (!iov)
goto err_free_pages;
/* copy all iovs from the old */
memcpy(&iov[1], &old_rq->rq_iov[0],
sizeof(struct kvec) * old_rq->rq_nvec);
new_rq->rq_iov = iov;
new_rq->rq_nvec = old_rq->rq_nvec + 1;
tr_hdr = kmalloc(sizeof(struct smb2_transform_hdr), GFP_KERNEL);
if (!tr_hdr)
goto err_free_iov;
orig_len = smb_rqst_len(server, old_rq);
/* fill the 2nd iov with a transform header */
fill_transform_hdr(tr_hdr, orig_len, old_rq);
new_rq->rq_iov[0].iov_base = tr_hdr;
new_rq->rq_iov[0].iov_len = sizeof(struct smb2_transform_hdr);
/* copy pages form the old */
for (i = 0; i < npages; i++) {
char *dst, *src;
unsigned int offset, len;
rqst_page_get_length(new_rq, i, &len, &offset);
dst = (char *) kmap(new_rq->rq_pages[i]) + offset;
src = (char *) kmap(old_rq->rq_pages[i]) + offset;
memcpy(dst, src, len);
kunmap(new_rq->rq_pages[i]);
kunmap(old_rq->rq_pages[i]);
}
rc = crypt_message(server, new_rq, 1);
cifs_dbg(FYI, "encrypt message returned %d", rc);
if (rc)
goto err_free_tr_hdr;
return rc;
err_free_tr_hdr:
kfree(tr_hdr);
err_free_iov:
kfree(iov);
err_free_pages:
for (i = i - 1; i >= 0; i--)
put_page(pages[i]);
kfree(pages);
return rc;
} }
static void /*
smb3_free_transform_rq(struct smb_rqst *rqst) * This function will initialize new_rq and encrypt the content.
* The first entry, new_rq[0], only contains a single iov which contains
* a smb2_transform_hdr and is pre-allocated by the caller.
* This function then populates new_rq[1+] with the content from olq_rq[0+].
*
* The end result is an array of smb_rqst structures where the first structure
* only contains a single iov for the transform header which we then can pass
* to crypt_message().
*
* new_rq[0].rq_iov[0] : smb2_transform_hdr pre-allocated by the caller
* new_rq[1+].rq_iov[*] == old_rq[0+].rq_iov[*] : SMB2/3 requests
*/
static int
smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst,
struct smb_rqst *new_rq, struct smb_rqst *old_rq)
{ {
int i = rqst->rq_npages - 1; struct page **pages;
struct smb2_transform_hdr *tr_hdr = new_rq[0].rq_iov[0].iov_base;
unsigned int npages;
unsigned int orig_len = 0;
int i, j;
int rc = -ENOMEM;
for (; i >= 0; i--) for (i = 1; i < num_rqst; i++) {
put_page(rqst->rq_pages[i]); npages = old_rq[i - 1].rq_npages;
kfree(rqst->rq_pages); pages = kmalloc_array(npages, sizeof(struct page *),
/* free transform header */ GFP_KERNEL);
kfree(rqst->rq_iov[0].iov_base); if (!pages)
kfree(rqst->rq_iov); goto err_free;
new_rq[i].rq_pages = pages;
new_rq[i].rq_npages = npages;
new_rq[i].rq_offset = old_rq[i - 1].rq_offset;
new_rq[i].rq_pagesz = old_rq[i - 1].rq_pagesz;
new_rq[i].rq_tailsz = old_rq[i - 1].rq_tailsz;
new_rq[i].rq_iov = old_rq[i - 1].rq_iov;
new_rq[i].rq_nvec = old_rq[i - 1].rq_nvec;
orig_len += smb_rqst_len(server, &old_rq[i - 1]);
for (j = 0; j < npages; j++) {
pages[j] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
if (!pages[j])
goto err_free;
}
/* copy pages form the old */
for (j = 0; j < npages; j++) {
char *dst, *src;
unsigned int offset, len;
rqst_page_get_length(&new_rq[i], j, &len, &offset);
dst = (char *) kmap(new_rq[i].rq_pages[j]) + offset;
src = (char *) kmap(old_rq[i - 1].rq_pages[j]) + offset;
memcpy(dst, src, len);
kunmap(new_rq[i].rq_pages[j]);
kunmap(old_rq[i - 1].rq_pages[j]);
}
}
/* fill the 1st iov with a transform header */
fill_transform_hdr(tr_hdr, orig_len, old_rq);
rc = crypt_message(server, num_rqst, new_rq, 1);
cifs_dbg(FYI, "encrypt message returned %d", rc);
if (rc)
goto err_free;
return rc;
err_free:
smb3_free_compound_rqst(num_rqst - 1, &new_rq[1]);
return rc;
} }
static int static int
@ -2603,7 +2781,7 @@ decrypt_raw_data(struct TCP_Server_Info *server, char *buf,
rqst.rq_pagesz = PAGE_SIZE; rqst.rq_pagesz = PAGE_SIZE;
rqst.rq_tailsz = (page_data_size % PAGE_SIZE) ? : PAGE_SIZE; rqst.rq_tailsz = (page_data_size % PAGE_SIZE) ? : PAGE_SIZE;
rc = crypt_message(server, &rqst, 0); rc = crypt_message(server, 1, &rqst, 0);
cifs_dbg(FYI, "decrypt message returned %d\n", rc); cifs_dbg(FYI, "decrypt message returned %d\n", rc);
if (rc) if (rc)
@ -2878,13 +3056,20 @@ discard_data:
static int static int
receive_encrypted_standard(struct TCP_Server_Info *server, receive_encrypted_standard(struct TCP_Server_Info *server,
struct mid_q_entry **mid) struct mid_q_entry **mids, char **bufs,
int *num_mids)
{ {
int length; int ret, length;
char *buf = server->smallbuf; char *buf = server->smallbuf;
char *tmpbuf;
struct smb2_sync_hdr *shdr;
unsigned int pdu_length = server->pdu_size; unsigned int pdu_length = server->pdu_size;
unsigned int buf_size; unsigned int buf_size;
struct mid_q_entry *mid_entry; struct mid_q_entry *mid_entry;
int next_is_large;
char *next_buffer = NULL;
*num_mids = 0;
/* switch to large buffer if too big for a small one */ /* switch to large buffer if too big for a small one */
if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) { if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) {
@ -2905,24 +3090,61 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
if (length) if (length)
return length; return length;
next_is_large = server->large_buf;
one_more:
shdr = (struct smb2_sync_hdr *)buf;
if (shdr->NextCommand) {
if (next_is_large) {
tmpbuf = server->bigbuf;
next_buffer = (char *)cifs_buf_get();
} else {
tmpbuf = server->smallbuf;
next_buffer = (char *)cifs_small_buf_get();
}
memcpy(next_buffer,
tmpbuf + le32_to_cpu(shdr->NextCommand),
pdu_length - le32_to_cpu(shdr->NextCommand));
}
mid_entry = smb2_find_mid(server, buf); mid_entry = smb2_find_mid(server, buf);
if (mid_entry == NULL) if (mid_entry == NULL)
cifs_dbg(FYI, "mid not found\n"); cifs_dbg(FYI, "mid not found\n");
else { else {
cifs_dbg(FYI, "mid found\n"); cifs_dbg(FYI, "mid found\n");
mid_entry->decrypted = true; mid_entry->decrypted = true;
mid_entry->resp_buf_size = server->pdu_size;
} }
*mid = mid_entry; if (*num_mids >= MAX_COMPOUND) {
cifs_dbg(VFS, "too many PDUs in compound\n");
return -1;
}
bufs[*num_mids] = buf;
mids[(*num_mids)++] = mid_entry;
if (mid_entry && mid_entry->handle) if (mid_entry && mid_entry->handle)
return mid_entry->handle(server, mid_entry); ret = mid_entry->handle(server, mid_entry);
else
ret = cifs_handle_standard(server, mid_entry);
return cifs_handle_standard(server, mid_entry); if (ret == 0 && shdr->NextCommand) {
pdu_length -= le32_to_cpu(shdr->NextCommand);
server->large_buf = next_is_large;
if (next_is_large)
server->bigbuf = next_buffer;
else
server->smallbuf = next_buffer;
buf += le32_to_cpu(shdr->NextCommand);
goto one_more;
}
return ret;
} }
static int static int
smb3_receive_transform(struct TCP_Server_Info *server, struct mid_q_entry **mid) smb3_receive_transform(struct TCP_Server_Info *server,
struct mid_q_entry **mids, char **bufs, int *num_mids)
{ {
char *buf = server->smallbuf; char *buf = server->smallbuf;
unsigned int pdu_length = server->pdu_size; unsigned int pdu_length = server->pdu_size;
@ -2945,10 +3167,11 @@ smb3_receive_transform(struct TCP_Server_Info *server, struct mid_q_entry **mid)
return -ECONNABORTED; return -ECONNABORTED;
} }
/* TODO: add support for compounds containing READ. */
if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server))
return receive_encrypted_read(server, mid); return receive_encrypted_read(server, &mids[0]);
return receive_encrypted_standard(server, mid); return receive_encrypted_standard(server, mids, bufs, num_mids);
} }
int int
@ -3250,7 +3473,6 @@ struct smb_version_operations smb30_operations = {
.fallocate = smb3_fallocate, .fallocate = smb3_fallocate,
.enum_snapshots = smb3_enum_snapshots, .enum_snapshots = smb3_enum_snapshots,
.init_transform_rq = smb3_init_transform_rq, .init_transform_rq = smb3_init_transform_rq,
.free_transform_rq = smb3_free_transform_rq,
.is_transform_hdr = smb3_is_transform_hdr, .is_transform_hdr = smb3_is_transform_hdr,
.receive_transform = smb3_receive_transform, .receive_transform = smb3_receive_transform,
.get_dfs_refer = smb2_get_dfs_refer, .get_dfs_refer = smb2_get_dfs_refer,
@ -3267,7 +3489,6 @@ struct smb_version_operations smb30_operations = {
.next_header = smb2_next_header, .next_header = smb2_next_header,
}; };
#ifdef CONFIG_CIFS_SMB311
struct smb_version_operations smb311_operations = { struct smb_version_operations smb311_operations = {
.compare_fids = smb2_compare_fids, .compare_fids = smb2_compare_fids,
.setup_request = smb2_setup_request, .setup_request = smb2_setup_request,
@ -3335,7 +3556,7 @@ struct smb_version_operations smb311_operations = {
.is_status_pending = smb2_is_status_pending, .is_status_pending = smb2_is_status_pending,
.is_session_expired = smb2_is_session_expired, .is_session_expired = smb2_is_session_expired,
.oplock_response = smb2_oplock_response, .oplock_response = smb2_oplock_response,
.queryfs = smb2_queryfs, .queryfs = smb311_queryfs,
.mand_lock = smb2_mand_lock, .mand_lock = smb2_mand_lock,
.mand_unlock_range = smb2_unlock_range, .mand_unlock_range = smb2_unlock_range,
.push_mand_locks = smb2_push_mandatory_locks, .push_mand_locks = smb2_push_mandatory_locks,
@ -3357,7 +3578,6 @@ struct smb_version_operations smb311_operations = {
.fallocate = smb3_fallocate, .fallocate = smb3_fallocate,
.enum_snapshots = smb3_enum_snapshots, .enum_snapshots = smb3_enum_snapshots,
.init_transform_rq = smb3_init_transform_rq, .init_transform_rq = smb3_init_transform_rq,
.free_transform_rq = smb3_free_transform_rq,
.is_transform_hdr = smb3_is_transform_hdr, .is_transform_hdr = smb3_is_transform_hdr,
.receive_transform = smb3_receive_transform, .receive_transform = smb3_receive_transform,
.get_dfs_refer = smb2_get_dfs_refer, .get_dfs_refer = smb2_get_dfs_refer,
@ -3366,9 +3586,13 @@ struct smb_version_operations smb311_operations = {
.query_all_EAs = smb2_query_eas, .query_all_EAs = smb2_query_eas,
.set_EA = smb2_set_ea, .set_EA = smb2_set_ea,
#endif /* CIFS_XATTR */ #endif /* CIFS_XATTR */
#ifdef CONFIG_CIFS_ACL
.get_acl = get_smb2_acl,
.get_acl_by_fid = get_smb2_acl_by_fid,
.set_acl = set_smb2_acl,
#endif /* CIFS_ACL */
.next_header = smb2_next_header, .next_header = smb2_next_header,
}; };
#endif /* CIFS_SMB311 */
struct smb_version_values smb20_values = { struct smb_version_values smb20_values = {
.version_string = SMB20_VERSION_STRING, .version_string = SMB20_VERSION_STRING,
@ -3496,7 +3720,6 @@ struct smb_version_values smb302_values = {
.create_lease_size = sizeof(struct create_lease_v2), .create_lease_size = sizeof(struct create_lease_v2),
}; };
#ifdef CONFIG_CIFS_SMB311
struct smb_version_values smb311_values = { struct smb_version_values smb311_values = {
.version_string = SMB311_VERSION_STRING, .version_string = SMB311_VERSION_STRING,
.protocol_id = SMB311_PROT_ID, .protocol_id = SMB311_PROT_ID,
@ -3517,4 +3740,3 @@ struct smb_version_values smb311_values = {
.signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
.create_lease_size = sizeof(struct create_lease_v2), .create_lease_size = sizeof(struct create_lease_v2),
}; };
#endif /* SMB311 */

View File

@ -80,7 +80,7 @@ static const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
/* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */ /* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */
}; };
static int smb3_encryption_required(const struct cifs_tcon *tcon) int smb3_encryption_required(const struct cifs_tcon *tcon)
{ {
if (!tcon) if (!tcon)
return 0; return 0;
@ -360,17 +360,15 @@ smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
total_len); total_len);
if (tcon != NULL) { if (tcon != NULL) {
#ifdef CONFIG_CIFS_STATS2
uint16_t com_code = le16_to_cpu(smb2_command); uint16_t com_code = le16_to_cpu(smb2_command);
cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]);
#endif
cifs_stats_inc(&tcon->num_smbs_sent); cifs_stats_inc(&tcon->num_smbs_sent);
} }
return rc; return rc;
} }
#ifdef CONFIG_CIFS_SMB311
/* offset is sizeof smb2_negotiate_req but rounded up to 8 bytes */ /* offset is sizeof smb2_negotiate_req but rounded up to 8 bytes */
#define OFFSET_OF_NEG_CONTEXT 0x68 /* sizeof(struct smb2_negotiate_req) */ #define OFFSET_OF_NEG_CONTEXT 0x68 /* sizeof(struct smb2_negotiate_req) */
@ -585,13 +583,6 @@ add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode)
return 0; return 0;
} }
#else
static void assemble_neg_contexts(struct smb2_negotiate_req *req,
unsigned int *total_len)
{
return;
}
#endif /* SMB311 */
/* /*
* *
@ -636,10 +627,9 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
return rc; return rc;
req->sync_hdr.SessionId = 0; req->sync_hdr.SessionId = 0;
#ifdef CONFIG_CIFS_SMB311
memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
#endif
if (strcmp(ses->server->vals->version_string, if (strcmp(ses->server->vals->version_string,
SMB3ANY_VERSION_STRING) == 0) { SMB3ANY_VERSION_STRING) == 0) {
@ -741,10 +731,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); cifs_dbg(FYI, "negotiated smb3.0 dialect\n");
else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID))
cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); cifs_dbg(FYI, "negotiated smb3.02 dialect\n");
#ifdef CONFIG_CIFS_SMB311
else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID))
cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n");
#endif /* SMB311 */
else { else {
cifs_dbg(VFS, "Illegal dialect returned by server 0x%x\n", cifs_dbg(VFS, "Illegal dialect returned by server 0x%x\n",
le16_to_cpu(rsp->DialectRevision)); le16_to_cpu(rsp->DialectRevision));
@ -753,9 +741,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
} }
server->dialect = le16_to_cpu(rsp->DialectRevision); server->dialect = le16_to_cpu(rsp->DialectRevision);
/* BB: add check that dialect was valid given dialect(s) we asked for */
#ifdef CONFIG_CIFS_SMB311
/* /*
* Keep a copy of the hash after negprot. This hash will be * Keep a copy of the hash after negprot. This hash will be
* the starting hash value for all sessions made from this * the starting hash value for all sessions made from this
@ -763,7 +748,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
*/ */
memcpy(server->preauth_sha_hash, ses->preauth_sha_hash, memcpy(server->preauth_sha_hash, ses->preauth_sha_hash,
SMB2_PREAUTH_HASH_SIZE); SMB2_PREAUTH_HASH_SIZE);
#endif
/* SMB2 only has an extended negflavor */ /* SMB2 only has an extended negflavor */
server->negflavor = CIFS_NEGFLAVOR_EXTENDED; server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
/* set it to the maximum buffer size value we can send with 1 credit */ /* set it to the maximum buffer size value we can send with 1 credit */
@ -804,7 +789,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
rc = -EIO; rc = -EIO;
} }
#ifdef CONFIG_CIFS_SMB311
if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
if (rsp->NegotiateContextCount) if (rsp->NegotiateContextCount)
rc = smb311_decode_neg_context(rsp, server, rc = smb311_decode_neg_context(rsp, server,
@ -812,7 +796,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
else else
cifs_dbg(VFS, "Missing expected negotiate contexts\n"); cifs_dbg(VFS, "Missing expected negotiate contexts\n");
} }
#endif /* CONFIG_CIFS_SMB311 */
neg_exit: neg_exit:
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
return rc; return rc;
@ -1373,13 +1356,11 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
sess_data->nls_cp = (struct nls_table *) nls_cp; sess_data->nls_cp = (struct nls_table *) nls_cp;
sess_data->previous_session = ses->Suid; sess_data->previous_session = ses->Suid;
#ifdef CONFIG_CIFS_SMB311
/* /*
* Initialize the session hash with the server one. * Initialize the session hash with the server one.
*/ */
memcpy(ses->preauth_sha_hash, ses->server->preauth_sha_hash, memcpy(ses->preauth_sha_hash, ses->server->preauth_sha_hash,
SMB2_PREAUTH_HASH_SIZE); SMB2_PREAUTH_HASH_SIZE);
#endif
while (sess_data->func) while (sess_data->func)
sess_data->func(sess_data); sess_data->func(sess_data);
@ -1875,6 +1856,51 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec,
return 0; return 0;
} }
/* See MS-SMB2 2.2.13.2.7 */
static struct crt_twarp_ctxt *
create_twarp_buf(__u64 timewarp)
{
struct crt_twarp_ctxt *buf;
buf = kzalloc(sizeof(struct crt_twarp_ctxt), GFP_KERNEL);
if (!buf)
return NULL;
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct crt_twarp_ctxt, Timestamp));
buf->ccontext.DataLength = cpu_to_le32(8);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
(struct crt_twarp_ctxt, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
/* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */
buf->Name[0] = 'T';
buf->Name[1] = 'W';
buf->Name[2] = 'r';
buf->Name[3] = 'p';
buf->Timestamp = cpu_to_le64(timewarp);
return buf;
}
/* See MS-SMB2 2.2.13.2.7 */
static int
add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp)
{
struct smb2_create_req *req = iov[0].iov_base;
unsigned int num = *num_iovec;
iov[num].iov_base = create_twarp_buf(timewarp);
if (iov[num].iov_base == NULL)
return -ENOMEM;
iov[num].iov_len = sizeof(struct crt_twarp_ctxt);
if (!req->CreateContextsOffset)
req->CreateContextsOffset = cpu_to_le32(
sizeof(struct smb2_create_req) +
iov[num - 1].iov_len);
le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_twarp_ctxt));
*num_iovec = num + 1;
return 0;
}
static int static int
alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
const char *treename, const __le16 *path) const char *treename, const __le16 *path)
@ -1920,7 +1946,6 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
return 0; return 0;
} }
#ifdef CONFIG_CIFS_SMB311
int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
umode_t mode, struct cifs_tcon *tcon, umode_t mode, struct cifs_tcon *tcon,
const char *full_path, const char *full_path,
@ -1928,7 +1953,7 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
{ {
struct smb_rqst rqst; struct smb_rqst rqst;
struct smb2_create_req *req; struct smb2_create_req *req;
struct smb2_create_rsp *rsp; struct smb2_create_rsp *rsp = NULL;
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses; struct cifs_ses *ses = tcon->ses;
struct kvec iov[3]; /* make sure at least one for each open context */ struct kvec iov[3]; /* make sure at least one for each open context */
@ -1943,27 +1968,31 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
char *pc_buf = NULL; char *pc_buf = NULL;
int flags = 0; int flags = 0;
unsigned int total_len; unsigned int total_len;
__le16 *path = cifs_convert_path_to_utf16(full_path, cifs_sb); __le16 *utf16_path = NULL;
if (!path)
return -ENOMEM;
cifs_dbg(FYI, "mkdir\n"); cifs_dbg(FYI, "mkdir\n");
/* resource #1: path allocation */
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path)
return -ENOMEM;
if (ses && (ses->server)) if (ses && (ses->server))
server = ses->server; server = ses->server;
else else {
return -EIO; rc = -EIO;
goto err_free_path;
}
/* resource #2: request */
rc = smb2_plain_req_init(SMB2_CREATE, tcon, (void **) &req, &total_len); rc = smb2_plain_req_init(SMB2_CREATE, tcon, (void **) &req, &total_len);
if (rc) if (rc)
return rc; goto err_free_path;
if (smb3_encryption_required(tcon)) if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ; flags |= CIFS_TRANSFORM_REQ;
req->ImpersonationLevel = IL_IMPERSONATION; req->ImpersonationLevel = IL_IMPERSONATION;
req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES); req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES);
/* File attributes ignored on open (used in create though) */ /* File attributes ignored on open (used in create though) */
@ -1992,50 +2021,44 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
rc = alloc_path_with_tree_prefix(&copy_path, &copy_size, rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
&name_len, &name_len,
tcon->treeName, path); tcon->treeName, utf16_path);
if (rc) { if (rc)
cifs_small_buf_release(req); goto err_free_req;
return rc;
}
req->NameLength = cpu_to_le16(name_len * 2); req->NameLength = cpu_to_le16(name_len * 2);
uni_path_len = copy_size; uni_path_len = copy_size;
path = copy_path; /* free before overwriting resource */
kfree(utf16_path);
utf16_path = copy_path;
} else { } else {
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; uni_path_len = (2 * UniStrnlen((wchar_t *)utf16_path, PATH_MAX)) + 2;
/* MUST set path len (NameLength) to 0 opening root of share */ /* MUST set path len (NameLength) to 0 opening root of share */
req->NameLength = cpu_to_le16(uni_path_len - 2); req->NameLength = cpu_to_le16(uni_path_len - 2);
if (uni_path_len % 8 != 0) { if (uni_path_len % 8 != 0) {
copy_size = roundup(uni_path_len, 8); copy_size = roundup(uni_path_len, 8);
copy_path = kzalloc(copy_size, GFP_KERNEL); copy_path = kzalloc(copy_size, GFP_KERNEL);
if (!copy_path) { if (!copy_path) {
cifs_small_buf_release(req); rc = -ENOMEM;
return -ENOMEM; goto err_free_req;
} }
memcpy((char *)copy_path, (const char *)path, memcpy((char *)copy_path, (const char *)utf16_path,
uni_path_len); uni_path_len);
uni_path_len = copy_size; uni_path_len = copy_size;
path = copy_path; /* free before overwriting resource */
kfree(utf16_path);
utf16_path = copy_path;
} }
} }
iov[1].iov_len = uni_path_len; iov[1].iov_len = uni_path_len;
iov[1].iov_base = path; iov[1].iov_base = utf16_path;
req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
if (tcon->posix_extensions) { if (tcon->posix_extensions) {
if (n_iov > 2) { /* resource #3: posix buf */
struct create_context *ccontext =
(struct create_context *)iov[n_iov-1].iov_base;
ccontext->Next =
cpu_to_le32(iov[n_iov-1].iov_len);
}
rc = add_posix_context(iov, &n_iov, mode); rc = add_posix_context(iov, &n_iov, mode);
if (rc) { if (rc)
cifs_small_buf_release(req); goto err_free_req;
kfree(copy_path);
return rc;
}
pc_buf = iov[n_iov-1].iov_base; pc_buf = iov[n_iov-1].iov_base;
} }
@ -2044,73 +2067,57 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
rqst.rq_iov = iov; rqst.rq_iov = iov;
rqst.rq_nvec = n_iov; rqst.rq_nvec = n_iov;
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, /* resource #4: response buffer */
&rsp_iov); rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
if (rc) {
cifs_small_buf_release(req);
rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
if (rc != 0) {
cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid, trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid,
CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES, rc); CREATE_NOT_FILE,
goto smb311_mkdir_exit; FILE_WRITE_ATTRIBUTES, rc);
} else goto err_free_rsp_buf;
trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, }
ses->Suid, CREATE_NOT_FILE,
FILE_WRITE_ATTRIBUTES); rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid,
ses->Suid, CREATE_NOT_FILE,
FILE_WRITE_ATTRIBUTES);
SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId);
/* Eventually save off posix specific response info and timestaps */ /* Eventually save off posix specific response info and timestaps */
smb311_mkdir_exit: err_free_rsp_buf:
kfree(copy_path);
kfree(pc_buf);
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
kfree(pc_buf);
err_free_req:
cifs_small_buf_release(req);
err_free_path:
kfree(utf16_path);
return rc; return rc;
} }
#endif /* SMB311 */
int int
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, __u8 *oplock,
__u8 *oplock, struct smb2_file_all_info *buf, struct cifs_open_parms *oparms, __le16 *path)
struct kvec *err_iov, int *buftype)
{ {
struct smb_rqst rqst; struct TCP_Server_Info *server = tcon->ses->server;
struct smb2_create_req *req; struct smb2_create_req *req;
struct smb2_create_rsp *rsp;
struct TCP_Server_Info *server;
struct cifs_tcon *tcon = oparms->tcon;
struct cifs_ses *ses = tcon->ses;
struct kvec iov[5]; /* make sure at least one for each open context */
struct kvec rsp_iov = {NULL, 0};
int resp_buftype;
int uni_path_len;
__le16 *copy_path = NULL;
int copy_size;
int rc = 0;
unsigned int n_iov = 2; unsigned int n_iov = 2;
__u32 file_attributes = 0; __u32 file_attributes = 0;
char *dhc_buf = NULL, *lc_buf = NULL, *pc_buf = NULL; int copy_size;
int flags = 0; int uni_path_len;
unsigned int total_len; unsigned int total_len;
struct kvec *iov = rqst->rq_iov;
cifs_dbg(FYI, "create/open\n"); __le16 *copy_path;
int rc;
if (ses && (ses->server))
server = ses->server;
else
return -EIO;
rc = smb2_plain_req_init(SMB2_CREATE, tcon, (void **) &req, &total_len); rc = smb2_plain_req_init(SMB2_CREATE, tcon, (void **) &req, &total_len);
if (rc) if (rc)
return rc; return rc;
if (smb3_encryption_required(tcon)) iov[0].iov_base = (char *)req;
flags |= CIFS_TRANSFORM_REQ; /* -1 since last byte is buf[0] which is sent below (path) */
iov[0].iov_len = total_len - 1;
if (oparms->create_options & CREATE_OPTION_READONLY) if (oparms->create_options & CREATE_OPTION_READONLY)
file_attributes |= ATTR_READONLY; file_attributes |= ATTR_READONLY;
@ -2124,11 +2131,6 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
req->ShareAccess = FILE_SHARE_ALL_LE; req->ShareAccess = FILE_SHARE_ALL_LE;
req->CreateDisposition = cpu_to_le32(oparms->disposition); req->CreateDisposition = cpu_to_le32(oparms->disposition);
req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
iov[0].iov_base = (char *)req;
/* -1 since last byte is buf[0] which is sent below (path) */
iov[0].iov_len = total_len - 1;
req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req));
/* [MS-SMB2] 2.2.13 NameOffset: /* [MS-SMB2] 2.2.13 NameOffset:
@ -2146,10 +2148,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
rc = alloc_path_with_tree_prefix(&copy_path, &copy_size, rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
&name_len, &name_len,
tcon->treeName, path); tcon->treeName, path);
if (rc) { if (rc)
cifs_small_buf_release(req);
return rc; return rc;
}
req->NameLength = cpu_to_le16(name_len * 2); req->NameLength = cpu_to_le16(name_len * 2);
uni_path_len = copy_size; uni_path_len = copy_size;
path = copy_path; path = copy_path;
@ -2157,18 +2157,16 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
/* MUST set path len (NameLength) to 0 opening root of share */ /* MUST set path len (NameLength) to 0 opening root of share */
req->NameLength = cpu_to_le16(uni_path_len - 2); req->NameLength = cpu_to_le16(uni_path_len - 2);
if (uni_path_len % 8 != 0) { copy_size = uni_path_len;
copy_size = roundup(uni_path_len, 8); if (copy_size % 8 != 0)
copy_path = kzalloc(copy_size, GFP_KERNEL); copy_size = roundup(copy_size, 8);
if (!copy_path) { copy_path = kzalloc(copy_size, GFP_KERNEL);
cifs_small_buf_release(req); if (!copy_path)
return -ENOMEM; return -ENOMEM;
} memcpy((char *)copy_path, (const char *)path,
memcpy((char *)copy_path, (const char *)path, uni_path_len);
uni_path_len); uni_path_len = copy_size;
uni_path_len = copy_size; path = copy_path;
path = copy_path;
}
} }
iov[1].iov_len = uni_path_len; iov[1].iov_len = uni_path_len;
@ -2183,12 +2181,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
else { else {
rc = add_lease_context(server, iov, &n_iov, rc = add_lease_context(server, iov, &n_iov,
oparms->fid->lease_key, oplock); oparms->fid->lease_key, oplock);
if (rc) { if (rc)
cifs_small_buf_release(req);
kfree(copy_path);
return rc; return rc;
}
lc_buf = iov[n_iov-1].iov_base;
} }
if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) {
@ -2202,16 +2196,10 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
rc = add_durable_context(iov, &n_iov, oparms, rc = add_durable_context(iov, &n_iov, oparms,
tcon->use_persistent); tcon->use_persistent);
if (rc) { if (rc)
cifs_small_buf_release(req);
kfree(copy_path);
kfree(lc_buf);
return rc; return rc;
}
dhc_buf = iov[n_iov-1].iov_base;
} }
#ifdef CONFIG_CIFS_SMB311
if (tcon->posix_extensions) { if (tcon->posix_extensions) {
if (n_iov > 2) { if (n_iov > 2) {
struct create_context *ccontext = struct create_context *ccontext =
@ -2221,24 +2209,79 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
} }
rc = add_posix_context(iov, &n_iov, oparms->mode); rc = add_posix_context(iov, &n_iov, oparms->mode);
if (rc) { if (rc)
cifs_small_buf_release(req);
kfree(copy_path);
kfree(lc_buf);
kfree(dhc_buf);
return rc; return rc;
}
pc_buf = iov[n_iov-1].iov_base;
} }
#endif /* SMB311 */
if (tcon->snapshot_time) {
cifs_dbg(FYI, "adding snapshot context\n");
if (n_iov > 2) {
struct create_context *ccontext =
(struct create_context *)iov[n_iov-1].iov_base;
ccontext->Next =
cpu_to_le32(iov[n_iov-1].iov_len);
}
rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time);
if (rc)
return rc;
}
rqst->rq_nvec = n_iov;
return 0;
}
/* rq_iov[0] is the request and is released by cifs_small_buf_release().
* All other vectors are freed by kfree().
*/
void
SMB2_open_free(struct smb_rqst *rqst)
{
int i;
cifs_small_buf_release(rqst->rq_iov[0].iov_base);
for (i = 1; i < rqst->rq_nvec; i++)
if (rqst->rq_iov[i].iov_base != smb2_padding)
kfree(rqst->rq_iov[i].iov_base);
}
int
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
__u8 *oplock, struct smb2_file_all_info *buf,
struct kvec *err_iov, int *buftype)
{
struct smb_rqst rqst;
struct smb2_create_rsp *rsp = NULL;
struct TCP_Server_Info *server;
struct cifs_tcon *tcon = oparms->tcon;
struct cifs_ses *ses = tcon->ses;
struct kvec iov[5]; /* make sure at least one for each open context */
struct kvec rsp_iov = {NULL, 0};
int resp_buftype;
int rc = 0;
int flags = 0;
cifs_dbg(FYI, "create/open\n");
if (ses && (ses->server))
server = ses->server;
else
return -EIO;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&rqst, 0, sizeof(struct smb_rqst));
memset(&iov, 0, sizeof(iov));
rqst.rq_iov = iov; rqst.rq_iov = iov;
rqst.rq_nvec = n_iov; rqst.rq_nvec = 5;
rc = SMB2_open_init(tcon, &rqst, oplock, oparms, path);
if (rc)
goto creat_exit;
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
&rsp_iov); &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
if (rc != 0) { if (rc != 0) {
@ -2275,10 +2318,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
else else
*oplock = rsp->OplockLevel; *oplock = rsp->OplockLevel;
creat_exit: creat_exit:
kfree(copy_path); SMB2_open_free(&rqst);
kfree(lc_buf);
kfree(dhc_buf);
kfree(pc_buf);
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
return rc; return rc;
} }
@ -2468,44 +2508,63 @@ SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
int
SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
u64 persistent_fid, u64 volatile_fid)
{
struct smb2_close_req *req;
struct kvec *iov = rqst->rq_iov;
unsigned int total_len;
int rc;
rc = smb2_plain_req_init(SMB2_CLOSE, tcon, (void **) &req, &total_len);
if (rc)
return rc;
req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid;
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
return 0;
}
void
SMB2_close_free(struct smb_rqst *rqst)
{
cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
}
int int
SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, int flags) u64 persistent_fid, u64 volatile_fid, int flags)
{ {
struct smb_rqst rqst; struct smb_rqst rqst;
struct smb2_close_req *req; struct smb2_close_rsp *rsp = NULL;
struct smb2_close_rsp *rsp;
struct cifs_ses *ses = tcon->ses; struct cifs_ses *ses = tcon->ses;
struct kvec iov[1]; struct kvec iov[1];
struct kvec rsp_iov; struct kvec rsp_iov;
int resp_buftype; int resp_buftype;
int rc = 0; int rc = 0;
unsigned int total_len;
cifs_dbg(FYI, "Close\n"); cifs_dbg(FYI, "Close\n");
if (!ses || !(ses->server)) if (!ses || !(ses->server))
return -EIO; return -EIO;
rc = smb2_plain_req_init(SMB2_CLOSE, tcon, (void **) &req, &total_len);
if (rc)
return rc;
if (smb3_encryption_required(tcon)) if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ; flags |= CIFS_TRANSFORM_REQ;
req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid;
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&rqst, 0, sizeof(struct smb_rqst));
memset(&iov, 0, sizeof(iov));
rqst.rq_iov = iov; rqst.rq_iov = iov;
rqst.rq_nvec = 1; rqst.rq_nvec = 1;
rc = SMB2_close_init(tcon, &rqst, persistent_fid, volatile_fid);
if (rc)
goto close_exit;
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_close_rsp *)rsp_iov.iov_base; rsp = (struct smb2_close_rsp *)rsp_iov.iov_base;
if (rc != 0) { if (rc != 0) {
@ -2518,6 +2577,7 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon,
/* BB FIXME - decode close response, update inode for caching */ /* BB FIXME - decode close response, update inode for caching */
close_exit: close_exit:
SMB2_close_free(&rqst);
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
return rc; return rc;
} }
@ -2529,9 +2589,9 @@ SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
return SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0); return SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0);
} }
static int int
validate_iov(unsigned int offset, unsigned int buffer_length, smb2_validate_iov(unsigned int offset, unsigned int buffer_length,
struct kvec *iov, unsigned int min_buf_size) struct kvec *iov, unsigned int min_buf_size)
{ {
unsigned int smb_len = iov->iov_len; unsigned int smb_len = iov->iov_len;
char *end_of_smb = smb_len + (char *)iov->iov_base; char *end_of_smb = smb_len + (char *)iov->iov_base;
@ -2575,7 +2635,7 @@ validate_and_copy_iov(unsigned int offset, unsigned int buffer_length,
if (!data) if (!data)
return -EINVAL; return -EINVAL;
rc = validate_iov(offset, buffer_length, iov, minbufsize); rc = smb2_validate_iov(offset, buffer_length, iov, minbufsize);
if (rc) if (rc)
return rc; return rc;
@ -2584,36 +2644,22 @@ validate_and_copy_iov(unsigned int offset, unsigned int buffer_length,
return 0; return 0;
} }
static int int
query_info(const unsigned int xid, struct cifs_tcon *tcon, SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type, u64 persistent_fid, u64 volatile_fid,
u32 additional_info, size_t output_len, size_t min_len, void **data, u8 info_class, u8 info_type, u32 additional_info,
u32 *dlen) size_t output_len)
{ {
struct smb_rqst rqst;
struct smb2_query_info_req *req; struct smb2_query_info_req *req;
struct smb2_query_info_rsp *rsp = NULL; struct kvec *iov = rqst->rq_iov;
struct kvec iov[2];
struct kvec rsp_iov;
int rc = 0;
int resp_buftype;
struct cifs_ses *ses = tcon->ses;
int flags = 0;
unsigned int total_len; unsigned int total_len;
int rc;
cifs_dbg(FYI, "Query Info\n");
if (!ses || !(ses->server))
return -EIO;
rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, (void **) &req, rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, (void **) &req,
&total_len); &total_len);
if (rc) if (rc)
return rc; return rc;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
req->InfoType = info_type; req->InfoType = info_type;
req->FileInfoClass = info_class; req->FileInfoClass = info_class;
req->PersistentFileId = persistent_fid; req->PersistentFileId = persistent_fid;
@ -2630,13 +2676,50 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
iov[0].iov_base = (char *)req; iov[0].iov_base = (char *)req;
/* 1 for Buffer */ /* 1 for Buffer */
iov[0].iov_len = total_len - 1; iov[0].iov_len = total_len - 1;
return 0;
}
void
SMB2_query_info_free(struct smb_rqst *rqst)
{
cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
}
static int
query_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type,
u32 additional_info, size_t output_len, size_t min_len, void **data,
u32 *dlen)
{
struct smb_rqst rqst;
struct smb2_query_info_rsp *rsp = NULL;
struct kvec iov[1];
struct kvec rsp_iov;
int rc = 0;
int resp_buftype;
struct cifs_ses *ses = tcon->ses;
int flags = 0;
cifs_dbg(FYI, "Query Info\n");
if (!ses || !(ses->server))
return -EIO;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&rqst, 0, sizeof(struct smb_rqst));
memset(&iov, 0, sizeof(iov));
rqst.rq_iov = iov; rqst.rq_iov = iov;
rqst.rq_nvec = 1; rqst.rq_nvec = 1;
rc = SMB2_query_info_init(tcon, &rqst, persistent_fid, volatile_fid,
info_class, info_type, additional_info,
output_len);
if (rc)
goto qinf_exit;
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
if (rc) { if (rc) {
@ -2665,6 +2748,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
&rsp_iov, min_len, *data); &rsp_iov, min_len, *data);
qinf_exit: qinf_exit:
SMB2_query_info_free(&rqst);
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
return rc; return rc;
} }
@ -3623,9 +3707,9 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
goto qdir_exit; goto qdir_exit;
} }
rc = validate_iov(le16_to_cpu(rsp->OutputBufferOffset), rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
info_buf_size); info_buf_size);
if (rc) if (rc)
goto qdir_exit; goto qdir_exit;
@ -3927,9 +4011,9 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
static void void
copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
struct kstatfs *kst) struct kstatfs *kst)
{ {
kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) * kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) *
le32_to_cpu(pfs_inf->SectorsPerAllocationUnit); le32_to_cpu(pfs_inf->SectorsPerAllocationUnit);
@ -3939,6 +4023,25 @@ copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
return; return;
} }
static void
copy_posix_fs_info_to_kstatfs(FILE_SYSTEM_POSIX_INFO *response_data,
struct kstatfs *kst)
{
kst->f_bsize = le32_to_cpu(response_data->BlockSize);
kst->f_blocks = le64_to_cpu(response_data->TotalBlocks);
kst->f_bfree = le64_to_cpu(response_data->BlocksAvail);
if (response_data->UserBlocksAvail == cpu_to_le64(-1))
kst->f_bavail = kst->f_bfree;
else
kst->f_bavail = le64_to_cpu(response_data->UserBlocksAvail);
if (response_data->TotalFileNodes != cpu_to_le64(-1))
kst->f_files = le64_to_cpu(response_data->TotalFileNodes);
if (response_data->FreeFileNodes != cpu_to_le64(-1))
kst->f_ffree = le64_to_cpu(response_data->FreeFileNodes);
return;
}
static int static int
build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level, build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level,
int outbuf_len, u64 persistent_fid, u64 volatile_fid) int outbuf_len, u64 persistent_fid, u64 volatile_fid)
@ -3975,6 +4078,54 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level,
return 0; return 0;
} }
int
SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
{
struct smb_rqst rqst;
struct smb2_query_info_rsp *rsp = NULL;
struct kvec iov;
struct kvec rsp_iov;
int rc = 0;
int resp_buftype;
struct cifs_ses *ses = tcon->ses;
FILE_SYSTEM_POSIX_INFO *info = NULL;
int flags = 0;
rc = build_qfs_info_req(&iov, tcon, FS_POSIX_INFORMATION,
sizeof(FILE_SYSTEM_POSIX_INFO),
persistent_fid, volatile_fid);
if (rc)
return rc;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
memset(&rqst, 0, sizeof(struct smb_rqst));
rqst.rq_iov = &iov;
rqst.rq_nvec = 1;
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(iov.iov_base);
if (rc) {
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
goto posix_qfsinf_exit;
}
rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
info = (FILE_SYSTEM_POSIX_INFO *)(
le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp);
rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
sizeof(FILE_SYSTEM_POSIX_INFO));
if (!rc)
copy_posix_fs_info_to_kstatfs(info, fsdata);
posix_qfsinf_exit:
free_rsp_buf(resp_buftype, rsp_iov.iov_base);
return rc;
}
int int
SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
@ -4012,11 +4163,11 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
info = (struct smb2_fs_full_size_info *)( info = (struct smb2_fs_full_size_info *)(
le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp);
rc = validate_iov(le16_to_cpu(rsp->OutputBufferOffset), rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
sizeof(struct smb2_fs_full_size_info)); sizeof(struct smb2_fs_full_size_info));
if (!rc) if (!rc)
copy_fs_info_to_kstatfs(info, fsdata); smb2_copy_fs_info_to_kstatfs(info, fsdata);
qfsinf_exit: qfsinf_exit:
free_rsp_buf(resp_buftype, rsp_iov.iov_base); free_rsp_buf(resp_buftype, rsp_iov.iov_base);
@ -4046,6 +4197,9 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
} else if (level == FS_SECTOR_SIZE_INFORMATION) { } else if (level == FS_SECTOR_SIZE_INFORMATION) {
max_len = sizeof(struct smb3_fs_ss_info); max_len = sizeof(struct smb3_fs_ss_info);
min_len = sizeof(struct smb3_fs_ss_info); min_len = sizeof(struct smb3_fs_ss_info);
} else if (level == FS_VOLUME_INFORMATION) {
max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN;
min_len = sizeof(struct smb3_fs_vol_info);
} else { } else {
cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level);
return -EINVAL; return -EINVAL;
@ -4073,7 +4227,7 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
rsp_len = le32_to_cpu(rsp->OutputBufferLength); rsp_len = le32_to_cpu(rsp->OutputBufferLength);
offset = le16_to_cpu(rsp->OutputBufferOffset); offset = le16_to_cpu(rsp->OutputBufferOffset);
rc = validate_iov(offset, rsp_len, &rsp_iov, min_len); rc = smb2_validate_iov(offset, rsp_len, &rsp_iov, min_len);
if (rc) if (rc)
goto qfsattr_exit; goto qfsattr_exit;
@ -4090,6 +4244,11 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
tcon->ss_flags = le32_to_cpu(ss_info->Flags); tcon->ss_flags = le32_to_cpu(ss_info->Flags);
tcon->perf_sector_size = tcon->perf_sector_size =
le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf);
} else if (level == FS_VOLUME_INFORMATION) {
struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *)
(offset + (char *)rsp);
tcon->vol_serial_number = vol_info->VolumeSerialNumber;
tcon->vol_create_time = vol_info->VolumeCreationTime;
} }
qfsattr_exit: qfsattr_exit:

View File

@ -153,6 +153,8 @@ struct smb2_transform_hdr {
* *
*/ */
#define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL
#define SMB2_ERROR_STRUCTURE_SIZE2 cpu_to_le16(9) #define SMB2_ERROR_STRUCTURE_SIZE2 cpu_to_le16(9)
struct smb2_err_rsp { struct smb2_err_rsp {
@ -765,6 +767,14 @@ struct create_durable_handle_reconnect_v2 {
struct durable_reconnect_context_v2 dcontext; struct durable_reconnect_context_v2 dcontext;
} __packed; } __packed;
/* See MS-SMB2 2.2.13.2.5 */
struct crt_twarp_ctxt {
struct create_context ccontext;
__u8 Name[8];
__le64 Timestamp;
} __packed;
#define COPY_CHUNK_RES_KEY_SIZE 24 #define COPY_CHUNK_RES_KEY_SIZE 24
struct resume_key_req { struct resume_key_req {
char ResumeKey[COPY_CHUNK_RES_KEY_SIZE]; char ResumeKey[COPY_CHUNK_RES_KEY_SIZE];
@ -1223,6 +1233,7 @@ struct smb2_lease_ack {
#define FS_DRIVER_PATH_INFORMATION 9 /* Local only */ #define FS_DRIVER_PATH_INFORMATION 9 /* Local only */
#define FS_VOLUME_FLAGS_INFORMATION 10 /* Local only */ #define FS_VOLUME_FLAGS_INFORMATION 10 /* Local only */
#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */ #define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */
#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */
struct smb2_fs_full_size_info { struct smb2_fs_full_size_info {
__le64 TotalAllocationUnits; __le64 TotalAllocationUnits;
@ -1248,6 +1259,17 @@ struct smb3_fs_ss_info {
__le32 ByteOffsetForPartitionAlignment; __le32 ByteOffsetForPartitionAlignment;
} __packed; } __packed;
/* volume info struct - see MS-FSCC 2.5.9 */
#define MAX_VOL_LABEL_LEN 32
struct smb3_fs_vol_info {
__le64 VolumeCreationTime;
__u32 VolumeSerialNumber;
__le32 VolumeLabelLength; /* includes trailing null */
__u8 SupportsObjects; /* True if eg like NTFS, supports objects */
__u8 Reserved;
__u8 VolumeLabel[0]; /* variable len */
} __packed;
/* partial list of QUERY INFO levels */ /* partial list of QUERY INFO levels */
#define FILE_DIRECTORY_INFORMATION 1 #define FILE_DIRECTORY_INFORMATION 1
#define FILE_FULL_DIRECTORY_INFORMATION 2 #define FILE_FULL_DIRECTORY_INFORMATION 2
@ -1361,4 +1383,6 @@ struct smb2_file_eof_info { /* encoding of request for level 10 */
__le64 EndOfFile; /* new end of file value */ __le64 EndOfFile; /* new end of file value */
} __packed; /* level 20 Set */ } __packed; /* level 20 Set */
extern char smb2_padding[7];
#endif /* _SMB2PDU_H */ #endif /* _SMB2PDU_H */

View File

@ -68,6 +68,7 @@ extern int smb3_handle_read_data(struct TCP_Server_Info *server,
extern int open_shroot(unsigned int xid, struct cifs_tcon *tcon, extern int open_shroot(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid *pfid); struct cifs_fid *pfid);
extern void close_shroot(struct cached_fid *cfid);
extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst, extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
struct smb2_file_all_info *src); struct smb2_file_all_info *src);
extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
@ -132,6 +133,10 @@ extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
__le16 *path, __u8 *oplock, __le16 *path, __u8 *oplock,
struct smb2_file_all_info *buf, struct smb2_file_all_info *buf,
struct kvec *err_iov, int *resp_buftype); struct kvec *err_iov, int *resp_buftype);
extern int SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
__u8 *oplock, struct cifs_open_parms *oparms,
__le16 *path);
extern void SMB2_open_free(struct smb_rqst *rqst);
extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 opcode, u64 persistent_fid, u64 volatile_fid, u32 opcode,
bool is_fsctl, char *in_data, u32 indatalen, bool is_fsctl, char *in_data, u32 indatalen,
@ -140,6 +145,9 @@ extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id); u64 persistent_file_id, u64 volatile_file_id);
extern int SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, int flags); u64 persistent_fid, u64 volatile_fid, int flags);
extern int SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
u64 persistent_file_id, u64 volatile_file_id);
extern void SMB2_close_free(struct smb_rqst *rqst);
extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id); u64 persistent_file_id, u64 volatile_file_id);
extern int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
@ -149,6 +157,11 @@ extern int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id, u64 persistent_file_id, u64 volatile_file_id,
struct smb2_file_all_info *data); struct smb2_file_all_info *data);
extern int SMB2_query_info_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
u64 persistent_fid, u64 volatile_fid,
u8 info_class, u8 info_type,
u32 additional_info, size_t output_len);
extern void SMB2_query_info_free(struct smb_rqst *rqst);
extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id, u64 persistent_file_id, u64 volatile_file_id,
void **data, unsigned int *plen); void **data, unsigned int *plen);
@ -197,6 +210,9 @@ void smb2_cancelled_close_fid(struct work_struct *work);
extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id, u64 persistent_file_id, u64 volatile_file_id,
struct kstatfs *FSData); struct kstatfs *FSData);
extern int SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id,
struct kstatfs *FSData);
extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id, int lvl); u64 persistent_file_id, u64 volatile_file_id, int lvl);
extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
@ -213,9 +229,13 @@ extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);
extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *, extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *,
enum securityEnum); enum securityEnum);
#ifdef CONFIG_CIFS_SMB311 extern int smb3_encryption_required(const struct cifs_tcon *tcon);
extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length,
struct kvec *iov, unsigned int min_buf_size);
extern void smb2_copy_fs_info_to_kstatfs(
struct smb2_fs_full_size_info *pfs_inf,
struct kstatfs *kst);
extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server); extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server);
extern int smb311_update_preauth_hash(struct cifs_ses *ses, extern int smb311_update_preauth_hash(struct cifs_ses *ses,
struct kvec *iov, int nvec); struct kvec *iov, int nvec);
#endif
#endif /* _SMB2PROTO_H */ #endif /* _SMB2PROTO_H */

View File

@ -70,7 +70,6 @@ err:
return rc; return rc;
} }
#ifdef CONFIG_CIFS_SMB311
int int
smb311_crypto_shash_allocate(struct TCP_Server_Info *server) smb311_crypto_shash_allocate(struct TCP_Server_Info *server)
{ {
@ -98,7 +97,6 @@ err:
cifs_free_hash(&p->hmacsha256, &p->sdeschmacsha256); cifs_free_hash(&p->hmacsha256, &p->sdeschmacsha256);
return rc; return rc;
} }
#endif
static struct cifs_ses * static struct cifs_ses *
smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id) smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
@ -173,7 +171,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
struct kvec *iov = rqst->rq_iov; struct kvec *iov = rqst->rq_iov;
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base;
struct cifs_ses *ses; struct cifs_ses *ses;
struct shash_desc *shash = &server->secmech.sdeschmacsha256->shash; struct shash_desc *shash;
struct smb_rqst drqst; struct smb_rqst drqst;
ses = smb2_find_smb_ses(server, shdr->SessionId); ses = smb2_find_smb_ses(server, shdr->SessionId);
@ -187,7 +185,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
rc = smb2_crypto_shash_allocate(server); rc = smb2_crypto_shash_allocate(server);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: shah256 alloc failed\n", __func__); cifs_dbg(VFS, "%s: sha256 alloc failed\n", __func__);
return rc; return rc;
} }
@ -198,6 +196,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return rc; return rc;
} }
shash = &server->secmech.sdeschmacsha256->shash;
rc = crypto_shash_init(shash); rc = crypto_shash_init(shash);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not init sha256", __func__); cifs_dbg(VFS, "%s: Could not init sha256", __func__);
@ -395,7 +394,6 @@ generate_smb30signingkey(struct cifs_ses *ses)
return generate_smb3signingkey(ses, &triplet); return generate_smb3signingkey(ses, &triplet);
} }
#ifdef CONFIG_CIFS_SMB311
int int
generate_smb311signingkey(struct cifs_ses *ses) generate_smb311signingkey(struct cifs_ses *ses)
@ -423,7 +421,6 @@ generate_smb311signingkey(struct cifs_ses *ses)
return generate_smb3signingkey(ses, &triplet); return generate_smb3signingkey(ses, &triplet);
} }
#endif /* 311 */
int int
smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)

View File

@ -281,6 +281,44 @@ DEFINE_EVENT(smb3_cmd_done_class, smb3_##name, \
TP_ARGS(tid, sesid, cmd, mid)) TP_ARGS(tid, sesid, cmd, mid))
DEFINE_SMB3_CMD_DONE_EVENT(cmd_done); DEFINE_SMB3_CMD_DONE_EVENT(cmd_done);
DEFINE_SMB3_CMD_DONE_EVENT(ses_expired);
DECLARE_EVENT_CLASS(smb3_mid_class,
TP_PROTO(__u16 cmd,
__u64 mid,
__u32 pid,
unsigned long when_sent,
unsigned long when_received),
TP_ARGS(cmd, mid, pid, when_sent, when_received),
TP_STRUCT__entry(
__field(__u16, cmd)
__field(__u64, mid)
__field(__u32, pid)
__field(unsigned long, when_sent)
__field(unsigned long, when_received)
),
TP_fast_assign(
__entry->cmd = cmd;
__entry->mid = mid;
__entry->pid = pid;
__entry->when_sent = when_sent;
__entry->when_received = when_received;
),
TP_printk("\tcmd=%u mid=%llu pid=%u, when_sent=%lu when_rcv=%lu",
__entry->cmd, __entry->mid, __entry->pid, __entry->when_sent,
__entry->when_received)
)
#define DEFINE_SMB3_MID_EVENT(name) \
DEFINE_EVENT(smb3_mid_class, smb3_##name, \
TP_PROTO(__u16 cmd, \
__u64 mid, \
__u32 pid, \
unsigned long when_sent, \
unsigned long when_received), \
TP_ARGS(cmd, mid, pid, when_sent, when_received))
DEFINE_SMB3_MID_EVENT(slow_rsp);
DECLARE_EVENT_CLASS(smb3_exit_err_class, DECLARE_EVENT_CLASS(smb3_exit_err_class,
TP_PROTO(unsigned int xid, TP_PROTO(unsigned int xid,
@ -422,6 +460,32 @@ DEFINE_EVENT(smb3_open_done_class, smb3_##name, \
DEFINE_SMB3_OPEN_DONE_EVENT(open_done); DEFINE_SMB3_OPEN_DONE_EVENT(open_done);
DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done); DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done);
DECLARE_EVENT_CLASS(smb3_reconnect_class,
TP_PROTO(__u64 currmid,
char *hostname),
TP_ARGS(currmid, hostname),
TP_STRUCT__entry(
__field(__u64, currmid)
__field(char *, hostname)
),
TP_fast_assign(
__entry->currmid = currmid;
__entry->hostname = hostname;
),
TP_printk("server=%s current_mid=0x%llx",
__entry->hostname,
__entry->currmid)
)
#define DEFINE_SMB3_RECONNECT_EVENT(name) \
DEFINE_EVENT(smb3_reconnect_class, smb3_##name, \
TP_PROTO(__u64 currmid, \
char *hostname), \
TP_ARGS(currmid, hostname))
DEFINE_SMB3_RECONNECT_EVENT(reconnect);
DEFINE_SMB3_RECONNECT_EVENT(partial_send_reconnect);
#endif /* _CIFS_TRACE_H */ #endif /* _CIFS_TRACE_H */
#undef TRACE_INCLUDE_PATH #undef TRACE_INCLUDE_PATH

View File

@ -115,8 +115,17 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
now = jiffies; now = jiffies;
/* commands taking longer than one second are indications that /* commands taking longer than one second are indications that
something is wrong, unless it is quite a slow link or server */ something is wrong, unless it is quite a slow link or server */
if (time_after(now, midEntry->when_alloc + HZ)) { if (time_after(now, midEntry->when_alloc + HZ) &&
if ((cifsFYI & CIFS_TIMER) && (midEntry->command != command)) { (midEntry->command != command)) {
/* smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command */
if ((le16_to_cpu(midEntry->command) < NUMBER_OF_SMB2_COMMANDS) &&
(le16_to_cpu(midEntry->command) >= 0))
cifs_stats_inc(&midEntry->server->smb2slowcmd[le16_to_cpu(midEntry->command)]);
trace_smb3_slow_rsp(le16_to_cpu(midEntry->command),
midEntry->mid, midEntry->pid,
midEntry->when_sent, midEntry->when_received);
if (cifsFYI & CIFS_TIMER) {
pr_debug(" CIFS slow rsp: cmd %d mid %llu", pr_debug(" CIFS slow rsp: cmd %d mid %llu",
midEntry->command, midEntry->mid); midEntry->command, midEntry->mid);
pr_info(" A: 0x%lx S: 0x%lx R: 0x%lx\n", pr_info(" A: 0x%lx S: 0x%lx R: 0x%lx\n",
@ -361,6 +370,8 @@ uncork:
* socket so the server throws away the partial SMB * socket so the server throws away the partial SMB
*/ */
server->tcpStatus = CifsNeedReconnect; server->tcpStatus = CifsNeedReconnect;
trace_smb3_partial_send_reconnect(server->CurrentMid,
server->hostname);
} }
smbd_done: smbd_done:
if (rc < 0 && rc != -EINTR) if (rc < 0 && rc != -EINTR)
@ -373,26 +384,42 @@ smbd_done:
} }
static int static int
smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst, int flags) smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
struct smb_rqst *rqst, int flags)
{ {
struct smb_rqst cur_rqst; struct kvec iov;
struct smb2_transform_hdr tr_hdr;
struct smb_rqst cur_rqst[MAX_COMPOUND];
int rc; int rc;
if (!(flags & CIFS_TRANSFORM_REQ)) if (!(flags & CIFS_TRANSFORM_REQ))
return __smb_send_rqst(server, 1, rqst); return __smb_send_rqst(server, num_rqst, rqst);
if (!server->ops->init_transform_rq || if (num_rqst > MAX_COMPOUND - 1)
!server->ops->free_transform_rq) { return -ENOMEM;
cifs_dbg(VFS, "Encryption requested but transform callbacks are missed\n");
memset(&cur_rqst[0], 0, sizeof(cur_rqst));
memset(&iov, 0, sizeof(iov));
memset(&tr_hdr, 0, sizeof(tr_hdr));
iov.iov_base = &tr_hdr;
iov.iov_len = sizeof(tr_hdr);
cur_rqst[0].rq_iov = &iov;
cur_rqst[0].rq_nvec = 1;
if (!server->ops->init_transform_rq) {
cifs_dbg(VFS, "Encryption requested but transform callback "
"is missing\n");
return -EIO; return -EIO;
} }
rc = server->ops->init_transform_rq(server, &cur_rqst, rqst); rc = server->ops->init_transform_rq(server, num_rqst + 1,
&cur_rqst[0], rqst);
if (rc) if (rc)
return rc; return rc;
rc = __smb_send_rqst(server, 1, &cur_rqst); rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]);
server->ops->free_transform_rq(&cur_rqst); smb3_free_compound_rqst(num_rqst, &cur_rqst[1]);
return rc; return rc;
} }
@ -606,7 +633,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
*/ */
cifs_save_when_sent(mid); cifs_save_when_sent(mid);
cifs_in_send_inc(server); cifs_in_send_inc(server);
rc = smb_send_rqst(server, rqst, flags); rc = smb_send_rqst(server, 1, rqst, flags);
cifs_in_send_dec(server); cifs_in_send_dec(server);
if (rc < 0) { if (rc < 0) {
@ -746,20 +773,21 @@ cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
} }
int int
cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
struct smb_rqst *rqst, int *resp_buf_type, const int flags, const int flags, const int num_rqst, struct smb_rqst *rqst,
struct kvec *resp_iov) int *resp_buf_type, struct kvec *resp_iov)
{ {
int rc = 0; int i, j, rc = 0;
int timeout, optype; int timeout, optype;
struct mid_q_entry *midQ; struct mid_q_entry *midQ[MAX_COMPOUND];
unsigned int credits = 1; unsigned int credits = 1;
char *buf; char *buf;
timeout = flags & CIFS_TIMEOUT_MASK; timeout = flags & CIFS_TIMEOUT_MASK;
optype = flags & CIFS_OP_MASK; optype = flags & CIFS_OP_MASK;
*resp_buf_type = CIFS_NO_BUFFER; /* no response buf yet */ for (i = 0; i < num_rqst; i++)
resp_buf_type[i] = CIFS_NO_BUFFER; /* no response buf yet */
if ((ses == NULL) || (ses->server == NULL)) { if ((ses == NULL) || (ses->server == NULL)) {
cifs_dbg(VFS, "Null session\n"); cifs_dbg(VFS, "Null session\n");
@ -786,97 +814,116 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
mutex_lock(&ses->server->srv_mutex); mutex_lock(&ses->server->srv_mutex);
midQ = ses->server->ops->setup_request(ses, rqst); for (i = 0; i < num_rqst; i++) {
if (IS_ERR(midQ)) { midQ[i] = ses->server->ops->setup_request(ses, &rqst[i]);
mutex_unlock(&ses->server->srv_mutex); if (IS_ERR(midQ[i])) {
/* Update # of requests on wire to server */ for (j = 0; j < i; j++)
add_credits(ses->server, 1, optype); cifs_delete_mid(midQ[j]);
return PTR_ERR(midQ); mutex_unlock(&ses->server->srv_mutex);
/* Update # of requests on wire to server */
add_credits(ses->server, 1, optype);
return PTR_ERR(midQ[i]);
}
midQ[i]->mid_state = MID_REQUEST_SUBMITTED;
} }
midQ->mid_state = MID_REQUEST_SUBMITTED;
cifs_in_send_inc(ses->server); cifs_in_send_inc(ses->server);
rc = smb_send_rqst(ses->server, rqst, flags); rc = smb_send_rqst(ses->server, num_rqst, rqst, flags);
cifs_in_send_dec(ses->server); cifs_in_send_dec(ses->server);
cifs_save_when_sent(midQ);
for (i = 0; i < num_rqst; i++)
cifs_save_when_sent(midQ[i]);
if (rc < 0) if (rc < 0)
ses->server->sequence_number -= 2; ses->server->sequence_number -= 2;
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&ses->server->srv_mutex);
if (rc < 0) for (i = 0; i < num_rqst; i++) {
goto out; if (rc < 0)
goto out;
#ifdef CONFIG_CIFS_SMB311 if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP))
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) smb311_update_preauth_hash(ses, rqst[i].rq_iov,
smb311_update_preauth_hash(ses, rqst->rq_iov, rqst[i].rq_nvec);
rqst->rq_nvec);
#endif
if (timeout == CIFS_ASYNC_OP) if (timeout == CIFS_ASYNC_OP)
goto out; goto out;
rc = wait_for_response(ses->server, midQ); rc = wait_for_response(ses->server, midQ[i]);
if (rc != 0) { if (rc != 0) {
cifs_dbg(FYI, "Cancelling wait for mid %llu\n", midQ->mid); cifs_dbg(FYI, "Cancelling wait for mid %llu\n",
send_cancel(ses->server, rqst, midQ); midQ[i]->mid);
spin_lock(&GlobalMid_Lock); send_cancel(ses->server, &rqst[i], midQ[i]);
if (midQ->mid_state == MID_REQUEST_SUBMITTED) { spin_lock(&GlobalMid_Lock);
midQ->mid_flags |= MID_WAIT_CANCELLED; if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) {
midQ->callback = DeleteMidQEntry; midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
midQ[i]->callback = DeleteMidQEntry;
spin_unlock(&GlobalMid_Lock);
add_credits(ses->server, 1, optype);
return rc;
}
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
}
rc = cifs_sync_mid_result(midQ[i], ses->server);
if (rc != 0) {
add_credits(ses->server, 1, optype); add_credits(ses->server, 1, optype);
return rc; return rc;
} }
spin_unlock(&GlobalMid_Lock);
if (!midQ[i]->resp_buf ||
midQ[i]->mid_state != MID_RESPONSE_RECEIVED) {
rc = -EIO;
cifs_dbg(FYI, "Bad MID state?\n");
goto out;
}
buf = (char *)midQ[i]->resp_buf;
resp_iov[i].iov_base = buf;
resp_iov[i].iov_len = midQ[i]->resp_buf_size +
ses->server->vals->header_preamble_size;
if (midQ[i]->large_buf)
resp_buf_type[i] = CIFS_LARGE_BUFFER;
else
resp_buf_type[i] = CIFS_SMALL_BUFFER;
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) {
struct kvec iov = {
.iov_base = resp_iov[i].iov_base,
.iov_len = resp_iov[i].iov_len
};
smb311_update_preauth_hash(ses, &iov, 1);
}
credits = ses->server->ops->get_credits(midQ[i]);
rc = ses->server->ops->check_receive(midQ[i], ses->server,
flags & CIFS_LOG_ERROR);
/* mark it so buf will not be freed by cifs_delete_mid */
if ((flags & CIFS_NO_RESP) == 0)
midQ[i]->resp_buf = NULL;
} }
rc = cifs_sync_mid_result(midQ, ses->server);
if (rc != 0) {
add_credits(ses->server, 1, optype);
return rc;
}
if (!midQ->resp_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) {
rc = -EIO;
cifs_dbg(FYI, "Bad MID state?\n");
goto out;
}
buf = (char *)midQ->resp_buf;
resp_iov->iov_base = buf;
resp_iov->iov_len = midQ->resp_buf_size +
ses->server->vals->header_preamble_size;
if (midQ->large_buf)
*resp_buf_type = CIFS_LARGE_BUFFER;
else
*resp_buf_type = CIFS_SMALL_BUFFER;
#ifdef CONFIG_CIFS_SMB311
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) {
struct kvec iov = {
.iov_base = resp_iov->iov_base,
.iov_len = resp_iov->iov_len
};
smb311_update_preauth_hash(ses, &iov, 1);
}
#endif
credits = ses->server->ops->get_credits(midQ);
rc = ses->server->ops->check_receive(midQ, ses->server,
flags & CIFS_LOG_ERROR);
/* mark it so buf will not be freed by cifs_delete_mid */
if ((flags & CIFS_NO_RESP) == 0)
midQ->resp_buf = NULL;
out: out:
cifs_delete_mid(midQ); for (i = 0; i < num_rqst; i++)
cifs_delete_mid(midQ[i]);
add_credits(ses->server, credits, optype); add_credits(ses->server, credits, optype);
return rc; return rc;
} }
int
cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
struct smb_rqst *rqst, int *resp_buf_type, const int flags,
struct kvec *resp_iov)
{
return compound_send_recv(xid, ses, flags, 1, rqst, resp_buf_type,
resp_iov);
}
int int
SendReceive2(const unsigned int xid, struct cifs_ses *ses, SendReceive2(const unsigned int xid, struct cifs_ses *ses,
struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,

View File

@ -35,6 +35,14 @@
#define CIFS_XATTR_CIFS_ACL "system.cifs_acl" #define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
#define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */ #define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */
#define CIFS_XATTR_CREATETIME "cifs.creationtime" /* user.cifs.creationtime */ #define CIFS_XATTR_CREATETIME "cifs.creationtime" /* user.cifs.creationtime */
/*
* Although these three are just aliases for the above, need to move away from
* confusing users and using the 20+ year old term 'cifs' when it is no longer
* secure, replaced by SMB2 (then even more highly secure SMB3) many years ago
*/
#define SMB3_XATTR_CIFS_ACL "system.smb3_acl"
#define SMB3_XATTR_ATTRIB "smb3.dosattrib" /* full name: user.smb3.dosattrib */
#define SMB3_XATTR_CREATETIME "smb3.creationtime" /* user.smb3.creationtime */
/* BB need to add server (Samba e.g) support for security and trusted prefix */ /* BB need to add server (Samba e.g) support for security and trusted prefix */
enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT }; enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT };
@ -220,10 +228,12 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
switch (handler->flags) { switch (handler->flags) {
case XATTR_USER: case XATTR_USER:
cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name); cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name);
if (strcmp(name, CIFS_XATTR_ATTRIB) == 0) { if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) ||
(strcmp(name, SMB3_XATTR_ATTRIB) == 0)) {
rc = cifs_attrib_get(dentry, inode, value, size); rc = cifs_attrib_get(dentry, inode, value, size);
break; break;
} else if (strcmp(name, CIFS_XATTR_CREATETIME) == 0) { } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) ||
(strcmp(name, SMB3_XATTR_CREATETIME) == 0)) {
rc = cifs_creation_time_get(dentry, inode, value, size); rc = cifs_creation_time_get(dentry, inode, value, size);
break; break;
} }
@ -363,6 +373,19 @@ static const struct xattr_handler cifs_cifs_acl_xattr_handler = {
.set = cifs_xattr_set, .set = cifs_xattr_set,
}; };
/*
* Although this is just an alias for the above, need to move away from
* confusing users and using the 20 year old term 'cifs' when it is no
* longer secure and was replaced by SMB2/SMB3 a long time ago, and
* SMB3 and later are highly secure.
*/
static const struct xattr_handler smb3_acl_xattr_handler = {
.name = SMB3_XATTR_CIFS_ACL,
.flags = XATTR_CIFS_ACL,
.get = cifs_xattr_get,
.set = cifs_xattr_set,
};
static const struct xattr_handler cifs_posix_acl_access_xattr_handler = { static const struct xattr_handler cifs_posix_acl_access_xattr_handler = {
.name = XATTR_NAME_POSIX_ACL_ACCESS, .name = XATTR_NAME_POSIX_ACL_ACCESS,
.flags = XATTR_ACL_ACCESS, .flags = XATTR_ACL_ACCESS,
@ -381,6 +404,7 @@ const struct xattr_handler *cifs_xattr_handlers[] = {
&cifs_user_xattr_handler, &cifs_user_xattr_handler,
&cifs_os2_xattr_handler, &cifs_os2_xattr_handler,
&cifs_cifs_acl_xattr_handler, &cifs_cifs_acl_xattr_handler,
&smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */
&cifs_posix_acl_access_xattr_handler, &cifs_posix_acl_access_xattr_handler,
&cifs_posix_acl_default_xattr_handler, &cifs_posix_acl_default_xattr_handler,
NULL NULL