diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-13 22:32:11 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-13 22:32:11 -0700 |
commit | 3bb37da509e576c80180fa0e4d1cfcaddf0cb82e (patch) | |
tree | 0c726cc6245c6a991a42becbc579ff5b46d32814 /fs/cifs | |
parent | 161fa27ff2e2fd4ea7ccb660184670eab2b6ea98 (diff) | |
parent | c4f7173ac3b7e22934e51f0121833642a581d723 (diff) | |
download | linux-3bb37da509e576c80180fa0e4d1cfcaddf0cb82e.tar.bz2 |
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
...
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/Kconfig | 59 | ||||
-rw-r--r-- | fs/cifs/cache.c | 6 | ||||
-rw-r--r-- | fs/cifs/cifs_debug.c | 66 | ||||
-rw-r--r-- | fs/cifs/cifsencrypt.c | 12 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 33 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 54 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 10 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 12 | ||||
-rw-r--r-- | fs/cifs/connect.c | 111 | ||||
-rw-r--r-- | fs/cifs/fscache.c | 12 | ||||
-rw-r--r-- | fs/cifs/fscache.h | 8 | ||||
-rw-r--r-- | fs/cifs/inode.c | 38 | ||||
-rw-r--r-- | fs/cifs/link.c | 4 | ||||
-rw-r--r-- | fs/cifs/misc.c | 2 | ||||
-rw-r--r-- | fs/cifs/netmisc.c | 19 | ||||
-rw-r--r-- | fs/cifs/smb1ops.c | 4 | ||||
-rw-r--r-- | fs/cifs/smb2inode.c | 6 | ||||
-rw-r--r-- | fs/cifs/smb2misc.c | 13 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 540 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 557 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 24 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 24 | ||||
-rw-r--r-- | fs/cifs/smb2transport.c | 9 | ||||
-rw-r--r-- | fs/cifs/trace.h | 64 | ||||
-rw-r--r-- | fs/cifs/transport.c | 211 | ||||
-rw-r--r-- | fs/cifs/xattr.c | 28 |
26 files changed, 1259 insertions, 667 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 5f132d59dfc2..35c83fe7dba0 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -16,24 +16,28 @@ config CIFS select CRYPTO_DES help 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 to the original dialect, the Server Message Block (SMB) protocol, the native file sharing mechanism for most early PC operating systems. - The SMB3 protocol is supported by most modern operating systems and - NAS appliances (e.g. Samba, Windows 8, Windows 2012, MacOS). + The SMB3 protocol is supported by most modern operating systems + 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 later) as well by Samba (which provides excellent CIFS and SMB3 - server support for Linux and many other operating systems). Limited - support for OS/2 and Windows ME and similar very old servers is - provided as well. + server support for Linux and many other operating systems). Use of + dialects older than SMB2.1 is often discouraged on public networks. + 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 support for DFS (hierarchical name space), secure per-user - session establishment via Kerberos or NTLM or NTLMv2, - safe distributed caching (oplock), optional packet + session establishment via Kerberos or NTLM or NTLMv2, RDMA + (smbdirect), advanced security features, per-share encryption, + directory leases, safe distributed caching (oplock), optional packet signing, Unicode and other internationalization improvements. 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 slightly simpler (compared to CIFS) due to protocol improvements. - If you need to mount to Samba, 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 + If you need to mount to Samba, Azure, Macs or Windows from this machine, say Y. config CIFS_STATS2 bool "Extended statistics" - depends on CIFS_STATS + depends on CIFS help Enabling this option will allow more detailed statistics on SMB 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 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 bool "Support legacy servers which use weaker LANMAN security" - depends on CIFS + depends on CIFS && CIFS_ALLOW_INSECURE_LEGACY help Modern CIFS servers including Samba and most Windows versions (since 1997) support stronger NTLM (and even NTLMv2 and Kerberos) @@ -186,15 +198,6 @@ config CIFS_NFSD_EXPORT help 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 bool "SMB Direct support (Experimental)" depends on CIFS=m && INFINIBAND && INFINIBAND_ADDR_TRANS || CIFS=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c index e1553d1e0e50..b7420e605b28 100644 --- a/fs/cifs/cache.c +++ b/fs/cifs/cache.c @@ -128,8 +128,10 @@ fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data, memset(&auxdata, 0, sizeof(auxdata)); auxdata.eof = cifsi->server_eof; - auxdata.last_write_time = timespec64_to_timespec(cifsi->vfs_inode.i_mtime); - auxdata.last_change_time = timespec64_to_timespec(cifsi->vfs_inode.i_ctime); + auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec; + 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) return FSCACHE_CHECKAUX_OBSOLETE; diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index bfe999505815..f1fbea947fef 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -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, "Features:"); #ifdef CONFIG_CIFS_DFS_UPCALL - seq_printf(m, " dfs"); + seq_printf(m, " DFS"); #endif #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 #ifdef CONFIG_CIFS_WEAK_PW_HASH - seq_printf(m, " lanman"); + seq_printf(m, ",WEAK_PW_HASH"); #endif #ifdef CONFIG_CIFS_POSIX - seq_printf(m, " posix"); + seq_printf(m, ",CIFS_POSIX"); #endif #ifdef CONFIG_CIFS_UPCALL - seq_printf(m, " spnego"); + seq_printf(m, ",UPCALL(SPNEGO)"); #endif #ifdef CONFIG_CIFS_XATTR - seq_printf(m, " xattr"); + seq_printf(m, ",XATTR"); #endif #ifdef CONFIG_CIFS_ACL - seq_printf(m, " acl"); + seq_printf(m, ",ACL"); #endif seq_putc(m, '\n'); seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid); @@ -259,10 +275,9 @@ skip_rdma: server->credits, server->dialect); if (server->sign) seq_printf(m, " signed"); -#ifdef CONFIG_CIFS_SMB311 if (server->posix_ext_supported) seq_printf(m, " posix"); -#endif /* 3.1.1 */ + i++; list_for_each(tmp2, &server->smb_ses_list) { ses = list_entry(tmp2, struct cifs_ses, @@ -350,7 +365,6 @@ skip_rdma: return 0; } -#ifdef CONFIG_CIFS_STATS static ssize_t cifs_stats_proc_write(struct file *file, 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); if (rc == 0) { #ifdef CONFIG_CIFS_STATS2 + int i; + atomic_set(&totBufAllocCount, 0); atomic_set(&totSmBufAllocCount, 0); #endif /* CONFIG_CIFS_STATS2 */ + spin_lock(&GlobalMid_Lock); + GlobalMaxActiveXid = 0; + GlobalCurrentXid = 0; + spin_unlock(&GlobalMid_Lock); spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp1, &cifs_tcp_ses_list) { server = list_entry(tmp1, struct TCP_Server_Info, tcp_ses_list); +#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) { ses = list_entry(tmp2, struct cifs_ses, smb_ses_list); @@ -379,6 +403,10 @@ static ssize_t cifs_stats_proc_write(struct file *file, struct cifs_tcon, tcon_list); 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) 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) { int i; +#ifdef CONFIG_CIFS_STATS2 + int j; +#endif /* STATS2 */ struct list_head *tmp1, *tmp2, *tmp3; struct TCP_Server_Info *server; struct cifs_ses *ses; struct cifs_tcon *tcon; - seq_printf(m, - "Resources in use\nCIFS Session: %d\n", + seq_printf(m, "Resources in use\nCIFS Session: %d\n", sesInfoAllocCount.counter); seq_printf(m, "Share (unique mount targets): %d\n", 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) { server = list_entry(tmp1, struct TCP_Server_Info, 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) { ses = list_entry(tmp2, struct cifs_ses, smb_ses_list); @@ -466,7 +503,6 @@ static const struct file_operations cifs_stats_proc_fops = { .release = single_release, .write = cifs_stats_proc_write, }; -#endif /* STATS */ #ifdef CONFIG_CIFS_SMB_DIRECT #define PROC_FILE_DEFINE(name) \ @@ -524,9 +560,7 @@ cifs_proc_init(void) proc_create_single("DebugData", 0, proc_fs_cifs, cifs_debug_data_proc_show); -#ifdef CONFIG_CIFS_STATS 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("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_fops); 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("cifsFYI", proc_fs_cifs); remove_proc_entry("traceSMB", proc_fs_cifs); -#ifdef CONFIG_CIFS_STATS remove_proc_entry("Stats", proc_fs_cifs); -#endif remove_proc_entry("SecurityFlags", proc_fs_cifs); remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); remove_proc_entry("LookupCacheEnabled", proc_fs_cifs); diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index ee2a8ec70056..85b31cfa2f3c 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -83,7 +83,13 @@ int __cifs_calc_signature(struct smb_rqst *rqst, 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]); } @@ -452,7 +458,7 @@ find_timestamp(struct cifs_ses *ses) unsigned char *blobptr; unsigned char *blobend; struct ntlmssp2_name *attrptr; - struct timespec ts; + struct timespec64 ts; if (!ses->auth_key.len || !ses->auth_key.response) return 0; @@ -477,7 +483,7 @@ find_timestamp(struct cifs_ses *ses) blobptr += attrsize; /* advance attr value */ } - ktime_get_real_ts(&ts); + ktime_get_real_ts64(&ts); return cpu_to_le64(cifs_UnixTimeToNT(ts)); } diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index d5aa7ae917bf..7065426b3280 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -139,6 +139,9 @@ cifs_read_super(struct super_block *sb) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_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) sb->s_maxbytes = MAX_LFS_FILESIZE; else @@ -209,14 +212,16 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) xid = get_xid(); - /* - * PATH_MAX may be too long - it would presumably be total path, - * but note that some servers (includinng Samba 3) have a shorter - * maximum path. - * - * Instead could get the real value via SMB_QUERY_FS_ATTRIBUTE_INFO. - */ - buf->f_namelen = PATH_MAX; + if (le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength) > 0) + buf->f_namelen = + le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength); + else + buf->f_namelen = PATH_MAX; + + buf->f_fsid.val[0] = tcon->vol_serial_number; + /* 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_ffree = 0; /* unlimited */ @@ -427,7 +432,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root) else if (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); if (srcaddr->sa_family != AF_UNSPEC) { @@ -481,20 +486,12 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_puts(s, ",persistenthandles"); else if (tcon->use_resilient) seq_puts(s, ",resilienthandles"); - -#ifdef CONFIG_CIFS_SMB311 if (tcon->posix_extensions) seq_puts(s, ",posix"); else if (tcon->unix_ext) seq_puts(s, ",unix"); else 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) seq_puts(s, ",posixpaths"); 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, ",echo_interval=%lu", 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 */ seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c923c7854027..0c9ab62c3df4 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -76,6 +76,9 @@ #define SMB_ECHO_INTERVAL_MAX 600 #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. * This value is chosen somewhat arbitrarily. The Windows client @@ -191,9 +194,7 @@ enum smb_version { Smb_21, Smb_30, Smb_302, -#ifdef CONFIG_CIFS_SMB311 Smb_311, -#endif /* SMB311 */ Smb_3any, Smb_default, Smb_version_err @@ -456,13 +457,11 @@ struct smb_version_operations { long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t, loff_t); /* init transform request - used for encryption for now */ - int (*init_transform_rq)(struct TCP_Server_Info *, struct smb_rqst *, - struct smb_rqst *); - /* free transform request */ - void (*free_transform_rq)(struct smb_rqst *); + int (*init_transform_rq)(struct TCP_Server_Info *, int num_rqst, + struct smb_rqst *, struct smb_rqst *); int (*is_transform_hdr)(void *buf); 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); int (*next_header)(char *); @@ -684,15 +683,14 @@ struct TCP_Server_Info { #ifdef CONFIG_CIFS_STATS2 atomic_t in_send; /* requests trying to send */ 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_write; -#ifdef CONFIG_CIFS_SMB311 __le16 cipher_type; /* save initital negprot hash */ __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; bool posix_ext_supported; -#endif /* 3.1.1 */ struct delayed_work reconnect; /* reconnect workqueue job */ struct mutex reconnect_mutex; /* prevent simultaneous reconnects */ unsigned long echo_interval; @@ -886,9 +884,7 @@ struct cifs_ses { __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; -#ifdef CONFIG_CIFS_SMB311 __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; -#endif /* 3.1.1 */ /* * Network interfaces available on the server this session is @@ -913,6 +909,7 @@ cap_unix(struct cifs_ses *ses) struct cached_fid { bool is_valid:1; /* Do we have a useable root fid */ + struct kref refcount; struct cifs_fid *fid; struct mutex fid_mutex; struct cifs_tcon *tcon; @@ -936,7 +933,6 @@ struct cifs_tcon { __u32 tid; /* The 4 byte tree id */ __u16 Flags; /* optional support bits */ enum statusEnum tidStatus; -#ifdef CONFIG_CIFS_STATS atomic_t num_smbs_sent; union { struct { @@ -967,24 +963,9 @@ struct cifs_tcon { atomic_t smb2_com_failed[NUMBER_OF_SMB2_COMMANDS]; } smb2_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_written; spinlock_t stat_lock; /* protects the two fields above */ -#endif /* CONFIG_CIFS_STATS */ FILE_SYSTEM_DEVICE_INFO fsDevInfo; FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ FILE_SYSTEM_UNIX_INFO fsUnixInfo; @@ -997,9 +978,7 @@ struct cifs_tcon { bool seal:1; /* transport encryption for this mounted share */ bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol for this mount even if server would support */ -#ifdef CONFIG_CIFS_SMB311 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 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 */ @@ -1046,6 +1025,7 @@ struct tcon_link { }; 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 * tlink_tcon(struct tcon_link *tlink) @@ -1352,7 +1332,6 @@ convert_delimiter(char *path, char delim) *pos = delim; } -#ifdef CONFIG_CIFS_STATS #define cifs_stats_inc atomic_inc 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; 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; unsigned int cf_nlink; unsigned int cf_dtype; - struct timespec cf_atime; - struct timespec cf_mtime; - struct timespec cf_ctime; + struct timespec64 cf_atime; + struct timespec64 cf_mtime; + struct timespec64 cf_ctime; }; static inline void free_dfs_info_param(struct dfs_info3_param *param) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 1890f534c88b..20adda4de83b 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -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, struct smb_rqst *rqst, int *resp_buf_type, 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 *, struct smb_hdr * /* input */ , struct smb_hdr * /* out */ , @@ -143,9 +147,9 @@ extern enum securityEnum select_sectype(struct TCP_Server_Info *server, enum securityEnum requested); extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *nls_cp); -extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); -extern u64 cifs_UnixTimeToNT(struct timespec); -extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, +extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); +extern u64 cifs_UnixTimeToNT(struct timespec64); +extern struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset); extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); extern int cifs_get_writer(struct cifsInodeInfo *cinode); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 93408eab92e7..dc2f4cf08fe9 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -508,13 +508,13 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) * this requirement. */ int val, seconds, remain, result; - struct timespec ts; - unsigned long utc = ktime_get_real_seconds(); + struct timespec64 ts; + time64_t utc = ktime_get_real_seconds(); ts = cnvrtDosUnixTm(rsp->SrvTime.Date, rsp->SrvTime.Time, 0); - cifs_dbg(FYI, "SrvTime %d sec since 1970 (utc: %d) diff: %d\n", - (int)ts.tv_sec, (int)utc, - (int)(utc - ts.tv_sec)); + cifs_dbg(FYI, "SrvTime %lld sec since 1970 (utc: %lld) diff: %lld\n", + ts.tv_sec, utc, + utc - ts.tv_sec); val = (int)(utc - ts.tv_sec); seconds = abs(val); result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ; @@ -4082,7 +4082,7 @@ QInfRetry: if (rc) { cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc); } else if (data) { - struct timespec ts; + struct timespec64 ts; __u32 time = le32_to_cpu(pSMBr->last_write_time); /* decode response */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 5df2c0698cda..c832a8a1970a 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -303,10 +303,8 @@ static const match_table_t cifs_smb_version_tokens = { { Smb_21, SMB21_VERSION_STRING }, { Smb_30, SMB30_VERSION_STRING }, { Smb_302, SMB302_VERSION_STRING }, -#ifdef CONFIG_CIFS_SMB311 { Smb_311, SMB311_VERSION_STRING }, { Smb_311, ALT_SMB311_VERSION_STRING }, -#endif /* SMB311 */ { Smb_3any, SMB3ANY_VERSION_STRING }, { Smb_default, SMBDEFAULT_VERSION_STRING }, { Smb_version_err, NULL } @@ -350,6 +348,7 @@ cifs_reconnect(struct TCP_Server_Info *server) server->max_read = 0; cifs_dbg(FYI, "Reconnecting tcp session\n"); + trace_smb3_reconnect(server->CurrentMid, server->hostname); /* before reconnecting the tcp session, mark the smb session (uid) 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 cifs_demultiplex_thread(void *p) { - int length; + int i, num_mids, length; struct TCP_Server_Info *server = p; unsigned int pdu_length; unsigned int next_offset; char *buf = 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; cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current)); @@ -924,58 +924,75 @@ next_pdu: 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 && server->ops->receive_transform && server->ops->is_transform_hdr(buf)) { length = server->ops->receive_transform(server, - &mid_entry); + mids, + bufs, + &num_mids); } 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) - length = standard_receive3(server, mid_entry); + if (!mids[0] || !mids[0]->receive) + length = standard_receive3(server, mids[0]); else - length = mid_entry->receive(server, mid_entry); + length = mids[0]->receive(server, mids[0]); } if (length < 0) { - if (mid_entry) - cifs_mid_q_entry_release(mid_entry); + for (i = 0; i < num_mids; i++) + if (mids[i]) + cifs_mid_q_entry_release(mids[i]); continue; } if (server->large_buf) buf = server->bigbuf; + server->lstrp = jiffies; - if (mid_entry != NULL) { - mid_entry->resp_buf_size = server->pdu_size; - if ((mid_entry->mid_flags & MID_WAIT_CANCELLED) && - mid_entry->mid_state == MID_RESPONSE_RECEIVED && - server->ops->handle_cancelled_mid) - server->ops->handle_cancelled_mid( - mid_entry->resp_buf, - server); - if (!mid_entry->multiRsp || mid_entry->multiEnd) - mid_entry->callback(mid_entry); + for (i = 0; i < num_mids; i++) { + if (mids[i] != NULL) { + mids[i]->resp_buf_size = server->pdu_size; + if ((mids[i]->mid_flags & MID_WAIT_CANCELLED) && + mids[i]->mid_state == MID_RESPONSE_RECEIVED && + server->ops->handle_cancelled_mid) + server->ops->handle_cancelled_mid( + mids[i]->resp_buf, + server); - cifs_mid_q_entry_release(mid_entry); - } else if (server->ops->is_oplock_break && - server->ops->is_oplock_break(buf, server)) { - cifs_dbg(FYI, "Received oplock break\n"); - } else { - cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n", - atomic_read(&midCount)); - cifs_dump_mem("Received Data is: ", buf, - HEADER_SIZE(server)); + if (!mids[i]->multiRsp || mids[i]->multiEnd) + mids[i]->callback(mids[i]); + + cifs_mid_q_entry_release(mids[i]); + } else if (server->ops->is_oplock_break && + server->ops->is_oplock_break(bufs[i], + server)) { + cifs_dbg(FYI, "Received oplock break\n"); + } else { + cifs_dbg(VFS, "No task to wake, unknown frame " + "received! NumMids %d\n", + atomic_read(&midCount)); + cifs_dump_mem("Received Data is: ", bufs[i], + HEADER_SIZE(server)); #ifdef CONFIG_CIFS_DEBUG2 - if (server->ops->dump_detail) - server->ops->dump_detail(buf, server); - cifs_dump_mids(server); + if (server->ops->dump_detail) + server->ops->dump_detail(bufs[i], + server); + cifs_dump_mids(server); #endif /* CIFS_DEBUG2 */ + } } + if (pdu_length > server->pdu_size) { if (!allocate_buffers(server)) continue; @@ -1174,6 +1191,7 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol, bool is_smb3) substring_t args[MAX_OPT_ARGS]; switch (match_token(value, cifs_smb_version_tokens, args)) { +#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY case Smb_1: if (disable_legacy_dialects) { 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->vals = &smb20_values; 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: vol->ops = &smb21_operations; 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->vals = &smb302_values; break; -#ifdef CONFIG_CIFS_SMB311 case Smb_311: vol->ops = &smb311_operations; vol->vals = &smb311_values; break; -#endif /* SMB311 */ case Smb_3any: vol->ops = &smb30_operations; /* currently identical with 3.0 */ 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) && (ses->server->posix_ext_supported)) { - if (ses->server->vals->protocol_id == SMB311_PROT_ID) { + if (volume_info->linux_ext) { + if (ses->server->posix_ext_supported) { tcon->posix_extensions = true; printk_once(KERN_WARNING "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 @@ -3992,11 +4018,9 @@ try_mount_again: goto remote_path_check; } -#ifdef CONFIG_CIFS_SMB311 /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ if (tcon->posix_extensions) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; -#endif /* SMB3.11 */ /* tell server which Unix caps we support */ if (cap_unix(tcon->ses)) { @@ -4459,11 +4483,10 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) goto out; } -#ifdef CONFIG_CIFS_SMB311 /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ if (tcon->posix_extensions) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; -#endif /* SMB3.11 */ + if (cap_unix(ses)) reset_cifs_unix_caps(0, tcon, NULL, vol_info); diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index 85145a763021..ea6ace9c2417 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -129,8 +129,10 @@ static void cifs_fscache_acquire_inode_cookie(struct cifsInodeInfo *cifsi, memset(&auxdata, 0, sizeof(auxdata)); auxdata.eof = cifsi->server_eof; - auxdata.last_write_time = timespec64_to_timespec(cifsi->vfs_inode.i_mtime); - auxdata.last_change_time = timespec64_to_timespec(cifsi->vfs_inode.i_ctime); + auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec; + 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 = fscache_acquire_cookie(tcon->fscache, @@ -166,8 +168,10 @@ void cifs_fscache_release_inode_cookie(struct inode *inode) if (cifsi->fscache) { memset(&auxdata, 0, sizeof(auxdata)); auxdata.eof = cifsi->server_eof; - auxdata.last_write_time = timespec64_to_timespec(cifsi->vfs_inode.i_mtime); - auxdata.last_change_time = timespec64_to_timespec(cifsi->vfs_inode.i_ctime); + auxdata.last_write_time_sec = cifsi->vfs_inode.i_mtime.tv_sec; + 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); fscache_relinquish_cookie(cifsi->fscache, &auxdata, false); diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index c7e3ac251e16..8c0862e41306 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -31,9 +31,11 @@ * Auxiliary data attached to CIFS inode within the cache */ struct cifs_fscache_inode_auxdata { - struct timespec last_write_time; - struct timespec last_change_time; - u64 eof; + u64 last_write_time_sec; + u64 last_change_time_sec; + u32 last_write_time_nsec; + u32 last_change_time_nsec; + u64 eof; }; /* diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index a2cfb33e85c1..d32eaa4b2437 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -95,7 +95,6 @@ static void cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) { struct cifsInodeInfo *cifs_i = CIFS_I(inode); - struct timespec ts; cifs_dbg(FYI, "%s: revalidating inode %llu\n", __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 */ - ts = timespec64_to_timespec(inode->i_mtime); - if (timespec_equal(&ts, &fattr->cf_mtime) && + if (timespec64_equal(&inode->i_mtime, &fattr->cf_mtime) && cifs_i->server_eof == fattr->cf_eof) { cifs_dbg(FYI, "%s: inode %llu is unchanged\n", __func__, cifs_i->uniqueid); @@ -164,9 +162,9 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) cifs_revalidate_cache(inode, fattr); spin_lock(&inode->i_lock); - inode->i_atime = timespec_to_timespec64(fattr->cf_atime); - inode->i_mtime = timespec_to_timespec64(fattr->cf_mtime); - inode->i_ctime = timespec_to_timespec64(fattr->cf_ctime); + inode->i_atime = fattr->cf_atime; + inode->i_mtime = fattr->cf_mtime; + inode->i_ctime = fattr->cf_ctime; inode->i_rdev = fattr->cf_rdev; cifs_nlink_fattr_to_inode(inode, fattr); 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_uid = cifs_sb->mnt_uid; fattr->cf_gid = cifs_sb->mnt_gid; - ktime_get_real_ts(&fattr->cf_mtime); - fattr->cf_mtime = timespec_trunc(fattr->cf_mtime, sb->s_time_gran); + ktime_get_real_ts64(&fattr->cf_mtime); + fattr->cf_mtime = timespec64_trunc(fattr->cf_mtime, sb->s_time_gran); fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime; fattr->cf_nlink = 2; 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) fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); else { - ktime_get_real_ts(&fattr->cf_atime); - fattr->cf_atime = timespec_trunc(fattr->cf_atime, sb->s_time_gran); + ktime_get_real_ts64(&fattr->cf_atime); + fattr->cf_atime = timespec64_trunc(fattr->cf_atime, sb->s_time_gran); } 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) return -ENOSYS; + info_buf.Pad = 0; + if (attrs->ia_valid & ATTR_ATIME) { set_time = true; info_buf.LastAccessTime = - cpu_to_le64(cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_atime))); + cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime)); } else info_buf.LastAccessTime = 0; if (attrs->ia_valid & ATTR_MTIME) { set_time = true; info_buf.LastWriteTime = - cpu_to_le64(cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_mtime))); + cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); } else 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)) { cifs_dbg(FYI, "CIFS - CTIME changed\n"); info_buf.ChangeTime = - cpu_to_le64(cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_ctime))); + cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); } else info_buf.ChangeTime = 0; @@ -1577,14 +1577,12 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) server = tcon->ses->server; -#ifdef CONFIG_CIFS_SMB311 if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) { rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path, cifs_sb); d_drop(direntry); /* for time being always refresh inode info */ goto mkdir_out; } -#endif /* SMB311 */ if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & 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 */ if (CIFS_I(inode)->createtime) { stat->result_mask |= STATX_BTIME; - stat->btime = timespec_to_timespec64( - cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime))); + stat->btime = + cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime)); } 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 */ if (attrs->ia_valid & ATTR_ATIME) - args->atime = cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_atime)); + args->atime = cifs_UnixTimeToNT(attrs->ia_atime); else args->atime = NO_CHANGE_64; if (attrs->ia_valid & ATTR_MTIME) - args->mtime = cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_mtime)); + args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime); else args->mtime = NO_CHANGE_64; if (attrs->ia_valid & ATTR_CTIME) - args->ctime = cifs_UnixTimeToNT(timespec64_to_timespec(attrs->ia_ctime)); + args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime); else args->ctime = NO_CHANGE_64; diff --git a/fs/cifs/link.c b/fs/cifs/link.c index de41f96aba49..2148b0f60e5e 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -396,7 +396,7 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, struct cifs_io_parms io_parms; int buf_type = CIFS_NO_BUFFER; __le16 *utf16_path; - __u8 oplock = SMB2_OPLOCK_LEVEL_II; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; struct smb2_file_all_info *pfile_info = NULL; oparms.tcon = tcon; @@ -459,7 +459,7 @@ smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, struct cifs_io_parms io_parms; int create_options = CREATE_NOT_DIR; __le16 *utf16_path; - __u8 oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; struct kvec iov[2]; if (backup_cred(cifs_sb)) diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 53e8362cbc4a..dacb2c05674c 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -122,9 +122,7 @@ tconInfoAlloc(void) mutex_init(&ret_buf->crfid.fid_mutex); ret_buf->crfid.fid = kzalloc(sizeof(struct cifs_fid), GFP_KERNEL); -#ifdef CONFIG_CIFS_STATS spin_lock_init(&ret_buf->stat_lock); -#endif } return ret_buf; } diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index d7ad0dfe4e68..fdd908e4a26b 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -918,10 +918,10 @@ smbCalcSize(void *buf, struct TCP_Server_Info *server) * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) * into Unix UTC (based 1970-01-01, in seconds). */ -struct timespec +struct timespec64 cifs_NTtimeToUnix(__le64 ntutc) { - struct timespec ts; + struct timespec64 ts; /* BB what about the timezone? BB */ /* Subtract the NTFS time offset, then convert to 1s intervals. */ @@ -935,12 +935,12 @@ cifs_NTtimeToUnix(__le64 ntutc) */ if (t < 0) { 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_sec = -abs_t; } else { 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; } @@ -949,7 +949,7 @@ cifs_NTtimeToUnix(__le64 ntutc) /* Convert the Unix UTC into NT UTC. */ u64 -cifs_UnixTimeToNT(struct timespec t) +cifs_UnixTimeToNT(struct timespec64 t) { /* Convert to 100ns intervals and then add the 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 }; -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; - int sec, min, days, month, year; + struct timespec64 ts; + time64_t sec; + int min, days, month, year; u16 date = le16_to_cpu(le_date); u16 time = le16_to_cpu(le_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; min = st->Minutes; 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 += 60 * 60 * st->Hours; if (st->Hours > 24) diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 646dcd149de1..378151e09e91 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -624,7 +624,6 @@ cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, static void 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_reads, 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_acl_get, 0); atomic_set(&tcon->stats.cifs_stats.num_acl_set, 0); -#endif } static void cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon) { -#ifdef CONFIG_CIFS_STATS seq_printf(m, " Oplocks breaks: %d", atomic_read(&tcon->stats.cifs_stats.num_oplock_brks)); 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_fnext), atomic_read(&tcon->stats.cifs_stats.num_fclose)); -#endif } static void diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index d01ad706d7fc..1eef1791d0c4 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -120,7 +120,9 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, 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); if (tmprc) rc = tmprc; @@ -281,7 +283,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path, int rc; if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) && - (buf->LastWriteTime == 0) && (buf->ChangeTime) && + (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) && (buf->Attributes == 0)) return 0; /* would be a no op, no sense sending this */ diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 3ff7cec2da81..303d4592ebe7 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -93,7 +93,6 @@ static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24) }; -#ifdef CONFIG_CIFS_SMB311 static __u32 get_neg_ctxt_len(struct smb2_sync_hdr *hdr, __u32 len, __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 */ return (len - nc_offset) + size_of_pad_before_neg_ctxts; } -#endif /* CIFS_SMB311 */ int 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); -#ifdef CONFIG_CIFS_SMB311 if (shdr->Command == SMB2_NEGOTIATE) clc_len += get_neg_ctxt_len(shdr, len, clc_len); -#endif /* SMB311 */ + if (len != clc_len) { cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n", 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 \ */ if (from[0] == '\\') start_of_path = from + 1; -#ifdef CONFIG_CIFS_SMB311 + /* SMB311 POSIX extensions paths do not include leading slash */ else if (cifs_sb_master_tlink(cifs_sb) && cifs_sb_master_tcon(cifs_sb)->posix_extensions && (from[0] == '/')) { start_of_path = from + 1; - } -#endif /* 311 */ - else + } else start_of_path = from; 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; } -#ifdef CONFIG_CIFS_SMB311 /** * 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; } -#endif diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index ea92a38b2f08..541258447c4c 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -444,7 +444,11 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */, NULL /* no data input */, 0 /* no data input */, (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); goto out; } @@ -466,21 +470,36 @@ out: return rc; } -void -smb2_cached_lease_break(struct work_struct *work) +static void +smb2_close_cached_fid(struct kref *ref) { - struct cached_fid *cfid = container_of(work, - struct cached_fid, lease_break); - mutex_lock(&cfid->fid_mutex); + struct cached_fid *cfid = container_of(ref, struct cached_fid, + refcount); + if (cfid->is_valid) { cifs_dbg(FYI, "clear cached root file handle\n"); SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid, cfid->fid->volatile_fid); 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); } +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 */ @@ -495,6 +514,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) if (tcon->crfid.is_valid) { cifs_dbg(FYI, "found a cached root file handle\n"); memcpy(pfid, tcon->crfid.fid, sizeof(struct cifs_fid)); + kref_get(&tcon->crfid.refcount); mutex_unlock(&tcon->crfid.fid_mutex); 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)); tcon->crfid.tcon = tcon; tcon->crfid.is_valid = true; + kref_init(&tcon->crfid.refcount); + kref_get(&tcon->crfid.refcount); } mutex_unlock(&tcon->crfid.fid_mutex); return rc; @@ -549,9 +571,14 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 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, FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */ if (no_cached_open) SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); + else + close_shroot(&tcon->crfid); + return; } @@ -873,13 +900,11 @@ smb2_can_echo(struct TCP_Server_Info *server) static void smb2_clear_stats(struct cifs_tcon *tcon) { -#ifdef CONFIG_CIFS_STATS int 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_failed[i], 0); } -#endif } static void @@ -918,67 +943,58 @@ smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon) static void 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 *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])); - seq_printf(m, "\nSessionSetups: %d sent %d failed", - atomic_read(&sent[SMB2_SESSION_SETUP_HE]), - atomic_read(&failed[SMB2_SESSION_SETUP_HE])); - seq_printf(m, "\nLogoffs: %d sent %d failed", - atomic_read(&sent[SMB2_LOGOFF_HE]), - atomic_read(&failed[SMB2_LOGOFF_HE])); - seq_printf(m, "\nTreeConnects: %d sent %d failed", + + /* + * Can't display SMB2_NEGOTIATE, SESSION_SETUP, LOGOFF, CANCEL and ECHO + * totals (requests sent) since those SMBs are per-session not per tcon + */ + seq_printf(m, "\nBytes read: %llu Bytes written: %llu", + (long long)(tcon->bytes_read), + (long long)(tcon->bytes_written)); + seq_printf(m, "\nTreeConnects: %d total %d failed", atomic_read(&sent[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(&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(&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(&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(&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(&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(&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(&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(&failed[SMB2_IOCTL_HE])); - seq_printf(m, "\nCancels: %d sent %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", + seq_printf(m, "\nQueryDirectories: %d total %d failed", atomic_read(&sent[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(&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(&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(&failed[SMB2_SET_INFO_HE])); seq_printf(m, "\nOplockBreaks: %d sent %d failed", atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]), atomic_read(&failed[SMB2_OPLOCK_BREAK_HE])); -#endif } 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 smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, struct cifsFileInfo *cfile, void __user *ioc_buf) @@ -1382,14 +1405,27 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, kfree(retbuf); 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)) rc = -EFAULT; @@ -1487,7 +1523,11 @@ smb2_is_session_expired(char *buf) shdr->Status != STATUS_USER_SESSION_DELETED) 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"); + return true; } @@ -1504,16 +1544,140 @@ smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, 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 smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, 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; __le16 srch_path = 0; /* Null - open root of share */ u8 oplock = SMB2_OPLOCK_LEVEL_NONE; struct cifs_open_parms oparms; struct cifs_fid fid; + if (!tcon->posix_extensions) + return smb2_queryfs(xid, tcon, buf); + oparms.tcon = tcon; oparms.desired_access = FILE_READ_ATTRIBUTES; 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); if (rc) return rc; + + rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid, + fid.volatile_fid, buf); 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); return rc; } @@ -1700,7 +1865,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, &resp_buftype); if (!rc || !err_iov.iov_base) { rc = -ENOENT; - goto querty_exit; + goto free_path; } err_buf = err_iov.iov_base; @@ -1741,6 +1906,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, querty_exit: free_rsp_buf(resp_buftype, err_buf); + free_path: kfree(utf16_path); 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)); } -/* Assumes: - * rqst->rq_iov[0] is transform header - * rqst->rq_iov[1+] data to be encrypted/decrypted +/* Assumes the first rqst has a transform header as the first iov. + * I.e. + * 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 * -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 assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; + unsigned int sg_len; struct scatterlist *sg; unsigned int i; 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); if (!sg) return NULL; sg_init_table(sg, sg_len); - smb2_sg_set_buf(&sg[0], rqst->rq_iov[0].iov_base + 20, assoc_data_len); - for (i = 1; i < rqst->rq_nvec; i++) - smb2_sg_set_buf(&sg[i], rqst->rq_iov[i].iov_base, - rqst->rq_iov[i].iov_len); - for (j = 0; i < sg_len - 1; i++, j++) { - unsigned int len, offset; + for (i = 0; i < num_rqst; i++) { + for (j = 0; j < rqst[i].rq_nvec; j++) { + /* + * The first rqst has a transform header where the + * first 20 bytes are not part of the encrypted blob + */ + 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); - sg_set_page(&sg[i], rqst->rq_pages[j], len, offset); + for (j = 0; j < rqst[i].rq_npages; j++) { + 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; } @@ -2386,10 +2568,11 @@ smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key) * untouched. */ 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 *)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; int rc = 0; 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; } - sg = init_sg(rqst, sign); + sg = init_sg(num_rqst, rqst, sign); if (!sg) { cifs_dbg(VFS, "%s: Failed to init sg", __func__); rc = -ENOMEM; @@ -2477,103 +2660,98 @@ free_req: return rc; } +void +smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst) +{ + int i, j; + + for (i = 0; i < num_rqst; i++) { + if (rqst[i].rq_pages) { + for (j = rqst[i].rq_npages - 1; j >= 0; j--) + put_page(rqst[i].rq_pages[j]); + kfree(rqst[i].rq_pages); + } + } +} + +/* + * 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, struct smb_rqst *new_rq, - struct smb_rqst *old_rq) +smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst, + struct smb_rqst *new_rq, struct smb_rqst *old_rq) { - struct kvec *iov; struct page **pages; - struct smb2_transform_hdr *tr_hdr; - unsigned int npages = old_rq->rq_npages; - unsigned int orig_len; - int i; + 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; - pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); - if (!pages) - return rc; - - new_rq->rq_pages = 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; + for (i = 1; i < num_rqst; i++) { + npages = old_rq[i - 1].rq_npages; + pages = kmalloc_array(npages, sizeof(struct page *), + GFP_KERNEL); + if (!pages) + 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 all iovs from the old */ - memcpy(&iov[1], &old_rq->rq_iov[0], - sizeof(struct kvec) * old_rq->rq_nvec); + /* copy pages form the old */ + for (j = 0; j < npages; j++) { + char *dst, *src; + unsigned int offset, len; - new_rq->rq_iov = iov; - new_rq->rq_nvec = old_rq->rq_nvec + 1; + rqst_page_get_length(&new_rq[i], j, &len, &offset); - tr_hdr = kmalloc(sizeof(struct smb2_transform_hdr), GFP_KERNEL); - if (!tr_hdr) - goto err_free_iov; + dst = (char *) kmap(new_rq[i].rq_pages[j]) + offset; + src = (char *) kmap(old_rq[i - 1].rq_pages[j]) + offset; - orig_len = smb_rqst_len(server, old_rq); + memcpy(dst, src, len); + kunmap(new_rq[i].rq_pages[j]); + kunmap(old_rq[i - 1].rq_pages[j]); + } + } - /* fill the 2nd iov with a transform header */ + /* fill the 1st 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); + rc = crypt_message(server, num_rqst, new_rq, 1); cifs_dbg(FYI, "encrypt message returned %d", rc); if (rc) - goto err_free_tr_hdr; + goto err_free; 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); +err_free: + smb3_free_compound_rqst(num_rqst - 1, &new_rq[1]); return rc; } -static void -smb3_free_transform_rq(struct smb_rqst *rqst) -{ - int i = rqst->rq_npages - 1; - - for (; i >= 0; i--) - put_page(rqst->rq_pages[i]); - kfree(rqst->rq_pages); - /* free transform header */ - kfree(rqst->rq_iov[0].iov_base); - kfree(rqst->rq_iov); -} - static int smb3_is_transform_hdr(void *buf) { @@ -2603,7 +2781,7 @@ decrypt_raw_data(struct TCP_Server_Info *server, char *buf, rqst.rq_pagesz = 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); if (rc) @@ -2878,13 +3056,20 @@ discard_data: static int 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 *tmpbuf; + struct smb2_sync_hdr *shdr; unsigned int pdu_length = server->pdu_size; unsigned int buf_size; 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 */ if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) { @@ -2905,24 +3090,61 @@ receive_encrypted_standard(struct TCP_Server_Info *server, if (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); if (mid_entry == NULL) cifs_dbg(FYI, "mid not found\n"); else { cifs_dbg(FYI, "mid found\n"); 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) - 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 -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; 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; } + /* TODO: add support for compounds containing READ. */ 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 @@ -3250,7 +3473,6 @@ struct smb_version_operations smb30_operations = { .fallocate = smb3_fallocate, .enum_snapshots = smb3_enum_snapshots, .init_transform_rq = smb3_init_transform_rq, - .free_transform_rq = smb3_free_transform_rq, .is_transform_hdr = smb3_is_transform_hdr, .receive_transform = smb3_receive_transform, .get_dfs_refer = smb2_get_dfs_refer, @@ -3267,7 +3489,6 @@ struct smb_version_operations smb30_operations = { .next_header = smb2_next_header, }; -#ifdef CONFIG_CIFS_SMB311 struct smb_version_operations smb311_operations = { .compare_fids = smb2_compare_fids, .setup_request = smb2_setup_request, @@ -3335,7 +3556,7 @@ struct smb_version_operations smb311_operations = { .is_status_pending = smb2_is_status_pending, .is_session_expired = smb2_is_session_expired, .oplock_response = smb2_oplock_response, - .queryfs = smb2_queryfs, + .queryfs = smb311_queryfs, .mand_lock = smb2_mand_lock, .mand_unlock_range = smb2_unlock_range, .push_mand_locks = smb2_push_mandatory_locks, @@ -3357,7 +3578,6 @@ struct smb_version_operations smb311_operations = { .fallocate = smb3_fallocate, .enum_snapshots = smb3_enum_snapshots, .init_transform_rq = smb3_init_transform_rq, - .free_transform_rq = smb3_free_transform_rq, .is_transform_hdr = smb3_is_transform_hdr, .receive_transform = smb3_receive_transform, .get_dfs_refer = smb2_get_dfs_refer, @@ -3366,9 +3586,13 @@ struct smb_version_operations smb311_operations = { .query_all_EAs = smb2_query_eas, .set_EA = smb2_set_ea, #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, }; -#endif /* CIFS_SMB311 */ struct smb_version_values smb20_values = { .version_string = SMB20_VERSION_STRING, @@ -3496,7 +3720,6 @@ struct smb_version_values smb302_values = { .create_lease_size = sizeof(struct create_lease_v2), }; -#ifdef CONFIG_CIFS_SMB311 struct smb_version_values smb311_values = { .version_string = SMB311_VERSION_STRING, .protocol_id = SMB311_PROT_ID, @@ -3517,4 +3740,3 @@ struct smb_version_values smb311_values = { .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, .create_lease_size = sizeof(struct create_lease_v2), }; -#endif /* SMB311 */ diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 3c92678cb45b..2f1938011395 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -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 */ }; -static int smb3_encryption_required(const struct cifs_tcon *tcon) +int smb3_encryption_required(const struct cifs_tcon *tcon) { if (!tcon) return 0; @@ -360,17 +360,15 @@ smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, total_len); if (tcon != NULL) { -#ifdef CONFIG_CIFS_STATS2 uint16_t com_code = le16_to_cpu(smb2_command); cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); -#endif cifs_stats_inc(&tcon->num_smbs_sent); } return rc; } -#ifdef CONFIG_CIFS_SMB311 + /* offset is sizeof smb2_negotiate_req but rounded up to 8 bytes */ #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; } -#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; req->sync_hdr.SessionId = 0; -#ifdef CONFIG_CIFS_SMB311 + memset(server->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, 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"); else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); -#ifdef CONFIG_CIFS_SMB311 else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); -#endif /* SMB311 */ else { cifs_dbg(VFS, "Illegal dialect returned by server 0x%x\n", 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); - /* 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 * 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, SMB2_PREAUTH_HASH_SIZE); -#endif + /* SMB2 only has an extended negflavor */ server->negflavor = CIFS_NEGFLAVOR_EXTENDED; /* 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; } -#ifdef CONFIG_CIFS_SMB311 if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { if (rsp->NegotiateContextCount) rc = smb311_decode_neg_context(rsp, server, @@ -812,7 +796,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) else cifs_dbg(VFS, "Missing expected negotiate contexts\n"); } -#endif /* CONFIG_CIFS_SMB311 */ neg_exit: free_rsp_buf(resp_buftype, rsp); 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->previous_session = ses->Suid; -#ifdef CONFIG_CIFS_SMB311 /* * Initialize the session hash with the server one. */ memcpy(ses->preauth_sha_hash, ses->server->preauth_sha_hash, SMB2_PREAUTH_HASH_SIZE); -#endif while (sess_data->func) sess_data->func(sess_data); @@ -1875,6 +1856,51 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec, 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 alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, 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; } -#ifdef CONFIG_CIFS_SMB311 int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, umode_t mode, struct cifs_tcon *tcon, const char *full_path, @@ -1928,7 +1953,7 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, { struct smb_rqst rqst; struct smb2_create_req *req; - struct smb2_create_rsp *rsp; + struct smb2_create_rsp *rsp = NULL; struct TCP_Server_Info *server; struct cifs_ses *ses = tcon->ses; 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; int flags = 0; unsigned int total_len; - __le16 *path = cifs_convert_path_to_utf16(full_path, cifs_sb); - - if (!path) - return -ENOMEM; + __le16 *utf16_path = NULL; 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)) server = ses->server; - else - return -EIO; + else { + rc = -EIO; + goto err_free_path; + } + /* resource #2: request */ rc = smb2_plain_req_init(SMB2_CREATE, tcon, (void **) &req, &total_len); - if (rc) - return rc; + goto err_free_path; + if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; - req->ImpersonationLevel = IL_IMPERSONATION; req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES); /* 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; rc = alloc_path_with_tree_prefix(©_path, ©_size, &name_len, - tcon->treeName, path); - if (rc) { - cifs_small_buf_release(req); - return rc; - } + tcon->treeName, utf16_path); + if (rc) + goto err_free_req; + req->NameLength = cpu_to_le16(name_len * 2); uni_path_len = copy_size; - path = copy_path; + /* free before overwriting resource */ + kfree(utf16_path); + utf16_path = copy_path; } 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 */ req->NameLength = cpu_to_le16(uni_path_len - 2); if (uni_path_len % 8 != 0) { copy_size = roundup(uni_path_len, 8); copy_path = kzalloc(copy_size, GFP_KERNEL); if (!copy_path) { - cifs_small_buf_release(req); - return -ENOMEM; + rc = -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 = 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_base = path; + iov[1].iov_base = utf16_path; req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; if (tcon->posix_extensions) { - 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); - } - + /* resource #3: posix buf */ rc = add_posix_context(iov, &n_iov, mode); - if (rc) { - cifs_small_buf_release(req); - kfree(copy_path); - return rc; - } + if (rc) + goto err_free_req; 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_nvec = n_iov; - rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, - &rsp_iov); - - cifs_small_buf_release(req); - rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; - - if (rc != 0) { + /* resource #4: response buffer */ + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); + if (rc) { cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid, - CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES, rc); - goto smb311_mkdir_exit; - } else - trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, - ses->Suid, CREATE_NOT_FILE, - FILE_WRITE_ATTRIBUTES); + CREATE_NOT_FILE, + FILE_WRITE_ATTRIBUTES, rc); + goto err_free_rsp_buf; + } + + 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); /* Eventually save off posix specific response info and timestaps */ -smb311_mkdir_exit: - kfree(copy_path); - kfree(pc_buf); +err_free_rsp_buf: 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; - } -#endif /* SMB311 */ 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) +SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, __u8 *oplock, + struct cifs_open_parms *oparms, __le16 *path) { - struct smb_rqst rqst; + struct TCP_Server_Info *server = tcon->ses->server; 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; __u32 file_attributes = 0; - char *dhc_buf = NULL, *lc_buf = NULL, *pc_buf = NULL; - int flags = 0; + int copy_size; + int uni_path_len; unsigned int total_len; - - cifs_dbg(FYI, "create/open\n"); - - if (ses && (ses->server)) - server = ses->server; - else - return -EIO; + struct kvec *iov = rqst->rq_iov; + __le16 *copy_path; + int rc; rc = smb2_plain_req_init(SMB2_CREATE, tcon, (void **) &req, &total_len); - if (rc) return rc; - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; + 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; if (oparms->create_options & CREATE_OPTION_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->CreateDisposition = cpu_to_le32(oparms->disposition); 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)); /* [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(©_path, ©_size, &name_len, tcon->treeName, path); - if (rc) { - cifs_small_buf_release(req); + if (rc) return rc; - } req->NameLength = cpu_to_le16(name_len * 2); uni_path_len = copy_size; 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; /* MUST set path len (NameLength) to 0 opening root of share */ req->NameLength = cpu_to_le16(uni_path_len - 2); - if (uni_path_len % 8 != 0) { - copy_size = roundup(uni_path_len, 8); - copy_path = kzalloc(copy_size, GFP_KERNEL); - if (!copy_path) { - cifs_small_buf_release(req); - return -ENOMEM; - } - memcpy((char *)copy_path, (const char *)path, - uni_path_len); - uni_path_len = copy_size; - path = copy_path; - } + copy_size = uni_path_len; + if (copy_size % 8 != 0) + copy_size = roundup(copy_size, 8); + copy_path = kzalloc(copy_size, GFP_KERNEL); + if (!copy_path) + return -ENOMEM; + memcpy((char *)copy_path, (const char *)path, + uni_path_len); + uni_path_len = copy_size; + path = copy_path; } 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 { rc = add_lease_context(server, iov, &n_iov, oparms->fid->lease_key, oplock); - if (rc) { - cifs_small_buf_release(req); - kfree(copy_path); + if (rc) return rc; - } - lc_buf = iov[n_iov-1].iov_base; } 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, tcon->use_persistent); - if (rc) { - cifs_small_buf_release(req); - kfree(copy_path); - kfree(lc_buf); + if (rc) return rc; - } - dhc_buf = iov[n_iov-1].iov_base; } -#ifdef CONFIG_CIFS_SMB311 if (tcon->posix_extensions) { if (n_iov > 2) { 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); - if (rc) { - cifs_small_buf_release(req); - kfree(copy_path); - kfree(lc_buf); - kfree(dhc_buf); + if (rc) return rc; + } + + 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); } - pc_buf = iov[n_iov-1].iov_base; + + rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time); + if (rc) + return rc; } -#endif /* SMB311 */ + + + 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(&iov, 0, sizeof(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, &rsp_iov); - cifs_small_buf_release(req); rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; if (rc != 0) { @@ -2275,10 +2318,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, else *oplock = rsp->OplockLevel; creat_exit: - kfree(copy_path); - kfree(lc_buf); - kfree(dhc_buf); - kfree(pc_buf); + SMB2_open_free(&rqst); free_rsp_buf(resp_buftype, rsp); return rc; } @@ -2469,43 +2509,62 @@ SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, } 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 SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, int flags) { struct smb_rqst rqst; - struct smb2_close_req *req; - struct smb2_close_rsp *rsp; + struct smb2_close_rsp *rsp = NULL; struct cifs_ses *ses = tcon->ses; struct kvec iov[1]; struct kvec rsp_iov; int resp_buftype; int rc = 0; - unsigned int total_len; cifs_dbg(FYI, "Close\n"); if (!ses || !(ses->server)) return -EIO; - rc = smb2_plain_req_init(SMB2_CLOSE, tcon, (void **) &req, &total_len); - if (rc) - return rc; - if (smb3_encryption_required(tcon)) 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(&iov, 0, sizeof(iov)); rqst.rq_iov = iov; 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); - cifs_small_buf_release(req); rsp = (struct smb2_close_rsp *)rsp_iov.iov_base; 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 */ close_exit: + SMB2_close_free(&rqst); free_rsp_buf(resp_buftype, rsp); 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); } -static int -validate_iov(unsigned int offset, unsigned int buffer_length, - struct kvec *iov, unsigned int min_buf_size) +int +smb2_validate_iov(unsigned int offset, unsigned int buffer_length, + struct kvec *iov, unsigned int min_buf_size) { unsigned int smb_len = iov->iov_len; 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) return -EINVAL; - rc = validate_iov(offset, buffer_length, iov, minbufsize); + rc = smb2_validate_iov(offset, buffer_length, iov, minbufsize); if (rc) return rc; @@ -2584,36 +2644,22 @@ validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, return 0; } -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) +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) { - struct smb_rqst rqst; struct smb2_query_info_req *req; - struct smb2_query_info_rsp *rsp = NULL; - struct kvec iov[2]; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype; - struct cifs_ses *ses = tcon->ses; - int flags = 0; + struct kvec *iov = rqst->rq_iov; unsigned int total_len; - - cifs_dbg(FYI, "Query Info\n"); - - if (!ses || !(ses->server)) - return -EIO; + int rc; rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, (void **) &req, &total_len); if (rc) return rc; - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - req->InfoType = info_type; req->FileInfoClass = info_class; req->PersistentFileId = persistent_fid; @@ -2630,13 +2676,50 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, iov[0].iov_base = (char *)req; /* 1 for Buffer */ 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(&iov, 0, sizeof(iov)); rqst.rq_iov = iov; 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); - cifs_small_buf_release(req); rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; if (rc) { @@ -2665,6 +2748,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, &rsp_iov, min_len, *data); qinf_exit: + SMB2_query_info_free(&rqst); free_rsp_buf(resp_buftype, rsp); return rc; } @@ -3623,9 +3707,9 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, goto qdir_exit; } - rc = validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, - info_buf_size); + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, + info_buf_size); if (rc) goto qdir_exit; @@ -3927,9 +4011,9 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, return rc; } -static void -copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, - struct kstatfs *kst) +void +smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, + struct kstatfs *kst) { kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) * le32_to_cpu(pfs_inf->SectorsPerAllocationUnit); @@ -3939,6 +4023,25 @@ copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, 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 build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level, int outbuf_len, u64 persistent_fid, u64 volatile_fid) @@ -3976,6 +4079,54 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level, } 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 SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, 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 *)( le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); - rc = validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, - sizeof(struct smb2_fs_full_size_info)); + rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, + sizeof(struct smb2_fs_full_size_info)); if (!rc) - copy_fs_info_to_kstatfs(info, fsdata); + smb2_copy_fs_info_to_kstatfs(info, fsdata); qfsinf_exit: 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) { max_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 { cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); return -EINVAL; @@ -4073,7 +4227,7 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, rsp_len = le32_to_cpu(rsp->OutputBufferLength); 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) 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->perf_sector_size = 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: diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index a671adcc44a6..a2eeae9e0432 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -153,6 +153,8 @@ struct smb2_transform_hdr { * */ +#define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL + #define SMB2_ERROR_STRUCTURE_SIZE2 cpu_to_le16(9) struct smb2_err_rsp { @@ -765,6 +767,14 @@ struct create_durable_handle_reconnect_v2 { struct durable_reconnect_context_v2 dcontext; } __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 struct resume_key_req { 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_VOLUME_FLAGS_INFORMATION 10 /* Local only */ #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 { __le64 TotalAllocationUnits; @@ -1248,6 +1259,17 @@ struct smb3_fs_ss_info { __le32 ByteOffsetForPartitionAlignment; } __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 */ #define FILE_DIRECTORY_INFORMATION 1 #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 */ } __packed; /* level 20 Set */ +extern char smb2_padding[7]; + #endif /* _SMB2PDU_H */ diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 6e6a4f2ec890..b4076577eeb7 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -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, struct cifs_fid *pfid); +extern void close_shroot(struct cached_fid *cfid); extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src); 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, struct smb2_file_all_info *buf, 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, u64 persistent_fid, u64 volatile_fid, u32 opcode, 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); extern int SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, 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, u64 persistent_file_id, u64 volatile_file_id); 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, u64 persistent_file_id, u64 volatile_file_id, 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, u64 persistent_file_id, u64 volatile_file_id, 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, u64 persistent_file_id, u64 volatile_file_id, 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, u64 persistent_file_id, u64 volatile_file_id, int lvl); 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 *, 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_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec); -#endif #endif /* _SMB2PROTO_H */ diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 719d55e63d88..7b351c65ee46 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -70,7 +70,6 @@ err: return rc; } -#ifdef CONFIG_CIFS_SMB311 int smb311_crypto_shash_allocate(struct TCP_Server_Info *server) { @@ -98,7 +97,6 @@ err: cifs_free_hash(&p->hmacsha256, &p->sdeschmacsha256); return rc; } -#endif static struct cifs_ses * 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 smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; struct cifs_ses *ses; - struct shash_desc *shash = &server->secmech.sdeschmacsha256->shash; + struct shash_desc *shash; struct smb_rqst drqst; 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); if (rc) { - cifs_dbg(VFS, "%s: shah256 alloc failed\n", __func__); + cifs_dbg(VFS, "%s: sha256 alloc failed\n", __func__); return rc; } @@ -198,6 +196,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) return rc; } + shash = &server->secmech.sdeschmacsha256->shash; rc = crypto_shash_init(shash); if (rc) { cifs_dbg(VFS, "%s: Could not init sha256", __func__); @@ -395,7 +394,6 @@ generate_smb30signingkey(struct cifs_ses *ses) return generate_smb3signingkey(ses, &triplet); } -#ifdef CONFIG_CIFS_SMB311 int generate_smb311signingkey(struct cifs_ses *ses) @@ -423,7 +421,6 @@ generate_smb311signingkey(struct cifs_ses *ses) return generate_smb3signingkey(ses, &triplet); } -#endif /* 311 */ int smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) diff --git a/fs/cifs/trace.h b/fs/cifs/trace.h index 67e413f6ee4d..d4aed5217a56 100644 --- a/fs/cifs/trace.h +++ b/fs/cifs/trace.h @@ -281,6 +281,44 @@ DEFINE_EVENT(smb3_cmd_done_class, smb3_##name, \ TP_ARGS(tid, sesid, cmd, mid)) 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, 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(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 */ #undef TRACE_INCLUDE_PATH diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index a341ec839c83..78f96fa3d7d9 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -115,8 +115,17 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) now = jiffies; /* commands taking longer than one second are indications that something is wrong, unless it is quite a slow link or server */ - if (time_after(now, midEntry->when_alloc + HZ)) { - if ((cifsFYI & CIFS_TIMER) && (midEntry->command != command)) { + if (time_after(now, midEntry->when_alloc + HZ) && + (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", midEntry->command, midEntry->mid); 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 */ server->tcpStatus = CifsNeedReconnect; + trace_smb3_partial_send_reconnect(server->CurrentMid, + server->hostname); } smbd_done: if (rc < 0 && rc != -EINTR) @@ -373,26 +384,42 @@ smbd_done: } 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; 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 || - !server->ops->free_transform_rq) { - cifs_dbg(VFS, "Encryption requested but transform callbacks are missed\n"); + if (num_rqst > MAX_COMPOUND - 1) + return -ENOMEM; + + 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; } - 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) return rc; - rc = __smb_send_rqst(server, 1, &cur_rqst); - server->ops->free_transform_rq(&cur_rqst); + rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]); + smb3_free_compound_rqst(num_rqst, &cur_rqst[1]); return rc; } @@ -606,7 +633,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, */ cifs_save_when_sent(mid); 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); if (rc < 0) { @@ -746,20 +773,21 @@ cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst) } 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) +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) { - int rc = 0; + int i, j, rc = 0; int timeout, optype; - struct mid_q_entry *midQ; + struct mid_q_entry *midQ[MAX_COMPOUND]; unsigned int credits = 1; char *buf; timeout = flags & CIFS_TIMEOUT_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)) { cifs_dbg(VFS, "Null session\n"); @@ -786,98 +814,117 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, mutex_lock(&ses->server->srv_mutex); - midQ = ses->server->ops->setup_request(ses, rqst); - if (IS_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); + for (i = 0; i < num_rqst; i++) { + midQ[i] = ses->server->ops->setup_request(ses, &rqst[i]); + if (IS_ERR(midQ[i])) { + for (j = 0; j < i; j++) + cifs_delete_mid(midQ[j]); + 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); - 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_save_when_sent(midQ); + + for (i = 0; i < num_rqst; i++) + cifs_save_when_sent(midQ[i]); if (rc < 0) ses->server->sequence_number -= 2; + mutex_unlock(&ses->server->srv_mutex); - if (rc < 0) - goto out; + for (i = 0; i < num_rqst; i++) { + if (rc < 0) + goto out; -#ifdef CONFIG_CIFS_SMB311 - if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) - smb311_update_preauth_hash(ses, rqst->rq_iov, - rqst->rq_nvec); -#endif + if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) + smb311_update_preauth_hash(ses, rqst[i].rq_iov, + rqst[i].rq_nvec); - if (timeout == CIFS_ASYNC_OP) - goto out; + if (timeout == CIFS_ASYNC_OP) + goto out; - rc = wait_for_response(ses->server, midQ); - if (rc != 0) { - cifs_dbg(FYI, "Cancelling wait for mid %llu\n", midQ->mid); - send_cancel(ses->server, rqst, midQ); - spin_lock(&GlobalMid_Lock); - if (midQ->mid_state == MID_REQUEST_SUBMITTED) { - midQ->mid_flags |= MID_WAIT_CANCELLED; - midQ->callback = DeleteMidQEntry; + rc = wait_for_response(ses->server, midQ[i]); + if (rc != 0) { + cifs_dbg(FYI, "Cancelling wait for mid %llu\n", + midQ[i]->mid); + send_cancel(ses->server, &rqst[i], midQ[i]); + spin_lock(&GlobalMid_Lock); + if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) { + 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); + } + + rc = cifs_sync_mid_result(midQ[i], ses->server); + if (rc != 0) { add_credits(ses->server, 1, optype); return rc; } - spin_unlock(&GlobalMid_Lock); - } - - 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; + if (!midQ[i]->resp_buf || + midQ[i]->mid_state != MID_RESPONSE_RECEIVED) { + rc = -EIO; + cifs_dbg(FYI, "Bad MID state?\n"); + goto out; + } -#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 + 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); + credits = ses->server->ops->get_credits(midQ[i]); - rc = ses->server->ops->check_receive(midQ, ses->server, - flags & CIFS_LOG_ERROR); + 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->resp_buf = NULL; + /* mark it so buf will not be freed by cifs_delete_mid */ + if ((flags & CIFS_NO_RESP) == 0) + midQ[i]->resp_buf = NULL; + } out: - cifs_delete_mid(midQ); + for (i = 0; i < num_rqst; i++) + cifs_delete_mid(midQ[i]); add_credits(ses->server, credits, optype); 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 SendReceive2(const unsigned int xid, struct cifs_ses *ses, struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, const int flags, struct kvec *resp_iov) diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 316af84674f1..50ddb795aaeb 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -35,6 +35,14 @@ #define CIFS_XATTR_CIFS_ACL "system.cifs_acl" #define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */ #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 */ 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) { case XATTR_USER: 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); 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); break; } @@ -363,6 +373,19 @@ static const struct xattr_handler cifs_cifs_acl_xattr_handler = { .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 = { .name = XATTR_NAME_POSIX_ACL_ACCESS, .flags = XATTR_ACL_ACCESS, @@ -381,6 +404,7 @@ const struct xattr_handler *cifs_xattr_handlers[] = { &cifs_user_xattr_handler, &cifs_os2_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_default_xattr_handler, NULL |