summaryrefslogtreecommitdiff
path: root/fs/smb
diff options
context:
space:
mode:
Diffstat (limited to 'fs/smb')
-rw-r--r--fs/smb/client/Kconfig9
-rw-r--r--fs/smb/client/Makefile2
-rw-r--r--fs/smb/client/asn1.c2
-rw-r--r--fs/smb/client/cached_dir.c279
-rw-r--r--fs/smb/client/cached_dir.h35
-rw-r--r--fs/smb/client/cifs_debug.c268
-rw-r--r--fs/smb/client/cifs_debug.h6
-rw-r--r--fs/smb/client/cifs_fs_sb.h1
-rw-r--r--fs/smb/client/cifs_ioctl.h2
-rw-r--r--fs/smb/client/cifs_spnego.c68
-rw-r--r--fs/smb/client/cifs_spnego.h2
-rw-r--r--fs/smb/client/cifs_swn.c20
-rw-r--r--fs/smb/client/cifs_unicode.c3
-rw-r--r--fs/smb/client/cifs_unicode.h3
-rw-r--r--fs/smb/client/cifsacl.c95
-rw-r--r--fs/smb/client/cifsencrypt.c535
-rw-r--r--fs/smb/client/cifsfs.c167
-rw-r--r--fs/smb/client/cifsfs.h13
-rw-r--r--fs/smb/client/cifsglob.h458
-rw-r--r--fs/smb/client/cifspdu.h610
-rw-r--r--fs/smb/client/cifsproto.h258
-rw-r--r--fs/smb/client/cifssmb.c1292
-rw-r--r--fs/smb/client/cifstransport.c263
-rw-r--r--fs/smb/client/compress.c94
-rw-r--r--fs/smb/client/compress.h19
-rw-r--r--fs/smb/client/connect.c887
-rw-r--r--fs/smb/client/dfs.c287
-rw-r--r--fs/smb/client/dfs.h51
-rw-r--r--fs/smb/client/dfs_cache.c102
-rw-r--r--fs/smb/client/dir.c126
-rw-r--r--fs/smb/client/dns_resolve.c108
-rw-r--r--fs/smb/client/dns_resolve.h23
-rw-r--r--fs/smb/client/file.c247
-rw-r--r--fs/smb/client/fs_context.c410
-rw-r--r--fs/smb/client/fs_context.h95
-rw-r--r--fs/smb/client/inode.c508
-rw-r--r--fs/smb/client/ioctl.c2
-rw-r--r--fs/smb/client/link.c104
-rw-r--r--fs/smb/client/misc.c153
-rw-r--r--fs/smb/client/namespace.c23
-rw-r--r--fs/smb/client/netmisc.c25
-rw-r--r--fs/smb/client/nterr.c9
-rw-r--r--fs/smb/client/nterr.h1
-rw-r--r--fs/smb/client/ntlmssp.h8
-rw-r--r--fs/smb/client/readdir.c138
-rw-r--r--fs/smb/client/reparse.c812
-rw-r--r--fs/smb/client/reparse.h39
-rw-r--r--fs/smb/client/rfc1002pdu.h14
-rw-r--r--fs/smb/client/sess.c197
-rw-r--r--fs/smb/client/smb1ops.c558
-rw-r--r--fs/smb/client/smb2file.c99
-rw-r--r--fs/smb/client/smb2glob.h4
-rw-r--r--fs/smb/client/smb2inode.c617
-rw-r--r--fs/smb/client/smb2maperror.c56
-rw-r--r--fs/smb/client/smb2misc.c84
-rw-r--r--fs/smb/client/smb2ops.c788
-rw-r--r--fs/smb/client/smb2pdu.c698
-rw-r--r--fs/smb/client/smb2pdu.h110
-rw-r--r--fs/smb/client/smb2proto.h53
-rw-r--r--fs/smb/client/smb2transport.c251
-rw-r--r--fs/smb/client/smbdirect.c2287
-rw-r--r--fs/smb/client/smbdirect.h259
-rw-r--r--fs/smb/client/trace.c2
-rw-r--r--fs/smb/client/trace.h283
-rw-r--r--fs/smb/client/transport.c803
-rw-r--r--fs/smb/client/xattr.c52
-rw-r--r--fs/smb/common/Makefile1
-rw-r--r--fs/smb/common/arc4.h23
-rw-r--r--fs/smb/common/cifs_arc4.c75
-rw-r--r--fs/smb/common/fscc.h174
-rw-r--r--fs/smb/common/smb2pdu.h332
-rw-r--r--fs/smb/common/smb2status.h5
-rw-r--r--fs/smb/common/smbacl.h11
-rw-r--r--fs/smb/common/smbdirect/smbdirect.h44
-rw-r--r--fs/smb/common/smbdirect/smbdirect_pdu.h55
-rw-r--r--fs/smb/common/smbdirect/smbdirect_socket.h547
-rw-r--r--fs/smb/common/smbfsctl.h3
-rw-r--r--fs/smb/common/smbglob.h71
-rw-r--r--fs/smb/server/Kconfig10
-rw-r--r--fs/smb/server/asn1.c6
-rw-r--r--fs/smb/server/auth.c468
-rw-r--r--fs/smb/server/auth.h12
-rw-r--r--fs/smb/server/connection.c79
-rw-r--r--fs/smb/server/connection.h38
-rw-r--r--fs/smb/server/crypto_ctx.c38
-rw-r--r--fs/smb/server/crypto_ctx.h19
-rw-r--r--fs/smb/server/glob.h2
-rw-r--r--fs/smb/server/ksmbd_netlink.h6
-rw-r--r--fs/smb/server/ksmbd_work.c15
-rw-r--r--fs/smb/server/ksmbd_work.h1
-rw-r--r--fs/smb/server/mgmt/ksmbd_ida.c11
-rw-r--r--fs/smb/server/mgmt/share_config.c12
-rw-r--r--fs/smb/server/mgmt/tree_connect.c23
-rw-r--r--fs/smb/server/mgmt/tree_connect.h1
-rw-r--r--fs/smb/server/mgmt/user_config.c8
-rw-r--r--fs/smb/server/mgmt/user_session.c88
-rw-r--r--fs/smb/server/mgmt/user_session.h3
-rw-r--r--fs/smb/server/misc.c22
-rw-r--r--fs/smb/server/ndr.c10
-rw-r--r--fs/smb/server/oplock.c121
-rw-r--r--fs/smb/server/oplock.h2
-rw-r--r--fs/smb/server/server.c31
-rw-r--r--fs/smb/server/server.h3
-rw-r--r--fs/smb/server/smb2misc.c2
-rw-r--r--fs/smb/server/smb2ops.c38
-rw-r--r--fs/smb/server/smb2pdu.c779
-rw-r--r--fs/smb/server/smb2pdu.h124
-rw-r--r--fs/smb/server/smb_common.c20
-rw-r--r--fs/smb/server/smb_common.h286
-rw-r--r--fs/smb/server/smbacl.c94
-rw-r--r--fs/smb/server/smbacl.h2
-rw-r--r--fs/smb/server/transport_ipc.c84
-rw-r--r--fs/smb/server/transport_ipc.h2
-rw-r--r--fs/smb/server/transport_rdma.c2249
-rw-r--r--fs/smb/server/transport_rdma.h49
-rw-r--r--fs/smb/server/transport_tcp.c247
-rw-r--r--fs/smb/server/transport_tcp.h2
-rw-r--r--fs/smb/server/unicode.c4
-rw-r--r--fs/smb/server/vfs.c457
-rw-r--r--fs/smb/server/vfs.h17
-rw-r--r--fs/smb/server/vfs_cache.c139
-rw-r--r--fs/smb/server/vfs_cache.h3
122 files changed, 13482 insertions, 9363 deletions
diff --git a/fs/smb/client/Kconfig b/fs/smb/client/Kconfig
index 2aff6d1395ce..17bd368574e9 100644
--- a/fs/smb/client/Kconfig
+++ b/fs/smb/client/Kconfig
@@ -2,20 +2,19 @@
config CIFS
tristate "SMB3 and CIFS support (advanced network filesystem)"
depends on INET
- select NETFS_SUPPORT
select NLS
select NLS_UCS2_UTILS
select CRYPTO
- select CRYPTO_MD5
- select CRYPTO_SHA256
- select CRYPTO_SHA512
select CRYPTO_CMAC
- select CRYPTO_HMAC
select CRYPTO_AEAD2
select CRYPTO_CCM
select CRYPTO_GCM
select CRYPTO_ECB
select CRYPTO_AES
+ select CRYPTO_LIB_ARC4
+ select CRYPTO_LIB_MD5
+ select CRYPTO_LIB_SHA256
+ select CRYPTO_LIB_SHA512
select KEYS
select DNS_RESOLVER
select ASN1
diff --git a/fs/smb/client/Makefile b/fs/smb/client/Makefile
index 22023e30915b..4c97b31a25c2 100644
--- a/fs/smb/client/Makefile
+++ b/fs/smb/client/Makefile
@@ -32,6 +32,6 @@ cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o
cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o
-cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o
+cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o cifstransport.o
cifs-$(CONFIG_CIFS_COMPRESSION) += compress.o compress/lz77.o
diff --git a/fs/smb/client/asn1.c b/fs/smb/client/asn1.c
index b5724ef9f182..214a44509e7b 100644
--- a/fs/smb/client/asn1.c
+++ b/fs/smb/client/asn1.c
@@ -52,6 +52,8 @@ int cifs_neg_token_init_mech_type(void *context, size_t hdrlen,
server->sec_kerberos = true;
else if (oid == OID_ntlmssp)
server->sec_ntlmssp = true;
+ else if (oid == OID_IAKerb)
+ server->sec_iakerb = true;
else {
char buf[50];
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index 06eb19dabb0e..1db7ab6c2529 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -16,6 +16,12 @@ static struct cached_fid *init_cached_dir(const char *path);
static void free_cached_dir(struct cached_fid *cfid);
static void smb2_close_cached_fid(struct kref *ref);
static void cfids_laundromat_worker(struct work_struct *work);
+static void close_cached_dir_locked(struct cached_fid *cfid);
+
+struct cached_dir_dentry {
+ struct list_head entry;
+ struct dentry *dentry;
+};
static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
const char *path,
@@ -24,7 +30,6 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
{
struct cached_fid *cfid;
- spin_lock(&cfids->cfid_list_lock);
list_for_each_entry(cfid, &cfids->entries, entry) {
if (!strcmp(cfid->path, path)) {
/*
@@ -32,26 +37,20 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
* fully cached or it may be in the process of
* being deleted due to a lease break.
*/
- if (!cfid->time || !cfid->has_lease) {
- spin_unlock(&cfids->cfid_list_lock);
+ if (!is_valid_cached_dir(cfid))
return NULL;
- }
kref_get(&cfid->refcount);
- spin_unlock(&cfids->cfid_list_lock);
return cfid;
}
}
if (lookup_only) {
- spin_unlock(&cfids->cfid_list_lock);
return NULL;
}
if (cfids->num_entries >= max_cached_dirs) {
- spin_unlock(&cfids->cfid_list_lock);
return NULL;
}
cfid = init_cached_dir(path);
if (cfid == NULL) {
- spin_unlock(&cfids->cfid_list_lock);
return NULL;
}
cfid->cfids = cfids;
@@ -69,7 +68,6 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
*/
cfid->has_lease = true;
- spin_unlock(&cfids->cfid_list_lock);
return cfid;
}
@@ -104,7 +102,8 @@ path_to_dentry(struct cifs_sb_info *cifs_sb, const char *path)
while (*s && *s != sep)
s++;
- child = lookup_positive_unlocked(p, dentry, s - p);
+ child = lookup_noperm_positive_unlocked(&QSTR_LEN(p, s - p),
+ dentry);
dput(dentry);
dentry = child;
} while (!IS_ERR(dentry));
@@ -156,16 +155,19 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
struct cached_fids *cfids;
const char *npath;
int retries = 0, cur_sleep = 1;
+ __le32 lease_flags = 0;
+
+ if (cifs_sb->root == NULL)
+ return -ENOENT;
- if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache ||
- is_smb1_server(tcon->ses->server) || (dir_cache_timeout == 0))
+ if (tcon == NULL)
return -EOPNOTSUPP;
ses = tcon->ses;
cfids = tcon->cfids;
- if (cifs_sb->root == NULL)
- return -ENOENT;
+ if (cfids == NULL)
+ return -EOPNOTSUPP;
replay_again:
/* reinitialize for possible replay */
@@ -174,14 +176,16 @@ replay_again:
server = cifs_pick_channel(ses);
if (!server->ops->new_lease_key)
- return -EIO;
+ return smb_EIO(smb_eio_trace_no_lease_key);
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
if (!utf16_path)
return -ENOMEM;
+ spin_lock(&cfids->cfid_list_lock);
cfid = find_or_create_cached_dir(cfids, path, lookup_only, tcon->max_cached_dirs);
if (cfid == NULL) {
+ spin_unlock(&cfids->cfid_list_lock);
kfree(utf16_path);
return -ENOENT;
}
@@ -190,8 +194,8 @@ replay_again:
* Otherwise, it is either a new entry or laundromat worker removed it
* from @cfids->entries. Caller will put last reference if the latter.
*/
- spin_lock(&cfids->cfid_list_lock);
- if (cfid->has_lease && cfid->time) {
+ if (is_valid_cached_dir(cfid)) {
+ cfid->last_access_time = jiffies;
spin_unlock(&cfids->cfid_list_lock);
*ret_cfid = cfid;
kfree(utf16_path);
@@ -199,8 +203,10 @@ replay_again:
}
spin_unlock(&cfids->cfid_list_lock);
+ pfid = &cfid->fid;
+
/*
- * Skip any prefix paths in @path as lookup_positive_unlocked() ends up
+ * Skip any prefix paths in @path as lookup_noperm_positive_unlocked() ends up
* calling ->lookup() which already adds those through
* build_path_from_dentry(). Also, do it earlier as we might reconnect
* below when trying to send compounded request and then potentially
@@ -220,8 +226,28 @@ replay_again:
rc = -ENOENT;
goto out;
}
+ if (dentry->d_parent && server->dialect >= SMB30_PROT_ID) {
+ struct cached_fid *parent_cfid;
+
+ spin_lock(&cfids->cfid_list_lock);
+ list_for_each_entry(parent_cfid, &cfids->entries, entry) {
+ if (parent_cfid->dentry == dentry->d_parent) {
+ cifs_dbg(FYI, "found a parent cached file handle\n");
+ if (is_valid_cached_dir(parent_cfid)) {
+ lease_flags
+ |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE;
+ memcpy(pfid->parent_lease_key,
+ parent_cfid->fid.lease_key,
+ SMB2_LEASE_KEY_SIZE);
+ }
+ break;
+ }
+ }
+ spin_unlock(&cfids->cfid_list_lock);
+ }
}
cfid->dentry = dentry;
+ cfid->tcon = tcon;
/*
* We do not hold the lock for the open because in case
@@ -233,7 +259,6 @@ replay_again:
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
- pfid = &cfid->fid;
server->ops->new_lease_key(pfid);
memset(rqst, 0, sizeof(rqst));
@@ -253,6 +278,7 @@ replay_again:
FILE_READ_EA,
.disposition = FILE_OPEN,
.fid = pfid,
+ .lease_flags = lease_flags,
.replay = !!(retries),
};
@@ -293,7 +319,6 @@ replay_again:
}
goto oshr_free;
}
- cfid->tcon = tcon;
cfid->is_open = true;
spin_lock(&cfids->cfid_list_lock);
@@ -339,6 +364,7 @@ replay_again:
cfid->file_all_info_is_valid = true;
cfid->time = jiffies;
+ cfid->last_access_time = jiffies;
spin_unlock(&cfids->cfid_list_lock);
/* At this point the directory handle is fully cached */
rc = 0;
@@ -363,11 +389,11 @@ out:
* lease. Release one here, and the second below.
*/
cfid->has_lease = false;
- kref_put(&cfid->refcount, smb2_close_cached_fid);
+ close_cached_dir_locked(cfid);
}
spin_unlock(&cfids->cfid_list_lock);
- kref_put(&cfid->refcount, smb2_close_cached_fid);
+ close_cached_dir(cfid);
} else {
*ret_cfid = cfid;
atomic_inc(&tcon->num_remote_opens);
@@ -389,14 +415,20 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
struct cached_fids *cfids = tcon->cfids;
if (cfids == NULL)
+ return -EOPNOTSUPP;
+
+ if (!dentry)
return -ENOENT;
spin_lock(&cfids->cfid_list_lock);
list_for_each_entry(cfid, &cfids->entries, entry) {
- if (dentry && cfid->dentry == dentry) {
+ if (cfid->dentry == dentry) {
+ if (!is_valid_cached_dir(cfid))
+ break;
cifs_dbg(FYI, "found a cached file handle by dentry\n");
kref_get(&cfid->refcount);
*ret_cfid = cfid;
+ cfid->last_access_time = jiffies;
spin_unlock(&cfids->cfid_list_lock);
return 0;
}
@@ -407,12 +439,14 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
static void
smb2_close_cached_fid(struct kref *ref)
+__releases(&cfid->cfids->cfid_list_lock)
{
struct cached_fid *cfid = container_of(ref, struct cached_fid,
refcount);
int rc;
- spin_lock(&cfid->cfids->cfid_list_lock);
+ lockdep_assert_held(&cfid->cfids->cfid_list_lock);
+
if (cfid->on_list) {
list_del(&cfid->entry);
cfid->on_list = false;
@@ -447,15 +481,49 @@ void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon,
spin_lock(&cfid->cfids->cfid_list_lock);
if (cfid->has_lease) {
cfid->has_lease = false;
- kref_put(&cfid->refcount, smb2_close_cached_fid);
+ close_cached_dir_locked(cfid);
}
spin_unlock(&cfid->cfids->cfid_list_lock);
close_cached_dir(cfid);
}
-
+/**
+ * close_cached_dir - drop a reference of a cached dir
+ *
+ * The release function will be called with cfid_list_lock held to remove the
+ * cached dirs from the list before any other thread can take another @cfid
+ * ref. Must not be called with cfid_list_lock held; use
+ * close_cached_dir_locked() called instead.
+ *
+ * @cfid: cached dir
+ */
void close_cached_dir(struct cached_fid *cfid)
{
+ lockdep_assert_not_held(&cfid->cfids->cfid_list_lock);
+ kref_put_lock(&cfid->refcount, smb2_close_cached_fid, &cfid->cfids->cfid_list_lock);
+}
+
+/**
+ * close_cached_dir_locked - put a reference of a cached dir with
+ * cfid_list_lock held
+ *
+ * Calling close_cached_dir() with cfid_list_lock held has the potential effect
+ * of causing a deadlock if the invariant of refcount >= 2 is false.
+ *
+ * This function is used in paths that hold cfid_list_lock and expect at least
+ * two references. If that invariant is violated, WARNs and returns without
+ * dropping a reference; the final put must still go through
+ * close_cached_dir().
+ *
+ * @cfid: cached dir
+ */
+static void close_cached_dir_locked(struct cached_fid *cfid)
+{
+ lockdep_assert_held(&cfid->cfids->cfid_list_lock);
+
+ if (WARN_ON(kref_read(&cfid->refcount) < 2))
+ return;
+
kref_put(&cfid->refcount, smb2_close_cached_fid);
}
@@ -470,7 +538,10 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
struct cifs_tcon *tcon;
struct tcon_link *tlink;
struct cached_fids *cfids;
+ struct cached_dir_dentry *tmp_list, *q;
+ LIST_HEAD(entry);
+ spin_lock(&cifs_sb->tlink_tree_lock);
for (node = rb_first(root); node; node = rb_next(node)) {
tlink = rb_entry(node, struct tcon_link, tl_rbnode);
tcon = tlink_tcon(tlink);
@@ -479,11 +550,39 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
cfids = tcon->cfids;
if (cfids == NULL)
continue;
+ spin_lock(&cfids->cfid_list_lock);
list_for_each_entry(cfid, &cfids->entries, entry) {
- dput(cfid->dentry);
+ tmp_list = kmalloc(sizeof(*tmp_list), GFP_ATOMIC);
+ if (tmp_list == NULL) {
+ /*
+ * If the malloc() fails, we won't drop all
+ * dentries, and unmounting is likely to trigger
+ * a 'Dentry still in use' error.
+ */
+ cifs_tcon_dbg(VFS, "Out of memory while dropping dentries\n");
+ spin_unlock(&cfids->cfid_list_lock);
+ spin_unlock(&cifs_sb->tlink_tree_lock);
+ goto done;
+ }
+
+ tmp_list->dentry = cfid->dentry;
cfid->dentry = NULL;
+
+ list_add_tail(&tmp_list->entry, &entry);
}
+ spin_unlock(&cfids->cfid_list_lock);
+ }
+ spin_unlock(&cifs_sb->tlink_tree_lock);
+
+done:
+ list_for_each_entry_safe(tmp_list, q, &entry, entry) {
+ list_del(&tmp_list->entry);
+ dput(tmp_list->dentry);
+ kfree(tmp_list);
}
+
+ /* Flush any pending work that will drop dentries */
+ flush_workqueue(cfid_put_wq);
}
/*
@@ -494,14 +593,18 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
{
struct cached_fids *cfids = tcon->cfids;
struct cached_fid *cfid, *q;
- LIST_HEAD(entry);
if (cfids == NULL)
return;
+ /*
+ * Mark all the cfids as closed, and move them to the cfids->dying list.
+ * They'll be cleaned up by laundromat. Take a reference to each cfid
+ * during this process.
+ */
spin_lock(&cfids->cfid_list_lock);
list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
- list_move(&cfid->entry, &entry);
+ list_move(&cfid->entry, &cfids->dying);
cfids->num_entries--;
cfid->is_open = false;
cfid->on_list = false;
@@ -516,27 +619,42 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
}
spin_unlock(&cfids->cfid_list_lock);
- list_for_each_entry_safe(cfid, q, &entry, entry) {
- list_del(&cfid->entry);
- cancel_work_sync(&cfid->lease_break);
- /*
- * Drop the ref-count from above, either the lease-ref (if there
- * was one) or the extra one acquired.
- */
- kref_put(&cfid->refcount, smb2_close_cached_fid);
- }
+ /* run laundromat unconditionally now as there might have been previously queued work */
+ mod_delayed_work(cfid_put_wq, &cfids->laundromat_work, 0);
+ flush_delayed_work(&cfids->laundromat_work);
}
static void
-smb2_cached_lease_break(struct work_struct *work)
+cached_dir_offload_close(struct work_struct *work)
{
struct cached_fid *cfid = container_of(work,
- struct cached_fid, lease_break);
+ struct cached_fid, close_work);
+ struct cifs_tcon *tcon = cfid->tcon;
- kref_put(&cfid->refcount, smb2_close_cached_fid);
+ WARN_ON(cfid->on_list);
+
+ close_cached_dir(cfid);
+ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cached_close);
+}
+
+/*
+ * Release the cached directory's dentry, and then queue work to drop cached
+ * directory itself (closing on server if needed).
+ *
+ * Must be called with a reference to the cached_fid and a reference to the
+ * tcon.
+ */
+static void cached_dir_put_work(struct work_struct *work)
+{
+ struct cached_fid *cfid = container_of(work, struct cached_fid,
+ put_work);
+ dput(cfid->dentry);
+ cfid->dentry = NULL;
+
+ queue_work(serverclose_wq, &cfid->close_work);
}
-int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
+bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
{
struct cached_fids *cfids = tcon->cfids;
struct cached_fid *cfid;
@@ -560,8 +678,10 @@ int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
cfid->on_list = false;
cfids->num_entries--;
- queue_work(cifsiod_wq,
- &cfid->lease_break);
+ ++tcon->tc_count;
+ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
+ netfs_trace_tcon_ref_get_cached_lease_break);
+ queue_work(cfid_put_wq, &cfid->put_work);
spin_unlock(&cfids->cfid_list_lock);
return true;
}
@@ -583,11 +703,11 @@ static struct cached_fid *init_cached_dir(const char *path)
return NULL;
}
- INIT_WORK(&cfid->lease_break, smb2_cached_lease_break);
+ INIT_WORK(&cfid->close_work, cached_dir_offload_close);
+ INIT_WORK(&cfid->put_work, cached_dir_put_work);
INIT_LIST_HEAD(&cfid->entry);
INIT_LIST_HEAD(&cfid->dirents.entries);
mutex_init(&cfid->dirents.de_mutex);
- spin_lock_init(&cfid->fid_lock);
kref_init(&cfid->refcount);
return cfid;
}
@@ -596,6 +716,9 @@ static void free_cached_dir(struct cached_fid *cfid)
{
struct cached_dirent *dirent, *q;
+ WARN_ON(work_pending(&cfid->close_work));
+ WARN_ON(work_pending(&cfid->put_work));
+
dput(cfid->dentry);
cfid->dentry = NULL;
@@ -608,6 +731,21 @@ static void free_cached_dir(struct cached_fid *cfid)
kfree(dirent);
}
+ /* adjust tcon-level counters and reset per-dir accounting */
+ if (cfid->cfids) {
+ if (cfid->dirents.entries_count)
+ atomic_long_sub((long)cfid->dirents.entries_count,
+ &cfid->cfids->total_dirents_entries);
+ if (cfid->dirents.bytes_used) {
+ atomic64_sub((long long)cfid->dirents.bytes_used,
+ &cfid->cfids->total_dirents_bytes);
+ atomic64_sub((long long)cfid->dirents.bytes_used,
+ &cifs_dircache_bytes_used);
+ }
+ }
+ cfid->dirents.entries_count = 0;
+ cfid->dirents.bytes_used = 0;
+
kfree(cfid->path);
cfid->path = NULL;
kfree(cfid);
@@ -622,9 +760,12 @@ static void cfids_laundromat_worker(struct work_struct *work)
cfids = container_of(work, struct cached_fids, laundromat_work.work);
spin_lock(&cfids->cfid_list_lock);
+ /* move cfids->dying to the local list */
+ list_cut_before(&entry, &cfids->dying, &cfids->dying);
+
list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
- if (cfid->time &&
- time_after(jiffies, cfid->time + HZ * dir_cache_timeout)) {
+ if (cfid->last_access_time &&
+ time_after(jiffies, cfid->last_access_time + HZ * dir_cache_timeout)) {
cfid->on_list = false;
list_move(&cfid->entry, &entry);
cfids->num_entries--;
@@ -642,18 +783,25 @@ static void cfids_laundromat_worker(struct work_struct *work)
list_for_each_entry_safe(cfid, q, &entry, entry) {
list_del(&cfid->entry);
- /*
- * Cancel and wait for the work to finish in case we are racing
- * with it.
- */
- cancel_work_sync(&cfid->lease_break);
- /*
- * Drop the ref-count from above, either the lease-ref (if there
- * was one) or the extra one acquired.
- */
- kref_put(&cfid->refcount, smb2_close_cached_fid);
- }
- queue_delayed_work(cifsiod_wq, &cfids->laundromat_work,
+
+ dput(cfid->dentry);
+ cfid->dentry = NULL;
+
+ if (cfid->is_open) {
+ spin_lock(&cifs_tcp_ses_lock);
+ ++cfid->tcon->tc_count;
+ trace_smb3_tcon_ref(cfid->tcon->debug_id, cfid->tcon->tc_count,
+ netfs_trace_tcon_ref_get_cached_laundromat);
+ spin_unlock(&cifs_tcp_ses_lock);
+ queue_work(serverclose_wq, &cfid->close_work);
+ } else
+ /*
+ * Drop the ref-count from above, either the lease-ref (if there
+ * was one) or the extra one acquired.
+ */
+ close_cached_dir(cfid);
+ }
+ queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
dir_cache_timeout * HZ);
}
@@ -666,11 +814,15 @@ struct cached_fids *init_cached_dirs(void)
return NULL;
spin_lock_init(&cfids->cfid_list_lock);
INIT_LIST_HEAD(&cfids->entries);
+ INIT_LIST_HEAD(&cfids->dying);
INIT_DELAYED_WORK(&cfids->laundromat_work, cfids_laundromat_worker);
- queue_delayed_work(cifsiod_wq, &cfids->laundromat_work,
+ queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
dir_cache_timeout * HZ);
+ atomic_long_set(&cfids->total_dirents_entries, 0);
+ atomic64_set(&cfids->total_dirents_bytes, 0);
+
return cfids;
}
@@ -694,6 +846,11 @@ void free_cached_dirs(struct cached_fids *cfids)
cfid->is_open = false;
list_move(&cfid->entry, &entry);
}
+ list_for_each_entry_safe(cfid, q, &cfids->dying, entry) {
+ cfid->on_list = false;
+ cfid->is_open = false;
+ list_move(&cfid->entry, &entry);
+ }
spin_unlock(&cfids->cfid_list_lock);
list_for_each_entry_safe(cfid, q, &entry, entry) {
diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h
index 81ba0fd5cc16..1e383db7c337 100644
--- a/fs/smb/client/cached_dir.h
+++ b/fs/smb/client/cached_dir.h
@@ -14,20 +14,22 @@ struct cached_dirent {
char *name;
int namelen;
loff_t pos;
-
struct cifs_fattr fattr;
};
struct cached_dirents {
bool is_valid:1;
bool is_failed:1;
- struct dir_context *ctx; /*
- * Only used to make sure we only take entries
- * from a single context. Never dereferenced.
- */
+ struct file *file; /*
+ * Used to associate the cache with a single
+ * open file instance.
+ */
struct mutex de_mutex;
- int pos; /* Expected ctx->pos */
+ loff_t pos; /* Expected ctx->pos */
struct list_head entries;
+ /* accounting for cached entries in this directory */
+ unsigned long entries_count;
+ unsigned long bytes_used;
};
struct cached_fid {
@@ -39,12 +41,13 @@ struct cached_fid {
bool on_list:1;
bool file_all_info_is_valid:1;
unsigned long time; /* jiffies of when lease was taken */
+ unsigned long last_access_time; /* jiffies of when last accessed */
struct kref refcount;
struct cifs_fid fid;
- spinlock_t fid_lock;
struct cifs_tcon *tcon;
struct dentry *dentry;
- struct work_struct lease_break;
+ struct work_struct put_work;
+ struct work_struct close_work;
struct smb2_file_all_info file_all_info;
struct cached_dirents dirents;
};
@@ -53,13 +56,27 @@ struct cached_fid {
struct cached_fids {
/* Must be held when:
* - accessing the cfids->entries list
+ * - accessing the cfids->dying list
*/
spinlock_t cfid_list_lock;
int num_entries;
struct list_head entries;
+ struct list_head dying;
struct delayed_work laundromat_work;
+ /* aggregate accounting for all cached dirents under this tcon */
+ atomic_long_t total_dirents_entries;
+ atomic64_t total_dirents_bytes;
};
+/* Module-wide directory cache accounting (defined in cifsfs.c) */
+extern atomic64_t cifs_dircache_bytes_used; /* bytes across all mounts */
+
+static inline bool
+is_valid_cached_dir(struct cached_fid *cfid)
+{
+ return cfid->time && cfid->has_lease;
+}
+
extern struct cached_fids *init_cached_dirs(void);
extern void free_cached_dirs(struct cached_fids *cfids);
extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
@@ -76,6 +93,6 @@ extern void drop_cached_dir_by_name(const unsigned int xid,
struct cifs_sb_info *cifs_sb);
extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
-extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
+extern bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
#endif /* _CACHED_DIR_H */
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index e03c890de0a0..2cb234d4bd2f 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -24,8 +24,10 @@
#endif
#ifdef CONFIG_CIFS_SMB_DIRECT
#include "smbdirect.h"
+#include "../common/smbdirect/smbdirect_pdu.h"
#endif
#include "cifs_swn.h"
+#include "cached_dir.h"
void
cifs_dump_mem(char *label, void *data, int length)
@@ -35,7 +37,7 @@ cifs_dump_mem(char *label, void *data, int length)
data, length, true);
}
-void cifs_dump_detail(void *buf, struct TCP_Server_Info *server)
+void cifs_dump_detail(void *buf, size_t buf_len, struct TCP_Server_Info *server)
{
#ifdef CONFIG_CIFS_DEBUG2
struct smb_hdr *smb = buf;
@@ -43,7 +45,7 @@ void cifs_dump_detail(void *buf, struct TCP_Server_Info *server)
cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d Wct: %d\n",
smb->Command, smb->Status.CifsError, smb->Flags,
smb->Flags2, smb->Mid, smb->Pid, smb->WordCount);
- if (!server->ops->check_message(buf, server->total_read, server)) {
+ if (!server->ops->check_message(buf, buf_len, server->total_read, server)) {
cifs_dbg(VFS, "smb buf %p len %u\n", smb,
server->ops->calc_smb_size(smb));
}
@@ -59,7 +61,7 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
return;
cifs_dbg(VFS, "Dump pending requests:\n");
- spin_lock(&server->mid_lock);
+ spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n",
mid_entry->mid_state,
@@ -77,12 +79,12 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
cifs_dbg(VFS, "IsMult: %d IsEnd: %d\n",
mid_entry->multiRsp, mid_entry->multiEnd);
if (mid_entry->resp_buf) {
- cifs_dump_detail(mid_entry->resp_buf, server);
- cifs_dump_mem("existing buf: ",
- mid_entry->resp_buf, 62);
+ cifs_dump_detail(mid_entry->resp_buf,
+ mid_entry->response_pdu_len, server);
+ cifs_dump_mem("existing buf: ", mid_entry->resp_buf, 62);
}
}
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
#endif /* CONFIG_CIFS_DEBUG2 */
}
@@ -238,14 +240,18 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
struct cifs_ses *ses;
struct cifs_tcon *tcon;
struct cifsFileInfo *cfile;
+ struct inode *inode;
+ struct cifsInodeInfo *cinode;
+ char lease[4];
+ int n;
seq_puts(m, "# Version:1\n");
seq_puts(m, "# Format:\n");
seq_puts(m, "# <tree id> <ses id> <persistent fid> <flags> <count> <pid> <uid>");
#ifdef CONFIG_CIFS_DEBUG2
- seq_printf(m, " <filename> <mid>\n");
+ seq_puts(m, " <filename> <lease> <lease-key> <mid>\n");
#else
- seq_printf(m, " <filename>\n");
+ seq_puts(m, " <filename> <lease> <lease-key>\n");
#endif /* CIFS_DEBUG2 */
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
@@ -265,11 +271,37 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
cfile->pid,
from_kuid(&init_user_ns, cfile->uid),
cfile->dentry);
+
+ /* Append lease/oplock caching state as RHW letters */
+ inode = d_inode(cfile->dentry);
+ cinode = NULL;
+ n = 0;
+ if (inode) {
+ cinode = CIFS_I(inode);
+ if (CIFS_CACHE_READ(cinode))
+ lease[n++] = 'R';
+ if (CIFS_CACHE_HANDLE(cinode))
+ lease[n++] = 'H';
+ if (CIFS_CACHE_WRITE(cinode))
+ lease[n++] = 'W';
+ }
+ lease[n] = '\0';
+ seq_puts(m, " ");
+ if (n)
+ seq_printf(m, "%s", lease);
+ else
+ seq_puts(m, "NONE");
+
+ seq_puts(m, " ");
+ if (cinode && cinode->lease_granted)
+ seq_printf(m, "%pUl", cinode->lease_key);
+ else
+ seq_puts(m, "-");
+
#ifdef CONFIG_CIFS_DEBUG2
- seq_printf(m, " %llu\n", cfile->fid.mid);
-#else
+ seq_printf(m, " %llu", cfile->fid.mid);
+#endif /* CONFIG_CIFS_DEBUG2 */
seq_printf(m, "\n");
-#endif /* CIFS_DEBUG2 */
}
spin_unlock(&tcon->open_file_lock);
}
@@ -280,6 +312,64 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
return 0;
}
+static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
+{
+ struct list_head *stmp, *tmp, *tmp1;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ struct cached_fids *cfids;
+ struct cached_fid *cfid;
+ LIST_HEAD(entry);
+
+ seq_puts(m, "# Version:1\n");
+ seq_puts(m, "# Format:\n");
+ seq_puts(m, "# <tree id> <sess id> <persistent fid> <lease-key> <path>\n");
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(stmp, &cifs_tcp_ses_list) {
+ server = list_entry(stmp, struct TCP_Server_Info,
+ tcp_ses_list);
+ list_for_each(tmp, &server->smb_ses_list) {
+ ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+ list_for_each(tmp1, &ses->tcon_list) {
+ tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
+ cfids = tcon->cfids;
+ if (!cfids)
+ continue;
+ spin_lock(&cfids->cfid_list_lock); /* check lock ordering */
+ seq_printf(m, "Num entries: %d, cached_dirents: %lu entries, %llu bytes\n",
+ cfids->num_entries,
+ (unsigned long)atomic_long_read(&cfids->total_dirents_entries),
+ (unsigned long long)atomic64_read(&cfids->total_dirents_bytes));
+ list_for_each_entry(cfid, &cfids->entries, entry) {
+ seq_printf(m, "0x%x 0x%llx 0x%llx ",
+ tcon->tid,
+ ses->Suid,
+ cfid->fid.persistent_fid);
+ if (cfid->has_lease)
+ seq_printf(m, "%pUl ", cfid->fid.lease_key);
+ else
+ seq_puts(m, "- ");
+ seq_printf(m, "%s", cfid->path);
+ if (cfid->file_all_info_is_valid)
+ seq_printf(m, "\tvalid file info");
+ if (cfid->dirents.is_valid)
+ seq_printf(m, ", valid dirents");
+ if (!list_empty(&cfid->dirents.entries))
+ seq_printf(m, ", dirents: %lu entries, %lu bytes",
+ cfid->dirents.entries_count, cfid->dirents.bytes_used);
+ seq_printf(m, "\n");
+ }
+ spin_unlock(&cfids->cfid_list_lock);
+ }
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ seq_putc(m, '\n');
+ return 0;
+}
+
static __always_inline const char *compression_alg_str(__le16 alg)
{
switch (alg) {
@@ -298,6 +388,22 @@ static __always_inline const char *compression_alg_str(__le16 alg)
}
}
+static __always_inline const char *cipher_alg_str(__le16 cipher)
+{
+ switch (cipher) {
+ case SMB2_ENCRYPTION_AES128_CCM:
+ return "AES128-CCM";
+ case SMB2_ENCRYPTION_AES128_GCM:
+ return "AES128-GCM";
+ case SMB2_ENCRYPTION_AES256_CCM:
+ return "AES256-CCM";
+ case SMB2_ENCRYPTION_AES256_GCM:
+ return "AES256-GCM";
+ default:
+ return "UNKNOWN";
+ }
+}
+
static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
{
struct mid_q_entry *mid_entry;
@@ -362,6 +468,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
c = 0;
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
+#ifdef CONFIG_CIFS_SMB_DIRECT
+ struct smbdirect_socket *sc;
+ struct smbdirect_socket_parameters *sp;
+#endif
+
/* channel info will be printed as a part of sessions below */
if (SERVER_IS_CHAN(server))
continue;
@@ -383,62 +494,58 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\nSMBDirect transport not available");
goto skip_rdma;
}
-
- seq_printf(m, "\nSMBDirect (in hex) protocol version: %x "
- "transport status: %x",
- server->smbd_conn->protocol,
- server->smbd_conn->transport_status);
- seq_printf(m, "\nConn receive_credit_max: %x "
- "send_credit_target: %x max_send_size: %x",
- server->smbd_conn->receive_credit_max,
- server->smbd_conn->send_credit_target,
- server->smbd_conn->max_send_size);
- seq_printf(m, "\nConn max_fragmented_recv_size: %x "
- "max_fragmented_send_size: %x max_receive_size:%x",
- server->smbd_conn->max_fragmented_recv_size,
- server->smbd_conn->max_fragmented_send_size,
- server->smbd_conn->max_receive_size);
- seq_printf(m, "\nConn keep_alive_interval: %x "
- "max_readwrite_size: %x rdma_readwrite_threshold: %x",
- server->smbd_conn->keep_alive_interval,
- server->smbd_conn->max_readwrite_size,
- server->smbd_conn->rdma_readwrite_threshold);
- seq_printf(m, "\nDebug count_get_receive_buffer: %x "
- "count_put_receive_buffer: %x count_send_empty: %x",
- server->smbd_conn->count_get_receive_buffer,
- server->smbd_conn->count_put_receive_buffer,
- server->smbd_conn->count_send_empty);
- seq_printf(m, "\nRead Queue count_reassembly_queue: %x "
- "count_enqueue_reassembly_queue: %x "
- "count_dequeue_reassembly_queue: %x "
- "fragment_reassembly_remaining: %x "
- "reassembly_data_length: %x "
- "reassembly_queue_length: %x",
- server->smbd_conn->count_reassembly_queue,
- server->smbd_conn->count_enqueue_reassembly_queue,
- server->smbd_conn->count_dequeue_reassembly_queue,
- server->smbd_conn->fragment_reassembly_remaining,
- server->smbd_conn->reassembly_data_length,
- server->smbd_conn->reassembly_queue_length);
- seq_printf(m, "\nCurrent Credits send_credits: %x "
- "receive_credits: %x receive_credit_target: %x",
- atomic_read(&server->smbd_conn->send_credits),
- atomic_read(&server->smbd_conn->receive_credits),
- server->smbd_conn->receive_credit_target);
- seq_printf(m, "\nPending send_pending: %x ",
- atomic_read(&server->smbd_conn->send_pending));
- seq_printf(m, "\nReceive buffers count_receive_queue: %x "
- "count_empty_packet_queue: %x",
- server->smbd_conn->count_receive_queue,
- server->smbd_conn->count_empty_packet_queue);
- seq_printf(m, "\nMR responder_resources: %x "
- "max_frmr_depth: %x mr_type: %x",
- server->smbd_conn->responder_resources,
- server->smbd_conn->max_frmr_depth,
- server->smbd_conn->mr_type);
- seq_printf(m, "\nMR mr_ready_count: %x mr_used_count: %x",
- atomic_read(&server->smbd_conn->mr_ready_count),
- atomic_read(&server->smbd_conn->mr_used_count));
+ sc = &server->smbd_conn->socket;
+ sp = &sc->parameters;
+
+ seq_printf(m, "\nSMBDirect protocol version: 0x%x "
+ "transport status: %s (%u)",
+ SMBDIRECT_V1,
+ smbdirect_socket_status_string(sc->status),
+ sc->status);
+ seq_printf(m, "\nConn receive_credit_max: %u "
+ "send_credit_target: %u max_send_size: %u",
+ sp->recv_credit_max,
+ sp->send_credit_target,
+ sp->max_send_size);
+ seq_printf(m, "\nConn max_fragmented_recv_size: %u "
+ "max_fragmented_send_size: %u max_receive_size:%u",
+ sp->max_fragmented_recv_size,
+ sp->max_fragmented_send_size,
+ sp->max_recv_size);
+ seq_printf(m, "\nConn keep_alive_interval: %u "
+ "max_readwrite_size: %u rdma_readwrite_threshold: %u",
+ sp->keepalive_interval_msec * 1000,
+ sp->max_read_write_size,
+ server->rdma_readwrite_threshold);
+ seq_printf(m, "\nDebug count_get_receive_buffer: %llu "
+ "count_put_receive_buffer: %llu count_send_empty: %llu",
+ sc->statistics.get_receive_buffer,
+ sc->statistics.put_receive_buffer,
+ sc->statistics.send_empty);
+ seq_printf(m, "\nRead Queue "
+ "count_enqueue_reassembly_queue: %llu "
+ "count_dequeue_reassembly_queue: %llu "
+ "reassembly_data_length: %u "
+ "reassembly_queue_length: %u",
+ sc->statistics.enqueue_reassembly_queue,
+ sc->statistics.dequeue_reassembly_queue,
+ sc->recv_io.reassembly.data_length,
+ sc->recv_io.reassembly.queue_length);
+ seq_printf(m, "\nCurrent Credits send_credits: %u "
+ "receive_credits: %u receive_credit_target: %u",
+ atomic_read(&sc->send_io.credits.count),
+ atomic_read(&sc->recv_io.credits.count),
+ sc->recv_io.credits.target);
+ seq_printf(m, "\nPending send_pending: %u ",
+ atomic_read(&sc->send_io.pending.count));
+ seq_printf(m, "\nMR responder_resources: %u "
+ "max_frmr_depth: %u mr_type: 0x%x",
+ sp->responder_resources,
+ sp->max_frmr_depth,
+ sc->mr_io.type);
+ seq_printf(m, "\nMR mr_ready_count: %u mr_used_count: %u",
+ atomic_read(&sc->mr_io.ready.count),
+ atomic_read(&sc->mr_io.used.count));
skip_rdma:
#endif
seq_printf(m, "\nNumber of credits: %d,%d,%d Dialect 0x%x",
@@ -487,6 +594,11 @@ skip_rdma:
else
seq_puts(m, "disabled (not supported by this server)");
+ /* Show negotiated encryption cipher, even if not required */
+ seq_puts(m, "\nEncryption: ");
+ if (server->cipher_type)
+ seq_printf(m, "Negotiated cipher (%s)", cipher_alg_str(server->cipher_type));
+
seq_printf(m, "\n\n\tSessions: ");
i = 0;
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
@@ -524,12 +636,8 @@ skip_rdma:
/* dump session id helpful for use with network trace */
seq_printf(m, " SessionId: 0x%llx", ses->Suid);
- if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) {
+ if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
seq_puts(m, " encrypted");
- /* can help in debugging to show encryption type */
- if (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)
- seq_puts(m, "(gcm256)");
- }
if (ses->sign)
seq_puts(m, " signed");
@@ -618,7 +726,7 @@ skip_rdma:
seq_printf(m, "\n\tServer ConnectionId: 0x%llx",
chan_server->conn_id);
- spin_lock(&chan_server->mid_lock);
+ spin_lock(&chan_server->mid_queue_lock);
list_for_each_entry(mid_entry, &chan_server->pending_mid_q, qhead) {
seq_printf(m, "\n\t\tState: %d com: %d pid: %d cbdata: %p mid %llu",
mid_entry->mid_state,
@@ -627,7 +735,7 @@ skip_rdma:
mid_entry->callback_data,
mid_entry->mid);
}
- spin_unlock(&chan_server->mid_lock);
+ spin_unlock(&chan_server->mid_queue_lock);
}
spin_unlock(&ses->chan_lock);
seq_puts(m, "\n--\n");
@@ -858,6 +966,9 @@ cifs_proc_init(void)
proc_create_single("open_files", 0400, proc_fs_cifs,
cifs_debug_files_proc_show);
+ proc_create_single("open_dirs", 0400, proc_fs_cifs,
+ cifs_debug_dirs_proc_show);
+
proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_ops);
proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_ops);
proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_ops);
@@ -902,6 +1013,7 @@ cifs_proc_clean(void)
remove_proc_entry("DebugData", proc_fs_cifs);
remove_proc_entry("open_files", proc_fs_cifs);
+ remove_proc_entry("open_dirs", proc_fs_cifs);
remove_proc_entry("cifsFYI", proc_fs_cifs);
remove_proc_entry("traceSMB", proc_fs_cifs);
remove_proc_entry("Stats", proc_fs_cifs);
@@ -1100,7 +1212,7 @@ static ssize_t cifs_security_flags_proc_write(struct file *file,
if ((count < 1) || (count > 11))
return -EINVAL;
- memset(flags_string, 0, 12);
+ memset(flags_string, 0, sizeof(flags_string));
if (copy_from_user(flags_string, buffer, count))
return -EFAULT;
@@ -1206,11 +1318,11 @@ static const struct proc_ops cifs_mount_params_proc_ops = {
};
#else
-inline void cifs_proc_init(void)
+void cifs_proc_init(void)
{
}
-inline void cifs_proc_clean(void)
+void cifs_proc_clean(void)
{
}
#endif /* PROC_FS */
diff --git a/fs/smb/client/cifs_debug.h b/fs/smb/client/cifs_debug.h
index ce5cfd236fdb..e0035ff42dba 100644
--- a/fs/smb/client/cifs_debug.h
+++ b/fs/smb/client/cifs_debug.h
@@ -15,10 +15,10 @@
#define pr_fmt(fmt) "CIFS: " fmt
void cifs_dump_mem(char *label, void *data, int length);
-void cifs_dump_detail(void *buf, struct TCP_Server_Info *ptcp_info);
-void cifs_dump_mids(struct TCP_Server_Info *);
+void cifs_dump_detail(void *buf, size_t buf_len, struct TCP_Server_Info *server);
+void cifs_dump_mids(struct TCP_Server_Info *server);
extern bool traceSMB; /* flag which enables the function below */
-void dump_smb(void *, int);
+void dump_smb(void *buf, int smb_buf_length);
#define CIFS_INFO 0x01
#define CIFS_RC 0x02
#define CIFS_TIMER 0x04
diff --git a/fs/smb/client/cifs_fs_sb.h b/fs/smb/client/cifs_fs_sb.h
index 651759192280..5e8d163cb5f8 100644
--- a/fs/smb/client/cifs_fs_sb.h
+++ b/fs/smb/client/cifs_fs_sb.h
@@ -49,6 +49,7 @@
struct cifs_sb_info {
struct rb_root tlink_tree;
+ struct list_head tcon_sb_link;
spinlock_t tlink_tree_lock;
struct tcon_link *master_tlink;
struct nls_table *local_nls;
diff --git a/fs/smb/client/cifs_ioctl.h b/fs/smb/client/cifs_ioctl.h
index 26327442e383..b51ce64fcccf 100644
--- a/fs/smb/client/cifs_ioctl.h
+++ b/fs/smb/client/cifs_ioctl.h
@@ -61,7 +61,7 @@ struct smb_query_info {
struct smb3_key_debug_info {
__u64 Suid;
__u16 cipher_type;
- __u8 auth_key[16]; /* SMB2_NTLMV2_SESSKEY_SIZE */
+ __u8 auth_key[SMB2_NTLMV2_SESSKEY_SIZE];
__u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
} __packed;
diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c
index 28f568b5fc27..3a41bbada04c 100644
--- a/fs/smb/client/cifs_spnego.c
+++ b/fs/smb/client/cifs_spnego.c
@@ -24,20 +24,14 @@ static const struct cred *spnego_cred;
static int
cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
- char *payload;
- int ret;
+ char *payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
- ret = -ENOMEM;
- payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
if (!payload)
- goto error;
+ return -ENOMEM;
/* attach the data */
key->payload.data[0] = payload;
- ret = 0;
-
-error:
- return ret;
+ return 0;
}
static void
@@ -96,7 +90,6 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo,
size_t desc_len;
struct key *spnego_key;
const char *hostname = server->hostname;
- const struct cred *saved_cred;
/* length of fields (with semicolons): ver=0xyz ip4=ipaddress
host=hostname sec=mechanism uid=0xFF user=username */
@@ -124,58 +117,49 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo,
dp = description;
/* start with version and hostname portion of UNC string */
spnego_key = ERR_PTR(-EINVAL);
- sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
- hostname);
- dp = description + strlen(description);
+ dp += sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
+ hostname);
/* add the server address */
if (server->dstaddr.ss_family == AF_INET)
- sprintf(dp, "ip4=%pI4", &sa->sin_addr);
+ dp += sprintf(dp, "ip4=%pI4", &sa->sin_addr);
else if (server->dstaddr.ss_family == AF_INET6)
- sprintf(dp, "ip6=%pI6", &sa6->sin6_addr);
+ dp += sprintf(dp, "ip6=%pI6", &sa6->sin6_addr);
else
goto out;
- dp = description + strlen(description);
-
- /* for now, only sec=krb5 and sec=mskrb5 are valid */
+ /* for now, only sec=krb5 and sec=mskrb5 and iakerb are valid */
if (server->sec_kerberos)
- sprintf(dp, ";sec=krb5");
+ dp += sprintf(dp, ";sec=krb5");
else if (server->sec_mskerberos)
- sprintf(dp, ";sec=mskrb5");
+ dp += sprintf(dp, ";sec=mskrb5");
+ else if (server->sec_iakerb)
+ dp += sprintf(dp, ";sec=iakerb");
else {
cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n");
- sprintf(dp, ";sec=krb5");
+ dp += sprintf(dp, ";sec=krb5");
}
- dp = description + strlen(description);
- sprintf(dp, ";uid=0x%x",
- from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
+ dp += sprintf(dp, ";uid=0x%x",
+ from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
- dp = description + strlen(description);
- sprintf(dp, ";creduid=0x%x",
+ dp += sprintf(dp, ";creduid=0x%x",
from_kuid_munged(&init_user_ns, sesInfo->cred_uid));
- if (sesInfo->user_name) {
- dp = description + strlen(description);
- sprintf(dp, ";user=%s", sesInfo->user_name);
- }
+ if (sesInfo->user_name)
+ dp += sprintf(dp, ";user=%s", sesInfo->user_name);
- dp = description + strlen(description);
- sprintf(dp, ";pid=0x%x", current->pid);
+ dp += sprintf(dp, ";pid=0x%x", current->pid);
- if (sesInfo->upcall_target == UPTARGET_MOUNT) {
- dp = description + strlen(description);
- sprintf(dp, ";upcall_target=mount");
- } else {
- dp = description + strlen(description);
- sprintf(dp, ";upcall_target=app");
- }
+ if (sesInfo->upcall_target == UPTARGET_MOUNT)
+ dp += sprintf(dp, ";upcall_target=mount");
+ else
+ dp += sprintf(dp, ";upcall_target=app");
cifs_dbg(FYI, "key description = %s\n", description);
- saved_cred = override_creds(spnego_cred);
- spnego_key = request_key(&cifs_spnego_key_type, description, "");
- revert_creds(saved_cred);
+ scoped_with_creds(spnego_cred)
+ spnego_key = request_key(&cifs_spnego_key_type, description, "");
+ trace_smb3_kerberos_auth(server, sesInfo, PTR_ERR_OR_ZERO(spnego_key));
#ifdef CONFIG_CIFS_DEBUG2
if (cifsFYI && !IS_ERR(spnego_key)) {
diff --git a/fs/smb/client/cifs_spnego.h b/fs/smb/client/cifs_spnego.h
index e4d751b0c812..e70929db3611 100644
--- a/fs/smb/client/cifs_spnego.h
+++ b/fs/smb/client/cifs_spnego.h
@@ -27,10 +27,8 @@ struct cifs_spnego_msg {
uint8_t data[];
};
-#ifdef __KERNEL__
extern struct key_type cifs_spnego_key_type;
extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo,
struct TCP_Server_Info *server);
-#endif /* KERNEL */
#endif /* _CIFS_SPNEGO_H */
diff --git a/fs/smb/client/cifs_swn.c b/fs/smb/client/cifs_swn.c
index 7233c6a7e6d7..68a1f87c446d 100644
--- a/fs/smb/client/cifs_swn.c
+++ b/fs/smb/client/cifs_swn.c
@@ -82,10 +82,8 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
int ret;
skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (skb == NULL) {
- ret = -ENOMEM;
- goto fail;
- }
+ if (!skb)
+ return -ENOMEM;
hdr = genlmsg_put(skb, 0, 0, &cifs_genl_family, 0, CIFS_GENL_CMD_SWN_REGISTER);
if (hdr == NULL) {
@@ -172,7 +170,6 @@ static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg)
nlmsg_fail:
genlmsg_cancel(skb, hdr);
nlmsg_free(skb);
-fail:
return ret;
}
@@ -313,17 +310,15 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
reg = cifs_find_swn_reg(tcon);
if (!IS_ERR(reg)) {
kref_get(&reg->ref_count);
- mutex_unlock(&cifs_swnreg_idr_mutex);
- return reg;
+ goto unlock;
} else if (PTR_ERR(reg) != -EEXIST) {
- mutex_unlock(&cifs_swnreg_idr_mutex);
- return reg;
+ goto unlock;
}
reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC);
if (reg == NULL) {
- mutex_unlock(&cifs_swnreg_idr_mutex);
- return ERR_PTR(-ENOMEM);
+ ret = -ENOMEM;
+ goto fail_unlock;
}
kref_init(&reg->ref_count);
@@ -354,7 +349,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT);
reg->tcon = tcon;
-
+unlock:
mutex_unlock(&cifs_swnreg_idr_mutex);
return reg;
@@ -365,6 +360,7 @@ fail_idr:
idr_remove(&cifs_swnreg_idr, reg->id);
fail:
kfree(reg);
+fail_unlock:
mutex_unlock(&cifs_swnreg_idr_mutex);
return ERR_PTR(ret);
}
diff --git a/fs/smb/client/cifs_unicode.c b/fs/smb/client/cifs_unicode.c
index 4cc6e0896fad..f8659d36793f 100644
--- a/fs/smb/client/cifs_unicode.c
+++ b/fs/smb/client/cifs_unicode.c
@@ -629,6 +629,9 @@ cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len,
int len;
__le16 *dst;
+ if (!src)
+ return NULL;
+
len = cifs_local_to_utf16_bytes(src, maxlen, cp);
len += 2; /* NULL */
dst = kmalloc(len, GFP_KERNEL);
diff --git a/fs/smb/client/cifs_unicode.h b/fs/smb/client/cifs_unicode.h
index e137a0dfbbe9..6e4b99786498 100644
--- a/fs/smb/client/cifs_unicode.h
+++ b/fs/smb/client/cifs_unicode.h
@@ -54,7 +54,6 @@
#define SFM_MAP_UNI_RSVD 1
#define SFU_MAP_UNI_RSVD 2
-#ifdef __KERNEL__
int cifs_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
const struct nls_table *cp, int map_type);
int cifs_utf16_bytes(const __le16 *from, int maxbytes,
@@ -69,8 +68,6 @@ extern int cifs_remap(struct cifs_sb_info *cifs_sb);
extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen,
int *utf16_len, const struct nls_table *cp,
int remap);
-#endif
-
wchar_t cifs_toupper(wchar_t in);
#endif /* _CIFS_UNICODE_H */
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
index ba79aa2107cc..7e6e473bd4a0 100644
--- a/fs/smb/client/cifsacl.c
+++ b/fs/smb/client/cifsacl.c
@@ -300,7 +300,7 @@ id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid)
__func__, sidtype == SIDOWNER ? 'u' : 'g', cid);
goto out_revert_creds;
} else if (sidkey->datalen < CIFS_SID_BASE_SIZE) {
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_malformed_sid_key, sidkey->datalen);
cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n",
__func__, sidkey->datalen);
goto invalidate_key;
@@ -317,7 +317,8 @@ id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid)
ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32));
if (ksid_size > sidkey->datalen) {
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_malformed_ksid_key,
+ ksid_size, sidkey->datalen);
cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu, ksid_size=%u)\n",
__func__, sidkey->datalen, ksid_size);
goto invalidate_key;
@@ -339,7 +340,6 @@ int
sid_to_id(struct cifs_sb_info *cifs_sb, struct smb_sid *psid,
struct cifs_fattr *fattr, uint sidtype)
{
- int rc = 0;
struct key *sidkey;
char *sidstr;
const struct cred *saved_cred;
@@ -353,7 +353,8 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct smb_sid *psid,
if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) {
cifs_dbg(FYI, "%s: %u subauthorities is too many!\n",
__func__, psid->num_subauth);
- return -EIO;
+ return smb_EIO2(smb_eio_trace_sid_too_many_auth,
+ psid->num_subauth, SID_MAX_SUB_AUTHORITIES);
}
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) ||
@@ -446,12 +447,12 @@ out_revert_creds:
* fails then we just fall back to using the ctx->linux_uid/linux_gid.
*/
got_valid_id:
- rc = 0;
if (sidtype == SIDOWNER)
fattr->cf_uid = fuid;
else
fattr->cf_gid = fgid;
- return rc;
+
+ return 0;
}
int
@@ -763,7 +764,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
struct cifs_fattr *fattr, bool mode_from_special_sid)
{
int i;
- int num_aces = 0;
+ u16 num_aces = 0;
int acl_size;
char *acl_base;
struct smb_ace **ppace;
@@ -778,14 +779,15 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
}
/* validate that we do not go past end of acl */
- if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
+ if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) ||
+ end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
return;
}
cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n",
le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
- le32_to_cpu(pdacl->num_aces));
+ le16_to_cpu(pdacl->num_aces));
/* reset rwx permissions for user/group/other.
Also, if num_aces is 0 i.e. DACL has no ACEs,
@@ -795,19 +797,38 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
acl_base = (char *)pdacl;
acl_size = sizeof(struct smb_acl);
- num_aces = le32_to_cpu(pdacl->num_aces);
+ num_aces = le16_to_cpu(pdacl->num_aces);
if (num_aces > 0) {
umode_t denied_mode = 0;
- if (num_aces > ULONG_MAX / sizeof(struct smb_ace *))
+ if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) /
+ (offsetof(struct smb_ace, sid) +
+ offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
return;
+
ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *),
GFP_KERNEL);
if (!ppace)
return;
for (i = 0; i < num_aces; ++i) {
+ if (end_of_acl - acl_base < acl_size)
+ break;
+
ppace[i] = (struct smb_ace *) (acl_base + acl_size);
+ acl_base = (char *)ppace[i];
+ acl_size = offsetof(struct smb_ace, sid) +
+ offsetof(struct smb_sid, sub_auth);
+
+ if (end_of_acl - acl_base < acl_size ||
+ ppace[i]->sid.num_subauth == 0 ||
+ ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
+ (end_of_acl - acl_base <
+ acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) ||
+ (le16_to_cpu(ppace[i]->size) <
+ acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth))
+ break;
+
#ifdef CONFIG_CIFS_DEBUG2
dump_ace(ppace[i], end_of_acl);
#endif
@@ -851,7 +872,6 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
(void *)ppace[i],
sizeof(struct smb_ace)); */
- acl_base = (char *)ppace[i];
acl_size = le16_to_cpu(ppace[i]->size);
}
@@ -937,12 +957,12 @@ unsigned int setup_special_user_owner_ACE(struct smb_ace *pntace)
static void populate_new_aces(char *nacl_base,
struct smb_sid *pownersid,
struct smb_sid *pgrpsid,
- __u64 *pnmode, u32 *pnum_aces, u16 *pnsize,
+ __u64 *pnmode, u16 *pnum_aces, u16 *pnsize,
bool modefromsid,
bool posix)
{
__u64 nmode;
- u32 num_aces = 0;
+ u16 num_aces = 0;
u16 nsize = 0;
__u64 user_mode;
__u64 group_mode;
@@ -1050,7 +1070,7 @@ static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *p
u16 size = 0;
struct smb_ace *pntace = NULL;
char *acl_base = NULL;
- u32 src_num_aces = 0;
+ u16 src_num_aces = 0;
u16 nsize = 0;
struct smb_ace *pnntace = NULL;
char *nacl_base = NULL;
@@ -1058,7 +1078,7 @@ static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *p
acl_base = (char *)pdacl;
size = sizeof(struct smb_acl);
- src_num_aces = le32_to_cpu(pdacl->num_aces);
+ src_num_aces = le16_to_cpu(pdacl->num_aces);
nacl_base = (char *)pndacl;
nsize = sizeof(struct smb_acl);
@@ -1090,11 +1110,11 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
u16 size = 0;
struct smb_ace *pntace = NULL;
char *acl_base = NULL;
- u32 src_num_aces = 0;
+ u16 src_num_aces = 0;
u16 nsize = 0;
struct smb_ace *pnntace = NULL;
char *nacl_base = NULL;
- u32 num_aces = 0;
+ u16 num_aces = 0;
bool new_aces_set = false;
/* Assuming that pndacl and pnmode are never NULL */
@@ -1112,7 +1132,7 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
acl_base = (char *)pdacl;
size = sizeof(struct smb_acl);
- src_num_aces = le32_to_cpu(pdacl->num_aces);
+ src_num_aces = le16_to_cpu(pdacl->num_aces);
/* Retain old ACEs which we can retain */
for (i = 0; i < src_num_aces; ++i) {
@@ -1158,7 +1178,7 @@ next_ace:
}
finalize_dacl:
- pndacl->num_aces = cpu_to_le32(num_aces);
+ pndacl->num_aces = cpu_to_le16(num_aces);
pndacl->size = cpu_to_le16(nsize);
return 0;
@@ -1209,7 +1229,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
__u32 dacloffset;
if (pntsd == NULL)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
le32_to_cpu(pntsd->osidoffset));
@@ -1293,7 +1313,7 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION);
ndacl_ptr->size = cpu_to_le16(0);
- ndacl_ptr->num_aces = cpu_to_le32(0);
+ ndacl_ptr->num_aces = cpu_to_le16(0);
rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr,
pnmode, mode_from_sid, posix);
@@ -1395,7 +1415,7 @@ chown_chgrp_exit:
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
struct smb_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
const struct cifs_fid *cifsfid, u32 *pacllen,
- u32 __maybe_unused unused)
+ u32 info)
{
struct smb_ntsd *pntsd = NULL;
unsigned int xid;
@@ -1407,7 +1427,7 @@ struct smb_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
xid = get_xid();
rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), cifsfid->netfid, &pntsd,
- pacllen);
+ pacllen, info);
free_xid(xid);
cifs_put_tlink(tlink);
@@ -1419,7 +1439,7 @@ struct smb_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
}
static struct smb_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
- const char *path, u32 *pacllen)
+ const char *path, u32 *pacllen, u32 info)
{
struct smb_ntsd *pntsd = NULL;
int oplock = 0;
@@ -1446,9 +1466,12 @@ static struct smb_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
.fid = &fid,
};
+ if (info & SACL_SECINFO)
+ oparms.desired_access |= SYSTEM_SECURITY;
+
rc = CIFS_open(xid, &oparms, &oplock, NULL);
if (!rc) {
- rc = CIFSSMBGetCIFSACL(xid, tcon, fid.netfid, &pntsd, pacllen);
+ rc = CIFSSMBGetCIFSACL(xid, tcon, fid.netfid, &pntsd, pacllen, info);
CIFSSMBClose(xid, tcon, fid.netfid);
}
@@ -1472,7 +1495,7 @@ struct smb_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
if (inode)
open_file = find_readable_file(CIFS_I(inode), true);
if (!open_file)
- return get_cifs_acl_by_path(cifs_sb, path, pacllen);
+ return get_cifs_acl_by_path(cifs_sb, path, pacllen, info);
pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info);
cifsFileInfo_put(open_file);
@@ -1485,7 +1508,7 @@ int set_cifs_acl(struct smb_ntsd *pnntsd, __u32 acllen,
{
int oplock = 0;
unsigned int xid;
- int rc, access_flags;
+ int rc, access_flags = 0;
struct cifs_tcon *tcon;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
@@ -1498,10 +1521,12 @@ int set_cifs_acl(struct smb_ntsd *pnntsd, __u32 acllen,
tcon = tlink_tcon(tlink);
xid = get_xid();
- if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP)
- access_flags = WRITE_OWNER;
- else
- access_flags = WRITE_DAC;
+ if (aclflag & CIFS_ACL_OWNER || aclflag & CIFS_ACL_GROUP)
+ access_flags |= WRITE_OWNER;
+ if (aclflag & CIFS_ACL_SACL)
+ access_flags |= SYSTEM_SECURITY;
+ if (aclflag & CIFS_ACL_DACL)
+ access_flags |= WRITE_DAC;
oparms = (struct cifs_open_parms) {
.tcon = tcon,
@@ -1541,7 +1566,7 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
int rc = 0;
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
struct smb_version_operations *ops;
- const u32 info = 0;
+ const u32 info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO;
cifs_dbg(NOISY, "converting ACL to mode for %s\n", path);
@@ -1595,7 +1620,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
struct tcon_link *tlink;
struct smb_version_operations *ops;
bool mode_from_sid, id_from_sid;
- const u32 info = 0;
+ const u32 info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO;
bool posix;
tlink = cifs_sb_tlink(cifs_sb);
@@ -1648,7 +1673,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
if (mode_from_sid)
nsecdesclen +=
- le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
+ le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
else /* cifsacl */
nsecdesclen += le16_to_cpu(dacl_ptr->size);
}
diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c
index 7a43daacc815..ca2a84e8673e 100644
--- a/fs/smb/client/cifsencrypt.c
+++ b/fs/smb/client/cifsencrypt.c
@@ -22,16 +22,45 @@
#include <linux/highmem.h>
#include <linux/fips.h>
#include <linux/iov_iter.h>
-#include "../common/arc4.h"
#include <crypto/aead.h>
+#include <crypto/arc4.h>
+#include <crypto/md5.h>
+#include <crypto/sha2.h>
-static size_t cifs_shash_step(void *iter_base, size_t progress, size_t len,
- void *priv, void *priv2)
+static int cifs_sig_update(struct cifs_calc_sig_ctx *ctx,
+ const u8 *data, size_t len)
{
- struct shash_desc *shash = priv;
+ if (ctx->md5) {
+ md5_update(ctx->md5, data, len);
+ return 0;
+ }
+ if (ctx->hmac) {
+ hmac_sha256_update(ctx->hmac, data, len);
+ return 0;
+ }
+ return crypto_shash_update(ctx->shash, data, len);
+}
+
+static int cifs_sig_final(struct cifs_calc_sig_ctx *ctx, u8 *out)
+{
+ if (ctx->md5) {
+ md5_final(ctx->md5, out);
+ return 0;
+ }
+ if (ctx->hmac) {
+ hmac_sha256_final(ctx->hmac, out);
+ return 0;
+ }
+ return crypto_shash_final(ctx->shash, out);
+}
+
+static size_t cifs_sig_step(void *iter_base, size_t progress, size_t len,
+ void *priv, void *priv2)
+{
+ struct cifs_calc_sig_ctx *ctx = priv;
int ret, *pret = priv2;
- ret = crypto_shash_update(shash, iter_base, len);
+ ret = cifs_sig_update(ctx, iter_base, len);
if (ret < 0) {
*pret = ret;
return len;
@@ -42,60 +71,45 @@ static size_t cifs_shash_step(void *iter_base, size_t progress, size_t len,
/*
* Pass the data from an iterator into a hash.
*/
-static int cifs_shash_iter(const struct iov_iter *iter, size_t maxsize,
- struct shash_desc *shash)
+static int cifs_sig_iter(const struct iov_iter *iter, size_t maxsize,
+ struct cifs_calc_sig_ctx *ctx)
{
struct iov_iter tmp_iter = *iter;
- int err = -EIO;
+ size_t did;
+ int err;
- if (iterate_and_advance_kernel(&tmp_iter, maxsize, shash, &err,
- cifs_shash_step) != maxsize)
- return err;
+ did = iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, &err,
+ cifs_sig_step);
+ if (did != maxsize)
+ return smb_EIO2(smb_eio_trace_sig_iter, did, maxsize);
return 0;
}
-int __cifs_calc_signature(struct smb_rqst *rqst,
- struct TCP_Server_Info *server, char *signature,
- struct shash_desc *shash)
+int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
+ char *signature, struct cifs_calc_sig_ctx *ctx)
{
- int i;
+ struct iov_iter iter;
ssize_t rc;
- struct kvec *iov = rqst->rq_iov;
- int n_vec = rqst->rq_nvec;
-
- /* iov[0] is actual data and not the rfc1002 length for SMB2+ */
- if (!is_smb1(server)) {
- if (iov[0].iov_len <= 4)
- return -EIO;
- i = 0;
- } else {
- if (n_vec < 2 || iov[0].iov_len != 4)
- return -EIO;
- i = 1; /* skip rfc1002 length */
- }
+ size_t size = 0;
- for (; i < n_vec; i++) {
- if (iov[i].iov_len == 0)
- continue;
- if (iov[i].iov_base == NULL) {
- cifs_dbg(VFS, "null iovec entry\n");
- return -EIO;
- }
+ for (int i = 0; i < rqst->rq_nvec; i++)
+ size += rqst->rq_iov[i].iov_len;
- rc = crypto_shash_update(shash,
- iov[i].iov_base, iov[i].iov_len);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not update with payload\n",
- __func__);
- return rc;
- }
- }
+ iov_iter_kvec(&iter, ITER_SOURCE, rqst->rq_iov, rqst->rq_nvec, size);
- rc = cifs_shash_iter(&rqst->rq_iter, iov_iter_count(&rqst->rq_iter), shash);
+ if (iov_iter_count(&iter) <= 4)
+ return smb_EIO2(smb_eio_trace_sig_data_too_small,
+ iov_iter_count(&iter), 4);
+
+ rc = cifs_sig_iter(&iter, iov_iter_count(&iter), ctx);
if (rc < 0)
return rc;
- rc = crypto_shash_final(shash, signature);
+ rc = cifs_sig_iter(&rqst->rq_iter, iov_iter_count(&rqst->rq_iter), ctx);
+ if (rc < 0)
+ return rc;
+
+ rc = cifs_sig_final(ctx, signature);
if (rc)
cifs_dbg(VFS, "%s: Could not generate hash\n", __func__);
@@ -112,29 +126,22 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
static int cifs_calc_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server, char *signature)
{
- int rc;
+ struct md5_ctx ctx;
if (!rqst->rq_iov || !signature || !server)
return -EINVAL;
-
- rc = cifs_alloc_hash("md5", &server->secmech.md5);
- if (rc)
- return -1;
-
- rc = crypto_shash_init(server->secmech.md5);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not init md5\n", __func__);
- return rc;
+ if (fips_enabled) {
+ cifs_dbg(VFS,
+ "MD5 signature support is disabled due to FIPS\n");
+ return -EOPNOTSUPP;
}
- rc = crypto_shash_update(server->secmech.md5,
- server->session_key.response, server->session_key.len);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
- return rc;
- }
+ md5_init(&ctx);
+ md5_update(&ctx, server->session_key.response, server->session_key.len);
- return __cifs_calc_signature(rqst, server, signature, server->secmech.md5);
+ return __cifs_calc_signature(
+ rqst, server, signature,
+ &(struct cifs_calc_sig_ctx){ .md5 = &ctx });
}
/* must be called with server->srv_mutex held */
@@ -145,10 +152,6 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
char smb_signature[20];
struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
- if (rqst->rq_iov[0].iov_len != 4 ||
- rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
- return -EIO;
-
if ((cifs_pdu == NULL) || (server == NULL))
return -EINVAL;
@@ -181,30 +184,6 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
return rc;
}
-int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
- __u32 *pexpected_response_sequence)
-{
- struct smb_rqst rqst = { .rq_iov = iov,
- .rq_nvec = n_vec };
-
- return cifs_sign_rqst(&rqst, server, pexpected_response_sequence);
-}
-
-/* must be called with server->srv_mutex held */
-int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
- __u32 *pexpected_response_sequence_number)
-{
- struct kvec iov[2];
-
- iov[0].iov_base = cifs_pdu;
- iov[0].iov_len = 4;
- iov[1].iov_base = (char *)cifs_pdu + 4;
- iov[1].iov_len = be32_to_cpu(cifs_pdu->smb_buf_length);
-
- return cifs_sign_smbv(iov, 2, server,
- pexpected_response_sequence_number);
-}
-
int cifs_verify_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server,
__u32 expected_sequence_number)
@@ -214,10 +193,6 @@ int cifs_verify_signature(struct smb_rqst *rqst,
char what_we_think_sig_should_be[20];
struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
- if (rqst->rq_iov[0].iov_len != 4 ||
- rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
- return -EIO;
-
if (cifs_pdu == NULL || server == NULL)
return -EINVAL;
@@ -315,59 +290,72 @@ build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp)
return 0;
}
-/* Server has provided av pairs/target info in the type 2 challenge
- * packet and we have plucked it and stored within smb session.
- * We parse that blob here to find netbios domain name to be used
- * as part of ntlmv2 authentication (in Target String), if not already
- * specified on the command line.
- * If this function returns without any error but without fetching
- * domain name, authentication may fail against some server but
- * may not fail against other (those who are not very particular
- * about target string i.e. for some, just user name might suffice.
+#define AV_TYPE(av) (le16_to_cpu(av->type))
+#define AV_LEN(av) (le16_to_cpu(av->length))
+#define AV_DATA_PTR(av) ((void *)av->data)
+
+#define av_for_each_entry(ses, av) \
+ for (av = NULL; (av = find_next_av(ses, av));)
+
+static struct ntlmssp2_name *find_next_av(struct cifs_ses *ses,
+ struct ntlmssp2_name *av)
+{
+ u16 len;
+ u8 *end;
+
+ end = (u8 *)ses->auth_key.response + ses->auth_key.len;
+ if (!av) {
+ if (unlikely(!ses->auth_key.response || !ses->auth_key.len))
+ return NULL;
+ av = (void *)ses->auth_key.response;
+ } else {
+ av = (void *)((u8 *)av + sizeof(*av) + AV_LEN(av));
+ }
+
+ if ((u8 *)av + sizeof(*av) > end)
+ return NULL;
+
+ len = AV_LEN(av);
+ if (AV_TYPE(av) == NTLMSSP_AV_EOL)
+ return NULL;
+ if ((u8 *)av + sizeof(*av) + len > end)
+ return NULL;
+ return av;
+}
+
+/*
+ * Check if server has provided av pair of @type in the NTLMSSP
+ * CHALLENGE_MESSAGE blob.
*/
-static int
-find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp)
+static int find_av_name(struct cifs_ses *ses, u16 type, char **name, u16 maxlen)
{
- unsigned int attrsize;
- unsigned int type;
- unsigned int onesize = sizeof(struct ntlmssp2_name);
- unsigned char *blobptr;
- unsigned char *blobend;
- struct ntlmssp2_name *attrptr;
+ const struct nls_table *nlsc = ses->local_nls;
+ struct ntlmssp2_name *av;
+ u16 len, nlen;
- if (!ses->auth_key.len || !ses->auth_key.response)
+ if (*name)
return 0;
- blobptr = ses->auth_key.response;
- blobend = blobptr + ses->auth_key.len;
-
- while (blobptr + onesize < blobend) {
- attrptr = (struct ntlmssp2_name *) blobptr;
- type = le16_to_cpu(attrptr->type);
- if (type == NTLMSSP_AV_EOL)
- break;
- blobptr += 2; /* advance attr type */
- attrsize = le16_to_cpu(attrptr->length);
- blobptr += 2; /* advance attr size */
- if (blobptr + attrsize > blobend)
+ av_for_each_entry(ses, av) {
+ len = AV_LEN(av);
+ if (AV_TYPE(av) != type || !len)
+ continue;
+ if (!IS_ALIGNED(len, sizeof(__le16))) {
+ cifs_dbg(VFS | ONCE, "%s: bad length(%u) for type %u\n",
+ __func__, len, type);
+ continue;
+ }
+ nlen = len / sizeof(__le16);
+ if (nlen <= maxlen) {
+ ++nlen;
+ *name = kmalloc(nlen, GFP_KERNEL);
+ if (!*name)
+ return -ENOMEM;
+ cifs_from_utf16(*name, AV_DATA_PTR(av), nlen,
+ len, nlsc, NO_MAP_UNI_RSVD);
break;
- if (type == NTLMSSP_AV_NB_DOMAIN_NAME) {
- if (!attrsize || attrsize >= CIFS_MAX_DOMAINNAME_LEN)
- break;
- if (!ses->domainName) {
- ses->domainName =
- kmalloc(attrsize + 1, GFP_KERNEL);
- if (!ses->domainName)
- return -ENOMEM;
- cifs_from_utf16(ses->domainName,
- (__le16 *)blobptr, attrsize, attrsize,
- nls_cp, NO_MAP_UNI_RSVD);
- break;
- }
}
- blobptr += attrsize; /* advance attr value */
}
-
return 0;
}
@@ -377,50 +365,26 @@ find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp)
* as part of ntlmv2 authentication (or local current time as
* default in case of failure)
*/
-static __le64
-find_timestamp(struct cifs_ses *ses)
+static __le64 find_timestamp(struct cifs_ses *ses)
{
- unsigned int attrsize;
- unsigned int type;
- unsigned int onesize = sizeof(struct ntlmssp2_name);
- unsigned char *blobptr;
- unsigned char *blobend;
- struct ntlmssp2_name *attrptr;
+ struct ntlmssp2_name *av;
struct timespec64 ts;
- if (!ses->auth_key.len || !ses->auth_key.response)
- return 0;
-
- blobptr = ses->auth_key.response;
- blobend = blobptr + ses->auth_key.len;
-
- while (blobptr + onesize < blobend) {
- attrptr = (struct ntlmssp2_name *) blobptr;
- type = le16_to_cpu(attrptr->type);
- if (type == NTLMSSP_AV_EOL)
- break;
- blobptr += 2; /* advance attr type */
- attrsize = le16_to_cpu(attrptr->length);
- blobptr += 2; /* advance attr size */
- if (blobptr + attrsize > blobend)
- break;
- if (type == NTLMSSP_AV_TIMESTAMP) {
- if (attrsize == sizeof(u64))
- return *((__le64 *)blobptr);
- }
- blobptr += attrsize; /* advance attr value */
+ av_for_each_entry(ses, av) {
+ if (AV_TYPE(av) == NTLMSSP_AV_TIMESTAMP &&
+ AV_LEN(av) == sizeof(u64))
+ return *((__le64 *)AV_DATA_PTR(av));
}
-
ktime_get_real_ts64(&ts);
return cpu_to_le64(cifs_UnixTimeToNT(ts));
}
static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
- const struct nls_table *nls_cp, struct shash_desc *hmacmd5)
+ const struct nls_table *nls_cp)
{
- int rc = 0;
int len;
char nt_hash[CIFS_NTHASH_SIZE];
+ struct hmac_md5_ctx hmac_ctx;
__le16 *user;
wchar_t *domain;
wchar_t *server;
@@ -428,17 +392,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
/* calculate md4 hash of password */
E_md4hash(ses->password, nt_hash, nls_cp);
- rc = crypto_shash_setkey(hmacmd5->tfm, nt_hash, CIFS_NTHASH_SIZE);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not set NT hash as a key, rc=%d\n", __func__, rc);
- return rc;
- }
-
- rc = crypto_shash_init(hmacmd5);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not init HMAC-MD5, rc=%d\n", __func__, rc);
- return rc;
- }
+ hmac_md5_init_usingrawkey(&hmac_ctx, nt_hash, CIFS_NTHASH_SIZE);
/* convert ses->user_name to unicode */
len = ses->user_name ? strlen(ses->user_name) : 0;
@@ -453,12 +407,8 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
*(u16 *)user = 0;
}
- rc = crypto_shash_update(hmacmd5, (char *)user, 2 * len);
+ hmac_md5_update(&hmac_ctx, (const u8 *)user, 2 * len);
kfree(user);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not update with user, rc=%d\n", __func__, rc);
- return rc;
- }
/* convert ses->domainName to unicode and uppercase */
if (ses->domainName) {
@@ -470,12 +420,8 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len,
nls_cp);
- rc = crypto_shash_update(hmacmd5, (char *)domain, 2 * len);
+ hmac_md5_update(&hmac_ctx, (const u8 *)domain, 2 * len);
kfree(domain);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not update with domain, rc=%d\n", __func__, rc);
- return rc;
- }
} else {
/* We use ses->ip_addr if no domain name available */
len = strlen(ses->ip_addr);
@@ -485,25 +431,16 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
return -ENOMEM;
len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len, nls_cp);
- rc = crypto_shash_update(hmacmd5, (char *)server, 2 * len);
+ hmac_md5_update(&hmac_ctx, (const u8 *)server, 2 * len);
kfree(server);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not update with server, rc=%d\n", __func__, rc);
- return rc;
- }
}
- rc = crypto_shash_final(hmacmd5, ntlmv2_hash);
- if (rc)
- cifs_dbg(VFS, "%s: Could not generate MD5 hash, rc=%d\n", __func__, rc);
-
- return rc;
+ hmac_md5_final(&hmac_ctx, ntlmv2_hash);
+ return 0;
}
-static int
-CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash, struct shash_desc *hmacmd5)
+static void CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
{
- int rc;
struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *)
(ses->auth_key.response + CIFS_SESS_KEY_SIZE);
unsigned int hash_len;
@@ -512,48 +449,77 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash, struct shash_
hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE +
offsetof(struct ntlmv2_resp, challenge.key[0]));
- rc = crypto_shash_setkey(hmacmd5->tfm, ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not set NTLMv2 hash as a key, rc=%d\n", __func__, rc);
- return rc;
- }
-
- rc = crypto_shash_init(hmacmd5);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not init HMAC-MD5, rc=%d\n", __func__, rc);
- return rc;
- }
-
if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED)
memcpy(ntlmv2->challenge.key, ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
else
memcpy(ntlmv2->challenge.key, ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
- rc = crypto_shash_update(hmacmd5, ntlmv2->challenge.key, hash_len);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not update with response, rc=%d\n", __func__, rc);
- return rc;
+ /* Note that the HMAC-MD5 value overwrites ntlmv2->challenge.key */
+ hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
+ ntlmv2->challenge.key, hash_len,
+ ntlmv2->ntlmv2_hash);
+}
+
+/*
+ * Set up NTLMv2 response blob with SPN (cifs/<hostname>) appended to the
+ * existing list of AV pairs.
+ */
+static int set_auth_key_response(struct cifs_ses *ses)
+{
+ size_t baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp);
+ size_t len, spnlen, tilen = 0, num_avs = 2 /* SPN + EOL */;
+ struct TCP_Server_Info *server = ses->server;
+ char *spn __free(kfree) = NULL;
+ struct ntlmssp2_name *av;
+ char *rsp = NULL;
+ int rc;
+
+ spnlen = strlen(server->hostname);
+ len = sizeof("cifs/") + spnlen;
+ spn = kmalloc(len, GFP_KERNEL);
+ if (!spn) {
+ rc = -ENOMEM;
+ goto out;
}
- /* Note that the MD5 digest over writes anon.challenge_key.key */
- rc = crypto_shash_final(hmacmd5, ntlmv2->ntlmv2_hash);
- if (rc)
- cifs_dbg(VFS, "%s: Could not generate MD5 hash, rc=%d\n", __func__, rc);
+ spnlen = scnprintf(spn, len, "cifs/%.*s",
+ (int)spnlen, server->hostname);
+
+ av_for_each_entry(ses, av)
+ tilen += sizeof(*av) + AV_LEN(av);
+
+ len = baselen + tilen + spnlen * sizeof(__le16) + num_avs * sizeof(*av);
+ rsp = kmalloc(len, GFP_KERNEL);
+ if (!rsp) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memcpy(rsp + baselen, ses->auth_key.response, tilen);
+ av = (void *)(rsp + baselen + tilen);
+ av->type = cpu_to_le16(NTLMSSP_AV_TARGET_NAME);
+ av->length = cpu_to_le16(spnlen * sizeof(__le16));
+ cifs_strtoUTF16((__le16 *)av->data, spn, spnlen, ses->local_nls);
+ av = (void *)((__u8 *)av + sizeof(*av) + AV_LEN(av));
+ av->type = cpu_to_le16(NTLMSSP_AV_EOL);
+ av->length = 0;
+
+ rc = 0;
+ ses->auth_key.len = len;
+out:
+ ses->auth_key.response = rsp;
return rc;
}
int
setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
{
- struct shash_desc *hmacmd5 = NULL;
- int rc;
- int baselen;
- unsigned int tilen;
+ unsigned char *tiblob = NULL; /* target info blob */
struct ntlmv2_resp *ntlmv2;
char ntlmv2_hash[16];
- unsigned char *tiblob = NULL; /* target info blob */
__le64 rsp_timestamp;
+ __u64 cc;
+ int rc;
if (nls_cp == NULL) {
cifs_dbg(VFS, "%s called with nls_cp==NULL\n", __func__);
@@ -563,16 +529,29 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) {
if (!ses->domainName) {
if (ses->domainAuto) {
- rc = find_domain_name(ses, nls_cp);
- if (rc) {
- cifs_dbg(VFS, "error %d finding domain name\n",
- rc);
+ /*
+ * Domain (workgroup) hasn't been specified in
+ * mount options, so try to find it in
+ * CHALLENGE_MESSAGE message and then use it as
+ * part of NTLMv2 authentication.
+ */
+ rc = find_av_name(ses, NTLMSSP_AV_NB_DOMAIN_NAME,
+ &ses->domainName,
+ CIFS_MAX_DOMAINNAME_LEN);
+ if (rc)
goto setup_ntlmv2_rsp_ret;
- }
} else {
ses->domainName = kstrdup("", GFP_KERNEL);
+ if (!ses->domainName) {
+ rc = -ENOMEM;
+ goto setup_ntlmv2_rsp_ret;
+ }
}
}
+ rc = find_av_name(ses, NTLMSSP_AV_DNS_DOMAIN_NAME,
+ &ses->dns_dom, CIFS_MAX_DOMAINNAME_LEN);
+ if (rc)
+ goto setup_ntlmv2_rsp_ret;
} else {
rc = build_avpair_blob(ses, nls_cp);
if (rc) {
@@ -586,77 +565,48 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
* (as Windows 7 does)
*/
rsp_timestamp = find_timestamp(ses);
+ get_random_bytes(&cc, sizeof(cc));
- baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp);
- tilen = ses->auth_key.len;
- tiblob = ses->auth_key.response;
+ cifs_server_lock(ses->server);
- ses->auth_key.response = kmalloc(baselen + tilen, GFP_KERNEL);
- if (!ses->auth_key.response) {
- rc = -ENOMEM;
+ tiblob = ses->auth_key.response;
+ rc = set_auth_key_response(ses);
+ if (rc) {
ses->auth_key.len = 0;
- goto setup_ntlmv2_rsp_ret;
+ goto unlock;
}
- ses->auth_key.len += baselen;
ntlmv2 = (struct ntlmv2_resp *)
(ses->auth_key.response + CIFS_SESS_KEY_SIZE);
ntlmv2->blob_signature = cpu_to_le32(0x00000101);
ntlmv2->reserved = 0;
ntlmv2->time = rsp_timestamp;
-
- get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal));
+ ntlmv2->client_chal = cc;
ntlmv2->reserved2 = 0;
- memcpy(ses->auth_key.response + baselen, tiblob, tilen);
-
- cifs_server_lock(ses->server);
-
- rc = cifs_alloc_hash("hmac(md5)", &hmacmd5);
- if (rc) {
- cifs_dbg(VFS, "Could not allocate HMAC-MD5, rc=%d\n", rc);
+ if (fips_enabled) {
+ cifs_dbg(VFS, "NTLMv2 support is disabled due to FIPS\n");
+ rc = -EOPNOTSUPP;
goto unlock;
}
/* calculate ntlmv2_hash */
- rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp, hmacmd5);
+ rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp);
if (rc) {
cifs_dbg(VFS, "Could not get NTLMv2 hash, rc=%d\n", rc);
goto unlock;
}
/* calculate first part of the client response (CR1) */
- rc = CalcNTLMv2_response(ses, ntlmv2_hash, hmacmd5);
- if (rc) {
- cifs_dbg(VFS, "Could not calculate CR1, rc=%d\n", rc);
- goto unlock;
- }
+ CalcNTLMv2_response(ses, ntlmv2_hash);
/* now calculate the session key for NTLMv2 */
- rc = crypto_shash_setkey(hmacmd5->tfm, ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not set NTLMv2 hash as a key, rc=%d\n", __func__, rc);
- goto unlock;
- }
-
- rc = crypto_shash_init(hmacmd5);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not init HMAC-MD5, rc=%d\n", __func__, rc);
- goto unlock;
- }
-
- rc = crypto_shash_update(hmacmd5, ntlmv2->ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not update with response, rc=%d\n", __func__, rc);
- goto unlock;
- }
-
- rc = crypto_shash_final(hmacmd5, ses->auth_key.response);
- if (rc)
- cifs_dbg(VFS, "%s: Could not generate MD5 hash, rc=%d\n", __func__, rc);
+ hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
+ ntlmv2->ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
+ ses->auth_key.response);
+ rc = 0;
unlock:
cifs_server_unlock(ses->server);
- cifs_free_hash(&hmacmd5);
setup_ntlmv2_rsp_ret:
kfree_sensitive(tiblob);
@@ -680,9 +630,9 @@ calc_seckey(struct cifs_ses *ses)
return -ENOMEM;
}
- cifs_arc4_setkey(ctx_arc4, ses->auth_key.response, CIFS_SESS_KEY_SIZE);
- cifs_arc4_crypt(ctx_arc4, ses->ntlmssp->ciphertext, sec_key,
- CIFS_CPHTXT_SIZE);
+ arc4_setkey(ctx_arc4, ses->auth_key.response, CIFS_SESS_KEY_SIZE);
+ arc4_crypt(ctx_arc4, ses->ntlmssp->ciphertext, sec_key,
+ CIFS_CPHTXT_SIZE);
/* make secondary_key/nonce as session key */
memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE);
@@ -698,22 +648,13 @@ void
cifs_crypto_secmech_release(struct TCP_Server_Info *server)
{
cifs_free_hash(&server->secmech.aes_cmac);
- cifs_free_hash(&server->secmech.hmacsha256);
- cifs_free_hash(&server->secmech.md5);
- cifs_free_hash(&server->secmech.sha512);
-
- if (!SERVER_IS_CHAN(server)) {
- if (server->secmech.enc) {
- crypto_free_aead(server->secmech.enc);
- server->secmech.enc = NULL;
- }
- if (server->secmech.dec) {
- crypto_free_aead(server->secmech.dec);
- server->secmech.dec = NULL;
- }
- } else {
+ if (server->secmech.enc) {
+ crypto_free_aead(server->secmech.enc);
server->secmech.enc = NULL;
+ }
+ if (server->secmech.dec) {
+ crypto_free_aead(server->secmech.dec);
server->secmech.dec = NULL;
}
}
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 979853471027..d9664634144d 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -28,6 +28,8 @@
#include <linux/splice.h>
#include <linux/uuid.h>
#include <linux/xattr.h>
+#include <linux/mm.h>
+#include <linux/key-type.h>
#include <uapi/linux/magic.h>
#include <net/ipv6.h>
#include "cifsfs.h"
@@ -35,10 +37,9 @@
#define DECLARE_GLOBALS_HERE
#include "cifsglob.h"
#include "cifsproto.h"
+#include "smb2proto.h"
#include "cifs_debug.h"
#include "cifs_fs_sb.h"
-#include <linux/mm.h>
-#include <linux/key-type.h>
#include "cifs_spnego.h"
#include "fscache.h"
#ifdef CONFIG_CIFS_DFS_UPCALL
@@ -70,7 +71,6 @@ bool require_gcm_256; /* false by default */
bool enable_negotiate_signing; /* false by default */
unsigned int global_secflags = CIFSSEC_DEF;
/* unsigned int ntlmv2_support = 0; */
-unsigned int sign_CIFS_PDUs = 1;
/*
* Global transaction id (XID) information
@@ -78,7 +78,7 @@ unsigned int sign_CIFS_PDUs = 1;
unsigned int GlobalCurrentXid; /* protected by GlobalMid_Lock */
unsigned int GlobalTotalActiveXid; /* prot by GlobalMid_Lock */
unsigned int GlobalMaxActiveXid; /* prot by GlobalMid_Lock */
-spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ entries */
+DEFINE_SPINLOCK(GlobalMid_Lock); /* protects above & list operations on midQ entries */
/*
* Global counters, updated atomically
@@ -98,7 +98,7 @@ atomic_t total_buf_alloc_count;
atomic_t total_small_buf_alloc_count;
#endif/* STATS2 */
struct list_head cifs_tcp_ses_list;
-spinlock_t cifs_tcp_ses_lock;
+DEFINE_SPINLOCK(cifs_tcp_ses_lock);
static const struct super_operations cifs_super_ops;
unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
module_param(CIFSMaxBufSize, uint, 0444);
@@ -122,6 +122,46 @@ unsigned int dir_cache_timeout = 30;
module_param(dir_cache_timeout, uint, 0644);
MODULE_PARM_DESC(dir_cache_timeout, "Number of seconds to cache directory contents for which we have a lease. Default: 30 "
"Range: 1 to 65000 seconds, 0 to disable caching dir contents");
+/* Module-wide total cached dirents (in bytes) across all tcons */
+atomic64_t cifs_dircache_bytes_used = ATOMIC64_INIT(0);
+
+/*
+ * Write-only module parameter to drop all cached directory entries across
+ * all CIFS mounts. Echo a non-zero value to trigger.
+ */
+static void cifs_drop_all_dir_caches(void)
+{
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ if (cifs_ses_exiting(ses))
+ continue;
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list)
+ invalidate_all_cached_dirs(tcon);
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+}
+
+static int cifs_param_set_drop_dir_cache(const char *val, const struct kernel_param *kp)
+{
+ bool bv;
+ int rc = kstrtobool(val, &bv);
+
+ if (rc)
+ return rc;
+ if (bv)
+ cifs_drop_all_dir_caches();
+ return 0;
+}
+
+module_param_call(drop_dir_cache, cifs_param_set_drop_dir_cache, NULL, NULL, 0200);
+MODULE_PARM_DESC(drop_dir_cache, "Write 1 to drop all cached directory entries across all CIFS mounts");
+
#ifdef CONFIG_CIFS_STATS2
unsigned int slow_rsp_threshold = 1;
module_param(slow_rsp_threshold, uint, 0644);
@@ -134,7 +174,7 @@ module_param(enable_oplocks, bool, 0644);
MODULE_PARM_DESC(enable_oplocks, "Enable or disable oplocks. Default: y/Y/1");
module_param(enable_gcm_256, bool, 0644);
-MODULE_PARM_DESC(enable_gcm_256, "Enable requesting strongest (256 bit) GCM encryption. Default: y/Y/0");
+MODULE_PARM_DESC(enable_gcm_256, "Enable requesting strongest (256 bit) GCM encryption. Default: y/Y/1");
module_param(require_gcm_256, bool, 0644);
MODULE_PARM_DESC(require_gcm_256, "Require strongest (256 bit) GCM encryption. Default: n/N/0");
@@ -157,6 +197,7 @@ struct workqueue_struct *fileinfo_put_wq;
struct workqueue_struct *cifsoplockd_wq;
struct workqueue_struct *deferredclose_wq;
struct workqueue_struct *serverclose_wq;
+struct workqueue_struct *cfid_put_wq;
__u32 cifs_lock_secret;
/*
@@ -260,9 +301,9 @@ cifs_read_super(struct super_block *sb)
}
if (tcon->nocase)
- sb->s_d_op = &cifs_ci_dentry_ops;
+ set_default_d_op(sb, &cifs_ci_dentry_ops);
else
- sb->s_d_op = &cifs_dentry_ops;
+ set_default_d_op(sb, &cifs_dentry_ops);
sb->s_root = d_make_root(inode);
if (!sb->s_root) {
@@ -352,11 +393,27 @@ static long cifs_fallocate(struct file *file, int mode, loff_t off, loff_t len)
struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
struct TCP_Server_Info *server = tcon->ses->server;
+ struct inode *inode = file_inode(file);
+ int rc;
+
+ if (!server->ops->fallocate)
+ return -EOPNOTSUPP;
+
+ rc = inode_lock_killable(inode);
+ if (rc)
+ return rc;
+
+ netfs_wait_for_outstanding_io(inode);
+
+ rc = file_modified(file);
+ if (rc)
+ goto out_unlock;
- if (server->ops->fallocate)
- return server->ops->fallocate(file, tcon, mode, off, len);
+ rc = server->ops->fallocate(file, tcon, mode, off, len);
- return -EOPNOTSUPP;
+out_unlock:
+ inode_unlock(inode);
+ return rc;
}
static int cifs_permission(struct mnt_idmap *idmap,
@@ -386,7 +443,7 @@ static struct kmem_cache *cifs_io_request_cachep;
static struct kmem_cache *cifs_io_subrequest_cachep;
mempool_t *cifs_sm_req_poolp;
mempool_t *cifs_req_poolp;
-mempool_t *cifs_mid_poolp;
+mempool_t cifs_mid_pool;
mempool_t cifs_io_request_pool;
mempool_t cifs_io_subrequest_pool;
@@ -397,7 +454,7 @@ cifs_alloc_inode(struct super_block *sb)
cifs_inode = alloc_inode_sb(sb, cifs_inode_cachep, GFP_KERNEL);
if (!cifs_inode)
return NULL;
- cifs_inode->cifsAttrs = 0x20; /* default */
+ cifs_inode->cifsAttrs = ATTR_ARCHIVE; /* default */
cifs_inode->time = 0;
/*
* Until the file is open and we have gotten oplock info back from the
@@ -444,7 +501,7 @@ cifs_evict_inode(struct inode *inode)
{
netfs_wait_for_outstanding_io(inode);
truncate_inode_pages_final(&inode->i_data);
- if (inode->i_state & I_PINNING_NETFS_WB)
+ if (inode_state_read_once(inode) & I_PINNING_NETFS_WB)
cifs_fscache_unuse_inode_cookie(inode, true);
cifs_fscache_release_inode_cookie(inode);
clear_inode(inode);
@@ -636,6 +693,10 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
cifs_sb->ctx->dir_mode);
if (cifs_sb->ctx->iocharset)
seq_printf(s, ",iocharset=%s", cifs_sb->ctx->iocharset);
+ if (tcon->ses->unicode == 0)
+ seq_puts(s, ",nounicode");
+ else if (tcon->ses->unicode == 1)
+ seq_puts(s, ",unicode");
if (tcon->seal)
seq_puts(s, ",seal");
else if (tcon->ses->server->ignore_signature)
@@ -714,6 +775,12 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
cifs_sb->ctx->backupgid));
seq_show_option(s, "reparse",
cifs_reparse_type_str(cifs_sb->ctx->reparse_type));
+ if (cifs_sb->ctx->nonativesocket)
+ seq_puts(s, ",nonativesocket");
+ else
+ seq_puts(s, ",nativesocket");
+ seq_show_option(s, "symlink",
+ cifs_symlink_type_str(cifs_symlink_type(cifs_sb)));
seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize);
seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize);
@@ -847,7 +914,7 @@ static int cifs_drop_inode(struct inode *inode)
/* no serverino => unconditional eviction */
return !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) ||
- generic_drop_inode(inode);
+ inode_generic_drop(inode);
}
static const struct super_operations cifs_super_ops = {
@@ -918,7 +985,8 @@ cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb)
while (*s && *s != sep)
s++;
- child = lookup_positive_unlocked(p, dentry, s - p);
+ child = lookup_noperm_positive_unlocked(&QSTR_LEN(p, s - p),
+ dentry);
dput(dentry);
dentry = child;
} while (!IS_ERR(dentry));
@@ -949,7 +1017,6 @@ cifs_smb3_do_mount(struct file_system_type *fs_type,
} else {
cifs_info("Attempting to mount %s\n", old_ctx->source);
}
-
cifs_sb = kzalloc(sizeof(*cifs_sb), GFP_KERNEL);
if (!cifs_sb)
return ERR_PTR(-ENOMEM);
@@ -1082,6 +1149,9 @@ cifs_setlease(struct file *file, int arg, struct file_lease **lease, void **priv
struct inode *inode = file_inode(file);
struct cifsFileInfo *cfile = file->private_data;
+ if (!S_ISREG(inode->i_mode))
+ return -EINVAL;
+
/* Check if file is oplocked if this is request for new lease */
if (arg == F_UNLCK ||
((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) ||
@@ -1347,6 +1417,20 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
truncate_setsize(target_inode, new_size);
fscache_resize_cookie(cifs_inode_cookie(target_inode),
new_size);
+ } else if (rc == -EOPNOTSUPP) {
+ /*
+ * copy_file_range syscall man page indicates EINVAL
+ * is returned e.g when "fd_in and fd_out refer to the
+ * same file and the source and target ranges overlap."
+ * Test generic/157 was what showed these cases where
+ * we need to remap EOPNOTSUPP to EINVAL
+ */
+ if (off >= src_inode->i_size) {
+ rc = -EINVAL;
+ } else if (src_inode == target_inode) {
+ if (off + len > destoff)
+ rc = -EINVAL;
+ }
}
if (rc == 0 && new_size > target_cifsi->netfs.zero_point)
target_cifsi->netfs.zero_point = new_size;
@@ -1514,7 +1598,7 @@ const struct file_operations cifs_file_ops = {
.flock = cifs_flock,
.fsync = cifs_fsync,
.flush = cifs_flush,
- .mmap = cifs_file_mmap,
+ .mmap_prepare = cifs_file_mmap_prepare,
.splice_read = filemap_splice_read,
.splice_write = iter_file_splice_write,
.llseek = cifs_llseek,
@@ -1534,7 +1618,7 @@ const struct file_operations cifs_file_strict_ops = {
.flock = cifs_flock,
.fsync = cifs_strict_fsync,
.flush = cifs_flush,
- .mmap = cifs_file_strict_mmap,
+ .mmap_prepare = cifs_file_strict_mmap_prepare,
.splice_read = filemap_splice_read,
.splice_write = iter_file_splice_write,
.llseek = cifs_llseek,
@@ -1554,7 +1638,7 @@ const struct file_operations cifs_file_direct_ops = {
.flock = cifs_flock,
.fsync = cifs_fsync,
.flush = cifs_flush,
- .mmap = cifs_file_mmap,
+ .mmap_prepare = cifs_file_mmap_prepare,
.splice_read = copy_splice_read,
.splice_write = iter_file_splice_write,
.unlocked_ioctl = cifs_ioctl,
@@ -1572,7 +1656,7 @@ const struct file_operations cifs_file_nobrl_ops = {
.release = cifs_close,
.fsync = cifs_fsync,
.flush = cifs_flush,
- .mmap = cifs_file_mmap,
+ .mmap_prepare = cifs_file_mmap_prepare,
.splice_read = filemap_splice_read,
.splice_write = iter_file_splice_write,
.llseek = cifs_llseek,
@@ -1590,7 +1674,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
.release = cifs_close,
.fsync = cifs_strict_fsync,
.flush = cifs_flush,
- .mmap = cifs_file_strict_mmap,
+ .mmap_prepare = cifs_file_strict_mmap_prepare,
.splice_read = filemap_splice_read,
.splice_write = iter_file_splice_write,
.llseek = cifs_llseek,
@@ -1608,7 +1692,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = {
.release = cifs_close,
.fsync = cifs_fsync,
.flush = cifs_flush,
- .mmap = cifs_file_mmap,
+ .mmap_prepare = cifs_file_mmap_prepare,
.splice_read = copy_splice_read,
.splice_write = iter_file_splice_write,
.unlocked_ioctl = cifs_ioctl,
@@ -1763,8 +1847,7 @@ static int init_mids(void)
return -ENOMEM;
/* 3 is a reasonable minimum number of simultaneous operations */
- cifs_mid_poolp = mempool_create_slab_pool(3, cifs_mid_cachep);
- if (cifs_mid_poolp == NULL) {
+ if (mempool_init_slab_pool(&cifs_mid_pool, 3, cifs_mid_cachep) < 0) {
kmem_cache_destroy(cifs_mid_cachep);
return -ENOMEM;
}
@@ -1774,7 +1857,7 @@ static int init_mids(void)
static void destroy_mids(void)
{
- mempool_destroy(cifs_mid_poolp);
+ mempool_exit(&cifs_mid_pool);
kmem_cache_destroy(cifs_mid_cachep);
}
@@ -1852,8 +1935,6 @@ init_cifs(void)
GlobalCurrentXid = 0;
GlobalTotalActiveXid = 0;
GlobalMaxActiveXid = 0;
- spin_lock_init(&cifs_tcp_ses_lock);
- spin_lock_init(&GlobalMid_Lock);
cifs_lock_secret = get_random_u32();
@@ -1872,7 +1953,9 @@ init_cifs(void)
cifs_dbg(VFS, "dir_cache_timeout set to max of 65000 seconds\n");
}
- cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
+ cifsiod_wq = alloc_workqueue("cifsiod",
+ WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
+ 0);
if (!cifsiod_wq) {
rc = -ENOMEM;
goto out_clean_proc;
@@ -1900,29 +1983,40 @@ init_cifs(void)
}
cifsoplockd_wq = alloc_workqueue("cifsoplockd",
- WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
+ WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
+ 0);
if (!cifsoplockd_wq) {
rc = -ENOMEM;
goto out_destroy_fileinfo_put_wq;
}
deferredclose_wq = alloc_workqueue("deferredclose",
- WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
+ WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
+ 0);
if (!deferredclose_wq) {
rc = -ENOMEM;
goto out_destroy_cifsoplockd_wq;
}
serverclose_wq = alloc_workqueue("serverclose",
- WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
+ WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
+ 0);
if (!serverclose_wq) {
rc = -ENOMEM;
goto out_destroy_deferredclose_wq;
}
+ cfid_put_wq = alloc_workqueue("cfid_put_wq",
+ WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
+ 0);
+ if (!cfid_put_wq) {
+ rc = -ENOMEM;
+ goto out_destroy_serverclose_wq;
+ }
+
rc = cifs_init_inodecache();
if (rc)
- goto out_destroy_serverclose_wq;
+ goto out_destroy_cfid_put_wq;
rc = cifs_init_netfs();
if (rc)
@@ -1990,6 +2084,8 @@ out_destroy_netfs:
cifs_destroy_netfs();
out_destroy_inodecache:
cifs_destroy_inodecache();
+out_destroy_cfid_put_wq:
+ destroy_workqueue(cfid_put_wq);
out_destroy_serverclose_wq:
destroy_workqueue(serverclose_wq);
out_destroy_deferredclose_wq:
@@ -2033,6 +2129,7 @@ exit_cifs(void)
destroy_workqueue(decrypt_wq);
destroy_workqueue(fileinfo_put_wq);
destroy_workqueue(serverclose_wq);
+ destroy_workqueue(cfid_put_wq);
destroy_workqueue(cifsiod_wq);
cifs_proc_clean();
}
@@ -2044,13 +2141,9 @@ MODULE_DESCRIPTION
"also older servers complying with the SNIA CIFS Specification)");
MODULE_VERSION(CIFS_VERSION);
MODULE_SOFTDEP("ecb");
-MODULE_SOFTDEP("hmac");
-MODULE_SOFTDEP("md5");
MODULE_SOFTDEP("nls");
MODULE_SOFTDEP("aes");
MODULE_SOFTDEP("cmac");
-MODULE_SOFTDEP("sha256");
-MODULE_SOFTDEP("sha512");
MODULE_SOFTDEP("aead2");
MODULE_SOFTDEP("ccm");
MODULE_SOFTDEP("gcm");
diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h
index 71b720dbb2ce..e9534258d1ef 100644
--- a/fs/smb/client/cifsfs.h
+++ b/fs/smb/client/cifsfs.h
@@ -59,8 +59,8 @@ extern int cifs_unlink(struct inode *dir, struct dentry *dentry);
extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *);
extern int cifs_mknod(struct mnt_idmap *, struct inode *, struct dentry *,
umode_t, dev_t);
-extern int cifs_mkdir(struct mnt_idmap *, struct inode *, struct dentry *,
- umode_t);
+extern struct dentry *cifs_mkdir(struct mnt_idmap *, struct inode *, struct dentry *,
+ umode_t);
extern int cifs_rmdir(struct inode *, struct dentry *);
extern int cifs_rename2(struct mnt_idmap *, struct inode *,
struct dentry *, struct inode *, struct dentry *,
@@ -103,8 +103,8 @@ extern int cifs_lock(struct file *, int, struct file_lock *);
extern int cifs_fsync(struct file *, loff_t, loff_t, int);
extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int);
extern int cifs_flush(struct file *, fl_owner_t id);
-extern int cifs_file_mmap(struct file *file, struct vm_area_struct *vma);
-extern int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma);
+int cifs_file_mmap_prepare(struct vm_area_desc *desc);
+int cifs_file_strict_mmap_prepare(struct vm_area_desc *desc);
extern const struct file_operations cifs_dir_ops;
extern int cifs_readdir(struct file *file, struct dir_context *ctx);
@@ -135,7 +135,6 @@ extern ssize_t cifs_file_copychunk_range(unsigned int xid,
extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern void cifs_setsize(struct inode *inode, loff_t offset);
-extern int cifs_truncate_page(struct address_space *mapping, loff_t from);
struct smb3_fs_context;
extern struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type,
@@ -146,6 +145,6 @@ extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */
/* when changing internal version - update following two lines at same time */
-#define SMB3_PRODUCT_BUILD 51
-#define CIFS_VERSION "2.51"
+#define SMB3_PRODUCT_BUILD 57
+#define CIFS_VERSION "2.57"
#endif /* _CIFSFS_H */
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index fc33dfe7e925..3eca5bfb7030 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -24,7 +24,9 @@
#include "cifsacl.h"
#include <crypto/internal/hash.h>
#include <uapi/linux/cifs/cifs_mount.h>
+#include "../common/smbglob.h"
#include "../common/smb2pdu.h"
+#include "../common/fscc.h"
#include "smb2pdu.h"
#include <linux/filelock.h>
@@ -87,7 +89,7 @@
#define SMB_INTERFACE_POLL_INTERVAL 600
/* maximum number of PDUs in one compound */
-#define MAX_COMPOUND 7
+#define MAX_COMPOUND 10
/*
* Default number of credits to keep available for SMB3.
@@ -151,6 +153,7 @@ enum securityEnum {
NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */
RawNTLMSSP, /* NTLMSSP without SPNEGO, NTLMv2 hash */
Kerberos, /* Kerberos via SPNEGO */
+ IAKerb, /* Kerberos proxy */
};
enum upcall_target_enum {
@@ -160,6 +163,7 @@ enum upcall_target_enum {
};
enum cifs_reparse_type {
+ CIFS_REPARSE_TYPE_NONE,
CIFS_REPARSE_TYPE_NFS,
CIFS_REPARSE_TYPE_WSL,
CIFS_REPARSE_TYPE_DEFAULT = CIFS_REPARSE_TYPE_NFS,
@@ -168,6 +172,8 @@ enum cifs_reparse_type {
static inline const char *cifs_reparse_type_str(enum cifs_reparse_type type)
{
switch (type) {
+ case CIFS_REPARSE_TYPE_NONE:
+ return "none";
case CIFS_REPARSE_TYPE_NFS:
return "nfs";
case CIFS_REPARSE_TYPE_WSL:
@@ -177,6 +183,39 @@ static inline const char *cifs_reparse_type_str(enum cifs_reparse_type type)
}
}
+enum cifs_symlink_type {
+ CIFS_SYMLINK_TYPE_DEFAULT,
+ CIFS_SYMLINK_TYPE_NONE,
+ CIFS_SYMLINK_TYPE_NATIVE,
+ CIFS_SYMLINK_TYPE_UNIX,
+ CIFS_SYMLINK_TYPE_MFSYMLINKS,
+ CIFS_SYMLINK_TYPE_SFU,
+ CIFS_SYMLINK_TYPE_NFS,
+ CIFS_SYMLINK_TYPE_WSL,
+};
+
+static inline const char *cifs_symlink_type_str(enum cifs_symlink_type type)
+{
+ switch (type) {
+ case CIFS_SYMLINK_TYPE_NONE:
+ return "none";
+ case CIFS_SYMLINK_TYPE_NATIVE:
+ return "native";
+ case CIFS_SYMLINK_TYPE_UNIX:
+ return "unix";
+ case CIFS_SYMLINK_TYPE_MFSYMLINKS:
+ return "mfsymlinks";
+ case CIFS_SYMLINK_TYPE_SFU:
+ return "sfu";
+ case CIFS_SYMLINK_TYPE_NFS:
+ return "nfs";
+ case CIFS_SYMLINK_TYPE_WSL:
+ return "wsl";
+ default:
+ return "unknown";
+ }
+}
+
struct session_key {
unsigned int len;
char *response;
@@ -184,9 +223,6 @@ struct session_key {
/* crypto hashing related structure/fields, not specific to a sec mech */
struct cifs_secmech {
- struct shash_desc *md5; /* md5 hash function, for CIFS/SMB1 signatures */
- struct shash_desc *hmacsha256; /* hmac-sha256 hash function, for SMB2 signatures */
- struct shash_desc *sha512; /* sha512 hash function, for SMB3.1.1 preauth hash */
struct shash_desc *aes_cmac; /* block-cipher based MAC function, for SMB3 signatures */
struct crypto_aead *enc; /* smb3 encryption AEAD TFM (AES-CCM and AES-GCM) */
@@ -215,10 +251,8 @@ struct cifs_cred {
struct cifs_open_info_data {
bool adjust_tz;
- union {
- bool reparse_point;
- bool symlink;
- };
+ bool reparse_point;
+ bool contains_posix_file_info;
struct {
/* ioctl response buffer */
struct {
@@ -226,10 +260,7 @@ struct cifs_open_info_data {
struct kvec iov;
} io;
__u32 tag;
- union {
- struct reparse_data_buffer *buf;
- struct reparse_posix_data *posix;
- };
+ struct reparse_data_buffer *buf;
} reparse;
struct {
__u8 eas[SMB2_WSL_MAX_QUERY_EA_RESP_SIZE];
@@ -280,8 +311,9 @@ struct cifs_open_parms;
struct cifs_credits;
struct smb_version_operations {
- int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *,
- struct mid_q_entry *);
+ int (*send_cancel)(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ struct smb_rqst *rqst, struct mid_q_entry *mid,
+ unsigned int xid);
bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
/* setup request: allocate mid, sign message */
struct mid_q_entry *(*setup_request)(struct cifs_ses *,
@@ -315,18 +347,19 @@ struct smb_version_operations {
/* map smb to linux error */
int (*map_error)(char *, bool);
/* find mid corresponding to the response message */
- struct mid_q_entry * (*find_mid)(struct TCP_Server_Info *, char *);
- void (*dump_detail)(void *buf, struct TCP_Server_Info *ptcp_info);
+ struct mid_q_entry *(*find_mid)(struct TCP_Server_Info *server, char *buf);
+ void (*dump_detail)(void *buf, size_t buf_len, struct TCP_Server_Info *ptcp_info);
void (*clear_stats)(struct cifs_tcon *);
void (*print_stats)(struct seq_file *m, struct cifs_tcon *);
void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *);
/* verify the message */
- int (*check_message)(char *, unsigned int, struct TCP_Server_Info *);
+ int (*check_message)(char *buf, unsigned int pdu_len, unsigned int len,
+ struct TCP_Server_Info *server);
bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
int (*handle_cancelled_mid)(struct mid_q_entry *, struct TCP_Server_Info *);
void (*downgrade_oplock)(struct TCP_Server_Info *server,
struct cifsInodeInfo *cinode, __u32 oplock,
- unsigned int epoch, bool *purge_cache);
+ __u16 epoch, bool *purge_cache);
/* process transaction2 response */
bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *,
char *, int);
@@ -504,8 +537,6 @@ struct smb_version_operations {
void (*new_lease_key)(struct cifs_fid *);
int (*generate_signingkey)(struct cifs_ses *ses,
struct TCP_Server_Info *server);
- int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *,
- bool allocate_crypto);
int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon,
struct cifsFileInfo *src_file);
int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
@@ -521,12 +552,12 @@ struct smb_version_operations {
/* if we can do cache read operations */
bool (*is_read_op)(__u32);
/* set oplock level for the inode */
- void (*set_oplock_level)(struct cifsInodeInfo *, __u32, unsigned int,
- bool *);
+ void (*set_oplock_level)(struct cifsInodeInfo *cinode, __u32 oplock, __u16 epoch,
+ bool *purge_cache);
/* create lease context buffer for CREATE request */
- char * (*create_lease_buf)(u8 *lease_key, u8 oplock);
+ char * (*create_lease_buf)(u8 *lease_key, u8 oplock, u8 *parent_lease_key, __le32 le_flags);
/* parse lease context buffer and return oplock/epoch info */
- __u8 (*parse_lease_buf)(void *buf, unsigned int *epoch, char *lkey);
+ __u8 (*parse_lease_buf)(void *buf, __u16 *epoch, char *lkey);
ssize_t (*copychunk_range)(const unsigned int,
struct cifsFileInfo *src_file,
struct cifsFileInfo *target_file,
@@ -593,42 +624,21 @@ struct smb_version_operations {
bool (*is_status_io_timeout)(char *buf);
/* Check for STATUS_NETWORK_NAME_DELETED */
bool (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv);
- int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb,
- struct kvec *rsp_iov,
- struct cifs_open_info_data *data);
- int (*create_reparse_symlink)(const unsigned int xid,
- struct inode *inode,
- struct dentry *dentry,
- struct cifs_tcon *tcon,
- const char *full_path,
- const char *symname);
-};
-
-struct smb_version_values {
- char *version_string;
- __u16 protocol_id;
- __u32 req_capabilities;
- __u32 large_lock_type;
- __u32 exclusive_lock_type;
- __u32 shared_lock_type;
- __u32 unlock_lock_type;
- size_t header_preamble_size;
- size_t header_size;
- size_t max_header_size;
- size_t read_rsp_size;
- __le16 lock_cmd;
- unsigned int cap_unix;
- unsigned int cap_nt_find;
- unsigned int cap_large_files;
- __u16 signing_enabled;
- __u16 signing_required;
- size_t create_lease_size;
+ struct reparse_data_buffer * (*get_reparse_point_buffer)(const struct kvec *rsp_iov,
+ u32 *plen);
+ struct inode * (*create_reparse_inode)(struct cifs_open_info_data *data,
+ struct super_block *sb,
+ const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const char *full_path,
+ bool directory,
+ struct kvec *reparse_iov,
+ struct kvec *xattr_iov);
};
#define HEADER_SIZE(server) (server->vals->header_size)
#define MAX_HEADER_SIZE(server) (server->vals->max_header_size)
-#define HEADER_PREAMBLE_SIZE(server) (server->vals->header_preamble_size)
-#define MID_HEADER_SIZE(server) (HEADER_SIZE(server) - 1 - HEADER_PREAMBLE_SIZE(server))
+#define MID_HEADER_SIZE(server) (HEADER_SIZE(server) - 1)
/**
* CIFS superblock mount flags (mnt_cifs_flags) to consider when
@@ -662,24 +672,15 @@ struct cifs_mnt_data {
int flags;
};
-static inline unsigned int
-get_rfc1002_length(void *buf)
-{
- return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
-}
-
-static inline void
-inc_rfc1001_len(void *buf, int count)
-{
- be32_add_cpu((__be32 *)buf, count);
-}
-
struct TCP_Server_Info {
struct list_head tcp_ses_list;
struct list_head smb_ses_list;
+ struct list_head rlist; /* reconnect list */
spinlock_t srv_lock; /* protect anything here that is not protected */
__u64 conn_id; /* connection identifier (useful for debugging) */
int srv_count; /* reference counter */
+ int rfc1001_sessinit; /* whether to estasblish netbios session */
+ bool with_rfc1001; /* if netbios session is used */
/* 15 character server name + 0x20 16th byte indicating type = srv */
char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
struct smb_version_operations *ops;
@@ -695,14 +696,15 @@ struct TCP_Server_Info {
#endif
wait_queue_head_t response_q;
wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
- spinlock_t mid_lock; /* protect mid queue and it's entries */
+ spinlock_t mid_queue_lock; /* protect mid queue */
+ spinlock_t mid_counter_lock;
struct list_head pending_mid_q;
bool noblocksnd; /* use blocking sendmsg */
bool noautotune; /* do not autotune send buf sizes */
bool nosharesock;
bool tcp_nodelay;
bool terminate;
- unsigned int credits; /* send no more requests at once */
+ int credits; /* send no more requests at once */
unsigned int max_credits; /* can override large 32000 default at mnt */
unsigned int in_flight; /* number of requests on the wire to server */
unsigned int max_in_flight; /* max number of requests that were on wire */
@@ -733,14 +735,17 @@ struct TCP_Server_Info {
/* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */
unsigned int capabilities; /* selective disabling of caps by smb sess */
int timeAdj; /* Adjust for difference in server time zone in sec */
- __u64 CurrentMid; /* multiplex id - rotating counter, protected by GlobalMid_Lock */
+ __u64 current_mid; /* multiplex id - rotating counter, protected by mid_counter_lock */
char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */
/* 16th byte of RFC1001 workstation name is always null */
char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
__u32 sequence_number; /* for signing, protected by srv_mutex */
__u32 reconnect_instance; /* incremented on each reconnect */
+ __le32 session_key_id; /* retrieved from negotiate response and send in session setup request */
struct session_key session_key;
unsigned long lstrp; /* when we got last response from this server */
+ unsigned long neg_start; /* when negotiate started (jiffies) */
+ unsigned long reconn_delay; /* when resched session and tcon reconnect */
struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */
#define CIFS_NEGFLAVOR_UNENCAP 1 /* wct == 17, but no ext_sec */
#define CIFS_NEGFLAVOR_EXTENDED 2 /* wct == 17, ext_sec bit set */
@@ -750,6 +755,7 @@ struct TCP_Server_Info {
bool sec_kerberosu2u; /* supports U2U Kerberos */
bool sec_kerberos; /* supports plain Kerberos */
bool sec_mskerberos; /* supports legacy MS Kerberos */
+ bool sec_iakerb; /* supports pass-through auth for Kerberos (krb5 proxy) */
bool large_buf; /* is current buffer large? */
/* use SMBD connection instead of socket */
bool rdma;
@@ -773,6 +779,13 @@ struct TCP_Server_Info {
unsigned int max_read;
unsigned int max_write;
unsigned int min_offload;
+ /*
+ * If payload is less than or equal to the threshold,
+ * use RDMA send/recv to send upper layer I/O.
+ * If payload is more than the threshold,
+ * use RDMA read/write through memory registration for I/O.
+ */
+ unsigned int rdma_readwrite_threshold;
unsigned int retrans;
struct {
bool requested; /* "compress" mount option set*/
@@ -810,28 +823,20 @@ struct TCP_Server_Info {
bool use_swn_dstaddr;
struct sockaddr_storage swn_dstaddr;
#endif
- struct mutex refpath_lock; /* protects leaf_fullpath */
/*
- * leaf_fullpath: Canonical DFS referral path related to this
- * connection.
- * It is used in DFS cache refresher, reconnect and may
- * change due to nested DFS links.
- *
- * Protected by @refpath_lock and @srv_lock. The @refpath_lock is
- * mostly used for not requiring a copy of @leaf_fullpath when getting
- * cached or new DFS referrals (which might also sleep during I/O).
- * While @srv_lock is held for making string and NULL comparisons against
- * both fields as in mount(2) and cache refresh.
+ * Canonical DFS referral path used in cifs_reconnect() for failover as
+ * well as in DFS cache refresher.
*
* format: \\HOST\SHARE[\OPTIONAL PATH]
*/
char *leaf_fullpath;
bool dfs_conn:1;
+ char dns_dom[CIFS_MAX_DOMAINNAME_LEN + 1];
};
-static inline bool is_smb1(struct TCP_Server_Info *server)
+static inline bool is_smb1(const struct TCP_Server_Info *server)
{
- return HEADER_PREAMBLE_SIZE(server) != 0;
+ return server->vals->protocol_id == SMB10_PROT_ID;
}
static inline void cifs_server_lock(struct TCP_Server_Info *server)
@@ -970,18 +975,16 @@ compare_mid(__u16 mid, const struct smb_hdr *smb)
* of kvecs to handle the receive, though that should only need to be done
* once.
*/
-#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4)
-#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4)
+#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ))
+#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP))
/*
* When the server doesn't allow large posix writes, only allow a rsize/wsize
* of 2^17-1 minus the size of the call header. That allows for a read or
* write up to the maximum size described by RFC1002.
*/
-#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4)
-#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4)
-
-#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
+#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ))
+#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP))
/*
* Windows only supports a max of 60kb reads and 65535 byte writes. Default to
@@ -1057,6 +1060,7 @@ struct cifs_chan {
};
#define CIFS_SES_FLAG_SCALE_CHANNELS (0x1)
+#define CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES (0x2)
/*
* Session structure. One of these for each uid session with a particular host
@@ -1094,6 +1098,7 @@ struct cifs_ses {
bool sign; /* is signing required? */
bool domainAuto:1;
bool expired_pwd; /* track if access denied or expired pwd so can know if need to update */
+ int unicode;
unsigned int flags;
__u16 session_flags;
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
@@ -1153,6 +1158,7 @@ struct cifs_ses {
/* ========= end: protected by chan_lock ======== */
struct cifs_ses *dfs_root_ses;
struct nls_table *local_nls;
+ char *dns_dom; /* FQDN of the domain */
};
static inline bool
@@ -1271,6 +1277,7 @@ struct cifs_tcon {
bool use_persistent:1; /* use persistent instead of durable handles */
bool no_lease:1; /* Do not request leases on files or directories */
bool use_witness:1; /* use witness protocol */
+ bool dummy:1; /* dummy tcon used for reconnecting channels */
__le32 capabilities;
__u32 share_flags;
__u32 maximal_access;
@@ -1292,7 +1299,8 @@ struct cifs_tcon {
#endif
struct list_head pending_opens; /* list of incomplete opens */
struct cached_fids *cfids;
- /* BB add field for back pointer to sb struct(s)? */
+ struct list_head cifs_sb_list;
+ spinlock_t sb_list_lock;
#ifdef CONFIG_CIFS_DFS_UPCALL
struct delayed_work dfs_cache_work;
struct list_head dfs_ses_list;
@@ -1411,6 +1419,7 @@ struct cifs_open_parms {
bool reconnect:1;
bool replay:1; /* indicates that this open is for a replay */
struct kvec *ea_cctx;
+ __le32 lease_flags;
};
struct cifs_fid {
@@ -1418,10 +1427,11 @@ struct cifs_fid {
__u64 persistent_fid; /* persist file id for smb2 */
__u64 volatile_fid; /* volatile file id for smb2 */
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */
+ __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
__u8 create_guid[16];
__u32 access;
struct cifs_pending_open *pending_open;
- unsigned int epoch;
+ __u16 epoch;
#ifdef CONFIG_CIFS_DEBUG2
__u64 mid;
#endif /* CIFS_DEBUG2 */
@@ -1454,7 +1464,7 @@ struct cifsFileInfo {
bool oplock_break_cancelled:1;
bool status_file_deleted:1; /* file has been deleted */
bool offload:1; /* offload final part of _put to a wq */
- unsigned int oplock_epoch; /* epoch from the lease break */
+ __u16 oplock_epoch; /* epoch from the lease break */
__u32 oplock_level; /* oplock/lease level from the lease break */
int count;
spinlock_t file_info_lock; /* protects four flag/count fields above */
@@ -1482,7 +1492,6 @@ struct cifs_io_parms {
struct cifs_io_request {
struct netfs_io_request rreq;
struct cifsFileInfo *cfile;
- struct TCP_Server_Info *server;
pid_t pid;
};
@@ -1501,7 +1510,7 @@ struct cifs_io_subrequest {
struct kvec iov[2];
struct TCP_Server_Info *server;
#ifdef CONFIG_CIFS_SMB_DIRECT
- struct smbd_mr *mr;
+ struct smbdirect_mr_io *mr;
#endif
struct cifs_credits credits;
};
@@ -1520,6 +1529,11 @@ struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file);
void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr,
bool offload);
void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
+int cifs_file_flush(const unsigned int xid, struct inode *inode,
+ struct cifsFileInfo *cfile);
+int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
+ const char *full_path, struct cifsFileInfo *open_file,
+ loff_t size);
#define CIFS_CACHE_READ_FLG 1
#define CIFS_CACHE_HANDLE_FLG 2
@@ -1551,7 +1565,7 @@ struct cifsInodeInfo {
spinlock_t open_file_lock; /* protects openFileList */
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
unsigned int oplock; /* oplock/lease level we have */
- unsigned int epoch; /* used to track lease state changes */
+ __u16 epoch; /* used to track lease state changes */
#define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */
#define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */
#define CIFS_INODE_FLAG_UNUSED (2) /* Unused flag */
@@ -1647,7 +1661,7 @@ static inline void cifs_stats_bytes_read(struct cifs_tcon *tcon,
* Returns zero on a successful receive, or an error. The receive state in
* the TCP_Server_Info will also be updated.
*/
-typedef int (mid_receive_t)(struct TCP_Server_Info *server,
+typedef int (*mid_receive_t)(struct TCP_Server_Info *server,
struct mid_q_entry *mid);
/*
@@ -1658,41 +1672,45 @@ typedef int (mid_receive_t)(struct TCP_Server_Info *server,
* - it will be called by cifsd, with no locks held
* - the mid will be removed from any lists
*/
-typedef void (mid_callback_t)(struct mid_q_entry *mid);
+typedef void (*mid_callback_t)(struct TCP_Server_Info *srv, struct mid_q_entry *mid);
/*
* This is the protopyte for mid handle function. This is called once the mid
* has been recognized after decryption of the message.
*/
-typedef int (mid_handle_t)(struct TCP_Server_Info *server,
+typedef int (*mid_handle_t)(struct TCP_Server_Info *server,
struct mid_q_entry *mid);
/* one of these for every pending CIFS request to the server */
struct mid_q_entry {
struct list_head qhead; /* mids waiting on reply from this server */
- struct kref refcount;
- struct TCP_Server_Info *server; /* server corresponding to this mid */
+ refcount_t refcount;
__u64 mid; /* multiplex id */
__u16 credits; /* number of credits consumed by this mid */
__u16 credits_received; /* number of credits from the response */
__u32 pid; /* process id */
__u32 sequence_number; /* for CIFS signing */
+ unsigned int sr_flags; /* Flags passed to send_recv() */
unsigned long when_alloc; /* when mid was created */
#ifdef CONFIG_CIFS_STATS2
unsigned long when_sent; /* time when smb send finished */
unsigned long when_received; /* when demux complete (taken off wire) */
#endif
- mid_receive_t *receive; /* call receive callback */
- mid_callback_t *callback; /* call completion callback */
- mid_handle_t *handle; /* call handle mid callback */
+ mid_receive_t receive; /* call receive callback */
+ mid_callback_t callback; /* call completion callback */
+ mid_handle_t handle; /* call handle mid callback */
void *callback_data; /* general purpose pointer for callback */
struct task_struct *creator;
void *resp_buf; /* pointer to received SMB header */
unsigned int resp_buf_size;
+ u32 response_pdu_len;
int mid_state; /* wish this were enum but can not pass to wait_event */
- unsigned int mid_flags;
+ int mid_rc; /* rc for MID_RC */
__le16 command; /* smb command code */
unsigned int optype; /* operation type */
+ spinlock_t mid_lock;
+ bool wait_cancelled:1; /* Cancelled while waiting for response */
+ bool deleted_from_q:1; /* Whether Mid has been dequeued frem pending_mid_q */
bool large_buf:1; /* if valid response, is pointer to large buf */
bool multiRsp:1; /* multiple trans2 responses for one request */
bool multiEnd:1; /* both received */
@@ -1840,9 +1858,12 @@ static inline bool is_replayable_error(int error)
/* cifs_get_writable_file() flags */
-#define FIND_WR_ANY 0
-#define FIND_WR_FSUID_ONLY 1
-#define FIND_WR_WITH_DELETE 2
+enum cifs_writable_file_flags {
+ FIND_WR_ANY = 0U,
+ FIND_WR_FSUID_ONLY = (1U << 0),
+ FIND_WR_WITH_DELETE = (1U << 1),
+ FIND_WR_NO_PENDING_DELETE = (1U << 2),
+};
#define MID_FREE 0
#define MID_REQUEST_ALLOCATED 1
@@ -1852,10 +1873,7 @@ static inline bool is_replayable_error(int error)
#define MID_RESPONSE_MALFORMED 0x10
#define MID_SHUTDOWN 0x20
#define MID_RESPONSE_READY 0x40 /* ready for other process handle the rsp */
-
-/* Flags */
-#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */
-#define MID_DELETED 2 /* Mid has been dequeued/deleted */
+#define MID_RC 0x80 /* mid_rc contains custom rc */
/* Types of response buffer returned from SendReceive2 */
#define CIFS_NO_BUFFER 0 /* Response buffer not returned */
@@ -1884,6 +1902,8 @@ static inline bool is_replayable_error(int error)
#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */
#define CIFS_NO_SRV_RSP 0x1000 /* there is no server response */
#define CIFS_COMPRESS_REQ 0x4000 /* compress request before sending */
+#define CIFS_INTERRUPTIBLE_WAIT 0x8000 /* Interruptible wait (e.g. lock request) */
+#define CIFS_WINDOWS_LOCK 0x10000 /* We're trying to get a Windows lock */
/* Security Flags: indicate type of session setup needed */
#define CIFSSEC_MAY_SIGN 0x00001
@@ -1957,8 +1977,7 @@ require use of the stronger protocol */
* TCP_Server_Info-> TCP_Server_Info cifs_get_tcp_session
* reconnect_mutex
* TCP_Server_Info->srv_mutex TCP_Server_Info cifs_get_tcp_session
- * cifs_ses->session_mutex cifs_ses sesInfoAlloc
- * cifs_tcon
+ * cifs_ses->session_mutex cifs_ses sesInfoAlloc
* cifs_tcon->open_file_lock cifs_tcon->openFileList tconInfoAlloc
* cifs_tcon->pending_opens
* cifs_tcon->stat_lock cifs_tcon->bytes_read tconInfoAlloc
@@ -1968,33 +1987,40 @@ require use of the stronger protocol */
* GlobalCurrentXid
* GlobalTotalActiveXid
* TCP_Server_Info->srv_lock (anything in struct not protected by another lock and can change)
- * TCP_Server_Info->mid_lock TCP_Server_Info->pending_mid_q cifs_get_tcp_session
- * ->CurrentMid
- * (any changes in mid_q_entry fields)
+ * TCP_Server_Info->mid_queue_lock TCP_Server_Info->pending_mid_q cifs_get_tcp_session
+ * mid_q_entry->deleted_from_q
+ * TCP_Server_Info->mid_counter_lock TCP_Server_Info->current_mid cifs_get_tcp_session
* TCP_Server_Info->req_lock TCP_Server_Info->in_flight cifs_get_tcp_session
* ->credits
* ->echo_credits
* ->oplock_credits
* ->reconnect_instance
* cifs_ses->ses_lock (anything that is not protected by another lock and can change)
+ * sesInfoAlloc
* cifs_ses->iface_lock cifs_ses->iface_list sesInfoAlloc
* ->iface_count
* ->iface_last_update
- * cifs_ses->chan_lock cifs_ses->chans
+ * cifs_ses->chan_lock cifs_ses->chans sesInfoAlloc
* ->chans_need_reconnect
* ->chans_in_reconnect
* cifs_tcon->tc_lock (anything that is not protected by another lock and can change)
+ * tcon_info_alloc
* inode->i_rwsem, taken by fs/netfs/locking.c e.g. should be taken before cifsInodeInfo locks
* cifsInodeInfo->open_file_lock cifsInodeInfo->openFileList cifs_alloc_inode
* cifsInodeInfo->writers_lock cifsInodeInfo->writers cifsInodeInfo_alloc
* cifsInodeInfo->lock_sem cifsInodeInfo->llist cifs_init_once
* ->can_cache_brlcks
* cifsInodeInfo->deferred_lock cifsInodeInfo->deferred_closes cifsInodeInfo_alloc
- * cached_fid->fid_mutex cifs_tcon->crfid tcon_info_alloc
- * cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo
+ * cached_fids->cfid_list_lock cifs_tcon->cfids->entries init_cached_dirs
+ * cached_fid->fid_lock (anything that is not protected by another lock and can change)
+ * init_cached_dir
+ * cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo
* cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo
* ->invalidHandle initiate_cifs_search
* ->oplock_break_cancelled
+ * mid_q_entry->mid_lock mid_q_entry->callback alloc_mid
+ * smb2_mid_entry_alloc
+ * (Any fields of mid_q_entry that will need protection)
****************************************************************************/
#ifdef DECLARE_GLOBALS_HERE
@@ -2078,39 +2104,30 @@ extern struct workqueue_struct *fileinfo_put_wq;
extern struct workqueue_struct *cifsoplockd_wq;
extern struct workqueue_struct *deferredclose_wq;
extern struct workqueue_struct *serverclose_wq;
+extern struct workqueue_struct *cfid_put_wq;
extern __u32 cifs_lock_secret;
extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp;
-extern mempool_t *cifs_mid_poolp;
+extern mempool_t cifs_mid_pool;
extern mempool_t cifs_io_request_pool;
extern mempool_t cifs_io_subrequest_pool;
/* Operations for different SMB versions */
-#define SMB1_VERSION_STRING "1.0"
-#define SMB20_VERSION_STRING "2.0"
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
extern struct smb_version_operations smb1_operations;
extern struct smb_version_values smb1_values;
extern struct smb_version_operations smb20_operations;
extern struct smb_version_values smb20_values;
-#endif /* CIFS_ALLOW_INSECURE_LEGACY */
-#define SMB21_VERSION_STRING "2.1"
+#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
extern struct smb_version_operations smb21_operations;
extern struct smb_version_values smb21_values;
-#define SMBDEFAULT_VERSION_STRING "default"
extern struct smb_version_values smbdefault_values;
-#define SMB3ANY_VERSION_STRING "3"
extern struct smb_version_values smb3any_values;
-#define SMB30_VERSION_STRING "3.0"
extern struct smb_version_operations smb30_operations;
extern struct smb_version_values smb30_values;
-#define SMB302_VERSION_STRING "3.02"
-#define ALT_SMB302_VERSION_STRING "3.0.2"
/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */
extern struct smb_version_values smb302_values;
-#define SMB311_VERSION_STRING "3.1.1"
-#define ALT_SMB311_VERSION_STRING "3.11"
extern struct smb_version_operations smb311_operations;
extern struct smb_version_values smb311_values;
@@ -2123,6 +2140,8 @@ static inline char *get_security_type_str(enum securityEnum sectype)
return "Kerberos";
case NTLMv2:
return "NTLMv2";
+ case IAKerb:
+ return "IAKerb";
default:
return "Unknown";
}
@@ -2178,102 +2197,16 @@ static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses)
static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const FILE_ALL_INFO *src)
{
- memcpy(dst, src, (size_t)((u8 *)&src->AccessFlags - (u8 *)src));
- dst->AccessFlags = src->AccessFlags;
- dst->CurrentByteOffset = src->CurrentByteOffset;
- dst->Mode = src->Mode;
- dst->AlignmentRequirement = src->AlignmentRequirement;
+ memcpy(dst, src, (size_t)((u8 *)&src->EASize - (u8 *)src));
+ dst->IndexNumber = 0;
+ dst->EASize = src->EASize;
+ dst->AccessFlags = 0;
+ dst->CurrentByteOffset = 0;
+ dst->Mode = 0;
+ dst->AlignmentRequirement = 0;
dst->FileNameLength = src->FileNameLength;
}
-static inline int cifs_get_num_sgs(const struct smb_rqst *rqst,
- int num_rqst,
- const u8 *sig)
-{
- unsigned int len, skip;
- unsigned int nents = 0;
- unsigned long addr;
- size_t data_size;
- int i, j;
-
- /*
- * The first rqst has a transform header where the first 20 bytes are
- * not part of the encrypted blob.
- */
- skip = 20;
-
- /* Assumes the first rqst has a transform header as the first iov.
- * I.e.
- * rqst[0].rq_iov[0] is transform header
- * rqst[0].rq_iov[1+] data to be encrypted/decrypted
- * rqst[1+].rq_iov[0+] data to be encrypted/decrypted
- */
- for (i = 0; i < num_rqst; i++) {
- data_size = iov_iter_count(&rqst[i].rq_iter);
-
- /* We really don't want a mixture of pinned and unpinned pages
- * in the sglist. It's hard to keep track of which is what.
- * Instead, we convert to a BVEC-type iterator higher up.
- */
- if (data_size &&
- WARN_ON_ONCE(user_backed_iter(&rqst[i].rq_iter)))
- return -EIO;
-
- /* We also don't want to have any extra refs or pins to clean
- * up in the sglist.
- */
- if (data_size &&
- WARN_ON_ONCE(iov_iter_extract_will_pin(&rqst[i].rq_iter)))
- return -EIO;
-
- for (j = 0; j < rqst[i].rq_nvec; j++) {
- struct kvec *iov = &rqst[i].rq_iov[j];
-
- addr = (unsigned long)iov->iov_base + skip;
- if (is_vmalloc_or_module_addr((void *)addr)) {
- len = iov->iov_len - skip;
- nents += DIV_ROUND_UP(offset_in_page(addr) + len,
- PAGE_SIZE);
- } else {
- nents++;
- }
- skip = 0;
- }
- if (data_size)
- nents += iov_iter_npages(&rqst[i].rq_iter, INT_MAX);
- }
- nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE);
- return nents;
-}
-
-/* We can not use the normal sg_set_buf() as we will sometimes pass a
- * stack object as buf.
- */
-static inline void cifs_sg_set_buf(struct sg_table *sgtable,
- const void *buf,
- unsigned int buflen)
-{
- unsigned long addr = (unsigned long)buf;
- unsigned int off = offset_in_page(addr);
-
- addr &= PAGE_MASK;
- if (is_vmalloc_or_module_addr((void *)addr)) {
- do {
- unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off);
-
- sg_set_page(&sgtable->sgl[sgtable->nents++],
- vmalloc_to_page((void *)addr), len, off);
-
- off = 0;
- addr += PAGE_SIZE;
- buflen -= len;
- } while (buflen);
- } else {
- sg_set_page(&sgtable->sgl[sgtable->nents++],
- virt_to_page((void *)addr), buflen, off);
- }
-}
-
#define CIFS_OPARMS(_cifs_sb, _tcon, _path, _da, _cd, _co, _mode) \
((struct cifs_open_parms) { \
.tcon = _tcon, \
@@ -2293,9 +2226,11 @@ struct smb2_compound_vars {
struct kvec qi_iov;
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
+ struct kvec unlink_iov[SMB2_SET_INFO_IOV_SIZE];
+ struct kvec rename_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec close_iov;
- struct smb2_file_rename_info rename_info;
- struct smb2_file_link_info link_info;
+ struct smb2_file_rename_info_hdr rename_info;
+ struct smb2_file_link_info_hdr link_info;
struct kvec ea_iov;
};
@@ -2309,4 +2244,73 @@ static inline bool cifs_ses_exiting(struct cifs_ses *ses)
return ret;
}
+static inline bool cifs_netbios_name(const char *name, size_t namelen)
+{
+ bool ret = false;
+ size_t i;
+
+ if (namelen >= 1 && namelen <= RFC1001_NAME_LEN) {
+ for (i = 0; i < namelen; i++) {
+ const unsigned char c = name[i];
+
+ if (c == '\\' || c == '/' || c == ':' || c == '*' ||
+ c == '?' || c == '"' || c == '<' || c == '>' ||
+ c == '|' || c == '.')
+ return false;
+ if (!ret && isalpha(c))
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Execute mid callback atomically - ensures callback runs exactly once
+ * and prevents sleeping in atomic context.
+ */
+static inline void mid_execute_callback(struct TCP_Server_Info *server,
+ struct mid_q_entry *mid)
+{
+ mid_callback_t callback;
+
+ spin_lock(&mid->mid_lock);
+ callback = mid->callback;
+ mid->callback = NULL; /* Mark as executed, */
+ spin_unlock(&mid->mid_lock);
+
+ if (callback)
+ callback(server, mid);
+}
+
+#define CIFS_REPARSE_SUPPORT(tcon) \
+ ((tcon)->posix_extensions || \
+ (le32_to_cpu((tcon)->fsAttrInfo.Attributes) & \
+ FILE_SUPPORTS_REPARSE_POINTS))
+
+struct cifs_calc_sig_ctx {
+ struct md5_ctx *md5;
+ struct hmac_sha256_ctx *hmac;
+ struct shash_desc *shash;
+};
+
+#define CIFS_RECONN_DELAY_SECS 30
+#define CIFS_MAX_RECONN_DELAY (4 * CIFS_RECONN_DELAY_SECS)
+
+static inline void cifs_queue_server_reconn(struct TCP_Server_Info *server)
+{
+ if (!delayed_work_pending(&server->reconnect)) {
+ WRITE_ONCE(server->reconn_delay, 0);
+ mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
+ }
+}
+
+static inline void cifs_requeue_server_reconn(struct TCP_Server_Info *server)
+{
+ unsigned long delay = READ_ONCE(server->reconn_delay);
+
+ delay = umin(delay + CIFS_RECONN_DELAY_SECS, CIFS_MAX_RECONN_DELAY);
+ WRITE_ONCE(server->reconn_delay, delay);
+ queue_delayed_work(cifsiod_wq, &server->reconnect, delay * HZ);
+}
+
#endif /* _CIFS_GLOB_H */
diff --git a/fs/smb/client/cifspdu.h b/fs/smb/client/cifspdu.h
index ee78bb6741d6..37b23664ddf3 100644
--- a/fs/smb/client/cifspdu.h
+++ b/fs/smb/client/cifspdu.h
@@ -12,12 +12,14 @@
#include <net/sock.h>
#include <linux/unaligned.h>
#include "../common/smbfsctl.h"
+#include "../common/smb2pdu.h"
#define CIFS_PROT 0
#define POSIX_PROT (CIFS_PROT+1)
#define BAD_PROT 0xFFFF
/* SMB command codes:
+ * See MS-CIFS 2.2.2.1
* Note some commands have minimal (wct=0,bcc=0), or uninteresting, responses
* (ie which include no useful data other than the SMB error code itself).
* This can allow us to avoid response buffer allocations and copy in some cases
@@ -86,10 +88,9 @@
#define NT_TRANSACT_GET_USER_QUOTA 0x07
#define NT_TRANSACT_SET_USER_QUOTA 0x08
-#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
/* future chained NTCreateXReadX bigger, but for time being NTCreateX biggest */
/* among the requests (NTCreateX response is bigger with wct of 34) */
-#define MAX_CIFS_HDR_SIZE 0x58 /* 4 len + 32 hdr + (2*24 wct) + 2 bct + 2 pad */
+#define MAX_CIFS_HDR_SIZE 0x54 /* 32 hdr + (2*24 wct) + 2 bct + 2 pad */
#define CIFS_SMALL_PATH 120 /* allows for (448-88)/3 */
/* internal cifs vfs structures */
@@ -152,6 +153,7 @@
/*
* SMB flag definitions
+ * See MS-CIFS 2.2.3.1
*/
#define SMBFLG_EXTD_LOCK 0x01 /* server supports lock-read write-unlock smb */
#define SMBFLG_RCV_POSTED 0x02 /* obsolete */
@@ -165,6 +167,8 @@
/*
* SMB flag2 definitions
+ * See MS-CIFS 2.2.3.1
+ * MS-SMB 2.2.3.1
*/
#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* can send long (non-8.3)
path names in response */
@@ -180,58 +184,7 @@
#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000)
#define SMBFLG2_UNICODE cpu_to_le16(0x8000)
-/*
- * These are the file access permission bits defined in CIFS for the
- * NTCreateAndX as well as the level 0x107
- * TRANS2_QUERY_PATH_INFORMATION API. The level 0x107, SMB_QUERY_FILE_ALL_INFO
- * responds with the AccessFlags.
- * The AccessFlags specifies the access permissions a caller has to the
- * file and can have any suitable combination of the following values:
- */
-
-#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */
-#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */
-#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */
-#define FILE_READ_EA 0x00000008 /* Extended attributes associated */
- /* with the file can be read */
-#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */
- /* with the file can be written */
-#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */
- /* the file using system paging I/O */
-#define FILE_DELETE_CHILD 0x00000040
-#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */
- /* file can be read */
-#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */
- /* file can be written */
-#define DELETE 0x00010000 /* The file can be deleted */
-#define READ_CONTROL 0x00020000 /* The access control list and */
- /* ownership associated with the */
- /* file can be read */
-#define WRITE_DAC 0x00040000 /* The access control list and */
- /* ownership associated with the */
- /* file can be written. */
-#define WRITE_OWNER 0x00080000 /* Ownership information associated */
- /* with the file can be written */
-#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */
- /* synchronize with the completion */
- /* of an input/output request */
-#define SYSTEM_SECURITY 0x01000000 /* The system access control list */
- /* can be read and changed */
-#define GENERIC_ALL 0x10000000
-#define GENERIC_EXECUTE 0x20000000
-#define GENERIC_WRITE 0x40000000
-#define GENERIC_READ 0x80000000
- /* In summary - Relevant file */
- /* access flags from CIFS are */
- /* file_read_data, file_write_data */
- /* file_execute, file_read_attributes*/
- /* write_dac, and delete. */
-
-#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES)
-#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
- | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
-#define FILE_EXEC_RIGHTS (FILE_EXECUTE)
-
+/* Combinations of file access permission bits */
#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \
| FILE_READ_ATTRIBUTES \
| FILE_WRITE_ATTRIBUTES \
@@ -243,15 +196,6 @@
| FILE_WRITE_ATTRIBUTES \
| DELETE | READ_CONTROL | WRITE_DAC \
| WRITE_OWNER | SYNCHRONIZE)
-#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \
- | FILE_READ_ATTRIBUTES \
- | FILE_WRITE_ATTRIBUTES \
- | DELETE | READ_CONTROL | WRITE_DAC \
- | WRITE_OWNER | SYNCHRONIZE)
-
-#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
- | READ_CONTROL | SYNCHRONIZE)
-
/*
* Invalid readdir handle
@@ -285,29 +229,30 @@
/*
* File Attribute flags
*/
-#define ATTR_READONLY 0x0001
-#define ATTR_HIDDEN 0x0002
-#define ATTR_SYSTEM 0x0004
-#define ATTR_VOLUME 0x0008
-#define ATTR_DIRECTORY 0x0010
-#define ATTR_ARCHIVE 0x0020
-#define ATTR_DEVICE 0x0040
-#define ATTR_NORMAL 0x0080
-#define ATTR_TEMPORARY 0x0100
-#define ATTR_SPARSE 0x0200
-#define ATTR_REPARSE 0x0400
-#define ATTR_COMPRESSED 0x0800
-#define ATTR_OFFLINE 0x1000 /* ie file not immediately available -
- on offline storage */
-#define ATTR_NOT_CONTENT_INDEXED 0x2000
-#define ATTR_ENCRYPTED 0x4000
-#define ATTR_POSIX_SEMANTICS 0x01000000
-#define ATTR_BACKUP_SEMANTICS 0x02000000
-#define ATTR_DELETE_ON_CLOSE 0x04000000
-#define ATTR_SEQUENTIAL_SCAN 0x08000000
-#define ATTR_RANDOM_ACCESS 0x10000000
-#define ATTR_NO_BUFFERING 0x20000000
-#define ATTR_WRITE_THROUGH 0x80000000
+#define ATTR_READONLY 0x0001 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_HIDDEN 0x0002 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_SYSTEM 0x0004 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_VOLUME 0x0008
+#define ATTR_DIRECTORY 0x0010 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_ARCHIVE 0x0020 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_DEVICE 0x0040
+#define ATTR_NORMAL 0x0080 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_TEMPORARY 0x0100 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_SPARSE 0x0200 /* See MS-SMB 2.2.1.2.1 */
+#define ATTR_REPARSE_POINT 0x0400 /* See MS-SMB 2.2.1.2.1 */
+#define ATTR_COMPRESSED 0x0800 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_OFFLINE 0x1000 /* See MS-SMB 2.2.1.2.1
+ ie file not immediately available -
+ on offline storage */
+#define ATTR_NOT_CONTENT_INDEXED 0x2000 /* See MS-SMB 2.2.1.2.1 */
+#define ATTR_ENCRYPTED 0x4000 /* See MS-SMB 2.2.1.2.1 */
+#define ATTR_POSIX_SEMANTICS 0x0100000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_BACKUP_SEMANTICS 0x0200000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_DELETE_ON_CLOSE 0x0400000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_SEQUENTIAL_SCAN 0x0800000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_RANDOM_ACCESS 0x1000000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_NO_BUFFERING 0x2000000 /* See MS-CIFS 2.2.1.2.3 */
+#define ATTR_WRITE_THROUGH 0x8000000 /* See MS-CIFS 2.2.1.2.3 */
/* ShareAccess flags */
#define FILE_NO_SHARE 0x00000000
@@ -377,38 +322,6 @@
#define GETU16(var) (*((__u16 *)var)) /* BB check for endian issues */
#define GETU32(var) (*((__u32 *)var)) /* BB check for endian issues */
-struct smb_hdr {
- __be32 smb_buf_length; /* BB length is only two (rarely three) bytes,
- with one or two byte "type" preceding it that will be
- zero - we could mask the type byte off */
- __u8 Protocol[4];
- __u8 Command;
- union {
- struct {
- __u8 ErrorClass;
- __u8 Reserved;
- __le16 Error;
- } __attribute__((packed)) DosError;
- __le32 CifsError;
- } __attribute__((packed)) Status;
- __u8 Flags;
- __le16 Flags2; /* note: le */
- __le16 PidHigh;
- union {
- struct {
- __le32 SequenceNumber; /* le */
- __u32 Reserved; /* zero */
- } __attribute__((packed)) Sequence;
- __u8 SecuritySignature[8]; /* le */
- } __attribute__((packed)) Signature;
- __u8 pad[2];
- __u16 Tid;
- __le16 Pid;
- __u16 Uid;
- __le16 Mid;
- __u8 WordCount;
-} __attribute__((packed));
-
/* given a pointer to an smb_hdr, retrieve a void pointer to the ByteCount */
static inline void *
BCC(struct smb_hdr *smb)
@@ -480,19 +393,15 @@ put_bcc(__u16 count, struct smb_hdr *hdr)
*
*/
-typedef struct negotiate_req {
- struct smb_hdr hdr; /* wct = 0 */
- __le16 ByteCount;
- unsigned char DialectsArray[];
-} __attribute__((packed)) NEGOTIATE_REQ;
-
#define MIN_TZ_ADJ (15 * 60) /* minimum grid for timezones in seconds */
#define READ_RAW_ENABLE 1
#define WRITE_RAW_ENABLE 2
#define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE)
#define SMB1_CLIENT_GUID_SIZE (16)
-typedef struct negotiate_rsp {
+
+/* See MS-CIFS 2.2.4.52.2 */
+typedef struct smb_negotiate_rsp {
struct smb_hdr hdr; /* wct = 17 */
__le16 DialectIndex; /* 0xFFFF = no dialect acceptable */
__u8 SecurityMode;
@@ -516,9 +425,9 @@ typedef struct negotiate_rsp {
struct {
unsigned char GUID[SMB1_CLIENT_GUID_SIZE];
unsigned char SecurityBlob[];
- } __attribute__((packed)) extended_response;
- } __attribute__((packed)) u;
-} __attribute__((packed)) NEGOTIATE_RSP;
+ } __packed extended_response;
+ } __packed u;
+} __packed SMB_NEGOTIATE_RSP;
/* SecurityMode bits */
#define SECMODE_USER 0x01 /* off indicates share level security */
@@ -557,7 +466,7 @@ typedef union smb_com_session_setup_andx {
__le16 MaxBufferSize;
__le16 MaxMpxCount;
__le16 VcNumber;
- __u32 SessionKey;
+ __le32 SessionKey;
__le16 SecurityBlobLength;
__u32 Reserved;
__le32 Capabilities; /* see below */
@@ -565,7 +474,7 @@ typedef union smb_com_session_setup_andx {
unsigned char SecurityBlob[]; /* followed by */
/* STRING NativeOS */
/* STRING NativeLanMan */
- } __attribute__((packed)) req; /* NTLM request format (with
+ } __packed req; /* NTLM request format (with
extended security */
struct { /* request format */
@@ -576,7 +485,7 @@ typedef union smb_com_session_setup_andx {
__le16 MaxBufferSize;
__le16 MaxMpxCount;
__le16 VcNumber;
- __u32 SessionKey;
+ __le32 SessionKey;
__le16 CaseInsensitivePasswordLength; /* ASCII password len */
__le16 CaseSensitivePasswordLength; /* Unicode password length*/
__u32 Reserved; /* see below */
@@ -588,7 +497,7 @@ typedef union smb_com_session_setup_andx {
/* STRING PrimaryDomain */
/* STRING NativeOS */
/* STRING NativeLanMan */
- } __attribute__((packed)) req_no_secext; /* NTLM request format (without
+ } __packed req_no_secext; /* NTLM request format (without
extended security */
struct { /* default (NTLM) response format */
@@ -603,7 +512,7 @@ typedef union smb_com_session_setup_andx {
/* unsigned char * NativeOS; */
/* unsigned char * NativeLanMan; */
/* unsigned char * PrimaryDomain; */
- } __attribute__((packed)) resp; /* NTLM response
+ } __packed resp; /* NTLM response
(with or without extended sec) */
struct { /* request format */
@@ -614,7 +523,7 @@ typedef union smb_com_session_setup_andx {
__le16 MaxBufferSize;
__le16 MaxMpxCount;
__le16 VcNumber;
- __u32 SessionKey;
+ __le32 SessionKey;
__le16 PasswordLength;
__u32 Reserved; /* encrypt key len and offset */
__le16 ByteCount;
@@ -623,7 +532,7 @@ typedef union smb_com_session_setup_andx {
/* STRING PrimaryDomain */
/* STRING NativeOS */
/* STRING NativeLanMan */
- } __attribute__((packed)) old_req; /* pre-NTLM (LANMAN2.1) req format */
+ } __packed old_req; /* pre-NTLM (LANMAN2.1) req format */
struct { /* default (NTLM) response format */
struct smb_hdr hdr; /* wct = 3 */
@@ -635,8 +544,8 @@ typedef union smb_com_session_setup_andx {
unsigned char NativeOS[]; /* followed by */
/* unsigned char * NativeLanMan; */
/* unsigned char * PrimaryDomain; */
- } __attribute__((packed)) old_resp; /* pre-NTLM (LANMAN2.1) response */
-} __attribute__((packed)) SESSION_SETUP_ANDX;
+ } __packed old_resp; /* pre-NTLM (LANMAN2.1) response */
+} __packed SESSION_SETUP_ANDX;
/* format of NLTMv2 Response ie "case sensitive password" hash when NTLMv2 */
@@ -649,8 +558,8 @@ typedef union smb_com_session_setup_andx {
struct ntlmssp2_name {
__le16 type;
__le16 length;
-/* char name[length]; */
-} __attribute__((packed));
+ __u8 data[];
+} __packed;
struct ntlmv2_resp {
union {
@@ -658,20 +567,25 @@ struct ntlmv2_resp {
struct {
__u8 reserved[8];
__u8 key[CIFS_SERVER_CHALLENGE_SIZE];
- } __attribute__((packed)) challenge;
- } __attribute__((packed));
+ } __packed challenge;
+ } __packed;
__le32 blob_signature;
__u32 reserved;
__le64 time;
__u64 client_chal; /* random */
__u32 reserved2;
/* array of name entries could follow ending in minimum 4 byte struct */
-} __attribute__((packed));
+} __packed;
#define CIFS_NETWORK_OPSYS "CIFS VFS Client for Linux"
-/* Capabilities bits (for NTLM SessSetup request) */
+
+/*
+ * Capabilities bits (for NTLM SessSetup request)
+ * See MS-CIFS 2.2.4.52.2
+ * MS-SMB 2.2.4.5.2.1
+ */
#define CAP_UNICODE 0x00000004
#define CAP_LARGE_FILES 0x00000008
#define CAP_NT_SMBS 0x00000010
@@ -679,7 +593,7 @@ struct ntlmv2_resp {
#define CAP_LEVEL_II_OPLOCKS 0x00000080
#define CAP_NT_FIND 0x00000200 /* reserved should be zero
(because NT_SMBs implies the same thing?) */
-#define CAP_BULK_TRANSFER 0x20000000
+#define CAP_BULK_TRANSFER 0x00000400
#define CAP_EXTENDED_SECURITY 0x80000000
/* Action bits */
@@ -696,7 +610,7 @@ typedef struct smb_com_tconx_req {
unsigned char Password[]; /* followed by */
/* STRING Path *//* \\server\share name */
/* STRING Service */
-} __attribute__((packed)) TCONX_REQ;
+} __packed TCONX_REQ;
typedef struct smb_com_tconx_rsp {
struct smb_hdr hdr; /* wct = 3 , not extended response */
@@ -707,7 +621,7 @@ typedef struct smb_com_tconx_rsp {
__u16 ByteCount;
unsigned char Service[]; /* always ASCII, not Unicode */
/* STRING NativeFileSystem */
-} __attribute__((packed)) TCONX_RSP;
+} __packed TCONX_RSP;
typedef struct smb_com_tconx_rsp_ext {
struct smb_hdr hdr; /* wct = 7, extended response */
@@ -720,7 +634,7 @@ typedef struct smb_com_tconx_rsp_ext {
__u16 ByteCount;
unsigned char Service[]; /* always ASCII, not Unicode */
/* STRING NativeFileSystem */
-} __attribute__((packed)) TCONX_RSP_EXT;
+} __packed TCONX_RSP_EXT;
/* tree connect Flags */
@@ -756,14 +670,14 @@ typedef struct smb_com_echo_req {
__le16 EchoCount;
__le16 ByteCount;
char Data[];
-} __attribute__((packed)) ECHO_REQ;
+} __packed ECHO_REQ;
typedef struct smb_com_echo_rsp {
struct smb_hdr hdr;
__le16 SequenceNumber;
__le16 ByteCount;
char Data[];
-} __attribute__((packed)) ECHO_RSP;
+} __packed ECHO_RSP;
typedef struct smb_com_logoff_andx_req {
struct smb_hdr hdr; /* wct = 2 */
@@ -771,7 +685,7 @@ typedef struct smb_com_logoff_andx_req {
__u8 AndXReserved;
__u16 AndXOffset;
__u16 ByteCount;
-} __attribute__((packed)) LOGOFF_ANDX_REQ;
+} __packed LOGOFF_ANDX_REQ;
typedef struct smb_com_logoff_andx_rsp {
struct smb_hdr hdr; /* wct = 2 */
@@ -779,7 +693,7 @@ typedef struct smb_com_logoff_andx_rsp {
__u8 AndXReserved;
__u16 AndXOffset;
__u16 ByteCount;
-} __attribute__((packed)) LOGOFF_ANDX_RSP;
+} __packed LOGOFF_ANDX_RSP;
typedef union smb_com_tree_disconnect { /* as an alternative can use flag on
tree_connect PDU to effect disconnect */
@@ -787,36 +701,36 @@ typedef union smb_com_tree_disconnect { /* as an alternative can use flag on
struct {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bcc = 0 */
- } __attribute__((packed)) req;
+ } __packed req;
struct {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bcc = 0 */
- } __attribute__((packed)) resp;
-} __attribute__((packed)) TREE_DISCONNECT;
+ } __packed resp;
+} __packed TREE_DISCONNECT;
typedef struct smb_com_close_req {
struct smb_hdr hdr; /* wct = 3 */
__u16 FileID;
__u32 LastWriteTime; /* should be zero or -1 */
__u16 ByteCount; /* 0 */
-} __attribute__((packed)) CLOSE_REQ;
+} __packed CLOSE_REQ;
typedef struct smb_com_close_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) CLOSE_RSP;
+} __packed CLOSE_RSP;
typedef struct smb_com_flush_req {
struct smb_hdr hdr; /* wct = 1 */
__u16 FileID;
__u16 ByteCount; /* 0 */
-} __attribute__((packed)) FLUSH_REQ;
+} __packed FLUSH_REQ;
typedef struct smb_com_findclose_req {
struct smb_hdr hdr; /* wct = 1 */
__u16 FileID;
__u16 ByteCount; /* 0 */
-} __attribute__((packed)) FINDCLOSE_REQ;
+} __packed FINDCLOSE_REQ;
/* OpenFlags */
#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */
@@ -863,7 +777,7 @@ typedef struct smb_com_open_req { /* also handles create */
__u8 SecurityFlags;
__le16 ByteCount;
char fileName[];
-} __attribute__((packed)) OPEN_REQ;
+} __packed OPEN_REQ;
/* open response: oplock levels */
#define OPLOCK_NONE 0
@@ -895,7 +809,7 @@ typedef struct smb_com_open_rsp {
__le16 DeviceState;
__u8 DirectoryFlag;
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) OPEN_RSP;
+} __packed OPEN_RSP;
typedef struct smb_com_open_rsp_ext {
struct smb_hdr hdr; /* wct = 42 but meaningless due to MS bug? */
@@ -920,7 +834,7 @@ typedef struct smb_com_open_rsp_ext {
__le32 MaximalAccessRights;
__le32 GuestMaximalAccessRights;
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) OPEN_RSP_EXT;
+} __packed OPEN_RSP_EXT;
/* format of legacy open request */
@@ -940,7 +854,7 @@ typedef struct smb_com_openx_req {
__le32 Reserved;
__le16 ByteCount; /* file name follows */
char fileName[];
-} __attribute__((packed)) OPENX_REQ;
+} __packed OPENX_REQ;
typedef struct smb_com_openx_rsp {
struct smb_hdr hdr; /* wct = 15 */
@@ -958,7 +872,7 @@ typedef struct smb_com_openx_rsp {
__u32 FileId;
__u16 Reserved;
__u16 ByteCount;
-} __attribute__((packed)) OPENX_RSP;
+} __packed OPENX_RSP;
/* For encoding of POSIX Open Request - see trans2 function 0x209 data struct */
@@ -980,7 +894,7 @@ typedef struct smb_com_writex_req {
__u8 Pad; /* BB check for whether padded to DWORD
boundary and optimum performance here */
char Data[];
-} __attribute__((packed)) WRITEX_REQ;
+} __packed WRITEX_REQ;
typedef struct smb_com_write_req {
struct smb_hdr hdr; /* wct = 14 */
@@ -1000,7 +914,7 @@ typedef struct smb_com_write_req {
__u8 Pad; /* BB check for whether padded to DWORD
boundary and optimum performance here */
char Data[];
-} __attribute__((packed)) WRITE_REQ;
+} __packed WRITE_REQ;
typedef struct smb_com_write_rsp {
struct smb_hdr hdr; /* wct = 6 */
@@ -1012,7 +926,7 @@ typedef struct smb_com_write_rsp {
__le16 CountHigh;
__u16 Reserved;
__u16 ByteCount;
-} __attribute__((packed)) WRITE_RSP;
+} __packed WRITE_RSP;
/* legacy read request for older servers */
typedef struct smb_com_readx_req {
@@ -1027,7 +941,7 @@ typedef struct smb_com_readx_req {
__le32 Reserved;
__le16 Remaining;
__le16 ByteCount;
-} __attribute__((packed)) READX_REQ;
+} __packed READX_REQ;
typedef struct smb_com_read_req {
struct smb_hdr hdr; /* wct = 12 */
@@ -1042,7 +956,7 @@ typedef struct smb_com_read_req {
__le16 Remaining;
__le32 OffsetHigh;
__le16 ByteCount;
-} __attribute__((packed)) READ_REQ;
+} __packed READ_REQ;
typedef struct smb_com_read_rsp {
struct smb_hdr hdr; /* wct = 12 */
@@ -1058,7 +972,7 @@ typedef struct smb_com_read_rsp {
__u64 Reserved2;
__u16 ByteCount;
/* read response data immediately follows */
-} __attribute__((packed)) READ_RSP;
+} __packed READ_RSP;
typedef struct locking_andx_range {
__le16 Pid;
@@ -1067,7 +981,7 @@ typedef struct locking_andx_range {
__le32 OffsetLow;
__le32 LengthHigh;
__le32 LengthLow;
-} __attribute__((packed)) LOCKING_ANDX_RANGE;
+} __packed LOCKING_ANDX_RANGE;
#define LOCKING_ANDX_SHARED_LOCK 0x01
#define LOCKING_ANDX_OPLOCK_RELEASE 0x02
@@ -1088,7 +1002,7 @@ typedef struct smb_com_lock_req {
__le16 NumberOfLocks;
__le16 ByteCount;
LOCKING_ANDX_RANGE Locks[];
-} __attribute__((packed)) LOCK_REQ;
+} __packed LOCK_REQ;
/* lock type */
#define CIFS_RDLCK 0
@@ -1101,7 +1015,7 @@ typedef struct cifs_posix_lock {
__le64 start;
__le64 length;
/* BB what about additional owner info to identify network client */
-} __attribute__((packed)) CIFS_POSIX_LOCK;
+} __packed CIFS_POSIX_LOCK;
typedef struct smb_com_lock_rsp {
struct smb_hdr hdr; /* wct = 2 */
@@ -1109,7 +1023,7 @@ typedef struct smb_com_lock_rsp {
__u8 AndXReserved;
__le16 AndXOffset;
__u16 ByteCount;
-} __attribute__((packed)) LOCK_RSP;
+} __packed LOCK_RSP;
typedef struct smb_com_rename_req {
struct smb_hdr hdr; /* wct = 1 */
@@ -1119,7 +1033,7 @@ typedef struct smb_com_rename_req {
unsigned char OldFileName[];
/* followed by __u8 BufferFormat2 */
/* followed by NewFileName */
-} __attribute__((packed)) RENAME_REQ;
+} __packed RENAME_REQ;
/* copy request flags */
#define COPY_MUST_BE_FILE 0x0001
@@ -1139,7 +1053,7 @@ typedef struct smb_com_copy_req {
unsigned char OldFileName[];
/* followed by __u8 BufferFormat2 */
/* followed by NewFileName string */
-} __attribute__((packed)) COPY_REQ;
+} __packed COPY_REQ;
typedef struct smb_com_copy_rsp {
struct smb_hdr hdr; /* wct = 1 */
@@ -1147,7 +1061,7 @@ typedef struct smb_com_copy_rsp {
__u16 ByteCount; /* may be zero */
__u8 BufferFormat; /* 0x04 - only present if errored file follows */
unsigned char ErrorFileName[]; /* only present if error in copy */
-} __attribute__((packed)) COPY_RSP;
+} __packed COPY_RSP;
#define CREATE_HARD_LINK 0x103
#define MOVEFILE_COPY_ALLOWED 0x0002
@@ -1163,12 +1077,12 @@ typedef struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */
unsigned char OldFileName[];
/* followed by __u8 BufferFormat2 */
/* followed by NewFileName */
-} __attribute__((packed)) NT_RENAME_REQ;
+} __packed NT_RENAME_REQ;
typedef struct smb_com_rename_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) RENAME_RSP;
+} __packed RENAME_RSP;
typedef struct smb_com_delete_file_req {
struct smb_hdr hdr; /* wct = 1 */
@@ -1176,43 +1090,43 @@ typedef struct smb_com_delete_file_req {
__le16 ByteCount;
__u8 BufferFormat; /* 4 = ASCII */
unsigned char fileName[];
-} __attribute__((packed)) DELETE_FILE_REQ;
+} __packed DELETE_FILE_REQ;
typedef struct smb_com_delete_file_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) DELETE_FILE_RSP;
+} __packed DELETE_FILE_RSP;
typedef struct smb_com_delete_directory_req {
struct smb_hdr hdr; /* wct = 0 */
__le16 ByteCount;
__u8 BufferFormat; /* 4 = ASCII */
unsigned char DirName[];
-} __attribute__((packed)) DELETE_DIRECTORY_REQ;
+} __packed DELETE_DIRECTORY_REQ;
typedef struct smb_com_delete_directory_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) DELETE_DIRECTORY_RSP;
+} __packed DELETE_DIRECTORY_RSP;
typedef struct smb_com_create_directory_req {
struct smb_hdr hdr; /* wct = 0 */
__le16 ByteCount;
__u8 BufferFormat; /* 4 = ASCII */
unsigned char DirName[];
-} __attribute__((packed)) CREATE_DIRECTORY_REQ;
+} __packed CREATE_DIRECTORY_REQ;
typedef struct smb_com_create_directory_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) CREATE_DIRECTORY_RSP;
+} __packed CREATE_DIRECTORY_RSP;
typedef struct smb_com_query_information_req {
struct smb_hdr hdr; /* wct = 0 */
__le16 ByteCount; /* 1 + namelen + 1 */
__u8 BufferFormat; /* 4 = ASCII */
unsigned char FileName[];
-} __attribute__((packed)) QUERY_INFORMATION_REQ;
+} __packed QUERY_INFORMATION_REQ;
typedef struct smb_com_query_information_rsp {
struct smb_hdr hdr; /* wct = 10 */
@@ -1221,23 +1135,22 @@ typedef struct smb_com_query_information_rsp {
__le32 size;
__u16 reserved[5];
__le16 ByteCount; /* bcc = 0 */
-} __attribute__((packed)) QUERY_INFORMATION_RSP;
+} __packed QUERY_INFORMATION_RSP;
typedef struct smb_com_setattr_req {
struct smb_hdr hdr; /* wct = 8 */
__le16 attr;
- __le16 time_low;
- __le16 time_high;
+ __le32 last_write_time;
__le16 reserved[5]; /* must be zero */
- __u16 ByteCount;
+ __le16 ByteCount;
__u8 BufferFormat; /* 4 = ASCII */
unsigned char fileName[];
-} __attribute__((packed)) SETATTR_REQ;
+} __packed SETATTR_REQ;
typedef struct smb_com_setattr_rsp {
struct smb_hdr hdr; /* wct = 0 */
__u16 ByteCount; /* bct = 0 */
-} __attribute__((packed)) SETATTR_RSP;
+} __packed SETATTR_RSP;
/* empty wct response to setattr */
@@ -1265,7 +1178,7 @@ typedef struct smb_com_ntransact_req {
__le16 ByteCount;
__u8 Pad[3];
__u8 Parms[];
-} __attribute__((packed)) NTRANSACT_REQ;
+} __packed NTRANSACT_REQ;
typedef struct smb_com_ntransact_rsp {
struct smb_hdr hdr; /* wct = 18 */
@@ -1282,15 +1195,7 @@ typedef struct smb_com_ntransact_rsp {
__u16 ByteCount;
/* __u8 Pad[3]; */
/* parms and data follow */
-} __attribute__((packed)) NTRANSACT_RSP;
-
-/* See MS-SMB 2.2.7.2.1.1 */
-struct srv_copychunk {
- __le64 SourceOffset;
- __le64 DestinationOffset;
- __le32 CopyLength;
- __u32 Reserved;
-} __packed;
+} __packed NTRANSACT_RSP;
typedef struct smb_com_transaction_ioctl_req {
struct smb_hdr hdr; /* wct = 23 */
@@ -1314,7 +1219,7 @@ typedef struct smb_com_transaction_ioctl_req {
__le16 ByteCount;
__u8 Pad[3];
__u8 Data[];
-} __attribute__((packed)) TRANSACT_IOCTL_REQ;
+} __packed TRANSACT_IOCTL_REQ;
typedef struct smb_com_transaction_compr_ioctl_req {
struct smb_hdr hdr; /* wct = 23 */
@@ -1338,7 +1243,7 @@ typedef struct smb_com_transaction_compr_ioctl_req {
__le16 ByteCount;
__u8 Pad[3];
__le16 compression_state; /* See below for valid flags */
-} __attribute__((packed)) TRANSACT_COMPR_IOCTL_REQ;
+} __packed TRANSACT_COMPR_IOCTL_REQ;
/* compression state flags */
#define COMPRESSION_FORMAT_NONE 0x0000
@@ -1359,7 +1264,7 @@ typedef struct smb_com_transaction_ioctl_rsp {
__u8 SetupCount; /* 1 */
__le16 ReturnedDataLen;
__le16 ByteCount;
-} __attribute__((packed)) TRANSACT_IOCTL_RSP;
+} __packed TRANSACT_IOCTL_RSP;
#define CIFS_ACL_OWNER 1
#define CIFS_ACL_GROUP 2
@@ -1386,7 +1291,7 @@ typedef struct smb_com_transaction_qsec_req {
__u16 Fid;
__u16 Reserved2;
__le32 AclFlags;
-} __attribute__((packed)) QUERY_SEC_DESC_REQ;
+} __packed QUERY_SEC_DESC_REQ;
typedef struct smb_com_transaction_ssec_req {
@@ -1409,7 +1314,7 @@ typedef struct smb_com_transaction_ssec_req {
__u16 Fid;
__u16 Reserved2;
__le32 AclFlags;
-} __attribute__((packed)) SET_SEC_DESC_REQ;
+} __packed SET_SEC_DESC_REQ;
typedef struct smb_com_transaction_change_notify_req {
struct smb_hdr hdr; /* wct = 23 */
@@ -1433,7 +1338,7 @@ typedef struct smb_com_transaction_change_notify_req {
__le16 ByteCount;
/* __u8 Pad[3];*/
/* __u8 Data[];*/
-} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_REQ;
+} __packed TRANSACT_CHANGE_NOTIFY_REQ;
/* BB eventually change to use generic ntransact rsp struct
and validation routine */
@@ -1451,7 +1356,7 @@ typedef struct smb_com_transaction_change_notify_rsp {
__u8 SetupCount; /* 0 */
__u16 ByteCount;
/* __u8 Pad[3]; */
-} __attribute__((packed)) TRANSACT_CHANGE_NOTIFY_RSP;
+} __packed TRANSACT_CHANGE_NOTIFY_RSP;
/* Completion Filter flags for Notify */
#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
@@ -1482,37 +1387,7 @@ struct file_notify_information {
__le32 Action;
__le32 FileNameLength;
__u8 FileName[];
-} __attribute__((packed));
-
-/* For IO_REPARSE_TAG_SYMLINK */
-struct reparse_symlink_data {
- __le32 ReparseTag;
- __le16 ReparseDataLength;
- __u16 Reserved;
- __le16 SubstituteNameOffset;
- __le16 SubstituteNameLength;
- __le16 PrintNameOffset;
- __le16 PrintNameLength;
- __le32 Flags;
- char PathBuffer[];
-} __attribute__((packed));
-
-/* Flag above */
-#define SYMLINK_FLAG_RELATIVE 0x00000001
-
-/* For IO_REPARSE_TAG_NFS */
-#define NFS_SPECFILE_LNK 0x00000000014B4E4C
-#define NFS_SPECFILE_CHR 0x0000000000524843
-#define NFS_SPECFILE_BLK 0x00000000004B4C42
-#define NFS_SPECFILE_FIFO 0x000000004F464946
-#define NFS_SPECFILE_SOCK 0x000000004B434F53
-struct reparse_posix_data {
- __le32 ReparseTag;
- __le16 ReparseDataLength;
- __u16 Reserved;
- __le64 InodeType; /* LNK, FIFO, CHR etc. */
- __u8 DataBuffer[];
-} __attribute__((packed));
+} __packed;
struct cifs_quota_data {
__u32 rsrvd1; /* 0 */
@@ -1522,7 +1397,7 @@ struct cifs_quota_data {
__u64 soft_limit;
__u64 hard_limit;
char sid[]; /* variable size? */
-} __attribute__((packed));
+} __packed;
/* quota sub commands */
#define QUOTA_LIST_CONTINUE 0
@@ -1548,12 +1423,12 @@ struct trans2_req {
__u8 Reserved3;
__le16 SubCommand; /* 1st setup word - SetupCount words follow */
__le16 ByteCount;
-} __attribute__((packed));
+} __packed;
struct smb_t2_req {
struct smb_hdr hdr;
struct trans2_req t2_req;
-} __attribute__((packed));
+} __packed;
struct trans2_resp {
/* struct smb_hdr hdr precedes. Note wct = 10 + setup count */
@@ -1572,12 +1447,12 @@ struct trans2_resp {
__u16 ByteCount;
__u16 Reserved2;*/
/* data area follows */
-} __attribute__((packed));
+} __packed;
struct smb_t2_rsp {
struct smb_hdr hdr;
struct trans2_resp t2_rsp;
-} __attribute__((packed));
+} __packed;
/* PathInfo/FileInfo infolevels */
#define SMB_INFO_STANDARD 1
@@ -1674,14 +1549,14 @@ typedef struct smb_com_transaction2_qpi_req {
__le16 InformationLevel;
__u32 Reserved4;
char FileName[];
-} __attribute__((packed)) TRANSACTION2_QPI_REQ;
+} __packed TRANSACTION2_QPI_REQ;
typedef struct smb_com_transaction2_qpi_rsp {
struct smb_hdr hdr; /* wct = 10 + SetupCount */
struct trans2_resp t2;
__u16 ByteCount;
__u16 Reserved2; /* parameter word is present for infolevels > 100 */
-} __attribute__((packed)) TRANSACTION2_QPI_RSP;
+} __packed TRANSACTION2_QPI_RSP;
typedef struct smb_com_transaction2_spi_req {
struct smb_hdr hdr; /* wct = 15 */
@@ -1707,21 +1582,21 @@ typedef struct smb_com_transaction2_spi_req {
__le16 InformationLevel;
__u32 Reserved4;
char FileName[];
-} __attribute__((packed)) TRANSACTION2_SPI_REQ;
+} __packed TRANSACTION2_SPI_REQ;
typedef struct smb_com_transaction2_spi_rsp {
struct smb_hdr hdr; /* wct = 10 + SetupCount */
struct trans2_resp t2;
__u16 ByteCount;
__u16 Reserved2; /* parameter word is present for infolevels > 100 */
-} __attribute__((packed)) TRANSACTION2_SPI_RSP;
+} __packed TRANSACTION2_SPI_RSP;
struct set_file_rename {
__le32 overwrite; /* 1 = overwrite dest */
__u32 root_fid; /* zero */
__le32 target_name_len;
char target_name[]; /* Must be unicode */
-} __attribute__((packed));
+} __packed;
struct smb_com_transaction2_sfi_req {
struct smb_hdr hdr; /* wct = 15 */
@@ -1748,14 +1623,14 @@ struct smb_com_transaction2_sfi_req {
__le16 InformationLevel;
__u16 Reserved4;
__u8 payload[];
-} __attribute__((packed));
+} __packed;
struct smb_com_transaction2_sfi_rsp {
struct smb_hdr hdr; /* wct = 10 + SetupCount */
struct trans2_resp t2;
__u16 ByteCount;
__u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */
-} __attribute__((packed));
+} __packed;
struct smb_t2_qfi_req {
struct smb_hdr hdr;
@@ -1763,14 +1638,14 @@ struct smb_t2_qfi_req {
__u8 Pad;
__u16 Fid;
__le16 InformationLevel;
-} __attribute__((packed));
+} __packed;
struct smb_t2_qfi_rsp {
struct smb_hdr hdr; /* wct = 10 + SetupCount */
struct trans2_resp t2;
__u16 ByteCount;
__u16 Reserved2; /* parameter word reserved - present for infolevels > 100 */
-} __attribute__((packed));
+} __packed;
/*
* Flags on T2 FINDFIRST and FINDNEXT
@@ -1812,13 +1687,13 @@ typedef struct smb_com_transaction2_ffirst_req {
__le16 InformationLevel;
__le32 SearchStorageType;
char FileName[];
-} __attribute__((packed)) TRANSACTION2_FFIRST_REQ;
+} __packed TRANSACTION2_FFIRST_REQ;
typedef struct smb_com_transaction2_ffirst_rsp {
struct smb_hdr hdr; /* wct = 10 */
struct trans2_resp t2;
__u16 ByteCount;
-} __attribute__((packed)) TRANSACTION2_FFIRST_RSP;
+} __packed TRANSACTION2_FFIRST_RSP;
typedef struct smb_com_transaction2_ffirst_rsp_parms {
__u16 SearchHandle;
@@ -1826,7 +1701,7 @@ typedef struct smb_com_transaction2_ffirst_rsp_parms {
__le16 EndofSearch;
__le16 EAErrorOffset;
__le16 LastNameOffset;
-} __attribute__((packed)) T2_FFIRST_RSP_PARMS;
+} __packed T2_FFIRST_RSP_PARMS;
typedef struct smb_com_transaction2_fnext_req {
struct smb_hdr hdr; /* wct = 15 */
@@ -1854,20 +1729,20 @@ typedef struct smb_com_transaction2_fnext_req {
__u32 ResumeKey;
__le16 SearchFlags;
char ResumeFileName[];
-} __attribute__((packed)) TRANSACTION2_FNEXT_REQ;
+} __packed TRANSACTION2_FNEXT_REQ;
typedef struct smb_com_transaction2_fnext_rsp {
struct smb_hdr hdr; /* wct = 10 */
struct trans2_resp t2;
__u16 ByteCount;
-} __attribute__((packed)) TRANSACTION2_FNEXT_RSP;
+} __packed TRANSACTION2_FNEXT_RSP;
typedef struct smb_com_transaction2_fnext_rsp_parms {
__le16 SearchCount;
__le16 EndofSearch;
__le16 EAErrorOffset;
__le16 LastNameOffset;
-} __attribute__((packed)) T2_FNEXT_RSP_PARMS;
+} __packed T2_FNEXT_RSP_PARMS;
/* QFSInfo Levels */
#define SMB_INFO_ALLOCATION 1
@@ -1911,14 +1786,14 @@ typedef struct smb_com_transaction2_qfsi_req {
__le16 ByteCount;
__u8 Pad;
__le16 InformationLevel;
-} __attribute__((packed)) TRANSACTION2_QFSI_REQ;
+} __packed TRANSACTION2_QFSI_REQ;
typedef struct smb_com_transaction_qfsi_rsp {
struct smb_hdr hdr; /* wct = 10 + SetupCount */
struct trans2_resp t2;
__u16 ByteCount;
__u8 Pad; /* may be three bytes? *//* followed by data area */
-} __attribute__((packed)) TRANSACTION2_QFSI_RSP;
+} __packed TRANSACTION2_QFSI_RSP;
typedef struct whoami_rsp_data { /* Query level 0x202 */
__u32 flags; /* 0 = Authenticated user 1 = GUEST */
@@ -1931,7 +1806,7 @@ typedef struct whoami_rsp_data { /* Query level 0x202 */
__u32 pad; /* reserved - MBZ */
/* __u64 gid_array[0]; */ /* may be empty */
/* __u8 * psid_list */ /* may be empty */
-} __attribute__((packed)) WHOAMI_RSP_DATA;
+} __packed WHOAMI_RSP_DATA;
/* SETFSInfo Levels */
#define SMB_SET_CIFS_UNIX_INFO 0x200
@@ -1964,7 +1839,7 @@ typedef struct smb_com_transaction2_setfsi_req {
__le16 ClientUnixMajor; /* Data start. */
__le16 ClientUnixMinor;
__le64 ClientUnixCap; /* Data end */
-} __attribute__((packed)) TRANSACTION2_SETFSI_REQ;
+} __packed TRANSACTION2_SETFSI_REQ;
/* level 0x203 request structure follows */
typedef struct smb_com_transaction2_setfs_enc_req {
@@ -1990,14 +1865,14 @@ typedef struct smb_com_transaction2_setfs_enc_req {
__u16 Reserved4; /* Parameters start. */
__le16 InformationLevel;/* Parameters end. */
/* NTLMSSP Blob, Data start. */
-} __attribute__((packed)) TRANSACTION2_SETFSI_ENC_REQ;
+} __packed TRANSACTION2_SETFSI_ENC_REQ;
/* response for setfsinfo levels 0x200 and 0x203 */
typedef struct smb_com_transaction2_setfsi_rsp {
struct smb_hdr hdr; /* wct = 10 */
struct trans2_resp t2;
__u16 ByteCount;
-} __attribute__((packed)) TRANSACTION2_SETFSI_RSP;
+} __packed TRANSACTION2_SETFSI_RSP;
typedef struct smb_com_transaction2_get_dfs_refer_req {
struct smb_hdr hdr; /* wct = 15 */
@@ -2023,7 +1898,7 @@ typedef struct smb_com_transaction2_get_dfs_refer_req {
seem to matter though */
__le16 MaxReferralLevel;
char RequestFileName[];
-} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_REQ;
+} __packed TRANSACTION2_GET_DFS_REFER_REQ;
#define DFS_VERSION cpu_to_le16(0x0003)
@@ -2045,7 +1920,7 @@ typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */
__le16 DfsAlternatePathOffset;
__le16 NetworkAddressOffset; /* offset of the link target */
__u8 ServiceSiteGuid[16]; /* MBZ, ignored */
-} __attribute__((packed)) REFERRAL3;
+} __packed REFERRAL3;
struct get_dfs_referral_rsp {
__le16 PathConsumed;
@@ -2085,7 +1960,7 @@ struct serverInfo {
unsigned char versionMinor;
unsigned long type;
unsigned int commentOffset;
-} __attribute__((packed));
+} __packed;
/*
* The following structure is the format of the data returned on a NetShareEnum
@@ -2097,27 +1972,20 @@ struct shareInfo {
char pad;
unsigned short type;
unsigned int commentOffset;
-} __attribute__((packed));
+} __packed;
struct aliasInfo {
char aliasName[9];
char pad;
unsigned int commentOffset;
unsigned char type[2];
-} __attribute__((packed));
+} __packed;
struct aliasInfo92 {
int aliasNameOffset;
int serverNameOffset;
int shareNameOffset;
-} __attribute__((packed));
-
-typedef struct {
- __le64 TotalAllocationUnits;
- __le64 FreeAllocationUnits;
- __le32 SectorsPerAllocationUnit;
- __le32 BytesPerSector;
-} __attribute__((packed)) FILE_SYSTEM_INFO; /* size info, level 0x103 */
+} __packed;
typedef struct {
__le32 fsid;
@@ -2125,13 +1993,13 @@ typedef struct {
__le32 TotalAllocationUnits;
__le32 FreeAllocationUnits;
__le16 BytesPerSector;
-} __attribute__((packed)) FILE_SYSTEM_ALLOC_INFO;
+} __packed FILE_SYSTEM_ALLOC_INFO;
typedef struct {
__le16 MajorVersionNumber;
__le16 MinorVersionNumber;
__le64 Capability;
-} __attribute__((packed)) FILE_SYSTEM_UNIX_INFO; /* Unix extension level 0x200*/
+} __packed FILE_SYSTEM_UNIX_INFO; /* Unix extension level 0x200*/
/* Version numbers for CIFS UNIX major and minor. */
#define CIFS_UNIX_MAJOR_VERSION 1
@@ -2166,28 +2034,6 @@ typedef struct {
#define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */
-typedef struct {
- /* For undefined recommended transfer size return -1 in that field */
- __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */
- __le32 BlockSize;
- /* The next three fields are in terms of the block size.
- (above). If block size is unknown, 4096 would be a
- reasonable block size for a server to report.
- Note that returning the blocks/blocksavail removes need
- to make a second call (to QFSInfo level 0x103 to get this info.
- UserBlockAvail is typically less than or equal to BlocksAvail,
- if no distinction is made return the same value in each */
- __le64 TotalBlocks;
- __le64 BlocksAvail; /* bfree */
- __le64 UserBlocksAvail; /* bavail */
- /* For undefined Node fields or FSID return -1 */
- __le64 TotalFileNodes;
- __le64 FreeFileNodes;
- __le64 FileSysIdentifier; /* fsid */
- /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */
- /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */
-} __attribute__((packed)) FILE_SYSTEM_POSIX_INFO;
-
/* DeviceType Flags */
#define FILE_DEVICE_CD_ROM 0x00000002
#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003
@@ -2222,46 +2068,6 @@ typedef struct {
#define FILE_PORTABLE_DEVICE 0x00004000
#define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000
-typedef struct {
- __le32 DeviceType;
- __le32 DeviceCharacteristics;
-} __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */
-
-/* minimum includes first three fields, and empty FS Name */
-#define MIN_FS_ATTR_INFO_SIZE 12
-
-
-/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */
-#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */
-#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */
-#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000
-#define FILE_SUPPORTS_USN_JOURNAL 0x02000000
-#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000
-#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000
-#define FILE_SUPPORTS_HARD_LINKS 0x00400000
-#define FILE_SUPPORTS_TRANSACTIONS 0x00200000
-#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000
-#define FILE_READ_ONLY_VOLUME 0x00080000
-#define FILE_NAMED_STREAMS 0x00040000
-#define FILE_SUPPORTS_ENCRYPTION 0x00020000
-#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
-#define FILE_VOLUME_IS_COMPRESSED 0x00008000
-#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
-#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
-#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
-#define FILE_VOLUME_QUOTAS 0x00000020
-#define FILE_FILE_COMPRESSION 0x00000010
-#define FILE_PERSISTENT_ACLS 0x00000008
-#define FILE_UNICODE_ON_DISK 0x00000004
-#define FILE_CASE_PRESERVED_NAMES 0x00000002
-#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
-typedef struct {
- __le32 Attributes;
- __le32 MaxPathNameComponentLength;
- __le32 FileSystemNameLen;
- char FileSystemName[52]; /* do not have to save this - get subset? */
-} __attribute__((packed)) FILE_SYSTEM_ATTRIBUTE_INFO;
-
/******************************************************************************/
/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */
/******************************************************************************/
@@ -2280,19 +2086,13 @@ typedef struct { /* data block encoding of response to level 263 QPathInfo */
__u8 DeletePending;
__u8 Directory;
__u16 Pad2;
- __le64 IndexNumber;
__le32 EASize;
- __le32 AccessFlags;
- __u64 IndexNumber1;
- __le64 CurrentByteOffset;
- __le32 Mode;
- __le32 AlignmentRequirement;
__le32 FileNameLength;
union {
char __pad;
DECLARE_FLEX_ARRAY(char, FileName);
};
-} __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */
+} __packed FILE_ALL_INFO; /* level 0x107 QPathInfo */
typedef struct {
__le64 AllocationSize;
@@ -2301,7 +2101,7 @@ typedef struct {
__u8 DeletePending;
__u8 Directory;
__u16 Pad;
-} __attribute__((packed)) FILE_STANDARD_INFO; /* level 0x102 QPathInfo */
+} __packed FILE_STANDARD_INFO; /* level 0x102 QPathInfo */
/* defines for enumerating possible values of the Unix type field below */
@@ -2326,11 +2126,11 @@ typedef struct {
__le64 UniqueId;
__le64 Permissions;
__le64 Nlinks;
-} __attribute__((packed)) FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */
+} __packed FILE_UNIX_BASIC_INFO; /* level 0x200 QPathInfo */
typedef struct {
DECLARE_FLEX_ARRAY(char, LinkDest);
-} __attribute__((packed)) FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */
+} __packed FILE_UNIX_LINK_INFO; /* level 0x201 QPathInfo */
/* The following three structures are needed only for
setting time to NT4 and some older servers via
@@ -2339,13 +2139,13 @@ typedef struct {
__u16 Day:5;
__u16 Month:4;
__u16 Year:7;
-} __attribute__((packed)) SMB_DATE;
+} __packed SMB_DATE;
typedef struct {
__u16 TwoSeconds:5;
__u16 Minutes:6;
__u16 Hours:5;
-} __attribute__((packed)) SMB_TIME;
+} __packed SMB_TIME;
typedef struct {
__le16 CreationDate; /* SMB Date see above */
@@ -2358,7 +2158,7 @@ typedef struct {
__le32 AllocationSize;
__le16 Attributes; /* verify not u32 */
__le32 EASize;
-} __attribute__((packed)) FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */
+} __packed FILE_INFO_STANDARD; /* level 1 SetPath/FileInfo */
typedef struct {
__le64 CreationTime;
@@ -2367,7 +2167,7 @@ typedef struct {
__le64 ChangeTime;
__le32 Attributes;
__u32 Pad;
-} __attribute__((packed)) FILE_BASIC_INFO; /* size info, level 0x101 */
+} __packed FILE_BASIC_INFO; /* size info, level 0x101 */
struct file_allocation_info {
__le64 AllocationSize; /* Note old Samba srvr rounds this up too much */
@@ -2375,11 +2175,11 @@ struct file_allocation_info {
struct file_end_of_file_info {
__le64 FileSize; /* offset to end of file */
-} __attribute__((packed)); /* size info, level 0x104 for set, 0x106 for query */
+} __packed; /* size info, level 0x104 for set, 0x106 for query */
struct file_alt_name_info {
DECLARE_FLEX_ARRAY(__u8, alt_name);
-} __attribute__((packed)); /* level 0x0108 */
+} __packed; /* level 0x0108 */
struct file_stream_info {
__le32 number_of_streams; /* BB check sizes and verify location */
@@ -2396,7 +2196,7 @@ struct file_compression_info {
__u8 ch_shift;
__u8 cl_shift;
__u8 pad[3];
-} __attribute__((packed)); /* level 0x10b */
+} __packed; /* level 0x10b */
/* POSIX ACL set/query path info structures */
#define CIFS_ACL_VERSION 1
@@ -2404,7 +2204,7 @@ struct cifs_posix_ace { /* access control entry (ACE) */
__u8 cifs_e_tag;
__u8 cifs_e_perm;
__le64 cifs_uid; /* or gid */
-} __attribute__((packed));
+} __packed;
struct cifs_posix_acl { /* access control list (ACL) */
__le16 version;
@@ -2412,7 +2212,7 @@ struct cifs_posix_acl { /* access control list (ACL) */
__le16 default_entry_count; /* default ACL - count of entries */
struct cifs_posix_ace ace_array[];
/* followed by struct cifs_posix_ace default_ace_array[] */
-} __attribute__((packed)); /* level 0x204 */
+} __packed; /* level 0x204 */
/* types of access control entries already defined in posix_acl.h */
/* #define CIFS_POSIX_ACL_USER_OBJ 0x01
@@ -2447,7 +2247,7 @@ typedef struct {
__le32 PosixOpenFlags;
__le64 Permissions;
__le16 Level; /* reply level requested (see QPathInfo levels) */
-} __attribute__((packed)) OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */
+} __packed OPEN_PSX_REQ; /* level 0x209 SetPathInfo data */
typedef struct {
__le16 OplockFlags;
@@ -2456,27 +2256,27 @@ typedef struct {
__le16 ReturnedLevel;
__le16 Pad;
/* struct following varies based on requested level */
-} __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */
+} __packed OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */
#define SMB_POSIX_UNLINK_FILE_TARGET 0
#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1
struct unlink_psx_rq { /* level 0x20a SetPathInfo */
__le16 type;
-} __attribute__((packed));
+} __packed;
struct file_internal_info {
__le64 UniqueId; /* inode number */
-} __attribute__((packed)); /* level 0x3ee */
+} __packed; /* level 0x3ee */
struct file_mode_info {
__le32 Mode;
-} __attribute__((packed)); /* level 0x3f8 */
+} __packed; /* level 0x3f8 */
struct file_attrib_tag {
__le32 Attribute;
__le32 ReparseTag;
-} __attribute__((packed)); /* level 0x40b */
+} __packed; /* level 0x40b */
/********************************************************/
@@ -2491,71 +2291,7 @@ typedef struct {
char __pad;
DECLARE_FLEX_ARRAY(char, FileName);
};
-} __attribute__((packed)) FILE_UNIX_INFO; /* level 0x202 */
-
-typedef struct {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- char FileName[];
-} __attribute__((packed)) FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */
-
-typedef struct {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize; /* length of the xattrs */
- char FileName[];
-} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */
-
-typedef struct {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize; /* EA size */
- __le32 Reserved;
- __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
- char FileName[];
-} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */
-
-typedef struct {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize; /* length of the xattrs */
- __u8 ShortNameLength;
- __u8 Reserved;
- __u8 ShortName[24];
- char FileName[];
-} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */
+} __packed FILE_UNIX_INFO; /* level 0x202 */
typedef struct {
__u32 ResumeKey;
@@ -2570,7 +2306,7 @@ typedef struct {
__le16 Attributes; /* verify not u32 */
__u8 FileNameLength;
char FileName[];
-} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */
+} __packed FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */
struct fea {
@@ -2579,21 +2315,21 @@ struct fea {
__le16 value_len;
char name[];
/* optionally followed by value */
-} __attribute__((packed));
+} __packed;
/* flags for _FEA.fEA */
#define FEA_NEEDEA 0x80 /* need EA bit */
struct fealist {
__le32 list_len;
struct fea list;
-} __attribute__((packed));
+} __packed;
/* used to hold an arbitrary blob of data */
struct data_blob {
__u8 *data;
size_t length;
void (*free) (struct data_blob *data_blob);
-} __attribute__((packed));
+} __packed;
#ifdef CONFIG_CIFS_POSIX
@@ -2696,7 +2432,7 @@ struct xsymlink {
char cr2; /* \n */
/* if room left, then end with \n then 0x20s by convention but not required */
char path[1024];
-} __attribute__((packed));
+} __packed;
typedef struct file_xattr_info {
/* BB do we need another field for flags? BB */
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index 075985bfb13a..f8c0615d4ee4 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -9,6 +9,7 @@
#define _CIFSPROTO_H
#include <linux/nls.h>
#include <linux/ctype.h>
+#include "cifsglob.h"
#include "trace.h"
#ifdef CONFIG_CIFS_DFS_UPCALL
#include "dfs_cache.h"
@@ -29,8 +30,9 @@ extern void cifs_buf_release(void *);
extern struct smb_hdr *cifs_small_buf_get(void);
extern void cifs_small_buf_release(void *);
extern void free_rsp_buf(int, void *);
-extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *,
- unsigned int /* length */);
+extern int smb_send_kvec(struct TCP_Server_Info *server,
+ struct msghdr *msg,
+ size_t *sent);
extern unsigned int _get_xid(void);
extern void _free_xid(unsigned int);
#define get_xid() \
@@ -78,11 +80,10 @@ extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx,
struct cifs_sb_info *cifs_sb,
struct cifs_tcon *tcon,
int add_treename);
-extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
char *cifs_build_devname(char *nodename, const char *prepath);
-extern void delete_mid(struct mid_q_entry *mid);
-void __release_mid(struct kref *refcount);
-extern void cifs_wake_up_task(struct mid_q_entry *mid);
+void delete_mid(struct TCP_Server_Info *server, struct mid_q_entry *mid);
+void __release_mid(struct TCP_Server_Info *server, struct mid_q_entry *mid);
+void cifs_wake_up_task(struct TCP_Server_Info *server, struct mid_q_entry *mid);
extern int cifs_handle_standard(struct TCP_Server_Info *server,
struct mid_q_entry *mid);
extern char *smb3_fs_context_fullpath(const struct smb3_fs_context *ctx,
@@ -93,10 +94,10 @@ extern int cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs);
extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs);
extern int cifs_discard_remaining_data(struct TCP_Server_Info *server);
extern int cifs_call_async(struct TCP_Server_Info *server,
- struct smb_rqst *rqst,
- mid_receive_t *receive, mid_callback_t *callback,
- mid_handle_t *handle, void *cbdata, const int flags,
- const struct cifs_credits *exist_credits);
+ struct smb_rqst *rqst,
+ mid_receive_t receive, mid_callback_t callback,
+ mid_handle_t handle, void *cbdata, const int flags,
+ const struct cifs_credits *exist_credits);
extern struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses);
extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
@@ -107,32 +108,42 @@ extern int compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
const int flags, const int num_rqst,
struct smb_rqst *rqst, int *resp_buf_type,
struct kvec *resp_iov);
-extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
- struct smb_hdr * /* input */ ,
- struct smb_hdr * /* out */ ,
- int * /* bytes returned */ , const int);
-extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
- char *in_buf, int flags);
-extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *,
- struct TCP_Server_Info *,
- struct smb_rqst *);
-extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *,
- struct smb_rqst *);
+int SendReceive(const unsigned int xid, struct cifs_ses *ses,
+ struct smb_hdr *in_buf, unsigned int in_len,
+ struct smb_hdr *out_buf, int *pbytes_returned, const int flags);
+int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
+ char *in_buf, unsigned int in_len, int flags);
+int cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server);
+struct mid_q_entry *cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored,
+ struct smb_rqst *rqst);
+struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *server,
+ struct smb_rqst *rqst);
+int __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
+ struct smb_rqst *rqst);
extern int cifs_check_receive(struct mid_q_entry *mid,
struct TCP_Server_Info *server, bool log_error);
+int wait_for_free_request(struct TCP_Server_Info *server, const int flags,
+ unsigned int *instance);
extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
size_t size, size_t *num,
struct cifs_credits *credits);
+
+static inline int
+send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ struct smb_rqst *rqst, struct mid_q_entry *mid,
+ unsigned int xid)
+{
+ return server->ops->send_cancel ?
+ server->ops->send_cancel(ses, server, rqst, mid, xid) : 0;
+}
+
+int wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ);
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
struct kvec *, int /* nvec to send */,
int * /* type of buf returned */, const int flags,
struct kvec * /* resp vec */);
-extern int SendReceiveBlockingLock(const unsigned int xid,
- struct cifs_tcon *ptcon,
- struct smb_hdr *in_buf,
- struct smb_hdr *out_buf,
- int *bytes_returned);
+void smb2_query_server_interfaces(struct work_struct *work);
void
cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
bool all_channels);
@@ -141,15 +152,13 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session);
extern int cifs_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session);
-extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
+int checkSMB(char *buf, unsigned int pdu_len, unsigned int len,
+ struct TCP_Server_Info *srvr);
extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
extern bool backup_cred(struct cifs_sb_info *);
extern bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 eof,
bool from_readdir);
-extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
- unsigned int bytes_written);
-void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t result,
- bool was_async);
+void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t result);
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, int);
extern int cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
int flags,
@@ -160,21 +169,22 @@ extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
struct cifsFileInfo **ret_file);
+extern int cifs_get_hardlink_path(struct cifs_tcon *tcon, struct inode *inode,
+ struct file *file);
extern unsigned int smbCalcSize(void *buf);
extern int decode_negTokenInit(unsigned char *security_blob, int length,
struct TCP_Server_Info *server);
extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port);
extern int map_smb_to_linux_error(char *buf, bool logErr);
-extern int map_and_check_smb_error(struct mid_q_entry *mid, bool logErr);
-extern void header_assemble(struct smb_hdr *, char /* command */ ,
- const struct cifs_tcon *, int /* length of
- fixed section (word count) in two byte units */);
+extern int map_and_check_smb_error(struct TCP_Server_Info *server,
+ struct mid_q_entry *mid, bool logErr);
+unsigned int header_assemble(struct smb_hdr *buffer, char smb_command,
+ const struct cifs_tcon *treeCon, int word_count
+ /* length of fixed section word count in two byte units */);
extern int small_smb_init_no_tc(const int smb_cmd, const int wct,
struct cifs_ses *ses,
void **request_buf);
-extern enum securityEnum select_sectype(struct TCP_Server_Info *server,
- enum securityEnum requested);
extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp);
@@ -249,7 +259,7 @@ extern unsigned int setup_special_mode_ACE(struct smb_ace *pace,
__u64 nmode);
extern unsigned int setup_special_user_owner_ACE(struct smb_ace *pace);
-extern void dequeue_mid(struct mid_q_entry *mid, bool malformed);
+void dequeue_mid(struct TCP_Server_Info *server, struct mid_q_entry *mid, bool malformed);
extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
unsigned int to_read);
extern ssize_t cifs_discard_from_socket(struct TCP_Server_Info *server,
@@ -292,8 +302,8 @@ extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode);
extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon);
-extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
- const char *path);
+void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
+ struct dentry *dentry);
extern void cifs_mark_open_handles_for_deleted_file(struct inode *inode,
const char *path);
@@ -314,8 +324,7 @@ extern void cifs_move_llist(struct list_head *source, struct list_head *dest);
extern void cifs_free_llist(struct list_head *llist);
extern void cifs_del_lock_waiters(struct cifsLockInfo *lock);
-extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon,
- const struct nls_table *nlsc);
+int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon);
extern int cifs_negotiate_protocol(const unsigned int xid,
struct cifs_ses *ses,
@@ -391,6 +400,10 @@ extern int CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon);
extern int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,
struct kstatfs *FSData);
+extern int SMBSetInformation(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fileName, __le32 attributes, __le64 write_time,
+ const struct nls_table *nls_codepage,
+ struct cifs_sb_info *cifs_sb);
extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const FILE_BASIC_INFO *data,
const struct nls_table *nls_codepage,
@@ -475,9 +488,14 @@ extern int cifs_query_reparse_point(const unsigned int xid,
const char *full_path,
u32 *tag, struct kvec *rsp,
int *rsp_buftype);
-extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
- __u16 fid, char **symlinkinfo,
- const struct nls_table *nls_codepage);
+extern struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
+ struct super_block *sb,
+ const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const char *full_path,
+ bool directory,
+ struct kvec *reparse_iov,
+ struct kvec *xattr_iov);
extern int CIFSSMB_set_compression(const unsigned int xid,
struct cifs_tcon *tcon, __u16 fid);
extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms,
@@ -536,12 +554,9 @@ extern void tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
__u32 *pexpected_response_sequence_number);
-extern int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
- __u32 *);
-extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *);
-extern int cifs_verify_signature(struct smb_rqst *rqst,
- struct TCP_Server_Info *server,
- __u32 expected_sequence_number);
+int cifs_verify_signature(struct smb_rqst *rqst,
+ struct TCP_Server_Info *server,
+ __u32 expected_sequence_number);
extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server);
extern int calc_seckey(struct cifs_ses *);
@@ -561,7 +576,7 @@ extern int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
const struct nls_table *nls_codepage,
struct cifs_sb_info *cifs_sb);
extern int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon,
- __u16 fid, struct smb_ntsd **acl_inf, __u32 *buflen);
+ __u16 fid, struct smb_ntsd **acl_inf, __u32 *buflen, __u32 info);
extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16,
struct smb_ntsd *pntsd, __u32 len, int aclflag);
extern int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon,
@@ -574,7 +589,7 @@ extern int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
const struct nls_table *nls_codepage, int remap);
extern int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon,
const int netfid, __u64 *pExtAttrBits, __u64 *pMask);
-#endif /* CIFS_ALLOW_INSECURE_LEGACY */
+#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
extern void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb);
extern bool couldbe_mf_symlink(const struct cifs_fattr *fattr);
extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
@@ -587,6 +602,8 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
extern struct TCP_Server_Info *
cifs_find_tcp_session(struct smb3_fs_context *ctx);
+struct cifs_tcon *cifs_setup_ipc(struct cifs_ses *ses, bool seal);
+
void __cifs_put_smb_ses(struct cifs_ses *ses);
extern struct cifs_ses *
@@ -596,7 +613,6 @@ int cifs_async_readv(struct cifs_io_subrequest *rdata);
int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);
void cifs_async_writev(struct cifs_io_subrequest *wdata);
-void cifs_writev_complete(struct work_struct *work);
int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const unsigned char *path, char *pbuf,
@@ -605,9 +621,8 @@ 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,
- struct TCP_Server_Info *server, char *signature,
- struct shash_desc *shash);
+int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
+ char *signature, struct cifs_calc_sig_ctx *ctx);
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
enum securityEnum);
@@ -615,10 +630,9 @@ int cifs_alloc_hash(const char *name, struct shash_desc **sdesc);
void cifs_free_hash(struct shash_desc **sdesc);
int cifs_try_adding_channels(struct cifs_ses *ses);
-bool is_server_using_iface(struct TCP_Server_Info *server,
- struct cifs_server_iface *iface);
+int smb3_update_ses_channels(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ bool from_reconnect, bool disable_mchan);
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
-void cifs_ses_mark_for_reconnect(struct cifs_ses *ses);
int
cifs_ses_get_chan_index(struct cifs_ses *ses,
@@ -642,7 +656,7 @@ bool
cifs_chan_is_iface_active(struct cifs_ses *ses,
struct TCP_Server_Info *server);
void
-cifs_disable_secondary_channels(struct cifs_ses *ses);
+cifs_decrease_secondary_channels(struct cifs_ses *ses, bool disable_mchan);
void
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server);
int
@@ -661,7 +675,8 @@ char *extract_hostname(const char *unc);
char *extract_sharename(const char *unc);
int parse_reparse_point(struct reparse_data_buffer *buf,
u32 plen, struct cifs_sb_info *cifs_sb,
- bool unicode, struct cifs_open_info_data *data);
+ const char *full_path,
+ struct cifs_open_info_data *data);
int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev,
@@ -669,6 +684,7 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev);
+umode_t wire_mode_to_posix(u32 wire, bool is_dir);
#ifdef CONFIG_CIFS_DFS_UPCALL
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
@@ -681,7 +697,7 @@ static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
}
int match_target_ip(struct TCP_Server_Info *server,
- const char *share, size_t share_len,
+ const char *host, size_t hostlen,
bool *result);
int cifs_inval_name_dfs_link_error(const unsigned int xid,
struct cifs_tcon *tcon,
@@ -743,9 +759,15 @@ static inline bool dfs_src_pathname_equal(const char *s1, const char *s2)
return true;
}
-static inline void release_mid(struct mid_q_entry *mid)
+static inline void smb_get_mid(struct mid_q_entry *mid)
{
- kref_put(&mid->refcount, __release_mid);
+ refcount_inc(&mid->refcount);
+}
+
+static inline void release_mid(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+ if (refcount_dec_and_test(&mid->refcount))
+ __release_mid(server, mid);
}
static inline void cifs_free_open_info(struct cifs_open_info_data *data)
@@ -755,4 +777,110 @@ static inline void cifs_free_open_info(struct cifs_open_info_data *data)
memset(data, 0, sizeof(*data));
}
+static inline int smb_EIO(enum smb_eio_trace trace)
+{
+ trace_smb3_eio(trace, 0, 0);
+ return -EIO;
+}
+
+static inline int smb_EIO1(enum smb_eio_trace trace, unsigned long info)
+{
+ trace_smb3_eio(trace, info, 0);
+ return -EIO;
+}
+
+static inline int smb_EIO2(enum smb_eio_trace trace, unsigned long info, unsigned long info2)
+{
+ trace_smb3_eio(trace, info, info2);
+ return -EIO;
+}
+
+static inline int cifs_get_num_sgs(const struct smb_rqst *rqst,
+ int num_rqst,
+ const u8 *sig)
+{
+ unsigned int len, skip;
+ unsigned int nents = 0;
+ unsigned long addr;
+ size_t data_size;
+ int i, j;
+
+ /*
+ * The first rqst has a transform header where the first 20 bytes are
+ * not part of the encrypted blob.
+ */
+ skip = 20;
+
+ /* Assumes the first rqst has a transform header as the first iov.
+ * I.e.
+ * rqst[0].rq_iov[0] is transform header
+ * rqst[0].rq_iov[1+] data to be encrypted/decrypted
+ * rqst[1+].rq_iov[0+] data to be encrypted/decrypted
+ */
+ for (i = 0; i < num_rqst; i++) {
+ data_size = iov_iter_count(&rqst[i].rq_iter);
+
+ /* We really don't want a mixture of pinned and unpinned pages
+ * in the sglist. It's hard to keep track of which is what.
+ * Instead, we convert to a BVEC-type iterator higher up.
+ */
+ if (data_size &&
+ WARN_ON_ONCE(user_backed_iter(&rqst[i].rq_iter)))
+ return smb_EIO(smb_eio_trace_user_iter);
+
+ /* We also don't want to have any extra refs or pins to clean
+ * up in the sglist.
+ */
+ if (data_size &&
+ WARN_ON_ONCE(iov_iter_extract_will_pin(&rqst[i].rq_iter)))
+ return smb_EIO(smb_eio_trace_extract_will_pin);
+
+ for (j = 0; j < rqst[i].rq_nvec; j++) {
+ struct kvec *iov = &rqst[i].rq_iov[j];
+
+ addr = (unsigned long)iov->iov_base + skip;
+ if (is_vmalloc_or_module_addr((void *)addr)) {
+ len = iov->iov_len - skip;
+ nents += DIV_ROUND_UP(offset_in_page(addr) + len,
+ PAGE_SIZE);
+ } else {
+ nents++;
+ }
+ skip = 0;
+ }
+ if (data_size)
+ nents += iov_iter_npages(&rqst[i].rq_iter, INT_MAX);
+ }
+ nents += DIV_ROUND_UP(offset_in_page(sig) + SMB2_SIGNATURE_SIZE, PAGE_SIZE);
+ return nents;
+}
+
+/* We can not use the normal sg_set_buf() as we will sometimes pass a
+ * stack object as buf.
+ */
+static inline void cifs_sg_set_buf(struct sg_table *sgtable,
+ const void *buf,
+ unsigned int buflen)
+{
+ unsigned long addr = (unsigned long)buf;
+ unsigned int off = offset_in_page(addr);
+
+ addr &= PAGE_MASK;
+ if (is_vmalloc_or_module_addr((void *)addr)) {
+ do {
+ unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off);
+
+ sg_set_page(&sgtable->sgl[sgtable->nents++],
+ vmalloc_to_page((void *)addr), len, off);
+
+ off = 0;
+ addr += PAGE_SIZE;
+ buflen -= len;
+ } while (buflen);
+ } else {
+ sg_set_page(&sgtable->sgl[sgtable->nents++],
+ virt_to_page((void *)addr), buflen, off);
+ }
+}
+
#endif /* _CIFSPROTO_H */
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index 4858331ee918..3db1a892c526 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -70,10 +70,9 @@ static struct {
static int
cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
{
- int rc;
- struct cifs_ses *ses;
struct TCP_Server_Info *server;
- struct nls_table *nls_codepage = NULL;
+ struct cifs_ses *ses;
+ int rc;
/*
* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
@@ -115,24 +114,26 @@ again:
mutex_lock(&ses->session_mutex);
/*
- * Recheck after acquire mutex. If another thread is negotiating
- * and the server never sends an answer the socket will be closed
- * and tcpStatus set to reconnect.
+ * Handle the case where a concurrent thread failed to negotiate or
+ * killed a channel.
*/
spin_lock(&server->srv_lock);
- if (server->tcpStatus == CifsNeedReconnect) {
+ switch (server->tcpStatus) {
+ case CifsExiting:
spin_unlock(&server->srv_lock);
mutex_unlock(&ses->session_mutex);
-
- if (tcon->retry)
- goto again;
- rc = -EHOSTDOWN;
- goto out;
+ return -EHOSTDOWN;
+ case CifsNeedReconnect:
+ spin_unlock(&server->srv_lock);
+ mutex_unlock(&ses->session_mutex);
+ if (!tcon->retry)
+ return -EHOSTDOWN;
+ goto again;
+ default:
+ break;
}
spin_unlock(&server->srv_lock);
- nls_codepage = ses->local_nls;
-
/*
* need to prevent multiple threads trying to simultaneously
* reconnect the same SMB session
@@ -155,8 +156,21 @@ again:
spin_unlock(&ses->ses_lock);
rc = cifs_negotiate_protocol(0, ses, server);
- if (!rc)
- rc = cifs_setup_session(0, ses, server, nls_codepage);
+ if (rc) {
+ mutex_unlock(&ses->session_mutex);
+ if (!tcon->retry)
+ return -EHOSTDOWN;
+ goto again;
+ }
+ rc = cifs_setup_session(0, ses, server, ses->local_nls);
+ if ((rc == -EACCES) || (rc == -EHOSTDOWN) || (rc == -EKEYREVOKED)) {
+ /*
+ * Try alternate password for next reconnect if an alternate
+ * password is available.
+ */
+ if (ses->password2)
+ swap(ses->password2, ses->password);
+ }
/* do we need to reconnect tcon? */
if (rc || !tcon->need_reconnect) {
@@ -166,7 +180,7 @@ again:
skip_sess_setup:
cifs_mark_open_files_invalid(tcon);
- rc = cifs_tree_connect(0, tcon, nls_codepage);
+ rc = cifs_tree_connect(0, tcon);
mutex_unlock(&ses->session_mutex);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
@@ -212,6 +226,7 @@ static int
small_smb_init(int smb_command, int wct, struct cifs_tcon *tcon,
void **request_buf)
{
+ unsigned int in_len;
int rc;
rc = cifs_reconnect_tcon(tcon, smb_command);
@@ -224,13 +239,13 @@ small_smb_init(int smb_command, int wct, struct cifs_tcon *tcon,
return -ENOMEM;
}
- header_assemble((struct smb_hdr *) *request_buf, smb_command,
- tcon, wct);
+ in_len = header_assemble((struct smb_hdr *) *request_buf, smb_command,
+ tcon, wct);
if (tcon != NULL)
cifs_stats_inc(&tcon->num_smbs_sent);
- return 0;
+ return in_len;
}
int
@@ -241,7 +256,7 @@ small_smb_init_no_tc(const int smb_command, const int wct,
struct smb_hdr *buffer;
rc = small_smb_init(smb_command, wct, NULL, request_buf);
- if (rc)
+ if (rc < 0)
return rc;
buffer = (struct smb_hdr *)*request_buf;
@@ -264,6 +279,8 @@ static int
__smb_init(int smb_command, int wct, struct cifs_tcon *tcon,
void **request_buf, void **response_buf)
{
+ unsigned int in_len;
+
*request_buf = cifs_buf_get();
if (*request_buf == NULL) {
/* BB should we add a retry in here if not a writepage? */
@@ -276,13 +293,13 @@ __smb_init(int smb_command, int wct, struct cifs_tcon *tcon,
if (response_buf)
*response_buf = *request_buf;
- header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
- wct);
+ in_len = header_assemble((struct smb_hdr *)*request_buf, smb_command, tcon,
+ wct);
if (tcon != NULL)
cifs_stats_inc(&tcon->num_smbs_sent);
- return 0;
+ return in_len;
}
/* If the return code is zero, this function must fill in request_buf pointer */
@@ -347,7 +364,7 @@ vt2_err:
}
static int
-decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr)
+decode_ext_sec_blob(struct cifs_ses *ses, SMB_NEGOTIATE_RSP *pSMBr)
{
int rc = 0;
u16 count;
@@ -356,7 +373,8 @@ decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr)
count = get_bcc(&pSMBr->hdr);
if (count < SMB1_CLIENT_GUID_SIZE)
- return -EIO;
+ return smb_EIO2(smb_eio_trace_neg_sec_blob_too_small,
+ count, SMB1_CLIENT_GUID_SIZE);
spin_lock(&cifs_tcp_ses_lock);
if (server->srv_count > 1) {
@@ -405,8 +423,9 @@ CIFSSMBNegotiate(const unsigned int xid,
struct cifs_ses *ses,
struct TCP_Server_Info *server)
{
- NEGOTIATE_REQ *pSMB;
- NEGOTIATE_RSP *pSMBr;
+ SMB_NEGOTIATE_REQ *pSMB;
+ SMB_NEGOTIATE_RSP *pSMBr;
+ unsigned int in_len;
int rc = 0;
int bytes_returned;
int i;
@@ -414,16 +433,20 @@ CIFSSMBNegotiate(const unsigned int xid,
if (!server) {
WARN(1, "%s: server is NULL!\n", __func__);
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
}
rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
(void **) &pSMB, (void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->hdr.Mid = get_next_mid(server);
- pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
+ pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
+
+ if (ses->unicode != 0)
+ pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
if (should_set_ext_sec_flag(ses->sectype)) {
cifs_dbg(FYI, "Requesting extended security\n");
@@ -441,10 +464,10 @@ CIFSSMBNegotiate(const unsigned int xid,
memcpy(&pSMB->DialectsArray[count], protocols[i].name, len);
count += len;
}
- inc_rfc1001_len(pSMB, count);
+ in_len += count;
pSMB->ByteCount = cpu_to_le16(count);
- rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc != 0)
goto neg_err_exit;
@@ -481,6 +504,7 @@ CIFSSMBNegotiate(const unsigned int xid,
server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
cifs_dbg(NOISY, "Max buf = %d\n", ses->server->maxBuf);
server->capabilities = le32_to_cpu(pSMBr->Capabilities);
+ server->session_key_id = pSMBr->SessionKey;
server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
server->timeAdj *= 60;
@@ -493,7 +517,8 @@ CIFSSMBNegotiate(const unsigned int xid,
server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
rc = decode_ext_sec_blob(ses, pSMBr);
} else if (server->sec_mode & SECMODE_PW_ENCRYPT) {
- rc = -EIO; /* no crypt key only if plain text pwd */
+ /* no crypt key only if plain text pwd */
+ rc = smb_EIO(smb_eio_trace_neg_no_crypt_key);
} else {
server->negflavor = CIFS_NEGFLAVOR_UNENCAP;
server->capabilities &= ~CAP_EXTENDED_SECURITY;
@@ -512,13 +537,14 @@ int
CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon)
{
struct smb_hdr *smb_buffer;
+ unsigned int in_len;
int rc = 0;
cifs_dbg(FYI, "In tree disconnect\n");
/* BB: do we need to check this? These should never be NULL. */
if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
/*
* No need to return error on this operation if tid invalidated and
@@ -529,16 +555,17 @@ CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon)
spin_lock(&tcon->ses->chan_lock);
if ((tcon->need_reconnect) || CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses)) {
spin_unlock(&tcon->ses->chan_lock);
- return -EIO;
+ return smb_EIO(smb_eio_trace_tdis_in_reconnect);
}
spin_unlock(&tcon->ses->chan_lock);
rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
(void **)&smb_buffer);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
- rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0);
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, in_len, 0);
cifs_small_buf_release(smb_buffer);
if (rc)
cifs_dbg(FYI, "Tree disconnect failed %d\n", rc);
@@ -559,12 +586,11 @@ CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon)
* FIXME: maybe we should consider checking that the reply matches request?
*/
static void
-cifs_echo_callback(struct mid_q_entry *mid)
+cifs_echo_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
- struct TCP_Server_Info *server = mid->callback_data;
struct cifs_credits credits = { .value = 1, .instance = 0 };
- release_mid(mid);
+ release_mid(server, mid);
add_credits(server, &credits, CIFS_ECHO_OP);
}
@@ -573,15 +599,19 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
{
ECHO_REQ *smb;
int rc = 0;
- struct kvec iov[2];
- struct smb_rqst rqst = { .rq_iov = iov,
- .rq_nvec = 2 };
+ struct kvec iov[1];
+ struct smb_rqst rqst = {
+ .rq_iov = iov,
+ .rq_nvec = ARRAY_SIZE(iov),
+ };
+ unsigned int in_len;
cifs_dbg(FYI, "In echo request\n");
rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (server->capabilities & CAP_UNICODE)
smb->hdr.Flags2 |= SMBFLG2_UNICODE;
@@ -592,12 +622,10 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
put_unaligned_le16(1, &smb->EchoCount);
put_bcc(1, &smb->hdr);
smb->Data[0] = 'a';
- inc_rfc1001_len(smb, 3);
+ in_len += 3;
- iov[0].iov_len = 4;
+ iov[0].iov_len = in_len;
iov[0].iov_base = smb;
- iov[1].iov_len = get_rfc1002_length(smb);
- iov[1].iov_base = (char *)smb + 4;
rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL,
server, CIFS_NON_BLOCKING | CIFS_ECHO_OP, NULL);
@@ -613,6 +641,7 @@ int
CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses)
{
LOGOFF_ANDX_REQ *pSMB;
+ unsigned int in_len;
int rc = 0;
cifs_dbg(FYI, "In SMBLogoff for session disconnect\n");
@@ -623,7 +652,7 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses)
* should probably be a BUG()
*/
if (!ses || !ses->server)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
mutex_lock(&ses->session_mutex);
spin_lock(&ses->chan_lock);
@@ -635,10 +664,11 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses)
spin_unlock(&ses->chan_lock);
rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
- if (rc) {
+ if (rc < 0) {
mutex_unlock(&ses->session_mutex);
return rc;
}
+ in_len = rc;
pSMB->hdr.Mid = get_next_mid(ses->server);
@@ -648,7 +678,7 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses)
pSMB->hdr.Uid = ses->Suid;
pSMB->AndXCommand = 0xFF;
- rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0);
+ rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, in_len, 0);
cifs_small_buf_release(pSMB);
session_already_dead:
mutex_unlock(&ses->session_mutex);
@@ -669,6 +699,7 @@ CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
TRANSACTION2_SPI_REQ *pSMB = NULL;
TRANSACTION2_SPI_RSP *pSMBr = NULL;
struct unlink_psx_rq *pRqD;
+ unsigned int in_len;
int name_len;
int rc = 0;
int bytes_returned = 0;
@@ -678,8 +709,9 @@ CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
PsxDelete:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -700,14 +732,11 @@ PsxDelete:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_spi_req,
- InformationLevel) - 4;
+ InformationLevel);
offset = param_offset + params;
- /* Setup pointer to Request Data (inode type).
- * Note that SMB offsets are from the beginning of SMB which is 4 bytes
- * in, after RFC1001 field
- */
- pRqD = (struct unlink_psx_rq *)((char *)(pSMB) + offset + 4);
+ /* Setup pointer to Request Data (inode type). */
+ pRqD = (struct unlink_psx_rq *)((char *)(pSMB) + offset);
pRqD->type = cpu_to_le16(type);
pSMB->ParameterOffset = cpu_to_le16(param_offset);
pSMB->DataOffset = cpu_to_le16(offset);
@@ -722,9 +751,9 @@ PsxDelete:
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc)
cifs_dbg(FYI, "Posix delete returned %d\n", rc);
@@ -744,6 +773,7 @@ CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
{
DELETE_FILE_REQ *pSMB = NULL;
DELETE_FILE_RSP *pSMBr = NULL;
+ unsigned int in_len;
int rc = 0;
int bytes_returned;
int name_len;
@@ -752,8 +782,9 @@ CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
DelFileRetry:
rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len = cifsConvertToUTF16((__le16 *) pSMB->fileName, name,
@@ -767,9 +798,9 @@ DelFileRetry:
pSMB->SearchAttributes =
cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
pSMB->BufferFormat = 0x04;
- inc_rfc1001_len(pSMB, name_len + 1);
+ in_len += name_len + 1;
pSMB->ByteCount = cpu_to_le16(name_len + 1);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes);
if (rc)
@@ -788,6 +819,7 @@ CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
{
DELETE_DIRECTORY_REQ *pSMB = NULL;
DELETE_DIRECTORY_RSP *pSMBr = NULL;
+ unsigned int in_len;
int rc = 0;
int bytes_returned;
int name_len;
@@ -797,8 +829,9 @@ CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
RmDirRetry:
rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name,
@@ -811,9 +844,9 @@ RmDirRetry:
}
pSMB->BufferFormat = 0x04;
- inc_rfc1001_len(pSMB, name_len + 1);
+ in_len += name_len + 1;
pSMB->ByteCount = cpu_to_le16(name_len + 1);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_rmdirs);
if (rc)
@@ -833,6 +866,7 @@ CIFSSMBMkDir(const unsigned int xid, struct inode *inode, umode_t mode,
int rc = 0;
CREATE_DIRECTORY_REQ *pSMB = NULL;
CREATE_DIRECTORY_RSP *pSMBr = NULL;
+ unsigned int in_len;
int bytes_returned;
int name_len;
int remap = cifs_remap(cifs_sb);
@@ -841,8 +875,9 @@ CIFSSMBMkDir(const unsigned int xid, struct inode *inode, umode_t mode,
MkDirRetry:
rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name,
@@ -855,9 +890,9 @@ MkDirRetry:
}
pSMB->BufferFormat = 0x04;
- inc_rfc1001_len(pSMB, name_len + 1);
+ in_len += name_len + 1;
pSMB->ByteCount = cpu_to_le16(name_len + 1);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_mkdirs);
if (rc)
@@ -878,6 +913,7 @@ CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon,
{
TRANSACTION2_SPI_REQ *pSMB = NULL;
TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ unsigned int in_len;
int name_len;
int rc = 0;
int bytes_returned = 0;
@@ -889,8 +925,9 @@ CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon,
PsxCreat:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -912,10 +949,9 @@ PsxCreat:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_spi_req,
- InformationLevel) - 4;
+ InformationLevel);
offset = param_offset + params;
- /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */
- pdata = (OPEN_PSX_REQ *)((char *)(pSMB) + offset + 4);
+ pdata = (OPEN_PSX_REQ *)((char *)(pSMB) + offset);
pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
pdata->Permissions = cpu_to_le64(mode);
pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
@@ -933,9 +969,9 @@ PsxCreat:
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Posix create returned %d\n", rc);
@@ -946,13 +982,14 @@ PsxCreat:
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc || get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)) {
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_create_rsp_too_small,
+ get_bcc(&pSMBr->hdr), sizeof(OPEN_PSX_RSP));
goto psx_create_err;
}
/* copy return information to pRetData */
- psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
- + le16_to_cpu(pSMBr->t2.DataOffset));
+ psx_rsp = (OPEN_PSX_RSP *)
+ ((char *)pSMBr + le16_to_cpu(pSMBr->t2.DataOffset));
*pOplock = le16_to_cpu(psx_rsp->OplockFlags);
if (netfid)
@@ -972,9 +1009,9 @@ PsxCreat:
pRetData->Type = cpu_to_le32(-1);
goto psx_create_err;
}
- memcpy((char *) pRetData,
- (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
- sizeof(FILE_UNIX_BASIC_INFO));
+ memcpy(pRetData,
+ (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
+ sizeof(*pRetData));
}
psx_create_err:
@@ -1024,15 +1061,31 @@ static __u16 convert_disposition(int disposition)
static int
access_flags_to_smbopen_mode(const int access_flags)
{
- int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
-
- if (masked_flags == GENERIC_READ)
- return SMBOPEN_READ;
- else if (masked_flags == GENERIC_WRITE)
+ /*
+ * SYSTEM_SECURITY grants both read and write access to SACL, treat is as read/write.
+ * MAXIMUM_ALLOWED grants as many access as possible, so treat it as read/write too.
+ * SYNCHRONIZE as is does not grant any specific access, so do not check its mask.
+ * If only SYNCHRONIZE bit is specified then fallback to read access.
+ */
+ bool with_write_flags = access_flags & (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA |
+ FILE_DELETE_CHILD | FILE_WRITE_ATTRIBUTES | DELETE |
+ WRITE_DAC | WRITE_OWNER | SYSTEM_SECURITY |
+ MAXIMUM_ALLOWED | GENERIC_WRITE | GENERIC_ALL);
+ bool with_read_flags = access_flags & (FILE_READ_DATA | FILE_READ_EA | FILE_EXECUTE |
+ FILE_READ_ATTRIBUTES | READ_CONTROL |
+ SYSTEM_SECURITY | MAXIMUM_ALLOWED | GENERIC_ALL |
+ GENERIC_EXECUTE | GENERIC_READ);
+ bool with_execute_flags = access_flags & (FILE_EXECUTE | MAXIMUM_ALLOWED | GENERIC_ALL |
+ GENERIC_EXECUTE);
+
+ if (with_write_flags && with_read_flags)
+ return SMBOPEN_READWRITE;
+ else if (with_write_flags)
return SMBOPEN_WRITE;
-
- /* just go for read/write */
- return SMBOPEN_READWRITE;
+ else if (with_execute_flags)
+ return SMBOPEN_EXECUTE;
+ else
+ return SMBOPEN_READ;
}
int
@@ -1045,6 +1098,7 @@ SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon,
int rc;
OPENX_REQ *pSMB = NULL;
OPENX_RSP *pSMBr = NULL;
+ unsigned int in_len;
int bytes_returned;
int name_len;
__u16 count;
@@ -1052,8 +1106,9 @@ SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon,
OldOpenRetry:
rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->AndXCommand = 0xFF; /* none */
@@ -1096,10 +1151,10 @@ OldOpenRetry:
pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
count += name_len;
- inc_rfc1001_len(pSMB, count);
+ in_len += count;
pSMB->ByteCount = cpu_to_le16(count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *)pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_opens);
if (rc) {
@@ -1129,7 +1184,7 @@ OldOpenRetry:
cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
pfile_info->EndOfFile = pfile_info->AllocationSize;
pfile_info->NumberOfLinks = cpu_to_le32(1);
- pfile_info->DeletePending = 0;
+ pfile_info->DeletePending = 0; /* successful open = not delete pending */
}
}
@@ -1157,12 +1212,14 @@ CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock,
int desired_access = oparms->desired_access;
int disposition = oparms->disposition;
const char *path = oparms->path;
+ unsigned int in_len;
openRetry:
rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **)&req,
(void **)&rsp);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
/* no commands go after this */
req->AndXCommand = 0xFF;
@@ -1220,10 +1277,10 @@ openRetry:
req->SecurityFlags = SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY;
count += name_len;
- inc_rfc1001_len(req, count);
+ in_len += count;
req->ByteCount = cpu_to_le16(count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req, in_len,
(struct smb_hdr *)rsp, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_opens);
if (rc) {
@@ -1254,30 +1311,21 @@ openRetry:
buf->AllocationSize = rsp->AllocationSize;
buf->EndOfFile = rsp->EndOfFile;
buf->NumberOfLinks = cpu_to_le32(1);
- buf->DeletePending = 0;
+ buf->DeletePending = 0; /* successful open = not delete pending */
}
cifs_buf_release(req);
return rc;
}
-static void cifs_readv_worker(struct work_struct *work)
-{
- struct cifs_io_subrequest *rdata =
- container_of(work, struct cifs_io_subrequest, subreq.work);
-
- netfs_read_subreq_terminated(&rdata->subreq, rdata->result, false);
-}
-
static void
-cifs_readv_callback(struct mid_q_entry *mid)
+cifs_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
struct cifs_io_subrequest *rdata = mid->callback_data;
struct netfs_inode *ictx = netfs_inode(rdata->rreq->inode);
struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink);
- struct TCP_Server_Info *server = tcon->ses->server;
struct smb_rqst rqst = { .rq_iov = rdata->iov,
- .rq_nvec = 2,
+ .rq_nvec = 1,
.rq_iter = rdata->subreq.io_iter };
struct cifs_credits credits = {
.value = 1,
@@ -1285,6 +1333,8 @@ cifs_readv_callback(struct mid_q_entry *mid)
.rreq_debug_id = rdata->rreq->debug_id,
.rreq_debug_index = rdata->subreq.debug_index,
};
+ unsigned int rreq_debug_id = rdata->rreq->debug_id;
+ unsigned int subreq_debug_index = rdata->subreq.debug_index;
cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%zu\n",
__func__, mid->mid, mid->mid_state, rdata->result,
@@ -1308,7 +1358,12 @@ cifs_readv_callback(struct mid_q_entry *mid)
cifs_stats_bytes_read(tcon, rdata->got_bytes);
break;
case MID_REQUEST_SUBMITTED:
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_req_submitted);
+ goto do_retry;
case MID_RETRY_NEEDED:
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed);
+do_retry:
+ __set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags);
rdata->result = -EAGAIN;
if (server->sign && rdata->got_bytes)
/* reset bytes number since we can not check a sign */
@@ -1317,28 +1372,61 @@ cifs_readv_callback(struct mid_q_entry *mid)
task_io_account_read(rdata->got_bytes);
cifs_stats_bytes_read(tcon, rdata->got_bytes);
break;
+ case MID_RESPONSE_MALFORMED:
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_malformed);
+ rdata->result = smb_EIO(smb_eio_trace_read_rsp_malformed);
+ break;
default:
- rdata->result = -EIO;
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_unknown);
+ rdata->result = smb_EIO1(smb_eio_trace_read_mid_state_unknown,
+ mid->mid_state);
+ break;
}
if (rdata->result == -ENODATA) {
- __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
rdata->result = 0;
+ __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
+ trace_smb3_read_err(rdata->rreq->debug_id,
+ rdata->subreq.debug_index,
+ rdata->xid,
+ rdata->req->cfile->fid.persistent_fid,
+ tcon->tid, tcon->ses->Suid,
+ rdata->subreq.start + rdata->subreq.transferred,
+ rdata->subreq.len - rdata->subreq.transferred,
+ rdata->result);
} else {
size_t trans = rdata->subreq.transferred + rdata->got_bytes;
if (trans < rdata->subreq.len &&
- rdata->subreq.start + trans == ictx->remote_i_size) {
- __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
+ rdata->subreq.start + trans >= ictx->remote_i_size) {
rdata->result = 0;
+ __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
+ } else if (rdata->got_bytes > 0) {
+ __set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags);
}
+ if (rdata->got_bytes)
+ __set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags);
+ trace_smb3_read_done(rdata->rreq->debug_id,
+ rdata->subreq.debug_index,
+ rdata->xid,
+ rdata->req->cfile->fid.persistent_fid,
+ tcon->tid, tcon->ses->Suid,
+ rdata->subreq.start + rdata->subreq.transferred,
+ rdata->got_bytes);
}
+ trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, rdata->credits.value,
+ server->credits, server->in_flight,
+ 0, cifs_trace_rw_credits_read_response_clear);
rdata->credits.value = 0;
+ rdata->subreq.error = rdata->result;
rdata->subreq.transferred += rdata->got_bytes;
- INIT_WORK(&rdata->subreq.work, cifs_readv_worker);
- queue_work(cifsiod_wq, &rdata->subreq.work);
- release_mid(mid);
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_progress);
+ netfs_read_subreq_terminated(&rdata->subreq);
+ release_mid(server, mid);
add_credits(server, &credits, 0);
+ trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, 0,
+ server->credits, server->in_flight,
+ credits.value, cifs_trace_rw_credits_read_response_add);
}
/* cifs_async_readv - send an async write, and set up mid to handle result */
@@ -1350,7 +1438,8 @@ cifs_async_readv(struct cifs_io_subrequest *rdata)
int wct;
struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink);
struct smb_rqst rqst = { .rq_iov = rdata->iov,
- .rq_nvec = 2 };
+ .rq_nvec = 1 };
+ unsigned int in_len;
cifs_dbg(FYI, "%s: offset=%llu bytes=%zu\n",
__func__, rdata->subreq.start, rdata->subreq.len);
@@ -1361,13 +1450,14 @@ cifs_async_readv(struct cifs_io_subrequest *rdata)
wct = 10; /* old style read */
if ((rdata->subreq.start >> 32) > 0) {
/* can not handle this big offset for old */
- return -EIO;
+ return smb_EIO(smb_eio_trace_read_too_far);
}
}
rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **)&smb);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
smb->hdr.Pid = cpu_to_le16((__u16)rdata->req->pid);
smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->req->pid >> 16));
@@ -1391,9 +1481,14 @@ cifs_async_readv(struct cifs_io_subrequest *rdata)
/* 4 for RFC1001 length + 1 for BCC */
rdata->iov[0].iov_base = smb;
- rdata->iov[0].iov_len = 4;
- rdata->iov[1].iov_base = (char *)smb + 4;
- rdata->iov[1].iov_len = get_rfc1002_length(smb);
+ rdata->iov[0].iov_len = in_len;
+
+ trace_smb3_read_enter(rdata->rreq->debug_id,
+ rdata->subreq.debug_index,
+ rdata->xid,
+ rdata->req->cfile->fid.netfid,
+ tcon->tid, tcon->ses->Suid,
+ rdata->subreq.start, rdata->subreq.len);
rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive,
cifs_readv_callback, NULL, rdata, 0, NULL);
@@ -1420,6 +1515,7 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
__u16 netfid = io_parms->netfid;
__u64 offset = io_parms->offset;
struct cifs_tcon *tcon = io_parms->tcon;
+ unsigned int in_len;
unsigned int count = io_parms->length;
cifs_dbg(FYI, "Reading %d bytes on fid %d\n", count, netfid);
@@ -1429,14 +1525,15 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
wct = 10; /* old style read */
if ((offset >> 32) > 0) {
/* can not handle this big offset for old */
- return -EIO;
+ return smb_EIO(smb_eio_trace_read_too_far);
}
}
*nbytes = 0;
rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->hdr.Pid = cpu_to_le16((__u16)pid);
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
@@ -1464,7 +1561,7 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
}
iov[0].iov_base = (char *)pSMB;
- iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;
+ iov[0].iov_len = in_len;
rc = SendReceive2(xid, tcon->ses, iov, 1, &resp_buf_type,
CIFS_LOG_ERROR, &rsp_iov);
cifs_small_buf_release(pSMB);
@@ -1483,7 +1580,8 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
|| (data_length > count)) {
cifs_dbg(FYI, "bad length %d for count %d\n",
data_length, count);
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_read_overlarge,
+ data_length, count);
*nbytes = 0;
} else {
pReadData = (char *) (&pSMBr->hdr.Protocol) +
@@ -1528,7 +1626,7 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
__u16 netfid = io_parms->netfid;
__u64 offset = io_parms->offset;
struct cifs_tcon *tcon = io_parms->tcon;
- unsigned int count = io_parms->length;
+ unsigned int count = io_parms->length, in_len;
*nbytes = 0;
@@ -1542,14 +1640,15 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
wct = 12;
if ((offset >> 32) > 0) {
/* can not handle big offset for old srv */
- return -EIO;
+ return smb_EIO(smb_eio_trace_write_too_far);
}
}
rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->hdr.Pid = cpu_to_le16((__u16)pid);
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
@@ -1582,7 +1681,7 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
if (bytes_sent > count)
bytes_sent = count;
pSMB->DataOffset =
- cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
+ cpu_to_le16(offsetof(struct smb_com_write_req, Data));
if (buf)
memcpy(pSMB->Data, buf, bytes_sent);
else if (count != 0) {
@@ -1597,7 +1696,7 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
if (wct == 14)
pSMB->ByteCount = cpu_to_le16(byte_count);
@@ -1608,7 +1707,7 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
pSMBW->ByteCount = cpu_to_le16(byte_count);
}
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
if (rc) {
@@ -1640,10 +1739,9 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
* workqueue completion task.
*/
static void
-cifs_writev_callback(struct mid_q_entry *mid)
+cifs_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
struct cifs_io_subrequest *wdata = mid->callback_data;
- struct TCP_Server_Info *server = wdata->server;
struct cifs_tcon *tcon = tlink_tcon(wdata->req->cfile->tlink);
WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf;
struct cifs_credits credits = {
@@ -1673,17 +1771,32 @@ cifs_writev_callback(struct mid_q_entry *mid)
if (written > wdata->subreq.len)
written &= 0xFFFF;
- if (written < wdata->subreq.len)
+ if (written < wdata->subreq.len) {
result = -ENOSPC;
- else
+ } else {
result = written;
+ if (written > 0)
+ __set_bit(NETFS_SREQ_MADE_PROGRESS, &wdata->subreq.flags);
+ }
break;
case MID_REQUEST_SUBMITTED:
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_req_submitted);
+ __set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags);
+ result = -EAGAIN;
+ break;
case MID_RETRY_NEEDED:
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_retry_needed);
+ __set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags);
result = -EAGAIN;
break;
+ case MID_RESPONSE_MALFORMED:
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_malformed);
+ result = smb_EIO(smb_eio_trace_write_rsp_malformed);
+ break;
default:
- result = -EIO;
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_unknown);
+ result = smb_EIO1(smb_eio_trace_write_mid_state_unknown,
+ mid->mid_state);
break;
}
@@ -1692,8 +1805,8 @@ cifs_writev_callback(struct mid_q_entry *mid)
server->credits, server->in_flight,
0, cifs_trace_rw_credits_write_response_clear);
wdata->credits.value = 0;
- cifs_write_subrequest_terminated(wdata, result, true);
- release_mid(mid);
+ cifs_write_subrequest_terminated(wdata, result);
+ release_mid(server, mid);
trace_smb3_rw_credits(credits.rreq_debug_id, credits.rreq_debug_index, 0,
server->credits, server->in_flight,
credits.value, cifs_trace_rw_credits_write_response_add);
@@ -1705,11 +1818,12 @@ void
cifs_async_writev(struct cifs_io_subrequest *wdata)
{
int rc = -EACCES;
- WRITE_REQ *smb = NULL;
+ WRITE_REQ *req = NULL;
int wct;
struct cifs_tcon *tcon = tlink_tcon(wdata->req->cfile->tlink);
- struct kvec iov[2];
+ struct kvec iov[1];
struct smb_rqst rqst = { };
+ unsigned int in_len;
if (tcon->ses->capabilities & CAP_LARGE_FILES) {
wct = 14;
@@ -1717,56 +1831,54 @@ cifs_async_writev(struct cifs_io_subrequest *wdata)
wct = 12;
if (wdata->subreq.start >> 32 > 0) {
/* can not handle big offset for old srv */
- rc = -EIO;
+ rc = smb_EIO(smb_eio_trace_write_too_far);
goto out;
}
}
- rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **)&smb);
- if (rc)
+ rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **)&req);
+ if (rc < 0)
goto async_writev_out;
+ in_len = rc;
- smb->hdr.Pid = cpu_to_le16((__u16)wdata->req->pid);
- smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->req->pid >> 16));
+ req->hdr.Pid = cpu_to_le16((__u16)wdata->req->pid);
+ req->hdr.PidHigh = cpu_to_le16((__u16)(wdata->req->pid >> 16));
- smb->AndXCommand = 0xFF; /* none */
- smb->Fid = wdata->req->cfile->fid.netfid;
- smb->OffsetLow = cpu_to_le32(wdata->subreq.start & 0xFFFFFFFF);
+ req->AndXCommand = 0xFF; /* none */
+ req->Fid = wdata->req->cfile->fid.netfid;
+ req->OffsetLow = cpu_to_le32(wdata->subreq.start & 0xFFFFFFFF);
if (wct == 14)
- smb->OffsetHigh = cpu_to_le32(wdata->subreq.start >> 32);
- smb->Reserved = 0xFFFFFFFF;
- smb->WriteMode = 0;
- smb->Remaining = 0;
+ req->OffsetHigh = cpu_to_le32(wdata->subreq.start >> 32);
+ req->Reserved = 0xFFFFFFFF;
+ req->WriteMode = 0;
+ req->Remaining = 0;
- smb->DataOffset =
- cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
+ req->DataOffset =
+ cpu_to_le16(offsetof(struct smb_com_write_req, Data));
- /* 4 for RFC1001 length + 1 for BCC */
- iov[0].iov_len = 4;
- iov[0].iov_base = smb;
- iov[1].iov_len = get_rfc1002_length(smb) + 1;
- iov[1].iov_base = (char *)smb + 4;
+ iov[0].iov_base = req;
+ iov[0].iov_len = in_len + 1; /* +1 for BCC */
rqst.rq_iov = iov;
- rqst.rq_nvec = 2;
+ rqst.rq_nvec = 1;
rqst.rq_iter = wdata->subreq.io_iter;
cifs_dbg(FYI, "async write at %llu %zu bytes\n",
wdata->subreq.start, wdata->subreq.len);
- smb->DataLengthLow = cpu_to_le16(wdata->subreq.len & 0xFFFF);
- smb->DataLengthHigh = cpu_to_le16(wdata->subreq.len >> 16);
+ req->DataLengthLow = cpu_to_le16(wdata->subreq.len & 0xFFFF);
+ req->DataLengthHigh = cpu_to_le16(wdata->subreq.len >> 16);
if (wct == 14) {
- inc_rfc1001_len(&smb->hdr, wdata->subreq.len + 1);
- put_bcc(wdata->subreq.len + 1, &smb->hdr);
+ in_len += wdata->subreq.len + 1;
+ put_bcc(wdata->subreq.len + 1, &req->hdr);
} else {
/* wct == 12 */
- struct smb_com_writex_req *smbw =
- (struct smb_com_writex_req *)smb;
- inc_rfc1001_len(&smbw->hdr, wdata->subreq.len + 5);
- put_bcc(wdata->subreq.len + 5, &smbw->hdr);
- iov[1].iov_len += 4; /* pad bigger by four bytes */
+ struct smb_com_writex_req *reqw =
+ (struct smb_com_writex_req *)req;
+ in_len += wdata->subreq.len + 5;
+ put_bcc(wdata->subreq.len + 5, &reqw->hdr);
+ iov[0].iov_len += 4; /* pad bigger by four bytes */
}
rc = cifs_call_async(tcon->ses->server, &rqst, NULL,
@@ -1776,11 +1888,11 @@ cifs_async_writev(struct cifs_io_subrequest *wdata)
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
async_writev_out:
- cifs_small_buf_release(smb);
+ cifs_small_buf_release(req);
out:
if (rc) {
add_credits_and_wake_if(wdata->server, &wdata->credits, 0);
- cifs_write_subrequest_terminated(wdata, rc, false);
+ cifs_write_subrequest_terminated(wdata, rc);
}
}
@@ -1799,6 +1911,7 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
struct cifs_tcon *tcon = io_parms->tcon;
unsigned int count = io_parms->length;
struct kvec rsp_iov;
+ unsigned int in_len;
*nbytes = 0;
@@ -1810,12 +1923,13 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
wct = 12;
if ((offset >> 32) > 0) {
/* can not handle big offset for old srv */
- return -EIO;
+ return smb_EIO(smb_eio_trace_write_too_far);
}
}
rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->hdr.Pid = cpu_to_le16((__u16)pid);
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
@@ -1834,16 +1948,16 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
pSMB->Remaining = 0;
pSMB->DataOffset =
- cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
+ cpu_to_le16(offsetof(struct smb_com_write_req, Data));
pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
/* header + 1 byte pad */
- smb_hdr_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 1;
+ smb_hdr_len = in_len + 1;
if (wct == 14)
- inc_rfc1001_len(pSMB, count + 1);
+ in_len += count + 1;
else /* wct == 12 */
- inc_rfc1001_len(pSMB, count + 5); /* smb data starts later */
+ in_len += count + 5; /* smb data starts later */
if (wct == 14)
pSMB->ByteCount = cpu_to_le16(count + 1);
else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
@@ -1865,7 +1979,7 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
cifs_dbg(FYI, "Send error Write2 = %d\n", rc);
} else if (resp_buf_type == 0) {
/* presumably this can not happen, but best to be safe */
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_write_bad_buf_type, resp_buf_type);
} else {
WRITE_RSP *pSMBr = (WRITE_RSP *)rsp_iov.iov_base;
*nbytes = le16_to_cpu(pSMBr->CountHigh);
@@ -1897,6 +2011,7 @@ int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon,
LOCK_REQ *pSMB = NULL;
struct kvec iov[2];
struct kvec rsp_iov;
+ unsigned int in_len;
int resp_buf_type;
__u16 count;
@@ -1904,8 +2019,9 @@ int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon,
num_lock, num_unlock);
rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->Timeout = 0;
pSMB->NumberOfLocks = cpu_to_le16(num_lock);
@@ -1915,11 +2031,11 @@ int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Fid = netfid; /* netfid stays le */
count = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
- inc_rfc1001_len(pSMB, count);
+ in_len += count;
pSMB->ByteCount = cpu_to_le16(count);
iov[0].iov_base = (char *)pSMB;
- iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4 -
+ iov[0].iov_len = in_len -
(num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
iov[1].iov_base = (char *)buf;
iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
@@ -1944,16 +2060,18 @@ CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
LOCK_REQ *pSMB = NULL;
/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
+ unsigned int in_len;
int bytes_returned;
- int flags = 0;
+ int flags = CIFS_WINDOWS_LOCK | CIFS_INTERRUPTIBLE_WAIT;
__u16 count;
cifs_dbg(FYI, "CIFSSMBLock timeout %d numLock %d\n",
(int)waitFlag, numLock);
rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
/* no response expected */
@@ -1985,14 +2103,15 @@ CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon,
/* oplock break */
count = 0;
}
- inc_rfc1001_len(pSMB, count);
+ in_len += count;
pSMB->ByteCount = cpu_to_le16(count);
if (waitFlag)
- rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
- (struct smb_hdr *) pSMB, &bytes_returned);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
+ (struct smb_hdr *) pSMB, &bytes_returned,
+ flags);
else
- rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, flags);
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, in_len, flags);
cifs_small_buf_release(pSMB);
cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
if (rc)
@@ -2013,8 +2132,9 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
struct smb_com_transaction2_sfi_req *pSMB = NULL;
struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
struct cifs_posix_lock *parm_data;
+ unsigned int in_len;
int rc = 0;
- int timeout = 0;
+ int sr_flags = CIFS_INTERRUPTIBLE_WAIT;
int bytes_returned = 0;
int resp_buf_type = 0;
__u16 params, param_offset, offset, byte_count, count;
@@ -2024,9 +2144,9 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
cifs_dbg(FYI, "Posix Lock\n");
rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
-
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
@@ -2035,7 +2155,7 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Reserved = 0;
pSMB->Flags = 0;
pSMB->Reserved2 = 0;
- param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid);
offset = param_offset + params;
count = sizeof(struct cifs_posix_lock);
@@ -2053,13 +2173,11 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->TotalDataCount = pSMB->DataCount;
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
- /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */
- parm_data = (struct cifs_posix_lock *)
- (((char *)pSMB) + offset + 4);
+ parm_data = (struct cifs_posix_lock *)(((char *)pSMB) + offset);
parm_data->lock_type = cpu_to_le16(lock_type);
if (waitFlag) {
- timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
+ sr_flags |= CIFS_BLOCKING_OP; /* blocking operation, no timeout */
parm_data->lock_flags = cpu_to_le16(1);
pSMB->Timeout = cpu_to_le32(-1);
} else
@@ -2073,16 +2191,17 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Fid = smb_file_id;
pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
if (waitFlag) {
- rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
- (struct smb_hdr *) pSMBr, &bytes_returned);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
+ (struct smb_hdr *) pSMBr, &bytes_returned,
+ sr_flags);
} else {
iov[0].iov_base = (char *)pSMB;
- iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;
+ iov[0].iov_len = in_len;
rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
- &resp_buf_type, timeout, &rsp_iov);
+ &resp_buf_type, sr_flags, &rsp_iov);
pSMBr = (struct smb_com_transaction2_sfi_rsp *)rsp_iov.iov_base;
}
cifs_small_buf_release(pSMB);
@@ -2096,13 +2215,15 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc || get_bcc(&pSMBr->hdr) < sizeof(*parm_data)) {
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_lock_bcc_too_small,
+ get_bcc(&pSMBr->hdr), sizeof(*parm_data));
goto plk_err_exit;
}
data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
data_count = le16_to_cpu(pSMBr->t2.DataCount);
if (data_count < sizeof(struct cifs_posix_lock)) {
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_lock_data_too_small,
+ data_count, sizeof(struct cifs_posix_lock));
goto plk_err_exit;
}
parm_data = (struct cifs_posix_lock *)
@@ -2140,19 +2261,22 @@ CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)
{
int rc = 0;
CLOSE_REQ *pSMB = NULL;
+ unsigned int in_len;
+
cifs_dbg(FYI, "In CIFSSMBClose\n");
/* do not retry on dead session on close */
rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
if (rc == -EAGAIN)
return 0;
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->FileID = (__u16) smb_file_id;
pSMB->LastWriteTime = 0xFFFFFFFF;
pSMB->ByteCount = 0;
- rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, in_len, 0);
cifs_small_buf_release(pSMB);
cifs_stats_inc(&tcon->stats.cifs_stats.num_closes);
if (rc) {
@@ -2174,15 +2298,18 @@ CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)
{
int rc = 0;
FLUSH_REQ *pSMB = NULL;
+ unsigned int in_len;
+
cifs_dbg(FYI, "In CIFSSMBFlush\n");
rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->FileID = (__u16) smb_file_id;
pSMB->ByteCount = 0;
- rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, in_len, 0);
cifs_small_buf_release(pSMB);
cifs_stats_inc(&tcon->stats.cifs_stats.num_flushes);
if (rc)
@@ -2199,6 +2326,7 @@ int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
RENAME_REQ *pSMB = NULL;
RENAME_RSP *pSMBr = NULL;
+ unsigned int in_len;
int bytes_returned;
int name_len, name_len2;
__u16 count;
@@ -2208,8 +2336,9 @@ int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
renameRetry:
rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->BufferFormat = 0x04;
pSMB->SearchAttributes =
@@ -2239,10 +2368,10 @@ renameRetry:
}
count = 1 /* 1st signature byte */ + name_len + name_len2;
- inc_rfc1001_len(pSMB, count);
+ in_len += count;
pSMB->ByteCount = cpu_to_le16(count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_renames);
if (rc)
@@ -2263,6 +2392,7 @@ int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon,
struct smb_com_transaction2_sfi_req *pSMB = NULL;
struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
struct set_file_rename *rename_info;
+ unsigned int in_len;
char *data_offset;
char dummy_string[30];
int rc = 0;
@@ -2273,8 +2403,9 @@ int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon,
cifs_dbg(FYI, "Rename to File by handle\n");
rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 6;
pSMB->MaxSetupCount = 0;
@@ -2282,11 +2413,10 @@ int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon,
pSMB->Flags = 0;
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
- param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid);
offset = param_offset + params;
- /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */
- data_offset = (char *)(pSMB) + offset + 4;
+ data_offset = (char *)(pSMB) + offset;
rename_info = (struct set_file_rename *) data_offset;
pSMB->MaxParameterCount = cpu_to_le16(2);
pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
@@ -2322,9 +2452,9 @@ int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon,
pSMB->InformationLevel =
cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&pTcon->stats.cifs_stats.num_t2renames);
if (rc)
@@ -2346,6 +2476,7 @@ CIFSUnixCreateSymLink(const unsigned int xid, struct cifs_tcon *tcon,
{
TRANSACTION2_SPI_REQ *pSMB = NULL;
TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ unsigned int in_len;
char *data_offset;
int name_len;
int name_len_target;
@@ -2357,8 +2488,9 @@ CIFSUnixCreateSymLink(const unsigned int xid, struct cifs_tcon *tcon,
createSymLinkRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -2378,11 +2510,10 @@ createSymLinkRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_spi_req,
- InformationLevel) - 4;
+ InformationLevel);
offset = param_offset + params;
- /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */
- data_offset = (char *)pSMB + offset + 4;
+ data_offset = (char *)pSMB + offset;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len_target =
cifsConvertToUTF16((__le16 *) data_offset, toName,
@@ -2409,9 +2540,9 @@ createSymLinkRetry:
pSMB->DataOffset = cpu_to_le16(offset);
pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_symlinks);
if (rc)
@@ -2433,6 +2564,7 @@ CIFSUnixCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
{
TRANSACTION2_SPI_REQ *pSMB = NULL;
TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ unsigned int in_len;
char *data_offset;
int name_len;
int name_len_target;
@@ -2444,8 +2576,9 @@ CIFSUnixCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
createHardLinkRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len = cifsConvertToUTF16((__le16 *) pSMB->FileName, toName,
@@ -2463,11 +2596,10 @@ createHardLinkRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_spi_req,
- InformationLevel) - 4;
+ InformationLevel);
offset = param_offset + params;
- /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */
- data_offset = (char *)pSMB + offset + 4;
+ data_offset = (char *)pSMB + offset;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len_target =
cifsConvertToUTF16((__le16 *) data_offset, fromName,
@@ -2493,9 +2625,9 @@ createHardLinkRetry:
pSMB->DataOffset = cpu_to_le16(offset);
pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks);
if (rc)
@@ -2518,6 +2650,7 @@ int CIFSCreateHardLink(const unsigned int xid,
int rc = 0;
NT_RENAME_REQ *pSMB = NULL;
RENAME_RSP *pSMBr = NULL;
+ unsigned int in_len;
int bytes_returned;
int name_len, name_len2;
__u16 count;
@@ -2528,8 +2661,9 @@ winCreateHardLinkRetry:
rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->SearchAttributes =
cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
@@ -2563,10 +2697,10 @@ winCreateHardLinkRetry:
}
count = 1 /* string type byte */ + name_len + name_len2;
- inc_rfc1001_len(pSMB, count);
+ in_len += count;
pSMB->ByteCount = cpu_to_le16(count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks);
if (rc)
@@ -2587,6 +2721,7 @@ CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
/* SMB_QUERY_FILE_UNIX_LINK */
TRANSACTION2_QPI_REQ *pSMB = NULL;
TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ unsigned int in_len;
int rc = 0;
int bytes_returned;
int name_len;
@@ -2598,8 +2733,9 @@ CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
querySymLinkRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -2622,7 +2758,7 @@ querySymLinkRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
pSMB->ParameterOffset = cpu_to_le16(offsetof(
- struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ struct smb_com_transaction2_qpi_req, InformationLevel));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 1;
@@ -2633,10 +2769,10 @@ querySymLinkRetry:
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in QuerySymLinkInfo = %d\n", rc);
@@ -2646,7 +2782,8 @@ querySymLinkRetry:
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
/* BB also check enough total bytes returned */
if (rc || get_bcc(&pSMBr->hdr) < 2)
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_qsym_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 2);
else {
bool is_unicode;
u16 count = le16_to_cpu(pSMBr->t2.DataCount);
@@ -2684,6 +2821,7 @@ int cifs_query_reparse_point(const unsigned int xid,
TRANSACT_IOCTL_REQ *io_req = NULL;
TRANSACT_IOCTL_RSP *io_rsp = NULL;
struct cifs_fid fid;
+ unsigned int in_len;
__u32 data_offset, data_count, len;
__u8 *start, *end;
int io_rsp_len;
@@ -2695,6 +2833,9 @@ int cifs_query_reparse_point(const unsigned int xid,
if (cap_unix(tcon->ses))
return -EOPNOTSUPP;
+ if (!CIFS_REPARSE_SUPPORT(tcon))
+ return -EOPNOTSUPP;
+
oparms = (struct cifs_open_parms) {
.tcon = tcon,
.cifs_sb = cifs_sb,
@@ -2712,15 +2853,16 @@ int cifs_query_reparse_point(const unsigned int xid,
rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon,
(void **)&io_req, (void **)&io_rsp);
- if (rc)
+ if (rc < 0)
goto error;
+ in_len = rc;
io_req->TotalParameterCount = 0;
io_req->TotalDataCount = 0;
- io_req->MaxParameterCount = cpu_to_le32(2);
+ io_req->MaxParameterCount = cpu_to_le32(0);
/* BB find exact data count max from sess structure BB */
io_req->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
- io_req->MaxSetupCount = 4;
+ io_req->MaxSetupCount = 1;
io_req->Reserved = 0;
io_req->ParameterOffset = 0;
io_req->DataCount = 0;
@@ -2734,7 +2876,7 @@ int cifs_query_reparse_point(const unsigned int xid,
io_req->Fid = fid.netfid;
io_req->ByteCount = 0;
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)io_req,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)io_req, in_len,
(struct smb_hdr *)io_rsp, &io_rsp_len, 0);
if (rc)
goto error;
@@ -2743,14 +2885,35 @@ int cifs_query_reparse_point(const unsigned int xid,
data_count = le32_to_cpu(io_rsp->DataCount);
if (get_bcc(&io_rsp->hdr) < 2 || data_offset > 512 ||
!data_count || data_count > 2048) {
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_qreparse_sizes_wrong,
+ get_bcc(&io_rsp->hdr), data_count);
+ goto error;
+ }
+
+ /* SetupCount must be 1, otherwise offset to ByteCount is incorrect. */
+ if (io_rsp->SetupCount != 1) {
+ rc = smb_EIO2(smb_eio_trace_qreparse_setup_count,
+ io_rsp->SetupCount, 1);
+ goto error;
+ }
+
+ /*
+ * ReturnedDataLen is output length of executed IOCTL.
+ * DataCount is output length transferred over network.
+ * Check that we have full FSCTL_GET_REPARSE_POINT buffer.
+ */
+ if (data_count != le16_to_cpu(io_rsp->ReturnedDataLen)) {
+ rc = smb_EIO2(smb_eio_trace_qreparse_ret_datalen,
+ data_count, le16_to_cpu(io_rsp->ReturnedDataLen));
goto error;
}
end = 2 + get_bcc(&io_rsp->hdr) + (__u8 *)&io_rsp->ByteCount;
start = (__u8 *)&io_rsp->hdr.Protocol + data_offset;
if (start >= end) {
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_qreparse_data_area,
+ (unsigned long)start - (unsigned long)io_rsp,
+ (unsigned long)end - (unsigned long)io_rsp);
goto error;
}
@@ -2759,7 +2922,8 @@ int cifs_query_reparse_point(const unsigned int xid,
len = sizeof(*buf);
if (data_count < len ||
data_count < le16_to_cpu(buf->ReparseDataLength) + len) {
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_qreparse_rep_datalen,
+ data_count, le16_to_cpu(buf->ReparseDataLength) + len);
goto error;
}
@@ -2776,6 +2940,129 @@ error:
return rc;
}
+struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
+ struct super_block *sb,
+ const unsigned int xid,
+ struct cifs_tcon *tcon,
+ const char *full_path,
+ bool directory,
+ struct kvec *reparse_iov,
+ struct kvec *xattr_iov)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ struct cifs_open_parms oparms;
+ TRANSACT_IOCTL_REQ *io_req;
+ struct inode *new = NULL;
+ struct kvec in_iov[2];
+ struct kvec out_iov;
+ struct cifs_fid fid;
+ unsigned int in_len;
+ int oplock = 0;
+ int buf_type = 0;
+ int rc;
+
+ cifs_tcon_dbg(FYI, "%s: path=%s\n", __func__, full_path);
+
+ /*
+ * If server filesystem does not support reparse points then do not
+ * attempt to create reparse point. This will prevent creating unusable
+ * empty object on the server.
+ */
+ if (!CIFS_REPARSE_SUPPORT(tcon))
+ return ERR_PTR(-EOPNOTSUPP);
+
+#ifndef CONFIG_CIFS_XATTR
+ if (xattr_iov)
+ return ERR_PTR(-EOPNOTSUPP);
+#endif
+
+ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
+ FILE_READ_ATTRIBUTES | FILE_WRITE_DATA | FILE_WRITE_EA,
+ FILE_CREATE,
+ (directory ? CREATE_NOT_FILE : CREATE_NOT_DIR) | OPEN_REPARSE_POINT,
+ ACL_NO_MODE);
+ oparms.fid = &fid;
+
+ rc = CIFS_open(xid, &oparms, &oplock, NULL);
+ if (rc)
+ return ERR_PTR(rc);
+
+#ifdef CONFIG_CIFS_XATTR
+ if (xattr_iov) {
+ struct smb2_file_full_ea_info *ea;
+
+ ea = &((struct smb2_create_ea_ctx *)xattr_iov->iov_base)->ea;
+ while (1) {
+ rc = CIFSSMBSetEA(xid,
+ tcon,
+ full_path,
+ &ea->ea_data[0],
+ &ea->ea_data[ea->ea_name_length+1],
+ le16_to_cpu(ea->ea_value_length),
+ cifs_sb->local_nls,
+ cifs_sb);
+ if (rc)
+ goto out_close;
+ if (le32_to_cpu(ea->next_entry_offset) == 0)
+ break;
+ ea = (struct smb2_file_full_ea_info *)((u8 *)ea +
+ le32_to_cpu(ea->next_entry_offset));
+ }
+ }
+#endif
+
+ rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **)&io_req, NULL);
+ if (rc < 0)
+ goto out_close;
+ in_len = rc;
+ in_len += sizeof(io_req->Pad);
+
+ /* NT IOCTL response contains one-word long output setup buffer with size of output data. */
+ io_req->MaxSetupCount = 1;
+ /* NT IOCTL response does not contain output parameters. */
+ io_req->MaxParameterCount = cpu_to_le32(0);
+ /* FSCTL_SET_REPARSE_POINT response contains empty output data. */
+ io_req->MaxDataCount = cpu_to_le32(0);
+
+ io_req->TotalParameterCount = cpu_to_le32(0);
+ io_req->TotalDataCount = cpu_to_le32(reparse_iov->iov_len);
+ io_req->ParameterCount = io_req->TotalParameterCount;
+ io_req->ParameterOffset = cpu_to_le32(0);
+ io_req->DataCount = io_req->TotalDataCount;
+ io_req->DataOffset = cpu_to_le32(offsetof(typeof(*io_req), Data));
+ io_req->SetupCount = 4;
+ io_req->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
+ io_req->FunctionCode = cpu_to_le32(FSCTL_SET_REPARSE_POINT);
+ io_req->Fid = fid.netfid;
+ io_req->IsFsctl = 1;
+ io_req->IsRootFlag = 0;
+ io_req->ByteCount = cpu_to_le16(le32_to_cpu(io_req->DataCount) + sizeof(io_req->Pad));
+
+ in_iov[0].iov_base = (char *)io_req;
+ in_iov[0].iov_len = in_len;
+ in_iov[1] = *reparse_iov;
+ rc = SendReceive2(xid, tcon->ses, in_iov, ARRAY_SIZE(in_iov), &buf_type,
+ CIFS_NO_RSP_BUF, &out_iov);
+
+ cifs_buf_release(io_req);
+
+ if (!rc)
+ rc = cifs_get_inode_info(&new, full_path, data, sb, xid, NULL);
+
+out_close:
+ CIFSSMBClose(xid, tcon, fid.netfid);
+
+ /*
+ * If CREATE was successful but FSCTL_SET_REPARSE_POINT failed then
+ * remove the intermediate object created by CREATE. Otherwise
+ * empty object stay on the server when reparse call failed.
+ */
+ if (rc)
+ CIFSSMBDelFile(xid, tcon, full_path, cifs_sb, NULL);
+
+ return rc ? ERR_PTR(rc) : new;
+}
+
int
CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
__u16 fid)
@@ -2784,12 +3071,14 @@ CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
int bytes_returned;
struct smb_com_transaction_compr_ioctl_req *pSMB;
struct smb_com_transaction_ioctl_rsp *pSMBr;
+ unsigned int in_len;
cifs_dbg(FYI, "Set compression for %u\n", fid);
rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
@@ -2803,7 +3092,7 @@ CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->DataCount = cpu_to_le32(2);
pSMB->DataOffset =
cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req,
- compression_state) - 4); /* 84 */
+ compression_state)); /* 84 */
pSMB->SetupCount = 4;
pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
pSMB->ParameterCount = 0;
@@ -2813,9 +3102,9 @@ CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Fid = fid; /* file handle always le */
/* 3 byte pad, followed by 2 byte compress state */
pSMB->ByteCount = cpu_to_le16(5);
- inc_rfc1001_len(pSMB, 5);
+ in_len += 5;
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc)
cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc);
@@ -3013,6 +3302,7 @@ int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon,
/* SMB_QUERY_POSIX_ACL */
TRANSACTION2_QPI_REQ *pSMB = NULL;
TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ unsigned int in_len;
int rc = 0;
int bytes_returned;
int name_len;
@@ -3023,8 +3313,9 @@ int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon,
queryAclRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -3051,7 +3342,7 @@ queryAclRetry:
pSMB->Reserved2 = 0;
pSMB->ParameterOffset = cpu_to_le16(
offsetof(struct smb_com_transaction2_qpi_req,
- InformationLevel) - 4);
+ InformationLevel));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 1;
@@ -3062,10 +3353,10 @@ queryAclRetry:
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get);
if (rc) {
@@ -3076,7 +3367,8 @@ queryAclRetry:
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
/* BB also check enough total bytes returned */
if (rc || get_bcc(&pSMBr->hdr) < 2)
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_getacl_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 2);
else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
__u16 count = le16_to_cpu(pSMBr->t2.DataCount);
@@ -3103,6 +3395,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
{
struct smb_com_transaction2_spi_req *pSMB = NULL;
struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
+ unsigned int in_len;
char *parm_data;
int name_len;
int rc = 0;
@@ -3113,8 +3406,9 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
setAclRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
@@ -3134,9 +3428,9 @@ setAclRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_spi_req,
- InformationLevel) - 4;
+ InformationLevel);
offset = param_offset + params;
- parm_data = ((char *)pSMB) + sizeof(pSMB->hdr.smb_buf_length) + offset;
+ parm_data = ((char *)pSMB) + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
/* convert to on the wire format for POSIX ACL */
@@ -3157,9 +3451,9 @@ setAclRetry:
pSMB->ParameterCount = cpu_to_le16(params);
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc)
cifs_dbg(FYI, "Set POSIX ACL returned %d\n", rc);
@@ -3195,6 +3489,7 @@ CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
struct smb_t2_qfi_req *pSMB = NULL;
struct smb_t2_qfi_rsp *pSMBr = NULL;
+ unsigned int in_len;
int bytes_returned;
__u16 params, byte_count;
@@ -3205,8 +3500,9 @@ CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon,
GetExtAttrRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 2 /* level */ + 2 /* fid */;
pSMB->t2.TotalDataCount = 0;
@@ -3219,7 +3515,7 @@ GetExtAttrRetry:
pSMB->t2.Timeout = 0;
pSMB->t2.Reserved2 = 0;
pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
- Fid) - 4);
+ Fid));
pSMB->t2.DataCount = 0;
pSMB->t2.DataOffset = 0;
pSMB->t2.SetupCount = 1;
@@ -3231,10 +3527,10 @@ GetExtAttrRetry:
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
pSMB->Pad = 0;
pSMB->Fid = netfid;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->t2.ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "error %d in GetExtAttr\n", rc);
@@ -3245,7 +3541,8 @@ GetExtAttrRetry:
if (rc || get_bcc(&pSMBr->hdr) < 2)
/* If rc should we check for EOPNOSUPP and
disable the srvino flag? or in caller? */
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_getextattr_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 2);
else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
__u16 count = le16_to_cpu(pSMBr->t2.DataCount);
@@ -3253,7 +3550,8 @@ GetExtAttrRetry:
if (count != 16) {
cifs_dbg(FYI, "Invalid size ret in GetExtAttr\n");
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_getextattr_inv_size,
+ count, 16);
goto GetExtAttrOut;
}
pfinfo = (struct file_chattr_info *)
@@ -3287,11 +3585,13 @@ smb_init_nttransact(const __u16 sub_command, const int setup_count,
int rc;
__u32 temp_offset;
struct smb_com_ntransact_req *pSMB;
+ unsigned int in_len;
rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
(void **)&pSMB);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
*ret_buf = (void *)pSMB;
pSMB->Reserved = 0;
pSMB->TotalParameterCount = cpu_to_le32(parm_len);
@@ -3300,12 +3600,12 @@ smb_init_nttransact(const __u16 sub_command, const int setup_count,
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->DataCount = pSMB->TotalDataCount;
temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
- (setup_count * 2) - 4 /* for rfc1001 length itself */;
+ (setup_count * 2);
pSMB->ParameterOffset = cpu_to_le32(temp_offset);
pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
pSMB->SubCommand = cpu_to_le16(sub_command);
- return 0;
+ return in_len;
}
static int
@@ -3364,13 +3664,14 @@ validate_ntransact(char *buf, char **ppparm, char **ppdata,
/* Get Security Descriptor (by handle) from remote server for a file or dir */
int
CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid,
- struct smb_ntsd **acl_inf, __u32 *pbuflen)
+ struct smb_ntsd **acl_inf, __u32 *pbuflen, __u32 info)
{
int rc = 0;
int buf_type = 0;
QUERY_SEC_DESC_REQ *pSMB;
struct kvec iov[1];
struct kvec rsp_iov;
+ unsigned int in_len;
cifs_dbg(FYI, "GetCifsACL\n");
@@ -3379,19 +3680,19 @@ CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid,
rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
8 /* parm len */, tcon, (void **) &pSMB);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->MaxParameterCount = cpu_to_le32(4);
/* BB TEST with big acls that might need to be e.g. larger than 16K */
pSMB->MaxSetupCount = 0;
pSMB->Fid = fid; /* file handle always le */
- pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
- CIFS_ACL_DACL);
+ pSMB->AclFlags = cpu_to_le32(info);
pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
- inc_rfc1001_len(pSMB, 11);
+ in_len += 11;
iov[0].iov_base = (char *)pSMB;
- iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;
+ iov[0].iov_len = in_len;
rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
0, &rsp_iov);
@@ -3417,7 +3718,8 @@ CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid,
pSMBr, parm, *acl_inf);
if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_getcifsacl_param_count,
+ le32_to_cpu(pSMBr->ParameterCount), 4);
*pbuflen = 0;
goto qsec_out;
}
@@ -3460,18 +3762,20 @@ CIFSSMBSetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid,
int rc = 0;
int bytes_returned = 0;
SET_SEC_DESC_REQ *pSMB = NULL;
+ unsigned int in_len;
void *pSMBr;
setCifsAclRetry:
rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
param_count = 8;
- param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
+ param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid);
data_count = acllen;
data_offset = param_offset + param_count;
byte_count = 3 /* pad */ + param_count;
@@ -3493,13 +3797,12 @@ setCifsAclRetry:
pSMB->AclFlags = cpu_to_le32(aclflag);
if (pntsd && acllen) {
- memcpy((char *)pSMBr + offsetof(struct smb_hdr, Protocol) +
- data_offset, pntsd, acllen);
- inc_rfc1001_len(pSMB, byte_count + data_count);
+ memcpy((char *)pSMBr + data_offset, pntsd, acllen);
+ in_len += byte_count + data_count;
} else
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_dbg(FYI, "SetCIFSACL bytes_returned: %d, rc: %d\n",
@@ -3524,6 +3827,7 @@ SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon,
{
QUERY_INFORMATION_REQ *pSMB;
QUERY_INFORMATION_RSP *pSMBr;
+ unsigned int in_len;
int rc = 0;
int bytes_returned;
int name_len;
@@ -3532,8 +3836,9 @@ SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon,
QInfRetry:
rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -3547,10 +3852,10 @@ QInfRetry:
}
pSMB->BufferFormat = 0x04;
name_len++; /* account for buffer type byte */
- inc_rfc1001_len(pSMB, (__u16)name_len);
+ in_len += name_len;
pSMB->ByteCount = cpu_to_le16(name_len);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc);
@@ -3572,8 +3877,10 @@ QInfRetry:
data->EndOfFile = data->AllocationSize;
data->Attributes =
cpu_to_le32(le16_to_cpu(pSMBr->attr));
- } else
- rc = -EIO; /* bad buffer passed in */
+ } else {
+ /* bad buffer passed in */
+ rc = smb_EIO(smb_eio_trace_null_pointers);
+ }
cifs_buf_release(pSMB);
@@ -3589,6 +3896,7 @@ CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
{
struct smb_t2_qfi_req *pSMB = NULL;
struct smb_t2_qfi_rsp *pSMBr = NULL;
+ unsigned int in_len;
int rc = 0;
int bytes_returned;
__u16 params, byte_count;
@@ -3596,8 +3904,9 @@ CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
QFileInfoRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 2 /* level */ + 2 /* fid */;
pSMB->t2.TotalDataCount = 0;
@@ -3610,7 +3919,7 @@ QFileInfoRetry:
pSMB->t2.Timeout = 0;
pSMB->t2.Reserved2 = 0;
pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
- Fid) - 4);
+ Fid));
pSMB->t2.DataCount = 0;
pSMB->t2.DataOffset = 0;
pSMB->t2.SetupCount = 1;
@@ -3622,10 +3931,10 @@ QFileInfoRetry:
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
pSMB->Pad = 0;
pSMB->Fid = netfid;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->t2.ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in QFileInfo = %d\n", rc);
@@ -3633,9 +3942,11 @@ QFileInfoRetry:
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc) /* BB add auto retry on EOPNOTSUPP? */
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_qfileinfo_invalid,
+ get_bcc(&pSMBr->hdr), 40);
else if (get_bcc(&pSMBr->hdr) < 40)
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_qfileinfo_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 40);
else if (pFindData) {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
memcpy((char *) pFindData,
@@ -3660,6 +3971,7 @@ CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
/* level 263 SMB_QUERY_FILE_ALL_INFO */
TRANSACTION2_QPI_REQ *pSMB = NULL;
TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ unsigned int in_len;
int rc = 0;
int bytes_returned;
int name_len;
@@ -3669,8 +3981,9 @@ CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
QPathInfoRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -3693,7 +4006,7 @@ QPathInfoRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
pSMB->ParameterOffset = cpu_to_le16(offsetof(
- struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ struct smb_com_transaction2_qpi_req, InformationLevel));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 1;
@@ -3707,10 +4020,10 @@ QPathInfoRetry:
else
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc);
@@ -3718,12 +4031,15 @@ QPathInfoRetry:
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc) /* BB add auto retry on EOPNOTSUPP? */
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_qpathinfo_invalid,
+ get_bcc(&pSMBr->hdr), 40);
else if (!legacy && get_bcc(&pSMBr->hdr) < 40)
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_qpathinfo_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 40);
else if (legacy && get_bcc(&pSMBr->hdr) < 24)
- rc = -EIO; /* 24 or 26 expected but we do not read
- last field */
+ /* 24 or 26 expected but we do not read last field */
+ rc = smb_EIO2(smb_eio_trace_qpathinfo_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 24);
else if (data) {
int size;
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
@@ -3756,6 +4072,7 @@ CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
{
struct smb_t2_qfi_req *pSMB = NULL;
struct smb_t2_qfi_rsp *pSMBr = NULL;
+ unsigned int in_len;
int rc = 0;
int bytes_returned;
__u16 params, byte_count;
@@ -3763,8 +4080,9 @@ CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
UnixQFileInfoRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 2 /* level */ + 2 /* fid */;
pSMB->t2.TotalDataCount = 0;
@@ -3777,7 +4095,7 @@ UnixQFileInfoRetry:
pSMB->t2.Timeout = 0;
pSMB->t2.Reserved2 = 0;
pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
- Fid) - 4);
+ Fid));
pSMB->t2.DataCount = 0;
pSMB->t2.DataOffset = 0;
pSMB->t2.SetupCount = 1;
@@ -3789,10 +4107,10 @@ UnixQFileInfoRetry:
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
pSMB->Pad = 0;
pSMB->Fid = netfid;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->t2.ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in UnixQFileInfo = %d\n", rc);
@@ -3801,7 +4119,8 @@ UnixQFileInfoRetry:
if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) {
cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n");
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_unixqfileinfo_bcc_too_small,
+ get_bcc(&pSMBr->hdr), sizeof(FILE_UNIX_BASIC_INFO));
} else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
memcpy((char *) pFindData,
@@ -3827,6 +4146,7 @@ CIFSSMBUnixQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
/* SMB_QUERY_FILE_UNIX_BASIC */
TRANSACTION2_QPI_REQ *pSMB = NULL;
TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ unsigned int in_len;
int rc = 0;
int bytes_returned = 0;
int name_len;
@@ -3836,8 +4156,9 @@ CIFSSMBUnixQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
UnixQPathInfoRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -3860,7 +4181,7 @@ UnixQPathInfoRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
pSMB->ParameterOffset = cpu_to_le16(offsetof(
- struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ struct smb_com_transaction2_qpi_req, InformationLevel));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 1;
@@ -3871,10 +4192,10 @@ UnixQPathInfoRetry:
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in UnixQPathInfo = %d\n", rc);
@@ -3883,7 +4204,8 @@ UnixQPathInfoRetry:
if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) {
cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n");
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_unixqpathinfo_bcc_too_small,
+ get_bcc(&pSMBr->hdr), sizeof(FILE_UNIX_BASIC_INFO));
} else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
memcpy((char *) pFindData,
@@ -3911,7 +4233,7 @@ CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
T2_FFIRST_RSP_PARMS *parms;
struct nls_table *nls_codepage;
- unsigned int lnoff;
+ unsigned int in_len, lnoff;
__u16 params, byte_count;
int bytes_returned = 0;
int name_len, remap;
@@ -3922,8 +4244,9 @@ CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
findFirstRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
nls_codepage = cifs_sb->local_nls;
remap = cifs_remap(cifs_sb);
@@ -3946,6 +4269,12 @@ findFirstRetry:
pSMB->FileName[name_len] = 0;
pSMB->FileName[name_len+1] = 0;
name_len += 2;
+ } else if (!searchName[0]) {
+ pSMB->FileName[0] = CIFS_DIR_SEP(cifs_sb);
+ pSMB->FileName[1] = 0;
+ pSMB->FileName[2] = 0;
+ pSMB->FileName[3] = 0;
+ name_len = 4;
}
} else {
name_len = copy_path_name(pSMB->FileName, searchName);
@@ -3957,6 +4286,10 @@ findFirstRetry:
pSMB->FileName[name_len] = '*';
pSMB->FileName[name_len+1] = 0;
name_len += 2;
+ } else if (!searchName[0]) {
+ pSMB->FileName[0] = CIFS_DIR_SEP(cifs_sb);
+ pSMB->FileName[1] = 0;
+ name_len = 2;
}
}
@@ -3973,8 +4306,7 @@ findFirstRetry:
pSMB->TotalParameterCount = cpu_to_le16(params);
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->ParameterOffset = cpu_to_le16(
- offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
- - 4);
+ offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
@@ -3983,16 +4315,16 @@ findFirstRetry:
pSMB->SearchAttributes =
cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
ATTR_DIRECTORY);
- pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
+ pSMB->SearchCount = cpu_to_le16(msearch ? CIFSMaxBufSize/sizeof(FILE_UNIX_INFO) : 1);
pSMB->SearchFlags = cpu_to_le16(search_flags);
pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
/* BB what should we set StorageType to? Does it matter? BB */
pSMB->SearchStorageType = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_ffirst);
@@ -4051,7 +4383,7 @@ int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon,
TRANSACTION2_FNEXT_REQ *pSMB = NULL;
TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
T2_FNEXT_RSP_PARMS *parms;
- unsigned int name_len;
+ unsigned int name_len, in_len;
unsigned int lnoff;
__u16 params, byte_count;
char *response_data;
@@ -4065,8 +4397,9 @@ int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon,
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 14; /* includes 2 bytes of null string, converted to LE below*/
byte_count = 0;
@@ -4079,7 +4412,7 @@ int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
pSMB->ParameterOffset = cpu_to_le16(
- offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
+ offsetof(struct smb_com_transaction2_fnext_req, SearchHandle));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 1;
@@ -4107,10 +4440,10 @@ int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon,
byte_count = params + 1 /* pad */ ;
pSMB->TotalParameterCount = cpu_to_le16(params);
pSMB->ParameterCount = pSMB->TotalParameterCount;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_fnext);
@@ -4176,6 +4509,7 @@ CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon,
{
int rc = 0;
FINDCLOSE_REQ *pSMB = NULL;
+ unsigned int in_len;
cifs_dbg(FYI, "In CIFSSMBFindClose\n");
rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
@@ -4184,12 +4518,13 @@ CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon,
as file handle has been closed */
if (rc == -EAGAIN)
return 0;
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->FileID = searchHandle;
pSMB->ByteCount = 0;
- rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, in_len, 0);
cifs_small_buf_release(pSMB);
if (rc)
cifs_dbg(VFS, "Send error in FindClose = %d\n", rc);
@@ -4211,6 +4546,7 @@ CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
TRANSACTION2_QPI_REQ *pSMB = NULL;
TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ unsigned int in_len;
int name_len, bytes_returned;
__u16 params, byte_count;
@@ -4221,8 +4557,9 @@ CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon,
GetInodeNumberRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -4246,7 +4583,7 @@ GetInodeNumberRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
pSMB->ParameterOffset = cpu_to_le16(offsetof(
- struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ struct smb_com_transaction2_qpi_req, InformationLevel));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 1;
@@ -4257,10 +4594,10 @@ GetInodeNumberRetry:
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "error %d in QueryInternalInfo\n", rc);
@@ -4271,7 +4608,8 @@ GetInodeNumberRetry:
if (rc || get_bcc(&pSMBr->hdr) < 2)
/* If rc should we check for EOPNOSUPP and
disable the srvino flag? or in caller? */
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_getsrvinonum_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 2);
else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
__u16 count = le16_to_cpu(pSMBr->t2.DataCount);
@@ -4279,7 +4617,8 @@ GetInodeNumberRetry:
/* BB Do we need a cast or hash here ? */
if (count < 8) {
cifs_dbg(FYI, "Invalid size ret in QryIntrnlInf\n");
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_getsrvinonum_size,
+ count, 8);
goto GetInodeNumOut;
}
pfinfo = (struct file_internal_info *)
@@ -4303,6 +4642,7 @@ CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
/* TRANS2_GET_DFS_REFERRAL */
TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
+ unsigned int in_len;
int rc = 0;
int bytes_returned;
int name_len;
@@ -4320,10 +4660,11 @@ getDFSRetry:
* CIFSGetDFSRefer() may be called from cifs_reconnect_tcon() and thus
* causing an infinite recursion.
*/
- rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc,
- (void **)&pSMB, (void **)&pSMBr);
- if (rc)
+ rc = smb_init(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc,
+ (void **)&pSMB, (void **)&pSMBr);
+ if (rc < 0)
return rc;
+ in_len = rc;
/* server pointer checked in called function,
but should never be null here anyway */
@@ -4365,7 +4706,7 @@ getDFSRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
pSMB->ParameterOffset = cpu_to_le16(offsetof(
- struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
+ struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel));
pSMB->SetupCount = 1;
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
@@ -4373,10 +4714,10 @@ getDFSRetry:
pSMB->ParameterCount = cpu_to_le16(params);
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->MaxReferralLevel = cpu_to_le16(3);
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in GetDFSRefer = %d\n", rc);
@@ -4386,7 +4727,8 @@ getDFSRetry:
/* BB Also check if enough total bytes returned? */
if (rc || get_bcc(&pSMBr->hdr) < 17) {
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_getdfsrefer_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 17);
goto GetDFSRefExit;
}
@@ -4418,6 +4760,7 @@ SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
TRANSACTION2_QFSI_REQ *pSMB = NULL;
TRANSACTION2_QFSI_RSP *pSMBr = NULL;
FILE_SYSTEM_ALLOC_INFO *response_data;
+ unsigned int in_len;
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count;
@@ -4426,8 +4769,9 @@ SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
oldQFSInfoRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 2; /* level */
pSMB->TotalDataCount = 0;
@@ -4442,17 +4786,17 @@ oldQFSInfoRetry:
pSMB->TotalParameterCount = cpu_to_le16(params);
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->ParameterOffset = cpu_to_le16(offsetof(
- struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ struct smb_com_transaction2_qfsi_req, InformationLevel));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 1;
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc);
@@ -4460,7 +4804,8 @@ oldQFSInfoRetry:
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc || get_bcc(&pSMBr->hdr) < 18)
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_oldqfsinfo_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 18);
else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
cifs_dbg(FYI, "qfsinf resp BCC: %d Offset %d\n",
@@ -4504,7 +4849,8 @@ CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
TRANSACTION2_QFSI_REQ *pSMB = NULL;
TRANSACTION2_QFSI_RSP *pSMBr = NULL;
- FILE_SYSTEM_INFO *response_data;
+ FILE_SYSTEM_SIZE_INFO *response_data;
+ unsigned int in_len;
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count;
@@ -4513,8 +4859,9 @@ CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
QFSInfoRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 2; /* level */
pSMB->TotalDataCount = 0;
@@ -4529,17 +4876,17 @@ QFSInfoRetry:
pSMB->TotalParameterCount = cpu_to_le16(params);
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->ParameterOffset = cpu_to_le16(offsetof(
- struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ struct smb_com_transaction2_qfsi_req, InformationLevel));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 1;
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc);
@@ -4547,12 +4894,13 @@ QFSInfoRetry:
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc || get_bcc(&pSMBr->hdr) < 24)
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_qfsinfo_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 24);
else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
response_data =
- (FILE_SYSTEM_INFO
+ (FILE_SYSTEM_SIZE_INFO
*) (((char *) &pSMBr->hdr.Protocol) +
data_offset);
FSData->f_bsize =
@@ -4569,7 +4917,7 @@ QFSInfoRetry:
FSData->f_blocks =
le64_to_cpu(response_data->TotalAllocationUnits);
FSData->f_bfree = FSData->f_bavail =
- le64_to_cpu(response_data->FreeAllocationUnits);
+ le64_to_cpu(response_data->AvailableAllocationUnits);
cifs_dbg(FYI, "Blocks: %lld Free: %lld Block size %ld\n",
(unsigned long long)FSData->f_blocks,
(unsigned long long)FSData->f_bfree,
@@ -4591,6 +4939,7 @@ CIFSSMBQFSAttributeInfo(const unsigned int xid, struct cifs_tcon *tcon)
TRANSACTION2_QFSI_REQ *pSMB = NULL;
TRANSACTION2_QFSI_RSP *pSMBr = NULL;
FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
+ unsigned int in_len;
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count;
@@ -4599,8 +4948,9 @@ CIFSSMBQFSAttributeInfo(const unsigned int xid, struct cifs_tcon *tcon)
QFSAttributeRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 2; /* level */
pSMB->TotalDataCount = 0;
@@ -4616,17 +4966,17 @@ QFSAttributeRetry:
pSMB->TotalParameterCount = cpu_to_le16(params);
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->ParameterOffset = cpu_to_le16(offsetof(
- struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ struct smb_com_transaction2_qfsi_req, InformationLevel));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 1;
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(VFS, "Send error in QFSAttributeInfo = %d\n", rc);
@@ -4635,7 +4985,8 @@ QFSAttributeRetry:
if (rc || get_bcc(&pSMBr->hdr) < 13) {
/* BB also check if enough bytes returned */
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_qfsattrinfo_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 13);
} else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
response_data =
@@ -4661,6 +5012,7 @@ CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon)
TRANSACTION2_QFSI_REQ *pSMB = NULL;
TRANSACTION2_QFSI_RSP *pSMBr = NULL;
FILE_SYSTEM_DEVICE_INFO *response_data;
+ unsigned int in_len;
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count;
@@ -4669,8 +5021,9 @@ CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon)
QFSDeviceRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 2; /* level */
pSMB->TotalDataCount = 0;
@@ -4686,7 +5039,7 @@ QFSDeviceRetry:
pSMB->TotalParameterCount = cpu_to_le16(params);
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->ParameterOffset = cpu_to_le16(offsetof(
- struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ struct smb_com_transaction2_qfsi_req, InformationLevel));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
@@ -4694,10 +5047,10 @@ QFSDeviceRetry:
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in QFSDeviceInfo = %d\n", rc);
@@ -4706,7 +5059,9 @@ QFSDeviceRetry:
if (rc || get_bcc(&pSMBr->hdr) <
sizeof(FILE_SYSTEM_DEVICE_INFO))
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_qfsdevinfo_bcc_too_small,
+ get_bcc(&pSMBr->hdr),
+ sizeof(FILE_SYSTEM_DEVICE_INFO));
else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
response_data =
@@ -4732,6 +5087,7 @@ CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon)
TRANSACTION2_QFSI_REQ *pSMB = NULL;
TRANSACTION2_QFSI_RSP *pSMBr = NULL;
FILE_SYSTEM_UNIX_INFO *response_data;
+ unsigned int in_len;
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count;
@@ -4740,8 +5096,9 @@ CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon)
QFSUnixRetry:
rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
(void **) &pSMB, (void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 2; /* level */
pSMB->TotalDataCount = 0;
@@ -4759,15 +5116,15 @@ QFSUnixRetry:
pSMB->ParameterCount = cpu_to_le16(params);
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
- smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ smb_com_transaction2_qfsi_req, InformationLevel));
pSMB->SetupCount = 1;
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(VFS, "Send error in QFSUnixInfo = %d\n", rc);
@@ -4775,7 +5132,8 @@ QFSUnixRetry:
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc || get_bcc(&pSMBr->hdr) < 13) {
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_qfsunixinfo_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 13);
} else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
response_data =
@@ -4801,6 +5159,7 @@ CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, __u64 cap)
/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
TRANSACTION2_SETFSI_REQ *pSMB = NULL;
TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
+ unsigned int in_len;
int rc = 0;
int bytes_returned = 0;
__u16 params, param_offset, offset, byte_count;
@@ -4810,8 +5169,9 @@ SETFSUnixRetry:
/* BB switch to small buf init to save memory */
rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
(void **) &pSMB, (void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 4; /* 2 bytes zero followed by info level. */
pSMB->MaxSetupCount = 0;
@@ -4819,8 +5179,7 @@ SETFSUnixRetry:
pSMB->Flags = 0;
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
- param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
- - 4;
+ param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum);
offset = param_offset + params;
pSMB->MaxParameterCount = cpu_to_le16(4);
@@ -4847,10 +5206,10 @@ SETFSUnixRetry:
pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
pSMB->ClientUnixCap = cpu_to_le64(cap);
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(VFS, "Send error in SETFSUnixInfo = %d\n", rc);
@@ -4877,6 +5236,7 @@ CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,
TRANSACTION2_QFSI_REQ *pSMB = NULL;
TRANSACTION2_QFSI_RSP *pSMBr = NULL;
FILE_SYSTEM_POSIX_INFO *response_data;
+ unsigned int in_len;
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count;
@@ -4885,8 +5245,9 @@ CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,
QFSPosixRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
params = 2; /* level */
pSMB->TotalDataCount = 0;
@@ -4904,15 +5265,15 @@ QFSPosixRetry:
pSMB->ParameterCount = cpu_to_le16(params);
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
- smb_com_transaction2_qfsi_req, InformationLevel) - 4);
+ smb_com_transaction2_qfsi_req, InformationLevel));
pSMB->SetupCount = 1;
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in QFSUnixInfo = %d\n", rc);
@@ -4920,7 +5281,8 @@ QFSPosixRetry:
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc || get_bcc(&pSMBr->hdr) < 13) {
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_qfsposixinfo_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 13);
} else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
response_data =
@@ -4977,6 +5339,7 @@ CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
struct smb_com_transaction2_spi_req *pSMB = NULL;
struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
struct file_end_of_file_info *parm_data;
+ unsigned int in_len;
int name_len;
int rc = 0;
int bytes_returned = 0;
@@ -4988,8 +5351,9 @@ CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
SetEOFRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -5010,7 +5374,7 @@ SetEOFRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_spi_req,
- InformationLevel) - 4;
+ InformationLevel);
offset = param_offset + params;
if (set_allocation) {
if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
@@ -5042,10 +5406,10 @@ SetEOFRetry:
pSMB->ParameterCount = cpu_to_le16(params);
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
parm_data->FileSize = cpu_to_le64(size);
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc)
cifs_dbg(FYI, "SetPathInfo (file size) returned %d\n", rc);
@@ -5064,15 +5428,16 @@ CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
{
struct smb_com_transaction2_sfi_req *pSMB = NULL;
struct file_end_of_file_info *parm_data;
+ unsigned int in_len;
int rc = 0;
__u16 params, param_offset, offset, byte_count, count;
cifs_dbg(FYI, "SetFileSize (via SetFileInfo) %lld\n",
(long long)size);
rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
-
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->hdr.Pid = cpu_to_le16((__u16)cfile->pid);
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(cfile->pid >> 16));
@@ -5083,7 +5448,7 @@ CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Flags = 0;
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
- param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid);
offset = param_offset + params;
count = sizeof(struct file_end_of_file_info);
@@ -5099,9 +5464,8 @@ CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->TotalDataCount = pSMB->DataCount;
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
- /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */
parm_data =
- (struct file_end_of_file_info *)(((char *)pSMB) + offset + 4);
+ (struct file_end_of_file_info *)(((char *)pSMB) + offset);
pSMB->DataOffset = cpu_to_le16(offset);
parm_data->FileSize = cpu_to_le64(size);
pSMB->Fid = cfile->fid.netfid;
@@ -5121,9 +5485,9 @@ CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
}
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, in_len, 0);
cifs_small_buf_release(pSMB);
if (rc) {
cifs_dbg(FYI, "Send error in SetFileInfo (SetFileSize) = %d\n",
@@ -5136,6 +5500,65 @@ CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
}
+int
+SMBSetInformation(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *fileName, __le32 attributes, __le64 write_time,
+ const struct nls_table *nls_codepage,
+ struct cifs_sb_info *cifs_sb)
+{
+ SETATTR_REQ *pSMB;
+ SETATTR_RSP *pSMBr;
+ struct timespec64 ts;
+ unsigned int in_len;
+ int bytes_returned;
+ int name_len;
+ int rc;
+
+ cifs_dbg(FYI, "In %s path %s\n", __func__, fileName);
+
+retry:
+ rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc < 0)
+ return rc;
+ in_len = rc;
+
+ if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
+ name_len =
+ cifsConvertToUTF16((__le16 *) pSMB->fileName,
+ fileName, PATH_MAX, nls_codepage,
+ cifs_remap(cifs_sb));
+ name_len++; /* trailing null */
+ name_len *= 2;
+ } else {
+ name_len = copy_path_name(pSMB->fileName, fileName);
+ }
+ /* Only few attributes can be set by this command, others are not accepted by Win9x. */
+ pSMB->attr = cpu_to_le16(le32_to_cpu(attributes) &
+ (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_ARCHIVE));
+ /* Zero write time value (in both NT and SETATTR formats) means to not change it. */
+ if (le64_to_cpu(write_time) != 0) {
+ ts = cifs_NTtimeToUnix(write_time);
+ pSMB->last_write_time = cpu_to_le32(ts.tv_sec);
+ }
+ pSMB->BufferFormat = 0x04;
+ name_len++; /* account for buffer type byte */
+ in_len += name_len;
+ pSMB->ByteCount = cpu_to_le16(name_len);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc)
+ cifs_dbg(FYI, "Send error in %s = %d\n", __func__, rc);
+
+ cifs_buf_release(pSMB);
+
+ if (rc == -EAGAIN)
+ goto retry;
+
+ return rc;
+}
+
/* Some legacy servers such as NT4 require that the file times be set on
an open handle, rather than by pathname - this is awkward due to
potential access conflicts on the open, but it is unavoidable for these
@@ -5147,15 +5570,16 @@ CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
{
struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ unsigned int in_len;
char *data_offset;
int rc = 0;
__u16 params, param_offset, offset, byte_count, count;
cifs_dbg(FYI, "Set Times (via SetFileInfo)\n");
rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
-
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
@@ -5166,11 +5590,10 @@ CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Flags = 0;
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
- param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid);
offset = param_offset + params;
- data_offset = (char *)pSMB +
- offsetof(struct smb_hdr, Protocol) + offset;
+ data_offset = (char *)pSMB + offset;
count = sizeof(FILE_BASIC_INFO);
pSMB->MaxParameterCount = cpu_to_le16(2);
@@ -5192,10 +5615,10 @@ CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
else
pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
- rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, in_len, 0);
cifs_small_buf_release(pSMB);
if (rc)
cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n",
@@ -5212,15 +5635,16 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,
bool delete_file, __u16 fid, __u32 pid_of_opener)
{
struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ unsigned int in_len;
char *data_offset;
int rc = 0;
__u16 params, param_offset, offset, byte_count, count;
cifs_dbg(FYI, "Set File Disposition (via SetFileInfo)\n");
rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
-
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
@@ -5231,11 +5655,9 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Flags = 0;
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
- param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid);
offset = param_offset + params;
-
- /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */
- data_offset = (char *)(pSMB) + offset + 4;
+ data_offset = (char *)(pSMB) + offset;
count = 1;
pSMB->MaxParameterCount = cpu_to_le16(2);
@@ -5254,10 +5676,10 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Fid = fid;
pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
*data_offset = delete_file ? 1 : 0;
- rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, in_len, 0);
cifs_small_buf_release(pSMB);
if (rc)
cifs_dbg(FYI, "Send error in SetFileDisposition = %d\n", rc);
@@ -5305,6 +5727,7 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
{
TRANSACTION2_SPI_REQ *pSMB = NULL;
TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ unsigned int in_len;
int name_len;
int rc = 0;
int bytes_returned = 0;
@@ -5317,8 +5740,9 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
SetTimesRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -5341,7 +5765,7 @@ SetTimesRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_spi_req,
- InformationLevel) - 4;
+ InformationLevel);
offset = param_offset + params;
data_offset = (char *)pSMB + offsetof(typeof(*pSMB), hdr.Protocol) + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
@@ -5360,10 +5784,10 @@ SetTimesRetry:
else
pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc)
cifs_dbg(FYI, "SetPathInfo (times) returned %d\n", rc);
@@ -5433,15 +5857,16 @@ CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
u16 fid, u32 pid_of_opener)
{
struct smb_com_transaction2_sfi_req *pSMB = NULL;
+ unsigned int in_len;
char *data_offset;
int rc = 0;
u16 params, param_offset, offset, byte_count, count;
cifs_dbg(FYI, "Set Unix Info (via SetFileInfo)\n");
rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
-
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
@@ -5452,11 +5877,10 @@ CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Flags = 0;
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
- param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
+ param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid);
offset = param_offset + params;
- data_offset = (char *)pSMB +
- offsetof(struct smb_hdr, Protocol) + offset;
+ data_offset = (char *)pSMB + offset;
count = sizeof(FILE_UNIX_BASIC_INFO);
@@ -5476,12 +5900,12 @@ CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->Fid = fid;
pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args);
- rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, in_len, 0);
cifs_small_buf_release(pSMB);
if (rc)
cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n",
@@ -5501,6 +5925,7 @@ CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
{
TRANSACTION2_SPI_REQ *pSMB = NULL;
TRANSACTION2_SPI_RSP *pSMBr = NULL;
+ unsigned int in_len;
int name_len;
int rc = 0;
int bytes_returned = 0;
@@ -5511,8 +5936,9 @@ CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
setPermsRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -5535,10 +5961,9 @@ setPermsRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_spi_req,
- InformationLevel) - 4;
+ InformationLevel);
offset = param_offset + params;
- /* SMB offsets are from the beginning of SMB which is 4 bytes in, after RFC1001 field */
- data_offset = (FILE_UNIX_BASIC_INFO *)((char *) pSMB + offset + 4);
+ data_offset = (FILE_UNIX_BASIC_INFO *)((char *) pSMB + offset);
memset(data_offset, 0, count);
pSMB->DataOffset = cpu_to_le16(offset);
pSMB->ParameterOffset = cpu_to_le16(param_offset);
@@ -5552,12 +5977,12 @@ setPermsRetry:
pSMB->TotalDataCount = pSMB->DataCount;
pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
cifs_fill_unix_set_info(data_offset, args);
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc)
cifs_dbg(FYI, "SetPathInfo (perms) returned %d\n", rc);
@@ -5589,6 +6014,7 @@ CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
TRANSACTION2_QPI_RSP *pSMBr = NULL;
int remap = cifs_remap(cifs_sb);
struct nls_table *nls_codepage = cifs_sb->local_nls;
+ unsigned int in_len;
int rc = 0;
int bytes_returned;
int list_len;
@@ -5603,8 +6029,9 @@ CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
QAllEAsRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
list_len =
@@ -5627,7 +6054,7 @@ QAllEAsRetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
pSMB->ParameterOffset = cpu_to_le16(offsetof(
- struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+ struct smb_com_transaction2_qpi_req, InformationLevel));
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 1;
@@ -5638,10 +6065,10 @@ QAllEAsRetry:
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in QueryAllEAs = %d\n", rc);
@@ -5655,7 +6082,8 @@ QAllEAsRetry:
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
if (rc || get_bcc(&pSMBr->hdr) < 4) {
- rc = -EIO; /* bad smb */
+ rc = smb_EIO2(smb_eio_trace_qalleas_bcc_too_small,
+ get_bcc(&pSMBr->hdr), 4);
goto QAllEAsOut;
}
@@ -5685,7 +6113,9 @@ QAllEAsRetry:
end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr);
if ((char *)ea_response_data + list_len > end_of_smb) {
cifs_dbg(FYI, "EA list appears to go beyond SMB\n");
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_qalleas_overlong,
+ (unsigned long)ea_response_data + list_len - (unsigned long)pSMBr,
+ (unsigned long)end_of_smb - (unsigned long)pSMBr);
goto QAllEAsOut;
}
@@ -5702,7 +6132,7 @@ QAllEAsRetry:
/* make sure we can read name_len and value_len */
if (list_len < 0) {
cifs_dbg(FYI, "EA entry goes beyond length of list\n");
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_qalleas_ea_overlong, list_len);
goto QAllEAsOut;
}
@@ -5711,7 +6141,7 @@ QAllEAsRetry:
list_len -= name_len + 1 + value_len;
if (list_len < 0) {
cifs_dbg(FYI, "EA entry goes beyond length of list\n");
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_qalleas_ea_overlong, list_len);
goto QAllEAsOut;
}
@@ -5773,6 +6203,7 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
struct smb_com_transaction2_spi_req *pSMB = NULL;
struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
struct fealist *parm_data;
+ unsigned int in_len;
int name_len;
int rc = 0;
int bytes_returned = 0;
@@ -5783,8 +6214,9 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
SetEARetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
- if (rc)
+ if (rc < 0)
return rc;
+ in_len = rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
@@ -5816,12 +6248,12 @@ SetEARetry:
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_spi_req,
- InformationLevel) - 4;
+ InformationLevel);
offset = param_offset + params;
pSMB->InformationLevel =
cpu_to_le16(SMB_SET_FILE_EA);
- parm_data = (void *)pSMB + offsetof(struct smb_hdr, Protocol) + offset;
+ parm_data = (void *)pSMB + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
pSMB->DataOffset = cpu_to_le16(offset);
pSMB->SetupCount = 1;
@@ -5850,9 +6282,9 @@ SetEARetry:
pSMB->ParameterCount = cpu_to_le16(params);
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->Reserved4 = 0;
- inc_rfc1001_len(pSMB, byte_count);
+ in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
- rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc)
cifs_dbg(FYI, "SetPathInfo (EA) returned %d\n", rc);
diff --git a/fs/smb/client/cifstransport.c b/fs/smb/client/cifstransport.c
new file mode 100644
index 000000000000..28d1cee90625
--- /dev/null
+++ b/fs/smb/client/cifstransport.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org) 2006.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/gfp.h>
+#include <linux/wait.h>
+#include <linux/net.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/tcp.h>
+#include <linux/bvec.h>
+#include <linux/highmem.h>
+#include <linux/uaccess.h>
+#include <linux/processor.h>
+#include <linux/mempool.h>
+#include <linux/sched/signal.h>
+#include <linux/task_io_accounting_ops.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "smb2proto.h"
+#include "smbdirect.h"
+#include "compress.h"
+
+/* Max number of iovectors we can use off the stack when sending requests. */
+#define CIFS_MAX_IOV_SIZE 8
+
+static struct mid_q_entry *
+alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
+{
+ struct mid_q_entry *temp;
+
+ if (server == NULL) {
+ cifs_dbg(VFS, "%s: null TCP session\n", __func__);
+ return NULL;
+ }
+
+ temp = mempool_alloc(&cifs_mid_pool, GFP_NOFS);
+ memset(temp, 0, sizeof(struct mid_q_entry));
+ refcount_set(&temp->refcount, 1);
+ spin_lock_init(&temp->mid_lock);
+ temp->mid = get_mid(smb_buffer);
+ temp->pid = current->pid;
+ temp->command = cpu_to_le16(smb_buffer->Command);
+ cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
+ /* easier to use jiffies */
+ /* when mid allocated can be before when sent */
+ temp->when_alloc = jiffies;
+
+ /*
+ * The default is for the mid to be synchronous, so the
+ * default callback just wakes up the current task.
+ */
+ get_task_struct(current);
+ temp->creator = current;
+ temp->callback = cifs_wake_up_task;
+ temp->callback_data = current;
+
+ atomic_inc(&mid_count);
+ temp->mid_state = MID_REQUEST_ALLOCATED;
+ return temp;
+}
+
+static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
+ struct mid_q_entry **ppmidQ)
+{
+ spin_lock(&ses->ses_lock);
+ if (ses->ses_status == SES_NEW) {
+ if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
+ (in_buf->Command != SMB_COM_NEGOTIATE)) {
+ spin_unlock(&ses->ses_lock);
+ return -EAGAIN;
+ }
+ /* else ok - we are setting up session */
+ }
+
+ if (ses->ses_status == SES_EXITING) {
+ /* check if SMB session is bad because we are setting it up */
+ if (in_buf->Command != SMB_COM_LOGOFF_ANDX) {
+ spin_unlock(&ses->ses_lock);
+ return -EAGAIN;
+ }
+ /* else ok - we are shutting down session */
+ }
+ spin_unlock(&ses->ses_lock);
+
+ *ppmidQ = alloc_mid(in_buf, ses->server);
+ if (*ppmidQ == NULL)
+ return -ENOMEM;
+ spin_lock(&ses->server->mid_queue_lock);
+ list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q);
+ spin_unlock(&ses->server->mid_queue_lock);
+ return 0;
+}
+
+struct mid_q_entry *
+cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+{
+ int rc;
+ struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+ struct mid_q_entry *mid;
+
+ /* enable signing if server requires it */
+ if (server->sign)
+ hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ mid = alloc_mid(hdr, server);
+ if (mid == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
+ if (rc) {
+ release_mid(server, mid);
+ return ERR_PTR(rc);
+ }
+
+ return mid;
+}
+
+/*
+ *
+ * Send an SMB Request. No response info (other than return code)
+ * needs to be parsed.
+ *
+ * flags indicate the type of request buffer and how long to wait
+ * and whether to log NT STATUS code (error) before mapping it to POSIX error
+ *
+ */
+int
+SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
+ char *in_buf, unsigned int in_len, int flags)
+{
+ int rc;
+ struct kvec iov[1];
+ struct kvec rsp_iov;
+ int resp_buf_type;
+
+ iov[0].iov_base = in_buf;
+ iov[0].iov_len = in_len;
+ flags |= CIFS_NO_RSP_BUF;
+ rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
+ cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
+
+ return rc;
+}
+
+int
+cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
+ bool log_error)
+{
+ unsigned int len = mid->response_pdu_len;
+
+ dump_smb(mid->resp_buf, min_t(u32, 92, len));
+
+ /* convert the length into a more usable form */
+ if (server->sign) {
+ struct kvec iov[1];
+ int rc = 0;
+ struct smb_rqst rqst = { .rq_iov = iov,
+ .rq_nvec = ARRAY_SIZE(iov) };
+
+ iov[0].iov_base = mid->resp_buf;
+ iov[0].iov_len = len;
+ /* FIXME: add code to kill session */
+ rc = cifs_verify_signature(&rqst, server,
+ mid->sequence_number);
+ if (rc)
+ cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
+ rc);
+ }
+
+ /* BB special case reconnect tid and uid here? */
+ return map_and_check_smb_error(server, mid, log_error);
+}
+
+struct mid_q_entry *
+cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ struct smb_rqst *rqst)
+{
+ int rc;
+ struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+ struct mid_q_entry *mid;
+
+ rc = allocate_mid(ses, hdr, &mid);
+ if (rc)
+ return ERR_PTR(rc);
+ rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
+ if (rc) {
+ delete_mid(server, mid);
+ return ERR_PTR(rc);
+ }
+ return mid;
+}
+
+int
+SendReceive2(const unsigned int xid, struct cifs_ses *ses,
+ struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
+ const int flags, struct kvec *resp_iov)
+{
+ struct smb_rqst rqst = {
+ .rq_iov = iov,
+ .rq_nvec = n_vec,
+ };
+
+ return cifs_send_recv(xid, ses, ses->server,
+ &rqst, resp_buf_type, flags, resp_iov);
+}
+
+int
+SendReceive(const unsigned int xid, struct cifs_ses *ses,
+ struct smb_hdr *in_buf, unsigned int in_len,
+ struct smb_hdr *out_buf, int *pbytes_returned, const int flags)
+{
+ struct TCP_Server_Info *server;
+ struct kvec resp_iov = {};
+ struct kvec iov = { .iov_base = in_buf, .iov_len = in_len };
+ struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
+ int resp_buf_type;
+ int rc = 0;
+
+ if (WARN_ON_ONCE(in_len > 0xffffff))
+ return smb_EIO1(smb_eio_trace_tx_too_long, in_len);
+ if (ses == NULL) {
+ cifs_dbg(VFS, "Null smb session\n");
+ return smb_EIO(smb_eio_trace_null_pointers);
+ }
+ server = ses->server;
+ if (server == NULL) {
+ cifs_dbg(VFS, "Null tcp session\n");
+ return smb_EIO(smb_eio_trace_null_pointers);
+ }
+
+ /* Ensure that we do not send more than 50 overlapping requests
+ to the same server. We may make this configurable later or
+ use ses->maxReq */
+
+ if (in_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
+ in_len);
+ return smb_EIO1(smb_eio_trace_tx_too_long, in_len);
+ }
+
+ rc = cifs_send_recv(xid, ses, ses->server,
+ &rqst, &resp_buf_type, flags, &resp_iov);
+ if (rc < 0)
+ return rc;
+
+ if (out_buf) {
+ *pbytes_returned = resp_iov.iov_len;
+ if (resp_iov.iov_len)
+ memcpy(out_buf, resp_iov.iov_base, resp_iov.iov_len);
+ }
+ free_rsp_buf(resp_buf_type, resp_iov.iov_base);
+ return rc;
+}
diff --git a/fs/smb/client/compress.c b/fs/smb/client/compress.c
index 766b4de13da7..e0c44b46080e 100644
--- a/fs/smb/client/compress.c
+++ b/fs/smb/client/compress.c
@@ -44,7 +44,7 @@ struct bucket {
unsigned int count;
};
-/**
+/*
* has_low_entropy() - Compute Shannon entropy of the sampled data.
* @bkt: Bytes counts of the sample.
* @slen: Size of the sample.
@@ -82,7 +82,7 @@ static bool has_low_entropy(struct bucket *bkt, size_t slen)
#define BYTE_DIST_BAD 0
#define BYTE_DIST_GOOD 1
#define BYTE_DIST_MAYBE 2
-/**
+/*
* calc_byte_distribution() - Compute byte distribution on the sampled data.
* @bkt: Byte counts of the sample.
* @slen: Size of the sample.
@@ -155,63 +155,34 @@ static int cmp_bkt(const void *_a, const void *_b)
}
/*
- * TODO:
- * Support other iter types, if required.
- * Only ITER_XARRAY is supported for now.
+ * Collect some 2K samples with 2K gaps between.
*/
-static int collect_sample(const struct iov_iter *iter, ssize_t max, u8 *sample)
+static int collect_sample(const struct iov_iter *source, ssize_t max, u8 *sample)
{
- struct folio *folios[16], *folio;
- unsigned int nr, i, j, npages;
- loff_t start = iter->xarray_start + iter->iov_offset;
- pgoff_t last, index = start / PAGE_SIZE;
- size_t len, off, foff;
- void *p;
- int s = 0;
-
- last = (start + max - 1) / PAGE_SIZE;
- do {
- nr = xa_extract(iter->xarray, (void **)folios, index, last, ARRAY_SIZE(folios),
- XA_PRESENT);
- if (nr == 0)
- return -EIO;
-
- for (i = 0; i < nr; i++) {
- folio = folios[i];
- npages = folio_nr_pages(folio);
- foff = start - folio_pos(folio);
- off = foff % PAGE_SIZE;
-
- for (j = foff / PAGE_SIZE; j < npages; j++) {
- size_t len2;
-
- len = min_t(size_t, max, PAGE_SIZE - off);
- len2 = min_t(size_t, len, SZ_2K);
-
- p = kmap_local_page(folio_page(folio, j));
- memcpy(&sample[s], p, len2);
- kunmap_local(p);
-
- s += len2;
-
- if (len2 < SZ_2K || s >= max - SZ_2K)
- return s;
-
- max -= len;
- if (max <= 0)
- return s;
-
- start += len;
- off = 0;
- index++;
- }
- }
- } while (nr == ARRAY_SIZE(folios));
+ struct iov_iter iter = *source;
+ size_t s = 0;
+
+ while (iov_iter_count(&iter) >= SZ_2K) {
+ size_t part = umin(umin(iov_iter_count(&iter), SZ_2K), max);
+ size_t n;
+
+ n = copy_from_iter(sample + s, part, &iter);
+ if (n != part)
+ return -EFAULT;
+
+ s += n;
+ max -= n;
+
+ if (iov_iter_count(&iter) < PAGE_SIZE - SZ_2K)
+ break;
+
+ iov_iter_advance(&iter, SZ_2K);
+ }
return s;
}
-/**
+/*
* is_compressible() - Determines if a chunk of data is compressible.
* @data: Iterator containing uncompressed data.
*
@@ -290,6 +261,21 @@ out:
return ret;
}
+/*
+ * should_compress() - Determines if a request (write) or the response to a
+ * request (read) should be compressed.
+ * @tcon: tcon of the request is being sent to
+ * @rqst: request to evaluate
+ *
+ * Return: true iff:
+ * - compression was successfully negotiated with server
+ * - server has enabled compression for the share
+ * - it's a read or write request
+ * - (write only) request length is >= SMB_COMPRESS_MIN_LEN
+ * - (write only) is_compressible() returns 1
+ *
+ * Return false otherwise.
+ */
bool should_compress(const struct cifs_tcon *tcon, const struct smb_rqst *rq)
{
const struct smb2_hdr *shdr = rq->rq_iov->iov_base;
@@ -339,7 +325,7 @@ int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, compress_s
iter = rq->rq_iter;
if (!copy_from_iter_full(src, slen, &iter)) {
- ret = -EIO;
+ ret = smb_EIO(smb_eio_trace_compress_copy);
goto err_free;
}
diff --git a/fs/smb/client/compress.h b/fs/smb/client/compress.h
index f3ed1d3e52fb..63aea32fbe92 100644
--- a/fs/smb/client/compress.h
+++ b/fs/smb/client/compress.h
@@ -29,26 +29,11 @@
#ifdef CONFIG_CIFS_COMPRESSION
typedef int (*compress_send_fn)(struct TCP_Server_Info *, int, struct smb_rqst *);
-int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, compress_send_fn send_fn);
-/**
- * should_compress() - Determines if a request (write) or the response to a
- * request (read) should be compressed.
- * @tcon: tcon of the request is being sent to
- * @rqst: request to evaluate
- *
- * Return: true iff:
- * - compression was successfully negotiated with server
- * - server has enabled compression for the share
- * - it's a read or write request
- * - (write only) request length is >= SMB_COMPRESS_MIN_LEN
- * - (write only) is_compressible() returns 1
- *
- * Return false otherwise.
- */
+int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, compress_send_fn send_fn);
bool should_compress(const struct cifs_tcon *tcon, const struct smb_rqst *rq);
-/**
+/*
* smb_compress_alg_valid() - Validate a compression algorithm.
* @alg: Compression algorithm to check.
* @valid_none: Conditional check whether NONE algorithm should be
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index 0a97228c06b1..ce620503e9f7 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -72,10 +72,8 @@ static void cifs_prune_tlinks(struct work_struct *work);
*/
static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
{
- int rc;
- int len;
- char *unc;
struct sockaddr_storage ss;
+ int rc;
if (!server->hostname)
return -EINVAL;
@@ -84,36 +82,22 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
if (server->hostname[0] == '\0')
return 0;
- len = strlen(server->hostname) + 3;
-
- unc = kmalloc(len, GFP_KERNEL);
- if (!unc) {
- cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__);
- return -ENOMEM;
- }
- scnprintf(unc, len, "\\\\%s", server->hostname);
-
spin_lock(&server->srv_lock);
ss = server->dstaddr;
spin_unlock(&server->srv_lock);
- rc = dns_resolve_server_name_to_ip(unc, (struct sockaddr *)&ss, NULL);
- kfree(unc);
-
- if (rc < 0) {
- cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n",
- __func__, server->hostname, rc);
- } else {
+ rc = dns_resolve_name(server->dns_dom, server->hostname,
+ strlen(server->hostname),
+ (struct sockaddr *)&ss);
+ if (!rc) {
spin_lock(&server->srv_lock);
memcpy(&server->dstaddr, &ss, sizeof(server->dstaddr));
spin_unlock(&server->srv_lock);
- rc = 0;
}
-
return rc;
}
-static void smb2_query_server_interfaces(struct work_struct *work)
+void smb2_query_server_interfaces(struct work_struct *work)
{
int rc;
int xid;
@@ -132,18 +116,22 @@ static void smb2_query_server_interfaces(struct work_struct *work)
rc = server->ops->query_server_interfaces(xid, tcon, false);
free_xid(xid);
- if (rc) {
- if (rc == -EOPNOTSUPP)
- return;
-
+ if (rc)
cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
__func__, rc);
- }
queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
(SMB_INTERFACE_POLL_INTERVAL * HZ));
}
+#define set_need_reco(server) \
+do { \
+ spin_lock(&server->srv_lock); \
+ if (server->tcpStatus != CifsExiting) \
+ server->tcpStatus = CifsNeedReconnect; \
+ spin_unlock(&server->srv_lock); \
+} while (0)
+
/*
* Update the tcpStatus for the server.
* This is used to signal the cifsd thread to call cifs_reconnect
@@ -157,39 +145,45 @@ void
cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
bool all_channels)
{
- struct TCP_Server_Info *pserver;
+ struct TCP_Server_Info *nserver;
struct cifs_ses *ses;
+ LIST_HEAD(reco);
int i;
- /* If server is a channel, select the primary channel */
- pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
-
/* if we need to signal just this channel */
if (!all_channels) {
- spin_lock(&server->srv_lock);
- if (server->tcpStatus != CifsExiting)
- server->tcpStatus = CifsNeedReconnect;
- spin_unlock(&server->srv_lock);
+ set_need_reco(server);
return;
}
- spin_lock(&cifs_tcp_ses_lock);
- list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
- if (cifs_ses_exiting(ses))
- continue;
- spin_lock(&ses->chan_lock);
- for (i = 0; i < ses->chan_count; i++) {
- if (!ses->chans[i].server)
+ if (SERVER_IS_CHAN(server))
+ server = server->primary_server;
+ scoped_guard(spinlock, &cifs_tcp_ses_lock) {
+ set_need_reco(server);
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ spin_lock(&ses->ses_lock);
+ if (ses->ses_status == SES_EXITING) {
+ spin_unlock(&ses->ses_lock);
continue;
-
- spin_lock(&ses->chans[i].server->srv_lock);
- if (ses->chans[i].server->tcpStatus != CifsExiting)
- ses->chans[i].server->tcpStatus = CifsNeedReconnect;
- spin_unlock(&ses->chans[i].server->srv_lock);
+ }
+ spin_lock(&ses->chan_lock);
+ for (i = 1; i < ses->chan_count; i++) {
+ nserver = ses->chans[i].server;
+ if (!nserver)
+ continue;
+ nserver->srv_count++;
+ list_add(&nserver->rlist, &reco);
+ }
+ spin_unlock(&ses->chan_lock);
+ spin_unlock(&ses->ses_lock);
}
- spin_unlock(&ses->chan_lock);
}
- spin_unlock(&cifs_tcp_ses_lock);
+
+ list_for_each_entry_safe(server, nserver, &reco, rlist) {
+ list_del_init(&server->rlist);
+ set_need_reco(server);
+ cifs_put_tcp_session(server, 0);
+ }
}
/*
@@ -316,6 +310,8 @@ cifs_abort_connection(struct TCP_Server_Info *server)
server->ssocket->flags);
sock_release(server->ssocket);
server->ssocket = NULL;
+ } else if (cifs_rdma_enabled(server)) {
+ smbd_destroy(server);
}
server->sequence_number = 0;
server->session_estab = false;
@@ -327,28 +323,22 @@ cifs_abort_connection(struct TCP_Server_Info *server)
/* mark submitted MIDs for retry and issue callback */
INIT_LIST_HEAD(&retry_list);
cifs_dbg(FYI, "%s: moving mids to private list\n", __func__);
- spin_lock(&server->mid_lock);
+ spin_lock(&server->mid_queue_lock);
list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) {
- kref_get(&mid->refcount);
+ smb_get_mid(mid);
if (mid->mid_state == MID_REQUEST_SUBMITTED)
mid->mid_state = MID_RETRY_NEEDED;
list_move(&mid->qhead, &retry_list);
- mid->mid_flags |= MID_DELETED;
+ mid->deleted_from_q = true;
}
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
cifs_server_unlock(server);
cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
list_for_each_entry_safe(mid, nmid, &retry_list, qhead) {
list_del_init(&mid->qhead);
- mid->callback(mid);
- release_mid(mid);
- }
-
- if (cifs_rdma_enabled(server)) {
- cifs_server_lock(server);
- smbd_destroy(server);
- cifs_server_unlock(server);
+ mid_execute_callback(server, mid);
+ release_mid(server, mid);
}
}
@@ -364,7 +354,7 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
}
cifs_dbg(FYI, "Mark tcp session as need reconnect\n");
- trace_smb3_reconnect(server->CurrentMid, server->conn_id,
+ trace_smb3_reconnect(server->current_mid, server->conn_id,
server->hostname);
server->tcpStatus = CifsNeedReconnect;
@@ -386,13 +376,20 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
*
*/
static int __cifs_reconnect(struct TCP_Server_Info *server,
- bool mark_smb_session)
+ bool mark_smb_session, bool once)
{
int rc = 0;
if (!cifs_tcp_ses_needs_reconnect(server, 1))
return 0;
+ /*
+ * if smb session has been marked for reconnect, also reconnect all
+ * connections. This way, the other connections do not end up bad.
+ */
+ if (mark_smb_session)
+ cifs_signal_cifsd_for_reconnect(server, mark_smb_session);
+
cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session);
cifs_abort_connection(server);
@@ -401,7 +398,8 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
try_to_freeze();
cifs_server_lock(server);
- if (!cifs_swn_set_server_dstaddr(server)) {
+ if (!cifs_swn_set_server_dstaddr(server) &&
+ !SERVER_IS_CHAN(server)) {
/* resolve the hostname again to make sure that IP address is up-to-date */
rc = reconn_set_ipaddr_from_hostname(server);
cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc);
@@ -414,6 +412,9 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
if (rc) {
cifs_server_unlock(server);
cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
+ /* If was asked to reconnect only once, do not try it more times */
+ if (once)
+ break;
msleep(3000);
} else {
atomic_inc(&tcpSesReconnectCount);
@@ -424,7 +425,7 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
spin_unlock(&server->srv_lock);
cifs_swn_reset_server_dstaddr(server);
cifs_server_unlock(server);
- mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
+ cifs_queue_server_reconn(server);
}
} while (server->tcpStatus == CifsNeedReconnect);
@@ -438,7 +439,8 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
}
#ifdef CONFIG_CIFS_DFS_UPCALL
-static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const char *target)
+static int __reconnect_target_locked(struct TCP_Server_Info *server,
+ const char *target)
{
int rc;
char *hostname;
@@ -471,34 +473,43 @@ static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const cha
return rc;
}
-static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_cache_tgt_list *tl,
- struct dfs_cache_tgt_iterator **target_hint)
+static int reconnect_target_locked(struct TCP_Server_Info *server,
+ struct dfs_cache_tgt_list *tl,
+ struct dfs_cache_tgt_iterator **target_hint)
{
- int rc;
struct dfs_cache_tgt_iterator *tit;
+ int rc;
*target_hint = NULL;
/* If dfs target list is empty, then reconnect to last server */
tit = dfs_cache_get_tgt_iterator(tl);
if (!tit)
- return __reconnect_target_unlocked(server, server->hostname);
+ return __reconnect_target_locked(server, server->hostname);
/* Otherwise, try every dfs target in @tl */
- for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
- rc = __reconnect_target_unlocked(server, dfs_cache_get_tgt_name(tit));
+ do {
+ const char *target = dfs_cache_get_tgt_name(tit);
+
+ spin_lock(&server->srv_lock);
+ if (server->tcpStatus != CifsNeedReconnect) {
+ spin_unlock(&server->srv_lock);
+ return -ECONNRESET;
+ }
+ spin_unlock(&server->srv_lock);
+ rc = __reconnect_target_locked(server, target);
if (!rc) {
*target_hint = tit;
break;
}
- }
+ } while ((tit = dfs_cache_get_next_tgt(tl, tit)));
return rc;
}
static int reconnect_dfs_server(struct TCP_Server_Info *server)
{
struct dfs_cache_tgt_iterator *target_hint = NULL;
-
+ const char *ref_path = server->leaf_fullpath + 1;
DFS_CACHE_TGT_LIST(tl);
int num_targets = 0;
int rc = 0;
@@ -511,10 +522,8 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
* through /proc/fs/cifs/dfscache or the target list is empty due to server settings after
* refreshing the referral, so, in this case, default it to 1.
*/
- mutex_lock(&server->refpath_lock);
- if (!dfs_cache_noreq_find(server->leaf_fullpath + 1, NULL, &tl))
+ if (!dfs_cache_noreq_find(ref_path, NULL, &tl))
num_targets = dfs_cache_get_nr_tgts(&tl);
- mutex_unlock(&server->refpath_lock);
if (!num_targets)
num_targets = 1;
@@ -534,7 +543,7 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
try_to_freeze();
cifs_server_lock(server);
- rc = reconnect_target_unlocked(server, &tl, &target_hint);
+ rc = reconnect_target_locked(server, &tl, &target_hint);
if (rc) {
/* Failed to reconnect socket */
cifs_server_unlock(server);
@@ -555,12 +564,10 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
spin_unlock(&server->srv_lock);
cifs_swn_reset_server_dstaddr(server);
cifs_server_unlock(server);
- mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
+ cifs_queue_server_reconn(server);
} while (server->tcpStatus == CifsNeedReconnect);
- mutex_lock(&server->refpath_lock);
- dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, target_hint);
- mutex_unlock(&server->refpath_lock);
+ dfs_cache_noreq_update_tgthint(ref_path, target_hint);
dfs_cache_free_tgts(&tl);
/* Need to set up echo worker again once connection has been established */
@@ -573,24 +580,33 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
return rc;
}
-int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
+static int
+_cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session, bool once)
{
- mutex_lock(&server->refpath_lock);
- if (!server->leaf_fullpath) {
- mutex_unlock(&server->refpath_lock);
- return __cifs_reconnect(server, mark_smb_session);
- }
- mutex_unlock(&server->refpath_lock);
-
+ if (!server->leaf_fullpath)
+ return __cifs_reconnect(server, mark_smb_session, once);
return reconnect_dfs_server(server);
}
#else
-int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
+static int
+_cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session, bool once)
{
- return __cifs_reconnect(server, mark_smb_session);
+ return __cifs_reconnect(server, mark_smb_session, once);
}
#endif
+int
+cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
+{
+ return _cifs_reconnect(server, mark_smb_session, false);
+}
+
+static int
+cifs_reconnect_once(struct TCP_Server_Info *server)
+{
+ return _cifs_reconnect(server, true, true);
+}
+
static void
cifs_echo_request(struct work_struct *work)
{
@@ -659,12 +675,12 @@ server_unresponsive(struct TCP_Server_Info *server)
/*
* If we're in the process of mounting a share or reconnecting a session
* and the server abruptly shut down (e.g. socket wasn't closed, packet
- * had been ACK'ed but no SMB response), don't wait longer than 20s to
- * negotiate protocol.
+ * had been ACK'ed but no SMB response), don't wait longer than 20s from
+ * when negotiate actually started.
*/
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsInNegotiate &&
- time_after(jiffies, server->lstrp + 20 * HZ)) {
+ time_after(jiffies, server->neg_start + 20 * HZ)) {
spin_unlock(&server->srv_lock);
cifs_reconnect(server, false);
return true;
@@ -817,26 +833,110 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
/* Regular SMB response */
return true;
case RFC1002_SESSION_KEEP_ALIVE:
+ /*
+ * RFC 1002 session keep alive can sent by the server only when
+ * we established a RFC 1002 session. But Samba servers send
+ * RFC 1002 session keep alive also over port 445 on which
+ * RFC 1002 session is not established.
+ */
cifs_dbg(FYI, "RFC 1002 session keep alive\n");
break;
case RFC1002_POSITIVE_SESSION_RESPONSE:
- cifs_dbg(FYI, "RFC 1002 positive session response\n");
+ /*
+ * RFC 1002 positive session response cannot be returned
+ * for SMB request. RFC 1002 session response is handled
+ * exclusively in ip_rfc1001_connect() function.
+ */
+ cifs_server_dbg(VFS, "RFC 1002 positive session response (unexpected)\n");
+ cifs_reconnect(server, true);
break;
case RFC1002_NEGATIVE_SESSION_RESPONSE:
/*
* We get this from Windows 98 instead of an error on
- * SMB negprot response.
- */
- cifs_dbg(FYI, "RFC 1002 negative session response\n");
- /* give server a second to clean up */
- msleep(1000);
- /*
- * Always try 445 first on reconnect since we get NACK
- * on some if we ever connected to port 139 (the NACK
- * is since we do not begin with RFC1001 session
- * initialize frame).
+ * SMB negprot response, when we have not established
+ * RFC 1002 session (which means ip_rfc1001_connect()
+ * was skipped). Note that same still happens with
+ * Windows Server 2022 when connecting via port 139.
+ * So for this case when mount option -o nonbsessinit
+ * was not specified, try to reconnect with establishing
+ * RFC 1002 session. If new socket establishment with
+ * RFC 1002 session was successful then return to the
+ * mid's caller -EAGAIN, so it can retry the request.
*/
- cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
+ if (!cifs_rdma_enabled(server) &&
+ server->tcpStatus == CifsInNegotiate &&
+ !server->with_rfc1001 &&
+ server->rfc1001_sessinit != 0) {
+ int rc, mid_rc;
+ struct mid_q_entry *mid, *nmid;
+ LIST_HEAD(dispose_list);
+
+ cifs_dbg(FYI, "RFC 1002 negative session response during SMB Negotiate, retrying with NetBIOS session\n");
+
+ /*
+ * Before reconnect, delete all pending mids for this
+ * server, so reconnect would not signal connection
+ * aborted error to mid's callbacks. Note that for this
+ * server there should be exactly one pending mid
+ * corresponding to SMB1/SMB2 Negotiate packet.
+ */
+ spin_lock(&server->mid_queue_lock);
+ list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) {
+ smb_get_mid(mid);
+ list_move(&mid->qhead, &dispose_list);
+ mid->deleted_from_q = true;
+ }
+ spin_unlock(&server->mid_queue_lock);
+
+ /* Now try to reconnect once with NetBIOS session. */
+ server->with_rfc1001 = true;
+ rc = cifs_reconnect_once(server);
+
+ /*
+ * If reconnect was successful then indicate -EAGAIN
+ * to mid's caller. If reconnect failed with -EAGAIN
+ * then mask it as -EHOSTDOWN, so mid's caller would
+ * know that it failed.
+ */
+ if (rc == 0)
+ mid_rc = -EAGAIN;
+ else if (rc == -EAGAIN)
+ mid_rc = -EHOSTDOWN;
+ else
+ mid_rc = rc;
+
+ /*
+ * After reconnect (either successful or unsuccessful)
+ * deliver reconnect status to mid's caller via mid's
+ * callback. Use MID_RC state which indicates that the
+ * return code should be read from mid_rc member.
+ */
+ list_for_each_entry_safe(mid, nmid, &dispose_list, qhead) {
+ list_del_init(&mid->qhead);
+ mid->mid_rc = mid_rc;
+ mid->mid_state = MID_RC;
+ mid_execute_callback(server, mid);
+ release_mid(server, mid);
+ }
+
+ /*
+ * If reconnect failed then wait two seconds. In most
+ * cases we were been called from the mount context and
+ * delivered failure to mid's callback will stop this
+ * receiver task thread and fails the mount process.
+ * So wait two seconds to prevent another reconnect
+ * in this task thread, which would be useless as the
+ * mount context will fail at all.
+ */
+ if (rc != 0)
+ msleep(2000);
+ } else {
+ cifs_server_dbg(VFS, "RFC 1002 negative session response (unexpected)\n");
+ cifs_reconnect(server, true);
+ }
+ break;
+ case RFC1002_RETARGET_SESSION_RESPONSE:
+ cifs_server_dbg(VFS, "RFC 1002 retarget session response (unexpected)\n");
cifs_reconnect(server, true);
break;
default:
@@ -848,12 +948,12 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
}
void
-dequeue_mid(struct mid_q_entry *mid, bool malformed)
+dequeue_mid(struct TCP_Server_Info *server, struct mid_q_entry *mid, bool malformed)
{
#ifdef CONFIG_CIFS_STATS2
mid->when_received = jiffies;
#endif
- spin_lock(&mid->server->mid_lock);
+ spin_lock(&server->mid_queue_lock);
if (!malformed)
mid->mid_state = MID_RESPONSE_RECEIVED;
else
@@ -862,13 +962,13 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)
* Trying to handle/dequeue a mid after the send_recv()
* function has finished processing it is a bug.
*/
- if (mid->mid_flags & MID_DELETED) {
- spin_unlock(&mid->server->mid_lock);
+ if (mid->deleted_from_q == true) {
+ spin_unlock(&server->mid_queue_lock);
pr_warn_once("trying to dequeue a deleted mid\n");
} else {
list_del_init(&mid->qhead);
- mid->mid_flags |= MID_DELETED;
- spin_unlock(&mid->server->mid_lock);
+ mid->deleted_from_q = true;
+ spin_unlock(&server->mid_queue_lock);
}
}
@@ -904,7 +1004,7 @@ handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server,
else
server->smallbuf = NULL;
}
- dequeue_mid(mid, malformed);
+ dequeue_mid(server, mid, malformed);
}
int
@@ -997,24 +1097,24 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
struct list_head *tmp, *tmp2;
LIST_HEAD(dispose_list);
- spin_lock(&server->mid_lock);
+ spin_lock(&server->mid_queue_lock);
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
cifs_dbg(FYI, "Clearing mid %llu\n", mid_entry->mid);
- kref_get(&mid_entry->refcount);
+ smb_get_mid(mid_entry);
mid_entry->mid_state = MID_SHUTDOWN;
list_move(&mid_entry->qhead, &dispose_list);
- mid_entry->mid_flags |= MID_DELETED;
+ mid_entry->deleted_from_q = true;
}
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
/* now walk dispose list and issue callbacks */
list_for_each_safe(tmp, tmp2, &dispose_list) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
cifs_dbg(FYI, "Callback mid %llu\n", mid_entry->mid);
list_del_init(&mid_entry->qhead);
- mid_entry->callback(mid_entry);
- release_mid(mid_entry);
+ mid_execute_callback(server, mid_entry);
+ release_mid(server, mid_entry);
}
/* 1/8th of sec is more than enough time for them to exit */
msleep(125);
@@ -1039,6 +1139,7 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
put_net(cifs_net_ns(server));
kfree(server->leaf_fullpath);
+ kfree(server->hostname);
kfree(server);
length = atomic_dec_return(&tcpSesAllocCount);
@@ -1054,15 +1155,14 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
unsigned int pdu_length = server->pdu_size;
/* make sure this will fit in a large buffer */
- if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
- HEADER_PREAMBLE_SIZE(server)) {
+ if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) {
cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
cifs_reconnect(server, true);
return -ECONNABORTED;
}
/* switch to large buffer if too big for a small one */
- if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
+ if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) {
server->large_buf = true;
memcpy(server->bigbuf, buf, server->total_read);
buf = server->bigbuf;
@@ -1095,7 +1195,8 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
* 48 bytes is enough to display the header and a little bit
* into the payload for debugging purposes.
*/
- rc = server->ops->check_message(buf, server->total_read, server);
+ rc = server->ops->check_message(buf, server->pdu_size,
+ server->total_read, server);
if (rc)
cifs_dump_mem("Bad SMB: ", buf,
min_t(unsigned int, server->total_read, 48));
@@ -1137,7 +1238,7 @@ smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
- trace_smb3_hdr_credits(server->CurrentMid,
+ trace_smb3_hdr_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
le16_to_cpu(shdr->CreditRequest), in_flight);
cifs_server_dbg(FYI, "%s: added %u credits total=%d\n",
@@ -1185,16 +1286,13 @@ cifs_demultiplex_thread(void *p)
if (length < 0)
continue;
- if (is_smb1(server))
- server->total_read = length;
- else
- server->total_read = 0;
+ server->total_read = 0;
/*
* The right amount was read from socket - 4 bytes,
* so we can now interpret the length field.
*/
- pdu_length = get_rfc1002_length(buf);
+ pdu_length = be32_to_cpup(((__be32 *)buf)) & 0xffffff;
cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
if (!is_smb_response(server, buf[0]))
@@ -1213,9 +1311,8 @@ next_pdu:
}
/* read down to the MID */
- length = cifs_read_from_socket(server,
- buf + HEADER_PREAMBLE_SIZE(server),
- MID_HEADER_SIZE(server));
+ length = cifs_read_from_socket(server, buf,
+ MID_HEADER_SIZE(server));
if (length < 0)
continue;
server->total_read += length;
@@ -1247,6 +1344,8 @@ next_pdu:
bufs[0] = buf;
num_mids = 1;
+ if (mids[0])
+ mids[0]->response_pdu_len = pdu_length;
if (!mids[0] || !mids[0]->receive)
length = standard_receive3(server, mids[0]);
else
@@ -1256,7 +1355,7 @@ next_pdu:
if (length < 0) {
for (i = 0; i < num_mids; i++)
if (mids[i])
- release_mid(mids[i]);
+ release_mid(server, mids[i]);
continue;
}
@@ -1289,9 +1388,9 @@ next_pdu:
}
if (!mids[i]->multiRsp || mids[i]->multiEnd)
- mids[i]->callback(mids[i]);
+ mid_execute_callback(server, mids[i]);
- release_mid(mids[i]);
+ release_mid(server, mids[i]);
} else if (server->ops->is_oplock_break &&
server->ops->is_oplock_break(bufs[i],
server)) {
@@ -1305,7 +1404,7 @@ next_pdu:
smb2_add_credits_from_hdr(bufs[i], server);
#ifdef CONFIG_CIFS_DEBUG2
if (server->ops->dump_detail)
- server->ops->dump_detail(bufs[i],
+ server->ops->dump_detail(bufs[i], pdu_length,
server);
cifs_dump_mids(server);
#endif /* CIFS_DEBUG2 */
@@ -1535,42 +1634,10 @@ static int match_server(struct TCP_Server_Info *server,
if (!cifs_match_ipaddr((struct sockaddr *)&ctx->srcaddr,
(struct sockaddr *)&server->srcaddr))
return 0;
- /*
- * When matching cifs.ko superblocks (@match_super == true), we can't
- * really match either @server->leaf_fullpath or @server->dstaddr
- * directly since this @server might belong to a completely different
- * server -- in case of domain-based DFS referrals or DFS links -- as
- * provided earlier by mount(2) through 'source' and 'ip' options.
- *
- * Otherwise, match the DFS referral in @server->leaf_fullpath or the
- * destination address in @server->dstaddr.
- *
- * When using 'nodfs' mount option, we avoid sharing it with DFS
- * connections as they might failover.
- */
- if (!match_super) {
- if (!ctx->nodfs) {
- if (server->leaf_fullpath) {
- if (!ctx->leaf_fullpath ||
- strcasecmp(server->leaf_fullpath,
- ctx->leaf_fullpath))
- return 0;
- } else if (ctx->leaf_fullpath) {
- return 0;
- }
- } else if (server->leaf_fullpath) {
- return 0;
- }
- }
- /*
- * Match for a regular connection (address/hostname/port) which has no
- * DFS referrals set.
- */
- if (!server->leaf_fullpath &&
- (strcasecmp(server->hostname, ctx->server_hostname) ||
- !match_server_address(server, addr) ||
- !match_port(server, addr)))
+ if (strcasecmp(server->hostname, ctx->server_hostname) ||
+ !match_server_address(server, addr) ||
+ !match_port(server, addr))
return 0;
if (!match_security(server, ctx))
@@ -1665,8 +1732,6 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
kfree_sensitive(server->session_key.response);
server->session_key.response = NULL;
server->session_key.len = 0;
- kfree(server->hostname);
- server->hostname = NULL;
task = xchg(&server->tsk, NULL);
if (task)
@@ -1706,6 +1771,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
goto out_err;
}
}
+ if (ctx->dns_dom)
+ strscpy(tcp_ses->dns_dom, ctx->dns_dom);
if (ctx->nosharesock)
tcp_ses->nosharesock = true;
@@ -1715,6 +1782,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
tcp_ses->vals = ctx->vals;
cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));
+ tcp_ses->sign = ctx->sign;
tcp_ses->conn_id = atomic_inc_return(&tcpSesNextId);
tcp_ses->noblockcnt = ctx->rootfs;
tcp_ses->noblocksnd = ctx->noblocksnd || ctx->rootfs;
@@ -1738,6 +1806,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
ctx->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
memcpy(tcp_ses->server_RFC1001_name,
ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
+ tcp_ses->rfc1001_sessinit = ctx->rfc1001_sessinit;
+ tcp_ses->with_rfc1001 = false;
tcp_ses->session_estab = false;
tcp_ses->sequence_number = 0;
tcp_ses->channel_sequence_num = 0; /* only tracked for primary channel */
@@ -1746,15 +1816,13 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
tcp_ses->compression.requested = ctx->compress;
spin_lock_init(&tcp_ses->req_lock);
spin_lock_init(&tcp_ses->srv_lock);
- spin_lock_init(&tcp_ses->mid_lock);
+ spin_lock_init(&tcp_ses->mid_queue_lock);
+ spin_lock_init(&tcp_ses->mid_counter_lock);
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
mutex_init(&tcp_ses->reconnect_mutex);
-#ifdef CONFIG_CIFS_DFS_UPCALL
- mutex_init(&tcp_ses->refpath_lock);
-#endif
memcpy(&tcp_ses->srcaddr, &ctx->srcaddr,
sizeof(tcp_ses->srcaddr));
memcpy(&tcp_ses->dstaddr, &ctx->dstaddr,
@@ -1771,12 +1839,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
*/
tcp_ses->tcpStatus = CifsNew;
++tcp_ses->srv_count;
+ tcp_ses->echo_interval = ctx->echo_interval * HZ;
- if (ctx->echo_interval >= SMB_ECHO_INTERVAL_MIN &&
- ctx->echo_interval <= SMB_ECHO_INTERVAL_MAX)
- tcp_ses->echo_interval = ctx->echo_interval * HZ;
- else
- tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ;
if (tcp_ses->rdma) {
#ifndef CONFIG_CIFS_SMB_DIRECT
cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n");
@@ -1864,9 +1928,8 @@ static int match_session(struct cifs_ses *ses,
struct smb3_fs_context *ctx,
bool match_super)
{
- if (ctx->sectype != Unspecified &&
- ctx->sectype != ses->sectype)
- return 0;
+ struct TCP_Server_Info *server = ses->server;
+ enum securityEnum ctx_sec, ses_sec;
if (!match_super && ctx->dfs_root_ses != ses->dfs_root_ses)
return 0;
@@ -1878,11 +1941,20 @@ static int match_session(struct cifs_ses *ses,
if (ses->chan_max < ctx->max_channels)
return 0;
- switch (ses->sectype) {
+ ctx_sec = server->ops->select_sectype(server, ctx->sectype);
+ ses_sec = server->ops->select_sectype(server, ses->sectype);
+
+ if (ctx_sec != ses_sec)
+ return 0;
+
+ switch (ctx_sec) {
+ case IAKerb:
case Kerberos:
if (!uid_eq(ctx->cred_uid, ses->cred_uid))
return 0;
break;
+ case NTLMv2:
+ case RawNTLMSSP:
default:
/* NULL username means anonymous session */
if (ses->user_name == NULL) {
@@ -1897,11 +1969,35 @@ static int match_session(struct cifs_ses *ses,
CIFS_MAX_USERNAME_LEN))
return 0;
if ((ctx->username && strlen(ctx->username) != 0) &&
- ses->password != NULL &&
- strncmp(ses->password,
- ctx->password ? ctx->password : "",
- CIFS_MAX_PASSWORD_LEN))
- return 0;
+ ses->password != NULL) {
+
+ /* New mount can only share sessions with an existing mount if:
+ * 1. Both password and password2 match, or
+ * 2. password2 of the old mount matches password of the new mount
+ * and password of the old mount matches password2 of the new
+ * mount
+ */
+ if (ses->password2 != NULL && ctx->password2 != NULL) {
+ if (!((strncmp(ses->password, ctx->password ?
+ ctx->password : "", CIFS_MAX_PASSWORD_LEN) == 0 &&
+ strncmp(ses->password2, ctx->password2,
+ CIFS_MAX_PASSWORD_LEN) == 0) ||
+ (strncmp(ses->password, ctx->password2,
+ CIFS_MAX_PASSWORD_LEN) == 0 &&
+ strncmp(ses->password2, ctx->password ?
+ ctx->password : "", CIFS_MAX_PASSWORD_LEN) == 0)))
+ return 0;
+
+ } else if ((ses->password2 == NULL && ctx->password2 != NULL) ||
+ (ses->password2 != NULL && ctx->password2 == NULL)) {
+ return 0;
+
+ } else {
+ if (strncmp(ses->password, ctx->password ?
+ ctx->password : "", CIFS_MAX_PASSWORD_LEN))
+ return 0;
+ }
+ }
}
if (strcmp(ctx->local_nls->charset, ses->local_nls->charset))
@@ -1913,39 +2009,31 @@ static int match_session(struct cifs_ses *ses,
/**
* cifs_setup_ipc - helper to setup the IPC tcon for the session
* @ses: smb session to issue the request on
- * @ctx: the superblock configuration context to use for building the
- * new tree connection for the IPC (interprocess communication RPC)
+ * @seal: if encryption is requested
*
* A new IPC connection is made and stored in the session
* tcon_ipc. The IPC tcon has the same lifetime as the session.
*/
-static int
-cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
+struct cifs_tcon *cifs_setup_ipc(struct cifs_ses *ses, bool seal)
{
int rc = 0, xid;
struct cifs_tcon *tcon;
char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0};
- bool seal = false;
struct TCP_Server_Info *server = ses->server;
/*
* If the mount request that resulted in the creation of the
* session requires encryption, force IPC to be encrypted too.
*/
- if (ctx->seal) {
- if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)
- seal = true;
- else {
- cifs_server_dbg(VFS,
- "IPC: server doesn't support encryption\n");
- return -EOPNOTSUPP;
- }
+ if (seal && !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) {
+ cifs_server_dbg(VFS, "IPC: server doesn't support encryption\n");
+ return ERR_PTR(-EOPNOTSUPP);
}
/* no need to setup directory caching on IPC share, so pass in false */
tcon = tcon_info_alloc(false, netfs_trace_tcon_ref_new_ipc);
if (tcon == NULL)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
spin_lock(&server->srv_lock);
scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);
@@ -1955,13 +2043,13 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
tcon->ses = ses;
tcon->ipc = true;
tcon->seal = seal;
- rc = server->ops->tree_connect(xid, ses, unc, tcon, ctx->local_nls);
+ rc = server->ops->tree_connect(xid, ses, unc, tcon, ses->local_nls);
free_xid(xid);
if (rc) {
- cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
+ cifs_server_dbg(VFS | ONCE, "failed to connect to IPC (rc=%d)\n", rc);
tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc_fail);
- goto out;
+ return ERR_PTR(rc);
}
cifs_dbg(FYI, "IPC tcon rc=%d ipc tid=0x%x\n", rc, tcon->tid);
@@ -1969,9 +2057,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
spin_lock(&tcon->tc_lock);
tcon->status = TID_GOOD;
spin_unlock(&tcon->tc_lock);
- ses->tcon_ipc = tcon;
-out:
- return rc;
+ return tcon;
}
static struct cifs_ses *
@@ -2224,8 +2310,8 @@ out_err:
}
#else /* ! CONFIG_KEYS */
static inline int
-cifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)),
- struct cifs_ses *ses __attribute__((unused)))
+cifs_set_cifscreds(struct smb3_fs_context *ctx __maybe_unused,
+ struct cifs_ses *ses __maybe_unused)
{
return -ENOSYS;
}
@@ -2243,11 +2329,14 @@ cifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)),
struct cifs_ses *
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
{
- int rc = 0;
- unsigned int xid;
- struct cifs_ses *ses;
- struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
+ struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
+ struct cifs_tcon *ipc;
+ struct cifs_ses *ses;
+ unsigned int xid;
+ int retries = 0;
+ size_t len;
+ int rc = 0;
xid = get_xid();
@@ -2262,6 +2351,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
cifs_dbg(FYI, "Session needs reconnect\n");
mutex_lock(&ses->session_mutex);
+
+retry_old_session:
rc = cifs_negotiate_protocol(xid, ses, server);
if (rc) {
mutex_unlock(&ses->session_mutex);
@@ -2274,6 +2365,13 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
rc = cifs_setup_session(xid, ses, server,
ctx->local_nls);
if (rc) {
+ if (((rc == -EACCES) || (rc == -EKEYEXPIRED) ||
+ (rc == -EKEYREVOKED)) && !retries && ses->password2) {
+ retries++;
+ cifs_dbg(FYI, "Session reconnect failed, retrying with alternate password\n");
+ swap(ses->password, ses->password2);
+ goto retry_old_session;
+ }
mutex_unlock(&ses->session_mutex);
/* problem -- put our reference */
cifs_put_smb_ses(ses);
@@ -2328,6 +2426,14 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL);
if (!ses->domainName)
goto get_ses_fail;
+
+ len = strnlen(ctx->domainname, CIFS_MAX_DOMAINNAME_LEN);
+ if (!cifs_netbios_name(ctx->domainname, len)) {
+ ses->dns_dom = kstrndup(ctx->domainname,
+ len, GFP_KERNEL);
+ if (!ses->dns_dom)
+ goto get_ses_fail;
+ }
}
strscpy(ses->workstation_name, ctx->workstation_name, sizeof(ses->workstation_name));
@@ -2337,6 +2443,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
ses->cred_uid = ctx->cred_uid;
ses->linux_uid = ctx->linux_uid;
+ ses->unicode = ctx->unicode;
ses->sectype = ctx->sectype;
ses->sign = ctx->sign;
@@ -2369,6 +2476,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
ses->chans_need_reconnect = 1;
spin_unlock(&ses->chan_lock);
+retry_new_session:
mutex_lock(&ses->session_mutex);
rc = cifs_negotiate_protocol(xid, ses, server);
if (!rc)
@@ -2381,8 +2489,16 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
sizeof(ses->smb3signingkey));
spin_unlock(&ses->chan_lock);
- if (rc)
- goto get_ses_fail;
+ if (rc) {
+ if (((rc == -EACCES) || (rc == -EKEYEXPIRED) ||
+ (rc == -EKEYREVOKED)) && !retries && ses->password2) {
+ retries++;
+ cifs_dbg(FYI, "Session setup failed, retrying with alternate password\n");
+ swap(ses->password, ses->password2);
+ goto retry_new_session;
+ } else
+ goto get_ses_fail;
+ }
/*
* success, put it on the list and add it as first channel
@@ -2394,7 +2510,12 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
list_add(&ses->smb_ses_list, &server->smb_ses_list);
spin_unlock(&cifs_tcp_ses_lock);
- cifs_setup_ipc(ses, ctx);
+ ipc = cifs_setup_ipc(ses, ctx->seal);
+ spin_lock(&cifs_tcp_ses_lock);
+ spin_lock(&ses->ses_lock);
+ ses->tcon_ipc = !IS_ERR(ipc) ? ipc : NULL;
+ spin_unlock(&ses->ses_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
free_xid(xid);
@@ -2433,6 +2554,8 @@ static int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
return 0;
if (tcon->nodelete != ctx->nodelete)
return 0;
+ if (tcon->posix_extensions != ctx->linux_ext)
+ return 0;
return 1;
}
@@ -2489,9 +2612,6 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
list_del_init(&tcon->tcon_list);
tcon->status = TID_EXITING;
-#ifdef CONFIG_CIFS_DFS_UPCALL
- list_replace_init(&tcon->dfs_ses_list, &ses_list);
-#endif
spin_unlock(&tcon->tc_lock);
spin_unlock(&cifs_tcp_ses_lock);
@@ -2499,6 +2619,7 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
cancel_delayed_work_sync(&tcon->query_interfaces);
#ifdef CONFIG_CIFS_DFS_UPCALL
cancel_delayed_work_sync(&tcon->dfs_cache_work);
+ list_replace_init(&tcon->dfs_ses_list, &ses_list);
#endif
if (tcon->use_witness) {
@@ -2571,7 +2692,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
if (ses->server->dialect >= SMB20_PROT_ID &&
(ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING))
- nohandlecache = ctx->nohandlecache;
+ nohandlecache = ctx->nohandlecache || !dir_cache_timeout;
else
nohandlecache = true;
tcon = tcon_info_alloc(!nohandlecache, netfs_trace_tcon_ref_new);
@@ -2750,20 +2871,14 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
tcon->max_cached_dirs = ctx->max_cached_dirs;
tcon->nodelete = ctx->nodelete;
tcon->local_lease = ctx->local_lease;
- INIT_LIST_HEAD(&tcon->pending_opens);
tcon->status = TID_GOOD;
- INIT_DELAYED_WORK(&tcon->query_interfaces,
- smb2_query_server_interfaces);
if (ses->server->dialect >= SMB30_PROT_ID &&
(ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
/* schedule query interfaces poll */
queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
(SMB_INTERFACE_POLL_INTERVAL * HZ));
}
-#ifdef CONFIG_CIFS_DFS_UPCALL
- INIT_DELAYED_WORK(&tcon->dfs_cache_work, dfs_cache_refresh);
-#endif
spin_lock(&cifs_tcp_ses_lock);
list_add(&tcon->tcon_list, &ses->tcon_list);
spin_unlock(&cifs_tcp_ses_lock);
@@ -2838,6 +2953,10 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
return 0;
if (old->ctx->reparse_type != new->ctx->reparse_type)
return 0;
+ if (old->ctx->nonativesocket != new->ctx->nonativesocket)
+ return 0;
+ if (old->ctx->symlink_type != new->ctx->symlink_type)
+ return 0;
return 1;
}
@@ -2983,7 +3102,7 @@ bind_socket(struct TCP_Server_Info *server)
struct socket *socket = server->ssocket;
rc = kernel_bind(socket,
- (struct sockaddr *) &server->srcaddr,
+ (struct sockaddr_unsized *) &server->srcaddr,
sizeof(server->srcaddr));
if (rc < 0) {
struct sockaddr_in *saddr4;
@@ -3003,6 +3122,44 @@ bind_socket(struct TCP_Server_Info *server)
}
static int
+smb_recv_kvec(struct TCP_Server_Info *server, struct msghdr *msg, size_t *recv)
+{
+ int rc = 0;
+ int retries = 0;
+ int msg_flags = server->noblocksnd ? MSG_DONTWAIT : 0;
+
+ *recv = 0;
+
+ while (msg_data_left(msg)) {
+ rc = sock_recvmsg(server->ssocket, msg, msg_flags);
+ if (rc == -EAGAIN) {
+ retries++;
+ if (retries >= 14 ||
+ (!server->noblocksnd && (retries > 2))) {
+ cifs_server_dbg(VFS, "sends on sock %p stuck for 15 seconds\n",
+ server->ssocket);
+ return -EAGAIN;
+ }
+ msleep(1 << retries);
+ continue;
+ }
+
+ if (rc < 0)
+ return rc;
+
+ if (rc == 0) {
+ cifs_dbg(FYI, "Received no data (TCP RST)\n");
+ return -ECONNABORTED;
+ }
+
+ /* recv was at least partially successful */
+ *recv += rc;
+ retries = 0; /* in case we get ENOSPC on the next send */
+ }
+ return 0;
+}
+
+static int
ip_rfc1001_connect(struct TCP_Server_Info *server)
{
int rc = 0;
@@ -3012,8 +3169,12 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
* sessinit is sent but no second negprot
*/
struct rfc1002_session_packet req = {};
- struct smb_hdr *smb_buf = (struct smb_hdr *)&req;
+ struct rfc1002_session_packet resp = {};
+ struct msghdr msg = {};
+ struct kvec iov = {};
unsigned int len;
+ size_t sent;
+ size_t recv;
req.trailer.session_req.called_len = sizeof(req.trailer.session_req.called_name);
@@ -3042,19 +3203,120 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
* As per rfc1002, @len must be the number of bytes that follows the
* length field of a rfc1002 session request payload.
*/
- len = sizeof(req) - offsetof(struct rfc1002_session_packet, trailer.session_req);
+ len = sizeof(req.trailer.session_req);
+ req.type = RFC1002_SESSION_REQUEST;
+ req.flags = 0;
+ req.length = cpu_to_be16(len);
+ len += offsetof(typeof(req), trailer.session_req);
+ iov.iov_base = &req;
+ iov.iov_len = len;
+ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, &iov, 1, len);
+ rc = smb_send_kvec(server, &msg, &sent);
+ if (rc < 0 || len != sent)
+ return (rc == -EINTR || rc == -EAGAIN) ? rc : -ECONNABORTED;
- smb_buf->smb_buf_length = cpu_to_be32((RFC1002_SESSION_REQUEST << 24) | len);
- rc = smb_send(server, smb_buf, len);
/*
* RFC1001 layer in at least one server requires very short break before
* negprot presumably because not expecting negprot to follow so fast.
- * This is a simple solution that works without complicating the code
- * and causes no significant slowing down on mount for everyone else
+ * For example DOS SMB servers cannot process negprot if it was received
+ * before the server sent response for SESSION_REQUEST packet. So, wait
+ * for the response, read it and parse it as it can contain useful error
+ * information (e.g. specified server name was incorrect). For example
+ * even the latest Windows Server 2022 SMB1 server over port 139 send
+ * error if its server name was in SESSION_REQUEST packet incorrect.
+ * Nowadays usage of port 139 is not common, so waiting for reply here
+ * does not slowing down mounting of common case (over port 445).
*/
- usleep_range(1000, 2000);
+ len = offsetof(typeof(resp), trailer);
+ iov.iov_base = &resp;
+ iov.iov_len = len;
+ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, len);
+ rc = smb_recv_kvec(server, &msg, &recv);
+ if (rc < 0 || recv != len)
+ return (rc == -EINTR || rc == -EAGAIN) ? rc : -ECONNABORTED;
+
+ switch (resp.type) {
+ case RFC1002_POSITIVE_SESSION_RESPONSE:
+ if (be16_to_cpu(resp.length) != 0) {
+ cifs_dbg(VFS, "RFC 1002 positive session response but with invalid non-zero length %u\n",
+ be16_to_cpu(resp.length));
+ return smb_EIO(smb_eio_trace_rx_pos_sess_resp);
+ }
+ cifs_dbg(FYI, "RFC 1002 positive session response");
+ break;
+ case RFC1002_NEGATIVE_SESSION_RESPONSE:
+ /* Read RFC1002 response error code and convert it to errno in rc */
+ len = sizeof(resp.trailer.neg_ses_resp_error_code);
+ iov.iov_base = &resp.trailer.neg_ses_resp_error_code;
+ iov.iov_len = len;
+ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, len);
+ if (be16_to_cpu(resp.length) == len &&
+ smb_recv_kvec(server, &msg, &recv) == 0 &&
+ recv == len) {
+ cifs_dbg(VFS, "RFC 1002 negative session response with error 0x%x\n",
+ resp.trailer.neg_ses_resp_error_code);
+ switch (resp.trailer.neg_ses_resp_error_code) {
+ case RFC1002_NOT_LISTENING_CALLED:
+ /* server does not listen for specified server name */
+ fallthrough;
+ case RFC1002_NOT_PRESENT:
+ /* server name is incorrect */
+ rc = -ENOENT;
+ cifs_dbg(VFS, "Server rejected NetBIOS servername %.15s\n",
+ server->server_RFC1001_name[0] ?
+ server->server_RFC1001_name :
+ DEFAULT_CIFS_CALLED_NAME);
+ cifs_dbg(VFS, "Specify correct NetBIOS servername in source path or with -o servern= option\n");
+ break;
+ case RFC1002_NOT_LISTENING_CALLING:
+ /* client name was not accepted by server */
+ rc = -EACCES;
+ cifs_dbg(VFS, "Server rejected NetBIOS clientname %.15s\n",
+ server->workstation_RFC1001_name[0] ?
+ server->workstation_RFC1001_name :
+ "LINUX_CIFS_CLNT");
+ cifs_dbg(VFS, "Specify correct NetBIOS clientname with -o netbiosname= option\n");
+ break;
+ case RFC1002_INSUFFICIENT_RESOURCE:
+ /* remote server resource error */
+ smb_EIO(smb_eio_trace_rx_insuff_res);
+ rc = -EREMOTEIO;
+ break;
+ case RFC1002_UNSPECIFIED_ERROR:
+ default:
+ /* other/unknown error */
+ rc = smb_EIO(smb_eio_trace_rx_unspec_error);
+ break;
+ }
+ } else {
+ cifs_dbg(VFS, "RFC 1002 negative session response\n");
+ rc = smb_EIO(smb_eio_trace_rx_neg_sess_resp);
+ }
+ return rc;
+ case RFC1002_RETARGET_SESSION_RESPONSE:
+ cifs_dbg(VFS, "RFC 1002 retarget session response\n");
+ if (be16_to_cpu(resp.length) == sizeof(resp.trailer.retarget_resp)) {
+ len = sizeof(resp.trailer.retarget_resp);
+ iov.iov_base = &resp.trailer.retarget_resp;
+ iov.iov_len = len;
+ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, len);
+ if (smb_recv_kvec(server, &msg, &recv) == 0 && recv == len) {
+ cifs_dbg(VFS, "Server wants to redirect connection\n");
+ cifs_dbg(VFS, "Remount with options -o ip=%pI4,port=%u\n",
+ &resp.trailer.retarget_resp.retarget_ip_addr,
+ be16_to_cpu(resp.trailer.retarget_resp.port));
+ }
+ }
+ cifs_dbg(VFS, "Closing connection\n");
+ /* FIXME: Should we automatically redirect to new retarget_resp server? */
+ return -EMULTIHOP;
+ default:
+ cifs_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", resp.type);
+ return smb_EIO1(smb_eio_trace_rx_unknown_resp, resp.type);
+ }
- return rc;
+ server->with_rfc1001 = true;
+ return 0;
}
static int
@@ -3092,18 +3354,15 @@ generic_ip_connect(struct TCP_Server_Info *server)
struct net *net = cifs_net_ns(server);
struct sock *sk;
- rc = __sock_create(net, sfamily, SOCK_STREAM,
- IPPROTO_TCP, &server->ssocket, 1);
+ rc = sock_create_kern(net, sfamily, SOCK_STREAM,
+ IPPROTO_TCP, &server->ssocket);
if (rc < 0) {
cifs_server_dbg(VFS, "Error %d creating socket\n", rc);
return rc;
}
sk = server->ssocket->sk;
- __netns_tracker_free(net, &sk->ns_tracker, false);
- sk->sk_net_refcnt = 1;
- get_net_track(net, &sk->ns_tracker, GFP_KERNEL);
- sock_inuse_add(net, 1);
+ sk_net_refcnt_upgrade(sk);
/* BB other socket options to set KEEPALIVE, NODELAY? */
cifs_dbg(FYI, "Socket created\n");
@@ -3143,7 +3402,7 @@ generic_ip_connect(struct TCP_Server_Info *server)
socket->sk->sk_sndbuf,
socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo);
- rc = kernel_connect(socket, saddr, slen,
+ rc = kernel_connect(socket, (struct sockaddr_unsized *)saddr, slen,
server->noblockcnt ? O_NONBLOCK : 0);
/*
* When mounting SMB root file systems, we do not want to block in
@@ -3160,7 +3419,16 @@ generic_ip_connect(struct TCP_Server_Info *server)
return rc;
}
trace_smb3_connect_done(server->hostname, server->conn_id, &server->dstaddr);
- if (sport == htons(RFC1001_PORT))
+
+ /*
+ * Establish RFC1001 NetBIOS session when it was explicitly requested
+ * by mount option -o nbsessinit, or when connecting to default RFC1001
+ * server port (139) and it was not explicitly disabled by mount option
+ * -o nonbsessinit.
+ */
+ if (server->with_rfc1001 ||
+ server->rfc1001_sessinit == 1 ||
+ (server->rfc1001_sessinit == -1 && sport == htons(RFC1001_PORT)))
rc = ip_rfc1001_connect(server);
return rc;
@@ -3309,6 +3577,7 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb)
struct smb3_fs_context *ctx = cifs_sb->ctx;
INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks);
+ INIT_LIST_HEAD(&cifs_sb->tcon_sb_link);
spin_lock_init(&cifs_sb->tlink_tree_lock);
cifs_sb->tlink_tree = RB_ROOT;
@@ -3446,9 +3715,15 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
goto out;
}
- /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */
- if (tcon->posix_extensions)
+ /*
+ * if new SMB3.11 POSIX extensions are supported, do not change anything in the
+ * path (i.e., do not remap / and \ and do not map any special characters)
+ */
+ if (tcon->posix_extensions) {
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS;
+ cifs_sb->mnt_cifs_flags &= ~(CIFS_MOUNT_MAP_SFM_CHR |
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ }
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
/* tell server which Unix caps we support */
@@ -3485,28 +3760,7 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
}
}
- /*
- * Clamp the rsize/wsize mount arguments if they are too big for the server
- * and set the rsize/wsize to the negotiated values if not passed in by
- * the user on mount
- */
- if ((cifs_sb->ctx->wsize == 0) ||
- (cifs_sb->ctx->wsize > server->ops->negotiate_wsize(tcon, ctx))) {
- cifs_sb->ctx->wsize =
- round_down(server->ops->negotiate_wsize(tcon, ctx), PAGE_SIZE);
- /*
- * in the very unlikely event that the server sent a max write size under PAGE_SIZE,
- * (which would get rounded down to 0) then reset wsize to absolute minimum eg 4096
- */
- if (cifs_sb->ctx->wsize == 0) {
- cifs_sb->ctx->wsize = PAGE_SIZE;
- cifs_dbg(VFS, "wsize too small, reset to minimum ie PAGE_SIZE, usually 4096\n");
- }
- }
- if ((cifs_sb->ctx->rsize == 0) ||
- (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx)))
- cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx);
-
+ cifs_negotiate_iosize(server, cifs_sb->ctx, tcon);
/*
* The cookie is initialized from volume info returned above.
* Inside cifs_fscache_get_super_cookie it checks
@@ -3541,6 +3795,10 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
spin_unlock(&cifs_sb->tlink_tree_lock);
+ spin_lock(&tcon->sb_list_lock);
+ list_add(&cifs_sb->tcon_sb_link, &tcon->cifs_sb_list);
+ spin_unlock(&tcon->sb_list_lock);
+
queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
TLINK_IDLE_EXPIRE);
return 0;
@@ -3668,7 +3926,9 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
ctx->prepath = NULL;
out:
- cifs_try_adding_channels(mnt_ctx.ses);
+ smb3_update_ses_channels(mnt_ctx.ses, mnt_ctx.server,
+ false /* from_reconnect */,
+ false /* disable_mchan */);
rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
if (rc)
goto error;
@@ -3740,11 +4000,11 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
TCONX_RSP *pSMBr;
unsigned char *bcc_ptr;
int rc = 0;
- int length;
+ int length, in_len;
__u16 bytes_left, count;
if (ses == NULL)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
smb_buffer = cifs_buf_get();
if (smb_buffer == NULL)
@@ -3752,8 +4012,8 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
smb_buffer_response = smb_buffer;
- header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
- NULL /*no tid */, 4 /*wct */);
+ in_len = header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
+ NULL /*no tid */, 4 /*wct */);
smb_buffer->Mid = get_next_mid(ses->server);
smb_buffer->Uid = ses->Suid;
@@ -3794,11 +4054,11 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
bcc_ptr += strlen("?????");
bcc_ptr += 1;
count = bcc_ptr - &pSMB->Password[0];
- be32_add_cpu(&pSMB->hdr.smb_buf_length, count);
+ in_len += count;
pSMB->ByteCount = cpu_to_le16(count);
- rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length,
- 0);
+ rc = SendReceive(xid, ses, smb_buffer, in_len, smb_buffer_response,
+ &length, 0);
/* above now done in SendReceive */
if (rc == 0) {
@@ -3882,9 +4142,19 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
struct rb_root *root = &cifs_sb->tlink_tree;
struct rb_node *node;
struct tcon_link *tlink;
+ struct cifs_tcon *tcon = NULL;
cancel_delayed_work_sync(&cifs_sb->prune_tlinks);
+ if (cifs_sb->master_tlink) {
+ tcon = cifs_sb->master_tlink->tl_tcon;
+ if (tcon) {
+ spin_lock(&tcon->sb_list_lock);
+ list_del_init(&cifs_sb->tcon_sb_link);
+ spin_unlock(&tcon->sb_list_lock);
+ }
+ }
+
spin_lock(&cifs_sb->tlink_tree_lock);
while ((node = rb_first(root))) {
tlink = rb_entry(node, struct tcon_link, tl_rbnode);
@@ -3906,11 +4176,13 @@ int
cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server)
{
+ bool in_retry = false;
int rc = 0;
if (!server->ops->need_neg || !server->ops->negotiate)
return -ENOSYS;
+retry:
/* only send once per connect */
spin_lock(&server->srv_lock);
if (server->tcpStatus != CifsGood &&
@@ -3927,9 +4199,18 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
}
server->tcpStatus = CifsInNegotiate;
+ server->neg_start = jiffies;
spin_unlock(&server->srv_lock);
rc = server->ops->negotiate(xid, ses, server);
+ if (rc == -EAGAIN) {
+ /* Allow one retry attempt */
+ if (!in_retry) {
+ in_retry = true;
+ goto retry;
+ }
+ rc = -EHOSTDOWN;
+ }
if (rc == 0) {
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsInNegotiate)
@@ -3952,13 +4233,15 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
struct nls_table *nls_info)
{
- int rc = -ENOSYS;
+ int rc = 0;
struct TCP_Server_Info *pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&pserver->dstaddr;
struct sockaddr_in *addr = (struct sockaddr_in *)&pserver->dstaddr;
bool is_binding = false;
+ bool new_ses;
spin_lock(&ses->ses_lock);
+ new_ses = ses->ses_status == SES_NEW;
cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n",
__func__, ses->chans_need_reconnect);
@@ -4004,6 +4287,26 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
if (!linuxExtEnabled)
ses->capabilities &= (~server->vals->cap_unix);
+ /*
+ * Check if the server supports specified encoding mode.
+ * Zero value in vals->cap_unicode indidcates that chosen
+ * protocol dialect does not support non-UNICODE mode.
+ */
+ if (ses->unicode == 1 && server->vals->cap_unicode != 0 &&
+ !(server->capabilities & server->vals->cap_unicode)) {
+ cifs_dbg(VFS, "Server does not support mounting in UNICODE mode\n");
+ rc = -EOPNOTSUPP;
+ } else if (ses->unicode == 0 && server->vals->cap_unicode == 0) {
+ cifs_dbg(VFS, "Server does not support mounting in non-UNICODE mode\n");
+ rc = -EOPNOTSUPP;
+ } else if (ses->unicode == 0) {
+ /*
+ * When UNICODE mode was explicitly disabled then
+ * do not announce client UNICODE capability.
+ */
+ ses->capabilities &= (~server->vals->cap_unicode);
+ }
+
if (ses->auth_key.response) {
cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
ses->auth_key.response);
@@ -4016,11 +4319,18 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
server->sec_mode, server->capabilities, server->timeAdj);
- if (server->ops->sess_setup)
- rc = server->ops->sess_setup(xid, ses, server, nls_info);
+ if (!rc) {
+ if (server->ops->sess_setup)
+ rc = server->ops->sess_setup(xid, ses, server, nls_info);
+ else
+ rc = -ENOSYS;
+ }
if (rc) {
- cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc);
+ if (new_ses) {
+ cifs_server_dbg(VFS, "failed to create a new SMB session with %s: %d\n",
+ get_security_type_str(ses->sectype), rc);
+ }
spin_lock(&ses->ses_lock);
if (ses->ses_status == SES_IN_SETUP)
ses->ses_status = SES_NEED_RECON;
@@ -4087,6 +4397,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
ctx->seal = master_tcon->seal;
ctx->witness = master_tcon->use_witness;
ctx->dfs_root_ses = master_tcon->ses->dfs_root_ses;
+ ctx->unicode = master_tcon->ses->unicode;
rc = cifs_set_vol_auth(ctx, master_tcon->ses);
if (rc) {
@@ -4146,6 +4457,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
out:
kfree(ctx->username);
+ kfree(ctx->domainname);
kfree_sensitive(ctx->password);
kfree(origin_fullpath);
kfree(ctx);
@@ -4344,10 +4656,10 @@ cifs_prune_tlinks(struct work_struct *work)
}
#ifndef CONFIG_CIFS_DFS_UPCALL
-int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
+int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon)
{
- int rc;
const struct smb_version_operations *ops = tcon->ses->server->ops;
+ int rc;
/* only send once per connect */
spin_lock(&tcon->tc_lock);
@@ -4370,7 +4682,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
tcon->status = TID_IN_TCON;
spin_unlock(&tcon->tc_lock);
- rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, nlsc);
+ rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name,
+ tcon, tcon->ses->local_nls);
if (rc) {
spin_lock(&tcon->tc_lock);
if (tcon->status == TID_IN_TCON)
diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c
index 3f6077c68d68..f65a8a90ba27 100644
--- a/fs/smb/client/dfs.c
+++ b/fs/smb/client/dfs.c
@@ -9,6 +9,8 @@
#include "fs_context.h"
#include "dfs.h"
+#define DFS_DOM(ctx) (ctx->dfs_root_ses ? ctx->dfs_root_ses->dns_dom : NULL)
+
/**
* dfs_parse_target_referral - set fs context for dfs target referral
*
@@ -46,8 +48,8 @@ int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_para
if (rc)
goto out;
- rc = dns_resolve_server_name_to_ip(path, (struct sockaddr *)&ctx->dstaddr, NULL);
-
+ rc = dns_resolve_unc(DFS_DOM(ctx), path,
+ (struct sockaddr *)&ctx->dstaddr);
out:
kfree(path);
return rc;
@@ -59,8 +61,9 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
int rc;
ctx->leaf_fullpath = (char *)full_path;
+ ctx->dns_dom = DFS_DOM(ctx);
rc = cifs_mount_get_session(mnt_ctx);
- ctx->leaf_fullpath = NULL;
+ ctx->leaf_fullpath = ctx->dns_dom = NULL;
return rc;
}
@@ -95,15 +98,16 @@ static inline int parse_dfs_target(struct smb3_fs_context *ctx,
return rc;
}
-static int setup_dfs_ref(struct cifs_mount_ctx *mnt_ctx,
- struct dfs_info3_param *tgt,
- struct dfs_ref_walk *rw)
+static int setup_dfs_ref(struct dfs_info3_param *tgt, struct dfs_ref_walk *rw)
{
- struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
- struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
+ struct cifs_sb_info *cifs_sb = rw->mnt_ctx->cifs_sb;
+ struct smb3_fs_context *ctx = rw->mnt_ctx->fs_ctx;
char *ref_path, *full_path;
int rc;
+ set_root_smb_session(rw->mnt_ctx);
+ ref_walk_ses(rw) = ctx->dfs_root_ses;
+
full_path = smb3_fs_context_fullpath(ctx, CIFS_DIR_SEP(cifs_sb));
if (IS_ERR(full_path))
return PTR_ERR(full_path);
@@ -120,35 +124,22 @@ static int setup_dfs_ref(struct cifs_mount_ctx *mnt_ctx,
}
ref_walk_path(rw) = ref_path;
ref_walk_fpath(rw) = full_path;
- ref_walk_ses(rw) = ctx->dfs_root_ses;
- return 0;
+
+ return dfs_get_referral(rw->mnt_ctx,
+ ref_walk_path(rw) + 1,
+ ref_walk_tl(rw));
}
-static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
- struct dfs_ref_walk *rw)
+static int __dfs_referral_walk(struct dfs_ref_walk *rw)
{
- struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
+ struct smb3_fs_context *ctx = rw->mnt_ctx->fs_ctx;
+ struct cifs_mount_ctx *mnt_ctx = rw->mnt_ctx;
struct dfs_info3_param tgt = {};
int rc = -ENOENT;
again:
do {
ctx->dfs_root_ses = ref_walk_ses(rw);
- if (ref_walk_empty(rw)) {
- rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1,
- NULL, ref_walk_tl(rw));
- if (rc) {
- rc = cifs_mount_get_tcon(mnt_ctx);
- if (!rc)
- rc = cifs_is_path_remote(mnt_ctx);
- continue;
- }
- if (!ref_walk_num_tgts(rw)) {
- rc = -ENOENT;
- continue;
- }
- }
-
while (ref_walk_next_tgt(rw)) {
rc = parse_dfs_target(ctx, rw, &tgt);
if (rc)
@@ -159,32 +150,31 @@ again:
if (rc)
continue;
- ref_walk_set_tgt_hint(rw);
- if (tgt.flags & DFSREF_STORAGE_SERVER) {
- rc = cifs_mount_get_tcon(mnt_ctx);
- if (!rc)
- rc = cifs_is_path_remote(mnt_ctx);
- if (!rc)
+ rc = cifs_mount_get_tcon(mnt_ctx);
+ if (rc) {
+ if (tgt.server_type == DFS_TYPE_LINK &&
+ DFS_INTERLINK(tgt.flags))
+ rc = -EREMOTE;
+ } else {
+ rc = cifs_is_path_remote(mnt_ctx);
+ if (!rc) {
+ ref_walk_set_tgt_hint(rw);
break;
- if (rc != -EREMOTE)
- continue;
+ }
}
-
- set_root_smb_session(mnt_ctx);
- rc = ref_walk_advance(rw);
- if (!rc) {
- rc = setup_dfs_ref(mnt_ctx, &tgt, rw);
+ if (rc == -EREMOTE) {
+ rc = ref_walk_advance(rw);
if (!rc) {
- rc = -EREMOTE;
+ rc = setup_dfs_ref(&tgt, rw);
+ if (rc)
+ break;
+ ref_walk_mark_end(rw);
goto again;
}
}
- if (rc != -ELOOP)
- goto out;
}
} while (rc && ref_walk_descend(rw));
-out:
free_dfs_info_param(&tgt);
return rc;
}
@@ -201,10 +191,10 @@ static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
return rc;
}
- ref_walk_init(*rw);
- rc = setup_dfs_ref(mnt_ctx, NULL, *rw);
+ ref_walk_init(*rw, mnt_ctx);
+ rc = setup_dfs_ref(NULL, *rw);
if (!rc)
- rc = __dfs_referral_walk(mnt_ctx, *rw);
+ rc = __dfs_referral_walk(*rw);
return rc;
}
@@ -264,7 +254,7 @@ static int update_fs_context_dstaddr(struct smb3_fs_context *ctx)
int rc = 0;
if (!ctx->nodfs && ctx->dfs_automount) {
- rc = dns_resolve_server_name_to_ip(ctx->source, addr, NULL);
+ rc = dns_resolve_unc(NULL, ctx->source, addr);
if (!rc)
cifs_set_port(addr, ctx->port);
ctx->dfs_automount = false;
@@ -294,7 +284,7 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
* to respond with PATH_NOT_COVERED to requests that include the prefix.
*/
if (!nodfs) {
- rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL);
+ rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL);
if (rc) {
cifs_dbg(FYI, "%s: no dfs referral for %s: %d\n",
__func__, ctx->UNC + 1, rc);
@@ -314,53 +304,8 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
cifs_mount_put_conns(mnt_ctx);
rc = get_session(mnt_ctx, NULL);
}
- if (!rc) {
- set_root_smb_session(mnt_ctx);
+ if (!rc)
rc = __dfs_mount_share(mnt_ctx);
- }
- return rc;
-}
-
-/* Update dfs referral path of superblock */
-static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb,
- const char *target)
-{
- int rc = 0;
- size_t len = strlen(target);
- char *refpath, *npath;
-
- if (unlikely(len < 2 || *target != '\\'))
- return -EINVAL;
-
- if (target[1] == '\\') {
- len += 1;
- refpath = kmalloc(len, GFP_KERNEL);
- if (!refpath)
- return -ENOMEM;
-
- scnprintf(refpath, len, "%s", target);
- } else {
- len += sizeof("\\");
- refpath = kmalloc(len, GFP_KERNEL);
- if (!refpath)
- return -ENOMEM;
-
- scnprintf(refpath, len, "\\%s", target);
- }
-
- npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb));
- kfree(refpath);
-
- if (IS_ERR(npath)) {
- rc = PTR_ERR(npath);
- } else {
- mutex_lock(&server->refpath_lock);
- spin_lock(&server->srv_lock);
- kfree(server->leaf_fullpath);
- server->leaf_fullpath = npath;
- spin_unlock(&server->srv_lock);
- mutex_unlock(&server->refpath_lock);
- }
return rc;
}
@@ -388,77 +333,22 @@ static int target_share_matches_server(struct TCP_Server_Info *server, char *sha
return rc;
}
-static void __tree_connect_ipc(const unsigned int xid, char *tree,
- struct cifs_sb_info *cifs_sb,
- struct cifs_ses *ses)
-{
- struct TCP_Server_Info *server = ses->server;
- struct cifs_tcon *tcon = ses->tcon_ipc;
- int rc;
-
- spin_lock(&ses->ses_lock);
- spin_lock(&ses->chan_lock);
- if (cifs_chan_needs_reconnect(ses, server) ||
- ses->ses_status != SES_GOOD) {
- spin_unlock(&ses->chan_lock);
- spin_unlock(&ses->ses_lock);
- cifs_server_dbg(FYI, "%s: skipping ipc reconnect due to disconnected ses\n",
- __func__);
- return;
- }
- spin_unlock(&ses->chan_lock);
- spin_unlock(&ses->ses_lock);
-
- cifs_server_lock(server);
- scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
- cifs_server_unlock(server);
-
- rc = server->ops->tree_connect(xid, ses, tree, tcon,
- cifs_sb->local_nls);
- cifs_server_dbg(FYI, "%s: tree_reconnect %s: %d\n", __func__, tree, rc);
- spin_lock(&tcon->tc_lock);
- if (rc) {
- tcon->status = TID_NEED_TCON;
- } else {
- tcon->status = TID_GOOD;
- tcon->need_reconnect = false;
- }
- spin_unlock(&tcon->tc_lock);
-}
-
-static void tree_connect_ipc(const unsigned int xid, char *tree,
- struct cifs_sb_info *cifs_sb,
- struct cifs_tcon *tcon)
-{
- struct cifs_ses *ses = tcon->ses;
-
- __tree_connect_ipc(xid, tree, cifs_sb, ses);
- __tree_connect_ipc(xid, tree, cifs_sb, CIFS_DFS_ROOT_SES(ses));
-}
-
-static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
- struct cifs_sb_info *cifs_sb, char *tree, bool islink,
- struct dfs_cache_tgt_list *tl)
+static int tree_connect_dfs_target(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ char *tree, bool islink,
+ struct dfs_cache_tgt_list *tl)
{
- int rc;
+ const struct smb_version_operations *ops = tcon->ses->server->ops;
struct TCP_Server_Info *server = tcon->ses->server;
- const struct smb_version_operations *ops = server->ops;
- struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses);
- char *share = NULL, *prefix = NULL;
struct dfs_cache_tgt_iterator *tit;
+ char *share = NULL, *prefix = NULL;
bool target_match;
-
- tit = dfs_cache_get_tgt_iterator(tl);
- if (!tit) {
- rc = -ENOENT;
- goto out;
- }
+ int rc = -ENOENT;
/* Try to tree connect to all dfs targets */
- for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
- const char *target = dfs_cache_get_tgt_name(tit);
- DFS_CACHE_TGT_LIST(ntl);
-
+ for (tit = dfs_cache_get_tgt_iterator(tl);
+ tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
kfree(share);
kfree(prefix);
share = prefix = NULL;
@@ -479,74 +369,21 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
}
dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, tit);
- tree_connect_ipc(xid, tree, cifs_sb, tcon);
-
scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
- if (!islink) {
- rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
- break;
- }
-
- /*
- * If no dfs referrals were returned from link target, then just do a TREE_CONNECT
- * to it. Otherwise, cache the dfs referral and then mark current tcp ses for
- * reconnect so either the demultiplex thread or the echo worker will reconnect to
- * newly resolved target.
- */
- if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target,
- NULL, &ntl)) {
- rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
- if (rc)
- continue;
-
+ rc = ops->tree_connect(xid, tcon->ses, tree,
+ tcon, tcon->ses->local_nls);
+ if (islink && !rc && cifs_sb)
rc = cifs_update_super_prepath(cifs_sb, prefix);
- } else {
- /* Target is another dfs share */
- rc = update_server_fullpath(server, cifs_sb, target);
- dfs_cache_free_tgts(tl);
-
- if (!rc) {
- rc = -EREMOTE;
- list_replace_init(&ntl.tl_list, &tl->tl_list);
- } else
- dfs_cache_free_tgts(&ntl);
- }
break;
}
-out:
kfree(share);
kfree(prefix);
-
- return rc;
-}
-
-static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
- struct cifs_sb_info *cifs_sb, char *tree, bool islink,
- struct dfs_cache_tgt_list *tl)
-{
- int rc;
- int num_links = 0;
- struct TCP_Server_Info *server = tcon->ses->server;
- char *old_fullpath = server->leaf_fullpath;
-
- do {
- rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl);
- if (!rc || rc != -EREMOTE)
- break;
- } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
- /*
- * If we couldn't tree connect to any targets from last referral path, then
- * retry it from newly resolved dfs referral.
- */
- if (rc && server->leaf_fullpath != old_fullpath)
- cifs_signal_cifsd_for_reconnect(server, true);
-
dfs_cache_free_tgts(tl);
return rc;
}
-int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
+int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon)
{
int rc;
struct TCP_Server_Info *server = tcon->ses->server;
@@ -588,7 +425,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
cifs_server_lock(server);
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
cifs_server_unlock(server);
- rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
+ rc = ops->tree_connect(xid, tcon->ses, tree,
+ tcon, tcon->ses->local_nls);
goto out;
}
@@ -596,14 +434,11 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
if (!IS_ERR(sb))
cifs_sb = CIFS_SB(sb);
- /*
- * Tree connect to last share in @tcon->tree_name whether dfs super or
- * cached dfs referral was not found.
- */
- if (!cifs_sb || !server->leaf_fullpath ||
+ /* Tree connect to last share in @tcon->tree_name if no DFS referral */
+ if (!server->leaf_fullpath ||
dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) {
- rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon,
- cifs_sb ? cifs_sb->local_nls : nlsc);
+ rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name,
+ tcon, tcon->ses->local_nls);
goto out;
}
diff --git a/fs/smb/client/dfs.h b/fs/smb/client/dfs.h
index 1aa2bc65b3bc..e60f0a24a8a1 100644
--- a/fs/smb/client/dfs.h
+++ b/fs/smb/client/dfs.h
@@ -12,6 +12,7 @@
#include "dfs_cache.h"
#include "cifs_unicode.h"
#include <linux/namei.h>
+#include <linux/errno.h>
#define DFS_INTERLINK(v) \
(((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER))
@@ -25,8 +26,9 @@ struct dfs_ref {
};
struct dfs_ref_walk {
- struct dfs_ref *ref;
- struct dfs_ref refs[MAX_NESTED_LINKS];
+ struct cifs_mount_ctx *mnt_ctx;
+ struct dfs_ref *ref;
+ struct dfs_ref refs[MAX_NESTED_LINKS];
};
#define ref_walk_start(w) ((w)->refs)
@@ -35,7 +37,6 @@ struct dfs_ref_walk {
#define ref_walk_descend(w) (--ref_walk_cur(w) >= ref_walk_start(w))
#define ref_walk_tit(w) (ref_walk_cur(w)->tit)
-#define ref_walk_empty(w) (!ref_walk_tit(w))
#define ref_walk_path(w) (ref_walk_cur(w)->path)
#define ref_walk_fpath(w) (ref_walk_cur(w)->full_path)
#define ref_walk_tl(w) (&ref_walk_cur(w)->tl)
@@ -51,9 +52,11 @@ static inline struct dfs_ref_walk *ref_walk_alloc(void)
return rw;
}
-static inline void ref_walk_init(struct dfs_ref_walk *rw)
+static inline void ref_walk_init(struct dfs_ref_walk *rw,
+ struct cifs_mount_ctx *mnt_ctx)
{
memset(rw, 0, sizeof(*rw));
+ rw->mnt_ctx = mnt_ctx;
ref_walk_cur(rw) = ref_walk_start(rw);
}
@@ -93,15 +96,23 @@ static inline int ref_walk_advance(struct dfs_ref_walk *rw)
static inline struct dfs_cache_tgt_iterator *
ref_walk_next_tgt(struct dfs_ref_walk *rw)
{
- struct dfs_cache_tgt_iterator *tit;
struct dfs_ref *ref = ref_walk_cur(rw);
+ struct dfs_cache_tgt_iterator *tit;
+
+ if (IS_ERR(ref->tit))
+ return NULL;
if (!ref->tit)
tit = dfs_cache_get_tgt_iterator(&ref->tl);
else
tit = dfs_cache_get_next_tgt(&ref->tl, ref->tit);
+
+ if (!tit) {
+ ref->tit = ERR_PTR(-ENOENT);
+ return NULL;
+ }
ref->tit = tit;
- return tit;
+ return ref->tit;
}
static inline int ref_walk_get_tgt(struct dfs_ref_walk *rw,
@@ -112,11 +123,6 @@ static inline int ref_walk_get_tgt(struct dfs_ref_walk *rw,
ref_walk_tit(rw), tgt);
}
-static inline int ref_walk_num_tgts(struct dfs_ref_walk *rw)
-{
- return dfs_cache_get_nr_tgts(ref_walk_tl(rw));
-}
-
static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw)
{
dfs_cache_noreq_update_tgthint(ref_walk_path(rw) + 1,
@@ -136,6 +142,15 @@ static inline void ref_walk_set_tcon(struct dfs_ref_walk *rw,
}
}
+static inline void ref_walk_mark_end(struct dfs_ref_walk *rw)
+{
+ struct dfs_ref *ref = ref_walk_cur(rw) - 1;
+
+ WARN_ON_ONCE(ref < ref_walk_start(rw));
+ dfs_cache_noreq_update_tgthint(ref->path + 1, ref->tit);
+ ref->tit = ERR_PTR(-ENOENT); /* end marker */
+}
+
int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref,
struct smb3_fs_context *ctx);
int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx);
@@ -145,15 +160,16 @@ static inline char *dfs_get_path(struct cifs_sb_info *cifs_sb, const char *path)
return dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb));
}
-static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *path,
- struct dfs_info3_param *ref, struct dfs_cache_tgt_list *tl)
+static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx,
+ const char *path,
+ struct dfs_cache_tgt_list *tl)
{
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
struct cifs_ses *rses = ctx->dfs_root_ses ?: mnt_ctx->ses;
return dfs_cache_find(mnt_ctx->xid, rses, cifs_sb->local_nls,
- cifs_remap(cifs_sb), path, ref, tl);
+ cifs_remap(cifs_sb), path, NULL, tl);
}
/*
@@ -172,4 +188,11 @@ static inline void dfs_put_root_smb_sessions(struct list_head *head)
}
}
+static inline const char *dfs_ses_refpath(struct cifs_ses *ses)
+{
+ const char *path = ses->server->leaf_fullpath;
+
+ return path ? path + 1 : ERR_PTR(-ENOENT);
+}
+
#endif /* _CIFS_DFS_H */
diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c
index 00820f57b434..f2ad0ccd08a7 100644
--- a/fs/smb/client/dfs_cache.c
+++ b/fs/smb/client/dfs_cache.c
@@ -24,8 +24,8 @@
#include "dfs_cache.h"
-#define CACHE_HTABLE_SIZE 32
-#define CACHE_MAX_ENTRIES 64
+#define CACHE_HTABLE_SIZE 512
+#define CACHE_MAX_ENTRIES 1024
#define CACHE_MIN_TTL 120 /* 2 minutes */
#define CACHE_DEFAULT_TTL 300 /* 5 minutes */
@@ -1096,11 +1096,8 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
static bool target_share_equal(struct cifs_tcon *tcon, const char *s1)
{
struct TCP_Server_Info *server = tcon->ses->server;
- struct sockaddr_storage ss;
- const char *host;
const char *s2 = &tcon->tree_name[1];
- size_t hostlen;
- char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0};
+ struct sockaddr_storage ss;
bool match;
int rc;
@@ -1111,73 +1108,91 @@ static bool target_share_equal(struct cifs_tcon *tcon, const char *s1)
* Resolve share's hostname and check if server address matches. Otherwise just ignore it
* as we could not have upcall to resolve hostname or failed to convert ip address.
*/
- extract_unc_hostname(s1, &host, &hostlen);
- scnprintf(unc, sizeof(unc), "\\\\%.*s", (int)hostlen, host);
-
- rc = dns_resolve_server_name_to_ip(unc, (struct sockaddr *)&ss, NULL);
- if (rc < 0) {
- cifs_dbg(FYI, "%s: could not resolve %.*s. assuming server address matches.\n",
- __func__, (int)hostlen, host);
+ rc = dns_resolve_unc(server->dns_dom, s1, (struct sockaddr *)&ss);
+ if (rc < 0)
return true;
- }
cifs_server_lock(server);
match = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, (struct sockaddr *)&ss);
+ cifs_dbg(FYI, "%s: [share=%s] ipaddr matched: %s\n", __func__, s1, str_yes_no(match));
cifs_server_unlock(server);
return match;
}
-static bool is_ses_good(struct cifs_ses *ses)
+static bool is_ses_good(struct cifs_tcon *tcon, struct cifs_ses *ses)
{
struct TCP_Server_Info *server = ses->server;
- struct cifs_tcon *tcon = ses->tcon_ipc;
+ struct cifs_tcon *ipc = NULL;
bool ret;
+ spin_lock(&cifs_tcp_ses_lock);
spin_lock(&ses->ses_lock);
spin_lock(&ses->chan_lock);
+
ret = !cifs_chan_needs_reconnect(ses, server) &&
- ses->ses_status == SES_GOOD &&
- !tcon->need_reconnect;
+ ses->ses_status == SES_GOOD;
+
spin_unlock(&ses->chan_lock);
- spin_unlock(&ses->ses_lock);
- return ret;
-}
-static char *get_ses_refpath(struct cifs_ses *ses)
-{
- struct TCP_Server_Info *server = ses->server;
- char *path = ERR_PTR(-ENOENT);
+ if (!ret)
+ goto out;
+
+ if (likely(ses->tcon_ipc)) {
+ if (ses->tcon_ipc->need_reconnect) {
+ ret = false;
+ goto out;
+ }
+ } else {
+ spin_unlock(&ses->ses_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
- mutex_lock(&server->refpath_lock);
- if (server->leaf_fullpath) {
- path = kstrdup(server->leaf_fullpath + 1, GFP_ATOMIC);
- if (!path)
- path = ERR_PTR(-ENOMEM);
+ ipc = cifs_setup_ipc(ses, tcon->seal);
+
+ spin_lock(&cifs_tcp_ses_lock);
+ spin_lock(&ses->ses_lock);
+ if (!IS_ERR(ipc)) {
+ if (!ses->tcon_ipc) {
+ ses->tcon_ipc = ipc;
+ ipc = NULL;
+ }
+ } else {
+ ret = false;
+ ipc = NULL;
+ }
+ }
+
+out:
+ spin_unlock(&ses->ses_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ if (ipc && server->ops->tree_disconnect) {
+ unsigned int xid = get_xid();
+
+ (void)server->ops->tree_disconnect(xid, ipc);
+ _free_xid(xid);
}
- mutex_unlock(&server->refpath_lock);
- return path;
+ tconInfoFree(ipc, netfs_trace_tcon_ref_free_ipc);
+ return ret;
}
/* Refresh dfs referral of @ses */
-static void refresh_ses_referral(struct cifs_ses *ses)
+static void refresh_ses_referral(struct cifs_tcon *tcon, struct cifs_ses *ses)
{
struct cache_entry *ce;
unsigned int xid;
- char *path;
+ const char *path;
int rc = 0;
xid = get_xid();
- path = get_ses_refpath(ses);
+ path = dfs_ses_refpath(ses);
if (IS_ERR(path)) {
rc = PTR_ERR(path);
- path = NULL;
goto out;
}
ses = CIFS_DFS_ROOT_SES(ses);
- if (!is_ses_good(ses)) {
+ if (!is_ses_good(tcon, ses)) {
cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",
__func__);
goto out;
@@ -1191,7 +1206,6 @@ static void refresh_ses_referral(struct cifs_ses *ses)
out:
free_xid(xid);
- kfree(path);
}
static int __refresh_tcon_referral(struct cifs_tcon *tcon,
@@ -1241,19 +1255,18 @@ static void refresh_tcon_referral(struct cifs_tcon *tcon, bool force_refresh)
struct dfs_info3_param *refs = NULL;
struct cache_entry *ce;
struct cifs_ses *ses;
- unsigned int xid;
bool needs_refresh;
- char *path;
+ const char *path;
+ unsigned int xid;
int numrefs = 0;
int rc = 0;
xid = get_xid();
ses = tcon->ses;
- path = get_ses_refpath(ses);
+ path = dfs_ses_refpath(ses);
if (IS_ERR(path)) {
rc = PTR_ERR(path);
- path = NULL;
goto out;
}
@@ -1267,7 +1280,7 @@ static void refresh_tcon_referral(struct cifs_tcon *tcon, bool force_refresh)
up_read(&htable_rw_lock);
ses = CIFS_DFS_ROOT_SES(ses);
- if (!is_ses_good(ses)) {
+ if (!is_ses_good(tcon, ses)) {
cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",
__func__);
goto out;
@@ -1281,7 +1294,6 @@ static void refresh_tcon_referral(struct cifs_tcon *tcon, bool force_refresh)
out:
free_xid(xid);
- kfree(path);
free_dfs_info_array(refs, numrefs);
}
@@ -1336,7 +1348,7 @@ void dfs_cache_refresh(struct work_struct *work)
tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work);
list_for_each_entry(ses, &tcon->dfs_ses_list, dlist)
- refresh_ses_referral(ses);
+ refresh_ses_referral(tcon, ses);
refresh_tcon_referral(tcon, false);
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c
index 864b194dbaa0..747256025e49 100644
--- a/fs/smb/client/dir.c
+++ b/fs/smb/client/dir.c
@@ -23,6 +23,7 @@
#include "fs_context.h"
#include "cifs_ioctl.h"
#include "fscache.h"
+#include "cached_dir.h"
static void
renew_parental_timestamps(struct dentry *direntry)
@@ -189,7 +190,9 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
int disposition;
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms;
+ struct cached_fid *parent_cfid = NULL;
int rdwr_for_fscache = 0;
+ __le32 lease_flags = 0;
*oplock = 0;
if (tcon->ses->server->oplocks)
@@ -197,8 +200,8 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
full_path = build_path_from_dentry(direntry, page);
if (IS_ERR(full_path)) {
- free_dentry_path(page);
- return PTR_ERR(full_path);
+ rc = PTR_ERR(full_path);
+ goto out;
}
/* If we're caching, we need to be able to fill in around partial writes. */
@@ -311,7 +314,29 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
create_options |= CREATE_OPTION_READONLY;
+
retry_open:
+ if (tcon->cfids && direntry->d_parent && server->dialect >= SMB30_PROT_ID) {
+ parent_cfid = NULL;
+ spin_lock(&tcon->cfids->cfid_list_lock);
+ list_for_each_entry(parent_cfid, &tcon->cfids->entries, entry) {
+ if (parent_cfid->dentry == direntry->d_parent) {
+ cifs_dbg(FYI, "found a parent cached file handle\n");
+ if (is_valid_cached_dir(parent_cfid)) {
+ lease_flags
+ |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE;
+ memcpy(fid->parent_lease_key,
+ parent_cfid->fid.lease_key,
+ SMB2_LEASE_KEY_SIZE);
+ parent_cfid->dirents.is_valid = false;
+ parent_cfid->dirents.is_failed = true;
+ }
+ break;
+ }
+ }
+ spin_unlock(&tcon->cfids->cfid_list_lock);
+ }
+
oparms = (struct cifs_open_parms) {
.tcon = tcon,
.cifs_sb = cifs_sb,
@@ -320,6 +345,7 @@ retry_open:
.disposition = disposition,
.path = full_path,
.fid = fid,
+ .lease_flags = lease_flags,
.mode = mode,
};
rc = server->ops->open(xid, &oparms, oplock, buf);
@@ -431,7 +457,7 @@ out_err:
int
cifs_atomic_open(struct inode *inode, struct dentry *direntry,
- struct file *file, unsigned oflags, umode_t mode)
+ struct file *file, unsigned int oflags, umode_t mode)
{
int rc;
unsigned int xid;
@@ -445,7 +471,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
struct cifs_open_info_data buf = {};
if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
- return -EIO;
+ return smb_EIO(smb_eio_trace_forced_shutdown);
/*
* Posix open is only called (at lookup time) for file create now. For
@@ -459,8 +485,6 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
* in network traffic in the other paths.
*/
if (!(oflags & O_CREAT)) {
- struct dentry *res;
-
/*
* Check for hashed negative dentry. We have already revalidated
* the dentry and it is fine. No need to perform another lookup.
@@ -468,11 +492,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
if (!d_in_lookup(direntry))
return -ENOENT;
- res = cifs_lookup(inode, direntry, 0);
- if (IS_ERR(res))
- return PTR_ERR(res);
-
- return finish_no_open(file, res);
+ return finish_no_open(file, cifs_lookup(inode, direntry, 0));
}
xid = get_xid();
@@ -569,7 +589,7 @@ int cifs_create(struct mnt_idmap *idmap, struct inode *inode,
inode, direntry, direntry);
if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) {
- rc = -EIO;
+ rc = smb_EIO(smb_eio_trace_forced_shutdown);
goto out_free_xid;
}
@@ -611,7 +631,7 @@ int cifs_mknod(struct mnt_idmap *idmap, struct inode *inode,
cifs_sb = CIFS_SB(inode->i_sb);
if (unlikely(cifs_forced_shutdown(cifs_sb)))
- return -EIO;
+ return smb_EIO(smb_eio_trace_forced_shutdown);
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
@@ -627,7 +647,7 @@ int cifs_mknod(struct mnt_idmap *idmap, struct inode *inode,
goto mknod_out;
}
- trace_smb3_mknod_enter(xid, tcon->ses->Suid, tcon->tid, full_path);
+ trace_smb3_mknod_enter(xid, tcon->tid, tcon->ses->Suid, full_path);
rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon,
full_path, mode,
@@ -635,9 +655,9 @@ int cifs_mknod(struct mnt_idmap *idmap, struct inode *inode,
mknod_out:
if (rc)
- trace_smb3_mknod_err(xid, tcon->ses->Suid, tcon->tid, rc);
+ trace_smb3_mknod_err(xid, tcon->tid, tcon->ses->Suid, rc);
else
- trace_smb3_mknod_done(xid, tcon->ses->Suid, tcon->tid);
+ trace_smb3_mknod_done(xid, tcon->tid, tcon->ses->Suid);
free_dentry_path(page);
free_xid(xid);
@@ -658,6 +678,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
const char *full_path;
void *page;
int retry_count = 0;
+ struct dentry *de;
xid = get_xid();
@@ -669,16 +690,15 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
- free_xid(xid);
- return ERR_CAST(tlink);
+ de = ERR_CAST(tlink);
+ goto free_xid;
}
pTcon = tlink_tcon(tlink);
rc = check_name(direntry, pTcon);
if (unlikely(rc)) {
- cifs_put_tlink(tlink);
- free_xid(xid);
- return ERR_PTR(rc);
+ de = ERR_PTR(rc);
+ goto put_tlink;
}
/* can not grab the rename sem here since it would
@@ -687,16 +707,38 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
page = alloc_dentry_path();
full_path = build_path_from_dentry(direntry, page);
if (IS_ERR(full_path)) {
- cifs_put_tlink(tlink);
- free_xid(xid);
- free_dentry_path(page);
- return ERR_CAST(full_path);
+ de = ERR_CAST(full_path);
+ goto free_dentry_path;
}
if (d_really_is_positive(direntry)) {
cifs_dbg(FYI, "non-NULL inode in lookup\n");
} else {
+ struct cached_fid *cfid = NULL;
+
cifs_dbg(FYI, "NULL inode in lookup\n");
+
+ /*
+ * We can only rely on negative dentries having the same
+ * spelling as the cached dirent if case insensitivity is
+ * forced on mount.
+ *
+ * XXX: if servers correctly announce Case Sensitivity Search
+ * on GetInfo of FileFSAttributeInformation, then we can take
+ * correct action even if case insensitive is not forced on
+ * mount.
+ */
+ if (pTcon->nocase && !open_cached_dir_by_dentry(pTcon, direntry->d_parent, &cfid)) {
+ /*
+ * dentry is negative and parent is fully cached:
+ * we can assume file does not exist
+ */
+ if (cfid->dirents.is_valid) {
+ close_cached_dir(cfid);
+ goto out;
+ }
+ close_cached_dir(cfid);
+ }
}
cifs_dbg(FYI, "Full path: %s inode = 0x%p\n",
full_path, d_inode(direntry));
@@ -730,23 +772,29 @@ again:
}
newInode = ERR_PTR(rc);
}
+
+out:
+ de = d_splice_alias(newInode, direntry);
+free_dentry_path:
free_dentry_path(page);
+put_tlink:
cifs_put_tlink(tlink);
+free_xid:
free_xid(xid);
- return d_splice_alias(newInode, direntry);
+ return de;
}
static int
-cifs_d_revalidate(struct dentry *direntry, unsigned int flags)
+cifs_d_revalidate(struct inode *dir, const struct qstr *name,
+ struct dentry *direntry, unsigned int flags)
{
- struct inode *inode;
- int rc;
-
if (flags & LOOKUP_RCU)
return -ECHILD;
if (d_really_is_positive(direntry)) {
- inode = d_inode(direntry);
+ int rc;
+ struct inode *inode = d_inode(direntry);
+
if ((flags & LOOKUP_REVAL) && !CIFS_CACHE_READ(CIFS_I(inode)))
CIFS_I(inode)->time = 0; /* force reval */
@@ -786,6 +834,22 @@ cifs_d_revalidate(struct dentry *direntry, unsigned int flags)
return 1;
}
+ } else {
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dir->i_sb);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ struct cached_fid *cfid;
+
+ if (!open_cached_dir_by_dentry(tcon, direntry->d_parent, &cfid)) {
+ /*
+ * dentry is negative and parent is fully cached:
+ * we can assume file does not exist
+ */
+ if (cfid->dirents.is_valid) {
+ close_cached_dir(cfid);
+ return 1;
+ }
+ close_cached_dir(cfid);
+ }
}
/*
diff --git a/fs/smb/client/dns_resolve.c b/fs/smb/client/dns_resolve.c
index 8bf8978bc5d6..de7f4b384718 100644
--- a/fs/smb/client/dns_resolve.c
+++ b/fs/smb/client/dns_resolve.c
@@ -20,69 +20,77 @@
#include "cifsproto.h"
#include "cifs_debug.h"
-/**
- * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
- * @unc: UNC path specifying the server (with '/' as delimiter)
- * @ip_addr: Where to return the IP address.
- * @expiry: Where to return the expiry time for the dns record.
- *
- * Returns zero success, -ve on error.
- */
-int
-dns_resolve_server_name_to_ip(const char *unc, struct sockaddr *ip_addr, time64_t *expiry)
+static int resolve_name(const char *name, size_t namelen, struct sockaddr *addr)
{
- const char *hostname, *sep;
char *ip;
- int len, rc;
+ int rc;
- if (!ip_addr || !unc)
- return -EINVAL;
+ rc = dns_query(current->nsproxy->net_ns, NULL, name,
+ namelen, NULL, &ip, NULL, false);
+ if (rc < 0) {
+ cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n",
+ __func__, (int)namelen, (int)namelen, name);
+ } else {
+ cifs_dbg(FYI, "%s: resolved: %*.*s to %s\n",
+ __func__, (int)namelen, (int)namelen, name, ip);
- len = strlen(unc);
- if (len < 3) {
- cifs_dbg(FYI, "%s: unc is too short: %s\n", __func__, unc);
- return -EINVAL;
+ rc = cifs_convert_address(addr, ip, strlen(ip));
+ kfree(ip);
+ if (!rc) {
+ cifs_dbg(FYI, "%s: unable to determine ip address\n",
+ __func__);
+ rc = -EHOSTUNREACH;
+ } else {
+ rc = 0;
+ }
}
+ return rc;
+}
- /* Discount leading slashes for cifs */
- len -= 2;
- hostname = unc + 2;
+/**
+ * dns_resolve_name - Perform an upcall to resolve hostname to an ip address.
+ * @dom: DNS domain name (or NULL)
+ * @name: Name to look up
+ * @namelen: Length of name
+ * @ip_addr: Where to return the IP address
+ *
+ * Returns zero on success, -ve code otherwise.
+ */
+int dns_resolve_name(const char *dom, const char *name,
+ size_t namelen, struct sockaddr *ip_addr)
+{
+ size_t len;
+ char *s;
+ int rc;
- /* Search for server name delimiter */
- sep = memchr(hostname, '/', len);
- if (sep)
- len = sep - hostname;
- else
- cifs_dbg(FYI, "%s: probably server name is whole unc: %s\n",
- __func__, unc);
+ cifs_dbg(FYI, "%s: dom=%s name=%.*s\n", __func__, dom, (int)namelen, name);
+ if (!ip_addr || !name || !*name || !namelen)
+ return -EINVAL;
+ cifs_dbg(FYI, "%s: hostname=%.*s\n", __func__, (int)namelen, name);
/* Try to interpret hostname as an IPv4 or IPv6 address */
- rc = cifs_convert_address(ip_addr, hostname, len);
+ rc = cifs_convert_address(ip_addr, name, namelen);
if (rc > 0) {
- cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %*.*s\n", __func__, len, len,
- hostname);
+ cifs_dbg(FYI, "%s: unc is IP, skipping dns upcall: %*.*s\n",
+ __func__, (int)namelen, (int)namelen, name);
return 0;
}
- /* Perform the upcall */
- rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len,
- NULL, &ip, expiry, false);
- if (rc < 0) {
- cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n",
- __func__, len, len, hostname);
- } else {
- cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n",
- __func__, len, len, hostname, ip,
- expiry ? (*expiry) : 0);
-
- rc = cifs_convert_address(ip_addr, ip, strlen(ip));
- kfree(ip);
+ /*
+ * If @name contains a NetBIOS name and @dom has been specified, then
+ * convert @name to an FQDN and try resolving it first.
+ */
+ if (dom && *dom && cifs_netbios_name(name, namelen)) {
+ len = strnlen(dom, CIFS_MAX_DOMAINNAME_LEN) + namelen + 2;
+ s = kmalloc(len, GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
- if (!rc) {
- cifs_dbg(FYI, "%s: unable to determine ip address\n", __func__);
- rc = -EHOSTUNREACH;
- } else
- rc = 0;
+ scnprintf(s, len, "%.*s.%s", (int)namelen, name, dom);
+ rc = resolve_name(s, len - 1, ip_addr);
+ kfree(s);
+ if (!rc)
+ return 0;
}
- return rc;
+ return resolve_name(name, namelen, ip_addr);
}
diff --git a/fs/smb/client/dns_resolve.h b/fs/smb/client/dns_resolve.h
index 6eb0c15a2440..36bc4a6a55bf 100644
--- a/fs/smb/client/dns_resolve.h
+++ b/fs/smb/client/dns_resolve.h
@@ -12,9 +12,26 @@
#define _DNS_RESOLVE_H
#include <linux/net.h>
+#include "cifsglob.h"
+#include "cifsproto.h"
-#ifdef __KERNEL__
-int dns_resolve_server_name_to_ip(const char *unc, struct sockaddr *ip_addr, time64_t *expiry);
-#endif /* KERNEL */
+int dns_resolve_name(const char *dom, const char *name,
+ size_t namelen, struct sockaddr *ip_addr);
+
+static inline int dns_resolve_unc(const char *dom, const char *unc,
+ struct sockaddr *ip_addr)
+{
+ const char *name;
+ size_t namelen;
+
+ if (!unc || strlen(unc) < 3)
+ return -EINVAL;
+
+ extract_unc_hostname(unc, &name, &namelen);
+ if (!namelen)
+ return -EINVAL;
+
+ return dns_resolve_name(dom, name, namelen, ip_addr);
+}
#endif /* _DNS_RESOLVE_H */
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index a58a3333ecc3..7ff5cc9c5c5b 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -9,6 +9,7 @@
*
*/
#include <linux/fs.h>
+#include <linux/fs_struct.h>
#include <linux/filelock.h>
#include <linux/backing-dev.h>
#include <linux/stat.h>
@@ -52,6 +53,7 @@ static void cifs_prepare_write(struct netfs_io_subrequest *subreq)
struct netfs_io_stream *stream = &req->rreq.io_streams[subreq->stream_nr];
struct TCP_Server_Info *server;
struct cifsFileInfo *open_file = req->cfile;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(wdata->rreq->inode->i_sb);
size_t wsize = req->rreq.wsize;
int rc;
@@ -63,6 +65,10 @@ static void cifs_prepare_write(struct netfs_io_subrequest *subreq)
server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses);
wdata->server = server;
+ if (cifs_sb->ctx->wsize == 0)
+ cifs_negotiate_wsize(server, cifs_sb->ctx,
+ tlink_tcon(req->cfile->tlink));
+
retry:
if (open_file->invalidHandle) {
rc = cifs_reopen_file(open_file, false);
@@ -92,8 +98,12 @@ retry:
cifs_trace_rw_credits_write_prepare);
#ifdef CONFIG_CIFS_SMB_DIRECT
- if (server->smbd_conn)
- stream->sreq_max_segs = server->smbd_conn->max_frmr_depth;
+ if (server->smbd_conn) {
+ const struct smbdirect_socket_parameters *sp =
+ smbd_get_parameters(server->smbd_conn);
+
+ stream->sreq_max_segs = sp->max_frmr_depth;
+ }
#endif
}
@@ -108,7 +118,7 @@ static void cifs_issue_write(struct netfs_io_subrequest *subreq)
int rc;
if (cifs_forced_shutdown(sbi)) {
- rc = -EIO;
+ rc = smb_EIO(smb_eio_trace_forced_shutdown);
goto fail;
}
@@ -130,7 +140,7 @@ fail:
else
trace_netfs_sreq(subreq, netfs_sreq_trace_fail);
add_credits_and_wake_if(wdata->server, &wdata->credits, 0);
- cifs_write_subrequest_terminated(wdata, rc, false);
+ cifs_write_subrequest_terminated(wdata, rc);
goto out;
}
@@ -147,7 +157,7 @@ static int cifs_prepare_read(struct netfs_io_subrequest *subreq)
struct netfs_io_request *rreq = subreq->rreq;
struct cifs_io_subrequest *rdata = container_of(subreq, struct cifs_io_subrequest, subreq);
struct cifs_io_request *req = container_of(subreq->rreq, struct cifs_io_request, rreq);
- struct TCP_Server_Info *server = req->server;
+ struct TCP_Server_Info *server;
struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb);
size_t size;
int rc = 0;
@@ -156,12 +166,13 @@ static int cifs_prepare_read(struct netfs_io_subrequest *subreq)
rdata->xid = get_xid();
rdata->have_xid = true;
}
+
+ server = cifs_pick_channel(tlink_tcon(req->cfile->tlink)->ses);
rdata->server = server;
if (cifs_sb->ctx->rsize == 0)
- cifs_sb->ctx->rsize =
- server->ops->negotiate_rsize(tlink_tcon(req->cfile->tlink),
- cifs_sb->ctx);
+ cifs_negotiate_rsize(server, cifs_sb->ctx,
+ tlink_tcon(req->cfile->tlink));
rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize,
&size, &rdata->credits);
@@ -181,8 +192,12 @@ static int cifs_prepare_read(struct netfs_io_subrequest *subreq)
cifs_trace_rw_credits_read_submit);
#ifdef CONFIG_CIFS_SMB_DIRECT
- if (server->smbd_conn)
- rreq->io_streams[0].sreq_max_segs = server->smbd_conn->max_frmr_depth;
+ if (server->smbd_conn) {
+ const struct smbdirect_socket_parameters *sp =
+ smbd_get_parameters(server->smbd_conn);
+
+ rreq->io_streams[0].sreq_max_segs = sp->max_frmr_depth;
+ }
#endif
return 0;
}
@@ -198,7 +213,7 @@ static void cifs_issue_read(struct netfs_io_subrequest *subreq)
struct netfs_io_request *rreq = subreq->rreq;
struct cifs_io_subrequest *rdata = container_of(subreq, struct cifs_io_subrequest, subreq);
struct cifs_io_request *req = container_of(subreq->rreq, struct cifs_io_request, rreq);
- struct TCP_Server_Info *server = req->server;
+ struct TCP_Server_Info *server = rdata->server;
int rc = 0;
cifs_dbg(FYI, "%s: op=%08x[%x] mapping=%p len=%zu/%zu\n",
@@ -217,7 +232,8 @@ static void cifs_issue_read(struct netfs_io_subrequest *subreq)
goto failed;
}
- if (subreq->rreq->origin != NETFS_DIO_READ)
+ if (subreq->rreq->origin != NETFS_UNBUFFERED_READ &&
+ subreq->rreq->origin != NETFS_DIO_READ)
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
@@ -227,7 +243,8 @@ static void cifs_issue_read(struct netfs_io_subrequest *subreq)
return;
failed:
- netfs_read_subreq_terminated(subreq, rc, false);
+ subreq->error = rc;
+ netfs_read_subreq_terminated(subreq);
}
/*
@@ -265,12 +282,11 @@ static int cifs_init_request(struct netfs_io_request *rreq, struct file *file)
open_file = file->private_data;
rreq->netfs_priv = file->private_data;
req->cfile = cifsFileInfo_get(open_file);
- req->server = cifs_pick_channel(tlink_tcon(req->cfile->tlink)->ses);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
req->pid = req->cfile->pid;
} else if (rreq->origin != NETFS_WRITEBACK) {
WARN_ON_ONCE(1);
- return -EIO;
+ return smb_EIO1(smb_eio_trace_not_netfs_writeback, rreq->origin);
}
return 0;
@@ -386,7 +402,7 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
spin_unlock(&tcon->tc_lock);
/*
- * BB Add call to invalidate_inodes(sb) for all superblocks mounted
+ * BB Add call to evict_inodes(sb) for all superblocks mounted
* to this tcon.
*/
}
@@ -937,6 +953,66 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
}
}
+int cifs_file_flush(const unsigned int xid, struct inode *inode,
+ struct cifsFileInfo *cfile)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifs_tcon *tcon;
+ int rc;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)
+ return 0;
+
+ if (cfile && (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE)) {
+ tcon = tlink_tcon(cfile->tlink);
+ return tcon->ses->server->ops->flush(xid, tcon,
+ &cfile->fid);
+ }
+ rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile);
+ if (!rc) {
+ tcon = tlink_tcon(cfile->tlink);
+ rc = tcon->ses->server->ops->flush(xid, tcon, &cfile->fid);
+ cifsFileInfo_put(cfile);
+ } else if (rc == -EBADF) {
+ rc = 0;
+ }
+ return rc;
+}
+
+static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
+{
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(dentry));
+ struct inode *inode = d_inode(dentry);
+ struct cifsFileInfo *cfile = NULL;
+ struct TCP_Server_Info *server;
+ struct cifs_tcon *tcon;
+ int rc;
+
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (is_interrupt_error(rc))
+ return -ERESTARTSYS;
+ mapping_set_error(inode->i_mapping, rc);
+
+ cfile = find_writable_file(cinode, FIND_WR_FSUID_ONLY);
+ rc = cifs_file_flush(xid, inode, cfile);
+ if (!rc) {
+ if (cfile) {
+ tcon = tlink_tcon(cfile->tlink);
+ server = tcon->ses->server;
+ rc = server->ops->set_file_size(xid, tcon,
+ cfile, 0, false);
+ }
+ if (!rc) {
+ netfs_resize_file(&cinode->netfs, 0, true);
+ cifs_setsize(inode, 0);
+ inode->i_blocks = 0;
+ }
+ }
+ if (cfile)
+ cifsFileInfo_put(cfile);
+ return rc;
+}
+
int cifs_open(struct inode *inode, struct file *file)
{
@@ -960,7 +1036,7 @@ int cifs_open(struct inode *inode, struct file *file)
cifs_sb = CIFS_SB(inode->i_sb);
if (unlikely(cifs_forced_shutdown(cifs_sb))) {
free_xid(xid);
- return -EIO;
+ return smb_EIO(smb_eio_trace_forced_shutdown);
}
tlink = cifs_sb_tlink(cifs_sb);
@@ -989,18 +1065,39 @@ int cifs_open(struct inode *inode, struct file *file)
file->f_op = &cifs_file_direct_ops;
}
+ if (file->f_flags & O_TRUNC) {
+ rc = cifs_do_truncate(xid, file_dentry(file));
+ if (rc)
+ goto out;
+ }
+
/* Get the cached handle as SMB2 close is deferred */
- rc = cifs_get_readable_path(tcon, full_path, &cfile);
+ if (OPEN_FMODE(file->f_flags) & FMODE_WRITE) {
+ rc = cifs_get_writable_path(tcon, full_path,
+ FIND_WR_FSUID_ONLY |
+ FIND_WR_NO_PENDING_DELETE,
+ &cfile);
+ } else {
+ rc = cifs_get_readable_path(tcon, full_path, &cfile);
+ }
if (rc == 0) {
- if (file->f_flags == cfile->f_flags) {
+ unsigned int oflags = file->f_flags & ~(O_CREAT|O_EXCL|O_TRUNC);
+ unsigned int cflags = cfile->f_flags & ~(O_CREAT|O_EXCL|O_TRUNC);
+
+ if (cifs_convert_flags(oflags, 0) == cifs_convert_flags(cflags, 0) &&
+ (oflags & (O_SYNC|O_DIRECT)) == (cflags & (O_SYNC|O_DIRECT))) {
file->private_data = cfile;
spin_lock(&CIFS_I(inode)->deferred_lock);
cifs_del_deferred_close(cfile);
spin_unlock(&CIFS_I(inode)->deferred_lock);
goto use_cache;
- } else {
- _cifsFileInfo_put(cfile, true, false);
}
+ _cifsFileInfo_put(cfile, true, false);
+ } else {
+ /* hard link on the defeered close file */
+ rc = cifs_get_hardlink_path(tcon, inode, file);
+ if (rc)
+ cifs_close_deferred_file(CIFS_I(inode));
}
if (server->oplocks)
@@ -2065,6 +2162,29 @@ cifs_move_llist(struct list_head *source, struct list_head *dest)
list_move(li, dest);
}
+int
+cifs_get_hardlink_path(struct cifs_tcon *tcon, struct inode *inode,
+ struct file *file)
+{
+ struct cifsFileInfo *open_file = NULL;
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ int rc = 0;
+
+ spin_lock(&tcon->open_file_lock);
+ spin_lock(&cinode->open_file_lock);
+
+ list_for_each_entry(open_file, &cinode->openFileList, flist) {
+ if (file->f_flags == open_file->f_flags) {
+ rc = -EINVAL;
+ break;
+ }
+ }
+
+ spin_unlock(&cinode->open_file_lock);
+ spin_unlock(&tcon->open_file_lock);
+ return rc;
+}
+
void
cifs_free_llist(struct list_head *llist)
{
@@ -2389,8 +2509,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
return rc;
}
-void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t result,
- bool was_async)
+void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t result)
{
struct netfs_io_request *wreq = wdata->rreq;
struct netfs_inode *ictx = netfs_inode(wreq->inode);
@@ -2407,7 +2526,7 @@ void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t
netfs_resize_file(ictx, wrend, true);
}
- netfs_write_subrequest_terminated(&wdata->subreq, result, was_async);
+ netfs_write_subrequest_terminated(&wdata->subreq, result);
}
struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
@@ -2489,6 +2608,9 @@ refind_writable:
continue;
if (with_delete && !(open_file->fid.access & DELETE))
continue;
+ if ((flags & FIND_WR_NO_PENDING_DELETE) &&
+ open_file->status_file_deleted)
+ continue;
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
if (!open_file->invalidHandle) {
/* found a good writable file */
@@ -2606,6 +2728,16 @@ cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
spin_unlock(&tcon->open_file_lock);
free_dentry_path(page);
*ret_file = find_readable_file(cinode, 0);
+ if (*ret_file) {
+ spin_lock(&cinode->open_file_lock);
+ if ((*ret_file)->status_file_deleted) {
+ spin_unlock(&cinode->open_file_lock);
+ cifsFileInfo_put(*ret_file);
+ *ret_file = NULL;
+ } else {
+ spin_unlock(&cinode->open_file_lock);
+ }
+ }
return *ret_file ? 0 : -ENOENT;
}
@@ -2620,13 +2752,10 @@ cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
int datasync)
{
- unsigned int xid;
- int rc = 0;
- struct cifs_tcon *tcon;
- struct TCP_Server_Info *server;
struct cifsFileInfo *smbfile = file->private_data;
struct inode *inode = file_inode(file);
- struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ unsigned int xid;
+ int rc;
rc = file_write_and_wait_range(file, start, end);
if (rc) {
@@ -2634,39 +2763,15 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
return rc;
}
- xid = get_xid();
-
- cifs_dbg(FYI, "Sync file - name: %pD datasync: 0x%x\n",
- file, datasync);
+ cifs_dbg(FYI, "%s: name=%pD datasync=0x%x\n", __func__, file, datasync);
if (!CIFS_CACHE_READ(CIFS_I(inode))) {
rc = cifs_zap_mapping(inode);
- if (rc) {
- cifs_dbg(FYI, "rc: %d during invalidate phase\n", rc);
- rc = 0; /* don't care about it in fsync */
- }
+ cifs_dbg(FYI, "%s: invalidate mapping: rc = %d\n", __func__, rc);
}
- tcon = tlink_tcon(smbfile->tlink);
- if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
- server = tcon->ses->server;
- if (server->ops->flush == NULL) {
- rc = -ENOSYS;
- goto strict_fsync_exit;
- }
-
- if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) {
- smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY);
- if (smbfile) {
- rc = server->ops->flush(xid, tcon, &smbfile->fid);
- cifsFileInfo_put(smbfile);
- } else
- cifs_dbg(FYI, "ignore fsync for file not open for write\n");
- } else
- rc = server->ops->flush(xid, tcon, &smbfile->fid);
- }
-
-strict_fsync_exit:
+ xid = get_xid();
+ rc = cifs_file_flush(xid, inode, smbfile);
free_xid(xid);
return rc;
}
@@ -2958,38 +3063,38 @@ static const struct vm_operations_struct cifs_file_vm_ops = {
.page_mkwrite = cifs_page_mkwrite,
};
-int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma)
+int cifs_file_strict_mmap_prepare(struct vm_area_desc *desc)
{
int xid, rc = 0;
- struct inode *inode = file_inode(file);
+ struct inode *inode = file_inode(desc->file);
xid = get_xid();
if (!CIFS_CACHE_READ(CIFS_I(inode)))
rc = cifs_zap_mapping(inode);
if (!rc)
- rc = generic_file_mmap(file, vma);
+ rc = generic_file_mmap_prepare(desc);
if (!rc)
- vma->vm_ops = &cifs_file_vm_ops;
+ desc->vm_ops = &cifs_file_vm_ops;
free_xid(xid);
return rc;
}
-int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
+int cifs_file_mmap_prepare(struct vm_area_desc *desc)
{
int rc, xid;
xid = get_xid();
- rc = cifs_revalidate_file(file);
+ rc = cifs_revalidate_file(desc->file);
if (rc)
cifs_dbg(FYI, "Validation prior to mmap failed, error=%d\n",
rc);
if (!rc)
- rc = generic_file_mmap(file, vma);
+ rc = generic_file_mmap_prepare(desc);
if (!rc)
- vma->vm_ops = &cifs_file_vm_ops;
+ desc->vm_ops = &cifs_file_vm_ops;
free_xid(xid);
return rc;
@@ -3047,7 +3152,8 @@ void cifs_oplock_break(struct work_struct *work)
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
oplock_break);
struct inode *inode = d_inode(cfile->dentry);
- struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct super_block *sb = inode->i_sb;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
@@ -3057,6 +3163,12 @@ void cifs_oplock_break(struct work_struct *work)
__u64 persistent_fid, volatile_fid;
__u16 net_fid;
+ /*
+ * Hold a reference to the superblock to prevent it and its inodes from
+ * being freed while we are accessing cinode. Otherwise, _cifsFileInfo_put()
+ * may release the last reference to the sb and trigger inode eviction.
+ */
+ cifs_sb_active(sb);
wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS,
TASK_UNINTERRUPTIBLE);
@@ -3076,7 +3188,7 @@ void cifs_oplock_break(struct work_struct *work)
cinode->oplock = 0;
}
- if (inode && S_ISREG(inode->i_mode)) {
+ if (S_ISREG(inode->i_mode)) {
if (CIFS_CACHE_READ(cinode))
break_lease(inode, O_RDONLY);
else
@@ -3129,6 +3241,7 @@ oplock_break_ack:
cifs_put_tlink(tlink);
out:
cifs_done_oplock_break(cinode);
+ cifs_sb_deactive(sb);
}
static int cifs_swap_activate(struct swap_info_struct *sis,
diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index c87879e4739b..c2de97e4ad59 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -133,6 +133,9 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
fsparam_flag("rootfs", Opt_rootfs),
fsparam_flag("compress", Opt_compress),
fsparam_flag("witness", Opt_witness),
+ fsparam_flag_no("nativesocket", Opt_nativesocket),
+ fsparam_flag_no("unicode", Opt_unicode),
+ fsparam_flag_no("nbsessinit", Opt_nbsessinit),
/* Mount options which take uid or gid */
fsparam_uid("backupuid", Opt_backupuid),
@@ -170,6 +173,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
fsparam_string("username", Opt_user),
fsparam_string("pass", Opt_pass),
fsparam_string("password", Opt_pass),
+ fsparam_string("pass2", Opt_pass2),
fsparam_string("password2", Opt_pass2),
fsparam_string("ip", Opt_ip),
fsparam_string("addr", Opt_ip),
@@ -185,6 +189,8 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
fsparam_string("cache", Opt_cache),
fsparam_string("reparse", Opt_reparse),
fsparam_string("upcall_target", Opt_upcalltarget),
+ fsparam_string("symlink", Opt_symlink),
+ fsparam_string("symlinkroot", Opt_symlinkroot),
/* Arguments that should be ignored */
fsparam_flag("guest", Opt_ignore),
@@ -332,6 +338,7 @@ cifs_parse_cache_flavor(struct fs_context *fc, char *value, struct smb3_fs_conte
static const match_table_t reparse_flavor_tokens = {
{ Opt_reparse_default, "default" },
+ { Opt_reparse_none, "none" },
{ Opt_reparse_nfs, "nfs" },
{ Opt_reparse_wsl, "wsl" },
{ Opt_reparse_err, NULL },
@@ -346,6 +353,9 @@ static int parse_reparse_flavor(struct fs_context *fc, char *value,
case Opt_reparse_default:
ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT;
break;
+ case Opt_reparse_none:
+ ctx->reparse_type = CIFS_REPARSE_TYPE_NONE;
+ break;
case Opt_reparse_nfs:
ctx->reparse_type = CIFS_REPARSE_TYPE_NFS;
break;
@@ -359,6 +369,55 @@ static int parse_reparse_flavor(struct fs_context *fc, char *value,
return 0;
}
+static const match_table_t symlink_flavor_tokens = {
+ { Opt_symlink_default, "default" },
+ { Opt_symlink_none, "none" },
+ { Opt_symlink_native, "native" },
+ { Opt_symlink_unix, "unix" },
+ { Opt_symlink_mfsymlinks, "mfsymlinks" },
+ { Opt_symlink_sfu, "sfu" },
+ { Opt_symlink_nfs, "nfs" },
+ { Opt_symlink_wsl, "wsl" },
+ { Opt_symlink_err, NULL },
+};
+
+static int parse_symlink_flavor(struct fs_context *fc, char *value,
+ struct smb3_fs_context *ctx)
+{
+ substring_t args[MAX_OPT_ARGS];
+
+ switch (match_token(value, symlink_flavor_tokens, args)) {
+ case Opt_symlink_default:
+ ctx->symlink_type = CIFS_SYMLINK_TYPE_DEFAULT;
+ break;
+ case Opt_symlink_none:
+ ctx->symlink_type = CIFS_SYMLINK_TYPE_NONE;
+ break;
+ case Opt_symlink_native:
+ ctx->symlink_type = CIFS_SYMLINK_TYPE_NATIVE;
+ break;
+ case Opt_symlink_unix:
+ ctx->symlink_type = CIFS_SYMLINK_TYPE_UNIX;
+ break;
+ case Opt_symlink_mfsymlinks:
+ ctx->symlink_type = CIFS_SYMLINK_TYPE_MFSYMLINKS;
+ break;
+ case Opt_symlink_sfu:
+ ctx->symlink_type = CIFS_SYMLINK_TYPE_SFU;
+ break;
+ case Opt_symlink_nfs:
+ ctx->symlink_type = CIFS_SYMLINK_TYPE_NFS;
+ break;
+ case Opt_symlink_wsl:
+ ctx->symlink_type = CIFS_SYMLINK_TYPE_WSL;
+ break;
+ default:
+ cifs_errorf(fc, "bad symlink= option: %s\n", value);
+ return 1;
+ }
+ return 0;
+}
+
#define DUP_CTX_STR(field) \
do { \
if (ctx->field) { \
@@ -385,6 +444,8 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
new_ctx->source = NULL;
new_ctx->iocharset = NULL;
new_ctx->leaf_fullpath = NULL;
+ new_ctx->dns_dom = NULL;
+ new_ctx->symlinkroot = NULL;
/*
* Make sure to stay in sync with smb3_cleanup_fs_context_contents()
*/
@@ -399,6 +460,8 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
DUP_CTX_STR(nodename);
DUP_CTX_STR(iocharset);
DUP_CTX_STR(leaf_fullpath);
+ DUP_CTX_STR(dns_dom);
+ DUP_CTX_STR(symlinkroot);
return 0;
}
@@ -442,7 +505,7 @@ cifs_parse_smb_version(struct fs_context *fc, char *value, struct smb3_fs_contex
case Smb_20:
cifs_errorf(fc, "vers=2.0 mount not permitted when legacy dialects disabled\n");
return 1;
-#endif /* CIFS_ALLOW_INSECURE_LEGACY */
+#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
case Smb_21:
ctx->ops = &smb21_operations;
ctx->vals = &smb21_values;
@@ -648,12 +711,54 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx)
return 0;
}
+static int smb3_handle_conflicting_options(struct fs_context *fc)
+{
+ struct smb3_fs_context *ctx = smb3_fc2context(fc);
+
+ if (ctx->multichannel_specified) {
+ if (ctx->multichannel) {
+ if (!ctx->max_channels_specified) {
+ ctx->max_channels = 2;
+ } else if (ctx->max_channels == 1) {
+ cifs_errorf(fc,
+ "max_channels must be greater than 1 when multichannel is enabled\n");
+ return -EINVAL;
+ }
+ } else {
+ if (!ctx->max_channels_specified) {
+ ctx->max_channels = 1;
+ } else if (ctx->max_channels > 1) {
+ cifs_errorf(fc,
+ "max_channels must be equal to 1 when multichannel is disabled\n");
+ return -EINVAL;
+ }
+ }
+ } else {
+ if (ctx->max_channels_specified) {
+ if (ctx->max_channels > 1)
+ ctx->multichannel = true;
+ else
+ ctx->multichannel = false;
+ } else {
+ ctx->multichannel = false;
+ ctx->max_channels = 1;
+ }
+ }
+
+ //resetting default values as remount doesn't initialize fs_context again
+ ctx->multichannel_specified = false;
+ ctx->max_channels_specified = false;
+
+ return 0;
+}
+
static void smb3_fs_context_free(struct fs_context *fc);
static int smb3_fs_context_parse_param(struct fs_context *fc,
struct fs_parameter *param);
static int smb3_fs_context_parse_monolithic(struct fs_context *fc,
void *data);
static int smb3_get_tree(struct fs_context *fc);
+static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels);
static int smb3_reconfigure(struct fs_context *fc);
static const struct fs_context_operations smb3_fs_context_ops = {
@@ -710,19 +815,18 @@ static int smb3_fs_context_parse_monolithic(struct fs_context *fc,
}
- len = 0;
value = strchr(key, '=');
if (value) {
if (value == key)
continue;
*value++ = 0;
- len = strlen(value);
}
- ret = vfs_parse_fs_string(fc, key, value, len);
+ ret = vfs_parse_fs_string(fc, key, value);
if (ret < 0)
break;
}
+ ret = smb3_handle_conflicting_options(fc);
return ret;
}
@@ -902,6 +1006,14 @@ static int smb3_verify_reconfigure_ctx(struct fs_context *fc,
cifs_errorf(fc, "can not change iocharset during remount\n");
return -EINVAL;
}
+ if (new_ctx->unicode != old_ctx->unicode) {
+ cifs_errorf(fc, "can not change unicode during remount\n");
+ return -EINVAL;
+ }
+ if (new_ctx->rfc1001_sessinit != old_ctx->rfc1001_sessinit) {
+ cifs_errorf(fc, "can not change nbsessinit during remount\n");
+ return -EINVAL;
+ }
return 0;
}
@@ -920,12 +1032,54 @@ do { \
cifs_sb->ctx->field = NULL; \
} while (0)
+int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
+{
+ if (ses->password &&
+ cifs_sb->ctx->password &&
+ strcmp(ses->password, cifs_sb->ctx->password)) {
+ kfree_sensitive(cifs_sb->ctx->password);
+ cifs_sb->ctx->password = kstrdup(ses->password, GFP_KERNEL);
+ if (!cifs_sb->ctx->password)
+ return -ENOMEM;
+ }
+ if (ses->password2 &&
+ cifs_sb->ctx->password2 &&
+ strcmp(ses->password2, cifs_sb->ctx->password2)) {
+ kfree_sensitive(cifs_sb->ctx->password2);
+ cifs_sb->ctx->password2 = kstrdup(ses->password2, GFP_KERNEL);
+ if (!cifs_sb->ctx->password2) {
+ kfree_sensitive(cifs_sb->ctx->password);
+ cifs_sb->ctx->password = NULL;
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+/*
+ * smb3_sync_ses_chan_max - Synchronize the session's maximum channel count
+ * @ses: pointer to the old CIFS session structure
+ * @max_channels: new maximum number of channels to allow
+ *
+ * Updates the session's chan_max field to the new value, protecting the update
+ * with the session's channel lock. This should be called whenever the maximum
+ * allowed channels for a session changes (e.g., after a remount or reconfigure).
+ */
+static void smb3_sync_ses_chan_max(struct cifs_ses *ses, unsigned int max_channels)
+{
+ spin_lock(&ses->chan_lock);
+ ses->chan_max = max_channels;
+ spin_unlock(&ses->chan_lock);
+}
+
static int smb3_reconfigure(struct fs_context *fc)
{
struct smb3_fs_context *ctx = smb3_fc2context(fc);
struct dentry *root = fc->root;
struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses;
+ unsigned int rsize = ctx->rsize, wsize = ctx->wsize;
+ char *new_password = NULL, *new_password2 = NULL;
bool need_recon = false;
int rc;
@@ -945,31 +1099,102 @@ static int smb3_reconfigure(struct fs_context *fc)
STEAL_STRING(cifs_sb, ctx, UNC);
STEAL_STRING(cifs_sb, ctx, source);
STEAL_STRING(cifs_sb, ctx, username);
+
if (need_recon == false)
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
else {
- kfree_sensitive(ses->password);
- ses->password = kstrdup(ctx->password, GFP_KERNEL);
- if (!ses->password)
+ if (ctx->password) {
+ new_password = kstrdup(ctx->password, GFP_KERNEL);
+ if (!new_password)
+ return -ENOMEM;
+ } else
+ STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
+ }
+
+ /*
+ * if a new password2 has been specified, then reset it's value
+ * inside the ses struct
+ */
+ if (ctx->password2) {
+ new_password2 = kstrdup(ctx->password2, GFP_KERNEL);
+ if (!new_password2) {
+ kfree_sensitive(new_password);
return -ENOMEM;
+ }
+ } else
+ STEAL_STRING_SENSITIVE(cifs_sb, ctx, password2);
+
+ /*
+ * we may update the passwords in the ses struct below. Make sure we do
+ * not race with smb2_reconnect
+ */
+ mutex_lock(&ses->session_mutex);
+
+ /*
+ * smb2_reconnect may swap password and password2 in case session setup
+ * failed. First get ctx passwords in sync with ses passwords. It should
+ * be okay to do this even if this function were to return an error at a
+ * later stage
+ */
+ rc = smb3_sync_session_ctx_passwords(cifs_sb, ses);
+ if (rc) {
+ mutex_unlock(&ses->session_mutex);
+ return rc;
+ }
+
+ /*
+ * now that allocations for passwords are done, commit them
+ */
+ if (new_password) {
+ kfree_sensitive(ses->password);
+ ses->password = new_password;
+ }
+ if (new_password2) {
kfree_sensitive(ses->password2);
- ses->password2 = kstrdup(ctx->password2, GFP_KERNEL);
- if (!ses->password2) {
- kfree_sensitive(ses->password);
- ses->password = NULL;
- return -ENOMEM;
+ ses->password2 = new_password2;
+ }
+
+ /*
+ * If multichannel or max_channels has changed, update the session's channels accordingly.
+ * This may add or remove channels to match the new configuration.
+ */
+ if ((ctx->multichannel != cifs_sb->ctx->multichannel) ||
+ (ctx->max_channels != cifs_sb->ctx->max_channels)) {
+
+ /* Synchronize ses->chan_max with the new mount context */
+ smb3_sync_ses_chan_max(ses, ctx->max_channels);
+ /* Now update the session's channels to match the new configuration */
+ /* Prevent concurrent scaling operations */
+ spin_lock(&ses->ses_lock);
+ if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) {
+ spin_unlock(&ses->ses_lock);
+ mutex_unlock(&ses->session_mutex);
+ return -EINVAL;
}
+ ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS;
+ spin_unlock(&ses->ses_lock);
+
+ mutex_unlock(&ses->session_mutex);
+
+ rc = smb3_update_ses_channels(ses, ses->server,
+ false /* from_reconnect */,
+ false /* disable_mchan */);
+
+ /* Clear scaling flag after operation */
+ spin_lock(&ses->ses_lock);
+ ses->flags &= ~CIFS_SES_FLAG_SCALE_CHANNELS;
+ spin_unlock(&ses->ses_lock);
+ } else {
+ mutex_unlock(&ses->session_mutex);
}
+
STEAL_STRING(cifs_sb, ctx, domainname);
STEAL_STRING(cifs_sb, ctx, nodename);
STEAL_STRING(cifs_sb, ctx, iocharset);
/* if rsize or wsize not passed in on remount, use previous values */
- if (ctx->rsize == 0)
- ctx->rsize = cifs_sb->ctx->rsize;
- if (ctx->wsize == 0)
- ctx->wsize = cifs_sb->ctx->wsize;
-
+ ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize;
+ ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize;
smb3_cleanup_fs_context_contents(cifs_sb->ctx);
rc = smb3_fs_context_dup(cifs_sb->ctx, ctx);
@@ -990,6 +1215,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
int i, opt;
bool is_smb3 = !strcmp(fc->fs_type->name, "smb3");
bool skip_parsing = false;
+ char *hostname;
cifs_dbg(FYI, "CIFS: parsing cifs mount option '%s'\n", param->key);
@@ -1004,6 +1230,9 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
} else if (!strcmp("user", param->key) || !strcmp("username", param->key)) {
skip_parsing = true;
opt = Opt_user;
+ } else if (!strcmp("pass2", param->key) || !strcmp("password2", param->key)) {
+ skip_parsing = true;
+ opt = Opt_pass2;
}
}
@@ -1112,15 +1341,11 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
ctx->nodelete = 1;
break;
case Opt_multichannel:
- if (result.negated) {
+ ctx->multichannel_specified = true;
+ if (result.negated)
ctx->multichannel = false;
- ctx->max_channels = 1;
- } else {
+ else
ctx->multichannel = true;
- /* if number of channels not specified, default to 2 */
- if (ctx->max_channels < 2)
- ctx->max_channels = 2;
- }
break;
case Opt_uid:
ctx->linux_uid = result.uid;
@@ -1170,7 +1395,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
__func__);
goto cifs_parse_mount_err;
}
- ctx->bsize = result.uint_32;
+ ctx->bsize = CIFS_ALIGN_BSIZE(fc, result.uint_32);
ctx->got_bsize = true;
break;
case Opt_rasize:
@@ -1194,40 +1419,31 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
ctx->rasize = result.uint_32;
break;
case Opt_rsize:
- ctx->rsize = result.uint_32;
+ ctx->rsize = CIFS_ALIGN_RSIZE(fc, result.uint_32);
ctx->got_rsize = true;
+ ctx->vol_rsize = ctx->rsize;
break;
case Opt_wsize:
- ctx->wsize = result.uint_32;
+ ctx->wsize = CIFS_ALIGN_WSIZE(fc, result.uint_32);
ctx->got_wsize = true;
- if (ctx->wsize % PAGE_SIZE != 0) {
- ctx->wsize = round_down(ctx->wsize, PAGE_SIZE);
- if (ctx->wsize == 0) {
- ctx->wsize = PAGE_SIZE;
- cifs_dbg(VFS, "wsize too small, reset to minimum %ld\n", PAGE_SIZE);
- } else {
- cifs_dbg(VFS,
- "wsize rounded down to %d to multiple of PAGE_SIZE %ld\n",
- ctx->wsize, PAGE_SIZE);
- }
- }
+ ctx->vol_wsize = ctx->wsize;
break;
case Opt_acregmax:
- ctx->acregmax = HZ * result.uint_32;
- if (ctx->acregmax > CIFS_MAX_ACTIMEO) {
+ if (result.uint_32 > CIFS_MAX_ACTIMEO / HZ) {
cifs_errorf(fc, "acregmax too large\n");
goto cifs_parse_mount_err;
}
+ ctx->acregmax = HZ * result.uint_32;
break;
case Opt_acdirmax:
- ctx->acdirmax = HZ * result.uint_32;
- if (ctx->acdirmax > CIFS_MAX_ACTIMEO) {
+ if (result.uint_32 > CIFS_MAX_ACTIMEO / HZ) {
cifs_errorf(fc, "acdirmax too large\n");
goto cifs_parse_mount_err;
}
+ ctx->acdirmax = HZ * result.uint_32;
break;
case Opt_actimeo:
- if (HZ * result.uint_32 > CIFS_MAX_ACTIMEO) {
+ if (result.uint_32 > CIFS_MAX_ACTIMEO / HZ) {
cifs_errorf(fc, "timeout too large\n");
goto cifs_parse_mount_err;
}
@@ -1239,13 +1455,18 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
ctx->acdirmax = ctx->acregmax = HZ * result.uint_32;
break;
case Opt_closetimeo:
- ctx->closetimeo = HZ * result.uint_32;
- if (ctx->closetimeo > SMB3_MAX_DCLOSETIMEO) {
+ if (result.uint_32 > SMB3_MAX_DCLOSETIMEO / HZ) {
cifs_errorf(fc, "closetimeo too large\n");
goto cifs_parse_mount_err;
}
+ ctx->closetimeo = HZ * result.uint_32;
break;
case Opt_echo_interval:
+ if (result.uint_32 < SMB_ECHO_INTERVAL_MIN ||
+ result.uint_32 > SMB_ECHO_INTERVAL_MAX) {
+ cifs_errorf(fc, "echo interval is out of bounds\n");
+ goto cifs_parse_mount_err;
+ }
ctx->echo_interval = result.uint_32;
break;
case Opt_snapshot:
@@ -1260,15 +1481,13 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
ctx->max_credits = result.uint_32;
break;
case Opt_max_channels:
+ ctx->max_channels_specified = true;
if (result.uint_32 < 1 || result.uint_32 > CIFS_MAX_CHANNELS) {
cifs_errorf(fc, "%s: Invalid max_channels value, needs to be 1-%d\n",
__func__, CIFS_MAX_CHANNELS);
goto cifs_parse_mount_err;
}
ctx->max_channels = result.uint_32;
- /* If more than one channel requested ... they want multichan */
- if (result.uint_32 > 1)
- ctx->multichannel = true;
break;
case Opt_max_cached_dirs:
if (result.uint_32 < 1) {
@@ -1301,17 +1520,29 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
cifs_errorf(fc, "Unknown error parsing devname\n");
goto cifs_parse_mount_err;
}
+ kfree(ctx->source);
ctx->source = smb3_fs_context_fullpath(ctx, '/');
if (IS_ERR(ctx->source)) {
ctx->source = NULL;
cifs_errorf(fc, "OOM when copying UNC string\n");
goto cifs_parse_mount_err;
}
+ kfree(fc->source);
fc->source = kstrdup(ctx->source, GFP_KERNEL);
if (fc->source == NULL) {
cifs_errorf(fc, "OOM when copying UNC string\n");
goto cifs_parse_mount_err;
}
+ hostname = extract_hostname(ctx->UNC);
+ if (IS_ERR(hostname)) {
+ cifs_errorf(fc, "Cannot extract hostname from UNC string\n");
+ goto cifs_parse_mount_err;
+ }
+ /* last byte, type, is 0x20 for servr type */
+ memset(ctx->target_rfc1001_name, 0x20, RFC1001_NAME_LEN_WITH_NULL);
+ for (i = 0; i < RFC1001_NAME_LEN && hostname[i] != 0; i++)
+ ctx->target_rfc1001_name[i] = toupper(hostname[i]);
+ kfree(hostname);
break;
case Opt_user:
kfree(ctx->username);
@@ -1324,40 +1555,26 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
break;
}
- if (strnlen(param->string, CIFS_MAX_USERNAME_LEN) >
+ if (strnlen(param->string, CIFS_MAX_USERNAME_LEN) ==
CIFS_MAX_USERNAME_LEN) {
pr_warn("username too long\n");
goto cifs_parse_mount_err;
}
- ctx->username = kstrdup(param->string, GFP_KERNEL);
- if (ctx->username == NULL) {
- cifs_errorf(fc, "OOM when copying username string\n");
- goto cifs_parse_mount_err;
- }
+ ctx->username = no_free_ptr(param->string);
break;
case Opt_pass:
kfree_sensitive(ctx->password);
ctx->password = NULL;
if (strlen(param->string) == 0)
break;
-
- ctx->password = kstrdup(param->string, GFP_KERNEL);
- if (ctx->password == NULL) {
- cifs_errorf(fc, "OOM when copying password string\n");
- goto cifs_parse_mount_err;
- }
+ ctx->password = no_free_ptr(param->string);
break;
case Opt_pass2:
kfree_sensitive(ctx->password2);
ctx->password2 = NULL;
if (strlen(param->string) == 0)
break;
-
- ctx->password2 = kstrdup(param->string, GFP_KERNEL);
- if (ctx->password2 == NULL) {
- cifs_errorf(fc, "OOM when copying password2 string\n");
- goto cifs_parse_mount_err;
- }
+ ctx->password2 = no_free_ptr(param->string);
break;
case Opt_ip:
if (strlen(param->string) == 0) {
@@ -1380,11 +1597,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
}
kfree(ctx->domainname);
- ctx->domainname = kstrdup(param->string, GFP_KERNEL);
- if (ctx->domainname == NULL) {
- cifs_errorf(fc, "OOM when copying domainname string\n");
- goto cifs_parse_mount_err;
- }
+ ctx->domainname = no_free_ptr(param->string);
cifs_dbg(FYI, "Domain name set\n");
break;
case Opt_srcaddr:
@@ -1404,11 +1617,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
if (strncasecmp(param->string, "default", 7) != 0) {
kfree(ctx->iocharset);
- ctx->iocharset = kstrdup(param->string, GFP_KERNEL);
- if (ctx->iocharset == NULL) {
- cifs_errorf(fc, "OOM when copying iocharset string\n");
- goto cifs_parse_mount_err;
- }
+ ctx->iocharset = no_free_ptr(param->string);
}
/* if iocharset not set then load_nls_default
* is used by caller
@@ -1455,6 +1664,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
if (i == RFC1001_NAME_LEN && param->string[i] != 0)
pr_warn("server netbiosname longer than 15 truncated\n");
break;
+ case Opt_nbsessinit:
+ ctx->rfc1001_sessinit = !result.negated;
+ cifs_dbg(FYI, "rfc1001_sessinit set to %d\n", ctx->rfc1001_sessinit);
+ break;
case Opt_ver:
/* version of mount userspace tools, not dialect */
/* If interface changes in mount.cifs bump to new ver */
@@ -1496,6 +1709,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
ctx->witness = true;
pr_warn_once("Witness protocol support is experimental\n");
break;
+ case Opt_unicode:
+ ctx->unicode = !result.negated;
+ cifs_dbg(FYI, "unicode set to %d\n", ctx->unicode);
+ break;
case Opt_rootfs:
#ifndef CONFIG_CIFS_ROOT
cifs_dbg(VFS, "rootfs support requires CONFIG_CIFS_ROOT config option\n");
@@ -1520,6 +1737,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
pr_warn_once("conflicting posix mount options specified\n");
ctx->linux_ext = 1;
ctx->no_linux_ext = 0;
+ ctx->nonativesocket = 1; /* POSIX mounts use NFS style reparse points */
}
break;
case Opt_nocase:
@@ -1658,6 +1876,27 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
if (parse_reparse_flavor(fc, param->string, ctx))
goto cifs_parse_mount_err;
break;
+ case Opt_nativesocket:
+ ctx->nonativesocket = result.negated;
+ break;
+ case Opt_symlink:
+ if (parse_symlink_flavor(fc, param->string, ctx))
+ goto cifs_parse_mount_err;
+ break;
+ case Opt_symlinkroot:
+ if (param->string[0] != '/') {
+ cifs_errorf(fc, "symlinkroot mount options must be absolute path\n");
+ goto cifs_parse_mount_err;
+ }
+ if (strnlen(param->string, PATH_MAX) == PATH_MAX) {
+ cifs_errorf(fc, "symlinkroot path too long (max path length: %u)\n",
+ PATH_MAX - 1);
+ goto cifs_parse_mount_err;
+ }
+ kfree(ctx->symlinkroot);
+ ctx->symlinkroot = param->string;
+ param->string = NULL;
+ break;
}
/* case Opt_ignore: - is ignored as expected ... */
@@ -1673,6 +1912,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
ctx->password = NULL;
kfree_sensitive(ctx->password2);
ctx->password2 = NULL;
+ kfree(ctx->source);
+ ctx->source = NULL;
+ kfree(fc->source);
+ fc->source = NULL;
return -EINVAL;
}
@@ -1696,13 +1939,16 @@ int smb3_init_fs_context(struct fs_context *fc)
memset(ctx->source_rfc1001_name, 0x20, RFC1001_NAME_LEN);
for (i = 0; i < strnlen(nodename, RFC1001_NAME_LEN); i++)
ctx->source_rfc1001_name[i] = toupper(nodename[i]);
-
ctx->source_rfc1001_name[RFC1001_NAME_LEN] = 0;
+
/*
* null target name indicates to use *SMBSERVR default called name
* if we end up sending RFC1001 session initialize
*/
ctx->target_rfc1001_name[0] = 0;
+
+ ctx->rfc1001_sessinit = -1; /* autodetect based on port number */
+
ctx->cred_uid = current_uid();
ctx->linux_uid = current_uid();
ctx->linux_gid = current_gid();
@@ -1745,6 +1991,8 @@ int smb3_init_fs_context(struct fs_context *fc)
/* default to no multichannel (single server connection) */
ctx->multichannel = false;
+ ctx->multichannel_specified = false;
+ ctx->max_channels_specified = false;
ctx->max_channels = 1;
ctx->backupuid_specified = false; /* no backup intent for a user */
@@ -1752,6 +2000,10 @@ int smb3_init_fs_context(struct fs_context *fc)
ctx->retrans = 1;
ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT;
+ ctx->symlink_type = CIFS_SYMLINK_TYPE_DEFAULT;
+ ctx->nonativesocket = 0;
+
+ ctx->unicode = -1; /* autodetect, but prefer UNICODE mode */
/*
* short int override_uid = -1;
@@ -1796,6 +2048,10 @@ smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx)
ctx->prepath = NULL;
kfree(ctx->leaf_fullpath);
ctx->leaf_fullpath = NULL;
+ kfree(ctx->dns_dom);
+ ctx->dns_dom = NULL;
+ kfree(ctx->symlinkroot);
+ ctx->symlinkroot = NULL;
}
void
diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h
index 67b7fc48ac58..7af7cbbe4208 100644
--- a/fs/smb/client/fs_context.h
+++ b/fs/smb/client/fs_context.h
@@ -20,6 +20,21 @@
cifs_dbg(VFS, fmt, ## __VA_ARGS__); \
} while (0)
+static inline size_t cifs_io_align(struct fs_context *fc,
+ const char *name, size_t size)
+{
+ if (!size || !IS_ALIGNED(size, PAGE_SIZE)) {
+ cifs_errorf(fc, "unaligned %s, making it a multiple of %lu bytes\n",
+ name, PAGE_SIZE);
+ size = umax(round_down(size, PAGE_SIZE), PAGE_SIZE);
+ }
+ return size;
+}
+
+#define CIFS_ALIGN_WSIZE(_fc, _size) cifs_io_align(_fc, "wsize", _size)
+#define CIFS_ALIGN_RSIZE(_fc, _size) cifs_io_align(_fc, "rsize", _size)
+#define CIFS_ALIGN_BSIZE(_fc, _size) cifs_io_align(_fc, "bsize", _size)
+
enum smb_version {
Smb_1 = 1,
Smb_20,
@@ -43,11 +58,24 @@ enum {
enum cifs_reparse_parm {
Opt_reparse_default,
+ Opt_reparse_none,
Opt_reparse_nfs,
Opt_reparse_wsl,
Opt_reparse_err
};
+enum cifs_symlink_parm {
+ Opt_symlink_default,
+ Opt_symlink_none,
+ Opt_symlink_native,
+ Opt_symlink_unix,
+ Opt_symlink_mfsymlinks,
+ Opt_symlink_sfu,
+ Opt_symlink_nfs,
+ Opt_symlink_wsl,
+ Opt_symlink_err
+};
+
enum cifs_sec_param {
Opt_sec_krb5,
Opt_sec_krb5i,
@@ -122,6 +150,7 @@ enum cifs_param {
Opt_witness,
Opt_is_upcall_target_mount,
Opt_is_upcall_target_application,
+ Opt_unicode,
/* Mount options which take numeric value */
Opt_backupuid,
@@ -160,12 +189,16 @@ enum cifs_param {
Opt_iocharset,
Opt_netbiosname,
Opt_servern,
+ Opt_nbsessinit,
Opt_ver,
Opt_vers,
Opt_sec,
Opt_cache,
Opt_reparse,
Opt_upcalltarget,
+ Opt_nativesocket,
+ Opt_symlink,
+ Opt_symlinkroot,
/* Mount options to be ignored */
Opt_ignore,
@@ -199,6 +232,7 @@ struct smb3_fs_context {
char *iocharset; /* local code page for mapping to and from Unicode */
char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */
char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */
+ int rfc1001_sessinit;
kuid_t cred_uid;
kuid_t linux_uid;
kgid_t linux_gid;
@@ -260,9 +294,14 @@ struct smb3_fs_context {
bool domainauto:1;
bool rdma:1;
bool multichannel:1;
+ bool multichannel_specified:1; /* true if user specified multichannel or nomultichannel */
+ bool max_channels_specified:1; /* true if user specified max_channels */
bool use_client_guid:1;
/* reuse existing guid for multichannel */
u8 client_guid[SMB2_CLIENT_GUID_SIZE];
+ /* User-specified original r/wsize value */
+ unsigned int vol_rsize;
+ unsigned int vol_wsize;
unsigned int bsize;
unsigned int rasize;
unsigned int rsize;
@@ -290,15 +329,38 @@ struct smb3_fs_context {
bool compress; /* enable SMB2 messages (READ/WRITE) de/compression */
bool rootfs:1; /* if it's a SMB root file system */
bool witness:1; /* use witness protocol */
+ int unicode;
char *leaf_fullpath;
struct cifs_ses *dfs_root_ses;
bool dfs_automount:1; /* set for dfs automount only */
enum cifs_reparse_type reparse_type;
+ enum cifs_symlink_type symlink_type;
+ bool nonativesocket:1;
bool dfs_conn:1; /* set for dfs mounts */
+ char *dns_dom;
+ char *symlinkroot; /* top level directory for native SMB symlinks in absolute format */
};
extern const struct fs_parameter_spec smb3_fs_parameters[];
+static inline enum cifs_symlink_type cifs_symlink_type(struct cifs_sb_info *cifs_sb)
+{
+ bool posix = cifs_sb_master_tcon(cifs_sb)->posix_extensions;
+
+ if (cifs_sb->ctx->symlink_type != CIFS_SYMLINK_TYPE_DEFAULT)
+ return cifs_sb->ctx->symlink_type;
+
+ if (cifs_sb->ctx->mfsymlinks)
+ return CIFS_SYMLINK_TYPE_MFSYMLINKS;
+ else if (cifs_sb->ctx->sfu_emul)
+ return CIFS_SYMLINK_TYPE_SFU;
+ else if (cifs_sb->ctx->linux_ext && !cifs_sb->ctx->no_linux_ext)
+ return posix ? CIFS_SYMLINK_TYPE_NATIVE : CIFS_SYMLINK_TYPE_UNIX;
+ else if (cifs_sb->ctx->reparse_type != CIFS_REPARSE_TYPE_NONE)
+ return CIFS_SYMLINK_TYPE_NATIVE;
+ return CIFS_SYMLINK_TYPE_NONE;
+}
+
extern int smb3_init_fs_context(struct fs_context *fc);
extern void smb3_cleanup_fs_context_contents(struct smb3_fs_context *ctx);
extern void smb3_cleanup_fs_context(struct smb3_fs_context *ctx);
@@ -309,6 +371,7 @@ static inline struct smb3_fs_context *smb3_fc2context(const struct fs_context *f
}
extern int smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx);
+extern int smb3_sync_session_ctx_passwords(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses);
extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb);
/*
@@ -331,4 +394,36 @@ static inline void cifs_mount_unlock(void)
mutex_unlock(&cifs_mount_mutex);
}
+static inline void cifs_negotiate_rsize(struct TCP_Server_Info *server,
+ struct smb3_fs_context *ctx,
+ struct cifs_tcon *tcon)
+{
+ unsigned int size;
+
+ size = umax(server->ops->negotiate_rsize(tcon, ctx), PAGE_SIZE);
+ if (ctx->rsize)
+ size = umax(umin(ctx->rsize, size), PAGE_SIZE);
+ ctx->rsize = round_down(size, PAGE_SIZE);
+}
+
+static inline void cifs_negotiate_wsize(struct TCP_Server_Info *server,
+ struct smb3_fs_context *ctx,
+ struct cifs_tcon *tcon)
+{
+ unsigned int size;
+
+ size = umax(server->ops->negotiate_wsize(tcon, ctx), PAGE_SIZE);
+ if (ctx->wsize)
+ size = umax(umin(ctx->wsize, size), PAGE_SIZE);
+ ctx->wsize = round_down(size, PAGE_SIZE);
+}
+
+static inline void cifs_negotiate_iosize(struct TCP_Server_Info *server,
+ struct smb3_fs_context *ctx,
+ struct cifs_tcon *tcon)
+{
+ cifs_negotiate_rsize(server, ctx, tcon);
+ cifs_negotiate_wsize(server, ctx, tcon);
+}
+
#endif
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index de8063b44072..f9ee95953fa4 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -6,6 +6,7 @@
*
*/
#include <linux/fs.h>
+#include <linux/fs_struct.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
@@ -101,7 +102,7 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
cifs_dbg(FYI, "%s: revalidating inode %llu\n",
__func__, cifs_i->uniqueid);
- if (inode->i_state & I_NEW) {
+ if (inode_state_read_once(inode) & I_NEW) {
cifs_dbg(FYI, "%s: inode %llu is new\n",
__func__, cifs_i->uniqueid);
return;
@@ -146,7 +147,7 @@ cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
*/
if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) {
/* only provide fake values on a new inode */
- if (inode->i_state & I_NEW) {
+ if (inode_state_read_once(inode) & I_NEW) {
if (fattr->cf_cifsattrs & ATTR_DIRECTORY)
set_nlink(inode, 2);
else
@@ -167,12 +168,12 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
- if (!(inode->i_state & I_NEW) &&
+ if (!(inode_state_read_once(inode) & I_NEW) &&
unlikely(inode_wrong_type(inode, fattr->cf_mode))) {
CIFS_I(inode)->time = 0; /* force reval */
return -ESTALE;
}
- if (inode->i_state & I_NEW)
+ if (inode_state_read_once(inode) & I_NEW)
CIFS_I(inode)->netfs.zero_point = fattr->cf_eof;
cifs_revalidate_cache(inode, fattr);
@@ -194,7 +195,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
inode->i_gid = fattr->cf_gid;
/* if dynperm is set, don't clobber existing mode */
- if (inode->i_state & I_NEW ||
+ if (inode_state_read(inode) & I_NEW ||
!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM))
inode->i_mode = fattr->cf_mode;
@@ -236,7 +237,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
if (fattr->cf_flags & CIFS_FATTR_JUNCTION)
inode->i_flags |= S_AUTOMOUNT;
- if (inode->i_state & I_NEW) {
+ if (inode_state_read_once(inode) & I_NEW) {
cifs_set_netfs_context(inode);
cifs_set_ops(inode);
}
@@ -746,6 +747,88 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
#endif
}
+#define POSIX_TYPE_FILE 0
+#define POSIX_TYPE_DIR 1
+#define POSIX_TYPE_SYMLINK 2
+#define POSIX_TYPE_CHARDEV 3
+#define POSIX_TYPE_BLKDEV 4
+#define POSIX_TYPE_FIFO 5
+#define POSIX_TYPE_SOCKET 6
+
+#define POSIX_X_OTH 0000001
+#define POSIX_W_OTH 0000002
+#define POSIX_R_OTH 0000004
+#define POSIX_X_GRP 0000010
+#define POSIX_W_GRP 0000020
+#define POSIX_R_GRP 0000040
+#define POSIX_X_USR 0000100
+#define POSIX_W_USR 0000200
+#define POSIX_R_USR 0000400
+#define POSIX_STICKY 0001000
+#define POSIX_SET_GID 0002000
+#define POSIX_SET_UID 0004000
+
+#define POSIX_OTH_MASK 0000007
+#define POSIX_GRP_MASK 0000070
+#define POSIX_USR_MASK 0000700
+#define POSIX_PERM_MASK 0000777
+#define POSIX_FILETYPE_MASK 0070000
+
+#define POSIX_FILETYPE_SHIFT 12
+
+static u32 wire_perms_to_posix(u32 wire)
+{
+ u32 mode = 0;
+
+ mode |= (wire & POSIX_X_OTH) ? S_IXOTH : 0;
+ mode |= (wire & POSIX_W_OTH) ? S_IWOTH : 0;
+ mode |= (wire & POSIX_R_OTH) ? S_IROTH : 0;
+ mode |= (wire & POSIX_X_GRP) ? S_IXGRP : 0;
+ mode |= (wire & POSIX_W_GRP) ? S_IWGRP : 0;
+ mode |= (wire & POSIX_R_GRP) ? S_IRGRP : 0;
+ mode |= (wire & POSIX_X_USR) ? S_IXUSR : 0;
+ mode |= (wire & POSIX_W_USR) ? S_IWUSR : 0;
+ mode |= (wire & POSIX_R_USR) ? S_IRUSR : 0;
+ mode |= (wire & POSIX_STICKY) ? S_ISVTX : 0;
+ mode |= (wire & POSIX_SET_GID) ? S_ISGID : 0;
+ mode |= (wire & POSIX_SET_UID) ? S_ISUID : 0;
+
+ return mode;
+}
+
+static u32 posix_filetypes[] = {
+ S_IFREG,
+ S_IFDIR,
+ S_IFLNK,
+ S_IFCHR,
+ S_IFBLK,
+ S_IFIFO,
+ S_IFSOCK
+};
+
+static u32 wire_filetype_to_posix(u32 wire_type)
+{
+ if (wire_type >= ARRAY_SIZE(posix_filetypes)) {
+ pr_warn("Unexpected type %u", wire_type);
+ return 0;
+ }
+ return posix_filetypes[wire_type];
+}
+
+umode_t wire_mode_to_posix(u32 wire, bool is_dir)
+{
+ u32 wire_type;
+ u32 mode;
+
+ wire_type = (wire & POSIX_FILETYPE_MASK) >> POSIX_FILETYPE_SHIFT;
+ /* older servers do not set POSIX file type in the mode field in the response */
+ if ((wire_type == 0) && is_dir)
+ mode = wire_perms_to_posix(wire) | S_IFDIR;
+ else
+ mode = (wire_perms_to_posix(wire) | wire_filetype_to_posix(wire_type));
+ return (umode_t)mode;
+}
+
/* Fill a cifs_fattr struct with info from POSIX info struct */
static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
struct cifs_open_info_data *data,
@@ -782,20 +865,14 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
fattr->cf_createtime = le64_to_cpu(info->CreationTime);
fattr->cf_nlink = le32_to_cpu(info->HardLinks);
- fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode);
+ fattr->cf_mode = wire_mode_to_posix(le32_to_cpu(info->Mode),
+ fattr->cf_cifsattrs & ATTR_DIRECTORY);
if (cifs_open_data_reparse(data) &&
cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
goto out_reparse;
- fattr->cf_mode &= ~S_IFMT;
- if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
- fattr->cf_mode |= S_IFDIR;
- fattr->cf_dtype = DT_DIR;
- } else { /* file */
- fattr->cf_mode |= S_IFREG;
- fattr->cf_dtype = DT_REG;
- }
+ fattr->cf_dtype = S_DT(fattr->cf_mode);
out_reparse:
if (S_ISLNK(fattr->cf_mode)) {
@@ -914,7 +991,7 @@ cifs_get_file_info(struct file *filp)
/* TODO: add support to query reparse tag */
data.adjust_tz = false;
if (data.symlink_target) {
- data.symlink = true;
+ data.reparse_point = true;
data.reparse.tag = IO_REPARSE_TAG_SYMLINK;
}
path = build_path_from_dentry(dentry, page);
@@ -1127,17 +1204,45 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
goto out;
}
break;
- case IO_REPARSE_TAG_MOUNT_POINT:
- cifs_create_junction_fattr(fattr, sb);
- rc = 0;
- goto out;
default:
/* Check for cached reparse point data */
if (data->symlink_target || data->reparse.buf) {
rc = 0;
- } else if (iov && server->ops->parse_reparse_point) {
- rc = server->ops->parse_reparse_point(cifs_sb,
- iov, data);
+ } else if (iov && server->ops->get_reparse_point_buffer) {
+ struct reparse_data_buffer *reparse_buf;
+ u32 reparse_len;
+
+ reparse_buf = server->ops->get_reparse_point_buffer(iov, &reparse_len);
+ rc = parse_reparse_point(reparse_buf, reparse_len,
+ cifs_sb, full_path, data);
+ /*
+ * If the reparse point was not handled but it is the
+ * name surrogate which points to directory, then treat
+ * is as a new mount point. Name surrogate reparse point
+ * represents another named entity in the system.
+ */
+ if (rc == -EOPNOTSUPP &&
+ IS_REPARSE_TAG_NAME_SURROGATE(data->reparse.tag) &&
+ (le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY)) {
+ rc = 0;
+ cifs_create_junction_fattr(fattr, sb);
+ goto out;
+ }
+ /*
+ * If the reparse point is unsupported by the Linux SMB
+ * client then let it process by the SMB server. So mask
+ * the -EOPNOTSUPP error code. This will allow Linux SMB
+ * client to send SMB OPEN request to server. If server
+ * does not support this reparse point too then server
+ * will return error during open the path.
+ */
+ if (rc == -EOPNOTSUPP)
+ rc = 0;
+ }
+
+ if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) {
+ bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
+ rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
}
break;
}
@@ -1224,7 +1329,7 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
/* for easier reading */
FILE_ALL_INFO *fi;
FILE_DIRECTORY_INFO *fdi;
- SEARCH_ID_FULL_DIR_INFO *si;
+ FILE_ID_FULL_DIR_INFO *si;
rc = cifs_backup_query_path_info(xid, tcon, sb,
full_path,
@@ -1235,7 +1340,7 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
move_cifs_info_to_smb2(&data->fi, fi);
fdi = (FILE_DIRECTORY_INFO *)fi;
- si = (SEARCH_ID_FULL_DIR_INFO *)fi;
+ si = (FILE_ID_FULL_DIR_INFO *)fi;
cifs_dir_info_to_fattr(fattr, fdi, cifs_sb);
fattr->cf_uniqueid = le64_to_cpu(si->UniqueId);
@@ -1326,7 +1431,7 @@ int cifs_get_inode_info(struct inode **inode,
struct cifs_fattr fattr = {};
int rc;
- if (is_inode_cache_good(*inode)) {
+ if (!data && is_inode_cache_good(*inode)) {
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
return 0;
}
@@ -1425,7 +1530,7 @@ int smb311_posix_get_inode_info(struct inode **inode,
struct cifs_fattr fattr = {};
int rc;
- if (is_inode_cache_good(*inode)) {
+ if (!data && is_inode_cache_good(*inode)) {
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
return 0;
}
@@ -1534,7 +1639,7 @@ retry_iget5_locked:
cifs_fattr_to_inode(inode, fattr, false);
if (sb->s_flags & SB_NOATIME)
inode->i_flags |= S_NOATIME | S_NOCMTIME;
- if (inode->i_state & I_NEW) {
+ if (inode_state_read_once(inode) & I_NEW) {
inode->i_ino = hash;
cifs_fscache_get_inode_cookie(inode);
unlock_new_inode(inode);
@@ -1827,7 +1932,7 @@ cifs_drop_nlink(struct inode *inode)
* but will return the EACCES to the caller. Note that the VFS does not call
* unlink on negative dentries currently.
*/
-int cifs_unlink(struct inode *dir, struct dentry *dentry)
+static int __cifs_unlink(struct inode *dir, struct dentry *dentry, bool sillyrename)
{
int rc = 0;
unsigned int xid;
@@ -1839,14 +1944,23 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct tcon_link *tlink;
struct cifs_tcon *tcon;
+ __u32 dosattr = 0, origattr = 0;
struct TCP_Server_Info *server;
struct iattr *attrs = NULL;
- __u32 dosattr = 0, origattr = 0;
+ bool rehash = false;
cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry);
if (unlikely(cifs_forced_shutdown(cifs_sb)))
- return -EIO;
+ return smb_EIO(smb_eio_trace_forced_shutdown);
+
+ /* Unhash dentry in advance to prevent any concurrent opens */
+ spin_lock(&dentry->d_lock);
+ if (!d_unhashed(dentry)) {
+ __d_drop(dentry);
+ rehash = true;
+ }
+ spin_unlock(&dentry->d_lock);
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
@@ -1870,7 +1984,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
goto unlink_out;
}
- cifs_close_deferred_file_under_dentry(tcon, full_path);
+ netfs_wait_for_outstanding_io(inode);
+ cifs_close_deferred_file_under_dentry(tcon, dentry);
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
@@ -1889,7 +2004,24 @@ retry_std_delete:
goto psx_del_no_retry;
}
- rc = server->ops->unlink(xid, tcon, full_path, cifs_sb, dentry);
+ /* For SMB2+, if the file is open, we always perform a silly rename.
+ *
+ * We check for d_count() right after calling
+ * cifs_close_deferred_file_under_dentry() to make sure that the
+ * dentry's refcount gets dropped in case the file had any deferred
+ * close.
+ */
+ if (!sillyrename && server->vals->protocol_id > SMB10_PROT_ID) {
+ spin_lock(&dentry->d_lock);
+ if (d_count(dentry) > 1)
+ sillyrename = true;
+ spin_unlock(&dentry->d_lock);
+ }
+
+ if (sillyrename)
+ rc = -EBUSY;
+ else
+ rc = server->ops->unlink(xid, tcon, full_path, cifs_sb, dentry);
psx_del_no_retry:
if (!rc) {
@@ -1898,7 +2030,8 @@ psx_del_no_retry:
cifs_drop_nlink(inode);
}
} else if (rc == -ENOENT) {
- d_drop(dentry);
+ if (simple_positive(dentry))
+ d_delete(dentry);
} else if (rc == -EBUSY) {
if (server->ops->rename_pending_delete) {
rc = server->ops->rename_pending_delete(full_path,
@@ -1951,9 +2084,16 @@ unlink_out:
kfree(attrs);
free_xid(xid);
cifs_put_tlink(tlink);
+ if (rehash)
+ d_rehash(dentry);
return rc;
}
+int cifs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ return __cifs_unlink(dir, dentry, false);
+}
+
static int
cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
const char *full_path, struct cifs_sb_info *cifs_sb,
@@ -2111,8 +2251,8 @@ posix_mkdir_get_info:
}
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
-int cifs_mkdir(struct mnt_idmap *idmap, struct inode *inode,
- struct dentry *direntry, umode_t mode)
+struct dentry *cifs_mkdir(struct mnt_idmap *idmap, struct inode *inode,
+ struct dentry *direntry, umode_t mode)
{
int rc = 0;
unsigned int xid;
@@ -2128,10 +2268,10 @@ int cifs_mkdir(struct mnt_idmap *idmap, struct inode *inode,
cifs_sb = CIFS_SB(inode->i_sb);
if (unlikely(cifs_forced_shutdown(cifs_sb)))
- return -EIO;
+ return ERR_PTR(smb_EIO(smb_eio_trace_forced_shutdown));
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
- return PTR_ERR(tlink);
+ return ERR_CAST(tlink);
tcon = tlink_tcon(tlink);
xid = get_xid();
@@ -2187,7 +2327,7 @@ mkdir_out:
free_dentry_path(page);
free_xid(xid);
cifs_put_tlink(tlink);
- return rc;
+ return ERR_PTR(rc);
}
int cifs_rmdir(struct inode *inode, struct dentry *direntry)
@@ -2214,7 +2354,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
cifs_sb = CIFS_SB(inode->i_sb);
if (unlikely(cifs_forced_shutdown(cifs_sb))) {
- rc = -EIO;
+ rc = smb_EIO(smb_eio_trace_forced_shutdown);
goto rmdir_exit;
}
@@ -2241,14 +2381,16 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb);
cifs_put_tlink(tlink);
+ cifsInode = CIFS_I(d_inode(direntry));
+
if (!rc) {
+ set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags);
spin_lock(&d_inode(direntry)->i_lock);
i_size_write(d_inode(direntry), 0);
clear_nlink(d_inode(direntry));
spin_unlock(&d_inode(direntry)->i_lock);
}
- cifsInode = CIFS_I(d_inode(direntry));
/* force revalidate to go get info when needed */
cifsInode->time = 0;
@@ -2290,8 +2432,10 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
- if (!server->ops->rename)
- return -ENOSYS;
+ if (!server->ops->rename) {
+ rc = -ENOSYS;
+ goto do_rename_exit;
+ }
/* try path-based rename first */
rc = server->ops->rename(xid, tcon, from_dentry,
@@ -2314,6 +2458,13 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
if (to_dentry->d_parent != from_dentry->d_parent)
goto do_rename_exit;
+ /*
+ * CIFSSMBRenameOpenFile() uses SMB_SET_FILE_RENAME_INFORMATION
+ * which is SMB PASSTHROUGH level.
+ */
+ if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU))
+ goto do_rename_exit;
+
oparms = (struct cifs_open_parms) {
.tcon = tcon,
.cifs_sb = cifs_sb,
@@ -2346,10 +2497,12 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
struct dentry *target_dentry, unsigned int flags)
{
const char *from_name, *to_name;
+ struct TCP_Server_Info *server;
void *page1, *page2;
struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifs_tcon *tcon;
+ bool rehash = false;
unsigned int xid;
int rc, tmprc;
int retry_count = 0;
@@ -2363,12 +2516,24 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
cifs_sb = CIFS_SB(source_dir->i_sb);
if (unlikely(cifs_forced_shutdown(cifs_sb)))
- return -EIO;
+ return smb_EIO(smb_eio_trace_forced_shutdown);
+
+ /*
+ * Prevent any concurrent opens on the target by unhashing the dentry.
+ * VFS already unhashes the target when renaming directories.
+ */
+ if (d_is_positive(target_dentry) && !d_is_dir(target_dentry)) {
+ if (!d_unhashed(target_dentry)) {
+ d_drop(target_dentry);
+ rehash = true;
+ }
+ }
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
page1 = alloc_dentry_path();
page2 = alloc_dentry_path();
@@ -2386,9 +2551,11 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
goto cifs_rename_exit;
}
- cifs_close_deferred_file_under_dentry(tcon, from_name);
- if (d_inode(target_dentry) != NULL)
- cifs_close_deferred_file_under_dentry(tcon, to_name);
+ cifs_close_deferred_file_under_dentry(tcon, source_dentry);
+ if (d_inode(target_dentry) != NULL) {
+ netfs_wait_for_outstanding_io(d_inode(target_dentry));
+ cifs_close_deferred_file_under_dentry(tcon, target_dentry);
+ }
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
to_name);
@@ -2404,6 +2571,8 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
}
}
+ if (!rc)
+ rehash = false;
/*
* No-replace is the natural behavior for CIFS, so skip unlink hacks.
*/
@@ -2451,23 +2620,61 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
unlink_target:
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
-
- /* Try unlinking the target dentry if it's not negative */
- if (d_really_is_positive(target_dentry) && (rc == -EACCES || rc == -EEXIST)) {
- if (d_is_dir(target_dentry))
- tmprc = cifs_rmdir(target_dir, target_dentry);
- else
- tmprc = cifs_unlink(target_dir, target_dentry);
- if (tmprc)
- goto cifs_rename_exit;
- rc = cifs_do_rename(xid, source_dentry, from_name,
- target_dentry, to_name);
+ if (d_really_is_positive(target_dentry)) {
+ if (!rc) {
+ struct inode *inode = d_inode(target_dentry);
+ /*
+ * Samba and ksmbd servers allow renaming a target
+ * directory that is open, so make sure to update
+ * ->i_nlink and then mark it as delete pending.
+ */
+ if (S_ISDIR(inode->i_mode)) {
+ drop_cached_dir_by_name(xid, tcon, to_name, cifs_sb);
+ spin_lock(&inode->i_lock);
+ i_size_write(inode, 0);
+ clear_nlink(inode);
+ spin_unlock(&inode->i_lock);
+ set_bit(CIFS_INO_DELETE_PENDING, &CIFS_I(inode)->flags);
+ CIFS_I(inode)->time = 0; /* force reval */
+ inode_set_ctime_current(inode);
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+ }
+ } else if (rc == -EACCES || rc == -EEXIST) {
+ /*
+ * Rename failed, possibly due to a busy target.
+ * Retry it by unliking the target first.
+ */
+ if (d_is_dir(target_dentry)) {
+ tmprc = cifs_rmdir(target_dir, target_dentry);
+ } else {
+ tmprc = __cifs_unlink(target_dir, target_dentry,
+ server->vals->protocol_id > SMB10_PROT_ID);
+ }
+ if (tmprc) {
+ /*
+ * Some servers will return STATUS_ACCESS_DENIED
+ * or STATUS_DIRECTORY_NOT_EMPTY when failing to
+ * rename a non-empty directory. Make sure to
+ * propagate the appropriate error back to
+ * userspace.
+ */
+ if (tmprc == -EEXIST || tmprc == -ENOTEMPTY)
+ rc = tmprc;
+ goto cifs_rename_exit;
+ }
+ rc = cifs_do_rename(xid, source_dentry, from_name,
+ target_dentry, to_name);
+ if (!rc)
+ rehash = false;
+ }
}
/* force revalidate to go get info when needed */
CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0;
cifs_rename_exit:
+ if (rehash)
+ d_rehash(target_dentry);
kfree(info_buf_source);
free_dentry_path(page2);
free_dentry_path(page1);
@@ -2485,6 +2692,8 @@ cifs_dentry_needs_reval(struct dentry *dentry)
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
struct cached_fid *cfid = NULL;
+ if (test_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags))
+ return false;
if (cifs_i->time == 0)
return true;
@@ -2495,13 +2704,10 @@ cifs_dentry_needs_reval(struct dentry *dentry)
return true;
if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) {
- spin_lock(&cfid->fid_lock);
- if (cfid->time && cifs_i->time > cfid->time) {
- spin_unlock(&cfid->fid_lock);
+ if (cifs_i->time > cfid->time) {
close_cached_dir(cfid);
return false;
}
- spin_unlock(&cfid->fid_lock);
close_cached_dir(cfid);
}
/*
@@ -2638,7 +2844,7 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry)
}
cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld\n",
- full_path, inode, inode->i_count.counter,
+ full_path, inode, icount_read(inode),
dentry, cifs_get_time(dentry), jiffies);
again:
@@ -2695,7 +2901,7 @@ int cifs_getattr(struct mnt_idmap *idmap, const struct path *path,
int rc;
if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
- return -EIO;
+ return smb_EIO(smb_eio_trace_forced_shutdown);
/*
* We need to be sure that all dirty pages are written and the server
@@ -2770,7 +2976,7 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
int rc;
if (unlikely(cifs_forced_shutdown(cifs_sb)))
- return -EIO;
+ return smb_EIO(smb_eio_trace_forced_shutdown);
/*
* We need to be sure that all dirty pages are written as they
@@ -2799,47 +3005,27 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
return -EOPNOTSUPP;
}
-int cifs_truncate_page(struct address_space *mapping, loff_t from)
-{
- pgoff_t index = from >> PAGE_SHIFT;
- unsigned offset = from & (PAGE_SIZE - 1);
- struct page *page;
- int rc = 0;
-
- page = grab_cache_page(mapping, index);
- if (!page)
- return -ENOMEM;
-
- zero_user_segment(page, offset, PAGE_SIZE);
- unlock_page(page);
- put_page(page);
- return rc;
-}
-
void cifs_setsize(struct inode *inode, loff_t offset)
{
- struct cifsInodeInfo *cifs_i = CIFS_I(inode);
-
spin_lock(&inode->i_lock);
i_size_write(inode, offset);
spin_unlock(&inode->i_lock);
-
- /* Cached inode must be refreshed on truncate */
- cifs_i->time = 0;
+ inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
truncate_pagecache(inode, offset);
+ netfs_wait_for_outstanding_io(inode);
}
-static int
-cifs_set_file_size(struct inode *inode, struct iattr *attrs,
- unsigned int xid, const char *full_path, struct dentry *dentry)
+int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
+ const char *full_path, struct cifsFileInfo *open_file,
+ loff_t size)
{
- int rc;
- struct cifsFileInfo *open_file;
- struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ struct inode *inode = d_inode(dentry);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsInodeInfo *cifsInode = CIFS_I(inode);
struct tcon_link *tlink = NULL;
struct cifs_tcon *tcon = NULL;
struct TCP_Server_Info *server;
+ int rc = -EINVAL;
/*
* To avoid spurious oplock breaks from server, in the case of
@@ -2850,19 +3036,25 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
* writebehind data than the SMB timeout for the SetPathInfo
* request would allow
*/
- open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY);
- if (open_file) {
+ if (open_file && (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE)) {
tcon = tlink_tcon(open_file->tlink);
server = tcon->ses->server;
- if (server->ops->set_file_size)
- rc = server->ops->set_file_size(xid, tcon, open_file,
- attrs->ia_size, false);
- else
- rc = -ENOSYS;
- cifsFileInfo_put(open_file);
- cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", rc);
- } else
- rc = -EINVAL;
+ rc = server->ops->set_file_size(xid, tcon,
+ open_file,
+ size, false);
+ cifs_dbg(FYI, "%s: set_file_size: rc = %d\n", __func__, rc);
+ } else {
+ open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY);
+ if (open_file) {
+ tcon = tlink_tcon(open_file->tlink);
+ server = tcon->ses->server;
+ rc = server->ops->set_file_size(xid, tcon,
+ open_file,
+ size, false);
+ cifs_dbg(FYI, "%s: set_file_size: rc = %d\n", __func__, rc);
+ cifsFileInfo_put(open_file);
+ }
+ }
if (!rc)
goto set_size_out;
@@ -2880,20 +3072,15 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
* valid, writeable file handle for it was found or because there was
* an error setting it by handle.
*/
- if (server->ops->set_path_size)
- rc = server->ops->set_path_size(xid, tcon, full_path,
- attrs->ia_size, cifs_sb, false, dentry);
- else
- rc = -ENOSYS;
- cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc);
-
- if (tlink)
- cifs_put_tlink(tlink);
+ rc = server->ops->set_path_size(xid, tcon, full_path, size,
+ cifs_sb, false, dentry);
+ cifs_dbg(FYI, "%s: SetEOF by path (setattrs) rc = %d\n", __func__, rc);
+ cifs_put_tlink(tlink);
set_size_out:
if (rc == 0) {
- netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
- cifs_setsize(inode, attrs->ia_size);
+ netfs_resize_file(&cifsInode->netfs, size, true);
+ cifs_setsize(inode, size);
/*
* i_blocks is not related to (i_size / i_blksize), but instead
* 512 byte (2**9) size is required for calculating num blocks.
@@ -2901,17 +3088,7 @@ set_size_out:
* this is best estimate we have for blocks allocated for a file
* Number of blocks must be rounded up so size 1 is not 0 blocks
*/
- inode->i_blocks = (512 - 1 + attrs->ia_size) >> 9;
-
- /*
- * The man page of truncate says if the size changed,
- * then the st_ctime and st_mtime fields for the file
- * are updated.
- */
- attrs->ia_ctime = attrs->ia_mtime = current_time(inode);
- attrs->ia_valid |= ATTR_CTIME | ATTR_MTIME;
-
- cifs_truncate_page(inode->i_mapping, inode->i_size);
+ inode->i_blocks = (512 - 1 + size) >> 9;
}
return rc;
@@ -2931,7 +3108,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
struct tcon_link *tlink;
struct cifs_tcon *pTcon;
struct cifs_unix_set_info_args *args = NULL;
- struct cifsFileInfo *open_file;
+ struct cifsFileInfo *open_file = NULL;
cifs_dbg(FYI, "setattr_unix on file %pd attrs->ia_valid=0x%x\n",
direntry, attrs->ia_valid);
@@ -2945,6 +3122,9 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
if (rc < 0)
goto out;
+ if (attrs->ia_valid & ATTR_FILE)
+ open_file = attrs->ia_file->private_data;
+
full_path = build_path_from_dentry(direntry, page);
if (IS_ERR(full_path)) {
rc = PTR_ERR(full_path);
@@ -2972,9 +3152,16 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
rc = 0;
if (attrs->ia_valid & ATTR_SIZE) {
- rc = cifs_set_file_size(inode, attrs, xid, full_path, direntry);
+ rc = cifs_file_set_size(xid, direntry, full_path,
+ open_file, attrs->ia_size);
if (rc != 0)
goto out;
+ /*
+ * Avoid setting timestamps on the server for ftruncate(2) to
+ * prevent it from disabling automatic timestamp updates as per
+ * MS-FSA 2.1.4.17.
+ */
+ attrs->ia_valid &= ~(ATTR_CTIME | ATTR_MTIME);
}
/* skip mode change if it's just for clearing setuid/setgid */
@@ -3019,14 +3206,24 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
args->ctime = NO_CHANGE_64;
args->device = 0;
- open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY);
- if (open_file) {
- u16 nfid = open_file->fid.netfid;
- u32 npid = open_file->pid;
+ rc = -EINVAL;
+ if (open_file && (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE)) {
pTcon = tlink_tcon(open_file->tlink);
- rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid);
- cifsFileInfo_put(open_file);
+ rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args,
+ open_file->fid.netfid,
+ open_file->pid);
} else {
+ open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY);
+ if (open_file) {
+ pTcon = tlink_tcon(open_file->tlink);
+ rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args,
+ open_file->fid.netfid,
+ open_file->pid);
+ cifsFileInfo_put(open_file);
+ }
+ }
+
+ if (rc) {
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink);
@@ -3034,8 +3231,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
}
pTcon = tlink_tcon(tlink);
rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args,
- cifs_sb->local_nls,
- cifs_remap(cifs_sb));
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
cifs_put_tlink(tlink);
}
@@ -3077,8 +3274,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
struct inode *inode = d_inode(direntry);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
- struct cifsFileInfo *wfile;
- struct cifs_tcon *tcon;
+ struct cifsFileInfo *cfile = NULL;
const char *full_path;
void *page = alloc_dentry_path();
int rc = -EACCES;
@@ -3098,6 +3294,9 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
if (rc < 0)
goto cifs_setattr_exit;
+ if (attrs->ia_valid & ATTR_FILE)
+ cfile = attrs->ia_file->private_data;
+
full_path = build_path_from_dentry(direntry, page);
if (IS_ERR(full_path)) {
rc = PTR_ERR(full_path);
@@ -3124,25 +3323,23 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
rc = 0;
- if ((attrs->ia_valid & ATTR_MTIME) &&
- !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
- rc = cifs_get_writable_file(cifsInode, FIND_WR_ANY, &wfile);
- if (!rc) {
- tcon = tlink_tcon(wfile->tlink);
- rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid);
- cifsFileInfo_put(wfile);
- if (rc)
- goto cifs_setattr_exit;
- } else if (rc != -EBADF)
+ if (attrs->ia_valid & ATTR_MTIME) {
+ rc = cifs_file_flush(xid, inode, cfile);
+ if (rc)
goto cifs_setattr_exit;
- else
- rc = 0;
}
if (attrs->ia_valid & ATTR_SIZE) {
- rc = cifs_set_file_size(inode, attrs, xid, full_path, direntry);
+ rc = cifs_file_set_size(xid, direntry, full_path,
+ cfile, attrs->ia_size);
if (rc != 0)
goto cifs_setattr_exit;
+ /*
+ * Avoid setting timestamps on the server for ftruncate(2) to
+ * prevent it from disabling automatic timestamp updates as per
+ * MS-FSA 2.1.4.17.
+ */
+ attrs->ia_valid &= ~(ATTR_CTIME | ATTR_MTIME);
}
if (attrs->ia_valid & ATTR_UID)
@@ -3271,7 +3468,14 @@ cifs_setattr(struct mnt_idmap *idmap, struct dentry *direntry,
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
if (unlikely(cifs_forced_shutdown(cifs_sb)))
- return -EIO;
+ return smb_EIO(smb_eio_trace_forced_shutdown);
+ /*
+ * Avoid setting [cm]time with O_TRUNC to prevent the server from
+ * disabling automatic timestamp updates as specified in
+ * MS-FSA 2.1.4.17.
+ */
+ if (attrs->ia_valid & ATTR_OPEN)
+ return 0;
do {
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c
index 56439da4f119..0a9935ce05a5 100644
--- a/fs/smb/client/ioctl.c
+++ b/fs/smb/client/ioctl.c
@@ -506,7 +506,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
le16_to_cpu(tcon->ses->server->cipher_type);
pkey_inf.Suid = tcon->ses->Suid;
memcpy(pkey_inf.auth_key, tcon->ses->auth_key.response,
- 16 /* SMB2_NTLMV2_SESSKEY_SIZE */);
+ SMB2_NTLMV2_SESSKEY_SIZE);
memcpy(pkey_inf.smb3decryptionkey,
tcon->ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
memcpy(pkey_inf.smb3encryptionkey,
diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c
index 47ddeb7fa111..fdfdc9a3abdd 100644
--- a/fs/smb/client/link.c
+++ b/fs/smb/client/link.c
@@ -5,6 +5,7 @@
* Author(s): Steve French (sfrench@us.ibm.com)
*
*/
+#include <crypto/md5.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/slab.h>
@@ -18,6 +19,8 @@
#include "cifs_unicode.h"
#include "smb2proto.h"
#include "cifs_ioctl.h"
+#include "fs_context.h"
+#include "reparse.h"
/*
* M-F Symlink Functions - Begin
@@ -35,23 +38,6 @@
#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) md5_hash
static int
-symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
-{
- int rc;
- struct shash_desc *md5 = NULL;
-
- rc = cifs_alloc_hash("md5", &md5);
- if (rc)
- return rc;
-
- rc = crypto_shash_digest(md5, link_str, link_len, md5_hash);
- if (rc)
- cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
- cifs_free_hash(&md5);
- return rc;
-}
-
-static int
parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len,
char **_link_str)
{
@@ -75,11 +61,7 @@ parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len,
if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
return -EINVAL;
- rc = symlink_hash(link_len, link_str, md5_hash);
- if (rc) {
- cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);
- return rc;
- }
+ md5(link_str, link_len, md5_hash);
scnprintf(md5_str2, sizeof(md5_str2),
CIFS_MF_SYMLINK_MD5_FORMAT,
@@ -101,7 +83,6 @@ parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len,
static int
format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)
{
- int rc;
unsigned int link_len;
unsigned int ofs;
u8 md5_hash[16];
@@ -114,11 +95,7 @@ format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)
if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
return -ENAMETOOLONG;
- rc = symlink_hash(link_len, link_str, md5_hash);
- if (rc) {
- cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);
- return rc;
- }
+ md5(link_str, link_len, md5_hash);
scnprintf(buf, buf_len,
CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
@@ -183,7 +160,8 @@ create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
goto out;
if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE)
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_symlink_file_size,
+ bytes_written, CIFS_MF_SYMLINK_FILE_SIZE);
out:
kfree(buf);
return rc;
@@ -257,7 +235,7 @@ cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_open_parms oparms;
struct cifs_io_parms io_parms = {0};
int buf_type = CIFS_NO_BUFFER;
- FILE_ALL_INFO file_info;
+ struct cifs_open_info_data query_data;
oparms = (struct cifs_open_parms) {
.tcon = tcon,
@@ -269,11 +247,11 @@ cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
.fid = &fid,
};
- rc = CIFS_open(xid, &oparms, &oplock, &file_info);
+ rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &query_data);
if (rc)
return rc;
- if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
+ if (query_data.fi.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
rc = -ENOENT;
/* it's not a symlink */
goto out;
@@ -312,7 +290,7 @@ cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
.fid = &fid,
};
- rc = CIFS_open(xid, &oparms, &oplock, NULL);
+ rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
if (rc)
return rc;
@@ -447,7 +425,8 @@ smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
/* Make sure we wrote all of the symlink data */
if ((rc == 0) && (*pbytes_written != CIFS_MF_SYMLINK_FILE_SIZE))
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_short_symlink_write,
+ *pbytes_written, CIFS_MF_SYMLINK_FILE_SIZE);
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
@@ -474,7 +453,7 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
struct cifsInodeInfo *cifsInode;
if (unlikely(cifs_forced_shutdown(cifs_sb)))
- return -EIO;
+ return smb_EIO(smb_eio_trace_forced_shutdown);
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
@@ -569,7 +548,6 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
int rc = -EOPNOTSUPP;
unsigned int xid;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
- struct TCP_Server_Info *server;
struct tcon_link *tlink;
struct cifs_tcon *pTcon;
const char *full_path;
@@ -577,7 +555,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
struct inode *newinode = NULL;
if (unlikely(cifs_forced_shutdown(cifs_sb)))
- return -EIO;
+ return smb_EIO(smb_eio_trace_forced_shutdown);
page = alloc_dentry_path();
if (!page)
@@ -592,7 +570,6 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
goto symlink_exit;
}
pTcon = tlink_tcon(tlink);
- server = cifs_pick_channel(pTcon->ses);
full_path = build_path_from_dentry(direntry, page);
if (IS_ERR(full_path)) {
@@ -604,22 +581,45 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
cifs_dbg(FYI, "symname is %s\n", symname);
/* BB what if DFS and this volume is on different share? BB */
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
- rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
- } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
- rc = __cifs_sfu_make_node(xid, inode, direntry, pTcon,
- full_path, S_IFLNK, 0, symname);
+ rc = -EOPNOTSUPP;
+ switch (cifs_symlink_type(cifs_sb)) {
+ case CIFS_SYMLINK_TYPE_UNIX:
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
- } else if (pTcon->unix_ext) {
- rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
- cifs_sb->local_nls,
- cifs_remap(cifs_sb));
+ if (pTcon->unix_ext) {
+ rc = CIFSUnixCreateSymLink(xid, pTcon, full_path,
+ symname,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+ }
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
- } else if (server->ops->create_reparse_symlink) {
- rc = server->ops->create_reparse_symlink(xid, inode, direntry,
- pTcon, full_path,
- symname);
- goto symlink_exit;
+ break;
+
+ case CIFS_SYMLINK_TYPE_MFSYMLINKS:
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
+ rc = create_mf_symlink(xid, pTcon, cifs_sb,
+ full_path, symname);
+ }
+ break;
+
+ case CIFS_SYMLINK_TYPE_SFU:
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+ rc = __cifs_sfu_make_node(xid, inode, direntry, pTcon,
+ full_path, S_IFLNK,
+ 0, symname);
+ }
+ break;
+
+ case CIFS_SYMLINK_TYPE_NATIVE:
+ case CIFS_SYMLINK_TYPE_NFS:
+ case CIFS_SYMLINK_TYPE_WSL:
+ if (CIFS_REPARSE_SUPPORT(pTcon)) {
+ rc = create_reparse_symlink(xid, inode, direntry, pTcon,
+ full_path, symname);
+ goto symlink_exit;
+ }
+ break;
+ default:
+ break;
}
if (rc == 0) {
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index 4373dd64b66d..9529fa385938 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -18,6 +18,7 @@
#include "nterr.h"
#include "cifs_unicode.h"
#include "smb2pdu.h"
+#include "smb2proto.h"
#include "cifsfs.h"
#ifdef CONFIG_CIFS_DFS_UPCALL
#include "dns_resolve.h"
@@ -101,6 +102,7 @@ sesInfoFree(struct cifs_ses *buf_to_free)
kfree_sensitive(buf_to_free->password2);
kfree(buf_to_free->user_name);
kfree(buf_to_free->domainName);
+ kfree(buf_to_free->dns_dom);
kfree_sensitive(buf_to_free->auth_key.response);
spin_lock(&buf_to_free->iface_lock);
list_for_each_entry_safe(iface, niface, &buf_to_free->iface_list,
@@ -136,8 +138,10 @@ tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace)
spin_lock_init(&ret_buf->tc_lock);
INIT_LIST_HEAD(&ret_buf->openFileList);
INIT_LIST_HEAD(&ret_buf->tcon_list);
+ INIT_LIST_HEAD(&ret_buf->cifs_sb_list);
spin_lock_init(&ret_buf->open_file_lock);
spin_lock_init(&ret_buf->stat_lock);
+ spin_lock_init(&ret_buf->sb_list_lock);
atomic_set(&ret_buf->num_local_opens, 0);
atomic_set(&ret_buf->num_remote_opens, 0);
ret_buf->stats_from_time = ktime_get_real_seconds();
@@ -148,6 +152,12 @@ tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace)
#ifdef CONFIG_CIFS_DFS_UPCALL
INIT_LIST_HEAD(&ret_buf->dfs_ses_list);
#endif
+ INIT_LIST_HEAD(&ret_buf->pending_opens);
+ INIT_DELAYED_WORK(&ret_buf->query_interfaces,
+ smb2_query_server_interfaces);
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ INIT_DELAYED_WORK(&ret_buf->dfs_cache_work, dfs_cache_refresh);
+#endif
return ret_buf;
}
@@ -255,19 +265,18 @@ free_rsp_buf(int resp_buftype, void *rsp)
/* NB: MID can not be set if treeCon not passed in, in that
case it is responsibility of caller to set the mid */
-void
-header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
+unsigned int
+header_assemble(struct smb_hdr *buffer, char smb_command,
const struct cifs_tcon *treeCon, int word_count
/* length of fixed section (word count) in two byte units */)
{
+ unsigned int in_len;
char *temp = (char *) buffer;
memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */
- buffer->smb_buf_length = cpu_to_be32(
- (2 * word_count) + sizeof(struct smb_hdr) -
- 4 /* RFC 1001 length field does not count */ +
- 2 /* for bcc field itself */) ;
+ in_len = (2 * word_count) + sizeof(struct smb_hdr) +
+ 2 /* for bcc field itself */;
buffer->Protocol[0] = 0xFF;
buffer->Protocol[1] = 'S';
@@ -302,14 +311,14 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
/* endian conversion of flags is now done just before sending */
buffer->WordCount = (char) word_count;
- return;
+ return in_len;
}
static int
check_smb_hdr(struct smb_hdr *smb)
{
/* does it have the right SMB "signature" ? */
- if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) {
+ if (*(__le32 *) smb->Protocol != SMB1_PROTO_NUMBER) {
cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n",
*(unsigned int *)smb->Protocol);
return 1;
@@ -323,16 +332,25 @@ check_smb_hdr(struct smb_hdr *smb)
if (smb->Command == SMB_COM_LOCKING_ANDX)
return 0;
+ /*
+ * Windows NT server returns error resposne (e.g. STATUS_DELETE_PENDING
+ * or STATUS_OBJECT_NAME_NOT_FOUND or ERRDOS/ERRbadfile or any other)
+ * for some TRANS2 requests without the RESPONSE flag set in header.
+ */
+ if (smb->Command == SMB_COM_TRANSACTION2 && smb->Status.CifsError != 0)
+ return 0;
+
cifs_dbg(VFS, "Server sent request, not response. mid=%u\n",
get_mid(smb));
return 1;
}
int
-checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server)
+checkSMB(char *buf, unsigned int pdu_len, unsigned int total_read,
+ struct TCP_Server_Info *server)
{
struct smb_hdr *smb = (struct smb_hdr *)buf;
- __u32 rfclen = be32_to_cpu(smb->smb_buf_length);
+ __u32 rfclen = pdu_len;
__u32 clc_len; /* calculated length */
cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n",
total_read, rfclen);
@@ -362,42 +380,47 @@ checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server)
return 0;
}
cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n");
+ return smb_EIO1(smb_eio_trace_rx_inv_bcc, tmp[sizeof(struct smb_hdr)]);
} else {
cifs_dbg(VFS, "Length less than smb header size\n");
+ return smb_EIO2(smb_eio_trace_rx_too_short,
+ total_read, smb->WordCount);
}
- return -EIO;
} else if (total_read < sizeof(*smb) + 2 * smb->WordCount) {
cifs_dbg(VFS, "%s: can't read BCC due to invalid WordCount(%u)\n",
__func__, smb->WordCount);
- return -EIO;
+ return smb_EIO2(smb_eio_trace_rx_check_rsp,
+ total_read, 2 + sizeof(struct smb_hdr));
}
/* otherwise, there is enough to get to the BCC */
if (check_smb_hdr(smb))
- return -EIO;
+ return smb_EIO1(smb_eio_trace_rx_rfc1002_magic, *(u32 *)smb->Protocol);
clc_len = smbCalcSize(smb);
- if (4 + rfclen != total_read) {
- cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n",
- rfclen);
- return -EIO;
+ if (rfclen != total_read) {
+ cifs_dbg(VFS, "Length read does not match RFC1001 length %d/%d\n",
+ rfclen, total_read);
+ return smb_EIO2(smb_eio_trace_rx_check_rsp,
+ total_read, rfclen);
}
- if (4 + rfclen != clc_len) {
+ if (rfclen != clc_len) {
__u16 mid = get_mid(smb);
/* check if bcc wrapped around for large read responses */
if ((rfclen > 64 * 1024) && (rfclen > clc_len)) {
/* check if lengths match mod 64K */
- if (((4 + rfclen) & 0xFFFF) == (clc_len & 0xFFFF))
+ if (((rfclen) & 0xFFFF) == (clc_len & 0xFFFF))
return 0; /* bcc wrapped */
}
cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n",
- clc_len, 4 + rfclen, mid);
+ clc_len, rfclen, mid);
- if (4 + rfclen < clc_len) {
+ if (rfclen < clc_len) {
cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n",
rfclen, mid);
- return -EIO;
+ return smb_EIO2(smb_eio_trace_rx_calc_len_too_big,
+ rfclen, clc_len);
} else if (rfclen > clc_len + 512) {
/*
* Some servers (Windows XP in particular) send more
@@ -410,7 +433,8 @@ checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server)
*/
cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n",
rfclen, mid);
- return -EIO;
+ return smb_EIO2(smb_eio_trace_rx_overlong,
+ rfclen, clc_len + 512);
}
}
return 0;
@@ -434,7 +458,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
(struct smb_com_transaction_change_notify_rsp *)buf;
struct file_notify_information *pnotify;
__u32 data_offset = 0;
- size_t len = srv->total_read - sizeof(pSMBr->hdr.smb_buf_length);
+ size_t len = srv->total_read - srv->pdu_size;
if (get_bcc(buf) > sizeof(struct file_notify_information)) {
data_offset = le32_to_cpu(pSMBr->DataOffset);
@@ -815,33 +839,28 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
kfree(tmp_list);
}
}
-void
-cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)
+
+void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
+ struct dentry *dentry)
{
- struct cifsFileInfo *cfile;
struct file_list *tmp_list, *tmp_next_list;
- void *page;
- const char *full_path;
+ struct cifsFileInfo *cfile;
LIST_HEAD(file_head);
- page = alloc_dentry_path();
spin_lock(&tcon->open_file_lock);
list_for_each_entry(cfile, &tcon->openFileList, tlist) {
- full_path = build_path_from_dentry(cfile->dentry, page);
- if (strstr(full_path, path)) {
- if (delayed_work_pending(&cfile->deferred)) {
- if (cancel_delayed_work(&cfile->deferred)) {
- spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
- cifs_del_deferred_close(cfile);
- spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
-
- tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
- if (tmp_list == NULL)
- break;
- tmp_list->cfile = cfile;
- list_add_tail(&tmp_list->list, &file_head);
- }
- }
+ if ((cfile->dentry == dentry) &&
+ delayed_work_pending(&cfile->deferred) &&
+ cancel_delayed_work(&cfile->deferred)) {
+ spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
+ cifs_del_deferred_close(cfile);
+ spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
+
+ tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
+ if (tmp_list == NULL)
+ break;
+ tmp_list->cfile = cfile;
+ list_add_tail(&tmp_list->list, &file_head);
}
}
spin_unlock(&tcon->open_file_lock);
@@ -851,7 +870,6 @@ cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)
list_del(&tmp_list->list);
kfree(tmp_list);
}
- free_dentry_path(page);
}
/*
@@ -905,11 +923,28 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
char *data_end;
struct dfs_referral_level_3 *ref;
+ if (rsp_size < sizeof(*rsp)) {
+ cifs_dbg(VFS | ONCE,
+ "%s: header is malformed (size is %u, must be %zu)\n",
+ __func__, rsp_size, sizeof(*rsp));
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
+
*num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals);
if (*num_of_nodes < 1) {
- cifs_dbg(VFS, "num_referrals: must be at least > 0, but we get num_referrals = %d\n",
- *num_of_nodes);
+ cifs_dbg(VFS | ONCE, "%s: [path=%s] num_referrals must be at least > 0, but we got %d\n",
+ __func__, searchName, *num_of_nodes);
+ rc = -ENOENT;
+ goto parse_DFS_referrals_exit;
+ }
+
+ if (sizeof(*rsp) + *num_of_nodes * sizeof(REFERRAL3) > rsp_size) {
+ cifs_dbg(VFS | ONCE,
+ "%s: malformed buffer (size is %u, must be at least %zu)\n",
+ __func__, rsp_size,
+ sizeof(*rsp) + *num_of_nodes * sizeof(REFERRAL3));
rc = -EINVAL;
goto parse_DFS_referrals_exit;
}
@@ -1171,33 +1206,25 @@ void cifs_put_tcp_super(struct super_block *sb)
#ifdef CONFIG_CIFS_DFS_UPCALL
int match_target_ip(struct TCP_Server_Info *server,
- const char *share, size_t share_len,
+ const char *host, size_t hostlen,
bool *result)
{
- int rc;
- char *target;
struct sockaddr_storage ss;
+ int rc;
- *result = false;
-
- target = kzalloc(share_len + 3, GFP_KERNEL);
- if (!target)
- return -ENOMEM;
-
- scnprintf(target, share_len + 3, "\\\\%.*s", (int)share_len, share);
-
- cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2);
+ cifs_dbg(FYI, "%s: hostname=%.*s\n", __func__, (int)hostlen, host);
- rc = dns_resolve_server_name_to_ip(target, (struct sockaddr *)&ss, NULL);
- kfree(target);
+ *result = false;
+ rc = dns_resolve_name(server->dns_dom, host, hostlen,
+ (struct sockaddr *)&ss);
if (rc < 0)
return rc;
spin_lock(&server->srv_lock);
*result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, (struct sockaddr *)&ss);
spin_unlock(&server->srv_lock);
- cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result);
+ cifs_dbg(FYI, "%s: ip addresses matched: %s\n", __func__, str_yes_no(*result));
return 0;
}
diff --git a/fs/smb/client/namespace.c b/fs/smb/client/namespace.c
index 0f788031b740..52a520349cb7 100644
--- a/fs/smb/client/namespace.c
+++ b/fs/smb/client/namespace.c
@@ -146,6 +146,9 @@ static char *automount_fullpath(struct dentry *dentry, void *page)
}
spin_unlock(&tcon->tc_lock);
+ if (unlikely(!page))
+ return ERR_PTR(-ENOMEM);
+
s = dentry_path_raw(dentry, page, PATH_MAX);
if (IS_ERR(s))
return s;
@@ -196,11 +199,28 @@ static struct vfsmount *cifs_do_automount(struct path *path)
struct smb3_fs_context tmp;
char *full_path;
struct vfsmount *mnt;
+ struct cifs_sb_info *mntpt_sb;
+ struct cifs_ses *ses;
if (IS_ROOT(mntpt))
return ERR_PTR(-ESTALE);
- cur_ctx = CIFS_SB(mntpt->d_sb)->ctx;
+ mntpt_sb = CIFS_SB(mntpt->d_sb);
+ ses = cifs_sb_master_tcon(mntpt_sb)->ses;
+ cur_ctx = mntpt_sb->ctx;
+
+ /*
+ * At this point, the root session should be in the mntpt sb. We should
+ * bring the sb context passwords in sync with the root session's
+ * passwords. This would help prevent unnecessary retries and password
+ * swaps for automounts.
+ */
+ mutex_lock(&ses->session_mutex);
+ rc = smb3_sync_session_ctx_passwords(mntpt_sb, ses);
+ mutex_unlock(&ses->session_mutex);
+
+ if (rc)
+ return ERR_PTR(rc);
fc = fs_context_for_submount(path->mnt->mnt_sb->s_type, mntpt);
if (IS_ERR(fc))
@@ -266,7 +286,6 @@ struct vfsmount *cifs_d_automount(struct path *path)
return newmnt;
}
- mntget(newmnt); /* prevent immediate expiration */
mnt_set_expiry(newmnt, &cifs_automount_list);
schedule_delayed_work(&cifs_automount_task,
cifs_mountpoint_expiry_timeout);
diff --git a/fs/smb/client/netmisc.c b/fs/smb/client/netmisc.c
index 2a8d71221e5e..ae15f0bef009 100644
--- a/fs/smb/client/netmisc.c
+++ b/fs/smb/client/netmisc.c
@@ -200,7 +200,7 @@ cifs_set_port(struct sockaddr *addr, const unsigned short int port)
}
/*****************************************************************************
-convert a NT status code to a dos class/code
+ *convert a NT status code to a dos class/code
*****************************************************************************/
/* NT status -> dos error map */
static const struct {
@@ -313,7 +313,6 @@ static const struct {
ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, {
ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, {
ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, {
- ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, {
ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, {
ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS},
/* { This NT error code was 'sqashed'
@@ -775,10 +774,10 @@ cifs_print_status(__u32 status_code)
int idx = 0;
while (nt_errs[idx].nt_errstr != NULL) {
- if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) ==
- (status_code & 0xFFFFFF)) {
+ if (nt_errs[idx].nt_errcode == status_code) {
pr_notice("Status code returned 0x%08x %s\n",
status_code, nt_errs[idx].nt_errstr);
+ return;
}
idx++;
}
@@ -871,17 +870,31 @@ map_smb_to_linux_error(char *buf, bool logErr)
}
/* else ERRHRD class errors or junk - return EIO */
+ /* special cases for NT status codes which cannot be translated to DOS codes */
+ if (smb->Flags2 & SMBFLG2_ERR_STATUS) {
+ __u32 err = le32_to_cpu(smb->Status.CifsError);
+ if (err == (NT_STATUS_NOT_A_REPARSE_POINT))
+ rc = -ENODATA;
+ else if (err == (NT_STATUS_PRIVILEGE_NOT_HELD))
+ rc = -EPERM;
+ }
+
cifs_dbg(FYI, "Mapping smb error code 0x%x to POSIX err %d\n",
le32_to_cpu(smb->Status.CifsError), rc);
/* generic corrective action e.g. reconnect SMB session on
* ERRbaduid could be added */
+ if (rc == -EIO)
+ smb_EIO2(smb_eio_trace_smb1_received_error,
+ le32_to_cpu(smb->Status.CifsError),
+ le16_to_cpu(smb->Flags2));
return rc;
}
int
-map_and_check_smb_error(struct mid_q_entry *mid, bool logErr)
+map_and_check_smb_error(struct TCP_Server_Info *server,
+ struct mid_q_entry *mid, bool logErr)
{
int rc;
struct smb_hdr *smb = (struct smb_hdr *)mid->resp_buf;
@@ -896,7 +909,7 @@ map_and_check_smb_error(struct mid_q_entry *mid, bool logErr)
if (class == ERRSRV && code == ERRbaduid) {
cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n",
code);
- cifs_signal_cifsd_for_reconnect(mid->server, false);
+ cifs_signal_cifsd_for_reconnect(server, false);
}
}
diff --git a/fs/smb/client/nterr.c b/fs/smb/client/nterr.c
index 358a766375b4..8f0bc441295e 100644
--- a/fs/smb/client/nterr.c
+++ b/fs/smb/client/nterr.c
@@ -13,6 +13,13 @@
const struct nt_err_code_struct nt_errs[] = {
{"NT_STATUS_OK", NT_STATUS_OK},
+ {"NT_STATUS_MEDIA_CHANGED", NT_STATUS_MEDIA_CHANGED},
+ {"NT_STATUS_END_OF_MEDIA", NT_STATUS_END_OF_MEDIA},
+ {"NT_STATUS_MEDIA_CHECK", NT_STATUS_MEDIA_CHECK},
+ {"NT_STATUS_NO_DATA_DETECTED", NT_STATUS_NO_DATA_DETECTED},
+ {"NT_STATUS_STOPPED_ON_SYMLINK", NT_STATUS_STOPPED_ON_SYMLINK},
+ {"NT_STATUS_DEVICE_REQUIRES_CLEANING", NT_STATUS_DEVICE_REQUIRES_CLEANING},
+ {"NT_STATUS_DEVICE_DOOR_OPEN", NT_STATUS_DEVICE_DOOR_OPEN},
{"NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL},
{"NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED},
{"NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS},
@@ -667,8 +674,10 @@ const struct nt_err_code_struct nt_errs[] = {
{"NT_STATUS_QUOTA_LIST_INCONSISTENT",
NT_STATUS_QUOTA_LIST_INCONSISTENT},
{"NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE},
+ {"NT_STATUS_NOT_A_REPARSE_POINT", NT_STATUS_NOT_A_REPARSE_POINT},
{"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES},
{"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES},
{"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED},
+ {"NT_STATUS_NO_SUCH_JOB", NT_STATUS_NO_SUCH_JOB},
{NULL, 0}
};
diff --git a/fs/smb/client/nterr.h b/fs/smb/client/nterr.h
index edd4741cab0a..180602c22355 100644
--- a/fs/smb/client/nterr.h
+++ b/fs/smb/client/nterr.h
@@ -546,6 +546,7 @@ extern const struct nt_err_code_struct nt_errs[];
#define NT_STATUS_TOO_MANY_LINKS 0xC0000000 | 0x0265
#define NT_STATUS_QUOTA_LIST_INCONSISTENT 0xC0000000 | 0x0266
#define NT_STATUS_FILE_IS_OFFLINE 0xC0000000 | 0x0267
+#define NT_STATUS_NOT_A_REPARSE_POINT 0xC0000000 | 0x0275
#define NT_STATUS_NO_SUCH_JOB 0xC0000000 | 0xEDE /* scheduler */
#endif /* _NTERR_H */
diff --git a/fs/smb/client/ntlmssp.h b/fs/smb/client/ntlmssp.h
index 875de43b72de..a11fddc321f6 100644
--- a/fs/smb/client/ntlmssp.h
+++ b/fs/smb/client/ntlmssp.h
@@ -73,7 +73,7 @@ typedef struct _SECURITY_BUFFER {
__le16 Length;
__le16 MaximumLength;
__le32 BufferOffset; /* offset to buffer */
-} __attribute__((packed)) SECURITY_BUFFER;
+} __packed SECURITY_BUFFER;
typedef struct _NEGOTIATE_MESSAGE {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
@@ -85,7 +85,7 @@ typedef struct _NEGOTIATE_MESSAGE {
do not set the version is present flag */
char DomainString[];
/* followed by WorkstationString */
-} __attribute__((packed)) NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
+} __packed NEGOTIATE_MESSAGE, *PNEGOTIATE_MESSAGE;
#define NTLMSSP_REVISION_W2K3 0x0F
@@ -121,7 +121,7 @@ typedef struct _CHALLENGE_MESSAGE {
SECURITY_BUFFER TargetInfoArray;
/* SECURITY_BUFFER for version info not present since we
do not set the version is present flag */
-} __attribute__((packed)) CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE;
+} __packed CHALLENGE_MESSAGE, *PCHALLENGE_MESSAGE;
typedef struct _AUTHENTICATE_MESSAGE {
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
@@ -136,7 +136,7 @@ typedef struct _AUTHENTICATE_MESSAGE {
struct ntlmssp_version Version;
/* SECURITY_BUFFER */
char UserString[];
-} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
+} __packed AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
/*
* Size of the session key (crypto key encrypted with the password
diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
index b3a8f9c6fcff..6844f1dc3921 100644
--- a/fs/smb/client/readdir.c
+++ b/fs/smb/client/readdir.c
@@ -9,6 +9,7 @@
*
*/
#include <linux/fs.h>
+#include <linux/namei.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/stat.h>
@@ -71,12 +72,14 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
struct inode *inode;
struct super_block *sb = parent->d_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ bool posix = cifs_sb_master_tcon(cifs_sb)->posix_extensions;
+ bool reparse_need_reval = false;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
int rc;
cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
- dentry = d_hash_and_lookup(parent, name);
+ dentry = try_lookup_noperm(name, parent);
if (!dentry) {
/*
* If we know that the inode will need to be revalidated
@@ -85,7 +88,21 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
* this spares us an invalidation.
*/
retry:
- if ((fattr->cf_cifsattrs & ATTR_REPARSE) ||
+ if (posix) {
+ switch (fattr->cf_mode & S_IFMT) {
+ case S_IFLNK:
+ case S_IFBLK:
+ case S_IFCHR:
+ reparse_need_reval = true;
+ break;
+ default:
+ break;
+ }
+ } else if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT) {
+ reparse_need_reval = true;
+ }
+
+ if (reparse_need_reval ||
(fattr->cf_flags & CIFS_FATTR_NEED_REVAL))
return;
@@ -121,7 +138,7 @@ retry:
* reparse tag and ctime haven't changed.
*/
rc = 0;
- if (fattr->cf_cifsattrs & ATTR_REPARSE) {
+ if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT) {
if (likely(reparse_inode_match(inode, fattr))) {
fattr->cf_mode = inode->i_mode;
fattr->cf_rdev = inode->i_rdev;
@@ -173,7 +190,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
* TODO: go through all documented reparse tags to see if we can
* reasonably map some of them to directories vs. files vs. symlinks
*/
- if ((fattr->cf_cifsattrs & ATTR_REPARSE) &&
+ if ((fattr->cf_cifsattrs & ATTR_REPARSE_POINT) &&
cifs_reparse_point_to_fattr(cifs_sb, fattr, &data))
goto out_reparse;
@@ -241,31 +258,29 @@ cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
fattr->cf_nlink = le32_to_cpu(info->HardLinks);
fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
- /*
- * Since we set the inode type below we need to mask off
- * to avoid strange results if bits set above.
- * XXX: why not make server&client use the type bits?
- */
- fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT;
+ if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT)
+ fattr->cf_cifstag = le32_to_cpu(info->ReparseTag);
+
+ /* The Mode field in the response can now include the file type as well */
+ fattr->cf_mode = wire_mode_to_posix(le32_to_cpu(info->Mode),
+ fattr->cf_cifsattrs & ATTR_DIRECTORY);
+ fattr->cf_dtype = S_DT(fattr->cf_mode);
+
+ switch (fattr->cf_mode & S_IFMT) {
+ case S_IFLNK:
+ case S_IFBLK:
+ case S_IFCHR:
+ fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
+ break;
+ default:
+ break;
+ }
cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n",
le32_to_cpu(info->DeviceId),
le32_to_cpu(info->ReparseTag),
le32_to_cpu(info->Mode));
- if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
- fattr->cf_mode |= S_IFDIR;
- fattr->cf_dtype = DT_DIR;
- } else {
- /*
- * mark anything that is not a dir as regular
- * file. special files should have the REPARSE
- * attribute and will be marked as needing revaluation
- */
- fattr->cf_mode |= S_IFREG;
- fattr->cf_dtype = DT_REG;
- }
-
sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER);
sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP);
}
@@ -301,7 +316,7 @@ static void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr,
__dir_info_to_fattr(fattr, info);
/* See MS-FSCC 2.4.14, 2.4.19 */
- if (fattr->cf_cifsattrs & ATTR_REPARSE)
+ if (fattr->cf_cifsattrs & ATTR_REPARSE_POINT)
fattr->cf_cifstag = le32_to_cpu(di->EaSize);
cifs_fill_common_info(fattr, cifs_sb);
}
@@ -399,7 +414,7 @@ ffirst_retry:
cifsFile->invalidHandle = false;
} else if ((rc == -EOPNOTSUPP) &&
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
- cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
+ cifs_autodisable_serverino(cifs_sb);
goto ffirst_retry;
}
error_exit:
@@ -533,7 +548,7 @@ static void cifs_fill_dirent_full(struct cifs_dirent *de,
}
static void cifs_fill_dirent_search(struct cifs_dirent *de,
- const SEARCH_ID_FULL_DIR_INFO *info)
+ const FILE_ID_FULL_DIR_INFO *info)
{
de->name = &info->FileName[0];
de->namelen = le32_to_cpu(info->FileNameLength);
@@ -719,7 +734,10 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
else
cifs_buf_release(cfile->srch_inf.
ntwrk_buf_start);
+ /* Reset all pointers to the network buffer to prevent stale references */
cfile->srch_inf.ntwrk_buf_start = NULL;
+ cfile->srch_inf.srch_entries_start = NULL;
+ cfile->srch_inf.last_entry = NULL;
}
rc = initiate_cifs_search(xid, file, full_path);
if (rc) {
@@ -742,11 +760,11 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
rc = server->ops->query_dir_next(xid, tcon, &cfile->fid,
search_flags,
&cfile->srch_inf);
+ if (rc)
+ return -ENOENT;
/* FindFirst/Next set last_entry to NULL on malformed reply */
if (cfile->srch_inf.last_entry)
cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
- if (rc)
- return -ENOENT;
}
if (index_to_find < cfile->srch_inf.index_of_last_entry) {
/* we found the buffer that contains the entry */
@@ -757,7 +775,7 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
if (cfile->srch_inf.ntwrk_buf_start == NULL) {
cifs_dbg(VFS, "ntwrk_buf_start is NULL during readdir\n");
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
}
end_of_smb = cfile->srch_inf.ntwrk_buf_start +
@@ -833,9 +851,9 @@ static bool emit_cached_dirents(struct cached_dirents *cde,
}
static void update_cached_dirents_count(struct cached_dirents *cde,
- struct dir_context *ctx)
+ struct file *file)
{
- if (cde->ctx != ctx)
+ if (cde->file != file)
return;
if (cde->is_valid || cde->is_failed)
return;
@@ -844,9 +862,9 @@ static void update_cached_dirents_count(struct cached_dirents *cde,
}
static void finished_cached_dirents_count(struct cached_dirents *cde,
- struct dir_context *ctx)
+ struct dir_context *ctx, struct file *file)
{
- if (cde->ctx != ctx)
+ if (cde->file != file)
return;
if (cde->is_valid || cde->is_failed)
return;
@@ -856,46 +874,52 @@ static void finished_cached_dirents_count(struct cached_dirents *cde,
cde->is_valid = 1;
}
-static void add_cached_dirent(struct cached_dirents *cde,
- struct dir_context *ctx,
- const char *name, int namelen,
- struct cifs_fattr *fattr)
+static bool add_cached_dirent(struct cached_dirents *cde,
+ struct dir_context *ctx, const char *name,
+ int namelen, struct cifs_fattr *fattr,
+ struct file *file)
{
struct cached_dirent *de;
- if (cde->ctx != ctx)
- return;
+ if (cde->file != file)
+ return false;
if (cde->is_valid || cde->is_failed)
- return;
+ return false;
if (ctx->pos != cde->pos) {
cde->is_failed = 1;
- return;
+ return false;
}
de = kzalloc(sizeof(*de), GFP_ATOMIC);
if (de == NULL) {
cde->is_failed = 1;
- return;
+ return false;
}
de->namelen = namelen;
de->name = kstrndup(name, namelen, GFP_ATOMIC);
if (de->name == NULL) {
kfree(de);
cde->is_failed = 1;
- return;
+ return false;
}
de->pos = ctx->pos;
memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr));
list_add_tail(&de->entry, &cde->entries);
+ /* update accounting */
+ cde->entries_count++;
+ cde->bytes_used += sizeof(*de) + (size_t)namelen + 1;
+ return true;
}
static bool cifs_dir_emit(struct dir_context *ctx,
const char *name, int namelen,
struct cifs_fattr *fattr,
- struct cached_fid *cfid)
+ struct cached_fid *cfid,
+ struct file *file)
{
- bool rc;
+ size_t delta_bytes = 0;
+ bool rc, added = false;
ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
rc = dir_emit(ctx, name, namelen, ino, fattr->cf_dtype);
@@ -903,10 +927,20 @@ static bool cifs_dir_emit(struct dir_context *ctx,
return rc;
if (cfid) {
+ /* Cost of this entry */
+ delta_bytes = sizeof(struct cached_dirent) + (size_t)namelen + 1;
+
mutex_lock(&cfid->dirents.de_mutex);
- add_cached_dirent(&cfid->dirents, ctx, name, namelen,
- fattr);
+ added = add_cached_dirent(&cfid->dirents, ctx, name, namelen,
+ fattr, file);
mutex_unlock(&cfid->dirents.de_mutex);
+
+ if (added) {
+ /* per-tcon then global for consistency with free path */
+ atomic64_add((long long)delta_bytes, &cfid->cfids->total_dirents_bytes);
+ atomic_long_inc(&cfid->cfids->total_dirents_entries);
+ atomic64_add((long long)delta_bytes, &cifs_dircache_bytes_used);
+ }
}
return rc;
@@ -1005,7 +1039,7 @@ static int cifs_filldir(char *find_entry, struct file *file,
cifs_prime_dcache(file_dentry(file), &name, &fattr);
return !cifs_dir_emit(ctx, name.name, name.len,
- &fattr, cfid);
+ &fattr, cfid, file);
}
@@ -1056,8 +1090,8 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
* we need to initialize scanning and storing the
* directory content.
*/
- if (ctx->pos == 0 && cfid->dirents.ctx == NULL) {
- cfid->dirents.ctx = ctx;
+ if (ctx->pos == 0 && cfid->dirents.file == NULL) {
+ cfid->dirents.file = file;
cfid->dirents.pos = 2;
}
/*
@@ -1125,7 +1159,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
} else {
if (cfid) {
mutex_lock(&cfid->dirents.de_mutex);
- finished_cached_dirents_count(&cfid->dirents, ctx);
+ finished_cached_dirents_count(&cfid->dirents, ctx, file);
mutex_unlock(&cfid->dirents.de_mutex);
}
cifs_dbg(FYI, "Could not find entry\n");
@@ -1166,7 +1200,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
ctx->pos++;
if (cfid) {
mutex_lock(&cfid->dirents.de_mutex);
- update_cached_dirents_count(&cfid->dirents, ctx);
+ update_cached_dirents_count(&cfid->dirents, file);
mutex_unlock(&cfid->dirents.de_mutex);
}
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index 90da1e2b6217..ce9b923498b5 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -14,47 +14,174 @@
#include "fs_context.h"
#include "reparse.h"
+static int mknod_nfs(unsigned int xid, struct inode *inode,
+ struct dentry *dentry, struct cifs_tcon *tcon,
+ const char *full_path, umode_t mode, dev_t dev,
+ const char *symname);
+
+static int mknod_wsl(unsigned int xid, struct inode *inode,
+ struct dentry *dentry, struct cifs_tcon *tcon,
+ const char *full_path, umode_t mode, dev_t dev,
+ const char *symname);
+
+static int create_native_symlink(const unsigned int xid, struct inode *inode,
+ struct dentry *dentry, struct cifs_tcon *tcon,
+ const char *full_path, const char *symname);
+
static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb,
const unsigned int xid,
const char *full_path,
const char *symname,
bool *directory);
-int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
+int create_reparse_symlink(const unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, const char *symname)
{
+ switch (cifs_symlink_type(CIFS_SB(inode->i_sb))) {
+ case CIFS_SYMLINK_TYPE_NATIVE:
+ return create_native_symlink(xid, inode, dentry, tcon, full_path, symname);
+ case CIFS_SYMLINK_TYPE_NFS:
+ return mknod_nfs(xid, inode, dentry, tcon, full_path, S_IFLNK, 0, symname);
+ case CIFS_SYMLINK_TYPE_WSL:
+ return mknod_wsl(xid, inode, dentry, tcon, full_path, S_IFLNK, 0, symname);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int create_native_symlink(const unsigned int xid, struct inode *inode,
+ struct dentry *dentry, struct cifs_tcon *tcon,
+ const char *full_path, const char *symname)
+{
struct reparse_symlink_data_buffer *buf = NULL;
- struct cifs_open_info_data data;
+ struct cifs_open_info_data data = {};
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ const char *symroot = cifs_sb->ctx->symlinkroot;
struct inode *new;
struct kvec iov;
- __le16 *path;
+ __le16 *path = NULL;
bool directory;
- char *sym, sep = CIFS_DIR_SEP(cifs_sb);
- u16 len, plen;
+ char *symlink_target = NULL;
+ char *sym = NULL;
+ char sep = CIFS_DIR_SEP(cifs_sb);
+ u16 len, plen, poff, slen;
int rc = 0;
if (strlen(symname) > REPARSE_SYM_PATH_MAX)
return -ENAMETOOLONG;
- sym = kstrdup(symname, GFP_KERNEL);
- if (!sym)
- return -ENOMEM;
+ symlink_target = kstrdup(symname, GFP_KERNEL);
+ if (!symlink_target) {
+ rc = -ENOMEM;
+ goto out;
+ }
data = (struct cifs_open_info_data) {
.reparse_point = true,
.reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
- .symlink_target = sym,
+ .symlink_target = symlink_target,
};
- convert_delimiter(sym, sep);
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) &&
+ symroot && symname[0] == '/') {
+ /*
+ * This is a request to create an absolute symlink on the server
+ * which does not support POSIX paths, and expects symlink in
+ * NT-style path. So convert absolute Linux symlink target path
+ * to the absolute NT-style path. Root of the NT-style path for
+ * symlinks is specified in "symlinkroot" mount option. This will
+ * ensure compatibility of this symlink stored in absolute form
+ * on the SMB server.
+ */
+ if (!strstarts(symname, symroot)) {
+ /*
+ * If the absolute Linux symlink target path is not
+ * inside "symlinkroot" location then there is no way
+ * to convert such Linux symlink to NT-style path.
+ */
+ cifs_dbg(VFS,
+ "absolute symlink '%s' cannot be converted to NT format "
+ "because it is outside of symlinkroot='%s'\n",
+ symname, symroot);
+ rc = -EINVAL;
+ goto out;
+ }
+ len = strlen(symroot);
+ if (symroot[len - 1] != '/')
+ len++;
+ if (symname[len] >= 'a' && symname[len] <= 'z' &&
+ (symname[len+1] == '/' || symname[len+1] == '\0')) {
+ /*
+ * Symlink points to Linux target /symlinkroot/x/path/...
+ * where 'x' is the lowercase local Windows drive.
+ * NT-style path for 'x' has common form \??\X:\path\...
+ * with uppercase local Windows drive.
+ */
+ int common_path_len = strlen(symname+len+1)+1;
+ sym = kzalloc(6+common_path_len, GFP_KERNEL);
+ if (!sym) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memcpy(sym, "\\??\\", 4);
+ sym[4] = symname[len] - ('a'-'A');
+ sym[5] = ':';
+ memcpy(sym+6, symname+len+1, common_path_len);
+ } else {
+ /* Unhandled absolute symlink. Report an error. */
+ cifs_dbg(
+ VFS,
+ "absolute symlink '%s' cannot be converted to NT format "
+ "because it points to unknown target\n",
+ symname);
+ rc = -EINVAL;
+ goto out;
+ }
+ } else {
+ /*
+ * This is request to either create an absolute symlink on
+ * server which expects POSIX paths or it is an request to
+ * create a relative symlink from the current directory.
+ * These paths have same format as relative SMB symlinks,
+ * so no conversion is needed. So just take symname as-is.
+ */
+ sym = kstrdup(symname, GFP_KERNEL);
+ if (!sym) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ }
+
+ if (sep == '\\')
+ convert_delimiter(sym, sep);
+
+ /*
+ * For absolute NT symlinks it is required to pass also leading
+ * backslash and to not mangle NT object prefix "\\??\\" and not to
+ * mangle colon in drive letter. But cifs_convert_path_to_utf16()
+ * removes leading backslash and replaces '?' and ':'. So temporary
+ * mask these characters in NT object prefix by '_' and then change
+ * them back.
+ */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) && symname[0] == '/')
+ sym[0] = sym[1] = sym[2] = sym[5] = '_';
+
path = cifs_convert_path_to_utf16(sym, cifs_sb);
if (!path) {
rc = -ENOMEM;
goto out;
}
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) && symname[0] == '/') {
+ sym[0] = '\\';
+ sym[1] = sym[2] = '?';
+ sym[5] = ':';
+ path[0] = cpu_to_le16('\\');
+ path[1] = path[2] = cpu_to_le16('?');
+ path[5] = cpu_to_le16(':');
+ }
+
/*
* SMB distinguish between symlink to directory and symlink to file.
* They cannot be exchanged (symlink of file type which points to
@@ -67,8 +194,18 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
if (rc < 0)
goto out;
- plen = 2 * UniStrnlen((wchar_t *)path, REPARSE_SYM_PATH_MAX);
- len = sizeof(*buf) + plen * 2;
+ slen = 2 * UniStrnlen((wchar_t *)path, REPARSE_SYM_PATH_MAX);
+ poff = 0;
+ plen = slen;
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) && symname[0] == '/') {
+ /*
+ * For absolute NT symlinks skip leading "\\??\\" in PrintName as
+ * PrintName is user visible location in DOS/Win32 format (not in NT format).
+ */
+ poff = 4;
+ plen -= 2 * poff;
+ }
+ len = sizeof(*buf) + plen + slen;
buf = kzalloc(len, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
@@ -77,20 +214,21 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
+
buf->SubstituteNameOffset = cpu_to_le16(plen);
- buf->SubstituteNameLength = cpu_to_le16(plen);
- memcpy(&buf->PathBuffer[plen], path, plen);
+ buf->SubstituteNameLength = cpu_to_le16(slen);
+ memcpy(&buf->PathBuffer[plen], path, slen);
+
buf->PrintNameOffset = 0;
buf->PrintNameLength = cpu_to_le16(plen);
- memcpy(buf->PathBuffer, path, plen);
+ memcpy(buf->PathBuffer, path+poff, plen);
+
buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
- if (*sym != sep)
- buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE);
- convert_delimiter(sym, '/');
iov.iov_base = buf;
iov.iov_len = len;
- new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
+ new = tcon->ses->server->ops->create_reparse_inode(
+ &data, inode->i_sb, xid,
tcon, full_path, directory,
&iov, NULL);
if (!IS_ERR(new))
@@ -98,6 +236,7 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
else
rc = PTR_ERR(new);
out:
+ kfree(sym);
kfree(path);
cifs_free_open_info(&data);
kfree(buf);
@@ -139,7 +278,7 @@ static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb,
}
/*
- * For absolute symlinks it is not possible to determinate
+ * For absolute symlinks it is not possible to determine
* if it should point to directory or file.
*/
if (symname[0] == '/') {
@@ -242,8 +381,40 @@ static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb,
return 0;
}
-static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
+static int create_native_socket(const unsigned int xid, struct inode *inode,
+ struct dentry *dentry, struct cifs_tcon *tcon,
+ const char *full_path)
+{
+ struct reparse_data_buffer buf = {
+ .ReparseTag = cpu_to_le32(IO_REPARSE_TAG_AF_UNIX),
+ .ReparseDataLength = cpu_to_le16(0),
+ };
+ struct cifs_open_info_data data = {
+ .reparse_point = true,
+ .reparse = { .tag = IO_REPARSE_TAG_AF_UNIX, .buf = &buf, },
+ };
+ struct kvec iov = {
+ .iov_base = &buf,
+ .iov_len = sizeof(buf),
+ };
+ struct inode *new;
+ int rc = 0;
+
+ new = tcon->ses->server->ops->create_reparse_inode(
+ &data, inode->i_sb, xid,
+ tcon, full_path, false, &iov, NULL);
+ if (!IS_ERR(new))
+ d_instantiate(dentry, new);
+ else
+ rc = PTR_ERR(new);
+ cifs_free_open_info(&data);
+ return rc;
+}
+
+static int nfs_set_reparse_buf(struct reparse_nfs_data_buffer *buf,
mode_t mode, dev_t dev,
+ __le16 *symname_utf16,
+ int symname_utf16_len,
struct kvec *iov)
{
u64 type;
@@ -254,7 +425,13 @@ static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
switch ((type = reparse_mode_nfs_type(mode))) {
case NFS_SPECFILE_BLK:
case NFS_SPECFILE_CHR:
- dlen = sizeof(__le64);
+ dlen = 2 * sizeof(__le32);
+ ((__le32 *)buf->DataBuffer)[0] = cpu_to_le32(MAJOR(dev));
+ ((__le32 *)buf->DataBuffer)[1] = cpu_to_le32(MINOR(dev));
+ break;
+ case NFS_SPECFILE_LNK:
+ dlen = symname_utf16_len;
+ memcpy(buf->DataBuffer, symname_utf16, symname_utf16_len);
break;
case NFS_SPECFILE_FIFO:
case NFS_SPECFILE_SOCK:
@@ -269,8 +446,6 @@ static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
buf->InodeType = cpu_to_le64(type);
buf->ReparseDataLength = cpu_to_le16(len + dlen -
sizeof(struct reparse_data_buffer));
- *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MINOR(dev) << 32) |
- MAJOR(dev));
iov->iov_base = buf;
iov->iov_len = len + dlen;
return 0;
@@ -278,38 +453,74 @@ static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
static int mknod_nfs(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
- const char *full_path, umode_t mode, dev_t dev)
+ const char *full_path, umode_t mode, dev_t dev,
+ const char *symname)
{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_open_info_data data;
- struct reparse_posix_data *p;
+ struct reparse_nfs_data_buffer *p = NULL;
+ __le16 *symname_utf16 = NULL;
+ int symname_utf16_len = 0;
struct inode *new;
struct kvec iov;
__u8 buf[sizeof(*p) + sizeof(__le64)];
int rc;
- p = (struct reparse_posix_data *)buf;
- rc = nfs_set_reparse_buf(p, mode, dev, &iov);
+ if (S_ISLNK(mode)) {
+ symname_utf16 = cifs_strndup_to_utf16(symname, strlen(symname),
+ &symname_utf16_len,
+ cifs_sb->local_nls,
+ NO_MAP_UNI_RSVD);
+ if (!symname_utf16) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ symname_utf16_len -= 2; /* symlink is without trailing wide-nul */
+ p = kzalloc(sizeof(*p) + symname_utf16_len, GFP_KERNEL);
+ if (!p) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ } else {
+ p = (struct reparse_nfs_data_buffer *)buf;
+ }
+ rc = nfs_set_reparse_buf(p, mode, dev, symname_utf16, symname_utf16_len, &iov);
if (rc)
- return rc;
+ goto out;
data = (struct cifs_open_info_data) {
.reparse_point = true,
- .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },
+ .reparse = { .tag = IO_REPARSE_TAG_NFS, .buf = (struct reparse_data_buffer *)p, },
+ .symlink_target = kstrdup(symname, GFP_KERNEL),
};
- new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
+ new = tcon->ses->server->ops->create_reparse_inode(
+ &data, inode->i_sb, xid,
tcon, full_path, false, &iov, NULL);
if (!IS_ERR(new))
d_instantiate(dentry, new);
else
rc = PTR_ERR(new);
cifs_free_open_info(&data);
+out:
+ if (S_ISLNK(mode)) {
+ kfree(symname_utf16);
+ kfree(p);
+ }
return rc;
}
-static int wsl_set_reparse_buf(struct reparse_data_buffer *buf,
- mode_t mode, struct kvec *iov)
+static int wsl_set_reparse_buf(struct reparse_data_buffer **buf,
+ mode_t mode, const char *symname,
+ struct cifs_sb_info *cifs_sb,
+ struct kvec *iov)
{
+ struct reparse_wsl_symlink_data_buffer *symlink_buf;
+ __le16 *symname_utf16;
+ int symname_utf16_len;
+ int symname_utf8_maxlen;
+ int symname_utf8_len;
+ size_t buf_len;
u32 tag;
switch ((tag = reparse_mode_wsl_tag(mode))) {
@@ -317,16 +528,45 @@ static int wsl_set_reparse_buf(struct reparse_data_buffer *buf,
case IO_REPARSE_TAG_LX_CHR:
case IO_REPARSE_TAG_LX_FIFO:
case IO_REPARSE_TAG_AF_UNIX:
+ buf_len = sizeof(struct reparse_data_buffer);
+ *buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!*buf)
+ return -ENOMEM;
+ break;
+ case IO_REPARSE_TAG_LX_SYMLINK:
+ symname_utf16 = cifs_strndup_to_utf16(symname, strlen(symname),
+ &symname_utf16_len,
+ cifs_sb->local_nls,
+ NO_MAP_UNI_RSVD);
+ if (!symname_utf16)
+ return -ENOMEM;
+ symname_utf8_maxlen = symname_utf16_len/2*3;
+ symlink_buf = kzalloc(sizeof(struct reparse_wsl_symlink_data_buffer) +
+ symname_utf8_maxlen, GFP_KERNEL);
+ if (!symlink_buf) {
+ kfree(symname_utf16);
+ return -ENOMEM;
+ }
+ /* Version field must be set to 2 (MS-FSCC 2.1.2.7) */
+ symlink_buf->Version = cpu_to_le32(2);
+ /* Target for Version 2 is in UTF-8 but without trailing null-term byte */
+ symname_utf8_len = utf16s_to_utf8s((wchar_t *)symname_utf16, symname_utf16_len/2,
+ UTF16_LITTLE_ENDIAN,
+ symlink_buf->Target,
+ symname_utf8_maxlen);
+ *buf = (struct reparse_data_buffer *)symlink_buf;
+ buf_len = sizeof(struct reparse_wsl_symlink_data_buffer) + symname_utf8_len;
+ kfree(symname_utf16);
break;
default:
return -EOPNOTSUPP;
}
- buf->ReparseTag = cpu_to_le32(tag);
- buf->Reserved = 0;
- buf->ReparseDataLength = 0;
- iov->iov_base = buf;
- iov->iov_len = sizeof(*buf);
+ (*buf)->ReparseTag = cpu_to_le32(tag);
+ (*buf)->Reserved = 0;
+ (*buf)->ReparseDataLength = cpu_to_le16(buf_len - sizeof(struct reparse_data_buffer));
+ iov->iov_base = *buf;
+ iov->iov_len = buf_len;
return 0;
}
@@ -378,8 +618,8 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
memset(iov, 0, sizeof(*iov));
- /* Exclude $LXDEV xattr for sockets and fifos */
- if (S_ISSOCK(_mode) || S_ISFIFO(_mode))
+ /* Exclude $LXDEV xattr for non-device files */
+ if (!S_ISBLK(_mode) && !S_ISCHR(_mode))
num_xattrs = ARRAY_SIZE(xattrs) - 1;
else
num_xattrs = ARRAY_SIZE(xattrs);
@@ -415,27 +655,32 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
static int mknod_wsl(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
- const char *full_path, umode_t mode, dev_t dev)
+ const char *full_path, umode_t mode, dev_t dev,
+ const char *symname)
{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_open_info_data data;
- struct reparse_data_buffer buf;
+ struct reparse_data_buffer *buf;
struct smb2_create_ea_ctx *cc;
struct inode *new;
unsigned int len;
struct kvec reparse_iov, xattr_iov;
int rc;
- rc = wsl_set_reparse_buf(&buf, mode, &reparse_iov);
+ rc = wsl_set_reparse_buf(&buf, mode, symname, cifs_sb, &reparse_iov);
if (rc)
return rc;
rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov);
- if (rc)
+ if (rc) {
+ kfree(buf);
return rc;
+ }
data = (struct cifs_open_info_data) {
.reparse_point = true,
- .reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, },
+ .reparse = { .tag = le32_to_cpu(buf->ReparseTag), .buf = buf, },
+ .symlink_target = kstrdup(symname, GFP_KERNEL),
};
cc = xattr_iov.iov_base;
@@ -443,7 +688,8 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
memcpy(data.wsl.eas, &cc->ea, len);
data.wsl.eas_len = len;
- new = smb2_get_reparse_inode(&data, inode->i_sb,
+ new = tcon->ses->server->ops->create_reparse_inode(
+ &data, inode->i_sb,
xid, tcon, full_path, false,
&reparse_iov, &xattr_iov);
if (!IS_ERR(new))
@@ -452,29 +698,31 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
rc = PTR_ERR(new);
cifs_free_open_info(&data);
kfree(xattr_iov.iov_base);
+ kfree(buf);
return rc;
}
-int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
+int mknod_reparse(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev)
{
struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
- int rc = -EOPNOTSUPP;
+
+ if (S_ISSOCK(mode) && !ctx->nonativesocket && ctx->reparse_type != CIFS_REPARSE_TYPE_NONE)
+ return create_native_socket(xid, inode, dentry, tcon, full_path);
switch (ctx->reparse_type) {
case CIFS_REPARSE_TYPE_NFS:
- rc = mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev);
- break;
+ return mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev, NULL);
case CIFS_REPARSE_TYPE_WSL:
- rc = mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev);
- break;
+ return mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev, NULL);
+ default:
+ return -EOPNOTSUPP;
}
- return rc;
}
/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
-static int parse_reparse_posix(struct reparse_posix_data *buf,
+static int parse_reparse_nfs(struct reparse_nfs_data_buffer *buf,
struct cifs_sb_info *cifs_sb,
struct cifs_open_info_data *data)
{
@@ -484,7 +732,8 @@ static int parse_reparse_posix(struct reparse_posix_data *buf,
len = le16_to_cpu(buf->ReparseDataLength);
if (len < sizeof(buf->InodeType)) {
cifs_dbg(VFS, "srv returned malformed nfs buffer\n");
- return -EIO;
+ return smb_EIO2(smb_eio_trace_reparse_nfs_too_short,
+ len, sizeof(buf->InodeType));
}
len -= sizeof(buf->InodeType);
@@ -493,7 +742,7 @@ static int parse_reparse_posix(struct reparse_posix_data *buf,
case NFS_SPECFILE_LNK:
if (len == 0 || (len % 2)) {
cifs_dbg(VFS, "srv returned malformed nfs symlink buffer\n");
- return -EIO;
+ return smb_EIO1(smb_eio_trace_reparse_nfs_symbuf, len);
}
/*
* Check that buffer does not contain UTF-16 null codepoint
@@ -501,7 +750,7 @@ static int parse_reparse_posix(struct reparse_posix_data *buf,
*/
if (UniStrnlen((wchar_t *)buf->DataBuffer, len/2) != len/2) {
cifs_dbg(VFS, "srv returned null byte in nfs symlink target location\n");
- return -EIO;
+ return smb_EIO1(smb_eio_trace_reparse_nfs_nul, len);
}
data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer,
len, true,
@@ -516,7 +765,7 @@ static int parse_reparse_posix(struct reparse_posix_data *buf,
/* DataBuffer for block and char devices contains two 32-bit numbers */
if (len != 8) {
cifs_dbg(VFS, "srv returned malformed nfs buffer for type: 0x%llx\n", type);
- return -EIO;
+ return smb_EIO1(smb_eio_trace_reparse_nfs_dev, len);
}
break;
case NFS_SPECFILE_FIFO:
@@ -524,7 +773,7 @@ static int parse_reparse_posix(struct reparse_posix_data *buf,
/* DataBuffer for fifos and sockets is empty */
if (len != 0) {
cifs_dbg(VFS, "srv returned malformed nfs buffer for type: 0x%llx\n", type);
- return -EIO;
+ return smb_EIO1(smb_eio_trace_reparse_nfs_sockfifo, len);
}
break;
default:
@@ -535,9 +784,209 @@ static int parse_reparse_posix(struct reparse_posix_data *buf,
return 0;
}
-static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
- u32 plen, bool unicode,
+int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
+ bool relative,
+ const char *full_path,
+ struct cifs_sb_info *cifs_sb)
+{
+ const char *symroot = cifs_sb->ctx->symlinkroot;
+ char sep = CIFS_DIR_SEP(cifs_sb);
+ char *linux_target = NULL;
+ char *smb_target = NULL;
+ int symlinkroot_len;
+ int abs_path_len;
+ char *abs_path;
+ int levels;
+ int rc, ulen;
+ int i;
+
+ /* Check that length it valid */
+ if (!len || (len % 2)) {
+ cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
+ rc = smb_EIO1(smb_eio_trace_reparse_native_nul, len);
+ goto out;
+ }
+
+ /*
+ * Check that buffer does not contain UTF-16 null codepoint
+ * because Linux cannot process symlink with null byte.
+ */
+ ulen = UniStrnlen((wchar_t *)buf, len/2);
+ if (ulen != len/2) {
+ cifs_dbg(VFS, "srv returned null byte in native symlink target location\n");
+ rc = smb_EIO2(smb_eio_trace_reparse_native_nul, ulen, len);
+ goto out;
+ }
+
+ smb_target = cifs_strndup_from_utf16(buf, len, true, cifs_sb->local_nls);
+ if (!smb_target) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) &&
+ symroot && !relative) {
+ /*
+ * This is an absolute symlink from the server which does not
+ * support POSIX paths, so the symlink is in NT-style path.
+ * So convert it to absolute Linux symlink target path. Root of
+ * the NT-style path for symlinks is specified in "symlinkroot"
+ * mount option.
+ *
+ * Root of the DOS and Win32 paths is at NT path \??\
+ * It means that DOS/Win32 path C:\folder\file.txt is
+ * NT path \??\C:\folder\file.txt
+ *
+ * NT systems have some well-known object symlinks in their NT
+ * hierarchy, which is needed to take into account when resolving
+ * other symlinks. Most commonly used symlink paths are:
+ * \?? -> \GLOBAL??
+ * \DosDevices -> \??
+ * \GLOBAL??\GLOBALROOT -> \
+ * \GLOBAL??\Global -> \GLOBAL??
+ * \GLOBAL??\NUL -> \Device\Null
+ * \GLOBAL??\UNC -> \Device\Mup
+ * \GLOBAL??\PhysicalDrive0 -> \Device\Harddisk0\DR0 (for each harddisk)
+ * \GLOBAL??\A: -> \Device\Floppy0 (if A: is the first floppy)
+ * \GLOBAL??\C: -> \Device\HarddiskVolume1 (if C: is the first harddisk)
+ * \GLOBAL??\D: -> \Device\CdRom0 (if D: is first cdrom)
+ * \SystemRoot -> \Device\Harddisk0\Partition1\WINDOWS (or where is NT system installed)
+ * \Volume{...} -> \Device\HarddiskVolume1 (where ... is system generated guid)
+ *
+ * In most common cases, absolute NT symlinks points to path on
+ * DOS/Win32 drive letter, system-specific Volume or on UNC share.
+ * Here are few examples of commonly used absolute NT symlinks
+ * created by mklink.exe tool:
+ * \??\C:\folder\file.txt
+ * \??\\C:\folder\file.txt
+ * \??\UNC\server\share\file.txt
+ * \??\\UNC\server\share\file.txt
+ * \??\Volume{b75e2c83-0000-0000-0000-602f00000000}\folder\file.txt
+ *
+ * It means that the most common path prefix \??\ is also NT path
+ * symlink (to \GLOBAL??). It is less common that second path
+ * separator is double backslash, but it is valid.
+ *
+ * Volume guid is randomly generated by the target system and so
+ * only the target system knows the mapping between guid and the
+ * hardisk number. Over SMB it is not possible to resolve this
+ * mapping, therefore symlinks pointing to target location of
+ * volume guids are totally unusable over SMB.
+ *
+ * For now parse only symlink paths available for DOS and Win32.
+ * Those are paths with \??\ prefix or paths which points to \??\
+ * via other NT symlink (\DosDevices\, \GLOBAL??\, ...).
+ */
+ abs_path = smb_target;
+globalroot:
+ if (strstarts(abs_path, "\\??\\"))
+ abs_path += sizeof("\\??\\")-1;
+ else if (strstarts(abs_path, "\\DosDevices\\"))
+ abs_path += sizeof("\\DosDevices\\")-1;
+ else if (strstarts(abs_path, "\\GLOBAL??\\"))
+ abs_path += sizeof("\\GLOBAL??\\")-1;
+ else
+ goto out_unhandled_target;
+
+ /* Sometimes path separator after \?? is double backslash */
+ if (abs_path[0] == '\\')
+ abs_path++;
+
+ while (strstarts(abs_path, "Global\\"))
+ abs_path += sizeof("Global\\")-1;
+
+ if (strstarts(abs_path, "GLOBALROOT\\")) {
+ /* Label globalroot requires path with leading '\\', so do not trim '\\' */
+ abs_path += sizeof("GLOBALROOT")-1;
+ goto globalroot;
+ }
+
+ /* For now parse only paths to drive letters */
+ if (((abs_path[0] >= 'A' && abs_path[0] <= 'Z') ||
+ (abs_path[0] >= 'a' && abs_path[0] <= 'z')) &&
+ abs_path[1] == ':' &&
+ (abs_path[2] == '\\' || abs_path[2] == '\0')) {
+ /* Convert drive letter to lowercase and drop colon */
+ char drive_letter = abs_path[0];
+ if (drive_letter >= 'A' && drive_letter <= 'Z')
+ drive_letter += 'a'-'A';
+ abs_path++;
+ abs_path[0] = drive_letter;
+ } else {
+ goto out_unhandled_target;
+ }
+
+ abs_path_len = strlen(abs_path)+1;
+ symlinkroot_len = strlen(symroot);
+ if (symroot[symlinkroot_len - 1] == '/')
+ symlinkroot_len--;
+ linux_target = kmalloc(symlinkroot_len + 1 + abs_path_len, GFP_KERNEL);
+ if (!linux_target) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memcpy(linux_target, symroot, symlinkroot_len);
+ linux_target[symlinkroot_len] = '/';
+ memcpy(linux_target + symlinkroot_len + 1, abs_path, abs_path_len);
+ } else if (smb_target[0] == sep && relative) {
+ /*
+ * This is a relative SMB symlink from the top of the share,
+ * which is the top level directory of the Linux mount point.
+ * Linux does not support such relative symlinks, so convert
+ * it to the relative symlink from the current directory.
+ * full_path is the SMB path to the symlink (from which is
+ * extracted current directory) and smb_target is the SMB path
+ * where symlink points, therefore full_path must always be on
+ * the SMB share.
+ */
+ int smb_target_len = strlen(smb_target)+1;
+ levels = 0;
+ for (i = 1; full_path[i]; i++) { /* i=1 to skip leading sep */
+ if (full_path[i] == sep)
+ levels++;
+ }
+ linux_target = kmalloc(levels*3 + smb_target_len, GFP_KERNEL);
+ if (!linux_target) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < levels; i++) {
+ linux_target[i*3 + 0] = '.';
+ linux_target[i*3 + 1] = '.';
+ linux_target[i*3 + 2] = sep;
+ }
+ memcpy(linux_target + levels*3, smb_target+1, smb_target_len); /* +1 to skip leading sep */
+ } else {
+ /*
+ * This is either an absolute symlink in POSIX-style format
+ * or relative SMB symlink from the current directory.
+ * These paths have same format as Linux symlinks, so no
+ * conversion is needed.
+ */
+out_unhandled_target:
+ linux_target = smb_target;
+ smb_target = NULL;
+ }
+
+ if (sep == '\\')
+ convert_delimiter(linux_target, '/');
+
+ rc = 0;
+ *target = linux_target;
+
+ cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, *target);
+
+out:
+ if (rc != 0)
+ kfree(linux_target);
+ kfree(smb_target);
+ return rc;
+}
+
+static int parse_reparse_native_symlink(struct reparse_symlink_data_buffer *sym,
+ u32 plen,
struct cifs_sb_info *cifs_sb,
+ const char *full_path,
struct cifs_open_info_data *data)
{
unsigned int len;
@@ -549,70 +998,131 @@ static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
len = le16_to_cpu(sym->SubstituteNameLength);
if (offs + 20 > plen || offs + len + 20 > plen) {
cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
- return -EIO;
+ return smb_EIO2(smb_eio_trace_reparse_native_sym_len,
+ offs << 16 | len, plen);
}
- data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs,
- len, unicode,
+ return smb2_parse_native_symlink(&data->symlink_target,
+ sym->PathBuffer + offs,
+ len,
+ le32_to_cpu(sym->Flags) & SYMLINK_FLAG_RELATIVE,
+ full_path,
+ cifs_sb);
+}
+
+static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf,
+ struct cifs_sb_info *cifs_sb,
+ struct cifs_open_info_data *data)
+{
+ int len = le16_to_cpu(buf->ReparseDataLength);
+ int data_offset = offsetof(typeof(*buf), Target) - offsetof(typeof(*buf), Version);
+ int symname_utf8_len;
+ __le16 *symname_utf16;
+ int symname_utf16_len;
+
+ if (len <= data_offset) {
+ cifs_dbg(VFS, "srv returned malformed wsl symlink buffer\n");
+ return smb_EIO2(smb_eio_trace_reparse_wsl_symbuf,
+ len, data_offset);
+ }
+
+ /* MS-FSCC 2.1.2.7 defines layout of the Target field only for Version 2. */
+ u32 version = le32_to_cpu(buf->Version);
+
+ if (version != 2) {
+ cifs_dbg(VFS, "srv returned unsupported wsl symlink version %u\n", version);
+ return smb_EIO1(smb_eio_trace_reparse_wsl_ver, version);
+ }
+
+ /* Target for Version 2 is in UTF-8 but without trailing null-term byte */
+ symname_utf8_len = len - data_offset;
+ /*
+ * Check that buffer does not contain null byte
+ * because Linux cannot process symlink with null byte.
+ */
+ size_t ulen = strnlen(buf->Target, symname_utf8_len);
+
+ if (ulen != symname_utf8_len) {
+ cifs_dbg(VFS, "srv returned null byte in wsl symlink target location\n");
+ return smb_EIO2(smb_eio_trace_reparse_wsl_ver,
+ ulen, symname_utf8_len);
+ }
+ symname_utf16 = kzalloc(symname_utf8_len * 2, GFP_KERNEL);
+ if (!symname_utf16)
+ return -ENOMEM;
+ symname_utf16_len = utf8s_to_utf16s(buf->Target, symname_utf8_len,
+ UTF16_LITTLE_ENDIAN,
+ (wchar_t *) symname_utf16, symname_utf8_len * 2);
+ if (symname_utf16_len < 0) {
+ kfree(symname_utf16);
+ return symname_utf16_len;
+ }
+ symname_utf16_len *= 2; /* utf8s_to_utf16s() returns number of u16 items, not byte length */
+
+ data->symlink_target = cifs_strndup_from_utf16((u8 *)symname_utf16,
+ symname_utf16_len, true,
cifs_sb->local_nls);
+ kfree(symname_utf16);
if (!data->symlink_target)
return -ENOMEM;
- convert_delimiter(data->symlink_target, '/');
- cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target);
-
return 0;
}
int parse_reparse_point(struct reparse_data_buffer *buf,
u32 plen, struct cifs_sb_info *cifs_sb,
- bool unicode, struct cifs_open_info_data *data)
+ const char *full_path,
+ struct cifs_open_info_data *data)
{
- struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
-
data->reparse.buf = buf;
/* See MS-FSCC 2.1.2 */
switch (le32_to_cpu(buf->ReparseTag)) {
case IO_REPARSE_TAG_NFS:
- return parse_reparse_posix((struct reparse_posix_data *)buf,
+ return parse_reparse_nfs((struct reparse_nfs_data_buffer *)buf,
cifs_sb, data);
case IO_REPARSE_TAG_SYMLINK:
- return parse_reparse_symlink(
+ return parse_reparse_native_symlink(
(struct reparse_symlink_data_buffer *)buf,
- plen, unicode, cifs_sb, data);
+ plen, cifs_sb, full_path, data);
case IO_REPARSE_TAG_LX_SYMLINK:
+ return parse_reparse_wsl_symlink(
+ (struct reparse_wsl_symlink_data_buffer *)buf,
+ cifs_sb, data);
case IO_REPARSE_TAG_AF_UNIX:
case IO_REPARSE_TAG_LX_FIFO:
case IO_REPARSE_TAG_LX_CHR:
- case IO_REPARSE_TAG_LX_BLK:
- break;
+ case IO_REPARSE_TAG_LX_BLK: {
+ u16 dlen = le16_to_cpu(buf->ReparseDataLength);
+
+ if (dlen != 0) {
+ u32 rtag = le32_to_cpu(buf->ReparseTag);
+ cifs_dbg(VFS, "srv returned malformed buffer for reparse point: 0x%08x\n",
+ rtag);
+ return smb_EIO2(smb_eio_trace_reparse_data_len, dlen, rtag);
+ }
+ return 0;
+ }
default:
- cifs_tcon_dbg(VFS | ONCE, "unhandled reparse tag: 0x%08x\n",
- le32_to_cpu(buf->ReparseTag));
- break;
+ return -EOPNOTSUPP;
}
- return 0;
}
-int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
- struct kvec *rsp_iov,
- struct cifs_open_info_data *data)
+struct reparse_data_buffer *smb2_get_reparse_point_buffer(const struct kvec *rsp_iov,
+ u32 *plen)
{
- struct reparse_data_buffer *buf;
struct smb2_ioctl_rsp *io = rsp_iov->iov_base;
- u32 plen = le32_to_cpu(io->OutputCount);
-
- buf = (struct reparse_data_buffer *)((u8 *)io +
- le32_to_cpu(io->OutputOffset));
- return parse_reparse_point(buf, plen, cifs_sb, true, data);
+ *plen = le32_to_cpu(io->OutputCount);
+ return (struct reparse_data_buffer *)((u8 *)io +
+ le32_to_cpu(io->OutputOffset));
}
-static void wsl_to_fattr(struct cifs_open_info_data *data,
+static bool wsl_to_fattr(struct cifs_open_info_data *data,
struct cifs_sb_info *cifs_sb,
u32 tag, struct cifs_fattr *fattr)
{
struct smb2_file_full_ea_info *ea;
+ bool have_xattr_dev = false;
u32 next = 0;
switch (tag) {
@@ -655,81 +1165,109 @@ static void wsl_to_fattr(struct cifs_open_info_data *data,
fattr->cf_uid = wsl_make_kuid(cifs_sb, v);
else if (!strncmp(name, SMB2_WSL_XATTR_GID, nlen))
fattr->cf_gid = wsl_make_kgid(cifs_sb, v);
- else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen))
+ else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen)) {
+ /* File type in reparse point tag and in xattr mode must match. */
+ if (S_DT(fattr->cf_mode) != S_DT(le32_to_cpu(*(__le32 *)v)))
+ return false;
fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
- else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen))
+ } else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) {
fattr->cf_rdev = reparse_mkdev(v);
+ have_xattr_dev = true;
+ }
} while (next);
out:
- fattr->cf_dtype = S_DT(fattr->cf_mode);
+
+ /* Major and minor numbers for char and block devices are mandatory. */
+ if (!have_xattr_dev && (tag == IO_REPARSE_TAG_LX_CHR || tag == IO_REPARSE_TAG_LX_BLK))
+ return false;
+
+ return true;
}
-bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
- struct cifs_fattr *fattr,
- struct cifs_open_info_data *data)
+static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb,
+ struct cifs_fattr *fattr,
+ struct cifs_open_info_data *data)
{
- struct reparse_posix_data *buf = data->reparse.posix;
- u32 tag = data->reparse.tag;
+ struct reparse_nfs_data_buffer *buf = (struct reparse_nfs_data_buffer *)data->reparse.buf;
+
+ if (buf == NULL)
+ return true;
+
+ if (le16_to_cpu(buf->ReparseDataLength) < sizeof(buf->InodeType)) {
+ WARN_ON_ONCE(1);
+ return false;
+ }
- if (tag == IO_REPARSE_TAG_NFS && buf) {
- if (le16_to_cpu(buf->ReparseDataLength) < sizeof(buf->InodeType))
+ switch (le64_to_cpu(buf->InodeType)) {
+ case NFS_SPECFILE_CHR:
+ if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) {
+ WARN_ON_ONCE(1);
return false;
- switch (le64_to_cpu(buf->InodeType)) {
- case NFS_SPECFILE_CHR:
- if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8)
- return false;
- fattr->cf_mode |= S_IFCHR;
- fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
- break;
- case NFS_SPECFILE_BLK:
- if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8)
- return false;
- fattr->cf_mode |= S_IFBLK;
- fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
- break;
- case NFS_SPECFILE_FIFO:
- fattr->cf_mode |= S_IFIFO;
- break;
- case NFS_SPECFILE_SOCK:
- fattr->cf_mode |= S_IFSOCK;
- break;
- case NFS_SPECFILE_LNK:
- fattr->cf_mode |= S_IFLNK;
- break;
- default:
+ }
+ fattr->cf_mode |= S_IFCHR;
+ fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
+ break;
+ case NFS_SPECFILE_BLK:
+ if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) {
WARN_ON_ONCE(1);
return false;
}
- goto out;
+ fattr->cf_mode |= S_IFBLK;
+ fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
+ break;
+ case NFS_SPECFILE_FIFO:
+ fattr->cf_mode |= S_IFIFO;
+ break;
+ case NFS_SPECFILE_SOCK:
+ fattr->cf_mode |= S_IFSOCK;
+ break;
+ case NFS_SPECFILE_LNK:
+ fattr->cf_mode |= S_IFLNK;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return false;
}
+ return true;
+}
+
+bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+ struct cifs_fattr *fattr,
+ struct cifs_open_info_data *data)
+{
+ u32 tag = data->reparse.tag;
+ bool ok;
switch (tag) {
- case IO_REPARSE_TAG_INTERNAL:
- if (!(fattr->cf_cifsattrs & ATTR_DIRECTORY))
- return false;
- fallthrough;
- case IO_REPARSE_TAG_DFS:
- case IO_REPARSE_TAG_DFSR:
- case IO_REPARSE_TAG_MOUNT_POINT:
- /* See cifs_create_junction_fattr() */
- fattr->cf_mode = S_IFDIR | 0711;
- break;
case IO_REPARSE_TAG_LX_SYMLINK:
case IO_REPARSE_TAG_LX_FIFO:
case IO_REPARSE_TAG_AF_UNIX:
case IO_REPARSE_TAG_LX_CHR:
case IO_REPARSE_TAG_LX_BLK:
- wsl_to_fattr(data, cifs_sb, tag, fattr);
+ ok = wsl_to_fattr(data, cifs_sb, tag, fattr);
+ if (!ok)
+ return false;
+ break;
+ case IO_REPARSE_TAG_NFS:
+ ok = posix_reparse_to_fattr(cifs_sb, fattr, data);
+ if (!ok)
+ return false;
break;
case 0: /* SMB1 symlink */
case IO_REPARSE_TAG_SYMLINK:
- case IO_REPARSE_TAG_NFS:
fattr->cf_mode |= S_IFLNK;
break;
default:
- return false;
+ if (!(fattr->cf_cifsattrs & ATTR_DIRECTORY))
+ return false;
+ if (!IS_REPARSE_TAG_NAME_SURROGATE(tag) &&
+ tag != IO_REPARSE_TAG_INTERNAL)
+ return false;
+ /* See cifs_create_junction_fattr() */
+ fattr->cf_mode = S_IFDIR | 0711;
+ break;
}
-out:
+
fattr->cf_dtype = S_DT(fattr->cf_mode);
return true;
}
diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h
index 2a9f4f9f79de..19caab2fd11e 100644
--- a/fs/smb/client/reparse.h
+++ b/fs/smb/client/reparse.h
@@ -50,6 +50,7 @@ static inline kgid_t wsl_make_kgid(struct cifs_sb_info *cifs_sb,
static inline u64 reparse_mode_nfs_type(mode_t mode)
{
switch (mode & S_IFMT) {
+ case S_IFLNK: return NFS_SPECFILE_LNK;
case S_IFBLK: return NFS_SPECFILE_BLK;
case S_IFCHR: return NFS_SPECFILE_CHR;
case S_IFIFO: return NFS_SPECFILE_FIFO;
@@ -61,6 +62,7 @@ static inline u64 reparse_mode_nfs_type(mode_t mode)
static inline u32 reparse_mode_wsl_tag(mode_t mode)
{
switch (mode & S_IFMT) {
+ case S_IFLNK: return IO_REPARSE_TAG_LX_SYMLINK;
case S_IFBLK: return IO_REPARSE_TAG_LX_BLK;
case S_IFCHR: return IO_REPARSE_TAG_LX_CHR;
case S_IFIFO: return IO_REPARSE_TAG_LX_FIFO;
@@ -91,33 +93,48 @@ static inline bool reparse_inode_match(struct inode *inode,
if (cinode->reparse_tag != IO_REPARSE_TAG_INTERNAL &&
cinode->reparse_tag != fattr->cf_cifstag)
return false;
- return (cinode->cifsAttrs & ATTR_REPARSE) &&
+ return (cinode->cifsAttrs & ATTR_REPARSE_POINT) &&
timespec64_equal(&ctime, &fattr->cf_ctime);
}
static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
{
- struct smb2_file_all_info *fi = &data->fi;
- u32 attrs = le32_to_cpu(fi->Attributes);
+ u32 attrs;
bool ret;
- ret = data->reparse_point || (attrs & ATTR_REPARSE);
- if (ret)
- attrs |= ATTR_REPARSE;
- fi->Attributes = cpu_to_le32(attrs);
+ if (data->contains_posix_file_info) {
+ struct smb311_posix_qinfo *fi = &data->posix_fi;
+
+ attrs = le32_to_cpu(fi->DosAttributes);
+ if (data->reparse_point) {
+ attrs |= ATTR_REPARSE_POINT;
+ fi->DosAttributes = cpu_to_le32(attrs);
+ }
+
+ } else {
+ struct smb2_file_all_info *fi = &data->fi;
+
+ attrs = le32_to_cpu(fi->Attributes);
+ if (data->reparse_point) {
+ attrs |= ATTR_REPARSE_POINT;
+ fi->Attributes = cpu_to_le32(attrs);
+ }
+ }
+
+ ret = attrs & ATTR_REPARSE_POINT;
+
return ret;
}
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr,
struct cifs_open_info_data *data);
-int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
+int create_reparse_symlink(const unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, const char *symname);
-int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
+int mknod_reparse(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev);
-int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov,
- struct cifs_open_info_data *data);
+struct reparse_data_buffer *smb2_get_reparse_point_buffer(const struct kvec *rsp_iov, u32 *len);
#endif /* _CIFS_REPARSE_H */
diff --git a/fs/smb/client/rfc1002pdu.h b/fs/smb/client/rfc1002pdu.h
index ae1d025da294..f5b143088b90 100644
--- a/fs/smb/client/rfc1002pdu.h
+++ b/fs/smb/client/rfc1002pdu.h
@@ -24,7 +24,7 @@
struct rfc1002_session_packet {
__u8 type;
__u8 flags;
- __u16 length;
+ __be16 length;
union {
struct {
__u8 called_len;
@@ -33,17 +33,17 @@ struct rfc1002_session_packet {
__u8 calling_len;
__u8 calling_name[32];
__u8 scope2; /* null */
- } __attribute__((packed)) session_req;
+ } __packed session_req;
struct {
- __u32 retarget_ip_addr;
- __u16 port;
- } __attribute__((packed)) retarget_resp;
+ __be32 retarget_ip_addr;
+ __be16 port;
+ } __packed retarget_resp;
__u8 neg_ses_resp_error_code;
/* POSITIVE_SESSION_RESPONSE packet does not include trailer.
SESSION_KEEP_ALIVE packet also does not include a trailer.
Trailer for the SESSION_MESSAGE packet is SMB/CIFS header */
- } __attribute__((packed)) trailer;
-} __attribute__((packed));
+ } __packed trailer;
+} __packed;
/* Negative Session Response error codes */
#define RFC1002_NOT_LISTENING_CALLED 0x80 /* not listening on called name */
diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
index c88e9657f47a..a72d6a6d20f0 100644
--- a/fs/smb/client/sess.c
+++ b/fs/smb/client/sess.c
@@ -27,31 +27,6 @@ static int
cifs_ses_add_channel(struct cifs_ses *ses,
struct cifs_server_iface *iface);
-bool
-is_server_using_iface(struct TCP_Server_Info *server,
- struct cifs_server_iface *iface)
-{
- struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr;
- struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr;
- struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr;
- struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr;
-
- if (server->dstaddr.ss_family != iface->sockaddr.ss_family)
- return false;
- if (server->dstaddr.ss_family == AF_INET) {
- if (s4->sin_addr.s_addr != i4->sin_addr.s_addr)
- return false;
- } else if (server->dstaddr.ss_family == AF_INET6) {
- if (memcmp(&s6->sin6_addr, &i6->sin6_addr,
- sizeof(i6->sin6_addr)) != 0)
- return false;
- } else {
- /* unknown family.. */
- return false;
- }
- return true;
-}
-
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
{
int i;
@@ -267,7 +242,7 @@ int cifs_try_adding_channels(struct cifs_ses *ses)
iface->num_channels++;
iface->weight_fulfilled++;
- cifs_dbg(VFS, "successfully opened new channel on iface:%pIS\n",
+ cifs_info("successfully opened new channel on iface:%pIS\n",
&iface->sockaddr);
break;
}
@@ -290,12 +265,16 @@ int cifs_try_adding_channels(struct cifs_ses *ses)
}
/*
- * called when multichannel is disabled by the server.
- * this always gets called from smb2_reconnect
- * and cannot get called in parallel threads.
+ * cifs_decrease_secondary_channels - Reduce the number of active secondary channels
+ * @ses: pointer to the CIFS session structure
+ * @disable_mchan: if true, reduce to a single channel; if false, reduce to chan_max
+ *
+ * This function disables and cleans up extra secondary channels for a CIFS session.
+ * If called during reconfiguration, it reduces the channel count to the new maximum (chan_max).
+ * Otherwise, it disables all but the primary channel.
*/
void
-cifs_disable_secondary_channels(struct cifs_ses *ses)
+cifs_decrease_secondary_channels(struct cifs_ses *ses, bool disable_mchan)
{
int i, chan_count;
struct TCP_Server_Info *server;
@@ -306,12 +285,16 @@ cifs_disable_secondary_channels(struct cifs_ses *ses)
if (chan_count == 1)
goto done;
- ses->chan_count = 1;
-
- /* for all secondary channels reset the need reconnect bit */
- ses->chans_need_reconnect &= 1;
+ /* Update the chan_count to the new maximum */
+ if (disable_mchan) {
+ cifs_dbg(FYI, "server does not support multichannel anymore.\n");
+ ses->chan_count = 1;
+ } else {
+ ses->chan_count = ses->chan_max;
+ }
- for (i = 1; i < chan_count; i++) {
+ /* Disable all secondary channels beyond the new chan_count */
+ for (i = ses->chan_count ; i < chan_count; i++) {
iface = ses->chans[i].iface;
server = ses->chans[i].server;
@@ -343,14 +326,20 @@ cifs_disable_secondary_channels(struct cifs_ses *ses)
spin_lock(&ses->chan_lock);
}
+ /* For extra secondary channels, reset the need reconnect bit */
+ if (ses->chan_count == 1) {
+ cifs_dbg(VFS, "Disable all secondary channels\n");
+ ses->chans_need_reconnect &= 1;
+ } else {
+ cifs_dbg(VFS, "Disable extra secondary channels\n");
+ ses->chans_need_reconnect &= ((1UL << ses->chan_max) - 1);
+ }
+
done:
spin_unlock(&ses->chan_lock);
}
-/*
- * update the iface for the channel if necessary.
- * Must be called with chan_lock held.
- */
+/* update the iface for the channel if necessary. */
void
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
{
@@ -360,6 +349,7 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
struct cifs_server_iface *old_iface = NULL;
struct cifs_server_iface *last_iface = NULL;
struct sockaddr_storage ss;
+ int retry = 0;
spin_lock(&ses->chan_lock);
chan_index = cifs_ses_get_chan_index(ses, server);
@@ -388,6 +378,7 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
return;
}
+try_again:
last_iface = list_last_entry(&ses->iface_list, struct cifs_server_iface,
iface_head);
iface_min_speed = last_iface->speed;
@@ -425,6 +416,13 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
}
if (list_entry_is_head(iface, &ses->iface_list, iface_head)) {
+ list_for_each_entry(iface, &ses->iface_list, iface_head)
+ iface->weight_fulfilled = 0;
+
+ /* see if it can be satisfied in second attempt */
+ if (!retry++)
+ goto try_again;
+
iface = NULL;
cifs_dbg(FYI, "unable to find a suitable iface\n");
}
@@ -473,6 +471,10 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
ses->chans[chan_index].iface = iface;
spin_unlock(&ses->chan_lock);
+
+ spin_lock(&server->srv_lock);
+ memcpy(&server->dstaddr, &iface->sockaddr, sizeof(server->dstaddr));
+ spin_unlock(&server->srv_lock);
}
static int
@@ -491,11 +493,11 @@ cifs_ses_add_channel(struct cifs_ses *ses,
if (iface->sockaddr.ss_family == AF_INET)
cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI4)\n",
- ses, iface->speed, iface->rdma_capable ? "yes" : "no",
+ ses, iface->speed, str_yes_no(iface->rdma_capable),
&ipv4->sin_addr);
else
cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ip:%pI6)\n",
- ses, iface->speed, iface->rdma_capable ? "yes" : "no",
+ ses, iface->speed, str_yes_no(iface->rdma_capable),
&ipv6->sin6_addr);
/*
@@ -522,13 +524,13 @@ cifs_ses_add_channel(struct cifs_ses *ses,
ctx->domainauto = ses->domainAuto;
ctx->domainname = ses->domainName;
- /* no hostname for extra channels */
- ctx->server_hostname = "";
+ ctx->server_hostname = ses->server->hostname;
ctx->username = ses->user_name;
ctx->password = ses->password;
ctx->sectype = ses->sectype;
ctx->sign = ses->sign;
+ ctx->unicode = ses->unicode;
/* UNC and paths */
/* XXX: Use ses->server->hostname? */
@@ -550,6 +552,13 @@ cifs_ses_add_channel(struct cifs_ses *ses,
ctx->sockopt_tcp_nodelay = ses->server->tcp_nodelay;
ctx->echo_interval = ses->server->echo_interval / HZ;
ctx->max_credits = ses->server->max_credits;
+ ctx->min_offload = ses->server->min_offload;
+ ctx->compress = ses->server->compression.requested;
+ ctx->dfs_conn = ses->server->dfs_conn;
+ ctx->ignore_signature = ses->server->ignore_signature;
+ ctx->leaf_fullpath = ses->server->leaf_fullpath;
+ ctx->rootfs = ses->server->noblockcnt;
+ ctx->retrans = ses->server->retrans;
/*
* This will be used for encoding/decoding user/domain/pw
@@ -592,7 +601,7 @@ cifs_ses_add_channel(struct cifs_ses *ses,
* to sign packets before we generate the channel signing key
* (we sign with the session key)
*/
- rc = smb311_crypto_shash_allocate(chan->server);
+ rc = smb3_crypto_shash_allocate(chan->server);
if (rc) {
cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
mutex_unlock(&ses->session_mutex);
@@ -648,6 +657,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses,
USHRT_MAX));
pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq);
pSMB->req.VcNumber = cpu_to_le16(1);
+ pSMB->req.SessionKey = server->session_key_id;
/* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
@@ -700,6 +710,22 @@ unicode_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp)
*pbcc_area = bcc_ptr;
}
+static void
+ascii_oslm_strings(char **pbcc_area, const struct nls_table *nls_cp)
+{
+ char *bcc_ptr = *pbcc_area;
+
+ strcpy(bcc_ptr, "Linux version ");
+ bcc_ptr += strlen("Linux version ");
+ strcpy(bcc_ptr, init_utsname()->release);
+ bcc_ptr += strlen(init_utsname()->release) + 1;
+
+ strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
+ bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+
+ *pbcc_area = bcc_ptr;
+}
+
static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses,
const struct nls_table *nls_cp)
{
@@ -724,6 +750,25 @@ static void unicode_domain_string(char **pbcc_area, struct cifs_ses *ses,
*pbcc_area = bcc_ptr;
}
+static void ascii_domain_string(char **pbcc_area, struct cifs_ses *ses,
+ const struct nls_table *nls_cp)
+{
+ char *bcc_ptr = *pbcc_area;
+ int len;
+
+ /* copy domain */
+ if (ses->domainName != NULL) {
+ len = strscpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
+ if (WARN_ON_ONCE(len < 0))
+ len = CIFS_MAX_DOMAINNAME_LEN - 1;
+ bcc_ptr += len;
+ } /* else we send a null domain name so server will default to its own domain */
+ *bcc_ptr = 0;
+ bcc_ptr++;
+
+ *pbcc_area = bcc_ptr;
+}
+
static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
const struct nls_table *nls_cp)
{
@@ -769,25 +814,10 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
*bcc_ptr = 0;
bcc_ptr++; /* account for null termination */
- /* copy domain */
- if (ses->domainName != NULL) {
- len = strscpy(bcc_ptr, ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
- if (WARN_ON_ONCE(len < 0))
- len = CIFS_MAX_DOMAINNAME_LEN - 1;
- bcc_ptr += len;
- } /* else we send a null domain name so server will default to its own domain */
- *bcc_ptr = 0;
- bcc_ptr++;
-
/* BB check for overflow here */
- strcpy(bcc_ptr, "Linux version ");
- bcc_ptr += strlen("Linux version ");
- strcpy(bcc_ptr, init_utsname()->release);
- bcc_ptr += strlen(init_utsname()->release) + 1;
-
- strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
- bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+ ascii_domain_string(&bcc_ptr, ses, nls_cp);
+ ascii_oslm_strings(&bcc_ptr, nls_cp);
*pbcc_area = bcc_ptr;
}
@@ -1263,12 +1293,13 @@ cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
switch (requested) {
case Kerberos:
case RawNTLMSSP:
+ case IAKerb:
return requested;
case Unspecified:
if (server->sec_ntlmssp &&
(global_secflags & CIFSSEC_MAY_NTLMSSP))
return RawNTLMSSP;
- if ((server->sec_kerberos || server->sec_mskerberos) &&
+ if ((server->sec_kerberos || server->sec_mskerberos || server->sec_iakerb) &&
(global_secflags & CIFSSEC_MAY_KRB5))
return Kerberos;
fallthrough;
@@ -1299,6 +1330,7 @@ struct sess_data {
struct nls_table *nls_cp;
void (*func)(struct sess_data *);
int result;
+ unsigned int in_len;
/* we will send the SMB in three pieces:
* a fixed length beginning part, an optional
@@ -1322,11 +1354,12 @@ sess_alloc_buffer(struct sess_data *sess_data, int wct)
rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
(void **)&smb_buf);
- if (rc)
+ if (rc < 0)
return rc;
+ sess_data->in_len = rc;
sess_data->iov[0].iov_base = (char *)smb_buf;
- sess_data->iov[0].iov_len = be32_to_cpu(smb_buf->smb_buf_length) + 4;
+ sess_data->iov[0].iov_len = sess_data->in_len;
/*
* This variable will be used to clear the buffer
* allocated above in case of any error in the calling function.
@@ -1404,7 +1437,7 @@ sess_sendreceive(struct sess_data *sess_data)
struct kvec rsp_iov = { NULL, 0 };
count = sess_data->iov[1].iov_len + sess_data->iov[2].iov_len;
- be32_add_cpu(&smb_buf->smb_buf_length, count);
+ sess_data->in_len += count;
put_bcc(count, smb_buf);
rc = SendReceive2(sess_data->xid, sess_data->ses,
@@ -1487,7 +1520,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data)
smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
if (smb_buf->WordCount != 3) {
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_sess_nl2_wcc, smb_buf->WordCount);
cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
goto out;
}
@@ -1589,7 +1622,7 @@ sess_auth_kerberos(struct sess_data *sess_data)
sess_data->iov[1].iov_len = msg->secblob_len;
pSMB->req.SecurityBlobLength = cpu_to_le16(sess_data->iov[1].iov_len);
- if (ses->capabilities & CAP_UNICODE) {
+ if (pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) {
/* unicode strings must be word aligned */
if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
*bcc_ptr = 0;
@@ -1598,8 +1631,8 @@ sess_auth_kerberos(struct sess_data *sess_data)
unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
unicode_domain_string(&bcc_ptr, ses, sess_data->nls_cp);
} else {
- /* BB: is this right? */
- ascii_ssetup_strings(&bcc_ptr, ses, sess_data->nls_cp);
+ ascii_oslm_strings(&bcc_ptr, sess_data->nls_cp);
+ ascii_domain_string(&bcc_ptr, ses, sess_data->nls_cp);
}
sess_data->iov[2].iov_len = (long) bcc_ptr -
@@ -1613,7 +1646,7 @@ sess_auth_kerberos(struct sess_data *sess_data)
smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
if (smb_buf->WordCount != 4) {
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_sess_krb_wcc, smb_buf->WordCount);
cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
goto out_put_spnego_key;
}
@@ -1683,22 +1716,22 @@ _sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
capabilities = cifs_ssetup_hdr(ses, server, pSMB);
- if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
- cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
- return -ENOSYS;
- }
-
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
capabilities |= CAP_EXTENDED_SECURITY;
pSMB->req.Capabilities |= cpu_to_le32(capabilities);
bcc_ptr = sess_data->iov[2].iov_base;
- /* unicode strings must be word aligned */
- if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
- *bcc_ptr = 0;
- bcc_ptr++;
+
+ if (pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) {
+ /* unicode strings must be word aligned */
+ if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
+ } else {
+ ascii_oslm_strings(&bcc_ptr, sess_data->nls_cp);
}
- unicode_oslm_strings(&bcc_ptr, sess_data->nls_cp);
sess_data->iov[2].iov_len = (long) bcc_ptr -
(long) sess_data->iov[2].iov_base;
@@ -1774,7 +1807,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
if (smb_buf->WordCount != 4) {
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_sess_rawnl_neg_wcc, smb_buf->WordCount);
cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
goto out_free_ntlmsspblob;
}
@@ -1864,7 +1897,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
smb_buf = (struct smb_hdr *)sess_data->iov[0].iov_base;
if (smb_buf->WordCount != 4) {
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_sess_rawnl_auth_wcc, smb_buf->WordCount);
cifs_dbg(VFS, "bad word count %d\n", smb_buf->WordCount);
goto out_free_ntlmsspblob;
}
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index 9a6ece66c4d3..9729b56bd9d4 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -7,6 +7,7 @@
#include <linux/pagemap.h>
#include <linux/vfs.h>
+#include <linux/fs_struct.h>
#include <uapi/linux/magic.h>
#include "cifsglob.h"
#include "cifsproto.h"
@@ -14,6 +15,9 @@
#include "cifspdu.h"
#include "cifs_unicode.h"
#include "fs_context.h"
+#include "nterr.h"
+#include "smberr.h"
+#include "reparse.h"
/*
* An NT cancel request header looks just like the original request except:
@@ -26,20 +30,25 @@
* SMB_COM_NT_CANCEL request and then sends it.
*/
static int
-send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
- struct mid_q_entry *mid)
+send_nt_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ struct smb_rqst *rqst, struct mid_q_entry *mid,
+ unsigned int xid)
{
- int rc = 0;
struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+ struct kvec iov[1];
+ struct smb_rqst crqst = { .rq_iov = iov, .rq_nvec = 1 };
+ int rc = 0;
- /* -4 for RFC1001 length and +2 for BCC field */
- in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2);
+ /* +2 for BCC field */
in_buf->Command = SMB_COM_NT_CANCEL;
in_buf->WordCount = 0;
put_bcc(0, in_buf);
+ iov[0].iov_base = in_buf;
+ iov[0].iov_len = sizeof(struct smb_hdr) + 2;
+
cifs_server_lock(server);
- rc = cifs_sign_smb(in_buf, server, &mid->sequence_number);
+ rc = cifs_sign_rqst(&crqst, server, &mid->sequence_number);
if (rc) {
cifs_server_unlock(server);
return rc;
@@ -51,7 +60,7 @@ send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
* after signing here.
*/
--server->sequence_number;
- rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
+ rc = __smb_send_rqst(server, 1, &crqst);
if (rc < 0)
server->sequence_number--;
@@ -63,6 +72,46 @@ send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
return rc;
}
+/*
+ * Send a LOCKINGX_CANCEL_LOCK to cause the Windows blocking lock to
+ * return.
+ */
+static int
+send_lock_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ struct smb_rqst *rqst, struct mid_q_entry *mid,
+ unsigned int xid)
+{
+ struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+ unsigned int in_len = rqst->rq_iov[0].iov_len;
+ LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
+ int rc;
+
+ /* We just modify the current in_buf to change
+ * the type of lock from LOCKING_ANDX_SHARED_LOCK
+ * or LOCKING_ANDX_EXCLUSIVE_LOCK to
+ * LOCKING_ANDX_CANCEL_LOCK.
+ */
+ pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
+ pSMB->Timeout = 0;
+ pSMB->hdr.Mid = get_next_mid(ses->server);
+
+ rc = SendReceive(xid, ses, in_buf, in_len, NULL, NULL, 0);
+ if (rc == -ENOLCK)
+ rc = 0; /* If we get back -ENOLCK, it probably means we managed
+ * to cancel the lock command before it took effect.
+ */
+ return rc;
+}
+
+static int cifs_send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ struct smb_rqst *rqst, struct mid_q_entry *mid,
+ unsigned int xid)
+{
+ if (mid->sr_flags & CIFS_WINDOWS_LOCK)
+ return send_lock_cancel(ses, server, rqst, mid, xid);
+ return send_nt_cancel(ses, server, rqst, mid, xid);
+}
+
static bool
cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
{
@@ -92,17 +141,17 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
struct smb_hdr *buf = (struct smb_hdr *)buffer;
struct mid_q_entry *mid;
- spin_lock(&server->mid_lock);
+ spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
if (compare_mid(mid->mid, buf) &&
mid->mid_state == MID_REQUEST_SUBMITTED &&
le16_to_cpu(mid->command) == buf->Command) {
- kref_get(&mid->refcount);
- spin_unlock(&server->mid_lock);
+ smb_get_mid(mid);
+ spin_unlock(&server->mid_queue_lock);
return mid;
}
}
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
return NULL;
}
@@ -166,10 +215,9 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
__u16 last_mid, cur_mid;
bool collision, reconnect = false;
- spin_lock(&server->mid_lock);
-
+ spin_lock(&server->mid_counter_lock);
/* mid is 16 bit only for CIFS/SMB */
- cur_mid = (__u16)((server->CurrentMid) & 0xffff);
+ cur_mid = (__u16)((server->current_mid) & 0xffff);
/* we do not want to loop forever */
last_mid = cur_mid;
cur_mid++;
@@ -195,6 +243,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
cur_mid++;
num_mids = 0;
+ spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
++num_mids;
if (mid_entry->mid == cur_mid &&
@@ -204,6 +253,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
break;
}
}
+ spin_unlock(&server->mid_queue_lock);
/*
* if we have more than 32k mids in the list, then something
@@ -220,12 +270,12 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
if (!collision) {
mid = (__u64)cur_mid;
- server->CurrentMid = mid;
+ server->current_mid = mid;
break;
}
cur_mid++;
}
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_counter_lock);
if (reconnect) {
cifs_signal_cifsd_for_reconnect(server, false);
@@ -284,7 +334,7 @@ check2ndT2(char *buf)
}
static int
-coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
+coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len)
{
struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf;
struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr;
@@ -350,15 +400,15 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
}
put_bcc(byte_count, target_hdr);
- byte_count = be32_to_cpu(target_hdr->smb_buf_length);
+ byte_count = *pdu_len;
byte_count += total_in_src;
/* don't allow buffer to overflow */
- if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
cifs_dbg(FYI, "coalesced BCC exceeds buffer size (%u)\n",
byte_count);
return -ENOBUFS;
}
- target_hdr->smb_buf_length = cpu_to_be32(byte_count);
+ *pdu_len = byte_count;
/* copy second buffer into end of first buffer */
memcpy(data_area_of_tgt, data_area_of_src, total_in_src);
@@ -377,7 +427,7 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)
static void
cifs_downgrade_oplock(struct TCP_Server_Info *server,
struct cifsInodeInfo *cinode, __u32 oplock,
- unsigned int epoch, bool *purge_cache)
+ __u16 epoch, bool *purge_cache)
{
cifs_set_oplock_level(cinode, oplock);
}
@@ -393,12 +443,12 @@ cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
mid->multiRsp = true;
if (mid->resp_buf) {
/* merge response - fix up 1st*/
- malformed = coalesce_t2(buf, mid->resp_buf);
+ malformed = coalesce_t2(buf, mid->resp_buf, &mid->response_pdu_len);
if (malformed > 0)
return true;
/* All parts received or packet is malformed. */
mid->multiEnd = true;
- dequeue_mid(mid, malformed);
+ dequeue_mid(server, mid, malformed);
return true;
}
if (!server->large_buf) {
@@ -426,26 +476,19 @@ cifs_negotiate(const unsigned int xid,
{
int rc;
rc = CIFSSMBNegotiate(xid, ses, server);
- if (rc == -EAGAIN) {
- /* retry only once on 1st time connection */
- set_credits(server, 1);
- rc = CIFSSMBNegotiate(xid, ses, server);
- if (rc == -EAGAIN)
- rc = -EHOSTDOWN;
- }
return rc;
}
static unsigned int
-cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
+smb1_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
{
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
struct TCP_Server_Info *server = tcon->ses->server;
unsigned int wsize;
/* start with specified wsize, or default */
- if (ctx->wsize)
- wsize = ctx->wsize;
+ if (ctx->got_wsize)
+ wsize = ctx->vol_wsize;
else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
wsize = CIFS_DEFAULT_IOSIZE;
else
@@ -463,7 +506,7 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
if (!(server->capabilities & CAP_LARGE_WRITE_X) ||
(!(server->capabilities & CAP_UNIX) && server->sign))
wsize = min_t(unsigned int, wsize,
- server->maxBuf - sizeof(WRITE_REQ) + 4);
+ server->maxBuf - sizeof(WRITE_REQ));
/* hard limit of CIFS_MAX_WSIZE */
wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE);
@@ -472,7 +515,7 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
}
static unsigned int
-cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
+smb1_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
{
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
struct TCP_Server_Info *server = tcon->ses->server;
@@ -497,7 +540,7 @@ cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
else
defsize = server->maxBuf - sizeof(READ_RSP);
- rsize = ctx->rsize ? ctx->rsize : defsize;
+ rsize = ctx->got_rsize ? ctx->vol_rsize : defsize;
/*
* no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to
@@ -548,54 +591,204 @@ static int cifs_query_path_info(const unsigned int xid,
const char *full_path,
struct cifs_open_info_data *data)
{
- int rc;
+ int rc = -EOPNOTSUPP;
FILE_ALL_INFO fi = {};
+ struct cifs_search_info search_info = {};
+ bool non_unicode_wildcard = false;
- data->symlink = false;
+ data->reparse_point = false;
data->adjust_tz = false;
- /* could do find first instead but this returns more info */
- rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls,
- cifs_remap(cifs_sb));
/*
- * BB optimize code so we do not make the above call when server claims
- * no NT SMB support and the above call failed at least once - set flag
- * in tcon or mount.
+ * First try CIFSSMBQPathInfo() function which returns more info
+ * (NumberOfLinks) than CIFSFindFirst() fallback function.
+ * Some servers like Win9x do not support SMB_QUERY_FILE_ALL_INFO over
+ * TRANS2_QUERY_PATH_INFORMATION, but supports it with filehandle over
+ * TRANS2_QUERY_FILE_INFORMATION (function CIFSSMBQFileInfo(). But SMB
+ * Open command on non-NT servers works only for files, does not work
+ * for directories. And moreover Win9x SMB server returns bogus data in
+ * SMB_QUERY_FILE_ALL_INFO Attributes field. So for non-NT servers,
+ * do not even use CIFSSMBQPathInfo() or CIFSSMBQFileInfo() function.
+ */
+ if (tcon->ses->capabilities & CAP_NT_SMBS)
+ rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */,
+ cifs_sb->local_nls, cifs_remap(cifs_sb));
+
+ /*
+ * Non-UNICODE variant of fallback functions below expands wildcards,
+ * so they cannot be used for querying paths with wildcard characters.
+ */
+ if (rc && !(tcon->ses->capabilities & CAP_UNICODE) && strpbrk(full_path, "*?\"><"))
+ non_unicode_wildcard = true;
+
+ /*
+ * Then fallback to CIFSFindFirst() which works also with non-NT servers
+ * but does not does not provide NumberOfLinks.
*/
- if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
+ if ((rc == -EOPNOTSUPP || rc == -EINVAL) &&
+ !non_unicode_wildcard) {
+ if (!(tcon->ses->capabilities & tcon->ses->server->vals->cap_nt_find))
+ search_info.info_level = SMB_FIND_FILE_INFO_STANDARD;
+ else
+ search_info.info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
+ rc = CIFSFindFirst(xid, tcon, full_path, cifs_sb, NULL,
+ CIFS_SEARCH_CLOSE_ALWAYS | CIFS_SEARCH_CLOSE_AT_END,
+ &search_info, false);
+ if (rc == 0) {
+ if (!(tcon->ses->capabilities & tcon->ses->server->vals->cap_nt_find)) {
+ FIND_FILE_STANDARD_INFO *di;
+ int offset = tcon->ses->server->timeAdj;
+
+ di = (FIND_FILE_STANDARD_INFO *)search_info.srch_entries_start;
+ fi.CreationTime = cpu_to_le64(cifs_UnixTimeToNT(cnvrtDosUnixTm(
+ di->CreationDate, di->CreationTime, offset)));
+ fi.LastAccessTime = cpu_to_le64(cifs_UnixTimeToNT(cnvrtDosUnixTm(
+ di->LastAccessDate, di->LastAccessTime, offset)));
+ fi.LastWriteTime = cpu_to_le64(cifs_UnixTimeToNT(cnvrtDosUnixTm(
+ di->LastWriteDate, di->LastWriteTime, offset)));
+ fi.ChangeTime = fi.LastWriteTime;
+ fi.Attributes = cpu_to_le32(le16_to_cpu(di->Attributes));
+ fi.AllocationSize = cpu_to_le64(le32_to_cpu(di->AllocationSize));
+ fi.EndOfFile = cpu_to_le64(le32_to_cpu(di->DataSize));
+ } else {
+ FILE_FULL_DIRECTORY_INFO *di;
+
+ di = (FILE_FULL_DIRECTORY_INFO *)search_info.srch_entries_start;
+ fi.CreationTime = di->CreationTime;
+ fi.LastAccessTime = di->LastAccessTime;
+ fi.LastWriteTime = di->LastWriteTime;
+ fi.ChangeTime = di->ChangeTime;
+ fi.Attributes = di->ExtFileAttributes;
+ fi.AllocationSize = di->AllocationSize;
+ fi.EndOfFile = di->EndOfFile;
+ fi.EASize = di->EaSize;
+ }
+ fi.NumberOfLinks = cpu_to_le32(1);
+ fi.DeletePending = 0;
+ fi.Directory = !!(le32_to_cpu(fi.Attributes) & ATTR_DIRECTORY);
+ cifs_buf_release(search_info.ntwrk_buf_start);
+ } else if (!full_path[0]) {
+ /*
+ * CIFSFindFirst() does not work on root path if the
+ * root path was exported on the server from the top
+ * level path (drive letter).
+ */
+ rc = -EOPNOTSUPP;
+ }
+ }
+
+ /*
+ * If everything failed then fallback to the legacy SMB command
+ * SMB_COM_QUERY_INFORMATION which works with all servers, but
+ * provide just few information.
+ */
+ if ((rc == -EOPNOTSUPP || rc == -EINVAL) && !non_unicode_wildcard) {
rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls,
cifs_remap(cifs_sb));
data->adjust_tz = true;
+ } else if ((rc == -EOPNOTSUPP || rc == -EINVAL) && non_unicode_wildcard) {
+ /* Path with non-UNICODE wildcard character cannot exist. */
+ rc = -ENOENT;
}
if (!rc) {
- int tmprc;
- int oplock = 0;
- struct cifs_fid fid;
- struct cifs_open_parms oparms;
-
move_cifs_info_to_smb2(&data->fi, &fi);
+ data->reparse_point = le32_to_cpu(fi.Attributes) & ATTR_REPARSE_POINT;
+ }
- if (!(le32_to_cpu(fi.Attributes) & ATTR_REPARSE))
- return 0;
-
- oparms = (struct cifs_open_parms) {
- .tcon = tcon,
- .cifs_sb = cifs_sb,
- .desired_access = FILE_READ_ATTRIBUTES,
- .create_options = cifs_create_options(cifs_sb, 0),
- .disposition = FILE_OPEN,
- .path = full_path,
- .fid = &fid,
- };
+#ifdef CONFIG_CIFS_XATTR
+ /*
+ * For non-symlink WSL reparse points it is required to fetch
+ * EA $LXMOD which contains in its S_DT part the mandatory file type.
+ */
+ if (!rc && data->reparse_point) {
+ struct smb2_file_full_ea_info *ea;
+ u32 next = 0;
+
+ ea = (struct smb2_file_full_ea_info *)data->wsl.eas;
+ do {
+ ea = (void *)((u8 *)ea + next);
+ next = le32_to_cpu(ea->next_entry_offset);
+ } while (next);
+ if (le16_to_cpu(ea->ea_value_length)) {
+ ea->next_entry_offset = cpu_to_le32(ALIGN(sizeof(*ea) +
+ ea->ea_name_length + 1 +
+ le16_to_cpu(ea->ea_value_length), 4));
+ ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset));
+ }
+
+ rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE,
+ &ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1],
+ SMB2_WSL_XATTR_MODE_SIZE, cifs_sb);
+ if (rc == SMB2_WSL_XATTR_MODE_SIZE) {
+ ea->next_entry_offset = cpu_to_le32(0);
+ ea->flags = 0;
+ ea->ea_name_length = SMB2_WSL_XATTR_NAME_LEN;
+ ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_MODE_SIZE);
+ memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_MODE, SMB2_WSL_XATTR_NAME_LEN + 1);
+ data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
+ SMB2_WSL_XATTR_MODE_SIZE, 4);
+ rc = 0;
+ } else if (rc >= 0) {
+ /* It is an error if EA $LXMOD has wrong size. */
+ rc = -EINVAL;
+ } else {
+ /*
+ * In all other cases ignore error if fetching
+ * of EA $LXMOD failed. It is needed only for
+ * non-symlink WSL reparse points and wsl_to_fattr()
+ * handle the case when EA is missing.
+ */
+ rc = 0;
+ }
+ }
+
+ /*
+ * For WSL CHR and BLK reparse points it is required to fetch
+ * EA $LXDEV which contains major and minor device numbers.
+ */
+ if (!rc && data->reparse_point) {
+ struct smb2_file_full_ea_info *ea;
+ u32 next = 0;
+
+ ea = (struct smb2_file_full_ea_info *)data->wsl.eas;
+ do {
+ ea = (void *)((u8 *)ea + next);
+ next = le32_to_cpu(ea->next_entry_offset);
+ } while (next);
+ if (le16_to_cpu(ea->ea_value_length)) {
+ ea->next_entry_offset = cpu_to_le32(ALIGN(sizeof(*ea) +
+ ea->ea_name_length + 1 +
+ le16_to_cpu(ea->ea_value_length), 4));
+ ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset));
+ }
- /* Need to check if this is a symbolic link or not */
- tmprc = CIFS_open(xid, &oparms, &oplock, NULL);
- if (tmprc == -EOPNOTSUPP)
- data->symlink = true;
- else if (tmprc == 0)
- CIFSSMBClose(xid, tcon, fid.netfid);
+ rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_DEV,
+ &ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1],
+ SMB2_WSL_XATTR_DEV_SIZE, cifs_sb);
+ if (rc == SMB2_WSL_XATTR_DEV_SIZE) {
+ ea->next_entry_offset = cpu_to_le32(0);
+ ea->flags = 0;
+ ea->ea_name_length = SMB2_WSL_XATTR_NAME_LEN;
+ ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE);
+ memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1);
+ data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
+ SMB2_WSL_XATTR_MODE_SIZE, 4);
+ rc = 0;
+ } else if (rc >= 0) {
+ /* It is an error if EA $LXDEV has wrong size. */
+ rc = -EINVAL;
+ } else {
+ /*
+ * In all other cases ignore error if fetching
+ * of EA $LXDEV failed. It is needed only for
+ * WSL CHR and BLK reparse points and wsl_to_fattr()
+ * handle the case when EA is missing.
+ */
+ rc = 0;
+ }
}
+#endif
return rc;
}
@@ -614,7 +807,13 @@ static int cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
* There may be higher info levels that work but are there Windows
* server or network appliances for which IndexNumber field is not
* guaranteed unique?
+ *
+ * CIFSGetSrvInodeNumber() uses SMB_QUERY_FILE_INTERNAL_INFO
+ * which is SMB PASSTHROUGH level therefore check for capability.
+ * Note that this function can be called with tcon == NULL.
*/
+ if (tcon && !(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU))
+ return -EOPNOTSUPP;
return CIFSGetSrvInodeNumber(xid, tcon, full_path, uniqueid,
cifs_sb->local_nls,
cifs_remap(cifs_sb));
@@ -626,6 +825,13 @@ static int cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
int rc;
FILE_ALL_INFO fi = {};
+ /*
+ * CIFSSMBQFileInfo() for non-NT servers returns bogus data in
+ * Attributes fields. So do not use this command for non-NT servers.
+ */
+ if (!(tcon->ses->capabilities & CAP_NT_SMBS))
+ return -EOPNOTSUPP;
+
if (cfile->symlink_target) {
data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!data->symlink_target)
@@ -716,6 +922,11 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
info.Attributes = cpu_to_le32(dosattrs);
rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls,
cifs_sb);
+ if (rc == -EOPNOTSUPP || rc == -EINVAL)
+ rc = SMBSetInformation(xid, tcon, full_path,
+ info.Attributes,
+ 0 /* do not change write time */,
+ cifs_sb->local_nls, cifs_sb);
if (rc == 0)
cifsInode->cifsAttrs = dosattrs;
}
@@ -796,6 +1007,9 @@ smb_set_file_info(struct inode *inode, const char *full_path,
struct cifs_fid fid;
struct cifs_open_parms oparms;
struct cifsFileInfo *open_file;
+ FILE_BASIC_INFO new_buf;
+ struct cifs_open_info_data query_data;
+ __le64 write_time = buf->LastWriteTime;
struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink = NULL;
@@ -803,20 +1017,58 @@ smb_set_file_info(struct inode *inode, const char *full_path,
/* if the file is already open for write, just use that fileid */
open_file = find_writable_file(cinode, FIND_WR_FSUID_ONLY);
+
if (open_file) {
fid.netfid = open_file->fid.netfid;
netpid = open_file->pid;
tcon = tlink_tcon(open_file->tlink);
- goto set_via_filehandle;
+ } else {
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink)) {
+ rc = PTR_ERR(tlink);
+ tlink = NULL;
+ goto out;
+ }
+ tcon = tlink_tcon(tlink);
}
- tlink = cifs_sb_tlink(cifs_sb);
- if (IS_ERR(tlink)) {
- rc = PTR_ERR(tlink);
- tlink = NULL;
- goto out;
+ /*
+ * Non-NT servers interprets zero time value in SMB_SET_FILE_BASIC_INFO
+ * over TRANS2_SET_FILE_INFORMATION as a valid time value. NT servers
+ * interprets zero time value as do not change existing value on server.
+ * API of ->set_file_info() callback expects that zero time value has
+ * the NT meaning - do not change. Therefore if server is non-NT and
+ * some time values in "buf" are zero, then fetch missing time values.
+ */
+ if (!(tcon->ses->capabilities & CAP_NT_SMBS) &&
+ (!buf->CreationTime || !buf->LastAccessTime ||
+ !buf->LastWriteTime || !buf->ChangeTime)) {
+ rc = cifs_query_path_info(xid, tcon, cifs_sb, full_path, &query_data);
+ if (rc) {
+ if (open_file) {
+ cifsFileInfo_put(open_file);
+ open_file = NULL;
+ }
+ goto out;
+ }
+ /*
+ * Original write_time from buf->LastWriteTime is preserved
+ * as SMBSetInformation() interprets zero as do not change.
+ */
+ new_buf = *buf;
+ buf = &new_buf;
+ if (!buf->CreationTime)
+ buf->CreationTime = query_data.fi.CreationTime;
+ if (!buf->LastAccessTime)
+ buf->LastAccessTime = query_data.fi.LastAccessTime;
+ if (!buf->LastWriteTime)
+ buf->LastWriteTime = query_data.fi.LastWriteTime;
+ if (!buf->ChangeTime)
+ buf->ChangeTime = query_data.fi.ChangeTime;
}
- tcon = tlink_tcon(tlink);
+
+ if (open_file)
+ goto set_via_filehandle;
rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls,
cifs_sb);
@@ -831,14 +1083,51 @@ smb_set_file_info(struct inode *inode, const char *full_path,
.tcon = tcon,
.cifs_sb = cifs_sb,
.desired_access = SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
- .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
+ .create_options = cifs_create_options(cifs_sb, 0),
.disposition = FILE_OPEN,
.path = full_path,
.fid = &fid,
};
- cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n");
- rc = CIFS_open(xid, &oparms, &oplock, NULL);
+ if (S_ISDIR(inode->i_mode) && !(tcon->ses->capabilities & CAP_NT_SMBS)) {
+ /* Opening directory path is not possible on non-NT servers. */
+ rc = -EOPNOTSUPP;
+ } else {
+ /*
+ * Use cifs_open_file() instead of CIFS_open() as the
+ * cifs_open_file() selects the correct function which
+ * works also on non-NT servers.
+ */
+ rc = cifs_open_file(xid, &oparms, &oplock, NULL);
+ /*
+ * Opening path for writing on non-NT servers is not
+ * possible when the read-only attribute is already set.
+ * Non-NT server in this case returns -EACCES. For those
+ * servers the only possible way how to clear the read-only
+ * bit is via SMB_COM_SETATTR command.
+ */
+ if (rc == -EACCES &&
+ (cinode->cifsAttrs & ATTR_READONLY) &&
+ le32_to_cpu(buf->Attributes) != 0 && /* 0 = do not change attrs */
+ !(le32_to_cpu(buf->Attributes) & ATTR_READONLY) &&
+ !(tcon->ses->capabilities & CAP_NT_SMBS))
+ rc = -EOPNOTSUPP;
+ }
+
+ /* Fallback to SMB_COM_SETATTR command when absolutely needed. */
+ if (rc == -EOPNOTSUPP) {
+ cifs_dbg(FYI, "calling SetInformation since SetPathInfo for attrs/times not supported by this server\n");
+ rc = SMBSetInformation(xid, tcon, full_path,
+ buf->Attributes != 0 ? buf->Attributes : cpu_to_le32(cinode->cifsAttrs),
+ write_time,
+ cifs_sb->local_nls, cifs_sb);
+ if (rc == 0)
+ cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
+ else
+ rc = -EACCES;
+ goto out;
+ }
+
if (rc != 0) {
if (rc == -EIO)
rc = -EINVAL;
@@ -846,6 +1135,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,
}
netpid = current->tgid;
+ cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for attrs/times not supported by this server\n");
set_via_filehandle:
rc = CIFSSMBSetFileInfo(xid, tcon, buf, fid.netfid, netpid);
@@ -856,6 +1146,21 @@ set_via_filehandle:
CIFSSMBClose(xid, tcon, fid.netfid);
else
cifsFileInfo_put(open_file);
+
+ /*
+ * Setting the read-only bit is not honored on non-NT servers when done
+ * via open-semantics. So for setting it, use SMB_COM_SETATTR command.
+ * This command works only after the file is closed, so use it only when
+ * operation was called without the filehandle.
+ */
+ if (open_file == NULL &&
+ !(tcon->ses->capabilities & CAP_NT_SMBS) &&
+ le32_to_cpu(buf->Attributes) & ATTR_READONLY) {
+ SMBSetInformation(xid, tcon, full_path,
+ buf->Attributes,
+ 0 /* do not change write time */,
+ cifs_sb->local_nls, cifs_sb);
+ }
out:
if (tlink != NULL)
cifs_put_tlink(tlink);
@@ -993,18 +1298,13 @@ static int cifs_query_symlink(const unsigned int xid,
return rc;
}
-static int cifs_parse_reparse_point(struct cifs_sb_info *cifs_sb,
- struct kvec *rsp_iov,
- struct cifs_open_info_data *data)
+static struct reparse_data_buffer *cifs_get_reparse_point_buffer(const struct kvec *rsp_iov,
+ u32 *plen)
{
- struct reparse_data_buffer *buf;
TRANSACT_IOCTL_RSP *io = rsp_iov->iov_base;
- bool unicode = !!(io->hdr.Flags2 & SMBFLG2_UNICODE);
- u32 plen = le16_to_cpu(io->ByteCount);
-
- buf = (struct reparse_data_buffer *)((__u8 *)&io->hdr.Protocol +
- le32_to_cpu(io->DataOffset));
- return parse_reparse_point(buf, plen, cifs_sb, unicode, data);
+ *plen = le16_to_cpu(io->ByteCount);
+ return (struct reparse_data_buffer *)((__u8 *)&io->hdr.Protocol +
+ le32_to_cpu(io->DataOffset));
}
static bool
@@ -1074,21 +1374,71 @@ cifs_make_node(unsigned int xid, struct inode *inode,
if (rc == 0)
d_instantiate(dentry, newinode);
return rc;
+ } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
+ /*
+ * Check if mounted with mount parm 'sfu' mount parm.
+ * SFU emulation should work with all servers
+ * and was used by default in earlier versions of Windows.
+ */
+ return cifs_sfu_make_node(xid, inode, dentry, tcon,
+ full_path, mode, dev);
+ } else if (CIFS_REPARSE_SUPPORT(tcon)) {
+ /*
+ * mknod via reparse points requires server support for
+ * storing reparse points, which is available since
+ * Windows 2000, but was not widely used until release
+ * of Windows Server 2012 by the Windows NFS server.
+ */
+ return mknod_reparse(xid, inode, dentry, tcon,
+ full_path, mode, dev);
+ } else {
+ return -EOPNOTSUPP;
}
- /*
- * Check if mounted with mount parm 'sfu' mount parm.
- * SFU emulation should work with all servers, but only
- * supports block and char device, socket & fifo,
- * and was used by default in earlier versions of Windows
- */
- if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
- return -EPERM;
- return cifs_sfu_make_node(xid, inode, dentry, tcon,
- full_path, mode, dev);
+}
+
+static bool
+cifs_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
+{
+ struct smb_hdr *shdr = (struct smb_hdr *)buf;
+ struct TCP_Server_Info *pserver;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+
+ if (shdr->Flags2 & SMBFLG2_ERR_STATUS) {
+ if (shdr->Status.CifsError != cpu_to_le32(NT_STATUS_NETWORK_NAME_DELETED))
+ return false;
+ } else {
+ if (shdr->Status.DosError.ErrorClass != ERRSRV ||
+ shdr->Status.DosError.Error != cpu_to_le16(ERRinvtid))
+ return false;
+ }
+
+ /* If server is a channel, select the primary channel */
+ pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
+ if (cifs_ses_exiting(ses))
+ continue;
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+ if (tcon->tid == shdr->Tid) {
+ spin_lock(&tcon->tc_lock);
+ tcon->need_reconnect = true;
+ spin_unlock(&tcon->tc_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ pr_warn_once("Server share %s deleted.\n",
+ tcon->tree_name);
+ return true;
+ }
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ return false;
}
struct smb_version_operations smb1_operations = {
- .send_cancel = send_nt_cancel,
+ .send_cancel = cifs_send_cancel,
.compare_fids = cifs_compare_fids,
.setup_request = cifs_setup_request,
.setup_async_request = cifs_setup_async_request,
@@ -1112,8 +1462,8 @@ struct smb_version_operations smb1_operations = {
.check_trans2 = cifs_check_trans2,
.need_neg = cifs_need_neg,
.negotiate = cifs_negotiate,
- .negotiate_wsize = cifs_negotiate_wsize,
- .negotiate_rsize = cifs_negotiate_rsize,
+ .negotiate_wsize = smb1_negotiate_wsize,
+ .negotiate_rsize = smb1_negotiate_rsize,
.sess_setup = CIFS_SessSetup,
.logoff = CIFSSMBLogoff,
.tree_connect = CIFSTCon,
@@ -1139,7 +1489,8 @@ struct smb_version_operations smb1_operations = {
.rename = CIFSSMBRename,
.create_hardlink = CIFSCreateHardLink,
.query_symlink = cifs_query_symlink,
- .parse_reparse_point = cifs_parse_reparse_point,
+ .get_reparse_point_buffer = cifs_get_reparse_point_buffer,
+ .create_reparse_inode = cifs_create_reparse_inode,
.open = cifs_open_file,
.set_fid = cifs_set_fid,
.close = cifs_close_file,
@@ -1171,6 +1522,7 @@ struct smb_version_operations smb1_operations = {
.get_acl_by_fid = get_cifs_acl_by_fid,
.set_acl = set_cifs_acl,
.make_node = cifs_make_node,
+ .is_network_name_deleted = cifs_is_network_name_deleted,
};
struct smb_version_values smb1_values = {
@@ -1180,7 +1532,6 @@ struct smb_version_values smb1_values = {
.exclusive_lock_type = 0,
.shared_lock_type = LOCKING_ANDX_SHARED_LOCK,
.unlock_lock_type = 0,
- .header_preamble_size = 4,
.header_size = sizeof(struct smb_hdr),
.max_header_size = MAX_CIFS_HDR_SIZE,
.read_rsp_size = sizeof(READ_RSP),
@@ -1188,6 +1539,7 @@ struct smb_version_values smb1_values = {
.cap_unix = CAP_UNIX,
.cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND,
.cap_large_files = CAP_LARGE_FILES,
+ .cap_unicode = CAP_UNICODE,
.signing_enabled = SECMODE_SIGN_ENABLED,
.signing_required = SECMODE_SIGN_REQUIRED,
};
diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
index e301349b0078..7f11ae6bb785 100644
--- a/fs/smb/client/smb2file.c
+++ b/fs/smb/client/smb2file.c
@@ -42,14 +42,14 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len);
do {
if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) {
- sym = (struct smb2_symlink_err_rsp *)&p->ErrorContextData;
+ sym = (struct smb2_symlink_err_rsp *)p->ErrorContextData;
break;
}
cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n",
__func__, le32_to_cpu(p->ErrorId));
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
- p = (struct smb2_error_context_rsp *)((u8 *)&p->ErrorContextData + len);
+ p = (struct smb2_error_context_rsp *)(p->ErrorContextData + len);
} while (p < end);
} else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) &&
iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) {
@@ -63,12 +63,58 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
return sym;
}
-int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path)
+int smb2_fix_symlink_target_type(char **target, bool directory, struct cifs_sb_info *cifs_sb)
+{
+ char *buf;
+ int len;
+
+ /*
+ * POSIX server does not distinguish between symlinks to file and
+ * symlink directory. So nothing is needed to fix on the client side.
+ */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
+ return 0;
+
+ if (!*target)
+ return smb_EIO(smb_eio_trace_null_pointers);
+
+ len = strlen(*target);
+ if (!len)
+ return smb_EIO1(smb_eio_trace_sym_target_len, len);
+
+ /*
+ * If this is directory symlink and it does not have trailing slash then
+ * append it. Trailing slash simulates Windows/SMB behavior which do not
+ * allow resolving directory symlink to file.
+ */
+ if (directory && (*target)[len-1] != '/') {
+ buf = krealloc(*target, len+2, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ buf[len] = '/';
+ buf[len+1] = '\0';
+ *target = buf;
+ len++;
+ }
+
+ /*
+ * If this is a file (non-directory) symlink and it points to path name
+ * with trailing slash then this is an invalid symlink because file name
+ * cannot contain slash character. File name with slash is invalid on
+ * both Windows and Linux systems. So return an error for such symlink.
+ */
+ if (!directory && (*target)[len-1] == '/')
+ return smb_EIO(smb_eio_trace_sym_slash);
+
+ return 0;
+}
+
+int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov,
+ const char *full_path, char **path)
{
struct smb2_symlink_err_rsp *sym;
unsigned int sub_offs, sub_len;
unsigned int print_offs, print_len;
- char *s;
if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path)
return -EINVAL;
@@ -86,18 +132,16 @@ int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec
iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len)
return -EINVAL;
- s = cifs_strndup_from_utf16((char *)sym->PathBuffer + sub_offs, sub_len, true,
- cifs_sb->local_nls);
- if (!s)
- return -ENOMEM;
- convert_delimiter(s, '/');
- cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, s);
-
- *path = s;
- return 0;
+ return smb2_parse_native_symlink(path,
+ (char *)sym->PathBuffer + sub_offs,
+ sub_len,
+ le32_to_cpu(sym->Flags) & SYMLINK_FLAG_RELATIVE,
+ full_path,
+ cifs_sb);
}
-int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf)
+int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
+ __u32 *oplock, void *buf)
{
int rc;
__le16 *smb2_path;
@@ -109,16 +153,35 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32
int err_buftype = CIFS_NO_BUFFER;
struct cifs_fid *fid = oparms->fid;
struct network_resiliency_req nr_ioctl_req;
+ bool retry_without_read_attributes = false;
smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb);
if (smb2_path == NULL)
return -ENOMEM;
- oparms->desired_access |= FILE_READ_ATTRIBUTES;
+ /*
+ * GENERIC_READ, GENERIC_EXECUTE, GENERIC_ALL and MAXIMUM_ALLOWED
+ * contains also FILE_READ_ATTRIBUTES access right. So do not append
+ * FILE_READ_ATTRIBUTES when not needed and prevent calling code path
+ * for retry_without_read_attributes.
+ */
+ if (!(oparms->desired_access & FILE_READ_ATTRIBUTES) &&
+ !(oparms->desired_access & GENERIC_READ) &&
+ !(oparms->desired_access & GENERIC_EXECUTE) &&
+ !(oparms->desired_access & GENERIC_ALL) &&
+ !(oparms->desired_access & MAXIMUM_ALLOWED)) {
+ oparms->desired_access |= FILE_READ_ATTRIBUTES;
+ retry_without_read_attributes = true;
+ }
smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
&err_buftype);
+ if (rc == -EACCES && retry_without_read_attributes) {
+ oparms->desired_access &= ~FILE_READ_ATTRIBUTES;
+ rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
+ &err_buftype);
+ }
if (rc && data) {
struct smb2_hdr *hdr = err_iov.iov_base;
@@ -126,6 +189,7 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32
goto out;
if (hdr->Status == STATUS_STOPPED_ON_SYMLINK) {
rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov,
+ oparms->path,
&data->symlink_target);
if (!rc) {
memset(smb2_data, 0, sizeof(*smb2_data));
@@ -134,6 +198,11 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32
NULL, NULL, NULL);
oparms->create_options &= ~OPEN_REPARSE_POINT;
}
+ if (!rc) {
+ bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
+ rc = smb2_fix_symlink_target_type(&data->symlink_target,
+ directory, oparms->cifs_sb);
+ }
}
}
diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h
index 2466e6155136..e56e4d402f13 100644
--- a/fs/smb/client/smb2glob.h
+++ b/fs/smb/client/smb2glob.h
@@ -30,14 +30,14 @@ enum smb2_compound_ops {
SMB2_OP_QUERY_DIR,
SMB2_OP_MKDIR,
SMB2_OP_RENAME,
- SMB2_OP_DELETE,
SMB2_OP_HARDLINK,
SMB2_OP_SET_EOF,
- SMB2_OP_RMDIR,
+ SMB2_OP_UNLINK,
SMB2_OP_POSIX_QUERY_INFO,
SMB2_OP_SET_REPARSE,
SMB2_OP_GET_REPARSE,
SMB2_OP_QUERY_WSL_EA,
+ SMB2_OP_OPEN_QUERY,
};
/* Used when constructing chained read requests. */
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index e49d0c25eb03..2ded3246600c 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -21,7 +21,6 @@
#include "cifs_unicode.h"
#include "fscache.h"
#include "smb2glob.h"
-#include "smb2pdu.h"
#include "smb2proto.h"
#include "cached_dir.h"
#include "../common/smb2status.h"
@@ -31,16 +30,20 @@ static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)
struct reparse_data_buffer *buf;
struct smb2_ioctl_rsp *io = iov->iov_base;
u32 off, count, len;
+ u16 rdlen;
count = le32_to_cpu(io->OutputCount);
off = le32_to_cpu(io->OutputOffset);
if (check_add_overflow(off, count, &len) || len > iov->iov_len)
- return ERR_PTR(-EIO);
+ return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_overlong,
+ off, count));
buf = (struct reparse_data_buffer *)((u8 *)io + off);
len = sizeof(*buf);
- if (count < len || count < le16_to_cpu(buf->ReparseDataLength) + len)
- return ERR_PTR(-EIO);
+ rdlen = le16_to_cpu(buf->ReparseDataLength);
+
+ if (count < len || count < rdlen + len)
+ return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, rdlen));
return buf;
}
@@ -50,7 +53,7 @@ static inline __u32 file_create_options(struct dentry *dentry)
if (dentry) {
ci = CIFS_I(d_inode(dentry));
- if (ci->cifsAttrs & ATTR_REPARSE)
+ if (ci->cifsAttrs & ATTR_REPARSE_POINT)
return OPEN_REPARSE_POINT;
}
return 0;
@@ -176,27 +179,28 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
struct kvec *out_iov, int *out_buftype, struct dentry *dentry)
{
- struct reparse_data_buffer *rbuf;
+ struct smb2_create_rsp *create_rsp = NULL;
+ struct smb2_query_info_rsp *qi_rsp = NULL;
struct smb2_compound_vars *vars = NULL;
- struct kvec *rsp_iov, *iov;
- struct smb_rqst *rqst;
- int rc;
- __le16 *utf16_path = NULL;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
- struct cifs_fid fid;
+ struct cifs_open_info_data *idata;
struct cifs_ses *ses = tcon->ses;
+ struct reparse_data_buffer *rbuf;
struct TCP_Server_Info *server;
- int num_rqst = 0, i;
int resp_buftype[MAX_COMPOUND];
- struct smb2_query_info_rsp *qi_rsp = NULL;
- struct cifs_open_info_data *idata;
+ int retries = 0, cur_sleep = 1;
+ __u8 delete_pending[8] = {1,};
+ struct kvec *rsp_iov, *iov;
struct inode *inode = NULL;
- int flags = 0;
- __u8 delete_pending[8] = {1, 0, 0, 0, 0, 0, 0, 0};
+ __le16 *utf16_path = NULL;
+ struct smb_rqst *rqst;
unsigned int size[2];
- void *data[2];
+ struct cifs_fid fid;
+ int num_rqst = 0, i;
unsigned int len;
- int retries = 0, cur_sleep = 1;
+ int tmp_rc, rc;
+ int flags = 0;
+ void *data[2];
replay_again:
/* reinitialize for possible replay */
@@ -206,8 +210,10 @@ replay_again:
server = cifs_pick_channel(ses);
vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
- if (vars == NULL)
- return -ENOMEM;
+ if (vars == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
rqst = &vars->rqst[0];
rsp_iov = &vars->rsp_iov[0];
@@ -265,7 +271,13 @@ replay_again:
num_rqst++;
rc = 0;
- for (i = 0; i < num_cmds; i++) {
+ i = 0;
+
+ /* Skip the leading explicit OPEN operation */
+ if (num_cmds > 0 && cmds[0] == SMB2_OP_OPEN_QUERY)
+ i++;
+
+ for (; i < num_cmds; i++) {
/* Operation */
switch (cmds[i]) {
case SMB2_OP_QUERY_INFO:
@@ -298,8 +310,8 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_query_info_compound_enter(xid, ses->Suid,
- tcon->tid, full_path);
+ trace_smb3_query_info_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
case SMB2_OP_POSIX_QUERY_INFO:
rqst[num_rqst].rq_iov = &vars->qi_iov;
@@ -334,36 +346,50 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_posix_query_info_compound_enter(xid, ses->Suid,
- tcon->tid, full_path);
- break;
- case SMB2_OP_DELETE:
- trace_smb3_delete_enter(xid, ses->Suid, tcon->tid, full_path);
+ trace_smb3_posix_query_info_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
case SMB2_OP_MKDIR:
/*
* Directories are created through parameters in the
* SMB2_open() call.
*/
- trace_smb3_mkdir_enter(xid, ses->Suid, tcon->tid, full_path);
+ trace_smb3_mkdir_enter(xid, tcon->tid, ses->Suid, full_path);
break;
- case SMB2_OP_RMDIR:
- rqst[num_rqst].rq_iov = &vars->si_iov[0];
+ case SMB2_OP_UNLINK:
+ rqst[num_rqst].rq_iov = vars->unlink_iov;
rqst[num_rqst].rq_nvec = 1;
size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
data[0] = &delete_pending[0];
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst], COMPOUND_FID,
- COMPOUND_FID, current->tgid,
- FILE_DISPOSITION_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- if (rc)
+ if (cfile) {
+ rc = SMB2_set_info_init(tcon, server,
+ &rqst[num_rqst],
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ current->tgid,
+ FILE_DISPOSITION_INFORMATION,
+ SMB2_O_INFO_FILE, 0,
+ data, size);
+ } else {
+ rc = SMB2_set_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMPOUND_FID,
+ COMPOUND_FID,
+ current->tgid,
+ FILE_DISPOSITION_INFORMATION,
+ SMB2_O_INFO_FILE, 0,
+ data, size);
+ }
+ if (!rc && (!cfile || num_rqst > 1)) {
+ smb2_set_next_command(tcon, &rqst[num_rqst]);
+ smb2_set_related(&rqst[num_rqst]);
+ } else if (rc) {
goto finished;
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst++]);
- trace_smb3_rmdir_enter(xid, ses->Suid, tcon->tid, full_path);
+ }
+ num_rqst++;
+ trace_smb3_unlink_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_EOF:
rqst[num_rqst].rq_iov = &vars->si_iov[0];
@@ -398,7 +424,7 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_set_eof_enter(xid, ses->Suid, tcon->tid, full_path);
+ trace_smb3_set_eof_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_INFO:
rqst[num_rqst].rq_iov = &vars->si_iov[0];
@@ -429,11 +455,11 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_set_info_compound_enter(xid, ses->Suid,
- tcon->tid, full_path);
+ trace_smb3_set_info_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
case SMB2_OP_RENAME:
- rqst[num_rqst].rq_iov = &vars->si_iov[0];
+ rqst[num_rqst].rq_iov = vars->rename_iov;
rqst[num_rqst].rq_nvec = 2;
len = in_iov[i].iov_len;
@@ -469,7 +495,7 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_rename_enter(xid, ses->Suid, tcon->tid, full_path);
+ trace_smb3_rename_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_HARDLINK:
rqst[num_rqst].rq_iov = &vars->si_iov[0];
@@ -496,7 +522,7 @@ replay_again:
goto finished;
smb2_set_next_command(tcon, &rqst[num_rqst]);
smb2_set_related(&rqst[num_rqst++]);
- trace_smb3_hardlink_enter(xid, ses->Suid, tcon->tid, full_path);
+ trace_smb3_hardlink_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_REPARSE:
rqst[num_rqst].rq_iov = vars->io_iov;
@@ -523,8 +549,8 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_set_reparse_compound_enter(xid, ses->Suid,
- tcon->tid, full_path);
+ trace_smb3_set_reparse_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
case SMB2_OP_GET_REPARSE:
rqst[num_rqst].rq_iov = vars->io_iov;
@@ -549,8 +575,8 @@ replay_again:
goto finished;
}
num_rqst++;
- trace_smb3_get_reparse_compound_enter(xid, ses->Suid,
- tcon->tid, full_path);
+ trace_smb3_get_reparse_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
case SMB2_OP_QUERY_WSL_EA:
rqst[num_rqst].rq_iov = &vars->ea_iov;
@@ -584,6 +610,8 @@ replay_again:
goto finished;
}
num_rqst++;
+ trace_smb3_query_wsl_ea_compound_enter(xid, tcon->tid,
+ ses->Suid, full_path);
break;
default:
cifs_dbg(VFS, "Invalid command\n");
@@ -637,10 +665,41 @@ finished:
tcon->need_reconnect = true;
}
+ tmp_rc = rc;
+
+ if (rc == 0 && num_cmds > 0 && cmds[0] == SMB2_OP_OPEN_QUERY) {
+ create_rsp = rsp_iov[0].iov_base;
+ idata = in_iov[0].iov_base;
+ idata->fi.CreationTime = create_rsp->CreationTime;
+ idata->fi.LastAccessTime = create_rsp->LastAccessTime;
+ idata->fi.LastWriteTime = create_rsp->LastWriteTime;
+ idata->fi.ChangeTime = create_rsp->ChangeTime;
+ idata->fi.Attributes = create_rsp->FileAttributes;
+ idata->fi.AllocationSize = create_rsp->AllocationSize;
+ idata->fi.EndOfFile = create_rsp->EndofFile;
+ if (le32_to_cpu(idata->fi.NumberOfLinks) == 0)
+ idata->fi.NumberOfLinks = cpu_to_le32(1); /* dummy value */
+ idata->fi.DeletePending = 0; /* successful open = not delete pending */
+ idata->fi.Directory = !!(le32_to_cpu(create_rsp->FileAttributes) & ATTR_DIRECTORY);
+
+ /* smb2_parse_contexts() fills idata->fi.IndexNumber */
+ rc = smb2_parse_contexts(server, &rsp_iov[0], &oparms->fid->epoch,
+ oparms->fid->lease_key, &oplock, &idata->fi, NULL);
+ if (rc)
+ cifs_dbg(VFS, "rc: %d parsing context of compound op\n", rc);
+ }
+
for (i = 0; i < num_cmds; i++) {
+ char *buf = rsp_iov[i + 1].iov_base;
+
+ if (buf && resp_buftype[i + 1] != CIFS_NO_BUFFER)
+ rc = server->ops->map_error(buf, false);
+ else
+ rc = tmp_rc;
switch (cmds[i]) {
case SMB2_OP_QUERY_INFO:
idata = in_iov[i].iov_base;
+ idata->contains_posix_file_info = false;
if (rc == 0 && cfile && cfile->symlink_target) {
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!idata->symlink_target)
@@ -656,14 +715,15 @@ finished:
}
SMB2_query_info_free(&rqst[num_rqst++]);
if (rc)
- trace_smb3_query_info_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_query_info_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
else
- trace_smb3_query_info_compound_done(xid, ses->Suid,
- tcon->tid);
+ trace_smb3_query_info_compound_done(xid, tcon->tid,
+ ses->Suid);
break;
case SMB2_OP_POSIX_QUERY_INFO:
idata = in_iov[i].iov_base;
+ idata->contains_posix_file_info = true;
if (rc == 0 && cfile && cfile->symlink_target) {
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!idata->symlink_target)
@@ -683,75 +743,62 @@ finished:
SMB2_query_info_free(&rqst[num_rqst++]);
if (rc)
- trace_smb3_posix_query_info_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_posix_query_info_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
else
- trace_smb3_posix_query_info_compound_done(xid, ses->Suid,
- tcon->tid);
- break;
- case SMB2_OP_DELETE:
- if (rc)
- trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc);
- else {
- /*
- * If dentry (hence, inode) is NULL, lease break is going to
- * take care of degrading leases on handles for deleted files.
- */
- if (inode)
- cifs_mark_open_handles_for_deleted_file(inode, full_path);
- trace_smb3_delete_done(xid, ses->Suid, tcon->tid);
- }
+ trace_smb3_posix_query_info_compound_done(xid, tcon->tid,
+ ses->Suid);
break;
case SMB2_OP_MKDIR:
if (rc)
- trace_smb3_mkdir_err(xid, ses->Suid, tcon->tid, rc);
+ trace_smb3_mkdir_err(xid, tcon->tid, ses->Suid, rc);
else
- trace_smb3_mkdir_done(xid, ses->Suid, tcon->tid);
+ trace_smb3_mkdir_done(xid, tcon->tid, ses->Suid);
break;
case SMB2_OP_HARDLINK:
if (rc)
- trace_smb3_hardlink_err(xid, ses->Suid, tcon->tid, rc);
+ trace_smb3_hardlink_err(xid, tcon->tid, ses->Suid, rc);
else
- trace_smb3_hardlink_done(xid, ses->Suid, tcon->tid);
+ trace_smb3_hardlink_done(xid, tcon->tid, ses->Suid);
SMB2_set_info_free(&rqst[num_rqst++]);
break;
case SMB2_OP_RENAME:
if (rc)
- trace_smb3_rename_err(xid, ses->Suid, tcon->tid, rc);
+ trace_smb3_rename_err(xid, tcon->tid, ses->Suid, rc);
else
- trace_smb3_rename_done(xid, ses->Suid, tcon->tid);
+ trace_smb3_rename_done(xid, tcon->tid, ses->Suid);
SMB2_set_info_free(&rqst[num_rqst++]);
break;
- case SMB2_OP_RMDIR:
- if (rc)
- trace_smb3_rmdir_err(xid, ses->Suid, tcon->tid, rc);
+ case SMB2_OP_UNLINK:
+ if (!rc)
+ trace_smb3_unlink_done(xid, tcon->tid, ses->Suid);
else
- trace_smb3_rmdir_done(xid, ses->Suid, tcon->tid);
+ trace_smb3_unlink_err(xid, tcon->tid, ses->Suid, rc);
SMB2_set_info_free(&rqst[num_rqst++]);
break;
case SMB2_OP_SET_EOF:
if (rc)
- trace_smb3_set_eof_err(xid, ses->Suid, tcon->tid, rc);
+ trace_smb3_set_eof_err(xid, tcon->tid, ses->Suid, rc);
else
- trace_smb3_set_eof_done(xid, ses->Suid, tcon->tid);
+ trace_smb3_set_eof_done(xid, tcon->tid, ses->Suid);
SMB2_set_info_free(&rqst[num_rqst++]);
break;
case SMB2_OP_SET_INFO:
if (rc)
- trace_smb3_set_info_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_set_info_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
else
- trace_smb3_set_info_compound_done(xid, ses->Suid,
- tcon->tid);
+ trace_smb3_set_info_compound_done(xid, tcon->tid,
+ ses->Suid);
SMB2_set_info_free(&rqst[num_rqst++]);
break;
case SMB2_OP_SET_REPARSE:
if (rc) {
- trace_smb3_set_reparse_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_set_reparse_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
} else {
- trace_smb3_set_reparse_compound_done(xid, ses->Suid,
- tcon->tid);
+ trace_smb3_set_reparse_compound_done(xid, tcon->tid,
+ ses->Suid);
}
SMB2_ioctl_free(&rqst[num_rqst++]);
break;
@@ -761,27 +808,29 @@ finished:
idata = in_iov[i].iov_base;
idata->reparse.io.iov = *iov;
idata->reparse.io.buftype = resp_buftype[i + 1];
+ idata->contains_posix_file_info = false; /* BB VERIFY */
rbuf = reparse_buf_ptr(iov);
if (IS_ERR(rbuf)) {
rc = PTR_ERR(rbuf);
- trace_smb3_set_reparse_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_get_reparse_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
} else {
idata->reparse.tag = le32_to_cpu(rbuf->ReparseTag);
- trace_smb3_set_reparse_compound_done(xid, ses->Suid,
- tcon->tid);
+ trace_smb3_get_reparse_compound_done(xid, tcon->tid,
+ ses->Suid);
}
memset(iov, 0, sizeof(*iov));
resp_buftype[i + 1] = CIFS_NO_BUFFER;
} else {
- trace_smb3_set_reparse_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_get_reparse_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
}
SMB2_ioctl_free(&rqst[num_rqst++]);
break;
case SMB2_OP_QUERY_WSL_EA:
if (!rc) {
idata = in_iov[i].iov_base;
+ idata->contains_posix_file_info = false;
qi_rsp = rsp_iov[i + 1].iov_base;
data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
@@ -792,17 +841,18 @@ finished:
}
}
if (!rc) {
- trace_smb3_query_wsl_ea_compound_done(xid, ses->Suid,
- tcon->tid);
+ trace_smb3_query_wsl_ea_compound_done(xid, tcon->tid,
+ ses->Suid);
} else {
- trace_smb3_query_wsl_ea_compound_err(xid, ses->Suid,
- tcon->tid, rc);
+ trace_smb3_query_wsl_ea_compound_err(xid, tcon->tid,
+ ses->Suid, rc);
}
SMB2_query_info_free(&rqst[num_rqst++]);
break;
}
}
SMB2_close_free(&rqst[num_rqst]);
+ rc = tmp_rc;
num_cmds += 2;
if (out_iov && out_buftype) {
@@ -820,6 +870,7 @@ finished:
smb2_should_replay(tcon, &retries, &cur_sleep))
goto replay_again;
+out:
if (cfile)
cifsFileInfo_put(cfile);
@@ -828,6 +879,7 @@ finished:
static int parse_create_response(struct cifs_open_info_data *data,
struct cifs_sb_info *cifs_sb,
+ const char *full_path,
const struct kvec *iov)
{
struct smb2_create_rsp *rsp = iov->iov_base;
@@ -841,6 +893,7 @@ static int parse_create_response(struct cifs_open_info_data *data,
break;
case STATUS_STOPPED_ON_SYMLINK:
rc = smb2_parse_symlink_response(cifs_sb, iov,
+ full_path,
&data->symlink_target);
if (rc)
return rc;
@@ -856,22 +909,52 @@ static int parse_create_response(struct cifs_open_info_data *data,
return rc;
}
+/* Check only if SMB2_OP_QUERY_WSL_EA command failed in the compound chain */
+static bool ea_unsupported(int *cmds, int num_cmds,
+ struct kvec *out_iov, int *out_buftype)
+{
+ int i;
+
+ if (cmds[num_cmds - 1] != SMB2_OP_QUERY_WSL_EA)
+ return false;
+
+ for (i = 1; i < num_cmds - 1; i++) {
+ struct smb2_hdr *hdr = out_iov[i].iov_base;
+
+ if (out_buftype[i] == CIFS_NO_BUFFER || !hdr ||
+ hdr->Status != STATUS_SUCCESS)
+ return false;
+ }
+ return true;
+}
+
+static inline void free_rsp_iov(struct kvec *iovs, int *buftype, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ free_rsp_buf(buftype[i], iovs[i].iov_base);
+ memset(&iovs[i], 0, sizeof(*iovs));
+ buftype[i] = CIFS_NO_BUFFER;
+ }
+}
+
int smb2_query_path_info(const unsigned int xid,
struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const char *full_path,
struct cifs_open_info_data *data)
{
+ struct kvec in_iov[3], out_iov[5] = {};
+ struct cached_fid *cfid = NULL;
struct cifs_open_parms oparms;
- __u32 create_options = 0;
struct cifsFileInfo *cfile;
- struct cached_fid *cfid = NULL;
+ __u32 create_options = 0;
+ int out_buftype[5] = {};
struct smb2_hdr *hdr;
- struct kvec in_iov[3], out_iov[3] = {};
- int out_buftype[3] = {};
+ int num_cmds = 0;
int cmds[3];
bool islink;
- int i, num_cmds = 0;
int rc, rc2;
data->adjust_tz = false;
@@ -930,24 +1013,63 @@ int smb2_query_path_info(const unsigned int xid,
switch (rc) {
case 0:
- rc = parse_create_response(data, cifs_sb, &out_iov[0]);
+ rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
+ break;
+ case -EACCES:
+ /*
+ * If SMB2_OP_QUERY_INFO (called when POSIX extensions are not used) failed with
+ * STATUS_ACCESS_DENIED then it means that caller does not have permission to
+ * open the path with FILE_READ_ATTRIBUTES access and therefore cannot issue
+ * SMB2_OP_QUERY_INFO command.
+ *
+ * There is an alternative way how to query limited information about path but still
+ * suitable for stat() syscall. SMB2 OPEN/CREATE operation returns in its successful
+ * response subset of query information.
+ *
+ * So try to open the path without FILE_READ_ATTRIBUTES but with MAXIMUM_ALLOWED
+ * access which will grant the maximum possible access to the file and the response
+ * will contain required query information for stat() syscall.
+ */
+
+ if (tcon->posix_extensions)
+ break;
+
+ num_cmds = 1;
+ cmds[0] = SMB2_OP_OPEN_QUERY;
+ in_iov[0].iov_base = data;
+ in_iov[0].iov_len = sizeof(*data);
+ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, MAXIMUM_ALLOWED,
+ FILE_OPEN, create_options, ACL_NO_MODE);
+ free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ &oparms, in_iov, cmds, num_cmds,
+ cfile, out_iov, out_buftype, NULL);
+
+ hdr = out_iov[0].iov_base;
+ if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)
+ goto out;
+
+ if (!rc)
+ rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
break;
case -EOPNOTSUPP:
/*
* BB TODO: When support for special files added to Samba
* re-verify this path.
*/
- rc = parse_create_response(data, cifs_sb, &out_iov[0]);
+ rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
if (rc || !data->reparse_point)
goto out;
- cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
/*
* Skip SMB2_OP_GET_REPARSE if symlink already parsed in create
* response.
*/
- if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK)
+ if (data->reparse.tag != IO_REPARSE_TAG_SYMLINK) {
cmds[num_cmds++] = SMB2_OP_GET_REPARSE;
+ if (!tcon->posix_extensions)
+ cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
+ }
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
FILE_READ_ATTRIBUTES |
@@ -955,9 +1077,23 @@ int smb2_query_path_info(const unsigned int xid,
FILE_OPEN, create_options |
OPEN_REPARSE_POINT, ACL_NO_MODE);
cifs_get_readable_path(tcon, full_path, &cfile);
+ free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
&oparms, in_iov, cmds, num_cmds,
- cfile, NULL, NULL, NULL);
+ cfile, out_iov, out_buftype, NULL);
+ if (rc && ea_unsupported(cmds, num_cmds,
+ out_iov, out_buftype)) {
+ if (data->reparse.tag != IO_REPARSE_TAG_LX_BLK &&
+ data->reparse.tag != IO_REPARSE_TAG_LX_CHR)
+ rc = 0;
+ else
+ rc = -EOPNOTSUPP;
+ }
+
+ if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) {
+ bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
+ rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
+ }
break;
case -EREMOTE:
break;
@@ -975,8 +1111,7 @@ int smb2_query_path_info(const unsigned int xid,
}
out:
- for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
- free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
+ free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
return rc;
}
@@ -1035,7 +1170,7 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
FILE_OPEN, CREATE_NOT_FILE, ACL_NO_MODE);
return smb2_compound_op(xid, tcon, cifs_sb,
name, &oparms, NULL,
- &(int){SMB2_OP_RMDIR}, 1,
+ &(int){SMB2_OP_UNLINK}, 1,
NULL, NULL, NULL, NULL);
}
@@ -1043,21 +1178,107 @@ int
smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
struct cifs_sb_info *cifs_sb, struct dentry *dentry)
{
+ struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
+ __le16 *utf16_path __free(kfree) = NULL;
+ int retries = 0, cur_sleep = 1;
+ struct TCP_Server_Info *server;
struct cifs_open_parms oparms;
+ struct smb2_create_req *creq;
+ struct inode *inode = NULL;
+ struct smb_rqst rqst[2];
+ struct kvec rsp_iov[2];
+ struct kvec close_iov;
+ int resp_buftype[2];
+ struct cifs_fid fid;
+ int flags = 0;
+ __u8 oplock;
+ int rc;
- oparms = CIFS_OPARMS(cifs_sb, tcon, name,
- DELETE, FILE_OPEN,
- CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
- ACL_NO_MODE);
- int rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,
- NULL, &(int){SMB2_OP_DELETE}, 1,
- NULL, NULL, NULL, dentry);
- if (rc == -EINVAL) {
- cifs_dbg(FYI, "invalid lease key, resending request without lease");
- rc = smb2_compound_op(xid, tcon, cifs_sb, name, &oparms,
- NULL, &(int){SMB2_OP_DELETE}, 1,
- NULL, NULL, NULL, NULL);
+ utf16_path = cifs_convert_path_to_utf16(name, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+
+ if (smb3_encryption_required(tcon))
+ flags |= CIFS_TRANSFORM_REQ;
+again:
+ oplock = SMB2_OPLOCK_LEVEL_NONE;
+ server = cifs_pick_channel(tcon->ses);
+
+ memset(rqst, 0, sizeof(rqst));
+ memset(resp_buftype, 0, sizeof(resp_buftype));
+ memset(rsp_iov, 0, sizeof(rsp_iov));
+
+ rqst[0].rq_iov = open_iov;
+ rqst[0].rq_nvec = ARRAY_SIZE(open_iov);
+
+ oparms = CIFS_OPARMS(cifs_sb, tcon, name, DELETE | FILE_READ_ATTRIBUTES,
+ FILE_OPEN, CREATE_DELETE_ON_CLOSE |
+ OPEN_REPARSE_POINT, ACL_NO_MODE);
+ oparms.fid = &fid;
+
+ if (dentry) {
+ inode = d_inode(dentry);
+ if (CIFS_I(inode)->lease_granted && server->ops->get_lease_key) {
+ oplock = SMB2_OPLOCK_LEVEL_LEASE;
+ server->ops->get_lease_key(inode, &fid);
+ }
+ }
+
+ rc = SMB2_open_init(tcon, server,
+ &rqst[0], &oplock, &oparms, utf16_path);
+ if (rc)
+ goto err_free;
+ smb2_set_next_command(tcon, &rqst[0]);
+ creq = rqst[0].rq_iov[0].iov_base;
+ creq->ShareAccess = FILE_SHARE_DELETE_LE;
+
+ rqst[1].rq_iov = &close_iov;
+ rqst[1].rq_nvec = 1;
+
+ rc = SMB2_close_init(tcon, server, &rqst[1],
+ COMPOUND_FID, COMPOUND_FID, false);
+ smb2_set_related(&rqst[1]);
+ if (rc)
+ goto err_free;
+
+ if (retries) {
+ for (int i = 0; i < ARRAY_SIZE(rqst); i++)
+ smb2_set_replay(server, &rqst[i]);
+ }
+
+ rc = compound_send_recv(xid, tcon->ses, server, flags,
+ ARRAY_SIZE(rqst), rqst,
+ resp_buftype, rsp_iov);
+ SMB2_open_free(&rqst[0]);
+ SMB2_close_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);
+
+ if (is_replayable_error(rc) &&
+ smb2_should_replay(tcon, &retries, &cur_sleep))
+ goto again;
+
+ /* Retry compound request without lease */
+ if (rc == -EINVAL && dentry) {
+ dentry = NULL;
+ retries = 0;
+ cur_sleep = 1;
+ goto again;
}
+ /*
+ * If dentry (hence, inode) is NULL, lease break is going to
+ * take care of degrading leases on handles for deleted files.
+ */
+ if (!rc && inode)
+ cifs_mark_open_handles_for_deleted_file(inode, name);
+
+ return rc;
+
+err_free:
+ SMB2_open_free(&rqst[0]);
+ SMB2_close_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;
}
@@ -1076,6 +1297,8 @@ static int smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb);
if (smb2_to_name == NULL) {
rc = -ENOMEM;
+ if (cfile)
+ cifsFileInfo_put(cfile);
goto smb2_rename_path;
}
in_iov.iov_base = smb2_to_name;
@@ -1164,36 +1387,38 @@ int
smb2_set_file_info(struct inode *inode, const char *full_path,
FILE_BASIC_INFO *buf, const unsigned int xid)
{
- struct cifs_open_parms oparms;
+ struct kvec in_iov = { .iov_base = buf, .iov_len = sizeof(*buf), };
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsFileInfo *cfile = NULL;
+ struct cifs_open_parms oparms;
struct tcon_link *tlink;
struct cifs_tcon *tcon;
- struct cifsFileInfo *cfile;
- struct kvec in_iov = { .iov_base = buf, .iov_len = sizeof(*buf), };
- int rc;
-
- if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
- (buf->LastWriteTime == 0) && (buf->ChangeTime == 0) &&
- (buf->Attributes == 0))
- return 0; /* would be a no op, no sense sending this */
+ int rc = 0;
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
- cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
+ (buf->LastWriteTime == 0) && (buf->ChangeTime == 0)) {
+ if (buf->Attributes == 0)
+ goto out; /* would be a no op, no sense sending this */
+ cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
+ }
+
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_ATTRIBUTES,
FILE_OPEN, 0, ACL_NO_MODE);
rc = smb2_compound_op(xid, tcon, cifs_sb,
full_path, &oparms, &in_iov,
&(int){SMB2_OP_SET_INFO}, 1,
cfile, NULL, NULL, NULL);
+out:
cifs_put_tlink(tlink);
return rc;
}
-struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data,
struct super_block *sb,
const unsigned int xid,
struct cifs_tcon *tcon,
@@ -1213,6 +1438,14 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
int rc;
int i;
+ /*
+ * If server filesystem does not support reparse points then do not
+ * attempt to create reparse point. This will prevent creating unusable
+ * empty object on the server.
+ */
+ if (!CIFS_REPARSE_SUPPORT(tcon))
+ return ERR_PTR(-EOPNOTSUPP);
+
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
SYNCHRONIZE | DELETE |
FILE_READ_ATTRIBUTES |
@@ -1302,3 +1535,113 @@ out:
cifs_free_open_info(&data);
return rc;
}
+
+static inline __le16 *utf16_smb2_path(struct cifs_sb_info *cifs_sb,
+ const char *name, size_t namelen)
+{
+ int len;
+
+ if (*name == '\\' ||
+ (cifs_sb_master_tlink(cifs_sb) &&
+ cifs_sb_master_tcon(cifs_sb)->posix_extensions && *name == '/'))
+ name++;
+ return cifs_strndup_to_utf16(name, namelen, &len,
+ cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
+}
+
+int smb2_rename_pending_delete(const char *full_path,
+ struct dentry *dentry,
+ const unsigned int xid)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(d_inode(dentry)->i_sb);
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(dentry));
+ __le16 *utf16_path __free(kfree) = NULL;
+ __u32 co = file_create_options(dentry);
+ int cmds[] = {
+ SMB2_OP_SET_INFO,
+ SMB2_OP_RENAME,
+ SMB2_OP_UNLINK,
+ };
+ const int num_cmds = ARRAY_SIZE(cmds);
+ char *to_name __free(kfree) = NULL;
+ __u32 attrs = cinode->cifsAttrs;
+ struct cifs_open_parms oparms;
+ static atomic_t sillycounter;
+ struct cifsFileInfo *cfile;
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct kvec iov[2];
+ const char *ppath;
+ void *page;
+ size_t len;
+ int rc;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+
+ page = alloc_dentry_path();
+
+ ppath = build_path_from_dentry(dentry->d_parent, page);
+ if (IS_ERR(ppath)) {
+ rc = PTR_ERR(ppath);
+ goto out;
+ }
+
+ len = strlen(ppath) + strlen("/.__smb1234") + 1;
+ to_name = kmalloc(len, GFP_KERNEL);
+ if (!to_name) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ scnprintf(to_name, len, "%s%c.__smb%04X", ppath, CIFS_DIR_SEP(cifs_sb),
+ atomic_inc_return(&sillycounter) & 0xffff);
+
+ utf16_path = utf16_smb2_path(cifs_sb, to_name, len);
+ if (!utf16_path) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ drop_cached_dir_by_name(xid, tcon, full_path, cifs_sb);
+ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
+ DELETE | FILE_WRITE_ATTRIBUTES,
+ FILE_OPEN, co, ACL_NO_MODE);
+
+ attrs &= ~ATTR_READONLY;
+ if (!attrs)
+ attrs = ATTR_NORMAL;
+ if (d_inode(dentry)->i_nlink <= 1)
+ attrs |= ATTR_HIDDEN;
+ iov[0].iov_base = &(FILE_BASIC_INFO) {
+ .Attributes = cpu_to_le32(attrs),
+ };
+ iov[0].iov_len = sizeof(FILE_BASIC_INFO);
+ iov[1].iov_base = utf16_path;
+ iov[1].iov_len = sizeof(*utf16_path) * UniStrlen((wchar_t *)utf16_path);
+
+ cifs_get_writable_path(tcon, full_path, FIND_WR_WITH_DELETE, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms, iov,
+ cmds, num_cmds, cfile, NULL, NULL, dentry);
+ if (rc == -EINVAL) {
+ cifs_dbg(FYI, "invalid lease key, resending request without lease\n");
+ cifs_get_writable_path(tcon, full_path,
+ FIND_WR_WITH_DELETE, &cfile);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms, iov,
+ cmds, num_cmds, cfile, NULL, NULL, NULL);
+ }
+ if (!rc) {
+ set_bit(CIFS_INO_DELETE_PENDING, &cinode->flags);
+ } else {
+ cifs_tcon_dbg(FYI, "%s: failed to rename '%s' to '%s': %d\n",
+ __func__, full_path, to_name, rc);
+ rc = smb_EIO1(smb_eio_trace_pend_del_fail, rc);
+ }
+out:
+ cifs_put_tlink(tlink);
+ free_dentry_path(page);
+ return rc;
+}
diff --git a/fs/smb/client/smb2maperror.c b/fs/smb/client/smb2maperror.c
index b05313acf9b2..4e1db02d22cb 100644
--- a/fs/smb/client/smb2maperror.c
+++ b/fs/smb/client/smb2maperror.c
@@ -9,11 +9,11 @@
*/
#include <linux/errno.h>
#include "cifsglob.h"
+#include "cifsproto.h"
#include "cifs_debug.h"
-#include "smb2pdu.h"
#include "smb2proto.h"
-#include "../common/smb2status.h"
#include "smb2glob.h"
+#include "../common/smb2status.h"
#include "trace.h"
struct status_to_posix_error {
@@ -23,14 +23,13 @@ struct status_to_posix_error {
};
static const struct status_to_posix_error smb2_error_map_table[] = {
- {STATUS_SUCCESS, 0, "STATUS_SUCCESS"},
- {STATUS_WAIT_0, 0, "STATUS_WAIT_0"},
{STATUS_WAIT_1, -EIO, "STATUS_WAIT_1"},
{STATUS_WAIT_2, -EIO, "STATUS_WAIT_2"},
{STATUS_WAIT_3, -EIO, "STATUS_WAIT_3"},
{STATUS_WAIT_63, -EIO, "STATUS_WAIT_63"},
- {STATUS_ABANDONED, -EIO, "STATUS_ABANDONED"},
- {STATUS_ABANDONED_WAIT_0, -EIO, "STATUS_ABANDONED_WAIT_0"},
+ {STATUS_ABANDONED, -EIO, "STATUS_ABANDONED or STATUS_ABANDONED_WAIT_0"},
+ {STATUS_ABANDONED_WAIT_0, -EIO,
+ "STATUS_ABANDONED or STATUS_ABANDONED_WAIT_0"},
{STATUS_ABANDONED_WAIT_63, -EIO, "STATUS_ABANDONED_WAIT_63"},
{STATUS_USER_APC, -EIO, "STATUS_USER_APC"},
{STATUS_KERNEL_APC, -EIO, "STATUS_KERNEL_APC"},
@@ -380,7 +379,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
{STATUS_NO_LOGON_SERVERS, -EIO, "STATUS_NO_LOGON_SERVERS"},
{STATUS_NO_SUCH_LOGON_SESSION, -EIO, "STATUS_NO_SUCH_LOGON_SESSION"},
{STATUS_NO_SUCH_PRIVILEGE, -EIO, "STATUS_NO_SUCH_PRIVILEGE"},
- {STATUS_PRIVILEGE_NOT_HELD, -EIO, "STATUS_PRIVILEGE_NOT_HELD"},
+ {STATUS_PRIVILEGE_NOT_HELD, -EPERM, "STATUS_PRIVILEGE_NOT_HELD"},
{STATUS_INVALID_ACCOUNT_NAME, -EIO, "STATUS_INVALID_ACCOUNT_NAME"},
{STATUS_USER_EXISTS, -EIO, "STATUS_USER_EXISTS"},
{STATUS_NO_SUCH_USER, -EIO, "STATUS_NO_SUCH_USER"},
@@ -736,6 +735,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
{STATUS_FS_DRIVER_REQUIRED, -EOPNOTSUPP, "STATUS_FS_DRIVER_REQUIRED"},
{STATUS_IMAGE_ALREADY_LOADED_AS_DLL, -EIO,
"STATUS_IMAGE_ALREADY_LOADED_AS_DLL"},
+ {STATUS_INVALID_LOCK_RANGE, -EIO, "STATUS_INVALID_LOCK_RANGE"},
{STATUS_NETWORK_OPEN_RESTRICTION, -EIO,
"STATUS_NETWORK_OPEN_RESTRICTION"},
{STATUS_NO_USER_SESSION_KEY, -EIO, "STATUS_NO_USER_SESSION_KEY"},
@@ -871,7 +871,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
{STATUS_VALIDATE_CONTINUE, -EIO, "STATUS_VALIDATE_CONTINUE"},
{STATUS_NO_MATCH, -EIO, "STATUS_NO_MATCH"},
{STATUS_NO_MORE_MATCHES, -EIO, "STATUS_NO_MORE_MATCHES"},
- {STATUS_NOT_A_REPARSE_POINT, -EIO, "STATUS_NOT_A_REPARSE_POINT"},
+ {STATUS_NOT_A_REPARSE_POINT, -ENODATA, "STATUS_NOT_A_REPARSE_POINT"},
{STATUS_IO_REPARSE_TAG_INVALID, -EIO, "STATUS_IO_REPARSE_TAG_INVALID"},
{STATUS_IO_REPARSE_TAG_MISMATCH, -EIO,
"STATUS_IO_REPARSE_TAG_MISMATCH"},
@@ -2298,8 +2298,9 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
{STATUS_FWP_LIFETIME_MISMATCH, -EIO, "STATUS_FWP_LIFETIME_MISMATCH"},
{STATUS_FWP_BUILTIN_OBJECT, -EIO, "STATUS_FWP_BUILTIN_OBJECT"},
{STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS, -EIO,
- "STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS"},
- {STATUS_FWP_TOO_MANY_CALLOUTS, -EIO, "STATUS_FWP_TOO_MANY_CALLOUTS"},
+ "STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS or STATUS_FWP_TOO_MANY_CALLOUTS"},
+ {STATUS_FWP_TOO_MANY_CALLOUTS, -EIO,
+ "STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS or STATUS_FWP_TOO_MANY_CALLOUTS"},
{STATUS_FWP_NOTIFICATION_DROPPED, -EIO,
"STATUS_FWP_NOTIFICATION_DROPPED"},
{STATUS_FWP_TRAFFIC_MISMATCH, -EIO, "STATUS_FWP_TRAFFIC_MISMATCH"},
@@ -2415,27 +2416,10 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
{STATUS_IPSEC_INTEGRITY_CHECK_FAILED, -EIO,
"STATUS_IPSEC_INTEGRITY_CHECK_FAILED"},
{STATUS_IPSEC_CLEAR_TEXT_DROP, -EIO, "STATUS_IPSEC_CLEAR_TEXT_DROP"},
- {0, 0, NULL}
+ {STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP, -EIO,
+ "STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP"},
};
-/*****************************************************************************
- Print an error message from the status code
- *****************************************************************************/
-static void
-smb2_print_status(__le32 status)
-{
- int idx = 0;
-
- while (smb2_error_map_table[idx].status_string != NULL) {
- if ((smb2_error_map_table[idx].smb2_status) == status) {
- pr_notice("Status code returned 0x%08x %s\n", status,
- smb2_error_map_table[idx].status_string);
- }
- idx++;
- }
- return;
-}
-
int
map_smb2_to_linux_error(char *buf, bool log_err)
{
@@ -2452,16 +2436,16 @@ map_smb2_to_linux_error(char *buf, bool log_err)
return 0;
}
- /* mask facility */
- if (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) &&
- (smb2err != STATUS_END_OF_FILE))
- smb2_print_status(smb2err);
- else if (cifsFYI & CIFS_RC)
- smb2_print_status(smb2err);
+ log_err = (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) &&
+ (smb2err != STATUS_END_OF_FILE)) ||
+ (cifsFYI & CIFS_RC);
for (i = 0; i < sizeof(smb2_error_map_table) /
sizeof(struct status_to_posix_error); i++) {
if (smb2_error_map_table[i].smb2_status == smb2err) {
+ if (log_err)
+ pr_notice("Status code returned 0x%08x %s\n", smb2err,
+ smb2_error_map_table[i].status_string);
rc = smb2_error_map_table[i].posix_error;
break;
}
@@ -2477,5 +2461,7 @@ map_smb2_to_linux_error(char *buf, bool log_err)
le16_to_cpu(shdr->Command),
le64_to_cpu(shdr->MessageId),
le32_to_cpu(smb2err), rc);
+ if (rc == -EIO)
+ smb_EIO1(smb_eio_trace_smb2_received_error, le32_to_cpu(smb2err));
return rc;
}
diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c
index f3c4b70b77b9..f3cb62d91450 100644
--- a/fs/smb/client/smb2misc.c
+++ b/fs/smb/client/smb2misc.c
@@ -7,6 +7,7 @@
* Pavel Shilovsky (pshilovsky@samba.org) 2012
*
*/
+#include <crypto/sha2.h>
#include <linux/ctype.h>
#include "cifsglob.h"
#include "cifsproto.h"
@@ -133,7 +134,8 @@ static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len,
}
int
-smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
+smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
+ struct TCP_Server_Info *server)
{
struct TCP_Server_Info *pserver;
struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
@@ -614,6 +616,15 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
struct cifs_tcon *tcon;
struct cifs_pending_open *open;
+ /* Trace receipt of lease break request from server */
+ trace_smb3_lease_break_enter(le32_to_cpu(rsp->CurrentLeaseState),
+ le32_to_cpu(rsp->Flags),
+ le16_to_cpu(rsp->Epoch),
+ le32_to_cpu(rsp->hdr.Id.SyncId.TreeId),
+ le64_to_cpu(rsp->hdr.SessionId),
+ *((u64 *)rsp->LeaseKey),
+ *((u64 *)&rsp->LeaseKey[8]));
+
cifs_dbg(FYI, "Checking for lease break\n");
/* If server is a channel, select the primary channel */
@@ -660,10 +671,12 @@ smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "Can not process lease break - no lease matched\n");
trace_smb3_lease_not_found(le32_to_cpu(rsp->CurrentLeaseState),
- le32_to_cpu(rsp->hdr.Id.SyncId.TreeId),
- le64_to_cpu(rsp->hdr.SessionId),
- *((u64 *)rsp->LeaseKey),
- *((u64 *)&rsp->LeaseKey[8]));
+ le32_to_cpu(rsp->Flags),
+ le16_to_cpu(rsp->Epoch),
+ le32_to_cpu(rsp->hdr.Id.SyncId.TreeId),
+ le64_to_cpu(rsp->hdr.SessionId),
+ *((u64 *)rsp->LeaseKey),
+ *((u64 *)&rsp->LeaseKey[8]));
return false;
}
@@ -816,11 +829,12 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative");
spin_unlock(&cifs_tcp_ses_lock);
- if (tcon->ses)
+ if (tcon->ses) {
server = tcon->ses->server;
-
- cifs_server_dbg(FYI, "tid=0x%x: tcon is closing, skipping async close retry of fid %llu %llu\n",
- tcon->tid, persistent_fid, volatile_fid);
+ cifs_server_dbg(FYI,
+ "tid=0x%x: tcon is closing, skipping async close retry of fid %llu %llu\n",
+ tcon->tid, persistent_fid, volatile_fid);
+ }
return 0;
}
@@ -876,13 +890,13 @@ smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *serve
* @iov: array containing the SMB request we will send to the server
* @nvec: number of array entries for the iov
*/
-int
+void
smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server,
struct kvec *iov, int nvec)
{
- int i, rc;
+ int i;
struct smb2_hdr *hdr;
- struct shash_desc *sha512 = NULL;
+ struct sha512_ctx sha_ctx;
hdr = (struct smb2_hdr *)iov[0].iov_base;
/* neg prot are always taken */
@@ -895,52 +909,22 @@ smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server,
* and we can test it. Preauth requires 3.1.1 for now.
*/
if (server->dialect != SMB311_PROT_ID)
- return 0;
+ return;
if (hdr->Command != SMB2_SESSION_SETUP)
- return 0;
+ return;
/* skip last sess setup response */
if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
&& (hdr->Status == NT_STATUS_OK
|| (hdr->Status !=
cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))))
- return 0;
+ return;
ok:
- rc = smb311_crypto_shash_allocate(server);
- if (rc)
- return rc;
-
- sha512 = server->secmech.sha512;
- rc = crypto_shash_init(sha512);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not init sha512 shash\n", __func__);
- return rc;
- }
-
- rc = crypto_shash_update(sha512, ses->preauth_sha_hash,
- SMB2_PREAUTH_HASH_SIZE);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not update sha512 shash\n", __func__);
- return rc;
- }
-
- for (i = 0; i < nvec; i++) {
- rc = crypto_shash_update(sha512, iov[i].iov_base, iov[i].iov_len);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not update sha512 shash\n",
- __func__);
- return rc;
- }
- }
-
- rc = crypto_shash_final(sha512, ses->preauth_sha_hash);
- if (rc) {
- cifs_dbg(VFS, "%s: Could not finalize sha512 shash\n",
- __func__);
- return rc;
- }
-
- return 0;
+ sha512_init(&sha_ctx);
+ sha512_update(&sha_ctx, ses->preauth_sha_hash, SMB2_PREAUTH_HASH_SIZE);
+ for (i = 0; i < nvec; i++)
+ sha512_update(&sha_ctx, iov[i].iov_base, iov[i].iov_len);
+ sha512_final(&sha_ctx, ses->preauth_sha_hash);
}
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index fa96ebed8310..a16ded46b5a2 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -17,9 +17,9 @@
#include <uapi/linux/magic.h>
#include "cifsfs.h"
#include "cifsglob.h"
-#include "smb2pdu.h"
-#include "smb2proto.h"
#include "cifsproto.h"
+#include "smb2proto.h"
+#include "smb2pdu.h"
#include "cifs_debug.h"
#include "cifs_unicode.h"
#include "../common/smb2status.h"
@@ -91,7 +91,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
if (*val > 65000) {
*val = 65000; /* Don't get near 64K credits, avoid srv bugs */
pr_warn_once("server overflowed SMB3 credits\n");
- trace_smb3_overflow_credits(server->CurrentMid,
+ trace_smb3_overflow_credits(server->current_mid,
server->conn_id, server->hostname, *val,
add, server->in_flight);
}
@@ -136,7 +136,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
wake_up(&server->request_q);
if (reconnect_detected) {
- trace_smb3_reconnect_detected(server->CurrentMid,
+ trace_smb3_reconnect_detected(server->current_mid,
server->conn_id, server->hostname, scredits, add, in_flight);
cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n",
@@ -144,7 +144,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
}
if (reconnect_with_invalid_credits) {
- trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
+ trace_smb3_reconnect_with_invalid_credits(server->current_mid,
server->conn_id, server->hostname, scredits, add, in_flight);
cifs_dbg(FYI, "Negotiate operation when server credits is non-zero. Optype: %d, server credits: %d, credits added: %d\n",
optype, scredits, add);
@@ -176,7 +176,7 @@ smb2_add_credits(struct TCP_Server_Info *server,
break;
}
- trace_smb3_add_credits(server->CurrentMid,
+ trace_smb3_add_credits(server->current_mid,
server->conn_id, server->hostname, scredits, add, in_flight);
cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, scredits);
}
@@ -203,7 +203,7 @@ smb2_set_credits(struct TCP_Server_Info *server, const int val)
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
- trace_smb3_set_credits(server->CurrentMid,
+ trace_smb3_set_credits(server->current_mid,
server->conn_id, server->hostname, scredits, val, in_flight);
cifs_dbg(FYI, "%s: set %u credits\n", __func__, val);
@@ -288,7 +288,7 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, size_t size,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
- trace_smb3_wait_credits(server->CurrentMid,
+ trace_smb3_wait_credits(server->current_mid,
server->conn_id, server->hostname, scredits, -(credits->value), in_flight);
cifs_dbg(FYI, "%s: removed %u credits total=%d\n",
__func__, credits->value, scredits);
@@ -316,7 +316,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
server->credits, server->in_flight,
new_val - credits->value,
cifs_trace_rw_credits_no_adjust_up);
- trace_smb3_too_many_credits(server->CurrentMid,
+ trace_smb3_too_many_credits(server->current_mid,
server->conn_id, server->hostname, 0, credits->value - new_val, 0);
cifs_server_dbg(VFS, "R=%x[%x] request has less credits (%d) than required (%d)",
subreq->rreq->debug_id, subreq->subreq.debug_index,
@@ -338,7 +338,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
server->credits, server->in_flight,
new_val - credits->value,
cifs_trace_rw_credits_old_session);
- trace_smb3_reconnect_detected(server->CurrentMid,
+ trace_smb3_reconnect_detected(server->current_mid,
server->conn_id, server->hostname, scredits,
credits->value - new_val, in_flight);
cifs_server_dbg(VFS, "R=%x[%x] trying to return %d credits to old session\n",
@@ -358,7 +358,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
- trace_smb3_adj_credits(server->CurrentMid,
+ trace_smb3_adj_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
credits->value - new_val, in_flight);
cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n",
@@ -374,19 +374,19 @@ smb2_get_next_mid(struct TCP_Server_Info *server)
{
__u64 mid;
/* for SMB2 we need the current value */
- spin_lock(&server->mid_lock);
- mid = server->CurrentMid++;
- spin_unlock(&server->mid_lock);
+ spin_lock(&server->mid_counter_lock);
+ mid = server->current_mid++;
+ spin_unlock(&server->mid_counter_lock);
return mid;
}
static void
smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val)
{
- spin_lock(&server->mid_lock);
- if (server->CurrentMid >= val)
- server->CurrentMid -= val;
- spin_unlock(&server->mid_lock);
+ spin_lock(&server->mid_counter_lock);
+ if (server->current_mid >= val)
+ server->current_mid -= val;
+ spin_unlock(&server->mid_counter_lock);
}
static struct mid_q_entry *
@@ -401,21 +401,21 @@ __smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue)
return NULL;
}
- spin_lock(&server->mid_lock);
+ spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
if ((mid->mid == wire_mid) &&
(mid->mid_state == MID_REQUEST_SUBMITTED) &&
(mid->command == shdr->Command)) {
- kref_get(&mid->refcount);
+ smb_get_mid(mid);
if (dequeue) {
list_del_init(&mid->qhead);
- mid->mid_flags |= MID_DELETED;
+ mid->deleted_from_q = true;
}
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
return mid;
}
}
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
return NULL;
}
@@ -432,7 +432,7 @@ smb2_find_dequeue_mid(struct TCP_Server_Info *server, char *buf)
}
static void
-smb2_dump_detail(void *buf, struct TCP_Server_Info *server)
+smb2_dump_detail(void *buf, size_t buf_len, struct TCP_Server_Info *server)
{
#ifdef CONFIG_CIFS_DEBUG2
struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
@@ -440,7 +440,7 @@ smb2_dump_detail(void *buf, struct TCP_Server_Info *server)
cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n",
shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId,
shdr->Id.SyncId.ProcessId);
- if (!server->ops->check_message(buf, server->total_read, server)) {
+ if (!server->ops->check_message(buf, buf_len, server->total_read, server)) {
cifs_server_dbg(VFS, "smb buf %p len %u\n", buf,
server->ops->calc_smb_size(buf));
}
@@ -460,16 +460,24 @@ smb2_negotiate(const unsigned int xid,
{
int rc;
- spin_lock(&server->mid_lock);
- server->CurrentMid = 0;
- spin_unlock(&server->mid_lock);
+ spin_lock(&server->mid_counter_lock);
+ server->current_mid = 0;
+ spin_unlock(&server->mid_counter_lock);
rc = SMB2_negotiate(xid, ses, server);
- /* BB we probably don't need to retry with modern servers */
- if (rc == -EAGAIN)
- rc = -EHOSTDOWN;
return rc;
}
+static inline unsigned int
+prevent_zero_iosize(unsigned int size, const char *type)
+{
+ if (size == 0) {
+ cifs_dbg(VFS, "SMB: Zero %ssize calculated, using minimum value %u\n",
+ type, CIFS_MIN_DEFAULT_IOSIZE);
+ return CIFS_MIN_DEFAULT_IOSIZE;
+ }
+ return size;
+}
+
static unsigned int
smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
{
@@ -477,12 +485,12 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
unsigned int wsize;
/* start with specified wsize, or default */
- wsize = ctx->wsize ? ctx->wsize : CIFS_DEFAULT_IOSIZE;
+ wsize = ctx->got_wsize ? ctx->vol_wsize : CIFS_DEFAULT_IOSIZE;
wsize = min_t(unsigned int, wsize, server->max_write);
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
- return wsize;
+ return prevent_zero_iosize(wsize, "w");
}
static unsigned int
@@ -492,10 +500,13 @@ smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
unsigned int wsize;
/* start with specified wsize, or default */
- wsize = ctx->wsize ? ctx->wsize : SMB3_DEFAULT_IOSIZE;
+ wsize = ctx->got_wsize ? ctx->vol_wsize : SMB3_DEFAULT_IOSIZE;
wsize = min_t(unsigned int, wsize, server->max_write);
#ifdef CONFIG_CIFS_SMB_DIRECT
if (server->rdma) {
+ const struct smbdirect_socket_parameters *sp =
+ smbd_get_parameters(server->smbd_conn);
+
if (server->sign)
/*
* Account for SMB2 data transfer packet header and
@@ -503,18 +514,18 @@ smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
*/
wsize = min_t(unsigned int,
wsize,
- server->smbd_conn->max_fragmented_send_size -
+ sp->max_fragmented_send_size -
SMB2_READWRITE_PDU_HEADER_SIZE -
sizeof(struct smb2_transform_hdr));
else
wsize = min_t(unsigned int,
- wsize, server->smbd_conn->max_readwrite_size);
+ wsize, sp->max_read_write_size);
}
#endif
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
- return wsize;
+ return prevent_zero_iosize(wsize, "w");
}
static unsigned int
@@ -524,13 +535,13 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
unsigned int rsize;
/* start with specified rsize, or default */
- rsize = ctx->rsize ? ctx->rsize : CIFS_DEFAULT_IOSIZE;
+ rsize = ctx->got_rsize ? ctx->vol_rsize : CIFS_DEFAULT_IOSIZE;
rsize = min_t(unsigned int, rsize, server->max_read);
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE);
- return rsize;
+ return prevent_zero_iosize(rsize, "r");
}
static unsigned int
@@ -540,10 +551,13 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
unsigned int rsize;
/* start with specified rsize, or default */
- rsize = ctx->rsize ? ctx->rsize : SMB3_DEFAULT_IOSIZE;
+ rsize = ctx->got_rsize ? ctx->vol_rsize : SMB3_DEFAULT_IOSIZE;
rsize = min_t(unsigned int, rsize, server->max_read);
#ifdef CONFIG_CIFS_SMB_DIRECT
if (server->rdma) {
+ const struct smbdirect_socket_parameters *sp =
+ smbd_get_parameters(server->smbd_conn);
+
if (server->sign)
/*
* Account for SMB2 data transfer packet header and
@@ -551,19 +565,19 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
*/
rsize = min_t(unsigned int,
rsize,
- server->smbd_conn->max_fragmented_recv_size -
+ sp->max_fragmented_recv_size -
SMB2_READWRITE_PDU_HEADER_SIZE -
sizeof(struct smb2_transform_hdr));
else
rsize = min_t(unsigned int,
- rsize, server->smbd_conn->max_readwrite_size);
+ rsize, sp->max_read_write_size);
}
#endif
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE);
- return rsize;
+ return prevent_zero_iosize(rsize, "r");
}
/*
@@ -610,8 +624,8 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
struct network_interface_info_ioctl_rsp *p;
struct sockaddr_in *addr4;
struct sockaddr_in6 *addr6;
- struct iface_info_ipv4 *p4;
- struct iface_info_ipv6 *p6;
+ struct smb_sockaddr_in *p4;
+ struct smb_sockaddr_in6 *p6;
struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL;
struct cifs_server_iface tmp_iface;
ssize_t bytes_left;
@@ -658,7 +672,8 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
while (bytes_left >= (ssize_t)sizeof(*p)) {
memset(&tmp_iface, 0, sizeof(tmp_iface));
- tmp_iface.speed = le64_to_cpu(p->LinkSpeed);
+ /* default to 1Gbps when link speed is unset */
+ tmp_iface.speed = le64_to_cpu(p->LinkSpeed) ?: 1000000000;
tmp_iface.rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0;
tmp_iface.rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0;
@@ -670,7 +685,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
*/
case INTERNETWORK:
addr4 = (struct sockaddr_in *)&tmp_iface.sockaddr;
- p4 = (struct iface_info_ipv4 *)p->Buffer;
+ p4 = (struct smb_sockaddr_in *)p->Buffer;
addr4->sin_family = AF_INET;
memcpy(&addr4->sin_addr, &p4->IPv4Address, 4);
@@ -682,7 +697,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
break;
case INTERNETWORKV6:
addr6 = (struct sockaddr_in6 *)&tmp_iface.sockaddr;
- p6 = (struct iface_info_ipv6 *)p->Buffer;
+ p6 = (struct smb_sockaddr_in6 *)p->Buffer;
addr6->sin6_family = AF_INET6;
memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16);
@@ -757,6 +772,13 @@ next_iface:
bytes_left -= sizeof(*p);
break;
}
+ /* Validate that Next doesn't point beyond the buffer */
+ if (next > bytes_left) {
+ cifs_dbg(VFS, "%s: invalid Next pointer %zu > %zd\n",
+ __func__, next, bytes_left);
+ rc = -EINVAL;
+ goto out;
+ }
p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next);
bytes_left -= next;
}
@@ -768,7 +790,9 @@ next_iface:
}
/* Azure rounds the buffer size up 8, to a 16 byte boundary */
- if ((bytes_left > 8) || p->Next)
+ if ((bytes_left > 8) ||
+ (bytes_left >= offsetof(struct network_interface_info_ioctl_rsp, Next)
+ + sizeof(p->Next) && p->Next))
cifs_dbg(VFS, "%s: incomplete interface info\n", __func__);
ses->iface_last_update = jiffies;
@@ -930,11 +954,8 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
if (!rc) {
- if (cfid->has_lease) {
- close_cached_dir(cfid);
- return 0;
- }
close_cached_dir(cfid);
+ return 0;
}
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
@@ -968,7 +989,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
if (islink)
rc = -EREMOTE;
}
- if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
+ if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) &&
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS))
rc = -EOPNOTSUPP;
goto out;
@@ -1000,6 +1021,7 @@ static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
if (!data->symlink_target)
return -ENOMEM;
}
+ data->contains_posix_file_info = false;
return SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, &data->fi);
}
@@ -1024,7 +1046,8 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size,
if (src_size < 8 + name_len + 1 + value_len) {
cifs_dbg(FYI, "EA entry goes beyond length of list\n");
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_ea_overrun,
+ src_size, 8 + name_len + 1 + value_len);
goto out;
}
@@ -1502,11 +1525,11 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
static int
SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
- struct copychunk_ioctl *pcchunk)
+ struct copychunk_ioctl_req *pcchunk)
{
int rc;
unsigned int ret_data_len;
- struct resume_key_req *res_key;
+ struct resume_key_ioctl_rsp *res_key;
rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
FSCTL_SRV_REQUEST_RESUME_KEY, NULL, 0 /* no input */,
@@ -1519,7 +1542,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
cifs_tcon_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc);
goto req_res_key_exit;
}
- if (ret_data_len < sizeof(struct resume_key_req)) {
+ if (ret_data_len < sizeof(struct resume_key_ioctl_rsp)) {
cifs_tcon_dbg(VFS, "Invalid refcopy resume key length\n");
rc = -EINVAL;
goto req_res_key_exit;
@@ -1585,7 +1608,7 @@ replay_again:
}
if (!ses || !server) {
- rc = -EIO;
+ rc = smb_EIO(smb_eio_trace_null_pointers);
goto free_vars;
}
@@ -1781,140 +1804,233 @@ free_vars:
return rc;
}
+/**
+ * calc_chunk_count - calculates the number chunks to be filled in the Chunks[]
+ * array of struct copychunk_ioctl
+ *
+ * @tcon: destination file tcon
+ * @bytes_left: how many bytes are left to copy
+ *
+ * Return: maximum number of chunks with which Chunks[] can be filled.
+ */
+static inline u32
+calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)
+{
+ u32 max_chunks = READ_ONCE(tcon->max_chunks);
+ u32 max_bytes_copy = READ_ONCE(tcon->max_bytes_copy);
+ u32 max_bytes_chunk = READ_ONCE(tcon->max_bytes_chunk);
+ u64 need;
+ u32 allowed;
+
+ if (!max_bytes_chunk || !max_bytes_copy || !max_chunks)
+ return 0;
+
+ /* chunks needed for the remaining bytes */
+ need = DIV_ROUND_UP_ULL(bytes_left, max_bytes_chunk);
+ /* chunks allowed per cc request */
+ allowed = DIV_ROUND_UP(max_bytes_copy, max_bytes_chunk);
+
+ return (u32)umin(need, umin(max_chunks, allowed));
+}
+
+/**
+ * smb2_copychunk_range - server-side copy of data range
+ *
+ * @xid: transaction id
+ * @src_file: source file
+ * @dst_file: destination file
+ * @src_off: source file byte offset
+ * @len: number of bytes to copy
+ * @dst_off: destination file byte offset
+ *
+ * Obtains a resume key for @src_file and issues FSCTL_SRV_COPYCHUNK_WRITE
+ * IOCTLs, splitting the request into chunks limited by tcon->max_*.
+ *
+ * Return: @len on success; negative errno on failure.
+ */
static ssize_t
smb2_copychunk_range(const unsigned int xid,
- struct cifsFileInfo *srcfile,
- struct cifsFileInfo *trgtfile, u64 src_off,
- u64 len, u64 dest_off)
+ struct cifsFileInfo *src_file,
+ struct cifsFileInfo *dst_file,
+ u64 src_off,
+ u64 len,
+ u64 dst_off)
{
- int rc;
- unsigned int ret_data_len;
- struct copychunk_ioctl *pcchunk;
- struct copychunk_ioctl_rsp *retbuf = NULL;
+ int rc = 0;
+ unsigned int ret_data_len = 0;
+ struct copychunk_ioctl_req *cc_req = NULL;
+ struct copychunk_ioctl_rsp *cc_rsp = NULL;
struct cifs_tcon *tcon;
- int chunks_copied = 0;
- bool chunk_sizes_updated = false;
- ssize_t bytes_written, total_bytes_written = 0;
+ struct srv_copychunk *chunk;
+ u32 chunks, chunk_count, chunk_bytes;
+ u32 copy_bytes, copy_bytes_left;
+ u32 chunks_written, bytes_written;
+ u64 total_bytes_left = len;
+ u64 src_off_prev, dst_off_prev;
+ u32 retries = 0;
+
+ tcon = tlink_tcon(dst_file->tlink);
+
+ trace_smb3_copychunk_enter(xid, src_file->fid.volatile_fid,
+ dst_file->fid.volatile_fid, tcon->tid,
+ tcon->ses->Suid, src_off, dst_off, len);
+
+retry:
+ chunk_count = calc_chunk_count(tcon, total_bytes_left);
+ if (!chunk_count) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
- pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
- if (pcchunk == NULL)
- return -ENOMEM;
+ cc_req = kzalloc(struct_size(cc_req, Chunks, chunk_count), GFP_KERNEL);
+ if (!cc_req) {
+ rc = -ENOMEM;
+ goto out;
+ }
- cifs_dbg(FYI, "%s: about to call request res key\n", __func__);
/* Request a key from the server to identify the source of the copy */
- rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink),
- srcfile->fid.persistent_fid,
- srcfile->fid.volatile_fid, pcchunk);
+ rc = SMB2_request_res_key(xid,
+ tlink_tcon(src_file->tlink),
+ src_file->fid.persistent_fid,
+ src_file->fid.volatile_fid,
+ cc_req);
- /* Note: request_res_key sets res_key null only if rc !=0 */
+ /* Note: request_res_key sets res_key null only if rc != 0 */
if (rc)
- goto cchunk_out;
+ goto out;
+
+ while (total_bytes_left > 0) {
+
+ /* Store previous offsets to allow rewind */
+ src_off_prev = src_off;
+ dst_off_prev = dst_off;
+
+ chunks = 0;
+ copy_bytes = 0;
+ copy_bytes_left = umin(total_bytes_left, tcon->max_bytes_copy);
+ while (copy_bytes_left > 0 && chunks < chunk_count) {
+ chunk = &cc_req->Chunks[chunks++];
- /* For now array only one chunk long, will make more flexible later */
- pcchunk->ChunkCount = cpu_to_le32(1);
- pcchunk->Reserved = 0;
- pcchunk->Reserved2 = 0;
+ chunk->SourceOffset = cpu_to_le64(src_off);
+ chunk->TargetOffset = cpu_to_le64(dst_off);
- tcon = tlink_tcon(trgtfile->tlink);
+ chunk_bytes = umin(copy_bytes_left, tcon->max_bytes_chunk);
- trace_smb3_copychunk_enter(xid, srcfile->fid.volatile_fid,
- trgtfile->fid.volatile_fid, tcon->tid,
- tcon->ses->Suid, src_off, dest_off, len);
+ chunk->Length = cpu_to_le32(chunk_bytes);
+ /* Buffer is zeroed, no need to set chunk->Reserved = 0 */
- while (len > 0) {
- pcchunk->SourceOffset = cpu_to_le64(src_off);
- pcchunk->TargetOffset = cpu_to_le64(dest_off);
- pcchunk->Length =
- cpu_to_le32(min_t(u64, len, tcon->max_bytes_chunk));
+ src_off += chunk_bytes;
+ dst_off += chunk_bytes;
+
+ copy_bytes_left -= chunk_bytes;
+ copy_bytes += chunk_bytes;
+ }
+
+ cc_req->ChunkCount = cpu_to_le32(chunks);
+ /* Buffer is zeroed, no need to set cc_req->Reserved = 0 */
/* Request server copy to target from src identified by key */
- kfree(retbuf);
- retbuf = NULL;
- rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
- trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
- (char *)pcchunk, sizeof(struct copychunk_ioctl),
- CIFSMaxBufSize, (char **)&retbuf, &ret_data_len);
+ kfree(cc_rsp);
+ cc_rsp = NULL;
+ rc = SMB2_ioctl(xid, tcon, dst_file->fid.persistent_fid,
+ dst_file->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
+ (char *)cc_req, struct_size(cc_req, Chunks, chunks),
+ CIFSMaxBufSize, (char **)&cc_rsp, &ret_data_len);
+
+ if (rc && rc != -EINVAL)
+ goto out;
+
+ if (unlikely(ret_data_len != sizeof(*cc_rsp))) {
+ cifs_tcon_dbg(VFS, "Copychunk invalid response: size %u/%zu\n",
+ ret_data_len, sizeof(*cc_rsp));
+ rc = smb_EIO1(smb_eio_trace_copychunk_inv_rsp, ret_data_len);
+ goto out;
+ }
+
+ bytes_written = le32_to_cpu(cc_rsp->TotalBytesWritten);
+ chunks_written = le32_to_cpu(cc_rsp->ChunksWritten);
+ chunk_bytes = le32_to_cpu(cc_rsp->ChunkBytesWritten);
+
if (rc == 0) {
- if (ret_data_len !=
- sizeof(struct copychunk_ioctl_rsp)) {
- cifs_tcon_dbg(VFS, "Invalid cchunk response size\n");
- rc = -EIO;
- goto cchunk_out;
- }
- if (retbuf->TotalBytesWritten == 0) {
- cifs_dbg(FYI, "no bytes copied\n");
- rc = -EIO;
- goto cchunk_out;
+ /* Check if server claimed to write more than we asked */
+ if (unlikely(!bytes_written || bytes_written > copy_bytes)) {
+ cifs_tcon_dbg(VFS, "Copychunk invalid response: bytes written %u/%u\n",
+ bytes_written, copy_bytes);
+ rc = smb_EIO2(smb_eio_trace_copychunk_overcopy_b,
+ bytes_written, copy_bytes);
+ goto out;
}
- /*
- * Check if server claimed to write more than we asked
- */
- if (le32_to_cpu(retbuf->TotalBytesWritten) >
- le32_to_cpu(pcchunk->Length)) {
- cifs_tcon_dbg(VFS, "Invalid copy chunk response\n");
- rc = -EIO;
- goto cchunk_out;
+ if (unlikely(!chunks_written || chunks_written > chunks)) {
+ cifs_tcon_dbg(VFS, "Copychunk invalid response: chunks written %u/%u\n",
+ chunks_written, chunks);
+ rc = smb_EIO2(smb_eio_trace_copychunk_overcopy_c,
+ chunks_written, chunks);
+ goto out;
}
- if (le32_to_cpu(retbuf->ChunksWritten) != 1) {
- cifs_tcon_dbg(VFS, "Invalid num chunks written\n");
- rc = -EIO;
- goto cchunk_out;
+
+ /* Partial write: rewind */
+ if (bytes_written < copy_bytes) {
+ u32 delta = copy_bytes - bytes_written;
+
+ src_off -= delta;
+ dst_off -= delta;
}
- chunks_copied++;
-
- bytes_written = le32_to_cpu(retbuf->TotalBytesWritten);
- src_off += bytes_written;
- dest_off += bytes_written;
- len -= bytes_written;
- total_bytes_written += bytes_written;
-
- cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %zu\n",
- le32_to_cpu(retbuf->ChunksWritten),
- le32_to_cpu(retbuf->ChunkBytesWritten),
- bytes_written);
- trace_smb3_copychunk_done(xid, srcfile->fid.volatile_fid,
- trgtfile->fid.volatile_fid, tcon->tid,
- tcon->ses->Suid, src_off, dest_off, len);
- } else if (rc == -EINVAL) {
- if (ret_data_len != sizeof(struct copychunk_ioctl_rsp))
- goto cchunk_out;
-
- cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n",
- le32_to_cpu(retbuf->ChunksWritten),
- le32_to_cpu(retbuf->ChunkBytesWritten),
- le32_to_cpu(retbuf->TotalBytesWritten));
- /*
- * Check if this is the first request using these sizes,
- * (ie check if copy succeed once with original sizes
- * and check if the server gave us different sizes after
- * we already updated max sizes on previous request).
- * if not then why is the server returning an error now
- */
- if ((chunks_copied != 0) || chunk_sizes_updated)
- goto cchunk_out;
-
- /* Check that server is not asking us to grow size */
- if (le32_to_cpu(retbuf->ChunkBytesWritten) <
- tcon->max_bytes_chunk)
- tcon->max_bytes_chunk =
- le32_to_cpu(retbuf->ChunkBytesWritten);
- else
- goto cchunk_out; /* server gave us bogus size */
+ total_bytes_left -= bytes_written;
+ continue;
+ }
- /* No need to change MaxChunks since already set to 1 */
- chunk_sizes_updated = true;
- } else
- goto cchunk_out;
+ /*
+ * Check if server is not asking us to reduce size.
+ *
+ * Note: As per MS-SMB2 2.2.32.1, the values returned
+ * in cc_rsp are not strictly lower than what existed
+ * before.
+ */
+ if (bytes_written < tcon->max_bytes_copy) {
+ cifs_tcon_dbg(FYI, "Copychunk MaxBytesCopy updated: %u -> %u\n",
+ tcon->max_bytes_copy, bytes_written);
+ tcon->max_bytes_copy = bytes_written;
+ }
+
+ if (chunks_written < tcon->max_chunks) {
+ cifs_tcon_dbg(FYI, "Copychunk MaxChunks updated: %u -> %u\n",
+ tcon->max_chunks, chunks_written);
+ tcon->max_chunks = chunks_written;
+ }
+
+ if (chunk_bytes < tcon->max_bytes_chunk) {
+ cifs_tcon_dbg(FYI, "Copychunk MaxBytesChunk updated: %u -> %u\n",
+ tcon->max_bytes_chunk, chunk_bytes);
+ tcon->max_bytes_chunk = chunk_bytes;
+ }
+
+ /* reset to last offsets */
+ if (retries++ < 2) {
+ src_off = src_off_prev;
+ dst_off = dst_off_prev;
+ kfree(cc_req);
+ cc_req = NULL;
+ goto retry;
+ }
+
+ break;
}
-cchunk_out:
- kfree(pcchunk);
- kfree(retbuf);
- if (rc)
+out:
+ kfree(cc_req);
+ kfree(cc_rsp);
+ if (rc) {
+ trace_smb3_copychunk_err(xid, src_file->fid.volatile_fid,
+ dst_file->fid.volatile_fid, tcon->tid,
+ tcon->ses->Suid, src_off, dst_off, len, rc);
return rc;
- else
- return total_bytes_written;
+ } else {
+ trace_smb3_copychunk_done(xid, src_file->fid.volatile_fid,
+ dst_file->fid.volatile_fid, tcon->tid,
+ tcon->ses->Suid, src_off, dst_off, len);
+ return len;
+ }
}
static int
@@ -2482,7 +2598,7 @@ smb2_is_status_pending(char *buf, struct TCP_Server_Info *server)
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
- trace_smb3_pend_credits(server->CurrentMid,
+ trace_smb3_pend_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
le16_to_cpu(shdr->CreditRequest), in_flight);
cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n",
@@ -2615,13 +2731,35 @@ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst)
}
/* SMB headers in a compound are 8 byte aligned. */
- if (!IS_ALIGNED(len, 8)) {
- num_padding = 8 - (len & 7);
+ if (IS_ALIGNED(len, 8))
+ goto out;
+
+ num_padding = 8 - (len & 7);
+ if (smb3_encryption_required(tcon)) {
+ int i;
+
+ /*
+ * Flatten request into a single buffer with required padding as
+ * the encryption layer can't handle the padding iovs.
+ */
+ for (i = 1; i < rqst->rq_nvec; i++) {
+ memcpy(rqst->rq_iov[0].iov_base +
+ rqst->rq_iov[0].iov_len,
+ rqst->rq_iov[i].iov_base,
+ rqst->rq_iov[i].iov_len);
+ rqst->rq_iov[0].iov_len += rqst->rq_iov[i].iov_len;
+ }
+ memset(rqst->rq_iov[0].iov_base + rqst->rq_iov[0].iov_len,
+ 0, num_padding);
+ rqst->rq_iov[0].iov_len += num_padding;
+ rqst->rq_nvec = 1;
+ } else {
rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding;
rqst->rq_iov[rqst->rq_nvec].iov_len = num_padding;
rqst->rq_nvec++;
- len += num_padding;
}
+ len += num_padding;
+out:
shdr->NextCommand = cpu_to_le32(len);
}
@@ -2669,11 +2807,12 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid fid;
int rc;
__le16 *utf16_path;
- struct cached_fid *cfid = NULL;
+ struct cached_fid *cfid;
int retries = 0, cur_sleep = 1;
replay_again:
/* reinitialize for possible replay */
+ cfid = NULL;
flags = CIFS_CP_CREATE_CLOSE_OP;
oplock = SMB2_OPLOCK_LEVEL_NONE;
server = cifs_pick_channel(ses);
@@ -2932,7 +3071,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
struct fsctl_get_dfs_referral_req *dfs_req = NULL;
struct get_dfs_referral_rsp *dfs_rsp = NULL;
u32 dfs_req_size = 0, dfs_rsp_size = 0;
- int retry_count = 0;
+ int retry_once = 0;
cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name);
@@ -2981,21 +3120,25 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
/* Path to resolve in an UTF-16 null-terminated string */
memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len);
- do {
+ for (;;) {
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
FSCTL_DFS_GET_REFERRALS,
(char *)dfs_req, dfs_req_size, CIFSMaxBufSize,
(char **)&dfs_rsp, &dfs_rsp_size);
- if (!is_retryable_error(rc))
+ if (fatal_signal_pending(current)) {
+ rc = -EINTR;
+ break;
+ }
+ if (!is_retryable_error(rc) || retry_once++)
break;
usleep_range(512, 2048);
- } while (++retry_count < 5);
+ }
if (!rc && !dfs_rsp)
- rc = -EIO;
+ rc = smb_EIO(smb_eio_trace_dfsref_no_rsp);
if (rc) {
if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP)
- cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc);
+ cifs_tcon_dbg(FYI, "%s: ioctl error: rc=%d\n", __func__, rc);
goto out;
}
@@ -3003,9 +3146,9 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
num_of_nodes, target_nodes,
nls_codepage, remap, search_name,
true /* is_unicode */);
- if (rc) {
- cifs_tcon_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc);
- goto out;
+ if (rc && rc != -ENOENT) {
+ cifs_tcon_dbg(VFS, "%s: failed to parse DFS referral %s: %d\n",
+ __func__, search_name, rc);
}
out:
@@ -3078,8 +3221,7 @@ get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
if (!utf16_path) {
rc = -ENOMEM;
- free_xid(xid);
- return ERR_PTR(rc);
+ goto put_tlink;
}
oparms = (struct cifs_open_parms) {
@@ -3111,6 +3253,7 @@ get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
}
+put_tlink:
cifs_put_tlink(tlink);
free_xid(xid);
@@ -3151,8 +3294,7 @@ set_smb2_acl(struct smb_ntsd *pnntsd, __u32 acllen,
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
if (!utf16_path) {
rc = -ENOMEM;
- free_xid(xid);
- return rc;
+ goto put_tlink;
}
oparms = (struct cifs_open_parms) {
@@ -3173,6 +3315,7 @@ set_smb2_acl(struct smb_ntsd *pnntsd, __u32 acllen,
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
}
+put_tlink:
cifs_put_tlink(tlink);
free_xid(xid);
return rc;
@@ -3233,7 +3376,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
ses->Suid, offset, len);
- inode_lock(inode);
filemap_invalidate_lock(inode->i_mapping);
i_size = i_size_read(inode);
@@ -3251,6 +3393,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
* first, otherwise the data may be inconsistent with the server.
*/
truncate_pagecache_range(inode, offset, offset + len - 1);
+ netfs_wait_for_outstanding_io(inode);
/* if file not oplocked can't be sure whether asking to extend size */
rc = -EOPNOTSUPP;
@@ -3279,7 +3422,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
zero_range_exit:
filemap_invalidate_unlock(inode->i_mapping);
- inode_unlock(inode);
free_xid(xid);
if (rc)
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
@@ -3303,7 +3445,6 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
xid = get_xid();
- inode_lock(inode);
/* Need to make file sparse, if not already, before freeing range. */
/* Consider adding equivalent for compressed since it could also work */
if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) {
@@ -3317,6 +3458,7 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
* caches first, otherwise the data may be inconsistent with the server.
*/
truncate_pagecache_range(inode, offset, offset + len - 1);
+ netfs_wait_for_outstanding_io(inode);
cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);
@@ -3351,7 +3493,6 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
unlock:
filemap_invalidate_unlock(inode->i_mapping);
out:
- inode_unlock(inode);
free_xid(xid);
return rc;
}
@@ -3520,8 +3661,6 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
if (rc == 0) {
netfs_resize_file(&cifsi->netfs, new_eof, true);
cifs_setsize(inode, new_eof);
- cifs_truncate_page(inode->i_mapping, inode->i_size);
- truncate_setsize(inode, new_eof);
}
goto out;
}
@@ -3617,8 +3756,6 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
xid = get_xid();
- inode_lock(inode);
-
old_eof = i_size_read(inode);
if ((off >= old_eof) ||
off + len >= old_eof) {
@@ -3633,6 +3770,7 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
truncate_pagecache_range(inode, off, old_eof);
ictx->zero_point = old_eof;
+ netfs_wait_for_outstanding_io(inode);
rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
old_eof - off - len, off);
@@ -3653,8 +3791,7 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
fscache_resize_cookie(cifs_inode_cookie(inode), new_eof);
out_2:
filemap_invalidate_unlock(inode->i_mapping);
- out:
- inode_unlock(inode);
+out:
free_xid(xid);
return rc;
}
@@ -3671,8 +3808,6 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
xid = get_xid();
- inode_lock(inode);
-
old_eof = i_size_read(inode);
if (off >= old_eof) {
rc = -EINVAL;
@@ -3687,6 +3822,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
if (rc < 0)
goto out_2;
truncate_pagecache_range(inode, off, old_eof);
+ netfs_wait_for_outstanding_io(inode);
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, new_eof);
@@ -3709,8 +3845,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
rc = 0;
out_2:
filemap_invalidate_unlock(inode->i_mapping);
- out:
- inode_unlock(inode);
+out:
free_xid(xid);
return rc;
}
@@ -3899,22 +4034,22 @@ static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
static void
smb2_downgrade_oplock(struct TCP_Server_Info *server,
struct cifsInodeInfo *cinode, __u32 oplock,
- unsigned int epoch, bool *purge_cache)
+ __u16 epoch, bool *purge_cache)
{
server->ops->set_oplock_level(cinode, oplock, 0, NULL);
}
static void
smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
- unsigned int epoch, bool *purge_cache);
+ __u16 epoch, bool *purge_cache);
static void
smb3_downgrade_oplock(struct TCP_Server_Info *server,
struct cifsInodeInfo *cinode, __u32 oplock,
- unsigned int epoch, bool *purge_cache)
+ __u16 epoch, bool *purge_cache)
{
unsigned int old_state = cinode->oplock;
- unsigned int old_epoch = cinode->epoch;
+ __u16 old_epoch = cinode->epoch;
unsigned int new_state;
if (epoch > old_epoch) {
@@ -3934,7 +4069,7 @@ smb3_downgrade_oplock(struct TCP_Server_Info *server,
static void
smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
- unsigned int epoch, bool *purge_cache)
+ __u16 epoch, bool *purge_cache)
{
oplock &= 0xFF;
cinode->lease_granted = false;
@@ -3958,7 +4093,7 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
static void
smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
- unsigned int epoch, bool *purge_cache)
+ __u16 epoch, bool *purge_cache)
{
char message[5] = {0};
unsigned int new_oplock = 0;
@@ -3995,7 +4130,7 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
static void
smb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
- unsigned int epoch, bool *purge_cache)
+ __u16 epoch, bool *purge_cache)
{
unsigned int old_oplock = cinode->oplock;
@@ -4057,7 +4192,7 @@ map_oplock_to_lease(u8 oplock)
}
static char *
-smb2_create_lease_buf(u8 *lease_key, u8 oplock)
+smb2_create_lease_buf(u8 *lease_key, u8 oplock, u8 *parent_lease_key, __le32 flags)
{
struct create_lease *buf;
@@ -4083,7 +4218,7 @@ smb2_create_lease_buf(u8 *lease_key, u8 oplock)
}
static char *
-smb3_create_lease_buf(u8 *lease_key, u8 oplock)
+smb3_create_lease_buf(u8 *lease_key, u8 oplock, u8 *parent_lease_key, __le32 flags)
{
struct create_lease_v2 *buf;
@@ -4093,6 +4228,9 @@ smb3_create_lease_buf(u8 *lease_key, u8 oplock)
memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE);
buf->lcontext.LeaseState = map_oplock_to_lease(oplock);
+ buf->lcontext.LeaseFlags = flags;
+ if (flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE)
+ memcpy(&buf->lcontext.ParentLeaseKey, parent_lease_key, SMB2_LEASE_KEY_SIZE);
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_lease_v2, lcontext));
@@ -4109,7 +4247,7 @@ smb3_create_lease_buf(u8 *lease_key, u8 oplock)
}
static __u8
-smb2_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key)
+smb2_parse_lease_buf(void *buf, __u16 *epoch, char *lease_key)
{
struct create_lease *lc = (struct create_lease *)buf;
@@ -4120,7 +4258,7 @@ smb2_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key)
}
static __u8
-smb3_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key)
+smb3_parse_lease_buf(void *buf, __u16 *epoch, char *lease_key)
{
struct create_lease_v2 *lc = (struct create_lease_v2 *)buf;
@@ -4167,7 +4305,7 @@ fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len,
static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst *rqst,
int num_rqst, const u8 *sig, u8 **iv,
struct aead_request **req, struct sg_table *sgt,
- unsigned int *num_sgs, size_t *sensitive_size)
+ unsigned int *num_sgs)
{
unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm);
unsigned int iv_size = crypto_aead_ivsize(tfm);
@@ -4184,9 +4322,8 @@ static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst
len += req_size;
len = ALIGN(len, __alignof__(struct scatterlist));
len += array_size(*num_sgs, sizeof(struct scatterlist));
- *sensitive_size = len;
- p = kvzalloc(len, GFP_NOFS);
+ p = kzalloc(len, GFP_NOFS);
if (!p)
return ERR_PTR(-ENOMEM);
@@ -4200,16 +4337,14 @@ static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst
static void *smb2_get_aead_req(struct crypto_aead *tfm, struct smb_rqst *rqst,
int num_rqst, const u8 *sig, u8 **iv,
- struct aead_request **req, struct scatterlist **sgl,
- size_t *sensitive_size)
+ struct aead_request **req, struct scatterlist **sgl)
{
struct sg_table sgtable = {};
unsigned int skip, num_sgs, i, j;
ssize_t rc;
void *p;
- p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, &sgtable,
- &num_sgs, sensitive_size);
+ p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, &sgtable, &num_sgs);
if (IS_ERR(p))
return ERR_CAST(p);
@@ -4295,9 +4430,9 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
u8 key[SMB3_ENC_DEC_KEY_SIZE];
struct aead_request *req;
u8 *iv;
+ DECLARE_CRYPTO_WAIT(wait);
unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
void *creq;
- size_t sensitive_size;
rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key);
if (rc) {
@@ -4323,8 +4458,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
return rc;
}
- creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg,
- &sensitive_size);
+ creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg);
if (IS_ERR(creq))
return PTR_ERR(creq);
@@ -4345,71 +4479,20 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
aead_request_set_crypt(req, sg, sg, crypt_len, iv);
aead_request_set_ad(req, assoc_data_len);
- rc = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
+ aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+
+ rc = crypto_wait_req(enc ? crypto_aead_encrypt(req)
+ : crypto_aead_decrypt(req), &wait);
if (!rc && enc)
memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE);
- kvfree_sensitive(creq, sensitive_size);
+ kfree_sensitive(creq);
return rc;
}
/*
- * Clear a read buffer, discarding the folios which have the 1st mark set.
- */
-static void cifs_clear_folioq_buffer(struct folio_queue *buffer)
-{
- struct folio_queue *folioq;
-
- while ((folioq = buffer)) {
- for (int s = 0; s < folioq_count(folioq); s++)
- if (folioq_is_marked(folioq, s))
- folio_put(folioq_folio(folioq, s));
- buffer = folioq->next;
- kfree(folioq);
- }
-}
-
-/*
- * Allocate buffer space into a folio queue.
- */
-static struct folio_queue *cifs_alloc_folioq_buffer(ssize_t size)
-{
- struct folio_queue *buffer = NULL, *tail = NULL, *p;
- struct folio *folio;
- unsigned int slot;
-
- do {
- if (!tail || folioq_full(tail)) {
- p = kmalloc(sizeof(*p), GFP_NOFS);
- if (!p)
- goto nomem;
- folioq_init(p);
- if (tail) {
- tail->next = p;
- p->prev = tail;
- } else {
- buffer = p;
- }
- tail = p;
- }
-
- folio = folio_alloc(GFP_KERNEL|__GFP_HIGHMEM, 0);
- if (!folio)
- goto nomem;
-
- slot = folioq_append_mark(tail, folio);
- size -= folioq_folio_size(tail, slot);
- } while (size > 0);
-
- return buffer;
-
-nomem:
- cifs_clear_folioq_buffer(buffer);
- return NULL;
-}
-
-/*
* Copy data from an iterator to the folios in a folio queue buffer.
*/
static bool cifs_copy_iter_to_folioq(struct iov_iter *iter, size_t size,
@@ -4434,7 +4517,7 @@ void
smb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst)
{
for (int i = 0; i < num_rqst; i++)
- cifs_clear_folioq_buffer(rqst[i].rq_buffer);
+ netfs_free_folioq_buffer(rqst[i].rq_buffer);
}
/*
@@ -4461,7 +4544,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst,
for (int i = 1; i < num_rqst; i++) {
struct smb_rqst *old = &old_rq[i - 1];
struct smb_rqst *new = &new_rq[i];
- struct folio_queue *buffer;
+ struct folio_queue *buffer = NULL;
size_t size = iov_iter_count(&old->rq_iter);
orig_len += smb_rqst_len(server, old);
@@ -4469,8 +4552,10 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst,
new->rq_nvec = old->rq_nvec;
if (size > 0) {
- buffer = cifs_alloc_folioq_buffer(size);
- if (!buffer)
+ size_t cur_size = 0;
+ rc = netfs_alloc_folioq_buffer(NULL, &buffer, &cur_size,
+ size, GFP_NOFS);
+ if (rc < 0)
goto err_free;
new->rq_buffer = buffer;
@@ -4478,7 +4563,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst,
buffer, 0, 0, size);
if (!cifs_copy_iter_to_folioq(&old->rq_iter, size, buffer)) {
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_tx_copy_iter_to_buf, size);
goto err_free;
}
}
@@ -4543,9 +4628,9 @@ decrypt_raw_data(struct TCP_Server_Info *server, char *buf,
return rc;
}
} else {
- if (unlikely(!server->secmech.dec))
- return -EIO;
-
+ rc = smb3_crypto_aead_allocate(server);
+ if (unlikely(rc))
+ return rc;
tfm = server->secmech.dec;
}
@@ -4579,7 +4664,8 @@ cifs_copy_folioq_to_iter(struct folio_queue *folioq, size_t data_size,
n = copy_folio_to_iter(folio, skip, len, iter);
if (n != len) {
cifs_dbg(VFS, "%s: something went wrong\n", __func__);
- return -EIO;
+ return smb_EIO2(smb_eio_trace_rx_copy_to_iter,
+ n, len);
}
data_size -= n;
skip = 0;
@@ -4601,7 +4687,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
unsigned int pad_len;
struct cifs_io_subrequest *rdata = mid->callback_data;
struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
- int length;
+ size_t copied;
bool use_rdma_mr = false;
if (shdr->Command != SMB2_READ) {
@@ -4639,7 +4725,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
if (is_offloaded)
mid->mid_state = MID_RESPONSE_RECEIVED;
else
- dequeue_mid(mid, false);
+ dequeue_mid(server, mid, false);
return 0;
}
@@ -4662,11 +4748,11 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
/* data_offset is beyond the end of smallbuf */
cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n",
__func__, data_offset);
- rdata->result = -EIO;
+ rdata->result = smb_EIO1(smb_eio_trace_rx_overlong, data_offset);
if (is_offloaded)
mid->mid_state = MID_RESPONSE_MALFORMED;
else
- dequeue_mid(mid, rdata->result);
+ dequeue_mid(server, mid, rdata->result);
return 0;
}
@@ -4681,21 +4767,21 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
/* data offset is beyond the 1st page of response */
cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n",
__func__, data_offset);
- rdata->result = -EIO;
+ rdata->result = smb_EIO1(smb_eio_trace_rx_overpage, data_offset);
if (is_offloaded)
mid->mid_state = MID_RESPONSE_MALFORMED;
else
- dequeue_mid(mid, rdata->result);
+ dequeue_mid(server, mid, rdata->result);
return 0;
}
if (data_len > buffer_len - pad_len) {
/* data_len is corrupt -- discard frame */
- rdata->result = -EIO;
+ rdata->result = smb_EIO1(smb_eio_trace_rx_bad_datalen, data_len);
if (is_offloaded)
mid->mid_state = MID_RESPONSE_MALFORMED;
else
- dequeue_mid(mid, rdata->result);
+ dequeue_mid(server, mid, rdata->result);
return 0;
}
@@ -4706,7 +4792,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
if (is_offloaded)
mid->mid_state = MID_RESPONSE_MALFORMED;
else
- dequeue_mid(mid, rdata->result);
+ dequeue_mid(server, mid, rdata->result);
return 0;
}
rdata->got_bytes = buffer_len;
@@ -4714,25 +4800,25 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
} else if (buf_len >= data_offset + data_len) {
/* read response payload is in buf */
WARN_ONCE(buffer, "read data can be either in buf or in buffer");
- length = copy_to_iter(buf + data_offset, data_len, &rdata->subreq.io_iter);
- if (length < 0)
- return length;
- rdata->got_bytes = data_len;
+ copied = copy_to_iter(buf + data_offset, data_len, &rdata->subreq.io_iter);
+ if (copied == 0)
+ return smb_EIO2(smb_eio_trace_rx_copy_to_iter, copied, data_len);
+ rdata->got_bytes = copied;
} else {
/* read response payload cannot be in both buf and pages */
WARN_ONCE(1, "buf can not contain only a part of read data");
- rdata->result = -EIO;
+ rdata->result = smb_EIO(smb_eio_trace_rx_both_buf);
if (is_offloaded)
mid->mid_state = MID_RESPONSE_MALFORMED;
else
- dequeue_mid(mid, rdata->result);
+ dequeue_mid(server, mid, rdata->result);
return 0;
}
if (is_offloaded)
mid->mid_state = MID_RESPONSE_RECEIVED;
else
- dequeue_mid(mid, false);
+ dequeue_mid(server, mid, false);
return 0;
}
@@ -4779,30 +4865,30 @@ static void smb2_decrypt_offload(struct work_struct *work)
dw->server->ops->is_network_name_deleted(dw->buf,
dw->server);
- mid->callback(mid);
+ mid_execute_callback(dw->server, mid);
} else {
spin_lock(&dw->server->srv_lock);
if (dw->server->tcpStatus == CifsNeedReconnect) {
- spin_lock(&dw->server->mid_lock);
+ spin_lock(&dw->server->mid_queue_lock);
mid->mid_state = MID_RETRY_NEEDED;
- spin_unlock(&dw->server->mid_lock);
+ spin_unlock(&dw->server->mid_queue_lock);
spin_unlock(&dw->server->srv_lock);
- mid->callback(mid);
+ mid_execute_callback(dw->server, mid);
} else {
- spin_lock(&dw->server->mid_lock);
+ spin_lock(&dw->server->mid_queue_lock);
mid->mid_state = MID_REQUEST_SUBMITTED;
- mid->mid_flags &= ~(MID_DELETED);
+ mid->deleted_from_q = false;
list_add_tail(&mid->qhead,
&dw->server->pending_mid_q);
- spin_unlock(&dw->server->mid_lock);
+ spin_unlock(&dw->server->mid_queue_lock);
spin_unlock(&dw->server->srv_lock);
}
}
- release_mid(mid);
+ release_mid(dw->server, mid);
}
free_pages:
- cifs_clear_folioq_buffer(dw->buffer);
+ netfs_free_folioq_buffer(dw->buffer);
cifs_small_buf_release(dw->buf);
kfree(dw);
}
@@ -4840,9 +4926,9 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
dw->len = len;
len = round_up(dw->len, PAGE_SIZE);
- rc = -ENOMEM;
- dw->buffer = cifs_alloc_folioq_buffer(len);
- if (!dw->buffer)
+ size_t cur_size = 0;
+ rc = netfs_alloc_folioq_buffer(NULL, &dw->buffer, &cur_size, len, GFP_NOFS);
+ if (rc < 0)
goto discard_data;
iov_iter_folio_queue(&iter, ITER_DEST, dw->buffer, 0, 0, len);
@@ -4903,7 +4989,7 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid,
}
free_pages:
- cifs_clear_folioq_buffer(dw->buffer);
+ netfs_free_folioq_buffer(dw->buffer);
free_dw:
kfree(dw);
return rc;
@@ -4959,6 +5045,10 @@ one_more:
next_buffer = (char *)cifs_buf_get();
else
next_buffer = (char *)cifs_small_buf_get();
+ if (!next_buffer) {
+ cifs_server_dbg(VFS, "No memory for (large) SMB response\n");
+ return -1;
+ }
memcpy(next_buffer, buf + next_cmd, pdu_length - next_cmd);
}
@@ -5072,6 +5162,7 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
{
struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms;
+ struct cifs_open_info_data idata;
struct cifs_io_parms io_parms = {};
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_fid fid;
@@ -5140,11 +5231,21 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
FILE_CREATE, CREATE_NOT_DIR |
CREATE_OPTION_SPECIAL, ACL_NO_MODE);
oparms.fid = &fid;
-
- rc = server->ops->open(xid, &oparms, &oplock, NULL);
+ idata.contains_posix_file_info = false;
+ rc = server->ops->open(xid, &oparms, &oplock, &idata);
if (rc)
goto out;
+ /*
+ * Check if the server honored ATTR_SYSTEM flag by CREATE_OPTION_SPECIAL
+ * option. If not then server does not support ATTR_SYSTEM and newly
+ * created file is not SFU compatible, which means that the call failed.
+ */
+ if (!(le32_to_cpu(idata.fi.Attributes) & ATTR_SYSTEM)) {
+ rc = -EOPNOTSUPP;
+ goto out_close;
+ }
+
if (type_len + data_len > 0) {
io_parms.pid = current->tgid;
io_parms.tcon = tcon;
@@ -5159,8 +5260,18 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
iov, ARRAY_SIZE(iov)-1);
}
+out_close:
server->ops->close(xid, tcon, &fid);
+ /*
+ * If CREATE was successful but either setting ATTR_SYSTEM failed or
+ * writing type/data information failed then remove the intermediate
+ * object created by CREATE. Otherwise intermediate empty object stay
+ * on the server.
+ */
+ if (rc)
+ server->ops->unlink(xid, tcon, full_path, cifs_sb, NULL);
+
out:
kfree(symname_utf16);
return rc;
@@ -5198,7 +5309,7 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
const char *full_path, umode_t mode, dev_t dev)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
- int rc;
+ int rc = -EOPNOTSUPP;
/*
* Check if mounted with mount parm 'sfu' mount parm.
@@ -5209,9 +5320,9 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
rc = cifs_sfu_make_node(xid, inode, dentry, tcon,
full_path, mode, dev);
- } else {
- rc = smb2_mknod_reparse(xid, inode, dentry, tcon,
- full_path, mode, dev);
+ } else if (CIFS_REPARSE_SUPPORT(tcon)) {
+ rc = mknod_reparse(xid, inode, dentry, tcon,
+ full_path, mode, dev);
}
return rc;
}
@@ -5266,10 +5377,10 @@ struct smb_version_operations smb20_operations = {
.unlink = smb2_unlink,
.rename = smb2_rename_path,
.create_hardlink = smb2_create_hardlink,
- .parse_reparse_point = smb2_parse_reparse_point,
+ .get_reparse_point_buffer = smb2_get_reparse_point_buffer,
.query_mf_symlink = smb3_query_mf_symlink,
.create_mf_symlink = smb3_create_mf_symlink,
- .create_reparse_symlink = smb2_create_reparse_symlink,
+ .create_reparse_inode = smb2_create_reparse_inode,
.open = smb2_open_file,
.set_fid = smb2_set_fid,
.close = smb2_close_file,
@@ -5292,7 +5403,6 @@ struct smb_version_operations smb20_operations = {
.get_lease_key = smb2_get_lease_key,
.set_lease_key = smb2_set_lease_key,
.new_lease_key = smb2_new_lease_key,
- .calc_signature = smb2_calc_signature,
.is_read_op = smb2_is_read_op,
.set_oplock_level = smb2_set_oplock_level,
.create_lease_buf = smb2_create_lease_buf,
@@ -5316,6 +5426,7 @@ struct smb_version_operations smb20_operations = {
.llseek = smb3_llseek,
.is_status_io_timeout = smb2_is_status_io_timeout,
.is_network_name_deleted = smb2_is_network_name_deleted,
+ .rename_pending_delete = smb2_rename_pending_delete,
};
#endif /* CIFS_ALLOW_INSECURE_LEGACY */
@@ -5369,10 +5480,10 @@ struct smb_version_operations smb21_operations = {
.unlink = smb2_unlink,
.rename = smb2_rename_path,
.create_hardlink = smb2_create_hardlink,
- .parse_reparse_point = smb2_parse_reparse_point,
+ .get_reparse_point_buffer = smb2_get_reparse_point_buffer,
.query_mf_symlink = smb3_query_mf_symlink,
.create_mf_symlink = smb3_create_mf_symlink,
- .create_reparse_symlink = smb2_create_reparse_symlink,
+ .create_reparse_inode = smb2_create_reparse_inode,
.open = smb2_open_file,
.set_fid = smb2_set_fid,
.close = smb2_close_file,
@@ -5395,7 +5506,6 @@ struct smb_version_operations smb21_operations = {
.get_lease_key = smb2_get_lease_key,
.set_lease_key = smb2_set_lease_key,
.new_lease_key = smb2_new_lease_key,
- .calc_signature = smb2_calc_signature,
.is_read_op = smb21_is_read_op,
.set_oplock_level = smb21_set_oplock_level,
.create_lease_buf = smb2_create_lease_buf,
@@ -5421,6 +5531,7 @@ struct smb_version_operations smb21_operations = {
.llseek = smb3_llseek,
.is_status_io_timeout = smb2_is_status_io_timeout,
.is_network_name_deleted = smb2_is_network_name_deleted,
+ .rename_pending_delete = smb2_rename_pending_delete,
};
struct smb_version_operations smb30_operations = {
@@ -5476,10 +5587,10 @@ struct smb_version_operations smb30_operations = {
.unlink = smb2_unlink,
.rename = smb2_rename_path,
.create_hardlink = smb2_create_hardlink,
- .parse_reparse_point = smb2_parse_reparse_point,
+ .get_reparse_point_buffer = smb2_get_reparse_point_buffer,
.query_mf_symlink = smb3_query_mf_symlink,
.create_mf_symlink = smb3_create_mf_symlink,
- .create_reparse_symlink = smb2_create_reparse_symlink,
+ .create_reparse_inode = smb2_create_reparse_inode,
.open = smb2_open_file,
.set_fid = smb2_set_fid,
.close = smb2_close_file,
@@ -5504,7 +5615,6 @@ struct smb_version_operations smb30_operations = {
.set_lease_key = smb2_set_lease_key,
.new_lease_key = smb2_new_lease_key,
.generate_signingkey = generate_smb30signingkey,
- .calc_signature = smb3_calc_signature,
.set_integrity = smb3_set_integrity,
.is_read_op = smb21_is_read_op,
.set_oplock_level = smb3_set_oplock_level,
@@ -5537,6 +5647,7 @@ struct smb_version_operations smb30_operations = {
.llseek = smb3_llseek,
.is_status_io_timeout = smb2_is_status_io_timeout,
.is_network_name_deleted = smb2_is_network_name_deleted,
+ .rename_pending_delete = smb2_rename_pending_delete,
};
struct smb_version_operations smb311_operations = {
@@ -5592,10 +5703,10 @@ struct smb_version_operations smb311_operations = {
.unlink = smb2_unlink,
.rename = smb2_rename_path,
.create_hardlink = smb2_create_hardlink,
- .parse_reparse_point = smb2_parse_reparse_point,
+ .get_reparse_point_buffer = smb2_get_reparse_point_buffer,
.query_mf_symlink = smb3_query_mf_symlink,
.create_mf_symlink = smb3_create_mf_symlink,
- .create_reparse_symlink = smb2_create_reparse_symlink,
+ .create_reparse_inode = smb2_create_reparse_inode,
.open = smb2_open_file,
.set_fid = smb2_set_fid,
.close = smb2_close_file,
@@ -5620,7 +5731,6 @@ struct smb_version_operations smb311_operations = {
.set_lease_key = smb2_set_lease_key,
.new_lease_key = smb2_new_lease_key,
.generate_signingkey = generate_smb311signingkey,
- .calc_signature = smb3_calc_signature,
.set_integrity = smb3_set_integrity,
.is_read_op = smb21_is_read_op,
.set_oplock_level = smb3_set_oplock_level,
@@ -5653,6 +5763,7 @@ struct smb_version_operations smb311_operations = {
.llseek = smb3_llseek,
.is_status_io_timeout = smb2_is_status_io_timeout,
.is_network_name_deleted = smb2_is_network_name_deleted,
+ .rename_pending_delete = smb2_rename_pending_delete,
};
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
@@ -5665,7 +5776,6 @@ struct smb_version_values smb20_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
- .header_preamble_size = 0,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp),
.lock_cmd = SMB2_LOCK,
@@ -5687,7 +5797,6 @@ struct smb_version_values smb21_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
- .header_preamble_size = 0,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp),
.lock_cmd = SMB2_LOCK,
@@ -5708,7 +5817,6 @@ struct smb_version_values smb3any_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
- .header_preamble_size = 0,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp),
.lock_cmd = SMB2_LOCK,
@@ -5729,7 +5837,6 @@ struct smb_version_values smbdefault_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
- .header_preamble_size = 0,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp),
.lock_cmd = SMB2_LOCK,
@@ -5750,7 +5857,6 @@ struct smb_version_values smb30_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
- .header_preamble_size = 0,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp),
.lock_cmd = SMB2_LOCK,
@@ -5771,7 +5877,6 @@ struct smb_version_values smb302_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
- .header_preamble_size = 0,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp),
.lock_cmd = SMB2_LOCK,
@@ -5792,7 +5897,6 @@ struct smb_version_values smb311_values = {
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
- .header_preamble_size = 0,
.max_header_size = MAX_SMB2_HDR_SIZE,
.read_rsp_size = sizeof(struct smb2_read_rsp),
.lock_cmd = SMB2_LOCK,
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 055236835537..5d57c895ca37 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -26,8 +26,8 @@
#include <linux/netfs.h>
#include <trace/events/netfs.h>
#include "cifsglob.h"
-#include "cifsacl.h"
#include "cifsproto.h"
+#include "cifsacl.h"
#include "smb2proto.h"
#include "cifs_unicode.h"
#include "cifs_debug.h"
@@ -36,6 +36,7 @@
#include "smb2glob.h"
#include "cifspdu.h"
#include "cifs_spnego.h"
+#include "../common/smbdirect/smbdirect.h"
#include "smbdirect.h"
#include "trace.h"
#ifdef CONFIG_CIFS_DFS_UPCALL
@@ -43,6 +44,7 @@
#endif
#include "cached_dir.h"
#include "compress.h"
+#include "fs_context.h"
/*
* The following table defines the expected "StructureSize" of SMB2 requests
@@ -166,7 +168,7 @@ out:
static int
cifs_chan_skip_or_disable(struct cifs_ses *ses,
struct TCP_Server_Info *server,
- bool from_reconnect)
+ bool from_reconnect, bool disable_mchan)
{
struct TCP_Server_Info *pserver;
unsigned int chan_index;
@@ -204,22 +206,53 @@ skip_terminate:
return -EHOSTDOWN;
}
- cifs_server_dbg(VFS,
- "server does not support multichannel anymore. Disable all other channels\n");
- cifs_disable_secondary_channels(ses);
-
+ cifs_decrease_secondary_channels(ses, disable_mchan);
return 0;
}
+/*
+ * smb3_update_ses_channels - Synchronize session channels with new configuration
+ * @ses: pointer to the CIFS session structure
+ * @server: pointer to the TCP server info structure
+ * @from_reconnect: indicates if called from reconnect context
+ * @disable_mchan: indicates if called from reconnect to disable multichannel
+ *
+ * Returns 0 on success or error code on failure.
+ *
+ * Outside of reconfigure, this function is called from cifs_mount() during mount
+ * and from reconnect scenarios to adjust channel count when the
+ * server's multichannel support changes.
+ */
+int smb3_update_ses_channels(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ bool from_reconnect, bool disable_mchan)
+{
+ int rc = 0;
+ /*
+ * Manage session channels based on current count vs max:
+ * - If disable requested, skip or disable the channel
+ * - If below max channels, attempt to add more
+ * - If above max channels, skip or disable excess channels
+ */
+ if (disable_mchan)
+ rc = cifs_chan_skip_or_disable(ses, server, from_reconnect, disable_mchan);
+ else {
+ if (ses->chan_count < ses->chan_max)
+ rc = cifs_try_adding_channels(ses);
+ else if (ses->chan_count > ses->chan_max)
+ rc = cifs_chan_skip_or_disable(ses, server, from_reconnect, disable_mchan);
+ }
+
+ return rc;
+}
+
static int
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
struct TCP_Server_Info *server, bool from_reconnect)
{
- int rc = 0;
- struct nls_table *nls_codepage = NULL;
struct cifs_ses *ses;
int xid;
+ int rc = 0;
/*
* SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
@@ -229,11 +262,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
if (tcon == NULL)
return 0;
- /*
- * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in
- * cifs_tree_connect().
- */
- if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL)
+ if (smb2_command == SMB2_TREE_CONNECT)
return 0;
spin_lock(&tcon->tc_lock);
@@ -243,8 +272,8 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
*/
if (smb2_command != SMB2_TREE_DISCONNECT) {
spin_unlock(&tcon->tc_lock);
- cifs_dbg(FYI, "can not send cmd %d while umounting\n",
- smb2_command);
+ cifs_tcon_dbg(FYI, "can not send cmd %d while umounting\n",
+ smb2_command);
return -ENODEV;
}
}
@@ -252,15 +281,15 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
ses = tcon->ses;
if (!ses)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
spin_lock(&ses->ses_lock);
if (ses->ses_status == SES_EXITING) {
spin_unlock(&ses->ses_lock);
- return -EIO;
+ return smb_EIO(smb_eio_trace_sess_exiting);
}
spin_unlock(&ses->ses_lock);
if (!ses->server || !server)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsNeedReconnect) {
@@ -299,43 +328,32 @@ again:
return 0;
}
spin_unlock(&ses->chan_lock);
- cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d",
- tcon->ses->chans_need_reconnect,
- tcon->need_reconnect);
+ cifs_tcon_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d\n",
+ tcon->ses->chans_need_reconnect,
+ tcon->need_reconnect);
mutex_lock(&ses->session_mutex);
/*
- * if this is called by delayed work, and the channel has been disabled
- * in parallel, the delayed work can continue to execute in parallel
- * there's a chance that this channel may not exist anymore
+ * Handle the case where a concurrent thread failed to negotiate or
+ * killed a channel.
*/
spin_lock(&server->srv_lock);
- if (server->tcpStatus == CifsExiting) {
+ switch (server->tcpStatus) {
+ case CifsExiting:
spin_unlock(&server->srv_lock);
mutex_unlock(&ses->session_mutex);
- rc = -EHOSTDOWN;
- goto out;
- }
-
- /*
- * Recheck after acquire mutex. If another thread is negotiating
- * and the server never sends an answer the socket will be closed
- * and tcpStatus set to reconnect.
- */
- if (server->tcpStatus == CifsNeedReconnect) {
+ return -EHOSTDOWN;
+ case CifsNeedReconnect:
spin_unlock(&server->srv_lock);
mutex_unlock(&ses->session_mutex);
-
- if (tcon->retry)
- goto again;
-
- rc = -EHOSTDOWN;
- goto out;
+ if (!tcon->retry)
+ return -EHOSTDOWN;
+ goto again;
+ default:
+ break;
}
spin_unlock(&server->srv_lock);
- nls_codepage = ses->local_nls;
-
/*
* need to prevent multiple threads trying to simultaneously
* reconnect the same SMB session
@@ -357,43 +375,41 @@ again:
spin_unlock(&ses->ses_lock);
rc = cifs_negotiate_protocol(0, ses, server);
- if (!rc) {
- /*
- * if server stopped supporting multichannel
- * and the first channel reconnected, disable all the others.
- */
- if (ses->chan_count > 1 &&
- !(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
- rc = cifs_chan_skip_or_disable(ses, server,
- from_reconnect);
- if (rc) {
- mutex_unlock(&ses->session_mutex);
- goto out;
- }
- }
-
- rc = cifs_setup_session(0, ses, server, nls_codepage);
- if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) {
- /*
- * Try alternate password for next reconnect (key rotation
- * could be enabled on the server e.g.) if an alternate
- * password is available and the current password is expired,
- * but do not swap on non pwd related errors like host down
- */
- if (ses->password2)
- swap(ses->password2, ses->password);
- }
-
- if ((rc == -EACCES) && !tcon->retry) {
- mutex_unlock(&ses->session_mutex);
- rc = -EHOSTDOWN;
- goto failed;
- } else if (rc) {
+ if (rc) {
+ mutex_unlock(&ses->session_mutex);
+ if (!tcon->retry)
+ return -EHOSTDOWN;
+ goto again;
+ }
+ /*
+ * if server stopped supporting multichannel
+ * and the first channel reconnected, disable all the others.
+ */
+ if (ses->chan_count > 1 &&
+ !(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
+ rc = smb3_update_ses_channels(ses, server,
+ from_reconnect, true /* disable_mchan */);
+ if (rc) {
mutex_unlock(&ses->session_mutex);
goto out;
}
- } else {
+ }
+
+ rc = cifs_setup_session(0, ses, server, ses->local_nls);
+ if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) {
+ /*
+ * Try alternate password for next reconnect (key rotation
+ * could be enabled on the server e.g.) if an alternate
+ * password is available and the current password is expired,
+ * but do not swap on non pwd related errors like host down
+ */
+ if (ses->password2)
+ swap(ses->password2, ses->password);
+ }
+ if (rc) {
mutex_unlock(&ses->session_mutex);
+ if (rc == -EACCES && !tcon->retry)
+ return -EHOSTDOWN;
goto out;
}
@@ -406,13 +422,13 @@ skip_sess_setup:
if (tcon->use_persistent)
tcon->need_reopen_files = true;
- rc = cifs_tree_connect(0, tcon, nls_codepage);
+ rc = cifs_tree_connect(0, tcon);
- cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
+ cifs_tcon_dbg(FYI, "reconnect tcon rc = %d\n", rc);
if (rc) {
/* If sess reconnected but tcon didn't, something strange ... */
mutex_unlock(&ses->session_mutex);
- cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc);
+ cifs_tcon_dbg(VFS, "reconnect tcon failed rc = %d\n", rc);
goto out;
}
@@ -428,14 +444,23 @@ skip_sess_setup:
if (!rc &&
(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL) &&
server->ops->query_server_interfaces) {
- mutex_unlock(&ses->session_mutex);
-
/*
- * query server network interfaces, in case they change
+ * query server network interfaces, in case they change.
+ * Also mark the session as pending this update while the query
+ * is in progress. This will be used to avoid calling
+ * smb2_reconnect recursively.
*/
+ ses->flags |= CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES;
xid = get_xid();
rc = server->ops->query_server_interfaces(xid, tcon, false);
free_xid(xid);
+ ses->flags &= ~CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES;
+
+ if (!tcon->ipc && !tcon->dummy)
+ queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
+ (SMB_INTERFACE_POLL_INTERVAL * HZ));
+
+ mutex_unlock(&ses->session_mutex);
if (rc == -EOPNOTSUPP && ses->chan_count > 1) {
/*
@@ -445,23 +470,22 @@ skip_sess_setup:
* treat this as server not supporting multichannel
*/
- rc = cifs_chan_skip_or_disable(ses, server,
- from_reconnect);
+ rc = smb3_update_ses_channels(ses, server,
+ from_reconnect,
+ true /* disable_mchan */);
goto skip_add_channels;
} else if (rc)
- cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
- __func__, rc);
+ cifs_tcon_dbg(FYI, "%s: failed to query server interfaces: %d\n",
+ __func__, rc);
if (ses->chan_max > ses->chan_count &&
ses->iface_count &&
!SERVER_IS_CHAN(server)) {
- if (ses->chan_count == 1) {
+ if (ses->chan_count == 1)
cifs_server_dbg(VFS, "supports multichannel now\n");
- queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
- (SMB_INTERFACE_POLL_INTERVAL * HZ));
- }
- cifs_try_adding_channels(ses);
+ smb3_update_ses_channels(ses, server, from_reconnect,
+ false /* disable_mchan */);
}
} else {
mutex_unlock(&ses->session_mutex);
@@ -473,7 +497,7 @@ skip_add_channels:
spin_unlock(&ses->ses_lock);
if (smb2_command != SMB2_INTERNAL_CMD)
- mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
+ cifs_queue_server_reconn(server);
atomic_inc(&tconInfoReconnectCount);
out:
@@ -494,9 +518,9 @@ out:
case SMB2_CHANGE_NOTIFY:
case SMB2_QUERY_INFO:
case SMB2_SET_INFO:
+ case SMB2_IOCTL:
rc = -EAGAIN;
}
-failed:
return rc;
}
@@ -577,11 +601,18 @@ static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon,
struct TCP_Server_Info *server,
void **request_buf, unsigned int *total_len)
{
- /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */
- if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) {
+ /*
+ * Skip reconnect in one of the following cases:
+ * 1. For FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs
+ * 2. For FSCTL_QUERY_NETWORK_INTERFACE_INFO IOCTL when called from
+ * smb2_reconnect (indicated by CIFS_SES_FLAG_SCALE_CHANNELS ses flag)
+ */
+ if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO ||
+ (opcode == FSCTL_QUERY_NETWORK_INTERFACE_INFO &&
+ (tcon->ses->flags & CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES)))
return __smb2_plain_req_init(SMB2_IOCTL, tcon, server,
request_buf, total_len);
- }
+
return smb2_plain_req_init(SMB2_IOCTL, tcon, server,
request_buf, total_len);
}
@@ -1064,7 +1095,7 @@ SMB2_negotiate(const unsigned int xid,
if (!server) {
WARN(1, "%s: server is NULL!\n", __func__);
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
}
rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server,
@@ -1108,8 +1139,7 @@ SMB2_negotiate(const unsigned int xid,
req->SecurityMode = 0;
req->Capabilities = cpu_to_le32(server->vals->req_capabilities);
- if (ses->chan_max > 1)
- req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
+ req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
/* ClientGUID must be zero for SMB2.02 dialect */
if (server->vals->protocol_id == SMB20_PROT_ID)
@@ -1145,64 +1175,84 @@ SMB2_negotiate(const unsigned int xid,
} else if (rc != 0)
goto neg_exit;
- rc = -EIO;
+ u16 dialect = le16_to_cpu(rsp->DialectRevision);
if (strcmp(server->vals->version_string,
SMB3ANY_VERSION_STRING) == 0) {
- if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
+ switch (dialect) {
+ case SMB20_PROT_ID:
cifs_server_dbg(VFS,
"SMB2 dialect returned but not requested\n");
+ rc = smb_EIO2(smb_eio_trace_neg_unreq_dialect, dialect, 3);
goto neg_exit;
- } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
+ case SMB21_PROT_ID:
cifs_server_dbg(VFS,
"SMB2.1 dialect returned but not requested\n");
+ rc = smb_EIO2(smb_eio_trace_neg_unreq_dialect, dialect, 3);
goto neg_exit;
- } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
+ case SMB311_PROT_ID:
/* ops set to 3.0 by default for default so update */
server->ops = &smb311_operations;
server->vals = &smb311_values;
+ break;
+ default:
+ break;
}
} else if (strcmp(server->vals->version_string,
- SMBDEFAULT_VERSION_STRING) == 0) {
- if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
+ SMBDEFAULT_VERSION_STRING) == 0) {
+ switch (dialect) {
+ case SMB20_PROT_ID:
cifs_server_dbg(VFS,
"SMB2 dialect returned but not requested\n");
+ rc = smb_EIO2(smb_eio_trace_neg_unreq_dialect, dialect, 0);
goto neg_exit;
- } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
+ case SMB21_PROT_ID:
/* ops set to 3.0 by default for default so update */
server->ops = &smb21_operations;
server->vals = &smb21_values;
- } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
+ break;
+ case SMB311_PROT_ID:
server->ops = &smb311_operations;
server->vals = &smb311_values;
+ break;
+ default:
+ break;
}
- } else if (le16_to_cpu(rsp->DialectRevision) !=
- server->vals->protocol_id) {
+ } else if (dialect != server->vals->protocol_id) {
/* if requested single dialect ensure returned dialect matched */
cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n",
- le16_to_cpu(rsp->DialectRevision));
+ dialect);
+ rc = smb_EIO2(smb_eio_trace_neg_unreq_dialect,
+ dialect, server->vals->protocol_id);
goto neg_exit;
}
cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode);
- if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID))
+ switch (dialect) {
+ case SMB20_PROT_ID:
cifs_dbg(FYI, "negotiated smb2.0 dialect\n");
- else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID))
+ break;
+ case SMB21_PROT_ID:
cifs_dbg(FYI, "negotiated smb2.1 dialect\n");
- else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID))
+ break;
+ case SMB30_PROT_ID:
cifs_dbg(FYI, "negotiated smb3.0 dialect\n");
- else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID))
+ break;
+ case SMB302_PROT_ID:
cifs_dbg(FYI, "negotiated smb3.02 dialect\n");
- else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID))
+ break;
+ case SMB311_PROT_ID:
cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n");
- else {
+ break;
+ default:
cifs_server_dbg(VFS, "Invalid dialect returned by server 0x%x\n",
- le16_to_cpu(rsp->DialectRevision));
+ dialect);
+ rc = smb_EIO1(smb_eio_trace_neg_inval_dialect, dialect);
goto neg_exit;
}
rc = 0;
- server->dialect = le16_to_cpu(rsp->DialectRevision);
+ server->dialect = dialect;
/*
* Keep a copy of the hash after negprot. This hash will be
@@ -1231,7 +1281,9 @@ SMB2_negotiate(const unsigned int xid,
* SMB3.0 supports only 1 cipher and doesn't have a encryption neg context
* Set the cipher type manually.
*/
- if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
+ if ((server->dialect == SMB30_PROT_ID ||
+ server->dialect == SMB302_PROT_ID) &&
+ (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
server->cipher_type = SMB2_ENCRYPTION_AES128_CCM;
security_blob = smb2_get_data_area_len(&blob_offset, &blob_length,
@@ -1256,10 +1308,10 @@ SMB2_negotiate(const unsigned int xid,
if (rc == 1)
rc = 0;
else if (rc == 0)
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_neg_decode_token, rc);
}
- if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
+ if (server->dialect == SMB311_PROT_ID) {
if (rsp->NegotiateContextCount)
rc = smb311_decode_neg_context(rsp, server,
rsp_iov.iov_len);
@@ -1267,15 +1319,8 @@ SMB2_negotiate(const unsigned int xid,
cifs_server_dbg(VFS, "Missing expected negotiate contexts\n");
}
- if (server->cipher_type && !rc) {
- if (!SERVER_IS_CHAN(server)) {
- rc = smb3_crypto_aead_allocate(server);
- } else {
- /* For channels, just reuse the primary server crypto secmech. */
- server->secmech.enc = server->primary_server->secmech.enc;
- server->secmech.dec = server->primary_server->secmech.dec;
- }
- }
+ if (server->cipher_type && !rc)
+ rc = smb3_crypto_aead_allocate(server);
neg_exit:
free_rsp_buf(resp_buftype, rsp);
return rc;
@@ -1320,8 +1365,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
pneg_inbuf->Capabilities =
cpu_to_le32(server->vals->req_capabilities);
- if (tcon->ses->chan_max > 1)
- pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
+ pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
memcpy(pneg_inbuf->Guid, server->client_guid,
SMB2_CLIENT_GUID_SIZE);
@@ -1379,32 +1423,47 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
} else if (rc != 0) {
cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n",
rc);
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_neg_info_fail, rc);
goto out_free_inbuf;
}
- rc = -EIO;
if (rsplen != sizeof(*pneg_rsp)) {
cifs_tcon_dbg(VFS, "Invalid protocol negotiate response size: %d\n",
rsplen);
/* relax check since Mac returns max bufsize allowed on ioctl */
- if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp))
+ if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp)) {
+ rc = smb_EIO1(smb_eio_trace_neg_bad_rsplen, rsplen);
goto out_free_rsp;
+ }
}
/* check validate negotiate info response matches what we got earlier */
- if (pneg_rsp->Dialect != cpu_to_le16(server->dialect))
+ u16 dialect = le16_to_cpu(pneg_rsp->Dialect);
+
+ if (dialect != server->dialect) {
+ rc = smb_EIO2(smb_eio_trace_neg_info_dialect,
+ dialect, server->dialect);
goto vneg_out;
+ }
- if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode))
+ u16 sec_mode = le16_to_cpu(pneg_rsp->SecurityMode);
+
+ if (sec_mode != server->sec_mode) {
+ rc = smb_EIO2(smb_eio_trace_neg_info_sec_mode,
+ sec_mode, server->sec_mode);
goto vneg_out;
+ }
/* do not validate server guid because not saved at negprot time yet */
+ u32 caps = le32_to_cpu(pneg_rsp->Capabilities);
- if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND |
- SMB2_LARGE_FILES) != server->capabilities)
+ if ((caps | SMB2_NT_FIND |
+ SMB2_LARGE_FILES) != server->capabilities) {
+ rc = smb_EIO2(smb_eio_trace_neg_info_caps,
+ caps, server->capabilities);
goto vneg_out;
+ }
/* validate negotiate successful */
rc = 0;
@@ -1433,7 +1492,7 @@ smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
if (server->sec_ntlmssp &&
(global_secflags & CIFSSEC_MAY_NTLMSSP))
return RawNTLMSSP;
- if ((server->sec_kerberos || server->sec_mskerberos) &&
+ if ((server->sec_kerberos || server->sec_mskerberos || server->sec_iakerb) &&
(global_secflags & CIFSSEC_MAY_KRB5))
return Kerberos;
fallthrough;
@@ -1636,8 +1695,6 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
spnego_key = cifs_get_spnego_key(ses, server);
if (IS_ERR(spnego_key)) {
rc = PTR_ERR(spnego_key);
- if (rc == -ENOKEY)
- cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n");
spnego_key = NULL;
goto out;
}
@@ -1766,11 +1823,11 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
if (rc)
goto out;
- if (offsetof(struct smb2_sess_setup_rsp, Buffer) !=
- le16_to_cpu(rsp->SecurityBufferOffset)) {
- cifs_dbg(VFS, "Invalid security buffer offset %d\n",
- le16_to_cpu(rsp->SecurityBufferOffset));
- rc = -EIO;
+ u16 boff = le16_to_cpu(rsp->SecurityBufferOffset);
+
+ if (offsetof(struct smb2_sess_setup_rsp, Buffer) != boff) {
+ cifs_dbg(VFS, "Invalid security buffer offset %d\n", boff);
+ rc = smb_EIO1(smb_eio_trace_sess_buf_off, boff);
goto out;
}
rc = decode_ntlmssp_challenge(rsp->Buffer,
@@ -1924,7 +1981,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
if (!server) {
WARN(1, "%s: server is NULL!\n", __func__);
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
}
sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL);
@@ -1974,10 +2031,9 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
cifs_dbg(FYI, "disconnect session %p\n", ses);
- if (ses && (ses->server))
- server = ses->server;
- else
- return -EIO;
+ if (!ses || !ses->server)
+ return smb_EIO(smb_eio_trace_null_pointers);
+ server = ses->server;
/* no need to send SMB logoff if uid already closed due to reconnect */
spin_lock(&ses->chan_lock);
@@ -2056,7 +2112,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
cifs_dbg(FYI, "TCON\n");
if (!server || !tree)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL);
if (unc_path == NULL)
@@ -2173,7 +2229,7 @@ tcon_exit:
tcon_error_exit:
if (rsp && rsp->hdr.Status == STATUS_BAD_NETWORK_NAME)
- cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree);
+ cifs_dbg(VFS | ONCE, "BAD_NETWORK_NAME: %s\n", tree);
goto tcon_exit;
}
@@ -2194,7 +2250,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
cifs_dbg(FYI, "Tree Disconnect\n");
if (!ses || !(ses->server))
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
trace_smb3_tdis_enter(xid, tcon->tid, ses->Suid, tcon->tree_name);
spin_lock(&ses->chan_lock);
@@ -2237,21 +2293,20 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
return rc;
}
-
-static struct create_durable *
+static create_durable_req_t *
create_durable_buf(void)
{
- struct create_durable *buf;
+ create_durable_req_t *buf;
- buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
+ buf = kzalloc(sizeof(create_durable_req_t), GFP_KERNEL);
if (!buf)
return NULL;
buf->ccontext.DataOffset = cpu_to_le16(offsetof
- (struct create_durable, Data));
+ (create_durable_req_t, Data));
buf->ccontext.DataLength = cpu_to_le32(16);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
- (struct create_durable, Name));
+ (create_durable_req_t, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */
buf->Name[0] = 'D';
@@ -2261,20 +2316,20 @@ create_durable_buf(void)
return buf;
}
-static struct create_durable *
+static create_durable_req_t *
create_reconnect_durable_buf(struct cifs_fid *fid)
{
- struct create_durable *buf;
+ create_durable_req_t *buf;
- buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
+ buf = kzalloc(sizeof(create_durable_req_t), GFP_KERNEL);
if (!buf)
return NULL;
buf->ccontext.DataOffset = cpu_to_le16(offsetof
- (struct create_durable, Data));
+ (create_durable_req_t, Data));
buf->ccontext.DataLength = cpu_to_le32(16);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
- (struct create_durable, Name));
+ (create_durable_req_t, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
buf->Data.Fid.PersistentFileId = fid->persistent_fid;
buf->Data.Fid.VolatileFileId = fid->volatile_fid;
@@ -2333,7 +2388,7 @@ parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info,
int smb2_parse_contexts(struct TCP_Server_Info *server,
struct kvec *rsp_iov,
- unsigned int *epoch,
+ __u16 *epoch,
char *lease_key, __u8 *oplock,
struct smb2_file_all_info *buf,
struct create_posix_rsp *posix)
@@ -2414,11 +2469,16 @@ static int
add_lease_context(struct TCP_Server_Info *server,
struct smb2_create_req *req,
struct kvec *iov,
- unsigned int *num_iovec, u8 *lease_key, __u8 *oplock)
+ unsigned int *num_iovec,
+ u8 *lease_key,
+ __u8 *oplock,
+ u8 *parent_lease_key,
+ __le32 flags)
{
unsigned int num = *num_iovec;
- iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock);
+ iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock,
+ parent_lease_key, flags);
if (iov[num].iov_base == NULL)
return -ENOMEM;
iov[num].iov_len = server->vals->create_lease_size;
@@ -2427,21 +2487,21 @@ add_lease_context(struct TCP_Server_Info *server,
return 0;
}
-static struct create_durable_v2 *
+static struct create_durable_req_v2 *
create_durable_v2_buf(struct cifs_open_parms *oparms)
{
struct cifs_fid *pfid = oparms->fid;
- struct create_durable_v2 *buf;
+ struct create_durable_req_v2 *buf;
- buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL);
+ buf = kzalloc(sizeof(struct create_durable_req_v2), GFP_KERNEL);
if (!buf)
return NULL;
buf->ccontext.DataOffset = cpu_to_le16(offsetof
- (struct create_durable_v2, dcontext));
- buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2));
+ (struct create_durable_req_v2, dcontext));
+ buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2_req));
buf->ccontext.NameOffset = cpu_to_le16(offsetof
- (struct create_durable_v2, Name));
+ (struct create_durable_req_v2, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
/*
@@ -2511,7 +2571,7 @@ add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec,
iov[num].iov_base = create_durable_v2_buf(oparms);
if (iov[num].iov_base == NULL)
return -ENOMEM;
- iov[num].iov_len = sizeof(struct create_durable_v2);
+ iov[num].iov_len = sizeof(struct create_durable_req_v2);
*num_iovec = num + 1;
return 0;
}
@@ -2555,7 +2615,7 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec,
iov[num].iov_base = create_durable_buf();
if (iov[num].iov_base == NULL)
return -ENOMEM;
- iov[num].iov_len = sizeof(struct create_durable);
+ iov[num].iov_len = sizeof(create_durable_req_t);
*num_iovec = num + 1;
return 0;
}
@@ -2860,7 +2920,7 @@ replay_again:
return -ENOMEM;
if (!ses || !server) {
- rc = -EIO;
+ rc = smb_EIO(smb_eio_trace_null_pointers);
goto err_free_path;
}
@@ -2943,6 +3003,7 @@ replay_again:
req->CreateContextsOffset = cpu_to_le32(
sizeof(struct smb2_create_req) +
iov[1].iov_len);
+ le32_add_cpu(&req->CreateContextsLength, iov[n_iov-1].iov_len);
pc_buf = iov[n_iov-1].iov_base;
}
@@ -2976,7 +3037,7 @@ replay_again:
*/
rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
if (rsp == NULL) {
- rc = -EIO;
+ rc = smb_EIO(smb_eio_trace_mkdir_no_rsp);
kfree(pc_buf);
goto err_free_req;
}
@@ -2989,7 +3050,7 @@ replay_again:
/* Eventually save off posix specific response info and timestamps */
err_free_rsp_buf:
- free_rsp_buf(resp_buftype, rsp);
+ free_rsp_buf(resp_buftype, rsp_iov.iov_base);
kfree(pc_buf);
err_free_req:
cifs_small_buf_release(req);
@@ -3090,7 +3151,9 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
req->RequestedOplockLevel = *oplock; /* no srv lease support */
else {
rc = add_lease_context(server, req, iov, &n_iov,
- oparms->fid->lease_key, oplock);
+ oparms->fid->lease_key, oplock,
+ oparms->fid->parent_lease_key,
+ oparms->lease_flags);
if (rc)
return rc;
}
@@ -3212,7 +3275,7 @@ replay_again:
cifs_dbg(FYI, "create/open\n");
if (!ses || !server)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
@@ -3277,7 +3340,7 @@ replay_again:
buf->EndOfFile = rsp->EndofFile;
buf->Attributes = rsp->FileAttributes;
buf->NumberOfLinks = cpu_to_le32(1);
- buf->DeletePending = 0;
+ buf->DeletePending = 0; /* successful open = not delete pending */
}
@@ -3418,11 +3481,11 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
int retries = 0, cur_sleep = 1;
if (!tcon)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
ses = tcon->ses;
if (!ses)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
replay_again:
/* reinitialize for possible replay */
@@ -3430,7 +3493,7 @@ replay_again:
server = cifs_pick_channel(ses);
if (!server)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
cifs_dbg(FYI, "SMB2 IOCTL\n");
@@ -3493,7 +3556,7 @@ replay_again:
* warning)
*/
if (rsp == NULL) {
- rc = -EIO;
+ rc = smb_EIO(smb_eio_trace_ioctl_no_rsp);
goto ioctl_exit;
}
@@ -3504,16 +3567,18 @@ replay_again:
goto ioctl_exit; /* server returned no data */
else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) {
cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen);
+ rc = smb_EIO2(smb_eio_trace_ioctl_data_len, *plen, rsp_iov.iov_len);
*plen = 0;
- rc = -EIO;
goto ioctl_exit;
}
- if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) {
- cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen,
- le32_to_cpu(rsp->OutputOffset));
+ u32 outoff = le32_to_cpu(rsp->OutputOffset);
+
+ if (rsp_iov.iov_len - *plen < outoff) {
+ cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n",
+ *plen, outoff);
+ rc = smb_EIO2(smb_eio_trace_ioctl_out_off, rsp_iov.iov_len - *plen, outoff);
*plen = 0;
- rc = -EIO;
goto ioctl_exit;
}
@@ -3621,7 +3686,7 @@ replay_again:
cifs_dbg(FYI, "Close\n");
if (!ses || !server)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
@@ -3818,7 +3883,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
cifs_dbg(FYI, "Query Info\n");
if (!ses)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
replay_again:
/* reinitialize for possible replay */
@@ -3827,7 +3892,7 @@ replay_again:
server = cifs_pick_channel(ses);
if (!server)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
@@ -3914,7 +3979,8 @@ int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
/* currently unused, as now we are doing compounding instead (see smb311_posix_query_path_info) */
int
SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon,
- u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen)
+ u64 persistent_fid, u64 volatile_fid,
+ struct smb311_posix_qinfo *data, u32 *plen)
{
size_t output_len = sizeof(struct smb311_posix_qinfo *) +
(sizeof(struct smb_sid) * 2) + (PATH_MAX * 2);
@@ -3932,12 +3998,10 @@ SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
void **data, u32 *plen, u32 extra_info)
{
- __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO |
- extra_info;
*plen = 0;
return query_info(xid, tcon, persistent_fid, volatile_fid,
- 0, SMB2_O_INFO_SECURITY, additional_info,
+ 0, SMB2_O_INFO_SECURITY, extra_info,
SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen);
}
@@ -4014,7 +4078,7 @@ replay_again:
cifs_dbg(FYI, "change notify\n");
if (!ses || !server)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
@@ -4056,9 +4120,12 @@ replay_again:
smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base;
- smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset),
- le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov,
+ rc = smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset),
+ le32_to_cpu(smb_rsp->OutputBufferLength),
+ &rsp_iov,
sizeof(struct file_notify_information));
+ if (rc)
+ goto cnotify_exit;
*out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset),
le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL);
@@ -4091,9 +4158,8 @@ replay_again:
* FIXME: maybe we should consider checking that the reply matches request?
*/
static void
-smb2_echo_callback(struct mid_q_entry *mid)
+smb2_echo_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
- struct TCP_Server_Info *server = mid->callback_data;
struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf;
struct cifs_credits credits = { .value = 0, .instance = 0 };
@@ -4103,10 +4169,24 @@ smb2_echo_callback(struct mid_q_entry *mid)
credits.instance = server->reconnect_instance;
}
- release_mid(mid);
+ release_mid(server, mid);
add_credits(server, &credits, CIFS_ECHO_OP);
}
+static void cifs_renegotiate_iosize(struct TCP_Server_Info *server,
+ struct cifs_tcon *tcon)
+{
+ struct cifs_sb_info *cifs_sb;
+
+ if (server == NULL || tcon == NULL)
+ return;
+
+ spin_lock(&tcon->sb_list_lock);
+ list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link)
+ cifs_negotiate_iosize(server, cifs_sb->ctx, tcon);
+ spin_unlock(&tcon->sb_list_lock);
+}
+
void smb2_reconnect_server(struct work_struct *work)
{
struct TCP_Server_Info *server = container_of(work,
@@ -4192,9 +4272,10 @@ void smb2_reconnect_server(struct work_struct *work)
list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true);
- if (!rc)
+ if (!rc) {
+ cifs_renegotiate_iosize(server, tcon);
cifs_reopen_persistent_handles(tcon);
- else
+ } else
resched = true;
list_del_init(&tcon->rlist);
if (tcon->ipc)
@@ -4216,10 +4297,8 @@ void smb2_reconnect_server(struct work_struct *work)
}
goto done;
}
-
tcon->status = TID_GOOD;
- tcon->retry = false;
- tcon->need_reconnect = false;
+ tcon->dummy = true;
/* now reconnect sessions for necessary channels */
list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) {
@@ -4235,7 +4314,7 @@ void smb2_reconnect_server(struct work_struct *work)
done:
cifs_dbg(FYI, "Reconnecting tcons and channels finished\n");
if (resched)
- queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ);
+ cifs_requeue_server_reconn(server);
mutex_unlock(&pserver->reconnect_mutex);
/* now we can safely release srv struct */
@@ -4259,7 +4338,7 @@ SMB2_echo(struct TCP_Server_Info *server)
server->ops->need_neg(server)) {
spin_unlock(&server->srv_lock);
/* No need to send echo on newly established connections */
- mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
+ cifs_queue_server_reconn(server);
return rc;
}
spin_unlock(&server->srv_lock);
@@ -4335,7 +4414,7 @@ replay_again:
cifs_dbg(FYI, "flush\n");
if (!ses || !(ses->server))
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
@@ -4400,7 +4479,7 @@ static inline bool smb3_use_rdma_offload(struct cifs_io_parms *io_parms)
return false;
/* offload also has its overhead, so only do it if desired */
- if (io_parms->length < server->smbd_conn->rdma_readwrite_threshold)
+ if (io_parms->length < server->rdma_readwrite_threshold)
return false;
return true;
@@ -4450,10 +4529,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
#ifdef CONFIG_CIFS_SMB_DIRECT
/*
* If we want to do a RDMA write, fill in and append
- * smbd_buffer_descriptor_v1 to the end of read request
+ * smbdirect_buffer_descriptor_v1 to the end of read request
*/
if (rdata && smb3_use_rdma_offload(io_parms)) {
- struct smbd_buffer_descriptor_v1 *v1;
+ struct smbdirect_buffer_descriptor_v1 *v1;
bool need_invalidate = server->dialect == SMB30_PROT_ID;
rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->subreq.io_iter,
@@ -4467,8 +4546,8 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
req->ReadChannelInfoOffset =
cpu_to_le16(offsetof(struct smb2_read_req, Buffer));
req->ReadChannelInfoLength =
- cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1));
- v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0];
+ cpu_to_le16(sizeof(struct smbdirect_buffer_descriptor_v1));
+ v1 = (struct smbdirect_buffer_descriptor_v1 *) &req->Buffer[0];
v1->offset = cpu_to_le64(rdata->mr->mr->iova);
v1->token = cpu_to_le32(rdata->mr->mr->rkey);
v1->length = cpu_to_le32(rdata->mr->mr->length);
@@ -4504,30 +4583,20 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
return rc;
}
-static void smb2_readv_worker(struct work_struct *work)
-{
- struct cifs_io_subrequest *rdata =
- container_of(work, struct cifs_io_subrequest, subreq.work);
-
- netfs_read_subreq_terminated(&rdata->subreq, rdata->result, false);
-}
-
static void
-smb2_readv_callback(struct mid_q_entry *mid)
+smb2_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
struct cifs_io_subrequest *rdata = mid->callback_data;
struct netfs_inode *ictx = netfs_inode(rdata->rreq->inode);
struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink);
- struct TCP_Server_Info *server = rdata->server;
- struct smb2_hdr *shdr =
- (struct smb2_hdr *)rdata->iov[0].iov_base;
+ struct smb2_hdr *shdr = (struct smb2_hdr *)rdata->iov[0].iov_base;
struct cifs_credits credits = {
.value = 0,
.instance = 0,
.rreq_debug_id = rdata->rreq->debug_id,
.rreq_debug_index = rdata->subreq.debug_index,
};
- struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], .rq_nvec = 1 };
+ struct smb_rqst rqst = { .rq_iov = &rdata->iov[0], .rq_nvec = 1 };
unsigned int rreq_debug_id = rdata->rreq->debug_id;
unsigned int subreq_debug_index = rdata->subreq.debug_index;
@@ -4535,9 +4604,9 @@ smb2_readv_callback(struct mid_q_entry *mid)
rqst.rq_iter = rdata->subreq.io_iter;
}
- WARN_ONCE(rdata->server != mid->server,
+ WARN_ONCE(rdata->server != server,
"rdata server %p != mid server %p",
- rdata->server, mid->server);
+ rdata->server, server);
cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%zu/%zu\n",
__func__, mid->mid, mid->mid_state, rdata->result,
@@ -4562,7 +4631,11 @@ smb2_readv_callback(struct mid_q_entry *mid)
cifs_stats_bytes_read(tcon, rdata->got_bytes);
break;
case MID_REQUEST_SUBMITTED:
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_req_submitted);
+ goto do_retry;
case MID_RETRY_NEEDED:
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed);
+do_retry:
__set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags);
rdata->result = -EAGAIN;
if (server->sign && rdata->got_bytes)
@@ -4573,11 +4646,16 @@ smb2_readv_callback(struct mid_q_entry *mid)
cifs_stats_bytes_read(tcon, rdata->got_bytes);
break;
case MID_RESPONSE_MALFORMED:
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_malformed);
credits.value = le16_to_cpu(shdr->CreditRequest);
credits.instance = server->reconnect_instance;
- fallthrough;
+ rdata->result = smb_EIO(smb_eio_trace_read_rsp_malformed);
+ break;
default:
- rdata->result = -EIO;
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_unknown);
+ rdata->result = smb_EIO1(smb_eio_trace_read_mid_state_unknown,
+ mid->mid_state);
+ break;
}
#ifdef CONFIG_CIFS_SMB_DIRECT
/*
@@ -4615,20 +4693,22 @@ smb2_readv_callback(struct mid_q_entry *mid)
} else {
size_t trans = rdata->subreq.transferred + rdata->got_bytes;
if (trans < rdata->subreq.len &&
- rdata->subreq.start + trans == ictx->remote_i_size) {
+ rdata->subreq.start + trans >= ictx->remote_i_size) {
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
rdata->result = 0;
}
+ if (rdata->got_bytes)
+ __set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags);
}
trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, rdata->credits.value,
server->credits, server->in_flight,
0, cifs_trace_rw_credits_read_response_clear);
rdata->credits.value = 0;
+ rdata->subreq.error = rdata->result;
rdata->subreq.transferred += rdata->got_bytes;
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_progress);
- INIT_WORK(&rdata->subreq.work, smb2_readv_worker);
- queue_work(cifsiod_wq, &rdata->subreq.work);
- release_mid(mid);
+ netfs_read_subreq_terminated(&rdata->subreq);
+ release_mid(server, mid);
trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, 0,
server->credits, server->in_flight,
credits.value, cifs_trace_rw_credits_read_response_add);
@@ -4783,7 +4863,8 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
(*nbytes > io_parms->length)) {
cifs_dbg(FYI, "bad length %d for count %d\n",
*nbytes, io_parms->length);
- rc = -EIO;
+ rc = smb_EIO2(smb_eio_trace_read_overlarge,
+ *nbytes, io_parms->length);
*nbytes = 0;
}
@@ -4805,11 +4886,10 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
* workqueue completion task.
*/
static void
-smb2_writev_callback(struct mid_q_entry *mid)
+smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
struct cifs_io_subrequest *wdata = mid->callback_data;
struct cifs_tcon *tcon = tlink_tcon(wdata->req->cfile->tlink);
- struct TCP_Server_Info *server = wdata->server;
struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf;
struct cifs_credits credits = {
.value = 0,
@@ -4822,17 +4902,20 @@ smb2_writev_callback(struct mid_q_entry *mid)
ssize_t result = 0;
size_t written;
- WARN_ONCE(wdata->server != mid->server,
+ WARN_ONCE(wdata->server != server,
"wdata server %p != mid server %p",
- wdata->server, mid->server);
+ wdata->server, server);
switch (mid->mid_state) {
case MID_RESPONSE_RECEIVED:
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_progress);
credits.value = le16_to_cpu(rsp->hdr.CreditRequest);
credits.instance = server->reconnect_instance;
result = smb2_check_receive(mid, server, 0);
- if (result != 0)
+ if (result != 0) {
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_bad);
break;
+ }
written = le32_to_cpu(rsp->DataLength);
/*
@@ -4844,21 +4927,35 @@ smb2_writev_callback(struct mid_q_entry *mid)
if (written > wdata->subreq.len)
written &= 0xFFFF;
- if (written < wdata->subreq.len)
+ cifs_stats_bytes_written(tcon, written);
+
+ if (written < wdata->subreq.len) {
wdata->result = -ENOSPC;
- else
+ } else if (written > 0) {
wdata->subreq.len = written;
+ __set_bit(NETFS_SREQ_MADE_PROGRESS, &wdata->subreq.flags);
+ }
break;
case MID_REQUEST_SUBMITTED:
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_req_submitted);
+ __set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags);
+ result = -EAGAIN;
+ break;
case MID_RETRY_NEEDED:
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_retry_needed);
+ __set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags);
result = -EAGAIN;
break;
case MID_RESPONSE_MALFORMED:
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_malformed);
credits.value = le16_to_cpu(rsp->hdr.CreditRequest);
credits.instance = server->reconnect_instance;
- fallthrough;
+ result = smb_EIO(smb_eio_trace_write_rsp_malformed);
+ break;
default:
- result = -EIO;
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_unknown);
+ result = smb_EIO1(smb_eio_trace_write_mid_state_unknown,
+ mid->mid_state);
break;
}
#ifdef CONFIG_CIFS_SMB_DIRECT
@@ -4897,9 +4994,8 @@ smb2_writev_callback(struct mid_q_entry *mid)
server->credits, server->in_flight,
0, cifs_trace_rw_credits_write_response_clear);
wdata->credits.value = 0;
- trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_progress);
- cifs_write_subrequest_terminated(wdata, result ?: written, true);
- release_mid(mid);
+ cifs_write_subrequest_terminated(wdata, result ?: written);
+ release_mid(server, mid);
trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, 0,
server->credits, server->in_flight,
credits.value, cifs_trace_rw_credits_write_response_add);
@@ -4978,10 +5074,10 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
#ifdef CONFIG_CIFS_SMB_DIRECT
/*
* If we want to do a server RDMA read, fill in and append
- * smbd_buffer_descriptor_v1 to the end of write request
+ * smbdirect_buffer_descriptor_v1 to the end of write request
*/
if (smb3_use_rdma_offload(io_parms)) {
- struct smbd_buffer_descriptor_v1 *v1;
+ struct smbdirect_buffer_descriptor_v1 *v1;
bool need_invalidate = server->dialect == SMB30_PROT_ID;
wdata->mr = smbd_register_mr(server->smbd_conn, &wdata->subreq.io_iter,
@@ -5000,8 +5096,8 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
req->WriteChannelInfoOffset =
cpu_to_le16(offsetof(struct smb2_write_req, Buffer));
req->WriteChannelInfoLength =
- cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1));
- v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0];
+ cpu_to_le16(sizeof(struct smbdirect_buffer_descriptor_v1));
+ v1 = (struct smbdirect_buffer_descriptor_v1 *) &req->Buffer[0];
v1->offset = cpu_to_le64(wdata->mr->mr->iova);
v1->token = cpu_to_le32(wdata->mr->mr->rkey);
v1->length = cpu_to_le32(wdata->mr->mr->length);
@@ -5016,7 +5112,7 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
}
#endif
- if (test_bit(NETFS_SREQ_RETRYING, &wdata->subreq.flags))
+ if (wdata->subreq.retry_count > 0)
smb2_set_replay(server, &rqst);
cifs_dbg(FYI, "async write at %llu %u bytes iter=%zx\n",
@@ -5071,7 +5167,7 @@ out:
-(int)wdata->credits.value,
cifs_trace_rw_credits_write_response_clear);
add_credits_and_wake_if(wdata->server, &wdata->credits, 0);
- cifs_write_subrequest_terminated(wdata, rc, true);
+ cifs_write_subrequest_terminated(wdata, rc);
}
}
@@ -5160,6 +5256,7 @@ replay_again:
cifs_dbg(VFS, "Send error in write = %d\n", rc);
} else {
*nbytes = le32_to_cpu(rsp->DataLength);
+ cifs_stats_bytes_written(io_parms->tcon, *nbytes);
trace_smb3_write_done(0, 0, xid,
req->PersistentFileId,
io_parms->tcon->tid,
@@ -5422,7 +5519,7 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
info_buf_size = sizeof(FILE_DIRECTORY_INFO);
break;
case SMB_FIND_FILE_ID_FULL_DIR_INFO:
- info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO);
+ info_buf_size = sizeof(FILE_ID_FULL_DIR_INFO);
break;
case SMB_FIND_FILE_POSIX_INFO:
/* note that posix payload are variable size */
@@ -5501,7 +5598,7 @@ replay_again:
server = cifs_pick_channel(ses);
if (!ses || !(ses->server))
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
if (smb3_encryption_required(tcon))
flags |= CIFS_TRANSFORM_REQ;
@@ -5636,7 +5733,7 @@ replay_again:
server = cifs_pick_channel(ses);
if (!ses || !server)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
if (!num)
return -EINVAL;
@@ -5833,7 +5930,7 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon,
cifs_dbg(FYI, "Query FSInfo level %d\n", level);
if ((tcon->ses == NULL) || server == NULL)
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server,
(void **) &req, &total_len);
@@ -5926,71 +6023,6 @@ posix_qfsinf_exit:
}
int
-SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
- u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
-{
- struct smb_rqst rqst;
- struct smb2_query_info_rsp *rsp = NULL;
- struct kvec iov;
- struct kvec rsp_iov;
- int rc = 0;
- int resp_buftype;
- struct cifs_ses *ses = tcon->ses;
- struct TCP_Server_Info *server;
- struct smb2_fs_full_size_info *info = NULL;
- int flags = 0;
- int retries = 0, cur_sleep = 1;
-
-replay_again:
- /* reinitialize for possible replay */
- flags = 0;
- server = cifs_pick_channel(ses);
-
- rc = build_qfs_info_req(&iov, tcon, server,
- FS_FULL_SIZE_INFORMATION,
- sizeof(struct smb2_fs_full_size_info),
- persistent_fid, volatile_fid);
- if (rc)
- return rc;
-
- if (smb3_encryption_required(tcon))
- flags |= CIFS_TRANSFORM_REQ;
-
- memset(&rqst, 0, sizeof(struct smb_rqst));
- rqst.rq_iov = &iov;
- rqst.rq_nvec = 1;
-
- if (retries)
- smb2_set_replay(server, &rqst);
-
- rc = cifs_send_recv(xid, ses, server,
- &rqst, &resp_buftype, flags, &rsp_iov);
- free_qfs_info_req(&iov);
- if (rc) {
- cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
- goto qfsinf_exit;
- }
- rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
-
- info = (struct smb2_fs_full_size_info *)(
- le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp);
- rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
- le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
- sizeof(struct smb2_fs_full_size_info));
- if (!rc)
- smb2_copy_fs_info_to_kstatfs(info, fsdata);
-
-qfsinf_exit:
- free_rsp_buf(resp_buftype, rsp_iov.iov_base);
-
- if (is_replayable_error(rc) &&
- smb2_should_replay(tcon, &retries, &cur_sleep))
- goto replay_again;
-
- return rc;
-}
-
-int
SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, int level)
{
@@ -6015,8 +6047,8 @@ replay_again:
max_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
min_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
} else if (level == FS_ATTRIBUTE_INFORMATION) {
- max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO);
- min_len = MIN_FS_ATTR_INFO_SIZE;
+ max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO) + MAX_FS_NAME_LEN;
+ min_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO);
} else if (level == FS_SECTOR_SIZE_INFORMATION) {
max_len = sizeof(struct smb3_fs_ss_info);
min_len = sizeof(struct smb3_fs_ss_info);
@@ -6062,7 +6094,7 @@ replay_again:
if (level == FS_ATTRIBUTE_INFORMATION)
memcpy(&tcon->fsAttrInfo, offset
+ (char *)rsp, min_t(unsigned int,
- rsp_len, max_len));
+ rsp_len, min_len));
else if (level == FS_DEVICE_INFORMATION)
memcpy(&tcon->fsDevInfo, offset
+ (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO));
@@ -6208,7 +6240,7 @@ SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
req->StructureSize = cpu_to_le16(36);
total_len += 12;
- memcpy(req->LeaseKey, lease_key, 16);
+ memcpy(req->LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE);
req->LeaseState = lease_state;
flags |= CIFS_NO_RSP_BUF;
@@ -6228,11 +6260,11 @@ SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
please_key_high = (__u64 *)(lease_key+8);
if (rc) {
cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
- trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid,
+ trace_smb3_lease_ack_err(le32_to_cpu(lease_state), tcon->tid,
ses->Suid, *please_key_low, *please_key_high, rc);
cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc);
} else
- trace_smb3_lease_done(le32_to_cpu(lease_state), tcon->tid,
+ trace_smb3_lease_ack_done(le32_to_cpu(lease_state), tcon->tid,
ses->Suid, *please_key_low, *please_key_high);
return rc;
diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h
index 076d9e83e1a0..78bb99f29d38 100644
--- a/fs/smb/client/smb2pdu.h
+++ b/fs/smb/client/smb2pdu.h
@@ -79,7 +79,7 @@ struct smb2_symlink_err_rsp {
struct smb2_error_context_rsp {
__le32 ErrorDataLength;
__le32 ErrorId;
- __u8 ErrorContextData; /* ErrorDataLength long array */
+ __u8 ErrorContextData[] __counted_by_le(ErrorDataLength);
} __packed;
/* ErrorId values */
@@ -133,46 +133,6 @@ struct share_redirect_error_context_rsp {
#define SMB2_LEASE_HANDLE_CACHING_HE 0x02
#define SMB2_LEASE_WRITE_CACHING_HE 0x04
-
-/* See MS-SMB2 2.2.13.2.11 */
-/* Flags */
-#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
-struct durable_context_v2 {
- __le32 Timeout;
- __le32 Flags;
- __u64 Reserved;
- __u8 CreateGuid[16];
-} __packed;
-
-struct create_durable_v2 {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- struct durable_context_v2 dcontext;
-} __packed;
-
-/* See MS-SMB2 2.2.13.2.12 */
-struct durable_reconnect_context_v2 {
- struct {
- __u64 PersistentFileId;
- __u64 VolatileFileId;
- } Fid;
- __u8 CreateGuid[16];
- __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */
-} __packed;
-
-/* See MS-SMB2 2.2.14.2.12 */
-struct durable_reconnect_context_v2_rsp {
- __le32 Timeout;
- __le32 Flags; /* see above DHANDLE_FLAG_PERSISTENT */
-} __packed;
-
-struct create_durable_handle_reconnect_v2 {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- struct durable_reconnect_context_v2 dcontext;
- __u8 Pad[4];
-} __packed;
-
/* See MS-SMB2 2.2.13.2.5 */
struct crt_twarp_ctxt {
struct create_context_hdr ccontext;
@@ -193,32 +153,6 @@ struct crt_sd_ctxt {
struct smb3_sd sd;
} __packed;
-
-#define COPY_CHUNK_RES_KEY_SIZE 24
-struct resume_key_req {
- char ResumeKey[COPY_CHUNK_RES_KEY_SIZE];
- __le32 ContextLength; /* MBZ */
- char Context[]; /* ignored, Windows sets to 4 bytes of zero */
-} __packed;
-
-/* this goes in the ioctl buffer when doing a copychunk request */
-struct copychunk_ioctl {
- char SourceKey[COPY_CHUNK_RES_KEY_SIZE];
- __le32 ChunkCount; /* we are only sending 1 */
- __le32 Reserved;
- /* array will only be one chunk long for us */
- __le64 SourceOffset;
- __le64 TargetOffset;
- __le32 Length; /* how many bytes to copy */
- __u32 Reserved2;
-} __packed;
-
-struct copychunk_ioctl_rsp {
- __le32 ChunksWritten;
- __le32 ChunkBytesWritten;
- __le32 TotalBytesWritten;
-} __packed;
-
/* See MS-FSCC 2.3.29 and 2.3.30 */
struct get_retrieval_pointer_count_req {
__le64 StartingVcn; /* virtual cluster number (signed) */
@@ -259,35 +193,6 @@ struct network_resiliency_req {
} __packed;
/* There is no buffer for the response ie no struct network_resiliency_rsp */
-#define RSS_CAPABLE cpu_to_le32(0x00000001)
-#define RDMA_CAPABLE cpu_to_le32(0x00000002)
-
-#define INTERNETWORK cpu_to_le16(0x0002)
-#define INTERNETWORKV6 cpu_to_le16(0x0017)
-
-struct network_interface_info_ioctl_rsp {
- __le32 Next; /* next interface. zero if this is last one */
- __le32 IfIndex;
- __le32 Capability; /* RSS or RDMA Capable */
- __le32 Reserved;
- __le64 LinkSpeed;
- __le16 Family;
- __u8 Buffer[126];
-} __packed;
-
-struct iface_info_ipv4 {
- __be16 Port;
- __be32 IPv4Address;
- __be64 Reserved;
-} __packed;
-
-struct iface_info_ipv6 {
- __be16 Port;
- __be32 FlowInfo;
- __u8 IPv6Address[16];
- __be32 ScopeId;
-} __packed;
-
#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */
struct compress_ioctl {
@@ -319,19 +224,6 @@ struct smb2_file_reparse_point_info {
__le32 Tag;
} __packed;
-struct smb2_file_network_open_info {
- struct_group_attr(network_open_info, __packed,
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 AllocationSize;
- __le64 EndOfFile;
- __le32 Attributes;
- );
- __le32 Reserved;
-} __packed; /* level 34 Query also similar returned in close rsp and open rsp */
-
/* See MS-FSCC 2.4.21 */
struct smb2_file_id_information {
__le64 VolumeSerialNumber;
diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
index 71504b30909e..063c9f83bbcd 100644
--- a/fs/smb/client/smb2proto.h
+++ b/fs/smb/client/smb2proto.h
@@ -9,8 +9,10 @@
*/
#ifndef _SMB2PROTO_H
#define _SMB2PROTO_H
+
#include <linux/nls.h>
#include <linux/key-type.h>
+#include "cached_dir.h"
struct statfs;
struct smb_rqst;
@@ -21,7 +23,7 @@ struct smb_rqst;
*****************************************************************
*/
extern int map_smb2_to_linux_error(char *buf, bool log_err);
-extern int smb2_check_message(char *buf, unsigned int length,
+extern int smb2_check_message(char *buf, unsigned int pdu_len, unsigned int length,
struct TCP_Server_Info *server);
extern unsigned int smb2_calc_size(void *buf);
extern char *smb2_get_data_area_len(int *off, int *len,
@@ -39,22 +41,12 @@ extern struct mid_q_entry *smb2_setup_async_request(
struct TCP_Server_Info *server, struct smb_rqst *rqst);
extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server,
__u64 ses_id, __u32 tid);
-extern int smb2_calc_signature(struct smb_rqst *rqst,
- struct TCP_Server_Info *server,
- bool allocate_crypto);
-extern int smb3_calc_signature(struct smb_rqst *rqst,
- struct TCP_Server_Info *server,
- bool allocate_crypto);
-extern void smb2_echo_request(struct work_struct *work);
extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode);
extern bool smb2_is_valid_oplock_break(char *buffer,
struct TCP_Server_Info *srv);
extern int smb3_handle_read_data(struct TCP_Server_Info *server,
struct mid_q_entry *mid);
-extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
- struct cifs_sb_info *cifs_sb, const char *path,
- __u32 *reparse_tag);
-struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
+struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data,
struct super_block *sb,
const unsigned int xid,
struct cifs_tcon *tcon,
@@ -111,7 +103,15 @@ extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const unsigned char *path, char *pbuf,
unsigned int *pbytes_read);
-int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path);
+int smb2_fix_symlink_target_type(char **target, bool directory, struct cifs_sb_info *cifs_sb);
+int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
+ bool relative,
+ const char *full_path,
+ struct cifs_sb_info *cifs_sb);
+int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb,
+ const struct kvec *iov,
+ const char *full_path,
+ char **path);
int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
void *buf);
extern int smb2_unlock_range(struct cifsFileInfo *cfile,
@@ -251,9 +251,6 @@ extern int smb2_handle_cancelled_close(struct cifs_tcon *tcon,
__u64 volatile_fid);
extern int smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server);
void smb2_cancelled_close_fid(struct work_struct *work);
-extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
- u64 persistent_file_id, u64 volatile_file_id,
- struct kstatfs *FSData);
extern int SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id,
struct kstatfs *FSData);
@@ -275,7 +272,7 @@ extern enum securityEnum smb2_select_sectype(struct TCP_Server_Info *,
enum securityEnum);
int smb2_parse_contexts(struct TCP_Server_Info *server,
struct kvec *rsp_iov,
- unsigned int *epoch,
+ __u16 *epoch,
char *lease_key, __u8 *oplock,
struct smb2_file_all_info *buf,
struct create_posix_rsp *posix);
@@ -290,10 +287,10 @@ extern int smb2_validate_and_copy_iov(unsigned int offset,
extern void smb2_copy_fs_info_to_kstatfs(
struct smb2_fs_full_size_info *pfs_inf,
struct kstatfs *kst);
-extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server);
-extern int smb311_update_preauth_hash(struct cifs_ses *ses,
- struct TCP_Server_Info *server,
- struct kvec *iov, int nvec);
+extern int smb3_crypto_shash_allocate(struct TCP_Server_Info *server);
+extern void smb311_update_preauth_hash(struct cifs_ses *ses,
+ struct TCP_Server_Info *server,
+ struct kvec *iov, int nvec);
extern int smb2_query_info_compound(const unsigned int xid,
struct cifs_tcon *tcon,
const char *path, u32 desired_access,
@@ -301,19 +298,11 @@ extern int smb2_query_info_compound(const unsigned int xid,
struct kvec *rsp, int *buftype,
struct cifs_sb_info *cifs_sb);
/* query path info from the server using SMB311 POSIX extensions*/
-int smb311_posix_query_path_info(const unsigned int xid,
- struct cifs_tcon *tcon,
- struct cifs_sb_info *cifs_sb,
- const char *full_path,
- struct cifs_open_info_data *data);
int posix_info_parse(const void *beg, const void *end,
struct smb2_posix_info_parsed *out);
int posix_info_sid_size(const void *beg, const void *end);
-int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
- struct dentry *dentry, struct cifs_tcon *tcon,
- const char *full_path, const char *symname);
-int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
- struct dentry *dentry, struct cifs_tcon *tcon,
- const char *full_path, umode_t mode, dev_t dev);
+int smb2_rename_pending_delete(const char *full_path,
+ struct dentry *dentry,
+ const unsigned int xid);
#endif /* _SMB2PROTO_H */
diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
index 475b36c27f65..8b9000a83181 100644
--- a/fs/smb/client/smb2transport.c
+++ b/fs/smb/client/smb2transport.c
@@ -19,6 +19,7 @@
#include <linux/mempool.h>
#include <linux/highmem.h>
#include <crypto/aead.h>
+#include <crypto/sha2.h>
#include "cifsglob.h"
#include "cifsproto.h"
#include "smb2proto.h"
@@ -26,53 +27,14 @@
#include "../common/smb2status.h"
#include "smb2glob.h"
-static int
+int
smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
{
struct cifs_secmech *p = &server->secmech;
- int rc;
-
- rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256);
- if (rc)
- goto err;
-
- rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac);
- if (rc)
- goto err;
- return 0;
-err:
- cifs_free_hash(&p->hmacsha256);
- return rc;
+ return cifs_alloc_hash("cmac(aes)", &p->aes_cmac);
}
-int
-smb311_crypto_shash_allocate(struct TCP_Server_Info *server)
-{
- struct cifs_secmech *p = &server->secmech;
- int rc = 0;
-
- rc = cifs_alloc_hash("hmac(sha256)", &p->hmacsha256);
- if (rc)
- return rc;
-
- rc = cifs_alloc_hash("cmac(aes)", &p->aes_cmac);
- if (rc)
- goto err;
-
- rc = cifs_alloc_hash("sha512", &p->sha512);
- if (rc)
- goto err;
-
- return 0;
-
-err:
- cifs_free_hash(&p->aes_cmac);
- cifs_free_hash(&p->hmacsha256);
- return rc;
-}
-
-
static
int smb3_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
{
@@ -191,7 +153,7 @@ static int smb2_get_sign_key(struct TCP_Server_Info *server,
memcpy(key, ses->auth_key.response,
SMB2_NTLMV2_SESSKEY_SIZE);
} else {
- rc = -EIO;
+ rc = smb_EIO(smb_eio_trace_no_auth_key);
}
break;
default:
@@ -240,11 +202,6 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid)
return NULL;
}
tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid);
- if (!tcon) {
- spin_unlock(&cifs_tcp_ses_lock);
- cifs_put_smb_ses(ses);
- return NULL;
- }
spin_unlock(&cifs_tcp_ses_lock);
/* tcon already has a ref to ses, so we don't need ses anymore */
cifs_put_smb_ses(ses);
@@ -252,16 +209,15 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid)
return tcon;
}
-int
+static int
smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
- bool allocate_crypto)
+ bool allocate_crypto)
{
int rc;
unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
- unsigned char *sigptr = smb2_signature;
struct kvec *iov = rqst->rq_iov;
struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base;
- struct shash_desc *shash = NULL;
+ struct hmac_sha256_ctx hmac_ctx;
struct smb_rqst drqst;
__u64 sid = le64_to_cpu(shdr->SessionId);
u8 key[SMB2_NTLMV2_SESSKEY_SIZE];
@@ -276,30 +232,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
- if (allocate_crypto) {
- rc = cifs_alloc_hash("hmac(sha256)", &shash);
- if (rc) {
- cifs_server_dbg(VFS,
- "%s: sha256 alloc failed\n", __func__);
- goto out;
- }
- } else {
- shash = server->secmech.hmacsha256;
- }
-
- rc = crypto_shash_setkey(shash->tfm, key, sizeof(key));
- if (rc) {
- cifs_server_dbg(VFS,
- "%s: Could not update with response\n",
- __func__);
- goto out;
- }
-
- rc = crypto_shash_init(shash);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not init sha256", __func__);
- goto out;
- }
+ hmac_sha256_init_usingrawkey(&hmac_ctx, key, sizeof(key));
/*
* For SMB2+, __cifs_calc_signature() expects to sign only the actual
@@ -310,25 +243,17 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
*/
drqst = *rqst;
if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) {
- rc = crypto_shash_update(shash, iov[0].iov_base,
- iov[0].iov_len);
- if (rc) {
- cifs_server_dbg(VFS,
- "%s: Could not update with payload\n",
- __func__);
- goto out;
- }
+ hmac_sha256_update(&hmac_ctx, iov[0].iov_base, iov[0].iov_len);
drqst.rq_iov++;
drqst.rq_nvec--;
}
- rc = __cifs_calc_signature(&drqst, server, sigptr, shash);
+ rc = __cifs_calc_signature(
+ &drqst, server, smb2_signature,
+ &(struct cifs_calc_sig_ctx){ .hmac = &hmac_ctx });
if (!rc)
- memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
+ memcpy(shdr->Signature, smb2_signature, SMB2_SIGNATURE_SIZE);
-out:
- if (allocate_crypto)
- cifs_free_hash(&shash);
return rc;
}
@@ -341,8 +266,8 @@ static int generate_key(struct cifs_ses *ses, struct kvec label,
__u8 L256[4] = {0, 0, 1, 0};
int rc = 0;
unsigned char prfhash[SMB2_HMACSHA256_SIZE];
- unsigned char *hashptr = prfhash;
struct TCP_Server_Info *server = ses->server;
+ struct hmac_sha256_ctx hmac_ctx;
memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
memset(key, 0x0, key_size);
@@ -350,67 +275,26 @@ static int generate_key(struct cifs_ses *ses, struct kvec label,
rc = smb3_crypto_shash_allocate(server);
if (rc) {
cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__);
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_setkey(server->secmech.hmacsha256->tfm,
- ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not set with session key\n", __func__);
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_init(server->secmech.hmacsha256);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not init sign hmac\n", __func__);
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_update(server->secmech.hmacsha256, i, 4);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not update with n\n", __func__);
- goto smb3signkey_ret;
+ return rc;
}
- rc = crypto_shash_update(server->secmech.hmacsha256, label.iov_base, label.iov_len);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not update with label\n", __func__);
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_update(server->secmech.hmacsha256, &zero, 1);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not update with zero\n", __func__);
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_update(server->secmech.hmacsha256, context.iov_base, context.iov_len);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not update with context\n", __func__);
- goto smb3signkey_ret;
- }
+ hmac_sha256_init_usingrawkey(&hmac_ctx, ses->auth_key.response,
+ SMB2_NTLMV2_SESSKEY_SIZE);
+ hmac_sha256_update(&hmac_ctx, i, 4);
+ hmac_sha256_update(&hmac_ctx, label.iov_base, label.iov_len);
+ hmac_sha256_update(&hmac_ctx, &zero, 1);
+ hmac_sha256_update(&hmac_ctx, context.iov_base, context.iov_len);
if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) ||
(server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) {
- rc = crypto_shash_update(server->secmech.hmacsha256, L256, 4);
+ hmac_sha256_update(&hmac_ctx, L256, 4);
} else {
- rc = crypto_shash_update(server->secmech.hmacsha256, L128, 4);
- }
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not update with L\n", __func__);
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_final(server->secmech.hmacsha256, hashptr);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__);
- goto smb3signkey_ret;
+ hmac_sha256_update(&hmac_ctx, L128, 4);
}
+ hmac_sha256_final(&hmac_ctx, prfhash);
- memcpy(key, hashptr, key_size);
-
-smb3signkey_ret:
- return rc;
+ memcpy(key, prfhash, key_size);
+ return 0;
}
struct derivation {
@@ -581,19 +465,21 @@ generate_smb311signingkey(struct cifs_ses *ses,
return generate_smb3signingkey(ses, server, &triplet);
}
-int
+static int
smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
- bool allocate_crypto)
+ bool allocate_crypto)
{
int rc;
unsigned char smb3_signature[SMB2_CMACAES_SIZE];
- unsigned char *sigptr = smb3_signature;
struct kvec *iov = rqst->rq_iov;
struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base;
struct shash_desc *shash = NULL;
struct smb_rqst drqst;
u8 key[SMB3_SIGN_KEY_SIZE];
+ if (server->vals->protocol_id <= SMB21_PROT_ID)
+ return smb2_calc_signature(rqst, server, allocate_crypto);
+
rc = smb3_get_sign_key(le64_to_cpu(shdr->SessionId), server, key);
if (unlikely(rc)) {
cifs_server_dbg(FYI, "%s: Could not get signing key\n", __func__);
@@ -648,9 +534,11 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
drqst.rq_nvec--;
}
- rc = __cifs_calc_signature(&drqst, server, sigptr, shash);
+ rc = __cifs_calc_signature(
+ &drqst, server, smb3_signature,
+ &(struct cifs_calc_sig_ctx){ .shash = shash });
if (!rc)
- memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE);
+ memcpy(shdr->Signature, smb3_signature, SMB2_SIGNATURE_SIZE);
out:
if (allocate_crypto)
@@ -662,7 +550,6 @@ out:
static int
smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{
- int rc = 0;
struct smb2_hdr *shdr;
struct smb2_sess_setup_req *ssr;
bool is_binding;
@@ -689,9 +576,7 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return 0;
}
- rc = server->ops->calc_signature(rqst, server, false);
-
- return rc;
+ return smb3_calc_signature(rqst, server, false);
}
int
@@ -727,7 +612,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE);
- rc = server->ops->calc_signature(rqst, server, true);
+ rc = smb3_calc_signature(rqst, server, true);
if (rc)
return rc;
@@ -768,15 +653,15 @@ smb2_mid_entry_alloc(const struct smb2_hdr *shdr,
return NULL;
}
- temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
+ temp = mempool_alloc(&cifs_mid_pool, GFP_NOFS);
memset(temp, 0, sizeof(struct mid_q_entry));
- kref_init(&temp->refcount);
+ refcount_set(&temp->refcount, 1);
+ spin_lock_init(&temp->mid_lock);
temp->mid = le64_to_cpu(shdr->MessageId);
temp->credits = credits > 0 ? credits : 1;
temp->pid = current->pid;
temp->command = shdr->Command; /* Always LE */
temp->when_alloc = jiffies;
- temp->server = server;
/*
* The default is for the mid to be synchronous, so the
@@ -799,50 +684,42 @@ static int
smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
struct smb2_hdr *shdr, struct mid_q_entry **mid)
{
- spin_lock(&server->srv_lock);
- if (server->tcpStatus == CifsExiting) {
- spin_unlock(&server->srv_lock);
+ switch (READ_ONCE(server->tcpStatus)) {
+ case CifsExiting:
return -ENOENT;
- }
-
- if (server->tcpStatus == CifsNeedReconnect) {
- spin_unlock(&server->srv_lock);
+ case CifsNeedReconnect:
cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
return -EAGAIN;
- }
-
- if (server->tcpStatus == CifsNeedNegotiate &&
- shdr->Command != SMB2_NEGOTIATE) {
- spin_unlock(&server->srv_lock);
- return -EAGAIN;
- }
- spin_unlock(&server->srv_lock);
-
- spin_lock(&ses->ses_lock);
- if (ses->ses_status == SES_NEW) {
- if ((shdr->Command != SMB2_SESSION_SETUP) &&
- (shdr->Command != SMB2_NEGOTIATE)) {
- spin_unlock(&ses->ses_lock);
+ case CifsNeedNegotiate:
+ if (shdr->Command != SMB2_NEGOTIATE)
return -EAGAIN;
- }
- /* else ok - we are setting up session */
+ break;
+ default:
+ break;
}
- if (ses->ses_status == SES_EXITING) {
- if (shdr->Command != SMB2_LOGOFF) {
- spin_unlock(&ses->ses_lock);
+ switch (READ_ONCE(ses->ses_status)) {
+ case SES_NEW:
+ if (shdr->Command != SMB2_SESSION_SETUP &&
+ shdr->Command != SMB2_NEGOTIATE)
+ return -EAGAIN;
+ /* else ok - we are setting up session */
+ break;
+ case SES_EXITING:
+ if (shdr->Command != SMB2_LOGOFF)
return -EAGAIN;
- }
/* else ok - we are shutting down the session */
+ break;
+ default:
+ break;
}
- spin_unlock(&ses->ses_lock);
*mid = smb2_mid_entry_alloc(shdr, server);
if (*mid == NULL)
return -ENOMEM;
- spin_lock(&server->mid_lock);
+ spin_lock(&server->mid_queue_lock);
list_add_tail(&(*mid)->qhead, &server->pending_mid_q);
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
return 0;
}
@@ -893,7 +770,7 @@ smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server,
rc = smb2_sign_rqst(rqst, server);
if (rc) {
revert_current_mid_from_hdr(server, shdr);
- delete_mid(mid);
+ delete_mid(server, mid);
return ERR_PTR(rc);
}
@@ -927,7 +804,7 @@ smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
rc = smb2_sign_rqst(rqst, server);
if (rc) {
revert_current_mid_from_hdr(server, shdr);
- release_mid(mid);
+ release_mid(server, mid);
return ERR_PTR(rc);
}
diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c
index b0b7254661e9..788a0670c4a8 100644
--- a/fs/smb/client/smbdirect.c
+++ b/fs/smb/client/smbdirect.c
@@ -7,37 +7,42 @@
#include <linux/module.h>
#include <linux/highmem.h>
#include <linux/folio_queue.h>
+#define __SMBDIRECT_SOCKET_DISCONNECT(__sc) smbd_disconnect_rdma_connection(__sc)
+#include "../common/smbdirect/smbdirect_pdu.h"
#include "smbdirect.h"
#include "cifs_debug.h"
#include "cifsproto.h"
#include "smb2proto.h"
-static struct smbd_response *get_empty_queue_buffer(
- struct smbd_connection *info);
-static struct smbd_response *get_receive_buffer(
- struct smbd_connection *info);
+const struct smbdirect_socket_parameters *smbd_get_parameters(struct smbd_connection *conn)
+{
+ struct smbdirect_socket *sc = &conn->socket;
+
+ return &sc->parameters;
+}
+
+static struct smbdirect_recv_io *get_receive_buffer(
+ struct smbdirect_socket *sc);
static void put_receive_buffer(
- struct smbd_connection *info,
- struct smbd_response *response);
-static int allocate_receive_buffers(struct smbd_connection *info, int num_buf);
-static void destroy_receive_buffers(struct smbd_connection *info);
+ struct smbdirect_socket *sc,
+ struct smbdirect_recv_io *response);
+static int allocate_receive_buffers(struct smbdirect_socket *sc, int num_buf);
+static void destroy_receive_buffers(struct smbdirect_socket *sc);
-static void put_empty_packet(
- struct smbd_connection *info, struct smbd_response *response);
static void enqueue_reassembly(
- struct smbd_connection *info,
- struct smbd_response *response, int data_length);
-static struct smbd_response *_get_first_reassembly(
- struct smbd_connection *info);
+ struct smbdirect_socket *sc,
+ struct smbdirect_recv_io *response, int data_length);
+static struct smbdirect_recv_io *_get_first_reassembly(
+ struct smbdirect_socket *sc);
static int smbd_post_recv(
- struct smbd_connection *info,
- struct smbd_response *response);
+ struct smbdirect_socket *sc,
+ struct smbdirect_recv_io *response);
-static int smbd_post_send_empty(struct smbd_connection *info);
+static int smbd_post_send_empty(struct smbdirect_socket *sc);
-static void destroy_mr_list(struct smbd_connection *info);
-static int allocate_mr_list(struct smbd_connection *info);
+static void destroy_mr_list(struct smbdirect_socket *sc);
+static int allocate_mr_list(struct smbdirect_socket *sc);
struct smb_extract_to_rdma {
struct ib_sge *sge;
@@ -50,9 +55,6 @@ struct smb_extract_to_rdma {
static ssize_t smb_extract_iter_to_rdma(struct iov_iter *iter, size_t len,
struct smb_extract_to_rdma *rdma);
-/* SMBD version number */
-#define SMBD_V1 0x0100
-
/* Port numbers for SMBD transport */
#define SMB_PORT 445
#define SMBD_PORT 5445
@@ -63,6 +65,9 @@ static ssize_t smb_extract_iter_to_rdma(struct iov_iter *iter, size_t len,
/* SMBD negotiation timeout in seconds */
#define SMBD_NEGOTIATE_TIMEOUT 120
+/* The timeout to wait for a keepalive message from peer in seconds */
+#define KEEPALIVE_RECV_TIMEOUT 5
+
/* SMBD minimum receive size and fragmented sized defined in [MS-SMBD] */
#define SMBD_MIN_RECEIVE_SIZE 128
#define SMBD_MIN_FRAGMENTED_SIZE 131072
@@ -161,78 +166,297 @@ do { \
#define log_rdma_mr(level, fmt, args...) \
log_rdma(level, LOG_RDMA_MR, fmt, ##args)
+static void smbd_disconnect_wake_up_all(struct smbdirect_socket *sc)
+{
+ /*
+ * Wake up all waiters in all wait queues
+ * in order to notice the broken connection.
+ */
+ wake_up_all(&sc->status_wait);
+ wake_up_all(&sc->send_io.lcredits.wait_queue);
+ wake_up_all(&sc->send_io.credits.wait_queue);
+ wake_up_all(&sc->send_io.pending.dec_wait_queue);
+ wake_up_all(&sc->send_io.pending.zero_wait_queue);
+ wake_up_all(&sc->recv_io.reassembly.wait_queue);
+ wake_up_all(&sc->mr_io.ready.wait_queue);
+ wake_up_all(&sc->mr_io.cleanup.wait_queue);
+}
+
static void smbd_disconnect_rdma_work(struct work_struct *work)
{
- struct smbd_connection *info =
- container_of(work, struct smbd_connection, disconnect_work);
+ struct smbdirect_socket *sc =
+ container_of(work, struct smbdirect_socket, disconnect_work);
+
+ if (sc->first_error == 0)
+ sc->first_error = -ECONNABORTED;
- if (info->transport_status == SMBD_CONNECTED) {
- info->transport_status = SMBD_DISCONNECTING;
- rdma_disconnect(info->id);
+ /*
+ * make sure this and other work is not queued again
+ * but here we don't block and avoid
+ * disable[_delayed]_work_sync()
+ */
+ disable_work(&sc->disconnect_work);
+ disable_work(&sc->recv_io.posted.refill_work);
+ disable_work(&sc->mr_io.recovery_work);
+ disable_work(&sc->idle.immediate_work);
+ disable_delayed_work(&sc->idle.timer_work);
+
+ switch (sc->status) {
+ case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED:
+ case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING:
+ case SMBDIRECT_SOCKET_NEGOTIATE_FAILED:
+ case SMBDIRECT_SOCKET_CONNECTED:
+ case SMBDIRECT_SOCKET_ERROR:
+ sc->status = SMBDIRECT_SOCKET_DISCONNECTING;
+ rdma_disconnect(sc->rdma.cm_id);
+ break;
+
+ case SMBDIRECT_SOCKET_CREATED:
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED:
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING:
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED:
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING:
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED:
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED:
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING:
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED:
+ /*
+ * rdma_connect() never reached
+ * RDMA_CM_EVENT_ESTABLISHED
+ */
+ sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
+ break;
+
+ case SMBDIRECT_SOCKET_DISCONNECTING:
+ case SMBDIRECT_SOCKET_DISCONNECTED:
+ case SMBDIRECT_SOCKET_DESTROYED:
+ break;
}
+
+ /*
+ * Wake up all waiters in all wait queues
+ * in order to notice the broken connection.
+ */
+ smbd_disconnect_wake_up_all(sc);
}
-static void smbd_disconnect_rdma_connection(struct smbd_connection *info)
+static void smbd_disconnect_rdma_connection(struct smbdirect_socket *sc)
{
- queue_work(info->workqueue, &info->disconnect_work);
+ if (sc->first_error == 0)
+ sc->first_error = -ECONNABORTED;
+
+ /*
+ * make sure other work (than disconnect_work) is
+ * not queued again but here we don't block and avoid
+ * disable[_delayed]_work_sync()
+ */
+ disable_work(&sc->recv_io.posted.refill_work);
+ disable_work(&sc->mr_io.recovery_work);
+ disable_work(&sc->idle.immediate_work);
+ disable_delayed_work(&sc->idle.timer_work);
+
+ switch (sc->status) {
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED:
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED:
+ case SMBDIRECT_SOCKET_NEGOTIATE_FAILED:
+ case SMBDIRECT_SOCKET_ERROR:
+ case SMBDIRECT_SOCKET_DISCONNECTING:
+ case SMBDIRECT_SOCKET_DISCONNECTED:
+ case SMBDIRECT_SOCKET_DESTROYED:
+ /*
+ * Keep the current error status
+ */
+ break;
+
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED:
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING:
+ sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED;
+ break;
+
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED:
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING:
+ sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED;
+ break;
+
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED:
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING:
+ sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED;
+ break;
+
+ case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED:
+ case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING:
+ sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
+ break;
+
+ case SMBDIRECT_SOCKET_CREATED:
+ sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
+ break;
+
+ case SMBDIRECT_SOCKET_CONNECTED:
+ sc->status = SMBDIRECT_SOCKET_ERROR;
+ break;
+ }
+
+ /*
+ * Wake up all waiters in all wait queues
+ * in order to notice the broken connection.
+ */
+ smbd_disconnect_wake_up_all(sc);
+
+ queue_work(sc->workqueue, &sc->disconnect_work);
}
/* Upcall from RDMA CM */
static int smbd_conn_upcall(
struct rdma_cm_id *id, struct rdma_cm_event *event)
{
- struct smbd_connection *info = id->context;
+ struct smbdirect_socket *sc = id->context;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ const char *event_name = rdma_event_msg(event->event);
+ u8 peer_initiator_depth;
+ u8 peer_responder_resources;
- log_rdma_event(INFO, "event=%d status=%d\n",
- event->event, event->status);
+ log_rdma_event(INFO, "event=%s status=%d\n",
+ event_name, event->status);
switch (event->event) {
case RDMA_CM_EVENT_ADDR_RESOLVED:
+ if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING))
+ break;
+ sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED;
+ wake_up(&sc->status_wait);
+ break;
+
case RDMA_CM_EVENT_ROUTE_RESOLVED:
- info->ri_rc = 0;
- complete(&info->ri_done);
+ if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING))
+ break;
+ sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED;
+ wake_up(&sc->status_wait);
break;
case RDMA_CM_EVENT_ADDR_ERROR:
- info->ri_rc = -EHOSTUNREACH;
- complete(&info->ri_done);
+ log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
+ sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED;
+ smbd_disconnect_rdma_work(&sc->disconnect_work);
break;
case RDMA_CM_EVENT_ROUTE_ERROR:
- info->ri_rc = -ENETUNREACH;
- complete(&info->ri_done);
+ log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
+ sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED;
+ smbd_disconnect_rdma_work(&sc->disconnect_work);
break;
case RDMA_CM_EVENT_ESTABLISHED:
- log_rdma_event(INFO, "connected event=%d\n", event->event);
- info->transport_status = SMBD_CONNECTED;
- wake_up_interruptible(&info->conn_wait);
+ log_rdma_event(INFO, "connected event=%s\n", event_name);
+
+ /*
+ * Here we work around an inconsistency between
+ * iWarp and other devices (at least rxe and irdma using RoCEv2)
+ */
+ if (rdma_protocol_iwarp(id->device, id->port_num)) {
+ /*
+ * iWarp devices report the peer's values
+ * with the perspective of the peer here.
+ * Tested with siw and irdma (in iwarp mode)
+ * We need to change to our perspective here,
+ * so we need to switch the values.
+ */
+ peer_initiator_depth = event->param.conn.responder_resources;
+ peer_responder_resources = event->param.conn.initiator_depth;
+ } else {
+ /*
+ * Non iWarp devices report the peer's values
+ * already changed to our perspective here.
+ * Tested with rxe and irdma (in roce mode).
+ */
+ peer_initiator_depth = event->param.conn.initiator_depth;
+ peer_responder_resources = event->param.conn.responder_resources;
+ }
+ if (rdma_protocol_iwarp(id->device, id->port_num) &&
+ event->param.conn.private_data_len == 8) {
+ /*
+ * Legacy clients with only iWarp MPA v1 support
+ * need a private blob in order to negotiate
+ * the IRD/ORD values.
+ */
+ const __be32 *ird_ord_hdr = event->param.conn.private_data;
+ u32 ird32 = be32_to_cpu(ird_ord_hdr[0]);
+ u32 ord32 = be32_to_cpu(ird_ord_hdr[1]);
+
+ /*
+ * cifs.ko sends the legacy IRD/ORD negotiation
+ * event if iWarp MPA v2 was used.
+ *
+ * Here we check that the values match and only
+ * mark the client as legacy if they don't match.
+ */
+ if ((u32)event->param.conn.initiator_depth != ird32 ||
+ (u32)event->param.conn.responder_resources != ord32) {
+ /*
+ * There are broken clients (old cifs.ko)
+ * using little endian and also
+ * struct rdma_conn_param only uses u8
+ * for initiator_depth and responder_resources,
+ * so we truncate the value to U8_MAX.
+ *
+ * smb_direct_accept_client() will then
+ * do the real negotiation in order to
+ * select the minimum between client and
+ * server.
+ */
+ ird32 = min_t(u32, ird32, U8_MAX);
+ ord32 = min_t(u32, ord32, U8_MAX);
+
+ sc->rdma.legacy_iwarp = true;
+ peer_initiator_depth = (u8)ird32;
+ peer_responder_resources = (u8)ord32;
+ }
+ }
+
+ /*
+ * negotiate the value by using the minimum
+ * between client and server if the client provided
+ * non 0 values.
+ */
+ if (peer_initiator_depth != 0)
+ sp->initiator_depth =
+ min_t(u8, sp->initiator_depth,
+ peer_initiator_depth);
+ if (peer_responder_resources != 0)
+ sp->responder_resources =
+ min_t(u8, sp->responder_resources,
+ peer_responder_resources);
+
+ if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING))
+ break;
+ sc->status = SMBDIRECT_SOCKET_NEGOTIATE_NEEDED;
+ wake_up(&sc->status_wait);
break;
case RDMA_CM_EVENT_CONNECT_ERROR:
case RDMA_CM_EVENT_UNREACHABLE:
case RDMA_CM_EVENT_REJECTED:
- log_rdma_event(INFO, "connecting failed event=%d\n", event->event);
- info->transport_status = SMBD_DISCONNECTED;
- wake_up_interruptible(&info->conn_wait);
+ log_rdma_event(ERR, "connecting failed event=%s\n", event_name);
+ sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED;
+ smbd_disconnect_rdma_work(&sc->disconnect_work);
break;
case RDMA_CM_EVENT_DEVICE_REMOVAL:
case RDMA_CM_EVENT_DISCONNECTED:
/* This happens when we fail the negotiation */
- if (info->transport_status == SMBD_NEGOTIATE_FAILED) {
- info->transport_status = SMBD_DISCONNECTED;
- wake_up(&info->conn_wait);
- break;
+ if (sc->status == SMBDIRECT_SOCKET_NEGOTIATE_FAILED) {
+ log_rdma_event(ERR, "event=%s during negotiation\n", event_name);
}
- info->transport_status = SMBD_DISCONNECTED;
- wake_up_interruptible(&info->disconn_wait);
- wake_up_interruptible(&info->wait_reassembly_queue);
- wake_up_interruptible_all(&info->wait_send_queue);
+ sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
+ smbd_disconnect_rdma_work(&sc->disconnect_work);
break;
default:
+ log_rdma_event(ERR, "unexpected event=%s status=%d\n",
+ event_name, event->status);
break;
}
@@ -243,15 +467,15 @@ static int smbd_conn_upcall(
static void
smbd_qp_async_error_upcall(struct ib_event *event, void *context)
{
- struct smbd_connection *info = context;
+ struct smbdirect_socket *sc = context;
- log_rdma_event(ERR, "%s on device %s info %p\n",
- ib_event_msg(event->event), event->device->name, info);
+ log_rdma_event(ERR, "%s on device %s socket %p\n",
+ ib_event_msg(event->event), event->device->name, sc);
switch (event->event) {
case IB_EVENT_CQ_ERR:
case IB_EVENT_QP_FATAL:
- smbd_disconnect_rdma_connection(info);
+ smbd_disconnect_rdma_connection(sc);
break;
default:
@@ -259,12 +483,12 @@ smbd_qp_async_error_upcall(struct ib_event *event, void *context)
}
}
-static inline void *smbd_request_payload(struct smbd_request *request)
+static inline void *smbdirect_send_io_payload(struct smbdirect_send_io *request)
{
return (void *)request->packet;
}
-static inline void *smbd_response_payload(struct smbd_response *response)
+static inline void *smbdirect_recv_io_payload(struct smbdirect_recv_io *response)
{
return (void *)response->packet;
}
@@ -273,33 +497,40 @@ static inline void *smbd_response_payload(struct smbd_response *response)
static void send_done(struct ib_cq *cq, struct ib_wc *wc)
{
int i;
- struct smbd_request *request =
- container_of(wc->wr_cqe, struct smbd_request, cqe);
+ struct smbdirect_send_io *request =
+ container_of(wc->wr_cqe, struct smbdirect_send_io, cqe);
+ struct smbdirect_socket *sc = request->socket;
+ int lcredits = 0;
- log_rdma_send(INFO, "smbd_request 0x%p completed wc->status=%d\n",
- request, wc->status);
-
- if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
- log_rdma_send(ERR, "wc->status=%d wc->opcode=%d\n",
- wc->status, wc->opcode);
- smbd_disconnect_rdma_connection(request->info);
- }
+ log_rdma_send(INFO, "smbdirect_send_io 0x%p completed wc->status=%s\n",
+ request, ib_wc_status_msg(wc->status));
for (i = 0; i < request->num_sge; i++)
- ib_dma_unmap_single(request->info->id->device,
+ ib_dma_unmap_single(sc->ib.dev,
request->sge[i].addr,
request->sge[i].length,
DMA_TO_DEVICE);
+ mempool_free(request, sc->send_io.mem.pool);
+ lcredits += 1;
- if (atomic_dec_and_test(&request->info->send_pending))
- wake_up(&request->info->wait_send_pending);
+ if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
+ if (wc->status != IB_WC_WR_FLUSH_ERR)
+ log_rdma_send(ERR, "wc->status=%s wc->opcode=%d\n",
+ ib_wc_status_msg(wc->status), wc->opcode);
+ smbd_disconnect_rdma_connection(sc);
+ return;
+ }
- wake_up(&request->info->wait_post_send);
+ atomic_add(lcredits, &sc->send_io.lcredits.count);
+ wake_up(&sc->send_io.lcredits.wait_queue);
- mempool_free(request, request->info->request_mempool);
+ if (atomic_dec_and_test(&sc->send_io.pending.count))
+ wake_up(&sc->send_io.pending.zero_wait_queue);
+
+ wake_up(&sc->send_io.pending.dec_wait_queue);
}
-static void dump_smbd_negotiate_resp(struct smbd_negotiate_resp *resp)
+static void dump_smbdirect_negotiate_resp(struct smbdirect_negotiate_resp *resp)
{
log_rdma_event(INFO, "resp message min_version %u max_version %u negotiated_version %u credits_requested %u credits_granted %u status %u max_readwrite_size %u preferred_send_size %u max_receive_size %u max_fragmented_size %u\n",
resp->min_version, resp->max_version,
@@ -315,52 +546,52 @@ static void dump_smbd_negotiate_resp(struct smbd_negotiate_resp *resp)
* return value: true if negotiation is a success, false if failed
*/
static bool process_negotiation_response(
- struct smbd_response *response, int packet_length)
+ struct smbdirect_recv_io *response, int packet_length)
{
- struct smbd_connection *info = response->info;
- struct smbd_negotiate_resp *packet = smbd_response_payload(response);
+ struct smbdirect_socket *sc = response->socket;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smbdirect_negotiate_resp *packet = smbdirect_recv_io_payload(response);
- if (packet_length < sizeof(struct smbd_negotiate_resp)) {
+ if (packet_length < sizeof(struct smbdirect_negotiate_resp)) {
log_rdma_event(ERR,
"error: packet_length=%d\n", packet_length);
return false;
}
- if (le16_to_cpu(packet->negotiated_version) != SMBD_V1) {
+ if (le16_to_cpu(packet->negotiated_version) != SMBDIRECT_V1) {
log_rdma_event(ERR, "error: negotiated_version=%x\n",
le16_to_cpu(packet->negotiated_version));
return false;
}
- info->protocol = le16_to_cpu(packet->negotiated_version);
if (packet->credits_requested == 0) {
log_rdma_event(ERR, "error: credits_requested==0\n");
return false;
}
- info->receive_credit_target = le16_to_cpu(packet->credits_requested);
+ sc->recv_io.credits.target = le16_to_cpu(packet->credits_requested);
+ sc->recv_io.credits.target = min_t(u16, sc->recv_io.credits.target, sp->recv_credit_max);
if (packet->credits_granted == 0) {
log_rdma_event(ERR, "error: credits_granted==0\n");
return false;
}
- atomic_set(&info->send_credits, le16_to_cpu(packet->credits_granted));
-
- atomic_set(&info->receive_credits, 0);
+ atomic_set(&sc->send_io.lcredits.count, sp->send_credit_target);
+ atomic_set(&sc->send_io.credits.count, le16_to_cpu(packet->credits_granted));
- if (le32_to_cpu(packet->preferred_send_size) > info->max_receive_size) {
+ if (le32_to_cpu(packet->preferred_send_size) > sp->max_recv_size) {
log_rdma_event(ERR, "error: preferred_send_size=%d\n",
le32_to_cpu(packet->preferred_send_size));
return false;
}
- info->max_receive_size = le32_to_cpu(packet->preferred_send_size);
+ sp->max_recv_size = le32_to_cpu(packet->preferred_send_size);
if (le32_to_cpu(packet->max_receive_size) < SMBD_MIN_RECEIVE_SIZE) {
log_rdma_event(ERR, "error: max_receive_size=%d\n",
le32_to_cpu(packet->max_receive_size));
return false;
}
- info->max_send_size = min_t(int, info->max_send_size,
- le32_to_cpu(packet->max_receive_size));
+ sp->max_send_size = min_t(u32, sp->max_send_size,
+ le32_to_cpu(packet->max_receive_size));
if (le32_to_cpu(packet->max_fragmented_size) <
SMBD_MIN_FRAGMENTED_SIZE) {
@@ -368,100 +599,82 @@ static bool process_negotiation_response(
le32_to_cpu(packet->max_fragmented_size));
return false;
}
- info->max_fragmented_send_size =
+ sp->max_fragmented_send_size =
le32_to_cpu(packet->max_fragmented_size);
- info->rdma_readwrite_threshold =
- rdma_readwrite_threshold > info->max_fragmented_send_size ?
- info->max_fragmented_send_size :
- rdma_readwrite_threshold;
- info->max_readwrite_size = min_t(u32,
+ sp->max_read_write_size = min_t(u32,
le32_to_cpu(packet->max_readwrite_size),
- info->max_frmr_depth * PAGE_SIZE);
- info->max_frmr_depth = info->max_readwrite_size / PAGE_SIZE;
+ sp->max_frmr_depth * PAGE_SIZE);
+ sp->max_frmr_depth = sp->max_read_write_size / PAGE_SIZE;
+ sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER;
return true;
}
static void smbd_post_send_credits(struct work_struct *work)
{
- int ret = 0;
- int use_receive_queue = 1;
int rc;
- struct smbd_response *response;
- struct smbd_connection *info =
- container_of(work, struct smbd_connection,
- post_send_credits_work);
+ struct smbdirect_recv_io *response;
+ struct smbdirect_socket *sc =
+ container_of(work, struct smbdirect_socket, recv_io.posted.refill_work);
- if (info->transport_status != SMBD_CONNECTED) {
- wake_up(&info->wait_receive_queues);
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
return;
}
- if (info->receive_credit_target >
- atomic_read(&info->receive_credits)) {
+ if (sc->recv_io.credits.target >
+ atomic_read(&sc->recv_io.credits.count)) {
while (true) {
- if (use_receive_queue)
- response = get_receive_buffer(info);
- else
- response = get_empty_queue_buffer(info);
- if (!response) {
- /* now switch to empty packet queue */
- if (use_receive_queue) {
- use_receive_queue = 0;
- continue;
- } else
- break;
- }
+ response = get_receive_buffer(sc);
+ if (!response)
+ break;
- response->type = SMBD_TRANSFER_DATA;
response->first_segment = false;
- rc = smbd_post_recv(info, response);
+ rc = smbd_post_recv(sc, response);
if (rc) {
log_rdma_recv(ERR,
"post_recv failed rc=%d\n", rc);
- put_receive_buffer(info, response);
+ put_receive_buffer(sc, response);
break;
}
- ret++;
+ atomic_inc(&sc->recv_io.posted.count);
}
}
- spin_lock(&info->lock_new_credits_offered);
- info->new_credits_offered += ret;
- spin_unlock(&info->lock_new_credits_offered);
-
/* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */
- info->send_immediate = true;
- if (atomic_read(&info->receive_credits) <
- info->receive_credit_target - 1) {
- if (info->keep_alive_requested == KEEP_ALIVE_PENDING ||
- info->send_immediate) {
- log_keep_alive(INFO, "send an empty message\n");
- smbd_post_send_empty(info);
- }
+ if (atomic_read(&sc->recv_io.credits.count) <
+ sc->recv_io.credits.target - 1) {
+ log_keep_alive(INFO, "schedule send of an empty message\n");
+ queue_work(sc->workqueue, &sc->idle.immediate_work);
}
}
/* Called from softirq, when recv is done */
static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
{
- struct smbd_data_transfer *data_transfer;
- struct smbd_response *response =
- container_of(wc->wr_cqe, struct smbd_response, cqe);
- struct smbd_connection *info = response->info;
- int data_length = 0;
-
- log_rdma_recv(INFO, "response=0x%p type=%d wc status=%d wc opcode %d byte_len=%d pkey_index=%u\n",
- response, response->type, wc->status, wc->opcode,
+ struct smbdirect_data_transfer *data_transfer;
+ struct smbdirect_recv_io *response =
+ container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
+ struct smbdirect_socket *sc = response->socket;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ u16 old_recv_credit_target;
+ u32 data_offset = 0;
+ u32 data_length = 0;
+ u32 remaining_data_length = 0;
+ bool negotiate_done = false;
+
+ log_rdma_recv(INFO,
+ "response=0x%p type=%d wc status=%s wc opcode %d byte_len=%d pkey_index=%u\n",
+ response, sc->recv_io.expected,
+ ib_wc_status_msg(wc->status), wc->opcode,
wc->byte_len, wc->pkey_index);
if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) {
- log_rdma_recv(INFO, "wc->status=%d opcode=%d\n",
- wc->status, wc->opcode);
- smbd_disconnect_rdma_connection(info);
+ if (wc->status != IB_WC_WR_FLUSH_ERR)
+ log_rdma_recv(ERR, "wc->status=%s opcode=%d\n",
+ ib_wc_status_msg(wc->status), wc->opcode);
goto error;
}
@@ -471,55 +684,81 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
response->sge.length,
DMA_FROM_DEVICE);
- switch (response->type) {
+ /*
+ * Reset timer to the keepalive interval in
+ * order to trigger our next keepalive message.
+ */
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_NONE;
+ mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->keepalive_interval_msec));
+
+ switch (sc->recv_io.expected) {
/* SMBD negotiation response */
- case SMBD_NEGOTIATE_RESP:
- dump_smbd_negotiate_resp(smbd_response_payload(response));
- info->full_packet_received = true;
- info->negotiate_done =
+ case SMBDIRECT_EXPECT_NEGOTIATE_REP:
+ dump_smbdirect_negotiate_resp(smbdirect_recv_io_payload(response));
+ sc->recv_io.reassembly.full_packet_received = true;
+ negotiate_done =
process_negotiation_response(response, wc->byte_len);
- complete(&info->negotiate_completion);
- break;
+ put_receive_buffer(sc, response);
+ if (SMBDIRECT_CHECK_STATUS_WARN(sc, SMBDIRECT_SOCKET_NEGOTIATE_RUNNING))
+ negotiate_done = false;
+ if (!negotiate_done) {
+ sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
+ smbd_disconnect_rdma_connection(sc);
+ } else {
+ sc->status = SMBDIRECT_SOCKET_CONNECTED;
+ wake_up(&sc->status_wait);
+ }
+
+ return;
/* SMBD data transfer packet */
- case SMBD_TRANSFER_DATA:
- data_transfer = smbd_response_payload(response);
+ case SMBDIRECT_EXPECT_DATA_TRANSFER:
+ data_transfer = smbdirect_recv_io_payload(response);
+
+ if (wc->byte_len <
+ offsetof(struct smbdirect_data_transfer, padding))
+ goto error;
+
+ remaining_data_length = le32_to_cpu(data_transfer->remaining_data_length);
+ data_offset = le32_to_cpu(data_transfer->data_offset);
data_length = le32_to_cpu(data_transfer->data_length);
+ if (wc->byte_len < data_offset ||
+ (u64)wc->byte_len < (u64)data_offset + data_length)
+ goto error;
+
+ if (remaining_data_length > sp->max_fragmented_recv_size ||
+ data_length > sp->max_fragmented_recv_size ||
+ (u64)remaining_data_length + (u64)data_length > (u64)sp->max_fragmented_recv_size)
+ goto error;
- /*
- * If this is a packet with data playload place the data in
- * reassembly queue and wake up the reading thread
- */
if (data_length) {
- if (info->full_packet_received)
+ if (sc->recv_io.reassembly.full_packet_received)
response->first_segment = true;
if (le32_to_cpu(data_transfer->remaining_data_length))
- info->full_packet_received = false;
+ sc->recv_io.reassembly.full_packet_received = false;
else
- info->full_packet_received = true;
-
- enqueue_reassembly(
- info,
- response,
- data_length);
- } else
- put_empty_packet(info, response);
-
- if (data_length)
- wake_up_interruptible(&info->wait_reassembly_queue);
+ sc->recv_io.reassembly.full_packet_received = true;
+ }
- atomic_dec(&info->receive_credits);
- info->receive_credit_target =
+ atomic_dec(&sc->recv_io.posted.count);
+ atomic_dec(&sc->recv_io.credits.count);
+ old_recv_credit_target = sc->recv_io.credits.target;
+ sc->recv_io.credits.target =
le16_to_cpu(data_transfer->credits_requested);
+ sc->recv_io.credits.target =
+ min_t(u16, sc->recv_io.credits.target, sp->recv_credit_max);
+ sc->recv_io.credits.target =
+ max_t(u16, sc->recv_io.credits.target, 1);
if (le16_to_cpu(data_transfer->credits_granted)) {
atomic_add(le16_to_cpu(data_transfer->credits_granted),
- &info->send_credits);
+ &sc->send_io.credits.count);
/*
* We have new send credits granted from remote peer
* If any sender is waiting for credits, unblock it
*/
- wake_up_interruptible(&info->wait_send_queue);
+ wake_up(&sc->send_io.credits.wait_queue);
}
log_incoming(INFO, "data flags %d data_offset %d data_length %d remaining_data_length %d\n",
@@ -528,33 +767,53 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
le32_to_cpu(data_transfer->data_length),
le32_to_cpu(data_transfer->remaining_data_length));
- /* Send a KEEP_ALIVE response right away if requested */
- info->keep_alive_requested = KEEP_ALIVE_NONE;
+ /* Send an immediate response right away if requested */
if (le16_to_cpu(data_transfer->flags) &
- SMB_DIRECT_RESPONSE_REQUESTED) {
- info->keep_alive_requested = KEEP_ALIVE_PENDING;
+ SMBDIRECT_FLAG_RESPONSE_REQUESTED) {
+ log_keep_alive(INFO, "schedule send of immediate response\n");
+ queue_work(sc->workqueue, &sc->idle.immediate_work);
}
+ /*
+ * If this is a packet with data playload place the data in
+ * reassembly queue and wake up the reading thread
+ */
+ if (data_length) {
+ if (sc->recv_io.credits.target > old_recv_credit_target)
+ queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
+
+ enqueue_reassembly(sc, response, data_length);
+ wake_up(&sc->recv_io.reassembly.wait_queue);
+ } else
+ put_receive_buffer(sc, response);
+
return;
- default:
- log_rdma_recv(ERR,
- "unexpected response type=%d\n", response->type);
+ case SMBDIRECT_EXPECT_NEGOTIATE_REQ:
+ /* Only server... */
+ break;
}
+ /*
+ * This is an internal error!
+ */
+ log_rdma_recv(ERR, "unexpected response type=%d\n", sc->recv_io.expected);
+ WARN_ON_ONCE(sc->recv_io.expected != SMBDIRECT_EXPECT_DATA_TRANSFER);
error:
- put_receive_buffer(info, response);
+ put_receive_buffer(sc, response);
+ smbd_disconnect_rdma_connection(sc);
}
static struct rdma_cm_id *smbd_create_id(
- struct smbd_connection *info,
+ struct smbdirect_socket *sc,
struct sockaddr *dstaddr, int port)
{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
struct rdma_cm_id *id;
int rc;
__be16 *sport;
- id = rdma_create_id(&init_net, smbd_conn_upcall, info,
+ id = rdma_create_id(&init_net, smbd_conn_upcall, sc,
RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(id)) {
rc = PTR_ERR(id);
@@ -569,43 +828,57 @@ static struct rdma_cm_id *smbd_create_id(
*sport = htons(port);
- init_completion(&info->ri_done);
- info->ri_rc = -ETIMEDOUT;
-
+ WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED);
+ sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING;
rc = rdma_resolve_addr(id, NULL, (struct sockaddr *)dstaddr,
- RDMA_RESOLVE_TIMEOUT);
+ sp->resolve_addr_timeout_msec);
if (rc) {
log_rdma_event(ERR, "rdma_resolve_addr() failed %i\n", rc);
goto out;
}
- rc = wait_for_completion_interruptible_timeout(
- &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT));
+ rc = wait_event_interruptible_timeout(
+ sc->status_wait,
+ sc->status != SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING,
+ msecs_to_jiffies(sp->resolve_addr_timeout_msec));
/* e.g. if interrupted returns -ERESTARTSYS */
if (rc < 0) {
log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc);
goto out;
}
- rc = info->ri_rc;
- if (rc) {
+ if (sc->status == SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING) {
+ rc = -ETIMEDOUT;
+ log_rdma_event(ERR, "rdma_resolve_addr() completed %i\n", rc);
+ goto out;
+ }
+ if (sc->status != SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED) {
+ rc = -EHOSTUNREACH;
log_rdma_event(ERR, "rdma_resolve_addr() completed %i\n", rc);
goto out;
}
- info->ri_rc = -ETIMEDOUT;
- rc = rdma_resolve_route(id, RDMA_RESOLVE_TIMEOUT);
+ WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED);
+ sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING;
+ rc = rdma_resolve_route(id, sp->resolve_route_timeout_msec);
if (rc) {
log_rdma_event(ERR, "rdma_resolve_route() failed %i\n", rc);
goto out;
}
- rc = wait_for_completion_interruptible_timeout(
- &info->ri_done, msecs_to_jiffies(RDMA_RESOLVE_TIMEOUT));
+ rc = wait_event_interruptible_timeout(
+ sc->status_wait,
+ sc->status != SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING,
+ msecs_to_jiffies(sp->resolve_route_timeout_msec));
/* e.g. if interrupted returns -ERESTARTSYS */
if (rc < 0) {
log_rdma_event(ERR, "rdma_resolve_addr timeout rc: %i\n", rc);
goto out;
}
- rc = info->ri_rc;
- if (rc) {
+ if (sc->status == SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING) {
+ rc = -ETIMEDOUT;
+ log_rdma_event(ERR, "rdma_resolve_route() completed %i\n", rc);
+ goto out;
+ }
+ if (sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED) {
+ rc = -ENETUNREACH;
log_rdma_event(ERR, "rdma_resolve_route() completed %i\n", rc);
goto out;
}
@@ -632,44 +905,42 @@ static bool frwr_is_supported(struct ib_device_attr *attrs)
}
static int smbd_ia_open(
- struct smbd_connection *info,
+ struct smbdirect_socket *sc,
struct sockaddr *dstaddr, int port)
{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
int rc;
- info->id = smbd_create_id(info, dstaddr, port);
- if (IS_ERR(info->id)) {
- rc = PTR_ERR(info->id);
+ WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_CREATED);
+ sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED;
+
+ sc->rdma.cm_id = smbd_create_id(sc, dstaddr, port);
+ if (IS_ERR(sc->rdma.cm_id)) {
+ rc = PTR_ERR(sc->rdma.cm_id);
goto out1;
}
+ sc->ib.dev = sc->rdma.cm_id->device;
- if (!frwr_is_supported(&info->id->device->attrs)) {
+ if (!frwr_is_supported(&sc->ib.dev->attrs)) {
log_rdma_event(ERR, "Fast Registration Work Requests (FRWR) is not supported\n");
log_rdma_event(ERR, "Device capability flags = %llx max_fast_reg_page_list_len = %u\n",
- info->id->device->attrs.device_cap_flags,
- info->id->device->attrs.max_fast_reg_page_list_len);
+ sc->ib.dev->attrs.device_cap_flags,
+ sc->ib.dev->attrs.max_fast_reg_page_list_len);
rc = -EPROTONOSUPPORT;
goto out2;
}
- info->max_frmr_depth = min_t(int,
- smbd_max_frmr_depth,
- info->id->device->attrs.max_fast_reg_page_list_len);
- info->mr_type = IB_MR_TYPE_MEM_REG;
- if (info->id->device->attrs.kernel_cap_flags & IBK_SG_GAPS_REG)
- info->mr_type = IB_MR_TYPE_SG_GAPS;
-
- info->pd = ib_alloc_pd(info->id->device, 0);
- if (IS_ERR(info->pd)) {
- rc = PTR_ERR(info->pd);
- log_rdma_event(ERR, "ib_alloc_pd() returned %d\n", rc);
- goto out2;
- }
+ sp->max_frmr_depth = min_t(u32,
+ sp->max_frmr_depth,
+ sc->ib.dev->attrs.max_fast_reg_page_list_len);
+ sc->mr_io.type = IB_MR_TYPE_MEM_REG;
+ if (sc->ib.dev->attrs.kernel_cap_flags & IBK_SG_GAPS_REG)
+ sc->mr_io.type = IB_MR_TYPE_SG_GAPS;
return 0;
out2:
- rdma_destroy_id(info->id);
- info->id = NULL;
+ rdma_destroy_id(sc->rdma.cm_id);
+ sc->rdma.cm_id = NULL;
out1:
return rc;
@@ -681,43 +952,44 @@ out1:
* After negotiation, the transport is connected and ready for
* carrying upper layer SMB payload
*/
-static int smbd_post_send_negotiate_req(struct smbd_connection *info)
+static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc)
{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
struct ib_send_wr send_wr;
int rc = -ENOMEM;
- struct smbd_request *request;
- struct smbd_negotiate_req *packet;
+ struct smbdirect_send_io *request;
+ struct smbdirect_negotiate_req *packet;
- request = mempool_alloc(info->request_mempool, GFP_KERNEL);
+ request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
if (!request)
return rc;
- request->info = info;
+ request->socket = sc;
- packet = smbd_request_payload(request);
- packet->min_version = cpu_to_le16(SMBD_V1);
- packet->max_version = cpu_to_le16(SMBD_V1);
+ packet = smbdirect_send_io_payload(request);
+ packet->min_version = cpu_to_le16(SMBDIRECT_V1);
+ packet->max_version = cpu_to_le16(SMBDIRECT_V1);
packet->reserved = 0;
- packet->credits_requested = cpu_to_le16(info->send_credit_target);
- packet->preferred_send_size = cpu_to_le32(info->max_send_size);
- packet->max_receive_size = cpu_to_le32(info->max_receive_size);
+ packet->credits_requested = cpu_to_le16(sp->send_credit_target);
+ packet->preferred_send_size = cpu_to_le32(sp->max_send_size);
+ packet->max_receive_size = cpu_to_le32(sp->max_recv_size);
packet->max_fragmented_size =
- cpu_to_le32(info->max_fragmented_recv_size);
+ cpu_to_le32(sp->max_fragmented_recv_size);
request->num_sge = 1;
request->sge[0].addr = ib_dma_map_single(
- info->id->device, (void *)packet,
+ sc->ib.dev, (void *)packet,
sizeof(*packet), DMA_TO_DEVICE);
- if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) {
+ if (ib_dma_mapping_error(sc->ib.dev, request->sge[0].addr)) {
rc = -EIO;
goto dma_mapping_failed;
}
request->sge[0].length = sizeof(*packet);
- request->sge[0].lkey = info->pd->local_dma_lkey;
+ request->sge[0].lkey = sc->ib.pd->local_dma_lkey;
ib_dma_sync_single_for_device(
- info->id->device, request->sge[0].addr,
+ sc->ib.dev, request->sge[0].addr,
request->sge[0].length, DMA_TO_DEVICE);
request->cqe.done = send_done;
@@ -733,21 +1005,21 @@ static int smbd_post_send_negotiate_req(struct smbd_connection *info)
request->sge[0].addr,
request->sge[0].length, request->sge[0].lkey);
- atomic_inc(&info->send_pending);
- rc = ib_post_send(info->id->qp, &send_wr, NULL);
+ atomic_inc(&sc->send_io.pending.count);
+ rc = ib_post_send(sc->ib.qp, &send_wr, NULL);
if (!rc)
return 0;
/* if we reach here, post send failed */
log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
- atomic_dec(&info->send_pending);
- ib_dma_unmap_single(info->id->device, request->sge[0].addr,
+ atomic_dec(&sc->send_io.pending.count);
+ ib_dma_unmap_single(sc->ib.dev, request->sge[0].addr,
request->sge[0].length, DMA_TO_DEVICE);
- smbd_disconnect_rdma_connection(info);
+ smbd_disconnect_rdma_connection(sc);
dma_mapping_failed:
- mempool_free(request, info->request_mempool);
+ mempool_free(request, sc->send_io.mem.pool);
return rc;
}
@@ -759,14 +1031,20 @@ dma_mapping_failed:
* buffer as possible, and extend the receive credits to remote peer
* return value: the new credtis being granted.
*/
-static int manage_credits_prior_sending(struct smbd_connection *info)
+static int manage_credits_prior_sending(struct smbdirect_socket *sc)
{
int new_credits;
- spin_lock(&info->lock_new_credits_offered);
- new_credits = info->new_credits_offered;
- info->new_credits_offered = 0;
- spin_unlock(&info->lock_new_credits_offered);
+ if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target)
+ return 0;
+
+ new_credits = atomic_read(&sc->recv_io.posted.count);
+ if (new_credits == 0)
+ return 0;
+
+ new_credits -= atomic_read(&sc->recv_io.credits.count);
+ if (new_credits <= 0)
+ return 0;
return new_credits;
}
@@ -774,24 +1052,32 @@ static int manage_credits_prior_sending(struct smbd_connection *info)
/*
* Check if we need to send a KEEP_ALIVE message
* The idle connection timer triggers a KEEP_ALIVE message when expires
- * SMB_DIRECT_RESPONSE_REQUESTED is set in the message flag to have peer send
+ * SMBDIRECT_FLAG_RESPONSE_REQUESTED is set in the message flag to have peer send
* back a response.
* return value:
- * 1 if SMB_DIRECT_RESPONSE_REQUESTED needs to be set
+ * 1 if SMBDIRECT_FLAG_RESPONSE_REQUESTED needs to be set
* 0: otherwise
*/
-static int manage_keep_alive_before_sending(struct smbd_connection *info)
+static int manage_keep_alive_before_sending(struct smbdirect_socket *sc)
{
- if (info->keep_alive_requested == KEEP_ALIVE_PENDING) {
- info->keep_alive_requested = KEEP_ALIVE_SENT;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+
+ if (sc->idle.keepalive == SMBDIRECT_KEEPALIVE_PENDING) {
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_SENT;
+ /*
+ * Now use the keepalive timeout (instead of keepalive interval)
+ * in order to wait for a response
+ */
+ mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->keepalive_timeout_msec));
return 1;
}
return 0;
}
/* Post the send request */
-static int smbd_post_send(struct smbd_connection *info,
- struct smbd_request *request)
+static int smbd_post_send(struct smbdirect_socket *sc,
+ struct smbdirect_send_io *request)
{
struct ib_send_wr send_wr;
int rc, i;
@@ -801,7 +1087,7 @@ static int smbd_post_send(struct smbd_connection *info,
"rdma_request sge[%d] addr=0x%llx length=%u\n",
i, request->sge[i].addr, request->sge[i].length);
ib_dma_sync_single_for_device(
- info->id->device,
+ sc->ib.dev,
request->sge[i].addr,
request->sge[i].length,
DMA_TO_DEVICE);
@@ -816,86 +1102,107 @@ static int smbd_post_send(struct smbd_connection *info,
send_wr.opcode = IB_WR_SEND;
send_wr.send_flags = IB_SEND_SIGNALED;
- rc = ib_post_send(info->id->qp, &send_wr, NULL);
+ rc = ib_post_send(sc->ib.qp, &send_wr, NULL);
if (rc) {
log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
- smbd_disconnect_rdma_connection(info);
+ smbd_disconnect_rdma_connection(sc);
rc = -EAGAIN;
- } else
- /* Reset timer for idle connection after packet is sent */
- mod_delayed_work(info->workqueue, &info->idle_timer_work,
- info->keep_alive_interval*HZ);
+ }
return rc;
}
-static int smbd_post_send_iter(struct smbd_connection *info,
+static int smbd_post_send_iter(struct smbdirect_socket *sc,
struct iov_iter *iter,
int *_remaining_data_length)
{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
int i, rc;
int header_length;
int data_length;
- struct smbd_request *request;
- struct smbd_data_transfer *packet;
+ struct smbdirect_send_io *request;
+ struct smbdirect_data_transfer *packet;
int new_credits = 0;
-wait_credit:
- /* Wait for send credits. A SMBD packet needs one credit */
- rc = wait_event_interruptible(info->wait_send_queue,
- atomic_read(&info->send_credits) > 0 ||
- info->transport_status != SMBD_CONNECTED);
+wait_lcredit:
+ /* Wait for local send credits */
+ rc = wait_event_interruptible(sc->send_io.lcredits.wait_queue,
+ atomic_read(&sc->send_io.lcredits.count) > 0 ||
+ sc->status != SMBDIRECT_SOCKET_CONNECTED);
if (rc)
- goto err_wait_credit;
+ goto err_wait_lcredit;
- if (info->transport_status != SMBD_CONNECTED) {
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
log_outgoing(ERR, "disconnected not sending on wait_credit\n");
rc = -EAGAIN;
- goto err_wait_credit;
+ goto err_wait_lcredit;
}
- if (unlikely(atomic_dec_return(&info->send_credits) < 0)) {
- atomic_inc(&info->send_credits);
- goto wait_credit;
+ if (unlikely(atomic_dec_return(&sc->send_io.lcredits.count) < 0)) {
+ atomic_inc(&sc->send_io.lcredits.count);
+ goto wait_lcredit;
}
-wait_send_queue:
- wait_event(info->wait_post_send,
- atomic_read(&info->send_pending) < info->send_credit_target ||
- info->transport_status != SMBD_CONNECTED);
+wait_credit:
+ /* Wait for send credits. A SMBD packet needs one credit */
+ rc = wait_event_interruptible(sc->send_io.credits.wait_queue,
+ atomic_read(&sc->send_io.credits.count) > 0 ||
+ sc->status != SMBDIRECT_SOCKET_CONNECTED);
+ if (rc)
+ goto err_wait_credit;
- if (info->transport_status != SMBD_CONNECTED) {
- log_outgoing(ERR, "disconnected not sending on wait_send_queue\n");
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
+ log_outgoing(ERR, "disconnected not sending on wait_credit\n");
rc = -EAGAIN;
- goto err_wait_send_queue;
+ goto err_wait_credit;
}
-
- if (unlikely(atomic_inc_return(&info->send_pending) >
- info->send_credit_target)) {
- atomic_dec(&info->send_pending);
- goto wait_send_queue;
+ if (unlikely(atomic_dec_return(&sc->send_io.credits.count) < 0)) {
+ atomic_inc(&sc->send_io.credits.count);
+ goto wait_credit;
}
- request = mempool_alloc(info->request_mempool, GFP_KERNEL);
+ request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
if (!request) {
rc = -ENOMEM;
goto err_alloc;
}
- request->info = info;
+ request->socket = sc;
memset(request->sge, 0, sizeof(request->sge));
+ /* Map the packet to DMA */
+ header_length = sizeof(struct smbdirect_data_transfer);
+ /* If this is a packet without payload, don't send padding */
+ if (!iter)
+ header_length = offsetof(struct smbdirect_data_transfer, padding);
+
+ packet = smbdirect_send_io_payload(request);
+ request->sge[0].addr = ib_dma_map_single(sc->ib.dev,
+ (void *)packet,
+ header_length,
+ DMA_TO_DEVICE);
+ if (ib_dma_mapping_error(sc->ib.dev, request->sge[0].addr)) {
+ rc = -EIO;
+ goto err_dma;
+ }
+
+ request->sge[0].length = header_length;
+ request->sge[0].lkey = sc->ib.pd->local_dma_lkey;
+ request->num_sge = 1;
+
/* Fill in the data payload to find out how much data we can add */
if (iter) {
struct smb_extract_to_rdma extract = {
- .nr_sge = 1,
- .max_sge = SMBDIRECT_MAX_SEND_SGE,
+ .nr_sge = request->num_sge,
+ .max_sge = SMBDIRECT_SEND_IO_MAX_SGE,
.sge = request->sge,
- .device = info->id->device,
- .local_dma_lkey = info->pd->local_dma_lkey,
+ .device = sc->ib.dev,
+ .local_dma_lkey = sc->ib.pd->local_dma_lkey,
.direction = DMA_TO_DEVICE,
};
+ size_t payload_len = umin(*_remaining_data_length,
+ sp->max_send_size - sizeof(*packet));
- rc = smb_extract_iter_to_rdma(iter, *_remaining_data_length,
+ rc = smb_extract_iter_to_rdma(iter, payload_len,
&extract);
if (rc < 0)
goto err_dma;
@@ -904,22 +1211,18 @@ wait_send_queue:
*_remaining_data_length -= data_length;
} else {
data_length = 0;
- request->num_sge = 1;
}
/* Fill in the packet header */
- packet = smbd_request_payload(request);
- packet->credits_requested = cpu_to_le16(info->send_credit_target);
+ packet->credits_requested = cpu_to_le16(sp->send_credit_target);
- new_credits = manage_credits_prior_sending(info);
- atomic_add(new_credits, &info->receive_credits);
+ new_credits = manage_credits_prior_sending(sc);
+ atomic_add(new_credits, &sc->recv_io.credits.count);
packet->credits_granted = cpu_to_le16(new_credits);
- info->send_immediate = false;
-
packet->flags = 0;
- if (manage_keep_alive_before_sending(info))
- packet->flags |= cpu_to_le16(SMB_DIRECT_RESPONSE_REQUESTED);
+ if (manage_keep_alive_before_sending(sc))
+ packet->flags |= cpu_to_le16(SMBDIRECT_FLAG_RESPONSE_REQUESTED);
packet->reserved = 0;
if (!data_length)
@@ -937,53 +1240,42 @@ wait_send_queue:
le32_to_cpu(packet->data_length),
le32_to_cpu(packet->remaining_data_length));
- /* Map the packet to DMA */
- header_length = sizeof(struct smbd_data_transfer);
- /* If this is a packet without payload, don't send padding */
- if (!data_length)
- header_length = offsetof(struct smbd_data_transfer, padding);
-
- request->sge[0].addr = ib_dma_map_single(info->id->device,
- (void *)packet,
- header_length,
- DMA_TO_DEVICE);
- if (ib_dma_mapping_error(info->id->device, request->sge[0].addr)) {
- rc = -EIO;
- request->sge[0].addr = 0;
- goto err_dma;
- }
-
- request->sge[0].length = header_length;
- request->sge[0].lkey = info->pd->local_dma_lkey;
+ /*
+ * Now that we got a local and a remote credit
+ * we add us as pending
+ */
+ atomic_inc(&sc->send_io.pending.count);
- rc = smbd_post_send(info, request);
+ rc = smbd_post_send(sc, request);
if (!rc)
return 0;
+ if (atomic_dec_and_test(&sc->send_io.pending.count))
+ wake_up(&sc->send_io.pending.zero_wait_queue);
+
+ wake_up(&sc->send_io.pending.dec_wait_queue);
+
err_dma:
for (i = 0; i < request->num_sge; i++)
if (request->sge[i].addr)
- ib_dma_unmap_single(info->id->device,
+ ib_dma_unmap_single(sc->ib.dev,
request->sge[i].addr,
request->sge[i].length,
DMA_TO_DEVICE);
- mempool_free(request, info->request_mempool);
+ mempool_free(request, sc->send_io.mem.pool);
- /* roll back receive credits and credits to be offered */
- spin_lock(&info->lock_new_credits_offered);
- info->new_credits_offered += new_credits;
- spin_unlock(&info->lock_new_credits_offered);
- atomic_sub(new_credits, &info->receive_credits);
+ /* roll back the granted receive credits */
+ atomic_sub(new_credits, &sc->recv_io.credits.count);
err_alloc:
- if (atomic_dec_and_test(&info->send_pending))
- wake_up(&info->wait_send_pending);
-
-err_wait_send_queue:
- /* roll back send credits and pending */
- atomic_inc(&info->send_credits);
+ atomic_inc(&sc->send_io.credits.count);
+ wake_up(&sc->send_io.credits.wait_queue);
err_wait_credit:
+ atomic_inc(&sc->send_io.lcredits.count);
+ wake_up(&sc->send_io.lcredits.wait_queue);
+
+err_wait_lcredit:
return rc;
}
@@ -992,12 +1284,33 @@ err_wait_credit:
* Empty message is used to extend credits to peer to for keep live
* while there is no upper layer payload to send at the time
*/
-static int smbd_post_send_empty(struct smbd_connection *info)
+static int smbd_post_send_empty(struct smbdirect_socket *sc)
{
int remaining_data_length = 0;
- info->count_send_empty++;
- return smbd_post_send_iter(info, NULL, &remaining_data_length);
+ sc->statistics.send_empty++;
+ return smbd_post_send_iter(sc, NULL, &remaining_data_length);
+}
+
+static int smbd_post_send_full_iter(struct smbdirect_socket *sc,
+ struct iov_iter *iter,
+ int *_remaining_data_length)
+{
+ int rc = 0;
+
+ /*
+ * smbd_post_send_iter() respects the
+ * negotiated max_send_size, so we need to
+ * loop until the full iter is posted
+ */
+
+ while (iov_iter_count(iter) > 0) {
+ rc = smbd_post_send_iter(sc, iter, _remaining_data_length);
+ if (rc < 0)
+ break;
+ }
+
+ return rc;
}
/*
@@ -1006,19 +1319,20 @@ static int smbd_post_send_empty(struct smbd_connection *info)
* The interaction is controlled by send/receive credit system
*/
static int smbd_post_recv(
- struct smbd_connection *info, struct smbd_response *response)
+ struct smbdirect_socket *sc, struct smbdirect_recv_io *response)
{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
struct ib_recv_wr recv_wr;
int rc = -EIO;
response->sge.addr = ib_dma_map_single(
- info->id->device, response->packet,
- info->max_receive_size, DMA_FROM_DEVICE);
- if (ib_dma_mapping_error(info->id->device, response->sge.addr))
+ sc->ib.dev, response->packet,
+ sp->max_recv_size, DMA_FROM_DEVICE);
+ if (ib_dma_mapping_error(sc->ib.dev, response->sge.addr))
return rc;
- response->sge.length = info->max_receive_size;
- response->sge.lkey = info->pd->local_dma_lkey;
+ response->sge.length = sp->max_recv_size;
+ response->sge.lkey = sc->ib.pd->local_dma_lkey;
response->cqe.done = recv_done;
@@ -1027,11 +1341,12 @@ static int smbd_post_recv(
recv_wr.sg_list = &response->sge;
recv_wr.num_sge = 1;
- rc = ib_post_recv(info->id->qp, &recv_wr, NULL);
+ rc = ib_post_recv(sc->ib.qp, &recv_wr, NULL);
if (rc) {
- ib_dma_unmap_single(info->id->device, response->sge.addr,
+ ib_dma_unmap_single(sc->ib.dev, response->sge.addr,
response->sge.length, DMA_FROM_DEVICE);
- smbd_disconnect_rdma_connection(info);
+ response->sge.length = 0;
+ smbd_disconnect_rdma_connection(sc);
log_rdma_recv(ERR, "ib_post_recv failed rc=%d\n", rc);
}
@@ -1039,30 +1354,36 @@ static int smbd_post_recv(
}
/* Perform SMBD negotiate according to [MS-SMBD] 3.1.5.2 */
-static int smbd_negotiate(struct smbd_connection *info)
+static int smbd_negotiate(struct smbdirect_socket *sc)
{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
int rc;
- struct smbd_response *response = get_receive_buffer(info);
+ struct smbdirect_recv_io *response = get_receive_buffer(sc);
- response->type = SMBD_NEGOTIATE_RESP;
- rc = smbd_post_recv(info, response);
+ WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_NEGOTIATE_NEEDED);
+ sc->status = SMBDIRECT_SOCKET_NEGOTIATE_RUNNING;
+
+ sc->recv_io.expected = SMBDIRECT_EXPECT_NEGOTIATE_REP;
+ rc = smbd_post_recv(sc, response);
log_rdma_event(INFO, "smbd_post_recv rc=%d iov.addr=0x%llx iov.length=%u iov.lkey=0x%x\n",
rc, response->sge.addr,
response->sge.length, response->sge.lkey);
- if (rc)
+ if (rc) {
+ put_receive_buffer(sc, response);
return rc;
+ }
- init_completion(&info->negotiate_completion);
- info->negotiate_done = false;
- rc = smbd_post_send_negotiate_req(info);
+ rc = smbd_post_send_negotiate_req(sc);
if (rc)
return rc;
- rc = wait_for_completion_interruptible_timeout(
- &info->negotiate_completion, SMBD_NEGOTIATE_TIMEOUT * HZ);
- log_rdma_event(INFO, "wait_for_completion_timeout rc=%d\n", rc);
+ rc = wait_event_interruptible_timeout(
+ sc->status_wait,
+ sc->status != SMBDIRECT_SOCKET_NEGOTIATE_RUNNING,
+ msecs_to_jiffies(sp->negotiate_timeout_msec));
+ log_rdma_event(INFO, "wait_event_interruptible_timeout rc=%d\n", rc);
- if (info->negotiate_done)
+ if (sc->status == SMBDIRECT_SOCKET_CONNECTED)
return 0;
if (rc == 0)
@@ -1075,17 +1396,6 @@ static int smbd_negotiate(struct smbd_connection *info)
return rc;
}
-static void put_empty_packet(
- struct smbd_connection *info, struct smbd_response *response)
-{
- spin_lock(&info->empty_packet_queue_lock);
- list_add_tail(&response->list, &info->empty_packet_queue);
- info->count_empty_packet_queue++;
- spin_unlock(&info->empty_packet_queue_lock);
-
- queue_work(info->workqueue, &info->post_send_credits_work);
-}
-
/*
* Implement Connection.FragmentReassemblyBuffer defined in [MS-SMBD] 3.1.1.1
* This is a queue for reassembling upper layer payload and present to upper
@@ -1097,13 +1407,15 @@ static void put_empty_packet(
* data_length: the size of payload in this packet
*/
static void enqueue_reassembly(
- struct smbd_connection *info,
- struct smbd_response *response,
+ struct smbdirect_socket *sc,
+ struct smbdirect_recv_io *response,
int data_length)
{
- spin_lock(&info->reassembly_queue_lock);
- list_add_tail(&response->list, &info->reassembly_queue);
- info->reassembly_queue_length++;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
+ list_add_tail(&response->list, &sc->recv_io.reassembly.list);
+ sc->recv_io.reassembly.queue_length++;
/*
* Make sure reassembly_data_length is updated after list and
* reassembly_queue_length are updated. On the dequeue side
@@ -1111,10 +1423,9 @@ static void enqueue_reassembly(
* if reassembly_queue_length and list is up to date
*/
virt_wmb();
- info->reassembly_data_length += data_length;
- spin_unlock(&info->reassembly_queue_lock);
- info->count_reassembly_queue++;
- info->count_enqueue_reassembly_queue++;
+ sc->recv_io.reassembly.data_length += data_length;
+ spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
+ sc->statistics.enqueue_reassembly_queue++;
}
/*
@@ -1122,34 +1433,15 @@ static void enqueue_reassembly(
* Caller is responsible for locking
* return value: the first entry if any, NULL if queue is empty
*/
-static struct smbd_response *_get_first_reassembly(struct smbd_connection *info)
-{
- struct smbd_response *ret = NULL;
-
- if (!list_empty(&info->reassembly_queue)) {
- ret = list_first_entry(
- &info->reassembly_queue,
- struct smbd_response, list);
- }
- return ret;
-}
-
-static struct smbd_response *get_empty_queue_buffer(
- struct smbd_connection *info)
+static struct smbdirect_recv_io *_get_first_reassembly(struct smbdirect_socket *sc)
{
- struct smbd_response *ret = NULL;
- unsigned long flags;
+ struct smbdirect_recv_io *ret = NULL;
- spin_lock_irqsave(&info->empty_packet_queue_lock, flags);
- if (!list_empty(&info->empty_packet_queue)) {
+ if (!list_empty(&sc->recv_io.reassembly.list)) {
ret = list_first_entry(
- &info->empty_packet_queue,
- struct smbd_response, list);
- list_del(&ret->list);
- info->count_empty_packet_queue--;
+ &sc->recv_io.reassembly.list,
+ struct smbdirect_recv_io, list);
}
- spin_unlock_irqrestore(&info->empty_packet_queue_lock, flags);
-
return ret;
}
@@ -1159,21 +1451,20 @@ static struct smbd_response *get_empty_queue_buffer(
* pre-allocated in advance.
* return value: the receive buffer, NULL if none is available
*/
-static struct smbd_response *get_receive_buffer(struct smbd_connection *info)
+static struct smbdirect_recv_io *get_receive_buffer(struct smbdirect_socket *sc)
{
- struct smbd_response *ret = NULL;
+ struct smbdirect_recv_io *ret = NULL;
unsigned long flags;
- spin_lock_irqsave(&info->receive_queue_lock, flags);
- if (!list_empty(&info->receive_queue)) {
+ spin_lock_irqsave(&sc->recv_io.free.lock, flags);
+ if (!list_empty(&sc->recv_io.free.list)) {
ret = list_first_entry(
- &info->receive_queue,
- struct smbd_response, list);
+ &sc->recv_io.free.list,
+ struct smbdirect_recv_io, list);
list_del(&ret->list);
- info->count_receive_queue--;
- info->count_get_receive_buffer++;
+ sc->statistics.get_receive_buffer++;
}
- spin_unlock_irqrestore(&info->receive_queue_lock, flags);
+ spin_unlock_irqrestore(&sc->recv_io.free.lock, flags);
return ret;
}
@@ -1185,100 +1476,103 @@ static struct smbd_response *get_receive_buffer(struct smbd_connection *info)
* receive buffer is returned.
*/
static void put_receive_buffer(
- struct smbd_connection *info, struct smbd_response *response)
+ struct smbdirect_socket *sc, struct smbdirect_recv_io *response)
{
unsigned long flags;
- ib_dma_unmap_single(info->id->device, response->sge.addr,
- response->sge.length, DMA_FROM_DEVICE);
+ if (likely(response->sge.length != 0)) {
+ ib_dma_unmap_single(sc->ib.dev,
+ response->sge.addr,
+ response->sge.length,
+ DMA_FROM_DEVICE);
+ response->sge.length = 0;
+ }
- spin_lock_irqsave(&info->receive_queue_lock, flags);
- list_add_tail(&response->list, &info->receive_queue);
- info->count_receive_queue++;
- info->count_put_receive_buffer++;
- spin_unlock_irqrestore(&info->receive_queue_lock, flags);
+ spin_lock_irqsave(&sc->recv_io.free.lock, flags);
+ list_add_tail(&response->list, &sc->recv_io.free.list);
+ sc->statistics.put_receive_buffer++;
+ spin_unlock_irqrestore(&sc->recv_io.free.lock, flags);
- queue_work(info->workqueue, &info->post_send_credits_work);
+ queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
}
/* Preallocate all receive buffer on transport establishment */
-static int allocate_receive_buffers(struct smbd_connection *info, int num_buf)
+static int allocate_receive_buffers(struct smbdirect_socket *sc, int num_buf)
{
+ struct smbdirect_recv_io *response;
int i;
- struct smbd_response *response;
-
- INIT_LIST_HEAD(&info->reassembly_queue);
- spin_lock_init(&info->reassembly_queue_lock);
- info->reassembly_data_length = 0;
- info->reassembly_queue_length = 0;
-
- INIT_LIST_HEAD(&info->receive_queue);
- spin_lock_init(&info->receive_queue_lock);
- info->count_receive_queue = 0;
-
- INIT_LIST_HEAD(&info->empty_packet_queue);
- spin_lock_init(&info->empty_packet_queue_lock);
- info->count_empty_packet_queue = 0;
-
- init_waitqueue_head(&info->wait_receive_queues);
for (i = 0; i < num_buf; i++) {
- response = mempool_alloc(info->response_mempool, GFP_KERNEL);
+ response = mempool_alloc(sc->recv_io.mem.pool, GFP_KERNEL);
if (!response)
goto allocate_failed;
- response->info = info;
- list_add_tail(&response->list, &info->receive_queue);
- info->count_receive_queue++;
+ response->socket = sc;
+ response->sge.length = 0;
+ list_add_tail(&response->list, &sc->recv_io.free.list);
}
return 0;
allocate_failed:
- while (!list_empty(&info->receive_queue)) {
+ while (!list_empty(&sc->recv_io.free.list)) {
response = list_first_entry(
- &info->receive_queue,
- struct smbd_response, list);
+ &sc->recv_io.free.list,
+ struct smbdirect_recv_io, list);
list_del(&response->list);
- info->count_receive_queue--;
- mempool_free(response, info->response_mempool);
+ mempool_free(response, sc->recv_io.mem.pool);
}
return -ENOMEM;
}
-static void destroy_receive_buffers(struct smbd_connection *info)
+static void destroy_receive_buffers(struct smbdirect_socket *sc)
{
- struct smbd_response *response;
+ struct smbdirect_recv_io *response;
- while ((response = get_receive_buffer(info)))
- mempool_free(response, info->response_mempool);
+ while ((response = get_receive_buffer(sc)))
+ mempool_free(response, sc->recv_io.mem.pool);
+}
- while ((response = get_empty_queue_buffer(info)))
- mempool_free(response, info->response_mempool);
+static void send_immediate_empty_message(struct work_struct *work)
+{
+ struct smbdirect_socket *sc =
+ container_of(work, struct smbdirect_socket, idle.immediate_work);
+
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
+ return;
+
+ log_keep_alive(INFO, "send an empty message\n");
+ smbd_post_send_empty(sc);
}
/* Implement idle connection timer [MS-SMBD] 3.1.6.2 */
static void idle_connection_timer(struct work_struct *work)
{
- struct smbd_connection *info = container_of(
- work, struct smbd_connection,
- idle_timer_work.work);
+ struct smbdirect_socket *sc =
+ container_of(work, struct smbdirect_socket, idle.timer_work.work);
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
- if (info->keep_alive_requested != KEEP_ALIVE_NONE) {
+ if (sc->idle.keepalive != SMBDIRECT_KEEPALIVE_NONE) {
log_keep_alive(ERR,
- "error status info->keep_alive_requested=%d\n",
- info->keep_alive_requested);
- smbd_disconnect_rdma_connection(info);
+ "error status sc->idle.keepalive=%d\n",
+ sc->idle.keepalive);
+ smbd_disconnect_rdma_connection(sc);
return;
}
- log_keep_alive(INFO, "about to send an empty idle message\n");
- smbd_post_send_empty(info);
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
+ return;
- /* Setup the next idle timeout work */
- queue_delayed_work(info->workqueue, &info->idle_timer_work,
- info->keep_alive_interval*HZ);
+ /*
+ * Now use the keepalive timeout (instead of keepalive interval)
+ * in order to wait for a response
+ */
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_PENDING;
+ mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->keepalive_timeout_msec));
+ log_keep_alive(INFO, "schedule send of empty idle message\n");
+ queue_work(sc->workqueue, &sc->idle.immediate_work);
}
/*
@@ -1289,87 +1583,87 @@ static void idle_connection_timer(struct work_struct *work)
void smbd_destroy(struct TCP_Server_Info *server)
{
struct smbd_connection *info = server->smbd_conn;
- struct smbd_response *response;
+ struct smbdirect_socket *sc;
+ struct smbdirect_recv_io *response;
unsigned long flags;
if (!info) {
log_rdma_event(INFO, "rdma session already destroyed\n");
return;
}
+ sc = &info->socket;
+
+ log_rdma_event(INFO, "cancelling and disable disconnect_work\n");
+ disable_work_sync(&sc->disconnect_work);
log_rdma_event(INFO, "destroying rdma session\n");
- if (info->transport_status != SMBD_DISCONNECTED) {
- rdma_disconnect(server->smbd_conn->id);
+ if (sc->status < SMBDIRECT_SOCKET_DISCONNECTING)
+ smbd_disconnect_rdma_work(&sc->disconnect_work);
+ if (sc->status < SMBDIRECT_SOCKET_DISCONNECTED) {
log_rdma_event(INFO, "wait for transport being disconnected\n");
- wait_event_interruptible(
- info->disconn_wait,
- info->transport_status == SMBD_DISCONNECTED);
+ wait_event(sc->status_wait, sc->status == SMBDIRECT_SOCKET_DISCONNECTED);
+ log_rdma_event(INFO, "waited for transport being disconnected\n");
}
+ /*
+ * Wake up all waiters in all wait queues
+ * in order to notice the broken connection.
+ *
+ * Most likely this was already called via
+ * smbd_disconnect_rdma_work(), but call it again...
+ */
+ smbd_disconnect_wake_up_all(sc);
+
+ log_rdma_event(INFO, "cancelling recv_io.posted.refill_work\n");
+ disable_work_sync(&sc->recv_io.posted.refill_work);
+
log_rdma_event(INFO, "destroying qp\n");
- ib_drain_qp(info->id->qp);
- rdma_destroy_qp(info->id);
+ ib_drain_qp(sc->ib.qp);
+ rdma_destroy_qp(sc->rdma.cm_id);
+ sc->ib.qp = NULL;
log_rdma_event(INFO, "cancelling idle timer\n");
- cancel_delayed_work_sync(&info->idle_timer_work);
-
- log_rdma_event(INFO, "wait for all send posted to IB to finish\n");
- wait_event(info->wait_send_pending,
- atomic_read(&info->send_pending) == 0);
+ disable_delayed_work_sync(&sc->idle.timer_work);
+ log_rdma_event(INFO, "cancelling send immediate work\n");
+ disable_work_sync(&sc->idle.immediate_work);
/* It's not possible for upper layer to get to reassembly */
log_rdma_event(INFO, "drain the reassembly queue\n");
do {
- spin_lock_irqsave(&info->reassembly_queue_lock, flags);
- response = _get_first_reassembly(info);
+ spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
+ response = _get_first_reassembly(sc);
if (response) {
list_del(&response->list);
spin_unlock_irqrestore(
- &info->reassembly_queue_lock, flags);
- put_receive_buffer(info, response);
+ &sc->recv_io.reassembly.lock, flags);
+ put_receive_buffer(sc, response);
} else
spin_unlock_irqrestore(
- &info->reassembly_queue_lock, flags);
+ &sc->recv_io.reassembly.lock, flags);
} while (response);
- info->reassembly_data_length = 0;
+ sc->recv_io.reassembly.data_length = 0;
log_rdma_event(INFO, "free receive buffers\n");
- wait_event(info->wait_receive_queues,
- info->count_receive_queue + info->count_empty_packet_queue
- == info->receive_credit_max);
- destroy_receive_buffers(info);
+ destroy_receive_buffers(sc);
- /*
- * For performance reasons, memory registration and deregistration
- * are not locked by srv_mutex. It is possible some processes are
- * blocked on transport srv_mutex while holding memory registration.
- * Release the transport srv_mutex to allow them to hit the failure
- * path when sending data, and then release memory registrations.
- */
log_rdma_event(INFO, "freeing mr list\n");
- wake_up_interruptible_all(&info->wait_mr);
- while (atomic_read(&info->mr_used_count)) {
- cifs_server_unlock(server);
- msleep(1000);
- cifs_server_lock(server);
- }
- destroy_mr_list(info);
+ destroy_mr_list(sc);
- ib_free_cq(info->send_cq);
- ib_free_cq(info->recv_cq);
- ib_dealloc_pd(info->pd);
- rdma_destroy_id(info->id);
+ ib_free_cq(sc->ib.send_cq);
+ ib_free_cq(sc->ib.recv_cq);
+ ib_dealloc_pd(sc->ib.pd);
+ rdma_destroy_id(sc->rdma.cm_id);
/* free mempools */
- mempool_destroy(info->request_mempool);
- kmem_cache_destroy(info->request_cache);
+ mempool_destroy(sc->send_io.mem.pool);
+ kmem_cache_destroy(sc->send_io.mem.cache);
- mempool_destroy(info->response_mempool);
- kmem_cache_destroy(info->response_cache);
+ mempool_destroy(sc->recv_io.mem.pool);
+ kmem_cache_destroy(sc->recv_io.mem.cache);
- info->transport_status = SMBD_DESTROYED;
+ sc->status = SMBDIRECT_SOCKET_DESTROYED;
- destroy_workqueue(info->workqueue);
+ destroy_workqueue(sc->workqueue);
log_rdma_event(INFO, "rdma session destroyed\n");
kfree(info);
server->smbd_conn = NULL;
@@ -1392,7 +1686,7 @@ int smbd_reconnect(struct TCP_Server_Info *server)
* This is possible if transport is disconnected and we haven't received
* notification from RDMA, but upper layer has detected timeout
*/
- if (server->smbd_conn->transport_status == SMBD_CONNECTED) {
+ if (server->smbd_conn->socket.status == SMBDIRECT_SOCKET_CONNECTED) {
log_rdma_event(INFO, "disconnecting transport\n");
smbd_destroy(server);
}
@@ -1411,77 +1705,78 @@ create_conn:
return -ENOENT;
}
-static void destroy_caches_and_workqueue(struct smbd_connection *info)
+static void destroy_caches(struct smbdirect_socket *sc)
{
- destroy_receive_buffers(info);
- destroy_workqueue(info->workqueue);
- mempool_destroy(info->response_mempool);
- kmem_cache_destroy(info->response_cache);
- mempool_destroy(info->request_mempool);
- kmem_cache_destroy(info->request_cache);
+ destroy_receive_buffers(sc);
+ mempool_destroy(sc->recv_io.mem.pool);
+ kmem_cache_destroy(sc->recv_io.mem.cache);
+ mempool_destroy(sc->send_io.mem.pool);
+ kmem_cache_destroy(sc->send_io.mem.cache);
}
#define MAX_NAME_LEN 80
-static int allocate_caches_and_workqueue(struct smbd_connection *info)
+static int allocate_caches(struct smbdirect_socket *sc)
{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
char name[MAX_NAME_LEN];
int rc;
- scnprintf(name, MAX_NAME_LEN, "smbd_request_%p", info);
- info->request_cache =
+ if (WARN_ON_ONCE(sp->max_recv_size < sizeof(struct smbdirect_data_transfer)))
+ return -ENOMEM;
+
+ scnprintf(name, MAX_NAME_LEN, "smbdirect_send_io_%p", sc);
+ sc->send_io.mem.cache =
kmem_cache_create(
name,
- sizeof(struct smbd_request) +
- sizeof(struct smbd_data_transfer),
+ sizeof(struct smbdirect_send_io) +
+ sizeof(struct smbdirect_data_transfer),
0, SLAB_HWCACHE_ALIGN, NULL);
- if (!info->request_cache)
+ if (!sc->send_io.mem.cache)
return -ENOMEM;
- info->request_mempool =
- mempool_create(info->send_credit_target, mempool_alloc_slab,
- mempool_free_slab, info->request_cache);
- if (!info->request_mempool)
+ sc->send_io.mem.pool =
+ mempool_create(sp->send_credit_target, mempool_alloc_slab,
+ mempool_free_slab, sc->send_io.mem.cache);
+ if (!sc->send_io.mem.pool)
goto out1;
- scnprintf(name, MAX_NAME_LEN, "smbd_response_%p", info);
- info->response_cache =
- kmem_cache_create(
- name,
- sizeof(struct smbd_response) +
- info->max_receive_size,
- 0, SLAB_HWCACHE_ALIGN, NULL);
- if (!info->response_cache)
+ scnprintf(name, MAX_NAME_LEN, "smbdirect_recv_io_%p", sc);
+
+ struct kmem_cache_args response_args = {
+ .align = __alignof__(struct smbdirect_recv_io),
+ .useroffset = (offsetof(struct smbdirect_recv_io, packet) +
+ sizeof(struct smbdirect_data_transfer)),
+ .usersize = sp->max_recv_size - sizeof(struct smbdirect_data_transfer),
+ };
+ sc->recv_io.mem.cache =
+ kmem_cache_create(name,
+ sizeof(struct smbdirect_recv_io) + sp->max_recv_size,
+ &response_args, SLAB_HWCACHE_ALIGN);
+ if (!sc->recv_io.mem.cache)
goto out2;
- info->response_mempool =
- mempool_create(info->receive_credit_max, mempool_alloc_slab,
- mempool_free_slab, info->response_cache);
- if (!info->response_mempool)
+ sc->recv_io.mem.pool =
+ mempool_create(sp->recv_credit_max, mempool_alloc_slab,
+ mempool_free_slab, sc->recv_io.mem.cache);
+ if (!sc->recv_io.mem.pool)
goto out3;
- scnprintf(name, MAX_NAME_LEN, "smbd_%p", info);
- info->workqueue = create_workqueue(name);
- if (!info->workqueue)
- goto out4;
-
- rc = allocate_receive_buffers(info, info->receive_credit_max);
+ rc = allocate_receive_buffers(sc, sp->recv_credit_max);
if (rc) {
log_rdma_event(ERR, "failed to allocate receive buffers\n");
- goto out5;
+ goto out4;
}
return 0;
-out5:
- destroy_workqueue(info->workqueue);
out4:
- mempool_destroy(info->response_mempool);
+ mempool_destroy(sc->recv_io.mem.pool);
out3:
- kmem_cache_destroy(info->response_cache);
+ kmem_cache_destroy(sc->recv_io.mem.cache);
out2:
- mempool_destroy(info->request_mempool);
+ mempool_destroy(sc->send_io.mem.pool);
out1:
- kmem_cache_destroy(info->request_cache);
+ kmem_cache_destroy(sc->send_io.mem.cache);
return -ENOMEM;
}
@@ -1491,113 +1786,151 @@ static struct smbd_connection *_smbd_get_connection(
{
int rc;
struct smbd_connection *info;
+ struct smbdirect_socket *sc;
+ struct smbdirect_socket_parameters *sp;
struct rdma_conn_param conn_param;
+ struct ib_qp_cap qp_cap;
struct ib_qp_init_attr qp_attr;
struct sockaddr_in *addr_in = (struct sockaddr_in *) dstaddr;
struct ib_port_immutable port_immutable;
- u32 ird_ord_hdr[2];
+ __be32 ird_ord_hdr[2];
+ char wq_name[80];
+ struct workqueue_struct *workqueue;
info = kzalloc(sizeof(struct smbd_connection), GFP_KERNEL);
if (!info)
return NULL;
-
- info->transport_status = SMBD_CONNECTING;
- rc = smbd_ia_open(info, dstaddr, port);
+ sc = &info->socket;
+ scnprintf(wq_name, ARRAY_SIZE(wq_name), "smbd_%p", sc);
+ workqueue = create_workqueue(wq_name);
+ if (!workqueue)
+ goto create_wq_failed;
+ smbdirect_socket_init(sc);
+ sc->workqueue = workqueue;
+ sp = &sc->parameters;
+
+ INIT_WORK(&sc->disconnect_work, smbd_disconnect_rdma_work);
+
+ sp->resolve_addr_timeout_msec = RDMA_RESOLVE_TIMEOUT;
+ sp->resolve_route_timeout_msec = RDMA_RESOLVE_TIMEOUT;
+ sp->rdma_connect_timeout_msec = RDMA_RESOLVE_TIMEOUT;
+ sp->negotiate_timeout_msec = SMBD_NEGOTIATE_TIMEOUT * 1000;
+ sp->initiator_depth = 1;
+ sp->responder_resources = SMBD_CM_RESPONDER_RESOURCES;
+ sp->recv_credit_max = smbd_receive_credit_max;
+ sp->send_credit_target = smbd_send_credit_target;
+ sp->max_send_size = smbd_max_send_size;
+ sp->max_fragmented_recv_size = smbd_max_fragmented_recv_size;
+ sp->max_recv_size = smbd_max_receive_size;
+ sp->max_frmr_depth = smbd_max_frmr_depth;
+ sp->keepalive_interval_msec = smbd_keep_alive_interval * 1000;
+ sp->keepalive_timeout_msec = KEEPALIVE_RECV_TIMEOUT * 1000;
+
+ rc = smbd_ia_open(sc, dstaddr, port);
if (rc) {
log_rdma_event(INFO, "smbd_ia_open rc=%d\n", rc);
goto create_id_failed;
}
- if (smbd_send_credit_target > info->id->device->attrs.max_cqe ||
- smbd_send_credit_target > info->id->device->attrs.max_qp_wr) {
+ if (sp->send_credit_target > sc->ib.dev->attrs.max_cqe ||
+ sp->send_credit_target > sc->ib.dev->attrs.max_qp_wr) {
log_rdma_event(ERR, "consider lowering send_credit_target = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n",
- smbd_send_credit_target,
- info->id->device->attrs.max_cqe,
- info->id->device->attrs.max_qp_wr);
+ sp->send_credit_target,
+ sc->ib.dev->attrs.max_cqe,
+ sc->ib.dev->attrs.max_qp_wr);
goto config_failed;
}
- if (smbd_receive_credit_max > info->id->device->attrs.max_cqe ||
- smbd_receive_credit_max > info->id->device->attrs.max_qp_wr) {
+ if (sp->recv_credit_max > sc->ib.dev->attrs.max_cqe ||
+ sp->recv_credit_max > sc->ib.dev->attrs.max_qp_wr) {
log_rdma_event(ERR, "consider lowering receive_credit_max = %d. Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n",
- smbd_receive_credit_max,
- info->id->device->attrs.max_cqe,
- info->id->device->attrs.max_qp_wr);
+ sp->recv_credit_max,
+ sc->ib.dev->attrs.max_cqe,
+ sc->ib.dev->attrs.max_qp_wr);
goto config_failed;
}
- info->receive_credit_max = smbd_receive_credit_max;
- info->send_credit_target = smbd_send_credit_target;
- info->max_send_size = smbd_max_send_size;
- info->max_fragmented_recv_size = smbd_max_fragmented_recv_size;
- info->max_receive_size = smbd_max_receive_size;
- info->keep_alive_interval = smbd_keep_alive_interval;
-
- if (info->id->device->attrs.max_send_sge < SMBDIRECT_MAX_SEND_SGE ||
- info->id->device->attrs.max_recv_sge < SMBDIRECT_MAX_RECV_SGE) {
+ if (sc->ib.dev->attrs.max_send_sge < SMBDIRECT_SEND_IO_MAX_SGE ||
+ sc->ib.dev->attrs.max_recv_sge < SMBDIRECT_RECV_IO_MAX_SGE) {
log_rdma_event(ERR,
"device %.*s max_send_sge/max_recv_sge = %d/%d too small\n",
IB_DEVICE_NAME_MAX,
- info->id->device->name,
- info->id->device->attrs.max_send_sge,
- info->id->device->attrs.max_recv_sge);
+ sc->ib.dev->name,
+ sc->ib.dev->attrs.max_send_sge,
+ sc->ib.dev->attrs.max_recv_sge);
goto config_failed;
}
- info->send_cq = NULL;
- info->recv_cq = NULL;
- info->send_cq =
- ib_alloc_cq_any(info->id->device, info,
- info->send_credit_target, IB_POLL_SOFTIRQ);
- if (IS_ERR(info->send_cq)) {
- info->send_cq = NULL;
+ sp->responder_resources =
+ min_t(u8, sp->responder_resources,
+ sc->ib.dev->attrs.max_qp_rd_atom);
+ log_rdma_mr(INFO, "responder_resources=%d\n",
+ sp->responder_resources);
+
+ /*
+ * We use allocate sp->responder_resources * 2 MRs
+ * and each MR needs WRs for REG and INV, so
+ * we use '* 4'.
+ *
+ * +1 for ib_drain_qp()
+ */
+ memset(&qp_cap, 0, sizeof(qp_cap));
+ qp_cap.max_send_wr = sp->send_credit_target + sp->responder_resources * 4 + 1;
+ qp_cap.max_recv_wr = sp->recv_credit_max + 1;
+ qp_cap.max_send_sge = SMBDIRECT_SEND_IO_MAX_SGE;
+ qp_cap.max_recv_sge = SMBDIRECT_RECV_IO_MAX_SGE;
+
+ sc->ib.pd = ib_alloc_pd(sc->ib.dev, 0);
+ if (IS_ERR(sc->ib.pd)) {
+ rc = PTR_ERR(sc->ib.pd);
+ sc->ib.pd = NULL;
+ log_rdma_event(ERR, "ib_alloc_pd() returned %d\n", rc);
+ goto alloc_pd_failed;
+ }
+
+ sc->ib.send_cq =
+ ib_alloc_cq_any(sc->ib.dev, sc,
+ qp_cap.max_send_wr, IB_POLL_SOFTIRQ);
+ if (IS_ERR(sc->ib.send_cq)) {
+ sc->ib.send_cq = NULL;
goto alloc_cq_failed;
}
- info->recv_cq =
- ib_alloc_cq_any(info->id->device, info,
- info->receive_credit_max, IB_POLL_SOFTIRQ);
- if (IS_ERR(info->recv_cq)) {
- info->recv_cq = NULL;
+ sc->ib.recv_cq =
+ ib_alloc_cq_any(sc->ib.dev, sc,
+ qp_cap.max_recv_wr, IB_POLL_SOFTIRQ);
+ if (IS_ERR(sc->ib.recv_cq)) {
+ sc->ib.recv_cq = NULL;
goto alloc_cq_failed;
}
memset(&qp_attr, 0, sizeof(qp_attr));
qp_attr.event_handler = smbd_qp_async_error_upcall;
- qp_attr.qp_context = info;
- qp_attr.cap.max_send_wr = info->send_credit_target;
- qp_attr.cap.max_recv_wr = info->receive_credit_max;
- qp_attr.cap.max_send_sge = SMBDIRECT_MAX_SEND_SGE;
- qp_attr.cap.max_recv_sge = SMBDIRECT_MAX_RECV_SGE;
- qp_attr.cap.max_inline_data = 0;
+ qp_attr.qp_context = sc;
+ qp_attr.cap = qp_cap;
qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
qp_attr.qp_type = IB_QPT_RC;
- qp_attr.send_cq = info->send_cq;
- qp_attr.recv_cq = info->recv_cq;
+ qp_attr.send_cq = sc->ib.send_cq;
+ qp_attr.recv_cq = sc->ib.recv_cq;
qp_attr.port_num = ~0;
- rc = rdma_create_qp(info->id, info->pd, &qp_attr);
+ rc = rdma_create_qp(sc->rdma.cm_id, sc->ib.pd, &qp_attr);
if (rc) {
log_rdma_event(ERR, "rdma_create_qp failed %i\n", rc);
goto create_qp_failed;
}
+ sc->ib.qp = sc->rdma.cm_id->qp;
memset(&conn_param, 0, sizeof(conn_param));
- conn_param.initiator_depth = 0;
-
- conn_param.responder_resources =
- min(info->id->device->attrs.max_qp_rd_atom,
- SMBD_CM_RESPONDER_RESOURCES);
- info->responder_resources = conn_param.responder_resources;
- log_rdma_mr(INFO, "responder_resources=%d\n",
- info->responder_resources);
+ conn_param.initiator_depth = sp->initiator_depth;
+ conn_param.responder_resources = sp->responder_resources;
/* Need to send IRD/ORD in private data for iWARP */
- info->id->device->ops.get_port_immutable(
- info->id->device, info->id->port_num, &port_immutable);
+ sc->ib.dev->ops.get_port_immutable(
+ sc->ib.dev, sc->rdma.cm_id->port_num, &port_immutable);
if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) {
- ird_ord_hdr[0] = info->responder_resources;
- ird_ord_hdr[1] = 1;
+ ird_ord_hdr[0] = cpu_to_be32(conn_param.responder_resources);
+ ird_ord_hdr[1] = cpu_to_be32(conn_param.initiator_depth);
conn_param.private_data = ird_ord_hdr;
conn_param.private_data_len = sizeof(ird_ord_hdr);
} else {
@@ -1612,53 +1945,51 @@ static struct smbd_connection *_smbd_get_connection(
log_rdma_event(INFO, "connecting to IP %pI4 port %d\n",
&addr_in->sin_addr, port);
- init_waitqueue_head(&info->conn_wait);
- init_waitqueue_head(&info->disconn_wait);
- init_waitqueue_head(&info->wait_reassembly_queue);
- rc = rdma_connect(info->id, &conn_param);
+ WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED);
+ sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING;
+ rc = rdma_connect(sc->rdma.cm_id, &conn_param);
if (rc) {
log_rdma_event(ERR, "rdma_connect() failed with %i\n", rc);
goto rdma_connect_failed;
}
- wait_event_interruptible(
- info->conn_wait, info->transport_status != SMBD_CONNECTING);
+ wait_event_interruptible_timeout(
+ sc->status_wait,
+ sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING,
+ msecs_to_jiffies(sp->rdma_connect_timeout_msec));
- if (info->transport_status != SMBD_CONNECTED) {
+ if (sc->status != SMBDIRECT_SOCKET_NEGOTIATE_NEEDED) {
log_rdma_event(ERR, "rdma_connect failed port=%d\n", port);
goto rdma_connect_failed;
}
log_rdma_event(INFO, "rdma_connect connected\n");
- rc = allocate_caches_and_workqueue(info);
+ rc = allocate_caches(sc);
if (rc) {
log_rdma_event(ERR, "cache allocation failed\n");
goto allocate_cache_failed;
}
- init_waitqueue_head(&info->wait_send_queue);
- INIT_DELAYED_WORK(&info->idle_timer_work, idle_connection_timer);
- queue_delayed_work(info->workqueue, &info->idle_timer_work,
- info->keep_alive_interval*HZ);
-
- init_waitqueue_head(&info->wait_send_pending);
- atomic_set(&info->send_pending, 0);
-
- init_waitqueue_head(&info->wait_post_send);
+ INIT_WORK(&sc->idle.immediate_work, send_immediate_empty_message);
+ INIT_DELAYED_WORK(&sc->idle.timer_work, idle_connection_timer);
+ /*
+ * start with the negotiate timeout and SMBDIRECT_KEEPALIVE_PENDING
+ * so that the timer will cause a disconnect.
+ */
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_PENDING;
+ mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->negotiate_timeout_msec));
- INIT_WORK(&info->disconnect_work, smbd_disconnect_rdma_work);
- INIT_WORK(&info->post_send_credits_work, smbd_post_send_credits);
- info->new_credits_offered = 0;
- spin_lock_init(&info->lock_new_credits_offered);
+ INIT_WORK(&sc->recv_io.posted.refill_work, smbd_post_send_credits);
- rc = smbd_negotiate(info);
+ rc = smbd_negotiate(sc);
if (rc) {
log_rdma_event(ERR, "smbd_negotiate rc=%d\n", rc);
goto negotiation_failed;
}
- rc = allocate_mr_list(info);
+ rc = allocate_mr_list(sc);
if (rc) {
log_rdma_mr(ERR, "memory registration allocation failed\n");
goto allocate_mr_failed;
@@ -1673,30 +2004,33 @@ allocate_mr_failed:
return NULL;
negotiation_failed:
- cancel_delayed_work_sync(&info->idle_timer_work);
- destroy_caches_and_workqueue(info);
- info->transport_status = SMBD_NEGOTIATE_FAILED;
- init_waitqueue_head(&info->conn_wait);
- rdma_disconnect(info->id);
- wait_event(info->conn_wait,
- info->transport_status == SMBD_DISCONNECTED);
+ disable_delayed_work_sync(&sc->idle.timer_work);
+ destroy_caches(sc);
+ sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
+ rdma_disconnect(sc->rdma.cm_id);
+ wait_event(sc->status_wait,
+ sc->status == SMBDIRECT_SOCKET_DISCONNECTED);
allocate_cache_failed:
rdma_connect_failed:
- rdma_destroy_qp(info->id);
+ rdma_destroy_qp(sc->rdma.cm_id);
create_qp_failed:
alloc_cq_failed:
- if (info->send_cq)
- ib_free_cq(info->send_cq);
- if (info->recv_cq)
- ib_free_cq(info->recv_cq);
+ if (sc->ib.send_cq)
+ ib_free_cq(sc->ib.send_cq);
+ if (sc->ib.recv_cq)
+ ib_free_cq(sc->ib.recv_cq);
+ ib_dealloc_pd(sc->ib.pd);
+
+alloc_pd_failed:
config_failed:
- ib_dealloc_pd(info->pd);
- rdma_destroy_id(info->id);
+ rdma_destroy_id(sc->rdma.cm_id);
create_id_failed:
+ destroy_workqueue(sc->workqueue);
+create_wq_failed:
kfree(info);
return NULL;
}
@@ -1705,6 +2039,7 @@ struct smbd_connection *smbd_get_connection(
struct TCP_Server_Info *server, struct sockaddr *dstaddr)
{
struct smbd_connection *ret;
+ const struct smbdirect_socket_parameters *sp;
int port = SMBD_PORT;
try_again:
@@ -1715,42 +2050,58 @@ try_again:
port = SMB_PORT;
goto try_again;
}
+ if (!ret)
+ return NULL;
+
+ sp = &ret->socket.parameters;
+
+ server->rdma_readwrite_threshold =
+ rdma_readwrite_threshold > sp->max_fragmented_send_size ?
+ sp->max_fragmented_send_size :
+ rdma_readwrite_threshold;
+
return ret;
}
/*
- * Receive data from receive reassembly queue
+ * Receive data from the transport's receive reassembly queue
* All the incoming data packets are placed in reassembly queue
- * buf: the buffer to read data into
+ * iter: the buffer to read data into
* size: the length of data to read
* return value: actual data read
- * Note: this implementation copies the data from reassebmly queue to receive
+ *
+ * Note: this implementation copies the data from reassembly queue to receive
* buffers used by upper layer. This is not the optimal code path. A better way
* to do it is to not have upper layer allocate its receive buffers but rather
* borrow the buffer from reassembly queue, and return it after data is
* consumed. But this will require more changes to upper layer code, and also
* need to consider packet boundaries while they still being reassembled.
*/
-static int smbd_recv_buf(struct smbd_connection *info, char *buf,
- unsigned int size)
+int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
{
- struct smbd_response *response;
- struct smbd_data_transfer *data_transfer;
+ struct smbdirect_socket *sc = &info->socket;
+ struct smbdirect_recv_io *response;
+ struct smbdirect_data_transfer *data_transfer;
+ size_t size = iov_iter_count(&msg->msg_iter);
int to_copy, to_read, data_read, offset;
u32 data_length, remaining_data_length, data_offset;
int rc;
+ if (WARN_ON_ONCE(iov_iter_rw(&msg->msg_iter) == WRITE))
+ return -EINVAL; /* It's a bug in upper layer to get there */
+
again:
/*
* No need to hold the reassembly queue lock all the time as we are
* the only one reading from the front of the queue. The transport
* may add more entries to the back of the queue at the same time
*/
- log_read(INFO, "size=%d info->reassembly_data_length=%d\n", size,
- info->reassembly_data_length);
- if (info->reassembly_data_length >= size) {
+ log_read(INFO, "size=%zd sc->recv_io.reassembly.data_length=%d\n", size,
+ sc->recv_io.reassembly.data_length);
+ if (sc->recv_io.reassembly.data_length >= size) {
int queue_length;
int queue_removed = 0;
+ unsigned long flags;
/*
* Need to make sure reassembly_data_length is read before
@@ -1760,13 +2111,13 @@ again:
* updated in SOFTIRQ as more data is received
*/
virt_rmb();
- queue_length = info->reassembly_queue_length;
+ queue_length = sc->recv_io.reassembly.queue_length;
data_read = 0;
to_read = size;
- offset = info->first_entry_offset;
+ offset = sc->recv_io.reassembly.first_entry_offset;
while (data_read < size) {
- response = _get_first_reassembly(info);
- data_transfer = smbd_response_payload(response);
+ response = _get_first_reassembly(sc);
+ data_transfer = smbdirect_recv_io_payload(response);
data_length = le32_to_cpu(data_transfer->data_length);
remaining_data_length =
le32_to_cpu(
@@ -1784,7 +2135,10 @@ again:
if (response->first_segment && size == 4) {
unsigned int rfc1002_len =
data_length + remaining_data_length;
- *((__be32 *)buf) = cpu_to_be32(rfc1002_len);
+ __be32 rfc1002_hdr = cpu_to_be32(rfc1002_len);
+ if (copy_to_iter(&rfc1002_hdr, sizeof(rfc1002_hdr),
+ &msg->msg_iter) != sizeof(rfc1002_hdr))
+ return -EFAULT;
data_read = 4;
response->first_segment = false;
log_read(INFO, "returning rfc1002 length %d\n",
@@ -1793,10 +2147,9 @@ again:
}
to_copy = min_t(int, data_length - offset, to_read);
- memcpy(
- buf + data_read,
- (char *)data_transfer + data_offset + offset,
- to_copy);
+ if (copy_to_iter((char *)data_transfer + data_offset + offset,
+ to_copy, &msg->msg_iter) != to_copy)
+ return -EFAULT;
/* move on to the next buffer? */
if (to_copy == data_length - offset) {
@@ -1808,16 +2161,15 @@ again:
if (queue_length)
list_del(&response->list);
else {
- spin_lock_irq(
- &info->reassembly_queue_lock);
+ spin_lock_irqsave(
+ &sc->recv_io.reassembly.lock, flags);
list_del(&response->list);
- spin_unlock_irq(
- &info->reassembly_queue_lock);
+ spin_unlock_irqrestore(
+ &sc->recv_io.reassembly.lock, flags);
}
queue_removed++;
- info->count_reassembly_queue--;
- info->count_dequeue_reassembly_queue++;
- put_receive_buffer(info, response);
+ sc->statistics.dequeue_reassembly_queue++;
+ put_receive_buffer(sc, response);
offset = 0;
log_read(INFO, "put_receive_buffer offset=0\n");
} else
@@ -1831,29 +2183,29 @@ again:
to_read, data_read, offset);
}
- spin_lock_irq(&info->reassembly_queue_lock);
- info->reassembly_data_length -= data_read;
- info->reassembly_queue_length -= queue_removed;
- spin_unlock_irq(&info->reassembly_queue_lock);
+ spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
+ sc->recv_io.reassembly.data_length -= data_read;
+ sc->recv_io.reassembly.queue_length -= queue_removed;
+ spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
- info->first_entry_offset = offset;
+ sc->recv_io.reassembly.first_entry_offset = offset;
log_read(INFO, "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n",
- data_read, info->reassembly_data_length,
- info->first_entry_offset);
+ data_read, sc->recv_io.reassembly.data_length,
+ sc->recv_io.reassembly.first_entry_offset);
read_rfc1002_done:
return data_read;
}
log_read(INFO, "wait_event on more data\n");
rc = wait_event_interruptible(
- info->wait_reassembly_queue,
- info->reassembly_data_length >= size ||
- info->transport_status != SMBD_CONNECTED);
+ sc->recv_io.reassembly.wait_queue,
+ sc->recv_io.reassembly.data_length >= size ||
+ sc->status != SMBDIRECT_SOCKET_CONNECTED);
/* Don't return any data if interrupted */
if (rc)
return rc;
- if (info->transport_status != SMBD_CONNECTED) {
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
log_read(ERR, "disconnected\n");
return -ECONNABORTED;
}
@@ -1862,89 +2214,6 @@ read_rfc1002_done:
}
/*
- * Receive a page from receive reassembly queue
- * page: the page to read data into
- * to_read: the length of data to read
- * return value: actual data read
- */
-static int smbd_recv_page(struct smbd_connection *info,
- 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(
- info->wait_reassembly_queue,
- info->reassembly_data_length >= to_read ||
- info->transport_status != SMBD_CONNECTED);
- if (ret)
- return ret;
-
- /* now we can read from reassembly queue and not sleep */
- 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(page_address);
-
- return ret;
-}
-
-/*
- * Receive data from transport
- * msg: a msghdr point to the buffer, can be ITER_KVEC or ITER_BVEC
- * return: total bytes read, or 0. SMB Direct will not do partial read.
- */
-int smbd_recv(struct smbd_connection *info, struct msghdr *msg)
-{
- char *buf;
- struct page *page;
- unsigned int to_read, page_offset;
- int rc;
-
- if (iov_iter_rw(&msg->msg_iter) == WRITE) {
- /* It's a bug in upper layer to get there */
- cifs_dbg(VFS, "Invalid msg iter dir %u\n",
- iov_iter_rw(&msg->msg_iter));
- rc = -EINVAL;
- goto out;
- }
-
- switch (iov_iter_type(&msg->msg_iter)) {
- case ITER_KVEC:
- buf = msg->msg_iter.kvec->iov_base;
- to_read = msg->msg_iter.kvec->iov_len;
- rc = smbd_recv_buf(info, buf, to_read);
- break;
-
- case 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, page_offset, to_read);
- break;
-
- default:
- /* It's a bug in upper layer to get there */
- cifs_dbg(VFS, "Invalid msg type %d\n",
- iov_iter_type(&msg->msg_iter));
- rc = -EINVAL;
- }
-
-out:
- /* SMBDirect will read it all or nothing */
- if (rc > 0)
- msg->msg_iter.count = 0;
- return rc;
-}
-
-/*
* Send data to transport
* Each rqst is transported as a SMBDirect payload
* rqst: the data to write
@@ -1954,12 +2223,14 @@ int smbd_send(struct TCP_Server_Info *server,
int num_rqst, struct smb_rqst *rqst_array)
{
struct smbd_connection *info = server->smbd_conn;
+ struct smbdirect_socket *sc = &info->socket;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
struct smb_rqst *rqst;
struct iov_iter iter;
unsigned int remaining_data_length, klen;
int rc, i, rqst_idx;
- if (info->transport_status != SMBD_CONNECTED)
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
return -EAGAIN;
/*
@@ -1971,10 +2242,10 @@ int smbd_send(struct TCP_Server_Info *server,
for (i = 0; i < num_rqst; i++)
remaining_data_length += smb_rqst_len(server, &rqst_array[i]);
- if (unlikely(remaining_data_length > info->max_fragmented_send_size)) {
+ if (unlikely(remaining_data_length > sp->max_fragmented_send_size)) {
/* assertion: payload never exceeds negotiated maximum */
log_write(ERR, "payload size %d > max size %d\n",
- remaining_data_length, info->max_fragmented_send_size);
+ remaining_data_length, sp->max_fragmented_send_size);
return -EINVAL;
}
@@ -2000,14 +2271,14 @@ int smbd_send(struct TCP_Server_Info *server,
klen += rqst->rq_iov[i].iov_len;
iov_iter_kvec(&iter, ITER_SOURCE, rqst->rq_iov, rqst->rq_nvec, klen);
- rc = smbd_post_send_iter(info, &iter, &remaining_data_length);
+ rc = smbd_post_send_full_iter(sc, &iter, &remaining_data_length);
if (rc < 0)
break;
if (iov_iter_count(&rqst->rq_iter) > 0) {
/* And then the data pages if there are any */
- rc = smbd_post_send_iter(info, &rqst->rq_iter,
- &remaining_data_length);
+ rc = smbd_post_send_full_iter(sc, &rqst->rq_iter,
+ &remaining_data_length);
if (rc < 0)
break;
}
@@ -2021,22 +2292,25 @@ int smbd_send(struct TCP_Server_Info *server,
* that means all the I/Os have been out and we are good to return
*/
- wait_event(info->wait_send_pending,
- atomic_read(&info->send_pending) == 0);
+ wait_event(sc->send_io.pending.zero_wait_queue,
+ atomic_read(&sc->send_io.pending.count) == 0 ||
+ sc->status != SMBDIRECT_SOCKET_CONNECTED);
+
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED && rc == 0)
+ rc = -EAGAIN;
return rc;
}
static void register_mr_done(struct ib_cq *cq, struct ib_wc *wc)
{
- struct smbd_mr *mr;
- struct ib_cqe *cqe;
+ struct smbdirect_mr_io *mr =
+ container_of(wc->wr_cqe, struct smbdirect_mr_io, cqe);
+ struct smbdirect_socket *sc = mr->socket;
if (wc->status) {
log_rdma_mr(ERR, "status=%d\n", wc->status);
- cqe = wc->wr_cqe;
- mr = container_of(cqe, struct smbd_mr, cqe);
- smbd_disconnect_rdma_connection(mr->conn);
+ smbd_disconnect_rdma_connection(sc);
}
}
@@ -2051,13 +2325,14 @@ static void register_mr_done(struct ib_cq *cq, struct ib_wc *wc)
*/
static void smbd_mr_recovery_work(struct work_struct *work)
{
- struct smbd_connection *info =
- container_of(work, struct smbd_connection, mr_recovery_work);
- struct smbd_mr *smbdirect_mr;
+ struct smbdirect_socket *sc =
+ container_of(work, struct smbdirect_socket, mr_io.recovery_work);
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smbdirect_mr_io *smbdirect_mr;
int rc;
- list_for_each_entry(smbdirect_mr, &info->mr_list, list) {
- if (smbdirect_mr->state == MR_ERROR) {
+ list_for_each_entry(smbdirect_mr, &sc->mr_io.all.list, list) {
+ if (smbdirect_mr->state == SMBDIRECT_MR_ERROR) {
/* recover this MR entry */
rc = ib_dereg_mr(smbdirect_mr->mr);
@@ -2065,25 +2340,25 @@ static void smbd_mr_recovery_work(struct work_struct *work)
log_rdma_mr(ERR,
"ib_dereg_mr failed rc=%x\n",
rc);
- smbd_disconnect_rdma_connection(info);
+ smbd_disconnect_rdma_connection(sc);
continue;
}
smbdirect_mr->mr = ib_alloc_mr(
- info->pd, info->mr_type,
- info->max_frmr_depth);
+ sc->ib.pd, sc->mr_io.type,
+ sp->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);
+ sc->mr_io.type,
+ sp->max_frmr_depth);
+ smbd_disconnect_rdma_connection(sc);
continue;
}
} else
/* This MR is being used, don't recover it */
continue;
- smbdirect_mr->state = MR_READY;
+ smbdirect_mr->state = SMBDIRECT_MR_READY;
/* smbdirect_mr->state is updated by this function
* and is read and updated by I/O issuing CPUs trying
@@ -2092,23 +2367,89 @@ static void smbd_mr_recovery_work(struct work_struct *work)
* value is updated before waking up any calls to
* get_mr() from the I/O issuing CPUs
*/
- if (atomic_inc_return(&info->mr_ready_count) == 1)
- wake_up_interruptible(&info->wait_mr);
+ if (atomic_inc_return(&sc->mr_io.ready.count) == 1)
+ wake_up(&sc->mr_io.ready.wait_queue);
}
}
-static void destroy_mr_list(struct smbd_connection *info)
+static void smbd_mr_disable_locked(struct smbdirect_mr_io *mr)
{
- struct smbd_mr *mr, *tmp;
+ struct smbdirect_socket *sc = mr->socket;
+
+ lockdep_assert_held(&mr->mutex);
+
+ if (mr->state == SMBDIRECT_MR_DISABLED)
+ return;
- cancel_work_sync(&info->mr_recovery_work);
- list_for_each_entry_safe(mr, tmp, &info->mr_list, list) {
- if (mr->state == MR_INVALIDATED)
- ib_dma_unmap_sg(info->id->device, mr->sgt.sgl,
- mr->sgt.nents, mr->dir);
+ if (mr->mr)
ib_dereg_mr(mr->mr);
- kfree(mr->sgt.sgl);
- kfree(mr);
+ if (mr->sgt.nents)
+ ib_dma_unmap_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir);
+ kfree(mr->sgt.sgl);
+
+ mr->mr = NULL;
+ mr->sgt.sgl = NULL;
+ mr->sgt.nents = 0;
+
+ mr->state = SMBDIRECT_MR_DISABLED;
+}
+
+static void smbd_mr_free_locked(struct kref *kref)
+{
+ struct smbdirect_mr_io *mr =
+ container_of(kref, struct smbdirect_mr_io, kref);
+
+ lockdep_assert_held(&mr->mutex);
+
+ /*
+ * smbd_mr_disable_locked() should already be called!
+ */
+ if (WARN_ON_ONCE(mr->state != SMBDIRECT_MR_DISABLED))
+ smbd_mr_disable_locked(mr);
+
+ mutex_unlock(&mr->mutex);
+ mutex_destroy(&mr->mutex);
+ kfree(mr);
+}
+
+static void destroy_mr_list(struct smbdirect_socket *sc)
+{
+ struct smbdirect_mr_io *mr, *tmp;
+ LIST_HEAD(all_list);
+ unsigned long flags;
+
+ disable_work_sync(&sc->mr_io.recovery_work);
+
+ spin_lock_irqsave(&sc->mr_io.all.lock, flags);
+ list_splice_tail_init(&sc->mr_io.all.list, &all_list);
+ spin_unlock_irqrestore(&sc->mr_io.all.lock, flags);
+
+ list_for_each_entry_safe(mr, tmp, &all_list, list) {
+ mutex_lock(&mr->mutex);
+
+ smbd_mr_disable_locked(mr);
+ list_del(&mr->list);
+ mr->socket = NULL;
+
+ /*
+ * No kref_put_mutex() as it's already locked.
+ *
+ * If smbd_mr_free_locked() is called
+ * and the mutex is unlocked and mr is gone,
+ * in that case kref_put() returned 1.
+ *
+ * If kref_put() returned 0 we know that
+ * smbd_mr_free_locked() didn't
+ * run. Not by us nor by anyone else, as we
+ * still hold the mutex, so we need to unlock.
+ *
+ * If the mr is still registered it will
+ * be dangling (detached from the connection
+ * waiting for smbd_deregister_mr() to be
+ * called in order to free the memory.
+ */
+ if (!kref_put(&mr->kref, smbd_mr_free_locked))
+ mutex_unlock(&mr->mutex);
}
}
@@ -2119,56 +2460,66 @@ static void destroy_mr_list(struct smbd_connection *info)
* Recovery is done in smbd_mr_recovery_work. The content of list entry changes
* as MRs are used and recovered for I/O, but the list links will not change
*/
-static int allocate_mr_list(struct smbd_connection *info)
+static int allocate_mr_list(struct smbdirect_socket *sc)
{
- int i;
- struct smbd_mr *smbdirect_mr, *tmp;
-
- INIT_LIST_HEAD(&info->mr_list);
- init_waitqueue_head(&info->wait_mr);
- spin_lock_init(&info->mr_list_lock);
- atomic_set(&info->mr_ready_count, 0);
- atomic_set(&info->mr_used_count, 0);
- init_waitqueue_head(&info->wait_for_mr_cleanup);
- INIT_WORK(&info->mr_recovery_work, smbd_mr_recovery_work);
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smbdirect_mr_io *mr;
+ int ret;
+ u32 i;
+
+ if (sp->responder_resources == 0) {
+ log_rdma_mr(ERR, "responder_resources negotiated as 0\n");
+ return -EINVAL;
+ }
+
/* Allocate more MRs (2x) than hardware responder_resources */
- for (i = 0; i < info->responder_resources * 2; i++) {
- smbdirect_mr = kzalloc(sizeof(*smbdirect_mr), GFP_KERNEL);
- if (!smbdirect_mr)
- goto cleanup_entries;
- smbdirect_mr->mr = ib_alloc_mr(info->pd, info->mr_type,
- info->max_frmr_depth);
- if (IS_ERR(smbdirect_mr->mr)) {
+ for (i = 0; i < sp->responder_resources * 2; i++) {
+ mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+ if (!mr) {
+ ret = -ENOMEM;
+ goto kzalloc_mr_failed;
+ }
+
+ kref_init(&mr->kref);
+ mutex_init(&mr->mutex);
+
+ mr->mr = ib_alloc_mr(sc->ib.pd,
+ sc->mr_io.type,
+ sp->max_frmr_depth);
+ if (IS_ERR(mr->mr)) {
+ ret = PTR_ERR(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);
- goto out;
+ sc->mr_io.type, sp->max_frmr_depth);
+ goto ib_alloc_mr_failed;
}
- smbdirect_mr->sgt.sgl = kcalloc(info->max_frmr_depth,
- sizeof(struct scatterlist),
- GFP_KERNEL);
- if (!smbdirect_mr->sgt.sgl) {
+
+ mr->sgt.sgl = kcalloc(sp->max_frmr_depth,
+ sizeof(struct scatterlist),
+ GFP_KERNEL);
+ if (!mr->sgt.sgl) {
+ ret = -ENOMEM;
log_rdma_mr(ERR, "failed to allocate sgl\n");
- ib_dereg_mr(smbdirect_mr->mr);
- goto out;
+ goto kcalloc_sgl_failed;
}
- smbdirect_mr->state = MR_READY;
- smbdirect_mr->conn = info;
+ mr->state = SMBDIRECT_MR_READY;
+ mr->socket = sc;
- list_add_tail(&smbdirect_mr->list, &info->mr_list);
- atomic_inc(&info->mr_ready_count);
+ list_add_tail(&mr->list, &sc->mr_io.all.list);
+ atomic_inc(&sc->mr_io.ready.count);
}
+
+ INIT_WORK(&sc->mr_io.recovery_work, smbd_mr_recovery_work);
+
return 0;
-out:
- kfree(smbdirect_mr);
-cleanup_entries:
- list_for_each_entry_safe(smbdirect_mr, tmp, &info->mr_list, list) {
- list_del(&smbdirect_mr->list);
- ib_dereg_mr(smbdirect_mr->mr);
- kfree(smbdirect_mr->sgt.sgl);
- kfree(smbdirect_mr);
- }
- return -ENOMEM;
+kcalloc_sgl_failed:
+ ib_dereg_mr(mr->mr);
+ib_alloc_mr_failed:
+ mutex_destroy(&mr->mutex);
+ kfree(mr);
+kzalloc_mr_failed:
+ destroy_mr_list(sc);
+ return ret;
}
/*
@@ -2179,37 +2530,38 @@ cleanup_entries:
* issuing I/O trying to get MR at the same time, mr_list_lock is used to
* protect this situation.
*/
-static struct smbd_mr *get_mr(struct smbd_connection *info)
+static struct smbdirect_mr_io *get_mr(struct smbdirect_socket *sc)
{
- struct smbd_mr *ret;
+ struct smbdirect_mr_io *ret;
+ unsigned long flags;
int rc;
again:
- rc = wait_event_interruptible(info->wait_mr,
- atomic_read(&info->mr_ready_count) ||
- info->transport_status != SMBD_CONNECTED);
+ rc = wait_event_interruptible(sc->mr_io.ready.wait_queue,
+ atomic_read(&sc->mr_io.ready.count) ||
+ sc->status != SMBDIRECT_SOCKET_CONNECTED);
if (rc) {
log_rdma_mr(ERR, "wait_event_interruptible rc=%x\n", rc);
return NULL;
}
- if (info->transport_status != SMBD_CONNECTED) {
- log_rdma_mr(ERR, "info->transport_status=%x\n",
- info->transport_status);
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
+ log_rdma_mr(ERR, "sc->status=%x\n", sc->status);
return NULL;
}
- spin_lock(&info->mr_list_lock);
- list_for_each_entry(ret, &info->mr_list, list) {
- if (ret->state == MR_READY) {
- ret->state = MR_REGISTERED;
- spin_unlock(&info->mr_list_lock);
- atomic_dec(&info->mr_ready_count);
- atomic_inc(&info->mr_used_count);
+ spin_lock_irqsave(&sc->mr_io.all.lock, flags);
+ list_for_each_entry(ret, &sc->mr_io.all.list, list) {
+ if (ret->state == SMBDIRECT_MR_READY) {
+ ret->state = SMBDIRECT_MR_REGISTERED;
+ kref_get(&ret->kref);
+ spin_unlock_irqrestore(&sc->mr_io.all.lock, flags);
+ atomic_dec(&sc->mr_io.ready.count);
+ atomic_inc(&sc->mr_io.used.count);
return ret;
}
}
- spin_unlock(&info->mr_list_lock);
+ spin_unlock_irqrestore(&sc->mr_io.all.lock, flags);
/*
* It is possible that we could fail to get MR because other processes may
* try to acquire a MR at the same time. If this is the case, retry it.
@@ -2220,8 +2572,7 @@ again:
/*
* Transcribe the pages from an iterator into an MR scatterlist.
*/
-static int smbd_iter_to_mr(struct smbd_connection *info,
- struct iov_iter *iter,
+static int smbd_iter_to_mr(struct iov_iter *iter,
struct sg_table *sgt,
unsigned int max_sg)
{
@@ -2243,66 +2594,65 @@ static int smbd_iter_to_mr(struct smbd_connection *info,
* need_invalidate: true if this MR needs to be locally invalidated after I/O
* return value: the MR registered, NULL if failed.
*/
-struct smbd_mr *smbd_register_mr(struct smbd_connection *info,
+struct smbdirect_mr_io *smbd_register_mr(struct smbd_connection *info,
struct iov_iter *iter,
bool writing, bool need_invalidate)
{
- struct smbd_mr *smbdirect_mr;
+ struct smbdirect_socket *sc = &info->socket;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smbdirect_mr_io *mr;
int rc, num_pages;
- enum dma_data_direction dir;
struct ib_reg_wr *reg_wr;
- num_pages = iov_iter_npages(iter, info->max_frmr_depth + 1);
- if (num_pages > info->max_frmr_depth) {
+ num_pages = iov_iter_npages(iter, sp->max_frmr_depth + 1);
+ if (num_pages > sp->max_frmr_depth) {
log_rdma_mr(ERR, "num_pages=%d max_frmr_depth=%d\n",
- num_pages, info->max_frmr_depth);
+ num_pages, sp->max_frmr_depth);
WARN_ON_ONCE(1);
return NULL;
}
- smbdirect_mr = get_mr(info);
- if (!smbdirect_mr) {
+ mr = get_mr(sc);
+ if (!mr) {
log_rdma_mr(ERR, "get_mr returning NULL\n");
return NULL;
}
- dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- smbdirect_mr->dir = dir;
- smbdirect_mr->need_invalidate = need_invalidate;
- smbdirect_mr->sgt.nents = 0;
- smbdirect_mr->sgt.orig_nents = 0;
+ mutex_lock(&mr->mutex);
+
+ mr->dir = writing ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ mr->need_invalidate = need_invalidate;
+ mr->sgt.nents = 0;
+ mr->sgt.orig_nents = 0;
log_rdma_mr(INFO, "num_pages=0x%x count=0x%zx depth=%u\n",
- num_pages, iov_iter_count(iter), info->max_frmr_depth);
- smbd_iter_to_mr(info, iter, &smbdirect_mr->sgt, info->max_frmr_depth);
+ num_pages, iov_iter_count(iter), sp->max_frmr_depth);
+ smbd_iter_to_mr(iter, &mr->sgt, sp->max_frmr_depth);
- rc = ib_dma_map_sg(info->id->device, smbdirect_mr->sgt.sgl,
- smbdirect_mr->sgt.nents, dir);
+ rc = ib_dma_map_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir);
if (!rc) {
log_rdma_mr(ERR, "ib_dma_map_sg num_pages=%x dir=%x rc=%x\n",
- num_pages, dir, rc);
+ num_pages, mr->dir, rc);
goto dma_map_error;
}
- rc = ib_map_mr_sg(smbdirect_mr->mr, smbdirect_mr->sgt.sgl,
- smbdirect_mr->sgt.nents, NULL, PAGE_SIZE);
- if (rc != smbdirect_mr->sgt.nents) {
+ rc = ib_map_mr_sg(mr->mr, mr->sgt.sgl, mr->sgt.nents, NULL, PAGE_SIZE);
+ if (rc != mr->sgt.nents) {
log_rdma_mr(ERR,
- "ib_map_mr_sg failed rc = %d nents = %x\n",
- rc, smbdirect_mr->sgt.nents);
+ "ib_map_mr_sg failed rc = %d nents = %x\n",
+ rc, mr->sgt.nents);
goto map_mr_error;
}
- ib_update_fast_reg_key(smbdirect_mr->mr,
- ib_inc_rkey(smbdirect_mr->mr->rkey));
- reg_wr = &smbdirect_mr->wr;
+ ib_update_fast_reg_key(mr->mr, ib_inc_rkey(mr->mr->rkey));
+ reg_wr = &mr->wr;
reg_wr->wr.opcode = IB_WR_REG_MR;
- smbdirect_mr->cqe.done = register_mr_done;
- reg_wr->wr.wr_cqe = &smbdirect_mr->cqe;
+ mr->cqe.done = register_mr_done;
+ reg_wr->wr.wr_cqe = &mr->cqe;
reg_wr->wr.num_sge = 0;
reg_wr->wr.send_flags = IB_SEND_SIGNALED;
- reg_wr->mr = smbdirect_mr->mr;
- reg_wr->key = smbdirect_mr->mr->rkey;
+ reg_wr->mr = mr->mr;
+ reg_wr->key = mr->mr->rkey;
reg_wr->access = writing ?
IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE :
IB_ACCESS_REMOTE_READ;
@@ -2312,39 +2662,66 @@ struct smbd_mr *smbd_register_mr(struct smbd_connection *info,
* on IB_WR_REG_MR. Hardware enforces a barrier and order of execution
* on the next ib_post_send when we actually send I/O to remote peer
*/
- rc = ib_post_send(info->id->qp, &reg_wr->wr, NULL);
- if (!rc)
- return smbdirect_mr;
+ rc = ib_post_send(sc->ib.qp, &reg_wr->wr, NULL);
+ if (!rc) {
+ /*
+ * get_mr() gave us a reference
+ * via kref_get(&mr->kref), we keep that and let
+ * the caller use smbd_deregister_mr()
+ * to remove it again.
+ */
+ mutex_unlock(&mr->mutex);
+ return mr;
+ }
log_rdma_mr(ERR, "ib_post_send failed rc=%x reg_wr->key=%x\n",
rc, reg_wr->key);
- /* If all failed, attempt to recover this MR by setting it MR_ERROR*/
+ /* If all failed, attempt to recover this MR by setting it SMBDIRECT_MR_ERROR*/
map_mr_error:
- ib_dma_unmap_sg(info->id->device, smbdirect_mr->sgt.sgl,
- smbdirect_mr->sgt.nents, smbdirect_mr->dir);
+ ib_dma_unmap_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir);
dma_map_error:
- smbdirect_mr->state = MR_ERROR;
- if (atomic_dec_and_test(&info->mr_used_count))
- wake_up(&info->wait_for_mr_cleanup);
+ mr->sgt.nents = 0;
+ mr->state = SMBDIRECT_MR_ERROR;
+ if (atomic_dec_and_test(&sc->mr_io.used.count))
+ wake_up(&sc->mr_io.cleanup.wait_queue);
- smbd_disconnect_rdma_connection(info);
+ smbd_disconnect_rdma_connection(sc);
+
+ /*
+ * get_mr() gave us a reference
+ * via kref_get(&mr->kref), we need to remove it again
+ * on error.
+ *
+ * No kref_put_mutex() as it's already locked.
+ *
+ * If smbd_mr_free_locked() is called
+ * and the mutex is unlocked and mr is gone,
+ * in that case kref_put() returned 1.
+ *
+ * If kref_put() returned 0 we know that
+ * smbd_mr_free_locked() didn't
+ * run. Not by us nor by anyone else, as we
+ * still hold the mutex, so we need to unlock.
+ */
+ if (!kref_put(&mr->kref, smbd_mr_free_locked))
+ mutex_unlock(&mr->mutex);
return NULL;
}
static void local_inv_done(struct ib_cq *cq, struct ib_wc *wc)
{
- struct smbd_mr *smbdirect_mr;
+ struct smbdirect_mr_io *smbdirect_mr;
struct ib_cqe *cqe;
cqe = wc->wr_cqe;
- smbdirect_mr = container_of(cqe, struct smbd_mr, cqe);
- smbdirect_mr->state = MR_INVALIDATED;
+ smbdirect_mr = container_of(cqe, struct smbdirect_mr_io, cqe);
+ smbdirect_mr->state = SMBDIRECT_MR_INVALIDATED;
if (wc->status != IB_WC_SUCCESS) {
log_rdma_mr(ERR, "invalidate failed status=%x\n", wc->status);
- smbdirect_mr->state = MR_ERROR;
+ smbdirect_mr->state = SMBDIRECT_MR_ERROR;
}
complete(&smbdirect_mr->invalidate_done);
}
@@ -2355,58 +2732,85 @@ static void local_inv_done(struct ib_cq *cq, struct ib_wc *wc)
* and we have to locally invalidate the buffer to prevent data is being
* modified by remote peer after upper layer consumes it
*/
-int smbd_deregister_mr(struct smbd_mr *smbdirect_mr)
+void smbd_deregister_mr(struct smbdirect_mr_io *mr)
{
- struct ib_send_wr *wr;
- struct smbd_connection *info = smbdirect_mr->conn;
- int rc = 0;
+ struct smbdirect_socket *sc = mr->socket;
+
+ mutex_lock(&mr->mutex);
+ if (mr->state == SMBDIRECT_MR_DISABLED)
+ goto put_kref;
+
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
+ smbd_mr_disable_locked(mr);
+ goto put_kref;
+ }
+
+ if (mr->need_invalidate) {
+ struct ib_send_wr *wr = &mr->inv_wr;
+ int rc;
- if (smbdirect_mr->need_invalidate) {
/* Need to finish local invalidation before returning */
- wr = &smbdirect_mr->inv_wr;
wr->opcode = IB_WR_LOCAL_INV;
- smbdirect_mr->cqe.done = local_inv_done;
- wr->wr_cqe = &smbdirect_mr->cqe;
+ mr->cqe.done = local_inv_done;
+ wr->wr_cqe = &mr->cqe;
wr->num_sge = 0;
- wr->ex.invalidate_rkey = smbdirect_mr->mr->rkey;
+ wr->ex.invalidate_rkey = mr->mr->rkey;
wr->send_flags = IB_SEND_SIGNALED;
- init_completion(&smbdirect_mr->invalidate_done);
- rc = ib_post_send(info->id->qp, wr, NULL);
+ init_completion(&mr->invalidate_done);
+ rc = ib_post_send(sc->ib.qp, wr, NULL);
if (rc) {
log_rdma_mr(ERR, "ib_post_send failed rc=%x\n", rc);
- smbd_disconnect_rdma_connection(info);
+ smbd_mr_disable_locked(mr);
+ smbd_disconnect_rdma_connection(sc);
goto done;
}
- wait_for_completion(&smbdirect_mr->invalidate_done);
- smbdirect_mr->need_invalidate = false;
+ wait_for_completion(&mr->invalidate_done);
+ mr->need_invalidate = false;
} else
/*
- * For remote invalidation, just set it to MR_INVALIDATED
+ * For remote invalidation, just set it to SMBDIRECT_MR_INVALIDATED
* and defer to mr_recovery_work to recover the MR for next use
*/
- smbdirect_mr->state = MR_INVALIDATED;
-
- if (smbdirect_mr->state == MR_INVALIDATED) {
- ib_dma_unmap_sg(
- info->id->device, smbdirect_mr->sgt.sgl,
- smbdirect_mr->sgt.nents,
- smbdirect_mr->dir);
- smbdirect_mr->state = MR_READY;
- if (atomic_inc_return(&info->mr_ready_count) == 1)
- wake_up_interruptible(&info->wait_mr);
+ mr->state = SMBDIRECT_MR_INVALIDATED;
+
+ if (mr->sgt.nents) {
+ ib_dma_unmap_sg(sc->ib.dev, mr->sgt.sgl, mr->sgt.nents, mr->dir);
+ mr->sgt.nents = 0;
+ }
+
+ if (mr->state == SMBDIRECT_MR_INVALIDATED) {
+ mr->state = SMBDIRECT_MR_READY;
+ if (atomic_inc_return(&sc->mr_io.ready.count) == 1)
+ wake_up(&sc->mr_io.ready.wait_queue);
} else
/*
* Schedule the work to do MR recovery for future I/Os MR
* recovery is slow and don't want it to block current I/O
*/
- queue_work(info->workqueue, &info->mr_recovery_work);
+ queue_work(sc->workqueue, &sc->mr_io.recovery_work);
done:
- if (atomic_dec_and_test(&info->mr_used_count))
- wake_up(&info->wait_for_mr_cleanup);
+ if (atomic_dec_and_test(&sc->mr_io.used.count))
+ wake_up(&sc->mr_io.cleanup.wait_queue);
- return rc;
+put_kref:
+ /*
+ * No kref_put_mutex() as it's already locked.
+ *
+ * If smbd_mr_free_locked() is called
+ * and the mutex is unlocked and mr is gone,
+ * in that case kref_put() returned 1.
+ *
+ * If kref_put() returned 0 we know that
+ * smbd_mr_free_locked() didn't
+ * run. Not by us nor by anyone else, as we
+ * still hold the mutex, so we need to unlock
+ * and keep the mr in SMBDIRECT_MR_READY or
+ * SMBDIRECT_MR_ERROR state.
+ */
+ if (!kref_put(&mr->kref, smbd_mr_free_locked))
+ mutex_unlock(&mr->mutex);
}
static bool smb_set_sge(struct smb_extract_to_rdma *rdma,
@@ -2552,13 +2956,14 @@ static ssize_t smb_extract_folioq_to_rdma(struct iov_iter *iter,
size_t fsize = folioq_folio_size(folioq, slot);
if (offset < fsize) {
- size_t part = umin(maxsize - ret, fsize - offset);
+ size_t part = umin(maxsize, fsize - offset);
if (!smb_set_sge(rdma, folio_page(folio, 0), offset, part))
return -EIO;
offset += part;
ret += part;
+ maxsize -= part;
}
if (offset >= fsize) {
@@ -2573,7 +2978,7 @@ static ssize_t smb_extract_folioq_to_rdma(struct iov_iter *iter,
slot = 0;
}
}
- } while (rdma->nr_sge < rdma->max_sge || maxsize > 0);
+ } while (rdma->nr_sge < rdma->max_sge && maxsize > 0);
iter->folioq = folioq;
iter->folioq_slot = slot;
diff --git a/fs/smb/client/smbdirect.h b/fs/smb/client/smbdirect.h
index c08e3665150d..577d37dbeb8a 100644
--- a/fs/smb/client/smbdirect.h
+++ b/fs/smb/client/smbdirect.h
@@ -15,6 +15,9 @@
#include <rdma/rdma_cm.h>
#include <linux/mempool.h>
+#include "../common/smbdirect/smbdirect.h"
+#include "../common/smbdirect/smbdirect_socket.h"
+
extern int rdma_readwrite_threshold;
extern int smbd_max_frmr_depth;
extern int smbd_keep_alive_interval;
@@ -24,22 +27,6 @@ extern int smbd_max_send_size;
extern int smbd_send_credit_target;
extern int smbd_receive_credit_max;
-enum keep_alive_status {
- KEEP_ALIVE_NONE,
- KEEP_ALIVE_PENDING,
- KEEP_ALIVE_SENT,
-};
-
-enum smbd_connection_status {
- SMBD_CREATED,
- SMBD_CONNECTING,
- SMBD_CONNECTED,
- SMBD_NEGOTIATE_FAILED,
- SMBD_DISCONNECTING,
- SMBD_DISCONNECTED,
- SMBD_DESTROYED
-};
-
/*
* The context for the SMBDirect transport
* Everything related to the transport is here. It has several logical parts
@@ -50,222 +37,15 @@ enum smbd_connection_status {
* 5. mempools for allocating packets
*/
struct smbd_connection {
- enum smbd_connection_status transport_status;
-
- /* RDMA related */
- struct rdma_cm_id *id;
- struct ib_qp_init_attr qp_attr;
- struct ib_pd *pd;
- struct ib_cq *send_cq, *recv_cq;
- struct ib_device_attr dev_attr;
- int ri_rc;
- struct completion ri_done;
- wait_queue_head_t conn_wait;
- wait_queue_head_t disconn_wait;
-
- struct completion negotiate_completion;
- bool negotiate_done;
-
- struct work_struct disconnect_work;
- struct work_struct post_send_credits_work;
-
- spinlock_t lock_new_credits_offered;
- int new_credits_offered;
-
- /* Connection parameters defined in [MS-SMBD] 3.1.1.1 */
- int receive_credit_max;
- int send_credit_target;
- int max_send_size;
- int max_fragmented_recv_size;
- int max_fragmented_send_size;
- int max_receive_size;
- int keep_alive_interval;
- int max_readwrite_size;
- enum keep_alive_status keep_alive_requested;
- int protocol;
- atomic_t send_credits;
- atomic_t receive_credits;
- int receive_credit_target;
- int fragment_reassembly_remaining;
-
- /* Memory registrations */
- /* Maximum number of RDMA read/write outstanding on this connection */
- int responder_resources;
- /* Maximum number of pages in a single RDMA write/read on this connection */
- int max_frmr_depth;
- /*
- * If payload is less than or equal to the threshold,
- * use RDMA send/recv to send upper layer I/O.
- * If payload is more than the threshold,
- * use RDMA read/write through memory registration for I/O.
- */
- int rdma_readwrite_threshold;
- enum ib_mr_type mr_type;
- struct list_head mr_list;
- spinlock_t mr_list_lock;
- /* The number of available MRs ready for memory registration */
- atomic_t mr_ready_count;
- atomic_t mr_used_count;
- wait_queue_head_t wait_mr;
- struct work_struct mr_recovery_work;
- /* Used by transport to wait until all MRs are returned */
- wait_queue_head_t wait_for_mr_cleanup;
-
- /* Activity accounting */
- atomic_t send_pending;
- wait_queue_head_t wait_send_pending;
- wait_queue_head_t wait_post_send;
-
- /* Receive queue */
- struct list_head receive_queue;
- int count_receive_queue;
- spinlock_t receive_queue_lock;
-
- struct list_head empty_packet_queue;
- int count_empty_packet_queue;
- spinlock_t empty_packet_queue_lock;
-
- wait_queue_head_t wait_receive_queues;
-
- /* Reassembly queue */
- struct list_head reassembly_queue;
- spinlock_t reassembly_queue_lock;
- wait_queue_head_t wait_reassembly_queue;
-
- /* total data length of reassembly queue */
- int reassembly_data_length;
- int reassembly_queue_length;
- /* the offset to first buffer in reassembly queue */
- int first_entry_offset;
-
- bool send_immediate;
-
- wait_queue_head_t wait_send_queue;
-
- /*
- * Indicate if we have received a full packet on the connection
- * This is used to identify the first SMBD packet of a assembled
- * payload (SMB packet) in reassembly queue so we can return a
- * RFC1002 length to upper layer to indicate the length of the SMB
- * packet received
- */
- bool full_packet_received;
-
- struct workqueue_struct *workqueue;
- struct delayed_work idle_timer_work;
-
- /* Memory pool for preallocating buffers */
- /* request pool for RDMA send */
- struct kmem_cache *request_cache;
- mempool_t *request_mempool;
-
- /* response pool for RDMA receive */
- struct kmem_cache *response_cache;
- mempool_t *response_mempool;
-
- /* for debug purposes */
- unsigned int count_get_receive_buffer;
- unsigned int count_put_receive_buffer;
- unsigned int count_reassembly_queue;
- unsigned int count_enqueue_reassembly_queue;
- unsigned int count_dequeue_reassembly_queue;
- unsigned int count_send_empty;
-};
-
-enum smbd_message_type {
- SMBD_NEGOTIATE_RESP,
- SMBD_TRANSFER_DATA,
-};
-
-#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001
-
-/* SMBD negotiation request packet [MS-SMBD] 2.2.1 */
-struct smbd_negotiate_req {
- __le16 min_version;
- __le16 max_version;
- __le16 reserved;
- __le16 credits_requested;
- __le32 preferred_send_size;
- __le32 max_receive_size;
- __le32 max_fragmented_size;
-} __packed;
-
-/* SMBD negotiation response packet [MS-SMBD] 2.2.2 */
-struct smbd_negotiate_resp {
- __le16 min_version;
- __le16 max_version;
- __le16 negotiated_version;
- __le16 reserved;
- __le16 credits_requested;
- __le16 credits_granted;
- __le32 status;
- __le32 max_readwrite_size;
- __le32 preferred_send_size;
- __le32 max_receive_size;
- __le32 max_fragmented_size;
-} __packed;
-
-/* SMBD data transfer packet with payload [MS-SMBD] 2.2.3 */
-struct smbd_data_transfer {
- __le16 credits_requested;
- __le16 credits_granted;
- __le16 flags;
- __le16 reserved;
- __le32 remaining_data_length;
- __le32 data_offset;
- __le32 data_length;
- __le32 padding;
- __u8 buffer[];
-} __packed;
-
-/* The packet fields for a registered RDMA buffer */
-struct smbd_buffer_descriptor_v1 {
- __le64 offset;
- __le32 token;
- __le32 length;
-} __packed;
-
-/* Maximum number of SGEs used by smbdirect.c in any send work request */
-#define SMBDIRECT_MAX_SEND_SGE 6
-
-/* The context for a SMBD request */
-struct smbd_request {
- struct smbd_connection *info;
- struct ib_cqe cqe;
-
- /* the SGE entries for this work request */
- struct ib_sge sge[SMBDIRECT_MAX_SEND_SGE];
- int num_sge;
-
- /* SMBD packet header follows this structure */
- u8 packet[];
-};
-
-/* Maximum number of SGEs used by smbdirect.c in any receive work request */
-#define SMBDIRECT_MAX_RECV_SGE 1
-
-/* The context for a SMBD response */
-struct smbd_response {
- struct smbd_connection *info;
- struct ib_cqe cqe;
- struct ib_sge sge;
-
- enum smbd_message_type type;
-
- /* Link to receive queue or reassembly queue */
- struct list_head list;
-
- /* Indicate if this is the 1st packet of a payload */
- bool first_segment;
-
- /* SMBD packet header and payload follows this structure */
- u8 packet[];
+ struct smbdirect_socket socket;
};
/* Create a SMBDirect session */
struct smbd_connection *smbd_get_connection(
struct TCP_Server_Info *server, struct sockaddr *dstaddr);
+const struct smbdirect_socket_parameters *smbd_get_parameters(struct smbd_connection *conn);
+
/* Reconnect SMBDirect session */
int smbd_reconnect(struct TCP_Server_Info *server);
/* Destroy SMBDirect session */
@@ -276,34 +56,11 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg);
int smbd_send(struct TCP_Server_Info *server,
int num_rqst, struct smb_rqst *rqst);
-enum mr_state {
- MR_READY,
- MR_REGISTERED,
- MR_INVALIDATED,
- MR_ERROR
-};
-
-struct smbd_mr {
- struct smbd_connection *conn;
- struct list_head list;
- enum mr_state state;
- struct ib_mr *mr;
- struct sg_table sgt;
- enum dma_data_direction dir;
- union {
- struct ib_reg_wr wr;
- struct ib_send_wr inv_wr;
- };
- struct ib_cqe cqe;
- bool need_invalidate;
- struct completion invalidate_done;
-};
-
/* Interfaces to register and deregister MR for RDMA read/write */
-struct smbd_mr *smbd_register_mr(
+struct smbdirect_mr_io *smbd_register_mr(
struct smbd_connection *info, struct iov_iter *iter,
bool writing, bool need_invalidate);
-int smbd_deregister_mr(struct smbd_mr *mr);
+void smbd_deregister_mr(struct smbdirect_mr_io *mr);
#else
#define cifs_rdma_enabled(server) 0
diff --git a/fs/smb/client/trace.c b/fs/smb/client/trace.c
index 465483787193..8a99b68d0c71 100644
--- a/fs/smb/client/trace.c
+++ b/fs/smb/client/trace.c
@@ -4,5 +4,7 @@
*
* Author(s): Steve French <stfrench@microsoft.com>
*/
+#include "cifsglob.h"
+#include "cifs_spnego.h"
#define CREATE_TRACE_POINTS
#include "trace.h"
diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
index 0b52d22a91a0..b0fbc2df642e 100644
--- a/fs/smb/client/trace.h
+++ b/fs/smb/client/trace.h
@@ -20,6 +20,136 @@
/*
* Specify enums for tracing information.
*/
+#define smb_eio_traces \
+ EM(smb_eio_trace_compress_copy, "compress_copy") \
+ EM(smb_eio_trace_copychunk_inv_rsp, "copychunk_inv_rsp") \
+ EM(smb_eio_trace_copychunk_overcopy_b, "copychunk_overcopy_b") \
+ EM(smb_eio_trace_copychunk_overcopy_c, "copychunk_overcopy_c") \
+ EM(smb_eio_trace_create_rsp_too_small, "create_rsp_too_small") \
+ EM(smb_eio_trace_dfsref_no_rsp, "dfsref_no_rsp") \
+ EM(smb_eio_trace_ea_overrun, "ea_overrun") \
+ EM(smb_eio_trace_extract_will_pin, "extract_will_pin") \
+ EM(smb_eio_trace_forced_shutdown, "forced_shutdown") \
+ EM(smb_eio_trace_getacl_bcc_too_small, "getacl_bcc_too_small") \
+ EM(smb_eio_trace_getcifsacl_param_count, "getcifsacl_param_count") \
+ EM(smb_eio_trace_getdfsrefer_bcc_too_small, "getdfsrefer_bcc_too_small") \
+ EM(smb_eio_trace_getextattr_bcc_too_small, "getextattr_bcc_too_small") \
+ EM(smb_eio_trace_getextattr_inv_size, "getextattr_inv_size") \
+ EM(smb_eio_trace_getsrvinonum_bcc_too_small, "getsrvinonum_bcc_too_small") \
+ EM(smb_eio_trace_getsrvinonum_size, "getsrvinonum_size") \
+ EM(smb_eio_trace_ioctl_data_len, "ioctl_data_len") \
+ EM(smb_eio_trace_ioctl_no_rsp, "ioctl_no_rsp") \
+ EM(smb_eio_trace_ioctl_out_off, "ioctl_out_off") \
+ EM(smb_eio_trace_lock_bcc_too_small, "lock_bcc_too_small") \
+ EM(smb_eio_trace_lock_data_too_small, "lock_data_too_small") \
+ EM(smb_eio_trace_malformed_ksid_key, "malformed_ksid_key") \
+ EM(smb_eio_trace_malformed_sid_key, "malformed_sid_key") \
+ EM(smb_eio_trace_mkdir_no_rsp, "mkdir_no_rsp") \
+ EM(smb_eio_trace_neg_bad_rsplen, "neg_bad_rsplen") \
+ EM(smb_eio_trace_neg_decode_token, "neg_decode_token") \
+ EM(smb_eio_trace_neg_info_caps, "neg_info_caps") \
+ EM(smb_eio_trace_neg_info_dialect, "neg_info_dialect") \
+ EM(smb_eio_trace_neg_info_fail, "neg_info_fail") \
+ EM(smb_eio_trace_neg_info_sec_mode, "neg_info_sec_mode") \
+ EM(smb_eio_trace_neg_inval_dialect, "neg_inval_dialect") \
+ EM(smb_eio_trace_neg_no_crypt_key, "neg_no_crypt_key") \
+ EM(smb_eio_trace_neg_sec_blob_too_small, "neg_sec_blob_too_small") \
+ EM(smb_eio_trace_neg_unreq_dialect, "neg_unreq_dialect") \
+ EM(smb_eio_trace_no_auth_key, "no_auth_key") \
+ EM(smb_eio_trace_no_lease_key, "no_lease_key") \
+ EM(smb_eio_trace_not_netfs_writeback, "not_netfs_writeback") \
+ EM(smb_eio_trace_null_pointers, "null_pointers") \
+ EM(smb_eio_trace_oldqfsinfo_bcc_too_small, "oldqfsinfo_bcc_too_small") \
+ EM(smb_eio_trace_pend_del_fail, "pend_del_fail") \
+ EM(smb_eio_trace_qalleas_bcc_too_small, "qalleas_bcc_too_small") \
+ EM(smb_eio_trace_qalleas_ea_overlong, "qalleas_ea_overlong") \
+ EM(smb_eio_trace_qalleas_overlong, "qalleas_overlong") \
+ EM(smb_eio_trace_qfileinfo_bcc_too_small, "qfileinfo_bcc_too_small") \
+ EM(smb_eio_trace_qfileinfo_invalid, "qfileinfo_invalid") \
+ EM(smb_eio_trace_qfsattrinfo_bcc_too_small, "qfsattrinfo_bcc_too_small") \
+ EM(smb_eio_trace_qfsdevinfo_bcc_too_small, "qfsdevinfo_bcc_too_small") \
+ EM(smb_eio_trace_qfsinfo_bcc_too_small, "qfsinfo_bcc_too_small") \
+ EM(smb_eio_trace_qfsposixinfo_bcc_too_small, "qfsposixinfo_bcc_too_small") \
+ EM(smb_eio_trace_qfsunixinfo_bcc_too_small, "qfsunixinfo_bcc_too_small") \
+ EM(smb_eio_trace_qpathinfo_bcc_too_small, "qpathinfo_bcc_too_small") \
+ EM(smb_eio_trace_qpathinfo_invalid, "qpathinfo_invalid") \
+ EM(smb_eio_trace_qreparse_data_area, "qreparse_data_area") \
+ EM(smb_eio_trace_qreparse_rep_datalen, "qreparse_rep_datalen") \
+ EM(smb_eio_trace_qreparse_ret_datalen, "qreparse_ret_datalen") \
+ EM(smb_eio_trace_qreparse_setup_count, "qreparse_setup_count") \
+ EM(smb_eio_trace_qreparse_sizes_wrong, "qreparse_sizes_wrong") \
+ EM(smb_eio_trace_qsym_bcc_too_small, "qsym_bcc_too_small") \
+ EM(smb_eio_trace_read_mid_state_unknown, "read_mid_state_unknown") \
+ EM(smb_eio_trace_read_overlarge, "read_overlarge") \
+ EM(smb_eio_trace_read_rsp_malformed, "read_rsp_malformed") \
+ EM(smb_eio_trace_read_rsp_short, "read_rsp_short") \
+ EM(smb_eio_trace_read_too_far, "read_too_far") \
+ EM(smb_eio_trace_reparse_data_len, "reparse_data_len") \
+ EM(smb_eio_trace_reparse_native_len, "reparse_native_len") \
+ EM(smb_eio_trace_reparse_native_nul, "reparse_native_nul") \
+ EM(smb_eio_trace_reparse_native_sym_len, "reparse_native_sym_len") \
+ EM(smb_eio_trace_reparse_nfs_dev, "reparse_nfs_dev") \
+ EM(smb_eio_trace_reparse_nfs_nul, "reparse_nfs_nul") \
+ EM(smb_eio_trace_reparse_nfs_sockfifo, "reparse_nfs_sockfifo") \
+ EM(smb_eio_trace_reparse_nfs_symbuf, "reparse_nfs_symbuf") \
+ EM(smb_eio_trace_reparse_nfs_too_short, "reparse_nfs_too_short") \
+ EM(smb_eio_trace_reparse_overlong, "reparse_overlong") \
+ EM(smb_eio_trace_reparse_rdlen, "reparse_rdlen") \
+ EM(smb_eio_trace_reparse_wsl_nul, "reparse_wsl_nul") \
+ EM(smb_eio_trace_reparse_wsl_symbuf, "reparse_wsl_symbuf") \
+ EM(smb_eio_trace_reparse_wsl_ver, "reparse_wsl_ver") \
+ EM(smb_eio_trace_rx_b_read_short, "rx_b_read_short") \
+ EM(smb_eio_trace_rx_bad_datalen, "rx_bad_datalen") \
+ EM(smb_eio_trace_rx_both_buf, "rx_both_buf") \
+ EM(smb_eio_trace_rx_calc_len_too_big, "rx_calc_len_too_big") \
+ EM(smb_eio_trace_rx_check_rsp, "rx_check_rsp") \
+ EM(smb_eio_trace_rx_copy_to_iter, "rx_copy_to_iter") \
+ EM(smb_eio_trace_rx_insuff_res, "rx_insuff_res") \
+ EM(smb_eio_trace_rx_inv_bcc, "rx_inv_bcc") \
+ EM(smb_eio_trace_rx_mid_unready, "rx_mid_unready") \
+ EM(smb_eio_trace_rx_neg_sess_resp, "rx_neg_sess_resp") \
+ EM(smb_eio_trace_rx_overlong, "rx_overlong") \
+ EM(smb_eio_trace_rx_overpage, "rx_overpage") \
+ EM(smb_eio_trace_rx_pos_sess_resp, "rx_pos_sess_resp") \
+ EM(smb_eio_trace_rx_rfc1002_magic, "rx_rfc1002_magic") \
+ EM(smb_eio_trace_rx_sync_mid_invalid, "rx_sync_mid_invalid") \
+ EM(smb_eio_trace_rx_sync_mid_malformed, "rx_sync_mid_malformed") \
+ EM(smb_eio_trace_rx_too_short, "rx_too_short") \
+ EM(smb_eio_trace_rx_trans2_extract, "rx_trans2_extract") \
+ EM(smb_eio_trace_rx_unknown_resp, "rx_unknown_resp") \
+ EM(smb_eio_trace_rx_unspec_error, "rx_unspec_error") \
+ EM(smb_eio_trace_sess_buf_off, "sess_buf_off") \
+ EM(smb_eio_trace_sess_exiting, "sess_exiting") \
+ EM(smb_eio_trace_sess_krb_wcc, "sess_krb_wcc") \
+ EM(smb_eio_trace_sess_nl2_wcc, "sess_nl2_wcc") \
+ EM(smb_eio_trace_sess_rawnl_auth_wcc, "sess_rawnl_auth_wcc") \
+ EM(smb_eio_trace_sess_rawnl_neg_wcc, "sess_rawnl_neg_wcc") \
+ EM(smb_eio_trace_short_symlink_write, "short_symlink_write") \
+ EM(smb_eio_trace_sid_too_many_auth, "sid_too_many_auth") \
+ EM(smb_eio_trace_sig_data_too_small, "sig_data_too_small") \
+ EM(smb_eio_trace_sig_iter, "sig_iter") \
+ EM(smb_eio_trace_smb1_received_error, "smb1_received_error") \
+ EM(smb_eio_trace_smb2_received_error, "smb2_received_error") \
+ EM(smb_eio_trace_sym_slash, "sym_slash") \
+ EM(smb_eio_trace_sym_target_len, "sym_target_len") \
+ EM(smb_eio_trace_symlink_file_size, "symlink_file_size") \
+ EM(smb_eio_trace_tdis_in_reconnect, "tdis_in_reconnect") \
+ EM(smb_eio_trace_tx_chained_async, "tx_chained_async") \
+ EM(smb_eio_trace_tx_compress_failed, "tx_compress_failed") \
+ EM(smb_eio_trace_tx_copy_iter_to_buf, "tx_copy_iter_to_buf") \
+ EM(smb_eio_trace_tx_copy_to_buf, "tx_copy_to_buf") \
+ EM(smb_eio_trace_tx_max_compound, "tx_max_compound") \
+ EM(smb_eio_trace_tx_miscopy_to_buf, "tx_miscopy_to_buf") \
+ EM(smb_eio_trace_tx_need_transform, "tx_need_transform") \
+ EM(smb_eio_trace_tx_too_long, "sr_too_long") \
+ EM(smb_eio_trace_unixqfileinfo_bcc_too_small, "unixqfileinfo_bcc_too_small") \
+ EM(smb_eio_trace_unixqpathinfo_bcc_too_small, "unixqpathinfo_bcc_too_small") \
+ EM(smb_eio_trace_user_iter, "user_iter") \
+ EM(smb_eio_trace_write_bad_buf_type, "write_bad_buf_type") \
+ EM(smb_eio_trace_write_mid_state_unknown, "write_mid_state_unknown") \
+ EM(smb_eio_trace_write_rsp_malformed, "write_rsp_malformed") \
+ E_(smb_eio_trace_write_too_far, "write_too_far")
+
#define smb3_rw_credits_traces \
EM(cifs_trace_rw_credits_call_readv_adjust, "rd-call-adj") \
EM(cifs_trace_rw_credits_call_writev_adjust, "wr-call-adj") \
@@ -44,6 +174,8 @@
EM(netfs_trace_tcon_ref_free_ipc, "FRE Ipc ") \
EM(netfs_trace_tcon_ref_free_ipc_fail, "FRE Ipc-F ") \
EM(netfs_trace_tcon_ref_free_reconnect_server, "FRE Reconn") \
+ EM(netfs_trace_tcon_ref_get_cached_laundromat, "GET Ch-Lau") \
+ EM(netfs_trace_tcon_ref_get_cached_lease_break, "GET Ch-Lea") \
EM(netfs_trace_tcon_ref_get_cancelled_close, "GET Cn-Cls") \
EM(netfs_trace_tcon_ref_get_dfs_refer, "GET DfsRef") \
EM(netfs_trace_tcon_ref_get_find, "GET Find ") \
@@ -52,6 +184,7 @@
EM(netfs_trace_tcon_ref_new, "NEW ") \
EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \
EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \
+ EM(netfs_trace_tcon_ref_put_cached_close, "PUT Ch-Cls") \
EM(netfs_trace_tcon_ref_put_cancelled_close, "PUT Cn-Cls") \
EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \
EM(netfs_trace_tcon_ref_put_cancelled_mid, "PUT Cn-Mid") \
@@ -76,6 +209,7 @@
#define EM(a, b) a,
#define E_(a, b) a
+enum smb_eio_trace { smb_eio_traces } __mode(byte);
enum smb3_rw_credits_trace { smb3_rw_credits_traces } __mode(byte);
enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte);
@@ -89,6 +223,7 @@ enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte);
#define EM(a, b) TRACE_DEFINE_ENUM(a);
#define E_(a, b) TRACE_DEFINE_ENUM(a);
+smb_eio_traces;
smb3_rw_credits_traces;
smb3_tcon_ref_traces;
@@ -137,7 +272,7 @@ DECLARE_EVENT_CLASS(smb3_rw_err_class,
__entry->len = len;
__entry->rc = rc;
),
- TP_printk("\tR=%08x[%x] xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d",
+ TP_printk("R=%08x[%x] xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d",
__entry->rreq_debug_id, __entry->rreq_debug_index,
__entry->xid, __entry->sesid, __entry->tid, __entry->fid,
__entry->offset, __entry->len, __entry->rc)
@@ -187,7 +322,7 @@ DECLARE_EVENT_CLASS(smb3_other_err_class,
__entry->len = len;
__entry->rc = rc;
),
- TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d",
+ TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx offset=0x%llx len=0x%x rc=%d",
__entry->xid, __entry->sesid, __entry->tid, __entry->fid,
__entry->offset, __entry->len, __entry->rc)
)
@@ -244,7 +379,7 @@ DECLARE_EVENT_CLASS(smb3_copy_range_err_class,
__entry->len = len;
__entry->rc = rc;
),
- TP_printk("\txid=%u sid=0x%llx tid=0x%x source fid=0x%llx source offset=0x%llx target fid=0x%llx target offset=0x%llx len=0x%x rc=%d",
+ TP_printk("xid=%u sid=0x%llx tid=0x%x source fid=0x%llx source offset=0x%llx target fid=0x%llx target offset=0x%llx len=0x%x rc=%d",
__entry->xid, __entry->sesid, __entry->tid, __entry->target_fid,
__entry->src_offset, __entry->target_fid, __entry->target_offset, __entry->len, __entry->rc)
)
@@ -263,7 +398,7 @@ DEFINE_EVENT(smb3_copy_range_err_class, smb3_##name, \
TP_ARGS(xid, src_fid, target_fid, tid, sesid, src_offset, target_offset, len, rc))
DEFINE_SMB3_COPY_RANGE_ERR_EVENT(clone_err);
-/* TODO: Add SMB3_COPY_RANGE_ERR_EVENT(copychunk_err) */
+DEFINE_SMB3_COPY_RANGE_ERR_EVENT(copychunk_err);
DECLARE_EVENT_CLASS(smb3_copy_range_done_class,
TP_PROTO(unsigned int xid,
@@ -295,7 +430,7 @@ DECLARE_EVENT_CLASS(smb3_copy_range_done_class,
__entry->target_offset = target_offset;
__entry->len = len;
),
- TP_printk("\txid=%u sid=0x%llx tid=0x%x source fid=0x%llx source offset=0x%llx target fid=0x%llx target offset=0x%llx len=0x%x",
+ TP_printk("xid=%u sid=0x%llx tid=0x%x source fid=0x%llx source offset=0x%llx target fid=0x%llx target offset=0x%llx len=0x%x",
__entry->xid, __entry->sesid, __entry->tid, __entry->target_fid,
__entry->src_offset, __entry->target_fid, __entry->target_offset, __entry->len)
)
@@ -479,7 +614,7 @@ DECLARE_EVENT_CLASS(smb3_fd_class,
__entry->tid = tid;
__entry->sesid = sesid;
),
- TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx",
+ TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx",
__entry->xid, __entry->sesid, __entry->tid, __entry->fid)
)
@@ -518,7 +653,7 @@ DECLARE_EVENT_CLASS(smb3_fd_err_class,
__entry->sesid = sesid;
__entry->rc = rc;
),
- TP_printk("\txid=%u sid=0x%llx tid=0x%x fid=0x%llx rc=%d",
+ TP_printk("xid=%u sid=0x%llx tid=0x%x fid=0x%llx rc=%d",
__entry->xid, __entry->sesid, __entry->tid, __entry->fid,
__entry->rc)
)
@@ -666,12 +801,12 @@ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(query_info_compound_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(posix_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(unlink_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(set_reparse_compound_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(get_reparse_compound_enter);
-DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter);
+DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(query_wsl_ea_compound_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter);
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mknod_enter);
@@ -706,13 +841,12 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_info_compound_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(posix_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(unlink_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(set_reparse_compound_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(get_reparse_compound_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(query_wsl_ea_compound_done);
-DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done);
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mknod_done);
@@ -752,14 +886,13 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_info_compound_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(posix_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(unlink_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(set_reparse_compound_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(get_reparse_compound_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(query_wsl_ea_compound_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err);
-DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err);
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mknod_err);
@@ -790,7 +923,7 @@ DECLARE_EVENT_CLASS(smb3_cmd_err_class,
__entry->status = status;
__entry->rc = rc;
),
- TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu status=0x%x rc=%d",
+ TP_printk("sid=0x%llx tid=0x%x cmd=%u mid=%llu status=0x%x rc=%d",
__entry->sesid, __entry->tid, __entry->cmd, __entry->mid,
__entry->status, __entry->rc)
)
@@ -825,7 +958,7 @@ DECLARE_EVENT_CLASS(smb3_cmd_done_class,
__entry->cmd = cmd;
__entry->mid = mid;
),
- TP_printk("\tsid=0x%llx tid=0x%x cmd=%u mid=%llu",
+ TP_printk("sid=0x%llx tid=0x%x cmd=%u mid=%llu",
__entry->sesid, __entry->tid,
__entry->cmd, __entry->mid)
)
@@ -863,7 +996,7 @@ DECLARE_EVENT_CLASS(smb3_mid_class,
__entry->when_sent = when_sent;
__entry->when_received = when_received;
),
- TP_printk("\tcmd=%u mid=%llu pid=%u, when_sent=%lu when_rcv=%lu",
+ TP_printk("cmd=%u mid=%llu pid=%u, when_sent=%lu when_rcv=%lu",
__entry->cmd, __entry->mid, __entry->pid, __entry->when_sent,
__entry->when_received)
)
@@ -894,7 +1027,7 @@ DECLARE_EVENT_CLASS(smb3_exit_err_class,
__assign_str(func_name);
__entry->rc = rc;
),
- TP_printk("\t%s: xid=%u rc=%d",
+ TP_printk("%s: xid=%u rc=%d",
__get_str(func_name), __entry->xid, __entry->rc)
)
@@ -920,7 +1053,7 @@ DECLARE_EVENT_CLASS(smb3_sync_err_class,
__entry->ino = ino;
__entry->rc = rc;
),
- TP_printk("\tino=%lu rc=%d",
+ TP_printk("ino=%lu rc=%d",
__entry->ino, __entry->rc)
)
@@ -946,7 +1079,7 @@ DECLARE_EVENT_CLASS(smb3_enter_exit_class,
__entry->xid = xid;
__assign_str(func_name);
),
- TP_printk("\t%s: xid=%u",
+ TP_printk("%s: xid=%u",
__get_str(func_name), __entry->xid)
)
@@ -1167,8 +1300,54 @@ DEFINE_EVENT(smb3_lease_done_class, smb3_##name, \
__u64 lease_key_high), \
TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high))
-DEFINE_SMB3_LEASE_DONE_EVENT(lease_done);
-DEFINE_SMB3_LEASE_DONE_EVENT(lease_not_found);
+DEFINE_SMB3_LEASE_DONE_EVENT(lease_ack_done);
+/* Tracepoint when a lease break request is received/entered (includes epoch and flags) */
+DECLARE_EVENT_CLASS(smb3_lease_enter_class,
+ TP_PROTO(__u32 lease_state,
+ __u32 flags,
+ __u16 epoch,
+ __u32 tid,
+ __u64 sesid,
+ __u64 lease_key_low,
+ __u64 lease_key_high),
+ TP_ARGS(lease_state, flags, epoch, tid, sesid, lease_key_low, lease_key_high),
+ TP_STRUCT__entry(
+ __field(__u32, lease_state)
+ __field(__u32, flags)
+ __field(__u16, epoch)
+ __field(__u32, tid)
+ __field(__u64, sesid)
+ __field(__u64, lease_key_low)
+ __field(__u64, lease_key_high)
+ ),
+ TP_fast_assign(
+ __entry->lease_state = lease_state;
+ __entry->flags = flags;
+ __entry->epoch = epoch;
+ __entry->tid = tid;
+ __entry->sesid = sesid;
+ __entry->lease_key_low = lease_key_low;
+ __entry->lease_key_high = lease_key_high;
+ ),
+ TP_printk("sid=0x%llx tid=0x%x lease_key=0x%llx%llx lease_state=0x%x flags=0x%x epoch=%u",
+ __entry->sesid, __entry->tid, __entry->lease_key_high,
+ __entry->lease_key_low, __entry->lease_state, __entry->flags, __entry->epoch)
+)
+
+#define DEFINE_SMB3_LEASE_ENTER_EVENT(name) \
+DEFINE_EVENT(smb3_lease_enter_class, smb3_##name, \
+ TP_PROTO(__u32 lease_state, \
+ __u32 flags, \
+ __u16 epoch, \
+ __u32 tid, \
+ __u64 sesid, \
+ __u64 lease_key_low, \
+ __u64 lease_key_high), \
+ TP_ARGS(lease_state, flags, epoch, tid, sesid, lease_key_low, lease_key_high))
+
+DEFINE_SMB3_LEASE_ENTER_EVENT(lease_break_enter);
+/* Lease not found: reuse lease_enter payload (includes epoch and flags) */
+DEFINE_SMB3_LEASE_ENTER_EVENT(lease_not_found);
DECLARE_EVENT_CLASS(smb3_lease_err_class,
TP_PROTO(__u32 lease_state,
@@ -1209,7 +1388,7 @@ DEFINE_EVENT(smb3_lease_err_class, smb3_##name, \
int rc), \
TP_ARGS(lease_state, tid, sesid, lease_key_low, lease_key_high, rc))
-DEFINE_SMB3_LEASE_ERR_EVENT(lease_err);
+DEFINE_SMB3_LEASE_ERR_EVENT(lease_ack_err);
DECLARE_EVENT_CLASS(smb3_connect_class,
TP_PROTO(char *hostname,
@@ -1513,6 +1692,49 @@ DEFINE_SMB3_CREDIT_EVENT(waitff_credits);
DEFINE_SMB3_CREDIT_EVENT(overflow_credits);
DEFINE_SMB3_CREDIT_EVENT(set_credits);
+TRACE_EVENT(smb3_kerberos_auth,
+ TP_PROTO(struct TCP_Server_Info *server,
+ struct cifs_ses *ses,
+ int rc),
+ TP_ARGS(server, ses, rc),
+ TP_STRUCT__entry(
+ __field(pid_t, pid)
+ __field(uid_t, uid)
+ __field(uid_t, cruid)
+ __string(host, server->hostname)
+ __string(user, ses->user_name)
+ __array(__u8, addr, sizeof(struct sockaddr_storage))
+ __array(char, sec, sizeof("ntlmsspi"))
+ __array(char, upcall_target, sizeof("mount"))
+ __field(int, rc)
+ ),
+ TP_fast_assign(
+ __entry->pid = current->pid;
+ __entry->uid = from_kuid_munged(&init_user_ns, ses->linux_uid);
+ __entry->cruid = from_kuid_munged(&init_user_ns, ses->cred_uid);
+ __assign_str(host);
+ __assign_str(user);
+ memcpy(__entry->addr, &server->dstaddr, sizeof(__entry->addr));
+
+ if (server->sec_kerberos)
+ memcpy(__entry->sec, "krb5", sizeof("krb5"));
+ else if (server->sec_mskerberos)
+ memcpy(__entry->sec, "mskrb5", sizeof("mskrb5"));
+ else if (server->sec_iakerb)
+ memcpy(__entry->sec, "iakerb", sizeof("iakerb"));
+ else
+ memcpy(__entry->sec, "krb5", sizeof("krb5"));
+
+ if (ses->upcall_target == UPTARGET_MOUNT)
+ memcpy(__entry->upcall_target, "mount", sizeof("mount"));
+ else
+ memcpy(__entry->upcall_target, "app", sizeof("app"));
+ __entry->rc = rc;
+ ),
+ TP_printk("vers=%d host=%s ip=%pISpsfc sec=%s uid=%d cruid=%d user=%s pid=%d upcall_target=%s err=%d",
+ CIFS_SPNEGO_UPCALL_VERSION, __get_str(host), __entry->addr,
+ __entry->sec, __entry->uid, __entry->cruid, __get_str(user),
+ __entry->pid, __entry->upcall_target, __entry->rc))
TRACE_EVENT(smb3_tcon_ref,
TP_PROTO(unsigned int tcon_debug_id, int ref,
@@ -1569,6 +1791,23 @@ TRACE_EVENT(smb3_rw_credits,
__entry->server_credits, __entry->in_flight)
);
+TRACE_EVENT(smb3_eio,
+ TP_PROTO(enum smb_eio_trace trace, unsigned long info, unsigned long info2),
+ TP_ARGS(trace, info, info2),
+ TP_STRUCT__entry(
+ __field(enum smb_eio_trace, trace)
+ __field(unsigned long, info)
+ __field(unsigned long, info2)
+ ),
+ TP_fast_assign(
+ __entry->trace = trace;
+ __entry->info = info;
+ __entry->info2 = info2;
+ ),
+ TP_printk("%s info=%lx,%lx",
+ __print_symbolic(__entry->trace, smb_eio_traces),
+ __entry->info, __entry->info2)
+ );
#undef EM
#undef E_
diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c
index 0dc80959ce48..3b34c3f4da2d 100644
--- a/fs/smb/client/transport.c
+++ b/fs/smb/client/transport.c
@@ -22,6 +22,7 @@
#include <linux/mempool.h>
#include <linux/sched/signal.h>
#include <linux/task_io_accounting_ops.h>
+#include <linux/task_work.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
@@ -30,66 +31,24 @@
#include "smbdirect.h"
#include "compress.h"
-/* Max number of iovectors we can use off the stack when sending requests. */
-#define CIFS_MAX_IOV_SIZE 8
-
void
-cifs_wake_up_task(struct mid_q_entry *mid)
+cifs_wake_up_task(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
if (mid->mid_state == MID_RESPONSE_RECEIVED)
mid->mid_state = MID_RESPONSE_READY;
wake_up_process(mid->callback_data);
}
-static struct mid_q_entry *
-alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
+void __release_mid(struct TCP_Server_Info *server, struct mid_q_entry *midEntry)
{
- struct mid_q_entry *temp;
-
- if (server == NULL) {
- cifs_dbg(VFS, "%s: null TCP session\n", __func__);
- return NULL;
- }
-
- temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
- memset(temp, 0, sizeof(struct mid_q_entry));
- kref_init(&temp->refcount);
- temp->mid = get_mid(smb_buffer);
- temp->pid = current->pid;
- temp->command = cpu_to_le16(smb_buffer->Command);
- cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
- /* easier to use jiffies */
- /* when mid allocated can be before when sent */
- temp->when_alloc = jiffies;
- temp->server = server;
-
- /*
- * The default is for the mid to be synchronous, so the
- * default callback just wakes up the current task.
- */
- get_task_struct(current);
- temp->creator = current;
- temp->callback = cifs_wake_up_task;
- temp->callback_data = current;
-
- atomic_inc(&mid_count);
- temp->mid_state = MID_REQUEST_ALLOCATED;
- return temp;
-}
-
-void __release_mid(struct kref *refcount)
-{
- struct mid_q_entry *midEntry =
- container_of(refcount, struct mid_q_entry, refcount);
#ifdef CONFIG_CIFS_STATS2
- __le16 command = midEntry->server->vals->lock_cmd;
+ __le16 command = server->vals->lock_cmd;
__u16 smb_cmd = le16_to_cpu(midEntry->command);
unsigned long now;
unsigned long roundtrip_time;
#endif
- struct TCP_Server_Info *server = midEntry->server;
- if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) &&
+ if (midEntry->resp_buf && (midEntry->wait_cancelled) &&
(midEntry->mid_state == MID_RESPONSE_RECEIVED ||
midEntry->mid_state == MID_RESPONSE_READY) &&
server->ops->handle_cancelled_mid)
@@ -154,20 +113,21 @@ void __release_mid(struct kref *refcount)
#endif
put_task_struct(midEntry->creator);
- mempool_free(midEntry, cifs_mid_poolp);
+ mempool_free(midEntry, &cifs_mid_pool);
}
void
-delete_mid(struct mid_q_entry *mid)
+delete_mid(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
- spin_lock(&mid->server->mid_lock);
- if (!(mid->mid_flags & MID_DELETED)) {
+ spin_lock(&server->mid_queue_lock);
+
+ if (!mid->deleted_from_q) {
list_del_init(&mid->qhead);
- mid->mid_flags |= MID_DELETED;
+ mid->deleted_from_q = true;
}
- spin_unlock(&mid->server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
- release_mid(mid);
+ release_mid(server, mid);
}
/*
@@ -179,7 +139,7 @@ delete_mid(struct mid_q_entry *mid)
* Our basic "send data to server" function. Should be called with srv_mutex
* held. The caller is responsible for handling the results.
*/
-static int
+int
smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg,
size_t *sent)
{
@@ -212,9 +172,16 @@ smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg,
* send a packet. In most cases if we fail to send
* after the retries we will kill the socket and
* reconnect which may clear the network problem.
+ *
+ * Even if regular signals are masked, EINTR might be
+ * propagated from sk_stream_wait_memory() to here when
+ * TIF_NOTIFY_SIGNAL is used for task work. For example,
+ * certain io_uring completions will use that. Treat
+ * having EINTR with pending task work the same as EAGAIN
+ * to avoid unnecessary reconnects.
*/
rc = sock_sendmsg(ssocket, smb_msg);
- if (rc == -EAGAIN) {
+ if (rc == -EAGAIN || unlikely(rc == -EINTR && task_work_pending(current))) {
retries++;
if (retries >= 14 ||
(!server->noblocksnd && (retries > 2))) {
@@ -269,9 +236,8 @@ smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst)
return buflen;
}
-static int
-__smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
- struct smb_rqst *rqst)
+int __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
+ struct smb_rqst *rqst)
{
int rc;
struct kvec *iov;
@@ -321,8 +287,8 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
- /* Generate a rfc1002 marker for SMB2+ */
- if (!is_smb1(server)) {
+ /* Generate a rfc1002 marker */
+ {
struct kvec hiov = {
.iov_base = &rfc1002_marker,
.iov_len = 4
@@ -363,8 +329,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
break;
total_len += sent;
}
-
-}
+ }
unmask:
sigprocmask(SIG_SETMASK, &oldmask, NULL);
@@ -397,7 +362,7 @@ unmask:
* socket so the server throws away the partial SMB
*/
cifs_signal_cifsd_for_reconnect(server, false);
- trace_smb3_partial_send_reconnect(server->CurrentMid,
+ trace_smb3_partial_send_reconnect(server->current_mid,
server->conn_id, server->hostname);
}
smbd_done:
@@ -437,11 +402,11 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
return __smb_send_rqst(server, num_rqst, rqst);
if (WARN_ON_ONCE(num_rqst > MAX_COMPOUND - 1))
- return -EIO;
+ return smb_EIO1(smb_eio_trace_tx_max_compound, num_rqst);
if (!server->ops->init_transform_rq) {
cifs_server_dbg(VFS, "Encryption requested but transform callback is missing\n");
- return -EIO;
+ return smb_EIO(smb_eio_trace_tx_need_transform);
}
new_rqst[0].rq_iov = &iov;
@@ -456,22 +421,6 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
return rc;
}
-int
-smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
- unsigned int smb_buf_length)
-{
- struct kvec iov[2];
- struct smb_rqst rqst = { .rq_iov = iov,
- .rq_nvec = 2 };
-
- iov[0].iov_base = smb_buffer;
- iov[0].iov_len = 4;
- iov[1].iov_base = (char *)smb_buffer + 4;
- iov[1].iov_len = smb_buf_length;
-
- return __smb_send_rqst(server, 1, &rqst);
-}
-
static int
wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
const int timeout, const int flags,
@@ -509,7 +458,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
- trace_smb3_nblk_credits(server->CurrentMid,
+ trace_smb3_nblk_credits(server->current_mid,
server->conn_id, server->hostname, scredits, -1, in_flight);
cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
__func__, 1, scredits);
@@ -542,7 +491,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
- trace_smb3_credit_timeout(server->CurrentMid,
+ trace_smb3_credit_timeout(server->current_mid,
server->conn_id, server->hostname, scredits,
num_credits, in_flight);
cifs_server_dbg(VFS, "wait timed out after %d ms\n",
@@ -585,7 +534,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
spin_unlock(&server->req_lock);
trace_smb3_credit_timeout(
- server->CurrentMid,
+ server->current_mid,
server->conn_id, server->hostname,
scredits, num_credits, in_flight);
cifs_server_dbg(VFS, "wait timed out after %d ms\n",
@@ -615,7 +564,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
- trace_smb3_waitff_credits(server->CurrentMid,
+ trace_smb3_waitff_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
-(num_credits), in_flight);
cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
@@ -626,9 +575,8 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
return 0;
}
-static int
-wait_for_free_request(struct TCP_Server_Info *server, const int flags,
- unsigned int *instance)
+int wait_for_free_request(struct TCP_Server_Info *server, const int flags,
+ unsigned int *instance)
{
return wait_for_free_credits(server, 1, -1, flags,
instance);
@@ -666,7 +614,7 @@ wait_for_compound_request(struct TCP_Server_Info *server, int num,
*/
if (server->in_flight == 0) {
spin_unlock(&server->req_lock);
- trace_smb3_insufficient_credits(server->CurrentMid,
+ trace_smb3_insufficient_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
num, in_flight);
cifs_dbg(FYI, "%s: %d requests in flight, needed %d total=%d\n",
@@ -690,89 +638,32 @@ cifs_wait_mtu_credits(struct TCP_Server_Info *server, size_t size,
return 0;
}
-static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
- struct mid_q_entry **ppmidQ)
-{
- spin_lock(&ses->ses_lock);
- if (ses->ses_status == SES_NEW) {
- if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
- (in_buf->Command != SMB_COM_NEGOTIATE)) {
- spin_unlock(&ses->ses_lock);
- return -EAGAIN;
- }
- /* else ok - we are setting up session */
- }
-
- if (ses->ses_status == SES_EXITING) {
- /* check if SMB session is bad because we are setting it up */
- if (in_buf->Command != SMB_COM_LOGOFF_ANDX) {
- spin_unlock(&ses->ses_lock);
- return -EAGAIN;
- }
- /* else ok - we are shutting down session */
- }
- spin_unlock(&ses->ses_lock);
-
- *ppmidQ = alloc_mid(in_buf, ses->server);
- if (*ppmidQ == NULL)
- return -ENOMEM;
- spin_lock(&ses->server->mid_lock);
- list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q);
- spin_unlock(&ses->server->mid_lock);
- return 0;
-}
-
-static int
-wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
+int wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
+ unsigned int sleep_state = TASK_KILLABLE;
int error;
+ if (mid->sr_flags & CIFS_INTERRUPTIBLE_WAIT)
+ sleep_state = TASK_INTERRUPTIBLE;
+
error = wait_event_state(server->response_q,
- midQ->mid_state != MID_REQUEST_SUBMITTED &&
- midQ->mid_state != MID_RESPONSE_RECEIVED,
- (TASK_KILLABLE|TASK_FREEZABLE_UNSAFE));
+ mid->mid_state != MID_REQUEST_SUBMITTED &&
+ mid->mid_state != MID_RESPONSE_RECEIVED,
+ (sleep_state | TASK_FREEZABLE_UNSAFE));
if (error < 0)
return -ERESTARTSYS;
return 0;
}
-struct mid_q_entry *
-cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
-{
- int rc;
- struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
- struct mid_q_entry *mid;
-
- if (rqst->rq_iov[0].iov_len != 4 ||
- rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
- return ERR_PTR(-EIO);
-
- /* enable signing if server requires it */
- if (server->sign)
- hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
-
- mid = alloc_mid(hdr, server);
- if (mid == NULL)
- return ERR_PTR(-ENOMEM);
-
- rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
- if (rc) {
- release_mid(mid);
- return ERR_PTR(rc);
- }
-
- return mid;
-}
-
/*
* Send a SMB request and set the callback function in the mid to handle
* the result. Caller is responsible for dealing with timeouts.
*/
int
cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
- mid_receive_t *receive, mid_callback_t *callback,
- mid_handle_t *handle, void *cbdata, const int flags,
+ mid_receive_t receive, mid_callback_t callback,
+ mid_handle_t handle, void *cbdata, const int flags,
const struct cifs_credits *exist_credits)
{
int rc;
@@ -812,6 +703,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
return PTR_ERR(mid);
}
+ mid->sr_flags = flags;
mid->receive = receive;
mid->callback = callback;
mid->callback_data = cbdata;
@@ -819,9 +711,9 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
mid->mid_state = MID_REQUEST_SUBMITTED;
/* put it on the pending_mid_q */
- spin_lock(&server->mid_lock);
+ spin_lock(&server->mid_queue_lock);
list_add_tail(&mid->qhead, &server->pending_mid_q);
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
/*
* Need to store the time in mid before calling I/O. For call_async,
@@ -833,7 +725,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
if (rc < 0) {
revert_current_mid(server, mid->credits);
server->sequence_number -= 2;
- delete_mid(mid);
+ delete_mid(server, mid);
}
cifs_server_unlock(server);
@@ -845,139 +737,51 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
return rc;
}
-/*
- *
- * Send an SMB Request. No response info (other than return code)
- * needs to be parsed.
- *
- * flags indicate the type of request buffer and how long to wait
- * and whether to log NT STATUS code (error) before mapping it to POSIX error
- *
- */
-int
-SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
- char *in_buf, int flags)
-{
- int rc;
- struct kvec iov[1];
- struct kvec rsp_iov;
- int resp_buf_type;
-
- iov[0].iov_base = in_buf;
- iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
- flags |= CIFS_NO_RSP_BUF;
- rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
- cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
-
- return rc;
-}
-
-static int
-cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
+int cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
{
int rc = 0;
cifs_dbg(FYI, "%s: cmd=%d mid=%llu state=%d\n",
__func__, le16_to_cpu(mid->command), mid->mid, mid->mid_state);
- spin_lock(&server->mid_lock);
+ spin_lock(&server->mid_queue_lock);
switch (mid->mid_state) {
case MID_RESPONSE_READY:
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
return rc;
case MID_RETRY_NEEDED:
rc = -EAGAIN;
break;
case MID_RESPONSE_MALFORMED:
- rc = -EIO;
+ rc = smb_EIO(smb_eio_trace_rx_sync_mid_malformed);
break;
case MID_SHUTDOWN:
rc = -EHOSTDOWN;
break;
+ case MID_RC:
+ rc = mid->mid_rc;
+ break;
default:
- if (!(mid->mid_flags & MID_DELETED)) {
+ if (mid->deleted_from_q == false) {
list_del_init(&mid->qhead);
- mid->mid_flags |= MID_DELETED;
+ mid->deleted_from_q = true;
}
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n",
__func__, mid->mid, mid->mid_state);
- rc = -EIO;
+ rc = smb_EIO1(smb_eio_trace_rx_sync_mid_invalid, mid->mid_state);
goto sync_mid_done;
}
- spin_unlock(&server->mid_lock);
+ spin_unlock(&server->mid_queue_lock);
sync_mid_done:
- release_mid(mid);
+ release_mid(server, mid);
return rc;
}
-static inline int
-send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
- struct mid_q_entry *mid)
-{
- return server->ops->send_cancel ?
- server->ops->send_cancel(server, rqst, mid) : 0;
-}
-
-int
-cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
- bool log_error)
-{
- unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;
-
- dump_smb(mid->resp_buf, min_t(u32, 92, len));
-
- /* convert the length into a more usable form */
- if (server->sign) {
- struct kvec iov[2];
- int rc = 0;
- struct smb_rqst rqst = { .rq_iov = iov,
- .rq_nvec = 2 };
-
- iov[0].iov_base = mid->resp_buf;
- iov[0].iov_len = 4;
- iov[1].iov_base = (char *)mid->resp_buf + 4;
- iov[1].iov_len = len - 4;
- /* FIXME: add code to kill session */
- rc = cifs_verify_signature(&rqst, server,
- mid->sequence_number);
- if (rc)
- cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
- rc);
- }
-
- /* BB special case reconnect tid and uid here? */
- return map_and_check_smb_error(mid, log_error);
-}
-
-struct mid_q_entry *
-cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored,
- struct smb_rqst *rqst)
-{
- int rc;
- struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
- struct mid_q_entry *mid;
-
- if (rqst->rq_iov[0].iov_len != 4 ||
- rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
- return ERR_PTR(-EIO);
-
- rc = allocate_mid(ses, hdr, &mid);
- if (rc)
- return ERR_PTR(rc);
- rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number);
- if (rc) {
- delete_mid(mid);
- return ERR_PTR(rc);
- }
- return mid;
-}
-
static void
-cifs_compound_callback(struct mid_q_entry *mid)
+cifs_compound_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
- struct TCP_Server_Info *server = mid->server;
struct cifs_credits credits = {
.value = server->ops->get_credits(mid),
.instance = server->reconnect_instance,
@@ -990,17 +794,17 @@ cifs_compound_callback(struct mid_q_entry *mid)
}
static void
-cifs_compound_last_callback(struct mid_q_entry *mid)
+cifs_compound_last_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
- cifs_compound_callback(mid);
- cifs_wake_up_task(mid);
+ cifs_compound_callback(server, mid);
+ cifs_wake_up_task(server, mid);
}
static void
-cifs_cancelled_callback(struct mid_q_entry *mid)
+cifs_cancelled_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
- cifs_compound_callback(mid);
- release_mid(mid);
+ cifs_compound_callback(server, mid);
+ release_mid(server, mid);
}
/*
@@ -1015,18 +819,20 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
uint index = 0;
unsigned int min_in_flight = UINT_MAX, max_in_flight = 0;
struct TCP_Server_Info *server = NULL;
- int i;
+ int i, start, cur;
if (!ses)
return NULL;
spin_lock(&ses->chan_lock);
+ start = atomic_inc_return(&ses->chan_seq);
for (i = 0; i < ses->chan_count; i++) {
- server = ses->chans[i].server;
+ cur = (start + i) % ses->chan_count;
+ server = ses->chans[cur].server;
if (!server || server->terminate)
continue;
- if (CIFS_CHAN_NEEDS_RECONNECT(ses, i))
+ if (CIFS_CHAN_NEEDS_RECONNECT(ses, cur))
continue;
/*
@@ -1039,17 +845,15 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
*/
if (server->in_flight < min_in_flight) {
min_in_flight = server->in_flight;
- index = i;
+ index = cur;
}
if (server->in_flight > max_in_flight)
max_in_flight = server->in_flight;
}
/* if all channels are equally loaded, fall back to round-robin */
- if (min_in_flight == max_in_flight) {
- index = (uint)atomic_inc_return(&ses->chan_seq);
- index %= ses->chan_count;
- }
+ if (min_in_flight == max_in_flight)
+ index = (uint)start % ses->chan_count;
server = ses->chans[index].server;
spin_unlock(&ses->chan_lock);
@@ -1064,7 +868,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
int *resp_buf_type, struct kvec *resp_iov)
{
int i, j, optype, rc = 0;
- struct mid_q_entry *midQ[MAX_COMPOUND];
+ struct mid_q_entry *mid[MAX_COMPOUND];
bool cancelled_mid[MAX_COMPOUND] = {false};
struct cifs_credits credits[MAX_COMPOUND] = {
{ .value = 0, .instance = 0 }
@@ -1079,7 +883,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
if (!ses || !ses->server || !server) {
cifs_dbg(VFS, "Null session\n");
- return -EIO;
+ return smb_EIO(smb_eio_trace_null_pointers);
}
spin_lock(&server->srv_lock);
@@ -1130,35 +934,36 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
}
for (i = 0; i < num_rqst; i++) {
- midQ[i] = server->ops->setup_request(ses, server, &rqst[i]);
- if (IS_ERR(midQ[i])) {
+ mid[i] = server->ops->setup_request(ses, server, &rqst[i]);
+ if (IS_ERR(mid[i])) {
revert_current_mid(server, i);
for (j = 0; j < i; j++)
- delete_mid(midQ[j]);
+ delete_mid(server, mid[j]);
cifs_server_unlock(server);
/* Update # of requests on wire to server */
for (j = 0; j < num_rqst; j++)
add_credits(server, &credits[j], optype);
- return PTR_ERR(midQ[i]);
+ return PTR_ERR(mid[i]);
}
- midQ[i]->mid_state = MID_REQUEST_SUBMITTED;
- midQ[i]->optype = optype;
+ mid[i]->sr_flags = flags;
+ mid[i]->mid_state = MID_REQUEST_SUBMITTED;
+ mid[i]->optype = optype;
/*
* Invoke callback for every part of the compound chain
* to calculate credits properly. Wake up this thread only when
* the last element is received.
*/
if (i < num_rqst - 1)
- midQ[i]->callback = cifs_compound_callback;
+ mid[i]->callback = cifs_compound_callback;
else
- midQ[i]->callback = cifs_compound_last_callback;
+ mid[i]->callback = cifs_compound_last_callback;
}
rc = smb_send_rqst(server, num_rqst, rqst, flags);
for (i = 0; i < num_rqst; i++)
- cifs_save_when_sent(midQ[i]);
+ cifs_save_when_sent(mid[i]);
if (rc < 0) {
revert_current_mid(server, num_rqst);
@@ -1192,6 +997,9 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) {
spin_unlock(&ses->ses_lock);
+ if (WARN_ON_ONCE(num_rqst != 1 || !resp_iov))
+ return -EINVAL;
+
cifs_server_lock(server);
smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec);
cifs_server_unlock(server);
@@ -1201,24 +1009,24 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
spin_unlock(&ses->ses_lock);
for (i = 0; i < num_rqst; i++) {
- rc = wait_for_response(server, midQ[i]);
+ rc = wait_for_response(server, mid[i]);
if (rc != 0)
break;
}
if (rc != 0) {
for (; i < num_rqst; i++) {
cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n",
- midQ[i]->mid, le16_to_cpu(midQ[i]->command));
- send_cancel(server, &rqst[i], midQ[i]);
- spin_lock(&server->mid_lock);
- midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
- if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED ||
- midQ[i]->mid_state == MID_RESPONSE_RECEIVED) {
- midQ[i]->callback = cifs_cancelled_callback;
+ mid[i]->mid, le16_to_cpu(mid[i]->command));
+ send_cancel(ses, server, &rqst[i], mid[i], xid);
+ spin_lock(&mid[i]->mid_lock);
+ mid[i]->wait_cancelled = true;
+ if (mid[i]->mid_state == MID_REQUEST_SUBMITTED ||
+ mid[i]->mid_state == MID_RESPONSE_RECEIVED) {
+ mid[i]->callback = cifs_cancelled_callback;
cancelled_mid[i] = true;
credits[i].value = 0;
}
- spin_unlock(&server->mid_lock);
+ spin_unlock(&mid[i]->mid_lock);
}
}
@@ -1226,37 +1034,37 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
if (rc < 0)
goto out;
- rc = cifs_sync_mid_result(midQ[i], server);
+ rc = cifs_sync_mid_result(mid[i], server);
if (rc != 0) {
/* mark this mid as cancelled to not free it below */
cancelled_mid[i] = true;
goto out;
}
- if (!midQ[i]->resp_buf ||
- midQ[i]->mid_state != MID_RESPONSE_READY) {
- rc = -EIO;
+ if (!mid[i]->resp_buf ||
+ mid[i]->mid_state != MID_RESPONSE_READY) {
+ rc = smb_EIO1(smb_eio_trace_rx_mid_unready, mid[i]->mid_state);
cifs_dbg(FYI, "Bad MID state?\n");
goto out;
}
- buf = (char *)midQ[i]->resp_buf;
- resp_iov[i].iov_base = buf;
- resp_iov[i].iov_len = midQ[i]->resp_buf_size +
- HEADER_PREAMBLE_SIZE(server);
+ rc = server->ops->check_receive(mid[i], server,
+ flags & CIFS_LOG_ERROR);
- if (midQ[i]->large_buf)
- resp_buf_type[i] = CIFS_LARGE_BUFFER;
- else
- resp_buf_type[i] = CIFS_SMALL_BUFFER;
+ if (resp_iov) {
+ buf = (char *)mid[i]->resp_buf;
+ resp_iov[i].iov_base = buf;
+ resp_iov[i].iov_len = mid[i]->resp_buf_size;
- rc = server->ops->check_receive(midQ[i], server,
- flags & CIFS_LOG_ERROR);
-
- /* mark it so buf will not be freed by delete_mid */
- if ((flags & CIFS_NO_RSP_BUF) == 0)
- midQ[i]->resp_buf = NULL;
+ if (mid[i]->large_buf)
+ resp_buf_type[i] = CIFS_LARGE_BUFFER;
+ else
+ resp_buf_type[i] = CIFS_SMALL_BUFFER;
+ /* mark it so buf will not be freed by delete_mid */
+ if ((flags & CIFS_NO_RSP_BUF) == 0)
+ mid[i]->resp_buf = NULL;
+ }
}
/*
@@ -1285,7 +1093,7 @@ out:
*/
for (i = 0; i < num_rqst; i++) {
if (!cancelled_mid[i])
- delete_mid(midQ[i]);
+ delete_mid(server, mid[i]);
}
return rc;
@@ -1301,344 +1109,6 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
rqst, resp_buf_type, resp_iov);
}
-int
-SendReceive2(const unsigned int xid, struct cifs_ses *ses,
- struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
- const int flags, struct kvec *resp_iov)
-{
- struct smb_rqst rqst;
- struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov;
- int rc;
-
- if (n_vec + 1 > CIFS_MAX_IOV_SIZE) {
- new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec),
- GFP_KERNEL);
- if (!new_iov) {
- /* otherwise cifs_send_recv below sets resp_buf_type */
- *resp_buf_type = CIFS_NO_BUFFER;
- return -ENOMEM;
- }
- } else
- new_iov = s_iov;
-
- /* 1st iov is a RFC1001 length followed by the rest of the packet */
- memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec));
-
- new_iov[0].iov_base = new_iov[1].iov_base;
- new_iov[0].iov_len = 4;
- new_iov[1].iov_base += 4;
- new_iov[1].iov_len -= 4;
-
- memset(&rqst, 0, sizeof(struct smb_rqst));
- rqst.rq_iov = new_iov;
- rqst.rq_nvec = n_vec + 1;
-
- rc = cifs_send_recv(xid, ses, ses->server,
- &rqst, resp_buf_type, flags, resp_iov);
- if (n_vec + 1 > CIFS_MAX_IOV_SIZE)
- kfree(new_iov);
- return rc;
-}
-
-int
-SendReceive(const unsigned int xid, struct cifs_ses *ses,
- struct smb_hdr *in_buf, struct smb_hdr *out_buf,
- int *pbytes_returned, const int flags)
-{
- int rc = 0;
- struct mid_q_entry *midQ;
- unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
- struct kvec iov = { .iov_base = in_buf, .iov_len = len };
- struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
- struct cifs_credits credits = { .value = 1, .instance = 0 };
- struct TCP_Server_Info *server;
-
- if (ses == NULL) {
- cifs_dbg(VFS, "Null smb session\n");
- return -EIO;
- }
- server = ses->server;
- if (server == NULL) {
- cifs_dbg(VFS, "Null tcp session\n");
- return -EIO;
- }
-
- spin_lock(&server->srv_lock);
- if (server->tcpStatus == CifsExiting) {
- spin_unlock(&server->srv_lock);
- return -ENOENT;
- }
- spin_unlock(&server->srv_lock);
-
- /* Ensure that we do not send more than 50 overlapping requests
- to the same server. We may make this configurable later or
- use ses->maxReq */
-
- if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
- cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
- len);
- return -EIO;
- }
-
- rc = wait_for_free_request(server, flags, &credits.instance);
- if (rc)
- return rc;
-
- /* 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
- of smb data */
-
- cifs_server_lock(server);
-
- rc = allocate_mid(ses, in_buf, &midQ);
- if (rc) {
- cifs_server_unlock(server);
- /* Update # of requests on wire to server */
- add_credits(server, &credits, 0);
- return rc;
- }
-
- rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
- if (rc) {
- cifs_server_unlock(server);
- goto out;
- }
-
- midQ->mid_state = MID_REQUEST_SUBMITTED;
-
- rc = smb_send(server, in_buf, len);
- cifs_save_when_sent(midQ);
-
- if (rc < 0)
- server->sequence_number -= 2;
-
- cifs_server_unlock(server);
-
- if (rc < 0)
- goto out;
-
- rc = wait_for_response(server, midQ);
- if (rc != 0) {
- send_cancel(server, &rqst, midQ);
- spin_lock(&server->mid_lock);
- if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
- midQ->mid_state == MID_RESPONSE_RECEIVED) {
- /* no longer considered to be "in-flight" */
- midQ->callback = release_mid;
- spin_unlock(&server->mid_lock);
- add_credits(server, &credits, 0);
- return rc;
- }
- spin_unlock(&server->mid_lock);
- }
-
- rc = cifs_sync_mid_result(midQ, server);
- if (rc != 0) {
- add_credits(server, &credits, 0);
- return rc;
- }
-
- if (!midQ->resp_buf || !out_buf ||
- midQ->mid_state != MID_RESPONSE_READY) {
- rc = -EIO;
- cifs_server_dbg(VFS, "Bad MID state?\n");
- goto out;
- }
-
- *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
- memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
- rc = cifs_check_receive(midQ, server, 0);
-out:
- delete_mid(midQ);
- add_credits(server, &credits, 0);
-
- return rc;
-}
-
-/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
- blocking lock to return. */
-
-static int
-send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon,
- struct smb_hdr *in_buf,
- struct smb_hdr *out_buf)
-{
- int bytes_returned;
- struct cifs_ses *ses = tcon->ses;
- LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
-
- /* We just modify the current in_buf to change
- the type of lock from LOCKING_ANDX_SHARED_LOCK
- or LOCKING_ANDX_EXCLUSIVE_LOCK to
- LOCKING_ANDX_CANCEL_LOCK. */
-
- pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
- pSMB->Timeout = 0;
- pSMB->hdr.Mid = get_next_mid(ses->server);
-
- return SendReceive(xid, ses, in_buf, out_buf,
- &bytes_returned, 0);
-}
-
-int
-SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
- struct smb_hdr *in_buf, struct smb_hdr *out_buf,
- int *pbytes_returned)
-{
- int rc = 0;
- int rstart = 0;
- struct mid_q_entry *midQ;
- struct cifs_ses *ses;
- unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
- struct kvec iov = { .iov_base = in_buf, .iov_len = len };
- struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
- unsigned int instance;
- struct TCP_Server_Info *server;
-
- if (tcon == NULL || tcon->ses == NULL) {
- cifs_dbg(VFS, "Null smb session\n");
- return -EIO;
- }
- ses = tcon->ses;
- server = ses->server;
-
- if (server == NULL) {
- cifs_dbg(VFS, "Null tcp session\n");
- return -EIO;
- }
-
- spin_lock(&server->srv_lock);
- if (server->tcpStatus == CifsExiting) {
- spin_unlock(&server->srv_lock);
- return -ENOENT;
- }
- spin_unlock(&server->srv_lock);
-
- /* Ensure that we do not send more than 50 overlapping requests
- to the same server. We may make this configurable later or
- use ses->maxReq */
-
- if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
- cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
- len);
- return -EIO;
- }
-
- rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance);
- if (rc)
- return rc;
-
- /* 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
- of smb data */
-
- cifs_server_lock(server);
-
- rc = allocate_mid(ses, in_buf, &midQ);
- if (rc) {
- cifs_server_unlock(server);
- return rc;
- }
-
- rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
- if (rc) {
- delete_mid(midQ);
- cifs_server_unlock(server);
- return rc;
- }
-
- midQ->mid_state = MID_REQUEST_SUBMITTED;
- rc = smb_send(server, in_buf, len);
- cifs_save_when_sent(midQ);
-
- if (rc < 0)
- server->sequence_number -= 2;
-
- cifs_server_unlock(server);
-
- if (rc < 0) {
- delete_mid(midQ);
- return rc;
- }
-
- /* Wait for a reply - allow signals to interrupt. */
- rc = wait_event_interruptible(server->response_q,
- (!(midQ->mid_state == MID_REQUEST_SUBMITTED ||
- midQ->mid_state == MID_RESPONSE_RECEIVED)) ||
- ((server->tcpStatus != CifsGood) &&
- (server->tcpStatus != CifsNew)));
-
- /* Were we interrupted by a signal ? */
- spin_lock(&server->srv_lock);
- if ((rc == -ERESTARTSYS) &&
- (midQ->mid_state == MID_REQUEST_SUBMITTED ||
- midQ->mid_state == MID_RESPONSE_RECEIVED) &&
- ((server->tcpStatus == CifsGood) ||
- (server->tcpStatus == CifsNew))) {
- spin_unlock(&server->srv_lock);
-
- if (in_buf->Command == SMB_COM_TRANSACTION2) {
- /* POSIX lock. We send a NT_CANCEL SMB to cause the
- blocking lock to return. */
- rc = send_cancel(server, &rqst, midQ);
- if (rc) {
- delete_mid(midQ);
- return rc;
- }
- } else {
- /* Windows lock. We send a LOCKINGX_CANCEL_LOCK
- to cause the blocking lock to return. */
-
- rc = send_lock_cancel(xid, tcon, in_buf, out_buf);
-
- /* If we get -ENOLCK back the lock may have
- already been removed. Don't exit in this case. */
- if (rc && rc != -ENOLCK) {
- delete_mid(midQ);
- return rc;
- }
- }
-
- rc = wait_for_response(server, midQ);
- if (rc) {
- send_cancel(server, &rqst, midQ);
- spin_lock(&server->mid_lock);
- if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
- midQ->mid_state == MID_RESPONSE_RECEIVED) {
- /* no longer considered to be "in-flight" */
- midQ->callback = release_mid;
- spin_unlock(&server->mid_lock);
- return rc;
- }
- spin_unlock(&server->mid_lock);
- }
-
- /* We got the response - restart system call. */
- rstart = 1;
- spin_lock(&server->srv_lock);
- }
- spin_unlock(&server->srv_lock);
-
- rc = cifs_sync_mid_result(midQ, server);
- if (rc != 0)
- return rc;
-
- /* rcvd frame is ok */
- if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_READY) {
- rc = -EIO;
- cifs_tcon_dbg(VFS, "Bad MID state?\n");
- goto out;
- }
-
- *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
- memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
- rc = cifs_check_receive(midQ, server, 0);
-out:
- delete_mid(midQ);
- if (rstart && rc == -EACCES)
- return -ERESTARTSYS;
- return rc;
-}
/*
* Discard any remaining data in the current SMB. To do this, we borrow the
@@ -1648,8 +1118,7 @@ int
cifs_discard_remaining_data(struct TCP_Server_Info *server)
{
unsigned int rfclen = server->pdu_size;
- size_t remaining = rfclen + HEADER_PREAMBLE_SIZE(server) -
- server->total_read;
+ size_t remaining = rfclen - server->total_read;
while (remaining > 0) {
ssize_t length;
@@ -1673,7 +1142,7 @@ __cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid,
int length;
length = cifs_discard_remaining_data(server);
- dequeue_mid(mid, malformed);
+ dequeue_mid(server, mid, malformed);
mid->resp_buf = server->smallbuf;
server->smallbuf = NULL;
return length;
@@ -1694,7 +1163,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
unsigned int data_offset, data_len;
struct cifs_io_subrequest *rdata = mid->callback_data;
char *buf = server->smallbuf;
- unsigned int buflen = server->pdu_size + HEADER_PREAMBLE_SIZE(server);
+ unsigned int buflen = server->pdu_size;
bool use_rdma_mr = false;
cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%zu\n",
@@ -1728,14 +1197,9 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
/* set up first two iov for signature check and to get credits */
rdata->iov[0].iov_base = buf;
- rdata->iov[0].iov_len = HEADER_PREAMBLE_SIZE(server);
- rdata->iov[1].iov_base = buf + HEADER_PREAMBLE_SIZE(server);
- rdata->iov[1].iov_len =
- server->total_read - HEADER_PREAMBLE_SIZE(server);
+ rdata->iov[0].iov_len = server->total_read;
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
- cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
- rdata->iov[1].iov_base, rdata->iov[1].iov_len);
/* Was the SMB read successful? */
rdata->result = server->ops->map_error(buf, false);
@@ -1751,12 +1215,12 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
cifs_dbg(FYI, "%s: server returned short header. got=%u expected=%zu\n",
__func__, server->total_read,
server->vals->read_rsp_size);
- rdata->result = -EIO;
+ rdata->result = smb_EIO2(smb_eio_trace_read_rsp_short,
+ server->total_read, server->vals->read_rsp_size);
return cifs_readv_discard(server, mid);
}
- data_offset = server->ops->read_data_offset(buf) +
- HEADER_PREAMBLE_SIZE(server);
+ data_offset = server->ops->read_data_offset(buf);
if (data_offset < server->total_read) {
/*
* win2k8 sometimes sends an offset of 0 when the read
@@ -1770,7 +1234,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
/* data_offset is beyond the end of smallbuf */
cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n",
__func__, data_offset);
- rdata->result = -EIO;
+ rdata->result = smb_EIO1(smb_eio_trace_read_overlarge,
+ data_offset);
return cifs_readv_discard(server, mid);
}
@@ -1785,6 +1250,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
if (length < 0)
return length;
server->total_read += length;
+ rdata->iov[0].iov_len = server->total_read;
}
/* how much data is in the response? */
@@ -1794,7 +1260,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
data_len = server->ops->read_data_length(buf, use_rdma_mr);
if (!use_rdma_mr && (data_offset + data_len > buflen)) {
/* data_len is corrupt -- discard frame */
- rdata->result = -EIO;
+ rdata->result = smb_EIO2(smb_eio_trace_read_rsp_malformed,
+ data_offset + data_len, buflen);
return cifs_readv_discard(server, mid);
}
@@ -1816,7 +1283,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
if (server->total_read < buflen)
return cifs_readv_discard(server, mid);
- dequeue_mid(mid, false);
+ dequeue_mid(server, mid, false);
mid->resp_buf = server->smallbuf;
server->smallbuf = NULL;
return length;
diff --git a/fs/smb/client/xattr.c b/fs/smb/client/xattr.c
index 58a584f0b27e..6bc89c59164a 100644
--- a/fs/smb/client/xattr.c
+++ b/fs/smb/client/xattr.c
@@ -31,6 +31,8 @@
* secure, replaced by SMB2 (then even more highly secure SMB3) many years ago
*/
#define SMB3_XATTR_CIFS_ACL "system.smb3_acl" /* DACL only */
+#define SMB3_XATTR_CIFS_NTSD_SACL "system.smb3_ntsd_sacl" /* SACL only */
+#define SMB3_XATTR_CIFS_NTSD_OWNER "system.smb3_ntsd_owner" /* owner only */
#define SMB3_XATTR_CIFS_NTSD "system.smb3_ntsd" /* owner plus DACL */
#define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */
#define SMB3_XATTR_ATTRIB "smb3.dosattrib" /* full name: user.smb3.dosattrib */
@@ -38,6 +40,7 @@
/* BB need to add server (Samba e.g) support for security and trusted prefix */
enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT,
+ XATTR_CIFS_NTSD_SACL, XATTR_CIFS_NTSD_OWNER,
XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL };
static int cifs_attrib_set(unsigned int xid, struct cifs_tcon *pTcon,
@@ -160,6 +163,8 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
break;
case XATTR_CIFS_ACL:
+ case XATTR_CIFS_NTSD_SACL:
+ case XATTR_CIFS_NTSD_OWNER:
case XATTR_CIFS_NTSD:
case XATTR_CIFS_NTSD_FULL: {
struct smb_ntsd *pacl;
@@ -173,7 +178,6 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
memcpy(pacl, value, size);
if (pTcon->ses->server->ops->set_acl) {
int aclflags = 0;
- rc = 0;
switch (handler->flags) {
case XATTR_CIFS_NTSD_FULL:
@@ -187,6 +191,13 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
CIFS_ACL_GROUP |
CIFS_ACL_DACL);
break;
+ case XATTR_CIFS_NTSD_OWNER:
+ aclflags = (CIFS_ACL_OWNER |
+ CIFS_ACL_GROUP);
+ break;
+ case XATTR_CIFS_NTSD_SACL:
+ aclflags = CIFS_ACL_SACL;
+ break;
case XATTR_CIFS_ACL:
default:
aclflags = CIFS_ACL_DACL;
@@ -308,6 +319,8 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
break;
case XATTR_CIFS_ACL:
+ case XATTR_CIFS_NTSD_SACL:
+ case XATTR_CIFS_NTSD_OWNER:
case XATTR_CIFS_NTSD:
case XATTR_CIFS_NTSD_FULL: {
/*
@@ -320,10 +333,23 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
if (pTcon->ses->server->ops->get_acl == NULL)
goto out; /* rc already EOPNOTSUPP */
- if (handler->flags == XATTR_CIFS_NTSD_FULL) {
+ switch (handler->flags) {
+ case XATTR_CIFS_NTSD_FULL:
+ extra_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | SACL_SECINFO;
+ break;
+ case XATTR_CIFS_NTSD:
+ extra_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO;
+ break;
+ case XATTR_CIFS_NTSD_OWNER:
+ extra_info = OWNER_SECINFO | GROUP_SECINFO;
+ break;
+ case XATTR_CIFS_NTSD_SACL:
extra_info = SACL_SECINFO;
- } else {
- extra_info = 0;
+ break;
+ case XATTR_CIFS_ACL:
+ default:
+ extra_info = DACL_SECINFO;
+ break;
}
pacl = pTcon->ses->server->ops->get_acl(cifs_sb,
inode, full_path, &acllen, extra_info);
@@ -371,7 +397,7 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
void *page;
if (unlikely(cifs_forced_shutdown(cifs_sb)))
- return -EIO;
+ return smb_EIO(smb_eio_trace_forced_shutdown);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
return -EOPNOTSUPP;
@@ -441,6 +467,20 @@ static const struct xattr_handler smb3_acl_xattr_handler = {
.set = cifs_xattr_set,
};
+static const struct xattr_handler smb3_ntsd_sacl_xattr_handler = {
+ .name = SMB3_XATTR_CIFS_NTSD_SACL,
+ .flags = XATTR_CIFS_NTSD_SACL,
+ .get = cifs_xattr_get,
+ .set = cifs_xattr_set,
+};
+
+static const struct xattr_handler smb3_ntsd_owner_xattr_handler = {
+ .name = SMB3_XATTR_CIFS_NTSD_OWNER,
+ .flags = XATTR_CIFS_NTSD_OWNER,
+ .get = cifs_xattr_get,
+ .set = cifs_xattr_set,
+};
+
static const struct xattr_handler cifs_cifs_ntsd_xattr_handler = {
.name = CIFS_XATTR_CIFS_NTSD,
.flags = XATTR_CIFS_NTSD,
@@ -486,6 +526,8 @@ const struct xattr_handler * const cifs_xattr_handlers[] = {
&cifs_os2_xattr_handler,
&cifs_cifs_acl_xattr_handler,
&smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */
+ &smb3_ntsd_sacl_xattr_handler,
+ &smb3_ntsd_owner_xattr_handler,
&cifs_cifs_ntsd_xattr_handler,
&smb3_ntsd_xattr_handler, /* alias for above since avoiding "cifs" */
&cifs_cifs_ntsd_full_xattr_handler,
diff --git a/fs/smb/common/Makefile b/fs/smb/common/Makefile
index c66dbbc1469c..9e0730a385fb 100644
--- a/fs/smb/common/Makefile
+++ b/fs/smb/common/Makefile
@@ -3,5 +3,4 @@
# Makefile for Linux filesystem routines that are shared by client and server.
#
-obj-$(CONFIG_SMBFS) += cifs_arc4.o
obj-$(CONFIG_SMBFS) += cifs_md4.o
diff --git a/fs/smb/common/arc4.h b/fs/smb/common/arc4.h
deleted file mode 100644
index 12e71ec033a1..000000000000
--- a/fs/smb/common/arc4.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * Common values for ARC4 Cipher Algorithm
- */
-
-#ifndef _CRYPTO_ARC4_H
-#define _CRYPTO_ARC4_H
-
-#include <linux/types.h>
-
-#define ARC4_MIN_KEY_SIZE 1
-#define ARC4_MAX_KEY_SIZE 256
-#define ARC4_BLOCK_SIZE 1
-
-struct arc4_ctx {
- u32 S[256];
- u32 x, y;
-};
-
-int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len);
-void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len);
-
-#endif /* _CRYPTO_ARC4_H */
diff --git a/fs/smb/common/cifs_arc4.c b/fs/smb/common/cifs_arc4.c
deleted file mode 100644
index df360ca47826..000000000000
--- a/fs/smb/common/cifs_arc4.c
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Cryptographic API
- *
- * ARC4 Cipher Algorithm
- *
- * Jon Oberheide <jon@oberheide.org>
- */
-
-#include <linux/module.h>
-#include "arc4.h"
-
-MODULE_DESCRIPTION("ARC4 Cipher Algorithm");
-MODULE_LICENSE("GPL");
-
-int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len)
-{
- int i, j = 0, k = 0;
-
- ctx->x = 1;
- ctx->y = 0;
-
- for (i = 0; i < 256; i++)
- ctx->S[i] = i;
-
- for (i = 0; i < 256; i++) {
- u32 a = ctx->S[i];
-
- j = (j + in_key[k] + a) & 0xff;
- ctx->S[i] = ctx->S[j];
- ctx->S[j] = a;
- if (++k >= key_len)
- k = 0;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(cifs_arc4_setkey);
-
-void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len)
-{
- u32 *const S = ctx->S;
- u32 x, y, a, b;
- u32 ty, ta, tb;
-
- if (len == 0)
- return;
-
- x = ctx->x;
- y = ctx->y;
-
- a = S[x];
- y = (y + a) & 0xff;
- b = S[y];
-
- do {
- S[y] = a;
- a = (a + b) & 0xff;
- S[x] = b;
- x = (x + 1) & 0xff;
- ta = S[x];
- ty = (y + ta) & 0xff;
- tb = S[ty];
- *out++ = *in++ ^ S[a];
- if (--len == 0)
- break;
- y = ty;
- a = ta;
- b = tb;
- } while (true);
-
- ctx->x = x;
- ctx->y = y;
-}
-EXPORT_SYMBOL_GPL(cifs_arc4_crypt);
diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h
new file mode 100644
index 000000000000..35dbacdbb902
--- /dev/null
+++ b/fs/smb/common/fscc.h
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ *
+ * Copyright (c) International Business Machines Corp., 2009, 2013
+ * Etersoft, 2012
+ * 2018 Samsung Electronics Co., Ltd.
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Pavel Shilovsky (pshilovsky@samba.org) 2012
+ * Namjae Jeon (linkinjeon@kernel.org)
+ *
+ */
+#ifndef _COMMON_SMB_FSCC_H
+#define _COMMON_SMB_FSCC_H
+
+/* See MS-FSCC 2.4.8 */
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ __le32 EaSize; /* length of the xattrs */
+ __u8 ShortNameLength;
+ __u8 Reserved;
+ __u8 ShortName[24];
+ char FileName[];
+} __packed FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */
+
+/* See MS-FSCC 2.4.10 */
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ char FileName[];
+} __packed FILE_DIRECTORY_INFO; /* level 0x101 FF resp data */
+
+/* See MS-FSCC 2.4.14 */
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ __le32 EaSize; /* length of the xattrs */
+ char FileName[];
+} __packed FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */
+
+/* See MS-FSCC 2.4.24 */
+typedef struct {
+ __le32 NextEntryOffset;
+ __u32 FileIndex;
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 EndOfFile;
+ __le64 AllocationSize;
+ __le32 ExtFileAttributes;
+ __le32 FileNameLength;
+ __le32 EaSize; /* EA size */
+ __le32 Reserved;
+ __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
+ char FileName[];
+} __packed FILE_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */
+
+/* See MS-FSCC 2.4.34 */
+struct smb2_file_network_open_info {
+ struct_group_attr(network_open_info, __packed,
+ __le64 CreationTime;
+ __le64 LastAccessTime;
+ __le64 LastWriteTime;
+ __le64 ChangeTime;
+ __le64 AllocationSize;
+ __le64 EndOfFile;
+ __le32 Attributes;
+ );
+ __le32 Reserved;
+} __packed; /* level 34 Query also similar returned in close rsp and open rsp */
+
+/* See MS-FSCC 2.5.1 */
+#define MAX_FS_NAME_LEN 52
+typedef struct {
+ __le32 Attributes;
+ __le32 MaxPathNameComponentLength;
+ __le32 FileSystemNameLen;
+ __le16 FileSystemName[]; /* do not have to save this - get subset? */
+} __packed FILE_SYSTEM_ATTRIBUTE_INFO;
+
+/* List of FileSystemAttributes - see MS-FSCC 2.5.1 */
+#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */
+#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */
+#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000
+#define FILE_SUPPORTS_USN_JOURNAL 0x02000000
+#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000
+#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000
+#define FILE_SUPPORTS_HARD_LINKS 0x00400000
+#define FILE_SUPPORTS_TRANSACTIONS 0x00200000
+#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000
+#define FILE_READ_ONLY_VOLUME 0x00080000
+#define FILE_NAMED_STREAMS 0x00040000
+#define FILE_SUPPORTS_ENCRYPTION 0x00020000
+#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
+#define FILE_VOLUME_IS_COMPRESSED 0x00008000
+#define FILE_SUPPORTS_POSIX_UNLINK_RENAME 0x00000400
+#define FILE_RETURNS_CLEANUP_RESULT_INFO 0x00000200
+#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
+#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
+#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
+#define FILE_VOLUME_QUOTAS 0x00000020
+#define FILE_FILE_COMPRESSION 0x00000010
+#define FILE_PERSISTENT_ACLS 0x00000008
+#define FILE_UNICODE_ON_DISK 0x00000004
+#define FILE_CASE_PRESERVED_NAMES 0x00000002
+#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
+
+/* See MS-FSCC 2.5.8 */
+typedef struct {
+ __le64 TotalAllocationUnits;
+ __le64 AvailableAllocationUnits;
+ __le32 SectorsPerAllocationUnit;
+ __le32 BytesPerSector;
+} __packed FILE_SYSTEM_SIZE_INFO; /* size info, level 0x103 */
+
+/* See MS-FSCC 2.5.10 */
+typedef struct {
+ __le32 DeviceType;
+ __le32 DeviceCharacteristics;
+} __packed FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */
+
+/*
+ * See POSIX Extensions to MS-FSCC 2.3.2.1
+ * Link: https://gitlab.com/samba-team/smb3-posix-spec/-/blob/master/fscc_posix_extensions.md
+ */
+typedef struct {
+ /* For undefined recommended transfer size return -1 in that field */
+ __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */
+ __le32 BlockSize;
+ /* The next three fields are in terms of the block size.
+ * (above). If block size is unknown, 4096 would be a
+ * reasonable block size for a server to report.
+ * Note that returning the blocks/blocksavail removes need
+ * to make a second call (to QFSInfo level 0x103 to get this info.
+ * UserBlockAvail is typically less than or equal to BlocksAvail,
+ * if no distinction is made return the same value in each
+ */
+ __le64 TotalBlocks;
+ __le64 BlocksAvail; /* bfree */
+ __le64 UserBlocksAvail; /* bavail */
+ /* For undefined Node fields or FSID return -1 */
+ __le64 TotalFileNodes;
+ __le64 FreeFileNodes;
+ __le64 FileSysIdentifier; /* fsid */
+ /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */
+ /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */
+} __packed FILE_SYSTEM_POSIX_INFO;
+
+#endif /* _COMMON_SMB_FSCC_H */
diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
index 9f272cc8f566..945a8e0cf36c 100644
--- a/fs/smb/common/smb2pdu.h
+++ b/fs/smb/common/smb2pdu.h
@@ -95,6 +95,9 @@
*/
#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024)
+/* According to MS-SMB2 specification The minimum recommended value is 65536.*/
+#define CIFS_MIN_DEFAULT_IOSIZE (65536)
+
/*
* SMB2 Header Definition
*
@@ -1146,12 +1149,6 @@ struct smb2_server_client_notification {
#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005)
#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007)
-#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \
- | FILE_READ_ATTRIBUTES)
-#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
- | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
-#define FILE_EXEC_RIGHTS (FILE_EXECUTE)
-
/* CreateOptions Flags */
#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001)
/* same as #define CREATE_NOT_FILE_LE cpu_to_le32(0x00000001) */
@@ -1268,7 +1265,7 @@ struct create_posix {
} __packed;
/* See MS-SMB2 2.2.13.2.3 and MS-SMB2 2.2.13.2.4 */
-struct create_durable {
+typedef struct {
struct create_context_hdr ccontext;
__u8 Name[8];
union {
@@ -1278,7 +1275,7 @@ struct create_durable {
__u64 VolatileFileId;
} Fid;
} Data;
-} __packed;
+} __packed create_durable_req_t, create_durable_reconn_t;
/* See MS-SMB2 2.2.13.2.5 */
struct create_mxac_req {
@@ -1287,6 +1284,56 @@ struct create_mxac_req {
__le64 Timestamp;
} __packed;
+/*
+ * Flags
+ * See MS-SMB2 2.2.13.2.11
+ * MS-SMB2 2.2.13.2.12
+ * MS-SMB2 2.2.14.2.12
+ */
+#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
+
+/* See MS-SMB2 2.2.13.2.11 */
+struct durable_context_v2_req {
+ __le32 Timeout;
+ __le32 Flags; /* see SMB2_DHANDLE_FLAG_PERSISTENT */
+ __u64 Reserved;
+ __u8 CreateGuid[16];
+} __packed;
+
+struct create_durable_req_v2 {
+ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct durable_context_v2_req dcontext;
+} __packed;
+
+/* See MS-SMB2 2.2.13.2.12 */
+struct durable_reconnect_context_v2 {
+ struct {
+ __u64 PersistentFileId;
+ __u64 VolatileFileId;
+ } Fid;
+ __u8 CreateGuid[16];
+ __le32 Flags; /* see SMB2_DHANDLE_FLAG_PERSISTENT */
+} __packed;
+
+struct create_durable_handle_reconnect_v2 {
+ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct durable_reconnect_context_v2 dcontext;
+} __packed;
+
+/* See MS-SMB2 2.2.14.2.12 */
+struct durable_context_v2_rsp {
+ __le32 Timeout;
+ __le32 Flags; /* see SMB2_DHANDLE_FLAG_PERSISTENT */
+} __packed;
+
+struct create_durable_rsp_v2 {
+ struct create_context_hdr ccontext;
+ __u8 Name[8];
+ struct durable_context_v2_rsp dcontext;
+} __packed;
+
/* See MS-SMB2 2.2.14.2.5 */
struct create_mxac_rsp {
struct create_context_hdr ccontext;
@@ -1385,6 +1432,45 @@ struct smb2_ioctl_req {
__u8 Buffer[];
} __packed;
+/* See MS-SMB2 2.2.31.1.1 */
+struct srv_copychunk {
+ __le64 SourceOffset;
+ __le64 TargetOffset;
+ __le32 Length;
+ __le32 Reserved;
+} __packed;
+
+#define COPY_CHUNK_RES_KEY_SIZE 24
+
+/* See MS-SMB2 2.2.31.1 */
+/* this goes in the ioctl buffer when doing a copychunk request */
+struct copychunk_ioctl_req {
+ union {
+ char SourceKey[COPY_CHUNK_RES_KEY_SIZE];
+ __le64 SourceKeyU64[3];
+ };
+ __le32 ChunkCount;
+ __le32 Reserved;
+ struct srv_copychunk Chunks[] __counted_by_le(ChunkCount);
+} __packed;
+
+/* See MS-SMB2 2.2.32.1 */
+struct copychunk_ioctl_rsp {
+ __le32 ChunksWritten;
+ __le32 ChunkBytesWritten;
+ __le32 TotalBytesWritten;
+} __packed;
+
+/* See MS-SMB2 2.2.32.3 */
+struct resume_key_ioctl_rsp {
+ union {
+ char ResumeKey[COPY_CHUNK_RES_KEY_SIZE];
+ __u64 ResumeKeyU64[3];
+ };
+ __le32 ContextLength; /* MBZ */
+ char Context[]; /* ignored, Windows sets to 4 bytes of zero */
+} __packed;
+
struct smb2_ioctl_rsp {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 49 */
@@ -1401,6 +1487,41 @@ struct smb2_ioctl_rsp {
__u8 Buffer[];
} __packed;
+/* See MS-SMB2 2.2.32.5.1.1 */
+struct smb_sockaddr_in {
+ __be16 Port;
+ __be32 IPv4Address;
+ __u8 Reserved[8];
+} __packed;
+
+/* See MS-SMB2 2.2.32.5.1.2 */
+struct smb_sockaddr_in6 {
+ __be16 Port;
+ __be32 FlowInfo;
+ __u8 IPv6Address[16];
+ __be32 ScopeId;
+} __packed;
+
+/* See MS-SMB2 2.2.32.5 and MS-SMB2 2.2.32.5.1 */
+#define RSS_CAPABLE cpu_to_le32(0x00000001)
+#define RDMA_CAPABLE cpu_to_le32(0x00000002)
+#define INTERNETWORK cpu_to_le16(0x0002)
+#define INTERNETWORKV6 cpu_to_le16(0x0017)
+struct network_interface_info_ioctl_rsp {
+ __le32 Next; /* next interface. zero if this is last one */
+ __le32 IfIndex;
+ __le32 Capability; /* RSS or RDMA Capable */
+ __le32 Reserved;
+ __le64 LinkSpeed;
+ union {
+ char SockAddr_Storage[128];
+ struct {
+ __le16 Family;
+ __u8 Buffer[126];
+ };
+ };
+} __packed;
+
/* this goes in the ioctl buffer when doing FSCTL_SET_ZERO_DATA */
struct file_zero_data_information {
__le64 FileOffset;
@@ -1550,7 +1671,28 @@ struct reparse_symlink_data_buffer {
__u8 PathBuffer[]; /* Variable Length */
} __packed;
-/* See MS-FSCC 2.1.2.6 and cifspdu.h for struct reparse_posix_data */
+/* For IO_REPARSE_TAG_NFS - see MS-FSCC 2.1.2.6 */
+#define NFS_SPECFILE_LNK 0x00000000014B4E4C
+#define NFS_SPECFILE_CHR 0x0000000000524843
+#define NFS_SPECFILE_BLK 0x00000000004B4C42
+#define NFS_SPECFILE_FIFO 0x000000004F464946
+#define NFS_SPECFILE_SOCK 0x000000004B434F53
+struct reparse_nfs_data_buffer {
+ __le32 ReparseTag;
+ __le16 ReparseDataLength;
+ __u16 Reserved;
+ __le64 InodeType; /* NFS_SPECFILE_* */
+ __u8 DataBuffer[];
+} __packed;
+
+/* For IO_REPARSE_TAG_LX_SYMLINK - see MS-FSCC 2.1.2.7 */
+struct reparse_wsl_symlink_data_buffer {
+ __le32 ReparseTag;
+ __le16 ReparseDataLength;
+ __u16 Reserved;
+ __le32 Version; /* Always 2 */
+ __u8 Target[]; /* Variable Length UTF-8 string without nul-term */
+} __packed;
struct validate_negotiate_info_req {
__le32 Capabilities;
@@ -1686,23 +1828,33 @@ struct smb2_file_internal_info {
} __packed; /* level 6 Query */
struct smb2_file_rename_info { /* encoding of request for level 10 */
- __u8 ReplaceIfExists; /* 1 = replace existing target with new */
- /* 0 = fail if target already exists */
- __u8 Reserved[7];
- __u64 RootDirectory; /* MBZ for network operations (why says spec?) */
- __le32 FileNameLength;
+ /* New members MUST be added within the struct_group() macro below. */
+ __struct_group(smb2_file_rename_info_hdr, __hdr, __packed,
+ __u8 ReplaceIfExists; /* 1 = replace existing target with new */
+ /* 0 = fail if target already exists */
+ __u8 Reserved[7];
+ __u64 RootDirectory; /* MBZ for network operations (why says spec?) */
+ __le32 FileNameLength;
+ );
char FileName[]; /* New name to be assigned */
/* padding - overall struct size must be >= 24 so filename + pad >= 6 */
} __packed; /* level 10 Set */
+static_assert(offsetof(struct smb2_file_rename_info, FileName) == sizeof(struct smb2_file_rename_info_hdr),
+ "struct member likely outside of __struct_group()");
struct smb2_file_link_info { /* encoding of request for level 11 */
- __u8 ReplaceIfExists; /* 1 = replace existing link with new */
- /* 0 = fail if link already exists */
- __u8 Reserved[7];
- __u64 RootDirectory; /* MBZ for network operations (why says spec?) */
- __le32 FileNameLength;
+ /* New members MUST be added within the struct_group() macro below. */
+ __struct_group(smb2_file_link_info_hdr, __hdr, __packed,
+ __u8 ReplaceIfExists; /* 1 = replace existing link with new */
+ /* 0 = fail if link already exists */
+ __u8 Reserved[7];
+ __u64 RootDirectory; /* MBZ for network operations (why says spec?) */
+ __le32 FileNameLength;
+ );
char FileName[]; /* Name to be assigned to new link */
} __packed; /* level 11 Set */
+static_assert(offsetof(struct smb2_file_link_info, FileName) == sizeof(struct smb2_file_link_info_hdr),
+ "struct member likely outside of __struct_group()");
/*
* This level 18, although with struct with same name is different from cifs
@@ -1859,6 +2011,148 @@ struct smb2_lease_ack {
__le64 LeaseDuration;
} __packed;
+/*
+ * See MS-CIFS 2.2.3.1
+ * MS-SMB 2.2.3.1
+ */
+struct smb_hdr {
+ __u8 Protocol[4];
+ __u8 Command;
+ union {
+ struct {
+ __u8 ErrorClass;
+ __u8 Reserved;
+ __le16 Error;
+ } __packed DosError;
+ __le32 CifsError;
+ } __packed Status;
+ __u8 Flags;
+ __le16 Flags2; /* note: le */
+ __le16 PidHigh;
+ union {
+ struct {
+ __le32 SequenceNumber; /* le */
+ __u32 Reserved; /* zero */
+ } __packed Sequence;
+ __u8 SecuritySignature[8]; /* le */
+ } __packed Signature;
+ __u8 pad[2];
+ __u16 Tid;
+ __le16 Pid;
+ __u16 Uid;
+ __le16 Mid;
+ __u8 WordCount;
+} __packed;
+
#define OP_BREAK_STRUCT_SIZE_20 24
#define OP_BREAK_STRUCT_SIZE_21 36
+
+/*
+ * See MS-SMB2 2.2.13.1.1
+ * MS-SMB 2.2.1.4.1
+ * These are the file access permission bits defined in CIFS for the
+ * NTCreateAndX as well as the level 0x107
+ * TRANS2_QUERY_PATH_INFORMATION API. The level 0x107, SMB_QUERY_FILE_ALL_INFO
+ * responds with the AccessFlags.
+ * The AccessFlags specifies the access permissions a caller has to the
+ * file and can have any suitable combination of the following values:
+ */
+#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */
+ /* or directory child entries can */
+ /* be listed together with the */
+ /* associated child attributes */
+ /* (so the FILE_READ_ATTRIBUTES on */
+ /* the child entry is not needed) */
+#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */
+ /* or new file can be created in */
+ /* the directory */
+#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */
+ /* (for non-local files over SMB it */
+ /* is same as FILE_WRITE_DATA) */
+ /* or new subdirectory can be */
+ /* created in the directory */
+#define FILE_READ_EA 0x00000008 /* Extended attributes associated */
+ /* with the file can be read */
+#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */
+ /* with the file can be written */
+#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */
+ /* the file using system paging I/O */
+ /* for executing the file / script */
+ /* or right to traverse directory */
+ /* (but by default all users have */
+ /* directory bypass traverse */
+ /* privilege and do not need this */
+ /* permission on directories at all)*/
+#define FILE_DELETE_CHILD 0x00000040 /* Child entry can be deleted from */
+ /* the directory (so the DELETE on */
+ /* the child entry is not needed) */
+#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */
+ /* file or directory can be read */
+#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */
+ /* file or directory can be written */
+#define DELETE 0x00010000 /* The file or dir can be deleted */
+#define READ_CONTROL 0x00020000 /* The discretionary access control */
+ /* list and ownership associated */
+ /* with the file or dir can be read */
+#define WRITE_DAC 0x00040000 /* The discretionary access control */
+ /* list associated with the file or */
+ /* directory can be written */
+#define WRITE_OWNER 0x00080000 /* Ownership information associated */
+ /* with the file/dir can be written */
+#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */
+ /* synchronize with the completion */
+ /* of an input/output request */
+#define SYSTEM_SECURITY 0x01000000 /* The system access control list */
+ /* associated with the file or */
+ /* directory can be read or written */
+ /* (cannot be in DACL, can in SACL) */
+#define MAXIMUM_ALLOWED 0x02000000 /* Maximal subset of GENERIC_ALL */
+ /* permissions which can be granted */
+ /* (cannot be in DACL nor SACL) */
+#define GENERIC_ALL 0x10000000 /* Same as: GENERIC_EXECUTE | */
+ /* GENERIC_WRITE | */
+ /* GENERIC_READ | */
+ /* FILE_DELETE_CHILD | */
+ /* DELETE | */
+ /* WRITE_DAC | */
+ /* WRITE_OWNER */
+ /* So GENERIC_ALL contains all bits */
+ /* mentioned above except these two */
+ /* SYSTEM_SECURITY MAXIMUM_ALLOWED */
+#define GENERIC_EXECUTE 0x20000000 /* Same as: FILE_EXECUTE | */
+ /* FILE_READ_ATTRIBUTES | */
+ /* READ_CONTROL | */
+ /* SYNCHRONIZE */
+#define GENERIC_WRITE 0x40000000 /* Same as: FILE_WRITE_DATA | */
+ /* FILE_APPEND_DATA | */
+ /* FILE_WRITE_EA | */
+ /* FILE_WRITE_ATTRIBUTES | */
+ /* READ_CONTROL | */
+ /* SYNCHRONIZE */
+#define GENERIC_READ 0x80000000 /* Same as: FILE_READ_DATA | */
+ /* FILE_READ_EA | */
+ /* FILE_READ_ATTRIBUTES | */
+ /* READ_CONTROL | */
+ /* SYNCHRONIZE */
+
+/* Combinations of file access permission bits */
+#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES)
+#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
+ | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
+#define FILE_EXEC_RIGHTS (FILE_EXECUTE)
+#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \
+ | FILE_READ_ATTRIBUTES \
+ | FILE_WRITE_ATTRIBUTES \
+ | DELETE | READ_CONTROL | WRITE_DAC \
+ | WRITE_OWNER | SYNCHRONIZE)
+#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
+ | READ_CONTROL | SYNCHRONIZE)
+
+/* See MS-CIFS 2.2.4.52.1 */
+typedef struct smb_negotiate_req {
+ struct smb_hdr hdr; /* wct = 0 */
+ __le16 ByteCount;
+ unsigned char DialectsArray[];
+} __packed SMB_NEGOTIATE_REQ;
+
#endif /* _COMMON_SMB2PDU_H */
diff --git a/fs/smb/common/smb2status.h b/fs/smb/common/smb2status.h
index 14b4a5f04564..7d6b8ed304fc 100644
--- a/fs/smb/common/smb2status.h
+++ b/fs/smb/common/smb2status.h
@@ -631,6 +631,7 @@ struct ntstatus {
#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B)
#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C)
#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D)
+#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001A1)
#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201)
#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202)
#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203)
@@ -1773,5 +1774,5 @@ struct ntstatus {
#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005)
#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006)
#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007)
-#define STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000)
-#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001a1)
+/* See MS-SMB2 3.3.5.4 */
+#define STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000)
diff --git a/fs/smb/common/smbacl.h b/fs/smb/common/smbacl.h
index 6a60698fc6f0..70bba5ff7fc1 100644
--- a/fs/smb/common/smbacl.h
+++ b/fs/smb/common/smbacl.h
@@ -92,14 +92,14 @@ struct smb_ntsd {
__le32 gsidoffset;
__le32 sacloffset;
__le32 dacloffset;
-} __attribute__((packed));
+} __packed;
struct smb_sid {
__u8 revision; /* revision level */
__u8 num_subauth;
__u8 authority[NUM_AUTHS];
__le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */
-} __attribute__((packed));
+} __packed;
/* size of a struct smb_sid, sans sub_auth array */
#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS)
@@ -107,8 +107,9 @@ struct smb_sid {
struct smb_acl {
__le16 revision; /* revision level */
__le16 size;
- __le32 num_aces;
-} __attribute__((packed));
+ __le16 num_aces;
+ __le16 reserved;
+} __packed;
struct smb_ace {
__u8 type; /* see above and MS-DTYP 2.4.4.1 */
@@ -116,6 +117,6 @@ struct smb_ace {
__le16 size;
__le32 access_req;
struct smb_sid sid; /* ie UUID of user or group who gets these perms */
-} __attribute__((packed));
+} __packed;
#endif /* _COMMON_SMBACL_H */
diff --git a/fs/smb/common/smbdirect/smbdirect.h b/fs/smb/common/smbdirect/smbdirect.h
new file mode 100644
index 000000000000..05cc6a9d0ccd
--- /dev/null
+++ b/fs/smb/common/smbdirect/smbdirect.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2017, Microsoft Corporation.
+ * Copyright (C) 2018, LG Electronics.
+ */
+
+#ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__
+#define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__
+
+/* SMB-DIRECT buffer descriptor V1 structure [MS-SMBD] 2.2.3.1 */
+struct smbdirect_buffer_descriptor_v1 {
+ __le64 offset;
+ __le32 token;
+ __le32 length;
+} __packed;
+
+/*
+ * Connection parameters mostly from [MS-SMBD] 3.1.1.1
+ *
+ * These are setup and negotiated at the beginning of a
+ * connection and remain constant unless explicitly changed.
+ *
+ * Some values are important for the upper layer.
+ */
+struct smbdirect_socket_parameters {
+ __u32 resolve_addr_timeout_msec;
+ __u32 resolve_route_timeout_msec;
+ __u32 rdma_connect_timeout_msec;
+ __u32 negotiate_timeout_msec;
+ __u8 initiator_depth;
+ __u8 responder_resources;
+ __u16 recv_credit_max;
+ __u16 send_credit_target;
+ __u32 max_send_size;
+ __u32 max_fragmented_send_size;
+ __u32 max_recv_size;
+ __u32 max_fragmented_recv_size;
+ __u32 max_read_write_size;
+ __u32 max_frmr_depth;
+ __u32 keepalive_interval_msec;
+ __u32 keepalive_timeout_msec;
+} __packed;
+
+#endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ */
diff --git a/fs/smb/common/smbdirect/smbdirect_pdu.h b/fs/smb/common/smbdirect/smbdirect_pdu.h
new file mode 100644
index 000000000000..ae9fdb05ce23
--- /dev/null
+++ b/fs/smb/common/smbdirect/smbdirect_pdu.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2017 Stefan Metzmacher
+ */
+
+#ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_PDU_H__
+#define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_PDU_H__
+
+#define SMBDIRECT_V1 0x0100
+
+/* SMBD negotiation request packet [MS-SMBD] 2.2.1 */
+struct smbdirect_negotiate_req {
+ __le16 min_version;
+ __le16 max_version;
+ __le16 reserved;
+ __le16 credits_requested;
+ __le32 preferred_send_size;
+ __le32 max_receive_size;
+ __le32 max_fragmented_size;
+} __packed;
+
+/* SMBD negotiation response packet [MS-SMBD] 2.2.2 */
+struct smbdirect_negotiate_resp {
+ __le16 min_version;
+ __le16 max_version;
+ __le16 negotiated_version;
+ __le16 reserved;
+ __le16 credits_requested;
+ __le16 credits_granted;
+ __le32 status;
+ __le32 max_readwrite_size;
+ __le32 preferred_send_size;
+ __le32 max_receive_size;
+ __le32 max_fragmented_size;
+} __packed;
+
+#define SMBDIRECT_DATA_MIN_HDR_SIZE 0x14
+#define SMBDIRECT_DATA_OFFSET 0x18
+
+#define SMBDIRECT_FLAG_RESPONSE_REQUESTED 0x0001
+
+/* SMBD data transfer packet with payload [MS-SMBD] 2.2.3 */
+struct smbdirect_data_transfer {
+ __le16 credits_requested;
+ __le16 credits_granted;
+ __le16 flags;
+ __le16 reserved;
+ __le32 remaining_data_length;
+ __le32 data_offset;
+ __le32 data_length;
+ __le32 padding;
+ __u8 buffer[];
+} __packed;
+
+#endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_PDU_H__ */
diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/smbdirect_socket.h
new file mode 100644
index 000000000000..384b19177e1c
--- /dev/null
+++ b/fs/smb/common/smbdirect/smbdirect_socket.h
@@ -0,0 +1,547 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2025 Stefan Metzmacher
+ */
+
+#ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_SOCKET_H__
+#define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_SOCKET_H__
+
+#include <rdma/rw.h>
+
+enum smbdirect_socket_status {
+ SMBDIRECT_SOCKET_CREATED,
+ SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED,
+ SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING,
+ SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED,
+ SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED,
+ SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING,
+ SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED,
+ SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED,
+ SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING,
+ SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED,
+ SMBDIRECT_SOCKET_NEGOTIATE_NEEDED,
+ SMBDIRECT_SOCKET_NEGOTIATE_RUNNING,
+ SMBDIRECT_SOCKET_NEGOTIATE_FAILED,
+ SMBDIRECT_SOCKET_CONNECTED,
+ SMBDIRECT_SOCKET_ERROR,
+ SMBDIRECT_SOCKET_DISCONNECTING,
+ SMBDIRECT_SOCKET_DISCONNECTED,
+ SMBDIRECT_SOCKET_DESTROYED
+};
+
+static __always_inline
+const char *smbdirect_socket_status_string(enum smbdirect_socket_status status)
+{
+ switch (status) {
+ case SMBDIRECT_SOCKET_CREATED:
+ return "CREATED";
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED:
+ return "RESOLVE_ADDR_NEEDED";
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING:
+ return "RESOLVE_ADDR_RUNNING";
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
+ return "RESOLVE_ADDR_FAILED";
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED:
+ return "RESOLVE_ROUTE_NEEDED";
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING:
+ return "RESOLVE_ROUTE_RUNNING";
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED:
+ return "RESOLVE_ROUTE_FAILED";
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED:
+ return "RDMA_CONNECT_NEEDED";
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING:
+ return "RDMA_CONNECT_RUNNING";
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED:
+ return "RDMA_CONNECT_FAILED";
+ case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED:
+ return "NEGOTIATE_NEEDED";
+ case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING:
+ return "NEGOTIATE_RUNNING";
+ case SMBDIRECT_SOCKET_NEGOTIATE_FAILED:
+ return "NEGOTIATE_FAILED";
+ case SMBDIRECT_SOCKET_CONNECTED:
+ return "CONNECTED";
+ case SMBDIRECT_SOCKET_ERROR:
+ return "ERROR";
+ case SMBDIRECT_SOCKET_DISCONNECTING:
+ return "DISCONNECTING";
+ case SMBDIRECT_SOCKET_DISCONNECTED:
+ return "DISCONNECTED";
+ case SMBDIRECT_SOCKET_DESTROYED:
+ return "DESTROYED";
+ }
+
+ return "<unknown>";
+}
+
+/*
+ * This can be used with %1pe to print errors as strings or '0'
+ * And it avoids warnings like: warn: passing zero to 'ERR_PTR'
+ * from smatch -p=kernel --pedantic
+ */
+static __always_inline
+const void * __must_check SMBDIRECT_DEBUG_ERR_PTR(long error)
+{
+ if (error == 0)
+ return NULL;
+ return ERR_PTR(error);
+}
+
+enum smbdirect_keepalive_status {
+ SMBDIRECT_KEEPALIVE_NONE,
+ SMBDIRECT_KEEPALIVE_PENDING,
+ SMBDIRECT_KEEPALIVE_SENT
+};
+
+struct smbdirect_socket {
+ enum smbdirect_socket_status status;
+ wait_queue_head_t status_wait;
+ int first_error;
+
+ /*
+ * This points to the workqueue to
+ * be used for this socket.
+ * It can be per socket (on the client)
+ * or point to a global workqueue (on the server)
+ */
+ struct workqueue_struct *workqueue;
+
+ struct work_struct disconnect_work;
+
+ /* RDMA related */
+ struct {
+ struct rdma_cm_id *cm_id;
+ /*
+ * This is for iWarp MPA v1
+ */
+ bool legacy_iwarp;
+ } rdma;
+
+ /* IB verbs related */
+ struct {
+ struct ib_pd *pd;
+ struct ib_cq *send_cq;
+ struct ib_cq *recv_cq;
+
+ /*
+ * shortcuts for rdma.cm_id->{qp,device};
+ */
+ struct ib_qp *qp;
+ struct ib_device *dev;
+ } ib;
+
+ struct smbdirect_socket_parameters parameters;
+
+ /*
+ * The state for keepalive and timeout handling
+ */
+ struct {
+ enum smbdirect_keepalive_status keepalive;
+ struct work_struct immediate_work;
+ struct delayed_work timer_work;
+ } idle;
+
+ /*
+ * The state for posted send buffers
+ */
+ struct {
+ /*
+ * Memory pools for preallocating
+ * smbdirect_send_io buffers
+ */
+ struct {
+ struct kmem_cache *cache;
+ mempool_t *pool;
+ } mem;
+
+ /*
+ * The local credit state for ib_post_send()
+ */
+ struct {
+ atomic_t count;
+ wait_queue_head_t wait_queue;
+ } lcredits;
+
+ /*
+ * The remote credit state for the send side
+ */
+ struct {
+ atomic_t count;
+ wait_queue_head_t wait_queue;
+ } credits;
+
+ /*
+ * The state about posted/pending sends
+ */
+ struct {
+ atomic_t count;
+ /*
+ * woken when count is decremented
+ */
+ wait_queue_head_t dec_wait_queue;
+ /*
+ * woken when count reached zero
+ */
+ wait_queue_head_t zero_wait_queue;
+ } pending;
+ } send_io;
+
+ /*
+ * The state for posted receive buffers
+ */
+ struct {
+ /*
+ * The type of PDU we are expecting
+ */
+ enum {
+ SMBDIRECT_EXPECT_NEGOTIATE_REQ = 1,
+ SMBDIRECT_EXPECT_NEGOTIATE_REP = 2,
+ SMBDIRECT_EXPECT_DATA_TRANSFER = 3,
+ } expected;
+
+ /*
+ * Memory pools for preallocating
+ * smbdirect_recv_io buffers
+ */
+ struct {
+ struct kmem_cache *cache;
+ mempool_t *pool;
+ } mem;
+
+ /*
+ * The list of free smbdirect_recv_io
+ * structures
+ */
+ struct {
+ struct list_head list;
+ spinlock_t lock;
+ } free;
+
+ /*
+ * The state for posted recv_io messages
+ * and the refill work struct.
+ */
+ struct {
+ atomic_t count;
+ struct work_struct refill_work;
+ } posted;
+
+ /*
+ * The credit state for the recv side
+ */
+ struct {
+ u16 target;
+ atomic_t count;
+ } credits;
+
+ /*
+ * The list of arrived non-empty smbdirect_recv_io
+ * structures
+ *
+ * This represents the reassembly queue.
+ */
+ struct {
+ struct list_head list;
+ spinlock_t lock;
+ wait_queue_head_t wait_queue;
+ /* total data length of reassembly queue */
+ int data_length;
+ int queue_length;
+ /* the offset to first buffer in reassembly queue */
+ int first_entry_offset;
+ /*
+ * Indicate if we have received a full packet on the
+ * connection This is used to identify the first SMBD
+ * packet of a assembled payload (SMB packet) in
+ * reassembly queue so we can return a RFC1002 length to
+ * upper layer to indicate the length of the SMB packet
+ * received
+ */
+ bool full_packet_received;
+ } reassembly;
+ } recv_io;
+
+ /*
+ * The state for Memory registrations on the client
+ */
+ struct {
+ enum ib_mr_type type;
+
+ /*
+ * The list of free smbdirect_mr_io
+ * structures
+ */
+ struct {
+ struct list_head list;
+ spinlock_t lock;
+ } all;
+
+ /*
+ * The number of available MRs ready for memory registration
+ */
+ struct {
+ atomic_t count;
+ wait_queue_head_t wait_queue;
+ } ready;
+
+ /*
+ * The number of used MRs
+ */
+ struct {
+ atomic_t count;
+ } used;
+
+ struct work_struct recovery_work;
+
+ /* Used by transport to wait until all MRs are returned */
+ struct {
+ wait_queue_head_t wait_queue;
+ } cleanup;
+ } mr_io;
+
+ /*
+ * The state for RDMA read/write requests on the server
+ */
+ struct {
+ /*
+ * The credit state for the send side
+ */
+ struct {
+ /*
+ * The maximum number of rw credits
+ */
+ size_t max;
+ /*
+ * The number of pages per credit
+ */
+ size_t num_pages;
+ atomic_t count;
+ wait_queue_head_t wait_queue;
+ } credits;
+ } rw_io;
+
+ /*
+ * For debug purposes
+ */
+ struct {
+ u64 get_receive_buffer;
+ u64 put_receive_buffer;
+ u64 enqueue_reassembly_queue;
+ u64 dequeue_reassembly_queue;
+ u64 send_empty;
+ } statistics;
+};
+
+static void __smbdirect_socket_disabled_work(struct work_struct *work)
+{
+ /*
+ * Should never be called as disable_[delayed_]work_sync() was used.
+ */
+ WARN_ON_ONCE(1);
+}
+
+static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc)
+{
+ /*
+ * This also sets status = SMBDIRECT_SOCKET_CREATED
+ */
+ BUILD_BUG_ON(SMBDIRECT_SOCKET_CREATED != 0);
+ memset(sc, 0, sizeof(*sc));
+
+ init_waitqueue_head(&sc->status_wait);
+
+ INIT_WORK(&sc->disconnect_work, __smbdirect_socket_disabled_work);
+ disable_work_sync(&sc->disconnect_work);
+
+ INIT_WORK(&sc->idle.immediate_work, __smbdirect_socket_disabled_work);
+ disable_work_sync(&sc->idle.immediate_work);
+ INIT_DELAYED_WORK(&sc->idle.timer_work, __smbdirect_socket_disabled_work);
+ disable_delayed_work_sync(&sc->idle.timer_work);
+
+ atomic_set(&sc->send_io.lcredits.count, 0);
+ init_waitqueue_head(&sc->send_io.lcredits.wait_queue);
+
+ atomic_set(&sc->send_io.credits.count, 0);
+ init_waitqueue_head(&sc->send_io.credits.wait_queue);
+
+ atomic_set(&sc->send_io.pending.count, 0);
+ init_waitqueue_head(&sc->send_io.pending.dec_wait_queue);
+ init_waitqueue_head(&sc->send_io.pending.zero_wait_queue);
+
+ INIT_LIST_HEAD(&sc->recv_io.free.list);
+ spin_lock_init(&sc->recv_io.free.lock);
+
+ atomic_set(&sc->recv_io.posted.count, 0);
+ INIT_WORK(&sc->recv_io.posted.refill_work, __smbdirect_socket_disabled_work);
+ disable_work_sync(&sc->recv_io.posted.refill_work);
+
+ atomic_set(&sc->recv_io.credits.count, 0);
+
+ INIT_LIST_HEAD(&sc->recv_io.reassembly.list);
+ spin_lock_init(&sc->recv_io.reassembly.lock);
+ init_waitqueue_head(&sc->recv_io.reassembly.wait_queue);
+
+ atomic_set(&sc->rw_io.credits.count, 0);
+ init_waitqueue_head(&sc->rw_io.credits.wait_queue);
+
+ spin_lock_init(&sc->mr_io.all.lock);
+ INIT_LIST_HEAD(&sc->mr_io.all.list);
+ atomic_set(&sc->mr_io.ready.count, 0);
+ init_waitqueue_head(&sc->mr_io.ready.wait_queue);
+ atomic_set(&sc->mr_io.used.count, 0);
+ INIT_WORK(&sc->mr_io.recovery_work, __smbdirect_socket_disabled_work);
+ disable_work_sync(&sc->mr_io.recovery_work);
+ init_waitqueue_head(&sc->mr_io.cleanup.wait_queue);
+}
+
+#define __SMBDIRECT_CHECK_STATUS_FAILED(__sc, __expected_status, __error_cmd, __unexpected_cmd) ({ \
+ bool __failed = false; \
+ if (unlikely((__sc)->first_error)) { \
+ __failed = true; \
+ __error_cmd \
+ } else if (unlikely((__sc)->status != (__expected_status))) { \
+ __failed = true; \
+ __unexpected_cmd \
+ } \
+ __failed; \
+})
+
+#define __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, __unexpected_cmd) \
+ __SMBDIRECT_CHECK_STATUS_FAILED(__sc, __expected_status, \
+ , \
+ { \
+ const struct sockaddr_storage *__src = NULL; \
+ const struct sockaddr_storage *__dst = NULL; \
+ if ((__sc)->rdma.cm_id) { \
+ __src = &(__sc)->rdma.cm_id->route.addr.src_addr; \
+ __dst = &(__sc)->rdma.cm_id->route.addr.dst_addr; \
+ } \
+ WARN_ONCE(1, \
+ "expected[%s] != %s first_error=%1pe local=%pISpsfc remote=%pISpsfc\n", \
+ smbdirect_socket_status_string(__expected_status), \
+ smbdirect_socket_status_string((__sc)->status), \
+ SMBDIRECT_DEBUG_ERR_PTR((__sc)->first_error), \
+ __src, __dst); \
+ __unexpected_cmd \
+ })
+
+#define SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status) \
+ __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, /* nothing */)
+
+#define SMBDIRECT_CHECK_STATUS_DISCONNECT(__sc, __expected_status) \
+ __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, \
+ __SMBDIRECT_SOCKET_DISCONNECT(__sc);)
+
+struct smbdirect_send_io {
+ struct smbdirect_socket *socket;
+ struct ib_cqe cqe;
+
+ /*
+ * The SGE entries for this work request
+ *
+ * The first points to the packet header
+ */
+#define SMBDIRECT_SEND_IO_MAX_SGE 6
+ size_t num_sge;
+ struct ib_sge sge[SMBDIRECT_SEND_IO_MAX_SGE];
+
+ /*
+ * Link to the list of sibling smbdirect_send_io
+ * messages.
+ */
+ struct list_head sibling_list;
+ struct ib_send_wr wr;
+
+ /* SMBD packet header follows this structure */
+ u8 packet[];
+};
+
+struct smbdirect_send_batch {
+ /*
+ * List of smbdirect_send_io messages
+ */
+ struct list_head msg_list;
+ /*
+ * Number of list entries
+ */
+ size_t wr_cnt;
+
+ /*
+ * Possible remote key invalidation state
+ */
+ bool need_invalidate_rkey;
+ u32 remote_key;
+};
+
+struct smbdirect_recv_io {
+ struct smbdirect_socket *socket;
+ struct ib_cqe cqe;
+
+ /*
+ * For now we only use a single SGE
+ * as we have just one large buffer
+ * per posted recv.
+ */
+#define SMBDIRECT_RECV_IO_MAX_SGE 1
+ struct ib_sge sge;
+
+ /* Link to free or reassembly list */
+ struct list_head list;
+
+ /* Indicate if this is the 1st packet of a payload */
+ bool first_segment;
+
+ /* SMBD packet header and payload follows this structure */
+ u8 packet[];
+};
+
+enum smbdirect_mr_state {
+ SMBDIRECT_MR_READY,
+ SMBDIRECT_MR_REGISTERED,
+ SMBDIRECT_MR_INVALIDATED,
+ SMBDIRECT_MR_ERROR,
+ SMBDIRECT_MR_DISABLED
+};
+
+struct smbdirect_mr_io {
+ struct smbdirect_socket *socket;
+ struct ib_cqe cqe;
+
+ /*
+ * We can have up to two references:
+ * 1. by the connection
+ * 2. by the registration
+ */
+ struct kref kref;
+ struct mutex mutex;
+
+ struct list_head list;
+
+ enum smbdirect_mr_state state;
+ struct ib_mr *mr;
+ struct sg_table sgt;
+ enum dma_data_direction dir;
+ union {
+ struct ib_reg_wr wr;
+ struct ib_send_wr inv_wr;
+ };
+
+ bool need_invalidate;
+ struct completion invalidate_done;
+};
+
+struct smbdirect_rw_io {
+ struct smbdirect_socket *socket;
+ struct ib_cqe cqe;
+
+ struct list_head list;
+
+ int error;
+ struct completion *completion;
+
+ struct rdma_rw_ctx rdma_ctx;
+ struct sg_table sgt;
+ struct scatterlist sg_list[];
+};
+
+#endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_SOCKET_H__ */
diff --git a/fs/smb/common/smbfsctl.h b/fs/smb/common/smbfsctl.h
index 4b379e84c46b..3253a18ecb5c 100644
--- a/fs/smb/common/smbfsctl.h
+++ b/fs/smb/common/smbfsctl.h
@@ -159,6 +159,9 @@
#define IO_REPARSE_TAG_LX_CHR 0x80000025
#define IO_REPARSE_TAG_LX_BLK 0x80000026
+/* If Name Surrogate Bit is set, the file or directory represents another named entity in the system. */
+#define IS_REPARSE_TAG_NAME_SURROGATE(tag) (!!((tag) & 0x20000000))
+
/* fsctl flags */
/* If Flags is set to this value, the request is an FSCTL not ioctl request */
#define SMB2_0_IOCTL_IS_FSCTL 0x00000001
diff --git a/fs/smb/common/smbglob.h b/fs/smb/common/smbglob.h
new file mode 100644
index 000000000000..9562845a5617
--- /dev/null
+++ b/fs/smb/common/smbglob.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2008
+ * 2018 Samsung Electronics Co., Ltd.
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ * Jeremy Allison (jra@samba.org)
+ * Namjae Jeon (linkinjeon@kernel.org)
+ *
+ */
+#ifndef _COMMON_SMB_GLOB_H
+#define _COMMON_SMB_GLOB_H
+
+#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff)
+
+struct smb_version_values {
+ char *version_string;
+ __u16 protocol_id;
+ __le16 lock_cmd;
+ __u32 req_capabilities;
+ __u32 max_read_size;
+ __u32 max_write_size;
+ __u32 max_trans_size;
+ __u32 max_credits;
+ __u32 large_lock_type;
+ __u32 exclusive_lock_type;
+ __u32 shared_lock_type;
+ __u32 unlock_lock_type;
+ size_t header_size;
+ size_t max_header_size;
+ size_t read_rsp_size;
+ unsigned int cap_unix;
+ unsigned int cap_nt_find;
+ unsigned int cap_large_files;
+ unsigned int cap_unicode;
+ __u16 signing_enabled;
+ __u16 signing_required;
+ size_t create_lease_size;
+ size_t create_durable_size;
+ size_t create_durable_v2_size;
+ size_t create_mxac_size;
+ size_t create_disk_id_size;
+ size_t create_posix_size;
+};
+
+static inline unsigned int get_rfc1002_len(void *buf)
+{
+ return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
+}
+
+static inline void inc_rfc1001_len(void *buf, int count)
+{
+ be32_add_cpu((__be32 *)buf, count);
+}
+
+#define SMB1_VERSION_STRING "1.0"
+#define SMB20_VERSION_STRING "2.0"
+#define SMB21_VERSION_STRING "2.1"
+#define SMBDEFAULT_VERSION_STRING "default"
+#define SMB3ANY_VERSION_STRING "3"
+#define SMB30_VERSION_STRING "3.0"
+#define SMB302_VERSION_STRING "3.02"
+#define ALT_SMB302_VERSION_STRING "3.0.2"
+#define SMB311_VERSION_STRING "3.1.1"
+#define ALT_SMB311_VERSION_STRING "3.11"
+
+#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
+
+#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
+
+#endif /* _COMMON_SMB_GLOB_H */
diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig
index cabe6a843c6a..2775162c535c 100644
--- a/fs/smb/server/Kconfig
+++ b/fs/smb/server/Kconfig
@@ -7,13 +7,13 @@ config SMB_SERVER
select NLS_UTF8
select NLS_UCS2_UTILS
select CRYPTO
- select CRYPTO_MD5
- select CRYPTO_HMAC
select CRYPTO_ECB
+ select CRYPTO_LIB_ARC4
select CRYPTO_LIB_DES
- select CRYPTO_SHA256
+ select CRYPTO_LIB_MD5
+ select CRYPTO_LIB_SHA256
+ select CRYPTO_LIB_SHA512
select CRYPTO_CMAC
- select CRYPTO_SHA512
select CRYPTO_AEAD2
select CRYPTO_CCM
select CRYPTO_GCM
@@ -70,4 +70,4 @@ config SMB_SERVER_CHECK_CAP_NET_ADMIN
config SMB_SERVER_KERBEROS5
bool "Support for Kerberos 5"
depends on SMB_SERVER
- default n
+ default y
diff --git a/fs/smb/server/asn1.c b/fs/smb/server/asn1.c
index b931a99ab9c8..5c4c5121fece 100644
--- a/fs/smb/server/asn1.c
+++ b/fs/smb/server/asn1.c
@@ -104,7 +104,7 @@ int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen,
oid_len + ntlmssp_len) * 2 +
neg_result_len + oid_len + ntlmssp_len;
- buf = kmalloc(total_len, GFP_KERNEL);
+ buf = kmalloc(total_len, KSMBD_DEFAULT_GFP);
if (!buf)
return -ENOMEM;
@@ -140,7 +140,7 @@ int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 +
neg_result_len;
- buf = kmalloc(total_len, GFP_KERNEL);
+ buf = kmalloc(total_len, KSMBD_DEFAULT_GFP);
if (!buf)
return -ENOMEM;
@@ -217,7 +217,7 @@ static int ksmbd_neg_token_alloc(void *context, size_t hdrlen,
if (!vlen)
return -EINVAL;
- conn->mechToken = kmemdup_nul(value, vlen, GFP_KERNEL);
+ conn->mechToken = kmemdup_nul(value, vlen, KSMBD_DEFAULT_GFP);
if (!conn->mechToken)
return -ENOMEM;
diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c
index 611716bc8f27..f2767c4b5132 100644
--- a/fs/smb/server/auth.c
+++ b/fs/smb/server/auth.c
@@ -13,6 +13,8 @@
#include <linux/xattr.h>
#include <crypto/hash.h>
#include <crypto/aead.h>
+#include <crypto/md5.h>
+#include <crypto/sha2.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
@@ -20,6 +22,7 @@
#include "glob.h"
#include <linux/fips.h>
+#include <crypto/arc4.h>
#include <crypto/des.h>
#include "server.h"
@@ -29,7 +32,6 @@
#include "mgmt/user_config.h"
#include "crypto_ctx.h"
#include "transport_ipc.h"
-#include "../common/arc4.h"
/*
* Fixed format data defining GSS header and fixed string
@@ -69,89 +71,20 @@ void ksmbd_copy_gss_neg_header(void *buf)
memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH);
}
-/**
- * ksmbd_gen_sess_key() - function to generate session key
- * @sess: session of connection
- * @hash: source hash value to be used for find session key
- * @hmac: source hmac value to be used for finding session key
- *
- */
-static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash,
- char *hmac)
-{
- struct ksmbd_crypto_ctx *ctx;
- int rc;
-
- ctx = ksmbd_crypto_ctx_find_hmacmd5();
- if (!ctx) {
- ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
- return -ENOMEM;
- }
-
- rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
- hash,
- CIFS_HMAC_MD5_HASH_SIZE);
- if (rc) {
- ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc);
- goto out;
- }
-
- rc = crypto_shash_init(CRYPTO_HMACMD5(ctx));
- if (rc) {
- ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc);
- goto out;
- }
-
- rc = crypto_shash_update(CRYPTO_HMACMD5(ctx),
- hmac,
- SMB2_NTLMV2_SESSKEY_SIZE);
- if (rc) {
- ksmbd_debug(AUTH, "Could not update with response error %d\n", rc);
- goto out;
- }
-
- rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key);
- if (rc) {
- ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc);
- goto out;
- }
-
-out:
- ksmbd_release_crypto_ctx(ctx);
- return rc;
-}
-
static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess,
char *ntlmv2_hash, char *dname)
{
int ret, len, conv_len;
wchar_t *domain = NULL;
__le16 *uniname = NULL;
- struct ksmbd_crypto_ctx *ctx;
-
- ctx = ksmbd_crypto_ctx_find_hmacmd5();
- if (!ctx) {
- ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n");
- return -ENOMEM;
- }
+ struct hmac_md5_ctx ctx;
- ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
- user_passkey(sess->user),
+ hmac_md5_init_usingrawkey(&ctx, user_passkey(sess->user),
CIFS_ENCPWD_SIZE);
- if (ret) {
- ksmbd_debug(AUTH, "Could not set NT Hash as a key\n");
- goto out;
- }
-
- ret = crypto_shash_init(CRYPTO_HMACMD5(ctx));
- if (ret) {
- ksmbd_debug(AUTH, "could not init hmacmd5\n");
- goto out;
- }
/* convert user_name to unicode */
len = strlen(user_name(sess->user));
- uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL);
+ uniname = kzalloc(2 + UNICODE_LEN(len), KSMBD_DEFAULT_GFP);
if (!uniname) {
ret = -ENOMEM;
goto out;
@@ -165,17 +98,11 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess,
}
UniStrupr(uniname);
- ret = crypto_shash_update(CRYPTO_HMACMD5(ctx),
- (char *)uniname,
- UNICODE_LEN(conv_len));
- if (ret) {
- ksmbd_debug(AUTH, "Could not update with user\n");
- goto out;
- }
+ hmac_md5_update(&ctx, (const u8 *)uniname, UNICODE_LEN(conv_len));
/* Convert domain name or conn name to unicode and uppercase */
len = strlen(dname);
- domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL);
+ domain = kzalloc(2 + UNICODE_LEN(len), KSMBD_DEFAULT_GFP);
if (!domain) {
ret = -ENOMEM;
goto out;
@@ -188,21 +115,12 @@ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess,
goto out;
}
- ret = crypto_shash_update(CRYPTO_HMACMD5(ctx),
- (char *)domain,
- UNICODE_LEN(conv_len));
- if (ret) {
- ksmbd_debug(AUTH, "Could not update with domain\n");
- goto out;
- }
-
- ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash);
- if (ret)
- ksmbd_debug(AUTH, "Could not generate md5 hash\n");
+ hmac_md5_update(&ctx, (const u8 *)domain, UNICODE_LEN(conv_len));
+ hmac_md5_final(&ctx, ntlmv2_hash);
+ ret = 0;
out:
kfree(uniname);
kfree(domain);
- ksmbd_release_crypto_ctx(ctx);
return ret;
}
@@ -223,73 +141,33 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
{
char ntlmv2_hash[CIFS_ENCPWD_SIZE];
char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE];
- struct ksmbd_crypto_ctx *ctx = NULL;
- char *construct = NULL;
- int rc, len;
-
- rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name);
- if (rc) {
- ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc);
- goto out;
- }
-
- ctx = ksmbd_crypto_ctx_find_hmacmd5();
- if (!ctx) {
- ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
- return -ENOMEM;
- }
-
- rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
- ntlmv2_hash,
- CIFS_HMAC_MD5_HASH_SIZE);
- if (rc) {
- ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n");
- goto out;
- }
-
- rc = crypto_shash_init(CRYPTO_HMACMD5(ctx));
- if (rc) {
- ksmbd_debug(AUTH, "Could not init hmacmd5\n");
- goto out;
- }
+ struct hmac_md5_ctx ctx;
+ int rc;
- len = CIFS_CRYPTO_KEY_SIZE + blen;
- construct = kzalloc(len, GFP_KERNEL);
- if (!construct) {
- rc = -ENOMEM;
- goto out;
+ if (fips_enabled) {
+ ksmbd_debug(AUTH, "NTLMv2 support is disabled due to FIPS\n");
+ return -EOPNOTSUPP;
}
- memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE);
- memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen);
-
- rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len);
+ rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name);
if (rc) {
- ksmbd_debug(AUTH, "Could not update with response\n");
- goto out;
+ ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc);
+ return rc;
}
- rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp);
- if (rc) {
- ksmbd_debug(AUTH, "Could not generate md5 hash\n");
- goto out;
- }
- ksmbd_release_crypto_ctx(ctx);
- ctx = NULL;
+ hmac_md5_init_usingrawkey(&ctx, ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
+ hmac_md5_update(&ctx, cryptkey, CIFS_CRYPTO_KEY_SIZE);
+ hmac_md5_update(&ctx, (const u8 *)&ntlmv2->blob_signature, blen);
+ hmac_md5_final(&ctx, ntlmv2_rsp);
- rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp);
- if (rc) {
- ksmbd_debug(AUTH, "Could not generate sess key\n");
- goto out;
- }
+ /* Generate the session key */
+ hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
+ ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE,
+ sess->sess_key);
if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0)
- rc = -EINVAL;
-out:
- if (ctx)
- ksmbd_release_crypto_ctx(ctx);
- kfree(construct);
- return rc;
+ return -EINVAL;
+ return 0;
}
/**
@@ -361,14 +239,13 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
if (sess_key_len > CIFS_KEY_SIZE)
return -EINVAL;
- ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL);
+ ctx_arc4 = kmalloc(sizeof(*ctx_arc4), KSMBD_DEFAULT_GFP);
if (!ctx_arc4)
return -ENOMEM;
- cifs_arc4_setkey(ctx_arc4, sess->sess_key,
- SMB2_NTLMV2_SESSKEY_SIZE);
- cifs_arc4_crypt(ctx_arc4, sess->sess_key,
- (char *)authblob + sess_key_off, sess_key_len);
+ arc4_setkey(ctx_arc4, sess->sess_key, SMB2_NTLMV2_SESSKEY_SIZE);
+ arc4_crypt(ctx_arc4, sess->sess_key,
+ (char *)authblob + sess_key_off, sess_key_len);
kfree_sensitive(ctx_arc4);
}
@@ -451,7 +328,7 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
chgblob->NegotiateFlags = cpu_to_le32(flags);
len = strlen(ksmbd_netbios_name());
- name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL);
+ name = kmalloc(2 + UNICODE_LEN(len), KSMBD_DEFAULT_GFP);
if (!name)
return -ENOMEM;
@@ -550,7 +427,19 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
retval = -ENOMEM;
goto out;
}
- sess->user = user;
+
+ if (!sess->user) {
+ /* First successful authentication */
+ sess->user = user;
+ } else {
+ if (!ksmbd_compare_user(sess->user, user)) {
+ ksmbd_debug(AUTH, "different user tried to reuse session\n");
+ retval = -EPERM;
+ ksmbd_free_user(user);
+ goto out;
+ }
+ ksmbd_free_user(user);
+ }
memcpy(sess->sess_key, resp->payload, resp->session_key_len);
memcpy(out_blob, resp->payload + resp->session_key_len,
@@ -578,46 +467,16 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
* @sig: signature value generated for client request packet
*
*/
-int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
- int n_vec, char *sig)
+void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
+ int n_vec, char *sig)
{
- struct ksmbd_crypto_ctx *ctx;
- int rc, i;
-
- ctx = ksmbd_crypto_ctx_find_hmacsha256();
- if (!ctx) {
- ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
- return -ENOMEM;
- }
-
- rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx),
- key,
- SMB2_NTLMV2_SESSKEY_SIZE);
- if (rc)
- goto out;
-
- rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx));
- if (rc) {
- ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc);
- goto out;
- }
+ struct hmac_sha256_ctx ctx;
+ int i;
- for (i = 0; i < n_vec; i++) {
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
- iov[i].iov_base,
- iov[i].iov_len);
- if (rc) {
- ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc);
- goto out;
- }
- }
-
- rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig);
- if (rc)
- ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc);
-out:
- ksmbd_release_crypto_ctx(ctx);
- return rc;
+ hmac_sha256_init_usingrawkey(&ctx, key, SMB2_NTLMV2_SESSKEY_SIZE);
+ for (i = 0; i < n_vec; i++)
+ hmac_sha256_update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ hmac_sha256_final(&ctx, sig);
}
/**
@@ -677,98 +536,39 @@ struct derivation {
bool binding;
};
-static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess,
- struct kvec label, struct kvec context, __u8 *key,
- unsigned int key_size)
+static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess,
+ struct kvec label, struct kvec context, __u8 *key,
+ unsigned int key_size)
{
unsigned char zero = 0x0;
__u8 i[4] = {0, 0, 0, 1};
__u8 L128[4] = {0, 0, 0, 128};
__u8 L256[4] = {0, 0, 1, 0};
- int rc;
unsigned char prfhash[SMB2_HMACSHA256_SIZE];
- unsigned char *hashptr = prfhash;
- struct ksmbd_crypto_ctx *ctx;
-
- memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
- memset(key, 0x0, key_size);
-
- ctx = ksmbd_crypto_ctx_find_hmacsha256();
- if (!ctx) {
- ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
- return -ENOMEM;
- }
-
- rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx),
- sess->sess_key,
- SMB2_NTLMV2_SESSKEY_SIZE);
- if (rc)
- goto smb3signkey_ret;
-
- rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx));
- if (rc) {
- ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc);
- goto smb3signkey_ret;
- }
+ struct hmac_sha256_ctx ctx;
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with n\n");
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
- label.iov_base,
- label.iov_len);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with label\n");
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with zero\n");
- goto smb3signkey_ret;
- }
-
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
- context.iov_base,
- context.iov_len);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with context\n");
- goto smb3signkey_ret;
- }
+ hmac_sha256_init_usingrawkey(&ctx, sess->sess_key,
+ SMB2_NTLMV2_SESSKEY_SIZE);
+ hmac_sha256_update(&ctx, i, 4);
+ hmac_sha256_update(&ctx, label.iov_base, label.iov_len);
+ hmac_sha256_update(&ctx, &zero, 1);
+ hmac_sha256_update(&ctx, context.iov_base, context.iov_len);
if (key_size == SMB3_ENC_DEC_KEY_SIZE &&
(conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM ||
conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM))
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4);
+ hmac_sha256_update(&ctx, L256, 4);
else
- rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with L\n");
- goto smb3signkey_ret;
- }
+ hmac_sha256_update(&ctx, L128, 4);
- rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr);
- if (rc) {
- ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n",
- rc);
- goto smb3signkey_ret;
- }
-
- memcpy(key, hashptr, key_size);
-
-smb3signkey_ret:
- ksmbd_release_crypto_ctx(ctx);
- return rc;
+ hmac_sha256_final(&ctx, prfhash);
+ memcpy(key, prfhash, key_size);
}
static int generate_smb3signingkey(struct ksmbd_session *sess,
struct ksmbd_conn *conn,
const struct derivation *signing)
{
- int rc;
struct channel *chann;
char *key;
@@ -781,10 +581,8 @@ static int generate_smb3signingkey(struct ksmbd_session *sess,
else
key = sess->smb3signingkey;
- rc = generate_key(conn, sess, signing->label, signing->context, key,
- SMB3_SIGN_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(conn, sess, signing->label, signing->context, key,
+ SMB3_SIGN_KEY_SIZE);
if (!(conn->dialect >= SMB30_PROT_ID && signing->binding))
memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE);
@@ -840,23 +638,17 @@ struct derivation_twin {
struct derivation decryption;
};
-static int generate_smb3encryptionkey(struct ksmbd_conn *conn,
- struct ksmbd_session *sess,
- const struct derivation_twin *ptwin)
+static void generate_smb3encryptionkey(struct ksmbd_conn *conn,
+ struct ksmbd_session *sess,
+ const struct derivation_twin *ptwin)
{
- int rc;
-
- rc = generate_key(conn, sess, ptwin->encryption.label,
- ptwin->encryption.context, sess->smb3encryptionkey,
- SMB3_ENC_DEC_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(conn, sess, ptwin->encryption.label,
+ ptwin->encryption.context, sess->smb3encryptionkey,
+ SMB3_ENC_DEC_KEY_SIZE);
- rc = generate_key(conn, sess, ptwin->decryption.label,
- ptwin->decryption.context,
- sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(conn, sess, ptwin->decryption.label,
+ ptwin->decryption.context,
+ sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE);
ksmbd_debug(AUTH, "dumping generated AES encryption keys\n");
ksmbd_debug(AUTH, "Cipher type %d\n", conn->cipher_type);
@@ -875,11 +667,10 @@ static int generate_smb3encryptionkey(struct ksmbd_conn *conn,
ksmbd_debug(AUTH, "ServerOut Key %*ph\n",
SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey);
}
- return 0;
}
-int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
- struct ksmbd_session *sess)
+void ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
+ struct ksmbd_session *sess)
{
struct derivation_twin twin;
struct derivation *d;
@@ -896,11 +687,11 @@ int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
d->context.iov_base = "ServerIn ";
d->context.iov_len = 10;
- return generate_smb3encryptionkey(conn, sess, &twin);
+ generate_smb3encryptionkey(conn, sess, &twin);
}
-int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
- struct ksmbd_session *sess)
+void ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
+ struct ksmbd_session *sess)
{
struct derivation_twin twin;
struct derivation *d;
@@ -917,88 +708,26 @@ int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
d->context.iov_base = sess->Preauth_HashValue;
d->context.iov_len = 64;
- return generate_smb3encryptionkey(conn, sess, &twin);
+ generate_smb3encryptionkey(conn, sess, &twin);
}
int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf,
__u8 *pi_hash)
{
- int rc;
struct smb2_hdr *rcv_hdr = smb2_get_msg(buf);
char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId;
int msg_size = get_rfc1002_len(buf);
- struct ksmbd_crypto_ctx *ctx = NULL;
+ struct sha512_ctx sha_ctx;
if (conn->preauth_info->Preauth_HashId !=
SMB2_PREAUTH_INTEGRITY_SHA512)
return -EINVAL;
- ctx = ksmbd_crypto_ctx_find_sha512();
- if (!ctx) {
- ksmbd_debug(AUTH, "could not alloc sha512\n");
- return -ENOMEM;
- }
-
- rc = crypto_shash_init(CRYPTO_SHA512(ctx));
- if (rc) {
- ksmbd_debug(AUTH, "could not init shashn");
- goto out;
- }
-
- rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with n\n");
- goto out;
- }
-
- rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with n\n");
- goto out;
- }
-
- rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash);
- if (rc) {
- ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc);
- goto out;
- }
-out:
- ksmbd_release_crypto_ctx(ctx);
- return rc;
-}
-
-int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len,
- __u8 *pi_hash)
-{
- int rc;
- struct ksmbd_crypto_ctx *ctx = NULL;
-
- ctx = ksmbd_crypto_ctx_find_sha256();
- if (!ctx) {
- ksmbd_debug(AUTH, "could not alloc sha256\n");
- return -ENOMEM;
- }
-
- rc = crypto_shash_init(CRYPTO_SHA256(ctx));
- if (rc) {
- ksmbd_debug(AUTH, "could not init shashn");
- goto out;
- }
-
- rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len);
- if (rc) {
- ksmbd_debug(AUTH, "could not update with n\n");
- goto out;
- }
-
- rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash);
- if (rc) {
- ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc);
- goto out;
- }
-out:
- ksmbd_release_crypto_ctx(ctx);
- return rc;
+ sha512_init(&sha_ctx);
+ sha512_update(&sha_ctx, pi_hash, 64);
+ sha512_update(&sha_ctx, all_bytes_msg, msg_size);
+ sha512_final(&sha_ctx, pi_hash);
+ return 0;
}
static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id,
@@ -1017,6 +746,8 @@ static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id,
ses_enc_key = enc ? sess->smb3encryptionkey :
sess->smb3decryptionkey;
memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE);
+ if (!enc)
+ ksmbd_user_session_put(sess);
return 0;
}
@@ -1043,7 +774,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
if (!nvec)
return NULL;
- nr_entries = kcalloc(nvec, sizeof(int), GFP_KERNEL);
+ nr_entries = kcalloc(nvec, sizeof(int), KSMBD_DEFAULT_GFP);
if (!nr_entries)
return NULL;
@@ -1063,7 +794,8 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
/* Add two entries for transform header and signature */
total_entries += 2;
- sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL);
+ sg = kmalloc_array(total_entries, sizeof(struct scatterlist),
+ KSMBD_DEFAULT_GFP);
if (!sg) {
kfree(nr_entries);
return NULL;
@@ -1163,7 +895,7 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov,
goto free_ctx;
}
- req = aead_request_alloc(tfm, GFP_KERNEL);
+ req = aead_request_alloc(tfm, KSMBD_DEFAULT_GFP);
if (!req) {
rc = -ENOMEM;
goto free_ctx;
@@ -1182,7 +914,7 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov,
}
iv_len = crypto_aead_ivsize(tfm);
- iv = kzalloc(iv_len, GFP_KERNEL);
+ iv = kzalloc(iv_len, KSMBD_DEFAULT_GFP);
if (!iv) {
rc = -ENOMEM;
goto free_sg;
@@ -1215,7 +947,7 @@ free_iv:
free_sg:
kfree(sg);
free_req:
- kfree(req);
+ aead_request_free(req);
free_ctx:
ksmbd_release_crypto_ctx(ctx);
return rc;
diff --git a/fs/smb/server/auth.h b/fs/smb/server/auth.h
index 362b6159a6cf..6d351d61b0e5 100644
--- a/fs/smb/server/auth.h
+++ b/fs/smb/server/auth.h
@@ -52,20 +52,18 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
struct ksmbd_conn *conn);
int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
int in_len, char *out_blob, int *out_len);
-int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
- int n_vec, char *sig);
+void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
+ int n_vec, char *sig);
int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
int n_vec, char *sig);
int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess,
struct ksmbd_conn *conn);
int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess,
struct ksmbd_conn *conn);
-int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
- struct ksmbd_session *sess);
-int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
+void ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
struct ksmbd_session *sess);
+void ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
+ struct ksmbd_session *sess);
int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf,
__u8 *pi_hash);
-int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len,
- __u8 *pi_hash);
#endif
diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index e6a72f75ab94..b6b4f1286b9c 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -19,7 +19,7 @@ static DEFINE_MUTEX(init_lock);
static struct ksmbd_conn_ops default_conn_ops;
-LIST_HEAD(conn_list);
+DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS);
DECLARE_RWSEM(conn_list_lock);
/**
@@ -33,14 +33,16 @@ DECLARE_RWSEM(conn_list_lock);
void ksmbd_conn_free(struct ksmbd_conn *conn)
{
down_write(&conn_list_lock);
- list_del(&conn->conns_list);
+ hash_del(&conn->hlist);
up_write(&conn_list_lock);
xa_destroy(&conn->sessions);
kvfree(conn->request_buf);
kfree(conn->preauth_info);
- if (atomic_dec_and_test(&conn->refcnt))
+ if (atomic_dec_and_test(&conn->refcnt)) {
+ conn->transport->ops->free_transport(conn->transport);
kfree(conn);
+ }
}
/**
@@ -52,7 +54,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
{
struct ksmbd_conn *conn;
- conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL);
+ conn = kzalloc(sizeof(struct ksmbd_conn), KSMBD_DEFAULT_GFP);
if (!conn)
return NULL;
@@ -70,13 +72,11 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
atomic_set(&conn->req_running, 0);
atomic_set(&conn->r_count, 0);
atomic_set(&conn->refcnt, 1);
- atomic_set(&conn->mux_smb_requests, 0);
conn->total_credits = 1;
conn->outstanding_credits = 0;
init_waitqueue_head(&conn->req_running_q);
init_waitqueue_head(&conn->r_count_q);
- INIT_LIST_HEAD(&conn->conns_list);
INIT_LIST_HEAD(&conn->requests);
INIT_LIST_HEAD(&conn->async_requests);
spin_lock_init(&conn->request_lock);
@@ -89,19 +89,17 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
init_rwsem(&conn->session_lock);
- down_write(&conn_list_lock);
- list_add(&conn->conns_list, &conn_list);
- up_write(&conn_list_lock);
return conn;
}
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c)
{
struct ksmbd_conn *t;
+ int bkt;
bool ret = false;
down_read(&conn_list_lock);
- list_for_each_entry(t, &conn_list, conns_list) {
+ hash_for_each(conn_list, bkt, t, hlist) {
if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE))
continue;
@@ -120,8 +118,8 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE)
requests_queue = &conn->requests;
+ atomic_inc(&conn->req_running);
if (requests_queue) {
- atomic_inc(&conn->req_running);
spin_lock(&conn->request_lock);
list_add_tail(&work->request_entry, requests_queue);
spin_unlock(&conn->request_lock);
@@ -132,11 +130,14 @@ void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
+ atomic_dec(&conn->req_running);
+ if (waitqueue_active(&conn->req_running_q))
+ wake_up(&conn->req_running_q);
+
if (list_empty(&work->request_entry) &&
list_empty(&work->async_request_entry))
return;
- atomic_dec(&conn->req_running);
spin_lock(&conn->request_lock);
list_del_init(&work->request_entry);
spin_unlock(&conn->request_lock);
@@ -159,9 +160,10 @@ void ksmbd_conn_unlock(struct ksmbd_conn *conn)
void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
{
struct ksmbd_conn *conn;
+ int bkt;
down_read(&conn_list_lock);
- list_for_each_entry(conn, &conn_list, conns_list) {
+ hash_for_each(conn_list, bkt, conn, hlist) {
if (conn->binding || xa_load(&conn->sessions, sess_id))
WRITE_ONCE(conn->status, status);
}
@@ -177,14 +179,14 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)
{
struct ksmbd_conn *conn;
int rc, retry_count = 0, max_timeout = 120;
- int rcount = 1;
+ int rcount = 1, bkt;
retry_idle:
if (retry_count >= max_timeout)
return -EIO;
down_read(&conn_list_lock);
- list_for_each_entry(conn, &conn_list, conns_list) {
+ hash_for_each(conn_list, bkt, conn, hlist) {
if (conn->binding || xa_load(&conn->sessions, sess_id)) {
if (conn == curr_conn)
rcount = 2;
@@ -239,7 +241,7 @@ int ksmbd_conn_write(struct ksmbd_work *work)
int ksmbd_conn_rdma_read(struct ksmbd_conn *conn,
void *buf, unsigned int buflen,
- struct smb2_buffer_desc_v1 *desc,
+ struct smbdirect_buffer_descriptor_v1 *desc,
unsigned int desc_len)
{
int ret = -EINVAL;
@@ -253,7 +255,7 @@ int ksmbd_conn_rdma_read(struct ksmbd_conn *conn,
int ksmbd_conn_rdma_write(struct ksmbd_conn *conn,
void *buf, unsigned int buflen,
- struct smb2_buffer_desc_v1 *desc,
+ struct smbdirect_buffer_descriptor_v1 *desc,
unsigned int desc_len)
{
int ret = -EINVAL;
@@ -308,7 +310,7 @@ int ksmbd_conn_handler_loop(void *p)
{
struct ksmbd_conn *conn = (struct ksmbd_conn *)p;
struct ksmbd_transport *t = conn->transport;
- unsigned int pdu_size, max_allowed_pdu_size;
+ unsigned int pdu_size, max_allowed_pdu_size, max_req;
char hdr_buf[4] = {0,};
int size;
@@ -318,6 +320,7 @@ int ksmbd_conn_handler_loop(void *p)
if (t->ops->prepare && t->ops->prepare(t))
goto out;
+ max_req = server_conf.max_inflight_req;
conn->last_active = jiffies;
set_freezable();
while (ksmbd_conn_alive(conn)) {
@@ -327,6 +330,13 @@ int ksmbd_conn_handler_loop(void *p)
kvfree(conn->request_buf);
conn->request_buf = NULL;
+recheck:
+ if (atomic_read(&conn->req_running) + 1 > max_req) {
+ wait_event_interruptible(conn->req_running_q,
+ atomic_read(&conn->req_running) < max_req);
+ goto recheck;
+ }
+
size = t->ops->read(t, hdr_buf, sizeof(hdr_buf), -1);
if (size != sizeof(hdr_buf))
break;
@@ -359,7 +369,7 @@ int ksmbd_conn_handler_loop(void *p)
/* 4 for rfc1002 length field */
/* 1 for implied bcc[0] */
size = pdu_size + 4 + 1;
- conn->request_buf = kvmalloc(size, GFP_KERNEL);
+ conn->request_buf = kvmalloc(size, KSMBD_DEFAULT_GFP);
if (!conn->request_buf)
break;
@@ -404,6 +414,7 @@ int ksmbd_conn_handler_loop(void *p)
out:
ksmbd_conn_set_releasing(conn);
/* Wait till all reference dropped to the Server object*/
+ ksmbd_debug(CONN, "Wait for all pending requests(%d)\n", atomic_read(&conn->r_count));
wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0);
if (IS_ENABLED(CONFIG_UNICODE))
@@ -422,6 +433,26 @@ void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops)
default_conn_ops.terminate_fn = ops->terminate_fn;
}
+void ksmbd_conn_r_count_inc(struct ksmbd_conn *conn)
+{
+ atomic_inc(&conn->r_count);
+}
+
+void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn)
+{
+ /*
+ * Checking waitqueue to dropping pending requests on
+ * disconnection. waitqueue_active is safe because it
+ * uses atomic operation for condition.
+ */
+ atomic_inc(&conn->refcnt);
+ if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
+ wake_up(&conn->r_count_q);
+
+ if (atomic_dec_and_test(&conn->refcnt))
+ kfree(conn);
+}
+
int ksmbd_conn_transport_init(void)
{
int ret;
@@ -447,10 +478,11 @@ static void stop_sessions(void)
{
struct ksmbd_conn *conn;
struct ksmbd_transport *t;
+ int bkt;
again:
down_read(&conn_list_lock);
- list_for_each_entry(conn, &conn_list, conns_list) {
+ hash_for_each(conn_list, bkt, conn, hlist) {
t = conn->transport;
ksmbd_conn_set_exiting(conn);
if (t->ops->shutdown) {
@@ -461,8 +493,8 @@ again:
}
up_read(&conn_list_lock);
- if (!list_empty(&conn_list)) {
- schedule_timeout_interruptible(HZ / 10); /* 100ms */
+ if (!hash_empty(conn_list)) {
+ msleep(100);
goto again;
}
}
@@ -471,7 +503,8 @@ void ksmbd_conn_transport_destroy(void)
{
mutex_lock(&init_lock);
ksmbd_tcp_destroy();
- ksmbd_rdma_destroy();
+ ksmbd_rdma_stop_listening();
stop_sessions();
+ ksmbd_rdma_destroy();
mutex_unlock(&init_lock);
}
diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h
index 8ddd5a3c7baf..7f9bcd9817b5 100644
--- a/fs/smb/server/connection.h
+++ b/fs/smb/server/connection.h
@@ -19,6 +19,8 @@
#include "smb_common.h"
#include "ksmbd_work.h"
+struct smbdirect_buffer_descriptor_v1;
+
#define KSMBD_SOCKET_BACKLOG 16
enum {
@@ -27,6 +29,7 @@ enum {
KSMBD_SESS_EXITING,
KSMBD_SESS_NEED_RECONNECT,
KSMBD_SESS_NEED_NEGOTIATE,
+ KSMBD_SESS_NEED_SETUP,
KSMBD_SESS_RELEASING
};
@@ -45,11 +48,18 @@ struct ksmbd_conn {
struct mutex srv_mutex;
int status;
unsigned int cli_cap;
+ union {
+ __be32 inet_addr;
+#if IS_ENABLED(CONFIG_IPV6)
+ u8 inet6_addr[16];
+#endif
+ };
+ unsigned int inet_hash;
char *request_buf;
struct ksmbd_transport *transport;
struct nls_table *local_nls;
struct unicode_map *um;
- struct list_head conns_list;
+ struct hlist_node hlist;
struct rw_semaphore session_lock;
/* smb session 1 per user */
struct xarray sessions;
@@ -107,7 +117,7 @@ struct ksmbd_conn {
__le16 signing_algorithm;
bool binding;
atomic_t refcnt;
- atomic_t mux_smb_requests;
+ bool is_aapl;
};
struct ksmbd_conn_ops {
@@ -126,12 +136,13 @@ struct ksmbd_transport_ops {
unsigned int remote_key);
int (*rdma_read)(struct ksmbd_transport *t,
void *buf, unsigned int len,
- struct smb2_buffer_desc_v1 *desc,
+ struct smbdirect_buffer_descriptor_v1 *desc,
unsigned int desc_len);
int (*rdma_write)(struct ksmbd_transport *t,
void *buf, unsigned int len,
- struct smb2_buffer_desc_v1 *desc,
+ struct smbdirect_buffer_descriptor_v1 *desc,
unsigned int desc_len);
+ void (*free_transport)(struct ksmbd_transport *kt);
};
struct ksmbd_transport {
@@ -143,7 +154,8 @@ struct ksmbd_transport {
#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ)
#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr))
-extern struct list_head conn_list;
+#define CONN_HASH_BITS 12
+extern DECLARE_HASHTABLE(conn_list, CONN_HASH_BITS);
extern struct rw_semaphore conn_list_lock;
bool ksmbd_conn_alive(struct ksmbd_conn *conn);
@@ -155,11 +167,11 @@ bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
int ksmbd_conn_write(struct ksmbd_work *work);
int ksmbd_conn_rdma_read(struct ksmbd_conn *conn,
void *buf, unsigned int buflen,
- struct smb2_buffer_desc_v1 *desc,
+ struct smbdirect_buffer_descriptor_v1 *desc,
unsigned int desc_len);
int ksmbd_conn_rdma_write(struct ksmbd_conn *conn,
void *buf, unsigned int buflen,
- struct smb2_buffer_desc_v1 *desc,
+ struct smbdirect_buffer_descriptor_v1 *desc,
unsigned int desc_len);
void ksmbd_conn_enqueue_request(struct ksmbd_work *work);
void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work);
@@ -169,6 +181,8 @@ int ksmbd_conn_transport_init(void);
void ksmbd_conn_transport_destroy(void);
void ksmbd_conn_lock(struct ksmbd_conn *conn);
void ksmbd_conn_unlock(struct ksmbd_conn *conn);
+void ksmbd_conn_r_count_inc(struct ksmbd_conn *conn);
+void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn);
/*
* WARNING
@@ -186,6 +200,11 @@ static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn)
return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE;
}
+static inline bool ksmbd_conn_need_setup(struct ksmbd_conn *conn)
+{
+ return READ_ONCE(conn->status) == KSMBD_SESS_NEED_SETUP;
+}
+
static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn)
{
return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT;
@@ -216,6 +235,11 @@ static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_conn *conn)
WRITE_ONCE(conn->status, KSMBD_SESS_NEED_NEGOTIATE);
}
+static inline void ksmbd_conn_set_need_setup(struct ksmbd_conn *conn)
+{
+ WRITE_ONCE(conn->status, KSMBD_SESS_NEED_SETUP);
+}
+
static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn)
{
WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT);
diff --git a/fs/smb/server/crypto_ctx.c b/fs/smb/server/crypto_ctx.c
index 81488d04199d..fe29d186baf6 100644
--- a/fs/smb/server/crypto_ctx.c
+++ b/fs/smb/server/crypto_ctx.c
@@ -66,21 +66,9 @@ static struct shash_desc *alloc_shash_desc(int id)
struct shash_desc *shash;
switch (id) {
- case CRYPTO_SHASH_HMACMD5:
- tfm = crypto_alloc_shash("hmac(md5)", 0, 0);
- break;
- case CRYPTO_SHASH_HMACSHA256:
- tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
- break;
case CRYPTO_SHASH_CMACAES:
tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
break;
- case CRYPTO_SHASH_SHA256:
- tfm = crypto_alloc_shash("sha256", 0, 0);
- break;
- case CRYPTO_SHASH_SHA512:
- tfm = crypto_alloc_shash("sha512", 0, 0);
- break;
default:
return NULL;
}
@@ -89,7 +77,7 @@ static struct shash_desc *alloc_shash_desc(int id)
return NULL;
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
- GFP_KERNEL);
+ KSMBD_DEFAULT_GFP);
if (!shash)
crypto_free_shash(tfm);
else
@@ -133,7 +121,7 @@ static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void)
ctx_list.avail_ctx++;
spin_unlock(&ctx_list.ctx_lock);
- ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL);
+ ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), KSMBD_DEFAULT_GFP);
if (!ctx) {
spin_lock(&ctx_list.ctx_lock);
ctx_list.avail_ctx--;
@@ -183,31 +171,11 @@ static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id)
return NULL;
}
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void)
-{
- return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5);
-}
-
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void)
-{
- return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256);
-}
-
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void)
{
return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES);
}
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void)
-{
- return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256);
-}
-
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void)
-{
- return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512);
-}
-
static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id)
{
struct ksmbd_crypto_ctx *ctx;
@@ -258,7 +226,7 @@ int ksmbd_crypto_create(void)
init_waitqueue_head(&ctx_list.ctx_wait);
ctx_list.avail_ctx = 1;
- ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL);
+ ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), KSMBD_DEFAULT_GFP);
if (!ctx)
return -ENOMEM;
list_add(&ctx->list, &ctx_list.idle_ctx);
diff --git a/fs/smb/server/crypto_ctx.h b/fs/smb/server/crypto_ctx.h
index 4a367c62f653..b9476ed520ae 100644
--- a/fs/smb/server/crypto_ctx.h
+++ b/fs/smb/server/crypto_ctx.h
@@ -10,11 +10,7 @@
#include <crypto/aead.h>
enum {
- CRYPTO_SHASH_HMACMD5 = 0,
- CRYPTO_SHASH_HMACSHA256,
- CRYPTO_SHASH_CMACAES,
- CRYPTO_SHASH_SHA256,
- CRYPTO_SHASH_SHA512,
+ CRYPTO_SHASH_CMACAES = 0,
CRYPTO_SHASH_MAX,
};
@@ -36,28 +32,15 @@ struct ksmbd_crypto_ctx {
struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX];
};
-#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5])
-#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256])
#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES])
-#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256])
-#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512])
-#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm)
-#define CRYPTO_HMACSHA256_TFM(c)\
- ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm)
#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm)
-#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm)
-#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm)
#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM])
#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM])
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx);
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void);
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void);
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void);
-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void);
void ksmbd_crypto_destroy(void);
diff --git a/fs/smb/server/glob.h b/fs/smb/server/glob.h
index d528b20b37a8..4ea187af2348 100644
--- a/fs/smb/server/glob.h
+++ b/fs/smb/server/glob.h
@@ -44,4 +44,6 @@ extern int ksmbd_debug_types;
#define UNICODE_LEN(x) ((x) * 2)
+#define KSMBD_DEFAULT_GFP (GFP_KERNEL | __GFP_RETRY_MAYFAIL)
+
#endif /* __KSMBD_GLOB_H */
diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h
index 3d01d9d15293..8ccd57fd904b 100644
--- a/fs/smb/server/ksmbd_netlink.h
+++ b/fs/smb/server/ksmbd_netlink.h
@@ -111,10 +111,12 @@ struct ksmbd_startup_request {
__u32 smb2_max_credits; /* MAX credits */
__u32 smbd_max_io_size; /* smbd read write size */
__u32 max_connections; /* Number of maximum simultaneous connections */
- __u32 reserved[126]; /* Reserved room */
+ __s8 bind_interfaces_only;
+ __u32 max_ip_connections; /* Number of maximum connection per ip address */
+ __s8 reserved[499]; /* Reserved room */
__u32 ifc_list_sz; /* interfaces list size */
__s8 ____payload[];
-};
+} __packed;
#define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload)
diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c
index d7c676c151e2..4a71f46d7020 100644
--- a/fs/smb/server/ksmbd_work.c
+++ b/fs/smb/server/ksmbd_work.c
@@ -18,7 +18,7 @@ static struct workqueue_struct *ksmbd_wq;
struct ksmbd_work *ksmbd_alloc_work_struct(void)
{
- struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL);
+ struct ksmbd_work *work = kmem_cache_zalloc(work_cache, KSMBD_DEFAULT_GFP);
if (work) {
work->compound_fid = KSMBD_NO_FID;
@@ -26,11 +26,10 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void)
INIT_LIST_HEAD(&work->request_entry);
INIT_LIST_HEAD(&work->async_request_entry);
INIT_LIST_HEAD(&work->fp_entry);
- INIT_LIST_HEAD(&work->interim_entry);
INIT_LIST_HEAD(&work->aux_read_list);
work->iov_alloc_cnt = 4;
work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec),
- GFP_KERNEL);
+ KSMBD_DEFAULT_GFP);
if (!work->iov) {
kmem_cache_free(work_cache, work);
work = NULL;
@@ -56,8 +55,6 @@ void ksmbd_free_work_struct(struct ksmbd_work *work)
kfree(work->tr_buf);
kvfree(work->request_buf);
kfree(work->iov);
- if (!list_empty(&work->interim_entry))
- list_del(&work->interim_entry);
if (work->async_id)
ksmbd_release_id(&work->conn->async_ida, work->async_id);
@@ -81,7 +78,7 @@ int ksmbd_work_pool_init(void)
int ksmbd_workqueue_init(void)
{
- ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0);
+ ksmbd_wq = alloc_workqueue("ksmbd-io", WQ_PERCPU, 0);
if (!ksmbd_wq)
return -ENOMEM;
return 0;
@@ -114,7 +111,7 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
if (aux_size) {
need_iov_cnt++;
- ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL);
+ ar = kmalloc(sizeof(struct aux_read), KSMBD_DEFAULT_GFP);
if (!ar)
return -ENOMEM;
}
@@ -125,7 +122,7 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
work->iov_alloc_cnt += 4;
new = krealloc(work->iov,
sizeof(struct kvec) * work->iov_alloc_cnt,
- GFP_KERNEL | __GFP_ZERO);
+ KSMBD_DEFAULT_GFP | __GFP_ZERO);
if (!new) {
kfree(ar);
work->iov_alloc_cnt -= 4;
@@ -169,7 +166,7 @@ int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
int allocate_interim_rsp_buf(struct ksmbd_work *work)
{
- work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL);
+ work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, KSMBD_DEFAULT_GFP);
if (!work->response_buf)
return -ENOMEM;
work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;
diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h
index 8ca2c813246e..d36393ff8310 100644
--- a/fs/smb/server/ksmbd_work.h
+++ b/fs/smb/server/ksmbd_work.h
@@ -89,7 +89,6 @@ struct ksmbd_work {
/* List head at conn->async_requests */
struct list_head async_request_entry;
struct list_head fp_entry;
- struct list_head interim_entry;
};
/**
diff --git a/fs/smb/server/mgmt/ksmbd_ida.c b/fs/smb/server/mgmt/ksmbd_ida.c
index a18e27e9e0cd..0e2ae994ab52 100644
--- a/fs/smb/server/mgmt/ksmbd_ida.c
+++ b/fs/smb/server/mgmt/ksmbd_ida.c
@@ -4,31 +4,32 @@
*/
#include "ksmbd_ida.h"
+#include "../glob.h"
int ksmbd_acquire_smb2_tid(struct ida *ida)
{
- return ida_alloc_range(ida, 1, 0xFFFFFFFE, GFP_KERNEL);
+ return ida_alloc_range(ida, 1, 0xFFFFFFFE, KSMBD_DEFAULT_GFP);
}
int ksmbd_acquire_smb2_uid(struct ida *ida)
{
int id;
- id = ida_alloc_min(ida, 1, GFP_KERNEL);
+ id = ida_alloc_min(ida, 1, KSMBD_DEFAULT_GFP);
if (id == 0xFFFE)
- id = ida_alloc_min(ida, 1, GFP_KERNEL);
+ id = ida_alloc_min(ida, 1, KSMBD_DEFAULT_GFP);
return id;
}
int ksmbd_acquire_async_msg_id(struct ida *ida)
{
- return ida_alloc_min(ida, 1, GFP_KERNEL);
+ return ida_alloc_min(ida, 1, KSMBD_DEFAULT_GFP);
}
int ksmbd_acquire_id(struct ida *ida)
{
- return ida_alloc(ida, GFP_KERNEL);
+ return ida_alloc(ida, KSMBD_DEFAULT_GFP);
}
void ksmbd_release_id(struct ida *ida, int id)
diff --git a/fs/smb/server/mgmt/share_config.c b/fs/smb/server/mgmt/share_config.c
index d8d03070ae44..c9b1108d6e96 100644
--- a/fs/smb/server/mgmt/share_config.c
+++ b/fs/smb/server/mgmt/share_config.c
@@ -19,7 +19,7 @@
#include "../transport_ipc.h"
#include "../misc.h"
-#define SHARE_HASH_BITS 3
+#define SHARE_HASH_BITS 12
static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS);
static DECLARE_RWSEM(shares_table_lock);
@@ -102,11 +102,11 @@ static int parse_veto_list(struct ksmbd_share_config *share,
if (!sz)
break;
- p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL);
+ p = kzalloc(sizeof(struct ksmbd_veto_pattern), KSMBD_DEFAULT_GFP);
if (!p)
return -ENOMEM;
- p->pattern = kstrdup(veto_list, GFP_KERNEL);
+ p->pattern = kstrdup(veto_list, KSMBD_DEFAULT_GFP);
if (!p->pattern) {
kfree(p);
return -ENOMEM;
@@ -150,14 +150,14 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work,
goto out;
}
- share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL);
+ share = kzalloc(sizeof(struct ksmbd_share_config), KSMBD_DEFAULT_GFP);
if (!share)
goto out;
share->flags = resp->flags;
atomic_set(&share->refcount, 1);
INIT_LIST_HEAD(&share->veto_list);
- share->name = kstrdup(name, GFP_KERNEL);
+ share->name = kstrdup(name, KSMBD_DEFAULT_GFP);
if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
int path_len = PATH_MAX;
@@ -166,7 +166,7 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work,
path_len = resp->payload_sz - resp->veto_list_sz;
share->path = kstrndup(ksmbd_share_config_path(resp), path_len,
- GFP_KERNEL);
+ KSMBD_DEFAULT_GFP);
if (share->path) {
share->path_sz = strlen(share->path);
while (share->path_sz > 1 &&
diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c
index 94a52a75014a..d3483d9c757c 100644
--- a/fs/smb/server/mgmt/tree_connect.c
+++ b/fs/smb/server/mgmt/tree_connect.c
@@ -31,7 +31,8 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name)
if (!sc)
return status;
- tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL);
+ tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect),
+ KSMBD_DEFAULT_GFP);
if (!tree_conn) {
status.ret = -ENOMEM;
goto out_error;
@@ -77,10 +78,9 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name)
tree_conn->t_state = TREE_NEW;
status.tree_conn = tree_conn;
atomic_set(&tree_conn->refcount, 1);
- init_waitqueue_head(&tree_conn->refcount_q);
ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
- GFP_KERNEL));
+ KSMBD_DEFAULT_GFP));
if (ret) {
status.ret = -ENOMEM;
goto out_error;
@@ -99,14 +99,8 @@ out_error:
void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
{
- /*
- * Checking waitqueue to releasing tree connect on
- * tree disconnect. waitqueue_active is safe because it
- * uses atomic operation for condition.
- */
- if (!atomic_dec_return(&tcon->refcount) &&
- waitqueue_active(&tcon->refcount_q))
- wake_up(&tcon->refcount_q);
+ if (atomic_dec_and_test(&tcon->refcount))
+ kfree(tcon);
}
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
@@ -118,14 +112,11 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
xa_erase(&sess->tree_conns, tree_conn->id);
write_unlock(&sess->tree_conns_lock);
- if (!atomic_dec_and_test(&tree_conn->refcount))
- wait_event(tree_conn->refcount_q,
- atomic_read(&tree_conn->refcount) == 0);
-
ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
ksmbd_release_tree_conn_id(sess, tree_conn->id);
ksmbd_share_config_put(tree_conn->share_conf);
- kfree(tree_conn);
+ if (atomic_dec_and_test(&tree_conn->refcount))
+ kfree(tree_conn);
return ret;
}
diff --git a/fs/smb/server/mgmt/tree_connect.h b/fs/smb/server/mgmt/tree_connect.h
index a42cdd051041..f0023d86716f 100644
--- a/fs/smb/server/mgmt/tree_connect.h
+++ b/fs/smb/server/mgmt/tree_connect.h
@@ -33,7 +33,6 @@ struct ksmbd_tree_connect {
int maximal_access;
bool posix_extensions;
atomic_t refcount;
- wait_queue_head_t refcount_q;
unsigned int t_state;
};
diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c
index 421a4a95e216..56c9a38ca878 100644
--- a/fs/smb/server/mgmt/user_config.c
+++ b/fs/smb/server/mgmt/user_config.c
@@ -36,16 +36,16 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
{
struct ksmbd_user *user;
- user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL);
+ user = kmalloc(sizeof(struct ksmbd_user), KSMBD_DEFAULT_GFP);
if (!user)
return NULL;
- user->name = kstrdup(resp->account, GFP_KERNEL);
+ user->name = kstrdup(resp->account, KSMBD_DEFAULT_GFP);
user->flags = resp->status;
user->gid = resp->gid;
user->uid = resp->uid;
user->passkey_sz = resp->hash_sz;
- user->passkey = kmalloc(resp->hash_sz, GFP_KERNEL);
+ user->passkey = kmalloc(resp->hash_sz, KSMBD_DEFAULT_GFP);
if (user->passkey)
memcpy(user->passkey, resp->hash, resp->hash_sz);
@@ -64,7 +64,7 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
user->sgid = kmemdup(resp_ext->____payload,
resp_ext->ngroups * sizeof(gid_t),
- GFP_KERNEL);
+ KSMBD_DEFAULT_GFP);
if (!user->sgid)
goto err_free;
diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
index ad02fe555fda..1c181ef99929 100644
--- a/fs/smb/server/mgmt/user_session.c
+++ b/fs/smb/server/mgmt/user_session.c
@@ -18,7 +18,7 @@
static DEFINE_IDA(session_ida);
-#define SESSION_HASH_BITS 3
+#define SESSION_HASH_BITS 12
static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS);
static DECLARE_RWSEM(sessions_table_lock);
@@ -59,10 +59,12 @@ static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)
struct ksmbd_session_rpc *entry;
long index;
+ down_write(&sess->rpc_lock);
xa_for_each(&sess->rpc_handle_list, index, entry) {
xa_erase(&sess->rpc_handle_list, index);
__session_rpc_close(sess, entry);
}
+ up_write(&sess->rpc_lock);
xa_destroy(&sess->rpc_handle_list);
}
@@ -92,32 +94,38 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
{
struct ksmbd_session_rpc *entry, *old;
struct ksmbd_rpc_command *resp;
- int method;
+ int method, id;
method = __rpc_method(rpc_name);
if (!method)
return -EINVAL;
- entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL);
+ entry = kzalloc(sizeof(struct ksmbd_session_rpc), KSMBD_DEFAULT_GFP);
if (!entry)
return -ENOMEM;
entry->method = method;
- entry->id = ksmbd_ipc_id_alloc();
- if (entry->id < 0)
+ entry->id = id = ksmbd_ipc_id_alloc();
+ if (id < 0)
goto free_entry;
- old = xa_store(&sess->rpc_handle_list, entry->id, entry, GFP_KERNEL);
- if (xa_is_err(old))
+
+ down_write(&sess->rpc_lock);
+ old = xa_store(&sess->rpc_handle_list, id, entry, KSMBD_DEFAULT_GFP);
+ if (xa_is_err(old)) {
+ up_write(&sess->rpc_lock);
goto free_id;
+ }
- resp = ksmbd_rpc_open(sess, entry->id);
- if (!resp)
- goto erase_xa;
+ resp = ksmbd_rpc_open(sess, id);
+ if (!resp) {
+ xa_erase(&sess->rpc_handle_list, entry->id);
+ up_write(&sess->rpc_lock);
+ goto free_id;
+ }
+ up_write(&sess->rpc_lock);
kvfree(resp);
- return entry->id;
-erase_xa:
- xa_erase(&sess->rpc_handle_list, entry->id);
+ return id;
free_id:
ksmbd_rpc_id_free(entry->id);
free_entry:
@@ -129,16 +137,20 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id)
{
struct ksmbd_session_rpc *entry;
+ down_write(&sess->rpc_lock);
entry = xa_erase(&sess->rpc_handle_list, id);
if (entry)
__session_rpc_close(sess, entry);
+ up_write(&sess->rpc_lock);
}
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id)
{
struct ksmbd_session_rpc *entry;
+ lockdep_assert_held(&sess->rpc_lock);
entry = xa_load(&sess->rpc_handle_list, id);
+
return entry ? entry->method : 0;
}
@@ -181,7 +193,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
down_write(&sessions_table_lock);
down_write(&conn->session_lock);
xa_for_each(&conn->sessions, id, sess) {
- if (atomic_read(&sess->refcnt) == 0 &&
+ if (atomic_read(&sess->refcnt) <= 1 &&
(sess->state != SMB2_SESSION_VALID ||
time_after(jiffies,
sess->last_active + SMB2_SESSION_TIMEOUT))) {
@@ -201,7 +213,7 @@ int ksmbd_session_register(struct ksmbd_conn *conn,
sess->dialect = conn->dialect;
memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
ksmbd_expire_session(conn);
- return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL));
+ return xa_err(xa_store(&conn->sessions, sess->id, sess, KSMBD_DEFAULT_GFP));
}
static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)
@@ -230,7 +242,11 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
if (!ksmbd_chann_del(conn, sess) &&
xa_empty(&sess->ksmbd_chann_list)) {
hash_del(&sess->hlist);
- ksmbd_session_destroy(sess);
+ down_write(&conn->session_lock);
+ xa_erase(&conn->sessions, sess->id);
+ up_write(&conn->session_lock);
+ if (atomic_dec_and_test(&sess->refcnt))
+ ksmbd_session_destroy(sess);
}
}
}
@@ -249,13 +265,30 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
if (xa_empty(&sess->ksmbd_chann_list)) {
xa_erase(&conn->sessions, sess->id);
hash_del(&sess->hlist);
- ksmbd_session_destroy(sess);
+ if (atomic_dec_and_test(&sess->refcnt))
+ ksmbd_session_destroy(sess);
}
}
up_write(&conn->session_lock);
up_write(&sessions_table_lock);
}
+bool is_ksmbd_session_in_connection(struct ksmbd_conn *conn,
+ unsigned long long id)
+{
+ struct ksmbd_session *sess;
+
+ down_read(&conn->session_lock);
+ sess = xa_load(&conn->sessions, id);
+ if (sess) {
+ up_read(&conn->session_lock);
+ return true;
+ }
+ up_read(&conn->session_lock);
+
+ return false;
+}
+
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
unsigned long long id)
{
@@ -263,8 +296,10 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
down_read(&conn->session_lock);
sess = xa_load(&conn->sessions, id);
- if (sess)
+ if (sess) {
sess->last_active = jiffies;
+ ksmbd_user_session_get(sess);
+ }
up_read(&conn->session_lock);
return sess;
}
@@ -275,6 +310,8 @@ struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
down_read(&sessions_table_lock);
sess = __session_lookup(id);
+ if (sess)
+ ksmbd_user_session_get(sess);
up_read(&sessions_table_lock);
return sess;
@@ -305,8 +342,8 @@ void ksmbd_user_session_put(struct ksmbd_session *sess)
if (atomic_read(&sess->refcnt) <= 0)
WARN_ON(1);
- else
- atomic_dec(&sess->refcnt);
+ else if (atomic_dec_and_test(&sess->refcnt))
+ ksmbd_session_destroy(sess);
}
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
@@ -314,7 +351,7 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
{
struct preauth_session *sess;
- sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL);
+ sess = kmalloc(sizeof(struct preauth_session), KSMBD_DEFAULT_GFP);
if (!sess)
return NULL;
@@ -349,13 +386,13 @@ void destroy_previous_session(struct ksmbd_conn *conn,
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_RECONNECT);
err = ksmbd_conn_wait_idle_sess_id(conn, id);
if (err) {
- ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
+ ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
goto out;
}
ksmbd_destroy_file_table(&prev_sess->file_table);
prev_sess->state = SMB2_SESSION_EXPIRED;
- ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
+ ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
ksmbd_launch_ksmbd_durable_scavenger();
out:
up_write(&conn->session_lock);
@@ -398,7 +435,7 @@ static struct ksmbd_session *__session_create(int protocol)
if (protocol != CIFDS_SESSION_FLAG_SMB2)
return NULL;
- sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL);
+ sess = kzalloc(sizeof(struct ksmbd_session), KSMBD_DEFAULT_GFP);
if (!sess)
return NULL;
@@ -413,7 +450,8 @@ static struct ksmbd_session *__session_create(int protocol)
xa_init(&sess->rpc_handle_list);
sess->sequence_number = 1;
rwlock_init(&sess->tree_conns_lock);
- atomic_set(&sess->refcnt, 1);
+ atomic_set(&sess->refcnt, 2);
+ init_rwsem(&sess->rpc_lock);
ret = __init_smb2_session(sess);
if (ret)
diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h
index c1c4b20bd5c6..c5749d6ec715 100644
--- a/fs/smb/server/mgmt/user_session.h
+++ b/fs/smb/server/mgmt/user_session.h
@@ -63,6 +63,7 @@ struct ksmbd_session {
rwlock_t tree_conns_lock;
atomic_t refcnt;
+ struct rw_semaphore rpc_lock;
};
static inline int test_session_flag(struct ksmbd_session *sess, int bit)
@@ -87,6 +88,8 @@ void ksmbd_session_destroy(struct ksmbd_session *sess);
struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id);
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
unsigned long long id);
+bool is_ksmbd_session_in_connection(struct ksmbd_conn *conn,
+ unsigned long long id);
int ksmbd_session_register(struct ksmbd_conn *conn,
struct ksmbd_session *sess);
void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c
index 1a5faa6f6e7b..a543ec9d3581 100644
--- a/fs/smb/server/misc.c
+++ b/fs/smb/server/misc.c
@@ -164,8 +164,10 @@ char *convert_to_nt_pathname(struct ksmbd_share_config *share,
{
char *pathname, *ab_pathname, *nt_pathname;
int share_path_len = share->path_sz;
+ size_t ab_pathname_len;
+ int prefix;
- pathname = kmalloc(PATH_MAX, GFP_KERNEL);
+ pathname = kmalloc(PATH_MAX, KSMBD_DEFAULT_GFP);
if (!pathname)
return ERR_PTR(-EACCES);
@@ -180,14 +182,18 @@ char *convert_to_nt_pathname(struct ksmbd_share_config *share,
goto free_pathname;
}
- nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, GFP_KERNEL);
+ ab_pathname_len = strlen(&ab_pathname[share_path_len]);
+ prefix = ab_pathname[share_path_len] == '\0' ? 1 : 0;
+ nt_pathname = kmalloc(prefix + ab_pathname_len + 1, KSMBD_DEFAULT_GFP);
if (!nt_pathname) {
nt_pathname = ERR_PTR(-ENOMEM);
goto free_pathname;
}
- if (ab_pathname[share_path_len] == '\0')
- strcpy(nt_pathname, "/");
- strcat(nt_pathname, &ab_pathname[share_path_len]);
+
+ if (prefix)
+ *nt_pathname = '/';
+ memcpy(nt_pathname + prefix, &ab_pathname[share_path_len],
+ ab_pathname_len + 1);
ksmbd_conv_path_to_windows(nt_pathname);
@@ -232,7 +238,7 @@ char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name)
char *cf_name;
int cf_len;
- cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, GFP_KERNEL);
+ cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, KSMBD_DEFAULT_GFP);
if (!cf_name)
return ERR_PTR(-ENOMEM);
@@ -294,7 +300,7 @@ char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
path_len = share->path_sz;
name_len = strlen(name);
- new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL);
+ new_name = kmalloc(path_len + name_len + 2, KSMBD_DEFAULT_GFP);
if (!new_name)
return new_name;
@@ -320,7 +326,7 @@ char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
if (!sz)
return NULL;
- conv = kmalloc(sz, GFP_KERNEL);
+ conv = kmalloc(sz, KSMBD_DEFAULT_GFP);
if (!conv)
return NULL;
diff --git a/fs/smb/server/ndr.c b/fs/smb/server/ndr.c
index 3507d8f89074..58d71560f626 100644
--- a/fs/smb/server/ndr.c
+++ b/fs/smb/server/ndr.c
@@ -18,7 +18,7 @@ static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz)
{
char *data;
- data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL);
+ data = krealloc(n->data, n->offset + sz + 1024, KSMBD_DEFAULT_GFP);
if (!data)
return -ENOMEM;
@@ -174,7 +174,7 @@ int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)
n->offset = 0;
n->length = 1024;
- n->data = kzalloc(n->length, GFP_KERNEL);
+ n->data = kzalloc(n->length, KSMBD_DEFAULT_GFP);
if (!n->data)
return -ENOMEM;
@@ -350,7 +350,7 @@ int ndr_encode_posix_acl(struct ndr *n,
n->offset = 0;
n->length = 1024;
- n->data = kzalloc(n->length, GFP_KERNEL);
+ n->data = kzalloc(n->length, KSMBD_DEFAULT_GFP);
if (!n->data)
return -ENOMEM;
@@ -401,7 +401,7 @@ int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
n->offset = 0;
n->length = 2048;
- n->data = kzalloc(n->length, GFP_KERNEL);
+ n->data = kzalloc(n->length, KSMBD_DEFAULT_GFP);
if (!n->data)
return -ENOMEM;
@@ -505,7 +505,7 @@ int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
return ret;
acl->sd_size = n->length - n->offset;
- acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL);
+ acl->sd_buf = kzalloc(acl->sd_size, KSMBD_DEFAULT_GFP);
if (!acl->sd_buf)
return -ENOMEM;
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index 4142c7ad5fa9..1f07ebf431d7 100644
--- a/fs/smb/server/oplock.c
+++ b/fs/smb/server/oplock.c
@@ -34,7 +34,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
struct ksmbd_session *sess = work->sess;
struct oplock_info *opinfo;
- opinfo = kzalloc(sizeof(struct oplock_info), GFP_KERNEL);
+ opinfo = kzalloc(sizeof(struct oplock_info), KSMBD_DEFAULT_GFP);
if (!opinfo)
return NULL;
@@ -46,7 +46,6 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
opinfo->fid = id;
opinfo->Tid = Tid;
INIT_LIST_HEAD(&opinfo->op_entry);
- INIT_LIST_HEAD(&opinfo->interim_list);
init_waitqueue_head(&opinfo->oplock_q);
init_waitqueue_head(&opinfo->oplock_brk);
atomic_set(&opinfo->refcount, 1);
@@ -94,7 +93,7 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx)
{
struct lease *lease;
- lease = kmalloc(sizeof(struct lease), GFP_KERNEL);
+ lease = kmalloc(sizeof(struct lease), KSMBD_DEFAULT_GFP);
if (!lease)
return -ENOMEM;
@@ -130,14 +129,6 @@ static void free_opinfo(struct oplock_info *opinfo)
kfree(opinfo);
}
-static inline void opinfo_free_rcu(struct rcu_head *rcu_head)
-{
- struct oplock_info *opinfo;
-
- opinfo = container_of(rcu_head, struct oplock_info, rcu_head);
- free_opinfo(opinfo);
-}
-
struct oplock_info *opinfo_get(struct ksmbd_file *fp)
{
struct oplock_info *opinfo;
@@ -155,12 +146,9 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
{
struct oplock_info *opinfo;
- if (list_empty(&ci->m_op_list))
- return NULL;
-
- rcu_read_lock();
- opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
- op_entry);
+ down_read(&ci->m_lock);
+ opinfo = list_first_entry_or_null(&ci->m_op_list, struct oplock_info,
+ op_entry);
if (opinfo) {
if (opinfo->conn == NULL ||
!atomic_inc_not_zero(&opinfo->refcount))
@@ -172,8 +160,7 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
}
}
}
-
- rcu_read_unlock();
+ up_read(&ci->m_lock);
return opinfo;
}
@@ -186,7 +173,7 @@ void opinfo_put(struct oplock_info *opinfo)
if (!atomic_dec_and_test(&opinfo->refcount))
return;
- call_rcu(&opinfo->rcu_head, opinfo_free_rcu);
+ free_opinfo(opinfo);
}
static void opinfo_add(struct oplock_info *opinfo)
@@ -194,7 +181,7 @@ static void opinfo_add(struct oplock_info *opinfo)
struct ksmbd_inode *ci = opinfo->o_fp->f_ci;
down_write(&ci->m_lock);
- list_add_rcu(&opinfo->op_entry, &ci->m_op_list);
+ list_add(&opinfo->op_entry, &ci->m_op_list);
up_write(&ci->m_lock);
}
@@ -208,7 +195,7 @@ static void opinfo_del(struct oplock_info *opinfo)
write_unlock(&lease_list_lock);
}
down_write(&ci->m_lock);
- list_del_rcu(&opinfo->op_entry);
+ list_del(&opinfo->op_entry);
up_write(&ci->m_lock);
}
@@ -635,6 +622,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
{
struct smb2_oplock_break *rsp = NULL;
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
+ struct ksmbd_conn *conn = work->conn;
struct oplock_break_info *br_info = work->request_buf;
struct smb2_hdr *rsp_hdr;
struct ksmbd_file *fp;
@@ -690,6 +678,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
out:
ksmbd_free_work_struct(work);
+ ksmbd_conn_r_count_dec(conn);
}
/**
@@ -709,7 +698,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
if (!work)
return -ENOMEM;
- br_info = kmalloc(sizeof(struct oplock_break_info), GFP_KERNEL);
+ br_info = kmalloc(sizeof(struct oplock_break_info), KSMBD_DEFAULT_GFP);
if (!br_info) {
ksmbd_free_work_struct(work);
return -ENOMEM;
@@ -723,6 +712,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
work->conn = conn;
work->sess = opinfo->sess;
+ ksmbd_conn_r_count_inc(conn);
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
INIT_WORK(&work->work, __smb2_oplock_break_noti);
ksmbd_queue_work(work);
@@ -745,6 +735,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
{
struct smb2_lease_break *rsp = NULL;
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
+ struct ksmbd_conn *conn = work->conn;
struct lease_break_info *br_info = work->request_buf;
struct smb2_hdr *rsp_hdr;
@@ -791,6 +782,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
out:
ksmbd_free_work_struct(work);
+ ksmbd_conn_r_count_dec(conn);
}
/**
@@ -803,7 +795,6 @@ out:
static int smb2_lease_break_noti(struct oplock_info *opinfo)
{
struct ksmbd_conn *conn = opinfo->conn;
- struct list_head *tmp, *t;
struct ksmbd_work *work;
struct lease_break_info *br_info;
struct lease *lease = opinfo->o_lease;
@@ -812,7 +803,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
if (!work)
return -ENOMEM;
- br_info = kmalloc(sizeof(struct lease_break_info), GFP_KERNEL);
+ br_info = kmalloc(sizeof(struct lease_break_info), KSMBD_DEFAULT_GFP);
if (!br_info) {
ksmbd_free_work_struct(work);
return -ENOMEM;
@@ -830,17 +821,8 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
work->conn = conn;
work->sess = opinfo->sess;
+ ksmbd_conn_r_count_inc(conn);
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
- list_for_each_safe(tmp, t, &opinfo->interim_list) {
- struct ksmbd_work *in_work;
-
- in_work = list_entry(tmp, struct ksmbd_work,
- interim_entry);
- setup_async_work(in_work, NULL, NULL);
- smb2_send_interim_resp(in_work, STATUS_PENDING);
- list_del_init(&in_work->interim_entry);
- release_async_work(in_work);
- }
INIT_WORK(&work->work, __smb2_lease_break_noti);
ksmbd_queue_work(work);
wait_for_break_ack(opinfo);
@@ -871,7 +853,8 @@ static void wait_lease_breaking(struct oplock_info *opinfo)
}
}
-static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
+static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level,
+ struct ksmbd_work *in_work)
{
int err = 0;
@@ -914,9 +897,15 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
}
if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE |
- SMB2_LEASE_HANDLE_CACHING_LE))
+ SMB2_LEASE_HANDLE_CACHING_LE)) {
+ if (in_work) {
+ setup_async_work(in_work, NULL, NULL);
+ smb2_send_interim_resp(in_work, STATUS_PENDING);
+ release_async_work(in_work);
+ }
+
brk_opinfo->op_state = OPLOCK_ACK_WAIT;
- else
+ } else
atomic_dec(&brk_opinfo->breaking_cnt);
} else {
err = oplock_break_pending(brk_opinfo, req_op_level);
@@ -1057,7 +1046,7 @@ static int add_lease_global_list(struct oplock_info *opinfo)
}
read_unlock(&lease_list_lock);
- lb = kmalloc(sizeof(struct lease_table), GFP_KERNEL);
+ lb = kmalloc(sizeof(struct lease_table), KSMBD_DEFAULT_GFP);
if (!lb)
return -ENOMEM;
@@ -1113,10 +1102,12 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
if (!atomic_inc_not_zero(&opinfo->refcount))
continue;
- if (ksmbd_conn_releasing(opinfo->conn))
+ if (ksmbd_conn_releasing(opinfo->conn)) {
+ opinfo_put(opinfo);
continue;
+ }
- oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
+ oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL);
opinfo_put(opinfo);
}
}
@@ -1150,9 +1141,12 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
if (!atomic_inc_not_zero(&opinfo->refcount))
continue;
- if (ksmbd_conn_releasing(opinfo->conn))
+ if (ksmbd_conn_releasing(opinfo->conn)) {
+ opinfo_put(opinfo);
continue;
- oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
+ }
+
+ oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL);
opinfo_put(opinfo);
}
}
@@ -1252,8 +1246,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
goto op_break_not_needed;
}
- list_add(&work->interim_entry, &prev_opinfo->interim_list);
- err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II);
+ err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II, work);
opinfo_put(prev_opinfo);
if (err == -ENOENT)
goto set_lev;
@@ -1322,8 +1315,7 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work,
}
brk_opinfo->open_trunc = is_trunc;
- list_add(&work->interim_entry, &brk_opinfo->interim_list);
- oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II);
+ oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work);
opinfo_put(brk_opinfo);
}
@@ -1348,18 +1340,19 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
ci = fp->f_ci;
op = opinfo_get(fp);
- rcu_read_lock();
- list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
+ down_read(&ci->m_lock);
+ list_for_each_entry(brk_op, &ci->m_op_list, op_entry) {
if (brk_op->conn == NULL)
continue;
if (!atomic_inc_not_zero(&brk_op->refcount))
continue;
- if (ksmbd_conn_releasing(brk_op->conn))
+ if (ksmbd_conn_releasing(brk_op->conn)) {
+ opinfo_put(brk_op);
continue;
+ }
- rcu_read_unlock();
if (brk_op->is_lease && (brk_op->o_lease->state &
(~(SMB2_LEASE_READ_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE)))) {
@@ -1386,12 +1379,11 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
SMB2_LEASE_KEY_SIZE))
goto next;
brk_op->open_trunc = is_trunc;
- oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE);
+ oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE, NULL);
next:
opinfo_put(brk_op);
- rcu_read_lock();
}
- rcu_read_unlock();
+ up_read(&ci->m_lock);
if (op)
opinfo_put(op);
@@ -1499,13 +1491,17 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
if (IS_ERR_OR_NULL(cc))
return NULL;
- lreq = kzalloc(sizeof(struct lease_ctx_info), GFP_KERNEL);
+ lreq = kzalloc(sizeof(struct lease_ctx_info), KSMBD_DEFAULT_GFP);
if (!lreq)
return NULL;
if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) {
struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
+ if (le16_to_cpu(cc->DataOffset) + le32_to_cpu(cc->DataLength) <
+ sizeof(struct create_lease_v2) - 4)
+ goto err_out;
+
memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
lreq->req_state = lc->lcontext.LeaseState;
lreq->flags = lc->lcontext.LeaseFlags;
@@ -1518,6 +1514,10 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
} else {
struct create_lease *lc = (struct create_lease *)cc;
+ if (le16_to_cpu(cc->DataOffset) + le32_to_cpu(cc->DataLength) <
+ sizeof(struct create_lease))
+ goto err_out;
+
memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
lreq->req_state = lc->lcontext.LeaseState;
lreq->flags = lc->lcontext.LeaseFlags;
@@ -1525,6 +1525,9 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
lreq->version = 1;
}
return lreq;
+err_out:
+ kfree(lreq);
+ return NULL;
}
/**
@@ -1614,9 +1617,9 @@ void create_durable_rsp_buf(char *cc)
*/
void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
{
- struct create_durable_v2_rsp *buf;
+ struct create_durable_rsp_v2 *buf;
- buf = (struct create_durable_v2_rsp *)cc;
+ buf = (struct create_durable_rsp_v2 *)cc;
memset(buf, 0, sizeof(struct create_durable_rsp));
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_durable_rsp, Data));
@@ -1630,9 +1633,9 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
buf->Name[2] = '2';
buf->Name[3] = 'Q';
- buf->Timeout = cpu_to_le32(fp->durable_timeout);
+ buf->dcontext.Timeout = cpu_to_le32(fp->durable_timeout);
if (fp->is_persistent)
- buf->Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
+ buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
}
/**
diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h
index 72bc88a63a40..9a56eaadd0dd 100644
--- a/fs/smb/server/oplock.h
+++ b/fs/smb/server/oplock.h
@@ -67,12 +67,10 @@ struct oplock_info {
bool is_lease;
bool open_trunc; /* truncate on open */
struct lease *o_lease;
- struct list_head interim_list;
struct list_head op_entry;
struct list_head lease_entry;
wait_queue_head_t oplock_q; /* Other server threads */
wait_queue_head_t oplock_brk; /* oplock breaking wait */
- struct rcu_head rcu_head;
};
struct lease_break_info {
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index e6cfedba9992..3cea16050e4f 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -47,7 +47,7 @@ static int ___server_conf_set(int idx, char *val)
return -EINVAL;
kfree(server_conf.conf[idx]);
- server_conf.conf[idx] = kstrdup(val, GFP_KERNEL);
+ server_conf.conf[idx] = kstrdup(val, KSMBD_DEFAULT_GFP);
if (!server_conf.conf[idx])
return -ENOMEM;
return 0;
@@ -241,14 +241,14 @@ send:
if (work->tcon)
ksmbd_tree_connect_put(work->tcon);
smb3_preauth_hash_rsp(work);
- if (work->sess)
- ksmbd_user_session_put(work->sess);
if (work->sess && work->sess->enc && work->encrypted &&
conn->ops->encrypt_resp) {
rc = conn->ops->encrypt_resp(work);
if (rc < 0)
conn->ops->set_rsp_status(work, STATUS_DATA_ERROR);
}
+ if (work->sess)
+ ksmbd_user_session_put(work->sess);
ksmbd_conn_write(work);
}
@@ -270,14 +270,7 @@ static void handle_ksmbd_work(struct work_struct *wk)
ksmbd_conn_try_dequeue_request(work);
ksmbd_free_work_struct(work);
- atomic_dec(&conn->mux_smb_requests);
- /*
- * Checking waitqueue to dropping pending requests on
- * disconnection. waitqueue_active is safe because it
- * uses atomic operation for condition.
- */
- if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
- wake_up(&conn->r_count_q);
+ ksmbd_conn_r_count_dec(conn);
}
/**
@@ -296,11 +289,6 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn)
if (err)
return 0;
- if (atomic_inc_return(&conn->mux_smb_requests) >= conn->vals->max_credits) {
- atomic_dec_return(&conn->mux_smb_requests);
- return -ENOSPC;
- }
-
work = ksmbd_alloc_work_struct();
if (!work) {
pr_err("allocation for work failed\n");
@@ -312,7 +300,7 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn)
conn->request_buf = NULL;
ksmbd_conn_enqueue_request(work);
- atomic_inc(&conn->r_count);
+ ksmbd_conn_r_count_inc(conn);
/* update activity on connection */
conn->last_active = jiffies;
INIT_WORK(&work->work, handle_ksmbd_work);
@@ -363,6 +351,7 @@ static int server_conf_init(void)
server_conf.auth_mechs |= KSMBD_AUTH_KRB5 |
KSMBD_AUTH_MSKRB5;
#endif
+ server_conf.max_inflight_req = SMB2_MAX_CREDITS;
return 0;
}
@@ -376,6 +365,7 @@ static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl)
return;
}
+ pr_info("running\n");
WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING);
}
@@ -415,7 +405,7 @@ static int __queue_ctrl_work(int type)
{
struct server_ctrl_struct *ctrl;
- ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL);
+ ctrl = kmalloc(sizeof(struct server_ctrl_struct), KSMBD_DEFAULT_GFP);
if (!ctrl)
return -ENOMEM;
@@ -632,16 +622,11 @@ MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>");
MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER");
MODULE_LICENSE("GPL");
MODULE_SOFTDEP("pre: ecb");
-MODULE_SOFTDEP("pre: hmac");
-MODULE_SOFTDEP("pre: md5");
MODULE_SOFTDEP("pre: nls");
MODULE_SOFTDEP("pre: aes");
MODULE_SOFTDEP("pre: cmac");
-MODULE_SOFTDEP("pre: sha256");
-MODULE_SOFTDEP("pre: sha512");
MODULE_SOFTDEP("pre: aead2");
MODULE_SOFTDEP("pre: ccm");
MODULE_SOFTDEP("pre: gcm");
-MODULE_SOFTDEP("pre: crc32");
module_init(ksmbd_server_init)
module_exit(ksmbd_server_exit)
diff --git a/fs/smb/server/server.h b/fs/smb/server/server.h
index 4fc529335271..b8a7317be86b 100644
--- a/fs/smb/server/server.h
+++ b/fs/smb/server/server.h
@@ -42,9 +42,12 @@ struct ksmbd_server_config {
struct smb_sid domain_sid;
unsigned int auth_mechs;
unsigned int max_connections;
+ unsigned int max_inflight_req;
+ unsigned int max_ip_connections;
char *conf[SERVER_CONF_WORK_GROUP + 1];
struct task_struct *dh_task;
+ bool bind_interfaces_only;
};
extern struct ksmbd_server_config server_conf;
diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c
index ae501024665e..67a2d7a793f6 100644
--- a/fs/smb/server/smb2misc.c
+++ b/fs/smb/server/smb2misc.c
@@ -460,7 +460,7 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
}
validate_credit:
- if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
+ if ((work->conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
smb2_validate_credit_charge(work->conn, hdr))
return 1;
diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c
index 606aa3c5189a..edd7eca0714a 100644
--- a/fs/smb/server/smb2ops.c
+++ b/fs/smb/server/smb2ops.c
@@ -15,7 +15,7 @@
static struct smb_version_values smb21_server_values = {
.version_string = SMB21_VERSION_STRING,
.protocol_id = SMB21_PROT_ID,
- .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+ .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB21_DEFAULT_IOSIZE,
.max_write_size = SMB21_DEFAULT_IOSIZE,
.max_trans_size = SMB21_DEFAULT_IOSIZE,
@@ -41,7 +41,7 @@ static struct smb_version_values smb21_server_values = {
static struct smb_version_values smb30_server_values = {
.version_string = SMB30_VERSION_STRING,
.protocol_id = SMB30_PROT_ID,
- .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+ .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB3_DEFAULT_IOSIZE,
.max_write_size = SMB3_DEFAULT_IOSIZE,
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
@@ -59,7 +59,7 @@ static struct smb_version_values smb30_server_values = {
.cap_large_files = SMB2_LARGE_FILES,
.create_lease_size = sizeof(struct create_lease_v2),
.create_durable_size = sizeof(struct create_durable_rsp),
- .create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
+ .create_durable_v2_size = sizeof(struct create_durable_rsp_v2),
.create_mxac_size = sizeof(struct create_mxac_rsp),
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
.create_posix_size = sizeof(struct create_posix_rsp),
@@ -68,7 +68,7 @@ static struct smb_version_values smb30_server_values = {
static struct smb_version_values smb302_server_values = {
.version_string = SMB302_VERSION_STRING,
.protocol_id = SMB302_PROT_ID,
- .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+ .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB3_DEFAULT_IOSIZE,
.max_write_size = SMB3_DEFAULT_IOSIZE,
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
@@ -86,7 +86,7 @@ static struct smb_version_values smb302_server_values = {
.cap_large_files = SMB2_LARGE_FILES,
.create_lease_size = sizeof(struct create_lease_v2),
.create_durable_size = sizeof(struct create_durable_rsp),
- .create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
+ .create_durable_v2_size = sizeof(struct create_durable_rsp_v2),
.create_mxac_size = sizeof(struct create_mxac_rsp),
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
.create_posix_size = sizeof(struct create_posix_rsp),
@@ -95,7 +95,7 @@ static struct smb_version_values smb302_server_values = {
static struct smb_version_values smb311_server_values = {
.version_string = SMB311_VERSION_STRING,
.protocol_id = SMB311_PROT_ID,
- .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
+ .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
.max_read_size = SMB3_DEFAULT_IOSIZE,
.max_write_size = SMB3_DEFAULT_IOSIZE,
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
@@ -113,7 +113,7 @@ static struct smb_version_values smb311_server_values = {
.cap_large_files = SMB2_LARGE_FILES,
.create_lease_size = sizeof(struct create_lease_v2),
.create_durable_size = sizeof(struct create_durable_rsp),
- .create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
+ .create_durable_v2_size = sizeof(struct create_durable_rsp_v2),
.create_mxac_size = sizeof(struct create_mxac_rsp),
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
.create_posix_size = sizeof(struct create_posix_rsp),
@@ -204,7 +204,7 @@ void init_smb2_1_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256_LE;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING;
}
/**
@@ -221,20 +221,20 @@ void init_smb3_0_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING |
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
(!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION))
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
}
/**
@@ -251,19 +251,19 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING |
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
(!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION))
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
}
/**
@@ -280,14 +280,14 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING |
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
- conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
+ conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
INIT_LIST_HEAD(&conn->preauth_sess_table);
return 0;
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 599118aed205..f3184b217575 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -23,6 +23,7 @@
#include "asn1.h"
#include "connection.h"
#include "transport_ipc.h"
+#include "../common/smbdirect/smbdirect.h"
#include "transport_rdma.h"
#include "vfs.h"
#include "vfs_cache.h"
@@ -38,6 +39,7 @@
#include "mgmt/user_session.h"
#include "mgmt/ksmbd_ida.h"
#include "ndr.h"
+#include "transport_tcp.h"
static void __wbuf(struct ksmbd_work *work, void **req, void **rsp)
{
@@ -67,8 +69,10 @@ static inline bool check_session_id(struct ksmbd_conn *conn, u64 id)
return false;
sess = ksmbd_session_lookup_all(conn, id);
- if (sess)
+ if (sess) {
+ ksmbd_user_session_put(sess);
return true;
+ }
pr_err("Invalid user session id: %llu\n", id);
return false;
}
@@ -278,7 +282,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work)
/* Not setting conn guid rsp->ServerGUID, as it
* not used by client for identifying connection
*/
- rsp->Capabilities = cpu_to_le32(conn->vals->capabilities);
+ rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities);
/* Default Max Message Size till SMB2.0, 64K*/
rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size);
rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size);
@@ -551,7 +555,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
if (le32_to_cpu(hdr->NextCommand) > 0)
sz = large_sz;
- work->response_buf = kvzalloc(sz, GFP_KERNEL);
+ work->response_buf = kvzalloc(sz, KSMBD_DEFAULT_GFP);
if (!work->response_buf)
return -ENOMEM;
@@ -605,10 +609,8 @@ int smb2_check_user_session(struct ksmbd_work *work)
/* Check for validity of user session */
work->sess = ksmbd_session_lookup_all(conn, sess_id);
- if (work->sess) {
- ksmbd_user_session_get(work->sess);
+ if (work->sess)
return 1;
- }
ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id);
return -ENOENT;
}
@@ -632,6 +634,11 @@ smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls)
return name;
}
+ if (*name == '\0') {
+ kfree(name);
+ return ERR_PTR(-EINVAL);
+ }
+
if (*name == '\\') {
pr_err("not allow directory name included leading slash\n");
kfree(name);
@@ -695,6 +702,9 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status)
struct smb2_hdr *rsp_hdr;
struct ksmbd_work *in_work = ksmbd_alloc_work_struct();
+ if (!in_work)
+ return;
+
if (allocate_interim_rsp_buf(in_work)) {
pr_err("smb_allocate_rsp_buf failed!\n");
ksmbd_free_work_struct(in_work);
@@ -886,7 +896,7 @@ static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn,
return STATUS_INVALID_PARAMETER;
if (pneg_ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512)
- return STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP;
+ return STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP;
conn->preauth_info->Preauth_HashId = SMB2_PREAUTH_INTEGRITY_SHA512;
return STATUS_SUCCESS;
@@ -946,7 +956,7 @@ bool smb3_encryption_negotiated(struct ksmbd_conn *conn)
* SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag.
* SMB 3.1.1 uses the cipher_type field.
*/
- return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) ||
+ return (conn->vals->req_capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) ||
conn->cipher_type;
}
@@ -1097,6 +1107,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
return rc;
}
+ ksmbd_conn_lock(conn);
smb2_buf_len = get_rfc1002_len(work->request_buf);
smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects);
if (smb2_neg_size > smb2_buf_len) {
@@ -1147,7 +1158,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
case SMB311_PROT_ID:
conn->preauth_info =
kzalloc(sizeof(struct preauth_integrity_info),
- GFP_KERNEL);
+ KSMBD_DEFAULT_GFP);
if (!conn->preauth_info) {
rc = -ENOMEM;
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
@@ -1199,7 +1210,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
rc = -EINVAL;
goto err_out;
}
- rsp->Capabilities = cpu_to_le32(conn->vals->capabilities);
+ rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities);
/* For stats */
conn->connection_type = conn->dialect;
@@ -1244,9 +1255,10 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
}
conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode);
- ksmbd_conn_set_need_negotiate(conn);
+ ksmbd_conn_set_need_setup(conn);
err_out:
+ ksmbd_conn_unlock(conn);
if (rc)
rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -1265,8 +1277,11 @@ static int alloc_preauth_hash(struct ksmbd_session *sess,
if (sess->Preauth_HashValue)
return 0;
+ if (!conn->preauth_info)
+ return -ENOMEM;
+
sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue,
- PREAUTH_HASHVALUE_SIZE, GFP_KERNEL);
+ PREAUTH_HASHVALUE_SIZE, KSMBD_DEFAULT_GFP);
if (!sess->Preauth_HashValue)
return -ENOMEM;
@@ -1352,7 +1367,7 @@ static int ntlm_negotiate(struct ksmbd_work *work,
sz = sizeof(struct challenge_message);
sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6;
- neg_blob = kzalloc(sz, GFP_KERNEL);
+ neg_blob = kzalloc(sz, KSMBD_DEFAULT_GFP);
if (!neg_blob)
return -ENOMEM;
@@ -1436,7 +1451,7 @@ static int ntlm_authenticate(struct ksmbd_work *work,
{
struct ksmbd_conn *conn = work->conn;
struct ksmbd_session *sess = work->sess;
- struct channel *chann = NULL;
+ struct channel *chann = NULL, *old;
struct ksmbd_user *user;
u64 prev_id;
int sz, rc;
@@ -1523,12 +1538,7 @@ static int ntlm_authenticate(struct ksmbd_work *work,
if (smb3_encryption_negotiated(conn) &&
!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
- rc = conn->ops->generate_encryptionkey(conn, sess);
- if (rc) {
- ksmbd_debug(SMB,
- "SMB3 encryption key generation failed\n");
- return -EINVAL;
- }
+ conn->ops->generate_encryptionkey(conn, sess);
sess->enc = true;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)
rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
@@ -1543,12 +1553,17 @@ binding_session:
if (conn->dialect >= SMB30_PROT_ID) {
chann = lookup_chann_list(sess, conn);
if (!chann) {
- chann = kmalloc(sizeof(struct channel), GFP_KERNEL);
+ chann = kmalloc(sizeof(struct channel), KSMBD_DEFAULT_GFP);
if (!chann)
return -ENOMEM;
chann->conn = conn;
- xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL);
+ old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann,
+ KSMBD_DEFAULT_GFP);
+ if (xa_is_err(old)) {
+ kfree(chann);
+ return xa_err(old);
+ }
}
}
@@ -1575,7 +1590,7 @@ static int krb5_authenticate(struct ksmbd_work *work,
struct ksmbd_conn *conn = work->conn;
struct ksmbd_session *sess = work->sess;
char *in_blob, *out_blob;
- struct channel *chann = NULL;
+ struct channel *chann = NULL, *old;
u64 prev_sess_id;
int in_len, out_len;
int retval;
@@ -1588,48 +1603,60 @@ static int krb5_authenticate(struct ksmbd_work *work,
out_len = work->response_sz -
(le16_to_cpu(rsp->SecurityBufferOffset) + 4);
- /* Check previous session */
- prev_sess_id = le64_to_cpu(req->PreviousSessionId);
- if (prev_sess_id && prev_sess_id != sess->id)
- destroy_previous_session(conn, sess->user, prev_sess_id);
-
- if (sess->state == SMB2_SESSION_VALID)
- ksmbd_free_user(sess->user);
-
retval = ksmbd_krb5_authenticate(sess, in_blob, in_len,
out_blob, &out_len);
if (retval) {
ksmbd_debug(SMB, "krb5 authentication failed\n");
return -EINVAL;
}
+
+ /* Check previous session */
+ prev_sess_id = le64_to_cpu(req->PreviousSessionId);
+ if (prev_sess_id && prev_sess_id != sess->id)
+ destroy_previous_session(conn, sess->user, prev_sess_id);
+
rsp->SecurityBufferLength = cpu_to_le16(out_len);
- if ((conn->sign || server_conf.enforced_signing) ||
+ /*
+ * If session state is SMB2_SESSION_VALID, We can assume
+ * that it is reauthentication. And the user/password
+ * has been verified, so return it here.
+ */
+ if (sess->state == SMB2_SESSION_VALID) {
+ if (conn->binding)
+ goto binding_session;
+ return 0;
+ }
+
+ if ((rsp->SessionFlags != SMB2_SESSION_FLAG_IS_GUEST_LE &&
+ (conn->sign || server_conf.enforced_signing)) ||
(req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED))
sess->sign = true;
- if (smb3_encryption_negotiated(conn)) {
- retval = conn->ops->generate_encryptionkey(conn, sess);
- if (retval) {
- ksmbd_debug(SMB,
- "SMB3 encryption key generation failed\n");
- return -EINVAL;
- }
+ if (smb3_encryption_negotiated(conn) &&
+ !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
+ conn->ops->generate_encryptionkey(conn, sess);
sess->enc = true;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)
rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
sess->sign = false;
}
+binding_session:
if (conn->dialect >= SMB30_PROT_ID) {
chann = lookup_chann_list(sess, conn);
if (!chann) {
- chann = kmalloc(sizeof(struct channel), GFP_KERNEL);
+ chann = kmalloc(sizeof(struct channel), KSMBD_DEFAULT_GFP);
if (!chann)
return -ENOMEM;
chann->conn = conn;
- xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL);
+ old = xa_store(&sess->ksmbd_chann_list, (long)conn,
+ chann, KSMBD_DEFAULT_GFP);
+ if (xa_is_err(old)) {
+ kfree(chann);
+ return xa_err(old);
+ }
}
}
@@ -1666,7 +1693,12 @@ int smb2_sess_setup(struct ksmbd_work *work)
unsigned int negblob_len, negblob_off;
int rc = 0;
- ksmbd_debug(SMB, "Received request for session setup\n");
+ ksmbd_debug(SMB, "Received smb2 session setup request\n");
+
+ if (!ksmbd_conn_need_setup(conn) && !ksmbd_conn_good(conn)) {
+ work->send_no_response = 1;
+ return rc;
+ }
WORK_BUFFERS(work, req, rsp);
@@ -1727,11 +1759,12 @@ int smb2_sess_setup(struct ksmbd_work *work)
if (ksmbd_conn_need_reconnect(conn)) {
rc = -EFAULT;
+ ksmbd_user_session_put(sess);
sess = NULL;
goto out_err;
}
- if (ksmbd_session_lookup(conn, sess_id)) {
+ if (is_ksmbd_session_in_connection(conn, sess_id)) {
rc = -EACCES;
goto out_err;
}
@@ -1742,7 +1775,6 @@ int smb2_sess_setup(struct ksmbd_work *work)
}
conn->binding = true;
- ksmbd_user_session_get(sess);
} else if ((conn->dialect < SMB30_PROT_ID ||
server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) &&
(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
@@ -1764,12 +1796,12 @@ int smb2_sess_setup(struct ksmbd_work *work)
if (ksmbd_conn_need_reconnect(conn)) {
rc = -EFAULT;
+ ksmbd_user_session_put(sess);
sess = NULL;
goto out_err;
}
conn->binding = false;
- ksmbd_user_session_get(sess);
}
work->sess = sess;
@@ -1812,8 +1844,6 @@ int smb2_sess_setup(struct ksmbd_work *work)
ksmbd_conn_set_good(conn);
sess->state = SMB2_SESSION_VALID;
}
- kfree(sess->Preauth_HashValue);
- sess->Preauth_HashValue = NULL;
} else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) {
if (negblob->MessageType == NtLmNegotiate) {
rc = ntlm_negotiate(work, negblob, negblob_len, rsp);
@@ -1840,8 +1870,6 @@ int smb2_sess_setup(struct ksmbd_work *work)
kfree(preauth_sess);
}
}
- kfree(sess->Preauth_HashValue);
- sess->Preauth_HashValue = NULL;
} else {
pr_info_ratelimited("Unknown NTLMSSP message type : 0x%x\n",
le32_to_cpu(negblob->MessageType));
@@ -1899,10 +1927,12 @@ out_err:
sess->last_active = jiffies;
sess->state = SMB2_SESSION_EXPIRED;
+ ksmbd_user_session_put(sess);
+ work->sess = NULL;
if (try_delay) {
ksmbd_conn_set_need_reconnect(conn);
ssleep(5);
- ksmbd_conn_set_need_negotiate(conn);
+ ksmbd_conn_set_need_setup(conn);
}
}
smb2_set_err_rsp(work);
@@ -1940,6 +1970,8 @@ int smb2_tree_connect(struct ksmbd_work *work)
struct ksmbd_share_config *share = NULL;
int rc = -EINVAL;
+ ksmbd_debug(SMB, "Received smb2 tree connect request\n");
+
WORK_BUFFERS(work, req, rsp);
treename = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->PathOffset),
@@ -2126,7 +2158,7 @@ static int smb2_create_open_flags(bool file_present, __le32 access,
* smb2_tree_disconnect() - handler for smb tree connect request
* @work: smb work containing request buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_tree_disconnect(struct ksmbd_work *work)
{
@@ -2136,9 +2168,9 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
struct ksmbd_tree_connect *tcon = work->tcon;
int err;
- WORK_BUFFERS(work, req, rsp);
+ ksmbd_debug(SMB, "Received smb2 tree disconnect request\n");
- ksmbd_debug(SMB, "request\n");
+ WORK_BUFFERS(work, req, rsp);
if (!tcon) {
ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
@@ -2158,7 +2190,6 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
goto err_out;
}
- WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount));
tcon->t_state = TREE_DISCONNECTED;
write_unlock(&sess->tree_conns_lock);
@@ -2168,8 +2199,6 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
goto err_out;
}
- work->tcon = NULL;
-
rsp->StructureSize = cpu_to_le16(4);
err = ksmbd_iov_pin_rsp(work, rsp,
sizeof(struct smb2_tree_disconnect_rsp));
@@ -2190,20 +2219,20 @@ err_out:
* smb2_session_logoff() - handler for session log off request
* @work: smb work containing request buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_session_logoff(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
+ struct ksmbd_session *sess = work->sess;
struct smb2_logoff_req *req;
struct smb2_logoff_rsp *rsp;
- struct ksmbd_session *sess;
u64 sess_id;
int err;
WORK_BUFFERS(work, req, rsp);
- ksmbd_debug(SMB, "request\n");
+ ksmbd_debug(SMB, "Received smb2 session logoff request\n");
ksmbd_conn_lock(conn);
if (!ksmbd_conn_good(conn)) {
@@ -2219,11 +2248,6 @@ int smb2_session_logoff(struct ksmbd_work *work)
ksmbd_close_session_fds(work);
ksmbd_conn_wait_idle(conn);
- /*
- * Re-lookup session to validate if session is deleted
- * while waiting request complete
- */
- sess = ksmbd_session_lookup_all(conn, sess_id);
if (ksmbd_tree_conn_session_logoff(sess)) {
ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
@@ -2231,14 +2255,11 @@ int smb2_session_logoff(struct ksmbd_work *work)
return -ENOENT;
}
- ksmbd_destroy_file_table(&sess->file_table);
down_write(&conn->session_lock);
sess->state = SMB2_SESSION_EXPIRED;
up_write(&conn->session_lock);
- ksmbd_free_user(sess->user);
- sess->user = NULL;
- ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE);
+ ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_SETUP);
rsp->StructureSize = cpu_to_le16(4);
err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
@@ -2346,7 +2367,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
le16_to_cpu(eabuf->EaValueLength))
return -EINVAL;
- attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL);
+ attr_name = kmalloc(XATTR_NAME_MAX + 1, KSMBD_DEFAULT_GFP);
if (!attr_name)
return -ENOMEM;
@@ -2564,7 +2585,7 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon,
}
}
-static int smb2_creat(struct ksmbd_work *work, struct path *parent_path,
+static int smb2_creat(struct ksmbd_work *work,
struct path *path, char *name, int open_flags,
umode_t posix_mode, bool is_dir)
{
@@ -2593,7 +2614,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *parent_path,
return rc;
}
- rc = ksmbd_vfs_kern_path_locked(work, name, 0, parent_path, path, 0);
+ rc = ksmbd_vfs_kern_path(work, name, 0, path, 0);
if (rc) {
pr_err("cannot get linux path (%s), err = %d\n",
name, rc);
@@ -2692,7 +2713,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
switch (dh_idx) {
case DURABLE_RECONN_V2:
{
- struct create_durable_reconn_v2_req *recon_v2;
+ struct create_durable_handle_reconnect_v2 *recon_v2;
if (dh_info->type == DURABLE_RECONN ||
dh_info->type == DURABLE_REQ_V2) {
@@ -2700,8 +2721,15 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
goto out;
}
- recon_v2 = (struct create_durable_reconn_v2_req *)context;
- persistent_id = recon_v2->Fid.PersistentFileId;
+ if (le16_to_cpu(context->DataOffset) +
+ le32_to_cpu(context->DataLength) <
+ sizeof(struct create_durable_handle_reconnect_v2)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ recon_v2 = (struct create_durable_handle_reconnect_v2 *)context;
+ persistent_id = recon_v2->dcontext.Fid.PersistentFileId;
dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
if (!dh_info->fp) {
ksmbd_debug(SMB, "Failed to get durable handle state\n");
@@ -2709,7 +2737,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
goto out;
}
- if (memcmp(dh_info->fp->create_guid, recon_v2->CreateGuid,
+ if (memcmp(dh_info->fp->create_guid, recon_v2->dcontext.CreateGuid,
SMB2_CREATE_GUID_SIZE)) {
err = -EBADF;
ksmbd_put_durable_fd(dh_info->fp);
@@ -2725,7 +2753,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
}
case DURABLE_RECONN:
{
- struct create_durable_reconn_req *recon;
+ create_durable_reconn_t *recon;
if (dh_info->type == DURABLE_RECONN_V2 ||
dh_info->type == DURABLE_REQ_V2) {
@@ -2733,7 +2761,14 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
goto out;
}
- recon = (struct create_durable_reconn_req *)context;
+ if (le16_to_cpu(context->DataOffset) +
+ le32_to_cpu(context->DataLength) <
+ sizeof(create_durable_reconn_t)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ recon = (create_durable_reconn_t *)context;
persistent_id = recon->Data.Fid.PersistentFileId;
dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
if (!dh_info->fp) {
@@ -2758,10 +2793,17 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
goto out;
}
+ if (le16_to_cpu(context->DataOffset) +
+ le32_to_cpu(context->DataLength) <
+ sizeof(struct create_durable_req_v2)) {
+ err = -EINVAL;
+ goto out;
+ }
+
durable_v2_blob =
(struct create_durable_req_v2 *)context;
ksmbd_debug(SMB, "Request for durable v2 open\n");
- dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->CreateGuid);
+ dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->dcontext.CreateGuid);
if (dh_info->fp) {
if (!memcmp(conn->ClientGUID, dh_info->fp->client_guid,
SMB2_CLIENT_GUID_SIZE)) {
@@ -2779,11 +2821,11 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
if ((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
req_op_level == SMB2_OPLOCK_LEVEL_BATCH) {
dh_info->CreateGuid =
- durable_v2_blob->CreateGuid;
+ durable_v2_blob->dcontext.CreateGuid;
dh_info->persistent =
- le32_to_cpu(durable_v2_blob->Flags);
+ le32_to_cpu(durable_v2_blob->dcontext.Flags);
dh_info->timeout =
- le32_to_cpu(durable_v2_blob->Timeout);
+ le32_to_cpu(durable_v2_blob->dcontext.Timeout);
dh_info->type = dh_idx;
}
break;
@@ -2822,7 +2864,7 @@ int smb2_open(struct ksmbd_work *work)
struct ksmbd_tree_connect *tcon = work->tcon;
struct smb2_create_req *req;
struct smb2_create_rsp *rsp;
- struct path path, parent_path;
+ struct path path;
struct ksmbd_share_config *share = tcon->share_conf;
struct ksmbd_file *fp = NULL;
struct file *filp = NULL;
@@ -2837,7 +2879,7 @@ int smb2_open(struct ksmbd_work *work)
int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0;
int rc = 0;
int contxt_cnt = 0, query_disk_id = 0;
- int maximal_access_ctxt = 0, posix_ctxt = 0;
+ bool maximal_access_ctxt = false, posix_ctxt = false;
int s_type = 0;
int next_off = 0;
char *name = NULL;
@@ -2849,6 +2891,8 @@ int smb2_open(struct ksmbd_work *work)
__le32 daccess, maximal_access = 0;
int iov_len = 0;
+ ksmbd_debug(SMB, "Received smb2 create request\n");
+
WORK_BUFFERS(work, req, rsp);
if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off &&
@@ -2864,6 +2908,27 @@ int smb2_open(struct ksmbd_work *work)
return create_smb2_pipe(work);
}
+ if (req->CreateContextsOffset && tcon->posix_extensions) {
+ context = smb2_find_context_vals(req, SMB2_CREATE_TAG_POSIX, 16);
+ if (IS_ERR(context)) {
+ rc = PTR_ERR(context);
+ goto err_out2;
+ } else if (context) {
+ struct create_posix *posix = (struct create_posix *)context;
+
+ if (le16_to_cpu(context->DataOffset) +
+ le32_to_cpu(context->DataLength) <
+ sizeof(struct create_posix) - 4) {
+ rc = -EINVAL;
+ goto err_out2;
+ }
+ ksmbd_debug(SMB, "get posix context\n");
+
+ posix_mode = le32_to_cpu(posix->Mode);
+ posix_ctxt = true;
+ }
+ }
+
if (req->NameLength) {
name = smb2_get_name((char *)req + le16_to_cpu(req->NameOffset),
le16_to_cpu(req->NameLength),
@@ -2875,21 +2940,24 @@ int smb2_open(struct ksmbd_work *work)
}
ksmbd_debug(SMB, "converted name = %s\n", name);
- if (strchr(name, ':')) {
- if (!test_share_config_flag(work->tcon->share_conf,
- KSMBD_SHARE_FLAG_STREAMS)) {
- rc = -EBADF;
- goto err_out2;
+
+ if (posix_ctxt == false) {
+ if (strchr(name, ':')) {
+ if (!test_share_config_flag(work->tcon->share_conf,
+ KSMBD_SHARE_FLAG_STREAMS)) {
+ rc = -EBADF;
+ goto err_out2;
+ }
+ rc = parse_stream_name(name, &stream_name, &s_type);
+ if (rc < 0)
+ goto err_out2;
}
- rc = parse_stream_name(name, &stream_name, &s_type);
+
+ rc = ksmbd_validate_filename(name);
if (rc < 0)
goto err_out2;
}
- rc = ksmbd_validate_filename(name);
- if (rc < 0)
- goto err_out2;
-
if (ksmbd_share_veto_filename(share, name)) {
rc = -ENOENT;
ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n",
@@ -2897,7 +2965,7 @@ int smb2_open(struct ksmbd_work *work)
goto err_out2;
}
} else {
- name = kstrdup("", GFP_KERNEL);
+ name = kstrdup("", KSMBD_DEFAULT_GFP);
if (!name) {
rc = -ENOMEM;
goto err_out2;
@@ -3046,28 +3114,6 @@ int smb2_open(struct ksmbd_work *work)
rc = -EBADF;
goto err_out2;
}
-
- if (tcon->posix_extensions) {
- context = smb2_find_context_vals(req,
- SMB2_CREATE_TAG_POSIX, 16);
- if (IS_ERR(context)) {
- rc = PTR_ERR(context);
- goto err_out2;
- } else if (context) {
- struct create_posix *posix =
- (struct create_posix *)context;
- if (le16_to_cpu(context->DataOffset) +
- le32_to_cpu(context->DataLength) <
- sizeof(struct create_posix) - 4) {
- rc = -EINVAL;
- goto err_out2;
- }
- ksmbd_debug(SMB, "get posix context\n");
-
- posix_mode = le32_to_cpu(posix->Mode);
- posix_ctxt = 1;
- }
- }
}
if (ksmbd_override_fsids(work)) {
@@ -3075,8 +3121,8 @@ int smb2_open(struct ksmbd_work *work)
goto err_out2;
}
- rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS,
- &parent_path, &path, 1);
+ rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS,
+ &path, 1);
if (!rc) {
file_present = true;
@@ -3197,7 +3243,7 @@ int smb2_open(struct ksmbd_work *work)
/*create file if not present */
if (!file_present) {
- rc = smb2_creat(work, &parent_path, &path, name, open_flags,
+ rc = smb2_creat(work, &path, name, open_flags,
posix_mode,
req->CreateOptions & FILE_DIRECTORY_FILE_LE);
if (rc) {
@@ -3338,7 +3384,7 @@ int smb2_open(struct ksmbd_work *work)
sizeof(struct smb_sid) * 3 +
sizeof(struct smb_acl) +
sizeof(struct smb_ace) * ace_num * 2,
- GFP_KERNEL);
+ KSMBD_DEFAULT_GFP);
if (!pntsd) {
posix_acl_release(fattr.cf_acls);
posix_acl_release(fattr.cf_dacls);
@@ -3387,6 +3433,8 @@ int smb2_open(struct ksmbd_work *work)
fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE |
FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE));
+ fp->is_posix_ctxt = posix_ctxt;
+
/* fp should be searchable through ksmbd_inode.m_fp_list
* after daccess, saccess, attrib_only, and stream are
* initialized.
@@ -3402,7 +3450,7 @@ int smb2_open(struct ksmbd_work *work)
}
if (file_present || created)
- ksmbd_vfs_kern_path_unlock(&parent_path, &path);
+ path_put(&path);
if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC &&
!fp->attrib_only && !stream_name) {
@@ -3413,7 +3461,7 @@ int smb2_open(struct ksmbd_work *work)
share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp);
if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) ||
(req_op_level == SMB2_OPLOCK_LEVEL_LEASE &&
- !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) {
+ !(conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LEASING))) {
if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) {
rc = share_ret;
goto err_out1;
@@ -3500,6 +3548,15 @@ int smb2_open(struct ksmbd_work *work)
ksmbd_debug(SMB, "get query on disk id context\n");
query_disk_id = 1;
}
+
+ if (conn->is_aapl == false) {
+ context = smb2_find_context_vals(req, SMB2_CREATE_AAPL, 4);
+ if (IS_ERR(context)) {
+ rc = PTR_ERR(context);
+ goto err_out1;
+ } else if (context)
+ conn->is_aapl = true;
+ }
}
rc = ksmbd_vfs_getattr(&path, &stat);
@@ -3674,7 +3731,7 @@ reconnected_fp:
err_out:
if (rc && (file_present || created))
- ksmbd_vfs_kern_path_unlock(&parent_path, &path);
+ path_put(&path);
err_out1:
ksmbd_revert_fsids(work);
@@ -3726,15 +3783,15 @@ static int readdir_info_level_struct_sz(int info_level)
{
switch (info_level) {
case FILE_FULL_DIRECTORY_INFORMATION:
- return sizeof(struct file_full_directory_info);
+ return sizeof(FILE_FULL_DIRECTORY_INFO);
case FILE_BOTH_DIRECTORY_INFORMATION:
- return sizeof(struct file_both_directory_info);
+ return sizeof(FILE_BOTH_DIRECTORY_INFO);
case FILE_DIRECTORY_INFORMATION:
- return sizeof(struct file_directory_info);
+ return sizeof(FILE_DIRECTORY_INFO);
case FILE_NAMES_INFORMATION:
return sizeof(struct file_names_info);
case FILEID_FULL_DIRECTORY_INFORMATION:
- return sizeof(struct file_id_full_dir_info);
+ return sizeof(FILE_ID_FULL_DIR_INFO);
case FILEID_BOTH_DIRECTORY_INFORMATION:
return sizeof(struct file_id_both_directory_info);
case SMB_FIND_FILE_POSIX_INFO:
@@ -3749,9 +3806,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level)
switch (info_level) {
case FILE_FULL_DIRECTORY_INFORMATION:
{
- struct file_full_directory_info *ffdinfo;
+ FILE_FULL_DIRECTORY_INFO *ffdinfo;
- ffdinfo = (struct file_full_directory_info *)d_info->rptr;
+ ffdinfo = (FILE_FULL_DIRECTORY_INFO *)d_info->rptr;
d_info->rptr += le32_to_cpu(ffdinfo->NextEntryOffset);
d_info->name = ffdinfo->FileName;
d_info->name_len = le32_to_cpu(ffdinfo->FileNameLength);
@@ -3759,9 +3816,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level)
}
case FILE_BOTH_DIRECTORY_INFORMATION:
{
- struct file_both_directory_info *fbdinfo;
+ FILE_BOTH_DIRECTORY_INFO *fbdinfo;
- fbdinfo = (struct file_both_directory_info *)d_info->rptr;
+ fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)d_info->rptr;
d_info->rptr += le32_to_cpu(fbdinfo->NextEntryOffset);
d_info->name = fbdinfo->FileName;
d_info->name_len = le32_to_cpu(fbdinfo->FileNameLength);
@@ -3769,9 +3826,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level)
}
case FILE_DIRECTORY_INFORMATION:
{
- struct file_directory_info *fdinfo;
+ FILE_DIRECTORY_INFO *fdinfo;
- fdinfo = (struct file_directory_info *)d_info->rptr;
+ fdinfo = (FILE_DIRECTORY_INFO *)d_info->rptr;
d_info->rptr += le32_to_cpu(fdinfo->NextEntryOffset);
d_info->name = fdinfo->FileName;
d_info->name_len = le32_to_cpu(fdinfo->FileNameLength);
@@ -3789,9 +3846,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level)
}
case FILEID_FULL_DIRECTORY_INFORMATION:
{
- struct file_id_full_dir_info *dinfo;
+ FILE_ID_FULL_DIR_INFO *dinfo;
- dinfo = (struct file_id_full_dir_info *)d_info->rptr;
+ dinfo = (FILE_ID_FULL_DIR_INFO *)d_info->rptr;
d_info->rptr += le32_to_cpu(dinfo->NextEntryOffset);
d_info->name = dinfo->FileName;
d_info->name_len = le32_to_cpu(dinfo->FileNameLength);
@@ -3874,9 +3931,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
switch (info_level) {
case FILE_FULL_DIRECTORY_INFORMATION:
{
- struct file_full_directory_info *ffdinfo;
+ FILE_FULL_DIRECTORY_INFO *ffdinfo;
- ffdinfo = (struct file_full_directory_info *)kstat;
+ ffdinfo = (FILE_FULL_DIRECTORY_INFO *)kstat;
ffdinfo->FileNameLength = cpu_to_le32(conv_len);
ffdinfo->EaSize =
smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode);
@@ -3890,9 +3947,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
}
case FILE_BOTH_DIRECTORY_INFORMATION:
{
- struct file_both_directory_info *fbdinfo;
+ FILE_BOTH_DIRECTORY_INFO *fbdinfo;
- fbdinfo = (struct file_both_directory_info *)kstat;
+ fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)kstat;
fbdinfo->FileNameLength = cpu_to_le32(conv_len);
fbdinfo->EaSize =
smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode);
@@ -3908,9 +3965,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
}
case FILE_DIRECTORY_INFORMATION:
{
- struct file_directory_info *fdinfo;
+ FILE_DIRECTORY_INFO *fdinfo;
- fdinfo = (struct file_directory_info *)kstat;
+ fdinfo = (FILE_DIRECTORY_INFO *)kstat;
fdinfo->FileNameLength = cpu_to_le32(conv_len);
if (d_info->hide_dot_file && d_info->name[0] == '.')
fdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE;
@@ -3930,16 +3987,19 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
}
case FILEID_FULL_DIRECTORY_INFORMATION:
{
- struct file_id_full_dir_info *dinfo;
+ FILE_ID_FULL_DIR_INFO *dinfo;
- dinfo = (struct file_id_full_dir_info *)kstat;
+ dinfo = (FILE_ID_FULL_DIR_INFO *)kstat;
dinfo->FileNameLength = cpu_to_le32(conv_len);
dinfo->EaSize =
smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode);
if (dinfo->EaSize)
dinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE;
dinfo->Reserved = 0;
- dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino);
+ if (conn->is_aapl)
+ dinfo->UniqueId = 0;
+ else
+ dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino);
if (d_info->hide_dot_file && d_info->name[0] == '.')
dinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE;
memcpy(dinfo->FileName, conv_name, conv_len);
@@ -3956,7 +4016,10 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode);
if (fibdinfo->EaSize)
fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE;
- fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino);
+ if (conn->is_aapl)
+ fibdinfo->UniqueId = 0;
+ else
+ fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino);
fibdinfo->ShortNameLength = 0;
fibdinfo->Reserved = 0;
fibdinfo->Reserved2 = cpu_to_le16(0);
@@ -3985,6 +4048,26 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
posix_info->DeviceId = cpu_to_le32(ksmbd_kstat->kstat->rdev);
posix_info->HardLinks = cpu_to_le32(ksmbd_kstat->kstat->nlink);
posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode & 0777);
+ switch (ksmbd_kstat->kstat->mode & S_IFMT) {
+ case S_IFDIR:
+ posix_info->Mode |= cpu_to_le32(POSIX_TYPE_DIR << POSIX_FILETYPE_SHIFT);
+ break;
+ case S_IFLNK:
+ posix_info->Mode |= cpu_to_le32(POSIX_TYPE_SYMLINK << POSIX_FILETYPE_SHIFT);
+ break;
+ case S_IFCHR:
+ posix_info->Mode |= cpu_to_le32(POSIX_TYPE_CHARDEV << POSIX_FILETYPE_SHIFT);
+ break;
+ case S_IFBLK:
+ posix_info->Mode |= cpu_to_le32(POSIX_TYPE_BLKDEV << POSIX_FILETYPE_SHIFT);
+ break;
+ case S_IFIFO:
+ posix_info->Mode |= cpu_to_le32(POSIX_TYPE_FIFO << POSIX_FILETYPE_SHIFT);
+ break;
+ case S_IFSOCK:
+ posix_info->Mode |= cpu_to_le32(POSIX_TYPE_SOCKET << POSIX_FILETYPE_SHIFT);
+ }
+
posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino);
posix_info->DosAttributes =
S_ISDIR(ksmbd_kstat->kstat->mode) ?
@@ -4032,20 +4115,6 @@ struct smb2_query_dir_private {
int info_level;
};
-static void lock_dir(struct ksmbd_file *dir_fp)
-{
- struct dentry *dir = dir_fp->filp->f_path.dentry;
-
- inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
-}
-
-static void unlock_dir(struct ksmbd_file *dir_fp)
-{
- struct dentry *dir = dir_fp->filp->f_path.dentry;
-
- inode_unlock(d_inode(dir));
-}
-
static int process_query_dir_entries(struct smb2_query_dir_private *priv)
{
struct mnt_idmap *idmap = file_mnt_idmap(priv->dir_fp->filp);
@@ -4060,11 +4129,10 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv)
if (dentry_name(priv->d_info, priv->info_level))
return -EINVAL;
- lock_dir(priv->dir_fp);
- dent = lookup_one(idmap, priv->d_info->name,
- priv->dir_fp->filp->f_path.dentry,
- priv->d_info->name_len);
- unlock_dir(priv->dir_fp);
+ dent = lookup_one_unlocked(idmap,
+ &QSTR_LEN(priv->d_info->name,
+ priv->d_info->name_len),
+ priv->dir_fp->filp->f_path.dentry);
if (IS_ERR(dent)) {
ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n",
@@ -4125,9 +4193,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info,
switch (info_level) {
case FILE_FULL_DIRECTORY_INFORMATION:
{
- struct file_full_directory_info *ffdinfo;
+ FILE_FULL_DIRECTORY_INFO *ffdinfo;
- ffdinfo = (struct file_full_directory_info *)d_info->wptr;
+ ffdinfo = (FILE_FULL_DIRECTORY_INFO *)d_info->wptr;
memcpy(ffdinfo->FileName, d_info->name, d_info->name_len);
ffdinfo->FileName[d_info->name_len] = 0x00;
ffdinfo->FileNameLength = cpu_to_le32(d_info->name_len);
@@ -4136,9 +4204,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info,
}
case FILE_BOTH_DIRECTORY_INFORMATION:
{
- struct file_both_directory_info *fbdinfo;
+ FILE_BOTH_DIRECTORY_INFO *fbdinfo;
- fbdinfo = (struct file_both_directory_info *)d_info->wptr;
+ fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)d_info->wptr;
memcpy(fbdinfo->FileName, d_info->name, d_info->name_len);
fbdinfo->FileName[d_info->name_len] = 0x00;
fbdinfo->FileNameLength = cpu_to_le32(d_info->name_len);
@@ -4147,9 +4215,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info,
}
case FILE_DIRECTORY_INFORMATION:
{
- struct file_directory_info *fdinfo;
+ FILE_DIRECTORY_INFO *fdinfo;
- fdinfo = (struct file_directory_info *)d_info->wptr;
+ fdinfo = (FILE_DIRECTORY_INFO *)d_info->wptr;
memcpy(fdinfo->FileName, d_info->name, d_info->name_len);
fdinfo->FileName[d_info->name_len] = 0x00;
fdinfo->FileNameLength = cpu_to_le32(d_info->name_len);
@@ -4169,9 +4237,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info,
}
case FILEID_FULL_DIRECTORY_INFORMATION:
{
- struct file_id_full_dir_info *dinfo;
+ FILE_ID_FULL_DIR_INFO *dinfo;
- dinfo = (struct file_id_full_dir_info *)d_info->wptr;
+ dinfo = (FILE_ID_FULL_DIR_INFO *)d_info->wptr;
memcpy(dinfo->FileName, d_info->name, d_info->name_len);
dinfo->FileName[d_info->name_len] = 0x00;
dinfo->FileNameLength = cpu_to_le32(d_info->name_len);
@@ -4224,6 +4292,7 @@ static bool __query_dir(struct dir_context *ctx, const char *name, int namlen,
/* dot and dotdot entries are already reserved */
if (!strcmp(".", name) || !strcmp("..", name))
return true;
+ d_info->num_scan++;
if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name))
return true;
if (!match_pattern(name, namlen, priv->search_pattern))
@@ -4296,6 +4365,8 @@ int smb2_query_dir(struct ksmbd_work *work)
int buffer_sz;
struct smb2_query_dir_private query_dir_private = {NULL, };
+ ksmbd_debug(SMB, "Received smb2 query directory request\n");
+
WORK_BUFFERS(work, req, rsp);
if (ksmbd_override_fsids(work)) {
@@ -4384,9 +4455,18 @@ int smb2_query_dir(struct ksmbd_work *work)
query_dir_private.info_level = req->FileInformationClass;
dir_fp->readdir_data.private = &query_dir_private;
set_ctx_actor(&dir_fp->readdir_data.ctx, __query_dir);
-
+again:
+ d_info.num_scan = 0;
rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx);
/*
+ * num_entry can be 0 if the directory iteration stops before reaching
+ * the end of the directory and no file is matched with the search
+ * pattern.
+ */
+ if (rc >= 0 && !d_info.num_entry && d_info.num_scan &&
+ d_info.out_buf_len > 0)
+ goto again;
+ /*
* req->OutputBufferLength is too small to contain even one entry.
* In this case, it immediately returns OutputBufferLength 0 to client.
*/
@@ -4421,7 +4501,7 @@ int smb2_query_dir(struct ksmbd_work *work)
goto err_out;
} else {
no_buf_len:
- ((struct file_directory_info *)
+ ((FILE_DIRECTORY_INFO *)
((char *)rsp->Buffer + d_info.last_entry_offset))
->NextEntryOffset = 0;
if (d_info.data_count >= d_info.last_entry_off_align)
@@ -4467,7 +4547,7 @@ err_out2:
smb2_set_err_rsp(work);
ksmbd_fd_put(work, dir_fp);
ksmbd_revert_fsids(work);
- return 0;
+ return rc;
}
/**
@@ -4533,8 +4613,15 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess,
* pipe without opening it, checking error condition here
*/
id = req->VolatileFileId;
- if (!ksmbd_session_rpc_method(sess, id))
+
+ lockdep_assert_not_held(&sess->rpc_lock);
+
+ down_read(&sess->rpc_lock);
+ if (!ksmbd_session_rpc_method(sess, id)) {
+ up_read(&sess->rpc_lock);
return -ENOENT;
+ }
+ up_read(&sess->rpc_lock);
ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n",
req->FileInfoClass, req->VolatileFileId);
@@ -4783,8 +4870,13 @@ static int get_file_standard_info(struct smb2_query_info_rsp *rsp,
sinfo = (struct smb2_file_standard_info *)rsp->Buffer;
delete_pending = ksmbd_inode_pending_delete(fp);
- sinfo->AllocationSize = cpu_to_le64(stat.blocks << 9);
- sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
+ if (ksmbd_stream_fd(fp) == false) {
+ sinfo->AllocationSize = cpu_to_le64(stat.blocks << 9);
+ sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
+ } else {
+ sinfo->AllocationSize = cpu_to_le64(fp->stream.size);
+ sinfo->EndOfFile = cpu_to_le64(fp->stream.size);
+ }
sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending);
sinfo->DeletePending = delete_pending;
sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0;
@@ -4847,9 +4939,14 @@ static int get_file_all_info(struct ksmbd_work *work,
file_info->ChangeTime = cpu_to_le64(time);
file_info->Attributes = fp->f_ci->m_fattr;
file_info->Pad1 = 0;
- file_info->AllocationSize =
- cpu_to_le64(stat.blocks << 9);
- file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
+ if (ksmbd_stream_fd(fp) == false) {
+ file_info->AllocationSize =
+ cpu_to_le64(stat.blocks << 9);
+ file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
+ } else {
+ file_info->AllocationSize = cpu_to_le64(fp->stream.size);
+ file_info->EndOfFile = cpu_to_le64(fp->stream.size);
+ }
file_info->NumberOfLinks =
cpu_to_le32(get_nlink(&stat) - delete_pending);
file_info->DeletePending = delete_pending;
@@ -4858,7 +4955,10 @@ static int get_file_all_info(struct ksmbd_work *work,
file_info->IndexNumber = cpu_to_le64(stat.ino);
file_info->EASize = 0;
file_info->AccessFlags = fp->daccess;
- file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos);
+ if (ksmbd_stream_fd(fp) == false)
+ file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos);
+ else
+ file_info->CurrentByteOffset = cpu_to_le64(fp->stream.pos);
file_info->Mode = fp->coption;
file_info->AlignmentRequirement = 0;
conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename,
@@ -4946,7 +5046,7 @@ static int get_file_stream_info(struct ksmbd_work *work,
/* plus : size */
streamlen += 1;
- stream_buf = kmalloc(streamlen + 1, GFP_KERNEL);
+ stream_buf = kmalloc(streamlen + 1, KSMBD_DEFAULT_GFP);
if (!stream_buf)
break;
@@ -5020,7 +5120,7 @@ static int get_file_internal_info(struct smb2_query_info_rsp *rsp,
static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
struct ksmbd_file *fp, void *rsp_org)
{
- struct smb2_file_ntwrk_info *file_info;
+ struct smb2_file_network_open_info *file_info;
struct kstat stat;
u64 time;
int ret;
@@ -5036,7 +5136,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
if (ret)
return ret;
- file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer;
+ file_info = (struct smb2_file_network_open_info *)rsp->Buffer;
file_info->CreationTime = cpu_to_le64(fp->create_time);
time = ksmbd_UnixTimeToNT(stat.atime);
@@ -5046,11 +5146,16 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
time = ksmbd_UnixTimeToNT(stat.ctime);
file_info->ChangeTime = cpu_to_le64(time);
file_info->Attributes = fp->f_ci->m_fattr;
- file_info->AllocationSize = cpu_to_le64(stat.blocks << 9);
- file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
+ if (ksmbd_stream_fd(fp) == false) {
+ file_info->AllocationSize = cpu_to_le64(stat.blocks << 9);
+ file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
+ } else {
+ file_info->AllocationSize = cpu_to_le64(fp->stream.size);
+ file_info->EndOfFile = cpu_to_le64(fp->stream.size);
+ }
file_info->Reserved = cpu_to_le32(0);
rsp->OutputBufferLength =
- cpu_to_le32(sizeof(struct smb2_file_ntwrk_info));
+ cpu_to_le32(sizeof(struct smb2_file_network_open_info));
return 0;
}
@@ -5070,7 +5175,11 @@ static void get_file_position_info(struct smb2_query_info_rsp *rsp,
struct smb2_file_pos_info *file_info;
file_info = (struct smb2_file_pos_info *)rsp->Buffer;
- file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos);
+ if (ksmbd_stream_fd(fp) == false)
+ file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos);
+ else
+ file_info->CurrentByteOffset = cpu_to_le64(fp->stream.pos);
+
rsp->OutputBufferLength =
cpu_to_le32(sizeof(struct smb2_file_pos_info));
}
@@ -5159,10 +5268,35 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp,
file_info->ChangeTime = cpu_to_le64(time);
file_info->DosAttributes = fp->f_ci->m_fattr;
file_info->Inode = cpu_to_le64(stat.ino);
- file_info->EndOfFile = cpu_to_le64(stat.size);
- file_info->AllocationSize = cpu_to_le64(stat.blocks << 9);
+ if (ksmbd_stream_fd(fp) == false) {
+ file_info->EndOfFile = cpu_to_le64(stat.size);
+ file_info->AllocationSize = cpu_to_le64(stat.blocks << 9);
+ } else {
+ file_info->EndOfFile = cpu_to_le64(fp->stream.size);
+ file_info->AllocationSize = cpu_to_le64(fp->stream.size);
+ }
file_info->HardLinks = cpu_to_le32(stat.nlink);
file_info->Mode = cpu_to_le32(stat.mode & 0777);
+ switch (stat.mode & S_IFMT) {
+ case S_IFDIR:
+ file_info->Mode |= cpu_to_le32(POSIX_TYPE_DIR << POSIX_FILETYPE_SHIFT);
+ break;
+ case S_IFLNK:
+ file_info->Mode |= cpu_to_le32(POSIX_TYPE_SYMLINK << POSIX_FILETYPE_SHIFT);
+ break;
+ case S_IFCHR:
+ file_info->Mode |= cpu_to_le32(POSIX_TYPE_CHARDEV << POSIX_FILETYPE_SHIFT);
+ break;
+ case S_IFBLK:
+ file_info->Mode |= cpu_to_le32(POSIX_TYPE_BLKDEV << POSIX_FILETYPE_SHIFT);
+ break;
+ case S_IFIFO:
+ file_info->Mode |= cpu_to_le32(POSIX_TYPE_FIFO << POSIX_FILETYPE_SHIFT);
+ break;
+ case S_IFSOCK:
+ file_info->Mode |= cpu_to_le32(POSIX_TYPE_SOCKET << POSIX_FILETYPE_SHIFT);
+ }
+
file_info->DeviceId = cpu_to_le32(stat.rdev);
/*
@@ -5329,9 +5463,9 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
switch (fsinfoclass) {
case FS_DEVICE_INFORMATION:
{
- struct filesystem_device_info *info;
+ FILE_SYSTEM_DEVICE_INFO *info;
- info = (struct filesystem_device_info *)rsp->Buffer;
+ info = (FILE_SYSTEM_DEVICE_INFO *)rsp->Buffer;
info->DeviceType = cpu_to_le32(FILE_DEVICE_DISK);
info->DeviceCharacteristics =
@@ -5345,10 +5479,10 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
}
case FS_ATTRIBUTE_INFORMATION:
{
- struct filesystem_attribute_info *info;
+ FILE_SYSTEM_ATTRIBUTE_INFO *info;
size_t sz;
- info = (struct filesystem_attribute_info *)rsp->Buffer;
+ info = (FILE_SYSTEM_ATTRIBUTE_INFO *)rsp->Buffer;
info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS |
FILE_PERSISTENT_ACLS |
FILE_UNICODE_ON_DISK |
@@ -5367,7 +5501,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
"NTFS", PATH_MAX, conn->local_nls, 0);
len = len * 2;
info->FileSystemNameLen = cpu_to_le32(len);
- sz = sizeof(struct filesystem_attribute_info) + len;
+ sz = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO) + len;
rsp->OutputBufferLength = cpu_to_le32(sz);
break;
}
@@ -5399,11 +5533,11 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
}
case FS_SIZE_INFORMATION:
{
- struct filesystem_info *info;
+ FILE_SYSTEM_SIZE_INFO *info;
- info = (struct filesystem_info *)(rsp->Buffer);
+ info = (FILE_SYSTEM_SIZE_INFO *)(rsp->Buffer);
info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks);
- info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree);
+ info->AvailableAllocationUnits = cpu_to_le64(stfs.f_bfree);
info->SectorsPerAllocationUnit = cpu_to_le32(1);
info->BytesPerSector = cpu_to_le32(stfs.f_bsize);
rsp->OutputBufferLength = cpu_to_le32(24);
@@ -5486,13 +5620,14 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
}
case FS_POSIX_INFORMATION:
{
- struct filesystem_posix_info *info;
+ FILE_SYSTEM_POSIX_INFO *info;
if (!work->tcon->posix_extensions) {
pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n");
- rc = -EOPNOTSUPP;
+ path_put(&path);
+ return -EOPNOTSUPP;
} else {
- info = (struct filesystem_posix_info *)(rsp->Buffer);
+ info = (FILE_SYSTEM_POSIX_INFO *)(rsp->Buffer);
info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize);
info->BlockSize = cpu_to_le32(stfs.f_bsize);
info->TotalBlocks = cpu_to_le64(stfs.f_blocks);
@@ -5602,9 +5737,9 @@ int smb2_query_info(struct ksmbd_work *work)
struct smb2_query_info_rsp *rsp;
int rc = 0;
- WORK_BUFFERS(work, req, rsp);
+ ksmbd_debug(SMB, "Received request smb2 query info request\n");
- ksmbd_debug(SMB, "GOT query info request\n");
+ WORK_BUFFERS(work, req, rsp);
if (ksmbd_override_fsids(work)) {
rc = -ENOMEM;
@@ -5696,7 +5831,7 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work)
* smb2_close() - handler for smb2 close file command
* @work: smb work containing close request buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_close(struct ksmbd_work *work)
{
@@ -5709,6 +5844,8 @@ int smb2_close(struct ksmbd_work *work)
u64 time;
int err = 0;
+ ksmbd_debug(SMB, "Received smb2 close request\n");
+
WORK_BUFFERS(work, req, rsp);
if (test_share_config_flag(work->tcon->share_conf,
@@ -5819,12 +5956,14 @@ out:
* smb2_echo() - handler for smb2 echo(ping) command
* @work: smb work containing echo request buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_echo(struct ksmbd_work *work)
{
struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf);
+ ksmbd_debug(SMB, "Received smb2 echo request\n");
+
if (work->next_smb2_rcv_hdr_off)
rsp = ksmbd_resp_buf_next(work);
@@ -5849,7 +5988,7 @@ static int smb2_rename(struct ksmbd_work *work,
if (IS_ERR(new_name))
return PTR_ERR(new_name);
- if (strchr(new_name, ':')) {
+ if (fp->is_posix_ctxt == false && strchr(new_name, ':')) {
int s_type;
char *xattr_stream_name, *stream_name = NULL;
size_t xattr_stream_size;
@@ -5912,8 +6051,7 @@ static int smb2_create_link(struct ksmbd_work *work,
struct nls_table *local_nls)
{
char *link_name = NULL, *target_name = NULL, *pathname = NULL;
- struct path path, parent_path;
- bool file_present = false;
+ struct path path;
int rc;
if (buf_len < (u64)sizeof(struct smb2_file_link_info) +
@@ -5921,7 +6059,7 @@ static int smb2_create_link(struct ksmbd_work *work,
return -EINVAL;
ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n");
- pathname = kmalloc(PATH_MAX, GFP_KERNEL);
+ pathname = kmalloc(PATH_MAX, KSMBD_DEFAULT_GFP);
if (!pathname)
return -ENOMEM;
@@ -5941,16 +6079,13 @@ static int smb2_create_link(struct ksmbd_work *work,
}
ksmbd_debug(SMB, "target name is %s\n", target_name);
- rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS,
- &parent_path, &path, 0);
+ rc = ksmbd_vfs_kern_path_start_removing(work, link_name, LOOKUP_NO_SYMLINKS,
+ &path, 0);
if (rc) {
if (rc != -ENOENT)
goto out;
- } else
- file_present = true;
-
- if (file_info->ReplaceIfExists) {
- if (file_present) {
+ } else {
+ if (file_info->ReplaceIfExists) {
rc = ksmbd_vfs_remove_file(work, &path);
if (rc) {
rc = -EINVAL;
@@ -5958,21 +6093,17 @@ static int smb2_create_link(struct ksmbd_work *work,
link_name);
goto out;
}
- }
- } else {
- if (file_present) {
+ } else {
rc = -EEXIST;
ksmbd_debug(SMB, "link already exists\n");
goto out;
}
+ ksmbd_vfs_kern_path_end_removing(&path);
}
-
rc = ksmbd_vfs_link(work, target_name, link_name);
if (rc)
rc = -EINVAL;
out:
- if (file_present)
- ksmbd_vfs_kern_path_unlock(&parent_path, &path);
if (!IS_ERR(link_name))
kfree(link_name);
@@ -6006,15 +6137,13 @@ static int set_file_basic_info(struct ksmbd_file *fp,
attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
}
- attrs.ia_valid |= ATTR_CTIME;
if (file_info->ChangeTime)
- attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime);
- else
- attrs.ia_ctime = inode_get_ctime(inode);
+ inode_set_ctime_to_ts(inode,
+ ksmbd_NTtimeToUnix(file_info->ChangeTime));
if (file_info->LastWriteTime) {
attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime);
- attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
+ attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET | ATTR_CTIME);
}
if (file_info->Attributes) {
@@ -6056,8 +6185,6 @@ static int set_file_basic_info(struct ksmbd_file *fp,
return -EACCES;
inode_lock(inode);
- inode_set_ctime_to_ts(inode, attrs.ia_ctime);
- attrs.ia_valid &= ~ATTR_CTIME;
rc = notify_change(idmap, dentry, &attrs, NULL);
inode_unlock(inode);
}
@@ -6082,6 +6209,9 @@ static int set_file_allocation_info(struct ksmbd_work *work,
if (!(fp->daccess & FILE_WRITE_DATA_LE))
return -EACCES;
+ if (ksmbd_stream_fd(fp) == true)
+ return 0;
+
rc = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (rc)
@@ -6140,7 +6270,8 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
* truncate of some filesystem like FAT32 fill zero data in
* truncated range.
*/
- if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) {
+ if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC &&
+ ksmbd_stream_fd(fp) == false) {
ksmbd_debug(SMB, "truncated to newsize %lld\n", newsize);
rc = ksmbd_vfs_truncate(work, fp, newsize);
if (rc) {
@@ -6213,7 +6344,13 @@ static int set_file_position_info(struct ksmbd_file *fp,
return -EINVAL;
}
- fp->filp->f_pos = current_byte_offset;
+ if (ksmbd_stream_fd(fp) == false)
+ fp->filp->f_pos = current_byte_offset;
+ else {
+ if (current_byte_offset > XATTR_SIZE_MAX)
+ current_byte_offset = XATTR_SIZE_MAX;
+ fp->stream.pos = current_byte_offset;
+ }
return 0;
}
@@ -6246,7 +6383,6 @@ static int set_file_mode_info(struct ksmbd_file *fp,
* @share: ksmbd_share_config pointer
*
* Return: 0 on success, otherwise error
- * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH
*/
static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
struct smb2_set_info_req *req,
@@ -6259,14 +6395,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_BASIC_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_basic_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_file_basic_info(fp, (struct smb2_file_basic_info *)buffer, share);
}
case FILE_ALLOCATION_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_alloc_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_file_allocation_info(work, fp,
(struct smb2_file_alloc_info *)buffer);
@@ -6274,7 +6410,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_END_OF_FILE_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_eof_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_end_of_file_info(work, fp,
(struct smb2_file_eof_info *)buffer);
@@ -6282,7 +6418,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_RENAME_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_rename_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_rename_info(work, fp,
(struct smb2_file_rename_info *)buffer,
@@ -6291,7 +6427,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_LINK_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_link_info))
- return -EINVAL;
+ return -EMSGSIZE;
return smb2_create_link(work, work->tcon->share_conf,
(struct smb2_file_link_info *)buffer,
@@ -6301,7 +6437,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_DISPOSITION_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_disposition_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_file_disposition_info(fp,
(struct smb2_file_disposition_info *)buffer);
@@ -6315,7 +6451,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
}
if (buf_len < sizeof(struct smb2_ea_info))
- return -EINVAL;
+ return -EMSGSIZE;
return smb2_set_ea((struct smb2_ea_info *)buffer,
buf_len, &fp->filp->f_path, true);
@@ -6323,14 +6459,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
case FILE_POSITION_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_pos_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_file_position_info(fp, (struct smb2_file_pos_info *)buffer);
}
case FILE_MODE_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_mode_info))
- return -EINVAL;
+ return -EMSGSIZE;
return set_file_mode_info(fp, (struct smb2_file_mode_info *)buffer);
}
@@ -6365,7 +6501,7 @@ int smb2_set_info(struct ksmbd_work *work)
int rc = 0;
unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
- ksmbd_debug(SMB, "Received set info request\n");
+ ksmbd_debug(SMB, "Received smb2 set info request\n");
if (work->next_smb2_rcv_hdr_off) {
req = ksmbd_req_buf_next(work);
@@ -6437,6 +6573,8 @@ err_out:
rsp->hdr.Status = STATUS_ACCESS_DENIED;
else if (rc == -EINVAL)
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+ else if (rc == -EMSGSIZE)
+ rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH;
else if (rc == -ESHARE)
rsp->hdr.Status = STATUS_SHARING_VIOLATION;
else if (rc == -ENOENT)
@@ -6485,7 +6623,7 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work)
}
aux_payload_buf =
- kvmalloc(rpc_resp->payload_sz, GFP_KERNEL);
+ kvmalloc(rpc_resp->payload_sz, KSMBD_DEFAULT_GFP);
if (!aux_payload_buf) {
err = -ENOMEM;
goto out;
@@ -6525,7 +6663,7 @@ out:
}
static int smb2_set_remote_key_for_rdma(struct ksmbd_work *work,
- struct smb2_buffer_desc_v1 *desc,
+ struct smbdirect_buffer_descriptor_v1 *desc,
__le32 Channel,
__le16 ChannelInfoLength)
{
@@ -6561,7 +6699,7 @@ static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work,
int err;
err = ksmbd_conn_rdma_write(work->conn, data_buf, length,
- (struct smb2_buffer_desc_v1 *)
+ (struct smbdirect_buffer_descriptor_v1 *)
((char *)req + le16_to_cpu(req->ReadChannelInfoOffset)),
le16_to_cpu(req->ReadChannelInfoLength));
if (err)
@@ -6591,6 +6729,8 @@ int smb2_read(struct ksmbd_work *work)
unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
void *aux_payload_buf;
+ ksmbd_debug(SMB, "Received smb2 read request\n");
+
if (test_share_config_flag(work->tcon->share_conf,
KSMBD_SHARE_FLAG_PIPE)) {
ksmbd_debug(SMB, "IPC pipe read request\n");
@@ -6619,7 +6759,11 @@ int smb2_read(struct ksmbd_work *work)
if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE ||
req->Channel == SMB2_CHANNEL_RDMA_V1) {
is_rdma_channel = true;
- max_read_size = get_smbd_max_read_write_size();
+ max_read_size = get_smbd_max_read_write_size(work->conn->transport);
+ if (max_read_size == 0) {
+ err = -EINVAL;
+ goto out;
+ }
}
if (is_rdma_channel == true) {
@@ -6630,7 +6774,7 @@ int smb2_read(struct ksmbd_work *work)
goto out;
}
err = smb2_set_remote_key_for_rdma(work,
- (struct smb2_buffer_desc_v1 *)
+ (struct smbdirect_buffer_descriptor_v1 *)
((char *)req + ch_offset),
req->Channel,
req->ReadChannelInfoLength);
@@ -6651,6 +6795,10 @@ int smb2_read(struct ksmbd_work *work)
}
offset = le64_to_cpu(req->Offset);
+ if (offset < 0) {
+ err = -EINVAL;
+ goto out;
+ }
length = le32_to_cpu(req->Length);
mincount = le32_to_cpu(req->MinimumCount);
@@ -6664,7 +6812,7 @@ int smb2_read(struct ksmbd_work *work)
ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n",
fp->filp, offset, length);
- aux_payload_buf = kvzalloc(length, GFP_KERNEL);
+ aux_payload_buf = kvzalloc(ALIGN(length, 8), KSMBD_DEFAULT_GFP);
if (!aux_payload_buf) {
err = -ENOMEM;
goto out;
@@ -6672,6 +6820,7 @@ int smb2_read(struct ksmbd_work *work)
nbytes = ksmbd_vfs_read(work, fp, length, &offset, aux_payload_buf);
if (nbytes < 0) {
+ kvfree(aux_payload_buf);
err = nbytes;
goto out;
}
@@ -6681,7 +6830,7 @@ int smb2_read(struct ksmbd_work *work)
rsp->hdr.Status = STATUS_END_OF_FILE;
smb2_set_err_rsp(work);
ksmbd_fd_put(work, fp);
- return 0;
+ return -ENODATA;
}
ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n",
@@ -6816,12 +6965,12 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work,
int ret;
ssize_t nbytes;
- data_buf = kvzalloc(length, GFP_KERNEL);
+ data_buf = kvzalloc(length, KSMBD_DEFAULT_GFP);
if (!data_buf)
return -ENOMEM;
ret = ksmbd_conn_rdma_read(work->conn, data_buf, length,
- (struct smb2_buffer_desc_v1 *)
+ (struct smbdirect_buffer_descriptor_v1 *)
((char *)req + le16_to_cpu(req->WriteChannelInfoOffset)),
le16_to_cpu(req->WriteChannelInfoLength));
if (ret < 0) {
@@ -6856,6 +7005,8 @@ int smb2_write(struct ksmbd_work *work)
int err = 0;
unsigned int max_write_size = work->conn->vals->max_write_size;
+ ksmbd_debug(SMB, "Received smb2 write request\n");
+
WORK_BUFFERS(work, req, rsp);
if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) {
@@ -6864,12 +7015,18 @@ int smb2_write(struct ksmbd_work *work)
}
offset = le64_to_cpu(req->Offset);
+ if (offset < 0)
+ return -EINVAL;
length = le32_to_cpu(req->Length);
if (req->Channel == SMB2_CHANNEL_RDMA_V1 ||
req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) {
is_rdma_channel = true;
- max_write_size = get_smbd_max_read_write_size();
+ max_write_size = get_smbd_max_read_write_size(work->conn->transport);
+ if (max_write_size == 0) {
+ err = -EINVAL;
+ goto out;
+ }
length = le32_to_cpu(req->RemainingBytes);
}
@@ -6882,7 +7039,7 @@ int smb2_write(struct ksmbd_work *work)
goto out;
}
err = smb2_set_remote_key_for_rdma(work,
- (struct smb2_buffer_desc_v1 *)
+ (struct smbdirect_buffer_descriptor_v1 *)
((char *)req + ch_offset),
req->Channel,
req->WriteChannelInfoLength);
@@ -6994,7 +7151,7 @@ int smb2_flush(struct ksmbd_work *work)
WORK_BUFFERS(work, req, rsp);
- ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", req->VolatileFileId);
+ ksmbd_debug(SMB, "Received smb2 flush request(fid : %llu)\n", req->VolatileFileId);
err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId);
if (err)
@@ -7145,7 +7302,7 @@ static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock,
{
struct ksmbd_lock *lock;
- lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL);
+ lock = kzalloc(sizeof(struct ksmbd_lock), KSMBD_DEFAULT_GFP);
if (!lock)
return NULL;
@@ -7202,11 +7359,11 @@ int smb2_lock(struct ksmbd_work *work)
int nolock = 0;
LIST_HEAD(lock_list);
LIST_HEAD(rollback_list);
- int prior_lock = 0;
+ int prior_lock = 0, bkt;
WORK_BUFFERS(work, req, rsp);
- ksmbd_debug(SMB, "Received lock request\n");
+ ksmbd_debug(SMB, "Received smb2 lock request\n");
fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId);
if (!fp) {
ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId);
@@ -7312,7 +7469,7 @@ int smb2_lock(struct ksmbd_work *work)
nolock = 1;
/* check locks in connection list */
down_read(&conn_list_lock);
- list_for_each_entry(conn, &conn_list, conns_list) {
+ hash_for_each(conn_list, bkt, conn, hlist) {
spin_lock(&conn->llist_lock);
list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) {
if (file_inode(cmp_lock->fl->c.flc_file) !=
@@ -7386,17 +7543,17 @@ out_check_cl:
}
no_check_cl:
+ flock = smb_lock->fl;
+ list_del(&smb_lock->llist);
+
if (smb_lock->zero_len) {
err = 0;
goto skip;
}
-
- flock = smb_lock->fl;
- list_del(&smb_lock->llist);
retry:
rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL);
skip:
- if (flags & SMB2_LOCKFLAG_UNLOCK) {
+ if (smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) {
if (!rc) {
ksmbd_debug(SMB, "File unlocked\n");
} else if (rc == -ENOENT) {
@@ -7413,7 +7570,7 @@ skip:
"would have to wait for getting lock\n");
list_add(&smb_lock->llist, &rollback_list);
- argv = kmalloc(sizeof(void *), GFP_KERNEL);
+ argv = kmalloc(sizeof(void *), KSMBD_DEFAULT_GFP);
if (!argv) {
err = -ENOMEM;
goto out;
@@ -7595,11 +7752,11 @@ static int fsctl_copychunk(struct ksmbd_work *work,
}
src_fp = ksmbd_lookup_foreign_fd(work,
- le64_to_cpu(ci_req->ResumeKey[0]));
+ le64_to_cpu(ci_req->SourceKeyU64[0]));
dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id);
ret = -EINVAL;
if (!src_fp ||
- src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) {
+ src_fp->persistent_id != le64_to_cpu(ci_req->SourceKeyU64[1])) {
rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto out;
}
@@ -7688,7 +7845,10 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn,
if (netdev->type == ARPHRD_LOOPBACK)
continue;
- flags = dev_get_flags(netdev);
+ if (!ksmbd_find_netdev_name_iface_list(netdev->name))
+ continue;
+
+ flags = netif_get_flags(netdev);
if (!(flags & IFF_RUNNING))
continue;
ipv6_retry:
@@ -7704,9 +7864,9 @@ ipv6_retry:
nii_rsp->Capability = 0;
if (netdev->real_num_tx_queues > 1)
- nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE);
+ nii_rsp->Capability |= RSS_CAPABLE;
if (ksmbd_rdma_capable_netdev(netdev))
- nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE);
+ nii_rsp->Capability |= RDMA_CAPABLE;
nii_rsp->Next = cpu_to_le32(152);
nii_rsp->Reserved = 0;
@@ -7732,13 +7892,13 @@ ipv6_retry:
if (!ipv4_set) {
struct in_device *idev;
- sockaddr_storage->Family = cpu_to_le16(INTERNETWORK);
+ sockaddr_storage->Family = INTERNETWORK;
sockaddr_storage->addr4.Port = 0;
idev = __in_dev_get_rtnl(netdev);
if (!idev)
continue;
- sockaddr_storage->addr4.IPv4address =
+ sockaddr_storage->addr4.IPv4Address =
idev_ipv4_address(idev);
nbytes += sizeof(struct network_interface_info_ioctl_rsp);
ipv4_set = true;
@@ -7746,9 +7906,9 @@ ipv6_retry:
} else {
struct inet6_dev *idev6;
struct inet6_ifaddr *ifa;
- __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address;
+ __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6Address;
- sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6);
+ sockaddr_storage->Family = INTERNETWORKV6;
sockaddr_storage->addr6.Port = 0;
sockaddr_storage->addr6.FlowInfo = 0;
@@ -7812,7 +7972,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn,
goto err_out;
}
- neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities);
+ neg_rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities);
memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE);
neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode);
neg_rsp->Dialect = cpu_to_le16(conn->dialect);
@@ -7950,8 +8110,8 @@ static int fsctl_request_resume_key(struct ksmbd_work *work,
return -ENOENT;
memset(key_rsp, 0, sizeof(*key_rsp));
- key_rsp->ResumeKey[0] = req->VolatileFileId;
- key_rsp->ResumeKey[1] = req->PersistentFileId;
+ key_rsp->ResumeKeyU64[0] = req->VolatileFileId;
+ key_rsp->ResumeKeyU64[1] = req->PersistentFileId;
ksmbd_fd_put(work, fp);
return 0;
@@ -7973,6 +8133,8 @@ int smb2_ioctl(struct ksmbd_work *work)
int ret = 0;
char *buffer;
+ ksmbd_debug(SMB, "Received smb2 ioctl request\n");
+
if (work->next_smb2_rcv_hdr_off) {
req = ksmbd_req_buf_next(work);
rsp = ksmbd_resp_buf_next(work);
@@ -7990,7 +8152,7 @@ int smb2_ioctl(struct ksmbd_work *work)
id = req->VolatileFileId;
if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) {
- rsp->hdr.Status = STATUS_NOT_SUPPORTED;
+ ret = -EOPNOTSUPP;
goto out;
}
@@ -8010,8 +8172,9 @@ int smb2_ioctl(struct ksmbd_work *work)
case FSCTL_DFS_GET_REFERRALS:
case FSCTL_DFS_GET_REFERRALS_EX:
/* Not support DFS yet */
+ ret = -EOPNOTSUPP;
rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED;
- goto out;
+ goto out2;
case FSCTL_CREATE_OR_GET_OBJECT_ID:
{
struct file_object_buf_type1_ioctl_rsp *obj_buf;
@@ -8301,8 +8464,10 @@ out:
rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL;
else if (ret < 0 || rsp->hdr.Status == 0)
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+
+out2:
smb2_set_err_rsp(work);
- return 0;
+ return ret;
}
/**
@@ -8412,11 +8577,6 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work)
goto err_out;
}
- opinfo->op_state = OPLOCK_STATE_NONE;
- wake_up_interruptible_all(&opinfo->oplock_q);
- opinfo_put(opinfo);
- ksmbd_fd_put(work, fp);
-
rsp->StructureSize = cpu_to_le16(24);
rsp->OplockLevel = rsp_oplevel;
rsp->Reserved = 0;
@@ -8424,16 +8584,15 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work)
rsp->VolatileFid = volatile_id;
rsp->PersistentFid = persistent_id;
ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_oplock_break));
- if (!ret)
- return;
-
+ if (ret) {
err_out:
+ smb2_set_err_rsp(work);
+ }
+
opinfo->op_state = OPLOCK_STATE_NONE;
wake_up_interruptible_all(&opinfo->oplock_q);
-
opinfo_put(opinfo);
ksmbd_fd_put(work, fp);
- smb2_set_err_rsp(work);
}
static int check_lease_state(struct lease *lease, __le32 req_state)
@@ -8563,11 +8722,6 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
}
lease_state = lease->state;
- opinfo->op_state = OPLOCK_STATE_NONE;
- wake_up_interruptible_all(&opinfo->oplock_q);
- atomic_dec(&opinfo->breaking_cnt);
- wake_up_interruptible_all(&opinfo->oplock_brk);
- opinfo_put(opinfo);
rsp->StructureSize = cpu_to_le16(36);
rsp->Reserved = 0;
@@ -8576,29 +8730,31 @@ static void smb21_lease_break_ack(struct ksmbd_work *work)
rsp->LeaseState = lease_state;
rsp->LeaseDuration = 0;
ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lease_ack));
- if (!ret)
- return;
-
+ if (ret) {
err_out:
+ smb2_set_err_rsp(work);
+ }
+
+ opinfo->op_state = OPLOCK_STATE_NONE;
wake_up_interruptible_all(&opinfo->oplock_q);
atomic_dec(&opinfo->breaking_cnt);
wake_up_interruptible_all(&opinfo->oplock_brk);
-
opinfo_put(opinfo);
- smb2_set_err_rsp(work);
}
/**
* smb2_oplock_break() - dispatcher for smb2.0 and 2.1 oplock/lease break
* @work: smb work containing oplock/lease break command buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_oplock_break(struct ksmbd_work *work)
{
struct smb2_oplock_break *req;
struct smb2_oplock_break *rsp;
+ ksmbd_debug(SMB, "Received smb2 oplock break acknowledgment request\n");
+
WORK_BUFFERS(work, req, rsp);
switch (le16_to_cpu(req->StructureSize)) {
@@ -8613,6 +8769,7 @@ int smb2_oplock_break(struct ksmbd_work *work)
le16_to_cpu(req->StructureSize));
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
smb2_set_err_rsp(work);
+ return -EINVAL;
}
return 0;
@@ -8622,24 +8779,26 @@ int smb2_oplock_break(struct ksmbd_work *work)
* smb2_notify() - handler for smb2 notify request
* @work: smb work containing notify command buffer
*
- * Return: 0
+ * Return: 0 on success, otherwise error
*/
int smb2_notify(struct ksmbd_work *work)
{
struct smb2_change_notify_req *req;
struct smb2_change_notify_rsp *rsp;
+ ksmbd_debug(SMB, "Received smb2 notify\n");
+
WORK_BUFFERS(work, req, rsp);
if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) {
rsp->hdr.Status = STATUS_INTERNAL_ERROR;
smb2_set_err_rsp(work);
- return 0;
+ return -EIO;
}
smb2_set_err_rsp(work);
rsp->hdr.Status = STATUS_NOT_IMPLEMENTED;
- return 0;
+ return -EOPNOTSUPP;
}
/**
@@ -8694,9 +8853,8 @@ int smb2_check_sign_req(struct ksmbd_work *work)
iov[0].iov_base = (char *)&hdr->ProtocolId;
iov[0].iov_len = len;
- if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1,
- signature))
- return 0;
+ ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1,
+ signature);
if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) {
pr_err("bad smb2 signature\n");
@@ -8729,9 +8887,9 @@ void smb2_set_sign_rsp(struct ksmbd_work *work)
iov = &work->iov[work->iov_idx];
}
- if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec,
- signature))
- memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
+ ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec,
+ signature);
+ memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
}
/**
@@ -8907,7 +9065,7 @@ int smb3_encrypt_resp(struct ksmbd_work *work)
int rc = -ENOMEM;
void *tr_buf;
- tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL);
+ tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, KSMBD_DEFAULT_GFP);
if (!tr_buf)
return rc;
@@ -8956,6 +9114,7 @@ int smb3_decrypt_req(struct ksmbd_work *work)
le64_to_cpu(tr_hdr->SessionId));
return -ECONNABORTED;
}
+ ksmbd_user_session_put(sess);
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4;
diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
index 649dacf7e8c4..66cdc8e4a648 100644
--- a/fs/smb/server/smb2pdu.h
+++ b/fs/smb/server/smb2pdu.h
@@ -63,40 +63,11 @@ struct preauth_integrity_info {
#define SMB2_SESSION_TIMEOUT (10 * HZ)
-struct create_durable_req_v2 {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- __le32 Timeout;
- __le32 Flags;
- __u8 Reserved[8];
- __u8 CreateGuid[16];
-} __packed;
+/* Apple Defined Contexts */
+#define SMB2_CREATE_AAPL "AAPL"
#define DURABLE_HANDLE_MAX_TIMEOUT 300000
-struct create_durable_reconn_req {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- union {
- __u8 Reserved[16];
- struct {
- __u64 PersistentFileId;
- __u64 VolatileFileId;
- } Fid;
- } Data;
-} __packed;
-
-struct create_durable_reconn_v2_req {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- struct {
- __u64 PersistentFileId;
- __u64 VolatileFileId;
- } Fid;
- __u8 CreateGuid[16];
- __le32 Flags;
-} __packed;
-
struct create_alloc_size_req {
struct create_context_hdr ccontext;
__u8 Name[8];
@@ -112,16 +83,6 @@ struct create_durable_rsp {
} Data;
} __packed;
-/* See MS-SMB2 2.2.13.2.11 */
-/* Flags */
-#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
-struct create_durable_v2_rsp {
- struct create_context_hdr ccontext;
- __u8 Name[8];
- __le32 Timeout;
- __le32 Flags;
-} __packed;
-
/* equivalent of the contents of SMB3.1.1 POSIX open context response */
struct create_posix_rsp {
struct create_context_hdr ccontext;
@@ -133,30 +94,8 @@ struct create_posix_rsp {
u8 SidBuffer[44];
} __packed;
-struct smb2_buffer_desc_v1 {
- __le64 offset;
- __le32 token;
- __le32 length;
-} __packed;
-
#define SMB2_0_IOCTL_IS_FSCTL 0x00000001
-struct smb_sockaddr_in {
- __be16 Port;
- __be32 IPv4address;
- __u8 Reserved[8];
-} __packed;
-
-struct smb_sockaddr_in6 {
- __be16 Port;
- __be32 FlowInfo;
- __u8 IPv6address[16];
- __be32 ScopeId;
-} __packed;
-
-#define INTERNETWORK 0x0002
-#define INTERNETWORKV6 0x0017
-
struct sockaddr_storage_rsp {
__le16 Family;
union {
@@ -165,18 +104,6 @@ struct sockaddr_storage_rsp {
};
} __packed;
-#define RSS_CAPABLE 0x00000001
-#define RDMA_CAPABLE 0x00000002
-
-struct network_interface_info_ioctl_rsp {
- __le32 Next; /* next interface. zero if this is last one */
- __le32 IfIndex;
- __le32 Capability; /* RSS or RDMA Capable */
- __le32 Reserved;
- __le64 LinkSpeed;
- char SockAddr_Storage[128];
-} __packed;
-
struct file_object_buf_type1_ioctl_rsp {
__u8 ObjectId[16];
__u8 BirthVolumeId[16];
@@ -184,32 +111,6 @@ struct file_object_buf_type1_ioctl_rsp {
__u8 DomainId[16];
} __packed;
-struct resume_key_ioctl_rsp {
- __u64 ResumeKey[3];
- __le32 ContextLength;
- __u8 Context[4]; /* ignored, Windows sets to 4 bytes of zero */
-} __packed;
-
-struct srv_copychunk {
- __le64 SourceOffset;
- __le64 TargetOffset;
- __le32 Length;
- __le32 Reserved;
-} __packed;
-
-struct copychunk_ioctl_req {
- __le64 ResumeKey[3];
- __le32 ChunkCount;
- __le32 Reserved;
- struct srv_copychunk Chunks[] __counted_by_le(ChunkCount);
-} __packed;
-
-struct copychunk_ioctl_rsp {
- __le32 ChunksWritten;
- __le32 ChunkBytesWritten;
- __le32 TotalBytesWritten;
-} __packed;
-
struct file_sparse {
__u8 SetSparse;
} __packed;
@@ -304,17 +205,6 @@ struct smb2_file_stream_info {
char StreamName[];
} __packed;
-struct smb2_file_ntwrk_info {
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 AllocationSize;
- __le64 EndOfFile;
- __le32 Attributes;
- __le32 Reserved;
-} __packed;
-
struct smb2_file_standard_info {
__le64 AllocationSize;
__le64 EndOfFile;
@@ -502,4 +392,14 @@ static inline void *smb2_get_msg(void *buf)
return buf + 4;
}
+#define POSIX_TYPE_FILE 0
+#define POSIX_TYPE_DIR 1
+#define POSIX_TYPE_SYMLINK 2
+#define POSIX_TYPE_CHARDEV 3
+#define POSIX_TYPE_BLKDEV 4
+#define POSIX_TYPE_FIFO 5
+#define POSIX_TYPE_SOCKET 6
+
+#define POSIX_FILETYPE_SHIFT 12
+
#endif /* _SMB2PDU_H */
diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c
index 75b4eb856d32..b23203a1c286 100644
--- a/fs/smb/server/smb_common.c
+++ b/fs/smb/server/smb_common.c
@@ -18,8 +18,8 @@
#include "mgmt/share_config.h"
/*for shortname implementation */
-static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%";
-#define MANGLE_BASE (sizeof(basechars) / sizeof(char) - 1)
+static const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%";
+#define MANGLE_BASE (strlen(basechars) - 1)
#define MAGIC_CHAR '~'
#define PERIOD '.'
#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE]))
@@ -358,7 +358,7 @@ static int smb1_check_user_session(struct ksmbd_work *work)
static int smb1_allocate_rsp_buf(struct ksmbd_work *work)
{
work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE,
- GFP_KERNEL);
+ KSMBD_DEFAULT_GFP);
work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;
if (!work->response_buf) {
@@ -515,7 +515,7 @@ int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname,
p = strrchr(longname, '.');
if (p == longname) { /*name starts with a dot*/
- strscpy(extension, "___", strlen("___"));
+ strscpy(extension, "___", sizeof(extension));
} else {
if (p) {
p++;
@@ -576,7 +576,7 @@ static int smb_handle_negotiate(struct ksmbd_work *work)
ksmbd_debug(SMB, "Unsupported SMB1 protocol\n");
- if (ksmbd_iov_pin_rsp(work, (void *)neg_rsp,
+ if (ksmbd_iov_pin_rsp(work, (void *)neg_rsp + 4,
sizeof(struct smb_negotiate_rsp) - 4))
return -ENOMEM;
@@ -781,10 +781,6 @@ int __ksmbd_override_fsids(struct ksmbd_work *work,
WARN_ON(work->saved_cred);
work->saved_cred = override_creds(cred);
- if (!work->saved_cred) {
- abort_creds(cred);
- return -EINVAL;
- }
return 0;
}
@@ -796,13 +792,11 @@ int ksmbd_override_fsids(struct ksmbd_work *work)
void ksmbd_revert_fsids(struct ksmbd_work *work)
{
const struct cred *cred;
-
WARN_ON(!work->saved_cred);
- cred = current_cred();
- revert_creds(work->saved_cred);
- put_cred(cred);
+ cred = revert_creds(work->saved_cred);
work->saved_cred = NULL;
+ put_cred(cred);
}
__le32 smb_map_generic_desired_access(__le32 daccess)
diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h
index a3d8a905b07e..2baf4aa330eb 100644
--- a/fs/smb/server/smb_common.h
+++ b/fs/smb/server/smb_common.h
@@ -10,7 +10,9 @@
#include "glob.h"
#include "nterr.h"
+#include "../common/smbglob.h"
#include "../common/smb2pdu.h"
+#include "../common/fscc.h"
#include "smb2pdu.h"
/* ksmbd's Specific ERRNO */
@@ -26,18 +28,8 @@
#define SMB311_PROT 6
#define BAD_PROT 0xFFFF
-#define SMB1_VERSION_STRING "1.0"
-#define SMB20_VERSION_STRING "2.0"
-#define SMB21_VERSION_STRING "2.1"
-#define SMB30_VERSION_STRING "3.0"
-#define SMB302_VERSION_STRING "3.02"
-#define SMB311_VERSION_STRING "3.1.1"
-
#define SMB_ECHO_INTERVAL (60 * HZ)
-#define CIFS_DEFAULT_IOSIZE (64 * 1024)
-#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
-
#define MAX_STREAM_PROT_LEN 0x00FFFFFF
/* Responses when opening a file. */
@@ -46,78 +38,7 @@
#define F_CREATED 2
#define F_OVERWRITTEN 3
-/*
- * File Attribute flags
- */
-#define ATTR_POSIX_SEMANTICS 0x01000000
-#define ATTR_BACKUP_SEMANTICS 0x02000000
-#define ATTR_DELETE_ON_CLOSE 0x04000000
-#define ATTR_SEQUENTIAL_SCAN 0x08000000
-#define ATTR_RANDOM_ACCESS 0x10000000
-#define ATTR_NO_BUFFERING 0x20000000
-#define ATTR_WRITE_THROUGH 0x80000000
-
-/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */
-#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */
-#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */
-#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000
-#define FILE_SUPPORTS_USN_JOURNAL 0x02000000
-#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000
-#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000
-#define FILE_SUPPORTS_HARD_LINKS 0x00400000
-#define FILE_SUPPORTS_TRANSACTIONS 0x00200000
-#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000
-#define FILE_READ_ONLY_VOLUME 0x00080000
-#define FILE_NAMED_STREAMS 0x00040000
-#define FILE_SUPPORTS_ENCRYPTION 0x00020000
-#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
-#define FILE_VOLUME_IS_COMPRESSED 0x00008000
-#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
-#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
-#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
-#define FILE_VOLUME_QUOTAS 0x00000020
-#define FILE_FILE_COMPRESSION 0x00000010
-#define FILE_PERSISTENT_ACLS 0x00000008
-#define FILE_UNICODE_ON_DISK 0x00000004
-#define FILE_CASE_PRESERVED_NAMES 0x00000002
-#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
-
-#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */
-#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */
-#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */
-#define FILE_READ_EA 0x00000008 /* Extended attributes associated */
-/* with the file can be read */
-#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */
-/* with the file can be written */
-#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */
-/* the file using system paging I/O */
-#define FILE_DELETE_CHILD 0x00000040
-#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */
-/* file can be read */
-#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */
-/* file can be written */
-#define DELETE 0x00010000 /* The file can be deleted */
-#define READ_CONTROL 0x00020000 /* The access control list and */
-/* ownership associated with the */
-/* file can be read */
-#define WRITE_DAC 0x00040000 /* The access control list and */
-/* ownership associated with the */
-/* file can be written. */
-#define WRITE_OWNER 0x00080000 /* Ownership information associated */
-/* with the file can be written */
-#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */
-/* synchronize with the completion */
-/* of an input/output request */
-#define GENERIC_ALL 0x10000000
-#define GENERIC_EXECUTE 0x20000000
-#define GENERIC_WRITE 0x40000000
-#define GENERIC_READ 0x80000000
-/* In summary - Relevant file */
-/* access flags from CIFS are */
-/* file_read_data, file_write_data */
-/* file_execute, file_read_attributes*/
-/* write_dac, and delete. */
-
+/* Combinations of file access permission bits */
#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \
| FILE_READ_ATTRIBUTES \
| DELETE | READ_CONTROL | WRITE_DAC \
@@ -128,14 +49,6 @@
| FILE_WRITE_ATTRIBUTES \
| DELETE | READ_CONTROL | WRITE_DAC \
| WRITE_OWNER | SYNCHRONIZE)
-#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \
- | FILE_READ_ATTRIBUTES \
- | FILE_WRITE_ATTRIBUTES \
- | DELETE | READ_CONTROL | WRITE_DAC \
- | WRITE_OWNER | SYNCHRONIZE)
-
-#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
- | READ_CONTROL | SYNCHRONIZE)
/* generic flags for file open */
#define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \
@@ -156,71 +69,27 @@
FILE_EXECUTE | FILE_DELETE_CHILD | \
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES)
-#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff)
-#define SMB_COM_NEGOTIATE 0x72
-#define SMB1_CLIENT_GUID_SIZE (16)
+#define SMB_COM_NEGOTIATE 0x72 /* See MS-CIFS 2.2.2.1 */
+/* See MS-CIFS 2.2.3.1 */
#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */
+/*
+ * See MS-CIFS 2.2.3.1
+ * MS-SMB 2.2.3.1
+ */
#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40)
#define SMBFLG2_EXT_SEC cpu_to_le16(0x800)
#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000)
#define SMBFLG2_UNICODE cpu_to_le16(0x8000)
-struct smb_hdr {
- __be32 smb_buf_length;
- __u8 Protocol[4];
- __u8 Command;
- union {
- struct {
- __u8 ErrorClass;
- __u8 Reserved;
- __le16 Error;
- } __packed DosError;
- __le32 CifsError;
- } __packed Status;
- __u8 Flags;
- __le16 Flags2; /* note: le */
- __le16 PidHigh;
- union {
- struct {
- __le32 SequenceNumber; /* le */
- __u32 Reserved; /* zero */
- } __packed Sequence;
- __u8 SecuritySignature[8]; /* le */
- } __packed Signature;
- __u8 pad[2];
- __le16 Tid;
- __le16 Pid;
- __le16 Uid;
- __le16 Mid;
- __u8 WordCount;
-} __packed;
-
-struct smb_negotiate_req {
- struct smb_hdr hdr; /* wct = 0 */
- __le16 ByteCount;
- unsigned char DialectsArray[];
-} __packed;
-
+/* See MS-CIFS 2.2.4.52.2 */
struct smb_negotiate_rsp {
struct smb_hdr hdr; /* wct = 17 */
__le16 DialectIndex; /* 0xFFFF = no dialect acceptable */
__le16 ByteCount;
} __packed;
-struct filesystem_attribute_info {
- __le32 Attributes;
- __le32 MaxPathNameComponentLength;
- __le32 FileSystemNameLen;
- __le16 FileSystemName[]; /* do not have to save this - get subset? */
-} __packed;
-
-struct filesystem_device_info {
- __le32 DeviceType;
- __le32 DeviceCharacteristics;
-} __packed; /* device info level 0x104 */
-
struct filesystem_vol_info {
__le64 VolumeCreationTime;
__le32 SerialNumber;
@@ -229,13 +98,6 @@ struct filesystem_vol_info {
__le16 VolumeLabel[];
} __packed;
-struct filesystem_info {
- __le64 TotalAllocationUnits;
- __le64 FreeAllocationUnits;
- __le32 SectorsPerAllocationUnit;
- __le32 BytesPerSector;
-} __packed; /* size info, level 0x103 */
-
#define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */
#define STRING_LENGTH 28
@@ -252,20 +114,6 @@ struct object_id_info {
struct fs_extended_info extended_info;
} __packed;
-struct file_directory_info {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- char FileName[];
-} __packed; /* level 0x101 FF resp data */
-
struct file_names_info {
__le32 NextEntryOffset;
__u32 FileIndex;
@@ -273,39 +121,6 @@ struct file_names_info {
char FileName[];
} __packed; /* level 0xc FF resp data */
-struct file_full_directory_info {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize;
- char FileName[];
-} __packed; /* level 0x102 FF resp */
-
-struct file_both_directory_info {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize; /* length of the xattrs */
- __u8 ShortNameLength;
- __u8 Reserved;
- __u8 ShortName[24];
- char FileName[];
-} __packed; /* level 0x104 FFrsp data */
-
struct file_id_both_directory_info {
__le32 NextEntryOffset;
__u32 FileIndex;
@@ -326,75 +141,6 @@ struct file_id_both_directory_info {
char FileName[];
} __packed;
-struct file_id_full_dir_info {
- __le32 NextEntryOffset;
- __u32 FileIndex;
- __le64 CreationTime;
- __le64 LastAccessTime;
- __le64 LastWriteTime;
- __le64 ChangeTime;
- __le64 EndOfFile;
- __le64 AllocationSize;
- __le32 ExtFileAttributes;
- __le32 FileNameLength;
- __le32 EaSize; /* EA size */
- __le32 Reserved;
- __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
- char FileName[];
-} __packed; /* level 0x105 FF rsp data */
-
-struct smb_version_values {
- char *version_string;
- __u16 protocol_id;
- __le16 lock_cmd;
- __u32 capabilities;
- __u32 max_read_size;
- __u32 max_write_size;
- __u32 max_trans_size;
- __u32 max_credits;
- __u32 large_lock_type;
- __u32 exclusive_lock_type;
- __u32 shared_lock_type;
- __u32 unlock_lock_type;
- size_t header_size;
- size_t max_header_size;
- size_t read_rsp_size;
- unsigned int cap_unix;
- unsigned int cap_nt_find;
- unsigned int cap_large_files;
- __u16 signing_enabled;
- __u16 signing_required;
- size_t create_lease_size;
- size_t create_durable_size;
- size_t create_durable_v2_size;
- size_t create_mxac_size;
- size_t create_disk_id_size;
- size_t create_posix_size;
-};
-
-struct filesystem_posix_info {
- /* For undefined recommended transfer size return -1 in that field */
- __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */
- __le32 BlockSize;
- /* The next three fields are in terms of the block size.
- * (above). If block size is unknown, 4096 would be a
- * reasonable block size for a server to report.
- * Note that returning the blocks/blocksavail removes need
- * to make a second call (to QFSInfo level 0x103 to get this info.
- * UserBlockAvail is typically less than or equal to BlocksAvail,
- * if no distinction is made return the same value in each
- */
- __le64 TotalBlocks;
- __le64 BlocksAvail; /* bfree */
- __le64 UserBlocksAvail; /* bavail */
- /* For undefined Node fields or FSID return -1 */
- __le64 TotalFileNodes;
- __le64 FreeFileNodes;
- __le64 FileSysIdentifier; /* fsid */
- /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */
- /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */
-} __packed;
-
struct smb_version_ops {
u16 (*get_cmd_val)(struct ksmbd_work *swork);
int (*init_rsp_hdr)(struct ksmbd_work *swork);
@@ -407,7 +153,7 @@ struct smb_version_ops {
int (*check_sign_req)(struct ksmbd_work *work);
void (*set_sign_rsp)(struct ksmbd_work *work);
int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn);
- int (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess);
+ void (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess);
bool (*is_transform_hdr)(void *buf);
int (*decrypt_req)(struct ksmbd_work *work);
int (*encrypt_resp)(struct ksmbd_work *work);
@@ -457,14 +203,4 @@ unsigned int ksmbd_server_side_copy_max_chunk_size(void);
unsigned int ksmbd_server_side_copy_max_total_size(void);
bool is_asterisk(char *p);
__le32 smb_map_generic_desired_access(__le32 daccess);
-
-static inline unsigned int get_rfc1002_len(void *buf)
-{
- return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
-}
-
-static inline void inc_rfc1001_len(void *buf, int count)
-{
- be32_add_cpu((__be32 *)buf, count);
-}
#endif /* __SMB_COMMON_H__ */
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index 1c9775f1efa5..5aa7a66334d9 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -270,6 +270,11 @@ static int sid_to_id(struct mnt_idmap *idmap,
return -EIO;
}
+ if (psid->num_subauth == 0) {
+ pr_err("%s: zero subauthorities!\n", __func__);
+ return -EIO;
+ }
+
if (sidtype == SIDOWNER) {
kuid_t uid;
uid_t id;
@@ -333,7 +338,7 @@ void posix_state_to_acl(struct posix_acl_state *state,
pace->e_perm = state->other.allow;
}
-int init_acl_state(struct posix_acl_state *state, int cnt)
+int init_acl_state(struct posix_acl_state *state, u16 cnt)
{
int alloc;
@@ -345,10 +350,10 @@ int init_acl_state(struct posix_acl_state *state, int cnt)
*/
alloc = sizeof(struct posix_ace_state_array)
+ cnt * sizeof(struct posix_user_ace_state);
- state->users = kzalloc(alloc, GFP_KERNEL);
+ state->users = kzalloc(alloc, KSMBD_DEFAULT_GFP);
if (!state->users)
return -ENOMEM;
- state->groups = kzalloc(alloc, GFP_KERNEL);
+ state->groups = kzalloc(alloc, KSMBD_DEFAULT_GFP);
if (!state->groups) {
kfree(state->users);
return -ENOMEM;
@@ -368,7 +373,7 @@ static void parse_dacl(struct mnt_idmap *idmap,
struct smb_fattr *fattr)
{
int i, ret;
- int num_aces = 0;
+ u16 num_aces = 0;
unsigned int acl_size;
char *acl_base;
struct smb_ace **ppace;
@@ -389,16 +394,18 @@ static void parse_dacl(struct mnt_idmap *idmap,
ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n",
le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
- le32_to_cpu(pdacl->num_aces));
+ le16_to_cpu(pdacl->num_aces));
acl_base = (char *)pdacl;
acl_size = sizeof(struct smb_acl);
- num_aces = le32_to_cpu(pdacl->num_aces);
+ num_aces = le16_to_cpu(pdacl->num_aces);
if (num_aces <= 0)
return;
- if (num_aces > ULONG_MAX / sizeof(struct smb_ace *))
+ if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) /
+ (offsetof(struct smb_ace, sid) +
+ offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
return;
ret = init_acl_state(&acl_state, num_aces);
@@ -410,7 +417,7 @@ static void parse_dacl(struct mnt_idmap *idmap,
return;
}
- ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL);
+ ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), KSMBD_DEFAULT_GFP);
if (!ppace) {
free_acl_state(&default_acl_state);
free_acl_state(&acl_state);
@@ -432,6 +439,7 @@ static void parse_dacl(struct mnt_idmap *idmap,
offsetof(struct smb_sid, sub_auth);
if (end_of_acl - acl_base < acl_size ||
+ ppace[i]->sid.num_subauth == 0 ||
ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
(end_of_acl - acl_base <
acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) ||
@@ -553,7 +561,7 @@ static void parse_dacl(struct mnt_idmap *idmap,
if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) {
fattr->cf_acls =
posix_acl_alloc(acl_state.users->n +
- acl_state.groups->n + 4, GFP_KERNEL);
+ acl_state.groups->n + 4, KSMBD_DEFAULT_GFP);
if (fattr->cf_acls) {
cf_pace = fattr->cf_acls->a_entries;
posix_state_to_acl(&acl_state, cf_pace);
@@ -567,7 +575,7 @@ static void parse_dacl(struct mnt_idmap *idmap,
if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) {
fattr->cf_dacls =
posix_acl_alloc(default_acl_state.users->n +
- default_acl_state.groups->n + 4, GFP_KERNEL);
+ default_acl_state.groups->n + 4, KSMBD_DEFAULT_GFP);
if (fattr->cf_dacls) {
cf_pdace = fattr->cf_dacls->a_entries;
posix_state_to_acl(&default_acl_state, cf_pdace);
@@ -580,7 +588,7 @@ static void parse_dacl(struct mnt_idmap *idmap,
static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
struct smb_ace *pndace,
- struct smb_fattr *fattr, u32 *num_aces,
+ struct smb_fattr *fattr, u16 *num_aces,
u16 *size, u32 nt_aces_num)
{
struct posix_acl_entry *pace;
@@ -595,7 +603,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) {
int flags = 0;
- sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL);
+ sid = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP);
if (!sid)
break;
@@ -662,7 +670,7 @@ posix_default_acl:
pace = fattr->cf_dacls->a_entries;
for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) {
- sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL);
+ sid = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP);
if (!sid)
break;
@@ -701,7 +709,7 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,
struct smb_fattr *fattr)
{
struct smb_ace *ntace, *pndace;
- int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0;
+ u16 nt_num_aces = le16_to_cpu(nt_dacl->num_aces), num_aces = 0;
unsigned short size = 0;
int i;
@@ -728,7 +736,7 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,
set_posix_acl_entries_dacl(idmap, pndace, fattr,
&num_aces, &size, nt_num_aces);
- pndacl->num_aces = cpu_to_le32(num_aces);
+ pndacl->num_aces = cpu_to_le16(num_aces);
pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size);
}
@@ -736,7 +744,7 @@ static void set_mode_dacl(struct mnt_idmap *idmap,
struct smb_acl *pndacl, struct smb_fattr *fattr)
{
struct smb_ace *pace, *pndace;
- u32 num_aces = 0;
+ u16 num_aces = 0;
u16 size = 0, ace_size = 0;
uid_t uid;
const struct smb_sid *sid;
@@ -792,7 +800,7 @@ static void set_mode_dacl(struct mnt_idmap *idmap,
fattr->cf_mode, 0007);
out:
- pndacl->num_aces = cpu_to_le32(num_aces);
+ pndacl->num_aces = cpu_to_le16(num_aces);
pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size);
}
@@ -807,6 +815,13 @@ static int parse_sid(struct smb_sid *psid, char *end_of_acl)
return -EINVAL;
}
+ if (!psid->num_subauth)
+ return 0;
+
+ if (psid->num_subauth > SID_MAX_SUB_AUTHORITIES ||
+ end_of_acl < (char *)psid + 8 + sizeof(__le32) * psid->num_subauth)
+ return -EINVAL;
+
return 0;
}
@@ -848,6 +863,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
pntsd->type = cpu_to_le16(DACL_PRESENT);
if (pntsd->osidoffset) {
+ if (le32_to_cpu(pntsd->osidoffset) < sizeof(struct smb_ntsd))
+ return -EINVAL;
+
rc = parse_sid(owner_sid_ptr, end_of_acl);
if (rc) {
pr_err("%s: Error %d parsing Owner SID\n", __func__, rc);
@@ -863,6 +881,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
}
if (pntsd->gsidoffset) {
+ if (le32_to_cpu(pntsd->gsidoffset) < sizeof(struct smb_ntsd))
+ return -EINVAL;
+
rc = parse_sid(group_sid_ptr, end_of_acl);
if (rc) {
pr_err("%s: Error %d mapping Owner SID to gid\n",
@@ -884,6 +905,9 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
pntsd->type |= cpu_to_le16(DACL_PROTECTED);
if (dacloffset) {
+ if (dacloffset < sizeof(struct smb_ntsd))
+ return -EINVAL;
+
parse_dacl(idmap, dacl_ptr, end_of_acl,
owner_sid_ptr, group_sid_ptr, fattr);
}
@@ -906,7 +930,7 @@ int build_sec_desc(struct mnt_idmap *idmap,
gid_t gid;
unsigned int sid_type = SIDOWNER;
- nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL);
+ nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP);
if (!nowner_sid_ptr)
return -ENOMEM;
@@ -915,7 +939,7 @@ int build_sec_desc(struct mnt_idmap *idmap,
sid_type = SIDUNIX_USER;
id_to_sid(uid, sid_type, nowner_sid_ptr);
- ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL);
+ ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), KSMBD_DEFAULT_GFP);
if (!ngroup_sid_ptr) {
kfree(nowner_sid_ptr);
return -ENOMEM;
@@ -1006,8 +1030,11 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
struct smb_sid owner_sid, group_sid;
struct dentry *parent = path->dentry->d_parent;
struct mnt_idmap *idmap = mnt_idmap(path->mnt);
- int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size;
- int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
+ int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size;
+ int rc = 0, pntsd_type, pntsd_size, acl_len, aces_size;
+ unsigned int dacloffset;
+ size_t dacl_struct_end;
+ u16 num_aces, ace_cnt = 0;
char *aces_base;
bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
@@ -1015,15 +1042,18 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
parent, &parent_pntsd);
if (pntsd_size <= 0)
return -ENOENT;
+
dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
- if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) {
+ if (!dacloffset ||
+ check_add_overflow(dacloffset, sizeof(struct smb_acl), &dacl_struct_end) ||
+ dacl_struct_end > (size_t)pntsd_size) {
rc = -EINVAL;
goto free_parent_pntsd;
}
parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset);
acl_len = pntsd_size - dacloffset;
- num_aces = le32_to_cpu(parent_pdacl->num_aces);
+ num_aces = le16_to_cpu(parent_pdacl->num_aces);
pntsd_type = le16_to_cpu(parent_pntsd->type);
pdacl_size = le16_to_cpu(parent_pdacl->size);
@@ -1032,7 +1062,8 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
goto free_parent_pntsd;
}
- aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL);
+ aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2,
+ KSMBD_DEFAULT_GFP);
if (!aces_base) {
rc = -ENOMEM;
goto free_parent_pntsd;
@@ -1126,7 +1157,7 @@ pass:
pntsd_alloc_size = sizeof(struct smb_ntsd) + powner_sid_size +
pgroup_sid_size + sizeof(struct smb_acl) + nt_size;
- pntsd = kzalloc(pntsd_alloc_size, GFP_KERNEL);
+ pntsd = kzalloc(pntsd_alloc_size, KSMBD_DEFAULT_GFP);
if (!pntsd) {
rc = -ENOMEM;
goto free_aces_base;
@@ -1182,7 +1213,7 @@ pass:
pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
pdacl->revision = cpu_to_le16(2);
pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size);
- pdacl->num_aces = cpu_to_le32(ace_cnt);
+ pdacl->num_aces = cpu_to_le16(ace_cnt);
pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
memcpy(pace, aces_base, nt_size);
pntsd_size += sizeof(struct smb_acl) + nt_size;
@@ -1219,7 +1250,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
struct smb_ntsd *pntsd = NULL;
struct smb_acl *pdacl;
struct posix_acl *posix_acls;
- int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset;
+ int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size;
+ unsigned int dacl_offset;
+ size_t dacl_struct_end;
struct smb_sid sid;
int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
struct smb_ace *ace;
@@ -1238,7 +1271,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
dacl_offset = le32_to_cpu(pntsd->dacloffset);
if (!dacl_offset ||
- (dacl_offset + sizeof(struct smb_acl) > pntsd_size))
+ check_add_overflow(dacl_offset, sizeof(struct smb_acl), &dacl_struct_end) ||
+ dacl_struct_end > (size_t)pntsd_size)
goto err_out;
pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
@@ -1263,7 +1297,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
aces_size = acl_size - sizeof(struct smb_acl);
- for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) {
+ for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
if (offsetof(struct smb_ace, access_req) > aces_size)
break;
ace_size = le16_to_cpu(ace->size);
@@ -1284,7 +1318,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
aces_size = acl_size - sizeof(struct smb_acl);
- for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) {
+ for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
if (offsetof(struct smb_ace, access_req) > aces_size)
break;
ace_size = le16_to_cpu(ace->size);
diff --git a/fs/smb/server/smbacl.h b/fs/smb/server/smbacl.h
index 24ce576fc292..355adaee39b8 100644
--- a/fs/smb/server/smbacl.h
+++ b/fs/smb/server/smbacl.h
@@ -86,7 +86,7 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
int build_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info,
__u32 *secdesclen, struct smb_fattr *fattr);
-int init_acl_state(struct posix_acl_state *state, int cnt);
+int init_acl_state(struct posix_acl_state *state, u16 cnt);
void free_acl_state(struct posix_acl_state *state);
void posix_state_to_acl(struct posix_acl_state *state,
struct posix_acl_entry *pace);
diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
index 2f27afb695f6..2dbabe2d8005 100644
--- a/fs/smb/server/transport_ipc.c
+++ b/fs/smb/server/transport_ipc.c
@@ -244,7 +244,7 @@ static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz)
struct ksmbd_ipc_msg *msg;
size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg);
- msg = kvzalloc(msg_sz, GFP_KERNEL);
+ msg = kvzalloc(msg_sz, KSMBD_DEFAULT_GFP);
if (msg)
msg->sz = sz;
return msg;
@@ -263,10 +263,16 @@ static void ipc_msg_handle_free(int handle)
static int handle_response(int type, void *payload, size_t sz)
{
- unsigned int handle = *(unsigned int *)payload;
+ unsigned int handle;
struct ipc_msg_table_entry *entry;
int ret = 0;
+ /* Prevent 4-byte read beyond declared payload size */
+ if (sz < sizeof(unsigned int))
+ return -EINVAL;
+
+ handle = *(unsigned int *)payload;
+
ipc_update_last_active();
down_read(&ipc_msg_table_lock);
hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) {
@@ -281,9 +287,10 @@ static int handle_response(int type, void *payload, size_t sz)
if (entry->type + 1 != type) {
pr_err("Waiting for IPC type %d, got %d. Ignore.\n",
entry->type + 1, type);
+ continue;
}
- entry->response = kvzalloc(sz, GFP_KERNEL);
+ entry->response = kvzalloc(sz, KSMBD_DEFAULT_GFP);
if (!entry->response) {
ret = -ENOMEM;
break;
@@ -309,7 +316,11 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
server_conf.signing = req->signing;
server_conf.tcp_port = req->tcp_port;
server_conf.ipc_timeout = req->ipc_timeout * HZ;
- server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL;
+ if (check_mul_overflow(req->deadtime, SMB_ECHO_INTERVAL,
+ &server_conf.deadtime)) {
+ ret = -EINVAL;
+ goto out;
+ }
server_conf.share_fake_fscaps = req->share_fake_fscaps;
ksmbd_init_domain(req->sub_auth);
@@ -319,19 +330,27 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
init_smb2_max_write_size(req->smb2_max_write);
if (req->smb2_max_trans)
init_smb2_max_trans_size(req->smb2_max_trans);
- if (req->smb2_max_credits)
+ if (req->smb2_max_credits) {
init_smb2_max_credits(req->smb2_max_credits);
+ server_conf.max_inflight_req =
+ req->smb2_max_credits;
+ }
if (req->smbd_max_io_size)
init_smbd_max_io_size(req->smbd_max_io_size);
if (req->max_connections)
server_conf.max_connections = req->max_connections;
+ if (req->max_ip_connections)
+ server_conf.max_ip_connections = req->max_ip_connections;
+
ret = ksmbd_set_netbios_name(req->netbios_name);
ret |= ksmbd_set_server_string(req->server_string);
ret |= ksmbd_set_work_group(req->work_group);
+ server_conf.bind_interfaces_only = req->bind_interfaces_only;
ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req),
req->ifc_list_sz);
+out:
if (ret) {
pr_err("Server configuration error: %s %s %s\n",
req->netbios_name, req->server_string,
@@ -444,7 +463,7 @@ static int ipc_msg_send(struct ksmbd_ipc_msg *msg)
if (!ksmbd_tools_pid)
return ret;
- skb = genlmsg_new(msg->sz, GFP_KERNEL);
+ skb = genlmsg_new(msg->sz, KSMBD_DEFAULT_GFP);
if (!skb)
return -ENOMEM;
@@ -534,12 +553,16 @@ static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle
up_write(&ipc_msg_table_lock);
ret = ipc_msg_send(msg);
- if (ret)
+ if (ret) {
+ down_write(&ipc_msg_table_lock);
goto out;
+ }
ret = wait_event_interruptible_timeout(entry.wait,
entry.response != NULL,
IPC_WAIT_TIMEOUT);
+
+ down_write(&ipc_msg_table_lock);
if (entry.response) {
ret = ipc_validate_msg(&entry);
if (ret) {
@@ -548,7 +571,6 @@ static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle
}
}
out:
- down_write(&ipc_msg_table_lock);
hash_del(&entry.ipc_table_hlist);
up_write(&ipc_msg_table_lock);
return entry.response;
@@ -623,6 +645,9 @@ ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len)
struct ksmbd_spnego_authen_request *req;
struct ksmbd_spnego_authen_response *resp;
+ if (blob_len > KSMBD_IPC_MAX_PAYLOAD)
+ return NULL;
+
msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) +
blob_len + 1);
if (!msg)
@@ -802,10 +827,16 @@ struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle
struct ksmbd_rpc_command *req;
struct ksmbd_rpc_command *resp;
+ if (payload_sz > KSMBD_IPC_MAX_PAYLOAD)
+ return NULL;
+
msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
if (!msg)
return NULL;
+ lockdep_assert_not_held(&sess->rpc_lock);
+
+ down_read(&sess->rpc_lock);
msg->type = KSMBD_EVENT_RPC_REQUEST;
req = (struct ksmbd_rpc_command *)msg->payload;
req->handle = handle;
@@ -814,6 +845,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle
req->flags |= KSMBD_RPC_WRITE_METHOD;
req->payload_sz = payload_sz;
memcpy(req->payload, payload, payload_sz);
+ up_read(&sess->rpc_lock);
resp = ipc_msg_send_request(msg, req->handle);
ipc_msg_free(msg);
@@ -830,6 +862,9 @@ struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle)
if (!msg)
return NULL;
+ lockdep_assert_not_held(&sess->rpc_lock);
+
+ down_read(&sess->rpc_lock);
msg->type = KSMBD_EVENT_RPC_REQUEST;
req = (struct ksmbd_rpc_command *)msg->payload;
req->handle = handle;
@@ -837,6 +872,7 @@ struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle)
req->flags |= rpc_context_flags(sess);
req->flags |= KSMBD_RPC_READ_METHOD;
req->payload_sz = 0;
+ up_read(&sess->rpc_lock);
resp = ipc_msg_send_request(msg, req->handle);
ipc_msg_free(msg);
@@ -850,10 +886,16 @@ struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle
struct ksmbd_rpc_command *req;
struct ksmbd_rpc_command *resp;
+ if (payload_sz > KSMBD_IPC_MAX_PAYLOAD)
+ return NULL;
+
msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
if (!msg)
return NULL;
+ lockdep_assert_not_held(&sess->rpc_lock);
+
+ down_read(&sess->rpc_lock);
msg->type = KSMBD_EVENT_RPC_REQUEST;
req = (struct ksmbd_rpc_command *)msg->payload;
req->handle = handle;
@@ -862,37 +904,13 @@ struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle
req->flags |= KSMBD_RPC_IOCTL_METHOD;
req->payload_sz = payload_sz;
memcpy(req->payload, payload, payload_sz);
+ up_read(&sess->rpc_lock);
resp = ipc_msg_send_request(msg, req->handle);
ipc_msg_free(msg);
return resp;
}
-struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload,
- size_t payload_sz)
-{
- struct ksmbd_ipc_msg *msg;
- struct ksmbd_rpc_command *req;
- struct ksmbd_rpc_command *resp;
-
- msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
- if (!msg)
- return NULL;
-
- msg->type = KSMBD_EVENT_RPC_REQUEST;
- req = (struct ksmbd_rpc_command *)msg->payload;
- req->handle = ksmbd_acquire_id(&ipc_ida);
- req->flags = rpc_context_flags(sess);
- req->flags |= KSMBD_RPC_RAP_METHOD;
- req->payload_sz = payload_sz;
- memcpy(req->payload, payload, payload_sz);
-
- resp = ipc_msg_send_request(msg, req->handle);
- ipc_msg_handle_free(req->handle);
- ipc_msg_free(msg);
- return resp;
-}
-
static int __ipc_heartbeat(void)
{
unsigned long delta;
diff --git a/fs/smb/server/transport_ipc.h b/fs/smb/server/transport_ipc.h
index d9b6737f8cd0..e51850f1423b 100644
--- a/fs/smb/server/transport_ipc.h
+++ b/fs/smb/server/transport_ipc.h
@@ -41,8 +41,6 @@ struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle
struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle);
struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle,
void *payload, size_t payload_sz);
-struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload,
- size_t payload_sz);
void ksmbd_ipc_release(void);
void ksmbd_ipc_soft_reset(void);
int ksmbd_ipc_init(void);
diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c
index 17c76713c6d0..4e7ab8d9314f 100644
--- a/fs/smb/server/transport_rdma.c
+++ b/fs/smb/server/transport_rdma.c
@@ -14,26 +14,35 @@
#include <linux/mempool.h>
#include <linux/highmem.h>
#include <linux/scatterlist.h>
+#include <linux/string_choices.h>
#include <rdma/ib_verbs.h>
#include <rdma/rdma_cm.h>
#include <rdma/rw.h>
+#define __SMBDIRECT_SOCKET_DISCONNECT(__sc) smb_direct_disconnect_rdma_connection(__sc)
+
#include "glob.h"
#include "connection.h"
#include "smb_common.h"
#include "../common/smb2status.h"
+#include "../common/smbdirect/smbdirect.h"
+#include "../common/smbdirect/smbdirect_pdu.h"
+#include "../common/smbdirect/smbdirect_socket.h"
#include "transport_rdma.h"
#define SMB_DIRECT_PORT_IWARP 5445
#define SMB_DIRECT_PORT_INFINIBAND 445
-#define SMB_DIRECT_VERSION_LE cpu_to_le16(0x0100)
+#define SMB_DIRECT_VERSION_LE cpu_to_le16(SMBDIRECT_V1)
+
+/* SMB_DIRECT negotiation timeout (for the server) in seconds */
+#define SMB_DIRECT_NEGOTIATE_TIMEOUT 5
-/* SMB_DIRECT negotiation timeout in seconds */
-#define SMB_DIRECT_NEGOTIATE_TIMEOUT 120
+/* The timeout to wait for a keepalive message from peer in seconds */
+#define SMB_DIRECT_KEEPALIVE_SEND_INTERVAL 120
-#define SMB_DIRECT_MAX_SEND_SGES 6
-#define SMB_DIRECT_MAX_RECV_SGES 1
+/* The timeout to wait for a keepalive message from peer in seconds */
+#define SMB_DIRECT_KEEPALIVE_RECV_TIMEOUT 5
/*
* Default maximum number of RDMA read/write outstanding on this connection
@@ -86,133 +95,38 @@ static struct smb_direct_listener {
static struct workqueue_struct *smb_direct_wq;
-enum smb_direct_status {
- SMB_DIRECT_CS_NEW = 0,
- SMB_DIRECT_CS_CONNECTED,
- SMB_DIRECT_CS_DISCONNECTING,
- SMB_DIRECT_CS_DISCONNECTED,
-};
-
struct smb_direct_transport {
struct ksmbd_transport transport;
- enum smb_direct_status status;
- bool full_packet_received;
- wait_queue_head_t wait_status;
-
- struct rdma_cm_id *cm_id;
- struct ib_cq *send_cq;
- struct ib_cq *recv_cq;
- struct ib_pd *pd;
- struct ib_qp *qp;
-
- int max_send_size;
- int max_recv_size;
- int max_fragmented_send_size;
- int max_fragmented_recv_size;
- int max_rdma_rw_size;
-
- spinlock_t reassembly_queue_lock;
- struct list_head reassembly_queue;
- int reassembly_data_length;
- int reassembly_queue_length;
- int first_entry_offset;
- wait_queue_head_t wait_reassembly_queue;
-
- spinlock_t receive_credit_lock;
- int recv_credits;
- int count_avail_recvmsg;
- int recv_credit_max;
- int recv_credit_target;
-
- spinlock_t recvmsg_queue_lock;
- struct list_head recvmsg_queue;
-
- spinlock_t empty_recvmsg_queue_lock;
- struct list_head empty_recvmsg_queue;
-
- int send_credit_target;
- atomic_t send_credits;
- spinlock_t lock_new_recv_credits;
- int new_recv_credits;
- int max_rw_credits;
- int pages_per_rw_credit;
- atomic_t rw_credits;
-
- wait_queue_head_t wait_send_credits;
- wait_queue_head_t wait_rw_credits;
-
- mempool_t *sendmsg_mempool;
- struct kmem_cache *sendmsg_cache;
- mempool_t *recvmsg_mempool;
- struct kmem_cache *recvmsg_cache;
-
- wait_queue_head_t wait_send_pending;
- atomic_t send_pending;
-
- struct delayed_work post_recv_credits_work;
- struct work_struct send_immediate_work;
- struct work_struct disconnect_work;
-
- bool negotiation_requested;
+ struct smbdirect_socket socket;
};
-#define KSMBD_TRANS(t) ((struct ksmbd_transport *)&((t)->transport))
-
-enum {
- SMB_DIRECT_MSG_NEGOTIATE_REQ = 0,
- SMB_DIRECT_MSG_DATA_TRANSFER
-};
+#define KSMBD_TRANS(t) (&(t)->transport)
+#define SMBD_TRANS(t) (container_of(t, \
+ struct smb_direct_transport, transport))
static const struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops;
-struct smb_direct_send_ctx {
- struct list_head msg_list;
- int wr_cnt;
- bool need_invalidate_rkey;
- unsigned int remote_key;
-};
-
-struct smb_direct_sendmsg {
- struct smb_direct_transport *transport;
- struct ib_send_wr wr;
- struct list_head list;
- int num_sge;
- struct ib_sge sge[SMB_DIRECT_MAX_SEND_SGES];
- struct ib_cqe cqe;
- u8 packet[];
-};
-
-struct smb_direct_recvmsg {
- struct smb_direct_transport *transport;
- struct list_head list;
- int type;
- struct ib_sge sge;
- struct ib_cqe cqe;
- bool first_segment;
- u8 packet[];
-};
-
-struct smb_direct_rdma_rw_msg {
- struct smb_direct_transport *t;
- struct ib_cqe cqe;
- int status;
- struct completion *completion;
- struct list_head list;
- struct rdma_rw_ctx rw_ctx;
- struct sg_table sgt;
- struct scatterlist sg_list[];
-};
-
void init_smbd_max_io_size(unsigned int sz)
{
sz = clamp_val(sz, SMBD_MIN_IOSIZE, SMBD_MAX_IOSIZE);
smb_direct_max_read_write_size = sz;
}
-unsigned int get_smbd_max_read_write_size(void)
+unsigned int get_smbd_max_read_write_size(struct ksmbd_transport *kt)
{
- return smb_direct_max_read_write_size;
+ struct smb_direct_transport *t;
+ struct smbdirect_socket *sc;
+ struct smbdirect_socket_parameters *sp;
+
+ if (kt->ops != &ksmbd_smb_direct_transport_ops)
+ return 0;
+
+ t = SMBD_TRANS(kt);
+ sc = &t->socket;
+ sp = &sc->parameters;
+
+ return sp->max_read_write_size;
}
static inline int get_buf_page_count(void *buf, int size)
@@ -221,92 +135,65 @@ static inline int get_buf_page_count(void *buf, int size)
(uintptr_t)buf / PAGE_SIZE;
}
-static void smb_direct_destroy_pools(struct smb_direct_transport *transport);
+static void smb_direct_destroy_pools(struct smbdirect_socket *sc);
static void smb_direct_post_recv_credits(struct work_struct *work);
-static int smb_direct_post_send_data(struct smb_direct_transport *t,
- struct smb_direct_send_ctx *send_ctx,
+static int smb_direct_post_send_data(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *send_ctx,
struct kvec *iov, int niov,
int remaining_data_length);
-static inline struct smb_direct_transport *
-smb_trans_direct_transfort(struct ksmbd_transport *t)
-{
- return container_of(t, struct smb_direct_transport, transport);
-}
-
static inline void
-*smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg)
+*smbdirect_recv_io_payload(struct smbdirect_recv_io *recvmsg)
{
return (void *)recvmsg->packet;
}
-static inline bool is_receive_credit_post_required(int receive_credits,
- int avail_recvmsg_count)
-{
- return receive_credits <= (smb_direct_receive_credit_max >> 3) &&
- avail_recvmsg_count >= (receive_credits >> 2);
-}
-
static struct
-smb_direct_recvmsg *get_free_recvmsg(struct smb_direct_transport *t)
+smbdirect_recv_io *get_free_recvmsg(struct smbdirect_socket *sc)
{
- struct smb_direct_recvmsg *recvmsg = NULL;
+ struct smbdirect_recv_io *recvmsg = NULL;
+ unsigned long flags;
- spin_lock(&t->recvmsg_queue_lock);
- if (!list_empty(&t->recvmsg_queue)) {
- recvmsg = list_first_entry(&t->recvmsg_queue,
- struct smb_direct_recvmsg,
+ spin_lock_irqsave(&sc->recv_io.free.lock, flags);
+ if (!list_empty(&sc->recv_io.free.list)) {
+ recvmsg = list_first_entry(&sc->recv_io.free.list,
+ struct smbdirect_recv_io,
list);
list_del(&recvmsg->list);
}
- spin_unlock(&t->recvmsg_queue_lock);
+ spin_unlock_irqrestore(&sc->recv_io.free.lock, flags);
return recvmsg;
}
-static void put_recvmsg(struct smb_direct_transport *t,
- struct smb_direct_recvmsg *recvmsg)
-{
- ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr,
- recvmsg->sge.length, DMA_FROM_DEVICE);
-
- spin_lock(&t->recvmsg_queue_lock);
- list_add(&recvmsg->list, &t->recvmsg_queue);
- spin_unlock(&t->recvmsg_queue_lock);
-}
-
-static struct
-smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t)
+static void put_recvmsg(struct smbdirect_socket *sc,
+ struct smbdirect_recv_io *recvmsg)
{
- struct smb_direct_recvmsg *recvmsg = NULL;
+ unsigned long flags;
- spin_lock(&t->empty_recvmsg_queue_lock);
- if (!list_empty(&t->empty_recvmsg_queue)) {
- recvmsg = list_first_entry(&t->empty_recvmsg_queue,
- struct smb_direct_recvmsg, list);
- list_del(&recvmsg->list);
+ if (likely(recvmsg->sge.length != 0)) {
+ ib_dma_unmap_single(sc->ib.dev,
+ recvmsg->sge.addr,
+ recvmsg->sge.length,
+ DMA_FROM_DEVICE);
+ recvmsg->sge.length = 0;
}
- spin_unlock(&t->empty_recvmsg_queue_lock);
- return recvmsg;
-}
-static void put_empty_recvmsg(struct smb_direct_transport *t,
- struct smb_direct_recvmsg *recvmsg)
-{
- ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr,
- recvmsg->sge.length, DMA_FROM_DEVICE);
+ spin_lock_irqsave(&sc->recv_io.free.lock, flags);
+ list_add(&recvmsg->list, &sc->recv_io.free.list);
+ spin_unlock_irqrestore(&sc->recv_io.free.lock, flags);
- spin_lock(&t->empty_recvmsg_queue_lock);
- list_add_tail(&recvmsg->list, &t->empty_recvmsg_queue);
- spin_unlock(&t->empty_recvmsg_queue_lock);
+ queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
}
-static void enqueue_reassembly(struct smb_direct_transport *t,
- struct smb_direct_recvmsg *recvmsg,
+static void enqueue_reassembly(struct smbdirect_socket *sc,
+ struct smbdirect_recv_io *recvmsg,
int data_length)
{
- spin_lock(&t->reassembly_queue_lock);
- list_add_tail(&recvmsg->list, &t->reassembly_queue);
- t->reassembly_queue_length++;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
+ list_add_tail(&recvmsg->list, &sc->recv_io.reassembly.list);
+ sc->recv_io.reassembly.queue_length++;
/*
* Make sure reassembly_data_length is updated after list and
* reassembly_queue_length are updated. On the dequeue side
@@ -314,92 +201,238 @@ static void enqueue_reassembly(struct smb_direct_transport *t,
* if reassembly_queue_length and list is up to date
*/
virt_wmb();
- t->reassembly_data_length += data_length;
- spin_unlock(&t->reassembly_queue_lock);
+ sc->recv_io.reassembly.data_length += data_length;
+ spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
}
-static struct smb_direct_recvmsg *get_first_reassembly(struct smb_direct_transport *t)
+static struct smbdirect_recv_io *get_first_reassembly(struct smbdirect_socket *sc)
{
- if (!list_empty(&t->reassembly_queue))
- return list_first_entry(&t->reassembly_queue,
- struct smb_direct_recvmsg, list);
+ if (!list_empty(&sc->recv_io.reassembly.list))
+ return list_first_entry(&sc->recv_io.reassembly.list,
+ struct smbdirect_recv_io, list);
else
return NULL;
}
+static void smb_direct_disconnect_wake_up_all(struct smbdirect_socket *sc)
+{
+ /*
+ * Wake up all waiters in all wait queues
+ * in order to notice the broken connection.
+ */
+ wake_up_all(&sc->status_wait);
+ wake_up_all(&sc->send_io.lcredits.wait_queue);
+ wake_up_all(&sc->send_io.credits.wait_queue);
+ wake_up_all(&sc->send_io.pending.zero_wait_queue);
+ wake_up_all(&sc->recv_io.reassembly.wait_queue);
+ wake_up_all(&sc->rw_io.credits.wait_queue);
+}
+
static void smb_direct_disconnect_rdma_work(struct work_struct *work)
{
- struct smb_direct_transport *t =
- container_of(work, struct smb_direct_transport,
- disconnect_work);
+ struct smbdirect_socket *sc =
+ container_of(work, struct smbdirect_socket, disconnect_work);
- if (t->status == SMB_DIRECT_CS_CONNECTED) {
- t->status = SMB_DIRECT_CS_DISCONNECTING;
- rdma_disconnect(t->cm_id);
+ if (sc->first_error == 0)
+ sc->first_error = -ECONNABORTED;
+
+ /*
+ * make sure this and other work is not queued again
+ * but here we don't block and avoid
+ * disable[_delayed]_work_sync()
+ */
+ disable_work(&sc->disconnect_work);
+ disable_work(&sc->recv_io.posted.refill_work);
+ disable_delayed_work(&sc->idle.timer_work);
+ disable_work(&sc->idle.immediate_work);
+
+ switch (sc->status) {
+ case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED:
+ case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING:
+ case SMBDIRECT_SOCKET_NEGOTIATE_FAILED:
+ case SMBDIRECT_SOCKET_CONNECTED:
+ case SMBDIRECT_SOCKET_ERROR:
+ sc->status = SMBDIRECT_SOCKET_DISCONNECTING;
+ rdma_disconnect(sc->rdma.cm_id);
+ break;
+
+ case SMBDIRECT_SOCKET_CREATED:
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED:
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING:
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED:
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING:
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED:
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED:
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING:
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED:
+ /*
+ * rdma_accept() never reached
+ * RDMA_CM_EVENT_ESTABLISHED
+ */
+ sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
+ break;
+
+ case SMBDIRECT_SOCKET_DISCONNECTING:
+ case SMBDIRECT_SOCKET_DISCONNECTED:
+ case SMBDIRECT_SOCKET_DESTROYED:
+ break;
}
+
+ /*
+ * Wake up all waiters in all wait queues
+ * in order to notice the broken connection.
+ */
+ smb_direct_disconnect_wake_up_all(sc);
}
static void
-smb_direct_disconnect_rdma_connection(struct smb_direct_transport *t)
+smb_direct_disconnect_rdma_connection(struct smbdirect_socket *sc)
{
- if (t->status == SMB_DIRECT_CS_CONNECTED)
- queue_work(smb_direct_wq, &t->disconnect_work);
+ if (sc->first_error == 0)
+ sc->first_error = -ECONNABORTED;
+
+ /*
+ * make sure other work (than disconnect_work) is
+ * not queued again but here we don't block and avoid
+ * disable[_delayed]_work_sync()
+ */
+ disable_work(&sc->recv_io.posted.refill_work);
+ disable_work(&sc->idle.immediate_work);
+ disable_delayed_work(&sc->idle.timer_work);
+
+ switch (sc->status) {
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED:
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED:
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED:
+ case SMBDIRECT_SOCKET_NEGOTIATE_FAILED:
+ case SMBDIRECT_SOCKET_ERROR:
+ case SMBDIRECT_SOCKET_DISCONNECTING:
+ case SMBDIRECT_SOCKET_DISCONNECTED:
+ case SMBDIRECT_SOCKET_DESTROYED:
+ /*
+ * Keep the current error status
+ */
+ break;
+
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_NEEDED:
+ case SMBDIRECT_SOCKET_RESOLVE_ADDR_RUNNING:
+ sc->status = SMBDIRECT_SOCKET_RESOLVE_ADDR_FAILED;
+ break;
+
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_NEEDED:
+ case SMBDIRECT_SOCKET_RESOLVE_ROUTE_RUNNING:
+ sc->status = SMBDIRECT_SOCKET_RESOLVE_ROUTE_FAILED;
+ break;
+
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED:
+ case SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING:
+ sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_FAILED;
+ break;
+
+ case SMBDIRECT_SOCKET_NEGOTIATE_NEEDED:
+ case SMBDIRECT_SOCKET_NEGOTIATE_RUNNING:
+ sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
+ break;
+
+ case SMBDIRECT_SOCKET_CREATED:
+ sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
+ break;
+
+ case SMBDIRECT_SOCKET_CONNECTED:
+ sc->status = SMBDIRECT_SOCKET_ERROR;
+ break;
+ }
+
+ /*
+ * Wake up all waiters in all wait queues
+ * in order to notice the broken connection.
+ */
+ smb_direct_disconnect_wake_up_all(sc);
+
+ queue_work(sc->workqueue, &sc->disconnect_work);
}
static void smb_direct_send_immediate_work(struct work_struct *work)
{
- struct smb_direct_transport *t = container_of(work,
- struct smb_direct_transport, send_immediate_work);
+ struct smbdirect_socket *sc =
+ container_of(work, struct smbdirect_socket, idle.immediate_work);
- if (t->status != SMB_DIRECT_CS_CONNECTED)
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
return;
- smb_direct_post_send_data(t, NULL, NULL, 0, 0);
+ smb_direct_post_send_data(sc, NULL, NULL, 0, 0);
+}
+
+static void smb_direct_idle_connection_timer(struct work_struct *work)
+{
+ struct smbdirect_socket *sc =
+ container_of(work, struct smbdirect_socket, idle.timer_work.work);
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+
+ if (sc->idle.keepalive != SMBDIRECT_KEEPALIVE_NONE) {
+ smb_direct_disconnect_rdma_connection(sc);
+ return;
+ }
+
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
+ return;
+
+ /*
+ * Now use the keepalive timeout (instead of keepalive interval)
+ * in order to wait for a response
+ */
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_PENDING;
+ mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->keepalive_timeout_msec));
+ queue_work(sc->workqueue, &sc->idle.immediate_work);
}
static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id)
{
struct smb_direct_transport *t;
+ struct smbdirect_socket *sc;
+ struct smbdirect_socket_parameters *sp;
struct ksmbd_conn *conn;
- t = kzalloc(sizeof(*t), GFP_KERNEL);
+ t = kzalloc(sizeof(*t), KSMBD_DEFAULT_GFP);
if (!t)
return NULL;
+ sc = &t->socket;
+ smbdirect_socket_init(sc);
+ sp = &sc->parameters;
- t->cm_id = cm_id;
- cm_id->context = t;
-
- t->status = SMB_DIRECT_CS_NEW;
- init_waitqueue_head(&t->wait_status);
-
- spin_lock_init(&t->reassembly_queue_lock);
- INIT_LIST_HEAD(&t->reassembly_queue);
- t->reassembly_data_length = 0;
- t->reassembly_queue_length = 0;
- init_waitqueue_head(&t->wait_reassembly_queue);
- init_waitqueue_head(&t->wait_send_credits);
- init_waitqueue_head(&t->wait_rw_credits);
+ sc->workqueue = smb_direct_wq;
- spin_lock_init(&t->receive_credit_lock);
- spin_lock_init(&t->recvmsg_queue_lock);
- INIT_LIST_HEAD(&t->recvmsg_queue);
+ INIT_WORK(&sc->disconnect_work, smb_direct_disconnect_rdma_work);
- spin_lock_init(&t->empty_recvmsg_queue_lock);
- INIT_LIST_HEAD(&t->empty_recvmsg_queue);
+ sp->negotiate_timeout_msec = SMB_DIRECT_NEGOTIATE_TIMEOUT * 1000;
+ sp->initiator_depth = SMB_DIRECT_CM_INITIATOR_DEPTH;
+ sp->responder_resources = 1;
+ sp->recv_credit_max = smb_direct_receive_credit_max;
+ sp->send_credit_target = smb_direct_send_credit_target;
+ sp->max_send_size = smb_direct_max_send_size;
+ sp->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size;
+ sp->max_recv_size = smb_direct_max_receive_size;
+ sp->max_read_write_size = smb_direct_max_read_write_size;
+ sp->keepalive_interval_msec = SMB_DIRECT_KEEPALIVE_SEND_INTERVAL * 1000;
+ sp->keepalive_timeout_msec = SMB_DIRECT_KEEPALIVE_RECV_TIMEOUT * 1000;
- init_waitqueue_head(&t->wait_send_pending);
- atomic_set(&t->send_pending, 0);
+ sc->rdma.cm_id = cm_id;
+ cm_id->context = sc;
- spin_lock_init(&t->lock_new_recv_credits);
+ sc->ib.dev = sc->rdma.cm_id->device;
- INIT_DELAYED_WORK(&t->post_recv_credits_work,
- smb_direct_post_recv_credits);
- INIT_WORK(&t->send_immediate_work, smb_direct_send_immediate_work);
- INIT_WORK(&t->disconnect_work, smb_direct_disconnect_rdma_work);
+ INIT_DELAYED_WORK(&sc->idle.timer_work, smb_direct_idle_connection_timer);
conn = ksmbd_conn_alloc();
if (!conn)
goto err;
+
+ down_write(&conn_list_lock);
+ hash_add(conn_list, &conn->hlist, 0);
+ up_write(&conn_list_lock);
+
conn->transport = KSMBD_TRANS(t);
KSMBD_TRANS(t)->conn = conn;
KSMBD_TRANS(t)->ops = &ksmbd_smb_direct_transport_ops;
@@ -409,91 +442,120 @@ err:
return NULL;
}
+static void smb_direct_free_transport(struct ksmbd_transport *kt)
+{
+ kfree(SMBD_TRANS(kt));
+}
+
static void free_transport(struct smb_direct_transport *t)
{
- struct smb_direct_recvmsg *recvmsg;
+ struct smbdirect_socket *sc = &t->socket;
+ struct smbdirect_recv_io *recvmsg;
+
+ disable_work_sync(&sc->disconnect_work);
+ if (sc->status < SMBDIRECT_SOCKET_DISCONNECTING)
+ smb_direct_disconnect_rdma_work(&sc->disconnect_work);
+ if (sc->status < SMBDIRECT_SOCKET_DISCONNECTED)
+ wait_event(sc->status_wait, sc->status == SMBDIRECT_SOCKET_DISCONNECTED);
- wake_up_interruptible(&t->wait_send_credits);
+ /*
+ * Wake up all waiters in all wait queues
+ * in order to notice the broken connection.
+ *
+ * Most likely this was already called via
+ * smb_direct_disconnect_rdma_work(), but call it again...
+ */
+ smb_direct_disconnect_wake_up_all(sc);
- ksmbd_debug(RDMA, "wait for all send posted to IB to finish\n");
- wait_event(t->wait_send_pending,
- atomic_read(&t->send_pending) == 0);
+ disable_work_sync(&sc->recv_io.posted.refill_work);
+ disable_delayed_work_sync(&sc->idle.timer_work);
+ disable_work_sync(&sc->idle.immediate_work);
- cancel_work_sync(&t->disconnect_work);
- cancel_delayed_work_sync(&t->post_recv_credits_work);
- cancel_work_sync(&t->send_immediate_work);
+ if (sc->rdma.cm_id)
+ rdma_lock_handler(sc->rdma.cm_id);
- if (t->qp) {
- ib_drain_qp(t->qp);
- ib_mr_pool_destroy(t->qp, &t->qp->rdma_mrs);
- ib_destroy_qp(t->qp);
+ if (sc->ib.qp) {
+ ib_drain_qp(sc->ib.qp);
+ sc->ib.qp = NULL;
+ rdma_destroy_qp(sc->rdma.cm_id);
}
ksmbd_debug(RDMA, "drain the reassembly queue\n");
do {
- spin_lock(&t->reassembly_queue_lock);
- recvmsg = get_first_reassembly(t);
+ unsigned long flags;
+
+ spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
+ recvmsg = get_first_reassembly(sc);
if (recvmsg) {
list_del(&recvmsg->list);
- spin_unlock(&t->reassembly_queue_lock);
- put_recvmsg(t, recvmsg);
+ spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
+ put_recvmsg(sc, recvmsg);
} else {
- spin_unlock(&t->reassembly_queue_lock);
+ spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
}
} while (recvmsg);
- t->reassembly_data_length = 0;
-
- if (t->send_cq)
- ib_free_cq(t->send_cq);
- if (t->recv_cq)
- ib_free_cq(t->recv_cq);
- if (t->pd)
- ib_dealloc_pd(t->pd);
- if (t->cm_id)
- rdma_destroy_id(t->cm_id);
-
- smb_direct_destroy_pools(t);
+ sc->recv_io.reassembly.data_length = 0;
+
+ if (sc->ib.send_cq)
+ ib_free_cq(sc->ib.send_cq);
+ if (sc->ib.recv_cq)
+ ib_free_cq(sc->ib.recv_cq);
+ if (sc->ib.pd)
+ ib_dealloc_pd(sc->ib.pd);
+ if (sc->rdma.cm_id) {
+ rdma_unlock_handler(sc->rdma.cm_id);
+ rdma_destroy_id(sc->rdma.cm_id);
+ }
+
+ smb_direct_destroy_pools(sc);
ksmbd_conn_free(KSMBD_TRANS(t)->conn);
- kfree(t);
}
-static struct smb_direct_sendmsg
-*smb_direct_alloc_sendmsg(struct smb_direct_transport *t)
+static struct smbdirect_send_io
+*smb_direct_alloc_sendmsg(struct smbdirect_socket *sc)
{
- struct smb_direct_sendmsg *msg;
+ struct smbdirect_send_io *msg;
- msg = mempool_alloc(t->sendmsg_mempool, GFP_KERNEL);
+ msg = mempool_alloc(sc->send_io.mem.pool, KSMBD_DEFAULT_GFP);
if (!msg)
return ERR_PTR(-ENOMEM);
- msg->transport = t;
- INIT_LIST_HEAD(&msg->list);
+ msg->socket = sc;
+ INIT_LIST_HEAD(&msg->sibling_list);
msg->num_sge = 0;
return msg;
}
-static void smb_direct_free_sendmsg(struct smb_direct_transport *t,
- struct smb_direct_sendmsg *msg)
+static void smb_direct_free_sendmsg(struct smbdirect_socket *sc,
+ struct smbdirect_send_io *msg)
{
int i;
+ /*
+ * The list needs to be empty!
+ * The caller should take care of it.
+ */
+ WARN_ON_ONCE(!list_empty(&msg->sibling_list));
+
if (msg->num_sge > 0) {
- ib_dma_unmap_single(t->cm_id->device,
+ ib_dma_unmap_single(sc->ib.dev,
msg->sge[0].addr, msg->sge[0].length,
DMA_TO_DEVICE);
for (i = 1; i < msg->num_sge; i++)
- ib_dma_unmap_page(t->cm_id->device,
+ ib_dma_unmap_page(sc->ib.dev,
msg->sge[i].addr, msg->sge[i].length,
DMA_TO_DEVICE);
}
- mempool_free(msg, t->sendmsg_mempool);
+ mempool_free(msg, sc->send_io.mem.pool);
}
-static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg)
+static int smb_direct_check_recvmsg(struct smbdirect_recv_io *recvmsg)
{
- switch (recvmsg->type) {
- case SMB_DIRECT_MSG_DATA_TRANSFER: {
- struct smb_direct_data_transfer *req =
- (struct smb_direct_data_transfer *)recvmsg->packet;
+ struct smbdirect_socket *sc = recvmsg->socket;
+
+ switch (sc->recv_io.expected) {
+ case SMBDIRECT_EXPECT_DATA_TRANSFER: {
+ struct smbdirect_data_transfer *req =
+ (struct smbdirect_data_transfer *)recvmsg->packet;
struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet
+ le32_to_cpu(req->data_offset));
ksmbd_debug(RDMA,
@@ -502,11 +564,11 @@ static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg)
le16_to_cpu(req->credits_requested),
req->data_length, req->remaining_data_length,
hdr->ProtocolId, hdr->Command);
- break;
+ return 0;
}
- case SMB_DIRECT_MSG_NEGOTIATE_REQ: {
- struct smb_direct_negotiate_req *req =
- (struct smb_direct_negotiate_req *)recvmsg->packet;
+ case SMBDIRECT_EXPECT_NEGOTIATE_REQ: {
+ struct smbdirect_negotiate_req *req =
+ (struct smbdirect_negotiate_req *)recvmsg->packet;
ksmbd_debug(RDMA,
"MinVersion: %u, MaxVersion: %u, CreditRequested: %u, MaxSendSize: %u, MaxRecvSize: %u, MaxFragmentedSize: %u\n",
le16_to_cpu(req->min_version),
@@ -524,30 +586,35 @@ static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg)
128 * 1024)
return -ECONNABORTED;
- break;
+ return 0;
}
- default:
- return -EINVAL;
+ case SMBDIRECT_EXPECT_NEGOTIATE_REP:
+ /* client only */
+ break;
}
- return 0;
+
+ /* This is an internal error */
+ return -EINVAL;
}
static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
{
- struct smb_direct_recvmsg *recvmsg;
- struct smb_direct_transport *t;
+ struct smbdirect_recv_io *recvmsg;
+ struct smbdirect_socket *sc;
+ struct smbdirect_socket_parameters *sp;
- recvmsg = container_of(wc->wr_cqe, struct smb_direct_recvmsg, cqe);
- t = recvmsg->transport;
+ recvmsg = container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
+ sc = recvmsg->socket;
+ sp = &sc->parameters;
if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) {
+ put_recvmsg(sc, recvmsg);
if (wc->status != IB_WC_WR_FLUSH_ERR) {
pr_err("Recv error. status='%s (%d)' opcode=%d\n",
ib_wc_status_msg(wc->status), wc->status,
wc->opcode);
- smb_direct_disconnect_rdma_connection(t);
+ smb_direct_disconnect_rdma_connection(sc);
}
- put_empty_recvmsg(t, recvmsg);
return;
}
@@ -558,98 +625,139 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
ib_dma_sync_single_for_cpu(wc->qp->device, recvmsg->sge.addr,
recvmsg->sge.length, DMA_FROM_DEVICE);
- switch (recvmsg->type) {
- case SMB_DIRECT_MSG_NEGOTIATE_REQ:
- if (wc->byte_len < sizeof(struct smb_direct_negotiate_req)) {
- put_empty_recvmsg(t, recvmsg);
+ /*
+ * Reset timer to the keepalive interval in
+ * order to trigger our next keepalive message.
+ */
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_NONE;
+ mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->keepalive_interval_msec));
+
+ switch (sc->recv_io.expected) {
+ case SMBDIRECT_EXPECT_NEGOTIATE_REQ:
+ if (wc->byte_len < sizeof(struct smbdirect_negotiate_req)) {
+ put_recvmsg(sc, recvmsg);
+ smb_direct_disconnect_rdma_connection(sc);
return;
}
- t->negotiation_requested = true;
- t->full_packet_received = true;
- t->status = SMB_DIRECT_CS_CONNECTED;
- enqueue_reassembly(t, recvmsg, 0);
- wake_up_interruptible(&t->wait_status);
- break;
- case SMB_DIRECT_MSG_DATA_TRANSFER: {
- struct smb_direct_data_transfer *data_transfer =
- (struct smb_direct_data_transfer *)recvmsg->packet;
- unsigned int data_length;
- int avail_recvmsg_count, receive_credits;
+ sc->recv_io.reassembly.full_packet_received = true;
+ /*
+ * Some drivers (at least mlx5_ib) might post a
+ * recv completion before RDMA_CM_EVENT_ESTABLISHED,
+ * we need to adjust our expectation in that case.
+ */
+ if (!sc->first_error && sc->status == SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING)
+ sc->status = SMBDIRECT_SOCKET_NEGOTIATE_NEEDED;
+ if (SMBDIRECT_CHECK_STATUS_WARN(sc, SMBDIRECT_SOCKET_NEGOTIATE_NEEDED)) {
+ put_recvmsg(sc, recvmsg);
+ smb_direct_disconnect_rdma_connection(sc);
+ return;
+ }
+ sc->status = SMBDIRECT_SOCKET_NEGOTIATE_RUNNING;
+ enqueue_reassembly(sc, recvmsg, 0);
+ wake_up(&sc->status_wait);
+ return;
+ case SMBDIRECT_EXPECT_DATA_TRANSFER: {
+ struct smbdirect_data_transfer *data_transfer =
+ (struct smbdirect_data_transfer *)recvmsg->packet;
+ u32 remaining_data_length, data_offset, data_length;
+ u16 old_recv_credit_target;
if (wc->byte_len <
- offsetof(struct smb_direct_data_transfer, padding)) {
- put_empty_recvmsg(t, recvmsg);
+ offsetof(struct smbdirect_data_transfer, padding)) {
+ put_recvmsg(sc, recvmsg);
+ smb_direct_disconnect_rdma_connection(sc);
return;
}
+ remaining_data_length = le32_to_cpu(data_transfer->remaining_data_length);
data_length = le32_to_cpu(data_transfer->data_length);
- if (data_length) {
- if (wc->byte_len < sizeof(struct smb_direct_data_transfer) +
- (u64)data_length) {
- put_empty_recvmsg(t, recvmsg);
- return;
- }
+ data_offset = le32_to_cpu(data_transfer->data_offset);
+ if (wc->byte_len < data_offset ||
+ wc->byte_len < (u64)data_offset + data_length) {
+ put_recvmsg(sc, recvmsg);
+ smb_direct_disconnect_rdma_connection(sc);
+ return;
+ }
+ if (remaining_data_length > sp->max_fragmented_recv_size ||
+ data_length > sp->max_fragmented_recv_size ||
+ (u64)remaining_data_length + (u64)data_length >
+ (u64)sp->max_fragmented_recv_size) {
+ put_recvmsg(sc, recvmsg);
+ smb_direct_disconnect_rdma_connection(sc);
+ return;
+ }
- if (t->full_packet_received)
+ if (data_length) {
+ if (sc->recv_io.reassembly.full_packet_received)
recvmsg->first_segment = true;
if (le32_to_cpu(data_transfer->remaining_data_length))
- t->full_packet_received = false;
+ sc->recv_io.reassembly.full_packet_received = false;
else
- t->full_packet_received = true;
-
- enqueue_reassembly(t, recvmsg, (int)data_length);
- wake_up_interruptible(&t->wait_reassembly_queue);
-
- spin_lock(&t->receive_credit_lock);
- receive_credits = --(t->recv_credits);
- avail_recvmsg_count = t->count_avail_recvmsg;
- spin_unlock(&t->receive_credit_lock);
- } else {
- put_empty_recvmsg(t, recvmsg);
-
- spin_lock(&t->receive_credit_lock);
- receive_credits = --(t->recv_credits);
- avail_recvmsg_count = ++(t->count_avail_recvmsg);
- spin_unlock(&t->receive_credit_lock);
+ sc->recv_io.reassembly.full_packet_received = true;
}
- t->recv_credit_target =
+ atomic_dec(&sc->recv_io.posted.count);
+ atomic_dec(&sc->recv_io.credits.count);
+
+ old_recv_credit_target = sc->recv_io.credits.target;
+ sc->recv_io.credits.target =
le16_to_cpu(data_transfer->credits_requested);
+ sc->recv_io.credits.target =
+ min_t(u16, sc->recv_io.credits.target, sp->recv_credit_max);
+ sc->recv_io.credits.target =
+ max_t(u16, sc->recv_io.credits.target, 1);
atomic_add(le16_to_cpu(data_transfer->credits_granted),
- &t->send_credits);
+ &sc->send_io.credits.count);
if (le16_to_cpu(data_transfer->flags) &
- SMB_DIRECT_RESPONSE_REQUESTED)
- queue_work(smb_direct_wq, &t->send_immediate_work);
+ SMBDIRECT_FLAG_RESPONSE_REQUESTED)
+ queue_work(sc->workqueue, &sc->idle.immediate_work);
- if (atomic_read(&t->send_credits) > 0)
- wake_up_interruptible(&t->wait_send_credits);
+ if (atomic_read(&sc->send_io.credits.count) > 0)
+ wake_up(&sc->send_io.credits.wait_queue);
- if (is_receive_credit_post_required(receive_credits, avail_recvmsg_count))
- mod_delayed_work(smb_direct_wq,
- &t->post_recv_credits_work, 0);
- break;
+ if (data_length) {
+ if (sc->recv_io.credits.target > old_recv_credit_target)
+ queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
+
+ enqueue_reassembly(sc, recvmsg, (int)data_length);
+ wake_up(&sc->recv_io.reassembly.wait_queue);
+ } else
+ put_recvmsg(sc, recvmsg);
+
+ return;
}
- default:
+ case SMBDIRECT_EXPECT_NEGOTIATE_REP:
+ /* client only */
break;
}
+
+ /*
+ * This is an internal error!
+ */
+ WARN_ON_ONCE(sc->recv_io.expected != SMBDIRECT_EXPECT_DATA_TRANSFER);
+ put_recvmsg(sc, recvmsg);
+ smb_direct_disconnect_rdma_connection(sc);
}
-static int smb_direct_post_recv(struct smb_direct_transport *t,
- struct smb_direct_recvmsg *recvmsg)
+static int smb_direct_post_recv(struct smbdirect_socket *sc,
+ struct smbdirect_recv_io *recvmsg)
{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
struct ib_recv_wr wr;
int ret;
- recvmsg->sge.addr = ib_dma_map_single(t->cm_id->device,
- recvmsg->packet, t->max_recv_size,
+ recvmsg->sge.addr = ib_dma_map_single(sc->ib.dev,
+ recvmsg->packet,
+ sp->max_recv_size,
DMA_FROM_DEVICE);
- ret = ib_dma_mapping_error(t->cm_id->device, recvmsg->sge.addr);
+ ret = ib_dma_mapping_error(sc->ib.dev, recvmsg->sge.addr);
if (ret)
return ret;
- recvmsg->sge.length = t->max_recv_size;
- recvmsg->sge.lkey = t->pd->local_dma_lkey;
+ recvmsg->sge.length = sp->max_recv_size;
+ recvmsg->sge.lkey = sc->ib.pd->local_dma_lkey;
recvmsg->cqe.done = recv_done;
wr.wr_cqe = &recvmsg->cqe;
@@ -657,13 +765,14 @@ static int smb_direct_post_recv(struct smb_direct_transport *t,
wr.sg_list = &recvmsg->sge;
wr.num_sge = 1;
- ret = ib_post_recv(t->qp, &wr, NULL);
+ ret = ib_post_recv(sc->ib.qp, &wr, NULL);
if (ret) {
pr_err("Can't post recv: %d\n", ret);
- ib_dma_unmap_single(t->cm_id->device,
+ ib_dma_unmap_single(sc->ib.dev,
recvmsg->sge.addr, recvmsg->sge.length,
DMA_FROM_DEVICE);
- smb_direct_disconnect_rdma_connection(t);
+ recvmsg->sge.length = 0;
+ smb_direct_disconnect_rdma_connection(sc);
return ret;
}
return ret;
@@ -672,15 +781,16 @@ static int smb_direct_post_recv(struct smb_direct_transport *t,
static int smb_direct_read(struct ksmbd_transport *t, char *buf,
unsigned int size, int unused)
{
- struct smb_direct_recvmsg *recvmsg;
- struct smb_direct_data_transfer *data_transfer;
+ struct smbdirect_recv_io *recvmsg;
+ struct smbdirect_data_transfer *data_transfer;
int to_copy, to_read, data_read, offset;
u32 data_length, remaining_data_length, data_offset;
int rc;
- struct smb_direct_transport *st = smb_trans_direct_transfort(t);
+ struct smb_direct_transport *st = SMBD_TRANS(t);
+ struct smbdirect_socket *sc = &st->socket;
again:
- if (st->status != SMB_DIRECT_CS_CONNECTED) {
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
pr_err("disconnected\n");
return -ENOTCONN;
}
@@ -690,9 +800,10 @@ again:
* the only one reading from the front of the queue. The transport
* may add more entries to the back of the queue at the same time
*/
- if (st->reassembly_data_length >= size) {
+ if (sc->recv_io.reassembly.data_length >= size) {
int queue_length;
int queue_removed = 0;
+ unsigned long flags;
/*
* Need to make sure reassembly_data_length is read before
@@ -702,13 +813,13 @@ again:
* updated in SOFTIRQ as more data is received
*/
virt_rmb();
- queue_length = st->reassembly_queue_length;
+ queue_length = sc->recv_io.reassembly.queue_length;
data_read = 0;
to_read = size;
- offset = st->first_entry_offset;
+ offset = sc->recv_io.reassembly.first_entry_offset;
while (data_read < size) {
- recvmsg = get_first_reassembly(st);
- data_transfer = smb_direct_recvmsg_payload(recvmsg);
+ recvmsg = get_first_reassembly(sc);
+ data_transfer = smbdirect_recv_io_payload(recvmsg);
data_length = le32_to_cpu(data_transfer->data_length);
remaining_data_length =
le32_to_cpu(data_transfer->remaining_data_length);
@@ -748,12 +859,12 @@ again:
if (queue_length) {
list_del(&recvmsg->list);
} else {
- spin_lock_irq(&st->reassembly_queue_lock);
+ spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
list_del(&recvmsg->list);
- spin_unlock_irq(&st->reassembly_queue_lock);
+ spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
}
queue_removed++;
- put_recvmsg(st, recvmsg);
+ put_recvmsg(sc, recvmsg);
offset = 0;
} else {
offset += to_copy;
@@ -763,34 +874,24 @@ again:
data_read += to_copy;
}
- spin_lock_irq(&st->reassembly_queue_lock);
- st->reassembly_data_length -= data_read;
- st->reassembly_queue_length -= queue_removed;
- spin_unlock_irq(&st->reassembly_queue_lock);
-
- spin_lock(&st->receive_credit_lock);
- st->count_avail_recvmsg += queue_removed;
- if (is_receive_credit_post_required(st->recv_credits, st->count_avail_recvmsg)) {
- spin_unlock(&st->receive_credit_lock);
- mod_delayed_work(smb_direct_wq,
- &st->post_recv_credits_work, 0);
- } else {
- spin_unlock(&st->receive_credit_lock);
- }
+ spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
+ sc->recv_io.reassembly.data_length -= data_read;
+ sc->recv_io.reassembly.queue_length -= queue_removed;
+ spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
- st->first_entry_offset = offset;
+ sc->recv_io.reassembly.first_entry_offset = offset;
ksmbd_debug(RDMA,
"returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n",
- data_read, st->reassembly_data_length,
- st->first_entry_offset);
+ data_read, sc->recv_io.reassembly.data_length,
+ sc->recv_io.reassembly.first_entry_offset);
read_rfc1002_done:
return data_read;
}
ksmbd_debug(RDMA, "wait_event on more data\n");
- rc = wait_event_interruptible(st->wait_reassembly_queue,
- st->reassembly_data_length >= size ||
- st->status != SMB_DIRECT_CS_CONNECTED);
+ rc = wait_event_interruptible(sc->recv_io.reassembly.wait_queue,
+ sc->recv_io.reassembly.data_length >= size ||
+ sc->status != SMBDIRECT_SOCKET_CONNECTED);
if (rc)
return -EINTR;
@@ -799,124 +900,127 @@ read_rfc1002_done:
static void smb_direct_post_recv_credits(struct work_struct *work)
{
- struct smb_direct_transport *t = container_of(work,
- struct smb_direct_transport, post_recv_credits_work.work);
- struct smb_direct_recvmsg *recvmsg;
- int receive_credits, credits = 0;
+ struct smbdirect_socket *sc =
+ container_of(work, struct smbdirect_socket, recv_io.posted.refill_work);
+ struct smbdirect_recv_io *recvmsg;
+ int credits = 0;
int ret;
- int use_free = 1;
- spin_lock(&t->receive_credit_lock);
- receive_credits = t->recv_credits;
- spin_unlock(&t->receive_credit_lock);
-
- if (receive_credits < t->recv_credit_target) {
+ if (atomic_read(&sc->recv_io.credits.count) < sc->recv_io.credits.target) {
while (true) {
- if (use_free)
- recvmsg = get_free_recvmsg(t);
- else
- recvmsg = get_empty_recvmsg(t);
- if (!recvmsg) {
- if (use_free) {
- use_free = 0;
- continue;
- } else {
- break;
- }
- }
+ recvmsg = get_free_recvmsg(sc);
+ if (!recvmsg)
+ break;
- recvmsg->type = SMB_DIRECT_MSG_DATA_TRANSFER;
recvmsg->first_segment = false;
- ret = smb_direct_post_recv(t, recvmsg);
+ ret = smb_direct_post_recv(sc, recvmsg);
if (ret) {
pr_err("Can't post recv: %d\n", ret);
- put_recvmsg(t, recvmsg);
+ put_recvmsg(sc, recvmsg);
break;
}
credits++;
+
+ atomic_inc(&sc->recv_io.posted.count);
}
}
- spin_lock(&t->receive_credit_lock);
- t->recv_credits += credits;
- t->count_avail_recvmsg -= credits;
- spin_unlock(&t->receive_credit_lock);
-
- spin_lock(&t->lock_new_recv_credits);
- t->new_recv_credits += credits;
- spin_unlock(&t->lock_new_recv_credits);
-
if (credits)
- queue_work(smb_direct_wq, &t->send_immediate_work);
+ queue_work(sc->workqueue, &sc->idle.immediate_work);
}
static void send_done(struct ib_cq *cq, struct ib_wc *wc)
{
- struct smb_direct_sendmsg *sendmsg, *sibling;
- struct smb_direct_transport *t;
- struct list_head *pos, *prev, *end;
+ struct smbdirect_send_io *sendmsg, *sibling, *next;
+ struct smbdirect_socket *sc;
+ int lcredits = 0;
- sendmsg = container_of(wc->wr_cqe, struct smb_direct_sendmsg, cqe);
- t = sendmsg->transport;
+ sendmsg = container_of(wc->wr_cqe, struct smbdirect_send_io, cqe);
+ sc = sendmsg->socket;
ksmbd_debug(RDMA, "Send completed. status='%s (%d)', opcode=%d\n",
ib_wc_status_msg(wc->status), wc->status,
wc->opcode);
+ /*
+ * Free possible siblings and then the main send_io
+ */
+ list_for_each_entry_safe(sibling, next, &sendmsg->sibling_list, sibling_list) {
+ list_del_init(&sibling->sibling_list);
+ smb_direct_free_sendmsg(sc, sibling);
+ lcredits += 1;
+ }
+ /* Note this frees wc->wr_cqe, but not wc */
+ smb_direct_free_sendmsg(sc, sendmsg);
+ lcredits += 1;
+
if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
pr_err("Send error. status='%s (%d)', opcode=%d\n",
ib_wc_status_msg(wc->status), wc->status,
wc->opcode);
- smb_direct_disconnect_rdma_connection(t);
+ smb_direct_disconnect_rdma_connection(sc);
+ return;
}
- if (atomic_dec_and_test(&t->send_pending))
- wake_up(&t->wait_send_pending);
-
- /* iterate and free the list of messages in reverse. the list's head
- * is invalid.
- */
- for (pos = &sendmsg->list, prev = pos->prev, end = sendmsg->list.next;
- prev != end; pos = prev, prev = prev->prev) {
- sibling = container_of(pos, struct smb_direct_sendmsg, list);
- smb_direct_free_sendmsg(t, sibling);
- }
+ atomic_add(lcredits, &sc->send_io.lcredits.count);
+ wake_up(&sc->send_io.lcredits.wait_queue);
- sibling = container_of(pos, struct smb_direct_sendmsg, list);
- smb_direct_free_sendmsg(t, sibling);
+ if (atomic_dec_and_test(&sc->send_io.pending.count))
+ wake_up(&sc->send_io.pending.zero_wait_queue);
}
-static int manage_credits_prior_sending(struct smb_direct_transport *t)
+static int manage_credits_prior_sending(struct smbdirect_socket *sc)
{
int new_credits;
- spin_lock(&t->lock_new_recv_credits);
- new_credits = t->new_recv_credits;
- t->new_recv_credits = 0;
- spin_unlock(&t->lock_new_recv_credits);
+ if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target)
+ return 0;
+ new_credits = atomic_read(&sc->recv_io.posted.count);
+ if (new_credits == 0)
+ return 0;
+
+ new_credits -= atomic_read(&sc->recv_io.credits.count);
+ if (new_credits <= 0)
+ return 0;
+
+ atomic_add(new_credits, &sc->recv_io.credits.count);
return new_credits;
}
-static int smb_direct_post_send(struct smb_direct_transport *t,
+static int manage_keep_alive_before_sending(struct smbdirect_socket *sc)
+{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+
+ if (sc->idle.keepalive == SMBDIRECT_KEEPALIVE_PENDING) {
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_SENT;
+ /*
+ * Now use the keepalive timeout (instead of keepalive interval)
+ * in order to wait for a response
+ */
+ mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->keepalive_timeout_msec));
+ return 1;
+ }
+ return 0;
+}
+
+static int smb_direct_post_send(struct smbdirect_socket *sc,
struct ib_send_wr *wr)
{
int ret;
- atomic_inc(&t->send_pending);
- ret = ib_post_send(t->qp, wr, NULL);
+ atomic_inc(&sc->send_io.pending.count);
+ ret = ib_post_send(sc->ib.qp, wr, NULL);
if (ret) {
pr_err("failed to post send: %d\n", ret);
- if (atomic_dec_and_test(&t->send_pending))
- wake_up(&t->wait_send_pending);
- smb_direct_disconnect_rdma_connection(t);
+ smb_direct_disconnect_rdma_connection(sc);
}
return ret;
}
-static void smb_direct_send_ctx_init(struct smb_direct_transport *t,
- struct smb_direct_send_ctx *send_ctx,
+static void smb_direct_send_ctx_init(struct smbdirect_send_batch *send_ctx,
bool need_invalidate_rkey,
unsigned int remote_key)
{
@@ -926,47 +1030,60 @@ static void smb_direct_send_ctx_init(struct smb_direct_transport *t,
send_ctx->remote_key = remote_key;
}
-static int smb_direct_flush_send_list(struct smb_direct_transport *t,
- struct smb_direct_send_ctx *send_ctx,
+static int smb_direct_flush_send_list(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *send_ctx,
bool is_last)
{
- struct smb_direct_sendmsg *first, *last;
+ struct smbdirect_send_io *first, *last;
int ret;
if (list_empty(&send_ctx->msg_list))
return 0;
first = list_first_entry(&send_ctx->msg_list,
- struct smb_direct_sendmsg,
- list);
+ struct smbdirect_send_io,
+ sibling_list);
last = list_last_entry(&send_ctx->msg_list,
- struct smb_direct_sendmsg,
- list);
+ struct smbdirect_send_io,
+ sibling_list);
+
+ if (send_ctx->need_invalidate_rkey) {
+ first->wr.opcode = IB_WR_SEND_WITH_INV;
+ first->wr.ex.invalidate_rkey = send_ctx->remote_key;
+ send_ctx->need_invalidate_rkey = false;
+ send_ctx->remote_key = 0;
+ }
last->wr.send_flags = IB_SEND_SIGNALED;
last->wr.wr_cqe = &last->cqe;
- if (is_last && send_ctx->need_invalidate_rkey) {
- last->wr.opcode = IB_WR_SEND_WITH_INV;
- last->wr.ex.invalidate_rkey = send_ctx->remote_key;
- }
- ret = smb_direct_post_send(t, &first->wr);
- if (!ret) {
- smb_direct_send_ctx_init(t, send_ctx,
- send_ctx->need_invalidate_rkey,
- send_ctx->remote_key);
- } else {
- atomic_add(send_ctx->wr_cnt, &t->send_credits);
- wake_up(&t->wait_send_credits);
- list_for_each_entry_safe(first, last, &send_ctx->msg_list,
- list) {
- smb_direct_free_sendmsg(t, first);
+ /*
+ * Remove last from send_ctx->msg_list
+ * and splice the rest of send_ctx->msg_list
+ * to last->sibling_list.
+ *
+ * send_ctx->msg_list is a valid empty list
+ * at the end.
+ */
+ list_del_init(&last->sibling_list);
+ list_splice_tail_init(&send_ctx->msg_list, &last->sibling_list);
+ send_ctx->wr_cnt = 0;
+
+ ret = smb_direct_post_send(sc, &first->wr);
+ if (ret) {
+ struct smbdirect_send_io *sibling, *next;
+
+ list_for_each_entry_safe(sibling, next, &last->sibling_list, sibling_list) {
+ list_del_init(&sibling->sibling_list);
+ smb_direct_free_sendmsg(sc, sibling);
}
+ smb_direct_free_sendmsg(sc, last);
}
+
return ret;
}
-static int wait_for_credits(struct smb_direct_transport *t,
+static int wait_for_credits(struct smbdirect_socket *sc,
wait_queue_head_t *waitq, atomic_t *total_credits,
int needed)
{
@@ -979,61 +1096,85 @@ static int wait_for_credits(struct smb_direct_transport *t,
atomic_add(needed, total_credits);
ret = wait_event_interruptible(*waitq,
atomic_read(total_credits) >= needed ||
- t->status != SMB_DIRECT_CS_CONNECTED);
+ sc->status != SMBDIRECT_SOCKET_CONNECTED);
- if (t->status != SMB_DIRECT_CS_CONNECTED)
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
return -ENOTCONN;
else if (ret < 0)
return ret;
} while (true);
}
-static int wait_for_send_credits(struct smb_direct_transport *t,
- struct smb_direct_send_ctx *send_ctx)
+static int wait_for_send_lcredit(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *send_ctx)
+{
+ if (send_ctx && (atomic_read(&sc->send_io.lcredits.count) <= 1)) {
+ int ret;
+
+ ret = smb_direct_flush_send_list(sc, send_ctx, false);
+ if (ret)
+ return ret;
+ }
+
+ return wait_for_credits(sc,
+ &sc->send_io.lcredits.wait_queue,
+ &sc->send_io.lcredits.count,
+ 1);
+}
+
+static int wait_for_send_credits(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *send_ctx)
{
int ret;
if (send_ctx &&
- (send_ctx->wr_cnt >= 16 || atomic_read(&t->send_credits) <= 1)) {
- ret = smb_direct_flush_send_list(t, send_ctx, false);
+ (send_ctx->wr_cnt >= 16 || atomic_read(&sc->send_io.credits.count) <= 1)) {
+ ret = smb_direct_flush_send_list(sc, send_ctx, false);
if (ret)
return ret;
}
- return wait_for_credits(t, &t->wait_send_credits, &t->send_credits, 1);
+ return wait_for_credits(sc, &sc->send_io.credits.wait_queue, &sc->send_io.credits.count, 1);
}
-static int wait_for_rw_credits(struct smb_direct_transport *t, int credits)
+static int wait_for_rw_credits(struct smbdirect_socket *sc, int credits)
{
- return wait_for_credits(t, &t->wait_rw_credits, &t->rw_credits, credits);
+ return wait_for_credits(sc,
+ &sc->rw_io.credits.wait_queue,
+ &sc->rw_io.credits.count,
+ credits);
}
-static int calc_rw_credits(struct smb_direct_transport *t,
+static int calc_rw_credits(struct smbdirect_socket *sc,
char *buf, unsigned int len)
{
return DIV_ROUND_UP(get_buf_page_count(buf, len),
- t->pages_per_rw_credit);
+ sc->rw_io.credits.num_pages);
}
-static int smb_direct_create_header(struct smb_direct_transport *t,
+static int smb_direct_create_header(struct smbdirect_socket *sc,
int size, int remaining_data_length,
- struct smb_direct_sendmsg **sendmsg_out)
+ struct smbdirect_send_io **sendmsg_out)
{
- struct smb_direct_sendmsg *sendmsg;
- struct smb_direct_data_transfer *packet;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smbdirect_send_io *sendmsg;
+ struct smbdirect_data_transfer *packet;
int header_length;
int ret;
- sendmsg = smb_direct_alloc_sendmsg(t);
+ sendmsg = smb_direct_alloc_sendmsg(sc);
if (IS_ERR(sendmsg))
return PTR_ERR(sendmsg);
/* Fill in the packet header */
- packet = (struct smb_direct_data_transfer *)sendmsg->packet;
- packet->credits_requested = cpu_to_le16(t->send_credit_target);
- packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(t));
+ packet = (struct smbdirect_data_transfer *)sendmsg->packet;
+ packet->credits_requested = cpu_to_le16(sp->send_credit_target);
+ packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(sc));
packet->flags = 0;
+ if (manage_keep_alive_before_sending(sc))
+ packet->flags |= cpu_to_le16(SMBDIRECT_FLAG_RESPONSE_REQUESTED);
+
packet->reserved = 0;
if (!size)
packet->data_offset = 0;
@@ -1052,25 +1193,25 @@ static int smb_direct_create_header(struct smb_direct_transport *t,
le32_to_cpu(packet->remaining_data_length));
/* Map the packet to DMA */
- header_length = sizeof(struct smb_direct_data_transfer);
+ header_length = sizeof(struct smbdirect_data_transfer);
/* If this is a packet without payload, don't send padding */
if (!size)
header_length =
- offsetof(struct smb_direct_data_transfer, padding);
+ offsetof(struct smbdirect_data_transfer, padding);
- sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device,
+ sendmsg->sge[0].addr = ib_dma_map_single(sc->ib.dev,
(void *)packet,
header_length,
DMA_TO_DEVICE);
- ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr);
+ ret = ib_dma_mapping_error(sc->ib.dev, sendmsg->sge[0].addr);
if (ret) {
- smb_direct_free_sendmsg(t, sendmsg);
+ smb_direct_free_sendmsg(sc, sendmsg);
return ret;
}
sendmsg->num_sge = 1;
sendmsg->sge[0].length = header_length;
- sendmsg->sge[0].lkey = t->pd->local_dma_lkey;
+ sendmsg->sge[0].lkey = sc->ib.pd->local_dma_lkey;
*sendmsg_out = sendmsg;
return 0;
@@ -1120,14 +1261,14 @@ static int get_mapped_sg_list(struct ib_device *device, void *buf, int size,
return ib_dma_map_sg(device, sg_list, npages, dir);
}
-static int post_sendmsg(struct smb_direct_transport *t,
- struct smb_direct_send_ctx *send_ctx,
- struct smb_direct_sendmsg *msg)
+static int post_sendmsg(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *send_ctx,
+ struct smbdirect_send_io *msg)
{
int i;
for (i = 0; i < msg->num_sge; i++)
- ib_dma_sync_single_for_device(t->cm_id->device,
+ ib_dma_sync_single_for_device(sc->ib.dev,
msg->sge[i].addr, msg->sge[i].length,
DMA_TO_DEVICE);
@@ -1141,65 +1282,67 @@ static int post_sendmsg(struct smb_direct_transport *t,
msg->wr.wr_cqe = NULL;
msg->wr.send_flags = 0;
if (!list_empty(&send_ctx->msg_list)) {
- struct smb_direct_sendmsg *last;
+ struct smbdirect_send_io *last;
last = list_last_entry(&send_ctx->msg_list,
- struct smb_direct_sendmsg,
- list);
+ struct smbdirect_send_io,
+ sibling_list);
last->wr.next = &msg->wr;
}
- list_add_tail(&msg->list, &send_ctx->msg_list);
+ list_add_tail(&msg->sibling_list, &send_ctx->msg_list);
send_ctx->wr_cnt++;
return 0;
}
msg->wr.wr_cqe = &msg->cqe;
msg->wr.send_flags = IB_SEND_SIGNALED;
- return smb_direct_post_send(t, &msg->wr);
+ return smb_direct_post_send(sc, &msg->wr);
}
-static int smb_direct_post_send_data(struct smb_direct_transport *t,
- struct smb_direct_send_ctx *send_ctx,
+static int smb_direct_post_send_data(struct smbdirect_socket *sc,
+ struct smbdirect_send_batch *send_ctx,
struct kvec *iov, int niov,
int remaining_data_length)
{
int i, j, ret;
- struct smb_direct_sendmsg *msg;
+ struct smbdirect_send_io *msg;
int data_length;
- struct scatterlist sg[SMB_DIRECT_MAX_SEND_SGES - 1];
+ struct scatterlist sg[SMBDIRECT_SEND_IO_MAX_SGE - 1];
- ret = wait_for_send_credits(t, send_ctx);
+ ret = wait_for_send_lcredit(sc, send_ctx);
if (ret)
- return ret;
+ goto lcredit_failed;
+
+ ret = wait_for_send_credits(sc, send_ctx);
+ if (ret)
+ goto credit_failed;
data_length = 0;
for (i = 0; i < niov; i++)
data_length += iov[i].iov_len;
- ret = smb_direct_create_header(t, data_length, remaining_data_length,
+ ret = smb_direct_create_header(sc, data_length, remaining_data_length,
&msg);
- if (ret) {
- atomic_inc(&t->send_credits);
- return ret;
- }
+ if (ret)
+ goto header_failed;
for (i = 0; i < niov; i++) {
struct ib_sge *sge;
int sg_cnt;
- sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES - 1);
- sg_cnt = get_mapped_sg_list(t->cm_id->device,
+ sg_init_table(sg, SMBDIRECT_SEND_IO_MAX_SGE - 1);
+ sg_cnt = get_mapped_sg_list(sc->ib.dev,
iov[i].iov_base, iov[i].iov_len,
- sg, SMB_DIRECT_MAX_SEND_SGES - 1,
+ sg, SMBDIRECT_SEND_IO_MAX_SGE - 1,
DMA_TO_DEVICE);
if (sg_cnt <= 0) {
pr_err("failed to map buffer\n");
ret = -ENOMEM;
goto err;
- } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES) {
+ } else if (sg_cnt + msg->num_sge > SMBDIRECT_SEND_IO_MAX_SGE) {
pr_err("buffer not fitted into sges\n");
ret = -E2BIG;
- ib_dma_unmap_sg(t->cm_id->device, sg, sg_cnt,
+ ib_dma_unmap_sg(sc->ib.dev, sg, sg_cnt,
DMA_TO_DEVICE);
goto err;
}
@@ -1208,18 +1351,22 @@ static int smb_direct_post_send_data(struct smb_direct_transport *t,
sge = &msg->sge[msg->num_sge];
sge->addr = sg_dma_address(&sg[j]);
sge->length = sg_dma_len(&sg[j]);
- sge->lkey = t->pd->local_dma_lkey;
+ sge->lkey = sc->ib.pd->local_dma_lkey;
msg->num_sge++;
}
}
- ret = post_sendmsg(t, send_ctx, msg);
+ ret = post_sendmsg(sc, send_ctx, msg);
if (ret)
goto err;
return 0;
err:
- smb_direct_free_sendmsg(t, msg);
- atomic_inc(&t->send_credits);
+ smb_direct_free_sendmsg(sc, msg);
+header_failed:
+ atomic_inc(&sc->send_io.credits.count);
+credit_failed:
+ atomic_inc(&sc->send_io.lcredits.count);
+lcredit_failed:
return ret;
}
@@ -1227,79 +1374,133 @@ static int smb_direct_writev(struct ksmbd_transport *t,
struct kvec *iov, int niovs, int buflen,
bool need_invalidate, unsigned int remote_key)
{
- struct smb_direct_transport *st = smb_trans_direct_transfort(t);
- int remaining_data_length;
- int start, i, j;
- int max_iov_size = st->max_send_size -
- sizeof(struct smb_direct_data_transfer);
+ struct smb_direct_transport *st = SMBD_TRANS(t);
+ struct smbdirect_socket *sc = &st->socket;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ size_t remaining_data_length;
+ size_t iov_idx;
+ size_t iov_ofs;
+ size_t max_iov_size = sp->max_send_size -
+ sizeof(struct smbdirect_data_transfer);
int ret;
- struct kvec vec;
- struct smb_direct_send_ctx send_ctx;
+ struct smbdirect_send_batch send_ctx;
+ int error = 0;
- if (st->status != SMB_DIRECT_CS_CONNECTED)
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
return -ENOTCONN;
//FIXME: skip RFC1002 header..
+ if (WARN_ON_ONCE(niovs <= 1 || iov[0].iov_len != 4))
+ return -EINVAL;
buflen -= 4;
+ iov_idx = 1;
+ iov_ofs = 0;
remaining_data_length = buflen;
ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen);
- smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key);
- start = i = 1;
- buflen = 0;
- while (true) {
- buflen += iov[i].iov_len;
- if (buflen > max_iov_size) {
- if (i > start) {
- remaining_data_length -=
- (buflen - iov[i].iov_len);
- ret = smb_direct_post_send_data(st, &send_ctx,
- &iov[start], i - start,
- remaining_data_length);
- if (ret)
+ smb_direct_send_ctx_init(&send_ctx, need_invalidate, remote_key);
+ while (remaining_data_length) {
+ struct kvec vecs[SMBDIRECT_SEND_IO_MAX_SGE - 1]; /* minus smbdirect hdr */
+ size_t possible_bytes = max_iov_size;
+ size_t possible_vecs;
+ size_t bytes = 0;
+ size_t nvecs = 0;
+
+ /*
+ * For the last message remaining_data_length should be
+ * have been 0 already!
+ */
+ if (WARN_ON_ONCE(iov_idx >= niovs)) {
+ error = -EINVAL;
+ goto done;
+ }
+
+ /*
+ * We have 2 factors which limit the arguments we pass
+ * to smb_direct_post_send_data():
+ *
+ * 1. The number of supported sges for the send,
+ * while one is reserved for the smbdirect header.
+ * And we currently need one SGE per page.
+ * 2. The number of negotiated payload bytes per send.
+ */
+ possible_vecs = min_t(size_t, ARRAY_SIZE(vecs), niovs - iov_idx);
+
+ while (iov_idx < niovs && possible_vecs && possible_bytes) {
+ struct kvec *v = &vecs[nvecs];
+ int page_count;
+
+ v->iov_base = ((u8 *)iov[iov_idx].iov_base) + iov_ofs;
+ v->iov_len = min_t(size_t,
+ iov[iov_idx].iov_len - iov_ofs,
+ possible_bytes);
+ page_count = get_buf_page_count(v->iov_base, v->iov_len);
+ if (page_count > possible_vecs) {
+ /*
+ * If the number of pages in the buffer
+ * is to much (because we currently require
+ * one SGE per page), we need to limit the
+ * length.
+ *
+ * We know possible_vecs is at least 1,
+ * so we always keep the first page.
+ *
+ * We need to calculate the number extra
+ * pages (epages) we can also keep.
+ *
+ * We calculate the number of bytes in the
+ * first page (fplen), this should never be
+ * larger than v->iov_len because page_count is
+ * at least 2, but adding a limitation feels
+ * better.
+ *
+ * Then we calculate the number of bytes (elen)
+ * we can keep for the extra pages.
+ */
+ size_t epages = possible_vecs - 1;
+ size_t fpofs = offset_in_page(v->iov_base);
+ size_t fplen = min_t(size_t, PAGE_SIZE - fpofs, v->iov_len);
+ size_t elen = min_t(size_t, v->iov_len - fplen, epages*PAGE_SIZE);
+
+ v->iov_len = fplen + elen;
+ page_count = get_buf_page_count(v->iov_base, v->iov_len);
+ if (WARN_ON_ONCE(page_count > possible_vecs)) {
+ /*
+ * Something went wrong in the above
+ * logic...
+ */
+ error = -EINVAL;
goto done;
- } else {
- /* iov[start] is too big, break it */
- int nvec = (buflen + max_iov_size - 1) /
- max_iov_size;
-
- for (j = 0; j < nvec; j++) {
- vec.iov_base =
- (char *)iov[start].iov_base +
- j * max_iov_size;
- vec.iov_len =
- min_t(int, max_iov_size,
- buflen - max_iov_size * j);
- remaining_data_length -= vec.iov_len;
- ret = smb_direct_post_send_data(st, &send_ctx, &vec, 1,
- remaining_data_length);
- if (ret)
- goto done;
}
- i++;
- if (i == niovs)
- break;
}
- start = i;
- buflen = 0;
- } else {
- i++;
- if (i == niovs) {
- /* send out all remaining vecs */
- remaining_data_length -= buflen;
- ret = smb_direct_post_send_data(st, &send_ctx,
- &iov[start], i - start,
- remaining_data_length);
- if (ret)
- goto done;
- break;
+ possible_vecs -= page_count;
+ nvecs += 1;
+ possible_bytes -= v->iov_len;
+ bytes += v->iov_len;
+
+ iov_ofs += v->iov_len;
+ if (iov_ofs >= iov[iov_idx].iov_len) {
+ iov_idx += 1;
+ iov_ofs = 0;
}
}
+
+ remaining_data_length -= bytes;
+
+ ret = smb_direct_post_send_data(sc, &send_ctx,
+ vecs, nvecs,
+ remaining_data_length);
+ if (unlikely(ret)) {
+ error = ret;
+ goto done;
+ }
}
done:
- ret = smb_direct_flush_send_list(st, &send_ctx, true);
+ ret = smb_direct_flush_send_list(sc, &send_ctx, true);
+ if (unlikely(!ret && error))
+ ret = error;
/*
* As an optimization, we don't wait for individual I/O to finish
@@ -1308,16 +1509,22 @@ done:
* that means all the I/Os have been out and we are good to return
*/
- wait_event(st->wait_send_pending,
- atomic_read(&st->send_pending) == 0);
+ wait_event(sc->send_io.pending.zero_wait_queue,
+ atomic_read(&sc->send_io.pending.count) == 0 ||
+ sc->status != SMBDIRECT_SOCKET_CONNECTED);
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED && ret == 0)
+ ret = -ENOTCONN;
+
return ret;
}
static void smb_direct_free_rdma_rw_msg(struct smb_direct_transport *t,
- struct smb_direct_rdma_rw_msg *msg,
+ struct smbdirect_rw_io *msg,
enum dma_data_direction dir)
{
- rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port,
+ struct smbdirect_socket *sc = &t->socket;
+
+ rdma_rw_ctx_destroy(&msg->rdma_ctx, sc->ib.qp, sc->ib.qp->port,
msg->sgt.sgl, msg->sgt.nents, dir);
sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE);
kfree(msg);
@@ -1326,16 +1533,16 @@ static void smb_direct_free_rdma_rw_msg(struct smb_direct_transport *t,
static void read_write_done(struct ib_cq *cq, struct ib_wc *wc,
enum dma_data_direction dir)
{
- struct smb_direct_rdma_rw_msg *msg = container_of(wc->wr_cqe,
- struct smb_direct_rdma_rw_msg, cqe);
- struct smb_direct_transport *t = msg->t;
+ struct smbdirect_rw_io *msg =
+ container_of(wc->wr_cqe, struct smbdirect_rw_io, cqe);
+ struct smbdirect_socket *sc = msg->socket;
if (wc->status != IB_WC_SUCCESS) {
- msg->status = -EIO;
+ msg->error = -EIO;
pr_err("read/write error. opcode = %d, status = %s(%d)\n",
wc->opcode, ib_wc_status_msg(wc->status), wc->status);
if (wc->status != IB_WC_WR_FLUSH_ERR)
- smb_direct_disconnect_rdma_connection(t);
+ smb_direct_disconnect_rdma_connection(sc);
}
complete(msg->completion);
@@ -1353,11 +1560,13 @@ static void write_done(struct ib_cq *cq, struct ib_wc *wc)
static int smb_direct_rdma_xmit(struct smb_direct_transport *t,
void *buf, int buf_len,
- struct smb2_buffer_desc_v1 *desc,
+ struct smbdirect_buffer_descriptor_v1 *desc,
unsigned int desc_len,
bool is_read)
{
- struct smb_direct_rdma_rw_msg *msg, *next_msg;
+ struct smbdirect_socket *sc = &t->socket;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smbdirect_rw_io *msg, *next_msg;
int i, ret;
DECLARE_COMPLETION_ONSTACK(completion);
struct ib_send_wr *first_wr;
@@ -1366,10 +1575,10 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t,
int credits_needed;
unsigned int desc_buf_len, desc_num = 0;
- if (t->status != SMB_DIRECT_CS_CONNECTED)
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
return -ENOTCONN;
- if (buf_len > t->max_rdma_rw_size)
+ if (buf_len > sp->max_read_write_size)
return -EINVAL;
/* calculate needed credits */
@@ -1389,16 +1598,16 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t,
buf_len = 0;
}
- credits_needed += calc_rw_credits(t, desc_buf, desc_buf_len);
+ credits_needed += calc_rw_credits(sc, desc_buf, desc_buf_len);
desc_buf += desc_buf_len;
buf_len -= desc_buf_len;
desc_num++;
}
ksmbd_debug(RDMA, "RDMA %s, len %#x, needed credits %#x\n",
- is_read ? "read" : "write", buf_len, credits_needed);
+ str_read_write(is_read), buf_len, credits_needed);
- ret = wait_for_rw_credits(t, credits_needed);
+ ret = wait_for_rw_credits(sc, credits_needed);
if (ret < 0)
return ret;
@@ -1406,7 +1615,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t,
desc_buf = buf;
for (i = 0; i < desc_num; i++) {
msg = kzalloc(struct_size(msg, sg_list, SG_CHUNK_SIZE),
- GFP_KERNEL);
+ KSMBD_DEFAULT_GFP);
if (!msg) {
ret = -ENOMEM;
goto out;
@@ -1414,7 +1623,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t,
desc_buf_len = le32_to_cpu(desc[i].length);
- msg->t = t;
+ msg->socket = sc;
msg->cqe.done = is_read ? read_done : write_done;
msg->completion = &completion;
@@ -1423,20 +1632,16 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t,
get_buf_page_count(desc_buf, desc_buf_len),
msg->sg_list, SG_CHUNK_SIZE);
if (ret) {
- kfree(msg);
ret = -ENOMEM;
- goto out;
+ goto free_msg;
}
ret = get_sg_list(desc_buf, desc_buf_len,
msg->sgt.sgl, msg->sgt.orig_nents);
- if (ret < 0) {
- sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE);
- kfree(msg);
- goto out;
- }
+ if (ret < 0)
+ goto free_table;
- ret = rdma_rw_ctx_init(&msg->rw_ctx, t->qp, t->qp->port,
+ ret = rdma_rw_ctx_init(&msg->rdma_ctx, sc->ib.qp, sc->ib.qp->port,
msg->sgt.sgl,
get_buf_page_count(desc_buf, desc_buf_len),
0,
@@ -1445,9 +1650,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t,
is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
if (ret < 0) {
pr_err("failed to init rdma_rw_ctx: %d\n", ret);
- sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE);
- kfree(msg);
- goto out;
+ goto free_table;
}
list_add_tail(&msg->list, &msg_list);
@@ -1457,96 +1660,111 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t,
/* concatenate work requests of rdma_rw_ctxs */
first_wr = NULL;
list_for_each_entry_reverse(msg, &msg_list, list) {
- first_wr = rdma_rw_ctx_wrs(&msg->rw_ctx, t->qp, t->qp->port,
+ first_wr = rdma_rw_ctx_wrs(&msg->rdma_ctx, sc->ib.qp, sc->ib.qp->port,
&msg->cqe, first_wr);
}
- ret = ib_post_send(t->qp, first_wr, NULL);
+ ret = ib_post_send(sc->ib.qp, first_wr, NULL);
if (ret) {
pr_err("failed to post send wr for RDMA R/W: %d\n", ret);
goto out;
}
- msg = list_last_entry(&msg_list, struct smb_direct_rdma_rw_msg, list);
+ msg = list_last_entry(&msg_list, struct smbdirect_rw_io, list);
wait_for_completion(&completion);
- ret = msg->status;
+ ret = msg->error;
out:
list_for_each_entry_safe(msg, next_msg, &msg_list, list) {
list_del(&msg->list);
smb_direct_free_rdma_rw_msg(t, msg,
is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
- atomic_add(credits_needed, &t->rw_credits);
- wake_up(&t->wait_rw_credits);
+ atomic_add(credits_needed, &sc->rw_io.credits.count);
+ wake_up(&sc->rw_io.credits.wait_queue);
return ret;
+
+free_table:
+ sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE);
+free_msg:
+ kfree(msg);
+ goto out;
}
static int smb_direct_rdma_write(struct ksmbd_transport *t,
void *buf, unsigned int buflen,
- struct smb2_buffer_desc_v1 *desc,
+ struct smbdirect_buffer_descriptor_v1 *desc,
unsigned int desc_len)
{
- return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen,
+ return smb_direct_rdma_xmit(SMBD_TRANS(t), buf, buflen,
desc, desc_len, false);
}
static int smb_direct_rdma_read(struct ksmbd_transport *t,
void *buf, unsigned int buflen,
- struct smb2_buffer_desc_v1 *desc,
+ struct smbdirect_buffer_descriptor_v1 *desc,
unsigned int desc_len)
{
- return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen,
+ return smb_direct_rdma_xmit(SMBD_TRANS(t), buf, buflen,
desc, desc_len, true);
}
static void smb_direct_disconnect(struct ksmbd_transport *t)
{
- struct smb_direct_transport *st = smb_trans_direct_transfort(t);
+ struct smb_direct_transport *st = SMBD_TRANS(t);
+ struct smbdirect_socket *sc = &st->socket;
- ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", st->cm_id);
+ ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", sc->rdma.cm_id);
- smb_direct_disconnect_rdma_work(&st->disconnect_work);
- wait_event_interruptible(st->wait_status,
- st->status == SMB_DIRECT_CS_DISCONNECTED);
free_transport(st);
}
static void smb_direct_shutdown(struct ksmbd_transport *t)
{
- struct smb_direct_transport *st = smb_trans_direct_transfort(t);
+ struct smb_direct_transport *st = SMBD_TRANS(t);
+ struct smbdirect_socket *sc = &st->socket;
- ksmbd_debug(RDMA, "smb-direct shutdown cm_id=%p\n", st->cm_id);
+ ksmbd_debug(RDMA, "smb-direct shutdown cm_id=%p\n", sc->rdma.cm_id);
- smb_direct_disconnect_rdma_work(&st->disconnect_work);
+ smb_direct_disconnect_rdma_work(&sc->disconnect_work);
}
static int smb_direct_cm_handler(struct rdma_cm_id *cm_id,
struct rdma_cm_event *event)
{
- struct smb_direct_transport *t = cm_id->context;
+ struct smbdirect_socket *sc = cm_id->context;
ksmbd_debug(RDMA, "RDMA CM event. cm_id=%p event=%s (%d)\n",
cm_id, rdma_event_msg(event->event), event->event);
switch (event->event) {
case RDMA_CM_EVENT_ESTABLISHED: {
- t->status = SMB_DIRECT_CS_CONNECTED;
- wake_up_interruptible(&t->wait_status);
+ /*
+ * Some drivers (at least mlx5_ib) might post a
+ * recv completion before RDMA_CM_EVENT_ESTABLISHED,
+ * we need to adjust our expectation in that case.
+ *
+ * As we already started the negotiation, we just
+ * ignore RDMA_CM_EVENT_ESTABLISHED here.
+ */
+ if (!sc->first_error && sc->status > SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING)
+ break;
+ if (SMBDIRECT_CHECK_STATUS_DISCONNECT(sc, SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING))
+ break;
+ sc->status = SMBDIRECT_SOCKET_NEGOTIATE_NEEDED;
+ wake_up(&sc->status_wait);
break;
}
case RDMA_CM_EVENT_DEVICE_REMOVAL:
case RDMA_CM_EVENT_DISCONNECTED: {
- ib_drain_qp(t->qp);
-
- t->status = SMB_DIRECT_CS_DISCONNECTED;
- wake_up_interruptible(&t->wait_status);
- wake_up_interruptible(&t->wait_reassembly_queue);
- wake_up(&t->wait_send_credits);
+ sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
+ smb_direct_disconnect_rdma_work(&sc->disconnect_work);
+ if (sc->ib.qp)
+ ib_drain_qp(sc->ib.qp);
break;
}
case RDMA_CM_EVENT_CONNECT_ERROR: {
- t->status = SMB_DIRECT_CS_DISCONNECTED;
- wake_up_interruptible(&t->wait_status);
+ sc->status = SMBDIRECT_SOCKET_DISCONNECTED;
+ smb_direct_disconnect_rdma_work(&sc->disconnect_work);
break;
}
default:
@@ -1560,38 +1778,41 @@ static int smb_direct_cm_handler(struct rdma_cm_id *cm_id,
static void smb_direct_qpair_handler(struct ib_event *event, void *context)
{
- struct smb_direct_transport *t = context;
+ struct smbdirect_socket *sc = context;
ksmbd_debug(RDMA, "Received QP event. cm_id=%p, event=%s (%d)\n",
- t->cm_id, ib_event_msg(event->event), event->event);
+ sc->rdma.cm_id, ib_event_msg(event->event), event->event);
switch (event->event) {
case IB_EVENT_CQ_ERR:
case IB_EVENT_QP_FATAL:
- smb_direct_disconnect_rdma_connection(t);
+ smb_direct_disconnect_rdma_connection(sc);
break;
default:
break;
}
}
-static int smb_direct_send_negotiate_response(struct smb_direct_transport *t,
+static int smb_direct_send_negotiate_response(struct smbdirect_socket *sc,
int failed)
{
- struct smb_direct_sendmsg *sendmsg;
- struct smb_direct_negotiate_resp *resp;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smbdirect_send_io *sendmsg;
+ struct smbdirect_negotiate_resp *resp;
int ret;
- sendmsg = smb_direct_alloc_sendmsg(t);
+ sendmsg = smb_direct_alloc_sendmsg(sc);
if (IS_ERR(sendmsg))
return -ENOMEM;
- resp = (struct smb_direct_negotiate_resp *)sendmsg->packet;
+ resp = (struct smbdirect_negotiate_resp *)sendmsg->packet;
if (failed) {
memset(resp, 0, sizeof(*resp));
- resp->min_version = cpu_to_le16(0x0100);
- resp->max_version = cpu_to_le16(0x0100);
+ resp->min_version = SMB_DIRECT_VERSION_LE;
+ resp->max_version = SMB_DIRECT_VERSION_LE;
resp->status = STATUS_NOT_SUPPORTED;
+
+ sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
} else {
resp->status = STATUS_SUCCESS;
resp->min_version = SMB_DIRECT_VERSION_LE;
@@ -1599,57 +1820,65 @@ static int smb_direct_send_negotiate_response(struct smb_direct_transport *t,
resp->negotiated_version = SMB_DIRECT_VERSION_LE;
resp->reserved = 0;
resp->credits_requested =
- cpu_to_le16(t->send_credit_target);
- resp->credits_granted = cpu_to_le16(manage_credits_prior_sending(t));
- resp->max_readwrite_size = cpu_to_le32(t->max_rdma_rw_size);
- resp->preferred_send_size = cpu_to_le32(t->max_send_size);
- resp->max_receive_size = cpu_to_le32(t->max_recv_size);
+ cpu_to_le16(sp->send_credit_target);
+ resp->credits_granted = cpu_to_le16(manage_credits_prior_sending(sc));
+ resp->max_readwrite_size = cpu_to_le32(sp->max_read_write_size);
+ resp->preferred_send_size = cpu_to_le32(sp->max_send_size);
+ resp->max_receive_size = cpu_to_le32(sp->max_recv_size);
resp->max_fragmented_size =
- cpu_to_le32(t->max_fragmented_recv_size);
+ cpu_to_le32(sp->max_fragmented_recv_size);
+
+ sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER;
+ sc->status = SMBDIRECT_SOCKET_CONNECTED;
}
- sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device,
+ sendmsg->sge[0].addr = ib_dma_map_single(sc->ib.dev,
(void *)resp, sizeof(*resp),
DMA_TO_DEVICE);
- ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr);
+ ret = ib_dma_mapping_error(sc->ib.dev, sendmsg->sge[0].addr);
if (ret) {
- smb_direct_free_sendmsg(t, sendmsg);
+ smb_direct_free_sendmsg(sc, sendmsg);
return ret;
}
sendmsg->num_sge = 1;
sendmsg->sge[0].length = sizeof(*resp);
- sendmsg->sge[0].lkey = t->pd->local_dma_lkey;
+ sendmsg->sge[0].lkey = sc->ib.pd->local_dma_lkey;
- ret = post_sendmsg(t, NULL, sendmsg);
+ ret = post_sendmsg(sc, NULL, sendmsg);
if (ret) {
- smb_direct_free_sendmsg(t, sendmsg);
+ smb_direct_free_sendmsg(sc, sendmsg);
return ret;
}
- wait_event(t->wait_send_pending,
- atomic_read(&t->send_pending) == 0);
+ wait_event(sc->send_io.pending.zero_wait_queue,
+ atomic_read(&sc->send_io.pending.count) == 0 ||
+ sc->status != SMBDIRECT_SOCKET_CONNECTED);
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
+ return -ENOTCONN;
+
return 0;
}
-static int smb_direct_accept_client(struct smb_direct_transport *t)
+static int smb_direct_accept_client(struct smbdirect_socket *sc)
{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
struct rdma_conn_param conn_param;
- struct ib_port_immutable port_immutable;
- u32 ird_ord_hdr[2];
+ __be32 ird_ord_hdr[2];
int ret;
+ /*
+ * smb_direct_handle_connect_request()
+ * already negotiated sp->initiator_depth
+ * and sp->responder_resources
+ */
memset(&conn_param, 0, sizeof(conn_param));
- conn_param.initiator_depth = min_t(u8, t->cm_id->device->attrs.max_qp_rd_atom,
- SMB_DIRECT_CM_INITIATOR_DEPTH);
- conn_param.responder_resources = 0;
-
- t->cm_id->device->ops.get_port_immutable(t->cm_id->device,
- t->cm_id->port_num,
- &port_immutable);
- if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) {
- ird_ord_hdr[0] = conn_param.responder_resources;
- ird_ord_hdr[1] = 1;
+ conn_param.initiator_depth = sp->initiator_depth;
+ conn_param.responder_resources = sp->responder_resources;
+
+ if (sc->rdma.legacy_iwarp) {
+ ird_ord_hdr[0] = cpu_to_be32(conn_param.responder_resources);
+ ird_ord_hdr[1] = cpu_to_be32(conn_param.initiator_depth);
conn_param.private_data = ird_ord_hdr;
conn_param.private_data_len = sizeof(ird_ord_hdr);
} else {
@@ -1660,7 +1889,17 @@ static int smb_direct_accept_client(struct smb_direct_transport *t)
conn_param.rnr_retry_count = SMB_DIRECT_CM_RNR_RETRY;
conn_param.flow_control = 0;
- ret = rdma_accept(t->cm_id, &conn_param);
+ /*
+ * start with the negotiate timeout and SMBDIRECT_KEEPALIVE_PENDING
+ * so that the timer will cause a disconnect.
+ */
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_PENDING;
+ mod_delayed_work(sc->workqueue, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->negotiate_timeout_msec));
+
+ WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED);
+ sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING;
+ ret = rdma_accept(sc->rdma.cm_id, &conn_param);
if (ret) {
pr_err("error at rdma_accept: %d\n", ret);
return ret;
@@ -1668,358 +1907,459 @@ static int smb_direct_accept_client(struct smb_direct_transport *t)
return 0;
}
-static int smb_direct_prepare_negotiation(struct smb_direct_transport *t)
+static int smb_direct_prepare_negotiation(struct smbdirect_socket *sc)
{
+ struct smbdirect_recv_io *recvmsg;
+ bool recv_posted = false;
int ret;
- struct smb_direct_recvmsg *recvmsg;
- recvmsg = get_free_recvmsg(t);
+ WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_CREATED);
+ sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED;
+
+ sc->recv_io.expected = SMBDIRECT_EXPECT_NEGOTIATE_REQ;
+
+ recvmsg = get_free_recvmsg(sc);
if (!recvmsg)
return -ENOMEM;
- recvmsg->type = SMB_DIRECT_MSG_NEGOTIATE_REQ;
- ret = smb_direct_post_recv(t, recvmsg);
+ ret = smb_direct_post_recv(sc, recvmsg);
if (ret) {
pr_err("Can't post recv: %d\n", ret);
goto out_err;
}
+ recv_posted = true;
- t->negotiation_requested = false;
- ret = smb_direct_accept_client(t);
+ ret = smb_direct_accept_client(sc);
if (ret) {
pr_err("Can't accept client\n");
goto out_err;
}
- smb_direct_post_recv_credits(&t->post_recv_credits_work.work);
return 0;
out_err:
- put_recvmsg(t, recvmsg);
+ /*
+ * If the recv was never posted, return it to the free list.
+ * If it was posted, leave it alone so disconnect teardown can
+ * drain the QP and complete it (flush) and the completion path
+ * will unmap it exactly once.
+ */
+ if (!recv_posted)
+ put_recvmsg(sc, recvmsg);
return ret;
}
-static unsigned int smb_direct_get_max_fr_pages(struct smb_direct_transport *t)
+static int smb_direct_init_params(struct smbdirect_socket *sc)
{
- return min_t(unsigned int,
- t->cm_id->device->attrs.max_fast_reg_page_list_len,
- 256);
-}
-
-static int smb_direct_init_params(struct smb_direct_transport *t,
- struct ib_qp_cap *cap)
-{
- struct ib_device *device = t->cm_id->device;
- int max_send_sges, max_rw_wrs, max_send_wrs;
- unsigned int max_sge_per_wr, wrs_per_credit;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ int max_send_sges;
+ unsigned int maxpages;
/* need 3 more sge. because a SMB_DIRECT header, SMB2 header,
* SMB2 response could be mapped.
*/
- t->max_send_size = smb_direct_max_send_size;
- max_send_sges = DIV_ROUND_UP(t->max_send_size, PAGE_SIZE) + 3;
- if (max_send_sges > SMB_DIRECT_MAX_SEND_SGES) {
- pr_err("max_send_size %d is too large\n", t->max_send_size);
+ max_send_sges = DIV_ROUND_UP(sp->max_send_size, PAGE_SIZE) + 3;
+ if (max_send_sges > SMBDIRECT_SEND_IO_MAX_SGE) {
+ pr_err("max_send_size %d is too large\n", sp->max_send_size);
return -EINVAL;
}
- /* Calculate the number of work requests for RDMA R/W.
- * The maximum number of pages which can be registered
- * with one Memory region can be transferred with one
- * R/W credit. And at least 4 work requests for each credit
- * are needed for MR registration, RDMA R/W, local & remote
- * MR invalidation.
- */
- t->max_rdma_rw_size = smb_direct_max_read_write_size;
- t->pages_per_rw_credit = smb_direct_get_max_fr_pages(t);
- t->max_rw_credits = DIV_ROUND_UP(t->max_rdma_rw_size,
- (t->pages_per_rw_credit - 1) *
- PAGE_SIZE);
-
- max_sge_per_wr = min_t(unsigned int, device->attrs.max_send_sge,
- device->attrs.max_sge_rd);
- max_sge_per_wr = max_t(unsigned int, max_sge_per_wr,
- max_send_sges);
- wrs_per_credit = max_t(unsigned int, 4,
- DIV_ROUND_UP(t->pages_per_rw_credit,
- max_sge_per_wr) + 1);
- max_rw_wrs = t->max_rw_credits * wrs_per_credit;
-
- max_send_wrs = smb_direct_send_credit_target + max_rw_wrs;
- if (max_send_wrs > device->attrs.max_cqe ||
- max_send_wrs > device->attrs.max_qp_wr) {
- pr_err("consider lowering send_credit_target = %d\n",
- smb_direct_send_credit_target);
- pr_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n",
- device->attrs.max_cqe, device->attrs.max_qp_wr);
- return -EINVAL;
- }
+ atomic_set(&sc->send_io.lcredits.count, sp->send_credit_target);
- if (smb_direct_receive_credit_max > device->attrs.max_cqe ||
- smb_direct_receive_credit_max > device->attrs.max_qp_wr) {
- pr_err("consider lowering receive_credit_max = %d\n",
- smb_direct_receive_credit_max);
- pr_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n",
- device->attrs.max_cqe, device->attrs.max_qp_wr);
- return -EINVAL;
- }
+ maxpages = DIV_ROUND_UP(sp->max_read_write_size, PAGE_SIZE);
+ sc->rw_io.credits.max = rdma_rw_mr_factor(sc->ib.dev,
+ sc->rdma.cm_id->port_num,
+ maxpages);
+ sc->rw_io.credits.num_pages = DIV_ROUND_UP(maxpages, sc->rw_io.credits.max);
+ /* add one extra in order to handle unaligned pages */
+ sc->rw_io.credits.max += 1;
- if (device->attrs.max_recv_sge < SMB_DIRECT_MAX_RECV_SGES) {
- pr_err("warning: device max_recv_sge = %d too small\n",
- device->attrs.max_recv_sge);
- return -EINVAL;
- }
+ sc->recv_io.credits.target = 1;
- t->recv_credits = 0;
- t->count_avail_recvmsg = 0;
+ atomic_set(&sc->rw_io.credits.count, sc->rw_io.credits.max);
- t->recv_credit_max = smb_direct_receive_credit_max;
- t->recv_credit_target = 10;
- t->new_recv_credits = 0;
-
- t->send_credit_target = smb_direct_send_credit_target;
- atomic_set(&t->send_credits, 0);
- atomic_set(&t->rw_credits, t->max_rw_credits);
-
- t->max_send_size = smb_direct_max_send_size;
- t->max_recv_size = smb_direct_max_receive_size;
- t->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size;
-
- cap->max_send_wr = max_send_wrs;
- cap->max_recv_wr = t->recv_credit_max;
- cap->max_send_sge = max_sge_per_wr;
- cap->max_recv_sge = SMB_DIRECT_MAX_RECV_SGES;
- cap->max_inline_data = 0;
- cap->max_rdma_ctxs = t->max_rw_credits;
return 0;
}
-static void smb_direct_destroy_pools(struct smb_direct_transport *t)
+static void smb_direct_destroy_pools(struct smbdirect_socket *sc)
{
- struct smb_direct_recvmsg *recvmsg;
+ struct smbdirect_recv_io *recvmsg;
- while ((recvmsg = get_free_recvmsg(t)))
- mempool_free(recvmsg, t->recvmsg_mempool);
- while ((recvmsg = get_empty_recvmsg(t)))
- mempool_free(recvmsg, t->recvmsg_mempool);
+ while ((recvmsg = get_free_recvmsg(sc)))
+ mempool_free(recvmsg, sc->recv_io.mem.pool);
- mempool_destroy(t->recvmsg_mempool);
- t->recvmsg_mempool = NULL;
+ mempool_destroy(sc->recv_io.mem.pool);
+ sc->recv_io.mem.pool = NULL;
- kmem_cache_destroy(t->recvmsg_cache);
- t->recvmsg_cache = NULL;
+ kmem_cache_destroy(sc->recv_io.mem.cache);
+ sc->recv_io.mem.cache = NULL;
- mempool_destroy(t->sendmsg_mempool);
- t->sendmsg_mempool = NULL;
+ mempool_destroy(sc->send_io.mem.pool);
+ sc->send_io.mem.pool = NULL;
- kmem_cache_destroy(t->sendmsg_cache);
- t->sendmsg_cache = NULL;
+ kmem_cache_destroy(sc->send_io.mem.cache);
+ sc->send_io.mem.cache = NULL;
}
-static int smb_direct_create_pools(struct smb_direct_transport *t)
+static int smb_direct_create_pools(struct smbdirect_socket *sc)
{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
char name[80];
int i;
- struct smb_direct_recvmsg *recvmsg;
+ struct smbdirect_recv_io *recvmsg;
- snprintf(name, sizeof(name), "smb_direct_rqst_pool_%p", t);
- t->sendmsg_cache = kmem_cache_create(name,
- sizeof(struct smb_direct_sendmsg) +
- sizeof(struct smb_direct_negotiate_resp),
+ snprintf(name, sizeof(name), "smbdirect_send_io_pool_%p", sc);
+ sc->send_io.mem.cache = kmem_cache_create(name,
+ sizeof(struct smbdirect_send_io) +
+ sizeof(struct smbdirect_negotiate_resp),
0, SLAB_HWCACHE_ALIGN, NULL);
- if (!t->sendmsg_cache)
+ if (!sc->send_io.mem.cache)
return -ENOMEM;
- t->sendmsg_mempool = mempool_create(t->send_credit_target,
+ sc->send_io.mem.pool = mempool_create(sp->send_credit_target,
mempool_alloc_slab, mempool_free_slab,
- t->sendmsg_cache);
- if (!t->sendmsg_mempool)
+ sc->send_io.mem.cache);
+ if (!sc->send_io.mem.pool)
goto err;
- snprintf(name, sizeof(name), "smb_direct_resp_%p", t);
- t->recvmsg_cache = kmem_cache_create(name,
- sizeof(struct smb_direct_recvmsg) +
- t->max_recv_size,
+ snprintf(name, sizeof(name), "smbdirect_recv_io_pool_%p", sc);
+ sc->recv_io.mem.cache = kmem_cache_create(name,
+ sizeof(struct smbdirect_recv_io) +
+ sp->max_recv_size,
0, SLAB_HWCACHE_ALIGN, NULL);
- if (!t->recvmsg_cache)
+ if (!sc->recv_io.mem.cache)
goto err;
- t->recvmsg_mempool =
- mempool_create(t->recv_credit_max, mempool_alloc_slab,
- mempool_free_slab, t->recvmsg_cache);
- if (!t->recvmsg_mempool)
+ sc->recv_io.mem.pool =
+ mempool_create(sp->recv_credit_max, mempool_alloc_slab,
+ mempool_free_slab, sc->recv_io.mem.cache);
+ if (!sc->recv_io.mem.pool)
goto err;
- INIT_LIST_HEAD(&t->recvmsg_queue);
-
- for (i = 0; i < t->recv_credit_max; i++) {
- recvmsg = mempool_alloc(t->recvmsg_mempool, GFP_KERNEL);
+ for (i = 0; i < sp->recv_credit_max; i++) {
+ recvmsg = mempool_alloc(sc->recv_io.mem.pool, KSMBD_DEFAULT_GFP);
if (!recvmsg)
goto err;
- recvmsg->transport = t;
- list_add(&recvmsg->list, &t->recvmsg_queue);
+ recvmsg->socket = sc;
+ recvmsg->sge.length = 0;
+ list_add(&recvmsg->list, &sc->recv_io.free.list);
}
- t->count_avail_recvmsg = t->recv_credit_max;
return 0;
err:
- smb_direct_destroy_pools(t);
+ smb_direct_destroy_pools(sc);
return -ENOMEM;
}
-static int smb_direct_create_qpair(struct smb_direct_transport *t,
- struct ib_qp_cap *cap)
+static u32 smb_direct_rdma_rw_send_wrs(struct ib_device *dev, const struct ib_qp_init_attr *attr)
+{
+ /*
+ * This could be split out of rdma_rw_init_qp()
+ * and be a helper function next to rdma_rw_mr_factor()
+ *
+ * We can't check unlikely(rdma_rw_force_mr) here,
+ * but that is most likely 0 anyway.
+ */
+ u32 factor;
+
+ WARN_ON_ONCE(attr->port_num == 0);
+
+ /*
+ * Each context needs at least one RDMA READ or WRITE WR.
+ *
+ * For some hardware we might need more, eventually we should ask the
+ * HCA driver for a multiplier here.
+ */
+ factor = 1;
+
+ /*
+ * If the device needs MRs to perform RDMA READ or WRITE operations,
+ * we'll need two additional MRs for the registrations and the
+ * invalidation.
+ */
+ if (rdma_protocol_iwarp(dev, attr->port_num) || dev->attrs.max_sgl_rd)
+ factor += 2; /* inv + reg */
+
+ return factor * attr->cap.max_rdma_ctxs;
+}
+
+static int smb_direct_create_qpair(struct smbdirect_socket *sc)
{
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
int ret;
+ struct ib_qp_cap qp_cap;
struct ib_qp_init_attr qp_attr;
- int pages_per_rw;
+ u32 max_send_wr;
+ u32 rdma_send_wr;
+
+ /*
+ * Note that {rdma,ib}_create_qp() will call
+ * rdma_rw_init_qp() if cap->max_rdma_ctxs is not 0.
+ * It will adjust cap->max_send_wr to the required
+ * number of additional WRs for the RDMA RW operations.
+ * It will cap cap->max_send_wr to the device limit.
+ *
+ * +1 for ib_drain_qp
+ */
+ qp_cap.max_send_wr = sp->send_credit_target + 1;
+ qp_cap.max_recv_wr = sp->recv_credit_max + 1;
+ qp_cap.max_send_sge = SMBDIRECT_SEND_IO_MAX_SGE;
+ qp_cap.max_recv_sge = SMBDIRECT_RECV_IO_MAX_SGE;
+ qp_cap.max_inline_data = 0;
+ qp_cap.max_rdma_ctxs = sc->rw_io.credits.max;
+
+ /*
+ * Find out the number of max_send_wr
+ * after rdma_rw_init_qp() adjusted it.
+ *
+ * We only do it on a temporary variable,
+ * as rdma_create_qp() will trigger
+ * rdma_rw_init_qp() again.
+ */
+ memset(&qp_attr, 0, sizeof(qp_attr));
+ qp_attr.cap = qp_cap;
+ qp_attr.port_num = sc->rdma.cm_id->port_num;
+ rdma_send_wr = smb_direct_rdma_rw_send_wrs(sc->ib.dev, &qp_attr);
+ max_send_wr = qp_cap.max_send_wr + rdma_send_wr;
+
+ if (qp_cap.max_send_wr > sc->ib.dev->attrs.max_cqe ||
+ qp_cap.max_send_wr > sc->ib.dev->attrs.max_qp_wr) {
+ pr_err("Possible CQE overrun: max_send_wr %d\n",
+ qp_cap.max_send_wr);
+ pr_err("device %.*s reporting max_cqe %d max_qp_wr %d\n",
+ IB_DEVICE_NAME_MAX,
+ sc->ib.dev->name,
+ sc->ib.dev->attrs.max_cqe,
+ sc->ib.dev->attrs.max_qp_wr);
+ pr_err("consider lowering send_credit_target = %d\n",
+ sp->send_credit_target);
+ return -EINVAL;
+ }
- t->pd = ib_alloc_pd(t->cm_id->device, 0);
- if (IS_ERR(t->pd)) {
+ if (qp_cap.max_rdma_ctxs &&
+ (max_send_wr >= sc->ib.dev->attrs.max_cqe ||
+ max_send_wr >= sc->ib.dev->attrs.max_qp_wr)) {
+ pr_err("Possible CQE overrun: rdma_send_wr %d + max_send_wr %d = %d\n",
+ rdma_send_wr, qp_cap.max_send_wr, max_send_wr);
+ pr_err("device %.*s reporting max_cqe %d max_qp_wr %d\n",
+ IB_DEVICE_NAME_MAX,
+ sc->ib.dev->name,
+ sc->ib.dev->attrs.max_cqe,
+ sc->ib.dev->attrs.max_qp_wr);
+ pr_err("consider lowering send_credit_target = %d, max_rdma_ctxs = %d\n",
+ sp->send_credit_target, qp_cap.max_rdma_ctxs);
+ return -EINVAL;
+ }
+
+ if (qp_cap.max_recv_wr > sc->ib.dev->attrs.max_cqe ||
+ qp_cap.max_recv_wr > sc->ib.dev->attrs.max_qp_wr) {
+ pr_err("Possible CQE overrun: max_recv_wr %d\n",
+ qp_cap.max_recv_wr);
+ pr_err("device %.*s reporting max_cqe %d max_qp_wr %d\n",
+ IB_DEVICE_NAME_MAX,
+ sc->ib.dev->name,
+ sc->ib.dev->attrs.max_cqe,
+ sc->ib.dev->attrs.max_qp_wr);
+ pr_err("consider lowering receive_credit_max = %d\n",
+ sp->recv_credit_max);
+ return -EINVAL;
+ }
+
+ if (qp_cap.max_send_sge > sc->ib.dev->attrs.max_send_sge ||
+ qp_cap.max_recv_sge > sc->ib.dev->attrs.max_recv_sge) {
+ pr_err("device %.*s max_send_sge/max_recv_sge = %d/%d too small\n",
+ IB_DEVICE_NAME_MAX,
+ sc->ib.dev->name,
+ sc->ib.dev->attrs.max_send_sge,
+ sc->ib.dev->attrs.max_recv_sge);
+ return -EINVAL;
+ }
+
+ sc->ib.pd = ib_alloc_pd(sc->ib.dev, 0);
+ if (IS_ERR(sc->ib.pd)) {
pr_err("Can't create RDMA PD\n");
- ret = PTR_ERR(t->pd);
- t->pd = NULL;
+ ret = PTR_ERR(sc->ib.pd);
+ sc->ib.pd = NULL;
return ret;
}
- t->send_cq = ib_alloc_cq(t->cm_id->device, t,
- smb_direct_send_credit_target + cap->max_rdma_ctxs,
- 0, IB_POLL_WORKQUEUE);
- if (IS_ERR(t->send_cq)) {
+ sc->ib.send_cq = ib_alloc_cq_any(sc->ib.dev, sc,
+ max_send_wr,
+ IB_POLL_WORKQUEUE);
+ if (IS_ERR(sc->ib.send_cq)) {
pr_err("Can't create RDMA send CQ\n");
- ret = PTR_ERR(t->send_cq);
- t->send_cq = NULL;
+ ret = PTR_ERR(sc->ib.send_cq);
+ sc->ib.send_cq = NULL;
goto err;
}
- t->recv_cq = ib_alloc_cq(t->cm_id->device, t,
- t->recv_credit_max, 0, IB_POLL_WORKQUEUE);
- if (IS_ERR(t->recv_cq)) {
+ sc->ib.recv_cq = ib_alloc_cq_any(sc->ib.dev, sc,
+ qp_cap.max_recv_wr,
+ IB_POLL_WORKQUEUE);
+ if (IS_ERR(sc->ib.recv_cq)) {
pr_err("Can't create RDMA recv CQ\n");
- ret = PTR_ERR(t->recv_cq);
- t->recv_cq = NULL;
+ ret = PTR_ERR(sc->ib.recv_cq);
+ sc->ib.recv_cq = NULL;
goto err;
}
+ /*
+ * We reset completely here!
+ * As the above use was just temporary
+ * to calc max_send_wr and rdma_send_wr.
+ *
+ * rdma_create_qp() will trigger rdma_rw_init_qp()
+ * again if max_rdma_ctxs is not 0.
+ */
memset(&qp_attr, 0, sizeof(qp_attr));
qp_attr.event_handler = smb_direct_qpair_handler;
- qp_attr.qp_context = t;
- qp_attr.cap = *cap;
+ qp_attr.qp_context = sc;
+ qp_attr.cap = qp_cap;
qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
qp_attr.qp_type = IB_QPT_RC;
- qp_attr.send_cq = t->send_cq;
- qp_attr.recv_cq = t->recv_cq;
+ qp_attr.send_cq = sc->ib.send_cq;
+ qp_attr.recv_cq = sc->ib.recv_cq;
qp_attr.port_num = ~0;
- ret = rdma_create_qp(t->cm_id, t->pd, &qp_attr);
+ ret = rdma_create_qp(sc->rdma.cm_id, sc->ib.pd, &qp_attr);
if (ret) {
pr_err("Can't create RDMA QP: %d\n", ret);
goto err;
}
- t->qp = t->cm_id->qp;
- t->cm_id->event_handler = smb_direct_cm_handler;
-
- pages_per_rw = DIV_ROUND_UP(t->max_rdma_rw_size, PAGE_SIZE) + 1;
- if (pages_per_rw > t->cm_id->device->attrs.max_sgl_rd) {
- ret = ib_mr_pool_init(t->qp, &t->qp->rdma_mrs,
- t->max_rw_credits, IB_MR_TYPE_MEM_REG,
- t->pages_per_rw_credit, 0);
- if (ret) {
- pr_err("failed to init mr pool count %d pages %d\n",
- t->max_rw_credits, t->pages_per_rw_credit);
- goto err;
- }
- }
+ sc->ib.qp = sc->rdma.cm_id->qp;
+ sc->rdma.cm_id->event_handler = smb_direct_cm_handler;
return 0;
err:
- if (t->qp) {
- ib_destroy_qp(t->qp);
- t->qp = NULL;
+ if (sc->ib.qp) {
+ sc->ib.qp = NULL;
+ rdma_destroy_qp(sc->rdma.cm_id);
}
- if (t->recv_cq) {
- ib_destroy_cq(t->recv_cq);
- t->recv_cq = NULL;
+ if (sc->ib.recv_cq) {
+ ib_destroy_cq(sc->ib.recv_cq);
+ sc->ib.recv_cq = NULL;
}
- if (t->send_cq) {
- ib_destroy_cq(t->send_cq);
- t->send_cq = NULL;
+ if (sc->ib.send_cq) {
+ ib_destroy_cq(sc->ib.send_cq);
+ sc->ib.send_cq = NULL;
}
- if (t->pd) {
- ib_dealloc_pd(t->pd);
- t->pd = NULL;
+ if (sc->ib.pd) {
+ ib_dealloc_pd(sc->ib.pd);
+ sc->ib.pd = NULL;
}
return ret;
}
static int smb_direct_prepare(struct ksmbd_transport *t)
{
- struct smb_direct_transport *st = smb_trans_direct_transfort(t);
- struct smb_direct_recvmsg *recvmsg;
- struct smb_direct_negotiate_req *req;
+ struct smb_direct_transport *st = SMBD_TRANS(t);
+ struct smbdirect_socket *sc = &st->socket;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smbdirect_recv_io *recvmsg;
+ struct smbdirect_negotiate_req *req;
+ unsigned long flags;
int ret;
+ /*
+ * We are waiting to pass the following states:
+ *
+ * SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED
+ * SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING
+ * SMBDIRECT_SOCKET_NEGOTIATE_NEEDED
+ *
+ * To finally get to SMBDIRECT_SOCKET_NEGOTIATE_RUNNING
+ * in order to continue below.
+ *
+ * Everything else is unexpected and an error.
+ */
ksmbd_debug(RDMA, "Waiting for SMB_DIRECT negotiate request\n");
- ret = wait_event_interruptible_timeout(st->wait_status,
- st->negotiation_requested ||
- st->status == SMB_DIRECT_CS_DISCONNECTED,
- SMB_DIRECT_NEGOTIATE_TIMEOUT * HZ);
- if (ret <= 0 || st->status == SMB_DIRECT_CS_DISCONNECTED)
+ ret = wait_event_interruptible_timeout(sc->status_wait,
+ sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED &&
+ sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING &&
+ sc->status != SMBDIRECT_SOCKET_NEGOTIATE_NEEDED,
+ msecs_to_jiffies(sp->negotiate_timeout_msec));
+ if (ret <= 0 || sc->status != SMBDIRECT_SOCKET_NEGOTIATE_RUNNING)
return ret < 0 ? ret : -ETIMEDOUT;
- recvmsg = get_first_reassembly(st);
+ recvmsg = get_first_reassembly(sc);
if (!recvmsg)
return -ECONNABORTED;
ret = smb_direct_check_recvmsg(recvmsg);
- if (ret == -ECONNABORTED)
- goto out;
+ if (ret)
+ goto put;
- req = (struct smb_direct_negotiate_req *)recvmsg->packet;
- st->max_recv_size = min_t(int, st->max_recv_size,
+ req = (struct smbdirect_negotiate_req *)recvmsg->packet;
+ sp->max_recv_size = min_t(int, sp->max_recv_size,
le32_to_cpu(req->preferred_send_size));
- st->max_send_size = min_t(int, st->max_send_size,
+ sp->max_send_size = min_t(int, sp->max_send_size,
le32_to_cpu(req->max_receive_size));
- st->max_fragmented_send_size =
+ sp->max_fragmented_send_size =
le32_to_cpu(req->max_fragmented_size);
- st->max_fragmented_recv_size =
- (st->recv_credit_max * st->max_recv_size) / 2;
-
- ret = smb_direct_send_negotiate_response(st, ret);
-out:
- spin_lock_irq(&st->reassembly_queue_lock);
- st->reassembly_queue_length--;
+ sp->max_fragmented_recv_size =
+ (sp->recv_credit_max * sp->max_recv_size) / 2;
+ sc->recv_io.credits.target = le16_to_cpu(req->credits_requested);
+ sc->recv_io.credits.target = min_t(u16, sc->recv_io.credits.target, sp->recv_credit_max);
+ sc->recv_io.credits.target = max_t(u16, sc->recv_io.credits.target, 1);
+
+put:
+ spin_lock_irqsave(&sc->recv_io.reassembly.lock, flags);
+ sc->recv_io.reassembly.queue_length--;
list_del(&recvmsg->list);
- spin_unlock_irq(&st->reassembly_queue_lock);
- put_recvmsg(st, recvmsg);
+ spin_unlock_irqrestore(&sc->recv_io.reassembly.lock, flags);
+ put_recvmsg(sc, recvmsg);
+
+ if (ret == -ECONNABORTED)
+ return ret;
+
+ if (ret)
+ goto respond;
+
+ /*
+ * We negotiated with success, so we need to refill the recv queue.
+ * We do that with sc->idle.immediate_work still being disabled
+ * via smbdirect_socket_init(), so that queue_work(sc->workqueue,
+ * &sc->idle.immediate_work) in smb_direct_post_recv_credits()
+ * is a no-op.
+ *
+ * The message that grants the credits to the client is
+ * the negotiate response.
+ */
+ INIT_WORK(&sc->recv_io.posted.refill_work, smb_direct_post_recv_credits);
+ smb_direct_post_recv_credits(&sc->recv_io.posted.refill_work);
+ if (unlikely(sc->first_error))
+ return sc->first_error;
+ INIT_WORK(&sc->idle.immediate_work, smb_direct_send_immediate_work);
+
+respond:
+ ret = smb_direct_send_negotiate_response(sc, ret);
return ret;
}
-static int smb_direct_connect(struct smb_direct_transport *st)
+static int smb_direct_connect(struct smbdirect_socket *sc)
{
int ret;
- struct ib_qp_cap qp_cap;
- ret = smb_direct_init_params(st, &qp_cap);
+ ret = smb_direct_init_params(sc);
if (ret) {
pr_err("Can't configure RDMA parameters\n");
return ret;
}
- ret = smb_direct_create_pools(st);
+ ret = smb_direct_create_pools(sc);
if (ret) {
pr_err("Can't init RDMA pool: %d\n", ret);
return ret;
}
- ret = smb_direct_create_qpair(st, &qp_cap);
+ ret = smb_direct_create_qpair(sc);
if (ret) {
pr_err("Can't accept RDMA client: %d\n", ret);
return ret;
}
- ret = smb_direct_prepare_negotiation(st);
+ ret = smb_direct_prepare_negotiation(sc);
if (ret) {
pr_err("Can't negotiate: %d\n", ret);
return ret;
@@ -2036,10 +2376,15 @@ static bool rdma_frwr_is_supported(struct ib_device_attr *attrs)
return true;
}
-static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id)
+static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id,
+ struct rdma_cm_event *event)
{
struct smb_direct_transport *t;
+ struct smbdirect_socket *sc;
+ struct smbdirect_socket_parameters *sp;
struct task_struct *handler;
+ u8 peer_initiator_depth;
+ u8 peer_responder_resources;
int ret;
if (!rdma_frwr_is_supported(&new_cm_id->device->attrs)) {
@@ -2052,8 +2397,71 @@ static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id)
t = alloc_transport(new_cm_id);
if (!t)
return -ENOMEM;
+ sc = &t->socket;
+ sp = &sc->parameters;
+
+ peer_initiator_depth = event->param.conn.initiator_depth;
+ peer_responder_resources = event->param.conn.responder_resources;
+ if (rdma_protocol_iwarp(new_cm_id->device, new_cm_id->port_num) &&
+ event->param.conn.private_data_len == 8) {
+ /*
+ * Legacy clients with only iWarp MPA v1 support
+ * need a private blob in order to negotiate
+ * the IRD/ORD values.
+ */
+ const __be32 *ird_ord_hdr = event->param.conn.private_data;
+ u32 ird32 = be32_to_cpu(ird_ord_hdr[0]);
+ u32 ord32 = be32_to_cpu(ird_ord_hdr[1]);
+
+ /*
+ * cifs.ko sends the legacy IRD/ORD negotiation
+ * event if iWarp MPA v2 was used.
+ *
+ * Here we check that the values match and only
+ * mark the client as legacy if they don't match.
+ */
+ if ((u32)event->param.conn.initiator_depth != ird32 ||
+ (u32)event->param.conn.responder_resources != ord32) {
+ /*
+ * There are broken clients (old cifs.ko)
+ * using little endian and also
+ * struct rdma_conn_param only uses u8
+ * for initiator_depth and responder_resources,
+ * so we truncate the value to U8_MAX.
+ *
+ * smb_direct_accept_client() will then
+ * do the real negotiation in order to
+ * select the minimum between client and
+ * server.
+ */
+ ird32 = min_t(u32, ird32, U8_MAX);
+ ord32 = min_t(u32, ord32, U8_MAX);
+
+ sc->rdma.legacy_iwarp = true;
+ peer_initiator_depth = (u8)ird32;
+ peer_responder_resources = (u8)ord32;
+ }
+ }
+
+ /*
+ * First set what the we as server are able to support
+ */
+ sp->initiator_depth = min_t(u8, sp->initiator_depth,
+ new_cm_id->device->attrs.max_qp_rd_atom);
- ret = smb_direct_connect(t);
+ /*
+ * negotiate the value by using the minimum
+ * between client and server if the client provided
+ * non 0 values.
+ */
+ if (peer_initiator_depth != 0)
+ sp->initiator_depth = min_t(u8, sp->initiator_depth,
+ peer_initiator_depth);
+ if (peer_responder_resources != 0)
+ sp->responder_resources = min_t(u8, sp->responder_resources,
+ peer_responder_resources);
+
+ ret = smb_direct_connect(sc);
if (ret)
goto out_err;
@@ -2077,7 +2485,7 @@ static int smb_direct_listen_handler(struct rdma_cm_id *cm_id,
{
switch (event->event) {
case RDMA_CM_EVENT_CONNECT_REQUEST: {
- int ret = smb_direct_handle_connect_request(cm_id);
+ int ret = smb_direct_handle_connect_request(cm_id, event);
if (ret) {
pr_err("Can't create transport: %d\n", ret);
@@ -2144,7 +2552,7 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev)
if (!rdma_frwr_is_supported(&ib_dev->attrs))
return 0;
- smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL);
+ smb_dev = kzalloc(sizeof(*smb_dev), KSMBD_DEFAULT_GFP);
if (!smb_dev)
return -ENOMEM;
smb_dev->ib_dev = ib_dev;
@@ -2197,7 +2605,8 @@ int ksmbd_rdma_init(void)
* for lack of credits
*/
smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq",
- WQ_HIGHPRI | WQ_MEM_RECLAIM, 0);
+ WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_PERCPU,
+ 0);
if (!smb_direct_wq)
return -ENOMEM;
@@ -2214,7 +2623,7 @@ int ksmbd_rdma_init(void)
return 0;
}
-void ksmbd_rdma_destroy(void)
+void ksmbd_rdma_stop_listening(void)
{
if (!smb_direct_listener.cm_id)
return;
@@ -2223,14 +2632,17 @@ void ksmbd_rdma_destroy(void)
rdma_destroy_id(smb_direct_listener.cm_id);
smb_direct_listener.cm_id = NULL;
+}
+void ksmbd_rdma_destroy(void)
+{
if (smb_direct_wq) {
destroy_workqueue(smb_direct_wq);
smb_direct_wq = NULL;
}
}
-bool ksmbd_rdma_capable_netdev(struct net_device *netdev)
+static bool ksmbd_find_rdma_capable_netdev(struct net_device *netdev)
{
struct smb_direct_device *smb_dev;
int i;
@@ -2241,38 +2653,16 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev)
for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) {
struct net_device *ndev;
- if (smb_dev->ib_dev->ops.get_netdev) {
- ndev = smb_dev->ib_dev->ops.get_netdev(
- smb_dev->ib_dev, i + 1);
- if (!ndev)
- continue;
+ ndev = ib_device_get_netdev(smb_dev->ib_dev, i + 1);
+ if (!ndev)
+ continue;
- if (ndev == netdev) {
- dev_put(ndev);
- rdma_capable = true;
- goto out;
- }
+ if (ndev == netdev) {
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;
- }
- }
+ rdma_capable = true;
+ goto out;
}
+ dev_put(ndev);
}
}
out:
@@ -2283,15 +2673,39 @@ out:
ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_UNKNOWN);
if (ibdev) {
- if (rdma_frwr_is_supported(&ibdev->attrs))
- rdma_capable = true;
+ rdma_capable = rdma_frwr_is_supported(&ibdev->attrs);
ib_device_put(ibdev);
}
}
+ ksmbd_debug(RDMA, "netdev(%s) rdma capable : %s\n",
+ netdev->name, str_true_false(rdma_capable));
+
return rdma_capable;
}
+bool ksmbd_rdma_capable_netdev(struct net_device *netdev)
+{
+ struct net_device *lower_dev;
+ struct list_head *iter;
+
+ if (ksmbd_find_rdma_capable_netdev(netdev))
+ return true;
+
+ /* check if netdev is bridge or VLAN */
+ if (netif_is_bridge_master(netdev) ||
+ netdev->priv_flags & IFF_802_1Q_VLAN)
+ netdev_for_each_lower_dev(netdev, lower_dev, iter)
+ if (ksmbd_find_rdma_capable_netdev(lower_dev))
+ return true;
+
+ /* check if netdev is IPoIB safely without layer violation */
+ if (netdev->type == ARPHRD_INFINIBAND)
+ return true;
+
+ return false;
+}
+
static const struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = {
.prepare = smb_direct_prepare,
.disconnect = smb_direct_disconnect,
@@ -2300,4 +2714,5 @@ static const struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = {
.read = smb_direct_read,
.rdma_read = smb_direct_rdma_read,
.rdma_write = smb_direct_rdma_write,
+ .free_transport = smb_direct_free_transport,
};
diff --git a/fs/smb/server/transport_rdma.h b/fs/smb/server/transport_rdma.h
index 77aee4e5c9dc..3f93c6a9f7e4 100644
--- a/fs/smb/server/transport_rdma.h
+++ b/fs/smb/server/transport_rdma.h
@@ -11,59 +11,20 @@
#define SMBD_MIN_IOSIZE (512 * 1024)
#define SMBD_MAX_IOSIZE (16 * 1024 * 1024)
-/* SMB DIRECT negotiation request packet [MS-SMBD] 2.2.1 */
-struct smb_direct_negotiate_req {
- __le16 min_version;
- __le16 max_version;
- __le16 reserved;
- __le16 credits_requested;
- __le32 preferred_send_size;
- __le32 max_receive_size;
- __le32 max_fragmented_size;
-} __packed;
-
-/* SMB DIRECT negotiation response packet [MS-SMBD] 2.2.2 */
-struct smb_direct_negotiate_resp {
- __le16 min_version;
- __le16 max_version;
- __le16 negotiated_version;
- __le16 reserved;
- __le16 credits_requested;
- __le16 credits_granted;
- __le32 status;
- __le32 max_readwrite_size;
- __le32 preferred_send_size;
- __le32 max_receive_size;
- __le32 max_fragmented_size;
-} __packed;
-
-#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001
-
-/* SMB DIRECT data transfer packet with payload [MS-SMBD] 2.2.3 */
-struct smb_direct_data_transfer {
- __le16 credits_requested;
- __le16 credits_granted;
- __le16 flags;
- __le16 reserved;
- __le32 remaining_data_length;
- __le32 data_offset;
- __le32 data_length;
- __le32 padding;
- __u8 buffer[];
-} __packed;
-
#ifdef CONFIG_SMB_SERVER_SMBDIRECT
int ksmbd_rdma_init(void);
+void ksmbd_rdma_stop_listening(void);
void ksmbd_rdma_destroy(void);
bool ksmbd_rdma_capable_netdev(struct net_device *netdev);
void init_smbd_max_io_size(unsigned int sz);
-unsigned int get_smbd_max_read_write_size(void);
+unsigned int get_smbd_max_read_write_size(struct ksmbd_transport *kt);
#else
static inline int ksmbd_rdma_init(void) { return 0; }
-static inline int ksmbd_rdma_destroy(void) { return 0; }
+static inline void ksmbd_rdma_stop_listening(void) { }
+static inline void ksmbd_rdma_destroy(void) { }
static inline bool ksmbd_rdma_capable_netdev(struct net_device *netdev) { return false; }
static inline void init_smbd_max_io_size(unsigned int sz) { }
-static inline unsigned int get_smbd_max_read_write_size(void) { return 0; }
+static inline unsigned int get_smbd_max_read_write_size(struct ksmbd_transport *kt) { return 0; }
#endif
#endif /* __KSMBD_TRANSPORT_RDMA_H__ */
diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c
index aaed9e293b2e..4bb07937d7ef 100644
--- a/fs/smb/server/transport_tcp.c
+++ b/fs/smb/server/transport_tcp.c
@@ -22,7 +22,6 @@ struct interface {
struct socket *ksmbd_socket;
struct list_head entry;
char *name;
- struct mutex sock_release_lock;
int state;
};
@@ -56,27 +55,12 @@ static inline void ksmbd_tcp_reuseaddr(struct socket *sock)
sock_set_reuseaddr(sock->sk);
}
-static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs)
-{
- lock_sock(sock->sk);
- if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1)
- sock->sk->sk_rcvtimeo = secs * HZ;
- else
- sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
- release_sock(sock->sk);
-}
-
-static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs)
-{
- sock_set_sndtimeo(sock->sk, secs);
-}
-
static struct tcp_transport *alloc_transport(struct socket *client_sk)
{
struct tcp_transport *t;
struct ksmbd_conn *conn;
- t = kzalloc(sizeof(*t), GFP_KERNEL);
+ t = kzalloc(sizeof(*t), KSMBD_DEFAULT_GFP);
if (!t)
return NULL;
t->sock = client_sk;
@@ -87,23 +71,43 @@ static struct tcp_transport *alloc_transport(struct socket *client_sk)
return NULL;
}
+#if IS_ENABLED(CONFIG_IPV6)
+ if (client_sk->sk->sk_family == AF_INET6) {
+ memcpy(&conn->inet6_addr, &client_sk->sk->sk_v6_daddr, 16);
+ conn->inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr);
+ } else {
+ conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr;
+ conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
+ }
+#else
+ conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr;
+ conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
+#endif
+ down_write(&conn_list_lock);
+ hash_add(conn_list, &conn->hlist, conn->inet_hash);
+ up_write(&conn_list_lock);
+
conn->transport = KSMBD_TRANS(t);
KSMBD_TRANS(t)->conn = conn;
KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops;
return t;
}
-static void free_transport(struct tcp_transport *t)
+static void ksmbd_tcp_free_transport(struct ksmbd_transport *kt)
{
- kernel_sock_shutdown(t->sock, SHUT_RDWR);
- sock_release(t->sock);
- t->sock = NULL;
+ struct tcp_transport *t = TCP_TRANS(kt);
- ksmbd_conn_free(KSMBD_TRANS(t)->conn);
+ sock_release(t->sock);
kfree(t->iov);
kfree(t);
}
+static void free_transport(struct tcp_transport *t)
+{
+ kernel_sock_shutdown(t->sock, SHUT_RDWR);
+ ksmbd_conn_free(KSMBD_TRANS(t)->conn);
+}
+
/**
* kvec_array_init() - initialize a IO vector segment
* @new: IO vector to be initialized
@@ -151,7 +155,7 @@ static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs
return t->iov;
/* not big enough -- allocate a new one and release the old */
- new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL);
+ new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), KSMBD_DEFAULT_GFP);
if (new_iov) {
kfree(t->iov);
t->iov = new_iov;
@@ -160,17 +164,6 @@ static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs
return new_iov;
}
-static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa)
-{
- switch (sa->sa_family) {
- case AF_INET:
- return ntohs(((struct sockaddr_in *)sa)->sin_port);
- case AF_INET6:
- return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
- }
- return 0;
-}
-
/**
* ksmbd_tcp_new_connection() - create a new tcp session on mount
* @client_sk: socket associated with new connection
@@ -182,7 +175,6 @@ static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa)
*/
static int ksmbd_tcp_new_connection(struct socket *client_sk)
{
- struct sockaddr *csin;
int rc = 0;
struct tcp_transport *t;
struct task_struct *handler;
@@ -193,27 +185,26 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk)
return -ENOMEM;
}
- csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn);
- if (kernel_getpeername(client_sk, csin) < 0) {
- pr_err("client ip resolution failed\n");
- rc = -EINVAL;
- goto out_error;
- }
-
+#if IS_ENABLED(CONFIG_IPV6)
+ if (client_sk->sk->sk_family == AF_INET6)
+ handler = kthread_run(ksmbd_conn_handler_loop,
+ KSMBD_TRANS(t)->conn, "ksmbd:%pI6c",
+ &KSMBD_TRANS(t)->conn->inet6_addr);
+ else
+ handler = kthread_run(ksmbd_conn_handler_loop,
+ KSMBD_TRANS(t)->conn, "ksmbd:%pI4",
+ &KSMBD_TRANS(t)->conn->inet_addr);
+#else
handler = kthread_run(ksmbd_conn_handler_loop,
- KSMBD_TRANS(t)->conn,
- "ksmbd:%u",
- ksmbd_tcp_get_port(csin));
+ KSMBD_TRANS(t)->conn, "ksmbd:%pI4",
+ &KSMBD_TRANS(t)->conn->inet_addr);
+#endif
if (IS_ERR(handler)) {
pr_err("cannot start conn thread\n");
rc = PTR_ERR(handler);
free_transport(t);
}
return rc;
-
-out_error:
- free_transport(t);
- return rc;
}
/**
@@ -226,24 +217,66 @@ static int ksmbd_kthread_fn(void *p)
{
struct socket *client_sk = NULL;
struct interface *iface = (struct interface *)p;
- int ret;
+ struct ksmbd_conn *conn;
+ int ret, inet_hash;
+ unsigned int max_ip_conns;
while (!kthread_should_stop()) {
- mutex_lock(&iface->sock_release_lock);
if (!iface->ksmbd_socket) {
- mutex_unlock(&iface->sock_release_lock);
break;
}
- ret = kernel_accept(iface->ksmbd_socket, &client_sk,
- SOCK_NONBLOCK);
- mutex_unlock(&iface->sock_release_lock);
- if (ret) {
- if (ret == -EAGAIN)
- /* check for new connections every 100 msecs */
- schedule_timeout_interruptible(HZ / 10);
+ ret = kernel_accept(iface->ksmbd_socket, &client_sk, 0);
+ if (ret == -EINVAL)
+ break;
+ if (ret)
+ continue;
+
+ if (!server_conf.max_ip_connections)
+ goto skip_max_ip_conns_limit;
+
+ /*
+ * Limits repeated connections from clients with the same IP.
+ */
+#if IS_ENABLED(CONFIG_IPV6)
+ if (client_sk->sk->sk_family == AF_INET6)
+ inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr);
+ else
+ inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
+#else
+ inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
+#endif
+
+ max_ip_conns = 0;
+ down_read(&conn_list_lock);
+ hash_for_each_possible(conn_list, conn, hlist, inet_hash) {
+#if IS_ENABLED(CONFIG_IPV6)
+ if (client_sk->sk->sk_family == AF_INET6) {
+ if (memcmp(&client_sk->sk->sk_v6_daddr,
+ &conn->inet6_addr, 16) == 0)
+ max_ip_conns++;
+ } else if (inet_sk(client_sk->sk)->inet_daddr ==
+ conn->inet_addr)
+ max_ip_conns++;
+#else
+ if (inet_sk(client_sk->sk)->inet_daddr ==
+ conn->inet_addr)
+ max_ip_conns++;
+#endif
+ if (server_conf.max_ip_connections <= max_ip_conns) {
+ pr_info_ratelimited("Maximum IP connections exceeded (%u/%u)\n",
+ max_ip_conns, server_conf.max_ip_connections);
+ ret = -EAGAIN;
+ break;
+ }
+ }
+ up_read(&conn_list_lock);
+ if (ret == -EAGAIN) {
+ /* Per-IP limit hit: release the just-accepted socket. */
+ sock_release(client_sk);
continue;
}
+skip_max_ip_conns_limit:
if (server_conf.max_connections &&
atomic_inc_return(&active_num_conn) >= server_conf.max_connections) {
pr_info_ratelimited("Limit the maximum number of connections(%u)\n",
@@ -405,10 +438,6 @@ static void tcp_destroy_socket(struct socket *ksmbd_socket)
if (!ksmbd_socket)
return;
- /* set zero to timeout */
- ksmbd_tcp_rcv_timeout(ksmbd_socket, 0);
- ksmbd_tcp_snd_timeout(ksmbd_socket, 0);
-
ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR);
if (ret)
pr_err("Failed to shutdown socket: %d\n", ret);
@@ -429,12 +458,13 @@ static int create_socket(struct interface *iface)
struct socket *ksmbd_socket;
bool ipv4 = false;
- ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket);
+ ret = sock_create_kern(current->nsproxy->net_ns, PF_INET6, SOCK_STREAM,
+ IPPROTO_TCP, &ksmbd_socket);
if (ret) {
if (ret != -EAFNOSUPPORT)
pr_err("Can't create socket for ipv6, fallback to ipv4: %d\n", ret);
- ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP,
- &ksmbd_socket);
+ ret = sock_create_kern(current->nsproxy->net_ns, PF_INET,
+ SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket);
if (ret) {
pr_err("Can't create socket for ipv4: %d\n", ret);
goto out_clear;
@@ -468,19 +498,16 @@ static int create_socket(struct interface *iface)
}
if (ipv4)
- ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin,
+ ret = kernel_bind(ksmbd_socket, (struct sockaddr_unsized *)&sin,
sizeof(sin));
else
- ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6,
+ ret = kernel_bind(ksmbd_socket, (struct sockaddr_unsized *)&sin6,
sizeof(sin6));
if (ret) {
pr_err("Failed to bind socket: %d\n", ret);
goto out_error;
}
- ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
- ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
-
ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG);
if (ret) {
pr_err("Port listen() error: %d\n", ret);
@@ -504,52 +531,60 @@ out_clear:
return ret;
}
+struct interface *ksmbd_find_netdev_name_iface_list(char *netdev_name)
+{
+ struct interface *iface;
+
+ list_for_each_entry(iface, &iface_list, entry)
+ if (!strcmp(iface->name, netdev_name))
+ return iface;
+ return NULL;
+}
+
static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
struct interface *iface;
- int ret, found = 0;
+ int ret;
switch (event) {
case NETDEV_UP:
if (netif_is_bridge_port(netdev))
return NOTIFY_OK;
- list_for_each_entry(iface, &iface_list, entry) {
- if (!strcmp(iface->name, netdev->name)) {
- found = 1;
- if (iface->state != IFACE_STATE_DOWN)
- break;
- ret = create_socket(iface);
- if (ret)
- return NOTIFY_OK;
- break;
- }
+ iface = ksmbd_find_netdev_name_iface_list(netdev->name);
+ if (iface && iface->state == IFACE_STATE_DOWN) {
+ ksmbd_debug(CONN, "netdev-up event: netdev(%s) is going up\n",
+ iface->name);
+ ret = create_socket(iface);
+ if (ret)
+ return NOTIFY_OK;
}
- if (!found && bind_additional_ifaces) {
- iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL));
+ if (!iface && bind_additional_ifaces) {
+ iface = alloc_iface(kstrdup(netdev->name, KSMBD_DEFAULT_GFP));
if (!iface)
return NOTIFY_OK;
+ ksmbd_debug(CONN, "netdev-up event: netdev(%s) is going up\n",
+ iface->name);
ret = create_socket(iface);
if (ret)
break;
}
break;
case NETDEV_DOWN:
- list_for_each_entry(iface, &iface_list, entry) {
- if (!strcmp(iface->name, netdev->name) &&
- iface->state == IFACE_STATE_CONFIGURED) {
- tcp_stop_kthread(iface->ksmbd_kthread);
- iface->ksmbd_kthread = NULL;
- mutex_lock(&iface->sock_release_lock);
- tcp_destroy_socket(iface->ksmbd_socket);
- iface->ksmbd_socket = NULL;
- mutex_unlock(&iface->sock_release_lock);
-
- iface->state = IFACE_STATE_DOWN;
- break;
- }
+ iface = ksmbd_find_netdev_name_iface_list(netdev->name);
+ if (iface && iface->state == IFACE_STATE_CONFIGURED) {
+ ksmbd_debug(CONN, "netdev-down event: netdev(%s) is going down\n",
+ iface->name);
+ kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR);
+ tcp_stop_kthread(iface->ksmbd_kthread);
+ iface->ksmbd_kthread = NULL;
+ sock_release(iface->ksmbd_socket);
+ iface->ksmbd_socket = NULL;
+
+ iface->state = IFACE_STATE_DOWN;
+ break;
}
break;
}
@@ -600,7 +635,7 @@ static struct interface *alloc_iface(char *ifname)
if (!ifname)
return NULL;
- iface = kzalloc(sizeof(struct interface), GFP_KERNEL);
+ iface = kzalloc(sizeof(struct interface), KSMBD_DEFAULT_GFP);
if (!iface) {
kfree(ifname);
return NULL;
@@ -609,7 +644,6 @@ static struct interface *alloc_iface(char *ifname)
iface->name = ifname;
iface->state = IFACE_STATE_DOWN;
list_add(&iface->entry, &iface_list);
- mutex_init(&iface->sock_release_lock);
return iface;
}
@@ -618,24 +652,12 @@ int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz)
int sz = 0;
if (!ifc_list_sz) {
- struct net_device *netdev;
-
- rtnl_lock();
- for_each_netdev(&init_net, netdev) {
- if (netif_is_bridge_port(netdev))
- continue;
- if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) {
- rtnl_unlock();
- return -ENOMEM;
- }
- }
- rtnl_unlock();
bind_additional_ifaces = 1;
return 0;
}
while (ifc_list_sz > 0) {
- if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL)))
+ if (!alloc_iface(kstrdup(ifc_list, KSMBD_DEFAULT_GFP)))
return -ENOMEM;
sz = strlen(ifc_list);
@@ -655,4 +677,5 @@ static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops = {
.read = ksmbd_tcp_read,
.writev = ksmbd_tcp_writev,
.disconnect = ksmbd_tcp_disconnect,
+ .free_transport = ksmbd_tcp_free_transport,
};
diff --git a/fs/smb/server/transport_tcp.h b/fs/smb/server/transport_tcp.h
index e338bebe322f..1e51675ee1b2 100644
--- a/fs/smb/server/transport_tcp.h
+++ b/fs/smb/server/transport_tcp.h
@@ -7,6 +7,8 @@
#define __KSMBD_TRANSPORT_TCP_H__
int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz);
+struct interface *ksmbd_find_netdev_name_iface_list(char *netdev_name);
+void ksmbd_free_transport(struct ksmbd_transport *kt);
int ksmbd_tcp_init(void);
void ksmbd_tcp_destroy(void);
diff --git a/fs/smb/server/unicode.c b/fs/smb/server/unicode.c
index 217106ff7b82..85e6791745ec 100644
--- a/fs/smb/server/unicode.c
+++ b/fs/smb/server/unicode.c
@@ -297,7 +297,7 @@ char *smb_strndup_from_utf16(const char *src, const int maxlen,
if (is_unicode) {
len = smb_utf16_bytes((__le16 *)src, maxlen, codepage);
len += nls_nullsize(codepage);
- dst = kmalloc(len, GFP_KERNEL);
+ dst = kmalloc(len, KSMBD_DEFAULT_GFP);
if (!dst)
return ERR_PTR(-ENOMEM);
ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage,
@@ -309,7 +309,7 @@ char *smb_strndup_from_utf16(const char *src, const int maxlen,
} else {
len = strnlen(src, maxlen);
len++;
- dst = kmalloc(len, GFP_KERNEL);
+ dst = kmalloc(len, KSMBD_DEFAULT_GFP);
if (!dst)
return ERR_PTR(-ENOMEM);
strscpy(dst, src, len);
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 7cbd580120d1..98b0eb966d91 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -4,6 +4,7 @@
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
+#include <crypto/sha2.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/filelock.h>
@@ -19,6 +20,7 @@
#include <linux/sched/xacct.h>
#include <linux/crc32c.h>
#include <linux/namei.h>
+#include <linux/splice.h>
#include "glob.h"
#include "oplock.h"
@@ -47,32 +49,13 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,
i_uid_write(inode, i_uid_read(parent_inode));
}
-/**
- * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable
- * @parent: parent dentry
- * @child: child dentry
- *
- * Returns: %0 on success, %-ENOENT if the parent dentry is not stable
- */
-int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child)
-{
- inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
- if (child->d_parent != parent) {
- inode_unlock(d_inode(parent));
- return -ENOENT;
- }
-
- return 0;
-}
-
-static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
- char *pathname, unsigned int flags,
- struct path *parent_path,
- struct path *path)
+static int ksmbd_vfs_path_lookup(struct ksmbd_share_config *share_conf,
+ char *pathname, unsigned int flags,
+ struct path *path, bool for_remove)
{
struct qstr last;
- struct filename *filename;
- struct path *root_share_path = &share_conf->vfs_path;
+ struct filename *filename __free(putname) = NULL;
+ const struct path *root_share_path = &share_conf->vfs_path;
int err, type;
struct dentry *d;
@@ -88,56 +71,55 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
return PTR_ERR(filename);
err = vfs_path_parent_lookup(filename, flags,
- parent_path, &last, &type,
+ path, &last, &type,
root_share_path);
- if (err) {
- putname(filename);
+ if (err)
return err;
- }
if (unlikely(type != LAST_NORM)) {
- path_put(parent_path);
- putname(filename);
+ path_put(path);
return -ENOENT;
}
- err = mnt_want_write(parent_path->mnt);
- if (err) {
- path_put(parent_path);
- putname(filename);
+ if (for_remove) {
+ err = mnt_want_write(path->mnt);
+ if (err) {
+ path_put(path);
+ return -ENOENT;
+ }
+
+ d = start_removing_noperm(path->dentry, &last);
+
+ if (!IS_ERR(d)) {
+ dput(path->dentry);
+ path->dentry = d;
+ return 0;
+ }
+ mnt_drop_write(path->mnt);
+ path_put(path);
return -ENOENT;
}
- inode_lock_nested(parent_path->dentry->d_inode, I_MUTEX_PARENT);
- d = lookup_one_qstr_excl(&last, parent_path->dentry, 0);
- if (IS_ERR(d))
- goto err_out;
-
- if (d_is_negative(d)) {
+ d = lookup_noperm_unlocked(&last, path->dentry);
+ if (!IS_ERR(d) && d_is_negative(d)) {
dput(d);
- goto err_out;
+ d = ERR_PTR(-ENOENT);
}
-
+ if (IS_ERR(d)) {
+ path_put(path);
+ return -ENOENT;
+ }
+ dput(path->dentry);
path->dentry = d;
- path->mnt = mntget(parent_path->mnt);
if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_CROSSMNT)) {
err = follow_down(path, 0);
if (err < 0) {
path_put(path);
- goto err_out;
+ return -ENOENT;
}
}
-
- putname(filename);
return 0;
-
-err_out:
- inode_unlock(d_inode(parent_path->dentry));
- mnt_drop_write(parent_path->mnt);
- path_put(parent_path);
- putname(filename);
- return -ENOENT;
}
void ksmbd_vfs_query_maximal_access(struct mnt_idmap *idmap,
@@ -186,8 +168,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
}
mode |= S_IFREG;
- err = vfs_create(mnt_idmap(path.mnt), d_inode(path.dentry),
- dentry, mode, true);
+ err = vfs_create(mnt_idmap(path.mnt), dentry, mode, NULL);
if (!err) {
ksmbd_vfs_inherit_owner(work, d_inode(path.dentry),
d_inode(dentry));
@@ -195,7 +176,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode)
pr_err("File(%s): creation failed (err:%d)\n", name, err);
}
- done_path_create(&path, dentry);
+ end_creating_path(&path, dentry);
return err;
}
@@ -211,8 +192,8 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
{
struct mnt_idmap *idmap;
struct path path;
- struct dentry *dentry;
- int err;
+ struct dentry *dentry, *d;
+ int err = 0;
dentry = ksmbd_vfs_kern_path_create(work, name,
LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY,
@@ -227,28 +208,16 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode)
idmap = mnt_idmap(path.mnt);
mode |= S_IFDIR;
- err = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode);
- if (!err && d_unhashed(dentry)) {
- struct dentry *d;
-
- d = lookup_one(idmap, dentry->d_name.name, dentry->d_parent,
- dentry->d_name.len);
- if (IS_ERR(d)) {
- err = PTR_ERR(d);
- goto out_err;
- }
- if (unlikely(d_is_negative(d))) {
- dput(d);
- err = -ENOENT;
- goto out_err;
- }
-
- ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d));
- dput(d);
- }
+ d = dentry;
+ dentry = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode, NULL);
+ if (IS_ERR(dentry))
+ err = PTR_ERR(dentry);
+ else if (d_is_negative(dentry))
+ err = -ENOENT;
+ if (!err && dentry != d)
+ ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(dentry));
-out_err:
- done_path_create(&path, dentry);
+ end_creating_path(&path, dentry);
if (err)
pr_err("mkdir(%s): creation failed (err:%d)\n", name, err);
return err;
@@ -309,6 +278,7 @@ static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos,
if (v_len - *pos < count)
count = v_len - *pos;
+ fp->stream.pos = v_len;
memcpy(buf, &stream_buf[*pos], count);
@@ -333,6 +303,9 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end,
struct file_lock_context *ctx = locks_inode_context(file_inode(filp));
int error = 0;
+ if (start == end)
+ return 0;
+
if (!ctx || list_empty_careful(&ctx->flc_posix))
return 0;
@@ -426,10 +399,15 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n",
*pos, count);
+ if (*pos >= XATTR_SIZE_MAX) {
+ pr_err("stream write position %lld is out of bounds\n", *pos);
+ return -EINVAL;
+ }
+
size = *pos + count;
if (size > XATTR_SIZE_MAX) {
size = XATTR_SIZE_MAX;
- count = (*pos + count) - XATTR_SIZE_MAX;
+ count = XATTR_SIZE_MAX - *pos;
}
v_len = ksmbd_vfs_getcasexattr(idmap,
@@ -444,7 +422,7 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
}
if (v_len < size) {
- wbuf = kvzalloc(size, GFP_KERNEL);
+ wbuf = kvzalloc(size, KSMBD_DEFAULT_GFP);
if (!wbuf) {
err = -ENOMEM;
goto out;
@@ -467,8 +445,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
true);
if (err < 0)
goto out;
-
- fp->filp->f_pos = *pos;
+ else
+ fp->stream.pos = size;
err = 0;
out:
kvfree(stream_buf);
@@ -496,7 +474,8 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
int err = 0;
if (work->conn->connection_type) {
- if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) {
+ if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE)) ||
+ S_ISDIR(file_inode(fp->filp)->i_mode)) {
pr_err("no right to write(%pD)\n", fp->filp);
err = -EACCES;
goto out;
@@ -557,7 +536,8 @@ int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat)
{
int err;
- err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT);
+ err = vfs_getattr(path, stat, STATX_BASIC_STATS | STATX_BTIME,
+ AT_STATX_SYNC_AS_STAT);
if (err)
pr_err("getattr failed, err %d\n", err);
return err;
@@ -611,7 +591,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path)
idmap = mnt_idmap(path->mnt);
if (S_ISDIR(d_inode(path->dentry)->i_mode)) {
- err = vfs_rmdir(idmap, d_inode(parent), path->dentry);
+ err = vfs_rmdir(idmap, d_inode(parent), path->dentry, NULL);
if (err && err != -ENOTEMPTY)
ksmbd_debug(VFS, "rmdir failed, err %d\n", err);
} else {
@@ -672,7 +652,7 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
ksmbd_debug(VFS, "vfs_link failed err %d\n", err);
out3:
- done_path_create(&newpath, dentry);
+ end_creating_path(&newpath, dentry);
out2:
path_put(&oldpath);
out1:
@@ -683,7 +663,6 @@ out1:
int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
char *newname, int flags)
{
- struct dentry *old_parent, *new_dentry, *trap;
struct dentry *old_child = old_path->dentry;
struct path new_path;
struct qstr new_last;
@@ -719,17 +698,14 @@ retry:
if (err)
goto out2;
- trap = lock_rename_child(old_child, new_path.dentry);
- if (IS_ERR(trap)) {
- err = PTR_ERR(trap);
+ rd.mnt_idmap = mnt_idmap(old_path->mnt);
+ rd.old_parent = NULL;
+ rd.new_parent = new_path.dentry;
+ rd.flags = flags;
+ rd.delegated_inode = NULL,
+ err = start_renaming_dentry(&rd, lookup_flags, old_child, &new_last);
+ if (err)
goto out_drop_write;
- }
-
- old_parent = dget(old_child->d_parent);
- if (d_unhashed(old_child)) {
- err = -EINVAL;
- goto out3;
- }
parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent);
if (parent_fp) {
@@ -742,55 +718,17 @@ retry:
ksmbd_fd_put(work, parent_fp);
}
- new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
- lookup_flags | LOOKUP_RENAME_TARGET);
- if (IS_ERR(new_dentry)) {
- err = PTR_ERR(new_dentry);
- goto out3;
- }
-
- if (d_is_symlink(new_dentry)) {
+ if (d_is_symlink(rd.new_dentry)) {
err = -EACCES;
- goto out4;
- }
-
- /*
- * explicitly handle file overwrite case, for compatibility with
- * filesystems that may not support rename flags (e.g: fuse)
- */
- if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) {
- err = -EEXIST;
- goto out4;
- }
- flags &= ~(RENAME_NOREPLACE);
-
- if (old_child == trap) {
- err = -EINVAL;
- goto out4;
- }
-
- if (new_dentry == trap) {
- err = -ENOTEMPTY;
- goto out4;
+ goto out3;
}
- rd.old_mnt_idmap = mnt_idmap(old_path->mnt),
- rd.old_dir = d_inode(old_parent),
- rd.old_dentry = old_child,
- rd.new_mnt_idmap = mnt_idmap(new_path.mnt),
- rd.new_dir = new_path.dentry->d_inode,
- rd.new_dentry = new_dentry,
- rd.flags = flags,
- rd.delegated_inode = NULL,
err = vfs_rename(&rd);
if (err)
ksmbd_debug(VFS, "vfs_rename failed err %d\n", err);
-out4:
- dput(new_dentry);
out3:
- dput(old_parent);
- unlock_rename(old_parent, new_path.dentry);
+ end_renaming(&rd);
out_drop_write:
mnt_drop_write(old_path->mnt);
out2:
@@ -832,7 +770,7 @@ int ksmbd_vfs_truncate(struct ksmbd_work *work,
if (size < inode->i_size) {
err = check_lock_range(filp, size,
inode->i_size - 1, WRITE);
- } else {
+ } else if (size > inode->i_size) {
err = check_lock_range(filp, inode->i_size,
size - 1, WRITE);
}
@@ -865,7 +803,7 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list)
if (size <= 0)
return size;
- vlist = kvzalloc(size, GFP_KERNEL);
+ vlist = kvzalloc(size, KSMBD_DEFAULT_GFP);
if (!vlist)
return -ENOMEM;
@@ -907,7 +845,7 @@ ssize_t ksmbd_vfs_getxattr(struct mnt_idmap *idmap,
if (xattr_len < 0)
return xattr_len;
- buf = kmalloc(xattr_len + 1, GFP_KERNEL);
+ buf = kmalloc(xattr_len + 1, KSMBD_DEFAULT_GFP);
if (!buf)
return -ENOMEM;
@@ -1088,18 +1026,17 @@ int ksmbd_vfs_unlink(struct file *filp)
return err;
dir = dget_parent(dentry);
- err = ksmbd_vfs_lock_parent(dir, dentry);
- if (err)
+ dentry = start_removing_dentry(dir, dentry);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
- dget(dentry);
if (S_ISDIR(d_inode(dentry)->i_mode))
- err = vfs_rmdir(idmap, d_inode(dir), dentry);
+ err = vfs_rmdir(idmap, d_inode(dir), dentry, NULL);
else
err = vfs_unlink(idmap, d_inode(dir), dentry, NULL);
- dput(dentry);
- inode_unlock(d_inode(dir));
+ end_removing(dentry);
if (err)
ksmbd_debug(VFS, "failed to delete, err %d\n", err);
out:
@@ -1208,102 +1145,113 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
return ret;
}
-/**
- * ksmbd_vfs_kern_path_locked() - lookup a file and get path info
- * @work: work
- * @name: file path that is relative to share
- * @flags: lookup flags
- * @parent_path: if lookup succeed, return parent_path info
- * @path: if lookup succeed, return path info
- * @caseless: caseless filename lookup
- *
- * Return: 0 on success, otherwise error
- */
-int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
- unsigned int flags, struct path *parent_path,
- struct path *path, bool caseless)
+static
+int __ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
+ unsigned int flags,
+ struct path *path, bool caseless, bool for_remove)
{
struct ksmbd_share_config *share_conf = work->tcon->share_conf;
+ struct path parent_path;
+ size_t path_len, remain_len;
int err;
- err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, parent_path,
- path);
- if (!err)
- return 0;
-
- if (caseless) {
- char *filepath;
- size_t path_len, remain_len;
-
- filepath = name;
- path_len = strlen(filepath);
- remain_len = path_len;
-
- *parent_path = share_conf->vfs_path;
- path_get(parent_path);
+retry:
+ err = ksmbd_vfs_path_lookup(share_conf, filepath, flags, path, for_remove);
+ if (!err || !caseless)
+ return err;
- while (d_can_lookup(parent_path->dentry)) {
- char *filename = filepath + path_len - remain_len;
- char *next = strchrnul(filename, '/');
- size_t filename_len = next - filename;
- bool is_last = !next[0];
+ path_len = strlen(filepath);
+ remain_len = path_len;
- if (filename_len == 0)
- break;
+ parent_path = share_conf->vfs_path;
+ path_get(&parent_path);
- err = ksmbd_vfs_lookup_in_dir(parent_path, filename,
- filename_len,
- work->conn->um);
- if (err)
- goto out2;
+ while (d_can_lookup(parent_path.dentry)) {
+ char *filename = filepath + path_len - remain_len;
+ char *next = strchrnul(filename, '/');
+ size_t filename_len = next - filename;
+ bool is_last = !next[0];
- next[0] = '\0';
+ if (filename_len == 0)
+ break;
- err = vfs_path_lookup(share_conf->vfs_path.dentry,
- share_conf->vfs_path.mnt,
- filepath,
- flags,
- path);
- if (err)
- goto out2;
- else if (is_last)
- goto out1;
- path_put(parent_path);
- *parent_path = *path;
-
- next[0] = '/';
- remain_len -= filename_len + 1;
+ err = ksmbd_vfs_lookup_in_dir(&parent_path, filename,
+ filename_len,
+ work->conn->um);
+ path_put(&parent_path);
+ if (err)
+ goto out;
+ if (is_last) {
+ caseless = false;
+ goto retry;
}
+ next[0] = '\0';
+
+ err = vfs_path_lookup(share_conf->vfs_path.dentry,
+ share_conf->vfs_path.mnt,
+ filepath,
+ flags,
+ &parent_path);
+ next[0] = '/';
+ if (err)
+ goto out;
- err = -EINVAL;
-out2:
- path_put(parent_path);
+ remain_len -= filename_len + 1;
}
-out1:
- if (!err) {
- err = mnt_want_write(parent_path->mnt);
- if (err) {
- path_put(path);
- path_put(parent_path);
- return err;
- }
-
- err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry);
- if (err) {
- path_put(path);
- path_put(parent_path);
- }
- }
+ err = -EINVAL;
+ path_put(&parent_path);
+out:
return err;
}
-void ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path)
+/**
+ * ksmbd_vfs_kern_path() - lookup a file and get path info
+ * @work: work
+ * @filepath: file path that is relative to share
+ * @flags: lookup flags
+ * @path: if lookup succeed, return path info
+ * @caseless: caseless filename lookup
+ *
+ * Perform the lookup, possibly crossing over any mount point.
+ * On return no locks will be held and write-access to filesystem
+ * won't have been checked.
+ * Return: 0 if file was found, otherwise error
+ */
+int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
+ unsigned int flags,
+ struct path *path, bool caseless)
{
- inode_unlock(d_inode(parent_path->dentry));
- mnt_drop_write(parent_path->mnt);
- path_put(path);
- path_put(parent_path);
+ return __ksmbd_vfs_kern_path(work, filepath, flags, path,
+ caseless, false);
+}
+
+/**
+ * ksmbd_vfs_kern_path_start_remove() - lookup a file and get path info prior to removal
+ * @work: work
+ * @filepath: file path that is relative to share
+ * @flags: lookup flags
+ * @path: if lookup succeed, return path info
+ * @caseless: caseless filename lookup
+ *
+ * Perform the lookup, but don't cross over any mount point.
+ * On return the parent of path->dentry will be locked and write-access to
+ * filesystem will have been gained.
+ * Return: 0 on if file was found, otherwise error
+ */
+int ksmbd_vfs_kern_path_start_removing(struct ksmbd_work *work, char *filepath,
+ unsigned int flags,
+ struct path *path, bool caseless)
+{
+ return __ksmbd_vfs_kern_path(work, filepath, flags, path,
+ caseless, true);
+}
+
+void ksmbd_vfs_kern_path_end_removing(const struct path *path)
+{
+ end_removing(path->dentry);
+ mnt_drop_write(path->mnt);
+ mntput(path->mnt);
}
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
@@ -1318,7 +1266,7 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
if (!abs_name)
return ERR_PTR(-ENOMEM);
- dent = kern_path_create(AT_FDCWD, abs_name, path, flags);
+ dent = start_creating_path(AT_FDCWD, abs_name, path, flags);
kfree(abs_name);
return dent;
}
@@ -1411,7 +1359,7 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct mnt_idmap *id
smb_acl = kzalloc(sizeof(struct xattr_smb_acl) +
sizeof(struct xattr_acl_entry) * posix_acls->a_count,
- GFP_KERNEL);
+ KSMBD_DEFAULT_GFP);
if (!smb_acl)
goto out;
@@ -1487,11 +1435,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
acl.sd_buf = (char *)pntsd;
acl.sd_size = len;
- rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash);
- if (rc) {
- pr_err("failed to generate hash for ndr acl\n");
- return rc;
- }
+ sha256(acl.sd_buf, acl.sd_size, acl.hash);
smb_acl = ksmbd_vfs_make_xattr_posix_acl(idmap, inode,
ACL_TYPE_ACCESS);
@@ -1506,12 +1450,7 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
goto out;
}
- rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset,
- acl.posix_acl_hash);
- if (rc) {
- pr_err("failed to generate hash for ndr acl\n");
- goto out;
- }
+ sha256(acl_ndr.data, acl_ndr.offset, acl.posix_acl_hash);
rc = ndr_encode_v4_ntacl(&sd_ndr, &acl);
if (rc) {
@@ -1568,11 +1507,7 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
goto out_free;
}
- rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, cmp_hash);
- if (rc) {
- pr_err("failed to generate hash for ndr acl\n");
- goto out_free;
- }
+ sha256(acl_ndr.data, acl_ndr.offset, cmp_hash);
if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) {
pr_err("hash value diff\n");
@@ -1659,7 +1594,7 @@ int ksmbd_vfs_get_dos_attrib_xattr(struct mnt_idmap *idmap,
*/
void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat)
{
- struct file_directory_info *info = (struct file_directory_info *)(*p);
+ FILE_DIRECTORY_INFO *info = (FILE_DIRECTORY_INFO *)(*p);
struct kstat *kstat = ksmbd_kstat->kstat;
u64 time;
@@ -1767,7 +1702,7 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
else
type = ":$DATA";
- buf = kasprintf(GFP_KERNEL, "%s%s%s",
+ buf = kasprintf(KSMBD_DEFAULT_GFP, "%s%s%s",
XATTR_NAME_STREAM, stream_name, type);
if (!buf)
return -ENOMEM;
@@ -1835,8 +1770,19 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
if (src_off + len > src_file_size)
return -E2BIG;
- ret = vfs_copy_file_range(src_fp->filp, src_off,
- dst_fp->filp, dst_off, len, 0);
+ /*
+ * vfs_copy_file_range does not allow overlapped copying
+ * within the same file.
+ */
+ if (file_inode(src_fp->filp) == file_inode(dst_fp->filp) &&
+ dst_off + len > src_off &&
+ dst_off < src_off + len)
+ ret = do_splice_direct(src_fp->filp, &src_off,
+ dst_fp->filp, &dst_off,
+ min_t(size_t, len, MAX_RW_COUNT), 0);
+ else
+ ret = vfs_copy_file_range(src_fp->filp, src_off,
+ dst_fp->filp, dst_off, len, 0);
if (ret == -EOPNOTSUPP || ret == -EXDEV)
ret = vfs_copy_file_range(src_fp->filp, src_off,
dst_fp->filp, dst_off, len,
@@ -1855,20 +1801,13 @@ void ksmbd_vfs_posix_lock_wait(struct file_lock *flock)
wait_event(flock->c.flc_wait, !flock->c.flc_blocker);
}
-int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout)
-{
- return wait_event_interruptible_timeout(flock->c.flc_wait,
- !flock->c.flc_blocker,
- timeout);
-}
-
void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock)
{
locks_delete_block(flock);
}
int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
- struct path *path)
+ const struct path *path)
{
struct posix_acl_state acl_state;
struct posix_acl *acls;
@@ -1896,7 +1835,7 @@ int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
acl_state.group.allow;
acl_state.mask.allow = 0x07;
- acls = posix_acl_alloc(6, GFP_KERNEL);
+ acls = posix_acl_alloc(6, KSMBD_DEFAULT_GFP);
if (!acls) {
free_acl_state(&acl_state);
return -ENOMEM;
@@ -1921,7 +1860,7 @@ int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
}
int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
- struct path *path, struct inode *parent_inode)
+ const struct path *path, struct inode *parent_inode)
{
struct posix_acl *acls;
struct posix_acl_entry *pace;
diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h
index cb76f4b5bafe..16ca29ee16e5 100644
--- a/fs/smb/server/vfs.h
+++ b/fs/smb/server/vfs.h
@@ -43,6 +43,7 @@ struct ksmbd_dir_info {
char *rptr;
int name_len;
int out_buf_len;
+ int num_scan;
int num_entry;
int data_count;
int last_entry_offset;
@@ -116,10 +117,13 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
const struct path *path, char *attr_name,
bool get_write);
-int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
- unsigned int flags, struct path *parent_path,
- struct path *path, bool caseless);
-void ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path);
+int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
+ unsigned int flags,
+ struct path *path, bool caseless);
+int ksmbd_vfs_kern_path_start_removing(struct ksmbd_work *work, char *name,
+ unsigned int flags,
+ struct path *path, bool caseless);
+void ksmbd_vfs_kern_path_end_removing(const struct path *path);
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
const char *name,
unsigned int flags,
@@ -139,7 +143,6 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
struct dentry *dentry,
struct ksmbd_kstat *ksmbd_kstat);
void ksmbd_vfs_posix_lock_wait(struct file_lock *flock);
-int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout);
void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock);
int ksmbd_vfs_remove_acl_xattrs(struct mnt_idmap *idmap,
const struct path *path);
@@ -161,8 +164,8 @@ int ksmbd_vfs_get_dos_attrib_xattr(struct mnt_idmap *idmap,
struct dentry *dentry,
struct xattr_dos_attrib *da);
int ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap,
- struct path *path);
+ const struct path *path);
int ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap,
- struct path *path,
+ const struct path *path,
struct inode *parent_inode);
#endif /* __KSMBD_VFS_H__ */
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index a19f4e563c7e..6ef116585af6 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -112,40 +112,62 @@ int ksmbd_query_inode_status(struct dentry *dentry)
read_lock(&inode_hash_lock);
ci = __ksmbd_inode_lookup(dentry);
- if (ci) {
- ret = KSMBD_INODE_STATUS_OK;
- if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS))
- ret = KSMBD_INODE_STATUS_PENDING_DELETE;
- atomic_dec(&ci->m_count);
- }
read_unlock(&inode_hash_lock);
+ if (!ci)
+ return ret;
+
+ down_read(&ci->m_lock);
+ if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS))
+ ret = KSMBD_INODE_STATUS_PENDING_DELETE;
+ else
+ ret = KSMBD_INODE_STATUS_OK;
+ up_read(&ci->m_lock);
+
+ atomic_dec(&ci->m_count);
return ret;
}
bool ksmbd_inode_pending_delete(struct ksmbd_file *fp)
{
- return (fp->f_ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS));
+ struct ksmbd_inode *ci = fp->f_ci;
+ int ret;
+
+ down_read(&ci->m_lock);
+ ret = (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS));
+ up_read(&ci->m_lock);
+
+ return ret;
}
void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp)
{
- fp->f_ci->m_flags |= S_DEL_PENDING;
+ struct ksmbd_inode *ci = fp->f_ci;
+
+ down_write(&ci->m_lock);
+ ci->m_flags |= S_DEL_PENDING;
+ up_write(&ci->m_lock);
}
void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp)
{
- fp->f_ci->m_flags &= ~S_DEL_PENDING;
+ struct ksmbd_inode *ci = fp->f_ci;
+
+ down_write(&ci->m_lock);
+ ci->m_flags &= ~S_DEL_PENDING;
+ up_write(&ci->m_lock);
}
void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
int file_info)
{
- if (ksmbd_stream_fd(fp)) {
- fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM;
- return;
- }
+ struct ksmbd_inode *ci = fp->f_ci;
- fp->f_ci->m_flags |= S_DEL_ON_CLS;
+ down_write(&ci->m_lock);
+ if (ksmbd_stream_fd(fp))
+ ci->m_flags |= S_DEL_ON_CLS_STREAM;
+ else
+ ci->m_flags |= S_DEL_ON_CLS;
+ up_write(&ci->m_lock);
}
static void ksmbd_inode_hash(struct ksmbd_inode *ci)
@@ -188,7 +210,7 @@ static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp)
if (ci)
return ci;
- ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL);
+ ci = kmalloc(sizeof(struct ksmbd_inode), KSMBD_DEFAULT_GFP);
if (!ci)
return NULL;
@@ -257,27 +279,41 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
struct file *filp;
filp = fp->filp;
- if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) {
- ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
- err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp),
- &filp->f_path,
- fp->stream.name,
- true);
- if (err)
- pr_err("remove xattr failed : %s\n",
- fp->stream.name);
+
+ if (ksmbd_stream_fd(fp)) {
+ bool remove_stream_xattr = false;
+
+ down_write(&ci->m_lock);
+ if (ci->m_flags & S_DEL_ON_CLS_STREAM) {
+ ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
+ remove_stream_xattr = true;
+ }
+ up_write(&ci->m_lock);
+
+ if (remove_stream_xattr) {
+ err = ksmbd_vfs_remove_xattr(file_mnt_idmap(filp),
+ &filp->f_path,
+ fp->stream.name,
+ true);
+ if (err)
+ pr_err("remove xattr failed : %s\n",
+ fp->stream.name);
+ }
}
if (atomic_dec_and_test(&ci->m_count)) {
+ bool do_unlink = false;
+
down_write(&ci->m_lock);
if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) {
ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING);
- up_write(&ci->m_lock);
- ksmbd_vfs_unlink(filp);
- down_write(&ci->m_lock);
+ do_unlink = true;
}
up_write(&ci->m_lock);
+ if (do_unlink)
+ ksmbd_vfs_unlink(filp);
+
ksmbd_inode_free(ci);
}
}
@@ -577,7 +613,7 @@ static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
return -EMFILE;
}
- idr_preload(GFP_KERNEL);
+ idr_preload(KSMBD_DEFAULT_GFP);
write_lock(&ft->lock);
ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT);
if (ret >= 0) {
@@ -605,7 +641,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
struct ksmbd_file *fp;
int ret;
- fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL);
+ fp = kmem_cache_zalloc(filp_cache, KSMBD_DEFAULT_GFP);
if (!fp) {
pr_err("Failed to allocate memory\n");
return ERR_PTR(-ENOMEM);
@@ -661,21 +697,40 @@ __close_file_table_ids(struct ksmbd_file_table *ft,
bool (*skip)(struct ksmbd_tree_connect *tcon,
struct ksmbd_file *fp))
{
- unsigned int id;
- struct ksmbd_file *fp;
- int num = 0;
+ struct ksmbd_file *fp;
+ unsigned int id = 0;
+ int num = 0;
+
+ while (1) {
+ write_lock(&ft->lock);
+ fp = idr_get_next(ft->idr, &id);
+ if (!fp) {
+ write_unlock(&ft->lock);
+ break;
+ }
- idr_for_each_entry(ft->idr, fp, id) {
- if (skip(tcon, fp))
+ if (skip(tcon, fp) ||
+ !atomic_dec_and_test(&fp->refcount)) {
+ id++;
+ write_unlock(&ft->lock);
continue;
+ }
set_close_state_blocked_works(fp);
+ idr_remove(ft->idr, fp->volatile_id);
+ fp->volatile_id = KSMBD_NO_FID;
+ write_unlock(&ft->lock);
+
+ down_write(&fp->f_ci->m_lock);
+ list_del_init(&fp->node);
+ up_write(&fp->f_ci->m_lock);
- if (!atomic_dec_and_test(&fp->refcount))
- continue;
__ksmbd_close_fd(ft, fp);
+
num++;
+ id++;
}
+
return num;
}
@@ -713,12 +768,8 @@ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
static bool ksmbd_durable_scavenger_alive(void)
{
- mutex_lock(&durable_scavenger_lock);
- if (!durable_scavenger_running) {
- mutex_unlock(&durable_scavenger_lock);
+ if (!durable_scavenger_running)
return false;
- }
- mutex_unlock(&durable_scavenger_lock);
if (kthread_should_stop())
return false;
@@ -799,9 +850,7 @@ static int ksmbd_durable_scavenger(void *dummy)
break;
}
- mutex_lock(&durable_scavenger_lock);
durable_scavenger_running = false;
- mutex_unlock(&durable_scavenger_lock);
module_put(THIS_MODULE);
@@ -923,7 +972,7 @@ int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share,
char *pathname, *ab_pathname;
int ret = 0;
- pathname = kmalloc(PATH_MAX, GFP_KERNEL);
+ pathname = kmalloc(PATH_MAX, KSMBD_DEFAULT_GFP);
if (!pathname)
return -EACCES;
@@ -983,7 +1032,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
int ksmbd_init_file_table(struct ksmbd_file_table *ft)
{
- ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL);
+ ft->idr = kzalloc(sizeof(struct idr), KSMBD_DEFAULT_GFP);
if (!ft->idr)
return -ENOMEM;
diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
index 5bbb179736c2..78b506c5ef03 100644
--- a/fs/smb/server/vfs_cache.h
+++ b/fs/smb/server/vfs_cache.h
@@ -44,6 +44,7 @@ struct ksmbd_lock {
struct stream {
char *name;
ssize_t size;
+ loff_t pos;
};
struct ksmbd_inode {
@@ -111,6 +112,8 @@ struct ksmbd_file {
bool is_durable;
bool is_persistent;
bool is_resilient;
+
+ bool is_posix_ctxt;
};
static inline void set_ctx_actor(struct dir_context *ctx,