mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-03-15 22:21:25 +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 "key_vault.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Emu/VFS.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)
|
||||
@ -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());
|
||||
|
||||
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);
|
||||
|
||||
@ -303,17 +304,17 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
case 0x15:
|
||||
case 0x16:
|
||||
{
|
||||
const std::string path = dir + name;
|
||||
const std::string path = dir + vfs::escape(name);
|
||||
|
||||
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);
|
||||
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)
|
||||
{
|
||||
@ -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 0x12:
|
||||
{
|
||||
const std::string path = dir + name;
|
||||
const std::string path = dir + vfs::escape(name);
|
||||
|
||||
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(), { "|" });
|
||||
|
||||
// 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)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.name = vfs::unescape(entry.name);
|
||||
|
||||
for (const auto& prefix : prefix_list)
|
||||
{
|
||||
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();
|
||||
|
||||
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
|
||||
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))
|
||||
{
|
||||
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_namlen = u8(std::min<size_t>(info.name.size(), CELL_FS_MAX_FS_FILE_NAME_LENGTH));
|
||||
strcpy_trunc(dir->d_name, info.name);
|
||||
dir->d_namlen = u8(std::min<size_t>(vfs_name.size(), CELL_FS_MAX_FS_FILE_NAME_LENGTH));
|
||||
strcpy_trunc(dir->d_name, vfs_name);
|
||||
*nread = sizeof(CellFsDirent);
|
||||
}
|
||||
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.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_namlen = u8(std::min<size_t>(info.name.size(), CELL_FS_MAX_FS_FILE_NAME_LENGTH));
|
||||
strcpy_trunc(entry.entry_name.d_name, info.name);
|
||||
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, vfs_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "IdManager.h"
|
||||
#include "VFS.h"
|
||||
|
||||
@ -42,7 +42,7 @@ std::string vfs::get(const std::string& vpath, vfs::type _type)
|
||||
return {};
|
||||
}
|
||||
|
||||
return found->second + vpath;
|
||||
return found->second + vfs::escape(vpath);
|
||||
}
|
||||
|
||||
if (_type == type::ps3 && match.length(1) == 0)
|
||||
@ -58,6 +58,231 @@ std::string vfs::get(const std::string& vpath, vfs::type _type)
|
||||
return {};
|
||||
}
|
||||
|
||||
// Concatenate
|
||||
return found->second + match.str(2);
|
||||
if (found->second.empty())
|
||||
{
|
||||
// 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
|
||||
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