summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-03-19 16:08:51 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-03-19 16:08:51 +0100
commit1c6d58b8a03b656bfe4c6930a7d6052782c0bc89 (patch)
tree70991c25d188f5b1cd6f5d84f9f352ec2fa2effa /fs
parent744943ac89cd209aec9414dd751c53528d5757e7 (diff)
parent4aea1dc4cad17cd146072e13b1fd404f32b8b3ef (diff)
Merge v6.18.19linux-rolling-lts
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/afs/addr_list.c8
-rw-r--r--fs/btrfs/extent_io.c1
-rw-r--r--fs/btrfs/inode.c19
-rw-r--r--fs/btrfs/ioctl.c24
-rw-r--r--fs/btrfs/space-info.c5
-rw-r--r--fs/btrfs/transaction.c16
-rw-r--r--fs/btrfs/uuid-tree.c38
-rw-r--r--fs/btrfs/uuid-tree.h2
-rw-r--r--fs/btrfs/volumes.c6
-rw-r--r--fs/ceph/addr.c1
-rw-r--r--fs/ceph/debugfs.c4
-rw-r--r--fs/ceph/dir.c17
-rw-r--r--fs/ceph/file.c4
-rw-r--r--fs/ceph/inode.c2
-rw-r--r--fs/ceph/mds_client.c3
-rw-r--r--fs/file_attr.c2
-rw-r--r--fs/iomap/ioend.c13
-rw-r--r--fs/nfs/nfs3proc.c7
-rw-r--r--fs/nfsd/nfsctl.c2
-rw-r--r--fs/smb/client/cifsencrypt.c3
-rw-r--r--fs/smb/client/cifsglob.h11
-rw-r--r--fs/smb/client/dir.c1
-rw-r--r--fs/smb/client/file.c18
-rw-r--r--fs/smb/client/fs_context.c2
-rw-r--r--fs/smb/client/smb2ops.c14
-rw-r--r--fs/smb/client/smb2pdu.c5
-rw-r--r--fs/smb/client/smb2transport.c4
-rw-r--r--fs/smb/server/Kconfig1
-rw-r--r--fs/smb/server/auth.c4
-rw-r--r--fs/smb/server/oplock.c35
-rw-r--r--fs/smb/server/oplock.h5
-rw-r--r--fs/smb/server/smb2pdu.c13
-rw-r--r--fs/xfs/libxfs/xfs_defer.c2
-rw-r--r--fs/xfs/xfs_bmap_item.c2
-rw-r--r--fs/xfs/xfs_dquot.c8
-rw-r--r--fs/xfs/xfs_log.c2
36 files changed, 234 insertions, 70 deletions
diff --git a/fs/afs/addr_list.c b/fs/afs/addr_list.c
index e941da5b6dd9..b1704de3d95f 100644
--- a/fs/afs/addr_list.c
+++ b/fs/afs/addr_list.c
@@ -298,8 +298,8 @@ int afs_merge_fs_addr4(struct afs_net *net, struct afs_addr_list *alist,
srx.transport.sin.sin_addr.s_addr = xdr;
peer = rxrpc_kernel_lookup_peer(net->socket, &srx, GFP_KERNEL);
- if (!peer)
- return -ENOMEM;
+ if (IS_ERR(peer))
+ return PTR_ERR(peer);
for (i = 0; i < alist->nr_ipv4; i++) {
if (peer == alist->addrs[i].peer) {
@@ -342,8 +342,8 @@ int afs_merge_fs_addr6(struct afs_net *net, struct afs_addr_list *alist,
memcpy(&srx.transport.sin6.sin6_addr, xdr, 16);
peer = rxrpc_kernel_lookup_peer(net->socket, &srx, GFP_KERNEL);
- if (!peer)
- return -ENOMEM;
+ if (IS_ERR(peer))
+ return PTR_ERR(peer);
for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
if (peer == alist->addrs[i].peer) {
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index c3524401ff03..4b2bd3cc3ed3 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -4478,6 +4478,7 @@ static int try_release_subpage_extent_buffer(struct folio *folio)
*/
if (!test_and_clear_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags)) {
spin_unlock(&eb->refs_lock);
+ rcu_read_lock();
break;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 2799b10592d5..7fe868a6a51b 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -6478,6 +6478,25 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
int ret;
bool xa_reserved = false;
+ if (!args->orphan && !args->subvol) {
+ /*
+ * Before anything else, check if we can add the name to the
+ * parent directory. We want to avoid a dir item overflow in
+ * case we have an existing dir item due to existing name
+ * hash collisions. We do this check here before we call
+ * btrfs_add_link() down below so that we can avoid a
+ * transaction abort (which could be exploited by malicious
+ * users).
+ *
+ * For subvolumes we already do this in btrfs_mksubvol().
+ */
+ ret = btrfs_check_dir_item_collision(BTRFS_I(dir)->root,
+ btrfs_ino(BTRFS_I(dir)),
+ name);
+ if (ret < 0)
+ return ret;
+ }
+
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 736a1b317070..5f0bac5cea7e 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3984,6 +3984,25 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file,
goto out;
}
+ received_uuid_changed = memcmp(root_item->received_uuid, sa->uuid,
+ BTRFS_UUID_SIZE);
+
+ /*
+ * Before we attempt to add the new received uuid, check if we have room
+ * for it in case there's already an item. If the size of the existing
+ * item plus this root's ID (u64) exceeds the maximum item size, we can
+ * return here without the need to abort a transaction. If we don't do
+ * this check, the btrfs_uuid_tree_add() call below would fail with
+ * -EOVERFLOW and result in a transaction abort. Malicious users could
+ * exploit this to turn the fs into RO mode.
+ */
+ if (received_uuid_changed && !btrfs_is_empty_uuid(sa->uuid)) {
+ ret = btrfs_uuid_tree_check_overflow(fs_info, sa->uuid,
+ BTRFS_UUID_KEY_RECEIVED_SUBVOL);
+ if (ret < 0)
+ goto out;
+ }
+
/*
* 1 - root item
* 2 - uuid items (received uuid + subvol uuid)
@@ -3999,8 +4018,6 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file,
sa->rtime.sec = ct.tv_sec;
sa->rtime.nsec = ct.tv_nsec;
- received_uuid_changed = memcmp(root_item->received_uuid, sa->uuid,
- BTRFS_UUID_SIZE);
if (received_uuid_changed &&
!btrfs_is_empty_uuid(root_item->received_uuid)) {
ret = btrfs_uuid_tree_remove(trans, root_item->received_uuid,
@@ -4022,7 +4039,8 @@ static long _btrfs_ioctl_set_received_subvol(struct file *file,
ret = btrfs_update_root(trans, fs_info->tree_root,
&root->root_key, &root->root_item);
- if (ret < 0) {
+ if (unlikely(ret < 0)) {
+ btrfs_abort_transaction(trans, ret);
btrfs_end_transaction(trans);
goto out;
}
diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c
index 6b64691034de..194f59020165 100644
--- a/fs/btrfs/space-info.c
+++ b/fs/btrfs/space-info.c
@@ -2171,8 +2171,11 @@ void btrfs_reclaim_sweep(const struct btrfs_fs_info *fs_info)
if (!btrfs_should_periodic_reclaim(space_info))
continue;
for (raid = 0; raid < BTRFS_NR_RAID_TYPES; raid++) {
- if (do_reclaim_sweep(space_info, raid))
+ if (do_reclaim_sweep(space_info, raid)) {
+ spin_lock(&space_info->lock);
btrfs_set_periodic_reclaim_ready(space_info, false);
+ spin_unlock(&space_info->lock);
+ }
}
}
}
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 089712b15d60..fc3953136d23 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -1890,6 +1890,22 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
ret = btrfs_uuid_tree_add(trans, new_root_item->received_uuid,
BTRFS_UUID_KEY_RECEIVED_SUBVOL,
objectid);
+ /*
+ * We are creating of lot of snapshots of the same root that was
+ * received (has a received UUID) and reached a leaf's limit for
+ * an item. We can safely ignore this and avoid a transaction
+ * abort. A deletion of this snapshot will still work since we
+ * ignore if an item with a BTRFS_UUID_KEY_RECEIVED_SUBVOL key
+ * is missing (see btrfs_delete_subvolume()). Send/receive will
+ * work too since it peeks the first root id from the existing
+ * item (it could peek any), and in case it's missing it
+ * falls back to search by BTRFS_UUID_KEY_SUBVOL keys.
+ * Creation of a snapshot does not require CAP_SYS_ADMIN, so
+ * we don't want users triggering transaction aborts, either
+ * intentionally or not.
+ */
+ if (ret == -EOVERFLOW)
+ ret = 0;
if (unlikely(ret && ret != -EEXIST)) {
btrfs_abort_transaction(trans, ret);
goto fail;
diff --git a/fs/btrfs/uuid-tree.c b/fs/btrfs/uuid-tree.c
index 17b5e81123a1..146d78fac8f8 100644
--- a/fs/btrfs/uuid-tree.c
+++ b/fs/btrfs/uuid-tree.c
@@ -227,6 +227,44 @@ out:
return ret;
}
+/*
+ * Check if we can add one root ID to a UUID key.
+ * If the key does not yet exists, we can, otherwise only if extended item does
+ * not exceeds the maximum item size permitted by the leaf size.
+ *
+ * Returns 0 on success, negative value on error.
+ */
+int btrfs_uuid_tree_check_overflow(struct btrfs_fs_info *fs_info,
+ const u8 *uuid, u8 type)
+{
+ BTRFS_PATH_AUTO_FREE(path);
+ int ret;
+ u32 item_size;
+ struct btrfs_key key;
+
+ if (WARN_ON_ONCE(!fs_info->uuid_root))
+ return -EINVAL;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ btrfs_uuid_to_key(uuid, type, &key);
+ ret = btrfs_search_slot(NULL, fs_info->uuid_root, &key, path, 0, 0);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ return 0;
+
+ item_size = btrfs_item_size(path->nodes[0], path->slots[0]);
+
+ if (sizeof(struct btrfs_item) + item_size + sizeof(u64) >
+ BTRFS_LEAF_DATA_SIZE(fs_info))
+ return -EOVERFLOW;
+
+ return 0;
+}
+
static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type,
u64 subid)
{
diff --git a/fs/btrfs/uuid-tree.h b/fs/btrfs/uuid-tree.h
index c60ad20325cc..02b235a3653f 100644
--- a/fs/btrfs/uuid-tree.h
+++ b/fs/btrfs/uuid-tree.h
@@ -12,6 +12,8 @@ int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, const u8 *uuid, u8 typ
u64 subid);
int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, const u8 *uuid, u8 type,
u64 subid);
+int btrfs_uuid_tree_check_overflow(struct btrfs_fs_info *fs_info,
+ const u8 *uuid, u8 type);
int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info);
int btrfs_create_uuid_tree(struct btrfs_fs_info *fs_info);
int btrfs_uuid_scan_kthread(void *data);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 4a1dc4720a0b..3fe3a6c7da4e 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -6759,8 +6759,10 @@ int btrfs_map_block(struct btrfs_fs_info *fs_info, enum btrfs_map_op op,
return PTR_ERR(map);
num_copies = btrfs_chunk_map_num_copies(map);
- if (io_geom.mirror_num > num_copies)
- return -EINVAL;
+ if (io_geom.mirror_num > num_copies) {
+ ret = -EINVAL;
+ goto out;
+ }
map_offset = logical - map->start;
io_geom.raid56_full_stripe_start = (u64)-1;
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 261f8996abc0..390f122feeaa 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -1334,7 +1334,6 @@ int ceph_process_folio_batch(struct address_space *mapping,
} else if (rc == -E2BIG) {
rc = 0;
folio_unlock(folio);
- ceph_wbc->fbatch.folios[i] = NULL;
break;
}
diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c
index f3fe786b4143..7dc307790240 100644
--- a/fs/ceph/debugfs.c
+++ b/fs/ceph/debugfs.c
@@ -79,7 +79,7 @@ static int mdsc_show(struct seq_file *s, void *p)
if (req->r_inode) {
seq_printf(s, " #%llx", ceph_ino(req->r_inode));
} else if (req->r_dentry) {
- struct ceph_path_info path_info;
+ struct ceph_path_info path_info = {0};
path = ceph_mdsc_build_path(mdsc, req->r_dentry, &path_info, 0);
if (IS_ERR(path))
path = NULL;
@@ -98,7 +98,7 @@ static int mdsc_show(struct seq_file *s, void *p)
}
if (req->r_old_dentry) {
- struct ceph_path_info path_info;
+ struct ceph_path_info path_info = {0};
path = ceph_mdsc_build_path(mdsc, req->r_old_dentry, &path_info, 0);
if (IS_ERR(path))
path = NULL;
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index d18c0eaef9b7..45e5edecc0cb 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1338,6 +1338,7 @@ static int ceph_unlink(struct inode *dir, struct dentry *dentry)
struct ceph_client *cl = fsc->client;
struct ceph_mds_client *mdsc = fsc->mdsc;
struct inode *inode = d_inode(dentry);
+ struct ceph_inode_info *ci = ceph_inode(inode);
struct ceph_mds_request *req;
bool try_async = ceph_test_mount_opt(fsc, ASYNC_DIROPS);
struct dentry *dn;
@@ -1362,7 +1363,7 @@ static int ceph_unlink(struct inode *dir, struct dentry *dentry)
if (!dn) {
try_async = false;
} else {
- struct ceph_path_info path_info;
+ struct ceph_path_info path_info = {0};
path = ceph_mdsc_build_path(mdsc, dn, &path_info, 0);
if (IS_ERR(path)) {
try_async = false;
@@ -1423,7 +1424,19 @@ retry:
* We have enough caps, so we assume that the unlink
* will succeed. Fix up the target inode and dcache.
*/
- drop_nlink(inode);
+
+ /*
+ * Protect the i_nlink update with i_ceph_lock
+ * to precent racing against ceph_fill_inode()
+ * handling our completion on a worker thread
+ * and don't decrement if i_nlink has already
+ * been updated to zero by this completion.
+ */
+ spin_lock(&ci->i_ceph_lock);
+ if (inode->i_nlink > 0)
+ drop_nlink(inode);
+ spin_unlock(&ci->i_ceph_lock);
+
d_delete(dentry);
} else {
spin_lock(&fsc->async_unlink_conflict_lock);
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index f43a42909e7c..ceb5706fe366 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -397,7 +397,7 @@ int ceph_open(struct inode *inode, struct file *file)
if (!dentry) {
do_sync = true;
} else {
- struct ceph_path_info path_info;
+ struct ceph_path_info path_info = {0};
path = ceph_mdsc_build_path(mdsc, dentry, &path_info, 0);
if (IS_ERR(path)) {
do_sync = true;
@@ -807,7 +807,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
if (!dn) {
try_async = false;
} else {
- struct ceph_path_info path_info;
+ struct ceph_path_info path_info = {0};
path = ceph_mdsc_build_path(mdsc, dn, &path_info, 0);
if (IS_ERR(path)) {
try_async = false;
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index a6e260d9e420..b6c60d787692 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -2562,7 +2562,7 @@ int __ceph_setattr(struct mnt_idmap *idmap, struct inode *inode,
if (!dentry) {
do_sync = true;
} else {
- struct ceph_path_info path_info;
+ struct ceph_path_info path_info = {0};
path = ceph_mdsc_build_path(mdsc, dentry, &path_info, 0);
if (IS_ERR(path)) {
do_sync = true;
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index f3d146b86943..ba9f96efc8ee 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -2767,6 +2767,7 @@ retry:
if (ret < 0) {
dput(parent);
dput(cur);
+ __putname(path);
return ERR_PTR(ret);
}
@@ -2776,6 +2777,7 @@ retry:
if (len < 0) {
dput(parent);
dput(cur);
+ __putname(path);
return ERR_PTR(len);
}
}
@@ -2812,6 +2814,7 @@ retry:
* cannot ever succeed. Creating paths that long is
* possible with Ceph, but Linux cannot use them.
*/
+ __putname(path);
return ERR_PTR(-ENAMETOOLONG);
}
diff --git a/fs/file_attr.c b/fs/file_attr.c
index 1dcec88c0680..9d3e177ad7d1 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -379,7 +379,7 @@ SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename,
struct filename *name __free(putname) = NULL;
unsigned int lookup_flags = 0;
struct file_attr fattr;
- struct file_kattr fa;
+ struct file_kattr fa = { .flags_valid = true }; /* hint only */
int error;
BUILD_BUG_ON(sizeof(struct file_attr) < FILE_ATTR_SIZE_VER0);
diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c
index b49fa75eab26..fb26cfd9c4cc 100644
--- a/fs/iomap/ioend.c
+++ b/fs/iomap/ioend.c
@@ -163,17 +163,18 @@ ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio,
WARN_ON_ONCE(!folio->private && map_len < dirty_len);
switch (wpc->iomap.type) {
- case IOMAP_INLINE:
- WARN_ON_ONCE(1);
- return -EIO;
+ case IOMAP_UNWRITTEN:
+ ioend_flags |= IOMAP_IOEND_UNWRITTEN;
+ break;
+ case IOMAP_MAPPED:
+ break;
case IOMAP_HOLE:
return map_len;
default:
- break;
+ WARN_ON_ONCE(1);
+ return -EIO;
}
- if (wpc->iomap.type == IOMAP_UNWRITTEN)
- ioend_flags |= IOMAP_IOEND_UNWRITTEN;
if (wpc->iomap.flags & IOMAP_F_SHARED)
ioend_flags |= IOMAP_IOEND_SHARED;
if (folio_test_dropbehind(folio))
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index a4cb67573aa7..993f62636a77 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -392,8 +392,13 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
if (status != 0)
goto out_release_acls;
- if (d_alias)
+ if (d_alias) {
+ if (d_is_dir(d_alias)) {
+ status = -EISDIR;
+ goto out_dput;
+ }
dentry = d_alias;
+ }
/* When we created the file with exclusive semantics, make
* sure we set the attributes afterwards. */
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 5b00b7a863b9..51c5fe6c3cc6 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -1993,7 +1993,7 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
}
ret = svc_xprt_create_from_sa(serv, xcl_name, net, sa, 0,
- get_current_cred());
+ current_cred());
/* always save the latest error */
if (ret < 0)
err = ret;
diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c
index 801824825ecf..b9e20bcaa8b5 100644
--- a/fs/smb/client/cifsencrypt.c
+++ b/fs/smb/client/cifsencrypt.c
@@ -26,6 +26,7 @@
#include <crypto/arc4.h>
#include <crypto/md5.h>
#include <crypto/sha2.h>
+#include <crypto/utils.h>
static int cifs_sig_update(struct cifs_calc_sig_ctx *ctx,
const u8 *data, size_t len)
@@ -277,7 +278,7 @@ int cifs_verify_signature(struct smb_rqst *rqst,
/* cifs_dump_mem("what we think it should be: ",
what_we_think_sig_should_be, 16); */
- if (memcmp(server_response_sig, what_we_think_sig_should_be, 8))
+ if (crypto_memneq(server_response_sig, what_we_think_sig_should_be, 8))
return -EACCES;
else
return 0;
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 203e2aaa3c25..b82663f609ed 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -20,6 +20,7 @@
#include <linux/utsname.h>
#include <linux/sched/mm.h>
#include <linux/netfs.h>
+#include <linux/fcntl.h>
#include "cifs_fs_sb.h"
#include "cifsacl.h"
#include <crypto/internal/hash.h>
@@ -2396,4 +2397,14 @@ static inline void mid_execute_callback(struct mid_q_entry *mid)
(le32_to_cpu((tcon)->fsAttrInfo.Attributes) & \
FILE_SUPPORTS_REPARSE_POINTS))
+static inline int cifs_open_create_options(unsigned int oflags, int opts)
+{
+ /* O_SYNC also has bit for O_DSYNC so following check picks up either */
+ if (oflags & O_SYNC)
+ opts |= CREATE_WRITE_THROUGH;
+ if (oflags & O_DIRECT)
+ opts |= CREATE_NO_BUFFER;
+ return opts;
+}
+
#endif /* _CIFS_GLOB_H */
diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c
index da5597dbf5b9..50c7c6ec068f 100644
--- a/fs/smb/client/dir.c
+++ b/fs/smb/client/dir.c
@@ -307,6 +307,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
goto out;
}
+ create_options |= cifs_open_create_options(oflags, create_options);
/*
* if we're not using unix extensions, see if we need to set
* ATTR_READONLY on the create call
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index 474dadeb1593..f9d16a72cdba 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -584,15 +584,8 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_
*********************************************************************/
disposition = cifs_get_disposition(f_flags);
-
/* BB pass O_SYNC flag through on file attributes .. BB */
-
- /* O_SYNC also has bit for O_DSYNC so following check picks up either */
- if (f_flags & O_SYNC)
- create_options |= CREATE_WRITE_THROUGH;
-
- if (f_flags & O_DIRECT)
- create_options |= CREATE_NO_BUFFER;
+ create_options |= cifs_open_create_options(f_flags, create_options);
retry_open:
oparms = (struct cifs_open_parms) {
@@ -1318,13 +1311,8 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
rdwr_for_fscache = 1;
desired_access = cifs_convert_flags(cfile->f_flags, rdwr_for_fscache);
-
- /* O_SYNC also has bit for O_DSYNC so following check picks up either */
- if (cfile->f_flags & O_SYNC)
- create_options |= CREATE_WRITE_THROUGH;
-
- if (cfile->f_flags & O_DIRECT)
- create_options |= CREATE_NO_BUFFER;
+ create_options |= cifs_open_create_options(cfile->f_flags,
+ create_options);
if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &cfile->fid);
diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index d8bd3cdc535d..be82acacc41d 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -1920,7 +1920,7 @@ int smb3_init_fs_context(struct fs_context *fc)
ctx->backupuid_specified = false; /* no backup intent for a user */
ctx->backupgid_specified = false; /* no backup intent for a group */
- ctx->retrans = 1;
+ ctx->retrans = 0;
ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT;
ctx->symlink_type = CIFS_SYMLINK_TYPE_DEFAULT;
ctx->nonativesocket = 0;
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 9c22daff2497..502b5cb05bc3 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -628,6 +628,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
struct iface_info_ipv6 *p6;
struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL;
struct cifs_server_iface tmp_iface;
+ __be16 port;
ssize_t bytes_left;
size_t next = 0;
int nb_iface = 0;
@@ -662,6 +663,15 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
goto out;
}
+ spin_lock(&ses->server->srv_lock);
+ if (ses->server->dstaddr.ss_family == AF_INET)
+ port = ((struct sockaddr_in *)&ses->server->dstaddr)->sin_port;
+ else if (ses->server->dstaddr.ss_family == AF_INET6)
+ port = ((struct sockaddr_in6 *)&ses->server->dstaddr)->sin6_port;
+ else
+ port = cpu_to_be16(CIFS_PORT);
+ spin_unlock(&ses->server->srv_lock);
+
while (bytes_left >= (ssize_t)sizeof(*p)) {
memset(&tmp_iface, 0, sizeof(tmp_iface));
/* default to 1Gbps when link speed is unset */
@@ -682,7 +692,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
memcpy(&addr4->sin_addr, &p4->IPv4Address, 4);
/* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */
- addr4->sin_port = cpu_to_be16(CIFS_PORT);
+ addr4->sin_port = port;
cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__,
&addr4->sin_addr);
@@ -696,7 +706,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
/* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */
addr6->sin6_flowinfo = 0;
addr6->sin6_scope_id = 0;
- addr6->sin6_port = cpu_to_be16(CIFS_PORT);
+ addr6->sin6_port = port;
cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__,
&addr6->sin6_addr);
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 309e2fcabc08..3e49c5b396b5 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -5172,7 +5172,10 @@ replay_again:
memset(&rqst, 0, sizeof(struct smb_rqst));
rqst.rq_iov = iov;
- rqst.rq_nvec = n_vec + 1;
+ /* iov[0] is the SMB header; move payload to rq_iter for encryption safety */
+ rqst.rq_nvec = 1;
+ iov_iter_kvec(&rqst.rq_iter, ITER_SOURCE, &iov[1], n_vec,
+ io_parms->length);
if (retries)
smb2_set_replay(server, &rqst);
diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
index 6a9b80385b86..211305d43f8d 100644
--- a/fs/smb/client/smb2transport.c
+++ b/fs/smb/client/smb2transport.c
@@ -20,6 +20,7 @@
#include <linux/highmem.h>
#include <crypto/aead.h>
#include <crypto/sha2.h>
+#include <crypto/utils.h>
#include "cifsglob.h"
#include "cifsproto.h"
#include "smb2proto.h"
@@ -617,7 +618,8 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
if (rc)
return rc;
- if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE)) {
+ if (crypto_memneq(server_response_sig, shdr->Signature,
+ SMB2_SIGNATURE_SIZE)) {
cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n",
shdr->Command, shdr->MessageId);
return -EACCES;
diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig
index 098cac98d31e..6200c71298f6 100644
--- a/fs/smb/server/Kconfig
+++ b/fs/smb/server/Kconfig
@@ -13,6 +13,7 @@ config SMB_SERVER
select CRYPTO_LIB_ARC4
select CRYPTO_LIB_DES
select CRYPTO_LIB_SHA256
+ select CRYPTO_LIB_UTILS
select CRYPTO_SHA256
select CRYPTO_CMAC
select CRYPTO_SHA512
diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c
index b4020bb55a26..f92b2f3dc6de 100644
--- a/fs/smb/server/auth.c
+++ b/fs/smb/server/auth.c
@@ -13,6 +13,7 @@
#include <linux/xattr.h>
#include <crypto/hash.h>
#include <crypto/aead.h>
+#include <crypto/utils.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
@@ -283,7 +284,8 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
goto out;
}
- if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0)
+ if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp,
+ CIFS_HMAC_MD5_HASH_SIZE))
rc = -EINVAL;
out:
if (ctx)
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index a04d5702820d..228166c47d8c 100644
--- a/fs/smb/server/oplock.c
+++ b/fs/smb/server/oplock.c
@@ -120,7 +120,7 @@ static void free_lease(struct oplock_info *opinfo)
kfree(lease);
}
-static void free_opinfo(struct oplock_info *opinfo)
+static void __free_opinfo(struct oplock_info *opinfo)
{
if (opinfo->is_lease)
free_lease(opinfo);
@@ -129,6 +129,18 @@ static void free_opinfo(struct oplock_info *opinfo)
kfree(opinfo);
}
+static void free_opinfo_rcu(struct rcu_head *rcu)
+{
+ struct oplock_info *opinfo = container_of(rcu, struct oplock_info, rcu);
+
+ __free_opinfo(opinfo);
+}
+
+static void free_opinfo(struct oplock_info *opinfo)
+{
+ call_rcu(&opinfo->rcu, free_opinfo_rcu);
+}
+
struct oplock_info *opinfo_get(struct ksmbd_file *fp)
{
struct oplock_info *opinfo;
@@ -176,9 +188,9 @@ void opinfo_put(struct oplock_info *opinfo)
free_opinfo(opinfo);
}
-static void opinfo_add(struct oplock_info *opinfo)
+static void opinfo_add(struct oplock_info *opinfo, struct ksmbd_file *fp)
{
- struct ksmbd_inode *ci = opinfo->o_fp->f_ci;
+ struct ksmbd_inode *ci = fp->f_ci;
down_write(&ci->m_lock);
list_add(&opinfo->op_entry, &ci->m_op_list);
@@ -1123,10 +1135,12 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
rcu_read_lock();
opinfo = rcu_dereference(fp->f_opinfo);
- rcu_read_unlock();
- if (!opinfo || !opinfo->is_lease || opinfo->o_lease->version != 2)
+ if (!opinfo || !opinfo->is_lease || opinfo->o_lease->version != 2) {
+ rcu_read_unlock();
return;
+ }
+ rcu_read_unlock();
p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
if (!p_ci)
@@ -1277,20 +1291,21 @@ set_lev:
set_oplock_level(opinfo, req_op_level, lctx);
out:
- rcu_assign_pointer(fp->f_opinfo, opinfo);
- opinfo->o_fp = fp;
-
opinfo_count_inc(fp);
- opinfo_add(opinfo);
+ opinfo_add(opinfo, fp);
+
if (opinfo->is_lease) {
err = add_lease_global_list(opinfo);
if (err)
goto err_out;
}
+ rcu_assign_pointer(fp->f_opinfo, opinfo);
+ opinfo->o_fp = fp;
+
return 0;
err_out:
- free_opinfo(opinfo);
+ __free_opinfo(opinfo);
return err;
}
diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h
index 9a56eaadd0dd..921e3199e4df 100644
--- a/fs/smb/server/oplock.h
+++ b/fs/smb/server/oplock.h
@@ -69,8 +69,9 @@ struct oplock_info {
struct lease *o_lease;
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 */
+ wait_queue_head_t oplock_q; /* Other server threads */
+ wait_queue_head_t oplock_brk; /* oplock breaking wait */
+ struct rcu_head rcu;
};
struct lease_break_info {
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index e52b9136abbf..b6915e2c636d 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -4,6 +4,7 @@
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*/
+#include <crypto/utils.h>
#include <linux/inetdevice.h>
#include <net/addrconf.h>
#include <linux/syscalls.h>
@@ -3020,13 +3021,14 @@ int smb2_open(struct ksmbd_work *work)
goto err_out2;
}
+ fp = dh_info.fp;
+
if (ksmbd_override_fsids(work)) {
rc = -ENOMEM;
ksmbd_put_durable_fd(dh_info.fp);
goto err_out2;
}
- fp = dh_info.fp;
file_info = FILE_OPENED;
rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat);
@@ -3624,10 +3626,8 @@ int smb2_open(struct ksmbd_work *work)
reconnected_fp:
rsp->StructureSize = cpu_to_le16(89);
- rcu_read_lock();
- opinfo = rcu_dereference(fp->f_opinfo);
+ opinfo = opinfo_get(fp);
rsp->OplockLevel = opinfo != NULL ? opinfo->level : 0;
- rcu_read_unlock();
rsp->Flags = 0;
rsp->CreateAction = cpu_to_le32(file_info);
rsp->CreationTime = cpu_to_le64(fp->create_time);
@@ -3668,6 +3668,7 @@ reconnected_fp:
next_ptr = &lease_ccontext->Next;
next_off = conn->vals->create_lease_size;
}
+ opinfo_put(opinfo);
if (maximal_access_ctxt) {
struct create_context *mxac_ccontext;
@@ -8881,7 +8882,7 @@ int smb2_check_sign_req(struct ksmbd_work *work)
signature))
return 0;
- if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) {
+ if (crypto_memneq(signature, signature_req, SMB2_SIGNATURE_SIZE)) {
pr_err("bad smb2 signature\n");
return 0;
}
@@ -8969,7 +8970,7 @@ int smb3_check_sign_req(struct ksmbd_work *work)
if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature))
return 0;
- if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) {
+ if (crypto_memneq(signature, signature_req, SMB2_SIGNATURE_SIZE)) {
pr_err("bad smb2 signature\n");
return 0;
}
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
index 5b377cbbb1f7..e8db2f6149e0 100644
--- a/fs/xfs/libxfs/xfs_defer.c
+++ b/fs/xfs/libxfs/xfs_defer.c
@@ -809,7 +809,7 @@ xfs_defer_can_append(
/* Paused items cannot absorb more work */
if (dfp->dfp_flags & XFS_DEFER_PAUSED)
- return NULL;
+ return false;
/* Already full? */
if (ops->max_items && dfp->dfp_count >= ops->max_items)
diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c
index 80f0c4bcc483..bcc89d65de7f 100644
--- a/fs/xfs/xfs_bmap_item.c
+++ b/fs/xfs/xfs_bmap_item.c
@@ -247,7 +247,7 @@ xfs_bmap_update_diff_items(
struct xfs_bmap_intent *ba = bi_entry(a);
struct xfs_bmap_intent *bb = bi_entry(b);
- return ba->bi_owner->i_ino - bb->bi_owner->i_ino;
+ return cmp_int(ba->bi_owner->i_ino, bb->bi_owner->i_ino);
}
/* Log bmap updates in the intent item. */
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index 0bd8022e47b4..92a8863bee36 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -1464,9 +1464,15 @@ xfs_qm_dqflush(
return 0;
out_abort:
+ /*
+ * Shut down the log before removing the dquot item from the AIL.
+ * Otherwise, the log tail may advance past this item's LSN while
+ * log writes are still in progress, making these unflushed changes
+ * unrecoverable on the next mount.
+ */
+ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
dqp->q_flags &= ~XFS_DQFLAG_DIRTY;
xfs_trans_ail_delete(lip, 0);
- xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
xfs_dqfunlock(dqp);
return error;
}
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 603e85c1ab4c..614b3c385178 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -1397,6 +1397,8 @@ xlog_alloc_log(
if (xfs_has_logv2(mp) && mp->m_sb.sb_logsunit > 1)
log->l_iclog_roundoff = mp->m_sb.sb_logsunit;
+ else if (mp->m_sb.sb_logsectsize > 0)
+ log->l_iclog_roundoff = mp->m_sb.sb_logsectsize;
else
log->l_iclog_roundoff = BBSIZE;