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)
|
// 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};
|
std::string sub_path{path};
|
||||||
|
|
||||||
fs::stat_t stat{};
|
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)
|
while (true)
|
||||||
{
|
{
|
||||||
std::string_view path_sv = fs::get_parent_dir_view(sub_path);
|
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())
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,13 +212,18 @@ bool has_non_directory_components(std::string_view path)
|
|||||||
{
|
{
|
||||||
if (!stat.is_directory)
|
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;
|
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);
|
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;
|
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()
|
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
|
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;
|
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
|
// 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};
|
case fs::error::isdir: return {CELL_EISDIR};
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
if (has_non_directory_components(local_path))
|
if (has_non_directory_components(local_path, ends_with_dot))
|
||||||
{
|
{
|
||||||
return {CELL_ENOTDIR};
|
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};
|
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:
|
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 };
|
return { CELL_ENOTDIR, path };
|
||||||
}
|
}
|
||||||
@@ -1723,7 +1779,7 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
|
|||||||
}
|
}
|
||||||
default:
|
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 };
|
return { CELL_ENOTDIR, path };
|
||||||
}
|
}
|
||||||
@@ -1868,7 +1924,7 @@ error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr<char> path, s32 mode)
|
|||||||
}
|
}
|
||||||
default:
|
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 };
|
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};
|
case fs::error::exist: return {CELL_EEXIST, to};
|
||||||
default:
|
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};
|
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};
|
case fs::error::notempty: return {CELL_ENOTEMPTY, path};
|
||||||
default:
|
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 };
|
return { CELL_ENOTDIR, vpath };
|
||||||
}
|
}
|
||||||
@@ -2070,7 +2126,7 @@ error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr<char> path)
|
|||||||
}
|
}
|
||||||
default:
|
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 };
|
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};
|
case fs::error::noent: return {CELL_ENOENT, path};
|
||||||
default:
|
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 };
|
return { CELL_ENOTDIR, path };
|
||||||
}
|
}
|
||||||
@@ -2983,7 +3039,7 @@ error_code sys_fs_truncate(ppu_thread& ppu, vm::cptr<char> path, u64 size)
|
|||||||
}
|
}
|
||||||
default:
|
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 };
|
return { CELL_ENOTDIR, path };
|
||||||
}
|
}
|
||||||
@@ -3103,7 +3159,7 @@ error_code sys_fs_chmod(ppu_thread&, vm::cptr<char> path, s32 mode)
|
|||||||
}
|
}
|
||||||
default:
|
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 };
|
return { CELL_ENOTDIR, path };
|
||||||
}
|
}
|
||||||
@@ -3250,7 +3306,7 @@ error_code sys_fs_utime(ppu_thread& ppu, vm::cptr<char> path, vm::cptr<CellFsUti
|
|||||||
}
|
}
|
||||||
default:
|
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 };
|
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
|
// 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);
|
static open_result_t open(std::string_view vpath, s32 flags, s32 mode, const void* arg = {}, u64 size = 0);
|
||||||
|
|
||||||
// File reading with intermediate buffer
|
// File reading with intermediate buffer
|
||||||
|
|||||||
Reference in New Issue
Block a user