mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-06 00:40:11 +00:00
Win32 FS: Rewrite (fix) vfs::host::rename
This commit is contained in:
parent
22269ca0d7
commit
b8fa6fb4c4
@ -1616,6 +1616,116 @@ bool fs::remove_all(const std::string& path, bool remove_root)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string fs::escape_path(std::string_view path)
|
||||
{
|
||||
std::string real; real.resize(path.size());
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr auto& delim = "/\\";
|
||||
#else
|
||||
constexpr auto& delim = "/";
|
||||
#endif
|
||||
|
||||
auto get_char = [&](std::size_t& from, std::size_t& to, std::size_t count)
|
||||
{
|
||||
std::memcpy(&real[to], &path[from], count);
|
||||
from += count, to += count;
|
||||
};
|
||||
|
||||
std::size_t i = 0, j = -1, pos_nondelim = 0, after_delim = 0;
|
||||
|
||||
if (i < path.size())
|
||||
{
|
||||
j = 0;
|
||||
}
|
||||
|
||||
for (; i < path.size();)
|
||||
{
|
||||
real[j] = path[i];
|
||||
#ifdef _Win32
|
||||
if (real[j] == '\\')
|
||||
{
|
||||
real[j] = '/';
|
||||
}
|
||||
#endif
|
||||
// If the current character was preceeded by a delimiter special treatment is required:
|
||||
// If another deleimiter is encountered, remove it (do not write it to output string)
|
||||
// Otherwise test if it is a "." or ".." sequence.
|
||||
if (std::exchange(after_delim, path[i] == delim[0] || path[i] == delim[1]))
|
||||
{
|
||||
if (!after_delim)
|
||||
{
|
||||
if (real[j] == '.')
|
||||
{
|
||||
if (i + 1 == path.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
get_char(i, j, 1);
|
||||
|
||||
switch (real[j])
|
||||
{
|
||||
case '.':
|
||||
{
|
||||
bool remove_element = true;
|
||||
std::size_t k = 1;
|
||||
|
||||
for (; k + i != path.size(); k++)
|
||||
{
|
||||
switch (path[i + k])
|
||||
{
|
||||
case '.': continue;
|
||||
case delim[0]: case delim[1]: break;
|
||||
default: remove_element = false; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (remove_element)
|
||||
{
|
||||
if (i == 1u)
|
||||
{
|
||||
j = pos_nondelim;
|
||||
real[j] = '\0';// Ensure termination at this posistion
|
||||
after_delim = true;
|
||||
i += k;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
get_char(i, j, k);
|
||||
continue;
|
||||
}
|
||||
case '/':
|
||||
{
|
||||
i++;
|
||||
after_delim = true;
|
||||
continue;
|
||||
}
|
||||
default: get_char(i, j, 1); continue;
|
||||
}
|
||||
}
|
||||
|
||||
pos_nondelim = j;
|
||||
get_char(i, j, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
get_char(i, j, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (j != umax && (real[j] == delim[0] || real[j] == delim[1])) j--; // Do not include a delmiter at the end
|
||||
|
||||
real.resize(j + 1);
|
||||
return real;
|
||||
}
|
||||
|
||||
u64 fs::get_dir_size(const std::string& path, u64 rounding_alignment)
|
||||
{
|
||||
u64 result = 0;
|
||||
|
@ -498,6 +498,9 @@ namespace fs
|
||||
// Get common cache directory
|
||||
const std::string& get_cache_dir();
|
||||
|
||||
// Get real path for comparisons (TODO: investigate std::filesystem::path::compare implementation)
|
||||
std::string escape_path(std::string_view path);
|
||||
|
||||
// Delete directory and all its contents recursively
|
||||
bool remove_all(const std::string& path, bool remove_root = true);
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "Emu/VFS.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Cell/lv2/sys_fs.h"
|
||||
|
||||
#include "cellSysutil.h"
|
||||
#include "cellMsgDialog.h"
|
||||
@ -555,7 +556,7 @@ error_code cellGameContentPermit(vm::ptr<char[CELL_GAME_PATH_MAX]> contentInfoPa
|
||||
psf::save_object(fs::file(perm->temp + "/PARAM.SFO", fs::rewrite), perm->sfo);
|
||||
|
||||
// Make temporary directory persistent (atomically)
|
||||
if (vfs::host::rename(perm->temp, vfs::get(dir), false))
|
||||
if (vfs::host::rename(perm->temp, vfs::get(dir), &g_mp_sys_dev_hdd0, false))
|
||||
{
|
||||
cellGame.success("cellGameContentPermit(): directory '%s' has been created", dir);
|
||||
|
||||
|
@ -78,11 +78,12 @@ error_code cellGifDecOpen(PMainHandle mainHandle, PPSubHandle subHandle, PSrc sr
|
||||
case CELL_GIFDEC_FILE:
|
||||
{
|
||||
// Get file descriptor and size
|
||||
fs::file file_s(vfs::get(src->fileName.get_ptr()));
|
||||
const auto real_path = vfs::get(src->fileName.get_ptr());
|
||||
fs::file file_s(real_path);
|
||||
if (!file_s) return CELL_GIFDEC_ERROR_OPEN_FILE;
|
||||
|
||||
current_subHandle.fileSize = file_s.size();
|
||||
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0);
|
||||
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0, real_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -69,11 +69,12 @@ error_code cellJpgDecOpen(u32 mainHandle, vm::ptr<u32> subHandle, vm::ptr<CellJp
|
||||
case CELL_JPGDEC_FILE:
|
||||
{
|
||||
// Get file descriptor and size
|
||||
fs::file file_s(vfs::get(src->fileName.get_ptr()));
|
||||
const auto real_path = vfs::get(src->fileName.get_ptr());
|
||||
fs::file file_s(real_path);
|
||||
if (!file_s) return CELL_JPGDEC_ERROR_OPEN_FILE;
|
||||
|
||||
current_subHandle.fileSize = file_s.size();
|
||||
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0);
|
||||
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0, real_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -432,8 +432,10 @@ error_code pngDecOpen(ppu_thread& ppu, PHandle handle, PPStream png_stream, PSrc
|
||||
// Depending on the source type, get the first 8 bytes
|
||||
if (source->srcSelect == CELL_PNGDEC_FILE)
|
||||
{
|
||||
const auto real_path = vfs::get(stream->source.fileName.get_ptr());
|
||||
|
||||
// Open a file stream
|
||||
fs::file file_stream(vfs::get(stream->source.fileName.get_ptr()));
|
||||
fs::file file_stream(real_path);
|
||||
|
||||
// Check if opening of the PNG file failed
|
||||
if (!file_stream)
|
||||
@ -450,7 +452,7 @@ error_code pngDecOpen(ppu_thread& ppu, PHandle handle, PPStream png_stream, PSrc
|
||||
}
|
||||
|
||||
// Get the file descriptor
|
||||
buffer->fd = idm::make<lv2_fs_object, lv2_file>(stream->source.fileName.get_ptr(), std::move(file_stream), 0, 0);
|
||||
buffer->fd = idm::make<lv2_fs_object, lv2_file>(stream->source.fileName.get_ptr(), std::move(file_stream), 0, 0, real_path);
|
||||
|
||||
// Indicate that we need to read from a file stream
|
||||
buffer->file = true;
|
||||
|
@ -964,7 +964,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
fs::remove_all(old_path);
|
||||
|
||||
// Remove savedata by renaming
|
||||
if (!vfs::host::rename(del_path, old_path, false))
|
||||
if (!vfs::host::rename(del_path, old_path, &g_mp_sys_dev_hdd0, false))
|
||||
{
|
||||
fmt::throw_exception("Failed to move directory %s (%s)", del_path, fs::g_tls_error);
|
||||
}
|
||||
@ -1917,13 +1917,13 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
fs::remove_all(old_path);
|
||||
|
||||
// Backup old savedata
|
||||
if (!vfs::host::rename(dir_path, old_path, false))
|
||||
if (!vfs::host::rename(dir_path, old_path, &g_mp_sys_dev_hdd0, false))
|
||||
{
|
||||
fmt::throw_exception("Failed to move directory %s (%s)", dir_path, fs::g_tls_error);
|
||||
}
|
||||
|
||||
// Commit new savedata
|
||||
if (!vfs::host::rename(new_path, dir_path, false))
|
||||
if (!vfs::host::rename(new_path, dir_path, &g_mp_sys_dev_hdd0, false))
|
||||
{
|
||||
// TODO: handle the case when only commit failed at the next save load
|
||||
fmt::throw_exception("Failed to move directory %s (%s)", new_path, fs::g_tls_error);
|
||||
|
@ -71,7 +71,7 @@ struct syscache_info
|
||||
void clear(bool remove_root) noexcept
|
||||
{
|
||||
// Clear cache
|
||||
if (!vfs::host::remove_all(cache_root + cache_id, cache_root, remove_root))
|
||||
if (!vfs::host::remove_all(cache_root + cache_id, cache_root, &g_mp_sys_dev_hdd1, remove_root))
|
||||
{
|
||||
cellSysutil.fatal("cellSysCache: failed to clear cache directory '%s%s' (%s)", cache_root, cache_id, fs::g_tls_error);
|
||||
}
|
||||
|
@ -230,33 +230,13 @@ error_code sys_fs_test(ppu_thread& ppu, u32 arg1, u32 arg2, vm::ptr<u32> arg3, u
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mode, const void* arg, u64 size)
|
||||
lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s32 flags, s32 mode, lv2_file_type type, const lv2_fs_mount_point* mp)
|
||||
{
|
||||
if (vpath.empty())
|
||||
{
|
||||
return {CELL_ENOENT};
|
||||
}
|
||||
|
||||
std::string path;
|
||||
const std::string local_path = vfs::get(vpath, nullptr, &path);
|
||||
|
||||
const auto mp = lv2_fs_object::get_mp(path);
|
||||
|
||||
if (vpath.find_first_not_of('/') == umax)
|
||||
{
|
||||
return {CELL_EISDIR, path};
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
return {CELL_ENOTMOUNTED, path};
|
||||
}
|
||||
|
||||
// TODO: other checks for path
|
||||
|
||||
if (fs::is_dir(local_path))
|
||||
{
|
||||
return {CELL_EISDIR, path};
|
||||
return {CELL_EISDIR};
|
||||
}
|
||||
|
||||
bs_t<fs::open_mode> open_mode{};
|
||||
@ -272,7 +252,7 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
||||
{
|
||||
if (flags & CELL_FS_O_ACCMODE || flags & (CELL_FS_O_CREAT | CELL_FS_O_TRUNC))
|
||||
{
|
||||
return {CELL_EPERM, path};
|
||||
return {CELL_EPERM};
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,14 +306,12 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
||||
|
||||
if (!open_mode)
|
||||
{
|
||||
fmt::throw_exception("lv2_file::open(%s): Invalid or unimplemented flags: %#o" HERE, path, flags);
|
||||
fmt::throw_exception("lv2_file::open_raw(): Invalid or unimplemented flags: %#o" HERE, flags);
|
||||
}
|
||||
|
||||
fs::file file;
|
||||
{
|
||||
std::lock_guard lock(mp->mutex);
|
||||
file.open(local_path, open_mode);
|
||||
}
|
||||
std::lock_guard lock(mp->mutex);
|
||||
|
||||
fs::file file(local_path, open_mode);
|
||||
|
||||
if (!file && open_mode == fs::read && fs::g_tls_error == fs::error::noent)
|
||||
{
|
||||
@ -365,7 +343,7 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
||||
// Failed to create file on read-only FS (file doesn't exist)
|
||||
if (flags & CELL_FS_O_CREAT)
|
||||
{
|
||||
return {CELL_EROFS, path};
|
||||
return {CELL_EROFS};
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,11 +354,11 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
||||
|
||||
switch (auto error = fs::g_tls_error)
|
||||
{
|
||||
case fs::error::noent: return {CELL_ENOENT, path};
|
||||
case fs::error::noent: return {CELL_ENOENT};
|
||||
default: sys_fs.error("lv2_file::open(): unknown error %s", error);
|
||||
}
|
||||
|
||||
return {CELL_EIO, path};
|
||||
return {CELL_EIO};
|
||||
}
|
||||
|
||||
if (mp->flags & lv2_mp_flag::read_only)
|
||||
@ -388,27 +366,27 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
||||
// Failed to create file on read-only FS (file exists)
|
||||
if (flags & CELL_FS_O_CREAT && flags & CELL_FS_O_EXCL)
|
||||
{
|
||||
return {CELL_EEXIST, path};
|
||||
return {CELL_EEXIST};
|
||||
}
|
||||
|
||||
// Failed to truncate file on read-only FS
|
||||
if (flags & CELL_FS_O_TRUNC)
|
||||
{
|
||||
return {CELL_EROFS, path};
|
||||
return {CELL_EROFS};
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & CELL_FS_O_MSELF && !verify_mself(file))
|
||||
{
|
||||
return {CELL_ENOTMSELF, path};
|
||||
return {CELL_ENOTMSELF};
|
||||
}
|
||||
|
||||
if (size == 8)
|
||||
if (type >= lv2_file_type::sdata)
|
||||
{
|
||||
// check for sdata
|
||||
switch (*static_cast<const be_t<u64>*>(arg))
|
||||
switch (type)
|
||||
{
|
||||
case 0x18000000010:
|
||||
case lv2_file_type::sdata:
|
||||
{
|
||||
// check if the file has the NPD header, or else assume its not encrypted
|
||||
u32 magic;
|
||||
@ -419,7 +397,7 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
||||
auto sdata_file = std::make_unique<EDATADecrypter>(std::move(file));
|
||||
if (!sdata_file->ReadHeader())
|
||||
{
|
||||
return {CELL_EFSSPECIFIC, path};
|
||||
return {CELL_EFSSPECIFIC};
|
||||
}
|
||||
|
||||
file.reset(std::move(sdata_file));
|
||||
@ -428,7 +406,7 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
||||
break;
|
||||
}
|
||||
// edata
|
||||
case 0x2:
|
||||
case lv2_file_type::edata:
|
||||
{
|
||||
// check if the file has the NPD header, or else assume its not encrypted
|
||||
u32 magic;
|
||||
@ -440,7 +418,7 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
||||
auto sdata_file = std::make_unique<EDATADecrypter>(std::move(file), edatkeys->devKlic.load(), edatkeys->rifKey.load());
|
||||
if (!sdata_file->ReadHeader())
|
||||
{
|
||||
return {CELL_EFSSPECIFIC, path};
|
||||
return {CELL_EFSSPECIFIC};
|
||||
}
|
||||
|
||||
file.reset(std::move(sdata_file));
|
||||
@ -452,7 +430,49 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
||||
}
|
||||
}
|
||||
|
||||
return {.error = {}, .ppath = path, .file = std::move(file)};
|
||||
return {.error = {}, .file = std::move(file)};
|
||||
}
|
||||
|
||||
lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mode, const void* arg, u64 size)
|
||||
{
|
||||
if (vpath.empty())
|
||||
{
|
||||
return {CELL_ENOENT};
|
||||
}
|
||||
|
||||
std::string path;
|
||||
const std::string local_path = vfs::get(vpath, nullptr, &path);
|
||||
|
||||
const auto mp = lv2_fs_object::get_mp(path);
|
||||
|
||||
if (vpath.find_first_not_of('/') == umax)
|
||||
{
|
||||
return {CELL_EISDIR, path};
|
||||
}
|
||||
|
||||
if (local_path.empty())
|
||||
{
|
||||
return {CELL_ENOTMOUNTED, path};
|
||||
}
|
||||
|
||||
|
||||
lv2_file_type type = lv2_file_type::regular;
|
||||
|
||||
if (size == 8)
|
||||
{
|
||||
// see lv2_file::open_raw
|
||||
switch (*static_cast<const be_t<u64>*>(arg))
|
||||
{
|
||||
case 0x18000000010: type = lv2_file_type::sdata; break;
|
||||
case 0x2: type = lv2_file_type::edata; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto [error, file] = open_raw(local_path, flags, mode, type, mp);
|
||||
|
||||
return {.error = error, .ppath = std::move(path), .real_path = std::move(local_path), .file = std::move(file), .type = type};
|
||||
}
|
||||
|
||||
error_code sys_fs_open(ppu_thread& ppu, vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, s32 mode, vm::cptr<void> arg, u64 size)
|
||||
@ -464,7 +484,7 @@ error_code sys_fs_open(ppu_thread& ppu, vm::cptr<char> path, s32 flags, vm::ptr<
|
||||
if (!path)
|
||||
return CELL_EFAULT;
|
||||
|
||||
auto [error, ppath, file] = lv2_file::open(path.get_ptr(), flags, mode, arg.get_ptr(), size);
|
||||
auto [error, ppath, real, file, type] = lv2_file::open(path.get_ptr(), flags, mode, arg.get_ptr(), size);
|
||||
|
||||
if (error)
|
||||
{
|
||||
@ -476,42 +496,25 @@ error_code sys_fs_open(ppu_thread& ppu, vm::cptr<char> path, s32 flags, vm::ptr<
|
||||
return {error, path};
|
||||
}
|
||||
|
||||
lv2_file_type type = lv2_file_type::regular;
|
||||
|
||||
if (size == 8)
|
||||
if (type >= lv2_file_type::sdata)
|
||||
{
|
||||
// see lv2_file::open
|
||||
switch (vm::read64(arg.addr()))
|
||||
{
|
||||
case 0x18000000010:
|
||||
case 0x2:
|
||||
{
|
||||
type = lv2_file_type::npdrm;
|
||||
sys_fs.warning("sys_fs_open(): NPDRM detected");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
sys_fs.warning("sys_fs_open(): NPDRM detected");
|
||||
|
||||
if (type == lv2_file_type::npdrm)
|
||||
{
|
||||
if (const u32 id = idm::import<lv2_fs_object, lv2_file>([&ppath = ppath, &file = file, mode, flags]() -> std::shared_ptr<lv2_file>
|
||||
if (const u32 id = idm::import<lv2_fs_object, lv2_file>([&ppath = ppath, &file = file, mode, flags, &real = real, &type = type]() -> std::shared_ptr<lv2_file>
|
||||
{
|
||||
if (!g_fxo->get<loaded_npdrm_keys>()->npdrm_fds.try_inc(16))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_shared<lv2_file>(ppath, std::move(file), mode, flags, lv2_file_type::npdrm);
|
||||
return std::make_shared<lv2_file>(ppath, std::move(file), mode, flags, real, type);
|
||||
}))
|
||||
{
|
||||
*fd = id;
|
||||
return CELL_OK;
|
||||
}
|
||||
}
|
||||
else if (const u32 id = idm::make<lv2_fs_object, lv2_file>(ppath, std::move(file), mode, flags))
|
||||
else if (const u32 id = idm::make<lv2_fs_object, lv2_file>(ppath, std::move(file), mode, flags, real))
|
||||
{
|
||||
*fd = id;
|
||||
return CELL_OK;
|
||||
@ -622,7 +625,7 @@ error_code sys_fs_close(ppu_thread& ppu, u32 fd)
|
||||
|
||||
const auto file = idm::withdraw<lv2_fs_object, lv2_file>(fd, [](lv2_file& file)
|
||||
{
|
||||
if (file.type == lv2_file_type::npdrm)
|
||||
if (file.type >= lv2_file_type::sdata)
|
||||
{
|
||||
g_fxo->get<loaded_npdrm_keys>()->npdrm_fds--;
|
||||
}
|
||||
@ -674,11 +677,9 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
|
||||
return {CELL_ENOTDIR, path};
|
||||
}
|
||||
|
||||
fs::dir dir;
|
||||
{
|
||||
std::lock_guard lock(mp->mutex);
|
||||
dir.open(local_path);
|
||||
}
|
||||
std::lock_guard lock(mp->mutex);
|
||||
|
||||
fs::dir dir(local_path);
|
||||
|
||||
if (!dir)
|
||||
{
|
||||
@ -1029,9 +1030,10 @@ error_code sys_fs_rename(ppu_thread& ppu, vm::cptr<char> from, vm::cptr<char> to
|
||||
return CELL_EROFS;
|
||||
}
|
||||
|
||||
std::lock_guard lock(mp->mutex);
|
||||
// Done in vfs::host::rename
|
||||
//std::lock_guard lock(mp->mutex);
|
||||
|
||||
if (!vfs::host::rename(local_from, local_to, false))
|
||||
if (!vfs::host::rename(local_from, local_to, mp, false))
|
||||
{
|
||||
switch (auto error = fs::g_tls_error)
|
||||
{
|
||||
@ -1266,6 +1268,8 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
|
||||
return CELL_EBADF;
|
||||
}
|
||||
|
||||
std::lock_guard lock(file->mp->mutex);
|
||||
|
||||
auto sdata_file = std::make_unique<EDATADecrypter>(lv2_file::make_view(file, arg->offset));
|
||||
|
||||
if (!sdata_file->ReadHeader())
|
||||
@ -1282,7 +1286,7 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_shared<lv2_file>(file, std::move(stream), file.mode, file.flags, lv2_file_type::npdrm);
|
||||
return std::make_shared<lv2_file>(file, std::move(stream), file.mode, file.flags, file.real_path, lv2_file_type::sdata);
|
||||
}))
|
||||
{
|
||||
arg->out_code = CELL_OK;
|
||||
|
@ -3,9 +3,9 @@
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Utilities/File.h"
|
||||
#include "Utilities/mutex.h"
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
// Open Flags
|
||||
enum : s32
|
||||
@ -134,7 +134,8 @@ enum class lv2_mp_flag
|
||||
enum class lv2_file_type
|
||||
{
|
||||
regular = 0,
|
||||
npdrm,
|
||||
sdata,
|
||||
edata,
|
||||
};
|
||||
|
||||
struct lv2_fs_mount_point
|
||||
@ -143,9 +144,17 @@ struct lv2_fs_mount_point
|
||||
const u32 block_size = 4096;
|
||||
const bs_t<lv2_mp_flag> flags{};
|
||||
|
||||
shared_mutex mutex;
|
||||
mutable std::recursive_mutex mutex;
|
||||
};
|
||||
|
||||
extern lv2_fs_mount_point g_mp_sys_dev_hdd0;
|
||||
extern lv2_fs_mount_point g_mp_sys_dev_hdd1;
|
||||
extern lv2_fs_mount_point g_mp_sys_dev_usb;
|
||||
extern lv2_fs_mount_point g_mp_sys_dev_bdvd;
|
||||
extern lv2_fs_mount_point g_mp_sys_app_home;
|
||||
extern lv2_fs_mount_point g_mp_sys_host_root;
|
||||
extern lv2_fs_mount_point g_mp_sys_dev_flash;
|
||||
|
||||
struct lv2_fs_object
|
||||
{
|
||||
using id_type = lv2_fs_object;
|
||||
@ -185,41 +194,61 @@ struct lv2_fs_object
|
||||
|
||||
struct lv2_file final : lv2_fs_object
|
||||
{
|
||||
const fs::file file;
|
||||
fs::file file;
|
||||
const s32 mode;
|
||||
const s32 flags;
|
||||
std::string real_path;
|
||||
const lv2_file_type type;
|
||||
|
||||
// Stream lock
|
||||
atomic_t<u32> lock{0};
|
||||
|
||||
lv2_file(std::string_view filename, fs::file&& file, s32 mode, s32 flags, lv2_file_type type = {})
|
||||
// Some variables for convinience of data restoration
|
||||
struct save_restore_t
|
||||
{
|
||||
u64 seek_pos;
|
||||
u64 atime;
|
||||
u64 mtime;
|
||||
} restore_data{};
|
||||
|
||||
lv2_file(std::string_view filename, fs::file&& file, s32 mode, s32 flags, const std::string& real_path, lv2_file_type type = {})
|
||||
: lv2_fs_object(lv2_fs_object::get_mp(filename), filename)
|
||||
, file(std::move(file))
|
||||
, mode(mode)
|
||||
, flags(flags)
|
||||
, real_path(real_path)
|
||||
, type(type)
|
||||
{
|
||||
}
|
||||
|
||||
lv2_file(const lv2_file& host, fs::file&& file, s32 mode, s32 flags, lv2_file_type type = {})
|
||||
lv2_file(const lv2_file& host, fs::file&& file, s32 mode, s32 flags, const std::string& real_path, lv2_file_type type = {})
|
||||
: lv2_fs_object(host.mp, host.name.data())
|
||||
, file(std::move(file))
|
||||
, mode(mode)
|
||||
, flags(flags)
|
||||
, real_path(real_path)
|
||||
, type(type)
|
||||
{
|
||||
}
|
||||
|
||||
struct open_raw_result_t
|
||||
{
|
||||
CellError error;
|
||||
fs::file file;
|
||||
};
|
||||
|
||||
struct open_result_t
|
||||
{
|
||||
CellError error;
|
||||
std::string ppath;
|
||||
std::string real_path;
|
||||
fs::file file;
|
||||
lv2_file_type type;
|
||||
};
|
||||
|
||||
// Open a file with wrapped logic of sys_fs_open
|
||||
static open_result_t open(std::string_view path, s32 flags, s32 mode, const void* arg = {}, u64 size = 0);
|
||||
static open_raw_result_t open_raw(const std::string& path, s32 flags, s32 mode, lv2_file_type type = lv2_file_type::regular, const lv2_fs_mount_point* mp = nullptr);
|
||||
static open_result_t open(std::string_view vpath, s32 flags, s32 mode, const void* arg = {}, u64 size = 0);
|
||||
|
||||
// File reading with intermediate buffer
|
||||
static u64 op_read(const fs::file& file, vm::ptr<void> buf, u64 size);
|
||||
|
@ -21,7 +21,7 @@ static error_code overlay_load_module(vm::ptr<u32> ovlmid, const std::string& vp
|
||||
{
|
||||
if (!src)
|
||||
{
|
||||
auto [fs_error, ppath, lv2_file] = lv2_file::open(vpath, 0, 0);
|
||||
auto [fs_error, ppath, path, lv2_file, type] = lv2_file::open(vpath, 0, 0);
|
||||
|
||||
if (fs_error)
|
||||
{
|
||||
|
@ -156,7 +156,7 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
|
||||
|
||||
if (!src)
|
||||
{
|
||||
auto [fs_error, ppath, lv2_file] = lv2_file::open(vpath, 0, 0);
|
||||
auto [fs_error, ppath, path0, lv2_file, type] = lv2_file::open(vpath, 0, 0);
|
||||
|
||||
if (fs_error)
|
||||
{
|
||||
|
@ -242,7 +242,7 @@ error_code sys_spu_image_open(ppu_thread& ppu, vm::ptr<sys_spu_image> img, vm::c
|
||||
|
||||
sys_spu.warning("sys_spu_image_open(img=*0x%x, path=%s)", img, path);
|
||||
|
||||
auto [fs_error, ppath, file] = lv2_file::open(path.get_ptr(), 0, 0);
|
||||
auto [fs_error, ppath, path0, file, type] = lv2_file::open(path.get_ptr(), 0, 0);
|
||||
|
||||
if (fs_error)
|
||||
{
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include "System.h"
|
||||
#include "VFS.h"
|
||||
|
||||
#include "Cell/lv2/sys_fs.h"
|
||||
|
||||
#include "Utilities/mutex.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
@ -10,6 +12,8 @@
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#include <thread>
|
||||
|
||||
struct vfs_directory
|
||||
{
|
||||
// Real path (empty if root or not exists)
|
||||
@ -706,18 +710,77 @@ std::string vfs::host::hash_path(const std::string& path, const std::string& dev
|
||||
return fmt::format(u8"%s/$%s%s", dev_root, fmt::base57(std::hash<std::string>()(path)), fmt::base57(__rdtsc()));
|
||||
}
|
||||
|
||||
bool vfs::host::rename(const std::string& from, const std::string& to, bool overwrite)
|
||||
bool vfs::host::rename(const std::string& from, const std::string& to, const lv2_fs_mount_point* mp, bool overwrite)
|
||||
{
|
||||
while (!fs::rename(from, to, overwrite))
|
||||
#ifdef _WIN32
|
||||
constexpr auto& delim = "/\\";
|
||||
#else
|
||||
constexpr auto& delim = "/";
|
||||
#endif
|
||||
|
||||
// Lock mount point, close file descriptors, retry
|
||||
const auto from0 = std::string_view(from).substr(0, from.find_last_not_of(delim) + 1);
|
||||
const auto escaped_from = fs::escape_path(from);
|
||||
|
||||
// Lock app_home as well because it could be in the same drive as current mount point (TODO)
|
||||
std::scoped_lock lock(mp->mutex, g_mp_sys_app_home.mutex);
|
||||
|
||||
auto check_path = [&](std::string_view path)
|
||||
{
|
||||
// Try to ignore access error in order to prevent spurious failure
|
||||
return path.starts_with(from) && (path.size() == from.size() || path[from.size()] == delim[0] || path[from.size()] == delim[1]);
|
||||
};
|
||||
|
||||
idm::select<lv2_fs_object, lv2_file>([&](u32 id, lv2_file& file)
|
||||
{
|
||||
if (check_path(fs::escape_path(file.real_path)))
|
||||
{
|
||||
verify(HERE), file.mp == mp || file.mp == &g_mp_sys_app_home;
|
||||
file.restore_data.seek_pos = file.file.pos();
|
||||
file.file.close(); // Actually close it!
|
||||
}
|
||||
});
|
||||
|
||||
bool res = false;
|
||||
|
||||
for (;; std::this_thread::yield())
|
||||
{
|
||||
if (fs::rename(from, to, overwrite))
|
||||
{
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Emu.IsStopped() || fs::g_tls_error != fs::error::acces)
|
||||
{
|
||||
return false;
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
const auto fs_error = fs::g_tls_error;
|
||||
|
||||
idm::select<lv2_fs_object, lv2_file>([&](u32 id, lv2_file& file)
|
||||
{
|
||||
const auto escaped_real = fs::escape_path(file.real_path);
|
||||
|
||||
if (check_path(escaped_real))
|
||||
{
|
||||
// Update internal path
|
||||
if (res)
|
||||
{
|
||||
file.real_path = to + (escaped_real != escaped_from ? '/' + file.real_path.substr(from0.size()) : ""s);
|
||||
}
|
||||
|
||||
// Reopen with ignored TRUNC, APPEND, CREATE and EXCL flags
|
||||
auto res0 = lv2_file::open_raw(file.real_path, file.flags & CELL_FS_O_ACCMODE, file.mode, file.type, file.mp);
|
||||
file.file = std::move(res0.file);
|
||||
verify(HERE), file.file.operator bool();
|
||||
file.file.seek(file.restore_data.seek_pos);
|
||||
}
|
||||
});
|
||||
|
||||
fs::g_tls_error = fs_error;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool vfs::host::unlink(const std::string& path, const std::string& dev_root)
|
||||
@ -754,7 +817,7 @@ bool vfs::host::unlink(const std::string& path, const std::string& dev_root)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool vfs::host::remove_all(const std::string& path, const std::string& dev_root, bool remove_root)
|
||||
bool vfs::host::remove_all(const std::string& path, const std::string& dev_root, const lv2_fs_mount_point* mp, bool remove_root)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (remove_root)
|
||||
@ -762,12 +825,12 @@ bool vfs::host::remove_all(const std::string& path, const std::string& dev_root,
|
||||
// Rename to special dummy folder which will be ignored by VFS (but opened file handles can still read or write it)
|
||||
const std::string dummy = hash_path(path, dev_root);
|
||||
|
||||
if (!vfs::host::rename(path, dummy, false))
|
||||
if (!vfs::host::rename(path, dummy, mp, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vfs::host::remove_all(dummy, dev_root, false))
|
||||
if (!vfs::host::remove_all(dummy, dev_root, mp, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -802,7 +865,7 @@ bool vfs::host::remove_all(const std::string& path, const std::string& dev_root,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!vfs::host::remove_all(path + '/' + entry.name, dev_root))
|
||||
if (!vfs::host::remove_all(path + '/' + entry.name, dev_root, mp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
struct lv2_fs_mount_point;
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
// Mount VFS device
|
||||
@ -24,12 +26,12 @@ namespace vfs
|
||||
std::string hash_path(const std::string& path, const std::string& dev_root);
|
||||
|
||||
// Call fs::rename with retry on access error
|
||||
bool rename(const std::string& from, const std::string& to, bool overwrite);
|
||||
bool rename(const std::string& from, const std::string& to, const lv2_fs_mount_point* mp, bool overwrite);
|
||||
|
||||
// Delete file without deleting its contents, emulated with MoveFileEx on Windows
|
||||
bool unlink(const std::string& path, const std::string& dev_root);
|
||||
|
||||
// Delete folder contents using rename, done atomically if remove_root is true
|
||||
bool remove_all(const std::string& path, const std::string& dev_root, bool remove_root = true);
|
||||
bool remove_all(const std::string& path, const std::string& dev_root, const lv2_fs_mount_point* mp, bool remove_root = true);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/VFS.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/lv2/sys_fs.h"
|
||||
#include "TRP.h"
|
||||
#include "Crypto/sha1.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
@ -41,7 +42,11 @@ bool TRPLoader::Install(const std::string& dest, bool show)
|
||||
{
|
||||
trp_f.seek(entry.offset);
|
||||
buffer.resize(entry.size);
|
||||
if (!trp_f.read(buffer)) continue; // ???
|
||||
if (!trp_f.read(buffer))
|
||||
{
|
||||
trp_log.error("Failed to read TRPEntry at: offset=0x%x, size=0x%x", entry.offset, entry.size);
|
||||
continue; // ???
|
||||
}
|
||||
|
||||
// Create the file in the temporary directory
|
||||
success = fs::write_file(temp + vfs::escape(entry.name), fs::create + fs::excl, buffer);
|
||||
@ -53,7 +58,7 @@ bool TRPLoader::Install(const std::string& dest, bool show)
|
||||
|
||||
if (success)
|
||||
{
|
||||
success = vfs::host::remove_all(local_path, Emu.GetHddDir(), true) || !fs::is_dir(local_path);
|
||||
success = vfs::host::remove_all(local_path, Emu.GetHddDir(), &g_mp_sys_dev_hdd0, true) || !fs::is_dir(local_path);
|
||||
|
||||
if (success)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user