]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/portable/portabled-image-bus.c
portable: add 'reattach' verb and DBUS interface
[thirdparty/systemd.git] / src / portable / portabled-image-bus.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <fcntl.h>
4 #include <sys/stat.h>
5 #include <sys/types.h>
6 #include <unistd.h>
7
8 #include "alloc-util.h"
9 #include "bus-common-errors.h"
10 #include "bus-get-properties.h"
11 #include "bus-label.h"
12 #include "bus-polkit.h"
13 #include "bus-util.h"
14 #include "discover-image.h"
15 #include "fd-util.h"
16 #include "fileio.h"
17 #include "io-util.h"
18 #include "missing_capability.h"
19 #include "portable.h"
20 #include "portabled-bus.h"
21 #include "portabled-image-bus.h"
22 #include "portabled-image.h"
23 #include "portabled.h"
24 #include "process-util.h"
25 #include "strv.h"
26 #include "user-util.h"
27
28 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
29
30 int bus_image_common_get_os_release(
31 Manager *m,
32 sd_bus_message *message,
33 const char *name_or_path,
34 Image *image,
35 sd_bus_error *error) {
36
37 int r;
38
39 assert(name_or_path || image);
40 assert(message);
41
42 if (!m) {
43 assert(image);
44 m = image->userdata;
45 }
46
47 r = bus_image_acquire(m,
48 message,
49 name_or_path,
50 image,
51 BUS_IMAGE_AUTHENTICATE_BY_PATH,
52 "org.freedesktop.portable1.inspect-images",
53 &image,
54 error);
55 if (r < 0)
56 return r;
57 if (r == 0) /* Will call us back */
58 return 1;
59
60 if (!image->metadata_valid) {
61 r = image_read_metadata(image);
62 if (r < 0)
63 return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
64 }
65
66 return bus_reply_pair_array(message, image->os_release);
67 }
68
69 static int bus_image_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
70 return bus_image_common_get_os_release(NULL, message, NULL, userdata, error);
71 }
72
73 static int append_fd(sd_bus_message *m, PortableMetadata *d) {
74 _cleanup_fclose_ FILE *f = NULL;
75 _cleanup_free_ char *buf = NULL;
76 size_t n;
77 int r;
78
79 assert(m);
80 assert(d);
81 assert(d->fd >= 0);
82
83 f = take_fdopen(&d->fd, "r");
84 if (!f)
85 return -errno;
86
87 r = read_full_stream(f, &buf, &n);
88 if (r < 0)
89 return r;
90
91 return sd_bus_message_append_array(m, 'y', buf, n);
92 }
93
94 int bus_image_common_get_metadata(
95 Manager *m,
96 sd_bus_message *message,
97 const char *name_or_path,
98 Image *image,
99 sd_bus_error *error) {
100
101 _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
102 _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
103 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
104 _cleanup_free_ PortableMetadata **sorted = NULL;
105 _cleanup_strv_free_ char **matches = NULL;
106 size_t i;
107 int r;
108
109 assert(name_or_path || image);
110 assert(message);
111
112 if (!m) {
113 assert(image);
114 m = image->userdata;
115 }
116
117 r = sd_bus_message_read_strv(message, &matches);
118 if (r < 0)
119 return r;
120
121 r = bus_image_acquire(m,
122 message,
123 name_or_path,
124 image,
125 BUS_IMAGE_AUTHENTICATE_BY_PATH,
126 "org.freedesktop.portable1.inspect-images",
127 &image,
128 error);
129 if (r < 0)
130 return r;
131 if (r == 0) /* Will call us back */
132 return 1;
133
134 r = portable_extract(
135 image->path,
136 matches,
137 &os_release,
138 &unit_files,
139 error);
140 if (r < 0)
141 return r;
142
143 r = portable_metadata_hashmap_to_sorted_array(unit_files, &sorted);
144 if (r < 0)
145 return r;
146
147 r = sd_bus_message_new_method_return(message, &reply);
148 if (r < 0)
149 return r;
150
151 r = sd_bus_message_append(reply, "s", image->path);
152 if (r < 0)
153 return r;
154
155 r = append_fd(reply, os_release);
156 if (r < 0)
157 return r;
158
159 r = sd_bus_message_open_container(reply, 'a', "{say}");
160 if (r < 0)
161 return r;
162
163 for (i = 0; i < hashmap_size(unit_files); i++) {
164
165 r = sd_bus_message_open_container(reply, 'e', "say");
166 if (r < 0)
167 return r;
168
169 r = sd_bus_message_append(reply, "s", sorted[i]->name);
170 if (r < 0)
171 return r;
172
173 r = append_fd(reply, sorted[i]);
174 if (r < 0)
175 return r;
176
177 r = sd_bus_message_close_container(reply);
178 if (r < 0)
179 return r;
180 }
181
182 r = sd_bus_message_close_container(reply);
183 if (r < 0)
184 return r;
185
186 return sd_bus_send(NULL, reply, NULL);
187 }
188
189 static int bus_image_method_get_metadata(sd_bus_message *message, void *userdata, sd_bus_error *error) {
190 return bus_image_common_get_metadata(NULL, message, NULL, userdata, error);
191 }
192
193 static int bus_image_method_get_state(
194 sd_bus_message *message,
195 void *userdata,
196 sd_bus_error *error) {
197
198 Image *image = userdata;
199 PortableState state;
200 int r;
201
202 assert(message);
203 assert(image);
204
205 r = portable_get_state(
206 sd_bus_message_get_bus(message),
207 image->path,
208 0,
209 &state,
210 error);
211 if (r < 0)
212 return r;
213
214 return sd_bus_reply_method_return(message, "s", portable_state_to_string(state));
215 }
216
217 int bus_image_common_attach(
218 Manager *m,
219 sd_bus_message *message,
220 const char *name_or_path,
221 Image *image,
222 sd_bus_error *error) {
223
224 _cleanup_strv_free_ char **matches = NULL;
225 PortableChange *changes = NULL;
226 PortableFlags flags = 0;
227 const char *profile, *copy_mode;
228 size_t n_changes = 0;
229 int runtime, r;
230
231 assert(message);
232 assert(name_or_path || image);
233
234 if (!m) {
235 assert(image);
236 m = image->userdata;
237 }
238
239 r = sd_bus_message_read_strv(message, &matches);
240 if (r < 0)
241 return r;
242
243 r = sd_bus_message_read(message, "sbs", &profile, &runtime, &copy_mode);
244 if (r < 0)
245 return r;
246
247 if (streq(copy_mode, "symlink"))
248 flags |= PORTABLE_PREFER_SYMLINK;
249 else if (streq(copy_mode, "copy"))
250 flags |= PORTABLE_PREFER_COPY;
251 else if (!isempty(copy_mode))
252 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
253
254 if (runtime)
255 flags |= PORTABLE_RUNTIME;
256
257 r = bus_image_acquire(m,
258 message,
259 name_or_path,
260 image,
261 BUS_IMAGE_AUTHENTICATE_ALL,
262 "org.freedesktop.portable1.attach-images",
263 &image,
264 error);
265 if (r < 0)
266 return r;
267 if (r == 0) /* Will call us back */
268 return 1;
269
270 r = portable_attach(
271 sd_bus_message_get_bus(message),
272 image->path,
273 matches,
274 profile,
275 flags,
276 &changes,
277 &n_changes,
278 error);
279 if (r < 0)
280 goto finish;
281
282 r = reply_portable_changes(message, changes, n_changes);
283
284 finish:
285 portable_changes_free(changes, n_changes);
286 return r;
287 }
288
289 static int bus_image_method_attach(sd_bus_message *message, void *userdata, sd_bus_error *error) {
290 return bus_image_common_attach(NULL, message, NULL, userdata, error);
291 }
292
293 static int bus_image_method_detach(
294 sd_bus_message *message,
295 void *userdata,
296 sd_bus_error *error) {
297
298 PortableChange *changes = NULL;
299 Image *image = userdata;
300 Manager *m = image->userdata;
301 size_t n_changes = 0;
302 int r, runtime;
303
304 assert(message);
305 assert(image);
306 assert(m);
307
308 r = sd_bus_message_read(message, "b", &runtime);
309 if (r < 0)
310 return r;
311
312 r = bus_verify_polkit_async(
313 message,
314 CAP_SYS_ADMIN,
315 "org.freedesktop.portable1.attach-images",
316 NULL,
317 false,
318 UID_INVALID,
319 &m->polkit_registry,
320 error);
321 if (r < 0)
322 return r;
323 if (r == 0)
324 return 1; /* Will call us back */
325
326 r = portable_detach(
327 sd_bus_message_get_bus(message),
328 image->path,
329 runtime ? PORTABLE_RUNTIME : 0,
330 &changes,
331 &n_changes,
332 error);
333 if (r < 0)
334 goto finish;
335
336 r = reply_portable_changes(message, changes, n_changes);
337
338 finish:
339 portable_changes_free(changes, n_changes);
340 return r;
341 }
342
343 int bus_image_common_remove(
344 Manager *m,
345 sd_bus_message *message,
346 const char *name_or_path,
347 Image *image,
348 sd_bus_error *error) {
349
350 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
351 _cleanup_(sigkill_waitp) pid_t child = 0;
352 PortableState state;
353 int r;
354
355 assert(message);
356 assert(name_or_path || image);
357
358 if (!m) {
359 assert(image);
360 m = image->userdata;
361 }
362
363 if (m->n_operations >= OPERATIONS_MAX)
364 return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
365
366 r = bus_image_acquire(m,
367 message,
368 name_or_path,
369 image,
370 BUS_IMAGE_AUTHENTICATE_ALL,
371 "org.freedesktop.portable1.manage-images",
372 &image,
373 error);
374 if (r < 0)
375 return r;
376 if (r == 0)
377 return 1; /* Will call us back */
378
379 r = portable_get_state(
380 sd_bus_message_get_bus(message),
381 image->path,
382 0,
383 &state,
384 error);
385 if (r < 0)
386 return r;
387
388 if (state != PORTABLE_DETACHED)
389 return sd_bus_error_set_errnof(error, EBUSY, "Image '%s' is not detached, refusing.", image->path);
390
391 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
392 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
393
394 r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
395 if (r < 0)
396 return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
397 if (r == 0) {
398 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
399
400 r = image_remove(image);
401 if (r < 0) {
402 (void) write(errno_pipe_fd[1], &r, sizeof(r));
403 _exit(EXIT_FAILURE);
404 }
405
406 _exit(EXIT_SUCCESS);
407 }
408
409 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
410
411 r = operation_new(m, child, message, errno_pipe_fd[0], NULL);
412 if (r < 0)
413 return r;
414
415 child = 0;
416 errno_pipe_fd[0] = -1;
417
418 return 1;
419 }
420
421 static int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) {
422 return bus_image_common_remove(NULL, message, NULL, userdata, error);
423 }
424
425 /* Given two PortableChange arrays, return a new array that has all elements of the first that are
426 * not also present in the second, comparing the basename of the path values. */
427 static int normalize_portable_changes(
428 const PortableChange *changes_attached,
429 size_t n_changes_attached,
430 const PortableChange *changes_detached,
431 size_t n_changes_detached,
432 PortableChange **ret_changes,
433 size_t *ret_n_changes) {
434
435 PortableChange *changes = NULL;
436 size_t n_changes = 0;
437 int r = 0;
438
439 assert(ret_n_changes);
440 assert(ret_changes);
441
442 if (n_changes_detached == 0)
443 return 0; /* Nothing to do */
444
445 changes = new0(PortableChange, n_changes_attached + n_changes_detached);
446 if (!changes)
447 return -ENOMEM;
448
449 /* Corner case: only detached, nothing attached */
450 if (n_changes_attached == 0) {
451 memcpy(changes, changes_detached, sizeof(PortableChange) * n_changes_detached);
452 *ret_changes = TAKE_PTR(changes);
453 *ret_n_changes = n_changes_detached;
454
455 return 0;
456 }
457
458 for (size_t i = 0; i < n_changes_detached; ++i) {
459 bool found = false;
460
461 for (size_t j = 0; j < n_changes_attached; ++j)
462 if (streq(basename(changes_detached[i].path), basename(changes_attached[j].path))) {
463 found = true;
464 break;
465 }
466
467 if (!found) {
468 _cleanup_free_ char *path = NULL, *source = NULL;
469
470 path = strdup(changes_detached[i].path);
471 if (!path) {
472 r = -ENOMEM;
473 goto fail;
474 }
475
476 if (changes_detached[i].source) {
477 source = strdup(changes_detached[i].source);
478 if (!source) {
479 r = -ENOMEM;
480 goto fail;
481 }
482 }
483
484 changes[n_changes++] = (PortableChange) {
485 .type = changes_detached[i].type,
486 .path = TAKE_PTR(path),
487 .source = TAKE_PTR(source),
488 };
489 }
490 }
491
492 *ret_n_changes = n_changes;
493 *ret_changes = TAKE_PTR(changes);
494
495 return 0;
496
497 fail:
498 portable_changes_free(changes, n_changes);
499 return r;
500 }
501
502 int bus_image_common_reattach(
503 Manager *m,
504 sd_bus_message *message,
505 const char *name_or_path,
506 Image *image,
507 sd_bus_error *error) {
508
509 PortableChange *changes_detached = NULL, *changes_attached = NULL, *changes_gone = NULL;
510 size_t n_changes_detached = 0, n_changes_attached = 0, n_changes_gone = 0;
511 _cleanup_strv_free_ char **matches = NULL;
512 PortableFlags flags = PORTABLE_REATTACH;
513 const char *profile, *copy_mode;
514 int runtime, r;
515
516 assert(message);
517 assert(name_or_path || image);
518
519 if (!m) {
520 assert(image);
521 m = image->userdata;
522 }
523
524 r = sd_bus_message_read_strv(message, &matches);
525 if (r < 0)
526 return r;
527
528 r = sd_bus_message_read(message, "sbs", &profile, &runtime, &copy_mode);
529 if (r < 0)
530 return r;
531
532 if (streq(copy_mode, "symlink"))
533 flags |= PORTABLE_PREFER_SYMLINK;
534 else if (streq(copy_mode, "copy"))
535 flags |= PORTABLE_PREFER_COPY;
536 else if (!isempty(copy_mode))
537 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Unknown copy mode '%s'", copy_mode);
538
539 if (runtime)
540 flags |= PORTABLE_RUNTIME;
541
542 r = bus_image_acquire(m,
543 message,
544 name_or_path,
545 image,
546 BUS_IMAGE_AUTHENTICATE_ALL,
547 "org.freedesktop.portable1.attach-images",
548 &image,
549 error);
550 if (r < 0)
551 return r;
552 if (r == 0) /* Will call us back */
553 return 1;
554
555 r = portable_detach(
556 sd_bus_message_get_bus(message),
557 image->path,
558 flags,
559 &changes_detached,
560 &n_changes_detached,
561 error);
562 if (r < 0)
563 goto finish;
564
565 r = portable_attach(
566 sd_bus_message_get_bus(message),
567 image->path,
568 matches,
569 profile,
570 flags,
571 &changes_attached,
572 &n_changes_attached,
573 error);
574 if (r < 0)
575 goto finish;
576
577 /* We want to return the list of units really removed by the detach,
578 * and not added again by the attach */
579 r = normalize_portable_changes(changes_attached, n_changes_attached,
580 changes_detached, n_changes_detached,
581 &changes_gone, &n_changes_gone);
582 if (r < 0)
583 goto finish;
584
585 /* First, return the units that are gone (so that the caller can stop them)
586 * Then, return the units that are changed/added (so that the caller can
587 * start/restart/enable them) */
588 r = reply_portable_changes_pair(message,
589 changes_gone, n_changes_gone,
590 changes_attached, n_changes_attached);
591 if (r < 0)
592 goto finish;
593
594 finish:
595 portable_changes_free(changes_detached, n_changes_detached);
596 portable_changes_free(changes_attached, n_changes_attached);
597 portable_changes_free(changes_gone, n_changes_gone);
598 return r;
599 }
600
601 static int bus_image_method_reattach(sd_bus_message *message, void *userdata, sd_bus_error *error) {
602 return bus_image_common_reattach(NULL, message, NULL, userdata, error);
603 }
604
605 int bus_image_common_mark_read_only(
606 Manager *m,
607 sd_bus_message *message,
608 const char *name_or_path,
609 Image *image,
610 sd_bus_error *error) {
611
612 int r, read_only;
613
614 assert(message);
615 assert(name_or_path || image);
616
617 if (!m) {
618 assert(image);
619 m = image->userdata;
620 }
621
622 r = sd_bus_message_read(message, "b", &read_only);
623 if (r < 0)
624 return r;
625
626 r = bus_image_acquire(m,
627 message,
628 name_or_path,
629 image,
630 BUS_IMAGE_AUTHENTICATE_ALL,
631 "org.freedesktop.portable1.manage-images",
632 &image,
633 error);
634 if (r < 0)
635 return r;
636 if (r == 0)
637 return 1; /* Will call us back */
638
639 r = image_read_only(image, read_only);
640 if (r < 0)
641 return r;
642
643 return sd_bus_reply_method_return(message, NULL);
644 }
645
646 static int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) {
647 return bus_image_common_mark_read_only(NULL, message, NULL, userdata, error);
648 }
649
650 int bus_image_common_set_limit(
651 Manager *m,
652 sd_bus_message *message,
653 const char *name_or_path,
654 Image *image,
655 sd_bus_error *error) {
656
657 uint64_t limit;
658 int r;
659
660 assert(message);
661 assert(name_or_path || image);
662
663 if (!m) {
664 assert(image);
665 m = image->userdata;
666 }
667
668 r = sd_bus_message_read(message, "t", &limit);
669 if (r < 0)
670 return r;
671 if (!FILE_SIZE_VALID_OR_INFINITY(limit))
672 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
673
674 r = bus_image_acquire(m,
675 message,
676 name_or_path,
677 image,
678 BUS_IMAGE_AUTHENTICATE_ALL,
679 "org.freedesktop.portable1.manage-images",
680 &image,
681 error);
682 if (r < 0)
683 return r;
684 if (r == 0)
685 return 1; /* Will call us back */
686
687 r = image_set_limit(image, limit);
688 if (r < 0)
689 return r;
690
691 return sd_bus_reply_method_return(message, NULL);
692 }
693
694 static int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
695 return bus_image_common_set_limit(NULL, message, NULL, userdata, error);
696 }
697
698 const sd_bus_vtable image_vtable[] = {
699 SD_BUS_VTABLE_START(0),
700 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
701 SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
702 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
703 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
704 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
705 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
706 SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
707 SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
708 SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
709 SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
710 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
711 SD_BUS_METHOD("GetMetadata", "as", "saya{say}", bus_image_method_get_metadata, SD_BUS_VTABLE_UNPRIVILEGED),
712 SD_BUS_METHOD("GetState", NULL, "s", bus_image_method_get_state, SD_BUS_VTABLE_UNPRIVILEGED),
713 SD_BUS_METHOD("Attach", "assbs", "a(sss)", bus_image_method_attach, SD_BUS_VTABLE_UNPRIVILEGED),
714 SD_BUS_METHOD("Detach", "b", "a(sss)", bus_image_method_detach, SD_BUS_VTABLE_UNPRIVILEGED),
715 SD_BUS_METHOD("Reattach", "assbs", "a(sss)a(sss)", bus_image_method_reattach, SD_BUS_VTABLE_UNPRIVILEGED),
716 SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
717 SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
718 SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
719 SD_BUS_VTABLE_END
720 };
721
722 int bus_image_path(Image *image, char **ret) {
723 assert(image);
724 assert(ret);
725
726 if (!image->discoverable)
727 return -EINVAL;
728
729 return sd_bus_path_encode("/org/freedesktop/portable1/image", image->name, ret);
730 }
731
732 int bus_image_acquire(
733 Manager *m,
734 sd_bus_message *message,
735 const char *name_or_path,
736 Image *image,
737 ImageAcquireMode mode,
738 const char *polkit_action,
739 Image **ret,
740 sd_bus_error *error) {
741
742 _cleanup_(image_unrefp) Image *loaded = NULL;
743 Image *cached;
744 int r;
745
746 assert(m);
747 assert(message);
748 assert(name_or_path || image);
749 assert(mode >= 0);
750 assert(mode < _BUS_IMAGE_ACQUIRE_MODE_MAX);
751 assert(polkit_action || mode == BUS_IMAGE_REFUSE_BY_PATH);
752 assert(ret);
753
754 /* Acquires an 'Image' object if not acquired yet, and enforces necessary authentication while doing so. */
755
756 if (mode == BUS_IMAGE_AUTHENTICATE_ALL) {
757 r = bus_verify_polkit_async(
758 message,
759 CAP_SYS_ADMIN,
760 polkit_action,
761 NULL,
762 false,
763 UID_INVALID,
764 &m->polkit_registry,
765 error);
766 if (r < 0)
767 return r;
768 if (r == 0) { /* Will call us back */
769 *ret = NULL;
770 return 0;
771 }
772 }
773
774 /* Already passed in? */
775 if (image) {
776 *ret = image;
777 return 1;
778 }
779
780 /* Let's see if this image is already cached? */
781 cached = manager_image_cache_get(m, name_or_path);
782 if (cached) {
783 *ret = cached;
784 return 1;
785 }
786
787 if (image_name_is_valid(name_or_path)) {
788
789 /* If it's a short name, let's search for it */
790 r = image_find(IMAGE_PORTABLE, name_or_path, NULL, &loaded);
791 if (r == -ENOENT)
792 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE, "No image '%s' found.", name_or_path);
793
794 /* other errors are handled below… */
795 } else {
796 /* Don't accept path if this is always forbidden */
797 if (mode == BUS_IMAGE_REFUSE_BY_PATH)
798 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Expected image name, not path in place of '%s'.", name_or_path);
799
800 if (!path_is_absolute(name_or_path))
801 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is not valid or not a valid path.", name_or_path);
802
803 if (!path_is_normalized(name_or_path))
804 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image path '%s' is not normalized.", name_or_path);
805
806 if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) {
807 r = bus_verify_polkit_async(
808 message,
809 CAP_SYS_ADMIN,
810 polkit_action,
811 NULL,
812 false,
813 UID_INVALID,
814 &m->polkit_registry,
815 error);
816 if (r < 0)
817 return r;
818 if (r == 0) { /* Will call us back */
819 *ret = NULL;
820 return 0;
821 }
822 }
823
824 r = image_from_path(name_or_path, &loaded);
825 }
826 if (r == -EMEDIUMTYPE) {
827 sd_bus_error_setf(error, BUS_ERROR_BAD_PORTABLE_IMAGE_TYPE, "Typ of image '%s' not recognized; supported image types are directories/btrfs subvolumes, block devices, and raw disk image files with suffix '.raw'.", name_or_path);
828 return r;
829 }
830 if (r < 0)
831 return r;
832
833 /* Add what we just loaded to the cache. This has as side-effect that the object stays in memory until the
834 * cache is purged again, i.e. at least for the current event loop iteration, which is all we need, and which
835 * means we don't actually need to ref the return object. */
836 r = manager_image_cache_add(m, loaded);
837 if (r < 0)
838 return r;
839
840 *ret = loaded;
841 return 1;
842 }
843
844 int bus_image_object_find(
845 sd_bus *bus,
846 const char *path,
847 const char *interface,
848 void *userdata,
849 void **found,
850 sd_bus_error *error) {
851
852 _cleanup_free_ char *e = NULL;
853 Manager *m = userdata;
854 Image *image = NULL;
855 int r;
856
857 assert(bus);
858 assert(path);
859 assert(interface);
860 assert(found);
861
862 r = sd_bus_path_decode(path, "/org/freedesktop/portable1/image", &e);
863 if (r < 0)
864 return 0;
865 if (r == 0)
866 goto not_found;
867
868 r = bus_image_acquire(m, sd_bus_get_current_message(bus), e, NULL, BUS_IMAGE_REFUSE_BY_PATH, NULL, &image, error);
869 if (r == -ENOENT)
870 goto not_found;
871 if (r < 0)
872 return r;
873
874 *found = image;
875 return 1;
876
877 not_found:
878 *found = NULL;
879 return 0;
880 }
881
882 int bus_image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
883 _cleanup_hashmap_free_ Hashmap *images = NULL;
884 _cleanup_strv_free_ char **l = NULL;
885 size_t n_allocated = 0, n = 0;
886 Manager *m = userdata;
887 Image *image;
888 int r;
889
890 assert(bus);
891 assert(path);
892 assert(nodes);
893
894 images = hashmap_new(&image_hash_ops);
895 if (!images)
896 return -ENOMEM;
897
898 r = manager_image_cache_discover(m, images, error);
899 if (r < 0)
900 return r;
901
902 HASHMAP_FOREACH(image, images) {
903 char *p;
904
905 r = bus_image_path(image, &p);
906 if (r < 0)
907 return r;
908
909 if (!GREEDY_REALLOC(l, n_allocated, n+2)) {
910 free(p);
911 return -ENOMEM;
912 }
913
914 l[n++] = p;
915 l[n] = NULL;
916 }
917
918 *nodes = TAKE_PTR(l);
919
920 return 1;
921 }