feature #30116 [Filesystem] Fix mirroring a directory into itself or in his child with realpath checks (Fleuv, XuruDragon)

This PR was merged into the 4.3-dev branch.

Discussion
----------

[Filesystem] Fix mirroring a directory into itself or in his child with realpath checks

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | yes
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | none / PR #29857
| License       | MIT
| Doc PR        | n/a

This this the continuity of #29857 by @Fleuv

Fix a bug while trying to mirror a directory into itself or in a child
Adding handle real path checks when mirroring.

Commits
-------

8011f494d4 Handling relative/absolute path
59437a4af9 Skipping iterations in the mirror() method where the iterated file's path name is equal to the target path
This commit is contained in:
Fabien Potencier 2019-03-31 11:15:35 +02:00
commit e92c120f27
2 changed files with 34 additions and 51 deletions

View File

@ -541,6 +541,10 @@ class Filesystem
$originDir = rtrim($originDir, '/\\');
$originDirLen = \strlen($originDir);
if (!$this->exists($originDir)) {
throw new IOException(sprintf('The origin directory specified "%s" was not found.', $originDir), 0, null, $originDir);
}
// Iterate in destination folder to remove obsolete entries
if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) {
$deleteIterator = $iterator;
@ -564,35 +568,24 @@ class Filesystem
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
}
if ($this->exists($originDir)) {
$this->mkdir($targetDir);
}
$this->mkdir($targetDir);
$targetDirInfo = new \SplFileInfo($targetDir);
foreach ($iterator as $file) {
if (false === strpos($file->getPath(), $originDir)) {
throw new IOException(sprintf('Unable to mirror "%s" directory. If the origin directory is relative, try using "realpath" before calling the mirror method.', $originDir), 0, null, $originDir);
if ($file->getPathName() === $targetDir || $file->getRealPath() === $targetDir || 0 === strpos($file->getRealPath(), $targetDirInfo->getRealPath())) {
continue;
}
$target = $targetDir.substr($file->getPathname(), $originDirLen);
if ($copyOnWindows) {
if (is_file($file)) {
$this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
} elseif (is_dir($file)) {
$this->mkdir($target);
} else {
throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file);
}
if (!$copyOnWindows && is_link($file)) {
$this->symlink($file->getLinkTarget(), $target);
} elseif (is_dir($file)) {
$this->mkdir($target);
} elseif (is_file($file)) {
$this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
} else {
if (is_link($file)) {
$this->symlink($file->getLinkTarget(), $target);
} elseif (is_dir($file)) {
$this->mkdir($target);
} elseif (is_file($file)) {
$this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
} else {
throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file);
}
throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file);
}
}
}

View File

@ -1332,44 +1332,34 @@ class FilesystemTest extends FilesystemTestCase
$this->assertFileNotExists($targetPath.'target');
}
public function testMirrorWithCustomIterator()
public function testMirrorAvoidCopyingTargetDirectoryIfInSourceDirectory()
{
$sourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR;
$directory = $sourcePath.'directory'.\DIRECTORY_SEPARATOR;
$file1 = $directory.'file1';
$file2 = $sourcePath.'file2';
mkdir($sourcePath);
mkdir($directory);
file_put_contents($file1, 'FILE1');
file_put_contents($file2, 'FILE2');
$file = $sourcePath.\DIRECTORY_SEPARATOR.'file';
file_put_contents($file, 'FILE');
$targetPath = $sourcePath.'target'.\DIRECTORY_SEPARATOR;
$targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR;
if ('\\' !== \DIRECTORY_SEPARATOR) {
$this->filesystem->symlink($targetPath, $sourcePath.'target_simlink');
}
$splFile = new \SplFileInfo($file);
$iterator = new \ArrayObject([$splFile]);
$this->filesystem->mirror($sourcePath, $targetPath, null, ['delete' => true]);
$this->filesystem->mirror($sourcePath, $targetPath, $iterator);
$this->assertTrue($this->filesystem->exists($targetPath));
$this->assertTrue($this->filesystem->exists($targetPath.'directory'));
$this->assertTrue(is_dir($targetPath));
$this->assertFileEquals($file, $targetPath.\DIRECTORY_SEPARATOR.'file');
}
$this->assertFileEquals($file1, $targetPath.'directory'.\DIRECTORY_SEPARATOR.'file1');
$this->assertFileEquals($file2, $targetPath.'file2');
/**
* @expectedException \Symfony\Component\Filesystem\Exception\IOException
* @expectedExceptionMessageRegExp /Unable to mirror "(.*)" directory/
*/
public function testMirrorWithCustomIteratorWithRelativePath()
{
$sourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR;
$realSourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR;
mkdir($realSourcePath);
$file = $realSourcePath.'file';
file_put_contents($file, 'FILE');
$targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR;
$splFile = new \SplFileInfo($file);
$iterator = new \ArrayObject([$splFile]);
$this->filesystem->mirror($sourcePath, $targetPath, $iterator);
$this->assertFalse($this->filesystem->exists($targetPath.'target_simlink'));
$this->assertFalse($this->filesystem->exists($targetPath.'target'));
}
/**