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