]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/import-common.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "alloc-util.h"
9 #include "btrfs-util.h"
10 #include "capability-util.h"
11 #include "chattr-util.h"
12 #include "dirent-util.h"
16 #include "hostname-util.h"
17 #include "import-common.h"
19 #include "process-util.h"
20 #include "selinux-util.h"
21 #include "signal-util.h"
22 #include "stat-util.h"
23 #include "tmpfile-util.h"
25 int import_fork_tar_x(const char *path
, pid_t
*ret
) {
26 _cleanup_close_pair_
int pipefd
[2] = PIPE_EBADF
;
34 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
35 return log_error_errno(errno
, "Failed to create pipe for tar: %m");
37 use_selinux
= mac_selinux_use();
39 r
= safe_fork("(tar)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_LOG
, &pid
);
43 const char *cmdline
[] = {
51 use_selinux
? "--selinux" : "--no-selinux",
57 (1ULL << CAP_FOWNER
) |
58 (1ULL << CAP_FSETID
) |
60 (1ULL << CAP_SETFCAP
) |
61 (1ULL << CAP_DAC_OVERRIDE
);
65 pipefd
[1] = safe_close(pipefd
[1]);
67 r
= rearrange_stdio(TAKE_FD(pipefd
[0]), -EBADF
, STDERR_FILENO
);
69 log_error_errno(r
, "Failed to rearrange stdin/stdout: %m");
73 if (unshare(CLONE_NEWNET
) < 0)
74 log_warning_errno(errno
, "Failed to lock tar into network namespace, ignoring: %m");
76 r
= capability_bounding_set_drop(retain
, true);
78 log_warning_errno(r
, "Failed to drop capabilities, ignoring: %m");
80 /* Try "gtar" before "tar". We only test things upstream with GNU tar. Some distros appear to
81 * install a different implementation as "tar" (in particular some that do not support the
82 * same command line switches), but then provide "gtar" as alias for the real thing, hence
83 * let's prefer that. (Yes, it's a bad idea they do that, given they don't provide equivalent
84 * command line support, but we are not here to argue, let's just expose the same
85 * behaviour/implementation everywhere.) */
86 execvp("gtar", (char* const*) cmdline
);
87 execvp("tar", (char* const*) cmdline
);
89 log_error_errno(errno
, "Failed to execute tar: %m");
95 return TAKE_FD(pipefd
[1]);
98 int import_fork_tar_c(const char *path
, pid_t
*ret
) {
99 _cleanup_close_pair_
int pipefd
[2] = PIPE_EBADF
;
107 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
108 return log_error_errno(errno
, "Failed to create pipe for tar: %m");
110 use_selinux
= mac_selinux_use();
112 r
= safe_fork("(tar)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_LOG
, &pid
);
116 const char *cmdline
[] = {
121 "--xattrs-include=*",
122 use_selinux
? "--selinux" : "--no-selinux",
127 uint64_t retain
= (1ULL << CAP_DAC_OVERRIDE
);
131 pipefd
[0] = safe_close(pipefd
[0]);
133 r
= rearrange_stdio(-EBADF
, TAKE_FD(pipefd
[1]), STDERR_FILENO
);
135 log_error_errno(r
, "Failed to rearrange stdin/stdout: %m");
139 if (unshare(CLONE_NEWNET
) < 0)
140 log_error_errno(errno
, "Failed to lock tar into network namespace, ignoring: %m");
142 r
= capability_bounding_set_drop(retain
, true);
144 log_error_errno(r
, "Failed to drop capabilities, ignoring: %m");
146 execvp("gtar", (char* const*) cmdline
);
147 execvp("tar", (char* const*) cmdline
);
149 log_error_errno(errno
, "Failed to execute tar: %m");
155 return TAKE_FD(pipefd
[0]);
158 int import_mangle_os_tree(const char *path
) {
159 _cleanup_free_
char *child
= NULL
, *t
= NULL
, *joined
= NULL
;
160 _cleanup_closedir_
DIR *d
= NULL
, *cd
= NULL
;
167 /* Some tarballs contain a single top-level directory that contains the actual OS directory tree. Try to
168 * recognize this, and move the tree one level up. */
170 r
= path_is_os_tree(path
);
172 return log_error_errno(r
, "Failed to determine whether '%s' is an OS tree: %m", path
);
174 log_debug("Directory tree '%s' is a valid OS tree.", path
);
178 log_debug("Directory tree '%s' is not recognizable as OS tree, checking whether to rearrange it.", path
);
182 return log_error_errno(r
, "Failed to open directory '%s': %m", path
);
185 dent
= readdir_no_dot(d
);
188 return log_error_errno(errno
, "Failed to iterate through directory '%s': %m", path
);
190 log_debug("Directory '%s' is empty, leaving it as it is.", path
);
194 child
= strdup(dent
->d_name
);
199 dent
= readdir_no_dot(d
);
202 return log_error_errno(errno
, "Failed to iterate through directory '%s': %m", path
);
204 log_debug("Directory '%s' does not look like an OS tree, and has multiple children, leaving as it is.", path
);
208 if (fstatat(dirfd(d
), child
, &st
, AT_SYMLINK_NOFOLLOW
) < 0)
209 return log_debug_errno(errno
, "Failed to stat file '%s/%s': %m", path
, child
);
210 r
= stat_verify_directory(&st
);
212 log_debug_errno(r
, "Child '%s' of directory '%s' is not a directory, leaving things as they are.", child
, path
);
216 joined
= path_join(path
, child
);
219 r
= path_is_os_tree(joined
);
221 log_debug("Directory '%s' does not look like an OS tree, and contains a single regular file only, leaving as it is.", path
);
225 return log_error_errno(r
, "Failed to determine whether '%s' is an OS tree: %m", joined
);
227 log_debug("Neither '%s' nor '%s' is a valid OS tree, leaving them as they are.", path
, joined
);
231 /* Nice, we have checked now:
233 * 1. The top-level directory does not qualify as OS tree
234 * 1. The top-level directory only contains one item
235 * 2. That item is a directory
236 * 3. And that directory qualifies as OS tree
238 * Let's now rearrange things, moving everything in the inner directory one level up */
240 cd
= xopendirat(dirfd(d
), child
, O_NOFOLLOW
);
242 return log_error_errno(errno
, "Can't open directory '%s': %m", joined
);
244 log_info("Rearranging '%s', moving OS tree one directory up.", joined
);
246 /* Let's rename the child to an unguessable name so that we can be sure all files contained in it can be
247 * safely moved up and won't collide with the name. */
248 r
= tempfn_random(child
, NULL
, &t
);
251 r
= rename_noreplace(dirfd(d
), child
, dirfd(d
), t
);
253 return log_error_errno(r
, "Unable to rename '%s' to '%s/%s': %m", joined
, path
, t
);
255 FOREACH_DIRENT_ALL(de
, cd
, return log_error_errno(errno
, "Failed to iterate through directory '%s': %m", joined
)) {
256 if (dot_or_dot_dot(de
->d_name
))
259 r
= rename_noreplace(dirfd(cd
), de
->d_name
, dirfd(d
), de
->d_name
);
261 return log_error_errno(r
, "Unable to move '%s/%s/%s' to '%s/%s': %m", path
, t
, de
->d_name
, path
, de
->d_name
);
264 if (unlinkat(dirfd(d
), t
, AT_REMOVEDIR
) < 0)
265 return log_error_errno(errno
, "Failed to remove temporary directory '%s/%s': %m", path
, t
);
267 r
= futimens(dirfd(d
), (struct timespec
[2]) { st
.st_atim
, st
.st_mtim
});
269 log_debug_errno(r
, "Failed to adjust top-level timestamps '%s', ignoring: %m", path
);
271 r
= fchmod_and_chown(dirfd(d
), st
.st_mode
, st
.st_uid
, st
.st_gid
);
273 return log_error_errno(r
, "Failed to adjust top-level directory mode/ownership '%s': %m", path
);
275 log_info("Successfully rearranged OS tree.");
280 bool import_validate_local(const char *name
, ImportFlags flags
) {
282 /* By default we insist on a valid hostname for naming images. But optionally we relax that, in which
283 * case it can be any path name */
285 if (FLAGS_SET(flags
, IMPORT_DIRECT
))
286 return path_is_valid(name
);
288 return hostname_is_valid(name
, 0);
291 static int interrupt_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
292 log_notice("Transfer aborted.");
293 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
297 int import_allocate_event_with_signals(sd_event
**ret
) {
298 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
303 r
= sd_event_default(&event
);
305 return log_error_errno(r
, "Failed to allocate event loop: %m");
307 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
308 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
309 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
311 *ret
= TAKE_PTR(event
);