Skip to content

fix(LocalStorage): Clear realpath cache on fopen failure and re-try access - #63101

Open
DerDreschner wants to merge 1 commit into
masterfrom
fix/add-retry-on-realpath-cache-miss
Open

fix(LocalStorage): Clear realpath cache on fopen failure and re-try access#63101
DerDreschner wants to merge 1 commit into
masterfrom
fix/add-retry-on-realpath-cache-miss

Conversation

@DerDreschner

@DerDreschner DerDreschner commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

PHP resolves the path of every fopen() through its realpath cache, which stores per path component whether that component is a directory. That cache is per process, and entries live for realpath_cache_ttl (120s by default). If one process has a path cached as a file and another process replaces it with a directory, the first process keeps refusing to descend into it and fopen() fails as if the file were missing - while stat()/file_exists() bypass that cache and keep reporting the file. The file looks perfectly present and is still unreadable, for up to two minutes.

That is why the filesdrop integration tests fail intermittently: the suite recycles the same user and paths, one scenario stores drop/Alice/folder as a file, the next needs it as a directory, and the dev server runs several worker processes (PHP_CLI_SERVER_WORKERS=2), so the worker serving the download is usually not the one that created the directory (example run: …). It is not CI-specific: php-fpm workers keep the same cache, so a user who deletes a file X, creates a folder X, and downloads X/y.txt via another worker gets a 503 for up to realpath_cache_ttl.

This is being fixed by this PR, which in this case walks up the parents, clears their cache entry and re-tries to access the path.

Checklist

AI (if applicable)

  • The content of this PR was partly or fully generated using AI

@DerDreschner DerDreschner self-assigned this Aug 10, 2026
@DerDreschner
DerDreschner requested a review from a team as a code owner August 10, 2026 01:50
@DerDreschner
DerDreschner requested review from provokateurin and salmart-dev and removed request for a team August 10, 2026 01:50
@DerDreschner DerDreschner added the 3. to review Waiting for reviews label Aug 10, 2026
@DerDreschner
DerDreschner requested a review from come-nc August 10, 2026 01:50
@DerDreschner

Copy link
Copy Markdown
Contributor Author

/backport to stable34

@DerDreschner

Copy link
Copy Markdown
Contributor Author

/backport to stable33

@DerDreschner

Copy link
Copy Markdown
Contributor Author

/backport to stable32

@DerDreschner

Copy link
Copy Markdown
Contributor Author

/backport to stable31

@DerDreschner

Copy link
Copy Markdown
Contributor Author

/backport to stable30

@DerDreschner

Copy link
Copy Markdown
Contributor Author

/backport to stable29

@DerDreschner
DerDreschner enabled auto-merge August 10, 2026 01:51
@DerDreschner

Copy link
Copy Markdown
Contributor Author

Backport down to stable29 is to stabilize our CI runs.

@joshtrichards joshtrichards left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some of the comments may be better represented by (or at least in) the regression tests. 🤷

Comment thread lib/private/Files/Storage/Local.php
Comment thread lib/private/Files/Storage/Local.php Outdated
// realpath_cache_ttl expires, this process still has it cached as a file, and
// because fopen() resolves through that cache, it refuses to look inside and
// fails as if the file did not exist. Dropping the stale entries and opening
// again is what fixes that. The retry deliberately stays unsuppressed, so the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm all for making troubleshooting clues not invisible, but this is an API contract change. Not sure...

$this->unlink($path);
}
$result = @fopen($sourcePath, $mode);
if ($result === false) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Any non-cache-related failures worth ignoring? (Not even sure it's possible).

@joshtrichards

Copy link
Copy Markdown
Member

+1 to an official php.net docs clarification in this area. ;-)

I wander.if this is relevant for any other Local (or elsewhere?) operations and/or anywhere else like the Storage-less Updater app?

Comment thread lib/private/Files/Storage/Local.php Outdated
…ccess

Assisted-by: ClaudeCode:claude-opus-5
Signed-off-by: David Dreschner <david.dreschner@nextcloud.com>
@DerDreschner
DerDreschner force-pushed the fix/add-retry-on-realpath-cache-miss branch from deec1b8 to ceca32a Compare August 10, 2026 08:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants