various tracing and debugging improvements, crediting fixes, some cleanup, and important fallocate fix (fixes three xfstests) and lock fix
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAlyMMnkACgkQiiy9cAdy T1ElsAv/YV7vKbDgJOQfb925LbHqaythYQf8Z9CLwJdjW96k0pNP0bB8KPgw/4dE t0Z1rzEoS7X7A1mh52tUUWEa1ygeOekMankJZtXzkMe2rl9m846jO/ynUDB0CFlE 5OuRdFpjSMlTdHIRw8F5GTBwO8PM/MYWvoNyO9+foJp+Z/rFtTtrPuAcJvr3NP/O vyOXXVZ+xbqWYe1s/WGzk04Fzm6gB5V0BQyUZmmf3jZen+5vmDKRa2QMlqk0tt5O DDZYj8utkgSGtEapWPWzgWU9gIWNSN5GdeKprIGLwESKxMrGrZiZDErpHDzwPKJX MMPlZVvpU7BYtnMQCe82EQ74Nu/YDcMCCQjnaQDWcbQVEM/bt7Z4RXVEFcVsFO9s aXwK3iRYYjLcIxuBxM3NWeZMPa5C4u6rCMjDNp91oKm5OZtJrZmB4JOHGwoeVYEF pJZhT/txmuws828qLmuVCh9IOKouzRH3UxZ/PBKMEtnix9rX7juqSaHCh8pxlW+1 3vQdxnx2 =dG+z -----END PGP SIGNATURE----- Merge tag '5.1-rc-smb3' of git://git.samba.org/sfrench/cifs-2.6 Pull more smb3 updates from Steve French: "Various tracing and debugging improvements, crediting fixes, some cleanup, and important fallocate fix (fixes three xfstests) and lock fix. Summary: - Various additional dynamic tracing tracepoints - Debugging improvements (including ability to query the server via SMB3 fsctl from userspace tools which can help with stats and debugging) - One minor performance improvement (root directory inode caching) - Crediting (SMB3 flow control) fixes - Some cleanup (docs and to mknod) - Important fixes: one to smb3 implementation of fallocate zero range (which fixes three xfstests) and a POSIX lock fix" * tag '5.1-rc-smb3' of git://git.samba.org/sfrench/cifs-2.6: (22 commits) CIFS: fix POSIX lock leak and invalid ptr deref SMB3: Allow SMB3 FSCTL queries to be sent to server from tools cifs: fix incorrect handling of smb2_set_sparse() return in smb3_simple_falloc smb2: fix typo in definition of a few error flags CIFS: make mknod() an smb_version_op cifs: minor documentation updates cifs: remove unused value pointed out by Coverity SMB3: passthru query info doesn't check for SMB3 FSCTL passthru smb3: add dynamic tracepoints for simple fallocate and zero range cifs: fix smb3_zero_range so it can expand the file-size when required cifs: add SMB2_ioctl_init/free helpers to be used with compounding smb3: Add dynamic trace points for various compounded smb3 ops cifs: cache FILE_ALL_INFO for the shared root handle smb3: display volume serial number for shares in /proc/fs/cifs/DebugData cifs: simplify how we handle credits in compound_send_recv() smb3: add dynamic tracepoint for timeout waiting for credits smb3: display security information in /proc/fs/cifs/DebugData more accurately cifs: add a timeout argument to wait_for_free_credits cifs: prevent starvation in wait_for_free_credits for multi-credit requests cifs: wait_for_free_credits() make it possible to wait for >=1 credits ...
This commit is contained in:
commit
9c7dc824d9
|
@ -111,7 +111,8 @@ negotiated size) and send larger write sizes to modern servers.
|
|||
|
||||
5) Continue to extend the smb3 "buildbot" which does automated xfstesting
|
||||
against Windows, Samba and Azure currently - to add additional tests and
|
||||
to allow the buildbot to execute the tests faster.
|
||||
to allow the buildbot to execute the tests faster. The URL for the
|
||||
buildbot is: http://smb3-test-rhel-75.southcentralus.cloudapp.azure.com
|
||||
|
||||
6) Address various coverity warnings (most are not bugs per-se, but
|
||||
the more warnings are addressed, the easier it is to spot real
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
This is the client VFS module for the SMB3 NAS protocol as well
|
||||
older dialects such as the Common Internet File System (CIFS)
|
||||
as for older dialects such as the Common Internet File System (CIFS)
|
||||
protocol which was the successor to the Server Message Block
|
||||
(SMB) protocol, the native file sharing mechanism for most early
|
||||
PC operating systems. New and improved versions of CIFS are now
|
||||
called SMB2 and SMB3. These dialects are also supported by the
|
||||
CIFS VFS module. CIFS is fully supported by network
|
||||
file servers such as Windows 2000, 2003, 2008, 2012 and 2016
|
||||
as well by Samba (which provides excellent CIFS
|
||||
server support for Linux and many other operating systems), Apple
|
||||
systems, as well as most Network Attached Storage vendors, so
|
||||
this network filesystem client can mount to a wide variety of
|
||||
servers.
|
||||
called SMB2 and SMB3. Use of SMB3 (and later, including SMB3.1.1)
|
||||
is strongly preferred over using older dialects like CIFS due to
|
||||
security reaasons. All modern dialects, including the most recent,
|
||||
SMB3.1.1 are supported by the CIFS VFS module. The SMB3 protocol
|
||||
is implemented and supported by all major file servers
|
||||
such as all modern versions of Windows (including Windows 2016
|
||||
Server), as well as by Samba (which provides excellent
|
||||
CIFS/SMB2/SMB3 server support and tools for Linux and many other
|
||||
operating systems). Apple systems also support SMB3 well, as
|
||||
do most Network Attached Storage vendors, so this network
|
||||
filesystem client can mount to a wide variety of systems.
|
||||
It also supports mounting to the cloud (for example
|
||||
Microsoft Azure), including the necessary security features.
|
||||
|
||||
The intent of this module is to provide the most advanced network
|
||||
file system function for SMB3 compliant servers, including advanced
|
||||
|
@ -24,12 +29,17 @@
|
|||
cluster file systems for fileserving in some Linux to Linux environments,
|
||||
not just in Linux to Windows (or Linux to Mac) environments.
|
||||
|
||||
This filesystem has an mount utility (mount.cifs) that can be obtained from
|
||||
This filesystem has a mount utility (mount.cifs) and various user space
|
||||
tools (including smbinfo and setcifsacl) that can be obtained from
|
||||
|
||||
https://ftp.samba.org/pub/linux-cifs/cifs-utils/
|
||||
https://git.samba.org/?p=cifs-utils.git
|
||||
or
|
||||
git://git.samba.org/cifs-utils.git
|
||||
|
||||
It must be installed in the directory with the other mount helpers.
|
||||
mount.cifs should be installed in the directory with the other mount helpers.
|
||||
|
||||
For more information on the module see the project wiki page at
|
||||
|
||||
https://wiki.samba.org/index.php/LinuxCIFS
|
||||
and
|
||||
https://wiki.samba.org/index.php/LinuxCIFS_utils
|
||||
|
|
|
@ -115,7 +115,12 @@ static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon)
|
|||
seq_puts(m, " type: CDROM ");
|
||||
else
|
||||
seq_printf(m, " type: %d ", dev_type);
|
||||
if (tcon->seal)
|
||||
|
||||
seq_printf(m, "Serial Number: 0x%x", tcon->vol_serial_number);
|
||||
|
||||
if ((tcon->seal) ||
|
||||
(tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) ||
|
||||
(tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA))
|
||||
seq_printf(m, " Encrypted");
|
||||
if (tcon->nocase)
|
||||
seq_printf(m, " nocase");
|
||||
|
@ -371,6 +376,10 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
|||
atomic_read(&server->in_send),
|
||||
atomic_read(&server->num_waiters));
|
||||
#endif
|
||||
if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
|
||||
seq_puts(m, " encrypted");
|
||||
if (ses->sign)
|
||||
seq_puts(m, " signed");
|
||||
|
||||
seq_puts(m, "\n\tShares:");
|
||||
j = 0;
|
||||
|
|
|
@ -43,6 +43,9 @@ struct smb_snapshot_array {
|
|||
/* snapshots[]; */
|
||||
} __packed;
|
||||
|
||||
/* query_info flags */
|
||||
#define PASSTHRU_QUERY_INFO 0x00000000
|
||||
#define PASSTHRU_FSCTL 0x00000001
|
||||
struct smb_query_info {
|
||||
__u32 info_type;
|
||||
__u32 file_info_class;
|
||||
|
|
|
@ -479,6 +479,14 @@ struct smb_version_operations {
|
|||
struct cifs_tcon *tcon,
|
||||
__le16 *path, int is_dir,
|
||||
unsigned long p);
|
||||
/* make unix special files (block, char, fifo, socket) */
|
||||
int (*make_node)(unsigned int xid,
|
||||
struct inode *inode,
|
||||
struct dentry *dentry,
|
||||
struct cifs_tcon *tcon,
|
||||
char *full_path,
|
||||
umode_t mode,
|
||||
dev_t device_number);
|
||||
};
|
||||
|
||||
struct smb_version_values {
|
||||
|
@ -735,13 +743,13 @@ in_flight(struct TCP_Server_Info *server)
|
|||
}
|
||||
|
||||
static inline bool
|
||||
has_credits(struct TCP_Server_Info *server, int *credits)
|
||||
has_credits(struct TCP_Server_Info *server, int *credits, int num_credits)
|
||||
{
|
||||
int num;
|
||||
spin_lock(&server->req_lock);
|
||||
num = *credits;
|
||||
spin_unlock(&server->req_lock);
|
||||
return num > 0;
|
||||
return num >= num_credits;
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -962,11 +970,14 @@ cap_unix(struct cifs_ses *ses)
|
|||
|
||||
struct cached_fid {
|
||||
bool is_valid:1; /* Do we have a useable root fid */
|
||||
bool file_all_info_is_valid:1;
|
||||
|
||||
struct kref refcount;
|
||||
struct cifs_fid *fid;
|
||||
struct mutex fid_mutex;
|
||||
struct cifs_tcon *tcon;
|
||||
struct work_struct lease_break;
|
||||
struct smb2_file_all_info file_all_info;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1735,6 +1746,7 @@ require use of the stronger protocol */
|
|||
* GlobalMid_Lock protects:
|
||||
* list operations on pending_mid_q and oplockQ
|
||||
* updates to XID counters, multiplex id and SMB sequence numbers
|
||||
* list operations on global DnotifyReqList
|
||||
* tcp_ses_lock protects:
|
||||
* list operations on tcp and SMB session lists
|
||||
* tcon->open_file_lock protects the list of open files hanging off the tcon
|
||||
|
|
|
@ -1191,10 +1191,6 @@ cifs_demultiplex_thread(void *p)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (server->large_buf)
|
||||
buf = server->bigbuf;
|
||||
|
||||
|
||||
server->lstrp = jiffies;
|
||||
|
||||
for (i = 0; i < num_mids; i++) {
|
||||
|
|
107
fs/cifs/dir.c
107
fs/cifs/dir.c
|
@ -621,20 +621,10 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
|
|||
{
|
||||
int rc = -EPERM;
|
||||
unsigned int xid;
|
||||
int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct tcon_link *tlink;
|
||||
struct cifs_tcon *tcon;
|
||||
struct cifs_io_parms io_parms;
|
||||
char *full_path = NULL;
|
||||
struct inode *newinode = NULL;
|
||||
__u32 oplock = 0;
|
||||
struct cifs_fid fid;
|
||||
struct cifs_open_parms oparms;
|
||||
FILE_ALL_INFO *buf = NULL;
|
||||
unsigned int bytes_written;
|
||||
struct win_dev *pdev;
|
||||
struct kvec iov[2];
|
||||
|
||||
if (!old_valid_dev(device_number))
|
||||
return -EINVAL;
|
||||
|
@ -654,103 +644,12 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
|
|||
goto mknod_out;
|
||||
}
|
||||
|
||||
if (tcon->unix_ext) {
|
||||
struct cifs_unix_set_info_args args = {
|
||||
.mode = mode & ~current_umask(),
|
||||
.ctime = NO_CHANGE_64,
|
||||
.atime = NO_CHANGE_64,
|
||||
.mtime = NO_CHANGE_64,
|
||||
.device = device_number,
|
||||
};
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
|
||||
args.uid = current_fsuid();
|
||||
args.gid = current_fsgid();
|
||||
} else {
|
||||
args.uid = INVALID_UID; /* no change */
|
||||
args.gid = INVALID_GID; /* no change */
|
||||
}
|
||||
rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
|
||||
cifs_sb->local_nls,
|
||||
cifs_remap(cifs_sb));
|
||||
if (rc)
|
||||
goto mknod_out;
|
||||
|
||||
rc = cifs_get_inode_info_unix(&newinode, full_path,
|
||||
inode->i_sb, xid);
|
||||
|
||||
if (rc == 0)
|
||||
d_instantiate(direntry, newinode);
|
||||
goto mknod_out;
|
||||
}
|
||||
|
||||
if (!S_ISCHR(mode) && !S_ISBLK(mode))
|
||||
goto mknod_out;
|
||||
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
|
||||
goto mknod_out;
|
||||
|
||||
|
||||
cifs_dbg(FYI, "sfu compat create special file\n");
|
||||
|
||||
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto mknod_out;
|
||||
}
|
||||
|
||||
if (backup_cred(cifs_sb))
|
||||
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
||||
|
||||
oparms.tcon = tcon;
|
||||
oparms.cifs_sb = cifs_sb;
|
||||
oparms.desired_access = GENERIC_WRITE;
|
||||
oparms.create_options = create_options;
|
||||
oparms.disposition = FILE_CREATE;
|
||||
oparms.path = full_path;
|
||||
oparms.fid = &fid;
|
||||
oparms.reconnect = false;
|
||||
|
||||
if (tcon->ses->server->oplocks)
|
||||
oplock = REQ_OPLOCK;
|
||||
else
|
||||
oplock = 0;
|
||||
rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
|
||||
if (rc)
|
||||
goto mknod_out;
|
||||
|
||||
/*
|
||||
* BB Do not bother to decode buf since no local inode yet to put
|
||||
* timestamps in, but we can reuse it safely.
|
||||
*/
|
||||
|
||||
pdev = (struct win_dev *)buf;
|
||||
io_parms.pid = current->tgid;
|
||||
io_parms.tcon = tcon;
|
||||
io_parms.offset = 0;
|
||||
io_parms.length = sizeof(struct win_dev);
|
||||
iov[1].iov_base = buf;
|
||||
iov[1].iov_len = sizeof(struct win_dev);
|
||||
if (S_ISCHR(mode)) {
|
||||
memcpy(pdev->type, "IntxCHR", 8);
|
||||
pdev->major = cpu_to_le64(MAJOR(device_number));
|
||||
pdev->minor = cpu_to_le64(MINOR(device_number));
|
||||
rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
|
||||
&bytes_written, iov, 1);
|
||||
} else if (S_ISBLK(mode)) {
|
||||
memcpy(pdev->type, "IntxBLK", 8);
|
||||
pdev->major = cpu_to_le64(MAJOR(device_number));
|
||||
pdev->minor = cpu_to_le64(MINOR(device_number));
|
||||
rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
|
||||
&bytes_written, iov, 1);
|
||||
}
|
||||
tcon->ses->server->ops->close(xid, tcon, &fid);
|
||||
d_drop(direntry);
|
||||
|
||||
/* FIXME: add code here to set EAs */
|
||||
rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon,
|
||||
full_path, mode,
|
||||
device_number);
|
||||
|
||||
mknod_out:
|
||||
kfree(full_path);
|
||||
kfree(buf);
|
||||
free_xid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
|
|
|
@ -1645,8 +1645,20 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
|
|||
rc = server->ops->mand_unlock_range(cfile, flock, xid);
|
||||
|
||||
out:
|
||||
if (flock->fl_flags & FL_POSIX && !rc)
|
||||
if (flock->fl_flags & FL_POSIX) {
|
||||
/*
|
||||
* If this is a request to remove all locks because we
|
||||
* are closing the file, it doesn't matter if the
|
||||
* unlocking failed as both cifs.ko and the SMB server
|
||||
* remove the lock on file close
|
||||
*/
|
||||
if (rc) {
|
||||
cifs_dbg(VFS, "%s failed rc=%d\n", __func__, rc);
|
||||
if (!(flock->fl_flags & FL_CLOSE))
|
||||
return rc;
|
||||
}
|
||||
rc = locks_lock_file_wait(file, flock);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -1027,6 +1027,131 @@ cifs_can_echo(struct TCP_Server_Info *server)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_make_node(unsigned int xid, struct inode *inode,
|
||||
struct dentry *dentry, struct cifs_tcon *tcon,
|
||||
char *full_path, umode_t mode, dev_t dev)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
struct inode *newinode = NULL;
|
||||
int rc = -EPERM;
|
||||
int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
|
||||
FILE_ALL_INFO *buf = NULL;
|
||||
struct cifs_io_parms io_parms;
|
||||
__u32 oplock = 0;
|
||||
struct cifs_fid fid;
|
||||
struct cifs_open_parms oparms;
|
||||
unsigned int bytes_written;
|
||||
struct win_dev *pdev;
|
||||
struct kvec iov[2];
|
||||
|
||||
if (tcon->unix_ext) {
|
||||
/*
|
||||
* SMB1 Unix Extensions: requires server support but
|
||||
* works with all special files
|
||||
*/
|
||||
struct cifs_unix_set_info_args args = {
|
||||
.mode = mode & ~current_umask(),
|
||||
.ctime = NO_CHANGE_64,
|
||||
.atime = NO_CHANGE_64,
|
||||
.mtime = NO_CHANGE_64,
|
||||
.device = dev,
|
||||
};
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
|
||||
args.uid = current_fsuid();
|
||||
args.gid = current_fsgid();
|
||||
} else {
|
||||
args.uid = INVALID_UID; /* no change */
|
||||
args.gid = INVALID_GID; /* no change */
|
||||
}
|
||||
rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
|
||||
cifs_sb->local_nls,
|
||||
cifs_remap(cifs_sb));
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = cifs_get_inode_info_unix(&newinode, full_path,
|
||||
inode->i_sb, xid);
|
||||
|
||||
if (rc == 0)
|
||||
d_instantiate(dentry, newinode);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* SMB1 SFU emulation: should work with all servers, but only
|
||||
* support block and char device (no socket & fifo)
|
||||
*/
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
|
||||
goto out;
|
||||
|
||||
if (!S_ISCHR(mode) && !S_ISBLK(mode))
|
||||
goto out;
|
||||
|
||||
cifs_dbg(FYI, "sfu compat create special file\n");
|
||||
|
||||
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (backup_cred(cifs_sb))
|
||||
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
||||
|
||||
oparms.tcon = tcon;
|
||||
oparms.cifs_sb = cifs_sb;
|
||||
oparms.desired_access = GENERIC_WRITE;
|
||||
oparms.create_options = create_options;
|
||||
oparms.disposition = FILE_CREATE;
|
||||
oparms.path = full_path;
|
||||
oparms.fid = &fid;
|
||||
oparms.reconnect = false;
|
||||
|
||||
if (tcon->ses->server->oplocks)
|
||||
oplock = REQ_OPLOCK;
|
||||
else
|
||||
oplock = 0;
|
||||
rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* BB Do not bother to decode buf since no local inode yet to put
|
||||
* timestamps in, but we can reuse it safely.
|
||||
*/
|
||||
|
||||
pdev = (struct win_dev *)buf;
|
||||
io_parms.pid = current->tgid;
|
||||
io_parms.tcon = tcon;
|
||||
io_parms.offset = 0;
|
||||
io_parms.length = sizeof(struct win_dev);
|
||||
iov[1].iov_base = buf;
|
||||
iov[1].iov_len = sizeof(struct win_dev);
|
||||
if (S_ISCHR(mode)) {
|
||||
memcpy(pdev->type, "IntxCHR", 8);
|
||||
pdev->major = cpu_to_le64(MAJOR(dev));
|
||||
pdev->minor = cpu_to_le64(MINOR(dev));
|
||||
rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
|
||||
&bytes_written, iov, 1);
|
||||
} else if (S_ISBLK(mode)) {
|
||||
memcpy(pdev->type, "IntxBLK", 8);
|
||||
pdev->major = cpu_to_le64(MAJOR(dev));
|
||||
pdev->minor = cpu_to_le64(MINOR(dev));
|
||||
rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
|
||||
&bytes_written, iov, 1);
|
||||
}
|
||||
tcon->ses->server->ops->close(xid, tcon, &fid);
|
||||
d_drop(dentry);
|
||||
|
||||
/* FIXME: add code here to set EAs */
|
||||
out:
|
||||
kfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct smb_version_operations smb1_operations = {
|
||||
.send_cancel = send_nt_cancel,
|
||||
.compare_fids = cifs_compare_fids,
|
||||
|
@ -1110,6 +1235,7 @@ struct smb_version_operations smb1_operations = {
|
|||
.get_acl_by_fid = get_cifs_acl_by_fid,
|
||||
.set_acl = set_cifs_acl,
|
||||
#endif /* CIFS_ACL */
|
||||
.make_node = cifs_make_node,
|
||||
};
|
||||
|
||||
struct smb_version_values smb1_values = {
|
||||
|
|
|
@ -37,6 +37,16 @@
|
|||
#include "smb2pdu.h"
|
||||
#include "smb2proto.h"
|
||||
|
||||
static void
|
||||
free_set_inf_compound(struct smb_rqst *rqst)
|
||||
{
|
||||
if (rqst[1].rq_iov)
|
||||
SMB2_set_info_free(&rqst[1]);
|
||||
if (rqst[2].rq_iov)
|
||||
SMB2_close_free(&rqst[2]);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *full_path,
|
||||
|
@ -112,14 +122,18 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
PATH_MAX * 2, 0, NULL);
|
||||
smb2_set_next_command(tcon, &rqst[num_rqst]);
|
||||
smb2_set_related(&rqst[num_rqst++]);
|
||||
trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid,
|
||||
full_path);
|
||||
break;
|
||||
case SMB2_OP_DELETE:
|
||||
trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path);
|
||||
break;
|
||||
case SMB2_OP_MKDIR:
|
||||
/*
|
||||
* Directories are created through parameters in the
|
||||
* SMB2_open() call.
|
||||
*/
|
||||
trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path);
|
||||
break;
|
||||
case SMB2_OP_RMDIR:
|
||||
memset(&si_iov, 0, sizeof(si_iov));
|
||||
|
@ -135,6 +149,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
SMB2_O_INFO_FILE, 0, data, size);
|
||||
smb2_set_next_command(tcon, &rqst[num_rqst]);
|
||||
smb2_set_related(&rqst[num_rqst++]);
|
||||
trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path);
|
||||
break;
|
||||
case SMB2_OP_SET_EOF:
|
||||
memset(&si_iov, 0, sizeof(si_iov));
|
||||
|
@ -150,6 +165,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
SMB2_O_INFO_FILE, 0, data, size);
|
||||
smb2_set_next_command(tcon, &rqst[num_rqst]);
|
||||
smb2_set_related(&rqst[num_rqst++]);
|
||||
trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);
|
||||
break;
|
||||
case SMB2_OP_SET_INFO:
|
||||
memset(&si_iov, 0, sizeof(si_iov));
|
||||
|
@ -166,6 +182,8 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
SMB2_O_INFO_FILE, 0, data, size);
|
||||
smb2_set_next_command(tcon, &rqst[num_rqst]);
|
||||
smb2_set_related(&rqst[num_rqst++]);
|
||||
trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid,
|
||||
full_path);
|
||||
break;
|
||||
case SMB2_OP_RENAME:
|
||||
memset(&si_iov, 0, sizeof(si_iov));
|
||||
|
@ -190,6 +208,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
SMB2_O_INFO_FILE, 0, data, size);
|
||||
smb2_set_next_command(tcon, &rqst[num_rqst]);
|
||||
smb2_set_related(&rqst[num_rqst++]);
|
||||
trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
|
||||
break;
|
||||
case SMB2_OP_HARDLINK:
|
||||
memset(&si_iov, 0, sizeof(si_iov));
|
||||
|
@ -214,6 +233,7 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
SMB2_O_INFO_FILE, 0, data, size);
|
||||
smb2_set_next_command(tcon, &rqst[num_rqst]);
|
||||
smb2_set_related(&rqst[num_rqst++]);
|
||||
trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
|
||||
break;
|
||||
default:
|
||||
cifs_dbg(VFS, "Invalid command\n");
|
||||
|
@ -252,21 +272,65 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
SMB2_query_info_free(&rqst[1]);
|
||||
if (rqst[2].rq_iov)
|
||||
SMB2_close_free(&rqst[2]);
|
||||
if (rc)
|
||||
trace_smb3_query_info_compound_err(xid, ses->Suid,
|
||||
tcon->tid, rc);
|
||||
else
|
||||
trace_smb3_query_info_compound_done(xid, ses->Suid,
|
||||
tcon->tid);
|
||||
break;
|
||||
case SMB2_OP_DELETE:
|
||||
if (rc)
|
||||
trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc);
|
||||
else
|
||||
trace_smb3_delete_done(xid, ses->Suid, tcon->tid);
|
||||
if (rqst[1].rq_iov)
|
||||
SMB2_close_free(&rqst[1]);
|
||||
break;
|
||||
case SMB2_OP_MKDIR:
|
||||
if (rc)
|
||||
trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc);
|
||||
else
|
||||
trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid);
|
||||
if (rqst[1].rq_iov)
|
||||
SMB2_close_free(&rqst[1]);
|
||||
break;
|
||||
case SMB2_OP_HARDLINK:
|
||||
if (rc)
|
||||
trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc);
|
||||
else
|
||||
trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid);
|
||||
free_set_inf_compound(rqst);
|
||||
break;
|
||||
case SMB2_OP_RENAME:
|
||||
if (rc)
|
||||
trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc);
|
||||
else
|
||||
trace_smb3_rename_done(xid, ses->Suid, tcon->tid);
|
||||
free_set_inf_compound(rqst);
|
||||
break;
|
||||
case SMB2_OP_RMDIR:
|
||||
if (rc)
|
||||
trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc);
|
||||
else
|
||||
trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid);
|
||||
free_set_inf_compound(rqst);
|
||||
break;
|
||||
case SMB2_OP_SET_EOF:
|
||||
if (rc)
|
||||
trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc);
|
||||
else
|
||||
trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid);
|
||||
free_set_inf_compound(rqst);
|
||||
break;
|
||||
case SMB2_OP_SET_INFO:
|
||||
if (rqst[1].rq_iov)
|
||||
SMB2_set_info_free(&rqst[1]);
|
||||
if (rqst[2].rq_iov)
|
||||
SMB2_close_free(&rqst[2]);
|
||||
if (rc)
|
||||
trace_smb3_set_info_compound_err(xid, ses->Suid,
|
||||
tcon->tid, rc);
|
||||
else
|
||||
trace_smb3_set_info_compound_done(xid, ses->Suid,
|
||||
tcon->tid);
|
||||
free_set_inf_compound(rqst);
|
||||
break;
|
||||
}
|
||||
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||
|
@ -309,12 +373,17 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
rc = open_shroot(xid, tcon, &fid);
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = SMB2_query_info(xid, tcon, fid.persistent_fid,
|
||||
fid.volatile_fid, smb2_data);
|
||||
|
||||
if (tcon->crfid.file_all_info_is_valid) {
|
||||
move_smb2_info_to_cifs(data,
|
||||
&tcon->crfid.file_all_info);
|
||||
} else {
|
||||
rc = SMB2_query_info(xid, tcon, fid.persistent_fid,
|
||||
fid.volatile_fid, smb2_data);
|
||||
if (!rc)
|
||||
move_smb2_info_to_cifs(data, smb2_data);
|
||||
}
|
||||
close_shroot(&tcon->crfid);
|
||||
if (rc)
|
||||
goto out;
|
||||
move_smb2_info_to_cifs(data, smb2_data);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
|
@ -185,7 +185,7 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
|
|||
spin_unlock(&server->req_lock);
|
||||
cifs_num_waiters_inc(server);
|
||||
rc = wait_event_killable(server->request_q,
|
||||
has_credits(server, &server->credits));
|
||||
has_credits(server, &server->credits, 1));
|
||||
cifs_num_waiters_dec(server);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
@ -619,6 +619,7 @@ smb2_close_cached_fid(struct kref *ref)
|
|||
SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
|
||||
cfid->fid->volatile_fid);
|
||||
cfid->is_valid = false;
|
||||
cfid->file_all_info_is_valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -643,9 +644,18 @@ smb2_cached_lease_break(struct work_struct *work)
|
|||
*/
|
||||
int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
|
||||
{
|
||||
struct cifs_open_parms oparams;
|
||||
int rc;
|
||||
__le16 srch_path = 0; /* Null - since an open of top of share */
|
||||
struct cifs_ses *ses = tcon->ses;
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
struct cifs_open_parms oparms;
|
||||
struct smb2_create_rsp *o_rsp = NULL;
|
||||
struct smb2_query_info_rsp *qi_rsp = NULL;
|
||||
int resp_buftype[2];
|
||||
struct smb_rqst rqst[2];
|
||||
struct kvec rsp_iov[2];
|
||||
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
|
||||
struct kvec qi_iov[1];
|
||||
int rc, flags = 0;
|
||||
__le16 utf16_path = 0; /* Null - since an open of top of share */
|
||||
u8 oplock = SMB2_OPLOCK_LEVEL_II;
|
||||
|
||||
mutex_lock(&tcon->crfid.fid_mutex);
|
||||
|
@ -657,22 +667,89 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
|
|||
return 0;
|
||||
}
|
||||
|
||||
oparams.tcon = tcon;
|
||||
oparams.create_options = 0;
|
||||
oparams.desired_access = FILE_READ_ATTRIBUTES;
|
||||
oparams.disposition = FILE_OPEN;
|
||||
oparams.fid = pfid;
|
||||
oparams.reconnect = false;
|
||||
if (smb3_encryption_required(tcon))
|
||||
flags |= CIFS_TRANSFORM_REQ;
|
||||
|
||||
rc = SMB2_open(xid, &oparams, &srch_path, &oplock, NULL, NULL, NULL);
|
||||
if (rc == 0) {
|
||||
memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
|
||||
tcon->crfid.tcon = tcon;
|
||||
tcon->crfid.is_valid = true;
|
||||
kref_init(&tcon->crfid.refcount);
|
||||
kref_get(&tcon->crfid.refcount);
|
||||
}
|
||||
memset(rqst, 0, sizeof(rqst));
|
||||
resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
|
||||
memset(rsp_iov, 0, sizeof(rsp_iov));
|
||||
|
||||
/* Open */
|
||||
memset(&open_iov, 0, sizeof(open_iov));
|
||||
rqst[0].rq_iov = open_iov;
|
||||
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
|
||||
|
||||
oparms.tcon = tcon;
|
||||
oparms.create_options = 0;
|
||||
oparms.desired_access = FILE_READ_ATTRIBUTES;
|
||||
oparms.disposition = FILE_OPEN;
|
||||
oparms.fid = pfid;
|
||||
oparms.reconnect = false;
|
||||
|
||||
rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, &utf16_path);
|
||||
if (rc)
|
||||
goto oshr_exit;
|
||||
smb2_set_next_command(tcon, &rqst[0]);
|
||||
|
||||
memset(&qi_iov, 0, sizeof(qi_iov));
|
||||
rqst[1].rq_iov = qi_iov;
|
||||
rqst[1].rq_nvec = 1;
|
||||
|
||||
rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID,
|
||||
COMPOUND_FID, FILE_ALL_INFORMATION,
|
||||
SMB2_O_INFO_FILE, 0,
|
||||
sizeof(struct smb2_file_all_info) +
|
||||
PATH_MAX * 2, 0, NULL);
|
||||
if (rc)
|
||||
goto oshr_exit;
|
||||
|
||||
smb2_set_related(&rqst[1]);
|
||||
|
||||
rc = compound_send_recv(xid, ses, flags, 2, rqst,
|
||||
resp_buftype, rsp_iov);
|
||||
if (rc)
|
||||
goto oshr_exit;
|
||||
|
||||
o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
|
||||
oparms.fid->persistent_fid = o_rsp->PersistentFileId;
|
||||
oparms.fid->volatile_fid = o_rsp->VolatileFileId;
|
||||
#ifdef CONFIG_CIFS_DEBUG2
|
||||
oparms.fid->mid = le64_to_cpu(o_rsp->sync_hdr.MessageId);
|
||||
#endif /* CIFS_DEBUG2 */
|
||||
|
||||
if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE)
|
||||
oplock = smb2_parse_lease_state(server, o_rsp,
|
||||
&oparms.fid->epoch,
|
||||
oparms.fid->lease_key);
|
||||
else
|
||||
goto oshr_exit;
|
||||
|
||||
|
||||
memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
|
||||
tcon->crfid.tcon = tcon;
|
||||
tcon->crfid.is_valid = true;
|
||||
kref_init(&tcon->crfid.refcount);
|
||||
kref_get(&tcon->crfid.refcount);
|
||||
|
||||
|
||||
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
|
||||
if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
|
||||
goto oshr_exit;
|
||||
rc = smb2_validate_and_copy_iov(
|
||||
le16_to_cpu(qi_rsp->OutputBufferOffset),
|
||||
sizeof(struct smb2_file_all_info),
|
||||
&rsp_iov[1], sizeof(struct smb2_file_all_info),
|
||||
(char *)&tcon->crfid.file_all_info);
|
||||
if (rc)
|
||||
goto oshr_exit;
|
||||
tcon->crfid.file_all_info_is_valid = 1;
|
||||
|
||||
oshr_exit:
|
||||
mutex_unlock(&tcon->crfid.fid_mutex);
|
||||
SMB2_open_free(&rqst[0]);
|
||||
SMB2_query_info_free(&rqst[1]);
|
||||
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1253,7 +1330,8 @@ smb2_ioctl_query_info(const unsigned int xid,
|
|||
struct smb_query_info __user *pqi;
|
||||
int rc = 0;
|
||||
int flags = 0;
|
||||
struct smb2_query_info_rsp *rsp = NULL;
|
||||
struct smb2_query_info_rsp *qi_rsp = NULL;
|
||||
struct smb2_ioctl_rsp *io_rsp = NULL;
|
||||
void *buffer = NULL;
|
||||
struct smb_rqst rqst[3];
|
||||
int resp_buftype[3];
|
||||
|
@ -1263,6 +1341,7 @@ smb2_ioctl_query_info(const unsigned int xid,
|
|||
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||
struct cifs_fid fid;
|
||||
struct kvec qi_iov[1];
|
||||
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
|
||||
struct kvec close_iov[1];
|
||||
|
||||
memset(rqst, 0, sizeof(rqst));
|
||||
|
@ -1313,15 +1392,35 @@ smb2_ioctl_query_info(const unsigned int xid,
|
|||
smb2_set_next_command(tcon, &rqst[0]);
|
||||
|
||||
/* Query */
|
||||
memset(&qi_iov, 0, sizeof(qi_iov));
|
||||
rqst[1].rq_iov = qi_iov;
|
||||
rqst[1].rq_nvec = 1;
|
||||
if (qi.flags & PASSTHRU_FSCTL) {
|
||||
/* Can eventually relax perm check since server enforces too */
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
rc = -EPERM;
|
||||
else {
|
||||
memset(&io_iov, 0, sizeof(io_iov));
|
||||
rqst[1].rq_iov = io_iov;
|
||||
rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
|
||||
|
||||
rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID, COMPOUND_FID,
|
||||
qi.file_info_class, qi.info_type,
|
||||
qi.additional_information,
|
||||
rc = SMB2_ioctl_init(tcon, &rqst[1],
|
||||
COMPOUND_FID, COMPOUND_FID,
|
||||
qi.info_type, true, NULL,
|
||||
0);
|
||||
}
|
||||
} else if (qi.flags == PASSTHRU_QUERY_INFO) {
|
||||
memset(&qi_iov, 0, sizeof(qi_iov));
|
||||
rqst[1].rq_iov = qi_iov;
|
||||
rqst[1].rq_nvec = 1;
|
||||
|
||||
rc = SMB2_query_info_init(tcon, &rqst[1], COMPOUND_FID,
|
||||
COMPOUND_FID, qi.file_info_class,
|
||||
qi.info_type, qi.additional_information,
|
||||
qi.input_buffer_length,
|
||||
qi.output_buffer_length, buffer);
|
||||
} else { /* unknown flags */
|
||||
cifs_dbg(VFS, "invalid passthru query flags: 0x%x\n", qi.flags);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
if (rc)
|
||||
goto iqinf_exit;
|
||||
smb2_set_next_command(tcon, &rqst[1]);
|
||||
|
@ -1341,24 +1440,44 @@ smb2_ioctl_query_info(const unsigned int xid,
|
|||
resp_buftype, rsp_iov);
|
||||
if (rc)
|
||||
goto iqinf_exit;
|
||||
pqi = (struct smb_query_info __user *)arg;
|
||||
rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
|
||||
if (le32_to_cpu(rsp->OutputBufferLength) < qi.input_buffer_length)
|
||||
qi.input_buffer_length = le32_to_cpu(rsp->OutputBufferLength);
|
||||
if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length,
|
||||
sizeof(qi.input_buffer_length))) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
if (copy_to_user(pqi + 1, rsp->Buffer, qi.input_buffer_length)) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
if (qi.flags & PASSTHRU_FSCTL) {
|
||||
pqi = (struct smb_query_info __user *)arg;
|
||||
io_rsp = (struct smb2_ioctl_rsp *)rsp_iov[1].iov_base;
|
||||
if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length)
|
||||
qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
|
||||
if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length,
|
||||
sizeof(qi.input_buffer_length))) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
if (copy_to_user(pqi + 1, &io_rsp[1], qi.input_buffer_length)) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
} else {
|
||||
pqi = (struct smb_query_info __user *)arg;
|
||||
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
|
||||
if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length)
|
||||
qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength);
|
||||
if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length,
|
||||
sizeof(qi.input_buffer_length))) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
if (copy_to_user(pqi + 1, qi_rsp->Buffer, qi.input_buffer_length)) {
|
||||
rc = -EFAULT;
|
||||
goto iqinf_exit;
|
||||
}
|
||||
}
|
||||
|
||||
iqinf_exit:
|
||||
kfree(buffer);
|
||||
SMB2_open_free(&rqst[0]);
|
||||
SMB2_query_info_free(&rqst[1]);
|
||||
if (qi.flags & PASSTHRU_FSCTL)
|
||||
SMB2_ioctl_free(&rqst[1]);
|
||||
else
|
||||
SMB2_query_info_free(&rqst[1]);
|
||||
|
||||
SMB2_close_free(&rqst[2]);
|
||||
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||
|
@ -2472,22 +2591,38 @@ get_smb2_acl(struct cifs_sb_info *cifs_sb,
|
|||
static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
||||
loff_t offset, loff_t len, bool keep_size)
|
||||
{
|
||||
struct cifs_ses *ses = tcon->ses;
|
||||
struct inode *inode;
|
||||
struct cifsInodeInfo *cifsi;
|
||||
struct cifsFileInfo *cfile = file->private_data;
|
||||
struct file_zero_data_information fsctl_buf;
|
||||
struct smb_rqst rqst[2];
|
||||
int resp_buftype[2];
|
||||
struct kvec rsp_iov[2];
|
||||
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
|
||||
struct kvec si_iov[1];
|
||||
unsigned int size[1];
|
||||
void *data[1];
|
||||
long rc;
|
||||
unsigned int xid;
|
||||
int num = 0, flags = 0;
|
||||
__le64 eof;
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
inode = d_inode(cfile->dentry);
|
||||
cifsi = CIFS_I(inode);
|
||||
|
||||
trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||
ses->Suid, offset, len);
|
||||
|
||||
|
||||
/* if file not oplocked can't be sure whether asking to extend size */
|
||||
if (!CIFS_CACHE_READ(cifsi))
|
||||
if (keep_size == false) {
|
||||
rc = -EOPNOTSUPP;
|
||||
trace_smb3_zero_err(xid, cfile->fid.persistent_fid,
|
||||
tcon->tid, ses->Suid, offset, len, rc);
|
||||
free_xid(xid);
|
||||
return rc;
|
||||
}
|
||||
|
@ -2498,33 +2633,73 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
|||
*/
|
||||
if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) {
|
||||
rc = -EOPNOTSUPP;
|
||||
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||
ses->Suid, offset, len, rc);
|
||||
free_xid(xid);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* need to make sure we are not asked to extend the file since the SMB3
|
||||
* fsctl does not change the file size. In the future we could change
|
||||
* this to zero the first part of the range then set the file size
|
||||
* which for a non sparse file would zero the newly extended range
|
||||
*/
|
||||
if (keep_size == false)
|
||||
if (i_size_read(inode) < offset + len) {
|
||||
rc = -EOPNOTSUPP;
|
||||
free_xid(xid);
|
||||
return rc;
|
||||
}
|
||||
|
||||
cifs_dbg(FYI, "offset %lld len %lld", offset, len);
|
||||
|
||||
fsctl_buf.FileOffset = cpu_to_le64(offset);
|
||||
fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
|
||||
|
||||
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
|
||||
true /* is_fctl */, (char *)&fsctl_buf,
|
||||
sizeof(struct file_zero_data_information), NULL, NULL);
|
||||
if (smb3_encryption_required(tcon))
|
||||
flags |= CIFS_TRANSFORM_REQ;
|
||||
|
||||
memset(rqst, 0, sizeof(rqst));
|
||||
resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
|
||||
memset(rsp_iov, 0, sizeof(rsp_iov));
|
||||
|
||||
|
||||
memset(&io_iov, 0, sizeof(io_iov));
|
||||
rqst[num].rq_iov = io_iov;
|
||||
rqst[num].rq_nvec = SMB2_IOCTL_IOV_SIZE;
|
||||
rc = SMB2_ioctl_init(tcon, &rqst[num++], cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
|
||||
true /* is_fctl */, (char *)&fsctl_buf,
|
||||
sizeof(struct file_zero_data_information));
|
||||
if (rc)
|
||||
goto zero_range_exit;
|
||||
|
||||
/*
|
||||
* do we also need to change the size of the file?
|
||||
*/
|
||||
if (keep_size == false && i_size_read(inode) < offset + len) {
|
||||
smb2_set_next_command(tcon, &rqst[0]);
|
||||
|
||||
memset(&si_iov, 0, sizeof(si_iov));
|
||||
rqst[num].rq_iov = si_iov;
|
||||
rqst[num].rq_nvec = 1;
|
||||
|
||||
eof = cpu_to_le64(offset + len);
|
||||
size[0] = 8; /* sizeof __le64 */
|
||||
data[0] = &eof;
|
||||
|
||||
rc = SMB2_set_info_init(tcon, &rqst[num++],
|
||||
cfile->fid.persistent_fid,
|
||||
cfile->fid.persistent_fid,
|
||||
current->tgid,
|
||||
FILE_END_OF_FILE_INFORMATION,
|
||||
SMB2_O_INFO_FILE, 0, data, size);
|
||||
smb2_set_related(&rqst[1]);
|
||||
}
|
||||
|
||||
rc = compound_send_recv(xid, ses, flags, num, rqst,
|
||||
resp_buftype, rsp_iov);
|
||||
|
||||
zero_range_exit:
|
||||
SMB2_ioctl_free(&rqst[0]);
|
||||
SMB2_set_info_free(&rqst[1]);
|
||||
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||
free_xid(xid);
|
||||
if (rc)
|
||||
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||
ses->Suid, offset, len, rc);
|
||||
else
|
||||
trace_smb3_zero_done(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||
ses->Suid, offset, len);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -2573,15 +2748,20 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
|||
struct cifsFileInfo *cfile = file->private_data;
|
||||
long rc = -EOPNOTSUPP;
|
||||
unsigned int xid;
|
||||
__le64 eof;
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
inode = d_inode(cfile->dentry);
|
||||
cifsi = CIFS_I(inode);
|
||||
|
||||
trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||
tcon->ses->Suid, off, len);
|
||||
/* if file not oplocked can't be sure whether asking to extend size */
|
||||
if (!CIFS_CACHE_READ(cifsi))
|
||||
if (keep_size == false) {
|
||||
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
|
||||
tcon->tid, tcon->ses->Suid, off, len, rc);
|
||||
free_xid(xid);
|
||||
return rc;
|
||||
}
|
||||
|
@ -2601,6 +2781,12 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
|||
/* BB: in future add else clause to extend file */
|
||||
else
|
||||
rc = -EOPNOTSUPP;
|
||||
if (rc)
|
||||
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
|
||||
tcon->tid, tcon->ses->Suid, off, len, rc);
|
||||
else
|
||||
trace_smb3_falloc_done(xid, cfile->fid.persistent_fid,
|
||||
tcon->tid, tcon->ses->Suid, off, len);
|
||||
free_xid(xid);
|
||||
return rc;
|
||||
}
|
||||
|
@ -2616,14 +2802,31 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
|||
*/
|
||||
if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) {
|
||||
rc = -EOPNOTSUPP;
|
||||
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
|
||||
tcon->tid, tcon->ses->Suid, off, len, rc);
|
||||
free_xid(xid);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = smb2_set_sparse(xid, tcon, cfile, inode, false);
|
||||
smb2_set_sparse(xid, tcon, cfile, inode, false);
|
||||
rc = 0;
|
||||
} else {
|
||||
smb2_set_sparse(xid, tcon, cfile, inode, false);
|
||||
rc = 0;
|
||||
if (i_size_read(inode) < off + len) {
|
||||
eof = cpu_to_le64(off + len);
|
||||
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, cfile->pid,
|
||||
&eof);
|
||||
}
|
||||
}
|
||||
/* BB: else ... in future add code to extend file and set sparse */
|
||||
|
||||
if (rc)
|
||||
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||
tcon->ses->Suid, off, len, rc);
|
||||
else
|
||||
trace_smb3_falloc_done(xid, cfile->fid.persistent_fid, tcon->tid,
|
||||
tcon->ses->Suid, off, len);
|
||||
|
||||
free_xid(xid);
|
||||
return rc;
|
||||
|
@ -3604,6 +3807,104 @@ smb2_next_header(char *buf)
|
|||
return le32_to_cpu(hdr->NextCommand);
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_make_node(unsigned int xid, struct inode *inode,
|
||||
struct dentry *dentry, struct cifs_tcon *tcon,
|
||||
char *full_path, umode_t mode, dev_t dev)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
int rc = -EPERM;
|
||||
int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
|
||||
FILE_ALL_INFO *buf = NULL;
|
||||
struct cifs_io_parms io_parms;
|
||||
__u32 oplock = 0;
|
||||
struct cifs_fid fid;
|
||||
struct cifs_open_parms oparms;
|
||||
unsigned int bytes_written;
|
||||
struct win_dev *pdev;
|
||||
struct kvec iov[2];
|
||||
|
||||
/*
|
||||
* Check if mounted with mount parm 'sfu' mount parm.
|
||||
* SFU emulation should work with all servers, but only
|
||||
* supports block and char device (no socket & fifo),
|
||||
* and was used by default in earlier versions of Windows
|
||||
*/
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* TODO: Add ability to create instead via reparse point. Windows (e.g.
|
||||
* their current NFS server) uses this approach to expose special files
|
||||
* over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions
|
||||
*/
|
||||
|
||||
if (!S_ISCHR(mode) && !S_ISBLK(mode))
|
||||
goto out;
|
||||
|
||||
cifs_dbg(FYI, "sfu compat create special file\n");
|
||||
|
||||
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (backup_cred(cifs_sb))
|
||||
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
||||
|
||||
oparms.tcon = tcon;
|
||||
oparms.cifs_sb = cifs_sb;
|
||||
oparms.desired_access = GENERIC_WRITE;
|
||||
oparms.create_options = create_options;
|
||||
oparms.disposition = FILE_CREATE;
|
||||
oparms.path = full_path;
|
||||
oparms.fid = &fid;
|
||||
oparms.reconnect = false;
|
||||
|
||||
if (tcon->ses->server->oplocks)
|
||||
oplock = REQ_OPLOCK;
|
||||
else
|
||||
oplock = 0;
|
||||
rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* BB Do not bother to decode buf since no local inode yet to put
|
||||
* timestamps in, but we can reuse it safely.
|
||||
*/
|
||||
|
||||
pdev = (struct win_dev *)buf;
|
||||
io_parms.pid = current->tgid;
|
||||
io_parms.tcon = tcon;
|
||||
io_parms.offset = 0;
|
||||
io_parms.length = sizeof(struct win_dev);
|
||||
iov[1].iov_base = buf;
|
||||
iov[1].iov_len = sizeof(struct win_dev);
|
||||
if (S_ISCHR(mode)) {
|
||||
memcpy(pdev->type, "IntxCHR", 8);
|
||||
pdev->major = cpu_to_le64(MAJOR(dev));
|
||||
pdev->minor = cpu_to_le64(MINOR(dev));
|
||||
rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
|
||||
&bytes_written, iov, 1);
|
||||
} else if (S_ISBLK(mode)) {
|
||||
memcpy(pdev->type, "IntxBLK", 8);
|
||||
pdev->major = cpu_to_le64(MAJOR(dev));
|
||||
pdev->minor = cpu_to_le64(MINOR(dev));
|
||||
rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
|
||||
&bytes_written, iov, 1);
|
||||
}
|
||||
tcon->ses->server->ops->close(xid, tcon, &fid);
|
||||
d_drop(dentry);
|
||||
|
||||
/* FIXME: add code here to set EAs */
|
||||
out:
|
||||
kfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
struct smb_version_operations smb20_operations = {
|
||||
.compare_fids = smb2_compare_fids,
|
||||
.setup_request = smb2_setup_request,
|
||||
|
@ -3698,6 +3999,7 @@ struct smb_version_operations smb20_operations = {
|
|||
#endif /* CIFS_ACL */
|
||||
.next_header = smb2_next_header,
|
||||
.ioctl_query_info = smb2_ioctl_query_info,
|
||||
.make_node = smb2_make_node,
|
||||
};
|
||||
|
||||
struct smb_version_operations smb21_operations = {
|
||||
|
@ -3796,6 +4098,7 @@ struct smb_version_operations smb21_operations = {
|
|||
#endif /* CIFS_ACL */
|
||||
.next_header = smb2_next_header,
|
||||
.ioctl_query_info = smb2_ioctl_query_info,
|
||||
.make_node = smb2_make_node,
|
||||
};
|
||||
|
||||
struct smb_version_operations smb30_operations = {
|
||||
|
@ -3903,6 +4206,7 @@ struct smb_version_operations smb30_operations = {
|
|||
#endif /* CIFS_ACL */
|
||||
.next_header = smb2_next_header,
|
||||
.ioctl_query_info = smb2_ioctl_query_info,
|
||||
.make_node = smb2_make_node,
|
||||
};
|
||||
|
||||
struct smb_version_operations smb311_operations = {
|
||||
|
@ -4011,6 +4315,7 @@ struct smb_version_operations smb311_operations = {
|
|||
#endif /* CIFS_ACL */
|
||||
.next_header = smb2_next_header,
|
||||
.ioctl_query_info = smb2_ioctl_query_info,
|
||||
.make_node = smb2_make_node,
|
||||
};
|
||||
|
||||
struct smb_version_values smb20_values = {
|
||||
|
|
|
@ -1797,9 +1797,10 @@ create_reconnect_durable_buf(struct cifs_fid *fid)
|
|||
return buf;
|
||||
}
|
||||
|
||||
static __u8
|
||||
parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp,
|
||||
unsigned int *epoch, char *lease_key)
|
||||
__u8
|
||||
smb2_parse_lease_state(struct TCP_Server_Info *server,
|
||||
struct smb2_create_rsp *rsp,
|
||||
unsigned int *epoch, char *lease_key)
|
||||
{
|
||||
char *data_offset;
|
||||
struct create_context *cc;
|
||||
|
@ -2456,8 +2457,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
|||
}
|
||||
|
||||
if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE)
|
||||
*oplock = parse_lease_state(server, rsp, &oparms->fid->epoch,
|
||||
oparms->fid->lease_key);
|
||||
*oplock = smb2_parse_lease_state(server, rsp,
|
||||
&oparms->fid->epoch,
|
||||
oparms->fid->lease_key);
|
||||
else
|
||||
*oplock = rsp->OplockLevel;
|
||||
creat_exit:
|
||||
|
@ -2466,65 +2468,46 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* SMB2 IOCTL is used for both IOCTLs and FSCTLs
|
||||
*/
|
||||
int
|
||||
SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
||||
u64 volatile_fid, u32 opcode, bool is_fsctl,
|
||||
char *in_data, u32 indatalen,
|
||||
char **out_data, u32 *plen /* returned data len */)
|
||||
SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
|
||||
u64 persistent_fid, u64 volatile_fid, u32 opcode,
|
||||
bool is_fsctl, char *in_data, u32 indatalen)
|
||||
{
|
||||
struct smb_rqst rqst;
|
||||
struct smb2_ioctl_req *req;
|
||||
struct smb2_ioctl_rsp *rsp;
|
||||
struct cifs_ses *ses;
|
||||
struct kvec iov[2];
|
||||
struct kvec rsp_iov;
|
||||
int resp_buftype;
|
||||
int n_iov;
|
||||
int rc = 0;
|
||||
int flags = 0;
|
||||
struct kvec *iov = rqst->rq_iov;
|
||||
unsigned int total_len;
|
||||
|
||||
cifs_dbg(FYI, "SMB2 IOCTL\n");
|
||||
|
||||
if (out_data != NULL)
|
||||
*out_data = NULL;
|
||||
|
||||
/* zero out returned data len, in case of error */
|
||||
if (plen)
|
||||
*plen = 0;
|
||||
|
||||
if (tcon)
|
||||
ses = tcon->ses;
|
||||
else
|
||||
return -EIO;
|
||||
|
||||
if (!ses || !(ses->server))
|
||||
return -EIO;
|
||||
int rc;
|
||||
|
||||
rc = smb2_plain_req_init(SMB2_IOCTL, tcon, (void **) &req, &total_len);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (smb3_encryption_required(tcon))
|
||||
flags |= CIFS_TRANSFORM_REQ;
|
||||
|
||||
req->CtlCode = cpu_to_le32(opcode);
|
||||
req->PersistentFileId = persistent_fid;
|
||||
req->VolatileFileId = volatile_fid;
|
||||
|
||||
iov[0].iov_base = (char *)req;
|
||||
/*
|
||||
* If no input data, the size of ioctl struct in
|
||||
* protocol spec still includes a 1 byte data buffer,
|
||||
* but if input data passed to ioctl, we do not
|
||||
* want to double count this, so we do not send
|
||||
* the dummy one byte of data in iovec[0] if sending
|
||||
* input data (in iovec[1]).
|
||||
*/
|
||||
if (indatalen) {
|
||||
req->InputCount = cpu_to_le32(indatalen);
|
||||
/* do not set InputOffset if no input data */
|
||||
req->InputOffset =
|
||||
cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer));
|
||||
rqst->rq_nvec = 2;
|
||||
iov[0].iov_len = total_len - 1;
|
||||
iov[1].iov_base = in_data;
|
||||
iov[1].iov_len = indatalen;
|
||||
n_iov = 2;
|
||||
} else
|
||||
n_iov = 1;
|
||||
} else {
|
||||
rqst->rq_nvec = 1;
|
||||
iov[0].iov_len = total_len;
|
||||
}
|
||||
|
||||
req->OutputOffset = 0;
|
||||
req->OutputCount = 0; /* MBZ */
|
||||
|
@ -2546,33 +2529,70 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
|||
else
|
||||
req->Flags = 0;
|
||||
|
||||
iov[0].iov_base = (char *)req;
|
||||
|
||||
/*
|
||||
* If no input data, the size of ioctl struct in
|
||||
* protocol spec still includes a 1 byte data buffer,
|
||||
* but if input data passed to ioctl, we do not
|
||||
* want to double count this, so we do not send
|
||||
* the dummy one byte of data in iovec[0] if sending
|
||||
* input data (in iovec[1]).
|
||||
*/
|
||||
|
||||
if (indatalen) {
|
||||
iov[0].iov_len = total_len - 1;
|
||||
} else
|
||||
iov[0].iov_len = total_len;
|
||||
|
||||
/* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */
|
||||
if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO)
|
||||
req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
SMB2_ioctl_free(struct smb_rqst *rqst)
|
||||
{
|
||||
if (rqst && rqst->rq_iov)
|
||||
cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
|
||||
}
|
||||
|
||||
/*
|
||||
* SMB2 IOCTL is used for both IOCTLs and FSCTLs
|
||||
*/
|
||||
int
|
||||
SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
||||
u64 volatile_fid, u32 opcode, bool is_fsctl,
|
||||
char *in_data, u32 indatalen,
|
||||
char **out_data, u32 *plen /* returned data len */)
|
||||
{
|
||||
struct smb_rqst rqst;
|
||||
struct smb2_ioctl_rsp *rsp = NULL;
|
||||
struct cifs_ses *ses;
|
||||
struct kvec iov[SMB2_IOCTL_IOV_SIZE];
|
||||
struct kvec rsp_iov = {NULL, 0};
|
||||
int resp_buftype = CIFS_NO_BUFFER;
|
||||
int rc = 0;
|
||||
int flags = 0;
|
||||
|
||||
cifs_dbg(FYI, "SMB2 IOCTL\n");
|
||||
|
||||
if (out_data != NULL)
|
||||
*out_data = NULL;
|
||||
|
||||
/* zero out returned data len, in case of error */
|
||||
if (plen)
|
||||
*plen = 0;
|
||||
|
||||
if (tcon)
|
||||
ses = tcon->ses;
|
||||
else
|
||||
return -EIO;
|
||||
|
||||
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 = n_iov;
|
||||
rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE;
|
||||
|
||||
rc = SMB2_ioctl_init(tcon, &rqst, persistent_fid, volatile_fid,
|
||||
opcode, is_fsctl, in_data, indatalen);
|
||||
if (rc)
|
||||
goto ioctl_exit;
|
||||
|
||||
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
|
||||
&rsp_iov);
|
||||
cifs_small_buf_release(req);
|
||||
rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base;
|
||||
|
||||
if (rc != 0)
|
||||
|
@ -2622,6 +2642,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
|||
}
|
||||
|
||||
ioctl_exit:
|
||||
SMB2_ioctl_free(&rqst);
|
||||
free_rsp_buf(resp_buftype, rsp);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -959,6 +959,13 @@ struct duplicate_extents_to_file {
|
|||
__le64 ByteCount; /* Bytes to be copied */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Maximum number of iovs we need for an ioctl request.
|
||||
* [0] : struct smb2_ioctl_req
|
||||
* [1] : in_data
|
||||
*/
|
||||
#define SMB2_IOCTL_IOV_SIZE 2
|
||||
|
||||
struct smb2_ioctl_req {
|
||||
struct smb2_sync_hdr sync_hdr;
|
||||
__le16 StructureSize; /* Must be 57 */
|
||||
|
|
|
@ -144,6 +144,10 @@ extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
u64 persistent_fid, u64 volatile_fid, u32 opcode,
|
||||
bool is_fsctl, char *in_data, u32 indatalen,
|
||||
char **out_data, u32 *plen /* returned data len */);
|
||||
extern int SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
|
||||
u64 persistent_fid, u64 volatile_fid, u32 opcode,
|
||||
bool is_fsctl, char *in_data, u32 indatalen);
|
||||
extern void SMB2_ioctl_free(struct smb_rqst *rqst);
|
||||
extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_file_id, u64 volatile_file_id);
|
||||
extern int SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
@ -223,6 +227,9 @@ extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);
|
|||
|
||||
extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *,
|
||||
enum securityEnum);
|
||||
extern __u8 smb2_parse_lease_state(struct TCP_Server_Info *server,
|
||||
struct smb2_create_rsp *rsp,
|
||||
unsigned int *epoch, char *lease_key);
|
||||
extern int smb3_encryption_required(const struct cifs_tcon *tcon);
|
||||
extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length,
|
||||
struct kvec *iov, unsigned int min_buf_size);
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
*/
|
||||
|
||||
#define STATUS_SEVERITY_SUCCESS __constant_cpu_to_le32(0x0000)
|
||||
#define STATUS_SEVERITY_INFORMATIONAL __constanst_cpu_to_le32(0x0001)
|
||||
#define STATUS_SEVERITY_WARNING __constanst_cpu_to_le32(0x0002)
|
||||
#define STATUS_SEVERITY_ERROR __constanst_cpu_to_le32(0x0003)
|
||||
#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001)
|
||||
#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002)
|
||||
#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003)
|
||||
|
||||
struct ntstatus {
|
||||
/* Facility is the high 12 bits of the following field */
|
||||
|
|
124
fs/cifs/trace.h
124
fs/cifs/trace.h
|
@ -59,6 +59,8 @@ DEFINE_EVENT(smb3_rw_err_class, smb3_##name, \
|
|||
DEFINE_SMB3_RW_ERR_EVENT(write_err);
|
||||
DEFINE_SMB3_RW_ERR_EVENT(read_err);
|
||||
DEFINE_SMB3_RW_ERR_EVENT(query_dir_err);
|
||||
DEFINE_SMB3_RW_ERR_EVENT(zero_err);
|
||||
DEFINE_SMB3_RW_ERR_EVENT(falloc_err);
|
||||
|
||||
|
||||
/* For logging successful read or write */
|
||||
|
@ -104,9 +106,13 @@ DEFINE_EVENT(smb3_rw_done_class, smb3_##name, \
|
|||
DEFINE_SMB3_RW_DONE_EVENT(write_enter);
|
||||
DEFINE_SMB3_RW_DONE_EVENT(read_enter);
|
||||
DEFINE_SMB3_RW_DONE_EVENT(query_dir_enter);
|
||||
DEFINE_SMB3_RW_DONE_EVENT(zero_enter);
|
||||
DEFINE_SMB3_RW_DONE_EVENT(falloc_enter);
|
||||
DEFINE_SMB3_RW_DONE_EVENT(write_done);
|
||||
DEFINE_SMB3_RW_DONE_EVENT(read_done);
|
||||
DEFINE_SMB3_RW_DONE_EVENT(query_dir_done);
|
||||
DEFINE_SMB3_RW_DONE_EVENT(zero_done);
|
||||
DEFINE_SMB3_RW_DONE_EVENT(falloc_done);
|
||||
|
||||
/*
|
||||
* For handle based calls other than read and write, and get/set info
|
||||
|
@ -242,6 +248,123 @@ DEFINE_SMB3_INF_ERR_EVENT(query_info_err);
|
|||
DEFINE_SMB3_INF_ERR_EVENT(set_info_err);
|
||||
DEFINE_SMB3_INF_ERR_EVENT(fsctl_err);
|
||||
|
||||
DECLARE_EVENT_CLASS(smb3_inf_compound_enter_class,
|
||||
TP_PROTO(unsigned int xid,
|
||||
__u32 tid,
|
||||
__u64 sesid,
|
||||
const char *full_path),
|
||||
TP_ARGS(xid, tid, sesid, full_path),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, xid)
|
||||
__field(__u32, tid)
|
||||
__field(__u64, sesid)
|
||||
__string(path, full_path)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->xid = xid;
|
||||
__entry->tid = tid;
|
||||
__entry->sesid = sesid;
|
||||
__assign_str(path, full_path);
|
||||
),
|
||||
TP_printk("xid=%u sid=0x%llx tid=0x%x path=%s",
|
||||
__entry->xid, __entry->sesid, __entry->tid,
|
||||
__get_str(path))
|
||||
)
|
||||
|
||||
#define DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(name) \
|
||||
DEFINE_EVENT(smb3_inf_compound_enter_class, smb3_##name, \
|
||||
TP_PROTO(unsigned int xid, \
|
||||
__u32 tid, \
|
||||
__u64 sesid, \
|
||||
const char *full_path), \
|
||||
TP_ARGS(xid, tid, sesid, full_path))
|
||||
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(query_info_compound_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(hardlink_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rename_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter);
|
||||
|
||||
|
||||
DECLARE_EVENT_CLASS(smb3_inf_compound_done_class,
|
||||
TP_PROTO(unsigned int xid,
|
||||
__u32 tid,
|
||||
__u64 sesid),
|
||||
TP_ARGS(xid, tid, sesid),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, xid)
|
||||
__field(__u32, tid)
|
||||
__field(__u64, sesid)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->xid = xid;
|
||||
__entry->tid = tid;
|
||||
__entry->sesid = sesid;
|
||||
),
|
||||
TP_printk("xid=%u sid=0x%llx tid=0x%x",
|
||||
__entry->xid, __entry->sesid, __entry->tid)
|
||||
)
|
||||
|
||||
#define DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(name) \
|
||||
DEFINE_EVENT(smb3_inf_compound_done_class, smb3_##name, \
|
||||
TP_PROTO(unsigned int xid, \
|
||||
__u32 tid, \
|
||||
__u64 sesid), \
|
||||
TP_ARGS(xid, tid, sesid))
|
||||
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_info_compound_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(hardlink_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rename_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done);
|
||||
|
||||
|
||||
DECLARE_EVENT_CLASS(smb3_inf_compound_err_class,
|
||||
TP_PROTO(unsigned int xid,
|
||||
__u32 tid,
|
||||
__u64 sesid,
|
||||
int rc),
|
||||
TP_ARGS(xid, tid, sesid, rc),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, xid)
|
||||
__field(__u32, tid)
|
||||
__field(__u64, sesid)
|
||||
__field(int, rc)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->xid = xid;
|
||||
__entry->tid = tid;
|
||||
__entry->sesid = sesid;
|
||||
__entry->rc = rc;
|
||||
),
|
||||
TP_printk("xid=%u sid=0x%llx tid=0x%x rc=%d",
|
||||
__entry->xid, __entry->sesid, __entry->tid,
|
||||
__entry->rc)
|
||||
)
|
||||
|
||||
#define DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(name) \
|
||||
DEFINE_EVENT(smb3_inf_compound_err_class, smb3_##name, \
|
||||
TP_PROTO(unsigned int xid, \
|
||||
__u32 tid, \
|
||||
__u64 sesid, \
|
||||
int rc), \
|
||||
TP_ARGS(xid, tid, sesid, rc))
|
||||
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_info_compound_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(hardlink_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rename_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err);
|
||||
|
||||
/*
|
||||
* For logging SMB3 Status code and Command for responses which return errors
|
||||
*/
|
||||
|
@ -713,6 +836,7 @@ DEFINE_EVENT(smb3_credit_class, smb3_##name, \
|
|||
TP_ARGS(currmid, hostname, credits))
|
||||
|
||||
DEFINE_SMB3_CREDIT_EVENT(reconnect_with_invalid_credits);
|
||||
DEFINE_SMB3_CREDIT_EVENT(credit_timeout);
|
||||
|
||||
#endif /* _CIFS_TRACE_H */
|
||||
|
||||
|
|
|
@ -486,15 +486,31 @@ smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
|
|||
}
|
||||
|
||||
static int
|
||||
wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
|
||||
int *credits, unsigned int *instance)
|
||||
wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
|
||||
const int timeout, const int flags,
|
||||
unsigned int *instance)
|
||||
{
|
||||
int rc;
|
||||
int *credits;
|
||||
int optype;
|
||||
long int t;
|
||||
|
||||
if (timeout < 0)
|
||||
t = MAX_JIFFY_OFFSET;
|
||||
else
|
||||
t = msecs_to_jiffies(timeout);
|
||||
|
||||
optype = flags & CIFS_OP_MASK;
|
||||
|
||||
*instance = 0;
|
||||
|
||||
credits = server->ops->get_credits_field(server, optype);
|
||||
/* Since an echo is already inflight, no need to wait to send another */
|
||||
if (*credits <= 0 && optype == CIFS_ECHO_OP)
|
||||
return -EAGAIN;
|
||||
|
||||
spin_lock(&server->req_lock);
|
||||
if (timeout == CIFS_ASYNC_OP) {
|
||||
if ((flags & CIFS_TIMEOUT_MASK) == CIFS_ASYNC_OP) {
|
||||
/* oplock breaks must not be held up */
|
||||
server->in_flight++;
|
||||
*credits -= 1;
|
||||
|
@ -504,14 +520,21 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
|
|||
}
|
||||
|
||||
while (1) {
|
||||
if (*credits <= 0) {
|
||||
if (*credits < num_credits) {
|
||||
spin_unlock(&server->req_lock);
|
||||
cifs_num_waiters_inc(server);
|
||||
rc = wait_event_killable(server->request_q,
|
||||
has_credits(server, credits));
|
||||
rc = wait_event_killable_timeout(server->request_q,
|
||||
has_credits(server, credits, num_credits), t);
|
||||
cifs_num_waiters_dec(server);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!rc) {
|
||||
trace_smb3_credit_timeout(server->CurrentMid,
|
||||
server->hostname, num_credits);
|
||||
cifs_dbg(VFS, "wait timed out after %d ms\n",
|
||||
timeout);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
if (rc == -ERESTARTSYS)
|
||||
return -ERESTARTSYS;
|
||||
spin_lock(&server->req_lock);
|
||||
} else {
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
|
@ -519,15 +542,53 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* For normal commands, reserve the last MAX_COMPOUND
|
||||
* credits to compound requests.
|
||||
* Otherwise these compounds could be permanently
|
||||
* starved for credits by single-credit requests.
|
||||
*
|
||||
* To prevent spinning CPU, block this thread until
|
||||
* there are >MAX_COMPOUND credits available.
|
||||
* But only do this is we already have a lot of
|
||||
* credits in flight to avoid triggering this check
|
||||
* for servers that are slow to hand out credits on
|
||||
* new sessions.
|
||||
*/
|
||||
if (!optype && num_credits == 1 &&
|
||||
server->in_flight > 2 * MAX_COMPOUND &&
|
||||
*credits <= MAX_COMPOUND) {
|
||||
spin_unlock(&server->req_lock);
|
||||
cifs_num_waiters_inc(server);
|
||||
rc = wait_event_killable_timeout(
|
||||
server->request_q,
|
||||
has_credits(server, credits,
|
||||
MAX_COMPOUND + 1),
|
||||
t);
|
||||
cifs_num_waiters_dec(server);
|
||||
if (!rc) {
|
||||
trace_smb3_credit_timeout(
|
||||
server->CurrentMid,
|
||||
server->hostname, num_credits);
|
||||
cifs_dbg(VFS, "wait timed out after %d ms\n",
|
||||
timeout);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
if (rc == -ERESTARTSYS)
|
||||
return -ERESTARTSYS;
|
||||
spin_lock(&server->req_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Can not count locking commands against total
|
||||
* as they are allowed to block on server.
|
||||
*/
|
||||
|
||||
/* update # of requests on the wire to server */
|
||||
if (timeout != CIFS_BLOCKING_OP) {
|
||||
*credits -= 1;
|
||||
server->in_flight++;
|
||||
if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) {
|
||||
*credits -= num_credits;
|
||||
server->in_flight += num_credits;
|
||||
*instance = server->reconnect_instance;
|
||||
}
|
||||
spin_unlock(&server->req_lock);
|
||||
|
@ -538,16 +599,36 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
|
|||
}
|
||||
|
||||
static int
|
||||
wait_for_free_request(struct TCP_Server_Info *server, const int timeout,
|
||||
const int optype, unsigned int *instance)
|
||||
wait_for_free_request(struct TCP_Server_Info *server, const int flags,
|
||||
unsigned int *instance)
|
||||
{
|
||||
int *val;
|
||||
return wait_for_free_credits(server, 1, -1, flags,
|
||||
instance);
|
||||
}
|
||||
|
||||
val = server->ops->get_credits_field(server, optype);
|
||||
/* Since an echo is already inflight, no need to wait to send another */
|
||||
if (*val <= 0 && optype == CIFS_ECHO_OP)
|
||||
return -EAGAIN;
|
||||
return wait_for_free_credits(server, timeout, val, instance);
|
||||
static int
|
||||
wait_for_compound_request(struct TCP_Server_Info *server, int num,
|
||||
const int flags, unsigned int *instance)
|
||||
{
|
||||
int *credits;
|
||||
|
||||
credits = server->ops->get_credits_field(server, flags & CIFS_OP_MASK);
|
||||
|
||||
spin_lock(&server->req_lock);
|
||||
if (*credits < num) {
|
||||
/*
|
||||
* Return immediately if not too many requests in flight since
|
||||
* we will likely be stuck on waiting for credits.
|
||||
*/
|
||||
if (server->in_flight < num - *credits) {
|
||||
spin_unlock(&server->req_lock);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
}
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
return wait_for_free_credits(server, num, 60000, flags,
|
||||
instance);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -646,16 +727,16 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
|
|||
mid_handle_t *handle, void *cbdata, const int flags,
|
||||
const struct cifs_credits *exist_credits)
|
||||
{
|
||||
int rc, timeout, optype;
|
||||
int rc;
|
||||
struct mid_q_entry *mid;
|
||||
struct cifs_credits credits = { .value = 0, .instance = 0 };
|
||||
unsigned int instance;
|
||||
int optype;
|
||||
|
||||
timeout = flags & CIFS_TIMEOUT_MASK;
|
||||
optype = flags & CIFS_OP_MASK;
|
||||
|
||||
if ((flags & CIFS_HAS_CREDITS) == 0) {
|
||||
rc = wait_for_free_request(server, timeout, optype, &instance);
|
||||
rc = wait_for_free_request(server, flags, &instance);
|
||||
if (rc)
|
||||
return rc;
|
||||
credits.value = 1;
|
||||
|
@ -871,18 +952,15 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
|||
const int flags, const int num_rqst, struct smb_rqst *rqst,
|
||||
int *resp_buf_type, struct kvec *resp_iov)
|
||||
{
|
||||
int i, j, rc = 0;
|
||||
int timeout, optype;
|
||||
int i, j, optype, rc = 0;
|
||||
struct mid_q_entry *midQ[MAX_COMPOUND];
|
||||
bool cancelled_mid[MAX_COMPOUND] = {false};
|
||||
struct cifs_credits credits[MAX_COMPOUND] = {
|
||||
{ .value = 0, .instance = 0 }
|
||||
};
|
||||
unsigned int instance;
|
||||
unsigned int first_instance = 0;
|
||||
char *buf;
|
||||
|
||||
timeout = flags & CIFS_TIMEOUT_MASK;
|
||||
optype = flags & CIFS_OP_MASK;
|
||||
|
||||
for (i = 0; i < num_rqst; i++)
|
||||
|
@ -896,81 +974,24 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
|||
if (ses->server->tcpStatus == CifsExiting)
|
||||
return -ENOENT;
|
||||
|
||||
spin_lock(&ses->server->req_lock);
|
||||
if (ses->server->credits < num_rqst) {
|
||||
/*
|
||||
* Return immediately if not too many requests in flight since
|
||||
* we will likely be stuck on waiting for credits.
|
||||
*/
|
||||
if (ses->server->in_flight < num_rqst - ses->server->credits) {
|
||||
spin_unlock(&ses->server->req_lock);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
} else {
|
||||
/* enough credits to send the whole compounded request */
|
||||
ses->server->credits -= num_rqst;
|
||||
ses->server->in_flight += num_rqst;
|
||||
first_instance = ses->server->reconnect_instance;
|
||||
}
|
||||
spin_unlock(&ses->server->req_lock);
|
||||
|
||||
if (first_instance) {
|
||||
cifs_dbg(FYI, "Acquired %d credits at once\n", num_rqst);
|
||||
for (i = 0; i < num_rqst; i++) {
|
||||
credits[i].value = 1;
|
||||
credits[i].instance = first_instance;
|
||||
}
|
||||
goto setup_rqsts;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are not enough credits to send the whole compound request but
|
||||
* there are requests in flight that may bring credits from the server.
|
||||
* Wait for all the requests to become available.
|
||||
* This approach still leaves the possibility to be stuck waiting for
|
||||
* credits if the server doesn't grant credits to the outstanding
|
||||
* requests. This should be fixed by returning immediately and letting
|
||||
* a caller fallback to sequential commands instead of compounding.
|
||||
* Ensure we obtain 1 credit per request in the compound chain.
|
||||
* requests and if the client is completely idle, not generating any
|
||||
* other requests.
|
||||
* This can be handled by the eventual session reconnect.
|
||||
*/
|
||||
rc = wait_for_compound_request(ses->server, num_rqst, flags,
|
||||
&instance);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for (i = 0; i < num_rqst; i++) {
|
||||
rc = wait_for_free_request(ses->server, timeout, optype,
|
||||
&instance);
|
||||
|
||||
if (rc == 0) {
|
||||
credits[i].value = 1;
|
||||
credits[i].instance = instance;
|
||||
/*
|
||||
* All parts of the compound chain must get credits from
|
||||
* the same session, otherwise we may end up using more
|
||||
* credits than the server granted. If there were
|
||||
* reconnects in between, return -EAGAIN and let callers
|
||||
* handle it.
|
||||
*/
|
||||
if (i == 0)
|
||||
first_instance = instance;
|
||||
else if (first_instance != instance) {
|
||||
i++;
|
||||
rc = -EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
/*
|
||||
* We haven't sent an SMB packet to the server yet but
|
||||
* we already obtained credits for i requests in the
|
||||
* compound chain - need to return those credits back
|
||||
* for future use. Note that we need to call add_credits
|
||||
* multiple times to match the way we obtained credits
|
||||
* in the first place and to account for in flight
|
||||
* requests correctly.
|
||||
*/
|
||||
for (j = 0; j < i; j++)
|
||||
add_credits(ses->server, &credits[j], optype);
|
||||
return rc;
|
||||
}
|
||||
credits[i].value = 1;
|
||||
credits[i].instance = instance;
|
||||
}
|
||||
|
||||
setup_rqsts:
|
||||
/*
|
||||
* Make sure that we sign in the same order that we send on this socket
|
||||
* and avoid races inside tcp sendmsg code that could cause corruption
|
||||
|
@ -981,14 +1002,12 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
|||
|
||||
/*
|
||||
* All the parts of the compound chain belong obtained credits from the
|
||||
* same session (see the appropriate checks above). In the same time
|
||||
* there might be reconnects after those checks but before we acquired
|
||||
* the srv_mutex. We can not use credits obtained from the previous
|
||||
* same session. We can not use credits obtained from the previous
|
||||
* session to send this request. Check if there were reconnects after
|
||||
* we obtained credits and return -EAGAIN in such cases to let callers
|
||||
* handle it.
|
||||
*/
|
||||
if (first_instance != ses->server->reconnect_instance) {
|
||||
if (instance != ses->server->reconnect_instance) {
|
||||
mutex_unlock(&ses->server->srv_mutex);
|
||||
for (j = 0; j < num_rqst; j++)
|
||||
add_credits(ses->server, &credits[j], optype);
|
||||
|
@ -1057,7 +1076,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
|||
smb311_update_preauth_hash(ses, rqst[0].rq_iov,
|
||||
rqst[0].rq_nvec);
|
||||
|
||||
if (timeout == CIFS_ASYNC_OP)
|
||||
if ((flags & CIFS_TIMEOUT_MASK) == CIFS_ASYNC_OP)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < num_rqst; i++) {
|
||||
|
@ -1194,7 +1213,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
|
|||
int
|
||||
SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
||||
struct smb_hdr *in_buf, struct smb_hdr *out_buf,
|
||||
int *pbytes_returned, const int timeout)
|
||||
int *pbytes_returned, const int flags)
|
||||
{
|
||||
int rc = 0;
|
||||
struct mid_q_entry *midQ;
|
||||
|
@ -1225,7 +1244,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
rc = wait_for_free_request(ses->server, timeout, 0, &credits.instance);
|
||||
rc = wait_for_free_request(ses->server, flags, &credits.instance);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
@ -1264,7 +1283,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
|||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
if (timeout == CIFS_ASYNC_OP)
|
||||
if ((flags & CIFS_TIMEOUT_MASK) == CIFS_ASYNC_OP)
|
||||
goto out;
|
||||
|
||||
rc = wait_for_response(ses->server, midQ);
|
||||
|
@ -1367,8 +1386,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP, 0,
|
||||
&instance);
|
||||
rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP, &instance);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
|
Loading…
Reference in New Issue