<term><option>--list</option></term>
<term><option>-l</option></term>
- <listitem><para>Prints the paths of all the files and directories in the specified OS image to
- standard output.</para></listitem>
+ <listitem><para>Prints the paths of all the files and directories in the specified OS image or
+ directory to standard output.</para></listitem>
</varlistentry>
<varlistentry>
<listitem><para>Generates a BSD
<citerefentry project='die-net'><refentrytitle>mtree</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- compatible file manifest of the specified disk image. This is useful for comparing disk image
+ compatible file manifest of the specified disk image or directory. This is useful for comparing image
contents in detail, including inode information and other metadata. While the generated manifest will
contain detailed inode information, it currently excludes extended attributes, file system
capabilities, MAC labels,
<term><option>--copy-from</option></term>
<term><option>-x</option></term>
- <listitem><para>Copies a file or directory from the specified OS image into the specified location on
- the host file system. Expects three arguments: a path to an image file, a source path (relative to
- the image's root directory) and a destination path (relative to the current working directory, or an
- absolute path, both outside of the image). If the destination path is omitted or specified as dash
- (<literal>-</literal>), the specified file is written to standard output. If the source path in the
- image file system refers to a regular file it is copied to the destination path. In this case access
- mode, extended attributes and timestamps are copied as well, but file ownership is not. If the source
- path in the image refers to a directory, it is copied to the destination path, recursively with all
- containing files and directories. In this case the file ownership is copied too.</para></listitem>
+ <listitem><para>Copies a file or directory from the specified OS image or directory into the
+ specified location on the host file system. Expects three arguments: a path to an image file or
+ directory, a source path (relative to the image's root directory) and a destination path (relative to
+ the current working directory, or an absolute path, both outside of the image). If the destination
+ path is omitted or specified as dash (<literal>-</literal>), the specified file is written to
+ standard output. If the source path in the image file system refers to a regular file it is copied to
+ the destination path. In this case access mode, extended attributes and timestamps are copied as
+ well, but file ownership is not. If the source path in the image refers to a directory, it is copied
+ to the destination path, recursively with all containing files and directories. In this case the file
+ ownership is copied too.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-a</option></term>
<listitem><para>Copies a file or directory from the specified location in the host file system into
- the specified OS image. Expects three arguments: a path to an image file, a source path (relative to
- the current working directory, or an absolute path, both outside of the image) and a destination path
- (relative to the image's root directory). If the source path is omitted or specified as dash
- (<literal>-</literal>), the data to write is read from standard input. If the source path in the host
- file system refers to a regular file, it is copied to the destination path. In this case access mode,
- extended attributes and timestamps are copied as well, but file ownership is not. If the source path
- in the host file system refers to a directory it is copied to the destination path, recursively with
- all containing files and directories. In this case the file ownership is copied
- too.</para>
+ the specified OS image or directory. Expects three arguments: a path to an image file or directory, a
+ source path (relative to the current working directory, or an absolute path, both outside of the
+ image) and a destination path (relative to the image's root directory). If the source path is omitted
+ or specified as dash (<literal>-</literal>), the data to write is read from standard input. If the
+ source path in the host file system refers to a regular file, it is copied to the destination path.
+ In this case access mode, extended attributes and timestamps are copied as well, but file ownership
+ is not. If the source path in the host file system refers to a directory it is copied to the
+ destination path, recursively with all containing files and directories. In this case the file
+ ownership is copied too.</para>
<para>As with <option>--mount</option> file system checks are implicitly run before the copy
operation begins.</para></listitem>
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;
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);
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 {
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;
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;
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;
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;
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;
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];
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;
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;
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;
_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);
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);
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");
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);
- }
-
- r = dissect_loop_device_and_warn(
- d,
- &arg_verity_settings,
- /* mount_options= */ NULL,
- arg_image_policy,
- arg_flags,
- &m);
- if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to read verity artifacts for %s: %m", arg_image);
- if (arg_action == ACTION_ATTACH)
- return action_attach(m, d);
+ 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);
- 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_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);
+ }
- if (arg_action != ACTION_DISSECT) {
- r = dissected_image_decrypt_interactively(
- m, NULL,
+ 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) {