Passed
Push — master ( d3d11c...e900f4 )
by Morris
10:26 queued 11s
created
lib/public/Lock/ILockingProvider.php 1 patch
Indentation   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -35,52 +35,52 @@
 block discarded – undo
35 35
  * @since 8.1.0
36 36
  */
37 37
 interface ILockingProvider {
38
-	/**
39
-	 * @since 8.1.0
40
-	 */
41
-	public const LOCK_SHARED = 1;
42
-	/**
43
-	 * @since 8.1.0
44
-	 */
45
-	public const LOCK_EXCLUSIVE = 2;
38
+    /**
39
+     * @since 8.1.0
40
+     */
41
+    public const LOCK_SHARED = 1;
42
+    /**
43
+     * @since 8.1.0
44
+     */
45
+    public const LOCK_EXCLUSIVE = 2;
46 46
 
47
-	/**
48
-	 * @param string $path
49
-	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
50
-	 * @return bool
51
-	 * @since 8.1.0
52
-	 */
53
-	public function isLocked(string $path, int $type): bool;
47
+    /**
48
+     * @param string $path
49
+     * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
50
+     * @return bool
51
+     * @since 8.1.0
52
+     */
53
+    public function isLocked(string $path, int $type): bool;
54 54
 
55
-	/**
56
-	 * @param string $path
57
-	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
58
-	 * @param string $readablePath human readable path to use in error messages, since 20.0.0
59
-	 * @throws \OCP\Lock\LockedException
60
-	 * @since 8.1.0
61
-	 */
62
-	public function acquireLock(string $path, int $type, string $readablePath = null);
55
+    /**
56
+     * @param string $path
57
+     * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
58
+     * @param string $readablePath human readable path to use in error messages, since 20.0.0
59
+     * @throws \OCP\Lock\LockedException
60
+     * @since 8.1.0
61
+     */
62
+    public function acquireLock(string $path, int $type, string $readablePath = null);
63 63
 
64
-	/**
65
-	 * @param string $path
66
-	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
67
-	 * @since 8.1.0
68
-	 */
69
-	public function releaseLock(string $path, int $type);
64
+    /**
65
+     * @param string $path
66
+     * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
67
+     * @since 8.1.0
68
+     */
69
+    public function releaseLock(string $path, int $type);
70 70
 
71
-	/**
72
-	 * Change the type of an existing lock
73
-	 *
74
-	 * @param string $path
75
-	 * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
76
-	 * @throws \OCP\Lock\LockedException
77
-	 * @since 8.1.0
78
-	 */
79
-	public function changeLock(string $path, int $targetType);
71
+    /**
72
+     * Change the type of an existing lock
73
+     *
74
+     * @param string $path
75
+     * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
76
+     * @throws \OCP\Lock\LockedException
77
+     * @since 8.1.0
78
+     */
79
+    public function changeLock(string $path, int $targetType);
80 80
 
81
-	/**
82
-	 * release all lock acquired by this instance
83
-	 * @since 8.1.0
84
-	 */
85
-	public function releaseAll();
81
+    /**
82
+     * release all lock acquired by this instance
83
+     * @since 8.1.0
84
+     */
85
+    public function releaseAll();
86 86
 }
Please login to merge, or discard this patch.
lib/public/Lock/LockedException.php 1 patch
Indentation   +44 added lines, -44 removed lines patch added patch discarded remove patch
@@ -37,52 +37,52 @@
 block discarded – undo
37 37
  */
38 38
 class LockedException extends \Exception {
39 39
 
40
-	/**
41
-	 * Locked path
42
-	 *
43
-	 * @var string
44
-	 */
45
-	private $path;
40
+    /**
41
+     * Locked path
42
+     *
43
+     * @var string
44
+     */
45
+    private $path;
46 46
 
47
-	/** @var string|null */
48
-	private $existingLock;
47
+    /** @var string|null */
48
+    private $existingLock;
49 49
 
50
-	/**
51
-	 * LockedException constructor.
52
-	 *
53
-	 * @param string $path locked path
54
-	 * @param \Exception|null $previous previous exception for cascading
55
-	 * @param string $existingLock since 14.0.0
56
-	 * @param string $readablePath since 20.0.0
57
-	 * @since 8.1.0
58
-	 */
59
-	public function __construct(string $path, \Exception $previous = null, string $existingLock = null, string $readablePath = null) {
60
-		if ($readablePath) {
61
-			$message = "\"$path\"(\"$readablePath\") is locked";
62
-		} else {
63
-			$message = '"' . $path . '" is locked';
64
-		}
65
-		$this->existingLock = $existingLock;
66
-		if ($existingLock) {
67
-			$message .= ', existing lock on file: ' . $existingLock;
68
-		}
69
-		parent::__construct($message, 0, $previous);
70
-		$this->path = $path;
71
-	}
50
+    /**
51
+     * LockedException constructor.
52
+     *
53
+     * @param string $path locked path
54
+     * @param \Exception|null $previous previous exception for cascading
55
+     * @param string $existingLock since 14.0.0
56
+     * @param string $readablePath since 20.0.0
57
+     * @since 8.1.0
58
+     */
59
+    public function __construct(string $path, \Exception $previous = null, string $existingLock = null, string $readablePath = null) {
60
+        if ($readablePath) {
61
+            $message = "\"$path\"(\"$readablePath\") is locked";
62
+        } else {
63
+            $message = '"' . $path . '" is locked';
64
+        }
65
+        $this->existingLock = $existingLock;
66
+        if ($existingLock) {
67
+            $message .= ', existing lock on file: ' . $existingLock;
68
+        }
69
+        parent::__construct($message, 0, $previous);
70
+        $this->path = $path;
71
+    }
72 72
 
73
-	/**
74
-	 * @return string
75
-	 * @since 8.1.0
76
-	 */
77
-	public function getPath(): string {
78
-		return $this->path;
79
-	}
73
+    /**
74
+     * @return string
75
+     * @since 8.1.0
76
+     */
77
+    public function getPath(): string {
78
+        return $this->path;
79
+    }
80 80
 
81
-	/**
82
-	 * @return string
83
-	 * @since 19.0.0
84
-	 */
85
-	public function getExistingLock(): ?string {
86
-		return $this->existingLock;
87
-	}
81
+    /**
82
+     * @return string
83
+     * @since 19.0.0
84
+     */
85
+    public function getExistingLock(): ?string {
86
+        return $this->existingLock;
87
+    }
88 88
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/Common.php 1 patch
Indentation   +801 added lines, -801 removed lines patch added patch discarded remove patch
@@ -76,809 +76,809 @@
 block discarded – undo
76 76
  * in classes which extend it, e.g. $this->stat() .
77 77
  */
78 78
 abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage {
79
-	use LocalTempFileTrait;
80
-
81
-	protected $cache;
82
-	protected $scanner;
83
-	protected $watcher;
84
-	protected $propagator;
85
-	protected $storageCache;
86
-	protected $updater;
87
-
88
-	protected $mountOptions = [];
89
-	protected $owner = null;
90
-
91
-	private $shouldLogLocks = null;
92
-	private $logger;
93
-
94
-	public function __construct($parameters) {
95
-	}
96
-
97
-	/**
98
-	 * Remove a file or folder
99
-	 *
100
-	 * @param string $path
101
-	 * @return bool
102
-	 */
103
-	protected function remove($path) {
104
-		if ($this->is_dir($path)) {
105
-			return $this->rmdir($path);
106
-		} elseif ($this->is_file($path)) {
107
-			return $this->unlink($path);
108
-		} else {
109
-			return false;
110
-		}
111
-	}
112
-
113
-	public function is_dir($path) {
114
-		return $this->filetype($path) === 'dir';
115
-	}
116
-
117
-	public function is_file($path) {
118
-		return $this->filetype($path) === 'file';
119
-	}
120
-
121
-	public function filesize($path) {
122
-		if ($this->is_dir($path)) {
123
-			return 0; //by definition
124
-		} else {
125
-			$stat = $this->stat($path);
126
-			if (isset($stat['size'])) {
127
-				return $stat['size'];
128
-			} else {
129
-				return 0;
130
-			}
131
-		}
132
-	}
133
-
134
-	public function isReadable($path) {
135
-		// at least check whether it exists
136
-		// subclasses might want to implement this more thoroughly
137
-		return $this->file_exists($path);
138
-	}
139
-
140
-	public function isUpdatable($path) {
141
-		// at least check whether it exists
142
-		// subclasses might want to implement this more thoroughly
143
-		// a non-existing file/folder isn't updatable
144
-		return $this->file_exists($path);
145
-	}
146
-
147
-	public function isCreatable($path) {
148
-		if ($this->is_dir($path) && $this->isUpdatable($path)) {
149
-			return true;
150
-		}
151
-		return false;
152
-	}
153
-
154
-	public function isDeletable($path) {
155
-		if ($path === '' || $path === '/') {
156
-			return false;
157
-		}
158
-		$parent = dirname($path);
159
-		return $this->isUpdatable($parent) && $this->isUpdatable($path);
160
-	}
161
-
162
-	public function isSharable($path) {
163
-		return $this->isReadable($path);
164
-	}
165
-
166
-	public function getPermissions($path) {
167
-		$permissions = 0;
168
-		if ($this->isCreatable($path)) {
169
-			$permissions |= \OCP\Constants::PERMISSION_CREATE;
170
-		}
171
-		if ($this->isReadable($path)) {
172
-			$permissions |= \OCP\Constants::PERMISSION_READ;
173
-		}
174
-		if ($this->isUpdatable($path)) {
175
-			$permissions |= \OCP\Constants::PERMISSION_UPDATE;
176
-		}
177
-		if ($this->isDeletable($path)) {
178
-			$permissions |= \OCP\Constants::PERMISSION_DELETE;
179
-		}
180
-		if ($this->isSharable($path)) {
181
-			$permissions |= \OCP\Constants::PERMISSION_SHARE;
182
-		}
183
-		return $permissions;
184
-	}
185
-
186
-	public function filemtime($path) {
187
-		$stat = $this->stat($path);
188
-		if (isset($stat['mtime']) && $stat['mtime'] > 0) {
189
-			return $stat['mtime'];
190
-		} else {
191
-			return 0;
192
-		}
193
-	}
194
-
195
-	public function file_get_contents($path) {
196
-		$handle = $this->fopen($path, "r");
197
-		if (!$handle) {
198
-			return false;
199
-		}
200
-		$data = stream_get_contents($handle);
201
-		fclose($handle);
202
-		return $data;
203
-	}
204
-
205
-	public function file_put_contents($path, $data) {
206
-		$handle = $this->fopen($path, "w");
207
-		$this->removeCachedFile($path);
208
-		$count = fwrite($handle, $data);
209
-		fclose($handle);
210
-		return $count;
211
-	}
212
-
213
-	public function rename($path1, $path2) {
214
-		$this->remove($path2);
215
-
216
-		$this->removeCachedFile($path1);
217
-		return $this->copy($path1, $path2) and $this->remove($path1);
218
-	}
219
-
220
-	public function copy($path1, $path2) {
221
-		if ($this->is_dir($path1)) {
222
-			$this->remove($path2);
223
-			$dir = $this->opendir($path1);
224
-			$this->mkdir($path2);
225
-			while ($file = readdir($dir)) {
226
-				if (!Filesystem::isIgnoredDir($file)) {
227
-					if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
228
-						return false;
229
-					}
230
-				}
231
-			}
232
-			closedir($dir);
233
-			return true;
234
-		} else {
235
-			$source = $this->fopen($path1, 'r');
236
-			$target = $this->fopen($path2, 'w');
237
-			[, $result] = \OC_Helper::streamCopy($source, $target);
238
-			if (!$result) {
239
-				\OC::$server->getLogger()->warning("Failed to write data while copying $path1 to $path2");
240
-			}
241
-			$this->removeCachedFile($path2);
242
-			return $result;
243
-		}
244
-	}
245
-
246
-	public function getMimeType($path) {
247
-		if ($this->is_dir($path)) {
248
-			return 'httpd/unix-directory';
249
-		} elseif ($this->file_exists($path)) {
250
-			return \OC::$server->getMimeTypeDetector()->detectPath($path);
251
-		} else {
252
-			return false;
253
-		}
254
-	}
255
-
256
-	public function hash($type, $path, $raw = false) {
257
-		$fh = $this->fopen($path, 'rb');
258
-		$ctx = hash_init($type);
259
-		hash_update_stream($ctx, $fh);
260
-		fclose($fh);
261
-		return hash_final($ctx, $raw);
262
-	}
263
-
264
-	public function search($query) {
265
-		return $this->searchInDir($query);
266
-	}
267
-
268
-	public function getLocalFile($path) {
269
-		return $this->getCachedFile($path);
270
-	}
271
-
272
-	/**
273
-	 * @param string $path
274
-	 * @param string $target
275
-	 */
276
-	private function addLocalFolder($path, $target) {
277
-		$dh = $this->opendir($path);
278
-		if (is_resource($dh)) {
279
-			while (($file = readdir($dh)) !== false) {
280
-				if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
281
-					if ($this->is_dir($path . '/' . $file)) {
282
-						mkdir($target . '/' . $file);
283
-						$this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
284
-					} else {
285
-						$tmp = $this->toTmpFile($path . '/' . $file);
286
-						rename($tmp, $target . '/' . $file);
287
-					}
288
-				}
289
-			}
290
-		}
291
-	}
292
-
293
-	/**
294
-	 * @param string $query
295
-	 * @param string $dir
296
-	 * @return array
297
-	 */
298
-	protected function searchInDir($query, $dir = '') {
299
-		$files = [];
300
-		$dh = $this->opendir($dir);
301
-		if (is_resource($dh)) {
302
-			while (($item = readdir($dh)) !== false) {
303
-				if (\OC\Files\Filesystem::isIgnoredDir($item)) {
304
-					continue;
305
-				}
306
-				if (strstr(strtolower($item), strtolower($query)) !== false) {
307
-					$files[] = $dir . '/' . $item;
308
-				}
309
-				if ($this->is_dir($dir . '/' . $item)) {
310
-					$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
311
-				}
312
-			}
313
-		}
314
-		closedir($dh);
315
-		return $files;
316
-	}
317
-
318
-	/**
319
-	 * check if a file or folder has been updated since $time
320
-	 *
321
-	 * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
322
-	 * the mtime should always return false here. As a result storage implementations that always return false expect
323
-	 * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
324
-	 * ownClouds filesystem.
325
-	 *
326
-	 * @param string $path
327
-	 * @param int $time
328
-	 * @return bool
329
-	 */
330
-	public function hasUpdated($path, $time) {
331
-		return $this->filemtime($path) > $time;
332
-	}
333
-
334
-	public function getCache($path = '', $storage = null) {
335
-		if (!$storage) {
336
-			$storage = $this;
337
-		}
338
-		if (!isset($storage->cache)) {
339
-			$storage->cache = new Cache($storage);
340
-		}
341
-		return $storage->cache;
342
-	}
343
-
344
-	public function getScanner($path = '', $storage = null) {
345
-		if (!$storage) {
346
-			$storage = $this;
347
-		}
348
-		if (!isset($storage->scanner)) {
349
-			$storage->scanner = new Scanner($storage);
350
-		}
351
-		return $storage->scanner;
352
-	}
353
-
354
-	public function getWatcher($path = '', $storage = null) {
355
-		if (!$storage) {
356
-			$storage = $this;
357
-		}
358
-		if (!isset($this->watcher)) {
359
-			$this->watcher = new Watcher($storage);
360
-			$globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
361
-			$this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
362
-		}
363
-		return $this->watcher;
364
-	}
365
-
366
-	/**
367
-	 * get a propagator instance for the cache
368
-	 *
369
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
370
-	 * @return \OC\Files\Cache\Propagator
371
-	 */
372
-	public function getPropagator($storage = null) {
373
-		if (!$storage) {
374
-			$storage = $this;
375
-		}
376
-		if (!isset($storage->propagator)) {
377
-			$config = \OC::$server->getSystemConfig();
378
-			$storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection(), ['appdata_' . $config->getValue('instanceid')]);
379
-		}
380
-		return $storage->propagator;
381
-	}
382
-
383
-	public function getUpdater($storage = null) {
384
-		if (!$storage) {
385
-			$storage = $this;
386
-		}
387
-		if (!isset($storage->updater)) {
388
-			$storage->updater = new Updater($storage);
389
-		}
390
-		return $storage->updater;
391
-	}
392
-
393
-	public function getStorageCache($storage = null) {
394
-		if (!$storage) {
395
-			$storage = $this;
396
-		}
397
-		if (!isset($this->storageCache)) {
398
-			$this->storageCache = new \OC\Files\Cache\Storage($storage);
399
-		}
400
-		return $this->storageCache;
401
-	}
402
-
403
-	/**
404
-	 * get the owner of a path
405
-	 *
406
-	 * @param string $path The path to get the owner
407
-	 * @return string|false uid or false
408
-	 */
409
-	public function getOwner($path) {
410
-		if ($this->owner === null) {
411
-			$this->owner = \OC_User::getUser();
412
-		}
413
-
414
-		return $this->owner;
415
-	}
416
-
417
-	/**
418
-	 * get the ETag for a file or folder
419
-	 *
420
-	 * @param string $path
421
-	 * @return string
422
-	 */
423
-	public function getETag($path) {
424
-		return uniqid();
425
-	}
426
-
427
-	/**
428
-	 * clean a path, i.e. remove all redundant '.' and '..'
429
-	 * making sure that it can't point to higher than '/'
430
-	 *
431
-	 * @param string $path The path to clean
432
-	 * @return string cleaned path
433
-	 */
434
-	public function cleanPath($path) {
435
-		if (strlen($path) == 0 or $path[0] != '/') {
436
-			$path = '/' . $path;
437
-		}
438
-
439
-		$output = [];
440
-		foreach (explode('/', $path) as $chunk) {
441
-			if ($chunk == '..') {
442
-				array_pop($output);
443
-			} elseif ($chunk == '.') {
444
-			} else {
445
-				$output[] = $chunk;
446
-			}
447
-		}
448
-		return implode('/', $output);
449
-	}
450
-
451
-	/**
452
-	 * Test a storage for availability
453
-	 *
454
-	 * @return bool
455
-	 */
456
-	public function test() {
457
-		try {
458
-			if ($this->stat('')) {
459
-				return true;
460
-			}
461
-			\OC::$server->getLogger()->info("External storage not available: stat() failed");
462
-			return false;
463
-		} catch (\Exception $e) {
464
-			\OC::$server->getLogger()->warning("External storage not available: " . $e->getMessage());
465
-			\OC::$server->getLogger()->logException($e, ['level' => ILogger::WARN]);
466
-			return false;
467
-		}
468
-	}
469
-
470
-	/**
471
-	 * get the free space in the storage
472
-	 *
473
-	 * @param string $path
474
-	 * @return int|false
475
-	 */
476
-	public function free_space($path) {
477
-		return \OCP\Files\FileInfo::SPACE_UNKNOWN;
478
-	}
479
-
480
-	/**
481
-	 * {@inheritdoc}
482
-	 */
483
-	public function isLocal() {
484
-		// the common implementation returns a temporary file by
485
-		// default, which is not local
486
-		return false;
487
-	}
488
-
489
-	/**
490
-	 * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
491
-	 *
492
-	 * @param string $class
493
-	 * @return bool
494
-	 */
495
-	public function instanceOfStorage($class) {
496
-		if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') {
497
-			// FIXME Temporary fix to keep existing checks working
498
-			$class = '\OCA\Files_Sharing\SharedStorage';
499
-		}
500
-		return is_a($this, $class);
501
-	}
502
-
503
-	/**
504
-	 * A custom storage implementation can return an url for direct download of a give file.
505
-	 *
506
-	 * For now the returned array can hold the parameter url - in future more attributes might follow.
507
-	 *
508
-	 * @param string $path
509
-	 * @return array|false
510
-	 */
511
-	public function getDirectDownload($path) {
512
-		return [];
513
-	}
514
-
515
-	/**
516
-	 * @inheritdoc
517
-	 * @throws InvalidPathException
518
-	 */
519
-	public function verifyPath($path, $fileName) {
520
-
521
-		// verify empty and dot files
522
-		$trimmed = trim($fileName);
523
-		if ($trimmed === '') {
524
-			throw new EmptyFileNameException();
525
-		}
526
-
527
-		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
528
-			throw new InvalidDirectoryException();
529
-		}
530
-
531
-		if (!\OC::$server->getDatabaseConnection()->supports4ByteText()) {
532
-			// verify database - e.g. mysql only 3-byte chars
533
-			if (preg_match('%(?:
79
+    use LocalTempFileTrait;
80
+
81
+    protected $cache;
82
+    protected $scanner;
83
+    protected $watcher;
84
+    protected $propagator;
85
+    protected $storageCache;
86
+    protected $updater;
87
+
88
+    protected $mountOptions = [];
89
+    protected $owner = null;
90
+
91
+    private $shouldLogLocks = null;
92
+    private $logger;
93
+
94
+    public function __construct($parameters) {
95
+    }
96
+
97
+    /**
98
+     * Remove a file or folder
99
+     *
100
+     * @param string $path
101
+     * @return bool
102
+     */
103
+    protected function remove($path) {
104
+        if ($this->is_dir($path)) {
105
+            return $this->rmdir($path);
106
+        } elseif ($this->is_file($path)) {
107
+            return $this->unlink($path);
108
+        } else {
109
+            return false;
110
+        }
111
+    }
112
+
113
+    public function is_dir($path) {
114
+        return $this->filetype($path) === 'dir';
115
+    }
116
+
117
+    public function is_file($path) {
118
+        return $this->filetype($path) === 'file';
119
+    }
120
+
121
+    public function filesize($path) {
122
+        if ($this->is_dir($path)) {
123
+            return 0; //by definition
124
+        } else {
125
+            $stat = $this->stat($path);
126
+            if (isset($stat['size'])) {
127
+                return $stat['size'];
128
+            } else {
129
+                return 0;
130
+            }
131
+        }
132
+    }
133
+
134
+    public function isReadable($path) {
135
+        // at least check whether it exists
136
+        // subclasses might want to implement this more thoroughly
137
+        return $this->file_exists($path);
138
+    }
139
+
140
+    public function isUpdatable($path) {
141
+        // at least check whether it exists
142
+        // subclasses might want to implement this more thoroughly
143
+        // a non-existing file/folder isn't updatable
144
+        return $this->file_exists($path);
145
+    }
146
+
147
+    public function isCreatable($path) {
148
+        if ($this->is_dir($path) && $this->isUpdatable($path)) {
149
+            return true;
150
+        }
151
+        return false;
152
+    }
153
+
154
+    public function isDeletable($path) {
155
+        if ($path === '' || $path === '/') {
156
+            return false;
157
+        }
158
+        $parent = dirname($path);
159
+        return $this->isUpdatable($parent) && $this->isUpdatable($path);
160
+    }
161
+
162
+    public function isSharable($path) {
163
+        return $this->isReadable($path);
164
+    }
165
+
166
+    public function getPermissions($path) {
167
+        $permissions = 0;
168
+        if ($this->isCreatable($path)) {
169
+            $permissions |= \OCP\Constants::PERMISSION_CREATE;
170
+        }
171
+        if ($this->isReadable($path)) {
172
+            $permissions |= \OCP\Constants::PERMISSION_READ;
173
+        }
174
+        if ($this->isUpdatable($path)) {
175
+            $permissions |= \OCP\Constants::PERMISSION_UPDATE;
176
+        }
177
+        if ($this->isDeletable($path)) {
178
+            $permissions |= \OCP\Constants::PERMISSION_DELETE;
179
+        }
180
+        if ($this->isSharable($path)) {
181
+            $permissions |= \OCP\Constants::PERMISSION_SHARE;
182
+        }
183
+        return $permissions;
184
+    }
185
+
186
+    public function filemtime($path) {
187
+        $stat = $this->stat($path);
188
+        if (isset($stat['mtime']) && $stat['mtime'] > 0) {
189
+            return $stat['mtime'];
190
+        } else {
191
+            return 0;
192
+        }
193
+    }
194
+
195
+    public function file_get_contents($path) {
196
+        $handle = $this->fopen($path, "r");
197
+        if (!$handle) {
198
+            return false;
199
+        }
200
+        $data = stream_get_contents($handle);
201
+        fclose($handle);
202
+        return $data;
203
+    }
204
+
205
+    public function file_put_contents($path, $data) {
206
+        $handle = $this->fopen($path, "w");
207
+        $this->removeCachedFile($path);
208
+        $count = fwrite($handle, $data);
209
+        fclose($handle);
210
+        return $count;
211
+    }
212
+
213
+    public function rename($path1, $path2) {
214
+        $this->remove($path2);
215
+
216
+        $this->removeCachedFile($path1);
217
+        return $this->copy($path1, $path2) and $this->remove($path1);
218
+    }
219
+
220
+    public function copy($path1, $path2) {
221
+        if ($this->is_dir($path1)) {
222
+            $this->remove($path2);
223
+            $dir = $this->opendir($path1);
224
+            $this->mkdir($path2);
225
+            while ($file = readdir($dir)) {
226
+                if (!Filesystem::isIgnoredDir($file)) {
227
+                    if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
228
+                        return false;
229
+                    }
230
+                }
231
+            }
232
+            closedir($dir);
233
+            return true;
234
+        } else {
235
+            $source = $this->fopen($path1, 'r');
236
+            $target = $this->fopen($path2, 'w');
237
+            [, $result] = \OC_Helper::streamCopy($source, $target);
238
+            if (!$result) {
239
+                \OC::$server->getLogger()->warning("Failed to write data while copying $path1 to $path2");
240
+            }
241
+            $this->removeCachedFile($path2);
242
+            return $result;
243
+        }
244
+    }
245
+
246
+    public function getMimeType($path) {
247
+        if ($this->is_dir($path)) {
248
+            return 'httpd/unix-directory';
249
+        } elseif ($this->file_exists($path)) {
250
+            return \OC::$server->getMimeTypeDetector()->detectPath($path);
251
+        } else {
252
+            return false;
253
+        }
254
+    }
255
+
256
+    public function hash($type, $path, $raw = false) {
257
+        $fh = $this->fopen($path, 'rb');
258
+        $ctx = hash_init($type);
259
+        hash_update_stream($ctx, $fh);
260
+        fclose($fh);
261
+        return hash_final($ctx, $raw);
262
+    }
263
+
264
+    public function search($query) {
265
+        return $this->searchInDir($query);
266
+    }
267
+
268
+    public function getLocalFile($path) {
269
+        return $this->getCachedFile($path);
270
+    }
271
+
272
+    /**
273
+     * @param string $path
274
+     * @param string $target
275
+     */
276
+    private function addLocalFolder($path, $target) {
277
+        $dh = $this->opendir($path);
278
+        if (is_resource($dh)) {
279
+            while (($file = readdir($dh)) !== false) {
280
+                if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
281
+                    if ($this->is_dir($path . '/' . $file)) {
282
+                        mkdir($target . '/' . $file);
283
+                        $this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
284
+                    } else {
285
+                        $tmp = $this->toTmpFile($path . '/' . $file);
286
+                        rename($tmp, $target . '/' . $file);
287
+                    }
288
+                }
289
+            }
290
+        }
291
+    }
292
+
293
+    /**
294
+     * @param string $query
295
+     * @param string $dir
296
+     * @return array
297
+     */
298
+    protected function searchInDir($query, $dir = '') {
299
+        $files = [];
300
+        $dh = $this->opendir($dir);
301
+        if (is_resource($dh)) {
302
+            while (($item = readdir($dh)) !== false) {
303
+                if (\OC\Files\Filesystem::isIgnoredDir($item)) {
304
+                    continue;
305
+                }
306
+                if (strstr(strtolower($item), strtolower($query)) !== false) {
307
+                    $files[] = $dir . '/' . $item;
308
+                }
309
+                if ($this->is_dir($dir . '/' . $item)) {
310
+                    $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
311
+                }
312
+            }
313
+        }
314
+        closedir($dh);
315
+        return $files;
316
+    }
317
+
318
+    /**
319
+     * check if a file or folder has been updated since $time
320
+     *
321
+     * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
322
+     * the mtime should always return false here. As a result storage implementations that always return false expect
323
+     * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
324
+     * ownClouds filesystem.
325
+     *
326
+     * @param string $path
327
+     * @param int $time
328
+     * @return bool
329
+     */
330
+    public function hasUpdated($path, $time) {
331
+        return $this->filemtime($path) > $time;
332
+    }
333
+
334
+    public function getCache($path = '', $storage = null) {
335
+        if (!$storage) {
336
+            $storage = $this;
337
+        }
338
+        if (!isset($storage->cache)) {
339
+            $storage->cache = new Cache($storage);
340
+        }
341
+        return $storage->cache;
342
+    }
343
+
344
+    public function getScanner($path = '', $storage = null) {
345
+        if (!$storage) {
346
+            $storage = $this;
347
+        }
348
+        if (!isset($storage->scanner)) {
349
+            $storage->scanner = new Scanner($storage);
350
+        }
351
+        return $storage->scanner;
352
+    }
353
+
354
+    public function getWatcher($path = '', $storage = null) {
355
+        if (!$storage) {
356
+            $storage = $this;
357
+        }
358
+        if (!isset($this->watcher)) {
359
+            $this->watcher = new Watcher($storage);
360
+            $globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
361
+            $this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
362
+        }
363
+        return $this->watcher;
364
+    }
365
+
366
+    /**
367
+     * get a propagator instance for the cache
368
+     *
369
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
370
+     * @return \OC\Files\Cache\Propagator
371
+     */
372
+    public function getPropagator($storage = null) {
373
+        if (!$storage) {
374
+            $storage = $this;
375
+        }
376
+        if (!isset($storage->propagator)) {
377
+            $config = \OC::$server->getSystemConfig();
378
+            $storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection(), ['appdata_' . $config->getValue('instanceid')]);
379
+        }
380
+        return $storage->propagator;
381
+    }
382
+
383
+    public function getUpdater($storage = null) {
384
+        if (!$storage) {
385
+            $storage = $this;
386
+        }
387
+        if (!isset($storage->updater)) {
388
+            $storage->updater = new Updater($storage);
389
+        }
390
+        return $storage->updater;
391
+    }
392
+
393
+    public function getStorageCache($storage = null) {
394
+        if (!$storage) {
395
+            $storage = $this;
396
+        }
397
+        if (!isset($this->storageCache)) {
398
+            $this->storageCache = new \OC\Files\Cache\Storage($storage);
399
+        }
400
+        return $this->storageCache;
401
+    }
402
+
403
+    /**
404
+     * get the owner of a path
405
+     *
406
+     * @param string $path The path to get the owner
407
+     * @return string|false uid or false
408
+     */
409
+    public function getOwner($path) {
410
+        if ($this->owner === null) {
411
+            $this->owner = \OC_User::getUser();
412
+        }
413
+
414
+        return $this->owner;
415
+    }
416
+
417
+    /**
418
+     * get the ETag for a file or folder
419
+     *
420
+     * @param string $path
421
+     * @return string
422
+     */
423
+    public function getETag($path) {
424
+        return uniqid();
425
+    }
426
+
427
+    /**
428
+     * clean a path, i.e. remove all redundant '.' and '..'
429
+     * making sure that it can't point to higher than '/'
430
+     *
431
+     * @param string $path The path to clean
432
+     * @return string cleaned path
433
+     */
434
+    public function cleanPath($path) {
435
+        if (strlen($path) == 0 or $path[0] != '/') {
436
+            $path = '/' . $path;
437
+        }
438
+
439
+        $output = [];
440
+        foreach (explode('/', $path) as $chunk) {
441
+            if ($chunk == '..') {
442
+                array_pop($output);
443
+            } elseif ($chunk == '.') {
444
+            } else {
445
+                $output[] = $chunk;
446
+            }
447
+        }
448
+        return implode('/', $output);
449
+    }
450
+
451
+    /**
452
+     * Test a storage for availability
453
+     *
454
+     * @return bool
455
+     */
456
+    public function test() {
457
+        try {
458
+            if ($this->stat('')) {
459
+                return true;
460
+            }
461
+            \OC::$server->getLogger()->info("External storage not available: stat() failed");
462
+            return false;
463
+        } catch (\Exception $e) {
464
+            \OC::$server->getLogger()->warning("External storage not available: " . $e->getMessage());
465
+            \OC::$server->getLogger()->logException($e, ['level' => ILogger::WARN]);
466
+            return false;
467
+        }
468
+    }
469
+
470
+    /**
471
+     * get the free space in the storage
472
+     *
473
+     * @param string $path
474
+     * @return int|false
475
+     */
476
+    public function free_space($path) {
477
+        return \OCP\Files\FileInfo::SPACE_UNKNOWN;
478
+    }
479
+
480
+    /**
481
+     * {@inheritdoc}
482
+     */
483
+    public function isLocal() {
484
+        // the common implementation returns a temporary file by
485
+        // default, which is not local
486
+        return false;
487
+    }
488
+
489
+    /**
490
+     * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
491
+     *
492
+     * @param string $class
493
+     * @return bool
494
+     */
495
+    public function instanceOfStorage($class) {
496
+        if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') {
497
+            // FIXME Temporary fix to keep existing checks working
498
+            $class = '\OCA\Files_Sharing\SharedStorage';
499
+        }
500
+        return is_a($this, $class);
501
+    }
502
+
503
+    /**
504
+     * A custom storage implementation can return an url for direct download of a give file.
505
+     *
506
+     * For now the returned array can hold the parameter url - in future more attributes might follow.
507
+     *
508
+     * @param string $path
509
+     * @return array|false
510
+     */
511
+    public function getDirectDownload($path) {
512
+        return [];
513
+    }
514
+
515
+    /**
516
+     * @inheritdoc
517
+     * @throws InvalidPathException
518
+     */
519
+    public function verifyPath($path, $fileName) {
520
+
521
+        // verify empty and dot files
522
+        $trimmed = trim($fileName);
523
+        if ($trimmed === '') {
524
+            throw new EmptyFileNameException();
525
+        }
526
+
527
+        if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
528
+            throw new InvalidDirectoryException();
529
+        }
530
+
531
+        if (!\OC::$server->getDatabaseConnection()->supports4ByteText()) {
532
+            // verify database - e.g. mysql only 3-byte chars
533
+            if (preg_match('%(?:
534 534
       \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
535 535
     | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
536 536
     | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
537 537
 )%xs', $fileName)) {
538
-				throw new InvalidCharacterInPathException();
539
-			}
540
-		}
541
-
542
-		// 255 characters is the limit on common file systems (ext/xfs)
543
-		// oc_filecache has a 250 char length limit for the filename
544
-		if (isset($fileName[250])) {
545
-			throw new FileNameTooLongException();
546
-		}
547
-
548
-		// NOTE: $path will remain unverified for now
549
-		$this->verifyPosixPath($fileName);
550
-	}
551
-
552
-	/**
553
-	 * @param string $fileName
554
-	 * @throws InvalidPathException
555
-	 */
556
-	protected function verifyPosixPath($fileName) {
557
-		$fileName = trim($fileName);
558
-		$this->scanForInvalidCharacters($fileName, "\\/");
559
-		$reservedNames = ['*'];
560
-		if (in_array($fileName, $reservedNames)) {
561
-			throw new ReservedWordException();
562
-		}
563
-	}
564
-
565
-	/**
566
-	 * @param string $fileName
567
-	 * @param string $invalidChars
568
-	 * @throws InvalidPathException
569
-	 */
570
-	private function scanForInvalidCharacters($fileName, $invalidChars) {
571
-		foreach (str_split($invalidChars) as $char) {
572
-			if (strpos($fileName, $char) !== false) {
573
-				throw new InvalidCharacterInPathException();
574
-			}
575
-		}
576
-
577
-		$sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
578
-		if ($sanitizedFileName !== $fileName) {
579
-			throw new InvalidCharacterInPathException();
580
-		}
581
-	}
582
-
583
-	/**
584
-	 * @param array $options
585
-	 */
586
-	public function setMountOptions(array $options) {
587
-		$this->mountOptions = $options;
588
-	}
589
-
590
-	/**
591
-	 * @param string $name
592
-	 * @param mixed $default
593
-	 * @return mixed
594
-	 */
595
-	public function getMountOption($name, $default = null) {
596
-		return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
597
-	}
598
-
599
-	/**
600
-	 * @param IStorage $sourceStorage
601
-	 * @param string $sourceInternalPath
602
-	 * @param string $targetInternalPath
603
-	 * @param bool $preserveMtime
604
-	 * @return bool
605
-	 */
606
-	public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
607
-		if ($sourceStorage === $this) {
608
-			return $this->copy($sourceInternalPath, $targetInternalPath);
609
-		}
610
-
611
-		if ($sourceStorage->is_dir($sourceInternalPath)) {
612
-			$dh = $sourceStorage->opendir($sourceInternalPath);
613
-			$result = $this->mkdir($targetInternalPath);
614
-			if (is_resource($dh)) {
615
-				while ($result and ($file = readdir($dh)) !== false) {
616
-					if (!Filesystem::isIgnoredDir($file)) {
617
-						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
618
-					}
619
-				}
620
-			}
621
-		} else {
622
-			$source = $sourceStorage->fopen($sourceInternalPath, 'r');
623
-			// TODO: call fopen in a way that we execute again all storage wrappers
624
-			// to avoid that we bypass storage wrappers which perform important actions
625
-			// for this operation. Same is true for all other operations which
626
-			// are not the same as the original one.Once this is fixed we also
627
-			// need to adjust the encryption wrapper.
628
-			$target = $this->fopen($targetInternalPath, 'w');
629
-			[, $result] = \OC_Helper::streamCopy($source, $target);
630
-			if ($result and $preserveMtime) {
631
-				$this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
632
-			}
633
-			fclose($source);
634
-			fclose($target);
635
-
636
-			if (!$result) {
637
-				// delete partially written target file
638
-				$this->unlink($targetInternalPath);
639
-				// delete cache entry that was created by fopen
640
-				$this->getCache()->remove($targetInternalPath);
641
-			}
642
-		}
643
-		return (bool)$result;
644
-	}
645
-
646
-	/**
647
-	 * Check if a storage is the same as the current one, including wrapped storages
648
-	 *
649
-	 * @param IStorage $storage
650
-	 * @return bool
651
-	 */
652
-	private function isSameStorage(IStorage $storage): bool {
653
-		while ($storage->instanceOfStorage(Wrapper::class)) {
654
-			/**
655
-			 * @var Wrapper $sourceStorage
656
-			 */
657
-			$storage = $storage->getWrapperStorage();
658
-		}
659
-
660
-		return $storage === $this;
661
-	}
662
-
663
-	/**
664
-	 * @param IStorage $sourceStorage
665
-	 * @param string $sourceInternalPath
666
-	 * @param string $targetInternalPath
667
-	 * @return bool
668
-	 */
669
-	public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
670
-		if ($this->isSameStorage($sourceStorage)) {
671
-			// resolve any jailed paths
672
-			while ($sourceStorage->instanceOfStorage(Jail::class)) {
673
-				/**
674
-				 * @var Jail $sourceStorage
675
-				 */
676
-				$sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
677
-				$sourceStorage = $sourceStorage->getUnjailedStorage();
678
-			}
679
-
680
-			return $this->rename($sourceInternalPath, $targetInternalPath);
681
-		}
682
-
683
-		if (!$sourceStorage->isDeletable($sourceInternalPath)) {
684
-			return false;
685
-		}
686
-
687
-		$result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
688
-		if ($result) {
689
-			if ($sourceStorage->is_dir($sourceInternalPath)) {
690
-				$result &= $sourceStorage->rmdir($sourceInternalPath);
691
-			} else {
692
-				$result &= $sourceStorage->unlink($sourceInternalPath);
693
-			}
694
-		}
695
-		return $result;
696
-	}
697
-
698
-	/**
699
-	 * @inheritdoc
700
-	 */
701
-	public function getMetaData($path) {
702
-		$permissions = $this->getPermissions($path);
703
-		if (!$permissions & \OCP\Constants::PERMISSION_READ) {
704
-			//can't read, nothing we can do
705
-			return null;
706
-		}
707
-
708
-		$data = [];
709
-		$data['mimetype'] = $this->getMimeType($path);
710
-		$data['mtime'] = $this->filemtime($path);
711
-		if ($data['mtime'] === false) {
712
-			$data['mtime'] = time();
713
-		}
714
-		if ($data['mimetype'] == 'httpd/unix-directory') {
715
-			$data['size'] = -1; //unknown
716
-		} else {
717
-			$data['size'] = $this->filesize($path);
718
-		}
719
-		$data['etag'] = $this->getETag($path);
720
-		$data['storage_mtime'] = $data['mtime'];
721
-		$data['permissions'] = $permissions;
722
-		$data['name'] = basename($path);
723
-
724
-		return $data;
725
-	}
726
-
727
-	/**
728
-	 * @param string $path
729
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
730
-	 * @param \OCP\Lock\ILockingProvider $provider
731
-	 * @throws \OCP\Lock\LockedException
732
-	 */
733
-	public function acquireLock($path, $type, ILockingProvider $provider) {
734
-		$logger = $this->getLockLogger();
735
-		if ($logger) {
736
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
737
-			$logger->info(
738
-				sprintf(
739
-					'acquire %s lock on "%s" on storage "%s"',
740
-					$typeString,
741
-					$path,
742
-					$this->getId()
743
-				),
744
-				[
745
-					'app' => 'locking',
746
-				]
747
-			);
748
-		}
749
-		try {
750
-			$provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type, $this->getId() . '::' . $path);
751
-		} catch (LockedException $e) {
752
-			if ($logger) {
753
-				$logger->logException($e, ['level' => ILogger::INFO]);
754
-			}
755
-			throw $e;
756
-		}
757
-	}
758
-
759
-	/**
760
-	 * @param string $path
761
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
762
-	 * @param \OCP\Lock\ILockingProvider $provider
763
-	 * @throws \OCP\Lock\LockedException
764
-	 */
765
-	public function releaseLock($path, $type, ILockingProvider $provider) {
766
-		$logger = $this->getLockLogger();
767
-		if ($logger) {
768
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
769
-			$logger->info(
770
-				sprintf(
771
-					'release %s lock on "%s" on storage "%s"',
772
-					$typeString,
773
-					$path,
774
-					$this->getId()
775
-				),
776
-				[
777
-					'app' => 'locking',
778
-				]
779
-			);
780
-		}
781
-		try {
782
-			$provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
783
-		} catch (LockedException $e) {
784
-			if ($logger) {
785
-				$logger->logException($e, ['level' => ILogger::INFO]);
786
-			}
787
-			throw $e;
788
-		}
789
-	}
790
-
791
-	/**
792
-	 * @param string $path
793
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
794
-	 * @param \OCP\Lock\ILockingProvider $provider
795
-	 * @throws \OCP\Lock\LockedException
796
-	 */
797
-	public function changeLock($path, $type, ILockingProvider $provider) {
798
-		$logger = $this->getLockLogger();
799
-		if ($logger) {
800
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
801
-			$logger->info(
802
-				sprintf(
803
-					'change lock on "%s" to %s on storage "%s"',
804
-					$path,
805
-					$typeString,
806
-					$this->getId()
807
-				),
808
-				[
809
-					'app' => 'locking',
810
-				]
811
-			);
812
-		}
813
-		try {
814
-			$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
815
-		} catch (LockedException $e) {
816
-			\OC::$server->getLogger()->logException($e, ['level' => ILogger::INFO]);
817
-			throw $e;
818
-		}
819
-	}
820
-
821
-	private function getLockLogger() {
822
-		if (is_null($this->shouldLogLocks)) {
823
-			$this->shouldLogLocks = \OC::$server->getConfig()->getSystemValue('filelocking.debug', false);
824
-			$this->logger = $this->shouldLogLocks ? \OC::$server->getLogger() : null;
825
-		}
826
-		return $this->logger;
827
-	}
828
-
829
-	/**
830
-	 * @return array [ available, last_checked ]
831
-	 */
832
-	public function getAvailability() {
833
-		return $this->getStorageCache()->getAvailability();
834
-	}
835
-
836
-	/**
837
-	 * @param bool $isAvailable
838
-	 */
839
-	public function setAvailability($isAvailable) {
840
-		$this->getStorageCache()->setAvailability($isAvailable);
841
-	}
842
-
843
-	/**
844
-	 * @return bool
845
-	 */
846
-	public function needsPartFile() {
847
-		return true;
848
-	}
849
-
850
-	/**
851
-	 * fallback implementation
852
-	 *
853
-	 * @param string $path
854
-	 * @param resource $stream
855
-	 * @param int $size
856
-	 * @return int
857
-	 */
858
-	public function writeStream(string $path, $stream, int $size = null): int {
859
-		$target = $this->fopen($path, 'w');
860
-		if (!$target) {
861
-			return 0;
862
-		}
863
-		try {
864
-			[$count, $result] = \OC_Helper::streamCopy($stream, $target);
865
-		} finally {
866
-			fclose($target);
867
-			fclose($stream);
868
-		}
869
-		return $count;
870
-	}
871
-
872
-	public function getDirectoryContent($directory): \Traversable {
873
-		$dh = $this->opendir($directory);
874
-		if (is_resource($dh)) {
875
-			$basePath = rtrim($directory, '/');
876
-			while (($file = readdir($dh)) !== false) {
877
-				if (!Filesystem::isIgnoredDir($file) && !Filesystem::isFileBlacklisted($file)) {
878
-					$childPath = $basePath . '/' . trim($file, '/');
879
-					yield $this->getMetaData($childPath);
880
-				}
881
-			}
882
-		}
883
-	}
538
+                throw new InvalidCharacterInPathException();
539
+            }
540
+        }
541
+
542
+        // 255 characters is the limit on common file systems (ext/xfs)
543
+        // oc_filecache has a 250 char length limit for the filename
544
+        if (isset($fileName[250])) {
545
+            throw new FileNameTooLongException();
546
+        }
547
+
548
+        // NOTE: $path will remain unverified for now
549
+        $this->verifyPosixPath($fileName);
550
+    }
551
+
552
+    /**
553
+     * @param string $fileName
554
+     * @throws InvalidPathException
555
+     */
556
+    protected function verifyPosixPath($fileName) {
557
+        $fileName = trim($fileName);
558
+        $this->scanForInvalidCharacters($fileName, "\\/");
559
+        $reservedNames = ['*'];
560
+        if (in_array($fileName, $reservedNames)) {
561
+            throw new ReservedWordException();
562
+        }
563
+    }
564
+
565
+    /**
566
+     * @param string $fileName
567
+     * @param string $invalidChars
568
+     * @throws InvalidPathException
569
+     */
570
+    private function scanForInvalidCharacters($fileName, $invalidChars) {
571
+        foreach (str_split($invalidChars) as $char) {
572
+            if (strpos($fileName, $char) !== false) {
573
+                throw new InvalidCharacterInPathException();
574
+            }
575
+        }
576
+
577
+        $sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
578
+        if ($sanitizedFileName !== $fileName) {
579
+            throw new InvalidCharacterInPathException();
580
+        }
581
+    }
582
+
583
+    /**
584
+     * @param array $options
585
+     */
586
+    public function setMountOptions(array $options) {
587
+        $this->mountOptions = $options;
588
+    }
589
+
590
+    /**
591
+     * @param string $name
592
+     * @param mixed $default
593
+     * @return mixed
594
+     */
595
+    public function getMountOption($name, $default = null) {
596
+        return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
597
+    }
598
+
599
+    /**
600
+     * @param IStorage $sourceStorage
601
+     * @param string $sourceInternalPath
602
+     * @param string $targetInternalPath
603
+     * @param bool $preserveMtime
604
+     * @return bool
605
+     */
606
+    public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
607
+        if ($sourceStorage === $this) {
608
+            return $this->copy($sourceInternalPath, $targetInternalPath);
609
+        }
610
+
611
+        if ($sourceStorage->is_dir($sourceInternalPath)) {
612
+            $dh = $sourceStorage->opendir($sourceInternalPath);
613
+            $result = $this->mkdir($targetInternalPath);
614
+            if (is_resource($dh)) {
615
+                while ($result and ($file = readdir($dh)) !== false) {
616
+                    if (!Filesystem::isIgnoredDir($file)) {
617
+                        $result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
618
+                    }
619
+                }
620
+            }
621
+        } else {
622
+            $source = $sourceStorage->fopen($sourceInternalPath, 'r');
623
+            // TODO: call fopen in a way that we execute again all storage wrappers
624
+            // to avoid that we bypass storage wrappers which perform important actions
625
+            // for this operation. Same is true for all other operations which
626
+            // are not the same as the original one.Once this is fixed we also
627
+            // need to adjust the encryption wrapper.
628
+            $target = $this->fopen($targetInternalPath, 'w');
629
+            [, $result] = \OC_Helper::streamCopy($source, $target);
630
+            if ($result and $preserveMtime) {
631
+                $this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
632
+            }
633
+            fclose($source);
634
+            fclose($target);
635
+
636
+            if (!$result) {
637
+                // delete partially written target file
638
+                $this->unlink($targetInternalPath);
639
+                // delete cache entry that was created by fopen
640
+                $this->getCache()->remove($targetInternalPath);
641
+            }
642
+        }
643
+        return (bool)$result;
644
+    }
645
+
646
+    /**
647
+     * Check if a storage is the same as the current one, including wrapped storages
648
+     *
649
+     * @param IStorage $storage
650
+     * @return bool
651
+     */
652
+    private function isSameStorage(IStorage $storage): bool {
653
+        while ($storage->instanceOfStorage(Wrapper::class)) {
654
+            /**
655
+             * @var Wrapper $sourceStorage
656
+             */
657
+            $storage = $storage->getWrapperStorage();
658
+        }
659
+
660
+        return $storage === $this;
661
+    }
662
+
663
+    /**
664
+     * @param IStorage $sourceStorage
665
+     * @param string $sourceInternalPath
666
+     * @param string $targetInternalPath
667
+     * @return bool
668
+     */
669
+    public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
670
+        if ($this->isSameStorage($sourceStorage)) {
671
+            // resolve any jailed paths
672
+            while ($sourceStorage->instanceOfStorage(Jail::class)) {
673
+                /**
674
+                 * @var Jail $sourceStorage
675
+                 */
676
+                $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
677
+                $sourceStorage = $sourceStorage->getUnjailedStorage();
678
+            }
679
+
680
+            return $this->rename($sourceInternalPath, $targetInternalPath);
681
+        }
682
+
683
+        if (!$sourceStorage->isDeletable($sourceInternalPath)) {
684
+            return false;
685
+        }
686
+
687
+        $result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
688
+        if ($result) {
689
+            if ($sourceStorage->is_dir($sourceInternalPath)) {
690
+                $result &= $sourceStorage->rmdir($sourceInternalPath);
691
+            } else {
692
+                $result &= $sourceStorage->unlink($sourceInternalPath);
693
+            }
694
+        }
695
+        return $result;
696
+    }
697
+
698
+    /**
699
+     * @inheritdoc
700
+     */
701
+    public function getMetaData($path) {
702
+        $permissions = $this->getPermissions($path);
703
+        if (!$permissions & \OCP\Constants::PERMISSION_READ) {
704
+            //can't read, nothing we can do
705
+            return null;
706
+        }
707
+
708
+        $data = [];
709
+        $data['mimetype'] = $this->getMimeType($path);
710
+        $data['mtime'] = $this->filemtime($path);
711
+        if ($data['mtime'] === false) {
712
+            $data['mtime'] = time();
713
+        }
714
+        if ($data['mimetype'] == 'httpd/unix-directory') {
715
+            $data['size'] = -1; //unknown
716
+        } else {
717
+            $data['size'] = $this->filesize($path);
718
+        }
719
+        $data['etag'] = $this->getETag($path);
720
+        $data['storage_mtime'] = $data['mtime'];
721
+        $data['permissions'] = $permissions;
722
+        $data['name'] = basename($path);
723
+
724
+        return $data;
725
+    }
726
+
727
+    /**
728
+     * @param string $path
729
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
730
+     * @param \OCP\Lock\ILockingProvider $provider
731
+     * @throws \OCP\Lock\LockedException
732
+     */
733
+    public function acquireLock($path, $type, ILockingProvider $provider) {
734
+        $logger = $this->getLockLogger();
735
+        if ($logger) {
736
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
737
+            $logger->info(
738
+                sprintf(
739
+                    'acquire %s lock on "%s" on storage "%s"',
740
+                    $typeString,
741
+                    $path,
742
+                    $this->getId()
743
+                ),
744
+                [
745
+                    'app' => 'locking',
746
+                ]
747
+            );
748
+        }
749
+        try {
750
+            $provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type, $this->getId() . '::' . $path);
751
+        } catch (LockedException $e) {
752
+            if ($logger) {
753
+                $logger->logException($e, ['level' => ILogger::INFO]);
754
+            }
755
+            throw $e;
756
+        }
757
+    }
758
+
759
+    /**
760
+     * @param string $path
761
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
762
+     * @param \OCP\Lock\ILockingProvider $provider
763
+     * @throws \OCP\Lock\LockedException
764
+     */
765
+    public function releaseLock($path, $type, ILockingProvider $provider) {
766
+        $logger = $this->getLockLogger();
767
+        if ($logger) {
768
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
769
+            $logger->info(
770
+                sprintf(
771
+                    'release %s lock on "%s" on storage "%s"',
772
+                    $typeString,
773
+                    $path,
774
+                    $this->getId()
775
+                ),
776
+                [
777
+                    'app' => 'locking',
778
+                ]
779
+            );
780
+        }
781
+        try {
782
+            $provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
783
+        } catch (LockedException $e) {
784
+            if ($logger) {
785
+                $logger->logException($e, ['level' => ILogger::INFO]);
786
+            }
787
+            throw $e;
788
+        }
789
+    }
790
+
791
+    /**
792
+     * @param string $path
793
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
794
+     * @param \OCP\Lock\ILockingProvider $provider
795
+     * @throws \OCP\Lock\LockedException
796
+     */
797
+    public function changeLock($path, $type, ILockingProvider $provider) {
798
+        $logger = $this->getLockLogger();
799
+        if ($logger) {
800
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
801
+            $logger->info(
802
+                sprintf(
803
+                    'change lock on "%s" to %s on storage "%s"',
804
+                    $path,
805
+                    $typeString,
806
+                    $this->getId()
807
+                ),
808
+                [
809
+                    'app' => 'locking',
810
+                ]
811
+            );
812
+        }
813
+        try {
814
+            $provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
815
+        } catch (LockedException $e) {
816
+            \OC::$server->getLogger()->logException($e, ['level' => ILogger::INFO]);
817
+            throw $e;
818
+        }
819
+    }
820
+
821
+    private function getLockLogger() {
822
+        if (is_null($this->shouldLogLocks)) {
823
+            $this->shouldLogLocks = \OC::$server->getConfig()->getSystemValue('filelocking.debug', false);
824
+            $this->logger = $this->shouldLogLocks ? \OC::$server->getLogger() : null;
825
+        }
826
+        return $this->logger;
827
+    }
828
+
829
+    /**
830
+     * @return array [ available, last_checked ]
831
+     */
832
+    public function getAvailability() {
833
+        return $this->getStorageCache()->getAvailability();
834
+    }
835
+
836
+    /**
837
+     * @param bool $isAvailable
838
+     */
839
+    public function setAvailability($isAvailable) {
840
+        $this->getStorageCache()->setAvailability($isAvailable);
841
+    }
842
+
843
+    /**
844
+     * @return bool
845
+     */
846
+    public function needsPartFile() {
847
+        return true;
848
+    }
849
+
850
+    /**
851
+     * fallback implementation
852
+     *
853
+     * @param string $path
854
+     * @param resource $stream
855
+     * @param int $size
856
+     * @return int
857
+     */
858
+    public function writeStream(string $path, $stream, int $size = null): int {
859
+        $target = $this->fopen($path, 'w');
860
+        if (!$target) {
861
+            return 0;
862
+        }
863
+        try {
864
+            [$count, $result] = \OC_Helper::streamCopy($stream, $target);
865
+        } finally {
866
+            fclose($target);
867
+            fclose($stream);
868
+        }
869
+        return $count;
870
+    }
871
+
872
+    public function getDirectoryContent($directory): \Traversable {
873
+        $dh = $this->opendir($directory);
874
+        if (is_resource($dh)) {
875
+            $basePath = rtrim($directory, '/');
876
+            while (($file = readdir($dh)) !== false) {
877
+                if (!Filesystem::isIgnoredDir($file) && !Filesystem::isFileBlacklisted($file)) {
878
+                    $childPath = $basePath . '/' . trim($file, '/');
879
+                    yield $this->getMetaData($childPath);
880
+                }
881
+            }
882
+        }
883
+    }
884 884
 }
Please login to merge, or discard this patch.
lib/private/Lock/NoopLockingProvider.php 1 patch
Indentation   +28 added lines, -28 removed lines patch added patch discarded remove patch
@@ -36,38 +36,38 @@
 block discarded – undo
36 36
  */
37 37
 class NoopLockingProvider implements ILockingProvider {
38 38
 
39
-	/**
40
-	 * {@inheritdoc}
41
-	 */
42
-	public function isLocked(string $path, int $type): bool {
43
-		return false;
44
-	}
39
+    /**
40
+     * {@inheritdoc}
41
+     */
42
+    public function isLocked(string $path, int $type): bool {
43
+        return false;
44
+    }
45 45
 
46
-	/**
47
-	 * {@inheritdoc}
48
-	 */
49
-	public function acquireLock(string $path, int $type, string $readablePath = null) {
50
-		// do nothing
51
-	}
46
+    /**
47
+     * {@inheritdoc}
48
+     */
49
+    public function acquireLock(string $path, int $type, string $readablePath = null) {
50
+        // do nothing
51
+    }
52 52
 
53
-	/**
54
-	 * {@inheritdoc}
55
-	 */
56
-	public function releaseLock(string $path, int $type) {
57
-		// do nothing
58
-	}
53
+    /**
54
+     * {@inheritdoc}
55
+     */
56
+    public function releaseLock(string $path, int $type) {
57
+        // do nothing
58
+    }
59 59
 
60
-	/**1
60
+    /**1
61 61
 	 * {@inheritdoc}
62 62
 	 */
63
-	public function releaseAll() {
64
-		// do nothing
65
-	}
63
+    public function releaseAll() {
64
+        // do nothing
65
+    }
66 66
 
67
-	/**
68
-	 * {@inheritdoc}
69
-	 */
70
-	public function changeLock(string $path, int $targetType) {
71
-		// do nothing
72
-	}
67
+    /**
68
+     * {@inheritdoc}
69
+     */
70
+    public function changeLock(string $path, int $targetType) {
71
+        // do nothing
72
+    }
73 73
 }
Please login to merge, or discard this patch.
lib/private/Lock/MemcacheLockingProvider.php 1 patch
Indentation   +108 added lines, -108 removed lines patch added patch discarded remove patch
@@ -32,120 +32,120 @@
 block discarded – undo
32 32
 use OCP\Lock\LockedException;
33 33
 
34 34
 class MemcacheLockingProvider extends AbstractLockingProvider {
35
-	/**
36
-	 * @var \OCP\IMemcache
37
-	 */
38
-	private $memcache;
35
+    /**
36
+     * @var \OCP\IMemcache
37
+     */
38
+    private $memcache;
39 39
 
40
-	/**
41
-	 * @param \OCP\IMemcache $memcache
42
-	 * @param int $ttl
43
-	 */
44
-	public function __construct(IMemcache $memcache, int $ttl = 3600) {
45
-		$this->memcache = $memcache;
46
-		$this->ttl = $ttl;
47
-	}
40
+    /**
41
+     * @param \OCP\IMemcache $memcache
42
+     * @param int $ttl
43
+     */
44
+    public function __construct(IMemcache $memcache, int $ttl = 3600) {
45
+        $this->memcache = $memcache;
46
+        $this->ttl = $ttl;
47
+    }
48 48
 
49
-	private function setTTL(string $path) {
50
-		if ($this->memcache instanceof IMemcacheTTL) {
51
-			$this->memcache->setTTL($path, $this->ttl);
52
-		}
53
-	}
49
+    private function setTTL(string $path) {
50
+        if ($this->memcache instanceof IMemcacheTTL) {
51
+            $this->memcache->setTTL($path, $this->ttl);
52
+        }
53
+    }
54 54
 
55
-	/**
56
-	 * @param string $path
57
-	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
58
-	 * @return bool
59
-	 */
60
-	public function isLocked(string $path, int $type): bool {
61
-		$lockValue = $this->memcache->get($path);
62
-		if ($type === self::LOCK_SHARED) {
63
-			return $lockValue > 0;
64
-		} elseif ($type === self::LOCK_EXCLUSIVE) {
65
-			return $lockValue === 'exclusive';
66
-		} else {
67
-			return false;
68
-		}
69
-	}
55
+    /**
56
+     * @param string $path
57
+     * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
58
+     * @return bool
59
+     */
60
+    public function isLocked(string $path, int $type): bool {
61
+        $lockValue = $this->memcache->get($path);
62
+        if ($type === self::LOCK_SHARED) {
63
+            return $lockValue > 0;
64
+        } elseif ($type === self::LOCK_EXCLUSIVE) {
65
+            return $lockValue === 'exclusive';
66
+        } else {
67
+            return false;
68
+        }
69
+    }
70 70
 
71
-	/**
72
-	 * @param string $path
73
-	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
74
-	 * @param string $readablePath human readable path to use in error messages
75
-	 * @throws \OCP\Lock\LockedException
76
-	 */
77
-	public function acquireLock(string $path, int $type, string $readablePath = null) {
78
-		if ($type === self::LOCK_SHARED) {
79
-			if (!$this->memcache->inc($path)) {
80
-				throw new LockedException($path, null, $this->getExistingLockForException($path), $readablePath);
81
-			}
82
-		} else {
83
-			$this->memcache->add($path, 0);
84
-			if (!$this->memcache->cas($path, 0, 'exclusive')) {
85
-				throw new LockedException($path, null, $this->getExistingLockForException($path), $readablePath);
86
-			}
87
-		}
88
-		$this->setTTL($path);
89
-		$this->markAcquire($path, $type);
90
-	}
71
+    /**
72
+     * @param string $path
73
+     * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
74
+     * @param string $readablePath human readable path to use in error messages
75
+     * @throws \OCP\Lock\LockedException
76
+     */
77
+    public function acquireLock(string $path, int $type, string $readablePath = null) {
78
+        if ($type === self::LOCK_SHARED) {
79
+            if (!$this->memcache->inc($path)) {
80
+                throw new LockedException($path, null, $this->getExistingLockForException($path), $readablePath);
81
+            }
82
+        } else {
83
+            $this->memcache->add($path, 0);
84
+            if (!$this->memcache->cas($path, 0, 'exclusive')) {
85
+                throw new LockedException($path, null, $this->getExistingLockForException($path), $readablePath);
86
+            }
87
+        }
88
+        $this->setTTL($path);
89
+        $this->markAcquire($path, $type);
90
+    }
91 91
 
92
-	/**
93
-	 * @param string $path
94
-	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
95
-	 */
96
-	public function releaseLock(string $path, int $type) {
97
-		if ($type === self::LOCK_SHARED) {
98
-			$newValue = 0;
99
-			if ($this->getOwnSharedLockCount($path) === 1) {
100
-				$removed = $this->memcache->cad($path, 1); // if we're the only one having a shared lock we can remove it in one go
101
-				if (!$removed) { //someone else also has a shared lock, decrease only
102
-					$newValue = $this->memcache->dec($path);
103
-				}
104
-			} else {
105
-				// if we own more than one lock ourselves just decrease
106
-				$newValue = $this->memcache->dec($path);
107
-			}
92
+    /**
93
+     * @param string $path
94
+     * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
95
+     */
96
+    public function releaseLock(string $path, int $type) {
97
+        if ($type === self::LOCK_SHARED) {
98
+            $newValue = 0;
99
+            if ($this->getOwnSharedLockCount($path) === 1) {
100
+                $removed = $this->memcache->cad($path, 1); // if we're the only one having a shared lock we can remove it in one go
101
+                if (!$removed) { //someone else also has a shared lock, decrease only
102
+                    $newValue = $this->memcache->dec($path);
103
+                }
104
+            } else {
105
+                // if we own more than one lock ourselves just decrease
106
+                $newValue = $this->memcache->dec($path);
107
+            }
108 108
 
109
-			// if we somehow release more locks then exists, reset the lock
110
-			if ($newValue < 0) {
111
-				$this->memcache->cad($path, $newValue);
112
-			}
113
-		} elseif ($type === self::LOCK_EXCLUSIVE) {
114
-			$this->memcache->cad($path, 'exclusive');
115
-		}
116
-		$this->markRelease($path, $type);
117
-	}
109
+            // if we somehow release more locks then exists, reset the lock
110
+            if ($newValue < 0) {
111
+                $this->memcache->cad($path, $newValue);
112
+            }
113
+        } elseif ($type === self::LOCK_EXCLUSIVE) {
114
+            $this->memcache->cad($path, 'exclusive');
115
+        }
116
+        $this->markRelease($path, $type);
117
+    }
118 118
 
119
-	/**
120
-	 * Change the type of an existing lock
121
-	 *
122
-	 * @param string $path
123
-	 * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
124
-	 * @throws \OCP\Lock\LockedException
125
-	 */
126
-	public function changeLock(string $path, int $targetType) {
127
-		if ($targetType === self::LOCK_SHARED) {
128
-			if (!$this->memcache->cas($path, 'exclusive', 1)) {
129
-				throw new LockedException($path, null, $this->getExistingLockForException($path));
130
-			}
131
-		} elseif ($targetType === self::LOCK_EXCLUSIVE) {
132
-			// we can only change a shared lock to an exclusive if there's only a single owner of the shared lock
133
-			if (!$this->memcache->cas($path, 1, 'exclusive')) {
134
-				throw new LockedException($path, null, $this->getExistingLockForException($path));
135
-			}
136
-		}
137
-		$this->setTTL($path);
138
-		$this->markChange($path, $targetType);
139
-	}
119
+    /**
120
+     * Change the type of an existing lock
121
+     *
122
+     * @param string $path
123
+     * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
124
+     * @throws \OCP\Lock\LockedException
125
+     */
126
+    public function changeLock(string $path, int $targetType) {
127
+        if ($targetType === self::LOCK_SHARED) {
128
+            if (!$this->memcache->cas($path, 'exclusive', 1)) {
129
+                throw new LockedException($path, null, $this->getExistingLockForException($path));
130
+            }
131
+        } elseif ($targetType === self::LOCK_EXCLUSIVE) {
132
+            // we can only change a shared lock to an exclusive if there's only a single owner of the shared lock
133
+            if (!$this->memcache->cas($path, 1, 'exclusive')) {
134
+                throw new LockedException($path, null, $this->getExistingLockForException($path));
135
+            }
136
+        }
137
+        $this->setTTL($path);
138
+        $this->markChange($path, $targetType);
139
+    }
140 140
 
141
-	private function getExistingLockForException($path) {
142
-		$existing = $this->memcache->get($path);
143
-		if (!$existing) {
144
-			return 'none';
145
-		} elseif ($existing === 'exclusive') {
146
-			return $existing;
147
-		} else {
148
-			return $existing . ' shared locks';
149
-		}
150
-	}
141
+    private function getExistingLockForException($path) {
142
+        $existing = $this->memcache->get($path);
143
+        if (!$existing) {
144
+            return 'none';
145
+        } elseif ($existing === 'exclusive') {
146
+            return $existing;
147
+        } else {
148
+            return $existing . ' shared locks';
149
+        }
150
+    }
151 151
 }
Please login to merge, or discard this patch.
lib/private/Lock/DBLockingProvider.php 1 patch
Indentation   +253 added lines, -253 removed lines patch added patch discarded remove patch
@@ -41,279 +41,279 @@
 block discarded – undo
41 41
  * Locking provider that stores the locks in the database
42 42
  */
43 43
 class DBLockingProvider extends AbstractLockingProvider {
44
-	/**
45
-	 * @var \OCP\IDBConnection
46
-	 */
47
-	private $connection;
44
+    /**
45
+     * @var \OCP\IDBConnection
46
+     */
47
+    private $connection;
48 48
 
49
-	/**
50
-	 * @var \OCP\ILogger
51
-	 */
52
-	private $logger;
49
+    /**
50
+     * @var \OCP\ILogger
51
+     */
52
+    private $logger;
53 53
 
54
-	/**
55
-	 * @var \OCP\AppFramework\Utility\ITimeFactory
56
-	 */
57
-	private $timeFactory;
54
+    /**
55
+     * @var \OCP\AppFramework\Utility\ITimeFactory
56
+     */
57
+    private $timeFactory;
58 58
 
59
-	private $sharedLocks = [];
59
+    private $sharedLocks = [];
60 60
 
61
-	/**
62
-	 * @var bool
63
-	 */
64
-	private $cacheSharedLocks;
61
+    /**
62
+     * @var bool
63
+     */
64
+    private $cacheSharedLocks;
65 65
 
66
-	/**
67
-	 * Check if we have an open shared lock for a path
68
-	 *
69
-	 * @param string $path
70
-	 * @return bool
71
-	 */
72
-	protected function isLocallyLocked(string $path): bool {
73
-		return isset($this->sharedLocks[$path]) && $this->sharedLocks[$path];
74
-	}
66
+    /**
67
+     * Check if we have an open shared lock for a path
68
+     *
69
+     * @param string $path
70
+     * @return bool
71
+     */
72
+    protected function isLocallyLocked(string $path): bool {
73
+        return isset($this->sharedLocks[$path]) && $this->sharedLocks[$path];
74
+    }
75 75
 
76
-	/**
77
-	 * Mark a locally acquired lock
78
-	 *
79
-	 * @param string $path
80
-	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
81
-	 */
82
-	protected function markAcquire(string $path, int $type) {
83
-		parent::markAcquire($path, $type);
84
-		if ($this->cacheSharedLocks) {
85
-			if ($type === self::LOCK_SHARED) {
86
-				$this->sharedLocks[$path] = true;
87
-			}
88
-		}
89
-	}
76
+    /**
77
+     * Mark a locally acquired lock
78
+     *
79
+     * @param string $path
80
+     * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
81
+     */
82
+    protected function markAcquire(string $path, int $type) {
83
+        parent::markAcquire($path, $type);
84
+        if ($this->cacheSharedLocks) {
85
+            if ($type === self::LOCK_SHARED) {
86
+                $this->sharedLocks[$path] = true;
87
+            }
88
+        }
89
+    }
90 90
 
91
-	/**
92
-	 * Change the type of an existing tracked lock
93
-	 *
94
-	 * @param string $path
95
-	 * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
96
-	 */
97
-	protected function markChange(string $path, int $targetType) {
98
-		parent::markChange($path, $targetType);
99
-		if ($this->cacheSharedLocks) {
100
-			if ($targetType === self::LOCK_SHARED) {
101
-				$this->sharedLocks[$path] = true;
102
-			} elseif ($targetType === self::LOCK_EXCLUSIVE) {
103
-				$this->sharedLocks[$path] = false;
104
-			}
105
-		}
106
-	}
91
+    /**
92
+     * Change the type of an existing tracked lock
93
+     *
94
+     * @param string $path
95
+     * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
96
+     */
97
+    protected function markChange(string $path, int $targetType) {
98
+        parent::markChange($path, $targetType);
99
+        if ($this->cacheSharedLocks) {
100
+            if ($targetType === self::LOCK_SHARED) {
101
+                $this->sharedLocks[$path] = true;
102
+            } elseif ($targetType === self::LOCK_EXCLUSIVE) {
103
+                $this->sharedLocks[$path] = false;
104
+            }
105
+        }
106
+    }
107 107
 
108
-	/**
109
-	 * @param \OCP\IDBConnection $connection
110
-	 * @param \OCP\ILogger $logger
111
-	 * @param \OCP\AppFramework\Utility\ITimeFactory $timeFactory
112
-	 * @param int $ttl
113
-	 * @param bool $cacheSharedLocks
114
-	 */
115
-	public function __construct(
116
-		IDBConnection $connection,
117
-		ILogger $logger,
118
-		ITimeFactory $timeFactory,
119
-		int $ttl = 3600,
120
-		$cacheSharedLocks = true
121
-	) {
122
-		$this->connection = $connection;
123
-		$this->logger = $logger;
124
-		$this->timeFactory = $timeFactory;
125
-		$this->ttl = $ttl;
126
-		$this->cacheSharedLocks = $cacheSharedLocks;
127
-	}
108
+    /**
109
+     * @param \OCP\IDBConnection $connection
110
+     * @param \OCP\ILogger $logger
111
+     * @param \OCP\AppFramework\Utility\ITimeFactory $timeFactory
112
+     * @param int $ttl
113
+     * @param bool $cacheSharedLocks
114
+     */
115
+    public function __construct(
116
+        IDBConnection $connection,
117
+        ILogger $logger,
118
+        ITimeFactory $timeFactory,
119
+        int $ttl = 3600,
120
+        $cacheSharedLocks = true
121
+    ) {
122
+        $this->connection = $connection;
123
+        $this->logger = $logger;
124
+        $this->timeFactory = $timeFactory;
125
+        $this->ttl = $ttl;
126
+        $this->cacheSharedLocks = $cacheSharedLocks;
127
+    }
128 128
 
129
-	/**
130
-	 * Insert a file locking row if it does not exists.
131
-	 *
132
-	 * @param string $path
133
-	 * @param int $lock
134
-	 * @return int number of inserted rows
135
-	 */
136
-	protected function initLockField(string $path, int $lock = 0): int {
137
-		$expire = $this->getExpireTime();
138
-		return $this->connection->insertIgnoreConflict('file_locks', [
139
-			'key' => $path,
140
-			'lock' => $lock,
141
-			'ttl' => $expire
142
-		]);
143
-	}
129
+    /**
130
+     * Insert a file locking row if it does not exists.
131
+     *
132
+     * @param string $path
133
+     * @param int $lock
134
+     * @return int number of inserted rows
135
+     */
136
+    protected function initLockField(string $path, int $lock = 0): int {
137
+        $expire = $this->getExpireTime();
138
+        return $this->connection->insertIgnoreConflict('file_locks', [
139
+            'key' => $path,
140
+            'lock' => $lock,
141
+            'ttl' => $expire
142
+        ]);
143
+    }
144 144
 
145
-	/**
146
-	 * @return int
147
-	 */
148
-	protected function getExpireTime(): int {
149
-		return $this->timeFactory->getTime() + $this->ttl;
150
-	}
145
+    /**
146
+     * @return int
147
+     */
148
+    protected function getExpireTime(): int {
149
+        return $this->timeFactory->getTime() + $this->ttl;
150
+    }
151 151
 
152
-	/**
153
-	 * @param string $path
154
-	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
155
-	 * @return bool
156
-	 */
157
-	public function isLocked(string $path, int $type): bool {
158
-		if ($this->hasAcquiredLock($path, $type)) {
159
-			return true;
160
-		}
161
-		$query = $this->connection->prepare('SELECT `lock` from `*PREFIX*file_locks` WHERE `key` = ?');
162
-		$query->execute([$path]);
163
-		$lockValue = (int)$query->fetchColumn();
164
-		if ($type === self::LOCK_SHARED) {
165
-			if ($this->isLocallyLocked($path)) {
166
-				// if we have a shared lock we kept open locally but it's released we always have at least 1 shared lock in the db
167
-				return $lockValue > 1;
168
-			} else {
169
-				return $lockValue > 0;
170
-			}
171
-		} elseif ($type === self::LOCK_EXCLUSIVE) {
172
-			return $lockValue === -1;
173
-		} else {
174
-			return false;
175
-		}
176
-	}
152
+    /**
153
+     * @param string $path
154
+     * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
155
+     * @return bool
156
+     */
157
+    public function isLocked(string $path, int $type): bool {
158
+        if ($this->hasAcquiredLock($path, $type)) {
159
+            return true;
160
+        }
161
+        $query = $this->connection->prepare('SELECT `lock` from `*PREFIX*file_locks` WHERE `key` = ?');
162
+        $query->execute([$path]);
163
+        $lockValue = (int)$query->fetchColumn();
164
+        if ($type === self::LOCK_SHARED) {
165
+            if ($this->isLocallyLocked($path)) {
166
+                // if we have a shared lock we kept open locally but it's released we always have at least 1 shared lock in the db
167
+                return $lockValue > 1;
168
+            } else {
169
+                return $lockValue > 0;
170
+            }
171
+        } elseif ($type === self::LOCK_EXCLUSIVE) {
172
+            return $lockValue === -1;
173
+        } else {
174
+            return false;
175
+        }
176
+    }
177 177
 
178
-	/**
179
-	 * @param string $path
180
-	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
181
-	 * @throws \OCP\Lock\LockedException
182
-	 */
183
-	public function acquireLock(string $path, int $type, string $readablePath = null) {
184
-		$expire = $this->getExpireTime();
185
-		if ($type === self::LOCK_SHARED) {
186
-			if (!$this->isLocallyLocked($path)) {
187
-				$result = $this->initLockField($path, 1);
188
-				if ($result <= 0) {
189
-					$result = $this->connection->executeUpdate(
190
-						'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` + 1, `ttl` = ? WHERE `key` = ? AND `lock` >= 0',
191
-						[$expire, $path]
192
-					);
193
-				}
194
-			} else {
195
-				$result = 1;
196
-			}
197
-		} else {
198
-			$existing = 0;
199
-			if ($this->hasAcquiredLock($path, ILockingProvider::LOCK_SHARED) === false && $this->isLocallyLocked($path)) {
200
-				$existing = 1;
201
-			}
202
-			$result = $this->initLockField($path, -1);
203
-			if ($result <= 0) {
204
-				$result = $this->connection->executeUpdate(
205
-					'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = ?',
206
-					[$expire, $path, $existing]
207
-				);
208
-			}
209
-		}
210
-		if ($result !== 1) {
211
-			throw new LockedException($path, null, null, $readablePath);
212
-		}
213
-		$this->markAcquire($path, $type);
214
-	}
178
+    /**
179
+     * @param string $path
180
+     * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
181
+     * @throws \OCP\Lock\LockedException
182
+     */
183
+    public function acquireLock(string $path, int $type, string $readablePath = null) {
184
+        $expire = $this->getExpireTime();
185
+        if ($type === self::LOCK_SHARED) {
186
+            if (!$this->isLocallyLocked($path)) {
187
+                $result = $this->initLockField($path, 1);
188
+                if ($result <= 0) {
189
+                    $result = $this->connection->executeUpdate(
190
+                        'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` + 1, `ttl` = ? WHERE `key` = ? AND `lock` >= 0',
191
+                        [$expire, $path]
192
+                    );
193
+                }
194
+            } else {
195
+                $result = 1;
196
+            }
197
+        } else {
198
+            $existing = 0;
199
+            if ($this->hasAcquiredLock($path, ILockingProvider::LOCK_SHARED) === false && $this->isLocallyLocked($path)) {
200
+                $existing = 1;
201
+            }
202
+            $result = $this->initLockField($path, -1);
203
+            if ($result <= 0) {
204
+                $result = $this->connection->executeUpdate(
205
+                    'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = ?',
206
+                    [$expire, $path, $existing]
207
+                );
208
+            }
209
+        }
210
+        if ($result !== 1) {
211
+            throw new LockedException($path, null, null, $readablePath);
212
+        }
213
+        $this->markAcquire($path, $type);
214
+    }
215 215
 
216
-	/**
217
-	 * @param string $path
218
-	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
219
-	 *
220
-	 * @suppress SqlInjectionChecker
221
-	 */
222
-	public function releaseLock(string $path, int $type) {
223
-		$this->markRelease($path, $type);
216
+    /**
217
+     * @param string $path
218
+     * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
219
+     *
220
+     * @suppress SqlInjectionChecker
221
+     */
222
+    public function releaseLock(string $path, int $type) {
223
+        $this->markRelease($path, $type);
224 224
 
225
-		// we keep shared locks till the end of the request so we can re-use them
226
-		if ($type === self::LOCK_EXCLUSIVE) {
227
-			$this->connection->executeUpdate(
228
-				'UPDATE `*PREFIX*file_locks` SET `lock` = 0 WHERE `key` = ? AND `lock` = -1',
229
-				[$path]
230
-			);
231
-		} elseif (!$this->cacheSharedLocks) {
232
-			$query = $this->connection->getQueryBuilder();
233
-			$query->update('file_locks')
234
-				->set('lock', $query->func()->subtract('lock', $query->createNamedParameter(1)))
235
-				->where($query->expr()->eq('key', $query->createNamedParameter($path)))
236
-				->andWhere($query->expr()->gt('lock', $query->createNamedParameter(0)));
237
-			$query->execute();
238
-		}
239
-	}
225
+        // we keep shared locks till the end of the request so we can re-use them
226
+        if ($type === self::LOCK_EXCLUSIVE) {
227
+            $this->connection->executeUpdate(
228
+                'UPDATE `*PREFIX*file_locks` SET `lock` = 0 WHERE `key` = ? AND `lock` = -1',
229
+                [$path]
230
+            );
231
+        } elseif (!$this->cacheSharedLocks) {
232
+            $query = $this->connection->getQueryBuilder();
233
+            $query->update('file_locks')
234
+                ->set('lock', $query->func()->subtract('lock', $query->createNamedParameter(1)))
235
+                ->where($query->expr()->eq('key', $query->createNamedParameter($path)))
236
+                ->andWhere($query->expr()->gt('lock', $query->createNamedParameter(0)));
237
+            $query->execute();
238
+        }
239
+    }
240 240
 
241
-	/**
242
-	 * Change the type of an existing lock
243
-	 *
244
-	 * @param string $path
245
-	 * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
246
-	 * @throws \OCP\Lock\LockedException
247
-	 */
248
-	public function changeLock(string $path, int $targetType) {
249
-		$expire = $this->getExpireTime();
250
-		if ($targetType === self::LOCK_SHARED) {
251
-			$result = $this->connection->executeUpdate(
252
-				'UPDATE `*PREFIX*file_locks` SET `lock` = 1, `ttl` = ? WHERE `key` = ? AND `lock` = -1',
253
-				[$expire, $path]
254
-			);
255
-		} else {
256
-			// since we only keep one shared lock in the db we need to check if we have more then one shared lock locally manually
257
-			if (isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 1) {
258
-				throw new LockedException($path);
259
-			}
260
-			$result = $this->connection->executeUpdate(
261
-				'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = 1',
262
-				[$expire, $path]
263
-			);
264
-		}
265
-		if ($result !== 1) {
266
-			throw new LockedException($path);
267
-		}
268
-		$this->markChange($path, $targetType);
269
-	}
241
+    /**
242
+     * Change the type of an existing lock
243
+     *
244
+     * @param string $path
245
+     * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
246
+     * @throws \OCP\Lock\LockedException
247
+     */
248
+    public function changeLock(string $path, int $targetType) {
249
+        $expire = $this->getExpireTime();
250
+        if ($targetType === self::LOCK_SHARED) {
251
+            $result = $this->connection->executeUpdate(
252
+                'UPDATE `*PREFIX*file_locks` SET `lock` = 1, `ttl` = ? WHERE `key` = ? AND `lock` = -1',
253
+                [$expire, $path]
254
+            );
255
+        } else {
256
+            // since we only keep one shared lock in the db we need to check if we have more then one shared lock locally manually
257
+            if (isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 1) {
258
+                throw new LockedException($path);
259
+            }
260
+            $result = $this->connection->executeUpdate(
261
+                'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = 1',
262
+                [$expire, $path]
263
+            );
264
+        }
265
+        if ($result !== 1) {
266
+            throw new LockedException($path);
267
+        }
268
+        $this->markChange($path, $targetType);
269
+    }
270 270
 
271
-	/**
272
-	 * cleanup empty locks
273
-	 */
274
-	public function cleanExpiredLocks() {
275
-		$expire = $this->timeFactory->getTime();
276
-		try {
277
-			$this->connection->executeUpdate(
278
-				'DELETE FROM `*PREFIX*file_locks` WHERE `ttl` < ?',
279
-				[$expire]
280
-			);
281
-		} catch (\Exception $e) {
282
-			// If the table is missing, the clean up was successful
283
-			if ($this->connection->tableExists('file_locks')) {
284
-				throw $e;
285
-			}
286
-		}
287
-	}
271
+    /**
272
+     * cleanup empty locks
273
+     */
274
+    public function cleanExpiredLocks() {
275
+        $expire = $this->timeFactory->getTime();
276
+        try {
277
+            $this->connection->executeUpdate(
278
+                'DELETE FROM `*PREFIX*file_locks` WHERE `ttl` < ?',
279
+                [$expire]
280
+            );
281
+        } catch (\Exception $e) {
282
+            // If the table is missing, the clean up was successful
283
+            if ($this->connection->tableExists('file_locks')) {
284
+                throw $e;
285
+            }
286
+        }
287
+    }
288 288
 
289
-	/**
290
-	 * release all lock acquired by this instance which were marked using the mark* methods
291
-	 *
292
-	 * @suppress SqlInjectionChecker
293
-	 */
294
-	public function releaseAll() {
295
-		parent::releaseAll();
289
+    /**
290
+     * release all lock acquired by this instance which were marked using the mark* methods
291
+     *
292
+     * @suppress SqlInjectionChecker
293
+     */
294
+    public function releaseAll() {
295
+        parent::releaseAll();
296 296
 
297
-		if (!$this->cacheSharedLocks) {
298
-			return;
299
-		}
300
-		// since we keep shared locks we need to manually clean those
301
-		$lockedPaths = array_keys($this->sharedLocks);
302
-		$lockedPaths = array_filter($lockedPaths, function ($path) {
303
-			return $this->sharedLocks[$path];
304
-		});
297
+        if (!$this->cacheSharedLocks) {
298
+            return;
299
+        }
300
+        // since we keep shared locks we need to manually clean those
301
+        $lockedPaths = array_keys($this->sharedLocks);
302
+        $lockedPaths = array_filter($lockedPaths, function ($path) {
303
+            return $this->sharedLocks[$path];
304
+        });
305 305
 
306
-		$chunkedPaths = array_chunk($lockedPaths, 100);
306
+        $chunkedPaths = array_chunk($lockedPaths, 100);
307 307
 
308
-		foreach ($chunkedPaths as $chunk) {
309
-			$builder = $this->connection->getQueryBuilder();
308
+        foreach ($chunkedPaths as $chunk) {
309
+            $builder = $this->connection->getQueryBuilder();
310 310
 
311
-			$query = $builder->update('file_locks')
312
-				->set('lock', $builder->func()->subtract('lock', $builder->expr()->literal(1)))
313
-				->where($builder->expr()->in('key', $builder->createNamedParameter($chunk, IQueryBuilder::PARAM_STR_ARRAY)))
314
-				->andWhere($builder->expr()->gt('lock', new Literal(0)));
311
+            $query = $builder->update('file_locks')
312
+                ->set('lock', $builder->func()->subtract('lock', $builder->expr()->literal(1)))
313
+                ->where($builder->expr()->in('key', $builder->createNamedParameter($chunk, IQueryBuilder::PARAM_STR_ARRAY)))
314
+                ->andWhere($builder->expr()->gt('lock', new Literal(0)));
315 315
 
316
-			$query->execute();
317
-		}
318
-	}
316
+            $query->execute();
317
+        }
318
+    }
319 319
 }
Please login to merge, or discard this patch.