static int extract_pretty(const char *path, const char *suffix, char **ret) { _cleanup_free_ char *name = NULL; const char *p; size_t n; assert(path); assert(ret); p = last_path_component(path); n = strcspn(p, "/"); name = strndup(p, n); if (!name) return -ENOMEM; if (suffix) { char *e; e = endswith(name, suffix); if (!e) return -EINVAL; *e = 0; } if (!image_name_is_valid(name)) return -EINVAL; *ret = TAKE_PTR(name); return 0; }
static int determine_image(const char *image, bool permit_non_existing, char **ret) { int r; /* If the specified name is a valid image name, we pass it as-is to portabled, which will search for it in the * usual search directories. Otherwise we presume it's a path, and will normalize it on the client's side * (among other things, to make the path independent of the client's working directory) before passing it * over. */ if (image_name_is_valid(image)) { char *c; if (!arg_quiet && laccess(image, F_OK) >= 0) log_warning("Ambiguous invocation: current working directory contains file matching non-path argument '%s', ignoring. " "Prefix argument with './' to force reference to file in current working directory.", image); c = strdup(image); if (!c) return log_oom(); *ret = c; return 0; } if (arg_transport != BUS_TRANSPORT_LOCAL) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Operations on images by path not supported when connecting to remote systems."); r = chase_symlinks(image, NULL, CHASE_TRAIL_SLASH | (permit_non_existing ? CHASE_NONEXISTENT : 0), ret); if (r < 0) return log_error_errno(r, "Cannot normalize specified image path '%s': %m", image); return 0; }
int bus_image_method_rename( sd_bus_message *message, void *userdata, sd_bus_error *error) { Image *image = userdata; Manager *m = image->userdata; const char *new_name; int r; assert(message); assert(image); r = sd_bus_message_read(message, "s", &new_name); if (r < 0) return r; if (!image_name_is_valid(new_name)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name); r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", NULL, false, UID_INVALID, &m->polkit_registry, error); if (r < 0) return r; if (r == 0) return 1; /* Will call us back */ r = image_rename(image, new_name); if (r < 0) return r; return sd_bus_reply_method_return(message, NULL); }
int image_find(const char *name, Image **ret) { const char *path; int r; assert(name); /* There are no images with invalid names */ if (!image_name_is_valid(name)) return 0; NULSTR_FOREACH(path, image_search_path) { _cleanup_closedir_ DIR *d = NULL; d = opendir(path); if (!d) { if (errno == ENOENT) continue; return -errno; } r = image_make(NULL, dirfd(d), path, name, ret); if (r == 0 || r == -ENOENT) { _cleanup_free_ char *raw = NULL; raw = strappend(name, ".raw"); if (!raw) return -ENOMEM; r = image_make(NULL, dirfd(d), path, raw, ret); if (r == 0 || r == -ENOENT) continue; } if (r < 0) return r; return 1; }
int bus_image_method_clone( sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; Image *image = userdata; Manager *m = image->userdata; const char *new_name; int r, read_only; pid_t child; assert(message); assert(image); assert(m); if (m->n_operations >= OPERATIONS_MAX) return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); r = sd_bus_message_read(message, "sb", &new_name, &read_only); if (r < 0) return r; if (!image_name_is_valid(new_name)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name); r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", NULL, false, UID_INVALID, &m->polkit_registry, error); if (r < 0) return r; if (r == 0) return 1; /* Will call us back */ if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); child = fork(); if (child < 0) return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); if (child == 0) { errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); r = image_clone(image, new_name, read_only); if (r < 0) { (void) write(errno_pipe_fd[1], &r, sizeof(r)); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); r = operation_new(m, NULL, child, message, errno_pipe_fd[0]); if (r < 0) { (void) sigkill_wait(child); return r; } errno_pipe_fd[0] = -1; return 1; }
int bus_image_acquire( Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, ImageAcquireMode mode, const char *polkit_action, Image **ret, sd_bus_error *error) { _cleanup_(image_unrefp) Image *loaded = NULL; Image *cached; int r; assert(m); assert(message); assert(name_or_path || image); assert(mode >= 0); assert(mode < _BUS_IMAGE_ACQUIRE_MODE_MAX); assert(polkit_action || mode == BUS_IMAGE_REFUSE_BY_PATH); assert(ret); /* Acquires an 'Image' object if not acquired yet, and enforces necessary authentication while doing so. */ if (mode == BUS_IMAGE_AUTHENTICATE_ALL) { r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, polkit_action, NULL, false, UID_INVALID, &m->polkit_registry, error); if (r < 0) return r; if (r == 0) { /* Will call us back */ *ret = NULL; return 0; } } /* Already passed in? */ if (image) { *ret = image; return 1; } /* Let's see if this image is already cached? */ cached = manager_image_cache_get(m, name_or_path); if (cached) { *ret = cached; return 1; } if (image_name_is_valid(name_or_path)) { /* If it's a short name, let's search for it */ r = image_find(IMAGE_PORTABLE, name_or_path, &loaded); if (r == -ENOENT) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE, "No image '%s' found.", name_or_path); /* other errors are handled below… */ } else { /* Don't accept path if this is always forbidden */ if (mode == BUS_IMAGE_REFUSE_BY_PATH) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Expected image name, not path in place of '%s'.", name_or_path); if (!path_is_absolute(name_or_path)) 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); if (!path_is_normalized(name_or_path)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image path '%s' is not normalized.", name_or_path); if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) { r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, polkit_action, NULL, false, UID_INVALID, &m->polkit_registry, error); if (r < 0) return r; if (r == 0) { /* Will call us back */ *ret = NULL; return 0; } } r = image_from_path(name_or_path, &loaded); } if (r == -EMEDIUMTYPE) { 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); return r; } if (r < 0) return r; /* Add what we just loaded to the cache. This has as side-effect that the object stays in memory until the * cache is purged again, i.e. at least for the current event loop iteration, which is all we need, and which * means we don't actually need to ref the return object. */ r = manager_image_cache_add(m, loaded); if (r < 0) return r; *ret = loaded; return 1; }