]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
basic: move acquire_data_fd() and fd_duplicate_data_fd() to new data-fd-util.c
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 21 Jun 2021 20:54:12 +0000 (22:54 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 24 Jun 2021 08:05:22 +0000 (10:05 +0200)
fd_duplicate_data_fd() is renamed to copy_data_fd(). This makes
the two functions have nicely similar names.

Now fd-util.[ch] is again about low-level file descriptor manipulations.
copy_data_fd() is a complex function that internally wraps the other
functions in copy.c. I want to move copy.c and the whole cluster of
related code from basic/ to shared/ later on, and this is a preparatory
step for that.

14 files changed:
src/basic/data-fd-util.c [new file with mode: 0644]
src/basic/data-fd-util.h [new file with mode: 0644]
src/basic/fd-util.c
src/basic/fd-util.h
src/basic/meson.build
src/basic/terminal-util.c
src/core/dbus-manager.c
src/core/execute.c
src/home/homed-home.c
src/oom/oomd-manager-bus.c
src/portable/portable.c
src/test/meson.build
src/test/test-data-fd-util.c [new file with mode: 0644]
src/test/test-fd-util.c

diff --git a/src/basic/data-fd-util.c b/src/basic/data-fd-util.c
new file mode 100644 (file)
index 0000000..a827ef5
--- /dev/null
@@ -0,0 +1,350 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "copy.h"
+#include "data-fd-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "io-util.h"
+#include "memfd-util.h"
+#include "tmpfile-util.h"
+
+/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */
+#define DATA_FD_MEMORY_LIMIT (64U*1024U)
+
+/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */
+#define DATA_FD_TMP_LIMIT (1024U*1024U)
+
+int acquire_data_fd(const void *data, size_t size, unsigned flags) {
+        _cleanup_close_pair_ int pipefds[2] = { -1, -1 };
+        char pattern[] = "/dev/shm/data-fd-XXXXXX";
+        _cleanup_close_ int fd = -1;
+        int isz = 0, r;
+        ssize_t n;
+        off_t f;
+
+        assert(data || size == 0);
+
+        /* Acquire a read-only file descriptor that when read from returns the specified data. This is much more
+         * complex than I wish it was. But here's why:
+         *
+         * a) First we try to use memfds. They are the best option, as we can seal them nicely to make them
+         *    read-only. Unfortunately they require kernel 3.17, and – at the time of writing – we still support 3.14.
+         *
+         * b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining
+         *    a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged
+         *    clients can only bump their size to a system-wide limit, which might be quite low.
+         *
+         * c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from
+         *    earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via
+         *    /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs.
+         *
+         * d) Finally, we try creating a regular file in /dev/shm, which we then delete.
+         *
+         * It sucks a bit that depending on the situation we return very different objects here, but that's Linux I
+         * figure. */
+
+        if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) {
+                /* As a special case, return /dev/null if we have been called for an empty data block */
+                r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (r < 0)
+                        return -errno;
+
+                return r;
+        }
+
+        if ((flags & ACQUIRE_NO_MEMFD) == 0) {
+                fd = memfd_new("data-fd");
+                if (fd < 0)
+                        goto try_pipe;
+
+                n = write(fd, data, size);
+                if (n < 0)
+                        return -errno;
+                if ((size_t) n != size)
+                        return -EIO;
+
+                f = lseek(fd, 0, SEEK_SET);
+                if (f != 0)
+                        return -errno;
+
+                r = memfd_set_sealed(fd);
+                if (r < 0)
+                        return r;
+
+                return TAKE_FD(fd);
+        }
+
+try_pipe:
+        if ((flags & ACQUIRE_NO_PIPE) == 0) {
+                if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
+                        return -errno;
+
+                isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
+                if (isz < 0)
+                        return -errno;
+
+                if ((size_t) isz < size) {
+                        isz = (int) size;
+                        if (isz < 0 || (size_t) isz != size)
+                                return -E2BIG;
+
+                        /* Try to bump the pipe size */
+                        (void) fcntl(pipefds[1], F_SETPIPE_SZ, isz);
+
+                        /* See if that worked */
+                        isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
+                        if (isz < 0)
+                                return -errno;
+
+                        if ((size_t) isz < size)
+                                goto try_dev_shm;
+                }
+
+                n = write(pipefds[1], data, size);
+                if (n < 0)
+                        return -errno;
+                if ((size_t) n != size)
+                        return -EIO;
+
+                (void) fd_nonblock(pipefds[0], false);
+
+                return TAKE_FD(pipefds[0]);
+        }
+
+try_dev_shm:
+        if ((flags & ACQUIRE_NO_TMPFILE) == 0) {
+                fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500);
+                if (fd < 0)
+                        goto try_dev_shm_without_o_tmpfile;
+
+                n = write(fd, data, size);
+                if (n < 0)
+                        return -errno;
+                if ((size_t) n != size)
+                        return -EIO;
+
+                /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
+                return fd_reopen(fd, O_RDONLY|O_CLOEXEC);
+        }
+
+try_dev_shm_without_o_tmpfile:
+        if ((flags & ACQUIRE_NO_REGULAR) == 0) {
+                fd = mkostemp_safe(pattern);
+                if (fd < 0)
+                        return fd;
+
+                n = write(fd, data, size);
+                if (n < 0) {
+                        r = -errno;
+                        goto unlink_and_return;
+                }
+                if ((size_t) n != size) {
+                        r = -EIO;
+                        goto unlink_and_return;
+                }
+
+                /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
+                r = open(pattern, O_RDONLY|O_CLOEXEC);
+                if (r < 0)
+                        r = -errno;
+
+        unlink_and_return:
+                (void) unlink(pattern);
+                return r;
+        }
+
+        return -EOPNOTSUPP;
+}
+
+int copy_data_fd(int fd) {
+        _cleanup_close_ int copy_fd = -1, tmp_fd = -1;
+        _cleanup_free_ void *remains = NULL;
+        size_t remains_size = 0;
+        const char *td;
+        struct stat st;
+        int r;
+
+        /* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but
+         * independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be
+         * somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported
+         * uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in
+         * /var/tmp. */
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        /* For now, let's only accept regular files, sockets, pipes and char devices */
+        if (S_ISDIR(st.st_mode))
+                return -EISDIR;
+        if (S_ISLNK(st.st_mode))
+                return -ELOOP;
+        if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode))
+                return -EBADFD;
+
+        /* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note
+         * that we use the reported regular file size only as a hint, given that there are plenty special files in
+         * /proc and /sys which report a zero file size but can be read from. */
+
+        if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) {
+
+                /* Try a memfd first */
+                copy_fd = memfd_new("data-fd");
+                if (copy_fd >= 0) {
+                        off_t f;
+
+                        r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0);
+                        if (r < 0)
+                                return r;
+
+                        f = lseek(copy_fd, 0, SEEK_SET);
+                        if (f != 0)
+                                return -errno;
+
+                        if (r == 0) {
+                                /* Did it fit into the limit? If so, we are done. */
+                                r = memfd_set_sealed(copy_fd);
+                                if (r < 0)
+                                        return r;
+
+                                return TAKE_FD(copy_fd);
+                        }
+
+                        /* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */
+
+                } else {
+                        _cleanup_(close_pairp) int pipefds[2] = { -1, -1 };
+                        int isz;
+
+                        /* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather
+                         * then block indefinitely when we hit the pipe size limit */
+
+                        if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
+                                return -errno;
+
+                        isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
+                        if (isz < 0)
+                                return -errno;
+
+                        /* Try to enlarge the pipe size if necessary */
+                        if ((size_t) isz < DATA_FD_MEMORY_LIMIT) {
+
+                                (void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT);
+
+                                isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
+                                if (isz < 0)
+                                        return -errno;
+                        }
+
+                        if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
+
+                                r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL);
+                                if (r < 0 && r != -EAGAIN)
+                                        return r; /* If we get EAGAIN it could be because of the source or because of
+                                                   * the destination fd, we can't know, as sendfile() and friends won't
+                                                   * tell us. Hence, treat this as reason to fall back, just to be
+                                                   * sure. */
+                                if (r == 0) {
+                                        /* Everything fit in, yay! */
+                                        (void) fd_nonblock(pipefds[0], false);
+
+                                        return TAKE_FD(pipefds[0]);
+                                }
+
+                                /* Things didn't fit in. But we read data into the pipe, let's remember that, so that
+                                 * when writing the new file we incorporate this first. */
+                                copy_fd = TAKE_FD(pipefds[0]);
+                        }
+                }
+        }
+
+        /* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */
+        if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) &&
+            (DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) {
+                off_t f;
+
+                tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC);
+                if (tmp_fd < 0)
+                        return tmp_fd;
+
+                if (copy_fd >= 0) {
+                        /* If we tried a memfd/pipe first and it ended up being too large, then copy this into the
+                         * temporary file first. */
+
+                        r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0);
+                        if (r < 0)
+                                return r;
+
+                        assert(r == 0);
+                }
+
+                if (remains_size > 0) {
+                        /* If there were remaining bytes (i.e. read into memory, but not written out yet) from the
+                         * failed copy operation, let's flush them out next. */
+
+                        r = loop_write(tmp_fd, remains, remains_size, false);
+                        if (r < 0)
+                                return r;
+                }
+
+                r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        goto finish;  /* Yay, it fit in */
+
+                /* It didn't fit in. Let's not forget to use what we already used */
+                f = lseek(tmp_fd, 0, SEEK_SET);
+                if (f != 0)
+                        return -errno;
+
+                CLOSE_AND_REPLACE(copy_fd, tmp_fd);
+
+                remains = mfree(remains);
+                remains_size = 0;
+        }
+
+        /* As last fallback use /var/tmp */
+        r = var_tmp_dir(&td);
+        if (r < 0)
+                return r;
+
+        tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC);
+        if (tmp_fd < 0)
+                return tmp_fd;
+
+        if (copy_fd >= 0) {
+                /* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this
+                 * into the temporary file first. */
+                r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
+                if (r < 0)
+                        return r;
+
+                assert(r == 0);
+        }
+
+        if (remains_size > 0) {
+                /* Then, copy in any read but not yet written bytes. */
+                r = loop_write(tmp_fd, remains, remains_size, false);
+                if (r < 0)
+                        return r;
+        }
+
+        /* Copy in the rest */
+        r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
+        if (r < 0)
+                return r;
+
+        assert(r == 0);
+
+finish:
+        /* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the
+         * file again */
+
+        return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
+}
diff --git a/src/basic/data-fd-util.h b/src/basic/data-fd-util.h
new file mode 100644 (file)
index 0000000..827e149
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stddef.h>
+
+int acquire_data_fd(const void *data, size_t size, unsigned flags);
+int copy_data_fd(int fd);
index ac6a37b5678fb1c0577c716894c54bcc8ff597db..008f474344df51189021b86429d10e57553fb35c 100644 (file)
 #include <unistd.h>
 
 #include "alloc-util.h"
-#include "copy.h"
 #include "dirent-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
 #include "io-util.h"
 #include "macro.h"
-#include "memfd-util.h"
 #include "missing_fcntl.h"
 #include "missing_syscall.h"
 #include "parse-util.h"
@@ -523,343 +521,6 @@ int move_fd(int from, int to, int cloexec) {
         return to;
 }
 
-int acquire_data_fd(const void *data, size_t size, unsigned flags) {
-
-        _cleanup_close_pair_ int pipefds[2] = { -1, -1 };
-        char pattern[] = "/dev/shm/data-fd-XXXXXX";
-        _cleanup_close_ int fd = -1;
-        int isz = 0, r;
-        ssize_t n;
-        off_t f;
-
-        assert(data || size == 0);
-
-        /* Acquire a read-only file descriptor that when read from returns the specified data. This is much more
-         * complex than I wish it was. But here's why:
-         *
-         * a) First we try to use memfds. They are the best option, as we can seal them nicely to make them
-         *    read-only. Unfortunately they require kernel 3.17, and – at the time of writing – we still support 3.14.
-         *
-         * b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining
-         *    a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged
-         *    clients can only bump their size to a system-wide limit, which might be quite low.
-         *
-         * c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from
-         *    earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via
-         *    /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs.
-         *
-         * d) Finally, we try creating a regular file in /dev/shm, which we then delete.
-         *
-         * It sucks a bit that depending on the situation we return very different objects here, but that's Linux I
-         * figure. */
-
-        if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) {
-                /* As a special case, return /dev/null if we have been called for an empty data block */
-                r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-                if (r < 0)
-                        return -errno;
-
-                return r;
-        }
-
-        if ((flags & ACQUIRE_NO_MEMFD) == 0) {
-                fd = memfd_new("data-fd");
-                if (fd < 0)
-                        goto try_pipe;
-
-                n = write(fd, data, size);
-                if (n < 0)
-                        return -errno;
-                if ((size_t) n != size)
-                        return -EIO;
-
-                f = lseek(fd, 0, SEEK_SET);
-                if (f != 0)
-                        return -errno;
-
-                r = memfd_set_sealed(fd);
-                if (r < 0)
-                        return r;
-
-                return TAKE_FD(fd);
-        }
-
-try_pipe:
-        if ((flags & ACQUIRE_NO_PIPE) == 0) {
-                if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
-                        return -errno;
-
-                isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
-                if (isz < 0)
-                        return -errno;
-
-                if ((size_t) isz < size) {
-                        isz = (int) size;
-                        if (isz < 0 || (size_t) isz != size)
-                                return -E2BIG;
-
-                        /* Try to bump the pipe size */
-                        (void) fcntl(pipefds[1], F_SETPIPE_SZ, isz);
-
-                        /* See if that worked */
-                        isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
-                        if (isz < 0)
-                                return -errno;
-
-                        if ((size_t) isz < size)
-                                goto try_dev_shm;
-                }
-
-                n = write(pipefds[1], data, size);
-                if (n < 0)
-                        return -errno;
-                if ((size_t) n != size)
-                        return -EIO;
-
-                (void) fd_nonblock(pipefds[0], false);
-
-                return TAKE_FD(pipefds[0]);
-        }
-
-try_dev_shm:
-        if ((flags & ACQUIRE_NO_TMPFILE) == 0) {
-                fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500);
-                if (fd < 0)
-                        goto try_dev_shm_without_o_tmpfile;
-
-                n = write(fd, data, size);
-                if (n < 0)
-                        return -errno;
-                if ((size_t) n != size)
-                        return -EIO;
-
-                /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
-                return fd_reopen(fd, O_RDONLY|O_CLOEXEC);
-        }
-
-try_dev_shm_without_o_tmpfile:
-        if ((flags & ACQUIRE_NO_REGULAR) == 0) {
-                fd = mkostemp_safe(pattern);
-                if (fd < 0)
-                        return fd;
-
-                n = write(fd, data, size);
-                if (n < 0) {
-                        r = -errno;
-                        goto unlink_and_return;
-                }
-                if ((size_t) n != size) {
-                        r = -EIO;
-                        goto unlink_and_return;
-                }
-
-                /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
-                r = open(pattern, O_RDONLY|O_CLOEXEC);
-                if (r < 0)
-                        r = -errno;
-
-        unlink_and_return:
-                (void) unlink(pattern);
-                return r;
-        }
-
-        return -EOPNOTSUPP;
-}
-
-/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */
-#define DATA_FD_MEMORY_LIMIT (64U*1024U)
-
-/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */
-#define DATA_FD_TMP_LIMIT (1024U*1024U)
-
-int fd_duplicate_data_fd(int fd) {
-
-        _cleanup_close_ int copy_fd = -1, tmp_fd = -1;
-        _cleanup_free_ void *remains = NULL;
-        size_t remains_size = 0;
-        const char *td;
-        struct stat st;
-        int r;
-
-        /* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but
-         * independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be
-         * somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported
-         * uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in
-         * /var/tmp. */
-
-        if (fstat(fd, &st) < 0)
-                return -errno;
-
-        /* For now, let's only accept regular files, sockets, pipes and char devices */
-        if (S_ISDIR(st.st_mode))
-                return -EISDIR;
-        if (S_ISLNK(st.st_mode))
-                return -ELOOP;
-        if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode))
-                return -EBADFD;
-
-        /* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note
-         * that we use the reported regular file size only as a hint, given that there are plenty special files in
-         * /proc and /sys which report a zero file size but can be read from. */
-
-        if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) {
-
-                /* Try a memfd first */
-                copy_fd = memfd_new("data-fd");
-                if (copy_fd >= 0) {
-                        off_t f;
-
-                        r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0);
-                        if (r < 0)
-                                return r;
-
-                        f = lseek(copy_fd, 0, SEEK_SET);
-                        if (f != 0)
-                                return -errno;
-
-                        if (r == 0) {
-                                /* Did it fit into the limit? If so, we are done. */
-                                r = memfd_set_sealed(copy_fd);
-                                if (r < 0)
-                                        return r;
-
-                                return TAKE_FD(copy_fd);
-                        }
-
-                        /* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */
-
-                } else {
-                        _cleanup_(close_pairp) int pipefds[2] = { -1, -1 };
-                        int isz;
-
-                        /* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather
-                         * then block indefinitely when we hit the pipe size limit */
-
-                        if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
-                                return -errno;
-
-                        isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
-                        if (isz < 0)
-                                return -errno;
-
-                        /* Try to enlarge the pipe size if necessary */
-                        if ((size_t) isz < DATA_FD_MEMORY_LIMIT) {
-
-                                (void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT);
-
-                                isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
-                                if (isz < 0)
-                                        return -errno;
-                        }
-
-                        if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
-
-                                r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL);
-                                if (r < 0 && r != -EAGAIN)
-                                        return r; /* If we get EAGAIN it could be because of the source or because of
-                                                   * the destination fd, we can't know, as sendfile() and friends won't
-                                                   * tell us. Hence, treat this as reason to fall back, just to be
-                                                   * sure. */
-                                if (r == 0) {
-                                        /* Everything fit in, yay! */
-                                        (void) fd_nonblock(pipefds[0], false);
-
-                                        return TAKE_FD(pipefds[0]);
-                                }
-
-                                /* Things didn't fit in. But we read data into the pipe, let's remember that, so that
-                                 * when writing the new file we incorporate this first. */
-                                copy_fd = TAKE_FD(pipefds[0]);
-                        }
-                }
-        }
-
-        /* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */
-        if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) &&
-            (DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) {
-                off_t f;
-
-                tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC);
-                if (tmp_fd < 0)
-                        return tmp_fd;
-
-                if (copy_fd >= 0) {
-                        /* If we tried a memfd/pipe first and it ended up being too large, then copy this into the
-                         * temporary file first. */
-
-                        r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0);
-                        if (r < 0)
-                                return r;
-
-                        assert(r == 0);
-                }
-
-                if (remains_size > 0) {
-                        /* If there were remaining bytes (i.e. read into memory, but not written out yet) from the
-                         * failed copy operation, let's flush them out next. */
-
-                        r = loop_write(tmp_fd, remains, remains_size, false);
-                        if (r < 0)
-                                return r;
-                }
-
-                r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        goto finish;  /* Yay, it fit in */
-
-                /* It didn't fit in. Let's not forget to use what we already used */
-                f = lseek(tmp_fd, 0, SEEK_SET);
-                if (f != 0)
-                        return -errno;
-
-                CLOSE_AND_REPLACE(copy_fd, tmp_fd);
-
-                remains = mfree(remains);
-                remains_size = 0;
-        }
-
-        /* As last fallback use /var/tmp */
-        r = var_tmp_dir(&td);
-        if (r < 0)
-                return r;
-
-        tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC);
-        if (tmp_fd < 0)
-                return tmp_fd;
-
-        if (copy_fd >= 0) {
-                /* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this
-                 * into the temporary file first. */
-                r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
-                if (r < 0)
-                        return r;
-
-                assert(r == 0);
-        }
-
-        if (remains_size > 0) {
-                /* Then, copy in any read but not yet written bytes. */
-                r = loop_write(tmp_fd, remains, remains_size, false);
-                if (r < 0)
-                        return r;
-        }
-
-        /* Copy in the rest */
-        r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
-        if (r < 0)
-                return r;
-
-        assert(r == 0);
-
-finish:
-        /* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the
-         * file again */
-
-        return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
-}
-
 int fd_move_above_stdio(int fd) {
         int flags, copy;
         PROTECT_ERRNO;
index eb696762b6ada6d6774cc8091fd6929ba78b9df7..9529a4723d39fa2060acd233de735eee348babc8 100644 (file)
@@ -76,10 +76,6 @@ enum {
         ACQUIRE_NO_REGULAR  = 1 << 4,
 };
 
-int acquire_data_fd(const void *data, size_t size, unsigned flags);
-
-int fd_duplicate_data_fd(int fd);
-
 int fd_move_above_stdio(int fd);
 
 int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd);
index 3685b639ae50505cc799055ceebe6c7b1c505caf..7cf2ff09ca6bc6160b8a1d2aa201f7b63e495615 100644 (file)
@@ -37,6 +37,8 @@ basic_sources = files('''
         copy.h
         creds-util.c
         creds-util.h
+        data-fd-util.c
+        data-fd-util.h
         def.h
         dirent-util.c
         dirent-util.h
index ed0632b02b623c8fdfdd00d768a00c6b991d49ff..d769423d6e904ba5ecc003a6ce595af59aae9162 100644 (file)
@@ -21,7 +21,6 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
-#include "copy.h"
 #include "def.h"
 #include "env-util.h"
 #include "fd-util.h"
index 758dd8f1b8b19c0a1fd68e5d12a6e085290ad812..de057a024529ab5afb7eded1b21c968f2c2b130b 100644 (file)
@@ -11,6 +11,7 @@
 #include "bus-common-errors.h"
 #include "bus-get-properties.h"
 #include "bus-log-control-api.h"
+#include "data-fd-util.h"
 #include "dbus-cgroup.h"
 #include "dbus-execute.h"
 #include "dbus-job.h"
index f9d23272c4fb4c0dce8b4546ee7c4a4367632ce3..42d76a346db45683c8a850d36cc2a4f74bd704fe 100644 (file)
@@ -46,6 +46,7 @@
 #include "cgroup-setup.h"
 #include "chown-recursive.h"
 #include "cpu-set-util.h"
+#include "data-fd-util.h"
 #include "def.h"
 #include "env-file.h"
 #include "env-util.h"
index 39dd501a32e8b480ba3fa22025787ced9be37fb0..104427dabe8a938eb47f85b0e0240f2a0a875987 100644 (file)
@@ -11,6 +11,7 @@
 #include "blockdev-util.h"
 #include "btrfs-util.h"
 #include "bus-common-errors.h"
+#include "data-fd-util.h"
 #include "env-util.h"
 #include "errno-list.h"
 #include "errno-util.h"
index 4ea2a338fc85882f3d1dc4b634a4ab60cb3bc924..b41e3663098544f6525b8902ddd4689c7e48b47e 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "bus-common-errors.h"
 #include "bus-polkit.h"
+#include "data-fd-util.h"
 #include "fd-util.h"
 #include "oomd-manager-bus.h"
 #include "oomd-manager.h"
index 9e33299ed51e3c0ba095ef255e85d3237c8f8cfe..a9ced3c865eddf816229bcebe5d762570e9aceba 100644 (file)
@@ -6,6 +6,7 @@
 #include "bus-error.h"
 #include "conf-files.h"
 #include "copy.h"
+#include "data-fd-util.h"
 #include "def.h"
 #include "dirent-util.h"
 #include "discover-image.h"
@@ -153,7 +154,7 @@ static int send_item(
         assert(name);
         assert(fd >= 0);
 
-        data_fd = fd_duplicate_data_fd(fd);
+        data_fd = copy_data_fd(fd);
         if (data_fd < 0)
                 return data_fd;
 
index 29f488f4d86cf386a938b26a6da78d111819d813..03f08673bb9a92ced08a7b8b3c7587d6eb567f4b 100644 (file)
@@ -159,6 +159,8 @@ tests += [
 
         [['src/test/test-copy.c']],
 
+        [['src/test/test-data-fd-util.c']],
+
         [['src/test/test-static-destruct.c']],
 
         [['src/test/test-sigbus.c']],
diff --git a/src/test/test-data-fd-util.c b/src/test/test-data-fd-util.c
new file mode 100644 (file)
index 0000000..22a57b5
--- /dev/null
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "data-fd-util.h"
+#include "fd-util.h"
+#include "memory-util.h"
+#include "process-util.h"
+#include "tests.h"
+#include "random-util.h"
+
+static void test_acquire_data_fd_one(unsigned flags) {
+        char wbuffer[196*1024 - 7];
+        char rbuffer[sizeof(wbuffer)];
+        int fd;
+
+        fd = acquire_data_fd("foo", 3, flags);
+        assert_se(fd >= 0);
+
+        zero(rbuffer);
+        assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 3);
+        assert_se(streq(rbuffer, "foo"));
+
+        fd = safe_close(fd);
+
+        fd = acquire_data_fd("", 0, flags);
+        assert_se(fd >= 0);
+
+        zero(rbuffer);
+        assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 0);
+        assert_se(streq(rbuffer, ""));
+
+        fd = safe_close(fd);
+
+        random_bytes(wbuffer, sizeof(wbuffer));
+
+        fd = acquire_data_fd(wbuffer, sizeof(wbuffer), flags);
+        assert_se(fd >= 0);
+
+        zero(rbuffer);
+        assert_se(read(fd, rbuffer, sizeof(rbuffer)) == sizeof(rbuffer));
+        assert_se(memcmp(rbuffer, wbuffer, sizeof(rbuffer)) == 0);
+
+        fd = safe_close(fd);
+}
+
+static void test_acquire_data_fd(void) {
+        test_acquire_data_fd_one(0);
+        test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL);
+        test_acquire_data_fd_one(ACQUIRE_NO_MEMFD);
+        test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD);
+        test_acquire_data_fd_one(ACQUIRE_NO_PIPE);
+        test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_PIPE);
+        test_acquire_data_fd_one(ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE);
+        test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE);
+        test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE|ACQUIRE_NO_TMPFILE);
+}
+
+static void assert_equal_fd(int fd1, int fd2) {
+        for (;;) {
+                uint8_t a[4096], b[4096];
+                ssize_t x, y;
+
+                x = read(fd1, a, sizeof(a));
+                assert_se(x >= 0);
+
+                y = read(fd2, b, sizeof(b));
+                assert_se(y >= 0);
+
+                assert_se(x == y);
+
+                if (x == 0)
+                        break;
+
+                assert_se(memcmp(a, b, x) == 0);
+        }
+}
+
+static void test_copy_data_fd(void) {
+        _cleanup_close_ int fd1 = -1, fd2 = -1;
+        _cleanup_(close_pairp) int sfd[2] = { -1, -1 };
+        _cleanup_(sigkill_waitp) pid_t pid = -1;
+        int r;
+
+        fd1 = open("/etc/fstab", O_RDONLY|O_CLOEXEC);
+        if (fd1 >= 0) {
+
+                fd2 = copy_data_fd(fd1);
+                assert_se(fd2 >= 0);
+
+                assert_se(lseek(fd1, 0, SEEK_SET) == 0);
+                assert_equal_fd(fd1, fd2);
+        }
+
+        fd1 = safe_close(fd1);
+        fd2 = safe_close(fd2);
+
+        fd1 = acquire_data_fd("hallo", 6,  0);
+        assert_se(fd1 >= 0);
+
+        fd2 = copy_data_fd(fd1);
+        assert_se(fd2 >= 0);
+
+        safe_close(fd1);
+        fd1 = acquire_data_fd("hallo", 6,  0);
+        assert_se(fd1 >= 0);
+
+        assert_equal_fd(fd1, fd2);
+
+        fd1 = safe_close(fd1);
+        fd2 = safe_close(fd2);
+
+        assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sfd) >= 0);
+
+        r = safe_fork("(sd-pipe)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+        assert_se(r >= 0);
+
+        if (r == 0) {
+                /* child */
+
+                sfd[0] = safe_close(sfd[0]);
+
+                for (uint64_t i = 0; i < 1536*1024 / sizeof(uint64_t); i++)
+                        assert_se(write(sfd[1], &i, sizeof(i)) == sizeof(i));
+
+                sfd[1] = safe_close(sfd[1]);
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        sfd[1] = safe_close(sfd[1]);
+
+        fd2 = copy_data_fd(sfd[0]);
+        assert_se(fd2 >= 0);
+
+        uint64_t j;
+        for (uint64_t i = 0; i < 1536*1024 / sizeof(uint64_t); i++) {
+                assert_se(read(fd2, &j, sizeof(j)) == sizeof(j));
+                assert_se(i == j);
+        }
+
+        assert_se(read(fd2, &j, sizeof(j)) == 0);
+}
+
+int main(int argc, char *argv[]) {
+        test_setup_logging(LOG_DEBUG);
+
+        test_acquire_data_fd();
+        test_copy_data_fd();
+
+        return 0;
+}
index bece89aef25bdd221a2201e26cfee2632d811b4d..1cd3bdfbbec640f4f91d0853f8e63f240ec498dd 100644 (file)
@@ -4,6 +4,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "data-fd-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "macro.h"
@@ -95,54 +96,6 @@ static void test_open_serialization_fd(void) {
         assert_se(write(fd, "test\n", 5) == 5);
 }
 
-static void test_acquire_data_fd_one(unsigned flags) {
-        char wbuffer[196*1024 - 7];
-        char rbuffer[sizeof(wbuffer)];
-        int fd;
-
-        fd = acquire_data_fd("foo", 3, flags);
-        assert_se(fd >= 0);
-
-        zero(rbuffer);
-        assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 3);
-        assert_se(streq(rbuffer, "foo"));
-
-        fd = safe_close(fd);
-
-        fd = acquire_data_fd("", 0, flags);
-        assert_se(fd >= 0);
-
-        zero(rbuffer);
-        assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 0);
-        assert_se(streq(rbuffer, ""));
-
-        fd = safe_close(fd);
-
-        random_bytes(wbuffer, sizeof(wbuffer));
-
-        fd = acquire_data_fd(wbuffer, sizeof(wbuffer), flags);
-        assert_se(fd >= 0);
-
-        zero(rbuffer);
-        assert_se(read(fd, rbuffer, sizeof(rbuffer)) == sizeof(rbuffer));
-        assert_se(memcmp(rbuffer, wbuffer, sizeof(rbuffer)) == 0);
-
-        fd = safe_close(fd);
-}
-
-static void test_acquire_data_fd(void) {
-
-        test_acquire_data_fd_one(0);
-        test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL);
-        test_acquire_data_fd_one(ACQUIRE_NO_MEMFD);
-        test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD);
-        test_acquire_data_fd_one(ACQUIRE_NO_PIPE);
-        test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_PIPE);
-        test_acquire_data_fd_one(ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE);
-        test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE);
-        test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE|ACQUIRE_NO_TMPFILE);
-}
-
 static void test_fd_move_above_stdio(void) {
         int original_stdin, new_fd;
 
@@ -227,93 +180,6 @@ static void test_rearrange_stdio(void) {
         }
 }
 
-static void assert_equal_fd(int fd1, int fd2) {
-
-        for (;;) {
-                uint8_t a[4096], b[4096];
-                ssize_t x, y;
-
-                x = read(fd1, a, sizeof(a));
-                assert_se(x >= 0);
-
-                y = read(fd2, b, sizeof(b));
-                assert_se(y >= 0);
-
-                assert_se(x == y);
-
-                if (x == 0)
-                        break;
-
-                assert_se(memcmp(a, b, x) == 0);
-        }
-}
-
-static void test_fd_duplicate_data_fd(void) {
-        _cleanup_close_ int fd1 = -1, fd2 = -1;
-        _cleanup_(close_pairp) int sfd[2] = { -1, -1 };
-        _cleanup_(sigkill_waitp) pid_t pid = -1;
-        uint64_t i, j;
-        int r;
-
-        fd1 = open("/etc/fstab", O_RDONLY|O_CLOEXEC);
-        if (fd1 >= 0) {
-
-                fd2 = fd_duplicate_data_fd(fd1);
-                assert_se(fd2 >= 0);
-
-                assert_se(lseek(fd1, 0, SEEK_SET) == 0);
-                assert_equal_fd(fd1, fd2);
-        }
-
-        fd1 = safe_close(fd1);
-        fd2 = safe_close(fd2);
-
-        fd1 = acquire_data_fd("hallo", 6,  0);
-        assert_se(fd1 >= 0);
-
-        fd2 = fd_duplicate_data_fd(fd1);
-        assert_se(fd2 >= 0);
-
-        safe_close(fd1);
-        fd1 = acquire_data_fd("hallo", 6,  0);
-        assert_se(fd1 >= 0);
-
-        assert_equal_fd(fd1, fd2);
-
-        fd1 = safe_close(fd1);
-        fd2 = safe_close(fd2);
-
-        assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sfd) >= 0);
-
-        r = safe_fork("(sd-pipe)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
-        assert_se(r >= 0);
-
-        if (r == 0) {
-                /* child */
-
-                sfd[0] = safe_close(sfd[0]);
-
-                for (i = 0; i < 1536*1024 / sizeof(uint64_t); i++)
-                        assert_se(write(sfd[1], &i, sizeof(i)) == sizeof(i));
-
-                sfd[1] = safe_close(sfd[1]);
-
-                _exit(EXIT_SUCCESS);
-        }
-
-        sfd[1] = safe_close(sfd[1]);
-
-        fd2 = fd_duplicate_data_fd(sfd[0]);
-        assert_se(fd2 >= 0);
-
-        for (i = 0; i < 1536*1024 / sizeof(uint64_t); i++) {
-                assert_se(read(fd2, &j, sizeof(j)) == sizeof(j));
-                assert_se(i == j);
-        }
-
-        assert_se(read(fd2, &j, sizeof(j)) == 0);
-}
-
 static void test_read_nr_open(void) {
         log_info("nr-open: %i", read_nr_open());
 }
@@ -420,10 +286,8 @@ int main(int argc, char *argv[]) {
         test_close_nointr();
         test_same_fd();
         test_open_serialization_fd();
-        test_acquire_data_fd();
         test_fd_move_above_stdio();
         test_rearrange_stdio();
-        test_fd_duplicate_data_fd();
         test_read_nr_open();
         test_close_all_fds();