Passed
Push — master ( 9fc2e2...b5902b )
by Roeland
19:29 queued 08:55
created
lib/private/Files/ObjectStore/ObjectStoreStorage.php 1 patch
Indentation   +450 added lines, -450 removed lines patch added patch discarded remove patch
@@ -36,454 +36,454 @@
 block discarded – undo
36 36
 use OCP\Files\ObjectStore\IObjectStore;
37 37
 
38 38
 class ObjectStoreStorage extends \OC\Files\Storage\Common {
39
-	/**
40
-	 * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
41
-	 */
42
-	protected $objectStore;
43
-	/**
44
-	 * @var string $id
45
-	 */
46
-	protected $id;
47
-	/**
48
-	 * @var \OC\User\User $user
49
-	 */
50
-	protected $user;
51
-
52
-	private $objectPrefix = 'urn:oid:';
53
-
54
-	private $logger;
55
-
56
-	public function __construct($params) {
57
-		if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
58
-			$this->objectStore = $params['objectstore'];
59
-		} else {
60
-			throw new \Exception('missing IObjectStore instance');
61
-		}
62
-		if (isset($params['storageid'])) {
63
-			$this->id = 'object::store:' . $params['storageid'];
64
-		} else {
65
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
66
-		}
67
-		if (isset($params['objectPrefix'])) {
68
-			$this->objectPrefix = $params['objectPrefix'];
69
-		}
70
-		//initialize cache with root directory in cache
71
-		if (!$this->is_dir('/')) {
72
-			$this->mkdir('/');
73
-		}
74
-
75
-		$this->logger = \OC::$server->getLogger();
76
-	}
77
-
78
-	public function mkdir($path) {
79
-		$path = $this->normalizePath($path);
80
-
81
-		if ($this->file_exists($path)) {
82
-			return false;
83
-		}
84
-
85
-		$mTime = time();
86
-		$data = [
87
-			'mimetype' => 'httpd/unix-directory',
88
-			'size' => 0,
89
-			'mtime' => $mTime,
90
-			'storage_mtime' => $mTime,
91
-			'permissions' => \OCP\Constants::PERMISSION_ALL,
92
-		];
93
-		if ($path === '') {
94
-			//create root on the fly
95
-			$data['etag'] = $this->getETag('');
96
-			$this->getCache()->put('', $data);
97
-			return true;
98
-		} else {
99
-			// if parent does not exist, create it
100
-			$parent = $this->normalizePath(dirname($path));
101
-			$parentType = $this->filetype($parent);
102
-			if ($parentType === false) {
103
-				if (!$this->mkdir($parent)) {
104
-					// something went wrong
105
-					return false;
106
-				}
107
-			} else if ($parentType === 'file') {
108
-				// parent is a file
109
-				return false;
110
-			}
111
-			// finally create the new dir
112
-			$mTime = time(); // update mtime
113
-			$data['mtime'] = $mTime;
114
-			$data['storage_mtime'] = $mTime;
115
-			$data['etag'] = $this->getETag($path);
116
-			$this->getCache()->put($path, $data);
117
-			return true;
118
-		}
119
-	}
120
-
121
-	/**
122
-	 * @param string $path
123
-	 * @return string
124
-	 */
125
-	private function normalizePath($path) {
126
-		$path = trim($path, '/');
127
-		//FIXME why do we sometimes get a path like 'files//username'?
128
-		$path = str_replace('//', '/', $path);
129
-
130
-		// dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
131
-		if (!$path || $path === '.') {
132
-			$path = '';
133
-		}
134
-
135
-		return $path;
136
-	}
137
-
138
-	/**
139
-	 * Object Stores use a NoopScanner because metadata is directly stored in
140
-	 * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
141
-	 *
142
-	 * @param string $path
143
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
144
-	 * @return \OC\Files\ObjectStore\NoopScanner
145
-	 */
146
-	public function getScanner($path = '', $storage = null) {
147
-		if (!$storage) {
148
-			$storage = $this;
149
-		}
150
-		if (!isset($this->scanner)) {
151
-			$this->scanner = new NoopScanner($storage);
152
-		}
153
-		return $this->scanner;
154
-	}
155
-
156
-	public function getId() {
157
-		return $this->id;
158
-	}
159
-
160
-	public function rmdir($path) {
161
-		$path = $this->normalizePath($path);
162
-
163
-		if (!$this->is_dir($path)) {
164
-			return false;
165
-		}
166
-
167
-		if (!$this->rmObjects($path)) {
168
-			return false;
169
-		}
170
-
171
-		$this->getCache()->remove($path);
172
-
173
-		return true;
174
-	}
175
-
176
-	private function rmObjects($path) {
177
-		$children = $this->getCache()->getFolderContents($path);
178
-		foreach ($children as $child) {
179
-			if ($child['mimetype'] === 'httpd/unix-directory') {
180
-				if (!$this->rmObjects($child['path'])) {
181
-					return false;
182
-				}
183
-			} else {
184
-				if(!$this->unlink($child['path'])) {
185
-					return false;
186
-				}
187
-			}
188
-		}
189
-
190
-		return true;
191
-	}
192
-
193
-	public function unlink($path) {
194
-		$path = $this->normalizePath($path);
195
-		$stat = $this->stat($path);
196
-
197
-		if ($stat && isset($stat['fileid'])) {
198
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
199
-				return $this->rmdir($path);
200
-			}
201
-			try {
202
-				$this->objectStore->deleteObject($this->getURN($stat['fileid']));
203
-			} catch (\Exception $ex) {
204
-				if ($ex->getCode() !== 404) {
205
-					$this->logger->logException($ex, [
206
-						'app' => 'objectstore',
207
-						'message' => 'Could not delete object ' . $this->getURN($stat['fileid']) . ' for ' . $path,
208
-					]);
209
-					return false;
210
-				}
211
-				//removing from cache is ok as it does not exist in the objectstore anyway
212
-			}
213
-			$this->getCache()->remove($path);
214
-			return true;
215
-		}
216
-		return false;
217
-	}
218
-
219
-	public function stat($path) {
220
-		$path = $this->normalizePath($path);
221
-		$cacheEntry = $this->getCache()->get($path);
222
-		if ($cacheEntry instanceof CacheEntry) {
223
-			return $cacheEntry->getData();
224
-		} else {
225
-			return false;
226
-		}
227
-	}
228
-
229
-	/**
230
-	 * Override this method if you need a different unique resource identifier for your object storage implementation.
231
-	 * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
232
-	 * You may need a mapping table to store your URN if it cannot be generated from the fileid.
233
-	 *
234
-	 * @param int $fileId the fileid
235
-	 * @return null|string the unified resource name used to identify the object
236
-	 */
237
-	public function getURN($fileId) {
238
-		if (is_numeric($fileId)) {
239
-			return $this->objectPrefix . $fileId;
240
-		}
241
-		return null;
242
-	}
243
-
244
-	public function opendir($path) {
245
-		$path = $this->normalizePath($path);
246
-
247
-		try {
248
-			$files = array();
249
-			$folderContents = $this->getCache()->getFolderContents($path);
250
-			foreach ($folderContents as $file) {
251
-				$files[] = $file['name'];
252
-			}
253
-
254
-			return IteratorDirectory::wrap($files);
255
-		} catch (\Exception $e) {
256
-			$this->logger->logException($e);
257
-			return false;
258
-		}
259
-	}
260
-
261
-	public function filetype($path) {
262
-		$path = $this->normalizePath($path);
263
-		$stat = $this->stat($path);
264
-		if ($stat) {
265
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
266
-				return 'dir';
267
-			}
268
-			return 'file';
269
-		} else {
270
-			return false;
271
-		}
272
-	}
273
-
274
-	public function fopen($path, $mode) {
275
-		$path = $this->normalizePath($path);
276
-
277
-		if (strrpos($path, '.') !== false) {
278
-			$ext = substr($path, strrpos($path, '.'));
279
-		} else {
280
-			$ext = '';
281
-		}
282
-
283
-		switch ($mode) {
284
-			case 'r':
285
-			case 'rb':
286
-				$stat = $this->stat($path);
287
-				if (is_array($stat)) {
288
-					try {
289
-						return $this->objectStore->readObject($this->getURN($stat['fileid']));
290
-					} catch (NotFoundException $e) {
291
-						$this->logger->logException($e, [
292
-							'app' => 'objectstore',
293
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
294
-						]);
295
-						throw $e;
296
-					} catch (\Exception $ex) {
297
-						$this->logger->logException($ex, [
298
-							'app' => 'objectstore',
299
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
300
-						]);
301
-						return false;
302
-					}
303
-				} else {
304
-					return false;
305
-				}
306
-			case 'w':
307
-			case 'wb':
308
-			case 'w+':
309
-			case 'wb+':
310
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
311
-				$handle = fopen($tmpFile, $mode);
312
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
313
-					$this->writeBack($tmpFile, $path);
314
-				});
315
-			case 'a':
316
-			case 'ab':
317
-			case 'r+':
318
-			case 'a+':
319
-			case 'x':
320
-			case 'x+':
321
-			case 'c':
322
-			case 'c+':
323
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
324
-				if ($this->file_exists($path)) {
325
-					$source = $this->fopen($path, 'r');
326
-					file_put_contents($tmpFile, $source);
327
-				}
328
-				$handle = fopen($tmpFile, $mode);
329
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
330
-					$this->writeBack($tmpFile, $path);
331
-				});
332
-		}
333
-		return false;
334
-	}
335
-
336
-	public function file_exists($path) {
337
-		$path = $this->normalizePath($path);
338
-		return (bool)$this->stat($path);
339
-	}
340
-
341
-	public function rename($source, $target) {
342
-		$source = $this->normalizePath($source);
343
-		$target = $this->normalizePath($target);
344
-		$this->remove($target);
345
-		$this->getCache()->move($source, $target);
346
-		$this->touch(dirname($target));
347
-		return true;
348
-	}
349
-
350
-	public function getMimeType($path) {
351
-		$path = $this->normalizePath($path);
352
-		return parent::getMimeType($path);
353
-	}
354
-
355
-	public function touch($path, $mtime = null) {
356
-		if (is_null($mtime)) {
357
-			$mtime = time();
358
-		}
359
-
360
-		$path = $this->normalizePath($path);
361
-		$dirName = dirname($path);
362
-		$parentExists = $this->is_dir($dirName);
363
-		if (!$parentExists) {
364
-			return false;
365
-		}
366
-
367
-		$stat = $this->stat($path);
368
-		if (is_array($stat)) {
369
-			// update existing mtime in db
370
-			$stat['mtime'] = $mtime;
371
-			$this->getCache()->update($stat['fileid'], $stat);
372
-		} else {
373
-			try {
374
-				//create a empty file, need to have at least on char to make it
375
-				// work with all object storage implementations
376
-				$this->file_put_contents($path, ' ');
377
-				$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
378
-				$stat = array(
379
-					'etag' => $this->getETag($path),
380
-					'mimetype' => $mimeType,
381
-					'size' => 0,
382
-					'mtime' => $mtime,
383
-					'storage_mtime' => $mtime,
384
-					'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
385
-				);
386
-				$this->getCache()->put($path, $stat);
387
-			} catch (\Exception $ex) {
388
-				$this->logger->logException($ex, [
389
-					'app' => 'objectstore',
390
-					'message' => 'Could not create object for ' . $path,
391
-				]);
392
-				throw $ex;
393
-			}
394
-		}
395
-		return true;
396
-	}
397
-
398
-	public function writeBack($tmpFile, $path) {
399
-		$size = filesize($tmpFile);
400
-		$this->writeStream($path, fopen($tmpFile, 'r'), $size);
401
-	}
402
-
403
-	/**
404
-	 * external changes are not supported, exclusive access to the object storage is assumed
405
-	 *
406
-	 * @param string $path
407
-	 * @param int $time
408
-	 * @return false
409
-	 */
410
-	public function hasUpdated($path, $time) {
411
-		return false;
412
-	}
413
-
414
-	public function needsPartFile() {
415
-		return false;
416
-	}
417
-
418
-	public function file_put_contents($path, $data) {
419
-		$handle = $this->fopen($path, 'w+');
420
-		fwrite($handle, $data);
421
-		fclose($handle);
422
-		return true;
423
-	}
424
-
425
-	public function writeStream(string $path, $stream, int $size = null): int {
426
-		$stat = $this->stat($path);
427
-		if (empty($stat)) {
428
-			// create new file
429
-			$stat = [
430
-				'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
431
-			];
432
-		}
433
-		// update stat with new data
434
-		$mTime = time();
435
-		$stat['size'] = (int)$size;
436
-		$stat['mtime'] = $mTime;
437
-		$stat['storage_mtime'] = $mTime;
438
-
439
-		$mimetypeDetector = \OC::$server->getMimeTypeDetector();
440
-		$mimetype = $mimetypeDetector->detectPath($path);
441
-
442
-		$stat['mimetype'] = $mimetype;
443
-		$stat['etag'] = $this->getETag($path);
444
-
445
-		$exists = $this->getCache()->inCache($path);
446
-		$uploadPath = $exists ? $path : $path . '.part';
447
-		$fileId = $this->getCache()->put($uploadPath, $stat);
448
-		$urn = $this->getURN($fileId);
449
-		try {
450
-			//upload to object storage
451
-			if ($size === null) {
452
-				$countStream = CountWrapper::wrap($stream, function ($writtenSize) use ($fileId, &$size) {
453
-					$this->getCache()->update($fileId, [
454
-						'size' => $writtenSize
455
-					]);
456
-					$size = $writtenSize;
457
-				});
458
-				$this->objectStore->writeObject($urn, $countStream);
459
-				if (is_resource($countStream)) {
460
-					fclose($countStream);
461
-				}
462
-			} else {
463
-				$this->objectStore->writeObject($urn, $stream);
464
-			}
465
-		} catch (\Exception $ex) {
466
-			$this->getCache()->remove($uploadPath);
467
-			$this->logger->logException($ex, [
468
-				'app' => 'objectstore',
469
-				'message' => 'Could not create object ' . $urn . ' for ' . $path,
470
-			]);
471
-			throw $ex; // make this bubble up
472
-		}
473
-
474
-		if (!$exists) {
475
-			if ($this->objectStore->objectExists($urn)) {
476
-				$this->getCache()->move($uploadPath, $path);
477
-			} else {
478
-				$this->getCache()->remove($uploadPath);
479
-				throw new \Exception("Object not found after writing (urn: $urn, path: $path)", 404);
480
-			}
481
-		}
482
-
483
-		return $size;
484
-	}
485
-
486
-	public function getObjectStore(): IObjectStore {
487
-		return $this->objectStore;
488
-	}
39
+    /**
40
+     * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
41
+     */
42
+    protected $objectStore;
43
+    /**
44
+     * @var string $id
45
+     */
46
+    protected $id;
47
+    /**
48
+     * @var \OC\User\User $user
49
+     */
50
+    protected $user;
51
+
52
+    private $objectPrefix = 'urn:oid:';
53
+
54
+    private $logger;
55
+
56
+    public function __construct($params) {
57
+        if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
58
+            $this->objectStore = $params['objectstore'];
59
+        } else {
60
+            throw new \Exception('missing IObjectStore instance');
61
+        }
62
+        if (isset($params['storageid'])) {
63
+            $this->id = 'object::store:' . $params['storageid'];
64
+        } else {
65
+            $this->id = 'object::store:' . $this->objectStore->getStorageId();
66
+        }
67
+        if (isset($params['objectPrefix'])) {
68
+            $this->objectPrefix = $params['objectPrefix'];
69
+        }
70
+        //initialize cache with root directory in cache
71
+        if (!$this->is_dir('/')) {
72
+            $this->mkdir('/');
73
+        }
74
+
75
+        $this->logger = \OC::$server->getLogger();
76
+    }
77
+
78
+    public function mkdir($path) {
79
+        $path = $this->normalizePath($path);
80
+
81
+        if ($this->file_exists($path)) {
82
+            return false;
83
+        }
84
+
85
+        $mTime = time();
86
+        $data = [
87
+            'mimetype' => 'httpd/unix-directory',
88
+            'size' => 0,
89
+            'mtime' => $mTime,
90
+            'storage_mtime' => $mTime,
91
+            'permissions' => \OCP\Constants::PERMISSION_ALL,
92
+        ];
93
+        if ($path === '') {
94
+            //create root on the fly
95
+            $data['etag'] = $this->getETag('');
96
+            $this->getCache()->put('', $data);
97
+            return true;
98
+        } else {
99
+            // if parent does not exist, create it
100
+            $parent = $this->normalizePath(dirname($path));
101
+            $parentType = $this->filetype($parent);
102
+            if ($parentType === false) {
103
+                if (!$this->mkdir($parent)) {
104
+                    // something went wrong
105
+                    return false;
106
+                }
107
+            } else if ($parentType === 'file') {
108
+                // parent is a file
109
+                return false;
110
+            }
111
+            // finally create the new dir
112
+            $mTime = time(); // update mtime
113
+            $data['mtime'] = $mTime;
114
+            $data['storage_mtime'] = $mTime;
115
+            $data['etag'] = $this->getETag($path);
116
+            $this->getCache()->put($path, $data);
117
+            return true;
118
+        }
119
+    }
120
+
121
+    /**
122
+     * @param string $path
123
+     * @return string
124
+     */
125
+    private function normalizePath($path) {
126
+        $path = trim($path, '/');
127
+        //FIXME why do we sometimes get a path like 'files//username'?
128
+        $path = str_replace('//', '/', $path);
129
+
130
+        // dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
131
+        if (!$path || $path === '.') {
132
+            $path = '';
133
+        }
134
+
135
+        return $path;
136
+    }
137
+
138
+    /**
139
+     * Object Stores use a NoopScanner because metadata is directly stored in
140
+     * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
141
+     *
142
+     * @param string $path
143
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
144
+     * @return \OC\Files\ObjectStore\NoopScanner
145
+     */
146
+    public function getScanner($path = '', $storage = null) {
147
+        if (!$storage) {
148
+            $storage = $this;
149
+        }
150
+        if (!isset($this->scanner)) {
151
+            $this->scanner = new NoopScanner($storage);
152
+        }
153
+        return $this->scanner;
154
+    }
155
+
156
+    public function getId() {
157
+        return $this->id;
158
+    }
159
+
160
+    public function rmdir($path) {
161
+        $path = $this->normalizePath($path);
162
+
163
+        if (!$this->is_dir($path)) {
164
+            return false;
165
+        }
166
+
167
+        if (!$this->rmObjects($path)) {
168
+            return false;
169
+        }
170
+
171
+        $this->getCache()->remove($path);
172
+
173
+        return true;
174
+    }
175
+
176
+    private function rmObjects($path) {
177
+        $children = $this->getCache()->getFolderContents($path);
178
+        foreach ($children as $child) {
179
+            if ($child['mimetype'] === 'httpd/unix-directory') {
180
+                if (!$this->rmObjects($child['path'])) {
181
+                    return false;
182
+                }
183
+            } else {
184
+                if(!$this->unlink($child['path'])) {
185
+                    return false;
186
+                }
187
+            }
188
+        }
189
+
190
+        return true;
191
+    }
192
+
193
+    public function unlink($path) {
194
+        $path = $this->normalizePath($path);
195
+        $stat = $this->stat($path);
196
+
197
+        if ($stat && isset($stat['fileid'])) {
198
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
199
+                return $this->rmdir($path);
200
+            }
201
+            try {
202
+                $this->objectStore->deleteObject($this->getURN($stat['fileid']));
203
+            } catch (\Exception $ex) {
204
+                if ($ex->getCode() !== 404) {
205
+                    $this->logger->logException($ex, [
206
+                        'app' => 'objectstore',
207
+                        'message' => 'Could not delete object ' . $this->getURN($stat['fileid']) . ' for ' . $path,
208
+                    ]);
209
+                    return false;
210
+                }
211
+                //removing from cache is ok as it does not exist in the objectstore anyway
212
+            }
213
+            $this->getCache()->remove($path);
214
+            return true;
215
+        }
216
+        return false;
217
+    }
218
+
219
+    public function stat($path) {
220
+        $path = $this->normalizePath($path);
221
+        $cacheEntry = $this->getCache()->get($path);
222
+        if ($cacheEntry instanceof CacheEntry) {
223
+            return $cacheEntry->getData();
224
+        } else {
225
+            return false;
226
+        }
227
+    }
228
+
229
+    /**
230
+     * Override this method if you need a different unique resource identifier for your object storage implementation.
231
+     * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
232
+     * You may need a mapping table to store your URN if it cannot be generated from the fileid.
233
+     *
234
+     * @param int $fileId the fileid
235
+     * @return null|string the unified resource name used to identify the object
236
+     */
237
+    public function getURN($fileId) {
238
+        if (is_numeric($fileId)) {
239
+            return $this->objectPrefix . $fileId;
240
+        }
241
+        return null;
242
+    }
243
+
244
+    public function opendir($path) {
245
+        $path = $this->normalizePath($path);
246
+
247
+        try {
248
+            $files = array();
249
+            $folderContents = $this->getCache()->getFolderContents($path);
250
+            foreach ($folderContents as $file) {
251
+                $files[] = $file['name'];
252
+            }
253
+
254
+            return IteratorDirectory::wrap($files);
255
+        } catch (\Exception $e) {
256
+            $this->logger->logException($e);
257
+            return false;
258
+        }
259
+    }
260
+
261
+    public function filetype($path) {
262
+        $path = $this->normalizePath($path);
263
+        $stat = $this->stat($path);
264
+        if ($stat) {
265
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
266
+                return 'dir';
267
+            }
268
+            return 'file';
269
+        } else {
270
+            return false;
271
+        }
272
+    }
273
+
274
+    public function fopen($path, $mode) {
275
+        $path = $this->normalizePath($path);
276
+
277
+        if (strrpos($path, '.') !== false) {
278
+            $ext = substr($path, strrpos($path, '.'));
279
+        } else {
280
+            $ext = '';
281
+        }
282
+
283
+        switch ($mode) {
284
+            case 'r':
285
+            case 'rb':
286
+                $stat = $this->stat($path);
287
+                if (is_array($stat)) {
288
+                    try {
289
+                        return $this->objectStore->readObject($this->getURN($stat['fileid']));
290
+                    } catch (NotFoundException $e) {
291
+                        $this->logger->logException($e, [
292
+                            'app' => 'objectstore',
293
+                            'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
294
+                        ]);
295
+                        throw $e;
296
+                    } catch (\Exception $ex) {
297
+                        $this->logger->logException($ex, [
298
+                            'app' => 'objectstore',
299
+                            'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
300
+                        ]);
301
+                        return false;
302
+                    }
303
+                } else {
304
+                    return false;
305
+                }
306
+            case 'w':
307
+            case 'wb':
308
+            case 'w+':
309
+            case 'wb+':
310
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
311
+                $handle = fopen($tmpFile, $mode);
312
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
313
+                    $this->writeBack($tmpFile, $path);
314
+                });
315
+            case 'a':
316
+            case 'ab':
317
+            case 'r+':
318
+            case 'a+':
319
+            case 'x':
320
+            case 'x+':
321
+            case 'c':
322
+            case 'c+':
323
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
324
+                if ($this->file_exists($path)) {
325
+                    $source = $this->fopen($path, 'r');
326
+                    file_put_contents($tmpFile, $source);
327
+                }
328
+                $handle = fopen($tmpFile, $mode);
329
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
330
+                    $this->writeBack($tmpFile, $path);
331
+                });
332
+        }
333
+        return false;
334
+    }
335
+
336
+    public function file_exists($path) {
337
+        $path = $this->normalizePath($path);
338
+        return (bool)$this->stat($path);
339
+    }
340
+
341
+    public function rename($source, $target) {
342
+        $source = $this->normalizePath($source);
343
+        $target = $this->normalizePath($target);
344
+        $this->remove($target);
345
+        $this->getCache()->move($source, $target);
346
+        $this->touch(dirname($target));
347
+        return true;
348
+    }
349
+
350
+    public function getMimeType($path) {
351
+        $path = $this->normalizePath($path);
352
+        return parent::getMimeType($path);
353
+    }
354
+
355
+    public function touch($path, $mtime = null) {
356
+        if (is_null($mtime)) {
357
+            $mtime = time();
358
+        }
359
+
360
+        $path = $this->normalizePath($path);
361
+        $dirName = dirname($path);
362
+        $parentExists = $this->is_dir($dirName);
363
+        if (!$parentExists) {
364
+            return false;
365
+        }
366
+
367
+        $stat = $this->stat($path);
368
+        if (is_array($stat)) {
369
+            // update existing mtime in db
370
+            $stat['mtime'] = $mtime;
371
+            $this->getCache()->update($stat['fileid'], $stat);
372
+        } else {
373
+            try {
374
+                //create a empty file, need to have at least on char to make it
375
+                // work with all object storage implementations
376
+                $this->file_put_contents($path, ' ');
377
+                $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
378
+                $stat = array(
379
+                    'etag' => $this->getETag($path),
380
+                    'mimetype' => $mimeType,
381
+                    'size' => 0,
382
+                    'mtime' => $mtime,
383
+                    'storage_mtime' => $mtime,
384
+                    'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
385
+                );
386
+                $this->getCache()->put($path, $stat);
387
+            } catch (\Exception $ex) {
388
+                $this->logger->logException($ex, [
389
+                    'app' => 'objectstore',
390
+                    'message' => 'Could not create object for ' . $path,
391
+                ]);
392
+                throw $ex;
393
+            }
394
+        }
395
+        return true;
396
+    }
397
+
398
+    public function writeBack($tmpFile, $path) {
399
+        $size = filesize($tmpFile);
400
+        $this->writeStream($path, fopen($tmpFile, 'r'), $size);
401
+    }
402
+
403
+    /**
404
+     * external changes are not supported, exclusive access to the object storage is assumed
405
+     *
406
+     * @param string $path
407
+     * @param int $time
408
+     * @return false
409
+     */
410
+    public function hasUpdated($path, $time) {
411
+        return false;
412
+    }
413
+
414
+    public function needsPartFile() {
415
+        return false;
416
+    }
417
+
418
+    public function file_put_contents($path, $data) {
419
+        $handle = $this->fopen($path, 'w+');
420
+        fwrite($handle, $data);
421
+        fclose($handle);
422
+        return true;
423
+    }
424
+
425
+    public function writeStream(string $path, $stream, int $size = null): int {
426
+        $stat = $this->stat($path);
427
+        if (empty($stat)) {
428
+            // create new file
429
+            $stat = [
430
+                'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
431
+            ];
432
+        }
433
+        // update stat with new data
434
+        $mTime = time();
435
+        $stat['size'] = (int)$size;
436
+        $stat['mtime'] = $mTime;
437
+        $stat['storage_mtime'] = $mTime;
438
+
439
+        $mimetypeDetector = \OC::$server->getMimeTypeDetector();
440
+        $mimetype = $mimetypeDetector->detectPath($path);
441
+
442
+        $stat['mimetype'] = $mimetype;
443
+        $stat['etag'] = $this->getETag($path);
444
+
445
+        $exists = $this->getCache()->inCache($path);
446
+        $uploadPath = $exists ? $path : $path . '.part';
447
+        $fileId = $this->getCache()->put($uploadPath, $stat);
448
+        $urn = $this->getURN($fileId);
449
+        try {
450
+            //upload to object storage
451
+            if ($size === null) {
452
+                $countStream = CountWrapper::wrap($stream, function ($writtenSize) use ($fileId, &$size) {
453
+                    $this->getCache()->update($fileId, [
454
+                        'size' => $writtenSize
455
+                    ]);
456
+                    $size = $writtenSize;
457
+                });
458
+                $this->objectStore->writeObject($urn, $countStream);
459
+                if (is_resource($countStream)) {
460
+                    fclose($countStream);
461
+                }
462
+            } else {
463
+                $this->objectStore->writeObject($urn, $stream);
464
+            }
465
+        } catch (\Exception $ex) {
466
+            $this->getCache()->remove($uploadPath);
467
+            $this->logger->logException($ex, [
468
+                'app' => 'objectstore',
469
+                'message' => 'Could not create object ' . $urn . ' for ' . $path,
470
+            ]);
471
+            throw $ex; // make this bubble up
472
+        }
473
+
474
+        if (!$exists) {
475
+            if ($this->objectStore->objectExists($urn)) {
476
+                $this->getCache()->move($uploadPath, $path);
477
+            } else {
478
+                $this->getCache()->remove($uploadPath);
479
+                throw new \Exception("Object not found after writing (urn: $urn, path: $path)", 404);
480
+            }
481
+        }
482
+
483
+        return $size;
484
+    }
485
+
486
+    public function getObjectStore(): IObjectStore {
487
+        return $this->objectStore;
488
+    }
489 489
 }
Please login to merge, or discard this patch.