Skip actively locked autosave files

Check the QLockFile in staleFiles() before returning a no-KF5 recovery candidate, matching the KAutoSaveFile contract that actively owned autosave files are not stale.

Extend the no-KF5 Catch test so a child process keeps the autosave lock alive while allStaleFiles() runs, then verify recovery after the child is killed.

Assisted-by: pi coding agent / Mika (OpenAI GPT-5.5)
This commit is contained in:
Gerhard Schwanzer
2026-07-05 17:39:38 +02:00
parent 7a97e873d9
commit dcec0bf7ff
2 changed files with 51 additions and 5 deletions
+21 -2
View File
@@ -48,6 +48,11 @@ QString metadataFileName(const QString &autosave_file_name)
return autosave_file_name + QStringLiteral(".path"); return autosave_file_name + QStringLiteral(".path");
} }
QString lockFileName(const QString &autosave_file_name)
{
return autosave_file_name + QStringLiteral(".lock");
}
QUrl normalizeManagedFile(const QUrl &url) QUrl normalizeManagedFile(const QUrl &url)
{ {
if (url.isEmpty()) { if (url.isEmpty()) {
@@ -165,6 +170,18 @@ QStringList findAllStaleFiles(const QString &application_name)
return files; return files;
} }
bool autosaveFileIsRecoverable(const QString &autosave_file_name)
{
QLockFile lock(lockFileName(autosave_file_name));
lock.setStaleLockTime(60 * 1000);
if (!lock.tryLock()) {
return false;
}
lock.unlock();
return true;
}
} // namespace } // namespace
KAutoSaveFile::KAutoSaveFile(const QUrl &filename, QObject *parent) : KAutoSaveFile::KAutoSaveFile(const QUrl &filename, QObject *parent) :
@@ -239,8 +256,7 @@ bool KAutoSaveFile::open(OpenMode openmode)
} }
if (!m_lock) { if (!m_lock) {
m_lock = std::make_unique<QLockFile>( m_lock = std::make_unique<QLockFile>(lockFileName(fileName()));
fileName() + QStringLiteral(".lock"));
m_lock->setStaleLockTime(60 * 1000); m_lock->setStaleLockTime(60 * 1000);
} }
@@ -268,6 +284,9 @@ QList<KAutoSaveFile *> KAutoSaveFile::staleFiles(
&& managed_file != managed_file_filter) { && managed_file != managed_file_filter) {
continue; continue;
} }
if (!autosaveFileIsRecoverable(file)) {
continue;
}
auto *stale_file = new KAutoSaveFile(managed_file); auto *stale_file = new KAutoSaveFile(managed_file);
stale_file->setFileName(file); stale_file->setFileName(file);
+30 -3
View File
@@ -11,6 +11,7 @@
#include <memory> #include <memory>
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
#include <signal.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
@@ -34,11 +35,16 @@ TEST_CASE("Qt-only KAutoSaveFile recovers stale files", "[nokde][autosave]")
REQUIRE(managed_file.write("<project/>\n") > 0); REQUIRE(managed_file.write("<project/>\n") > 0);
managed_file.close(); managed_file.close();
int ready_pipe[2] = {-1, -1};
REQUIRE(pipe(ready_pipe) == 0);
const QByteArray payload("<project><diagram /></project>\n"); const QByteArray payload("<project><diagram /></project>\n");
const auto child_pid = fork(); const auto child_pid = fork();
REQUIRE(child_pid >= 0); REQUIRE(child_pid >= 0);
if (child_pid == 0) { if (child_pid == 0) {
close(ready_pipe[0]);
KAutoSaveFile backup(QUrl::fromLocalFile(managed_path)); KAutoSaveFile backup(QUrl::fromLocalFile(managed_path));
if (!backup.open(QIODevice::WriteOnly if (!backup.open(QIODevice::WriteOnly
| QIODevice::Truncate | QIODevice::Truncate
@@ -52,13 +58,34 @@ TEST_CASE("Qt-only KAutoSaveFile recovers stale files", "[nokde][autosave]")
_exit(4); _exit(4);
} }
_exit(0); const char ready = '1';
if (write(ready_pipe[1], &ready, 1) != 1) {
_exit(5);
}
close(ready_pipe[1]);
for (;;) {
pause();
}
} }
close(ready_pipe[1]);
char ready = 0;
REQUIRE(read(ready_pipe[0], &ready, 1) == 1);
close(ready_pipe[0]);
REQUIRE(ready == '1');
auto active_files = KAutoSaveFile::allStaleFiles();
CHECK(active_files.isEmpty());
for (auto *file : active_files) {
delete file;
}
REQUIRE(kill(child_pid, SIGKILL) == 0);
int status = 0; int status = 0;
REQUIRE(waitpid(child_pid, &status, 0) == child_pid); REQUIRE(waitpid(child_pid, &status, 0) == child_pid);
REQUIRE(WIFEXITED(status)); REQUIRE(WIFSIGNALED(status));
REQUIRE(WEXITSTATUS(status) == 0); REQUIRE(WTERMSIG(status) == SIGKILL);
auto stale_files = KAutoSaveFile::allStaleFiles(); auto stale_files = KAutoSaveFile::allStaleFiles();
REQUIRE(stale_files.size() == 1); REQUIRE(stale_files.size() == 1);