]>
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] = EBADF_PAIR
;
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_full("(tar)",
40 (int[]) { pipefd
[0], -EBADF
, STDERR_FILENO
},
42 FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG_SIGTERM
|FORK_REARRANGE_STDIO
|FORK_LOG
, &pid
);
46 const char *cmdline
[] = {
55 use_selinux
? "--selinux" : "--no-selinux",
61 (1ULL << CAP_FOWNER
) |
62 (1ULL << CAP_FSETID
) |
64 (1ULL << CAP_SETFCAP
) |
65 (1ULL << CAP_DAC_OVERRIDE
);
69 if (unshare(CLONE_NEWNET
) < 0)
70 log_warning_errno(errno
, "Failed to lock tar into network namespace, ignoring: %m");
72 r
= capability_bounding_set_drop(retain
, true);
74 log_warning_errno(r
, "Failed to drop capabilities, ignoring: %m");
76 /* Try "gtar" before "tar". We only test things upstream with GNU tar. Some distros appear to
77 * install a different implementation as "tar" (in particular some that do not support the
78 * same command line switches), but then provide "gtar" as alias for the real thing, hence
79 * let's prefer that. (Yes, it's a bad idea they do that, given they don't provide equivalent
80 * command line support, but we are not here to argue, let's just expose the same
81 * behaviour/implementation everywhere.) */
82 execvp("gtar", (char* const*) cmdline
);
83 execvp("tar", (char* const*) cmdline
);
85 log_error_errno(errno
, "Failed to execute tar: %m");
91 return TAKE_FD(pipefd
[1]);
94 int import_fork_tar_c(const char *path
, pid_t
*ret
) {
95 _cleanup_close_pair_
int pipefd
[2] = EBADF_PAIR
;
103 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
104 return log_error_errno(errno
, "Failed to create pipe for tar: %m");
106 use_selinux
= mac_selinux_use();
108 r
= safe_fork_full("(tar)",
109 (int[]) { -EBADF
, pipefd
[1], STDERR_FILENO
},
111 FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG_SIGTERM
|FORK_REARRANGE_STDIO
|FORK_LOG
, &pid
);
115 const char *cmdline
[] = {
120 "--xattrs-include=*",
121 use_selinux
? "--selinux" : "--no-selinux",
126 uint64_t retain
= (1ULL << CAP_DAC_OVERRIDE
);
130 if (unshare(CLONE_NEWNET
) < 0)
131 log_error_errno(errno
, "Failed to lock tar into network namespace, ignoring: %m");
133 r
= capability_bounding_set_drop(retain
, true);
135 log_error_errno(r
, "Failed to drop capabilities, ignoring: %m");
137 execvp("gtar", (char* const*) cmdline
);
138 execvp("tar", (char* const*) cmdline
);
140 log_error_errno(errno
, "Failed to execute tar: %m");
146 return TAKE_FD(pipefd
[0]);
149 int import_mangle_os_tree(const char *path
) {
150 _cleanup_free_
char *child
= NULL
, *t
= NULL
, *joined
= NULL
;
151 _cleanup_closedir_
DIR *d
= NULL
, *cd
= NULL
;
158 /* Some tarballs contain a single top-level directory that contains the actual OS directory tree. Try to
159 * recognize this, and move the tree one level up. */
161 r
= path_is_os_tree(path
);
163 return log_error_errno(r
, "Failed to determine whether '%s' is an OS tree: %m", path
);
165 log_debug("Directory tree '%s' is a valid OS tree.", path
);
169 log_debug("Directory tree '%s' is not recognizable as OS tree, checking whether to rearrange it.", path
);
173 return log_error_errno(r
, "Failed to open directory '%s': %m", path
);
176 dent
= readdir_no_dot(d
);
179 return log_error_errno(errno
, "Failed to iterate through directory '%s': %m", path
);
181 log_debug("Directory '%s' is empty, leaving it as it is.", path
);
185 child
= strdup(dent
->d_name
);
190 dent
= readdir_no_dot(d
);
193 return log_error_errno(errno
, "Failed to iterate through directory '%s': %m", path
);
195 log_debug("Directory '%s' does not look like an OS tree, and has multiple children, leaving as it is.", path
);
199 if (fstatat(dirfd(d
), child
, &st
, AT_SYMLINK_NOFOLLOW
) < 0)
200 return log_debug_errno(errno
, "Failed to stat file '%s/%s': %m", path
, child
);
201 r
= stat_verify_directory(&st
);
203 log_debug_errno(r
, "Child '%s' of directory '%s' is not a directory, leaving things as they are.", child
, path
);
207 joined
= path_join(path
, child
);
210 r
= path_is_os_tree(joined
);
212 log_debug("Directory '%s' does not look like an OS tree, and contains a single regular file only, leaving as it is.", path
);
216 return log_error_errno(r
, "Failed to determine whether '%s' is an OS tree: %m", joined
);
218 log_debug("Neither '%s' nor '%s' is a valid OS tree, leaving them as they are.", path
, joined
);
222 /* Nice, we have checked now:
224 * 1. The top-level directory does not qualify as OS tree
225 * 1. The top-level directory only contains one item
226 * 2. That item is a directory
227 * 3. And that directory qualifies as OS tree
229 * Let's now rearrange things, moving everything in the inner directory one level up */
231 cd
= xopendirat(dirfd(d
), child
, O_NOFOLLOW
);
233 return log_error_errno(errno
, "Can't open directory '%s': %m", joined
);
235 log_info("Rearranging '%s', moving OS tree one directory up.", joined
);
237 /* Let's rename the child to an unguessable name so that we can be sure all files contained in it can be
238 * safely moved up and won't collide with the name. */
239 r
= tempfn_random(child
, NULL
, &t
);
242 r
= rename_noreplace(dirfd(d
), child
, dirfd(d
), t
);
244 return log_error_errno(r
, "Unable to rename '%s' to '%s/%s': %m", joined
, path
, t
);
246 FOREACH_DIRENT_ALL(de
, cd
, return log_error_errno(errno
, "Failed to iterate through directory '%s': %m", joined
)) {
247 if (dot_or_dot_dot(de
->d_name
))
250 r
= rename_noreplace(dirfd(cd
), de
->d_name
, dirfd(d
), de
->d_name
);
252 return log_error_errno(r
, "Unable to move '%s/%s/%s' to '%s/%s': %m", path
, t
, de
->d_name
, path
, de
->d_name
);
255 if (unlinkat(dirfd(d
), t
, AT_REMOVEDIR
) < 0)
256 return log_error_errno(errno
, "Failed to remove temporary directory '%s/%s': %m", path
, t
);
258 r
= futimens(dirfd(d
), (struct timespec
[2]) { st
.st_atim
, st
.st_mtim
});
260 log_debug_errno(r
, "Failed to adjust top-level timestamps '%s', ignoring: %m", path
);
262 r
= fchmod_and_chown(dirfd(d
), st
.st_mode
, st
.st_uid
, st
.st_gid
);
264 return log_error_errno(r
, "Failed to adjust top-level directory mode/ownership '%s': %m", path
);
266 log_info("Successfully rearranged OS tree.");
271 bool import_validate_local(const char *name
, ImportFlags flags
) {
273 /* By default we insist on a valid hostname for naming images. But optionally we relax that, in which
274 * case it can be any path name */
276 if (FLAGS_SET(flags
, IMPORT_DIRECT
))
277 return path_is_valid(name
);
279 return image_name_is_valid(name
);
282 static int interrupt_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
283 log_notice("Transfer aborted.");
284 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
288 int import_allocate_event_with_signals(sd_event
**ret
) {
289 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
294 r
= sd_event_default(&event
);
296 return log_error_errno(r
, "Failed to allocate event loop: %m");
298 (void) sd_event_add_signal(event
, NULL
, SIGTERM
|SD_EVENT_SIGNAL_PROCMASK
, interrupt_signal_handler
, NULL
);
299 (void) sd_event_add_signal(event
, NULL
, SIGINT
|SD_EVENT_SIGNAL_PROCMASK
, interrupt_signal_handler
, NULL
);
301 *ret
= TAKE_PTR(event
);