Public Access
Debug Console put: verify the card's copy, never zero-fill after a failed write
Found by M3's shared-bus test: when the card refused a write, the retry closed the file (losing up to 3 KB of earlier chunks still in the write buffer), then truncate() extended it back with zeros. The checksum only covered the received bytes, so `put` reported success with 3 KB of zeros on the card. Now a retry gives up if the card lost data, and the finished file is read back and must hash the same before it's renamed. The card refuses a write about once in five 1.7 MB uploads, with the radio asleep as often as listening. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "file_receiver.h"
|
||||
#include "sha256.h"
|
||||
#include "platform/console.h"
|
||||
#include "version.h"
|
||||
|
||||
@@ -233,11 +234,20 @@ void DebugConsole::put(NetworkClient& client, const std::string& args) {
|
||||
const auto& c = r.chunk();
|
||||
bool ok = f.write(c.data(), c.size()) == c.size();
|
||||
// A card can fail one write and take the next. FATFS keeps a failed file in error,
|
||||
// so: close, cut back to the last good byte, reopen, try again.
|
||||
// so: close, cut back to the last good byte, reopen, try again. Earlier chunks may
|
||||
// have been lost with the write buffer (M3: 3 KB came back as zeros): never extend
|
||||
// the file to cover them; give up instead, as the sender can't resend them.
|
||||
for (int retry = 1; !ok && retry <= 3; retry++) {
|
||||
console.printf("put: write failed at %u, retry %d\n", (unsigned)r.received(), retry);
|
||||
f.close();
|
||||
delay(50 * retry);
|
||||
f = SD.open(part.c_str(), FILE_READ);
|
||||
size_t onCard = f ? f.size() : 0;
|
||||
if (f) f.close();
|
||||
if (onCard < r.received()) {
|
||||
console.printf("put: the card lost %u B written before\n", (unsigned)(r.received() - onCard));
|
||||
break;
|
||||
}
|
||||
truncate(("/sd" + part).c_str(), r.received());
|
||||
f = SD.open(part.c_str(), FILE_APPEND);
|
||||
ok = f && f.size() == r.received() && f.write(c.data(), c.size()) == c.size();
|
||||
@@ -254,6 +264,15 @@ void DebugConsole::put(NetworkClient& client, const std::string& args) {
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
if (r.state() == S::Finishing) { // read it back: the received checksum doesn't cover the card
|
||||
Sha256 sha;
|
||||
f = SD.open(part.c_str(), FILE_READ);
|
||||
for (int n; f && (n = f.read(buf, sizeof buf)) > 0;) sha.update(buf, n);
|
||||
if (f) f.close();
|
||||
uint8_t digest[32];
|
||||
sha.finish(digest);
|
||||
r.cardChecked(digest);
|
||||
}
|
||||
if (r.state() == S::Finishing) {
|
||||
if (SD.exists(r.path().c_str())) SD.remove(r.path().c_str());
|
||||
r.finished(SD.rename(part.c_str(), r.path().c_str()));
|
||||
|
||||
Reference in New Issue
Block a user