]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/machine/image.c
machined: make image read-only check indepenednt on own privs
[thirdparty/systemd.git] / src / machine / image.c
... / ...
CommitLineData
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 <sys/statfs.h>
23
24#include "strv.h"
25#include "utf8.h"
26#include "btrfs-util.h"
27#include "path-util.h"
28#include "image.h"
29
30static const char image_search_path[] =
31 "/var/lib/machines\0"
32 "/var/lib/container\0"
33 "/usr/local/lib/machines\0"
34 "/usr/lib/machines\0";
35
36Image *image_unref(Image *i) {
37 if (!i)
38 return NULL;
39
40 free(i->name);
41 free(i->path);
42 free(i);
43 return NULL;
44}
45
46static int image_new(
47 ImageType t,
48 const char *pretty,
49 const char *path,
50 const char *filename,
51 bool read_only,
52 usec_t crtime,
53 usec_t mtime,
54 Image **ret) {
55
56 _cleanup_(image_unrefp) Image *i = NULL;
57
58 assert(t >= 0);
59 assert(t < _IMAGE_TYPE_MAX);
60 assert(pretty);
61 assert(filename);
62 assert(ret);
63
64 i = new0(Image, 1);
65 if (!i)
66 return -ENOMEM;
67
68 i->type = t;
69 i->read_only = read_only;
70 i->crtime = crtime;
71 i->mtime = mtime;
72
73 i->name = strdup(pretty);
74 if (!i->name)
75 return -ENOMEM;
76
77 if (path)
78 i->path = strjoin(path, "/", filename, NULL);
79 else
80 i->path = strdup(filename);
81
82 if (!i->path)
83 return -ENOMEM;
84
85 path_kill_slashes(i->path);
86
87 *ret = i;
88 i = NULL;
89
90 return 0;
91}
92
93static int image_make(
94 const char *pretty,
95 int dfd,
96 const char *path,
97 const char *filename,
98 Image **ret) {
99
100 struct stat st;
101 bool read_only;
102 int r;
103
104 assert(filename);
105
106 /* We explicitly *do* follow symlinks here, since we want to
107 * allow symlinking trees into /var/lib/container/, and treat
108 * them normally. */
109
110 if (fstatat(dfd, filename, &st, 0) < 0)
111 return -errno;
112
113 read_only =
114 (path && path_startswith(path, "/usr")) ||
115 (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);
116
117 if (S_ISDIR(st.st_mode)) {
118
119 if (!ret)
120 return 1;
121
122 if (!pretty)
123 pretty = filename;
124
125 /* btrfs subvolumes have inode 256 */
126 if (st.st_ino == 256) {
127 _cleanup_close_ int fd = -1;
128 struct statfs sfs;
129
130 fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
131 if (fd < 0)
132 return -errno;
133
134 if (fstatfs(fd, &sfs) < 0)
135 return -errno;
136
137 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
138 BtrfsSubvolInfo info;
139
140 /* It's a btrfs subvolume */
141
142 r = btrfs_subvol_get_info_fd(fd, &info);
143 if (r < 0)
144 return r;
145
146 r = image_new(IMAGE_SUBVOLUME,
147 pretty,
148 path,
149 filename,
150 info.read_only || read_only,
151 info.otime,
152 0,
153 ret);
154 if (r < 0)
155 return r;
156
157 return 1;
158 }
159 }
160
161 /* It's just a normal directory. */
162
163 r = image_new(IMAGE_DIRECTORY,
164 pretty,
165 path,
166 filename,
167 read_only,
168 0,
169 0,
170 ret);
171 if (r < 0)
172 return r;
173
174 return 1;
175
176 } else if (S_ISREG(st.st_mode) && endswith(filename, ".gpt")) {
177 usec_t crtime = 0;
178
179 /* It's a GPT block device */
180
181 if (!ret)
182 return 1;
183
184 fd_getcrtime_at(dfd, filename, &crtime, 0);
185
186 if (!pretty)
187 pretty = strndupa(filename, strlen(filename) - 4);
188
189 r = image_new(IMAGE_GPT,
190 pretty,
191 path,
192 filename,
193 !(st.st_mode & 0222) || read_only,
194 crtime,
195 timespec_load(&st.st_mtim),
196 ret);
197 if (r < 0)
198 return r;
199
200 return 1;
201 }
202
203 return 0;
204}
205
206int image_find(const char *name, Image **ret) {
207 const char *path;
208 int r;
209
210 assert(name);
211
212 /* There are no images with invalid names */
213 if (!image_name_is_valid(name))
214 return 0;
215
216 NULSTR_FOREACH(path, image_search_path) {
217 _cleanup_closedir_ DIR *d = NULL;
218
219 d = opendir(path);
220 if (!d) {
221 if (errno == ENOENT)
222 continue;
223
224 return -errno;
225 }
226
227 r = image_make(NULL, dirfd(d), path, name, ret);
228 if (r == 0 || r == -ENOENT) {
229 _cleanup_free_ char *gpt = NULL;
230
231 gpt = strappend(name, ".gpt");
232 if (!gpt)
233 return -ENOMEM;
234
235 r = image_make(NULL, dirfd(d), path, gpt, ret);
236 if (r == 0 || r == -ENOENT)
237 continue;
238 }
239 if (r < 0)
240 return r;
241
242 return 1;
243 }
244
245 if (streq(name, ".host"))
246 return image_make(NULL, AT_FDCWD, NULL, "/", ret);
247
248 return 0;
249};
250
251int image_discover(Hashmap *h) {
252 const char *path;
253 int r;
254
255 assert(h);
256
257 NULSTR_FOREACH(path, image_search_path) {
258 _cleanup_closedir_ DIR *d = NULL;
259 struct dirent *de;
260
261 d = opendir(path);
262 if (!d) {
263 if (errno == ENOENT)
264 continue;
265
266 return -errno;
267 }
268
269 FOREACH_DIRENT_ALL(de, d, return -errno) {
270 _cleanup_(image_unrefp) Image *image = NULL;
271
272 if (!image_name_is_valid(de->d_name))
273 continue;
274
275 if (hashmap_contains(h, de->d_name))
276 continue;
277
278 r = image_make(NULL, dirfd(d), path, de->d_name, &image);
279 if (r == 0 || r == -ENOENT)
280 continue;
281 if (r < 0)
282 return r;
283
284 r = hashmap_put(h, image->name, image);
285 if (r < 0)
286 return r;
287
288 image = NULL;
289 }
290 }
291
292 if (!hashmap_contains(h, ".host")) {
293 _cleanup_(image_unrefp) Image *image = NULL;
294
295 r = image_make(".host", AT_FDCWD, NULL, "/", &image);
296 if (r < 0)
297 return r;
298
299 r = hashmap_put(h, image->name, image);
300 if (r < 0)
301 return r;
302
303 image = NULL;
304
305 }
306
307 return 0;
308}
309
310void image_hashmap_free(Hashmap *map) {
311 Image *i;
312
313 while ((i = hashmap_steal_first(map)))
314 image_unref(i);
315
316 hashmap_free(map);
317}
318
319static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
320 [IMAGE_DIRECTORY] = "directory",
321 [IMAGE_SUBVOLUME] = "subvolume",
322 [IMAGE_GPT] = "gpt",
323};
324
325DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);