Completed
Push — master ( b4df57...e6895c )
by Lukas
26s
created

Updater   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 214
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 214
rs 8.8
wmc 36
lcom 1
cbo 8

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A disable() 0 3 1
A enable() 0 3 1
A getPropagator() 0 3 1
A propagate() 0 6 2
C update() 0 24 8
B remove() 0 25 6
C renameFromStorage() 0 44 11
A updateStorageMTimeOnly() 0 11 2
A correctParentStorageMtime() 0 10 3
1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 * @author Daniel Jagszent <[email protected]>
5
 * @author Michael Gapczynski <[email protected]>
6
 * @author Morris Jobke <[email protected]>
7
 * @author Robin Appelman <[email protected]>
8
 * @author Vincent Petry <[email protected]>
9
 *
10
 * @copyright Copyright (c) 2016, 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\Cache;
28
29
use OCP\Files\Cache\ICacheEntry;
30
use OCP\Files\Cache\IUpdater;
31
use OCP\Files\Storage\IStorage;
32
33
/**
34
 * Update the cache and propagate changes
35
 *
36
 */
37
class Updater implements IUpdater {
38
	/**
39
	 * @var bool
40
	 */
41
	protected $enabled = true;
42
43
	/**
44
	 * @var \OC\Files\Storage\Storage
45
	 */
46
	protected $storage;
47
48
	/**
49
	 * @var \OC\Files\Cache\Propagator
50
	 */
51
	protected $propagator;
52
53
	/**
54
	 * @var Scanner
55
	 */
56
	protected $scanner;
57
58
	/**
59
	 * @var Cache
60
	 */
61
	protected $cache;
62
63
	/**
64
	 * @param \OC\Files\Storage\Storage $storage
65
	 */
66
	public function __construct(\OC\Files\Storage\Storage $storage) {
67
		$this->storage = $storage;
68
		$this->propagator = $storage->getPropagator();
69
		$this->scanner = $storage->getScanner();
70
		$this->cache = $storage->getCache();
71
	}
72
73
	/**
74
	 * Disable updating the cache trough this updater
75
	 */
76
	public function disable() {
77
		$this->enabled = false;
78
	}
79
80
	/**
81
	 * Re-enable the updating of the cache trough this updater
82
	 */
83
	public function enable() {
84
		$this->enabled = true;
85
	}
86
87
	/**
88
	 * Get the propagator for etags and mtime for the view the updater works on
89
	 *
90
	 * @return Propagator
91
	 */
92
	public function getPropagator() {
93
		return $this->propagator;
94
	}
95
96
	/**
97
	 * Propagate etag and mtime changes for the parent folders of $path up to the root of the filesystem
98
	 *
99
	 * @param string $path the path of the file to propagate the changes for
100
	 * @param int|null $time the timestamp to set as mtime for the parent folders, if left out the current time is used
101
	 */
102
	public function propagate($path, $time = null) {
103
		if (Scanner::isPartialFile($path)) {
104
			return;
105
		}
106
		$this->propagator->propagateChange($path, $time);
107
	}
108
109
	/**
110
	 * Update the cache for $path and update the size, etag and mtime of the parent folders
111
	 *
112
	 * @param string $path
113
	 * @param int $time
114
	 */
115
	public function update($path, $time = null) {
116
		if (!$this->enabled or Scanner::isPartialFile($path)) {
117
			return;
118
		}
119
		if (is_null($time)) {
120
			$time = time();
121
		}
122
123
		$data = $this->scanner->scan($path, Scanner::SCAN_SHALLOW, -1, false);
124
		if (
125
			isset($data['oldSize']) && isset($data['size']) &&
126
			!$data['encrypted'] // encryption is a pita and touches the cache itself
127
		) {
128
			$sizeDifference = $data['size'] - $data['oldSize'];
129
		} else {
130
			// scanner didn't provide size info, fallback to full size calculation
131
			$sizeDifference = 0;
132
			if ($this->cache instanceof Cache) {
133
				$this->cache->correctFolderSize($path, $data);
134
			}
135
		}
136
		$this->correctParentStorageMtime($path);
137
		$this->propagator->propagateChange($path, $time, $sizeDifference);
138
	}
139
140
	/**
141
	 * Remove $path from the cache and update the size, etag and mtime of the parent folders
142
	 *
143
	 * @param string $path
144
	 */
145
	public function remove($path) {
146
		if (!$this->enabled or Scanner::isPartialFile($path)) {
147
			return;
148
		}
149
150
		$parent = dirname($path);
151
		if ($parent === '.') {
152
			$parent = '';
153
		}
154
155
		$entry = $this->cache->get($path);
156
157
		$this->cache->remove($path);
158
159
		$this->correctParentStorageMtime($path);
160
		if ($entry instanceof ICacheEntry) {
161
			$this->propagator->propagateChange($path, time(), -$entry->getSize());
162
		} else {
163
			$this->propagator->propagateChange($path, time());
164
			if ($this->cache instanceof Cache) {
165
				$this->cache->correctFolderSize($parent);
166
			}
167
		}
168
169
	}
170
171
	/**
172
	 * Rename a file or folder in the cache and update the size, etag and mtime of the parent folders
173
	 *
174
	 * @param IStorage $sourceStorage
175
	 * @param string $source
176
	 * @param string $target
177
	 */
178
	public function renameFromStorage(IStorage $sourceStorage, $source, $target) {
179
		if (!$this->enabled or Scanner::isPartialFile($source) or Scanner::isPartialFile($target)) {
180
			return;
181
		}
182
183
		$time = time();
184
185
		$sourceCache = $sourceStorage->getCache();
186
		$sourceUpdater = $sourceStorage->getUpdater();
187
		$sourcePropagator = $sourceStorage->getPropagator();
188
189
		if ($sourceCache->inCache($source)) {
190
			if ($this->cache->inCache($target)) {
191
				$this->cache->remove($target);
192
			}
193
194
			if ($sourceStorage === $this->storage) {
195
				$this->cache->move($source, $target);
196
			} else {
197
				$this->cache->moveFromCache($sourceCache, $source, $target);
198
			}
199
		}
200
201
		if (pathinfo($source, PATHINFO_EXTENSION) !== pathinfo($target, PATHINFO_EXTENSION)) {
202
			// handle mime type change
203
			$mimeType = $this->storage->getMimeType($target);
204
			$fileId = $this->cache->getId($target);
205
			$this->cache->update($fileId, ['mimetype' => $mimeType]);
206
		}
207
208
		if ($sourceCache instanceof Cache) {
209
			$sourceCache->correctFolderSize($source);
210
		}
211
		if ($this->cache instanceof Cache) {
212
			$this->cache->correctFolderSize($target);
213
		}
214
		if ($sourceUpdater instanceof Updater) {
215
			$sourceUpdater->correctParentStorageMtime($source);
216
		}
217
		$this->correctParentStorageMtime($target);
218
		$this->updateStorageMTimeOnly($target);
219
		$sourcePropagator->propagateChange($source, $time);
220
		$this->propagator->propagateChange($target, $time);
221
	}
222
223
	private function updateStorageMTimeOnly($internalPath) {
224
		$fileId = $this->cache->getId($internalPath);
225
		if ($fileId !== -1) {
226
			$this->cache->update(
227
				$fileId, [
228
					'mtime' => null, // this magic tells it to not overwrite mtime
229
					'storage_mtime' => $this->storage->filemtime($internalPath)
230
				]
231
			);
232
		}
233
	}
234
235
	/**
236
	 * update the storage_mtime of the direct parent in the cache to the mtime from the storage
237
	 *
238
	 * @param string $internalPath
239
	 */
240
	private function correctParentStorageMtime($internalPath) {
241
		$parentId = $this->cache->getParentId($internalPath);
242
		$parent = dirname($internalPath);
243
		if ($parentId != -1) {
244
			$mtime = $this->storage->filemtime($parent);
245
			if ($mtime !== false) {
246
				$this->cache->update($parentId, array('storage_mtime' => $mtime));
247
			}
248
		}
249
	}
250
}
251