Completed
Push — master ( 34c0b9...0c1cf5 )
by Lukas
08:37
created

Common::verifyWindowsPath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 8
rs 9.4285
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
		$this->verifyPosixPath($fileName);
493
	}
494
495
	/**
496
	 * @param string $fileName
497
	 * @throws InvalidPathException
498
	 */
499
	protected function verifyPosixPath($fileName) {
500
		$fileName = trim($fileName);
501
		$this->scanForInvalidCharacters($fileName, "\\/");
502
		$reservedNames = ['*'];
503
		if (in_array($fileName, $reservedNames)) {
504
			throw new ReservedWordException();
505
		}
506
	}
507
508
	/**
509
	 * @param string $fileName
510
	 * @param string $invalidChars
511
	 * @throws InvalidPathException
512
	 */
513
	private function scanForInvalidCharacters($fileName, $invalidChars) {
514
		foreach (str_split($invalidChars) as $char) {
515
			if (strpos($fileName, $char) !== false) {
516
				throw new InvalidCharacterInPathException();
517
			}
518
		}
519
520
		$sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
521
		if ($sanitizedFileName !== $fileName) {
522
			throw new InvalidCharacterInPathException();
523
		}
524
	}
525
526
	/**
527
	 * @param array $options
528
	 */
529
	public function setMountOptions(array $options) {
530
		$this->mountOptions = $options;
531
	}
532
533
	/**
534
	 * @param string $name
535
	 * @param mixed $default
536
	 * @return mixed
537
	 */
538
	public function getMountOption($name, $default = null) {
539
		return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
540
	}
541
542
	/**
543
	 * @param \OCP\Files\Storage $sourceStorage
544
	 * @param string $sourceInternalPath
545
	 * @param string $targetInternalPath
546
	 * @param bool $preserveMtime
547
	 * @return bool
548
	 */
549
	public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
550
		if ($sourceStorage === $this) {
551
			return $this->copy($sourceInternalPath, $targetInternalPath);
552
		}
553
554
		if ($sourceStorage->is_dir($sourceInternalPath)) {
555
			$dh = $sourceStorage->opendir($sourceInternalPath);
556
			$result = $this->mkdir($targetInternalPath);
557 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...
558
				while ($result and ($file = readdir($dh)) !== false) {
559
					if (!Filesystem::isIgnoredDir($file)) {
560
						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
561
					}
562
				}
563
			}
564
		} else {
565
			$source = $sourceStorage->fopen($sourceInternalPath, 'r');
566
			// TODO: call fopen in a way that we execute again all storage wrappers
567
			// to avoid that we bypass storage wrappers which perform important actions
568
			// for this operation. Same is true for all other operations which
569
			// are not the same as the original one.Once this is fixed we also
570
			// need to adjust the encryption wrapper.
571
			$target = $this->fopen($targetInternalPath, 'w');
572
			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...
573
			if ($result and $preserveMtime) {
574
				$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...
575
			}
576
			fclose($source);
577
			fclose($target);
578
579
			if (!$result) {
580
				// delete partially written target file
581
				$this->unlink($targetInternalPath);
582
				// delete cache entry that was created by fopen
583
				$this->getCache()->remove($targetInternalPath);
584
			}
585
		}
586
		return (bool)$result;
587
	}
588
589
	/**
590
	 * @param \OCP\Files\Storage $sourceStorage
591
	 * @param string $sourceInternalPath
592
	 * @param string $targetInternalPath
593
	 * @return bool
594
	 */
595 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...
596
		if ($sourceStorage === $this) {
597
			return $this->rename($sourceInternalPath, $targetInternalPath);
598
		}
599
600
		if (!$sourceStorage->isDeletable($sourceInternalPath)) {
601
			return false;
602
		}
603
604
		$result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
605
		if ($result) {
606
			if ($sourceStorage->is_dir($sourceInternalPath)) {
607
				$result &= $sourceStorage->rmdir($sourceInternalPath);
608
			} else {
609
				$result &= $sourceStorage->unlink($sourceInternalPath);
610
			}
611
		}
612
		return $result;
613
	}
614
615
	/**
616
	 * @inheritdoc
617
	 */
618
	public function getMetaData($path) {
619
		$permissions = $this->getPermissions($path);
620
		if (!$permissions & \OCP\Constants::PERMISSION_READ) {
621
			//can't read, nothing we can do
622
			return null;
623
		}
624
625
		$data = [];
626
		$data['mimetype'] = $this->getMimeType($path);
627
		$data['mtime'] = $this->filemtime($path);
628
		if ($data['mtime'] === false) {
629
			$data['mtime'] = time();
630
		}
631
		if ($data['mimetype'] == 'httpd/unix-directory') {
632
			$data['size'] = -1; //unknown
633
		} else {
634
			$data['size'] = $this->filesize($path);
635
		}
636
		$data['etag'] = $this->getETag($path);
637
		$data['storage_mtime'] = $data['mtime'];
638
		$data['permissions'] = $permissions;
639
640
		return $data;
641
	}
642
643
	/**
644
	 * @param string $path
645
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
646
	 * @param \OCP\Lock\ILockingProvider $provider
647
	 * @throws \OCP\Lock\LockedException
648
	 */
649
	public function acquireLock($path, $type, ILockingProvider $provider) {
650
		$provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
651
	}
652
653
	/**
654
	 * @param string $path
655
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
656
	 * @param \OCP\Lock\ILockingProvider $provider
657
	 */
658
	public function releaseLock($path, $type, ILockingProvider $provider) {
659
		$provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
660
	}
661
662
	/**
663
	 * @param string $path
664
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
665
	 * @param \OCP\Lock\ILockingProvider $provider
666
	 */
667
	public function changeLock($path, $type, ILockingProvider $provider) {
668
		$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
669
	}
670
671
	/**
672
	 * @return array [ available, last_checked ]
673
	 */
674
	public function getAvailability() {
675
		return $this->getStorageCache()->getAvailability();
676
	}
677
678
	/**
679
	 * @param bool $isAvailable
680
	 */
681
	public function setAvailability($isAvailable) {
682
		$this->getStorageCache()->setAvailability($isAvailable);
683
	}
684
}
685