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