Merge pull request #3449 from nextcloud/bugfix/fix-vfs-crash-and-false-conflict-on-local-new

Fix VFS crash and false conflict on local new.
This commit is contained in:
allexzander 2021-07-01 13:33:21 +03:00 committed by GitHub
commit dca7b8d8d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 38 deletions

View file

@ -126,6 +126,8 @@ public:
ASSERT(_isError);
return std::move(_error);
}
bool isValid() const { return !_isError; }
};
namespace detail {

View file

@ -896,46 +896,68 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
auto postProcessLocalNew = [item, localEntry, path, this]() {
// TODO: We may want to execute the same logic for non-VFS mode, as, moving/renaming the same folder by 2 or more clients at the same time is not possible in Web UI.
// Keeping it like this (for VFS files and folders only) just to fix a user issue.
const auto isVfsEnabled = _discoveryData->_syncOptions._vfs && _discoveryData->_syncOptions._vfs->mode() != Vfs::Off;
if (localEntry.isVirtualFile || (localEntry.isDirectory && isVfsEnabled)) {
// must be a dehydrated placeholder
const bool isFilePlaceHolder = !localEntry.isDirectory && _discoveryData->_syncOptions._vfs->isDehydratedPlaceholder(_discoveryData->_localDir + path._local);
// a folder must be online-only (no files should be hydrated)
const bool isFolderPlaceholder = localEntry.isDirectory && *_discoveryData->_syncOptions._vfs->availability(path._local) == VfsItemAvailability::OnlineOnly;
Q_ASSERT(item->_instruction == CSYNC_INSTRUCTION_NEW);
if (item->_instruction != CSYNC_INSTRUCTION_NEW) {
qCWarning(lcDisco) << "Wiping virtual file without db entry for" << path._local << ". But, item->_instruction is" << item->_instruction;
}
// must be a file placeholder or an online-only folder placeholder
if (isFilePlaceHolder || isFolderPlaceholder) {
if (isFolderPlaceholder) {
qCInfo(lcDisco) << "Wiping virtual folder without db entry for" << path._local;
} else {
qCInfo(lcDisco) << "Wiping virtual file without db entry for" << path._local;
}
item->_instruction = CSYNC_INSTRUCTION_REMOVE;
item->_direction = SyncFileItem::Down;
// this flag needs to be unset, otherwise a folder would get marked as new in the processSubJobs
_childModified = false;
if (isFolderPlaceholder && _discoveryData) {
emit _discoveryData->addErrorToGui(SyncFileItem::SoftError, tr("Conflict when uploading a folder. It's going to get cleared!"), path._local);
}
} else {
if (localEntry.isDirectory && !isFolderPlaceholder) {
qCInfo(lcDisco) << "Virtual directory without db entry for" << path._local << "but it contains hydrated file(s), so let's keep it and reupload.";
if (_discoveryData) {
emit _discoveryData->addErrorToGui(SyncFileItem::SoftError, tr("Conflict when uploading some files to a folder. Those, conflicted, are going to get cleared!"), path._local);
}
return;
}
qCWarning(lcDisco) << "Virtual file without db entry for" << path._local
<< "but looks odd, keeping";
item->_instruction = CSYNC_INSTRUCTION_IGNORE;
}
if (!(_discoveryData && _discoveryData->_syncOptions._vfs && _discoveryData->_syncOptions._vfs->mode() != Vfs::Off)) {
// for VFS files and folders only
return;
}
if (!localEntry.isVirtualFile && !localEntry.isDirectory) {
return;
}
Q_ASSERT(item->_instruction == CSYNC_INSTRUCTION_NEW);
if (item->_instruction != CSYNC_INSTRUCTION_NEW) {
qCWarning(lcDisco) << "Trying to wipe a virtual item" << path._local << " with item->_instruction" << item->_instruction;
return;
}
// must be a dehydrated placeholder
const bool isFilePlaceHolder = !localEntry.isDirectory && _discoveryData->_syncOptions._vfs->isDehydratedPlaceholder(_discoveryData->_localDir + path._local);
// either correct availability, or a result with error if the folder is new or otherwise has no availability set yet
const auto folderPlaceHolderAvailability = localEntry.isDirectory ? _discoveryData->_syncOptions._vfs->availability(path._local) : Vfs::AvailabilityResult(Vfs::AvailabilityError::NoSuchItem);
const auto folderPinState = localEntry.isDirectory ? _discoveryData->_syncOptions._vfs->pinState(path._local) : Optional<PinStateEnums::PinState>(PinState::Unspecified);
if (!isFilePlaceHolder && !folderPlaceHolderAvailability.isValid() && !folderPinState.isValid()) {
// not a file placeholder and not a synced folder placeholder (new local folder)
return;
}
const auto isFolderPinStateOnlineOnly = (folderPinState.isValid() && *folderPinState == PinState::OnlineOnly);
const auto isfolderPlaceHolderAvailabilityOnlineOnly = (folderPlaceHolderAvailability.isValid() && *folderPlaceHolderAvailability == VfsItemAvailability::OnlineOnly);
// a folder is considered online-only if: no files are hydrated, or, if it's an empty folder
const auto isOnlineOnlyFolder = isfolderPlaceHolderAvailabilityOnlineOnly || !folderPlaceHolderAvailability && isFolderPinStateOnlineOnly;
if (!isFilePlaceHolder && !isOnlineOnlyFolder) {
if (localEntry.isDirectory && folderPlaceHolderAvailability.isValid() && !isOnlineOnlyFolder) {
// a VFS folder but is not online0only (has some files hydrated)
qCInfo(lcDisco) << "Virtual directory without db entry for" << path._local << "but it contains hydrated file(s), so let's keep it and reupload.";
emit _discoveryData->addErrorToGui(SyncFileItem::SoftError, tr("Conflict when uploading some files to a folder. Those, conflicted, are going to get cleared!"), path._local);
return;
}
qCWarning(lcDisco) << "Virtual file without db entry for" << path._local
<< "but looks odd, keeping";
item->_instruction = CSYNC_INSTRUCTION_IGNORE;
return;
}
if (isOnlineOnlyFolder) {
// if we're wiping a folder, we will only get this function called once and will wipe a folder along with it's files and also display one error in GUI
qCInfo(lcDisco) << "Wiping virtual folder without db entry for" << path._local;
emit _discoveryData->addErrorToGui(SyncFileItem::SoftError, tr("Conflict when uploading a folder. It's going to get cleared!"), path._local);
} else {
qCInfo(lcDisco) << "Wiping virtual file without db entry for" << path._local;
emit _discoveryData->addErrorToGui(SyncFileItem::SoftError, tr("Conflict when uploading a file. It's going to get removed!"), path._local);
}
item->_instruction = CSYNC_INSTRUCTION_REMOVE;
item->_direction = SyncFileItem::Down;
// this flag needs to be unset, otherwise a folder would get marked as new in the processSubJobs
_childModified = false;
};
// Check if it is a move