mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-25 08:44:38 +00:00
Fix possible crash in mtpFileLoader.
If several cdn file parts hashes are received in getCdnFileHashesDone and some middle one of them cancels the entire loader (for example because of a file write error) a !_finished assert violation happens.
This commit is contained in:
parent
de8de84a33
commit
a27ea2d631
@ -509,6 +509,8 @@ void mtpFileLoader::makeRequest(int offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mtpFileLoader::requestMoreCdnFileHashes() {
|
void mtpFileLoader::requestMoreCdnFileHashes() {
|
||||||
|
Expects(!_finished);
|
||||||
|
|
||||||
if (_cdnHashesRequestId || _cdnUncheckedParts.empty()) {
|
if (_cdnHashesRequestId || _cdnUncheckedParts.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -519,7 +521,13 @@ void mtpFileLoader::requestMoreCdnFileHashes() {
|
|||||||
requestData.dcIndex = 0;
|
requestData.dcIndex = 0;
|
||||||
requestData.offset = offset;
|
requestData.offset = offset;
|
||||||
auto shiftedDcId = MTP::downloadDcId(requestData.dcId, requestData.dcIndex);
|
auto shiftedDcId = MTP::downloadDcId(requestData.dcId, requestData.dcIndex);
|
||||||
auto requestId = _cdnHashesRequestId = MTP::send(MTPupload_GetCdnFileHashes(MTP_bytes(_cdnToken), MTP_int(offset)), rpcDone(&mtpFileLoader::getCdnFileHashesDone), rpcFail(&mtpFileLoader::cdnPartFailed), shiftedDcId);
|
auto requestId = _cdnHashesRequestId = MTP::send(
|
||||||
|
MTPupload_GetCdnFileHashes(
|
||||||
|
MTP_bytes(_cdnToken),
|
||||||
|
MTP_int(offset)),
|
||||||
|
rpcDone(&mtpFileLoader::getCdnFileHashesDone),
|
||||||
|
rpcFail(&mtpFileLoader::cdnPartFailed),
|
||||||
|
shiftedDcId);
|
||||||
placeSentRequest(requestId, requestData);
|
placeSentRequest(requestId, requestData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,9 +604,7 @@ void mtpFileLoader::cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequestId
|
|||||||
cancel(true);
|
cancel(true);
|
||||||
} return;
|
} return;
|
||||||
|
|
||||||
case CheckCdnHashResult::Good: {
|
case CheckCdnHashResult::Good: return partLoaded(offset, bytes);
|
||||||
partLoaded(offset, bytes);
|
|
||||||
} return;
|
|
||||||
}
|
}
|
||||||
Unexpected("Result of checkCdnFileHash()");
|
Unexpected("Result of checkCdnFileHash()");
|
||||||
}
|
}
|
||||||
@ -627,12 +633,12 @@ void mtpFileLoader::getCdnFileHashesDone(const MTPVector<MTPCdnFileHash> &result
|
|||||||
|
|
||||||
_cdnHashesRequestId = 0;
|
_cdnHashesRequestId = 0;
|
||||||
|
|
||||||
auto offset = finishSentRequestGetOffset(requestId);
|
const auto offset = finishSentRequestGetOffset(requestId);
|
||||||
addCdnHashes(result.v);
|
addCdnHashes(result.v);
|
||||||
auto someMoreChecked = false;
|
auto someMoreChecked = false;
|
||||||
for (auto i = _cdnUncheckedParts.begin(); i != _cdnUncheckedParts.cend();) {
|
for (auto i = _cdnUncheckedParts.begin(); i != _cdnUncheckedParts.cend();) {
|
||||||
auto uncheckedOffset = i->first;
|
const auto uncheckedOffset = i->first;
|
||||||
auto uncheckedBytes = gsl::as_bytes(gsl::make_span(i->second));
|
const auto uncheckedBytes = gsl::as_bytes(gsl::make_span(i->second));
|
||||||
|
|
||||||
switch (checkCdnFileHash(uncheckedOffset, uncheckedBytes)) {
|
switch (checkCdnFileHash(uncheckedOffset, uncheckedBytes)) {
|
||||||
case CheckCdnHashResult::NoHash: {
|
case CheckCdnHashResult::NoHash: {
|
||||||
@ -647,13 +653,17 @@ void mtpFileLoader::getCdnFileHashesDone(const MTPVector<MTPCdnFileHash> &result
|
|||||||
|
|
||||||
case CheckCdnHashResult::Good: {
|
case CheckCdnHashResult::Good: {
|
||||||
someMoreChecked = true;
|
someMoreChecked = true;
|
||||||
auto goodOffset = uncheckedOffset;
|
const auto goodOffset = uncheckedOffset;
|
||||||
auto goodBytes = std::move(i->second);
|
const auto goodBytes = std::move(i->second);
|
||||||
|
const auto goodBytesSpan = gsl::make_span(goodBytes);
|
||||||
|
const auto weak = QPointer<mtpFileLoader>(this);
|
||||||
i = _cdnUncheckedParts.erase(i);
|
i = _cdnUncheckedParts.erase(i);
|
||||||
auto guarded = QPointer<mtpFileLoader>(this);
|
if (!feedPart(goodOffset, gsl::as_bytes(goodBytesSpan))
|
||||||
partLoaded(goodOffset, gsl::as_bytes(gsl::make_span(goodBytes)));
|
|| !weak) {
|
||||||
if (!guarded) {
|
return;
|
||||||
// Perhaps we were destroyed already?..
|
} else if (_finished) {
|
||||||
|
emit progress(this);
|
||||||
|
loadNext();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -661,12 +671,13 @@ void mtpFileLoader::getCdnFileHashesDone(const MTPVector<MTPCdnFileHash> &result
|
|||||||
default: Unexpected("Result of checkCdnFileHash()");
|
default: Unexpected("Result of checkCdnFileHash()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!someMoreChecked) {
|
if (someMoreChecked) {
|
||||||
LOG(("API Error: Could not find cdnFileHash for offset %1 after getCdnFileHashes request.").arg(offset));
|
emit progress(this);
|
||||||
cancel(true);
|
loadNext();
|
||||||
} else {
|
return requestMoreCdnFileHashes();
|
||||||
requestMoreCdnFileHashes();
|
|
||||||
}
|
}
|
||||||
|
LOG(("API Error: Could not find cdnFileHash for offset %1 after getCdnFileHashes request.").arg(offset));
|
||||||
|
cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtpFileLoader::placeSentRequest(mtpRequestId requestId, const RequestData &requestData) {
|
void mtpFileLoader::placeSentRequest(mtpRequestId requestId, const RequestData &requestData) {
|
||||||
@ -690,7 +701,7 @@ int mtpFileLoader::finishSentRequestGetOffset(mtpRequestId requestId) {
|
|||||||
return requestData.offset;
|
return requestData.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes) {
|
bool mtpFileLoader::feedPart(int offset, base::const_byte_span bytes) {
|
||||||
Expects(!_finished);
|
Expects(!_finished);
|
||||||
|
|
||||||
if (bytes.size()) {
|
if (bytes.size()) {
|
||||||
@ -703,7 +714,8 @@ void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes) {
|
|||||||
}
|
}
|
||||||
_file.seek(offset);
|
_file.seek(offset);
|
||||||
if (_file.write(reinterpret_cast<const char*>(bytes.data()), bytes.size()) != qint64(bytes.size())) {
|
if (_file.write(reinterpret_cast<const char*>(bytes.data()), bytes.size()) != qint64(bytes.size())) {
|
||||||
return cancel(true);
|
cancel(true);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (offset > 100 * 1024 * 1024) {
|
if (offset > 100 * 1024 * 1024) {
|
||||||
@ -737,14 +749,16 @@ void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes) {
|
|||||||
if (!bytes.size() || (bytes.size() % 1024)) { // bad next offset
|
if (!bytes.size() || (bytes.size() % 1024)) { // bad next offset
|
||||||
_lastComplete = true;
|
_lastComplete = true;
|
||||||
}
|
}
|
||||||
if (_sentRequests.empty() && _cdnUncheckedParts.empty() && (_lastComplete || (_size && _nextRequestOffset >= _size))) {
|
if (_sentRequests.empty()
|
||||||
|
&& _cdnUncheckedParts.empty()
|
||||||
|
&& (_lastComplete || (_size && _nextRequestOffset >= _size))) {
|
||||||
if (!_filename.isEmpty() && (_toCache == LoadToCacheAsWell)) {
|
if (!_filename.isEmpty() && (_toCache == LoadToCacheAsWell)) {
|
||||||
if (!_fileIsOpen) _fileIsOpen = _file.open(QIODevice::WriteOnly);
|
|
||||||
if (!_fileIsOpen) {
|
if (!_fileIsOpen) {
|
||||||
return cancel(true);
|
_fileIsOpen = _file.open(QIODevice::WriteOnly);
|
||||||
}
|
}
|
||||||
if (_file.write(_data) != qint64(_data.size())) {
|
if (!_fileIsOpen || _file.write(_data) != qint64(_data.size())) {
|
||||||
return cancel(true);
|
cancel(true);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_finished = true;
|
_finished = true;
|
||||||
@ -778,10 +792,14 @@ void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes) {
|
|||||||
if (_finished) {
|
if (_finished) {
|
||||||
_downloader->taskFinished().notify();
|
_downloader->taskFinished().notify();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
emit progress(this);
|
void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes) {
|
||||||
|
if (feedPart(offset, bytes)) {
|
||||||
loadNext();
|
emit progress(this);
|
||||||
|
loadNext();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mtpFileLoader::partFailed(const RPCError &error) {
|
bool mtpFileLoader::partFailed(const RPCError &error) {
|
||||||
|
@ -238,7 +238,9 @@ private:
|
|||||||
void requestMoreCdnFileHashes();
|
void requestMoreCdnFileHashes();
|
||||||
void getCdnFileHashesDone(const MTPVector<MTPCdnFileHash> &result, mtpRequestId requestId);
|
void getCdnFileHashesDone(const MTPVector<MTPCdnFileHash> &result, mtpRequestId requestId);
|
||||||
|
|
||||||
|
bool feedPart(int offset, base::const_byte_span bytes);
|
||||||
void partLoaded(int offset, base::const_byte_span bytes);
|
void partLoaded(int offset, base::const_byte_span bytes);
|
||||||
|
|
||||||
bool partFailed(const RPCError &error);
|
bool partFailed(const RPCError &error);
|
||||||
bool cdnPartFailed(const RPCError &error, mtpRequestId requestId);
|
bool cdnPartFailed(const RPCError &error, mtpRequestId requestId);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user