summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-07-26 12:22:57 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-07-26 12:22:57 -0700
commit4235cb24ec1e8e96843f3671ba4da2a6ccca2c7b (patch)
tree9572f57056ea3d1870ae1611a845c0f302d245be /fs
parent72841e8e8345ebf1005095149cc08eb6e2889a54 (diff)
parent749d7aa0377aae32af8c0a4ad43371e7bf830ab5 (diff)
Merge tag 'vfs-7.2-rc5.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull vfs fixes from Christian Brauner: - vfs: Preserve the ACL_DONT_CACHE state in forget_cached_acl(). ACL_DONT_CACHE is meant to be a permanent opt-out from ACL caching which FUSE relies on for servers that don't negotiate FUSE_POSIX_ACL. The helper replaced it with ACL_NOT_CACHED, silently re-enabling the cache, and as fuse doesn't invalidate the cache for such servers a properly timed get_acl() returned stale ACLs. Comes with a fuse selftest reproducing this. - pidfs: - Preserve PIDFD_THREAD when a thread pidfd is reopened via open_by_handle_at(). PIDFD_THREAD shares the O_EXCL bit which do_dentry_open() strips after the flags have been validated, so the reopened pidfd silently became a process pidfd. Comes with a selftest. - Add a pidfs_dentry_open() helper so the regular pidfd allocation path and the file handle path share the code that forces O_RDWR and reapplies the pidfd flags that do_dentry_open() strips. - Handle FS_IOC32_GETVERSION in the compat ioctl path. - Make pidfs_ino_lock static. - iomap: - Fix the block range calculation in ifs_clear_range_dirty() so a partial clear doesn't drop the dirty state of blocks the range only partially covers. - Support invalidating partial folios so a partial truncate or hole punch with blocksize < foliosize doesn't leave stale dirty bits behind. - Only set did_zero when iomap_zero_iter() actually zeroed something. - Guard ifs_set_range_dirty() and ifs_set_range_uptodate() against zero-length ranges where the unsigned last-block calculation underflows and bitmap_set() writes far beyond the ifs->state allocation. - Don't merge ioends with different io_private values as the merge could leak or corrupt the private data of the individual ioends. - exec: - Raise bprm->have_execfd only once the binfmt_misc interpreter has actually been opened. The flag was set as soon as a matching 'O' or 'C' entry was found. If the interpreter open failed with ENOEXEC the exec fell through to the next binary format with have_execfd raised but no executable staged and begin_new_exec() NULL derefed past the point of no return. - Fix an unsigned loop counter wrap in transfer_args_to_stack() on nommu. An overlong argument or environment string pushes bprm->p below PAGE_SIZE, the stop index becomes zero, and the loop never terminates, wrapping its counter and copying garbage from in front of the page array into the new process stack. - Make binfmt_elf_fdpic only honour the first PT_INTERP like binfmt_elf does. Each additional PT_INTERP overwrote the previous interpreter, leaking the name allocation and the interpreter file reference together with the write denial open_exec() took, leaving the file unwritable for as long as the system runs. - overlayfs: - Compare the full escaped xattr prefix including the trailing dot. An xattr like "trusted.overlay.overlayfoo" was misclassified as an escaped overlay xattr. - Check read access to the copy_file_range() source with the source's mounter credentials. - super: Thawing a filesystem whose block device was frozen with bdev_freeze() deadlocked. Dropping the last block layer freeze reference from under s_umount ends up in fs_bdev_thaw() which reacquires s_umount on the same task. Pin the superblock with an active reference instead and call bdev_thaw() without holding s_umount. - procfs: Return EACCES instead of success when the ptrace access check for namespace links fails. - afs: Use afs_dir_get_block() rather than afs_dir_find_block() for block 0 in afs_edit_dir_remove(), matching afs_edit_dir_add(). - Push the memcg gating of ->nr_cached_objects() down into the btrfs and shmem callbacks instead of skipping every callback during non-root memcg reclaim. The blanket check short-circuited XFS whose inode reclaim hook is intentionally driven from per-memcg contexts to free memcg-charged slab. - eventpoll: Pin files while checking reverse paths. Since struct file became SLAB_TYPESAFE_BY_RCU a concurrent close could free and recycle the file under the check which then took and dropped the f_lock of whatever live file now occupies that slot. * tag 'vfs-7.2-rc5.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (24 commits) super: fix emergency thaw deadlock on frozen block devices pidfs: make pidfs_ino_lock static eventpoll: pin files while checking reverse paths fs: push nr_cached_objects memcg gating into individual filesystems afs: Fix afs_edit_dir_remove() to get, not find, block 0 iomap: prevent ioend merge when io_private differs iomap: add comments for ifs_clear/set_range_dirty() iomap: fix out-of-bounds bitmap_set() with zero-length range iomap: fix incorrect did_zero setting in iomap_zero_iter() iomap: support invalidating partial folios iomap: correct the range of a partial dirty clear fs/super: fix emergency thaw double-unlock of s_umount pidfs: handle FS_IOC32_GETVERSION in compat ioctl ovl: check access to copy_file_range source with src mounter creds proc: Fix broken error paths for namespace links pidfs: add pidfs_dentry_open() helper selftests/pidfd: check PIDFD_THREAD survives open_by_handle_at() pidfs: preserve thread pidfds reopened by file handle ovl: fix trusted xattr escape prefix matching selftests/fuse: add ACL_DONT_CACHE regression test ...
Diffstat (limited to 'fs')
-rw-r--r--fs/afs/dir_edit.c2
-rw-r--r--fs/binfmt_elf_fdpic.c4
-rw-r--r--fs/binfmt_misc.c5
-rw-r--r--fs/btrfs/super.c10
-rw-r--r--fs/eventpoll.c18
-rw-r--r--fs/exec.c2
-rw-r--r--fs/iomap/buffered-io.c58
-rw-r--r--fs/iomap/ioend.c2
-rw-r--r--fs/overlayfs/file.c16
-rw-r--r--fs/overlayfs/xattrs.c2
-rw-r--r--fs/pidfs.c54
-rw-r--r--fs/posix_acl.c7
-rw-r--r--fs/proc/namespaces.c4
-rw-r--r--fs/super.c34
14 files changed, 164 insertions, 54 deletions
diff --git a/fs/afs/dir_edit.c b/fs/afs/dir_edit.c
index fd3aa9f97ce6..3ead36a07048 100644
--- a/fs/afs/dir_edit.c
+++ b/fs/afs/dir_edit.c
@@ -415,7 +415,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
if (!afs_dir_init_iter(&iter, name))
return;
- meta = afs_dir_find_block(&iter, 0);
+ meta = afs_dir_get_block(&iter, 0);
if (!meta)
return;
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 7e3108489c83..fe0b5c5ed2bc 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -231,6 +231,10 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm)
for (i = 0; i < exec_params.hdr.e_phnum; i++, phdr++) {
switch (phdr->p_type) {
case PT_INTERP:
+ /* elf ABI allows only one interpreter */
+ if (interpreter_name)
+ continue;
+
retval = -ENOMEM;
if (phdr->p_filesz > PATH_MAX)
goto error;
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 84349fcb93f1..5de615ca7a75 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -228,9 +228,6 @@ static int load_misc_binary(struct linux_binprm *bprm)
goto ret;
}
- if (fmt->flags & MISC_FMT_OPEN_BINARY)
- bprm->have_execfd = 1;
-
/* make argv[1] be the path to the binary */
retval = copy_string_kernel(bprm->interp, bprm);
if (retval < 0)
@@ -260,6 +257,8 @@ static int load_misc_binary(struct linux_binprm *bprm)
goto ret;
bprm->interpreter = interp_file;
+ if (fmt->flags & MISC_FMT_OPEN_BINARY)
+ bprm->have_execfd = 1;
if (fmt->flags & MISC_FMT_CREDENTIALS)
bprm->execfd_creds = 1;
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index a7d804219bec..cc4537435399 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -22,6 +22,7 @@
#include <linux/namei.h>
#include <linux/miscdevice.h>
#include <linux/magic.h>
+#include <linux/memcontrol.h>
#include <linux/slab.h>
#include <linux/ratelimit.h>
#include <linux/crc32c.h>
@@ -2434,6 +2435,15 @@ static long btrfs_nr_cached_objects(struct super_block *sb, struct shrink_contro
struct btrfs_fs_info *fs_info = btrfs_sb(sb);
const s64 nr = percpu_counter_read_positive(&fs_info->evictable_extent_maps);
+ /*
+ * The evictable extent map counter is filesystem-global and does not
+ * honour sc->memcg, so it is only meaningful on the global (kswapd or
+ * root direct reclaim) shrink path. Skip the per-memcg iterations of
+ * shrink_slab_memcg() to avoid queueing duplicate global work.
+ */
+ if (!mem_cgroup_shrink_is_root(sc))
+ return 0;
+
trace_btrfs_extent_map_shrinker_count(fs_info, nr);
return nr;
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 0e65c7431dfc..eed8cecd94e3 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -459,11 +459,14 @@ static struct kmem_cache *pwq_cache __ro_after_init;
* Wrapper anchor for file->f_ep when the watched file is not itself an
* eventpoll; for the epoll-watches-epoll case, file->f_ep points at
* &watched_ep->refs directly. The ->next field threads
- * ctx->tfile_check_list during one EPOLL_CTL_ADD path check.
+ * ctx->tfile_check_list during one EPOLL_CTL_ADD path check. The ->file
+ * field holds a reference to the associated file while the head is on
+ * the list.
*/
struct epitems_head {
struct hlist_head epitems;
struct epitems_head *next;
+ struct file *file;
};
static struct kmem_cache *ephead_cache __ro_after_init;
@@ -480,6 +483,16 @@ static void list_file(struct file *file, struct ep_ctl_ctx *ctx)
head = container_of(file->f_ep, struct epitems_head, epitems);
if (!head->next) {
+ /*
+ * The caller owns a reference to @file or holds the ep->mtx for the
+ * epitem that led here. The latter blocks eventpoll_release_file()
+ * before the file allocation can be freed and reused. A dying leaf
+ * can be skipped since removing links cannot increase the reverse
+ * path count.
+ */
+ if (!file_ref_get(&file->f_ref))
+ return;
+ head->file = file;
head->next = ctx->tfile_check_list;
ctx->tfile_check_list = head;
}
@@ -489,15 +502,18 @@ static void unlist_file(struct epitems_head *head)
{
struct epitems_head *to_free = head;
struct hlist_node *p = rcu_dereference(hlist_first_rcu(&head->epitems));
+ struct file *file = head->file;
if (p) {
struct epitem *epi= container_of(p, struct epitem, fllink);
spin_lock(&epi->ffd.file->f_lock);
if (!hlist_empty(&head->epitems))
to_free = NULL;
head->next = NULL;
+ head->file = NULL;
spin_unlock(&epi->ffd.file->f_lock);
}
free_ephead(to_free);
+ fput(file);
}
#ifdef CONFIG_SYSCTL
diff --git a/fs/exec.c b/fs/exec.c
index d5993cedc829..c7b8f2d6366c 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -740,7 +740,7 @@ int transfer_args_to_stack(struct linux_binprm *bprm,
stop = bprm->p >> PAGE_SHIFT;
sp = *sp_location;
- for (index = MAX_ARG_PAGES - 1; index >= stop; index--) {
+ for (index = MAX_ARG_PAGES; index-- > stop; ) {
unsigned int offset = index == stop ? bprm->p & ~PAGE_MASK : 0;
char *src = kmap_local_page(bprm->page[index]) + offset;
sp -= PAGE_SIZE - offset;
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 276720bc18dc..6d9a2efd4bee 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -68,11 +68,13 @@ static bool ifs_set_range_uptodate(struct folio *folio,
struct iomap_folio_state *ifs, size_t off, size_t len)
{
struct inode *inode = folio->mapping->host;
- unsigned int first_blk = off >> inode->i_blkbits;
- unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
- unsigned int nr_blks = last_blk - first_blk + 1;
+ unsigned int first_blk, last_blk;
- bitmap_set(ifs->state, first_blk, nr_blks);
+ if (len) {
+ first_blk = off >> inode->i_blkbits;
+ last_blk = (off + len - 1) >> inode->i_blkbits;
+ bitmap_set(ifs->state, first_blk, last_blk - first_blk + 1);
+ }
return ifs_is_fully_uptodate(folio, ifs);
}
@@ -172,18 +174,29 @@ static unsigned iomap_find_dirty_range(struct folio *folio, u64 *range_start,
return range_end - *range_start;
}
+/*
+ * Clear the per-block dirty bits for the range [@off, @off + @len) within a
+ * folio. The range is rounded inwards so that only blocks fully covered by
+ * the range are cleared. This is required for operations like folio
+ * invalidation, where we must ensure a block is fully clean before discarding
+ * it.
+ */
static void ifs_clear_range_dirty(struct folio *folio,
struct iomap_folio_state *ifs, size_t off, size_t len)
{
struct inode *inode = folio->mapping->host;
unsigned int blks_per_folio = i_blocks_per_folio(inode, folio);
- unsigned int first_blk = (off >> inode->i_blkbits);
- unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
- unsigned int nr_blks = last_blk - first_blk + 1;
+ unsigned int first_blk = round_up(off, i_blocksize(inode)) >>
+ inode->i_blkbits;
+ unsigned int last_blk = (off + len) >> inode->i_blkbits;
unsigned long flags;
+ if (first_blk >= last_blk)
+ return;
+
spin_lock_irqsave(&ifs->state_lock, flags);
- bitmap_clear(ifs->state, first_blk + blks_per_folio, nr_blks);
+ bitmap_clear(ifs->state, first_blk + blks_per_folio,
+ last_blk - first_blk);
spin_unlock_irqrestore(&ifs->state_lock, flags);
}
@@ -195,18 +208,29 @@ static void iomap_clear_range_dirty(struct folio *folio, size_t off, size_t len)
ifs_clear_range_dirty(folio, ifs, off, len);
}
+/*
+ * Set the per-block dirty bits for the range [@off, @off + @len) within a
+ * folio. The range is rounded outwards so that any block partially touched
+ * by the range is marked dirty. This ensures blocks containing even a
+ * single dirty byte will be included in subsequent writeback, preventing
+ * data loss when partial blocks are written.
+ */
static void ifs_set_range_dirty(struct folio *folio,
struct iomap_folio_state *ifs, size_t off, size_t len)
{
struct inode *inode = folio->mapping->host;
unsigned int blks_per_folio = i_blocks_per_folio(inode, folio);
- unsigned int first_blk = (off >> inode->i_blkbits);
- unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
- unsigned int nr_blks = last_blk - first_blk + 1;
+ unsigned int first_blk, last_blk;
unsigned long flags;
+ if (!len)
+ return;
+
+ first_blk = off >> inode->i_blkbits;
+ last_blk = (off + len - 1) >> inode->i_blkbits;
spin_lock_irqsave(&ifs->state_lock, flags);
- bitmap_set(ifs->state, first_blk + blks_per_folio, nr_blks);
+ bitmap_set(ifs->state, first_blk + blks_per_folio,
+ last_blk - first_blk + 1);
spin_unlock_irqrestore(&ifs->state_lock, flags);
}
@@ -811,6 +835,8 @@ void iomap_invalidate_folio(struct folio *folio, size_t offset, size_t len)
WARN_ON_ONCE(folio_test_writeback(folio));
folio_cancel_dirty(folio);
ifs_free(folio);
+ } else {
+ iomap_clear_range_dirty(folio, offset, len);
}
}
EXPORT_SYMBOL_GPL(iomap_invalidate_folio);
@@ -1619,6 +1645,7 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
const struct iomap_write_ops *write_ops)
{
u64 bytes = iomap_length(iter);
+ bool zeroed = false;
int status;
do {
@@ -1639,6 +1666,8 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
/* a NULL folio means we're done with a folio batch */
if (!folio) {
status = iomap_iter_advance_full(iter);
+ if (status)
+ return status;
break;
}
@@ -1649,6 +1678,7 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
bytes);
folio_zero_range(folio, offset, bytes);
+ zeroed = true;
folio_mark_accessed(folio);
ret = iomap_write_end(iter, bytes, bytes, folio);
@@ -1658,10 +1688,10 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
status = iomap_iter_advance(iter, bytes);
if (status)
- break;
+ return status;
} while ((bytes = iomap_length(iter)) > 0);
- if (did_zero)
+ if (did_zero && zeroed)
*did_zero = true;
return status;
}
diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c
index 0565328764c1..30468d51b5ad 100644
--- a/fs/iomap/ioend.c
+++ b/fs/iomap/ioend.c
@@ -385,6 +385,8 @@ static bool iomap_ioend_can_merge(struct iomap_ioend *ioend,
if (ioend->io_bio.bi_status != next->io_bio.bi_status)
return false;
+ if (ioend->io_private != next->io_private)
+ return false;
if (next->io_flags & IOMAP_IOEND_BOUNDARY)
return false;
if ((ioend->io_flags & IOMAP_IOEND_NOMERGE_FLAGS) !=
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 27cc07738f33..f3d97eb146e8 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -528,6 +528,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
loff_t len, unsigned int flags, enum ovl_copyop op)
{
+ struct inode *inode_in = file_inode(file_in);
struct inode *inode_out = file_inode(file_out);
struct file *realfile_in, *realfile_out;
loff_t ret;
@@ -551,7 +552,20 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
if (IS_ERR(realfile_in))
goto out_unlock;
- with_ovl_creds(file_inode(file_out)->i_sb) {
+ /*
+ * For cross-sb copy, vfs_copy_file_range() will verify read access with
+ * the mounter creds of the dest fs mounter, so we need to explicitly
+ * verify read access with the source mounter creds.
+ */
+ if (unlikely(inode_in->i_sb != inode_out->i_sb)) {
+ with_ovl_creds(inode_in->i_sb) {
+ ret = rw_verify_area(READ, realfile_in, &pos_in, len);
+ if (unlikely(ret))
+ goto out_unlock;
+ }
+ }
+
+ with_ovl_creds(inode_out->i_sb) {
switch (op) {
case OVL_COPY:
ret = vfs_copy_file_range(realfile_in, pos_in,
diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c
index aa95855c7023..859e80ae6f40 100644
--- a/fs/overlayfs/xattrs.c
+++ b/fs/overlayfs/xattrs.c
@@ -13,7 +13,7 @@ static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name)
OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0;
else
return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX,
- OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0;
+ OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN) == 0;
}
static bool ovl_is_own_xattr(struct super_block *sb, const char *name)
diff --git a/fs/pidfs.c b/fs/pidfs.c
index aaa609ddab04..b57ecc96e967 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/anon_inodes.h>
+#include <linux/compat.h>
#include <linux/exportfs.h>
#include <linux/file.h>
#include <linux/fs.h>
@@ -107,7 +108,7 @@ struct pidfs_attr {
#if BITS_PER_LONG == 32
-DEFINE_SPINLOCK(pidfs_ino_lock);
+static DEFINE_SPINLOCK(pidfs_ino_lock);
static u64 pidfs_ino_nr = 1;
static inline unsigned long pidfs_ino(u64 ino)
@@ -659,6 +660,17 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return open_namespace(ns_common);
}
+#ifdef CONFIG_COMPAT
+static long pidfd_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ if (cmd == FS_IOC32_GETVERSION)
+ cmd = FS_IOC_GETVERSION;
+
+ return pidfd_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
static int pidfs_file_release(struct inode *inode, struct file *file)
{
struct pid *pid = inode->i_private;
@@ -686,7 +698,9 @@ static const struct file_operations pidfs_file_operations = {
.show_fdinfo = pidfd_show_fdinfo,
#endif
.unlocked_ioctl = pidfd_ioctl,
- .compat_ioctl = compat_ptr_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = pidfd_compat_ioctl,
+#endif
};
struct pid *pidfd_pid(const struct file *file)
@@ -915,6 +929,20 @@ static struct dentry *pidfs_fh_to_dentry(struct super_block *sb,
return path.dentry;
}
+static struct file *pidfs_dentry_open(const struct path *path,
+ unsigned int flags,
+ const struct cred *cred)
+{
+ struct file *file;
+
+ /* pidfds are always O_RDWR. */
+ file = dentry_open(path, flags | O_RDWR, cred);
+ /* do_dentry_open() strips O_EXCL and O_TRUNC. */
+ if (!IS_ERR(file))
+ file->f_flags |= flags & (PIDFD_THREAD | PIDFD_AUTOKILL);
+ return file;
+}
+
/*
* Make sure that we reject any nonsensical flags that users pass via
* open_by_handle_at(). Note that PIDFD_THREAD is defined as O_EXCL, and
@@ -940,11 +968,13 @@ static int pidfs_export_permission(struct handle_to_path_ctx *ctx,
static struct file *pidfs_export_open(const struct path *path, unsigned int oflags)
{
/*
- * Clear O_LARGEFILE as open_by_handle_at() forces it and raise
- * O_RDWR as pidfds always are.
+ * Opening via file handle may never raise PIDFD_AUTOKILL. That can
+ * only be done at task creation!
*/
- oflags &= ~O_LARGEFILE;
- return dentry_open(path, oflags | O_RDWR, current_cred());
+ if (WARN_ON_ONCE(oflags & PIDFD_AUTOKILL))
+ return ERR_PTR(-EINVAL);
+ /* Clear O_LARGEFILE as open_by_handle_at() forces it. */
+ return pidfs_dentry_open(path, oflags & ~O_LARGEFILE, current_cred());
}
static const struct export_operations pidfs_export_operations = {
@@ -1108,7 +1138,6 @@ static struct file_system_type pidfs_type = {
struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags)
{
- struct file *pidfd_file;
struct path path __free(path_put) = {};
int ret;
@@ -1126,16 +1155,7 @@ struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags)
VFS_WARN_ON_ONCE(!pid->attr);
flags &= ~PIDFD_STALE;
- flags |= O_RDWR;
- pidfd_file = dentry_open(&path, flags, current_cred());
- /*
- * Raise PIDFD_THREAD and PIDFD_AUTOKILL explicitly as
- * do_dentry_open() strips O_EXCL and O_TRUNC.
- */
- if (!IS_ERR(pidfd_file))
- pidfd_file->f_flags |= (flags & (PIDFD_THREAD | PIDFD_AUTOKILL));
-
- return pidfd_file;
+ return pidfs_dentry_open(&path, flags, current_cred());
}
void __init pidfs_init(void)
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index b4bfe4ddf64e..3dc62c1c2708 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -93,6 +93,13 @@ static void __forget_cached_acl(struct posix_acl **p)
{
struct posix_acl *old;
+ /*
+ * ACL_DONT_CACHE is expected to be a "const" value and xchg it with
+ * ACL_NOT_CACHED would enable acl caching for the inode -
+ * clearly not what the caller has intended.
+ */
+ if (READ_ONCE(*p) == ACL_DONT_CACHE)
+ return;
old = xchg(p, ACL_NOT_CACHED);
if (!is_uncached_acl(old))
posix_acl_release(old);
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index 2f46f1396744..ea6ec61a0430 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -46,7 +46,7 @@ static const char *proc_ns_get_link(struct dentry *dentry,
const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
struct task_struct *task;
struct path ns_path;
- int error = -EACCES;
+ int error;
if (!dentry)
return ERR_PTR(-ECHILD);
@@ -59,6 +59,7 @@ static const char *proc_ns_get_link(struct dentry *dentry,
if (error)
goto out_put_task;
+ error = -EACCES;
if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
goto out;
@@ -90,6 +91,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl
if (res)
goto out_put_task;
+ res = -EACCES;
if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
res = ns_get_name(name, sizeof(name), task, ns_ops);
if (res >= 0)
diff --git a/fs/super.c b/fs/super.c
index a8fd61136aaf..ffdcc6a2e0de 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1082,18 +1082,35 @@ void emergency_remount(void)
}
}
+static inline bool get_active_super(struct super_block *sb)
+{
+ bool active = false;
+
+ if (super_lock_excl(sb)) {
+ active = atomic_inc_not_zero(&sb->s_active);
+ super_unlock_excl(sb);
+ }
+ return active;
+}
+
static void do_thaw_all_callback(struct super_block *sb, void *unused)
{
+ if (!get_active_super(sb))
+ return;
+
+ /* fs_bdev_thaw() acquires s_umount so it must not be held here */
if (IS_ENABLED(CONFIG_BLOCK))
while (sb->s_bdev && !bdev_thaw(sb->s_bdev))
pr_warn("Emergency Thaw on %pg\n", sb->s_bdev);
- thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL);
- return;
+
+ if (super_lock_excl(sb))
+ thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL);
+ deactivate_super(sb);
}
static void do_thaw_all(struct work_struct *work)
{
- __iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_EXCL);
+ __iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_UNLOCKED);
kfree(work);
printk(KERN_WARNING "Emergency Thaw complete\n");
}
@@ -1114,17 +1131,6 @@ void emergency_thaw_all(void)
}
}
-static inline bool get_active_super(struct super_block *sb)
-{
- bool active = false;
-
- if (super_lock_excl(sb)) {
- active = atomic_inc_not_zero(&sb->s_active);
- super_unlock_excl(sb);
- }
- return active;
-}
-
static const char *filesystems_freeze_ptr = "filesystems_freeze";
static void filesystems_freeze_callback(struct super_block *sb, void *freeze_all_ptr)