mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-03-16 07:20:59 +00:00
Escape problematic characters in VFS
With full-width <>:"\|?*
This commit is contained in:
parent
19f3bb8cb0
commit
5b19908996
@ -4,6 +4,7 @@
|
|||||||
#include "sha1.h"
|
#include "sha1.h"
|
||||||
#include "key_vault.h"
|
#include "key_vault.h"
|
||||||
#include "Utilities/StrFmt.h"
|
#include "Utilities/StrFmt.h"
|
||||||
|
#include "Emu/VFS.h"
|
||||||
#include "unpkg.h"
|
#include "unpkg.h"
|
||||||
|
|
||||||
bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>& sync, const std::string& pkg_filepath)
|
bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>& sync, const std::string& pkg_filepath)
|
||||||
@ -285,7 +286,7 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
|||||||
|
|
||||||
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : dec_key.data());
|
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : dec_key.data());
|
||||||
|
|
||||||
const std::string name(reinterpret_cast<char*>(buf.get()), entry.name_size);
|
std::string name{reinterpret_cast<char*>(buf.get()), entry.name_size};
|
||||||
|
|
||||||
LOG_NOTICE(LOADER, "Entry 0x%08x: %s", entry.type, name);
|
LOG_NOTICE(LOADER, "Entry 0x%08x: %s", entry.type, name);
|
||||||
|
|
||||||
@ -303,17 +304,17 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
|||||||
case 0x15:
|
case 0x15:
|
||||||
case 0x16:
|
case 0x16:
|
||||||
{
|
{
|
||||||
const std::string path = dir + name;
|
const std::string path = dir + vfs::escape(name);
|
||||||
|
|
||||||
const bool did_overwrite = fs::is_file(path);
|
const bool did_overwrite = fs::is_file(path);
|
||||||
|
|
||||||
if (did_overwrite && (entry.type&PKG_FILE_ENTRY_OVERWRITE) == 0)
|
if (did_overwrite && (entry.type & PKG_FILE_ENTRY_OVERWRITE) == 0)
|
||||||
{
|
{
|
||||||
LOG_NOTICE(LOADER, "Didn't overwrite %s", name);
|
LOG_NOTICE(LOADER, "Didn't overwrite %s", name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fs::file out{ path, fs::rewrite })
|
if (fs::file out{path, fs::rewrite})
|
||||||
{
|
{
|
||||||
for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE)
|
for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE)
|
||||||
{
|
{
|
||||||
@ -358,7 +359,7 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
|||||||
case PKG_FILE_ENTRY_FOLDER:
|
case PKG_FILE_ENTRY_FOLDER:
|
||||||
case 0x12:
|
case 0x12:
|
||||||
{
|
{
|
||||||
const std::string path = dir + name;
|
const std::string path = dir + vfs::escape(name);
|
||||||
|
|
||||||
if (fs::create_dir(path))
|
if (fs::create_dir(path))
|
||||||
{
|
{
|
||||||
|
@ -103,13 +103,15 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
|||||||
const auto prefix_list = fmt::split(setList->dirNamePrefix.get_ptr(), { "|" });
|
const auto prefix_list = fmt::split(setList->dirNamePrefix.get_ptr(), { "|" });
|
||||||
|
|
||||||
// get the saves matching the supplied prefix
|
// get the saves matching the supplied prefix
|
||||||
for (const auto& entry : fs::dir(base_dir))
|
for (auto&& entry : fs::dir(base_dir))
|
||||||
{
|
{
|
||||||
if (!entry.is_directory)
|
if (!entry.is_directory)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry.name = vfs::unescape(entry.name);
|
||||||
|
|
||||||
for (const auto& prefix : prefix_list)
|
for (const auto& prefix : prefix_list)
|
||||||
{
|
{
|
||||||
if (entry.name.substr(0, prefix.size()) == prefix)
|
if (entry.name.substr(0, prefix.size()) == prefix)
|
||||||
@ -453,8 +455,10 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
|||||||
|
|
||||||
auto file_list = statGet->fileList.get_ptr();
|
auto file_list = statGet->fileList.get_ptr();
|
||||||
|
|
||||||
for (const auto& entry : fs::dir(dir_path))
|
for (auto&& entry : fs::dir(dir_path))
|
||||||
{
|
{
|
||||||
|
entry.name = vfs::unescape(entry.name);
|
||||||
|
|
||||||
// only files, system files ignored, fileNum is limited by setBuf->fileListMax
|
// only files, system files ignored, fileNum is limited by setBuf->fileListMax
|
||||||
if (!entry.is_directory && entry.name != "PARAM.SFO" && statGet->fileListNum++ < setBuf->fileListMax)
|
if (!entry.is_directory && entry.name != "PARAM.SFO" && statGet->fileListNum++ < setBuf->fileListMax)
|
||||||
{
|
{
|
||||||
|
@ -480,9 +480,10 @@ error_code sys_fs_readdir(u32 fd, vm::ptr<CellFsDirent> dir, vm::ptr<u64> nread)
|
|||||||
|
|
||||||
if (directory->dir.read(info))
|
if (directory->dir.read(info))
|
||||||
{
|
{
|
||||||
|
const std::string vfs_name = vfs::unescape(info.name);
|
||||||
dir->d_type = info.is_directory ? CELL_FS_TYPE_DIRECTORY : CELL_FS_TYPE_REGULAR;
|
dir->d_type = info.is_directory ? CELL_FS_TYPE_DIRECTORY : CELL_FS_TYPE_REGULAR;
|
||||||
dir->d_namlen = u8(std::min<size_t>(info.name.size(), CELL_FS_MAX_FS_FILE_NAME_LENGTH));
|
dir->d_namlen = u8(std::min<size_t>(vfs_name.size(), CELL_FS_MAX_FS_FILE_NAME_LENGTH));
|
||||||
strcpy_trunc(dir->d_name, info.name);
|
strcpy_trunc(dir->d_name, vfs_name);
|
||||||
*nread = sizeof(CellFsDirent);
|
*nread = sizeof(CellFsDirent);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1014,9 +1015,10 @@ error_code sys_fs_fcntl(u32 fd, u32 op, vm::ptr<void> _arg, u32 _size)
|
|||||||
entry.attribute.size = info.size;
|
entry.attribute.size = info.size;
|
||||||
entry.attribute.blksize = 4096; // ???
|
entry.attribute.blksize = 4096; // ???
|
||||||
|
|
||||||
|
const std::string vfs_name = vfs::unescape(info.name);
|
||||||
entry.entry_name.d_type = info.is_directory ? CELL_FS_TYPE_DIRECTORY : CELL_FS_TYPE_REGULAR;
|
entry.entry_name.d_type = info.is_directory ? CELL_FS_TYPE_DIRECTORY : CELL_FS_TYPE_REGULAR;
|
||||||
entry.entry_name.d_namlen = u8(std::min<size_t>(info.name.size(), CELL_FS_MAX_FS_FILE_NAME_LENGTH));
|
entry.entry_name.d_namlen = u8(std::min<size_t>(vfs_name.size(), CELL_FS_MAX_FS_FILE_NAME_LENGTH));
|
||||||
strcpy_trunc(entry.entry_name.d_name, info.name);
|
strcpy_trunc(entry.entry_name.d_name, vfs_name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "IdManager.h"
|
#include "IdManager.h"
|
||||||
#include "VFS.h"
|
#include "VFS.h"
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ std::string vfs::get(const std::string& vpath, vfs::type _type)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return found->second + vpath;
|
return found->second + vfs::escape(vpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_type == type::ps3 && match.length(1) == 0)
|
if (_type == type::ps3 && match.length(1) == 0)
|
||||||
@ -58,6 +58,231 @@ std::string vfs::get(const std::string& vpath, vfs::type _type)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concatenate
|
if (found->second.empty())
|
||||||
return found->second + match.str(2);
|
{
|
||||||
|
// Don't escape /host_root (TODO)
|
||||||
|
return match.str(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape and concatenate
|
||||||
|
return found->second + vfs::escape(match.str(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string vfs::escape(const std::string& path)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
result.reserve(path.size());
|
||||||
|
|
||||||
|
for (std::size_t i = 0, s = path.size(); i < s; i++)
|
||||||
|
{
|
||||||
|
switch (char c = path[i])
|
||||||
|
{
|
||||||
|
case '<':
|
||||||
|
{
|
||||||
|
result += u8"<";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '>':
|
||||||
|
{
|
||||||
|
result += u8">";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ':':
|
||||||
|
{
|
||||||
|
result += u8":";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '"':
|
||||||
|
{
|
||||||
|
result += u8""";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '\\':
|
||||||
|
{
|
||||||
|
result += u8"\";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '|':
|
||||||
|
{
|
||||||
|
result += u8"|";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '?':
|
||||||
|
{
|
||||||
|
result += u8"?";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '*':
|
||||||
|
{
|
||||||
|
result += u8"*";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case char{u8"!"[0]}:
|
||||||
|
{
|
||||||
|
// Escape full-width characters 0xFF01..0xFF5e with ! (0xFF01)
|
||||||
|
switch (path[i + 1])
|
||||||
|
{
|
||||||
|
case char{u8"!"[1]}:
|
||||||
|
{
|
||||||
|
const uchar c3 = reinterpret_cast<const uchar&>(path[i + 2]);
|
||||||
|
|
||||||
|
if (c3 >= 0x81 && c3 <= 0xbf)
|
||||||
|
{
|
||||||
|
result += u8"!";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case char{u8"`"[1]}:
|
||||||
|
{
|
||||||
|
const uchar c3 = reinterpret_cast<const uchar&>(path[i + 2]);
|
||||||
|
|
||||||
|
if (c3 >= 0x80 && c3 <= 0x9e)
|
||||||
|
{
|
||||||
|
result += u8"!";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result += c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
result += c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string vfs::unescape(const std::string& path)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
result.reserve(path.size());
|
||||||
|
|
||||||
|
for (std::size_t i = 0, s = path.size(); i < s; i++)
|
||||||
|
{
|
||||||
|
switch (char c = path[i])
|
||||||
|
{
|
||||||
|
case char{u8"!"[0]}:
|
||||||
|
{
|
||||||
|
switch (path[i + 1])
|
||||||
|
{
|
||||||
|
case char{u8"!"[1]}:
|
||||||
|
{
|
||||||
|
const uchar c3 = reinterpret_cast<const uchar&>(path[i + 2]);
|
||||||
|
|
||||||
|
if (c3 >= 0x81 && c3 <= 0xbf)
|
||||||
|
{
|
||||||
|
switch (path[i + 2])
|
||||||
|
{
|
||||||
|
case char{u8"!"[2]}:
|
||||||
|
{
|
||||||
|
i += 3;
|
||||||
|
result += c;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case char{u8"<"[2]}:
|
||||||
|
{
|
||||||
|
result += '<';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case char{u8">"[2]}:
|
||||||
|
{
|
||||||
|
result += '>';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case char{u8":"[2]}:
|
||||||
|
{
|
||||||
|
result += ':';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case char{u8"""[2]}:
|
||||||
|
{
|
||||||
|
result += '"';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case char{u8"\"[2]}:
|
||||||
|
{
|
||||||
|
result += '\\';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case char{u8"?"[2]}:
|
||||||
|
{
|
||||||
|
result += '?';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case char{u8"*"[2]}:
|
||||||
|
{
|
||||||
|
result += '*';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// Unrecognized character (ignored)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case char{u8"`"[1]}:
|
||||||
|
{
|
||||||
|
const uchar c3 = reinterpret_cast<const uchar&>(path[i + 2]);
|
||||||
|
|
||||||
|
if (c3 >= 0x80 && c3 <= 0x9e)
|
||||||
|
{
|
||||||
|
switch (path[i + 2])
|
||||||
|
{
|
||||||
|
case char{u8"|"[2]}:
|
||||||
|
{
|
||||||
|
result += '|';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// Unrecognized character (ignored)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
result += c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
result += c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -16,4 +16,10 @@ namespace vfs
|
|||||||
|
|
||||||
// Convert VFS path to fs path
|
// Convert VFS path to fs path
|
||||||
std::string get(const std::string& vpath, type _type = type::ps3);
|
std::string get(const std::string& vpath, type _type = type::ps3);
|
||||||
|
|
||||||
|
// Escape VFS path by replacing non-portable characters with surrogates
|
||||||
|
std::string escape(const std::string& path);
|
||||||
|
|
||||||
|
// Invert escape operation
|
||||||
|
std::string unescape(const std::string& path);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user