Completed
Push — stable8.1 ( 0df34d...256c2c )
by Roeland
65:01
created

Scanner::backgroundScan()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5.0113

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 16
ccs 12
cts 13
cp 0.9231
rs 8.8571
c 1
b 0
f 0
cc 5
eloc 11
nc 4
nop 1
crap 5.0113
1
<?php
2
/**
3
 * @author Jörn Friedrich Dreyer <[email protected]>
4
 * @author Morris Jobke <[email protected]>
5
 * @author Robin Appelman <[email protected]>
6
 * @author Scrutinizer Auto-Fixer <[email protected]>
7
 * @author Thomas Müller <[email protected]>
8
 *
9
 * @copyright Copyright (c) 2015, 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\View;
29
use OC\Files\Cache\ChangePropagator;
30
use OC\Files\Filesystem;
31
use OC\ForbiddenException;
32
use OC\Hooks\PublicEmitter;
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 \OC\Files\Cache\ChangePropagator
53
	 */
54
	protected $propagator;
55
56
	/**
57
	 * @var \OCP\IDBConnection
58
	 */
59
	protected $db;
60
61
	/**
62
	 * @var ILogger
63
	 */
64
	protected $logger;
65
66
	/**
67
	 * @param string $user
68
	 * @param \OCP\IDBConnection $db
69
	 * @param ILogger $logger
70
	 */
71 8
	public function __construct($user, $db, ILogger $logger) {
72 8
		$this->logger = $logger;
73 8
		$this->user = $user;
74 8
		$this->propagator = new ChangePropagator(new View(''));
75 8
		$this->db = $db;
76 8
	}
77
78
	/**
79
	 * get all storages for $dir
80
	 *
81
	 * @param string $dir
82
	 * @return \OC\Files\Mount\MountPoint[]
83
	 */
84 2
	protected function getMounts($dir) {
85
		//TODO: move to the node based fileapi once that's done
86 2
		\OC_Util::tearDownFS();
87 2
		\OC_Util::setupFS($this->user);
88
89 2
		$mountManager = Filesystem::getMountManager();
90 2
		$mounts = $mountManager->findIn($dir);
91 2
		$mounts[] = $mountManager->find($dir);
92 2
		$mounts = array_reverse($mounts); //start with the mount of $dir
93
94 2
		return $mounts;
95
	}
96
97
	/**
98
	 * attach listeners to the scanner
99
	 *
100
	 * @param \OC\Files\Mount\MountPoint $mount
101
	 */
102 5
	protected function attachListener($mount) {
103 5
		$scanner = $mount->getStorage()->getScanner();
104 5
		$emitter = $this;
105
		$scanner->listen('\OC\Files\Cache\Scanner', 'scanFile', function ($path) use ($mount, $emitter) {
106 4
			$emitter->emit('\OC\Files\Utils\Scanner', 'scanFile', array($mount->getMountPoint() . $path));
107 5
		});
108
		$scanner->listen('\OC\Files\Cache\Scanner', 'scanFolder', function ($path) use ($mount, $emitter) {
109 4
			$emitter->emit('\OC\Files\Utils\Scanner', 'scanFolder', array($mount->getMountPoint() . $path));
110 5
		});
111
		$scanner->listen('\OC\Files\Cache\Scanner', 'postScanFile', function ($path) use ($mount, $emitter) {
112 4
			$emitter->emit('\OC\Files\Utils\Scanner', 'postScanFile', array($mount->getMountPoint() . $path));
113 5
		});
114
		$scanner->listen('\OC\Files\Cache\Scanner', 'postScanFolder', function ($path) use ($mount, $emitter) {
115 4
			$emitter->emit('\OC\Files\Utils\Scanner', 'postScanFolder', array($mount->getMountPoint() . $path));
116 5
		});
117
		// propagate etag and mtimes when files are changed or removed
118 5
		$propagator = $this->propagator;
119 5
		$propagatorListener = function ($path) use ($mount, $propagator) {
120 4
			$fullPath = Filesystem::normalizePath($mount->getMountPoint() . $path);
121 4
			$propagator->addChange($fullPath);
122 5
		};
123 5
		$scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', $propagatorListener);
124 5
		$scanner->listen('\OC\Files\Cache\Scanner', 'removeFromCache', $propagatorListener);
125 5
	}
126
127
	/**
128
	 * @param string $dir
129
	 */
130 1
	public function backgroundScan($dir) {
131 1
		$mounts = $this->getMounts($dir);
132 1
		foreach ($mounts as $mount) {
133 1
			if (is_null($mount->getStorage())) {
134
				continue;
135
			}
136
			// don't scan the root storage
137 1
			if ($mount->getStorage()->instanceOfStorage('\OC\Files\Storage\Local') && $mount->getMountPoint() === '/') {
138 1
				continue;
139
			}
140 1
			$scanner = $mount->getStorage()->getScanner();
141 1
			$this->attachListener($mount);
0 ignored issues
show
Bug introduced by
It seems like $mount defined by $mount on line 132 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...
142 1
			$scanner->backgroundScan();
143 1
		}
144 1
		$this->propagator->propagateChanges(time());
145 1
	}
146
147
	/**
148
	 * @param string $dir
149
	 * @throws \OC\ForbiddenException
150
	 */
151 7
	public function scan($dir = '') {
152 7
		if (!Filesystem::isValidPath($dir)) {
153 3
			throw new \InvalidArgumentException('Invalid path to scan');
154
		}
155 4
		$mounts = $this->getMounts($dir);
156 4
		foreach ($mounts as $mount) {
157 4
			if (is_null($mount->getStorage())) {
158
				continue;
159
			}
160 4
			$storage = $mount->getStorage();
161
			// if the home storage isn't writable then the scanner is run as the wrong user
162 4
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home') and
163
				(!$storage->isCreatable('') or !$storage->isCreatable('files'))
164 4
			) {
165
				throw new ForbiddenException();
166
			}
167 4
			$relativePath = $mount->getInternalPath($dir);
168 4
			$scanner = $storage->getScanner();
169 4
			$scanner->setUseTransactions(false);
170 4
			$this->attachListener($mount);
0 ignored issues
show
Bug introduced by
It seems like $mount defined by $mount on line 156 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...
171 4
			$this->db->beginTransaction();
172
			try {
173 4
				$scanner->scan($relativePath, \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE);
174 4
			} catch (StorageNotAvailableException $e) {
175
				$this->logger->error('Storage ' . $storage->getId() . ' not available');
176
				$this->logger->error($e->getMessage());
177
				$this->emit('\OC\Files\Utils\Scanner', 'StorageNotAvailable', [$e]);
178
			}
179 4
			$this->db->commit();
180 4
		}
181 4
		$this->propagator->propagateChanges(time());
182 4
	}
183
}
184
185