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.8] properly preserve the format of e2ee metadata during DB operations #5599

Merged
merged 3 commits into from
Apr 13, 2023
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
4 changes: 2 additions & 2 deletions src/common/syncjournaldb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ static void fillFileRecordFromGetQuery(SyncJournalFileRecord &rec, SqlQuery &que
rec._serverHasIgnoredFiles = (query.intValue(8) > 0);
rec._checksumHeader = query.baValue(9);
rec._e2eMangledName = query.baValue(10);
rec._isE2eEncrypted = static_cast<SyncJournalFileRecord::EncryptionStatus>(query.intValue(11));
rec._e2eEncryptionStatus = static_cast<SyncJournalFileRecord::EncryptionStatus>(query.intValue(11));
rec._lockstate._locked = query.intValue(12) > 0;
rec._lockstate._lockOwnerDisplayName = query.stringValue(13);
rec._lockstate._lockOwnerId = query.stringValue(14);
Expand Down Expand Up @@ -968,7 +968,7 @@ Result<void, QString> SyncJournalDb::setFileRecord(const SyncJournalFileRecord &
query->bindValue(15, checksum);
query->bindValue(16, contentChecksumTypeId);
query->bindValue(17, record._e2eMangledName);
query->bindValue(18, static_cast<int>(record._isE2eEncrypted));
query->bindValue(18, static_cast<int>(record._e2eEncryptionStatus));
query->bindValue(19, record._lockstate._locked ? 1 : 0);
query->bindValue(20, record._lockstate._lockOwnerType);
query->bindValue(21, record._lockstate._lockOwnerDisplayName);
Expand Down
10 changes: 3 additions & 7 deletions src/common/syncjournalfilerecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,7 @@ class OCSYNC_EXPORT SyncJournalFileRecord
return !_path.isEmpty();
}

enum class EncryptionStatus : int {
NotEncrypted = 0,
Encrypted = 1,
EncryptedMigratedV1_2 = 2,
};
using EncryptionStatus = EncryptionStatusEnums::JournalDbEncryptionStatus;

/** Returns the numeric part of the full id in _fileId.
*
Expand All @@ -73,7 +69,7 @@ class OCSYNC_EXPORT SyncJournalFileRecord
[[nodiscard]] bool isVirtualFile() const { return _type == ItemTypeVirtualFile || _type == ItemTypeVirtualFileDownload; }
[[nodiscard]] QString path() const { return QString::fromUtf8(_path); }
[[nodiscard]] QString e2eMangledName() const { return QString::fromUtf8(_e2eMangledName); }
[[nodiscard]] bool isE2eEncrypted() const { return _isE2eEncrypted != SyncJournalFileRecord::EncryptionStatus::NotEncrypted; }
[[nodiscard]] bool isE2eEncrypted() const { return _e2eEncryptionStatus != EncryptionStatus::NotEncrypted; }

QByteArray _path;
quint64 _inode = 0;
Expand All @@ -86,7 +82,7 @@ class OCSYNC_EXPORT SyncJournalFileRecord
bool _serverHasIgnoredFiles = false;
QByteArray _checksumHeader;
QByteArray _e2eMangledName;
EncryptionStatus _isE2eEncrypted = EncryptionStatus::NotEncrypted;
EncryptionStatus _e2eEncryptionStatus = EncryptionStatus::NotEncrypted;
SyncJournalFileLockInfo _lockstate;
bool _isShared = false;
qint64 _lastShareStateFetchedTimestamp = 0;
Expand Down
28 changes: 28 additions & 0 deletions src/csync/csync.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,34 @@

namespace OCC {
class SyncJournalFileRecord;

namespace EncryptionStatusEnums {

Q_NAMESPACE

enum class ItemEncryptionStatus : int {
NotEncrypted = 0,
Encrypted = 1,
EncryptedMigratedV1_2 = 2,
};

Q_ENUM_NS(ItemEncryptionStatus)

enum class JournalDbEncryptionStatus : int {
NotEncrypted = 0,
Encrypted = 1,
EncryptedMigratedV1_2Invalid = 2,
EncryptedMigratedV1_2 = 3,
};

Q_ENUM_NS(JournalDbEncryptionStatus)

ItemEncryptionStatus fromDbEncryptionStatus(JournalDbEncryptionStatus encryptionStatus);

JournalDbEncryptionStatus toDbEncryptionStatus(ItemEncryptionStatus encryptionStatus);

}

}

#if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && !defined(Q_CC_CLANG) && (__GNUC__ * 100 + __GNUC_MINOR__ < 408)
Expand Down
4 changes: 2 additions & 2 deletions src/libsync/discovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(
item->_etag = serverEntry.etag;
item->_directDownloadUrl = serverEntry.directDownloadUrl;
item->_directDownloadCookies = serverEntry.directDownloadCookies;
item->_isEncrypted = serverEntry.isE2eEncrypted() ? SyncFileItem::EncryptionStatus::Encrypted : SyncFileItem::EncryptionStatus::NotEncrypted;
item->_e2eEncryptionStatus = serverEntry.isE2eEncrypted() ? SyncFileItem::EncryptionStatus::Encrypted : SyncFileItem::EncryptionStatus::NotEncrypted;
item->_encryptedFileName = [=] {
if (serverEntry.e2eMangledName.isEmpty()) {
return QString();
Expand Down Expand Up @@ -1292,7 +1292,7 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
if (base.isE2eEncrypted()) {
// renaming the encrypted folder is done via remove + re-upload hence we need to mark the newly created folder as encrypted
// base is a record in the SyncJournal database that contains the data about the being-renamed folder with it's old name and encryption information
item->_isEncrypted = static_cast<SyncFileItem::EncryptionStatus>(base._isE2eEncrypted);
item->_e2eEncryptionStatus = EncryptionStatusEnums::fromDbEncryptionStatus(base._e2eEncryptionStatus);
}
postProcessLocalNew();
finalize();
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/encryptfolderjob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ void EncryptFolderJob::slotEncryptionFlagSuccess(const QByteArray &fileId)
qCWarning(lcEncryptFolderJob) << "No valid record found in local DB for fileId" << fileId;
}

rec._isE2eEncrypted = SyncJournalFileRecord::EncryptionStatus::EncryptedMigratedV1_2;
rec._e2eEncryptionStatus = SyncJournalFileRecord::EncryptionStatus::EncryptedMigratedV1_2;
const auto result = _journal->setFileRecord(rec);
if (!result) {
qCWarning(lcEncryptFolderJob) << "Error when setting the file record to the database" << rec._path << result.error();
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/owncloudpropagator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ void OwncloudPropagator::startDirectoryPropagation(const SyncFileItemPtr &item,
_anotherSyncNeeded = true;
} else if (item->_isEncryptedMetadataNeedUpdate) {
SyncJournalFileRecord record;
if (_journal->getFileRecord(item->_file, &record) && record._isE2eEncrypted == SyncJournalFileRecord::EncryptionStatus::EncryptedMigratedV1_2) {
if (_journal->getFileRecord(item->_file, &record) && record._e2eEncryptionStatus == SyncJournalFileRecord::EncryptionStatus::EncryptedMigratedV1_2) {
qCDebug(lcPropagator) << "could have upgraded metadata";
item->_instruction = CSyncEnums::CSYNC_INSTRUCTION_ERROR;
item->_errorString = tr("Error with the metadata. Getting unexpected metadata format.");
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/propagatedownloadencrypted.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ void PropagateDownloadEncrypted::checkFolderEncryptedMetadata(const QJsonDocumen
<< _item->_instruction << _item->_file << _item->_encryptedFileName;
const QString filename = _info.fileName();
const FolderMetadata metadata(_propagator->account(),
_item->_isEncrypted == SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2 ? FolderMetadata::RequiredMetadataVersion::Version1_2 : FolderMetadata::RequiredMetadataVersion::Version1,
_item->_e2eEncryptionStatus == SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2 ? FolderMetadata::RequiredMetadataVersion::Version1_2 : FolderMetadata::RequiredMetadataVersion::Version1,
json.toJson(QJsonDocument::Compact));
if (metadata.isMetadataSetup()) {
const QVector<EncryptedFile> files = metadata.files();
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/propagateremotedeleteencrypted.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void PropagateRemoteDeleteEncrypted::slotFolderEncryptedMetadataReceived(const Q
}

FolderMetadata metadata(_propagator->account(),
_item->_isEncrypted == SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2 ? FolderMetadata::RequiredMetadataVersion::Version1_2 : FolderMetadata::RequiredMetadataVersion::Version1,
_item->_e2eEncryptionStatus == SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2 ? FolderMetadata::RequiredMetadataVersion::Version1_2 : FolderMetadata::RequiredMetadataVersion::Version1,
json.toJson(QJsonDocument::Compact), statusCode);

if (!metadata.isMetadataSetup()) {
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/propagateremotedeleteencryptedrootfolder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ void PropagateRemoteDeleteEncryptedRootFolder::slotFolderEncryptedMetadataReceiv
}

FolderMetadata metadata(_propagator->account(),
_item->_isEncrypted == SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2 ? FolderMetadata::RequiredMetadataVersion::Version1_2 : FolderMetadata::RequiredMetadataVersion::Version1,
_item->_e2eEncryptionStatus == SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2 ? FolderMetadata::RequiredMetadataVersion::Version1_2 : FolderMetadata::RequiredMetadataVersion::Version1,
json.toJson(QJsonDocument::Compact), statusCode);

if (!metadata.isMetadataSetup()) {
Expand Down
2 changes: 1 addition & 1 deletion src/libsync/propagateremotemkdir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ void PropagateRemoteMkdir::slotEncryptFolderFinished()
{
qCDebug(lcPropagateRemoteMkdir) << "Success making the new folder encrypted";
propagator()->_activeJobList.removeOne(this);
_item->_isEncrypted = SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2;
_item->_e2eEncryptionStatus = SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2;
success();
}

Expand Down
4 changes: 2 additions & 2 deletions src/libsync/propagateuploadencrypted.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ void PropagateUploadEncrypted::slotFolderEncryptedMetadataReceived(const QJsonDo

// Encrypt File!
_metadata.reset(new FolderMetadata(_propagator->account(),
_item->_isEncrypted == SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2 ? FolderMetadata::RequiredMetadataVersion::Version1_2 : FolderMetadata::RequiredMetadataVersion::Version1,
_item->_e2eEncryptionStatus == SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2 ? FolderMetadata::RequiredMetadataVersion::Version1_2 : FolderMetadata::RequiredMetadataVersion::Version1,
json.toJson(QJsonDocument::Compact), statusCode));

if (!_metadata->isMetadataSetup()) {
Expand Down Expand Up @@ -171,7 +171,7 @@ void PropagateUploadEncrypted::slotFolderEncryptedMetadataReceived(const QJsonDo
encryptedFile.initializationVector = EncryptionHelper::generateRandom(16);

_item->_encryptedFileName = _remoteParentPath + QLatin1Char('/') + encryptedFile.encryptedFilename;
_item->_isEncrypted = SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2;
_item->_e2eEncryptionStatus = SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2;

qCDebug(lcPropagateUploadEncrypted) << "Creating the encrypted file.";

Expand Down
53 changes: 50 additions & 3 deletions src/libsync/syncfileitem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,53 @@ namespace OCC {

Q_LOGGING_CATEGORY(lcFileItem, "nextcloud.sync.fileitem", QtInfoMsg)

namespace EncryptionStatusEnums {

ItemEncryptionStatus fromDbEncryptionStatus(JournalDbEncryptionStatus encryptionStatus)
{
auto result = ItemEncryptionStatus::NotEncrypted;

switch (encryptionStatus)
{
case JournalDbEncryptionStatus::Encrypted:
result = ItemEncryptionStatus::Encrypted;
break;
case JournalDbEncryptionStatus::EncryptedMigratedV1_2:
result = ItemEncryptionStatus::EncryptedMigratedV1_2;
break;
case JournalDbEncryptionStatus::EncryptedMigratedV1_2Invalid:
result = ItemEncryptionStatus::Encrypted;
break;
case JournalDbEncryptionStatus::NotEncrypted:
result = ItemEncryptionStatus::NotEncrypted;
break;
}

return result;
}

JournalDbEncryptionStatus toDbEncryptionStatus(ItemEncryptionStatus encryptionStatus)
{
auto result = JournalDbEncryptionStatus::NotEncrypted;

switch (encryptionStatus)
{
case ItemEncryptionStatus::Encrypted:
result = JournalDbEncryptionStatus::Encrypted;
break;
case ItemEncryptionStatus::EncryptedMigratedV1_2:
result = JournalDbEncryptionStatus::EncryptedMigratedV1_2;
break;
case ItemEncryptionStatus::NotEncrypted:
result = JournalDbEncryptionStatus::NotEncrypted;
break;
}

return result;
}

}

SyncJournalFileRecord SyncFileItem::toSyncJournalFileRecordWithInode(const QString &localFileName) const
{
SyncJournalFileRecord rec;
Expand All @@ -49,7 +96,7 @@ SyncJournalFileRecord SyncFileItem::toSyncJournalFileRecordWithInode(const QStri
rec._serverHasIgnoredFiles = _serverHasIgnoredFiles;
rec._checksumHeader = _checksumHeader;
rec._e2eMangledName = _encryptedFileName.toUtf8();
rec._isE2eEncrypted = isEncrypted() ? SyncJournalFileRecord::EncryptionStatus::EncryptedMigratedV1_2 : SyncJournalFileRecord::EncryptionStatus::NotEncrypted;
rec._e2eEncryptionStatus = EncryptionStatusEnums::toDbEncryptionStatus(_e2eEncryptionStatus);
rec._lockstate._locked = _locked == LockStatus::LockedItem;
rec._lockstate._lockOwnerDisplayName = _lockOwnerDisplayName;
rec._lockstate._lockOwnerId = _lockOwnerId;
Expand Down Expand Up @@ -86,7 +133,7 @@ SyncFileItemPtr SyncFileItem::fromSyncJournalFileRecord(const SyncJournalFileRec
item->_serverHasIgnoredFiles = rec._serverHasIgnoredFiles;
item->_checksumHeader = rec._checksumHeader;
item->_encryptedFileName = rec.e2eMangledName();
item->_isEncrypted = static_cast<SyncFileItem::EncryptionStatus>(rec._isE2eEncrypted);
item->_e2eEncryptionStatus = EncryptionStatusEnums::fromDbEncryptionStatus(rec._e2eEncryptionStatus);
item->_locked = rec._lockstate._locked ? LockStatus::LockedItem : LockStatus::UnlockedItem;
item->_lockOwnerDisplayName = rec._lockstate._lockOwnerDisplayName;
item->_lockOwnerId = rec._lockstate._lockOwnerId;
Expand Down Expand Up @@ -123,7 +170,7 @@ SyncFileItemPtr SyncFileItem::fromProperties(const QString &filePath, const QMap
item->_isShared = item->_remotePerm.hasPermission(RemotePermissions::IsShared);
item->_lastShareStateFetchedTimestamp = QDateTime::currentMSecsSinceEpoch();

item->_isEncrypted = (properties.value(QStringLiteral("is-encrypted")) == QStringLiteral("1") ? SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2 : SyncFileItem::EncryptionStatus::NotEncrypted);
item->_e2eEncryptionStatus = (properties.value(QStringLiteral("is-encrypted")) == QStringLiteral("1") ? SyncFileItem::EncryptionStatus::EncryptedMigratedV1_2 : SyncFileItem::EncryptionStatus::NotEncrypted);
item->_locked =
properties.value(QStringLiteral("lock")) == QStringLiteral("1") ? SyncFileItem::LockStatus::LockedItem : SyncFileItem::LockStatus::UnlockedItem;
item->_lockOwnerDisplayName = properties.value(QStringLiteral("lock-owner-displayname"));
Expand Down
11 changes: 3 additions & 8 deletions src/libsync/syncfileitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,7 @@ class OWNCLOUDSYNC_EXPORT SyncFileItem
};
Q_ENUM(Direction)

enum class EncryptionStatus : int {
NotEncrypted = 0,
Encrypted = 1,
EncryptedMigratedV1_2 = 2,
};
Q_ENUM(EncryptionStatus)
using EncryptionStatus = EncryptionStatusEnums::ItemEncryptionStatus;

// Note: the order of these statuses is used for ordering in the SortedActivityListModel
enum Status { // stored in 4 bits
Expand Down Expand Up @@ -234,7 +229,7 @@ class OWNCLOUDSYNC_EXPORT SyncFileItem
&& !(_instruction == CSYNC_INSTRUCTION_CONFLICT && _status == SyncFileItem::Success);
}

[[nodiscard]] bool isEncrypted() const { return _isEncrypted != SyncFileItem::EncryptionStatus::NotEncrypted; }
[[nodiscard]] bool isEncrypted() const { return _e2eEncryptionStatus != EncryptionStatus::NotEncrypted; }

// Variables useful for everybody

Expand Down Expand Up @@ -281,7 +276,7 @@ class OWNCLOUDSYNC_EXPORT SyncFileItem
Status _status BITFIELD(4);
bool _isRestoration BITFIELD(1); // The original operation was forbidden, and this is a restoration
bool _isSelectiveSync BITFIELD(1); // The file is removed or ignored because it is in the selective sync list
EncryptionStatus _isEncrypted = EncryptionStatus::NotEncrypted; // The file is E2EE or the content of the directory should be E2EE
EncryptionStatus _e2eEncryptionStatus = EncryptionStatus::NotEncrypted; // The file is E2EE or the content of the directory should be E2EE
quint16 _httpErrorCode = 0;
RemotePermissions _remotePerm;
QString _errorString; // Contains a string only in case of error
Expand Down
2 changes: 1 addition & 1 deletion test/testfolderman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private slots:
// the server, let's just manually set the encryption bool in the folder journal
SyncJournalFileRecord rec;
QVERIFY(folder->journalDb()->getFileRecord(QStringLiteral("encrypted"), &rec));
rec._isE2eEncrypted = SyncJournalFileRecord::EncryptionStatus::EncryptedMigratedV1_2;
rec._e2eEncryptionStatus = SyncJournalFileRecord::EncryptionStatus::EncryptedMigratedV1_2;
rec._path = QStringLiteral("encrypted").toUtf8();
rec._type = CSyncEnums::ItemTypeDirectory;
QVERIFY(folder->journalDb()->setFileRecord(rec));
Expand Down