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