Completed
Push — master ( 7113af...257e28 )
by Lukas
09:52
created

Common::isDeletable()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 3
nop 1
dl 0
loc 7
rs 9.2
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bart Visscher <[email protected]>
7
 * @author Björn Schießle <[email protected]>
8
 * @author hkjolhede <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author Jörn Friedrich Dreyer <[email protected]>
11
 * @author Lukas Reschke <[email protected]>
12
 * @author Martin Mattel <[email protected]>
13
 * @author Michael Gapczynski <[email protected]>
14
 * @author Morris Jobke <[email protected]>
15
 * @author Robin Appelman <[email protected]>
16
 * @author Robin McCorkell <[email protected]>
17
 * @author Roeland Jago Douma <[email protected]>
18
 * @author Sam Tuke <[email protected]>
19
 * @author scambra <[email protected]>
20
 * @author Stefan Weil <[email protected]>
21
 * @author Thomas Müller <[email protected]>
22
 * @author Vincent Petry <[email protected]>
23
 *
24
 * @license AGPL-3.0
25
 *
26
 * This code is free software: you can redistribute it and/or modify
27
 * it under the terms of the GNU Affero General Public License, version 3,
28
 * as published by the Free Software Foundation.
29
 *
30
 * This program is distributed in the hope that it will be useful,
31
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33
 * GNU Affero General Public License for more details.
34
 *
35
 * You should have received a copy of the GNU Affero General Public License, version 3,
36
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
37
 *
38
 */
39
40
namespace OC\Files\Storage;
41
42
use OC\Files\Cache\Cache;
43
use OC\Files\Cache\Propagator;
44
use OC\Files\Cache\Scanner;
45
use OC\Files\Cache\Updater;
46
use OC\Files\Filesystem;
47
use OC\Files\Cache\Watcher;
48
use OCP\Files\EmptyFileNameException;
49
use OCP\Files\FileNameTooLongException;
50
use OCP\Files\InvalidCharacterInPathException;
51
use OCP\Files\InvalidDirectoryException;
52
use OCP\Files\InvalidPathException;
53
use OCP\Files\ReservedWordException;
54
use OCP\Files\Storage\ILockingStorage;
55
use OCP\Lock\ILockingProvider;
56
use OCP\Lock\LockedException;
57
58
/**
59
 * Storage backend class for providing common filesystem operation methods
60
 * which are not storage-backend specific.
61
 *
62
 * \OC\Files\Storage\Common is never used directly; it is extended by all other
63
 * storage backends, where its methods may be overridden, and additional
64
 * (backend-specific) methods are defined.
65
 *
66
 * Some \OC\Files\Storage\Common methods call functions which are first defined
67
 * in classes which extend it, e.g. $this->stat() .
68
 */
69
abstract class Common implements Storage, ILockingStorage {
70
71
	use LocalTempFileTrait;
72
73
	protected $cache;
74
	protected $scanner;
75
	protected $watcher;
76
	protected $propagator;
77
	protected $storageCache;
78
	protected $updater;
79
80
	protected $mountOptions = [];
81
	protected $owner = null;
82
83
	private $shouldLogLocks = null;
84
	private $logger;
85
86
	public function __construct($parameters) {
87
	}
88
89
	/**
90
	 * Remove a file or folder
91
	 *
92
	 * @param string $path
93
	 * @return bool
94
	 */
95 View Code Duplication
	protected function remove($path) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
96
		if ($this->is_dir($path)) {
97
			return $this->rmdir($path);
98
		} else if ($this->is_file($path)) {
99
			return $this->unlink($path);
100
		} else {
101
			return false;
102
		}
103
	}
104
105
	public function is_dir($path) {
106
		return $this->filetype($path) === 'dir';
107
	}
108
109
	public function is_file($path) {
110
		return $this->filetype($path) === 'file';
111
	}
112
113
	public function filesize($path) {
114
		if ($this->is_dir($path)) {
115
			return 0; //by definition
116
		} else {
117
			$stat = $this->stat($path);
118
			if (isset($stat['size'])) {
119
				return $stat['size'];
120
			} else {
121
				return 0;
122
			}
123
		}
124
	}
125
126
	public function isReadable($path) {
127
		// at least check whether it exists
128
		// subclasses might want to implement this more thoroughly
129
		return $this->file_exists($path);
130
	}
131
132
	public function isUpdatable($path) {
133
		// at least check whether it exists
134
		// subclasses might want to implement this more thoroughly
135
		// a non-existing file/folder isn't updatable
136
		return $this->file_exists($path);
137
	}
138
139
	public function isCreatable($path) {
140
		if ($this->is_dir($path) && $this->isUpdatable($path)) {
141
			return true;
142
		}
143
		return false;
144
	}
145
146
	public function isDeletable($path) {
147
		if ($path === '' || $path === '/') {
148
			return false;
149
		}
150
		$parent = dirname($path);
151
		return $this->isUpdatable($parent) && $this->isUpdatable($path);
152
	}
153
154
	public function isSharable($path) {
155
		return $this->isReadable($path);
156
	}
157
158
	public function getPermissions($path) {
159
		$permissions = 0;
160
		if ($this->isCreatable($path)) {
161
			$permissions |= \OCP\Constants::PERMISSION_CREATE;
162
		}
163
		if ($this->isReadable($path)) {
164
			$permissions |= \OCP\Constants::PERMISSION_READ;
165
		}
166
		if ($this->isUpdatable($path)) {
167
			$permissions |= \OCP\Constants::PERMISSION_UPDATE;
168
		}
169
		if ($this->isDeletable($path)) {
170
			$permissions |= \OCP\Constants::PERMISSION_DELETE;
171
		}
172
		if ($this->isSharable($path)) {
173
			$permissions |= \OCP\Constants::PERMISSION_SHARE;
174
		}
175
		return $permissions;
176
	}
177
178
	public function filemtime($path) {
179
		$stat = $this->stat($path);
180
		if (isset($stat['mtime']) && $stat['mtime'] > 0) {
181
			return $stat['mtime'];
182
		} else {
183
			return 0;
184
		}
185
	}
186
187
	public function file_get_contents($path) {
188
		$handle = $this->fopen($path, "r");
189
		if (!$handle) {
190
			return false;
191
		}
192
		$data = stream_get_contents($handle);
193
		fclose($handle);
194
		return $data;
195
	}
196
197
	public function file_put_contents($path, $data) {
198
		$handle = $this->fopen($path, "w");
199
		$this->removeCachedFile($path);
200
		$count = fwrite($handle, $data);
201
		fclose($handle);
202
		return $count;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $count; (integer) is incompatible with the return type declared by the interface OCP\Files\Storage::file_put_contents of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
203
	}
204
205
	public function rename($path1, $path2) {
206
		$this->remove($path2);
207
208
		$this->removeCachedFile($path1);
209
		return $this->copy($path1, $path2) and $this->remove($path1);
210
	}
211
212
	public function copy($path1, $path2) {
213
		if ($this->is_dir($path1)) {
214
			$this->remove($path2);
215
			$dir = $this->opendir($path1);
216
			$this->mkdir($path2);
217
			while ($file = readdir($dir)) {
218
				if (!Filesystem::isIgnoredDir($file)) {
219
					if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
220
						return false;
221
					}
222
				}
223
			}
224
			closedir($dir);
225
			return true;
226
		} else {
227
			$source = $this->fopen($path1, 'r');
228
			$target = $this->fopen($path2, 'w');
229
			list(, $result) = \OC_Helper::streamCopy($source, $target);
0 ignored issues
show
Security Bug introduced by
It seems like $source can also be of type false; however, OC_Helper::streamCopy() does only seem to accept resource, did you maybe forget to handle an error condition?
Loading history...
Security Bug introduced by
It seems like $target can also be of type false; however, OC_Helper::streamCopy() does only seem to accept resource, did you maybe forget to handle an error condition?
Loading history...
230
			$this->removeCachedFile($path2);
231
			return $result;
232
		}
233
	}
234
235
	public function getMimeType($path) {
236
		if ($this->is_dir($path)) {
237
			return 'httpd/unix-directory';
238
		} elseif ($this->file_exists($path)) {
239
			return \OC::$server->getMimeTypeDetector()->detectPath($path);
240
		} else {
241
			return false;
242
		}
243
	}
244
245 View Code Duplication
	public function hash($type, $path, $raw = false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
246
		$fh = $this->fopen($path, 'rb');
247
		$ctx = hash_init($type);
248
		hash_update_stream($ctx, $fh);
249
		fclose($fh);
250
		return hash_final($ctx, $raw);
251
	}
252
253
	public function search($query) {
254
		return $this->searchInDir($query);
255
	}
256
257
	public function getLocalFile($path) {
258
		return $this->getCachedFile($path);
259
	}
260
261
	/**
262
	 * @param string $path
263
	 * @param string $target
264
	 */
265
	private function addLocalFolder($path, $target) {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
266
		$dh = $this->opendir($path);
267
		if (is_resource($dh)) {
268
			while (($file = readdir($dh)) !== false) {
269
				if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
270
					if ($this->is_dir($path . '/' . $file)) {
271
						mkdir($target . '/' . $file);
272
						$this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
273
					} else {
274
						$tmp = $this->toTmpFile($path . '/' . $file);
275
						rename($tmp, $target . '/' . $file);
276
					}
277
				}
278
			}
279
		}
280
	}
281
282
	/**
283
	 * @param string $query
284
	 * @param string $dir
285
	 * @return array
286
	 */
287
	protected function searchInDir($query, $dir = '') {
288
		$files = array();
289
		$dh = $this->opendir($dir);
290
		if (is_resource($dh)) {
291
			while (($item = readdir($dh)) !== false) {
292
				if (\OC\Files\Filesystem::isIgnoredDir($item)) continue;
293 View Code Duplication
				if (strstr(strtolower($item), strtolower($query)) !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
294
					$files[] = $dir . '/' . $item;
295
				}
296
				if ($this->is_dir($dir . '/' . $item)) {
297
					$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
298
				}
299
			}
300
		}
301
		closedir($dh);
302
		return $files;
303
	}
304
305
	/**
306
	 * check if a file or folder has been updated since $time
307
	 *
308
	 * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
309
	 * the mtime should always return false here. As a result storage implementations that always return false expect
310
	 * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
311
	 * ownClouds filesystem.
312
	 *
313
	 * @param string $path
314
	 * @param int $time
315
	 * @return bool
316
	 */
317
	public function hasUpdated($path, $time) {
318
		return $this->filemtime($path) > $time;
319
	}
320
321
	public function getCache($path = '', $storage = null) {
322
		if (!$storage) {
323
			$storage = $this;
324
		}
325
		if (!isset($storage->cache)) {
326
			$storage->cache = new Cache($storage);
327
		}
328
		return $storage->cache;
329
	}
330
331
	public function getScanner($path = '', $storage = null) {
332
		if (!$storage) {
333
			$storage = $this;
334
		}
335
		if (!isset($storage->scanner)) {
336
			$storage->scanner = new Scanner($storage);
337
		}
338
		return $storage->scanner;
339
	}
340
341
	public function getWatcher($path = '', $storage = null) {
342
		if (!$storage) {
343
			$storage = $this;
344
		}
345
		if (!isset($this->watcher)) {
346
			$this->watcher = new Watcher($storage);
347
			$globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
348
			$this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
349
		}
350
		return $this->watcher;
351
	}
352
353
	/**
354
	 * get a propagator instance for the cache
355
	 *
356
	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
357
	 * @return \OC\Files\Cache\Propagator
358
	 */
359
	public function getPropagator($storage = null) {
360
		if (!$storage) {
361
			$storage = $this;
362
		}
363
		if (!isset($storage->propagator)) {
364
			$storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection());
365
		}
366
		return $storage->propagator;
367
	}
368
369
	public function getUpdater($storage = null) {
370
		if (!$storage) {
371
			$storage = $this;
372
		}
373
		if (!isset($storage->updater)) {
374
			$storage->updater = new Updater($storage);
375
		}
376
		return $storage->updater;
377
	}
378
379
	public function getStorageCache($storage = null) {
380
		if (!$storage) {
381
			$storage = $this;
382
		}
383
		if (!isset($this->storageCache)) {
384
			$this->storageCache = new \OC\Files\Cache\Storage($storage);
385
		}
386
		return $this->storageCache;
387
	}
388
389
	/**
390
	 * get the owner of a path
391
	 *
392
	 * @param string $path The path to get the owner
393
	 * @return string|false uid or false
394
	 */
395
	public function getOwner($path) {
396
		if ($this->owner === null) {
397
			$this->owner = \OC_User::getUser();
398
		}
399
400
		return $this->owner;
401
	}
402
403
	/**
404
	 * get the ETag for a file or folder
405
	 *
406
	 * @param string $path
407
	 * @return string
408
	 */
409
	public function getETag($path) {
410
		return uniqid();
411
	}
412
413
	/**
414
	 * clean a path, i.e. remove all redundant '.' and '..'
415
	 * making sure that it can't point to higher than '/'
416
	 *
417
	 * @param string $path The path to clean
418
	 * @return string cleaned path
419
	 */
420
	public function cleanPath($path) {
421
		if (strlen($path) == 0 or $path[0] != '/') {
422
			$path = '/' . $path;
423
		}
424
425
		$output = array();
426
		foreach (explode('/', $path) as $chunk) {
427
			if ($chunk == '..') {
428
				array_pop($output);
429
			} else if ($chunk == '.') {
430
			} else {
431
				$output[] = $chunk;
432
			}
433
		}
434
		return implode('/', $output);
435
	}
436
437
	/**
438
	 * Test a storage for availability
439
	 *
440
	 * @return bool
441
	 */
442
	public function test() {
443
		try {
444
			if ($this->stat('')) {
445
				return true;
446
			}
447
			return false;
448
		} catch (\Exception $e) {
449
			return false;
450
		}
451
	}
452
453
	/**
454
	 * get the free space in the storage
455
	 *
456
	 * @param string $path
457
	 * @return int|false
458
	 */
459
	public function free_space($path) {
460
		return \OCP\Files\FileInfo::SPACE_UNKNOWN;
461
	}
462
463
	/**
464
	 * {@inheritdoc}
465
	 */
466
	public function isLocal() {
467
		// the common implementation returns a temporary file by
468
		// default, which is not local
469
		return false;
470
	}
471
472
	/**
473
	 * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
474
	 *
475
	 * @param string $class
476
	 * @return bool
477
	 */
478
	public function instanceOfStorage($class) {
479
		if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') {
480
			// FIXME Temporary fix to keep existing checks working
481
			$class = '\OCA\Files_Sharing\SharedStorage';
482
		}
483
		return is_a($this, $class);
484
	}
485
486
	/**
487
	 * A custom storage implementation can return an url for direct download of a give file.
488
	 *
489
	 * For now the returned array can hold the parameter url - in future more attributes might follow.
490
	 *
491
	 * @param string $path
492
	 * @return array|false
493
	 */
494
	public function getDirectDownload($path) {
495
		return [];
496
	}
497
498
	/**
499
	 * @inheritdoc
500
	 * @throws InvalidPathException
501
	 */
502
	public function verifyPath($path, $fileName) {
503
504
		// verify empty and dot files
505
		$trimmed = trim($fileName);
506
		if ($trimmed === '') {
507
			throw new EmptyFileNameException();
508
		}
509
510
		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
511
			throw new InvalidDirectoryException();
512
		}
513
514
		if (!\OC::$server->getDatabaseConnection()->supports4ByteText()) {
515
			// verify database - e.g. mysql only 3-byte chars
516
			if (preg_match('%(?:
517
      \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
518
    | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
519
    | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
520
)%xs', $fileName)) {
521
				throw new InvalidCharacterInPathException();
522
			}
523
		}
524
525
		if (isset($fileName[255])) {
526
			throw new FileNameTooLongException();
527
		}
528
529
		// NOTE: $path will remain unverified for now
530
		$this->verifyPosixPath($fileName);
531
	}
532
533
	/**
534
	 * @param string $fileName
535
	 * @throws InvalidPathException
536
	 */
537
	protected function verifyPosixPath($fileName) {
538
		$fileName = trim($fileName);
539
		$this->scanForInvalidCharacters($fileName, "\\/");
540
		$reservedNames = ['*'];
541
		if (in_array($fileName, $reservedNames)) {
542
			throw new ReservedWordException();
543
		}
544
	}
545
546
	/**
547
	 * @param string $fileName
548
	 * @param string $invalidChars
549
	 * @throws InvalidPathException
550
	 */
551
	private function scanForInvalidCharacters($fileName, $invalidChars) {
552
		foreach (str_split($invalidChars) as $char) {
553
			if (strpos($fileName, $char) !== false) {
554
				throw new InvalidCharacterInPathException();
555
			}
556
		}
557
558
		$sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
559
		if ($sanitizedFileName !== $fileName) {
560
			throw new InvalidCharacterInPathException();
561
		}
562
	}
563
564
	/**
565
	 * @param array $options
566
	 */
567
	public function setMountOptions(array $options) {
568
		$this->mountOptions = $options;
569
	}
570
571
	/**
572
	 * @param string $name
573
	 * @param mixed $default
574
	 * @return mixed
575
	 */
576
	public function getMountOption($name, $default = null) {
577
		return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
578
	}
579
580
	/**
581
	 * @param \OCP\Files\Storage $sourceStorage
582
	 * @param string $sourceInternalPath
583
	 * @param string $targetInternalPath
584
	 * @param bool $preserveMtime
585
	 * @return bool
586
	 */
587
	public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
588
		if ($sourceStorage === $this) {
589
			return $this->copy($sourceInternalPath, $targetInternalPath);
590
		}
591
592
		if ($sourceStorage->is_dir($sourceInternalPath)) {
593
			$dh = $sourceStorage->opendir($sourceInternalPath);
594
			$result = $this->mkdir($targetInternalPath);
595 View Code Duplication
			if (is_resource($dh)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
596
				while ($result and ($file = readdir($dh)) !== false) {
597
					if (!Filesystem::isIgnoredDir($file)) {
598
						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
599
					}
600
				}
601
			}
602
		} else {
603
			$source = $sourceStorage->fopen($sourceInternalPath, 'r');
604
			// TODO: call fopen in a way that we execute again all storage wrappers
605
			// to avoid that we bypass storage wrappers which perform important actions
606
			// for this operation. Same is true for all other operations which
607
			// are not the same as the original one.Once this is fixed we also
608
			// need to adjust the encryption wrapper.
609
			$target = $this->fopen($targetInternalPath, 'w');
610
			list(, $result) = \OC_Helper::streamCopy($source, $target);
0 ignored issues
show
Security Bug introduced by
It seems like $source can also be of type false; however, OC_Helper::streamCopy() does only seem to accept resource, did you maybe forget to handle an error condition?
Loading history...
Security Bug introduced by
It seems like $target can also be of type false; however, OC_Helper::streamCopy() does only seem to accept resource, did you maybe forget to handle an error condition?
Loading history...
611
			if ($result and $preserveMtime) {
612
				$this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
0 ignored issues
show
Security Bug introduced by
It seems like $sourceStorage->filemtime($sourceInternalPath) targeting OCP\Files\Storage::filemtime() can also be of type false; however, OCP\Files\Storage::touch() does only seem to accept integer|null, did you maybe forget to handle an error condition?
Loading history...
613
			}
614
			fclose($source);
615
			fclose($target);
616
617
			if (!$result) {
618
				// delete partially written target file
619
				$this->unlink($targetInternalPath);
620
				// delete cache entry that was created by fopen
621
				$this->getCache()->remove($targetInternalPath);
622
			}
623
		}
624
		return (bool)$result;
625
	}
626
627
	/**
628
	 * @param \OCP\Files\Storage $sourceStorage
629
	 * @param string $sourceInternalPath
630
	 * @param string $targetInternalPath
631
	 * @return bool
632
	 */
633 View Code Duplication
	public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
634
		if ($sourceStorage === $this) {
635
			return $this->rename($sourceInternalPath, $targetInternalPath);
636
		}
637
638
		if (!$sourceStorage->isDeletable($sourceInternalPath)) {
639
			return false;
640
		}
641
642
		$result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
643
		if ($result) {
644
			if ($sourceStorage->is_dir($sourceInternalPath)) {
645
				$result &= $sourceStorage->rmdir($sourceInternalPath);
646
			} else {
647
				$result &= $sourceStorage->unlink($sourceInternalPath);
648
			}
649
		}
650
		return $result;
651
	}
652
653
	/**
654
	 * @inheritdoc
655
	 */
656
	public function getMetaData($path) {
657
		$permissions = $this->getPermissions($path);
658
		if (!$permissions & \OCP\Constants::PERMISSION_READ) {
659
			//can't read, nothing we can do
660
			return null;
661
		}
662
663
		$data = [];
664
		$data['mimetype'] = $this->getMimeType($path);
665
		$data['mtime'] = $this->filemtime($path);
666
		if ($data['mtime'] === false) {
667
			$data['mtime'] = time();
668
		}
669
		if ($data['mimetype'] == 'httpd/unix-directory') {
670
			$data['size'] = -1; //unknown
671
		} else {
672
			$data['size'] = $this->filesize($path);
673
		}
674
		$data['etag'] = $this->getETag($path);
675
		$data['storage_mtime'] = $data['mtime'];
676
		$data['permissions'] = $permissions;
677
678
		return $data;
679
	}
680
681
	/**
682
	 * @param string $path
683
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
684
	 * @param \OCP\Lock\ILockingProvider $provider
685
	 * @throws \OCP\Lock\LockedException
686
	 */
687 View Code Duplication
	public function acquireLock($path, $type, ILockingProvider $provider) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
688
		$logger = $this->getLockLogger();
689
		if ($logger) {
690
			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
691
			$logger->info(
692
				sprintf(
693
					'acquire %s lock on "%s" on storage "%s"',
694
					$typeString,
695
					$path,
696
					$this->getId()
697
				),
698
				[
699
					'app' => 'locking',
700
				]
701
			);
702
		}
703
		try {
704
			$provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
705
		} catch (LockedException $e) {
706
			if ($logger) {
707
				$logger->logException($e);
708
			}
709
			throw $e;
710
		}
711
	}
712
713
	/**
714
	 * @param string $path
715
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
716
	 * @param \OCP\Lock\ILockingProvider $provider
717
	 * @throws \OCP\Lock\LockedException
718
	 */
719 View Code Duplication
	public function releaseLock($path, $type, ILockingProvider $provider) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
720
		$logger = $this->getLockLogger();
721
		if ($logger) {
722
			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
723
			$logger->info(
724
				sprintf(
725
					'release %s lock on "%s" on storage "%s"',
726
					$typeString,
727
					$path,
728
					$this->getId()
729
				),
730
				[
731
					'app' => 'locking',
732
				]
733
			);
734
		}
735
		try {
736
			$provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
737
		} catch (LockedException $e) {
738
			if ($logger) {
739
				$logger->logException($e);
740
			}
741
			throw $e;
742
		}
743
	}
744
745
	/**
746
	 * @param string $path
747
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
748
	 * @param \OCP\Lock\ILockingProvider $provider
749
	 * @throws \OCP\Lock\LockedException
750
	 */
751 View Code Duplication
	public function changeLock($path, $type, ILockingProvider $provider) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
752
		$logger = $this->getLockLogger();
753
		if ($logger) {
754
			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
755
			$logger->info(
756
				sprintf(
757
					'change lock on "%s" to %s on storage "%s"',
758
					$path,
759
					$typeString,
760
					$this->getId()
761
				),
762
				[
763
					'app' => 'locking',
764
				]
765
			);
766
		}
767
		try {
768
			$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
769
		} catch (LockedException $e) {
770
			if ($logger) {
771
				$logger->logException($e);
772
			}
773
			throw $e;
774
		}
775
	}
776
777
	private function getLockLogger() {
778
		if (is_null($this->shouldLogLocks)) {
779
			$this->shouldLogLocks = \OC::$server->getConfig()->getSystemValue('filelocking.debug', false);
780
			$this->logger = $this->shouldLogLocks ? \OC::$server->getLogger() : null;
781
		}
782
		return $this->logger;
783
	}
784
785
	/**
786
	 * @return array [ available, last_checked ]
787
	 */
788
	public function getAvailability() {
789
		return $this->getStorageCache()->getAvailability();
790
	}
791
792
	/**
793
	 * @param bool $isAvailable
794
	 */
795
	public function setAvailability($isAvailable) {
796
		$this->getStorageCache()->setAvailability($isAvailable);
797
	}
798
}
799