]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
import: rerrange tarball/fs imports that have a single top-level directory containing...
authorLennart Poettering <lennart@poettering.net>
Thu, 11 Oct 2018 08:01:41 +0000 (10:01 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 26 Nov 2018 17:09:01 +0000 (18:09 +0100)
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
src/import/import-common.c
src/import/import-common.h
src/import/import-fs.c
src/import/import-tar.c

index e2de2c2dd061ce12dfb4beb1fd67378375a3b26d..24cab484a20f88eaeb723331d187c114e5f7691c 100644 (file)
@@ -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;
+}
index 34ed829de01930c2746bf76d13259bb3803e1457..94d224f4129889b557cfd9f8463a403748b680a8 100644 (file)
@@ -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);
index 3836f87570c2bd97178ea161c384063ac7f2aef3..bc2e631e7bd7c626f2fc9d67b490bb7a33e81293 100644 (file)
@@ -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) {
index 0399b03747f8f2fe3dd6b1934724b9efaa13140b..a164417de095eab48e4fbc2340f79c934d812b11 100644 (file)
@@ -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)