]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/basic/memfd-util.c
Two follow-ups for recent PRs (#38062)
[thirdparty/systemd.git] / src / basic / memfd-util.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <sched.h>
4#include <stdio.h>
5#include <sys/prctl.h>
6#include <sys/stat.h>
7#include <unistd.h>
8
9#include "alloc-util.h"
10#include "errno-util.h"
11#include "fd-util.h"
12#include "memfd-util.h"
13#include "string-util.h"
14#include "utf8.h"
15
16int memfd_create_wrapper(const char *name, unsigned mode) {
17 unsigned mode_compat;
18 int mfd;
19
20 assert(name);
21
22 /* Wrapper around memfd_create() which adds compat with older kernels where memfd_create() didn't
23 * support MFD_EXEC/MFD_NOEXEC_SEAL. (kernel 6.3+) */
24
25 mfd = RET_NERRNO(memfd_create(name, mode));
26 if (mfd != -EINVAL)
27 return mfd;
28
29 mode_compat = mode & ~(MFD_EXEC | MFD_NOEXEC_SEAL);
30
31 if (mode == mode_compat)
32 return mfd;
33
34 return RET_NERRNO(memfd_create(name, mode_compat));
35}
36
37int memfd_new_full(const char *name, unsigned extra_flags) {
38 _cleanup_free_ char *g = NULL;
39
40 if (!name) {
41 char pr[TASK_COMM_LEN] = {};
42
43 /* If no name is specified we generate one. We include
44 * a hint indicating our library implementation, and
45 * add the thread name to it */
46
47 assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0);
48
49 if (isempty(pr))
50 name = "sd";
51 else {
52 _cleanup_free_ char *e = NULL;
53
54 e = utf8_escape_invalid(pr);
55 if (!e)
56 return -ENOMEM;
57
58 g = strjoin("sd-", e);
59 if (!g)
60 return -ENOMEM;
61
62 name = g;
63 }
64 }
65
66 return memfd_create_wrapper(
67 name,
68 MFD_CLOEXEC | MFD_NOEXEC_SEAL | extra_flags);
69}
70
71static int memfd_add_seals(int fd, unsigned seals) {
72 assert(fd >= 0);
73
74 return RET_NERRNO(fcntl(fd, F_ADD_SEALS, seals));
75}
76
77static int memfd_get_seals(int fd, unsigned *ret_seals) {
78 int r;
79
80 assert(fd >= 0);
81
82 r = RET_NERRNO(fcntl(fd, F_GET_SEALS));
83 if (r < 0)
84 return r;
85
86 if (ret_seals)
87 *ret_seals = r;
88 return 0;
89}
90
91int memfd_set_sealed(int fd) {
92 return memfd_add_seals(fd, F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
93}
94
95int memfd_get_sealed(int fd) {
96 unsigned seals;
97 int r;
98
99 r = memfd_get_seals(fd, &seals);
100 if (r < 0)
101 return r;
102
103 /* We ignore F_SEAL_EXEC here to support older kernels. */
104 return FLAGS_SET(seals, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
105}
106
107int memfd_get_size(int fd, uint64_t *ret) {
108 struct stat stat;
109
110 assert(fd >= 0);
111 assert(ret);
112
113 if (fstat(fd, &stat) < 0)
114 return -errno;
115
116 *ret = stat.st_size;
117 return 0;
118}
119
120int memfd_set_size(int fd, uint64_t sz) {
121 assert(fd >= 0);
122
123 return RET_NERRNO(ftruncate(fd, sz));
124}
125
126int memfd_new_and_seal(const char *name, const void *data, size_t sz) {
127 _cleanup_close_ int fd = -EBADF;
128 int r;
129
130 assert(data || sz == 0);
131
132 if (sz == SIZE_MAX)
133 sz = strlen(data);
134
135 fd = memfd_new_full(name, MFD_ALLOW_SEALING);
136 if (fd < 0)
137 return fd;
138
139 if (sz > 0) {
140 ssize_t n = pwrite(fd, data, sz, 0);
141 if (n < 0)
142 return -errno;
143 if ((size_t) n != sz)
144 return -EIO;
145 }
146
147 r = memfd_set_sealed(fd);
148 if (r < 0)
149 return r;
150
151 return TAKE_FD(fd);
152}