Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable-3.14] create a permanent log of delete actions #7381

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions src/csync/csync.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*

Check notice on line 1 in src/csync/csync.h

View workflow job for this annotation

GitHub Actions / build

Run clang-format on src/csync/csync.h

File src/csync/csync.h does not conform to Custom style guidelines. (lines 35, 45)
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2008-2013 by Andreas Schneider <[email protected]>
Expand Down Expand Up @@ -32,22 +32,24 @@
#ifndef _CSYNC_H
#define _CSYNC_H

#include "std/c_private.h"
#include "ocsynclib.h"

#include <sys/stat.h>
#include <QByteArray>

Check failure on line 35 in src/csync/csync.h

View workflow job for this annotation

GitHub Actions / build

src/csync/csync.h:35:10 [clang-diagnostic-error]

'QByteArray' file not found
#include <QVariant>
#include <QLoggingCategory>

#include <cstdint>
#include <sys/stat.h>
#include <sys/types.h>
#include <config_csync.h>
#include <functional>
#include <memory>
#include <QByteArray>
#include <QVariant>

#include "ocsynclib.h"
#include "config_csync.h"
#include "std/c_private.h"
#include "common/remotepermissions.h"

namespace OCC {
Q_DECLARE_LOGGING_CATEGORY(lcPermanentLog)

class SyncJournalFileRecord;

namespace EncryptionStatusEnums {
Expand Down
3 changes: 2 additions & 1 deletion src/gui/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,8 @@ void Application::setupLogging()
logger->setLogDebug(true);
#endif

logger->enterNextLogFile();
logger->enterNextLogFile(QStringLiteral("nextcloud.log"), OCC::Logger::LogType::Log);
logger->enterNextLogFile(QStringLiteral("permanent_delete.log"), OCC::Logger::LogType::DeleteLog);

qCInfo(lcApplication) << "##################" << _theme->appName()
<< "locale:" << QLocale::system().name()
Expand Down
2 changes: 1 addition & 1 deletion src/gui/logbrowser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ void LogBrowser::togglePermanentLogging(bool enabled)
if (enabled) {
if (!logger->isLoggingToFile()) {
logger->setupTemporaryFolderLogDir();
logger->enterNextLogFile();
logger->enterNextLogFile(QStringLiteral("nextcloud.log"), OCC::Logger::LogType::Log);
}
} else {
logger->disableTemporaryFolderLogDir();
Expand Down
2 changes: 1 addition & 1 deletion src/gui/owncloudgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ void ownCloudGui::slotSyncStateChange(Folder *folder)
|| result.status() == SyncResult::Problem
|| result.status() == SyncResult::SyncAbortRequested
|| result.status() == SyncResult::Error) {
Logger::instance()->enterNextLogFile();
Logger::instance()->enterNextLogFile(QStringLiteral("nextcloud.log"), OCC::Logger::LogType::Log);
}
}

Expand Down
44 changes: 21 additions & 23 deletions src/libsync/discovery.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*

Check notice on line 1 in src/libsync/discovery.cpp

View workflow job for this annotation

GitHub Actions / build

Run clang-format on src/libsync/discovery.cpp

File src/libsync/discovery.cpp does not conform to Custom style guidelines. (lines 534, 536, 537, 538, 539, 540, 542, 543, 544, 546, 547)
* Copyright (C) by Olivier Goffart <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -528,29 +528,26 @@
const auto localFileIsLocked = dbEntry._lockstate._locked ? "locked" : "not locked";
const auto serverFileLockType = serverEntry.isValid() ? QString::number(static_cast<int>(serverEntry.lockOwnerType)) : QStringLiteral("");
const auto localFileLockType = dbEntry._lockstate._locked ? QString::number(static_cast<int>(dbEntry._lockstate._lockOwnerType)) : QStringLiteral("");
qCInfo(lcDisco).nospace() << "Processing " << path._original
<< " | (db/local/remote)"
<< " | valid: " << dbEntry.isValid() << "/" << hasLocal << "/" << hasServer
<< " | mtime: " << dbEntry._modtime << "/" << localEntry.modtime << "/" << serverEntry.modtime
<< " | size: " << dbEntry._fileSize << "/" << localEntry.size << "/" << serverEntry.size
<< " | etag: " << dbEntry._etag << "//" << serverEntry.etag
<< " | checksum: " << dbEntry._checksumHeader << "//" << serverEntry.checksumHeader
<< " | perm: " << dbEntry._remotePerm << "//" << serverEntry.remotePerm
<< " | fileid: " << dbEntry._fileId << "//" << serverEntry.fileId
<< " | inode: " << dbEntry._inode << "/" << localEntry.inode << "/"
<< " | type: " << dbEntry._type << "/" << localEntry.type << "/" << (serverEntry.isDirectory ? ItemTypeDirectory : ItemTypeFile)
<< " | e2ee: " << dbEntry.isE2eEncrypted() << "/" << serverEntry.isE2eEncrypted()
<< " | e2eeMangledName: " << dbEntry.e2eMangledName() << "/" << serverEntry.e2eMangledName
<< " | file lock: " << localFileIsLocked << "//" << serverFileIsLocked
<< " | file lock type: " << localFileLockType << "//" << serverFileLockType
<< " | metadata missing: /" << localEntry.isMetadataMissing << '/';

if (localEntry.isValid()
&& !serverEntry.isValid()
&& !dbEntry.isValid()
&& localEntry.modtime < _lastSyncTimestamp) {
qCWarning(lcDisco) << "File" << path._original << "was modified before the last sync run and is not in the sync journal and server";
}

QString processingLog;
QDebug deleteLogger{&processingLog};
deleteLogger.nospace() << "Processing " << path._original
<< " | (db/local/remote)"
<< " | valid: " << dbEntry.isValid() << "/" << hasLocal << "/" << hasServer
<< " | mtime: " << dbEntry._modtime << "/" << localEntry.modtime << "/" << serverEntry.modtime
<< " | size: " << dbEntry._fileSize << "/" << localEntry.size << "/" << serverEntry.size
<< " | etag: " << dbEntry._etag << "//" << serverEntry.etag
<< " | checksum: " << dbEntry._checksumHeader << "//" << serverEntry.checksumHeader
<< " | perm: " << dbEntry._remotePerm << "//" << serverEntry.remotePerm
<< " | fileid: " << dbEntry._fileId << "//" << serverEntry.fileId
<< " | type: " << dbEntry._type << "/" << localEntry.type << "/" << (serverEntry.isDirectory ? ItemTypeDirectory : ItemTypeFile)
<< " | e2ee: " << dbEntry.isE2eEncrypted() << "/" << serverEntry.isE2eEncrypted()
<< " | e2eeMangledName: " << dbEntry.e2eMangledName() << "/" << serverEntry.e2eMangledName
<< " | file lock: " << localFileIsLocked << "//" << serverFileIsLocked
<< " | file lock type: " << localFileLockType << "//" << serverFileLockType
<< " | metadata missing: /" << localEntry.isMetadataMissing << '/';

qCInfo(lcDisco).nospace() << processingLog;

if (_discoveryData->isRenamed(path._original)) {
qCDebug(lcDisco) << "Ignoring renamed";
Expand All @@ -562,6 +559,7 @@
item->_originalFile = path._original;
item->_previousSize = dbEntry._fileSize;
item->_previousModtime = dbEntry._modtime;
item->_discoveryResult = std::move(processingLog);

if (dbEntry._modtime == localEntry.modtime && dbEntry._type == ItemTypeVirtualFile && localEntry.type == ItemTypeFile) {
item->_type = ItemTypeFile;
Expand Down
92 changes: 76 additions & 16 deletions src/libsync/logger.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*

Check notice on line 1 in src/libsync/logger.cpp

View workflow job for this annotation

GitHub Actions / build

Run clang-format on src/libsync/logger.cpp

File src/libsync/logger.cpp does not conform to Custom style guidelines. (lines 310, 319)
* Copyright (C) by Klaas Freitag <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -68,6 +68,8 @@

namespace OCC {

Q_LOGGING_CATEGORY(lcPermanentLog, "nextcloud.log.permanent")

Check warning on line 71 in src/libsync/logger.cpp

View workflow job for this annotation

GitHub Actions / build

src/libsync/logger.cpp:71:1 [cppcoreguidelines-avoid-non-const-global-variables]

variable 'Q_LOGGING_CATEGORY' is non-const and globally accessible, consider making it const

Logger *Logger::instance()
{
static Logger log;
Expand Down Expand Up @@ -134,7 +136,7 @@
_logstream->flush();
}
closeNoLock();
enterNextLogFileNoLock();
enterNextLogFileNoLock(QStringLiteral("nextcloud.log"), LogType::Log);
}
++linesCounter;

Expand All @@ -146,6 +148,13 @@
if (_doFileFlush)
_logstream->flush();
}
if (_permanentDeleteLogStream && strcmp(ctx.category, lcPermanentLog().categoryName()) == 0) {
(*_permanentDeleteLogStream) << msg << "\n";
_permanentDeleteLogStream->flush();
if (_permanentDeleteLogFile.size() > 10LL * 1024LL) {
enterNextLogFileNoLock(QStringLiteral("permanent_delete.log"), LogType::DeleteLog);
}
}
if (type == QtFatalMsg) {
closeNoLock();
#if defined(Q_OS_WIN)
Expand Down Expand Up @@ -180,6 +189,12 @@
setLogFileNoLock(name);
}

void Logger::setPermanentDeleteLogFile(const QString &name)
{
QMutexLocker locker(&_mutex);
setPermanentDeleteLogFileNoLock(name);
}

void Logger::setLogExpire(int expire)
{
_logExpire = expire;
Expand Down Expand Up @@ -232,7 +247,7 @@
if (!_temporaryFolderLogDir)
return;

enterNextLogFile();
enterNextLogFile("nextcloud.log", LogType::Log);
setLogDir(QString());
setLogDebug(false);
setLogFile(QString());
Expand Down Expand Up @@ -262,7 +277,7 @@
}
}

void Logger::enterNextLogFileNoLock()
void Logger::enterNextLogFileNoLock(const QString &baseFileName, LogType type)
{
if (!_logDirectory.isEmpty()) {

Expand All @@ -274,29 +289,44 @@
// Tentative new log name, will be adjusted if one like this already exists
const auto now = QDateTime::currentDateTime();
const auto cLocale = QLocale::c(); // Some system locales generate strings that are incompatible with filesystem
QString newLogName = cLocale.toString(now, QStringLiteral("yyyyMMdd_HHmm")) + QStringLiteral("_nextcloud.log");
QString newLogName = cLocale.toString(now, QStringLiteral("yyyyMMdd_HHmm")) + QStringLiteral("_%1").arg(baseFileName);

// Expire old log files and deal with conflicts
QStringList files = dir.entryList(QStringList("*owncloud.log.*"), QDir::Files, QDir::Name) +
dir.entryList(QStringList("*nextcloud.log.*"), QDir::Files, QDir::Name);
static const QRegularExpression rx(QRegularExpression::anchoredPattern(R"(.*(next|own)cloud\.log\.(\d+).*)"));
int maxNumber = -1;
foreach (const QString &s, files) {
const auto files = dir.entryList({QStringLiteral("*owncloud.log.*"), QStringLiteral("*%1.*").arg(baseFileName)}, QDir::Files, QDir::Name);
for (const auto &s : files) {
if (_logExpire > 0) {
QFileInfo fileInfo(dir.absoluteFilePath(s));
if (fileInfo.lastModified().addSecs(60 * 60 * _logExpire) < now) {
dir.remove(s);
}
}
const auto rxMatch = rx.match(s);
if (s.startsWith(newLogName) && rxMatch.hasMatch()) {
maxNumber = qMax(maxNumber, rxMatch.captured(2).toInt());
}

const auto regexpText = QString{"%1\\.(\\d+).*"}.arg(QRegularExpression::escape(newLogName));
const auto anchoredPatternRegexpText = QRegularExpression::anchoredPattern(regexpText);
const QRegularExpression rx(regexpText);
int maxNumber = -1;
const auto collidingFileNames = dir.entryList({QStringLiteral("%1.*").arg(newLogName)}, QDir::Files, QDir::Name);
for(const auto &fileName : collidingFileNames) {
const auto rxMatch = rx.match(fileName);
if (rxMatch.hasMatch()) {
maxNumber = qMax(maxNumber, rxMatch.captured(1).toInt());
}
}
newLogName.append("." + QString::number(maxNumber + 1));

auto previousLog = _logFile.fileName();
setLogFileNoLock(dir.filePath(newLogName));
auto previousLog = QString{};
switch (type)
{
case OCC::Logger::LogType::Log:
previousLog = _logFile.fileName();
setLogFileNoLock(dir.filePath(newLogName));
break;
case OCC::Logger::LogType::DeleteLog:
previousLog = _permanentDeleteLogFile.fileName();
setPermanentDeleteLogFileNoLock(dir.filePath(newLogName));
break;
}

// Compress the previous log file. On a restart this can be the most recent
// log file.
Expand Down Expand Up @@ -344,10 +374,40 @@
_logstream.reset(new QTextStream(&_logFile));
}

void Logger::enterNextLogFile()
void Logger::setPermanentDeleteLogFileNoLock(const QString &name)
{
if (_permanentDeleteLogStream) {
_permanentDeleteLogStream.reset(nullptr);
_permanentDeleteLogFile.close();
}

if (name.isEmpty()) {
return;
}

bool openSucceeded = false;
if (name == QLatin1String("-")) {
openSucceeded = _permanentDeleteLogFile.open(stdout, QIODevice::WriteOnly);
} else {
_permanentDeleteLogFile.setFileName(name);
openSucceeded = _permanentDeleteLogFile.open(QIODevice::WriteOnly);
}

if (!openSucceeded) {
postGuiMessage(tr("Error"),
QString(tr("<nobr>File \"%1\"<br/>cannot be opened for writing.<br/><br/>"
"The log output <b>cannot</b> be saved!</nobr>"))
.arg(name));
return;
}

_permanentDeleteLogStream.reset(new QTextStream(&_permanentDeleteLogFile));
}

void Logger::enterNextLogFile(const QString &baseFileName, LogType type)
{
QMutexLocker locker(&_mutex);
enterNextLogFileNoLock();
enterNextLogFileNoLock(baseFileName, type);
}

} // namespace OCC
19 changes: 15 additions & 4 deletions src/libsync/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
#ifndef LOGGER_H
#define LOGGER_H

#include <QObject>

Check failure on line 18 in src/libsync/logger.h

View workflow job for this annotation

GitHub Actions / build

src/libsync/logger.h:18:10 [clang-diagnostic-error]

'QObject' file not found
#include <QList>
#include <QDateTime>
#include <QFile>
#include <QTextStream>
#include <qmutex.h>
#include <QRecursiveMutex>

#include "common/utility.h"
#include "owncloudlib.h"
Expand All @@ -35,6 +35,12 @@
{
Q_OBJECT
public:
enum class LogType {
Log,
DeleteLog,
};
Q_ENUM(LogType)

bool isLoggingToFile() const;

void doLog(QtMsgType type, const QMessageLogContext &ctx, const QString &message);
Expand All @@ -47,6 +53,8 @@
QString logFile() const;
void setLogFile(const QString &name);

void setPermanentDeleteLogFile(const QString &name);

void setLogExpire(int expire);

QString logDir() const;
Expand Down Expand Up @@ -88,28 +96,31 @@
void guiMessage(const QString &, const QString &);

public slots:
void enterNextLogFile();
void enterNextLogFile(const QString &baseFileName, OCC::Logger::LogType type);

private:
Logger(QObject *parent = nullptr);
~Logger() override;

void closeNoLock();
void dumpCrashLog();
void enterNextLogFileNoLock();
void enterNextLogFileNoLock(const QString &baseFileName, LogType type);
void setLogFileNoLock(const QString &name);
void setPermanentDeleteLogFileNoLock(const QString &name);

QFile _logFile;
bool _doFileFlush = false;
int _logExpire = 0;
bool _logDebug = false;
QScopedPointer<QTextStream> _logstream;
mutable QMutex _mutex;
mutable QRecursiveMutex _mutex;
QString _logDirectory;
bool _temporaryFolderLogDir = false;
QSet<QString> _logRules;
QVector<QString> _crashLog;
int _crashLogIndex = 0;
QFile _permanentDeleteLogFile;
QScopedPointer<QTextStream> _permanentDeleteLogStream;
};

} // namespace OCC
Expand Down
1 change: 1 addition & 0 deletions src/libsync/propagateremotedelete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Q_LOGGING_CATEGORY(lcPropagateRemoteDelete, "nextcloud.sync.propagator.remotedel
void PropagateRemoteDelete::start()
{
qCInfo(lcPropagateRemoteDelete) << "Start propagate remote delete job for" << _item->_file;
qCInfo(lcPermanentLog) << "delete" << _item->_file << _item->_discoveryResult;

if (propagator()->_abortRequested)
return;
Expand Down
1 change: 1 addition & 0 deletions src/libsync/propagatorjobs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ bool PropagateLocalRemove::removeRecursively(const QString &path)
void PropagateLocalRemove::start()
{
qCInfo(lcPropagateLocalRemove) << "Start propagate local remove job";
qCInfo(lcPermanentLog) << "delete" << _item->_file << _item->_discoveryResult;

_moveToTrash = propagator()->syncOptions()._moveFilesToTrash;

Expand Down
2 changes: 2 additions & 0 deletions src/libsync/syncfileitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#ifndef SYNCFILEITEM_H
#define SYNCFILEITEM_H

#include <QVector>

Check failure on line 18 in src/libsync/syncfileitem.h

View workflow job for this annotation

GitHub Actions / build

src/libsync/syncfileitem.h:18:10 [clang-diagnostic-error]

'QVector' file not found
#include <QString>
#include <QDateTime>
#include <QMetaType>
Expand Down Expand Up @@ -339,6 +339,8 @@

bool _isAnyInvalidCharChild = false;
bool _isAnyCaseClashChild = false;

QString _discoveryResult;
};

inline bool operator<(const SyncFileItemPtr &item1, const SyncFileItemPtr &item2)
Expand Down
3 changes: 3 additions & 0 deletions src/libsync/syncfilestatustracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ void SyncFileStatusTracker::slotAboutToPropagate(SyncFileItemVector &items)
}

SharedFlag sharedFlag = item->_remotePerm.hasPermission(RemotePermissions::IsShared) ? Shared : NotShared;
if (item->_instruction != CSyncEnums::CSYNC_INSTRUCTION_REMOVE) {
item->_discoveryResult.clear();
}
if (item->_instruction != CSYNC_INSTRUCTION_NONE
&& item->_instruction != CSYNC_INSTRUCTION_UPDATE_METADATA
&& item->_instruction != CSYNC_INSTRUCTION_IGNORE
Expand Down
Loading