Commit 145f47c7 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull smb3 fixes from Steve French:
 "Four smb3 fixes for stable:

   - fix an open path where we had an unitialized structure

   - fix for snapshot (previous version) enumeration

   - allow reconnect timeout on handles to be configurable to better
     handle network or server crash

   - correctly handle lack of file_all_info structure"

* tag '5.1-rc3-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: a smb2_validate_and_copy_iov failure does not mean the handle is invalid.
  SMB3: Allow persistent handle timeout to be configurable on mount
  smb3: Fix enumerating snapshots to Azure
  cifs: fix kref underflow in close_shroot()
parents 8ed86627 4811e309
...@@ -559,6 +559,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) ...@@ -559,6 +559,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
tcon->ses->server->echo_interval / HZ); tcon->ses->server->echo_interval / HZ);
if (tcon->snapshot_time) if (tcon->snapshot_time)
seq_printf(s, ",snapshot=%llu", tcon->snapshot_time); seq_printf(s, ",snapshot=%llu", tcon->snapshot_time);
if (tcon->handle_timeout)
seq_printf(s, ",handletimeout=%u", tcon->handle_timeout);
/* convert actimeo and display it in seconds */ /* convert actimeo and display it in seconds */
seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ); seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ);
......
...@@ -59,6 +59,12 @@ ...@@ -59,6 +59,12 @@
*/ */
#define CIFS_MAX_ACTIMEO (1 << 30) #define CIFS_MAX_ACTIMEO (1 << 30)
/*
* Max persistent and resilient handle timeout (milliseconds).
* Windows durable max was 960000 (16 minutes)
*/
#define SMB3_MAX_HANDLE_TIMEOUT 960000
/* /*
* MAX_REQ is the maximum number of requests that WE will send * MAX_REQ is the maximum number of requests that WE will send
* on one socket concurrently. * on one socket concurrently.
...@@ -586,6 +592,7 @@ struct smb_vol { ...@@ -586,6 +592,7 @@ struct smb_vol {
struct nls_table *local_nls; struct nls_table *local_nls;
unsigned int echo_interval; /* echo interval in secs */ unsigned int echo_interval; /* echo interval in secs */
__u64 snapshot_time; /* needed for timewarp tokens */ __u64 snapshot_time; /* needed for timewarp tokens */
__u32 handle_timeout; /* persistent and durable handle timeout in ms */
unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
}; };
...@@ -1058,6 +1065,7 @@ struct cifs_tcon { ...@@ -1058,6 +1065,7 @@ struct cifs_tcon {
__u32 vol_serial_number; __u32 vol_serial_number;
__le64 vol_create_time; __le64 vol_create_time;
__u64 snapshot_time; /* for timewarp tokens - timestamp of snapshot */ __u64 snapshot_time; /* for timewarp tokens - timestamp of snapshot */
__u32 handle_timeout; /* persistent and durable handle timeout in ms */
__u32 ss_flags; /* sector size flags */ __u32 ss_flags; /* sector size flags */
__u32 perf_sector_size; /* best sector size for perf */ __u32 perf_sector_size; /* best sector size for perf */
__u32 max_chunks; __u32 max_chunks;
......
...@@ -103,7 +103,7 @@ enum { ...@@ -103,7 +103,7 @@ enum {
Opt_cruid, Opt_gid, Opt_file_mode, Opt_cruid, Opt_gid, Opt_file_mode,
Opt_dirmode, Opt_port, Opt_dirmode, Opt_port,
Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo, Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo,
Opt_echo_interval, Opt_max_credits, Opt_echo_interval, Opt_max_credits, Opt_handletimeout,
Opt_snapshot, Opt_snapshot,
/* Mount options which take string value */ /* Mount options which take string value */
...@@ -208,6 +208,7 @@ static const match_table_t cifs_mount_option_tokens = { ...@@ -208,6 +208,7 @@ static const match_table_t cifs_mount_option_tokens = {
{ Opt_rsize, "rsize=%s" }, { Opt_rsize, "rsize=%s" },
{ Opt_wsize, "wsize=%s" }, { Opt_wsize, "wsize=%s" },
{ Opt_actimeo, "actimeo=%s" }, { Opt_actimeo, "actimeo=%s" },
{ Opt_handletimeout, "handletimeout=%s" },
{ Opt_echo_interval, "echo_interval=%s" }, { Opt_echo_interval, "echo_interval=%s" },
{ Opt_max_credits, "max_credits=%s" }, { Opt_max_credits, "max_credits=%s" },
{ Opt_snapshot, "snapshot=%s" }, { Opt_snapshot, "snapshot=%s" },
...@@ -1619,6 +1620,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -1619,6 +1620,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
vol->actimeo = CIFS_DEF_ACTIMEO; vol->actimeo = CIFS_DEF_ACTIMEO;
/* Most clients set timeout to 0, allows server to use its default */
vol->handle_timeout = 0; /* See MS-SMB2 spec section 2.2.14.2.12 */
/* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */ /* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */
vol->ops = &smb30_operations; vol->ops = &smb30_operations;
vol->vals = &smbdefault_values; vol->vals = &smbdefault_values;
...@@ -2017,6 +2021,18 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, ...@@ -2017,6 +2021,18 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
goto cifs_parse_mount_err; goto cifs_parse_mount_err;
} }
break; break;
case Opt_handletimeout:
if (get_option_ul(args, &option)) {
cifs_dbg(VFS, "%s: Invalid handletimeout value\n",
__func__);
goto cifs_parse_mount_err;
}
vol->handle_timeout = option;
if (vol->handle_timeout > SMB3_MAX_HANDLE_TIMEOUT) {
cifs_dbg(VFS, "Invalid handle cache timeout, longer than 16 minutes\n");
goto cifs_parse_mount_err;
}
break;
case Opt_echo_interval: case Opt_echo_interval:
if (get_option_ul(args, &option)) { if (get_option_ul(args, &option)) {
cifs_dbg(VFS, "%s: Invalid echo interval value\n", cifs_dbg(VFS, "%s: Invalid echo interval value\n",
...@@ -3183,6 +3199,8 @@ static int match_tcon(struct cifs_tcon *tcon, struct smb_vol *volume_info) ...@@ -3183,6 +3199,8 @@ static int match_tcon(struct cifs_tcon *tcon, struct smb_vol *volume_info)
return 0; return 0;
if (tcon->snapshot_time != volume_info->snapshot_time) if (tcon->snapshot_time != volume_info->snapshot_time)
return 0; return 0;
if (tcon->handle_timeout != volume_info->handle_timeout)
return 0;
return 1; return 1;
} }
...@@ -3297,6 +3315,16 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) ...@@ -3297,6 +3315,16 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
tcon->snapshot_time = volume_info->snapshot_time; tcon->snapshot_time = volume_info->snapshot_time;
} }
if (volume_info->handle_timeout) {
if (ses->server->vals->protocol_id == 0) {
cifs_dbg(VFS,
"Use SMB2.1 or later for handle timeout option\n");
rc = -EOPNOTSUPP;
goto out_fail;
} else
tcon->handle_timeout = volume_info->handle_timeout;
}
tcon->ses = ses; tcon->ses = ses;
if (volume_info->password) { if (volume_info->password) {
tcon->password = kstrdup(volume_info->password, GFP_KERNEL); tcon->password = kstrdup(volume_info->password, GFP_KERNEL);
......
...@@ -68,13 +68,15 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, ...@@ -68,13 +68,15 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
if (oparms->tcon->use_resilient) { if (oparms->tcon->use_resilient) {
nr_ioctl_req.Timeout = 0; /* use server default (120 seconds) */ /* default timeout is 0, servers pick default (120 seconds) */
nr_ioctl_req.Timeout =
cpu_to_le32(oparms->tcon->handle_timeout);
nr_ioctl_req.Reserved = 0; nr_ioctl_req.Reserved = 0;
rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid, rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid,
fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY, fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY,
true /* is_fsctl */, true /* is_fsctl */,
(char *)&nr_ioctl_req, sizeof(nr_ioctl_req), (char *)&nr_ioctl_req, sizeof(nr_ioctl_req),
NULL, NULL /* no return info */); CIFSMaxBufSize, NULL, NULL /* no return info */);
if (rc == -EOPNOTSUPP) { if (rc == -EOPNOTSUPP) {
cifs_dbg(VFS, cifs_dbg(VFS,
"resiliency not supported by server, disabling\n"); "resiliency not supported by server, disabling\n");
......
...@@ -581,7 +581,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -581,7 +581,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */, FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
NULL /* no data input */, 0 /* no data input */, NULL /* no data input */, 0 /* no data input */,
(char **)&out_buf, &ret_data_len); CIFSMaxBufSize, (char **)&out_buf, &ret_data_len);
if (rc == -EOPNOTSUPP) { if (rc == -EOPNOTSUPP) {
cifs_dbg(FYI, cifs_dbg(FYI,
"server does not support query network interfaces\n"); "server does not support query network interfaces\n");
...@@ -717,32 +717,28 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) ...@@ -717,32 +717,28 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
oparms.fid->mid = le64_to_cpu(o_rsp->sync_hdr.MessageId); oparms.fid->mid = le64_to_cpu(o_rsp->sync_hdr.MessageId);
#endif /* CIFS_DEBUG2 */ #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)); memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
tcon->crfid.tcon = tcon; tcon->crfid.tcon = tcon;
tcon->crfid.is_valid = true; tcon->crfid.is_valid = true;
kref_init(&tcon->crfid.refcount); kref_init(&tcon->crfid.refcount);
kref_get(&tcon->crfid.refcount);
if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
kref_get(&tcon->crfid.refcount);
oplock = smb2_parse_lease_state(server, o_rsp,
&oparms.fid->epoch,
oparms.fid->lease_key);
} else
goto oshr_exit;
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; 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)) if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
goto oshr_exit; goto oshr_exit;
rc = smb2_validate_and_copy_iov( if (!smb2_validate_and_copy_iov(
le16_to_cpu(qi_rsp->OutputBufferOffset), le16_to_cpu(qi_rsp->OutputBufferOffset),
sizeof(struct smb2_file_all_info), sizeof(struct smb2_file_all_info),
&rsp_iov[1], sizeof(struct smb2_file_all_info), &rsp_iov[1], sizeof(struct smb2_file_all_info),
(char *)&tcon->crfid.file_all_info); (char *)&tcon->crfid.file_all_info))
if (rc) tcon->crfid.file_all_info_is_valid = 1;
goto oshr_exit;
tcon->crfid.file_all_info_is_valid = 1;
oshr_exit: oshr_exit:
mutex_unlock(&tcon->crfid.fid_mutex); mutex_unlock(&tcon->crfid.fid_mutex);
...@@ -1299,7 +1295,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1299,7 +1295,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */, FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */,
NULL, 0 /* no input */, NULL, 0 /* no input */, CIFSMaxBufSize,
(char **)&res_key, &ret_data_len); (char **)&res_key, &ret_data_len);
if (rc) { if (rc) {
...@@ -1404,7 +1400,7 @@ smb2_ioctl_query_info(const unsigned int xid, ...@@ -1404,7 +1400,7 @@ smb2_ioctl_query_info(const unsigned int xid,
rc = SMB2_ioctl_init(tcon, &rqst[1], rc = SMB2_ioctl_init(tcon, &rqst[1],
COMPOUND_FID, COMPOUND_FID, COMPOUND_FID, COMPOUND_FID,
qi.info_type, true, NULL, qi.info_type, true, NULL,
0); 0, CIFSMaxBufSize);
} }
} else if (qi.flags == PASSTHRU_QUERY_INFO) { } else if (qi.flags == PASSTHRU_QUERY_INFO) {
memset(&qi_iov, 0, sizeof(qi_iov)); memset(&qi_iov, 0, sizeof(qi_iov));
...@@ -1532,8 +1528,8 @@ smb2_copychunk_range(const unsigned int xid, ...@@ -1532,8 +1528,8 @@ smb2_copychunk_range(const unsigned int xid,
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
true /* is_fsctl */, (char *)pcchunk, true /* is_fsctl */, (char *)pcchunk,
sizeof(struct copychunk_ioctl), (char **)&retbuf, sizeof(struct copychunk_ioctl), CIFSMaxBufSize,
&ret_data_len); (char **)&retbuf, &ret_data_len);
if (rc == 0) { if (rc == 0) {
if (ret_data_len != if (ret_data_len !=
sizeof(struct copychunk_ioctl_rsp)) { sizeof(struct copychunk_ioctl_rsp)) {
...@@ -1693,7 +1689,7 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1693,7 +1689,7 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, FSCTL_SET_SPARSE, cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
true /* is_fctl */, true /* is_fctl */,
&setsparse, 1, NULL, NULL); &setsparse, 1, CIFSMaxBufSize, NULL, NULL);
if (rc) { if (rc) {
tcon->broken_sparse_sup = true; tcon->broken_sparse_sup = true;
cifs_dbg(FYI, "set sparse rc = %d\n", rc); cifs_dbg(FYI, "set sparse rc = %d\n", rc);
...@@ -1766,7 +1762,7 @@ smb2_duplicate_extents(const unsigned int xid, ...@@ -1766,7 +1762,7 @@ smb2_duplicate_extents(const unsigned int xid,
true /* is_fsctl */, true /* is_fsctl */,
(char *)&dup_ext_buf, (char *)&dup_ext_buf,
sizeof(struct duplicate_extents_to_file), sizeof(struct duplicate_extents_to_file),
NULL, CIFSMaxBufSize, NULL,
&ret_data_len); &ret_data_len);
if (ret_data_len > 0) if (ret_data_len > 0)
...@@ -1801,7 +1797,7 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1801,7 +1797,7 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
true /* is_fsctl */, true /* is_fsctl */,
(char *)&integr_info, (char *)&integr_info,
sizeof(struct fsctl_set_integrity_information_req), sizeof(struct fsctl_set_integrity_information_req),
NULL, CIFSMaxBufSize, NULL,
&ret_data_len); &ret_data_len);
} }
...@@ -1809,6 +1805,8 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1809,6 +1805,8 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
/* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */ /* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */
#define GMT_TOKEN_SIZE 50 #define GMT_TOKEN_SIZE 50
#define MIN_SNAPSHOT_ARRAY_SIZE 16 /* See MS-SMB2 section 3.3.5.15.1 */
/* /*
* Input buffer contains (empty) struct smb_snapshot array with size filled in * Input buffer contains (empty) struct smb_snapshot array with size filled in
* For output see struct SRV_SNAPSHOT_ARRAY in MS-SMB2 section 2.2.32.2 * For output see struct SRV_SNAPSHOT_ARRAY in MS-SMB2 section 2.2.32.2
...@@ -1820,13 +1818,29 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1820,13 +1818,29 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
char *retbuf = NULL; char *retbuf = NULL;
unsigned int ret_data_len = 0; unsigned int ret_data_len = 0;
int rc; int rc;
u32 max_response_size;
struct smb_snapshot_array snapshot_in; struct smb_snapshot_array snapshot_in;
if (get_user(ret_data_len, (unsigned int __user *)ioc_buf))
return -EFAULT;
/*
* Note that for snapshot queries that servers like Azure expect that
* the first query be minimal size (and just used to get the number/size
* of previous versions) so response size must be specified as EXACTLY
* sizeof(struct snapshot_array) which is 16 when rounded up to multiple
* of eight bytes.
*/
if (ret_data_len == 0)
max_response_size = MIN_SNAPSHOT_ARRAY_SIZE;
else
max_response_size = CIFSMaxBufSize;
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->fid.volatile_fid,
FSCTL_SRV_ENUMERATE_SNAPSHOTS, FSCTL_SRV_ENUMERATE_SNAPSHOTS,
true /* is_fsctl */, true /* is_fsctl */,
NULL, 0 /* no input data */, NULL, 0 /* no input data */, max_response_size,
(char **)&retbuf, (char **)&retbuf,
&ret_data_len); &ret_data_len);
cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n", cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n",
...@@ -2304,7 +2318,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, ...@@ -2304,7 +2318,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
FSCTL_DFS_GET_REFERRALS, FSCTL_DFS_GET_REFERRALS,
true /* is_fsctl */, true /* is_fsctl */,
(char *)dfs_req, dfs_req_size, (char *)dfs_req, dfs_req_size, CIFSMaxBufSize,
(char **)&dfs_rsp, &dfs_rsp_size); (char **)&dfs_rsp, &dfs_rsp_size);
} while (rc == -EAGAIN); } while (rc == -EAGAIN);
...@@ -2658,7 +2672,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, ...@@ -2658,7 +2672,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
rc = SMB2_ioctl_init(tcon, &rqst[num++], cfile->fid.persistent_fid, rc = SMB2_ioctl_init(tcon, &rqst[num++], cfile->fid.persistent_fid,
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
true /* is_fctl */, (char *)&fsctl_buf, true /* is_fctl */, (char *)&fsctl_buf,
sizeof(struct file_zero_data_information)); sizeof(struct file_zero_data_information),
CIFSMaxBufSize);
if (rc) if (rc)
goto zero_range_exit; goto zero_range_exit;
...@@ -2735,7 +2750,8 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, ...@@ -2735,7 +2750,8 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
true /* is_fctl */, (char *)&fsctl_buf, true /* is_fctl */, (char *)&fsctl_buf,
sizeof(struct file_zero_data_information), NULL, NULL); sizeof(struct file_zero_data_information),
CIFSMaxBufSize, NULL, NULL);
free_xid(xid); free_xid(xid);
return rc; return rc;
} }
......
...@@ -1002,7 +1002,8 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -1002,7 +1002,8 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */, FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
(char *)pneg_inbuf, inbuflen, (char **)&pneg_rsp, &rsplen); (char *)pneg_inbuf, inbuflen, CIFSMaxBufSize,
(char **)&pneg_rsp, &rsplen);
if (rc == -EOPNOTSUPP) { if (rc == -EOPNOTSUPP) {
/* /*
* Old Windows versions or Netapp SMB server can return * Old Windows versions or Netapp SMB server can return
...@@ -1858,8 +1859,9 @@ add_lease_context(struct TCP_Server_Info *server, struct kvec *iov, ...@@ -1858,8 +1859,9 @@ add_lease_context(struct TCP_Server_Info *server, struct kvec *iov,
} }
static struct create_durable_v2 * static struct create_durable_v2 *
create_durable_v2_buf(struct cifs_fid *pfid) create_durable_v2_buf(struct cifs_open_parms *oparms)
{ {
struct cifs_fid *pfid = oparms->fid;
struct create_durable_v2 *buf; struct create_durable_v2 *buf;
buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL);
...@@ -1873,7 +1875,14 @@ create_durable_v2_buf(struct cifs_fid *pfid) ...@@ -1873,7 +1875,14 @@ create_durable_v2_buf(struct cifs_fid *pfid)
(struct create_durable_v2, Name)); (struct create_durable_v2, Name));
buf->ccontext.NameLength = cpu_to_le16(4); buf->ccontext.NameLength = cpu_to_le16(4);
buf->dcontext.Timeout = 0; /* Should this be configurable by workload */ /*
* NB: Handle timeout defaults to 0, which allows server to choose
* (most servers default to 120 seconds) and most clients default to 0.
* This can be overridden at mount ("handletimeout=") if the user wants
* a different persistent (or resilient) handle timeout for all opens
* opens on a particular SMB3 mount.
*/
buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout);
buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
generate_random_uuid(buf->dcontext.CreateGuid); generate_random_uuid(buf->dcontext.CreateGuid);
memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
...@@ -1926,7 +1935,7 @@ add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec, ...@@ -1926,7 +1935,7 @@ add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec,
struct smb2_create_req *req = iov[0].iov_base; struct smb2_create_req *req = iov[0].iov_base;
unsigned int num = *num_iovec; unsigned int num = *num_iovec;
iov[num].iov_base = create_durable_v2_buf(oparms->fid); iov[num].iov_base = create_durable_v2_buf(oparms);
if (iov[num].iov_base == NULL) if (iov[num].iov_base == NULL)
return -ENOMEM; return -ENOMEM;
iov[num].iov_len = sizeof(struct create_durable_v2); iov[num].iov_len = sizeof(struct create_durable_v2);
...@@ -2478,7 +2487,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, ...@@ -2478,7 +2487,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
int int
SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
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,
__u32 max_response_size)
{ {
struct smb2_ioctl_req *req; struct smb2_ioctl_req *req;
struct kvec *iov = rqst->rq_iov; struct kvec *iov = rqst->rq_iov;
...@@ -2520,16 +2530,21 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, ...@@ -2520,16 +2530,21 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
req->OutputCount = 0; /* MBZ */ req->OutputCount = 0; /* MBZ */
/* /*
* Could increase MaxOutputResponse, but that would require more * In most cases max_response_size is set to 16K (CIFSMaxBufSize)
* than one credit. Windows typically sets this smaller, but for some * We Could increase default MaxOutputResponse, but that could require
* more credits. Windows typically sets this smaller, but for some
* ioctls it may be useful to allow server to send more. No point * ioctls it may be useful to allow server to send more. No point
* limiting what the server can send as long as fits in one credit * limiting what the server can send as long as fits in one credit
* Unfortunately - we can not handle more than CIFS_MAX_MSG_SIZE * We can not handle more than CIFS_MAX_BUF_SIZE yet but may want
* (by default, note that it can be overridden to make max larger) * to increase this limit up in the future.
* in responses (except for read responses which can be bigger. * Note that for snapshot queries that servers like Azure expect that
* We may want to bump this limit up * the first query be minimal size (and just used to get the number/size
* of previous versions) so response size must be specified as EXACTLY
* sizeof(struct snapshot_array) which is 16 when rounded up to multiple
* of eight bytes. Currently that is the only case where we set max
* response size smaller.
*/ */
req->MaxOutputResponse = cpu_to_le32(CIFSMaxBufSize); req->MaxOutputResponse = cpu_to_le32(max_response_size);
if (is_fsctl) if (is_fsctl)
req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
...@@ -2550,13 +2565,14 @@ SMB2_ioctl_free(struct smb_rqst *rqst) ...@@ -2550,13 +2565,14 @@ SMB2_ioctl_free(struct smb_rqst *rqst)
cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
} }
/* /*
* SMB2 IOCTL is used for both IOCTLs and FSCTLs * 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(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
u64 volatile_fid, u32 opcode, bool is_fsctl, u64 volatile_fid, u32 opcode, bool is_fsctl,
char *in_data, u32 indatalen, char *in_data, u32 indatalen, u32 max_out_data_len,
char **out_data, u32 *plen /* returned data len */) char **out_data, u32 *plen /* returned data len */)
{ {
struct smb_rqst rqst; struct smb_rqst rqst;
...@@ -2593,8 +2609,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, ...@@ -2593,8 +2609,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
rqst.rq_iov = iov; rqst.rq_iov = iov;
rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE; rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE;
rc = SMB2_ioctl_init(tcon, &rqst, persistent_fid, volatile_fid, rc = SMB2_ioctl_init(tcon, &rqst, persistent_fid, volatile_fid, opcode,
opcode, is_fsctl, in_data, indatalen); is_fsctl, in_data, indatalen, max_out_data_len);
if (rc) if (rc)
goto ioctl_exit; goto ioctl_exit;
...@@ -2672,7 +2688,8 @@ SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2672,7 +2688,8 @@ SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
FSCTL_SET_COMPRESSION, true /* is_fsctl */, FSCTL_SET_COMPRESSION, true /* is_fsctl */,
(char *)&fsctl_input /* data input */, (char *)&fsctl_input /* data input */,
2 /* in data len */, &ret_data /* out data */, NULL); 2 /* in data len */, CIFSMaxBufSize /* max out data */,
&ret_data /* out data */, NULL);
cifs_dbg(FYI, "set compression rc %d\n", rc); cifs_dbg(FYI, "set compression rc %d\n", rc);
......
...@@ -142,11 +142,12 @@ extern int SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, ...@@ -142,11 +142,12 @@ extern int SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
extern void SMB2_open_free(struct smb_rqst *rqst); extern void SMB2_open_free(struct smb_rqst *rqst);
extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, 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, u32 maxoutlen,
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, extern int SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst,
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,
__u32 max_response_size);
extern void SMB2_ioctl_free(struct smb_rqst *rqst); 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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment