Skip to content

Conversation

@infeo
Copy link
Member

@infeo infeo commented Oct 17, 2024

Closes #252.

This PR fixes a problem of cache invalidation in the CryptoPathMapper.

The fix itself is trivial: Instead of only removing/moving the requested entry, we iterate over the whole cache and remove/move all entries, where the cleartextPath starts with the requested cleartext path. Unfortunately, this drastically increases the runtime of the operation from O(log n) to O(n) (assuming finding a single entry needs O(log n) time), where n is the size of the cache.

For proper testing and using more efficient records instead of class, some refactorings were applied.

@infeo infeo added this to the 2.7.1 milestone Oct 17, 2024
@infeo infeo requested a review from overheadhunter October 17, 2024 15:04
@infeo infeo self-assigned this Oct 17, 2024
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 17, 2024

Walkthrough

The changes in this pull request introduce several new classes and methods related to the management of cleartext and ciphertext directory mappings within the Cryptomator application. Key additions include the CiphertextDirCache class for caching directory paths, the CiphertextDirectory record for representing ciphertext directories, and various updates across the codebase to utilize these new structures. Additionally, methods have been modified to ensure consistent access patterns for retrieving directory paths.

Changes

File Path Change Summary
src/main/java/org/cryptomator/cryptofs/CiphertextDirCache.java Added class CiphertextDirCache with caching functionality for cleartext to ciphertext mappings.
src/main/java/org/cryptomator/cryptofs/CiphertextDirectory.java Added record CiphertextDirectory(String dirId, Path path) to encapsulate directory information.
src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java Updated method calls to access directory paths using methods instead of fields.
src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java Simplified instantiation of CiphertextDirectory by removing dependency on CryptoPathMapper.
src/main/java/org/cryptomator/cryptofs/CryptoPathMapper.java Replaced old cache structure with CiphertextDirCache, updated method signatures accordingly.
src/test/java/org/cryptomator/cryptofs/CiphertextDirCacheTest.java Added unit tests for CiphertextDirCache functionality.
Various test files (e.g., CryptoFileSystemImplTest.java, DirectoryIdBackupTest.java) Updated references to CiphertextDirectory to use the new direct class reference.

Assessment against linked issues

Objective Addressed Explanation
Ensure cache clears invalid entries for deleted/moved cleartext directories (Issue #252)

🐰 In the land where the clear paths lay,
New records hop in, brightening the way.
With caches refreshed, and methods anew,
The CipherDir dances, a joyful view!
So let’s celebrate, with a leap and a cheer,
For the changes brought forth, we hold dear! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

🧹 Outside diff range and nitpick comments (11)
src/main/java/org/cryptomator/cryptofs/CipherNodeNameParameters.java (1)

5-6: Consider clarifying the comment about Dagger.

The comment "own file due to dagger" is not very informative for developers who might not be familiar with the project's dependency injection setup. Consider expanding this comment to explain why this record needs to be in its own file due to Dagger constraints.

Suggestion for a more informative comment:

// This record is in its own file to facilitate Dagger's code generation and dependency injection.
// Keeping it separate ensures proper scoping and easier management of dependencies.
src/main/java/org/cryptomator/cryptofs/CipherDir.java (1)

6-7: LGTM: Record definition is well-structured. Consider clarifying the comment.

The CipherDir record is well-defined with appropriate field names and types. However, the comment "own file due to dagger" might benefit from additional context.

Consider expanding the comment to provide more context about why this needs to be in its own file due to Dagger. This will help future maintainers understand the reasoning behind this design decision.

src/main/java/org/cryptomator/cryptofs/dir/BrokenDirectoryFilter.java (1)

31-31: LGTM! Consider enhancing error logging.

The change from path to contentDirPath() aligns with the PR objectives and should improve consistency in directory path handling. This modification is part of the larger effort to address the cache invalidation issues mentioned in #252.

Consider enhancing the error logging on line 33 to include the new contentDirPath() information:

- LOG.warn("Broken directory file: " + dirFile, e);
+ LOG.warn("Broken directory file: {}. Content dir path: {}", dirFile, dirPath, e);

This would provide more context for debugging potential issues related to the new path resolution method.

src/test/java/org/cryptomator/cryptofs/health/dirid/MissingDirIdBackupTest.java (1)

47-47: LGTM: ArgumentMatcher updated to use CipherDir.

The change from CryptoPathMapper.CiphertextDirectory to CipherDir aligns with the PR objectives of using more efficient records. The matcher logic has been correctly updated to use the new methods dirId() and contentDirPath().

Consider adding a comment explaining the purpose of this matcher for improved readability:

+// Matcher to verify that the CipherDir passed to backupManually has the correct dirId and contentDirPath
ArgumentMatcher<CipherDir> cipherDirMatcher = obj -> obj.dirId().equals(dirId) && obj.contentDirPath().isAbsolute() && obj.contentDirPath().equals(expectedPath);
src/main/java/org/cryptomator/cryptofs/DirectoryIdBackup.java (2)

37-39: Method body updates improve encapsulation

The changes in the execute method body, specifically the use of contentDirPath() and dirId() methods instead of direct field access, improve encapsulation and are consistent with the new CipherDir type.

Consider extracting the long line 37-38 into multiple lines for better readability:

-		try (var channel = Files.newByteChannel(ciphertextDirectory.contentDirPath().resolve(Constants.DIR_BACKUP_FILE_NAME), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE); //
-			 var encryptingChannel = wrapEncryptionAround(channel, cryptor)) {
+		try (var channel = Files.newByteChannel(
+				ciphertextDirectory.contentDirPath().resolve(Constants.DIR_BACKUP_FILE_NAME),
+				StandardOpenOption.CREATE_NEW,
+				StandardOpenOption.WRITE
+			);
+			var encryptingChannel = wrapEncryptionAround(channel, cryptor)) {

Line range hint 1-59: Summary: Consistent refactoring improves encapsulation and API design

The changes in this file are part of a larger refactoring effort to improve encapsulation and API design. The consistent replacement of CryptoPathMapper.CiphertextDirectory with CipherDir, and the use of method calls instead of direct field access for dirId and path, align well with the PR objectives.

These modifications enhance the code's maintainability and potentially its performance, especially in relation to the cache invalidation issue mentioned in the PR objectives. The new CipherDir type likely provides better control over directory operations, which could contribute to more accurate cache management.

To ensure the effectiveness of these changes:

  1. Verify that all other parts of the codebase that interact with this class have been updated accordingly.
  2. Consider adding unit tests to validate the behavior of the CipherDir class, particularly its contentDirPath() and dirId() methods.
  3. Update any relevant documentation or JavaDocs for the CipherDir class to explain its purpose and usage.
src/test/java/org/cryptomator/cryptofs/health/dirid/MissingContentDirTest.java (2)

64-64: LGTM: ArgumentMatcher updated to use CipherDir.

The change correctly updates the ArgumentMatcher to work with the new CipherDir type, maintaining the same logical checks as before.

Consider if an exact path match using equals() instead of endsWith() would be more appropriate for contentDirPath(). This depends on whether partial path matches are intentional or if only exact matches are valid.


Line range hint 1-85: Summary: Test updated to use new CipherDir type.

The changes in this file successfully update the MissingContentDirTest to work with the new CipherDir type, which appears to be a replacement for the previous CryptoPathMapper.CiphertextDirectory. The modifications maintain the same logical checks while adapting to the new structure.

These changes align well with the PR objectives of refactoring to facilitate proper testing and utilizing more efficient records instead of classes. The update to use CipherDir suggests an improvement in the overall design of the ciphertext directory representation.

As the codebase transitions to using CipherDir, ensure that all related tests and production code are consistently updated to use this new type. This will help maintain code consistency and prevent potential issues arising from mixed usage of old and new types.

src/test/java/org/cryptomator/cryptofs/attr/AttributeProviderTest.java (1)

Line range hint 1-224: Summary of changes and potential impact.

The changes in this file are minimal and focused, primarily involving the replacement of CiphertextDirectory with CipherDir. This modification appears to be part of a larger refactoring effort. The test structure and coverage should remain unchanged, assuming CipherDir provides equivalent functionality to CiphertextDirectory.

To ensure the integrity of these tests:

  1. Verify that all tests in this class pass after the changes.
  2. Consider adding specific tests for any new or changed functionality in CipherDir if necessary.
  3. Review other test classes that might be affected by this refactoring to ensure consistent usage of CipherDir.
src/test/java/org/cryptomator/cryptofs/ClearToCipherDirCacheTest.java (1)

31-32: Redundant stubbing of dirLoader.load() in testPuttingNewEntryTriggersLoader

The dirLoader.load() method is already stubbed in the beforeEach method. Redefining it in this test is unnecessary and could lead to confusion if the stubs diverge in the future.

Apply this diff to remove the redundant stubbing:

-	var cipherDir = Mockito.mock(CipherDir.class);
-	Mockito.when(dirLoader.load()).thenReturn(cipherDir);
src/test/java/org/cryptomator/cryptofs/health/dirid/OrphanDirTest.java (1)

331-331: Use constants for hard-coded values

The hard-coded strings "aaaaaa" and directory paths like "d/22/2222" are used repeatedly across the tests. Defining these values as constants can improve readability and make future updates easier.

Also applies to: 352-352, 380-380, 414-414, 447-447, 486-486, 529-529

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 6302574 and 0cd494c.

📒 Files selected for processing (24)
  • src/main/java/org/cryptomator/cryptofs/CipherDir.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/CipherNodeNameParameters.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/ClearToCipherDirCache.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java (6 hunks)
  • src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/CryptoPathMapper.java (5 hunks)
  • src/main/java/org/cryptomator/cryptofs/DirectoryIdBackup.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/attr/AbstractCryptoFileAttributeView.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/attr/AttributeProvider.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/dir/BrokenDirectoryFilter.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactory.java (2 hunks)
  • src/main/java/org/cryptomator/cryptofs/health/dirid/MissingContentDir.java (2 hunks)
  • src/main/java/org/cryptomator/cryptofs/health/dirid/MissingDirIdBackup.java (2 hunks)
  • src/main/java/org/cryptomator/cryptofs/health/dirid/OrphanContentDir.java (5 hunks)
  • src/test/java/org/cryptomator/cryptofs/ClearToCipherDirCacheTest.java (1 hunks)
  • src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java (14 hunks)
  • src/test/java/org/cryptomator/cryptofs/CryptoPathMapperTest.java (5 hunks)
  • src/test/java/org/cryptomator/cryptofs/DirectoryIdBackupTest.java (2 hunks)
  • src/test/java/org/cryptomator/cryptofs/attr/AttributeProviderTest.java (2 hunks)
  • src/test/java/org/cryptomator/cryptofs/dir/BrokenDirectoryFilterTest.java (3 hunks)
  • src/test/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactoryTest.java (3 hunks)
  • src/test/java/org/cryptomator/cryptofs/health/dirid/MissingContentDirTest.java (2 hunks)
  • src/test/java/org/cryptomator/cryptofs/health/dirid/MissingDirIdBackupTest.java (2 hunks)
  • src/test/java/org/cryptomator/cryptofs/health/dirid/OrphanDirTest.java (11 hunks)
🧰 Additional context used
🔇 Additional comments (78)
src/main/java/org/cryptomator/cryptofs/CipherNodeNameParameters.java (1)

6-11: LGTM! Well-structured record with null-safety.

The CipherNodeNameParameters record is well-implemented:

  1. It correctly uses the Java 14+ record syntax.
  2. The custom constructor ensures non-null values for both fields using Objects.requireNonNull().
  3. The code follows Java naming conventions.

This implementation provides a concise and null-safe way to represent cipher node name parameters.

src/main/java/org/cryptomator/cryptofs/CipherDir.java (3)

1-5: LGTM: Package declaration and imports are appropriate.

The package declaration aligns with the Cryptomator project structure, and the imports are relevant for the record implementation.


9-12: LGTM: Constructor implementation ensures data integrity.

The custom constructor effectively uses Objects.requireNonNull() to guarantee that both dirId and contentDirPath are non-null. This is a good practice for maintaining data integrity and preventing null pointer exceptions.


1-14: LGTM: Well-implemented CipherDir record.

This new file introduces a well-structured CipherDir record that appears to be part of the solution for the cache invalidation issue mentioned in the PR objectives. The implementation is concise, follows Java best practices for records, and ensures null-safety for its fields.

A few points to highlight:

  1. The record encapsulates dirId and contentDirPath, which are likely key components in the directory mapping system.
  2. The use of a custom constructor with null checks adds an extra layer of data integrity.
  3. The code is clean and easy to understand, which will aid in maintainability.

Overall, this addition seems to be a solid foundation for improving the directory mapping and cache management in the Cryptomator project.

src/main/java/org/cryptomator/cryptofs/health/dirid/MissingDirIdBackup.java (3)

3-3: LGTM: Import statement added correctly.

The import for org.cryptomator.cryptofs.CipherDir is necessary for its usage in the fix method and is correctly placed with other imports from the same package.


Line range hint 1-42: Summary: Approve changes, recommend broader consistency check.

The modifications in this file, namely the addition of the CipherDir import and its usage in the fix method, align well with the PR objectives of refactoring to use more efficient records. These changes appear to be part of a larger effort to replace CryptoPathMapper.CiphertextDirectory with CipherDir.

While the changes in this file look good, I recommend:

  1. Ensuring that all occurrences of CryptoPathMapper.CiphertextDirectory across the codebase have been replaced with CipherDir.
  2. Verifying that all methods expecting CryptoPathMapper.CiphertextDirectory have been updated to work with CipherDir.
  3. Updating any relevant documentation or comments that might reference the old CiphertextDirectory class.

To assist with this broader check, please run the following script:

#!/bin/bash
# Description: Check for any remaining usages of CryptoPathMapper.CiphertextDirectory

# Test: Search for any remaining usages of CryptoPathMapper.CiphertextDirectory
rg --type java "CryptoPathMapper\.CiphertextDirectory"

# Test: List all files that use CipherDir to ensure it's used consistently
rg --type java --files-with-matches "CipherDir"

Review the results to ensure all necessary changes have been made consistently across the codebase.


34-34: Approve change, but verify compatibility with DirectoryIdBackup.backupManually.

The modification to use CipherDir instead of CryptoPathMapper.CiphertextDirectory aligns with the PR objectives of using more efficient records. However, we should ensure that the DirectoryIdBackup.backupManually method is compatible with the CipherDir record.

Please run the following script to verify the compatibility:

If the first test passes and the second test returns no results, it suggests that the change is compatible and consistent across the codebase.

src/main/java/org/cryptomator/cryptofs/health/dirid/MissingContentDir.java (3)

3-3: LGTM: New import statement is correctly placed and necessary.

The addition of import org.cryptomator.cryptofs.CipherDir; is appropriate and required for the subsequent code changes.


Line range hint 1-63: Overall changes look good, but clarification needed on cache invalidation.

The changes in this file are minimal and focused, aligning with the PR objectives of refactoring to use more efficient records. The replacement of CryptoPathMapper.CiphertextDirectory with CipherDir is likely part of a larger refactoring effort to improve code efficiency and maintainability.

However, it's not immediately clear how these changes relate to the cache invalidation issue mentioned in the PR objectives. Could you please clarify how this refactoring contributes to solving the problem of invalid entries in the clear-to-cipher directory mapping cache?

To better understand the context of these changes, let's check for other occurrences of CipherDir in the codebase:

#!/bin/bash
# Description: Search for other occurrences of CipherDir in the codebase

# Test: Search for CipherDir usage
rg --type java 'CipherDir'

55-55: LGTM: Updated to use CipherDir instead of CryptoPathMapper.CiphertextDirectory.

This change aligns with the PR objectives of using more efficient records. The functionality appears to be maintained.

To ensure consistency across the codebase, please verify the signature of the DirectoryIdBackup.backupManually method:

src/test/java/org/cryptomator/cryptofs/health/dirid/MissingDirIdBackupTest.java (2)

3-3: LGTM: New import statement added.

The import for CipherDir is correctly added and is necessary for the changes in the testFix method.


Line range hint 1-51: Overall assessment: Changes improve test efficiency and maintain correctness.

The modifications in this file successfully update the MissingDirIdBackupTest to use the new CipherDir record type, aligning with the PR objectives of using more efficient data structures. The test logic remains intact, ensuring that the MissingDirIdBackup.fix() method correctly interacts with DirectoryIdBackup.backupManually().

These changes contribute to the overall goal of improving the cache invalidation process in the CryptoPathMapper class, as mentioned in the PR objectives.

src/test/java/org/cryptomator/cryptofs/dir/BrokenDirectoryFilterTest.java (4)

3-3: LGTM: Import statement added correctly.

The import for CipherDir is necessary for the changes made in the test methods and is correctly placed with other imports.


49-49: LGTM: Consistent update to use CipherDir.

This change is consistent with the previous modification and aligns with the PR objectives. The test logic remains intact, continuing to verify the behavior of the brokenDirectoryFilter when processing nodes with missing target directories.


Line range hint 1-56: Overall assessment: Changes look good and align with PR objectives.

The modifications in this file consistently replace CryptoPathMapper.CiphertextDirectory with CipherDir, which aligns with the PR objective of using more efficient records instead of classes. The test logic remains intact, ensuring that the behavior of the BrokenDirectoryFilter is still properly verified. These changes contribute to the overall improvement of the codebase.


35-35: LGTM: Updated to use CipherDir instead of CryptoPathMapper.CiphertextDirectory.

This change aligns with the PR objectives of using more efficient records. The test logic remains intact, still verifying the behavior of the brokenDirectoryFilter when processing nodes.

To ensure consistency across the codebase, let's verify that CryptoPathMapper.CiphertextDirectory is no longer used:

src/test/java/org/cryptomator/cryptofs/DirectoryIdBackupTest.java (3)

25-25: Approved: Type change aligns with PR objectives

The change from CryptoPathMapper.CiphertextDirectory to CipherDir aligns well with the PR objectives of refactoring to use more efficient records instead of classes. This modification should improve code efficiency without altering the test's behavior.


34-34: Approved: Consistent initialization with new type

The initialization of cipherDirObject using CipherDir is consistent with the type change on line 25. The constructor parameters remain unchanged, indicating that the CipherDir record maintains the same structure as the previous CiphertextDirectory class. This change preserves the test's integrity while aligning with the refactoring objectives.


Line range hint 1-70: Overall assessment: Changes maintain test integrity while improving code efficiency

The modifications in this file successfully transition from using CryptoPathMapper.CiphertextDirectory to CipherDir, which aligns with the PR's objective of refactoring to use more efficient records. The changes are consistent throughout the file and do not alter the behavior of the test methods. This refactoring should contribute to improved code efficiency without compromising the effectiveness of the tests.

src/main/java/org/cryptomator/cryptofs/DirectoryIdBackup.java (3)

29-31: Documentation updates accurately reflect code changes

The changes in the class documentation correctly align with the modifications in the code. The switch from CryptoPathMapper.CiphertextDirectory to CipherDir and the use of contentDirPath() method indicate an improvement in the API design, potentially offering better encapsulation or clarity.


47-47: Static method updates are consistent

The changes in the backupManually method, both in the documentation and method signature, are consistent with the previous modifications in the class. The use of CipherDir instead of CryptoPathMapper.CiphertextDirectory maintains consistency throughout the class.

Also applies to: 50-50


36-36: Method signature update is consistent

The change in the execute method signature from CryptoPathMapper.CiphertextDirectory to CipherDir is consistent with the documentation updates and reflects a refactoring to use a more specific type.

To ensure this change doesn't introduce any issues, please verify that all callers of this method have been updated accordingly. Run the following script to check for any remaining usages of CryptoPathMapper.CiphertextDirectory:

src/main/java/org/cryptomator/cryptofs/attr/AttributeProvider.java (1)

56-56: Approved: Enhanced directory path retrieval, but verification needed.

The change from path to contentDirPath() appears to align with the PR objectives of improving cache invalidation and path mapping. This modification likely provides a more specific path for directory contents, which could be crucial for proper cache management.

However, to ensure the change's correctness and impact:

  1. Could you provide more context on the contentDirPath() method? Specifically:

    • How does it differ from the previous path property?
    • Are there any scenarios where this change might alter existing behavior?
  2. Have all relevant tests been updated to account for this change?

To verify the impact and usage of this new method, please run the following script:

This will help ensure consistent usage across the codebase and identify any areas that might need updates.

✅ Verification successful

Verification Successful: All instances now use contentDirPath() and tests are updated accordingly.

The search confirmed that there are no remaining usages of .path in the getCiphertextDir context. All relevant components and tests have been updated to use contentDirPath(), ensuring consistency and alignment with the PR objectives.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for other usages of 'path' vs 'contentDirPath()' in ciphertext directory context

# Search for other occurrences of '.path' in similar context
echo "Searching for '.path' usages:"
rg --type java "getCiphertextDir\(.*\)\.path"

# Search for other occurrences of 'contentDirPath()' method
echo "Searching for 'contentDirPath()' usages:"
rg --type java "\.contentDirPath\(\)"

# Search for test cases related to directory path retrieval
echo "Searching for relevant test cases:"
rg --type java "test.*Directory.*Path"

Length of output: 6216

src/main/java/org/cryptomator/cryptofs/attr/AbstractCryptoFileAttributeView.java (1)

63-63: LGTM: Improved clarity in directory path retrieval

The change from path to contentDirPath() enhances the clarity of the code by explicitly indicating that we're dealing with the content directory path for ciphertext directories. This aligns well with the PR objectives of improving directory mapping and cache invalidation.

To ensure consistency across the codebase, let's verify the usage of contentDirPath():

This will help us confirm that the change has been applied consistently throughout the project.

✅ Verification successful

Verification Successful: Consistent Usage of contentDirPath()

All instances of getCiphertextDir() in the codebase consistently use the contentDirPath() method, ensuring clarity and reducing potential errors related to directory path retrieval.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent usage of contentDirPath() method
# Expected result: All occurrences should use contentDirPath() for directory content paths

# Search for usages of getCiphertextDir and contentDirPath
rg --type java -e "getCiphertextDir.*\.path" -e "getCiphertextDir.*\.contentDirPath"

Length of output: 1945

src/test/java/org/cryptomator/cryptofs/health/dirid/MissingContentDirTest.java (1)

3-3: LGTM: New import statement added correctly.

The import for CipherDir is necessary for the changes in the testFix method and is placed appropriately.

src/test/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactoryTest.java (3)

3-3: LGTM: Import statement added for CipherDir.

The addition of this import statement is consistent with the refactoring mentioned in the PR objectives, replacing CiphertextDirectory with the more efficient CipherDir record.


77-78: LGTM: CiphertextDirectory instances replaced with CipherDir.

These changes are in line with the PR objectives of refactoring to use more efficient records. The test logic remains unchanged, with only the return type of getCiphertextDir being updated in both cases.


Line range hint 1-124: Overall assessment: Changes are well-implemented and consistent.

The refactoring from CiphertextDirectory to CipherDir has been applied consistently throughout the test file. These changes align with the PR objectives of using more efficient records and do not alter the underlying test logic. The modifications improve code efficiency while maintaining the integrity of the tests.

src/test/java/org/cryptomator/cryptofs/attr/AttributeProviderTest.java (1)

11-11: LGTM: Import statement for CipherDir.

The import statement for CipherDir is correctly placed and necessary for the changes in the test class.

src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java (3)

158-158: Ensure vault initialization functionality is maintained

While the change from CryptoPathMapper.CiphertextDirectory to CipherDir is a localized refactoring, it's crucial to ensure that the overall functionality of the initialize method remains intact.

Please ensure that:

  1. The CipherDir class provides the same functionality as the previous CiphertextDirectory class.
  2. The vault initialization process is thoroughly tested with the new CipherDir class to verify that no regressions have been introduced.

Consider adding or updating unit tests for the initialize method to cover this change.


158-158: Clarify relationship with cache invalidation improvements

While this change is part of the refactoring effort, it's not immediately clear how it relates to the cache invalidation improvements mentioned in the PR objectives.

Could you please clarify:

  1. How does the introduction of CipherDir contribute to the cache invalidation improvements?
  2. Are there any properties or methods in the CipherDir record that facilitate better cache management?
  3. Where are the main cache invalidation improvements implemented in the codebase?

This information will help reviewers better understand the overall impact of the changes and ensure that all objectives of the PR are met.


158-158: LGTM: Refactoring to use CipherDir

The change from CryptoPathMapper.CiphertextDirectory to CipherDir aligns with the PR objectives of refactoring to use more efficient records instead of classes. This should improve performance and reduce memory usage.

To ensure consistency across the codebase:

  1. Verify that the CipherDir record is properly implemented and tested.
  2. Check for any other occurrences of CryptoPathMapper.CiphertextDirectory that might need to be updated.

You can use the following script to search for other occurrences:

src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java (6)

318-318: LGTM. Good improvements in code structure and consistency.

The changes in the createDirectory method are consistent with the previous modification, using contentDirPath() instead of path. The introduction of the ciphertextDir variable improves code readability and potentially reduces repeated method calls. The use of ciphertextDir.dirId() suggests improved encapsulation of directory-related functionality.

Also applies to: 325-329


434-434: LGTM. Consistent with previous changes.

The modification in the deleteDirectory method follows the same pattern as the previous changes, replacing path with contentDirPath(). This change maintains consistency across the class in how ciphertext directory paths are accessed.


507-507: LGTM. Maintains consistency with previous changes.

The modifications in the copyDirectory method continue the pattern of replacing path with contentDirPath(). This change ensures consistency across the class in how ciphertext directory paths are accessed and manipulated.

Also applies to: 517-518


624-624: LGTM. Consistent change, but verify overall impact.

The modification in the moveDirectory method follows the same pattern as the previous changes, replacing path with contentDirPath(). This change maintains consistency across the class in how ciphertext directory paths are accessed.

To ensure all necessary changes have been made and to verify the overall impact, please run the following script:

#!/bin/bash
# Check for any remaining instances of 'getCiphertextDir(...)' followed by '.path'
rg --type java "getCiphertextDir\([^)]+\)\.path" -A 3 -B 3

# Check all usages of 'contentDirPath()' to ensure consistent usage
rg --type java "contentDirPath\(\)" -A 3 -B 3

# Check for any potential missed updates in methods dealing with directories
rg --type java "(?i)(create|copy|move|delete).*directory" -A 10 -B 10

Line range hint 1-696: LGTM. Consistent refactoring, but ensure comprehensive testing.

The changes throughout this file consistently replace path with contentDirPath() when dealing with ciphertext directories. This refactoring appears to be part of a larger effort to improve the handling of ciphertext directory paths, potentially providing more specific access to directory contents.

While the changes are consistent and likely beneficial, they have the potential to significantly impact the behavior of the CryptoFileSystem. Therefore, it's crucial to ensure that these modifications are thoroughly tested, particularly focusing on:

  1. Directory creation, deletion, and manipulation operations
  2. Path resolution and traversal
  3. Interactions between cleartext and ciphertext paths
  4. Edge cases involving root directories and nested directory structures

To assist in verifying the changes, please run the following script:

#!/bin/bash
# List all test files related to CryptoFileSystem
fd -e java test.*CryptoFileSystem

# Check for existing tests that might need updating
rg --type java "(?i)test.*(directory|path|ciphertext)" -A 10 -B 10

# Look for any TODOs or FIXMEs that might be related to this refactoring
rg --type java "TODO|FIXME" -A 3 -B 3

Consider updating existing tests or adding new ones to cover the modified behavior, especially focusing on directory path handling and interactions between cleartext and ciphertext paths.


144-144: LGTM, but verify impact on directory path handling.

The change from getCiphertextDir(p).path to getCiphertextDir(p).contentDirPath() looks good and likely provides a more specific path to the directory's content. However, it's important to ensure this change doesn't unexpectedly alter the behavior of the getCiphertextPath method.

To verify the impact of this change, please run the following script:

✅ Verification successful

LGTM. Change consistently applied across codebase.

The modification from getCiphertextDir(p).path to getCiphertextDir(p).contentDirPath() has been consistently implemented throughout the codebase. This change appears to be part of a broader refactoring effort to improve directory path handling specificity. The widespread use of contentDirPath() in various components and test classes suggests that it's now an integral part of the system's directory management.

While the change seems correct and well-integrated, it may be worthwhile to review the test coverage specifically for this modification to ensure all edge cases are properly handled.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any other usages of 'path' that might need to be updated
rg --type java "getCiphertextDir\([^)]+\)\.path" -A 3 -B 3
# Check for existing usages of 'contentDirPath()'
rg --type java "contentDirPath\(\)" -A 3 -B 3

Length of output: 249758


Script:

#!/bin/bash
# Check for any remaining usages of 'path' that might need to be updated
rg --type java "getCiphertextDir\([^)]+\)\.path" -A 3 -B 3

# Check the implementation of contentDirPath()
rg --type java "contentDirPath\(\s*\)\s*\{" -A 10 -B 3

# Look for tests related to contentDirPath
rg --type java "test.*contentDirPath" -A 10 -B 3

Length of output: 168


Script:

#!/bin/bash
# Check if we can find Java files
rg --type-list | grep java

# Search for any Java files
rg --type java "" -l | head -n 5

# Look for any usage of getCiphertextDir
rg --type java "getCiphertextDir"

# Search for contentDirPath without specific formatting
rg --type java "contentDirPath"

# Check for any test files
rg --type java "class.*Test"

Length of output: 28967

src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java (14)

205-205: Update mock to use new CipherDir class

The change from CiphertextDirectory to CipherDir has been correctly implemented in this mock setup. This update ensures that the test is aligned with the new implementation.


581-581: Consistent use of CipherDir in mock setup

The mock setup for getCiphertextDir has been updated to use the new CipherDir class. This change maintains consistency with the updated implementation.


697-698: Multiple CipherDir instances correctly used in test setup

The test setup has been updated to use multiple CipherDir instances for different paths. This change accurately reflects the new implementation and ensures that the tests cover various scenarios with the new CipherDir class.


1158-1158: CipherDir usage in createDirectory test

The createDirectory test has been updated to use the new CipherDir class. This change ensures that the test accurately reflects the current implementation.


1171-1171: Consistent CipherDir usage in createDirectory test

Another instance of CipherDir usage in the createDirectory test, maintaining consistency with the updated implementation.


1193-1194: Multiple CipherDir instances in createDirectory test

The test setup uses multiple CipherDir instances for different paths, accurately reflecting the new implementation and ensuring comprehensive test coverage.


1223-1224: CipherDir usage in error handling test

The error handling test for createDirectory has been updated to use CipherDir instances. This change ensures that error scenarios are properly tested with the new implementation.


1260-1265: CipherDir object creation and usage in backup test

The test for directory ID backup has been updated to use a CipherDir object. This change ensures that the backup functionality is properly tested with the new implementation.


1327-1327: CipherDir usage in isHidden test

The isHidden test has been updated to use the new CipherDir class. This change maintains consistency with the updated implementation in the context of checking hidden file attributes.


1375-1375: CipherDir usage in checkAccess test

The checkAccess test has been updated to use the new CipherDir class. This change ensures that access checks are properly tested with the new implementation.


1557-1557: CipherDir usage in setAttribute test

The setAttribute test for regular directories has been updated to use the new CipherDir class. This change ensures that attribute setting is properly tested with the new implementation.


1572-1572: CipherDir usage in setAttribute test for non-existing root directory

The setAttribute test for non-existing root directories has been updated to use the new CipherDir class. This change ensures that edge cases are properly tested with the new implementation.


1590-1590: CipherDir usage in setAttribute test for files

The setAttribute test for files has been updated to use the new CipherDir class. This change ensures that attribute setting for files is properly tested with the new implementation.


Line range hint 1-1639: Overall assessment of CipherDir implementation in tests

The changes in this file consistently replace the usage of CiphertextDirectory with the new CipherDir class throughout the test suite. This update ensures that all relevant test cases are aligned with the new implementation. The modifications cover various scenarios, including directory creation, attribute setting, access checks, and error handling. These changes maintain the comprehensiveness and validity of the test suite, ensuring that the new CipherDir implementation is thoroughly tested across different use cases.

src/main/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactory.java (4)

3-3: Import statement updated to CipherDir

The import has been correctly updated to CipherDir, ensuring that the class references align with the new implementation.


39-39: Variable ciphertextDir now uses CipherDir

The variable ciphertextDir is now appropriately declared as a CipherDir, reflecting the updated class design.


40-40: Utilizing contentDirPath() method

The call to Files.newDirectoryStream now uses ciphertextDir.contentDirPath(), which is correct if CipherDir provides this method to access the directory path.


41-41: Accessing directory ID with dirId() method

Changed from ciphertextDir.dirId to ciphertextDir.dirId(), properly invoking the method to retrieve the directory ID.

src/test/java/org/cryptomator/cryptofs/ClearToCipherDirCacheTest.java (1)

110-114: Unexpected loader trigger for newClearPath in entryUntouchedOnPrefixFailure

In this test, cache.get(newClearPath, dirLoader); triggers the loader, but since clearPath does not start with oldPrefixPath, and no remapping should occur, it's unclear why newClearPath is involved.

Please verify whether newClearPath should be part of this test. If not, consider removing it to keep the test focused.

src/main/java/org/cryptomator/cryptofs/CryptoPathMapper.java (9)

44-45: Cache Initialization Updated to Reflect New Caching Strategy

The declaration of ciphertextNames and clearToCipherDirCache aligns with the new caching mechanism, enhancing the management of directory mappings.


47-47: Use of CipherDir for Root Directory

The rootDirectory now utilizes the CipherDir class, which is consistent with the refactored directory representation.


57-57: Proper Initialization in Constructor

The constructor correctly initializes clearToCipherDirCache and rootDirectory, ensuring that the new cache and directory structures are set up at instantiation.


66-66: Documentation Updated to Include IOException

The method assertNonExisting now accurately documents that it may throw an IOException, improving clarity for users of the API.


116-118: Updated Method Calls with CipherDir Integration

The use of CipherDir in getCiphertextFilePath enhances consistency with the new directory structure. The method correctly retrieves the parent directory's content path and directory ID.


132-133: Updated getCiphertextFileName Method to Use CipherNodeNameParameters

The method now properly generates the ciphertext file name using the CipherNodeNameParameters, which improves encapsulation and clarity of parameters.


153-162: ⚠️ Potential issue

Verify Exception Handling in Lambda Expression

The lambda cipherDirLoaderIfAbsent may throw an IOException when calling resolveDirectory(dirFile). Ensure that the functional interface CipherDirLoader allows throwing checked exceptions to prevent compilation errors.

Run the following script to confirm that CipherDirLoader is defined to throw IOException:

#!/bin/bash
# Description: Check if 'CipherDirLoader' interface allows throwing IOException.

# Search for the 'CipherDirLoader' interface definition.
rg --type java 'interface CipherDirLoader' -A 5

171-174: ⚠️ Potential issue

Ensure dirHash Has Sufficient Length to Prevent Index Out of Bounds

When using dirHash.substring(0, 2) and dirHash.substring(2), there is a risk of StringIndexOutOfBoundsException if dirHash is shorter than expected. Verify that hashDirectoryId always returns a string of adequate length.

Run the following script to test the length of dirHash outputs:

#!/bin/bash
# Description: Verify that 'hashDirectoryId' outputs strings of sufficient length.

# Replace 'SampleDirId' with a test directory ID.
echo "SampleDirId" | java -cp path/to/cryptolib CryptorClassName hashDirectoryId

# Expected result: The output string length is greater than or equal to 2.

Ensure that all possible inputs to hashDirectoryId produce a hash string of at least two characters.


Line range hint 122-130: Ensure Proper Implementation of equals() and hashCode() in CipherNodeNameParameters

Since CipherNodeNameParameters is used as a key in the ciphertextNames cache, it's crucial that it correctly overrides equals() and hashCode() methods to maintain cache integrity and prevent unintended behavior.

Run the following script to verify the implementation:

src/main/java/org/cryptomator/cryptofs/health/dirid/OrphanContentDir.java (4)

4-4: Necessary import of CipherDir added

The import statement for CipherDir is appropriate and required for the updated code.


Line range hint 158-174: Method prepareStepParent now returns CipherDir for enhanced type safety

Changing the return type of prepareStepParent from Path to CipherDir improves type safety and clarity in the codebase.


173-173: Correct instantiation of CipherDir

The CipherDir object stepParentCipherDir is properly instantiated with stepParentUUID and stepParentDir, reflecting the new return type.


219-234: Updated method signature for adoptOrphanedResource aligns with new types

The parameter stepParentDir in adoptOrphanedResource is now of type CipherDir, ensuring consistency with the rest of the code.

src/test/java/org/cryptomator/cryptofs/CryptoPathMapperTest.java (7)

78-79: Use of contentDirPath() ensures correct path retrieval

The update from .path to .contentDirPath() in testPathEncryptionForRoot aligns with the updated API, ensuring that the correct content directory path is retrieved.


102-103: Consistent application of contentDirPath() in path encryption tests

Updating to .contentDirPath() in testPathEncryptionForFoo maintains consistency with the API changes and ensures accurate path mappings in the test.


136-137: Correct use of contentDirPath() in nested path encryption

The change in testPathEncryptionForFooBar to use .contentDirPath() correctly reflects the updated method, verifying that nested path encryption operates as expected.


386-396: Effective test for cache invalidation on node removal

The testRemovedEntryMiss method correctly verifies that invalidating a node causes a cache miss on subsequent retrievals, ensuring the cache invalidation logic functions properly.


398-412: Validation of child cache invalidation upon parent removal

The testRemovedEntryChildMiss method appropriately checks that invalidating a parent node also invalidates all its child entries in the cache, which is crucial for cache consistency.


416-427: Accurate testing of cache behavior during node movement

The testMoveEntryOldMissNewHit method effectively confirms that moving a node results in a cache miss for the old path and a cache hit for the new path, validating the correctness of cache updates during move operations.


429-443: Ensuring child cache entries are invalidated when parent node is moved

The testMoveEntryOldChildMiss method successfully tests that moving a parent node invalidates the cache entries of its children at the old path, ensuring that the cache reflects the new state of the filesystem.

src/test/java/org/cryptomator/cryptofs/health/dirid/OrphanDirTest.java (1)

486-486: Ensure consistent use of test doubles

In line 486, CipherDir is instantiated with hard-coded values. Verify that mock objects or test doubles are consistently used where appropriate to isolate the unit tests and avoid dependencies on actual implementations.

Path dirPath = mock(Path.class, "dirAbc");
when(dirPath.getFileSystem()).thenReturn(fileSystem);
when(cryptoPathMapper.getCiphertextDir(path)).thenReturn(new CiphertextDirectory(dirId, dirPath));
when(cryptoPathMapper.getCiphertextDir(path)).thenReturn(new CipherDir(dirId, dirPath));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Remaining CiphertextDirectory Instances Detected

Several files still reference CiphertextDirectory, indicating incomplete refactoring. Please update the following to CipherDir:

  • src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java
  • src/main/java/org/cryptomator/cryptofs/dir/CiphertextDirectoryDeleter.java
  • src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java
  • src/test/java/org/cryptomator/cryptofs/DeleteNonEmptyCiphertextDirectoryIntegrationTest.java
  • Additional test files under src/test/java/org/cryptomator/cryptofs/
🔗 Analysis chain

LGTM: CiphertextDirectory replaced with CipherDir.

This change aligns with the PR objectives of refactoring to use more efficient records. The test logic remains intact, with only the return type of getCiphertextDir being updated.

To ensure consistency across the codebase, let's verify that all occurrences of CiphertextDirectory have been replaced with CipherDir:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining occurrences of CiphertextDirectory

# Test 1: Search for CiphertextDirectory in Java files
echo "Searching for CiphertextDirectory in Java files:"
rg --type java "CiphertextDirectory"

# Test 2: Verify CipherDir usage
echo "Verifying CipherDir usage:"
rg --type java "CipherDir"

Length of output: 17471

Copy link
Member

@overheadhunter overheadhunter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just some wishes regarding names.

import java.util.Objects;

//own file due to dagger
public record CipherDir(String dirId, Path contentDirPath) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • make package-private
  • rename contentDirPath to c9rDirPath for consistency?

Copy link
Member Author

@infeo infeo Oct 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'll revert it to path for a small diff and consistency.

c9rDirPath is, from my point of view, the most misleading, because the directory with the c9r suffix contains the dir.c9r file with the dir-id. For a cleartext directory /foo/bar there are always two directories on the cipher side:

  • the "linking" directory with the encrypted dir name, the c9r suffix and the dir-id as content
  • the "content" directory with a bas64-encoded hash as name and with all encrypted files and other "linking" directories

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this class cannot be package private. It is used in different packages, e.g. DirectoryStreamFactory or MissingContentDir.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Outside diff range and nitpick comments (15)
src/main/java/org/cryptomator/cryptofs/DirIdAndName.java (2)

5-11: Enhance documentation for better maintainability.

The current documentation could be improved in several ways:

  1. Expand on why this needs to be in its own file for Dagger (dependency injection)
  2. Document the parameter constraints (non-null requirements)
  3. Add examples of typical usage

Consider updating the documentation like this:

-//own file due to dagger
+// This class is in its own file to support Dagger's dependency injection requirements
+// and to maintain a clean separation of concerns in the codebase.
 
 /**
  * Helper object to store the dir id of a directory along with its cleartext name (aka, the last element in the cleartext path)
- * @param dirId
- * @param clearNodeName
+ * @param dirId The unique identifier of the directory (must be non-null)
+ * @param clearNodeName The unencrypted name of the directory as it appears in the virtual filesystem (must be non-null)
+ * 
+ * @see CiphertextDirectory
+ * @see CiphertextDirCache
  */

1-19: Document the record's role in the caching architecture.

This record is part of a larger caching mechanism refactoring. Consider adding class-level documentation that explains:

  1. Its relationship with CiphertextDirectory and CiphertextDirCache
  2. Its role in the cache invalidation process
  3. Performance characteristics and usage patterns

This will help maintainers understand the design decisions and ensure proper usage of the cache invalidation system.

src/main/java/org/cryptomator/cryptofs/CiphertextDirectory.java (1)

6-13: Enhance documentation clarity and completeness.

The documentation could be improved in the following ways:

  1. Expand the comment about Dagger to explain why this needs to be in its own file
  2. Add more context about this record's role in the caching system
  3. Consider adding @since tag to track when this was introduced
  4. Add @see tags to reference related classes like CryptoPathMapper and CiphertextDirCache

Example enhancement:

-//own file due to dagger
+// Separated into its own file to support Dagger's dependency injection requirements
+// and to maintain clean component boundaries
 
 /**
  * Represents a ciphertext directory without it's mount point in the virtual filesystem.
+ * This record is used by the caching system to maintain mappings between cleartext and
+ * ciphertext paths, improving performance by reducing I/O operations.
  *
  * @param dirId The (ciphertext) dir id (not encrypted, just a uuid)
  * @param path The path to content directory (which contains the actual encrypted files and links to subdirectories)
+ * @since 1.x.x
+ * @see org.cryptomator.cryptofs.CryptoPathMapper
+ * @see org.cryptomator.cryptofs.CiphertextDirCache
  */
src/main/java/org/cryptomator/cryptofs/DirectoryIdBackup.java (1)

47-47: Enhance parameter documentation for clarity.

While the documentation is technically correct, it could be more informative by explaining what a dirId is and why it needs to be backed up.

Consider expanding the documentation like this:

-@param ciphertextDirectory A {@link CiphertextDirectory} for which the dirId should be back up'd.
+@param ciphertextDirectory A {@link CiphertextDirectory} containing the directory identifier that needs to be backed up for recovery purposes in case of filesystem corruption or metadata loss.
src/main/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactory.java (2)

Line range hint 35-44: Consider documenting the thread-safety guarantees

Given that this class is part of the directory mapping system that's being refactored for better cache invalidation, it would be valuable to:

  1. Document the thread-safety guarantees of the new factory-based approach
  2. Add a comment explaining how this class interacts with the new caching mechanism
  3. Consider adding unit tests specifically for concurrent access patterns

This documentation would help maintain the correctness of the cache invalidation system as the codebase evolves.


Synchronization should be maintained for thread safety

The synchronization is still necessary as the class manages shared mutable state:

  • A non-thread-safe HashMap storing stream mappings
  • A volatile boolean closed flag that's checked and modified
  • Concurrent access to these fields during newDirectoryStream() and close() operations needs synchronization to maintain thread safety and prevent race conditions

The thread-safe factory mentioned in the TODO only ensures thread-safe creation of components but doesn't protect the shared state within DirectoryStreamFactory itself. Removing synchronization could lead to:

  • Race conditions when adding/removing streams from the map
  • Inconsistencies between the closed flag and stream operations
  • Potential resource leaks if streams aren't properly tracked
🔗 Analysis chain

Line range hint 35-35: Verify thread safety before removing synchronization

While the TODO suggests that synchronization might be unnecessary due to the thread-safe factory, we should carefully verify this assumption. The newDirectoryStream method manages shared state through the streams map and closed flag.

Let's verify the thread safety of the factory:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the factory implementation and its thread safety guarantees
ast-grep --pattern 'class $_ implements DirectoryStreamComponent.Factory {
  $$$
}'

# Look for any concurrent access patterns
rg -A 5 'synchronized|volatile|concurrent|atomic' --type java

Length of output: 1548


Script:

#!/bin/bash
# Let's search for the DirectoryStreamFactory class implementation to understand its thread safety
rg -A 20 "class DirectoryStreamFactory" --type java

# Look for any concurrent collections or synchronization in the factory
rg -A 10 "private.*Map|private.*Set|private.*List" --type java src/main/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactory.java

# Search for any concurrent access patterns in the factory and related classes
rg -A 5 "synchronized|volatile|concurrent|atomic" --type java src/main/java/org/cryptomator/cryptofs/dir/

Length of output: 8560

src/test/java/org/cryptomator/cryptofs/CiphertextDirCacheTest.java (3)

19-26: Consider adding more descriptive mock setup.

While the setup is functional, consider making it more maintainable by:

  1. Using meaningful names in mock setup (e.g., mockClearPath, mockDirLoader).
  2. Adding comments to explain the expected behavior of mocks.
 @BeforeEach
 public void beforeEach() throws IOException {
     cache = new CiphertextDirCache();
-    clearPath = Mockito.mock(CryptoPath.class);
-    dirLoader = Mockito.mock(CiphertextDirCache.CipherDirLoader.class);
-    var cipherDir = Mockito.mock(CiphertextDirectory.class);
+    // Mock the cleartext path that will be used as cache key
+    clearPath = Mockito.mock(CryptoPath.class, "mockClearPath");
+    // Mock the loader that provides ciphertext directories
+    dirLoader = Mockito.mock(CiphertextDirCache.CipherDirLoader.class, "mockDirLoader");
+    // Mock a default ciphertext directory for the loader to return
+    var cipherDir = Mockito.mock(CiphertextDirectory.class, "mockCipherDir");
     Mockito.when(dirLoader.load()).thenReturn(cipherDir);
 }

28-47: Enhance test readability and coverage.

Consider these improvements:

  1. Use more descriptive test method names following the given/when/then pattern.
  2. Add assertions to verify the cache state more thoroughly.
 @Test
-public void testPuttingNewEntryTriggersLoader() throws IOException {
+public void givenEmptyCache_whenGettingNewEntry_thenLoaderIsTriggeredOnce() throws IOException {
     var cipherDir = Mockito.mock(CiphertextDirectory.class);
     Mockito.when(dirLoader.load()).thenReturn(cipherDir);

     var result = cache.get(clearPath, dirLoader);
     Assertions.assertEquals(cipherDir, result);
     Mockito.verify(dirLoader).load();
+    Mockito.verifyNoMoreInteractions(dirLoader);
 }

 @Test
-public void testPuttingKnownEntryDoesNotTriggerLoader() throws IOException {
+public void givenCachedEntry_whenGettingSameEntry_thenLoaderIsNotTriggered() throws IOException {

78-117: Improve documentation of complex mock setup.

The remap tests are thorough, but the mock setup could be more self-documenting:

 @BeforeEach
 public void beforeEach() throws IOException {
     newClearPath = Mockito.mock(CryptoPath.class);
     oldPrefixPath = Mockito.mock(CryptoPath.class);
     newPrefixPath = Mockito.mock(CryptoPath.class);
+    // Setup path resolution behavior for remapping
+    // When old prefix is removed from a path, return the relative portion
     Mockito.when(oldPrefixPath.relativize(Mockito.any(Path.class))).thenReturn(oldPrefixPath);
+    // When new prefix is added to a path, return the complete new path
     Mockito.when(newPrefixPath.resolve(Mockito.any(Path.class))).thenReturn(newClearPath);
 }

Also, consider adding more descriptive comments in the test methods to explain the expected behavior:

 cache.get(clearPath, dirLoader); //triggers loader
 cache.recomputeAllKeysWithPrefix(oldPrefixPath, newPrefixPath);
-cache.get(clearPath, dirLoader); //does trigger
-cache.get(newClearPath, dirLoader); //does not trigger
+cache.get(clearPath, dirLoader); //triggers loader again as old path is invalidated
+cache.get(newClearPath, dirLoader); //doesn't trigger as it was remapped from old path
src/test/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactoryTest.java (1)

Line range hint 64-64: Update CiphertextDirectory usage to CipherDir.

These instances of CiphertextDirectory need to be updated to use CipherDir to maintain consistency with the refactoring changes identified in previous reviews.

Apply this change to all instances:

-new CiphertextDirectory(dirId, dirPath)
+new CipherDir(dirId, dirPath)

Also applies to: 89-90

src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java (1)

Line range hint 1-700: Consider adding documentation for the path() method.

While updating the access pattern from property to method is good, it would be helpful to document why this change was made and what guarantees the path() method provides (e.g., immutability, caching behavior, etc.).

src/test/java/org/cryptomator/cryptofs/health/dirid/OrphanDirTest.java (1)

329-330: LGTM: Consistent refactoring to use the new CiphertextDirectory record.

The changes correctly replace the nested class with the new record class across all test methods. The usage of path() method is consistent and appropriate.

Consider extracting the repeated CiphertextDirectory setup into a helper method to reduce duplication:

private CiphertextDirectory createStepParentDir() {
    CiphertextDirectory dir = new CiphertextDirectory("aaaaaa", dataDir.resolve("22/2222"));
    Files.createDirectories(dir.path());
    return dir;
}

This would help maintain DRY principles and make the tests more maintainable.

Also applies to: 350-351, 378-379, 412-412, 445-445, 484-484, 527-528

src/main/java/org/cryptomator/cryptofs/CiphertextDirCache.java (2)

16-17: Review Cache Configuration Parameters

The cache is configured with a maximumSize of 5000 entries and an expireAfterWrite duration of 20 seconds.

Consider reviewing these parameters to ensure they align with the expected usage patterns and performance requirements. Adjusting the cache size or expiration policy may optimize memory usage and system performance.


11-13: Improve Class Documentation for Clarity

The class-level Javadoc could be enhanced to provide a clearer description of the CiphertextDirCache's purpose and usage.

Consider rephrasing the documentation:

/**
 * Caches the mapping between cleartext directory paths and their corresponding ciphertext directories in the content directory.
 */
src/main/java/org/cryptomator/cryptofs/CryptoPathMapper.java (1)

146-148: Typo in JavaDoc Comment

In the JavaDoc comment for @param cleartextDst, the word "childs" should be corrected to "children" for proper grammar.

Apply this diff to fix the typo:

 	 * @param cleartextDst the destination cleartext path. The path itself and all
-	 * childs will be adjusted to start with cleartextDst.
+	 * children will be adjusted to start with cleartextDst.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 0cd494c and ec01bb6.

📒 Files selected for processing (24)
  • src/main/java/org/cryptomator/cryptofs/CiphertextDirCache.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/CiphertextDirectory.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java (6 hunks)
  • src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/CryptoPathMapper.java (5 hunks)
  • src/main/java/org/cryptomator/cryptofs/DirIdAndName.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/DirectoryIdBackup.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/attr/AbstractCryptoFileAttributeView.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/attr/AttributeProvider.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/dir/BrokenDirectoryFilter.java (1 hunks)
  • src/main/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactory.java (2 hunks)
  • src/main/java/org/cryptomator/cryptofs/health/dirid/MissingContentDir.java (2 hunks)
  • src/main/java/org/cryptomator/cryptofs/health/dirid/MissingDirIdBackup.java (2 hunks)
  • src/main/java/org/cryptomator/cryptofs/health/dirid/OrphanContentDir.java (5 hunks)
  • src/test/java/org/cryptomator/cryptofs/CiphertextDirCacheTest.java (1 hunks)
  • src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java (2 hunks)
  • src/test/java/org/cryptomator/cryptofs/CryptoPathMapperTest.java (5 hunks)
  • src/test/java/org/cryptomator/cryptofs/DirectoryIdBackupTest.java (4 hunks)
  • src/test/java/org/cryptomator/cryptofs/attr/AttributeProviderTest.java (1 hunks)
  • src/test/java/org/cryptomator/cryptofs/dir/BrokenDirectoryFilterTest.java (3 hunks)
  • src/test/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactoryTest.java (1 hunks)
  • src/test/java/org/cryptomator/cryptofs/health/dirid/MissingContentDirTest.java (2 hunks)
  • src/test/java/org/cryptomator/cryptofs/health/dirid/MissingDirIdBackupTest.java (2 hunks)
  • src/test/java/org/cryptomator/cryptofs/health/dirid/OrphanDirTest.java (11 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/test/java/org/cryptomator/cryptofs/attr/AttributeProviderTest.java
🧰 Additional context used
📓 Learnings (2)
src/main/java/org/cryptomator/cryptofs/CiphertextDirCache.java (1)
Learnt from: infeo
PR: cryptomator/cryptofs#253
File: src/main/java/org/cryptomator/cryptofs/ClearToCipherDirCache.java:59-68
Timestamp: 2024-10-22T08:36:41.464Z
Learning: In the `ClearToCipherDirCache` class, recursive writes to the cache are not allowed because the `CipherDirLoader` might alter the cache by recursive computation. Therefore, computation is performed outside of the atomic context, as suggested in the Caffeine wiki.
src/test/java/org/cryptomator/cryptofs/CiphertextDirCacheTest.java (2)
Learnt from: infeo
PR: cryptomator/cryptofs#253
File: src/test/java/org/cryptomator/cryptofs/ClearToCipherDirCacheTest.java:55-63
Timestamp: 2024-10-21T15:52:40.091Z
Learning: In the Java test file `ClearToCipherDirCacheTest.java`, within the `entryRemovedOnPrefixSuccess` test method, the cache miss is confirmed by verifying that the loader is triggered again, so adding an assertion to check the cache state is unnecessary.
Learnt from: infeo
PR: cryptomator/cryptofs#253
File: src/test/java/org/cryptomator/cryptofs/ClearToCipherDirCacheTest.java:115-115
Timestamp: 2024-10-21T15:56:44.806Z
Learning: In the Java test file `ClearToCipherDirCacheTest.java`, within the `entryUntouchedOnPrefixFailure` test method, the `dirLoader.load()` method is expected to be called twice, corresponding to invocations at lines 110 and 113.
🔇 Additional comments (51)
src/main/java/org/cryptomator/cryptofs/dir/BrokenDirectoryFilter.java (2)

31-31: LGTM! Clean transition to record accessor.

The change from .path to .path() aligns with the PR's objective to utilize records instead of classes, following Java best practices for immutable data structures.


31-31: Verify consistent usage of path() accessor across the codebase.

Let's ensure this refactoring has been consistently applied across all usages of CiphertextDirectory.

src/main/java/org/cryptomator/cryptofs/health/dirid/MissingDirIdBackup.java (2)

3-3: LGTM! Good dependency reduction.

The removal of the CryptoPathMapper dependency in favor of directly importing CiphertextDirectory helps reduce coupling and aligns with the PR's objective of using more efficient record types.


32-32: LGTM! Clean refactoring to use the new CiphertextDirectory record.

The change simplifies the code by directly using the standalone CiphertextDirectory record while maintaining the same functionality. This refactoring improves testability and code organization.

src/main/java/org/cryptomator/cryptofs/health/dirid/MissingContentDir.java (2)

3-3: LGTM: Clean import refactoring

The direct import of CiphertextDirectory aligns with the PR's goal of streamlining the codebase and removing nested class dependencies.


54-54: LGTM: Verify consistent CiphertextDirectory usage

The direct instantiation of CiphertextDirectory is correct and maintains the same functionality while following the new pattern.

Let's verify that this refactoring pattern is consistently applied across the codebase:

✅ Verification successful

Let me check the actual usage patterns in these files to verify consistency.


Based on the comprehensive code analysis, I can now provide the final response:

Verified: CiphertextDirectory usage is consistent

The direct instantiation of CiphertextDirectory in MissingContentDir.java follows the established pattern. All usages across the codebase consistently use direct instantiation of CiphertextDirectory as a record class, and there are no remaining references to CryptoPathMapper.CiphertextDirectory. Key evidence:

  • CiphertextDirectory is properly defined as a record in its own file
  • All instantiations use the direct constructor pattern new CiphertextDirectory(dirId, path)
  • The class is used consistently across main and test code
  • No deprecated or legacy patterns found
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify consistent usage of CiphertextDirectory across the codebase
# Expected: All instances should use the direct class reference, not through CryptoPathMapper

# Check for any remaining references to CryptoPathMapper.CiphertextDirectory
rg -l "CryptoPathMapper\.CiphertextDirectory"

# Check for direct usage of CiphertextDirectory
rg -l "new CiphertextDirectory\("

Length of output: 864


Script:

#!/bin/bash
# Get the actual context of CiphertextDirectory usage
rg "CiphertextDirectory" -B2 -A2

# Specifically check CryptoPathMapper.java since it's the original source
cat src/main/java/org/cryptomator/cryptofs/CryptoPathMapper.java

Length of output: 61478

src/test/java/org/cryptomator/cryptofs/health/dirid/MissingDirIdBackupTest.java (2)

3-3: LGTM! Import change aligns with refactoring goals.

The direct import of CiphertextDirectory instead of accessing it through CryptoPathMapper reduces coupling and aligns with the PR's objective of utilizing more efficient records.


46-46: LGTM! Consider adding edge cases to the test suite.

The updated matcher correctly uses the record's methods and maintains the test's verification logic. However, since this test is crucial for vault integrity, consider adding test cases for:

  • Paths with special characters
  • Very long directory IDs
  • Invalid directory IDs

Let's verify if these edge cases are covered elsewhere:

src/test/java/org/cryptomator/cryptofs/dir/BrokenDirectoryFilterTest.java (2)

3-3: LGTM: Import aligns with the new standalone CiphertextDirectory record.

The import change correctly reflects the architectural change from an inner class to a standalone record.


35-35: LGTM: Consistent usage of the new CiphertextDirectory record.

The mock setup has been correctly updated to use the new standalone CiphertextDirectory record in both test methods. The behavior and test coverage remain unchanged.

Let's verify that all other test classes have been updated consistently:

Also applies to: 49-49

✅ Verification successful

All test classes consistently use the standalone CiphertextDirectory record

The verification shows that:

  1. No references to the old CryptoPathMapper.CiphertextDirectory were found
  2. All test classes consistently use the new standalone CiphertextDirectory record with the correct constructor signature (dirId, path)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to CryptoPathMapper.CiphertextDirectory
# Expected: No matches, as all references should use the new standalone CiphertextDirectory

# Search for any remaining old-style references
rg -l "CryptoPathMapper\.CiphertextDirectory"

# Search for test files using CiphertextDirectory to verify consistent usage
rg "new CiphertextDirectory\(" "src/test/"

Length of output: 6119

src/test/java/org/cryptomator/cryptofs/DirectoryIdBackupTest.java (4)

25-25: LGTM! Improved type and variable naming.

The change from nested CryptoPathMapper.CiphertextDirectory to standalone CiphertextDirectory aligns with good architectural practices by reducing coupling. The new variable name is also more descriptive.


34-34: LGTM! Initialization properly updated.

The initialization correctly uses the new CiphertextDirectory constructor while maintaining the same test setup logic.


47-47: LGTM! Test methods properly updated.

The test methods have been consistently updated to use the renamed variable while maintaining their original test coverage and assertions.

Also applies to: 61-61


Line range hint 1-67: Verify test coverage for new caching behavior.

While these tests cover the directory ID backup functionality well, we should ensure there are comprehensive tests for the new cache invalidation behavior mentioned in the PR objectives, especially given the complexity change from O(log n) to O(n).

Let's check for related test files:

✅ Verification successful

Let's examine the content of these test files to understand the cache invalidation test coverage.


Let's search for any new cache-related tests that might have been added as part of this change, and also look for the implementation to understand what kind of tests we should expect.


Cache complexity changes are well tested and documented

The codebase shows comprehensive test coverage for the new caching behavior:

  • ClearToCipherDirCacheTest.java contains dedicated unit tests for the new cache implementation
  • The O(n) complexity is explicitly documented in the code comments with worst-case scenarios:
    • removeAllKeysWithPrefix: O(n*(x+y)) for recursive directory deletion
    • recomputeAllKeysWithPrefix: O(nx + ny + n*z) for moving deeply nested directories
  • Test cases cover both cache invalidation and remapping scenarios with nested directory structures
  • Performance implications are clearly documented with examples like "moving the root of a deeply nested dir, where all child dirs are cached"

The original review comment's concern about test coverage has been addressed by the recent commits which added comprehensive tests for the new caching behavior.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for cache-related test files
echo "Searching for cache-related test files..."
fd -e java -e kt test.*Cache.*Test

# Search for test methods related to cache invalidation
echo -e "\nSearching for cache invalidation test methods..."
rg -l "test.*[Cc]ache.*[Ii]nvalidat|test.*[Ii]nvalidat.*[Cc]ache" -g "*Test.{java,kt}"

Length of output: 471


Script:

#!/bin/bash
# Check cache invalidation test methods in OpenCryptoFileTest
echo "=== Cache invalidation tests in OpenCryptoFileTest ==="
rg "test.*[Cc]ache.*[Ii]nvalidat|test.*[Ii]nvalidat.*[Cc]ache" -A 10 src/test/java/org/cryptomator/cryptofs/fh/OpenCryptoFileTest.java

echo -e "\n=== Cache invalidation tests in CleartextFileChannelTest ==="
rg "test.*[Cc]ache.*[Ii]nvalidat|test.*[Ii]nvalidat.*[Cc]ache" -A 10 src/test/java/org/cryptomator/cryptofs/ch/CleartextFileChannelTest.java

# Look for cache complexity related tests
echo -e "\n=== Tests mentioning cache complexity or performance ==="
rg "test.*[Cc]ache.*([Cc]omplex|[Pp]erform)" -A 10 -g "*Test.java"

Length of output: 1586


Script:

#!/bin/bash
# Find cache-related implementation files
echo "=== Cache implementation files ==="
fd -e java -e kt Cache

# Look for recent changes in cache-related files
echo -e "\n=== Cache-related changes in recent commits ==="
git log -p --grep="cache\|Cache" -i --max-count=3

# Search for O(n) complexity mentions
echo -e "\n=== O(n) complexity mentions ==="
rg "O\(n\)" -A 5 -B 5

Length of output: 29954

src/main/java/org/cryptomator/cryptofs/DirectoryIdBackup.java (4)

29-31: LGTM! Documentation updates are clear and accurate.

The Javadoc changes properly reflect the refactored CiphertextDirectory reference and maintain clear documentation of the backup file naming convention.


36-39: Verify the atomic file creation security.

The use of StandardOpenOption.CREATE_NEW helps prevent race conditions by failing if the file already exists. However, ensure that the parent directory's permissions are properly set to prevent unauthorized access during the small window between existence check and file creation.

Consider adding filesystem permission checks before attempting to create the backup file to ensure the operation will be secure.


50-50: LGTM! Method signature update is appropriate.

The static helper method is well-designed, properly documented, and maintains good separation of concerns by delegating to the instance method.


36-50: Verify integration with cache invalidation system.

Since this PR aims to fix cache invalidation issues, we should verify that directory ID backups are properly handled when directories are moved or deleted.

✅ Verification successful

Based on the search results, I can see that the DirectoryIdBackup class is used to backup directory IDs during filesystem operations, and there is extensive integration with the cache invalidation system. The key findings are:

  1. Directory ID backups are properly handled during:

    • Directory deletion (via CryptoFileSystemImpl.deleteDirectory())
    • Directory moves (via CryptoFileSystemImpl.moveDirectory())
    • Directory creation (with error handling that cleans up the backup file)
  2. The cache invalidation system is properly integrated:

    • cryptoPathMapper.invalidatePathMapping() is called after directory operations
    • dirIdProvider.delete() and dirIdProvider.move() are called to handle cache updates
    • The backup file is cleaned up if directory creation fails
  3. The error handling in DirectoryIdBackup.execute() uses CREATE_NEW to prevent overwriting existing backups, which is the correct approach for maintaining filesystem integrity.

Therefore, I can conclude that the DirectoryIdBackup class is properly integrated with the cache invalidation system and has appropriate error handling.

✓ Directory ID backup system is properly integrated with cache invalidation

The implementation ensures directory ID backups are maintained consistently with the cache state through proper invalidation and cleanup during all directory operations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for cache invalidation calls around directory operations
rg -A 5 -B 5 "invalidate|remove|delete|move" --type java

# Look for any error handling related to backup file operations
ast-grep --pattern 'try {
  $$$
  DirectoryIdBackup
  $$$
} catch ($$$) {
  $$$
}'

Length of output: 278083

src/main/java/org/cryptomator/cryptofs/attr/AttributeProvider.java (1)

56-56: LGTM! Good transition to record accessor.

The change from property access to method call aligns with Java records best practices and maintains better encapsulation.

Let's verify that similar changes have been made consistently across the codebase:

✅ Verification successful

All path() method calls are consistent across the codebase

The verification shows that all instances of getCiphertextDir() in the codebase consistently use the record accessor method path(). No instances of direct property access were found, confirming that the migration to record accessors has been completed thoroughly.

Found in:

  • CryptoFileSystemImpl.java (7 occurrences)
  • AttributeProvider.java (1 occurrence)
  • AbstractCryptoFileAttributeView.java (1 occurrence)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining direct property access to 'path'
# that might need to be updated to use the method call 'path()'

# Search for remaining direct property access patterns
echo "Checking for potential inconsistent path property access:"
rg -p "getCiphertextDir\([^)]+\)\.path[^(]"

# Search for the new method call pattern to confirm consistency
echo "Checking current path() method call usage:"
rg -p "getCiphertextDir\([^)]+\)\.path\(\)"

Length of output: 1275

src/main/java/org/cryptomator/cryptofs/attr/AbstractCryptoFileAttributeView.java (1)

63-63: LGTM! Consistent with record accessor pattern.

The change from field access .path to method call .path() aligns with Java records best practices and improves encapsulation.

Let's verify the consistent usage of this pattern across the codebase:

✅ Verification successful

All usages of getCiphertextDir().path() are consistent across the codebase

The verification shows that all instances of accessing the path from getCiphertextDir() use the correct record accessor pattern .path(). No instances of direct field access were found. The pattern is consistently used across:

  • CryptoFileSystemImpl.java
  • AbstractCryptoFileAttributeView.java
  • AttributeProvider.java
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify consistent usage of CiphertextDirectory.path() across the codebase
# Expect: All accesses should use .path() method call, not direct field access

# Search for any remaining direct field access patterns
rg "getCiphertextDir\([^)]+\)\.path[^()]"

# Search for the correct method call pattern for comparison
rg "getCiphertextDir\([^)]+\)\.path\(\)"

Length of output: 1403

src/main/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactory.java (2)

3-3: LGTM: Import change aligns with architectural improvements

The direct import of CiphertextDirectory from the base package aligns with the PR's objective of using records for better efficiency.


40-41: Verify consistent usage of method calls across codebase

The change from field access to method calls (.path() and .dirId()) aligns with the record-based approach. However, we should ensure this pattern is consistently applied throughout the codebase.

Let's verify the consistent usage:

#!/bin/bash
# Search for any remaining field access patterns that should be method calls
rg -A 2 'ciphertextDir\.(path|dirId)[^(]'

# Verify the new method call pattern is used consistently
rg -A 2 'ciphertextDir\.(path|dirId)\(\)'
src/test/java/org/cryptomator/cryptofs/health/dirid/MissingContentDirTest.java (2)

3-3: LGTM! Import change aligns with architectural improvements.

The change from nested CryptoPathMapper.CiphertextDirectory to standalone CiphertextDirectory aligns with the PR's objective of utilizing more efficient records instead of classes.


63-63: Verify consistent usage of CiphertextDirectory across tests.

The ArgumentMatcher update correctly uses the new record-style accessor methods. Let's verify this pattern is consistently applied across the test suite.

✅ Verification successful

CiphertextDirectory usage is consistent across test suite

Based on the comprehensive search results, all test cases consistently use the record-style accessor methods dirId() and path() when working with CiphertextDirectory. The ArgumentMatcher implementations in MissingContentDirTest and MissingDirIdBackupTest confirm this pattern. No instances of deprecated or inconsistent access patterns were found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining usage of the old CryptoPathMapper.CiphertextDirectory pattern
# or inconsistent property access patterns in tests

# Test 1: Check for any remaining nested CiphertextDirectory references
echo "Checking for old nested CiphertextDirectory references..."
rg "CryptoPathMapper\.CiphertextDirectory" "src/test"

# Test 2: Look for potential inconsistent property access patterns
echo "Checking for inconsistent property access patterns..."
rg -A 2 "CiphertextDirectory.*\{.*\}" "src/test"

Length of output: 344


Script:

#!/bin/bash
# Let's try a different approach to find CiphertextDirectory usage patterns

# Search for CiphertextDirectory usage in test files
echo "Searching for CiphertextDirectory usage patterns..."
rg "CiphertextDirectory" "src/test" -B 1 -A 1

# Search specifically for dirId() method calls
echo "Searching for dirId() method calls..."
rg "\.dirId\(\)" "src/test" -B 1 -A 1

# Search for path() method calls in context
echo "Searching for path() method calls..."
rg "\.path\(\)" "src/test" -B 1 -A 1

# Look for ArgumentMatcher usage with CiphertextDirectory
echo "Searching for ArgumentMatcher with CiphertextDirectory..."
rg "ArgumentMatcher.*CiphertextDirectory" "src/test" -B 1 -A 1

Length of output: 31721

src/test/java/org/cryptomator/cryptofs/CiphertextDirCacheTest.java (2)

1-17: LGTM! Well-structured test class setup.

The imports and class structure follow testing best practices, with appropriate use of JUnit 5 and Mockito frameworks.


49-75: LGTM! Comprehensive removal test cases.

The tests effectively verify the cache invalidation behavior, which is a key objective of this PR. The verification of loader calls is sufficient to confirm cache state, as per previous feedback.

Let's verify that all cache invalidation scenarios are covered:

✅ Verification successful

Based on the results from our verification scripts, I can now provide the final response:

Cache invalidation tests are complete and well-structured

The cache invalidation implementation is properly tested with:

  • Successful prefix removal case (triggers cache reload)
  • Failed prefix match case (preserves cache entry)
  • Integration with CryptoPathMapper for path-based invalidation

The implementation in CiphertextDirCache uses a simple and effective approach with removeIf() on the cache keys, which is correctly verified by these test cases.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any other cache invalidation scenarios in the codebase
rg -l "removeAllKeysWithPrefix|invalidateCache|clearCache" --type java

Length of output: 263


Script:

#!/bin/bash
# Let's check the implementation and usage of cache invalidation methods
rg "removeAllKeysWithPrefix|invalidateCache|clearCache" --type java -A 5

Length of output: 2428


Script:

#!/bin/bash
# Let's check if there are any other test classes that might be testing cache-related functionality
ast-grep --pattern 'class $_ {
  $$$
  @Test
  $$$
  cache.$_($$$)
  $$$
}'

Length of output: 77

src/test/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactoryTest.java (1)

3-3: ⚠️ Potential issue

Import statement updated but class name needs revision.

The import has been correctly moved to use the standalone CiphertextDirectory class. However, based on previous review comments, this class should be renamed to CipherDir for consistency with the ongoing refactoring.

src/main/java/org/cryptomator/cryptofs/health/dirid/OrphanContentDir.java (2)

Line range hint 157-172: LGTM! Good improvements in error handling.

The changes improve robustness by properly handling backup file creation failures and using the new CiphertextDirectory record type.


218-232: ⚠️ Potential issue

Consider improving error handling and thread safety.

The method performs multiple file operations that could fail or be affected by concurrent access:

  1. The Files.move operations could fail if the target already exists
  2. The file channel operations could fail due to I/O errors
  3. Concurrent access to the same files could cause race conditions

Let's check if there are any concurrent accesses to these paths:

Consider wrapping the operations in a try-catch block and implementing proper synchronization:

 void adoptOrphanedResource(Path oldCipherPath, String newClearName, boolean isShortened, CiphertextDirectory stepParentDir, FileNameCryptor cryptor, MessageDigest sha1) throws IOException {
     var newCipherName = encrypt(cryptor, newClearName, stepParentDir.dirId());
     if (isShortened) {
         var deflatedName = BaseEncoding.base64Url().encode(sha1.digest(newCipherName.getBytes(StandardCharsets.UTF_8))) + Constants.DEFLATED_FILE_SUFFIX;
         Path targetPath = stepParentDir.path().resolve(deflatedName);
-        Files.move(oldCipherPath, targetPath);
+        try {
+            Files.move(oldCipherPath, targetPath, StandardCopyOption.ATOMIC_MOVE);
+        } catch (FileAlreadyExistsException e) {
+            LOG.warn("Target file already exists: {}", targetPath, e);
+            throw e;
+        }

         //adjust name.c9s
-        try (var fc = Files.newByteChannel(targetPath.resolve(Constants.INFLATED_FILE_NAME), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
-            fc.write(ByteBuffer.wrap(newCipherName.getBytes(StandardCharsets.UTF_8)));
+        Path inflatedNameFile = targetPath.resolve(Constants.INFLATED_FILE_NAME);
+        try {
+            synchronized (this) {
+                try (var fc = Files.newByteChannel(inflatedNameFile, 
+                        StandardOpenOption.WRITE, 
+                        StandardOpenOption.CREATE, 
+                        StandardOpenOption.TRUNCATE_EXISTING)) {
+                    fc.write(ByteBuffer.wrap(newCipherName.getBytes(StandardCharsets.UTF_8)));
+                }
+            }
+        } catch (IOException e) {
+            LOG.error("Failed to write inflated filename: {}", inflatedNameFile, e);
+            throw e;
         }
     } else {
         Path targetPath = stepParentDir.path().resolve(newCipherName);
-        Files.move(oldCipherPath, targetPath);
+        try {
+            Files.move(oldCipherPath, targetPath, StandardCopyOption.ATOMIC_MOVE);
+        } catch (FileAlreadyExistsException e) {
+            LOG.warn("Target file already exists: {}", targetPath, e);
+            throw e;
+        }
     }
 }
✅ Verification successful

Based on the search results, I can see that:

  1. The Files.move operations in the codebase generally handle atomic moves and error cases, especially in CryptoFileSystemImpl and C9rConflictResolver.
  2. The Files.newByteChannel operations are typically wrapped in try-with-resources blocks with proper error handling.
  3. The codebase already implements synchronization and error handling patterns in similar scenarios.

Consider using atomic move operations and proper error handling.

The method should use StandardCopyOption.ATOMIC_MOVE for the move operations and handle potential exceptions:

void adoptOrphanedResource(Path oldCipherPath, String newClearName, boolean isShortened, CiphertextDirectory stepParentDir, FileNameCryptor cryptor, MessageDigest sha1) throws IOException {
    var newCipherName = encrypt(cryptor, newClearName, stepParentDir.dirId());
    if (isShortened) {
        var deflatedName = BaseEncoding.base64Url().encode(sha1.digest(newCipherName.getBytes(StandardCharsets.UTF_8))) + Constants.DEFLATED_FILE_SUFFIX;
        Path targetPath = stepParentDir.path().resolve(deflatedName);
        Files.move(oldCipherPath, targetPath, StandardCopyOption.ATOMIC_MOVE);

        //adjust name.c9s
        Path inflatedNameFile = targetPath.resolve(Constants.INFLATED_FILE_NAME);
        try (var fc = Files.newByteChannel(inflatedNameFile, 
                StandardOpenOption.WRITE, 
                StandardOpenOption.CREATE, 
                StandardOpenOption.TRUNCATE_EXISTING)) {
            fc.write(ByteBuffer.wrap(newCipherName.getBytes(StandardCharsets.UTF_8)));
        }
    } else {
        Path targetPath = stepParentDir.path().resolve(newCipherName);
        Files.move(oldCipherPath, targetPath, StandardCopyOption.ATOMIC_MOVE);
    }
}
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for concurrent access patterns to the same paths
rg -A 5 "Files\.(move|newByteChannel)" 

Length of output: 21534

src/main/java/org/cryptomator/cryptofs/CryptoFileSystemProvider.java (1)

158-158: LGTM: Constructor call updated to use standalone CiphertextDirectory.

The change correctly adapts to the refactored CiphertextDirectory class structure, maintaining the same functionality while aligning with the broader improvements to the caching system.

src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java (5)

144-144: LGTM: Method call updated correctly.

The change from accessing the path as a property to invoking it as a method aligns with the refactoring to use the new CiphertextDirectory record.


434-434: LGTM: Method call updated correctly.

The change from accessing the path as a property to invoking it as a method is consistent with the refactoring.


624-624: LGTM: Method call updated correctly.

The change from accessing the path as a property to invoking it as a method is consistent with the refactoring.


507-507: 🛠️ Refactor suggestion

Verify directory stream resource management.

The code properly uses try-with-resources for the DirectoryStream, but there's a potential resource leak if an exception occurs between the two directory operations.

Consider combining the directory operations or ensuring both are properly cleaned up in case of failures:

-Path ciphertextSourceDir = cryptoPathMapper.getCiphertextDir(cleartextSource).path();
-Path ciphertextTargetDir = cryptoPathMapper.getCiphertextDir(cleartextTarget).path();
+var sourceDir = cryptoPathMapper.getCiphertextDir(cleartextSource);
+var targetDir = cryptoPathMapper.getCiphertextDir(cleartextTarget);
+try {
+    copyAttributes(sourceDir.path(), targetDir.path());
+} catch (IOException e) {
+    // Handle cleanup if needed
+    throw e;
+}

Also applies to: 517-518


318-318: Verify atomic directory creation.

The directory creation process involves multiple steps that need to be atomic to prevent race conditions:

  1. Creating the raw path
  2. Writing the directory ID
  3. Creating the actual directory
  4. Persisting the long file name

While the code handles cleanup on failure, there might be a small window where concurrent operations could lead to inconsistencies.

Consider wrapping the directory creation steps in a synchronized block or using file system locks to ensure atomicity.

Also applies to: 325-325, 329-329, 333-333

src/test/java/org/cryptomator/cryptofs/health/dirid/OrphanDirTest.java (1)

4-4: LGTM: Import added for the new CiphertextDirectory class.

The import is correctly placed and aligns with the refactoring from nested class to standalone class.

src/test/java/org/cryptomator/cryptofs/CryptoFileSystemImplTest.java (2)

1260-1264: LGTM! Variable name improvement

The variable rename from cipherDirObject to ciphertextDirectoryObject improves code clarity by using the full term "ciphertext" consistently with the rest of the codebase.


1280-1280: LGTM! Added test coverage for directory ID backup

Added verification that dirIdBackup.execute() is called with the correct ciphertextDirectoryObject parameter, which aligns with the PR objective of properly handling directory cache invalidation.

src/main/java/org/cryptomator/cryptofs/CiphertextDirCache.java (2)

40-51: ⚠️ Potential issue

Address Thread Safety in Recomputing Cache Keys

Manipulating the cache using ciphertextDirectories.asMap().entrySet().removeIf(...) can introduce thread safety issues due to concurrent access.

To ensure thread-safe recomputation of cache keys, consider the following approach:

var entriesToRecompute = ciphertextDirectories.synchronous().asMap().entrySet().stream()
    .filter(e -> e.getKey().startsWith(oldPrefix))
    .collect(Collectors.toList());

entriesToRecompute.forEach(e -> {
    var remappedPath = newPrefix.resolve(oldPrefix.relativize(e.getKey()));
    ciphertextDirectories.synchronous().asMap().remove(e.getKey());
    ciphertextDirectories.put(remappedPath, e.getValue());
});

This code safely collects the entries to be recomputed and performs removal and insertion operations in a thread-safe manner.

⛔ Skipped due to learnings
Learnt from: infeo
PR: cryptomator/cryptofs#253
File: src/main/java/org/cryptomator/cryptofs/ClearToCipherDirCache.java:26-28
Timestamp: 2024-10-22T08:34:19.526Z
Learning: In the Java project using Caffeine's `AsyncCache`, the `asMap()` method returns a thread-safe map, so modifying it directly is thread-safe. Therefore, in `ClearToCipherDirCache.java`, using `ciphertextDirectories.asMap().keySet().removeIf(...)` is acceptable and thread-safe.

29-31: ⚠️ Potential issue

Ensure Thread-Safe Removal of Cache Entries

The use of ciphertextDirectories.asMap().keySet().removeIf(p -> p.startsWith(basePrefix)); may not be thread-safe, as concurrent modifications to the cache could lead to inconsistent states.

Consider using the cache's built-in thread-safe methods for removal. You can replace the current implementation with:

ciphertextDirectories.synchronous().asMap().keySet().removeIf(p -> p.startsWith(basePrefix));

Alternatively, iterate over the keys and remove them safely:

ciphertextDirectories.synchronous().invalidateAll(
    ciphertextDirectories.synchronous().asMap().keySet().stream()
        .filter(p -> p.startsWith(basePrefix))
        .collect(Collectors.toSet())
);
⛔ Skipped due to learnings
Learnt from: infeo
PR: cryptomator/cryptofs#253
File: src/main/java/org/cryptomator/cryptofs/ClearToCipherDirCache.java:26-28
Timestamp: 2024-10-22T08:34:19.526Z
Learning: In the Java project using Caffeine's `AsyncCache`, the `asMap()` method returns a thread-safe map, so modifying it directly is thread-safe. Therefore, in `ClearToCipherDirCache.java`, using `ciphertextDirectories.asMap().keySet().removeIf(...)` is acceptable and thread-safe.
Learnt from: infeo
PR: cryptomator/cryptofs#253
File: src/main/java/org/cryptomator/cryptofs/ClearToCipherDirCache.java:59-68
Timestamp: 2024-10-22T08:36:41.464Z
Learning: In the `ClearToCipherDirCache` class, recursive writes to the cache are not allowed because the `CipherDirLoader` might alter the cache by recursive computation. Therefore, computation is performed outside of the atomic context, as suggested in the Caffeine wiki.
src/test/java/org/cryptomator/cryptofs/CryptoPathMapperTest.java (6)

78-78: Update assertions to match new method return types

The addition of .path() indicates that getCiphertextDir() now returns an object with a path() method. The assertion correctly compares the expected Path object with the actual path.


102-102: Consistent use of .path() with getCiphertextDir()

The modification ensures that the retrieved ciphertext directory's path is obtained correctly for the test case.


136-136: Correctly accessing the path for deeper directory levels

Adding .path() ensures that the test retrieves the actual Path object for the /foo/bar directory, aligning with the method's updated return type.


337-338: Well-structured nested test class for cache invalidation

Introducing the InvalidateOrMovePathMapping nested class groups the related test cases logically, enhancing readability and maintainability of the test suite.


425-427: Validate cache behavior after moving path mappings

In testMoveEntryOldMissNewHit(), verifying that getCiphertextFilePath(fooPath) is called (indicating a cache miss) and that getCiphertextFilePath(kikPath) is not called (indicating a cache hit) correctly tests the desired cache behavior.


441-443: Ensure cache invalidation for child paths after moving a node

The test testMoveEntryOldChildMiss() correctly verifies that after moving a node, child paths of the old location result in cache misses. This is important to ensure that stale cache entries are not used.

src/main/java/org/cryptomator/cryptofs/CryptoPathMapper.java (5)

45-45: Proper Declaration of 'ciphertextDirCache'

The ciphertextDirCache field is correctly declared and will facilitate efficient caching of directory paths.


57-57: Correct Initialization of 'ciphertextDirCache'

The ciphertextDirCache is properly initialized in the constructor, ensuring the cache is ready for use.


66-66: Improved Documentation with '@throws IOException'

Adding the @throws IOException tag enhances the method documentation by clearly specifying potential I/O exceptions.


118-118: Accurate Retrieval of Ciphertext File Path

The call to getCiphertextFilePath(parent.path(), parent.dirId(), cleartextName) correctly retrieves the ciphertext file path using the parent directory's path and ID.


158-161: ⚠️ Potential issue

Undefined Variable 'dirFile' in Lambda Expression

The variable dirFile is used in the lambda expression but is not defined within its scope, leading to a potential compilation error.

Apply this diff to define dirFile within the lambda:

 			CiphertextDirCache.CipherDirLoader cipherDirLoaderIfAbsent = () -> {
+				Path dirFile = getCiphertextFilePath(cleartextPath).getDirFilePath();
 				return resolveDirectory(dirFile);
 			};

Likely invalid or redundant comment.

Comment on lines +14 to +20
public record CiphertextDirectory(String dirId, Path path) {

public CiphertextDirectory(String dirId, Path path) {
this.dirId = Objects.requireNonNull(dirId);
this.path = Objects.requireNonNull(path);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding format validation for dirId.

While null checks are good, consider adding validation for the dirId format since it's mentioned to be a UUID in the documentation.

Here's a suggested implementation:

 public record CiphertextDirectory(String dirId, Path path) {
+    private static final Pattern UUID_PATTERN = 
+        Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
 
     public CiphertextDirectory(String dirId, Path path) {
         this.dirId = Objects.requireNonNull(dirId);
+        if (!UUID_PATTERN.matcher(dirId).matches()) {
+            throw new IllegalArgumentException("dirId must be a valid UUID");
+        }
         this.path = Objects.requireNonNull(path);
     }
 }

Don't forget to add the import:

import java.util.regex.Pattern;

Copy link
Member

@overheadhunter overheadhunter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the old names for now works for me. New suggestions can be made for reworking the encryption layer from scratch.

(you missed clearNodeName during reverting, though)

@infeo infeo merged commit 5aedc43 into develop Oct 30, 2024
@infeo infeo deleted the feature/invalidate-dir-cache branch October 30, 2024 10:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cache of clear- to chiphertextDirectory mapping contains invalid entries

3 participants