mirror of
https://github.com/systemd/systemd
synced 2024-10-15 04:24:19 +00:00
Merge pull request #26500 from DaanDeMeyer/repart-mountpoints
repart: Several CopyFiles= improvements
This commit is contained in:
commit
97be742d95
|
@ -453,6 +453,10 @@
|
|||
exclude multiple files or directories from host from being copied into the newly formatted file
|
||||
system.</para>
|
||||
|
||||
<para>If the path is a directory and ends with <literal>/</literal>, only the directory's
|
||||
contents are excluded but not the directory itself. If the path is a directory and does not end with
|
||||
<literal>/</literal>, both the directory and its contents are excluded.</para>
|
||||
|
||||
<para>When
|
||||
<citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
is invoked with the <option>--image=</option> or <option>--root=</option> command line switches the
|
||||
|
|
|
@ -3787,7 +3787,7 @@ static int context_copy_blocks(Context *context) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int do_copy_files(Partition *p, const char *root, const Set *denylist) {
|
||||
static int do_copy_files(Partition *p, const char *root, Hashmap *denylist) {
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
@ -3932,7 +3932,7 @@ static bool partition_needs_populate(Partition *p) {
|
|||
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories);
|
||||
}
|
||||
|
||||
static int partition_populate_directory(Partition *p, const Set *denylist, char **ret) {
|
||||
static int partition_populate_directory(Partition *p, Hashmap *denylist, char **ret) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
|
||||
const char *vt;
|
||||
int r;
|
||||
|
@ -3963,7 +3963,7 @@ static int partition_populate_directory(Partition *p, const Set *denylist, char
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int partition_populate_filesystem(Partition *p, const char *node, const Set *denylist) {
|
||||
static int partition_populate_filesystem(Partition *p, const char *node, Hashmap *denylist) {
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
@ -4010,8 +4010,37 @@ static int partition_populate_filesystem(Partition *p, const char *node, const S
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int make_copy_files_denylist(Context *context, const Partition *p, Set **ret) {
|
||||
_cleanup_set_free_ Set *denylist = NULL;
|
||||
static int add_exclude_path(const char *path, Hashmap **denylist, DenyType type) {
|
||||
_cleanup_free_ struct stat *st = NULL;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(denylist);
|
||||
|
||||
st = new(struct stat, 1);
|
||||
if (!st)
|
||||
return log_oom();
|
||||
|
||||
r = chase_symlinks_and_stat(path, arg_root, CHASE_PREFIX_ROOT, NULL, st, NULL);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to stat source file '%s%s': %m",
|
||||
strempty(arg_root), path);
|
||||
|
||||
if (hashmap_contains(*denylist, st))
|
||||
return 0;
|
||||
|
||||
if (hashmap_ensure_put(denylist, &inode_hash_ops, st, INT_TO_PTR(type)) < 0)
|
||||
return log_oom();
|
||||
|
||||
TAKE_PTR(st);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_copy_files_denylist(Context *context, const Partition *p, Hashmap **ret) {
|
||||
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
|
@ -4019,55 +4048,33 @@ static int make_copy_files_denylist(Context *context, const Partition *p, Set **
|
|||
assert(ret);
|
||||
|
||||
LIST_FOREACH(partitions, q, context->partitions) {
|
||||
if (p == q)
|
||||
continue;
|
||||
|
||||
const char *sources = gpt_partition_type_mountpoint_nulstr(q->type);
|
||||
if (!sources)
|
||||
continue;
|
||||
|
||||
NULSTR_FOREACH(s, sources) {
|
||||
_cleanup_free_ char *d = NULL;
|
||||
struct stat st;
|
||||
/* Exclude the children of partition mount points so that the nested partition mount
|
||||
* point itself still ends up in the upper partition. */
|
||||
|
||||
r = chase_symlinks_and_stat(s, arg_root, CHASE_PREFIX_ROOT, NULL, &st, NULL);
|
||||
if (r == -ENOENT)
|
||||
continue;
|
||||
r = add_exclude_path(s, &denylist, DENY_CONTENTS);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to stat source file '%s%s': %m",
|
||||
strempty(arg_root), s);
|
||||
|
||||
if (set_contains(denylist, &st))
|
||||
continue;
|
||||
|
||||
d = memdup(&st, sizeof(st));
|
||||
if (!d)
|
||||
return log_oom();
|
||||
if (set_ensure_put(&denylist, &inode_hash_ops, d) < 0)
|
||||
return log_oom();
|
||||
|
||||
TAKE_PTR(d);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
STRV_FOREACH(e, p->exclude_files) {
|
||||
_cleanup_free_ char *d = NULL;
|
||||
struct stat st;
|
||||
|
||||
r = chase_symlinks_and_stat(*e, arg_root, CHASE_PREFIX_ROOT, NULL, &st, NULL);
|
||||
if (r == -ENOENT)
|
||||
continue;
|
||||
FOREACH_STRING(s, "proc", "sys", "dev", "tmp", "run", "var/tmp") {
|
||||
r = add_exclude_path(s, &denylist, DENY_CONTENTS);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to stat source file '%s%s': %m",
|
||||
strempty(arg_root), *e);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (set_contains(denylist, &st))
|
||||
continue;
|
||||
|
||||
d = memdup(&st, sizeof(st));
|
||||
if (!d)
|
||||
return log_oom();
|
||||
if (set_ensure_put(&denylist, &inode_hash_ops, d) < 0)
|
||||
return log_oom();
|
||||
|
||||
TAKE_PTR(d);
|
||||
STRV_FOREACH(e, p->exclude_files) {
|
||||
r = add_exclude_path(*e, &denylist, endswith(*e, "/") ? DENY_CONTENTS : DENY_INODE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(denylist);
|
||||
|
@ -4082,7 +4089,7 @@ static int context_mkfs(Context *context) {
|
|||
/* Make a file system */
|
||||
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
_cleanup_set_free_ Set *denylist = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
||||
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
|
||||
_cleanup_(partition_target_freep) PartitionTarget *t = NULL;
|
||||
|
||||
|
@ -5402,7 +5409,7 @@ static int context_minimize(Context *context) {
|
|||
return log_error_errno(r, "Could not determine temporary directory: %m");
|
||||
|
||||
LIST_FOREACH(partitions, p, context->partitions) {
|
||||
_cleanup_set_free_ Set *denylist = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
||||
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
|
||||
_cleanup_(unlink_and_freep) char *temp = NULL;
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
|
|
|
@ -694,7 +694,7 @@ static int fd_copy_tree_generic(
|
|||
uid_t override_uid,
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags,
|
||||
const Set *denylist,
|
||||
Hashmap *denylist,
|
||||
HardlinkContext *hardlink_context,
|
||||
const char *display_path,
|
||||
copy_progress_path_t progress_path,
|
||||
|
@ -897,7 +897,7 @@ static int fd_copy_directory(
|
|||
uid_t override_uid,
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags,
|
||||
const Set *denylist,
|
||||
Hashmap *denylist,
|
||||
HardlinkContext *hardlink_context,
|
||||
const char *display_path,
|
||||
copy_progress_path_t progress_path,
|
||||
|
@ -971,6 +971,11 @@ static int fd_copy_directory(
|
|||
|
||||
r = 0;
|
||||
|
||||
if (PTR_TO_INT(hashmap_get(denylist, st)) == DENY_CONTENTS) {
|
||||
log_debug("%s is in the denylist, not recursing", from);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
FOREACH_DIRENT_ALL(de, d, return -errno) {
|
||||
const char *child_display_path = NULL;
|
||||
_cleanup_free_ char *dp = NULL;
|
||||
|
@ -1000,8 +1005,8 @@ static int fd_copy_directory(
|
|||
return r;
|
||||
}
|
||||
|
||||
if (set_contains(denylist, &buf)) {
|
||||
log_debug("%s/%s is in the denylist, skipping", from, de->d_name);
|
||||
if (PTR_TO_INT(hashmap_get(denylist, &buf)) == DENY_INODE) {
|
||||
log_debug("%s/%s is in the denylist, ignoring", from, de->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1050,6 +1055,7 @@ static int fd_copy_directory(
|
|||
r = q;
|
||||
}
|
||||
|
||||
finish:
|
||||
if (created) {
|
||||
if (fchown(fdt,
|
||||
uid_is_valid(override_uid) ? override_uid : st->st_uid,
|
||||
|
@ -1111,7 +1117,7 @@ static int fd_copy_tree_generic(
|
|||
uid_t override_uid,
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags,
|
||||
const Set *denylist,
|
||||
Hashmap *denylist,
|
||||
HardlinkContext *hardlink_context,
|
||||
const char *display_path,
|
||||
copy_progress_path_t progress_path,
|
||||
|
@ -1124,6 +1130,13 @@ static int fd_copy_tree_generic(
|
|||
override_gid, copy_flags, denylist, hardlink_context, display_path,
|
||||
progress_path, progress_bytes, userdata);
|
||||
|
||||
DenyType t = PTR_TO_INT(hashmap_get(denylist, st));
|
||||
if (t == DENY_INODE) {
|
||||
log_debug("%s is in the denylist, ignoring", from);
|
||||
return 0;
|
||||
} else if (t == DENY_CONTENTS)
|
||||
log_debug("%s is configured to have its contents excluded, but is not a directory", from);
|
||||
|
||||
r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
|
||||
/* We just tried to copy a leaf node of the tree. If it failed because the node already exists *and* the COPY_REPLACE flag has been provided, we should unlink the node and re-copy. */
|
||||
if (r == -EEXIST && (copy_flags & COPY_REPLACE)) {
|
||||
|
@ -1145,7 +1158,7 @@ int copy_tree_at_full(
|
|||
uid_t override_uid,
|
||||
gid_t override_gid,
|
||||
CopyFlags copy_flags,
|
||||
const Set *denylist,
|
||||
Hashmap *denylist,
|
||||
copy_progress_path_t progress_path,
|
||||
copy_progress_bytes_t progress_bytes,
|
||||
void *userdata) {
|
||||
|
|
|
@ -30,6 +30,14 @@ typedef enum CopyFlags {
|
|||
COPY_GRACEFUL_WARN = 1 << 15, /* Skip copying file types that aren't supported by the target filesystem */
|
||||
} CopyFlags;
|
||||
|
||||
typedef enum DenyType {
|
||||
DENY_DONT = 0, /* we want INT_TO_PTR(DENY_DONT) to map to NULL */
|
||||
DENY_INODE,
|
||||
DENY_CONTENTS,
|
||||
_DENY_TYPE_MAX,
|
||||
_DENY_TYPE_INVALID = -EINVAL,
|
||||
} DenyType;
|
||||
|
||||
typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);
|
||||
typedef int (*copy_progress_path_t)(const char *path, const struct stat *st, void *userdata);
|
||||
|
||||
|
@ -54,11 +62,11 @@ static inline int copy_file_atomic(const char *from, const char *to, mode_t mode
|
|||
return copy_file_atomic_full(from, to, mode, chattr_flags, chattr_mask, copy_flags, NULL, NULL);
|
||||
}
|
||||
|
||||
int copy_tree_at_full(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, const Set *denylist, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, const Set *denylist) {
|
||||
int copy_tree_at_full(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, Hashmap *denylist, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
|
||||
static inline int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, Hashmap *denylist) {
|
||||
return copy_tree_at_full(fdf, from, fdt, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL);
|
||||
}
|
||||
static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, const Set *denylist) {
|
||||
static inline int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags, Hashmap *denylist) {
|
||||
return copy_tree_at_full(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags, denylist, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ TEST(copy_file_fd) {
|
|||
}
|
||||
|
||||
TEST(copy_tree) {
|
||||
_cleanup_set_free_ Set *denylist = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
|
||||
_cleanup_free_ char *cp = NULL;
|
||||
char original_dir[] = "/tmp/test-copy_tree/";
|
||||
char copy_dir[] = "/tmp/test-copy_tree-copy/";
|
||||
|
@ -191,7 +191,7 @@ TEST(copy_tree) {
|
|||
assert_se(write_string_file(ignorep, "ignore", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) == 0);
|
||||
assert_se(RET_NERRNO(stat(ignorep, &st)) >= 0);
|
||||
assert_se(cp = memdup(&st, sizeof(st)));
|
||||
assert_se(set_ensure_put(&denylist, &inode_hash_ops, cp) >= 0);
|
||||
assert_se(hashmap_ensure_put(&denylist, &inode_hash_ops, cp, INT_TO_PTR(DENY_INODE)) >= 0);
|
||||
TAKE_PTR(cp);
|
||||
|
||||
assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS, denylist) == 0);
|
||||
|
|
|
@ -852,6 +852,8 @@ test_exclude_files() {
|
|||
runas testuser mkdir "$root/usr"
|
||||
runas testuser touch "$root/usr/def"
|
||||
runas testuser touch "$root/usr/qed"
|
||||
runas testuser mkdir "$root/tmp"
|
||||
runas testuser touch "$root/tmp/prs"
|
||||
|
||||
runas testuser tee "$defs/00-root.conf" <<EOF
|
||||
[Partition]
|
||||
|
@ -883,14 +885,18 @@ EOF
|
|||
loop=$(losetup -P --show -f "$imgs/zzz")
|
||||
udevadm wait --timeout 60 --settle "${loop:?}"
|
||||
|
||||
# Test that the /usr directory did not end up in the root partition but other files did.
|
||||
# Test that /usr/def did not end up in the root partition but other files did.
|
||||
mkdir "$imgs/mnt"
|
||||
mount -t ext4 "${loop}p1" "$imgs/mnt"
|
||||
assert_rc 0 ls "$imgs/mnt/abc"
|
||||
assert_rc 2 ls "$imgs/mnt/usr"
|
||||
assert_rc 0 ls "$imgs/mnt/usr"
|
||||
assert_rc 2 ls "$imgs/mnt/usr/def"
|
||||
|
||||
# Test that the qed file did not end up in the usr partition but other files did.
|
||||
mkdir "$imgs/mnt/usr"
|
||||
# Test that /tmp/prs did not end up in the root partition but /tmp did.
|
||||
assert_rc 0 ls "$imgs/mnt/tmp"
|
||||
assert_rc 2 ls "$imgs/mnt/tmp/prs"
|
||||
|
||||
# Test that /usr/qed did not end up in the usr partition but /usr/def did.
|
||||
mount -t ext4 "${loop}p2" "$imgs/mnt/usr"
|
||||
assert_rc 0 ls "$imgs/mnt/usr/def"
|
||||
assert_rc 2 ls "$imgs/mnt/usr/qed"
|
||||
|
|
Loading…
Reference in a new issue