Merge pull request #42679 from nextcloud/fix/chunked-quota

fix(quota): automatically detect chunk quota with `OC-Total-Length` header
pull/42705/head
Julius Härtl 5 months ago committed by GitHub
commit f8625cfe60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -31,6 +31,7 @@
namespace OCA\DAV\Connector\Sabre;
use OCA\DAV\Upload\FutureFile;
use OCA\DAV\Upload\UploadFolder;
use OCP\Files\StorageNotAvailableException;
use Sabre\DAV\Exception\InsufficientStorage;
use Sabre\DAV\Exception\ServiceUnavailable;
@ -90,6 +91,19 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
* @param bool $modified modified
*/
public function beforeCreateFile($uri, $data, INode $parent, $modified) {
$request = $this->server->httpRequest;
if ($parent instanceof UploadFolder && $request->getHeader('Destination')) {
// If chunked upload and Total-Length header is set, use that
// value for quota check. This allows us to also check quota while
// uploading chunks and not only when the file is assembled.
$length = $request->getHeader('OC-Total-Length');
$destinationPath = $this->server->calculateUri($request->getHeader('Destination'));
$quotaPath = $this->getPathForDestination($destinationPath);
if ($quotaPath && is_numeric($length)) {
return $this->checkQuota($quotaPath, (int)$length);
}
}
if (!$parent instanceof Node) {
return;
}
@ -114,29 +128,20 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
}
/**
* Check if we're moving a Futurefile in which case we need to check
* Check if we're moving a FutureFile in which case we need to check
* the quota on the target destination.
*
* @param string $source source path
* @param string $destination destination path
*/
public function beforeMove($source, $destination) {
$sourceNode = $this->server->tree->getNodeForPath($source);
public function beforeMove(string $sourcePath, string $destinationPath): bool {
$sourceNode = $this->server->tree->getNodeForPath($sourcePath);
if (!$sourceNode instanceof FutureFile) {
return;
return true;
}
// get target node for proper path conversion
if ($this->server->tree->nodeExists($destination)) {
$destinationNode = $this->server->tree->getNodeForPath($destination);
$path = $destinationNode->getPath();
} else {
$parent = dirname($destination);
if ($parent === '.') {
$parent = '';
}
$parentNode = $this->server->tree->getNodeForPath($parent);
$path = $parentNode->getPath();
try {
// The final path is not known yet, we check the quota on the parent
$path = $this->getPathForDestination($destinationPath);
} catch (\Exception $e) {
return true;
}
return $this->checkQuota($path, $sourceNode->getSize());
@ -151,26 +156,36 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
return true;
}
try {
$path = $this->getPathForDestination($destinationPath);
} catch (\Exception $e) {
return true;
}
return $this->checkQuota($path, $sourceNode->getSize());
}
private function getPathForDestination(string $destinationPath): string {
// get target node for proper path conversion
if ($this->server->tree->nodeExists($destinationPath)) {
$destinationNode = $this->server->tree->getNodeForPath($destinationPath);
if (!$destinationNode instanceof Node) {
return true;
}
$path = $destinationNode->getPath();
} else {
$parent = dirname($destinationPath);
if ($parent === '.') {
$parent = '';
}
$parentNode = $this->server->tree->getNodeForPath($parent);
if (!$parentNode instanceof Node) {
return true;
throw new \Exception('Invalid destination node');
}
$path = $parentNode->getPath();
return $destinationNode->getPath();
}
return $this->checkQuota($path, $sourceNode->getSize());
$parent = dirname($destinationPath);
if ($parent === '.') {
$parent = '';
}
$parentNode = $this->server->tree->getNodeForPath($parent);
if (!$parentNode instanceof Node) {
throw new \Exception('Invalid destination node');
}
return $parentNode->getPath();
}
@ -182,7 +197,7 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
* @throws InsufficientStorage
* @return bool
*/
public function checkQuota($path, $length = null) {
public function checkQuota(string $path, $length = null) {
if ($length === null) {
$length = $this->getLength();
}
@ -194,7 +209,7 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
}
$req = $this->server->httpRequest;
// If chunked upload
// If LEGACY chunked upload
if ($req->getHeader('OC-Chunked')) {
$info = \OC_FileChunking::decodeName($newName);
$chunkHandler = $this->getFileChunking($info);
@ -210,12 +225,14 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
$freeSpace = $this->getFreeSpace($path);
if ($freeSpace >= 0 && $length > $freeSpace) {
// If LEGACY chunked upload, clean up
if (isset($chunkHandler)) {
$chunkHandler->cleanup();
}
throw new InsufficientStorage("Insufficient space in $path, $length required, $freeSpace available");
}
}
return true;
}

Loading…
Cancel
Save