diff --git a/Utilities/File.cpp b/Utilities/File.cpp index eaebe1c99d..4a5eccd3cd 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -244,6 +244,11 @@ namespace fs #endif } + file_id file_base::get_id() + { + return {}; + } + u64 file_base::write_gather(const iovec_clone* buffers, u64 buf_count) { u64 total = 0; @@ -272,6 +277,66 @@ namespace fs return this->write(buf.get(), total); } + file_id::operator bool() const + { + return !type.empty() && !data.empty(); + } + + // Test if identical + // For example: when LHS writes one byte to a file at X offset, RHS file be able to read that exact byte at X offset) + bool file_id::is_mirror_of(const file_id& rhs) const + { + // If either is an invalid ID we cannot compare safely, return false + return rhs && type == rhs.type && data == rhs.data; + } + + // Test if both files point to the same file + // For example: if a file descriptor pointing to the complete file exists and is being truncated to 0 bytes from non- + // -zero size state: this has to affect both RHS and LHS files. + bool file_id::is_coherent_with(const file_id& rhs) const + { + struct id_view + { + std::string_view type_view; + std::basic_string_view data_view; + }; + + id_view _rhs{rhs.type, {rhs.data.data(), rhs.data.size()}}; + id_view _lhs{type, {data.data(), data.size()}}; + + // Peek through fs::file wrappers + auto peek_wrapppers = [](id_view& id) + { + u64 offset_count = 0; + constexpr auto lv2_view = "lv2_file::file_view: "sv; + + for (usz pos = 0; (pos = id.type_view.find(lv2_view, pos)) != umax; pos += lv2_view.size()) + { + offset_count++; + } + + // Remove offsets data + id.data_view.remove_suffix(sizeof(u64) * offset_count); + + // Get last category identifier + if (usz sep = id.type_view.rfind(": "); sep != umax) + { + id.type_view.remove_prefix(sep + 2); + } + }; + + peek_wrapppers(_rhs); + peek_wrapppers(_lhs); + + // If either is an invalid ID we cannot compare safely, return false + if (_rhs.type_view.empty() || _rhs.data_view.empty()) + { + return false; + } + + return _rhs.type_view == _lhs.type_view && _rhs.data_view == _lhs.data_view; + } + dir_base::~dir_base() { } @@ -1289,6 +1354,28 @@ fs::file::file(const std::string& path, bs_t mode) { return m_handle; } + + file_id get_id() override + { + file_id id{"windows_file"}; + id.data.resize(sizeof(FILE_ID_INFO)); + + FILE_ID_INFO info; + + if (!GetFileInformationByHandleEx(m_handle, FileIdInfo, &info, sizeof(info))) + { + // Try GetFileInformationByHandle as a fallback + BY_HANDLE_FILE_INFORMATION info2; + ensure(GetFileInformationByHandle(m_handle, &info2)); + + info = {}; + info.VolumeSerialNumber = info2.dwVolumeSerialNumber; + std::memcpy(&info.FileId, &info2.nFileIndexHigh, 8); + } + + std::memcpy(id.data.data(), &info, sizeof(info)); + return id; + } }; m_file = std::make_unique(handle); @@ -1481,6 +1568,19 @@ fs::file::file(const std::string& path, bs_t mode) return m_fd; } + file_id get_id() override + { + struct ::stat file_info; + ensure(::fstat(m_fd, &file_info) == 0); // "file::get_id" + + file_id id{"unix_file"}; + id.data.resize(sizeof(file_info.st_dev) + sizeof(file_info.st_ino)); + + std::memcpy(id.data.data(), &file_info.st_dev, sizeof(file_info.st_dev)); + std::memcpy(id.data.data() + sizeof(file_info.st_dev), &file_info.st_ino, sizeof(file_info.st_ino)); + return id; + } + u64 write_gather(const iovec_clone* buffers, u64 buf_count) override { static_assert(sizeof(iovec) == sizeof(iovec_clone), "Weird iovec size"); @@ -1615,6 +1715,16 @@ fs::native_handle fs::file::get_handle() const #endif } +fs::file_id fs::file::get_id() const +{ + if (m_file) + { + return m_file->get_id(); + } + + return {}; +} + bool fs::dir::open(const std::string& path) { if (path.empty()) @@ -2256,3 +2366,12 @@ void fmt_class_string::format(std::string& out, u64 arg) return unknown; }); } + +template<> +void fmt_class_string::format(std::string& out, u64 arg) +{ + const fs::file_id& id = get_object(arg); + + // TODO: Format data + fmt::append(out, "{type='%s'}", id.type); +} diff --git a/Utilities/File.h b/Utilities/File.h index 34b0b8341b..ca9d74bed4 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -84,6 +84,16 @@ namespace fs usz iov_len; }; + struct file_id + { + std::string type; + std::vector data; + + explicit operator bool() const; + bool is_mirror_of(const file_id&) const; + bool is_coherent_with(const file_id&) const; + }; + // File handle base struct file_base { @@ -98,6 +108,7 @@ namespace fs virtual u64 seek(s64 offset, seek_mode whence) = 0; virtual u64 size() = 0; virtual native_handle get_handle(); + virtual file_id get_id(); virtual u64 write_gather(const iovec_clone* buffers, u64 buf_count); }; @@ -505,6 +516,9 @@ namespace fs // Get native handle if available native_handle get_handle() const; + // Get file ID information (custom ID) + file_id get_id() const; + // Gathered write u64 write_gather(const iovec_clone* buffers, u64 buf_count, u32 line = __builtin_LINE(), diff --git a/rpcs3/Crypto/unedat.h b/rpcs3/Crypto/unedat.h index bb7d7f664f..3ff54d1024 100644 --- a/rpcs3/Crypto/unedat.h +++ b/rpcs3/Crypto/unedat.h @@ -143,4 +143,11 @@ public: } u64 size() override { return file_size; } + + fs::file_id get_id() override + { + fs::file_id id = edata_file.get_id(); + id.type.insert(0, "EDATADecrypter: "sv); + return id; + } }; diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.cpp b/rpcs3/Emu/Cell/lv2/sys_fs.cpp index 5f8cc1ef23..df755bcf68 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_fs.cpp @@ -641,6 +641,18 @@ struct lv2_file::file_view : fs::file_base { return m_file->file.size(); } + + fs::file_id get_id() override + { + fs::file_id id = m_file->file.get_id(); + + be_t off = m_off; + const auto ptr = reinterpret_cast(&off); + + id.data.insert(id.data.end(), ptr, ptr + sizeof(off)); + id.type.insert(0, "lv2_file::file_view: "sv); + return id; + } }; fs::file lv2_file::make_view(const std::shared_ptr& _file, u64 offset)