mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 16:37:19 +09:00
Merge pull request #26410 from DaanDeMeyer/xattr-symlink
Copy symlink xattrs
This commit is contained in:
@@ -293,3 +293,77 @@ int listxattr_at_malloc(
|
||||
l = (size_t) n;
|
||||
}
|
||||
}
|
||||
|
||||
int xsetxattr(int fd,
|
||||
const char *path,
|
||||
const char *name,
|
||||
const char *value,
|
||||
size_t size,
|
||||
int flags) {
|
||||
|
||||
_cleanup_close_ int opened_fd = -EBADF;
|
||||
bool by_procfs = false;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert(name);
|
||||
assert(value);
|
||||
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
|
||||
/* So, this is a single function that does what setxattr()/lsetxattr()/fsetxattr() do, but in one go,
|
||||
* and with additional bells and whistles. Specifically:
|
||||
*
|
||||
* 1. This works on O_PATH fds (which fsetxattr() does not)
|
||||
* 2. Provides full openat()-style semantics, i.e. by-fd, by-path and combination thereof
|
||||
* 3. As extension to openat()-style semantics implies AT_EMPTY_PATH if path is NULL.
|
||||
*/
|
||||
|
||||
if (!path) /* If path is NULL, imply AT_EMPTY_PATH. – But if it's "", don't — for safety reasons. */
|
||||
flags |= AT_EMPTY_PATH;
|
||||
|
||||
if (size == SIZE_MAX)
|
||||
size = strlen(value);
|
||||
|
||||
if (isempty(path)) {
|
||||
if (!FLAGS_SET(flags, AT_EMPTY_PATH))
|
||||
return -EINVAL;
|
||||
|
||||
if (fd == AT_FDCWD) /* Both unspecified? Then operate on current working directory */
|
||||
path = ".";
|
||||
else
|
||||
path = NULL;
|
||||
|
||||
} else if (fd != AT_FDCWD) {
|
||||
|
||||
/* If both have been specified, then we go via O_PATH */
|
||||
opened_fd = openat(fd, path, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : O_NOFOLLOW));
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
fd = opened_fd;
|
||||
path = NULL;
|
||||
by_procfs = true; /* fsetxattr() is not going to work, go via /proc/ link right-away */
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (path)
|
||||
r = FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? setxattr(path, name, value, size, 0)
|
||||
: lsetxattr(path, name, value, size, 0);
|
||||
else
|
||||
r = by_procfs ? setxattr(FORMAT_PROC_FD_PATH(fd), name, value, size, 0)
|
||||
: fsetxattr(fd, name, value, size, 0);
|
||||
if (r < 0) {
|
||||
if (errno == EBADF) {
|
||||
if (by_procfs || path)
|
||||
return -EBADF;
|
||||
|
||||
by_procfs = true; /* Might be an O_PATH fd, try again via /proc/ link */
|
||||
continue;
|
||||
}
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,3 +36,5 @@ static inline int llistxattr_malloc(const char *path, char **ret) {
|
||||
static inline int flistxattr_malloc(int fd, char **ret) {
|
||||
return listxattr_at_malloc(fd, NULL, AT_EMPTY_PATH, ret);
|
||||
}
|
||||
|
||||
int xsetxattr(int fd, const char *path, const char *name, const char *value, size_t size, int flags);
|
||||
|
||||
@@ -1105,7 +1105,7 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to '%s': %m", arg_source, arg_image, arg_target);
|
||||
|
||||
(void) copy_xattr(source_fd, target_fd, 0);
|
||||
(void) copy_xattr(source_fd, NULL, target_fd, NULL, 0);
|
||||
(void) copy_access(source_fd, target_fd);
|
||||
(void) copy_times(source_fd, target_fd, 0);
|
||||
|
||||
@@ -1182,7 +1182,7 @@ static int action_list_or_mtree_or_copy(DissectedImage *m, LoopDevice *d) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy bytes from '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
|
||||
|
||||
(void) copy_xattr(source_fd, target_fd, 0);
|
||||
(void) copy_xattr(source_fd, NULL, target_fd, NULL, 0);
|
||||
(void) copy_access(source_fd, target_fd);
|
||||
(void) copy_times(source_fd, target_fd, 0);
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ static int raw_export_process(RawExport *e) {
|
||||
finish:
|
||||
if (r >= 0) {
|
||||
(void) copy_times(e->input_fd, e->output_fd, COPY_CRTIME);
|
||||
(void) copy_xattr(e->input_fd, e->output_fd, 0);
|
||||
(void) copy_xattr(e->input_fd, NULL, e->output_fd, NULL, 0);
|
||||
}
|
||||
|
||||
if (e->on_finished)
|
||||
|
||||
@@ -234,7 +234,7 @@ static int raw_import_finish(RawImport *i) {
|
||||
|
||||
if (S_ISREG(i->input_stat.st_mode)) {
|
||||
(void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME);
|
||||
(void) copy_xattr(i->input_fd, i->output_fd, 0);
|
||||
(void) copy_xattr(i->input_fd, NULL, i->output_fd, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -396,7 +396,7 @@ static int raw_pull_make_local_copy(RawPull *i) {
|
||||
return log_error_errno(r, "Failed to make writable copy of image: %m");
|
||||
|
||||
(void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
|
||||
(void) copy_xattr(i->raw_job->disk_fd, dfd, 0);
|
||||
(void) copy_xattr(i->raw_job->disk_fd, NULL, dfd, NULL, 0);
|
||||
|
||||
dfd = safe_close(dfd);
|
||||
|
||||
|
||||
@@ -921,7 +921,7 @@ int locale_gen_enable_locale(const char *locale) {
|
||||
r = copy_access(fileno(fr), fileno(fw));
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = copy_xattr(fileno(fr), fileno(fw), COPY_ALL_XATTRS);
|
||||
r = copy_xattr(fileno(fr), NULL, fileno(fw), NULL, COPY_ALL_XATTRS);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to copy all xattrs from old to new /etc/locale.gen file, ignoring: %m");
|
||||
}
|
||||
|
||||
@@ -3752,6 +3752,31 @@ static int do_copy_files(Partition *p, const char *root, const Set *denylist) {
|
||||
assert(p);
|
||||
assert(root);
|
||||
|
||||
/* copy_tree_at() automatically copies the permissions of source directories to target directories if
|
||||
* it created them. However, the root directory is created by us, so we have to manually take care
|
||||
* that it is initialized. We use the first source directory targeting "/" as the metadata source for
|
||||
* the root directory. */
|
||||
STRV_FOREACH_PAIR(source, target, p->copy_files) {
|
||||
_cleanup_close_ int rfd = -EBADF, sfd = -EBADF;
|
||||
|
||||
if (!path_equal(*target, "/"))
|
||||
continue;
|
||||
|
||||
rfd = open(root, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (rfd < 0)
|
||||
return rfd;
|
||||
|
||||
sfd = chase_symlinks_and_open(*source, arg_root, CHASE_PREFIX_ROOT, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOCTTY, NULL);
|
||||
if (sfd < 0)
|
||||
return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_root), *source);
|
||||
|
||||
(void) copy_xattr(sfd, NULL, rfd, NULL, COPY_ALL_XATTRS);
|
||||
(void) copy_access(sfd, rfd);
|
||||
(void) copy_times(sfd, rfd, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
STRV_FOREACH_PAIR(source, target, p->copy_files) {
|
||||
_cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
|
||||
|
||||
@@ -3788,10 +3813,6 @@ static int do_copy_files(Partition *p, const char *root, const Set *denylist) {
|
||||
if (pfd < 0)
|
||||
return log_error_errno(pfd, "Failed to open parent directory of target: %m");
|
||||
|
||||
/* Make sure everything is owned by the user running repart so that
|
||||
* make_filesystem() can map the user running repart to "root" in a user
|
||||
* namespace to have the files owned by root in the final image. */
|
||||
|
||||
r = copy_tree_at(
|
||||
sfd, ".",
|
||||
pfd, fn,
|
||||
@@ -3840,7 +3861,7 @@ static int do_copy_files(Partition *p, const char *root, const Set *denylist) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
|
||||
|
||||
(void) copy_xattr(sfd, tfd, COPY_ALL_XATTRS);
|
||||
(void) copy_xattr(sfd, NULL, tfd, NULL, COPY_ALL_XATTRS);
|
||||
(void) copy_access(sfd, tfd);
|
||||
(void) copy_times(sfd, tfd, 0);
|
||||
}
|
||||
@@ -3872,17 +3893,22 @@ static bool partition_needs_populate(Partition *p) {
|
||||
|
||||
static int partition_populate_directory(Partition *p, const Set *denylist, char **ret) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *root = NULL;
|
||||
_cleanup_close_ int rfd = -EBADF;
|
||||
const char *vt;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
rfd = mkdtemp_open("/var/tmp/repart-XXXXXX", 0, &root);
|
||||
if (rfd < 0)
|
||||
return log_error_errno(rfd, "Failed to create temporary directory: %m");
|
||||
r = var_tmp_dir(&vt);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not determine temporary directory: %m");
|
||||
|
||||
if (fchmod(rfd, 0755) < 0)
|
||||
return log_error_errno(errno, "Failed to change mode of temporary directory: %m");
|
||||
r = tempfn_random_child(vt, "repart", &root);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to generate temporary directory: %m");
|
||||
|
||||
r = mkdir(root, 0755);
|
||||
if (r < 0)
|
||||
return log_error_errno(errno, "Failed to create temporary directory: %m");
|
||||
|
||||
r = do_copy_files(p, root, denylist);
|
||||
if (r < 0)
|
||||
|
||||
@@ -504,6 +504,7 @@ static int fd_copy_symlink(
|
||||
AT_SYMLINK_NOFOLLOW) < 0)
|
||||
r = -errno;
|
||||
|
||||
(void) copy_xattr(df, from, dt, to, copy_flags);
|
||||
(void) utimensat(dt, to, (struct timespec[]) { st->st_atim, st->st_mtim }, AT_SYMLINK_NOFOLLOW);
|
||||
return r;
|
||||
}
|
||||
@@ -754,7 +755,7 @@ static int fd_copy_regular(
|
||||
r = -errno;
|
||||
|
||||
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
|
||||
(void) copy_xattr(fdf, fdt, copy_flags);
|
||||
(void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
|
||||
|
||||
if (copy_flags & COPY_FSYNC) {
|
||||
if (fsync(fdt) < 0) {
|
||||
@@ -1058,7 +1059,7 @@ static int fd_copy_directory(
|
||||
if (fchmod(fdt, st->st_mode & 07777) < 0)
|
||||
r = -errno;
|
||||
|
||||
(void) copy_xattr(dirfd(d), fdt, copy_flags);
|
||||
(void) copy_xattr(dirfd(d), NULL, fdt, NULL, copy_flags);
|
||||
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
|
||||
}
|
||||
|
||||
@@ -1313,7 +1314,7 @@ int copy_file_fd_full(
|
||||
* mode/ownership of that device node...) */
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
(void) copy_times(fdf, fdt, copy_flags);
|
||||
(void) copy_xattr(fdf, fdt, copy_flags);
|
||||
(void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
|
||||
}
|
||||
|
||||
if (copy_flags & COPY_FSYNC_FULL) {
|
||||
@@ -1385,7 +1386,7 @@ int copy_file_full(
|
||||
goto fail;
|
||||
|
||||
(void) copy_times(fdf, fdt, copy_flags);
|
||||
(void) copy_xattr(fdf, fdt, copy_flags);
|
||||
(void) copy_xattr(fdf, NULL, fdt, NULL, copy_flags);
|
||||
|
||||
if (chattr_mask != 0)
|
||||
(void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
|
||||
@@ -1570,11 +1571,11 @@ int copy_rights_with_fallback(int fdf, int fdt, const char *patht) {
|
||||
return fchmod_and_chown_with_fallback(fdt, patht, st.st_mode & 07777, st.st_uid, st.st_gid);
|
||||
}
|
||||
|
||||
int copy_xattr(int fdf, int fdt, CopyFlags copy_flags) {
|
||||
int copy_xattr(int df, const char *from, int dt, const char *to, CopyFlags copy_flags) {
|
||||
_cleanup_free_ char *names = NULL;
|
||||
int ret = 0, r;
|
||||
|
||||
r = flistxattr_malloc(fdf, &names);
|
||||
r = listxattr_at_malloc(df, from, 0, &names);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1584,13 +1585,13 @@ int copy_xattr(int fdf, int fdt, CopyFlags copy_flags) {
|
||||
if (!FLAGS_SET(copy_flags, COPY_ALL_XATTRS) && !startswith(p, "user."))
|
||||
continue;
|
||||
|
||||
r = fgetxattr_malloc(fdf, p, &value);
|
||||
r = getxattr_at_malloc(df, from, p, 0, &value);
|
||||
if (r == -ENODATA)
|
||||
continue; /* gone by now */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (fsetxattr(fdt, p, value, r, 0) < 0)
|
||||
if (xsetxattr(dt, to, p, value, r, 0) < 0)
|
||||
ret = -errno;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,4 +77,4 @@ int copy_rights_with_fallback(int fdf, int fdt, const char *patht);
|
||||
static inline int copy_rights(int fdf, int fdt) {
|
||||
return copy_rights_with_fallback(fdf, fdt, NULL); /* no fallback */
|
||||
}
|
||||
int copy_xattr(int fdf, int fdt, CopyFlags copy_flags);
|
||||
int copy_xattr(int df, const char *from, int dt, const char *to, CopyFlags copy_flags);
|
||||
|
||||
@@ -529,7 +529,7 @@ int mount_setup(bool loaded_policy, bool leave_propagation) {
|
||||
|
||||
after_relabel = now(CLOCK_MONOTONIC);
|
||||
|
||||
log_info("Relabelled /dev, /dev/shm, /run, /sys/fs/cgroup%s in %s.",
|
||||
log_info("Relabeled /dev, /dev/shm, /run, /sys/fs/cgroup%s in %s.",
|
||||
n_extra > 0 ? ", additional files" : "",
|
||||
FORMAT_TIMESPAN(after_relabel - before_relabel, 0));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user