Commit 4652b8e4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '6.7-rc-ksmbd-server-fixes' of git://git.samba.org/ksmbd

Pull smb server updates from Steve French:
 "Seven ksmbd server fixes:

   - logoff improvement for multichannel bound connections

   - unicode fix for surrogate pairs

   - RDMA (smbdirect) fix for IB devices

   - fix locking deadlock in kern_path_create during rename

   - iov memory allocation fix

   - two minor cleanup patches (doc cleanup, and unused variable)"

* tag '6.7-rc-ksmbd-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: no need to wait for binded connection termination at logoff
  ksmbd: add support for surrogate pair conversion
  ksmbd: fix missing RDMA-capable flag for IPoIB device in ksmbd_rdma_capable_netdev()
  ksmbd: fix recursive locking in vfs helpers
  ksmbd: fix kernel-doc comment of ksmbd_vfs_setxattr()
  ksmbd: reorganize ksmbd_iov_pin_rsp()
  ksmbd: Remove unused field in ksmbd_user struct
parents 71fb7b32 67797da8
...@@ -167,23 +167,7 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status) ...@@ -167,23 +167,7 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id) void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id)
{ {
struct ksmbd_conn *bind_conn;
wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);
down_read(&conn_list_lock);
list_for_each_entry(bind_conn, &conn_list, conns_list) {
if (bind_conn == conn)
continue;
if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) &&
!ksmbd_conn_releasing(bind_conn) &&
atomic_read(&bind_conn->req_running)) {
wait_event(bind_conn->req_running_q,
atomic_read(&bind_conn->req_running) == 0);
}
}
up_read(&conn_list_lock);
} }
int ksmbd_conn_write(struct ksmbd_work *work) int ksmbd_conn_write(struct ksmbd_work *work)
......
...@@ -95,11 +95,28 @@ bool ksmbd_queue_work(struct ksmbd_work *work) ...@@ -95,11 +95,28 @@ bool ksmbd_queue_work(struct ksmbd_work *work)
return queue_work(ksmbd_wq, &work->work); return queue_work(ksmbd_wq, &work->work);
} }
static int ksmbd_realloc_iov_pin(struct ksmbd_work *work, void *ib, static inline void __ksmbd_iov_pin(struct ksmbd_work *work, void *ib,
unsigned int ib_len) unsigned int ib_len)
{ {
work->iov[++work->iov_idx].iov_base = ib;
work->iov[work->iov_idx].iov_len = ib_len;
work->iov_cnt++;
}
static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
void *aux_buf, unsigned int aux_size)
{
struct aux_read *ar;
int need_iov_cnt = 1;
if (aux_size) {
need_iov_cnt++;
ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL);
if (!ar)
return -ENOMEM;
}
if (work->iov_alloc_cnt <= work->iov_cnt) { if (work->iov_alloc_cnt < work->iov_cnt + need_iov_cnt) {
struct kvec *new; struct kvec *new;
work->iov_alloc_cnt += 4; work->iov_alloc_cnt += 4;
...@@ -111,16 +128,6 @@ static int ksmbd_realloc_iov_pin(struct ksmbd_work *work, void *ib, ...@@ -111,16 +128,6 @@ static int ksmbd_realloc_iov_pin(struct ksmbd_work *work, void *ib,
work->iov = new; work->iov = new;
} }
work->iov[++work->iov_idx].iov_base = ib;
work->iov[work->iov_idx].iov_len = ib_len;
work->iov_cnt++;
return 0;
}
static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
void *aux_buf, unsigned int aux_size)
{
/* Plus rfc_length size on first iov */ /* Plus rfc_length size on first iov */
if (!work->iov_idx) { if (!work->iov_idx) {
work->iov[work->iov_idx].iov_base = work->response_buf; work->iov[work->iov_idx].iov_base = work->response_buf;
...@@ -129,19 +136,13 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, ...@@ -129,19 +136,13 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
work->iov_cnt++; work->iov_cnt++;
} }
ksmbd_realloc_iov_pin(work, ib, len); __ksmbd_iov_pin(work, ib, len);
inc_rfc1001_len(work->iov[0].iov_base, len); inc_rfc1001_len(work->iov[0].iov_base, len);
if (aux_size) { if (aux_size) {
struct aux_read *ar; __ksmbd_iov_pin(work, aux_buf, aux_size);
ksmbd_realloc_iov_pin(work, aux_buf, aux_size);
inc_rfc1001_len(work->iov[0].iov_base, aux_size); inc_rfc1001_len(work->iov[0].iov_base, aux_size);
ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL);
if (!ar)
return -ENOMEM;
ar->buf = aux_buf; ar->buf = aux_buf;
list_add(&ar->entry, &work->aux_read_list); list_add(&ar->entry, &work->aux_read_list);
} }
......
...@@ -18,7 +18,6 @@ struct ksmbd_user { ...@@ -18,7 +18,6 @@ struct ksmbd_user {
size_t passkey_sz; size_t passkey_sz;
char *passkey; char *passkey;
unsigned int failed_login_count;
}; };
static inline bool user_guest(struct ksmbd_user *user) static inline bool user_guest(struct ksmbd_user *user)
......
...@@ -2140,8 +2140,7 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev) ...@@ -2140,8 +2140,7 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev)
if (ib_dev->node_type != RDMA_NODE_IB_CA) if (ib_dev->node_type != RDMA_NODE_IB_CA)
smb_direct_port = SMB_DIRECT_PORT_IWARP; smb_direct_port = SMB_DIRECT_PORT_IWARP;
if (!ib_dev->ops.get_netdev || if (!rdma_frwr_is_supported(&ib_dev->attrs))
!rdma_frwr_is_supported(&ib_dev->attrs))
return 0; return 0;
smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL); smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL);
...@@ -2241,8 +2240,9 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev) ...@@ -2241,8 +2240,9 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev)
for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) { for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) {
struct net_device *ndev; struct net_device *ndev;
ndev = smb_dev->ib_dev->ops.get_netdev(smb_dev->ib_dev, if (smb_dev->ib_dev->ops.get_netdev) {
i + 1); ndev = smb_dev->ib_dev->ops.get_netdev(
smb_dev->ib_dev, i + 1);
if (!ndev) if (!ndev)
continue; continue;
...@@ -2252,6 +2252,26 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev) ...@@ -2252,6 +2252,26 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev)
goto out; goto out;
} }
dev_put(ndev); dev_put(ndev);
/* if ib_dev does not implement ops.get_netdev
* check for matching infiniband GUID in hw_addr
*/
} else if (netdev->type == ARPHRD_INFINIBAND) {
struct netdev_hw_addr *ha;
union ib_gid gid;
u32 port_num;
int ret;
netdev_hw_addr_list_for_each(
ha, &netdev->dev_addrs) {
memcpy(&gid, ha->addr + 4, sizeof(gid));
ret = ib_find_gid(smb_dev->ib_dev, &gid,
&port_num, NULL);
if (!ret) {
rdma_capable = true;
goto out;
}
}
}
} }
} }
out: out:
......
...@@ -13,46 +13,10 @@ ...@@ -13,46 +13,10 @@
#include "unicode.h" #include "unicode.h"
#include "smb_common.h" #include "smb_common.h"
/*
* smb_utf16_bytes() - how long will a string be after conversion?
* @from: pointer to input string
* @maxbytes: don't go past this many bytes of input string
* @codepage: destination codepage
*
* Walk a utf16le string and return the number of bytes that the string will
* be after being converted to the given charset, not including any null
* termination required. Don't walk past maxbytes in the source buffer.
*
* Return: string length after conversion
*/
static int smb_utf16_bytes(const __le16 *from, int maxbytes,
const struct nls_table *codepage)
{
int i;
int charlen, outlen = 0;
int maxwords = maxbytes / 2;
char tmp[NLS_MAX_CHARSET_SIZE];
__u16 ftmp;
for (i = 0; i < maxwords; i++) {
ftmp = get_unaligned_le16(&from[i]);
if (ftmp == 0)
break;
charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE);
if (charlen > 0)
outlen += charlen;
else
outlen++;
}
return outlen;
}
/* /*
* cifs_mapchar() - convert a host-endian char to proper char in codepage * cifs_mapchar() - convert a host-endian char to proper char in codepage
* @target: where converted character should be copied * @target: where converted character should be copied
* @src_char: 2 byte host-endian source character * @from: host-endian source string
* @cp: codepage to which character should be converted * @cp: codepage to which character should be converted
* @mapchar: should character be mapped according to mapchars mount option? * @mapchar: should character be mapped according to mapchars mount option?
* *
...@@ -63,10 +27,13 @@ static int smb_utf16_bytes(const __le16 *from, int maxbytes, ...@@ -63,10 +27,13 @@ static int smb_utf16_bytes(const __le16 *from, int maxbytes,
* Return: string length after conversion * Return: string length after conversion
*/ */
static int static int
cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp,
bool mapchar) bool mapchar)
{ {
int len = 1; int len = 1;
__u16 src_char;
src_char = *from;
if (!mapchar) if (!mapchar)
goto cp_convert; goto cp_convert;
...@@ -104,12 +71,66 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, ...@@ -104,12 +71,66 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
cp_convert: cp_convert:
len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
if (len <= 0) { if (len <= 0)
goto surrogate_pair;
goto out;
surrogate_pair:
/* convert SURROGATE_PAIR and IVS */
if (strcmp(cp->charset, "utf8"))
goto unknown;
len = utf16s_to_utf8s(from, 3, UTF16_LITTLE_ENDIAN, target, 6);
if (len <= 0)
goto unknown;
return len;
unknown:
*target = '?'; *target = '?';
len = 1; len = 1;
goto out;
}
/*
* smb_utf16_bytes() - compute converted string length
* @from: pointer to input string
* @maxbytes: input string length
* @codepage: destination codepage
*
* Walk a utf16le string and return the number of bytes that the string will
* be after being converted to the given charset, not including any null
* termination required. Don't walk past maxbytes in the source buffer.
*
* Return: string length after conversion
*/
static int smb_utf16_bytes(const __le16 *from, int maxbytes,
const struct nls_table *codepage)
{
int i, j;
int charlen, outlen = 0;
int maxwords = maxbytes / 2;
char tmp[NLS_MAX_CHARSET_SIZE];
__u16 ftmp[3];
for (i = 0; i < maxwords; i++) {
ftmp[0] = get_unaligned_le16(&from[i]);
if (ftmp[0] == 0)
break;
for (j = 1; j <= 2; j++) {
if (i + j < maxwords)
ftmp[j] = get_unaligned_le16(&from[i + j]);
else
ftmp[j] = 0;
} }
goto out; charlen = cifs_mapchar(tmp, ftmp, codepage, 0);
if (charlen > 0)
outlen += charlen;
else
outlen++;
}
return outlen;
} }
/* /*
...@@ -139,12 +160,12 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, ...@@ -139,12 +160,12 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
const struct nls_table *codepage, bool mapchar) const struct nls_table *codepage, bool mapchar)
{ {
int i, charlen, safelen; int i, j, charlen, safelen;
int outlen = 0; int outlen = 0;
int nullsize = nls_nullsize(codepage); int nullsize = nls_nullsize(codepage);
int fromwords = fromlen / 2; int fromwords = fromlen / 2;
char tmp[NLS_MAX_CHARSET_SIZE]; char tmp[NLS_MAX_CHARSET_SIZE];
__u16 ftmp; __u16 ftmp[3]; /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */
/* /*
* because the chars can be of varying widths, we need to take care * because the chars can be of varying widths, we need to take care
...@@ -155,9 +176,15 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, ...@@ -155,9 +176,15 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize);
for (i = 0; i < fromwords; i++) { for (i = 0; i < fromwords; i++) {
ftmp = get_unaligned_le16(&from[i]); ftmp[0] = get_unaligned_le16(&from[i]);
if (ftmp == 0) if (ftmp[0] == 0)
break; break;
for (j = 1; j <= 2; j++) {
if (i + j < fromwords)
ftmp[j] = get_unaligned_le16(&from[i + j]);
else
ftmp[j] = 0;
}
/* /*
* check to see if converting this character might make the * check to see if converting this character might make the
...@@ -172,6 +199,19 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, ...@@ -172,6 +199,19 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
/* put converted char into 'to' buffer */ /* put converted char into 'to' buffer */
charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar);
outlen += charlen; outlen += charlen;
/*
* charlen (=bytes of UTF-8 for 1 character)
* 4bytes UTF-8(surrogate pair) is charlen=4
* (4bytes UTF-16 code)
* 7-8bytes UTF-8(IVS) is charlen=3+4 or 4+4
* (2 UTF-8 pairs divided to 2 UTF-16 pairs)
*/
if (charlen == 4)
i++;
else if (charlen >= 5)
/* 5-6bytes UTF-8 */
i += 2;
} }
/* properly null-terminate string */ /* properly null-terminate string */
...@@ -306,6 +346,9 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen, ...@@ -306,6 +346,9 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
char src_char; char src_char;
__le16 dst_char; __le16 dst_char;
wchar_t tmp; wchar_t tmp;
wchar_t wchar_to[6]; /* UTF-16 */
int ret;
unicode_t u;
if (!mapchars) if (!mapchars)
return smb_strtoUTF16(target, source, srclen, cp); return smb_strtoUTF16(target, source, srclen, cp);
...@@ -348,11 +391,57 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen, ...@@ -348,11 +391,57 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
* if no match, use question mark, which at least in * if no match, use question mark, which at least in
* some cases serves as wild card * some cases serves as wild card
*/ */
if (charlen < 1) { if (charlen > 0)
goto ctoUTF16;
/* convert SURROGATE_PAIR */
if (strcmp(cp->charset, "utf8"))
goto unknown;
if (*(source + i) & 0x80) {
charlen = utf8_to_utf32(source + i, 6, &u);
if (charlen < 0)
goto unknown;
} else
goto unknown;
ret = utf8s_to_utf16s(source + i, charlen,
UTF16_LITTLE_ENDIAN,
wchar_to, 6);
if (ret < 0)
goto unknown;
i += charlen;
dst_char = cpu_to_le16(*wchar_to);
if (charlen <= 3)
/* 1-3bytes UTF-8 to 2bytes UTF-16 */
put_unaligned(dst_char, &target[j]);
else if (charlen == 4) {
/*
* 4bytes UTF-8(surrogate pair) to 4bytes UTF-16
* 7-8bytes UTF-8(IVS) divided to 2 UTF-16
* (charlen=3+4 or 4+4)
*/
put_unaligned(dst_char, &target[j]);
dst_char = cpu_to_le16(*(wchar_to + 1));
j++;
put_unaligned(dst_char, &target[j]);
} else if (charlen >= 5) {
/* 5-6bytes UTF-8 to 6bytes UTF-16 */
put_unaligned(dst_char, &target[j]);
dst_char = cpu_to_le16(*(wchar_to + 1));
j++;
put_unaligned(dst_char, &target[j]);
dst_char = cpu_to_le16(*(wchar_to + 2));
j++;
put_unaligned(dst_char, &target[j]);
}
continue;
unknown:
dst_char = cpu_to_le16(0x003f); dst_char = cpu_to_le16(0x003f);
charlen = 1; charlen = 1;
} }
}
ctoUTF16:
/* /*
* character may take more than one byte in the source string, * character may take more than one byte in the source string,
* but will take exactly two bytes in the target string * but will take exactly two bytes in the target string
......
...@@ -173,10 +173,6 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) ...@@ -173,10 +173,6 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
return err; return err;
} }
err = mnt_want_write(path.mnt);
if (err)
goto out_err;
mode |= S_IFREG; mode |= S_IFREG;
err = vfs_create(mnt_idmap(path.mnt), d_inode(path.dentry), err = vfs_create(mnt_idmap(path.mnt), d_inode(path.dentry),
dentry, mode, true); dentry, mode, true);
...@@ -186,9 +182,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) ...@@ -186,9 +182,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
} else { } else {
pr_err("File(%s): creation failed (err:%d)\n", name, err); pr_err("File(%s): creation failed (err:%d)\n", name, err);
} }
mnt_drop_write(path.mnt);
out_err:
done_path_create(&path, dentry); done_path_create(&path, dentry);
return err; return err;
} }
...@@ -219,10 +213,6 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) ...@@ -219,10 +213,6 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
return err; return err;
} }
err = mnt_want_write(path.mnt);
if (err)
goto out_err2;
idmap = mnt_idmap(path.mnt); idmap = mnt_idmap(path.mnt);
mode |= S_IFDIR; mode |= S_IFDIR;
err = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode); err = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode);
...@@ -233,21 +223,19 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) ...@@ -233,21 +223,19 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
dentry->d_name.len); dentry->d_name.len);
if (IS_ERR(d)) { if (IS_ERR(d)) {
err = PTR_ERR(d); err = PTR_ERR(d);
goto out_err1; goto out_err;
} }
if (unlikely(d_is_negative(d))) { if (unlikely(d_is_negative(d))) {
dput(d); dput(d);
err = -ENOENT; err = -ENOENT;
goto out_err1; goto out_err;
} }
ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d));
dput(d); dput(d);
} }
out_err1: out_err:
mnt_drop_write(path.mnt);
out_err2:
done_path_create(&path, dentry); done_path_create(&path, dentry);
if (err) if (err)
pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); pr_err("mkdir(%s): creation failed (err:%d)\n", name, err);
...@@ -665,16 +653,11 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, ...@@ -665,16 +653,11 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
goto out3; goto out3;
} }
err = mnt_want_write(newpath.mnt);
if (err)
goto out3;
err = vfs_link(oldpath.dentry, mnt_idmap(newpath.mnt), err = vfs_link(oldpath.dentry, mnt_idmap(newpath.mnt),
d_inode(newpath.dentry), d_inode(newpath.dentry),
dentry, NULL); dentry, NULL);
if (err) if (err)
ksmbd_debug(VFS, "vfs_link failed err %d\n", err); ksmbd_debug(VFS, "vfs_link failed err %d\n", err);
mnt_drop_write(newpath.mnt);
out3: out3:
done_path_create(&newpath, dentry); done_path_create(&newpath, dentry);
...@@ -919,7 +902,7 @@ ssize_t ksmbd_vfs_getxattr(struct mnt_idmap *idmap, ...@@ -919,7 +902,7 @@ ssize_t ksmbd_vfs_getxattr(struct mnt_idmap *idmap,
/** /**
* ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value
* @idmap: idmap of the relevant mount * @idmap: idmap of the relevant mount
* @dentry: dentry to set XATTR at * @path: path of dentry to set XATTR at
* @attr_name: xattr name for setxattr * @attr_name: xattr name for setxattr
* @attr_value: xattr value to set * @attr_value: xattr value to set
* @attr_size: size of xattr value * @attr_size: size of xattr value
......
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