1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "alloc-util.h"
11 #include "discover-image.h"
15 #include "hostname-util.h"
16 #include "import-raw.h"
17 #include "import-tar.h"
18 #include "import-util.h"
20 #include "main-func.h"
21 #include "parse-argument.h"
22 #include "parse-util.h"
23 #include "signal-util.h"
24 #include "string-util.h"
25 #include "terminal-util.h"
28 static const char *arg_image_root
= NULL
;
29 static ImportFlags arg_import_flags
= IMPORT_BTRFS_SUBVOL
| IMPORT_BTRFS_QUOTA
| IMPORT_CONVERT_QCOW2
| IMPORT_SYNC
;
30 static uint64_t arg_offset
= UINT64_MAX
, arg_size_max
= UINT64_MAX
;
31 static ImageClass arg_class
= IMAGE_MACHINE
;
33 static int normalize_local(const char *local
, char **ret
) {
34 _cleanup_free_
char *ll
= NULL
;
39 if (arg_import_flags
& IMPORT_DIRECT
) {
42 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No local path specified.");
44 if (!path_is_absolute(local
)) {
45 ll
= path_join(arg_image_root
, local
);
52 if (!path_is_valid(local
))
53 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
54 "Local path name '%s' is not valid.", local
);
57 if (!image_name_is_valid(local
))
58 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
59 "Local image name '%s' is not valid.",
64 if (!FLAGS_SET(arg_import_flags
, IMPORT_FORCE
)) {
65 r
= image_find(arg_class
, local
, NULL
, NULL
);
68 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
70 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
71 "Image '%s' already exists.",
86 static int open_source(const char *path
, const char *local
, int *ret_open_fd
) {
87 _cleanup_close_
int open_fd
= -EBADF
;
94 open_fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
96 return log_error_errno(errno
, "Failed to open source file '%s': %m", path
);
100 if (arg_offset
!= UINT64_MAX
)
101 log_info("Importing '%s', saving at offset %" PRIu64
" in '%s'.", path
, arg_offset
, local
);
103 log_info("Importing '%s', saving as '%s'.", path
, local
);
105 _cleanup_free_
char *pretty
= NULL
;
107 retval
= STDIN_FILENO
;
109 (void) fd_get_path(STDIN_FILENO
, &pretty
);
111 if (arg_offset
!= UINT64_MAX
)
112 log_info("Importing '%s', saving at offset %" PRIu64
" in '%s'.", strempty(pretty
), arg_offset
, local
);
114 log_info("Importing '%s', saving as '%s'.", strempty(pretty
), local
);
117 if (!FLAGS_SET(arg_import_flags
, IMPORT_DIRECT
))
118 log_info("Operating on image directory '%s'.", arg_image_root
);
120 if (!FLAGS_SET(arg_import_flags
, IMPORT_SYNC
))
121 log_info("File system synchronization on completion is off.");
123 *ret_open_fd
= TAKE_FD(open_fd
);
127 static void on_tar_finished(TarImport
*import
, int error
, void *userdata
) {
128 sd_event
*event
= userdata
;
132 log_info("Operation completed successfully.");
134 sd_event_exit(event
, abs(error
));
137 static int import_tar(int argc
, char *argv
[], void *userdata
) {
138 _cleanup_(tar_import_unrefp
) TarImport
*import
= NULL
;
139 _cleanup_free_
char *ll
= NULL
, *normalized
= NULL
;
140 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
141 const char *path
= NULL
, *local
= NULL
;
142 _cleanup_close_
int open_fd
= -EBADF
;
146 path
= empty_or_dash_to_null(argv
[1]);
149 local
= empty_or_dash_to_null(argv
[2]);
151 _cleanup_free_
char *l
= NULL
;
153 r
= path_extract_filename(path
, &l
);
155 return log_error_errno(r
, "Failed to extract filename from path '%s': %m", path
);
157 r
= tar_strip_suffixes(l
, &ll
);
164 r
= normalize_local(local
, &normalized
);
168 fd
= open_source(path
, normalized
, &open_fd
);
172 r
= import_allocate_event_with_signals(&event
);
176 r
= tar_import_new(&import
, event
, arg_image_root
, on_tar_finished
, event
);
178 return log_error_errno(r
, "Failed to allocate importer: %m");
180 r
= tar_import_start(
184 arg_import_flags
& IMPORT_FLAGS_MASK_TAR
);
186 return log_error_errno(r
, "Failed to import image: %m");
188 r
= sd_event_loop(event
);
190 return log_error_errno(r
, "Failed to run event loop: %m");
192 log_info("Exiting.");
196 static void on_raw_finished(RawImport
*import
, int error
, void *userdata
) {
197 sd_event
*event
= userdata
;
201 log_info("Operation completed successfully.");
203 sd_event_exit(event
, abs(error
));
206 static int import_raw(int argc
, char *argv
[], void *userdata
) {
207 _cleanup_(raw_import_unrefp
) RawImport
*import
= NULL
;
208 _cleanup_free_
char *ll
= NULL
, *normalized
= NULL
;
209 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
210 const char *path
= NULL
, *local
= NULL
;
211 _cleanup_close_
int open_fd
= -EBADF
;
215 path
= empty_or_dash_to_null(argv
[1]);
218 local
= empty_or_dash_to_null(argv
[2]);
220 _cleanup_free_
char *l
= NULL
;
222 r
= path_extract_filename(path
, &l
);
224 return log_error_errno(r
, "Failed to extract filename from path '%s': %m", path
);
226 r
= raw_strip_suffixes(l
, &ll
);
233 r
= normalize_local(local
, &normalized
);
237 fd
= open_source(path
, normalized
, &open_fd
);
241 r
= import_allocate_event_with_signals(&event
);
245 r
= raw_import_new(&import
, event
, arg_image_root
, on_raw_finished
, event
);
247 return log_error_errno(r
, "Failed to allocate importer: %m");
249 r
= raw_import_start(
255 arg_import_flags
& IMPORT_FLAGS_MASK_RAW
);
257 return log_error_errno(r
, "Failed to import image: %m");
259 r
= sd_event_loop(event
);
261 return log_error_errno(r
, "Failed to run event loop: %m");
263 log_info("Exiting.");
267 static int help(int argc
, char *argv
[], void *userdata
) {
269 printf("%1$s [OPTIONS...] {COMMAND} ...\n"
270 "\n%4$sImport disk images.%5$s\n"
271 "\n%2$sCommands:%3$s\n"
272 " tar FILE [NAME] Import a TAR image\n"
273 " raw FILE [NAME] Import a RAW image\n"
274 "\n%2$sOptions:%3$s\n"
275 " -h --help Show this help\n"
276 " --version Show package version\n"
277 " --force Force creation of image\n"
278 " --image-root=PATH Image root directory\n"
279 " --read-only Create a read-only image\n"
280 " --direct Import directly to specified file\n"
281 " --btrfs-subvol=BOOL Controls whether to create a btrfs subvolume\n"
282 " instead of a directory\n"
283 " --btrfs-quota=BOOL Controls whether to set up quota for btrfs\n"
285 " --convert-qcow2=BOOL Controls whether to convert QCOW2 images to\n"
286 " regular disk images\n"
287 " --sync=BOOL Controls whether to sync() before completing\n"
288 " --offset=BYTES Offset to seek to in destination\n"
289 " --size-max=BYTES Maximum number of bytes to write to destination\n"
290 " --class=CLASS Select image class (machine, sysext, confext,\n"
292 program_invocation_short_name
,
301 static int parse_argv(int argc
, char *argv
[]) {
318 static const struct option options
[] = {
319 { "help", no_argument
, NULL
, 'h' },
320 { "version", no_argument
, NULL
, ARG_VERSION
},
321 { "force", no_argument
, NULL
, ARG_FORCE
},
322 { "image-root", required_argument
, NULL
, ARG_IMAGE_ROOT
},
323 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
324 { "direct", no_argument
, NULL
, ARG_DIRECT
},
325 { "btrfs-subvol", required_argument
, NULL
, ARG_BTRFS_SUBVOL
},
326 { "btrfs-quota", required_argument
, NULL
, ARG_BTRFS_QUOTA
},
327 { "convert-qcow2", required_argument
, NULL
, ARG_CONVERT_QCOW2
},
328 { "sync", required_argument
, NULL
, ARG_SYNC
},
329 { "offset", required_argument
, NULL
, ARG_OFFSET
},
330 { "size-max", required_argument
, NULL
, ARG_SIZE_MAX
},
331 { "class", required_argument
, NULL
, ARG_CLASS
},
340 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
345 return help(0, NULL
, NULL
);
351 arg_import_flags
|= IMPORT_FORCE
;
355 arg_image_root
= optarg
;
359 arg_import_flags
|= IMPORT_READ_ONLY
;
363 arg_import_flags
|= IMPORT_DIRECT
;
366 case ARG_BTRFS_SUBVOL
:
367 r
= parse_boolean_argument("--btrfs-subvol=", optarg
, NULL
);
371 SET_FLAG(arg_import_flags
, IMPORT_BTRFS_SUBVOL
, r
);
374 case ARG_BTRFS_QUOTA
:
375 r
= parse_boolean_argument("--btrfs-quota=", optarg
, NULL
);
379 SET_FLAG(arg_import_flags
, IMPORT_BTRFS_QUOTA
, r
);
382 case ARG_CONVERT_QCOW2
:
383 r
= parse_boolean_argument("--convert-qcow2=", optarg
, NULL
);
387 SET_FLAG(arg_import_flags
, IMPORT_CONVERT_QCOW2
, r
);
391 r
= parse_boolean_argument("--sync=", optarg
, NULL
);
395 SET_FLAG(arg_import_flags
, IMPORT_SYNC
, r
);
401 r
= safe_atou64(optarg
, &u
);
403 return log_error_errno(r
, "Failed to parse --offset= argument: %s", optarg
);
404 if (!FILE_SIZE_VALID(u
))
405 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Argument to --offset= switch too large: %s", optarg
);
414 r
= parse_size(optarg
, 1024, &u
);
416 return log_error_errno(r
, "Failed to parse --size-max= argument: %s", optarg
);
417 if (!FILE_SIZE_VALID(u
))
418 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Argument to --size-max= switch too large: %s", optarg
);
425 arg_class
= image_class_from_string(optarg
);
427 return log_error_errno(arg_class
, "Failed to parse --class= argument: %s", optarg
);
435 assert_not_reached();
438 /* Make sure offset+size is still in the valid range if both set */
439 if (arg_offset
!= UINT64_MAX
&& arg_size_max
!= UINT64_MAX
&&
440 ((arg_size_max
> (UINT64_MAX
- arg_offset
)) ||
441 !FILE_SIZE_VALID(arg_offset
+ arg_size_max
)))
442 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "File offset und maximum size out of range.");
444 if (arg_offset
!= UINT64_MAX
&& !FLAGS_SET(arg_import_flags
, IMPORT_DIRECT
))
445 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "File offset only supported in --direct mode.");
448 arg_image_root
= image_root_to_string(arg_class
);
453 static int import_main(int argc
, char *argv
[]) {
454 static const Verb verbs
[] = {
455 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
456 { "tar", 2, 3, 0, import_tar
},
457 { "raw", 2, 3, 0, import_raw
},
461 return dispatch_verb(argc
, argv
, verbs
, NULL
);
464 static void parse_env(void) {
467 /* Let's make these relatively low-level settings also controllable via env vars. User can then set
468 * them to systemd-import if they like to tweak behaviour */
470 r
= getenv_bool("SYSTEMD_IMPORT_BTRFS_SUBVOL");
472 SET_FLAG(arg_import_flags
, IMPORT_BTRFS_SUBVOL
, r
);
473 else if (r
!= -ENXIO
)
474 log_warning_errno(r
, "Failed to parse $SYSTEMD_IMPORT_BTRFS_SUBVOL: %m");
476 r
= getenv_bool("SYSTEMD_IMPORT_BTRFS_QUOTA");
478 SET_FLAG(arg_import_flags
, IMPORT_BTRFS_QUOTA
, r
);
479 else if (r
!= -ENXIO
)
480 log_warning_errno(r
, "Failed to parse $SYSTEMD_IMPORT_BTRFS_QUOTA: %m");
482 r
= getenv_bool("SYSTEMD_IMPORT_SYNC");
484 SET_FLAG(arg_import_flags
, IMPORT_SYNC
, r
);
485 else if (r
!= -ENXIO
)
486 log_warning_errno(r
, "Failed to parse $SYSTEMD_IMPORT_SYNC: %m");
489 static int run(int argc
, char *argv
[]) {
492 setlocale(LC_ALL
, "");
497 r
= parse_argv(argc
, argv
);
501 (void) ignore_signals(SIGPIPE
);
503 return import_main(argc
, argv
);
506 DEFINE_MAIN_FUNCTION(run
);