1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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-object.h"
13 #include "bus-polkit.h"
15 #include "discover-image.h"
19 #include "missing_capability.h"
22 #include "portabled-bus.h"
23 #include "portabled-image-bus.h"
24 #include "portabled-image.h"
25 #include "portabled.h"
26 #include "process-util.h"
28 #include "user-util.h"
30 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, image_type
, ImageType
);
32 int bus_image_common_get_os_release(
34 sd_bus_message
*message
,
35 const char *name_or_path
,
37 sd_bus_error
*error
) {
41 assert(name_or_path
|| image
);
49 r
= bus_image_acquire(m
,
53 BUS_IMAGE_AUTHENTICATE_BY_PATH
,
54 "org.freedesktop.portable1.inspect-images",
59 if (r
== 0) /* Will call us back */
62 if (!image
->metadata_valid
) {
63 r
= image_read_metadata(image
);
65 return sd_bus_error_set_errnof(error
, r
, "Failed to read image metadata: %m");
68 return bus_reply_pair_array(message
, image
->os_release
);
71 static int bus_image_method_get_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
72 return bus_image_common_get_os_release(NULL
, message
, NULL
, userdata
, error
);
75 static int append_fd(sd_bus_message
*m
, PortableMetadata
*d
) {
76 _cleanup_fclose_
FILE *f
= NULL
;
77 _cleanup_free_
char *buf
= NULL
;
86 f
= take_fdopen(&d
->fd
, "r");
90 r
= read_full_stream(f
, &buf
, &n
);
95 return sd_bus_message_append_array(m
, 'y', buf
, n
);
98 int bus_image_common_get_metadata(
100 sd_bus_message
*message
,
101 const char *name_or_path
,
103 sd_bus_error
*error
) {
105 _cleanup_(portable_metadata_unrefp
) PortableMetadata
*os_release
= NULL
;
106 _cleanup_strv_free_
char **matches
= NULL
, **extension_images
= NULL
;
107 _cleanup_hashmap_free_ Hashmap
*unit_files
= NULL
;
108 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
109 _cleanup_free_ PortableMetadata
**sorted
= NULL
;
110 /* Unused for now, but added to the DBUS methods for future-proofing */
111 uint64_t input_flags
= 0;
115 assert(name_or_path
|| image
);
123 if (sd_bus_message_is_method_call(message
, NULL
, "GetImageMetadataWithExtensions") ||
124 sd_bus_message_is_method_call(message
, NULL
, "GetMetadataWithExtensions")) {
125 r
= sd_bus_message_read_strv(message
, &extension_images
);
130 r
= sd_bus_message_read_strv(message
, &matches
);
134 if (sd_bus_message_is_method_call(message
, NULL
, "GetImageMetadataWithExtensions") ||
135 sd_bus_message_is_method_call(message
, NULL
, "GetMetadataWithExtensions")) {
136 r
= sd_bus_message_read(message
, "t", &input_flags
);
139 /* Let clients know that this version doesn't support any flags */
140 if (input_flags
!= 0)
141 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
142 "Invalid 'flags' parameter '%" PRIu64
"'",
146 r
= bus_image_acquire(m
,
150 BUS_IMAGE_AUTHENTICATE_BY_PATH
,
151 "org.freedesktop.portable1.inspect-images",
156 if (r
== 0) /* Will call us back */
159 r
= portable_extract(
169 r
= portable_metadata_hashmap_to_sorted_array(unit_files
, &sorted
);
173 r
= sd_bus_message_new_method_return(message
, &reply
);
177 r
= sd_bus_message_append(reply
, "s", image
->path
);
181 r
= append_fd(reply
, os_release
);
185 r
= sd_bus_message_open_container(reply
, 'a', "{say}");
189 for (i
= 0; i
< hashmap_size(unit_files
); i
++) {
191 r
= sd_bus_message_open_container(reply
, 'e', "say");
195 r
= sd_bus_message_append(reply
, "s", sorted
[i
]->name
);
199 r
= append_fd(reply
, sorted
[i
]);
203 r
= sd_bus_message_close_container(reply
);
208 r
= sd_bus_message_close_container(reply
);
212 return sd_bus_send(NULL
, reply
, NULL
);
215 static int bus_image_method_get_metadata(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
216 return bus_image_common_get_metadata(NULL
, message
, NULL
, userdata
, error
);
219 static int bus_image_method_get_state(
220 sd_bus_message
*message
,
222 sd_bus_error
*error
) {
224 Image
*image
= userdata
;
231 r
= portable_get_state(
232 sd_bus_message_get_bus(message
),
240 return sd_bus_reply_method_return(message
, "s", portable_state_to_string(state
));
243 int bus_image_common_attach(
245 sd_bus_message
*message
,
246 const char *name_or_path
,
248 sd_bus_error
*error
) {
250 _cleanup_strv_free_
char **matches
= NULL
, **extension_images
= NULL
;
251 PortableChange
*changes
= NULL
;
252 PortableFlags flags
= 0;
253 const char *profile
, *copy_mode
;
254 size_t n_changes
= 0;
258 assert(name_or_path
|| image
);
265 if (sd_bus_message_is_method_call(message
, NULL
, "AttachImageWithExtensions") ||
266 sd_bus_message_is_method_call(message
, NULL
, "AttachWithExtensions")) {
267 r
= sd_bus_message_read_strv(message
, &extension_images
);
272 r
= sd_bus_message_read_strv(message
, &matches
);
276 r
= sd_bus_message_read(message
, "s", &profile
);
280 if (sd_bus_message_is_method_call(message
, NULL
, "AttachImageWithExtensions") ||
281 sd_bus_message_is_method_call(message
, NULL
, "AttachWithExtensions")) {
282 uint64_t input_flags
= 0;
284 r
= sd_bus_message_read(message
, "st", ©_mode
, &input_flags
);
287 if ((input_flags
& ~_PORTABLE_MASK_PUBLIC
) != 0)
288 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
289 "Invalid 'flags' parameter '%" PRIu64
"'",
291 flags
|= input_flags
;
295 r
= sd_bus_message_read(message
, "bs", &runtime
, ©_mode
);
300 flags
|= PORTABLE_RUNTIME
;
303 if (streq(copy_mode
, "symlink"))
304 flags
|= PORTABLE_PREFER_SYMLINK
;
305 else if (streq(copy_mode
, "copy"))
306 flags
|= PORTABLE_PREFER_COPY
;
307 else if (!isempty(copy_mode
))
308 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown copy mode '%s'", copy_mode
);
310 r
= bus_image_acquire(m
,
314 BUS_IMAGE_AUTHENTICATE_ALL
,
315 "org.freedesktop.portable1.attach-images",
320 if (r
== 0) /* Will call us back */
324 sd_bus_message_get_bus(message
),
336 r
= reply_portable_changes(message
, changes
, n_changes
);
339 portable_changes_free(changes
, n_changes
);
343 static int bus_image_method_attach(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
344 return bus_image_common_attach(NULL
, message
, NULL
, userdata
, error
);
347 static int bus_image_method_detach(
348 sd_bus_message
*message
,
350 sd_bus_error
*error
) {
352 _cleanup_strv_free_
char **extension_images
= NULL
;
353 PortableChange
*changes
= NULL
;
354 Image
*image
= userdata
;
355 Manager
*m
= image
->userdata
;
356 PortableFlags flags
= 0;
357 size_t n_changes
= 0;
364 if (sd_bus_message_is_method_call(message
, NULL
, "DetachWithExtensions")) {
365 r
= sd_bus_message_read_strv(message
, &extension_images
);
370 if (sd_bus_message_is_method_call(message
, NULL
, "DetachWithExtensions")) {
371 uint64_t input_flags
= 0;
373 r
= sd_bus_message_read(message
, "t", &input_flags
);
377 if ((input_flags
& ~_PORTABLE_MASK_PUBLIC
) != 0)
378 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
379 "Invalid 'flags' parameter '%" PRIu64
"'",
381 flags
|= input_flags
;
385 r
= sd_bus_message_read(message
, "b", &runtime
);
390 flags
|= PORTABLE_RUNTIME
;
393 r
= bus_verify_polkit_async(
396 "org.freedesktop.portable1.attach-images",
405 return 1; /* Will call us back */
408 sd_bus_message_get_bus(message
),
418 r
= reply_portable_changes(message
, changes
, n_changes
);
421 portable_changes_free(changes
, n_changes
);
425 int bus_image_common_remove(
427 sd_bus_message
*message
,
428 const char *name_or_path
,
430 sd_bus_error
*error
) {
432 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
433 _cleanup_(sigkill_waitp
) pid_t child
= 0;
438 assert(name_or_path
|| image
);
445 if (m
->n_operations
>= OPERATIONS_MAX
)
446 return sd_bus_error_set(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing operations.");
448 r
= bus_image_acquire(m
,
452 BUS_IMAGE_AUTHENTICATE_ALL
,
453 "org.freedesktop.portable1.manage-images",
459 return 1; /* Will call us back */
461 r
= portable_get_state(
462 sd_bus_message_get_bus(message
),
470 if (state
!= PORTABLE_DETACHED
)
471 return sd_bus_error_set_errnof(error
, EBUSY
, "Image '%s' is not detached, refusing.", image
->path
);
473 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
474 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
476 r
= safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS
, &child
);
478 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
480 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
482 r
= image_remove(image
);
484 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
491 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
493 r
= operation_new(m
, child
, message
, errno_pipe_fd
[0], NULL
);
498 errno_pipe_fd
[0] = -1;
503 static int bus_image_method_remove(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
504 return bus_image_common_remove(NULL
, message
, NULL
, userdata
, error
);
507 /* Given two PortableChange arrays, return a new array that has all elements of the first that are
508 * not also present in the second, comparing the basename of the path values. */
509 static int normalize_portable_changes(
510 const PortableChange
*changes_attached
,
511 size_t n_changes_attached
,
512 const PortableChange
*changes_detached
,
513 size_t n_changes_detached
,
514 PortableChange
**ret_changes
,
515 size_t *ret_n_changes
) {
517 PortableChange
*changes
= NULL
;
518 size_t n_changes
= 0;
521 assert(ret_n_changes
);
524 if (n_changes_detached
== 0)
525 return 0; /* Nothing to do */
527 changes
= new0(PortableChange
, n_changes_attached
+ n_changes_detached
);
531 /* Corner case: only detached, nothing attached */
532 if (n_changes_attached
== 0) {
533 memcpy(changes
, changes_detached
, sizeof(PortableChange
) * n_changes_detached
);
534 *ret_changes
= TAKE_PTR(changes
);
535 *ret_n_changes
= n_changes_detached
;
540 for (size_t i
= 0; i
< n_changes_detached
; ++i
) {
543 for (size_t j
= 0; j
< n_changes_attached
; ++j
)
544 if (streq(basename(changes_detached
[i
].path
), basename(changes_attached
[j
].path
))) {
550 _cleanup_free_
char *path
= NULL
, *source
= NULL
;
552 path
= strdup(changes_detached
[i
].path
);
558 if (changes_detached
[i
].source
) {
559 source
= strdup(changes_detached
[i
].source
);
566 changes
[n_changes
++] = (PortableChange
) {
567 .type_or_errno
= changes_detached
[i
].type_or_errno
,
568 .path
= TAKE_PTR(path
),
569 .source
= TAKE_PTR(source
),
574 *ret_n_changes
= n_changes
;
575 *ret_changes
= TAKE_PTR(changes
);
580 portable_changes_free(changes
, n_changes
);
584 int bus_image_common_reattach(
586 sd_bus_message
*message
,
587 const char *name_or_path
,
589 sd_bus_error
*error
) {
591 PortableChange
*changes_detached
= NULL
, *changes_attached
= NULL
, *changes_gone
= NULL
;
592 size_t n_changes_detached
= 0, n_changes_attached
= 0, n_changes_gone
= 0;
593 _cleanup_strv_free_
char **matches
= NULL
, **extension_images
= NULL
;
594 PortableFlags flags
= PORTABLE_REATTACH
;
595 const char *profile
, *copy_mode
;
599 assert(name_or_path
|| image
);
606 if (sd_bus_message_is_method_call(message
, NULL
, "ReattachImageWithExtensions") ||
607 sd_bus_message_is_method_call(message
, NULL
, "ReattachWithExtensions")) {
608 r
= sd_bus_message_read_strv(message
, &extension_images
);
613 r
= sd_bus_message_read_strv(message
, &matches
);
617 r
= sd_bus_message_read(message
, "s", &profile
);
621 if (sd_bus_message_is_method_call(message
, NULL
, "ReattachImageWithExtensions") ||
622 sd_bus_message_is_method_call(message
, NULL
, "ReattachWithExtensions")) {
623 uint64_t input_flags
= 0;
625 r
= sd_bus_message_read(message
, "st", ©_mode
, &input_flags
);
629 if ((input_flags
& ~_PORTABLE_MASK_PUBLIC
) != 0)
630 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
631 "Invalid 'flags' parameter '%" PRIu64
"'",
633 flags
|= input_flags
;
637 r
= sd_bus_message_read(message
, "bs", &runtime
, ©_mode
);
642 flags
|= PORTABLE_RUNTIME
;
645 if (streq(copy_mode
, "symlink"))
646 flags
|= PORTABLE_PREFER_SYMLINK
;
647 else if (streq(copy_mode
, "copy"))
648 flags
|= PORTABLE_PREFER_COPY
;
649 else if (!isempty(copy_mode
))
650 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown copy mode '%s'", copy_mode
);
652 r
= bus_image_acquire(m
,
656 BUS_IMAGE_AUTHENTICATE_ALL
,
657 "org.freedesktop.portable1.attach-images",
662 if (r
== 0) /* Will call us back */
666 sd_bus_message_get_bus(message
),
677 sd_bus_message_get_bus(message
),
689 /* We want to return the list of units really removed by the detach,
690 * and not added again by the attach */
691 r
= normalize_portable_changes(changes_attached
, n_changes_attached
,
692 changes_detached
, n_changes_detached
,
693 &changes_gone
, &n_changes_gone
);
697 /* First, return the units that are gone (so that the caller can stop them)
698 * Then, return the units that are changed/added (so that the caller can
699 * start/restart/enable them) */
700 r
= reply_portable_changes_pair(message
,
701 changes_gone
, n_changes_gone
,
702 changes_attached
, n_changes_attached
);
707 portable_changes_free(changes_detached
, n_changes_detached
);
708 portable_changes_free(changes_attached
, n_changes_attached
);
709 portable_changes_free(changes_gone
, n_changes_gone
);
713 static int bus_image_method_reattach(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
714 return bus_image_common_reattach(NULL
, message
, NULL
, userdata
, error
);
717 int bus_image_common_mark_read_only(
719 sd_bus_message
*message
,
720 const char *name_or_path
,
722 sd_bus_error
*error
) {
727 assert(name_or_path
|| image
);
734 r
= sd_bus_message_read(message
, "b", &read_only
);
738 r
= bus_image_acquire(m
,
742 BUS_IMAGE_AUTHENTICATE_ALL
,
743 "org.freedesktop.portable1.manage-images",
749 return 1; /* Will call us back */
751 r
= image_read_only(image
, read_only
);
755 return sd_bus_reply_method_return(message
, NULL
);
758 static int bus_image_method_mark_read_only(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
759 return bus_image_common_mark_read_only(NULL
, message
, NULL
, userdata
, error
);
762 int bus_image_common_set_limit(
764 sd_bus_message
*message
,
765 const char *name_or_path
,
767 sd_bus_error
*error
) {
773 assert(name_or_path
|| image
);
780 r
= sd_bus_message_read(message
, "t", &limit
);
783 if (!FILE_SIZE_VALID_OR_INFINITY(limit
))
784 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
786 r
= bus_image_acquire(m
,
790 BUS_IMAGE_AUTHENTICATE_ALL
,
791 "org.freedesktop.portable1.manage-images",
797 return 1; /* Will call us back */
799 r
= image_set_limit(image
, limit
);
803 return sd_bus_reply_method_return(message
, NULL
);
806 static int bus_image_method_set_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
807 return bus_image_common_set_limit(NULL
, message
, NULL
, userdata
, error
);
810 const sd_bus_vtable image_vtable
[] = {
811 SD_BUS_VTABLE_START(0),
812 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Image
, name
), 0),
813 SD_BUS_PROPERTY("Path", "s", NULL
, offsetof(Image
, path
), 0),
814 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Image
, type
), 0),
815 SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool
, offsetof(Image
, read_only
), 0),
816 SD_BUS_PROPERTY("CreationTimestamp", "t", NULL
, offsetof(Image
, crtime
), 0),
817 SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL
, offsetof(Image
, mtime
), 0),
818 SD_BUS_PROPERTY("Usage", "t", NULL
, offsetof(Image
, usage
), 0),
819 SD_BUS_PROPERTY("Limit", "t", NULL
, offsetof(Image
, limit
), 0),
820 SD_BUS_PROPERTY("UsageExclusive", "t", NULL
, offsetof(Image
, usage_exclusive
), 0),
821 SD_BUS_PROPERTY("LimitExclusive", "t", NULL
, offsetof(Image
, limit_exclusive
), 0),
822 SD_BUS_METHOD_WITH_ARGS("GetOSRelease",
824 SD_BUS_RESULT("a{ss}", os_release
),
825 bus_image_method_get_os_release
,
826 SD_BUS_VTABLE_UNPRIVILEGED
),
827 SD_BUS_METHOD_WITH_ARGS("GetMetadata",
828 SD_BUS_ARGS("as", matches
),
829 SD_BUS_RESULT("s", image
,
832 bus_image_method_get_metadata
,
833 SD_BUS_VTABLE_UNPRIVILEGED
),
834 SD_BUS_METHOD_WITH_ARGS("GetMetadataWithExtensions",
835 SD_BUS_ARGS("as", extensions
,
838 SD_BUS_RESULT("s", image
,
841 bus_image_method_get_metadata
,
842 SD_BUS_VTABLE_UNPRIVILEGED
),
843 SD_BUS_METHOD_WITH_ARGS("GetState",
845 SD_BUS_RESULT("s", state
),
846 bus_image_method_get_state
,
847 SD_BUS_VTABLE_UNPRIVILEGED
),
848 SD_BUS_METHOD_WITH_ARGS("Attach",
849 SD_BUS_ARGS("as", matches
,
853 SD_BUS_RESULT("a(sss)", changes
),
854 bus_image_method_attach
,
855 SD_BUS_VTABLE_UNPRIVILEGED
),
856 SD_BUS_METHOD_WITH_ARGS("AttachWithExtensions",
857 SD_BUS_ARGS("as", extensions
,
862 SD_BUS_RESULT("a(sss)", changes
),
863 bus_image_method_attach
,
864 SD_BUS_VTABLE_UNPRIVILEGED
),
865 SD_BUS_METHOD_WITH_ARGS("Detach",
866 SD_BUS_ARGS("b", runtime
),
867 SD_BUS_RESULT("a(sss)", changes
),
868 bus_image_method_detach
,
869 SD_BUS_VTABLE_UNPRIVILEGED
),
870 SD_BUS_METHOD_WITH_ARGS("DetachWithExtensions",
871 SD_BUS_ARGS("as", extensions
,
873 SD_BUS_RESULT("a(sss)", changes
),
874 bus_image_method_detach
,
875 SD_BUS_VTABLE_UNPRIVILEGED
),
876 SD_BUS_METHOD_WITH_ARGS("Reattach",
877 SD_BUS_ARGS("as", matches
,
881 SD_BUS_RESULT("a(sss)", changes_removed
,
882 "a(sss)", changes_updated
),
883 bus_image_method_reattach
,
884 SD_BUS_VTABLE_UNPRIVILEGED
),
885 SD_BUS_METHOD_WITH_ARGS("ReattacheWithExtensions",
886 SD_BUS_ARGS("as", extensions
,
891 SD_BUS_RESULT("a(sss)", changes_removed
,
892 "a(sss)", changes_updated
),
893 bus_image_method_reattach
,
894 SD_BUS_VTABLE_UNPRIVILEGED
),
895 SD_BUS_METHOD_WITH_ARGS("Remove",
898 bus_image_method_remove
,
899 SD_BUS_VTABLE_UNPRIVILEGED
),
900 SD_BUS_METHOD_WITH_ARGS("MarkReadOnly",
901 SD_BUS_ARGS("b", read_only
),
903 bus_image_method_mark_read_only
,
904 SD_BUS_VTABLE_UNPRIVILEGED
),
905 SD_BUS_METHOD_WITH_ARGS("SetLimit",
906 SD_BUS_ARGS("t", limit
),
908 bus_image_method_set_limit
,
909 SD_BUS_VTABLE_UNPRIVILEGED
),
913 int bus_image_path(Image
*image
, char **ret
) {
917 if (!image
->discoverable
)
920 return sd_bus_path_encode("/org/freedesktop/portable1/image", image
->name
, ret
);
923 int bus_image_acquire(
925 sd_bus_message
*message
,
926 const char *name_or_path
,
928 ImageAcquireMode mode
,
929 const char *polkit_action
,
931 sd_bus_error
*error
) {
933 _cleanup_(image_unrefp
) Image
*loaded
= NULL
;
939 assert(name_or_path
|| image
);
941 assert(mode
< _BUS_IMAGE_ACQUIRE_MODE_MAX
);
942 assert(polkit_action
|| mode
== BUS_IMAGE_REFUSE_BY_PATH
);
945 /* Acquires an 'Image' object if not acquired yet, and enforces necessary authentication while doing so. */
947 if (mode
== BUS_IMAGE_AUTHENTICATE_ALL
) {
948 r
= bus_verify_polkit_async(
959 if (r
== 0) { /* Will call us back */
965 /* Already passed in? */
971 /* Let's see if this image is already cached? */
972 cached
= manager_image_cache_get(m
, name_or_path
);
978 if (image_name_is_valid(name_or_path
)) {
980 /* If it's a short name, let's search for it */
981 r
= image_find(IMAGE_PORTABLE
, name_or_path
, NULL
, &loaded
);
983 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE
, "No image '%s' found.", name_or_path
);
985 /* other errors are handled below… */
987 /* Don't accept path if this is always forbidden */
988 if (mode
== BUS_IMAGE_REFUSE_BY_PATH
)
989 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Expected image name, not path in place of '%s'.", name_or_path
);
991 if (!path_is_absolute(name_or_path
))
992 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
);
994 if (!path_is_normalized(name_or_path
))
995 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Image path '%s' is not normalized.", name_or_path
);
997 if (mode
== BUS_IMAGE_AUTHENTICATE_BY_PATH
) {
998 r
= bus_verify_polkit_async(
1005 &m
->polkit_registry
,
1009 if (r
== 0) { /* Will call us back */
1015 r
= image_from_path(name_or_path
, &loaded
);
1017 if (r
== -EMEDIUMTYPE
) {
1018 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
);
1024 /* Add what we just loaded to the cache. This has as side-effect that the object stays in memory until the
1025 * cache is purged again, i.e. at least for the current event loop iteration, which is all we need, and which
1026 * means we don't actually need to ref the return object. */
1027 r
= manager_image_cache_add(m
, loaded
);
1035 int bus_image_object_find(
1038 const char *interface
,
1041 sd_bus_error
*error
) {
1043 _cleanup_free_
char *e
= NULL
;
1044 Manager
*m
= userdata
;
1045 Image
*image
= NULL
;
1053 r
= sd_bus_path_decode(path
, "/org/freedesktop/portable1/image", &e
);
1059 r
= bus_image_acquire(m
, sd_bus_get_current_message(bus
), e
, NULL
, BUS_IMAGE_REFUSE_BY_PATH
, NULL
, &image
, error
);
1073 int bus_image_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1074 _cleanup_hashmap_free_ Hashmap
*images
= NULL
;
1075 _cleanup_strv_free_
char **l
= NULL
;
1076 Manager
*m
= userdata
;
1085 images
= hashmap_new(&image_hash_ops
);
1089 r
= manager_image_cache_discover(m
, images
, error
);
1093 HASHMAP_FOREACH(image
, images
) {
1096 r
= bus_image_path(image
, &p
);
1100 if (!GREEDY_REALLOC(l
, n
+2)) {
1109 *nodes
= TAKE_PTR(l
);
1114 const BusObjectImplementation image_object
= {
1115 "/org/freedesktop/portable1/image",
1116 "org.freedesktop.portable1.Image",
1117 .fallback_vtables
= BUS_FALLBACK_VTABLES({image_vtable
, bus_image_object_find
}),
1118 .node_enumerator
= bus_image_node_enumerator
,