From a45e12abc8881b347475877cd07f8fe33bfe8c77 Mon Sep 17 00:00:00 2001 From: Benjamin Gaussorgues Date: Mon, 23 Oct 2023 10:15:46 +0200 Subject: [PATCH] fix(files_external): on case insensitive system, block case change When a file/directory is renamed to the same name with only case change, the rename fail. We block this kind of rename. The user will have to rename to another name first. Signed-off-by: Benjamin Gaussorgues --- apps/files_external/lib/Lib/Backend/SMB.php | 5 +++++ apps/files_external/lib/Lib/Storage/SMB.php | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/apps/files_external/lib/Lib/Backend/SMB.php b/apps/files_external/lib/Lib/Backend/SMB.php index bf73c5b40f844..6c87967608876 100644 --- a/apps/files_external/lib/Lib/Backend/SMB.php +++ b/apps/files_external/lib/Lib/Backend/SMB.php @@ -59,6 +59,11 @@ public function __construct(IL10N $l, Password $legacyAuth) { (new DefinitionParameter('show_hidden', $l->t('Show hidden files'))) ->setType(DefinitionParameter::VALUE_BOOLEAN) ->setFlag(DefinitionParameter::FLAG_OPTIONAL), + (new DefinitionParameter('case_sensitive', $l->t('Case sensitive file system'))) + ->setType(DefinitionParameter::VALUE_BOOLEAN) + ->setFlag(DefinitionParameter::FLAG_OPTIONAL) + ->setDefaultValue(true) + ->setTooltip($l->t('Disabling it will allow to use a case insentive file system, but comes with a performance penalty')), (new DefinitionParameter('check_acl', $l->t('Verify ACL access when listing files'))) ->setType(DefinitionParameter::VALUE_BOOLEAN) ->setFlag(DefinitionParameter::FLAG_OPTIONAL) diff --git a/apps/files_external/lib/Lib/Storage/SMB.php b/apps/files_external/lib/Lib/Storage/SMB.php index c3ccc106239cf..2c0a412d341e8 100644 --- a/apps/files_external/lib/Lib/Storage/SMB.php +++ b/apps/files_external/lib/Lib/Storage/SMB.php @@ -93,6 +93,8 @@ class SMB extends Common implements INotifyStorage { /** @var bool */ protected $showHidden; + private bool $caseSensitive; + /** @var bool */ protected $checkAcl; @@ -139,6 +141,7 @@ public function __construct($params) { $this->root = rtrim($this->root, '/') . '/'; $this->showHidden = isset($params['show_hidden']) && $params['show_hidden']; + $this->caseSensitive = (bool) ($params['case_sensitive'] ?? true); $this->checkAcl = isset($params['check_acl']) && $params['check_acl']; $this->statCache = new CappedMemoryCache(); @@ -325,6 +328,12 @@ public function rename($source, $target, $retry = true): bool { if ($this->isRootDir($source) || $this->isRootDir($target)) { return false; } + if ($this->caseSensitive === false + && mb_strtolower($target) === mb_strtolower($source) + ) { + // Forbid changing case only on case-insensitive file system + return false; + } $absoluteSource = $this->buildPath($source); $absoluteTarget = $this->buildPath($target); @@ -674,6 +683,16 @@ public function mkdir($path) { public function file_exists($path) { try { + if ($this->caseSensitive === false) { + $filename = basename($path); + $siblings = $this->getDirectoryContent(dirname($this->buildPath($path))); + foreach ($siblings as $sibling) { + if ($sibling['name'] === $filename) { + return true; + } + } + return false; + } $this->getFileInfo($path); return true; } catch (\OCP\Files\NotFoundException $e) {