various cifs/smb3 fixes (including for share deleted cases) and features including improved encrypted read performance, and various debugging improvements

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAl2Dq7wACgkQiiy9cAdy
 T1H5Kgv+NdJCBEfMjYzDPCci0plAFjurbPh5GvlrgkrYuRyCEw7KcG0/DltQSoCk
 TOQ0JTVYulyKPd8uwha2p8ylscKwEi3BPHa0+CMg6qRmCtrRMMhh85TF2pScy8Jz
 s1s9RskEvdJMpZ0disu1uge9O4JHNFB3LnfM7bDMxGTDUYMtuAXYIXTqMpLX3KVq
 8NzcXUD0gAdjqo8iYZ/BtdeE7MBUCTvjp4O7pJh0cq+EV7p2G2DzaPhLJ3U5dL/y
 IlMgMBfyuYNH6wZueB+R2ZO1GeiPe6M4sq/Beh2/5dXKGaNECxo3feDFOHsvsasT
 WTllXAe8rz9AH8g8+QT4mxPQYBYw0IcJaQk5y/12gTiD2CR4BiTcNmgYv49tqiTH
 b4Gl6KbjTvyT0iieStUBywCpU93qD04nwuzBOPqT0FG0T6Papx4sTvvYM3ThGs16
 X891RLxdvj9QOjpPnQ4vw9yAd1s7PDjVm7BA5CRhRf0Yvdc7q2YVyitzGYBzjqF0
 K2f/4HRJ
 =4/w5
 -----END PGP SIGNATURE-----

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

Pull cifs updates from Steve French:
 "Various cifs/smb3 fixes (including for share deleted cases) and
  features including improved encrypted read performance, and various
  debugging improvements.

  Note that since I am at a test event this week with the Samba team,
  and at the annual Storage Developer Conference/SMB3 Plugfest test
  event next week a higher than usual number of fixes is expected later
  next week as other features in progress get additional testing and
  review during these two events"

* tag '5.4-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6: (38 commits)
  cifs: update internal module version number
  cifs: modefromsid: write mode ACE first
  cifs: cifsroot: add more err checking
  smb3: add missing worker function for SMB3 change notify
  cifs: Add support for root file systems
  cifs: modefromsid: make room for 4 ACE
  smb3: fix potential null dereference in decrypt offload
  smb3: fix unmount hang in open_shroot
  smb3: allow disabling requesting leases
  smb3: improve handling of share deleted (and share recreated)
  smb3: display max smb3 requests in flight at any one time
  smb3: only offload decryption of read responses if multiple requests
  cifs: add a helper to find an existing readable handle to a file
  smb3: enable offload of decryption of large reads via mount option
  smb3: allow parallelizing decryption of reads
  cifs: add a debug macro that prints \\server\share for errors
  smb3: fix signing verification of large reads
  smb3: allow skipping signature verification for perf sensitive configurations
  smb3: add dynamic tracepoints for flush and close
  smb3: log warning if CSC policy conflicts with cache mount option
  ...
This commit is contained in:
Linus Torvalds 2019-09-19 10:32:16 -07:00
commit 7e3d2c8210
29 changed files with 1218 additions and 319 deletions

View File

@ -0,0 +1,97 @@
Mounting root file system via SMB (cifs.ko)
===========================================
Written 2019 by Paulo Alcantara <palcantara@suse.de>
Written 2019 by Aurelien Aptel <aaptel@suse.com>
The CONFIG_CIFS_ROOT option enables experimental root file system
support over the SMB protocol via cifs.ko.
It introduces a new kernel command-line option called 'cifsroot='
which will tell the kernel to mount the root file system over the
network by utilizing SMB or CIFS protocol.
In order to mount, the network stack will also need to be set up by
using 'ip=' config option. For more details, see
Documentation/filesystems/nfs/nfsroot.txt.
A CIFS root mount currently requires the use of SMB1+UNIX Extensions
which is only supported by the Samba server. SMB1 is the older
deprecated version of the protocol but it has been extended to support
POSIX features (See [1]). The equivalent extensions for the newer
recommended version of the protocol (SMB3) have not been fully
implemented yet which means SMB3 doesn't support some required POSIX
file system objects (e.g. block devices, pipes, sockets).
As a result, a CIFS root will default to SMB1 for now but the version
to use can nonetheless be changed via the 'vers=' mount option. This
default will change once the SMB3 POSIX extensions are fully
implemented.
Server configuration
====================
To enable SMB1+UNIX extensions you will need to set these global
settings in Samba smb.conf:
[global]
server min protocol = NT1
unix extension = yes # default
Kernel command line
===================
root=/dev/cifs
This is just a virtual device that basically tells the kernel to mount
the root file system via SMB protocol.
cifsroot=//<server-ip>/<share>[,options]
Enables the kernel to mount the root file system via SMB that are
located in the <server-ip> and <share> specified in this option.
The default mount options are set in fs/cifs/cifsroot.c.
server-ip
IPv4 address of the server.
share
Path to SMB share (rootfs).
options
Optional mount options. For more information, see mount.cifs(8).
Examples
========
Export root file system as a Samba share in smb.conf file.
...
[linux]
path = /path/to/rootfs
read only = no
guest ok = yes
force user = root
force group = root
browseable = yes
writeable = yes
admin users = root
public = yes
create mask = 0777
directory mask = 0777
...
Restart smb service.
# systemctl restart smb
Test it under QEMU on a kernel built with CONFIG_CIFS_ROOT and
CONFIG_IP_PNP options enabled.
# qemu-system-x86_64 -enable-kvm -cpu host -m 1024 \
-kernel /path/to/linux/arch/x86/boot/bzImage -nographic \
-append "root=/dev/cifs rw ip=dhcp cifsroot=//10.0.2.2/linux,username=foo,password=bar console=ttyS0 3"
1: https://wiki.samba.org/index.php/UNIX_Extensions

View File

@ -211,3 +211,11 @@ config CIFS_FSCACHE
Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
to be cached locally on disk through the general filesystem cache to be cached locally on disk through the general filesystem cache
manager. If unsure, say N. manager. If unsure, say N.
config CIFS_ROOT
bool "SMB root file system (Experimental)"
depends on CIFS=y && IP_PNP
help
Enables root file system support over SMB protocol.
Most people say N here.

View File

@ -21,3 +21,5 @@ cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o
cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o

View File

@ -452,6 +452,7 @@ static ssize_t cifs_stats_proc_write(struct file *file,
list_for_each(tmp1, &cifs_tcp_ses_list) { list_for_each(tmp1, &cifs_tcp_ses_list) {
server = list_entry(tmp1, struct TCP_Server_Info, server = list_entry(tmp1, struct TCP_Server_Info,
tcp_ses_list); tcp_ses_list);
server->max_in_flight = 0;
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) {
atomic_set(&server->num_cmds[i], 0); atomic_set(&server->num_cmds[i], 0);
@ -526,6 +527,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
list_for_each(tmp1, &cifs_tcp_ses_list) { list_for_each(tmp1, &cifs_tcp_ses_list) {
server = list_entry(tmp1, struct TCP_Server_Info, server = list_entry(tmp1, struct TCP_Server_Info,
tcp_ses_list); tcp_ses_list);
seq_printf(m, "\nMax requests in flight: %d", server->max_in_flight);
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
seq_puts(m, "\nTotal time spent processing by command. Time "); seq_puts(m, "\nTotal time spent processing by command. Time ");
seq_printf(m, "units are jiffies (%d per second)\n", HZ); seq_printf(m, "units are jiffies (%d per second)\n", HZ);

View File

@ -80,6 +80,60 @@ do { \
type, fmt, ##__VA_ARGS__); \ type, fmt, ##__VA_ARGS__); \
} while (0) } while (0)
#define cifs_server_dbg_func(ratefunc, type, fmt, ...) \
do { \
const char *sn = ""; \
if (server && server->hostname) \
sn = server->hostname; \
if ((type) & FYI && cifsFYI & CIFS_INFO) { \
pr_debug_ ## ratefunc("%s: \\\\%s " fmt, \
__FILE__, sn, ##__VA_ARGS__); \
} else if ((type) & VFS) { \
pr_err_ ## ratefunc("CIFS VFS: \\\\%s " fmt, \
sn, ##__VA_ARGS__); \
} else if ((type) & NOISY && (NOISY != 0)) { \
pr_debug_ ## ratefunc("\\\\%s " fmt, \
sn, ##__VA_ARGS__); \
} \
} while (0)
#define cifs_server_dbg(type, fmt, ...) \
do { \
if ((type) & ONCE) \
cifs_server_dbg_func(once, \
type, fmt, ##__VA_ARGS__); \
else \
cifs_server_dbg_func(ratelimited, \
type, fmt, ##__VA_ARGS__); \
} while (0)
#define cifs_tcon_dbg_func(ratefunc, type, fmt, ...) \
do { \
const char *tn = ""; \
if (tcon && tcon->treeName) \
tn = tcon->treeName; \
if ((type) & FYI && cifsFYI & CIFS_INFO) { \
pr_debug_ ## ratefunc("%s: %s " fmt, \
__FILE__, tn, ##__VA_ARGS__); \
} else if ((type) & VFS) { \
pr_err_ ## ratefunc("CIFS VFS: %s " fmt, \
tn, ##__VA_ARGS__); \
} else if ((type) & NOISY && (NOISY != 0)) { \
pr_debug_ ## ratefunc("%s " fmt, \
tn, ##__VA_ARGS__); \
} \
} while (0)
#define cifs_tcon_dbg(type, fmt, ...) \
do { \
if ((type) & ONCE) \
cifs_tcon_dbg_func(once, \
type, fmt, ##__VA_ARGS__); \
else \
cifs_tcon_dbg_func(ratelimited, \
type, fmt, ##__VA_ARGS__); \
} while (0)
/* /*
* debug OFF * debug OFF
* --------- * ---------
@ -91,6 +145,19 @@ do { \
pr_debug(fmt, ##__VA_ARGS__); \ pr_debug(fmt, ##__VA_ARGS__); \
} while (0) } while (0)
#define cifs_server_dbg(type, fmt, ...) \
do { \
if (0) \
pr_debug("\\\\%s " fmt, \
server->hostname, ##__VA_ARGS__); \
} while (0)
#define cifs_tcon_dbg(type, fmt, ...) \
do { \
if (0) \
pr_debug("%s " fmt, tcon->treeName, ##__VA_ARGS__); \
} while (0)
#define cifs_info(fmt, ...) \ #define cifs_info(fmt, ...) \
do { \ do { \
pr_info("CIFS: "fmt, ##__VA_ARGS__); \ pr_info("CIFS: "fmt, ##__VA_ARGS__); \

View File

@ -53,6 +53,8 @@
#define CIFS_MOUNT_NO_HANDLE_CACHE 0x4000000 /* disable caching dir handles */ #define CIFS_MOUNT_NO_HANDLE_CACHE 0x4000000 /* disable caching dir handles */
#define CIFS_MOUNT_NO_DFS 0x8000000 /* disable DFS resolving */ #define CIFS_MOUNT_NO_DFS 0x8000000 /* disable DFS resolving */
#define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */ #define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */
#define CIFS_MOUNT_RO_CACHE 0x20000000 /* assumes share will not change */
#define CIFS_MOUNT_RW_CACHE 0x40000000 /* assumes only client accessing */
struct cifs_sb_info { struct cifs_sb_info {
struct rb_root tlink_tree; struct rb_root tlink_tree;

View File

@ -46,6 +46,7 @@ struct smb_snapshot_array {
/* query_info flags */ /* query_info flags */
#define PASSTHRU_QUERY_INFO 0x00000000 #define PASSTHRU_QUERY_INFO 0x00000000
#define PASSTHRU_FSCTL 0x00000001 #define PASSTHRU_FSCTL 0x00000001
#define PASSTHRU_SET_INFO 0x00000002
struct smb_query_info { struct smb_query_info {
__u32 info_type; __u32 info_type;
__u32 file_info_class; __u32 file_info_class;

View File

@ -701,10 +701,9 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
} }
#endif #endif
static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
struct cifs_fattr *fattr) struct cifs_fattr *fattr, bool mode_from_special_sid)
{ {
int i; int i;
int num_aces = 0; int num_aces = 0;
@ -757,22 +756,34 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
#ifdef CONFIG_CIFS_DEBUG2 #ifdef CONFIG_CIFS_DEBUG2
dump_ace(ppace[i], end_of_acl); dump_ace(ppace[i], end_of_acl);
#endif #endif
if (compare_sids(&(ppace[i]->sid), pownersid) == 0) if (mode_from_special_sid &&
(compare_sids(&(ppace[i]->sid),
&sid_unix_NFS_mode) == 0)) {
/*
* Full permissions are:
* 07777 = S_ISUID | S_ISGID | S_ISVTX |
* S_IRWXU | S_IRWXG | S_IRWXO
*/
fattr->cf_mode &= ~07777;
fattr->cf_mode |=
le32_to_cpu(ppace[i]->sid.sub_auth[2]);
break;
} else if (compare_sids(&(ppace[i]->sid), pownersid) == 0)
access_flags_to_mode(ppace[i]->access_req, access_flags_to_mode(ppace[i]->access_req,
ppace[i]->type, ppace[i]->type,
&fattr->cf_mode, &fattr->cf_mode,
&user_mask); &user_mask);
if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0) else if (compare_sids(&(ppace[i]->sid), pgrpsid) == 0)
access_flags_to_mode(ppace[i]->access_req, access_flags_to_mode(ppace[i]->access_req,
ppace[i]->type, ppace[i]->type,
&fattr->cf_mode, &fattr->cf_mode,
&group_mask); &group_mask);
if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0) else if (compare_sids(&(ppace[i]->sid), &sid_everyone) == 0)
access_flags_to_mode(ppace[i]->access_req, access_flags_to_mode(ppace[i]->access_req,
ppace[i]->type, ppace[i]->type,
&fattr->cf_mode, &fattr->cf_mode,
&other_mask); &other_mask);
if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0) else if (compare_sids(&(ppace[i]->sid), &sid_authusers) == 0)
access_flags_to_mode(ppace[i]->access_req, access_flags_to_mode(ppace[i]->access_req,
ppace[i]->type, ppace[i]->type,
&fattr->cf_mode, &fattr->cf_mode,
@ -795,22 +806,49 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid, static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
struct cifs_sid *pgrpsid, __u64 nmode) struct cifs_sid *pgrpsid, __u64 nmode, bool modefromsid)
{ {
u16 size = 0; u16 size = 0;
u32 num_aces = 0;
struct cifs_acl *pnndacl; struct cifs_acl *pnndacl;
pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl)); pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
if (modefromsid) {
struct cifs_ace *pntace =
(struct cifs_ace *)((char *)pnndacl + size);
int i;
pntace->type = ACCESS_ALLOWED;
pntace->flags = 0x0;
pntace->access_req = 0;
pntace->sid.num_subauth = 3;
pntace->sid.revision = 1;
for (i = 0; i < NUM_AUTHS; i++)
pntace->sid.authority[i] =
sid_unix_NFS_mode.authority[i];
pntace->sid.sub_auth[0] = sid_unix_NFS_mode.sub_auth[0];
pntace->sid.sub_auth[1] = sid_unix_NFS_mode.sub_auth[1];
pntace->sid.sub_auth[2] = cpu_to_le32(nmode & 07777);
/* size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth*4) */
pntace->size = cpu_to_le16(28);
size += 28;
num_aces++;
}
size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size), size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
pownersid, nmode, S_IRWXU); pownersid, nmode, S_IRWXU);
num_aces++;
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
pgrpsid, nmode, S_IRWXG); pgrpsid, nmode, S_IRWXG);
num_aces++;
size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
&sid_everyone, nmode, S_IRWXO); &sid_everyone, nmode, S_IRWXO);
num_aces++;
pndacl->num_aces = cpu_to_le32(num_aces);
pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl)); pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
pndacl->num_aces = cpu_to_le32(3);
return 0; return 0;
} }
@ -851,7 +889,8 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
/* Convert CIFS ACL to POSIX form */ /* Convert CIFS ACL to POSIX form */
static int parse_sec_desc(struct cifs_sb_info *cifs_sb, static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr) struct cifs_ntsd *pntsd, int acl_len, struct cifs_fattr *fattr,
bool get_mode_from_special_sid)
{ {
int rc = 0; int rc = 0;
struct cifs_sid *owner_sid_ptr, *group_sid_ptr; struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
@ -900,7 +939,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
if (dacloffset) if (dacloffset)
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
group_sid_ptr, fattr); group_sid_ptr, fattr, get_mode_from_special_sid);
else else
cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */ cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */
@ -909,7 +948,8 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
/* Convert permission bits from mode to equivalent CIFS ACL */ /* Convert permission bits from mode to equivalent CIFS ACL */
static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
__u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid, int *aclflag) __u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid,
bool mode_from_sid, int *aclflag)
{ {
int rc = 0; int rc = 0;
__u32 dacloffset; __u32 dacloffset;
@ -934,7 +974,7 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
ndacl_ptr->num_aces = 0; ndacl_ptr->num_aces = 0;
rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr,
nmode); nmode, mode_from_sid);
sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
/* copy sec desc control portion & owner and group sids */ /* copy sec desc control portion & owner and group sids */
copy_sec_desc(pntsd, pnntsd, sidsoffset); copy_sec_desc(pntsd, pnntsd, sidsoffset);
@ -1128,8 +1168,8 @@ out:
/* Translate the CIFS ACL (similar to NTFS ACL) for a file into mode bits */ /* Translate the CIFS ACL (similar to NTFS ACL) for a file into mode bits */
int int
cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
struct inode *inode, const char *path, struct inode *inode, bool mode_from_special_sid,
const struct cifs_fid *pfid) const char *path, const struct cifs_fid *pfid)
{ {
struct cifs_ntsd *pntsd = NULL; struct cifs_ntsd *pntsd = NULL;
u32 acllen = 0; u32 acllen = 0;
@ -1156,8 +1196,11 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
if (IS_ERR(pntsd)) { if (IS_ERR(pntsd)) {
rc = PTR_ERR(pntsd); rc = PTR_ERR(pntsd);
cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc);
} else if (mode_from_special_sid) {
rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, true);
} else { } else {
rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr); /* get approximated mode from ACL */
rc = parse_sec_desc(cifs_sb, pntsd, acllen, fattr, false);
kfree(pntsd); kfree(pntsd);
if (rc) if (rc)
cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc); cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc);
@ -1181,6 +1224,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
struct smb_version_operations *ops; struct smb_version_operations *ops;
bool mode_from_sid;
if (IS_ERR(tlink)) if (IS_ERR(tlink))
return PTR_ERR(tlink); return PTR_ERR(tlink);
@ -1218,8 +1262,13 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,
return -ENOMEM; return -ENOMEM;
} }
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)
mode_from_sid = true;
else
mode_from_sid = false;
rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid, rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
&aclflag); mode_from_sid, &aclflag);
cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc);

View File

@ -45,7 +45,7 @@
*/ */
#define DEFAULT_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + \ #define DEFAULT_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + \
sizeof(struct cifs_acl) + \ sizeof(struct cifs_acl) + \
(sizeof(struct cifs_ace) * 3)) (sizeof(struct cifs_ace) * 4))
/* /*
* Maximum size of a string representation of a SID: * Maximum size of a string representation of a SID:

View File

@ -118,6 +118,7 @@ extern mempool_t *cifs_req_poolp;
extern mempool_t *cifs_mid_poolp; extern mempool_t *cifs_mid_poolp;
struct workqueue_struct *cifsiod_wq; struct workqueue_struct *cifsiod_wq;
struct workqueue_struct *decrypt_wq;
struct workqueue_struct *cifsoplockd_wq; struct workqueue_struct *cifsoplockd_wq;
__u32 cifs_lock_secret; __u32 cifs_lock_secret;
@ -422,6 +423,10 @@ cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb)
seq_puts(s, "strict"); seq_puts(s, "strict");
else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO)
seq_puts(s, "none"); seq_puts(s, "none");
else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE)
seq_puts(s, "singleclient"); /* assume only one client access */
else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE)
seq_puts(s, "ro"); /* read only caching assumed */
else else
seq_puts(s, "loose"); seq_puts(s, "loose");
} }
@ -455,6 +460,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
cifs_show_security(s, tcon->ses); cifs_show_security(s, tcon->ses);
cifs_show_cache_flavor(s, cifs_sb); cifs_show_cache_flavor(s, cifs_sb);
if (tcon->no_lease)
seq_puts(s, ",nolease");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)
seq_puts(s, ",multiuser"); seq_puts(s, ",multiuser");
else if (tcon->ses->user_name) else if (tcon->ses->user_name)
@ -576,6 +583,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
seq_printf(s, ",rsize=%u", cifs_sb->rsize); seq_printf(s, ",rsize=%u", cifs_sb->rsize);
seq_printf(s, ",wsize=%u", cifs_sb->wsize); seq_printf(s, ",wsize=%u", cifs_sb->wsize);
seq_printf(s, ",bsize=%u", cifs_sb->bsize); seq_printf(s, ",bsize=%u", cifs_sb->bsize);
if (tcon->ses->server->min_offload)
seq_printf(s, ",esize=%u", tcon->ses->server->min_offload);
seq_printf(s, ",echo_interval=%lu", seq_printf(s, ",echo_interval=%lu",
tcon->ses->server->echo_interval / HZ); tcon->ses->server->echo_interval / HZ);
@ -1517,11 +1526,25 @@ init_cifs(void)
goto out_clean_proc; goto out_clean_proc;
} }
/*
* Consider in future setting limit!=0 maybe to min(num_of_cores - 1, 3)
* so that we don't launch too many worker threads but
* Documentation/workqueue.txt recommends setting it to 0
*/
/* WQ_UNBOUND allows decrypt tasks to run on any CPU */
decrypt_wq = alloc_workqueue("smb3decryptd",
WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
if (!decrypt_wq) {
rc = -ENOMEM;
goto out_destroy_cifsiod_wq;
}
cifsoplockd_wq = alloc_workqueue("cifsoplockd", cifsoplockd_wq = alloc_workqueue("cifsoplockd",
WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
if (!cifsoplockd_wq) { if (!cifsoplockd_wq) {
rc = -ENOMEM; rc = -ENOMEM;
goto out_destroy_cifsiod_wq; goto out_destroy_decrypt_wq;
} }
rc = cifs_fscache_register(); rc = cifs_fscache_register();
@ -1587,6 +1610,8 @@ out_unreg_fscache:
cifs_fscache_unregister(); cifs_fscache_unregister();
out_destroy_cifsoplockd_wq: out_destroy_cifsoplockd_wq:
destroy_workqueue(cifsoplockd_wq); destroy_workqueue(cifsoplockd_wq);
out_destroy_decrypt_wq:
destroy_workqueue(decrypt_wq);
out_destroy_cifsiod_wq: out_destroy_cifsiod_wq:
destroy_workqueue(cifsiod_wq); destroy_workqueue(cifsiod_wq);
out_clean_proc: out_clean_proc:
@ -1613,6 +1638,7 @@ exit_cifs(void)
cifs_destroy_inodecache(); cifs_destroy_inodecache();
cifs_fscache_unregister(); cifs_fscache_unregister();
destroy_workqueue(cifsoplockd_wq); destroy_workqueue(cifsoplockd_wq);
destroy_workqueue(decrypt_wq);
destroy_workqueue(cifsiod_wq); destroy_workqueue(cifsiod_wq);
cifs_proc_clean(); cifs_proc_clean();
} }

View File

@ -152,5 +152,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops; extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */ #endif /* CONFIG_CIFS_NFSD_EXPORT */
#define CIFS_VERSION "2.22" #define CIFS_VERSION "2.23"
#endif /* _CIFSFS_H */ #endif /* _CIFSFS_H */

View File

@ -542,6 +542,7 @@ struct smb_vol {
umode_t dir_mode; umode_t dir_mode;
enum securityEnum sectype; /* sectype requested via mnt opts */ enum securityEnum sectype; /* sectype requested via mnt opts */
bool sign; /* was signing requested via mnt opts? */ bool sign; /* was signing requested via mnt opts? */
bool ignore_signature:1;
bool retry:1; bool retry:1;
bool intr:1; bool intr:1;
bool setuids:1; bool setuids:1;
@ -559,6 +560,8 @@ struct smb_vol {
bool server_ino:1; /* use inode numbers from server ie UniqueId */ bool server_ino:1; /* use inode numbers from server ie UniqueId */
bool direct_io:1; bool direct_io:1;
bool strict_io:1; /* strict cache behavior */ bool strict_io:1; /* strict cache behavior */
bool cache_ro:1;
bool cache_rw:1;
bool remap:1; /* set to remap seven reserved chars in filenames */ bool remap:1; /* set to remap seven reserved chars in filenames */
bool sfu_remap:1; /* remap seven reserved chars ala SFU */ bool sfu_remap:1; /* remap seven reserved chars ala SFU */
bool posix_paths:1; /* unset to not ask for posix pathnames. */ bool posix_paths:1; /* unset to not ask for posix pathnames. */
@ -576,6 +579,7 @@ struct smb_vol {
bool noblocksnd:1; bool noblocksnd:1;
bool noautotune:1; bool noautotune:1;
bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ bool nostrictsync:1; /* do not force expensive SMBflush on every sync */
bool no_lease:1; /* disable requesting leases */
bool fsc:1; /* enable fscache */ bool fsc:1; /* enable fscache */
bool mfsymlinks:1; /* use Minshall+French Symlinks */ bool mfsymlinks:1; /* use Minshall+French Symlinks */
bool multiuser:1; bool multiuser:1;
@ -589,6 +593,7 @@ struct smb_vol {
unsigned int bsize; unsigned int bsize;
unsigned int rsize; unsigned int rsize;
unsigned int wsize; unsigned int wsize;
unsigned int min_offload;
bool sockopt_tcp_nodelay:1; bool sockopt_tcp_nodelay:1;
unsigned long actimeo; /* attribute cache timeout (jiffies) */ unsigned long actimeo; /* attribute cache timeout (jiffies) */
struct smb_version_operations *ops; struct smb_version_operations *ops;
@ -602,6 +607,7 @@ struct smb_vol {
__u32 handle_timeout; /* persistent and durable handle timeout in ms */ __u32 handle_timeout; /* persistent and durable handle timeout in ms */
unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
__u16 compression; /* compression algorithm 0xFFFF default 0=disabled */ __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
bool rootfs:1; /* if it's a SMB root file system */
}; };
/** /**
@ -620,7 +626,8 @@ struct smb_vol {
CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \ CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \
CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID | \ CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID | \
CIFS_MOUNT_UID_FROM_ACL | CIFS_MOUNT_NO_HANDLE_CACHE | \ CIFS_MOUNT_UID_FROM_ACL | CIFS_MOUNT_NO_HANDLE_CACHE | \
CIFS_MOUNT_NO_DFS | CIFS_MOUNT_MODE_FROM_SID) CIFS_MOUNT_NO_DFS | CIFS_MOUNT_MODE_FROM_SID | \
CIFS_MOUNT_RO_CACHE | CIFS_MOUNT_RW_CACHE)
/** /**
* Generic VFS superblock mount flags (s_flags) to consider when * Generic VFS superblock mount flags (s_flags) to consider when
@ -672,12 +679,14 @@ struct TCP_Server_Info {
unsigned int credits; /* send no more requests at once */ unsigned int credits; /* send no more requests at once */
unsigned int max_credits; /* can override large 32000 default at mnt */ unsigned int max_credits; /* can override large 32000 default at mnt */
unsigned int in_flight; /* number of requests on the wire to server */ unsigned int in_flight; /* number of requests on the wire to server */
unsigned int max_in_flight; /* max number of requests that were on wire */
spinlock_t req_lock; /* protect the two values above */ spinlock_t req_lock; /* protect the two values above */
struct mutex srv_mutex; struct mutex srv_mutex;
struct task_struct *tsk; struct task_struct *tsk;
char server_GUID[16]; char server_GUID[16];
__u16 sec_mode; __u16 sec_mode;
bool sign; /* is signing enabled on this connection? */ bool sign; /* is signing enabled on this connection? */
bool ignore_signature:1; /* skip validation of signatures in SMB2/3 rsp */
bool session_estab; /* mark when very first sess is established */ bool session_estab; /* mark when very first sess is established */
int echo_credits; /* echo reserved slots */ int echo_credits; /* echo reserved slots */
int oplock_credits; /* oplock break reserved slots */ int oplock_credits; /* oplock break reserved slots */
@ -740,6 +749,7 @@ struct TCP_Server_Info {
#endif /* STATS2 */ #endif /* STATS2 */
unsigned int max_read; unsigned int max_read;
unsigned int max_write; unsigned int max_write;
unsigned int min_offload;
__le16 compress_algorithm; __le16 compress_algorithm;
__le16 cipher_type; __le16 cipher_type;
/* save initital negprot hash */ /* save initital negprot hash */
@ -755,6 +765,7 @@ struct TCP_Server_Info {
* reconnect. * reconnect.
*/ */
int nr_targets; int nr_targets;
bool noblockcnt; /* use non-blocking connect() */
}; };
struct cifs_credits { struct cifs_credits {
@ -1082,6 +1093,7 @@ struct cifs_tcon {
bool need_reopen_files:1; /* need to reopen tcon file handles */ bool need_reopen_files:1; /* need to reopen tcon file handles */
bool use_resilient:1; /* use resilient instead of durable handles */ bool use_resilient:1; /* use resilient instead of durable handles */
bool use_persistent:1; /* use persistent instead of durable handles */ bool use_persistent:1; /* use persistent instead of durable handles */
bool no_lease:1; /* Do not request leases on files or directories */
__le32 capabilities; __le32 capabilities;
__u32 share_flags; __u32 share_flags;
__u32 maximal_access; __u32 maximal_access;
@ -1366,9 +1378,9 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
#define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG) #define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG)
#define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG) #define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG)
#define CIFS_CACHE_READ(cinode) (cinode->oplock & CIFS_CACHE_READ_FLG) #define CIFS_CACHE_READ(cinode) ((cinode->oplock & CIFS_CACHE_READ_FLG) || (CIFS_SB(cinode->vfs_inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE))
#define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG) #define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG)
#define CIFS_CACHE_WRITE(cinode) (cinode->oplock & CIFS_CACHE_WRITE_FLG) #define CIFS_CACHE_WRITE(cinode) ((cinode->oplock & CIFS_CACHE_WRITE_FLG) || (CIFS_SB(cinode->vfs_inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE))
/* /*
* One of these for each file inode * One of these for each file inode
@ -1887,6 +1899,7 @@ void cifs_queue_oplock_break(struct cifsFileInfo *cfile);
extern const struct slow_work_ops cifs_oplock_break_ops; extern const struct slow_work_ops cifs_oplock_break_ops;
extern struct workqueue_struct *cifsiod_wq; extern struct workqueue_struct *cifsiod_wq;
extern struct workqueue_struct *decrypt_wq;
extern struct workqueue_struct *cifsoplockd_wq; extern struct workqueue_struct *cifsoplockd_wq;
extern __u32 cifs_lock_secret; extern __u32 cifs_lock_secret;

View File

@ -137,7 +137,11 @@ extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
bool fsuid_only, bool fsuid_only,
struct cifsFileInfo **ret_file); struct cifsFileInfo **ret_file);
extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
struct cifsFileInfo **ret_file);
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
struct cifsFileInfo **ret_file);
extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server); extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server);
extern int decode_negTokenInit(unsigned char *security_blob, int length, extern int decode_negTokenInit(unsigned char *security_blob, int length,
struct TCP_Server_Info *server); struct TCP_Server_Info *server);
@ -197,6 +201,7 @@ extern int cifs_rename_pending_delete(const char *full_path,
const unsigned int xid); const unsigned int xid);
extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr, struct inode *inode, struct cifs_fattr *fattr, struct inode *inode,
bool get_mode_from_special_sid,
const char *path, const struct cifs_fid *pfid); const char *path, const struct cifs_fid *pfid);
extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64, extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64,
kuid_t, kgid_t); kuid_t, kgid_t);

94
fs/cifs/cifsroot.c Normal file
View File

@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0
/*
* SMB root file system support
*
* Copyright (c) 2019 Paulo Alcantara <palcantara@suse.de>
*/
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/root_dev.h>
#include <linux/kernel.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <net/ipconfig.h>
#define DEFAULT_MNT_OPTS \
"vers=1.0,cifsacl,mfsymlinks,rsize=1048576,wsize=65536,uid=0,gid=0," \
"hard,rootfs"
static char root_dev[2048] __initdata = "";
static char root_opts[1024] __initdata = DEFAULT_MNT_OPTS;
static __be32 __init parse_srvaddr(char *start, char *end)
{
/* TODO: ipv6 support */
char addr[sizeof("aaa.bbb.ccc.ddd")];
int i = 0;
while (start < end && i < sizeof(addr) - 1) {
if (isdigit(*start) || *start == '.')
addr[i++] = *start;
start++;
}
addr[i] = '\0';
return in_aton(addr);
}
/* cifsroot=//<server-ip>/<share>[,options] */
static int __init cifs_root_setup(char *line)
{
char *s;
int len;
__be32 srvaddr = htonl(INADDR_NONE);
ROOT_DEV = Root_CIFS;
if (strlen(line) > 3 && line[0] == '/' && line[1] == '/') {
s = strchr(&line[2], '/');
if (!s || s[1] == '\0')
return 1;
/* make s point to ',' or '\0' at end of line */
s = strchrnul(s, ',');
/* len is strlen(unc) + '\0' */
len = s - line + 1;
if (len > sizeof(root_dev)) {
printk(KERN_ERR "Root-CIFS: UNC path too long\n");
return 1;
}
strlcpy(root_dev, line, len);
srvaddr = parse_srvaddr(&line[2], s);
if (*s) {
int n = snprintf(root_opts,
sizeof(root_opts), "%s,%s",
DEFAULT_MNT_OPTS, s + 1);
if (n >= sizeof(root_opts)) {
printk(KERN_ERR "Root-CIFS: mount options string too long\n");
root_opts[sizeof(root_opts)-1] = '\0';
return 1;
}
}
}
root_server_addr = srvaddr;
return 1;
}
__setup("cifsroot=", cifs_root_setup);
int __init cifs_root_data(char **dev, char **opts)
{
if (!root_dev[0] || root_server_addr == htonl(INADDR_NONE)) {
printk(KERN_ERR "Root-CIFS: no SMB server address\n");
return -1;
}
*dev = root_dev;
*opts = root_opts;
return 0;
}

View File

@ -1393,7 +1393,7 @@ int
CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock, CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock,
FILE_ALL_INFO *buf) FILE_ALL_INFO *buf)
{ {
int rc = -EACCES; int rc;
OPEN_REQ *req = NULL; OPEN_REQ *req = NULL;
OPEN_RSP *rsp = NULL; OPEN_RSP *rsp = NULL;
int bytes_returned; int bytes_returned;

View File

@ -74,7 +74,7 @@ enum {
Opt_user_xattr, Opt_nouser_xattr, Opt_user_xattr, Opt_nouser_xattr,
Opt_forceuid, Opt_noforceuid, Opt_forceuid, Opt_noforceuid,
Opt_forcegid, Opt_noforcegid, Opt_forcegid, Opt_noforcegid,
Opt_noblocksend, Opt_noautotune, Opt_noblocksend, Opt_noautotune, Opt_nolease,
Opt_hard, Opt_soft, Opt_perm, Opt_noperm, Opt_hard, Opt_soft, Opt_perm, Opt_noperm,
Opt_mapposix, Opt_nomapposix, Opt_mapposix, Opt_nomapposix,
Opt_mapchars, Opt_nomapchars, Opt_sfu, Opt_mapchars, Opt_nomapchars, Opt_sfu,
@ -91,18 +91,19 @@ enum {
Opt_serverino, Opt_noserverino, Opt_serverino, Opt_noserverino,
Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl, Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl,
Opt_acl, Opt_noacl, Opt_locallease, Opt_acl, Opt_noacl, Opt_locallease,
Opt_sign, Opt_seal, Opt_noac, Opt_sign, Opt_ignore_signature, Opt_seal, Opt_noac,
Opt_fsc, Opt_mfsymlinks, Opt_fsc, Opt_mfsymlinks,
Opt_multiuser, Opt_sloppy, Opt_nosharesock, Opt_multiuser, Opt_sloppy, Opt_nosharesock,
Opt_persistent, Opt_nopersistent, Opt_persistent, Opt_nopersistent,
Opt_resilient, Opt_noresilient, Opt_resilient, Opt_noresilient,
Opt_domainauto, Opt_rdma, Opt_modesid, Opt_domainauto, Opt_rdma, Opt_modesid, Opt_rootfs,
Opt_compress, Opt_compress,
/* Mount options which take numeric value */ /* Mount options which take numeric value */
Opt_backupuid, Opt_backupgid, Opt_uid, Opt_backupuid, Opt_backupgid, Opt_uid,
Opt_cruid, Opt_gid, Opt_file_mode, Opt_cruid, Opt_gid, Opt_file_mode,
Opt_dirmode, Opt_port, Opt_dirmode, Opt_port,
Opt_min_enc_offload,
Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo, Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo,
Opt_echo_interval, Opt_max_credits, Opt_handletimeout, Opt_echo_interval, Opt_max_credits, Opt_handletimeout,
Opt_snapshot, Opt_snapshot,
@ -134,6 +135,7 @@ static const match_table_t cifs_mount_option_tokens = {
{ Opt_noforcegid, "noforcegid" }, { Opt_noforcegid, "noforcegid" },
{ Opt_noblocksend, "noblocksend" }, { Opt_noblocksend, "noblocksend" },
{ Opt_noautotune, "noautotune" }, { Opt_noautotune, "noautotune" },
{ Opt_nolease, "nolease" },
{ Opt_hard, "hard" }, { Opt_hard, "hard" },
{ Opt_soft, "soft" }, { Opt_soft, "soft" },
{ Opt_perm, "perm" }, { Opt_perm, "perm" },
@ -183,6 +185,7 @@ static const match_table_t cifs_mount_option_tokens = {
{ Opt_noacl, "noacl" }, { Opt_noacl, "noacl" },
{ Opt_locallease, "locallease" }, { Opt_locallease, "locallease" },
{ Opt_sign, "sign" }, { Opt_sign, "sign" },
{ Opt_ignore_signature, "signloosely" },
{ Opt_seal, "seal" }, { Opt_seal, "seal" },
{ Opt_noac, "noac" }, { Opt_noac, "noac" },
{ Opt_fsc, "fsc" }, { Opt_fsc, "fsc" },
@ -206,6 +209,7 @@ static const match_table_t cifs_mount_option_tokens = {
{ Opt_dirmode, "dirmode=%s" }, { Opt_dirmode, "dirmode=%s" },
{ Opt_dirmode, "dir_mode=%s" }, { Opt_dirmode, "dir_mode=%s" },
{ Opt_port, "port=%s" }, { Opt_port, "port=%s" },
{ Opt_min_enc_offload, "esize=%s" },
{ Opt_blocksize, "bsize=%s" }, { Opt_blocksize, "bsize=%s" },
{ Opt_rsize, "rsize=%s" }, { Opt_rsize, "rsize=%s" },
{ Opt_wsize, "wsize=%s" }, { Opt_wsize, "wsize=%s" },
@ -262,6 +266,7 @@ static const match_table_t cifs_mount_option_tokens = {
{ Opt_ignore, "nomand" }, { Opt_ignore, "nomand" },
{ Opt_ignore, "relatime" }, { Opt_ignore, "relatime" },
{ Opt_ignore, "_netdev" }, { Opt_ignore, "_netdev" },
{ Opt_rootfs, "rootfs" },
{ Opt_err, NULL } { Opt_err, NULL }
}; };
@ -298,6 +303,8 @@ enum {
Opt_cache_loose, Opt_cache_loose,
Opt_cache_strict, Opt_cache_strict,
Opt_cache_none, Opt_cache_none,
Opt_cache_ro,
Opt_cache_rw,
Opt_cache_err Opt_cache_err
}; };
@ -305,6 +312,8 @@ static const match_table_t cifs_cacheflavor_tokens = {
{ Opt_cache_loose, "loose" }, { Opt_cache_loose, "loose" },
{ Opt_cache_strict, "strict" }, { Opt_cache_strict, "strict" },
{ Opt_cache_none, "none" }, { Opt_cache_none, "none" },
{ Opt_cache_ro, "ro" },
{ Opt_cache_rw, "singleclient" },
{ Opt_cache_err, NULL } { Opt_cache_err, NULL }
}; };
@ -489,7 +498,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
} else { } else {
rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list, &tgt_it); rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list, &tgt_it);
if (rc && (rc != -EOPNOTSUPP)) { if (rc && (rc != -EOPNOTSUPP)) {
cifs_dbg(VFS, "%s: no target servers for DFS failover\n", cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n",
__func__); __func__);
} else { } else {
server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list); server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list);
@ -617,12 +626,12 @@ cifs_reconnect(struct TCP_Server_Info *server)
rc = dfs_cache_noreq_update_tgthint(cifs_sb->origin_fullpath + 1, rc = dfs_cache_noreq_update_tgthint(cifs_sb->origin_fullpath + 1,
tgt_it); tgt_it);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: failed to update DFS target hint: rc = %d\n", cifs_server_dbg(VFS, "%s: failed to update DFS target hint: rc = %d\n",
__func__, rc); __func__, rc);
} }
rc = dfs_cache_update_vol(cifs_sb->origin_fullpath, server); rc = dfs_cache_update_vol(cifs_sb->origin_fullpath, server);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: failed to update vol info in DFS cache: rc = %d\n", cifs_server_dbg(VFS, "%s: failed to update vol info in DFS cache: rc = %d\n",
__func__, rc); __func__, rc);
} }
dfs_cache_free_tgts(&tgt_list); dfs_cache_free_tgts(&tgt_list);
@ -678,7 +687,7 @@ allocate_buffers(struct TCP_Server_Info *server)
if (!server->bigbuf) { if (!server->bigbuf) {
server->bigbuf = (char *)cifs_buf_get(); server->bigbuf = (char *)cifs_buf_get();
if (!server->bigbuf) { if (!server->bigbuf) {
cifs_dbg(VFS, "No memory for large SMB response\n"); cifs_server_dbg(VFS, "No memory for large SMB response\n");
msleep(3000); msleep(3000);
/* retry will check if exiting */ /* retry will check if exiting */
return false; return false;
@ -691,7 +700,7 @@ allocate_buffers(struct TCP_Server_Info *server)
if (!server->smallbuf) { if (!server->smallbuf) {
server->smallbuf = (char *)cifs_small_buf_get(); server->smallbuf = (char *)cifs_small_buf_get();
if (!server->smallbuf) { if (!server->smallbuf) {
cifs_dbg(VFS, "No memory for SMB response\n"); cifs_server_dbg(VFS, "No memory for SMB response\n");
msleep(1000); msleep(1000);
/* retry will check if exiting */ /* retry will check if exiting */
return false; return false;
@ -712,7 +721,7 @@ server_unresponsive(struct TCP_Server_Info *server)
* We need to wait 3 echo intervals to make sure we handle such * We need to wait 3 echo intervals to make sure we handle such
* situations right: * situations right:
* 1s client sends a normal SMB request * 1s client sends a normal SMB request
* 3s client gets a response * 2s client gets a response
* 30s echo workqueue job pops, and decides we got a response recently * 30s echo workqueue job pops, and decides we got a response recently
* and don't need to send another * and don't need to send another
* ... * ...
@ -722,8 +731,8 @@ server_unresponsive(struct TCP_Server_Info *server)
if ((server->tcpStatus == CifsGood || if ((server->tcpStatus == CifsGood ||
server->tcpStatus == CifsNeedNegotiate) && server->tcpStatus == CifsNeedNegotiate) &&
time_after(jiffies, server->lstrp + 3 * server->echo_interval)) { time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
cifs_dbg(VFS, "Server %s has not responded in %lu seconds. Reconnecting...\n", cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n",
server->hostname, (3 * server->echo_interval) / HZ); (3 * server->echo_interval) / HZ);
cifs_reconnect(server); cifs_reconnect(server);
wake_up(&server->response_q); wake_up(&server->response_q);
return true; return true;
@ -861,7 +870,7 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
wake_up(&server->response_q); wake_up(&server->response_q);
break; break;
default: default:
cifs_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type); cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type);
cifs_reconnect(server); cifs_reconnect(server);
} }
@ -1008,7 +1017,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
/* make sure this will fit in a large buffer */ /* make sure this will fit in a large buffer */
if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
server->vals->header_preamble_size) { server->vals->header_preamble_size) {
cifs_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length); cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
cifs_reconnect(server); cifs_reconnect(server);
wake_up(&server->response_q); wake_up(&server->response_q);
return -ECONNABORTED; return -ECONNABORTED;
@ -1149,7 +1158,7 @@ next_pdu:
/* make sure we have enough to get to the MID */ /* make sure we have enough to get to the MID */
if (server->pdu_size < HEADER_SIZE(server) - 1 - if (server->pdu_size < HEADER_SIZE(server) - 1 -
server->vals->header_preamble_size) { server->vals->header_preamble_size) {
cifs_dbg(VFS, "SMB response too short (%u bytes)\n", cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n",
server->pdu_size); server->pdu_size);
cifs_reconnect(server); cifs_reconnect(server);
wake_up(&server->response_q); wake_up(&server->response_q);
@ -1222,7 +1231,7 @@ next_pdu:
smb2_add_credits_from_hdr(bufs[i], server); smb2_add_credits_from_hdr(bufs[i], server);
cifs_dbg(FYI, "Received oplock break\n"); cifs_dbg(FYI, "Received oplock break\n");
} else { } else {
cifs_dbg(VFS, "No task to wake, unknown frame " cifs_server_dbg(VFS, "No task to wake, unknown frame "
"received! NumMids %d\n", "received! NumMids %d\n",
atomic_read(&midCount)); atomic_read(&midCount));
cifs_dump_mem("Received Data is: ", bufs[i], cifs_dump_mem("Received Data is: ", bufs[i],
@ -1418,14 +1427,32 @@ cifs_parse_cache_flavor(char *value, struct smb_vol *vol)
case Opt_cache_loose: case Opt_cache_loose:
vol->direct_io = false; vol->direct_io = false;
vol->strict_io = false; vol->strict_io = false;
vol->cache_ro = false;
vol->cache_rw = false;
break; break;
case Opt_cache_strict: case Opt_cache_strict:
vol->direct_io = false; vol->direct_io = false;
vol->strict_io = true; vol->strict_io = true;
vol->cache_ro = false;
vol->cache_rw = false;
break; break;
case Opt_cache_none: case Opt_cache_none:
vol->direct_io = true; vol->direct_io = true;
vol->strict_io = false; vol->strict_io = false;
vol->cache_ro = false;
vol->cache_rw = false;
break;
case Opt_cache_ro:
vol->direct_io = false;
vol->strict_io = false;
vol->cache_ro = true;
vol->cache_rw = false;
break;
case Opt_cache_rw:
vol->direct_io = false;
vol->strict_io = false;
vol->cache_ro = false;
vol->cache_rw = true;
break; break;
default: default:
cifs_dbg(VFS, "bad cache= option: %s\n", value); cifs_dbg(VFS, "bad cache= option: %s\n", value);
@ -1713,6 +1740,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
case Opt_noautotune: case Opt_noautotune:
vol->noautotune = 1; vol->noautotune = 1;
break; break;
case Opt_nolease:
vol->no_lease = 1;
break;
case Opt_hard: case Opt_hard:
vol->retry = 1; vol->retry = 1;
break; break;
@ -1748,6 +1778,11 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
case Opt_nodfs: case Opt_nodfs:
vol->nodfs = 1; vol->nodfs = 1;
break; break;
case Opt_rootfs:
#ifdef CONFIG_CIFS_ROOT
vol->rootfs = true;
#endif
break;
case Opt_posixpaths: case Opt_posixpaths:
vol->posix_paths = 1; vol->posix_paths = 1;
break; break;
@ -1855,6 +1890,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
case Opt_sign: case Opt_sign:
vol->sign = true; vol->sign = true;
break; break;
case Opt_ignore_signature:
vol->sign = true;
vol->ignore_signature = true;
break;
case Opt_seal: case Opt_seal:
/* we do not do the following in secFlags because seal /* we do not do the following in secFlags because seal
* is a per tree connection (mount) not a per socket * is a per tree connection (mount) not a per socket
@ -1989,6 +2028,13 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
} }
port = (unsigned short)option; port = (unsigned short)option;
break; break;
case Opt_min_enc_offload:
if (get_option_ul(args, &option)) {
cifs_dbg(VFS, "Invalid minimum encrypted read offload size (esize)\n");
goto cifs_parse_mount_err;
}
vol->min_offload = option;
break;
case Opt_blocksize: case Opt_blocksize:
if (get_option_ul(args, &option)) { if (get_option_ul(args, &option)) {
cifs_dbg(VFS, "%s: Invalid blocksize value\n", cifs_dbg(VFS, "%s: Invalid blocksize value\n",
@ -2586,6 +2632,12 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol)
if (server->rdma != vol->rdma) if (server->rdma != vol->rdma)
return 0; return 0;
if (server->ignore_signature != vol->ignore_signature)
return 0;
if (server->min_offload != vol->min_offload)
return 0;
return 1; return 1;
} }
@ -2681,11 +2733,13 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
goto out_err_crypto_release; goto out_err_crypto_release;
} }
tcp_ses->noblocksnd = volume_info->noblocksnd; tcp_ses->noblockcnt = volume_info->rootfs;
tcp_ses->noblocksnd = volume_info->noblocksnd || volume_info->rootfs;
tcp_ses->noautotune = volume_info->noautotune; tcp_ses->noautotune = volume_info->noautotune;
tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay; tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay;
tcp_ses->rdma = volume_info->rdma; tcp_ses->rdma = volume_info->rdma;
tcp_ses->in_flight = 0; tcp_ses->in_flight = 0;
tcp_ses->max_in_flight = 0;
tcp_ses->credits = 1; tcp_ses->credits = 1;
init_waitqueue_head(&tcp_ses->response_q); init_waitqueue_head(&tcp_ses->response_q);
init_waitqueue_head(&tcp_ses->request_q); init_waitqueue_head(&tcp_ses->request_q);
@ -2760,10 +2814,11 @@ smbd_connected:
module_put(THIS_MODULE); module_put(THIS_MODULE);
goto out_err_crypto_release; goto out_err_crypto_release;
} }
tcp_ses->min_offload = volume_info->min_offload;
tcp_ses->tcpStatus = CifsNeedNegotiate; tcp_ses->tcpStatus = CifsNeedNegotiate;
tcp_ses->nr_targets = 1; tcp_ses->nr_targets = 1;
tcp_ses->ignore_signature = volume_info->ignore_signature;
/* thread spawned, put it on the list */ /* thread spawned, put it on the list */
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);
@ -2840,16 +2895,17 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info)
struct nls_table *nls_codepage; struct nls_table *nls_codepage;
char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0}; char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0};
bool seal = false; bool seal = false;
struct TCP_Server_Info *server = ses->server;
/* /*
* If the mount request that resulted in the creation of the * If the mount request that resulted in the creation of the
* session requires encryption, force IPC to be encrypted too. * session requires encryption, force IPC to be encrypted too.
*/ */
if (volume_info->seal) { if (volume_info->seal) {
if (ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)
seal = true; seal = true;
else { else {
cifs_dbg(VFS, cifs_server_dbg(VFS,
"IPC: server doesn't support encryption\n"); "IPC: server doesn't support encryption\n");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
@ -2859,7 +2915,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info)
if (tcon == NULL) if (tcon == NULL)
return -ENOMEM; return -ENOMEM;
scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", ses->server->hostname); scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);
/* cannot fail */ /* cannot fail */
nls_codepage = load_nls_default(); nls_codepage = load_nls_default();
@ -2868,11 +2924,11 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info)
tcon->ses = ses; tcon->ses = ses;
tcon->ipc = true; tcon->ipc = true;
tcon->seal = seal; tcon->seal = seal;
rc = ses->server->ops->tree_connect(xid, ses, unc, tcon, nls_codepage); rc = server->ops->tree_connect(xid, ses, unc, tcon, nls_codepage);
free_xid(xid); free_xid(xid);
if (rc) { if (rc) {
cifs_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc); cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
tconInfoFree(tcon); tconInfoFree(tcon);
goto out; goto out;
} }
@ -2958,7 +3014,7 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
xid = get_xid(); xid = get_xid();
rc = server->ops->logoff(xid, ses); rc = server->ops->logoff(xid, ses);
if (rc) if (rc)
cifs_dbg(VFS, "%s: Session Logoff failure rc=%d\n", cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n",
__func__, rc); __func__, rc);
_free_xid(xid); _free_xid(xid);
} }
@ -3212,7 +3268,6 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
ses->sectype = volume_info->sectype; ses->sectype = volume_info->sectype;
ses->sign = volume_info->sign; ses->sign = volume_info->sign;
mutex_lock(&ses->session_mutex); mutex_lock(&ses->session_mutex);
rc = cifs_negotiate_protocol(xid, ses); rc = cifs_negotiate_protocol(xid, ses);
if (!rc) if (!rc)
@ -3250,6 +3305,8 @@ static int match_tcon(struct cifs_tcon *tcon, struct smb_vol *volume_info)
return 0; return 0;
if (tcon->handle_timeout != volume_info->handle_timeout) if (tcon->handle_timeout != volume_info->handle_timeout)
return 0; return 0;
if (tcon->no_lease != volume_info->no_lease)
return 0;
return 1; return 1;
} }
@ -3455,6 +3512,14 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
tcon->use_resilient = true; tcon->use_resilient = true;
} }
/* If the user really knows what they are doing they can override */
if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) {
if (volume_info->cache_ro)
cifs_dbg(VFS, "cache=ro requested on mount but NO_CACHING flag set on share\n");
else if (volume_info->cache_rw)
cifs_dbg(VFS, "cache=singleclient requested on mount but NO_CACHING flag set on share\n");
}
/* /*
* We can have only one retry value for a connection to a share so for * We can have only one retry value for a connection to a share so for
* resources mounted more than once to the same server share the last * resources mounted more than once to the same server share the last
@ -3464,6 +3529,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
tcon->nocase = volume_info->nocase; tcon->nocase = volume_info->nocase;
tcon->nohandlecache = volume_info->nohandlecache; tcon->nohandlecache = volume_info->nohandlecache;
tcon->local_lease = volume_info->local_lease; tcon->local_lease = volume_info->local_lease;
tcon->no_lease = volume_info->no_lease;
INIT_LIST_HEAD(&tcon->pending_opens); INIT_LIST_HEAD(&tcon->pending_opens);
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
@ -3659,10 +3725,10 @@ bind_socket(struct TCP_Server_Info *server)
saddr4 = (struct sockaddr_in *)&server->srcaddr; saddr4 = (struct sockaddr_in *)&server->srcaddr;
saddr6 = (struct sockaddr_in6 *)&server->srcaddr; saddr6 = (struct sockaddr_in6 *)&server->srcaddr;
if (saddr6->sin6_family == AF_INET6) if (saddr6->sin6_family == AF_INET6)
cifs_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n", cifs_server_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n",
&saddr6->sin6_addr, rc); &saddr6->sin6_addr, rc);
else else
cifs_dbg(VFS, "Failed to bind to: %pI4, error: %d\n", cifs_server_dbg(VFS, "Failed to bind to: %pI4, error: %d\n",
&saddr4->sin_addr.s_addr, rc); &saddr4->sin_addr.s_addr, rc);
} }
} }
@ -3766,7 +3832,7 @@ generic_ip_connect(struct TCP_Server_Info *server)
rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM, rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM,
IPPROTO_TCP, &socket, 1); IPPROTO_TCP, &socket, 1);
if (rc < 0) { if (rc < 0) {
cifs_dbg(VFS, "Error %d creating socket\n", rc); cifs_server_dbg(VFS, "Error %d creating socket\n", rc);
server->ssocket = NULL; server->ssocket = NULL;
return rc; return rc;
} }
@ -3814,7 +3880,11 @@ generic_ip_connect(struct TCP_Server_Info *server)
socket->sk->sk_sndbuf, socket->sk->sk_sndbuf,
socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo); socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo);
rc = socket->ops->connect(socket, saddr, slen, 0); rc = socket->ops->connect(socket, saddr, slen,
server->noblockcnt ? O_NONBLOCK : 0);
if (rc == -EINPROGRESS)
rc = 0;
if (rc < 0) { if (rc < 0) {
cifs_dbg(FYI, "Error %d connecting to server\n", rc); cifs_dbg(FYI, "Error %d connecting to server\n", rc);
sock_release(socket); sock_release(socket);
@ -4040,6 +4110,14 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
cifs_dbg(FYI, "mounting share using direct i/o\n"); cifs_dbg(FYI, "mounting share using direct i/o\n");
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
} }
if (pvolume_info->cache_ro) {
cifs_dbg(VFS, "mounting share with read only caching. Ensure that the share will not be modified while in use.\n");
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RO_CACHE;
} else if (pvolume_info->cache_rw) {
cifs_dbg(VFS, "mounting share in single client RW caching mode. Ensure that no other systems will be accessing the share.\n");
cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_RO_CACHE |
CIFS_MOUNT_RW_CACHE);
}
if (pvolume_info->mfsymlinks) { if (pvolume_info->mfsymlinks) {
if (pvolume_info->sfu_emul) { if (pvolume_info->sfu_emul) {
/* /*
@ -4150,7 +4228,7 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
if ((vol->persistent == true) && (!(ses->server->capabilities & if ((vol->persistent == true) && (!(ses->server->capabilities &
SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) { SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) {
cifs_dbg(VFS, "persistent handles not supported by server\n"); cifs_server_dbg(VFS, "persistent handles not supported by server\n");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
@ -4182,8 +4260,18 @@ static int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
tcon->unix_ext = 0; /* server does not support them */ tcon->unix_ext = 0; /* server does not support them */
/* do not care if a following call succeed - informational */ /* do not care if a following call succeed - informational */
if (!tcon->pipe && server->ops->qfs_tcon) if (!tcon->pipe && server->ops->qfs_tcon) {
server->ops->qfs_tcon(*xid, tcon); server->ops->qfs_tcon(*xid, tcon);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) {
if (tcon->fsDevInfo.DeviceCharacteristics &
FILE_READ_ONLY_DEVICE)
cifs_dbg(VFS, "mounted to read only share\n");
else if ((cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_RW_CACHE) == 0)
cifs_dbg(VFS, "read only mount of RW share\n");
/* no need to log a RW mount of a typical RW share */
}
}
cifs_sb->wsize = server->ops->negotiate_wsize(tcon, vol); cifs_sb->wsize = server->ops->negotiate_wsize(tcon, vol);
cifs_sb->rsize = server->ops->negotiate_rsize(tcon, vol); cifs_sb->rsize = server->ops->negotiate_rsize(tcon, vol);
@ -4588,7 +4676,7 @@ static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb_vol *vol,
rc = cifs_are_all_path_components_accessible(server, xid, tcon, rc = cifs_are_all_path_components_accessible(server, xid, tcon,
cifs_sb, full_path, tcon->Flags & SMB_SHARE_IS_IN_DFS); cifs_sb, full_path, tcon->Flags & SMB_SHARE_IS_IN_DFS);
if (rc != 0) { if (rc != 0) {
cifs_dbg(VFS, "cannot query dirs between root and final path, " cifs_server_dbg(VFS, "cannot query dirs between root and final path, "
"enabling CIFS_MOUNT_USE_PREFIX_PATH\n"); "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
rc = 0; rc = 0;
@ -5090,7 +5178,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
rc = server->ops->sess_setup(xid, ses, nls_info); rc = server->ops->sess_setup(xid, ses, nls_info);
if (rc) if (rc)
cifs_dbg(VFS, "Send error in SessSetup = %d\n", rc); cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc);
return rc; return rc;
} }

View File

@ -125,7 +125,7 @@ cifs_bp_rename_retry:
} }
rcu_read_unlock(); rcu_read_unlock();
full_path = kmalloc(namelen+1, GFP_KERNEL); full_path = kmalloc(namelen+1, GFP_ATOMIC);
if (full_path == NULL) if (full_path == NULL)
return full_path; return full_path;
full_path[namelen] = 0; /* trailing null */ full_path[namelen] = 0; /* trailing null */

View File

@ -1693,9 +1693,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
bool posix_lck = false; bool posix_lck = false;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct cifsInodeInfo *cinode;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
__u16 netfid;
__u32 type; __u32 type;
rc = -EACCES; rc = -EACCES;
@ -1711,8 +1709,6 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag, cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag,
tcon->ses->server); tcon->ses->server);
cifs_sb = CIFS_FILE_SB(file); cifs_sb = CIFS_FILE_SB(file);
netfid = cfile->fid.netfid;
cinode = CIFS_I(file_inode(file));
if (cap_unix(tcon->ses) && if (cap_unix(tcon->ses) &&
(CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
@ -1764,7 +1760,6 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data,
int rc = 0; int rc = 0;
unsigned int bytes_written = 0; unsigned int bytes_written = 0;
unsigned int total_written; unsigned int total_written;
struct cifs_sb_info *cifs_sb;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
unsigned int xid; unsigned int xid;
@ -1772,8 +1767,6 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data,
struct cifsInodeInfo *cifsi = CIFS_I(d_inode(dentry)); struct cifsInodeInfo *cifsi = CIFS_I(d_inode(dentry));
struct cifs_io_parms io_parms; struct cifs_io_parms io_parms;
cifs_sb = CIFS_SB(dentry->d_sb);
cifs_dbg(FYI, "write %zd bytes to offset %lld of %pd\n", cifs_dbg(FYI, "write %zd bytes to offset %lld of %pd\n",
write_size, *offset, dentry); write_size, *offset, dentry);
@ -1980,6 +1973,77 @@ find_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only)
return cfile; return cfile;
} }
int
cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
struct cifsFileInfo **ret_file)
{
struct list_head *tmp;
struct cifsFileInfo *cfile;
struct cifsInodeInfo *cinode;
char *full_path;
*ret_file = NULL;
spin_lock(&tcon->open_file_lock);
list_for_each(tmp, &tcon->openFileList) {
cfile = list_entry(tmp, struct cifsFileInfo,
tlist);
full_path = build_path_from_dentry(cfile->dentry);
if (full_path == NULL) {
spin_unlock(&tcon->open_file_lock);
return -ENOMEM;
}
if (strcmp(full_path, name)) {
kfree(full_path);
continue;
}
kfree(full_path);
cinode = CIFS_I(d_inode(cfile->dentry));
spin_unlock(&tcon->open_file_lock);
return cifs_get_writable_file(cinode, 0, ret_file);
}
spin_unlock(&tcon->open_file_lock);
return -ENOENT;
}
int
cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
struct cifsFileInfo **ret_file)
{
struct list_head *tmp;
struct cifsFileInfo *cfile;
struct cifsInodeInfo *cinode;
char *full_path;
*ret_file = NULL;
spin_lock(&tcon->open_file_lock);
list_for_each(tmp, &tcon->openFileList) {
cfile = list_entry(tmp, struct cifsFileInfo,
tlist);
full_path = build_path_from_dentry(cfile->dentry);
if (full_path == NULL) {
spin_unlock(&tcon->open_file_lock);
return -ENOMEM;
}
if (strcmp(full_path, name)) {
kfree(full_path);
continue;
}
kfree(full_path);
cinode = CIFS_I(d_inode(cfile->dentry));
spin_unlock(&tcon->open_file_lock);
*ret_file = find_readable_file(cinode, 0);
return *ret_file ? 0 : -ENOENT;
}
spin_unlock(&tcon->open_file_lock);
return -ENOENT;
}
static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
{ {
struct address_space *mapping = page->mapping; struct address_space *mapping = page->mapping;
@ -3577,10 +3641,8 @@ collect_uncached_read_data(struct cifs_aio_ctx *ctx)
struct cifs_readdata *rdata, *tmp; struct cifs_readdata *rdata, *tmp;
struct iov_iter *to = &ctx->iter; struct iov_iter *to = &ctx->iter;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifs_tcon *tcon;
int rc; int rc;
tcon = tlink_tcon(ctx->cfile->tlink);
cifs_sb = CIFS_SB(ctx->cfile->dentry->d_sb); cifs_sb = CIFS_SB(ctx->cfile->dentry->d_sb);
mutex_lock(&ctx->aio_mutex); mutex_lock(&ctx->aio_mutex);

View File

@ -893,8 +893,17 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
} }
/* fill in 0777 bits from ACL */ /* fill in 0777 bits from ACL */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) {
rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, full_path, fid); rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, true,
full_path, fid);
if (rc) {
cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n",
__func__, rc);
goto cgii_exit;
}
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, false,
full_path, fid);
if (rc) { if (rc) {
cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n", cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n",
__func__, rc); __func__, rc);
@ -2480,7 +2489,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
if (attrs->ia_valid & ATTR_GID) if (attrs->ia_valid & ATTR_GID)
gid = attrs->ia_gid; gid = attrs->ia_gid;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) ||
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) {
if (uid_valid(uid) || gid_valid(gid)) { if (uid_valid(uid) || gid_valid(gid)) {
rc = id_mode_to_cifs_acl(inode, full_path, NO_CHANGE_64, rc = id_mode_to_cifs_acl(inode, full_path, NO_CHANGE_64,
uid, gid); uid, gid);
@ -2501,7 +2511,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
if (attrs->ia_valid & ATTR_MODE) { if (attrs->ia_valid & ATTR_MODE) {
mode = attrs->ia_mode; mode = attrs->ia_mode;
rc = 0; rc = 0;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) ||
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) {
rc = id_mode_to_cifs_acl(inode, full_path, mode, rc = id_mode_to_cifs_acl(inode, full_path, mode,
INVALID_UID, INVALID_GID); INVALID_UID, INVALID_GID);
if (rc) { if (rc) {

View File

@ -51,7 +51,8 @@ static int
smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
__u32 desired_access, __u32 create_disposition, __u32 desired_access, __u32 create_disposition,
__u32 create_options, void *ptr, int command) __u32 create_options, void *ptr, int command,
struct cifsFileInfo *cfile)
{ {
int rc; int rc;
__le16 *utf16_path = NULL; __le16 *utf16_path = NULL;
@ -83,10 +84,16 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
memset(rsp_iov, 0, sizeof(rsp_iov)); memset(rsp_iov, 0, sizeof(rsp_iov));
/* We already have a handle so we can skip the open */
if (cfile)
goto after_open;
/* Open */ /* Open */
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path) if (!utf16_path) {
return -ENOMEM; rc = -ENOMEM;
goto finished;
}
oparms.tcon = tcon; oparms.tcon = tcon;
oparms.desired_access = desired_access; oparms.desired_access = desired_access;
@ -106,7 +113,10 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
if (rc) if (rc)
goto finished; goto finished;
smb2_set_next_command(tcon, &rqst[num_rqst++]); smb2_set_next_command(tcon, &rqst[num_rqst]);
after_open:
num_rqst++;
rc = 0;
/* Operation */ /* Operation */
switch (command) { switch (command) {
@ -115,15 +125,31 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
rqst[num_rqst].rq_iov = qi_iov; rqst[num_rqst].rq_iov = qi_iov;
rqst[num_rqst].rq_nvec = 1; rqst[num_rqst].rq_nvec = 1;
rc = SMB2_query_info_init(tcon, &rqst[num_rqst], COMPOUND_FID, if (cfile)
COMPOUND_FID, FILE_ALL_INFORMATION, rc = SMB2_query_info_init(tcon, &rqst[num_rqst],
cfile->fid.persistent_fid,
cfile->fid.volatile_fid,
FILE_ALL_INFORMATION,
SMB2_O_INFO_FILE, 0, SMB2_O_INFO_FILE, 0,
sizeof(struct smb2_file_all_info) + sizeof(struct smb2_file_all_info) +
PATH_MAX * 2, 0, NULL); PATH_MAX * 2, 0, NULL);
else {
rc = SMB2_query_info_init(tcon, &rqst[num_rqst],
COMPOUND_FID,
COMPOUND_FID,
FILE_ALL_INFORMATION,
SMB2_O_INFO_FILE, 0,
sizeof(struct smb2_file_all_info) +
PATH_MAX * 2, 0, NULL);
if (!rc) {
smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst]);
}
}
if (rc) if (rc)
goto finished; goto finished;
smb2_set_next_command(tcon, &rqst[num_rqst]); num_rqst++;
smb2_set_related(&rqst[num_rqst++]);
trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid, trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid,
full_path); full_path);
break; break;
@ -182,14 +208,27 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
size[0] = sizeof(FILE_BASIC_INFO); size[0] = sizeof(FILE_BASIC_INFO);
data[0] = ptr; data[0] = ptr;
rc = SMB2_set_info_init(tcon, &rqst[num_rqst], COMPOUND_FID, if (cfile)
COMPOUND_FID, current->tgid, rc = SMB2_set_info_init(tcon, &rqst[num_rqst],
FILE_BASIC_INFORMATION, cfile->fid.persistent_fid,
SMB2_O_INFO_FILE, 0, data, size); cfile->fid.volatile_fid, current->tgid,
FILE_BASIC_INFORMATION,
SMB2_O_INFO_FILE, 0, data, size);
else {
rc = SMB2_set_info_init(tcon, &rqst[num_rqst],
COMPOUND_FID,
COMPOUND_FID, current->tgid,
FILE_BASIC_INFORMATION,
SMB2_O_INFO_FILE, 0, data, size);
if (!rc) {
smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst]);
}
}
if (rc) if (rc)
goto finished; goto finished;
smb2_set_next_command(tcon, &rqst[num_rqst]); num_rqst++;
smb2_set_related(&rqst[num_rqst++]);
trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid, trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid,
full_path); full_path);
break; break;
@ -210,14 +249,25 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
size[1] = len + 2 /* null */; size[1] = len + 2 /* null */;
data[1] = (__le16 *)ptr; data[1] = (__le16 *)ptr;
rc = SMB2_set_info_init(tcon, &rqst[num_rqst], COMPOUND_FID, if (cfile)
COMPOUND_FID, current->tgid, rc = SMB2_set_info_init(tcon, &rqst[num_rqst],
FILE_RENAME_INFORMATION, cfile->fid.persistent_fid,
cfile->fid.volatile_fid,
current->tgid, FILE_RENAME_INFORMATION,
SMB2_O_INFO_FILE, 0, data, size); SMB2_O_INFO_FILE, 0, data, size);
else {
rc = SMB2_set_info_init(tcon, &rqst[num_rqst],
COMPOUND_FID, COMPOUND_FID,
current->tgid, FILE_RENAME_INFORMATION,
SMB2_O_INFO_FILE, 0, data, size);
if (!rc) {
smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst]);
}
}
if (rc) if (rc)
goto finished; goto finished;
smb2_set_next_command(tcon, &rqst[num_rqst]); num_rqst++;
smb2_set_related(&rqst[num_rqst++]);
trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path); trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
break; break;
case SMB2_OP_HARDLINK: case SMB2_OP_HARDLINK:
@ -254,21 +304,43 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
if (rc) if (rc)
goto finished; goto finished;
/* We already have a handle so we can skip the close */
if (cfile)
goto after_close;
/* Close */ /* Close */
memset(&close_iov, 0, sizeof(close_iov)); memset(&close_iov, 0, sizeof(close_iov));
rqst[num_rqst].rq_iov = close_iov; rqst[num_rqst].rq_iov = close_iov;
rqst[num_rqst].rq_nvec = 1; rqst[num_rqst].rq_nvec = 1;
rc = SMB2_close_init(tcon, &rqst[num_rqst], COMPOUND_FID, rc = SMB2_close_init(tcon, &rqst[num_rqst], COMPOUND_FID,
COMPOUND_FID); COMPOUND_FID);
smb2_set_related(&rqst[num_rqst++]); smb2_set_related(&rqst[num_rqst]);
if (rc) if (rc)
goto finished; goto finished;
after_close:
num_rqst++;
rc = compound_send_recv(xid, ses, flags, num_rqst, rqst, if (cfile) {
resp_buftype, rsp_iov); cifsFileInfo_put(cfile);
cfile = NULL;
rc = compound_send_recv(xid, ses, flags, num_rqst - 2,
&rqst[1], &resp_buftype[1],
&rsp_iov[1]);
} else
rc = compound_send_recv(xid, ses, flags, num_rqst,
rqst, resp_buftype,
rsp_iov);
finished: finished:
if (cfile)
cifsFileInfo_put(cfile);
SMB2_open_free(&rqst[0]); SMB2_open_free(&rqst[0]);
if (rc == -EREMCHG) {
printk_once(KERN_WARNING "server share %s deleted\n",
tcon->treeName);
tcon->need_reconnect = true;
}
switch (command) { switch (command) {
case SMB2_OP_QUERY_INFO: case SMB2_OP_QUERY_INFO:
if (rc == 0) { if (rc == 0) {
@ -371,6 +443,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
__u32 create_options = 0; __u32 create_options = 0;
struct cifs_fid fid; struct cifs_fid fid;
bool no_cached_open = tcon->nohandlecache; bool no_cached_open = tcon->nohandlecache;
struct cifsFileInfo *cfile;
*adjust_tz = false; *adjust_tz = false;
*symlink = false; *symlink = false;
@ -402,9 +475,10 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
if (backup_cred(cifs_sb)) if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT; create_options |= CREATE_OPEN_BACKUP_INTENT;
cifs_get_readable_path(tcon, full_path, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN, create_options, FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
smb2_data, SMB2_OP_QUERY_INFO); smb2_data, SMB2_OP_QUERY_INFO, cfile);
if (rc == -EOPNOTSUPP) { if (rc == -EOPNOTSUPP) {
*symlink = true; *symlink = true;
create_options |= OPEN_REPARSE_POINT; create_options |= OPEN_REPARSE_POINT;
@ -413,7 +487,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN, FILE_READ_ATTRIBUTES, FILE_OPEN,
create_options, smb2_data, create_options, smb2_data,
SMB2_OP_QUERY_INFO); SMB2_OP_QUERY_INFO, NULL);
} }
if (rc) if (rc)
goto out; goto out;
@ -430,7 +504,7 @@ smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
{ {
return smb2_compound_op(xid, tcon, cifs_sb, name, return smb2_compound_op(xid, tcon, cifs_sb, name,
FILE_WRITE_ATTRIBUTES, FILE_CREATE, FILE_WRITE_ATTRIBUTES, FILE_CREATE,
CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR); CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR, NULL);
} }
void void
@ -440,6 +514,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
{ {
FILE_BASIC_INFO data; FILE_BASIC_INFO data;
struct cifsInodeInfo *cifs_i; struct cifsInodeInfo *cifs_i;
struct cifsFileInfo *cfile;
u32 dosattrs; u32 dosattrs;
int tmprc; int tmprc;
@ -447,9 +522,11 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
cifs_i = CIFS_I(inode); cifs_i = CIFS_I(inode);
dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
data.Attributes = cpu_to_le32(dosattrs); data.Attributes = cpu_to_le32(dosattrs);
cifs_get_writable_path(tcon, name, &cfile);
tmprc = smb2_compound_op(xid, tcon, cifs_sb, name, tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
FILE_WRITE_ATTRIBUTES, FILE_CREATE, FILE_WRITE_ATTRIBUTES, FILE_CREATE,
CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO); CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO,
cfile);
if (tmprc == 0) if (tmprc == 0)
cifs_i->cifsAttrs = dosattrs; cifs_i->cifsAttrs = dosattrs;
} }
@ -460,7 +537,7 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
{ {
return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
CREATE_NOT_FILE, CREATE_NOT_FILE,
NULL, SMB2_OP_RMDIR); NULL, SMB2_OP_RMDIR, NULL);
} }
int int
@ -469,13 +546,14 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
{ {
return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT, CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
NULL, SMB2_OP_DELETE); NULL, SMB2_OP_DELETE, NULL);
} }
static int static int
smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
const char *from_name, const char *to_name, const char *from_name, const char *to_name,
struct cifs_sb_info *cifs_sb, __u32 access, int command) struct cifs_sb_info *cifs_sb, __u32 access, int command,
struct cifsFileInfo *cfile)
{ {
__le16 *smb2_to_name = NULL; __le16 *smb2_to_name = NULL;
int rc; int rc;
@ -486,7 +564,7 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
goto smb2_rename_path; goto smb2_rename_path;
} }
rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access, rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
FILE_OPEN, 0, smb2_to_name, command); FILE_OPEN, 0, smb2_to_name, command, cfile);
smb2_rename_path: smb2_rename_path:
kfree(smb2_to_name); kfree(smb2_to_name);
return rc; return rc;
@ -497,8 +575,12 @@ smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
const char *from_name, const char *to_name, const char *from_name, const char *to_name,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, struct cifsFileInfo *cfile;
DELETE, SMB2_OP_RENAME);
cifs_get_writable_path(tcon, from_name, &cfile);
return smb2_set_path_attr(xid, tcon, from_name, to_name,
cifs_sb, DELETE, SMB2_OP_RENAME, cfile);
} }
int int
@ -507,7 +589,8 @@ smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb, return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK); FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK,
NULL);
} }
int int
@ -519,7 +602,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
return smb2_compound_op(xid, tcon, cifs_sb, full_path, return smb2_compound_op(xid, tcon, cifs_sb, full_path,
FILE_WRITE_DATA, FILE_OPEN, 0, &eof, FILE_WRITE_DATA, FILE_OPEN, 0, &eof,
SMB2_OP_SET_EOF); SMB2_OP_SET_EOF, NULL);
} }
int int
@ -541,7 +624,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
rc = smb2_compound_op(xid, tlink_tcon(tlink), cifs_sb, full_path, rc = smb2_compound_op(xid, tlink_tcon(tlink), cifs_sb, full_path,
FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf, FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf,
SMB2_OP_SET_INFO); SMB2_OP_SET_INFO, NULL);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
return rc; return rc;
} }

View File

@ -511,7 +511,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
{STATUS_PRINT_QUEUE_FULL, -EIO, "STATUS_PRINT_QUEUE_FULL"}, {STATUS_PRINT_QUEUE_FULL, -EIO, "STATUS_PRINT_QUEUE_FULL"},
{STATUS_NO_SPOOL_SPACE, -EIO, "STATUS_NO_SPOOL_SPACE"}, {STATUS_NO_SPOOL_SPACE, -EIO, "STATUS_NO_SPOOL_SPACE"},
{STATUS_PRINT_CANCELLED, -EIO, "STATUS_PRINT_CANCELLED"}, {STATUS_PRINT_CANCELLED, -EIO, "STATUS_PRINT_CANCELLED"},
{STATUS_NETWORK_NAME_DELETED, -EIO, "STATUS_NETWORK_NAME_DELETED"}, {STATUS_NETWORK_NAME_DELETED, -EREMCHG, "STATUS_NETWORK_NAME_DELETED"},
{STATUS_NETWORK_ACCESS_DENIED, -EACCES, "STATUS_NETWORK_ACCESS_DENIED"}, {STATUS_NETWORK_ACCESS_DENIED, -EACCES, "STATUS_NETWORK_ACCESS_DENIED"},
{STATUS_BAD_DEVICE_TYPE, -EIO, "STATUS_BAD_DEVICE_TYPE"}, {STATUS_BAD_DEVICE_TYPE, -EIO, "STATUS_BAD_DEVICE_TYPE"},
{STATUS_BAD_NETWORK_NAME, -ENOENT, "STATUS_BAD_NETWORK_NAME"}, {STATUS_BAD_NETWORK_NAME, -ENOENT, "STATUS_BAD_NETWORK_NAME"},

View File

@ -109,10 +109,10 @@ smb2_add_credits(struct TCP_Server_Info *server,
/* change_conf hasn't been executed */ /* change_conf hasn't been executed */
break; break;
case 0: case 0:
cifs_dbg(VFS, "Possible client or server bug - zero credits\n"); cifs_server_dbg(VFS, "Possible client or server bug - zero credits\n");
break; break;
case 1: case 1:
cifs_dbg(VFS, "disabling echoes and oplocks\n"); cifs_server_dbg(VFS, "disabling echoes and oplocks\n");
break; break;
case 2: case 2:
cifs_dbg(FYI, "disabling oplocks\n"); cifs_dbg(FYI, "disabling oplocks\n");
@ -203,6 +203,8 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
credits->instance = server->reconnect_instance; credits->instance = server->reconnect_instance;
server->credits -= credits->value; server->credits -= credits->value;
server->in_flight++; server->in_flight++;
if (server->in_flight > server->max_in_flight)
server->max_in_flight = server->in_flight;
break; break;
} }
} }
@ -230,7 +232,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
if (server->reconnect_instance != credits->instance) { if (server->reconnect_instance != credits->instance) {
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
cifs_dbg(VFS, "trying to return %d credits to old session\n", cifs_server_dbg(VFS, "trying to return %d credits to old session\n",
credits->value - new_val); credits->value - new_val);
return -EAGAIN; return -EAGAIN;
} }
@ -270,7 +272,7 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf)
__u64 wire_mid = le64_to_cpu(shdr->MessageId); __u64 wire_mid = le64_to_cpu(shdr->MessageId);
if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
cifs_dbg(VFS, "Encrypted frame parsing not supported yet\n"); cifs_server_dbg(VFS, "Encrypted frame parsing not supported yet\n");
return NULL; return NULL;
} }
@ -294,10 +296,10 @@ smb2_dump_detail(void *buf, struct TCP_Server_Info *server)
#ifdef CONFIG_CIFS_DEBUG2 #ifdef CONFIG_CIFS_DEBUG2
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf;
cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n", cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n",
shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId, shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId,
shdr->ProcessId); shdr->ProcessId);
cifs_dbg(VFS, "smb buf %p len %u\n", buf, cifs_server_dbg(VFS, "smb buf %p len %u\n", buf,
server->ops->calc_smb_size(buf, server)); server->ops->calc_smb_size(buf, server));
#endif #endif
} }
@ -576,7 +578,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
"server does not support query network interfaces\n"); "server does not support query network interfaces\n");
goto out; goto out;
} else if (rc != 0) { } else if (rc != 0) {
cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc); cifs_tcon_dbg(VFS, "error %d on ioctl to get interface list\n", rc);
goto out; goto out;
} }
@ -656,6 +658,15 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
return 0; return 0;
} }
/*
* We do not hold the lock for the open because in case
* SMB2_open needs to reconnect, it will end up calling
* cifs_mark_open_files_invalid() which takes the lock again
* thus causing a deadlock
*/
mutex_unlock(&tcon->crfid.fid_mutex);
if (smb3_encryption_required(tcon)) if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ; flags |= CIFS_TRANSFORM_REQ;
@ -677,7 +688,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, &utf16_path); rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, &utf16_path);
if (rc) if (rc)
goto oshr_exit; goto oshr_free;
smb2_set_next_command(tcon, &rqst[0]); smb2_set_next_command(tcon, &rqst[0]);
memset(&qi_iov, 0, sizeof(qi_iov)); memset(&qi_iov, 0, sizeof(qi_iov));
@ -690,18 +701,10 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
sizeof(struct smb2_file_all_info) + sizeof(struct smb2_file_all_info) +
PATH_MAX * 2, 0, NULL); PATH_MAX * 2, 0, NULL);
if (rc) if (rc)
goto oshr_exit; goto oshr_free;
smb2_set_related(&rqst[1]); smb2_set_related(&rqst[1]);
/*
* We do not hold the lock for the open because in case
* SMB2_open needs to reconnect, it will end up calling
* cifs_mark_open_files_invalid() which takes the lock again
* thus causing a deadlock
*/
mutex_unlock(&tcon->crfid.fid_mutex);
rc = compound_send_recv(xid, ses, flags, 2, rqst, rc = compound_send_recv(xid, ses, flags, 2, rqst,
resp_buftype, rsp_iov); resp_buftype, rsp_iov);
mutex_lock(&tcon->crfid.fid_mutex); mutex_lock(&tcon->crfid.fid_mutex);
@ -739,8 +742,14 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
/* Cached root is still invalid, continue normaly */ /* Cached root is still invalid, continue normaly */
if (rc) if (rc) {
if (rc == -EREMCHG) {
tcon->need_reconnect = true;
printk_once(KERN_WARNING "server share %s deleted\n",
tcon->treeName);
}
goto oshr_exit; goto oshr_exit;
}
o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
oparms.fid->persistent_fid = o_rsp->PersistentFileId; oparms.fid->persistent_fid = o_rsp->PersistentFileId;
@ -1330,11 +1339,11 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
(char **)&res_key, &ret_data_len); (char **)&res_key, &ret_data_len);
if (rc) { if (rc) {
cifs_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc); cifs_tcon_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc);
goto req_res_key_exit; goto req_res_key_exit;
} }
if (ret_data_len < sizeof(struct resume_key_req)) { if (ret_data_len < sizeof(struct resume_key_req)) {
cifs_dbg(VFS, "Invalid refcopy resume key length\n"); cifs_tcon_dbg(VFS, "Invalid refcopy resume key length\n");
rc = -EINVAL; rc = -EINVAL;
goto req_res_key_exit; goto req_res_key_exit;
} }
@ -1369,7 +1378,10 @@ smb2_ioctl_query_info(const unsigned int xid,
struct cifs_fid fid; struct cifs_fid fid;
struct kvec qi_iov[1]; struct kvec qi_iov[1];
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec close_iov[1]; struct kvec close_iov[1];
unsigned int size[2];
void *data[2];
memset(rqst, 0, sizeof(rqst)); memset(rqst, 0, sizeof(rqst));
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
@ -1404,7 +1416,6 @@ smb2_ioctl_query_info(const unsigned int xid,
memset(&oparms, 0, sizeof(oparms)); memset(&oparms, 0, sizeof(oparms));
oparms.tcon = tcon; oparms.tcon = tcon;
oparms.desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL;
oparms.disposition = FILE_OPEN; oparms.disposition = FILE_OPEN;
if (is_dir) if (is_dir)
oparms.create_options = CREATE_NOT_FILE; oparms.create_options = CREATE_NOT_FILE;
@ -1413,9 +1424,6 @@ smb2_ioctl_query_info(const unsigned int xid,
oparms.fid = &fid; oparms.fid = &fid;
oparms.reconnect = false; oparms.reconnect = false;
/*
* FSCTL codes encode the special access they need in the fsctl code.
*/
if (qi.flags & PASSTHRU_FSCTL) { if (qi.flags & PASSTHRU_FSCTL) {
switch (qi.info_type & FSCTL_DEVICE_ACCESS_MASK) { switch (qi.info_type & FSCTL_DEVICE_ACCESS_MASK) {
case FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS: case FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS:
@ -1431,6 +1439,10 @@ smb2_ioctl_query_info(const unsigned int xid,
oparms.desired_access = GENERIC_WRITE; oparms.desired_access = GENERIC_WRITE;
break; break;
} }
} else if (qi.flags & PASSTHRU_SET_INFO) {
oparms.desired_access = GENERIC_WRITE;
} else {
oparms.desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL;
} }
rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, path); rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, path);
@ -1454,6 +1466,24 @@ smb2_ioctl_query_info(const unsigned int xid,
qi.output_buffer_length, qi.output_buffer_length,
CIFSMaxBufSize); CIFSMaxBufSize);
} }
} else if (qi.flags == PASSTHRU_SET_INFO) {
/* Can eventually relax perm check since server enforces too */
if (!capable(CAP_SYS_ADMIN))
rc = -EPERM;
else {
memset(&si_iov, 0, sizeof(si_iov));
rqst[1].rq_iov = si_iov;
rqst[1].rq_nvec = 1;
size[0] = 8;
data[0] = buffer;
rc = SMB2_set_info_init(tcon, &rqst[1],
COMPOUND_FID, COMPOUND_FID,
current->tgid,
FILE_END_OF_FILE_INFORMATION,
SMB2_O_INFO_FILE, 0, data, size);
}
} else if (qi.flags == PASSTHRU_QUERY_INFO) { } else if (qi.flags == PASSTHRU_QUERY_INFO) {
memset(&qi_iov, 0, sizeof(qi_iov)); memset(&qi_iov, 0, sizeof(qi_iov));
rqst[1].rq_iov = qi_iov; rqst[1].rq_iov = qi_iov;
@ -1465,7 +1495,7 @@ smb2_ioctl_query_info(const unsigned int xid,
qi.input_buffer_length, qi.input_buffer_length,
qi.output_buffer_length, buffer); qi.output_buffer_length, buffer);
} else { /* unknown flags */ } else { /* unknown flags */
cifs_dbg(VFS, "invalid passthru query flags: 0x%x\n", qi.flags); cifs_tcon_dbg(VFS, "invalid passthru query flags: 0x%x\n", qi.flags);
rc = -EINVAL; rc = -EINVAL;
} }
@ -1592,7 +1622,7 @@ smb2_copychunk_range(const unsigned int xid,
if (rc == 0) { if (rc == 0) {
if (ret_data_len != if (ret_data_len !=
sizeof(struct copychunk_ioctl_rsp)) { sizeof(struct copychunk_ioctl_rsp)) {
cifs_dbg(VFS, "invalid cchunk response size\n"); cifs_tcon_dbg(VFS, "invalid cchunk response size\n");
rc = -EIO; rc = -EIO;
goto cchunk_out; goto cchunk_out;
} }
@ -1606,12 +1636,12 @@ smb2_copychunk_range(const unsigned int xid,
*/ */
if (le32_to_cpu(retbuf->TotalBytesWritten) > if (le32_to_cpu(retbuf->TotalBytesWritten) >
le32_to_cpu(pcchunk->Length)) { le32_to_cpu(pcchunk->Length)) {
cifs_dbg(VFS, "invalid copy chunk response\n"); cifs_tcon_dbg(VFS, "invalid copy chunk response\n");
rc = -EIO; rc = -EIO;
goto cchunk_out; goto cchunk_out;
} }
if (le32_to_cpu(retbuf->ChunksWritten) != 1) { if (le32_to_cpu(retbuf->ChunksWritten) != 1) {
cifs_dbg(VFS, "invalid num chunks written\n"); cifs_tcon_dbg(VFS, "invalid num chunks written\n");
rc = -EIO; rc = -EIO;
goto cchunk_out; goto cchunk_out;
} }
@ -2214,6 +2244,11 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
resp_buftype, rsp_iov); resp_buftype, rsp_iov);
if (rc) { if (rc) {
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
if (rc == -EREMCHG) {
tcon->need_reconnect = true;
printk_once(KERN_WARNING "server share %s deleted\n",
tcon->treeName);
}
goto qic_exit; goto qic_exit;
} }
*rsp = rsp_iov[1]; *rsp = rsp_iov[1];
@ -2401,7 +2436,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
if (rc) { if (rc) {
if ((rc != -ENOENT) && (rc != -EOPNOTSUPP)) if ((rc != -ENOENT) && (rc != -EOPNOTSUPP))
cifs_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc); cifs_tcon_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc);
goto out; goto out;
} }
@ -2410,7 +2445,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
nls_codepage, remap, search_name, nls_codepage, remap, search_name,
true /* is_unicode */); true /* is_unicode */);
if (rc) { if (rc) {
cifs_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc); cifs_tcon_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc);
goto out; goto out;
} }
@ -2640,7 +2675,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) >
rsp_iov[1].iov_len) { rsp_iov[1].iov_len) {
cifs_dbg(VFS, "srv returned invalid ioctl len: %d\n", cifs_tcon_dbg(VFS, "srv returned invalid ioctl len: %d\n",
plen); plen);
rc = -EIO; rc = -EIO;
goto querty_exit; goto querty_exit;
@ -2939,7 +2974,6 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
loff_t offset, loff_t len) loff_t offset, loff_t len)
{ {
struct inode *inode; struct inode *inode;
struct cifsInodeInfo *cifsi;
struct cifsFileInfo *cfile = file->private_data; struct cifsFileInfo *cfile = file->private_data;
struct file_zero_data_information fsctl_buf; struct file_zero_data_information fsctl_buf;
long rc; long rc;
@ -2949,7 +2983,6 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
xid = get_xid(); xid = get_xid();
inode = d_inode(cfile->dentry); inode = d_inode(cfile->dentry);
cifsi = CIFS_I(inode);
/* Need to make file sparse, if not already, before freeing range. */ /* Need to make file sparse, if not already, before freeing range. */
/* Consider adding equivalent for compressed since it could also work */ /* Consider adding equivalent for compressed since it could also work */
@ -3595,14 +3628,14 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
rc = smb2_get_enc_key(server, tr_hdr->SessionId, enc, key); rc = smb2_get_enc_key(server, tr_hdr->SessionId, enc, key);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not get %scryption key\n", __func__, cifs_server_dbg(VFS, "%s: Could not get %scryption key\n", __func__,
enc ? "en" : "de"); enc ? "en" : "de");
return 0; return 0;
} }
rc = smb3_crypto_aead_allocate(server); rc = smb3_crypto_aead_allocate(server);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__);
return rc; return rc;
} }
@ -3610,19 +3643,19 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
server->secmech.ccmaesdecrypt; server->secmech.ccmaesdecrypt;
rc = crypto_aead_setkey(tfm, key, SMB3_SIGN_KEY_SIZE); rc = crypto_aead_setkey(tfm, key, SMB3_SIGN_KEY_SIZE);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc); cifs_server_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc);
return rc; return rc;
} }
rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc); cifs_server_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc);
return rc; return rc;
} }
req = aead_request_alloc(tfm, GFP_KERNEL); req = aead_request_alloc(tfm, GFP_KERNEL);
if (!req) { if (!req) {
cifs_dbg(VFS, "%s: Failed to alloc aead request\n", __func__); cifs_server_dbg(VFS, "%s: Failed to alloc aead request\n", __func__);
return -ENOMEM; return -ENOMEM;
} }
@ -3633,7 +3666,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
sg = init_sg(num_rqst, rqst, sign); sg = init_sg(num_rqst, rqst, sign);
if (!sg) { if (!sg) {
cifs_dbg(VFS, "%s: Failed to init sg\n", __func__); cifs_server_dbg(VFS, "%s: Failed to init sg\n", __func__);
rc = -ENOMEM; rc = -ENOMEM;
goto free_req; goto free_req;
} }
@ -3641,7 +3674,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
iv_len = crypto_aead_ivsize(tfm); iv_len = crypto_aead_ivsize(tfm);
iv = kzalloc(iv_len, GFP_KERNEL); iv = kzalloc(iv_len, GFP_KERNEL);
if (!iv) { if (!iv) {
cifs_dbg(VFS, "%s: Failed to alloc iv\n", __func__); cifs_server_dbg(VFS, "%s: Failed to alloc iv\n", __func__);
rc = -ENOMEM; rc = -ENOMEM;
goto free_sg; goto free_sg;
} }
@ -3883,7 +3916,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
bool use_rdma_mr = false; bool use_rdma_mr = false;
if (shdr->Command != SMB2_READ) { if (shdr->Command != SMB2_READ) {
cifs_dbg(VFS, "only big read responses are supported\n"); cifs_server_dbg(VFS, "only big read responses are supported\n");
return -ENOTSUPP; return -ENOTSUPP;
} }
@ -3998,8 +4031,55 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
return length; return length;
} }
struct smb2_decrypt_work {
struct work_struct decrypt;
struct TCP_Server_Info *server;
struct page **ppages;
char *buf;
unsigned int npages;
unsigned int len;
};
static void smb2_decrypt_offload(struct work_struct *work)
{
struct smb2_decrypt_work *dw = container_of(work,
struct smb2_decrypt_work, decrypt);
int i, rc;
struct mid_q_entry *mid;
rc = decrypt_raw_data(dw->server, dw->buf, dw->server->vals->read_rsp_size,
dw->ppages, dw->npages, dw->len);
if (rc) {
cifs_dbg(VFS, "error decrypting rc=%d\n", rc);
goto free_pages;
}
dw->server->lstrp = jiffies;
mid = smb2_find_mid(dw->server, dw->buf);
if (mid == NULL)
cifs_dbg(FYI, "mid not found\n");
else {
mid->decrypted = true;
rc = handle_read_data(dw->server, mid, dw->buf,
dw->server->vals->read_rsp_size,
dw->ppages, dw->npages, dw->len);
mid->callback(mid);
cifs_mid_q_entry_release(mid);
}
free_pages:
for (i = dw->npages-1; i >= 0; i--)
put_page(dw->ppages[i]);
kfree(dw->ppages);
cifs_small_buf_release(dw->buf);
}
static int static int
receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid) receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
int *num_mids)
{ {
char *buf = server->smallbuf; char *buf = server->smallbuf;
struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
@ -4009,7 +4089,9 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid)
unsigned int buflen = server->pdu_size; unsigned int buflen = server->pdu_size;
int rc; int rc;
int i = 0; int i = 0;
struct smb2_decrypt_work *dw;
*num_mids = 1;
len = min_t(unsigned int, buflen, server->vals->read_rsp_size + len = min_t(unsigned int, buflen, server->vals->read_rsp_size +
sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1; sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1;
@ -4045,6 +4127,32 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid)
if (rc) if (rc)
goto free_pages; goto free_pages;
/*
* For large reads, offload to different thread for better performance,
* use more cores decrypting which can be expensive
*/
if ((server->min_offload) && (server->in_flight > 1) &&
(server->pdu_size >= server->min_offload)) {
dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL);
if (dw == NULL)
goto non_offloaded_decrypt;
dw->buf = server->smallbuf;
server->smallbuf = (char *)cifs_small_buf_get();
INIT_WORK(&dw->decrypt, smb2_decrypt_offload);
dw->npages = npages;
dw->server = server;
dw->ppages = pages;
dw->len = len;
queue_work(cifsiod_wq, &dw->decrypt);
*num_mids = 0; /* worker thread takes care of finding mid */
return -1;
}
non_offloaded_decrypt:
rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size, rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size,
pages, npages, len); pages, npages, len);
if (rc) if (rc)
@ -4129,7 +4237,7 @@ one_more:
} }
if (*num_mids >= MAX_COMPOUND) { if (*num_mids >= MAX_COMPOUND) {
cifs_dbg(VFS, "too many PDUs in compound\n"); cifs_server_dbg(VFS, "too many PDUs in compound\n");
return -1; return -1;
} }
bufs[*num_mids] = buf; bufs[*num_mids] = buf;
@ -4175,7 +4283,7 @@ smb3_receive_transform(struct TCP_Server_Info *server,
if (pdu_length < sizeof(struct smb2_transform_hdr) + if (pdu_length < sizeof(struct smb2_transform_hdr) +
sizeof(struct smb2_sync_hdr)) { sizeof(struct smb2_sync_hdr)) {
cifs_dbg(VFS, "Transform message is too small (%u)\n", cifs_server_dbg(VFS, "Transform message is too small (%u)\n",
pdu_length); pdu_length);
cifs_reconnect(server); cifs_reconnect(server);
wake_up(&server->response_q); wake_up(&server->response_q);
@ -4183,7 +4291,7 @@ smb3_receive_transform(struct TCP_Server_Info *server,
} }
if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) { if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) {
cifs_dbg(VFS, "Transform message is broken\n"); cifs_server_dbg(VFS, "Transform message is broken\n");
cifs_reconnect(server); cifs_reconnect(server);
wake_up(&server->response_q); wake_up(&server->response_q);
return -ECONNABORTED; return -ECONNABORTED;
@ -4191,8 +4299,7 @@ smb3_receive_transform(struct TCP_Server_Info *server,
/* TODO: add support for compounds containing READ. */ /* TODO: add support for compounds containing READ. */
if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) { if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) {
*num_mids = 1; return receive_encrypted_read(server, &mids[0], num_mids);
return receive_encrypted_read(server, &mids[0]);
} }
return receive_encrypted_standard(server, mids, bufs, num_mids); return receive_encrypted_standard(server, mids, bufs, num_mids);

View File

@ -503,8 +503,7 @@ build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname)
pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID; pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID;
/* copy up to max of first 100 bytes of server name to NetName field */ /* copy up to max of first 100 bytes of server name to NetName field */
pneg_ctxt->DataLength = cpu_to_le16(2 + pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp));
(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)));
/* context size is DataLength + minimal smb2_neg_context */ /* context size is DataLength + minimal smb2_neg_context */
return DIV_ROUND_UP(le16_to_cpu(pneg_ctxt->DataLength) + return DIV_ROUND_UP(le16_to_cpu(pneg_ctxt->DataLength) +
sizeof(struct smb2_neg_context), 8) * 8; sizeof(struct smb2_neg_context), 8) * 8;
@ -543,7 +542,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
if (*total_len > 200) { if (*total_len > 200) {
/* In case length corrupted don't want to overrun smb buffer */ /* In case length corrupted don't want to overrun smb buffer */
cifs_dbg(VFS, "Bad frame length assembling neg contexts\n"); cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n");
return; return;
} }
@ -661,7 +660,7 @@ static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp,
cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt); cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt);
if (len_of_smb <= offset) { if (len_of_smb <= offset) {
cifs_dbg(VFS, "Invalid response: negotiate context offset\n"); cifs_server_dbg(VFS, "Invalid response: negotiate context offset\n");
return -EINVAL; return -EINVAL;
} }
@ -693,7 +692,7 @@ static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp,
else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE)
server->posix_ext_supported = true; server->posix_ext_supported = true;
else else
cifs_dbg(VFS, "unknown negcontext of type %d ignored\n", cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n",
le16_to_cpu(pctx->ContextType)); le16_to_cpu(pctx->ContextType));
if (rc) if (rc)
@ -818,7 +817,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
req->DialectCount = cpu_to_le16(2); req->DialectCount = cpu_to_le16(2);
total_len += 4; total_len += 4;
} else if (strcmp(ses->server->vals->version_string, } else if (strcmp(server->vals->version_string,
SMBDEFAULT_VERSION_STRING) == 0) { SMBDEFAULT_VERSION_STRING) == 0) {
req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
@ -841,16 +840,16 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
else else
req->SecurityMode = 0; req->SecurityMode = 0;
req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities); req->Capabilities = cpu_to_le32(server->vals->req_capabilities);
/* ClientGUID must be zero for SMB2.02 dialect */ /* ClientGUID must be zero for SMB2.02 dialect */
if (ses->server->vals->protocol_id == SMB20_PROT_ID) if (server->vals->protocol_id == SMB20_PROT_ID)
memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE);
else { else {
memcpy(req->ClientGUID, server->client_guid, memcpy(req->ClientGUID, server->client_guid,
SMB2_CLIENT_GUID_SIZE); SMB2_CLIENT_GUID_SIZE);
if ((ses->server->vals->protocol_id == SMB311_PROT_ID) || if ((server->vals->protocol_id == SMB311_PROT_ID) ||
(strcmp(ses->server->vals->version_string, (strcmp(server->vals->version_string,
SMBDEFAULT_VERSION_STRING) == 0)) SMBDEFAULT_VERSION_STRING) == 0))
assemble_neg_contexts(req, server, &total_len); assemble_neg_contexts(req, server, &total_len);
} }
@ -869,42 +868,42 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
* cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
*/ */
if (rc == -EOPNOTSUPP) { if (rc == -EOPNOTSUPP) {
cifs_dbg(VFS, "Dialect not supported by server. Consider " cifs_server_dbg(VFS, "Dialect not supported by server. Consider "
"specifying vers=1.0 or vers=2.0 on mount for accessing" "specifying vers=1.0 or vers=2.0 on mount for accessing"
" older servers\n"); " older servers\n");
goto neg_exit; goto neg_exit;
} else if (rc != 0) } else if (rc != 0)
goto neg_exit; goto neg_exit;
if (strcmp(ses->server->vals->version_string, if (strcmp(server->vals->version_string,
SMB3ANY_VERSION_STRING) == 0) { SMB3ANY_VERSION_STRING) == 0) {
if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
cifs_dbg(VFS, cifs_server_dbg(VFS,
"SMB2 dialect returned but not requested\n"); "SMB2 dialect returned but not requested\n");
return -EIO; return -EIO;
} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
cifs_dbg(VFS, cifs_server_dbg(VFS,
"SMB2.1 dialect returned but not requested\n"); "SMB2.1 dialect returned but not requested\n");
return -EIO; return -EIO;
} }
} else if (strcmp(ses->server->vals->version_string, } else if (strcmp(server->vals->version_string,
SMBDEFAULT_VERSION_STRING) == 0) { SMBDEFAULT_VERSION_STRING) == 0) {
if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
cifs_dbg(VFS, cifs_server_dbg(VFS,
"SMB2 dialect returned but not requested\n"); "SMB2 dialect returned but not requested\n");
return -EIO; return -EIO;
} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
/* ops set to 3.0 by default for default so update */ /* ops set to 3.0 by default for default so update */
ses->server->ops = &smb21_operations; server->ops = &smb21_operations;
ses->server->vals = &smb21_values; server->vals = &smb21_values;
} else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
ses->server->ops = &smb311_operations; server->ops = &smb311_operations;
ses->server->vals = &smb311_values; server->vals = &smb311_values;
} }
} else if (le16_to_cpu(rsp->DialectRevision) != } else if (le16_to_cpu(rsp->DialectRevision) !=
ses->server->vals->protocol_id) { server->vals->protocol_id) {
/* if requested single dialect ensure returned dialect matched */ /* if requested single dialect ensure returned dialect matched */
cifs_dbg(VFS, "Illegal 0x%x dialect returned: not requested\n", cifs_server_dbg(VFS, "Illegal 0x%x dialect returned: not requested\n",
le16_to_cpu(rsp->DialectRevision)); le16_to_cpu(rsp->DialectRevision));
return -EIO; return -EIO;
} }
@ -922,7 +921,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID))
cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n");
else { else {
cifs_dbg(VFS, "Illegal dialect returned by server 0x%x\n", cifs_server_dbg(VFS, "Illegal dialect returned by server 0x%x\n",
le16_to_cpu(rsp->DialectRevision)); le16_to_cpu(rsp->DialectRevision));
rc = -EIO; rc = -EIO;
goto neg_exit; goto neg_exit;
@ -982,7 +981,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
rc = smb311_decode_neg_context(rsp, server, rc = smb311_decode_neg_context(rsp, server,
rsp_iov.iov_len); rsp_iov.iov_len);
else else
cifs_dbg(VFS, "Missing expected negotiate contexts\n"); cifs_server_dbg(VFS, "Missing expected negotiate contexts\n");
} }
neg_exit: neg_exit:
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
@ -996,11 +995,12 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
struct validate_negotiate_info_rsp *pneg_rsp = NULL; struct validate_negotiate_info_rsp *pneg_rsp = NULL;
u32 rsplen; u32 rsplen;
u32 inbuflen; /* max of 4 dialects */ u32 inbuflen; /* max of 4 dialects */
struct TCP_Server_Info *server = tcon->ses->server;
cifs_dbg(FYI, "validate negotiate\n"); cifs_dbg(FYI, "validate negotiate\n");
/* In SMB3.11 preauth integrity supersedes validate negotiate */ /* In SMB3.11 preauth integrity supersedes validate negotiate */
if (tcon->ses->server->dialect == SMB311_PROT_ID) if (server->dialect == SMB311_PROT_ID)
return 0; return 0;
/* /*
@ -1019,15 +1019,15 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
} }
if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
cifs_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n");
pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS);
if (!pneg_inbuf) if (!pneg_inbuf)
return -ENOMEM; return -ENOMEM;
pneg_inbuf->Capabilities = pneg_inbuf->Capabilities =
cpu_to_le32(tcon->ses->server->vals->req_capabilities); cpu_to_le32(server->vals->req_capabilities);
memcpy(pneg_inbuf->Guid, tcon->ses->server->client_guid, memcpy(pneg_inbuf->Guid, server->client_guid,
SMB2_CLIENT_GUID_SIZE); SMB2_CLIENT_GUID_SIZE);
if (tcon->ses->sign) if (tcon->ses->sign)
@ -1040,7 +1040,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
pneg_inbuf->SecurityMode = 0; pneg_inbuf->SecurityMode = 0;
if (strcmp(tcon->ses->server->vals->version_string, if (strcmp(server->vals->version_string,
SMB3ANY_VERSION_STRING) == 0) { SMB3ANY_VERSION_STRING) == 0) {
pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
@ -1048,7 +1048,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
/* structure is big enough for 3 dialects, sending only 2 */ /* structure is big enough for 3 dialects, sending only 2 */
inbuflen = sizeof(*pneg_inbuf) - inbuflen = sizeof(*pneg_inbuf) -
(2 * sizeof(pneg_inbuf->Dialects[0])); (2 * sizeof(pneg_inbuf->Dialects[0]));
} else if (strcmp(tcon->ses->server->vals->version_string, } else if (strcmp(server->vals->version_string,
SMBDEFAULT_VERSION_STRING) == 0) { SMBDEFAULT_VERSION_STRING) == 0) {
pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
@ -1060,7 +1060,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
} else { } else {
/* otherwise specific dialect was requested */ /* otherwise specific dialect was requested */
pneg_inbuf->Dialects[0] = pneg_inbuf->Dialects[0] =
cpu_to_le16(tcon->ses->server->vals->protocol_id); cpu_to_le16(server->vals->protocol_id);
pneg_inbuf->DialectCount = cpu_to_le16(1); pneg_inbuf->DialectCount = cpu_to_le16(1);
/* structure is big enough for 3 dialects, sending only 1 */ /* structure is big enough for 3 dialects, sending only 1 */
inbuflen = sizeof(*pneg_inbuf) - inbuflen = sizeof(*pneg_inbuf) -
@ -1076,18 +1076,18 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
* Old Windows versions or Netapp SMB server can return * Old Windows versions or Netapp SMB server can return
* not supported error. Client should accept it. * not supported error. Client should accept it.
*/ */
cifs_dbg(VFS, "Server does not support validate negotiate\n"); cifs_tcon_dbg(VFS, "Server does not support validate negotiate\n");
rc = 0; rc = 0;
goto out_free_inbuf; goto out_free_inbuf;
} else if (rc != 0) { } else if (rc != 0) {
cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n", rc);
rc = -EIO; rc = -EIO;
goto out_free_inbuf; goto out_free_inbuf;
} }
rc = -EIO; rc = -EIO;
if (rsplen != sizeof(*pneg_rsp)) { if (rsplen != sizeof(*pneg_rsp)) {
cifs_dbg(VFS, "invalid protocol negotiate response size: %d\n", cifs_tcon_dbg(VFS, "invalid protocol negotiate response size: %d\n",
rsplen); rsplen);
/* relax check since Mac returns max bufsize allowed on ioctl */ /* relax check since Mac returns max bufsize allowed on ioctl */
@ -1096,16 +1096,16 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
} }
/* check validate negotiate info response matches what we got earlier */ /* check validate negotiate info response matches what we got earlier */
if (pneg_rsp->Dialect != cpu_to_le16(tcon->ses->server->dialect)) if (pneg_rsp->Dialect != cpu_to_le16(server->dialect))
goto vneg_out; goto vneg_out;
if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode)) if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode))
goto vneg_out; goto vneg_out;
/* do not validate server guid because not saved at negprot time yet */ /* do not validate server guid because not saved at negprot time yet */
if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND |
SMB2_LARGE_FILES) != tcon->ses->server->capabilities) SMB2_LARGE_FILES) != server->capabilities)
goto vneg_out; goto vneg_out;
/* validate negotiate successful */ /* validate negotiate successful */
@ -1114,7 +1114,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
goto out_free_rsp; goto out_free_rsp;
vneg_out: vneg_out:
cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n"); cifs_tcon_dbg(VFS, "protocol revalidation - security settings mismatch\n");
out_free_rsp: out_free_rsp:
kfree(pneg_rsp); kfree(pneg_rsp);
out_free_inbuf: out_free_inbuf:
@ -1568,7 +1568,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
sess_data->func(sess_data); sess_data->func(sess_data);
if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign)) if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign))
cifs_dbg(VFS, "signing requested but authenticated as guest\n"); cifs_server_dbg(VFS, "signing requested but authenticated as guest\n");
rc = sess_data->result; rc = sess_data->result;
out: out:
kfree(sess_data); kfree(sess_data);
@ -1661,10 +1661,11 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
__le16 *unc_path = NULL; __le16 *unc_path = NULL;
int flags = 0; int flags = 0;
unsigned int total_len; unsigned int total_len;
struct TCP_Server_Info *server = ses->server;
cifs_dbg(FYI, "TCON\n"); cifs_dbg(FYI, "TCON\n");
if (!(ses->server) || !tree) if (!server || !tree)
return -EIO; return -EIO;
unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL);
@ -1707,7 +1708,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
* unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1 * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1
* (Samba servers don't always set the flag so also check if null user) * (Samba servers don't always set the flag so also check if null user)
*/ */
if ((ses->server->dialect == SMB311_PROT_ID) && if ((server->dialect == SMB311_PROT_ID) &&
!smb3_encryption_required(tcon) && !smb3_encryption_required(tcon) &&
!(ses->session_flags & !(ses->session_flags &
(SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) && (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) &&
@ -1746,7 +1747,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
cifs_dbg(FYI, "connection to printer\n"); cifs_dbg(FYI, "connection to printer\n");
break; break;
default: default:
cifs_dbg(VFS, "unknown share type %d\n", rsp->ShareType); cifs_server_dbg(VFS, "unknown share type %d\n", rsp->ShareType);
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
goto tcon_error_exit; goto tcon_error_exit;
} }
@ -1761,15 +1762,15 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) &&
((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); cifs_tcon_dbg(VFS, "DFS capability contradicts DFS flag\n");
if (tcon->seal && if (tcon->seal &&
!(tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
cifs_dbg(VFS, "Encryption is requested but not supported\n"); cifs_tcon_dbg(VFS, "Encryption is requested but not supported\n");
init_copy_chunk_defaults(tcon); init_copy_chunk_defaults(tcon);
if (tcon->ses->server->ops->validate_negotiate) if (server->ops->validate_negotiate)
rc = tcon->ses->server->ops->validate_negotiate(xid, tcon); rc = server->ops->validate_negotiate(xid, tcon);
tcon_exit: tcon_exit:
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
@ -1778,7 +1779,7 @@ tcon_exit:
tcon_error_exit: tcon_error_exit:
if (rsp && rsp->sync_hdr.Status == STATUS_BAD_NETWORK_NAME) { if (rsp && rsp->sync_hdr.Status == STATUS_BAD_NETWORK_NAME) {
cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree);
} }
goto tcon_exit; goto tcon_exit;
} }
@ -2458,7 +2459,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, __u8 *oplock,
iov[1].iov_len = uni_path_len; iov[1].iov_len = uni_path_len;
iov[1].iov_base = path; iov[1].iov_base = path;
if (!server->oplocks) if ((!server->oplocks) || (tcon->no_lease))
*oplock = SMB2_OPLOCK_LEVEL_NONE; *oplock = SMB2_OPLOCK_LEVEL_NONE;
if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) ||
@ -2594,6 +2595,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
} }
trace_smb3_open_err(xid, tcon->tid, ses->Suid, trace_smb3_open_err(xid, tcon->tid, ses->Suid,
oparms->create_options, oparms->desired_access, rc); oparms->create_options, oparms->desired_access, rc);
if (rc == -EREMCHG) {
printk_once(KERN_WARNING "server share %s deleted\n",
tcon->treeName);
tcon->need_reconnect = true;
}
goto creat_exit; goto creat_exit;
} else } else
trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid,
@ -2742,6 +2748,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
int resp_buftype = CIFS_NO_BUFFER; int resp_buftype = CIFS_NO_BUFFER;
int rc = 0; int rc = 0;
int flags = 0; int flags = 0;
struct TCP_Server_Info *server;
cifs_dbg(FYI, "SMB2 IOCTL\n"); cifs_dbg(FYI, "SMB2 IOCTL\n");
@ -2757,7 +2764,10 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
else else
return -EIO; return -EIO;
if (!ses || !(ses->server)) if (!ses)
return -EIO;
server = ses->server;
if (!server)
return -EIO; return -EIO;
if (smb3_encryption_required(tcon)) if (smb3_encryption_required(tcon))
@ -2807,14 +2817,14 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
if (*plen == 0) if (*plen == 0)
goto ioctl_exit; /* server returned no data */ goto ioctl_exit; /* server returned no data */
else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) {
cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen);
*plen = 0; *plen = 0;
rc = -EIO; rc = -EIO;
goto ioctl_exit; goto ioctl_exit;
} }
if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) {
cifs_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen,
le32_to_cpu(rsp->OutputOffset)); le32_to_cpu(rsp->OutputOffset));
*plen = 0; *plen = 0;
rc = -EIO; rc = -EIO;
@ -2913,6 +2923,7 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon,
rqst.rq_iov = iov; rqst.rq_iov = iov;
rqst.rq_nvec = 1; rqst.rq_nvec = 1;
trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid);
rc = SMB2_close_init(tcon, &rqst, persistent_fid, volatile_fid); rc = SMB2_close_init(tcon, &rqst, persistent_fid, volatile_fid);
if (rc) if (rc)
goto close_exit; goto close_exit;
@ -2925,7 +2936,9 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon,
trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid, trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid,
rc); rc);
goto close_exit; goto close_exit;
} } else
trace_smb3_close_done(xid, persistent_fid, tcon->tid,
ses->Suid);
atomic_dec(&tcon->num_remote_opens); atomic_dec(&tcon->num_remote_opens);
@ -3055,12 +3068,16 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0; int rc = 0;
int resp_buftype = CIFS_NO_BUFFER; int resp_buftype = CIFS_NO_BUFFER;
struct cifs_ses *ses = tcon->ses; struct cifs_ses *ses = tcon->ses;
struct TCP_Server_Info *server;
int flags = 0; int flags = 0;
bool allocated = false; bool allocated = false;
cifs_dbg(FYI, "Query Info\n"); cifs_dbg(FYI, "Query Info\n");
if (!ses || !(ses->server)) if (!ses)
return -EIO;
server = ses->server;
if (!server)
return -EIO; return -EIO;
if (smb3_encryption_required(tcon)) if (smb3_encryption_required(tcon))
@ -3098,7 +3115,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
if (!*data) { if (!*data) {
*data = kmalloc(*dlen, GFP_KERNEL); *data = kmalloc(*dlen, GFP_KERNEL);
if (!*data) { if (!*data) {
cifs_dbg(VFS, cifs_tcon_dbg(VFS,
"Error %d allocating memory for acl\n", "Error %d allocating memory for acl\n",
rc); rc);
*dlen = 0; *dlen = 0;
@ -3158,6 +3175,91 @@ SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
(void **)&uniqueid, NULL); (void **)&uniqueid, NULL);
} }
/*
* CHANGE_NOTIFY Request is sent to get notifications on changes to a directory
* See MS-SMB2 2.2.35 and 2.2.36
*/
int
SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst,
struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid,
u32 completion_filter, bool watch_tree)
{
struct smb2_change_notify_req *req;
struct kvec *iov = rqst->rq_iov;
unsigned int total_len;
int rc;
rc = smb2_plain_req_init(SMB2_CHANGE_NOTIFY, tcon, (void **) &req, &total_len);
if (rc)
return rc;
req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid;
req->OutputBufferLength = SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE;
req->CompletionFilter = cpu_to_le32(completion_filter);
if (watch_tree)
req->Flags = cpu_to_le16(SMB2_WATCH_TREE);
else
req->Flags = 0;
iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len;
return 0;
}
int
SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, bool watch_tree,
u32 completion_filter)
{
struct cifs_ses *ses = tcon->ses;
struct smb_rqst rqst;
struct kvec iov[1];
struct kvec rsp_iov = {NULL, 0};
int resp_buftype = CIFS_NO_BUFFER;
int flags = 0;
int rc = 0;
cifs_dbg(FYI, "change notify\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_notify_init(xid, &rqst, tcon, persistent_fid, volatile_fid,
completion_filter, watch_tree);
if (rc)
goto cnotify_exit;
trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid,
(u8)watch_tree, completion_filter);
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
if (rc != 0) {
cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE);
trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid,
(u8)watch_tree, completion_filter, rc);
} else
trace_smb3_notify_done(xid, persistent_fid, tcon->tid,
ses->Suid, (u8)watch_tree, completion_filter);
cnotify_exit:
if (rqst.rq_iov)
cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */
free_rsp_buf(resp_buftype, rsp_iov.iov_base);
return rc;
}
/* /*
* This is a no-op for now. We're not really interested in the reply, but * This is a no-op for now. We're not really interested in the reply, but
* rather in the fact that the server sent one and that server->lstrp * rather in the fact that the server sent one and that server->lstrp
@ -3287,51 +3389,76 @@ SMB2_echo(struct TCP_Server_Info *server)
return rc; return rc;
} }
int void
SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, SMB2_flush_free(struct smb_rqst *rqst)
u64 volatile_fid) {
if (rqst && rqst->rq_iov)
cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
}
int
SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst,
struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid)
{ {
struct smb_rqst rqst;
struct smb2_flush_req *req; struct smb2_flush_req *req;
struct cifs_ses *ses = tcon->ses; struct kvec *iov = rqst->rq_iov;
struct kvec iov[1];
struct kvec rsp_iov;
int resp_buftype;
int rc = 0;
int flags = 0;
unsigned int total_len; unsigned int total_len;
int rc;
cifs_dbg(FYI, "Flush\n");
if (!ses || !(ses->server))
return -EIO;
rc = smb2_plain_req_init(SMB2_FLUSH, tcon, (void **) &req, &total_len); rc = smb2_plain_req_init(SMB2_FLUSH, tcon, (void **) &req, &total_len);
if (rc) if (rc)
return rc; return rc;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
req->PersistentFileId = persistent_fid; req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid; req->VolatileFileId = volatile_fid;
iov[0].iov_base = (char *)req; iov[0].iov_base = (char *)req;
iov[0].iov_len = total_len; iov[0].iov_len = total_len;
return 0;
}
int
SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
u64 volatile_fid)
{
struct cifs_ses *ses = tcon->ses;
struct smb_rqst rqst;
struct kvec iov[1];
struct kvec rsp_iov = {NULL, 0};
int resp_buftype = CIFS_NO_BUFFER;
int flags = 0;
int rc = 0;
cifs_dbg(FYI, "flush\n");
if (!ses || !(ses->server))
return -EIO;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&rqst, 0, sizeof(struct smb_rqst));
memset(&iov, 0, sizeof(iov));
rqst.rq_iov = iov; rqst.rq_iov = iov;
rqst.rq_nvec = 1; rqst.rq_nvec = 1;
rc = SMB2_flush_init(xid, &rqst, tcon, persistent_fid, volatile_fid);
if (rc)
goto flush_exit;
trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid);
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
if (rc != 0) { if (rc != 0) {
cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE);
trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid, trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid,
rc); rc);
} } else
trace_smb3_flush_done(xid, persistent_fid, tcon->tid,
ses->Suid);
flush_exit:
SMB2_flush_free(&rqst);
free_rsp_buf(resp_buftype, rsp_iov.iov_base); free_rsp_buf(resp_buftype, rsp_iov.iov_base);
return rc; return rc;
} }
@ -3446,8 +3573,8 @@ smb2_readv_callback(struct mid_q_entry *mid)
struct smb2_sync_hdr *shdr = struct smb2_sync_hdr *shdr =
(struct smb2_sync_hdr *)rdata->iov[0].iov_base; (struct smb2_sync_hdr *)rdata->iov[0].iov_base;
struct cifs_credits credits = { .value = 0, .instance = 0 }; struct cifs_credits credits = { .value = 0, .instance = 0 };
struct smb_rqst rqst = { .rq_iov = rdata->iov, struct smb_rqst rqst = { .rq_iov = &rdata->iov[1],
.rq_nvec = 2, .rq_nvec = 1,
.rq_pages = rdata->pages, .rq_pages = rdata->pages,
.rq_offset = rdata->page_offset, .rq_offset = rdata->page_offset,
.rq_npages = rdata->nr_pages, .rq_npages = rdata->nr_pages,
@ -3468,7 +3595,7 @@ smb2_readv_callback(struct mid_q_entry *mid)
rc = smb2_verify_signature(&rqst, server); rc = smb2_verify_signature(&rqst, server);
if (rc) if (rc)
cifs_dbg(VFS, "SMB signature verification returned error = %d\n", cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n",
rc); rc);
} }
/* FIXME: should this be counted toward the initiating task? */ /* FIXME: should this be counted toward the initiating task? */
@ -3595,7 +3722,7 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
unsigned int *nbytes, char **buf, int *buf_type) unsigned int *nbytes, char **buf, int *buf_type)
{ {
struct smb_rqst rqst; struct smb_rqst rqst;
int resp_buftype, rc = -EACCES; int resp_buftype, rc;
struct smb2_read_plain_req *req = NULL; struct smb2_read_plain_req *req = NULL;
struct smb2_read_rsp *rsp = NULL; struct smb2_read_rsp *rsp = NULL;
struct kvec iov[1]; struct kvec iov[1];
@ -4058,7 +4185,7 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1;
break; break;
default: default:
cifs_dbg(VFS, "info level %u isn't supported\n", cifs_tcon_dbg(VFS, "info level %u isn't supported\n",
srch_inf->info_level); srch_inf->info_level);
rc = -EINVAL; rc = -EINVAL;
goto qdir_exit; goto qdir_exit;
@ -4149,7 +4276,7 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
else if (resp_buftype == CIFS_SMALL_BUFFER) else if (resp_buftype == CIFS_SMALL_BUFFER)
srch_inf->smallBuf = true; srch_inf->smallBuf = true;
else else
cifs_dbg(VFS, "illegal search buffer type\n"); cifs_tcon_dbg(VFS, "illegal search buffer type\n");
trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid,
tcon->ses->Suid, index, srch_inf->entries_in_buffer); tcon->ses->Suid, index, srch_inf->entries_in_buffer);

View File

@ -143,7 +143,9 @@ struct smb2_transform_hdr {
#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) #define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002)
#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) #define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004)
#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) #define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008)
#define SMB2_FLAGS_PRIORITY_MASK cpu_to_le32(0x00000070) /* SMB3.1.1 */
#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) #define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000)
#define SMB2_FLAGS_REPLAY_OPERATION cpu_to_le32(0x20000000) /* SMB3 & up */
/* /*
* Definitions for SMB2 Protocol Data Units (network frames) * Definitions for SMB2 Protocol Data Units (network frames)

View File

@ -158,6 +158,10 @@ extern int SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
extern void SMB2_close_free(struct smb_rqst *rqst); extern void SMB2_close_free(struct smb_rqst *rqst);
extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id); u64 persistent_file_id, u64 volatile_file_id);
extern int SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst,
struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id);
extern void SMB2_flush_free(struct smb_rqst *rqst);
extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id, u64 persistent_file_id, u64 volatile_file_id,
struct smb2_file_all_info *data); struct smb2_file_all_info *data);

View File

@ -176,7 +176,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
ses = smb2_find_smb_ses(server, shdr->SessionId); ses = smb2_find_smb_ses(server, shdr->SessionId);
if (!ses) { if (!ses) {
cifs_dbg(VFS, "%s: Could not find session\n", __func__); cifs_server_dbg(VFS, "%s: Could not find session\n", __func__);
return 0; return 0;
} }
@ -185,21 +185,21 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
rc = smb2_crypto_shash_allocate(server); rc = smb2_crypto_shash_allocate(server);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: sha256 alloc failed\n", __func__); cifs_server_dbg(VFS, "%s: sha256 alloc failed\n", __func__);
return rc; return rc;
} }
rc = crypto_shash_setkey(server->secmech.hmacsha256, rc = crypto_shash_setkey(server->secmech.hmacsha256,
ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with response\n", __func__); cifs_server_dbg(VFS, "%s: Could not update with response\n", __func__);
return rc; return rc;
} }
shash = &server->secmech.sdeschmacsha256->shash; shash = &server->secmech.sdeschmacsha256->shash;
rc = crypto_shash_init(shash); rc = crypto_shash_init(shash);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not init sha256", __func__); cifs_server_dbg(VFS, "%s: Could not init sha256", __func__);
return rc; return rc;
} }
@ -215,7 +215,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
rc = crypto_shash_update(shash, iov[0].iov_base, rc = crypto_shash_update(shash, iov[0].iov_base,
iov[0].iov_len); iov[0].iov_len);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with payload\n", cifs_server_dbg(VFS, "%s: Could not update with payload\n",
__func__); __func__);
return rc; return rc;
} }
@ -239,68 +239,69 @@ static int generate_key(struct cifs_ses *ses, struct kvec label,
int rc = 0; int rc = 0;
unsigned char prfhash[SMB2_HMACSHA256_SIZE]; unsigned char prfhash[SMB2_HMACSHA256_SIZE];
unsigned char *hashptr = prfhash; unsigned char *hashptr = prfhash;
struct TCP_Server_Info *server = ses->server;
memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
memset(key, 0x0, key_size); memset(key, 0x0, key_size);
rc = smb3_crypto_shash_allocate(ses->server); rc = smb3_crypto_shash_allocate(server);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_setkey(ses->server->secmech.hmacsha256, rc = crypto_shash_setkey(server->secmech.hmacsha256,
ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not set with session key\n", __func__); cifs_server_dbg(VFS, "%s: Could not set with session key\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_init(&ses->server->secmech.sdeschmacsha256->shash); rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not init sign hmac\n", __func__); cifs_server_dbg(VFS, "%s: Could not init sign hmac\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
i, 4); i, 4);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with n\n", __func__); cifs_server_dbg(VFS, "%s: Could not update with n\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
label.iov_base, label.iov_len); label.iov_base, label.iov_len);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with label\n", __func__); cifs_server_dbg(VFS, "%s: Could not update with label\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
&zero, 1); &zero, 1);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with zero\n", __func__); cifs_server_dbg(VFS, "%s: Could not update with zero\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
context.iov_base, context.iov_len); context.iov_base, context.iov_len);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with context\n", __func__); cifs_server_dbg(VFS, "%s: Could not update with context\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_update(&ses->server->secmech.sdeschmacsha256->shash, rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
L, 4); L, 4);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with L\n", __func__); cifs_server_dbg(VFS, "%s: Could not update with L\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
rc = crypto_shash_final(&ses->server->secmech.sdeschmacsha256->shash, rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
hashptr); hashptr);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); cifs_server_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__);
goto smb3signkey_ret; goto smb3signkey_ret;
} }
@ -436,7 +437,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
ses = smb2_find_smb_ses(server, shdr->SessionId); ses = smb2_find_smb_ses(server, shdr->SessionId);
if (!ses) { if (!ses) {
cifs_dbg(VFS, "%s: Could not find session\n", __func__); cifs_server_dbg(VFS, "%s: Could not find session\n", __func__);
return 0; return 0;
} }
@ -446,7 +447,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
rc = crypto_shash_setkey(server->secmech.cmacaes, rc = crypto_shash_setkey(server->secmech.cmacaes,
ses->smb3signingkey, SMB2_CMACAES_SIZE); ses->smb3signingkey, SMB2_CMACAES_SIZE);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
return rc; return rc;
} }
@ -457,7 +458,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
*/ */
rc = crypto_shash_init(shash); rc = crypto_shash_init(shash);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__); cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
return rc; return rc;
} }
@ -473,7 +474,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
rc = crypto_shash_update(shash, iov[0].iov_base, rc = crypto_shash_update(shash, iov[0].iov_base,
iov[0].iov_len); iov[0].iov_len);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with payload\n", cifs_server_dbg(VFS, "%s: Could not update with payload\n",
__func__); __func__);
return rc; return rc;
} }
@ -521,6 +522,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
if ((shdr->Command == SMB2_NEGOTIATE) || if ((shdr->Command == SMB2_NEGOTIATE) ||
(shdr->Command == SMB2_SESSION_SETUP) || (shdr->Command == SMB2_SESSION_SETUP) ||
(shdr->Command == SMB2_OPLOCK_BREAK) || (shdr->Command == SMB2_OPLOCK_BREAK) ||
server->ignore_signature ||
(!server->session_estab)) (!server->session_estab))
return 0; return 0;
@ -665,7 +667,7 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
rc = smb2_verify_signature(&rqst, server); rc = smb2_verify_signature(&rqst, server);
if (rc) if (rc)
cifs_dbg(VFS, "SMB signature verification returned error = %d\n", cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
rc); rc);
} }
@ -739,7 +741,7 @@ smb3_crypto_aead_allocate(struct TCP_Server_Info *server)
else else
tfm = crypto_alloc_aead("ccm(aes)", 0, 0); tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
if (IS_ERR(tfm)) { if (IS_ERR(tfm)) {
cifs_dbg(VFS, "%s: Failed to alloc encrypt aead\n", cifs_server_dbg(VFS, "%s: Failed to alloc encrypt aead\n",
__func__); __func__);
return PTR_ERR(tfm); return PTR_ERR(tfm);
} }
@ -754,7 +756,7 @@ smb3_crypto_aead_allocate(struct TCP_Server_Info *server)
if (IS_ERR(tfm)) { if (IS_ERR(tfm)) {
crypto_free_aead(server->secmech.ccmaesencrypt); crypto_free_aead(server->secmech.ccmaesencrypt);
server->secmech.ccmaesencrypt = NULL; server->secmech.ccmaesencrypt = NULL;
cifs_dbg(VFS, "%s: Failed to alloc decrypt aead\n", cifs_server_dbg(VFS, "%s: Failed to alloc decrypt aead\n",
__func__); __func__);
return PTR_ERR(tfm); return PTR_ERR(tfm);
} }

View File

@ -117,6 +117,41 @@ DEFINE_SMB3_RW_DONE_EVENT(falloc_done);
/* /*
* For handle based calls other than read and write, and get/set info * For handle based calls other than read and write, and get/set info
*/ */
DECLARE_EVENT_CLASS(smb3_fd_class,
TP_PROTO(unsigned int xid,
__u64 fid,
__u32 tid,
__u64 sesid),
TP_ARGS(xid, fid, tid, sesid),
TP_STRUCT__entry(
__field(unsigned int, xid)
__field(__u64, fid)
__field(__u32, tid)
__field(__u64, sesid)
),
TP_fast_assign(
__entry->xid = xid;
__entry->fid = fid;
__entry->tid = tid;
__entry->sesid = sesid;
),
TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx",
__entry->xid, __entry->sesid, __entry->tid, __entry->fid)
)
#define DEFINE_SMB3_FD_EVENT(name) \
DEFINE_EVENT(smb3_fd_class, smb3_##name, \
TP_PROTO(unsigned int xid, \
__u64 fid, \
__u32 tid, \
__u64 sesid), \
TP_ARGS(xid, fid, tid, sesid))
DEFINE_SMB3_FD_EVENT(flush_enter);
DEFINE_SMB3_FD_EVENT(flush_done);
DEFINE_SMB3_FD_EVENT(close_enter);
DEFINE_SMB3_FD_EVENT(close_done);
DECLARE_EVENT_CLASS(smb3_fd_err_class, DECLARE_EVENT_CLASS(smb3_fd_err_class,
TP_PROTO(unsigned int xid, TP_PROTO(unsigned int xid,
__u64 fid, __u64 fid,
@ -200,6 +235,8 @@ DEFINE_EVENT(smb3_inf_enter_class, smb3_##name, \
DEFINE_SMB3_INF_ENTER_EVENT(query_info_enter); DEFINE_SMB3_INF_ENTER_EVENT(query_info_enter);
DEFINE_SMB3_INF_ENTER_EVENT(query_info_done); DEFINE_SMB3_INF_ENTER_EVENT(query_info_done);
DEFINE_SMB3_INF_ENTER_EVENT(notify_enter);
DEFINE_SMB3_INF_ENTER_EVENT(notify_done);
DECLARE_EVENT_CLASS(smb3_inf_err_class, DECLARE_EVENT_CLASS(smb3_inf_err_class,
TP_PROTO(unsigned int xid, TP_PROTO(unsigned int xid,
@ -246,6 +283,7 @@ DEFINE_EVENT(smb3_inf_err_class, smb3_##name, \
DEFINE_SMB3_INF_ERR_EVENT(query_info_err); DEFINE_SMB3_INF_ERR_EVENT(query_info_err);
DEFINE_SMB3_INF_ERR_EVENT(set_info_err); DEFINE_SMB3_INF_ERR_EVENT(set_info_err);
DEFINE_SMB3_INF_ERR_EVENT(notify_err);
DEFINE_SMB3_INF_ERR_EVENT(fsctl_err); DEFINE_SMB3_INF_ERR_EVENT(fsctl_err);
DECLARE_EVENT_CLASS(smb3_inf_compound_enter_class, DECLARE_EVENT_CLASS(smb3_inf_compound_enter_class,

View File

@ -118,7 +118,7 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
now = jiffies; now = jiffies;
if (now < midEntry->when_alloc) if (now < midEntry->when_alloc)
cifs_dbg(VFS, "invalid mid allocation time\n"); cifs_server_dbg(VFS, "invalid mid allocation time\n");
roundtrip_time = now - midEntry->when_alloc; roundtrip_time = now - midEntry->when_alloc;
if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) { if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) {
@ -232,7 +232,7 @@ smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg,
retries++; retries++;
if (retries >= 14 || if (retries >= 14 ||
(!server->noblocksnd && (retries > 2))) { (!server->noblocksnd && (retries > 2))) {
cifs_dbg(VFS, "sends on sock %p stuck for 15 seconds\n", cifs_server_dbg(VFS, "sends on sock %p stuck for 15 seconds\n",
ssocket); ssocket);
return -EAGAIN; return -EAGAIN;
} }
@ -246,7 +246,7 @@ smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg,
if (rc == 0) { if (rc == 0) {
/* should never happen, letting socket clear before /* should never happen, letting socket clear before
retrying is our only obvious option here */ retrying is our only obvious option here */
cifs_dbg(VFS, "tcp sent no data\n"); cifs_server_dbg(VFS, "tcp sent no data\n");
msleep(500); msleep(500);
continue; continue;
} }
@ -440,7 +440,7 @@ unmask:
} }
smbd_done: smbd_done:
if (rc < 0 && rc != -EINTR) if (rc < 0 && rc != -EINTR)
cifs_dbg(VFS, "Error %d sending data on socket to server\n", cifs_server_dbg(VFS, "Error %d sending data on socket to server\n",
rc); rc);
else if (rc > 0) else if (rc > 0)
rc = 0; rc = 0;
@ -473,8 +473,8 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
cur_rqst[0].rq_nvec = 1; cur_rqst[0].rq_nvec = 1;
if (!server->ops->init_transform_rq) { if (!server->ops->init_transform_rq) {
cifs_dbg(VFS, "Encryption requested but transform callback " cifs_server_dbg(VFS, "Encryption requested but transform "
"is missing\n"); "callback is missing\n");
return -EIO; return -EIO;
} }
@ -532,6 +532,8 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
if ((flags & CIFS_TIMEOUT_MASK) == CIFS_NON_BLOCKING) { if ((flags & CIFS_TIMEOUT_MASK) == CIFS_NON_BLOCKING) {
/* oplock breaks must not be held up */ /* oplock breaks must not be held up */
server->in_flight++; server->in_flight++;
if (server->in_flight > server->max_in_flight)
server->max_in_flight = server->in_flight;
*credits -= 1; *credits -= 1;
*instance = server->reconnect_instance; *instance = server->reconnect_instance;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
@ -548,7 +550,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
if (!rc) { if (!rc) {
trace_smb3_credit_timeout(server->CurrentMid, trace_smb3_credit_timeout(server->CurrentMid,
server->hostname, num_credits); server->hostname, num_credits);
cifs_dbg(VFS, "wait timed out after %d ms\n", cifs_server_dbg(VFS, "wait timed out after %d ms\n",
timeout); timeout);
return -ENOTSUPP; return -ENOTSUPP;
} }
@ -589,7 +591,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
trace_smb3_credit_timeout( trace_smb3_credit_timeout(
server->CurrentMid, server->CurrentMid,
server->hostname, num_credits); server->hostname, num_credits);
cifs_dbg(VFS, "wait timed out after %d ms\n", cifs_server_dbg(VFS, "wait timed out after %d ms\n",
timeout); timeout);
return -ENOTSUPP; return -ENOTSUPP;
} }
@ -608,6 +610,8 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) { if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) {
*credits -= num_credits; *credits -= num_credits;
server->in_flight += num_credits; server->in_flight += num_credits;
if (server->in_flight > server->max_in_flight)
server->max_in_flight = server->in_flight;
*instance = server->reconnect_instance; *instance = server->reconnect_instance;
} }
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
@ -869,7 +873,7 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
break; break;
default: default:
list_del_init(&mid->qhead); list_del_init(&mid->qhead);
cifs_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n", cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n",
__func__, mid->mid, mid->mid_state); __func__, mid->mid, mid->mid_state);
rc = -EIO; rc = -EIO;
} }
@ -910,7 +914,7 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
rc = cifs_verify_signature(&rqst, server, rc = cifs_verify_signature(&rqst, server,
mid->sequence_number); mid->sequence_number);
if (rc) if (rc)
cifs_dbg(VFS, "SMB signature verification returned error = %d\n", cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
rc); rc);
} }
@ -1107,7 +1111,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
} }
if (rc != 0) { if (rc != 0) {
for (; i < num_rqst; i++) { for (; i < num_rqst; i++) {
cifs_dbg(VFS, "Cancelling wait for mid %llu cmd: %d\n", cifs_server_dbg(VFS, "Cancelling wait for mid %llu cmd: %d\n",
midQ[i]->mid, le16_to_cpu(midQ[i]->command)); midQ[i]->mid, le16_to_cpu(midQ[i]->command));
send_cancel(server, &rqst[i], midQ[i]); send_cancel(server, &rqst[i], midQ[i]);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
@ -1242,17 +1246,19 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
struct kvec iov = { .iov_base = in_buf, .iov_len = len }; struct kvec iov = { .iov_base = in_buf, .iov_len = len };
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
struct cifs_credits credits = { .value = 1, .instance = 0 }; struct cifs_credits credits = { .value = 1, .instance = 0 };
struct TCP_Server_Info *server;
if (ses == NULL) { if (ses == NULL) {
cifs_dbg(VFS, "Null smb session\n"); cifs_dbg(VFS, "Null smb session\n");
return -EIO; return -EIO;
} }
if (ses->server == NULL) { server = ses->server;
if (server == NULL) {
cifs_dbg(VFS, "Null tcp session\n"); cifs_dbg(VFS, "Null tcp session\n");
return -EIO; return -EIO;
} }
if (ses->server->tcpStatus == CifsExiting) if (server->tcpStatus == CifsExiting)
return -ENOENT; return -ENOENT;
/* Ensure that we do not send more than 50 overlapping requests /* Ensure that we do not send more than 50 overlapping requests
@ -1260,12 +1266,12 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
use ses->maxReq */ use ses->maxReq */
if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", cifs_server_dbg(VFS, "Illegal length, greater than maximum frame, %d\n",
len); len);
return -EIO; return -EIO;
} }
rc = wait_for_free_request(ses->server, flags, &credits.instance); rc = wait_for_free_request(server, flags, &credits.instance);
if (rc) if (rc)
return rc; return rc;
@ -1273,70 +1279,70 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
and avoid races inside tcp sendmsg code that could cause corruption and avoid races inside tcp sendmsg code that could cause corruption
of smb data */ of smb data */
mutex_lock(&ses->server->srv_mutex); mutex_lock(&server->srv_mutex);
rc = allocate_mid(ses, in_buf, &midQ); rc = allocate_mid(ses, in_buf, &midQ);
if (rc) { if (rc) {
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&ses->server->srv_mutex);
/* Update # of requests on wire to server */ /* Update # of requests on wire to server */
add_credits(ses->server, &credits, 0); add_credits(server, &credits, 0);
return rc; return rc;
} }
rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
if (rc) { if (rc) {
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&server->srv_mutex);
goto out; goto out;
} }
midQ->mid_state = MID_REQUEST_SUBMITTED; midQ->mid_state = MID_REQUEST_SUBMITTED;
cifs_in_send_inc(ses->server); cifs_in_send_inc(server);
rc = smb_send(ses->server, in_buf, len); rc = smb_send(server, in_buf, len);
cifs_in_send_dec(ses->server); cifs_in_send_dec(server);
cifs_save_when_sent(midQ); cifs_save_when_sent(midQ);
if (rc < 0) if (rc < 0)
ses->server->sequence_number -= 2; server->sequence_number -= 2;
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&server->srv_mutex);
if (rc < 0) if (rc < 0)
goto out; goto out;
rc = wait_for_response(ses->server, midQ); rc = wait_for_response(server, midQ);
if (rc != 0) { if (rc != 0) {
send_cancel(ses->server, &rqst, midQ); send_cancel(server, &rqst, midQ);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (midQ->mid_state == MID_REQUEST_SUBMITTED) { if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
/* no longer considered to be "in-flight" */ /* no longer considered to be "in-flight" */
midQ->callback = DeleteMidQEntry; midQ->callback = DeleteMidQEntry;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
add_credits(ses->server, &credits, 0); add_credits(server, &credits, 0);
return rc; return rc;
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
} }
rc = cifs_sync_mid_result(midQ, ses->server); rc = cifs_sync_mid_result(midQ, server);
if (rc != 0) { if (rc != 0) {
add_credits(ses->server, &credits, 0); add_credits(server, &credits, 0);
return rc; return rc;
} }
if (!midQ->resp_buf || !out_buf || if (!midQ->resp_buf || !out_buf ||
midQ->mid_state != MID_RESPONSE_RECEIVED) { midQ->mid_state != MID_RESPONSE_RECEIVED) {
rc = -EIO; rc = -EIO;
cifs_dbg(VFS, "Bad MID state?\n"); cifs_server_dbg(VFS, "Bad MID state?\n");
goto out; goto out;
} }
*pbytes_returned = get_rfc1002_length(midQ->resp_buf); *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, ses->server, 0); rc = cifs_check_receive(midQ, server, 0);
out: out:
cifs_delete_mid(midQ); cifs_delete_mid(midQ);
add_credits(ses->server, &credits, 0); add_credits(server, &credits, 0);
return rc; return rc;
} }
@ -1379,19 +1385,21 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
struct kvec iov = { .iov_base = in_buf, .iov_len = len }; struct kvec iov = { .iov_base = in_buf, .iov_len = len };
struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 }; struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
unsigned int instance; unsigned int instance;
struct TCP_Server_Info *server;
if (tcon == NULL || tcon->ses == NULL) { if (tcon == NULL || tcon->ses == NULL) {
cifs_dbg(VFS, "Null smb session\n"); cifs_dbg(VFS, "Null smb session\n");
return -EIO; return -EIO;
} }
ses = tcon->ses; ses = tcon->ses;
server = ses->server;
if (ses->server == NULL) { if (server == NULL) {
cifs_dbg(VFS, "Null tcp session\n"); cifs_dbg(VFS, "Null tcp session\n");
return -EIO; return -EIO;
} }
if (ses->server->tcpStatus == CifsExiting) if (server->tcpStatus == CifsExiting)
return -ENOENT; return -ENOENT;
/* Ensure that we do not send more than 50 overlapping requests /* Ensure that we do not send more than 50 overlapping requests
@ -1399,12 +1407,12 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
use ses->maxReq */ use ses->maxReq */
if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", cifs_tcon_dbg(VFS, "Illegal length, greater than maximum frame, %d\n",
len); len);
return -EIO; return -EIO;
} }
rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP, &instance); rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance);
if (rc) if (rc)
return rc; return rc;
@ -1412,31 +1420,31 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
and avoid races inside tcp sendmsg code that could cause corruption and avoid races inside tcp sendmsg code that could cause corruption
of smb data */ of smb data */
mutex_lock(&ses->server->srv_mutex); mutex_lock(&server->srv_mutex);
rc = allocate_mid(ses, in_buf, &midQ); rc = allocate_mid(ses, in_buf, &midQ);
if (rc) { if (rc) {
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&server->srv_mutex);
return rc; return rc;
} }
rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
if (rc) { if (rc) {
cifs_delete_mid(midQ); cifs_delete_mid(midQ);
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&server->srv_mutex);
return rc; return rc;
} }
midQ->mid_state = MID_REQUEST_SUBMITTED; midQ->mid_state = MID_REQUEST_SUBMITTED;
cifs_in_send_inc(ses->server); cifs_in_send_inc(server);
rc = smb_send(ses->server, in_buf, len); rc = smb_send(server, in_buf, len);
cifs_in_send_dec(ses->server); cifs_in_send_dec(server);
cifs_save_when_sent(midQ); cifs_save_when_sent(midQ);
if (rc < 0) if (rc < 0)
ses->server->sequence_number -= 2; server->sequence_number -= 2;
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&server->srv_mutex);
if (rc < 0) { if (rc < 0) {
cifs_delete_mid(midQ); cifs_delete_mid(midQ);
@ -1444,21 +1452,21 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
} }
/* Wait for a reply - allow signals to interrupt. */ /* Wait for a reply - allow signals to interrupt. */
rc = wait_event_interruptible(ses->server->response_q, rc = wait_event_interruptible(server->response_q,
(!(midQ->mid_state == MID_REQUEST_SUBMITTED)) || (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) ||
((ses->server->tcpStatus != CifsGood) && ((server->tcpStatus != CifsGood) &&
(ses->server->tcpStatus != CifsNew))); (server->tcpStatus != CifsNew)));
/* Were we interrupted by a signal ? */ /* Were we interrupted by a signal ? */
if ((rc == -ERESTARTSYS) && if ((rc == -ERESTARTSYS) &&
(midQ->mid_state == MID_REQUEST_SUBMITTED) && (midQ->mid_state == MID_REQUEST_SUBMITTED) &&
((ses->server->tcpStatus == CifsGood) || ((server->tcpStatus == CifsGood) ||
(ses->server->tcpStatus == CifsNew))) { (server->tcpStatus == CifsNew))) {
if (in_buf->Command == SMB_COM_TRANSACTION2) { if (in_buf->Command == SMB_COM_TRANSACTION2) {
/* POSIX lock. We send a NT_CANCEL SMB to cause the /* POSIX lock. We send a NT_CANCEL SMB to cause the
blocking lock to return. */ blocking lock to return. */
rc = send_cancel(ses->server, &rqst, midQ); rc = send_cancel(server, &rqst, midQ);
if (rc) { if (rc) {
cifs_delete_mid(midQ); cifs_delete_mid(midQ);
return rc; return rc;
@ -1477,9 +1485,9 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
} }
} }
rc = wait_for_response(ses->server, midQ); rc = wait_for_response(server, midQ);
if (rc) { if (rc) {
send_cancel(ses->server, &rqst, midQ); send_cancel(server, &rqst, midQ);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (midQ->mid_state == MID_REQUEST_SUBMITTED) { if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
/* no longer considered to be "in-flight" */ /* no longer considered to be "in-flight" */
@ -1494,20 +1502,20 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
rstart = 1; rstart = 1;
} }
rc = cifs_sync_mid_result(midQ, ses->server); rc = cifs_sync_mid_result(midQ, server);
if (rc != 0) if (rc != 0)
return rc; return rc;
/* rcvd frame is ok */ /* rcvd frame is ok */
if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) { if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) {
rc = -EIO; rc = -EIO;
cifs_dbg(VFS, "Bad MID state?\n"); cifs_tcon_dbg(VFS, "Bad MID state?\n");
goto out; goto out;
} }
*pbytes_returned = get_rfc1002_length(midQ->resp_buf); *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, ses->server, 0); rc = cifs_check_receive(midQ, server, 0);
out: out:
cifs_delete_mid(midQ); cifs_delete_mid(midQ);
if (rstart && rc == -EACCES) if (rstart && rc == -EACCES)

View File

@ -8,6 +8,7 @@
enum { enum {
Root_NFS = MKDEV(UNNAMED_MAJOR, 255), Root_NFS = MKDEV(UNNAMED_MAJOR, 255),
Root_CIFS = MKDEV(UNNAMED_MAJOR, 254),
Root_RAM0 = MKDEV(RAMDISK_MAJOR, 0), Root_RAM0 = MKDEV(RAMDISK_MAJOR, 0),
Root_RAM1 = MKDEV(RAMDISK_MAJOR, 1), Root_RAM1 = MKDEV(RAMDISK_MAJOR, 1),
Root_FD0 = MKDEV(FLOPPY_MAJOR, 0), Root_FD0 = MKDEV(FLOPPY_MAJOR, 0),