Completed
Push — master ( 8e5f43...9d98f8 )
by Marcel
43:53
created
tests/lib/Files/Cache/FileAccessTest.php 1 patch
Indentation   +424 added lines, -424 removed lines patch added patch discarded remove patch
@@ -23,428 +23,428 @@
 block discarded – undo
23 23
  * @group DB
24 24
  */
25 25
 class FileAccessTest extends TestCase {
26
-	private IDBConnection $dbConnection;
27
-	private FileAccess $fileAccess;
28
-
29
-	protected function setUp(): void {
30
-		parent::setUp();
31
-
32
-		// Setup the actual database connection (assume the database is configured properly in PHPUnit setup)
33
-		$this->dbConnection = Server::get(IDBConnection::class);
34
-
35
-		// Ensure FileAccess is instantiated with the real connection
36
-		$this->fileAccess = new FileAccess(
37
-			$this->dbConnection,
38
-			Server::get(SystemConfig::class),
39
-			Server::get(LoggerInterface::class),
40
-			Server::get(FilesMetadataManager::class),
41
-			Server::get(IMimeTypeLoader::class)
42
-		);
43
-
44
-		// Clear and prepare `filecache` table for tests
45
-		$queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards();
46
-		$queryBuilder->delete('filecache')->executeStatement();
47
-
48
-		// Clean up potential leftovers from other tests
49
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
50
-		$queryBuilder->delete('mounts')->executeStatement();
51
-
52
-
53
-		$this->setUpTestDatabaseForGetDistinctMounts();
54
-		$this->setUpTestDatabaseForGetByAncestorInStorage();
55
-	}
56
-
57
-	private function setUpTestDatabaseForGetDistinctMounts(): void {
58
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
59
-
60
-		// Insert test data
61
-		$queryBuilder->insert('mounts')
62
-			->values([
63
-				'storage_id' => $queryBuilder->createNamedParameter(1, IQueryBuilder::PARAM_INT),
64
-				'root_id' => $queryBuilder->createNamedParameter(10, IQueryBuilder::PARAM_INT),
65
-				'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
66
-				'mount_point' => $queryBuilder->createNamedParameter('/files'),
67
-				'user_id' => $queryBuilder->createNamedParameter('test'),
68
-			])
69
-			->executeStatement();
70
-
71
-		$queryBuilder->insert('mounts')
72
-			->values([
73
-				'storage_id' => $queryBuilder->createNamedParameter(3, IQueryBuilder::PARAM_INT),
74
-				'root_id' => $queryBuilder->createNamedParameter(30, IQueryBuilder::PARAM_INT),
75
-				'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
76
-				'mount_point' => $queryBuilder->createNamedParameter('/documents'),
77
-				'user_id' => $queryBuilder->createNamedParameter('test'),
78
-			])
79
-			->executeStatement();
80
-
81
-		$queryBuilder->insert('mounts')
82
-			->values([
83
-				'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
84
-				'root_id' => $queryBuilder->createNamedParameter(31, IQueryBuilder::PARAM_INT),
85
-				'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass2'),
86
-				'mount_point' => $queryBuilder->createNamedParameter('/foobar'),
87
-				'user_id' => $queryBuilder->createNamedParameter('test'),
88
-			])
89
-			->executeStatement();
90
-	}
91
-
92
-	/**
93
-	 * Test that getDistinctMounts returns all mounts without filters
94
-	 */
95
-	public function testGetDistinctMountsWithoutFilters(): void {
96
-		$result = iterator_to_array($this->fileAccess->getDistinctMounts([], false));
97
-
98
-		$this->assertCount(3, $result);
99
-
100
-		$this->assertEquals([
101
-			'storage_id' => 1,
102
-			'root_id' => 10,
103
-			'overridden_root' => 10,
104
-		], $result[0]);
105
-
106
-		$this->assertEquals([
107
-			'storage_id' => 3,
108
-			'root_id' => 30,
109
-			'overridden_root' => 30,
110
-		], $result[1]);
111
-
112
-		$this->assertEquals([
113
-			'storage_id' => 4,
114
-			'root_id' => 31,
115
-			'overridden_root' => 31,
116
-		], $result[2]);
117
-	}
118
-
119
-	/**
120
-	 * Test that getDistinctMounts applies filtering by mount providers
121
-	 */
122
-	public function testGetDistinctMountsWithMountProviderFilter(): void {
123
-		$result = iterator_to_array($this->fileAccess->getDistinctMounts(['TestProviderClass1'], false));
124
-
125
-		$this->assertCount(2, $result);
126
-
127
-		$this->assertEquals([
128
-			'storage_id' => 1,
129
-			'root_id' => 10,
130
-			'overridden_root' => 10,
131
-		], $result[0]);
132
-
133
-		$this->assertEquals([
134
-			'storage_id' => 3,
135
-			'root_id' => 30,
136
-			'overridden_root' => 30,
137
-		], $result[1]);
138
-	}
139
-
140
-	/**
141
-	 * Test that getDistinctMounts rewrites home directory paths
142
-	 */
143
-	public function testGetDistinctMountsWithRewriteHomeDirectories(): void {
144
-		// Add additional test data for a home directory mount
145
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
146
-		$queryBuilder->insert('mounts')
147
-			->values([
148
-				'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
149
-				'root_id' => $queryBuilder->createNamedParameter(40, IQueryBuilder::PARAM_INT),
150
-				'mount_provider_class' => $queryBuilder->createNamedParameter(LocalHomeMountProvider::class),
151
-				'mount_point' => $queryBuilder->createNamedParameter('/home/user'),
152
-				'user_id' => $queryBuilder->createNamedParameter('test'),
153
-			])
154
-			->executeStatement();
155
-
156
-		// Add a mount that is mounted in the home directory
157
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
158
-		$queryBuilder->insert('mounts')
159
-			->values([
160
-				'storage_id' => $queryBuilder->createNamedParameter(5, IQueryBuilder::PARAM_INT),
161
-				'root_id' => $queryBuilder->createNamedParameter(41, IQueryBuilder::PARAM_INT),
162
-				'mount_provider_class' => $queryBuilder->createNamedParameter('TestMountProvider3'),
163
-				'mount_point' => $queryBuilder->createNamedParameter('/test/files/foobar'),
164
-				'user_id' => $queryBuilder->createNamedParameter('test'),
165
-			])
166
-			->executeStatement();
167
-
168
-		// Simulate adding a "files" directory to the filecache table
169
-		$queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards();
170
-		$queryBuilder->delete('filecache')->executeStatement();
171
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
172
-		$queryBuilder->insert('filecache')
173
-			->values([
174
-				'fileid' => $queryBuilder->createNamedParameter(99, IQueryBuilder::PARAM_INT),
175
-				'storage' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
176
-				'parent' => $queryBuilder->createNamedParameter(40),
177
-				'name' => $queryBuilder->createNamedParameter('files'),
178
-				'path' => $queryBuilder->createNamedParameter('files'),
179
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files')),
180
-			])
181
-			->executeStatement();
182
-
183
-		$result = iterator_to_array($this->fileAccess->getDistinctMounts());
184
-
185
-		$this->assertCount(2, $result);
186
-
187
-		$this->assertEquals([
188
-			'storage_id' => 4,
189
-			'root_id' => 40,
190
-			'overridden_root' => 99,
191
-		], $result[0]);
192
-
193
-		$this->assertEquals([
194
-			'storage_id' => 5,
195
-			'root_id' => 41,
196
-			'overridden_root' => 41,
197
-		], $result[1]);
198
-	}
199
-
200
-	private function setUpTestDatabaseForGetByAncestorInStorage(): void {
201
-		// prepare `filecache` table for tests
202
-		$queryBuilder = $this->dbConnection->getQueryBuilder();
203
-
204
-		$queryBuilder->insert('filecache')
205
-			->values([
206
-				'fileid' => 1,
207
-				'parent' => 0,
208
-				'path' => $queryBuilder->createNamedParameter('files'),
209
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files')),
210
-				'storage' => $queryBuilder->createNamedParameter(1),
211
-				'name' => $queryBuilder->createNamedParameter('files'),
212
-				'mimetype' => 1,
213
-				'encrypted' => 0,
214
-				'size' => 1,
215
-			])
216
-			->executeStatement();
217
-
218
-		$queryBuilder->insert('filecache')
219
-			->values([
220
-				'fileid' => 2,
221
-				'parent' => 1,
222
-				'path' => $queryBuilder->createNamedParameter('files/documents'),
223
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/documents')),
224
-				'storage' => $queryBuilder->createNamedParameter(1),
225
-				'name' => $queryBuilder->createNamedParameter('documents'),
226
-				'mimetype' => 2,
227
-				'encrypted' => 1,
228
-				'size' => 1,
229
-			])
230
-			->executeStatement();
231
-
232
-		$queryBuilder->insert('filecache')
233
-			->values([
234
-				'fileid' => 3,
235
-				'parent' => 1,
236
-				'path' => $queryBuilder->createNamedParameter('files/photos'),
237
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos')),
238
-				'storage' => $queryBuilder->createNamedParameter(1),
239
-				'name' => $queryBuilder->createNamedParameter('photos'),
240
-				'mimetype' => 3,
241
-				'encrypted' => 1,
242
-				'size' => 1,
243
-			])
244
-			->executeStatement();
245
-
246
-		$queryBuilder->insert('filecache')
247
-			->values([
248
-				'fileid' => 4,
249
-				'parent' => 3,
250
-				'path' => $queryBuilder->createNamedParameter('files/photos/endtoendencrypted'),
251
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos/endtoendencrypted')),
252
-				'storage' => $queryBuilder->createNamedParameter(1),
253
-				'name' => $queryBuilder->createNamedParameter('endtoendencrypted'),
254
-				'mimetype' => 4,
255
-				'encrypted' => 0,
256
-				'size' => 1,
257
-			])
258
-			->executeStatement();
259
-
260
-		$queryBuilder->insert('filecache')
261
-			->values([
262
-				'fileid' => 5,
263
-				'parent' => 1,
264
-				'path' => $queryBuilder->createNamedParameter('files/serversideencrypted'),
265
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/serversideencrypted')),
266
-				'storage' => $queryBuilder->createNamedParameter(1),
267
-				'name' => $queryBuilder->createNamedParameter('serversideencrypted'),
268
-				'mimetype' => 4,
269
-				'encrypted' => 1,
270
-				'size' => 1,
271
-			])
272
-			->executeStatement();
273
-
274
-		$queryBuilder->insert('filecache')
275
-			->values([
276
-				'fileid' => 6,
277
-				'parent' => 0,
278
-				'path' => $queryBuilder->createNamedParameter('files/storage2'),
279
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2')),
280
-				'storage' => $queryBuilder->createNamedParameter(2),
281
-				'name' => $queryBuilder->createNamedParameter('storage2'),
282
-				'mimetype' => 5,
283
-				'encrypted' => 0,
284
-				'size' => 1,
285
-			])
286
-			->executeStatement();
287
-
288
-		$queryBuilder->insert('filecache')
289
-			->values([
290
-				'fileid' => 7,
291
-				'parent' => 6,
292
-				'path' => $queryBuilder->createNamedParameter('files/storage2/file'),
293
-				'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2/file')),
294
-				'storage' => $queryBuilder->createNamedParameter(2),
295
-				'name' => $queryBuilder->createNamedParameter('file'),
296
-				'mimetype' => 6,
297
-				'encrypted' => 0,
298
-				'size' => 1,
299
-			])
300
-			->executeStatement();
301
-	}
302
-
303
-	/**
304
-	 * Test fetching files by ancestor in storage.
305
-	 */
306
-	public function testGetByAncestorInStorage(): void {
307
-		$generator = $this->fileAccess->getByAncestorInStorage(
308
-			1, // storageId
309
-			1, // rootId
310
-			0, // lastFileId
311
-			10, // maxResults
312
-			[], // mimeTypes
313
-			true, // include end-to-end encrypted files
314
-			true, // include server-side encrypted files
315
-		);
316
-
317
-		$result = iterator_to_array($generator);
318
-
319
-		$this->assertCount(4, $result);
320
-
321
-		$paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result);
322
-		$this->assertEquals([
323
-			'files/documents',
324
-			'files/photos',
325
-			'files/photos/endtoendencrypted',
326
-			'files/serversideencrypted',
327
-		], $paths);
328
-	}
329
-
330
-	/**
331
-	 * Test filtering by mime types.
332
-	 */
333
-	public function testGetByAncestorInStorageWithMimeTypes(): void {
334
-		$generator = $this->fileAccess->getByAncestorInStorage(
335
-			1,
336
-			1,
337
-			0,
338
-			10,
339
-			[2], // Only include documents (mimetype=2)
340
-			true,
341
-			true,
342
-		);
343
-
344
-		$result = iterator_to_array($generator);
345
-
346
-		$this->assertCount(1, $result);
347
-		$this->assertEquals('files/documents', $result[0]->getPath());
348
-	}
349
-
350
-	/**
351
-	 * Test excluding end-to-end encrypted files.
352
-	 */
353
-	public function testGetByAncestorInStorageWithoutEndToEndEncrypted(): void {
354
-		$generator = $this->fileAccess->getByAncestorInStorage(
355
-			1,
356
-			1,
357
-			0,
358
-			10,
359
-			[],
360
-			false, // exclude end-to-end encrypted files
361
-			true,
362
-		);
363
-
364
-		$result = iterator_to_array($generator);
365
-
366
-		$this->assertCount(3, $result);
367
-		$paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result);
368
-		$this->assertEquals(['files/documents', 'files/photos', 'files/serversideencrypted'], $paths);
369
-	}
370
-
371
-	/**
372
-	 * Test excluding server-side encrypted files.
373
-	 */
374
-	public function testGetByAncestorInStorageWithoutServerSideEncrypted(): void {
375
-		$generator = $this->fileAccess->getByAncestorInStorage(
376
-			1,
377
-			1,
378
-			0,
379
-			10,
380
-			[],
381
-			true,
382
-			false, // exclude server-side encrypted files
383
-		);
384
-
385
-		$result = iterator_to_array($generator);
386
-
387
-		$this->assertCount(1, $result);
388
-		$this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath());
389
-	}
390
-
391
-	/**
392
-	 * Test max result limits.
393
-	 */
394
-	public function testGetByAncestorInStorageWithMaxResults(): void {
395
-		$generator = $this->fileAccess->getByAncestorInStorage(
396
-			1,
397
-			1,
398
-			0,
399
-			1, // Limit to 1 result
400
-			[],
401
-			true,
402
-			true,
403
-		);
404
-
405
-		$result = iterator_to_array($generator);
406
-
407
-		$this->assertCount(1, $result);
408
-		$this->assertEquals('files/documents', $result[0]->getPath());
409
-	}
410
-
411
-	/**
412
-	 * Test rootId filter
413
-	 */
414
-	public function testGetByAncestorInStorageWithRootIdFilter(): void {
415
-		$generator = $this->fileAccess->getByAncestorInStorage(
416
-			1,
417
-			3, // Filter by rootId
418
-			0,
419
-			10,
420
-			[],
421
-			true,
422
-			true,
423
-		);
424
-
425
-		$result = iterator_to_array($generator);
426
-
427
-		$this->assertCount(1, $result);
428
-		$this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath());
429
-	}
430
-
431
-	/**
432
-	 * Test rootId filter
433
-	 */
434
-	public function testGetByAncestorInStorageWithStorageFilter(): void {
435
-		$generator = $this->fileAccess->getByAncestorInStorage(
436
-			2, // Filter by storage
437
-			6, // and by rootId
438
-			0,
439
-			10,
440
-			[],
441
-			true,
442
-			true,
443
-		);
444
-
445
-		$result = iterator_to_array($generator);
446
-
447
-		$this->assertCount(1, $result);
448
-		$this->assertEquals('files/storage2/file', $result[0]->getPath());
449
-	}
26
+    private IDBConnection $dbConnection;
27
+    private FileAccess $fileAccess;
28
+
29
+    protected function setUp(): void {
30
+        parent::setUp();
31
+
32
+        // Setup the actual database connection (assume the database is configured properly in PHPUnit setup)
33
+        $this->dbConnection = Server::get(IDBConnection::class);
34
+
35
+        // Ensure FileAccess is instantiated with the real connection
36
+        $this->fileAccess = new FileAccess(
37
+            $this->dbConnection,
38
+            Server::get(SystemConfig::class),
39
+            Server::get(LoggerInterface::class),
40
+            Server::get(FilesMetadataManager::class),
41
+            Server::get(IMimeTypeLoader::class)
42
+        );
43
+
44
+        // Clear and prepare `filecache` table for tests
45
+        $queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards();
46
+        $queryBuilder->delete('filecache')->executeStatement();
47
+
48
+        // Clean up potential leftovers from other tests
49
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
50
+        $queryBuilder->delete('mounts')->executeStatement();
51
+
52
+
53
+        $this->setUpTestDatabaseForGetDistinctMounts();
54
+        $this->setUpTestDatabaseForGetByAncestorInStorage();
55
+    }
56
+
57
+    private function setUpTestDatabaseForGetDistinctMounts(): void {
58
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
59
+
60
+        // Insert test data
61
+        $queryBuilder->insert('mounts')
62
+            ->values([
63
+                'storage_id' => $queryBuilder->createNamedParameter(1, IQueryBuilder::PARAM_INT),
64
+                'root_id' => $queryBuilder->createNamedParameter(10, IQueryBuilder::PARAM_INT),
65
+                'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
66
+                'mount_point' => $queryBuilder->createNamedParameter('/files'),
67
+                'user_id' => $queryBuilder->createNamedParameter('test'),
68
+            ])
69
+            ->executeStatement();
70
+
71
+        $queryBuilder->insert('mounts')
72
+            ->values([
73
+                'storage_id' => $queryBuilder->createNamedParameter(3, IQueryBuilder::PARAM_INT),
74
+                'root_id' => $queryBuilder->createNamedParameter(30, IQueryBuilder::PARAM_INT),
75
+                'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
76
+                'mount_point' => $queryBuilder->createNamedParameter('/documents'),
77
+                'user_id' => $queryBuilder->createNamedParameter('test'),
78
+            ])
79
+            ->executeStatement();
80
+
81
+        $queryBuilder->insert('mounts')
82
+            ->values([
83
+                'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
84
+                'root_id' => $queryBuilder->createNamedParameter(31, IQueryBuilder::PARAM_INT),
85
+                'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass2'),
86
+                'mount_point' => $queryBuilder->createNamedParameter('/foobar'),
87
+                'user_id' => $queryBuilder->createNamedParameter('test'),
88
+            ])
89
+            ->executeStatement();
90
+    }
91
+
92
+    /**
93
+     * Test that getDistinctMounts returns all mounts without filters
94
+     */
95
+    public function testGetDistinctMountsWithoutFilters(): void {
96
+        $result = iterator_to_array($this->fileAccess->getDistinctMounts([], false));
97
+
98
+        $this->assertCount(3, $result);
99
+
100
+        $this->assertEquals([
101
+            'storage_id' => 1,
102
+            'root_id' => 10,
103
+            'overridden_root' => 10,
104
+        ], $result[0]);
105
+
106
+        $this->assertEquals([
107
+            'storage_id' => 3,
108
+            'root_id' => 30,
109
+            'overridden_root' => 30,
110
+        ], $result[1]);
111
+
112
+        $this->assertEquals([
113
+            'storage_id' => 4,
114
+            'root_id' => 31,
115
+            'overridden_root' => 31,
116
+        ], $result[2]);
117
+    }
118
+
119
+    /**
120
+     * Test that getDistinctMounts applies filtering by mount providers
121
+     */
122
+    public function testGetDistinctMountsWithMountProviderFilter(): void {
123
+        $result = iterator_to_array($this->fileAccess->getDistinctMounts(['TestProviderClass1'], false));
124
+
125
+        $this->assertCount(2, $result);
126
+
127
+        $this->assertEquals([
128
+            'storage_id' => 1,
129
+            'root_id' => 10,
130
+            'overridden_root' => 10,
131
+        ], $result[0]);
132
+
133
+        $this->assertEquals([
134
+            'storage_id' => 3,
135
+            'root_id' => 30,
136
+            'overridden_root' => 30,
137
+        ], $result[1]);
138
+    }
139
+
140
+    /**
141
+     * Test that getDistinctMounts rewrites home directory paths
142
+     */
143
+    public function testGetDistinctMountsWithRewriteHomeDirectories(): void {
144
+        // Add additional test data for a home directory mount
145
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
146
+        $queryBuilder->insert('mounts')
147
+            ->values([
148
+                'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
149
+                'root_id' => $queryBuilder->createNamedParameter(40, IQueryBuilder::PARAM_INT),
150
+                'mount_provider_class' => $queryBuilder->createNamedParameter(LocalHomeMountProvider::class),
151
+                'mount_point' => $queryBuilder->createNamedParameter('/home/user'),
152
+                'user_id' => $queryBuilder->createNamedParameter('test'),
153
+            ])
154
+            ->executeStatement();
155
+
156
+        // Add a mount that is mounted in the home directory
157
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
158
+        $queryBuilder->insert('mounts')
159
+            ->values([
160
+                'storage_id' => $queryBuilder->createNamedParameter(5, IQueryBuilder::PARAM_INT),
161
+                'root_id' => $queryBuilder->createNamedParameter(41, IQueryBuilder::PARAM_INT),
162
+                'mount_provider_class' => $queryBuilder->createNamedParameter('TestMountProvider3'),
163
+                'mount_point' => $queryBuilder->createNamedParameter('/test/files/foobar'),
164
+                'user_id' => $queryBuilder->createNamedParameter('test'),
165
+            ])
166
+            ->executeStatement();
167
+
168
+        // Simulate adding a "files" directory to the filecache table
169
+        $queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards();
170
+        $queryBuilder->delete('filecache')->executeStatement();
171
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
172
+        $queryBuilder->insert('filecache')
173
+            ->values([
174
+                'fileid' => $queryBuilder->createNamedParameter(99, IQueryBuilder::PARAM_INT),
175
+                'storage' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
176
+                'parent' => $queryBuilder->createNamedParameter(40),
177
+                'name' => $queryBuilder->createNamedParameter('files'),
178
+                'path' => $queryBuilder->createNamedParameter('files'),
179
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files')),
180
+            ])
181
+            ->executeStatement();
182
+
183
+        $result = iterator_to_array($this->fileAccess->getDistinctMounts());
184
+
185
+        $this->assertCount(2, $result);
186
+
187
+        $this->assertEquals([
188
+            'storage_id' => 4,
189
+            'root_id' => 40,
190
+            'overridden_root' => 99,
191
+        ], $result[0]);
192
+
193
+        $this->assertEquals([
194
+            'storage_id' => 5,
195
+            'root_id' => 41,
196
+            'overridden_root' => 41,
197
+        ], $result[1]);
198
+    }
199
+
200
+    private function setUpTestDatabaseForGetByAncestorInStorage(): void {
201
+        // prepare `filecache` table for tests
202
+        $queryBuilder = $this->dbConnection->getQueryBuilder();
203
+
204
+        $queryBuilder->insert('filecache')
205
+            ->values([
206
+                'fileid' => 1,
207
+                'parent' => 0,
208
+                'path' => $queryBuilder->createNamedParameter('files'),
209
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files')),
210
+                'storage' => $queryBuilder->createNamedParameter(1),
211
+                'name' => $queryBuilder->createNamedParameter('files'),
212
+                'mimetype' => 1,
213
+                'encrypted' => 0,
214
+                'size' => 1,
215
+            ])
216
+            ->executeStatement();
217
+
218
+        $queryBuilder->insert('filecache')
219
+            ->values([
220
+                'fileid' => 2,
221
+                'parent' => 1,
222
+                'path' => $queryBuilder->createNamedParameter('files/documents'),
223
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/documents')),
224
+                'storage' => $queryBuilder->createNamedParameter(1),
225
+                'name' => $queryBuilder->createNamedParameter('documents'),
226
+                'mimetype' => 2,
227
+                'encrypted' => 1,
228
+                'size' => 1,
229
+            ])
230
+            ->executeStatement();
231
+
232
+        $queryBuilder->insert('filecache')
233
+            ->values([
234
+                'fileid' => 3,
235
+                'parent' => 1,
236
+                'path' => $queryBuilder->createNamedParameter('files/photos'),
237
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos')),
238
+                'storage' => $queryBuilder->createNamedParameter(1),
239
+                'name' => $queryBuilder->createNamedParameter('photos'),
240
+                'mimetype' => 3,
241
+                'encrypted' => 1,
242
+                'size' => 1,
243
+            ])
244
+            ->executeStatement();
245
+
246
+        $queryBuilder->insert('filecache')
247
+            ->values([
248
+                'fileid' => 4,
249
+                'parent' => 3,
250
+                'path' => $queryBuilder->createNamedParameter('files/photos/endtoendencrypted'),
251
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos/endtoendencrypted')),
252
+                'storage' => $queryBuilder->createNamedParameter(1),
253
+                'name' => $queryBuilder->createNamedParameter('endtoendencrypted'),
254
+                'mimetype' => 4,
255
+                'encrypted' => 0,
256
+                'size' => 1,
257
+            ])
258
+            ->executeStatement();
259
+
260
+        $queryBuilder->insert('filecache')
261
+            ->values([
262
+                'fileid' => 5,
263
+                'parent' => 1,
264
+                'path' => $queryBuilder->createNamedParameter('files/serversideencrypted'),
265
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/serversideencrypted')),
266
+                'storage' => $queryBuilder->createNamedParameter(1),
267
+                'name' => $queryBuilder->createNamedParameter('serversideencrypted'),
268
+                'mimetype' => 4,
269
+                'encrypted' => 1,
270
+                'size' => 1,
271
+            ])
272
+            ->executeStatement();
273
+
274
+        $queryBuilder->insert('filecache')
275
+            ->values([
276
+                'fileid' => 6,
277
+                'parent' => 0,
278
+                'path' => $queryBuilder->createNamedParameter('files/storage2'),
279
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2')),
280
+                'storage' => $queryBuilder->createNamedParameter(2),
281
+                'name' => $queryBuilder->createNamedParameter('storage2'),
282
+                'mimetype' => 5,
283
+                'encrypted' => 0,
284
+                'size' => 1,
285
+            ])
286
+            ->executeStatement();
287
+
288
+        $queryBuilder->insert('filecache')
289
+            ->values([
290
+                'fileid' => 7,
291
+                'parent' => 6,
292
+                'path' => $queryBuilder->createNamedParameter('files/storage2/file'),
293
+                'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2/file')),
294
+                'storage' => $queryBuilder->createNamedParameter(2),
295
+                'name' => $queryBuilder->createNamedParameter('file'),
296
+                'mimetype' => 6,
297
+                'encrypted' => 0,
298
+                'size' => 1,
299
+            ])
300
+            ->executeStatement();
301
+    }
302
+
303
+    /**
304
+     * Test fetching files by ancestor in storage.
305
+     */
306
+    public function testGetByAncestorInStorage(): void {
307
+        $generator = $this->fileAccess->getByAncestorInStorage(
308
+            1, // storageId
309
+            1, // rootId
310
+            0, // lastFileId
311
+            10, // maxResults
312
+            [], // mimeTypes
313
+            true, // include end-to-end encrypted files
314
+            true, // include server-side encrypted files
315
+        );
316
+
317
+        $result = iterator_to_array($generator);
318
+
319
+        $this->assertCount(4, $result);
320
+
321
+        $paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result);
322
+        $this->assertEquals([
323
+            'files/documents',
324
+            'files/photos',
325
+            'files/photos/endtoendencrypted',
326
+            'files/serversideencrypted',
327
+        ], $paths);
328
+    }
329
+
330
+    /**
331
+     * Test filtering by mime types.
332
+     */
333
+    public function testGetByAncestorInStorageWithMimeTypes(): void {
334
+        $generator = $this->fileAccess->getByAncestorInStorage(
335
+            1,
336
+            1,
337
+            0,
338
+            10,
339
+            [2], // Only include documents (mimetype=2)
340
+            true,
341
+            true,
342
+        );
343
+
344
+        $result = iterator_to_array($generator);
345
+
346
+        $this->assertCount(1, $result);
347
+        $this->assertEquals('files/documents', $result[0]->getPath());
348
+    }
349
+
350
+    /**
351
+     * Test excluding end-to-end encrypted files.
352
+     */
353
+    public function testGetByAncestorInStorageWithoutEndToEndEncrypted(): void {
354
+        $generator = $this->fileAccess->getByAncestorInStorage(
355
+            1,
356
+            1,
357
+            0,
358
+            10,
359
+            [],
360
+            false, // exclude end-to-end encrypted files
361
+            true,
362
+        );
363
+
364
+        $result = iterator_to_array($generator);
365
+
366
+        $this->assertCount(3, $result);
367
+        $paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result);
368
+        $this->assertEquals(['files/documents', 'files/photos', 'files/serversideencrypted'], $paths);
369
+    }
370
+
371
+    /**
372
+     * Test excluding server-side encrypted files.
373
+     */
374
+    public function testGetByAncestorInStorageWithoutServerSideEncrypted(): void {
375
+        $generator = $this->fileAccess->getByAncestorInStorage(
376
+            1,
377
+            1,
378
+            0,
379
+            10,
380
+            [],
381
+            true,
382
+            false, // exclude server-side encrypted files
383
+        );
384
+
385
+        $result = iterator_to_array($generator);
386
+
387
+        $this->assertCount(1, $result);
388
+        $this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath());
389
+    }
390
+
391
+    /**
392
+     * Test max result limits.
393
+     */
394
+    public function testGetByAncestorInStorageWithMaxResults(): void {
395
+        $generator = $this->fileAccess->getByAncestorInStorage(
396
+            1,
397
+            1,
398
+            0,
399
+            1, // Limit to 1 result
400
+            [],
401
+            true,
402
+            true,
403
+        );
404
+
405
+        $result = iterator_to_array($generator);
406
+
407
+        $this->assertCount(1, $result);
408
+        $this->assertEquals('files/documents', $result[0]->getPath());
409
+    }
410
+
411
+    /**
412
+     * Test rootId filter
413
+     */
414
+    public function testGetByAncestorInStorageWithRootIdFilter(): void {
415
+        $generator = $this->fileAccess->getByAncestorInStorage(
416
+            1,
417
+            3, // Filter by rootId
418
+            0,
419
+            10,
420
+            [],
421
+            true,
422
+            true,
423
+        );
424
+
425
+        $result = iterator_to_array($generator);
426
+
427
+        $this->assertCount(1, $result);
428
+        $this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath());
429
+    }
430
+
431
+    /**
432
+     * Test rootId filter
433
+     */
434
+    public function testGetByAncestorInStorageWithStorageFilter(): void {
435
+        $generator = $this->fileAccess->getByAncestorInStorage(
436
+            2, // Filter by storage
437
+            6, // and by rootId
438
+            0,
439
+            10,
440
+            [],
441
+            true,
442
+            true,
443
+        );
444
+
445
+        $result = iterator_to_array($generator);
446
+
447
+        $this->assertCount(1, $result);
448
+        $this->assertEquals('files/storage2/file', $result[0]->getPath());
449
+    }
450 450
 }
Please login to merge, or discard this patch.
lib/private/Files/Cache/FileAccess.php 2 patches
Indentation   +227 added lines, -227 removed lines patch added patch discarded remove patch
@@ -21,231 +21,231 @@
 block discarded – undo
21 21
  * Low level access to the file cache
22 22
  */
23 23
 class FileAccess implements IFileAccess {
24
-	public function __construct(
25
-		private IDBConnection $connection,
26
-		private SystemConfig $systemConfig,
27
-		private LoggerInterface $logger,
28
-		private FilesMetadataManager $metadataManager,
29
-		private IMimeTypeLoader $mimeTypeLoader,
30
-	) {
31
-	}
32
-
33
-	private function getQuery(): CacheQueryBuilder {
34
-		return new CacheQueryBuilder(
35
-			$this->connection->getQueryBuilder(),
36
-			$this->metadataManager,
37
-		);
38
-	}
39
-
40
-	public function getByFileIdInStorage(int $fileId, int $storageId): ?CacheEntry {
41
-		$items = array_values($this->getByFileIdsInStorage([$fileId], $storageId));
42
-		return $items[0] ?? null;
43
-	}
44
-
45
-	public function getByPathInStorage(string $path, int $storageId): ?CacheEntry {
46
-		$query = $this->getQuery()->selectFileCache();
47
-		$query->andWhere($query->expr()->eq('filecache.path_hash', $query->createNamedParameter(md5($path))));
48
-		$query->andWhere($query->expr()->eq('filecache.storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
49
-
50
-		$row = $query->executeQuery()->fetch();
51
-		return $row ? Cache::cacheEntryFromData($row, $this->mimeTypeLoader) : null;
52
-	}
53
-
54
-	public function getByFileId(int $fileId): ?CacheEntry {
55
-		$items = array_values($this->getByFileIds([$fileId]));
56
-		return $items[0] ?? null;
57
-	}
58
-
59
-	/**
60
-	 * @param array[] $rows
61
-	 * @return array<int, CacheEntry>
62
-	 */
63
-	private function rowsToEntries(array $rows): array {
64
-		$result = [];
65
-		foreach ($rows as $row) {
66
-			$entry = Cache::cacheEntryFromData($row, $this->mimeTypeLoader);
67
-			$result[$entry->getId()] = $entry;
68
-		}
69
-		return $result;
70
-	}
71
-
72
-	/**
73
-	 * @param int[] $fileIds
74
-	 * @return array<int, CacheEntry>
75
-	 */
76
-	public function getByFileIds(array $fileIds): array {
77
-		$query = $this->getQuery()->selectFileCache();
78
-		$query->andWhere($query->expr()->in('filecache.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)));
79
-
80
-		$rows = $query->executeQuery()->fetchAll();
81
-		return $this->rowsToEntries($rows);
82
-	}
83
-
84
-	/**
85
-	 * @param int[] $fileIds
86
-	 * @param int $storageId
87
-	 * @return array<int, CacheEntry>
88
-	 */
89
-	public function getByFileIdsInStorage(array $fileIds, int $storageId): array {
90
-		$fileIds = array_values($fileIds);
91
-		$query = $this->getQuery()->selectFileCache();
92
-		$query->andWhere($query->expr()->in('filecache.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)));
93
-		$query->andWhere($query->expr()->eq('filecache.storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
94
-
95
-		$rows = $query->executeQuery()->fetchAll();
96
-		return $this->rowsToEntries($rows);
97
-	}
98
-
99
-	public function getByAncestorInStorage(int $storageId, int $folderId, int $fileIdCursor = 0, int $maxResults = 100, array $mimeTypeIds = [], bool $endToEndEncrypted = true, bool $serverSideEncrypted = true): \Generator {
100
-		$qb = $this->getQuery();
101
-		$qb->select('path')
102
-			->from('filecache')
103
-			->where($qb->expr()->eq('fileid', $qb->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)));
104
-		$result = $qb->executeQuery();
105
-		/** @var array{path:string}|false $root */
106
-		$root = $result->fetch();
107
-		$result->closeCursor();
108
-
109
-		if ($root === false) {
110
-			throw new Exception('Could not fetch storage root');
111
-		}
112
-
113
-		$qb = $this->getQuery();
114
-
115
-		$path = $root['path'] === '' ? '' : $root['path'] . '/';
116
-
117
-		$qb->selectDistinct('f.*')
118
-			->from('filecache', 'f')
119
-			->where($qb->expr()->like('f.path', $qb->createNamedParameter($this->connection->escapeLikeParameter($path) . '%')))
120
-			->andWhere($qb->expr()->eq('f.storage', $qb->createNamedParameter($storageId)))
121
-			->andWhere($qb->expr()->gt('f.fileid', $qb->createNamedParameter($fileIdCursor, IQueryBuilder::PARAM_INT)))
122
-			->hintShardKey('storage', $storageId);
123
-
124
-		if (!$endToEndEncrypted && $this->connection->getShardDefinition('filecache') === null) {
125
-			// End to end encrypted files are descendants of a folder with encrypted=1
126
-			// We can only do this inner join if the filecache table is not sharded
127
-			$qb->innerJoin('f', 'filecache', 'f2', $qb->expr()->eq('f2.fileid', 'f.parent'));
128
-
129
-			$qb->andWhere(
130
-				$qb->expr()->eq('f2.encrypted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT))
131
-			);
132
-		}
133
-
134
-		if (!$serverSideEncrypted) {
135
-			// Server side encrypted files have encrypted=1 directly
136
-			$qb->andWhere($qb->expr()->eq('f.encrypted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
137
-		}
138
-
139
-		if (count($mimeTypeIds) > 0) {
140
-			$qb->andWhere($qb->expr()->in('f.mimetype', $qb->createNamedParameter($mimeTypeIds, IQueryBuilder::PARAM_INT_ARRAY)));
141
-		}
142
-
143
-		if ($maxResults !== 0) {
144
-			$qb->setMaxResults($maxResults);
145
-		}
146
-		$qb->orderBy('f.fileid', 'ASC');
147
-		$files = $qb->executeQuery();
148
-
149
-		if (!$endToEndEncrypted && $this->connection->getShardDefinition('filecache') !== null) {
150
-			// End to end encrypted files are descendants of a folder with encrypted=1
151
-			// If the filecache table is sharded we need to check with a separate query if the parent is encrypted
152
-			$rows = [];
153
-			do {
154
-				while (count($rows) < 1000 && ($row = $files->fetch())) {
155
-					$rows[] = $row;
156
-				}
157
-				$parents = array_map(function ($row) {
158
-					return $row['parent'];
159
-				}, $rows);
160
-
161
-				$parentQuery = $this->getQuery();
162
-				$parentQuery->select('fileid', 'encrypted')->from('filecache');
163
-				$parentQuery->where($parentQuery->expr()->in('fileid', $parentQuery->createNamedParameter($parents, IQueryBuilder::PARAM_INT_ARRAY)));
164
-				$parentQuery->hintShardKey('storage', $storageId);
165
-				$result = $parentQuery->executeQuery();
166
-				$parentRows = $result->fetchAll();
167
-				$result->closeCursor();
168
-
169
-				$encryptedByFileId = array_column($parentRows, 'encrypted', 'fileid');
170
-				foreach ($rows as $row) {
171
-					if ($encryptedByFileId[$row['parent']]) {
172
-						continue;
173
-					}
174
-					yield Cache::cacheEntryFromData($row, $this->mimeTypeLoader);
175
-				}
176
-				$rows = [];
177
-			} while ($rows[] = $files->fetch());
178
-		} else {
179
-			while (
180
-				/** @var array */
181
-				$row = $files->fetch()
182
-			) {
183
-				yield Cache::cacheEntryFromData($row, $this->mimeTypeLoader);
184
-			}
185
-		}
186
-
187
-		$files->closeCursor();
188
-	}
189
-
190
-	public function getDistinctMounts(array $mountProviders = [], bool $onlyUserFilesMounts = true): \Generator {
191
-		$qb = $this->connection->getQueryBuilder();
192
-		$qb->selectDistinct(['root_id', 'storage_id', 'mount_provider_class'])
193
-			->from('mounts');
194
-		if ($onlyUserFilesMounts) {
195
-			$qb->andWhere(
196
-				$qb->expr()->orX(
197
-					$qb->expr()->like('mount_point', $qb->createNamedParameter('/%/files/%')),
198
-					$qb->expr()->in('mount_provider_class', $qb->createNamedParameter([
199
-						\OC\Files\Mount\LocalHomeMountProvider::class,
200
-						\OC\Files\Mount\ObjectHomeMountProvider::class,
201
-					], IQueryBuilder::PARAM_STR_ARRAY))
202
-				)
203
-			);
204
-		}
205
-		if (count($mountProviders) > 0) {
206
-			$qb->andWhere($qb->expr()->in('mount_provider_class', $qb->createNamedParameter($mountProviders, IQueryBuilder::PARAM_STR_ARRAY)));
207
-		}
208
-		$qb->orderBy('root_id', 'ASC');
209
-		$result = $qb->executeQuery();
210
-
211
-		while (
212
-			/** @var array{storage_id:int, root_id:int,mount_provider_class:string} $row */
213
-			$row = $result->fetch()
214
-		) {
215
-			$storageId = (int)$row['storage_id'];
216
-			$rootId = (int)$row['root_id'];
217
-			$overrideRoot = $rootId;
218
-			// LocalHomeMountProvider is the default provider for user home directories
219
-			// ObjectHomeMountProvider is the home directory provider for when S3 primary storage is used
220
-			if ($onlyUserFilesMounts && in_array($row['mount_provider_class'], [
221
-				\OC\Files\Mount\LocalHomeMountProvider::class,
222
-				\OC\Files\Mount\ObjectHomeMountProvider::class,
223
-			], true)) {
224
-				// Only crawl files, not cache or trashbin
225
-				$qb = $this->getQuery();
226
-				try {
227
-					$qb->select('fileid')
228
-						->from('filecache')
229
-						->where($qb->expr()->eq('storage', $qb->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
230
-						->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($rootId, IQueryBuilder::PARAM_INT)))
231
-						->andWhere($qb->expr()->eq('path', $qb->createNamedParameter('files')));
232
-					/** @var array|false $root */
233
-					$root = $qb->executeQuery()->fetch();
234
-					if ($root !== false) {
235
-						$overrideRoot = (int)$root['fileid'];
236
-					}
237
-				} catch (Exception $e) {
238
-					$this->logger->error('Could not fetch home storage files root for storage ' . $storageId, ['exception' => $e]);
239
-					continue;
240
-				}
241
-			}
242
-			// Reference to root_id is still necessary even if we have the overridden_root_id, because storage_id and root_id uniquely identify a mount
243
-			yield [
244
-				'storage_id' => $storageId,
245
-				'root_id' => $rootId,
246
-				'overridden_root' => $overrideRoot,
247
-			];
248
-		}
249
-		$result->closeCursor();
250
-	}
24
+    public function __construct(
25
+        private IDBConnection $connection,
26
+        private SystemConfig $systemConfig,
27
+        private LoggerInterface $logger,
28
+        private FilesMetadataManager $metadataManager,
29
+        private IMimeTypeLoader $mimeTypeLoader,
30
+    ) {
31
+    }
32
+
33
+    private function getQuery(): CacheQueryBuilder {
34
+        return new CacheQueryBuilder(
35
+            $this->connection->getQueryBuilder(),
36
+            $this->metadataManager,
37
+        );
38
+    }
39
+
40
+    public function getByFileIdInStorage(int $fileId, int $storageId): ?CacheEntry {
41
+        $items = array_values($this->getByFileIdsInStorage([$fileId], $storageId));
42
+        return $items[0] ?? null;
43
+    }
44
+
45
+    public function getByPathInStorage(string $path, int $storageId): ?CacheEntry {
46
+        $query = $this->getQuery()->selectFileCache();
47
+        $query->andWhere($query->expr()->eq('filecache.path_hash', $query->createNamedParameter(md5($path))));
48
+        $query->andWhere($query->expr()->eq('filecache.storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
49
+
50
+        $row = $query->executeQuery()->fetch();
51
+        return $row ? Cache::cacheEntryFromData($row, $this->mimeTypeLoader) : null;
52
+    }
53
+
54
+    public function getByFileId(int $fileId): ?CacheEntry {
55
+        $items = array_values($this->getByFileIds([$fileId]));
56
+        return $items[0] ?? null;
57
+    }
58
+
59
+    /**
60
+     * @param array[] $rows
61
+     * @return array<int, CacheEntry>
62
+     */
63
+    private function rowsToEntries(array $rows): array {
64
+        $result = [];
65
+        foreach ($rows as $row) {
66
+            $entry = Cache::cacheEntryFromData($row, $this->mimeTypeLoader);
67
+            $result[$entry->getId()] = $entry;
68
+        }
69
+        return $result;
70
+    }
71
+
72
+    /**
73
+     * @param int[] $fileIds
74
+     * @return array<int, CacheEntry>
75
+     */
76
+    public function getByFileIds(array $fileIds): array {
77
+        $query = $this->getQuery()->selectFileCache();
78
+        $query->andWhere($query->expr()->in('filecache.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)));
79
+
80
+        $rows = $query->executeQuery()->fetchAll();
81
+        return $this->rowsToEntries($rows);
82
+    }
83
+
84
+    /**
85
+     * @param int[] $fileIds
86
+     * @param int $storageId
87
+     * @return array<int, CacheEntry>
88
+     */
89
+    public function getByFileIdsInStorage(array $fileIds, int $storageId): array {
90
+        $fileIds = array_values($fileIds);
91
+        $query = $this->getQuery()->selectFileCache();
92
+        $query->andWhere($query->expr()->in('filecache.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY)));
93
+        $query->andWhere($query->expr()->eq('filecache.storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
94
+
95
+        $rows = $query->executeQuery()->fetchAll();
96
+        return $this->rowsToEntries($rows);
97
+    }
98
+
99
+    public function getByAncestorInStorage(int $storageId, int $folderId, int $fileIdCursor = 0, int $maxResults = 100, array $mimeTypeIds = [], bool $endToEndEncrypted = true, bool $serverSideEncrypted = true): \Generator {
100
+        $qb = $this->getQuery();
101
+        $qb->select('path')
102
+            ->from('filecache')
103
+            ->where($qb->expr()->eq('fileid', $qb->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)));
104
+        $result = $qb->executeQuery();
105
+        /** @var array{path:string}|false $root */
106
+        $root = $result->fetch();
107
+        $result->closeCursor();
108
+
109
+        if ($root === false) {
110
+            throw new Exception('Could not fetch storage root');
111
+        }
112
+
113
+        $qb = $this->getQuery();
114
+
115
+        $path = $root['path'] === '' ? '' : $root['path'] . '/';
116
+
117
+        $qb->selectDistinct('f.*')
118
+            ->from('filecache', 'f')
119
+            ->where($qb->expr()->like('f.path', $qb->createNamedParameter($this->connection->escapeLikeParameter($path) . '%')))
120
+            ->andWhere($qb->expr()->eq('f.storage', $qb->createNamedParameter($storageId)))
121
+            ->andWhere($qb->expr()->gt('f.fileid', $qb->createNamedParameter($fileIdCursor, IQueryBuilder::PARAM_INT)))
122
+            ->hintShardKey('storage', $storageId);
123
+
124
+        if (!$endToEndEncrypted && $this->connection->getShardDefinition('filecache') === null) {
125
+            // End to end encrypted files are descendants of a folder with encrypted=1
126
+            // We can only do this inner join if the filecache table is not sharded
127
+            $qb->innerJoin('f', 'filecache', 'f2', $qb->expr()->eq('f2.fileid', 'f.parent'));
128
+
129
+            $qb->andWhere(
130
+                $qb->expr()->eq('f2.encrypted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT))
131
+            );
132
+        }
133
+
134
+        if (!$serverSideEncrypted) {
135
+            // Server side encrypted files have encrypted=1 directly
136
+            $qb->andWhere($qb->expr()->eq('f.encrypted', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
137
+        }
138
+
139
+        if (count($mimeTypeIds) > 0) {
140
+            $qb->andWhere($qb->expr()->in('f.mimetype', $qb->createNamedParameter($mimeTypeIds, IQueryBuilder::PARAM_INT_ARRAY)));
141
+        }
142
+
143
+        if ($maxResults !== 0) {
144
+            $qb->setMaxResults($maxResults);
145
+        }
146
+        $qb->orderBy('f.fileid', 'ASC');
147
+        $files = $qb->executeQuery();
148
+
149
+        if (!$endToEndEncrypted && $this->connection->getShardDefinition('filecache') !== null) {
150
+            // End to end encrypted files are descendants of a folder with encrypted=1
151
+            // If the filecache table is sharded we need to check with a separate query if the parent is encrypted
152
+            $rows = [];
153
+            do {
154
+                while (count($rows) < 1000 && ($row = $files->fetch())) {
155
+                    $rows[] = $row;
156
+                }
157
+                $parents = array_map(function ($row) {
158
+                    return $row['parent'];
159
+                }, $rows);
160
+
161
+                $parentQuery = $this->getQuery();
162
+                $parentQuery->select('fileid', 'encrypted')->from('filecache');
163
+                $parentQuery->where($parentQuery->expr()->in('fileid', $parentQuery->createNamedParameter($parents, IQueryBuilder::PARAM_INT_ARRAY)));
164
+                $parentQuery->hintShardKey('storage', $storageId);
165
+                $result = $parentQuery->executeQuery();
166
+                $parentRows = $result->fetchAll();
167
+                $result->closeCursor();
168
+
169
+                $encryptedByFileId = array_column($parentRows, 'encrypted', 'fileid');
170
+                foreach ($rows as $row) {
171
+                    if ($encryptedByFileId[$row['parent']]) {
172
+                        continue;
173
+                    }
174
+                    yield Cache::cacheEntryFromData($row, $this->mimeTypeLoader);
175
+                }
176
+                $rows = [];
177
+            } while ($rows[] = $files->fetch());
178
+        } else {
179
+            while (
180
+                /** @var array */
181
+                $row = $files->fetch()
182
+            ) {
183
+                yield Cache::cacheEntryFromData($row, $this->mimeTypeLoader);
184
+            }
185
+        }
186
+
187
+        $files->closeCursor();
188
+    }
189
+
190
+    public function getDistinctMounts(array $mountProviders = [], bool $onlyUserFilesMounts = true): \Generator {
191
+        $qb = $this->connection->getQueryBuilder();
192
+        $qb->selectDistinct(['root_id', 'storage_id', 'mount_provider_class'])
193
+            ->from('mounts');
194
+        if ($onlyUserFilesMounts) {
195
+            $qb->andWhere(
196
+                $qb->expr()->orX(
197
+                    $qb->expr()->like('mount_point', $qb->createNamedParameter('/%/files/%')),
198
+                    $qb->expr()->in('mount_provider_class', $qb->createNamedParameter([
199
+                        \OC\Files\Mount\LocalHomeMountProvider::class,
200
+                        \OC\Files\Mount\ObjectHomeMountProvider::class,
201
+                    ], IQueryBuilder::PARAM_STR_ARRAY))
202
+                )
203
+            );
204
+        }
205
+        if (count($mountProviders) > 0) {
206
+            $qb->andWhere($qb->expr()->in('mount_provider_class', $qb->createNamedParameter($mountProviders, IQueryBuilder::PARAM_STR_ARRAY)));
207
+        }
208
+        $qb->orderBy('root_id', 'ASC');
209
+        $result = $qb->executeQuery();
210
+
211
+        while (
212
+            /** @var array{storage_id:int, root_id:int,mount_provider_class:string} $row */
213
+            $row = $result->fetch()
214
+        ) {
215
+            $storageId = (int)$row['storage_id'];
216
+            $rootId = (int)$row['root_id'];
217
+            $overrideRoot = $rootId;
218
+            // LocalHomeMountProvider is the default provider for user home directories
219
+            // ObjectHomeMountProvider is the home directory provider for when S3 primary storage is used
220
+            if ($onlyUserFilesMounts && in_array($row['mount_provider_class'], [
221
+                \OC\Files\Mount\LocalHomeMountProvider::class,
222
+                \OC\Files\Mount\ObjectHomeMountProvider::class,
223
+            ], true)) {
224
+                // Only crawl files, not cache or trashbin
225
+                $qb = $this->getQuery();
226
+                try {
227
+                    $qb->select('fileid')
228
+                        ->from('filecache')
229
+                        ->where($qb->expr()->eq('storage', $qb->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
230
+                        ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($rootId, IQueryBuilder::PARAM_INT)))
231
+                        ->andWhere($qb->expr()->eq('path', $qb->createNamedParameter('files')));
232
+                    /** @var array|false $root */
233
+                    $root = $qb->executeQuery()->fetch();
234
+                    if ($root !== false) {
235
+                        $overrideRoot = (int)$root['fileid'];
236
+                    }
237
+                } catch (Exception $e) {
238
+                    $this->logger->error('Could not fetch home storage files root for storage ' . $storageId, ['exception' => $e]);
239
+                    continue;
240
+                }
241
+            }
242
+            // Reference to root_id is still necessary even if we have the overridden_root_id, because storage_id and root_id uniquely identify a mount
243
+            yield [
244
+                'storage_id' => $storageId,
245
+                'root_id' => $rootId,
246
+                'overridden_root' => $overrideRoot,
247
+            ];
248
+        }
249
+        $result->closeCursor();
250
+    }
251 251
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -112,11 +112,11 @@  discard block
 block discarded – undo
112 112
 
113 113
 		$qb = $this->getQuery();
114 114
 
115
-		$path = $root['path'] === '' ? '' : $root['path'] . '/';
115
+		$path = $root['path'] === '' ? '' : $root['path'].'/';
116 116
 
117 117
 		$qb->selectDistinct('f.*')
118 118
 			->from('filecache', 'f')
119
-			->where($qb->expr()->like('f.path', $qb->createNamedParameter($this->connection->escapeLikeParameter($path) . '%')))
119
+			->where($qb->expr()->like('f.path', $qb->createNamedParameter($this->connection->escapeLikeParameter($path).'%')))
120 120
 			->andWhere($qb->expr()->eq('f.storage', $qb->createNamedParameter($storageId)))
121 121
 			->andWhere($qb->expr()->gt('f.fileid', $qb->createNamedParameter($fileIdCursor, IQueryBuilder::PARAM_INT)))
122 122
 			->hintShardKey('storage', $storageId);
@@ -154,7 +154,7 @@  discard block
 block discarded – undo
154 154
 				while (count($rows) < 1000 && ($row = $files->fetch())) {
155 155
 					$rows[] = $row;
156 156
 				}
157
-				$parents = array_map(function ($row) {
157
+				$parents = array_map(function($row) {
158 158
 					return $row['parent'];
159 159
 				}, $rows);
160 160
 
@@ -212,8 +212,8 @@  discard block
 block discarded – undo
212 212
 			/** @var array{storage_id:int, root_id:int,mount_provider_class:string} $row */
213 213
 			$row = $result->fetch()
214 214
 		) {
215
-			$storageId = (int)$row['storage_id'];
216
-			$rootId = (int)$row['root_id'];
215
+			$storageId = (int) $row['storage_id'];
216
+			$rootId = (int) $row['root_id'];
217 217
 			$overrideRoot = $rootId;
218 218
 			// LocalHomeMountProvider is the default provider for user home directories
219 219
 			// ObjectHomeMountProvider is the home directory provider for when S3 primary storage is used
@@ -232,10 +232,10 @@  discard block
 block discarded – undo
232 232
 					/** @var array|false $root */
233 233
 					$root = $qb->executeQuery()->fetch();
234 234
 					if ($root !== false) {
235
-						$overrideRoot = (int)$root['fileid'];
235
+						$overrideRoot = (int) $root['fileid'];
236 236
 					}
237 237
 				} catch (Exception $e) {
238
-					$this->logger->error('Could not fetch home storage files root for storage ' . $storageId, ['exception' => $e]);
238
+					$this->logger->error('Could not fetch home storage files root for storage '.$storageId, ['exception' => $e]);
239 239
 					continue;
240 240
 				}
241 241
 			}
Please login to merge, or discard this patch.