diff --git a/lib/private/Files/Storage/Wrapper/Quota.php b/lib/private/Files/Storage/Wrapper/Quota.php index b0ff0117b960f..a7fd91b1f4c6a 100644 --- a/lib/private/Files/Storage/Wrapper/Quota.php +++ b/lib/private/Files/Storage/Wrapper/Quota.php @@ -230,7 +230,7 @@ public function writeStream(string $path, $stream, ?int $size = null): int { } if ($size !== null) { - if ($size < $free) { + if ($free < 0 || $size < $free) { return parent::writeStream($path, $stream, $size); } else { throw new NotEnoughSpaceException(); diff --git a/tests/lib/Files/Storage/Wrapper/QuotaTest.php b/tests/lib/Files/Storage/Wrapper/QuotaTest.php index 150917b9dd9e9..875dfd7b992e1 100644 --- a/tests/lib/Files/Storage/Wrapper/QuotaTest.php +++ b/tests/lib/Files/Storage/Wrapper/QuotaTest.php @@ -13,6 +13,7 @@ use OC\Files\Storage\Local; use OC\Files\Storage\Wrapper\Quota; use OCP\Files; +use OCP\Files\FileInfo; use OCP\Files\NotEnoughSpaceException; use OCP\ITempManager; use OCP\Server; @@ -134,6 +135,34 @@ public function testStreamCopyNotEnoughSpace(): void { fclose($outputStream); } + public function testWriteStreamWithUnlimitedFreeSpaceSucceeds(): void { + // object stores report SPACE_UNLIMITED for quota-exempt uploads/ paths + $storage = $this->getMockBuilder(Local::class) + ->onlyMethods(['free_space']) + ->setConstructorArgs([['datadir' => $this->tmpDir]]) + ->getMock(); + $storage->method('free_space')->willReturn(FileInfo::SPACE_UNLIMITED); + $storage->mkdir('uploads'); + + $instance = new Quota(['storage' => $storage, 'quota' => 9]); + + $stream = fopen('data://text/plain,foobar', 'r'); + $this->assertEquals(6, $instance->writeStream('uploads/foo', $stream, 6)); + fclose($stream); + } + + public function testWriteStreamWithKnownSizeStillEnforcesQuota(): void { + $instance = $this->getLimitedStorage(9); + + $stream = fopen('data://text/plain,foobarqwerty', 'r'); + $this->expectException(NotEnoughSpaceException::class); + try { + $instance->writeStream('files/foo', $stream, 12); + } finally { + fclose($stream); + } + } + public function testReturnFalseWhenFopenFailed(): void { $failStorage = $this->getMockBuilder(Local::class) ->onlyMethods(['fopen'])