]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
ddeb4241 | 2 | |
11c3a366 | 3 | #include <errno.h> |
11c3a366 TA |
4 | #include <sys/stat.h> |
5 | #include <unistd.h> | |
349cc4a5 | 6 | #if HAVE_LINUX_MEMFD_H |
07630cea | 7 | #include <linux/memfd.h> |
27c64db6 | 8 | #endif |
07630cea | 9 | #include <stdio.h> |
07630cea | 10 | #include <sys/prctl.h> |
27c64db6 | 11 | |
b5efdb8a | 12 | #include "alloc-util.h" |
7c248223 | 13 | #include "errno-util.h" |
3ffd4af2 | 14 | #include "fd-util.h" |
93cc7779 | 15 | #include "macro.h" |
3ffd4af2 | 16 | #include "memfd-util.h" |
f5947a5e YW |
17 | #include "missing_fcntl.h" |
18 | #include "missing_mman.h" | |
19 | #include "missing_syscall.h" | |
07630cea LP |
20 | #include "string-util.h" |
21 | #include "utf8.h" | |
ddeb4241 | 22 | |
ad62530e TW |
23 | int memfd_create_wrapper(const char *name, unsigned mode) { |
24 | unsigned mode_compat; | |
25 | int mfd; | |
26 | ||
27 | mfd = RET_NERRNO(memfd_create(name, mode)); | |
28 | if (mfd != -EINVAL) | |
29 | return mfd; | |
30 | ||
31 | mode_compat = mode & ~(MFD_EXEC | MFD_NOEXEC_SEAL); | |
32 | ||
33 | if (mode == mode_compat) | |
34 | return mfd; | |
35 | ||
36 | return RET_NERRNO(memfd_create(name, mode_compat)); | |
37 | } | |
38 | ||
4531a9bc | 39 | int memfd_new(const char *name) { |
7f96b1d8 | 40 | _cleanup_free_ char *g = NULL; |
ddeb4241 | 41 | |
5755381f | 42 | if (!name) { |
7f96b1d8 LP |
43 | char pr[17] = {}; |
44 | ||
45 | /* If no name is specified we generate one. We include | |
46 | * a hint indicating our library implementation, and | |
47 | * add the thread name to it */ | |
48 | ||
49 | assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0); | |
50 | ||
51 | if (isempty(pr)) | |
52 | name = "sd"; | |
53 | else { | |
5755381f LP |
54 | _cleanup_free_ char *e = NULL; |
55 | ||
56 | e = utf8_escape_invalid(pr); | |
57 | if (!e) | |
58 | return -ENOMEM; | |
59 | ||
b910cc72 | 60 | g = strjoin("sd-", e); |
7f96b1d8 LP |
61 | if (!g) |
62 | return -ENOMEM; | |
63 | ||
64 | name = g; | |
65 | } | |
66 | } | |
67 | ||
c29715a8 | 68 | return memfd_create_wrapper(name, MFD_ALLOW_SEALING | MFD_CLOEXEC | MFD_NOEXEC_SEAL); |
ddeb4241 LP |
69 | } |
70 | ||
4d903003 DR |
71 | int memfd_add_seals(int fd, unsigned int seals) { |
72 | assert(fd >= 0); | |
73 | ||
74 | return RET_NERRNO(fcntl(fd, F_ADD_SEALS, seals)); | |
75 | } | |
76 | ||
77 | int memfd_get_seals(int fd, unsigned int *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 | ||
fac9c0d5 | 91 | int memfd_map(int fd, uint64_t offset, size_t size, void **p) { |
ddeb4241 LP |
92 | void *q; |
93 | int sealed; | |
94 | ||
4531a9bc LP |
95 | assert(fd >= 0); |
96 | assert(size > 0); | |
97 | assert(p); | |
ddeb4241 | 98 | |
fac9c0d5 | 99 | sealed = memfd_get_sealed(fd); |
ddeb4241 LP |
100 | if (sealed < 0) |
101 | return sealed; | |
102 | ||
23972f42 | 103 | if (sealed) |
fac9c0d5 | 104 | q = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset); |
23972f42 | 105 | else |
fac9c0d5 | 106 | q = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); |
ddeb4241 LP |
107 | if (q == MAP_FAILED) |
108 | return -errno; | |
109 | ||
110 | *p = q; | |
111 | return 0; | |
112 | } | |
113 | ||
fac9c0d5 | 114 | int memfd_set_sealed(int fd) { |
4d903003 | 115 | return memfd_add_seals(fd, F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE); |
ddeb4241 LP |
116 | } |
117 | ||
fac9c0d5 | 118 | int memfd_get_sealed(int fd) { |
4d903003 | 119 | unsigned int seals; |
a6082d77 | 120 | int r; |
ddeb4241 | 121 | |
4d903003 | 122 | r = memfd_get_seals(fd, &seals); |
ddeb4241 | 123 | if (r < 0) |
4d903003 | 124 | return r; |
ddeb4241 | 125 | |
17915ea5 | 126 | /* We ignore F_SEAL_EXEC here to support older kernels. */ |
4d903003 | 127 | return FLAGS_SET(seals, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE); |
ddeb4241 LP |
128 | } |
129 | ||
fac9c0d5 | 130 | int memfd_get_size(int fd, uint64_t *sz) { |
a6082d77 | 131 | struct stat stat; |
ddeb4241 | 132 | |
4531a9bc LP |
133 | assert(fd >= 0); |
134 | assert(sz); | |
ddeb4241 | 135 | |
7c248223 | 136 | if (fstat(fd, &stat) < 0) |
ddeb4241 LP |
137 | return -errno; |
138 | ||
a6082d77 | 139 | *sz = stat.st_size; |
5755381f | 140 | return 0; |
ddeb4241 LP |
141 | } |
142 | ||
fac9c0d5 | 143 | int memfd_set_size(int fd, uint64_t sz) { |
4531a9bc | 144 | assert(fd >= 0); |
ddeb4241 | 145 | |
7c248223 | 146 | return RET_NERRNO(ftruncate(fd, sz)); |
ddeb4241 | 147 | } |
453a0c29 | 148 | |
4531a9bc | 149 | int memfd_new_and_map(const char *name, size_t sz, void **p) { |
254d1313 | 150 | _cleanup_close_ int fd = -EBADF; |
453a0c29 LP |
151 | int r; |
152 | ||
4531a9bc LP |
153 | assert(sz > 0); |
154 | assert(p); | |
155 | ||
156 | fd = memfd_new(name); | |
157 | if (fd < 0) | |
158 | return fd; | |
453a0c29 | 159 | |
4531a9bc | 160 | r = memfd_set_size(fd, sz); |
8a02deca | 161 | if (r < 0) |
453a0c29 | 162 | return r; |
453a0c29 | 163 | |
4531a9bc | 164 | r = memfd_map(fd, 0, sz, p); |
8a02deca | 165 | if (r < 0) |
453a0c29 | 166 | return r; |
453a0c29 | 167 | |
c10d6bdb | 168 | return TAKE_FD(fd); |
453a0c29 | 169 | } |
44777d7a DDM |
170 | |
171 | int memfd_new_and_seal(const char *name, const void *data, size_t sz) { | |
172 | _cleanup_close_ int fd = -EBADF; | |
173 | ssize_t n; | |
174 | off_t f; | |
175 | int r; | |
176 | ||
177 | assert(data || sz == 0); | |
178 | ||
179 | fd = memfd_new(name); | |
180 | if (fd < 0) | |
181 | return fd; | |
182 | ||
183 | if (sz > 0) { | |
184 | n = write(fd, data, sz); | |
185 | if (n < 0) | |
186 | return -errno; | |
187 | if ((size_t) n != sz) | |
188 | return -EIO; | |
189 | ||
190 | f = lseek(fd, 0, SEEK_SET); | |
191 | if (f != 0) | |
192 | return -errno; | |
193 | } | |
194 | ||
195 | r = memfd_set_sealed(fd); | |
196 | if (r < 0) | |
197 | return r; | |
198 | ||
199 | return TAKE_FD(fd); | |
200 | } |