Commit graph

1,514 commits

Author SHA1 Message Date
Christian Brauner
214897acc8
Revert "namei: don't reveal overmounted entries in refwalk"
This reverts commit 8189ab688f.

This bug class is bigger than can be fixed with this simple patch.
Back out of it and give us some time to fix this properly.

Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-10-16 10:50:57 +02:00
Christian Brauner
98b6cf102b
namei: test for a negative dentry the way traverse_mounts() does
lookup_fast_for_open() only hands an O_CREAT open a cached dentry if
it's positive. Otherwise the open goes on to lookup_open(), which takes
the directory lock and creates the file. It tests for that with
!dentry->d_inode, but traverse_mounts() later decides with the type in
d_flags. And when a dentry is instantiated __d_set_inode_and_type()
stores ->d_inode first and only then releases the type into ->d_flags.

So a refwalk that took its reference to a negative dentry right before
another task instantiates it can find ->d_inode already set in
lookup_fast_for_open(), go on to step_into(), and still find the
negative type in traverse_mounts(). The open then fails with -ENOENT,
which an O_CREAT open must never do. rcuwalk doesn't get that far, the
d_seq check in step_into() catches the instantiation.

Use d_is_negative() so both look at the type. While the type is still
negative the open takes lookup_open() and waits for the directory lock
like any other O_CREAT open of a negative dentry.

On its own the window is a couple of instructions in the creating task,
so it takes an interrupt or a preempted vcpu in exactly the right spot.
I haven't caught it without help in about 600 million racing O_CREAT
opens.

The debug patch below stalls the creating task between the two stores
for names starting with "ocrace", the way an interrupt landing there
would. The reproducer below opens "ocrace" with O_CREAT through
symlinks on a strictatime tmpfs: following them updates their atime,
which drops the walk out of rcuwalk, so the name is always looked up in
refwalk. With

  echo 20 > /proc/sys/fs/ocrace_instantiate_us
  ./ocreat_enoent -t 60

on a KASAN kernel open() failed with -ENOENT 43 times in 4.9 million
calls. With this patch it never failed in 4.8 million calls. Tracing
step_into_slowpath() during such a run, each of the 98 -ENOENTs it
returned came right after the same task had entered it with a dentry
that was still typed negative.

  // SPDX-License-Identifier: GPL-2.0
  /*
   * ocreat_enoent: open(O_CREAT) racing the creation of the same name.
   *
   * Writers unlink "ocrace" and create it again. Readers open it with
   * O_CREAT through a symlink of their own on a strictatime tmpfs.
   * Following the symlink updates its atime, which drops the walk out of
   * rcu mode, so the name is always looked up in refwalk. An O_CREAT open
   * must never fail with ENOENT.
   *
   * -p arms a cpu-clock perf timer with that period in ns on every writer,
   * so that interrupts land at random points in it.
   *
   *   usage: ocreat_enoent [-t secs] [-w writers] [-r readers] [-p ns]
   */
  #define _GNU_SOURCE
  #include <errno.h>
  #include <fcntl.h>
  #include <pthread.h>
  #include <stdatomic.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/mount.h>
  #include <sys/stat.h>
  #include <sys/syscall.h>
  #include <unistd.h>
  #include <linux/perf_event.h>

  #define DIR "/tmp/oc"

  static atomic_int stop;
  static atomic_long opens, creates, enoent, other;
  static long period;

  static void die(const char *what)
  {
  	fprintf(stderr, "FATAL %s: %s\n", what, strerror(errno));
  	exit(2);
  }

  static void *writer(void *arg)
  {
  	(void)arg;
  	if (period) {
  		struct perf_event_attr attr = {
  			.type = PERF_TYPE_SOFTWARE,
  			.size = sizeof(attr),
  			.config = PERF_COUNT_SW_CPU_CLOCK,
  			.sample_period = period,
  		};

  		if (syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0) < 0)
  			die("perf_event_open");
  	}
  	while (!atomic_load(&stop)) {
  		int fd;

  		unlink(DIR "/ocrace");
  		fd = open(DIR "/ocrace", O_CREAT | O_RDWR | O_CLOEXEC, 0644);
  		if (fd >= 0)
  			close(fd);
  		atomic_fetch_add(&creates, 1);
  	}
  	return NULL;
  }

  static void *reader(void *arg)
  {
  	char lnk[64];

  	snprintf(lnk, sizeof(lnk), DIR "/lnk.%ld", (long)arg);
  	while (!atomic_load(&stop)) {
  		int fd = open(lnk, O_CREAT | O_RDWR | O_CLOEXEC, 0644);

  		if (fd >= 0)
  			close(fd);
  		else if (errno == ENOENT)
  			atomic_fetch_add(&enoent, 1);
  		else
  			atomic_fetch_add(&other, 1);
  		atomic_fetch_add(&opens, 1);
  	}
  	return NULL;
  }

  int main(int argc, char **argv)
  {
  	int secs = 60, nw = 4, nr = 12, c, n = 0;
  	pthread_t t[256];

  	while ((c = getopt(argc, argv, "t:w:r:p:")) != -1) {
  		switch (c) {
  		case 't': secs = atoi(optarg); break;
  		case 'w': nw = atoi(optarg); break;
  		case 'r': nr = atoi(optarg); break;
  		case 'p': period = atol(optarg); break;
  		default: return 2;
  		}
  	}
  	if (nw + nr > 256)
  		return 2;
  	mkdir(DIR, 0755);
  	if (mount("oc", DIR, "tmpfs", MS_STRICTATIME, "mode=0755"))
  		die("tmpfs");
  	for (long i = 0; i < nr; i++) {
  		char lnk[64];

  		snprintf(lnk, sizeof(lnk), DIR "/lnk.%ld", i);
  		if (symlink("ocrace", lnk))
  			die("symlink");
  	}
  	for (long i = 0; i < nw; i++)
  		if (pthread_create(&t[n++], NULL, writer, NULL))
  			die("writer");
  	for (long i = 0; i < nr; i++)
  		if (pthread_create(&t[n++], NULL, reader, (void *)i))
  			die("reader");
  	sleep(secs);
  	atomic_store(&stop, 1);
  	for (int i = 0; i < n; i++)
  		pthread_join(t[i], NULL);
  	printf("%ds: %ld opens, %ld creates, ENOENT %ld, other errors %ld\n",
  	       secs, atomic_load(&opens), atomic_load(&creates),
  	       atomic_load(&enoent), atomic_load(&other));
  	umount2(DIR, MNT_DETACH);
  	return atomic_load(&enoent) ? 1 : 0;
  }

  diff --git a/fs/dcache.c b/fs/dcache.c
  --- a/fs/dcache.c
  +++ b/fs/dcache.c
  @@ -32,9 +32,13 @@
   #include <linux/bit_spinlock.h>
   #include <linux/rculist_bl.h>
   #include <linux/list_lru.h>
  +#include <linux/delay.h>
   #include "internal.h"
   #include "mount.h"

  +/* debug: stall "ocrace*" names between ->d_inode and the type */
  +static int sysctl_ocrace_instantiate_us;
  +
   #include <asm/runtime-const.h>

   /*
  @@ -200,6 +204,13 @@
   }

   static const struct ctl_table fs_dcache_sysctls[] = {
  +	{
  +		.procname	= "ocrace_instantiate_us",
  +		.data		= &sysctl_ocrace_instantiate_us,
  +		.maxlen		= sizeof(int),
  +		.mode		= 0644,
  +		.proc_handler	= proc_dointvec,
  +	},
   	{
   		.procname	= "dentry-state",
   		.data		= &dentry_stat,
  @@ -405,6 +416,10 @@
   	unsigned flags;

   	dentry->d_inode = inode;
  +	/* debug: an interrupt landing right here */
  +	if (unlikely(READ_ONCE(sysctl_ocrace_instantiate_us)) &&
  +	    !strncmp(dentry->d_name.name, "ocrace", 6))
  +		udelay(READ_ONCE(sysctl_ocrace_instantiate_us));
   	flags = READ_ONCE(dentry->d_flags);
   	flags &= ~DCACHE_ENTRY_TYPE;
   	flags |= type_flags;

Fixes: e747e15156 ("fs: try an opportunistic lookup for O_CREAT opens too")
Cc: stable@vger.kernel.org # v6.12+
Link: https://patch.msgid.link/20261012-work-namei-ocreate-v1-1-c7883b17ee28@kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-10-14 22:08:05 +02:00
Christian Brauner
8189ab688f
namei: don't reveal overmounted entries in refwalk
In rcuwalk the dentry is validated before it is accepted. step_into()
step_into() rechecks d_seq and __follow_mount_rcu() rechecks mount_lock.
An entry that gets unlinked in between causes the lookup to retry and
miss.

A refwalk doesn't do this. lookup_fast() takes a reference on the hashed
dentry and simply accepts it. So an unlink that happens after the
reference was taken isn't seen by refwalk. That's fine for a simple
file. We just happened to open it before it was unlinked, no problem.

For a mountpoint and specifically a locked mountpoint it very much
isn't. The unlink detaches all mounts and then removes the name. Any
refwalk that hasn't traversed the mounts yet simply reveals the
underlying entry. It's a very narrow window but it can be hit:

  reads of a covered file in 60 s, 3 walkers, ~15000 unlinks
    no widening            27
    that step + 200 us   3741

See the appended patch for a more reliable reproducer.

So check the entry after step_into(). unlink(), rmdir() and rename()
mark the dentry with dont_mount() before they detach the mounts and
remove the dentry.

So a dentry that is marked with DCACHE_CANT_MOUNT and is unhashed by the
time its mounts were looked at is a name that was unlinked under the
refwalk. DCACHE_CANT_MOUNT is read after the mounts were looked up and
it is set before they are detached by detach_mounts(). A refwalk that
missed the mounts will see DCACHE_CANT_MOUNT.

Plain d_unlinked() is fine. A rename takes the dentry off its hash chain
but ___d_drop() leaves d_hash.pprev set. So only __d_drop() and a rename
over the dentry unhash it. The only move that flips IS_ROOT splices in a
disconnected alias. That doesn't have DCACHE_CANT_MOUNT set.

Add the to step_into_slowpath(). ".." and LOOKUP_DOWN may legitimately
land on an unhashed directory. A refwalk that crossed onto a mount has
path.mnt different from nd->path.mnt. A dentry that a filesystem dropped
on its own (d_invalidate(), d_drop()) doesn't have the flag set and is
treated as before.

  // SPDX-License-Identifier: GPL-2.0
  /*
   * unlink_covered: unlink a file that is a mountpoint in a detached copy of
   * its mount, against walkers that open it through that copy.
   *
   * A is a tmpfs with the file f1 (content SECRET_F) and the plain file
   * MARK_A. A' is an open_tree(OPEN_TREE_CLONE) copy of A with MARK_A bound
   * on A'/f1, held through an O_PATH fd on its root once the tree fd is
   * closed. Walkers open f1 through that fd while the driver unlinks A/f1,
   * where nothing is mounted on it. A walker may read MARK_A or get ENOENT.
   * A read of SECRET_F is a hit: the name was found after its mount was
   * gone. The lockless walks use openat2(RESOLVE_CACHED).
   *
   * usage: unlink_covered [-t seconds] [-w walkers]
   */
  #ifndef _GNU_SOURCE
  #define _GNU_SOURCE
  #endif
  #include <errno.h>
  #include <fcntl.h>
  #include <pthread.h>
  #include <stdatomic.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
  #include <sys/mount.h>
  #include <sys/stat.h>
  #include <sys/syscall.h>
  #include <linux/openat2.h>

  #ifndef __NR_open_tree
  #define __NR_open_tree 428
  #endif
  #ifndef __NR_move_mount
  #define __NR_move_mount 429
  #endif
  #ifndef __NR_openat2
  #define __NR_openat2 437
  #endif
  #ifndef OPEN_TREE_CLONE
  #define OPEN_TREE_CLONE 1
  #endif
  #ifndef OPEN_TREE_CLOEXEC
  #define OPEN_TREE_CLOEXEC O_CLOEXEC
  #endif
  #ifndef MOVE_MOUNT_F_EMPTY_PATH
  #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004
  #endif

  #define WORK "/tmp/uc"
  #define ADIR WORK "/A"

  static int duration = 60, nwalkers = 3;
  static atomic_int stop, writer_waiting;
  static pthread_rwlock_t cur_lock = PTHREAD_RWLOCK_INITIALIZER;
  static int cur_fd = -1;		/* the root of A', -1 while there is none */
  static atomic_long n_unlink, n_walk, n_mark, n_enoent, n_other, n_secret,
  		   n_secret_cached;

  static void die(const char *what)
  {
  	fprintf(stderr, "FATAL %s: %s\n", what, strerror(errno));
  	exit(2);
  }

  static void put_file(int dfd, const char *name, const char *content)
  {
  	int fd = openat(dfd, name, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
  			0644);

  	if (fd < 0 || write(fd, content, strlen(content)) < 0)
  		die(name);
  	close(fd);
  }

  /* "plain/../" @n times, then f1: a longer walk that checks nothing on its way */
  static char *longpath(int n)
  {
  	char *p = malloc(n * 9 + 3), *q = p;

  	for (int i = 0; i < n; i++, q += 9)
  		memcpy(q, "plain/../", 9);
  	strcpy(q, "f1");
  	return p;
  }

  static void try_read(int dfd, const char *path, int cached)
  {
  	struct open_how how = { .flags = O_RDONLY | O_CLOEXEC,
  				.resolve = RESOLVE_CACHED };
  	char buf[32] = "";
  	long n;
  	int fd;

  	if (cached)
  		fd = syscall(__NR_openat2, dfd, path, &how, sizeof(how));
  	else
  		fd = openat(dfd, path, O_RDONLY | O_CLOEXEC);
  	atomic_fetch_add(&n_walk, 1);
  	if (fd < 0) {
  		if (errno == ENOENT)
  			atomic_fetch_add(&n_enoent, 1);
  		else if (!cached || errno != EAGAIN)
  			atomic_fetch_add(&n_other, 1);
  		return;
  	}
  	n = read(fd, buf, sizeof(buf) - 1);
  	close(fd);
  	if (n >= 6 && !strncmp(buf, "SECRET", 6)) {
  		if (cached)
  			atomic_fetch_add(&n_secret_cached, 1);
  		if (!atomic_fetch_add(&n_secret, 1))
  			printf("HIT: read \"%s\" through %s (%s walk)\n", buf,
  			       path, cached ? "lockless" : "any");
  	} else {
  		atomic_fetch_add(&n_mark, 1);
  	}
  }

  static void *walker(void *arg)
  {
  	unsigned int r = (long)arg * 2654435761u;

  	while (!atomic_load(&stop)) {
  		char *p_long, *p_short;
  		int dfd;

  		while (atomic_load(&writer_waiting) && !atomic_load(&stop))
  			usleep(20);
  		pthread_rwlock_rdlock(&cur_lock);
  		dfd = cur_fd;
  		if (dfd < 0) {
  			pthread_rwlock_unlock(&cur_lock);
  			usleep(100);
  			continue;
  		}
  		r = r * 1103515245u + 12345u;
  		p_long = longpath(1 + (r >> 8) % 400);
  		p_short = longpath(0);
  		try_read(dfd, p_long, 0);
  		try_read(dfd, p_short, 0);
  		try_read(dfd, p_long, 1);
  		pthread_rwlock_unlock(&cur_lock);
  		free(p_long);
  		free(p_short);
  	}
  	return NULL;
  }

  /* hand the walkers a new A' (or none), close the old one */
  static void publish(int fd)
  {
  	int old;

  	atomic_fetch_add(&writer_waiting, 1);
  	pthread_rwlock_wrlock(&cur_lock);
  	old = cur_fd;
  	cur_fd = fd;
  	pthread_rwlock_unlock(&cur_lock);
  	atomic_fetch_sub(&writer_waiting, 1);
  	if (old >= 0)
  		close(old);
  }

  static void *driver(void *arg __attribute__((unused)))
  {
  	int a;

  	if (mkdir(ADIR, 0755) && errno != EEXIST)
  		die("mkdir A");
  	if (mount("A", ADIR, "tmpfs", 0, "size=4M"))
  		die("mount A");
  	a = open(ADIR, O_PATH | O_DIRECTORY | O_CLOEXEC);
  	if (a < 0 || mkdirat(a, "plain", 0755))
  		die("A/plain");
  	put_file(a, "MARK_A", "MARK_A");
  	while (!atomic_load(&stop)) {
  		int t, m, fd_a;

  		put_file(a, "f1", "SECRET_F");
  		t = syscall(__NR_open_tree, a, "",
  			    OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_EMPTY_PATH);
  		if (t < 0)
  			die("open_tree A");
  		m = syscall(__NR_open_tree, a, "MARK_A",
  			    OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
  		if (m < 0)
  			die("open_tree MARK_A");
  		if (syscall(__NR_move_mount, m, "", t, "f1",
  			    MOVE_MOUNT_F_EMPTY_PATH))
  			die("move_mount");
  		close(m);
  		fd_a = openat(t, ".", O_PATH | O_DIRECTORY | O_CLOEXEC);
  		if (fd_a < 0)
  			die("open A'");
  		publish(fd_a);
  		usleep(200 + rand() % 1000);
  		close(t);			/* A' is unmounted, fd_a holds it */
  		usleep(200 + rand() % 1000);
  		if (unlinkat(a, "f1", 0))	/* through A, a plain file there */
  			die("unlink f1");
  		atomic_fetch_add(&n_unlink, 1);
  		usleep(rand() % 300);
  		publish(-1);
  	}
  	close(a);
  	umount2(ADIR, MNT_DETACH);
  	return NULL;
  }

  int main(int argc, char **argv)
  {
  	pthread_t d, *w;
  	int c, i;

  	setvbuf(stdout, NULL, _IOLBF, 0);
  	while ((c = getopt(argc, argv, "t:w:")) != -1) {
  		switch (c) {
  		case 't':
  			duration = atoi(optarg);
  			break;
  		case 'w':
  			nwalkers = atoi(optarg);
  			break;
  		default:
  			fprintf(stderr, "usage: unlink_covered [-t seconds] [-w walkers]\n");
  			return 2;
  		}
  	}
  	if (unshare(CLONE_NEWNS) ||
  	    mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL))
  		die("unshare");
  	if (mkdir(WORK, 0755) && errno != EEXIST)
  		die("mkdir");
  	w = calloc(nwalkers, sizeof(*w));
  	for (i = 0; i < nwalkers; i++)
  		if (pthread_create(&w[i], NULL, walker, (void *)(long)i))
  			die("pthread_create");
  	if (pthread_create(&d, NULL, driver, NULL))
  		die("pthread_create");
  	sleep(duration);
  	atomic_store(&stop, 1);
  	pthread_join(d, NULL);
  	for (i = 0; i < nwalkers; i++)
  		pthread_join(w[i], NULL);
  	printf("unlink_covered: %d s, %d walkers: unlinks %ld walks %ld mark %ld enoent %ld other %ld SECRET %ld (lockless %ld)\n",
  	       duration, nwalkers, atomic_load(&n_unlink), atomic_load(&n_walk),
  	       atomic_load(&n_mark), atomic_load(&n_enoent),
  	       atomic_load(&n_other), atomic_load(&n_secret),
  	       atomic_load(&n_secret_cached));
  	return atomic_load(&n_secret) ? 1 : 0;
  }

  diff --git a/fs/namei.c b/fs/namei.c
  --- a/fs/namei.c
  +++ b/fs/namei.c
  @@ -35,6 +35,7 @@
   #include <linux/fcntl.h>
   #include <linux/device_cgroup.h>
   #include <linux/fs_struct.h>
  +#include <linux/delay.h>
   #include <linux/posix_acl.h>
   #include <linux/hash.h>
   #include <linux/bitops.h>
  @@ -1205,9 +1206,26 @@ static int sysctl_protected_symlinks __read_mostly;
   static int sysctl_protected_hardlinks __read_mostly;
   static int sysctl_protected_fifos __read_mostly;
   static int sysctl_protected_regular __read_mostly;
  +/* debug: widen the two windows of the detach_mounts() race */
  +static int sysctl_detach_race_walk_us __read_mostly;
  +static int sysctl_detach_race_unlink_us __read_mostly;

   #ifdef CONFIG_SYSCTL
   static const struct ctl_table namei_sysctls[] = {
  +	{
  +		.procname	= "detach_race_walk_us",
  +		.data		= &sysctl_detach_race_walk_us,
  +		.maxlen		= sizeof(int),
  +		.mode		= 0644,
  +		.proc_handler	= proc_dointvec,
  +	},
  +	{
  +		.procname	= "detach_race_unlink_us",
  +		.data		= &sysctl_detach_race_unlink_us,
  +		.maxlen		= sizeof(int),
  +		.mode		= 0644,
  +		.proc_handler	= proc_dointvec,
  +	},
   	{
   		.procname	= "protected_symlinks",
   		.data		= &sysctl_protected_symlinks,
  @@ -1878,6 +1896,10 @@ static struct dentry *lookup_fast(struct nameidata *nd)
   		dentry = __d_lookup(parent, &nd->last);
   		if (unlikely(!dentry))
   			return NULL;
  +		/* debug: between finding a mountpoint and crossing its mounts */
  +		if (unlikely(sysctl_detach_race_walk_us) && d_mountpoint(dentry))
  +			usleep_range(sysctl_detach_race_walk_us,
  +				     sysctl_detach_race_walk_us + 10);
   		status = d_revalidate(nd->inode, &nd->last, dentry, nd->flags);
   	}
   	if (unlikely(status <= 0)) {
  @@ -5693,6 +5715,10 @@ int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
   			if (!error) {
   				dont_mount(dentry);
   				detach_mounts(dentry);
  +				/* debug: the mounts are gone, d_delete() is still to come */
  +				if (unlikely(sysctl_detach_race_unlink_us))
  +					usleep_range(sysctl_detach_race_unlink_us,
  +						     sysctl_detach_race_unlink_us + 10);
   			}
   		}
   	}

Fixes: 8ed936b567 ("vfs: Lazily remove mounts on unlinked files and directories.")
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20261002-work-mount-fixes-4-v1-2-dd44b89d44ce@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-10-04 12:31:38 +02:00
Linus Torvalds
1c3e8cef79 vfs-7.3-rc1.misc
Please consider pulling these changes from the signed vfs-7.3-rc1.misc tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCan7RJQAKCRCRxhvAZXjc
 otI6AQCIwshv02UI9fmbTC9pFr8b+v/9IWQwdVULaH61HXgICQD7BE/CktL8nfRs
 zCzGS6kBdWgiOA6bdVpgGGLqiMRnQgo=
 =Kc+G
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.3-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull misc vfs updates from Christian Brauner:
 "Bigger cleanups:

   - The lockref dead-count handling is tidied up.

     The open-coded check for a count below zero as the dead marker
     relies on information the caller should not have.

   - make put_mnt_ns() leave mounts connected. Destroying a mount
     namespace disconnected its mounts from their mount points. So a
     file descriptor still open on the parent of a mount point could be
     used to peek under it.

     Locked mounts were already kept connected to prevent exactly that.
     But a mount is only locked when its tree is copied across a user
     namespace boundary. So a mount namespace set up by a privileged
     component had no locked mounts and its mounts were disconnected.
     Passing UMOUNT_CONNECTED keeps every mount connected and prevents
     that bug.

   - vfs_prepare_mode() passes S_IFDIR for directories. I meant to fix
     that ago but didn't get to it. So now someone finally did it.

     This kills the exception where the mode could be 0 when a directory
     was created whereas every other creation operation passed it
     explicitly already.

   - move long delayed work for ufs, jffs2, hfsplus, hfs and affs from
     the per-cpu system_long_wq to the new unbound system_dfl_long_wq.

     None of that work relies on per-cpu state and the work item is
     enqueued with queue_delayed_work() whose timer is global anyway. So
     it may as well benefit from scheduler task placement.

  Smaller fixes and cleanups:

   - unlock_buffer() and journal_end_buffer_io_sync() use
     clear_and_wake_up_bit()

   - the pipe page pools are unified into a single per-pipe pool and the
     extra wake_up(rd_wait) is limited to EPOLLET consumers

   - eventpoll now computes its timer slack lazily in ep_poll()

   - shrink_dcache_for_umount() keeps making progress on busy roots

   - excess xarray nodes are freed in clear_inode()

   - romfs detects hard link cycles

   - the user path of nested backing files is fixed

   - pidfd holds exec_update_lock around the namespace ioctl

   - non-memcg-aware nr_cached_objects is skipped during memcg slab
     shrink

   - iomap_write_iter() always returns status

   - mangle_path() is renamed to seq_mangle_path()

   - inode timestamp accessors are annotated

   - new regression test for pipe->poll_usage.

   - a few documentation, kernel-doc and selftest fixes"

* tag 'vfs-7.3-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (67 commits)
  selftests/namespaces: Fix racy pipe handshake in timens and pidns_separate
  selftests/epoll: add a regression test for pipe->poll_usage
  pipe: only enable the extra wake_up(rd_wait) for EPOLLET consumers
  pidfd: hold exec_update_lock around namespace ioctl
  fs: fix user path of nested backing files
  fs: remove stale inode_insert5() kernel-doc parameter
  fs: fix switch/case indentation in sysfs() syscall
  fs: document semantics of kstat::{uid,gid} fields
  dcache: keep shrink_dcache_for_umount() making progress on busy roots
  seq_file: rename mangle_path to seq_mangle_path
  nstree: add/fix struct ns_id_req kernel-doc member fields
  dcache: use lockref routines for dead count checks
  lockref: tidy up dead count handling
  initramfs: fix typo in reserve_initrd_mem comment
  fs/pipe: unify the page pools into a single per-pipe pool
  fs: annotate inode timestamp accessors
  eventpoll: compute timer slack lazily in ep_poll()
  selftests/filesystems: add mntns cleanup test
  put_mnt_ns(): leave mounts connected
  affs: Move long delayed work on system_dfl_long_wq
  ...
2026-08-17 12:56:12 -07:00
Linus Torvalds
ab5ed08f2d vfs-7.3-rc1.lookup
Please consider pulling these changes from the signed vfs-7.3-rc1.lookup tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCan7RJQAKCRCRxhvAZXjc
 ouj6AQCnBHstCt5RRimywLYU+eTmdKe203zRQYxXpwlYOAzm7wEAm1mIC7/Of6bD
 jkiJilcPDcyYcJsqwBw/Vcz5HR+v8wo=
 =TX6K
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.3-rc1.lookup' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs lookup updates from Christian Brauner:
 "This refactors lookup_open() and adds vfs_lookup_open() for nfsd.

  mnt_want_write() and parent locking are moved into lookup_open()
  itself.

  audit_inode_child() is also now called in lookup_open() on failure.
  That is the calling convention in vfs_create() and vfs_mkdir(), but
  lookup_open() made no such call when atomic_open() should have created
  a file and did not. And neither did the regular ->create() path fwiw.

  This also contains work to remove the unneeded excl argument from the
  ->create() inode op"

* tag 'vfs-7.3-rc1.lookup' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  fs/namei.c: fix coding style in atomic_open() and lookup_open()
  fs/namei.c: fix kerneldoc of atomic_open() and vfs_lookup_open()
  fs/namei.c: update stale comments in lookup_open()
  Remove excl arg to ->create inode_operation
  fs/namei.c: update kerneldoc of atomic_open()
  vfs: call audit_inode_child() in lookup_open() on failure
  vfs: move create error && negative dentry case in lookup_open() up
  VFS: add vfs_lookup_open() for nfsd
  VFS: move delegated_inode retry loop into lookup_open()
  VFS: move mnt_want_write() and locking into lookup_open()
2026-08-17 12:03:34 -07:00
Christian Brauner
b89b75f362
fs/namei.c: fix coding style in atomic_open() and lookup_open()
Commit 4886c80eef ("vfs: call audit_inode_child() in lookup_open() on
failure") indented a continuation line with spaces, left three
declarations without a following blank line and used a trailing */ on the
last line of a block comment.  Clean all of that up, no functional change.

Link: https://patch.msgid.link/20260731-work-lookup-fixes-v1-3-2412b85cf65c@kernel.org
Fixes: 4886c80eef ("vfs: call audit_inode_child() in lookup_open() on failure")
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-31 10:37:40 +02:00
Christian Brauner
e02bbfd940
fs/namei.c: fix kerneldoc of atomic_open() and vfs_lookup_open()
Commit ba0e870266 ("fs/namei.c: update kerneldoc of atomic_open()")
turned the comment above atomic_open() into kerneldoc, but wrote the
return description as running text.  kernel-doc only recognises a return
section introduced by "Return:" or "Returns:", so this added a warning
under W=1:

  fs/namei.c:4362 No description found for return value of 'atomic_open'

Give it the missing colon.  The summary line also has to stand on its
own line, so move the "from a negative dentry" part into the body, where
it can say that the caller has to hand over a negative dentry.

Also add the "to" missing from vfs_lookup_open()'s description.

Link: https://patch.msgid.link/20260731-work-lookup-fixes-v1-2-2412b85cf65c@kernel.org
Fixes: ba0e870266 ("fs/namei.c: update kerneldoc of atomic_open()")
Fixes: 536227b814 ("VFS: add vfs_lookup_open() for nfsd")
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-31 10:37:40 +02:00
Christian Brauner
aa00a8fd9d
fs/namei.c: update stale comments in lookup_open()
Commit ddb6e6c72a ("VFS: move mnt_want_write() and locking into
lookup_open()") moved the parent inode locking into lookup_open(), but
left the comment claiming the caller has to take it.  A caller following
that comment now deadlocks, and the series added a second caller.

Describe what the function actually does.  While at it drop the claim
that it returns 0 on success and updates @path, wrong ever since
lookup_open() started returning a dentry in v5.7, and fix the reference
to lookup_open() in a comment that now sits inside lookup_open() itself.

Link: https://patch.msgid.link/20260731-work-lookup-fixes-v1-1-2412b85cf65c@kernel.org
Fixes: ddb6e6c72a ("VFS: move mnt_want_write() and locking into lookup_open()")
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-31 10:37:40 +02:00
NeilBrown
b2f1e6301e
Remove excl arg to ->create inode_operation
The only time that 'false' is passed as the 'excl' arg to the ->create
inode_operation is in lookup_open() when ->atomic_open is not provided
by the parent directory.
*all* directory inode_operations which do not have ->atomic_open
completely ignore the 'excl' arg.

Therefore we don't need the 'excl' arg.  Those few ->create operations
which pay attention to the arg are only ever called with a value of
'true'.

We remove that arg and change all ->create operations to behave as those
thhe arg were 'true'.

Signed-off-by: NeilBrown <neil@brown.name>
Link: https://patch.msgid.link/178290671516.27465.15984496764174914338@noble.neil.brown.name
Reviewed-by: Jori Koolstra <jkoolstra@xs4all.nl>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-31 10:18:30 +02:00
Jori Koolstra
ba0e870266
fs/namei.c: update kerneldoc of atomic_open()
The comments above atomic_open() contain several errors:

  - atomic_open() does not return 0 if successful
  - @path is not updated

Fix those and be more explicit about when FMODE_OPENED and FMODE_CREATED
are set. Change to a full kerneldoc.

Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl>
Link: https://patch.msgid.link/20260710164233.827744-4-jkoolstra@xs4all.nl
Reviewed-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-30 12:16:56 +02:00
Jori Koolstra
4886c80eef
vfs: call audit_inode_child() in lookup_open() on failure
audit_inode_child() is called in may_create_dentry() so that failed
filesystem operations still register an audit entry. On success, the
entry is overwritten when, for instance, fsnotify_create() is called.
This is the calling convention in vfs_create() and vfs_mkdir().
In lookup_open(), however, when atomic_open() should have created a
file but didn't, no call to audit_inode_child() is made. The same is
true for the regular ->create() path.

Fix the calling of audit_inode_child() in lookup_open() to match the
vfs_create() path. For the ->atomic_open() filesystems this logic has
been pushed into atomic_open(). This function is also reordered a bit
to make the case distinction of the possible returns from
->atomic_open() more explicit (i.e. finish_open() or finish_no_open()).

When retrying delegation breaking, audit_inode_child() could be called
more than once, but this is OK because those entries are reused.

Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl>
Link: https://patch.msgid.link/20260710164233.827744-3-jkoolstra@xs4all.nl
Acked-by: Paul Moore (audit) <paul@paul-moore.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-30 12:16:56 +02:00
Jori Koolstra
4d315e54aa
vfs: move create error && negative dentry case in lookup_open() up
O_CREAT is stripped when create_error is set in lookup_open(), so when
lookup does not return an inode, the case

	if (!dentry->d_inode && (open_flag & O_CREAT))

is always skipped. We can get rid of this cognitive step by handling the
error case first.

Reviewed-by: NeilBrown <neil@brown.name>
Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl>
Link: https://patch.msgid.link/20260710164233.827744-2-jkoolstra@xs4all.nl
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-30 12:16:55 +02:00
NeilBrown
536227b814
VFS: add vfs_lookup_open() for nfsd
vfs_lookup_open() is a limited version of lookup_open() which is
exported for nfsd to use - to replace dentry_create().

It is limited in that no filename is given (thus no auditing) and no
LOOKUP_ flags are passed.  A few "intent" LOOKUP flags are deduced from
the open flags.

If a non-regular file is found and appropriate error is returned and no
file is opened.

Signed-off-by: NeilBrown <neil@brown.name>
Link: https://patch.msgid.link/20260714230534.776886-4-neilb@ownmail.net
Reviewed-by: Jori Koolstra <jkoolstra@xs4all.nl>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-23 15:42:48 +02:00
NeilBrown
a5438415be
VFS: move delegated_inode retry loop into lookup_open()
By moving this retry into lookup_open() we no longer need to pass around
the delegated_inode pointer.

Various variable assignments need to be moved out of the declaration
block so that they can be repeated after the "goto retry".

Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: Jori Koolstra <jkoolstra@xs4all.nl>
Signed-off-by: NeilBrown <neil@brown.name>
Link: https://patch.msgid.link/20260714230534.776886-3-neilb@ownmail.net
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-23 15:42:48 +02:00
NeilBrown
ddb6e6c72a
VFS: move mnt_want_write() and locking into lookup_open()
The mnt_want_write() call and the parent inode locking in
open_last_lookups() are only needed for lookup_open().  So we can move
them and all the got_write handling into lookup_open().

Note that we need to also check create_error when determining whether to
unlock shared or not, as O_CREAT can be cleared, but create_error is
only set of O_CREAT was set.

The fsnotify calls come too as they must be in the locked region.

Also use the existing dir_inode uniformly for dir->d_inode.

This is a step towards exporting an better "open/create" interface to nfsd.

Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: Jori Koolstra <jkoolstra@xs4all.nl>
Signed-off-by: NeilBrown <neil@brown.name>
Link: https://patch.msgid.link/20260714230534.776886-2-neilb@ownmail.net
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-23 15:42:48 +02:00
Christian Brauner
539dce1144
fs: refuse O_TMPFILE creation with an unmapped fsuid or fsgid
vfs_tmpfile() never checked that the caller's fsuid and fsgid map into
the filesystem.  On an idmapped mount whose idmapping does not cover the
caller's fs{u,g}id, the ->tmpfile() instance initializes the new inode
through inode_init_owner(), where mapped_fsuid()/mapped_fsgid() return
INVALID_UID/INVALID_GID, and the tmpfile ends up owned by (uid_t)-1.

Every other creation path already refuses this: may_o_create() (O_CREAT)
and may_create_dentry() (mkdir, mknod, symlink, link) bail out with
-EOVERFLOW via fsuidgid_has_mapping() precisely so that an object cannot
be created with an owner the filesystem cannot represent.  An O_TMPFILE
is no exception: it is created I_LINKABLE and linkat(2) can splice it
into the namespace afterwards, so the same guarantee must hold.

Add the missing fsuidgid_has_mapping() check to vfs_tmpfile().  On a
non-idmapped mount the caller's fs{u,g}id always map in the superblock's
user namespace, so this is a no-op there and only takes effect on an
idmapped mount that does not map the caller.  It applies to every
filesystem that sets FS_ALLOW_IDMAP and implements ->tmpfile() (tmpfs,
ext4, btrfs, xfs, f2fs, ...), and to overlayfs, whose upper-layer
tmpfile creation funnels through vfs_tmpfile() via backing_tmpfile_open().

Fixes: 8e5389132a ("fs: introduce fsuidgid_has_mapping() helper")
Link: https://patch.msgid.link/20260615-work-idmapped-tmpfile-v1-1-754a94d81f83@kernel.org
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-01 15:26:18 +02:00
Jori Koolstra
2f3a7a488c
vfs: pass S_IFDIR mode to vfs_prepare_mode()
There is a comment in vfs_prepare_mode() that says:

  Note that it's currently valid for @type to be 0 if a directory is
  created. Filesystems raise that flag individually and we need to check
  whether each filesystem can deal with receiving S_IFDIR from the vfs
  before we enforce a non-zero type.

It is safe to do this clean-up except that three filesystems (fuse,
cifs, and coda) forward the mkdir @mode unchanged to something outside
the kernel. Mask S_IFDIR back out in coda_mkdir(), fuse_mkdir() and
cifs_mkdir() so that what is sent outside the kernel is unchanged.
Their maintainers can drop the mask once they have confirmed it is safe.

Assisted-by: LLM
Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl>
Link: https://patch.msgid.link/20260630105400.68459-2-jkoolstra@xs4all.nl
Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-07-01 12:47:13 +02:00
Linus Torvalds
e8a56d6fc8 dentry memory safety stuff
* d_alloc_parallel() API change (Neil's with my changes).
  * NORCU fixes.
  * Reorganization and simplification of dentry eviction logics.
  * Simplifying rcu_read_lock() scopes in fs/dcache.c.
  * Secondary roots work - getting rid of NFS fake root dentries and
  dealing with remaining shrink_dcache_for_umount()/shrink_dentry_list()
  races.
  * making cursors NORCU (surprisingly easy)
 
 Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQqUNBr3gm4hGXdBJlZ7Krx/gZQ6wUCai8ppAAKCRBZ7Krx/gZQ
 67xXAP48lSBS6WyiTztk/ZD/0WFO+EemnxEDXZ0s55ejQ1/uswEApWNMaxBYkzxB
 0lgAjKPojjtqYovttP/AHklomSLzMA4=
 =llw0
 -----END PGP SIGNATURE-----

Merge tag 'pull-dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull dcache updates from Al Viro:

 - d_alloc_parallel() API change (Neil's with my changes)

 - NORCU fixes

 - Reorganization and simplification of dentry eviction logic

 - Simplifying rcu_read_lock() scopes in fs/dcache.c

 - Secondary roots work - getting rid of NFS fake root dentries and
   dealing with remaining shrink_dcache_for_umount() and
   shrink_dentry_list() races

 - making cursors NORCU (surprisingly easy)

* tag 'pull-dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (22 commits)
  make cursors NORCU
  nfs: get rid of fake root dentries
  wind ->s_roots via ->d_sib instead of ->d_hash
  shrink_dentry_tree(): unify the calls of shrink_dentry_list()
  shrinking rcu_read_lock() scope in d_alloc_parallel()
  d_walk(): shrink rcu_read_lock() scope
  document dentry_kill()
  adjust calling conventions of lock_for_kill(), fold __dentry_kill() into dentry_kill()
  Document rcu_read_lock() use in select_collect2()
  Shift rcu_read_{,un}lock() inside fast_dput()
  simplify safety for lock_for_kill() slowpath
  fold lock_for_kill() and __dentry_kill() into common helper
  fold lock_for_kill() into shrink_kill()
  shrink_dentry_list(): start with removing from shrink list
  d_prune_aliases(): make sure to skip NORCU aliases
  kill d_dispose_if_unused()
  make to_shrink_list() return whether it has moved dentry to list
  select_collect(): ignore dentries on shrink lists if they have positive refcounts
  find_acceptable_alias(): skip NORCU aliases with zero refcount
  fix a race between d_find_any_alias() and final dput() of NORCU dentries
  ...
2026-06-15 04:15:31 +05:30
Linus Torvalds
7e0e7bd60d vfs-7.2-rc1.misc
Please consider pulling these changes from the signed vfs-7.2-rc1.misc tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaiwLKgAKCRCRxhvAZXjc
 ou/zAP9SOUE6n58i0BdhLYw0RA9Ge5tz42e4inSFi4tkgfCrDwEAlxbRHDcMyhWB
 dHmx8OW6b5riMrW+lGPMH58RMoTkdQ4=
 =3vuc
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.2-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull misc vfs updates from Christian Brauner:
 "Features:

   - Reduce pipe->mutex contention by pre-allocating pages outside the
     lock in anon_pipe_write().

     anon_pipe_write() called alloc_page() once per page while holding
     pipe->mutex. The allocation can sleep doing direct reclaim and runs
     memcg charging, which extends the critical section and stalls any
     concurrent reader on the same mutex. Now up to 8 pages are
     pre-allocated before the mutex is taken, leftovers are recycled
     into the per-pipe tmp_page[] cache before unlock, and any remainder
     is released after unlock, keeping the allocator out of the critical
     section on both sides. On a writers x readers sweep with 64KB
     writes against a 1 MB pipe throughput improves 6-28% and average
     write latency drops 5-22%; under memory pressure - when the cost of
     holding the mutex across reclaim is highest - throughput improves
     21-48% and latency drops 17-33%. The microbenchmark is added to
     selftests.

   - uaccess/sockptr: fix the ignored_trailing logic in
     copy_struct_to_user() to behave as documented and the usize check
     in copy_struct_from_sockptr() for user pointers, and add
     copy_struct_{from,to}_bounce_buffer() and copy_struct_to_sockptr()
     helpers for upcoming users (IPPROTO_SMBDIRECT, IPPROTO_QUIC).

   - bpf: add a sleepable bpf_real_inode() kfunc that resolves the real
     inode backing a dentry via d_real_inode(). On overlayfs the inode
     attached to the dentry doesn't carry the underlying device
     information; this is used by the filesystem restriction BPF program
     that was merged into systemd.

   - docs: add guidelines for submitting new filesystems, motivated by
     the maintenance burden abandoned and untestable filesystems impose
     on VFS developers, blocking infrastructure work like folio
     conversions and iomap migration.

  Fixes:

   - libfs: set SB_I_NOEXEC and SB_I_NODEV by default in init_pseudo()
     and drop the now-redundant assignments in callers. This began as a
     one-line dma-buf fix for a path_noexec() warning; a pseudo
     filesystem has no reason not to set SB_I_NOEXEC. All init_pseudo()
     callers were audited: the only visible effect is on dma-buf where
     SB_I_NOEXEC silences the warning.

   - Handle set_blocksize() failures in legacy filesystems (bfs, hpfs,
     qnx4, jfs, befs, affs, isofs, minix, ntfs3, omfs). Mounting a
     device with a sector size > PAGE_SIZE crashed roughly half of them;
     the rest had the same missing error handling pattern. Plus a
     follow-up releasing the superblock buffer_head when setting the
     minix v3 block size fails.

   - mount: honour SB_NOUSER in the new mount API.

   - fs/fcntl: fix a SOFTIRQ-unsafe lock order in fasync signaling by
     switching the process-group paths of send_sigio() and send_sigurg()
     from read_lock(&tasklist_lock) to RCU, matching the single-PID
     path.

   - vfs: add an FS_USERNS_DELEGATABLE flag and set it for NFS, fixing
     delegated NFS mounts (fsopen() in a container with the mount
     performed by a privileged daemon) that broke when non-init
     s_user_ns was tied to FS_USERNS_MOUNT.

   - selftests/namespaces: fix a hang in nsid_test where an unreaped
     grandchild kept the TAP pipe write-end open, a waitpid(-1) race in
     listns_efault_test, and a false FAIL on kernels without listns()
     where the tests should SKIP.

   - filelock: fix the break_lease() stub signature for
     CONFIG_FILE_LOCKING=n.

   - init/initramfs_test: wait for the async initramfs unpacking before
     running; the test and do_populate_rootfs() share the parser state.

   - fs/coredump: reduce redundant log noise in
     validate_coredump_safety().

   - iomap: pass the correct length to fserror_report_io() in
     __iomap_write_begin().

   - backing-file: fix the backing_file_open() kerneldoc.

  Cleanups:

   - initramfs: refactor the cpio hex header parsing to use hex2bin()
     instead of the hand-rolled simple_strntoul() which is reverted, and
     extend the initramfs KUnit tests to cover header fields with 0x
     prefixes.

   - Replace __get_free_pages() and friends with kmalloc()/kzalloc()
     across quota, proc, ocfs2/dlm, nilfs2, nfs, nfsd, libfs, jfs, jbd2,
     isofs, fuse, select, namespace, configfs, binfmt_misc, bfs, and the
     do_mounts init code - part of the larger work of replacing page
     allocator calls with kmalloc().

   - Use clear_and_wake_up_bit() in unlock_buffer() and
     journal_end_buffer_io_sync() instead of open-coding the sequence.

   - Drop unused VFS exports: unexport drop_super_exclusive(), remove
     start_removing_user_path_at(), and fold __start_removing_path()
     into start_removing_path().

   - fs/read_write: narrow the __kernel_write() export with
     EXPORT_SYMBOL_FOR_MODULES().

   - vfs: uapi: retire octal and hex constants in favor of (1 << n) for
     the O_ flags. Finding a free bit for a new flag across the
     architectures was needlessly hard with the mixed bases.

   - dcache: add extra sanity checks of dead dentries in dentry_free()
     via a new DENTRY_WARN_ONCE() that also prints d_flags.

   - iov_iter: use kmemdup_array() in dup_iter() to harden the
     allocation against multiplication overflow.

   - fs/pipe: write to ->poll_usage only once.

   - vfs: remove an always-taken if-branch in find_next_fd().

   - dcache: use kmalloc_flex() for struct external_name in __d_alloc().

   - namei: use QSTR() instead of QSTR_INIT() in path_pts().

   - sync_file_range: delete dead S_ISLNK code.

   - Comment fixes: retire a stale comment in fget_task_next() and fix
     assorted spelling mistakes"

* tag 'vfs-7.2-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (73 commits)
  backing-file: fix backing_file_open() kerneldoc parameter
  iomap: pass the correct len to fserror_report_io in __iomap_write_begin
  vfs: add FS_USERNS_DELEGATABLE flag and set it for NFS
  filelock: fix break_lease() stub signature for CONFIG_FILE_LOCKING=n
  vfs: uapi: retire octal and hex numbers in favor of (1 << n) for O_ flags
  bpf: add bpf_real_inode() kfunc
  fs/read_write: Do not export __kernel_write() to the entire world
  libfs: drop redundant SB_I_NOEXEC/SB_I_NODEV in init_pseudo() callers
  libfs: set SB_I_NOEXEC and SB_I_NODEV by default in init_pseudo()
  mount: honour SB_NOUSER in the new mount API
  fs/fcntl: fix SOFTIRQ-unsafe lock order in fasync signaling
  selftests/pipe: add pipe_bench microbenchmark
  fs/pipe: pre-allocate pages outside pipe->mutex in anon_pipe_write
  fs: retire stale comment in fget_task_next()
  fs: fix spelling mistakes in comment
  bfs: replace get_zeroed_page() with kzalloc()
  binfmt_misc: replace __get_free_page() with kmalloc()
  configfs: replace __get_free_pages() with kzalloc()
  fs/namespace: use __getname() to allocate mntpath buffer
  fs/select: replace __get_free_page() with kmalloc()
  ...
2026-06-15 03:59:45 +05:30
Linus Torvalds
50b900c564 vfs-7.2-rc1.openat2
Please consider pulling these changes from the signed vfs-7.2-rc1.openat2 tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaiwLKgAKCRCRxhvAZXjc
 ogT3AQCKd2J6Qz6vsVaHK8Bo9oE06Px4v2NGeu+FgCV0QQMpJQD+KTEnVl4ZepJu
 JsEw202uWeQw9Aj/SoD1oyuYO0ZIrQw=
 =rQxh
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.2-rc1.openat2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull openat2 updates from Christian Brauner:
 "Features:

   - Add O_EMPTYPATH to openat(2)/openat2(2). To get an operable file
     descriptor from an O_PATH file descriptor it is possible to use
     openat(fd, ".", O_DIRECTORY) for directories, but other file types
     require going through open("/proc/<pid>/fd/<nr>") and thus depend
     on a functioning procfs.

     With O_EMPTYPATH an empty path string is accepted and LOOKUP_EMPTY
     is set at path resolution time, allowing to reopen the file behind
     the file descriptor directly. Selftests are included.

   - Add an OPENAT2_REGULAR flag for openat2(2) which refuses to open
     anything but regular files with the new EFTYPE error code.

     This implements the "ability to only open regular files" feature
     requested by userspace via uapi-group.org and protects services
     from being redirected to fifos, device nodes, and friends.

     All atomic_open implementations were audited for OPENAT2_REGULAR
     handling. Explicit checks were added to ceph, gfs2, nfs (v4), and
     cifs/smb - these are the filesystems whose atomic_open can
     encounter an existing non-regular file and would otherwise call
     finish_open() on it or return a misleading error code.

     The remaining implementations (9p, fuse, vboxsf, nfs v2/v3) only
     call finish_open() on freshly created files and use
     finish_no_open() for lookup hits, letting the VFS catch non-regular
     files via the do_open() safety net.

  Cleanups:

   - Migrate the openat2 selftests to the kselftest harness and move
     them under selftests/filesystems/. The tests were written in the
     early days of selftests' TAP support and the modern kselftest
     harness is much easier to follow and maintain. The contents of the
     tests are unchanged and the new emptypath tests are ported on top.

   - Make the LAST_XXX last-type constants private to fs/namei.c. The
     only user outside of fs/namei.c was ksmbd which only needs to know
     whether the last component is a regular one, so
     vfs_path_parent_lookup() now performs the LAST_NORM check
     internally. The ints are replaced with a dedicated enum last_type"

* tag 'vfs-7.2-rc1.openat2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  vfs: replace ints with enum last_type for LAST_XXX
  vfs: make LAST_XXX private to fs/namei.c
  selftests: openat2: port emptypath_test to kselftest harness
  kselftest/openat2: test for OPENAT2_REGULAR flag
  openat2: new OPENAT2_REGULAR flag support
  openat2: introduce EFTYPE error code
  selftest: add tests for O_EMPTYPATH
  vfs: add O_EMPTYPATH to openat(2)/openat2(2)
  selftests: openat2: migrate to kselftest harness
  selftests: openat2: switch from custom ARRAY_LEN to ARRAY_SIZE
  selftests: openat2: move helpers to header
  selftests: move openat2 tests to selftests/filesystems/
2026-06-15 03:11:05 +05:30
Linus Torvalds
6b5b72ac2c vfs-7.2-rc1.directory.delegations
Please consider pulling these changes from the signed vfs-7.2-rc1.directory.delegations tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaiwLKQAKCRCRxhvAZXjc
 opMRAQD9zbiUu66CHNd49yD3sFtuG8uM7I2VuIoczgW+qKka5QEA8HBVqMtdurZf
 eEcj415FqHQNX1W3jytjU1jYjiwmtgY=
 =Ahl/
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.2-rc1.directory.delegations' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs directory delegations from Christian Brauner:
 "This contains the VFS prerequisites for supporting directory
  delegations in nfsd via CB_NOTIFY callbacks.

  The filelock core gains support for ignoring delegation breaks for
  directory change events together with an inode_lease_ignore_mask()
  helper, and fsnotify gains fsnotify_modify_mark_mask() and a
  FSNOTIFY_EVENT_RENAME data type.

  With this in place nfsd can request delegations on directories and set
  up inotify watches to trigger sending CB_NOTIFY events to clients
  instead of having every directory change break the delegation.

  New tracepoints are added to fsnotify() and to the start of
  break_lease(), and trace_break_lease_block() is passed the currently
  blocking lease instead of the new one.

  A follow-up fix moves the LEASE_BREAK_* flags out of
  #ifdef CONFIG_FILE_LOCKING to fix the build for CONFIG_FILE_LOCKING=n
  configurations"

* tag 'vfs-7.2-rc1.directory.delegations' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  filelock: move LEASE_BREAK_* flags out of #ifdef CONFIG_FILE_LOCKING
  fsnotify: add FSNOTIFY_EVENT_RENAME data type
  fsnotify: add fsnotify_modify_mark_mask()
  fsnotify: new tracepoint in fsnotify()
  filelock: add an inode_lease_ignore_mask helper
  filelock: add a tracepoint to start of break_lease()
  filelock: add support for ignoring deleg breaks for dir change events
  filelock: pass current blocking lease to trace_break_lease_block() rather than "new_fl"
2026-06-15 02:50:44 +05:30
NeilBrown
0e0e490f5d VFS: use wait_var_event for waiting in d_alloc_parallel()
Parallel lookup starts with a call of d_alloc_parallel().  That primitive
either returns a matching hashed dentry or allocates a new one in the
in-lookup state and returns it to the caller.  Once the caller is done
with lookup, it indicates so either by call of d_{splice_alias,add}()
or by call of d_done_lookup(); at that point dentry leaves the in-lookup
state.

If d_alloc_parallel() finds a matching in-lookup dentry, it must wait for
that dentry to leave the in-lookup state, one way or another.  Currently
by supplying wait_queue_head to d_alloc_parallel().  If d_alloc_parallel()
creates a new in-lookup dentry, the address of that wait_queue_head is stored
in ->d_wait of new dentry and stays there while it's in the in-lookup;
subsequent d_alloc_parallel() will wait on the queue found in the matching
in-lookup dentry.  Transition out of in-lookup state wakes waiters on that
queue (if any).

That works, but the calling conventions are inconvenient - the caller must
supply wait_queue_head and make sure that it survives at least until the new
in-lookup dentry leaves the in-lookup state.  That amounts to boilerplate
in the d_alloc_parallel() callers that are followed by a call of d_lookup_done()
in the same function; in cases like nfs asynchronous unlink it gets worse than
that.

This patch changes d_alloc_parallel() to use wake_up_var_locked() to
wake up waiters, and wait_var_event_spinlock() to wait.  dentry->d_lock
is used for synchronisation as it is already held and the relevant
times.

That eliminates the need of caller-supplied wait_queue_head, simplifying
the calling conventions.  Better yet, we only need one bit of information
stored in dentry itself: whether there are any waiters to be woken up,
and that can be easily stored in ->d_flags; ->d_wait goes away.

The reason we need that bit (DCACHE_LOOKUP_WAITERS) is that with wait_var
machinery the queues are shared with all kinds of stuff and there's
no way tell if any of the waiters have anything to do with our dentry;
most of the time none of them will be relevant, so we need to avoid the
pointless wakeups.

Another benefit of the new scheme comes from the fact that wakeups
have to be done outside of write-side critical areas of ->i_dir_seq;
with the old scheme we need to carry the value picked from ->d_wait from
__d_lookup_unhash() to the place where we actually wake the waiters up.
Now we can just leave DCACHE_LOOKUP_WAITERS in ->d_flags until we get
to doing wakeups - that's done within the same ->d_lock scope, so we
are fine; new bit is accessed only under ->d_lock and it's seen only
on dentries with DCACHE_PAR_LOOKUP in ->d_flags.

__d_lookup_unhash() no longer needs to re-init ->d_lru.  That was
previously shared (in a union) with ->d_wait but ->d_wait is now gone
so it no longer corrupts ->d_lru.

Co-developed-by: Al Viro <viro@zeniv.linux.org.uk> # saner handling of flags
Signed-off-by: NeilBrown <neil@brown.name>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2026-06-05 00:34:54 -04:00
Jori Koolstra
318643721d
vfs: replace ints with enum last_type for LAST_XXX
Several functions in namei.c take an "int *type" parameter, such as
filename_parentat(). To know what values this can take you have to find
the anonymous struct that defines the LAST_XXX values. Define an enum
last_type to make this type explicit.

Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl>
Link: https://patch.msgid.link/20260528175854.57626-2-jkoolstra@xs4all.nl
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-29 09:47:02 +02:00
Jori Koolstra
f2f1dddcca
vfs: make LAST_XXX private to fs/namei.c
The only user of LAST_XXX outside of fs/namei.c is fs/smb/server/vfs.c;
ksmbd_vfs_path_lookup() calls vfs_path_parent_lookup() and expects a
LAST_NORM last type (or it will be ENOENT). ksmbd_vfs_rename() also calls
vfs_path_parent_lookup() but forgets the LAST_NORM check.

It does not really make sense to have vfs_path_parent_lookup() expose
the last_type because it is only needed to ensure it is LAST_NORM. So
let's do this check in vfs_path_parent_lookup() instead and keep the
LAST_XXX internal to fs/namei.c. This changes the ENOENT errno in
ksmbd_vfs_path_lookup() to EINVAL, which matches better with how this is
handled by callers of filename_parentat().

Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl>
Link: https://patch.msgid.link/20260528175854.57626-1-jkoolstra@xs4all.nl
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-29 09:47:02 +02:00
NeilBrown
e824bbd4d2
VFS: fix possible failure to unlock in nfsd4_create_file()
atomic_create() in fs/namei.c drops the reference to the dentry
when it returns an error.
This behaviour was imported into dentry_create() so that it
will drop the reference if an error is returned from atomic_create(),
though not if vfs_create() returns an error (in the case where
->atomic_create is not supported).

The caller - nfsd4_create_file() - is made aware of this by checking
path->dentry, which will either be a counted reference to a dentry, or
an error pointer.

However the change to use start_creating()/end_creating() (which landed
shortly before the dentry_create() change landed, though was likely
developed around the same time) means that nfsd4_create_file() *needs* a
valid dentry so that it can unlock the parent.

The net result is that if NFSD exports a filesystem which uses
->atomic_create, and if a call to ->atomic_create returns an error, then
nfsd4_create_file() will pass an error pointer to end_creating()
and the parent will not be unlocked.

Fix this by changing dentry_create() to make sure path->dentry is always
a valid dentry, never an error-pointer.  The actual error is already
returned a different way.

Note that if ->atomic_create() returns a different dentry (which may not
be possible in practice) we are guaranteed (because it is only ever
provided by d_spliace_alias()) that it will have the same d_parent and
so it will have the same effect when passed to end_creating().

Fixes: 64a989dbd1 ("VFS/knfsd: Teach dentry_create() to use atomic_open()")
Signed-off-by: NeilBrown <neil@brown.name>
Link: https://patch.msgid.link/177969022571.3379282.16448744624428323496@noble.neil.brown.name
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Benjamin Coddington <bcodding@hammerspace.com>
Reviewed-by: Jori Koolstra <jkoolstra@xs4all.nl>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-28 14:14:04 +02:00
Dorjoy Chowdhury
8b82cacad9
openat2: new OPENAT2_REGULAR flag support
This flag indicates the path should be opened if it's a regular file.
This is useful to write secure programs that want to avoid being
tricked into opening device nodes with special semantics while thinking
they operate on regular files. This is a requested feature from the
uapi-group[1].

The previously introduced EFTYPE error code is returned when the path
doesn't refer to a regular file. For example, if openat2 is called on
path /dev/null with OPENAT2_REGULAR in the flag param, it will return
-EFTYPE.

When used in combination with O_CREAT, either the regular file is
created, or if the path already exists, it is opened if it's a regular
file. Otherwise, -EFTYPE is returned.

When OPENAT2_REGULAR is combined with O_DIRECTORY, -EINVAL is returned
as it doesn't make sense to open a path that is both a directory and a
regular file.

The UAPI bit lives in the upper 32 bits of open_how::flags
(((__u64)1 << 32)) so that open(2) and openat(2) -- whose @flags
argument is a C int -- cannot physically express it. This is a
structural guarantee, not a runtime mask: the bit is unrepresentable in
32 bits.

Because the rest of the VFS open path narrows to 32 bits in several
places (op->open_flag, f->f_flags, the unsigned open_flag argument of
i_op->atomic_open()), build_open_flags() translates OPENAT2_REGULAR
into a kernel-internal lower-32-bit carrier __O_REGULAR (bit 4, unused
as an O_* on every architecture) before the assignment to op->open_flag.
__O_REGULAR then rides through the existing channels exactly like
__FMODE_EXEC. do_dentry_open() strips it so it cannot leak back to
userspace via fcntl(F_GETFL).

Four BUILD_BUG_ON_MSG() invariants in build_open_flags() prevent any
future bit collision or accidental low-32 redefinition:

  - VALID_OPEN_FLAGS fits in 32 bits.
  - OPENAT2_REGULAR lives in the upper 32 bits.
  - OPENAT2_REGULAR does not alias any open()/openat() flag.
  - __O_REGULAR does not alias any user-visible flag.

[1]: https://uapi-group.org/kernel-features/#ability-to-only-open-regular-files

Christian Brauner <brauner@kernel.org> says:

Move OPENAT2_REGULAR to the upper 32 bits of open_how::flags with a
kernel-internal __O_REGULAR carrier so that open(2)/openat(2) cannot
encode the flag; add BUILD_BUG_ON_MSG() invariants and register
__O_REGULAR in the fcntl_init() allocation-uniqueness BUILD_BUG_ON()
(bit count 21 -> 22).

Signed-off-by: Dorjoy Chowdhury <dorjoychy111@gmail.com>
Link: https://patch.msgid.link/20260328172314.45807-2-dorjoychy111@gmail.com
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Aleksa Sarai <aleksa@amutable.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-21 15:33:47 +02:00
Thorsten Blum
de7680d943
namei: use QSTR() instead of QSTR_INIT() in path_pts
Drop the hard-coded length argument and use the simpler QSTR(). Inline
the code and drop the local variable.

Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
Link: https://patch.msgid.link/20260505114242.158883-2-thorsten.blum@linux.dev
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-21 13:39:36 +02:00
Christoph Hellwig
5d833d8ba5
fs: fold __start_removing_path into start_removing_path
Only one caller left, and simplified this way.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Link: https://patch.msgid.link/20260511072239.2456725-4-hch@lst.de
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-21 13:39:34 +02:00
Christoph Hellwig
e6666aef11
fs: remove start_removing_user_path_at
This function is entirely unused, remove it.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Link: https://patch.msgid.link/20260511072239.2456725-3-hch@lst.de
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-05-21 13:39:34 +02:00
Jeff Layton
24cbf43337
filelock: add support for ignoring deleg breaks for dir change events
If a NFS client requests a directory delegation with a notification
bitmask covering directory change events, the server shouldn't recall
the delegation. Instead the client will be notified of the change after
the fact.

Add support for ignoring lease breaks on directory changes. Add a new
flags parameter to try_break_deleg() and teach __break_lease how to
ignore certain types of delegation break events.

Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260428-dir-deleg-v3-2-5a0780ba9def@kernel.org
Acked-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-05-15 19:24:32 +02:00
Linus Torvalds
ef3da345cc vfs-7.1-rc1.misc
Please consider pulling these changes from the signed vfs-7.1-rc1.misc tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCadjZCwAKCRCRxhvAZXjc
 ohhBAQCAmQMlMRAXAgUZFYMTZpeQlcujP5rv+/vT2Tf/xS76YwD/dRDaw1FH294+
 qtk/Z1NjleNixzE2sld1K9J32NxeyAc=
 =+g9q
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.1-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull misc vfs updates from Christian Brauner:
 "Features:
   - coredump: add tracepoint for coredump events
   - fs: hide file and bfile caches behind runtime const machinery

  Fixes:
   - fix architecture-specific compat_ftruncate64 implementations
   - dcache: Limit the minimal number of bucket to two
   - fs/omfs: reject s_sys_blocksize smaller than OMFS_DIR_START
   - fs/mbcache: cancel shrink work before destroying the cache
   - dcache: permit dynamic_dname()s up to NAME_MAX

  Cleanups:
   - remove or unexport unused fs_context infrastructure
   - trivial ->setattr cleanups
   - selftests/filesystems: Assume that TIOCGPTPEER is defined
   - writeback: fix kernel-doc function name mismatch for wb_put_many()
   - autofs: replace manual symlink buffer allocation in autofs_dir_symlink
   - init/initramfs.c: trivial fix: FSM -> Finite-state machine
   - fs: remove stale and duplicate forward declarations
   - readdir: Introduce dirent_size()
   - fs: Replace user_access_{begin/end} by scoped user access
   - kernel: acct: fix duplicate word in comment
   - fs: write a better comment in step_into() concerning .mnt assignment
   - fs: attr: fix comment formatting and spelling issues"

* tag 'vfs-7.1-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (28 commits)
  dcache: permit dynamic_dname()s up to NAME_MAX
  fs: attr: fix comment formatting and spelling issues
  fs: hide file and bfile caches behind runtime const machinery
  fs: write a better comment in step_into() concerning .mnt assignment
  proc: rename proc_notify_change to proc_setattr
  proc: rename proc_setattr to proc_nochmod_setattr
  affs: rename affs_notify_change to affs_setattr
  adfs: rename adfs_notify_change to adfs_setattr
  hfs: update comments on hfs_inode_setattr
  kernel: acct: fix duplicate word in comment
  fs: Replace user_access_{begin/end} by scoped user access
  readdir: Introduce dirent_size()
  coredump: add tracepoint for coredump events
  fs: remove do_sys_truncate
  fs: pass on FTRUNCATE_* flags to do_truncate
  fs: fix archiecture-specific compat_ftruncate64
  fs: remove stale and duplicate forward declarations
  init/initramfs.c: trivial fix: FSM -> Finite-state machine
  autofs: replace manual symlink buffer allocation in autofs_dir_symlink
  fs/mbcache: cancel shrink work before destroying the cache
  ...
2026-04-13 14:20:11 -07:00
Linus Torvalds
3383589700 vfs-7.1-rc1.directory
Please consider pulling these changes from the signed vfs-7.1-rc1.directory tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCadjZCgAKCRCRxhvAZXjc
 oj2yAQDHLWMfxDr8INpBuGc09tqX04qyb7td3U+zfM5c3bvLLAD/eDFSSUvzLtPD
 u540EWvTaJBV5ALUx3vQK96PPvS6Vg4=
 =20jQ
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.1-rc1.directory' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs directory updates from Christian Brauner:
 "Recently 'start_creating', 'start_removing', 'start_renaming' and
  related interfaces were added which combine the locking and the
  lookup.

  At that time many callers were changed to use the new interfaces.
  However there are still an assortment of places out side of the core
  vfs where the directory is locked explictly, whether with inode_lock()
  or lock_rename() or similar. These were missed in the first pass for
  an assortment of uninteresting reasons.

  This addresses the remaining places where explicit locking is used,
  and changes them to use the new interfaces, or otherwise removes the
  explicit locking.

  The biggest changes are in overlayfs. The other changes are quite
  simple, though maybe the cachefiles changes is the least simple of
  those"

* tag 'vfs-7.1-rc1.directory' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  VFS: unexport lock_rename(), lock_rename_child(), unlock_rename()
  ovl: remove ovl_lock_rename_workdir()
  ovl: use is_subdir() for testing if one thing is a subdir of another
  ovl: change ovl_create_real() to get a new lock when re-opening created file.
  ovl: pass name buffer to ovl_start_creating_temp()
  cachefiles: change cachefiles_bury_object to use start_renaming_dentry()
  ovl: Simplify ovl_lookup_real_one()
  VFS: make lookup_one_qstr_excl() static.
  nfsd: switch purge_old() to use start_removing_noperm()
  selinux: Use simple_start_creating() / simple_done_creating()
  Apparmor: Use simple_start_creating() / simple_done_creating()
  libfs: change simple_done_creating() to use end_creating()
  VFS: move the start_dirop() kerndoc comment to before start_dirop()
  fs/proc: Don't lock root inode when creating "self" and "thread-self"
  VFS: note error returns in documentation for various lookup functions
2026-04-13 10:24:33 -07:00
Mateusz Guzik
18f2e0ea20
fs: write a better comment in step_into() concerning .mnt assignment
Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
Link: https://patch.msgid.link/20260328175841.3390950-1-mjguzik@gmail.com
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-03-31 11:43:01 +02:00
Jori Koolstra
8fb6857f2f
vfs: fix docstring of hash_name()
The docstring of hash_name() is falsely reporting that it returns the
component length, whereas it returns a pointer to the terminating '/'
or NUL character in the pathname being resolved.

Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl>
Link: https://patch.msgid.link/20260318203953.5770-1-jkoolstra@xs4all.nl
Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-03-19 11:18:01 +01:00
NeilBrown
4d94ce88c7
VFS: unexport lock_rename(), lock_rename_child(), unlock_rename()
These three function are now only used in namei.c, so they don't need to
be exported.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
Link: https://patch.msgid.link/20260224222542.3458677-16-neilb@ownmail.net
Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-03-09 09:43:04 +01:00
NeilBrown
336faf5d91
VFS: make lookup_one_qstr_excl() static.
lookup_one_qstr_excl() is no longer used outside of namei.c, so
make it static.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
Link: https://patch.msgid.link/20260224222542.3458677-9-neilb@ownmail.net
Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-03-06 10:24:12 +01:00
NeilBrown
4eb94abd6b
VFS: move the start_dirop() kerndoc comment to before start_dirop()
This kerneldoc comment was always meant for start_dirop(), not for
__start_dirop() which is a static function and doesn't need
documentation.

It was in the wrong place and was then incorrectly renamed (instead of
moved) and useless "documentation" was added for "@state" was provided.

This patch reverts the name, removes the mention of @state, and moves
the comment to where it belongs.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
Link: https://patch.msgid.link/20260224222542.3458677-4-neilb@ownmail.net
Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-03-06 10:24:11 +01:00
NeilBrown
c025a53314
VFS: note error returns in documentation for various lookup functions
Darrick recently noted that try_lookup_noperm() is documented as
"Look up a dentry by name in the dcache, returning NULL if it does not
currently exist." but it can in fact return an error.

So update the documentation for that and related functions.

Link: https://lore.kernel.org/all/20260218234917.GA6490@frogsfrogsfrogs/
Cc: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
Link: https://patch.msgid.link/20260224222542.3458677-2-neilb@ownmail.net
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-03-06 10:24:11 +01:00
Kees Cook
69050f8d6d treewide: Replace kmalloc with kmalloc_obj for non-scalar types
This is the result of running the Coccinelle script from
scripts/coccinelle/api/kmalloc_objs.cocci. The script is designed to
avoid scalar types (which need careful case-by-case checking), and
instead replace kmalloc-family calls that allocate struct or union
object instances:

Single allocations:	kmalloc(sizeof(TYPE), ...)
are replaced with:	kmalloc_obj(TYPE, ...)

Array allocations:	kmalloc_array(COUNT, sizeof(TYPE), ...)
are replaced with:	kmalloc_objs(TYPE, COUNT, ...)

Flex array allocations:	kmalloc(struct_size(PTR, FAM, COUNT), ...)
are replaced with:	kmalloc_flex(*PTR, FAM, COUNT, ...)

(where TYPE may also be *VAR)

The resulting allocations no longer return "void *", instead returning
"TYPE *".

Signed-off-by: Kees Cook <kees@kernel.org>
2026-02-21 01:02:28 -08:00
Linus Torvalds
45a43ac5ac vfs-7.0-rc1.misc.2
Please consider pulling these changes from the signed vfs-7.0-rc1.misc.2 tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaZMOCwAKCRCRxhvAZXjc
 oswrAP9r1zjzMimjX2J0hBoMnYjNzQfLLew8+IRygImQ+yaqWgD9Fiw/cQ9eE1Hm
 TMLqck/ky588ywSDaBzfztrXAY3ISgg=
 =4yr2
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.0-rc1.misc.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull more misc vfs updates from Christian Brauner:
 "Features:

   - Optimize close_range() from O(range size) to O(active FDs) by using
     find_next_bit() on the open_fds bitmap instead of linearly scanning
     the entire requested range. This is a significant improvement for
     large-range close operations on sparse file descriptor tables.

   - Add FS_XFLAG_VERITY file attribute for fs-verity files, retrievable
     via FS_IOC_FSGETXATTR and file_getattr(). The flag is read-only.
     Add tracepoints for fs-verity enable and verify operations,
     replacing the previously removed debug printk's.

   - Prevent nfsd from exporting special kernel filesystems like pidfs
     and nsfs. These filesystems have custom ->open() and ->permission()
     export methods that are designed for open_by_handle_at(2) only and
     are incompatible with nfsd. Update the exportfs documentation
     accordingly.

  Fixes:

   - Fix KMSAN uninit-value in ovl_fill_real() where strcmp() was used
     on a non-null-terminated decrypted directory entry name from
     fscrypt. This triggered on encrypted lower layers when the
     decrypted name buffer contained uninitialized tail data.

     The fix also adds VFS-level name_is_dot(), name_is_dotdot(), and
     name_is_dot_dotdot() helpers, replacing various open-coded "." and
     ".." checks across the tree.

   - Fix read-only fsflags not being reset together with xflags in
     vfs_fileattr_set(). Currently harmless since no read-only xflags
     overlap with flags, but this would cause inconsistencies for any
     future shared read-only flag

   - Return -EREMOTE instead of -ESRCH from PIDFD_GET_INFO when the
     target process is in a different pid namespace. This lets userspace
     distinguish "process exited" from "process in another namespace",
     matching glibc's pidfd_getpid() behavior

  Cleanups:

   - Use C-string literals in the Rust seq_file bindings, replacing the
     kernel::c_str!() macro (available since Rust 1.77)

   - Fix typo in d_walk_ret enum comment, add porting notes for the
     readlink_copy() calling convention change"

* tag 'vfs-7.0-rc1.misc.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  fs: add porting notes about readlink_copy()
  pidfs: return -EREMOTE when PIDFD_GET_INFO is called on another ns
  nfsd: do not allow exporting of special kernel filesystems
  exportfs: clarify the documentation of open()/permission() expotrfs ops
  fsverity: add tracepoints
  fs: add FS_XFLAG_VERITY for fs-verity files
  rust: seq_file: replace `kernel::c_str!` with C-Strings
  fs: dcache: fix typo in enum d_walk_ret comment
  ovl: use name_is_dot* helpers in readdir code
  fs: add helpers name_is_dot{,dot,_dotdot}
  ovl: Fix uninit-value in ovl_fill_real
  fs: reset read-only fsflags together with xflags
  fs/file: optimize close_range() complexity from O(N) to O(Sparse)
2026-02-16 13:00:36 -08:00
Linus Torvalds
26c9342bb7 struct filename series
[mostly] sanitize struct filename hanling
 
 Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQqUNBr3gm4hGXdBJlZ7Krx/gZQ6wUCaYlcJgAKCRBZ7Krx/gZQ
 6xlKAP9c9J13sJ/mcobsj1Ov7nSHISNbnYqvRRCu09Wq3UQvJgEApNQYOEdLtpff
 zUnWOAQ0nOKY7w9VMLkRRustXpuGjAc=
 =Fld4
 -----END PGP SIGNATURE-----

Merge tag 'pull-filename' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull vfs 'struct filename' updates from Al Viro:
 "[Mostly] sanitize struct filename handling"

* tag 'pull-filename' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (68 commits)
  sysfs(2): fs_index() argument is _not_ a pathname
  alpha: switch osf_mount() to strndup_user()
  ksmbd: use CLASS(filename_kernel)
  mqueue: switch to CLASS(filename)
  user_statfs(): switch to CLASS(filename)
  statx: switch to CLASS(filename_maybe_null)
  quotactl_block(): switch to CLASS(filename)
  chroot(2): switch to CLASS(filename)
  move_mount(2): switch to CLASS(filename_maybe_null)
  namei.c: switch user pathname imports to CLASS(filename{,_flags})
  namei.c: convert getname_kernel() callers to CLASS(filename_kernel)
  do_f{chmod,chown,access}at(): use CLASS(filename_uflags)
  do_readlinkat(): switch to CLASS(filename_flags)
  do_sys_truncate(): switch to CLASS(filename)
  do_utimes_path(): switch to CLASS(filename_uflags)
  chdir(2): unspaghettify a bit...
  do_fchownat(): unspaghettify a bit...
  fspick(2): use CLASS(filename_flags)
  name_to_handle_at(): use CLASS(filename_uflags)
  vfs_open_tree(): use CLASS(filename_uflags)
  ...
2026-02-09 16:58:28 -08:00
Linus Torvalds
9e355113f0 vfs-7.0-rc1.misc
Please consider pulling these changes from the signed vfs-7.0-rc1.misc tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaYX49QAKCRCRxhvAZXjc
 ojrZAQD1VJzY46r5FnAVf4jlEHyjIbDnZCP/n+c4x6XnqpU6EQEAgB0yAtAGP6+u
 SBuytElqHoTT5VtmEXTAabCNQ9Ks8wo=
 =JwZz
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.0-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull misc vfs updates from Christian Brauner:
 "This contains a mix of VFS cleanups, performance improvements, API
  fixes, documentation, and a deprecation notice.

  Scalability and performance:

   - Rework pid allocation to only take pidmap_lock once instead of
     twice during alloc_pid(), improving thread creation/teardown
     throughput by 10-16% depending on false-sharing luck. Pad the
     namespace refcount to reduce false-sharing

   - Track file lock presence via a flag in ->i_opflags instead of
     reading ->i_flctx, avoiding false-sharing with ->i_readcount on
     open/close hot paths. Measured 4-16% improvement on 24-core
     open-in-a-loop benchmarks

   - Use a consume fence in locks_inode_context() to match the
     store-release/load-consume idiom, eliminating a hardware fence on
     some architectures

   - Annotate cdev_lock with __cacheline_aligned_in_smp to prevent
     false-sharing

   - Remove a redundant DCACHE_MANAGED_DENTRY check in
     __follow_mount_rcu() that never fires since the caller already
     verifies it, eliminating a 100% mispredicted branch

   - Fix a 100% mispredicted likely() in devcgroup_inode_permission()
     that became wrong after a prior code reorder

  Bug fixes and correctness:

   - Make insert_inode_locked() wait for inode destruction instead of
     skipping, fixing a corner case where two matching inodes could
     exist in the hash

   - Move f_mode initialization before file_ref_init() in alloc_file()
     to respect the SLAB_TYPESAFE_BY_RCU ordering contract

   - Add a WARN_ON_ONCE guard in try_to_free_buffers() for folios with
     no buffers attached, preventing a null pointer dereference when
     AS_RELEASE_ALWAYS is set but no release_folio op exists

   - Fix select restart_block to store end_time as timespec64, avoiding
     truncation of tv_sec on 32-bit architectures

   - Make dump_inode() use get_kernel_nofault() to safely access inode
     and superblock fields, matching the dump_mapping() pattern

  API modernization:

   - Make posix_acl_to_xattr() allocate the buffer internally since
     every single caller was doing it anyway. Reduces boilerplate and
     unnecessary error checking across ~15 filesystems

   - Replace deprecated simple_strtoul() with kstrtoul() for the
     ihash_entries, dhash_entries, mhash_entries, and mphash_entries
     boot parameters, adding proper error handling

   - Convert chardev code to use guard(mutex) and __free(kfree) cleanup
     patterns

   - Replace min_t() with min() or umin() in VFS code to avoid silently
     truncating unsigned long to unsigned int

   - Gate LOOKUP_RCU assertions behind CONFIG_DEBUG_VFS since callers
     already check the flag

  Deprecation:

   - Begin deprecating legacy BSD process accounting (acct(2)). The
     interface has numerous footguns and better alternatives exist
     (eBPF)

  Documentation:

   - Fix and complete kernel-doc for struct export_operations, removing
     duplicated documentation between ReST and source

   - Fix kernel-doc warnings for __start_dirop() and ilookup5_nowait()

  Testing:

   - Add a kunit test for initramfs cpio handling of entries with
     filesize > PATH_MAX

  Misc:

   - Add missing <linux/init_task.h> include in fs_struct.c"

* tag 'vfs-7.0-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (28 commits)
  posix_acl: make posix_acl_to_xattr() alloc the buffer
  fs: make insert_inode_locked() wait for inode destruction
  initramfs_test: kunit test for cpio.filesize > PATH_MAX
  fs: improve dump_inode() to safely access inode fields
  fs: add <linux/init_task.h> for 'init_fs'
  docs: exportfs: Use source code struct documentation
  fs: move initializing f_mode before file_ref_init()
  exportfs: Complete kernel-doc for struct export_operations
  exportfs: Mark struct export_operations functions at kernel-doc
  exportfs: Fix kernel-doc output for get_name()
  acct(2): begin the deprecation of legacy BSD process accounting
  device_cgroup: remove branch hint after code refactor
  VFS: fix __start_dirop() kernel-doc warnings
  fs: Describe @isnew parameter in ilookup5_nowait()
  fs/namei: Remove redundant DCACHE_MANAGED_DENTRY check in __follow_mount_rcu
  fs: only assert on LOOKUP_RCU when built with CONFIG_DEBUG_VFS
  select: store end_time as timespec64 in restart block
  chardev: Switch to guard(mutex) and __free(kfree)
  namespace: Replace simple_strtoul with kstrtoul to parse boot params
  dcache: Replace simple_strtoul with kstrtoul in set_dhash_entries
  ...
2026-02-09 15:13:05 -08:00
Linus Torvalds
8113b3998d vfs-7.0-rc1.atomic_open
Please consider pulling these changes from the signed vfs-7.0-rc1.atomic_open tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaYX49gAKCRCRxhvAZXjc
 ogUIAQDJTGgoi7H5a8OllRLXU/6D4OXhIhvZtvrK31HfLLDTRAEAw8JFnvFrCJP9
 xf3yVklTJ9aW65zeh2mG0uiJ87JORgE=
 =qP0i
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.0-rc1.atomic_open' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs atomic_open updates from Christian Brauner:
 "Allow knfsd to use atomic_open()

  While knfsd offers combined exclusive create and open results to
  clients, on some filesystems those results are not atomic. The
  separate vfs_create() + vfs_open() sequence in dentry_create() can
  produce races and unexpected errors. For example, open O_CREAT with
  mode 0 will succeed in creating the file but return -EACCES from
  vfs_open(). Additionally, network filesystems benefit from reducing
  remote round-trip operations by using a single atomic_open() call.

  Teach dentry_create() -- whose sole caller is knfsd -- to use
  atomic_open() for filesystems that support it"

* tag 'vfs-7.0-rc1.atomic_open' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  fs/namei: fix kernel-doc markup for dentry_create
  VFS/knfsd: Teach dentry_create() to use atomic_open()
  VFS: Prepare atomic_open() for dentry_create()
  VFS: move dentry_create() from fs/open.c to fs/namei.c
2026-02-09 14:25:37 -08:00
Linus Torvalds
6124fa45e2 vfs-7.0-rc1.btrfs
Please consider pulling these changes from the signed vfs-7.0-rc1.btrfs tag.
 
 Thanks!
 Christian
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaYX49gAKCRCRxhvAZXjc
 oogiAP0bJ72jxff4CcV1VDltO/mDT2XcCBRz3hYSZdC12Q+AYAD/XlozEUrUgbgg
 V2pWb1Xo+NrbNyhtNQ+2btHFmkzJ1gY=
 =7Xrx
 -----END PGP SIGNATURE-----

Merge tag 'vfs-7.0-rc1.btrfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs updates for btrfs from Christian Brauner:
 "This contains some changes for btrfs that are taken to the vfs tree to
  stop duplicating VFS code for subvolume/snapshot dentry

  Btrfs has carried private copies of the VFS may_delete() and
  may_create() functions in fs/btrfs/ioctl.c for permission checks
  during subvolume creation and snapshot destruction. These copies have
  drifted out of sync with the VFS originals — btrfs_may_delete() is
  missing the uid/gid validity check and btrfs_may_create() is missing
  the audit_inode_child() call.

  Export the VFS functions as may_{create,delete}_dentry() and switch
  btrfs to use them, removing ~70 lines of duplicated code"

* tag 'vfs-7.0-rc1.btrfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  btrfs: use may_create_dentry() in btrfs_mksubvol()
  btrfs: use may_delete_dentry() in btrfs_ioctl_snap_destroy()
  fs: export may_create() as may_create_dentry()
  fs: export may_delete() as may_delete_dentry()
2026-02-09 13:05:35 -08:00
Amir Goldstein
55fb177d3a
fs: add helpers name_is_dot{,dot,_dotdot}
Rename the helper is_dot_dotdot() into the name_ namespace
and add complementary helpers to check for dot and dotdot
names individually.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Link: https://patch.msgid.link/20260128132406.23768-3-amir73il@gmail.com
Reviewed-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-01-29 10:06:59 +01:00
Jay Winston
6ea258d1f6
fs/namei: fix kernel-doc markup for dentry_create
O_ is interpreted as a broken hyperlink target. Escape _ with a backslash.

The asterisk in "struct file *" is interpreted as an opening emphasis
string that never closes. Replace double quotes with rST backticks.

Change "a ERR_PTR" to "an ERR_PTR".

Signed-off-by: Jay Winston <jaybenjaminwinston@gmail.com>
Link: https://patch.msgid.link/20260118110401.2651-1-jaybenjaminwinston@gmail.com
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Christian Brauner <brauner@kernel.org>
2026-01-20 14:54:01 +01:00
Al Viro
904f58b507 namei.c: switch user pathname imports to CLASS(filename{,_flags})
filename_flags is used by user_path_at().  I suspect that mixing
LOOKUP_EMPTY with real lookup flags had been a mistake all along; the
former belongs to pathname import, the latter - to pathwalk.  Right now
none of the remaining in-tree callers of user_path_at() are getting
LOOKUP_EMPTY in flags, so user_path_at() could probably be switched
to CLASS(filename)...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2026-01-16 12:52:03 -05:00
Al Viro
e9817d5b8c namei.c: convert getname_kernel() callers to CLASS(filename_kernel)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2026-01-16 12:52:03 -05:00
Al Viro
e50aae1d39 non-consuming variants of do_{unlinkat,rmdir}()
similar to previous commit; replacements are filename_{unlinkat,rmdir}()

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2026-01-16 12:51:50 -05:00
Al Viro
88fdc27617 non-consuming variant of do_mknodat()
similar to previous commit; replacement is filename_mknodat()

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2026-01-16 12:49:26 -05:00