Log MailFolder save failures instead of failing silently

std::ofstream does not throw on a failed open, so a permissions or
disk-full problem writing .imapindex/.uidnext/.uidvalidity previously
just... did not happen, with no trace anywhere. Confirmed live: the
barant accounts inbox and Trash index files were root-owned mode 644
while the mail service runs as a non-root user, so every flag/counter
persist attempt (marking a message \Seen, for instance) silently
failed for weeks - the in-memory session reflected the change for that
one connection, but nothing ever reached disk, so any fresh IMAP
session saw the old state again. Ownership fixed directly on the
affected files; this logs the failure mode so a recurrence is
visible.
This commit is contained in:
Brad Arant committed 2026-09-07 02:29:16 +00:00
1 parent e8b2a79907
commit 5f3e6ed2e6
1 file changed
+16
+16
View File
@@ -1,4 +1,5 @@
#include "MailFolder.h" #include "MailFolder.h"
#include "Log.h"
#include <algorithm> #include <algorithm>
#include <ctime> #include <ctime>
#include <fstream> #include <fstream>
@@ -47,6 +48,15 @@ namespace mail {
void writeCounterFile(const std::string &path, uint32_t value) { void writeCounterFile(const std::string &path, uint32_t value) {
std::ofstream out(path, std::ios::trunc); std::ofstream out(path, std::ios::trunc);
out << value; out << value;
// ofstream doesn't throw on a failed open (e.g. a permissions
// problem, a full/read-only filesystem) - confirmed live: two
// pre-existing mailbox index files were root-owned mode 644
// while the mail service runs as a non-root user, so every
// counter/flag persist attempt failed here completely silently
// for weeks. Logging it is the only way this becomes visible
// instead of just quietly not happening.
if (!out)
coreutils::Log(coreutils::LOG_EXCEPT) << "MailFolder: failed to write " << path << " - check ownership/permissions.";
} }
} }
@@ -160,9 +170,15 @@ namespace mail {
writeCounterFile(path + "/.uidvalidity", mUidValidity); writeCounterFile(path + "/.uidvalidity", mUidValidity);
writeCounterFile(path + "/.uidnext", mUidNext); writeCounterFile(path + "/.uidnext", mUidNext);
std::ofstream out(path + "/.imapindex", std::ios::trunc); std::ofstream out(path + "/.imapindex", std::ios::trunc);
if (!out) {
coreutils::Log(coreutils::LOG_EXCEPT) << "MailFolder: failed to open " << path << "/.imapindex for writing - flag/message changes will not persist. Check ownership/permissions.";
return;
}
for (const Message &message : mMessages) for (const Message &message : mMessages)
out << message.fileName << " " << message.uid << " " << (long long)message.internalDate out << message.fileName << " " << message.uid << " " << (long long)message.internalDate
<< " " << joinFlags(message.flags) << "\n"; << " " << joinFlags(message.flags) << "\n";
if (!out)
coreutils::Log(coreutils::LOG_EXCEPT) << "MailFolder: write to " << path << "/.imapindex failed partway through - index may be incomplete.";
} }
uint32_t MailFolder::seq(uint32_t uid) const { uint32_t MailFolder::seq(uint32_t uid) const {