diff --git a/src/kernel/emulation/linux/unistd/fchmodat.c b/src/kernel/emulation/linux/unistd/fchmodat.c index da1a298b1..4e79db95f 100644 --- a/src/kernel/emulation/linux/unistd/fchmodat.c +++ b/src/kernel/emulation/linux/unistd/fchmodat.c @@ -6,8 +6,54 @@ #include "../vchroot_expand.h" #include #include +#include +#include "../stat/common.h" +#include extern char* strcpy(char* dst, const char* src); +extern long close_internal(int fd); + +#ifndef LINUX_O_PATH + #define LINUX_O_PATH 0x200000 +#endif +#ifndef LINUX_O_NOFOLLOW + #define LINUX_O_NOFOLLOW 0x0100 +#endif +#ifndef LINUX_O_CLOEXEC + #define LINUX_O_CLOEXEC 0x1000000 +#endif +#ifndef LINUX_AT_EMPTY_PATH + #define LINUX_AT_EMPTY_PATH 0x1000 +#endif + +#ifndef __NR_fstatat64 + #define __NR_fstatat64 __NR_newfstatat +#endif + +int do_linux_fchmodat(int fd, const char* path, int mode, int flag) { + if (flag == 0) { + return LINUX_SYSCALL(__NR_fchmodat, fd, path, mode); + } else { + struct linux_stat st; + if (LINUX_SYSCALL(__NR_fstatat64, fd, path, &st, LINUX_AT_EMPTY_PATH | LINUX_AT_SYMLINK_NOFOLLOW) != 0) + return -1; + + // on Linux, symlinks can't be chmodded (only their targets) + // + // we can't open a file handle directly on the symlink + // and do fchmod/chmod on that because Linux ignores us + // and follows the symlink anyways + // + // this still happens even if we try to open a file handle to + // the symlink and modify it through `/proc/self/fd` + // + // so instead, just return success + if (S_ISLNK(st.st_mode)) + return 0; + + return LINUX_SYSCALL(__NR_fchmodat, fd, path, mode); + } +}; long sys_fchmodat(int fd, const char* path, int mode, int flag) { @@ -23,7 +69,7 @@ long sys_fchmodat(int fd, const char* path, int mode, int flag) if (ret < 0) return errno_linux_to_bsd(ret); - ret = LINUX_SYSCALL(__NR_fchmodat, vc.dfd, vc.path, mode, atflags_bsd_to_linux(flag)); + ret = do_linux_fchmodat(vc.dfd, vc.path, mode, atflags_bsd_to_linux(flag)); if (ret < 0) ret = errno_linux_to_bsd(ret); diff --git a/src/kernel/emulation/linux/xattr/setattrlist_generic.c b/src/kernel/emulation/linux/xattr/setattrlist_generic.c index f9b499425..2fe8219b9 100644 --- a/src/kernel/emulation/linux/xattr/setattrlist_generic.c +++ b/src/kernel/emulation/linux/xattr/setattrlist_generic.c @@ -8,6 +8,7 @@ #include extern char *strcpy(char *dest, const char *src); +extern int do_linux_fchmodat(int fd, const char* path, int mode, int flag); #define ATTR_BIT_MAP_COUNT 5 @@ -125,7 +126,7 @@ struct attrlist* alist, void *attributeBuffer, __SIZE_TYPE__ bufferSize, unsigne { int perms = *((int*) next); #if HAS_PATH - rv = LINUX_SYSCALL(__NR_chmod, vc.path, perms); + rv = do_linux_fchmodat(LINUX_AT_FDCWD, vc.path, perms, LINUX_AT_SYMLINK_NOFOLLOW); #else rv = LINUX_SYSCALL(__NR_fchmod, fd, perms); #endif