fix: Raw Disk Provider not working correctly on Linux and macOS (#1195)

<!--
Please provide as much information as possible about what your PR aims
to do.
PRs with no description will most likely be closed until more
information is provided.
If you're planing on changing fundamental behaviour or add big new
features, please open a GitHub Issue first before starting to work on
it.
If it's not something big and you still want to contact us about it,
feel free to do so !
-->

### Problem description
- Fixed disk provider not working for linux

### Implementation description
- Used ioctl instead of fstat
- Fixed buffer issues

---------

Co-authored-by: WerWolv <werwolv98@gmail.com>
This commit is contained in:
Justus Garbe
2023-07-16 18:18:41 +02:00
committed by GitHub
parent 893b06c78b
commit 1a3debd6c9

View File

@@ -16,16 +16,25 @@
#include <imgui.h>
#if defined(OS_WINDOWS)
#include <winioctl.h>
#elif defined(OS_LINUX) || defined(OS_MACOS)
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <winioctl.h>
#elif defined(OS_LINUX)
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#elif defined(OS_MACOS)
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/disk.h>
#endif
#if defined(OS_LINUX)
#define lseek lseek64
#define lseek lseek64
#endif
namespace hex::plugin::builtin {
@@ -35,15 +44,15 @@ namespace hex::plugin::builtin {
}
bool DiskProvider::isAvailable() const {
#if defined(OS_WINDOWS)
#if defined(OS_WINDOWS)
return this->m_diskHandle != INVALID_HANDLE_VALUE;
return this->m_diskHandle != INVALID_HANDLE_VALUE;
#else
#else
return this->m_diskHandle != -1;
return this->m_diskHandle != -1;
#endif
#endif
}
bool DiskProvider::isReadable() const {
@@ -67,13 +76,75 @@ namespace hex::plugin::builtin {
this->m_path = path;
}
#if defined (OS_LINUX)
#ifdef BLKSSZGET
int blkdev_get_sector_size(int fd, int *sector_size) {
if (ioctl(fd, BLKSSZGET, sector_size) < 0)
return -1;
return 0;
}
#else
int blkdev_get_sector_size(int fd, int *sector_size) {
(void)fd;
*sector_size = DEFAULT_SECTOR_SIZE;
return 0;
}
#endif
#ifdef BLKGETSIZE64
int blkdev_get_size(int fd, u64 *bytes) {
if (ioctl(fd, BLKGETSIZE64, bytes) < 0)
return -1;
return 0;
}
#else
int blkdev_get_size(int fd, u64 *bytes) {
struct stat st;
if (fstat(fd, &st) < 0)
return -1;
if (st.st_size == 0) {
// try BLKGETSIZE
unsigned long long bytes64;
if (ioctl(fd, BLKGETSIZE, &bytes64) >= 0) {
*bytes = bytes64;
return 0;
}
}
*bytes = st.st_size;
return 0;
}
#endif
#elif defined(OS_MACOS)
int blkdev_get_sector_size(int fd, int *sector_size) {
if (ioctl(fd, DKIOCGETBLOCKSIZE, sector_size) >= 0)
return 0;
return -1;
}
int blkdev_get_size(int fd, u64 *bytes) {
int sectorSize = 0;
if (blkdev_get_sector_size(fd, &sectorSize) < 0)
return -1;
if (ioctl(fd, DKIOCGETBLOCKCOUNT, bytes) < 0)
return -1;
*bytes *= sectorSize;
return 0;
}
#endif
bool DiskProvider::open() {
this->m_readable = true;
this->m_writable = true;
#if defined(OS_WINDOWS)
#if defined(OS_WINDOWS)
const auto &path = this->m_path.native();
const auto &path = this->m_path.native();
this->m_diskHandle = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (this->m_diskHandle == INVALID_HANDLE_VALUE) {
@@ -110,61 +181,59 @@ namespace hex::plugin::builtin {
return false;
}
#else
#else
const auto &path = this->m_path.native();
const auto &path = this->m_path.native();
struct stat driveStat;
this->m_diskHandle = ::open(path.c_str(), O_RDWR);
if (this->m_diskHandle == -1) {
this->setErrorMessage(hex::format("hex.builtin.provider.disk.error.read_rw"_lang, path, ::strerror(errno)));
log::warn(this->getErrorMessage());
this->m_diskHandle = ::open(path.c_str(), O_RDONLY);
this->m_writable = false;
}
if (::stat(path.c_str(), &driveStat) == 0)
this->m_diskSize = driveStat.st_size;
else
this->m_diskSize = 0;
if (this->m_diskHandle == -1) {
this->setErrorMessage(hex::format("hex.builtin.provider.disk.error.read_ro"_lang, path, ::strerror(errno)));
log::warn(this->getErrorMessage());
this->m_readable = false;
return false;
}
this->m_sectorSize = 0;
u64 diskSize = 0;
blkdev_get_size(this->m_diskHandle, &diskSize);
this->m_diskSize = diskSize;
blkdev_get_sector_size(this->m_diskHandle, reinterpret_cast<int *>(&this->m_sectorSize));
this->m_diskHandle = ::open(path.c_str(), O_RDWR);
if (this->m_diskHandle == -1) {
this->setErrorMessage(hex::format("hex.builtin.provider.disk.error.read_rw"_lang, path, ::strerror(errno)));
log::warn(this->getErrorMessage());
this->m_diskHandle = ::open(path.c_str(), O_RDONLY);
this->m_writable = false;
}
this->m_sectorBuffer.resize(this->m_sectorSize);
if (this->m_diskHandle == -1) {
this->setErrorMessage(hex::format("hex.builtin.provider.disk.error.read_ro"_lang, path, ::strerror(errno)));
log::warn(this->getErrorMessage());
this->m_readable = false;
return false;
}
#endif
#endif
return true;
}
void DiskProvider::close() {
#if defined(OS_WINDOWS)
#if defined(OS_WINDOWS)
if (this->m_diskHandle != INVALID_HANDLE_VALUE)
if (this->m_diskHandle != INVALID_HANDLE_VALUE)
::CloseHandle(this->m_diskHandle);
this->m_diskHandle = INVALID_HANDLE_VALUE;
#else
#else
if (this->m_diskHandle != -1)
::close(this->m_diskHandle);
if (this->m_diskHandle != -1)
::close(this->m_diskHandle);
this->m_diskHandle = -1;
this->m_diskHandle = -1;
#endif
#endif
}
void DiskProvider::readRaw(u64 offset, void *buffer, size_t size) {
#if defined(OS_WINDOWS)
#if defined(OS_WINDOWS)
DWORD bytesRead = 0;
DWORD bytesRead = 0;
u64 startOffset = offset;
@@ -185,34 +254,36 @@ namespace hex::plugin::builtin {
offset += this->m_sectorSize;
}
#else
#else
u64 startOffset = offset;
while (size > 0) {
u64 seekPosition = offset - (offset % this->m_sectorSize);
if (this->m_sectorBufferAddress != seekPosition) {
if (this->m_sectorBufferAddress != seekPosition || this->m_sectorBufferAddress == 0) {
::lseek(this->m_diskHandle, seekPosition, SEEK_SET);
if (::read(this->m_diskHandle, buffer, size) < 0)
if (::read(this->m_diskHandle, this->m_sectorBuffer.data(), this->m_sectorBuffer.size()) == -1)
break;
this->m_sectorBufferAddress = seekPosition;
}
std::memcpy(reinterpret_cast<u8 *>(buffer) + (offset - startOffset), this->m_sectorBuffer.data() + (offset & (this->m_sectorSize - 1)), std::min(this->m_sectorSize, size));
std::memcpy(reinterpret_cast<u8 *>(buffer) + (offset - startOffset),
this->m_sectorBuffer.data() + (offset & (this->m_sectorSize - 1)),
std::min(this->m_sectorSize, size));
size = std::max<ssize_t>(static_cast<ssize_t>(size) - this->m_sectorSize, 0);
offset += this->m_sectorSize;
}
#endif
#endif
}
void DiskProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
#if defined(OS_WINDOWS)
#if defined(OS_WINDOWS)
DWORD bytesWritten = 0;
DWORD bytesWritten = 0;
u64 startOffset = offset;
@@ -237,29 +308,29 @@ namespace hex::plugin::builtin {
size -= currSize;
}
#else
#else
u64 startOffset = offset;
u64 startOffset = offset;
std::vector<u8> modifiedSectorBuffer;
modifiedSectorBuffer.resize(this->m_sectorSize);
std::vector<u8> modifiedSectorBuffer;
modifiedSectorBuffer.resize(this->m_sectorSize);
while (size > 0) {
u64 sectorBase = offset - (offset % this->m_sectorSize);
size_t currSize = std::min(size, this->m_sectorSize);
while (size > 0) {
u64 sectorBase = offset - (offset % this->m_sectorSize);
size_t currSize = std::min(size, this->m_sectorSize);
this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size());
std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast<const u8 *>(buffer) + (startOffset - offset), currSize);
this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size());
std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast<const u8 *>(buffer) + (startOffset - offset), currSize);
::lseek(this->m_diskHandle, sectorBase, SEEK_SET);
if (::write(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size()) < 0)
break;
::lseek(this->m_diskHandle, sectorBase, SEEK_SET);
if (::write(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size()) < 0)
break;
offset += currSize;
size -= currSize;
}
offset += currSize;
size -= currSize;
}
#endif
#endif
}
size_t DiskProvider::getActualSize() const {
@@ -272,17 +343,17 @@ namespace hex::plugin::builtin {
std::vector<DiskProvider::Description> DiskProvider::getDataDescription() const {
return {
{ "hex.builtin.provider.disk.selected_disk"_lang, wolv::util::toUTF8String(this->m_path) },
{ "hex.builtin.provider.disk.disk_size"_lang, hex::toByteString(this->m_diskSize) },
{ "hex.builtin.provider.disk.sector_size"_lang, hex::toByteString(this->m_sectorSize) }
{ "hex.builtin.provider.disk.selected_disk"_lang, wolv::util::toUTF8String(this->m_path) },
{ "hex.builtin.provider.disk.disk_size"_lang, hex::toByteString(this->m_diskSize) },
{ "hex.builtin.provider.disk.sector_size"_lang, hex::toByteString(this->m_sectorSize) }
};
}
void DiskProvider::reloadDrives() {
#if defined(OS_WINDOWS)
#if defined(OS_WINDOWS)
this->m_availableDrives.clear();
this->m_availableDrives.clear();
std::bitset<32> drives = ::GetLogicalDrives();
for (char i = 0; i < 26; i++) {
if (drives[i])
@@ -315,13 +386,13 @@ namespace hex::plugin::builtin {
::CloseHandle(handle);
}
#endif
#endif
}
bool DiskProvider::drawLoadInterface() {
#if defined(OS_WINDOWS)
#if defined(OS_WINDOWS)
if (this->m_availableDrives.empty())
if (this->m_availableDrives.empty())
this->reloadDrives();
if (ImGui::BeginListBox("hex.builtin.provider.disk.selected_disk"_lang)) {
@@ -340,12 +411,12 @@ namespace hex::plugin::builtin {
this->reloadDrives();
}
#else
#else
if (ImGui::InputText("hex.builtin.provider.disk.selected_disk"_lang, this->m_pathBuffer.data(), this->m_pathBuffer.size(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &this->m_pathBuffer))
this->m_path = this->m_pathBuffer;
if (ImGui::InputText("hex.builtin.provider.disk.selected_disk"_lang, this->m_pathBuffer.data(), this->m_pathBuffer.size(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &this->m_pathBuffer))
this->m_path = this->m_pathBuffer;
#endif
#endif
return !this->m_path.empty();
}