2
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation, either version 2 or (at your option)
7
* version 3 of the License.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
#include "HashedBlockStream.h"
20
#include "core/Endian.h"
21
#include "crypto/CryptoHash.h"
23
const QSysInfo::Endian HashedBlockStream::ByteOrder = QSysInfo::LittleEndian;
25
HashedBlockStream::HashedBlockStream(QIODevice* baseDevice)
26
: LayeredStream(baseDevice)
27
, m_blockSize(1024 * 1024)
32
HashedBlockStream::HashedBlockStream(QIODevice* baseDevice, qint32 blockSize)
33
: LayeredStream(baseDevice)
34
, m_blockSize(blockSize)
39
HashedBlockStream::~HashedBlockStream()
44
void HashedBlockStream::init()
53
bool HashedBlockStream::reset()
55
// Write final block(s) only if device is writable and we haven't
56
// already written a final block.
57
if (isWritable() && (!m_buffer.isEmpty() || m_blockIndex != 0)) {
58
if (!m_buffer.isEmpty()) {
59
if (!writeHashedBlock()) {
64
// write empty final block
65
if (!writeHashedBlock()) {
75
void HashedBlockStream::close()
77
// Write final block(s) only if device is writable and we haven't
78
// already written a final block.
79
if (isWritable() && (!m_buffer.isEmpty() || m_blockIndex != 0)) {
80
if (!m_buffer.isEmpty()) {
84
// write empty final block
88
LayeredStream::close();
91
qint64 HashedBlockStream::readData(char* data, qint64 maxSize)
99
qint64 bytesRemaining = maxSize;
102
while (bytesRemaining > 0) {
103
if (m_bufferPos == m_buffer.size()) {
104
if (!readHashedBlock()) {
108
return maxSize - bytesRemaining;
113
int bytesToCopy = qMin(bytesRemaining, static_cast<qint64>(m_buffer.size() - m_bufferPos));
115
memcpy(data + offset, m_buffer.constData() + m_bufferPos, bytesToCopy);
117
offset += bytesToCopy;
118
m_bufferPos += bytesToCopy;
119
bytesRemaining -= bytesToCopy;
125
bool HashedBlockStream::readHashedBlock()
129
auto index = Endian::readSizedInt<quint32>(m_baseDevice, ByteOrder, &ok);
130
if (!ok || index != m_blockIndex) {
132
setErrorString("Invalid block index.");
136
QByteArray hash = m_baseDevice->read(32);
137
if (hash.size() != 32) {
139
setErrorString("Invalid hash size.");
143
m_blockSize = Endian::readSizedInt<qint32>(m_baseDevice, ByteOrder, &ok);
144
if (!ok || m_blockSize < 0) {
146
setErrorString("Invalid block size.");
150
if (m_blockSize == 0) {
151
if (hash.count('\0') != 32) {
153
setErrorString("Invalid hash of final block.");
161
m_buffer = m_baseDevice->read(m_blockSize);
162
if (m_buffer.size() != m_blockSize) {
164
setErrorString("Block too short.");
168
if (hash != CryptoHash::hash(m_buffer, CryptoHash::Sha256)) {
170
setErrorString("Mismatch between hash and data.");
180
qint64 HashedBlockStream::writeData(const char* data, qint64 maxSize)
182
Q_ASSERT(maxSize >= 0);
188
qint64 bytesRemaining = maxSize;
191
while (bytesRemaining > 0) {
192
int bytesToCopy = qMin(bytesRemaining, static_cast<qint64>(m_blockSize - m_buffer.size()));
194
m_buffer.append(data + offset, bytesToCopy);
196
offset += bytesToCopy;
197
bytesRemaining -= bytesToCopy;
199
if (m_buffer.size() == m_blockSize) {
200
if (!writeHashedBlock()) {
204
return maxSize - bytesRemaining;
213
bool HashedBlockStream::writeHashedBlock()
215
if (!Endian::writeSizedInt<qint32>(m_blockIndex, m_baseDevice, ByteOrder)) {
217
setErrorString(m_baseDevice->errorString());
223
if (!m_buffer.isEmpty()) {
224
hash = CryptoHash::hash(m_buffer, CryptoHash::Sha256);
229
if (m_baseDevice->write(hash) != hash.size()) {
231
setErrorString(m_baseDevice->errorString());
235
if (!Endian::writeSizedInt<qint32>(m_buffer.size(), m_baseDevice, ByteOrder)) {
237
setErrorString(m_baseDevice->errorString());
241
if (!m_buffer.isEmpty()) {
242
if (m_baseDevice->write(m_buffer) != m_buffer.size()) {
244
setErrorString(m_baseDevice->errorString());
254
bool HashedBlockStream::atEnd() const