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:
Linus Torvalds 2019-03-15 18:52:12 -07:00
commit 9c7dc824d9
17 changed files with 979 additions and 360 deletions

View File

@ -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 5) Continue to extend the smb3 "buildbot" which does automated xfstesting
against Windows, Samba and Azure currently - to add additional tests and 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 6) Address various coverity warnings (most are not bugs per-se, but
the more warnings are addressed, the easier it is to spot real the more warnings are addressed, the easier it is to spot real

View File

@ -1,16 +1,21 @@
This is the client VFS module for the SMB3 NAS protocol as well 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 protocol which was the successor to the Server Message Block
(SMB) protocol, the native file sharing mechanism for most early (SMB) protocol, the native file sharing mechanism for most early
PC operating systems. New and improved versions of CIFS are now PC operating systems. New and improved versions of CIFS are now
called SMB2 and SMB3. These dialects are also supported by the called SMB2 and SMB3. Use of SMB3 (and later, including SMB3.1.1)
CIFS VFS module. CIFS is fully supported by network is strongly preferred over using older dialects like CIFS due to
file servers such as Windows 2000, 2003, 2008, 2012 and 2016 security reaasons. All modern dialects, including the most recent,
as well by Samba (which provides excellent CIFS SMB3.1.1 are supported by the CIFS VFS module. The SMB3 protocol
server support for Linux and many other operating systems), Apple is implemented and supported by all major file servers
systems, as well as most Network Attached Storage vendors, so such as all modern versions of Windows (including Windows 2016
this network filesystem client can mount to a wide variety of Server), as well as by Samba (which provides excellent
servers. 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 The intent of this module is to provide the most advanced network
file system function for SMB3 compliant servers, including advanced file system function for SMB3 compliant servers, including advanced
@ -24,12 +29,17 @@
cluster file systems for fileserving in some Linux to Linux environments, cluster file systems for fileserving in some Linux to Linux environments,
not just in Linux to Windows (or Linux to Mac) environments. not just in Linux to Windows (or Linux to Mac) environments.
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 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 https://wiki.samba.org/index.php/LinuxCIFS_utils

View File

@ -115,7 +115,12 @@ static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon)
seq_puts(m, " type: CDROM "); seq_puts(m, " type: CDROM ");
else else
seq_printf(m, " type: %d ", dev_type); 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"); seq_printf(m, " Encrypted");
if (tcon->nocase) if (tcon->nocase)
seq_printf(m, " 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->in_send),
atomic_read(&server->num_waiters)); atomic_read(&server->num_waiters));
#endif #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:"); seq_puts(m, "\n\tShares:");
j = 0; j = 0;

View File

@ -43,6 +43,9 @@ struct smb_snapshot_array {
/* snapshots[]; */ /* snapshots[]; */
} __packed; } __packed;
/* query_info flags */
#define PASSTHRU_QUERY_INFO 0x00000000
#define PASSTHRU_FSCTL 0x00000001
struct smb_query_info { struct smb_query_info {
__u32 info_type; __u32 info_type;
__u32 file_info_class; __u32 file_info_class;

View File

@ -479,6 +479,14 @@ struct smb_version_operations {
struct cifs_tcon *tcon, struct cifs_tcon *tcon,
__le16 *path, int is_dir, __le16 *path, int is_dir,
unsigned long p); 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 { struct smb_version_values {
@ -735,13 +743,13 @@ in_flight(struct TCP_Server_Info *server)
} }
static inline bool 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; int num;
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
num = *credits; num = *credits;
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
return num > 0; return num >= num_credits;
} }
static inline void static inline void
@ -962,11 +970,14 @@ cap_unix(struct cifs_ses *ses)
struct cached_fid { struct cached_fid {
bool is_valid:1; /* Do we have a useable root fid */ bool is_valid:1; /* Do we have a useable root fid */
bool file_all_info_is_valid:1;
struct kref refcount; struct kref refcount;
struct cifs_fid *fid; struct cifs_fid *fid;
struct mutex fid_mutex; struct mutex fid_mutex;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct work_struct lease_break; 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: * GlobalMid_Lock protects:
* list operations on pending_mid_q and oplockQ * list operations on pending_mid_q and oplockQ
* updates to XID counters, multiplex id and SMB sequence numbers * updates to XID counters, multiplex id and SMB sequence numbers
* list operations on global DnotifyReqList
* tcp_ses_lock protects: * tcp_ses_lock protects:
* list operations on tcp and SMB session lists * list operations on tcp and SMB session lists
* tcon->open_file_lock protects the list of open files hanging off the tcon * tcon->open_file_lock protects the list of open files hanging off the tcon

View File

@ -1191,10 +1191,6 @@ cifs_demultiplex_thread(void *p)
continue; continue;
} }
if (server->large_buf)
buf = server->bigbuf;
server->lstrp = jiffies; server->lstrp = jiffies;
for (i = 0; i < num_mids; i++) { for (i = 0; i < num_mids; i++) {

View File

@ -621,20 +621,10 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
{ {
int rc = -EPERM; int rc = -EPERM;
unsigned int xid; unsigned int xid;
int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink; struct tcon_link *tlink;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct cifs_io_parms io_parms;
char *full_path = NULL; 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)) if (!old_valid_dev(device_number))
return -EINVAL; return -EINVAL;
@ -654,103 +644,12 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
goto mknod_out; goto mknod_out;
} }
if (tcon->unix_ext) { rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon,
struct cifs_unix_set_info_args args = { full_path, mode,
.mode = mode & ~current_umask(), device_number);
.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 */
mknod_out: mknod_out:
kfree(full_path); kfree(full_path);
kfree(buf);
free_xid(xid); free_xid(xid);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
return rc; return rc;

View File

@ -1645,8 +1645,20 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
rc = server->ops->mand_unlock_range(cfile, flock, xid); rc = server->ops->mand_unlock_range(cfile, flock, xid);
out: 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); rc = locks_lock_file_wait(file, flock);
}
return rc; return rc;
} }

View File

@ -1027,6 +1027,131 @@ cifs_can_echo(struct TCP_Server_Info *server)
return false; 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 = { struct smb_version_operations smb1_operations = {
.send_cancel = send_nt_cancel, .send_cancel = send_nt_cancel,
.compare_fids = cifs_compare_fids, .compare_fids = cifs_compare_fids,
@ -1110,6 +1235,7 @@ struct smb_version_operations smb1_operations = {
.get_acl_by_fid = get_cifs_acl_by_fid, .get_acl_by_fid = get_cifs_acl_by_fid,
.set_acl = set_cifs_acl, .set_acl = set_cifs_acl,
#endif /* CIFS_ACL */ #endif /* CIFS_ACL */
.make_node = cifs_make_node,
}; };
struct smb_version_values smb1_values = { struct smb_version_values smb1_values = {

View File

@ -37,6 +37,16 @@
#include "smb2pdu.h" #include "smb2pdu.h"
#include "smb2proto.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 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,
@ -112,14 +122,18 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
PATH_MAX * 2, 0, NULL); PATH_MAX * 2, 0, NULL);
smb2_set_next_command(tcon, &rqst[num_rqst]); smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst++]); smb2_set_related(&rqst[num_rqst++]);
trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid,
full_path);
break; break;
case SMB2_OP_DELETE: case SMB2_OP_DELETE:
trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path);
break; break;
case SMB2_OP_MKDIR: case SMB2_OP_MKDIR:
/* /*
* Directories are created through parameters in the * Directories are created through parameters in the
* SMB2_open() call. * SMB2_open() call.
*/ */
trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path);
break; break;
case SMB2_OP_RMDIR: case SMB2_OP_RMDIR:
memset(&si_iov, 0, sizeof(si_iov)); 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_O_INFO_FILE, 0, data, size);
smb2_set_next_command(tcon, &rqst[num_rqst]); smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst++]); smb2_set_related(&rqst[num_rqst++]);
trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path);
break; break;
case SMB2_OP_SET_EOF: case SMB2_OP_SET_EOF:
memset(&si_iov, 0, sizeof(si_iov)); 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_O_INFO_FILE, 0, data, size);
smb2_set_next_command(tcon, &rqst[num_rqst]); smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst++]); smb2_set_related(&rqst[num_rqst++]);
trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);
break; break;
case SMB2_OP_SET_INFO: case SMB2_OP_SET_INFO:
memset(&si_iov, 0, sizeof(si_iov)); 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_O_INFO_FILE, 0, data, size);
smb2_set_next_command(tcon, &rqst[num_rqst]); smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst++]); smb2_set_related(&rqst[num_rqst++]);
trace_smb3_set_info_compound_enter(xid, ses->Suid, tcon->tid,
full_path);
break; break;
case SMB2_OP_RENAME: case SMB2_OP_RENAME:
memset(&si_iov, 0, sizeof(si_iov)); 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_O_INFO_FILE, 0, data, size);
smb2_set_next_command(tcon, &rqst[num_rqst]); smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst++]); smb2_set_related(&rqst[num_rqst++]);
trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
break; break;
case SMB2_OP_HARDLINK: case SMB2_OP_HARDLINK:
memset(&si_iov, 0, sizeof(si_iov)); 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_O_INFO_FILE, 0, data, size);
smb2_set_next_command(tcon, &rqst[num_rqst]); smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst++]); smb2_set_related(&rqst[num_rqst++]);
trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
break; break;
default: default:
cifs_dbg(VFS, "Invalid command\n"); 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]); SMB2_query_info_free(&rqst[1]);
if (rqst[2].rq_iov) if (rqst[2].rq_iov)
SMB2_close_free(&rqst[2]); 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; break;
case SMB2_OP_DELETE: 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: 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) if (rqst[1].rq_iov)
SMB2_close_free(&rqst[1]); SMB2_close_free(&rqst[1]);
break; break;
case SMB2_OP_HARDLINK: 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: 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: 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: 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: case SMB2_OP_SET_INFO:
if (rqst[1].rq_iov) if (rc)
SMB2_set_info_free(&rqst[1]); trace_smb3_set_info_compound_err(xid, ses->Suid,
if (rqst[2].rq_iov) tcon->tid, rc);
SMB2_close_free(&rqst[2]); else
trace_smb3_set_info_compound_done(xid, ses->Suid,
tcon->tid);
free_set_inf_compound(rqst);
break; break;
} }
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 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); rc = open_shroot(xid, tcon, &fid);
if (rc) if (rc)
goto out; 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); close_shroot(&tcon->crfid);
if (rc)
goto out;
move_smb2_info_to_cifs(data, smb2_data);
goto out; goto out;
} }

View File

@ -185,7 +185,7 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
cifs_num_waiters_inc(server); cifs_num_waiters_inc(server);
rc = wait_event_killable(server->request_q, rc = wait_event_killable(server->request_q,
has_credits(server, &server->credits)); has_credits(server, &server->credits, 1));
cifs_num_waiters_dec(server); cifs_num_waiters_dec(server);
if (rc) if (rc)
return rc; return rc;
@ -619,6 +619,7 @@ smb2_close_cached_fid(struct kref *ref)
SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid, SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
cfid->fid->volatile_fid); cfid->fid->volatile_fid);
cfid->is_valid = false; cfid->is_valid = false;
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) int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
{ {
struct cifs_open_parms oparams; struct cifs_ses *ses = tcon->ses;
int rc; struct TCP_Server_Info *server = ses->server;
__le16 srch_path = 0; /* Null - since an open of top of share */ 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; u8 oplock = SMB2_OPLOCK_LEVEL_II;
mutex_lock(&tcon->crfid.fid_mutex); 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; return 0;
} }
oparams.tcon = tcon; if (smb3_encryption_required(tcon))
oparams.create_options = 0; flags |= CIFS_TRANSFORM_REQ;
oparams.desired_access = FILE_READ_ATTRIBUTES;
oparams.disposition = FILE_OPEN;
oparams.fid = pfid;
oparams.reconnect = false;
rc = SMB2_open(xid, &oparams, &srch_path, &oplock, NULL, NULL, NULL); memset(rqst, 0, sizeof(rqst));
if (rc == 0) { resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid)); memset(rsp_iov, 0, sizeof(rsp_iov));
tcon->crfid.tcon = tcon;
tcon->crfid.is_valid = true; /* Open */
kref_init(&tcon->crfid.refcount); memset(&open_iov, 0, sizeof(open_iov));
kref_get(&tcon->crfid.refcount); 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); 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; return rc;
} }
@ -1253,7 +1330,8 @@ smb2_ioctl_query_info(const unsigned int xid,
struct smb_query_info __user *pqi; struct smb_query_info __user *pqi;
int rc = 0; int rc = 0;
int flags = 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; void *buffer = NULL;
struct smb_rqst rqst[3]; struct smb_rqst rqst[3];
int resp_buftype[3]; int resp_buftype[3];
@ -1263,6 +1341,7 @@ smb2_ioctl_query_info(const unsigned int xid,
u8 oplock = SMB2_OPLOCK_LEVEL_NONE; u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
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 close_iov[1]; struct kvec close_iov[1];
memset(rqst, 0, sizeof(rqst)); memset(rqst, 0, sizeof(rqst));
@ -1313,15 +1392,35 @@ smb2_ioctl_query_info(const unsigned int xid,
smb2_set_next_command(tcon, &rqst[0]); smb2_set_next_command(tcon, &rqst[0]);
/* Query */ /* Query */
memset(&qi_iov, 0, sizeof(qi_iov)); if (qi.flags & PASSTHRU_FSCTL) {
rqst[1].rq_iov = qi_iov; /* Can eventually relax perm check since server enforces too */
rqst[1].rq_nvec = 1; 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, rc = SMB2_ioctl_init(tcon, &rqst[1],
qi.file_info_class, qi.info_type, COMPOUND_FID, COMPOUND_FID,
qi.additional_information, 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.input_buffer_length,
qi.output_buffer_length, buffer); qi.output_buffer_length, buffer);
} else { /* unknown flags */
cifs_dbg(VFS, "invalid passthru query flags: 0x%x\n", qi.flags);
rc = -EINVAL;
}
if (rc) if (rc)
goto iqinf_exit; goto iqinf_exit;
smb2_set_next_command(tcon, &rqst[1]); smb2_set_next_command(tcon, &rqst[1]);
@ -1341,24 +1440,44 @@ smb2_ioctl_query_info(const unsigned int xid,
resp_buftype, rsp_iov); resp_buftype, rsp_iov);
if (rc) if (rc)
goto iqinf_exit; goto iqinf_exit;
pqi = (struct smb_query_info __user *)arg; if (qi.flags & PASSTHRU_FSCTL) {
rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; pqi = (struct smb_query_info __user *)arg;
if (le32_to_cpu(rsp->OutputBufferLength) < qi.input_buffer_length) io_rsp = (struct smb2_ioctl_rsp *)rsp_iov[1].iov_base;
qi.input_buffer_length = le32_to_cpu(rsp->OutputBufferLength); if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length)
if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length, qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
sizeof(qi.input_buffer_length))) { if (copy_to_user(&pqi->input_buffer_length, &qi.input_buffer_length,
rc = -EFAULT; sizeof(qi.input_buffer_length))) {
goto iqinf_exit; rc = -EFAULT;
} goto iqinf_exit;
if (copy_to_user(pqi + 1, rsp->Buffer, qi.input_buffer_length)) { }
rc = -EFAULT; if (copy_to_user(pqi + 1, &io_rsp[1], qi.input_buffer_length)) {
goto iqinf_exit; 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: iqinf_exit:
kfree(buffer); kfree(buffer);
SMB2_open_free(&rqst[0]); 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]); SMB2_close_free(&rqst[2]);
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); free_rsp_buf(resp_buftype[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, static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
loff_t offset, loff_t len, bool keep_size) loff_t offset, loff_t len, bool keep_size)
{ {
struct cifs_ses *ses = tcon->ses;
struct inode *inode; struct inode *inode;
struct cifsInodeInfo *cifsi; 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;
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; long rc;
unsigned int xid; unsigned int xid;
int num = 0, flags = 0;
__le64 eof;
xid = get_xid(); xid = get_xid();
inode = d_inode(cfile->dentry); inode = d_inode(cfile->dentry);
cifsi = CIFS_I(inode); 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 file not oplocked can't be sure whether asking to extend size */
if (!CIFS_CACHE_READ(cifsi)) if (!CIFS_CACHE_READ(cifsi))
if (keep_size == false) { if (keep_size == false) {
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
trace_smb3_zero_err(xid, cfile->fid.persistent_fid,
tcon->tid, ses->Suid, offset, len, rc);
free_xid(xid); free_xid(xid);
return rc; 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)) { if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) {
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
ses->Suid, offset, len, rc);
free_xid(xid); free_xid(xid);
return rc; 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); cifs_dbg(FYI, "offset %lld len %lld", offset, len);
fsctl_buf.FileOffset = cpu_to_le64(offset); fsctl_buf.FileOffset = cpu_to_le64(offset);
fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, if (smb3_encryption_required(tcon))
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, flags |= CIFS_TRANSFORM_REQ;
true /* is_fctl */, (char *)&fsctl_buf,
sizeof(struct file_zero_data_information), NULL, NULL); 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); 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; return rc;
} }
@ -2573,15 +2748,20 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile = file->private_data; struct cifsFileInfo *cfile = file->private_data;
long rc = -EOPNOTSUPP; long rc = -EOPNOTSUPP;
unsigned int xid; unsigned int xid;
__le64 eof;
xid = get_xid(); xid = get_xid();
inode = d_inode(cfile->dentry); inode = d_inode(cfile->dentry);
cifsi = CIFS_I(inode); 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 file not oplocked can't be sure whether asking to extend size */
if (!CIFS_CACHE_READ(cifsi)) if (!CIFS_CACHE_READ(cifsi))
if (keep_size == false) { if (keep_size == false) {
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, off, len, rc);
free_xid(xid); free_xid(xid);
return rc; 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 */ /* BB: in future add else clause to extend file */
else else
rc = -EOPNOTSUPP; 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); free_xid(xid);
return rc; 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))) { if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) {
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, off, len, rc);
free_xid(xid); free_xid(xid);
return rc; 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); free_xid(xid);
return rc; return rc;
@ -3604,6 +3807,104 @@ smb2_next_header(char *buf)
return le32_to_cpu(hdr->NextCommand); 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 = { struct smb_version_operations smb20_operations = {
.compare_fids = smb2_compare_fids, .compare_fids = smb2_compare_fids,
.setup_request = smb2_setup_request, .setup_request = smb2_setup_request,
@ -3698,6 +3999,7 @@ struct smb_version_operations smb20_operations = {
#endif /* CIFS_ACL */ #endif /* CIFS_ACL */
.next_header = smb2_next_header, .next_header = smb2_next_header,
.ioctl_query_info = smb2_ioctl_query_info, .ioctl_query_info = smb2_ioctl_query_info,
.make_node = smb2_make_node,
}; };
struct smb_version_operations smb21_operations = { struct smb_version_operations smb21_operations = {
@ -3796,6 +4098,7 @@ struct smb_version_operations smb21_operations = {
#endif /* CIFS_ACL */ #endif /* CIFS_ACL */
.next_header = smb2_next_header, .next_header = smb2_next_header,
.ioctl_query_info = smb2_ioctl_query_info, .ioctl_query_info = smb2_ioctl_query_info,
.make_node = smb2_make_node,
}; };
struct smb_version_operations smb30_operations = { struct smb_version_operations smb30_operations = {
@ -3903,6 +4206,7 @@ struct smb_version_operations smb30_operations = {
#endif /* CIFS_ACL */ #endif /* CIFS_ACL */
.next_header = smb2_next_header, .next_header = smb2_next_header,
.ioctl_query_info = smb2_ioctl_query_info, .ioctl_query_info = smb2_ioctl_query_info,
.make_node = smb2_make_node,
}; };
struct smb_version_operations smb311_operations = { struct smb_version_operations smb311_operations = {
@ -4011,6 +4315,7 @@ struct smb_version_operations smb311_operations = {
#endif /* CIFS_ACL */ #endif /* CIFS_ACL */
.next_header = smb2_next_header, .next_header = smb2_next_header,
.ioctl_query_info = smb2_ioctl_query_info, .ioctl_query_info = smb2_ioctl_query_info,
.make_node = smb2_make_node,
}; };
struct smb_version_values smb20_values = { struct smb_version_values smb20_values = {

View File

@ -1797,9 +1797,10 @@ create_reconnect_durable_buf(struct cifs_fid *fid)
return buf; return buf;
} }
static __u8 __u8
parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp, smb2_parse_lease_state(struct TCP_Server_Info *server,
unsigned int *epoch, char *lease_key) struct smb2_create_rsp *rsp,
unsigned int *epoch, char *lease_key)
{ {
char *data_offset; char *data_offset;
struct create_context *cc; 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) if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE)
*oplock = parse_lease_state(server, rsp, &oparms->fid->epoch, *oplock = smb2_parse_lease_state(server, rsp,
oparms->fid->lease_key); &oparms->fid->epoch,
oparms->fid->lease_key);
else else
*oplock = rsp->OplockLevel; *oplock = rsp->OplockLevel;
creat_exit: creat_exit:
@ -2466,65 +2468,46 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
return rc; return rc;
} }
/*
* SMB2 IOCTL is used for both IOCTLs and FSCTLs
*/
int int
SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
u64 volatile_fid, u32 opcode, bool is_fsctl, u64 persistent_fid, u64 volatile_fid, u32 opcode,
char *in_data, u32 indatalen, bool is_fsctl, char *in_data, u32 indatalen)
char **out_data, u32 *plen /* returned data len */)
{ {
struct smb_rqst rqst;
struct smb2_ioctl_req *req; struct smb2_ioctl_req *req;
struct smb2_ioctl_rsp *rsp; struct kvec *iov = rqst->rq_iov;
struct cifs_ses *ses;
struct kvec iov[2];
struct kvec rsp_iov;
int resp_buftype;
int n_iov;
int rc = 0;
int flags = 0;
unsigned int total_len; unsigned int total_len;
int rc;
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;
rc = smb2_plain_req_init(SMB2_IOCTL, tcon, (void **) &req, &total_len); rc = smb2_plain_req_init(SMB2_IOCTL, tcon, (void **) &req, &total_len);
if (rc) if (rc)
return rc; return rc;
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
req->CtlCode = cpu_to_le32(opcode); req->CtlCode = cpu_to_le32(opcode);
req->PersistentFileId = persistent_fid; req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_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) { if (indatalen) {
req->InputCount = cpu_to_le32(indatalen); req->InputCount = cpu_to_le32(indatalen);
/* do not set InputOffset if no input data */ /* do not set InputOffset if no input data */
req->InputOffset = req->InputOffset =
cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer)); 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_base = in_data;
iov[1].iov_len = indatalen; iov[1].iov_len = indatalen;
n_iov = 2; } else {
} else rqst->rq_nvec = 1;
n_iov = 1; iov[0].iov_len = total_len;
}
req->OutputOffset = 0; req->OutputOffset = 0;
req->OutputCount = 0; /* MBZ */ req->OutputCount = 0; /* MBZ */
@ -2546,33 +2529,70 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
else else
req->Flags = 0; 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 */ /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */
if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO)
req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; 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(&rqst, 0, sizeof(struct smb_rqst));
memset(&iov, 0, sizeof(iov));
rqst.rq_iov = iov; rqst.rq_iov = iov;
rqst.rq_nvec = n_iov; rqst.rq_nvec = 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, rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags,
&rsp_iov); &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base; rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base;
if (rc != 0) if (rc != 0)
@ -2622,6 +2642,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
} }
ioctl_exit: ioctl_exit:
SMB2_ioctl_free(&rqst);
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
return rc; return rc;
} }

View File

@ -959,6 +959,13 @@ struct duplicate_extents_to_file {
__le64 ByteCount; /* Bytes to be copied */ __le64 ByteCount; /* Bytes to be copied */
} __packed; } __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_ioctl_req {
struct smb2_sync_hdr sync_hdr; struct smb2_sync_hdr sync_hdr;
__le16 StructureSize; /* Must be 57 */ __le16 StructureSize; /* Must be 57 */

View File

@ -144,6 +144,10 @@ extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 opcode, u64 persistent_fid, u64 volatile_fid, u32 opcode,
bool is_fsctl, char *in_data, u32 indatalen, bool is_fsctl, char *in_data, u32 indatalen,
char **out_data, u32 *plen /* returned data len */); 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, extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id); u64 persistent_file_id, u64 volatile_file_id);
extern int SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon,
@ -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 *, extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *,
enum securityEnum); 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 smb3_encryption_required(const struct cifs_tcon *tcon);
extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length, extern int smb2_validate_iov(unsigned int offset, unsigned int buffer_length,
struct kvec *iov, unsigned int min_buf_size); struct kvec *iov, unsigned int min_buf_size);

View File

@ -30,9 +30,9 @@
*/ */
#define STATUS_SEVERITY_SUCCESS __constant_cpu_to_le32(0x0000) #define STATUS_SEVERITY_SUCCESS __constant_cpu_to_le32(0x0000)
#define STATUS_SEVERITY_INFORMATIONAL __constanst_cpu_to_le32(0x0001) #define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001)
#define STATUS_SEVERITY_WARNING __constanst_cpu_to_le32(0x0002) #define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002)
#define STATUS_SEVERITY_ERROR __constanst_cpu_to_le32(0x0003) #define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003)
struct ntstatus { struct ntstatus {
/* Facility is the high 12 bits of the following field */ /* Facility is the high 12 bits of the following field */

View File

@ -59,6 +59,8 @@ DEFINE_EVENT(smb3_rw_err_class, smb3_##name, \
DEFINE_SMB3_RW_ERR_EVENT(write_err); DEFINE_SMB3_RW_ERR_EVENT(write_err);
DEFINE_SMB3_RW_ERR_EVENT(read_err); DEFINE_SMB3_RW_ERR_EVENT(read_err);
DEFINE_SMB3_RW_ERR_EVENT(query_dir_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 */ /* 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(write_enter);
DEFINE_SMB3_RW_DONE_EVENT(read_enter); DEFINE_SMB3_RW_DONE_EVENT(read_enter);
DEFINE_SMB3_RW_DONE_EVENT(query_dir_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(write_done);
DEFINE_SMB3_RW_DONE_EVENT(read_done); DEFINE_SMB3_RW_DONE_EVENT(read_done);
DEFINE_SMB3_RW_DONE_EVENT(query_dir_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 * 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(set_info_err);
DEFINE_SMB3_INF_ERR_EVENT(fsctl_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 * 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)) TP_ARGS(currmid, hostname, credits))
DEFINE_SMB3_CREDIT_EVENT(reconnect_with_invalid_credits); DEFINE_SMB3_CREDIT_EVENT(reconnect_with_invalid_credits);
DEFINE_SMB3_CREDIT_EVENT(credit_timeout);
#endif /* _CIFS_TRACE_H */ #endif /* _CIFS_TRACE_H */

View File

@ -486,15 +486,31 @@ smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
} }
static int static int
wait_for_free_credits(struct TCP_Server_Info *server, const int timeout, wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
int *credits, unsigned int *instance) const int timeout, const int flags,
unsigned int *instance)
{ {
int rc; 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; *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); 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 */ /* oplock breaks must not be held up */
server->in_flight++; server->in_flight++;
*credits -= 1; *credits -= 1;
@ -504,14 +520,21 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
} }
while (1) { while (1) {
if (*credits <= 0) { if (*credits < num_credits) {
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
cifs_num_waiters_inc(server); cifs_num_waiters_inc(server);
rc = wait_event_killable(server->request_q, rc = wait_event_killable_timeout(server->request_q,
has_credits(server, credits)); has_credits(server, credits, num_credits), t);
cifs_num_waiters_dec(server); cifs_num_waiters_dec(server);
if (rc) if (!rc) {
return 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); spin_lock(&server->req_lock);
} else { } else {
if (server->tcpStatus == CifsExiting) { if (server->tcpStatus == CifsExiting) {
@ -519,15 +542,53 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
return -ENOENT; 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 * Can not count locking commands against total
* as they are allowed to block on server. * as they are allowed to block on server.
*/ */
/* update # of requests on the wire to server */ /* update # of requests on the wire to server */
if (timeout != CIFS_BLOCKING_OP) { if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) {
*credits -= 1; *credits -= num_credits;
server->in_flight++; server->in_flight += num_credits;
*instance = server->reconnect_instance; *instance = server->reconnect_instance;
} }
spin_unlock(&server->req_lock); spin_unlock(&server->req_lock);
@ -538,16 +599,36 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int timeout,
} }
static int static int
wait_for_free_request(struct TCP_Server_Info *server, const int timeout, wait_for_free_request(struct TCP_Server_Info *server, const int flags,
const int optype, unsigned int *instance) unsigned int *instance)
{ {
int *val; return wait_for_free_credits(server, 1, -1, flags,
instance);
}
val = server->ops->get_credits_field(server, optype); static int
/* Since an echo is already inflight, no need to wait to send another */ wait_for_compound_request(struct TCP_Server_Info *server, int num,
if (*val <= 0 && optype == CIFS_ECHO_OP) const int flags, unsigned int *instance)
return -EAGAIN; {
return wait_for_free_credits(server, timeout, val, 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 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, mid_handle_t *handle, void *cbdata, const int flags,
const struct cifs_credits *exist_credits) const struct cifs_credits *exist_credits)
{ {
int rc, timeout, optype; int rc;
struct mid_q_entry *mid; struct mid_q_entry *mid;
struct cifs_credits credits = { .value = 0, .instance = 0 }; struct cifs_credits credits = { .value = 0, .instance = 0 };
unsigned int instance; unsigned int instance;
int optype;
timeout = flags & CIFS_TIMEOUT_MASK;
optype = flags & CIFS_OP_MASK; optype = flags & CIFS_OP_MASK;
if ((flags & CIFS_HAS_CREDITS) == 0) { 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) if (rc)
return rc; return rc;
credits.value = 1; 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, const int flags, const int num_rqst, struct smb_rqst *rqst,
int *resp_buf_type, struct kvec *resp_iov) int *resp_buf_type, struct kvec *resp_iov)
{ {
int i, j, rc = 0; int i, j, optype, rc = 0;
int timeout, optype;
struct mid_q_entry *midQ[MAX_COMPOUND]; struct mid_q_entry *midQ[MAX_COMPOUND];
bool cancelled_mid[MAX_COMPOUND] = {false}; bool cancelled_mid[MAX_COMPOUND] = {false};
struct cifs_credits credits[MAX_COMPOUND] = { struct cifs_credits credits[MAX_COMPOUND] = {
{ .value = 0, .instance = 0 } { .value = 0, .instance = 0 }
}; };
unsigned int instance; unsigned int instance;
unsigned int first_instance = 0;
char *buf; char *buf;
timeout = flags & CIFS_TIMEOUT_MASK;
optype = flags & CIFS_OP_MASK; optype = flags & CIFS_OP_MASK;
for (i = 0; i < num_rqst; i++) 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) if (ses->server->tcpStatus == CifsExiting)
return -ENOENT; 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 * Wait for all the requests to become available.
* there are requests in flight that may bring credits from the server.
* This approach still leaves the possibility to be stuck waiting for * This approach still leaves the possibility to be stuck waiting for
* credits if the server doesn't grant credits to the outstanding * credits if the server doesn't grant credits to the outstanding
* requests. This should be fixed by returning immediately and letting * requests and if the client is completely idle, not generating any
* a caller fallback to sequential commands instead of compounding. * other requests.
* Ensure we obtain 1 credit per request in the compound chain. * 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++) { for (i = 0; i < num_rqst; i++) {
rc = wait_for_free_request(ses->server, timeout, optype, credits[i].value = 1;
&instance); credits[i].instance = 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;
}
} }
setup_rqsts:
/* /*
* Make sure that we sign in the same order that we send on this socket * 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 * 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 * All the parts of the compound chain belong obtained credits from the
* same session (see the appropriate checks above). In the same time * same session. We can not use credits obtained from the previous
* there might be reconnects after those checks but before we acquired
* the srv_mutex. We can not use credits obtained from the previous
* session to send this request. Check if there were reconnects after * session to send this request. Check if there were reconnects after
* we obtained credits and return -EAGAIN in such cases to let callers * we obtained credits and return -EAGAIN in such cases to let callers
* handle it. * handle it.
*/ */
if (first_instance != ses->server->reconnect_instance) { if (instance != ses->server->reconnect_instance) {
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&ses->server->srv_mutex);
for (j = 0; j < num_rqst; j++) for (j = 0; j < num_rqst; j++)
add_credits(ses->server, &credits[j], optype); 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, smb311_update_preauth_hash(ses, rqst[0].rq_iov,
rqst[0].rq_nvec); rqst[0].rq_nvec);
if (timeout == CIFS_ASYNC_OP) if ((flags & CIFS_TIMEOUT_MASK) == CIFS_ASYNC_OP)
goto out; goto out;
for (i = 0; i < num_rqst; i++) { for (i = 0; i < num_rqst; i++) {
@ -1194,7 +1213,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
int int
SendReceive(const unsigned int xid, struct cifs_ses *ses, SendReceive(const unsigned int xid, struct cifs_ses *ses,
struct smb_hdr *in_buf, struct smb_hdr *out_buf, 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; int rc = 0;
struct mid_q_entry *midQ; struct mid_q_entry *midQ;
@ -1225,7 +1244,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
return -EIO; 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) if (rc)
return rc; return rc;
@ -1264,7 +1283,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
if (rc < 0) if (rc < 0)
goto out; goto out;
if (timeout == CIFS_ASYNC_OP) if ((flags & CIFS_TIMEOUT_MASK) == CIFS_ASYNC_OP)
goto out; goto out;
rc = wait_for_response(ses->server, midQ); rc = wait_for_response(ses->server, midQ);
@ -1367,8 +1386,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
return -EIO; return -EIO;
} }
rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP, 0, rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP, &instance);
&instance);
if (rc) if (rc)
return rc; return rc;