Completed
Pull Request — master (#31)
by Blizzz
13:09 queued 04:36
created

Common::moveFromStorage()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 12

Duplication

Lines 19
Ratio 100 %

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 5
nop 3
dl 19
loc 19
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Bart Visscher <[email protected]>
5
 * @author Björn Schießle <[email protected]>
6
 * @author hkjolhede <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Jörn Friedrich Dreyer <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Martin Mattel <[email protected]>
11
 * @author Michael Gapczynski <[email protected]>
12
 * @author Morris Jobke <[email protected]>
13
 * @author Robin Appelman <[email protected]>
14
 * @author Robin McCorkell <[email protected]>
15
 * @author Roeland Jago Douma <[email protected]>
16
 * @author Sam Tuke <[email protected]>
17
 * @author scambra <[email protected]>
18
 * @author Stefan Weil <[email protected]>
19
 * @author Thomas Müller <[email protected]>
20
 * @author Vincent Petry <[email protected]>
21
 *
22
 * @copyright Copyright (c) 2016, ownCloud, Inc.
23
 * @license AGPL-3.0
24
 *
25
 * This code is free software: you can redistribute it and/or modify
26
 * it under the terms of the GNU Affero General Public License, version 3,
27
 * as published by the Free Software Foundation.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License, version 3,
35
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
36
 *
37
 */
38
39
namespace OC\Files\Storage;
40
41
use OC\Files\Cache\Cache;
42
use OC\Files\Cache\Propagator;
43
use OC\Files\Cache\Scanner;
44
use OC\Files\Cache\Updater;
45
use OC\Files\Filesystem;
46
use OC\Files\Cache\Watcher;
47
use OCP\Files\FileNameTooLongException;
48
use OCP\Files\InvalidCharacterInPathException;
49
use OCP\Files\InvalidPathException;
50
use OCP\Files\ReservedWordException;
51
use OCP\Files\Storage\ILockingStorage;
52
use OCP\Lock\ILockingProvider;
53
54
/**
55
 * Storage backend class for providing common filesystem operation methods
56
 * which are not storage-backend specific.
57
 *
58
 * \OC\Files\Storage\Common is never used directly; it is extended by all other
59
 * storage backends, where its methods may be overridden, and additional
60
 * (backend-specific) methods are defined.
61
 *
62
 * Some \OC\Files\Storage\Common methods call functions which are first defined
63
 * in classes which extend it, e.g. $this->stat() .
64
 */
65
abstract class Common implements Storage, ILockingStorage {
66
67
	use LocalTempFileTrait;
68
69
	protected $cache;
70
	protected $scanner;
71
	protected $watcher;
72
	protected $propagator;
73
	protected $storageCache;
74
	protected $updater;
75
76
	protected $mountOptions = [];
77
	protected $owner = null;
78
79
	public function __construct($parameters) {
80
	}
81
82
	/**
83
	 * Remove a file or folder
84
	 *
85
	 * @param string $path
86
	 * @return bool
87
	 */
88 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...
89
		if ($this->is_dir($path)) {
90
			return $this->rmdir($path);
91
		} else if ($this->is_file($path)) {
92
			return $this->unlink($path);
93
		} else {
94
			return false;
95
		}
96
	}
97
98
	public function is_dir($path) {
99
		return $this->filetype($path) === 'dir';
100
	}
101
102
	public function is_file($path) {
103
		return $this->filetype($path) === 'file';
104
	}
105
106
	public function filesize($path) {
107
		if ($this->is_dir($path)) {
108
			return 0; //by definition
109
		} else {
110
			$stat = $this->stat($path);
111
			if (isset($stat['size'])) {
112
				return $stat['size'];
113
			} else {
114
				return 0;
115
			}
116
		}
117
	}
118
119
	public function isReadable($path) {
120
		// at least check whether it exists
121
		// subclasses might want to implement this more thoroughly
122
		return $this->file_exists($path);
123
	}
124
125
	public function isUpdatable($path) {
126
		// at least check whether it exists
127
		// subclasses might want to implement this more thoroughly
128
		// a non-existing file/folder isn't updatable
129
		return $this->file_exists($path);
130
	}
131
132
	public function isCreatable($path) {
133
		if ($this->is_dir($path) && $this->isUpdatable($path)) {
134
			return true;
135
		}
136
		return false;
137
	}
138
139
	public function isDeletable($path) {
140
		if ($path === '' || $path === '/') {
141
			return false;
142
		}
143
		$parent = dirname($path);
144
		return $this->isUpdatable($parent) && $this->isUpdatable($path);
145
	}
146
147
	public function isSharable($path) {
148
		return $this->isReadable($path);
149
	}
150
151
	public function getPermissions($path) {
152
		$permissions = 0;
153
		if ($this->isCreatable($path)) {
154
			$permissions |= \OCP\Constants::PERMISSION_CREATE;
155
		}
156
		if ($this->isReadable($path)) {
157
			$permissions |= \OCP\Constants::PERMISSION_READ;
158
		}
159
		if ($this->isUpdatable($path)) {
160
			$permissions |= \OCP\Constants::PERMISSION_UPDATE;
161
		}
162
		if ($this->isDeletable($path)) {
163
			$permissions |= \OCP\Constants::PERMISSION_DELETE;
164
		}
165
		if ($this->isSharable($path)) {
166
			$permissions |= \OCP\Constants::PERMISSION_SHARE;
167
		}
168
		return $permissions;
169
	}
170
171
	public function filemtime($path) {
172
		$stat = $this->stat($path);
173
		if (isset($stat['mtime']) && $stat['mtime'] > 0) {
174
			return $stat['mtime'];
175
		} else {
176
			return 0;
177
		}
178
	}
179
180
	public function file_get_contents($path) {
181
		$handle = $this->fopen($path, "r");
182
		if (!$handle) {
183
			return false;
184
		}
185
		$data = stream_get_contents($handle);
186
		fclose($handle);
187
		return $data;
188
	}
189
190
	public function file_put_contents($path, $data) {
191
		$handle = $this->fopen($path, "w");
192
		$this->removeCachedFile($path);
193
		$count = fwrite($handle, $data);
194
		fclose($handle);
195
		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...
196
	}
197
198
	public function rename($path1, $path2) {
199
		$this->remove($path2);
200
201
		$this->removeCachedFile($path1);
202
		return $this->copy($path1, $path2) and $this->remove($path1);
203
	}
204
205
	public function copy($path1, $path2) {
206
		if ($this->is_dir($path1)) {
207
			$this->remove($path2);
208
			$dir = $this->opendir($path1);
209
			$this->mkdir($path2);
210
			while ($file = readdir($dir)) {
211
				if (!Filesystem::isIgnoredDir($file)) {
212
					if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
213
						return false;
214
					}
215
				}
216
			}
217
			closedir($dir);
218
			return true;
219
		} else {
220
			$source = $this->fopen($path1, 'r');
221
			$target = $this->fopen($path2, 'w');
222
			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...
223
			$this->removeCachedFile($path2);
224
			return $result;
225
		}
226
	}
227
228
	public function getMimeType($path) {
229
		if ($this->is_dir($path)) {
230
			return 'httpd/unix-directory';
231
		} elseif ($this->file_exists($path)) {
232
			return \OC::$server->getMimeTypeDetector()->detectPath($path);
233
		} else {
234
			return false;
235
		}
236
	}
237
238 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...
239
		$fh = $this->fopen($path, 'rb');
240
		$ctx = hash_init($type);
241
		hash_update_stream($ctx, $fh);
242
		fclose($fh);
243
		return hash_final($ctx, $raw);
244
	}
245
246
	public function search($query) {
247
		return $this->searchInDir($query);
248
	}
249
250
	public function getLocalFile($path) {
251
		return $this->getCachedFile($path);
252
	}
253
254
	/**
255
	 * @param string $path
256
	 * @param string $target
257
	 */
258
	private function addLocalFolder($path, $target) {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
259
		$dh = $this->opendir($path);
260
		if (is_resource($dh)) {
261
			while (($file = readdir($dh)) !== false) {
262
				if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
263
					if ($this->is_dir($path . '/' . $file)) {
264
						mkdir($target . '/' . $file);
265
						$this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
266
					} else {
267
						$tmp = $this->toTmpFile($path . '/' . $file);
268
						rename($tmp, $target . '/' . $file);
269
					}
270
				}
271
			}
272
		}
273
	}
274
275
	/**
276
	 * @param string $query
277
	 * @param string $dir
278
	 * @return array
279
	 */
280
	protected function searchInDir($query, $dir = '') {
281
		$files = array();
282
		$dh = $this->opendir($dir);
283
		if (is_resource($dh)) {
284
			while (($item = readdir($dh)) !== false) {
285
				if (\OC\Files\Filesystem::isIgnoredDir($item)) continue;
286 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...
287
					$files[] = $dir . '/' . $item;
288
				}
289
				if ($this->is_dir($dir . '/' . $item)) {
290
					$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
291
				}
292
			}
293
		}
294
		closedir($dh);
295
		return $files;
296
	}
297
298
	/**
299
	 * check if a file or folder has been updated since $time
300
	 *
301
	 * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
302
	 * the mtime should always return false here. As a result storage implementations that always return false expect
303
	 * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
304
	 * ownClouds filesystem.
305
	 *
306
	 * @param string $path
307
	 * @param int $time
308
	 * @return bool
309
	 */
310
	public function hasUpdated($path, $time) {
311
		return $this->filemtime($path) > $time;
312
	}
313
314
	public function getCache($path = '', $storage = null) {
315
		if (!$storage) {
316
			$storage = $this;
317
		}
318
		if (!isset($storage->cache)) {
319
			$storage->cache = new Cache($storage);
320
		}
321
		return $storage->cache;
322
	}
323
324
	public function getScanner($path = '', $storage = null) {
325
		if (!$storage) {
326
			$storage = $this;
327
		}
328
		if (!isset($storage->scanner)) {
329
			$storage->scanner = new Scanner($storage);
330
		}
331
		return $storage->scanner;
332
	}
333
334
	public function getWatcher($path = '', $storage = null) {
335
		if (!$storage) {
336
			$storage = $this;
337
		}
338
		if (!isset($this->watcher)) {
339
			$this->watcher = new Watcher($storage);
340
			$globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
341
			$this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
342
		}
343
		return $this->watcher;
344
	}
345
346
	/**
347
	 * get a propagator instance for the cache
348
	 *
349
	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
350
	 * @return \OC\Files\Cache\Propagator
351
	 */
352
	public function getPropagator($storage = null) {
353
		if (!$storage) {
354
			$storage = $this;
355
		}
356
		if (!isset($storage->propagator)) {
357
			$storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection());
358
		}
359
		return $storage->propagator;
360
	}
361
362
	public function getUpdater($storage = null) {
363
		if (!$storage) {
364
			$storage = $this;
365
		}
366
		if (!isset($storage->updater)) {
367
			$storage->updater = new Updater($storage);
368
		}
369
		return $storage->updater;
370
	}
371
372
	public function getStorageCache($storage = null) {
373
		if (!$storage) {
374
			$storage = $this;
375
		}
376
		if (!isset($this->storageCache)) {
377
			$this->storageCache = new \OC\Files\Cache\Storage($storage);
378
		}
379
		return $this->storageCache;
380
	}
381
382
	/**
383
	 * get the owner of a path
384
	 *
385
	 * @param string $path The path to get the owner
386
	 * @return string|false uid or false
387
	 */
388
	public function getOwner($path) {
389
		if ($this->owner === null) {
390
			$this->owner = \OC_User::getUser();
391
		}
392
393
		return $this->owner;
394
	}
395
396
	/**
397
	 * get the ETag for a file or folder
398
	 *
399
	 * @param string $path
400
	 * @return string
401
	 */
402
	public function getETag($path) {
403
		return uniqid();
404
	}
405
406
	/**
407
	 * clean a path, i.e. remove all redundant '.' and '..'
408
	 * making sure that it can't point to higher than '/'
409
	 *
410
	 * @param string $path The path to clean
411
	 * @return string cleaned path
412
	 */
413
	public function cleanPath($path) {
414
		if (strlen($path) == 0 or $path[0] != '/') {
415
			$path = '/' . $path;
416
		}
417
418
		$output = array();
419
		foreach (explode('/', $path) as $chunk) {
420
			if ($chunk == '..') {
421
				array_pop($output);
422
			} else if ($chunk == '.') {
423
			} else {
424
				$output[] = $chunk;
425
			}
426
		}
427
		return implode('/', $output);
428
	}
429
430
	/**
431
	 * Test a storage for availability
432
	 *
433
	 * @return bool
434
	 */
435
	public function test() {
436
		if ($this->stat('')) {
437
			return true;
438
		}
439
		return false;
440
	}
441
442
	/**
443
	 * get the free space in the storage
444
	 *
445
	 * @param string $path
446
	 * @return int|false
447
	 */
448
	public function free_space($path) {
449
		return \OCP\Files\FileInfo::SPACE_UNKNOWN;
450
	}
451
452
	/**
453
	 * {@inheritdoc}
454
	 */
455
	public function isLocal() {
456
		// the common implementation returns a temporary file by
457
		// default, which is not local
458
		return false;
459
	}
460
461
	/**
462
	 * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
463
	 *
464
	 * @param string $class
465
	 * @return bool
466
	 */
467
	public function instanceOfStorage($class) {
468
		return is_a($this, $class);
469
	}
470
471
	/**
472
	 * A custom storage implementation can return an url for direct download of a give file.
473
	 *
474
	 * For now the returned array can hold the parameter url - in future more attributes might follow.
475
	 *
476
	 * @param string $path
477
	 * @return array|false
478
	 */
479
	public function getDirectDownload($path) {
480
		return [];
481
	}
482
483
	/**
484
	 * @inheritdoc
485
	 */
486
	public function verifyPath($path, $fileName) {
487
		if (isset($fileName[255])) {
488
			throw new FileNameTooLongException();
489
		}
490
491
		// NOTE: $path will remain unverified for now
492
		if (\OC_Util::runningOnWindows()) {
493
			$this->verifyWindowsPath($fileName);
494
		} else {
495
			$this->verifyPosixPath($fileName);
496
		}
497
	}
498
499
	/**
500
	 * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
501
	 * @param string $fileName
502
	 * @throws InvalidPathException
503
	 */
504
	protected function verifyWindowsPath($fileName) {
505
		$fileName = trim($fileName);
506
		$this->scanForInvalidCharacters($fileName, "\\/<>:\"|?*");
507
		$reservedNames = ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'];
508
		if (in_array(strtoupper($fileName), $reservedNames)) {
509
			throw new ReservedWordException();
510
		}
511
	}
512
513
	/**
514
	 * @param string $fileName
515
	 * @throws InvalidPathException
516
	 */
517
	protected function verifyPosixPath($fileName) {
518
		$fileName = trim($fileName);
519
		$this->scanForInvalidCharacters($fileName, "\\/");
520
		$reservedNames = ['*'];
521
		if (in_array($fileName, $reservedNames)) {
522
			throw new ReservedWordException();
523
		}
524
	}
525
526
	/**
527
	 * @param string $fileName
528
	 * @param string $invalidChars
529
	 * @throws InvalidPathException
530
	 */
531
	private function scanForInvalidCharacters($fileName, $invalidChars) {
532
		foreach (str_split($invalidChars) as $char) {
533
			if (strpos($fileName, $char) !== false) {
534
				throw new InvalidCharacterInPathException();
535
			}
536
		}
537
538
		$sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
539
		if ($sanitizedFileName !== $fileName) {
540
			throw new InvalidCharacterInPathException();
541
		}
542
	}
543
544
	/**
545
	 * @param array $options
546
	 */
547
	public function setMountOptions(array $options) {
548
		$this->mountOptions = $options;
549
	}
550
551
	/**
552
	 * @param string $name
553
	 * @param mixed $default
554
	 * @return mixed
555
	 */
556
	public function getMountOption($name, $default = null) {
557
		return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
558
	}
559
560
	/**
561
	 * @param \OCP\Files\Storage $sourceStorage
562
	 * @param string $sourceInternalPath
563
	 * @param string $targetInternalPath
564
	 * @param bool $preserveMtime
565
	 * @return bool
566
	 */
567
	public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
568
		if ($sourceStorage === $this) {
569
			return $this->copy($sourceInternalPath, $targetInternalPath);
570
		}
571
572
		if ($sourceStorage->is_dir($sourceInternalPath)) {
573
			$dh = $sourceStorage->opendir($sourceInternalPath);
574
			$result = $this->mkdir($targetInternalPath);
575 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...
576
				while ($result and ($file = readdir($dh)) !== false) {
577
					if (!Filesystem::isIgnoredDir($file)) {
578
						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
579
					}
580
				}
581
			}
582
		} else {
583
			$source = $sourceStorage->fopen($sourceInternalPath, 'r');
584
			// TODO: call fopen in a way that we execute again all storage wrappers
585
			// to avoid that we bypass storage wrappers which perform important actions
586
			// for this operation. Same is true for all other operations which
587
			// are not the same as the original one.Once this is fixed we also
588
			// need to adjust the encryption wrapper.
589
			$target = $this->fopen($targetInternalPath, 'w');
590
			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...
591
			if ($result and $preserveMtime) {
592
				$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...
593
			}
594
			fclose($source);
595
			fclose($target);
596
597
			if (!$result) {
598
				// delete partially written target file
599
				$this->unlink($targetInternalPath);
600
				// delete cache entry that was created by fopen
601
				$this->getCache()->remove($targetInternalPath);
602
			}
603
		}
604
		return (bool)$result;
605
	}
606
607
	/**
608
	 * @param \OCP\Files\Storage $sourceStorage
609
	 * @param string $sourceInternalPath
610
	 * @param string $targetInternalPath
611
	 * @return bool
612
	 */
613 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...
614
		if ($sourceStorage === $this) {
615
			return $this->rename($sourceInternalPath, $targetInternalPath);
616
		}
617
618
		if (!$sourceStorage->isDeletable($sourceInternalPath)) {
619
			return false;
620
		}
621
622
		$result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
623
		if ($result) {
624
			if ($sourceStorage->is_dir($sourceInternalPath)) {
625
				$result &= $sourceStorage->rmdir($sourceInternalPath);
626
			} else {
627
				$result &= $sourceStorage->unlink($sourceInternalPath);
628
			}
629
		}
630
		return $result;
631
	}
632
633
	/**
634
	 * @inheritdoc
635
	 */
636
	public function getMetaData($path) {
637
		$permissions = $this->getPermissions($path);
638
		if (!$permissions & \OCP\Constants::PERMISSION_READ) {
639
			//can't read, nothing we can do
640
			return null;
641
		}
642
643
		$data = [];
644
		$data['mimetype'] = $this->getMimeType($path);
645
		$data['mtime'] = $this->filemtime($path);
646
		if ($data['mtime'] === false) {
647
			$data['mtime'] = time();
648
		}
649
		if ($data['mimetype'] == 'httpd/unix-directory') {
650
			$data['size'] = -1; //unknown
651
		} else {
652
			$data['size'] = $this->filesize($path);
653
		}
654
		$data['etag'] = $this->getETag($path);
655
		$data['storage_mtime'] = $data['mtime'];
656
		$data['permissions'] = $permissions;
657
658
		return $data;
659
	}
660
661
	/**
662
	 * @param string $path
663
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
664
	 * @param \OCP\Lock\ILockingProvider $provider
665
	 * @throws \OCP\Lock\LockedException
666
	 */
667
	public function acquireLock($path, $type, ILockingProvider $provider) {
668
		$provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
669
	}
670
671
	/**
672
	 * @param string $path
673
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
674
	 * @param \OCP\Lock\ILockingProvider $provider
675
	 */
676
	public function releaseLock($path, $type, ILockingProvider $provider) {
677
		$provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
678
	}
679
680
	/**
681
	 * @param string $path
682
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
683
	 * @param \OCP\Lock\ILockingProvider $provider
684
	 */
685
	public function changeLock($path, $type, ILockingProvider $provider) {
686
		$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
687
	}
688
689
	/**
690
	 * @return array [ available, last_checked ]
691
	 */
692
	public function getAvailability() {
693
		return $this->getStorageCache()->getAvailability();
694
	}
695
696
	/**
697
	 * @param bool $isAvailable
698
	 */
699
	public function setAvailability($isAvailable) {
700
		$this->getStorageCache()->setAvailability($isAvailable);
701
	}
702
}
703