Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <sys/file.h>
4 : : #include <sys/mount.h>
5 : :
6 : : #include "alloc-util.h"
7 : : #include "bus-label.h"
8 : : #include "bus-util.h"
9 : : #include "copy.h"
10 : : #include "dissect-image.h"
11 : : #include "fd-util.h"
12 : : #include "fileio.h"
13 : : #include "fs-util.h"
14 : : #include "image-dbus.h"
15 : : #include "io-util.h"
16 : : #include "loop-util.h"
17 : : #include "machine-image.h"
18 : : #include "missing_capability.h"
19 : : #include "mount-util.h"
20 : : #include "process-util.h"
21 : : #include "raw-clone.h"
22 : : #include "strv.h"
23 : : #include "user-util.h"
24 : :
25 [ # # # # : 0 : static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
# # ]
26 : :
27 : 0 : int bus_image_method_remove(
28 : : sd_bus_message *message,
29 : : void *userdata,
30 : : sd_bus_error *error) {
31 : :
32 : 0 : _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
33 : 0 : Image *image = userdata;
34 : 0 : Manager *m = image->userdata;
35 : : pid_t child;
36 : : int r;
37 : :
38 [ # # ]: 0 : assert(message);
39 [ # # ]: 0 : assert(image);
40 : :
41 [ # # ]: 0 : if (m->n_operations >= OPERATIONS_MAX)
42 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
43 : :
44 : 0 : r = bus_verify_polkit_async(
45 : : message,
46 : : CAP_SYS_ADMIN,
47 : : "org.freedesktop.machine1.manage-images",
48 : : NULL,
49 : : false,
50 : : UID_INVALID,
51 : : &m->polkit_registry,
52 : : error);
53 [ # # ]: 0 : if (r < 0)
54 : 0 : return r;
55 [ # # ]: 0 : if (r == 0)
56 : 0 : return 1; /* Will call us back */
57 : :
58 [ # # ]: 0 : if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
59 : 0 : return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
60 : :
61 : 0 : r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child);
62 [ # # ]: 0 : if (r < 0)
63 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
64 [ # # ]: 0 : if (r == 0) {
65 : 0 : errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
66 : :
67 : 0 : r = image_remove(image);
68 [ # # ]: 0 : if (r < 0) {
69 : 0 : (void) write(errno_pipe_fd[1], &r, sizeof(r));
70 : 0 : _exit(EXIT_FAILURE);
71 : : }
72 : :
73 : 0 : _exit(EXIT_SUCCESS);
74 : : }
75 : :
76 : 0 : errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
77 : :
78 : 0 : r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
79 [ # # ]: 0 : if (r < 0) {
80 : 0 : (void) sigkill_wait(child);
81 : 0 : return r;
82 : : }
83 : :
84 : 0 : errno_pipe_fd[0] = -1;
85 : :
86 : 0 : return 1;
87 : : }
88 : :
89 : 0 : int bus_image_method_rename(
90 : : sd_bus_message *message,
91 : : void *userdata,
92 : : sd_bus_error *error) {
93 : :
94 : 0 : Image *image = userdata;
95 : 0 : Manager *m = image->userdata;
96 : : const char *new_name;
97 : : int r;
98 : :
99 [ # # ]: 0 : assert(message);
100 [ # # ]: 0 : assert(image);
101 : :
102 : 0 : r = sd_bus_message_read(message, "s", &new_name);
103 [ # # ]: 0 : if (r < 0)
104 : 0 : return r;
105 : :
106 [ # # ]: 0 : if (!image_name_is_valid(new_name))
107 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
108 : :
109 : 0 : r = bus_verify_polkit_async(
110 : : message,
111 : : CAP_SYS_ADMIN,
112 : : "org.freedesktop.machine1.manage-images",
113 : : NULL,
114 : : false,
115 : : UID_INVALID,
116 : : &m->polkit_registry,
117 : : error);
118 [ # # ]: 0 : if (r < 0)
119 : 0 : return r;
120 [ # # ]: 0 : if (r == 0)
121 : 0 : return 1; /* Will call us back */
122 : :
123 : 0 : r = image_rename(image, new_name);
124 [ # # ]: 0 : if (r < 0)
125 : 0 : return r;
126 : :
127 : 0 : return sd_bus_reply_method_return(message, NULL);
128 : : }
129 : :
130 : 0 : int bus_image_method_clone(
131 : : sd_bus_message *message,
132 : : void *userdata,
133 : : sd_bus_error *error) {
134 : :
135 : 0 : _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
136 : 0 : Image *image = userdata;
137 : 0 : Manager *m = image->userdata;
138 : : const char *new_name;
139 : : int r, read_only;
140 : : pid_t child;
141 : :
142 [ # # ]: 0 : assert(message);
143 [ # # ]: 0 : assert(image);
144 [ # # ]: 0 : assert(m);
145 : :
146 [ # # ]: 0 : if (m->n_operations >= OPERATIONS_MAX)
147 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
148 : :
149 : 0 : r = sd_bus_message_read(message, "sb", &new_name, &read_only);
150 [ # # ]: 0 : if (r < 0)
151 : 0 : return r;
152 : :
153 [ # # ]: 0 : if (!image_name_is_valid(new_name))
154 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
155 : :
156 : 0 : r = bus_verify_polkit_async(
157 : : message,
158 : : CAP_SYS_ADMIN,
159 : : "org.freedesktop.machine1.manage-images",
160 : : NULL,
161 : : false,
162 : : UID_INVALID,
163 : : &m->polkit_registry,
164 : : error);
165 [ # # ]: 0 : if (r < 0)
166 : 0 : return r;
167 [ # # ]: 0 : if (r == 0)
168 : 0 : return 1; /* Will call us back */
169 : :
170 [ # # ]: 0 : if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
171 : 0 : return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
172 : :
173 : 0 : r = safe_fork("(sd-imgclone)", FORK_RESET_SIGNALS, &child);
174 [ # # ]: 0 : if (r < 0)
175 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
176 [ # # ]: 0 : if (r == 0) {
177 : 0 : errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
178 : :
179 : 0 : r = image_clone(image, new_name, read_only);
180 [ # # ]: 0 : if (r < 0) {
181 : 0 : (void) write(errno_pipe_fd[1], &r, sizeof(r));
182 : 0 : _exit(EXIT_FAILURE);
183 : : }
184 : :
185 : 0 : _exit(EXIT_SUCCESS);
186 : : }
187 : :
188 : 0 : errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
189 : :
190 : 0 : r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);
191 [ # # ]: 0 : if (r < 0) {
192 : 0 : (void) sigkill_wait(child);
193 : 0 : return r;
194 : : }
195 : :
196 : 0 : errno_pipe_fd[0] = -1;
197 : :
198 : 0 : return 1;
199 : : }
200 : :
201 : 0 : int bus_image_method_mark_read_only(
202 : : sd_bus_message *message,
203 : : void *userdata,
204 : : sd_bus_error *error) {
205 : :
206 : 0 : Image *image = userdata;
207 : 0 : Manager *m = image->userdata;
208 : : int r, read_only;
209 : :
210 [ # # ]: 0 : assert(message);
211 : :
212 : 0 : r = sd_bus_message_read(message, "b", &read_only);
213 [ # # ]: 0 : if (r < 0)
214 : 0 : return r;
215 : :
216 : 0 : r = bus_verify_polkit_async(
217 : : message,
218 : : CAP_SYS_ADMIN,
219 : : "org.freedesktop.machine1.manage-images",
220 : : NULL,
221 : : false,
222 : : UID_INVALID,
223 : : &m->polkit_registry,
224 : : error);
225 [ # # ]: 0 : if (r < 0)
226 : 0 : return r;
227 [ # # ]: 0 : if (r == 0)
228 : 0 : return 1; /* Will call us back */
229 : :
230 : 0 : r = image_read_only(image, read_only);
231 [ # # ]: 0 : if (r < 0)
232 : 0 : return r;
233 : :
234 : 0 : return sd_bus_reply_method_return(message, NULL);
235 : : }
236 : :
237 : 0 : int bus_image_method_set_limit(
238 : : sd_bus_message *message,
239 : : void *userdata,
240 : : sd_bus_error *error) {
241 : :
242 : 0 : Image *image = userdata;
243 : 0 : Manager *m = image->userdata;
244 : : uint64_t limit;
245 : : int r;
246 : :
247 [ # # ]: 0 : assert(message);
248 : :
249 : 0 : r = sd_bus_message_read(message, "t", &limit);
250 [ # # ]: 0 : if (r < 0)
251 : 0 : return r;
252 [ # # ]: 0 : if (!FILE_SIZE_VALID_OR_INFINITY(limit))
253 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
254 : :
255 : 0 : r = bus_verify_polkit_async(
256 : : message,
257 : : CAP_SYS_ADMIN,
258 : : "org.freedesktop.machine1.manage-images",
259 : : NULL,
260 : : false,
261 : : UID_INVALID,
262 : : &m->polkit_registry,
263 : : error);
264 [ # # ]: 0 : if (r < 0)
265 : 0 : return r;
266 [ # # ]: 0 : if (r == 0)
267 : 0 : return 1; /* Will call us back */
268 : :
269 : 0 : r = image_set_limit(image, limit);
270 [ # # ]: 0 : if (r < 0)
271 : 0 : return r;
272 : :
273 : 0 : return sd_bus_reply_method_return(message, NULL);
274 : : }
275 : :
276 : 0 : int bus_image_method_get_hostname(
277 : : sd_bus_message *message,
278 : : void *userdata,
279 : : sd_bus_error *error) {
280 : :
281 : 0 : Image *image = userdata;
282 : : int r;
283 : :
284 [ # # ]: 0 : if (!image->metadata_valid) {
285 : 0 : r = image_read_metadata(image);
286 [ # # ]: 0 : if (r < 0)
287 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
288 : : }
289 : :
290 : 0 : return sd_bus_reply_method_return(message, "s", image->hostname);
291 : : }
292 : :
293 : 0 : int bus_image_method_get_machine_id(
294 : : sd_bus_message *message,
295 : : void *userdata,
296 : : sd_bus_error *error) {
297 : :
298 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
299 : 0 : Image *image = userdata;
300 : : int r;
301 : :
302 [ # # ]: 0 : if (!image->metadata_valid) {
303 : 0 : r = image_read_metadata(image);
304 [ # # ]: 0 : if (r < 0)
305 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
306 : : }
307 : :
308 : 0 : r = sd_bus_message_new_method_return(message, &reply);
309 [ # # ]: 0 : if (r < 0)
310 : 0 : return r;
311 : :
312 [ # # ]: 0 : if (sd_id128_is_null(image->machine_id)) /* Add an empty array if the ID is zero */
313 : 0 : r = sd_bus_message_append(reply, "ay", 0);
314 : : else
315 : 0 : r = sd_bus_message_append_array(reply, 'y', image->machine_id.bytes, 16);
316 [ # # ]: 0 : if (r < 0)
317 : 0 : return r;
318 : :
319 : 0 : return sd_bus_send(NULL, reply, NULL);
320 : : }
321 : :
322 : 0 : int bus_image_method_get_machine_info(
323 : : sd_bus_message *message,
324 : : void *userdata,
325 : : sd_bus_error *error) {
326 : :
327 : 0 : Image *image = userdata;
328 : : int r;
329 : :
330 [ # # ]: 0 : if (!image->metadata_valid) {
331 : 0 : r = image_read_metadata(image);
332 [ # # ]: 0 : if (r < 0)
333 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
334 : : }
335 : :
336 : 0 : return bus_reply_pair_array(message, image->machine_info);
337 : : }
338 : :
339 : 0 : int bus_image_method_get_os_release(
340 : : sd_bus_message *message,
341 : : void *userdata,
342 : : sd_bus_error *error) {
343 : :
344 : 0 : Image *image = userdata;
345 : : int r;
346 : :
347 [ # # ]: 0 : if (!image->metadata_valid) {
348 : 0 : r = image_read_metadata(image);
349 [ # # ]: 0 : if (r < 0)
350 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
351 : : }
352 : :
353 : 0 : return bus_reply_pair_array(message, image->os_release);
354 : : }
355 : :
356 : : const sd_bus_vtable image_vtable[] = {
357 : : SD_BUS_VTABLE_START(0),
358 : : SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
359 : : SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
360 : : SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0),
361 : : SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
362 : : SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
363 : : SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
364 : : SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
365 : : SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
366 : : SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
367 : : SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
368 : : SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
369 : : SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED),
370 : : SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
371 : : SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
372 : : SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
373 : : SD_BUS_METHOD("GetHostname", NULL, "s", bus_image_method_get_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
374 : : SD_BUS_METHOD("GetMachineID", NULL, "ay", bus_image_method_get_machine_id, SD_BUS_VTABLE_UNPRIVILEGED),
375 : : SD_BUS_METHOD("GetMachineInfo", NULL, "a{ss}", bus_image_method_get_machine_info, SD_BUS_VTABLE_UNPRIVILEGED),
376 : : SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
377 : : SD_BUS_VTABLE_END
378 : : };
379 : :
380 : 0 : static int image_flush_cache(sd_event_source *s, void *userdata) {
381 : 0 : Manager *m = userdata;
382 : :
383 [ # # ]: 0 : assert(s);
384 [ # # ]: 0 : assert(m);
385 : :
386 : 0 : hashmap_clear(m->image_cache);
387 : 0 : return 0;
388 : : }
389 : :
390 : 0 : int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
391 : 0 : _cleanup_free_ char *e = NULL;
392 : 0 : Manager *m = userdata;
393 : 0 : Image *image = NULL;
394 : : const char *p;
395 : : int r;
396 : :
397 [ # # ]: 0 : assert(bus);
398 [ # # ]: 0 : assert(path);
399 [ # # ]: 0 : assert(interface);
400 [ # # ]: 0 : assert(found);
401 : :
402 : 0 : p = startswith(path, "/org/freedesktop/machine1/image/");
403 [ # # ]: 0 : if (!p)
404 : 0 : return 0;
405 : :
406 : 0 : e = bus_label_unescape(p);
407 [ # # ]: 0 : if (!e)
408 : 0 : return -ENOMEM;
409 : :
410 : 0 : image = hashmap_get(m->image_cache, e);
411 [ # # ]: 0 : if (image) {
412 : 0 : *found = image;
413 : 0 : return 1;
414 : : }
415 : :
416 : 0 : r = hashmap_ensure_allocated(&m->image_cache, &image_hash_ops);
417 [ # # ]: 0 : if (r < 0)
418 : 0 : return r;
419 : :
420 [ # # ]: 0 : if (!m->image_cache_defer_event) {
421 : 0 : r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m);
422 [ # # ]: 0 : if (r < 0)
423 : 0 : return r;
424 : :
425 : 0 : r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
426 [ # # ]: 0 : if (r < 0)
427 : 0 : return r;
428 : : }
429 : :
430 : 0 : r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
431 [ # # ]: 0 : if (r < 0)
432 : 0 : return r;
433 : :
434 : 0 : r = image_find(IMAGE_MACHINE, e, &image);
435 [ # # ]: 0 : if (r == -ENOENT)
436 : 0 : return 0;
437 [ # # ]: 0 : if (r < 0)
438 : 0 : return r;
439 : :
440 : 0 : image->userdata = m;
441 : :
442 : 0 : r = hashmap_put(m->image_cache, image->name, image);
443 [ # # ]: 0 : if (r < 0) {
444 : 0 : image_unref(image);
445 : 0 : return r;
446 : : }
447 : :
448 : 0 : *found = image;
449 : 0 : return 1;
450 : : }
451 : :
452 : 0 : char *image_bus_path(const char *name) {
453 : 0 : _cleanup_free_ char *e = NULL;
454 : :
455 [ # # ]: 0 : assert(name);
456 : :
457 : 0 : e = bus_label_escape(name);
458 [ # # ]: 0 : if (!e)
459 : 0 : return NULL;
460 : :
461 : 0 : return strjoin("/org/freedesktop/machine1/image/", e);
462 : : }
463 : :
464 : 0 : int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
465 : 0 : _cleanup_hashmap_free_ Hashmap *images = NULL;
466 : 0 : _cleanup_strv_free_ char **l = NULL;
467 : : Image *image;
468 : : Iterator i;
469 : : int r;
470 : :
471 [ # # ]: 0 : assert(bus);
472 [ # # ]: 0 : assert(path);
473 [ # # ]: 0 : assert(nodes);
474 : :
475 : 0 : images = hashmap_new(&image_hash_ops);
476 [ # # ]: 0 : if (!images)
477 : 0 : return -ENOMEM;
478 : :
479 : 0 : r = image_discover(IMAGE_MACHINE, images);
480 [ # # ]: 0 : if (r < 0)
481 : 0 : return r;
482 : :
483 [ # # ]: 0 : HASHMAP_FOREACH(image, images, i) {
484 : : char *p;
485 : :
486 : 0 : p = image_bus_path(image->name);
487 [ # # ]: 0 : if (!p)
488 : 0 : return -ENOMEM;
489 : :
490 : 0 : r = strv_consume(&l, p);
491 [ # # ]: 0 : if (r < 0)
492 : 0 : return r;
493 : : }
494 : :
495 : 0 : *nodes = TAKE_PTR(l);
496 : :
497 : 0 : return 1;
498 : : }
|