fix(libkernel): fail rmdir on a symbolic link instead of removing it
sceKernelRmdir, and rmdir through it, checked the path with
std::filesystem::is_directory and is_empty, which follow a symbolic
link, and then removed the path itself. So rmdir on a link to an empty
directory returned 0 and deleted the link, while the target stayed.
FreeBSD looks up the last component of rmdir's path without following
it (sys/kern/vfs_syscalls.c, kern_rmdirat: NDINIT_ATRIGHTS with
LOCKPARENT | LOCKLEAF and no FOLLOW) and then fails with ENOTDIR for
anything that is not a directory: "if (vp->v_type != VDIR) { error =
ENOTDIR; goto out; }". A link, whether it points to a directory or
nowhere, is a VLNK there.
rmdir now returns ENOTDIR for a symbolic link and leaves it alone, as
unlink already looks at the link itself through symlink_status. A
trailing slash still resolves through the link, as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9bee015035
commit
e8cb97e6e2
|
|
@ -1028,6 +1028,7 @@ int APS5_VABI sceKernelRmdir(const char* path) {
|
|||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
|
||||
std::error_code error;
|
||||
if (std::filesystem::is_symlink(native, error)) return SceErrorFromErrno(GUEST_ENOTDIR);
|
||||
if (!std::filesystem::is_directory(native, error)) {
|
||||
const bool blockedByFile = HasNonDirectoryPrefix(native) || std::filesystem::exists(WithoutTrailingSeparator(native), error);
|
||||
return SceErrorFromErrno(blockedByFile ? GUEST_ENOTDIR : GUEST_ENOENT);
|
||||
|
|
|
|||
|
|
@ -580,6 +580,20 @@ int main() {
|
|||
Require(rmdir_nid_postfix((missingName + "/").c_str()) == -1 && *__error_nid_postfix() == 2);
|
||||
Require(sceKernelRmdir((presentName + "/").c_str()) == static_cast<int>(0x80020014u));
|
||||
Require(sceKernelRmdir((missingName + "/").c_str()) == static_cast<int>(0x80020002u));
|
||||
#ifndef _WIN32
|
||||
const auto linkedDirectory = root / "linked-directory";
|
||||
const auto directoryLink = root / "directory-link";
|
||||
const auto danglingDirectoryLink = root / "dangling-directory-link";
|
||||
Require(std::filesystem::create_directory(linkedDirectory));
|
||||
std::filesystem::create_directory_symlink("linked-directory", directoryLink);
|
||||
std::filesystem::create_directory_symlink("missing-directory", danglingDirectoryLink);
|
||||
Require(rmdir_nid_postfix(directoryLink.string().c_str()) == -1 && *__error_nid_postfix() == 20);
|
||||
Require(sceKernelRmdir(directoryLink.string().c_str()) == static_cast<int>(0x80020014u));
|
||||
Require(rmdir_nid_postfix(danglingDirectoryLink.string().c_str()) == -1 && *__error_nid_postfix() == 20);
|
||||
Require(std::filesystem::is_symlink(directoryLink) && std::filesystem::is_directory(linkedDirectory));
|
||||
Require(unlink_nid_postfix(directoryLink.string().c_str()) == 0 && unlink_nid_postfix(danglingDirectoryLink.string().c_str()) == 0);
|
||||
Require(rmdir_nid_postfix(linkedDirectory.string().c_str()) == 0 && !std::filesystem::exists(linkedDirectory));
|
||||
#endif
|
||||
const auto dotDirectory = root / "dot-directory";
|
||||
Require(std::filesystem::create_directory(dotDirectory));
|
||||
const auto dotDirectoryName = dotDirectory.string() + "/.";
|
||||
|
|
|
|||
Loading…
Reference in New Issue