Passed
Push — master ( e0d767...d0a7f8 )
by Roeland
24:12 queued 10:57
created
apps/files/lib/Service/OwnershipTransferService.php 2 patches
Indentation   +297 added lines, -297 removed lines patch added patch discarded remove patch
@@ -57,301 +57,301 @@
 block discarded – undo
57 57
 
58 58
 class OwnershipTransferService {
59 59
 
60
-	/** @var IEncryptionManager */
61
-	private $encryptionManager;
62
-
63
-	/** @var IShareManager */
64
-	private $shareManager;
65
-
66
-	/** @var IMountManager */
67
-	private $mountManager;
68
-
69
-	/** @var IUserMountCache */
70
-	private $userMountCache;
71
-
72
-	public function __construct(IEncryptionManager $manager,
73
-								IShareManager $shareManager,
74
-								IMountManager $mountManager,
75
-								IUserMountCache $userMountCache) {
76
-		$this->encryptionManager = $manager;
77
-		$this->shareManager = $shareManager;
78
-		$this->mountManager = $mountManager;
79
-		$this->userMountCache = $userMountCache;
80
-	}
81
-
82
-	/**
83
-	 * @param IUser $sourceUser
84
-	 * @param IUser $destinationUser
85
-	 * @param string $path
86
-	 *
87
-	 * @param OutputInterface|null $output
88
-	 * @param bool $move
89
-	 * @throws TransferOwnershipException
90
-	 * @throws \OC\User\NoUserException
91
-	 */
92
-	public function transfer(IUser $sourceUser,
93
-							 IUser $destinationUser,
94
-							 string $path,
95
-							 ?OutputInterface $output = null,
96
-							 bool $move = false,
97
-							 bool $firstLogin = false): void {
98
-		$output = $output ?? new NullOutput();
99
-		$sourceUid = $sourceUser->getUID();
100
-		$destinationUid = $destinationUser->getUID();
101
-		$sourcePath = rtrim($sourceUid . '/files/' . $path, '/');
102
-
103
-		// target user has to be ready
104
-		if ($destinationUser->getLastLogin() === 0 || !$this->encryptionManager->isReadyForUser($destinationUid)) {
105
-			throw new TransferOwnershipException("The target user is not ready to accept files. The user has at least to have logged in once.", 2);
106
-		}
107
-
108
-		// setup filesystem
109
-		Filesystem::initMountPoints($sourceUid);
110
-		Filesystem::initMountPoints($destinationUid);
111
-
112
-		$view = new View();
113
-
114
-		if ($move) {
115
-			$finalTarget = "$destinationUid/files/";
116
-		} else {
117
-			$date = date('Y-m-d H-i-s');
118
-
119
-			// Remove some characters which are prone to cause errors
120
-			$cleanUserName = str_replace(['\\', '/', ':', '.', '?', '#', '\'', '"'], '-', $sourceUser->getDisplayName());
121
-			// Replace multiple dashes with one dash
122
-			$cleanUserName = preg_replace('/-{2,}/s', '-', $cleanUserName);
123
-			$cleanUserName = $cleanUserName ?: $sourceUid;
124
-
125
-			$finalTarget = "$destinationUid/files/transferred from $cleanUserName on $date";
126
-			try {
127
-				$view->verifyPath(dirname($finalTarget), basename($finalTarget));
128
-			} catch (InvalidPathException $e) {
129
-				$finalTarget = "$destinationUid/files/transferred from $sourceUid on $date";
130
-			}
131
-		}
132
-
133
-		if (!($view->is_dir($sourcePath) || $view->is_file($sourcePath))) {
134
-			throw new TransferOwnershipException("Unknown path provided: $path", 1);
135
-		}
136
-
137
-		if ($move && (
138
-				!$view->is_dir($finalTarget) || (
139
-					!$firstLogin &&
140
-					count($view->getDirectoryContent($finalTarget)) > 0
141
-				)
142
-			)
143
-		) {
144
-			throw new TransferOwnershipException("Destination path does not exists or is not empty", 1);
145
-		}
146
-
147
-
148
-		// analyse source folder
149
-		$this->analyse(
150
-			$sourceUid,
151
-			$destinationUid,
152
-			$sourcePath,
153
-			$view,
154
-			$output
155
-		);
156
-
157
-		// collect all the shares
158
-		$shares = $this->collectUsersShares(
159
-			$sourceUid,
160
-			$output,
161
-			$view,
162
-			$sourcePath
163
-		);
164
-
165
-		// transfer the files
166
-		$this->transferFiles(
167
-			$sourceUid,
168
-			$sourcePath,
169
-			$finalTarget,
170
-			$view,
171
-			$output
172
-		);
173
-
174
-		// restore the shares
175
-		$this->restoreShares(
176
-			$sourceUid,
177
-			$destinationUid,
178
-			$shares,
179
-			$output
180
-		);
181
-	}
182
-
183
-	private function walkFiles(View $view, $path, Closure $callBack) {
184
-		foreach ($view->getDirectoryContent($path) as $fileInfo) {
185
-			if (!$callBack($fileInfo)) {
186
-				return;
187
-			}
188
-			if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) {
189
-				$this->walkFiles($view, $fileInfo->getPath(), $callBack);
190
-			}
191
-		}
192
-	}
193
-
194
-	/**
195
-	 * @param OutputInterface $output
196
-	 *
197
-	 * @throws \Exception
198
-	 */
199
-	protected function analyse(string $sourceUid,
200
-							   string $destinationUid,
201
-							   string $sourcePath,
202
-							   View $view,
203
-							   OutputInterface $output): void {
204
-		$output->writeln('Validating quota');
205
-		$size = $view->getFileInfo($sourcePath, false)->getSize(false);
206
-		$freeSpace = $view->free_space($destinationUid . '/files/');
207
-		if ($size > $freeSpace && $freeSpace !== FileInfo::SPACE_UNKNOWN) {
208
-			$output->writeln('<error>Target user does not have enough free space available.</error>');
209
-			throw new \Exception('Execution terminated.');
210
-		}
211
-
212
-		$output->writeln("Analysing files of $sourceUid ...");
213
-		$progress = new ProgressBar($output);
214
-		$progress->start();
215
-
216
-		$encryptedFiles = [];
217
-		$this->walkFiles($view, $sourcePath,
218
-			function (FileInfo $fileInfo) use ($progress) {
219
-				if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) {
220
-					// only analyze into folders from main storage,
221
-					if (!$fileInfo->getStorage()->instanceOfStorage(IHomeStorage::class)) {
222
-						return false;
223
-					}
224
-					return true;
225
-				}
226
-				$progress->advance();
227
-				if ($fileInfo->isEncrypted()) {
228
-					$encryptedFiles[] = $fileInfo;
229
-				}
230
-				return true;
231
-			});
232
-		$progress->finish();
233
-		$output->writeln('');
234
-
235
-		// no file is allowed to be encrypted
236
-		if (!empty($encryptedFiles)) {
237
-			$output->writeln("<error>Some files are encrypted - please decrypt them first.</error>");
238
-			foreach ($encryptedFiles as $encryptedFile) {
239
-				/** @var FileInfo $encryptedFile */
240
-				$output->writeln("  " . $encryptedFile->getPath());
241
-			}
242
-			throw new \Exception('Execution terminated.');
243
-		}
244
-	}
245
-
246
-	private function collectUsersShares(string $sourceUid,
247
-										OutputInterface $output,
248
-										View $view,
249
-										?string $path = null): array {
250
-		$output->writeln("Collecting all share information for files and folders of $sourceUid ...");
251
-
252
-		$shares = [];
253
-		$progress = new ProgressBar($output);
254
-		foreach ([IShare::TYPE_GROUP, IShare::TYPE_USER, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_ROOM, IShare::TYPE_EMAIL, IShare::TYPE_CIRCLE] as $shareType) {
255
-			$offset = 0;
256
-			while (true) {
257
-				$sharePage = $this->shareManager->getSharesBy($sourceUid, $shareType, null, true, 50, $offset);
258
-				$progress->advance(count($sharePage));
259
-				if (empty($sharePage)) {
260
-					break;
261
-				}
262
-				if ($path !== null) {
263
-					$sharePage = array_filter($sharePage, function (IShare $share) use ($view, $path) {
264
-						try {
265
-							$relativePath = $view->getPath($share->getNodeId());
266
-							$singleFileTranfer = $view->is_file($path);
267
-							if ($singleFileTranfer) {
268
-								return Filesystem::normalizePath($relativePath) === Filesystem::normalizePath($path);
269
-							}
270
-
271
-							return mb_strpos(
272
-								Filesystem::normalizePath($relativePath . '/', false),
273
-								Filesystem::normalizePath($path . '/', false)) === 0;
274
-						} catch (\Exception $e) {
275
-							return false;
276
-						}
277
-					});
278
-				}
279
-				$shares = array_merge($shares, $sharePage);
280
-				$offset += 50;
281
-			}
282
-		}
283
-
284
-		$progress->finish();
285
-		$output->writeln('');
286
-		return $shares;
287
-	}
288
-
289
-	/**
290
-	 * @throws TransferOwnershipException
291
-	 */
292
-	protected function transferFiles(string $sourceUid,
293
-									 string $sourcePath,
294
-									 string $finalTarget,
295
-									 View $view,
296
-									 OutputInterface $output): void {
297
-		$output->writeln("Transferring files to $finalTarget ...");
298
-
299
-		// This change will help user to transfer the folder specified using --path option.
300
-		// Else only the content inside folder is transferred which is not correct.
301
-		if ($sourcePath !== "$sourceUid/files") {
302
-			$view->mkdir($finalTarget);
303
-			$finalTarget = $finalTarget . '/' . basename($sourcePath);
304
-		}
305
-		if ($view->rename($sourcePath, $finalTarget) === false) {
306
-			throw new TransferOwnershipException("Could not transfer files.", 1);
307
-		}
308
-		if (!is_dir("$sourceUid/files")) {
309
-			// because the files folder is moved away we need to recreate it
310
-			$view->mkdir("$sourceUid/files");
311
-		}
312
-	}
313
-
314
-	private function restoreShares(string $sourceUid,
315
-								   string $destinationUid,
316
-								   array $shares,
317
-								   OutputInterface $output) {
318
-		$output->writeln("Restoring shares ...");
319
-		$progress = new ProgressBar($output, count($shares));
320
-
321
-		foreach ($shares as $share) {
322
-			try {
323
-				if ($share->getShareType() === IShare::TYPE_USER &&
324
-					$share->getSharedWith() === $destinationUid) {
325
-					// Unmount the shares before deleting, so we don't try to get the storage later on.
326
-					$shareMountPoint = $this->mountManager->find('/' . $destinationUid . '/files' . $share->getTarget());
327
-					if ($shareMountPoint) {
328
-						$this->mountManager->removeMount($shareMountPoint->getMountPoint());
329
-					}
330
-					$this->shareManager->deleteShare($share);
331
-				} else {
332
-					if ($share->getShareOwner() === $sourceUid) {
333
-						$share->setShareOwner($destinationUid);
334
-					}
335
-					if ($share->getSharedBy() === $sourceUid) {
336
-						$share->setSharedBy($destinationUid);
337
-					}
338
-
339
-
340
-					// trigger refetching of the node so that the new owner and mountpoint are taken into account
341
-					// otherwise the checks on the share update will fail due to the original node not being available in the new user scope
342
-					$this->userMountCache->clear();
343
-					$share->setNodeId($share->getNode()->getId());
344
-
345
-					$this->shareManager->updateShare($share);
346
-				}
347
-			} catch (\OCP\Files\NotFoundException $e) {
348
-				$output->writeln('<error>Share with id ' . $share->getId() . ' points at deleted file, skipping</error>');
349
-			} catch (\Throwable $e) {
350
-				$output->writeln('<error>Could not restore share with id ' . $share->getId() . ':' . $e->getTraceAsString() . '</error>');
351
-			}
352
-			$progress->advance();
353
-		}
354
-		$progress->finish();
355
-		$output->writeln('');
356
-	}
60
+    /** @var IEncryptionManager */
61
+    private $encryptionManager;
62
+
63
+    /** @var IShareManager */
64
+    private $shareManager;
65
+
66
+    /** @var IMountManager */
67
+    private $mountManager;
68
+
69
+    /** @var IUserMountCache */
70
+    private $userMountCache;
71
+
72
+    public function __construct(IEncryptionManager $manager,
73
+                                IShareManager $shareManager,
74
+                                IMountManager $mountManager,
75
+                                IUserMountCache $userMountCache) {
76
+        $this->encryptionManager = $manager;
77
+        $this->shareManager = $shareManager;
78
+        $this->mountManager = $mountManager;
79
+        $this->userMountCache = $userMountCache;
80
+    }
81
+
82
+    /**
83
+     * @param IUser $sourceUser
84
+     * @param IUser $destinationUser
85
+     * @param string $path
86
+     *
87
+     * @param OutputInterface|null $output
88
+     * @param bool $move
89
+     * @throws TransferOwnershipException
90
+     * @throws \OC\User\NoUserException
91
+     */
92
+    public function transfer(IUser $sourceUser,
93
+                                IUser $destinationUser,
94
+                                string $path,
95
+                             ?OutputInterface $output = null,
96
+                                bool $move = false,
97
+                                bool $firstLogin = false): void {
98
+        $output = $output ?? new NullOutput();
99
+        $sourceUid = $sourceUser->getUID();
100
+        $destinationUid = $destinationUser->getUID();
101
+        $sourcePath = rtrim($sourceUid . '/files/' . $path, '/');
102
+
103
+        // target user has to be ready
104
+        if ($destinationUser->getLastLogin() === 0 || !$this->encryptionManager->isReadyForUser($destinationUid)) {
105
+            throw new TransferOwnershipException("The target user is not ready to accept files. The user has at least to have logged in once.", 2);
106
+        }
107
+
108
+        // setup filesystem
109
+        Filesystem::initMountPoints($sourceUid);
110
+        Filesystem::initMountPoints($destinationUid);
111
+
112
+        $view = new View();
113
+
114
+        if ($move) {
115
+            $finalTarget = "$destinationUid/files/";
116
+        } else {
117
+            $date = date('Y-m-d H-i-s');
118
+
119
+            // Remove some characters which are prone to cause errors
120
+            $cleanUserName = str_replace(['\\', '/', ':', '.', '?', '#', '\'', '"'], '-', $sourceUser->getDisplayName());
121
+            // Replace multiple dashes with one dash
122
+            $cleanUserName = preg_replace('/-{2,}/s', '-', $cleanUserName);
123
+            $cleanUserName = $cleanUserName ?: $sourceUid;
124
+
125
+            $finalTarget = "$destinationUid/files/transferred from $cleanUserName on $date";
126
+            try {
127
+                $view->verifyPath(dirname($finalTarget), basename($finalTarget));
128
+            } catch (InvalidPathException $e) {
129
+                $finalTarget = "$destinationUid/files/transferred from $sourceUid on $date";
130
+            }
131
+        }
132
+
133
+        if (!($view->is_dir($sourcePath) || $view->is_file($sourcePath))) {
134
+            throw new TransferOwnershipException("Unknown path provided: $path", 1);
135
+        }
136
+
137
+        if ($move && (
138
+                !$view->is_dir($finalTarget) || (
139
+                    !$firstLogin &&
140
+                    count($view->getDirectoryContent($finalTarget)) > 0
141
+                )
142
+            )
143
+        ) {
144
+            throw new TransferOwnershipException("Destination path does not exists or is not empty", 1);
145
+        }
146
+
147
+
148
+        // analyse source folder
149
+        $this->analyse(
150
+            $sourceUid,
151
+            $destinationUid,
152
+            $sourcePath,
153
+            $view,
154
+            $output
155
+        );
156
+
157
+        // collect all the shares
158
+        $shares = $this->collectUsersShares(
159
+            $sourceUid,
160
+            $output,
161
+            $view,
162
+            $sourcePath
163
+        );
164
+
165
+        // transfer the files
166
+        $this->transferFiles(
167
+            $sourceUid,
168
+            $sourcePath,
169
+            $finalTarget,
170
+            $view,
171
+            $output
172
+        );
173
+
174
+        // restore the shares
175
+        $this->restoreShares(
176
+            $sourceUid,
177
+            $destinationUid,
178
+            $shares,
179
+            $output
180
+        );
181
+    }
182
+
183
+    private function walkFiles(View $view, $path, Closure $callBack) {
184
+        foreach ($view->getDirectoryContent($path) as $fileInfo) {
185
+            if (!$callBack($fileInfo)) {
186
+                return;
187
+            }
188
+            if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) {
189
+                $this->walkFiles($view, $fileInfo->getPath(), $callBack);
190
+            }
191
+        }
192
+    }
193
+
194
+    /**
195
+     * @param OutputInterface $output
196
+     *
197
+     * @throws \Exception
198
+     */
199
+    protected function analyse(string $sourceUid,
200
+                                string $destinationUid,
201
+                                string $sourcePath,
202
+                                View $view,
203
+                                OutputInterface $output): void {
204
+        $output->writeln('Validating quota');
205
+        $size = $view->getFileInfo($sourcePath, false)->getSize(false);
206
+        $freeSpace = $view->free_space($destinationUid . '/files/');
207
+        if ($size > $freeSpace && $freeSpace !== FileInfo::SPACE_UNKNOWN) {
208
+            $output->writeln('<error>Target user does not have enough free space available.</error>');
209
+            throw new \Exception('Execution terminated.');
210
+        }
211
+
212
+        $output->writeln("Analysing files of $sourceUid ...");
213
+        $progress = new ProgressBar($output);
214
+        $progress->start();
215
+
216
+        $encryptedFiles = [];
217
+        $this->walkFiles($view, $sourcePath,
218
+            function (FileInfo $fileInfo) use ($progress) {
219
+                if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) {
220
+                    // only analyze into folders from main storage,
221
+                    if (!$fileInfo->getStorage()->instanceOfStorage(IHomeStorage::class)) {
222
+                        return false;
223
+                    }
224
+                    return true;
225
+                }
226
+                $progress->advance();
227
+                if ($fileInfo->isEncrypted()) {
228
+                    $encryptedFiles[] = $fileInfo;
229
+                }
230
+                return true;
231
+            });
232
+        $progress->finish();
233
+        $output->writeln('');
234
+
235
+        // no file is allowed to be encrypted
236
+        if (!empty($encryptedFiles)) {
237
+            $output->writeln("<error>Some files are encrypted - please decrypt them first.</error>");
238
+            foreach ($encryptedFiles as $encryptedFile) {
239
+                /** @var FileInfo $encryptedFile */
240
+                $output->writeln("  " . $encryptedFile->getPath());
241
+            }
242
+            throw new \Exception('Execution terminated.');
243
+        }
244
+    }
245
+
246
+    private function collectUsersShares(string $sourceUid,
247
+                                        OutputInterface $output,
248
+                                        View $view,
249
+                                        ?string $path = null): array {
250
+        $output->writeln("Collecting all share information for files and folders of $sourceUid ...");
251
+
252
+        $shares = [];
253
+        $progress = new ProgressBar($output);
254
+        foreach ([IShare::TYPE_GROUP, IShare::TYPE_USER, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_ROOM, IShare::TYPE_EMAIL, IShare::TYPE_CIRCLE] as $shareType) {
255
+            $offset = 0;
256
+            while (true) {
257
+                $sharePage = $this->shareManager->getSharesBy($sourceUid, $shareType, null, true, 50, $offset);
258
+                $progress->advance(count($sharePage));
259
+                if (empty($sharePage)) {
260
+                    break;
261
+                }
262
+                if ($path !== null) {
263
+                    $sharePage = array_filter($sharePage, function (IShare $share) use ($view, $path) {
264
+                        try {
265
+                            $relativePath = $view->getPath($share->getNodeId());
266
+                            $singleFileTranfer = $view->is_file($path);
267
+                            if ($singleFileTranfer) {
268
+                                return Filesystem::normalizePath($relativePath) === Filesystem::normalizePath($path);
269
+                            }
270
+
271
+                            return mb_strpos(
272
+                                Filesystem::normalizePath($relativePath . '/', false),
273
+                                Filesystem::normalizePath($path . '/', false)) === 0;
274
+                        } catch (\Exception $e) {
275
+                            return false;
276
+                        }
277
+                    });
278
+                }
279
+                $shares = array_merge($shares, $sharePage);
280
+                $offset += 50;
281
+            }
282
+        }
283
+
284
+        $progress->finish();
285
+        $output->writeln('');
286
+        return $shares;
287
+    }
288
+
289
+    /**
290
+     * @throws TransferOwnershipException
291
+     */
292
+    protected function transferFiles(string $sourceUid,
293
+                                        string $sourcePath,
294
+                                        string $finalTarget,
295
+                                        View $view,
296
+                                        OutputInterface $output): void {
297
+        $output->writeln("Transferring files to $finalTarget ...");
298
+
299
+        // This change will help user to transfer the folder specified using --path option.
300
+        // Else only the content inside folder is transferred which is not correct.
301
+        if ($sourcePath !== "$sourceUid/files") {
302
+            $view->mkdir($finalTarget);
303
+            $finalTarget = $finalTarget . '/' . basename($sourcePath);
304
+        }
305
+        if ($view->rename($sourcePath, $finalTarget) === false) {
306
+            throw new TransferOwnershipException("Could not transfer files.", 1);
307
+        }
308
+        if (!is_dir("$sourceUid/files")) {
309
+            // because the files folder is moved away we need to recreate it
310
+            $view->mkdir("$sourceUid/files");
311
+        }
312
+    }
313
+
314
+    private function restoreShares(string $sourceUid,
315
+                                    string $destinationUid,
316
+                                    array $shares,
317
+                                    OutputInterface $output) {
318
+        $output->writeln("Restoring shares ...");
319
+        $progress = new ProgressBar($output, count($shares));
320
+
321
+        foreach ($shares as $share) {
322
+            try {
323
+                if ($share->getShareType() === IShare::TYPE_USER &&
324
+                    $share->getSharedWith() === $destinationUid) {
325
+                    // Unmount the shares before deleting, so we don't try to get the storage later on.
326
+                    $shareMountPoint = $this->mountManager->find('/' . $destinationUid . '/files' . $share->getTarget());
327
+                    if ($shareMountPoint) {
328
+                        $this->mountManager->removeMount($shareMountPoint->getMountPoint());
329
+                    }
330
+                    $this->shareManager->deleteShare($share);
331
+                } else {
332
+                    if ($share->getShareOwner() === $sourceUid) {
333
+                        $share->setShareOwner($destinationUid);
334
+                    }
335
+                    if ($share->getSharedBy() === $sourceUid) {
336
+                        $share->setSharedBy($destinationUid);
337
+                    }
338
+
339
+
340
+                    // trigger refetching of the node so that the new owner and mountpoint are taken into account
341
+                    // otherwise the checks on the share update will fail due to the original node not being available in the new user scope
342
+                    $this->userMountCache->clear();
343
+                    $share->setNodeId($share->getNode()->getId());
344
+
345
+                    $this->shareManager->updateShare($share);
346
+                }
347
+            } catch (\OCP\Files\NotFoundException $e) {
348
+                $output->writeln('<error>Share with id ' . $share->getId() . ' points at deleted file, skipping</error>');
349
+            } catch (\Throwable $e) {
350
+                $output->writeln('<error>Could not restore share with id ' . $share->getId() . ':' . $e->getTraceAsString() . '</error>');
351
+            }
352
+            $progress->advance();
353
+        }
354
+        $progress->finish();
355
+        $output->writeln('');
356
+    }
357 357
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -98,7 +98,7 @@  discard block
 block discarded – undo
98 98
 		$output = $output ?? new NullOutput();
99 99
 		$sourceUid = $sourceUser->getUID();
100 100
 		$destinationUid = $destinationUser->getUID();
101
-		$sourcePath = rtrim($sourceUid . '/files/' . $path, '/');
101
+		$sourcePath = rtrim($sourceUid.'/files/'.$path, '/');
102 102
 
103 103
 		// target user has to be ready
104 104
 		if ($destinationUser->getLastLogin() === 0 || !$this->encryptionManager->isReadyForUser($destinationUid)) {
@@ -203,7 +203,7 @@  discard block
 block discarded – undo
203 203
 							   OutputInterface $output): void {
204 204
 		$output->writeln('Validating quota');
205 205
 		$size = $view->getFileInfo($sourcePath, false)->getSize(false);
206
-		$freeSpace = $view->free_space($destinationUid . '/files/');
206
+		$freeSpace = $view->free_space($destinationUid.'/files/');
207 207
 		if ($size > $freeSpace && $freeSpace !== FileInfo::SPACE_UNKNOWN) {
208 208
 			$output->writeln('<error>Target user does not have enough free space available.</error>');
209 209
 			throw new \Exception('Execution terminated.');
@@ -215,7 +215,7 @@  discard block
 block discarded – undo
215 215
 
216 216
 		$encryptedFiles = [];
217 217
 		$this->walkFiles($view, $sourcePath,
218
-			function (FileInfo $fileInfo) use ($progress) {
218
+			function(FileInfo $fileInfo) use ($progress) {
219 219
 				if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) {
220 220
 					// only analyze into folders from main storage,
221 221
 					if (!$fileInfo->getStorage()->instanceOfStorage(IHomeStorage::class)) {
@@ -237,7 +237,7 @@  discard block
 block discarded – undo
237 237
 			$output->writeln("<error>Some files are encrypted - please decrypt them first.</error>");
238 238
 			foreach ($encryptedFiles as $encryptedFile) {
239 239
 				/** @var FileInfo $encryptedFile */
240
-				$output->writeln("  " . $encryptedFile->getPath());
240
+				$output->writeln("  ".$encryptedFile->getPath());
241 241
 			}
242 242
 			throw new \Exception('Execution terminated.');
243 243
 		}
@@ -260,7 +260,7 @@  discard block
 block discarded – undo
260 260
 					break;
261 261
 				}
262 262
 				if ($path !== null) {
263
-					$sharePage = array_filter($sharePage, function (IShare $share) use ($view, $path) {
263
+					$sharePage = array_filter($sharePage, function(IShare $share) use ($view, $path) {
264 264
 						try {
265 265
 							$relativePath = $view->getPath($share->getNodeId());
266 266
 							$singleFileTranfer = $view->is_file($path);
@@ -269,8 +269,8 @@  discard block
 block discarded – undo
269 269
 							}
270 270
 
271 271
 							return mb_strpos(
272
-								Filesystem::normalizePath($relativePath . '/', false),
273
-								Filesystem::normalizePath($path . '/', false)) === 0;
272
+								Filesystem::normalizePath($relativePath.'/', false),
273
+								Filesystem::normalizePath($path.'/', false)) === 0;
274 274
 						} catch (\Exception $e) {
275 275
 							return false;
276 276
 						}
@@ -300,7 +300,7 @@  discard block
 block discarded – undo
300 300
 		// Else only the content inside folder is transferred which is not correct.
301 301
 		if ($sourcePath !== "$sourceUid/files") {
302 302
 			$view->mkdir($finalTarget);
303
-			$finalTarget = $finalTarget . '/' . basename($sourcePath);
303
+			$finalTarget = $finalTarget.'/'.basename($sourcePath);
304 304
 		}
305 305
 		if ($view->rename($sourcePath, $finalTarget) === false) {
306 306
 			throw new TransferOwnershipException("Could not transfer files.", 1);
@@ -323,7 +323,7 @@  discard block
 block discarded – undo
323 323
 				if ($share->getShareType() === IShare::TYPE_USER &&
324 324
 					$share->getSharedWith() === $destinationUid) {
325 325
 					// Unmount the shares before deleting, so we don't try to get the storage later on.
326
-					$shareMountPoint = $this->mountManager->find('/' . $destinationUid . '/files' . $share->getTarget());
326
+					$shareMountPoint = $this->mountManager->find('/'.$destinationUid.'/files'.$share->getTarget());
327 327
 					if ($shareMountPoint) {
328 328
 						$this->mountManager->removeMount($shareMountPoint->getMountPoint());
329 329
 					}
@@ -345,9 +345,9 @@  discard block
 block discarded – undo
345 345
 					$this->shareManager->updateShare($share);
346 346
 				}
347 347
 			} catch (\OCP\Files\NotFoundException $e) {
348
-				$output->writeln('<error>Share with id ' . $share->getId() . ' points at deleted file, skipping</error>');
348
+				$output->writeln('<error>Share with id '.$share->getId().' points at deleted file, skipping</error>');
349 349
 			} catch (\Throwable $e) {
350
-				$output->writeln('<error>Could not restore share with id ' . $share->getId() . ':' . $e->getTraceAsString() . '</error>');
350
+				$output->writeln('<error>Could not restore share with id '.$share->getId().':'.$e->getTraceAsString().'</error>');
351 351
 			}
352 352
 			$progress->advance();
353 353
 		}
Please login to merge, or discard this patch.
lib/private/Files/Config/UserMountCache.php 1 patch
Indentation   +368 added lines, -368 removed lines patch added patch discarded remove patch
@@ -46,372 +46,372 @@
 block discarded – undo
46 46
  * Cache mounts points per user in the cache so we can easilly look them up
47 47
  */
48 48
 class UserMountCache implements IUserMountCache {
49
-	/**
50
-	 * @var IDBConnection
51
-	 */
52
-	private $connection;
53
-
54
-	/**
55
-	 * @var IUserManager
56
-	 */
57
-	private $userManager;
58
-
59
-	/**
60
-	 * Cached mount info.
61
-	 * Map of $userId to ICachedMountInfo.
62
-	 *
63
-	 * @var ICache
64
-	 **/
65
-	private $mountsForUsers;
66
-
67
-	/**
68
-	 * @var ILogger
69
-	 */
70
-	private $logger;
71
-
72
-	/**
73
-	 * @var ICache
74
-	 */
75
-	private $cacheInfoCache;
76
-
77
-	/**
78
-	 * UserMountCache constructor.
79
-	 *
80
-	 * @param IDBConnection $connection
81
-	 * @param IUserManager $userManager
82
-	 * @param ILogger $logger
83
-	 */
84
-	public function __construct(IDBConnection $connection, IUserManager $userManager, ILogger $logger) {
85
-		$this->connection = $connection;
86
-		$this->userManager = $userManager;
87
-		$this->logger = $logger;
88
-		$this->cacheInfoCache = new CappedMemoryCache();
89
-		$this->mountsForUsers = new CappedMemoryCache();
90
-	}
91
-
92
-	public function registerMounts(IUser $user, array $mounts) {
93
-		// filter out non-proper storages coming from unit tests
94
-		$mounts = array_filter($mounts, function (IMountPoint $mount) {
95
-			return $mount instanceof SharedMount || $mount->getStorage() && $mount->getStorage()->getCache();
96
-		});
97
-		/** @var ICachedMountInfo[] $newMounts */
98
-		$newMounts = array_map(function (IMountPoint $mount) use ($user) {
99
-			// filter out any storages which aren't scanned yet since we aren't interested in files from those storages (yet)
100
-			if ($mount->getStorageRootId() === -1) {
101
-				return null;
102
-			} else {
103
-				return new LazyStorageMountInfo($user, $mount);
104
-			}
105
-		}, $mounts);
106
-		$newMounts = array_values(array_filter($newMounts));
107
-		$newMountRootIds = array_map(function (ICachedMountInfo $mount) {
108
-			return $mount->getRootId();
109
-		}, $newMounts);
110
-		$newMounts = array_combine($newMountRootIds, $newMounts);
111
-
112
-		$cachedMounts = $this->getMountsForUser($user);
113
-		$cachedMountRootIds = array_map(function (ICachedMountInfo $mount) {
114
-			return $mount->getRootId();
115
-		}, $cachedMounts);
116
-		$cachedMounts = array_combine($cachedMountRootIds, $cachedMounts);
117
-
118
-		$addedMounts = [];
119
-		$removedMounts = [];
120
-
121
-		foreach ($newMounts as $rootId => $newMount) {
122
-			if (!isset($cachedMounts[$rootId])) {
123
-				$addedMounts[] = $newMount;
124
-			}
125
-		}
126
-
127
-		foreach ($cachedMounts as $rootId => $cachedMount) {
128
-			if (!isset($newMounts[$rootId])) {
129
-				$removedMounts[] = $cachedMount;
130
-			}
131
-		}
132
-
133
-		$changedMounts = $this->findChangedMounts($newMounts, $cachedMounts);
134
-
135
-		foreach ($addedMounts as $mount) {
136
-			$this->addToCache($mount);
137
-			$this->mountsForUsers[$user->getUID()][] = $mount;
138
-		}
139
-		foreach ($removedMounts as $mount) {
140
-			$this->removeFromCache($mount);
141
-			$index = array_search($mount, $this->mountsForUsers[$user->getUID()]);
142
-			unset($this->mountsForUsers[$user->getUID()][$index]);
143
-		}
144
-		foreach ($changedMounts as $mount) {
145
-			$this->updateCachedMount($mount);
146
-		}
147
-	}
148
-
149
-	/**
150
-	 * @param ICachedMountInfo[] $newMounts
151
-	 * @param ICachedMountInfo[] $cachedMounts
152
-	 * @return ICachedMountInfo[]
153
-	 */
154
-	private function findChangedMounts(array $newMounts, array $cachedMounts) {
155
-		$new = [];
156
-		foreach ($newMounts as $mount) {
157
-			$new[$mount->getRootId()] = $mount;
158
-		}
159
-		$changed = [];
160
-		foreach ($cachedMounts as $cachedMount) {
161
-			$rootId = $cachedMount->getRootId();
162
-			if (isset($new[$rootId])) {
163
-				$newMount = $new[$rootId];
164
-				if (
165
-					$newMount->getMountPoint() !== $cachedMount->getMountPoint() ||
166
-					$newMount->getStorageId() !== $cachedMount->getStorageId() ||
167
-					$newMount->getMountId() !== $cachedMount->getMountId()
168
-				) {
169
-					$changed[] = $newMount;
170
-				}
171
-			}
172
-		}
173
-		return $changed;
174
-	}
175
-
176
-	private function addToCache(ICachedMountInfo $mount) {
177
-		if ($mount->getStorageId() !== -1) {
178
-			$this->connection->insertIfNotExist('*PREFIX*mounts', [
179
-				'storage_id' => $mount->getStorageId(),
180
-				'root_id' => $mount->getRootId(),
181
-				'user_id' => $mount->getUser()->getUID(),
182
-				'mount_point' => $mount->getMountPoint(),
183
-				'mount_id' => $mount->getMountId()
184
-			], ['root_id', 'user_id']);
185
-		} else {
186
-			// in some cases this is legitimate, like orphaned shares
187
-			$this->logger->debug('Could not get storage info for mount at ' . $mount->getMountPoint());
188
-		}
189
-	}
190
-
191
-	private function updateCachedMount(ICachedMountInfo $mount) {
192
-		$builder = $this->connection->getQueryBuilder();
193
-
194
-		$query = $builder->update('mounts')
195
-			->set('storage_id', $builder->createNamedParameter($mount->getStorageId()))
196
-			->set('mount_point', $builder->createNamedParameter($mount->getMountPoint()))
197
-			->set('mount_id', $builder->createNamedParameter($mount->getMountId(), IQueryBuilder::PARAM_INT))
198
-			->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
199
-			->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT)));
200
-
201
-		$query->execute();
202
-	}
203
-
204
-	private function removeFromCache(ICachedMountInfo $mount) {
205
-		$builder = $this->connection->getQueryBuilder();
206
-
207
-		$query = $builder->delete('mounts')
208
-			->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
209
-			->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT)));
210
-		$query->execute();
211
-	}
212
-
213
-	private function dbRowToMountInfo(array $row) {
214
-		$user = $this->userManager->get($row['user_id']);
215
-		if (is_null($user)) {
216
-			return null;
217
-		}
218
-		$mount_id = $row['mount_id'];
219
-		if (!is_null($mount_id)) {
220
-			$mount_id = (int)$mount_id;
221
-		}
222
-		return new CachedMountInfo($user, (int)$row['storage_id'], (int)$row['root_id'], $row['mount_point'], $mount_id, isset($row['path']) ? $row['path'] : '');
223
-	}
224
-
225
-	/**
226
-	 * @param IUser $user
227
-	 * @return ICachedMountInfo[]
228
-	 */
229
-	public function getMountsForUser(IUser $user) {
230
-		if (!isset($this->mountsForUsers[$user->getUID()])) {
231
-			$builder = $this->connection->getQueryBuilder();
232
-			$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path')
233
-				->from('mounts', 'm')
234
-				->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
235
-				->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($user->getUID())));
236
-
237
-			$rows = $query->execute()->fetchAll();
238
-
239
-			$this->mountsForUsers[$user->getUID()] = array_filter(array_map([$this, 'dbRowToMountInfo'], $rows));
240
-		}
241
-		return $this->mountsForUsers[$user->getUID()];
242
-	}
243
-
244
-	/**
245
-	 * @param int $numericStorageId
246
-	 * @param string|null $user limit the results to a single user
247
-	 * @return CachedMountInfo[]
248
-	 */
249
-	public function getMountsForStorageId($numericStorageId, $user = null) {
250
-		$builder = $this->connection->getQueryBuilder();
251
-		$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path')
252
-			->from('mounts', 'm')
253
-			->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
254
-			->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($numericStorageId, IQueryBuilder::PARAM_INT)));
255
-
256
-		if ($user) {
257
-			$query->andWhere($builder->expr()->eq('user_id', $builder->createPositionalParameter($user)));
258
-		}
259
-
260
-		$rows = $query->execute()->fetchAll();
261
-
262
-		return array_filter(array_map([$this, 'dbRowToMountInfo'], $rows));
263
-	}
264
-
265
-	/**
266
-	 * @param int $rootFileId
267
-	 * @return CachedMountInfo[]
268
-	 */
269
-	public function getMountsForRootId($rootFileId) {
270
-		$builder = $this->connection->getQueryBuilder();
271
-		$query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path')
272
-			->from('mounts', 'm')
273
-			->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
274
-			->where($builder->expr()->eq('root_id', $builder->createPositionalParameter($rootFileId, IQueryBuilder::PARAM_INT)));
275
-
276
-		$rows = $query->execute()->fetchAll();
277
-
278
-		return array_filter(array_map([$this, 'dbRowToMountInfo'], $rows));
279
-	}
280
-
281
-	/**
282
-	 * @param $fileId
283
-	 * @return array
284
-	 * @throws \OCP\Files\NotFoundException
285
-	 */
286
-	private function getCacheInfoFromFileId($fileId) {
287
-		if (!isset($this->cacheInfoCache[$fileId])) {
288
-			$builder = $this->connection->getQueryBuilder();
289
-			$query = $builder->select('storage', 'path', 'mimetype')
290
-				->from('filecache')
291
-				->where($builder->expr()->eq('fileid', $builder->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
292
-
293
-			$row = $query->execute()->fetch();
294
-			if (is_array($row)) {
295
-				$this->cacheInfoCache[$fileId] = [
296
-					(int)$row['storage'],
297
-					$row['path'],
298
-					(int)$row['mimetype']
299
-				];
300
-			} else {
301
-				throw new NotFoundException('File with id "' . $fileId . '" not found');
302
-			}
303
-		}
304
-		return $this->cacheInfoCache[$fileId];
305
-	}
306
-
307
-	/**
308
-	 * @param int $fileId
309
-	 * @param string|null $user optionally restrict the results to a single user
310
-	 * @return ICachedMountFileInfo[]
311
-	 * @since 9.0.0
312
-	 */
313
-	public function getMountsForFileId($fileId, $user = null) {
314
-		try {
315
-			list($storageId, $internalPath) = $this->getCacheInfoFromFileId($fileId);
316
-		} catch (NotFoundException $e) {
317
-			return [];
318
-		}
319
-		$mountsForStorage = $this->getMountsForStorageId($storageId, $user);
320
-
321
-		// filter mounts that are from the same storage but a different directory
322
-		$filteredMounts = array_filter($mountsForStorage, function (ICachedMountInfo $mount) use ($internalPath, $fileId) {
323
-			if ($fileId === $mount->getRootId()) {
324
-				return true;
325
-			}
326
-			$internalMountPath = $mount->getRootInternalPath();
327
-
328
-			return $internalMountPath === '' || substr($internalPath, 0, strlen($internalMountPath) + 1) === $internalMountPath . '/';
329
-		});
330
-
331
-		return array_map(function (ICachedMountInfo $mount) use ($internalPath) {
332
-			return new CachedMountFileInfo(
333
-				$mount->getUser(),
334
-				$mount->getStorageId(),
335
-				$mount->getRootId(),
336
-				$mount->getMountPoint(),
337
-				$mount->getMountId(),
338
-				$mount->getRootInternalPath(),
339
-				$internalPath
340
-			);
341
-		}, $filteredMounts);
342
-	}
343
-
344
-	/**
345
-	 * Remove all cached mounts for a user
346
-	 *
347
-	 * @param IUser $user
348
-	 */
349
-	public function removeUserMounts(IUser $user) {
350
-		$builder = $this->connection->getQueryBuilder();
351
-
352
-		$query = $builder->delete('mounts')
353
-			->where($builder->expr()->eq('user_id', $builder->createNamedParameter($user->getUID())));
354
-		$query->execute();
355
-	}
356
-
357
-	public function removeUserStorageMount($storageId, $userId) {
358
-		$builder = $this->connection->getQueryBuilder();
359
-
360
-		$query = $builder->delete('mounts')
361
-			->where($builder->expr()->eq('user_id', $builder->createNamedParameter($userId)))
362
-			->andWhere($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
363
-		$query->execute();
364
-	}
365
-
366
-	public function remoteStorageMounts($storageId) {
367
-		$builder = $this->connection->getQueryBuilder();
368
-
369
-		$query = $builder->delete('mounts')
370
-			->where($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
371
-		$query->execute();
372
-	}
373
-
374
-	/**
375
-	 * @param array $users
376
-	 * @return array
377
-	 * @suppress SqlInjectionChecker
378
-	 */
379
-	public function getUsedSpaceForUsers(array $users) {
380
-		$builder = $this->connection->getQueryBuilder();
381
-
382
-		$slash = $builder->createNamedParameter('/');
383
-
384
-		$mountPoint = $builder->func()->concat(
385
-			$builder->func()->concat($slash, 'user_id'),
386
-			$slash
387
-		);
388
-
389
-		$userIds = array_map(function (IUser $user) {
390
-			return $user->getUID();
391
-		}, $users);
392
-
393
-		$query = $builder->select('m.user_id', 'f.size')
394
-			->from('mounts', 'm')
395
-			->innerJoin('m', 'filecache', 'f',
396
-				$builder->expr()->andX(
397
-					$builder->expr()->eq('m.storage_id', 'f.storage'),
398
-					$builder->expr()->eq('f.path_hash', $builder->createNamedParameter(md5('files')))
399
-				))
400
-			->where($builder->expr()->eq('m.mount_point', $mountPoint))
401
-			->andWhere($builder->expr()->in('m.user_id', $builder->createNamedParameter($userIds, IQueryBuilder::PARAM_STR_ARRAY)));
402
-
403
-		$result = $query->execute();
404
-
405
-		$results = [];
406
-		while ($row = $result->fetch()) {
407
-			$results[$row['user_id']] = $row['size'];
408
-		}
409
-		$result->closeCursor();
410
-		return $results;
411
-	}
412
-
413
-	public function clear(): void {
414
-		$this->cacheInfoCache = new CappedMemoryCache();
415
-		$this->mountsForUsers = new CappedMemoryCache();
416
-	}
49
+    /**
50
+     * @var IDBConnection
51
+     */
52
+    private $connection;
53
+
54
+    /**
55
+     * @var IUserManager
56
+     */
57
+    private $userManager;
58
+
59
+    /**
60
+     * Cached mount info.
61
+     * Map of $userId to ICachedMountInfo.
62
+     *
63
+     * @var ICache
64
+     **/
65
+    private $mountsForUsers;
66
+
67
+    /**
68
+     * @var ILogger
69
+     */
70
+    private $logger;
71
+
72
+    /**
73
+     * @var ICache
74
+     */
75
+    private $cacheInfoCache;
76
+
77
+    /**
78
+     * UserMountCache constructor.
79
+     *
80
+     * @param IDBConnection $connection
81
+     * @param IUserManager $userManager
82
+     * @param ILogger $logger
83
+     */
84
+    public function __construct(IDBConnection $connection, IUserManager $userManager, ILogger $logger) {
85
+        $this->connection = $connection;
86
+        $this->userManager = $userManager;
87
+        $this->logger = $logger;
88
+        $this->cacheInfoCache = new CappedMemoryCache();
89
+        $this->mountsForUsers = new CappedMemoryCache();
90
+    }
91
+
92
+    public function registerMounts(IUser $user, array $mounts) {
93
+        // filter out non-proper storages coming from unit tests
94
+        $mounts = array_filter($mounts, function (IMountPoint $mount) {
95
+            return $mount instanceof SharedMount || $mount->getStorage() && $mount->getStorage()->getCache();
96
+        });
97
+        /** @var ICachedMountInfo[] $newMounts */
98
+        $newMounts = array_map(function (IMountPoint $mount) use ($user) {
99
+            // filter out any storages which aren't scanned yet since we aren't interested in files from those storages (yet)
100
+            if ($mount->getStorageRootId() === -1) {
101
+                return null;
102
+            } else {
103
+                return new LazyStorageMountInfo($user, $mount);
104
+            }
105
+        }, $mounts);
106
+        $newMounts = array_values(array_filter($newMounts));
107
+        $newMountRootIds = array_map(function (ICachedMountInfo $mount) {
108
+            return $mount->getRootId();
109
+        }, $newMounts);
110
+        $newMounts = array_combine($newMountRootIds, $newMounts);
111
+
112
+        $cachedMounts = $this->getMountsForUser($user);
113
+        $cachedMountRootIds = array_map(function (ICachedMountInfo $mount) {
114
+            return $mount->getRootId();
115
+        }, $cachedMounts);
116
+        $cachedMounts = array_combine($cachedMountRootIds, $cachedMounts);
117
+
118
+        $addedMounts = [];
119
+        $removedMounts = [];
120
+
121
+        foreach ($newMounts as $rootId => $newMount) {
122
+            if (!isset($cachedMounts[$rootId])) {
123
+                $addedMounts[] = $newMount;
124
+            }
125
+        }
126
+
127
+        foreach ($cachedMounts as $rootId => $cachedMount) {
128
+            if (!isset($newMounts[$rootId])) {
129
+                $removedMounts[] = $cachedMount;
130
+            }
131
+        }
132
+
133
+        $changedMounts = $this->findChangedMounts($newMounts, $cachedMounts);
134
+
135
+        foreach ($addedMounts as $mount) {
136
+            $this->addToCache($mount);
137
+            $this->mountsForUsers[$user->getUID()][] = $mount;
138
+        }
139
+        foreach ($removedMounts as $mount) {
140
+            $this->removeFromCache($mount);
141
+            $index = array_search($mount, $this->mountsForUsers[$user->getUID()]);
142
+            unset($this->mountsForUsers[$user->getUID()][$index]);
143
+        }
144
+        foreach ($changedMounts as $mount) {
145
+            $this->updateCachedMount($mount);
146
+        }
147
+    }
148
+
149
+    /**
150
+     * @param ICachedMountInfo[] $newMounts
151
+     * @param ICachedMountInfo[] $cachedMounts
152
+     * @return ICachedMountInfo[]
153
+     */
154
+    private function findChangedMounts(array $newMounts, array $cachedMounts) {
155
+        $new = [];
156
+        foreach ($newMounts as $mount) {
157
+            $new[$mount->getRootId()] = $mount;
158
+        }
159
+        $changed = [];
160
+        foreach ($cachedMounts as $cachedMount) {
161
+            $rootId = $cachedMount->getRootId();
162
+            if (isset($new[$rootId])) {
163
+                $newMount = $new[$rootId];
164
+                if (
165
+                    $newMount->getMountPoint() !== $cachedMount->getMountPoint() ||
166
+                    $newMount->getStorageId() !== $cachedMount->getStorageId() ||
167
+                    $newMount->getMountId() !== $cachedMount->getMountId()
168
+                ) {
169
+                    $changed[] = $newMount;
170
+                }
171
+            }
172
+        }
173
+        return $changed;
174
+    }
175
+
176
+    private function addToCache(ICachedMountInfo $mount) {
177
+        if ($mount->getStorageId() !== -1) {
178
+            $this->connection->insertIfNotExist('*PREFIX*mounts', [
179
+                'storage_id' => $mount->getStorageId(),
180
+                'root_id' => $mount->getRootId(),
181
+                'user_id' => $mount->getUser()->getUID(),
182
+                'mount_point' => $mount->getMountPoint(),
183
+                'mount_id' => $mount->getMountId()
184
+            ], ['root_id', 'user_id']);
185
+        } else {
186
+            // in some cases this is legitimate, like orphaned shares
187
+            $this->logger->debug('Could not get storage info for mount at ' . $mount->getMountPoint());
188
+        }
189
+    }
190
+
191
+    private function updateCachedMount(ICachedMountInfo $mount) {
192
+        $builder = $this->connection->getQueryBuilder();
193
+
194
+        $query = $builder->update('mounts')
195
+            ->set('storage_id', $builder->createNamedParameter($mount->getStorageId()))
196
+            ->set('mount_point', $builder->createNamedParameter($mount->getMountPoint()))
197
+            ->set('mount_id', $builder->createNamedParameter($mount->getMountId(), IQueryBuilder::PARAM_INT))
198
+            ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
199
+            ->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT)));
200
+
201
+        $query->execute();
202
+    }
203
+
204
+    private function removeFromCache(ICachedMountInfo $mount) {
205
+        $builder = $this->connection->getQueryBuilder();
206
+
207
+        $query = $builder->delete('mounts')
208
+            ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
209
+            ->andWhere($builder->expr()->eq('root_id', $builder->createNamedParameter($mount->getRootId(), IQueryBuilder::PARAM_INT)));
210
+        $query->execute();
211
+    }
212
+
213
+    private function dbRowToMountInfo(array $row) {
214
+        $user = $this->userManager->get($row['user_id']);
215
+        if (is_null($user)) {
216
+            return null;
217
+        }
218
+        $mount_id = $row['mount_id'];
219
+        if (!is_null($mount_id)) {
220
+            $mount_id = (int)$mount_id;
221
+        }
222
+        return new CachedMountInfo($user, (int)$row['storage_id'], (int)$row['root_id'], $row['mount_point'], $mount_id, isset($row['path']) ? $row['path'] : '');
223
+    }
224
+
225
+    /**
226
+     * @param IUser $user
227
+     * @return ICachedMountInfo[]
228
+     */
229
+    public function getMountsForUser(IUser $user) {
230
+        if (!isset($this->mountsForUsers[$user->getUID()])) {
231
+            $builder = $this->connection->getQueryBuilder();
232
+            $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path')
233
+                ->from('mounts', 'm')
234
+                ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
235
+                ->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($user->getUID())));
236
+
237
+            $rows = $query->execute()->fetchAll();
238
+
239
+            $this->mountsForUsers[$user->getUID()] = array_filter(array_map([$this, 'dbRowToMountInfo'], $rows));
240
+        }
241
+        return $this->mountsForUsers[$user->getUID()];
242
+    }
243
+
244
+    /**
245
+     * @param int $numericStorageId
246
+     * @param string|null $user limit the results to a single user
247
+     * @return CachedMountInfo[]
248
+     */
249
+    public function getMountsForStorageId($numericStorageId, $user = null) {
250
+        $builder = $this->connection->getQueryBuilder();
251
+        $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path')
252
+            ->from('mounts', 'm')
253
+            ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
254
+            ->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($numericStorageId, IQueryBuilder::PARAM_INT)));
255
+
256
+        if ($user) {
257
+            $query->andWhere($builder->expr()->eq('user_id', $builder->createPositionalParameter($user)));
258
+        }
259
+
260
+        $rows = $query->execute()->fetchAll();
261
+
262
+        return array_filter(array_map([$this, 'dbRowToMountInfo'], $rows));
263
+    }
264
+
265
+    /**
266
+     * @param int $rootFileId
267
+     * @return CachedMountInfo[]
268
+     */
269
+    public function getMountsForRootId($rootFileId) {
270
+        $builder = $this->connection->getQueryBuilder();
271
+        $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path')
272
+            ->from('mounts', 'm')
273
+            ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
274
+            ->where($builder->expr()->eq('root_id', $builder->createPositionalParameter($rootFileId, IQueryBuilder::PARAM_INT)));
275
+
276
+        $rows = $query->execute()->fetchAll();
277
+
278
+        return array_filter(array_map([$this, 'dbRowToMountInfo'], $rows));
279
+    }
280
+
281
+    /**
282
+     * @param $fileId
283
+     * @return array
284
+     * @throws \OCP\Files\NotFoundException
285
+     */
286
+    private function getCacheInfoFromFileId($fileId) {
287
+        if (!isset($this->cacheInfoCache[$fileId])) {
288
+            $builder = $this->connection->getQueryBuilder();
289
+            $query = $builder->select('storage', 'path', 'mimetype')
290
+                ->from('filecache')
291
+                ->where($builder->expr()->eq('fileid', $builder->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
292
+
293
+            $row = $query->execute()->fetch();
294
+            if (is_array($row)) {
295
+                $this->cacheInfoCache[$fileId] = [
296
+                    (int)$row['storage'],
297
+                    $row['path'],
298
+                    (int)$row['mimetype']
299
+                ];
300
+            } else {
301
+                throw new NotFoundException('File with id "' . $fileId . '" not found');
302
+            }
303
+        }
304
+        return $this->cacheInfoCache[$fileId];
305
+    }
306
+
307
+    /**
308
+     * @param int $fileId
309
+     * @param string|null $user optionally restrict the results to a single user
310
+     * @return ICachedMountFileInfo[]
311
+     * @since 9.0.0
312
+     */
313
+    public function getMountsForFileId($fileId, $user = null) {
314
+        try {
315
+            list($storageId, $internalPath) = $this->getCacheInfoFromFileId($fileId);
316
+        } catch (NotFoundException $e) {
317
+            return [];
318
+        }
319
+        $mountsForStorage = $this->getMountsForStorageId($storageId, $user);
320
+
321
+        // filter mounts that are from the same storage but a different directory
322
+        $filteredMounts = array_filter($mountsForStorage, function (ICachedMountInfo $mount) use ($internalPath, $fileId) {
323
+            if ($fileId === $mount->getRootId()) {
324
+                return true;
325
+            }
326
+            $internalMountPath = $mount->getRootInternalPath();
327
+
328
+            return $internalMountPath === '' || substr($internalPath, 0, strlen($internalMountPath) + 1) === $internalMountPath . '/';
329
+        });
330
+
331
+        return array_map(function (ICachedMountInfo $mount) use ($internalPath) {
332
+            return new CachedMountFileInfo(
333
+                $mount->getUser(),
334
+                $mount->getStorageId(),
335
+                $mount->getRootId(),
336
+                $mount->getMountPoint(),
337
+                $mount->getMountId(),
338
+                $mount->getRootInternalPath(),
339
+                $internalPath
340
+            );
341
+        }, $filteredMounts);
342
+    }
343
+
344
+    /**
345
+     * Remove all cached mounts for a user
346
+     *
347
+     * @param IUser $user
348
+     */
349
+    public function removeUserMounts(IUser $user) {
350
+        $builder = $this->connection->getQueryBuilder();
351
+
352
+        $query = $builder->delete('mounts')
353
+            ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($user->getUID())));
354
+        $query->execute();
355
+    }
356
+
357
+    public function removeUserStorageMount($storageId, $userId) {
358
+        $builder = $this->connection->getQueryBuilder();
359
+
360
+        $query = $builder->delete('mounts')
361
+            ->where($builder->expr()->eq('user_id', $builder->createNamedParameter($userId)))
362
+            ->andWhere($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
363
+        $query->execute();
364
+    }
365
+
366
+    public function remoteStorageMounts($storageId) {
367
+        $builder = $this->connection->getQueryBuilder();
368
+
369
+        $query = $builder->delete('mounts')
370
+            ->where($builder->expr()->eq('storage_id', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)));
371
+        $query->execute();
372
+    }
373
+
374
+    /**
375
+     * @param array $users
376
+     * @return array
377
+     * @suppress SqlInjectionChecker
378
+     */
379
+    public function getUsedSpaceForUsers(array $users) {
380
+        $builder = $this->connection->getQueryBuilder();
381
+
382
+        $slash = $builder->createNamedParameter('/');
383
+
384
+        $mountPoint = $builder->func()->concat(
385
+            $builder->func()->concat($slash, 'user_id'),
386
+            $slash
387
+        );
388
+
389
+        $userIds = array_map(function (IUser $user) {
390
+            return $user->getUID();
391
+        }, $users);
392
+
393
+        $query = $builder->select('m.user_id', 'f.size')
394
+            ->from('mounts', 'm')
395
+            ->innerJoin('m', 'filecache', 'f',
396
+                $builder->expr()->andX(
397
+                    $builder->expr()->eq('m.storage_id', 'f.storage'),
398
+                    $builder->expr()->eq('f.path_hash', $builder->createNamedParameter(md5('files')))
399
+                ))
400
+            ->where($builder->expr()->eq('m.mount_point', $mountPoint))
401
+            ->andWhere($builder->expr()->in('m.user_id', $builder->createNamedParameter($userIds, IQueryBuilder::PARAM_STR_ARRAY)));
402
+
403
+        $result = $query->execute();
404
+
405
+        $results = [];
406
+        while ($row = $result->fetch()) {
407
+            $results[$row['user_id']] = $row['size'];
408
+        }
409
+        $result->closeCursor();
410
+        return $results;
411
+    }
412
+
413
+    public function clear(): void {
414
+        $this->cacheInfoCache = new CappedMemoryCache();
415
+        $this->mountsForUsers = new CappedMemoryCache();
416
+    }
417 417
 }
Please login to merge, or discard this patch.
lib/public/Files/Config/IUserMountCache.php 1 patch
Indentation   +83 added lines, -83 removed lines patch added patch discarded remove patch
@@ -32,96 +32,96 @@
 block discarded – undo
32 32
  * @since 9.0.0
33 33
  */
34 34
 interface IUserMountCache {
35
-	/**
36
-	 * Register mounts for a user to the cache
37
-	 *
38
-	 * @param IUser $user
39
-	 * @param IMountPoint[] $mounts
40
-	 * @since 9.0.0
41
-	 */
42
-	public function registerMounts(IUser $user, array $mounts);
35
+    /**
36
+     * Register mounts for a user to the cache
37
+     *
38
+     * @param IUser $user
39
+     * @param IMountPoint[] $mounts
40
+     * @since 9.0.0
41
+     */
42
+    public function registerMounts(IUser $user, array $mounts);
43 43
 
44
-	/**
45
-	 * Get all cached mounts for a user
46
-	 *
47
-	 * @param IUser $user
48
-	 * @return ICachedMountInfo[]
49
-	 * @since 9.0.0
50
-	 */
51
-	public function getMountsForUser(IUser $user);
44
+    /**
45
+     * Get all cached mounts for a user
46
+     *
47
+     * @param IUser $user
48
+     * @return ICachedMountInfo[]
49
+     * @since 9.0.0
50
+     */
51
+    public function getMountsForUser(IUser $user);
52 52
 
53
-	/**
54
-	 * Get all cached mounts by storage
55
-	 *
56
-	 * @param int $numericStorageId
57
-	 * @param string|null $user limit the results to a single user @since 12.0.0
58
-	 * @return ICachedMountInfo[]
59
-	 * @since 9.0.0
60
-	 */
61
-	public function getMountsForStorageId($numericStorageId, $user = null);
53
+    /**
54
+     * Get all cached mounts by storage
55
+     *
56
+     * @param int $numericStorageId
57
+     * @param string|null $user limit the results to a single user @since 12.0.0
58
+     * @return ICachedMountInfo[]
59
+     * @since 9.0.0
60
+     */
61
+    public function getMountsForStorageId($numericStorageId, $user = null);
62 62
 
63
-	/**
64
-	 * Get all cached mounts by root
65
-	 *
66
-	 * @param int $rootFileId
67
-	 * @return ICachedMountInfo[]
68
-	 * @since 9.0.0
69
-	 */
70
-	public function getMountsForRootId($rootFileId);
63
+    /**
64
+     * Get all cached mounts by root
65
+     *
66
+     * @param int $rootFileId
67
+     * @return ICachedMountInfo[]
68
+     * @since 9.0.0
69
+     */
70
+    public function getMountsForRootId($rootFileId);
71 71
 
72
-	/**
73
-	 * Get all cached mounts that contain a file
74
-	 *
75
-	 * @param int $fileId
76
-	 * @param string|null $user optionally restrict the results to a single user @since 12.0.0
77
-	 * @return ICachedMountFileInfo[]
78
-	 * @since 9.0.0
79
-	 */
80
-	public function getMountsForFileId($fileId, $user = null);
72
+    /**
73
+     * Get all cached mounts that contain a file
74
+     *
75
+     * @param int $fileId
76
+     * @param string|null $user optionally restrict the results to a single user @since 12.0.0
77
+     * @return ICachedMountFileInfo[]
78
+     * @since 9.0.0
79
+     */
80
+    public function getMountsForFileId($fileId, $user = null);
81 81
 
82
-	/**
83
-	 * Remove all cached mounts for a user
84
-	 *
85
-	 * @param IUser $user
86
-	 * @since 9.0.0
87
-	 */
88
-	public function removeUserMounts(IUser $user);
82
+    /**
83
+     * Remove all cached mounts for a user
84
+     *
85
+     * @param IUser $user
86
+     * @since 9.0.0
87
+     */
88
+    public function removeUserMounts(IUser $user);
89 89
 
90
-	/**
91
-	 * Remove all mounts for a user and storage
92
-	 *
93
-	 * @param $storageId
94
-	 * @param string $userId
95
-	 * @return mixed
96
-	 * @since 9.0.0
97
-	 */
98
-	public function removeUserStorageMount($storageId, $userId);
90
+    /**
91
+     * Remove all mounts for a user and storage
92
+     *
93
+     * @param $storageId
94
+     * @param string $userId
95
+     * @return mixed
96
+     * @since 9.0.0
97
+     */
98
+    public function removeUserStorageMount($storageId, $userId);
99 99
 
100
-	/**
101
-	 * Remove all cached mounts for a storage
102
-	 *
103
-	 * @param $storageId
104
-	 * @return mixed
105
-	 * @since 9.0.0
106
-	 */
107
-	public function remoteStorageMounts($storageId);
100
+    /**
101
+     * Remove all cached mounts for a storage
102
+     *
103
+     * @param $storageId
104
+     * @return mixed
105
+     * @since 9.0.0
106
+     */
107
+    public function remoteStorageMounts($storageId);
108 108
 
109
-	/**
110
-	 * Get the used space for users
111
-	 *
112
-	 * Note that this only includes the space in their home directory,
113
-	 * not any incoming shares or external storages.
114
-	 *
115
-	 * @param IUser[] $users
116
-	 * @return int[] [$userId => $userSpace]
117
-	 * @since 13.0.0
118
-	 */
119
-	public function getUsedSpaceForUsers(array $users);
109
+    /**
110
+     * Get the used space for users
111
+     *
112
+     * Note that this only includes the space in their home directory,
113
+     * not any incoming shares or external storages.
114
+     *
115
+     * @param IUser[] $users
116
+     * @return int[] [$userId => $userSpace]
117
+     * @since 13.0.0
118
+     */
119
+    public function getUsedSpaceForUsers(array $users);
120 120
 
121
-	/**
122
-	 * Clear all entries from the in-memory cache
123
-	 *
124
-	 * @since 20.0.0
125
-	 */
126
-	public function clear(): void;
121
+    /**
122
+     * Clear all entries from the in-memory cache
123
+     *
124
+     * @since 20.0.0
125
+     */
126
+    public function clear(): void;
127 127
 }
Please login to merge, or discard this patch.