mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-04-25 12:29:46 -06:00
sys_fs: Reimplement path analysis
This commit is contained in:
parent
b729772041
commit
48acbbe4f5
@ -14,7 +14,6 @@
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <span>
|
||||
#include <shared_mutex>
|
||||
|
||||
@ -93,15 +92,22 @@ void fmt_class_string<lv2_dir>::format(std::string& out, u64 arg)
|
||||
bool has_fs_write_rights(std::string_view vpath)
|
||||
{
|
||||
// VSH has access to everything
|
||||
if (g_ps3_process_info.has_root_perm())
|
||||
return true;
|
||||
const bool has_root_perm = g_ps3_process_info.has_root_perm();
|
||||
|
||||
const auto norm_vpath = lv2_fs_object::get_normalized_path(vpath);
|
||||
const auto parent_dir = fs::get_parent_dir_view(norm_vpath);
|
||||
const auto parent_dir = fs::get_parent_dir_view(vpath);
|
||||
const auto [dev_root, trail] = lv2_fs_object::get_path_root_and_trail(parent_dir);
|
||||
|
||||
// This is not exhaustive, PS3 has a unix filesystem with rights for each directory and files
|
||||
// This is mostly meant to protect against games doing insane things(ie NPUB30003 => NPUB30008)
|
||||
if (parent_dir == "/dev_hdd0" || parent_dir == "/dev_hdd0/game")
|
||||
// This is mostly meant to protect against games doing insane things (ie NPUB30003 => NPUB30008)
|
||||
if (dev_root == "dev_hdd0"sv && (trail.empty() || trail == "game"sv))
|
||||
return has_root_perm;
|
||||
|
||||
// This is read-only for games
|
||||
if (dev_root.starts_with("dev_flash"sv))
|
||||
return has_root_perm;
|
||||
|
||||
// Technically should not reach here, but handle it anyways
|
||||
if (dev_root == "dev_bdvd"sv || dev_root == "dev_ps2disc"sv || dev_root.empty())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -205,27 +211,29 @@ bool lv2_fs_mount_info_map::remove(std::string_view path)
|
||||
|
||||
const lv2_fs_mount_info& lv2_fs_mount_info_map::lookup(std::string_view path, bool no_cell_fs_path, std::string* mount_path) const
|
||||
{
|
||||
if (path.starts_with("/"sv))
|
||||
const auto [dev_root, trail] = lv2_fs_object::get_path_root_and_trail(path);
|
||||
|
||||
if (dev_root.empty())
|
||||
{
|
||||
if (trail.empty())
|
||||
{
|
||||
return map.find("/")->second;
|
||||
}
|
||||
|
||||
return g_mi_sys_not_found;
|
||||
}
|
||||
|
||||
if (const auto iterator = map.find("/" + std::string{dev_root}); iterator != map.end())
|
||||
{
|
||||
constexpr std::string_view cell_fs_path = "CELL_FS_PATH:"sv;
|
||||
const std::string normalized_path = lv2_fs_object::get_normalized_path(path);
|
||||
std::string_view parent_dir;
|
||||
u32 parent_level = 0;
|
||||
|
||||
do
|
||||
{
|
||||
parent_dir = fs::get_parent_dir_view(normalized_path, parent_level++);
|
||||
if (const auto iterator = map.find(parent_dir); iterator != map.end())
|
||||
{
|
||||
if (iterator->second == &g_mp_sys_dev_root && parent_level > 1)
|
||||
break;
|
||||
if (no_cell_fs_path && iterator->second.device.starts_with(cell_fs_path))
|
||||
return lookup(iterator->second.device.substr(cell_fs_path.size()), no_cell_fs_path, mount_path); // Recursively look up the parent mount info
|
||||
if (mount_path)
|
||||
*mount_path = iterator->first;
|
||||
return iterator->second;
|
||||
}
|
||||
} while (parent_dir.length() > 1); // Exit the loop when parent_dir == "/" or empty
|
||||
if (no_cell_fs_path && iterator->second.device.starts_with(cell_fs_path))
|
||||
return lookup(iterator->second.device.substr(cell_fs_path.size()), no_cell_fs_path, mount_path); // Recursively look up the parent mount info
|
||||
|
||||
if (mount_path)
|
||||
*mount_path = iterator->first;
|
||||
|
||||
return iterator->second;
|
||||
}
|
||||
|
||||
return g_mi_sys_not_found;
|
||||
@ -287,36 +295,89 @@ bool lv2_fs_mount_info_map::vfs_unmount(std::string_view vpath, bool remove_from
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string lv2_fs_object::get_normalized_path(std::string_view path)
|
||||
std::pair<std::string_view, std::string> lv2_fs_object::get_path_root_and_trail(std::string_view filename)
|
||||
{
|
||||
std::string normalized_path = std::filesystem::path(path).lexically_normal().string();
|
||||
|
||||
#ifdef _WIN32
|
||||
std::replace(normalized_path.begin(), normalized_path.end(), '\\', '/');
|
||||
#endif
|
||||
|
||||
if (normalized_path.ends_with('/'))
|
||||
normalized_path.pop_back();
|
||||
|
||||
return normalized_path.empty() ? "/" : normalized_path;
|
||||
}
|
||||
|
||||
std::string lv2_fs_object::get_device_root(std::string_view filename)
|
||||
{
|
||||
std::string path = get_normalized_path(filename); // Prevent getting fooled by ".." trick such as "/dev_usb000/../dev_flash"
|
||||
|
||||
if (const auto first = path.find_first_not_of("/"sv); first != umax)
|
||||
if (filename.empty())
|
||||
{
|
||||
if (const auto pos = path.substr(first).find_first_of("/"sv); pos != umax)
|
||||
path = path.substr(0, first + pos);
|
||||
path = path.substr(std::max<std::make_signed_t<usz>>(0, first - 1)); // Remove duplicate leading '/' while keeping only one
|
||||
}
|
||||
else
|
||||
{
|
||||
path = path.substr(0, 1);
|
||||
// Should CELL_ENOENT later - root cannot have a trail
|
||||
return {""sv, "ENOENT"};
|
||||
}
|
||||
|
||||
return path;
|
||||
std::string_view root;
|
||||
std::string trail;
|
||||
|
||||
usz level = 0;
|
||||
usz pos = 0;
|
||||
|
||||
while (pos != umax)
|
||||
{
|
||||
const usz ndl_pos = filename.find_first_not_of("/", pos);
|
||||
|
||||
if (ndl_pos == pos)
|
||||
{
|
||||
// Should CELL_ENOENT later - root cannot have a trail
|
||||
return {""sv, "ENOENT"};
|
||||
}
|
||||
|
||||
if (ndl_pos == umax)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const usz dl_pos = ndl_pos == umax ? usz{umax} : filename.find_first_of("/", ndl_pos);
|
||||
std::string_view component = filename.substr(ndl_pos, dl_pos - ndl_pos);
|
||||
|
||||
if (component == "."sv)
|
||||
{
|
||||
// No change
|
||||
// level += 0;
|
||||
pos = dl_pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (component == ".."sv)
|
||||
{
|
||||
if (level > 1)
|
||||
{
|
||||
ensure(!trail.empty());
|
||||
trail.resize(trail.find_last_of("/") + 1);
|
||||
trail.resize(trail.find_last_not_of("/") + 1);
|
||||
}
|
||||
else if (level == 1)
|
||||
{
|
||||
// Reset root
|
||||
root = {};
|
||||
}
|
||||
else//if (level == 0)
|
||||
{
|
||||
// Should CELL_ENOENT later - root cannot have a trail
|
||||
return {""sv, "ENOENT"};
|
||||
}
|
||||
|
||||
ensure(level)--;
|
||||
pos = dl_pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (level == 0)
|
||||
{
|
||||
root = component;
|
||||
}
|
||||
else if (trail.empty())
|
||||
{
|
||||
trail = std::string{component};
|
||||
}
|
||||
else
|
||||
{
|
||||
trail += "/";
|
||||
trail.append(component);
|
||||
}
|
||||
|
||||
level++;
|
||||
pos = dl_pos;
|
||||
}
|
||||
|
||||
return { root, std::move(trail) };
|
||||
}
|
||||
|
||||
lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string* vfs_path)
|
||||
@ -328,7 +389,7 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string
|
||||
filename.remove_prefix(cell_fs_path.size());
|
||||
|
||||
const bool is_path = filename.starts_with("/"sv);
|
||||
std::string mp_name = is_path ? get_device_root(filename) : std::string(filename);
|
||||
std::string mp_name = is_path ? std::string{get_path_root_and_trail(filename).first} : std::string(filename);
|
||||
|
||||
const auto check_mp = [&]()
|
||||
{
|
||||
@ -1405,6 +1466,10 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
|
||||
|
||||
break;
|
||||
}
|
||||
case fs::error::notdir:
|
||||
{
|
||||
return { CELL_ENOTDIR, path };
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (has_non_directory_components(local_path))
|
||||
@ -3398,7 +3463,7 @@ error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
|
||||
return {path_error, path_sv};
|
||||
}
|
||||
|
||||
const std::string vpath = lv2_fs_object::get_normalized_path(path_sv);
|
||||
const auto [root_name, trail] = lv2_fs_object::get_path_root_and_trail(path_sv);
|
||||
|
||||
std::string vfs_path;
|
||||
const auto mp = lv2_fs_object::get_mp(device_name, &vfs_path);
|
||||
@ -3416,8 +3481,8 @@ error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
|
||||
if (vfs_path.empty())
|
||||
return {CELL_ENOTSUP, device_name};
|
||||
|
||||
if (vpath.find_first_not_of('/') == umax || !vfs::get(vpath).empty())
|
||||
return {CELL_EEXIST, vpath};
|
||||
if (root_name.empty() || !vfs::get(path_sv).empty())
|
||||
return {CELL_EEXIST, path_sv};
|
||||
|
||||
if (mp == &g_mp_sys_dev_hdd1)
|
||||
{
|
||||
@ -3452,7 +3517,7 @@ error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
|
||||
}
|
||||
}
|
||||
|
||||
if (!vfs::mount(vpath, vfs_path, !is_simplefs))
|
||||
if (!vfs::mount("/" + std::string{root_name}, vfs_path, !is_simplefs))
|
||||
{
|
||||
if (is_simplefs)
|
||||
{
|
||||
@ -3469,7 +3534,7 @@ error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
|
||||
return CELL_EIO;
|
||||
}
|
||||
|
||||
g_fxo->get<lv2_fs_mount_info_map>().add(vpath, mp, device_name, filesystem, prot);
|
||||
g_fxo->get<lv2_fs_mount_info_map>().add("/" + std::string{root_name}, mp, device_name, filesystem, prot);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -245,11 +245,15 @@ public:
|
||||
|
||||
lv2_fs_object& operator=(const lv2_fs_object&) = delete;
|
||||
|
||||
// Normalize a virtual path
|
||||
static std::string get_normalized_path(std::string_view path);
|
||||
// Get the device's root path (e.g. "/dev_hdd0") from a given path
|
||||
// Cut the trail and return it in seccond argument
|
||||
static std::pair<std::string_view, std::string> get_path_root_and_trail(std::string_view path);
|
||||
|
||||
// Get the device's root path (e.g. "/dev_hdd0") from a given path
|
||||
static std::string get_device_root(std::string_view filename);
|
||||
static std::string get_device_root(std::string_view filename)
|
||||
{
|
||||
return std::string{get_path_root_and_trail(filename).first};
|
||||
}
|
||||
|
||||
// Filename can be either a path starting with '/' or a CELL_FS device name
|
||||
// This should be used only when handling devices that are not mounted
|
||||
|
||||
Loading…
Reference in New Issue
Block a user