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
9 changes: 8 additions & 1 deletion inc/Core/FilesRepository/FilesystemHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ public static function get(): ?\WP_Filesystem_Base {
* @return bool True if the file is (or was made) group-writable, false otherwise.
*/
public static function make_group_writable( string $filepath, int $permissions = self::AGENT_FILE_PERMISSIONS ): bool {
// Atomic replacement or another process may have changed the inode or mode.
// Permission and ownership decisions must describe the current file.
clearstatcache( true, $filepath );

if ( ! file_exists( $filepath ) ) {
return false;
}
Expand All @@ -127,7 +131,10 @@ public static function make_group_writable( string $filepath, int $permissions =

// Suppress any residual warning — a failed chmod is non-fatal here.
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_chmod, WordPress.PHP.NoSilencedErrors.Discouraged
return @chmod( $filepath, $permissions );
$result = @chmod( $filepath, $permissions );
// PHP 8.2 can retain the pre-chmod mode for subsequent stat calls.
clearstatcache( true, $filepath );
return $result;
}

/**
Expand Down
5 changes: 5 additions & 0 deletions tests/Unit/Abilities/Media/StackTemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ public function test_list_item_colors_render_without_leaking_to_other_items(): v
);
$paths = \DataMachine\Abilities\Media\BrandTokens::with_overrides( $data['brand'], static fn() => $t->render( $data, $r ) );
$this->files = array_merge( $this->files, $paths );
foreach ( array( 'body', 'heading' ) as $font ) {
if ( ! is_readable( $r->get_font( $font ) ) ) {
$this->markTestSkipped( 'Readable body and heading fonts required for pixel assertions.' );
}
}
$box = $t->plan_layout( $r, $data )[0];
$line = (int) ( 64 * 1.2 );
foreach ( array( 0 => 0xffffff, 1 => 0xff0000, 2 => 0xffffff ) as $i => $expected ) {
Expand Down
48 changes: 48 additions & 0 deletions tests/filesystem-permission-cache-smoke.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/**
* Permission helpers must expose current metadata, including on PHP 8.2.
*
* Run with: php tests/filesystem-permission-cache-smoke.php
*
* @package DataMachine\Tests
*/

require_once __DIR__ . '/bootstrap-unit.php';

use DataMachine\Core\FilesRepository\FilesystemHelper;

$permission_fixture = tempnam( sys_get_temp_dir(), 'datamachine-permissions-' );
if ( false === $permission_fixture ) {
throw new RuntimeException( 'Could not create permission fixture.' );
}

try {
chmod( $permission_fixture, 0600 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_chmod -- Tests native filesystem metadata.
clearstatcache( true, $permission_fixture );
if ( 0600 !== ( fileperms( $permission_fixture ) & 0777 ) ) {
throw new RuntimeException( 'Fixture must prime the restrictive mode in the stat cache.' );
}

if ( ! FilesystemHelper::make_group_writable( $permission_fixture ) ) {
throw new RuntimeException( 'Permission helper failed for an owned file.' );
}

// Do not clear the cache here: callers must immediately see the new mode.
if ( 0060 !== ( fileperms( $permission_fixture ) & 0060 ) ) {
throw new RuntimeException( 'Permission helper left stale metadata after chmod.' );
}

// Independent stat verifies the helper changed the file, not just its cache.
clearstatcache( true, $permission_fixture );
if ( 0060 !== ( fileperms( $permission_fixture ) & 0060 ) ) {
throw new RuntimeException( 'File on disk is not group-readable and writable.' );
}

if ( ! FilesystemHelper::make_group_writable( $permission_fixture ) ) {
throw new RuntimeException( 'Already shared file should remain successful.' );
}
} finally {
unlink( $permission_fixture ); // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink -- Removes native filesystem fixture.
}

echo "Filesystem permission cache smoke passed.\n";
Loading