You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

806 lines
19 KiB

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