Skip to content
This repository was archived by the owner on Jul 26, 2024. It is now read-only.

Commit e6d2f17

Browse files
committed
Improved symlink support for backup/restore
Updates filesystem iteration to specifically handle symlinks when iterating through files, and uses the file's real path when fetching their contents. Fixes #10 Adds test to cover backup case.
1 parent 8853033 commit e6d2f17

File tree

5 files changed

+49
-7
lines changed

5 files changed

+49
-7
lines changed

src/Commands/BackupCommand.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Cli\Services\EnvironmentLoader;
77
use Cli\Services\MySqlRunner;
88
use Cli\Services\Paths;
9+
use FilesystemIterator;
910
use RecursiveDirectoryIterator;
1011
use SplFileInfo;
1112
use Symfony\Component\Console\Command\Command;
@@ -158,12 +159,15 @@ protected function addUploadFoldersToZip(ZipArchive $zip, string $appDir): void
158159
*/
159160
protected function addFolderToZipRecursive(ZipArchive $zip, string $dirPath, string $targetZipPath): void
160161
{
161-
$dirIter = new RecursiveDirectoryIterator($dirPath);
162+
$dirIter = new RecursiveDirectoryIterator(
163+
$dirPath,
164+
FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::FOLLOW_SYMLINKS
165+
);
162166
$fileIter = new \RecursiveIteratorIterator($dirIter);
163167
/** @var SplFileInfo $file */
164168
foreach ($fileIter as $file) {
165169
if (!$file->isDir()) {
166-
$zip->addFile($file->getPathname(), $targetZipPath . '/' . $fileIter->getSubPathname());
170+
$zip->addFile($file->getRealPath(), $targetZipPath . '/' . $fileIter->getSubPathname());
167171
}
168172
}
169173
}

src/Services/AppLocator.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ public static function search(string $directory = ''): string
1313
static::getCliDirectory(),
1414
];
1515

16-
var_dump($directoriesToSearch);
17-
1816
foreach ($directoriesToSearch as $directory) {
1917
if ($directory && static::isProbablyAppDirectory($directory)) {
2018
return $directory;

src/Services/Directories.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
<?php
1+
<?php declare(strict_types=1);
22

33
namespace Cli\Services;
4+
use FilesystemIterator;
45
use RecursiveDirectoryIterator;
56
use RecursiveIteratorIterator;
67

@@ -19,7 +20,7 @@ public static function copy(string $src, string $target): void
1920

2021
/** @var RecursiveDirectoryIterator $files */
2122
$files = new RecursiveIteratorIterator(
22-
new RecursiveDirectoryIterator($src, RecursiveDirectoryIterator::SKIP_DOTS),
23+
new RecursiveDirectoryIterator($src, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS),
2324
RecursiveIteratorIterator::SELF_FIRST
2425
);
2526

@@ -43,7 +44,7 @@ public static function copy(string $src, string $target): void
4344
public static function delete(string $dir): void
4445
{
4546
$files = new RecursiveIteratorIterator(
46-
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
47+
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS),
4748
RecursiveIteratorIterator::CHILD_FIRST
4849
);
4950

tests/Commands/BackupCommandTest.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,42 @@ public function test_backup_path_argument()
119119
unlink($zipFile);
120120
}
121121

122+
public function test_command_resolves_nested_symlinks()
123+
{
124+
$symDirs = ['storage/uploads/files', 'storage/uploads/images'];
125+
exec('cp -r /var/www/bookstack /var/www/bookstack-symlink-backup');
126+
mkdir('/symlinks');
127+
foreach ($symDirs as $dir) {
128+
$targetFile = str_replace('/', '-', $dir);
129+
$code = 0;
130+
$output = null;
131+
exec("mkdir -p /var/www/bookstack-symlink-backup/{$dir}", $output, $code);
132+
exec("mv /var/www/bookstack-symlink-backup/{$dir} /symlinks/{$targetFile}", $output, $code);
133+
exec("ln -s /symlinks/{$targetFile} /var/www/bookstack-symlink-backup/{$dir}", $output, $code);
134+
file_put_contents("/symlinks/{$targetFile}/test.txt", "Hello from $dir");
135+
if ($code !== 0) {
136+
$this->fail("Error when setting up symlinks");
137+
}
138+
}
139+
140+
chdir('/var/www/bookstack-symlink-backup');
141+
$this->assertCount(0, glob('storage/backups/bookstack-backup-*.zip'));
142+
$result = $this->runCommand('backup');
143+
$result->assertSuccessfulExit();
144+
$this->assertCount(1, glob('storage/backups/bookstack-backup-*.zip'));
145+
$zipFile = glob('storage/backups/bookstack-backup-*.zip')[0];
146+
147+
$zip = new \ZipArchive();
148+
$zip->open($zipFile);
149+
foreach ($symDirs as $dir) {
150+
$fileData = $zip->getFromName("{$dir}/test.txt");
151+
$this->assertNotFalse($fileData);
152+
$this->assertStringContainsString("Hello from {$dir}", $fileData);
153+
}
154+
$zip->close();
155+
156+
exec('rm -rf /symlinks');
157+
exec('rm -rf /var/www/bookstack-symlink-backup');
158+
}
159+
122160
}

tests/Commands/RestoreCommandTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ public function test_restore_with_symlinked_content_folders()
162162
$this->assertStringEqualsFile('/var/www/bookstack-symlink-restore/themes/test.txt', 'hello-themes');
163163

164164
exec('rm -rf /var/www/bookstack-symlink-restore');
165+
exec('rm -rf /symlinks');
165166
}
166167

167168
protected function buildZip(callable $builder): string

0 commit comments

Comments
 (0)