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