|
17 | 17 | use GuzzleHttp\Psr7\Utils; |
18 | 18 | use OC\Files\Stream\SeekableHttpStream; |
19 | 19 | use OCA\DAV\Connector\Sabre\Exception\BadGateway; |
| 20 | +use OCP\Files\EntityTooLargeException; |
20 | 21 | use Psr\Http\Message\StreamInterface; |
21 | 22 |
|
22 | 23 | trait S3ObjectTrait { |
@@ -94,6 +95,24 @@ private function buildS3Metadata(array $metadata): array { |
94 | 95 | return $result; |
95 | 96 | } |
96 | 97 |
|
| 98 | + private function isStorageFullException(\Throwable $e) { |
| 99 | + while ($e !== null) { |
| 100 | + if ($e instanceof AwsException) { |
| 101 | + // MinIO: dedicated error code for storage-full |
| 102 | + if ($e->getAwsErrorCode() === 'XMinioStorageFull') { |
| 103 | + return true; |
| 104 | + } |
| 105 | + // RustFS: returns generic error code but with a recognisable message |
| 106 | + if (str_starts_with($e->getAwsErrorMessage() ?? '', 'Bucket quota exceeded.')) { |
| 107 | + return true; |
| 108 | + } |
| 109 | + } |
| 110 | + $e = $e->getPrevious(); |
| 111 | + } |
| 112 | + |
| 113 | + return false; |
| 114 | + } |
| 115 | + |
97 | 116 | /** |
98 | 117 | * Single object put helper |
99 | 118 | * |
@@ -121,7 +140,14 @@ protected function writeSingle(string $urn, StreamInterface $stream, array $meta |
121 | 140 | $args['ContentLength'] = $size; |
122 | 141 | } |
123 | 142 |
|
124 | | - $this->getConnection()->putObject($args); |
| 143 | + try { |
| 144 | + $this->getConnection()->putObject($args); |
| 145 | + } catch (AwsException $e) { |
| 146 | + if ($this->isStorageFullException($e)) { |
| 147 | + throw new EntityTooLargeException('Quota exceeded on S3 storage', 0, $e); |
| 148 | + } |
| 149 | + throw $e; |
| 150 | + } |
125 | 151 | } |
126 | 152 |
|
127 | 153 |
|
@@ -198,6 +224,10 @@ protected function writeMultiPart(string $urn, StreamInterface $stream, array $m |
198 | 224 | $this->getConnection()->abortMultipartUpload($uploadInfo); |
199 | 225 | } |
200 | 226 |
|
| 227 | + if ($this->isStorageFullException($exception)) { |
| 228 | + throw new EntityTooLargeException('Quota exceeded on S3 storage', 0, $exception); |
| 229 | + } |
| 230 | + |
201 | 231 | throw new BadGateway('Error while uploading to S3 bucket', 0, $exception); |
202 | 232 | } |
203 | 233 | } |
@@ -290,12 +320,24 @@ public function copyObject($from, $to, array $options = []) { |
290 | 320 | 'params' => $this->getServerSideEncryptionParameters() + $this->getServerSideEncryptionParameters(true), |
291 | 321 | 'source_metadata' => $sourceMetadata |
292 | 322 | ], $options)); |
293 | | - $copy->copy(); |
| 323 | + try { |
| 324 | + $copy->copy(); |
| 325 | + } catch (\Throwable $e) { |
| 326 | + if ($this->isStorageFullException($e)) { |
| 327 | + throw new EntityTooLargeException('Quota exceeded on S3 storage', 0, $e); |
| 328 | + } |
| 329 | + } |
294 | 330 | } else { |
295 | | - $this->getConnection()->copy($this->getBucket(), $from, $this->getBucket(), $to, 'private', array_merge([ |
296 | | - 'params' => $this->getServerSideEncryptionParameters() + $this->getServerSideEncryptionParameters(true), |
297 | | - 'mup_threshold' => PHP_INT_MAX, |
298 | | - ], $options)); |
| 331 | + try { |
| 332 | + $this->getConnection()->copy($this->getBucket(), $from, $this->getBucket(), $to, 'private', array_merge([ |
| 333 | + 'params' => $this->getServerSideEncryptionParameters() + $this->getServerSideEncryptionParameters(true), |
| 334 | + 'mup_threshold' => PHP_INT_MAX, |
| 335 | + ], $options)); |
| 336 | + } catch (AwsException $e) { |
| 337 | + if ($this->isStorageFullException($e)) { |
| 338 | + throw new EntityTooLargeException('Quota exceeded on S3 storage', 0, $e); |
| 339 | + } |
| 340 | + } |
299 | 341 | } |
300 | 342 | } |
301 | 343 |
|
|
0 commit comments