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