Passed
Push — master ( 422044...b2e342 )
by Blizzz
18:52 queued 13s
created
lib/private/Files/ObjectStore/ObjectStoreStorage.php 2 patches
Indentation   +677 added lines, -677 removed lines patch added patch discarded remove patch
@@ -48,684 +48,684 @@
 block discarded – undo
48 48
 use OCP\Files\Storage\IStorage;
49 49
 
50 50
 class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFileWrite {
51
-	use CopyDirectory;
52
-
53
-	/**
54
-	 * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
55
-	 */
56
-	protected $objectStore;
57
-	/**
58
-	 * @var string $id
59
-	 */
60
-	protected $id;
61
-	/**
62
-	 * @var \OC\User\User $user
63
-	 */
64
-	protected $user;
65
-
66
-	private $objectPrefix = 'urn:oid:';
67
-
68
-	private $logger;
69
-
70
-	/** @var bool */
71
-	protected $validateWrites = true;
72
-
73
-	public function __construct($params) {
74
-		if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
75
-			$this->objectStore = $params['objectstore'];
76
-		} else {
77
-			throw new \Exception('missing IObjectStore instance');
78
-		}
79
-		if (isset($params['storageid'])) {
80
-			$this->id = 'object::store:' . $params['storageid'];
81
-		} else {
82
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
83
-		}
84
-		if (isset($params['objectPrefix'])) {
85
-			$this->objectPrefix = $params['objectPrefix'];
86
-		}
87
-		if (isset($params['validateWrites'])) {
88
-			$this->validateWrites = (bool)$params['validateWrites'];
89
-		}
90
-
91
-		$this->logger = \OC::$server->getLogger();
92
-	}
93
-
94
-	public function mkdir($path, bool $force = false) {
95
-		$path = $this->normalizePath($path);
96
-		if (!$force && $this->file_exists($path)) {
97
-			$this->logger->warning("Tried to create an object store folder that already exists: $path");
98
-			return false;
99
-		}
100
-
101
-		$mTime = time();
102
-		$data = [
103
-			'mimetype' => 'httpd/unix-directory',
104
-			'size' => 0,
105
-			'mtime' => $mTime,
106
-			'storage_mtime' => $mTime,
107
-			'permissions' => \OCP\Constants::PERMISSION_ALL,
108
-		];
109
-		if ($path === '') {
110
-			//create root on the fly
111
-			$data['etag'] = $this->getETag('');
112
-			$this->getCache()->put('', $data);
113
-			return true;
114
-		} else {
115
-			// if parent does not exist, create it
116
-			$parent = $this->normalizePath(dirname($path));
117
-			$parentType = $this->filetype($parent);
118
-			if ($parentType === false) {
119
-				if (!$this->mkdir($parent)) {
120
-					// something went wrong
121
-					$this->logger->warning("Parent folder ($parent) doesn't exist and couldn't be created");
122
-					return false;
123
-				}
124
-			} elseif ($parentType === 'file') {
125
-				// parent is a file
126
-				$this->logger->warning("Parent ($parent) is a file");
127
-				return false;
128
-			}
129
-			// finally create the new dir
130
-			$mTime = time(); // update mtime
131
-			$data['mtime'] = $mTime;
132
-			$data['storage_mtime'] = $mTime;
133
-			$data['etag'] = $this->getETag($path);
134
-			$this->getCache()->put($path, $data);
135
-			return true;
136
-		}
137
-	}
138
-
139
-	/**
140
-	 * @param string $path
141
-	 * @return string
142
-	 */
143
-	private function normalizePath($path) {
144
-		$path = trim($path, '/');
145
-		//FIXME why do we sometimes get a path like 'files//username'?
146
-		$path = str_replace('//', '/', $path);
147
-
148
-		// dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
149
-		if (!$path || $path === '.') {
150
-			$path = '';
151
-		}
152
-
153
-		return $path;
154
-	}
155
-
156
-	/**
157
-	 * Object Stores use a NoopScanner because metadata is directly stored in
158
-	 * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
159
-	 *
160
-	 * @param string $path
161
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
162
-	 * @return \OC\Files\ObjectStore\ObjectStoreScanner
163
-	 */
164
-	public function getScanner($path = '', $storage = null) {
165
-		if (!$storage) {
166
-			$storage = $this;
167
-		}
168
-		if (!isset($this->scanner)) {
169
-			$this->scanner = new ObjectStoreScanner($storage);
170
-		}
171
-		return $this->scanner;
172
-	}
173
-
174
-	public function getId() {
175
-		return $this->id;
176
-	}
177
-
178
-	public function rmdir($path) {
179
-		$path = $this->normalizePath($path);
180
-		$entry = $this->getCache()->get($path);
181
-
182
-		if (!$entry || $entry->getMimeType() !== ICacheEntry::DIRECTORY_MIMETYPE) {
183
-			return false;
184
-		}
185
-
186
-		return $this->rmObjects($entry);
187
-	}
188
-
189
-	private function rmObjects(ICacheEntry $entry): bool {
190
-		$children = $this->getCache()->getFolderContentsById($entry->getId());
191
-		foreach ($children as $child) {
192
-			if ($child->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
193
-				if (!$this->rmObjects($child)) {
194
-					return false;
195
-				}
196
-			} else {
197
-				if (!$this->rmObject($child)) {
198
-					return false;
199
-				}
200
-			}
201
-		}
202
-
203
-		$this->getCache()->remove($entry->getPath());
204
-
205
-		return true;
206
-	}
207
-
208
-	public function unlink($path) {
209
-		$path = $this->normalizePath($path);
210
-		$entry = $this->getCache()->get($path);
211
-
212
-		if ($entry instanceof ICacheEntry) {
213
-			if ($entry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
214
-				return $this->rmObjects($entry);
215
-			} else {
216
-				return $this->rmObject($entry);
217
-			}
218
-		}
219
-		return false;
220
-	}
221
-
222
-	public function rmObject(ICacheEntry $entry): bool {
223
-		try {
224
-			$this->objectStore->deleteObject($this->getURN($entry->getId()));
225
-		} catch (\Exception $ex) {
226
-			if ($ex->getCode() !== 404) {
227
-				$this->logger->logException($ex, [
228
-					'app' => 'objectstore',
229
-					'message' => 'Could not delete object ' . $this->getURN($entry->getId()) . ' for ' . $entry->getPath(),
230
-				]);
231
-				return false;
232
-			}
233
-			//removing from cache is ok as it does not exist in the objectstore anyway
234
-		}
235
-		$this->getCache()->remove($entry->getPath());
236
-		return true;
237
-	}
238
-
239
-	public function stat($path) {
240
-		$path = $this->normalizePath($path);
241
-		$cacheEntry = $this->getCache()->get($path);
242
-		if ($cacheEntry instanceof CacheEntry) {
243
-			return $cacheEntry->getData();
244
-		} else {
245
-			if ($path === '') {
246
-				$this->mkdir('', true);
247
-				$cacheEntry = $this->getCache()->get($path);
248
-				if ($cacheEntry instanceof CacheEntry) {
249
-					return $cacheEntry->getData();
250
-				}
251
-			}
252
-			return false;
253
-		}
254
-	}
255
-
256
-	public function getPermissions($path) {
257
-		$stat = $this->stat($path);
258
-
259
-		if (is_array($stat) && isset($stat['permissions'])) {
260
-			return $stat['permissions'];
261
-		}
262
-
263
-		return parent::getPermissions($path);
264
-	}
265
-
266
-	/**
267
-	 * Override this method if you need a different unique resource identifier for your object storage implementation.
268
-	 * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
269
-	 * You may need a mapping table to store your URN if it cannot be generated from the fileid.
270
-	 *
271
-	 * @param int $fileId the fileid
272
-	 * @return null|string the unified resource name used to identify the object
273
-	 */
274
-	public function getURN($fileId) {
275
-		if (is_numeric($fileId)) {
276
-			return $this->objectPrefix . $fileId;
277
-		}
278
-		return null;
279
-	}
280
-
281
-	public function opendir($path) {
282
-		$path = $this->normalizePath($path);
283
-
284
-		try {
285
-			$files = [];
286
-			$folderContents = $this->getCache()->getFolderContents($path);
287
-			foreach ($folderContents as $file) {
288
-				$files[] = $file['name'];
289
-			}
290
-
291
-			return IteratorDirectory::wrap($files);
292
-		} catch (\Exception $e) {
293
-			$this->logger->logException($e);
294
-			return false;
295
-		}
296
-	}
297
-
298
-	public function filetype($path) {
299
-		$path = $this->normalizePath($path);
300
-		$stat = $this->stat($path);
301
-		if ($stat) {
302
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
303
-				return 'dir';
304
-			}
305
-			return 'file';
306
-		} else {
307
-			return false;
308
-		}
309
-	}
310
-
311
-	public function fopen($path, $mode) {
312
-		$path = $this->normalizePath($path);
313
-
314
-		if (strrpos($path, '.') !== false) {
315
-			$ext = substr($path, strrpos($path, '.'));
316
-		} else {
317
-			$ext = '';
318
-		}
319
-
320
-		switch ($mode) {
321
-			case 'r':
322
-			case 'rb':
323
-				$stat = $this->stat($path);
324
-				if (is_array($stat)) {
325
-					$filesize = $stat['size'] ?? 0;
326
-					// Reading 0 sized files is a waste of time
327
-					if ($filesize === 0) {
328
-						return fopen('php://memory', $mode);
329
-					}
330
-
331
-					try {
332
-						$handle = $this->objectStore->readObject($this->getURN($stat['fileid']));
333
-						if ($handle === false) {
334
-							return false; // keep backward compatibility
335
-						}
336
-						$streamStat = fstat($handle);
337
-						$actualSize = $streamStat['size'] ?? -1;
338
-						if ($actualSize > -1 && $actualSize !== $filesize) {
339
-							$this->getCache()->update((int)$stat['fileid'], ['size' => $actualSize]);
340
-						}
341
-						return $handle;
342
-					} catch (NotFoundException $e) {
343
-						$this->logger->logException($e, [
344
-							'app' => 'objectstore',
345
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
346
-						]);
347
-						throw $e;
348
-					} catch (\Exception $ex) {
349
-						$this->logger->logException($ex, [
350
-							'app' => 'objectstore',
351
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
352
-						]);
353
-						return false;
354
-					}
355
-				} else {
356
-					return false;
357
-				}
358
-				// no break
359
-			case 'w':
360
-			case 'wb':
361
-			case 'w+':
362
-			case 'wb+':
363
-				$dirName = dirname($path);
364
-				$parentExists = $this->is_dir($dirName);
365
-				if (!$parentExists) {
366
-					return false;
367
-				}
368
-
369
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
370
-				$handle = fopen($tmpFile, $mode);
371
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
372
-					$this->writeBack($tmpFile, $path);
373
-					unlink($tmpFile);
374
-				});
375
-			case 'a':
376
-			case 'ab':
377
-			case 'r+':
378
-			case 'a+':
379
-			case 'x':
380
-			case 'x+':
381
-			case 'c':
382
-			case 'c+':
383
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
384
-				if ($this->file_exists($path)) {
385
-					$source = $this->fopen($path, 'r');
386
-					file_put_contents($tmpFile, $source);
387
-				}
388
-				$handle = fopen($tmpFile, $mode);
389
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
390
-					$this->writeBack($tmpFile, $path);
391
-					unlink($tmpFile);
392
-				});
393
-		}
394
-		return false;
395
-	}
396
-
397
-	public function file_exists($path) {
398
-		$path = $this->normalizePath($path);
399
-		return (bool)$this->stat($path);
400
-	}
401
-
402
-	public function rename($source, $target) {
403
-		$source = $this->normalizePath($source);
404
-		$target = $this->normalizePath($target);
405
-		$this->remove($target);
406
-		$this->getCache()->move($source, $target);
407
-		$this->touch(dirname($target));
408
-		return true;
409
-	}
410
-
411
-	public function getMimeType($path) {
412
-		$path = $this->normalizePath($path);
413
-		return parent::getMimeType($path);
414
-	}
415
-
416
-	public function touch($path, $mtime = null) {
417
-		if (is_null($mtime)) {
418
-			$mtime = time();
419
-		}
420
-
421
-		$path = $this->normalizePath($path);
422
-		$dirName = dirname($path);
423
-		$parentExists = $this->is_dir($dirName);
424
-		if (!$parentExists) {
425
-			return false;
426
-		}
427
-
428
-		$stat = $this->stat($path);
429
-		if (is_array($stat)) {
430
-			// update existing mtime in db
431
-			$stat['mtime'] = $mtime;
432
-			$this->getCache()->update($stat['fileid'], $stat);
433
-		} else {
434
-			try {
435
-				//create a empty file, need to have at least on char to make it
436
-				// work with all object storage implementations
437
-				$this->file_put_contents($path, ' ');
438
-				$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
439
-				$stat = [
440
-					'etag' => $this->getETag($path),
441
-					'mimetype' => $mimeType,
442
-					'size' => 0,
443
-					'mtime' => $mtime,
444
-					'storage_mtime' => $mtime,
445
-					'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
446
-				];
447
-				$this->getCache()->put($path, $stat);
448
-			} catch (\Exception $ex) {
449
-				$this->logger->logException($ex, [
450
-					'app' => 'objectstore',
451
-					'message' => 'Could not create object for ' . $path,
452
-				]);
453
-				throw $ex;
454
-			}
455
-		}
456
-		return true;
457
-	}
458
-
459
-	public function writeBack($tmpFile, $path) {
460
-		$size = filesize($tmpFile);
461
-		$this->writeStream($path, fopen($tmpFile, 'r'), $size);
462
-	}
463
-
464
-	/**
465
-	 * external changes are not supported, exclusive access to the object storage is assumed
466
-	 *
467
-	 * @param string $path
468
-	 * @param int $time
469
-	 * @return false
470
-	 */
471
-	public function hasUpdated($path, $time) {
472
-		return false;
473
-	}
474
-
475
-	public function needsPartFile() {
476
-		return false;
477
-	}
478
-
479
-	public function file_put_contents($path, $data) {
480
-		$handle = $this->fopen($path, 'w+');
481
-		if (!$handle) {
482
-			return false;
483
-		}
484
-		$result = fwrite($handle, $data);
485
-		fclose($handle);
486
-		return $result;
487
-	}
488
-
489
-	public function writeStream(string $path, $stream, int $size = null): int {
490
-		$stat = $this->stat($path);
491
-		if (empty($stat)) {
492
-			// create new file
493
-			$stat = [
494
-				'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
495
-			];
496
-		}
497
-		// update stat with new data
498
-		$mTime = time();
499
-		$stat['size'] = (int)$size;
500
-		$stat['mtime'] = $mTime;
501
-		$stat['storage_mtime'] = $mTime;
502
-
503
-		$mimetypeDetector = \OC::$server->getMimeTypeDetector();
504
-		$mimetype = $mimetypeDetector->detectPath($path);
505
-
506
-		$stat['mimetype'] = $mimetype;
507
-		$stat['etag'] = $this->getETag($path);
508
-		$stat['checksum'] = '';
509
-
510
-		$exists = $this->getCache()->inCache($path);
511
-		$uploadPath = $exists ? $path : $path . '.part';
512
-
513
-		if ($exists) {
514
-			$fileId = $stat['fileid'];
515
-		} else {
516
-			$fileId = $this->getCache()->put($uploadPath, $stat);
517
-		}
518
-
519
-		$urn = $this->getURN($fileId);
520
-		try {
521
-			//upload to object storage
522
-			if ($size === null) {
523
-				$countStream = CountWrapper::wrap($stream, function ($writtenSize) use ($fileId, &$size) {
524
-					$this->getCache()->update($fileId, [
525
-						'size' => $writtenSize,
526
-					]);
527
-					$size = $writtenSize;
528
-				});
529
-				$this->objectStore->writeObject($urn, $countStream, $mimetype);
530
-				if (is_resource($countStream)) {
531
-					fclose($countStream);
532
-				}
533
-				$stat['size'] = $size;
534
-			} else {
535
-				$this->objectStore->writeObject($urn, $stream, $mimetype);
536
-				if (is_resource($stream)) {
537
-					fclose($stream);
538
-				}
539
-			}
540
-		} catch (\Exception $ex) {
541
-			if (!$exists) {
542
-				/*
51
+    use CopyDirectory;
52
+
53
+    /**
54
+     * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
55
+     */
56
+    protected $objectStore;
57
+    /**
58
+     * @var string $id
59
+     */
60
+    protected $id;
61
+    /**
62
+     * @var \OC\User\User $user
63
+     */
64
+    protected $user;
65
+
66
+    private $objectPrefix = 'urn:oid:';
67
+
68
+    private $logger;
69
+
70
+    /** @var bool */
71
+    protected $validateWrites = true;
72
+
73
+    public function __construct($params) {
74
+        if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
75
+            $this->objectStore = $params['objectstore'];
76
+        } else {
77
+            throw new \Exception('missing IObjectStore instance');
78
+        }
79
+        if (isset($params['storageid'])) {
80
+            $this->id = 'object::store:' . $params['storageid'];
81
+        } else {
82
+            $this->id = 'object::store:' . $this->objectStore->getStorageId();
83
+        }
84
+        if (isset($params['objectPrefix'])) {
85
+            $this->objectPrefix = $params['objectPrefix'];
86
+        }
87
+        if (isset($params['validateWrites'])) {
88
+            $this->validateWrites = (bool)$params['validateWrites'];
89
+        }
90
+
91
+        $this->logger = \OC::$server->getLogger();
92
+    }
93
+
94
+    public function mkdir($path, bool $force = false) {
95
+        $path = $this->normalizePath($path);
96
+        if (!$force && $this->file_exists($path)) {
97
+            $this->logger->warning("Tried to create an object store folder that already exists: $path");
98
+            return false;
99
+        }
100
+
101
+        $mTime = time();
102
+        $data = [
103
+            'mimetype' => 'httpd/unix-directory',
104
+            'size' => 0,
105
+            'mtime' => $mTime,
106
+            'storage_mtime' => $mTime,
107
+            'permissions' => \OCP\Constants::PERMISSION_ALL,
108
+        ];
109
+        if ($path === '') {
110
+            //create root on the fly
111
+            $data['etag'] = $this->getETag('');
112
+            $this->getCache()->put('', $data);
113
+            return true;
114
+        } else {
115
+            // if parent does not exist, create it
116
+            $parent = $this->normalizePath(dirname($path));
117
+            $parentType = $this->filetype($parent);
118
+            if ($parentType === false) {
119
+                if (!$this->mkdir($parent)) {
120
+                    // something went wrong
121
+                    $this->logger->warning("Parent folder ($parent) doesn't exist and couldn't be created");
122
+                    return false;
123
+                }
124
+            } elseif ($parentType === 'file') {
125
+                // parent is a file
126
+                $this->logger->warning("Parent ($parent) is a file");
127
+                return false;
128
+            }
129
+            // finally create the new dir
130
+            $mTime = time(); // update mtime
131
+            $data['mtime'] = $mTime;
132
+            $data['storage_mtime'] = $mTime;
133
+            $data['etag'] = $this->getETag($path);
134
+            $this->getCache()->put($path, $data);
135
+            return true;
136
+        }
137
+    }
138
+
139
+    /**
140
+     * @param string $path
141
+     * @return string
142
+     */
143
+    private function normalizePath($path) {
144
+        $path = trim($path, '/');
145
+        //FIXME why do we sometimes get a path like 'files//username'?
146
+        $path = str_replace('//', '/', $path);
147
+
148
+        // dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
149
+        if (!$path || $path === '.') {
150
+            $path = '';
151
+        }
152
+
153
+        return $path;
154
+    }
155
+
156
+    /**
157
+     * Object Stores use a NoopScanner because metadata is directly stored in
158
+     * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
159
+     *
160
+     * @param string $path
161
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
162
+     * @return \OC\Files\ObjectStore\ObjectStoreScanner
163
+     */
164
+    public function getScanner($path = '', $storage = null) {
165
+        if (!$storage) {
166
+            $storage = $this;
167
+        }
168
+        if (!isset($this->scanner)) {
169
+            $this->scanner = new ObjectStoreScanner($storage);
170
+        }
171
+        return $this->scanner;
172
+    }
173
+
174
+    public function getId() {
175
+        return $this->id;
176
+    }
177
+
178
+    public function rmdir($path) {
179
+        $path = $this->normalizePath($path);
180
+        $entry = $this->getCache()->get($path);
181
+
182
+        if (!$entry || $entry->getMimeType() !== ICacheEntry::DIRECTORY_MIMETYPE) {
183
+            return false;
184
+        }
185
+
186
+        return $this->rmObjects($entry);
187
+    }
188
+
189
+    private function rmObjects(ICacheEntry $entry): bool {
190
+        $children = $this->getCache()->getFolderContentsById($entry->getId());
191
+        foreach ($children as $child) {
192
+            if ($child->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
193
+                if (!$this->rmObjects($child)) {
194
+                    return false;
195
+                }
196
+            } else {
197
+                if (!$this->rmObject($child)) {
198
+                    return false;
199
+                }
200
+            }
201
+        }
202
+
203
+        $this->getCache()->remove($entry->getPath());
204
+
205
+        return true;
206
+    }
207
+
208
+    public function unlink($path) {
209
+        $path = $this->normalizePath($path);
210
+        $entry = $this->getCache()->get($path);
211
+
212
+        if ($entry instanceof ICacheEntry) {
213
+            if ($entry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
214
+                return $this->rmObjects($entry);
215
+            } else {
216
+                return $this->rmObject($entry);
217
+            }
218
+        }
219
+        return false;
220
+    }
221
+
222
+    public function rmObject(ICacheEntry $entry): bool {
223
+        try {
224
+            $this->objectStore->deleteObject($this->getURN($entry->getId()));
225
+        } catch (\Exception $ex) {
226
+            if ($ex->getCode() !== 404) {
227
+                $this->logger->logException($ex, [
228
+                    'app' => 'objectstore',
229
+                    'message' => 'Could not delete object ' . $this->getURN($entry->getId()) . ' for ' . $entry->getPath(),
230
+                ]);
231
+                return false;
232
+            }
233
+            //removing from cache is ok as it does not exist in the objectstore anyway
234
+        }
235
+        $this->getCache()->remove($entry->getPath());
236
+        return true;
237
+    }
238
+
239
+    public function stat($path) {
240
+        $path = $this->normalizePath($path);
241
+        $cacheEntry = $this->getCache()->get($path);
242
+        if ($cacheEntry instanceof CacheEntry) {
243
+            return $cacheEntry->getData();
244
+        } else {
245
+            if ($path === '') {
246
+                $this->mkdir('', true);
247
+                $cacheEntry = $this->getCache()->get($path);
248
+                if ($cacheEntry instanceof CacheEntry) {
249
+                    return $cacheEntry->getData();
250
+                }
251
+            }
252
+            return false;
253
+        }
254
+    }
255
+
256
+    public function getPermissions($path) {
257
+        $stat = $this->stat($path);
258
+
259
+        if (is_array($stat) && isset($stat['permissions'])) {
260
+            return $stat['permissions'];
261
+        }
262
+
263
+        return parent::getPermissions($path);
264
+    }
265
+
266
+    /**
267
+     * Override this method if you need a different unique resource identifier for your object storage implementation.
268
+     * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
269
+     * You may need a mapping table to store your URN if it cannot be generated from the fileid.
270
+     *
271
+     * @param int $fileId the fileid
272
+     * @return null|string the unified resource name used to identify the object
273
+     */
274
+    public function getURN($fileId) {
275
+        if (is_numeric($fileId)) {
276
+            return $this->objectPrefix . $fileId;
277
+        }
278
+        return null;
279
+    }
280
+
281
+    public function opendir($path) {
282
+        $path = $this->normalizePath($path);
283
+
284
+        try {
285
+            $files = [];
286
+            $folderContents = $this->getCache()->getFolderContents($path);
287
+            foreach ($folderContents as $file) {
288
+                $files[] = $file['name'];
289
+            }
290
+
291
+            return IteratorDirectory::wrap($files);
292
+        } catch (\Exception $e) {
293
+            $this->logger->logException($e);
294
+            return false;
295
+        }
296
+    }
297
+
298
+    public function filetype($path) {
299
+        $path = $this->normalizePath($path);
300
+        $stat = $this->stat($path);
301
+        if ($stat) {
302
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
303
+                return 'dir';
304
+            }
305
+            return 'file';
306
+        } else {
307
+            return false;
308
+        }
309
+    }
310
+
311
+    public function fopen($path, $mode) {
312
+        $path = $this->normalizePath($path);
313
+
314
+        if (strrpos($path, '.') !== false) {
315
+            $ext = substr($path, strrpos($path, '.'));
316
+        } else {
317
+            $ext = '';
318
+        }
319
+
320
+        switch ($mode) {
321
+            case 'r':
322
+            case 'rb':
323
+                $stat = $this->stat($path);
324
+                if (is_array($stat)) {
325
+                    $filesize = $stat['size'] ?? 0;
326
+                    // Reading 0 sized files is a waste of time
327
+                    if ($filesize === 0) {
328
+                        return fopen('php://memory', $mode);
329
+                    }
330
+
331
+                    try {
332
+                        $handle = $this->objectStore->readObject($this->getURN($stat['fileid']));
333
+                        if ($handle === false) {
334
+                            return false; // keep backward compatibility
335
+                        }
336
+                        $streamStat = fstat($handle);
337
+                        $actualSize = $streamStat['size'] ?? -1;
338
+                        if ($actualSize > -1 && $actualSize !== $filesize) {
339
+                            $this->getCache()->update((int)$stat['fileid'], ['size' => $actualSize]);
340
+                        }
341
+                        return $handle;
342
+                    } catch (NotFoundException $e) {
343
+                        $this->logger->logException($e, [
344
+                            'app' => 'objectstore',
345
+                            'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
346
+                        ]);
347
+                        throw $e;
348
+                    } catch (\Exception $ex) {
349
+                        $this->logger->logException($ex, [
350
+                            'app' => 'objectstore',
351
+                            'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
352
+                        ]);
353
+                        return false;
354
+                    }
355
+                } else {
356
+                    return false;
357
+                }
358
+                // no break
359
+            case 'w':
360
+            case 'wb':
361
+            case 'w+':
362
+            case 'wb+':
363
+                $dirName = dirname($path);
364
+                $parentExists = $this->is_dir($dirName);
365
+                if (!$parentExists) {
366
+                    return false;
367
+                }
368
+
369
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
370
+                $handle = fopen($tmpFile, $mode);
371
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
372
+                    $this->writeBack($tmpFile, $path);
373
+                    unlink($tmpFile);
374
+                });
375
+            case 'a':
376
+            case 'ab':
377
+            case 'r+':
378
+            case 'a+':
379
+            case 'x':
380
+            case 'x+':
381
+            case 'c':
382
+            case 'c+':
383
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
384
+                if ($this->file_exists($path)) {
385
+                    $source = $this->fopen($path, 'r');
386
+                    file_put_contents($tmpFile, $source);
387
+                }
388
+                $handle = fopen($tmpFile, $mode);
389
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
390
+                    $this->writeBack($tmpFile, $path);
391
+                    unlink($tmpFile);
392
+                });
393
+        }
394
+        return false;
395
+    }
396
+
397
+    public function file_exists($path) {
398
+        $path = $this->normalizePath($path);
399
+        return (bool)$this->stat($path);
400
+    }
401
+
402
+    public function rename($source, $target) {
403
+        $source = $this->normalizePath($source);
404
+        $target = $this->normalizePath($target);
405
+        $this->remove($target);
406
+        $this->getCache()->move($source, $target);
407
+        $this->touch(dirname($target));
408
+        return true;
409
+    }
410
+
411
+    public function getMimeType($path) {
412
+        $path = $this->normalizePath($path);
413
+        return parent::getMimeType($path);
414
+    }
415
+
416
+    public function touch($path, $mtime = null) {
417
+        if (is_null($mtime)) {
418
+            $mtime = time();
419
+        }
420
+
421
+        $path = $this->normalizePath($path);
422
+        $dirName = dirname($path);
423
+        $parentExists = $this->is_dir($dirName);
424
+        if (!$parentExists) {
425
+            return false;
426
+        }
427
+
428
+        $stat = $this->stat($path);
429
+        if (is_array($stat)) {
430
+            // update existing mtime in db
431
+            $stat['mtime'] = $mtime;
432
+            $this->getCache()->update($stat['fileid'], $stat);
433
+        } else {
434
+            try {
435
+                //create a empty file, need to have at least on char to make it
436
+                // work with all object storage implementations
437
+                $this->file_put_contents($path, ' ');
438
+                $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
439
+                $stat = [
440
+                    'etag' => $this->getETag($path),
441
+                    'mimetype' => $mimeType,
442
+                    'size' => 0,
443
+                    'mtime' => $mtime,
444
+                    'storage_mtime' => $mtime,
445
+                    'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
446
+                ];
447
+                $this->getCache()->put($path, $stat);
448
+            } catch (\Exception $ex) {
449
+                $this->logger->logException($ex, [
450
+                    'app' => 'objectstore',
451
+                    'message' => 'Could not create object for ' . $path,
452
+                ]);
453
+                throw $ex;
454
+            }
455
+        }
456
+        return true;
457
+    }
458
+
459
+    public function writeBack($tmpFile, $path) {
460
+        $size = filesize($tmpFile);
461
+        $this->writeStream($path, fopen($tmpFile, 'r'), $size);
462
+    }
463
+
464
+    /**
465
+     * external changes are not supported, exclusive access to the object storage is assumed
466
+     *
467
+     * @param string $path
468
+     * @param int $time
469
+     * @return false
470
+     */
471
+    public function hasUpdated($path, $time) {
472
+        return false;
473
+    }
474
+
475
+    public function needsPartFile() {
476
+        return false;
477
+    }
478
+
479
+    public function file_put_contents($path, $data) {
480
+        $handle = $this->fopen($path, 'w+');
481
+        if (!$handle) {
482
+            return false;
483
+        }
484
+        $result = fwrite($handle, $data);
485
+        fclose($handle);
486
+        return $result;
487
+    }
488
+
489
+    public function writeStream(string $path, $stream, int $size = null): int {
490
+        $stat = $this->stat($path);
491
+        if (empty($stat)) {
492
+            // create new file
493
+            $stat = [
494
+                'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
495
+            ];
496
+        }
497
+        // update stat with new data
498
+        $mTime = time();
499
+        $stat['size'] = (int)$size;
500
+        $stat['mtime'] = $mTime;
501
+        $stat['storage_mtime'] = $mTime;
502
+
503
+        $mimetypeDetector = \OC::$server->getMimeTypeDetector();
504
+        $mimetype = $mimetypeDetector->detectPath($path);
505
+
506
+        $stat['mimetype'] = $mimetype;
507
+        $stat['etag'] = $this->getETag($path);
508
+        $stat['checksum'] = '';
509
+
510
+        $exists = $this->getCache()->inCache($path);
511
+        $uploadPath = $exists ? $path : $path . '.part';
512
+
513
+        if ($exists) {
514
+            $fileId = $stat['fileid'];
515
+        } else {
516
+            $fileId = $this->getCache()->put($uploadPath, $stat);
517
+        }
518
+
519
+        $urn = $this->getURN($fileId);
520
+        try {
521
+            //upload to object storage
522
+            if ($size === null) {
523
+                $countStream = CountWrapper::wrap($stream, function ($writtenSize) use ($fileId, &$size) {
524
+                    $this->getCache()->update($fileId, [
525
+                        'size' => $writtenSize,
526
+                    ]);
527
+                    $size = $writtenSize;
528
+                });
529
+                $this->objectStore->writeObject($urn, $countStream, $mimetype);
530
+                if (is_resource($countStream)) {
531
+                    fclose($countStream);
532
+                }
533
+                $stat['size'] = $size;
534
+            } else {
535
+                $this->objectStore->writeObject($urn, $stream, $mimetype);
536
+                if (is_resource($stream)) {
537
+                    fclose($stream);
538
+                }
539
+            }
540
+        } catch (\Exception $ex) {
541
+            if (!$exists) {
542
+                /*
543 543
 				 * Only remove the entry if we are dealing with a new file.
544 544
 				 * Else people lose access to existing files
545 545
 				 */
546
-				$this->getCache()->remove($uploadPath);
547
-				$this->logger->logException($ex, [
548
-					'app' => 'objectstore',
549
-					'message' => 'Could not create object ' . $urn . ' for ' . $path,
550
-				]);
551
-			} else {
552
-				$this->logger->logException($ex, [
553
-					'app' => 'objectstore',
554
-					'message' => 'Could not update object ' . $urn . ' for ' . $path,
555
-				]);
556
-			}
557
-			throw $ex; // make this bubble up
558
-		}
559
-
560
-		if ($exists) {
561
-			$this->getCache()->update($fileId, $stat);
562
-		} else {
563
-			if (!$this->validateWrites || $this->objectStore->objectExists($urn)) {
564
-				$this->getCache()->move($uploadPath, $path);
565
-			} else {
566
-				$this->getCache()->remove($uploadPath);
567
-				throw new \Exception("Object not found after writing (urn: $urn, path: $path)", 404);
568
-			}
569
-		}
570
-
571
-		return $size;
572
-	}
573
-
574
-	public function getObjectStore(): IObjectStore {
575
-		return $this->objectStore;
576
-	}
577
-
578
-	public function copyFromStorage(
579
-		IStorage $sourceStorage,
580
-		$sourceInternalPath,
581
-		$targetInternalPath,
582
-		$preserveMtime = false
583
-	) {
584
-		if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) {
585
-			/** @var ObjectStoreStorage $sourceStorage */
586
-			if ($sourceStorage->getObjectStore()->getStorageId() === $this->getObjectStore()->getStorageId()) {
587
-				/** @var CacheEntry $sourceEntry */
588
-				$sourceEntry = $sourceStorage->getCache()->get($sourceInternalPath);
589
-				$sourceEntryData = $sourceEntry->getData();
590
-				// $sourceEntry['permissions'] here is the permissions from the jailed storage for the current
591
-				// user. Instead we use $sourceEntryData['scan_permissions'] that are the permissions from the
592
-				// unjailed storage.
593
-				if (is_array($sourceEntryData) && array_key_exists('scan_permissions', $sourceEntryData)) {
594
-					$sourceEntry['permissions'] = $sourceEntryData['scan_permissions'];
595
-				}
596
-				$this->copyInner($sourceEntry, $targetInternalPath);
597
-				return true;
598
-			}
599
-		}
600
-
601
-		return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
602
-	}
603
-
604
-	public function copy($source, $target) {
605
-		$source = $this->normalizePath($source);
606
-		$target = $this->normalizePath($target);
607
-
608
-		$cache = $this->getCache();
609
-		$sourceEntry = $cache->get($source);
610
-		if (!$sourceEntry) {
611
-			throw new NotFoundException('Source object not found');
612
-		}
613
-
614
-		$this->copyInner($sourceEntry, $target);
615
-
616
-		return true;
617
-	}
618
-
619
-	private function copyInner(ICacheEntry $sourceEntry, string $to) {
620
-		$cache = $this->getCache();
621
-
622
-		if ($sourceEntry->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
623
-			if ($cache->inCache($to)) {
624
-				$cache->remove($to);
625
-			}
626
-			$this->mkdir($to);
627
-
628
-			foreach ($cache->getFolderContentsById($sourceEntry->getId()) as $child) {
629
-				$this->copyInner($child, $to . '/' . $child->getName());
630
-			}
631
-		} else {
632
-			$this->copyFile($sourceEntry, $to);
633
-		}
634
-	}
635
-
636
-	private function copyFile(ICacheEntry $sourceEntry, string $to) {
637
-		$cache = $this->getCache();
638
-
639
-		$sourceUrn = $this->getURN($sourceEntry->getId());
640
-
641
-		if (!$cache instanceof Cache) {
642
-			throw new \Exception("Invalid source cache for object store copy");
643
-		}
644
-
645
-		$targetId = $cache->copyFromCache($cache, $sourceEntry, $to);
646
-
647
-		$targetUrn = $this->getURN($targetId);
648
-
649
-		try {
650
-			$this->objectStore->copyObject($sourceUrn, $targetUrn);
651
-		} catch (\Exception $e) {
652
-			$cache->remove($to);
653
-
654
-			throw $e;
655
-		}
656
-	}
657
-
658
-	public function startChunkedWrite(string $targetPath): string {
659
-		if (!$this->objectStore instanceof IObjectStoreMultiPartUpload) {
660
-			throw new GenericFileException('Object store does not support multipart upload');
661
-		}
662
-		$cacheEntry = $this->getCache()->get($targetPath);
663
-		$urn = $this->getURN($cacheEntry->getId());
664
-		return $this->objectStore->initiateMultipartUpload($urn);
665
-	}
666
-
667
-	/**
668
-	 *
669
-	 * @throws GenericFileException
670
-	 */
671
-	public function putChunkedWritePart(
672
-		string $targetPath,
673
-		string $writeToken,
674
-		string $chunkId,
675
-		$data,
676
-		$size = null
677
-	): ?array {
678
-		if (!$this->objectStore instanceof IObjectStoreMultiPartUpload) {
679
-			throw new GenericFileException('Object store does not support multipart upload');
680
-		}
681
-		$cacheEntry = $this->getCache()->get($targetPath);
682
-		$urn = $this->getURN($cacheEntry->getId());
683
-
684
-		$result = $this->objectStore->uploadMultipartPart($urn, $writeToken, (int)$chunkId, $data, $size);
685
-
686
-		$parts[$chunkId] = [
687
-			'PartNumber' => $chunkId,
688
-			'ETag' => trim($result->get('ETag'), '"'),
689
-		];
690
-		return $parts[$chunkId];
691
-	}
692
-
693
-	public function completeChunkedWrite(string $targetPath, string $writeToken): int {
694
-		if (!$this->objectStore instanceof IObjectStoreMultiPartUpload) {
695
-			throw new GenericFileException('Object store does not support multipart upload');
696
-		}
697
-		$cacheEntry = $this->getCache()->get($targetPath);
698
-		$urn = $this->getURN($cacheEntry->getId());
699
-		$parts = $this->objectStore->getMultipartUploads($urn, $writeToken);
700
-		$sortedParts = array_values($parts);
701
-		sort($sortedParts);
702
-		try {
703
-			$size = $this->objectStore->completeMultipartUpload($urn, $writeToken, $sortedParts);
704
-			$stat = $this->stat($targetPath);
705
-			$mtime = time();
706
-			if (is_array($stat)) {
707
-				$stat['size'] = $size;
708
-				$stat['mtime'] = $mtime;
709
-				$stat['mimetype'] = $this->getMimeType($targetPath);
710
-				$this->getCache()->update($stat['fileid'], $stat);
711
-			}
712
-		} catch (S3MultipartUploadException|S3Exception $e) {
713
-			$this->objectStore->abortMultipartUpload($urn, $writeToken);
714
-			$this->logger->logException($e, [
715
-				'app' => 'objectstore',
716
-				'message' => 'Could not compete multipart upload ' . $urn . ' with uploadId ' . $writeToken,
717
-			]);
718
-			throw new GenericFileException('Could not write chunked file');
719
-		}
720
-		return $size;
721
-	}
722
-
723
-	public function cancelChunkedWrite(string $targetPath, string $writeToken): void {
724
-		if (!$this->objectStore instanceof IObjectStoreMultiPartUpload) {
725
-			throw new GenericFileException('Object store does not support multipart upload');
726
-		}
727
-		$cacheEntry = $this->getCache()->get($targetPath);
728
-		$urn = $this->getURN($cacheEntry->getId());
729
-		$this->objectStore->abortMultipartUpload($urn, $writeToken);
730
-	}
546
+                $this->getCache()->remove($uploadPath);
547
+                $this->logger->logException($ex, [
548
+                    'app' => 'objectstore',
549
+                    'message' => 'Could not create object ' . $urn . ' for ' . $path,
550
+                ]);
551
+            } else {
552
+                $this->logger->logException($ex, [
553
+                    'app' => 'objectstore',
554
+                    'message' => 'Could not update object ' . $urn . ' for ' . $path,
555
+                ]);
556
+            }
557
+            throw $ex; // make this bubble up
558
+        }
559
+
560
+        if ($exists) {
561
+            $this->getCache()->update($fileId, $stat);
562
+        } else {
563
+            if (!$this->validateWrites || $this->objectStore->objectExists($urn)) {
564
+                $this->getCache()->move($uploadPath, $path);
565
+            } else {
566
+                $this->getCache()->remove($uploadPath);
567
+                throw new \Exception("Object not found after writing (urn: $urn, path: $path)", 404);
568
+            }
569
+        }
570
+
571
+        return $size;
572
+    }
573
+
574
+    public function getObjectStore(): IObjectStore {
575
+        return $this->objectStore;
576
+    }
577
+
578
+    public function copyFromStorage(
579
+        IStorage $sourceStorage,
580
+        $sourceInternalPath,
581
+        $targetInternalPath,
582
+        $preserveMtime = false
583
+    ) {
584
+        if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) {
585
+            /** @var ObjectStoreStorage $sourceStorage */
586
+            if ($sourceStorage->getObjectStore()->getStorageId() === $this->getObjectStore()->getStorageId()) {
587
+                /** @var CacheEntry $sourceEntry */
588
+                $sourceEntry = $sourceStorage->getCache()->get($sourceInternalPath);
589
+                $sourceEntryData = $sourceEntry->getData();
590
+                // $sourceEntry['permissions'] here is the permissions from the jailed storage for the current
591
+                // user. Instead we use $sourceEntryData['scan_permissions'] that are the permissions from the
592
+                // unjailed storage.
593
+                if (is_array($sourceEntryData) && array_key_exists('scan_permissions', $sourceEntryData)) {
594
+                    $sourceEntry['permissions'] = $sourceEntryData['scan_permissions'];
595
+                }
596
+                $this->copyInner($sourceEntry, $targetInternalPath);
597
+                return true;
598
+            }
599
+        }
600
+
601
+        return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
602
+    }
603
+
604
+    public function copy($source, $target) {
605
+        $source = $this->normalizePath($source);
606
+        $target = $this->normalizePath($target);
607
+
608
+        $cache = $this->getCache();
609
+        $sourceEntry = $cache->get($source);
610
+        if (!$sourceEntry) {
611
+            throw new NotFoundException('Source object not found');
612
+        }
613
+
614
+        $this->copyInner($sourceEntry, $target);
615
+
616
+        return true;
617
+    }
618
+
619
+    private function copyInner(ICacheEntry $sourceEntry, string $to) {
620
+        $cache = $this->getCache();
621
+
622
+        if ($sourceEntry->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
623
+            if ($cache->inCache($to)) {
624
+                $cache->remove($to);
625
+            }
626
+            $this->mkdir($to);
627
+
628
+            foreach ($cache->getFolderContentsById($sourceEntry->getId()) as $child) {
629
+                $this->copyInner($child, $to . '/' . $child->getName());
630
+            }
631
+        } else {
632
+            $this->copyFile($sourceEntry, $to);
633
+        }
634
+    }
635
+
636
+    private function copyFile(ICacheEntry $sourceEntry, string $to) {
637
+        $cache = $this->getCache();
638
+
639
+        $sourceUrn = $this->getURN($sourceEntry->getId());
640
+
641
+        if (!$cache instanceof Cache) {
642
+            throw new \Exception("Invalid source cache for object store copy");
643
+        }
644
+
645
+        $targetId = $cache->copyFromCache($cache, $sourceEntry, $to);
646
+
647
+        $targetUrn = $this->getURN($targetId);
648
+
649
+        try {
650
+            $this->objectStore->copyObject($sourceUrn, $targetUrn);
651
+        } catch (\Exception $e) {
652
+            $cache->remove($to);
653
+
654
+            throw $e;
655
+        }
656
+    }
657
+
658
+    public function startChunkedWrite(string $targetPath): string {
659
+        if (!$this->objectStore instanceof IObjectStoreMultiPartUpload) {
660
+            throw new GenericFileException('Object store does not support multipart upload');
661
+        }
662
+        $cacheEntry = $this->getCache()->get($targetPath);
663
+        $urn = $this->getURN($cacheEntry->getId());
664
+        return $this->objectStore->initiateMultipartUpload($urn);
665
+    }
666
+
667
+    /**
668
+     *
669
+     * @throws GenericFileException
670
+     */
671
+    public function putChunkedWritePart(
672
+        string $targetPath,
673
+        string $writeToken,
674
+        string $chunkId,
675
+        $data,
676
+        $size = null
677
+    ): ?array {
678
+        if (!$this->objectStore instanceof IObjectStoreMultiPartUpload) {
679
+            throw new GenericFileException('Object store does not support multipart upload');
680
+        }
681
+        $cacheEntry = $this->getCache()->get($targetPath);
682
+        $urn = $this->getURN($cacheEntry->getId());
683
+
684
+        $result = $this->objectStore->uploadMultipartPart($urn, $writeToken, (int)$chunkId, $data, $size);
685
+
686
+        $parts[$chunkId] = [
687
+            'PartNumber' => $chunkId,
688
+            'ETag' => trim($result->get('ETag'), '"'),
689
+        ];
690
+        return $parts[$chunkId];
691
+    }
692
+
693
+    public function completeChunkedWrite(string $targetPath, string $writeToken): int {
694
+        if (!$this->objectStore instanceof IObjectStoreMultiPartUpload) {
695
+            throw new GenericFileException('Object store does not support multipart upload');
696
+        }
697
+        $cacheEntry = $this->getCache()->get($targetPath);
698
+        $urn = $this->getURN($cacheEntry->getId());
699
+        $parts = $this->objectStore->getMultipartUploads($urn, $writeToken);
700
+        $sortedParts = array_values($parts);
701
+        sort($sortedParts);
702
+        try {
703
+            $size = $this->objectStore->completeMultipartUpload($urn, $writeToken, $sortedParts);
704
+            $stat = $this->stat($targetPath);
705
+            $mtime = time();
706
+            if (is_array($stat)) {
707
+                $stat['size'] = $size;
708
+                $stat['mtime'] = $mtime;
709
+                $stat['mimetype'] = $this->getMimeType($targetPath);
710
+                $this->getCache()->update($stat['fileid'], $stat);
711
+            }
712
+        } catch (S3MultipartUploadException|S3Exception $e) {
713
+            $this->objectStore->abortMultipartUpload($urn, $writeToken);
714
+            $this->logger->logException($e, [
715
+                'app' => 'objectstore',
716
+                'message' => 'Could not compete multipart upload ' . $urn . ' with uploadId ' . $writeToken,
717
+            ]);
718
+            throw new GenericFileException('Could not write chunked file');
719
+        }
720
+        return $size;
721
+    }
722
+
723
+    public function cancelChunkedWrite(string $targetPath, string $writeToken): void {
724
+        if (!$this->objectStore instanceof IObjectStoreMultiPartUpload) {
725
+            throw new GenericFileException('Object store does not support multipart upload');
726
+        }
727
+        $cacheEntry = $this->getCache()->get($targetPath);
728
+        $urn = $this->getURN($cacheEntry->getId());
729
+        $this->objectStore->abortMultipartUpload($urn, $writeToken);
730
+    }
731 731
 }
Please login to merge, or discard this patch.
Spacing   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -77,15 +77,15 @@  discard block
 block discarded – undo
77 77
 			throw new \Exception('missing IObjectStore instance');
78 78
 		}
79 79
 		if (isset($params['storageid'])) {
80
-			$this->id = 'object::store:' . $params['storageid'];
80
+			$this->id = 'object::store:'.$params['storageid'];
81 81
 		} else {
82
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
82
+			$this->id = 'object::store:'.$this->objectStore->getStorageId();
83 83
 		}
84 84
 		if (isset($params['objectPrefix'])) {
85 85
 			$this->objectPrefix = $params['objectPrefix'];
86 86
 		}
87 87
 		if (isset($params['validateWrites'])) {
88
-			$this->validateWrites = (bool)$params['validateWrites'];
88
+			$this->validateWrites = (bool) $params['validateWrites'];
89 89
 		}
90 90
 
91 91
 		$this->logger = \OC::$server->getLogger();
@@ -226,7 +226,7 @@  discard block
 block discarded – undo
226 226
 			if ($ex->getCode() !== 404) {
227 227
 				$this->logger->logException($ex, [
228 228
 					'app' => 'objectstore',
229
-					'message' => 'Could not delete object ' . $this->getURN($entry->getId()) . ' for ' . $entry->getPath(),
229
+					'message' => 'Could not delete object '.$this->getURN($entry->getId()).' for '.$entry->getPath(),
230 230
 				]);
231 231
 				return false;
232 232
 			}
@@ -273,7 +273,7 @@  discard block
 block discarded – undo
273 273
 	 */
274 274
 	public function getURN($fileId) {
275 275
 		if (is_numeric($fileId)) {
276
-			return $this->objectPrefix . $fileId;
276
+			return $this->objectPrefix.$fileId;
277 277
 		}
278 278
 		return null;
279 279
 	}
@@ -336,19 +336,19 @@  discard block
 block discarded – undo
336 336
 						$streamStat = fstat($handle);
337 337
 						$actualSize = $streamStat['size'] ?? -1;
338 338
 						if ($actualSize > -1 && $actualSize !== $filesize) {
339
-							$this->getCache()->update((int)$stat['fileid'], ['size' => $actualSize]);
339
+							$this->getCache()->update((int) $stat['fileid'], ['size' => $actualSize]);
340 340
 						}
341 341
 						return $handle;
342 342
 					} catch (NotFoundException $e) {
343 343
 						$this->logger->logException($e, [
344 344
 							'app' => 'objectstore',
345
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
345
+							'message' => 'Could not get object '.$this->getURN($stat['fileid']).' for file '.$path,
346 346
 						]);
347 347
 						throw $e;
348 348
 					} catch (\Exception $ex) {
349 349
 						$this->logger->logException($ex, [
350 350
 							'app' => 'objectstore',
351
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
351
+							'message' => 'Could not get object '.$this->getURN($stat['fileid']).' for file '.$path,
352 352
 						]);
353 353
 						return false;
354 354
 					}
@@ -368,7 +368,7 @@  discard block
 block discarded – undo
368 368
 
369 369
 				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
370 370
 				$handle = fopen($tmpFile, $mode);
371
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
371
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
372 372
 					$this->writeBack($tmpFile, $path);
373 373
 					unlink($tmpFile);
374 374
 				});
@@ -386,7 +386,7 @@  discard block
 block discarded – undo
386 386
 					file_put_contents($tmpFile, $source);
387 387
 				}
388 388
 				$handle = fopen($tmpFile, $mode);
389
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
389
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
390 390
 					$this->writeBack($tmpFile, $path);
391 391
 					unlink($tmpFile);
392 392
 				});
@@ -396,7 +396,7 @@  discard block
 block discarded – undo
396 396
 
397 397
 	public function file_exists($path) {
398 398
 		$path = $this->normalizePath($path);
399
-		return (bool)$this->stat($path);
399
+		return (bool) $this->stat($path);
400 400
 	}
401 401
 
402 402
 	public function rename($source, $target) {
@@ -448,7 +448,7 @@  discard block
 block discarded – undo
448 448
 			} catch (\Exception $ex) {
449 449
 				$this->logger->logException($ex, [
450 450
 					'app' => 'objectstore',
451
-					'message' => 'Could not create object for ' . $path,
451
+					'message' => 'Could not create object for '.$path,
452 452
 				]);
453 453
 				throw $ex;
454 454
 			}
@@ -496,7 +496,7 @@  discard block
 block discarded – undo
496 496
 		}
497 497
 		// update stat with new data
498 498
 		$mTime = time();
499
-		$stat['size'] = (int)$size;
499
+		$stat['size'] = (int) $size;
500 500
 		$stat['mtime'] = $mTime;
501 501
 		$stat['storage_mtime'] = $mTime;
502 502
 
@@ -508,7 +508,7 @@  discard block
 block discarded – undo
508 508
 		$stat['checksum'] = '';
509 509
 
510 510
 		$exists = $this->getCache()->inCache($path);
511
-		$uploadPath = $exists ? $path : $path . '.part';
511
+		$uploadPath = $exists ? $path : $path.'.part';
512 512
 
513 513
 		if ($exists) {
514 514
 			$fileId = $stat['fileid'];
@@ -520,7 +520,7 @@  discard block
 block discarded – undo
520 520
 		try {
521 521
 			//upload to object storage
522 522
 			if ($size === null) {
523
-				$countStream = CountWrapper::wrap($stream, function ($writtenSize) use ($fileId, &$size) {
523
+				$countStream = CountWrapper::wrap($stream, function($writtenSize) use ($fileId, &$size) {
524 524
 					$this->getCache()->update($fileId, [
525 525
 						'size' => $writtenSize,
526 526
 					]);
@@ -546,12 +546,12 @@  discard block
 block discarded – undo
546 546
 				$this->getCache()->remove($uploadPath);
547 547
 				$this->logger->logException($ex, [
548 548
 					'app' => 'objectstore',
549
-					'message' => 'Could not create object ' . $urn . ' for ' . $path,
549
+					'message' => 'Could not create object '.$urn.' for '.$path,
550 550
 				]);
551 551
 			} else {
552 552
 				$this->logger->logException($ex, [
553 553
 					'app' => 'objectstore',
554
-					'message' => 'Could not update object ' . $urn . ' for ' . $path,
554
+					'message' => 'Could not update object '.$urn.' for '.$path,
555 555
 				]);
556 556
 			}
557 557
 			throw $ex; // make this bubble up
@@ -626,7 +626,7 @@  discard block
 block discarded – undo
626 626
 			$this->mkdir($to);
627 627
 
628 628
 			foreach ($cache->getFolderContentsById($sourceEntry->getId()) as $child) {
629
-				$this->copyInner($child, $to . '/' . $child->getName());
629
+				$this->copyInner($child, $to.'/'.$child->getName());
630 630
 			}
631 631
 		} else {
632 632
 			$this->copyFile($sourceEntry, $to);
@@ -681,7 +681,7 @@  discard block
 block discarded – undo
681 681
 		$cacheEntry = $this->getCache()->get($targetPath);
682 682
 		$urn = $this->getURN($cacheEntry->getId());
683 683
 
684
-		$result = $this->objectStore->uploadMultipartPart($urn, $writeToken, (int)$chunkId, $data, $size);
684
+		$result = $this->objectStore->uploadMultipartPart($urn, $writeToken, (int) $chunkId, $data, $size);
685 685
 
686 686
 		$parts[$chunkId] = [
687 687
 			'PartNumber' => $chunkId,
@@ -709,11 +709,11 @@  discard block
 block discarded – undo
709 709
 				$stat['mimetype'] = $this->getMimeType($targetPath);
710 710
 				$this->getCache()->update($stat['fileid'], $stat);
711 711
 			}
712
-		} catch (S3MultipartUploadException|S3Exception $e) {
712
+		} catch (S3MultipartUploadException | S3Exception $e) {
713 713
 			$this->objectStore->abortMultipartUpload($urn, $writeToken);
714 714
 			$this->logger->logException($e, [
715 715
 				'app' => 'objectstore',
716
-				'message' => 'Could not compete multipart upload ' . $urn . ' with uploadId ' . $writeToken,
716
+				'message' => 'Could not compete multipart upload '.$urn.' with uploadId '.$writeToken,
717 717
 			]);
718 718
 			throw new GenericFileException('Could not write chunked file');
719 719
 		}
Please login to merge, or discard this patch.