chiark / gitweb /
6a2e12196a05e8a1362d0709fb8c467ea12bdd83
[elogind.git] / src / shared / memfd.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdio.h>
23 #include <fcntl.h>
24 #include <sys/ioctl.h>
25 #include <sys/mman.h>
26 #include <sys/prctl.h>
27
28 #include "util.h"
29 #include "bus-label.h"
30 #include "missing.h"
31 #include "memfd.h"
32
33 #include "sd-bus.h"
34
35 struct sd_memfd {
36         int fd;
37         FILE *f;
38 };
39
40 int sd_memfd_new(sd_memfd **m, const char *name) {
41
42         _cleanup_free_ char *g = NULL;
43         sd_memfd *n;
44
45         assert_return(m, -EINVAL);
46
47         if (name) {
48                 /* The kernel side is pretty picky about the character
49                  * set here, let's do the usual bus escaping to deal
50                  * with that. */
51
52                 g = bus_label_escape(name);
53                 if (!g)
54                         return -ENOMEM;
55
56                 name = g;
57
58         } else {
59                 char pr[17] = {};
60
61                 /* If no name is specified we generate one. We include
62                  * a hint indicating our library implementation, and
63                  * add the thread name to it */
64
65                 assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0);
66
67                 if (isempty(pr))
68                         name = "sd";
69                 else {
70                         _cleanup_free_ char *e = NULL;
71
72                         e = bus_label_escape(pr);
73                         if (!e)
74                                 return -ENOMEM;
75
76                         g = strappend("sd-", e);
77                         if (!g)
78                                 return -ENOMEM;
79
80                         name = g;
81                 }
82         }
83
84         n = new0(struct sd_memfd, 1);
85         if (!n)
86                 return -ENOMEM;
87
88         n->fd = memfd_create(name, MFD_ALLOW_SEALING);
89         if (n->fd < 0) {
90                 free(n);
91                 return -errno;
92         }
93
94         *m = n;
95         return 0;
96 }
97
98 int sd_memfd_new_from_fd(sd_memfd **m, int fd) {
99         sd_memfd *n;
100
101         assert_return(m, -EINVAL);
102         assert_return(fd >= 0, -EINVAL);
103
104         /* Check if this is a sealable fd */
105         if (fcntl(fd, F_GET_SEALS) < 0)
106                 return -ENOTTY;
107
108         n = new0(struct sd_memfd, 1);
109         if (!n)
110                 return -ENOMEM;
111
112         n->fd = fd;
113         *m = n;
114
115         return 0;
116 }
117
118 void sd_memfd_free(sd_memfd *m) {
119         if (!m)
120                 return;
121
122         if (m->f)
123                 fclose(m->f);
124         else
125                 safe_close(m->fd);
126
127         free(m);
128 }
129
130 int sd_memfd_get_fd(sd_memfd *m) {
131         assert_return(m, -EINVAL);
132
133         return m->fd;
134 }
135
136 int sd_memfd_get_file(sd_memfd *m, FILE **f) {
137         assert_return(m, -EINVAL);
138         assert_return(f, -EINVAL);
139
140         if (!m->f) {
141                 m->f = fdopen(m->fd, "r+");
142                 if (!m->f)
143                         return -errno;
144         }
145
146         *f = m->f;
147         return 0;
148 }
149
150 int sd_memfd_dup_fd(sd_memfd *m) {
151         int fd;
152
153         assert_return(m, -EINVAL);
154
155         fd = fcntl(m->fd, F_DUPFD_CLOEXEC, 3);
156         if (fd < 0)
157                 return -errno;
158
159         return fd;
160 }
161
162 int sd_memfd_map(sd_memfd *m, uint64_t offset, size_t size, void **p) {
163         void *q;
164         int sealed;
165
166         assert_return(m, -EINVAL);
167         assert_return(size > 0, -EINVAL);
168         assert_return(p, -EINVAL);
169
170         sealed = sd_memfd_get_sealed(m);
171         if (sealed < 0)
172                 return sealed;
173
174         q = mmap(NULL, size, sealed ? PROT_READ : PROT_READ|PROT_WRITE, MAP_PRIVATE, m->fd, offset);
175         if (q == MAP_FAILED)
176                 return -errno;
177
178         *p = q;
179         return 0;
180 }
181
182 int sd_memfd_set_sealed(sd_memfd *m) {
183         int r;
184
185         assert_return(m, -EINVAL);
186
187         r = fcntl(m->fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
188         if (r < 0)
189                 return -errno;
190
191         return 0;
192 }
193
194 int sd_memfd_get_sealed(sd_memfd *m) {
195         int r;
196
197         assert_return(m, -EINVAL);
198
199         r = fcntl(m->fd, F_GET_SEALS);
200         if (r < 0)
201                 return -errno;
202
203         return (r & (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)) ==
204                     (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
205 }
206
207 int sd_memfd_get_size(sd_memfd *m, uint64_t *sz) {
208         int r;
209         struct stat stat;
210
211         assert_return(m, -EINVAL);
212         assert_return(sz, -EINVAL);
213
214         r = fstat(m->fd, &stat);
215         if (r < 0)
216                 return -errno;
217
218         *sz = stat.st_size;
219         return r;
220 }
221
222 int sd_memfd_set_size(sd_memfd *m, uint64_t sz) {
223         int r;
224
225         assert_return(m, -EINVAL);
226
227         r = ftruncate(m->fd, sz);
228         if (r < 0)
229                 return -errno;
230
231         return r;
232 }
233
234 int sd_memfd_new_and_map(sd_memfd **m, const char *name, size_t sz, void **p) {
235         sd_memfd *n;
236         int r;
237
238         r = sd_memfd_new(&n, name);
239         if (r < 0)
240                 return r;
241
242         r = sd_memfd_set_size(n, sz);
243         if (r < 0) {
244                 sd_memfd_free(n);
245                 return r;
246         }
247
248         r = sd_memfd_map(n, 0, sz, p);
249         if (r < 0) {
250                 sd_memfd_free(n);
251                 return r;
252         }
253
254         *m = n;
255         return 0;
256 }
257
258 int sd_memfd_get_name(sd_memfd *m, char **name) {
259         char path[sizeof("/proc/self/fd/") + DECIMAL_STR_MAX(int)], buf[FILENAME_MAX+1], *e;
260         const char *delim, *end;
261         _cleanup_free_ char *n = NULL;
262         ssize_t k;
263
264         assert_return(m, -EINVAL);
265         assert_return(name, -EINVAL);
266
267         sprintf(path, "/proc/self/fd/%i", m->fd);
268
269         k = readlink(path, buf, sizeof(buf));
270         if (k < 0)
271                 return -errno;
272
273         if ((size_t) k >= sizeof(buf))
274                 return -E2BIG;
275
276         buf[k] = 0;
277
278         delim = strstr(buf, ":[");
279         if (!delim)
280                 return -EIO;
281
282         delim = strchr(delim + 2, ':');
283         if (!delim)
284                 return -EIO;
285
286         delim++;
287
288         end = strchr(delim, ']');
289         if (!end)
290                 return -EIO;
291
292         n = strndup(delim, end - delim);
293         if (!n)
294                 return -ENOMEM;
295
296         e = bus_label_unescape(n);
297         if (!e)
298                 return -ENOMEM;
299
300         *name = e;
301
302         return 0;
303 }