chiark / gitweb /
memfd: move code from public library to src/shared
[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_close_ int kdbus = -1;
43         _cleanup_free_ char *g = NULL;
44         sd_memfd *n;
45
46         assert_return(m, -EINVAL);
47
48         kdbus = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
49         if (kdbus < 0)
50                 return -errno;
51
52         if (name) {
53                 /* The kernel side is pretty picky about the character
54                  * set here, let's do the usual bus escaping to deal
55                  * with that. */
56
57                 g = bus_label_escape(name);
58                 if (!g)
59                         return -ENOMEM;
60
61                 name = g;
62
63         } else {
64                 char pr[17] = {};
65
66                 /* If no name is specified we generate one. We include
67                  * a hint indicating our library implementation, and
68                  * add the thread name to it */
69
70                 assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0);
71
72                 if (isempty(pr))
73                         name = "sd";
74                 else {
75                         _cleanup_free_ char *e = NULL;
76
77                         e = bus_label_escape(pr);
78                         if (!e)
79                                 return -ENOMEM;
80
81                         g = strappend("sd-", e);
82                         if (!g)
83                                 return -ENOMEM;
84
85                         name = g;
86                 }
87         }
88
89         n = new0(struct sd_memfd, 1);
90         if (!n)
91                 return -ENOMEM;
92
93         n->fd = memfd_create(name, 0, MFD_ALLOW_SEALING);
94         if (n->fd < 0) {
95                 free(n);
96                 return -errno;
97         }
98
99         *m = n;
100         return 0;
101 }
102
103 int sd_memfd_new_from_fd(sd_memfd **m, int fd) {
104         sd_memfd *n;
105
106         assert_return(m, -EINVAL);
107         assert_return(fd >= 0, -EINVAL);
108
109         /* Check if this is a sealable fd */
110         if (fcntl(fd, F_GET_SEALS) < 0)
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         q = mmap(NULL, size, sealed ? PROT_READ : PROT_READ|PROT_WRITE, MAP_PRIVATE, m->fd, offset);
180         if (q == MAP_FAILED)
181                 return -errno;
182
183         *p = q;
184         return 0;
185 }
186
187 int sd_memfd_set_sealed(sd_memfd *m) {
188         int r;
189
190         assert_return(m, -EINVAL);
191
192         r = fcntl(m->fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
193         if (r < 0)
194                 return -errno;
195
196         return 0;
197 }
198
199 int sd_memfd_get_sealed(sd_memfd *m) {
200         int r;
201
202         assert_return(m, -EINVAL);
203
204         r = fcntl(m->fd, F_GET_SEALS);
205         if (r < 0)
206                 return -errno;
207
208         return (r & (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)) ==
209                     (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
210 }
211
212 int sd_memfd_get_size(sd_memfd *m, uint64_t *sz) {
213         int r;
214         struct stat stat;
215
216         assert_return(m, -EINVAL);
217         assert_return(sz, -EINVAL);
218
219         r = fstat(m->fd, &stat);
220         if (r < 0)
221                 return -errno;
222
223         *sz = stat.st_size;
224         return r;
225 }
226
227 int sd_memfd_set_size(sd_memfd *m, uint64_t sz) {
228         int r;
229
230         assert_return(m, -EINVAL);
231
232         r = ftruncate(m->fd, sz);
233         if (r < 0)
234                 return -errno;
235
236         return r;
237 }
238
239 int sd_memfd_new_and_map(sd_memfd **m, const char *name, size_t sz, void **p) {
240         sd_memfd *n;
241         int r;
242
243         r = sd_memfd_new(&n, name);
244         if (r < 0)
245                 return r;
246
247         r = sd_memfd_set_size(n, sz);
248         if (r < 0) {
249                 sd_memfd_free(n);
250                 return r;
251         }
252
253         r = sd_memfd_map(n, 0, sz, p);
254         if (r < 0) {
255                 sd_memfd_free(n);
256                 return r;
257         }
258
259         *m = n;
260         return 0;
261 }
262
263 int sd_memfd_get_name(sd_memfd *m, char **name) {
264         char path[sizeof("/proc/self/fd/") + DECIMAL_STR_MAX(int)], buf[FILENAME_MAX+1], *e;
265         const char *delim, *end;
266         _cleanup_free_ char *n = NULL;
267         ssize_t k;
268
269         assert_return(m, -EINVAL);
270         assert_return(name, -EINVAL);
271
272         sprintf(path, "/proc/self/fd/%i", m->fd);
273
274         k = readlink(path, buf, sizeof(buf));
275         if (k < 0)
276                 return -errno;
277
278         if ((size_t) k >= sizeof(buf))
279                 return -E2BIG;
280
281         buf[k] = 0;
282
283         delim = strstr(buf, ":[");
284         if (!delim)
285                 return -EIO;
286
287         delim = strchr(delim + 2, ':');
288         if (!delim)
289                 return -EIO;
290
291         delim++;
292
293         end = strchr(delim, ']');
294         if (!end)
295                 return -EIO;
296
297         n = strndup(delim, end - delim);
298         if (!n)
299                 return -ENOMEM;
300
301         e = bus_label_unescape(n);
302         if (!e)
303                 return -ENOMEM;
304
305         *name = e;
306
307         return 0;
308 }