Completed
Pull Request — stable8.2 (#24893)
by Robin
10:29
created

Scanner   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 149
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 90.91%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 149
ccs 70
cts 77
cp 0.9091
rs 10
c 2
b 0
f 0
wmc 19
lcom 1
cbo 13

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
C scan() 0 42 11
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\Cache\Cache;
32
use OC\Files\Filesystem;
33
use OC\ForbiddenException;
34
use OC\Hooks\PublicEmitter;
35
use OC\Lock\DBLockingProvider;
36
use OCP\Files\StorageNotAvailableException;
37
use OCP\ILogger;
38
39
/**
40
 * Class Scanner
41
 *
42
 * Hooks available in scope \OC\Utils\Scanner
43
 *  - scanFile(string $absolutePath)
44
 *  - scanFolder(string $absolutePath)
45
 *
46
 * @package OC\Files\Utils
47
 */
48
class Scanner extends PublicEmitter {
49
	/**
50
	 * @var string $user
51
	 */
52
	private $user;
53
54
	/**
55
	 * @var \OC\Files\Cache\ChangePropagator
56
	 */
57
	protected $propagator;
58
59
	/**
60
	 * @var \OCP\IDBConnection
61
	 */
62
	protected $db;
63
64
	/**
65
	 * @var ILogger
66
	 */
67
	protected $logger;
68
69
	/**
70
	 * @param string $user
71
	 * @param \OCP\IDBConnection $db
72
	 * @param ILogger $logger
73 8
	 */
74 8
	public function __construct($user, $db, ILogger $logger) {
75 8
		$this->logger = $logger;
76 8
		$this->user = $user;
77 8
		$this->propagator = new ChangePropagator(new View(''));
78 8
		$this->db = $db;
79
	}
80
81
	/**
82
	 * get all storages for $dir
83
	 *
84
	 * @param string $dir
85
	 * @return \OC\Files\Mount\MountPoint[]
86 2
	 */
87
	protected function getMounts($dir) {
88 2
		//TODO: move to the node based fileapi once that's done
89 2
		\OC_Util::tearDownFS();
90
		\OC_Util::setupFS($this->user);
91 2
92 2
		$mountManager = Filesystem::getMountManager();
93 2
		$mounts = $mountManager->findIn($dir);
94 2
		$mounts[] = $mountManager->find($dir);
95
		$mounts = array_reverse($mounts); //start with the mount of $dir
96 2
97
		return $mounts;
98
	}
99
100
	/**
101
	 * attach listeners to the scanner
102
	 *
103
	 * @param \OC\Files\Mount\MountPoint $mount
104 5
	 */
105 5
	protected function attachListener($mount) {
106 5
		$scanner = $mount->getStorage()->getScanner();
107
		$emitter = $this;
108 4
		$scanner->listen('\OC\Files\Cache\Scanner', 'scanFile', function ($path) use ($mount, $emitter) {
109 5
			$emitter->emit('\OC\Files\Utils\Scanner', 'scanFile', array($mount->getMountPoint() . $path));
110
		});
111 4
		$scanner->listen('\OC\Files\Cache\Scanner', 'scanFolder', function ($path) use ($mount, $emitter) {
112 5
			$emitter->emit('\OC\Files\Utils\Scanner', 'scanFolder', array($mount->getMountPoint() . $path));
113
		});
114 4
		$scanner->listen('\OC\Files\Cache\Scanner', 'postScanFile', function ($path) use ($mount, $emitter) {
115 5
			$emitter->emit('\OC\Files\Utils\Scanner', 'postScanFile', array($mount->getMountPoint() . $path));
116
		});
117 4
		$scanner->listen('\OC\Files\Cache\Scanner', 'postScanFolder', function ($path) use ($mount, $emitter) {
118 5
			$emitter->emit('\OC\Files\Utils\Scanner', 'postScanFolder', array($mount->getMountPoint() . $path));
119
		});
120 5
		// propagate etag and mtimes when files are changed or removed
121 5
		$propagator = $this->propagator;
122 4
		$propagatorListener = function ($path) use ($mount, $propagator) {
123 4
			$fullPath = Filesystem::normalizePath($mount->getMountPoint() . $path);
124 5
			$propagator->addChange($fullPath);
125 5
		};
126 5
		$scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', $propagatorListener);
127 5
		$scanner->listen('\OC\Files\Cache\Scanner', 'removeFromCache', $propagatorListener);
128
	}
129
130
	/**
131
	 * @param string $dir
132 1
	 */
133 1
	public function backgroundScan($dir) {
134 1
		$mounts = $this->getMounts($dir);
135 1
		foreach ($mounts as $mount) {
136
			if (is_null($mount->getStorage())) {
137
				continue;
138
			}
139 1
			// don't scan the root storage
140 1
			if ($mount->getStorage()->instanceOfStorage('\OC\Files\Storage\Local') && $mount->getMountPoint() === '/') {
141
				continue;
142 1
			}
143 1
			$scanner = $mount->getStorage()->getScanner();
144 1
			$this->attachListener($mount);
0 ignored issues
show
Bug introduced by
It seems like $mount defined by $mount on line 135 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...
145 1
			$scanner->backgroundScan();
146 1
		}
147 1
		$this->propagator->propagateChanges(time());
148
	}
149
150
	/**
151
	 * @param string $dir
152
	 * @throws \OC\ForbiddenException
153 7
	 */
154 7
	public function scan($dir = '') {
155 3
		if (!Filesystem::isValidPath($dir)) {
156
			throw new \InvalidArgumentException('Invalid path to scan');
157 4
		}
158 4
		$mounts = $this->getMounts($dir);
159 4
		foreach ($mounts as $mount) {
160
			if (is_null($mount->getStorage())) {
161
				continue;
162 4
			}
163
			$storage = $mount->getStorage();
164 4
			// if the home storage isn't writable then the scanner is run as the wrong user
165
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home') and
166 4
				(!$storage->isCreatable('') or !$storage->isCreatable('files'))
167
			) {
168
				throw new ForbiddenException();
169 4
			}
170 4
			$relativePath = $mount->getInternalPath($dir);
171 4
			$scanner = $storage->getScanner();
172 4
			$scanner->setUseTransactions(false);
173 4
			$this->attachListener($mount);
0 ignored issues
show
Bug introduced by
It seems like $mount defined by $mount on line 159 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...
174 4
			$isDbLocking = \OC::$server->getLockingProvider() instanceof DBLockingProvider;
175 4
			if (!$isDbLocking) {
176 4
				$this->db->beginTransaction();
177
			}
178 4
			try {
179 4
				$scanner->scan($relativePath, \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE);
180
				$cache = $storage->getCache();
181
				if ($cache instanceof Cache) {
182
					// only re-calculate for the root folder we scanned, anything below that is taken care of by the scanner
183
					$cache->correctFolderSize($relativePath);
184 4
				}
185 4
			} catch (StorageNotAvailableException $e) {
186 4
				$this->logger->error('Storage ' . $storage->getId() . ' not available');
187 4
				$this->logger->logException($e);
188 4
				$this->emit('\OC\Files\Utils\Scanner', 'StorageNotAvailable', [$e]);
189 4
			}
190
			if (!$isDbLocking) {
191
				$this->db->commit();
192
			}
193
		}
194
		$this->propagator->propagateChanges(time());
195
	}
196
}
197
198