From 75825097928e7f40009ffb6513f2d52d4cf4a5d1 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 12:35:45 +0200 Subject: [PATCH] Protect pending avatar uploads while bounding disk cache retention --- tools/chatgui/src/avatars/avatarcache.cpp | 4 ++-- tools/chatgui/src/avatars/avatarcache.h | 3 ++- tools/chatgui/src/avatars/avatarmanager.cpp | 10 ++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/tools/chatgui/src/avatars/avatarcache.cpp b/tools/chatgui/src/avatars/avatarcache.cpp index aebcded3..5b7a1761 100644 --- a/tools/chatgui/src/avatars/avatarcache.cpp +++ b/tools/chatgui/src/avatars/avatarcache.cpp @@ -11,14 +11,14 @@ extern "C" { #include "debug_config.h" } -bool AvatarCache::initialize(const QString& directory) { +bool AvatarCache::initialize(const QString& directory, const QStringList& pinned) { if (!QDir().mkpath(directory)) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "avatar: cannot create cache path=%s", qPrintable(directory)); return false; } const auto entries = QDir(directory).entryInfoList({QStringLiteral("*.part*"), QStringLiteral("*.download*")}, QDir::Files); for (const auto& entry : entries) if (!QFile::remove(entry.absoluteFilePath())) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "avatar: cannot remove interrupted cache file=%s", qPrintable(entry.fileName())); - prune(directory, {}); + prune(directory, pinned); return true; } diff --git a/tools/chatgui/src/avatars/avatarcache.h b/tools/chatgui/src/avatars/avatarcache.h index 4291489d..85eee422 100644 --- a/tools/chatgui/src/avatars/avatarcache.h +++ b/tools/chatgui/src/avatars/avatarcache.h @@ -2,12 +2,13 @@ #pragma once #include #include +#include namespace AvatarCache { constexpr int MaxBytes = 1024 * 1024; constexpr qint64 DiskLimit = 100 * 1024 * 1024; struct Result { QImage image; QString hash, error; }; -bool initialize(const QString& directory); +bool initialize(const QString& directory, const QStringList& pinned = {}); Result load(const QString& path, const QString& hash); Result prepare(const QString& source, const QString& directory); Result publish(const QString& temporary, const QString& directory, const QString& hash); diff --git a/tools/chatgui/src/avatars/avatarmanager.cpp b/tools/chatgui/src/avatars/avatarmanager.cpp index e56efd3e..9d59c1fb 100644 --- a/tools/chatgui/src/avatars/avatarmanager.cpp +++ b/tools/chatgui/src/avatars/avatarmanager.cpp @@ -52,8 +52,9 @@ void AvatarManager::initialize() { const QString file = m_db->getUiState("avatar_default_file"); if (mf_avatar_file_valid(file.toLatin1().constData())) m_defaultHash = file.left(64); const QString directory = m_directory; - m_pool.start(QRunnable::create([this, directory] { - bool ready = AvatarCache::initialize(directory); + const QStringList pinned = m_defaultHash.isEmpty() ? QStringList() : QStringList{m_defaultHash}; + m_pool.start(QRunnable::create([this, directory, pinned] { + bool ready = AvatarCache::initialize(directory, pinned); QMetaObject::invokeMethod(this, [this, ready] { m_ready = ready; if (!ready) { emit statusChanged(text("Cannot open avatar cache")); return; } @@ -207,7 +208,8 @@ void AvatarManager::clearDefault() { void AvatarManager::scheduleOwn(quint64 group) { if (!m_ready || m_preparing || !m_defaultServer || m_defaultHash.isEmpty() || !m_servers.value(group).contains(m_defaultServer) || - !m_images.contains(m_defaultHash)) return; + m_missing.contains(m_defaultHash)) return; + if (!m_images.contains(m_defaultHash)) { loadCache(m_defaultHash); return; } const QString context = key(group, m_db->myNodeId()); if (!m_refs.contains(context) || m_retry.value(context) > QDateTime::currentMSecsSinceEpoch()) return; const Ref old = m_refs.value(context); @@ -297,7 +299,7 @@ void AvatarManager::onTransfer(const uint8_t* data, int length) { if (job.upload) { publish(job); return; } const Job snapshot = job; const QString directory = m_directory; job.stage = 101; QStringList pinned; if (!m_defaultHash.isEmpty()) pinned << m_defaultHash; - for (const auto& ref : m_refs) if (m_wanted.contains(key(ref.group, ref.owner))) pinned << ref.hash; + for (const auto& active : m_jobs) if (active.upload) pinned << active.ref.hash; m_pool.start(QRunnable::create([this, snapshot, directory, pinned] { const auto result = AvatarCache::publish(snapshot.local, directory, snapshot.ref.hash); AvatarCache::prune(directory, pinned); QMetaObject::invokeMethod(this, [this, snapshot, result] {