Passed
Push — master ( 7a09b7...f1e2fb )
by Robin
14:09 queued 14s
created
lib/public/Files/Cache/ICache.php 1 patch
Indentation   +206 added lines, -206 removed lines patch added patch discarded remove patch
@@ -38,231 +38,231 @@
 block discarded – undo
38 38
  * @since 9.0.0
39 39
  */
40 40
 interface ICache {
41
-	public const NOT_FOUND = 0;
42
-	public const PARTIAL = 1; //only partial data available, file not cached in the database
43
-	public const SHALLOW = 2; //folder in cache, but not all child files are completely scanned
44
-	public const COMPLETE = 3;
41
+    public const NOT_FOUND = 0;
42
+    public const PARTIAL = 1; //only partial data available, file not cached in the database
43
+    public const SHALLOW = 2; //folder in cache, but not all child files are completely scanned
44
+    public const COMPLETE = 3;
45 45
 
46
-	/**
47
-	 * Get the numeric storage id for this cache's storage
48
-	 *
49
-	 * @return int
50
-	 * @since 9.0.0
51
-	 */
52
-	public function getNumericStorageId();
46
+    /**
47
+     * Get the numeric storage id for this cache's storage
48
+     *
49
+     * @return int
50
+     * @since 9.0.0
51
+     */
52
+    public function getNumericStorageId();
53 53
 
54
-	/**
55
-	 * get the stored metadata of a file or folder
56
-	 *
57
-	 * @param string | int $file either the path of a file or folder or the file id for a file or folder
58
-	 * @return ICacheEntry|false the cache entry or false if the file is not found in the cache
59
-	 * @since 9.0.0
60
-	 */
61
-	public function get($file);
54
+    /**
55
+     * get the stored metadata of a file or folder
56
+     *
57
+     * @param string | int $file either the path of a file or folder or the file id for a file or folder
58
+     * @return ICacheEntry|false the cache entry or false if the file is not found in the cache
59
+     * @since 9.0.0
60
+     */
61
+    public function get($file);
62 62
 
63
-	/**
64
-	 * get the metadata of all files stored in $folder
65
-	 *
66
-	 * Only returns files one level deep, no recursion
67
-	 *
68
-	 * @param string $folder
69
-	 * @return ICacheEntry[]
70
-	 * @since 9.0.0
71
-	 */
72
-	public function getFolderContents($folder);
63
+    /**
64
+     * get the metadata of all files stored in $folder
65
+     *
66
+     * Only returns files one level deep, no recursion
67
+     *
68
+     * @param string $folder
69
+     * @return ICacheEntry[]
70
+     * @since 9.0.0
71
+     */
72
+    public function getFolderContents($folder);
73 73
 
74
-	/**
75
-	 * get the metadata of all files stored in $folder
76
-	 *
77
-	 * Only returns files one level deep, no recursion
78
-	 *
79
-	 * @param int $fileId the file id of the folder
80
-	 * @return ICacheEntry[]
81
-	 * @since 9.0.0
82
-	 */
83
-	public function getFolderContentsById($fileId);
74
+    /**
75
+     * get the metadata of all files stored in $folder
76
+     *
77
+     * Only returns files one level deep, no recursion
78
+     *
79
+     * @param int $fileId the file id of the folder
80
+     * @return ICacheEntry[]
81
+     * @since 9.0.0
82
+     */
83
+    public function getFolderContentsById($fileId);
84 84
 
85
-	/**
86
-	 * store meta data for a file or folder
87
-	 * This will automatically call either insert or update depending on if the file exists
88
-	 *
89
-	 * @param string $file
90
-	 * @param array $data
91
-	 *
92
-	 * @return int file id
93
-	 * @throws \RuntimeException
94
-	 * @since 9.0.0
95
-	 */
96
-	public function put($file, array $data);
85
+    /**
86
+     * store meta data for a file or folder
87
+     * This will automatically call either insert or update depending on if the file exists
88
+     *
89
+     * @param string $file
90
+     * @param array $data
91
+     *
92
+     * @return int file id
93
+     * @throws \RuntimeException
94
+     * @since 9.0.0
95
+     */
96
+    public function put($file, array $data);
97 97
 
98
-	/**
99
-	 * insert meta data for a new file or folder
100
-	 *
101
-	 * @param string $file
102
-	 * @param array $data
103
-	 *
104
-	 * @return int file id
105
-	 * @throws \RuntimeException
106
-	 * @since 9.0.0
107
-	 */
108
-	public function insert($file, array $data);
98
+    /**
99
+     * insert meta data for a new file or folder
100
+     *
101
+     * @param string $file
102
+     * @param array $data
103
+     *
104
+     * @return int file id
105
+     * @throws \RuntimeException
106
+     * @since 9.0.0
107
+     */
108
+    public function insert($file, array $data);
109 109
 
110
-	/**
111
-	 * update the metadata of an existing file or folder in the cache
112
-	 *
113
-	 * @param int $id the fileid of the existing file or folder
114
-	 * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
115
-	 * @since 9.0.0
116
-	 */
117
-	public function update($id, array $data);
110
+    /**
111
+     * update the metadata of an existing file or folder in the cache
112
+     *
113
+     * @param int $id the fileid of the existing file or folder
114
+     * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
115
+     * @since 9.0.0
116
+     */
117
+    public function update($id, array $data);
118 118
 
119
-	/**
120
-	 * get the file id for a file
121
-	 *
122
-	 * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
123
-	 *
124
-	 * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
125
-	 *
126
-	 * @param string $file
127
-	 * @return int
128
-	 * @since 9.0.0
129
-	 */
130
-	public function getId($file);
119
+    /**
120
+     * get the file id for a file
121
+     *
122
+     * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
123
+     *
124
+     * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
125
+     *
126
+     * @param string $file
127
+     * @return int
128
+     * @since 9.0.0
129
+     */
130
+    public function getId($file);
131 131
 
132
-	/**
133
-	 * get the id of the parent folder of a file
134
-	 *
135
-	 * @param string $file
136
-	 * @return int
137
-	 * @since 9.0.0
138
-	 */
139
-	public function getParentId($file);
132
+    /**
133
+     * get the id of the parent folder of a file
134
+     *
135
+     * @param string $file
136
+     * @return int
137
+     * @since 9.0.0
138
+     */
139
+    public function getParentId($file);
140 140
 
141
-	/**
142
-	 * check if a file is available in the cache
143
-	 *
144
-	 * @param string $file
145
-	 * @return bool
146
-	 * @since 9.0.0
147
-	 */
148
-	public function inCache($file);
141
+    /**
142
+     * check if a file is available in the cache
143
+     *
144
+     * @param string $file
145
+     * @return bool
146
+     * @since 9.0.0
147
+     */
148
+    public function inCache($file);
149 149
 
150
-	/**
151
-	 * remove a file or folder from the cache
152
-	 *
153
-	 * when removing a folder from the cache all files and folders inside the folder will be removed as well
154
-	 *
155
-	 * @param string $file
156
-	 * @since 9.0.0
157
-	 */
158
-	public function remove($file);
150
+    /**
151
+     * remove a file or folder from the cache
152
+     *
153
+     * when removing a folder from the cache all files and folders inside the folder will be removed as well
154
+     *
155
+     * @param string $file
156
+     * @since 9.0.0
157
+     */
158
+    public function remove($file);
159 159
 
160
-	/**
161
-	 * Move a file or folder in the cache
162
-	 *
163
-	 * @param string $source
164
-	 * @param string $target
165
-	 * @since 9.0.0
166
-	 */
167
-	public function move($source, $target);
160
+    /**
161
+     * Move a file or folder in the cache
162
+     *
163
+     * @param string $source
164
+     * @param string $target
165
+     * @since 9.0.0
166
+     */
167
+    public function move($source, $target);
168 168
 
169
-	/**
170
-	 * Move a file or folder in the cache
171
-	 *
172
-	 * Note that this should make sure the entries are removed from the source cache
173
-	 *
174
-	 * @param \OCP\Files\Cache\ICache $sourceCache
175
-	 * @param string $sourcePath
176
-	 * @param string $targetPath
177
-	 * @throws \OC\DatabaseException
178
-	 * @since 9.0.0
179
-	 */
180
-	public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath);
169
+    /**
170
+     * Move a file or folder in the cache
171
+     *
172
+     * Note that this should make sure the entries are removed from the source cache
173
+     *
174
+     * @param \OCP\Files\Cache\ICache $sourceCache
175
+     * @param string $sourcePath
176
+     * @param string $targetPath
177
+     * @throws \OC\DatabaseException
178
+     * @since 9.0.0
179
+     */
180
+    public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath);
181 181
 
182
-	/**
183
-	 * Copy a file or folder in the cache
184
-	 *
185
-	 * @param ICache $sourceCache
186
-	 * @param ICacheEntry $sourceEntry
187
-	 * @param string $targetPath
188
-	 * @return int fileid of copied entry
189
-	 * @since 22.0.0
190
-	 */
191
-	public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int;
182
+    /**
183
+     * Copy a file or folder in the cache
184
+     *
185
+     * @param ICache $sourceCache
186
+     * @param ICacheEntry $sourceEntry
187
+     * @param string $targetPath
188
+     * @return int fileid of copied entry
189
+     * @since 22.0.0
190
+     */
191
+    public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int;
192 192
 
193
-	/**
194
-	 * Get the scan status of a file
195
-	 *
196
-	 * - ICache::NOT_FOUND: File is not in the cache
197
-	 * - ICache::PARTIAL: File is not stored in the cache but some incomplete data is known
198
-	 * - ICache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
199
-	 * - ICache::COMPLETE: The file or folder, with all it's children) are fully scanned
200
-	 *
201
-	 * @param string $file
202
-	 *
203
-	 * @return int ICache::NOT_FOUND, ICache::PARTIAL, ICache::SHALLOW or ICache::COMPLETE
204
-	 * @since 9.0.0
205
-	 */
206
-	public function getStatus($file);
193
+    /**
194
+     * Get the scan status of a file
195
+     *
196
+     * - ICache::NOT_FOUND: File is not in the cache
197
+     * - ICache::PARTIAL: File is not stored in the cache but some incomplete data is known
198
+     * - ICache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
199
+     * - ICache::COMPLETE: The file or folder, with all it's children) are fully scanned
200
+     *
201
+     * @param string $file
202
+     *
203
+     * @return int ICache::NOT_FOUND, ICache::PARTIAL, ICache::SHALLOW or ICache::COMPLETE
204
+     * @since 9.0.0
205
+     */
206
+    public function getStatus($file);
207 207
 
208
-	/**
209
-	 * search for files matching $pattern, files are matched if their filename matches the search pattern
210
-	 *
211
-	 * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
212
-	 * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
213
-	 * @since 9.0.0
214
-	 * @deprecated 9.0.0 due to lack of pagination, not all backends might implement this
215
-	 */
216
-	public function search($pattern);
208
+    /**
209
+     * search for files matching $pattern, files are matched if their filename matches the search pattern
210
+     *
211
+     * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
212
+     * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
213
+     * @since 9.0.0
214
+     * @deprecated 9.0.0 due to lack of pagination, not all backends might implement this
215
+     */
216
+    public function search($pattern);
217 217
 
218
-	/**
219
-	 * search for files by mimetype
220
-	 *
221
-	 * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
222
-	 *        where it will search for all mimetypes in the group ('image/*')
223
-	 * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
224
-	 * @since 9.0.0
225
-	 * @deprecated 9.0.0 due to lack of pagination, not all backends might implement this
226
-	 */
227
-	public function searchByMime($mimetype);
218
+    /**
219
+     * search for files by mimetype
220
+     *
221
+     * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
222
+     *        where it will search for all mimetypes in the group ('image/*')
223
+     * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
224
+     * @since 9.0.0
225
+     * @deprecated 9.0.0 due to lack of pagination, not all backends might implement this
226
+     */
227
+    public function searchByMime($mimetype);
228 228
 
229
-	/**
230
-	 * Search for files with a flexible query
231
-	 *
232
-	 * @param ISearchQuery $query
233
-	 * @return ICacheEntry[]
234
-	 * @throw \InvalidArgumentException if the cache is unable to perform the query
235
-	 * @since 12.0.0
236
-	 */
237
-	public function searchQuery(ISearchQuery $query);
229
+    /**
230
+     * Search for files with a flexible query
231
+     *
232
+     * @param ISearchQuery $query
233
+     * @return ICacheEntry[]
234
+     * @throw \InvalidArgumentException if the cache is unable to perform the query
235
+     * @since 12.0.0
236
+     */
237
+    public function searchQuery(ISearchQuery $query);
238 238
 
239
-	/**
240
-	 * find a folder in the cache which has not been fully scanned
241
-	 *
242
-	 * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
243
-	 * use the one with the highest id gives the best result with the background scanner, since that is most
244
-	 * likely the folder where we stopped scanning previously
245
-	 *
246
-	 * @return string|bool the path of the folder or false when no folder matched
247
-	 * @since 9.0.0
248
-	 */
249
-	public function getIncomplete();
239
+    /**
240
+     * find a folder in the cache which has not been fully scanned
241
+     *
242
+     * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
243
+     * use the one with the highest id gives the best result with the background scanner, since that is most
244
+     * likely the folder where we stopped scanning previously
245
+     *
246
+     * @return string|bool the path of the folder or false when no folder matched
247
+     * @since 9.0.0
248
+     */
249
+    public function getIncomplete();
250 250
 
251
-	/**
252
-	 * get the path of a file on this storage by it's file id
253
-	 *
254
-	 * @param int $id the file id of the file or folder to search
255
-	 * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
256
-	 * @since 9.0.0
257
-	 */
258
-	public function getPathById($id);
251
+    /**
252
+     * get the path of a file on this storage by it's file id
253
+     *
254
+     * @param int $id the file id of the file or folder to search
255
+     * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
256
+     * @since 9.0.0
257
+     */
258
+    public function getPathById($id);
259 259
 
260
-	/**
261
-	 * normalize the given path for usage in the cache
262
-	 *
263
-	 * @param string $path
264
-	 * @return string
265
-	 * @since 9.0.0
266
-	 */
267
-	public function normalize($path);
260
+    /**
261
+     * normalize the given path for usage in the cache
262
+     *
263
+     * @param string $path
264
+     * @return string
265
+     * @since 9.0.0
266
+     */
267
+    public function normalize($path);
268 268
 }
Please login to merge, or discard this patch.
lib/private/Files/Cache/Cache.php 2 patches
Indentation   +1052 added lines, -1052 removed lines patch added patch discarded remove patch
@@ -67,1056 +67,1056 @@
 block discarded – undo
67 67
  * - ChangePropagator: updates the mtime and etags of parent folders whenever a change to the cache is made to the cache by the updater
68 68
  */
69 69
 class Cache implements ICache {
70
-	use MoveFromCacheTrait {
71
-		MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
72
-	}
73
-
74
-	/**
75
-	 * @var array partial data for the cache
76
-	 */
77
-	protected $partial = [];
78
-
79
-	/**
80
-	 * @var string
81
-	 */
82
-	protected $storageId;
83
-
84
-	private $storage;
85
-
86
-	/**
87
-	 * @var Storage $storageCache
88
-	 */
89
-	protected $storageCache;
90
-
91
-	/** @var IMimeTypeLoader */
92
-	protected $mimetypeLoader;
93
-
94
-	/**
95
-	 * @var IDBConnection
96
-	 */
97
-	protected $connection;
98
-
99
-	/**
100
-	 * @var IEventDispatcher
101
-	 */
102
-	protected $eventDispatcher;
103
-
104
-	/** @var QuerySearchHelper */
105
-	protected $querySearchHelper;
106
-
107
-	/**
108
-	 * @param IStorage $storage
109
-	 */
110
-	public function __construct(IStorage $storage) {
111
-		$this->storageId = $storage->getId();
112
-		$this->storage = $storage;
113
-		if (strlen($this->storageId) > 64) {
114
-			$this->storageId = md5($this->storageId);
115
-		}
116
-
117
-		$this->storageCache = new Storage($storage);
118
-		$this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
119
-		$this->connection = \OC::$server->getDatabaseConnection();
120
-		$this->eventDispatcher = \OC::$server->get(IEventDispatcher::class);
121
-		$this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader);
122
-	}
123
-
124
-	protected function getQueryBuilder() {
125
-		return new CacheQueryBuilder(
126
-			$this->connection,
127
-			\OC::$server->getSystemConfig(),
128
-			\OC::$server->getLogger(),
129
-			$this
130
-		);
131
-	}
132
-
133
-	/**
134
-	 * Get the numeric storage id for this cache's storage
135
-	 *
136
-	 * @return int
137
-	 */
138
-	public function getNumericStorageId() {
139
-		return $this->storageCache->getNumericId();
140
-	}
141
-
142
-	/**
143
-	 * get the stored metadata of a file or folder
144
-	 *
145
-	 * @param string | int $file either the path of a file or folder or the file id for a file or folder
146
-	 * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
147
-	 */
148
-	public function get($file) {
149
-		$query = $this->getQueryBuilder();
150
-		$query->selectFileCache();
151
-
152
-		if (is_string($file) or $file == '') {
153
-			// normalize file
154
-			$file = $this->normalize($file);
155
-
156
-			$query->whereStorageId()
157
-				->wherePath($file);
158
-		} else { //file id
159
-			$query->whereFileId($file);
160
-		}
161
-
162
-		$result = $query->execute();
163
-		$data = $result->fetch();
164
-		$result->closeCursor();
165
-
166
-		//merge partial data
167
-		if (!$data and is_string($file) and isset($this->partial[$file])) {
168
-			return $this->partial[$file];
169
-		} elseif (!$data) {
170
-			return $data;
171
-		} else {
172
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
173
-		}
174
-	}
175
-
176
-	/**
177
-	 * Create a CacheEntry from database row
178
-	 *
179
-	 * @param array $data
180
-	 * @param IMimeTypeLoader $mimetypeLoader
181
-	 * @return CacheEntry
182
-	 */
183
-	public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
184
-		//fix types
185
-		$data['fileid'] = (int)$data['fileid'];
186
-		$data['parent'] = (int)$data['parent'];
187
-		$data['size'] = 0 + $data['size'];
188
-		$data['mtime'] = (int)$data['mtime'];
189
-		$data['storage_mtime'] = (int)$data['storage_mtime'];
190
-		$data['encryptedVersion'] = (int)$data['encrypted'];
191
-		$data['encrypted'] = (bool)$data['encrypted'];
192
-		$data['storage_id'] = $data['storage'];
193
-		$data['storage'] = (int)$data['storage'];
194
-		$data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
195
-		$data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
196
-		if ($data['storage_mtime'] == 0) {
197
-			$data['storage_mtime'] = $data['mtime'];
198
-		}
199
-		$data['permissions'] = (int)$data['permissions'];
200
-		if (isset($data['creation_time'])) {
201
-			$data['creation_time'] = (int) $data['creation_time'];
202
-		}
203
-		if (isset($data['upload_time'])) {
204
-			$data['upload_time'] = (int) $data['upload_time'];
205
-		}
206
-		return new CacheEntry($data);
207
-	}
208
-
209
-	/**
210
-	 * get the metadata of all files stored in $folder
211
-	 *
212
-	 * @param string $folder
213
-	 * @return ICacheEntry[]
214
-	 */
215
-	public function getFolderContents($folder) {
216
-		$fileId = $this->getId($folder);
217
-		return $this->getFolderContentsById($fileId);
218
-	}
219
-
220
-	/**
221
-	 * get the metadata of all files stored in $folder
222
-	 *
223
-	 * @param int $fileId the file id of the folder
224
-	 * @return ICacheEntry[]
225
-	 */
226
-	public function getFolderContentsById($fileId) {
227
-		if ($fileId > -1) {
228
-			$query = $this->getQueryBuilder();
229
-			$query->selectFileCache()
230
-				->whereParent($fileId)
231
-				->orderBy('name', 'ASC');
232
-
233
-			$result = $query->execute();
234
-			$files = $result->fetchAll();
235
-			$result->closeCursor();
236
-
237
-			return array_map(function (array $data) {
238
-				return self::cacheEntryFromData($data, $this->mimetypeLoader);
239
-			}, $files);
240
-		}
241
-		return [];
242
-	}
243
-
244
-	/**
245
-	 * insert or update meta data for a file or folder
246
-	 *
247
-	 * @param string $file
248
-	 * @param array $data
249
-	 *
250
-	 * @return int file id
251
-	 * @throws \RuntimeException
252
-	 */
253
-	public function put($file, array $data) {
254
-		if (($id = $this->getId($file)) > -1) {
255
-			$this->update($id, $data);
256
-			return $id;
257
-		} else {
258
-			return $this->insert($file, $data);
259
-		}
260
-	}
261
-
262
-	/**
263
-	 * insert meta data for a new file or folder
264
-	 *
265
-	 * @param string $file
266
-	 * @param array $data
267
-	 *
268
-	 * @return int file id
269
-	 * @throws \RuntimeException
270
-	 */
271
-	public function insert($file, array $data) {
272
-		// normalize file
273
-		$file = $this->normalize($file);
274
-
275
-		if (isset($this->partial[$file])) { //add any saved partial data
276
-			$data = array_merge($this->partial[$file], $data);
277
-			unset($this->partial[$file]);
278
-		}
279
-
280
-		$requiredFields = ['size', 'mtime', 'mimetype'];
281
-		foreach ($requiredFields as $field) {
282
-			if (!isset($data[$field])) { //data not complete save as partial and return
283
-				$this->partial[$file] = $data;
284
-				return -1;
285
-			}
286
-		}
287
-
288
-		$data['path'] = $file;
289
-		if (!isset($data['parent'])) {
290
-			$data['parent'] = $this->getParentId($file);
291
-		}
292
-		$data['name'] = basename($file);
293
-
294
-		[$values, $extensionValues] = $this->normalizeData($data);
295
-		$storageId = $this->getNumericStorageId();
296
-		$values['storage'] = $storageId;
297
-
298
-		try {
299
-			$builder = $this->connection->getQueryBuilder();
300
-			$builder->insert('filecache');
301
-
302
-			foreach ($values as $column => $value) {
303
-				$builder->setValue($column, $builder->createNamedParameter($value));
304
-			}
305
-
306
-			if ($builder->execute()) {
307
-				$fileId = $builder->getLastInsertId();
308
-
309
-				if (count($extensionValues)) {
310
-					$query = $this->getQueryBuilder();
311
-					$query->insert('filecache_extended');
312
-
313
-					$query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
314
-					foreach ($extensionValues as $column => $value) {
315
-						$query->setValue($column, $query->createNamedParameter($value));
316
-					}
317
-					$query->execute();
318
-				}
319
-
320
-				$event = new CacheEntryInsertedEvent($this->storage, $file, $fileId, $storageId);
321
-				$this->eventDispatcher->dispatch(CacheInsertEvent::class, $event);
322
-				$this->eventDispatcher->dispatchTyped($event);
323
-				return $fileId;
324
-			}
325
-		} catch (UniqueConstraintViolationException $e) {
326
-			// entry exists already
327
-			if ($this->connection->inTransaction()) {
328
-				$this->connection->commit();
329
-				$this->connection->beginTransaction();
330
-			}
331
-		}
332
-
333
-		// The file was created in the mean time
334
-		if (($id = $this->getId($file)) > -1) {
335
-			$this->update($id, $data);
336
-			return $id;
337
-		} else {
338
-			throw new \RuntimeException('File entry could not be inserted but could also not be selected with getId() in order to perform an update. Please try again.');
339
-		}
340
-	}
341
-
342
-	/**
343
-	 * update the metadata of an existing file or folder in the cache
344
-	 *
345
-	 * @param int $id the fileid of the existing file or folder
346
-	 * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
347
-	 */
348
-	public function update($id, array $data) {
349
-		if (isset($data['path'])) {
350
-			// normalize path
351
-			$data['path'] = $this->normalize($data['path']);
352
-		}
353
-
354
-		if (isset($data['name'])) {
355
-			// normalize path
356
-			$data['name'] = $this->normalize($data['name']);
357
-		}
358
-
359
-		[$values, $extensionValues] = $this->normalizeData($data);
360
-
361
-		if (count($values)) {
362
-			$query = $this->getQueryBuilder();
363
-
364
-			$query->update('filecache')
365
-				->whereFileId($id)
366
-				->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
367
-					return $query->expr()->orX(
368
-						$query->expr()->neq($key, $query->createNamedParameter($value)),
369
-						$query->expr()->isNull($key)
370
-					);
371
-				}, array_keys($values), array_values($values))));
372
-
373
-			foreach ($values as $key => $value) {
374
-				$query->set($key, $query->createNamedParameter($value));
375
-			}
376
-
377
-			$query->execute();
378
-		}
379
-
380
-		if (count($extensionValues)) {
381
-			try {
382
-				$query = $this->getQueryBuilder();
383
-				$query->insert('filecache_extended');
384
-
385
-				$query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT));
386
-				foreach ($extensionValues as $column => $value) {
387
-					$query->setValue($column, $query->createNamedParameter($value));
388
-				}
389
-
390
-				$query->execute();
391
-			} catch (UniqueConstraintViolationException $e) {
392
-				$query = $this->getQueryBuilder();
393
-				$query->update('filecache_extended')
394
-					->whereFileId($id)
395
-					->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
396
-						return $query->expr()->orX(
397
-							$query->expr()->neq($key, $query->createNamedParameter($value)),
398
-							$query->expr()->isNull($key)
399
-						);
400
-					}, array_keys($extensionValues), array_values($extensionValues))));
401
-
402
-				foreach ($extensionValues as $key => $value) {
403
-					$query->set($key, $query->createNamedParameter($value));
404
-				}
405
-
406
-				$query->execute();
407
-			}
408
-		}
409
-
410
-		$path = $this->getPathById($id);
411
-		// path can still be null if the file doesn't exist
412
-		if ($path !== null) {
413
-			$event = new CacheEntryUpdatedEvent($this->storage, $path, $id, $this->getNumericStorageId());
414
-			$this->eventDispatcher->dispatch(CacheUpdateEvent::class, $event);
415
-			$this->eventDispatcher->dispatchTyped($event);
416
-		}
417
-	}
418
-
419
-	/**
420
-	 * extract query parts and params array from data array
421
-	 *
422
-	 * @param array $data
423
-	 * @return array
424
-	 */
425
-	protected function normalizeData(array $data): array {
426
-		$fields = [
427
-			'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
428
-			'etag', 'permissions', 'checksum', 'storage'];
429
-		$extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
430
-
431
-		$doNotCopyStorageMTime = false;
432
-		if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
433
-			// this horrific magic tells it to not copy storage_mtime to mtime
434
-			unset($data['mtime']);
435
-			$doNotCopyStorageMTime = true;
436
-		}
437
-
438
-		$params = [];
439
-		$extensionParams = [];
440
-		foreach ($data as $name => $value) {
441
-			if (array_search($name, $fields) !== false) {
442
-				if ($name === 'path') {
443
-					$params['path_hash'] = md5($value);
444
-				} elseif ($name === 'mimetype') {
445
-					$params['mimepart'] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
446
-					$value = $this->mimetypeLoader->getId($value);
447
-				} elseif ($name === 'storage_mtime') {
448
-					if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
449
-						$params['mtime'] = $value;
450
-					}
451
-				} elseif ($name === 'encrypted') {
452
-					if (isset($data['encryptedVersion'])) {
453
-						$value = $data['encryptedVersion'];
454
-					} else {
455
-						// Boolean to integer conversion
456
-						$value = $value ? 1 : 0;
457
-					}
458
-				}
459
-				$params[$name] = $value;
460
-			}
461
-			if (array_search($name, $extensionFields) !== false) {
462
-				$extensionParams[$name] = $value;
463
-			}
464
-		}
465
-		return [$params, array_filter($extensionParams)];
466
-	}
467
-
468
-	/**
469
-	 * get the file id for a file
470
-	 *
471
-	 * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
472
-	 *
473
-	 * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
474
-	 *
475
-	 * @param string $file
476
-	 * @return int
477
-	 */
478
-	public function getId($file) {
479
-		// normalize file
480
-		$file = $this->normalize($file);
481
-
482
-		$query = $this->getQueryBuilder();
483
-		$query->select('fileid')
484
-			->from('filecache')
485
-			->whereStorageId()
486
-			->wherePath($file);
487
-
488
-		$result = $query->execute();
489
-		$id = $result->fetchOne();
490
-		$result->closeCursor();
491
-
492
-		return $id === false ? -1 : (int)$id;
493
-	}
494
-
495
-	/**
496
-	 * get the id of the parent folder of a file
497
-	 *
498
-	 * @param string $file
499
-	 * @return int
500
-	 */
501
-	public function getParentId($file) {
502
-		if ($file === '') {
503
-			return -1;
504
-		} else {
505
-			$parent = $this->getParentPath($file);
506
-			return (int)$this->getId($parent);
507
-		}
508
-	}
509
-
510
-	private function getParentPath($path) {
511
-		$parent = dirname($path);
512
-		if ($parent === '.') {
513
-			$parent = '';
514
-		}
515
-		return $parent;
516
-	}
517
-
518
-	/**
519
-	 * check if a file is available in the cache
520
-	 *
521
-	 * @param string $file
522
-	 * @return bool
523
-	 */
524
-	public function inCache($file) {
525
-		return $this->getId($file) != -1;
526
-	}
527
-
528
-	/**
529
-	 * remove a file or folder from the cache
530
-	 *
531
-	 * when removing a folder from the cache all files and folders inside the folder will be removed as well
532
-	 *
533
-	 * @param string $file
534
-	 */
535
-	public function remove($file) {
536
-		$entry = $this->get($file);
537
-
538
-		if ($entry) {
539
-			$query = $this->getQueryBuilder();
540
-			$query->delete('filecache')
541
-				->whereFileId($entry->getId());
542
-			$query->execute();
543
-
544
-			$query = $this->getQueryBuilder();
545
-			$query->delete('filecache_extended')
546
-				->whereFileId($entry->getId());
547
-			$query->execute();
548
-
549
-			if ($entry->getMimeType() == FileInfo::MIMETYPE_FOLDER) {
550
-				$this->removeChildren($entry);
551
-			}
552
-
553
-			$this->eventDispatcher->dispatchTyped(new CacheEntryRemovedEvent($this->storage, $entry->getPath(), $entry->getId(), $this->getNumericStorageId()));
554
-		}
555
-	}
556
-
557
-	/**
558
-	 * Get all sub folders of a folder
559
-	 *
560
-	 * @param ICacheEntry $entry the cache entry of the folder to get the subfolders for
561
-	 * @return ICacheEntry[] the cache entries for the subfolders
562
-	 */
563
-	private function getSubFolders(ICacheEntry $entry) {
564
-		$children = $this->getFolderContentsById($entry->getId());
565
-		return array_filter($children, function ($child) {
566
-			return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
567
-		});
568
-	}
569
-
570
-	/**
571
-	 * Recursively remove all children of a folder
572
-	 *
573
-	 * @param ICacheEntry $entry the cache entry of the folder to remove the children of
574
-	 * @throws \OC\DatabaseException
575
-	 */
576
-	private function removeChildren(ICacheEntry $entry) {
577
-		$parentIds = [$entry->getId()];
578
-		$queue = [$entry->getId()];
579
-
580
-		// we walk depth first trough the file tree, removing all filecache_extended attributes while we walk
581
-		// and collecting all folder ids to later use to delete the filecache entries
582
-		while ($entryId = array_pop($queue)) {
583
-			$children = $this->getFolderContentsById($entryId);
584
-			$childIds = array_map(function (ICacheEntry $cacheEntry) {
585
-				return $cacheEntry->getId();
586
-			}, $children);
587
-
588
-			$query = $this->getQueryBuilder();
589
-			$query->delete('filecache_extended')
590
-				->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY)));
591
-			$query->execute();
592
-
593
-			/** @var ICacheEntry[] $childFolders */
594
-			$childFolders = array_filter($children, function ($child) {
595
-				return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
596
-			});
597
-			foreach ($childFolders as $folder) {
598
-				$parentIds[] = $folder->getId();
599
-				$queue[] = $folder->getId();
600
-			}
601
-		}
602
-
603
-		$query = $this->getQueryBuilder();
604
-		$query->delete('filecache')
605
-			->whereParentIn($parentIds);
606
-		$query->execute();
607
-	}
608
-
609
-	/**
610
-	 * Move a file or folder in the cache
611
-	 *
612
-	 * @param string $source
613
-	 * @param string $target
614
-	 */
615
-	public function move($source, $target) {
616
-		$this->moveFromCache($this, $source, $target);
617
-	}
618
-
619
-	/**
620
-	 * Get the storage id and path needed for a move
621
-	 *
622
-	 * @param string $path
623
-	 * @return array [$storageId, $internalPath]
624
-	 */
625
-	protected function getMoveInfo($path) {
626
-		return [$this->getNumericStorageId(), $path];
627
-	}
628
-
629
-	/**
630
-	 * Move a file or folder in the cache
631
-	 *
632
-	 * @param ICache $sourceCache
633
-	 * @param string $sourcePath
634
-	 * @param string $targetPath
635
-	 * @throws \OC\DatabaseException
636
-	 * @throws \Exception if the given storages have an invalid id
637
-	 */
638
-	public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
639
-		if ($sourceCache instanceof Cache) {
640
-			// normalize source and target
641
-			$sourcePath = $this->normalize($sourcePath);
642
-			$targetPath = $this->normalize($targetPath);
643
-
644
-			$sourceData = $sourceCache->get($sourcePath);
645
-			$sourceId = $sourceData['fileid'];
646
-			$newParentId = $this->getParentId($targetPath);
647
-
648
-			[$sourceStorageId, $sourcePath] = $sourceCache->getMoveInfo($sourcePath);
649
-			[$targetStorageId, $targetPath] = $this->getMoveInfo($targetPath);
650
-
651
-			if (is_null($sourceStorageId) || $sourceStorageId === false) {
652
-				throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
653
-			}
654
-			if (is_null($targetStorageId) || $targetStorageId === false) {
655
-				throw new \Exception('Invalid target storage id: ' . $targetStorageId);
656
-			}
657
-
658
-			$this->connection->beginTransaction();
659
-			if ($sourceData['mimetype'] === 'httpd/unix-directory') {
660
-				//update all child entries
661
-				$sourceLength = mb_strlen($sourcePath);
662
-				$query = $this->connection->getQueryBuilder();
663
-
664
-				$fun = $query->func();
665
-				$newPathFunction = $fun->concat(
666
-					$query->createNamedParameter($targetPath),
667
-					$fun->substring('path', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash
668
-				);
669
-				$query->update('filecache')
670
-					->set('storage', $query->createNamedParameter($targetStorageId, IQueryBuilder::PARAM_INT))
671
-					->set('path_hash', $fun->md5($newPathFunction))
672
-					->set('path', $newPathFunction)
673
-					->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
674
-					->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
675
-
676
-				try {
677
-					$query->execute();
678
-				} catch (\OC\DatabaseException $e) {
679
-					$this->connection->rollBack();
680
-					throw $e;
681
-				}
682
-			}
683
-
684
-			$query = $this->getQueryBuilder();
685
-			$query->update('filecache')
686
-				->set('storage', $query->createNamedParameter($targetStorageId))
687
-				->set('path', $query->createNamedParameter($targetPath))
688
-				->set('path_hash', $query->createNamedParameter(md5($targetPath)))
689
-				->set('name', $query->createNamedParameter(basename($targetPath)))
690
-				->set('parent', $query->createNamedParameter($newParentId, IQueryBuilder::PARAM_INT))
691
-				->whereFileId($sourceId);
692
-			$query->execute();
693
-
694
-			$this->connection->commit();
695
-
696
-			if ($sourceCache->getNumericStorageId() !== $this->getNumericStorageId()) {
697
-				$this->eventDispatcher->dispatchTyped(new CacheEntryRemovedEvent($this->storage, $sourcePath, $sourceId, $sourceCache->getNumericStorageId()));
698
-				$event = new CacheEntryInsertedEvent($this->storage, $targetPath, $sourceId, $this->getNumericStorageId());
699
-				$this->eventDispatcher->dispatch(CacheInsertEvent::class, $event);
700
-				$this->eventDispatcher->dispatchTyped($event);
701
-			} else {
702
-				$event = new CacheEntryUpdatedEvent($this->storage, $targetPath, $sourceId, $this->getNumericStorageId());
703
-				$this->eventDispatcher->dispatch(CacheUpdateEvent::class, $event);
704
-				$this->eventDispatcher->dispatchTyped($event);
705
-			}
706
-		} else {
707
-			$this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
708
-		}
709
-	}
710
-
711
-	/**
712
-	 * remove all entries for files that are stored on the storage from the cache
713
-	 */
714
-	public function clear() {
715
-		$query = $this->getQueryBuilder();
716
-		$query->delete('filecache')
717
-			->whereStorageId();
718
-		$query->execute();
719
-
720
-		$query = $this->connection->getQueryBuilder();
721
-		$query->delete('storages')
722
-			->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId)));
723
-		$query->execute();
724
-	}
725
-
726
-	/**
727
-	 * Get the scan status of a file
728
-	 *
729
-	 * - Cache::NOT_FOUND: File is not in the cache
730
-	 * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
731
-	 * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
732
-	 * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
733
-	 *
734
-	 * @param string $file
735
-	 *
736
-	 * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
737
-	 */
738
-	public function getStatus($file) {
739
-		// normalize file
740
-		$file = $this->normalize($file);
741
-
742
-		$query = $this->getQueryBuilder();
743
-		$query->select('size')
744
-			->from('filecache')
745
-			->whereStorageId()
746
-			->wherePath($file);
747
-
748
-		$result = $query->execute();
749
-		$size = $result->fetchOne();
750
-		$result->closeCursor();
751
-
752
-		if ($size !== false) {
753
-			if ((int)$size === -1) {
754
-				return self::SHALLOW;
755
-			} else {
756
-				return self::COMPLETE;
757
-			}
758
-		} else {
759
-			if (isset($this->partial[$file])) {
760
-				return self::PARTIAL;
761
-			} else {
762
-				return self::NOT_FOUND;
763
-			}
764
-		}
765
-	}
766
-
767
-	/**
768
-	 * search for files matching $pattern
769
-	 *
770
-	 * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
771
-	 * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
772
-	 */
773
-	public function search($pattern) {
774
-		// normalize pattern
775
-		$pattern = $this->normalize($pattern);
776
-
777
-		if ($pattern === '%%') {
778
-			return [];
779
-		}
780
-
781
-		$query = $this->getQueryBuilder();
782
-		$query->selectFileCache()
783
-			->whereStorageId()
784
-			->andWhere($query->expr()->iLike('name', $query->createNamedParameter($pattern)));
785
-
786
-		$result = $query->execute();
787
-		$files = $result->fetchAll();
788
-		$result->closeCursor();
789
-
790
-		return array_map(function (array $data) {
791
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
792
-		}, $files);
793
-	}
794
-
795
-	/**
796
-	 * @param IResult $result
797
-	 * @return CacheEntry[]
798
-	 */
799
-	private function searchResultToCacheEntries(IResult $result): array {
800
-		$files = $result->fetchAll();
801
-
802
-		return array_map(function (array $data) {
803
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
804
-		}, $files);
805
-	}
806
-
807
-	/**
808
-	 * search for files by mimetype
809
-	 *
810
-	 * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
811
-	 *        where it will search for all mimetypes in the group ('image/*')
812
-	 * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
813
-	 */
814
-	public function searchByMime($mimetype) {
815
-		$mimeId = $this->mimetypeLoader->getId($mimetype);
816
-
817
-		$query = $this->getQueryBuilder();
818
-		$query->selectFileCache()
819
-			->whereStorageId();
820
-
821
-		if (strpos($mimetype, '/')) {
822
-			$query->andWhere($query->expr()->eq('mimetype', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
823
-		} else {
824
-			$query->andWhere($query->expr()->eq('mimepart', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
825
-		}
826
-
827
-		$result = $query->execute();
828
-		$files = $result->fetchAll();
829
-		$result->closeCursor();
830
-
831
-		return array_map(function (array $data) {
832
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
833
-		}, $files);
834
-	}
835
-
836
-	public function searchQuery(ISearchQuery $searchQuery) {
837
-		$builder = $this->getQueryBuilder();
838
-
839
-		$query = $builder->selectFileCache('file');
840
-
841
-		$query->whereStorageId();
842
-
843
-		if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) {
844
-			$query
845
-				->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
846
-				->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
847
-					$builder->expr()->eq('tagmap.type', 'tag.type'),
848
-					$builder->expr()->eq('tagmap.categoryid', 'tag.id')
849
-				))
850
-				->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
851
-				->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($searchQuery->getUser()->getUID())));
852
-		}
853
-
854
-		$searchExpr = $this->querySearchHelper->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation());
855
-		if ($searchExpr) {
856
-			$query->andWhere($searchExpr);
857
-		}
858
-
859
-		if ($searchQuery->limitToHome() && ($this instanceof HomeCache)) {
860
-			$query->andWhere($builder->expr()->like('path', $query->expr()->literal('files/%')));
861
-		}
862
-
863
-		$this->querySearchHelper->addSearchOrdersToQuery($query, $searchQuery->getOrder());
864
-
865
-		if ($searchQuery->getLimit()) {
866
-			$query->setMaxResults($searchQuery->getLimit());
867
-		}
868
-		if ($searchQuery->getOffset()) {
869
-			$query->setFirstResult($searchQuery->getOffset());
870
-		}
871
-
872
-		$result = $query->execute();
873
-		$cacheEntries = $this->searchResultToCacheEntries($result);
874
-		$result->closeCursor();
875
-		return $cacheEntries;
876
-	}
877
-
878
-	/**
879
-	 * Re-calculate the folder size and the size of all parent folders
880
-	 *
881
-	 * @param string|boolean $path
882
-	 * @param array $data (optional) meta data of the folder
883
-	 */
884
-	public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
885
-		$this->calculateFolderSize($path, $data);
886
-		if ($path !== '') {
887
-			$parent = dirname($path);
888
-			if ($parent === '.' or $parent === '/') {
889
-				$parent = '';
890
-			}
891
-			if ($isBackgroundScan) {
892
-				$parentData = $this->get($parent);
893
-				if ($parentData['size'] !== -1 && $this->getIncompleteChildrenCount($parentData['fileid']) === 0) {
894
-					$this->correctFolderSize($parent, $parentData, $isBackgroundScan);
895
-				}
896
-			} else {
897
-				$this->correctFolderSize($parent);
898
-			}
899
-		}
900
-	}
901
-
902
-	/**
903
-	 * get the incomplete count that shares parent $folder
904
-	 *
905
-	 * @param int $fileId the file id of the folder
906
-	 * @return int
907
-	 */
908
-	public function getIncompleteChildrenCount($fileId) {
909
-		if ($fileId > -1) {
910
-			$query = $this->getQueryBuilder();
911
-			$query->select($query->func()->count())
912
-				->from('filecache')
913
-				->whereParent($fileId)
914
-				->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
915
-
916
-			$result = $query->execute();
917
-			$size = (int)$result->fetchOne();
918
-			$result->closeCursor();
919
-
920
-			return $size;
921
-		}
922
-		return -1;
923
-	}
924
-
925
-	/**
926
-	 * calculate the size of a folder and set it in the cache
927
-	 *
928
-	 * @param string $path
929
-	 * @param array $entry (optional) meta data of the folder
930
-	 * @return int
931
-	 */
932
-	public function calculateFolderSize($path, $entry = null) {
933
-		$totalSize = 0;
934
-		if (is_null($entry) or !isset($entry['fileid'])) {
935
-			$entry = $this->get($path);
936
-		}
937
-		if (isset($entry['mimetype']) && $entry['mimetype'] === FileInfo::MIMETYPE_FOLDER) {
938
-			$id = $entry['fileid'];
939
-
940
-			$query = $this->getQueryBuilder();
941
-			$query->selectAlias($query->func()->sum('size'), 'f1')
942
-				->selectAlias($query->func()->min('size'), 'f2')
943
-				->from('filecache')
944
-				->whereStorageId()
945
-				->whereParent($id);
946
-
947
-			$result = $query->execute();
948
-			$row = $result->fetch();
949
-			$result->closeCursor();
950
-
951
-			if ($row) {
952
-				[$sum, $min] = array_values($row);
953
-				$sum = 0 + $sum;
954
-				$min = 0 + $min;
955
-				if ($min === -1) {
956
-					$totalSize = $min;
957
-				} else {
958
-					$totalSize = $sum;
959
-				}
960
-				if ($entry['size'] !== $totalSize) {
961
-					$this->update($id, ['size' => $totalSize]);
962
-				}
963
-			}
964
-		}
965
-		return $totalSize;
966
-	}
967
-
968
-	/**
969
-	 * get all file ids on the files on the storage
970
-	 *
971
-	 * @return int[]
972
-	 */
973
-	public function getAll() {
974
-		$query = $this->getQueryBuilder();
975
-		$query->select('fileid')
976
-			->from('filecache')
977
-			->whereStorageId();
978
-
979
-		$result = $query->execute();
980
-		$files = $result->fetchAll(\PDO::FETCH_COLUMN);
981
-		$result->closeCursor();
982
-
983
-		return array_map(function ($id) {
984
-			return (int)$id;
985
-		}, $files);
986
-	}
987
-
988
-	/**
989
-	 * find a folder in the cache which has not been fully scanned
990
-	 *
991
-	 * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
992
-	 * use the one with the highest id gives the best result with the background scanner, since that is most
993
-	 * likely the folder where we stopped scanning previously
994
-	 *
995
-	 * @return string|bool the path of the folder or false when no folder matched
996
-	 */
997
-	public function getIncomplete() {
998
-		$query = $this->getQueryBuilder();
999
-		$query->select('path')
1000
-			->from('filecache')
1001
-			->whereStorageId()
1002
-			->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
1003
-			->orderBy('fileid', 'DESC')
1004
-			->setMaxResults(1);
1005
-
1006
-		$result = $query->execute();
1007
-		$path = $result->fetchOne();
1008
-		$result->closeCursor();
1009
-
1010
-		return $path;
1011
-	}
1012
-
1013
-	/**
1014
-	 * get the path of a file on this storage by it's file id
1015
-	 *
1016
-	 * @param int $id the file id of the file or folder to search
1017
-	 * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
1018
-	 */
1019
-	public function getPathById($id) {
1020
-		$query = $this->getQueryBuilder();
1021
-		$query->select('path')
1022
-			->from('filecache')
1023
-			->whereStorageId()
1024
-			->whereFileId($id);
1025
-
1026
-		$result = $query->execute();
1027
-		$path = $result->fetchOne();
1028
-		$result->closeCursor();
1029
-
1030
-		if ($path === false) {
1031
-			return null;
1032
-		}
1033
-
1034
-		return (string) $path;
1035
-	}
1036
-
1037
-	/**
1038
-	 * get the storage id of the storage for a file and the internal path of the file
1039
-	 * unlike getPathById this does not limit the search to files on this storage and
1040
-	 * instead does a global search in the cache table
1041
-	 *
1042
-	 * @param int $id
1043
-	 * @return array first element holding the storage id, second the path
1044
-	 * @deprecated use getPathById() instead
1045
-	 */
1046
-	public static function getById($id) {
1047
-		$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
1048
-		$query->select('path', 'storage')
1049
-			->from('filecache')
1050
-			->where($query->expr()->eq('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
1051
-
1052
-		$result = $query->execute();
1053
-		$row = $result->fetch();
1054
-		$result->closeCursor();
1055
-
1056
-		if ($row) {
1057
-			$numericId = $row['storage'];
1058
-			$path = $row['path'];
1059
-		} else {
1060
-			return null;
1061
-		}
1062
-
1063
-		if ($id = Storage::getStorageId($numericId)) {
1064
-			return [$id, $path];
1065
-		} else {
1066
-			return null;
1067
-		}
1068
-	}
1069
-
1070
-	/**
1071
-	 * normalize the given path
1072
-	 *
1073
-	 * @param string $path
1074
-	 * @return string
1075
-	 */
1076
-	public function normalize($path) {
1077
-		return trim(\OC_Util::normalizeUnicode($path), '/');
1078
-	}
1079
-
1080
-	/**
1081
-	 * Copy a file or folder in the cache
1082
-	 *
1083
-	 * @param ICache $sourceCache
1084
-	 * @param ICacheEntry $sourceEntry
1085
-	 * @param string $targetPath
1086
-	 * @return int fileid of copied entry
1087
-	 */
1088
-	public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int {
1089
-		if ($sourceEntry->getId() < 0) {
1090
-			throw new \RuntimeException("Invalid source cache entry on copyFromCache");
1091
-		}
1092
-		$data = $this->cacheEntryToArray($sourceEntry);
1093
-		$fileId = $this->put($targetPath, $data);
1094
-		if ($fileId <= 0) {
1095
-			throw new \RuntimeException("Failed to copy to " . $targetPath . " from cache with source data " . json_encode($data) . " ");
1096
-		}
1097
-		if ($sourceEntry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
1098
-			$folderContent = $sourceCache->getFolderContentsById($sourceEntry->getId());
1099
-			foreach ($folderContent as $subEntry) {
1100
-				$subTargetPath = $targetPath . '/' . $subEntry->getName();
1101
-				$this->copyFromCache($sourceCache, $subEntry, $subTargetPath);
1102
-			}
1103
-		}
1104
-		return $fileId;
1105
-	}
1106
-
1107
-	private function cacheEntryToArray(ICacheEntry $entry): array {
1108
-		return [
1109
-			'size' => $entry->getSize(),
1110
-			'mtime' => $entry->getMTime(),
1111
-			'storage_mtime' => $entry->getStorageMTime(),
1112
-			'mimetype' => $entry->getMimeType(),
1113
-			'mimepart' => $entry->getMimePart(),
1114
-			'etag' => $entry->getEtag(),
1115
-			'permissions' => $entry->getPermissions(),
1116
-			'encrypted' => $entry->isEncrypted(),
1117
-			'creation_time' => $entry->getCreationTime(),
1118
-			'upload_time' => $entry->getUploadTime(),
1119
-			'metadata_etag' => $entry->getMetadataEtag(),
1120
-		];
1121
-	}
70
+    use MoveFromCacheTrait {
71
+        MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
72
+    }
73
+
74
+    /**
75
+     * @var array partial data for the cache
76
+     */
77
+    protected $partial = [];
78
+
79
+    /**
80
+     * @var string
81
+     */
82
+    protected $storageId;
83
+
84
+    private $storage;
85
+
86
+    /**
87
+     * @var Storage $storageCache
88
+     */
89
+    protected $storageCache;
90
+
91
+    /** @var IMimeTypeLoader */
92
+    protected $mimetypeLoader;
93
+
94
+    /**
95
+     * @var IDBConnection
96
+     */
97
+    protected $connection;
98
+
99
+    /**
100
+     * @var IEventDispatcher
101
+     */
102
+    protected $eventDispatcher;
103
+
104
+    /** @var QuerySearchHelper */
105
+    protected $querySearchHelper;
106
+
107
+    /**
108
+     * @param IStorage $storage
109
+     */
110
+    public function __construct(IStorage $storage) {
111
+        $this->storageId = $storage->getId();
112
+        $this->storage = $storage;
113
+        if (strlen($this->storageId) > 64) {
114
+            $this->storageId = md5($this->storageId);
115
+        }
116
+
117
+        $this->storageCache = new Storage($storage);
118
+        $this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
119
+        $this->connection = \OC::$server->getDatabaseConnection();
120
+        $this->eventDispatcher = \OC::$server->get(IEventDispatcher::class);
121
+        $this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader);
122
+    }
123
+
124
+    protected function getQueryBuilder() {
125
+        return new CacheQueryBuilder(
126
+            $this->connection,
127
+            \OC::$server->getSystemConfig(),
128
+            \OC::$server->getLogger(),
129
+            $this
130
+        );
131
+    }
132
+
133
+    /**
134
+     * Get the numeric storage id for this cache's storage
135
+     *
136
+     * @return int
137
+     */
138
+    public function getNumericStorageId() {
139
+        return $this->storageCache->getNumericId();
140
+    }
141
+
142
+    /**
143
+     * get the stored metadata of a file or folder
144
+     *
145
+     * @param string | int $file either the path of a file or folder or the file id for a file or folder
146
+     * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
147
+     */
148
+    public function get($file) {
149
+        $query = $this->getQueryBuilder();
150
+        $query->selectFileCache();
151
+
152
+        if (is_string($file) or $file == '') {
153
+            // normalize file
154
+            $file = $this->normalize($file);
155
+
156
+            $query->whereStorageId()
157
+                ->wherePath($file);
158
+        } else { //file id
159
+            $query->whereFileId($file);
160
+        }
161
+
162
+        $result = $query->execute();
163
+        $data = $result->fetch();
164
+        $result->closeCursor();
165
+
166
+        //merge partial data
167
+        if (!$data and is_string($file) and isset($this->partial[$file])) {
168
+            return $this->partial[$file];
169
+        } elseif (!$data) {
170
+            return $data;
171
+        } else {
172
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
173
+        }
174
+    }
175
+
176
+    /**
177
+     * Create a CacheEntry from database row
178
+     *
179
+     * @param array $data
180
+     * @param IMimeTypeLoader $mimetypeLoader
181
+     * @return CacheEntry
182
+     */
183
+    public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
184
+        //fix types
185
+        $data['fileid'] = (int)$data['fileid'];
186
+        $data['parent'] = (int)$data['parent'];
187
+        $data['size'] = 0 + $data['size'];
188
+        $data['mtime'] = (int)$data['mtime'];
189
+        $data['storage_mtime'] = (int)$data['storage_mtime'];
190
+        $data['encryptedVersion'] = (int)$data['encrypted'];
191
+        $data['encrypted'] = (bool)$data['encrypted'];
192
+        $data['storage_id'] = $data['storage'];
193
+        $data['storage'] = (int)$data['storage'];
194
+        $data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
195
+        $data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
196
+        if ($data['storage_mtime'] == 0) {
197
+            $data['storage_mtime'] = $data['mtime'];
198
+        }
199
+        $data['permissions'] = (int)$data['permissions'];
200
+        if (isset($data['creation_time'])) {
201
+            $data['creation_time'] = (int) $data['creation_time'];
202
+        }
203
+        if (isset($data['upload_time'])) {
204
+            $data['upload_time'] = (int) $data['upload_time'];
205
+        }
206
+        return new CacheEntry($data);
207
+    }
208
+
209
+    /**
210
+     * get the metadata of all files stored in $folder
211
+     *
212
+     * @param string $folder
213
+     * @return ICacheEntry[]
214
+     */
215
+    public function getFolderContents($folder) {
216
+        $fileId = $this->getId($folder);
217
+        return $this->getFolderContentsById($fileId);
218
+    }
219
+
220
+    /**
221
+     * get the metadata of all files stored in $folder
222
+     *
223
+     * @param int $fileId the file id of the folder
224
+     * @return ICacheEntry[]
225
+     */
226
+    public function getFolderContentsById($fileId) {
227
+        if ($fileId > -1) {
228
+            $query = $this->getQueryBuilder();
229
+            $query->selectFileCache()
230
+                ->whereParent($fileId)
231
+                ->orderBy('name', 'ASC');
232
+
233
+            $result = $query->execute();
234
+            $files = $result->fetchAll();
235
+            $result->closeCursor();
236
+
237
+            return array_map(function (array $data) {
238
+                return self::cacheEntryFromData($data, $this->mimetypeLoader);
239
+            }, $files);
240
+        }
241
+        return [];
242
+    }
243
+
244
+    /**
245
+     * insert or update meta data for a file or folder
246
+     *
247
+     * @param string $file
248
+     * @param array $data
249
+     *
250
+     * @return int file id
251
+     * @throws \RuntimeException
252
+     */
253
+    public function put($file, array $data) {
254
+        if (($id = $this->getId($file)) > -1) {
255
+            $this->update($id, $data);
256
+            return $id;
257
+        } else {
258
+            return $this->insert($file, $data);
259
+        }
260
+    }
261
+
262
+    /**
263
+     * insert meta data for a new file or folder
264
+     *
265
+     * @param string $file
266
+     * @param array $data
267
+     *
268
+     * @return int file id
269
+     * @throws \RuntimeException
270
+     */
271
+    public function insert($file, array $data) {
272
+        // normalize file
273
+        $file = $this->normalize($file);
274
+
275
+        if (isset($this->partial[$file])) { //add any saved partial data
276
+            $data = array_merge($this->partial[$file], $data);
277
+            unset($this->partial[$file]);
278
+        }
279
+
280
+        $requiredFields = ['size', 'mtime', 'mimetype'];
281
+        foreach ($requiredFields as $field) {
282
+            if (!isset($data[$field])) { //data not complete save as partial and return
283
+                $this->partial[$file] = $data;
284
+                return -1;
285
+            }
286
+        }
287
+
288
+        $data['path'] = $file;
289
+        if (!isset($data['parent'])) {
290
+            $data['parent'] = $this->getParentId($file);
291
+        }
292
+        $data['name'] = basename($file);
293
+
294
+        [$values, $extensionValues] = $this->normalizeData($data);
295
+        $storageId = $this->getNumericStorageId();
296
+        $values['storage'] = $storageId;
297
+
298
+        try {
299
+            $builder = $this->connection->getQueryBuilder();
300
+            $builder->insert('filecache');
301
+
302
+            foreach ($values as $column => $value) {
303
+                $builder->setValue($column, $builder->createNamedParameter($value));
304
+            }
305
+
306
+            if ($builder->execute()) {
307
+                $fileId = $builder->getLastInsertId();
308
+
309
+                if (count($extensionValues)) {
310
+                    $query = $this->getQueryBuilder();
311
+                    $query->insert('filecache_extended');
312
+
313
+                    $query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
314
+                    foreach ($extensionValues as $column => $value) {
315
+                        $query->setValue($column, $query->createNamedParameter($value));
316
+                    }
317
+                    $query->execute();
318
+                }
319
+
320
+                $event = new CacheEntryInsertedEvent($this->storage, $file, $fileId, $storageId);
321
+                $this->eventDispatcher->dispatch(CacheInsertEvent::class, $event);
322
+                $this->eventDispatcher->dispatchTyped($event);
323
+                return $fileId;
324
+            }
325
+        } catch (UniqueConstraintViolationException $e) {
326
+            // entry exists already
327
+            if ($this->connection->inTransaction()) {
328
+                $this->connection->commit();
329
+                $this->connection->beginTransaction();
330
+            }
331
+        }
332
+
333
+        // The file was created in the mean time
334
+        if (($id = $this->getId($file)) > -1) {
335
+            $this->update($id, $data);
336
+            return $id;
337
+        } else {
338
+            throw new \RuntimeException('File entry could not be inserted but could also not be selected with getId() in order to perform an update. Please try again.');
339
+        }
340
+    }
341
+
342
+    /**
343
+     * update the metadata of an existing file or folder in the cache
344
+     *
345
+     * @param int $id the fileid of the existing file or folder
346
+     * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
347
+     */
348
+    public function update($id, array $data) {
349
+        if (isset($data['path'])) {
350
+            // normalize path
351
+            $data['path'] = $this->normalize($data['path']);
352
+        }
353
+
354
+        if (isset($data['name'])) {
355
+            // normalize path
356
+            $data['name'] = $this->normalize($data['name']);
357
+        }
358
+
359
+        [$values, $extensionValues] = $this->normalizeData($data);
360
+
361
+        if (count($values)) {
362
+            $query = $this->getQueryBuilder();
363
+
364
+            $query->update('filecache')
365
+                ->whereFileId($id)
366
+                ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
367
+                    return $query->expr()->orX(
368
+                        $query->expr()->neq($key, $query->createNamedParameter($value)),
369
+                        $query->expr()->isNull($key)
370
+                    );
371
+                }, array_keys($values), array_values($values))));
372
+
373
+            foreach ($values as $key => $value) {
374
+                $query->set($key, $query->createNamedParameter($value));
375
+            }
376
+
377
+            $query->execute();
378
+        }
379
+
380
+        if (count($extensionValues)) {
381
+            try {
382
+                $query = $this->getQueryBuilder();
383
+                $query->insert('filecache_extended');
384
+
385
+                $query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT));
386
+                foreach ($extensionValues as $column => $value) {
387
+                    $query->setValue($column, $query->createNamedParameter($value));
388
+                }
389
+
390
+                $query->execute();
391
+            } catch (UniqueConstraintViolationException $e) {
392
+                $query = $this->getQueryBuilder();
393
+                $query->update('filecache_extended')
394
+                    ->whereFileId($id)
395
+                    ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
396
+                        return $query->expr()->orX(
397
+                            $query->expr()->neq($key, $query->createNamedParameter($value)),
398
+                            $query->expr()->isNull($key)
399
+                        );
400
+                    }, array_keys($extensionValues), array_values($extensionValues))));
401
+
402
+                foreach ($extensionValues as $key => $value) {
403
+                    $query->set($key, $query->createNamedParameter($value));
404
+                }
405
+
406
+                $query->execute();
407
+            }
408
+        }
409
+
410
+        $path = $this->getPathById($id);
411
+        // path can still be null if the file doesn't exist
412
+        if ($path !== null) {
413
+            $event = new CacheEntryUpdatedEvent($this->storage, $path, $id, $this->getNumericStorageId());
414
+            $this->eventDispatcher->dispatch(CacheUpdateEvent::class, $event);
415
+            $this->eventDispatcher->dispatchTyped($event);
416
+        }
417
+    }
418
+
419
+    /**
420
+     * extract query parts and params array from data array
421
+     *
422
+     * @param array $data
423
+     * @return array
424
+     */
425
+    protected function normalizeData(array $data): array {
426
+        $fields = [
427
+            'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
428
+            'etag', 'permissions', 'checksum', 'storage'];
429
+        $extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
430
+
431
+        $doNotCopyStorageMTime = false;
432
+        if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
433
+            // this horrific magic tells it to not copy storage_mtime to mtime
434
+            unset($data['mtime']);
435
+            $doNotCopyStorageMTime = true;
436
+        }
437
+
438
+        $params = [];
439
+        $extensionParams = [];
440
+        foreach ($data as $name => $value) {
441
+            if (array_search($name, $fields) !== false) {
442
+                if ($name === 'path') {
443
+                    $params['path_hash'] = md5($value);
444
+                } elseif ($name === 'mimetype') {
445
+                    $params['mimepart'] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
446
+                    $value = $this->mimetypeLoader->getId($value);
447
+                } elseif ($name === 'storage_mtime') {
448
+                    if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
449
+                        $params['mtime'] = $value;
450
+                    }
451
+                } elseif ($name === 'encrypted') {
452
+                    if (isset($data['encryptedVersion'])) {
453
+                        $value = $data['encryptedVersion'];
454
+                    } else {
455
+                        // Boolean to integer conversion
456
+                        $value = $value ? 1 : 0;
457
+                    }
458
+                }
459
+                $params[$name] = $value;
460
+            }
461
+            if (array_search($name, $extensionFields) !== false) {
462
+                $extensionParams[$name] = $value;
463
+            }
464
+        }
465
+        return [$params, array_filter($extensionParams)];
466
+    }
467
+
468
+    /**
469
+     * get the file id for a file
470
+     *
471
+     * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
472
+     *
473
+     * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
474
+     *
475
+     * @param string $file
476
+     * @return int
477
+     */
478
+    public function getId($file) {
479
+        // normalize file
480
+        $file = $this->normalize($file);
481
+
482
+        $query = $this->getQueryBuilder();
483
+        $query->select('fileid')
484
+            ->from('filecache')
485
+            ->whereStorageId()
486
+            ->wherePath($file);
487
+
488
+        $result = $query->execute();
489
+        $id = $result->fetchOne();
490
+        $result->closeCursor();
491
+
492
+        return $id === false ? -1 : (int)$id;
493
+    }
494
+
495
+    /**
496
+     * get the id of the parent folder of a file
497
+     *
498
+     * @param string $file
499
+     * @return int
500
+     */
501
+    public function getParentId($file) {
502
+        if ($file === '') {
503
+            return -1;
504
+        } else {
505
+            $parent = $this->getParentPath($file);
506
+            return (int)$this->getId($parent);
507
+        }
508
+    }
509
+
510
+    private function getParentPath($path) {
511
+        $parent = dirname($path);
512
+        if ($parent === '.') {
513
+            $parent = '';
514
+        }
515
+        return $parent;
516
+    }
517
+
518
+    /**
519
+     * check if a file is available in the cache
520
+     *
521
+     * @param string $file
522
+     * @return bool
523
+     */
524
+    public function inCache($file) {
525
+        return $this->getId($file) != -1;
526
+    }
527
+
528
+    /**
529
+     * remove a file or folder from the cache
530
+     *
531
+     * when removing a folder from the cache all files and folders inside the folder will be removed as well
532
+     *
533
+     * @param string $file
534
+     */
535
+    public function remove($file) {
536
+        $entry = $this->get($file);
537
+
538
+        if ($entry) {
539
+            $query = $this->getQueryBuilder();
540
+            $query->delete('filecache')
541
+                ->whereFileId($entry->getId());
542
+            $query->execute();
543
+
544
+            $query = $this->getQueryBuilder();
545
+            $query->delete('filecache_extended')
546
+                ->whereFileId($entry->getId());
547
+            $query->execute();
548
+
549
+            if ($entry->getMimeType() == FileInfo::MIMETYPE_FOLDER) {
550
+                $this->removeChildren($entry);
551
+            }
552
+
553
+            $this->eventDispatcher->dispatchTyped(new CacheEntryRemovedEvent($this->storage, $entry->getPath(), $entry->getId(), $this->getNumericStorageId()));
554
+        }
555
+    }
556
+
557
+    /**
558
+     * Get all sub folders of a folder
559
+     *
560
+     * @param ICacheEntry $entry the cache entry of the folder to get the subfolders for
561
+     * @return ICacheEntry[] the cache entries for the subfolders
562
+     */
563
+    private function getSubFolders(ICacheEntry $entry) {
564
+        $children = $this->getFolderContentsById($entry->getId());
565
+        return array_filter($children, function ($child) {
566
+            return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
567
+        });
568
+    }
569
+
570
+    /**
571
+     * Recursively remove all children of a folder
572
+     *
573
+     * @param ICacheEntry $entry the cache entry of the folder to remove the children of
574
+     * @throws \OC\DatabaseException
575
+     */
576
+    private function removeChildren(ICacheEntry $entry) {
577
+        $parentIds = [$entry->getId()];
578
+        $queue = [$entry->getId()];
579
+
580
+        // we walk depth first trough the file tree, removing all filecache_extended attributes while we walk
581
+        // and collecting all folder ids to later use to delete the filecache entries
582
+        while ($entryId = array_pop($queue)) {
583
+            $children = $this->getFolderContentsById($entryId);
584
+            $childIds = array_map(function (ICacheEntry $cacheEntry) {
585
+                return $cacheEntry->getId();
586
+            }, $children);
587
+
588
+            $query = $this->getQueryBuilder();
589
+            $query->delete('filecache_extended')
590
+                ->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY)));
591
+            $query->execute();
592
+
593
+            /** @var ICacheEntry[] $childFolders */
594
+            $childFolders = array_filter($children, function ($child) {
595
+                return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
596
+            });
597
+            foreach ($childFolders as $folder) {
598
+                $parentIds[] = $folder->getId();
599
+                $queue[] = $folder->getId();
600
+            }
601
+        }
602
+
603
+        $query = $this->getQueryBuilder();
604
+        $query->delete('filecache')
605
+            ->whereParentIn($parentIds);
606
+        $query->execute();
607
+    }
608
+
609
+    /**
610
+     * Move a file or folder in the cache
611
+     *
612
+     * @param string $source
613
+     * @param string $target
614
+     */
615
+    public function move($source, $target) {
616
+        $this->moveFromCache($this, $source, $target);
617
+    }
618
+
619
+    /**
620
+     * Get the storage id and path needed for a move
621
+     *
622
+     * @param string $path
623
+     * @return array [$storageId, $internalPath]
624
+     */
625
+    protected function getMoveInfo($path) {
626
+        return [$this->getNumericStorageId(), $path];
627
+    }
628
+
629
+    /**
630
+     * Move a file or folder in the cache
631
+     *
632
+     * @param ICache $sourceCache
633
+     * @param string $sourcePath
634
+     * @param string $targetPath
635
+     * @throws \OC\DatabaseException
636
+     * @throws \Exception if the given storages have an invalid id
637
+     */
638
+    public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
639
+        if ($sourceCache instanceof Cache) {
640
+            // normalize source and target
641
+            $sourcePath = $this->normalize($sourcePath);
642
+            $targetPath = $this->normalize($targetPath);
643
+
644
+            $sourceData = $sourceCache->get($sourcePath);
645
+            $sourceId = $sourceData['fileid'];
646
+            $newParentId = $this->getParentId($targetPath);
647
+
648
+            [$sourceStorageId, $sourcePath] = $sourceCache->getMoveInfo($sourcePath);
649
+            [$targetStorageId, $targetPath] = $this->getMoveInfo($targetPath);
650
+
651
+            if (is_null($sourceStorageId) || $sourceStorageId === false) {
652
+                throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
653
+            }
654
+            if (is_null($targetStorageId) || $targetStorageId === false) {
655
+                throw new \Exception('Invalid target storage id: ' . $targetStorageId);
656
+            }
657
+
658
+            $this->connection->beginTransaction();
659
+            if ($sourceData['mimetype'] === 'httpd/unix-directory') {
660
+                //update all child entries
661
+                $sourceLength = mb_strlen($sourcePath);
662
+                $query = $this->connection->getQueryBuilder();
663
+
664
+                $fun = $query->func();
665
+                $newPathFunction = $fun->concat(
666
+                    $query->createNamedParameter($targetPath),
667
+                    $fun->substring('path', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash
668
+                );
669
+                $query->update('filecache')
670
+                    ->set('storage', $query->createNamedParameter($targetStorageId, IQueryBuilder::PARAM_INT))
671
+                    ->set('path_hash', $fun->md5($newPathFunction))
672
+                    ->set('path', $newPathFunction)
673
+                    ->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
674
+                    ->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
675
+
676
+                try {
677
+                    $query->execute();
678
+                } catch (\OC\DatabaseException $e) {
679
+                    $this->connection->rollBack();
680
+                    throw $e;
681
+                }
682
+            }
683
+
684
+            $query = $this->getQueryBuilder();
685
+            $query->update('filecache')
686
+                ->set('storage', $query->createNamedParameter($targetStorageId))
687
+                ->set('path', $query->createNamedParameter($targetPath))
688
+                ->set('path_hash', $query->createNamedParameter(md5($targetPath)))
689
+                ->set('name', $query->createNamedParameter(basename($targetPath)))
690
+                ->set('parent', $query->createNamedParameter($newParentId, IQueryBuilder::PARAM_INT))
691
+                ->whereFileId($sourceId);
692
+            $query->execute();
693
+
694
+            $this->connection->commit();
695
+
696
+            if ($sourceCache->getNumericStorageId() !== $this->getNumericStorageId()) {
697
+                $this->eventDispatcher->dispatchTyped(new CacheEntryRemovedEvent($this->storage, $sourcePath, $sourceId, $sourceCache->getNumericStorageId()));
698
+                $event = new CacheEntryInsertedEvent($this->storage, $targetPath, $sourceId, $this->getNumericStorageId());
699
+                $this->eventDispatcher->dispatch(CacheInsertEvent::class, $event);
700
+                $this->eventDispatcher->dispatchTyped($event);
701
+            } else {
702
+                $event = new CacheEntryUpdatedEvent($this->storage, $targetPath, $sourceId, $this->getNumericStorageId());
703
+                $this->eventDispatcher->dispatch(CacheUpdateEvent::class, $event);
704
+                $this->eventDispatcher->dispatchTyped($event);
705
+            }
706
+        } else {
707
+            $this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
708
+        }
709
+    }
710
+
711
+    /**
712
+     * remove all entries for files that are stored on the storage from the cache
713
+     */
714
+    public function clear() {
715
+        $query = $this->getQueryBuilder();
716
+        $query->delete('filecache')
717
+            ->whereStorageId();
718
+        $query->execute();
719
+
720
+        $query = $this->connection->getQueryBuilder();
721
+        $query->delete('storages')
722
+            ->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId)));
723
+        $query->execute();
724
+    }
725
+
726
+    /**
727
+     * Get the scan status of a file
728
+     *
729
+     * - Cache::NOT_FOUND: File is not in the cache
730
+     * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
731
+     * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
732
+     * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
733
+     *
734
+     * @param string $file
735
+     *
736
+     * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
737
+     */
738
+    public function getStatus($file) {
739
+        // normalize file
740
+        $file = $this->normalize($file);
741
+
742
+        $query = $this->getQueryBuilder();
743
+        $query->select('size')
744
+            ->from('filecache')
745
+            ->whereStorageId()
746
+            ->wherePath($file);
747
+
748
+        $result = $query->execute();
749
+        $size = $result->fetchOne();
750
+        $result->closeCursor();
751
+
752
+        if ($size !== false) {
753
+            if ((int)$size === -1) {
754
+                return self::SHALLOW;
755
+            } else {
756
+                return self::COMPLETE;
757
+            }
758
+        } else {
759
+            if (isset($this->partial[$file])) {
760
+                return self::PARTIAL;
761
+            } else {
762
+                return self::NOT_FOUND;
763
+            }
764
+        }
765
+    }
766
+
767
+    /**
768
+     * search for files matching $pattern
769
+     *
770
+     * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
771
+     * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
772
+     */
773
+    public function search($pattern) {
774
+        // normalize pattern
775
+        $pattern = $this->normalize($pattern);
776
+
777
+        if ($pattern === '%%') {
778
+            return [];
779
+        }
780
+
781
+        $query = $this->getQueryBuilder();
782
+        $query->selectFileCache()
783
+            ->whereStorageId()
784
+            ->andWhere($query->expr()->iLike('name', $query->createNamedParameter($pattern)));
785
+
786
+        $result = $query->execute();
787
+        $files = $result->fetchAll();
788
+        $result->closeCursor();
789
+
790
+        return array_map(function (array $data) {
791
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
792
+        }, $files);
793
+    }
794
+
795
+    /**
796
+     * @param IResult $result
797
+     * @return CacheEntry[]
798
+     */
799
+    private function searchResultToCacheEntries(IResult $result): array {
800
+        $files = $result->fetchAll();
801
+
802
+        return array_map(function (array $data) {
803
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
804
+        }, $files);
805
+    }
806
+
807
+    /**
808
+     * search for files by mimetype
809
+     *
810
+     * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
811
+     *        where it will search for all mimetypes in the group ('image/*')
812
+     * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
813
+     */
814
+    public function searchByMime($mimetype) {
815
+        $mimeId = $this->mimetypeLoader->getId($mimetype);
816
+
817
+        $query = $this->getQueryBuilder();
818
+        $query->selectFileCache()
819
+            ->whereStorageId();
820
+
821
+        if (strpos($mimetype, '/')) {
822
+            $query->andWhere($query->expr()->eq('mimetype', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
823
+        } else {
824
+            $query->andWhere($query->expr()->eq('mimepart', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT)));
825
+        }
826
+
827
+        $result = $query->execute();
828
+        $files = $result->fetchAll();
829
+        $result->closeCursor();
830
+
831
+        return array_map(function (array $data) {
832
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
833
+        }, $files);
834
+    }
835
+
836
+    public function searchQuery(ISearchQuery $searchQuery) {
837
+        $builder = $this->getQueryBuilder();
838
+
839
+        $query = $builder->selectFileCache('file');
840
+
841
+        $query->whereStorageId();
842
+
843
+        if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) {
844
+            $query
845
+                ->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
846
+                ->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
847
+                    $builder->expr()->eq('tagmap.type', 'tag.type'),
848
+                    $builder->expr()->eq('tagmap.categoryid', 'tag.id')
849
+                ))
850
+                ->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
851
+                ->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($searchQuery->getUser()->getUID())));
852
+        }
853
+
854
+        $searchExpr = $this->querySearchHelper->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation());
855
+        if ($searchExpr) {
856
+            $query->andWhere($searchExpr);
857
+        }
858
+
859
+        if ($searchQuery->limitToHome() && ($this instanceof HomeCache)) {
860
+            $query->andWhere($builder->expr()->like('path', $query->expr()->literal('files/%')));
861
+        }
862
+
863
+        $this->querySearchHelper->addSearchOrdersToQuery($query, $searchQuery->getOrder());
864
+
865
+        if ($searchQuery->getLimit()) {
866
+            $query->setMaxResults($searchQuery->getLimit());
867
+        }
868
+        if ($searchQuery->getOffset()) {
869
+            $query->setFirstResult($searchQuery->getOffset());
870
+        }
871
+
872
+        $result = $query->execute();
873
+        $cacheEntries = $this->searchResultToCacheEntries($result);
874
+        $result->closeCursor();
875
+        return $cacheEntries;
876
+    }
877
+
878
+    /**
879
+     * Re-calculate the folder size and the size of all parent folders
880
+     *
881
+     * @param string|boolean $path
882
+     * @param array $data (optional) meta data of the folder
883
+     */
884
+    public function correctFolderSize($path, $data = null, $isBackgroundScan = false) {
885
+        $this->calculateFolderSize($path, $data);
886
+        if ($path !== '') {
887
+            $parent = dirname($path);
888
+            if ($parent === '.' or $parent === '/') {
889
+                $parent = '';
890
+            }
891
+            if ($isBackgroundScan) {
892
+                $parentData = $this->get($parent);
893
+                if ($parentData['size'] !== -1 && $this->getIncompleteChildrenCount($parentData['fileid']) === 0) {
894
+                    $this->correctFolderSize($parent, $parentData, $isBackgroundScan);
895
+                }
896
+            } else {
897
+                $this->correctFolderSize($parent);
898
+            }
899
+        }
900
+    }
901
+
902
+    /**
903
+     * get the incomplete count that shares parent $folder
904
+     *
905
+     * @param int $fileId the file id of the folder
906
+     * @return int
907
+     */
908
+    public function getIncompleteChildrenCount($fileId) {
909
+        if ($fileId > -1) {
910
+            $query = $this->getQueryBuilder();
911
+            $query->select($query->func()->count())
912
+                ->from('filecache')
913
+                ->whereParent($fileId)
914
+                ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
915
+
916
+            $result = $query->execute();
917
+            $size = (int)$result->fetchOne();
918
+            $result->closeCursor();
919
+
920
+            return $size;
921
+        }
922
+        return -1;
923
+    }
924
+
925
+    /**
926
+     * calculate the size of a folder and set it in the cache
927
+     *
928
+     * @param string $path
929
+     * @param array $entry (optional) meta data of the folder
930
+     * @return int
931
+     */
932
+    public function calculateFolderSize($path, $entry = null) {
933
+        $totalSize = 0;
934
+        if (is_null($entry) or !isset($entry['fileid'])) {
935
+            $entry = $this->get($path);
936
+        }
937
+        if (isset($entry['mimetype']) && $entry['mimetype'] === FileInfo::MIMETYPE_FOLDER) {
938
+            $id = $entry['fileid'];
939
+
940
+            $query = $this->getQueryBuilder();
941
+            $query->selectAlias($query->func()->sum('size'), 'f1')
942
+                ->selectAlias($query->func()->min('size'), 'f2')
943
+                ->from('filecache')
944
+                ->whereStorageId()
945
+                ->whereParent($id);
946
+
947
+            $result = $query->execute();
948
+            $row = $result->fetch();
949
+            $result->closeCursor();
950
+
951
+            if ($row) {
952
+                [$sum, $min] = array_values($row);
953
+                $sum = 0 + $sum;
954
+                $min = 0 + $min;
955
+                if ($min === -1) {
956
+                    $totalSize = $min;
957
+                } else {
958
+                    $totalSize = $sum;
959
+                }
960
+                if ($entry['size'] !== $totalSize) {
961
+                    $this->update($id, ['size' => $totalSize]);
962
+                }
963
+            }
964
+        }
965
+        return $totalSize;
966
+    }
967
+
968
+    /**
969
+     * get all file ids on the files on the storage
970
+     *
971
+     * @return int[]
972
+     */
973
+    public function getAll() {
974
+        $query = $this->getQueryBuilder();
975
+        $query->select('fileid')
976
+            ->from('filecache')
977
+            ->whereStorageId();
978
+
979
+        $result = $query->execute();
980
+        $files = $result->fetchAll(\PDO::FETCH_COLUMN);
981
+        $result->closeCursor();
982
+
983
+        return array_map(function ($id) {
984
+            return (int)$id;
985
+        }, $files);
986
+    }
987
+
988
+    /**
989
+     * find a folder in the cache which has not been fully scanned
990
+     *
991
+     * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
992
+     * use the one with the highest id gives the best result with the background scanner, since that is most
993
+     * likely the folder where we stopped scanning previously
994
+     *
995
+     * @return string|bool the path of the folder or false when no folder matched
996
+     */
997
+    public function getIncomplete() {
998
+        $query = $this->getQueryBuilder();
999
+        $query->select('path')
1000
+            ->from('filecache')
1001
+            ->whereStorageId()
1002
+            ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
1003
+            ->orderBy('fileid', 'DESC')
1004
+            ->setMaxResults(1);
1005
+
1006
+        $result = $query->execute();
1007
+        $path = $result->fetchOne();
1008
+        $result->closeCursor();
1009
+
1010
+        return $path;
1011
+    }
1012
+
1013
+    /**
1014
+     * get the path of a file on this storage by it's file id
1015
+     *
1016
+     * @param int $id the file id of the file or folder to search
1017
+     * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
1018
+     */
1019
+    public function getPathById($id) {
1020
+        $query = $this->getQueryBuilder();
1021
+        $query->select('path')
1022
+            ->from('filecache')
1023
+            ->whereStorageId()
1024
+            ->whereFileId($id);
1025
+
1026
+        $result = $query->execute();
1027
+        $path = $result->fetchOne();
1028
+        $result->closeCursor();
1029
+
1030
+        if ($path === false) {
1031
+            return null;
1032
+        }
1033
+
1034
+        return (string) $path;
1035
+    }
1036
+
1037
+    /**
1038
+     * get the storage id of the storage for a file and the internal path of the file
1039
+     * unlike getPathById this does not limit the search to files on this storage and
1040
+     * instead does a global search in the cache table
1041
+     *
1042
+     * @param int $id
1043
+     * @return array first element holding the storage id, second the path
1044
+     * @deprecated use getPathById() instead
1045
+     */
1046
+    public static function getById($id) {
1047
+        $query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
1048
+        $query->select('path', 'storage')
1049
+            ->from('filecache')
1050
+            ->where($query->expr()->eq('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
1051
+
1052
+        $result = $query->execute();
1053
+        $row = $result->fetch();
1054
+        $result->closeCursor();
1055
+
1056
+        if ($row) {
1057
+            $numericId = $row['storage'];
1058
+            $path = $row['path'];
1059
+        } else {
1060
+            return null;
1061
+        }
1062
+
1063
+        if ($id = Storage::getStorageId($numericId)) {
1064
+            return [$id, $path];
1065
+        } else {
1066
+            return null;
1067
+        }
1068
+    }
1069
+
1070
+    /**
1071
+     * normalize the given path
1072
+     *
1073
+     * @param string $path
1074
+     * @return string
1075
+     */
1076
+    public function normalize($path) {
1077
+        return trim(\OC_Util::normalizeUnicode($path), '/');
1078
+    }
1079
+
1080
+    /**
1081
+     * Copy a file or folder in the cache
1082
+     *
1083
+     * @param ICache $sourceCache
1084
+     * @param ICacheEntry $sourceEntry
1085
+     * @param string $targetPath
1086
+     * @return int fileid of copied entry
1087
+     */
1088
+    public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int {
1089
+        if ($sourceEntry->getId() < 0) {
1090
+            throw new \RuntimeException("Invalid source cache entry on copyFromCache");
1091
+        }
1092
+        $data = $this->cacheEntryToArray($sourceEntry);
1093
+        $fileId = $this->put($targetPath, $data);
1094
+        if ($fileId <= 0) {
1095
+            throw new \RuntimeException("Failed to copy to " . $targetPath . " from cache with source data " . json_encode($data) . " ");
1096
+        }
1097
+        if ($sourceEntry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
1098
+            $folderContent = $sourceCache->getFolderContentsById($sourceEntry->getId());
1099
+            foreach ($folderContent as $subEntry) {
1100
+                $subTargetPath = $targetPath . '/' . $subEntry->getName();
1101
+                $this->copyFromCache($sourceCache, $subEntry, $subTargetPath);
1102
+            }
1103
+        }
1104
+        return $fileId;
1105
+    }
1106
+
1107
+    private function cacheEntryToArray(ICacheEntry $entry): array {
1108
+        return [
1109
+            'size' => $entry->getSize(),
1110
+            'mtime' => $entry->getMTime(),
1111
+            'storage_mtime' => $entry->getStorageMTime(),
1112
+            'mimetype' => $entry->getMimeType(),
1113
+            'mimepart' => $entry->getMimePart(),
1114
+            'etag' => $entry->getEtag(),
1115
+            'permissions' => $entry->getPermissions(),
1116
+            'encrypted' => $entry->isEncrypted(),
1117
+            'creation_time' => $entry->getCreationTime(),
1118
+            'upload_time' => $entry->getUploadTime(),
1119
+            'metadata_etag' => $entry->getMetadataEtag(),
1120
+        ];
1121
+    }
1122 1122
 }
Please login to merge, or discard this patch.
Spacing   +28 added lines, -28 removed lines patch added patch discarded remove patch
@@ -182,21 +182,21 @@  discard block
 block discarded – undo
182 182
 	 */
183 183
 	public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
184 184
 		//fix types
185
-		$data['fileid'] = (int)$data['fileid'];
186
-		$data['parent'] = (int)$data['parent'];
185
+		$data['fileid'] = (int) $data['fileid'];
186
+		$data['parent'] = (int) $data['parent'];
187 187
 		$data['size'] = 0 + $data['size'];
188
-		$data['mtime'] = (int)$data['mtime'];
189
-		$data['storage_mtime'] = (int)$data['storage_mtime'];
190
-		$data['encryptedVersion'] = (int)$data['encrypted'];
191
-		$data['encrypted'] = (bool)$data['encrypted'];
188
+		$data['mtime'] = (int) $data['mtime'];
189
+		$data['storage_mtime'] = (int) $data['storage_mtime'];
190
+		$data['encryptedVersion'] = (int) $data['encrypted'];
191
+		$data['encrypted'] = (bool) $data['encrypted'];
192 192
 		$data['storage_id'] = $data['storage'];
193
-		$data['storage'] = (int)$data['storage'];
193
+		$data['storage'] = (int) $data['storage'];
194 194
 		$data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
195 195
 		$data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
196 196
 		if ($data['storage_mtime'] == 0) {
197 197
 			$data['storage_mtime'] = $data['mtime'];
198 198
 		}
199
-		$data['permissions'] = (int)$data['permissions'];
199
+		$data['permissions'] = (int) $data['permissions'];
200 200
 		if (isset($data['creation_time'])) {
201 201
 			$data['creation_time'] = (int) $data['creation_time'];
202 202
 		}
@@ -234,7 +234,7 @@  discard block
 block discarded – undo
234 234
 			$files = $result->fetchAll();
235 235
 			$result->closeCursor();
236 236
 
237
-			return array_map(function (array $data) {
237
+			return array_map(function(array $data) {
238 238
 				return self::cacheEntryFromData($data, $this->mimetypeLoader);
239 239
 			}, $files);
240 240
 		}
@@ -363,7 +363,7 @@  discard block
 block discarded – undo
363 363
 
364 364
 			$query->update('filecache')
365 365
 				->whereFileId($id)
366
-				->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
366
+				->andWhere($query->expr()->orX(...array_map(function($key, $value) use ($query) {
367 367
 					return $query->expr()->orX(
368 368
 						$query->expr()->neq($key, $query->createNamedParameter($value)),
369 369
 						$query->expr()->isNull($key)
@@ -392,7 +392,7 @@  discard block
 block discarded – undo
392 392
 				$query = $this->getQueryBuilder();
393 393
 				$query->update('filecache_extended')
394 394
 					->whereFileId($id)
395
-					->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) {
395
+					->andWhere($query->expr()->orX(...array_map(function($key, $value) use ($query) {
396 396
 						return $query->expr()->orX(
397 397
 							$query->expr()->neq($key, $query->createNamedParameter($value)),
398 398
 							$query->expr()->isNull($key)
@@ -489,7 +489,7 @@  discard block
 block discarded – undo
489 489
 		$id = $result->fetchOne();
490 490
 		$result->closeCursor();
491 491
 
492
-		return $id === false ? -1 : (int)$id;
492
+		return $id === false ? -1 : (int) $id;
493 493
 	}
494 494
 
495 495
 	/**
@@ -503,7 +503,7 @@  discard block
 block discarded – undo
503 503
 			return -1;
504 504
 		} else {
505 505
 			$parent = $this->getParentPath($file);
506
-			return (int)$this->getId($parent);
506
+			return (int) $this->getId($parent);
507 507
 		}
508 508
 	}
509 509
 
@@ -562,7 +562,7 @@  discard block
 block discarded – undo
562 562
 	 */
563 563
 	private function getSubFolders(ICacheEntry $entry) {
564 564
 		$children = $this->getFolderContentsById($entry->getId());
565
-		return array_filter($children, function ($child) {
565
+		return array_filter($children, function($child) {
566 566
 			return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
567 567
 		});
568 568
 	}
@@ -581,7 +581,7 @@  discard block
 block discarded – undo
581 581
 		// and collecting all folder ids to later use to delete the filecache entries
582 582
 		while ($entryId = array_pop($queue)) {
583 583
 			$children = $this->getFolderContentsById($entryId);
584
-			$childIds = array_map(function (ICacheEntry $cacheEntry) {
584
+			$childIds = array_map(function(ICacheEntry $cacheEntry) {
585 585
 				return $cacheEntry->getId();
586 586
 			}, $children);
587 587
 
@@ -591,7 +591,7 @@  discard block
 block discarded – undo
591 591
 			$query->execute();
592 592
 
593 593
 			/** @var ICacheEntry[] $childFolders */
594
-			$childFolders = array_filter($children, function ($child) {
594
+			$childFolders = array_filter($children, function($child) {
595 595
 				return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER;
596 596
 			});
597 597
 			foreach ($childFolders as $folder) {
@@ -649,10 +649,10 @@  discard block
 block discarded – undo
649 649
 			[$targetStorageId, $targetPath] = $this->getMoveInfo($targetPath);
650 650
 
651 651
 			if (is_null($sourceStorageId) || $sourceStorageId === false) {
652
-				throw new \Exception('Invalid source storage id: ' . $sourceStorageId);
652
+				throw new \Exception('Invalid source storage id: '.$sourceStorageId);
653 653
 			}
654 654
 			if (is_null($targetStorageId) || $targetStorageId === false) {
655
-				throw new \Exception('Invalid target storage id: ' . $targetStorageId);
655
+				throw new \Exception('Invalid target storage id: '.$targetStorageId);
656 656
 			}
657 657
 
658 658
 			$this->connection->beginTransaction();
@@ -671,7 +671,7 @@  discard block
 block discarded – undo
671 671
 					->set('path_hash', $fun->md5($newPathFunction))
672 672
 					->set('path', $newPathFunction)
673 673
 					->where($query->expr()->eq('storage', $query->createNamedParameter($sourceStorageId, IQueryBuilder::PARAM_INT)))
674
-					->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath) . '/%')));
674
+					->andWhere($query->expr()->like('path', $query->createNamedParameter($this->connection->escapeLikeParameter($sourcePath).'/%')));
675 675
 
676 676
 				try {
677 677
 					$query->execute();
@@ -750,7 +750,7 @@  discard block
 block discarded – undo
750 750
 		$result->closeCursor();
751 751
 
752 752
 		if ($size !== false) {
753
-			if ((int)$size === -1) {
753
+			if ((int) $size === -1) {
754 754
 				return self::SHALLOW;
755 755
 			} else {
756 756
 				return self::COMPLETE;
@@ -787,7 +787,7 @@  discard block
 block discarded – undo
787 787
 		$files = $result->fetchAll();
788 788
 		$result->closeCursor();
789 789
 
790
-		return array_map(function (array $data) {
790
+		return array_map(function(array $data) {
791 791
 			return self::cacheEntryFromData($data, $this->mimetypeLoader);
792 792
 		}, $files);
793 793
 	}
@@ -799,7 +799,7 @@  discard block
 block discarded – undo
799 799
 	private function searchResultToCacheEntries(IResult $result): array {
800 800
 		$files = $result->fetchAll();
801 801
 
802
-		return array_map(function (array $data) {
802
+		return array_map(function(array $data) {
803 803
 			return self::cacheEntryFromData($data, $this->mimetypeLoader);
804 804
 		}, $files);
805 805
 	}
@@ -828,7 +828,7 @@  discard block
 block discarded – undo
828 828
 		$files = $result->fetchAll();
829 829
 		$result->closeCursor();
830 830
 
831
-		return array_map(function (array $data) {
831
+		return array_map(function(array $data) {
832 832
 			return self::cacheEntryFromData($data, $this->mimetypeLoader);
833 833
 		}, $files);
834 834
 	}
@@ -914,7 +914,7 @@  discard block
 block discarded – undo
914 914
 				->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
915 915
 
916 916
 			$result = $query->execute();
917
-			$size = (int)$result->fetchOne();
917
+			$size = (int) $result->fetchOne();
918 918
 			$result->closeCursor();
919 919
 
920 920
 			return $size;
@@ -980,8 +980,8 @@  discard block
 block discarded – undo
980 980
 		$files = $result->fetchAll(\PDO::FETCH_COLUMN);
981 981
 		$result->closeCursor();
982 982
 
983
-		return array_map(function ($id) {
984
-			return (int)$id;
983
+		return array_map(function($id) {
984
+			return (int) $id;
985 985
 		}, $files);
986 986
 	}
987 987
 
@@ -1092,12 +1092,12 @@  discard block
 block discarded – undo
1092 1092
 		$data = $this->cacheEntryToArray($sourceEntry);
1093 1093
 		$fileId = $this->put($targetPath, $data);
1094 1094
 		if ($fileId <= 0) {
1095
-			throw new \RuntimeException("Failed to copy to " . $targetPath . " from cache with source data " . json_encode($data) . " ");
1095
+			throw new \RuntimeException("Failed to copy to ".$targetPath." from cache with source data ".json_encode($data)." ");
1096 1096
 		}
1097 1097
 		if ($sourceEntry->getMimeType() === ICacheEntry::DIRECTORY_MIMETYPE) {
1098 1098
 			$folderContent = $sourceCache->getFolderContentsById($sourceEntry->getId());
1099 1099
 			foreach ($folderContent as $subEntry) {
1100
-				$subTargetPath = $targetPath . '/' . $subEntry->getName();
1100
+				$subTargetPath = $targetPath.'/'.$subEntry->getName();
1101 1101
 				$this->copyFromCache($sourceCache, $subEntry, $subTargetPath);
1102 1102
 			}
1103 1103
 		}
Please login to merge, or discard this patch.
lib/private/Files/Cache/MoveFromCacheTrait.php 1 patch
Indentation   +23 added lines, -23 removed lines patch added patch discarded remove patch
@@ -30,31 +30,31 @@
 block discarded – undo
30 30
  * Fallback implementation for moveFromCache
31 31
  */
32 32
 trait MoveFromCacheTrait {
33
-	/**
34
-	 * store meta data for a file or folder
35
-	 *
36
-	 * @param string $file
37
-	 * @param array $data
38
-	 *
39
-	 * @return int file id
40
-	 * @throws \RuntimeException
41
-	 */
42
-	abstract public function put($file, array $data);
33
+    /**
34
+     * store meta data for a file or folder
35
+     *
36
+     * @param string $file
37
+     * @param array $data
38
+     *
39
+     * @return int file id
40
+     * @throws \RuntimeException
41
+     */
42
+    abstract public function put($file, array $data);
43 43
 
44
-	abstract public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int;
44
+    abstract public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int;
45 45
 
46
-	/**
47
-	 * Move a file or folder in the cache
48
-	 *
49
-	 * @param \OCP\Files\Cache\ICache $sourceCache
50
-	 * @param string $sourcePath
51
-	 * @param string $targetPath
52
-	 */
53
-	public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
54
-		$sourceEntry = $sourceCache->get($sourcePath);
46
+    /**
47
+     * Move a file or folder in the cache
48
+     *
49
+     * @param \OCP\Files\Cache\ICache $sourceCache
50
+     * @param string $sourcePath
51
+     * @param string $targetPath
52
+     */
53
+    public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
54
+        $sourceEntry = $sourceCache->get($sourcePath);
55 55
 
56
-		$this->copyFromCache($sourceCache, $sourceEntry, $targetPath);
56
+        $this->copyFromCache($sourceCache, $sourceEntry, $targetPath);
57 57
 
58
-		$sourceCache->remove($sourcePath);
59
-	}
58
+        $sourceCache->remove($sourcePath);
59
+    }
60 60
 }
Please login to merge, or discard this patch.
lib/private/Files/Cache/FailedCache.php 1 patch
Indentation   +108 added lines, -108 removed lines patch added patch discarded remove patch
@@ -31,112 +31,112 @@
 block discarded – undo
31 31
  * Storage placeholder to represent a missing precondition, storage unavailable
32 32
  */
33 33
 class FailedCache implements ICache {
34
-	/** @var bool whether to show the failed storage in the ui */
35
-	private $visible;
36
-
37
-	/**
38
-	 * FailedCache constructor.
39
-	 *
40
-	 * @param bool $visible
41
-	 */
42
-	public function __construct($visible = true) {
43
-		$this->visible = $visible;
44
-	}
45
-
46
-
47
-	public function getNumericStorageId() {
48
-		return -1;
49
-	}
50
-
51
-	public function get($file) {
52
-		if ($file === '') {
53
-			return new CacheEntry([
54
-				'fileid' => -1,
55
-				'size' => 0,
56
-				'mimetype' => 'httpd/unix-directory',
57
-				'mimepart' => 'httpd',
58
-				'permissions' => $this->visible ? Constants::PERMISSION_READ : 0,
59
-				'mtime' => time()
60
-			]);
61
-		} else {
62
-			return false;
63
-		}
64
-	}
65
-
66
-	public function getFolderContents($folder) {
67
-		return [];
68
-	}
69
-
70
-	public function getFolderContentsById($fileId) {
71
-		return [];
72
-	}
73
-
74
-	public function put($file, array $data) {
75
-	}
76
-
77
-	public function insert($file, array $data) {
78
-	}
79
-
80
-	public function update($id, array $data) {
81
-	}
82
-
83
-	public function getId($file) {
84
-		return -1;
85
-	}
86
-
87
-	public function getParentId($file) {
88
-		return -1;
89
-	}
90
-
91
-	public function inCache($file) {
92
-		return false;
93
-	}
94
-
95
-	public function remove($file) {
96
-	}
97
-
98
-	public function move($source, $target) {
99
-	}
100
-
101
-	public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
102
-	}
103
-
104
-	public function clear() {
105
-	}
106
-
107
-	public function getStatus($file) {
108
-		return ICache::NOT_FOUND;
109
-	}
110
-
111
-	public function search($pattern) {
112
-		return [];
113
-	}
114
-
115
-	public function searchByMime($mimetype) {
116
-		return [];
117
-	}
118
-
119
-	public function searchQuery(ISearchQuery $query) {
120
-		return [];
121
-	}
122
-
123
-	public function getAll() {
124
-		return [];
125
-	}
126
-
127
-	public function getIncomplete() {
128
-		return [];
129
-	}
130
-
131
-	public function getPathById($id) {
132
-		return null;
133
-	}
134
-
135
-	public function normalize($path) {
136
-		return $path;
137
-	}
138
-
139
-	public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int {
140
-		throw new \Exception("Invalid cache");
141
-	}
34
+    /** @var bool whether to show the failed storage in the ui */
35
+    private $visible;
36
+
37
+    /**
38
+     * FailedCache constructor.
39
+     *
40
+     * @param bool $visible
41
+     */
42
+    public function __construct($visible = true) {
43
+        $this->visible = $visible;
44
+    }
45
+
46
+
47
+    public function getNumericStorageId() {
48
+        return -1;
49
+    }
50
+
51
+    public function get($file) {
52
+        if ($file === '') {
53
+            return new CacheEntry([
54
+                'fileid' => -1,
55
+                'size' => 0,
56
+                'mimetype' => 'httpd/unix-directory',
57
+                'mimepart' => 'httpd',
58
+                'permissions' => $this->visible ? Constants::PERMISSION_READ : 0,
59
+                'mtime' => time()
60
+            ]);
61
+        } else {
62
+            return false;
63
+        }
64
+    }
65
+
66
+    public function getFolderContents($folder) {
67
+        return [];
68
+    }
69
+
70
+    public function getFolderContentsById($fileId) {
71
+        return [];
72
+    }
73
+
74
+    public function put($file, array $data) {
75
+    }
76
+
77
+    public function insert($file, array $data) {
78
+    }
79
+
80
+    public function update($id, array $data) {
81
+    }
82
+
83
+    public function getId($file) {
84
+        return -1;
85
+    }
86
+
87
+    public function getParentId($file) {
88
+        return -1;
89
+    }
90
+
91
+    public function inCache($file) {
92
+        return false;
93
+    }
94
+
95
+    public function remove($file) {
96
+    }
97
+
98
+    public function move($source, $target) {
99
+    }
100
+
101
+    public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
102
+    }
103
+
104
+    public function clear() {
105
+    }
106
+
107
+    public function getStatus($file) {
108
+        return ICache::NOT_FOUND;
109
+    }
110
+
111
+    public function search($pattern) {
112
+        return [];
113
+    }
114
+
115
+    public function searchByMime($mimetype) {
116
+        return [];
117
+    }
118
+
119
+    public function searchQuery(ISearchQuery $query) {
120
+        return [];
121
+    }
122
+
123
+    public function getAll() {
124
+        return [];
125
+    }
126
+
127
+    public function getIncomplete() {
128
+        return [];
129
+    }
130
+
131
+    public function getPathById($id) {
132
+        return null;
133
+    }
134
+
135
+    public function normalize($path) {
136
+        return $path;
137
+    }
138
+
139
+    public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int {
140
+        throw new \Exception("Invalid cache");
141
+    }
142 142
 }
Please login to merge, or discard this patch.
lib/private/Files/ObjectStore/ObjectStoreStorage.php 1 patch
Indentation   +552 added lines, -552 removed lines patch added patch discarded remove patch
@@ -43,559 +43,559 @@
 block discarded – undo
43 43
 use OCP\Files\Storage\IStorage;
44 44
 
45 45
 class ObjectStoreStorage extends \OC\Files\Storage\Common {
46
-	use CopyDirectory;
47
-
48
-	/**
49
-	 * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
50
-	 */
51
-	protected $objectStore;
52
-	/**
53
-	 * @var string $id
54
-	 */
55
-	protected $id;
56
-	/**
57
-	 * @var \OC\User\User $user
58
-	 */
59
-	protected $user;
60
-
61
-	private $objectPrefix = 'urn:oid:';
62
-
63
-	private $logger;
64
-
65
-	public function __construct($params) {
66
-		if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
67
-			$this->objectStore = $params['objectstore'];
68
-		} else {
69
-			throw new \Exception('missing IObjectStore instance');
70
-		}
71
-		if (isset($params['storageid'])) {
72
-			$this->id = 'object::store:' . $params['storageid'];
73
-		} else {
74
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
75
-		}
76
-		if (isset($params['objectPrefix'])) {
77
-			$this->objectPrefix = $params['objectPrefix'];
78
-		}
79
-		//initialize cache with root directory in cache
80
-		if (!$this->is_dir('/')) {
81
-			$this->mkdir('/');
82
-		}
83
-
84
-		$this->logger = \OC::$server->getLogger();
85
-	}
86
-
87
-	public function mkdir($path) {
88
-		$path = $this->normalizePath($path);
89
-
90
-		if ($this->file_exists($path)) {
91
-			return false;
92
-		}
93
-
94
-		$mTime = time();
95
-		$data = [
96
-			'mimetype' => 'httpd/unix-directory',
97
-			'size' => 0,
98
-			'mtime' => $mTime,
99
-			'storage_mtime' => $mTime,
100
-			'permissions' => \OCP\Constants::PERMISSION_ALL,
101
-		];
102
-		if ($path === '') {
103
-			//create root on the fly
104
-			$data['etag'] = $this->getETag('');
105
-			$this->getCache()->put('', $data);
106
-			return true;
107
-		} else {
108
-			// if parent does not exist, create it
109
-			$parent = $this->normalizePath(dirname($path));
110
-			$parentType = $this->filetype($parent);
111
-			if ($parentType === false) {
112
-				if (!$this->mkdir($parent)) {
113
-					// something went wrong
114
-					return false;
115
-				}
116
-			} elseif ($parentType === 'file') {
117
-				// parent is a file
118
-				return false;
119
-			}
120
-			// finally create the new dir
121
-			$mTime = time(); // update mtime
122
-			$data['mtime'] = $mTime;
123
-			$data['storage_mtime'] = $mTime;
124
-			$data['etag'] = $this->getETag($path);
125
-			$this->getCache()->put($path, $data);
126
-			return true;
127
-		}
128
-	}
129
-
130
-	/**
131
-	 * @param string $path
132
-	 * @return string
133
-	 */
134
-	private function normalizePath($path) {
135
-		$path = trim($path, '/');
136
-		//FIXME why do we sometimes get a path like 'files//username'?
137
-		$path = str_replace('//', '/', $path);
138
-
139
-		// dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
140
-		if (!$path || $path === '.') {
141
-			$path = '';
142
-		}
143
-
144
-		return $path;
145
-	}
146
-
147
-	/**
148
-	 * Object Stores use a NoopScanner because metadata is directly stored in
149
-	 * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
150
-	 *
151
-	 * @param string $path
152
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
153
-	 * @return \OC\Files\ObjectStore\NoopScanner
154
-	 */
155
-	public function getScanner($path = '', $storage = null) {
156
-		if (!$storage) {
157
-			$storage = $this;
158
-		}
159
-		if (!isset($this->scanner)) {
160
-			$this->scanner = new NoopScanner($storage);
161
-		}
162
-		return $this->scanner;
163
-	}
164
-
165
-	public function getId() {
166
-		return $this->id;
167
-	}
168
-
169
-	public function rmdir($path) {
170
-		$path = $this->normalizePath($path);
171
-
172
-		if (!$this->is_dir($path)) {
173
-			return false;
174
-		}
175
-
176
-		if (!$this->rmObjects($path)) {
177
-			return false;
178
-		}
179
-
180
-		$this->getCache()->remove($path);
181
-
182
-		return true;
183
-	}
184
-
185
-	private function rmObjects($path) {
186
-		$children = $this->getCache()->getFolderContents($path);
187
-		foreach ($children as $child) {
188
-			if ($child['mimetype'] === 'httpd/unix-directory') {
189
-				if (!$this->rmObjects($child['path'])) {
190
-					return false;
191
-				}
192
-			} else {
193
-				if (!$this->unlink($child['path'])) {
194
-					return false;
195
-				}
196
-			}
197
-		}
198
-
199
-		return true;
200
-	}
201
-
202
-	public function unlink($path) {
203
-		$path = $this->normalizePath($path);
204
-		$stat = $this->stat($path);
205
-
206
-		if ($stat && isset($stat['fileid'])) {
207
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
208
-				return $this->rmdir($path);
209
-			}
210
-			try {
211
-				$this->objectStore->deleteObject($this->getURN($stat['fileid']));
212
-			} catch (\Exception $ex) {
213
-				if ($ex->getCode() !== 404) {
214
-					$this->logger->logException($ex, [
215
-						'app' => 'objectstore',
216
-						'message' => 'Could not delete object ' . $this->getURN($stat['fileid']) . ' for ' . $path,
217
-					]);
218
-					return false;
219
-				}
220
-				//removing from cache is ok as it does not exist in the objectstore anyway
221
-			}
222
-			$this->getCache()->remove($path);
223
-			return true;
224
-		}
225
-		return false;
226
-	}
227
-
228
-	public function stat($path) {
229
-		$path = $this->normalizePath($path);
230
-		$cacheEntry = $this->getCache()->get($path);
231
-		if ($cacheEntry instanceof CacheEntry) {
232
-			return $cacheEntry->getData();
233
-		} else {
234
-			return false;
235
-		}
236
-	}
237
-
238
-	public function getPermissions($path) {
239
-		$stat = $this->stat($path);
240
-
241
-		if (is_array($stat) && isset($stat['permissions'])) {
242
-			return $stat['permissions'];
243
-		}
244
-
245
-		return parent::getPermissions($path);
246
-	}
247
-
248
-	/**
249
-	 * Override this method if you need a different unique resource identifier for your object storage implementation.
250
-	 * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
251
-	 * You may need a mapping table to store your URN if it cannot be generated from the fileid.
252
-	 *
253
-	 * @param int $fileId the fileid
254
-	 * @return null|string the unified resource name used to identify the object
255
-	 */
256
-	public function getURN($fileId) {
257
-		if (is_numeric($fileId)) {
258
-			return $this->objectPrefix . $fileId;
259
-		}
260
-		return null;
261
-	}
262
-
263
-	public function opendir($path) {
264
-		$path = $this->normalizePath($path);
265
-
266
-		try {
267
-			$files = [];
268
-			$folderContents = $this->getCache()->getFolderContents($path);
269
-			foreach ($folderContents as $file) {
270
-				$files[] = $file['name'];
271
-			}
272
-
273
-			return IteratorDirectory::wrap($files);
274
-		} catch (\Exception $e) {
275
-			$this->logger->logException($e);
276
-			return false;
277
-		}
278
-	}
279
-
280
-	public function filetype($path) {
281
-		$path = $this->normalizePath($path);
282
-		$stat = $this->stat($path);
283
-		if ($stat) {
284
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
285
-				return 'dir';
286
-			}
287
-			return 'file';
288
-		} else {
289
-			return false;
290
-		}
291
-	}
292
-
293
-	public function fopen($path, $mode) {
294
-		$path = $this->normalizePath($path);
295
-
296
-		if (strrpos($path, '.') !== false) {
297
-			$ext = substr($path, strrpos($path, '.'));
298
-		} else {
299
-			$ext = '';
300
-		}
301
-
302
-		switch ($mode) {
303
-			case 'r':
304
-			case 'rb':
305
-				$stat = $this->stat($path);
306
-				if (is_array($stat)) {
307
-					// Reading 0 sized files is a waste of time
308
-					if (isset($stat['size']) && $stat['size'] === 0) {
309
-						return fopen('php://memory', $mode);
310
-					}
311
-
312
-					try {
313
-						return $this->objectStore->readObject($this->getURN($stat['fileid']));
314
-					} catch (NotFoundException $e) {
315
-						$this->logger->logException($e, [
316
-							'app' => 'objectstore',
317
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
318
-						]);
319
-						throw $e;
320
-					} catch (\Exception $ex) {
321
-						$this->logger->logException($ex, [
322
-							'app' => 'objectstore',
323
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
324
-						]);
325
-						return false;
326
-					}
327
-				} else {
328
-					return false;
329
-				}
330
-			// no break
331
-			case 'w':
332
-			case 'wb':
333
-			case 'w+':
334
-			case 'wb+':
335
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
336
-				$handle = fopen($tmpFile, $mode);
337
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
338
-					$this->writeBack($tmpFile, $path);
339
-				});
340
-			case 'a':
341
-			case 'ab':
342
-			case 'r+':
343
-			case 'a+':
344
-			case 'x':
345
-			case 'x+':
346
-			case 'c':
347
-			case 'c+':
348
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
349
-				if ($this->file_exists($path)) {
350
-					$source = $this->fopen($path, 'r');
351
-					file_put_contents($tmpFile, $source);
352
-				}
353
-				$handle = fopen($tmpFile, $mode);
354
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
355
-					$this->writeBack($tmpFile, $path);
356
-				});
357
-		}
358
-		return false;
359
-	}
360
-
361
-	public function file_exists($path) {
362
-		$path = $this->normalizePath($path);
363
-		return (bool)$this->stat($path);
364
-	}
365
-
366
-	public function rename($source, $target) {
367
-		$source = $this->normalizePath($source);
368
-		$target = $this->normalizePath($target);
369
-		$this->remove($target);
370
-		$this->getCache()->move($source, $target);
371
-		$this->touch(dirname($target));
372
-		return true;
373
-	}
374
-
375
-	public function getMimeType($path) {
376
-		$path = $this->normalizePath($path);
377
-		return parent::getMimeType($path);
378
-	}
379
-
380
-	public function touch($path, $mtime = null) {
381
-		if (is_null($mtime)) {
382
-			$mtime = time();
383
-		}
384
-
385
-		$path = $this->normalizePath($path);
386
-		$dirName = dirname($path);
387
-		$parentExists = $this->is_dir($dirName);
388
-		if (!$parentExists) {
389
-			return false;
390
-		}
391
-
392
-		$stat = $this->stat($path);
393
-		if (is_array($stat)) {
394
-			// update existing mtime in db
395
-			$stat['mtime'] = $mtime;
396
-			$this->getCache()->update($stat['fileid'], $stat);
397
-		} else {
398
-			try {
399
-				//create a empty file, need to have at least on char to make it
400
-				// work with all object storage implementations
401
-				$this->file_put_contents($path, ' ');
402
-				$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
403
-				$stat = [
404
-					'etag' => $this->getETag($path),
405
-					'mimetype' => $mimeType,
406
-					'size' => 0,
407
-					'mtime' => $mtime,
408
-					'storage_mtime' => $mtime,
409
-					'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
410
-				];
411
-				$this->getCache()->put($path, $stat);
412
-			} catch (\Exception $ex) {
413
-				$this->logger->logException($ex, [
414
-					'app' => 'objectstore',
415
-					'message' => 'Could not create object for ' . $path,
416
-				]);
417
-				throw $ex;
418
-			}
419
-		}
420
-		return true;
421
-	}
422
-
423
-	public function writeBack($tmpFile, $path) {
424
-		$size = filesize($tmpFile);
425
-		$this->writeStream($path, fopen($tmpFile, 'r'), $size);
426
-	}
427
-
428
-	/**
429
-	 * external changes are not supported, exclusive access to the object storage is assumed
430
-	 *
431
-	 * @param string $path
432
-	 * @param int $time
433
-	 * @return false
434
-	 */
435
-	public function hasUpdated($path, $time) {
436
-		return false;
437
-	}
438
-
439
-	public function needsPartFile() {
440
-		return false;
441
-	}
442
-
443
-	public function file_put_contents($path, $data) {
444
-		$handle = $this->fopen($path, 'w+');
445
-		$result = fwrite($handle, $data);
446
-		fclose($handle);
447
-		return $result;
448
-	}
449
-
450
-	public function writeStream(string $path, $stream, int $size = null): int {
451
-		$stat = $this->stat($path);
452
-		if (empty($stat)) {
453
-			// create new file
454
-			$stat = [
455
-				'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
456
-			];
457
-		}
458
-		// update stat with new data
459
-		$mTime = time();
460
-		$stat['size'] = (int)$size;
461
-		$stat['mtime'] = $mTime;
462
-		$stat['storage_mtime'] = $mTime;
463
-
464
-		$mimetypeDetector = \OC::$server->getMimeTypeDetector();
465
-		$mimetype = $mimetypeDetector->detectPath($path);
466
-
467
-		$stat['mimetype'] = $mimetype;
468
-		$stat['etag'] = $this->getETag($path);
469
-
470
-		$exists = $this->getCache()->inCache($path);
471
-		$uploadPath = $exists ? $path : $path . '.part';
472
-
473
-		if ($exists) {
474
-			$fileId = $stat['fileid'];
475
-		} else {
476
-			$fileId = $this->getCache()->put($uploadPath, $stat);
477
-		}
478
-
479
-		$urn = $this->getURN($fileId);
480
-		try {
481
-			//upload to object storage
482
-			if ($size === null) {
483
-				$countStream = CountWrapper::wrap($stream, function ($writtenSize) use ($fileId, &$size) {
484
-					$this->getCache()->update($fileId, [
485
-						'size' => $writtenSize,
486
-					]);
487
-					$size = $writtenSize;
488
-				});
489
-				$this->objectStore->writeObject($urn, $countStream);
490
-				if (is_resource($countStream)) {
491
-					fclose($countStream);
492
-				}
493
-				$stat['size'] = $size;
494
-			} else {
495
-				$this->objectStore->writeObject($urn, $stream);
496
-			}
497
-		} catch (\Exception $ex) {
498
-			if (!$exists) {
499
-				/*
46
+    use CopyDirectory;
47
+
48
+    /**
49
+     * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
50
+     */
51
+    protected $objectStore;
52
+    /**
53
+     * @var string $id
54
+     */
55
+    protected $id;
56
+    /**
57
+     * @var \OC\User\User $user
58
+     */
59
+    protected $user;
60
+
61
+    private $objectPrefix = 'urn:oid:';
62
+
63
+    private $logger;
64
+
65
+    public function __construct($params) {
66
+        if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
67
+            $this->objectStore = $params['objectstore'];
68
+        } else {
69
+            throw new \Exception('missing IObjectStore instance');
70
+        }
71
+        if (isset($params['storageid'])) {
72
+            $this->id = 'object::store:' . $params['storageid'];
73
+        } else {
74
+            $this->id = 'object::store:' . $this->objectStore->getStorageId();
75
+        }
76
+        if (isset($params['objectPrefix'])) {
77
+            $this->objectPrefix = $params['objectPrefix'];
78
+        }
79
+        //initialize cache with root directory in cache
80
+        if (!$this->is_dir('/')) {
81
+            $this->mkdir('/');
82
+        }
83
+
84
+        $this->logger = \OC::$server->getLogger();
85
+    }
86
+
87
+    public function mkdir($path) {
88
+        $path = $this->normalizePath($path);
89
+
90
+        if ($this->file_exists($path)) {
91
+            return false;
92
+        }
93
+
94
+        $mTime = time();
95
+        $data = [
96
+            'mimetype' => 'httpd/unix-directory',
97
+            'size' => 0,
98
+            'mtime' => $mTime,
99
+            'storage_mtime' => $mTime,
100
+            'permissions' => \OCP\Constants::PERMISSION_ALL,
101
+        ];
102
+        if ($path === '') {
103
+            //create root on the fly
104
+            $data['etag'] = $this->getETag('');
105
+            $this->getCache()->put('', $data);
106
+            return true;
107
+        } else {
108
+            // if parent does not exist, create it
109
+            $parent = $this->normalizePath(dirname($path));
110
+            $parentType = $this->filetype($parent);
111
+            if ($parentType === false) {
112
+                if (!$this->mkdir($parent)) {
113
+                    // something went wrong
114
+                    return false;
115
+                }
116
+            } elseif ($parentType === 'file') {
117
+                // parent is a file
118
+                return false;
119
+            }
120
+            // finally create the new dir
121
+            $mTime = time(); // update mtime
122
+            $data['mtime'] = $mTime;
123
+            $data['storage_mtime'] = $mTime;
124
+            $data['etag'] = $this->getETag($path);
125
+            $this->getCache()->put($path, $data);
126
+            return true;
127
+        }
128
+    }
129
+
130
+    /**
131
+     * @param string $path
132
+     * @return string
133
+     */
134
+    private function normalizePath($path) {
135
+        $path = trim($path, '/');
136
+        //FIXME why do we sometimes get a path like 'files//username'?
137
+        $path = str_replace('//', '/', $path);
138
+
139
+        // dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
140
+        if (!$path || $path === '.') {
141
+            $path = '';
142
+        }
143
+
144
+        return $path;
145
+    }
146
+
147
+    /**
148
+     * Object Stores use a NoopScanner because metadata is directly stored in
149
+     * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
150
+     *
151
+     * @param string $path
152
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
153
+     * @return \OC\Files\ObjectStore\NoopScanner
154
+     */
155
+    public function getScanner($path = '', $storage = null) {
156
+        if (!$storage) {
157
+            $storage = $this;
158
+        }
159
+        if (!isset($this->scanner)) {
160
+            $this->scanner = new NoopScanner($storage);
161
+        }
162
+        return $this->scanner;
163
+    }
164
+
165
+    public function getId() {
166
+        return $this->id;
167
+    }
168
+
169
+    public function rmdir($path) {
170
+        $path = $this->normalizePath($path);
171
+
172
+        if (!$this->is_dir($path)) {
173
+            return false;
174
+        }
175
+
176
+        if (!$this->rmObjects($path)) {
177
+            return false;
178
+        }
179
+
180
+        $this->getCache()->remove($path);
181
+
182
+        return true;
183
+    }
184
+
185
+    private function rmObjects($path) {
186
+        $children = $this->getCache()->getFolderContents($path);
187
+        foreach ($children as $child) {
188
+            if ($child['mimetype'] === 'httpd/unix-directory') {
189
+                if (!$this->rmObjects($child['path'])) {
190
+                    return false;
191
+                }
192
+            } else {
193
+                if (!$this->unlink($child['path'])) {
194
+                    return false;
195
+                }
196
+            }
197
+        }
198
+
199
+        return true;
200
+    }
201
+
202
+    public function unlink($path) {
203
+        $path = $this->normalizePath($path);
204
+        $stat = $this->stat($path);
205
+
206
+        if ($stat && isset($stat['fileid'])) {
207
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
208
+                return $this->rmdir($path);
209
+            }
210
+            try {
211
+                $this->objectStore->deleteObject($this->getURN($stat['fileid']));
212
+            } catch (\Exception $ex) {
213
+                if ($ex->getCode() !== 404) {
214
+                    $this->logger->logException($ex, [
215
+                        'app' => 'objectstore',
216
+                        'message' => 'Could not delete object ' . $this->getURN($stat['fileid']) . ' for ' . $path,
217
+                    ]);
218
+                    return false;
219
+                }
220
+                //removing from cache is ok as it does not exist in the objectstore anyway
221
+            }
222
+            $this->getCache()->remove($path);
223
+            return true;
224
+        }
225
+        return false;
226
+    }
227
+
228
+    public function stat($path) {
229
+        $path = $this->normalizePath($path);
230
+        $cacheEntry = $this->getCache()->get($path);
231
+        if ($cacheEntry instanceof CacheEntry) {
232
+            return $cacheEntry->getData();
233
+        } else {
234
+            return false;
235
+        }
236
+    }
237
+
238
+    public function getPermissions($path) {
239
+        $stat = $this->stat($path);
240
+
241
+        if (is_array($stat) && isset($stat['permissions'])) {
242
+            return $stat['permissions'];
243
+        }
244
+
245
+        return parent::getPermissions($path);
246
+    }
247
+
248
+    /**
249
+     * Override this method if you need a different unique resource identifier for your object storage implementation.
250
+     * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
251
+     * You may need a mapping table to store your URN if it cannot be generated from the fileid.
252
+     *
253
+     * @param int $fileId the fileid
254
+     * @return null|string the unified resource name used to identify the object
255
+     */
256
+    public function getURN($fileId) {
257
+        if (is_numeric($fileId)) {
258
+            return $this->objectPrefix . $fileId;
259
+        }
260
+        return null;
261
+    }
262
+
263
+    public function opendir($path) {
264
+        $path = $this->normalizePath($path);
265
+
266
+        try {
267
+            $files = [];
268
+            $folderContents = $this->getCache()->getFolderContents($path);
269
+            foreach ($folderContents as $file) {
270
+                $files[] = $file['name'];
271
+            }
272
+
273
+            return IteratorDirectory::wrap($files);
274
+        } catch (\Exception $e) {
275
+            $this->logger->logException($e);
276
+            return false;
277
+        }
278
+    }
279
+
280
+    public function filetype($path) {
281
+        $path = $this->normalizePath($path);
282
+        $stat = $this->stat($path);
283
+        if ($stat) {
284
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
285
+                return 'dir';
286
+            }
287
+            return 'file';
288
+        } else {
289
+            return false;
290
+        }
291
+    }
292
+
293
+    public function fopen($path, $mode) {
294
+        $path = $this->normalizePath($path);
295
+
296
+        if (strrpos($path, '.') !== false) {
297
+            $ext = substr($path, strrpos($path, '.'));
298
+        } else {
299
+            $ext = '';
300
+        }
301
+
302
+        switch ($mode) {
303
+            case 'r':
304
+            case 'rb':
305
+                $stat = $this->stat($path);
306
+                if (is_array($stat)) {
307
+                    // Reading 0 sized files is a waste of time
308
+                    if (isset($stat['size']) && $stat['size'] === 0) {
309
+                        return fopen('php://memory', $mode);
310
+                    }
311
+
312
+                    try {
313
+                        return $this->objectStore->readObject($this->getURN($stat['fileid']));
314
+                    } catch (NotFoundException $e) {
315
+                        $this->logger->logException($e, [
316
+                            'app' => 'objectstore',
317
+                            'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
318
+                        ]);
319
+                        throw $e;
320
+                    } catch (\Exception $ex) {
321
+                        $this->logger->logException($ex, [
322
+                            'app' => 'objectstore',
323
+                            'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
324
+                        ]);
325
+                        return false;
326
+                    }
327
+                } else {
328
+                    return false;
329
+                }
330
+            // no break
331
+            case 'w':
332
+            case 'wb':
333
+            case 'w+':
334
+            case 'wb+':
335
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
336
+                $handle = fopen($tmpFile, $mode);
337
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
338
+                    $this->writeBack($tmpFile, $path);
339
+                });
340
+            case 'a':
341
+            case 'ab':
342
+            case 'r+':
343
+            case 'a+':
344
+            case 'x':
345
+            case 'x+':
346
+            case 'c':
347
+            case 'c+':
348
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
349
+                if ($this->file_exists($path)) {
350
+                    $source = $this->fopen($path, 'r');
351
+                    file_put_contents($tmpFile, $source);
352
+                }
353
+                $handle = fopen($tmpFile, $mode);
354
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
355
+                    $this->writeBack($tmpFile, $path);
356
+                });
357
+        }
358
+        return false;
359
+    }
360
+
361
+    public function file_exists($path) {
362
+        $path = $this->normalizePath($path);
363
+        return (bool)$this->stat($path);
364
+    }
365
+
366
+    public function rename($source, $target) {
367
+        $source = $this->normalizePath($source);
368
+        $target = $this->normalizePath($target);
369
+        $this->remove($target);
370
+        $this->getCache()->move($source, $target);
371
+        $this->touch(dirname($target));
372
+        return true;
373
+    }
374
+
375
+    public function getMimeType($path) {
376
+        $path = $this->normalizePath($path);
377
+        return parent::getMimeType($path);
378
+    }
379
+
380
+    public function touch($path, $mtime = null) {
381
+        if (is_null($mtime)) {
382
+            $mtime = time();
383
+        }
384
+
385
+        $path = $this->normalizePath($path);
386
+        $dirName = dirname($path);
387
+        $parentExists = $this->is_dir($dirName);
388
+        if (!$parentExists) {
389
+            return false;
390
+        }
391
+
392
+        $stat = $this->stat($path);
393
+        if (is_array($stat)) {
394
+            // update existing mtime in db
395
+            $stat['mtime'] = $mtime;
396
+            $this->getCache()->update($stat['fileid'], $stat);
397
+        } else {
398
+            try {
399
+                //create a empty file, need to have at least on char to make it
400
+                // work with all object storage implementations
401
+                $this->file_put_contents($path, ' ');
402
+                $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
403
+                $stat = [
404
+                    'etag' => $this->getETag($path),
405
+                    'mimetype' => $mimeType,
406
+                    'size' => 0,
407
+                    'mtime' => $mtime,
408
+                    'storage_mtime' => $mtime,
409
+                    'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
410
+                ];
411
+                $this->getCache()->put($path, $stat);
412
+            } catch (\Exception $ex) {
413
+                $this->logger->logException($ex, [
414
+                    'app' => 'objectstore',
415
+                    'message' => 'Could not create object for ' . $path,
416
+                ]);
417
+                throw $ex;
418
+            }
419
+        }
420
+        return true;
421
+    }
422
+
423
+    public function writeBack($tmpFile, $path) {
424
+        $size = filesize($tmpFile);
425
+        $this->writeStream($path, fopen($tmpFile, 'r'), $size);
426
+    }
427
+
428
+    /**
429
+     * external changes are not supported, exclusive access to the object storage is assumed
430
+     *
431
+     * @param string $path
432
+     * @param int $time
433
+     * @return false
434
+     */
435
+    public function hasUpdated($path, $time) {
436
+        return false;
437
+    }
438
+
439
+    public function needsPartFile() {
440
+        return false;
441
+    }
442
+
443
+    public function file_put_contents($path, $data) {
444
+        $handle = $this->fopen($path, 'w+');
445
+        $result = fwrite($handle, $data);
446
+        fclose($handle);
447
+        return $result;
448
+    }
449
+
450
+    public function writeStream(string $path, $stream, int $size = null): int {
451
+        $stat = $this->stat($path);
452
+        if (empty($stat)) {
453
+            // create new file
454
+            $stat = [
455
+                'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
456
+            ];
457
+        }
458
+        // update stat with new data
459
+        $mTime = time();
460
+        $stat['size'] = (int)$size;
461
+        $stat['mtime'] = $mTime;
462
+        $stat['storage_mtime'] = $mTime;
463
+
464
+        $mimetypeDetector = \OC::$server->getMimeTypeDetector();
465
+        $mimetype = $mimetypeDetector->detectPath($path);
466
+
467
+        $stat['mimetype'] = $mimetype;
468
+        $stat['etag'] = $this->getETag($path);
469
+
470
+        $exists = $this->getCache()->inCache($path);
471
+        $uploadPath = $exists ? $path : $path . '.part';
472
+
473
+        if ($exists) {
474
+            $fileId = $stat['fileid'];
475
+        } else {
476
+            $fileId = $this->getCache()->put($uploadPath, $stat);
477
+        }
478
+
479
+        $urn = $this->getURN($fileId);
480
+        try {
481
+            //upload to object storage
482
+            if ($size === null) {
483
+                $countStream = CountWrapper::wrap($stream, function ($writtenSize) use ($fileId, &$size) {
484
+                    $this->getCache()->update($fileId, [
485
+                        'size' => $writtenSize,
486
+                    ]);
487
+                    $size = $writtenSize;
488
+                });
489
+                $this->objectStore->writeObject($urn, $countStream);
490
+                if (is_resource($countStream)) {
491
+                    fclose($countStream);
492
+                }
493
+                $stat['size'] = $size;
494
+            } else {
495
+                $this->objectStore->writeObject($urn, $stream);
496
+            }
497
+        } catch (\Exception $ex) {
498
+            if (!$exists) {
499
+                /*
500 500
 				 * Only remove the entry if we are dealing with a new file.
501 501
 				 * Else people lose access to existing files
502 502
 				 */
503
-				$this->getCache()->remove($uploadPath);
504
-				$this->logger->logException($ex, [
505
-					'app' => 'objectstore',
506
-					'message' => 'Could not create object ' . $urn . ' for ' . $path,
507
-				]);
508
-			} else {
509
-				$this->logger->logException($ex, [
510
-					'app' => 'objectstore',
511
-					'message' => 'Could not update object ' . $urn . ' for ' . $path,
512
-				]);
513
-			}
514
-			throw $ex; // make this bubble up
515
-		}
516
-
517
-		if ($exists) {
518
-			$this->getCache()->update($fileId, $stat);
519
-		} else {
520
-			if ($this->objectStore->objectExists($urn)) {
521
-				$this->getCache()->move($uploadPath, $path);
522
-			} else {
523
-				$this->getCache()->remove($uploadPath);
524
-				throw new \Exception("Object not found after writing (urn: $urn, path: $path)", 404);
525
-			}
526
-		}
527
-
528
-		return $size;
529
-	}
530
-
531
-	public function getObjectStore(): IObjectStore {
532
-		return $this->objectStore;
533
-	}
534
-
535
-	public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
536
-		if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) {
537
-			/** @var ObjectStoreStorage $sourceStorage */
538
-			if ($sourceStorage->getObjectStore()->getStorageId() === $this->getObjectStore()->getStorageId()) {
539
-				$sourceEntry = $sourceStorage->getCache()->get($sourceInternalPath);
540
-				$this->copyInner($sourceEntry, $targetInternalPath);
541
-				return true;
542
-			}
543
-		}
544
-
545
-		return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
546
-	}
547
-
548
-	public function copy($path1, $path2) {
549
-		$path1 = $this->normalizePath($path1);
550
-		$path2 = $this->normalizePath($path2);
551
-
552
-		$cache = $this->getCache();
553
-		$sourceEntry = $cache->get($path1);
554
-		if (!$sourceEntry) {
555
-			throw new NotFoundException('Source object not found');
556
-		}
557
-
558
-		$this->copyInner($sourceEntry, $path2);
559
-
560
-		return true;
561
-	}
562
-
563
-	private function copyInner(ICacheEntry $sourceEntry, string $to) {
564
-		$cache = $this->getCache();
565
-
566
-		if ($sourceEntry->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
567
-			if ($cache->inCache($to)) {
568
-				$cache->remove($to);
569
-			}
570
-			$this->mkdir($to);
571
-
572
-			foreach ($cache->getFolderContentsById($sourceEntry->getId()) as $child) {
573
-				$this->copyInner($child, $to . '/' . $child->getName());
574
-			}
575
-		} else {
576
-			$this->copyFile($sourceEntry, $to);
577
-		}
578
-	}
579
-
580
-	private function copyFile(ICacheEntry $sourceEntry, string $to) {
581
-		$cache = $this->getCache();
582
-
583
-		$sourceUrn = $this->getURN($sourceEntry->getId());
584
-
585
-		if (!$cache instanceof Cache) {
586
-			throw new \Exception("Invalid source cache for object store copy");
587
-		}
588
-
589
-		$targetId = $cache->copyFromCache($cache, $sourceEntry, $to);
590
-
591
-		$targetUrn = $this->getURN($targetId);
592
-
593
-		try {
594
-			$this->objectStore->copyObject($sourceUrn, $targetUrn);
595
-		} catch (\Exception $e) {
596
-			$cache->remove($to);
597
-
598
-			throw $e;
599
-		}
600
-	}
503
+                $this->getCache()->remove($uploadPath);
504
+                $this->logger->logException($ex, [
505
+                    'app' => 'objectstore',
506
+                    'message' => 'Could not create object ' . $urn . ' for ' . $path,
507
+                ]);
508
+            } else {
509
+                $this->logger->logException($ex, [
510
+                    'app' => 'objectstore',
511
+                    'message' => 'Could not update object ' . $urn . ' for ' . $path,
512
+                ]);
513
+            }
514
+            throw $ex; // make this bubble up
515
+        }
516
+
517
+        if ($exists) {
518
+            $this->getCache()->update($fileId, $stat);
519
+        } else {
520
+            if ($this->objectStore->objectExists($urn)) {
521
+                $this->getCache()->move($uploadPath, $path);
522
+            } else {
523
+                $this->getCache()->remove($uploadPath);
524
+                throw new \Exception("Object not found after writing (urn: $urn, path: $path)", 404);
525
+            }
526
+        }
527
+
528
+        return $size;
529
+    }
530
+
531
+    public function getObjectStore(): IObjectStore {
532
+        return $this->objectStore;
533
+    }
534
+
535
+    public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
536
+        if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) {
537
+            /** @var ObjectStoreStorage $sourceStorage */
538
+            if ($sourceStorage->getObjectStore()->getStorageId() === $this->getObjectStore()->getStorageId()) {
539
+                $sourceEntry = $sourceStorage->getCache()->get($sourceInternalPath);
540
+                $this->copyInner($sourceEntry, $targetInternalPath);
541
+                return true;
542
+            }
543
+        }
544
+
545
+        return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
546
+    }
547
+
548
+    public function copy($path1, $path2) {
549
+        $path1 = $this->normalizePath($path1);
550
+        $path2 = $this->normalizePath($path2);
551
+
552
+        $cache = $this->getCache();
553
+        $sourceEntry = $cache->get($path1);
554
+        if (!$sourceEntry) {
555
+            throw new NotFoundException('Source object not found');
556
+        }
557
+
558
+        $this->copyInner($sourceEntry, $path2);
559
+
560
+        return true;
561
+    }
562
+
563
+    private function copyInner(ICacheEntry $sourceEntry, string $to) {
564
+        $cache = $this->getCache();
565
+
566
+        if ($sourceEntry->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
567
+            if ($cache->inCache($to)) {
568
+                $cache->remove($to);
569
+            }
570
+            $this->mkdir($to);
571
+
572
+            foreach ($cache->getFolderContentsById($sourceEntry->getId()) as $child) {
573
+                $this->copyInner($child, $to . '/' . $child->getName());
574
+            }
575
+        } else {
576
+            $this->copyFile($sourceEntry, $to);
577
+        }
578
+    }
579
+
580
+    private function copyFile(ICacheEntry $sourceEntry, string $to) {
581
+        $cache = $this->getCache();
582
+
583
+        $sourceUrn = $this->getURN($sourceEntry->getId());
584
+
585
+        if (!$cache instanceof Cache) {
586
+            throw new \Exception("Invalid source cache for object store copy");
587
+        }
588
+
589
+        $targetId = $cache->copyFromCache($cache, $sourceEntry, $to);
590
+
591
+        $targetUrn = $this->getURN($targetId);
592
+
593
+        try {
594
+            $this->objectStore->copyObject($sourceUrn, $targetUrn);
595
+        } catch (\Exception $e) {
596
+            $cache->remove($to);
597
+
598
+            throw $e;
599
+        }
600
+    }
601 601
 }
Please login to merge, or discard this patch.
lib/private/Lockdown/Filesystem/NullCache.php 1 patch
Indentation   +96 added lines, -96 removed lines patch added patch discarded remove patch
@@ -31,100 +31,100 @@
 block discarded – undo
31 31
 use OCP\Files\Search\ISearchQuery;
32 32
 
33 33
 class NullCache implements ICache {
34
-	public function getNumericStorageId() {
35
-		return -1;
36
-	}
37
-
38
-	public function get($file) {
39
-		return $file !== '' ? null :
40
-			new CacheEntry([
41
-				'fileid' => -1,
42
-				'parent' => -1,
43
-				'name' => '',
44
-				'path' => '',
45
-				'size' => '0',
46
-				'mtime' => time(),
47
-				'storage_mtime' => time(),
48
-				'etag' => '',
49
-				'mimetype' => FileInfo::MIMETYPE_FOLDER,
50
-				'mimepart' => 'httpd',
51
-				'permissions' => Constants::PERMISSION_READ
52
-			]);
53
-	}
54
-
55
-	public function getFolderContents($folder) {
56
-		return [];
57
-	}
58
-
59
-	public function getFolderContentsById($fileId) {
60
-		return [];
61
-	}
62
-
63
-	public function put($file, array $data) {
64
-		throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
65
-	}
66
-
67
-	public function insert($file, array $data) {
68
-		throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
69
-	}
70
-
71
-	public function update($id, array $data) {
72
-		throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
73
-	}
74
-
75
-	public function getId($file) {
76
-		return -1;
77
-	}
78
-
79
-	public function getParentId($file) {
80
-		return -1;
81
-	}
82
-
83
-	public function inCache($file) {
84
-		return $file === '';
85
-	}
86
-
87
-	public function remove($file) {
88
-		throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
89
-	}
90
-
91
-	public function move($source, $target) {
92
-		throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
93
-	}
94
-
95
-	public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
96
-		throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
97
-	}
98
-
99
-	public function getStatus($file) {
100
-		return ICache::COMPLETE;
101
-	}
102
-
103
-	public function search($pattern) {
104
-		return [];
105
-	}
106
-
107
-	public function searchByMime($mimetype) {
108
-		return [];
109
-	}
110
-
111
-	public function searchQuery(ISearchQuery $query) {
112
-		return [];
113
-	}
114
-
115
-	public function getIncomplete() {
116
-		return [];
117
-	}
118
-
119
-	public function getPathById($id) {
120
-		return '';
121
-	}
122
-
123
-	public function normalize($path) {
124
-		return $path;
125
-	}
126
-
127
-	public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int {
128
-		throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
129
-	}
34
+    public function getNumericStorageId() {
35
+        return -1;
36
+    }
37
+
38
+    public function get($file) {
39
+        return $file !== '' ? null :
40
+            new CacheEntry([
41
+                'fileid' => -1,
42
+                'parent' => -1,
43
+                'name' => '',
44
+                'path' => '',
45
+                'size' => '0',
46
+                'mtime' => time(),
47
+                'storage_mtime' => time(),
48
+                'etag' => '',
49
+                'mimetype' => FileInfo::MIMETYPE_FOLDER,
50
+                'mimepart' => 'httpd',
51
+                'permissions' => Constants::PERMISSION_READ
52
+            ]);
53
+    }
54
+
55
+    public function getFolderContents($folder) {
56
+        return [];
57
+    }
58
+
59
+    public function getFolderContentsById($fileId) {
60
+        return [];
61
+    }
62
+
63
+    public function put($file, array $data) {
64
+        throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
65
+    }
66
+
67
+    public function insert($file, array $data) {
68
+        throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
69
+    }
70
+
71
+    public function update($id, array $data) {
72
+        throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
73
+    }
74
+
75
+    public function getId($file) {
76
+        return -1;
77
+    }
78
+
79
+    public function getParentId($file) {
80
+        return -1;
81
+    }
82
+
83
+    public function inCache($file) {
84
+        return $file === '';
85
+    }
86
+
87
+    public function remove($file) {
88
+        throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
89
+    }
90
+
91
+    public function move($source, $target) {
92
+        throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
93
+    }
94
+
95
+    public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
96
+        throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
97
+    }
98
+
99
+    public function getStatus($file) {
100
+        return ICache::COMPLETE;
101
+    }
102
+
103
+    public function search($pattern) {
104
+        return [];
105
+    }
106
+
107
+    public function searchByMime($mimetype) {
108
+        return [];
109
+    }
110
+
111
+    public function searchQuery(ISearchQuery $query) {
112
+        return [];
113
+    }
114
+
115
+    public function getIncomplete() {
116
+        return [];
117
+    }
118
+
119
+    public function getPathById($id) {
120
+        return '';
121
+    }
122
+
123
+    public function normalize($path) {
124
+        return $path;
125
+    }
126
+
127
+    public function copyFromCache(ICache $sourceCache, ICacheEntry $sourceEntry, string $targetPath): int {
128
+        throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
129
+    }
130 130
 }
Please login to merge, or discard this patch.