mirror of
https://github.com/cmclark00/retro-imager.git
synced 2025-05-18 16:05:21 +01:00
176 lines
4.5 KiB
C++
176 lines
4.5 KiB
C++
|
/*
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
* Copyright (C) 2022 Raspberry Pi Ltd
|
||
|
*/
|
||
|
|
||
|
#include "devicewrapper.h"
|
||
|
#include "devicewrapperblockcacheentry.h"
|
||
|
#include "devicewrapperstructs.h"
|
||
|
#include "devicewrapperfatpartition.h"
|
||
|
#include <QDebug>
|
||
|
|
||
|
DeviceWrapper::DeviceWrapper(DeviceWrapperFile *file, QObject *parent)
|
||
|
: QObject(parent), _dirty(false), _file(file)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
DeviceWrapper::~DeviceWrapper()
|
||
|
{
|
||
|
sync();
|
||
|
}
|
||
|
|
||
|
void DeviceWrapper::_seekToBlock(quint64 blockNr)
|
||
|
{
|
||
|
if (!_file->seek(blockNr*4096))
|
||
|
{
|
||
|
throw std::runtime_error("Error seeking device");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DeviceWrapper::sync()
|
||
|
{
|
||
|
if (!_dirty)
|
||
|
return;
|
||
|
|
||
|
const auto blockNrs = _blockcache.keys();
|
||
|
for (auto blockNr : blockNrs)
|
||
|
{
|
||
|
if (blockNr == 0)
|
||
|
continue; /* Save writing first block with MBR for last */
|
||
|
|
||
|
auto block = _blockcache.value(blockNr);
|
||
|
|
||
|
if (!block->dirty)
|
||
|
continue;
|
||
|
|
||
|
_seekToBlock(blockNr);
|
||
|
if (_file->write(block->block, 4096) != 4096)
|
||
|
{
|
||
|
std::string errmsg = "Error writing to device: "+_file->errorString().toStdString();
|
||
|
throw std::runtime_error(errmsg);
|
||
|
}
|
||
|
block->dirty = false;
|
||
|
}
|
||
|
|
||
|
if (_blockcache.contains(0))
|
||
|
{
|
||
|
/* Write first block with MBR */
|
||
|
auto block = _blockcache.value(0);
|
||
|
|
||
|
if (block->dirty)
|
||
|
{
|
||
|
_seekToBlock(0);
|
||
|
if (_file->write(block->block, 4096) != 4096)
|
||
|
{
|
||
|
std::string errmsg = "Error writing MBR to device: "+_file->errorString().toStdString();
|
||
|
throw std::runtime_error(errmsg);
|
||
|
}
|
||
|
block->dirty = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_dirty = false;
|
||
|
}
|
||
|
|
||
|
void DeviceWrapper::_readIntoBlockCacheIfNeeded(quint64 offset, quint64 size)
|
||
|
{
|
||
|
if (!size)
|
||
|
return;
|
||
|
|
||
|
quint64 firstBlock = offset/4096;
|
||
|
quint64 lastBlock = (offset+size)/4096;
|
||
|
|
||
|
for (auto i = firstBlock; i <= lastBlock; i++)
|
||
|
{
|
||
|
if (!_blockcache.contains(i))
|
||
|
{
|
||
|
_seekToBlock(i);
|
||
|
|
||
|
auto cacheEntry = new DeviceWrapperBlockCacheEntry(this);
|
||
|
int bytesRead = _file->read(cacheEntry->block, 4096);
|
||
|
if (bytesRead != 4096)
|
||
|
{
|
||
|
std::string errmsg = "Error reading from device: "+_file->errorString().toStdString();
|
||
|
throw std::runtime_error(errmsg);
|
||
|
}
|
||
|
_blockcache.insert(i, cacheEntry);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DeviceWrapper::pread(char *buf, quint64 size, quint64 offset)
|
||
|
{
|
||
|
if (!size)
|
||
|
return;
|
||
|
|
||
|
_readIntoBlockCacheIfNeeded(offset, size);
|
||
|
quint64 firstBlock = offset / 4096;
|
||
|
quint64 offsetInBlock = offset % 4096;
|
||
|
|
||
|
for (auto i = firstBlock; size; i++)
|
||
|
{
|
||
|
auto block = _blockcache.value(i);
|
||
|
size_t bytesToCopyFromBlock = qMin(4096-offsetInBlock, size);
|
||
|
memcpy(buf, block->block + offsetInBlock, bytesToCopyFromBlock);
|
||
|
|
||
|
buf += bytesToCopyFromBlock;
|
||
|
size -= bytesToCopyFromBlock;
|
||
|
offsetInBlock = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DeviceWrapper::pwrite(const char *buf, quint64 size, quint64 offset)
|
||
|
{
|
||
|
if (!size)
|
||
|
return;
|
||
|
|
||
|
quint64 firstBlock = offset / 4096;
|
||
|
quint64 offsetInBlock = offset % 4096;
|
||
|
|
||
|
if (offsetInBlock || size % 4096)
|
||
|
{
|
||
|
/* Need to read existing data from disk
|
||
|
as we will only be replacing a part of a block. */
|
||
|
_readIntoBlockCacheIfNeeded(offset, size);
|
||
|
}
|
||
|
|
||
|
for (auto i = firstBlock; size; i++)
|
||
|
{
|
||
|
auto block = _blockcache.value(i, NULL);
|
||
|
if (!block)
|
||
|
{
|
||
|
block = new DeviceWrapperBlockCacheEntry(this);
|
||
|
_blockcache.insert(i, block);
|
||
|
}
|
||
|
|
||
|
block->dirty = true;
|
||
|
size_t bytesToCopyFromBlock = qMin(4096-offsetInBlock, size);
|
||
|
memcpy(block->block + offsetInBlock, buf, bytesToCopyFromBlock);
|
||
|
|
||
|
buf += bytesToCopyFromBlock;
|
||
|
size -= bytesToCopyFromBlock;
|
||
|
offsetInBlock = 0;
|
||
|
}
|
||
|
|
||
|
_dirty = true;
|
||
|
}
|
||
|
|
||
|
DeviceWrapperFatPartition *DeviceWrapper::fatPartition(int nr)
|
||
|
{
|
||
|
if (nr > 4 || nr < 1)
|
||
|
throw std::runtime_error("Only basic partitions 1-4 supported");
|
||
|
|
||
|
struct mbr_table mbr;
|
||
|
pread((char *) &mbr, sizeof(mbr), 0);
|
||
|
|
||
|
if (mbr.signature[0] != 0x55 || mbr.signature[1] != 0xAA)
|
||
|
throw std::runtime_error("MBR does not have valid signature");
|
||
|
|
||
|
if (!mbr.part[nr-1].starting_sector || !mbr.part[nr-1].nr_of_sectors)
|
||
|
throw std::runtime_error("Partition does not exist");
|
||
|
|
||
|
return new DeviceWrapperFatPartition(this, mbr.part[nr-1].starting_sector*512, mbr.part[nr-1].nr_of_sectors*512, this);
|
||
|
}
|
||
|
|