]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
data-fd-util: add new memfd_clone_fd() helper
authorLennart Poettering <lennart@poettering.net>
Thu, 8 Dec 2022 11:45:26 +0000 (12:45 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 8 Dec 2022 11:47:01 +0000 (12:47 +0100)
This adds a new helper for cloning any file's contents (or block device contents) into a new memfd.

src/shared/data-fd-util.c
src/shared/data-fd-util.h

index b6eb3d9f4185017d4a44dc50733879a9af84e025..58ee1845afd6ca61117edfc31876b9ef03d1b326 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 */
@@ -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 = -1;
+
+                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);
+}
index 827e149662d6a49671585cef43414cc9783f82ca..f0e7923556f7a4a339892099367857d7822703db 100644 (file)
@@ -5,3 +5,4 @@
 
 int acquire_data_fd(const void *data, size_t size, unsigned flags);
 int copy_data_fd(int fd);
+int memfd_clone_fd(int fd, const char *name, int mode);