diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index ddeccdc15078..9bafd4fe593c 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -481,11 +481,10 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare E2EFiles e2eFiles = new E2EFiles(parentFile, null, new File(mOriginalStoragePath), null, null); FileLock fileLock = null; long size; - boolean metadataExists = false; String token = null; Object object = null; - + FileChannel channel = null; ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(getContext()); String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY); @@ -497,7 +496,13 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } long counter = getE2ECounter(parentFile); - token = getFolderUnlockTokenOrLockFolder(client, parentFile, counter); + + try { + token = getFolderUnlockTokenOrLockFolder(client, parentFile, counter); + } catch (Exception e) { + Log_OC.e(TAG, "Failed to lock folder", e); + return new RemoteOperationResult<>(e); + } // Update metadata EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2(); @@ -508,7 +513,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare if (isEndToEndVersionAtLeastV2()) { if (object == null) { - return new RemoteOperationResult(new IllegalStateException("Metadata does not exist")); + return new RemoteOperationResult<>(new IllegalStateException("Metadata does not exist")); } } else { object = getDecryptedFolderMetadataV1(publicKey, object); @@ -518,7 +523,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare List fileNames = getCollidedFileNames(object); - RemoteOperationResult collisionResult = checkNameCollision(parentFile, client, fileNames, parentFile.isEncrypted()); + final var collisionResult = checkNameCollision(parentFile, client, fileNames, parentFile.isEncrypted()); if (collisionResult != null) { result = collisionResult; return collisionResult; @@ -550,7 +555,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare Triple channelResult = initFileChannel(result, fileLock, e2eFiles); fileLock = channelResult.getFirst(); result = channelResult.getSecond(); - FileChannel channel = channelResult.getThird(); + channel = channelResult.getThird(); size = getChannelSize(channel); updateSize(size); @@ -563,15 +568,15 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } } catch (FileNotFoundException e) { Log_OC.e(TAG, mFile.getStoragePath() + " does not exist anymore"); - result = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); + result = new RemoteOperationResult<>(ResultCode.LOCAL_FILE_NOT_FOUND); } catch (OverlappingFileLockException e) { Log_OC.e(TAG, "Overlapping file lock exception"); - result = new RemoteOperationResult(ResultCode.LOCK_FAILED); + result = new RemoteOperationResult<>(ResultCode.LOCK_FAILED); } catch (Exception e) { Log_OC.e(TAG, "UploadFileOperation exception: " + e.getLocalizedMessage()); - result = new RemoteOperationResult(e); + result = new RemoteOperationResult<>(e); } finally { - result = cleanupE2EUpload(fileLock, e2eFiles, result, object, client, token); + result = cleanupE2EUpload(fileLock, channel, e2eFiles, result, object, client, token); } completeE2EUpload(result, e2eFiles, client); @@ -599,13 +604,20 @@ private long getE2ECounter(OCFile parentFile) { private String getFolderUnlockTokenOrLockFolder(OwnCloudClient client, OCFile parentFile, long counter) throws UploadException { if (mFolderUnlockToken != null && !mFolderUnlockToken.isEmpty()) { + Log_OC.d(TAG, "Reusing existing folder unlock token from previous upload attempt"); return mFolderUnlockToken; } String token = EncryptionUtils.lockFolder(parentFile, client, counter); + if (token == null || token.isEmpty()) { + Log_OC.e(TAG, "Lock folder returned null or empty token"); + throw new UploadException("Failed to lock folder: token is null or empty"); + } + mUpload.setFolderUnlockToken(token); uploadsStorageManager.updateUpload(mUpload); + Log_OC.d(TAG, "Folder locked successfully, token saved"); return token; } @@ -696,7 +708,8 @@ private void setUploadOperationForE2E(String token, private Triple initFileChannel(RemoteOperationResult result, FileLock fileLock, E2EFiles e2eFiles) throws IOException { FileChannel channel = null; - try (RandomAccessFile randomAccessFile = new RandomAccessFile(mFile.getStoragePath(), "rw")) { + try { + RandomAccessFile randomAccessFile = new RandomAccessFile(mFile.getStoragePath(), "rw"); channel = randomAccessFile.getChannel(); fileLock = channel.tryLock(); } catch (IOException ioException) { @@ -725,7 +738,7 @@ private Triple initFileChannel(Rem Log_OC.d(TAG, "Error caught at getChannelFromFile: " + e); } } else { - result = new RemoteOperationResult(ResultCode.LOCK_FAILED); + result = new RemoteOperationResult<>(ResultCode.LOCK_FAILED); } } } @@ -750,12 +763,12 @@ private RemoteOperationResult performE2EUpload(E2EClientData data) throws Operat throw new OperationCancelledException(); } - RemoteOperationResult result = mUploadOperation.execute(data.getClient()); + var result = mUploadOperation.execute(data.getClient()); /// move local temporal file or original file to its corresponding // location in the Nextcloud local folder if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { - result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); + result = new RemoteOperationResult<>(ResultCode.SYNC_CONFLICT); } return result; @@ -879,31 +892,61 @@ private void completeE2EUpload(RemoteOperationResult result, E2EFiles e2eFiles, e2eFiles.deleteTemporalFile(); } - private RemoteOperationResult cleanupE2EUpload(FileLock fileLock, E2EFiles e2eFiles, RemoteOperationResult result, Object object, OwnCloudClient client, String token) { + private RemoteOperationResult cleanupE2EUpload(FileLock fileLock, FileChannel channel, E2EFiles e2eFiles, RemoteOperationResult result, Object object, OwnCloudClient client, String token) { mUploadStarted.set(false); if (fileLock != null) { try { - fileLock.release(); + // Only release if the channel is still open/valid + if (channel != null && channel.isOpen()) { + fileLock.release(); + } } catch (IOException e) { Log_OC.e(TAG, "Failed to unlock file with path " + mFile.getStoragePath()); } } + if (channel != null) { + try { + channel.close(); + } catch (IOException e) { + Log_OC.e(TAG, "Failed to close file channel", e); + } + } + e2eFiles.deleteTemporalFileWithOriginalFileComparison(); if (result == null) { - result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); + result = new RemoteOperationResult<>(ResultCode.UNKNOWN_ERROR); } logResult(result, mFile.getStoragePath(), mFile.getRemotePath()); + if (token == null || token.isEmpty()) { + Log_OC.e(TAG, "CRITICAL ERROR: Folder was locked but token is null/empty. Cannot unlock! " + + "Folder: " + e2eFiles.getParentFile().getFileName()); + RemoteOperationResult tokenError = new RemoteOperationResult<>( + new IllegalStateException("Folder locked but token lost - manual intervention may be required") + ); + + // Override result only if original operation succeeded + if (result.isSuccess()) { + result = tokenError; + } + return result; + } + // Unlock must be done otherwise folder stays locked and user can't upload any file RemoteOperationResult unlockFolderResult; - if (object instanceof DecryptedFolderMetadataFileV1) { - unlockFolderResult = EncryptionUtils.unlockFolderV1(e2eFiles.getParentFile(), client, token); - } else { - unlockFolderResult = EncryptionUtils.unlockFolder(e2eFiles.getParentFile(), client, token); + try { + if (object instanceof DecryptedFolderMetadataFileV1) { + unlockFolderResult = EncryptionUtils.unlockFolderV1(e2eFiles.getParentFile(), client, token); + } else { + unlockFolderResult = EncryptionUtils.unlockFolder(e2eFiles.getParentFile(), client, token); + } + } catch (Exception e) { + Log_OC.e(TAG, "CRITICAL ERROR: Exception during folder unlock", e); + unlockFolderResult = new RemoteOperationResult<>(e); } if (unlockFolderResult != null && !unlockFolderResult.isSuccess()) { diff --git a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt index d67a0958857c..aee43b97f19e 100644 --- a/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt +++ b/app/src/main/java/com/owncloud/android/operations/e2e/E2EFiles.kt @@ -38,7 +38,7 @@ data class E2EFiles( fun deleteEncryptedTempFile() { if (encryptedTempFile != null) { val isTempEncryptedFileDeleted = encryptedTempFile?.delete() - Log_OC.e(tag, "isTempEncryptedFileDeleted: $isTempEncryptedFileDeleted") + Log_OC.d(tag, "isTempEncryptedFileDeleted: $isTempEncryptedFileDeleted") } else { Log_OC.e(tag, "Encrypted temp file cannot be found") }