PageRenderTime 48ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/private/files/filesystem.php

https://github.com/JasonEades/core
PHP | 777 lines | 368 code | 101 blank | 308 comment | 47 complexity | 1ad5689c7b1aa3406214ff5d85c531be MD5 | raw file
Possible License(s): AGPL-3.0, AGPL-1.0, Apache-2.0, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file.
  7. */
  8. /**
  9. * Class for abstraction of filesystem functions
  10. * This class won't call any filesystem functions for itself but will pass them to the correct OC_Filestorage object
  11. * this class should also handle all the file permission related stuff
  12. *
  13. * Hooks provided:
  14. * read(path)
  15. * write(path, &run)
  16. * post_write(path)
  17. * create(path, &run) (when a file is created, both create and write will be emitted in that order)
  18. * post_create(path)
  19. * delete(path, &run)
  20. * post_delete(path)
  21. * rename(oldpath,newpath, &run)
  22. * post_rename(oldpath,newpath)
  23. * copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emitted in that order)
  24. * post_rename(oldpath,newpath)
  25. * post_initMountPoints(user, user_dir)
  26. *
  27. * the &run parameter can be set to false to prevent the operation from occurring
  28. */
  29. namespace OC\Files;
  30. use OC\Files\Storage\Loader;
  31. const SPACE_NOT_COMPUTED = -1;
  32. const SPACE_UNKNOWN = -2;
  33. const SPACE_UNLIMITED = -3;
  34. class Filesystem {
  35. /**
  36. * @var Mount\Manager $mounts
  37. */
  38. private static $mounts;
  39. public static $loaded = false;
  40. /**
  41. * @var \OC\Files\View $defaultInstance
  42. */
  43. static private $defaultInstance;
  44. /**
  45. * classname which used for hooks handling
  46. * used as signalclass in OC_Hooks::emit()
  47. */
  48. const CLASSNAME = 'OC_Filesystem';
  49. /**
  50. * signalname emitted before file renaming
  51. *
  52. * @param string $oldpath
  53. * @param string $newpath
  54. */
  55. const signal_rename = 'rename';
  56. /**
  57. * signal emitted after file renaming
  58. *
  59. * @param string $oldpath
  60. * @param string $newpath
  61. */
  62. const signal_post_rename = 'post_rename';
  63. /**
  64. * signal emitted before file/dir creation
  65. *
  66. * @param string $path
  67. * @param bool $run changing this flag to false in hook handler will cancel event
  68. */
  69. const signal_create = 'create';
  70. /**
  71. * signal emitted after file/dir creation
  72. *
  73. * @param string $path
  74. * @param bool $run changing this flag to false in hook handler will cancel event
  75. */
  76. const signal_post_create = 'post_create';
  77. /**
  78. * signal emits before file/dir copy
  79. *
  80. * @param string $oldpath
  81. * @param string $newpath
  82. * @param bool $run changing this flag to false in hook handler will cancel event
  83. */
  84. const signal_copy = 'copy';
  85. /**
  86. * signal emits after file/dir copy
  87. *
  88. * @param string $oldpath
  89. * @param string $newpath
  90. */
  91. const signal_post_copy = 'post_copy';
  92. /**
  93. * signal emits before file/dir save
  94. *
  95. * @param string $path
  96. * @param bool $run changing this flag to false in hook handler will cancel event
  97. */
  98. const signal_write = 'write';
  99. /**
  100. * signal emits after file/dir save
  101. *
  102. * @param string $path
  103. */
  104. const signal_post_write = 'post_write';
  105. /**
  106. * signal emitted before file/dir update
  107. *
  108. * @param string $path
  109. * @param bool $run changing this flag to false in hook handler will cancel event
  110. */
  111. const signal_update = 'update';
  112. /**
  113. * signal emitted after file/dir update
  114. *
  115. * @param string $path
  116. * @param bool $run changing this flag to false in hook handler will cancel event
  117. */
  118. const signal_post_update = 'post_update';
  119. /**
  120. * signal emits when reading file/dir
  121. *
  122. * @param string $path
  123. */
  124. const signal_read = 'read';
  125. /**
  126. * signal emits when removing file/dir
  127. *
  128. * @param string $path
  129. */
  130. const signal_delete = 'delete';
  131. /**
  132. * parameters definitions for signals
  133. */
  134. const signal_param_path = 'path';
  135. const signal_param_oldpath = 'oldpath';
  136. const signal_param_newpath = 'newpath';
  137. /**
  138. * run - changing this flag to false in hook handler will cancel event
  139. */
  140. const signal_param_run = 'run';
  141. /**
  142. * @var \OC\Files\Storage\Loader $loader
  143. */
  144. private static $loader;
  145. /**
  146. * @param callable $wrapper
  147. */
  148. public static function addStorageWrapper($wrapperName, $wrapper) {
  149. self::getLoader()->addStorageWrapper($wrapperName, $wrapper);
  150. $mounts = self::getMountManager()->getAll();
  151. foreach ($mounts as $mount) {
  152. $mount->wrapStorage($wrapper);
  153. }
  154. }
  155. public static function getLoader() {
  156. if (!self::$loader) {
  157. self::$loader = new Loader();
  158. }
  159. return self::$loader;
  160. }
  161. public static function getMountManager() {
  162. if (!self::$mounts) {
  163. \OC_Util::setupFS();
  164. }
  165. return self::$mounts;
  166. }
  167. /**
  168. * get the mountpoint of the storage object for a path
  169. * ( note: because a storage is not always mounted inside the fakeroot, the
  170. * returned mountpoint is relative to the absolute root of the filesystem
  171. * and doesn't take the chroot into account )
  172. *
  173. * @param string $path
  174. * @return string
  175. */
  176. static public function getMountPoint($path) {
  177. if (!self::$mounts) {
  178. \OC_Util::setupFS();
  179. }
  180. $mount = self::$mounts->find($path);
  181. if ($mount) {
  182. return $mount->getMountPoint();
  183. } else {
  184. return '';
  185. }
  186. }
  187. /**
  188. * get a list of all mount points in a directory
  189. *
  190. * @param string $path
  191. * @return string[]
  192. */
  193. static public function getMountPoints($path) {
  194. if (!self::$mounts) {
  195. \OC_Util::setupFS();
  196. }
  197. $result = array();
  198. $mounts = self::$mounts->findIn($path);
  199. foreach ($mounts as $mount) {
  200. $result[] = $mount->getMountPoint();
  201. }
  202. return $result;
  203. }
  204. /**
  205. * get the storage mounted at $mountPoint
  206. *
  207. * @param string $mountPoint
  208. * @return \OC\Files\Storage\Storage
  209. */
  210. public static function getStorage($mountPoint) {
  211. if (!self::$mounts) {
  212. \OC_Util::setupFS();
  213. }
  214. $mount = self::$mounts->find($mountPoint);
  215. return $mount->getStorage();
  216. }
  217. /**
  218. * @param string $id
  219. * @return Mount\Mount[]
  220. */
  221. public static function getMountByStorageId($id) {
  222. if (!self::$mounts) {
  223. \OC_Util::setupFS();
  224. }
  225. return self::$mounts->findByStorageId($id);
  226. }
  227. /**
  228. * @param int $id
  229. * @return Mount\Mount[]
  230. */
  231. public static function getMountByNumericId($id) {
  232. if (!self::$mounts) {
  233. \OC_Util::setupFS();
  234. }
  235. return self::$mounts->findByNumericId($id);
  236. }
  237. /**
  238. * resolve a path to a storage and internal path
  239. *
  240. * @param string $path
  241. * @return array an array consisting of the storage and the internal path
  242. */
  243. static public function resolvePath($path) {
  244. if (!self::$mounts) {
  245. \OC_Util::setupFS();
  246. }
  247. $mount = self::$mounts->find($path);
  248. if ($mount) {
  249. return array($mount->getStorage(), $mount->getInternalPath($path));
  250. } else {
  251. return array(null, null);
  252. }
  253. }
  254. static public function init($user, $root) {
  255. if (self::$defaultInstance) {
  256. return false;
  257. }
  258. self::getLoader();
  259. self::$defaultInstance = new View($root);
  260. if (!self::$mounts) {
  261. self::$mounts = new Mount\Manager();
  262. }
  263. //load custom mount config
  264. self::initMountPoints($user);
  265. self::$loaded = true;
  266. return true;
  267. }
  268. static public function initMounts() {
  269. if (!self::$mounts) {
  270. self::$mounts = new Mount\Manager();
  271. }
  272. }
  273. /**
  274. * Initialize system and personal mount points for a user
  275. *
  276. * @param string $user
  277. */
  278. public static function initMountPoints($user = '') {
  279. if ($user == '') {
  280. $user = \OC_User::getUser();
  281. }
  282. $parser = new \OC\ArrayParser();
  283. $root = \OC_User::getHome($user);
  284. $userObject = \OC_User::getManager()->get($user);
  285. if (!is_null($userObject)) {
  286. // check for legacy home id (<= 5.0.12)
  287. if (\OC\Files\Cache\Storage::exists('local::' . $root . '/')) {
  288. self::mount('\OC\Files\Storage\Home', array('user' => $userObject, 'legacy' => true), $user);
  289. }
  290. else {
  291. self::mount('\OC\Files\Storage\Home', array('user' => $userObject), $user);
  292. }
  293. }
  294. else {
  295. self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user);
  296. }
  297. self::mountCacheDir($user);
  298. // Chance to mount for other storages
  299. \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user, 'user_dir' => $root));
  300. }
  301. /**
  302. * Mounts the cache directory
  303. * @param string $user user name
  304. */
  305. private static function mountCacheDir($user) {
  306. $cacheBaseDir = \OC_Config::getValue('cache_path', '');
  307. if ($cacheBaseDir === '') {
  308. // use local cache dir relative to the user's home
  309. $subdir = 'cache';
  310. $view = new \OC\Files\View('/' . $user);
  311. if(!$view->file_exists($subdir)) {
  312. $view->mkdir($subdir);
  313. }
  314. } else {
  315. $cacheDir = rtrim($cacheBaseDir, '/') . '/' . $user;
  316. if (!file_exists($cacheDir)) {
  317. mkdir($cacheDir, 0770, true);
  318. }
  319. // mount external cache dir to "/$user/cache" mount point
  320. self::mount('\OC\Files\Storage\Local', array('datadir' => $cacheDir), '/' . $user . '/cache');
  321. }
  322. }
  323. /**
  324. * get the default filesystem view
  325. *
  326. * @return View
  327. */
  328. static public function getView() {
  329. return self::$defaultInstance;
  330. }
  331. /**
  332. * tear down the filesystem, removing all storage providers
  333. */
  334. static public function tearDown() {
  335. self::clearMounts();
  336. self::$defaultInstance = null;
  337. }
  338. /**
  339. * get the relative path of the root data directory for the current user
  340. * @return string
  341. *
  342. * Returns path like /admin/files
  343. */
  344. static public function getRoot() {
  345. if (!self::$defaultInstance) {
  346. return null;
  347. }
  348. return self::$defaultInstance->getRoot();
  349. }
  350. /**
  351. * clear all mounts and storage backends
  352. */
  353. public static function clearMounts() {
  354. if (self::$mounts) {
  355. self::$mounts->clear();
  356. }
  357. }
  358. /**
  359. * mount an \OC\Files\Storage\Storage in our virtual filesystem
  360. *
  361. * @param \OC\Files\Storage\Storage|string $class
  362. * @param array $arguments
  363. * @param string $mountpoint
  364. */
  365. static public function mount($class, $arguments, $mountpoint) {
  366. if (!self::$mounts) {
  367. \OC_Util::setupFS();
  368. }
  369. $mount = new Mount\Mount($class, $mountpoint, $arguments, self::getLoader());
  370. self::$mounts->addMount($mount);
  371. }
  372. /**
  373. * return the path to a local version of the file
  374. * we need this because we can't know if a file is stored local or not from
  375. * outside the filestorage and for some purposes a local file is needed
  376. *
  377. * @param string $path
  378. * @return string
  379. */
  380. static public function getLocalFile($path) {
  381. return self::$defaultInstance->getLocalFile($path);
  382. }
  383. /**
  384. * @param string $path
  385. * @return string
  386. */
  387. static public function getLocalFolder($path) {
  388. return self::$defaultInstance->getLocalFolder($path);
  389. }
  390. /**
  391. * return path to file which reflects one visible in browser
  392. *
  393. * @param string $path
  394. * @return string
  395. */
  396. static public function getLocalPath($path) {
  397. $datadir = \OC_User::getHome(\OC_User::getUser()) . '/files';
  398. $newpath = $path;
  399. if (strncmp($newpath, $datadir, strlen($datadir)) == 0) {
  400. $newpath = substr($path, strlen($datadir));
  401. }
  402. return $newpath;
  403. }
  404. /**
  405. * check if the requested path is valid
  406. *
  407. * @param string $path
  408. * @return bool
  409. */
  410. static public function isValidPath($path) {
  411. $path = self::normalizePath($path);
  412. if (!$path || $path[0] !== '/') {
  413. $path = '/' . $path;
  414. }
  415. if (strstr($path, '/../') || strrchr($path, '/') === '/..') {
  416. return false;
  417. }
  418. return true;
  419. }
  420. /**
  421. * checks if a file is blacklisted for storage in the filesystem
  422. * Listens to write and rename hooks
  423. *
  424. * @param array $data from hook
  425. */
  426. static public function isBlacklisted($data) {
  427. if (isset($data['path'])) {
  428. $path = $data['path'];
  429. } else if (isset($data['newpath'])) {
  430. $path = $data['newpath'];
  431. }
  432. if (isset($path)) {
  433. if (self::isFileBlacklisted($path)) {
  434. $data['run'] = false;
  435. }
  436. }
  437. }
  438. /**
  439. * @param string $filename
  440. * @return bool
  441. */
  442. static public function isFileBlacklisted($filename) {
  443. $blacklist = \OC_Config::getValue('blacklisted_files', array('.htaccess'));
  444. $filename = strtolower(basename($filename));
  445. return (in_array($filename, $blacklist));
  446. }
  447. /**
  448. * check if the directory should be ignored when scanning
  449. * NOTE: the special directories . and .. would cause never ending recursion
  450. * @param String $dir
  451. * @return boolean
  452. */
  453. static public function isIgnoredDir($dir) {
  454. if ($dir === '.' || $dir === '..') {
  455. return true;
  456. }
  457. return false;
  458. }
  459. /**
  460. * following functions are equivalent to their php builtin equivalents for arguments/return values.
  461. */
  462. static public function mkdir($path) {
  463. return self::$defaultInstance->mkdir($path);
  464. }
  465. static public function rmdir($path) {
  466. return self::$defaultInstance->rmdir($path);
  467. }
  468. static public function opendir($path) {
  469. return self::$defaultInstance->opendir($path);
  470. }
  471. static public function readdir($path) {
  472. return self::$defaultInstance->readdir($path);
  473. }
  474. static public function is_dir($path) {
  475. return self::$defaultInstance->is_dir($path);
  476. }
  477. static public function is_file($path) {
  478. return self::$defaultInstance->is_file($path);
  479. }
  480. static public function stat($path) {
  481. return self::$defaultInstance->stat($path);
  482. }
  483. static public function filetype($path) {
  484. return self::$defaultInstance->filetype($path);
  485. }
  486. static public function filesize($path) {
  487. return self::$defaultInstance->filesize($path);
  488. }
  489. static public function readfile($path) {
  490. return self::$defaultInstance->readfile($path);
  491. }
  492. static public function isCreatable($path) {
  493. return self::$defaultInstance->isCreatable($path);
  494. }
  495. static public function isReadable($path) {
  496. return self::$defaultInstance->isReadable($path);
  497. }
  498. static public function isUpdatable($path) {
  499. return self::$defaultInstance->isUpdatable($path);
  500. }
  501. static public function isDeletable($path) {
  502. return self::$defaultInstance->isDeletable($path);
  503. }
  504. static public function isSharable($path) {
  505. return self::$defaultInstance->isSharable($path);
  506. }
  507. static public function file_exists($path) {
  508. return self::$defaultInstance->file_exists($path);
  509. }
  510. static public function filemtime($path) {
  511. return self::$defaultInstance->filemtime($path);
  512. }
  513. static public function touch($path, $mtime = null) {
  514. return self::$defaultInstance->touch($path, $mtime);
  515. }
  516. /**
  517. * @return string
  518. */
  519. static public function file_get_contents($path) {
  520. return self::$defaultInstance->file_get_contents($path);
  521. }
  522. static public function file_put_contents($path, $data) {
  523. return self::$defaultInstance->file_put_contents($path, $data);
  524. }
  525. static public function unlink($path) {
  526. return self::$defaultInstance->unlink($path);
  527. }
  528. static public function rename($path1, $path2) {
  529. return self::$defaultInstance->rename($path1, $path2);
  530. }
  531. static public function copy($path1, $path2) {
  532. return self::$defaultInstance->copy($path1, $path2);
  533. }
  534. static public function fopen($path, $mode) {
  535. return self::$defaultInstance->fopen($path, $mode);
  536. }
  537. /**
  538. * @return string
  539. */
  540. static public function toTmpFile($path) {
  541. return self::$defaultInstance->toTmpFile($path);
  542. }
  543. static public function fromTmpFile($tmpFile, $path) {
  544. return self::$defaultInstance->fromTmpFile($tmpFile, $path);
  545. }
  546. static public function getMimeType($path) {
  547. return self::$defaultInstance->getMimeType($path);
  548. }
  549. static public function hash($type, $path, $raw = false) {
  550. return self::$defaultInstance->hash($type, $path, $raw);
  551. }
  552. static public function free_space($path = '/') {
  553. return self::$defaultInstance->free_space($path);
  554. }
  555. static public function search($query) {
  556. return self::$defaultInstance->search($query);
  557. }
  558. /**
  559. * @param string $query
  560. */
  561. static public function searchByMime($query) {
  562. return self::$defaultInstance->searchByMime($query);
  563. }
  564. /**
  565. * check if a file or folder has been updated since $time
  566. *
  567. * @param string $path
  568. * @param int $time
  569. * @return bool
  570. */
  571. static public function hasUpdated($path, $time) {
  572. return self::$defaultInstance->hasUpdated($path, $time);
  573. }
  574. /**
  575. * Fix common problems with a file path
  576. * @param string $path
  577. * @param bool $stripTrailingSlash
  578. * @return string
  579. */
  580. public static function normalizePath($path, $stripTrailingSlash = true) {
  581. if ($path == '') {
  582. return '/';
  583. }
  584. //no windows style slashes
  585. $path = str_replace('\\', '/', $path);
  586. //add leading slash
  587. if ($path[0] !== '/') {
  588. $path = '/' . $path;
  589. }
  590. // remove '/./'
  591. // ugly, but str_replace() can't replace them all in one go
  592. // as the replacement itself is part of the search string
  593. // which will only be found during the next iteration
  594. while (strpos($path, '/./') !== false) {
  595. $path = str_replace('/./', '/', $path);
  596. }
  597. // remove sequences of slashes
  598. $path = preg_replace('#/{2,}#', '/', $path);
  599. //remove trailing slash
  600. if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') {
  601. $path = substr($path, 0, -1);
  602. }
  603. // remove trailing '/.'
  604. if (substr($path, -2) == '/.') {
  605. $path = substr($path, 0, -2);
  606. }
  607. //normalize unicode if possible
  608. $path = \OC_Util::normalizeUnicode($path);
  609. return $path;
  610. }
  611. /**
  612. * get the filesystem info
  613. *
  614. * @param string $path
  615. * @param boolean $includeMountPoints whether to add mountpoint sizes,
  616. * defaults to true
  617. * @return \OC\Files\FileInfo
  618. */
  619. public static function getFileInfo($path, $includeMountPoints = true) {
  620. return self::$defaultInstance->getFileInfo($path, $includeMountPoints);
  621. }
  622. /**
  623. * change file metadata
  624. *
  625. * @param string $path
  626. * @param array $data
  627. * @return int
  628. *
  629. * returns the fileid of the updated file
  630. */
  631. public static function putFileInfo($path, $data) {
  632. return self::$defaultInstance->putFileInfo($path, $data);
  633. }
  634. /**
  635. * get the content of a directory
  636. *
  637. * @param string $directory path under datadirectory
  638. * @param string $mimetype_filter limit returned content to this mimetype or mimepart
  639. * @return \OC\Files\FileInfo[]
  640. */
  641. public static function getDirectoryContent($directory, $mimetype_filter = '') {
  642. return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter);
  643. }
  644. /**
  645. * Get the path of a file by id
  646. *
  647. * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file
  648. *
  649. * @param int $id
  650. * @return string
  651. */
  652. public static function getPath($id) {
  653. return self::$defaultInstance->getPath($id);
  654. }
  655. /**
  656. * Get the owner for a file or folder
  657. *
  658. * @param string $path
  659. * @return string
  660. */
  661. public static function getOwner($path) {
  662. return self::$defaultInstance->getOwner($path);
  663. }
  664. /**
  665. * get the ETag for a file or folder
  666. *
  667. * @param string $path
  668. * @return string
  669. */
  670. static public function getETag($path) {
  671. return self::$defaultInstance->getETag($path);
  672. }
  673. }
  674. \OC_Util::setupFS();