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