Skip to content

Commit 8676730

Browse files
committed
Merge patch series "fhandle, pidfs: allow open_by_handle_at() purely based on file handle"
Christian Brauner <[email protected]> says: Various filesystems such as pidfs and drm support opening file handles without having to require a file descriptor to identify the filesystem. The filesystem are global single instances and can be trivially identified solely on the information encoded in the file handle. This makes it possible to not have to keep or acquire a sentinal file descriptor just to pass it to open_by_handle_at() to identify the filesystem. That's especially useful when such sentinel file descriptor cannot or should not be acquired. For pidfs this means a file handle can function as full replacement for storing a pid in a file. Instead a file handle can be stored and reopened purely based on the file handle. Such autonomous file handles can be opened with or without specifying a a file descriptor. If no proper file descriptor is used the FD_PIDFS_ROOT sentinel must be passed. This allows us to define further special negative fd sentinels in the future. Userspace can trivially test for support by trying to open the file handle with an invalid file descriptor. * patches from https://lore.kernel.org/[email protected]: selftests/pidfd: decode pidfd file handles withou having to specify an fd fhandle, pidfs: support open_by_handle_at() purely based on file handle uapi/fcntl: add FD_PIDFS_ROOT uapi/fcntl: add FD_INVALID uapi/fcntl: mark range as reserved fhandle: reflow get_path_anchor() pidfs: add pidfs_root_path() helper fhandle: rename to get_path_anchor() fhandle: hoist copy_from_user() above get_path_from_fd() fhandle: raise FILEID_IS_DIR in handle_type Link: https://lore.kernel.org/[email protected] Signed-off-by: Christian Brauner <[email protected]>
2 parents f077638 + 914e6b1 commit 8676730

File tree

8 files changed

+129
-48
lines changed

8 files changed

+129
-48
lines changed

fs/fhandle.c

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ static long do_sys_name_to_handle(const struct path *path,
8888
if (fh_flags & EXPORT_FH_CONNECTABLE) {
8989
handle->handle_type |= FILEID_IS_CONNECTABLE;
9090
if (d_is_dir(path->dentry))
91-
fh_flags |= FILEID_IS_DIR;
91+
handle->handle_type |= FILEID_IS_DIR;
9292
}
9393
retval = 0;
9494
}
@@ -168,23 +168,32 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
168168
return err;
169169
}
170170

171-
static int get_path_from_fd(int fd, struct path *root)
171+
static int get_path_anchor(int fd, struct path *root)
172172
{
173+
if (fd >= 0) {
174+
CLASS(fd, f)(fd);
175+
if (fd_empty(f))
176+
return -EBADF;
177+
*root = fd_file(f)->f_path;
178+
path_get(root);
179+
return 0;
180+
}
181+
173182
if (fd == AT_FDCWD) {
174183
struct fs_struct *fs = current->fs;
175184
spin_lock(&fs->lock);
176185
*root = fs->pwd;
177186
path_get(root);
178187
spin_unlock(&fs->lock);
179-
} else {
180-
CLASS(fd, f)(fd);
181-
if (fd_empty(f))
182-
return -EBADF;
183-
*root = fd_file(f)->f_path;
184-
path_get(root);
188+
return 0;
185189
}
186190

187-
return 0;
191+
if (fd == FD_PIDFS_ROOT) {
192+
pidfs_get_root(root);
193+
return 0;
194+
}
195+
196+
return -EBADF;
188197
}
189198

190199
static int vfs_dentry_acceptable(void *context, struct dentry *dentry)
@@ -323,13 +332,24 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
323332
{
324333
int retval = 0;
325334
struct file_handle f_handle;
326-
struct file_handle *handle = NULL;
335+
struct file_handle *handle __free(kfree) = NULL;
327336
struct handle_to_path_ctx ctx = {};
328337
const struct export_operations *eops;
329338

330-
retval = get_path_from_fd(mountdirfd, &ctx.root);
339+
if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle)))
340+
return -EFAULT;
341+
342+
if ((f_handle.handle_bytes > MAX_HANDLE_SZ) ||
343+
(f_handle.handle_bytes == 0))
344+
return -EINVAL;
345+
346+
if (f_handle.handle_type < 0 ||
347+
FILEID_USER_FLAGS(f_handle.handle_type) & ~FILEID_VALID_USER_FLAGS)
348+
return -EINVAL;
349+
350+
retval = get_path_anchor(mountdirfd, &ctx.root);
331351
if (retval)
332-
goto out_err;
352+
return retval;
333353

334354
eops = ctx.root.mnt->mnt_sb->s_export_op;
335355
if (eops && eops->permission)
@@ -339,21 +359,6 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
339359
if (retval)
340360
goto out_path;
341361

342-
if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) {
343-
retval = -EFAULT;
344-
goto out_path;
345-
}
346-
if ((f_handle.handle_bytes > MAX_HANDLE_SZ) ||
347-
(f_handle.handle_bytes == 0)) {
348-
retval = -EINVAL;
349-
goto out_path;
350-
}
351-
if (f_handle.handle_type < 0 ||
352-
FILEID_USER_FLAGS(f_handle.handle_type) & ~FILEID_VALID_USER_FLAGS) {
353-
retval = -EINVAL;
354-
goto out_path;
355-
}
356-
357362
handle = kmalloc(struct_size(handle, f_handle, f_handle.handle_bytes),
358363
GFP_KERNEL);
359364
if (!handle) {
@@ -366,7 +371,7 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
366371
&ufh->f_handle,
367372
f_handle.handle_bytes)) {
368373
retval = -EFAULT;
369-
goto out_handle;
374+
goto out_path;
370375
}
371376

372377
/*
@@ -384,11 +389,8 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
384389
handle->handle_type &= ~FILEID_USER_FLAGS_MASK;
385390
retval = do_handle_to_path(handle, path, &ctx);
386391

387-
out_handle:
388-
kfree(handle);
389392
out_path:
390393
path_put(&ctx.root);
391-
out_err:
392394
return retval;
393395
}
394396

fs/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,4 @@ int anon_inode_getattr(struct mnt_idmap *idmap, const struct path *path,
353353
unsigned int query_flags);
354354
int anon_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
355355
struct iattr *attr);
356+
void pidfs_get_root(struct path *path);

fs/pidfs.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
static struct kmem_cache *pidfs_attr_cachep __ro_after_init;
3232
static struct kmem_cache *pidfs_xattr_cachep __ro_after_init;
3333

34+
static struct path pidfs_root_path = {};
35+
36+
void pidfs_get_root(struct path *path)
37+
{
38+
*path = pidfs_root_path;
39+
path_get(path);
40+
}
41+
3442
/*
3543
* Stashes information that userspace needs to access even after the
3644
* process has been reaped.
@@ -1068,4 +1076,7 @@ void __init pidfs_init(void)
10681076
pidfs_mnt = kern_mount(&pidfs_type);
10691077
if (IS_ERR(pidfs_mnt))
10701078
panic("Failed to mount pidfs pseudo filesystem");
1079+
1080+
pidfs_root_path.mnt = pidfs_mnt;
1081+
pidfs_root_path.dentry = pidfs_mnt->mnt_root;
10711082
}

include/uapi/linux/fcntl.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,28 @@
9090
#define DN_ATTRIB 0x00000020 /* File changed attibutes */
9191
#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
9292

93+
/* Reserved kernel ranges [-100], [-10000, -40000]. */
9394
#define AT_FDCWD -100 /* Special value for dirfd used to
9495
indicate openat should use the
9596
current working directory. */
9697

98+
/*
99+
* The concept of process and threads in userland and the kernel is a confusing
100+
* one - within the kernel every thread is a 'task' with its own individual PID,
101+
* however from userland's point of view threads are grouped by a single PID,
102+
* which is that of the 'thread group leader', typically the first thread
103+
* spawned.
104+
*
105+
* To cut the Gideon knot, for internal kernel usage, we refer to
106+
* PIDFD_SELF_THREAD to refer to the current thread (or task from a kernel
107+
* perspective), and PIDFD_SELF_THREAD_GROUP to refer to the current thread
108+
* group leader...
109+
*/
110+
#define PIDFD_SELF_THREAD -10000 /* Current thread. */
111+
#define PIDFD_SELF_THREAD_GROUP -10001 /* Current thread group leader. */
112+
113+
#define FD_PIDFS_ROOT -10002 /* Root of the pidfs filesystem */
114+
#define FD_INVALID -10009 /* Invalid file descriptor: -10000 - EBADF = -10009 */
97115

98116
/* Generic flags for the *at(2) family of syscalls. */
99117

include/uapi/linux/pidfd.h

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,6 @@
4242
#define PIDFD_COREDUMP_USER (1U << 2) /* coredump was done as the user. */
4343
#define PIDFD_COREDUMP_ROOT (1U << 3) /* coredump was done as root. */
4444

45-
/*
46-
* The concept of process and threads in userland and the kernel is a confusing
47-
* one - within the kernel every thread is a 'task' with its own individual PID,
48-
* however from userland's point of view threads are grouped by a single PID,
49-
* which is that of the 'thread group leader', typically the first thread
50-
* spawned.
51-
*
52-
* To cut the Gideon knot, for internal kernel usage, we refer to
53-
* PIDFD_SELF_THREAD to refer to the current thread (or task from a kernel
54-
* perspective), and PIDFD_SELF_THREAD_GROUP to refer to the current thread
55-
* group leader...
56-
*/
57-
#define PIDFD_SELF_THREAD -10000 /* Current thread. */
58-
#define PIDFD_SELF_THREAD_GROUP -20000 /* Current thread group leader. */
59-
6045
/*
6146
* ...and for userland we make life simpler - PIDFD_SELF refers to the current
6247
* thread, PIDFD_SELF_PROCESS refers to the process thread group leader.

tools/testing/selftests/pidfd/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# SPDX-License-Identifier: GPL-2.0-only
2-
CFLAGS += -g $(KHDR_INCLUDES) -pthread -Wall
2+
CFLAGS += -g $(KHDR_INCLUDES) $(TOOLS_INCLUDES) -pthread -Wall
33

44
TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test \
55
pidfd_poll_test pidfd_wait pidfd_getfd_test pidfd_setns_test \

tools/testing/selftests/pidfd/pidfd.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
#include "../kselftest.h"
2020
#include "../clone3/clone3_selftests.h"
2121

22+
#ifndef FD_PIDFS_ROOT
23+
#define FD_PIDFS_ROOT -10002
24+
#endif
25+
2226
#ifndef P_PIDFD
2327
#define P_PIDFD 3
2428
#endif
@@ -56,7 +60,7 @@
5660
#endif
5761

5862
#ifndef PIDFD_SELF_THREAD_GROUP
59-
#define PIDFD_SELF_THREAD_GROUP -20000 /* Current thread group leader. */
63+
#define PIDFD_SELF_THREAD_GROUP -10001 /* Current thread group leader. */
6064
#endif
6165

6266
#ifndef PIDFD_SELF

tools/testing/selftests/pidfd/pidfd_file_handle_test.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,4 +500,64 @@ TEST_F(file_handle, valid_name_to_handle_at_flags)
500500
ASSERT_EQ(close(pidfd), 0);
501501
}
502502

503+
/*
504+
* That we decode a file handle without having to pass a pidfd.
505+
*/
506+
TEST_F(file_handle, decode_purely_based_on_file_handle)
507+
{
508+
int mnt_id;
509+
struct file_handle *fh;
510+
int pidfd = -EBADF;
511+
struct stat st1, st2;
512+
513+
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
514+
ASSERT_NE(fh, NULL);
515+
memset(fh, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ);
516+
fh->handle_bytes = MAX_HANDLE_SZ;
517+
518+
ASSERT_EQ(name_to_handle_at(self->child_pidfd1, "", fh, &mnt_id, AT_EMPTY_PATH), 0);
519+
520+
ASSERT_EQ(fstat(self->child_pidfd1, &st1), 0);
521+
522+
pidfd = open_by_handle_at(FD_PIDFS_ROOT, fh, 0);
523+
ASSERT_GE(pidfd, 0);
524+
525+
ASSERT_EQ(fstat(pidfd, &st2), 0);
526+
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
527+
528+
ASSERT_EQ(close(pidfd), 0);
529+
530+
pidfd = open_by_handle_at(FD_PIDFS_ROOT, fh, O_CLOEXEC);
531+
ASSERT_GE(pidfd, 0);
532+
533+
ASSERT_EQ(fstat(pidfd, &st2), 0);
534+
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
535+
536+
ASSERT_EQ(close(pidfd), 0);
537+
538+
pidfd = open_by_handle_at(FD_PIDFS_ROOT, fh, O_NONBLOCK);
539+
ASSERT_GE(pidfd, 0);
540+
541+
ASSERT_EQ(fstat(pidfd, &st2), 0);
542+
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
543+
544+
ASSERT_EQ(close(pidfd), 0);
545+
546+
pidfd = open_by_handle_at(self->pidfd, fh, 0);
547+
ASSERT_GE(pidfd, 0);
548+
549+
ASSERT_EQ(fstat(pidfd, &st2), 0);
550+
ASSERT_TRUE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
551+
552+
ASSERT_EQ(close(pidfd), 0);
553+
554+
pidfd = open_by_handle_at(-EBADF, fh, 0);
555+
ASSERT_LT(pidfd, 0);
556+
557+
pidfd = open_by_handle_at(AT_FDCWD, fh, 0);
558+
ASSERT_LT(pidfd, 0);
559+
560+
free(fh);
561+
}
562+
503563
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)