Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions core/libs/prx/libkernel/File/src/Stdio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,19 @@ bool DescriptorIsOpen_nid_no_patch(int descriptor) {
}
#endif

static std::filesystem::path WithoutTrailingSeparator(const std::filesystem::path& native) {
return native.has_filename() ? native : native.parent_path();
}

static bool HasNonDirectoryPrefix(const std::filesystem::path& native) {
std::error_code error;
for (auto prefix = WithoutTrailingSeparator(native).parent_path(); prefix.has_relative_path(); prefix = prefix.parent_path()) {
const auto status = std::filesystem::status(prefix, error);
if (std::filesystem::exists(status)) return !std::filesystem::is_directory(status);
}
return false;
}

extern "C" {

int APS5_VABI chmod_nid_postfix(const char* path, int mode) {
Expand Down Expand Up @@ -548,7 +561,9 @@ int APS5_VABI mkdir_nid_postfix(const char* path, uint16_t mode) {
if (const int error = PathError(path)) return PosixFailure(error);
auto native = ResolvePath_nid_no_patch(path);
if (NativeMkdir(native, mode) != 0) {
return PosixResult(SceErrorFromErrno(errno));
const int nativeError = errno;
const bool blockedByFile = (nativeError == ENOENT || nativeError == EINVAL) && HasNonDirectoryPrefix(native);
return PosixResult(SceErrorFromErrno(blockedByFile ? GUEST_ENOTDIR : nativeError));
}
RecordWrittenPath_nid_no_patch(native);
return 0;
Expand Down Expand Up @@ -771,14 +786,10 @@ int APS5_VABI sceKernelMkdir(const char* path, uint16_t mode) {
auto native = ResolvePath_nid_no_patch(path);
while (!native.has_filename() && native.has_relative_path()) native = native.parent_path();
std::error_code error;
const auto status = std::filesystem::status(native, error);
if (error && error != std::errc::no_such_file_or_directory) return SceErrorFromErrno(error.value());
if (std::filesystem::exists(status)) return SceErrorFromErrno(GUEST_EEXIST);
error.clear();
const auto parent = std::filesystem::status(native.parent_path(), error);
if (error) return SceErrorFromErrno(error.value());
if (!std::filesystem::exists(parent)) return SceErrorFromErrno(GUEST_ENOENT);
if (!std::filesystem::is_directory(parent)) return SceErrorFromErrno(GUEST_ENOTDIR);
if (std::filesystem::exists(native, error)) return SceErrorFromErrno(GUEST_EEXIST);
if (!std::filesystem::is_directory(WithoutTrailingSeparator(native).parent_path(), error)) {
return SceErrorFromErrno(HasNonDirectoryPrefix(native) ? GUEST_ENOTDIR : GUEST_ENOENT);
}
if (!std::filesystem::create_directory(native, error)) return SceErrorFromErrno(error.value() ? error.value() : GUEST_EIO);
RecordWrittenPath_nid_no_patch(native);
return 0;
Expand Down Expand Up @@ -979,7 +990,10 @@ int APS5_VABI sceKernelRmdir(const char* path) {
if (path == nullptr) throw std::invalid_argument("sceKernelRmdir: path is null");
const auto native = ResolvePath_nid_no_patch(path);
std::error_code error;
if (!std::filesystem::is_directory(native, error)) return SceErrorFromErrno(std::filesystem::exists(native, error) ? GUEST_ENOTDIR : GUEST_ENOENT);
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);
}
if (!std::filesystem::is_empty(native, error)) return SceErrorFromErrno(GUEST_ENOTEMPTY);
if (!RemoveEmptyDirectory(native)) return SceErrorFromErrno(GUEST_EIO);
RecordWrittenPath_nid_no_patch(native);
Expand Down
27 changes: 27 additions & 0 deletions core/libs/tests/GuestFilesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ int APS5_VABI sceKernelStat(const char*, FileStat*);
int APS5_VABI sceKernelFstat(int, FileStat*);
int APS5_VABI sceKernelUnlink(const char*);
int APS5_VABI sceKernelRmdir(const char*);
int APS5_VABI sceKernelMkdir(const char*, unsigned short);
int* APS5_VABI __error_nid_postfix();
int APS5_VABI pipe_nid_postfix(int*);
std::int64_t APS5_VABI read_nid_postfix(int, void*, std::size_t);
Expand Down Expand Up @@ -447,6 +448,32 @@ int main() {
Require(rmdir_nid_postfix(lockedDirectoryPosix.string().c_str()) == 0 && !std::filesystem::exists(lockedDirectoryPosix));
const auto lockedDirectoryKernel = lockedDirectory("locked-directory-kernel");
Require(sceKernelRmdir(lockedDirectoryKernel.string().c_str()) == 0 && !std::filesystem::exists(lockedDirectoryKernel));
const auto underFile = presentName + "/child";
const auto deepUnderFile = presentName + "/child/leaf";
Require(mkdir_nid_postfix(underFile.c_str(), 0700) == -1 && *__error_nid_postfix() == 20);
Require(mkdir_nid_postfix(deepUnderFile.c_str(), 0700) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelMkdir(underFile.c_str(), 0700) == static_cast<int>(0x80020014u));
Require(sceKernelMkdir(deepUnderFile.c_str(), 0700) == static_cast<int>(0x80020014u));
Require(rmdir_nid_postfix(underFile.c_str()) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelRmdir(deepUnderFile.c_str()) == static_cast<int>(0x80020014u));
Require(rmdir_nid_postfix((presentName + "/").c_str()) == -1 && *__error_nid_postfix() == 20);
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));
const auto missingParentLeaf = rootName + "/missing/leaf";
Require(sceKernelMkdir(missingParentLeaf.c_str(), 0700) == static_cast<int>(0x80020002u));
const auto invalidNameUnderFile = presentName + "/a:b/x";
Require(mkdir_nid_postfix(invalidNameUnderFile.c_str(), 0700) == -1 && *__error_nid_postfix() == 20);
Require(sceKernelMkdir(invalidNameUnderFile.c_str(), 0700) == static_cast<int>(0x80020014u));
const auto trailingPosix = root / "trailing_posix";
const auto trailingKernel = root / "trailing_kernel";
const auto trailingPosixName = trailingPosix.string() + "/";
const auto trailingKernelName = trailingKernel.string() + "/";
Require(mkdir_nid_postfix(trailingPosixName.c_str(), 0700) == 0 && std::filesystem::is_directory(trailingPosix));
Require(sceKernelMkdir(trailingKernelName.c_str(), 0700) == 0 && std::filesystem::is_directory(trailingKernel));
Require(rmdir_nid_postfix(trailingPosix.string().c_str()) == 0 && sceKernelRmdir(trailingKernel.string().c_str()) == 0);
Require(!std::filesystem::exists(trailingPosix) && !std::filesystem::exists(trailingKernel));
Require(std::filesystem::is_regular_file(present));
Require(rmdir_nid_postfix("") == -1 && *__error_nid_postfix() == 2);
Require(rmdir_nid_postfix(nullptr) == -1 && *__error_nid_postfix() == 14);
Require(unlink_nid_postfix(missingName.c_str()) == -1 && *__error_nid_postfix() == 2);
Expand Down
Loading