]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/image.c
machined: make image read-only check indepenednt on own privs
[thirdparty/systemd.git] / src / machine / image.c
CommitLineData
cd61c3bf
LP
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"
ebeccf9e 27#include "path-util.h"
cd61c3bf 28#include "image.h"
cd61c3bf 29
c2ce6a3d 30static const char image_search_path[] =
42c6f2c9 31 "/var/lib/machines\0"
c2ce6a3d 32 "/var/lib/container\0"
42c6f2c9
LP
33 "/usr/local/lib/machines\0"
34 "/usr/lib/machines\0";
c2ce6a3d 35
cd61c3bf
LP
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
c2ce6a3d 46static int image_new(
cd61c3bf 47 ImageType t,
5fc7f358 48 const char *pretty,
cd61c3bf 49 const char *path,
5fc7f358 50 const char *filename,
cd61c3bf 51 bool read_only,
10f9c755 52 usec_t crtime,
cd61c3bf 53 usec_t mtime,
c2ce6a3d 54 Image **ret) {
cd61c3bf
LP
55
56 _cleanup_(image_unrefp) Image *i = NULL;
cd61c3bf 57
cd61c3bf
LP
58 assert(t >= 0);
59 assert(t < _IMAGE_TYPE_MAX);
5fc7f358
LP
60 assert(pretty);
61 assert(filename);
c2ce6a3d 62 assert(ret);
cd61c3bf 63
c2ce6a3d 64 i = new0(Image, 1);
cd61c3bf
LP
65 if (!i)
66 return -ENOMEM;
67
68 i->type = t;
69 i->read_only = read_only;
10f9c755 70 i->crtime = crtime;
cd61c3bf 71 i->mtime = mtime;
cd61c3bf 72
5fc7f358 73 i->name = strdup(pretty);
cd61c3bf
LP
74 if (!i->name)
75 return -ENOMEM;
76
5fc7f358
LP
77 if (path)
78 i->path = strjoin(path, "/", filename, NULL);
79 else
80 i->path = strdup(filename);
ebeccf9e 81
5fc7f358
LP
82 if (!i->path)
83 return -ENOMEM;
84
85 path_kill_slashes(i->path);
cd61c3bf 86
c2ce6a3d 87 *ret = i;
cd61c3bf 88 i = NULL;
c2ce6a3d 89
cd61c3bf
LP
90 return 0;
91}
92
5fc7f358
LP
93static int image_make(
94 const char *pretty,
95 int dfd,
96 const char *path,
97 const char *filename,
98 Image **ret) {
99
c2ce6a3d 100 struct stat st;
5fc7f358 101 bool read_only;
cd61c3bf
LP
102 int r;
103
5fc7f358 104 assert(filename);
cd61c3bf 105
c2ce6a3d
LP
106 /* We explicitly *do* follow symlinks here, since we want to
107 * allow symlinking trees into /var/lib/container/, and treat
108 * them normally. */
cd61c3bf 109
5fc7f358 110 if (fstatat(dfd, filename, &st, 0) < 0)
c2ce6a3d 111 return -errno;
cd61c3bf 112
5fc7f358
LP
113 read_only =
114 (path && path_startswith(path, "/usr")) ||
08ff5529 115 (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);
86e339c8 116
c2ce6a3d 117 if (S_ISDIR(st.st_mode)) {
cd61c3bf 118
c2ce6a3d
LP
119 if (!ret)
120 return 1;
cd61c3bf 121
5fc7f358
LP
122 if (!pretty)
123 pretty = filename;
124
c2ce6a3d
LP
125 /* btrfs subvolumes have inode 256 */
126 if (st.st_ino == 256) {
127 _cleanup_close_ int fd = -1;
128 struct statfs sfs;
cd61c3bf 129
5fc7f358 130 fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
c2ce6a3d
LP
131 if (fd < 0)
132 return -errno;
cd61c3bf 133
c2ce6a3d
LP
134 if (fstatfs(fd, &sfs) < 0)
135 return -errno;
cd61c3bf 136
c2ce6a3d 137 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
10f9c755 138 BtrfsSubvolInfo info;
cd61c3bf 139
c2ce6a3d 140 /* It's a btrfs subvolume */
cd61c3bf 141
10f9c755
LP
142 r = btrfs_subvol_get_info_fd(fd, &info);
143 if (r < 0)
144 return r;
c2ce6a3d
LP
145
146 r = image_new(IMAGE_SUBVOLUME,
5fc7f358 147 pretty,
c2ce6a3d 148 path,
5fc7f358
LP
149 filename,
150 info.read_only || read_only,
10f9c755 151 info.otime,
c2ce6a3d 152 0,
c2ce6a3d
LP
153 ret);
154 if (r < 0)
155 return r;
156
157 return 1;
cd61c3bf 158 }
c2ce6a3d 159 }
cd61c3bf 160
c2ce6a3d 161 /* It's just a normal directory. */
cd61c3bf 162
c2ce6a3d 163 r = image_new(IMAGE_DIRECTORY,
5fc7f358 164 pretty,
c2ce6a3d 165 path,
5fc7f358
LP
166 filename,
167 read_only,
c2ce6a3d
LP
168 0,
169 0,
170 ret);
171 if (r < 0)
172 return r;
cd61c3bf 173
c2ce6a3d 174 return 1;
cd61c3bf 175
5fc7f358 176 } else if (S_ISREG(st.st_mode) && endswith(filename, ".gpt")) {
10f9c755 177 usec_t crtime = 0;
cd61c3bf 178
c2ce6a3d 179 /* It's a GPT block device */
cd61c3bf 180
c2ce6a3d
LP
181 if (!ret)
182 return 1;
cd61c3bf 183
5fc7f358 184 fd_getcrtime_at(dfd, filename, &crtime, 0);
10f9c755 185
5fc7f358
LP
186 if (!pretty)
187 pretty = strndupa(filename, strlen(filename) - 4);
10f9c755 188
c2ce6a3d 189 r = image_new(IMAGE_GPT,
5fc7f358 190 pretty,
c2ce6a3d 191 path,
5fc7f358
LP
192 filename,
193 !(st.st_mode & 0222) || read_only,
10f9c755 194 crtime,
c2ce6a3d 195 timespec_load(&st.st_mtim),
c2ce6a3d
LP
196 ret);
197 if (r < 0)
198 return r;
cd61c3bf 199
c2ce6a3d
LP
200 return 1;
201 }
cd61c3bf 202
c2ce6a3d
LP
203 return 0;
204}
cd61c3bf 205
c2ce6a3d
LP
206int image_find(const char *name, Image **ret) {
207 const char *path;
208 int r;
cd61c3bf 209
c2ce6a3d 210 assert(name);
cd61c3bf 211
c2ce6a3d
LP
212 /* There are no images with invalid names */
213 if (!image_name_is_valid(name))
214 return 0;
cd61c3bf 215
c2ce6a3d
LP
216 NULSTR_FOREACH(path, image_search_path) {
217 _cleanup_closedir_ DIR *d = NULL;
cd61c3bf 218
c2ce6a3d
LP
219 d = opendir(path);
220 if (!d) {
221 if (errno == ENOENT)
222 continue;
cd61c3bf 223
c2ce6a3d
LP
224 return -errno;
225 }
cd61c3bf 226
5fc7f358
LP
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 }
c2ce6a3d
LP
239 if (r < 0)
240 return r;
cd61c3bf 241
c2ce6a3d
LP
242 return 1;
243 }
244
5fc7f358
LP
245 if (streq(name, ".host"))
246 return image_make(NULL, AT_FDCWD, NULL, "/", ret);
247
c2ce6a3d
LP
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)
a67a4c8c 264 continue;
c2ce6a3d
LP
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
5fc7f358 278 r = image_make(NULL, dirfd(d), path, de->d_name, &image);
c2ce6a3d
LP
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;
cd61c3bf
LP
289 }
290 }
291
5fc7f358
LP
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
cd61c3bf
LP
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
cd61c3bf
LP
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);