1
0
Fork 0

Introduce a cross-platform safe version of is_readable to support UNC / wsl$ paths on Windows (#9861)

pull/9873/head
Jordi Boggiano 2021-05-04 13:25:52 +02:00 committed by GitHub
parent bfea0f7d1e
commit 3380178798
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 48 additions and 18 deletions

View File

@ -346,7 +346,7 @@ EOF;
$classmapFile .= ");\n"; $classmapFile .= ");\n";
if (!$suffix) { if (!$suffix) {
if (!$config->get('autoloader-suffix') && is_readable($vendorPath.'/autoload.php')) { if (!$config->get('autoloader-suffix') && Filesystem::isReadable($vendorPath.'/autoload.php')) {
$content = file_get_contents($vendorPath.'/autoload.php'); $content = file_get_contents($vendorPath.'/autoload.php');
if (preg_match('{ComposerAutoloaderInit([^:\s]+)::}', $content, $match)) { if (preg_match('{ComposerAutoloaderInit([^:\s]+)::}', $content, $match)) {
$suffix = $match[1]; $suffix = $match[1];
@ -1131,7 +1131,7 @@ INITIALIZER;
foreach ($autoload[$type] as $namespace => $paths) { foreach ($autoload[$type] as $namespace => $paths) {
foreach ((array) $paths as $path) { foreach ((array) $paths as $path) {
if (($type === 'files' || $type === 'classmap' || $type === 'exclude-from-classmap') && $package->getTargetDir() && !is_readable($installPath.'/'.$path)) { if (($type === 'files' || $type === 'classmap' || $type === 'exclude-from-classmap') && $package->getTargetDir() && !Filesystem::isReadable($installPath.'/'.$path)) {
// remove target-dir from file paths of the root package // remove target-dir from file paths of the root package
if ($package === $rootPackage) { if ($package === $rootPackage) {
$targetDir = str_replace('\\<dirsep\\>', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '<dirsep>', $package->getTargetDir()))); $targetDir = str_replace('\\<dirsep\\>', '[\\\\/]', preg_quote(str_replace(array('/', '\\'), '<dirsep>', $package->getTargetDir())));

View File

@ -225,7 +225,7 @@ class ClassMapGenerator
if (!$contents) { if (!$contents) {
if (!file_exists($path)) { if (!file_exists($path)) {
$message = 'File at "%s" does not exist, check your classmap definitions'; $message = 'File at "%s" does not exist, check your classmap definitions';
} elseif (!is_readable($path)) { } elseif (!Filesystem::isReadable($path)) {
$message = 'File at "%s" is not readable, check its permissions'; $message = 'File at "%s" is not readable, check its permissions';
} elseif ('' === trim(file_get_contents($path))) { } elseif ('' === trim(file_get_contents($path))) {
// The input file was really empty and thus contains no classes // The input file was really empty and thus contains no classes

View File

@ -12,6 +12,7 @@
namespace Composer\Command; namespace Composer\Command;
use Composer\Util\Filesystem;
use Composer\Util\Platform; use Composer\Util\Platform;
use Composer\Util\Silencer; use Composer\Util\Silencer;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@ -411,7 +412,7 @@ EOT
'secure-http' => array($booleanValidator, $booleanNormalizer), 'secure-http' => array($booleanValidator, $booleanNormalizer),
'cafile' => array( 'cafile' => array(
function ($val) { function ($val) {
return file_exists($val) && is_readable($val); return file_exists($val) && Filesystem::isReadable($val);
}, },
function ($val) { function ($val) {
return $val === 'null' ? null : $val; return $val === 'null' ? null : $val;
@ -419,7 +420,7 @@ EOT
), ),
'capath' => array( 'capath' => array(
function ($val) { function ($val) {
return is_dir($val) && is_readable($val); return is_dir($val) && Filesystem::isReadable($val);
}, },
function ($val) { function ($val) {
return $val === 'null' ? null : $val; return $val === 'null' ? null : $val;

View File

@ -796,7 +796,7 @@ EOT
} }
$file = Factory::getComposerFile(); $file = Factory::getComposerFile();
if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) { if (is_file($file) && Filesystem::isReadable($file) && is_array($composer = json_decode(file_get_contents($file), true))) {
if (!empty($composer['minimum-stability'])) { if (!empty($composer['minimum-stability'])) {
return VersionParser::normalizeStability($composer['minimum-stability']); return VersionParser::normalizeStability($composer['minimum-stability']);
} }

View File

@ -13,6 +13,7 @@
namespace Composer\Command; namespace Composer\Command;
use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Request;
use Composer\Util\Filesystem;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
@ -119,9 +120,7 @@ EOT
return 1; return 1;
} }
// check for readability by reading the file as is_readable can not be trusted on network-mounts if (!Filesystem::isReadable($this->file)) {
// see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926
if (!is_readable($this->file) && false === Silencer::call('file_get_contents', $this->file)) {
$io->writeError('<error>'.$this->file.' is not readable.</error>'); $io->writeError('<error>'.$this->file.' is not readable.</error>');
return 1; return 1;

View File

@ -381,7 +381,7 @@ TAGSPUBKEY
if (!is_file($oldFile)) { if (!is_file($oldFile)) {
throw new FilesystemException('Composer rollback failed: "'.$oldFile.'" could not be found'); throw new FilesystemException('Composer rollback failed: "'.$oldFile.'" could not be found');
} }
if (!is_readable($oldFile)) { if (!Filesystem::isReadable($oldFile)) {
throw new FilesystemException('Composer rollback failed: "'.$oldFile.'" could not be read'); throw new FilesystemException('Composer rollback failed: "'.$oldFile.'" could not be read');
} }
@ -582,7 +582,7 @@ EOT;
@unlink($script); @unlink($script);
// see if the file was moved and is still accessible // see if the file was moved and is still accessible
if ($result = is_readable($localFilename) && (hash_file('sha256', $localFilename) === $checksum)) { if ($result = Filesystem::isReadable($localFilename) && (hash_file('sha256', $localFilename) === $checksum)) {
$io->writeError('<info>Operation succeeded.</info>'); $io->writeError('<info>Operation succeeded.</info>');
@unlink($newFilename); @unlink($newFilename);
} else { } else {

View File

@ -17,6 +17,7 @@ use Composer\Package\Loader\ValidatingArrayLoader;
use Composer\Plugin\CommandEvent; use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents; use Composer\Plugin\PluginEvents;
use Composer\Util\ConfigValidator; use Composer\Util\ConfigValidator;
use Composer\Util\Filesystem;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
@ -77,7 +78,7 @@ EOT
return 3; return 3;
} }
if (!is_readable($file)) { if (!Filesystem::isReadable($file)) {
$io->writeError('<error>' . $file . ' is not readable.</error>'); $io->writeError('<error>' . $file . ' is not readable.</error>');
return 3; return 3;

View File

@ -15,6 +15,7 @@ namespace Composer\Config;
use Composer\Json\JsonFile; use Composer\Json\JsonFile;
use Composer\Json\JsonManipulator; use Composer\Json\JsonManipulator;
use Composer\Json\JsonValidationException; use Composer\Json\JsonValidationException;
use Composer\Util\Filesystem;
use Composer\Util\Silencer; use Composer\Util\Silencer;
/** /**
@ -217,7 +218,7 @@ class JsonConfigSource implements ConfigSourceInterface
throw new \RuntimeException(sprintf('The file "%s" is not writable.', $this->file->getPath())); throw new \RuntimeException(sprintf('The file "%s" is not writable.', $this->file->getPath()));
} }
if (!is_readable($this->file->getPath())) { if (!Filesystem::isReadable($this->file->getPath())) {
throw new \RuntimeException(sprintf('The file "%s" is not readable.', $this->file->getPath())); throw new \RuntimeException(sprintf('The file "%s" is not readable.', $this->file->getPath()));
} }

View File

@ -13,6 +13,7 @@
namespace Composer\Console; namespace Composer\Console;
use Composer\IO\NullIO; use Composer\IO\NullIO;
use Composer\Util\Filesystem;
use Composer\Util\Platform; use Composer\Util\Platform;
use Composer\Util\Silencer; use Composer\Util\Silencer;
use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Application as BaseApplication;
@ -282,7 +283,7 @@ class Application extends BaseApplication
// add non-standard scripts as own commands // add non-standard scripts as own commands
$file = Factory::getComposerFile(); $file = Factory::getComposerFile();
if (is_file($file) && is_readable($file) && is_array($composer = json_decode(file_get_contents($file), true))) { if (is_file($file) && Filesystem::isReadable($file) && is_array($composer = json_decode(file_get_contents($file), true))) {
if (isset($composer['scripts']) && is_array($composer['scripts'])) { if (isset($composer['scripts']) && is_array($composer['scripts'])) {
foreach ($composer['scripts'] as $script => $dummy) { foreach ($composer['scripts'] as $script => $dummy) {
if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) { if (!defined('Composer\Script\ScriptEvents::'.str_replace('-', '_', strtoupper($script)))) {

View File

@ -79,7 +79,7 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
$installPath = $this->getInstallPath($package); $installPath = $this->getInstallPath($package);
if (is_readable($installPath)) { if (Filesystem::isReadable($installPath)) {
return true; return true;
} }
@ -128,7 +128,7 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
$downloadPath = $this->getInstallPath($package); $downloadPath = $this->getInstallPath($package);
// remove the binaries if it appears the package files are missing // remove the binaries if it appears the package files are missing
if (!is_readable($downloadPath) && $repo->hasPackage($package)) { if (!Filesystem::isReadable($downloadPath) && $repo->hasPackage($package)) {
$this->binaryInstaller->removeBinaries($package); $this->binaryInstaller->removeBinaries($package);
} }

View File

@ -600,6 +600,33 @@ class Filesystem
return preg_replace('{^file://}i', '', $path); return preg_replace('{^file://}i', '', $path);
} }
/**
* Cross-platform safe version of is_readable()
*
* This will also check for readability by reading the file as is_readable can not be trusted on network-mounts
* and \\wsl$ paths. See https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926
*
* @param string $path
* @return bool
*/
public static function isReadable($path)
{
if (is_readable($path)) {
return true;
}
if (is_file($path)) {
return false !== Silencer::call('file_get_contents', $path, false, null, 0, 1);
}
if (is_dir($path)) {
return false !== Silencer::call('opendir', $path);
}
// assume false otherwise
return false;
}
protected function directorySize($directory) protected function directorySize($directory)
{ {
$it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS); $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS);

View File

@ -211,11 +211,11 @@ final class StreamContextFactory
} }
} }
if (isset($defaults['ssl']['cafile']) && (!is_readable($defaults['ssl']['cafile']) || !CaBundle::validateCaFile($defaults['ssl']['cafile'], $logger))) { if (isset($defaults['ssl']['cafile']) && (!Filesystem::isReadable($defaults['ssl']['cafile']) || !CaBundle::validateCaFile($defaults['ssl']['cafile'], $logger))) {
throw new TransportException('The configured cafile was not valid or could not be read.'); throw new TransportException('The configured cafile was not valid or could not be read.');
} }
if (isset($defaults['ssl']['capath']) && (!is_dir($defaults['ssl']['capath']) || !is_readable($defaults['ssl']['capath']))) { if (isset($defaults['ssl']['capath']) && (!is_dir($defaults['ssl']['capath']) || !Filesystem::isReadable($defaults['ssl']['capath']))) {
throw new TransportException('The configured capath was not valid or could not be read.'); throw new TransportException('The configured capath was not valid or could not be read.');
} }