mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-28 00:35:47 +00:00
fix(libkernel): fail open with O_NOFOLLOW on a symbolic link
sceKernelOpen built the host flags from the bits it knows and dropped O_NOFOLLOW (0x100), so open and sceKernelOpen with O_NOFOLLOW followed a symbolic link and opened its target, and with O_CREAT created the missing target of a dangling link. FreeBSD looks up the last component without following it when O_NOFOLLOW is set (sys/kern/vfs_vnops.c, vn_open_cred: "((fmode & O_NOFOLLOW) ? NOFOLLOW : FOLLOW) | LOCKLEAF", and with O_CREAT "if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0) ndp->ni_cnd.cn_flags |= FOLLOW;"), and then refuses the link itself in vn_open_vnode: "if (vp->v_type == VLNK) return (EMLINK);". open now fails with EMLINK when O_NOFOLLOW is set and the path names a symbolic link. O_CREAT | O_EXCL keeps failing with EEXIST, and a trailing slash still resolves through the link. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
9bee015035
commit
9421484572
3 files changed
+16
No files matched your search
@@ -9,6 +9,7 @@ constexpr std::int32_t SCE_KERNEL_O_RDWR = 0x00000002;
|
||||
constexpr std::int32_t SCE_KERNEL_O_ACCMODE = 0x00000003;
|
||||
constexpr std::int32_t SCE_KERNEL_O_APPEND = 0x00000008;
|
||||
constexpr std::int32_t SCE_KERNEL_O_FSYNC = 0x00000080;
|
||||
constexpr std::int32_t SCE_KERNEL_O_NOFOLLOW = 0x00000100;
|
||||
constexpr std::int32_t SCE_KERNEL_O_SYNC = 0x00000080;
|
||||
constexpr std::int32_t SCE_KERNEL_O_CREAT = 0x00000200;
|
||||
constexpr std::int32_t SCE_KERNEL_O_TRUNC = 0x00000400;
|
||||
|
||||
@@ -152,6 +152,11 @@ int APS5_VABI sceKernelOpen(const char* path, int flags, std::uint16_t mode) {
|
||||
if (std::filesystem::exists(status) && !std::filesystem::is_directory(status)) return SceErrorFromErrno(ENOTDIR);
|
||||
}
|
||||
#endif
|
||||
if ((flags & SCE_KERNEL_O_NOFOLLOW) != 0 && (flags & (SCE_KERNEL_O_CREAT | SCE_KERNEL_O_EXCL)) != (SCE_KERNEL_O_CREAT | SCE_KERNEL_O_EXCL)) {
|
||||
constexpr int GuestEmlink = 31;
|
||||
std::error_code error;
|
||||
if (std::filesystem::is_symlink(native, error)) return SceErrorFromErrno(GuestEmlink);
|
||||
}
|
||||
int fd = NativeOpen(native, MapFlags(flags), mode);
|
||||
#ifdef _WIN32
|
||||
if (fd < 0 && errno != ENOENT) {
|
||||
|
||||
@@ -409,6 +409,16 @@ int main() {
|
||||
const auto dangling = root / "dangling";
|
||||
std::filesystem::create_symlink("missing.txt", dangling);
|
||||
Require(lstat_nid_postfix(dangling.string().c_str(), &linkStatus) == 0 && (linkStatus.st_mode & 0170000) == 0120000);
|
||||
constexpr int noFollow = 0x100;
|
||||
Require(sceKernelOpen(link.string().c_str(), noFollow, 0) == static_cast<int>(0x8002001Fu));
|
||||
Require(open_nid_postfix(link.string().c_str(), noFollow, 0) == -1 && *__error_nid_postfix() == 31);
|
||||
Require(open_nid_postfix(dangling.string().c_str(), noFollow | 0x201, 0600) == -1 && *__error_nid_postfix() == 31);
|
||||
Require(!std::filesystem::exists(root / "missing.txt"));
|
||||
Require(open_nid_postfix(dangling.string().c_str(), noFollow | 0xA01, 0600) == -1 && *__error_nid_postfix() == 17);
|
||||
const int followed = open_nid_postfix(link.string().c_str(), 0, 0);
|
||||
Require(followed >= 0 && close_nid_postfix(followed) == 0);
|
||||
const int plain = open_nid_postfix(presentName.c_str(), noFollow, 0);
|
||||
Require(plain >= 0 && close_nid_postfix(plain) == 0);
|
||||
Require(stat_nid_postfix(dangling.string().c_str(), &status) == -1 && *__error_nid_postfix() == 2);
|
||||
Require(lstat_nid_postfix((root / "present.txt" / "child").string().c_str(), &linkStatus) == -1 && *__error_nid_postfix() == 20);
|
||||
const auto viaMissing = rootName + "/missing/../present.txt";
|
||||
|
||||
Reference in new issue
Block a user