Bug Summary

File:build-scan/../src/shared/machine-image.c
Warning:line 806, column 13
Potential leak of memory pointed to by 'j'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name machine-image.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/shared/libsystemd-shared-239.a.p -I src/shared -I ../src/shared -I src/basic -I ../src/basic -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -I . -I .. -I /usr/include/blkid -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility default -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/shared/machine-image.c
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
44static 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
59Image *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
79Image *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
89static 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)
;
8
Taking false branch
9
Loop condition is false. Exiting loop
95
96 l = new0(char*, 4)((char**) calloc((4), sizeof(char*)));
10
Memory is allocated
97 if (!l)
11
Assuming 'l' is non-null
12
Taking false branch
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_; })
;
13
Loop condition is false. Execution continues on line 100
14
Loop condition is false. Execution continues on line 100
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]; }))
{
15
'?' condition is true
16
Loop condition is true. Entering loop body
19
Loop condition is true. Entering loop body
22
Loop condition is false. Execution continues on line 110
103 l[i] = strappend(s, fn);
104 if (!l[i])
17
Assuming the condition is false
18
Taking false branch
20
Assuming the condition is false
21
Taking false branch
105 return NULL((void*)0);
106
107 i++;
108 }
109
110 l[i] = file_in_same_dir(image->path, fn);
111 if (!l[i])
23
Assuming the condition is false
24
Taking false branch
112 return NULL((void*)0);
113
114 return TAKE_PTR(l)({ typeof(l) _ptr_ = (l); (l) = ((void*)0); _ptr_; });
115}
116
117static 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
127static 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
175static 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
207static 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, &quota);
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
419int 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
499int 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
511int 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
518int 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
614int 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
692static 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
705int 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)
;
1
Assuming 'i' is non-null
2
Taking false branch
3
Loop condition is false. Exiting loop
714
715 if (!image_name_is_valid(new_name))
4
Taking false branch
716 return -EINVAL22;
717
718 if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
5
Assuming the condition is false
6
Taking false branch
719 return -EROFS30;
720
721 settings = image_settings_path(i);
7
Calling 'image_settings_path'
25
Returned allocated memory
722 if (!settings
25.1
'settings' is non-null
)
26
Taking false branch
723 return -ENOMEM12;
724
725 roothash = image_roothash_path(i);
726 if (!roothash)
27
Assuming 'roothash' is non-null
28
Taking false branch
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
28.1
'r' is >= 0
< 0)
29
Taking false branch
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
29.1
'r' is >= 0
< 0)
30
Taking false branch
739 return r;
740
741 r = image_find(IMAGE_MACHINE, new_name, NULL((void*)0));
742 if (r >= 0)
31
Assuming 'r' is < 0
32
Taking false branch
743 return -EEXIST17;
744 if (r != -ENOENT2)
33
Assuming the condition is false
34
Taking false branch
745 return r;
746
747 switch (i->type) {
35
Control jumps to 'case IMAGE_RAW:' at line 770
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_; })
;
36
Loop condition is true. Entering loop body
37
Loop condition is true. Entering loop body
38
Loop condition is true. Entering loop body
39
Loop condition is true. Entering loop body
774 new_path = file_in_same_dir(i->path, fn);
775 break;
40
Execution continues on line 782
776 }
777
778 default:
779 return -EOPNOTSUPP95;
780 }
781
782 if (!new_path)
41
Assuming 'new_path' is non-null
42
Taking false branch
783 return -ENOMEM12;
784
785 nn = strdup(new_name);
786 if (!nn)
43
Assuming 'nn' is non-null
44
Taking false branch
787 return -ENOMEM12;
788
789 r = rename_noreplace(AT_FDCWD-100, i->path, AT_FDCWD-100, new_path);
790 if (r < 0)
45
Assuming 'r' is >= 0
46
Taking false branch
791 return r;
792
793 /* Restore the immutable bit, if it was set before */
794 if (file_attr & FS_IMMUTABLE_FL0x00000010)
47
Taking false branch
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)++) {
48
Loop condition is true. Entering loop body
52
Loop condition is true. Entering loop body
54
Loop condition is true. Entering loop body
56
Loop condition is false. Execution continues on line 806
801 r = rename_auxiliary_file(*j, new_name, ".nspawn");
802 if (r
48.1
'r' is < 0
< 0
&& r != -ENOENT2)
49
Taking true branch
53
Assuming 'r' is >= 0
55
Assuming 'r' is >= 0
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); })
;
50
Assuming the condition is false
51
'?' condition is false
804 }
805
806 r = rename_auxiliary_file(roothash, new_name, ".roothash");
57
Potential leak of memory pointed to by 'j'
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
813static 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
826int 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
909int 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
995int 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
1061int 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
1080int 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
1183int 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
1208bool_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
1225bool_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
1254static 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
1261DEFINE_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); }
;