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
),
176 log_debug_errno(r
, "Failed to get state of image '%s', ignoring: %s",
177 image
->path
, bus_error_message(&error_state
, r
));
179 r
= sd_bus_message_append(reply
, "(ssbtttso)",
181 image_type_to_string(image
->type
),
186 portable_state_to_string(state
),
192 r
= sd_bus_message_close_container(reply
);
196 return sd_bus_send(NULL
, reply
, NULL
);
199 static int redirect_method_to_image(
201 sd_bus_message
*message
,
203 int (*method
)(Manager
*m
, sd_bus_message
*message
, const char *name_or_path
, Image
*image
, sd_bus_error
* error
)) {
205 const char *name_or_path
;
212 r
= sd_bus_message_read(message
, "s", &name_or_path
);
216 return method(m
, message
, name_or_path
, NULL
, error
);
219 static int method_get_image_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
220 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_get_os_release
);
223 static int method_get_image_metadata(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
224 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_get_metadata
);
227 static int method_get_image_state(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
228 const char *name_or_path
;
234 r
= sd_bus_message_read(message
, "s", &name_or_path
);
238 r
= portable_get_state(
239 sd_bus_message_get_bus(message
),
247 return sd_bus_reply_method_return(message
, "s", portable_state_to_string(state
));
250 static int method_attach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
251 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_attach
);
254 static int method_detach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
255 _cleanup_strv_free_
char **extension_images
= NULL
;
256 PortableChange
*changes
= NULL
;
257 PortableFlags flags
= 0;
258 Manager
*m
= userdata
;
259 size_t n_changes
= 0;
260 const char *name_or_path
;
266 /* Note that we do not redirect detaching to the image object here, because we want to allow that users can
267 * detach already deleted images too, in case the user already deleted an image before properly detaching
270 r
= sd_bus_message_read(message
, "s", &name_or_path
);
274 if (sd_bus_message_is_method_call(message
, NULL
, "DetachImageWithExtensions")) {
275 uint64_t input_flags
= 0;
277 r
= sd_bus_message_read_strv(message
, &extension_images
);
281 r
= sd_bus_message_read(message
, "t", &input_flags
);
285 if ((input_flags
& ~_PORTABLE_MASK_PUBLIC
) != 0)
286 return sd_bus_reply_method_errorf(message
, SD_BUS_ERROR_INVALID_ARGS
,
287 "Invalid 'flags' parameter '%" PRIu64
"'",
289 flags
|= input_flags
;
293 r
= sd_bus_message_read(message
, "b", &runtime
);
298 flags
|= PORTABLE_RUNTIME
;
301 r
= bus_verify_polkit_async(
304 "org.freedesktop.portable1.attach-images",
313 return 1; /* Will call us back */
316 sd_bus_message_get_bus(message
),
326 r
= reply_portable_changes(message
, changes
, n_changes
);
329 portable_changes_free(changes
, n_changes
);
333 static int method_reattach_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
334 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_reattach
);
337 static int method_remove_image(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
338 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_remove
);
341 static int method_mark_image_read_only(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
342 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_mark_read_only
);
345 static int method_set_image_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
346 return redirect_method_to_image(userdata
, message
, error
, bus_image_common_set_limit
);
349 static int method_set_pool_limit(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
350 Manager
*m
= userdata
;
356 r
= sd_bus_message_read(message
, "t", &limit
);
359 if (!FILE_SIZE_VALID_OR_INFINITY(limit
))
360 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "New limit out of range");
362 r
= bus_verify_polkit_async(
365 "org.freedesktop.portable1.manage-images",
374 return 1; /* Will call us back */
376 (void) btrfs_qgroup_set_limit("/var/lib/portables", 0, limit
);
378 r
= btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit
);
380 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Quota is only supported on btrfs.");
382 return sd_bus_error_set_errnof(error
, r
, "Failed to adjust quota limit: %m");
384 return sd_bus_reply_method_return(message
, NULL
);
387 const sd_bus_vtable manager_vtable
[] = {
388 SD_BUS_VTABLE_START(0),
389 SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path
, 0, 0),
390 SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage
, 0, 0),
391 SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit
, 0, 0),
392 SD_BUS_PROPERTY("Profiles", "as", property_get_profiles
, 0, 0),
393 SD_BUS_METHOD_WITH_ARGS("GetImage",
394 SD_BUS_ARGS("s", image
),
395 SD_BUS_RESULT("o", object
),
397 SD_BUS_VTABLE_UNPRIVILEGED
),
398 SD_BUS_METHOD_WITH_ARGS("ListImages",
400 SD_BUS_RESULT("a(ssbtttso)", images
),
402 SD_BUS_VTABLE_UNPRIVILEGED
),
403 SD_BUS_METHOD_WITH_ARGS("GetImageOSRelease",
404 SD_BUS_ARGS("s", image
),
405 SD_BUS_RESULT("a{ss}", os_release
),
406 method_get_image_os_release
,
407 SD_BUS_VTABLE_UNPRIVILEGED
),
408 SD_BUS_METHOD_WITH_ARGS("GetImageMetadata",
409 SD_BUS_ARGS("s", image
,
411 SD_BUS_RESULT("s", image
,
414 method_get_image_metadata
,
415 SD_BUS_VTABLE_UNPRIVILEGED
),
416 SD_BUS_METHOD_WITH_ARGS("GetImageMetadataWithExtensions",
417 SD_BUS_ARGS("s", image
,
421 SD_BUS_RESULT("s", image
,
423 "a{say}", extensions
,
425 method_get_image_metadata
,
426 SD_BUS_VTABLE_UNPRIVILEGED
),
427 SD_BUS_METHOD_WITH_ARGS("GetImageState",
428 SD_BUS_ARGS("s", image
),
429 SD_BUS_RESULT("s", state
),
430 method_get_image_state
,
431 SD_BUS_VTABLE_UNPRIVILEGED
),
432 SD_BUS_METHOD_WITH_ARGS("AttachImage",
433 SD_BUS_ARGS("s", image
,
438 SD_BUS_RESULT("a(sss)", changes
),
440 SD_BUS_VTABLE_UNPRIVILEGED
),
441 SD_BUS_METHOD_WITH_ARGS("AttachImageWithExtensions",
442 SD_BUS_ARGS("s", image
,
448 SD_BUS_RESULT("a(sss)", changes
),
450 SD_BUS_VTABLE_UNPRIVILEGED
),
451 SD_BUS_METHOD_WITH_ARGS("DetachImage",
452 SD_BUS_ARGS("s", image
,
454 SD_BUS_RESULT("a(sss)", changes
),
456 SD_BUS_VTABLE_UNPRIVILEGED
),
457 SD_BUS_METHOD_WITH_ARGS("DetachImageWithExtensions",
458 SD_BUS_ARGS("s", image
,
461 SD_BUS_RESULT("a(sss)", changes
),
463 SD_BUS_VTABLE_UNPRIVILEGED
),
464 SD_BUS_METHOD_WITH_ARGS("ReattachImage",
465 SD_BUS_ARGS("s", image
,
470 SD_BUS_RESULT("a(sss)", changes_removed
,
471 "a(sss)", changes_updated
),
472 method_reattach_image
,
473 SD_BUS_VTABLE_UNPRIVILEGED
),
474 SD_BUS_METHOD_WITH_ARGS("ReattachImageWithExtensions",
475 SD_BUS_ARGS("s", image
,
481 SD_BUS_RESULT("a(sss)", changes_removed
,
482 "a(sss)", changes_updated
),
483 method_reattach_image
,
484 SD_BUS_VTABLE_UNPRIVILEGED
),
485 SD_BUS_METHOD_WITH_ARGS("RemoveImage",
486 SD_BUS_ARGS("s", image
),
489 SD_BUS_VTABLE_UNPRIVILEGED
),
490 SD_BUS_METHOD_WITH_ARGS("MarkImageReadOnly",
491 SD_BUS_ARGS("s", image
,
494 method_mark_image_read_only
,
495 SD_BUS_VTABLE_UNPRIVILEGED
),
496 SD_BUS_METHOD_WITH_ARGS("SetImageLimit",
497 SD_BUS_ARGS("s", image
,
500 method_set_image_limit
,
501 SD_BUS_VTABLE_UNPRIVILEGED
),
502 SD_BUS_METHOD_WITH_ARGS("SetPoolLimit",
503 SD_BUS_ARGS("t", limit
),
505 method_set_pool_limit
,
506 SD_BUS_VTABLE_UNPRIVILEGED
),
510 const BusObjectImplementation manager_object
= {
511 "/org/freedesktop/portable1",
512 "org.freedesktop.portable1.Manager",
513 .vtables
= BUS_VTABLES(manager_vtable
),
514 .children
= BUS_IMPLEMENTATIONS(&image_object
),
517 static int reply_portable_compose_message(sd_bus_message
*reply
, const PortableChange
*changes
, size_t n_changes
) {
522 assert(changes
|| n_changes
== 0);
524 r
= sd_bus_message_open_container(reply
, 'a', "(sss)");
528 for (i
= 0; i
< n_changes
; i
++) {
529 if (changes
[i
].type_or_errno
< 0)
532 r
= sd_bus_message_append(reply
, "(sss)",
533 portable_change_type_to_string(changes
[i
].type_or_errno
),
540 r
= sd_bus_message_close_container(reply
);
547 int reply_portable_changes(sd_bus_message
*m
, const PortableChange
*changes
, size_t n_changes
) {
548 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
553 r
= sd_bus_message_new_method_return(m
, &reply
);
557 r
= reply_portable_compose_message(reply
, changes
, n_changes
);
561 return sd_bus_send(NULL
, reply
, NULL
);
564 int reply_portable_changes_pair(
566 const PortableChange
*changes_first
,
567 size_t n_changes_first
,
568 const PortableChange
*changes_second
,
569 size_t n_changes_second
) {
571 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
576 r
= sd_bus_message_new_method_return(m
, &reply
);
580 r
= reply_portable_compose_message(reply
, changes_first
, n_changes_first
);
584 r
= reply_portable_compose_message(reply
, changes_second
, n_changes_second
);
588 return sd_bus_send(NULL
, reply
, NULL
);