Merge pull request #29817 from YHNdnzj/fchmodat2
Add support for fchmodat2 and use it where appropriate
This commit is contained in:
@@ -568,6 +568,8 @@ foreach ident : [
|
||||
['memfd_create', '''#include <sys/mman.h>'''],
|
||||
['gettid', '''#include <sys/types.h>
|
||||
#include <unistd.h>'''],
|
||||
['fchmodat2', '''#include <stdlib.h>
|
||||
#include <fcntl.h>'''], # no known header declares fchmodat2
|
||||
['pivot_root', '''#include <stdlib.h>
|
||||
#include <unistd.h>'''], # no known header declares pivot_root
|
||||
['ioprio_get', '''#include <sched.h>'''], # no known header declares ioprio_get
|
||||
|
||||
+16
-2
@@ -288,8 +288,22 @@ int fchmod_umask(int fd, mode_t m) {
|
||||
|
||||
int fchmod_opath(int fd, mode_t m) {
|
||||
/* This function operates also on fd that might have been opened with
|
||||
* O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like
|
||||
* fchownat() does. */
|
||||
* O_PATH. The tool set we have is non-intuitive:
|
||||
* - fchmod(2) only operates on open files (i. e., fds with an open file description);
|
||||
* - fchmodat(2) does not have a flag arg like fchownat(2) does, so no way to pass AT_EMPTY_PATH;
|
||||
* + it should not be confused with the libc fchmodat(3) interface, which adds 4th flag argument,
|
||||
* but does not support AT_EMPTY_PATH (only supports AT_SYMLINK_NOFOLLOW);
|
||||
* - fchmodat2(2) supports all the AT_* flags, but is still very recent.
|
||||
*
|
||||
* We try to use fchmodat2(), and, if it is not supported, resort
|
||||
* to the /proc/self/fd dance. */
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (fchmodat2(fd, "", m, AT_EMPTY_PATH) >= 0)
|
||||
return 0;
|
||||
if (!IN_SET(errno, ENOSYS, EPERM)) /* Some container managers block unknown syscalls with EPERM */
|
||||
return -errno;
|
||||
|
||||
if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
|
||||
if (errno != ENOENT)
|
||||
|
||||
@@ -32,6 +32,21 @@
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_FCHMODAT2
|
||||
static inline int missing_fchmodat2(int dirfd, const char *path, mode_t mode, int flags) {
|
||||
# ifdef __NR_fchmodat2
|
||||
return syscall(__NR_fchmodat2, dirfd, path, mode, flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define fchmodat2 missing_fchmodat2
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_PIVOT_ROOT
|
||||
static inline int missing_pivot_root(const char *new_root, const char *put_old) {
|
||||
return syscall(__NR_pivot_root, new_root, put_old);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "missing_magic.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "nspawn-def.h"
|
||||
#include "nspawn-patch-uid.h"
|
||||
#include "stat-util.h"
|
||||
@@ -239,14 +240,18 @@ static int patch_fd(int fd, const char *name, const struct stat *st, uid_t shift
|
||||
|
||||
/* The Linux kernel alters the mode in some cases of chown(). Let's undo this. */
|
||||
if (name) {
|
||||
/* It looks like older glibc (before 2016) did not support AT_SYMLINK_NOFOLLOW. */
|
||||
if (!S_ISLNK(st->st_mode))
|
||||
r = fchmodat(fd, name, st->st_mode, 0);
|
||||
else /* AT_SYMLINK_NOFOLLOW is not available for fchmodat() */
|
||||
r = 0;
|
||||
r = RET_NERRNO(fchmodat(fd, name, st->st_mode, 0));
|
||||
else {
|
||||
r = RET_NERRNO(fchmodat2(fd, name, st->st_mode, AT_SYMLINK_NOFOLLOW));
|
||||
if (IN_SET(r, -ENOSYS, -EPERM))
|
||||
r = 0;
|
||||
}
|
||||
} else
|
||||
r = fchmod(fd, st->st_mode);
|
||||
r = RET_NERRNO(fchmod(fd, st->st_mode));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
return r;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
+12
-11
@@ -21,7 +21,7 @@
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
#include "missing_sched.h"
|
||||
#include "missing_syscall_def.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "nsflags.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "process-util.h"
|
||||
@@ -1007,21 +1007,22 @@ static int real_open(const char *path, int flags, mode_t mode) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static int try_fchmodat2(int dirfd, const char *path, int flags, mode_t mode) {
|
||||
static int try_fchmodat2(int dirfd, const char *path, mode_t mode, int flags) {
|
||||
int r;
|
||||
|
||||
/* glibc does not provide a direct wrapper for fchmodat2(). Let's hence define our own wrapper for
|
||||
* testing purposes that calls the real syscall, on architectures and in environments where
|
||||
* SYS_fchmodat2 is defined. Otherwise, let's just fall back to the glibc fchmodat() call. */
|
||||
|
||||
#if defined __NR_fchmodat2 && __NR_fchmodat2 >= 0
|
||||
int r;
|
||||
r = (int) syscall(__NR_fchmodat2, dirfd, path, flags, mode);
|
||||
/* Not supported by fchmodat() */
|
||||
assert_se(!FLAGS_SET(flags, AT_EMPTY_PATH));
|
||||
|
||||
r = RET_NERRNO(fchmodat2(dirfd, path, mode, flags));
|
||||
if (r != -ENOSYS)
|
||||
return r;
|
||||
|
||||
/* The syscall might still be unsupported by kernel or libseccomp. */
|
||||
if (r < 0 && errno == ENOSYS)
|
||||
return fchmodat(dirfd, path, flags, mode);
|
||||
return r;
|
||||
#else
|
||||
return fchmodat(dirfd, path, flags, mode);
|
||||
#endif
|
||||
return RET_NERRNO(fchmodat(dirfd, path, mode, flags));
|
||||
}
|
||||
|
||||
TEST(restrict_suid_sgid) {
|
||||
|
||||
Reference in New Issue
Block a user