diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php index 75baff228d6b4..5333cfc773810 100644 --- a/apps/dav/lib/Connector/Sabre/File.php +++ b/apps/dav/lib/Connector/Sabre/File.php @@ -611,6 +611,9 @@ private function convertToSabreException(\Exception $e) { if ($e instanceof NotFoundException) { throw new NotFound($this->l10n->t('File not found: %1$s', [$e->getMessage()]), 0, $e); } + if ($e instanceof Files\NotEnoughSpaceException) { + throw new EntityTooLarge($this->l10n->t('Insufficient space'), 0, $e); + } throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e); } diff --git a/lib/private/Files/Storage/Wrapper/Quota.php b/lib/private/Files/Storage/Wrapper/Quota.php index 35a265f8c8e72..827048f4fe479 100644 --- a/lib/private/Files/Storage/Wrapper/Quota.php +++ b/lib/private/Files/Storage/Wrapper/Quota.php @@ -11,6 +11,8 @@ use OC\SystemConfig; use OCP\Files\Cache\ICacheEntry; use OCP\Files\FileInfo; +use OCP\Files\GenericFileException; +use OCP\Files\NotEnoughSpaceException; use OCP\Files\Storage\IStorage; class Quota extends Wrapper { @@ -121,14 +123,16 @@ public function fopen(string $path, string $mode) { } $source = $this->storage->fopen($path, $mode); - // don't apply quota for part files - if (!$this->isPartFile($path)) { - $free = $this->free_space($path); - if ($source && (is_int($free) || is_float($free)) && $free >= 0 && $mode !== 'r' && $mode !== 'rb') { - // only apply quota for files, not metadata, trash or others - if ($this->shouldApplyQuota($path)) { - return \OC\Files\Stream\Quota::wrap($source, $free); - } + $free = $this->free_space($path); + if ($this->shouldApplyQuota($path) && $free == 0) { + return false; + } + + $source = $this->getWrapperStorage()->fopen($path, $mode); + if ($source && (is_int($free) || is_float($free)) && $free >= 0 && $mode !== 'r' && $mode !== 'rb') { + // only apply quota for files, not metadata, trash or others + if ($this->shouldApplyQuota($path)) { + return \OC\Files\Stream\Quota::wrap($source, $free); } } @@ -205,4 +209,31 @@ public function touch(string $path, ?int $mtime = null): bool { public function enableQuota(bool $enabled): void { $this->enabled = $enabled; } + + #[\Override] + public function writeStream(string $path, $stream, ?int $size = null): int { + if (!$this->hasQuota()) { + return parent::writeStream($path, $stream, $size); + } + + $free = $this->free_space($path); + if ($this->shouldApplyQuota($path) && $free == 0) { + throw new NotEnoughSpaceException(); + } + + if ($size !== null) { + if ($size < $free) { + return parent::writeStream($path, $stream, $size); + } else { + throw new NotEnoughSpaceException(); + } + } else { + // force fallback through `fopen` to handle the quota + try { + return parent::writeStreamFallback($path, $stream); + } catch (GenericFileException) { + throw new NotEnoughSpaceException(); + } + } + } } diff --git a/tests/lib/Files/Storage/Wrapper/QuotaTest.php b/tests/lib/Files/Storage/Wrapper/QuotaTest.php index 3d313666a93b0..cf939d0eb1064 100644 --- a/tests/lib/Files/Storage/Wrapper/QuotaTest.php +++ b/tests/lib/Files/Storage/Wrapper/QuotaTest.php @@ -229,4 +229,31 @@ public function testNoTouchQuotaZero(): void { $instance = $this->getLimitedStorage(0.0); $this->assertFalse($instance->touch('foobar')); } + + public function testNoFopenQuotaZero(): void { + $instance = $this->getLimitedStorage(0.0); + $fh = $instance->fopen('files/test.txt', 'w'); + $this->assertFalse($fh); + } + + public function testNoWriteStreamQuota(): void { + $instance = $this->getLimitedStorage(5.0); + $stream = fopen('php://temp', 'w+'); + fwrite($stream, 'foo'); + rewind($stream); + $instance->writeStream('files/test.txt', $stream); + + $stream = fopen('php://temp', 'w+'); + fwrite($stream, 'foobar'); + rewind($stream); + $this->expectException(Files\NotEnoughSpaceException::class); + $instance->writeStream('files/test.txt', $stream); + } + + public function testNoWriteStreamQuotaZero(): void { + $instance = $this->getLimitedStorage(0.0); + $stream = fopen('php://temp', 'w+'); + $this->expectException(Files\NotEnoughSpaceException::class); + $instance->writeStream('files/test.txt', $stream); + } }