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
This commit is contained in:
Lennart Poettering 2018-10-11 10:01:41 +02:00
parent 1d7579c473
commit e21b7229ff
4 changed files with 119 additions and 0 deletions

View 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;
}

View 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);

View 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) {

View 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)