1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
8 #include "btrfs-util.h"
9 #include "discover-image.h"
11 #include "format-util.h"
13 #include "hostname-util.h"
14 #include "import-common.h"
15 #include "import-util.h"
16 #include "install-file.h"
17 #include "main-func.h"
18 #include "mkdir-label.h"
19 #include "parse-argument.h"
20 #include "ratelimit.h"
22 #include "signal-util.h"
23 #include "string-util.h"
24 #include "terminal-util.h"
25 #include "tmpfile-util.h"
28 static bool arg_force
= false;
29 static bool arg_read_only
= false;
30 static bool arg_btrfs_subvol
= true;
31 static bool arg_btrfs_quota
= true;
32 static bool arg_sync
= true;
33 static bool arg_direct
= false;
34 static const char *arg_image_root
= "/var/lib/machines";
36 typedef struct ProgressInfo
{
41 bool logged_incomplete
;
44 static void progress_info_free(ProgressInfo
*p
) {
48 static void progress_show(ProgressInfo
*p
) {
51 /* Show progress only every now and then. */
52 if (!ratelimit_below(&p
->limit
))
55 /* Suppress the first message, start with the second one */
61 /* Mention the list is incomplete before showing first output. */
62 if (!p
->logged_incomplete
) {
63 log_notice("(Note: file list shown below is incomplete, and is intended as sporadic progress report only.)");
64 p
->logged_incomplete
= true;
68 log_info("Copying tree, currently at '%s'...", p
->path
);
70 log_info("Copying tree, currently at '%s' (@%s)...", p
->path
, FORMAT_BYTES(p
->size
));
73 static int progress_path(const char *path
, const struct stat
*st
, void *userdata
) {
74 ProgressInfo
*p
= ASSERT_PTR(userdata
);
77 r
= free_and_strdup(&p
->path
, path
);
87 static int progress_bytes(uint64_t nbytes
, void *userdata
) {
88 ProgressInfo
*p
= ASSERT_PTR(userdata
);
90 assert(p
->size
!= UINT64_MAX
);
98 static int import_fs(int argc
, char *argv
[], void *userdata
) {
99 _cleanup_(rm_rf_subvolume_and_freep
) char *temp_path
= NULL
;
100 _cleanup_(progress_info_free
) ProgressInfo progress
= {};
101 _cleanup_free_
char *l
= NULL
, *final_path
= NULL
;
102 const char *path
= NULL
, *local
= NULL
, *dest
= NULL
;
103 _cleanup_close_
int open_fd
= -1;
107 path
= empty_or_dash_to_null(argv
[1]);
110 local
= empty_or_dash_to_null(argv
[2]);
112 r
= path_extract_filename(path
, &l
);
114 return log_error_errno(r
, "Failed to extract filename from path '%s': %m", path
);
121 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No local path specified.");
123 if (path_is_absolute(local
))
124 final_path
= strdup(local
);
126 final_path
= path_join(arg_image_root
, local
);
130 if (!path_is_valid(final_path
))
131 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
132 "Local path name '%s' is not valid.", final_path
);
135 if (!hostname_is_valid(local
, 0))
136 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
137 "Local image name '%s' is not valid.", local
);
141 final_path
= path_join(arg_image_root
, local
);
146 r
= image_find(IMAGE_MACHINE
, local
, NULL
, NULL
);
149 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
151 return log_error_errno(SYNTHETIC_ERRNO(EEXIST
),
152 "Image '%s' already exists.", local
);
157 open_fd
= open(path
, O_DIRECTORY
|O_RDONLY
|O_CLOEXEC
);
159 return log_error_errno(errno
, "Failed to open directory to import: %m");
163 log_info("Importing '%s', saving as '%s'.", path
, local
);
165 _cleanup_free_
char *pretty
= NULL
;
169 (void) fd_get_path(fd
, &pretty
);
170 log_info("Importing '%s', saving as '%s'.", strempty(pretty
), local
);
174 log_info("File system synchronization on completion is off.");
178 (void) rm_rf(final_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
182 r
= tempfn_random(final_path
, NULL
, &temp_path
);
189 (void) mkdir_parents_label(dest
, 0700);
191 progress
.limit
= (const RateLimit
) { 200*USEC_PER_MSEC
, 1 };
194 BLOCK_SIGNALS(SIGINT
, SIGTERM
);
196 if (arg_btrfs_subvol
)
197 r
= btrfs_subvol_snapshot_fd_full(
200 BTRFS_SNAPSHOT_FALLBACK_COPY
|
201 BTRFS_SNAPSHOT_FALLBACK_DIRECTORY
|
202 BTRFS_SNAPSHOT_RECURSIVE
|
203 BTRFS_SNAPSHOT_SIGINT
|
204 BTRFS_SNAPSHOT_SIGTERM
,
209 r
= copy_directory_fd_full(
217 (arg_direct
? COPY_MERGE_EMPTY
: 0),
221 if (r
== -EINTR
) /* SIGINT/SIGTERM hit */
222 return log_error_errno(r
, "Copy cancelled.");
224 return log_error_errno(r
, "Failed to copy directory: %m");
227 r
= import_mangle_os_tree(dest
);
231 if (arg_btrfs_quota
) {
233 (void) import_assign_pool_quota_and_warn(arg_image_root
);
234 (void) import_assign_pool_quota_and_warn(dest
);
237 r
= install_file(AT_FDCWD
, dest
,
238 AT_FDCWD
, arg_direct
? NULL
: final_path
, /* pass NULL as target in case of direct
239 * mode since file is already in place */
240 (arg_force
? INSTALL_REPLACE
: 0) |
241 (arg_read_only
? INSTALL_READ_ONLY
: 0) |
242 (arg_sync
? INSTALL_SYNCFS
: 0));
244 return log_error_errno(r
, "Failed install directory as '%s': %m", final_path
);
246 temp_path
= mfree(temp_path
);
248 log_info("Directory '%s successfully installed. Exiting.", final_path
);
252 static int help(int argc
, char *argv
[], void *userdata
) {
254 printf("%1$s [OPTIONS...] {COMMAND} ...\n"
255 "\n%4$sImport container images from a file system directories.%5$s\n"
256 "\n%2$sCommands:%3$s\n"
257 " run DIRECTORY [NAME] Import a directory\n"
258 "\n%2$sOptions:%3$s\n"
259 " -h --help Show this help\n"
260 " --version Show package version\n"
261 " --force Force creation of image\n"
262 " --image-root=PATH Image root directory\n"
263 " --read-only Create a read-only image\n"
264 " --direct Import directly to specified directory\n"
265 " --btrfs-subvol=BOOL Controls whether to create a btrfs subvolume\n"
266 " instead of a directory\n"
267 " --btrfs-quota=BOOL Controls whether to set up quota for btrfs\n"
269 " --sync=BOOL Controls whether to sync() before completing\n",
270 program_invocation_short_name
,
279 static int parse_argv(int argc
, char *argv
[]) {
292 static const struct option options
[] = {
293 { "help", no_argument
, NULL
, 'h' },
294 { "version", no_argument
, NULL
, ARG_VERSION
},
295 { "force", no_argument
, NULL
, ARG_FORCE
},
296 { "image-root", required_argument
, NULL
, ARG_IMAGE_ROOT
},
297 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
298 { "direct", no_argument
, NULL
, ARG_DIRECT
},
299 { "btrfs-subvol", required_argument
, NULL
, ARG_BTRFS_SUBVOL
},
300 { "btrfs-quota", required_argument
, NULL
, ARG_BTRFS_QUOTA
},
301 { "sync", required_argument
, NULL
, ARG_SYNC
},
310 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
315 return help(0, NULL
, NULL
);
325 arg_image_root
= optarg
;
329 arg_read_only
= true;
336 case ARG_BTRFS_SUBVOL
:
337 r
= parse_boolean_argument("--btrfs-subvol=", optarg
, &arg_btrfs_subvol
);
343 case ARG_BTRFS_QUOTA
:
344 r
= parse_boolean_argument("--btrfs-quota=", optarg
, &arg_btrfs_quota
);
351 r
= parse_boolean_argument("--sync=", optarg
, &arg_sync
);
361 assert_not_reached();
367 static int import_fs_main(int argc
, char *argv
[]) {
369 static const Verb verbs
[] = {
370 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
371 { "run", 2, 3, 0, import_fs
},
375 return dispatch_verb(argc
, argv
, verbs
, NULL
);
378 static int run(int argc
, char *argv
[]) {
381 setlocale(LC_ALL
, "");
382 log_parse_environment();
385 r
= parse_argv(argc
, argv
);
389 return import_fs_main(argc
, argv
);
392 DEFINE_MAIN_FUNCTION(run
);