mirror of
https://github.com/ARMSX2/ARMSX3.git
synced 2026-08-24 16:58:52 -07:00
sys_fs: Fix ENOTDIR check for paths ending with /dot or /dotdot
They must be directories, even file-path/dot points to itself.
This commit is contained in:
@@ -147,12 +147,52 @@ bool verify_mself(const fs::file& mself_file)
|
||||
}
|
||||
|
||||
// TODO: May not be thread-safe (or even, process-safe)
|
||||
bool has_non_directory_components(std::string_view path)
|
||||
bool has_non_directory_components(std::string_view path, bool ends_with_delim_dot_or_dotdot)
|
||||
{
|
||||
std::string sub_path{path};
|
||||
|
||||
fs::stat_t stat{};
|
||||
|
||||
if (ends_with_delim_dot_or_dotdot)
|
||||
{
|
||||
// Special case: /dev_bdvd/PS3_GAME/USRDIR/EBOOT.BIN/./ is not allowed
|
||||
// Because lookup inside a file is not allowed, even if /./ points to itself
|
||||
// This check is relevant only for when it is at the end of path
|
||||
// Because if it is other path componenets the functions handle it fine
|
||||
// We cannot know this from `path` argument because it is resolved by VFS
|
||||
std::string_view edited_path{path};
|
||||
|
||||
edited_path = edited_path.substr(0, edited_path.find_last_not_of(fs::delim) + 1);
|
||||
|
||||
const auto elem = edited_path.substr(edited_path.find_last_of(fs::delim) + 1);
|
||||
|
||||
if (elem == "." || elem == "..")
|
||||
{
|
||||
edited_path.remove_suffix(elem.size());
|
||||
edited_path = edited_path.substr(0, edited_path.find_last_not_of(fs::delim) + 1);
|
||||
}
|
||||
|
||||
if (fs::get_stat(std::string{edited_path}, stat))
|
||||
{
|
||||
if (!stat.is_directory)
|
||||
{
|
||||
sys_fs.warning("has_non_directory_components() returned an affirmative (edited_path=%s)", edited_path);
|
||||
}
|
||||
|
||||
return !stat.is_directory;
|
||||
}
|
||||
|
||||
if (fs::g_tls_error == fs::error::notdir)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fs::g_tls_error != fs::error::inval && fs::g_tls_error != fs::error::noent)
|
||||
{
|
||||
fmt::throw_exception("sys_fs: fs::get_stat() failed with error '%s' (edited_path=%s)", fs::g_tls_error, edited_path);
|
||||
}
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::string_view path_sv = fs::get_parent_dir_view(sub_path);
|
||||
@@ -160,7 +200,7 @@ bool has_non_directory_components(std::string_view path)
|
||||
|
||||
if (sv_size >= sub_path.size())
|
||||
{
|
||||
sys_fs.error("has_non_directory_components() did not find a directory! (path=%s, sub_path=%s)", path, sub_path);
|
||||
sys_fs.warning("has_non_directory_components() did not find a directory! (path=%s, sub_path=%s)", path, sub_path);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -172,13 +212,18 @@ bool has_non_directory_components(std::string_view path)
|
||||
{
|
||||
if (!stat.is_directory)
|
||||
{
|
||||
sys_fs.error("has_non_directory_components() returned an affirmative (sub_path=%s)", sub_path);
|
||||
sys_fs.warning("has_non_directory_components() returned an affirmative (sub_path=%s)", sub_path);
|
||||
}
|
||||
|
||||
return !stat.is_directory;
|
||||
}
|
||||
|
||||
if (fs::g_tls_error != fs::error::noent)
|
||||
if (fs::g_tls_error == fs::error::notdir)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fs::g_tls_error != fs::error::inval && fs::g_tls_error != fs::error::noent)
|
||||
{
|
||||
fmt::throw_exception("sys_fs: fs::get_stat() failed with error '%s' (sub_path=%s)", fs::g_tls_error, sub_path);
|
||||
}
|
||||
@@ -187,6 +232,17 @@ bool has_non_directory_components(std::string_view path)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Takes virtual path only
|
||||
bool ends_with_delim_dot_or_dotdot(std::string_view vpath)
|
||||
{
|
||||
if (vpath.find_last_not_of('/') != vpath.size() - 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return vpath.ends_with("/.") || vpath.ends_with("/..");
|
||||
}
|
||||
|
||||
lv2_fs_mount_info_map::lv2_fs_mount_info_map()
|
||||
{
|
||||
for (auto mp = &g_mp_sys_dev_root; mp; mp = mp->next) // Scan and keep track of pre-mounted devices
|
||||
@@ -879,7 +935,7 @@ error_code sys_fs_test(ppu_thread&, u32 arg1, u32 arg2, vm::ptr<u32> arg3, u32 a
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s32 flags, bool has_write_access, lv2_file_type type, const lv2_fs_mount_info& mp)
|
||||
lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s32 flags, bool has_write_access, lv2_file_type type, const lv2_fs_mount_info& mp, bool ends_with_dot)
|
||||
{
|
||||
// TODO: other checks for path
|
||||
|
||||
@@ -1010,7 +1066,7 @@ lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s3
|
||||
case fs::error::isdir: return {CELL_EISDIR};
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path))
|
||||
if (has_non_directory_components(local_path, ends_with_dot))
|
||||
{
|
||||
return {CELL_ENOTDIR};
|
||||
}
|
||||
@@ -1152,7 +1208,7 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 /*
|
||||
}
|
||||
}
|
||||
|
||||
auto [error, file] = open_raw(local_path, flags, has_fs_write_rights(vpath), type, mp);
|
||||
auto [error, file] = open_raw(local_path, flags, has_fs_write_rights(vpath), type, mp, ends_with_delim_dot_or_dotdot(vpath));
|
||||
|
||||
return {.error = error, .ppath = std::move(path), .real_path = std::move(local_path), .file = std::move(file), .type = type};
|
||||
}
|
||||
@@ -1492,7 +1548,7 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path))
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
{
|
||||
return { CELL_ENOTDIR, path };
|
||||
}
|
||||
@@ -1723,7 +1779,7 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path))
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
{
|
||||
return { CELL_ENOTDIR, path };
|
||||
}
|
||||
@@ -1868,7 +1924,7 @@ error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr<char> path, s32 mode)
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path))
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
{
|
||||
return { CELL_ENOTDIR, path };
|
||||
}
|
||||
@@ -1940,7 +1996,7 @@ error_code sys_fs_rename(ppu_thread& ppu, vm::cptr<char> from, vm::cptr<char> to
|
||||
case fs::error::exist: return {CELL_EEXIST, to};
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_from))
|
||||
if (has_non_directory_components(local_from, ends_with_delim_dot_or_dotdot(vfrom)))
|
||||
{
|
||||
return {CELL_ENOTDIR, from};
|
||||
}
|
||||
@@ -2002,7 +2058,7 @@ error_code sys_fs_rmdir(ppu_thread& ppu, vm::cptr<char> path)
|
||||
case fs::error::notempty: return {CELL_ENOTEMPTY, path};
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path))
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
{
|
||||
return { CELL_ENOTDIR, vpath };
|
||||
}
|
||||
@@ -2070,7 +2126,7 @@ error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr<char> path)
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path))
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
{
|
||||
return { CELL_ENOTDIR, path };
|
||||
}
|
||||
@@ -2915,7 +2971,7 @@ error_code sys_fs_get_block_size(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u
|
||||
case fs::error::noent: return {CELL_ENOENT, path};
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path))
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
{
|
||||
return { CELL_ENOTDIR, path };
|
||||
}
|
||||
@@ -2983,7 +3039,7 @@ error_code sys_fs_truncate(ppu_thread& ppu, vm::cptr<char> path, u64 size)
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path))
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
{
|
||||
return { CELL_ENOTDIR, path };
|
||||
}
|
||||
@@ -3103,7 +3159,7 @@ error_code sys_fs_chmod(ppu_thread&, vm::cptr<char> path, s32 mode)
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path))
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
{
|
||||
return { CELL_ENOTDIR, path };
|
||||
}
|
||||
@@ -3250,7 +3306,7 @@ error_code sys_fs_utime(ppu_thread& ppu, vm::cptr<char> path, vm::cptr<CellFsUti
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path))
|
||||
if (has_non_directory_components(local_path, ends_with_delim_dot_or_dotdot(vpath)))
|
||||
{
|
||||
return { CELL_ENOTDIR, path };
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ struct lv2_file final : lv2_fs_object
|
||||
};
|
||||
|
||||
// Open a file with wrapped logic of sys_fs_open
|
||||
static open_raw_result_t open_raw(const std::string& path, s32 flags, bool has_write_access, lv2_file_type type = lv2_file_type::regular, const lv2_fs_mount_info& mp = g_mi_sys_not_found);
|
||||
static open_raw_result_t open_raw(const std::string& path, s32 flags, bool has_write_access, lv2_file_type type = lv2_file_type::regular, const lv2_fs_mount_info& mp = g_mi_sys_not_found, bool ends_with_dot = false);
|
||||
static open_result_t open(std::string_view vpath, s32 flags, s32 mode, const void* arg = {}, u64 size = 0);
|
||||
|
||||
// File reading with intermediate buffer
|
||||
|
||||
Reference in New Issue
Block a user