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
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
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;
|
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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
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(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 */
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue