Passed
Push — master ( fd284f...fa2878 )
by Morris
13:09 queued 12s
created

Storage::cleanByMountId()   A

Complexity

Conditions 2
Paths 15

Size

Total Lines 31
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 24
nc 15
nop 1
dl 0
loc 31
rs 9.536
c 1
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Jörn Friedrich Dreyer <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Robin McCorkell <[email protected]>
11
 * @author Thomas Müller <[email protected]>
12
 * @author Vincent Petry <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program. If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OC\Files\Cache;
31
32
use OCP\DB\QueryBuilder\IQueryBuilder;
33
use OCP\Files\Storage\IStorage;
34
use Psr\Log\LoggerInterface;
35
36
/**
37
 * Handle the mapping between the string and numeric storage ids
38
 *
39
 * Each storage has 2 different ids
40
 * 	a string id which is generated by the storage backend and reflects the configuration of the storage (e.g. 'smb://user@host/share')
41
 * 	and a numeric storage id which is referenced in the file cache
42
 *
43
 * A mapping between the two storage ids is stored in the database and accessible trough this class
44
 *
45
 * @package OC\Files\Cache
46
 */
47
class Storage {
48
	/** @var StorageGlobal|null */
49
	private static $globalCache = null;
50
	private $storageId;
51
	private $numericId;
52
53
	/**
54
	 * @return StorageGlobal
55
	 */
56
	public static function getGlobalCache() {
57
		if (is_null(self::$globalCache)) {
58
			self::$globalCache = new StorageGlobal(\OC::$server->getDatabaseConnection());
59
		}
60
		return self::$globalCache;
61
	}
62
63
	/**
64
	 * @param \OC\Files\Storage\Storage|string $storage
65
	 * @param bool $isAvailable
66
	 * @throws \RuntimeException
67
	 */
68
	public function __construct($storage, $isAvailable = true) {
69
		if ($storage instanceof IStorage) {
70
			$this->storageId = $storage->getId();
71
		} else {
72
			$this->storageId = $storage;
73
		}
74
		$this->storageId = self::adjustStorageId($this->storageId);
75
76
		if ($row = self::getStorageById($this->storageId)) {
77
			$this->numericId = (int)$row['numeric_id'];
78
		} else {
79
			$connection = \OC::$server->getDatabaseConnection();
80
			$available = $isAvailable ? 1 : 0;
81
			if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId, 'available' => $available])) {
82
				$this->numericId = $connection->lastInsertId('*PREFIX*storages');
83
			} else {
84
				if ($row = self::getStorageById($this->storageId)) {
85
					$this->numericId = (int)$row['numeric_id'];
86
				} else {
87
					throw new \RuntimeException('Storage could neither be inserted nor be selected from the database: ' . $this->storageId);
88
				}
89
			}
90
		}
91
	}
92
93
	/**
94
	 * @param string $storageId
95
	 * @return array
96
	 */
97
	public static function getStorageById($storageId) {
98
		return self::getGlobalCache()->getStorageInfo($storageId);
99
	}
100
101
	/**
102
	 * Adjusts the storage id to use md5 if too long
103
	 * @param string $storageId storage id
104
	 * @return string unchanged $storageId if its length is less than 64 characters,
105
	 * else returns the md5 of $storageId
106
	 */
107
	public static function adjustStorageId($storageId) {
108
		if (strlen($storageId) > 64) {
109
			return md5($storageId);
110
		}
111
		return $storageId;
112
	}
113
114
	/**
115
	 * Get the numeric id for the storage
116
	 *
117
	 * @return int
118
	 */
119
	public function getNumericId() {
120
		return $this->numericId;
121
	}
122
123
	/**
124
	 * Get the string id for the storage
125
	 *
126
	 * @param int $numericId
127
	 * @return string|null either the storage id string or null if the numeric id is not known
128
	 */
129
	public static function getStorageId($numericId) {
130
		$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
131
		$query->select('id')
132
			->from('storages')
133
			->where($query->expr()->eq('numeric_id', $query->createNamedParameter($numericId)));
134
		$result = $query->execute();
135
		$row = $result->fetch();
136
		$result->closeCursor();
137
		if ($row) {
138
			return $row['id'];
139
		} else {
140
			return null;
141
		}
142
	}
143
144
	/**
145
	 * Get the numeric of the storage with the provided string id
146
	 *
147
	 * @param $storageId
148
	 * @return int|null either the numeric storage id or null if the storage id is not knwon
149
	 */
150
	public static function getNumericStorageId($storageId) {
151
		$storageId = self::adjustStorageId($storageId);
152
153
		if ($row = self::getStorageById($storageId)) {
154
			return (int)$row['numeric_id'];
155
		} else {
156
			return null;
157
		}
158
	}
159
160
	/**
161
	 * @return array|null [ available, last_checked ]
162
	 */
163
	public function getAvailability() {
164
		if ($row = self::getStorageById($this->storageId)) {
165
			return [
166
				'available' => (int)$row['available'] === 1,
167
				'last_checked' => $row['last_checked']
168
			];
169
		} else {
170
			return null;
171
		}
172
	}
173
174
	/**
175
	 * @param bool $isAvailable
176
	 * @param int $delay amount of seconds to delay reconsidering that storage further
177
	 */
178
	public function setAvailability($isAvailable, int $delay = 0) {
179
		$available = $isAvailable ? 1 : 0;
180
		if (!$isAvailable) {
181
			\OC::$server->get(LoggerInterface::class)->info('Storage with ' . $this->storageId . ' marked as unavailable', ['app' => 'lib']);
182
		}
183
184
		$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
185
		$query->update('storages')
186
			->set('available', $query->createNamedParameter($available))
187
			->set('last_checked', $query->createNamedParameter(time() + $delay))
188
			->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId)));
189
		$query->execute();
190
	}
191
192
	/**
193
	 * Check if a string storage id is known
194
	 *
195
	 * @param string $storageId
196
	 * @return bool
197
	 */
198
	public static function exists($storageId) {
199
		return !is_null(self::getNumericStorageId($storageId));
200
	}
201
202
	/**
203
	 * remove the entry for the storage
204
	 *
205
	 * @param string $storageId
206
	 */
207
	public static function remove($storageId) {
208
		$storageId = self::adjustStorageId($storageId);
209
		$numericId = self::getNumericStorageId($storageId);
210
211
		$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
212
		$query->delete('storages')
213
			->where($query->expr()->eq('id', $query->createNamedParameter($storageId)));
214
		$query->execute();
215
216
		if (!is_null($numericId)) {
217
			$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
218
			$query->delete('filecache')
219
				->where($query->expr()->eq('storage', $query->createNamedParameter($numericId)));
220
			$query->execute();
221
		}
222
	}
223
224
	/**
225
	 * remove the entry for the storage by the mount id
226
	 *
227
	 * @param int $mountId
228
	 */
229
	public static function cleanByMountId(int $mountId) {
230
		$db = \OC::$server->getDatabaseConnection();
231
232
		try {
233
			$db->beginTransaction();
234
235
			$query = $db->getQueryBuilder();
236
			$query->select('storage_id')
237
				->from('mounts')
238
				->where($query->expr()->eq('mount_id', $query->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
239
			$storageIds = $query->executeQuery()->fetchAll(\PDO::FETCH_COLUMN);
240
241
			$query = $db->getQueryBuilder();
242
			$query->delete('filecache')
243
				->where($query->expr()->in('storage', $query->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)));
244
			$query->executeStatement();
245
246
			$query = $db->getQueryBuilder();
247
			$query->delete('storages')
248
				->where($query->expr()->eq('numeric_id', $query->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)));
249
			$query->executeStatement();
250
251
			$query = $db->getQueryBuilder();
252
			$query->delete('mounts')
253
				->where($query->expr()->eq('mount_id', $query->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
254
			$query->executeStatement();
255
256
			$db->commit();
257
		} catch (\Exception $e) {
258
			$db->rollBack();
259
			throw $e;
260
		}
261
	}
262
}
263