summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/ceph/dir.c3
-rw-r--r--fs/ceph/file.c30
-rw-r--r--fs/ceph/inode.c11
-rw-r--r--fs/ceph/io.c100
-rw-r--r--fs/ceph/io.h8
-rw-r--r--fs/ceph/ioctl.c17
-rw-r--r--fs/ceph/locks.c5
-rw-r--r--fs/ceph/mds_client.c22
-rw-r--r--fs/ceph/mdsmap.c14
-rw-r--r--fs/ceph/super.c14
-rw-r--r--fs/ceph/super.h17
-rw-r--r--fs/hpfs/anode.c43
-rw-r--r--fs/hpfs/ea.c2
-rw-r--r--fs/hpfs/file.c4
-rw-r--r--fs/hpfs/hpfs.h44
-rw-r--r--fs/hpfs/map.c8
-rw-r--r--fs/hpfs/namei.c18
-rw-r--r--fs/hpfs/super.c8
-rw-r--r--fs/iomap/direct-io.c3
-rw-r--r--fs/smb/client/cached_dir.c37
-rw-r--r--fs/smb/client/cached_dir.h1
-rw-r--r--fs/smb/client/cifs_spnego.c12
-rw-r--r--fs/smb/client/cifs_swn.c20
-rw-r--r--fs/smb/client/cifsfs.c22
-rw-r--r--fs/smb/client/cifsfs.h4
-rw-r--r--fs/smb/client/cifsglob.h5
-rw-r--r--fs/smb/client/cifssmb.c4
-rw-r--r--fs/smb/client/dir.c38
-rw-r--r--fs/smb/client/file.c105
-rw-r--r--fs/smb/client/inode.c151
-rw-r--r--fs/smb/client/smb1ops.c69
-rw-r--r--fs/smb/client/smb2inode.c24
-rw-r--r--fs/smb/client/smb2ops.c334
-rw-r--r--fs/smb/client/smb2pdu.c2
-rw-r--r--fs/smb/client/smb2pdu.h16
-rw-r--r--fs/smb/client/smb2transport.c5
-rw-r--r--fs/smb/client/trace.h2
37 files changed, 769 insertions, 453 deletions
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 32973c62c1a2..d18c0eaef9b7 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1260,8 +1260,7 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc,
spin_unlock(&fsc->async_unlink_conflict_lock);
spin_lock(&dentry->d_lock);
- di->flags &= ~CEPH_DENTRY_ASYNC_UNLINK;
- wake_up_bit(&di->flags, CEPH_DENTRY_ASYNC_UNLINK_BIT);
+ clear_and_wake_up_bit(CEPH_DENTRY_ASYNC_UNLINK_BIT, &di->flags);
spin_unlock(&dentry->d_lock);
synchronize_rcu();
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 978acd3d4b32..99b30f784ee2 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -579,8 +579,7 @@ static void wake_async_create_waiters(struct inode *inode,
spin_lock(&ci->i_ceph_lock);
if (ci->i_ceph_flags & CEPH_I_ASYNC_CREATE) {
- ci->i_ceph_flags &= ~CEPH_I_ASYNC_CREATE;
- wake_up_bit(&ci->i_ceph_flags, CEPH_ASYNC_CREATE_BIT);
+ clear_and_wake_up_bit(CEPH_ASYNC_CREATE_BIT, &ci->i_ceph_flags);
if (ci->i_ceph_flags & CEPH_I_ASYNC_CHECK_CAPS) {
ci->i_ceph_flags &= ~CEPH_I_ASYNC_CHECK_CAPS;
@@ -762,8 +761,7 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode,
}
spin_lock(&dentry->d_lock);
- di->flags &= ~CEPH_DENTRY_ASYNC_CREATE;
- wake_up_bit(&di->flags, CEPH_DENTRY_ASYNC_CREATE_BIT);
+ clear_and_wake_up_bit(CEPH_DENTRY_ASYNC_CREATE_BIT, &di->flags);
spin_unlock(&dentry->d_lock);
return ret;
@@ -2121,10 +2119,10 @@ again:
if (ceph_inode_is_shutdown(inode))
return -ESTALE;
- if (direct_lock)
- ceph_start_io_direct(inode);
- else
- ceph_start_io_read(inode);
+ ret = direct_lock ? ceph_start_io_direct(inode) :
+ ceph_start_io_read(inode);
+ if (ret)
+ return ret;
if (!(fi->flags & CEPH_F_SYNC) && !direct_lock)
want |= CEPH_CAP_FILE_CACHE;
@@ -2277,7 +2275,9 @@ static ssize_t ceph_splice_read(struct file *in, loff_t *ppos,
(fi->flags & CEPH_F_SYNC))
return copy_splice_read(in, ppos, pipe, len, flags);
- ceph_start_io_read(inode);
+ ret = ceph_start_io_read(inode);
+ if (ret)
+ return ret;
want = CEPH_CAP_FILE_CACHE;
if (fi->fmode & CEPH_FILE_MODE_LAZY)
@@ -2356,10 +2356,10 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from)
direct_lock = true;
retry_snap:
- if (direct_lock)
- ceph_start_io_direct(inode);
- else
- ceph_start_io_write(inode);
+ err = direct_lock ? ceph_start_io_direct(inode) :
+ ceph_start_io_write(inode);
+ if (err)
+ goto out_unlocked;
if (iocb->ki_flags & IOCB_APPEND) {
err = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE, false);
@@ -2878,7 +2878,7 @@ static ssize_t ceph_do_objects_copy(struct ceph_inode_info *src_ci, u64 *src_off
struct ceph_object_id src_oid, dst_oid;
struct ceph_osd_client *osdc;
struct ceph_osd_request *req;
- size_t bytes = 0;
+ ssize_t bytes = 0;
u64 src_objnum, src_objoff, dst_objnum, dst_objoff;
u32 src_objlen, dst_objlen;
u32 object_size = src_ci->i_layout.object_size;
@@ -2928,7 +2928,7 @@ static ssize_t ceph_do_objects_copy(struct ceph_inode_info *src_ci, u64 *src_off
"OSDs don't support copy-from2; disabling copy offload\n");
}
doutc(cl, "returned %d\n", ret);
- if (!bytes)
+ if (bytes <= 0)
bytes = ret;
goto out;
}
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 949f0badc944..a6e260d9e420 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1794,6 +1794,11 @@ retry_lookup:
goto done;
}
+ if (unlikely(!in)) {
+ err = -EINVAL;
+ goto done;
+ }
+
/* attach proper inode */
if (d_really_is_negative(dn)) {
ceph_dir_clear_ordered(dir);
@@ -1829,6 +1834,12 @@ retry_lookup:
doutc(cl, " linking snapped dir %p to dn %p\n", in,
req->r_dentry);
ceph_dir_clear_ordered(dir);
+
+ if (unlikely(!in)) {
+ err = -EINVAL;
+ goto done;
+ }
+
ihold(in);
err = splice_dentry(&req->r_dentry, in);
if (err < 0)
diff --git a/fs/ceph/io.c b/fs/ceph/io.c
index c456509b31c3..2d10f49c93a9 100644
--- a/fs/ceph/io.c
+++ b/fs/ceph/io.c
@@ -21,14 +21,23 @@
/* Call with exclusively locked inode->i_rwsem */
static void ceph_block_o_direct(struct ceph_inode_info *ci, struct inode *inode)
{
+ bool is_odirect;
+
lockdep_assert_held_write(&inode->i_rwsem);
- if (READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT) {
- spin_lock(&ci->i_ceph_lock);
- ci->i_ceph_flags &= ~CEPH_I_ODIRECT;
- spin_unlock(&ci->i_ceph_lock);
- inode_dio_wait(inode);
+ spin_lock(&ci->i_ceph_lock);
+ /* ensure that bit state is consistent */
+ smp_mb__before_atomic();
+ is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;
+ if (is_odirect) {
+ clear_bit(CEPH_I_ODIRECT_BIT, &ci->i_ceph_flags);
+ /* ensure modified bit is visible */
+ smp_mb__after_atomic();
}
+ spin_unlock(&ci->i_ceph_lock);
+
+ if (is_odirect)
+ inode_dio_wait(inode);
}
/**
@@ -47,20 +56,35 @@ static void ceph_block_o_direct(struct ceph_inode_info *ci, struct inode *inode)
* Note that buffered writes and truncates both take a write lock on
* inode->i_rwsem, meaning that those are serialised w.r.t. the reads.
*/
-void
-ceph_start_io_read(struct inode *inode)
+int ceph_start_io_read(struct inode *inode)
{
struct ceph_inode_info *ci = ceph_inode(inode);
+ bool is_odirect;
+ int err;
/* Be an optimist! */
- down_read(&inode->i_rwsem);
- if (!(READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT))
- return;
+ err = down_read_killable(&inode->i_rwsem);
+ if (err)
+ return err;
+
+ spin_lock(&ci->i_ceph_lock);
+ /* ensure that bit state is consistent */
+ smp_mb__before_atomic();
+ is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;
+ spin_unlock(&ci->i_ceph_lock);
+ if (!is_odirect)
+ return 0;
up_read(&inode->i_rwsem);
+
/* Slow path.... */
- down_write(&inode->i_rwsem);
+ err = down_write_killable(&inode->i_rwsem);
+ if (err)
+ return err;
+
ceph_block_o_direct(ci, inode);
downgrade_write(&inode->i_rwsem);
+
+ return 0;
}
/**
@@ -83,11 +107,12 @@ ceph_end_io_read(struct inode *inode)
* Declare that a buffered write operation is about to start, and ensure
* that we block all direct I/O.
*/
-void
-ceph_start_io_write(struct inode *inode)
+int ceph_start_io_write(struct inode *inode)
{
- down_write(&inode->i_rwsem);
- ceph_block_o_direct(ceph_inode(inode), inode);
+ int err = down_write_killable(&inode->i_rwsem);
+ if (!err)
+ ceph_block_o_direct(ceph_inode(inode), inode);
+ return err;
}
/**
@@ -106,12 +131,22 @@ ceph_end_io_write(struct inode *inode)
/* Call with exclusively locked inode->i_rwsem */
static void ceph_block_buffered(struct ceph_inode_info *ci, struct inode *inode)
{
+ bool is_odirect;
+
lockdep_assert_held_write(&inode->i_rwsem);
- if (!(READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT)) {
- spin_lock(&ci->i_ceph_lock);
- ci->i_ceph_flags |= CEPH_I_ODIRECT;
- spin_unlock(&ci->i_ceph_lock);
+ spin_lock(&ci->i_ceph_lock);
+ /* ensure that bit state is consistent */
+ smp_mb__before_atomic();
+ is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;
+ if (!is_odirect) {
+ set_bit(CEPH_I_ODIRECT_BIT, &ci->i_ceph_flags);
+ /* ensure modified bit is visible */
+ smp_mb__after_atomic();
+ }
+ spin_unlock(&ci->i_ceph_lock);
+
+ if (!is_odirect) {
/* FIXME: unmap_mapping_range? */
filemap_write_and_wait(inode->i_mapping);
}
@@ -133,20 +168,35 @@ static void ceph_block_buffered(struct ceph_inode_info *ci, struct inode *inode)
* Note that buffered writes and truncates both take a write lock on
* inode->i_rwsem, meaning that those are serialised w.r.t. O_DIRECT.
*/
-void
-ceph_start_io_direct(struct inode *inode)
+int ceph_start_io_direct(struct inode *inode)
{
struct ceph_inode_info *ci = ceph_inode(inode);
+ bool is_odirect;
+ int err;
/* Be an optimist! */
- down_read(&inode->i_rwsem);
- if (READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT)
- return;
+ err = down_read_killable(&inode->i_rwsem);
+ if (err)
+ return err;
+
+ spin_lock(&ci->i_ceph_lock);
+ /* ensure that bit state is consistent */
+ smp_mb__before_atomic();
+ is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;
+ spin_unlock(&ci->i_ceph_lock);
+ if (is_odirect)
+ return 0;
up_read(&inode->i_rwsem);
+
/* Slow path.... */
- down_write(&inode->i_rwsem);
+ err = down_write_killable(&inode->i_rwsem);
+ if (err)
+ return err;
+
ceph_block_buffered(ci, inode);
downgrade_write(&inode->i_rwsem);
+
+ return 0;
}
/**
diff --git a/fs/ceph/io.h b/fs/ceph/io.h
index fa594cd77348..79029825e8b8 100644
--- a/fs/ceph/io.h
+++ b/fs/ceph/io.h
@@ -2,11 +2,13 @@
#ifndef _FS_CEPH_IO_H
#define _FS_CEPH_IO_H
-void ceph_start_io_read(struct inode *inode);
+#include <linux/compiler_attributes.h>
+
+int __must_check ceph_start_io_read(struct inode *inode);
void ceph_end_io_read(struct inode *inode);
-void ceph_start_io_write(struct inode *inode);
+int __must_check ceph_start_io_write(struct inode *inode);
void ceph_end_io_write(struct inode *inode);
-void ceph_start_io_direct(struct inode *inode);
+int __must_check ceph_start_io_direct(struct inode *inode);
void ceph_end_io_direct(struct inode *inode);
#endif /* FS_CEPH_IO_H */
diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
index e861de3c79b9..15cde055f3da 100644
--- a/fs/ceph/ioctl.c
+++ b/fs/ceph/ioctl.c
@@ -246,21 +246,28 @@ static long ceph_ioctl_lazyio(struct file *file)
struct ceph_inode_info *ci = ceph_inode(inode);
struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc;
struct ceph_client *cl = mdsc->fsc->client;
+ bool is_file_already_lazy = false;
+ spin_lock(&ci->i_ceph_lock);
if ((fi->fmode & CEPH_FILE_MODE_LAZY) == 0) {
- spin_lock(&ci->i_ceph_lock);
fi->fmode |= CEPH_FILE_MODE_LAZY;
ci->i_nr_by_mode[ffs(CEPH_FILE_MODE_LAZY)]++;
__ceph_touch_fmode(ci, mdsc, fi->fmode);
- spin_unlock(&ci->i_ceph_lock);
+ } else {
+ is_file_already_lazy = true;
+ }
+ spin_unlock(&ci->i_ceph_lock);
+
+ if (is_file_already_lazy) {
+ doutc(cl, "file %p %p %llx.%llx already lazy\n", file, inode,
+ ceph_vinop(inode));
+ } else {
doutc(cl, "file %p %p %llx.%llx marked lazy\n", file, inode,
ceph_vinop(inode));
ceph_check_caps(ci, 0);
- } else {
- doutc(cl, "file %p %p %llx.%llx already lazy\n", file, inode,
- ceph_vinop(inode));
}
+
return 0;
}
diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c
index ebf4ac0055dd..dd764f9c64b9 100644
--- a/fs/ceph/locks.c
+++ b/fs/ceph/locks.c
@@ -221,7 +221,10 @@ static int ceph_lock_wait_for_completion(struct ceph_mds_client *mdsc,
if (err && err != -ERESTARTSYS)
return err;
- wait_for_completion_killable(&req->r_safe_completion);
+ err = wait_for_completion_killable(&req->r_safe_completion);
+ if (err)
+ return err;
+
return 0;
}
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 73da2648fa0f..1740047aef0f 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -979,14 +979,15 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc,
if (mds >= mdsc->max_sessions) {
int newmax = 1 << get_count_order(mds + 1);
struct ceph_mds_session **sa;
+ size_t ptr_size = sizeof(struct ceph_mds_session *);
doutc(cl, "realloc to %d\n", newmax);
- sa = kcalloc(newmax, sizeof(void *), GFP_NOFS);
+ sa = kcalloc(newmax, ptr_size, GFP_NOFS);
if (!sa)
goto fail_realloc;
if (mdsc->sessions) {
memcpy(sa, mdsc->sessions,
- mdsc->max_sessions * sizeof(void *));
+ mdsc->max_sessions * ptr_size);
kfree(mdsc->sessions);
}
mdsc->sessions = sa;
@@ -2532,6 +2533,7 @@ int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req,
struct ceph_mount_options *opt = req->r_mdsc->fsc->mount_options;
size_t size = sizeof(struct ceph_mds_reply_dir_entry);
unsigned int num_entries;
+ u64 bytes_count;
int order;
spin_lock(&ci->i_ceph_lock);
@@ -2540,7 +2542,11 @@ int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req,
num_entries = max(num_entries, 1U);
num_entries = min(num_entries, opt->max_readdir);
- order = get_order(size * num_entries);
+ bytes_count = (u64)size * num_entries;
+ if (unlikely(bytes_count > ULONG_MAX))
+ bytes_count = ULONG_MAX;
+
+ order = get_order((unsigned long)bytes_count);
while (order >= 0) {
rinfo->dir_entries = (void*)__get_free_pages(GFP_KERNEL |
__GFP_NOWARN |
@@ -2550,7 +2556,7 @@ int ceph_alloc_readdir_reply_buffer(struct ceph_mds_request *req,
break;
order--;
}
- if (!rinfo->dir_entries)
+ if (!rinfo->dir_entries || unlikely(order < 0))
return -ENOMEM;
num_entries = (PAGE_SIZE << order) / size;
@@ -5649,11 +5655,19 @@ static int ceph_mds_auth_match(struct ceph_mds_client *mdsc,
u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid);
u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid);
struct ceph_client *cl = mdsc->fsc->client;
+ const char *fs_name = mdsc->fsc->mount_options->mds_namespace;
const char *spath = mdsc->fsc->mount_options->server_path;
bool gid_matched = false;
u32 gid, tlen, len;
int i, j;
+ doutc(cl, "fsname check fs_name=%s match.fs_name=%s\n",
+ fs_name, auth->match.fs_name ? auth->match.fs_name : "");
+ if (auth->match.fs_name && strcmp(auth->match.fs_name, fs_name)) {
+ /* fsname mismatch, try next one */
+ return 0;
+ }
+
doutc(cl, "match.uid %lld\n", auth->match.uid);
if (auth->match.uid != MDS_AUTH_UID_ANY) {
if (auth->match.uid != caller_uid)
diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c
index 8109aba66e02..2c7b151a7c95 100644
--- a/fs/ceph/mdsmap.c
+++ b/fs/ceph/mdsmap.c
@@ -353,10 +353,22 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
__decode_and_drop_type(p, end, u8, bad_ext);
}
if (mdsmap_ev >= 8) {
+ u32 fsname_len;
/* enabled */
ceph_decode_8_safe(p, end, m->m_enabled, bad_ext);
/* fs_name */
- ceph_decode_skip_string(p, end, bad_ext);
+ ceph_decode_32_safe(p, end, fsname_len, bad_ext);
+
+ /* validate fsname against mds_namespace */
+ if (!namespace_equals(mdsc->fsc->mount_options, *p,
+ fsname_len)) {
+ pr_warn_client(cl, "fsname %*pE doesn't match mds_namespace %s\n",
+ (int)fsname_len, (char *)*p,
+ mdsc->fsc->mount_options->mds_namespace);
+ goto bad;
+ }
+ /* skip fsname after validation */
+ ceph_decode_skip_n(p, end, fsname_len, bad);
}
/* damaged */
if (mdsmap_ev >= 9) {
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index db6c2db68f96..ad0cf177e75a 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -246,20 +246,6 @@ static void canonicalize_path(char *path)
path[j] = '\0';
}
-/*
- * Check if the mds namespace in ceph_mount_options matches
- * the passed in namespace string. First time match (when
- * ->mds_namespace is NULL) is treated specially, since
- * ->mds_namespace needs to be initialized by the caller.
- */
-static int namespace_equals(struct ceph_mount_options *fsopt,
- const char *namespace, size_t len)
-{
- return !(fsopt->mds_namespace &&
- (strlen(fsopt->mds_namespace) != len ||
- strncmp(fsopt->mds_namespace, namespace, len)));
-}
-
static int ceph_parse_old_source(const char *dev_name, const char *dev_name_end,
struct fs_context *fc)
{
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 25d8bacbcf44..a1f781c46b41 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -104,6 +104,20 @@ struct ceph_mount_options {
struct fscrypt_dummy_policy dummy_enc_policy;
};
+/*
+ * Check if the mds namespace in ceph_mount_options matches
+ * the passed in namespace string. First time match (when
+ * ->mds_namespace is NULL) is treated specially, since
+ * ->mds_namespace needs to be initialized by the caller.
+ */
+static inline int namespace_equals(struct ceph_mount_options *fsopt,
+ const char *namespace, size_t len)
+{
+ return !(fsopt->mds_namespace &&
+ (strlen(fsopt->mds_namespace) != len ||
+ strncmp(fsopt->mds_namespace, namespace, len)));
+}
+
/* mount state */
enum {
CEPH_MOUNT_MOUNTING,
@@ -639,7 +653,8 @@ static inline struct inode *ceph_find_inode(struct super_block *sb,
#define CEPH_I_FLUSH_SNAPS (1 << 8) /* need flush snapss */
#define CEPH_I_ERROR_WRITE (1 << 9) /* have seen write errors */
#define CEPH_I_ERROR_FILELOCK (1 << 10) /* have seen file lock errors */
-#define CEPH_I_ODIRECT (1 << 11) /* inode in direct I/O mode */
+#define CEPH_I_ODIRECT_BIT (11) /* inode in direct I/O mode */
+#define CEPH_I_ODIRECT (1 << CEPH_I_ODIRECT_BIT)
#define CEPH_ASYNC_CREATE_BIT (12) /* async create in flight for this */
#define CEPH_I_ASYNC_CREATE (1 << CEPH_ASYNC_CREATE_BIT)
#define CEPH_I_SHUTDOWN (1 << 13) /* inode is no longer usable */
diff --git a/fs/hpfs/anode.c b/fs/hpfs/anode.c
index c14c9a035ee0..a4f5321eafae 100644
--- a/fs/hpfs/anode.c
+++ b/fs/hpfs/anode.c
@@ -27,7 +27,7 @@ secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode,
a = le32_to_cpu(btree->u.internal[i].down);
brelse(bh);
if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
- btree = &anode->btree;
+ btree = GET_BTREE_PTR(&anode->btree);
goto go_down;
}
hpfs_error(s, "sector %08x not found in internal anode %08x", sec, a);
@@ -69,12 +69,13 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
int n;
unsigned fs;
int c1, c2 = 0;
+
if (fnod) {
if (!(fnode = hpfs_map_fnode(s, node, &bh))) return -1;
- btree = &fnode->btree;
+ btree = GET_BTREE_PTR(&fnode->btree);
} else {
if (!(anode = hpfs_map_anode(s, node, &bh))) return -1;
- btree = &anode->btree;
+ btree = GET_BTREE_PTR(&anode->btree);
}
a = node;
go_down:
@@ -91,7 +92,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_add_sector_to_btree #1")) return -1;
if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
- btree = &anode->btree;
+ btree = GET_BTREE_PTR(&anode->btree);
goto go_down;
}
if (n >= 0) {
@@ -151,7 +152,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
}
brelse(bh);
bh = bh1;
- btree = &anode->btree;
+ btree = GET_BTREE_PTR(&anode->btree);
}
btree->n_free_nodes--; n = btree->n_used_nodes++;
le16_add_cpu(&btree->first_free, 12);
@@ -168,10 +169,10 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
if (hpfs_stop_cycles(s, up, &c1, &c2, "hpfs_add_sector_to_btree #2")) return -1;
if (up != node || !fnod) {
if (!(anode = hpfs_map_anode(s, up, &bh))) return -1;
- btree = &anode->btree;
+ btree = GET_BTREE_PTR(&anode->btree);
} else {
if (!(fnode = hpfs_map_fnode(s, up, &bh))) return -1;
- btree = &fnode->btree;
+ btree = GET_BTREE_PTR(&fnode->btree);
}
if (btree->n_free_nodes) {
btree->n_free_nodes--; n = btree->n_used_nodes++;
@@ -206,8 +207,8 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
anode->btree.n_used_nodes = 1;
anode->btree.n_free_nodes = 59;
anode->btree.first_free = cpu_to_le16(16);
- anode->btree.u.internal[0].down = cpu_to_le32(a);
- anode->btree.u.internal[0].file_secno = cpu_to_le32(-1);
+ GET_BTREE_PTR(&anode->btree)->u.internal[0].down = cpu_to_le32(a);
+ GET_BTREE_PTR(&anode->btree)->u.internal[0].file_secno = cpu_to_le32(-1);
mark_buffer_dirty(bh);
brelse(bh);
if ((anode = hpfs_map_anode(s, a, &bh))) {
@@ -229,20 +230,20 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi
brelse(bh2);
return -1;
}
- btree = &anode->btree;
+ btree = GET_BTREE_PTR(&anode->btree);
} else {
if (!(fnode = hpfs_map_fnode(s, node, &bh))) {
brelse(bh2);
return -1;
}
- btree = &fnode->btree;
+ btree = GET_BTREE_PTR(&fnode->btree);
}
ranode->up = cpu_to_le32(node);
memcpy(&ranode->btree, btree, le16_to_cpu(btree->first_free));
if (fnod)
ranode->btree.flags |= BP_fnode_parent;
- ranode->btree.n_free_nodes = (bp_internal(&ranode->btree) ? 60 : 40) - ranode->btree.n_used_nodes;
- if (bp_internal(&ranode->btree)) for (n = 0; n < ranode->btree.n_used_nodes; n++) {
+ GET_BTREE_PTR(&ranode->btree)->n_free_nodes = (bp_internal(GET_BTREE_PTR(&ranode->btree)) ? 60 : 40) - GET_BTREE_PTR(&ranode->btree)->n_used_nodes;
+ if (bp_internal(GET_BTREE_PTR(&ranode->btree))) for (n = 0; n < GET_BTREE_PTR(&ranode->btree)->n_used_nodes; n++) {
struct anode *unode;
if ((unode = hpfs_map_anode(s, le32_to_cpu(ranode->u.internal[n].down), &bh1))) {
unode->up = cpu_to_le32(ra);
@@ -291,7 +292,7 @@ void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree)
if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1"))
return;
if (!(anode = hpfs_map_anode(s, ano, &bh))) return;
- btree1 = &anode->btree;
+ btree1 = GET_BTREE_PTR(&anode->btree);
level++;
pos = 0;
}
@@ -307,7 +308,7 @@ void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree)
ano = le32_to_cpu(anode->up);
if (--level) {
if (!(anode = hpfs_map_anode(s, ano, &bh))) return;
- btree1 = &anode->btree;
+ btree1 = GET_BTREE_PTR(&anode->btree);
} else btree1 = btree;
for (i = 0; i < btree1->n_used_nodes; i++) {
if (le32_to_cpu(btree1->u.internal[i].down) == oano) {
@@ -332,7 +333,7 @@ static secno anode_lookup(struct super_block *s, anode_secno a, unsigned sec)
struct anode *anode;
struct buffer_head *bh;
if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
- return hpfs_bplus_lookup(s, NULL, &anode->btree, sec, bh);
+ return hpfs_bplus_lookup(s, NULL, GET_BTREE_PTR(&anode->btree), sec, bh);
}
int hpfs_ea_read(struct super_block *s, secno a, int ano, unsigned pos,
@@ -388,7 +389,7 @@ void hpfs_ea_remove(struct super_block *s, secno a, int ano, unsigned len)
struct buffer_head *bh;
if (ano) {
if (!(anode = hpfs_map_anode(s, a, &bh))) return;
- hpfs_remove_btree(s, &anode->btree);
+ hpfs_remove_btree(s, GET_BTREE_PTR(&anode->btree));
brelse(bh);
hpfs_free_sectors(s, a, 1);
} else hpfs_free_sectors(s, a, (len + 511) >> 9);
@@ -407,10 +408,10 @@ void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs)
int c1, c2 = 0;
if (fno) {
if (!(fnode = hpfs_map_fnode(s, f, &bh))) return;
- btree = &fnode->btree;
+ btree = GET_BTREE_PTR(&fnode->btree);
} else {
if (!(anode = hpfs_map_anode(s, f, &bh))) return;
- btree = &anode->btree;
+ btree = GET_BTREE_PTR(&anode->btree);
}
if (!secs) {
hpfs_remove_btree(s, btree);
@@ -448,7 +449,7 @@ void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs)
if (hpfs_stop_cycles(s, node, &c1, &c2, "hpfs_truncate_btree"))
return;
if (!(anode = hpfs_map_anode(s, node, &bh))) return;
- btree = &anode->btree;
+ btree = GET_BTREE_PTR(&anode->btree);
}
nodes = btree->n_used_nodes + btree->n_free_nodes;
for (i = 0; i < btree->n_used_nodes; i++)
@@ -485,7 +486,7 @@ void hpfs_remove_fnode(struct super_block *s, fnode_secno fno)
struct extended_attribute *ea;
struct extended_attribute *ea_end;
if (!(fnode = hpfs_map_fnode(s, fno, &bh))) return;
- if (!fnode_is_dir(fnode)) hpfs_remove_btree(s, &fnode->btree);
+ if (!fnode_is_dir(fnode)) hpfs_remove_btree(s, GET_BTREE_PTR(&fnode->btree));
else hpfs_remove_dtree(s, le32_to_cpu(fnode->u.external[0].disk_secno));
ea_end = fnode_end_ea(fnode);
for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
diff --git a/fs/hpfs/ea.c b/fs/hpfs/ea.c
index 102ba18e561f..2149d3ca530b 100644
--- a/fs/hpfs/ea.c
+++ b/fs/hpfs/ea.c
@@ -41,7 +41,7 @@ void hpfs_ea_ext_remove(struct super_block *s, secno a, int ano, unsigned len)
struct buffer_head *bh;
struct anode *anode;
if ((anode = hpfs_map_anode(s, a, &bh))) {
- hpfs_remove_btree(s, &anode->btree);
+ hpfs_remove_btree(s, GET_BTREE_PTR(&anode->btree));
brelse(bh);
hpfs_free_sectors(s, a, 1);
}
diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c
index 263b5bbe1849..29e876705369 100644
--- a/fs/hpfs/file.c
+++ b/fs/hpfs/file.c
@@ -51,7 +51,9 @@ static secno hpfs_bmap(struct inode *inode, unsigned file_secno, unsigned *n_sec
return hpfs_inode->i_disk_sec + n;
}
if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) return 0;
- disk_secno = hpfs_bplus_lookup(inode->i_sb, inode, &fnode->btree, file_secno, bh);
+ disk_secno = hpfs_bplus_lookup(inode->i_sb, inode,
+ GET_BTREE_PTR(&fnode->btree),
+ file_secno, bh);
if (disk_secno == -1) return 0;
if (hpfs_chk_sectors(inode->i_sb, disk_secno, 1, "bmap")) return 0;
n = file_secno - hpfs_inode->i_file_sec;
diff --git a/fs/hpfs/hpfs.h b/fs/hpfs/hpfs.h
index 281dec8f636b..353f73c914d9 100644
--- a/fs/hpfs/hpfs.h
+++ b/fs/hpfs/hpfs.h
@@ -394,27 +394,45 @@ enum {
BP_binary_search = 0x40,
BP_internal = 0x80
};
+
+/**
+ * GET_BTREE_PTR() - Get a pointer to struct bplus_header
+ *
+ * Wrapper around container_of() to retrieve a pointer to struct
+ * bplus_header from a pointer to struct bplus_header_fixed.
+ *
+ * @ptr: Pointer to struct bplus_header_fixed.
+ *
+ */
+#define GET_BTREE_PTR(ptr) \
+ container_of(ptr, struct bplus_header, __hdr)
+
struct bplus_header
{
- u8 flags; /* bit 0 - high bit of first free entry offset
+ /* New members MUST be added within the struct_group() macro below. */
+ struct_group_tagged(bplus_header_fixed, __hdr,
+ u8 flags; /* bit 0 - high bit of first free entry offset
bit 5 - we're pointed to by an fnode,
the data btree or some ea or the
main ea bootage pointer ea_secno
bit 6 - suggest binary search (unused)
bit 7 - 1 -> (internal) tree of anodes
0 -> (leaf) list of extents */
- u8 fill[3];
- u8 n_free_nodes; /* free nodes in following array */
- u8 n_used_nodes; /* used nodes in following array */
- __le16 first_free; /* offset from start of header to
+ u8 fill[3];
+ u8 n_free_nodes; /* free nodes in following array */
+ u8 n_used_nodes; /* used nodes in following array */
+ __le16 first_free; /* offset from start of header to
first free node in array */
- union {
- /* (internal) 2-word entries giving subtree pointers */
- DECLARE_FLEX_ARRAY(struct bplus_internal_node, internal);
- /* (external) 3-word entries giving sector runs */
- DECLARE_FLEX_ARRAY(struct bplus_leaf_node, external);
- } u;
+ );
+ union {
+ /* (internal) 2-word entries giving subtree pointers */
+ DECLARE_FLEX_ARRAY(struct bplus_internal_node, internal);
+ /* (external) 3-word entries giving sector runs */
+ DECLARE_FLEX_ARRAY(struct bplus_leaf_node, external);
+ } u;
};
+static_assert(offsetof(struct bplus_header, u.internal) == sizeof(struct bplus_header_fixed),
+ "struct member likely outside of struct_group_tagged()");
static inline bool bp_internal(struct bplus_header *bp)
{
@@ -453,7 +471,7 @@ struct fnode
__le16 flags; /* bit 1 set -> ea_secno is an anode */
/* bit 8 set -> directory. first & only extent
points to dnode. */
- struct bplus_header btree; /* b+ tree, 8 extents or 12 subtrees */
+ struct bplus_header_fixed btree; /* b+ tree, 8 extents or 12 subtrees */
union {
struct bplus_leaf_node external[8];
struct bplus_internal_node internal[12];
@@ -495,7 +513,7 @@ struct anode
__le32 self; /* pointer to this anode */
__le32 up; /* parent anode or fnode */
- struct bplus_header btree; /* b+tree, 40 extents or 60 subtrees */
+ struct bplus_header_fixed btree; /* b+tree, 40 extents or 60 subtrees */
union {
struct bplus_leaf_node external[40];
struct bplus_internal_node internal[60];
diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c
index ecd9fccd1663..be73233502f8 100644
--- a/fs/hpfs/map.c
+++ b/fs/hpfs/map.c
@@ -178,14 +178,14 @@ struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_hea
}
if (!fnode_is_dir(fnode)) {
if ((unsigned)fnode->btree.n_used_nodes + (unsigned)fnode->btree.n_free_nodes !=
- (bp_internal(&fnode->btree) ? 12 : 8)) {
+ (bp_internal(GET_BTREE_PTR(&fnode->btree)) ? 12 : 8)) {
hpfs_error(s,
"bad number of nodes in fnode %08lx",
(unsigned long)ino);
goto bail;
}
if (le16_to_cpu(fnode->btree.first_free) !=
- 8 + fnode->btree.n_used_nodes * (bp_internal(&fnode->btree) ? 8 : 12)) {
+ 8 + fnode->btree.n_used_nodes * (bp_internal(GET_BTREE_PTR(&fnode->btree)) ? 8 : 12)) {
hpfs_error(s,
"bad first_free pointer in fnode %08lx",
(unsigned long)ino);
@@ -233,12 +233,12 @@ struct anode *hpfs_map_anode(struct super_block *s, anode_secno ano, struct buff
goto bail;
}
if ((unsigned)anode->btree.n_used_nodes + (unsigned)anode->btree.n_free_nodes !=
- (bp_internal(&anode->btree) ? 60 : 40)) {
+ (bp_internal(GET_BTREE_PTR(&anode->btree)) ? 60 : 40)) {
hpfs_error(s, "bad number of nodes in anode %08x", ano);
goto bail;
}
if (le16_to_cpu(anode->btree.first_free) !=
- 8 + anode->btree.n_used_nodes * (bp_internal(&anode->btree) ? 8 : 12)) {
+ 8 + anode->btree.n_used_nodes * (bp_internal(GET_BTREE_PTR(&anode->btree)) ? 8 : 12)) {
hpfs_error(s, "bad first_free pointer in anode %08x", ano);
goto bail;
}
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index e3cdc421dfba..353e13a615f5 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -52,8 +52,10 @@ static struct dentry *hpfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
dee.fnode = cpu_to_le32(fno);
dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(local_get_seconds(dir->i_sb));
result = new_inode(dir->i_sb);
- if (!result)
+ if (!result) {
+ err = -ENOMEM;
goto bail2;
+ }
hpfs_init_inode(result);
result->i_ino = fno;
hpfs_i(result)->i_parent_dir = dir->i_ino;
@@ -153,9 +155,10 @@ static int hpfs_create(struct mnt_idmap *idmap, struct inode *dir,
dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(local_get_seconds(dir->i_sb));
result = new_inode(dir->i_sb);
- if (!result)
+ if (!result) {
+ err = -ENOMEM;
goto bail1;
-
+ }
hpfs_init_inode(result);
result->i_ino = fno;
result->i_mode |= S_IFREG;
@@ -239,9 +242,10 @@ static int hpfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(local_get_seconds(dir->i_sb));
result = new_inode(dir->i_sb);
- if (!result)
+ if (!result) {
+ err = -ENOMEM;
goto bail1;
-
+ }
hpfs_init_inode(result);
result->i_ino = fno;
hpfs_i(result)->i_parent_dir = dir->i_ino;
@@ -314,8 +318,10 @@ static int hpfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
dee.creation_date = dee.write_date = dee.read_date = cpu_to_le32(local_get_seconds(dir->i_sb));
result = new_inode(dir->i_sb);
- if (!result)
+ if (!result) {
+ err = -ENOMEM;
goto bail1;
+ }
result->i_ino = fno;
hpfs_init_inode(result);
hpfs_i(result)->i_parent_dir = dir->i_ino;
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
index 42b779b4d87f..8ab85e7ac91e 100644
--- a/fs/hpfs/super.c
+++ b/fs/hpfs/super.c
@@ -404,15 +404,11 @@ static int hpfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
break;
case Opt_timeshift:
{
- int m = 1;
char *rhs = param->string;
int timeshift;
- if (*rhs == '-') m = -1;
- if (*rhs == '+' || *rhs == '-') rhs++;
- timeshift = simple_strtoul(rhs, &rhs, 0) * m;
- if (*rhs)
- return -EINVAL;
+ if (kstrtoint(rhs, 0, &timeshift))
+ return -EINVAL;
ctx->timeshift = timeshift;
break;
}
diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c
index 9802b2cc29bb..5d5d63efbd57 100644
--- a/fs/iomap/direct-io.c
+++ b/fs/iomap/direct-io.c
@@ -433,7 +433,8 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
bio->bi_private = dio;
bio->bi_end_io = iomap_dio_bio_end_io;
- ret = bio_iov_iter_get_bdev_pages(bio, dio->submit.iter, iomap->bdev);
+ ret = bio_iov_iter_get_pages(bio, dio->submit.iter,
+ bdev_logical_block_size(iomap->bdev) - 1);
if (unlikely(ret)) {
/*
* We have to stop part way through an IO. We must fall
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index b36f9f9340f0..b8ac7b7faf61 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -562,8 +562,8 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
/*
* Mark all the cfids as closed, and move them to the cfids->dying list.
- * They'll be cleaned up later by cfids_invalidation_worker. Take
- * a reference to each cfid during this process.
+ * 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) {
@@ -580,12 +580,11 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
} else
kref_get(&cfid->refcount);
}
- /*
- * Queue dropping of the dentries once locks have been dropped
- */
- if (!list_empty(&cfids->dying))
- queue_work(cfid_put_wq, &cfids->invalidation_work);
spin_unlock(&cfids->cfid_list_lock);
+
+ /* 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
@@ -715,25 +714,6 @@ static void free_cached_dir(struct cached_fid *cfid)
kfree(cfid);
}
-static void cfids_invalidation_worker(struct work_struct *work)
-{
- struct cached_fids *cfids = container_of(work, struct cached_fids,
- invalidation_work);
- struct cached_fid *cfid, *q;
- LIST_HEAD(entry);
-
- spin_lock(&cfids->cfid_list_lock);
- /* move cfids->dying to the local list */
- list_cut_before(&entry, &cfids->dying, &cfids->dying);
- spin_unlock(&cfids->cfid_list_lock);
-
- list_for_each_entry_safe(cfid, q, &entry, entry) {
- list_del(&cfid->entry);
- /* Drop the ref-count acquired in invalidate_all_cached_dirs */
- kref_put(&cfid->refcount, smb2_close_cached_fid);
- }
-}
-
static void cfids_laundromat_worker(struct work_struct *work)
{
struct cached_fids *cfids;
@@ -743,6 +723,9 @@ 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->last_access_time &&
time_after(jiffies, cfid->last_access_time + HZ * dir_cache_timeout)) {
@@ -796,7 +779,6 @@ struct cached_fids *init_cached_dirs(void)
INIT_LIST_HEAD(&cfids->entries);
INIT_LIST_HEAD(&cfids->dying);
- INIT_WORK(&cfids->invalidation_work, cfids_invalidation_worker);
INIT_DELAYED_WORK(&cfids->laundromat_work, cfids_laundromat_worker);
queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
dir_cache_timeout * HZ);
@@ -820,7 +802,6 @@ void free_cached_dirs(struct cached_fids *cfids)
return;
cancel_delayed_work_sync(&cfids->laundromat_work);
- cancel_work_sync(&cfids->invalidation_work);
spin_lock(&cfids->cfid_list_lock);
list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h
index 31339dc32719..1e383db7c337 100644
--- a/fs/smb/client/cached_dir.h
+++ b/fs/smb/client/cached_dir.h
@@ -62,7 +62,6 @@ struct cached_fids {
int num_entries;
struct list_head entries;
struct list_head dying;
- struct work_struct invalidation_work;
struct delayed_work laundromat_work;
/* aggregate accounting for all cached dirents under this tcon */
atomic_long_t total_dirents_entries;
diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c
index 43b86fa4d695..9891f55bac1e 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
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/cifsfs.c b/fs/smb/client/cifsfs.c
index 1775c2b7528f..05b1fa76e8cc 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -392,11 +392,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);
- if (server->ops->fallocate)
- return server->ops->fallocate(file, tcon, mode, off, len);
+ rc = file_modified(file);
+ if (rc)
+ goto out_unlock;
+
+ 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,
diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h
index 3ce7c614ccc0..e9534258d1ef 100644
--- a/fs/smb/client/cifsfs.h
+++ b/fs/smb/client/cifsfs.h
@@ -145,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 56
-#define CIFS_VERSION "2.56"
+#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 3ac254e123dc..8f6f567d7474 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -1566,6 +1566,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
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index d20766f664c4..2881efcbe09a 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -1163,7 +1163,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 */
}
}
@@ -1288,7 +1288,7 @@ 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);
diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c
index fc67a6441c96..da5597dbf5b9 100644
--- a/fs/smb/client/dir.c
+++ b/fs/smb/client/dir.c
@@ -200,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. */
@@ -678,7 +678,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
const char *full_path;
void *page;
int retry_count = 0;
- struct cached_fid *cfid = NULL;
+ struct dentry *de;
xid = get_xid();
@@ -690,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
@@ -708,15 +707,15 @@ 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");
/*
@@ -775,25 +774,27 @@ again:
}
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 inode *dir, const struct qstr *name,
struct dentry *direntry, unsigned int flags)
{
- struct inode *inode = NULL;
- struct cached_fid *cfid;
- 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 */
@@ -836,6 +837,7 @@ cifs_d_revalidate(struct inode *dir, const struct qstr *name,
} 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)) {
/*
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index a5ed742afa00..474dadeb1593 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -952,6 +952,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)
{
@@ -1004,6 +1064,12 @@ 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 */
if (OPEN_FMODE(file->f_flags) & FMODE_WRITE) {
rc = cifs_get_writable_path(tcon, full_path,
@@ -2685,13 +2751,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) {
@@ -2699,39 +2762,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;
}
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 8bb544be401e..239dd84a336f 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -3007,28 +3007,25 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
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
@@ -3039,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;
@@ -3069,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.
@@ -3090,15 +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;
+ inode->i_blocks = (512 - 1 + size) >> 9;
}
return rc;
@@ -3118,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);
@@ -3132,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);
@@ -3159,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 */
@@ -3206,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);
@@ -3221,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);
}
@@ -3264,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;
@@ -3285,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);
@@ -3311,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)
@@ -3459,6 +3469,13 @@ cifs_setattr(struct mnt_idmap *idmap, struct dentry *direntry,
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
+ /*
+ * 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/smb1ops.c b/fs/smb/client/smb1ops.c
index a02d41d1ce4a..ca8f3dd7ff63 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -652,13 +652,71 @@ static int cifs_query_path_info(const unsigned int xid,
#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));
+ }
+
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);
@@ -668,8 +726,8 @@ static int cifs_query_path_info(const unsigned int xid,
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 = sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
- SMB2_WSL_XATTR_DEV_SIZE;
+ 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. */
@@ -818,6 +876,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;
}
@@ -974,7 +1037,7 @@ 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,
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 0985db9f86e5..09e3fc81d7cb 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -676,7 +676,7 @@ finished:
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;
+ 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 */
@@ -1382,31 +1382,33 @@ 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;
}
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 058050f744c0..7c392cf5940b 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1803,140 +1803,226 @@ 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 *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 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;
- /* For now array only one chunk long, will make more flexible later */
- pcchunk->ChunkCount = cpu_to_le32(1);
- pcchunk->Reserved = 0;
- pcchunk->Reserved2 = 0;
+ 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++];
- tcon = tlink_tcon(trgtfile->tlink);
+ chunk->SourceOffset = cpu_to_le64(src_off);
+ chunk->TargetOffset = cpu_to_le64(dst_off);
- trace_smb3_copychunk_enter(xid, srcfile->fid.volatile_fid,
- trgtfile->fid.volatile_fid, tcon->tid,
- tcon->ses->Suid, src_off, dest_off, len);
+ chunk_bytes = umin(copy_bytes_left, tcon->max_bytes_chunk);
- 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));
+ chunk->Length = cpu_to_le32(chunk_bytes);
+ /* Buffer is zeroed, no need to set chunk->Reserved = 0 */
+
+ 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 = -EIO;
+ 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");
+ /* Check if server claimed to write more than we asked */
+ if (unlikely(!bytes_written || bytes_written > copy_bytes ||
+ !chunks_written || chunks_written > chunks)) {
+ cifs_tcon_dbg(VFS, "Copychunk invalid response: bytes written %u/%u, chunks written %u/%u\n",
+ bytes_written, copy_bytes, chunks_written, chunks);
rc = -EIO;
- goto cchunk_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;
+ 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
@@ -3281,7 +3367,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);
@@ -3299,6 +3384,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;
@@ -3327,7 +3413,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,
@@ -3351,7 +3436,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)) {
@@ -3365,6 +3449,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);
@@ -3399,7 +3484,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;
}
@@ -3663,8 +3747,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) {
@@ -3679,6 +3761,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);
@@ -3699,8 +3782,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;
}
@@ -3717,8 +3799,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;
@@ -3733,6 +3813,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);
@@ -3755,8 +3836,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;
}
@@ -4650,7 +4730,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) {
@@ -4763,10 +4843,10 @@ 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 -EIO;
+ 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");
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 42e2d4ea344d..b0739a2661bf 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -3277,7 +3277,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 */
}
diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h
index 3c09a58dfd07..101024f8f725 100644
--- a/fs/smb/client/smb2pdu.h
+++ b/fs/smb/client/smb2pdu.h
@@ -201,16 +201,20 @@ struct resume_key_req {
char Context[]; /* ignored, Windows sets to 4 bytes of zero */
} __packed;
+
+struct copychunk {
+ __le64 SourceOffset;
+ __le64 TargetOffset;
+ __le32 Length;
+ __le32 Reserved;
+} __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 ChunkCount;
__le32 Reserved;
- /* array will only be one chunk long for us */
- __le64 SourceOffset;
- __le64 TargetOffset;
- __le32 Length; /* how many bytes to copy */
- __u32 Reserved2;
+ struct copychunk Chunks[];
} __packed;
struct copychunk_ioctl_rsp {
diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
index bc0e92eb2b64..33f33013b392 100644
--- a/fs/smb/client/smb2transport.c
+++ b/fs/smb/client/smb2transport.c
@@ -240,11 +240,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);
diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
index fd650e2afc76..28e00c34df1c 100644
--- a/fs/smb/client/trace.h
+++ b/fs/smb/client/trace.h
@@ -266,7 +266,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,