dissect: Allow a few verbs to operate on directories as well as image files
--copy-to, --copy-from, --list and --mtree are useful for image directories as well as image files, so for those verbs, let's check if we were passed a directory and skip all the image file setup if that's the case.
This commit is contained in:
+119
-85
@@ -64,6 +64,7 @@ static enum {
|
||||
ACTION_VALIDATE,
|
||||
} arg_action = ACTION_DISSECT;
|
||||
static char *arg_image = NULL;
|
||||
static char *arg_root = NULL;
|
||||
static char *arg_path = NULL;
|
||||
static const char *arg_source = NULL;
|
||||
static const char *arg_target = NULL;
|
||||
@@ -87,6 +88,7 @@ static char *arg_loop_ref = NULL;
|
||||
static ImagePolicy* arg_image_policy = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_path, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_argv, strv_freep);
|
||||
@@ -206,6 +208,31 @@ static int patch_argv(int *argc, char ***argv, char ***buf) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_image_path_argument(const char *path, char **ret_root, char **ret_image) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(ret_image);
|
||||
|
||||
r = parse_path_argument(path, /* suppress_root= */ false, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (stat(p, &st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat %s: %m", p);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (!ret_root)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s is not an image file.", p);
|
||||
|
||||
*ret_root = TAKE_PTR(p);
|
||||
} else
|
||||
*ret_image = TAKE_PTR(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
enum {
|
||||
@@ -493,7 +520,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path as only argument.");
|
||||
|
||||
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image);
|
||||
r = parse_image_path_argument(argv[optind], NULL, &arg_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -506,7 +533,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path and mount point path as only arguments.");
|
||||
|
||||
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image);
|
||||
r = parse_image_path_argument(argv[optind], NULL, &arg_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -532,7 +559,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path as only argument.");
|
||||
|
||||
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image);
|
||||
r = parse_image_path_argument(argv[optind], NULL, &arg_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
@@ -542,7 +569,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path or loopback device as only argument.");
|
||||
|
||||
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image);
|
||||
r = parse_image_path_argument(argv[optind], NULL, &arg_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
@@ -551,9 +578,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
case ACTION_MTREE:
|
||||
if (optind + 1 != argc)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path as only argument.");
|
||||
"Expected an image file or directory path as only argument.");
|
||||
|
||||
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image);
|
||||
r = parse_image_path_argument(argv[optind], &arg_root, &arg_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -563,9 +590,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
case ACTION_COPY_FROM:
|
||||
if (argc < optind + 2 || argc > optind + 3)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path, a source path and an optional destination path as only arguments.");
|
||||
"Expected an image file or directory path, a source path and an optional destination path as only arguments.");
|
||||
|
||||
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image);
|
||||
r = parse_image_path_argument(argv[optind], &arg_root, &arg_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
arg_source = argv[optind + 1];
|
||||
@@ -577,9 +604,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
case ACTION_COPY_TO:
|
||||
if (argc < optind + 2 || argc > optind + 3)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path, an optional source path and a destination path as only arguments.");
|
||||
"Expected an image file or directory path, an optional source path and a destination path as only arguments.");
|
||||
|
||||
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image);
|
||||
r = parse_image_path_argument(argv[optind], &arg_root, &arg_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -599,7 +626,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path and an optional command line.");
|
||||
|
||||
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image);
|
||||
r = parse_image_path_argument(argv[optind], NULL, &arg_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -622,7 +649,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Expected an image file path as only argument.");
|
||||
|
||||
r = parse_path_argument(argv[optind], /* suppress_root= */ false, &arg_image);
|
||||
r = parse_image_path_argument(argv[optind], NULL, &arg_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1232,46 +1259,52 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
|
||||
_cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
|
||||
_cleanup_(rmdir_and_freep) char *created_dir = NULL;
|
||||
_cleanup_free_ char *temp = NULL;
|
||||
const char *root;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(d);
|
||||
assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO));
|
||||
|
||||
r = detach_mount_namespace();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to detach mount namespace: %m");
|
||||
if (arg_image) {
|
||||
assert(m);
|
||||
assert(d);
|
||||
|
||||
r = tempfn_random_child(NULL, program_invocation_short_name, &temp);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to generate temporary mount directory: %m");
|
||||
r = detach_mount_namespace();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to detach mount namespace: %m");
|
||||
|
||||
r = mkdir_p(temp, 0700);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create mount point: %m");
|
||||
r = tempfn_random_child(NULL, program_invocation_short_name, &temp);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to generate temporary mount directory: %m");
|
||||
|
||||
created_dir = TAKE_PTR(temp);
|
||||
r = mkdir_p(temp, 0700);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create mount point: %m");
|
||||
|
||||
r = dissected_image_mount_and_warn(m, created_dir, UID_INVALID, UID_INVALID, arg_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
created_dir = TAKE_PTR(temp);
|
||||
|
||||
mounted_dir = TAKE_PTR(created_dir);
|
||||
r = dissected_image_mount_and_warn(m, created_dir, UID_INVALID, UID_INVALID, arg_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = loop_device_flock(d, LOCK_UN);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to unlock loopback block device: %m");
|
||||
mounted_dir = TAKE_PTR(created_dir);
|
||||
|
||||
r = dissected_image_relinquish(m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m");
|
||||
r = loop_device_flock(d, LOCK_UN);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to unlock loopback block device: %m");
|
||||
|
||||
r = dissected_image_relinquish(m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m");
|
||||
}
|
||||
|
||||
root = mounted_dir ?: arg_root;
|
||||
|
||||
switch (arg_action) {
|
||||
|
||||
case ACTION_COPY_FROM: {
|
||||
_cleanup_close_ int source_fd = -EBADF, target_fd = -EBADF;
|
||||
|
||||
source_fd = chase_and_open(arg_source, mounted_dir, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
|
||||
source_fd = chase_and_open(arg_source, root, CHASE_PREFIX_ROOT|CHASE_WARN, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
|
||||
if (source_fd < 0)
|
||||
return log_error_errno(source_fd, "Failed to open source path '%s' in image '%s': %m", arg_source, arg_image);
|
||||
|
||||
@@ -1328,7 +1361,7 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
|
||||
return log_error_errno(r, "Failed to extract filename from target path '%s': %m", arg_target);
|
||||
is_dir = r == O_DIRECTORY;
|
||||
|
||||
r = chase(dn, mounted_dir, CHASE_PREFIX_ROOT|CHASE_WARN, NULL, &dfd);
|
||||
r = chase(dn, root, CHASE_PREFIX_ROOT|CHASE_WARN, NULL, &dfd);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open '%s': %m", dn);
|
||||
|
||||
@@ -1398,7 +1431,7 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
|
||||
case ACTION_MTREE: {
|
||||
_cleanup_close_ int dfd = -EBADF;
|
||||
|
||||
dfd = open(mounted_dir, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
|
||||
dfd = open(root, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
|
||||
if (dfd < 0)
|
||||
return log_error_errno(errno, "Failed to open mount directory: %m");
|
||||
|
||||
@@ -1778,64 +1811,65 @@ static int run(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
r = verity_settings_load(
|
||||
if (arg_image) {
|
||||
r = verity_settings_load(
|
||||
&arg_verity_settings,
|
||||
arg_image, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read verity artifacts for %s: %m", arg_image);
|
||||
|
||||
if (arg_verity_settings.data_path)
|
||||
arg_flags |= DISSECT_IMAGE_NO_PARTITION_TABLE; /* We only support Verity per file system,
|
||||
* hence if there's external Verity data
|
||||
* available we turn off partition table
|
||||
* support */
|
||||
|
||||
if (arg_action == ACTION_VALIDATE)
|
||||
return action_validate();
|
||||
|
||||
open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR;
|
||||
loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN;
|
||||
|
||||
if (arg_in_memory)
|
||||
r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
|
||||
else
|
||||
r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
|
||||
|
||||
if (arg_loop_ref) {
|
||||
r = loop_device_set_filename(d, arg_loop_ref);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref);
|
||||
}
|
||||
return log_error_errno(r, "Failed to read verity artifacts for %s: %m", arg_image);
|
||||
|
||||
r = dissect_loop_device_and_warn(
|
||||
d,
|
||||
&arg_verity_settings,
|
||||
/* mount_options= */ NULL,
|
||||
arg_image_policy,
|
||||
arg_flags,
|
||||
&m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (arg_verity_settings.data_path)
|
||||
arg_flags |= DISSECT_IMAGE_NO_PARTITION_TABLE; /* We only support Verity per file system,
|
||||
* hence if there's external Verity data
|
||||
* available we turn off partition table
|
||||
* support */
|
||||
|
||||
if (arg_action == ACTION_ATTACH)
|
||||
return action_attach(m, d);
|
||||
if (arg_action == ACTION_VALIDATE)
|
||||
return action_validate();
|
||||
|
||||
r = dissected_image_load_verity_sig_partition(
|
||||
m,
|
||||
d->fd,
|
||||
&arg_verity_settings);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load verity signature partition: %m");
|
||||
open_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR;
|
||||
loop_flags = FLAGS_SET(arg_flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN;
|
||||
if (arg_in_memory)
|
||||
r = loop_device_make_by_path_memory(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
|
||||
else
|
||||
r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
|
||||
|
||||
if (arg_action != ACTION_DISSECT) {
|
||||
r = dissected_image_decrypt_interactively(
|
||||
m, NULL,
|
||||
if (arg_loop_ref) {
|
||||
r = loop_device_set_filename(d, arg_loop_ref);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to set loop reference string to '%s', ignoring: %m", arg_loop_ref);
|
||||
}
|
||||
|
||||
r = dissect_loop_device_and_warn(
|
||||
d,
|
||||
&arg_verity_settings,
|
||||
arg_flags);
|
||||
/* mount_options= */ NULL,
|
||||
arg_image_policy,
|
||||
arg_flags,
|
||||
&m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_action == ACTION_ATTACH)
|
||||
return action_attach(m, d);
|
||||
|
||||
r = dissected_image_load_verity_sig_partition(
|
||||
m,
|
||||
d->fd,
|
||||
&arg_verity_settings);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load verity signature partition: %m");
|
||||
|
||||
if (arg_action != ACTION_DISSECT) {
|
||||
r = dissected_image_decrypt_interactively(
|
||||
m, NULL,
|
||||
&arg_verity_settings,
|
||||
arg_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
switch (arg_action) {
|
||||
|
||||
Reference in New Issue
Block a user