chiark / gitweb /
memfd: use _cleanup_ if applicable
[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         int r;
101
102         assert_return(m, -EINVAL);
103         assert_return(fd >= 0, -EINVAL);
104
105         /* Check if this is a sealable fd. The kernel sets F_SEAL_SEAL on memfds
106          * that don't support sealing, so check for that, too. A file with
107          * *only* F_SEAL_SEAL set is the same as a random shmem file, so no
108          * reason to allow opening it as memfd. */
109         r = fcntl(fd, F_GET_SEALS);
110         if (r < 0 || r == F_SEAL_SEAL)
111                 return -ENOTTY;
112
113         n = new0(struct sd_memfd, 1);
114         if (!n)
115                 return -ENOMEM;
116
117         n->fd = fd;
118         *m = n;
119
120         return 0;
121 }
122
123 void sd_memfd_free(sd_memfd *m) {
124         if (!m)
125                 return;
126
127         if (m->f)
128                 fclose(m->f);
129         else
130                 safe_close(m->fd);
131
132         free(m);
133 }
134
135 int sd_memfd_get_fd(sd_memfd *m) {
136         assert_return(m, -EINVAL);
137
138         return m->fd;
139 }
140
141 int sd_memfd_get_file(sd_memfd *m, FILE **f) {
142         assert_return(m, -EINVAL);
143         assert_return(f, -EINVAL);
144
145         if (!m->f) {
146                 m->f = fdopen(m->fd, "r+");
147                 if (!m->f)
148                         return -errno;
149         }
150
151         *f = m->f;
152         return 0;
153 }
154
155 int sd_memfd_dup_fd(sd_memfd *m) {
156         int fd;
157
158         assert_return(m, -EINVAL);
159
160         fd = fcntl(m->fd, F_DUPFD_CLOEXEC, 3);
161         if (fd < 0)
162                 return -errno;
163
164         return fd;
165 }
166
167 int sd_memfd_map(sd_memfd *m, uint64_t offset, size_t size, void **p) {
168         void *q;
169         int sealed;
170
171         assert_return(m, -EINVAL);
172         assert_return(size > 0, -EINVAL);
173         assert_return(p, -EINVAL);
174
175         sealed = sd_memfd_get_sealed(m);
176         if (sealed < 0)
177                 return sealed;
178
179         if (sealed)
180                 q = mmap(NULL, size, PROT_READ, MAP_PRIVATE, m->fd, offset);
181         else
182                 q = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, m->fd, offset);
183
184         if (q == MAP_FAILED)
185                 return -errno;
186
187         *p = q;
188         return 0;
189 }
190
191 int sd_memfd_set_sealed(sd_memfd *m) {
192         int r;
193
194         assert_return(m, -EINVAL);
195
196         r = fcntl(m->fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
197         if (r < 0)
198                 return -errno;
199
200         return 0;
201 }
202
203 int sd_memfd_get_sealed(sd_memfd *m) {
204         int r;
205
206         assert_return(m, -EINVAL);
207
208         r = fcntl(m->fd, F_GET_SEALS);
209         if (r < 0)
210                 return -errno;
211
212         return (r & (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)) ==
213                     (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
214 }
215
216 int sd_memfd_get_size(sd_memfd *m, uint64_t *sz) {
217         int r;
218         struct stat stat;
219
220         assert_return(m, -EINVAL);
221         assert_return(sz, -EINVAL);
222
223         r = fstat(m->fd, &stat);
224         if (r < 0)
225                 return -errno;
226
227         *sz = stat.st_size;
228         return r;
229 }
230
231 int sd_memfd_set_size(sd_memfd *m, uint64_t sz) {
232         int r;
233
234         assert_return(m, -EINVAL);
235
236         r = ftruncate(m->fd, sz);
237         if (r < 0)
238                 return -errno;
239
240         return r;
241 }
242
243 int sd_memfd_new_and_map(sd_memfd **m, const char *name, size_t sz, void **p) {
244         _cleanup_(sd_memfd_freep) sd_memfd *n = NULL;
245         int r;
246
247         r = sd_memfd_new(&n, name);
248         if (r < 0)
249                 return r;
250
251         r = sd_memfd_set_size(n, sz);
252         if (r < 0)
253                 return r;
254
255         r = sd_memfd_map(n, 0, sz, p);
256         if (r < 0)
257                 return r;
258
259         *m = n;
260         n = NULL;
261         return 0;
262 }
263
264 int sd_memfd_get_name(sd_memfd *m, char **name) {
265         char path[sizeof("/proc/self/fd/") + DECIMAL_STR_MAX(int)], buf[FILENAME_MAX+1], *e;
266         const char *delim, *end;
267         _cleanup_free_ char *n = NULL;
268         ssize_t k;
269
270         assert_return(m, -EINVAL);
271         assert_return(name, -EINVAL);
272
273         sprintf(path, "/proc/self/fd/%i", m->fd);
274
275         k = readlink(path, buf, sizeof(buf));
276         if (k < 0)
277                 return -errno;
278
279         if ((size_t) k >= sizeof(buf))
280                 return -E2BIG;
281
282         buf[k] = 0;
283
284         delim = strstr(buf, ":[");
285         if (!delim)
286                 return -EIO;
287
288         delim = strchr(delim + 2, ':');
289         if (!delim)
290                 return -EIO;
291
292         delim++;
293
294         end = strchr(delim, ']');
295         if (!end)
296                 return -EIO;
297
298         n = strndup(delim, end - delim);
299         if (!n)
300                 return -ENOMEM;
301
302         e = bus_label_unescape(n);
303         if (!e)
304                 return -ENOMEM;
305
306         *name = e;
307
308         return 0;
309 }