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