Skip to content

Commit 63ebd2b

Browse files
fxprunayrejoshtrichards
authored andcommitted
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 13ae34a commit 63ebd2b

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
@@ -100,35 +100,40 @@ public function rmdir(string $path): bool {
100100
return false;
101101
}
102102
try {
103-
$it = new \RecursiveIteratorIterator(
104-
new \RecursiveDirectoryIterator($this->getSourcePath($path)),
105-
\RecursiveIteratorIterator::CHILD_FIRST
106-
);
107-
/**
108-
* RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
109-
* This bug is fixed in PHP 5.5.9 or before
110-
* See #8376
111-
*/
112-
$it->rewind();
113-
while ($it->valid()) {
103+
if (is_link($this->getSourcePath($path))) {
104+
clearstatcache(true, $this->getSourcePath($path));
105+
return unlink($this->getSourcePath($path));
106+
} else {
107+
$it = new \RecursiveIteratorIterator(
108+
new \RecursiveDirectoryIterator($this->getSourcePath($path)),
109+
\RecursiveIteratorIterator::CHILD_FIRST
110+
);
114111
/**
115-
* @var \SplFileInfo $file
112+
* RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
113+
* This bug is fixed in PHP 5.5.9 or before
114+
* See #8376
116115
*/
117-
$file = $it->current();
118-
clearstatcache(true, $file->getRealPath());
119-
if (in_array($file->getBasename(), ['.', '..'])) {
116+
$it->rewind();
117+
while ($it->valid()) {
118+
/**
119+
* @var \SplFileInfo $file
120+
*/
121+
$file = $it->current();
122+
clearstatcache(true, $file->getRealPath());
123+
if (in_array($file->getBasename(), ['.', '..'])) {
124+
$it->next();
125+
continue;
126+
} elseif ($file->isFile() || $file->isLink()) {
127+
unlink($file->getPathname());
128+
} elseif ($file->isDir()) {
129+
rmdir($file->getPathname());
130+
}
120131
$it->next();
121-
continue;
122-
} elseif ($file->isFile() || $file->isLink()) {
123-
unlink($file->getPathname());
124-
} elseif ($file->isDir()) {
125-
rmdir($file->getPathname());
126132
}
127-
$it->next();
133+
unset($it); // Release iterator and thereby its potential directory lock (e.g. in case of VirtualBox shared folders)
134+
clearstatcache(true, $this->getSourcePath($path));
135+
return rmdir($this->getSourcePath($path));
128136
}
129-
unset($it); // Release iterator and thereby its potential directory lock (e.g. in case of VirtualBox shared folders)
130-
clearstatcache(true, $this->getSourcePath($path));
131-
return rmdir($this->getSourcePath($path));
132137
} catch (\UnexpectedValueException $e) {
133138
return false;
134139
}

0 commit comments

Comments
 (0)