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