1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "alloc-util.h"
10 #include "ansi-color.h"
12 #include "discover-image.h"
15 #include "import-raw.h"
16 #include "import-tar.h"
17 #include "import-util.h"
20 #include "main-func.h"
21 #include "parse-argument.h"
22 #include "parse-util.h"
23 #include "path-util.h"
24 #include "runtime-scope.h"
25 #include "signal-util.h"
26 #include "string-util.h"
29 static const char *arg_image_root
= NULL
;
30 static ImportFlags arg_import_flags
= IMPORT_BTRFS_SUBVOL
| IMPORT_BTRFS_QUOTA
| IMPORT_CONVERT_QCOW2
| IMPORT_SYNC
;
31 static uint64_t arg_offset
= UINT64_MAX
, arg_size_max
= UINT64_MAX
;
32 static ImageClass arg_class
= IMAGE_MACHINE
;
33 static RuntimeScope arg_runtime_scope
= _RUNTIME_SCOPE_INVALID
;
35 static int normalize_local(const char *local
, char **ret
) {
36 _cleanup_free_
char *ll
= NULL
;
41 if (arg_import_flags
& IMPORT_DIRECT
) {
44 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No local path specified.");
46 if (!path_is_absolute(local
)) {
47 ll
= path_join(arg_image_root
, local
);
54 if (!path_is_valid(local
))
55 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
56 "Local path name '%s' is not valid.", local
);
59 if (!image_name_is_valid(local
))
60 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
61 "Local image name '%s' is not valid.",
66 if (!FLAGS_SET(arg_import_flags
, IMPORT_FORCE
)) {
67 r
= image_find(arg_runtime_scope
, arg_class
, local
, NULL
, NULL
);
70 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
72 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
73 "Image '%s' already exists.",
88 static int open_source(const char *path
, const char *local
, int *ret_open_fd
) {
89 _cleanup_close_
int open_fd
= -EBADF
;
96 open_fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
98 return log_error_errno(errno
, "Failed to open source file '%s': %m", path
);
102 if (arg_offset
!= UINT64_MAX
)
103 log_info("Importing '%s', saving at offset %" PRIu64
" in '%s'.", path
, arg_offset
, local
);
105 log_info("Importing '%s', saving as '%s'.", path
, local
);
107 _cleanup_free_
char *pretty
= NULL
;
109 retval
= STDIN_FILENO
;
111 (void) fd_get_path(STDIN_FILENO
, &pretty
);
113 if (arg_offset
!= UINT64_MAX
)
114 log_info("Importing '%s', saving at offset %" PRIu64
" in '%s'.", strempty(pretty
), arg_offset
, local
);
116 log_info("Importing '%s', saving as '%s'.", strempty(pretty
), local
);
119 if (!FLAGS_SET(arg_import_flags
, IMPORT_DIRECT
))
120 log_info("Operating on image directory '%s'.", arg_image_root
);
122 if (!FLAGS_SET(arg_import_flags
, IMPORT_SYNC
))
123 log_info("File system synchronization on completion is off.");
125 *ret_open_fd
= TAKE_FD(open_fd
);
129 static void on_tar_finished(TarImport
*import
, int error
, void *userdata
) {
130 sd_event
*event
= userdata
;
134 log_info("Operation completed successfully.");
136 sd_event_exit(event
, ABS(error
));
139 static int import_tar(int argc
, char *argv
[], void *userdata
) {
140 _cleanup_(tar_import_unrefp
) TarImport
*import
= NULL
;
141 _cleanup_free_
char *ll
= NULL
, *normalized
= NULL
;
142 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
143 const char *path
= NULL
, *local
= NULL
;
144 _cleanup_close_
int open_fd
= -EBADF
;
148 path
= empty_or_dash_to_null(argv
[1]);
151 local
= empty_or_dash_to_null(argv
[2]);
153 _cleanup_free_
char *l
= NULL
;
155 r
= path_extract_filename(path
, &l
);
157 return log_error_errno(r
, "Failed to extract filename from path '%s': %m", path
);
159 r
= tar_strip_suffixes(l
, &ll
);
166 r
= normalize_local(local
, &normalized
);
170 fd
= open_source(path
, normalized
, &open_fd
);
174 r
= import_allocate_event_with_signals(&event
);
178 r
= tar_import_new(&import
, event
, arg_image_root
, on_tar_finished
, event
);
180 return log_error_errno(r
, "Failed to allocate importer: %m");
182 r
= tar_import_start(
186 arg_import_flags
& IMPORT_FLAGS_MASK_TAR
);
188 return log_error_errno(r
, "Failed to import image: %m");
190 r
= sd_event_loop(event
);
192 return log_error_errno(r
, "Failed to run event loop: %m");
194 log_info("Exiting.");
198 static void on_raw_finished(RawImport
*import
, int error
, void *userdata
) {
199 sd_event
*event
= userdata
;
203 log_info("Operation completed successfully.");
205 sd_event_exit(event
, ABS(error
));
208 static int import_raw(int argc
, char *argv
[], void *userdata
) {
209 _cleanup_(raw_import_unrefp
) RawImport
*import
= NULL
;
210 _cleanup_free_
char *ll
= NULL
, *normalized
= NULL
;
211 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
212 const char *path
= NULL
, *local
= NULL
;
213 _cleanup_close_
int open_fd
= -EBADF
;
217 path
= empty_or_dash_to_null(argv
[1]);
220 local
= empty_or_dash_to_null(argv
[2]);
222 _cleanup_free_
char *l
= NULL
;
224 r
= path_extract_filename(path
, &l
);
226 return log_error_errno(r
, "Failed to extract filename from path '%s': %m", path
);
228 r
= raw_strip_suffixes(l
, &ll
);
235 r
= normalize_local(local
, &normalized
);
239 fd
= open_source(path
, normalized
, &open_fd
);
243 r
= import_allocate_event_with_signals(&event
);
247 r
= raw_import_new(&import
, event
, arg_image_root
, on_raw_finished
, event
);
249 return log_error_errno(r
, "Failed to allocate importer: %m");
251 r
= raw_import_start(
257 arg_import_flags
& IMPORT_FLAGS_MASK_RAW
);
259 return log_error_errno(r
, "Failed to import image: %m");
261 r
= sd_event_loop(event
);
263 return log_error_errno(r
, "Failed to run event loop: %m");
265 log_info("Exiting.");
269 static int help(int argc
, char *argv
[], void *userdata
) {
271 printf("%1$s [OPTIONS...] {COMMAND} ...\n"
272 "\n%4$sImport disk images.%5$s\n"
273 "\n%2$sCommands:%3$s\n"
274 " tar FILE [NAME] Import a TAR image\n"
275 " raw FILE [NAME] Import a RAW image\n"
276 "\n%2$sOptions:%3$s\n"
277 " -h --help Show this help\n"
278 " --version Show package version\n"
279 " --force Force creation of image\n"
280 " --image-root=PATH Image root directory\n"
281 " --read-only Create a read-only image\n"
282 " --direct Import directly to specified file\n"
283 " --btrfs-subvol=BOOL Controls whether to create a btrfs subvolume\n"
284 " instead of a directory\n"
285 " --btrfs-quota=BOOL Controls whether to set up quota for btrfs\n"
287 " --convert-qcow2=BOOL Controls whether to convert QCOW2 images to\n"
288 " regular disk images\n"
289 " --sync=BOOL Controls whether to sync() before completing\n"
290 " --offset=BYTES Offset to seek to in destination\n"
291 " --size-max=BYTES Maximum number of bytes to write to destination\n"
292 " --class=CLASS Select image class (machine, sysext, confext,\n"
294 program_invocation_short_name
,
303 static int parse_argv(int argc
, char *argv
[]) {
320 static const struct option options
[] = {
321 { "help", no_argument
, NULL
, 'h' },
322 { "version", no_argument
, NULL
, ARG_VERSION
},
323 { "force", no_argument
, NULL
, ARG_FORCE
},
324 { "image-root", required_argument
, NULL
, ARG_IMAGE_ROOT
},
325 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
326 { "direct", no_argument
, NULL
, ARG_DIRECT
},
327 { "btrfs-subvol", required_argument
, NULL
, ARG_BTRFS_SUBVOL
},
328 { "btrfs-quota", required_argument
, NULL
, ARG_BTRFS_QUOTA
},
329 { "convert-qcow2", required_argument
, NULL
, ARG_CONVERT_QCOW2
},
330 { "sync", required_argument
, NULL
, ARG_SYNC
},
331 { "offset", required_argument
, NULL
, ARG_OFFSET
},
332 { "size-max", required_argument
, NULL
, ARG_SIZE_MAX
},
333 { "class", required_argument
, NULL
, ARG_CLASS
},
342 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
347 return help(0, NULL
, NULL
);
353 arg_import_flags
|= IMPORT_FORCE
;
357 arg_image_root
= optarg
;
361 arg_import_flags
|= IMPORT_READ_ONLY
;
365 arg_import_flags
|= IMPORT_DIRECT
;
368 case ARG_BTRFS_SUBVOL
:
369 r
= parse_boolean_argument("--btrfs-subvol=", optarg
, NULL
);
373 SET_FLAG(arg_import_flags
, IMPORT_BTRFS_SUBVOL
, r
);
376 case ARG_BTRFS_QUOTA
:
377 r
= parse_boolean_argument("--btrfs-quota=", optarg
, NULL
);
381 SET_FLAG(arg_import_flags
, IMPORT_BTRFS_QUOTA
, r
);
384 case ARG_CONVERT_QCOW2
:
385 r
= parse_boolean_argument("--convert-qcow2=", optarg
, NULL
);
389 SET_FLAG(arg_import_flags
, IMPORT_CONVERT_QCOW2
, r
);
393 r
= parse_boolean_argument("--sync=", optarg
, NULL
);
397 SET_FLAG(arg_import_flags
, IMPORT_SYNC
, r
);
403 r
= safe_atou64(optarg
, &u
);
405 return log_error_errno(r
, "Failed to parse --offset= argument: %s", optarg
);
406 if (!FILE_SIZE_VALID(u
))
407 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Argument to --offset= switch too large: %s", optarg
);
416 r
= parse_size(optarg
, 1024, &u
);
418 return log_error_errno(r
, "Failed to parse --size-max= argument: %s", optarg
);
419 if (!FILE_SIZE_VALID(u
))
420 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Argument to --size-max= switch too large: %s", optarg
);
427 arg_class
= image_class_from_string(optarg
);
429 return log_error_errno(arg_class
, "Failed to parse --class= argument: %s", optarg
);
437 assert_not_reached();
440 /* Make sure offset+size is still in the valid range if both set */
441 if (arg_offset
!= UINT64_MAX
&& arg_size_max
!= UINT64_MAX
&&
442 ((arg_size_max
> (UINT64_MAX
- arg_offset
)) ||
443 !FILE_SIZE_VALID(arg_offset
+ arg_size_max
)))
444 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "File offset und maximum size out of range.");
446 if (arg_offset
!= UINT64_MAX
&& !FLAGS_SET(arg_import_flags
, IMPORT_DIRECT
))
447 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "File offset only supported in --direct mode.");
450 arg_image_root
= image_root_to_string(arg_class
);
455 static int import_main(int argc
, char *argv
[]) {
456 static const Verb verbs
[] = {
457 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
458 { "tar", 2, 3, 0, import_tar
},
459 { "raw", 2, 3, 0, import_raw
},
463 return dispatch_verb(argc
, argv
, verbs
, NULL
);
466 static void parse_env(void) {
469 /* Let's make these relatively low-level settings also controllable via env vars. User can then set
470 * them to systemd-import if they like to tweak behaviour */
472 r
= getenv_bool("SYSTEMD_IMPORT_BTRFS_SUBVOL");
474 SET_FLAG(arg_import_flags
, IMPORT_BTRFS_SUBVOL
, r
);
475 else if (r
!= -ENXIO
)
476 log_warning_errno(r
, "Failed to parse $SYSTEMD_IMPORT_BTRFS_SUBVOL: %m");
478 r
= getenv_bool("SYSTEMD_IMPORT_BTRFS_QUOTA");
480 SET_FLAG(arg_import_flags
, IMPORT_BTRFS_QUOTA
, r
);
481 else if (r
!= -ENXIO
)
482 log_warning_errno(r
, "Failed to parse $SYSTEMD_IMPORT_BTRFS_QUOTA: %m");
484 r
= getenv_bool("SYSTEMD_IMPORT_SYNC");
486 SET_FLAG(arg_import_flags
, IMPORT_SYNC
, r
);
487 else if (r
!= -ENXIO
)
488 log_warning_errno(r
, "Failed to parse $SYSTEMD_IMPORT_SYNC: %m");
491 static int run(int argc
, char *argv
[]) {
494 setlocale(LC_ALL
, "");
499 r
= parse_argv(argc
, argv
);
503 (void) ignore_signals(SIGPIPE
);
505 return import_main(argc
, argv
);
508 DEFINE_MAIN_FUNCTION(run
);