]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/data-fd-util.c
tree-wide: use -EBADF for fd initialization
[thirdparty/systemd.git] / src / shared / data-fd-util.c
index 6ae3f115350f9754048fe7e593ce24bd0a269241..15d31ac20ce2488e4cb3e281fe6f5eecaad22f60 100644 (file)
@@ -4,6 +4,9 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#if HAVE_LINUX_MEMFD_H
+#include <linux/memfd.h>
+#endif
 
 #include "alloc-util.h"
 #include "copy.h"
@@ -12,6 +15,8 @@
 #include "fs-util.h"
 #include "io-util.h"
 #include "memfd-util.h"
+#include "missing_mman.h"
+#include "missing_syscall.h"
 #include "tmpfile-util.h"
 
 /* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */
@@ -23,7 +28,7 @@
 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;
+        _cleanup_close_ int fd = -EBADF;
         int isz = 0, r;
         ssize_t n;
         off_t f;
@@ -158,7 +163,7 @@ try_dev_shm_without_o_tmpfile:
 }
 
 int copy_data_fd(int fd) {
-        _cleanup_close_ int copy_fd = -1, tmp_fd = -1;
+        _cleanup_close_ int copy_fd = -EBADF, tmp_fd = -EBADF;
         _cleanup_free_ void *remains = NULL;
         size_t remains_size = 0;
         const char *td;
@@ -298,7 +303,7 @@ int copy_data_fd(int fd) {
                 if (f != 0)
                         return -errno;
 
-                CLOSE_AND_REPLACE(copy_fd, tmp_fd);
+                close_and_replace(copy_fd, tmp_fd);
 
                 remains = mfree(remains);
                 remains_size = 0;
@@ -343,3 +348,50 @@ finish:
 
         return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
 }
+
+int memfd_clone_fd(int fd, const char *name, int mode) {
+        _cleanup_close_ int mfd = -EBADF;
+        bool ro;
+        int r;
+
+        /* Creates a clone of a regular file in a memfd. Unlike copy_data_fd() this returns strictly a memfd
+         * (and if it can't it will fail). Thus the resulting fd is seekable, and definitely reports as
+         * S_ISREG. */
+
+        assert(fd >= 0);
+        assert(name);
+        assert(IN_SET(mode & O_ACCMODE, O_RDONLY, O_RDWR));
+        assert((mode & ~(O_RDONLY|O_RDWR|O_CLOEXEC)) == 0);
+
+        ro = (mode & O_ACCMODE) == O_RDONLY;
+
+        mfd = memfd_create(name,
+                           ((FLAGS_SET(mode, O_CLOEXEC) || ro) ? MFD_CLOEXEC : 0) |
+                           (ro ? MFD_ALLOW_SEALING : 0));
+        if (mfd < 0)
+                return -errno;
+
+        r = copy_bytes(fd, mfd, UINT64_MAX, COPY_REFLINK);
+        if (r < 0)
+                return r;
+
+        if (ro) {
+                _cleanup_close_ int rfd = -EBADF;
+
+                r = memfd_set_sealed(mfd);
+                if (r < 0)
+                        return r;
+
+                rfd = fd_reopen(mfd, mode);
+                if (rfd < 0)
+                        return rfd;
+
+                return TAKE_FD(rfd);
+        }
+
+        off_t f = lseek(mfd, 0, SEEK_SET);
+        if (f < 0)
+                return -errno;
+
+        return TAKE_FD(mfd);
+}