]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/import-common.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
8 #include "alloc-util.h"
9 #include "btrfs-util.h"
10 #include "capability-util.h"
11 #include "dirent-util.h"
15 #include "import-common.h"
17 #include "process-util.h"
18 #include "signal-util.h"
19 #include "tmpfile-util.h"
22 int import_make_read_only_fd(int fd
) {
27 /* First, let's make this a read-only subvolume if it refers
29 r
= btrfs_subvol_set_read_only_fd(fd
, true);
30 if (IN_SET(r
, -ENOTTY
, -ENOTDIR
, -EINVAL
)) {
33 /* This doesn't refer to a subvolume, or the file
34 * system isn't even btrfs. In that, case fall back to
39 return log_error_errno(errno
, "Failed to stat temporary image: %m");
42 if (fchmod(fd
, st
.st_mode
& 07555) < 0)
43 return log_error_errno(errno
, "Failed to chmod() final image: %m");
48 return log_error_errno(r
, "Failed to make subvolume read-only: %m");
53 int import_make_read_only(const char *path
) {
54 _cleanup_close_
int fd
= 1;
56 fd
= open(path
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
58 return log_error_errno(errno
, "Failed to open %s: %m", path
);
60 return import_make_read_only_fd(fd
);
63 int import_fork_tar_x(const char *path
, pid_t
*ret
) {
64 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
71 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
72 return log_error_errno(errno
, "Failed to create pipe for tar: %m");
74 r
= safe_fork("(tar)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_LOG
, &pid
);
80 (1ULL << CAP_FOWNER
) |
81 (1ULL << CAP_FSETID
) |
83 (1ULL << CAP_SETFCAP
) |
84 (1ULL << CAP_DAC_OVERRIDE
);
88 pipefd
[1] = safe_close(pipefd
[1]);
90 r
= rearrange_stdio(pipefd
[0], -1, STDERR_FILENO
);
92 log_error_errno(r
, "Failed to rearrange stdin/stdout: %m");
96 if (unshare(CLONE_NEWNET
) < 0)
97 log_error_errno(errno
, "Failed to lock tar into network namespace, ignoring: %m");
99 r
= capability_bounding_set_drop(retain
, true);
101 log_error_errno(r
, "Failed to drop capabilities, ignoring: %m");
103 execlp("tar", "tar", "--numeric-owner", "-C", path
, "-px", "--xattrs", "--xattrs-include=*", NULL
);
104 log_error_errno(errno
, "Failed to execute tar: %m");
110 return TAKE_FD(pipefd
[1]);
113 int import_fork_tar_c(const char *path
, pid_t
*ret
) {
114 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
121 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
122 return log_error_errno(errno
, "Failed to create pipe for tar: %m");
124 r
= safe_fork("(tar)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_LOG
, &pid
);
128 uint64_t retain
= (1ULL << CAP_DAC_OVERRIDE
);
132 pipefd
[0] = safe_close(pipefd
[0]);
134 r
= rearrange_stdio(-1, pipefd
[1], STDERR_FILENO
);
136 log_error_errno(r
, "Failed to rearrange stdin/stdout: %m");
140 if (unshare(CLONE_NEWNET
) < 0)
141 log_error_errno(errno
, "Failed to lock tar into network namespace, ignoring: %m");
143 r
= capability_bounding_set_drop(retain
, true);
145 log_error_errno(r
, "Failed to drop capabilities, ignoring: %m");
147 execlp("tar", "tar", "-C", path
, "-c", "--xattrs", "--xattrs-include=*", ".", NULL
);
148 log_error_errno(errno
, "Failed to execute tar: %m");
154 return TAKE_FD(pipefd
[0]);
157 int import_mangle_os_tree(const char *path
) {
158 _cleanup_closedir_
DIR *d
= NULL
, *cd
= NULL
;
159 _cleanup_free_
char *child
= NULL
, *t
= NULL
;
166 /* Some tarballs contain a single top-level directory that contains the actual OS directory tree. Try to
167 * recognize this, and move the tree one level up. */
169 r
= path_is_os_tree(path
);
171 return log_error_errno(r
, "Failed to determine whether '%s' is an OS tree: %m", path
);
173 log_debug("Directory tree '%s' is a valid OS tree.", path
);
177 log_debug("Directory tree '%s' is not recognizable as OS tree, checking whether to rearrange it.", path
);
181 return log_error_errno(r
, "Failed to open directory '%s': %m", path
);
184 de
= readdir_no_dot(d
);
187 return log_error_errno(errno
, "Failed to iterate through directory '%s': %m", path
);
189 log_debug("Directory '%s' is empty, leaving it as it is.", path
);
193 child
= strdup(de
->d_name
);
198 de
= readdir_no_dot(d
);
201 return log_error_errno(errno
, "Failed to iterate through directory '%s': %m", path
);
203 log_debug("Directory '%s' does not look like a directory tree, and has multiple children, leaving as it is.", path
);
207 joined
= strjoina(path
, "/", child
);
208 r
= path_is_os_tree(joined
);
210 log_debug("Directory '%s' does not look like a directory tree, and contains a single regular file only, leaving as it is.", path
);
214 return log_error_errno(r
, "Failed to determine whether '%s' is an OS tree: %m", joined
);
216 log_debug("Neither '%s' nor '%s' is a valid OS tree, leaving them as they are.", path
, joined
);
220 /* Nice, we have checked now:
222 * 1. The top-level directory does not qualify as OS tree
223 * 1. The top-level directory only contains one item
224 * 2. That item is a directory
225 * 3. And that directory qualifies as OS tree
227 * Let's now rearrange things, moving everything in the inner directory one level up */
229 cd
= xopendirat(dirfd(d
), child
, O_NOFOLLOW
);
231 return log_error_errno(errno
, "Can't open directory '%s': %m", joined
);
233 log_info("Rearranging '%s', moving OS tree one directory up.", joined
);
235 /* Let's rename the child to an unguessable name so that we can be sure all files contained in it can be
236 * safely moved up and won't collide with the name. */
237 r
= tempfn_random(child
, NULL
, &t
);
240 r
= rename_noreplace(dirfd(d
), child
, dirfd(d
), t
);
242 return log_error_errno(r
, "Unable to rename '%s' to '%s/%s': %m", joined
, path
, t
);
244 FOREACH_DIRENT_ALL(de
, cd
, return log_error_errno(errno
, "Failed to iterate through directory '%s': %m", joined
)) {
245 if (dot_or_dot_dot(de
->d_name
))
248 r
= rename_noreplace(dirfd(cd
), de
->d_name
, dirfd(d
), de
->d_name
);
250 return log_error_errno(r
, "Unable to move '%s/%s/%s' to '%s/%s': %m", path
, t
, de
->d_name
, path
, de
->d_name
);
253 if (unlinkat(dirfd(d
), t
, AT_REMOVEDIR
) < 0)
254 return log_error_errno(errno
, "Failed to remove temporary directory '%s/%s': %m", path
, t
);
256 log_info("Successfully rearranged OS tree.");