File: | build-scan/../src/shared/machine-image.c |
Warning: | line 791, column 24 Potential leak of memory pointed to by 'nn' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
2 | ||||
3 | #include <dirent.h> | |||
4 | #include <errno(*__errno_location ()).h> | |||
5 | #include <fcntl.h> | |||
6 | #include <stdio.h> | |||
7 | #include <stdlib.h> | |||
8 | #include <string.h> | |||
9 | #include <sys/file.h> | |||
10 | #include <sys/stat.h> | |||
11 | #include <unistd.h> | |||
12 | #include <linux1/fs.h> | |||
13 | ||||
14 | #include "alloc-util.h" | |||
15 | #include "btrfs-util.h" | |||
16 | #include "chattr-util.h" | |||
17 | #include "copy.h" | |||
18 | #include "dirent-util.h" | |||
19 | #include "dissect-image.h" | |||
20 | #include "env-util.h" | |||
21 | #include "fd-util.h" | |||
22 | #include "fileio.h" | |||
23 | #include "fs-util.h" | |||
24 | #include "hashmap.h" | |||
25 | #include "hostname-util.h" | |||
26 | #include "id128-util.h" | |||
27 | #include "lockfile-util.h" | |||
28 | #include "log.h" | |||
29 | #include "loop-util.h" | |||
30 | #include "machine-image.h" | |||
31 | #include "macro.h" | |||
32 | #include "mkdir.h" | |||
33 | #include "os-util.h" | |||
34 | #include "path-util.h" | |||
35 | #include "rm-rf.h" | |||
36 | #include "string-table.h" | |||
37 | #include "string-util.h" | |||
38 | #include "strv.h" | |||
39 | #include "time-util.h" | |||
40 | #include "utf8.h" | |||
41 | #include "util.h" | |||
42 | #include "xattr-util.h" | |||
43 | ||||
44 | static const char* const image_search_path[_IMAGE_CLASS_MAX] = { | |||
45 | [IMAGE_MACHINE] = "/etc/machines\0" /* only place symlinks here */ | |||
46 | "/run/machines\0" /* and here too */ | |||
47 | "/var/lib/machines\0" /* the main place for images */ | |||
48 | "/var/lib/container\0" /* legacy */ | |||
49 | "/usr/local/lib/machines\0" | |||
50 | "/usr/lib/machines\0", | |||
51 | ||||
52 | [IMAGE_PORTABLE] = "/etc/portables\0" /* only place symlinks here */ | |||
53 | "/run/portables\0" /* and here too */ | |||
54 | "/var/lib/portables\0" /* the main place for images */ | |||
55 | "/usr/local/lib/portables\0" | |||
56 | "/usr/lib/portables\0", | |||
57 | }; | |||
58 | ||||
59 | Image *image_unref(Image *i) { | |||
60 | if (!i) | |||
61 | return NULL((void*)0); | |||
62 | ||||
63 | assert(i->n_ref > 0)do { if ((__builtin_expect(!!(!(i->n_ref > 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i->n_ref > 0"), "../src/shared/machine-image.c" , 63, __PRETTY_FUNCTION__); } while (0); | |||
64 | i->n_ref--; | |||
65 | ||||
66 | if (i->n_ref > 0) | |||
67 | return NULL((void*)0); | |||
68 | ||||
69 | free(i->name); | |||
70 | free(i->path); | |||
71 | ||||
72 | free(i->hostname); | |||
73 | strv_free(i->machine_info); | |||
74 | strv_free(i->os_release); | |||
75 | ||||
76 | return mfree(i); | |||
77 | } | |||
78 | ||||
79 | Image *image_ref(Image *i) { | |||
80 | if (!i) | |||
81 | return NULL((void*)0); | |||
82 | ||||
83 | assert(i->n_ref > 0)do { if ((__builtin_expect(!!(!(i->n_ref > 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i->n_ref > 0"), "../src/shared/machine-image.c" , 83, __PRETTY_FUNCTION__); } while (0); | |||
84 | i->n_ref++; | |||
85 | ||||
86 | return i; | |||
87 | } | |||
88 | ||||
89 | static char **image_settings_path(Image *image) { | |||
90 | _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **l = NULL((void*)0); | |||
91 | const char *fn, *s; | |||
92 | unsigned i = 0; | |||
93 | ||||
94 | assert(image)do { if ((__builtin_expect(!!(!(image)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("image"), "../src/shared/machine-image.c" , 94, __PRETTY_FUNCTION__); } while (0); | |||
95 | ||||
96 | l = new0(char*, 4)((char**) calloc((4), sizeof(char*))); | |||
97 | if (!l) | |||
98 | return NULL((void*)0); | |||
99 | ||||
100 | fn = strjoina(image->name, ".nspawn")({ const char *_appendees_[] = { image->name, ".nspawn" }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
101 | ||||
102 | FOREACH_STRING(s, "/etc/systemd/nspawn/", "/run/systemd/nspawn/")for (char **_l = ({ char **_ll = ((char**) ((const char*[]) { "/etc/systemd/nspawn/", "/run/systemd/nspawn/", ((void*)0) } )); s = _ll ? _ll[0] : ((void*)0); _ll; }); _l && *_l ; s = ({ _l ++; _l[0]; })) { | |||
103 | l[i] = strappend(s, fn); | |||
104 | if (!l[i]) | |||
105 | return NULL((void*)0); | |||
106 | ||||
107 | i++; | |||
108 | } | |||
109 | ||||
110 | l[i] = file_in_same_dir(image->path, fn); | |||
111 | if (!l[i]) | |||
112 | return NULL((void*)0); | |||
113 | ||||
114 | return TAKE_PTR(l)({ typeof(l) _ptr_ = (l); (l) = ((void*)0); _ptr_; }); | |||
115 | } | |||
116 | ||||
117 | static char *image_roothash_path(Image *image) { | |||
118 | const char *fn; | |||
119 | ||||
120 | assert(image)do { if ((__builtin_expect(!!(!(image)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("image"), "../src/shared/machine-image.c" , 120, __PRETTY_FUNCTION__); } while (0); | |||
121 | ||||
122 | fn = strjoina(image->name, ".roothash")({ const char *_appendees_[] = { image->name, ".roothash" } ; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0 ; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
123 | ||||
124 | return file_in_same_dir(image->path, fn); | |||
125 | } | |||
126 | ||||
127 | static int image_new( | |||
128 | ImageType t, | |||
129 | const char *pretty, | |||
130 | const char *path, | |||
131 | const char *filename, | |||
132 | bool_Bool read_only, | |||
133 | usec_t crtime, | |||
134 | usec_t mtime, | |||
135 | Image **ret) { | |||
136 | ||||
137 | _cleanup_(image_unrefp)__attribute__((cleanup(image_unrefp))) Image *i = NULL((void*)0); | |||
138 | ||||
139 | assert(t >= 0)do { if ((__builtin_expect(!!(!(t >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t >= 0"), "../src/shared/machine-image.c" , 139, __PRETTY_FUNCTION__); } while (0); | |||
140 | assert(t < _IMAGE_TYPE_MAX)do { if ((__builtin_expect(!!(!(t < _IMAGE_TYPE_MAX)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("t < _IMAGE_TYPE_MAX" ), "../src/shared/machine-image.c", 140, __PRETTY_FUNCTION__) ; } while (0); | |||
141 | assert(pretty)do { if ((__builtin_expect(!!(!(pretty)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("pretty"), "../src/shared/machine-image.c" , 141, __PRETTY_FUNCTION__); } while (0); | |||
142 | assert(filename)do { if ((__builtin_expect(!!(!(filename)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("filename"), "../src/shared/machine-image.c" , 142, __PRETTY_FUNCTION__); } while (0); | |||
143 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/shared/machine-image.c", 143, __PRETTY_FUNCTION__); } while (0); | |||
144 | ||||
145 | i = new0(Image, 1)((Image*) calloc((1), sizeof(Image))); | |||
146 | if (!i) | |||
147 | return -ENOMEM12; | |||
148 | ||||
149 | i->n_ref = 1; | |||
150 | i->type = t; | |||
151 | i->read_only = read_only; | |||
152 | i->crtime = crtime; | |||
153 | i->mtime = mtime; | |||
154 | i->usage = i->usage_exclusive = (uint64_t) -1; | |||
155 | i->limit = i->limit_exclusive = (uint64_t) -1; | |||
156 | ||||
157 | i->name = strdup(pretty); | |||
158 | if (!i->name) | |||
159 | return -ENOMEM12; | |||
160 | ||||
161 | if (path) | |||
162 | i->path = strjoin(path, "/", filename)strjoin_real((path), "/", filename, ((void*)0)); | |||
163 | else | |||
164 | i->path = strdup(filename); | |||
165 | if (!i->path) | |||
166 | return -ENOMEM12; | |||
167 | ||||
168 | path_simplify(i->path, false0); | |||
169 | ||||
170 | *ret = TAKE_PTR(i)({ typeof(i) _ptr_ = (i); (i) = ((void*)0); _ptr_; }); | |||
171 | ||||
172 | return 0; | |||
173 | } | |||
174 | ||||
175 | static int extract_pretty(const char *path, const char *suffix, char **ret) { | |||
176 | _cleanup_free___attribute__((cleanup(freep))) char *name = NULL((void*)0); | |||
177 | const char *p; | |||
178 | size_t n; | |||
179 | ||||
180 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/shared/machine-image.c" , 180, __PRETTY_FUNCTION__); } while (0); | |||
181 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/shared/machine-image.c", 181, __PRETTY_FUNCTION__); } while (0); | |||
182 | ||||
183 | p = last_path_component(path); | |||
184 | n = strcspn(p, "/"); | |||
185 | ||||
186 | name = strndup(p, n); | |||
187 | if (!name) | |||
188 | return -ENOMEM12; | |||
189 | ||||
190 | if (suffix) { | |||
191 | char *e; | |||
192 | ||||
193 | e = endswith(name, suffix); | |||
194 | if (!e) | |||
195 | return -EINVAL22; | |||
196 | ||||
197 | *e = 0; | |||
198 | } | |||
199 | ||||
200 | if (!image_name_is_valid(name)) | |||
201 | return -EINVAL22; | |||
202 | ||||
203 | *ret = TAKE_PTR(name)({ typeof(name) _ptr_ = (name); (name) = ((void*)0); _ptr_; } ); | |||
204 | return 0; | |||
205 | } | |||
206 | ||||
207 | static int image_make( | |||
208 | const char *pretty, | |||
209 | int dfd, | |||
210 | const char *path, | |||
211 | const char *filename, | |||
212 | const struct stat *st, | |||
213 | Image **ret) { | |||
214 | ||||
215 | _cleanup_free___attribute__((cleanup(freep))) char *pretty_buffer = NULL((void*)0); | |||
216 | struct stat stbuf; | |||
217 | bool_Bool read_only; | |||
218 | int r; | |||
219 | ||||
220 | assert(dfd >= 0 || dfd == AT_FDCWD)do { if ((__builtin_expect(!!(!(dfd >= 0 || dfd == -100)), 0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("dfd >= 0 || dfd == AT_FDCWD" ), "../src/shared/machine-image.c", 220, __PRETTY_FUNCTION__) ; } while (0); | |||
221 | assert(filename)do { if ((__builtin_expect(!!(!(filename)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("filename"), "../src/shared/machine-image.c" , 221, __PRETTY_FUNCTION__); } while (0); | |||
222 | ||||
223 | /* We explicitly *do* follow symlinks here, since we want to allow symlinking trees, raw files and block | |||
224 | * devices into /var/lib/machines/, and treat them normally. | |||
225 | * | |||
226 | * This function returns -ENOENT if we can't find the image after all, and -EMEDIUMTYPE if it's not a file we | |||
227 | * recognize. */ | |||
228 | ||||
229 | if (!st) { | |||
230 | if (fstatat(dfd, filename, &stbuf, 0) < 0) | |||
231 | return -errno(*__errno_location ()); | |||
232 | ||||
233 | st = &stbuf; | |||
234 | } | |||
235 | ||||
236 | read_only = | |||
237 | (path && path_startswith(path, "/usr")) || | |||
238 | (faccessat(dfd, filename, W_OK2, AT_EACCESS0x200) < 0 && errno(*__errno_location ()) == EROFS30); | |||
239 | ||||
240 | if (S_ISDIR(st->st_mode)((((st->st_mode)) & 0170000) == (0040000))) { | |||
241 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | |||
242 | unsigned file_attr = 0; | |||
243 | ||||
244 | if (!ret) | |||
245 | return 0; | |||
246 | ||||
247 | if (!pretty) { | |||
248 | r = extract_pretty(filename, NULL((void*)0), &pretty_buffer); | |||
249 | if (r < 0) | |||
250 | return r; | |||
251 | ||||
252 | pretty = pretty_buffer; | |||
253 | } | |||
254 | ||||
255 | fd = openat(dfd, filename, O_CLOEXEC02000000|O_NOCTTY0400|O_DIRECTORY0200000); | |||
256 | if (fd < 0) | |||
257 | return -errno(*__errno_location ()); | |||
258 | ||||
259 | /* btrfs subvolumes have inode 256 */ | |||
260 | if (st->st_ino == 256) { | |||
261 | ||||
262 | r = btrfs_is_filesystem(fd); | |||
263 | if (r < 0) | |||
264 | return r; | |||
265 | if (r) { | |||
266 | BtrfsSubvolInfo info; | |||
267 | ||||
268 | /* It's a btrfs subvolume */ | |||
269 | ||||
270 | r = btrfs_subvol_get_info_fd(fd, 0, &info); | |||
271 | if (r < 0) | |||
272 | return r; | |||
273 | ||||
274 | r = image_new(IMAGE_SUBVOLUME, | |||
275 | pretty, | |||
276 | path, | |||
277 | filename, | |||
278 | info.read_only || read_only, | |||
279 | info.otime, | |||
280 | 0, | |||
281 | ret); | |||
282 | if (r < 0) | |||
283 | return r; | |||
284 | ||||
285 | if (btrfs_quota_scan_ongoing(fd) == 0) { | |||
286 | BtrfsQuotaInfo quota; | |||
287 | ||||
288 | r = btrfs_subvol_get_subtree_quota_fd(fd, 0, "a); | |||
289 | if (r >= 0) { | |||
290 | (*ret)->usage = quota.referenced; | |||
291 | (*ret)->usage_exclusive = quota.exclusive; | |||
292 | ||||
293 | (*ret)->limit = quota.referenced_max; | |||
294 | (*ret)->limit_exclusive = quota.exclusive_max; | |||
295 | } | |||
296 | } | |||
297 | ||||
298 | return 0; | |||
299 | } | |||
300 | } | |||
301 | ||||
302 | /* If the IMMUTABLE bit is set, we consider the | |||
303 | * directory read-only. Since the ioctl is not | |||
304 | * supported everywhere we ignore failures. */ | |||
305 | (void) read_attr_fd(fd, &file_attr); | |||
306 | ||||
307 | /* It's just a normal directory. */ | |||
308 | r = image_new(IMAGE_DIRECTORY, | |||
309 | pretty, | |||
310 | path, | |||
311 | filename, | |||
312 | read_only || (file_attr & FS_IMMUTABLE_FL0x00000010), | |||
313 | 0, | |||
314 | 0, | |||
315 | ret); | |||
316 | if (r < 0) | |||
317 | return r; | |||
318 | ||||
319 | return 0; | |||
320 | ||||
321 | } else if (S_ISREG(st->st_mode)((((st->st_mode)) & 0170000) == (0100000)) && endswith(filename, ".raw")) { | |||
322 | usec_t crtime = 0; | |||
323 | ||||
324 | /* It's a RAW disk image */ | |||
325 | ||||
326 | if (!ret) | |||
327 | return 0; | |||
328 | ||||
329 | (void) fd_getcrtime_at(dfd, filename, &crtime, 0); | |||
330 | ||||
331 | if (!pretty) { | |||
332 | r = extract_pretty(filename, ".raw", &pretty_buffer); | |||
333 | if (r < 0) | |||
334 | return r; | |||
335 | ||||
336 | pretty = pretty_buffer; | |||
337 | } | |||
338 | ||||
339 | r = image_new(IMAGE_RAW, | |||
340 | pretty, | |||
341 | path, | |||
342 | filename, | |||
343 | !(st->st_mode & 0222) || read_only, | |||
344 | crtime, | |||
345 | timespec_load(&st->st_mtim), | |||
346 | ret); | |||
347 | if (r < 0) | |||
348 | return r; | |||
349 | ||||
350 | (*ret)->usage = (*ret)->usage_exclusive = st->st_blocks * 512; | |||
351 | (*ret)->limit = (*ret)->limit_exclusive = st->st_size; | |||
352 | ||||
353 | return 0; | |||
354 | ||||
355 | } else if (S_ISBLK(st->st_mode)((((st->st_mode)) & 0170000) == (0060000))) { | |||
356 | _cleanup_close___attribute__((cleanup(closep))) int block_fd = -1; | |||
357 | uint64_t size = UINT64_MAX(18446744073709551615UL); | |||
358 | ||||
359 | /* A block device */ | |||
360 | ||||
361 | if (!ret) | |||
362 | return 0; | |||
363 | ||||
364 | if (!pretty) { | |||
365 | r = extract_pretty(filename, NULL((void*)0), &pretty_buffer); | |||
366 | if (r < 0) | |||
367 | return r; | |||
368 | ||||
369 | pretty = pretty_buffer; | |||
370 | } | |||
371 | ||||
372 | block_fd = openat(dfd, filename, O_RDONLY00|O_NONBLOCK04000|O_CLOEXEC02000000|O_NOCTTY0400); | |||
373 | if (block_fd < 0) | |||
374 | log_debug_errno(errno, "Failed to open block device %s/%s, ignoring: %m", path, filename)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/machine-image.c", 374, __func__ , "Failed to open block device %s/%s, ignoring: %m", path, filename ) : -abs(_e); }); | |||
375 | else { | |||
376 | /* Refresh stat data after opening the node */ | |||
377 | if (fstat(block_fd, &stbuf) < 0) | |||
378 | return -errno(*__errno_location ()); | |||
379 | st = &stbuf; | |||
380 | ||||
381 | if (!S_ISBLK(st->st_mode)((((st->st_mode)) & 0170000) == (0060000))) /* Verify that what we opened is actually what we think it is */ | |||
382 | return -ENOTTY25; | |||
383 | ||||
384 | if (!read_only) { | |||
385 | int state = 0; | |||
386 | ||||
387 | if (ioctl(block_fd, BLKROGET(((0U) << (((0 +8)+8)+14)) | (((0x12)) << (0 +8)) | (((94)) << 0) | ((0) << ((0 +8)+8))), &state) < 0) | |||
388 | log_debug_errno(errno, "Failed to issue BLKROGET on device %s/%s, ignoring: %m", path, filename)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/machine-image.c", 388, __func__ , "Failed to issue BLKROGET on device %s/%s, ignoring: %m", path , filename) : -abs(_e); }); | |||
389 | else if (state) | |||
390 | read_only = true1; | |||
391 | } | |||
392 | ||||
393 | if (ioctl(block_fd, BLKGETSIZE64(((2U) << (((0 +8)+8)+14)) | (((0x12)) << (0 +8)) | (((114)) << 0) | ((((sizeof(size_t)))) << ((0 + 8)+8))), &size) < 0) | |||
394 | log_debug_errno(errno, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path, filename)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/machine-image.c", 394, __func__ , "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m" , path, filename) : -abs(_e); }); | |||
395 | ||||
396 | block_fd = safe_close(block_fd); | |||
397 | } | |||
398 | ||||
399 | r = image_new(IMAGE_BLOCK, | |||
400 | pretty, | |||
401 | path, | |||
402 | filename, | |||
403 | !(st->st_mode & 0222) || read_only, | |||
404 | 0, | |||
405 | 0, | |||
406 | ret); | |||
407 | if (r < 0) | |||
408 | return r; | |||
409 | ||||
410 | if (size != 0 && size != UINT64_MAX(18446744073709551615UL)) | |||
411 | (*ret)->usage = (*ret)->usage_exclusive = (*ret)->limit = (*ret)->limit_exclusive = size; | |||
412 | ||||
413 | return 0; | |||
414 | } | |||
415 | ||||
416 | return -EMEDIUMTYPE124; | |||
417 | } | |||
418 | ||||
419 | int image_find(ImageClass class, const char *name, Image **ret) { | |||
420 | const char *path; | |||
421 | int r; | |||
422 | ||||
423 | assert(class >= 0)do { if ((__builtin_expect(!!(!(class >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("class >= 0"), "../src/shared/machine-image.c" , 423, __PRETTY_FUNCTION__); } while (0); | |||
424 | assert(class < _IMAGE_CLASS_MAX)do { if ((__builtin_expect(!!(!(class < _IMAGE_CLASS_MAX)) ,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("class < _IMAGE_CLASS_MAX" ), "../src/shared/machine-image.c", 424, __PRETTY_FUNCTION__) ; } while (0); | |||
425 | assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name"), "../src/shared/machine-image.c" , 425, __PRETTY_FUNCTION__); } while (0); | |||
426 | ||||
427 | /* There are no images with invalid names */ | |||
428 | if (!image_name_is_valid(name)) | |||
429 | return -ENOENT2; | |||
430 | ||||
431 | NULSTR_FOREACH(path, image_search_path[class])for ((path) = (image_search_path[class]); (path) && * (path); (path) = strchr((path), 0)+1) { | |||
432 | _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0); | |||
433 | struct stat st; | |||
434 | ||||
435 | d = opendir(path); | |||
436 | if (!d) { | |||
437 | if (errno(*__errno_location ()) == ENOENT2) | |||
438 | continue; | |||
439 | ||||
440 | return -errno(*__errno_location ()); | |||
441 | } | |||
442 | ||||
443 | /* As mentioned above, we follow symlinks on this fstatat(), because we want to permit people to | |||
444 | * symlink block devices into the search path */ | |||
445 | if (fstatat(dirfd(d), name, &st, 0) < 0) { | |||
446 | _cleanup_free___attribute__((cleanup(freep))) char *raw = NULL((void*)0); | |||
447 | ||||
448 | if (errno(*__errno_location ()) != ENOENT2) | |||
449 | return -errno(*__errno_location ()); | |||
450 | ||||
451 | raw = strappend(name, ".raw"); | |||
452 | if (!raw) | |||
453 | return -ENOMEM12; | |||
454 | ||||
455 | if (fstatat(dirfd(d), raw, &st, 0) < 0) { | |||
456 | ||||
457 | if (errno(*__errno_location ()) == ENOENT2) | |||
458 | continue; | |||
459 | ||||
460 | return -errno(*__errno_location ()); | |||
461 | } | |||
462 | ||||
463 | if (!S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000))) | |||
464 | continue; | |||
465 | ||||
466 | r = image_make(name, dirfd(d), path, raw, &st, ret); | |||
467 | ||||
468 | } else { | |||
469 | if (!S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000)) && !S_ISBLK(st.st_mode)((((st.st_mode)) & 0170000) == (0060000))) | |||
470 | continue; | |||
471 | ||||
472 | r = image_make(name, dirfd(d), path, name, &st, ret); | |||
473 | } | |||
474 | if (IN_SET(r, -ENOENT, -EMEDIUMTYPE)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){-2, -124})/sizeof(int)]; switch(r) { case -2: case -124: _found = 1; break; default: break; } _found; } )) | |||
475 | continue; | |||
476 | if (r < 0) | |||
477 | return r; | |||
478 | ||||
479 | if (ret) | |||
480 | (*ret)->discoverable = true1; | |||
481 | ||||
482 | return 1; | |||
483 | } | |||
484 | ||||
485 | if (class == IMAGE_MACHINE && streq(name, ".host")(strcmp((name),(".host")) == 0)) { | |||
486 | r = image_make(".host", AT_FDCWD-100, NULL((void*)0), "/", NULL((void*)0), ret); | |||
487 | if (r < 0) | |||
488 | return r; | |||
489 | ||||
490 | if (ret) | |||
491 | (*ret)->discoverable = true1; | |||
492 | ||||
493 | return r; | |||
494 | } | |||
495 | ||||
496 | return -ENOENT2; | |||
497 | }; | |||
498 | ||||
499 | int image_from_path(const char *path, Image **ret) { | |||
500 | ||||
501 | /* Note that we don't set the 'discoverable' field of the returned object, because we don't check here whether | |||
502 | * the image is in the image search path. And if it is we don't know if the path we used is actually not | |||
503 | * overridden by another, different image earlier in the search path */ | |||
504 | ||||
505 | if (path_equal(path, "/")) | |||
506 | return image_make(".host", AT_FDCWD-100, NULL((void*)0), "/", NULL((void*)0), ret); | |||
507 | ||||
508 | return image_make(NULL((void*)0), AT_FDCWD-100, NULL((void*)0), path, NULL((void*)0), ret); | |||
509 | } | |||
510 | ||||
511 | int image_find_harder(ImageClass class, const char *name_or_path, Image **ret) { | |||
512 | if (image_name_is_valid(name_or_path)) | |||
513 | return image_find(class, name_or_path, ret); | |||
514 | ||||
515 | return image_from_path(name_or_path, ret); | |||
516 | } | |||
517 | ||||
518 | int image_discover(ImageClass class, Hashmap *h) { | |||
519 | const char *path; | |||
520 | int r; | |||
521 | ||||
522 | assert(class >= 0)do { if ((__builtin_expect(!!(!(class >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("class >= 0"), "../src/shared/machine-image.c" , 522, __PRETTY_FUNCTION__); } while (0); | |||
523 | assert(class < _IMAGE_CLASS_MAX)do { if ((__builtin_expect(!!(!(class < _IMAGE_CLASS_MAX)) ,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("class < _IMAGE_CLASS_MAX" ), "../src/shared/machine-image.c", 523, __PRETTY_FUNCTION__) ; } while (0); | |||
524 | assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("h"), "../src/shared/machine-image.c", 524 , __PRETTY_FUNCTION__); } while (0); | |||
525 | ||||
526 | NULSTR_FOREACH(path, image_search_path[class])for ((path) = (image_search_path[class]); (path) && * (path); (path) = strchr((path), 0)+1) { | |||
527 | _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0); | |||
528 | struct dirent *de; | |||
529 | ||||
530 | d = opendir(path); | |||
531 | if (!d) { | |||
532 | if (errno(*__errno_location ()) == ENOENT2) | |||
533 | continue; | |||
534 | ||||
535 | return -errno(*__errno_location ()); | |||
536 | } | |||
537 | ||||
538 | FOREACH_DIRENT_ALL(de, d, return -errno)for ((*__errno_location ()) = 0, de = readdir(d);; (*__errno_location ()) = 0, de = readdir(d)) if (!de) { if ((*__errno_location ( )) > 0) { return -(*__errno_location ()); } break; } else { | |||
539 | _cleanup_(image_unrefp)__attribute__((cleanup(image_unrefp))) Image *image = NULL((void*)0); | |||
540 | _cleanup_free___attribute__((cleanup(freep))) char *truncated = NULL((void*)0); | |||
541 | const char *pretty; | |||
542 | struct stat st; | |||
543 | ||||
544 | if (dot_or_dot_dot(de->d_name)) | |||
545 | continue; | |||
546 | ||||
547 | /* As mentioned above, we follow symlinks on this fstatat(), because we want to permit people | |||
548 | * to symlink block devices into the search path */ | |||
549 | if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) { | |||
550 | if (errno(*__errno_location ()) == ENOENT2) | |||
551 | continue; | |||
552 | ||||
553 | return -errno(*__errno_location ()); | |||
554 | } | |||
555 | ||||
556 | if (S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000))) { | |||
557 | const char *e; | |||
558 | ||||
559 | e = endswith(de->d_name, ".raw"); | |||
560 | if (!e) | |||
561 | continue; | |||
562 | ||||
563 | truncated = strndup(de->d_name, e - de->d_name); | |||
564 | if (!truncated) | |||
565 | return -ENOMEM12; | |||
566 | ||||
567 | pretty = truncated; | |||
568 | } else if (S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000)) || S_ISBLK(st.st_mode)((((st.st_mode)) & 0170000) == (0060000))) | |||
569 | pretty = de->d_name; | |||
570 | else | |||
571 | continue; | |||
572 | ||||
573 | if (!image_name_is_valid(pretty)) | |||
574 | continue; | |||
575 | ||||
576 | if (hashmap_contains(h, pretty)) | |||
577 | continue; | |||
578 | ||||
579 | r = image_make(pretty, dirfd(d), path, de->d_name, &st, &image); | |||
580 | if (IN_SET(r, -ENOENT, -EMEDIUMTYPE)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){-2, -124})/sizeof(int)]; switch(r) { case -2: case -124: _found = 1; break; default: break; } _found; } )) | |||
581 | continue; | |||
582 | if (r < 0) | |||
583 | return r; | |||
584 | ||||
585 | image->discoverable = true1; | |||
586 | ||||
587 | r = hashmap_put(h, image->name, image); | |||
588 | if (r < 0) | |||
589 | return r; | |||
590 | ||||
591 | image = NULL((void*)0); | |||
592 | } | |||
593 | } | |||
594 | ||||
595 | if (class == IMAGE_MACHINE && !hashmap_contains(h, ".host")) { | |||
596 | _cleanup_(image_unrefp)__attribute__((cleanup(image_unrefp))) Image *image = NULL((void*)0); | |||
597 | ||||
598 | r = image_make(".host", AT_FDCWD-100, NULL((void*)0), "/", NULL((void*)0), &image); | |||
599 | if (r < 0) | |||
600 | return r; | |||
601 | ||||
602 | image->discoverable = true1; | |||
603 | ||||
604 | r = hashmap_put(h, image->name, image); | |||
605 | if (r < 0) | |||
606 | return r; | |||
607 | ||||
608 | image = NULL((void*)0); | |||
609 | } | |||
610 | ||||
611 | return 0; | |||
612 | } | |||
613 | ||||
614 | int image_remove(Image *i) { | |||
615 | _cleanup_(release_lock_file)__attribute__((cleanup(release_lock_file))) LockFile global_lock = LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }, local_lock = LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }; | |||
616 | _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **settings = NULL((void*)0); | |||
617 | _cleanup_free___attribute__((cleanup(freep))) char *roothash = NULL((void*)0); | |||
618 | char **j; | |||
619 | int r; | |||
620 | ||||
621 | assert(i)do { if ((__builtin_expect(!!(!(i)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i"), "../src/shared/machine-image.c", 621 , __PRETTY_FUNCTION__); } while (0); | |||
622 | ||||
623 | if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) | |||
624 | return -EROFS30; | |||
625 | ||||
626 | settings = image_settings_path(i); | |||
627 | if (!settings) | |||
628 | return -ENOMEM12; | |||
629 | ||||
630 | roothash = image_roothash_path(i); | |||
631 | if (!roothash) | |||
632 | return -ENOMEM12; | |||
633 | ||||
634 | /* Make sure we don't interfere with a running nspawn */ | |||
635 | r = image_path_lock(i->path, LOCK_EX2|LOCK_NB4, &global_lock, &local_lock); | |||
636 | if (r < 0) | |||
637 | return r; | |||
638 | ||||
639 | switch (i->type) { | |||
640 | ||||
641 | case IMAGE_SUBVOLUME: | |||
642 | ||||
643 | /* Let's unlink first, maybe it is a symlink? If that works we are happy. Otherwise, let's get out the | |||
644 | * big guns */ | |||
645 | if (unlink(i->path) < 0) { | |||
646 | r = btrfs_subvol_remove(i->path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); | |||
647 | if (r < 0) | |||
648 | return r; | |||
649 | } | |||
650 | ||||
651 | break; | |||
652 | ||||
653 | case IMAGE_DIRECTORY: | |||
654 | /* Allow deletion of read-only directories */ | |||
655 | (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL0x00000010); | |||
656 | r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); | |||
657 | if (r < 0) | |||
658 | return r; | |||
659 | ||||
660 | break; | |||
661 | ||||
662 | case IMAGE_BLOCK: | |||
663 | ||||
664 | /* If this is inside of /dev, then it's a real block device, hence let's not touch the device node | |||
665 | * itself (but let's remove the stuff stored alongside it). If it's anywhere else, let's try to unlink | |||
666 | * the thing (it's most likely a symlink after all). */ | |||
667 | ||||
668 | if (path_startswith(i->path, "/dev")) | |||
669 | break; | |||
670 | ||||
671 | _fallthrough_; | |||
672 | case IMAGE_RAW: | |||
673 | if (unlink(i->path) < 0) | |||
674 | return -errno(*__errno_location ()); | |||
675 | break; | |||
676 | ||||
677 | default: | |||
678 | return -EOPNOTSUPP95; | |||
679 | } | |||
680 | ||||
681 | STRV_FOREACH(j, settings)for ((j) = (settings); (j) && *(j); (j)++) { | |||
682 | if (unlink(*j) < 0 && errno(*__errno_location ()) != ENOENT2) | |||
683 | log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/machine-image.c", 683, __func__ , "Failed to unlink %s, ignoring: %m", *j) : -abs(_e); }); | |||
684 | } | |||
685 | ||||
686 | if (unlink(roothash) < 0 && errno(*__errno_location ()) != ENOENT2) | |||
687 | log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", roothash)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/machine-image.c", 687, __func__ , "Failed to unlink %s, ignoring: %m", roothash) : -abs(_e); } ); | |||
688 | ||||
689 | return 0; | |||
690 | } | |||
691 | ||||
692 | static int rename_auxiliary_file(const char *path, const char *new_name, const char *suffix) { | |||
693 | _cleanup_free___attribute__((cleanup(freep))) char *rs = NULL((void*)0); | |||
694 | const char *fn; | |||
695 | ||||
696 | fn = strjoina(new_name, suffix)({ const char *_appendees_[] = { new_name, suffix }; char *_d_ , *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
697 | ||||
698 | rs = file_in_same_dir(path, fn); | |||
699 | if (!rs) | |||
700 | return -ENOMEM12; | |||
701 | ||||
702 | return rename_noreplace(AT_FDCWD-100, path, AT_FDCWD-100, rs); | |||
703 | } | |||
704 | ||||
705 | int image_rename(Image *i, const char *new_name) { | |||
706 | _cleanup_(release_lock_file)__attribute__((cleanup(release_lock_file))) LockFile global_lock = LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }, local_lock = LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }, name_lock = LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }; | |||
707 | _cleanup_free___attribute__((cleanup(freep))) char *new_path = NULL((void*)0), *nn = NULL((void*)0), *roothash = NULL((void*)0); | |||
708 | _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **settings = NULL((void*)0); | |||
709 | unsigned file_attr = 0; | |||
710 | char **j; | |||
711 | int r; | |||
712 | ||||
713 | assert(i)do { if ((__builtin_expect(!!(!(i)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i"), "../src/shared/machine-image.c", 713 , __PRETTY_FUNCTION__); } while (0); | |||
| ||||
714 | ||||
715 | if (!image_name_is_valid(new_name)) | |||
716 | return -EINVAL22; | |||
717 | ||||
718 | if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) | |||
719 | return -EROFS30; | |||
720 | ||||
721 | settings = image_settings_path(i); | |||
722 | if (!settings
| |||
723 | return -ENOMEM12; | |||
724 | ||||
725 | roothash = image_roothash_path(i); | |||
726 | if (!roothash) | |||
727 | return -ENOMEM12; | |||
728 | ||||
729 | /* Make sure we don't interfere with a running nspawn */ | |||
730 | r = image_path_lock(i->path, LOCK_EX2|LOCK_NB4, &global_lock, &local_lock); | |||
731 | if (r
| |||
732 | return r; | |||
733 | ||||
734 | /* Make sure nobody takes the new name, between the time we | |||
735 | * checked it is currently unused in all search paths, and the | |||
736 | * time we take possession of it */ | |||
737 | r = image_name_lock(new_name, LOCK_EX2|LOCK_NB4, &name_lock); | |||
738 | if (r
| |||
739 | return r; | |||
740 | ||||
741 | r = image_find(IMAGE_MACHINE, new_name, NULL((void*)0)); | |||
742 | if (r >= 0) | |||
743 | return -EEXIST17; | |||
744 | if (r != -ENOENT2) | |||
745 | return r; | |||
746 | ||||
747 | switch (i->type) { | |||
748 | ||||
749 | case IMAGE_DIRECTORY: | |||
750 | /* Turn of the immutable bit while we rename the image, so that we can rename it */ | |||
751 | (void) read_attr_path(i->path, &file_attr); | |||
752 | ||||
753 | if (file_attr & FS_IMMUTABLE_FL0x00000010) | |||
754 | (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL0x00000010); | |||
755 | ||||
756 | _fallthrough_; | |||
757 | case IMAGE_SUBVOLUME: | |||
758 | new_path = file_in_same_dir(i->path, new_name); | |||
759 | break; | |||
760 | ||||
761 | case IMAGE_BLOCK: | |||
762 | ||||
763 | /* Refuse renaming raw block devices in /dev, the names are picked by udev after all. */ | |||
764 | if (path_startswith(i->path, "/dev")) | |||
765 | return -EROFS30; | |||
766 | ||||
767 | new_path = file_in_same_dir(i->path, new_name); | |||
768 | break; | |||
769 | ||||
770 | case IMAGE_RAW: { | |||
771 | const char *fn; | |||
772 | ||||
773 | fn = strjoina(new_name, ".raw")({ const char *_appendees_[] = { new_name, ".raw" }; char *_d_ , *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
774 | new_path = file_in_same_dir(i->path, fn); | |||
775 | break; | |||
776 | } | |||
777 | ||||
778 | default: | |||
779 | return -EOPNOTSUPP95; | |||
780 | } | |||
781 | ||||
782 | if (!new_path) | |||
783 | return -ENOMEM12; | |||
784 | ||||
785 | nn = strdup(new_name); | |||
786 | if (!nn) | |||
787 | return -ENOMEM12; | |||
788 | ||||
789 | r = rename_noreplace(AT_FDCWD-100, i->path, AT_FDCWD-100, new_path); | |||
790 | if (r < 0) | |||
791 | return r; | |||
| ||||
792 | ||||
793 | /* Restore the immutable bit, if it was set before */ | |||
794 | if (file_attr & FS_IMMUTABLE_FL0x00000010) | |||
795 | (void) chattr_path(new_path, FS_IMMUTABLE_FL0x00000010, FS_IMMUTABLE_FL0x00000010); | |||
796 | ||||
797 | free_and_replace(i->path, new_path)({ free(i->path); (i->path) = (new_path); (new_path) = ( (void*)0); 0; }); | |||
798 | free_and_replace(i->name, nn)({ free(i->name); (i->name) = (nn); (nn) = ((void*)0); 0 ; }); | |||
799 | ||||
800 | STRV_FOREACH(j, settings)for ((j) = (settings); (j) && *(j); (j)++) { | |||
801 | r = rename_auxiliary_file(*j, new_name, ".nspawn"); | |||
802 | if (r < 0 && r != -ENOENT2) | |||
803 | log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/shared/machine-image.c", 803, __func__, "Failed to rename settings file %s, ignoring: %m" , *j) : -abs(_e); }); | |||
804 | } | |||
805 | ||||
806 | r = rename_auxiliary_file(roothash, new_name, ".roothash"); | |||
807 | if (r < 0 && r != -ENOENT2) | |||
808 | log_debug_errno(r, "Failed to rename roothash file %s, ignoring: %m", roothash)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/shared/machine-image.c", 808, __func__, "Failed to rename roothash file %s, ignoring: %m" , roothash) : -abs(_e); }); | |||
809 | ||||
810 | return 0; | |||
811 | } | |||
812 | ||||
813 | static int clone_auxiliary_file(const char *path, const char *new_name, const char *suffix) { | |||
814 | _cleanup_free___attribute__((cleanup(freep))) char *rs = NULL((void*)0); | |||
815 | const char *fn; | |||
816 | ||||
817 | fn = strjoina(new_name, suffix)({ const char *_appendees_[] = { new_name, suffix }; char *_d_ , *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
818 | ||||
819 | rs = file_in_same_dir(path, fn); | |||
820 | if (!rs) | |||
821 | return -ENOMEM12; | |||
822 | ||||
823 | return copy_file_atomic(path, rs, 0664, 0, COPY_REFLINK); | |||
824 | } | |||
825 | ||||
826 | int image_clone(Image *i, const char *new_name, bool_Bool read_only) { | |||
827 | _cleanup_(release_lock_file)__attribute__((cleanup(release_lock_file))) LockFile name_lock = LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }; | |||
828 | _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **settings = NULL((void*)0); | |||
829 | _cleanup_free___attribute__((cleanup(freep))) char *roothash = NULL((void*)0); | |||
830 | const char *new_path; | |||
831 | char **j; | |||
832 | int r; | |||
833 | ||||
834 | assert(i)do { if ((__builtin_expect(!!(!(i)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i"), "../src/shared/machine-image.c", 834 , __PRETTY_FUNCTION__); } while (0); | |||
835 | ||||
836 | if (!image_name_is_valid(new_name)) | |||
837 | return -EINVAL22; | |||
838 | ||||
839 | settings = image_settings_path(i); | |||
840 | if (!settings) | |||
841 | return -ENOMEM12; | |||
842 | ||||
843 | roothash = image_roothash_path(i); | |||
844 | if (!roothash) | |||
845 | return -ENOMEM12; | |||
846 | ||||
847 | /* Make sure nobody takes the new name, between the time we | |||
848 | * checked it is currently unused in all search paths, and the | |||
849 | * time we take possession of it */ | |||
850 | r = image_name_lock(new_name, LOCK_EX2|LOCK_NB4, &name_lock); | |||
851 | if (r < 0) | |||
852 | return r; | |||
853 | ||||
854 | r = image_find(IMAGE_MACHINE, new_name, NULL((void*)0)); | |||
855 | if (r >= 0) | |||
856 | return -EEXIST17; | |||
857 | if (r != -ENOENT2) | |||
858 | return r; | |||
859 | ||||
860 | switch (i->type) { | |||
861 | ||||
862 | case IMAGE_SUBVOLUME: | |||
863 | case IMAGE_DIRECTORY: | |||
864 | /* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain | |||
865 | * directory. */ | |||
866 | ||||
867 | new_path = strjoina("/var/lib/machines/", new_name)({ const char *_appendees_[] = { "/var/lib/machines/", new_name }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
868 | ||||
869 | r = btrfs_subvol_snapshot(i->path, new_path, | |||
870 | (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | | |||
871 | BTRFS_SNAPSHOT_FALLBACK_COPY | | |||
872 | BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | | |||
873 | BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE | | |||
874 | BTRFS_SNAPSHOT_RECURSIVE | | |||
875 | BTRFS_SNAPSHOT_QUOTA); | |||
876 | if (r >= 0) | |||
877 | /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */ | |||
878 | (void) btrfs_subvol_auto_qgroup(new_path, 0, true1); | |||
879 | ||||
880 | break; | |||
881 | ||||
882 | case IMAGE_RAW: | |||
883 | new_path = strjoina("/var/lib/machines/", new_name, ".raw")({ const char *_appendees_[] = { "/var/lib/machines/", new_name , ".raw" }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
884 | ||||
885 | r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, FS_NOCOW_FL0x00800000, COPY_REFLINK); | |||
886 | break; | |||
887 | ||||
888 | case IMAGE_BLOCK: | |||
889 | default: | |||
890 | return -EOPNOTSUPP95; | |||
891 | } | |||
892 | ||||
893 | if (r < 0) | |||
894 | return r; | |||
895 | ||||
896 | STRV_FOREACH(j, settings)for ((j) = (settings); (j) && *(j); (j)++) { | |||
897 | r = clone_auxiliary_file(*j, new_name, ".nspawn"); | |||
898 | if (r < 0 && r != -ENOENT2) | |||
899 | log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/shared/machine-image.c", 899, __func__, "Failed to clone settings %s, ignoring: %m" , *j) : -abs(_e); }); | |||
900 | } | |||
901 | ||||
902 | r = clone_auxiliary_file(roothash, new_name, ".roothash"); | |||
903 | if (r < 0 && r != -ENOENT2) | |||
904 | log_debug_errno(r, "Failed to clone root hash file %s, ignoring: %m", roothash)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/shared/machine-image.c", 904, __func__, "Failed to clone root hash file %s, ignoring: %m" , roothash) : -abs(_e); }); | |||
905 | ||||
906 | return 0; | |||
907 | } | |||
908 | ||||
909 | int image_read_only(Image *i, bool_Bool b) { | |||
910 | _cleanup_(release_lock_file)__attribute__((cleanup(release_lock_file))) LockFile global_lock = LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }, local_lock = LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }; | |||
911 | int r; | |||
912 | ||||
913 | assert(i)do { if ((__builtin_expect(!!(!(i)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i"), "../src/shared/machine-image.c", 913 , __PRETTY_FUNCTION__); } while (0); | |||
914 | ||||
915 | if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) | |||
916 | return -EROFS30; | |||
917 | ||||
918 | /* Make sure we don't interfere with a running nspawn */ | |||
919 | r = image_path_lock(i->path, LOCK_EX2|LOCK_NB4, &global_lock, &local_lock); | |||
920 | if (r < 0) | |||
921 | return r; | |||
922 | ||||
923 | switch (i->type) { | |||
924 | ||||
925 | case IMAGE_SUBVOLUME: | |||
926 | ||||
927 | /* Note that we set the flag only on the top-level | |||
928 | * subvolume of the image. */ | |||
929 | ||||
930 | r = btrfs_subvol_set_read_only(i->path, b); | |||
931 | if (r < 0) | |||
932 | return r; | |||
933 | ||||
934 | break; | |||
935 | ||||
936 | case IMAGE_DIRECTORY: | |||
937 | /* For simple directory trees we cannot use the access | |||
938 | mode of the top-level directory, since it has an | |||
939 | effect on the container itself. However, we can | |||
940 | use the "immutable" flag, to at least make the | |||
941 | top-level directory read-only. It's not as good as | |||
942 | a read-only subvolume, but at least something, and | |||
943 | we can read the value back. */ | |||
944 | ||||
945 | r = chattr_path(i->path, b ? FS_IMMUTABLE_FL0x00000010 : 0, FS_IMMUTABLE_FL0x00000010); | |||
946 | if (r < 0) | |||
947 | return r; | |||
948 | ||||
949 | break; | |||
950 | ||||
951 | case IMAGE_RAW: { | |||
952 | struct stat st; | |||
953 | ||||
954 | if (stat(i->path, &st) < 0) | |||
955 | return -errno(*__errno_location ()); | |||
956 | ||||
957 | if (chmod(i->path, (st.st_mode & 0444) | (b ? 0000 : 0200)) < 0) | |||
958 | return -errno(*__errno_location ()); | |||
959 | ||||
960 | /* If the images is now read-only, it's a good time to | |||
961 | * defrag it, given that no write patterns will | |||
962 | * fragment it again. */ | |||
963 | if (b) | |||
964 | (void) btrfs_defrag(i->path); | |||
965 | break; | |||
966 | } | |||
967 | ||||
968 | case IMAGE_BLOCK: { | |||
969 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | |||
970 | struct stat st; | |||
971 | int state = b; | |||
972 | ||||
973 | fd = open(i->path, O_CLOEXEC02000000|O_RDONLY00|O_NONBLOCK04000|O_NOCTTY0400); | |||
974 | if (fd < 0) | |||
975 | return -errno(*__errno_location ()); | |||
976 | ||||
977 | if (fstat(fd, &st) < 0) | |||
978 | return -errno(*__errno_location ()); | |||
979 | if (!S_ISBLK(st.st_mode)((((st.st_mode)) & 0170000) == (0060000))) | |||
980 | return -ENOTTY25; | |||
981 | ||||
982 | if (ioctl(fd, BLKROSET(((0U) << (((0 +8)+8)+14)) | (((0x12)) << (0 +8)) | (((93)) << 0) | ((0) << ((0 +8)+8))), &state) < 0) | |||
983 | return -errno(*__errno_location ()); | |||
984 | ||||
985 | break; | |||
986 | } | |||
987 | ||||
988 | default: | |||
989 | return -EOPNOTSUPP95; | |||
990 | } | |||
991 | ||||
992 | return 0; | |||
993 | } | |||
994 | ||||
995 | int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local) { | |||
996 | _cleanup_free___attribute__((cleanup(freep))) char *p = NULL((void*)0); | |||
997 | LockFile t = LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }; | |||
998 | struct stat st; | |||
999 | int r; | |||
1000 | ||||
1001 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/shared/machine-image.c" , 1001, __PRETTY_FUNCTION__); } while (0); | |||
1002 | assert(global)do { if ((__builtin_expect(!!(!(global)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("global"), "../src/shared/machine-image.c" , 1002, __PRETTY_FUNCTION__); } while (0); | |||
1003 | assert(local)do { if ((__builtin_expect(!!(!(local)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("local"), "../src/shared/machine-image.c" , 1003, __PRETTY_FUNCTION__); } while (0); | |||
1004 | ||||
1005 | /* Locks an image path. This actually creates two locks: one | |||
1006 | * "local" one, next to the image path itself, which might be | |||
1007 | * shared via NFS. And another "global" one, in /run, that | |||
1008 | * uses the device/inode number. This has the benefit that we | |||
1009 | * can even lock a tree that is a mount point, correctly. */ | |||
1010 | ||||
1011 | if (!path_is_absolute(path)) | |||
1012 | return -EINVAL22; | |||
1013 | ||||
1014 | if (getenv_bool("SYSTEMD_NSPAWN_LOCK") == 0) { | |||
1015 | *local = *global = (LockFile) LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }; | |||
1016 | return 0; | |||
1017 | } | |||
1018 | ||||
1019 | if (path_equal(path, "/")) | |||
1020 | return -EBUSY16; | |||
1021 | ||||
1022 | if (stat(path, &st) >= 0) { | |||
1023 | if (S_ISBLK(st.st_mode)((((st.st_mode)) & 0170000) == (0060000))) | |||
1024 | r = asprintf(&p, "/run/systemd/nspawn/locks/block-%u:%u", major(st.st_rdev)gnu_dev_major (st.st_rdev), minor(st.st_rdev)gnu_dev_minor (st.st_rdev)); | |||
1025 | else if (S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000)) || S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000))) | |||
1026 | r = asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino); | |||
1027 | else | |||
1028 | return -ENOTTY25; | |||
1029 | ||||
1030 | if (r < 0) | |||
1031 | return -ENOMEM12; | |||
1032 | } | |||
1033 | ||||
1034 | /* For block devices we don't need the "local" lock, as the major/minor lock above should be sufficient, since | |||
1035 | * block devices are device local anyway. */ | |||
1036 | if (!path_startswith(path, "/dev")) { | |||
1037 | r = make_lock_file_for(path, operation, &t); | |||
1038 | if (r < 0) { | |||
1039 | if ((operation & LOCK_SH1) && r == -EROFS30) | |||
1040 | log_debug_errno(r, "Failed to create shared lock for '%s', ignoring: %m", path)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/shared/machine-image.c", 1040, __func__, "Failed to create shared lock for '%s', ignoring: %m" , path) : -abs(_e); }); | |||
1041 | else | |||
1042 | return r; | |||
1043 | } | |||
1044 | } | |||
1045 | ||||
1046 | if (p) { | |||
1047 | mkdir_p("/run/systemd/nspawn/locks", 0700); | |||
1048 | ||||
1049 | r = make_lock_file(p, operation, global); | |||
1050 | if (r < 0) { | |||
1051 | release_lock_file(&t); | |||
1052 | return r; | |||
1053 | } | |||
1054 | } else | |||
1055 | *global = (LockFile) LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }; | |||
1056 | ||||
1057 | *local = t; | |||
1058 | return 0; | |||
1059 | } | |||
1060 | ||||
1061 | int image_set_limit(Image *i, uint64_t referenced_max) { | |||
1062 | assert(i)do { if ((__builtin_expect(!!(!(i)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i"), "../src/shared/machine-image.c", 1062 , __PRETTY_FUNCTION__); } while (0); | |||
1063 | ||||
1064 | if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) | |||
1065 | return -EROFS30; | |||
1066 | ||||
1067 | if (i->type != IMAGE_SUBVOLUME) | |||
1068 | return -EOPNOTSUPP95; | |||
1069 | ||||
1070 | /* We set the quota both for the subvolume as well as for the | |||
1071 | * subtree. The latter is mostly for historical reasons, since | |||
1072 | * we didn't use to have a concept of subtree quota, and hence | |||
1073 | * only modified the subvolume quota. */ | |||
1074 | ||||
1075 | (void) btrfs_qgroup_set_limit(i->path, 0, referenced_max); | |||
1076 | (void) btrfs_subvol_auto_qgroup(i->path, 0, true1); | |||
1077 | return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max); | |||
1078 | } | |||
1079 | ||||
1080 | int image_read_metadata(Image *i) { | |||
1081 | _cleanup_(release_lock_file)__attribute__((cleanup(release_lock_file))) LockFile global_lock = LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }, local_lock = LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }; | |||
1082 | int r; | |||
1083 | ||||
1084 | assert(i)do { if ((__builtin_expect(!!(!(i)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("i"), "../src/shared/machine-image.c", 1084 , __PRETTY_FUNCTION__); } while (0); | |||
1085 | ||||
1086 | r = image_path_lock(i->path, LOCK_SH1|LOCK_NB4, &global_lock, &local_lock); | |||
1087 | if (r < 0) | |||
1088 | return r; | |||
1089 | ||||
1090 | switch (i->type) { | |||
1091 | ||||
1092 | case IMAGE_SUBVOLUME: | |||
1093 | case IMAGE_DIRECTORY: { | |||
1094 | _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **machine_info = NULL((void*)0), **os_release = NULL((void*)0); | |||
1095 | sd_id128_t machine_id = SD_ID128_NULL((const sd_id128_t) { .qwords = { 0, 0 }}); | |||
1096 | _cleanup_free___attribute__((cleanup(freep))) char *hostname = NULL((void*)0); | |||
1097 | _cleanup_free___attribute__((cleanup(freep))) char *path = NULL((void*)0); | |||
1098 | ||||
1099 | r = chase_symlinks("/etc/hostname", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path); | |||
1100 | if (r < 0 && r != -ENOENT2) | |||
1101 | log_debug_errno(r, "Failed to chase /etc/hostname in image %s: %m", i->name)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/shared/machine-image.c", 1101, __func__, "Failed to chase /etc/hostname in image %s: %m" , i->name) : -abs(_e); }); | |||
1102 | else if (r >= 0) { | |||
1103 | r = read_etc_hostname(path, &hostname); | |||
1104 | if (r < 0) | |||
1105 | log_debug_errno(errno, "Failed to read /etc/hostname of image %s: %m", i->name)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/machine-image.c", 1105, __func__ , "Failed to read /etc/hostname of image %s: %m", i->name) : -abs(_e); }); | |||
1106 | } | |||
1107 | ||||
1108 | path = mfree(path); | |||
1109 | ||||
1110 | r = chase_symlinks("/etc/machine-id", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path); | |||
1111 | if (r < 0 && r != -ENOENT2) | |||
1112 | log_debug_errno(r, "Failed to chase /etc/machine-id in image %s: %m", i->name)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/shared/machine-image.c", 1112, __func__, "Failed to chase /etc/machine-id in image %s: %m" , i->name) : -abs(_e); }); | |||
1113 | else if (r >= 0) { | |||
1114 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | |||
1115 | ||||
1116 | fd = open(path, O_RDONLY00|O_CLOEXEC02000000|O_NOCTTY0400); | |||
1117 | if (fd < 0) | |||
1118 | log_debug_errno(errno, "Failed to open %s: %m", path)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/shared/machine-image.c", 1118, __func__ , "Failed to open %s: %m", path) : -abs(_e); }); | |||
1119 | else { | |||
1120 | r = id128_read_fd(fd, ID128_PLAIN, &machine_id); | |||
1121 | if (r < 0) | |||
1122 | log_debug_errno(r, "Image %s contains invalid machine ID.", i->name)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/shared/machine-image.c", 1122, __func__, "Image %s contains invalid machine ID." , i->name) : -abs(_e); }); | |||
1123 | } | |||
1124 | } | |||
1125 | ||||
1126 | path = mfree(path); | |||
1127 | ||||
1128 | r = chase_symlinks("/etc/machine-info", i->path, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &path); | |||
1129 | if (r < 0 && r != -ENOENT2) | |||
1130 | log_debug_errno(r, "Failed to chase /etc/machine-info in image %s: %m", i->name)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/shared/machine-image.c", 1130, __func__, "Failed to chase /etc/machine-info in image %s: %m" , i->name) : -abs(_e); }); | |||
1131 | else if (r >= 0) { | |||
1132 | r = load_env_file_pairs(NULL((void*)0), path, NULL((void*)0), &machine_info); | |||
1133 | if (r < 0) | |||
1134 | log_debug_errno(r, "Failed to parse machine-info data of %s: %m", i->name)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/shared/machine-image.c", 1134, __func__, "Failed to parse machine-info data of %s: %m" , i->name) : -abs(_e); }); | |||
1135 | } | |||
1136 | ||||
1137 | r = load_os_release_pairs(i->path, &os_release); | |||
1138 | if (r < 0) | |||
1139 | log_debug_errno(r, "Failed to read os-release in image, ignoring: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/shared/machine-image.c", 1139, __func__, "Failed to read os-release in image, ignoring: %m" ) : -abs(_e); }); | |||
1140 | ||||
1141 | free_and_replace(i->hostname, hostname)({ free(i->hostname); (i->hostname) = (hostname); (hostname ) = ((void*)0); 0; }); | |||
1142 | i->machine_id = machine_id; | |||
1143 | strv_free_and_replace(i->machine_info, machine_info)({ strv_free(i->machine_info); (i->machine_info) = (machine_info ); (machine_info) = ((void*)0); 0; }); | |||
1144 | strv_free_and_replace(i->os_release, os_release)({ strv_free(i->os_release); (i->os_release) = (os_release ); (os_release) = ((void*)0); 0; }); | |||
1145 | ||||
1146 | break; | |||
1147 | } | |||
1148 | ||||
1149 | case IMAGE_RAW: | |||
1150 | case IMAGE_BLOCK: { | |||
1151 | _cleanup_(loop_device_unrefp)__attribute__((cleanup(loop_device_unrefp))) LoopDevice *d = NULL((void*)0); | |||
1152 | _cleanup_(dissected_image_unrefp)__attribute__((cleanup(dissected_image_unrefp))) DissectedImage *m = NULL((void*)0); | |||
1153 | ||||
1154 | r = loop_device_make_by_path(i->path, O_RDONLY00, &d); | |||
1155 | if (r < 0) | |||
1156 | return r; | |||
1157 | ||||
1158 | r = dissect_image(d->fd, NULL((void*)0), 0, DISSECT_IMAGE_REQUIRE_ROOT, &m); | |||
1159 | if (r < 0) | |||
1160 | return r; | |||
1161 | ||||
1162 | r = dissected_image_acquire_metadata(m); | |||
1163 | if (r < 0) | |||
1164 | return r; | |||
1165 | ||||
1166 | free_and_replace(i->hostname, m->hostname)({ free(i->hostname); (i->hostname) = (m->hostname); (m->hostname) = ((void*)0); 0; }); | |||
1167 | i->machine_id = m->machine_id; | |||
1168 | strv_free_and_replace(i->machine_info, m->machine_info)({ strv_free(i->machine_info); (i->machine_info) = (m-> machine_info); (m->machine_info) = ((void*)0); 0; }); | |||
1169 | strv_free_and_replace(i->os_release, m->os_release)({ strv_free(i->os_release); (i->os_release) = (m->os_release ); (m->os_release) = ((void*)0); 0; }); | |||
1170 | ||||
1171 | break; | |||
1172 | } | |||
1173 | ||||
1174 | default: | |||
1175 | return -EOPNOTSUPP95; | |||
1176 | } | |||
1177 | ||||
1178 | i->metadata_valid = true1; | |||
1179 | ||||
1180 | return 0; | |||
1181 | } | |||
1182 | ||||
1183 | int image_name_lock(const char *name, int operation, LockFile *ret) { | |||
1184 | const char *p; | |||
1185 | ||||
1186 | assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name"), "../src/shared/machine-image.c" , 1186, __PRETTY_FUNCTION__); } while (0); | |||
1187 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/shared/machine-image.c", 1187, __PRETTY_FUNCTION__); } while (0); | |||
1188 | ||||
1189 | /* Locks an image name, regardless of the precise path used. */ | |||
1190 | ||||
1191 | if (!image_name_is_valid(name)) | |||
1192 | return -EINVAL22; | |||
1193 | ||||
1194 | if (getenv_bool("SYSTEMD_NSPAWN_LOCK") == 0) { | |||
1195 | *ret = (LockFile) LOCK_FILE_INIT{ .fd = -1, .path = ((void*)0) }; | |||
1196 | return 0; | |||
1197 | } | |||
1198 | ||||
1199 | if (streq(name, ".host")(strcmp((name),(".host")) == 0)) | |||
1200 | return -EBUSY16; | |||
1201 | ||||
1202 | mkdir_p("/run/systemd/nspawn/locks", 0700); | |||
1203 | p = strjoina("/run/systemd/nspawn/locks/name-", name)({ const char *_appendees_[] = { "/run/systemd/nspawn/locks/name-" , name }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for ( _i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
1204 | ||||
1205 | return make_lock_file(p, operation, ret); | |||
1206 | } | |||
1207 | ||||
1208 | bool_Bool image_name_is_valid(const char *s) { | |||
1209 | if (!filename_is_valid(s)) | |||
1210 | return false0; | |||
1211 | ||||
1212 | if (string_has_cc(s, NULL((void*)0))) | |||
1213 | return false0; | |||
1214 | ||||
1215 | if (!utf8_is_valid(s)) | |||
1216 | return false0; | |||
1217 | ||||
1218 | /* Temporary files for atomically creating new files */ | |||
1219 | if (startswith(s, ".#")) | |||
1220 | return false0; | |||
1221 | ||||
1222 | return true1; | |||
1223 | } | |||
1224 | ||||
1225 | bool_Bool image_in_search_path(ImageClass class, const char *image) { | |||
1226 | const char *path; | |||
1227 | ||||
1228 | assert(image)do { if ((__builtin_expect(!!(!(image)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("image"), "../src/shared/machine-image.c" , 1228, __PRETTY_FUNCTION__); } while (0); | |||
1229 | ||||
1230 | NULSTR_FOREACH(path, image_search_path[class])for ((path) = (image_search_path[class]); (path) && * (path); (path) = strchr((path), 0)+1) { | |||
1231 | const char *p; | |||
1232 | size_t k; | |||
1233 | ||||
1234 | p = path_startswith(image, path); | |||
1235 | if (!p) | |||
1236 | continue; | |||
1237 | ||||
1238 | /* Make sure there's a filename following */ | |||
1239 | k = strcspn(p, "/"); | |||
1240 | if (k == 0) | |||
1241 | continue; | |||
1242 | ||||
1243 | p += k; | |||
1244 | ||||
1245 | /* Accept trailing slashes */ | |||
1246 | if (p[strspn(p, "/")] == 0) | |||
1247 | return true1; | |||
1248 | ||||
1249 | } | |||
1250 | ||||
1251 | return false0; | |||
1252 | } | |||
1253 | ||||
1254 | static const char* const image_type_table[_IMAGE_TYPE_MAX] = { | |||
1255 | [IMAGE_DIRECTORY] = "directory", | |||
1256 | [IMAGE_SUBVOLUME] = "subvolume", | |||
1257 | [IMAGE_RAW] = "raw", | |||
1258 | [IMAGE_BLOCK] = "block", | |||
1259 | }; | |||
1260 | ||||
1261 | DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType)const char *image_type_to_string(ImageType i) { if (i < 0 || i >= (ImageType) __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(image_type_table), typeof(&*(image_type_table))), sizeof(image_type_table)/sizeof((image_type_table)[0]), ((void )0)))) return ((void*)0); return image_type_table[i]; } ImageType image_type_from_string(const char *s) { return (ImageType) string_table_lookup (image_type_table, __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(image_type_table), typeof(&*(image_type_table))), sizeof(image_type_table)/sizeof((image_type_table)[0]), ((void )0))), s); }; |