Commit 0c14e43a authored by Linus Torvalds's avatar Linus Torvalds

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

Pull cifs fixes from Steve French:

 - one smb3 (ACL related) fix for stable

 - one SMB3 security enhancement (when mounting -t smb3 forbid less
   secure dialects)

 - some RDMA and compounding fixes

* tag '4.18-fixes-smb3' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: fix a buffer leak in smb2_query_symlink
  smb3: do not allow insecure cifs mounts when using smb3
  CIFS: Fix NULL ptr deref
  CIFS: fix encryption in SMB3.1.1
  CIFS: Pass page offset for encrypting
  CIFS: Pass page offset for calculating signature
  CIFS: SMBD: Support page offset in memory registration
  CIFS: SMBD: Support page offset in RDMA recv
  CIFS: SMBD: Support page offset in RDMA send
  CIFS: When sending data on socket, pass the correct page offset
  CIFS: Introduce helper function to get page offset and length in smb_rqst
  CIFS: Calculate the correct request length based on page offset and tail size
  cifs: For SMB2 security informaion query, check for minimum sized security descriptor instead of sizeof FileAllInformation class
  CIFS: Fix signing for SMB2/3
parents bbaa1013 9d874c36
......@@ -98,4 +98,18 @@ struct cifs_ace {
struct cifs_sid sid; /* ie UUID of user or group who gets these perms */
} __attribute__((packed));
/*
* Minimum security identifier can be one for system defined Users
* and Groups such as NULL SID and World or Built-in accounts such
* as Administrator and Guest and consists of
* Revision + Num (Sub)Auths + Authority + Domain (one Subauthority)
*/
#define MIN_SID_LEN (1 + 1 + 6 + 4) /* in bytes */
/*
* Minimum security descriptor can be one without any SACL and DACL and can
* consist of revision, type, and two sids of minimum size for owner and group
*/
#define MIN_SEC_DESC_LEN (sizeof(struct cifs_ntsd) + (2 * MIN_SID_LEN))
#endif /* _CIFSACL_H */
......@@ -37,6 +37,7 @@
#include <crypto/aead.h>
int __cifs_calc_signature(struct smb_rqst *rqst,
int start,
struct TCP_Server_Info *server, char *signature,
struct shash_desc *shash)
{
......@@ -45,10 +46,7 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
struct kvec *iov = rqst->rq_iov;
int n_vec = rqst->rq_nvec;
if (n_vec < 2 || iov[0].iov_len != 4)
return -EIO;
for (i = 1; i < n_vec; i++) {
for (i = start; i < n_vec; i++) {
if (iov[i].iov_len == 0)
continue;
if (iov[i].iov_base == NULL) {
......@@ -68,11 +66,12 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
/* now hash over the rq_pages array */
for (i = 0; i < rqst->rq_npages; i++) {
void *kaddr = kmap(rqst->rq_pages[i]);
size_t len = rqst->rq_pagesz;
void *kaddr;
unsigned int len, offset;
rqst_page_get_length(rqst, i, &len, &offset);
if (i == rqst->rq_npages - 1)
len = rqst->rq_tailsz;
kaddr = (char *) kmap(rqst->rq_pages[i]) + offset;
crypto_shash_update(shash, kaddr, len);
......@@ -119,7 +118,7 @@ static int cifs_calc_signature(struct smb_rqst *rqst,
return rc;
}
return __cifs_calc_signature(rqst, server, signature,
return __cifs_calc_signature(rqst, 1, server, signature,
&server->secmech.sdescmd5->shash);
}
......
......@@ -698,8 +698,8 @@ static int cifs_set_super(struct super_block *sb, void *data)
}
static struct dentry *
cifs_do_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
cifs_smb3_do_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, bool is_smb3)
{
int rc;
struct super_block *sb;
......@@ -710,7 +710,7 @@ cifs_do_mount(struct file_system_type *fs_type,
cifs_dbg(FYI, "Devname: %s flags: %d\n", dev_name, flags);
volume_info = cifs_get_volume_info((char *)data, dev_name);
volume_info = cifs_get_volume_info((char *)data, dev_name, is_smb3);
if (IS_ERR(volume_info))
return ERR_CAST(volume_info);
......@@ -790,6 +790,20 @@ cifs_do_mount(struct file_system_type *fs_type,
goto out;
}
static struct dentry *
smb3_do_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return cifs_smb3_do_mount(fs_type, flags, dev_name, data, true);
}
static struct dentry *
cifs_do_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return cifs_smb3_do_mount(fs_type, flags, dev_name, data, false);
}
static ssize_t
cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
......@@ -925,7 +939,7 @@ MODULE_ALIAS_FS("cifs");
static struct file_system_type smb3_fs_type = {
.owner = THIS_MODULE,
.name = "smb3",
.mount = cifs_do_mount,
.mount = smb3_do_mount,
.kill_sb = cifs_kill_sb,
/* .fs_flags */
};
......
......@@ -1019,6 +1019,12 @@ tlink_tcon(struct tcon_link *tlink)
return tlink->tl_tcon;
}
static inline struct tcon_link *
cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb)
{
return cifs_sb->master_tlink;
}
extern void cifs_put_tlink(struct tcon_link *tlink);
static inline struct tcon_link *
......
......@@ -211,7 +211,7 @@ extern int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
extern int cifs_match_super(struct super_block *, void *);
extern void cifs_cleanup_volume_info(struct smb_vol *pvolume_info);
extern struct smb_vol *cifs_get_volume_info(char *mount_data,
const char *devname);
const char *devname, bool is_smb3);
extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
extern void cifs_umount(struct cifs_sb_info *);
extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
......@@ -544,7 +544,7 @@ int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const unsigned char *path, char *pbuf,
unsigned int *pbytes_written);
int __cifs_calc_signature(struct smb_rqst *rqst,
int __cifs_calc_signature(struct smb_rqst *rqst, int start,
struct TCP_Server_Info *server, char *signature,
struct shash_desc *shash);
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
......@@ -557,4 +557,7 @@ int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
struct sdesc **sdesc);
void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc);
extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
unsigned int *len, unsigned int *offset);
#endif /* _CIFSPROTO_H */
......@@ -320,7 +320,7 @@ static int generic_ip_connect(struct TCP_Server_Info *server);
static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
static void cifs_prune_tlinks(struct work_struct *work);
static int cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
const char *devname);
const char *devname, bool is_smb3);
/*
* cifs tcp session reconnection
......@@ -1166,7 +1166,7 @@ cifs_parse_cache_flavor(char *value, struct smb_vol *vol)
}
static int
cifs_parse_smb_version(char *value, struct smb_vol *vol)
cifs_parse_smb_version(char *value, struct smb_vol *vol, bool is_smb3)
{
substring_t args[MAX_OPT_ARGS];
......@@ -1176,6 +1176,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
cifs_dbg(VFS, "mount with legacy dialect disabled\n");
return 1;
}
if (is_smb3) {
cifs_dbg(VFS, "vers=1.0 (cifs) not permitted when mounting with smb3\n");
return 1;
}
vol->ops = &smb1_operations;
vol->vals = &smb1_values;
break;
......@@ -1184,6 +1188,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
cifs_dbg(VFS, "mount with legacy dialect disabled\n");
return 1;
}
if (is_smb3) {
cifs_dbg(VFS, "vers=2.0 not permitted when mounting with smb3\n");
return 1;
}
vol->ops = &smb20_operations;
vol->vals = &smb20_values;
break;
......@@ -1272,7 +1280,7 @@ cifs_parse_devname(const char *devname, struct smb_vol *vol)
static int
cifs_parse_mount_options(const char *mountdata, const char *devname,
struct smb_vol *vol)
struct smb_vol *vol, bool is_smb3)
{
char *data, *end;
char *mountdata_copy = NULL, *options;
......@@ -1985,7 +1993,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
if (string == NULL)
goto out_nomem;
if (cifs_parse_smb_version(string, vol) != 0)
if (cifs_parse_smb_version(string, vol, is_smb3) != 0)
goto cifs_parse_mount_err;
got_version = true;
break;
......@@ -3116,12 +3124,6 @@ cifs_put_tlink(struct tcon_link *tlink)
return;
}
static inline struct tcon_link *
cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb)
{
return cifs_sb->master_tlink;
}
static int
compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
{
......@@ -3803,7 +3805,7 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
} else {
cleanup_volume_info_contents(volume_info);
rc = cifs_setup_volume_info(volume_info, mdata,
fake_devname);
fake_devname, false);
}
kfree(fake_devname);
kfree(cifs_sb->mountdata);
......@@ -3816,11 +3818,11 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
static int
cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
const char *devname)
const char *devname, bool is_smb3)
{
int rc = 0;
if (cifs_parse_mount_options(mount_data, devname, volume_info))
if (cifs_parse_mount_options(mount_data, devname, volume_info, is_smb3))
return -EINVAL;
if (volume_info->nullauth) {
......@@ -3854,7 +3856,7 @@ cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
}
struct smb_vol *
cifs_get_volume_info(char *mount_data, const char *devname)
cifs_get_volume_info(char *mount_data, const char *devname, bool is_smb3)
{
int rc;
struct smb_vol *volume_info;
......@@ -3863,7 +3865,7 @@ cifs_get_volume_info(char *mount_data, const char *devname)
if (!volume_info)
return ERR_PTR(-ENOMEM);
rc = cifs_setup_volume_info(volume_info, mount_data, devname);
rc = cifs_setup_volume_info(volume_info, mount_data, devname, is_smb3);
if (rc) {
cifs_cleanup_volume_info(volume_info);
volume_info = ERR_PTR(rc);
......
......@@ -421,7 +421,8 @@ smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
return -ENOMEM;
}
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL);
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL,
NULL);
if (rc)
goto qmf_out_open_fail;
......@@ -478,7 +479,8 @@ smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,
NULL);
if (rc) {
kfree(utf16_path);
return rc;
......
......@@ -905,3 +905,20 @@ cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc)
crypto_free_shash(*shash);
*shash = NULL;
}
/**
* rqst_page_get_length - obtain the length and offset for a page in smb_rqst
* Input: rqst - a smb_rqst, page - a page index for rqst
* Output: *len - the length for this page, *offset - the offset for this page
*/
void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
unsigned int *len, unsigned int *offset)
{
*len = rqst->rq_pagesz;
*offset = (page == 0) ? rqst->rq_offset : 0;
if (rqst->rq_npages == 1 || page == rqst->rq_npages-1)
*len = rqst->rq_tailsz;
else if (page == 0)
*len = rqst->rq_pagesz - rqst->rq_offset;
}
......@@ -64,7 +64,8 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE);
rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data, NULL);
rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data, NULL,
NULL);
if (rc)
goto out;
......
......@@ -71,7 +71,8 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,
NULL);
if (rc) {
kfree(utf16_path);
return rc;
......
......@@ -453,8 +453,10 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
start_of_path = from + 1;
#ifdef CONFIG_CIFS_SMB311
/* SMB311 POSIX extensions paths do not include leading slash */
else if (cifs_sb_master_tcon(cifs_sb)->posix_extensions)
else if (cifs_sb_master_tlink(cifs_sb) &&
cifs_sb_master_tcon(cifs_sb)->posix_extensions) {
start_of_path = from + 1;
}
#endif /* 311 */
else
start_of_path = from;
......
......@@ -348,7 +348,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
oparams.fid = pfid;
oparams.reconnect = false;
rc = SMB2_open(xid, &oparams, &srch_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparams, &srch_path, &oplock, NULL, NULL, NULL);
if (rc == 0) {
memcpy(tcon->prfid, pfid, sizeof(struct cifs_fid));
tcon->valid_root_fid = true;
......@@ -375,7 +375,8 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
oparms.reconnect = false;
if (no_cached_open)
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL,
NULL);
else
rc = open_shroot(xid, tcon, &fid);
......@@ -413,7 +414,7 @@ smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, NULL);
if (rc)
return;
......@@ -449,7 +450,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
if (rc) {
kfree(utf16_path);
return rc;
......@@ -598,7 +599,7 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
kfree(utf16_path);
if (rc) {
cifs_dbg(FYI, "open failed rc=%d\n", rc);
......@@ -677,7 +678,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
kfree(utf16_path);
if (rc) {
cifs_dbg(FYI, "open failed rc=%d\n", rc);
......@@ -1261,7 +1262,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
kfree(utf16_path);
if (rc) {
cifs_dbg(FYI, "open dir failed rc=%d\n", rc);
......@@ -1361,7 +1362,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, NULL);
if (rc)
return rc;
buf->f_type = SMB2_MAGIC_NUMBER;
......@@ -1515,7 +1516,8 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_open_parms oparms;
struct cifs_fid fid;
struct kvec err_iov = {NULL, 0};
struct smb2_err_rsp *err_buf;
struct smb2_err_rsp *err_buf = NULL;
int resp_buftype;
struct smb2_symlink_err_rsp *symlink;
unsigned int sub_len;
unsigned int sub_offset;
......@@ -1535,18 +1537,18 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_iov);
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_iov,
&resp_buftype);
if (!rc || !err_iov.iov_base) {
kfree(utf16_path);
return -ENOENT;
rc = -ENOENT;
goto querty_exit;
}
err_buf = err_iov.iov_base;
if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) ||
err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE) {
kfree(utf16_path);
return -ENOENT;
rc = -ENOENT;
goto querty_exit;
}
/* open must fail on symlink - reset rc */
......@@ -1558,25 +1560,28 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
print_offset = le16_to_cpu(symlink->PrintNameOffset);
if (err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) {
kfree(utf16_path);
return -ENOENT;
rc = -ENOENT;
goto querty_exit;
}
if (err_iov.iov_len <
SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) {
kfree(utf16_path);
return -ENOENT;
rc = -ENOENT;
goto querty_exit;
}
*target_path = cifs_strndup_from_utf16(
(char *)symlink->PathBuffer + sub_offset,
sub_len, true, cifs_sb->local_nls);
if (!(*target_path)) {
kfree(utf16_path);
return -ENOMEM;
rc = -ENOMEM;
goto querty_exit;
}
convert_delimiter(*target_path, '/');
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
querty_exit:
free_rsp_buf(resp_buftype, err_buf);
kfree(utf16_path);
return rc;
}
......@@ -1649,7 +1654,7 @@ get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
kfree(utf16_path);
if (!rc) {
rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid,
......@@ -1712,7 +1717,7 @@ set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
kfree(utf16_path);
if (!rc) {
rc = SMB2_set_acl(xid, tlink_tcon(tlink), fid.persistent_fid,
......@@ -2189,9 +2194,10 @@ init_sg(struct smb_rqst *rqst, u8 *sign)
smb2_sg_set_buf(&sg[i], rqst->rq_iov[i+1].iov_base,
rqst->rq_iov[i+1].iov_len);
for (j = 0; i < sg_len - 1; i++, j++) {
unsigned int len = (j < rqst->rq_npages - 1) ? rqst->rq_pagesz
: rqst->rq_tailsz;
sg_set_page(&sg[i], rqst->rq_pages[j], len, 0);
unsigned int len, offset;
rqst_page_get_length(rqst, j, &len, &offset);
sg_set_page(&sg[i], rqst->rq_pages[j], len, offset);
}
smb2_sg_set_buf(&sg[sg_len - 1], sign, SMB2_SIGNATURE_SIZE);
return sg;
......@@ -2229,7 +2235,7 @@ static int
crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
{
struct smb2_transform_hdr *tr_hdr =
(struct smb2_transform_hdr *)rqst->rq_iov[0].iov_base;
(struct smb2_transform_hdr *)rqst->rq_iov[1].iov_base;
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
int rc = 0;
struct scatterlist *sg;
......@@ -2338,6 +2344,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,
return rc;
new_rq->rq_pages = pages;
new_rq->rq_offset = old_rq->rq_offset;
new_rq->rq_npages = old_rq->rq_npages;
new_rq->rq_pagesz = old_rq->rq_pagesz;
new_rq->rq_tailsz = old_rq->rq_tailsz;
......@@ -2379,10 +2386,14 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq,
/* copy pages form the old */
for (i = 0; i < npages; i++) {
char *dst = kmap(new_rq->rq_pages[i]);
char *src = kmap(old_rq->rq_pages[i]);
unsigned int len = (i < npages - 1) ? new_rq->rq_pagesz :
new_rq->rq_tailsz;
char *dst, *src;
unsigned int offset, len;
rqst_page_get_length(new_rq, i, &len, &offset);
dst = (char *) kmap(new_rq->rq_pages[i]) + offset;
src = (char *) kmap(old_rq->rq_pages[i]) + offset;
memcpy(dst, src, len);
kunmap(new_rq->rq_pages[i]);
kunmap(old_rq->rq_pages[i]);
......
......@@ -1889,7 +1889,7 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
int
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
__u8 *oplock, struct smb2_file_all_info *buf,
struct kvec *err_iov)
struct kvec *err_iov, int *buftype)
{
struct smb2_create_req *req;
struct smb2_create_rsp *rsp;
......@@ -2052,6 +2052,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
if (err_iov && rsp) {
*err_iov = rsp_iov;
*buftype = resp_buftype;
resp_buftype = CIFS_NO_BUFFER;
rsp = NULL;
}
......@@ -2492,8 +2493,7 @@ SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
return query_info(xid, tcon, persistent_fid, volatile_fid,
0, SMB2_O_INFO_SECURITY, additional_info,
SMB2_MAX_BUFFER_SIZE,
sizeof(struct smb2_file_all_info), data, plen);
SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen);
}
int
......@@ -2721,8 +2721,8 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
rdata->mr = smbd_register_mr(
server->smbd_conn, rdata->pages,
rdata->nr_pages, rdata->tailsz,
true, need_invalidate);
rdata->nr_pages, rdata->page_offset,
rdata->tailsz, true, need_invalidate);
if (!rdata->mr)
return -ENOBUFS;
......@@ -3108,16 +3108,22 @@ smb2_async_writev(struct cifs_writedata *wdata,
wdata->mr = smbd_register_mr(
server->smbd_conn, wdata->pages,
wdata->nr_pages, wdata->tailsz,
false, need_invalidate);
wdata->nr_pages, wdata->page_offset,
wdata->tailsz, false, need_invalidate);
if (!wdata->mr) {
rc = -ENOBUFS;
goto async_writev_out;
}
req->Length = 0;
req->DataOffset = 0;
req->RemainingBytes =
cpu_to_le32((wdata->nr_pages-1)*PAGE_SIZE + wdata->tailsz);
if (wdata->nr_pages > 1)
req->RemainingBytes =
cpu_to_le32(
(wdata->nr_pages - 1) * wdata->pagesz -
wdata->page_offset + wdata->tailsz
);
else
req->RemainingBytes = cpu_to_le32(wdata->tailsz);
req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
if (need_invalidate)
req->Channel = SMB2_CHANNEL_RDMA_V1;
......
......@@ -125,7 +125,7 @@ extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
__le16 *path, __u8 *oplock,
struct smb2_file_all_info *buf,
struct kvec *err_iov);
struct kvec *err_iov, int *resp_buftype);
extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 opcode,
bool is_fsctl, char *in_data, u32 indatalen,
......
......@@ -171,7 +171,9 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
unsigned char *sigptr = smb2_signature;
struct kvec *iov = rqst->rq_iov;
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base;
int iov_hdr_index = rqst->rq_nvec > 1 ? 1 : 0;
struct smb2_sync_hdr *shdr =
(struct smb2_sync_hdr *)iov[iov_hdr_index].iov_base;
struct cifs_ses *ses;
ses = smb2_find_smb_ses(server, shdr->SessionId);
......@@ -202,7 +204,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return rc;
}
rc = __cifs_calc_signature(rqst, server, sigptr,
rc = __cifs_calc_signature(rqst, iov_hdr_index, server, sigptr,
&server->secmech.sdeschmacsha256->shash);
if (!rc)
......@@ -412,7 +414,9 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
unsigned char smb3_signature[SMB2_CMACAES_SIZE];
unsigned char *sigptr = smb3_signature;
struct kvec *iov = rqst->rq_iov;
struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[1].iov_base;
int iov_hdr_index = rqst->rq_nvec > 1 ? 1 : 0;
struct smb2_sync_hdr *shdr =
(struct smb2_sync_hdr *)iov[iov_hdr_index].iov_base;
struct cifs_ses *ses;
ses = smb2_find_smb_ses(server, shdr->SessionId);
......@@ -443,7 +447,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return rc;
}
rc = __cifs_calc_signature(rqst, server, sigptr,
rc = __cifs_calc_signature(rqst, iov_hdr_index, server, sigptr,
&server->secmech.sdesccmacaes->shash);
if (!rc)
......
......@@ -17,6 +17,7 @@
#include <linux/highmem.h>
#include "smbdirect.h"
#include "cifs_debug.h"
#include "cifsproto.h"
static struct smbd_response *get_empty_queue_buffer(
struct smbd_connection *info);
......@@ -2003,10 +2004,12 @@ static int smbd_recv_buf(struct smbd_connection *info, char *buf,
* return value: actual data read
*/
static int smbd_recv_page(struct smbd_connection *info,
struct page *page, unsigned int to_read)
struct page *page, unsigned int page_offset,
unsigned int to_read)
{
int ret;
char *to_address;
void *page_address;
/* make sure we have the page ready for read */
ret = wait_event_interruptible(
......@@ -2014,16 +2017,17 @@ static int smbd_recv_page(struct smbd_connection *info,
info->reassembly_data_length >= to_read ||
info->transport_status != SMBD_CONNECTED);
if (ret)
return 0;
return ret;
/* now we can read from reassembly queue and not sleep */
to_address = kmap_atomic(page);
page_address = kmap_atomic(page);
to_address = (char *) page_address + page_offset;
log_read(INFO, "reading from page=%p address=%p to_read=%d\n",
page, to_address, to_read);
ret = smbd_recv_buf(info, to_address, to_read);
kunmap_atomic(to_address);
kunmap_atomic(page_address);
return ret;
}
......@@ -2037,7 +2041,7 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
{
char *buf;
struct page *page;
unsigned int to_read;
unsigned int to_read, page_offset;
int rc;
info->smbd_recv_pending++;
......@@ -2051,15 +2055,16 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
case READ | ITER_BVEC:
page = msg->msg_iter.bvec->bv_page;
page_offset = msg->msg_iter.bvec->bv_offset;
to_read = msg->msg_iter.bvec->bv_len;
rc = smbd_recv_page(info, page, to_read);
rc = smbd_recv_page(info, page, page_offset, to_read);
break;
default:
/* It's a bug in upper layer to get there */
cifs_dbg(VFS, "CIFS: invalid msg type %d\n",
msg->msg_iter.type);
rc = -EIO;
rc = -EINVAL;
}
info->smbd_recv_pending--;
......@@ -2082,7 +2087,7 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
struct kvec vec;
int nvecs;
int size;
int buflen = 0, remaining_data_length;
unsigned int buflen = 0, remaining_data_length;
int start, i, j;
int max_iov_size =
info->max_send_size - sizeof(struct smbd_data_transfer);
......@@ -2113,10 +2118,17 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
buflen += iov[i].iov_len;
}
/* add in the page array if there is one */
/*
* Add in the page array if there is one. The caller needs to set
* rq_tailsz to PAGE_SIZE when the buffer has multiple pages and
* ends at page boundary
*/
if (rqst->rq_npages) {
buflen += rqst->rq_pagesz * (rqst->rq_npages - 1);
buflen += rqst->rq_tailsz;
if (rqst->rq_npages == 1)
buflen += rqst->rq_tailsz;
else
buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) -
rqst->rq_offset + rqst->rq_tailsz;
}
if (buflen + sizeof(struct smbd_data_transfer) >
......@@ -2213,8 +2225,9 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
/* now sending pages if there are any */
for (i = 0; i < rqst->rq_npages; i++) {
buflen = (i == rqst->rq_npages-1) ?
rqst->rq_tailsz : rqst->rq_pagesz;
unsigned int offset;
rqst_page_get_length(rqst, i, &buflen, &offset);
nvecs = (buflen + max_iov_size - 1) / max_iov_size;
log_write(INFO, "sending pages buflen=%d nvecs=%d\n",
buflen, nvecs);
......@@ -2225,9 +2238,11 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
remaining_data_length -= size;
log_write(INFO, "sending pages i=%d offset=%d size=%d"
" remaining_data_length=%d\n",
i, j*max_iov_size, size, remaining_data_length);
i, j*max_iov_size+offset, size,
remaining_data_length);
rc = smbd_post_send_page(
info, rqst->rq_pages[i], j*max_iov_size,
info, rqst->rq_pages[i],
j*max_iov_size + offset,
size, remaining_data_length);
if (rc)
goto done;
......@@ -2284,37 +2299,37 @@ static void smbd_mr_recovery_work(struct work_struct *work)
if (smbdirect_mr->state == MR_INVALIDATED ||
smbdirect_mr->state == MR_ERROR) {
if (smbdirect_mr->state == MR_INVALIDATED) {
/* recover this MR entry */
rc = ib_dereg_mr(smbdirect_mr->mr);
if (rc) {
log_rdma_mr(ERR,
"ib_dereg_mr failed rc=%x\n",
rc);
smbd_disconnect_rdma_connection(info);
continue;
}
smbdirect_mr->mr = ib_alloc_mr(
info->pd, info->mr_type,
info->max_frmr_depth);
if (IS_ERR(smbdirect_mr->mr)) {
log_rdma_mr(ERR,
"ib_alloc_mr failed mr_type=%x "
"max_frmr_depth=%x\n",
info->mr_type,
info->max_frmr_depth);
smbd_disconnect_rdma_connection(info);
continue;
}
if (smbdirect_mr->state == MR_INVALIDATED)
ib_dma_unmap_sg(
info->id->device, smbdirect_mr->sgl,
smbdirect_mr->sgl_count,
smbdirect_mr->dir);
smbdirect_mr->state = MR_READY;
} else if (smbdirect_mr->state == MR_ERROR) {
/* recover this MR entry */
rc = ib_dereg_mr(smbdirect_mr->mr);
if (rc) {
log_rdma_mr(ERR,
"ib_dereg_mr failed rc=%x\n",
rc);
smbd_disconnect_rdma_connection(info);
}
smbdirect_mr->mr = ib_alloc_mr(
info->pd, info->mr_type,
info->max_frmr_depth);
if (IS_ERR(smbdirect_mr->mr)) {
log_rdma_mr(ERR,
"ib_alloc_mr failed mr_type=%x "
"max_frmr_depth=%x\n",
info->mr_type,
info->max_frmr_depth);
smbd_disconnect_rdma_connection(info);
}
smbdirect_mr->state = MR_READY;
smbdirect_mr->state = MR_READY;
}
/* smbdirect_mr->state is updated by this function
* and is read and updated by I/O issuing CPUs trying
* to get a MR, the call to atomic_inc_return
......@@ -2460,7 +2475,7 @@ static struct smbd_mr *get_mr(struct smbd_connection *info)
*/
struct smbd_mr *smbd_register_mr(
struct smbd_connection *info, struct page *pages[], int num_pages,
int tailsz, bool writing, bool need_invalidate)
int offset, int tailsz, bool writing, bool need_invalidate)
{
struct smbd_mr *smbdirect_mr;
int rc, i;
......@@ -2483,17 +2498,31 @@ struct smbd_mr *smbd_register_mr(
smbdirect_mr->sgl_count = num_pages;
sg_init_table(smbdirect_mr->sgl, num_pages);
for (i = 0; i < num_pages - 1; i++)
sg_set_page(&smbdirect_mr->sgl[i], pages[i], PAGE_SIZE, 0);
log_rdma_mr(INFO, "num_pages=0x%x offset=0x%x tailsz=0x%x\n",
num_pages, offset, tailsz);
if (num_pages == 1) {
sg_set_page(&smbdirect_mr->sgl[0], pages[0], tailsz, offset);
goto skip_multiple_pages;
}
/* We have at least two pages to register */
sg_set_page(
&smbdirect_mr->sgl[0], pages[0], PAGE_SIZE - offset, offset);
i = 1;
while (i < num_pages - 1) {
sg_set_page(&smbdirect_mr->sgl[i], pages[i], PAGE_SIZE, 0);
i++;
}
sg_set_page(&smbdirect_mr->sgl[i], pages[i],
tailsz ? tailsz : PAGE_SIZE, 0);
skip_multiple_pages:
dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
smbdirect_mr->dir = dir;
rc = ib_dma_map_sg(info->id->device, smbdirect_mr->sgl, num_pages, dir);
if (!rc) {
log_rdma_mr(INFO, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n",
log_rdma_mr(ERR, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n",
num_pages, dir, rc);
goto dma_map_error;
}
......@@ -2501,8 +2530,8 @@ struct smbd_mr *smbd_register_mr(
rc = ib_map_mr_sg(smbdirect_mr->mr, smbdirect_mr->sgl, num_pages,
NULL, PAGE_SIZE);
if (rc != num_pages) {
log_rdma_mr(INFO,
"ib_map_mr_sg failed rc = %x num_pages = %x\n",
log_rdma_mr(ERR,
"ib_map_mr_sg failed rc = %d num_pages = %x\n",
rc, num_pages);
goto map_mr_error;
}
......
......@@ -321,7 +321,7 @@ struct smbd_mr {
/* Interfaces to register and deregister MR for RDMA read/write */
struct smbd_mr *smbd_register_mr(
struct smbd_connection *info, struct page *pages[], int num_pages,
int tailsz, bool writing, bool need_invalidate);
int offset, int tailsz, bool writing, bool need_invalidate);
int smbd_deregister_mr(struct smbd_mr *mr);
#else
......
......@@ -212,10 +212,24 @@ rqst_len(struct smb_rqst *rqst)
for (i = 0; i < rqst->rq_nvec; i++)
buflen += iov[i].iov_len;
/* add in the page array if there is one */
/*
* Add in the page array if there is one. The caller needs to make
* sure rq_offset and rq_tailsz are set correctly. If a buffer of
* multiple pages ends at page boundary, rq_tailsz needs to be set to
* PAGE_SIZE.
*/
if (rqst->rq_npages) {
buflen += rqst->rq_pagesz * (rqst->rq_npages - 1);
buflen += rqst->rq_tailsz;
if (rqst->rq_npages == 1)
buflen += rqst->rq_tailsz;
else {
/*
* If there is more than one page, calculate the
* buffer length based on rq_offset and rq_tailsz
*/
buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) -
rqst->rq_offset;
buflen += rqst->rq_tailsz;
}
}
return buflen;
......@@ -274,15 +288,13 @@ __smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
/* now walk the page array and send each page in it */
for (i = 0; i < rqst->rq_npages; i++) {
size_t len = i == rqst->rq_npages - 1
? rqst->rq_tailsz
: rqst->rq_pagesz;
struct bio_vec bvec = {
.bv_page = rqst->rq_pages[i],
.bv_len = len
};
struct bio_vec bvec;
bvec.bv_page = rqst->rq_pages[i];
rqst_page_get_length(rqst, i, &bvec.bv_len, &bvec.bv_offset);
iov_iter_bvec(&smb_msg.msg_iter, WRITE | ITER_BVEC,
&bvec, 1, len);
&bvec, 1, bvec.bv_len);
rc = smb_send_kvec(server, &smb_msg, &sent);
if (rc < 0)
break;
......
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