From 289df252b56d1db7573692c57ba6d50b1af52e7b Mon Sep 17 00:00:00 2001 From: allexzander Date: Tue, 13 Jul 2021 16:56:31 +0300 Subject: VFS + E2EE. Handle scenario of sync journal deleted. Signed-off-by: allexzander --- src/libsync/discovery.cpp | 72 +++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 33 deletions(-) (limited to 'src/libsync/discovery.cpp') diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 8252c82fc..555a2d195 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -460,17 +460,9 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( // The file is known in the db already if (dbEntry.isValid()) { - qint64 size = serverEntry.size; - - bool metaDataSizeNeedsUpdateForE2EEFile = false; - - if (dbEntry.isVirtualFile() && (!item->_encryptedFileName.isEmpty()) && size > 0) { - // make sure we set correct size when file was downloaded previously and has now been changed on the server - // serverEntry always includes extra CommonConstants::e2EeTagSize bytes for e2e encrypted files - // we don't need those neither when creating a placeholder nor when storing hydrated file on disk - size = serverEntry.size - CommonConstants::e2EeTagSize; - metaDataSizeNeedsUpdateForE2EEFile = dbEntry._fileSize == serverEntry.size; - } + const bool isVirtualE2EePlaceholder = dbEntry.isVirtualFile() && !item->_encryptedFileName.isEmpty() && serverEntry.size > 0; + const qint64 sizeOnServer = isVirtualE2EePlaceholder ? serverEntry.size - CommonConstants::e2EeTagSize : serverEntry.size; + const bool metaDataSizeNeedsUpdateForE2EeFilePlaceholder = isVirtualE2EePlaceholder && dbEntry._fileSize == serverEntry.size; if (serverEntry.isDirectory != dbEntry.isDirectory()) { // If the type of the entity changed, it's like NEW, but @@ -478,7 +470,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( item->_instruction = CSYNC_INSTRUCTION_TYPE_CHANGE; item->_direction = SyncFileItem::Down; item->_modtime = serverEntry.modtime; - item->_size = size; + item->_size = sizeOnServer; } else if ((dbEntry._type == ItemTypeVirtualFileDownload || localEntry.type == ItemTypeVirtualFileDownload) && (localEntry.isValid() || _queryLocal == ParentNotChanged)) { // The above check for the localEntry existing is important. Otherwise it breaks @@ -489,7 +481,7 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( } else if (dbEntry._etag != serverEntry.etag) { item->_direction = SyncFileItem::Down; item->_modtime = serverEntry.modtime; - item->_size = size; + item->_size = sizeOnServer; if (serverEntry.isDirectory) { ENFORCE(dbEntry.isDirectory()); item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; @@ -499,9 +491,10 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( } else { item->_instruction = CSYNC_INSTRUCTION_SYNC; } - } else if (dbEntry._remotePerm != serverEntry.remotePerm || dbEntry._fileId != serverEntry.fileId || metaDataSizeNeedsUpdateForE2EEFile) { - if (metaDataSizeNeedsUpdateForE2EEFile) { - item->_size = serverEntry.size - CommonConstants::e2EeTagSize; + } else if (dbEntry._remotePerm != serverEntry.remotePerm || dbEntry._fileId != serverEntry.fileId || metaDataSizeNeedsUpdateForE2EeFilePlaceholder) { + if (metaDataSizeNeedsUpdateForE2EeFilePlaceholder) { + // we are updating placeholder sizes after migrating from older versions with VFS + E2EE implicit hydration not supported + item->_size = sizeOnServer; } item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA; item->_direction = SyncFileItem::Down; @@ -509,21 +502,26 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( // if (is virtual mode enabled and folder is encrypted - check if the size is the same as on the server and then - trigger server query // to update a placeholder with corrected size (-16 Bytes) // or, maybe, add a flag to the database - vfsE2eeSizeCorrected? if it is not set - subtract it from the placeholder's size and re-create/update a placeholder? - QueryMode serverQueryMode = ParentNotChanged; - if (dbEntry.isDirectory() && dbEntry._isE2eEncrypted) { - qint64 totalSizeOfPath = 0; - if (_discoveryData->_statedb->listFilesInPath(dbEntry.path().toUtf8(), [this, &totalSizeOfPath](const OCC::SyncJournalFileRecord &record) { + const QueryMode serverQueryMode = [this, &dbEntry, &serverEntry]() { + if (dbEntry.isDirectory() && dbEntry._isE2eEncrypted) { + qint64 localFolderSize = 0; + const auto listFilesCallback = [this, &localFolderSize](const OCC::SyncJournalFileRecord &record) { if (record.isFile()) { - totalSizeOfPath += record._fileSize + CommonConstants::e2EeTagSize; + // add CommonConstants::e2EeTagSize so we will know the size of E2EE file on the server + localFolderSize += record._fileSize + CommonConstants::e2EeTagSize; } else if (record.isVirtualFile()) { - totalSizeOfPath += record._fileSize; + // just a virtual file, so, the size must contain CommonConstants::e2EeTagSize if it was not corrected already + localFolderSize += record._fileSize; } - })) { - if (totalSizeOfPath != 0 && totalSizeOfPath == serverEntry.sizeOfFolder) { - serverQueryMode = NormalQuery; + }; + if (_discoveryData->_statedb->listFilesInPath(dbEntry.path().toUtf8(), listFilesCallback) + && localFolderSize != 0 && localFolderSize == serverEntry.sizeOfFolder) { + return NormalQuery; } } - } + return ParentNotChanged; + }(); + processFileAnalyzeLocalInfo(item, path, localEntry, serverEntry, dbEntry, serverQueryMode); return; } @@ -562,16 +560,24 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( && _pinState != PinState::AlwaysLocal && !FileSystem::isExcludeFile(item->_file)) { item->_type = ItemTypeVirtualFile; - if (!item->_encryptedFileName.isEmpty()) { - // We are syncing a file for the first time (local entry is invalid) and it is encrypted file that will be virtual once synced - // to avoid having error of "file has changed during sync" when trying to hydrate it excplicitly - we must remove CommonConstants::e2EeTagSize bytes from the end - // as explicit hydration does not care if these bytes are present in the placeholder or not, but, the size must not change in the middle of the sync - // this way it works for both implicit and explicit hydration by making a placeholder size that does not includes encryption tag CommonConstants::e2EeTagSize bytes - item->_size = serverEntry.size - CommonConstants::e2EeTagSize; - } if (isVfsWithSuffix()) addVirtualFileSuffix(tmp_path._original); } + // a virtual file is on disk but is not in the database + const bool fileOnDiskIsVirtual = localEntry.isValid() && localEntry.isVirtualFile; + // a new virtual file + const bool isNewVirtualFileNotOnDiskYet = !localEntry.isValid() && item->_type == ItemTypeVirtualFile; + + if ((fileOnDiskIsVirtual || isNewVirtualFileNotOnDiskYet) + && opts._vfs->mode() != Vfs::Off + && !item->_encryptedFileName.isEmpty()) { + // We are syncing a file for the first time (local entry is invalid) and it is encrypted file that will be virtual once synced + // to avoid having error of "file has changed during sync" when trying to hydrate it excplicitly - we must remove CommonConstants::e2EeTagSize bytes from the end + // as explicit hydration does not care if these bytes are present in the placeholder or not, but, the size must not change in the middle of the sync + // this way it works for both implicit and explicit hydration by making a placeholder size that does not includes encryption tag CommonConstants::e2EeTagSize bytes + // another scenario - we are syncing a file which is on disk but not in the database (database was removed or file was not written there yet) + item->_size = serverEntry.size - CommonConstants::e2EeTagSize; + } processFileAnalyzeLocalInfo(item, tmp_path, localEntry, serverEntry, dbEntry, _queryServer); }; -- cgit v1.2.3