Completed
Push — master ( 63038d...c013e0 )
by Thomas
11:13
created

Updater::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
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 Thomas Müller <[email protected]>
9
 * @author Vincent Petry <[email protected]>
10
 *
11
 * @copyright Copyright (c) 2018, ownCloud GmbH
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OC\Files\Cache;
29
30
use OCP\Files\Cache\ICacheEntry;
31
use OCP\Files\Cache\IUpdater;
32
use OCP\Files\Storage\IStorage;
33
34
/**
35
 * Update the cache and propagate changes
36
 *
37
 */
38
class Updater implements IUpdater {
39
	/**
40
	 * @var bool
41
	 */
42
	protected $enabled = true;
43
44
	/**
45
	 * @var \OC\Files\Storage\Storage
46
	 */
47
	protected $storage;
48
49
	/**
50
	 * @var \OC\Files\Cache\Propagator
51
	 */
52
	protected $propagator;
53
54
	/**
55
	 * @var Scanner
56
	 */
57
	protected $scanner;
58
59
	/**
60
	 * @var Cache
61
	 */
62
	protected $cache;
63
64
	/**
65
	 * @param \OC\Files\Storage\Storage $storage
66
	 */
67
	public function __construct(\OC\Files\Storage\Storage $storage) {
68
		$this->storage = $storage;
69
		$this->propagator = $storage->getPropagator();
70
		$this->scanner = $storage->getScanner();
71
		$this->cache = $storage->getCache();
72
	}
73
74
	/**
75
	 * Disable updating the cache trough this updater
76
	 */
77
	public function disable() {
78
		$this->enabled = false;
79
	}
80
81
	/**
82
	 * Re-enable the updating of the cache trough this updater
83
	 */
84
	public function enable() {
85
		$this->enabled = true;
86
	}
87
88
	/**
89
	 * Get the propagator for etags and mtime for the view the updater works on
90
	 *
91
	 * @return Propagator
92
	 */
93
	public function getPropagator() {
94
		return $this->propagator;
95
	}
96
97
	/**
98
	 * Propagate etag and mtime changes for the parent folders of $path up to the root of the filesystem
99
	 *
100
	 * @param string $path the path of the file to propagate the changes for
101
	 * @param int|null $time the timestamp to set as mtime for the parent folders, if left out the current time is used
102
	 */
103
	public function propagate($path, $time = null) {
104
		if (Scanner::isPartialFile($path)) {
105
			return;
106
		}
107
		$this->propagator->propagateChange($path, $time);
108
	}
109
110
	/**
111
	 * Update the cache for $path and update the size, etag and mtime of the parent folders
112
	 *
113
	 * @param string $path
114
	 * @param int $time
115
	 */
116
	public function update($path, $time = null) {
117
		if (!$this->enabled or Scanner::isPartialFile($path)) {
118
			return;
119
		}
120
		if (\is_null($time)) {
121
			$time = \time();
122
		}
123
124
		$data = $this->scanner->scan($path, Scanner::SCAN_SHALLOW, -1, false);
125
		if (
126
			isset($data['oldSize'], $data['size'])   &&
127
			!$data['encrypted'] // encryption is a pita and touches the cache itself
128
		) {
129
			$sizeDifference = $data['size'] - $data['oldSize'];
130
		} else {
131
			// scanner didn't provide size info, fallback to full size calculation
132
			$sizeDifference = 0;
133
			if ($this->cache instanceof Cache) {
134
				$this->cache->correctFolderSize($path, $data);
135
			}
136
		}
137
		$this->correctParentStorageMtime($path);
138
		$this->propagator->propagateChange($path, $time, $sizeDifference);
139
	}
140
141
	/**
142
	 * Remove $path from the cache and update the size, etag and mtime of the parent folders
143
	 *
144
	 * @param string $path
145
	 */
146
	public function remove($path) {
147
		if (!$this->enabled or Scanner::isPartialFile($path)) {
148
			return;
149
		}
150
151
		$parent = \dirname($path);
152
		if ($parent === '.') {
153
			$parent = '';
154
		}
155
156
		$entry = $this->cache->get($path);
157
158
		$this->cache->remove($path);
159
160
		$this->correctParentStorageMtime($path);
161
		if ($entry instanceof ICacheEntry) {
162
			$this->propagator->propagateChange($path, \time(), -$entry->getSize());
163
		} else {
164
			$this->propagator->propagateChange($path, \time());
165
			if ($this->cache instanceof Cache) {
166
				$this->cache->correctFolderSize($parent);
167
			}
168
		}
169
170
	}
171
172
	/**
173
	 * Rename a file or folder in the cache and update the size, etag and mtime of the parent folders
174
	 *
175
	 * @param IStorage $sourceStorage
176
	 * @param string $source
177
	 * @param string $target
178
	 */
179
	public function renameFromStorage(IStorage $sourceStorage, $source, $target) {
180
		if (!$this->enabled or Scanner::isPartialFile($source) or Scanner::isPartialFile($target)) {
181
			return;
182
		}
183
184
		$time = \time();
185
186
		$sourceCache = $sourceStorage->getCache();
187
		$sourceUpdater = $sourceStorage->getUpdater();
188
		$sourcePropagator = $sourceStorage->getPropagator();
189
190
		if ($sourceCache->inCache($source)) {
191
			if ($this->cache->inCache($target)) {
192
				$this->cache->remove($target);
193
			}
194
195
			if ($sourceStorage === $this->storage) {
196
				$this->cache->move($source, $target);
197
			} else {
198
				$this->cache->moveFromCache($sourceCache, $source, $target);
199
			}
200
		}
201
202
		if (\pathinfo($source, PATHINFO_EXTENSION) !== \pathinfo($target, PATHINFO_EXTENSION)) {
203
			// handle mime type change
204
			$mimeType = $this->storage->getMimeType($target);
205
			$fileId = $this->cache->getId($target);
206
			$this->cache->update($fileId, ['mimetype' => $mimeType]);
207
		}
208
209
		if ($sourceCache instanceof Cache) {
210
			$sourceCache->correctFolderSize($source);
211
		}
212
		if ($this->cache instanceof Cache) {
213
			$this->cache->correctFolderSize($target);
214
		}
215
		if ($sourceUpdater instanceof Updater) {
216
			$sourceUpdater->correctParentStorageMtime($source);
217
		}
218
		$this->correctParentStorageMtime($target);
219
		$this->updateStorageMTimeOnly($target);
220
		$sourcePropagator->propagateChange($source, $time);
221
		$this->propagator->propagateChange($target, $time);
222
	}
223
224
	private function updateStorageMTimeOnly($internalPath) {
225
		$fileId = $this->cache->getId($internalPath);
226
		if ($fileId !== -1) {
227
			$this->cache->update(
228
				$fileId, [
229
					'mtime' => null, // this magic tells it to not overwrite mtime
230
					'storage_mtime' => $this->storage->filemtime($internalPath)
231
				]
232
			);
233
		}
234
	}
235
236
	/**
237
	 * update the storage_mtime of the direct parent in the cache to the mtime from the storage
238
	 *
239
	 * @param string $internalPath
240
	 */
241
	private function correctParentStorageMtime($internalPath) {
242
		$parentId = $this->cache->getParentId($internalPath);
243
		$parent = \dirname($internalPath);
244
		if ($parentId != -1) {
245
			$mtime = $this->storage->filemtime($parent);
246
			if ($mtime !== false) {
247
				$this->cache->update($parentId, ['storage_mtime' => $mtime]);
248
			}
249
		}
250
	}
251
}
252