fcntl: add F_DUPFD_QUERY fcntl()
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 9 May 2024 11:04:24 +0000 (13:04 +0200)
committerChristian Brauner <brauner@kernel.org>
Fri, 10 May 2024 06:26:31 +0000 (08:26 +0200)
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
Link: https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/render/vulkan/texture.c#L490
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
[brauner: commit message]
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/fcntl.c
include/uapi/linux/fcntl.h

index 54cc85d3338ed5afb12021e02a4fe6877aa675af..300e5d9ad913b588c1aa2a59cab4e2e8ab2ef5a7 100644 (file)
@@ -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:
index 282e90aeb163c0288590995b38fe011b19e85111..c0bcc185fa48f85244d3f3b0de01a1c979732b15 100644 (file)
@@ -8,6 +8,14 @@
 #define F_SETLEASE     (F_LINUX_SPECIFIC_BASE + 0)
 #define F_GETLEASE     (F_LINUX_SPECIFIC_BASE + 1)
 
+/*
+ * Request nofications on a directory.
+ * See below for events that may be notified.
+ */
+#define F_NOTIFY       (F_LINUX_SPECIFIC_BASE + 2)
+
+#define F_DUPFD_QUERY  (F_LINUX_SPECIFIC_BASE + 3)
+
 /*
  * Cancel a blocking posix lock; internal use only until we expose an
  * asynchronous lock api to userspace:
 /* Create a file descriptor with FD_CLOEXEC set. */
 #define F_DUPFD_CLOEXEC        (F_LINUX_SPECIFIC_BASE + 6)
 
-/*
- * Request nofications on a directory.
- * See below for events that may be notified.
- */
-#define F_NOTIFY       (F_LINUX_SPECIFIC_BASE+2)
-
 /*
  * Set and get of pipe page size array
  */