summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-10-11 10:01:41 +0200
committerLennart Poettering <lennart@poettering.net>2018-11-26 18:09:01 +0100
commite21b7229ff776891443fe3200ebf4648f3f2940b (patch)
tree3614bdad75940841fc521e007a58e6e55fd12d49
parent1d7579c473dd665df213fbc8a30f032994546246 (diff)
downloadsystemd-e21b7229ff776891443fe3200ebf4648f3f2940b.tar.gz
import: rerrange tarball/fs imports that have a single top-level directory containing the OS tree
Let's handle tarball imports nicer that have a single top-level directory containing the OS tree: let's move everything down during import, so that the OS tree is ready to use automatically. Fixes: #2116
-rw-r--r--src/import/import-common.c109
-rw-r--r--src/import/import-common.h2
-rw-r--r--src/import/import-fs.c4
-rw-r--r--src/import/import-tar.c4
4 files changed, 119 insertions, 0 deletions
diff --git a/src/import/import-common.c b/src/import/import-common.c
index e2de2c2dd0..24cab484a2 100644
--- a/src/import/import-common.c
+++ b/src/import/import-common.c
@@ -5,10 +5,15 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "alloc-util.h"
#include "btrfs-util.h"
#include "capability-util.h"
+#include "dirent-util.h"
#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
#include "import-common.h"
+#include "os-util.h"
#include "process-util.h"
#include "signal-util.h"
#include "util.h"
@@ -147,3 +152,107 @@ int import_fork_tar_c(const char *path, pid_t *ret) {
return TAKE_FD(pipefd[0]);
}
+
+int import_mangle_os_tree(const char *path) {
+ _cleanup_closedir_ DIR *d = NULL, *cd = NULL;
+ _cleanup_free_ char *child = NULL, *t = NULL;
+ const char *joined;
+ struct dirent *de;
+ int r;
+
+ assert(path);
+
+ /* Some tarballs contain a single top-level directory that contains the actual OS directory tree. Try to
+ * recognize this, and move the tree one level up. */
+
+ r = path_is_os_tree(path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", path);
+ if (r > 0) {
+ log_debug("Directory tree '%s' is a valid OS tree.", path);
+ return 0;
+ }
+
+ log_debug("Directory tree '%s' is not recognizable as OS tree, checking whether to rearrange it.", path);
+
+ d = opendir(path);
+ if (!d)
+ return log_error_errno(r, "Failed to open directory '%s': %m", path);
+
+ errno = 0;
+ de = readdir_no_dot(d);
+ if (!de) {
+ if (errno != 0)
+ return log_error_errno(errno, "Failed to iterate through directory '%s': %m", path);
+
+ log_debug("Directory '%s' is empty, leaving it as it is.", path);
+ return 0;
+ }
+
+ child = strdup(de->d_name);
+ if (!child)
+ return log_oom();
+
+ errno = 0;
+ de = readdir_no_dot(d);
+ if (de) {
+ if (errno != 0)
+ return log_error_errno(errno, "Failed to iterate through directory '%s': %m", path);
+
+ log_debug("Directory '%s' does not look like a directory tree, and has multiple children, leaving as it is.", path);
+ return 0;
+ }
+
+ joined = strjoina(path, "/", child);
+ r = path_is_os_tree(joined);
+ if (r == -ENOTDIR) {
+ log_debug("Directory '%s' does not look like a directory tree, and contains a single regular file only, leaving as it is.", path);
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", joined);
+ if (r == 0) {
+ log_debug("Neither '%s' nor '%s' is a valid OS tree, leaving them as they are.", path, joined);
+ return 0;
+ }
+
+ /* Nice, we have checked now:
+ *
+ * 1. The top-level directory does not qualify as OS tree
+ * 1. The top-level directory only contains one item
+ * 2. That item is a directory
+ * 3. And that directory qualifies as OS tree
+ *
+ * Let's now rearrange things, moving everything in the inner directory one level up */
+
+ cd = xopendirat(dirfd(d), child, O_NOFOLLOW);
+ if (!cd)
+ return log_error_errno(errno, "Can't open directory '%s': %m", joined);
+
+ log_info("Rearranging '%s', moving OS tree one directory up.", joined);
+
+ /* Let's rename the child to an unguessable name so that we can be sure all files contained in it can be
+ * safely moved up and won't collide with the name. */
+ r = tempfn_random(child, NULL, &t);
+ if (r < 0)
+ return log_oom();
+ r = rename_noreplace(dirfd(d), child, dirfd(d), t);
+ if (r < 0)
+ return log_error_errno(r, "Unable to rename '%s' to '%s/%s': %m", joined, path, t);
+
+ FOREACH_DIRENT_ALL(de, cd, return log_error_errno(errno, "Failed to iterate through directory '%s': %m", joined)) {
+ if (dot_or_dot_dot(de->d_name))
+ continue;
+
+ r = rename_noreplace(dirfd(cd), de->d_name, dirfd(d), de->d_name);
+ if (r < 0)
+ return log_error_errno(r, "Unable to move '%s/%s/%s' to '%s/%s': %m", path, t, de->d_name, path, de->d_name);
+ }
+
+ if (unlinkat(dirfd(d), t, AT_REMOVEDIR) < 0)
+ return log_error_errno(errno, "Failed to remove temporary directory '%s/%s': %m", path, t);
+
+ log_info("Successfully rearranged OS tree.");
+
+ return 0;
+}
diff --git a/src/import/import-common.h b/src/import/import-common.h
index 34ed829de0..94d224f412 100644
--- a/src/import/import-common.h
+++ b/src/import/import-common.h
@@ -8,3 +8,5 @@ int import_make_read_only(const char *path);
int import_fork_tar_c(const char *path, pid_t *ret);
int import_fork_tar_x(const char *path, pid_t *ret);
+
+int import_mangle_os_tree(const char *path);
diff --git a/src/import/import-fs.c b/src/import/import-fs.c
index 3836f87570..bc2e631e7b 100644
--- a/src/import/import-fs.c
+++ b/src/import/import-fs.c
@@ -192,6 +192,10 @@ static int import_fs(int argc, char *argv[], void *userdata) {
goto finish;
}
+ r = import_mangle_os_tree(temp_path);
+ if (r < 0)
+ goto finish;
+
(void) import_assign_pool_quota_and_warn(temp_path);
if (arg_read_only) {
diff --git a/src/import/import-tar.c b/src/import/import-tar.c
index 0399b03747..a164417de0 100644
--- a/src/import/import-tar.c
+++ b/src/import/import-tar.c
@@ -184,6 +184,10 @@ static int tar_import_finish(TarImport *i) {
return r;
}
+ r = import_mangle_os_tree(i->temp_path);
+ if (r < 0)
+ return r;
+
if (i->read_only) {
r = import_make_read_only(i->temp_path);
if (r < 0)