PageRenderTime 66ms CodeModel.GetById 39ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/files/filesystem.php

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