Visitar URL original
[4.x] Do not make files world-writable in File::delete() and name the full path on failure by Hackwar · Pull Request #110 · joomla-framework/filesystem · GitHub
Skip to content

[4.x] Do not make files world-writable in File::delete() and name the full path on failure - #110

Open
Hackwar wants to merge 1 commit into
joomla-framework:4.x-devfrom
Hackwar:4.x-file-delete-permissions
Open

Hackwar wants to merge 1 commit into
joomla-framework:4.x-devfrom
Hackwar:4.x-file-delete-permissions

Conversation

@Hackwar

@Hackwar Hackwar commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

Summary of Changes

File::delete() called @chmod($file, 0777) on every file before unlink(), to be able to delete read-only files on Windows. On POSIX systems whether a file can be deleted depends on its folder, not its mode, so the call only widened the permissions — and when unlink() failed (e.g. the folder is not writable), the file was left world-writable.

delete() now tries unlink() first. Only if that fails does it add the owner's write bit (fileperms | 0200) and try again; if the second attempt fails too, the original permissions are restored. Read-only files on Windows are still deleted.

The same lines build the error message, which contained only basename($file): with several index.html files the log could not tell which one failed (#64). The message now contains the full cleaned path; FilesystemException already replaces the root and temp directories with [ROOT]/[TMP]. Both changes touch the same lines, hence one PR. Folder's delete messages already contain the path.

Testing Instructions

New tests in FileTest:

  • testFailedDeleteKeepsPermissions (POSIX, not as root) deletes a 0644 file in a 0555 folder. Before: the exception is thrown and the file is 0777; after: it stays 0644.
  • testDeleteErrorNamesTheFullPath — before: Failed deleting index.html; after: the message contains /sub/index.html.
  • testDeleteReadOnlyFile checks that a 0444 file is still deleted (on Windows through the second attempt).

Documentation Changes Required

docs/overview.md gets a "Deleting files" section.

#88 rewrites docs/overview.md; whichever of the two is merged second has to resolve the conflict there. In #88's "Things to know before you build on this", the paragraph "File::delete() chmods to 0777 first" describes the behaviour changed here and has to go.

Fixes #64

🤖 Generated with Claude Code

delete() chmodded every file to 0777 before unlinking it, so that
read-only files can be deleted on Windows. On POSIX systems deleting
depends on the folder, so this only widened the permissions, and left
the file world-writable when unlink() failed. Now the file is deleted
first; only if that fails is the owner's write permission added for a
second attempt, and the permissions are restored if it fails too.

The error message named only the base name of the file, so logs could
not tell which of several files with the same name failed. It contains
the full path now; FilesystemException masks the root directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Hackwar

Hackwar commented Nov 12, 2026

Copy link
Copy Markdown
Contributor Author

The CI failures are unrelated to this change and occur on 4.x-dev as well (see its scheduled runs):

  • Unit tests: the only failing test is HelperTest::testRemotefsize. https://www.example.org no longer sends a Content-Length header, so Helper::remotefsize() returns false. [4.x] Fix Stream reads at EOF, of "0" chunks and of Windows paths #88 makes that test skip when the header is missing.
  • Unit tests (Windows) (8.3): composer install --ignore-platform-reqs installs a PHPUnit release that requires PHP 8.4, so PHPUnit refuses to start ("This version of PHPUnit requires PHP >= 8.4.1"). Whichever Windows job fails first cancels the other (fail-fast).
  • PHPStan (non-blocking, continue-on-error): reports the existing errors in FtpClient and File::isSafeFile(). This change adds no new ones. FileTest::testFailedDeleteKeepsPermissions is skipped in CI because the container runs as root, for whom the folder permissions do not apply.

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.

Error logs on deleting files should contain path information

1 participant