1
0
Fork 0

Preserve file permissions on Windows self-update (#9733)

Windows file operations result in different file permissions depending
if the file is copied or moved. A copy operation applies permissions
from the destination folder (or file if it already exists and does not
use inheritance), while a move operation generally preserves the source
file permissions.

Windows PHP `rename` uses MoveFileEx so if the user is running as an
admin and the destination is in a common (non-user) location, then the
permission for other users will be replaced by the admin user. Likewise
for the UAC elevation feature, which uses the cmd.exe `move` command.

This fix uses copy and delete operations on Windows, so that other users
can continue to run composer.phar
pull/9734/head
John Stevenson 2021-03-05 18:57:47 +00:00 committed by GitHub
parent e3af4ee606
commit 07f59a9162
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 15 additions and 7 deletions

View File

@ -425,7 +425,14 @@ TAGSPUBKEY
} }
try { try {
if (Platform::isWindows()) {
// use copy to apply permissions from the destination directory
// as rename uses source permissions and may block other users
copy($newFilename, $localFilename);
@unlink($newFilename);
} else {
rename($newFilename, $localFilename); rename($newFilename, $localFilename);
}
return true; return true;
} catch (\Exception $e) { } catch (\Exception $e) {
@ -534,7 +541,7 @@ TAGSPUBKEY
/** /**
* Invokes a UAC prompt to update composer.phar as an admin * Invokes a UAC prompt to update composer.phar as an admin
* *
* Uses a .vbs script to elevate and run the cmd.exe move command. * Uses a .vbs script to elevate and run the cmd.exe copy command.
* *
* @param string $localFilename The composer.phar location * @param string $localFilename The composer.phar location
* @param string $newFilename The downloaded or backup phar * @param string $newFilename The downloaded or backup phar
@ -560,13 +567,13 @@ TAGSPUBKEY
$checksum = hash_file('sha256', $newFilename); $checksum = hash_file('sha256', $newFilename);
// cmd's internal move is fussy about backslashes // cmd's internal copy is fussy about backslashes
$source = str_replace('/', '\\', $newFilename); $source = str_replace('/', '\\', $newFilename);
$destination = str_replace('/', '\\', $localFilename); $destination = str_replace('/', '\\', $localFilename);
$vbs = <<<EOT $vbs = <<<EOT
Set UAC = CreateObject("Shell.Application") Set UAC = CreateObject("Shell.Application")
UAC.ShellExecute "cmd.exe", "/c move /y ""$source"" ""$destination""", "", "runas", 0 UAC.ShellExecute "cmd.exe", "/c copy /b /y ""$source"" ""$destination""", "", "runas", 0
Wscript.Sleep(300) Wscript.Sleep(300)
EOT; EOT;
@ -574,11 +581,12 @@ EOT;
exec('"'.$script.'"'); exec('"'.$script.'"');
@unlink($script); @unlink($script);
// see if the file was moved // see if the file was moved and is still accessible
if ($result = (hash_file('sha256', $localFilename) === $checksum)) { if ($result = is_readable($localFilename) && (hash_file('sha256', $localFilename) === $checksum)) {
$io->writeError('<info>Operation succeeded.</info>'); $io->writeError('<info>Operation succeeded.</info>');
@unlink($newFilename);
} else { } else {
$io->writeError('<error>Operation failed (file not written). '.$helpMessage.'</error>'); $io->writeError('<error>Operation failed.'.$helpMessage.'</error>');
} }
return $result; return $result;