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