✨ Add recursive delete for non-empty directories - #641
Merged
vinceglb merged 4 commits intoSep 7, 2026
Merged
Conversation
delete() gains a `recursively` flag, defaulting to false, so existing calls behave exactly as before and a non-empty directory still fails. Symlinks are unlinked rather than followed. kotlinx-io's FileMetadata carries no link information and isDirectory() resolves the link, so a naive recursion would delete the contents of whatever the link points at. A new internal isSymbolicLink() answers that per platform: Files.isSymbolicLink on JVM, Os.lstat on Android (java.nio needs API 26 and this library supports 21), attributesOfItemAtPath on Apple, lstat on Linux, and the reparse point attribute on Windows. The SAF branch on Android is left alone: removing a document is the provider's job and there is no empty-directory rule to work around. Closes vinceglb#640 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Inspect and unlink filesystem links before querying directory metadata. Use Windows reparse-point attributes on JVM to avoid traversing junctions, and direct deletion primitives to remove dangling and cyclic links. Add JVM, native Apple, and Android host regressions, including a Windows junction test. Run shared file tests under Robolectric, and supply no-follow host filesystem behavior for its link tests because the default lstat shadow follows directory links. Document recursive deletion and link behavior. Manual native verification: create a directory containing a link to an outside directory with a sentinel file, call delete(recursively = true), and verify the directory is gone while the sentinel is unchanged. Repeat with a dangling link and a self-referencing link. On Windows, repeat using a directory junction created with mklink /J.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #640.
Adds opt-in recursive deletion of non-empty directories:
For filesystem paths,
recursively = falsekeeps the existing non-empty-directory failure. Withtrue, FileKit deletes the contents before removing the directory. Android document URIs continue to delegate deletion to the provider.Links are inspected and unlinked before directory metadata is queried. This preserves outside targets and handles dangling and self-referencing links. Windows JVM uses reparse-point attributes so directory junctions are also recognized. Link removal bypasses kotlinx-io's target-existence check, which otherwise leaves dangling links behind.
Regression coverage
lstatfollows directory links.AGENTS.mdto keep local validation targeted; broad builds belong in CI because they overload the maintainer's Mac.Validation
Manual native verification
outside/treasure.txtwith known contents anddoomed/linkpointing tooutside. On Windows, also repeat with a junction created usingmklink /J.PlatformFile(doomedPath).delete(recursively = true)in the target app. Verifydoomedis gone and the sentinel's contents are unchanged.doomed. The directory and links should all be removed.recursively = false; deletion should fail and its contents should remain.