Skip to content

Commit aa797a3

Browse files
authored
Merge pull request #59995 from nextcloud/quota-writestream-fopen
2 parents f3ae8ab + 7c4b601 commit aa797a3

4 files changed

Lines changed: 77 additions & 10 deletions

File tree

apps/dav/lib/Connector/Sabre/File.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,9 @@ private function convertToSabreException(\Exception $e) {
621621
if ($e instanceof NotFoundException) {
622622
throw new NotFound($this->l10n->t('File not found: %1$s', [$e->getMessage()]), 0, $e);
623623
}
624+
if ($e instanceof Files\NotEnoughSpaceException) {
625+
throw new EntityTooLarge($this->l10n->t('Insufficient space'), 0, $e);
626+
}
624627

625628
throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e);
626629
}

lib/private/Files/Storage/Wrapper/Quota.php

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use OC\SystemConfig;
1212
use OCP\Files\Cache\ICacheEntry;
1313
use OCP\Files\FileInfo;
14+
use OCP\Files\GenericFileException;
15+
use OCP\Files\NotEnoughSpaceException;
1416
use OCP\Files\Storage\IStorage;
1517

1618
class Quota extends Wrapper {
@@ -120,19 +122,20 @@ public function copy(string $source, string $target): bool {
120122

121123
#[\Override]
122124
public function fopen(string $path, string $mode) {
123-
if (!$this->hasQuota()) {
125+
if (!$this->hasQuota() || $this->isPartFile($path)) {
124126
return $this->getWrapperStorage()->fopen($path, $mode);
125127
}
126-
$source = $this->getWrapperStorage()->fopen($path, $mode);
127128

128-
// don't apply quota for part files
129-
if (!$this->isPartFile($path)) {
130-
$free = $this->free_space($path);
131-
if ($source && (is_int($free) || is_float($free)) && $free >= 0 && $mode !== 'r' && $mode !== 'rb') {
132-
// only apply quota for files, not metadata, trash or others
133-
if ($this->shouldApplyQuota($path)) {
134-
return \OC\Files\Stream\Quota::wrap($source, $free);
135-
}
129+
$free = $this->free_space($path);
130+
if ($this->shouldApplyQuota($path) && $free == 0) {
131+
return false;
132+
}
133+
134+
$source = $this->getWrapperStorage()->fopen($path, $mode);
135+
if ($source && (is_int($free) || is_float($free)) && $free >= 0 && $mode !== 'r' && $mode !== 'rb') {
136+
// only apply quota for files, not metadata, trash or others
137+
if ($this->shouldApplyQuota($path)) {
138+
return \OC\Files\Stream\Quota::wrap($source, $free);
136139
}
137140
}
138141

@@ -213,4 +216,31 @@ public function touch(string $path, ?int $mtime = null): bool {
213216
public function enableQuota(bool $enabled): void {
214217
$this->enabled = $enabled;
215218
}
219+
220+
#[\Override]
221+
public function writeStream(string $path, $stream, ?int $size = null): int {
222+
if (!$this->hasQuota()) {
223+
return parent::writeStream($path, $stream, $size);
224+
}
225+
226+
$free = $this->free_space($path);
227+
if ($this->shouldApplyQuota($path) && $free == 0) {
228+
throw new NotEnoughSpaceException();
229+
}
230+
231+
if ($size !== null) {
232+
if ($size < $free) {
233+
return parent::writeStream($path, $stream, $size);
234+
} else {
235+
throw new NotEnoughSpaceException();
236+
}
237+
} else {
238+
// force fallback through `fopen` to handle the quota
239+
try {
240+
return parent::writeStreamFallback($path, $stream);
241+
} catch (GenericFileException) {
242+
throw new NotEnoughSpaceException();
243+
}
244+
}
245+
}
216246
}

lib/private/Files/Storage/Wrapper/Wrapper.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,13 @@ public function writeStream(string $path, $stream, ?int $size = null): int {
392392
return $storage->writeStream($path, $stream, $size);
393393
}
394394

395+
return $this->writeStreamFallback($path, $stream);
396+
}
397+
398+
/**
399+
* @param resource $stream
400+
*/
401+
protected function writeStreamFallback(string $path, $stream): int {
395402
$target = $this->fopen($path, 'w');
396403
if ($target === false) {
397404
throw new GenericFileException('Failed to open ' . $path);

tests/lib/Files/Storage/Wrapper/QuotaTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,31 @@ public function testNoTouchQuotaZero(): void {
229229
$instance = $this->getLimitedStorage(0.0);
230230
$this->assertFalse($instance->touch('foobar'));
231231
}
232+
233+
public function testNoFopenQuotaZero(): void {
234+
$instance = $this->getLimitedStorage(0.0);
235+
$fh = $instance->fopen('files/test.txt', 'w');
236+
$this->assertFalse($fh);
237+
}
238+
239+
public function testNoWriteStreamQuota(): void {
240+
$instance = $this->getLimitedStorage(5.0);
241+
$stream = fopen('php://temp', 'w+');
242+
fwrite($stream, 'foo');
243+
rewind($stream);
244+
$instance->writeStream('files/test.txt', $stream);
245+
246+
$stream = fopen('php://temp', 'w+');
247+
fwrite($stream, 'foobar');
248+
rewind($stream);
249+
$this->expectException(Files\NotEnoughSpaceException::class);
250+
$instance->writeStream('files/test.txt', $stream);
251+
}
252+
253+
public function testNoWriteStreamQuotaZero(): void {
254+
$instance = $this->getLimitedStorage(0.0);
255+
$stream = fopen('php://temp', 'w+');
256+
$this->expectException(Files\NotEnoughSpaceException::class);
257+
$instance->writeStream('files/test.txt', $stream);
258+
}
232259
}

0 commit comments

Comments
 (0)