Completed
Push — stable8.2 ( 696ee3...54f8ec )
by Roeland
70:33
created

Scanner   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 90.91%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 18
lcom 1
cbo 12
dl 0
loc 144
ccs 70
cts 77
cp 0.9091
rs 10
c 3
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getMounts() 0 12 1
B attachListener() 0 24 1
B backgroundScan() 0 16 5
D scan() 0 37 10
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 Scrutinizer Auto-Fixer <[email protected]>
8
 * @author Thomas Müller <[email protected]>
9
 *
10
 * @copyright Copyright (c) 2015, ownCloud, Inc.
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
27
namespace OC\Files\Utils;
28
29
use OC\Files\View;
30
use OC\Files\Cache\ChangePropagator;
31
use OC\Files\Filesystem;
32
use OC\ForbiddenException;
33
use OC\Hooks\PublicEmitter;
34
use OC\Lock\DBLockingProvider;
35
use OCP\Files\StorageNotAvailableException;
36
use OCP\ILogger;
37
38
/**
39
 * Class Scanner
40
 *
41
 * Hooks available in scope \OC\Utils\Scanner
42
 *  - scanFile(string $absolutePath)
43
 *  - scanFolder(string $absolutePath)
44
 *
45
 * @package OC\Files\Utils
46
 */
47
class Scanner extends PublicEmitter {
48
	/**
49
	 * @var string $user
50
	 */
51
	private $user;
52
53
	/**
54
	 * @var \OC\Files\Cache\ChangePropagator
55
	 */
56
	protected $propagator;
57
58
	/**
59
	 * @var \OCP\IDBConnection
60
	 */
61
	protected $db;
62
63
	/**
64
	 * @var ILogger
65
	 */
66
	protected $logger;
67
68
	/**
69
	 * @param string $user
70
	 * @param \OCP\IDBConnection $db
71
	 * @param ILogger $logger
72
	 */
73 8
	public function __construct($user, $db, ILogger $logger) {
74 8
		$this->logger = $logger;
75 8
		$this->user = $user;
76 8
		$this->propagator = new ChangePropagator(new View(''));
77 8
		$this->db = $db;
78 8
	}
79
80
	/**
81
	 * get all storages for $dir
82
	 *
83
	 * @param string $dir
84
	 * @return \OC\Files\Mount\MountPoint[]
85
	 */
86 2
	protected function getMounts($dir) {
87
		//TODO: move to the node based fileapi once that's done
88 2
		\OC_Util::tearDownFS();
89 2
		\OC_Util::setupFS($this->user);
90
91 2
		$mountManager = Filesystem::getMountManager();
92 2
		$mounts = $mountManager->findIn($dir);
93 2
		$mounts[] = $mountManager->find($dir);
94 2
		$mounts = array_reverse($mounts); //start with the mount of $dir
95
96 2
		return $mounts;
97
	}
98
99
	/**
100
	 * attach listeners to the scanner
101
	 *
102
	 * @param \OC\Files\Mount\MountPoint $mount
103
	 */
104 5
	protected function attachListener($mount) {
105 5
		$scanner = $mount->getStorage()->getScanner();
106 5
		$emitter = $this;
107
		$scanner->listen('\OC\Files\Cache\Scanner', 'scanFile', function ($path) use ($mount, $emitter) {
108 4
			$emitter->emit('\OC\Files\Utils\Scanner', 'scanFile', array($mount->getMountPoint() . $path));
109 5
		});
110
		$scanner->listen('\OC\Files\Cache\Scanner', 'scanFolder', function ($path) use ($mount, $emitter) {
111 4
			$emitter->emit('\OC\Files\Utils\Scanner', 'scanFolder', array($mount->getMountPoint() . $path));
112 5
		});
113
		$scanner->listen('\OC\Files\Cache\Scanner', 'postScanFile', function ($path) use ($mount, $emitter) {
114 4
			$emitter->emit('\OC\Files\Utils\Scanner', 'postScanFile', array($mount->getMountPoint() . $path));
115 5
		});
116
		$scanner->listen('\OC\Files\Cache\Scanner', 'postScanFolder', function ($path) use ($mount, $emitter) {
117 4
			$emitter->emit('\OC\Files\Utils\Scanner', 'postScanFolder', array($mount->getMountPoint() . $path));
118 5
		});
119
		// propagate etag and mtimes when files are changed or removed
120 5
		$propagator = $this->propagator;
121 5
		$propagatorListener = function ($path) use ($mount, $propagator) {
122 4
			$fullPath = Filesystem::normalizePath($mount->getMountPoint() . $path);
123 4
			$propagator->addChange($fullPath);
124 5
		};
125 5
		$scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', $propagatorListener);
126 5
		$scanner->listen('\OC\Files\Cache\Scanner', 'removeFromCache', $propagatorListener);
127 5
	}
128
129
	/**
130
	 * @param string $dir
131
	 */
132 1
	public function backgroundScan($dir) {
133 1
		$mounts = $this->getMounts($dir);
134 1
		foreach ($mounts as $mount) {
135 1
			if (is_null($mount->getStorage())) {
136
				continue;
137
			}
138
			// don't scan the root storage
139 1
			if ($mount->getStorage()->instanceOfStorage('\OC\Files\Storage\Local') && $mount->getMountPoint() === '/') {
140 1
				continue;
141
			}
142 1
			$scanner = $mount->getStorage()->getScanner();
143 1
			$this->attachListener($mount);
0 ignored issues
show
Bug introduced by
It seems like $mount defined by $mount on line 134 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...
144 1
			$scanner->backgroundScan();
145 1
		}
146 1
		$this->propagator->propagateChanges(time());
147 1
	}
148
149
	/**
150
	 * @param string $dir
151
	 * @throws \OC\ForbiddenException
152
	 */
153 7
	public function scan($dir = '') {
154 7
		if (!Filesystem::isValidPath($dir)) {
155 3
			throw new \InvalidArgumentException('Invalid path to scan');
156
		}
157 4
		$mounts = $this->getMounts($dir);
158 4
		foreach ($mounts as $mount) {
159 4
			if (is_null($mount->getStorage())) {
160
				continue;
161
			}
162 4
			$storage = $mount->getStorage();
163
			// if the home storage isn't writable then the scanner is run as the wrong user
164 4
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home') and
165
				(!$storage->isCreatable('') or !$storage->isCreatable('files'))
166 4
			) {
167
				throw new ForbiddenException();
168
			}
169 4
			$relativePath = $mount->getInternalPath($dir);
170 4
			$scanner = $storage->getScanner();
171 4
			$scanner->setUseTransactions(false);
172 4
			$this->attachListener($mount);
0 ignored issues
show
Bug introduced by
It seems like $mount defined by $mount on line 158 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...
173 4
			$isDbLocking = \OC::$server->getLockingProvider() instanceof DBLockingProvider;
174 4
			if (!$isDbLocking) {
175 4
				$this->db->beginTransaction();
176 4
			}
177
			try {
178 4
				$scanner->scan($relativePath, \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE);
179 4
			} catch (StorageNotAvailableException $e) {
180
				$this->logger->error('Storage ' . $storage->getId() . ' not available');
181
				$this->logger->logException($e);
182
				$this->emit('\OC\Files\Utils\Scanner', 'StorageNotAvailable', [$e]);
183
			}
184 4
			if (!$isDbLocking) {
185 4
				$this->db->commit();
186 4
			}
187 4
		}
188 4
		$this->propagator->propagateChanges(time());
189 4
	}
190
}
191
192