diff --git a/autotests/index/test_plugin.cpp b/autotests/index/test_plugin.cpp index b0e9fa8..8d705e2 100644 --- a/autotests/index/test_plugin.cpp +++ b/autotests/index/test_plugin.cpp @@ -83,15 +83,14 @@ class PluginTest : public testing::Test stub_ext::StubExt stub; }; -TEST_F(PluginTest, DSMRegister_ReturnsZero) +TEST_F(PluginTest, DSMRegister_ReturnsValidResult) { - // DSMRegister creates DBus objects; should return 0 - // DBus registration may fail in sandbox but the function - // should still return 0 + // Returns 0 on success, -1 if DBus registration fails (e.g. in sandbox). + // Either way the function must not crash. int result = anything_index::registerIndexServices(); - EXPECT_EQ(result, 0); + EXPECT_TRUE(result == 0 || result == -1); - // Clean up via DSMUnRegister + // Clean up via DSMUnRegister (safe to call even if register failed) anything_index::unregisterIndexServices(); } @@ -125,6 +124,6 @@ TEST_F(PluginTest, DSMUnRegister_CalledTwice) TEST_F(PluginTest, DSMRegister_WithNullName) { int result = anything_index::registerIndexServices(); - EXPECT_EQ(result, 0); + EXPECT_TRUE(result == 0 || result == -1); anything_index::unregisterIndexServices(); } diff --git a/src/index/dbus/filenameindexdbus.cpp b/src/index/dbus/filenameindexdbus.cpp index 4a32566..dbc4960 100644 --- a/src/index/dbus/filenameindexdbus.cpp +++ b/src/index/dbus/filenameindexdbus.cpp @@ -11,11 +11,29 @@ #include #include #include +#include + +#include +#include ANYTHING_INDEX_USE_NAMESPACE namespace { +// Sentinel file used to detect abnormal service exit (SIGKILL, crash, OOM). +// Created at startup, removed during normal cleanup(). If it still exists +// when the service starts, the previous run was killed without cleanup. +inline constexpr char kSentinelFileName[] = ".filename_index_running"; + +QString sentinelFilePath(const QString &indexDir) +{ + if (indexDir.isEmpty()) { + return QString(); + } + + return indexDir + QLatin1Char('/') + QLatin1String(kSentinelFileName); +} + QStringList defaultPathsToProcess() { const auto &configuredDirs = DFMSEARCH::Global::defaultIndexedDirectory(); @@ -43,6 +61,32 @@ void FileNameIndexDBusPrivate::initialize() // time because the service process is always started fresh by the daemon. runtime->fsEventController()->setSilentlyRefreshStarted(true); + // Sentinel file: detect abnormal exit from the previous run. + // On normal shutdown cleanup() removes the sentinel. If it still exists + // at startup, the previous process was killed without a chance to clean + // up (SIGKILL, segfault, OOM) — mark state Dirty so handleSilentStart() + // triggers a compensating full Update that covers all missed file changes. + const QString indexDir = runtime->profile().indexDirectory(); + const QString sentinelPath = sentinelFilePath(indexDir); + if (!sentinelPath.isEmpty() && QFile::exists(sentinelPath)) { + qWarning() << "FileNameIndexDBus: Sentinel file found, previous exit was abnormal, marking state as dirty"; + runtime->stateStore().setIndexState(IndexUtility::IndexState::Dirty); + } + + // Create (or recreate) the sentinel file for this run. + // Use O_NOFOLLOW to avoid following symlinks that could truncate an + // arbitrary file if an attacker pre-placed one in the index directory. + if (!indexDir.isEmpty()) { + QDir().mkpath(indexDir); + const QByteArray pathBytes = QFile::encodeName(sentinelPath); + int fd = ::open(pathBytes.constData(), O_CREAT | O_WRONLY | O_NOFOLLOW | O_TRUNC, 0644); + if (fd >= 0) { + ::close(fd); + } else { + qWarning() << "FileNameIndexDBus: Failed to create sentinel file:" << sentinelPath; + } + } + // Check for dirty state at startup and set recovery pending flag // This must be done before any incremental task can complete and clear the Dirty state const IndexUtility::IndexState state = runtime->stateStore().getIndexState(); @@ -209,6 +253,14 @@ void FileNameIndexDBus::cleanup() } StopCurrentTask(); + + // Remove sentinel file on normal exit so the next startup knows + // the previous shutdown was clean. + const QString indexDir = d->runtime->profile().indexDirectory(); + const QString sentinelPath = sentinelFilePath(indexDir); + if (!sentinelPath.isEmpty() && QFile::exists(sentinelPath) && !QFile::remove(sentinelPath)) { + qWarning() << "FileNameIndexDBus: Failed to remove sentinel file:" << sentinelPath; + } } bool FileNameIndexDBus::IsEnabled() diff --git a/src/index/main.cpp b/src/index/main.cpp index bc8476f..75a6f93 100644 --- a/src/index/main.cpp +++ b/src/index/main.cpp @@ -39,7 +39,10 @@ int main(int argc, char *argv[]) QGuiApplication::setApplicationName("deepin-anything-index"); QGuiApplication::setApplicationVersion("1.0.0"); - anything_index::registerIndexServices(); + if (anything_index::registerIndexServices() != 0) { + qWarning() << "deepin-anything-index: failed to register DBus services, exiting"; + return 1; + } // Turn SIGTERM/SIGINT into a graceful shutdown (stop monitoring, stop the // running task, mark unfinished indexes dirty, unregister bus names). diff --git a/src/index/serviceentry.cpp b/src/index/serviceentry.cpp index 80ff284..36b33c4 100644 --- a/src/index/serviceentry.cpp +++ b/src/index/serviceentry.cpp @@ -28,19 +28,22 @@ int registerIndexServices() qDBusRegisterMetaType>(); QDBusConnection bus = QDBusConnection::sessionBus(); - if (!bus.registerService(Defines::kTextIndexDBusService) - && bus.lastError().type() != QDBusError::NoError) { - qWarning() << "deepin-anything-index: failed to register text index DBus service:" << bus.lastError().message(); + if (!bus.registerService(Defines::kTextIndexDBusService)) { + qWarning() << "deepin-anything-index: failed to register text index DBus service, another instance may be running:" << bus.lastError().message(); + return -1; } - if (!bus.registerService(Defines::kOcrIndexDBusService) - && bus.lastError().type() != QDBusError::NoError) { - qWarning() << "deepin-anything-index: failed to register OCR index DBus service:" << bus.lastError().message(); + if (!bus.registerService(Defines::kOcrIndexDBusService)) { + qWarning() << "deepin-anything-index: failed to register OCR index DBus service, another instance may be running:" << bus.lastError().message(); + bus.unregisterService(Defines::kTextIndexDBusService); + return -1; } - if (!bus.registerService(Defines::kFileNameIndexDBusService) - && bus.lastError().type() != QDBusError::NoError) { - qWarning() << "deepin-anything-index: failed to register filename index DBus service:" << bus.lastError().message(); + if (!bus.registerService(Defines::kFileNameIndexDBusService)) { + qWarning() << "deepin-anything-index: failed to register filename index DBus service, another instance may be running:" << bus.lastError().message(); + bus.unregisterService(Defines::kOcrIndexDBusService); + bus.unregisterService(Defines::kTextIndexDBusService); + return -1; } textIndexDBus = new TextIndexDBus(); diff --git a/src/index/serviceentry.h b/src/index/serviceentry.h index 138a215..9bd7e10 100644 --- a/src/index/serviceentry.h +++ b/src/index/serviceentry.h @@ -9,7 +9,7 @@ namespace anything_index { // Registers the three D-Bus service names (org.deepin.Filemanager.TextIndex / // OcrIndex / FileNameIndex), creates the three D-Bus objects and lowers the -// process priority. Returns 0 on success, matching the old DSMRegister contract. +// process priority. Returns 0 on success, -1 if any DBus registration fails. int registerIndexServices(); // Stops monitoring and running tasks, marks unfinished indexes dirty and