Passed
Push — master ( cddfb0...9e7341 )
by Julius
13:55 queued 13s
created

OC_Helper::clearStorageInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 2 Features 0
Metric Value
cc 1
eloc 5
c 2
b 2
f 0
nc 1
nop 1
dl 0
loc 7
rs 10
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Ardinis <[email protected]>
6
 * @author Arthur Schiwon <[email protected]>
7
 * @author Bart Visscher <[email protected]>
8
 * @author Björn Schießle <[email protected]>
9
 * @author Christoph Wurst <[email protected]>
10
 * @author Daniel Kesselberg <[email protected]>
11
 * @author Felix Moeller <[email protected]>
12
 * @author J0WI <[email protected]>
13
 * @author Jakob Sack <[email protected]>
14
 * @author Jan-Christoph Borchardt <[email protected]>
15
 * @author Joas Schilling <[email protected]>
16
 * @author Jörn Friedrich Dreyer <[email protected]>
17
 * @author Julius Härtl <[email protected]>
18
 * @author Lukas Reschke <[email protected]>
19
 * @author Morris Jobke <[email protected]>
20
 * @author Olivier Paroz <[email protected]>
21
 * @author Pellaeon Lin <[email protected]>
22
 * @author RealRancor <[email protected]>
23
 * @author Robin Appelman <[email protected]>
24
 * @author Robin McCorkell <[email protected]>
25
 * @author Roeland Jago Douma <[email protected]>
26
 * @author Simon Könnecke <[email protected]>
27
 * @author Thomas Müller <[email protected]>
28
 * @author Thomas Tanghus <[email protected]>
29
 * @author Vincent Petry <[email protected]>
30
 *
31
 * @license AGPL-3.0
32
 *
33
 * This code is free software: you can redistribute it and/or modify
34
 * it under the terms of the GNU Affero General Public License, version 3,
35
 * as published by the Free Software Foundation.
36
 *
37
 * This program is distributed in the hope that it will be useful,
38
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40
 * GNU Affero General Public License for more details.
41
 *
42
 * You should have received a copy of the GNU Affero General Public License, version 3,
43
 * along with this program. If not, see <http://www.gnu.org/licenses/>
44
 *
45
 */
46
use bantu\IniGetWrapper\IniGetWrapper;
47
use OC\Files\Filesystem;
48
use OCP\Files\Mount\IMountPoint;
49
use OCP\ICacheFactory;
50
use OCP\IBinaryFinder;
51
use OCP\IUser;
52
use OCP\Util;
53
use Psr\Log\LoggerInterface;
54
55
/**
56
 * Collection of useful functions
57
 */
58
class OC_Helper {
59
	private static $templateManager;
60
61
	/**
62
	 * Make a human file size
63
	 * @param int|float $bytes file size in bytes
64
	 * @return string a human readable file size
65
	 *
66
	 * Makes 2048 to 2 kB.
67
	 */
68
	public static function humanFileSize(int|float $bytes): string {
69
		if ($bytes < 0) {
70
			return "?";
71
		}
72
		if ($bytes < 1024) {
73
			return "$bytes B";
74
		}
75
		$bytes = round($bytes / 1024, 0);
76
		if ($bytes < 1024) {
77
			return "$bytes KB";
78
		}
79
		$bytes = round($bytes / 1024, 1);
80
		if ($bytes < 1024) {
81
			return "$bytes MB";
82
		}
83
		$bytes = round($bytes / 1024, 1);
84
		if ($bytes < 1024) {
85
			return "$bytes GB";
86
		}
87
		$bytes = round($bytes / 1024, 1);
88
		if ($bytes < 1024) {
89
			return "$bytes TB";
90
		}
91
92
		$bytes = round($bytes / 1024, 1);
93
		return "$bytes PB";
94
	}
95
96
	/**
97
	 * Make a computer file size
98
	 * @param string $str file size in human readable format
99
	 * @return false|int|float a file size in bytes
100
	 *
101
	 * Makes 2kB to 2048.
102
	 *
103
	 * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418
104
	 */
105
	public static function computerFileSize(string $str): false|int|float {
106
		$str = strtolower($str);
107
		if (is_numeric($str)) {
108
			return Util::numericToNumber($str);
109
		}
110
111
		$bytes_array = [
112
			'b' => 1,
113
			'k' => 1024,
114
			'kb' => 1024,
115
			'mb' => 1024 * 1024,
116
			'm' => 1024 * 1024,
117
			'gb' => 1024 * 1024 * 1024,
118
			'g' => 1024 * 1024 * 1024,
119
			'tb' => 1024 * 1024 * 1024 * 1024,
120
			't' => 1024 * 1024 * 1024 * 1024,
121
			'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
122
			'p' => 1024 * 1024 * 1024 * 1024 * 1024,
123
		];
124
125
		$bytes = (float)$str;
126
127
		if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
128
			$bytes *= $bytes_array[$matches[1]];
129
		} else {
130
			return false;
131
		}
132
133
		return Util::numericToNumber(round($bytes));
134
	}
135
136
	/**
137
	 * Recursive copying of folders
138
	 * @param string $src source folder
139
	 * @param string $dest target folder
140
	 * @return void
141
	 */
142
	public static function copyr($src, $dest) {
143
		if (is_dir($src)) {
144
			if (!is_dir($dest)) {
145
				mkdir($dest);
146
			}
147
			$files = scandir($src);
148
			foreach ($files as $file) {
149
				if ($file != "." && $file != "..") {
150
					self::copyr("$src/$file", "$dest/$file");
151
				}
152
			}
153
		} elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) {
154
			copy($src, $dest);
155
		}
156
	}
157
158
	/**
159
	 * Recursive deletion of folders
160
	 * @param string $dir path to the folder
161
	 * @param bool $deleteSelf if set to false only the content of the folder will be deleted
162
	 * @return bool
163
	 */
164
	public static function rmdirr($dir, $deleteSelf = true) {
165
		if (is_dir($dir)) {
166
			$files = new RecursiveIteratorIterator(
167
				new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
168
				RecursiveIteratorIterator::CHILD_FIRST
169
			);
170
171
			foreach ($files as $fileInfo) {
172
				/** @var SplFileInfo $fileInfo */
173
				if ($fileInfo->isLink()) {
174
					unlink($fileInfo->getPathname());
175
				} elseif ($fileInfo->isDir()) {
176
					rmdir($fileInfo->getRealPath());
177
				} else {
178
					unlink($fileInfo->getRealPath());
179
				}
180
			}
181
			if ($deleteSelf) {
182
				rmdir($dir);
183
			}
184
		} elseif (file_exists($dir)) {
185
			if ($deleteSelf) {
186
				unlink($dir);
187
			}
188
		}
189
		if (!$deleteSelf) {
190
			return true;
191
		}
192
193
		return !file_exists($dir);
194
	}
195
196
	/**
197
	 * @deprecated 18.0.0
198
	 * @return \OC\Files\Type\TemplateManager
199
	 */
200
	public static function getFileTemplateManager() {
201
		if (!self::$templateManager) {
202
			self::$templateManager = new \OC\Files\Type\TemplateManager();
0 ignored issues
show
Deprecated Code introduced by
The class OC\Files\Type\TemplateManager has been deprecated: 18.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

202
			self::$templateManager = /** @scrutinizer ignore-deprecated */ new \OC\Files\Type\TemplateManager();
Loading history...
203
		}
204
		return self::$templateManager;
205
	}
206
207
	/**
208
	 * detect if a given program is found in the search PATH
209
	 *
210
	 * @param string $name
211
	 * @param bool $path
212
	 * @internal param string $program name
213
	 * @internal param string $optional search path, defaults to $PATH
214
	 * @return bool    true if executable program found in path
215
	 */
216
	public static function canExecute($name, $path = false) {
217
		// path defaults to PATH from environment if not set
218
		if ($path === false) {
219
			$path = getenv("PATH");
220
		}
221
		// we look for an executable file of that name
222
		$exts = [""];
223
		$check_fn = "is_executable";
224
		// Default check will be done with $path directories :
225
		$dirs = explode(PATH_SEPARATOR, $path);
226
		// WARNING : We have to check if open_basedir is enabled :
227
		$obd = OC::$server->get(IniGetWrapper::class)->getString('open_basedir');
228
		if ($obd != "none") {
229
			$obd_values = explode(PATH_SEPARATOR, $obd);
230
			if (count($obd_values) > 0 and $obd_values[0]) {
231
				// open_basedir is in effect !
232
				// We need to check if the program is in one of these dirs :
233
				$dirs = $obd_values;
234
			}
235
		}
236
		foreach ($dirs as $dir) {
237
			foreach ($exts as $ext) {
238
				if ($check_fn("$dir/$name" . $ext)) {
239
					return true;
240
				}
241
			}
242
		}
243
		return false;
244
	}
245
246
	/**
247
	 * copy the contents of one stream to another
248
	 *
249
	 * @param resource $source
250
	 * @param resource $target
251
	 * @return array the number of bytes copied and result
252
	 */
253
	public static function streamCopy($source, $target) {
254
		if (!$source or !$target) {
0 ignored issues
show
introduced by
$source is of type resource, thus it always evaluated to false.
Loading history...
255
			return [0, false];
256
		}
257
		$bufSize = 8192;
258
		$result = true;
259
		$count = 0;
260
		while (!feof($source)) {
261
			$buf = fread($source, $bufSize);
262
			$bytesWritten = fwrite($target, $buf);
263
			if ($bytesWritten !== false) {
264
				$count += $bytesWritten;
265
			}
266
			// note: strlen is expensive so only use it when necessary,
267
			// on the last block
268
			if ($bytesWritten === false
269
				|| ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
270
			) {
271
				// write error, could be disk full ?
272
				$result = false;
273
				break;
274
			}
275
		}
276
		return [$count, $result];
277
	}
278
279
	/**
280
	 * Adds a suffix to the name in case the file exists
281
	 *
282
	 * @param string $path
283
	 * @param string $filename
284
	 * @return string
285
	 */
286
	public static function buildNotExistingFileName($path, $filename) {
287
		$view = \OC\Files\Filesystem::getView();
288
		return self::buildNotExistingFileNameForView($path, $filename, $view);
289
	}
290
291
	/**
292
	 * Adds a suffix to the name in case the file exists
293
	 *
294
	 * @param string $path
295
	 * @param string $filename
296
	 * @return string
297
	 */
298
	public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
299
		if ($path === '/') {
300
			$path = '';
301
		}
302
		if ($pos = strrpos($filename, '.')) {
303
			$name = substr($filename, 0, $pos);
304
			$ext = substr($filename, $pos);
305
		} else {
306
			$name = $filename;
307
			$ext = '';
308
		}
309
310
		$newpath = $path . '/' . $filename;
311
		if ($view->file_exists($newpath)) {
312
			if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
313
				//Replace the last "(number)" with "(number+1)"
314
				$last_match = count($matches[0]) - 1;
315
				$counter = $matches[1][$last_match][0] + 1;
316
				$offset = $matches[0][$last_match][1];
317
				$match_length = strlen($matches[0][$last_match][0]);
318
			} else {
319
				$counter = 2;
320
				$match_length = 0;
321
				$offset = false;
322
			}
323
			do {
324
				if ($offset) {
325
					//Replace the last "(number)" with "(number+1)"
326
					$newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
327
				} else {
328
					$newname = $name . ' (' . $counter . ')';
329
				}
330
				$newpath = $path . '/' . $newname . $ext;
0 ignored issues
show
Bug introduced by
Are you sure $newname of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

330
				$newpath = $path . '/' . /** @scrutinizer ignore-type */ $newname . $ext;
Loading history...
331
				$counter++;
332
			} while ($view->file_exists($newpath));
333
		}
334
335
		return $newpath;
336
	}
337
338
	/**
339
	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
340
	 *
341
	 * @param array $input The array to work on
342
	 * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
343
	 * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
344
	 * @return array
345
	 *
346
	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
347
	 * based on https://www.php.net/manual/en/function.array-change-key-case.php#107715
348
	 *
349
	 */
350
	public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
351
		$case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
352
		$ret = [];
353
		foreach ($input as $k => $v) {
354
			$ret[mb_convert_case($k, $case, $encoding)] = $v;
355
		}
356
		return $ret;
357
	}
358
359
	/**
360
	 * performs a search in a nested array
361
	 * @param array $haystack the array to be searched
362
	 * @param string $needle the search string
363
	 * @param mixed $index optional, only search this key name
364
	 * @return mixed the key of the matching field, otherwise false
365
	 *
366
	 * performs a search in a nested array
367
	 *
368
	 * taken from https://www.php.net/manual/en/function.array-search.php#97645
369
	 */
370
	public static function recursiveArraySearch($haystack, $needle, $index = null) {
371
		$aIt = new RecursiveArrayIterator($haystack);
372
		$it = new RecursiveIteratorIterator($aIt);
373
374
		while ($it->valid()) {
375
			if (((isset($index) and ($it->key() == $index)) or !isset($index)) and ($it->current() == $needle)) {
376
				return $aIt->key();
377
			}
378
379
			$it->next();
380
		}
381
382
		return false;
383
	}
384
385
	/**
386
	 * calculates the maximum upload size respecting system settings, free space and user quota
387
	 *
388
	 * @param string $dir the current folder where the user currently operates
389
	 * @param int|float $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
390
	 * @return int|float number of bytes representing
391
	 */
392
	public static function maxUploadFilesize($dir, $freeSpace = null) {
393
		if (is_null($freeSpace) || $freeSpace < 0) {
394
			$freeSpace = self::freeSpace($dir);
395
		}
396
		return min($freeSpace, self::uploadLimit());
397
	}
398
399
	/**
400
	 * Calculate free space left within user quota
401
	 *
402
	 * @param string $dir the current folder where the user currently operates
403
	 * @return int|float number of bytes representing
404
	 */
405
	public static function freeSpace($dir) {
406
		$freeSpace = \OC\Files\Filesystem::free_space($dir);
407
		if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
408
			$freeSpace = max($freeSpace, 0);
409
			return $freeSpace;
410
		} else {
411
			return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
412
		}
413
	}
414
415
	/**
416
	 * Calculate PHP upload limit
417
	 *
418
	 * @return int|float PHP upload file size limit
419
	 */
420
	public static function uploadLimit() {
421
		$ini = \OC::$server->get(IniGetWrapper::class);
422
		$upload_max_filesize = Util::computerFileSize($ini->get('upload_max_filesize')) ?: 0;
423
		$post_max_size = Util::computerFileSize($ini->get('post_max_size')) ?: 0;
424
		if ($upload_max_filesize === 0 && $post_max_size === 0) {
425
			return INF;
426
		} elseif ($upload_max_filesize === 0 || $post_max_size === 0) {
427
			return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
428
		} else {
429
			return min($upload_max_filesize, $post_max_size);
430
		}
431
	}
432
433
	/**
434
	 * Checks if a function is available
435
	 *
436
	 * @deprecated Since 25.0.0 use \OCP\Util::isFunctionEnabled instead
437
	 */
438
	public static function is_function_enabled(string $function_name): bool {
439
		return \OCP\Util::isFunctionEnabled($function_name);
440
	}
441
442
	/**
443
	 * Try to find a program
444
	 * @deprecated Since 25.0.0 Use \OC\BinaryFinder directly
445
	 */
446
	public static function findBinaryPath(string $program): ?string {
447
		$result = \OCP\Server::get(IBinaryFinder::class)->findBinaryPath($program);
448
		return $result !== false ? $result : null;
449
	}
450
451
	/**
452
	 * Calculate the disc space for the given path
453
	 *
454
	 * BEWARE: this requires that Util::setupFS() was called
455
	 * already !
456
	 *
457
	 * @param string $path
458
	 * @param \OCP\Files\FileInfo $rootInfo (optional)
459
	 * @param bool $includeMountPoints whether to include mount points in the size calculation
460
	 * @param bool $useCache whether to use the cached quota values
461
	 * @return array
462
	 * @throws \OCP\Files\NotFoundException
463
	 */
464
	public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true, $useCache = true) {
465
		/** @var ICacheFactory $cacheFactory */
466
		$cacheFactory = \OC::$server->get(ICacheFactory::class);
467
		$memcache = $cacheFactory->createLocal('storage_info');
468
469
		// return storage info without adding mount points
470
		$includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
471
472
		$view = Filesystem::getView();
473
		if (!$view) {
0 ignored issues
show
introduced by
$view is of type OC\Files\View, thus it always evaluated to true.
Loading history...
474
			throw new \OCP\Files\NotFoundException();
475
		}
476
		$fullPath = Filesystem::normalizePath($view->getAbsolutePath($path));
477
478
		$cacheKey = $fullPath. '::' . ($includeMountPoints ? 'include' : 'exclude');
479
		if ($useCache) {
480
			$cached = $memcache->get($cacheKey);
481
			if ($cached) {
482
				return $cached;
483
			}
484
		}
485
486
		if (!$rootInfo) {
487
			$rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false);
488
		}
489
		if (!$rootInfo instanceof \OCP\Files\FileInfo) {
490
			throw new \OCP\Files\NotFoundException();
491
		}
492
		$used = $rootInfo->getSize($includeMountPoints);
493
		if ($used < 0) {
494
			$used = 0;
495
		}
496
		$quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
497
		$mount = $rootInfo->getMountPoint();
498
		$storage = $mount->getStorage();
499
		$sourceStorage = $storage;
500
		if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
501
			$includeExtStorage = false;
502
		}
503
		if ($includeExtStorage) {
504
			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
505
				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
506
			) {
507
				/** @var \OC\Files\Storage\Home $storage */
508
				$user = $storage->getUser();
509
			} else {
510
				$user = \OC::$server->getUserSession()->getUser();
511
			}
512
			$quota = OC_Util::getUserQuota($user);
513
			if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
514
				// always get free space / total space from root + mount points
515
				return self::getGlobalStorageInfo($quota, $user, $mount);
516
			}
517
		}
518
519
		// TODO: need a better way to get total space from storage
520
		if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
0 ignored issues
show
Bug introduced by
The method instanceOfStorage() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

520
		if ($sourceStorage->/** @scrutinizer ignore-call */ instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
521
			/** @var \OC\Files\Storage\Wrapper\Quota $storage */
522
			$quota = $sourceStorage->getQuota();
0 ignored issues
show
Bug introduced by
The method getQuota() does not exist on OCP\Files\Storage\IStorage. It seems like you code against a sub-type of OCP\Files\Storage\IStorage such as OCA\Files_Sharing\SharedStorage or OCA\Files_Sharing\SharedStorage or OC\Files\Storage\Wrapper\Wrapper or OC\Files\Storage\Wrapper\Wrapper. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

522
			/** @scrutinizer ignore-call */ 
523
   $quota = $sourceStorage->getQuota();
Loading history...
523
		}
524
		try {
525
			$free = $sourceStorage->free_space($rootInfo->getInternalPath());
526
		} catch (\Exception $e) {
527
			if ($path === "") {
528
				throw $e;
529
			}
530
			/** @var LoggerInterface $logger */
531
			$logger = \OC::$server->get(LoggerInterface::class);
532
			$logger->warning("Error while getting quota info, using root quota", ['exception' => $e]);
533
			$rootInfo = self::getStorageInfo("");
534
			$memcache->set($cacheKey, $rootInfo, 5 * 60);
535
			return $rootInfo;
536
		}
537
		if ($free >= 0) {
538
			$total = $free + $used;
539
		} else {
540
			$total = $free; //either unknown or unlimited
541
		}
542
		if ($total > 0) {
543
			if ($quota > 0 && $total > $quota) {
544
				$total = $quota;
545
			}
546
			// prevent division by zero or error codes (negative values)
547
			$relative = round(($used / $total) * 10000) / 100;
548
		} else {
549
			$relative = 0;
550
		}
551
552
		$ownerId = $storage->getOwner($path);
553
		$ownerDisplayName = '';
554
		if ($ownerId) {
555
			$ownerDisplayName = \OC::$server->getUserManager()->getDisplayName($ownerId) ?? '';
556
		}
557
558
		if (substr_count($mount->getMountPoint(), '/') < 3) {
559
			$mountPoint = '';
560
		} else {
561
			[,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
562
		}
563
564
		$info = [
565
			'free' => $free,
566
			'used' => $used,
567
			'quota' => $quota,
568
			'total' => $total,
569
			'relative' => $relative,
570
			'owner' => $ownerId,
571
			'ownerDisplayName' => $ownerDisplayName,
572
			'mountType' => $mount->getMountType(),
573
			'mountPoint' => trim($mountPoint, '/'),
574
		];
575
576
		$memcache->set($cacheKey, $info, 5 * 60);
577
578
		return $info;
579
	}
580
581
	/**
582
	 * Get storage info including all mount points and quota
583
	 */
584
	private static function getGlobalStorageInfo(int|float $quota, IUser $user, IMountPoint $mount): array {
585
		$rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
586
		$used = $rootInfo['size'];
587
		if ($used < 0) {
588
			$used = 0;
589
		}
590
591
		$total = $quota;
592
		$free = $quota - $used;
593
594
		if ($total > 0) {
595
			if ($quota > 0 && $total > $quota) {
596
				$total = $quota;
597
			}
598
			// prevent division by zero or error codes (negative values)
599
			$relative = round(($used / $total) * 10000) / 100;
600
		} else {
601
			$relative = 0;
602
		}
603
604
		if (substr_count($mount->getMountPoint(), '/') < 3) {
605
			$mountPoint = '';
606
		} else {
607
			[,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
608
		}
609
610
		return [
611
			'free' => $free,
612
			'used' => $used,
613
			'total' => $total,
614
			'relative' => $relative,
615
			'quota' => $quota,
616
			'owner' => $user->getUID(),
617
			'ownerDisplayName' => $user->getDisplayName(),
618
			'mountType' => $mount->getMountType(),
619
			'mountPoint' => trim($mountPoint, '/'),
620
		];
621
	}
622
623
	public static function clearStorageInfo(string $absolutePath): void {
624
		/** @var ICacheFactory $cacheFactory */
625
		$cacheFactory = \OC::$server->get(ICacheFactory::class);
626
		$memcache = $cacheFactory->createLocal('storage_info');
627
		$cacheKeyPrefix = Filesystem::normalizePath($absolutePath) . '::';
628
		$memcache->remove($cacheKeyPrefix . 'include');
629
		$memcache->remove($cacheKeyPrefix . 'exclude');
630
	}
631
632
	/**
633
	 * Returns whether the config file is set manually to read-only
634
	 * @return bool
635
	 */
636
	public static function isReadOnlyConfigEnabled() {
637
		return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false);
638
	}
639
}
640