Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/dav/lib/Connector/Sabre/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,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) {
Comment thread
kyteinsky marked this conversation as resolved.
Outdated
throw new EntityTooLarge($this->l10n->t('Insufficient space'), 0, $e);
}

throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e);
}
Expand Down
51 changes: 41 additions & 10 deletions lib/private/Files/Storage/Wrapper/Quota.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -116,19 +118,21 @@ public function copy(string $source, string $target): bool {
}

public function fopen(string $path, string $mode) {
if (!$this->hasQuota()) {
if (!$this->hasQuota() || $this->isPartFile($path)) {
return $this->storage->fopen($path, $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;
}

// todo: storage or getWrapperStorage() ?
$source = $this->getWrapperStorage()->fopen($path, $mode);
Comment thread
kyteinsky marked this conversation as resolved.
Outdated
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);
}
}

Expand Down Expand Up @@ -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();
}
}
}
}
29 changes: 22 additions & 7 deletions lib/private/Files/Storage/Wrapper/Wrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@

use OC\Files\Storage\FailedStorage;
use OC\Files\Storage\Storage;
use OCP\Files;
use OCP\Files\Cache\ICache;
use OCP\Files\Cache\IPropagator;
use OCP\Files\Cache\IScanner;
use OCP\Files\Cache\IUpdater;
use OCP\Files\Cache\IWatcher;
use OCP\Files\GenericFileException;
use OCP\Files\Storage\ILockingStorage;
use OCP\Files\Storage\IStorage;
use OCP\Files\Storage\IWriteStreamStorage;
Expand Down Expand Up @@ -321,13 +321,28 @@ public function writeStream(string $path, $stream, ?int $size = null): int {
if ($storage->instanceOfStorage(IWriteStreamStorage::class)) {
/** @var IWriteStreamStorage $storage */
return $storage->writeStream($path, $stream, $size);
} else {
$target = $this->fopen($path, 'w');
$count = Files::streamCopy($stream, $target);
fclose($stream);
fclose($target);
return $count;
}

return $this->writeStreamFallback($path, $stream);
}

/**
* @param resource $stream
*/
protected function writeStreamFallback(string $path, $stream): int {
$target = $this->fopen($path, 'w');
if ($target === false) {
throw new GenericFileException('Failed to open ' . $path);
}

$count = stream_copy_to_stream($stream, $target);
fclose($stream);
fclose($target);
if ($count === false) {
throw new GenericFileException('Failed to copy stream.');
}

return $count;
}

public function getDirectoryContent(string $directory): \Traversable {
Expand Down
27 changes: 27 additions & 0 deletions tests/lib/Files/Storage/Wrapper/QuotaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Loading