Skip to content

Commit 36ca73a

Browse files
committed
fix(storage): Unlink symlink instead of deleting target content and link
Nextcloud allow following symlink (https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/config_sample_php_parameters.html#localstorage-allowsymlinks) but removal of symlink remove files in the target of the symlink and fail to remove the link returning an error. Using the following structure: ``` . ├── afolder │   └── test.txt ├── alink -> afolder └── welcome.txt ``` created with: ```bash mkdir afolder touch afolder/test.txt ln -s afolder alink ``` After deletion of `alink` symbolic link, the content of `afolder` is removed, the link also and an error is reported. ```json { "method":"DELETE", "url":"/remote.php/dav/files/admin/alink", "message":"rmdir(/data/dev/nextcloud/data/admin/files/alink): Not a directory at /data/dev/nextcloud/lib/private/Files/Storage/Local.php#128" } ``` Results is: ``` . ├── afolder └── welcome.txt ``` If the resource to be deleted is a link, unlink it (and preserve link target content). ``` . ├── afolder │   └── test.txt └── welcome.txt ``` Signed-off-by: Francois Prunayre <fx.prunayre@gmail.com>
1 parent c4f071a commit 36ca73a

1 file changed

Lines changed: 29 additions & 24 deletions

File tree

lib/private/Files/Storage/Local.php

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -92,35 +92,40 @@ public function rmdir(string $path): bool {
9292
return false;
9393
}
9494
try {
95-
$it = new \RecursiveIteratorIterator(
96-
new \RecursiveDirectoryIterator($this->getSourcePath($path)),
97-
\RecursiveIteratorIterator::CHILD_FIRST
98-
);
99-
/**
100-
* RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
101-
* This bug is fixed in PHP 5.5.9 or before
102-
* See #8376
103-
*/
104-
$it->rewind();
105-
while ($it->valid()) {
95+
if(is_link($this->getSourcePath($path))) {
96+
clearstatcache(true, $this->getSourcePath($path));
97+
return unlink($this->getSourcePath($path));
98+
} else {
99+
$it = new \RecursiveIteratorIterator(
100+
new \RecursiveDirectoryIterator($this->getSourcePath($path)),
101+
\RecursiveIteratorIterator::CHILD_FIRST
102+
);
106103
/**
107-
* @var \SplFileInfo $file
104+
* RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
105+
* This bug is fixed in PHP 5.5.9 or before
106+
* See #8376
108107
*/
109-
$file = $it->current();
110-
clearstatcache(true, $file->getRealPath());
111-
if (in_array($file->getBasename(), ['.', '..'])) {
108+
$it->rewind();
109+
while ($it->valid()) {
110+
/**
111+
* @var \SplFileInfo $file
112+
*/
113+
$file = $it->current();
114+
clearstatcache(true, $file->getRealPath());
115+
if (in_array($file->getBasename(), ['.', '..'])) {
116+
$it->next();
117+
continue;
118+
} elseif ($file->isFile() || $file->isLink()) {
119+
unlink($file->getPathname());
120+
} elseif ($file->isDir()) {
121+
rmdir($file->getPathname());
122+
}
112123
$it->next();
113-
continue;
114-
} elseif ($file->isFile() || $file->isLink()) {
115-
unlink($file->getPathname());
116-
} elseif ($file->isDir()) {
117-
rmdir($file->getPathname());
118124
}
119-
$it->next();
125+
unset($it); // Release iterator and thereby its potential directory lock (e.g. in case of VirtualBox shared folders)
126+
clearstatcache(true, $this->getSourcePath($path));
127+
return rmdir($this->getSourcePath($path));
120128
}
121-
unset($it); // Release iterator and thereby its potential directory lock (e.g. in case of VirtualBox shared folders)
122-
clearstatcache(true, $this->getSourcePath($path));
123-
return rmdir($this->getSourcePath($path));
124129
} catch (\UnexpectedValueException $e) {
125130
return false;
126131
}

0 commit comments

Comments
 (0)