1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
4 #include "btrfs-util.h"
5 #include "bus-common-errors.h"
6 #include "bus-object.h"
7 #include "bus-polkit.h"
8 #include "discover-image.h"
11 #include "missing_capability.h"
13 #include "portabled-bus.h"
14 #include "portabled-image-bus.h"
15 #include "portabled-image.h"
16 #include "portabled.h"
18 #include "user-util.h"
20 static int property_get_pool_path(
23 const char *interface
,
25 sd_bus_message
*reply
,
27 sd_bus_error
*error
) {
32 return sd_bus_message_append(reply
, "s", "/var/lib/portables");
35 static int property_get_pool_usage(
38 const char *interface
,
40 sd_bus_message
*reply
,
42 sd_bus_error
*error
) {
44 _cleanup_close_
int fd
= -1;
45 uint64_t usage
= UINT64_MAX
;
50 fd
= open("/var/lib/portables", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
54 if (btrfs_subvol_get_subtree_quota_fd(fd
, 0, &q
) >= 0)
58 return sd_bus_message_append(reply
, "t", usage
);
61 static int property_get_pool_limit(
64 const char *interface
,
66 sd_bus_message
*reply
,
68 sd_bus_error
*error
) {
70 _cleanup_close_
int fd
= -1;
71 uint64_t size
= UINT64_MAX
;
76 fd
= open("/var/lib/portables", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
80 if (btrfs_subvol_get_subtree_quota_fd(fd
, 0, &q
) >= 0)
81 size
= q
.referenced_max
;
84 return sd_bus_message_append(reply
, "t", size
);
87 static int property_get_profiles(
90 const char *interface
,
92 sd_bus_message
*reply
,
94 sd_bus_error
*error
) {
96 _cleanup_strv_free_
char **l
= NULL
;
102 r
= portable_get_profiles(&l
);
106 return sd_bus_message_append_strv(reply
, l
);
109 static int method_get_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
110 _cleanup_free_
char *p
= NULL
;
111 Manager
*m
= userdata
;
119 r
= sd_bus_message_read(message
, "s", &name
);
123 r
= bus_image_acquire(m
, message
, name
, NULL
, BUS_IMAGE_REFUSE_BY_PATH
, NULL
, &image
, error
);
127 r
= bus_image_path(image
, &p
);
131 return sd_bus_reply_method_return(message
, "o", p
);
134 static int method_list_images(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
135 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
136 _cleanup_hashmap_free_ Hashmap
*images
= NULL
;
137 Manager
*m
= userdata
;
144 images
= hashmap_new(&image_hash_ops
);
148 r
= manager_image_cache_discover(m
, images
, error
);
152 r
= sd_bus_message_new_method_return(message
, &reply
);
156 r
= sd_bus_message_open_container(reply
, 'a', "(ssbtttso)");
160 HASHMAP_FOREACH(image
, images
) {
161 _cleanup_(sd_bus_error_free
) sd_bus_error error_state
= SD_BUS_ERROR_NULL
;
162 PortableState state
= _PORTABLE_STATE_INVALID
;
163 _cleanup_free_
char *p
= NULL
;
165 r
= bus_image_path(image
, &p
);
169 r
= portable_get_state(
170 sd_bus_message_get_bus(message
),
177 log_debug_errno(r
, "Failed to get state of image '%s', ignoring: %s",
178 image
->path
, bus_error_message(&error_state
, r
));
180 r
= sd_bus_message_append(reply
, "(ssbtttso)",
182 image_type_to_string(image
->type
),
187 portable_state_to_string(state
),
193 r
= sd_bus_message_close_container(reply
);
197 return sd_bus_send(NULL
, reply
, NULL
);
200 static int redirect_method_to_image(
202 sd_bus_message
*message
,
204 int (*method
)(Manager
*m
, sd_bus_message
*message
, const char *name_or_path
, Image
*image
, sd_bus_error
* error
)) {
206 const char *name_or_path
;
213 r
= sd_bus_message_read(message
, "s", &name_or_path
);
217 return method(m
, message
, name_or_path
, NULL
, error
);
220 static int method_get_image_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
221 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_get_os_release
);
224 static int method_get_image_metadata(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
225 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_get_metadata
);
228 static int method_get_image_state(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
229 _cleanup_strv_free_
char **extension_images
= NULL
;
230 const char *name_or_path
;
236 r
= sd_bus_message_read(message
, "s", &name_or_path
);
240 if (sd_bus_message_is_method_call(message
, NULL
, "GetImageStateWithExtensions")) {
241 uint64_t input_flags
= 0;
243 r
= sd_bus_message_read_strv(message
, &extension_images
);
247 r
= sd_bus_message_read(message
, "t", &input_flags
);
251 /* No flags are supported by this method for now. */
252 if (input_flags
!= 0)
253 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
254 "Invalid 'flags' parameter '%" PRIu64
"'",
258 r
= portable_get_state(
259 sd_bus_message_get_bus(message
),
268 return sd_bus_reply_method_return(message
, "s", portable_state_to_string(state
));
271 static int method_attach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
272 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_attach
);
275 static int method_detach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
276 _cleanup_strv_free_
char **extension_images
= NULL
;
277 PortableChange
*changes
= NULL
;
278 PortableFlags flags
= 0;
279 Manager
*m
= userdata
;
280 size_t n_changes
= 0;
281 const char *name_or_path
;
287 /* Note that we do not redirect detaching to the image object here, because we want to allow that users can
288 * detach already deleted images too, in case the user already deleted an image before properly detaching
291 r
= sd_bus_message_read(message
, "s", &name_or_path
);
295 if (sd_bus_message_is_method_call(message
, NULL
, "DetachImageWithExtensions")) {
296 uint64_t input_flags
= 0;
298 r
= sd_bus_message_read_strv(message
, &extension_images
);
302 r
= sd_bus_message_read(message
, "t", &input_flags
);
306 if ((input_flags
& ~_PORTABLE_MASK_PUBLIC
) != 0)
307 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
308 "Invalid 'flags' parameter '%" PRIu64
"'",
310 flags
|= input_flags
;
314 r
= sd_bus_message_read(message
, "b", &runtime
);
319 flags
|= PORTABLE_RUNTIME
;
322 r
= bus_verify_polkit_async(
325 "org.freedesktop.portable1.attach-images",
334 return 1; /* Will call us back */
337 sd_bus_message_get_bus(message
),
347 r
= reply_portable_changes(message
, changes
, n_changes
);
350 portable_changes_free(changes
, n_changes
);
354 static int method_reattach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
355 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_reattach
);
358 static int method_remove_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
359 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_remove
);
362 static int method_mark_image_read_only(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
363 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_mark_read_only
);
366 static int method_set_image_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
367 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_set_limit
);
370 static int method_set_pool_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
371 Manager
*m
= userdata
;
377 r
= sd_bus_message_read(message
, "t", &limit
);
380 if (!FILE_SIZE_VALID_OR_INFINITY(limit
))
381 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
383 r
= bus_verify_polkit_async(
386 "org.freedesktop.portable1.manage-images",
395 return 1; /* Will call us back */
397 (void) btrfs_qgroup_set_limit("/var/lib/portables", 0, limit
);
399 r
= btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit
);
401 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Quota is only supported on btrfs.");
403 return sd_bus_error_set_errnof(error
, r
, "Failed to adjust quota limit: %m");
405 return sd_bus_reply_method_return(message
, NULL
);
408 const sd_bus_vtable manager_vtable
[] = {
409 SD_BUS_VTABLE_START(0),
410 SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path
, 0, 0),
411 SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage
, 0, 0),
412 SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit
, 0, 0),
413 SD_BUS_PROPERTY("Profiles", "as", property_get_profiles
, 0, 0),
414 SD_BUS_METHOD_WITH_ARGS("GetImage",
415 SD_BUS_ARGS("s", image
),
416 SD_BUS_RESULT("o", object
),
418 SD_BUS_VTABLE_UNPRIVILEGED
),
419 SD_BUS_METHOD_WITH_ARGS("ListImages",
421 SD_BUS_RESULT("a(ssbtttso)", images
),
423 SD_BUS_VTABLE_UNPRIVILEGED
),
424 SD_BUS_METHOD_WITH_ARGS("GetImageOSRelease",
425 SD_BUS_ARGS("s", image
),
426 SD_BUS_RESULT("a{ss}", os_release
),
427 method_get_image_os_release
,
428 SD_BUS_VTABLE_UNPRIVILEGED
),
429 SD_BUS_METHOD_WITH_ARGS("GetImageMetadata",
430 SD_BUS_ARGS("s", image
,
432 SD_BUS_RESULT("s", image
,
435 method_get_image_metadata
,
436 SD_BUS_VTABLE_UNPRIVILEGED
),
437 SD_BUS_METHOD_WITH_ARGS("GetImageMetadataWithExtensions",
438 SD_BUS_ARGS("s", image
,
442 SD_BUS_RESULT("s", image
,
445 method_get_image_metadata
,
446 SD_BUS_VTABLE_UNPRIVILEGED
),
447 SD_BUS_METHOD_WITH_ARGS("GetImageState",
448 SD_BUS_ARGS("s", image
),
449 SD_BUS_RESULT("s", state
),
450 method_get_image_state
,
451 SD_BUS_VTABLE_UNPRIVILEGED
),
452 SD_BUS_METHOD_WITH_ARGS("GetImageStateWithExtensions",
453 SD_BUS_ARGS("s", image
,
456 SD_BUS_RESULT("s", state
),
457 method_get_image_state
,
458 SD_BUS_VTABLE_UNPRIVILEGED
),
459 SD_BUS_METHOD_WITH_ARGS("AttachImage",
460 SD_BUS_ARGS("s", image
,
465 SD_BUS_RESULT("a(sss)", changes
),
467 SD_BUS_VTABLE_UNPRIVILEGED
),
468 SD_BUS_METHOD_WITH_ARGS("AttachImageWithExtensions",
469 SD_BUS_ARGS("s", image
,
475 SD_BUS_RESULT("a(sss)", changes
),
477 SD_BUS_VTABLE_UNPRIVILEGED
),
478 SD_BUS_METHOD_WITH_ARGS("DetachImage",
479 SD_BUS_ARGS("s", image
,
481 SD_BUS_RESULT("a(sss)", changes
),
483 SD_BUS_VTABLE_UNPRIVILEGED
),
484 SD_BUS_METHOD_WITH_ARGS("DetachImageWithExtensions",
485 SD_BUS_ARGS("s", image
,
488 SD_BUS_RESULT("a(sss)", changes
),
490 SD_BUS_VTABLE_UNPRIVILEGED
),
491 SD_BUS_METHOD_WITH_ARGS("ReattachImage",
492 SD_BUS_ARGS("s", image
,
497 SD_BUS_RESULT("a(sss)", changes_removed
,
498 "a(sss)", changes_updated
),
499 method_reattach_image
,
500 SD_BUS_VTABLE_UNPRIVILEGED
),
501 SD_BUS_METHOD_WITH_ARGS("ReattachImageWithExtensions",
502 SD_BUS_ARGS("s", image
,
508 SD_BUS_RESULT("a(sss)", changes_removed
,
509 "a(sss)", changes_updated
),
510 method_reattach_image
,
511 SD_BUS_VTABLE_UNPRIVILEGED
),
512 SD_BUS_METHOD_WITH_ARGS("RemoveImage",
513 SD_BUS_ARGS("s", image
),
516 SD_BUS_VTABLE_UNPRIVILEGED
),
517 SD_BUS_METHOD_WITH_ARGS("MarkImageReadOnly",
518 SD_BUS_ARGS("s", image
,
521 method_mark_image_read_only
,
522 SD_BUS_VTABLE_UNPRIVILEGED
),
523 SD_BUS_METHOD_WITH_ARGS("SetImageLimit",
524 SD_BUS_ARGS("s", image
,
527 method_set_image_limit
,
528 SD_BUS_VTABLE_UNPRIVILEGED
),
529 SD_BUS_METHOD_WITH_ARGS("SetPoolLimit",
530 SD_BUS_ARGS("t", limit
),
532 method_set_pool_limit
,
533 SD_BUS_VTABLE_UNPRIVILEGED
),
537 const BusObjectImplementation manager_object
= {
538 "/org/freedesktop/portable1",
539 "org.freedesktop.portable1.Manager",
540 .vtables
= BUS_VTABLES(manager_vtable
),
541 .children
= BUS_IMPLEMENTATIONS(&image_object
),
544 static int reply_portable_compose_message(sd_bus_message
*reply
, const PortableChange
*changes
, size_t n_changes
) {
549 assert(changes
|| n_changes
== 0);
551 r
= sd_bus_message_open_container(reply
, 'a', "(sss)");
555 for (i
= 0; i
< n_changes
; i
++) {
556 if (changes
[i
].type_or_errno
< 0)
559 r
= sd_bus_message_append(reply
, "(sss)",
560 portable_change_type_to_string(changes
[i
].type_or_errno
),
567 r
= sd_bus_message_close_container(reply
);
574 int reply_portable_changes(sd_bus_message
*m
, const PortableChange
*changes
, size_t n_changes
) {
575 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
580 r
= sd_bus_message_new_method_return(m
, &reply
);
584 r
= reply_portable_compose_message(reply
, changes
, n_changes
);
588 return sd_bus_send(NULL
, reply
, NULL
);
591 int reply_portable_changes_pair(
593 const PortableChange
*changes_first
,
594 size_t n_changes_first
,
595 const PortableChange
*changes_second
,
596 size_t n_changes_second
) {
598 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
603 r
= sd_bus_message_new_method_return(m
, &reply
);
607 r
= reply_portable_compose_message(reply
, changes_first
, n_changes_first
);
611 r
= reply_portable_compose_message(reply
, changes_second
, n_changes_second
);
615 return sd_bus_send(NULL
, reply
, NULL
);