Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,9 @@ Each package has a test project of its own, `tests/WriteableFileProviders.<Packa
`TutorialTests` runs step 2 of `docs/guide/getting-started.md` (download URLs for local files) over the Physical provider.
- **Physical:** the suite on the local disk, under a temp folder of its own that the class deletes when it ends;
`PhysicalProviderTests` and `PhysicalOptionsTests` cover the temp files, the races, links, the rules that differ by OS
and the stored root forms; `PhysicalLinkAndSweepTests`: `RefuseLinks` and the sweep of leftover temp files. CI runs it
on Linux (x64 and Arm64), Windows and macOS.
and the stored root forms; `PhysicalLinkAndSweepTests`: `RefuseLinks` and the sweep of leftover temp files;
`PhysicalFolderDeleteTests`: the folder delete through handles, links swapped in while it runs, and the rules each
system's answers are read by. CI runs it on Linux (x64 and Arm64), Windows and macOS.
- **Azure Blob**, on Azurite: `AzuriteConformanceTests` (providers built from options) and
`AzuriteStrictConformanceTests` (over a container client whose pipeline, `AzureHandler`, refuses what Azure refuses
and Azurite allows: replacing an append blob, more blocks than the limit, here lowered to 6), and
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ version's section as its GitHub release notes.
while the file there is empty (a move's destination, a file system without hard links, or a file another writer left
empty), since it can't tell such a file from one a rename is about to fill, which would undo the append; on Windows,
one of two appends to one file at once can still fail with `UnauthorizedAccessException`.
- **A Physical folder delete can no longer be steered outside the root** by a link swapped in while it runs: on Linux
(x64, Arm, Arm64), macOS and Windows it opens each entry in its folder without following a link and removes it there,
so a link inside the folder is removed as itself, and with `RefuseLinks` every folder on the way from the root is
opened the same way and a link swapped in there is refused. On Windows a folder holding a junction is now deleted
(before, `UnauthorizedAccessException`). A folder that still has entries after the delete (a file another process
holds open on a file system without POSIX deletes) throws `IOException`, where it returned true. On macOS the folder
that holds the deleted one, and with `RefuseLinks` every folder on the way, must be readable. On those systems the
delete can now be cancelled: it stops between entries, the folder partly deleted. Elsewhere (Linux on other
architectures, other systems) a folder is deleted by its path, as before.

## 2027.1.0 (2026-10-13)

Expand Down
23 changes: 21 additions & 2 deletions docs/plans/2026-10-13-2027-1-1.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ Decided while planning. The maintainer's approval of this plan approves them.
| `O_CLOEXEC` | 0x80000 | 0x80000 | 0x1000000 |
| `AT_REMOVEDIR` | 0x200 | 0x200 | 0x80 |
| `ENOENT`, `EACCES`, `EPERM`, `ENOTDIR`, `EINVAL` | 2, 13, 1, 20, 22 | same | same |
| `ELOOP`, `ENOTEMPTY` | 40, 39 | 40, 39 | 62, 66 |
| `ELOOP`, `ENOTEMPTY` (unread since step 3: "anything else") | 40, 39 | 40, 39 | 62, 66 |

What each error means to the walk (N5): `ENOENT`, gone (skipped, or false at the start); `EACCES` and `EPERM`, refused
(`UnauthorizedAccessException`); `ENOTDIR` and `ELOOP` from an open, not a folder (then `readlinkat`); `EINVAL` from
Expand Down Expand Up @@ -466,4 +466,23 @@ expectation (the winner is done before the loser retries); the locked restore of
them, so Darwin's limit (32) and Linux's (40) are pinned on the kernel. The docs name the jobs, and the Native AOT job
that the release docs had left out. Code review: round 1, no blocker or major, five fixes (two on the maintainer's
yes); round 2 clean. On the maintainer's choice, both jobs become required checks on `main` after the merge (P8's
ruleset question).
ruleset question). Merged as #75; `main`'s ruleset now requires both.
- **2026-10-13, step 3 (#70):** `FolderDelete` (the walk, what an entry is, the errors, and the rules: `UnixTable`,
`WindowsRules`), `UnixFolders` and `WindowsFolders`, written before step 1 merged and rebased onto it. As built: the
seam is `FolderSystem`, an abstract class whose `Current` is the system's or null, and the provider's `Folders`
property (null deletes by path) stands for the plan's `DeletesByHandle`; the classic-delete switch is
`ClassicDeletes`; the seam's `Open` returns an entry already classified by shared rules (`WindowsRules.KindOf`, and
`UnixFolders`, measured on Linux), so `WindowsFolders` keeps only the calls; a folder that cannot be listed records
the failure like any other and the walk throws it at the end. Run on Windows and on Linux in a container, Physical's
coverage on Linux 98.48% of lines and 95.00% of branches before the review. Code review, deep tier (the maintainer's
choice), round 1: no blocker; three majors, all fixed on the maintainer's yes: a listing that names `.` or `..` as
files (FUSE, some NFS servers) would have opened the folder's parent, or itself, through its handle, so a listed name
is now only ever an entry of the folder (`.` and `..` skipped, a separator refused), with listing behind the seam so a
test can hand it those names (F1); the R3 test (a folder on the way held open without delete sharing), shown to fail
when the walk asks for delete access there (F2); the README's account of where the walk runs and the fallback (F3).
Minors: a subfolder gone between its open and its listing is skipped (F4); the walk is cancellable between entries
(F5); `Failure.NotEmpty` and the table's `ENOTEMPTY` go, so a folder not empty is "anything else" (F6, a departure
from D6's table); one representation of gone (F7). F8, dropping `ClassicDeletes`, was declined: the plan names it.
Round 2: F1 to F7 verified. G1, an empty listed name would reopen the folder itself on Windows, refused as a name with
a separator is; G2, the check for a name gone after a refused removal looks it up in the extended form on Windows,
which keeps a trailing dot or space, with a test; nits. Round 3: G1 to G3 verified, clean; one test nit applied.
Loading
Loading