Win32 FS: Rewrite (fix) vfs::host::rename

This commit is contained in:
Eladash 2020-09-11 14:06:46 +03:00 committed by Ivan
parent 22269ca0d7
commit b8fa6fb4c4
16 changed files with 329 additions and 108 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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)
{