1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @copyright Copyright (c) 2016, ownCloud, Inc. |
4
|
|
|
* |
5
|
|
|
* @author Arthur Schiwon <[email protected]> |
6
|
|
|
* @author Bart Visscher <[email protected]> |
7
|
|
|
* @author Christopher Schäpers <[email protected]> |
8
|
|
|
* @author Christoph Wurst <[email protected]> |
9
|
|
|
* @author Florin Peter <[email protected]> |
10
|
|
|
* @author Joas Schilling <[email protected]> |
11
|
|
|
* @author Jörn Friedrich Dreyer <[email protected]> |
12
|
|
|
* @author Lukas Reschke <[email protected]> |
13
|
|
|
* @author Michael Gapczynski <[email protected]> |
14
|
|
|
* @author Morris Jobke <[email protected]> |
15
|
|
|
* @author Robin Appelman <[email protected]> |
16
|
|
|
* @author Robin McCorkell <[email protected]> |
17
|
|
|
* @author Roeland Jago Douma <[email protected]> |
18
|
|
|
* @author Sam Tuke <[email protected]> |
19
|
|
|
* @author Stephan Peijnik <[email protected]> |
20
|
|
|
* @author Vincent Petry <[email protected]> |
21
|
|
|
* |
22
|
|
|
* @license AGPL-3.0 |
23
|
|
|
* |
24
|
|
|
* This code is free software: you can redistribute it and/or modify |
25
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
26
|
|
|
* as published by the Free Software Foundation. |
27
|
|
|
* |
28
|
|
|
* This program is distributed in the hope that it will be useful, |
29
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
30
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
31
|
|
|
* GNU Affero General Public License for more details. |
32
|
|
|
* |
33
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
34
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
35
|
|
|
* |
36
|
|
|
*/ |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Class for abstraction of filesystem functions |
40
|
|
|
* This class won't call any filesystem functions for itself but will pass them to the correct OC_Filestorage object |
41
|
|
|
* this class should also handle all the file permission related stuff |
42
|
|
|
* |
43
|
|
|
* Hooks provided: |
44
|
|
|
* read(path) |
45
|
|
|
* write(path, &run) |
46
|
|
|
* post_write(path) |
47
|
|
|
* create(path, &run) (when a file is created, both create and write will be emitted in that order) |
48
|
|
|
* post_create(path) |
49
|
|
|
* delete(path, &run) |
50
|
|
|
* post_delete(path) |
51
|
|
|
* rename(oldpath,newpath, &run) |
52
|
|
|
* post_rename(oldpath,newpath) |
53
|
|
|
* copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emitted in that order) |
54
|
|
|
* post_rename(oldpath,newpath) |
55
|
|
|
* post_initMountPoints(user, user_dir) |
56
|
|
|
* |
57
|
|
|
* the &run parameter can be set to false to prevent the operation from occurring |
58
|
|
|
*/ |
59
|
|
|
|
60
|
|
|
namespace OC\Files; |
61
|
|
|
|
62
|
|
|
use OC\Cache\CappedMemoryCache; |
63
|
|
|
use OC\Files\Config\MountProviderCollection; |
64
|
|
|
use OC\Files\Mount\MountPoint; |
65
|
|
|
use OC\Lockdown\Filesystem\NullStorage; |
66
|
|
|
use OCP\Files\Config\IMountProvider; |
67
|
|
|
use OCP\Files\NotFoundException; |
68
|
|
|
use OCP\Files\Storage\IStorageFactory; |
69
|
|
|
use OCP\ILogger; |
70
|
|
|
use OCP\IUserManager; |
71
|
|
|
|
72
|
|
|
class Filesystem { |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @var Mount\Manager $mounts |
76
|
|
|
*/ |
77
|
|
|
private static $mounts; |
78
|
|
|
|
79
|
|
|
public static $loaded = false; |
80
|
|
|
/** |
81
|
|
|
* @var \OC\Files\View $defaultInstance |
82
|
|
|
*/ |
83
|
|
|
private static $defaultInstance; |
84
|
|
|
|
85
|
|
|
private static $usersSetup = []; |
86
|
|
|
|
87
|
|
|
private static $normalizedPathCache = null; |
88
|
|
|
|
89
|
|
|
private static $listeningForProviders = false; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* classname which used for hooks handling |
93
|
|
|
* used as signalclass in OC_Hooks::emit() |
94
|
|
|
*/ |
95
|
|
|
public const CLASSNAME = 'OC_Filesystem'; |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* signalname emitted before file renaming |
99
|
|
|
* |
100
|
|
|
* @param string $oldpath |
101
|
|
|
* @param string $newpath |
102
|
|
|
*/ |
103
|
|
|
public const signal_rename = 'rename'; |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* signal emitted after file renaming |
107
|
|
|
* |
108
|
|
|
* @param string $oldpath |
109
|
|
|
* @param string $newpath |
110
|
|
|
*/ |
111
|
|
|
public const signal_post_rename = 'post_rename'; |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* signal emitted before file/dir creation |
115
|
|
|
* |
116
|
|
|
* @param string $path |
117
|
|
|
* @param bool $run changing this flag to false in hook handler will cancel event |
118
|
|
|
*/ |
119
|
|
|
public const signal_create = 'create'; |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* signal emitted after file/dir creation |
123
|
|
|
* |
124
|
|
|
* @param string $path |
125
|
|
|
* @param bool $run changing this flag to false in hook handler will cancel event |
126
|
|
|
*/ |
127
|
|
|
public const signal_post_create = 'post_create'; |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* signal emits before file/dir copy |
131
|
|
|
* |
132
|
|
|
* @param string $oldpath |
133
|
|
|
* @param string $newpath |
134
|
|
|
* @param bool $run changing this flag to false in hook handler will cancel event |
135
|
|
|
*/ |
136
|
|
|
public const signal_copy = 'copy'; |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* signal emits after file/dir copy |
140
|
|
|
* |
141
|
|
|
* @param string $oldpath |
142
|
|
|
* @param string $newpath |
143
|
|
|
*/ |
144
|
|
|
public const signal_post_copy = 'post_copy'; |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* signal emits before file/dir save |
148
|
|
|
* |
149
|
|
|
* @param string $path |
150
|
|
|
* @param bool $run changing this flag to false in hook handler will cancel event |
151
|
|
|
*/ |
152
|
|
|
public const signal_write = 'write'; |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* signal emits after file/dir save |
156
|
|
|
* |
157
|
|
|
* @param string $path |
158
|
|
|
*/ |
159
|
|
|
public const signal_post_write = 'post_write'; |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* signal emitted before file/dir update |
163
|
|
|
* |
164
|
|
|
* @param string $path |
165
|
|
|
* @param bool $run changing this flag to false in hook handler will cancel event |
166
|
|
|
*/ |
167
|
|
|
public const signal_update = 'update'; |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* signal emitted after file/dir update |
171
|
|
|
* |
172
|
|
|
* @param string $path |
173
|
|
|
* @param bool $run changing this flag to false in hook handler will cancel event |
174
|
|
|
*/ |
175
|
|
|
public const signal_post_update = 'post_update'; |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* signal emits when reading file/dir |
179
|
|
|
* |
180
|
|
|
* @param string $path |
181
|
|
|
*/ |
182
|
|
|
public const signal_read = 'read'; |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* signal emits when removing file/dir |
186
|
|
|
* |
187
|
|
|
* @param string $path |
188
|
|
|
*/ |
189
|
|
|
public const signal_delete = 'delete'; |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* parameters definitions for signals |
193
|
|
|
*/ |
194
|
|
|
public const signal_param_path = 'path'; |
195
|
|
|
public const signal_param_oldpath = 'oldpath'; |
196
|
|
|
public const signal_param_newpath = 'newpath'; |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* run - changing this flag to false in hook handler will cancel event |
200
|
|
|
*/ |
201
|
|
|
public const signal_param_run = 'run'; |
202
|
|
|
|
203
|
|
|
public const signal_create_mount = 'create_mount'; |
204
|
|
|
public const signal_delete_mount = 'delete_mount'; |
205
|
|
|
public const signal_param_mount_type = 'mounttype'; |
206
|
|
|
public const signal_param_users = 'users'; |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* @var \OC\Files\Storage\StorageFactory $loader |
210
|
|
|
*/ |
211
|
|
|
private static $loader; |
212
|
|
|
|
213
|
|
|
/** @var bool */ |
214
|
|
|
private static $logWarningWhenAddingStorageWrapper = true; |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* @param bool $shouldLog |
218
|
|
|
* @return bool previous value |
219
|
|
|
* @internal |
220
|
|
|
*/ |
221
|
|
|
public static function logWarningWhenAddingStorageWrapper($shouldLog) { |
222
|
|
|
$previousValue = self::$logWarningWhenAddingStorageWrapper; |
223
|
|
|
self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog; |
224
|
|
|
return $previousValue; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* @param string $wrapperName |
229
|
|
|
* @param callable $wrapper |
230
|
|
|
* @param int $priority |
231
|
|
|
*/ |
232
|
|
|
public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) { |
233
|
|
|
if (self::$logWarningWhenAddingStorageWrapper) { |
234
|
|
|
\OC::$server->getLogger()->warning("Storage wrapper '{wrapper}' was not registered via the 'OC_Filesystem - preSetup' hook which could cause potential problems.", [ |
235
|
|
|
'wrapper' => $wrapperName, |
236
|
|
|
'app' => 'filesystem', |
237
|
|
|
]); |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
$mounts = self::getMountManager()->getAll(); |
241
|
|
|
if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) { |
242
|
|
|
// do not re-wrap if storage with this name already existed |
243
|
|
|
return; |
244
|
|
|
} |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Returns the storage factory |
249
|
|
|
* |
250
|
|
|
* @return IStorageFactory |
251
|
|
|
*/ |
252
|
|
|
public static function getLoader() { |
253
|
|
|
if (!self::$loader) { |
254
|
|
|
self::$loader = \OC::$server->query(IStorageFactory::class); |
255
|
|
|
} |
256
|
|
|
return self::$loader; |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
/** |
260
|
|
|
* Returns the mount manager |
261
|
|
|
* |
262
|
|
|
* @return \OC\Files\Mount\Manager |
263
|
|
|
*/ |
264
|
|
|
public static function getMountManager($user = '') { |
265
|
|
|
if (!self::$mounts) { |
266
|
|
|
\OC_Util::setupFS($user); |
267
|
|
|
} |
268
|
|
|
return self::$mounts; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* get the mountpoint of the storage object for a path |
273
|
|
|
* ( note: because a storage is not always mounted inside the fakeroot, the |
274
|
|
|
* returned mountpoint is relative to the absolute root of the filesystem |
275
|
|
|
* and doesn't take the chroot into account ) |
276
|
|
|
* |
277
|
|
|
* @param string $path |
278
|
|
|
* @return string |
279
|
|
|
*/ |
280
|
|
|
public static function getMountPoint($path) { |
281
|
|
|
if (!self::$mounts) { |
282
|
|
|
\OC_Util::setupFS(); |
283
|
|
|
} |
284
|
|
|
$mount = self::$mounts->find($path); |
285
|
|
|
if ($mount) { |
286
|
|
|
return $mount->getMountPoint(); |
287
|
|
|
} else { |
288
|
|
|
return ''; |
289
|
|
|
} |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* get a list of all mount points in a directory |
294
|
|
|
* |
295
|
|
|
* @param string $path |
296
|
|
|
* @return string[] |
297
|
|
|
*/ |
298
|
|
|
public static function getMountPoints($path) { |
299
|
|
|
if (!self::$mounts) { |
300
|
|
|
\OC_Util::setupFS(); |
301
|
|
|
} |
302
|
|
|
$result = []; |
303
|
|
|
$mounts = self::$mounts->findIn($path); |
304
|
|
|
foreach ($mounts as $mount) { |
305
|
|
|
$result[] = $mount->getMountPoint(); |
306
|
|
|
} |
307
|
|
|
return $result; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* get the storage mounted at $mountPoint |
312
|
|
|
* |
313
|
|
|
* @param string $mountPoint |
314
|
|
|
* @return \OC\Files\Storage\Storage |
315
|
|
|
*/ |
316
|
|
|
public static function getStorage($mountPoint) { |
317
|
|
|
if (!self::$mounts) { |
318
|
|
|
\OC_Util::setupFS(); |
319
|
|
|
} |
320
|
|
|
$mount = self::$mounts->find($mountPoint); |
321
|
|
|
return $mount->getStorage(); |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* @param string $id |
326
|
|
|
* @return Mount\MountPoint[] |
327
|
|
|
*/ |
328
|
|
|
public static function getMountByStorageId($id) { |
329
|
|
|
if (!self::$mounts) { |
330
|
|
|
\OC_Util::setupFS(); |
331
|
|
|
} |
332
|
|
|
return self::$mounts->findByStorageId($id); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* @param int $id |
337
|
|
|
* @return Mount\MountPoint[] |
338
|
|
|
*/ |
339
|
|
|
public static function getMountByNumericId($id) { |
340
|
|
|
if (!self::$mounts) { |
341
|
|
|
\OC_Util::setupFS(); |
342
|
|
|
} |
343
|
|
|
return self::$mounts->findByNumericId($id); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* resolve a path to a storage and internal path |
348
|
|
|
* |
349
|
|
|
* @param string $path |
350
|
|
|
* @return array an array consisting of the storage and the internal path |
351
|
|
|
*/ |
352
|
|
|
public static function resolvePath($path) { |
353
|
|
|
if (!self::$mounts) { |
354
|
|
|
\OC_Util::setupFS(); |
355
|
|
|
} |
356
|
|
|
$mount = self::$mounts->find($path); |
357
|
|
|
if ($mount) { |
358
|
|
|
return [$mount->getStorage(), rtrim($mount->getInternalPath($path), '/')]; |
359
|
|
|
} else { |
360
|
|
|
return [null, null]; |
361
|
|
|
} |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
public static function init($user, $root) { |
365
|
|
|
if (self::$defaultInstance) { |
366
|
|
|
return false; |
367
|
|
|
} |
368
|
|
|
self::getLoader(); |
369
|
|
|
self::$defaultInstance = new View($root); |
370
|
|
|
|
371
|
|
|
if (!self::$mounts) { |
372
|
|
|
self::$mounts = \OC::$server->getMountManager(); |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
//load custom mount config |
376
|
|
|
self::initMountPoints($user); |
377
|
|
|
|
378
|
|
|
self::$loaded = true; |
379
|
|
|
|
380
|
|
|
return true; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
public static function initMountManager() { |
384
|
|
|
if (!self::$mounts) { |
385
|
|
|
self::$mounts = \OC::$server->getMountManager(); |
386
|
|
|
} |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* Initialize system and personal mount points for a user |
391
|
|
|
* |
392
|
|
|
* @param string $user |
393
|
|
|
* @throws \OC\User\NoUserException if the user is not available |
394
|
|
|
*/ |
395
|
|
|
public static function initMountPoints($user = '') { |
396
|
|
|
if ($user == '') { |
397
|
|
|
$user = \OC_User::getUser(); |
398
|
|
|
} |
399
|
|
|
if ($user === null || $user === false || $user === '') { |
400
|
|
|
throw new \OC\User\NoUserException('Attempted to initialize mount points for null user and no user in session'); |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
if (isset(self::$usersSetup[$user])) { |
404
|
|
|
return; |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
self::$usersSetup[$user] = true; |
408
|
|
|
|
409
|
|
|
$userManager = \OC::$server->getUserManager(); |
410
|
|
|
$userObject = $userManager->get($user); |
411
|
|
|
|
412
|
|
|
if (is_null($userObject)) { |
413
|
|
|
\OCP\Util::writeLog('files', ' Backends provided no user object for ' . $user, ILogger::ERROR); |
|
|
|
|
414
|
|
|
// reset flag, this will make it possible to rethrow the exception if called again |
415
|
|
|
unset(self::$usersSetup[$user]); |
416
|
|
|
throw new \OC\User\NoUserException('Backends provided no user object for ' . $user); |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
$realUid = $userObject->getUID(); |
420
|
|
|
// workaround in case of different casings |
421
|
|
|
if ($user !== $realUid) { |
422
|
|
|
$stack = json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 50)); |
423
|
|
|
\OCP\Util::writeLog('files', 'initMountPoints() called with wrong user casing. This could be a bug. Expected: "' . $realUid . '" got "' . $user . '". Stack: ' . $stack, ILogger::WARN); |
|
|
|
|
424
|
|
|
$user = $realUid; |
425
|
|
|
|
426
|
|
|
// again with the correct casing |
427
|
|
|
if (isset(self::$usersSetup[$user])) { |
428
|
|
|
return; |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
self::$usersSetup[$user] = true; |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
if (\OC::$server->getLockdownManager()->canAccessFilesystem()) { |
435
|
|
|
/** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */ |
436
|
|
|
$mountConfigManager = \OC::$server->getMountProviderCollection(); |
437
|
|
|
|
438
|
|
|
// home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers |
439
|
|
|
$homeMount = $mountConfigManager->getHomeMountForUser($userObject); |
440
|
|
|
if ($homeMount->getStorageRootId() === -1) { |
441
|
|
|
$homeMount->getStorage()->mkdir(''); |
442
|
|
|
$homeMount->getStorage()->getScanner()->scan(''); |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
self::getMountManager()->addMount($homeMount); |
446
|
|
|
|
447
|
|
|
\OC\Files\Filesystem::getStorage($user); |
448
|
|
|
|
449
|
|
|
// Chance to mount for other storages |
450
|
|
|
if ($userObject) { |
|
|
|
|
451
|
|
|
$mounts = $mountConfigManager->addMountForUser($userObject, self::getMountManager()); |
452
|
|
|
$mounts[] = $homeMount; |
453
|
|
|
$mountConfigManager->registerMounts($userObject, $mounts); |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
self::listenForNewMountProviders($mountConfigManager, $userManager); |
457
|
|
|
} else { |
458
|
|
|
self::getMountManager()->addMount(new MountPoint( |
459
|
|
|
new NullStorage([]), |
460
|
|
|
'/' . $user |
461
|
|
|
)); |
462
|
|
|
self::getMountManager()->addMount(new MountPoint( |
463
|
|
|
new NullStorage([]), |
464
|
|
|
'/' . $user . '/files' |
465
|
|
|
)); |
466
|
|
|
} |
467
|
|
|
\OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', ['user' => $user]); |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
/** |
471
|
|
|
* Get mounts from mount providers that are registered after setup |
472
|
|
|
* |
473
|
|
|
* @param MountProviderCollection $mountConfigManager |
474
|
|
|
* @param IUserManager $userManager |
475
|
|
|
*/ |
476
|
|
|
private static function listenForNewMountProviders(MountProviderCollection $mountConfigManager, IUserManager $userManager) { |
477
|
|
|
if (!self::$listeningForProviders) { |
478
|
|
|
self::$listeningForProviders = true; |
479
|
|
|
$mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function (IMountProvider $provider) use ($userManager) { |
480
|
|
|
foreach (Filesystem::$usersSetup as $user => $setup) { |
481
|
|
|
$userObject = $userManager->get($user); |
482
|
|
|
if ($userObject) { |
483
|
|
|
$mounts = $provider->getMountsForUser($userObject, Filesystem::getLoader()); |
484
|
|
|
array_walk($mounts, [self::$mounts, 'addMount']); |
485
|
|
|
} |
486
|
|
|
} |
487
|
|
|
}); |
488
|
|
|
} |
489
|
|
|
} |
490
|
|
|
|
491
|
|
|
/** |
492
|
|
|
* get the default filesystem view |
493
|
|
|
* |
494
|
|
|
* @return View |
495
|
|
|
*/ |
496
|
|
|
public static function getView() { |
497
|
|
|
return self::$defaultInstance; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
/** |
501
|
|
|
* tear down the filesystem, removing all storage providers |
502
|
|
|
*/ |
503
|
|
|
public static function tearDown() { |
504
|
|
|
self::clearMounts(); |
505
|
|
|
self::$defaultInstance = null; |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* get the relative path of the root data directory for the current user |
510
|
|
|
* |
511
|
|
|
* @return string |
512
|
|
|
* |
513
|
|
|
* Returns path like /admin/files |
514
|
|
|
*/ |
515
|
|
|
public static function getRoot() { |
516
|
|
|
if (!self::$defaultInstance) { |
517
|
|
|
return null; |
518
|
|
|
} |
519
|
|
|
return self::$defaultInstance->getRoot(); |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
/** |
523
|
|
|
* clear all mounts and storage backends |
524
|
|
|
*/ |
525
|
|
|
public static function clearMounts() { |
526
|
|
|
if (self::$mounts) { |
527
|
|
|
self::$usersSetup = []; |
528
|
|
|
self::$mounts->clear(); |
529
|
|
|
} |
530
|
|
|
} |
531
|
|
|
|
532
|
|
|
/** |
533
|
|
|
* mount an \OC\Files\Storage\Storage in our virtual filesystem |
534
|
|
|
* |
535
|
|
|
* @param \OC\Files\Storage\Storage|string $class |
536
|
|
|
* @param array $arguments |
537
|
|
|
* @param string $mountpoint |
538
|
|
|
*/ |
539
|
|
|
public static function mount($class, $arguments, $mountpoint) { |
540
|
|
|
if (!self::$mounts) { |
541
|
|
|
\OC_Util::setupFS(); |
542
|
|
|
} |
543
|
|
|
$mount = new Mount\MountPoint($class, $mountpoint, $arguments, self::getLoader()); |
544
|
|
|
self::$mounts->addMount($mount); |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
/** |
548
|
|
|
* return the path to a local version of the file |
549
|
|
|
* we need this because we can't know if a file is stored local or not from |
550
|
|
|
* outside the filestorage and for some purposes a local file is needed |
551
|
|
|
* |
552
|
|
|
* @param string $path |
553
|
|
|
* @return string |
554
|
|
|
*/ |
555
|
|
|
public static function getLocalFile($path) { |
556
|
|
|
return self::$defaultInstance->getLocalFile($path); |
557
|
|
|
} |
558
|
|
|
|
559
|
|
|
/** |
560
|
|
|
* @param string $path |
561
|
|
|
* @return string |
562
|
|
|
*/ |
563
|
|
|
public static function getLocalFolder($path) { |
564
|
|
|
return self::$defaultInstance->getLocalFolder($path); |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
/** |
568
|
|
|
* return path to file which reflects one visible in browser |
569
|
|
|
* |
570
|
|
|
* @param string $path |
571
|
|
|
* @return string |
572
|
|
|
*/ |
573
|
|
|
public static function getLocalPath($path) { |
574
|
|
|
$datadir = \OC_User::getHome(\OC_User::getUser()) . '/files'; |
|
|
|
|
575
|
|
|
$newpath = $path; |
576
|
|
|
if (strncmp($newpath, $datadir, strlen($datadir)) == 0) { |
577
|
|
|
$newpath = substr($path, strlen($datadir)); |
578
|
|
|
} |
579
|
|
|
return $newpath; |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
/** |
583
|
|
|
* check if the requested path is valid |
584
|
|
|
* |
585
|
|
|
* @param string $path |
586
|
|
|
* @return bool |
587
|
|
|
*/ |
588
|
|
|
public static function isValidPath($path) { |
589
|
|
|
$path = self::normalizePath($path); |
590
|
|
|
if (!$path || $path[0] !== '/') { |
591
|
|
|
$path = '/' . $path; |
592
|
|
|
} |
593
|
|
|
if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') { |
594
|
|
|
return false; |
595
|
|
|
} |
596
|
|
|
return true; |
597
|
|
|
} |
598
|
|
|
|
599
|
|
|
/** |
600
|
|
|
* checks if a file is blacklisted for storage in the filesystem |
601
|
|
|
* Listens to write and rename hooks |
602
|
|
|
* |
603
|
|
|
* @param array $data from hook |
604
|
|
|
*/ |
605
|
|
|
public static function isBlacklisted($data) { |
606
|
|
|
if (isset($data['path'])) { |
607
|
|
|
$path = $data['path']; |
608
|
|
|
} elseif (isset($data['newpath'])) { |
609
|
|
|
$path = $data['newpath']; |
610
|
|
|
} |
611
|
|
|
if (isset($path)) { |
612
|
|
|
if (self::isFileBlacklisted($path)) { |
|
|
|
|
613
|
|
|
$data['run'] = false; |
614
|
|
|
} |
615
|
|
|
} |
616
|
|
|
} |
617
|
|
|
|
618
|
|
|
/** |
619
|
|
|
* @param string $filename |
620
|
|
|
* @return bool |
621
|
|
|
*/ |
622
|
|
|
public static function isFileBlacklisted($filename) { |
623
|
|
|
$filename = self::normalizePath($filename); |
624
|
|
|
|
625
|
|
|
$blacklist = \OC::$server->getConfig()->getSystemValue('blacklisted_files', ['.htaccess']); |
626
|
|
|
$filename = strtolower(basename($filename)); |
627
|
|
|
return in_array($filename, $blacklist); |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
/** |
631
|
|
|
* check if the directory should be ignored when scanning |
632
|
|
|
* NOTE: the special directories . and .. would cause never ending recursion |
633
|
|
|
* |
634
|
|
|
* @param string $dir |
635
|
|
|
* @return boolean |
636
|
|
|
*/ |
637
|
|
|
public static function isIgnoredDir($dir) { |
638
|
|
|
if ($dir === '.' || $dir === '..') { |
639
|
|
|
return true; |
640
|
|
|
} |
641
|
|
|
return false; |
642
|
|
|
} |
643
|
|
|
|
644
|
|
|
/** |
645
|
|
|
* following functions are equivalent to their php builtin equivalents for arguments/return values. |
646
|
|
|
*/ |
647
|
|
|
public static function mkdir($path) { |
648
|
|
|
return self::$defaultInstance->mkdir($path); |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
public static function rmdir($path) { |
652
|
|
|
return self::$defaultInstance->rmdir($path); |
653
|
|
|
} |
654
|
|
|
|
655
|
|
|
public static function is_dir($path) { |
656
|
|
|
return self::$defaultInstance->is_dir($path); |
657
|
|
|
} |
658
|
|
|
|
659
|
|
|
public static function is_file($path) { |
660
|
|
|
return self::$defaultInstance->is_file($path); |
661
|
|
|
} |
662
|
|
|
|
663
|
|
|
public static function stat($path) { |
664
|
|
|
return self::$defaultInstance->stat($path); |
665
|
|
|
} |
666
|
|
|
|
667
|
|
|
public static function filetype($path) { |
668
|
|
|
return self::$defaultInstance->filetype($path); |
669
|
|
|
} |
670
|
|
|
|
671
|
|
|
public static function filesize($path) { |
672
|
|
|
return self::$defaultInstance->filesize($path); |
673
|
|
|
} |
674
|
|
|
|
675
|
|
|
public static function readfile($path) { |
676
|
|
|
return self::$defaultInstance->readfile($path); |
677
|
|
|
} |
678
|
|
|
|
679
|
|
|
public static function isCreatable($path) { |
680
|
|
|
return self::$defaultInstance->isCreatable($path); |
681
|
|
|
} |
682
|
|
|
|
683
|
|
|
public static function isReadable($path) { |
684
|
|
|
return self::$defaultInstance->isReadable($path); |
685
|
|
|
} |
686
|
|
|
|
687
|
|
|
public static function isUpdatable($path) { |
688
|
|
|
return self::$defaultInstance->isUpdatable($path); |
689
|
|
|
} |
690
|
|
|
|
691
|
|
|
public static function isDeletable($path) { |
692
|
|
|
return self::$defaultInstance->isDeletable($path); |
693
|
|
|
} |
694
|
|
|
|
695
|
|
|
public static function isSharable($path) { |
696
|
|
|
return self::$defaultInstance->isSharable($path); |
697
|
|
|
} |
698
|
|
|
|
699
|
|
|
public static function file_exists($path) { |
700
|
|
|
return self::$defaultInstance->file_exists($path); |
701
|
|
|
} |
702
|
|
|
|
703
|
|
|
public static function filemtime($path) { |
704
|
|
|
return self::$defaultInstance->filemtime($path); |
705
|
|
|
} |
706
|
|
|
|
707
|
|
|
public static function touch($path, $mtime = null) { |
708
|
|
|
return self::$defaultInstance->touch($path, $mtime); |
709
|
|
|
} |
710
|
|
|
|
711
|
|
|
/** |
712
|
|
|
* @return string |
713
|
|
|
*/ |
714
|
|
|
public static function file_get_contents($path) { |
715
|
|
|
return self::$defaultInstance->file_get_contents($path); |
|
|
|
|
716
|
|
|
} |
717
|
|
|
|
718
|
|
|
public static function file_put_contents($path, $data) { |
719
|
|
|
return self::$defaultInstance->file_put_contents($path, $data); |
720
|
|
|
} |
721
|
|
|
|
722
|
|
|
public static function unlink($path) { |
723
|
|
|
return self::$defaultInstance->unlink($path); |
724
|
|
|
} |
725
|
|
|
|
726
|
|
|
public static function rename($path1, $path2) { |
727
|
|
|
return self::$defaultInstance->rename($path1, $path2); |
728
|
|
|
} |
729
|
|
|
|
730
|
|
|
public static function copy($path1, $path2) { |
731
|
|
|
return self::$defaultInstance->copy($path1, $path2); |
732
|
|
|
} |
733
|
|
|
|
734
|
|
|
public static function fopen($path, $mode) { |
735
|
|
|
return self::$defaultInstance->fopen($path, $mode); |
736
|
|
|
} |
737
|
|
|
|
738
|
|
|
/** |
739
|
|
|
* @return string |
740
|
|
|
*/ |
741
|
|
|
public static function toTmpFile($path) { |
742
|
|
|
return self::$defaultInstance->toTmpFile($path); |
|
|
|
|
743
|
|
|
} |
744
|
|
|
|
745
|
|
|
public static function fromTmpFile($tmpFile, $path) { |
746
|
|
|
return self::$defaultInstance->fromTmpFile($tmpFile, $path); |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
public static function getMimeType($path) { |
750
|
|
|
return self::$defaultInstance->getMimeType($path); |
751
|
|
|
} |
752
|
|
|
|
753
|
|
|
public static function hash($type, $path, $raw = false) { |
754
|
|
|
return self::$defaultInstance->hash($type, $path, $raw); |
755
|
|
|
} |
756
|
|
|
|
757
|
|
|
public static function free_space($path = '/') { |
758
|
|
|
return self::$defaultInstance->free_space($path); |
759
|
|
|
} |
760
|
|
|
|
761
|
|
|
public static function search($query) { |
762
|
|
|
return self::$defaultInstance->search($query); |
763
|
|
|
} |
764
|
|
|
|
765
|
|
|
/** |
766
|
|
|
* @param string $query |
767
|
|
|
*/ |
768
|
|
|
public static function searchByMime($query) { |
769
|
|
|
return self::$defaultInstance->searchByMime($query); |
770
|
|
|
} |
771
|
|
|
|
772
|
|
|
/** |
773
|
|
|
* @param string|int $tag name or tag id |
774
|
|
|
* @param string $userId owner of the tags |
775
|
|
|
* @return FileInfo[] array or file info |
776
|
|
|
*/ |
777
|
|
|
public static function searchByTag($tag, $userId) { |
778
|
|
|
return self::$defaultInstance->searchByTag($tag, $userId); |
779
|
|
|
} |
780
|
|
|
|
781
|
|
|
/** |
782
|
|
|
* check if a file or folder has been updated since $time |
783
|
|
|
* |
784
|
|
|
* @param string $path |
785
|
|
|
* @param int $time |
786
|
|
|
* @return bool |
787
|
|
|
*/ |
788
|
|
|
public static function hasUpdated($path, $time) { |
789
|
|
|
return self::$defaultInstance->hasUpdated($path, $time); |
790
|
|
|
} |
791
|
|
|
|
792
|
|
|
/** |
793
|
|
|
* Fix common problems with a file path |
794
|
|
|
* |
795
|
|
|
* @param string $path |
796
|
|
|
* @param bool $stripTrailingSlash whether to strip the trailing slash |
797
|
|
|
* @param bool $isAbsolutePath whether the given path is absolute |
798
|
|
|
* @param bool $keepUnicode true to disable unicode normalization |
799
|
|
|
* @return string |
800
|
|
|
*/ |
801
|
|
|
public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false, $keepUnicode = false) { |
802
|
|
|
if (is_null(self::$normalizedPathCache)) { |
803
|
|
|
self::$normalizedPathCache = new CappedMemoryCache(2048); |
804
|
|
|
} |
805
|
|
|
|
806
|
|
|
/** |
807
|
|
|
* FIXME: This is a workaround for existing classes and files which call |
808
|
|
|
* this function with another type than a valid string. This |
809
|
|
|
* conversion should get removed as soon as all existing |
810
|
|
|
* function calls have been fixed. |
811
|
|
|
*/ |
812
|
|
|
$path = (string)$path; |
813
|
|
|
|
814
|
|
|
$cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath, $keepUnicode]); |
815
|
|
|
|
816
|
|
|
if (isset(self::$normalizedPathCache[$cacheKey])) { |
817
|
|
|
return self::$normalizedPathCache[$cacheKey]; |
818
|
|
|
} |
819
|
|
|
|
820
|
|
|
if ($path === '') { |
821
|
|
|
return '/'; |
822
|
|
|
} |
823
|
|
|
|
824
|
|
|
//normalize unicode if possible |
825
|
|
|
if (!$keepUnicode) { |
826
|
|
|
$path = \OC_Util::normalizeUnicode($path); |
827
|
|
|
} |
828
|
|
|
|
829
|
|
|
//add leading slash, if it is already there we strip it anyway |
830
|
|
|
$path = '/' . $path; |
831
|
|
|
|
832
|
|
|
$patterns = [ |
833
|
|
|
'/\\\\/s', // no windows style slashes |
834
|
|
|
'/\/\.(\/\.)?\//s', // remove '/./' |
835
|
|
|
'/\/{2,}/s', // remove sequence of slashes |
836
|
|
|
'/\/\.$/s', // remove trailing /. |
837
|
|
|
]; |
838
|
|
|
|
839
|
|
|
do { |
840
|
|
|
$count = 0; |
841
|
|
|
$path = preg_replace($patterns, '/', $path, -1, $count); |
842
|
|
|
} while ($count > 0); |
843
|
|
|
|
844
|
|
|
//remove trailing slash |
845
|
|
|
if ($stripTrailingSlash && strlen($path) > 1) { |
846
|
|
|
$path = rtrim($path, '/'); |
847
|
|
|
} |
848
|
|
|
|
849
|
|
|
self::$normalizedPathCache[$cacheKey] = $path; |
850
|
|
|
|
851
|
|
|
return $path; |
852
|
|
|
} |
853
|
|
|
|
854
|
|
|
/** |
855
|
|
|
* get the filesystem info |
856
|
|
|
* |
857
|
|
|
* @param string $path |
858
|
|
|
* @param boolean $includeMountPoints whether to add mountpoint sizes, |
859
|
|
|
* defaults to true |
860
|
|
|
* @return \OC\Files\FileInfo|bool False if file does not exist |
861
|
|
|
*/ |
862
|
|
|
public static function getFileInfo($path, $includeMountPoints = true) { |
863
|
|
|
return self::$defaultInstance->getFileInfo($path, $includeMountPoints); |
864
|
|
|
} |
865
|
|
|
|
866
|
|
|
/** |
867
|
|
|
* change file metadata |
868
|
|
|
* |
869
|
|
|
* @param string $path |
870
|
|
|
* @param array $data |
871
|
|
|
* @return int |
872
|
|
|
* |
873
|
|
|
* returns the fileid of the updated file |
874
|
|
|
*/ |
875
|
|
|
public static function putFileInfo($path, $data) { |
876
|
|
|
return self::$defaultInstance->putFileInfo($path, $data); |
877
|
|
|
} |
878
|
|
|
|
879
|
|
|
/** |
880
|
|
|
* get the content of a directory |
881
|
|
|
* |
882
|
|
|
* @param string $directory path under datadirectory |
883
|
|
|
* @param string $mimetype_filter limit returned content to this mimetype or mimepart |
884
|
|
|
* @return \OC\Files\FileInfo[] |
885
|
|
|
*/ |
886
|
|
|
public static function getDirectoryContent($directory, $mimetype_filter = '') { |
887
|
|
|
return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter); |
888
|
|
|
} |
889
|
|
|
|
890
|
|
|
/** |
891
|
|
|
* Get the path of a file by id |
892
|
|
|
* |
893
|
|
|
* Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file |
894
|
|
|
* |
895
|
|
|
* @param int $id |
896
|
|
|
* @throws NotFoundException |
897
|
|
|
* @return string |
898
|
|
|
*/ |
899
|
|
|
public static function getPath($id) { |
900
|
|
|
return self::$defaultInstance->getPath($id); |
901
|
|
|
} |
902
|
|
|
|
903
|
|
|
/** |
904
|
|
|
* Get the owner for a file or folder |
905
|
|
|
* |
906
|
|
|
* @param string $path |
907
|
|
|
* @return string |
908
|
|
|
*/ |
909
|
|
|
public static function getOwner($path) { |
910
|
|
|
return self::$defaultInstance->getOwner($path); |
911
|
|
|
} |
912
|
|
|
|
913
|
|
|
/** |
914
|
|
|
* get the ETag for a file or folder |
915
|
|
|
* |
916
|
|
|
* @param string $path |
917
|
|
|
* @return string |
918
|
|
|
*/ |
919
|
|
|
public static function getETag($path) { |
920
|
|
|
return self::$defaultInstance->getETag($path); |
921
|
|
|
} |
922
|
|
|
} |
923
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.