Completed
Push — master ( ef8fc6...23e7ad )
by Morris
08:27
created

Scanner::triggerPropagator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 2
1
<?php
2
/**
3
 * @author Jörn Friedrich Dreyer <[email protected]>
4
 * @author Morris Jobke <[email protected]>
5
 * @author Olivier Paroz <[email protected]>
6
 * @author Robin Appelman <[email protected]>
7
 * @author Thomas Müller <[email protected]>
8
 *
9
 * @copyright Copyright (c) 2016, ownCloud, Inc.
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
26
namespace OC\Files\Utils;
27
28
use OC\Files\Filesystem;
29
use OC\ForbiddenException;
30
use OC\Hooks\PublicEmitter;
31
use OC\Lock\DBLockingProvider;
32
use OCP\Files\Storage\IStorage;
33
use OCP\Files\StorageNotAvailableException;
34
use OCP\ILogger;
35
36
/**
37
 * Class Scanner
38
 *
39
 * Hooks available in scope \OC\Utils\Scanner
40
 *  - scanFile(string $absolutePath)
41
 *  - scanFolder(string $absolutePath)
42
 *
43
 * @package OC\Files\Utils
44
 */
45
class Scanner extends PublicEmitter {
46
	/**
47
	 * @var string $user
48
	 */
49
	private $user;
50
51
	/**
52
	 * @var \OCP\IDBConnection
53
	 */
54
	protected $db;
55
56
	/**
57
	 * @var ILogger
58
	 */
59
	protected $logger;
60
61
	/**
62
	 * @param string $user
63
	 * @param \OCP\IDBConnection $db
64
	 * @param ILogger $logger
65
	 */
66
	public function __construct($user, $db, ILogger $logger) {
67
		$this->logger = $logger;
68
		$this->user = $user;
69
		$this->db = $db;
70
	}
71
72
	/**
73
	 * get all storages for $dir
74
	 *
75
	 * @param string $dir
76
	 * @return \OC\Files\Mount\MountPoint[]
77
	 */
78
	protected function getMounts($dir) {
79
		//TODO: move to the node based fileapi once that's done
80
		\OC_Util::tearDownFS();
81
		\OC_Util::setupFS($this->user);
82
83
		$mountManager = Filesystem::getMountManager();
84
		$mounts = $mountManager->findIn($dir);
85
		$mounts[] = $mountManager->find($dir);
86
		$mounts = array_reverse($mounts); //start with the mount of $dir
87
88
		return $mounts;
89
	}
90
91
	/**
92
	 * attach listeners to the scanner
93
	 *
94
	 * @param \OC\Files\Mount\MountPoint $mount
95
	 */
96
	protected function attachListener($mount) {
97
		$scanner = $mount->getStorage()->getScanner();
98
		$emitter = $this;
99
		$scanner->listen('\OC\Files\Cache\Scanner', 'scanFile', function ($path) use ($mount, $emitter) {
100
			$emitter->emit('\OC\Files\Utils\Scanner', 'scanFile', array($mount->getMountPoint() . $path));
101
		});
102
		$scanner->listen('\OC\Files\Cache\Scanner', 'scanFolder', function ($path) use ($mount, $emitter) {
103
			$emitter->emit('\OC\Files\Utils\Scanner', 'scanFolder', array($mount->getMountPoint() . $path));
104
		});
105
		$scanner->listen('\OC\Files\Cache\Scanner', 'postScanFile', function ($path) use ($mount, $emitter) {
106
			$emitter->emit('\OC\Files\Utils\Scanner', 'postScanFile', array($mount->getMountPoint() . $path));
107
		});
108
		$scanner->listen('\OC\Files\Cache\Scanner', 'postScanFolder', function ($path) use ($mount, $emitter) {
109
			$emitter->emit('\OC\Files\Utils\Scanner', 'postScanFolder', array($mount->getMountPoint() . $path));
110
		});
111
	}
112
113
	/**
114
	 * @param string $dir
115
	 */
116
	public function backgroundScan($dir) {
117
		$mounts = $this->getMounts($dir);
118
		foreach ($mounts as $mount) {
119
			if (is_null($mount->getStorage())) {
120
				continue;
121
			}
122
			// don't scan the root storage
123
			if ($mount->getStorage()->instanceOfStorage('\OC\Files\Storage\Local') && $mount->getMountPoint() === '/') {
124
				continue;
125
			}
126
			$scanner = $mount->getStorage()->getScanner();
127
			$this->attachListener($mount);
0 ignored issues
show
Bug introduced by
It seems like $mount defined by $mount on line 118 can be null; however, OC\Files\Utils\Scanner::attachListener() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
128
			$scanner->backgroundScan();
129
		}
130
	}
131
132
	/**
133
	 * @param string $dir
134
	 * @throws \OC\ForbiddenException
135
	 */
136
	public function scan($dir = '') {
137
		if (!Filesystem::isValidPath($dir)) {
138
			throw new \InvalidArgumentException('Invalid path to scan');
139
		}
140
		$mounts = $this->getMounts($dir);
141
		foreach ($mounts as $mount) {
142
			if (is_null($mount->getStorage())) {
143
				continue;
144
			}
145
			$storage = $mount->getStorage();
146
			// if the home storage isn't writable then the scanner is run as the wrong user
147
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home') and
148
				(!$storage->isCreatable('') or !$storage->isCreatable('files'))
149
			) {
150
				throw new ForbiddenException();
151
			}
152
			$relativePath = $mount->getInternalPath($dir);
153
			$scanner = $storage->getScanner();
154
			$scanner->setUseTransactions(false);
155
			$this->attachListener($mount);
0 ignored issues
show
Bug introduced by
It seems like $mount defined by $mount on line 141 can be null; however, OC\Files\Utils\Scanner::attachListener() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
156
			$isDbLocking = \OC::$server->getLockingProvider() instanceof DBLockingProvider;
157
158
			$scanner->listen('\OC\Files\Cache\Scanner', 'removeFromCache', function ($path) use ($storage) {
159
				$this->triggerPropagator($storage, $path);
160
			});
161
			$scanner->listen('\OC\Files\Cache\Scanner', 'updateCache', function ($path) use ($storage) {
162
				$this->triggerPropagator($storage, $path);
163
			});
164
			$scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function ($path) use ($storage) {
165
				$this->triggerPropagator($storage, $path);
166
			});
167
168
			if (!$isDbLocking) {
169
				$this->db->beginTransaction();
170
			}
171
			try {
172
				$scanner->scan($relativePath, \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE);
173
			} catch (StorageNotAvailableException $e) {
174
				$this->logger->error('Storage ' . $storage->getId() . ' not available');
175
				$this->logger->logException($e);
176
				$this->emit('\OC\Files\Utils\Scanner', 'StorageNotAvailable', [$e]);
177
			}
178
			if (!$isDbLocking) {
179
				$this->db->commit();
180
			}
181
		}
182
	}
183
184
	private function triggerPropagator(IStorage $storage, $internalPath) {
185
		$storage->getPropagator()->propagateChange($internalPath, time());
186
	}
187
}
188
189