diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-05-09 13:04:24 +0200 |
---|---|---|
committer | Christian Brauner <brauner@kernel.org> | 2024-05-10 08:26:31 +0200 |
commit | c62b758bae6af16fee94f556091fa74883a96b1e (patch) | |
tree | 78133c1a8dbb78f59a4afa5aa84b6b78e3a2540e /fs/fcntl.c | |
parent | file: add fd_raw cleanup class (diff) | |
download | linux-c62b758bae6af16fee94f556091fa74883a96b1e.tar.xz linux-c62b758bae6af16fee94f556091fa74883a96b1e.zip |
fcntl: add F_DUPFD_QUERY fcntl()
Often userspace needs to know whether two file descriptors refer to the
same struct file. For example, systemd uses this to filter out duplicate
file descriptors in it's file descriptor store (cf. [1]) and vulkan uses
it to compare dma-buf fds (cf. [2]).
The only api we provided for this was kcmp() but that's not generally
available or might be disallowed because it is way more powerful (allows
ordering of file pointers, operates on non-current task) etc. So give
userspace a simple way of comparing two file descriptors for sameness
adding a new fcntl() F_DUDFD_QUERY.
Link: https://github.com/systemd/systemd/blob/a4f0e0da3573a10bc5404142be8799418760b1d1/src/basic/fd-util.c#L517 [1]
Link: https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/render/vulkan/texture.c#L490 [2]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
[brauner: commit message]
Signed-off-by: Christian Brauner <brauner@kernel.org>
Diffstat (limited to 'fs/fcntl.c')
-rw-r--r-- | fs/fcntl.c | 20 |
1 files changed, 20 insertions, 0 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c index 54cc85d3338e..300e5d9ad913 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -327,6 +327,22 @@ static long fcntl_set_rw_hint(struct file *file, unsigned int cmd, return 0; } +/* Is the file descriptor a dup of the file? */ +static long f_dupfd_query(int fd, struct file *filp) +{ + CLASS(fd_raw, f)(fd); + + /* + * We can do the 'fdput()' immediately, as the only thing that + * matters is the pointer value which isn't changed by the fdput. + * + * Technically we didn't need a ref at all, and 'fdget()' was + * overkill, but given our lockless file pointer lookup, the + * alternatives are complicated. + */ + return f.file == filp; +} + static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, struct file *filp) { @@ -342,6 +358,9 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, case F_DUPFD_CLOEXEC: err = f_dupfd(argi, filp, O_CLOEXEC); break; + case F_DUPFD_QUERY: + err = f_dupfd_query(argi, filp); + break; case F_GETFD: err = get_close_on_exec(fd) ? FD_CLOEXEC : 0; break; @@ -446,6 +465,7 @@ static int check_fcntl_cmd(unsigned cmd) switch (cmd) { case F_DUPFD: case F_DUPFD_CLOEXEC: + case F_DUPFD_QUERY: case F_GETFD: case F_SETFD: case F_GETFL: |