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:
parent
1d7579c473
commit
e21b7229ff
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue