]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/machine/image.c
machined: fix image search path iteration
[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
LP
47 ImageType t,
48 const char *name,
49 const char *path,
50 bool read_only,
10f9c755 51 usec_t crtime,
cd61c3bf 52 usec_t mtime,
c2ce6a3d 53 Image **ret) {
cd61c3bf
LP
54
55 _cleanup_(image_unrefp) Image *i = NULL;
cd61c3bf 56
cd61c3bf
LP
57 assert(t >= 0);
58 assert(t < _IMAGE_TYPE_MAX);
59 assert(name);
c2ce6a3d 60 assert(ret);
cd61c3bf 61
c2ce6a3d 62 i = new0(Image, 1);
cd61c3bf
LP
63 if (!i)
64 return -ENOMEM;
65
66 i->type = t;
67 i->read_only = read_only;
10f9c755 68 i->crtime = crtime;
cd61c3bf 69 i->mtime = mtime;
cd61c3bf
LP
70
71 i->name = strdup(name);
72 if (!i->name)
73 return -ENOMEM;
74
75 if (path) {
ebeccf9e 76 i->path = strjoin(path, "/", name, NULL);
cd61c3bf
LP
77 if (!i->path)
78 return -ENOMEM;
ebeccf9e
LP
79
80 path_kill_slashes(i->path);
cd61c3bf
LP
81 }
82
c2ce6a3d 83 *ret = i;
cd61c3bf 84 i = NULL;
c2ce6a3d 85
cd61c3bf
LP
86 return 0;
87}
88
c2ce6a3d
LP
89static int image_make(int dfd, const char *name, const char *path, Image **ret) {
90 struct stat st;
86e339c8 91 bool writable;
cd61c3bf
LP
92 int r;
93
c2ce6a3d
LP
94 assert(dfd >= 0);
95 assert(name);
cd61c3bf 96
c2ce6a3d
LP
97 /* We explicitly *do* follow symlinks here, since we want to
98 * allow symlinking trees into /var/lib/container/, and treat
99 * them normally. */
cd61c3bf 100
c2ce6a3d
LP
101 if (fstatat(dfd, name, &st, 0) < 0)
102 return -errno;
cd61c3bf 103
86e339c8
LP
104 writable = faccessat(dfd, name, W_OK, AT_EACCESS) >= 0;
105
c2ce6a3d 106 if (S_ISDIR(st.st_mode)) {
cd61c3bf 107
c2ce6a3d
LP
108 if (!ret)
109 return 1;
cd61c3bf 110
c2ce6a3d
LP
111 /* btrfs subvolumes have inode 256 */
112 if (st.st_ino == 256) {
113 _cleanup_close_ int fd = -1;
114 struct statfs sfs;
cd61c3bf 115
c2ce6a3d
LP
116 fd = openat(dfd, name, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
117 if (fd < 0)
118 return -errno;
cd61c3bf 119
c2ce6a3d
LP
120 if (fstatfs(fd, &sfs) < 0)
121 return -errno;
cd61c3bf 122
c2ce6a3d 123 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
10f9c755 124 BtrfsSubvolInfo info;
cd61c3bf 125
c2ce6a3d 126 /* It's a btrfs subvolume */
cd61c3bf 127
10f9c755
LP
128 r = btrfs_subvol_get_info_fd(fd, &info);
129 if (r < 0)
130 return r;
c2ce6a3d
LP
131
132 r = image_new(IMAGE_SUBVOLUME,
133 name,
134 path,
86e339c8 135 info.read_only || !writable,
10f9c755 136 info.otime,
c2ce6a3d 137 0,
c2ce6a3d
LP
138 ret);
139 if (r < 0)
140 return r;
141
142 return 1;
cd61c3bf 143 }
c2ce6a3d 144 }
cd61c3bf 145
c2ce6a3d 146 /* It's just a normal directory. */
cd61c3bf 147
c2ce6a3d
LP
148 r = image_new(IMAGE_DIRECTORY,
149 name,
150 path,
86e339c8 151 !writable,
c2ce6a3d
LP
152 0,
153 0,
154 ret);
155 if (r < 0)
156 return r;
cd61c3bf 157
c2ce6a3d 158 return 1;
cd61c3bf 159
c2ce6a3d 160 } else if (S_ISREG(st.st_mode) && endswith(name, ".gpt")) {
10f9c755
LP
161 const char *truncated;
162 usec_t crtime = 0;
cd61c3bf 163
c2ce6a3d 164 /* It's a GPT block device */
cd61c3bf 165
c2ce6a3d
LP
166 if (!ret)
167 return 1;
cd61c3bf 168
10f9c755
LP
169 fd_getcrtime_at(dfd, name, &crtime, 0);
170
171 truncated = strndupa(name, strlen(name) - 4);
172
c2ce6a3d 173 r = image_new(IMAGE_GPT,
10f9c755 174 truncated,
c2ce6a3d 175 path,
86e339c8 176 !(st.st_mode & 0222) || !writable,
10f9c755 177 crtime,
c2ce6a3d 178 timespec_load(&st.st_mtim),
c2ce6a3d
LP
179 ret);
180 if (r < 0)
181 return r;
cd61c3bf 182
c2ce6a3d
LP
183 return 1;
184 }
cd61c3bf 185
c2ce6a3d
LP
186 return 0;
187}
cd61c3bf 188
c2ce6a3d
LP
189int image_find(const char *name, Image **ret) {
190 const char *path;
191 int r;
cd61c3bf 192
c2ce6a3d 193 assert(name);
cd61c3bf 194
c2ce6a3d
LP
195 /* There are no images with invalid names */
196 if (!image_name_is_valid(name))
197 return 0;
cd61c3bf 198
c2ce6a3d
LP
199 NULSTR_FOREACH(path, image_search_path) {
200 _cleanup_closedir_ DIR *d = NULL;
cd61c3bf 201
c2ce6a3d
LP
202 d = opendir(path);
203 if (!d) {
204 if (errno == ENOENT)
205 continue;
cd61c3bf 206
c2ce6a3d
LP
207 return -errno;
208 }
cd61c3bf 209
c2ce6a3d
LP
210 r = image_make(dirfd(d), name, path, ret);
211 if (r == 0 || r == -ENOENT)
212 continue;
213 if (r < 0)
214 return r;
cd61c3bf 215
c2ce6a3d
LP
216 return 1;
217 }
218
219 return 0;
220};
221
222int image_discover(Hashmap *h) {
223 const char *path;
224 int r;
225
226 assert(h);
227
228 NULSTR_FOREACH(path, image_search_path) {
229 _cleanup_closedir_ DIR *d = NULL;
230 struct dirent *de;
231
232 d = opendir(path);
233 if (!d) {
234 if (errno == ENOENT)
a67a4c8c 235 continue;
c2ce6a3d
LP
236
237 return -errno;
238 }
239
240 FOREACH_DIRENT_ALL(de, d, return -errno) {
241 _cleanup_(image_unrefp) Image *image = NULL;
242
243 if (!image_name_is_valid(de->d_name))
244 continue;
245
246 if (hashmap_contains(h, de->d_name))
247 continue;
248
249 r = image_make(dirfd(d), de->d_name, path, &image);
250 if (r == 0 || r == -ENOENT)
251 continue;
252 if (r < 0)
253 return r;
254
255 r = hashmap_put(h, image->name, image);
256 if (r < 0)
257 return r;
258
259 image = NULL;
cd61c3bf
LP
260 }
261 }
262
263 return 0;
264}
265
266void image_hashmap_free(Hashmap *map) {
267 Image *i;
268
269 while ((i = hashmap_steal_first(map)))
270 image_unref(i);
271
272 hashmap_free(map);
273}
274
cd61c3bf
LP
275static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
276 [IMAGE_DIRECTORY] = "directory",
277 [IMAGE_SUBVOLUME] = "subvolume",
278 [IMAGE_GPT] = "gpt",
279};
280
281DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);