chiark / gitweb /
memfd: simplify API
[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 int memfd_new(const char *name) {
34
35         _cleanup_free_ char *g = NULL;
36         int fd;
37
38         assert(name);
39
40         if (name) {
41                 /* The kernel side is pretty picky about the character
42                  * set here, let's do the usual bus escaping to deal
43                  * with that. */
44
45                 g = bus_label_escape(name);
46                 if (!g)
47                         return -ENOMEM;
48
49                 name = g;
50
51         } else {
52                 char pr[17] = {};
53
54                 /* If no name is specified we generate one. We include
55                  * a hint indicating our library implementation, and
56                  * add the thread name to it */
57
58                 assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0);
59
60                 if (isempty(pr))
61                         name = "sd";
62                 else {
63                         _cleanup_free_ char *e = NULL;
64
65                         e = bus_label_escape(pr);
66                         if (!e)
67                                 return -ENOMEM;
68
69                         g = strappend("sd-", e);
70                         if (!g)
71                                 return -ENOMEM;
72
73                         name = g;
74                 }
75         }
76
77         fd = memfd_create(name, MFD_ALLOW_SEALING);
78         if (fd < 0)
79                 return -errno;
80
81         return fd;
82 }
83
84 int memfd_map(int fd, uint64_t offset, size_t size, void **p) {
85         void *q;
86         int sealed;
87
88         assert(fd >= 0);
89         assert(size > 0);
90         assert(p);
91
92         sealed = memfd_get_sealed(fd);
93         if (sealed < 0)
94                 return sealed;
95
96         if (sealed)
97                 q = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset);
98         else
99                 q = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
100
101         if (q == MAP_FAILED)
102                 return -errno;
103
104         *p = q;
105         return 0;
106 }
107
108 int memfd_set_sealed(int fd) {
109         int r;
110
111         assert(fd >= 0);
112
113         r = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
114         if (r < 0)
115                 return -errno;
116
117         return 0;
118 }
119
120 int memfd_get_sealed(int fd) {
121         int r;
122
123         assert(fd >= 0);
124
125         r = fcntl(fd, F_GET_SEALS);
126         if (r < 0)
127                 return -errno;
128
129         return (r & (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)) ==
130                     (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE);
131 }
132
133 int memfd_get_size(int fd, uint64_t *sz) {
134         struct stat stat;
135         int r;
136
137         assert(fd >= 0);
138         assert(sz);
139
140         r = fstat(fd, &stat);
141         if (r < 0)
142                 return -errno;
143
144         *sz = stat.st_size;
145         return r;
146 }
147
148 int memfd_set_size(int fd, uint64_t sz) {
149         int r;
150
151         assert(fd >= 0);
152
153         r = ftruncate(fd, sz);
154         if (r < 0)
155                 return -errno;
156
157         return r;
158 }
159
160 int memfd_new_and_map(const char *name, size_t sz, void **p) {
161         _cleanup_close_ int fd = -1;
162         int r;
163
164         assert(name);
165         assert(sz > 0);
166         assert(p);
167
168         fd = memfd_new(name);
169         if (fd < 0)
170                 return fd;
171
172         r = memfd_set_size(fd, sz);
173         if (r < 0)
174                 return r;
175
176         r = memfd_map(fd, 0, sz, p);
177         if (r < 0)
178                 return r;
179
180         r = fd;
181         fd = -1;
182
183         return r;
184 }
185
186 int memfd_get_name(int fd, char **name) {
187         char path[sizeof("/proc/self/fd/") + DECIMAL_STR_MAX(int)], buf[FILENAME_MAX+1], *e;
188         const char *delim, *end;
189         _cleanup_free_ char *n = NULL;
190         ssize_t k;
191
192         assert(fd >= 0);
193         assert(name);
194
195         sprintf(path, "/proc/self/fd/%i", fd);
196
197         k = readlink(path, buf, sizeof(buf));
198         if (k < 0)
199                 return -errno;
200
201         if ((size_t) k >= sizeof(buf))
202                 return -E2BIG;
203
204         buf[k] = 0;
205
206         delim = strstr(buf, ":[");
207         if (!delim)
208                 return -EIO;
209
210         delim = strchr(delim + 2, ':');
211         if (!delim)
212                 return -EIO;
213
214         delim++;
215
216         end = strchr(delim, ']');
217         if (!end)
218                 return -EIO;
219
220         n = strndup(delim, end - delim);
221         if (!n)
222                 return -ENOMEM;
223
224         e = bus_label_unescape(n);
225         if (!e)
226                 return -ENOMEM;
227
228         *name = e;
229
230         return 0;
231 }