Advertisement
Guest User

Untitled

a guest
Jan 23rd, 2019
469
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 65.26 KB | None | 0 0
  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 Björn Schießle <[email protected]>
  8. * @author Florin Peter <[email protected]>
  9. * @author Jesús Macias <[email protected]>
  10. * @author Joas Schilling <[email protected]>
  11. * @author Jörn Friedrich Dreyer <[email protected]>
  12. * @author karakayasemi <[email protected]>
  13. * @author Klaas Freitag <[email protected]>
  14. * @author Lukas Reschke <[email protected]>
  15. * @author Luke Policinski <[email protected]>
  16. * @author Michael Gapczynski <[email protected]>
  17. * @author Morris Jobke <[email protected]>
  18. * @author Petr Svoboda <[email protected]>
  19. * @author Piotr Filiciak <[email protected]>
  20. * @author Robin Appelman <[email protected]>
  21. * @author Robin McCorkell <[email protected]>
  22. * @author Roeland Jago Douma <[email protected]>
  23. * @author Sam Tuke <[email protected]>
  24. * @author Stefan Weil <[email protected]>
  25. * @author Thomas Müller <[email protected]>
  26. * @author Thomas Tanghus <[email protected]>
  27. * @author Vincent Petry <[email protected]>
  28. *
  29. * @license AGPL-3.0
  30. *
  31. * This code is free software: you can redistribute it and/or modify
  32. * it under the terms of the GNU Affero General Public License, version 3,
  33. * as published by the Free Software Foundation.
  34. *
  35. * This program is distributed in the hope that it will be useful,
  36. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  37. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  38. * GNU Affero General Public License for more details.
  39. *
  40. * You should have received a copy of the GNU Affero General Public License, version 3,
  41. * along with this program. If not, see <http://www.gnu.org/licenses/>
  42. *
  43. */
  44.  
  45.  
  46. namespace OC\Files;
  47.  
  48. use Icewind\Streams\CallbackWrapper;
  49. use OC\Files\Mount\MoveableMount;
  50. use OC\Files\Storage\Storage;
  51. use OC\User\User;
  52. use OCP\Constants;
  53. use OCP\Files\Cache\ICacheEntry;
  54. use OCP\Files\EmptyFileNameException;
  55. use OCP\Files\FileNameTooLongException;
  56. use OCP\Files\InvalidCharacterInPathException;
  57. use OCP\Files\InvalidDirectoryException;
  58. use OCP\Files\InvalidPathException;
  59. use OCP\Files\Mount\IMountPoint;
  60. use OCP\Files\NotFoundException;
  61. use OCP\Files\ReservedWordException;
  62. use OCP\ILogger;
  63. use OCP\IUser;
  64. use OCP\Lock\ILockingProvider;
  65. use OCP\Lock\LockedException;
  66.  
  67. /**
  68. * Class to provide access to ownCloud filesystem via a "view", and methods for
  69. * working with files within that view (e.g. read, write, delete, etc.). Each
  70. * view is restricted to a set of directories via a virtual root. The default view
  71. * uses the currently logged in user's data directory as root (parts of
  72. * OC_Filesystem are merely a wrapper for OC\Files\View).
  73. *
  74. * Apps that need to access files outside of the user data folders (to modify files
  75. * belonging to a user other than the one currently logged in, for example) should
  76. * use this class directly rather than using OC_Filesystem, or making use of PHP's
  77. * built-in file manipulation functions. This will ensure all hooks and proxies
  78. * are triggered correctly.
  79. *
  80. * Filesystem functions are not called directly; they are passed to the correct
  81. * \OC\Files\Storage\Storage object
  82. */
  83. class View {
  84. /** @var string */
  85. private $fakeRoot = '';
  86.  
  87. /**
  88. * @var \OCP\Lock\ILockingProvider
  89. */
  90. protected $lockingProvider;
  91.  
  92. private $lockingEnabled;
  93.  
  94. private $updaterEnabled = true;
  95.  
  96. /** @var \OC\User\Manager */
  97. private $userManager;
  98.  
  99. /** @var \OCP\ILogger */
  100. private $logger;
  101.  
  102. /**
  103. * @param string $root
  104. * @throws \Exception If $root contains an invalid path
  105. */
  106. public function __construct($root = '') {
  107. if (is_null($root)) {
  108. throw new \InvalidArgumentException('Root can\'t be null');
  109. }
  110. if (!Filesystem::isValidPath($root)) {
  111. throw new \Exception();
  112. }
  113.  
  114. $this->fakeRoot = $root;
  115. $this->lockingProvider = \OC::$server->getLockingProvider();
  116. $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider);
  117. $this->userManager = \OC::$server->getUserManager();
  118. $this->logger = \OC::$server->getLogger();
  119. }
  120.  
  121. public function getAbsolutePath($path = '/') {
  122. if ($path === null) {
  123. return null;
  124. }
  125. $this->assertPathLength($path);
  126. if ($path === '') {
  127. $path = '/';
  128. }
  129. if ($path[0] !== '/') {
  130. $path = '/' . $path;
  131. }
  132. return $this->fakeRoot . $path;
  133. }
  134.  
  135. /**
  136. * change the root to a fake root
  137. *
  138. * @param string $fakeRoot
  139. * @return boolean|null
  140. */
  141. public function chroot($fakeRoot) {
  142. if (!$fakeRoot == '') {
  143. if ($fakeRoot[0] !== '/') {
  144. $fakeRoot = '/' . $fakeRoot;
  145. }
  146. }
  147. $this->fakeRoot = $fakeRoot;
  148. }
  149.  
  150. /**
  151. * get the fake root
  152. *
  153. * @return string
  154. */
  155. public function getRoot() {
  156. return $this->fakeRoot;
  157. }
  158.  
  159. /**
  160. * get path relative to the root of the view
  161. *
  162. * @param string $path
  163. * @return string
  164. */
  165. public function getRelativePath($path) {
  166. $this->assertPathLength($path);
  167. if ($this->fakeRoot == '') {
  168. return $path;
  169. }
  170.  
  171. if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) {
  172. return '/';
  173. }
  174.  
  175. // missing slashes can cause wrong matches!
  176. $root = rtrim($this->fakeRoot, '/') . '/';
  177.  
  178. if (strpos($path, $root) !== 0) {
  179. return null;
  180. } else {
  181. $path = substr($path, strlen($this->fakeRoot));
  182. if (strlen($path) === 0) {
  183. return '/';
  184. } else {
  185. return $path;
  186. }
  187. }
  188. }
  189.  
  190. /**
  191. * get the mountpoint of the storage object for a path
  192. * ( note: because a storage is not always mounted inside the fakeroot, the
  193. * returned mountpoint is relative to the absolute root of the filesystem
  194. * and does not take the chroot into account )
  195. *
  196. * @param string $path
  197. * @return string
  198. */
  199. public function getMountPoint($path) {
  200. return Filesystem::getMountPoint($this->getAbsolutePath($path));
  201. }
  202.  
  203. /**
  204. * get the mountpoint of the storage object for a path
  205. * ( note: because a storage is not always mounted inside the fakeroot, the
  206. * returned mountpoint is relative to the absolute root of the filesystem
  207. * and does not take the chroot into account )
  208. *
  209. * @param string $path
  210. * @return \OCP\Files\Mount\IMountPoint
  211. */
  212. public function getMount($path) {
  213. return Filesystem::getMountManager()->find($this->getAbsolutePath($path));
  214. }
  215.  
  216. /**
  217. * resolve a path to a storage and internal path
  218. *
  219. * @param string $path
  220. * @return array an array consisting of the storage and the internal path
  221. */
  222. public function resolvePath($path) {
  223. $a = $this->getAbsolutePath($path);
  224. $p = Filesystem::normalizePath($a);
  225. return Filesystem::resolvePath($p);
  226. }
  227.  
  228. /**
  229. * return the path to a local version of the file
  230. * we need this because we can't know if a file is stored local or not from
  231. * outside the filestorage and for some purposes a local file is needed
  232. *
  233. * @param string $path
  234. * @return string
  235. */
  236. public function getLocalFile($path) {
  237. $parent = substr($path, 0, strrpos($path, '/'));
  238. $path = $this->getAbsolutePath($path);
  239. list($storage, $internalPath) = Filesystem::resolvePath($path);
  240. if (Filesystem::isValidPath($parent) and $storage) {
  241. return $storage->getLocalFile($internalPath);
  242. } else {
  243. return null;
  244. }
  245. }
  246.  
  247. /**
  248. * @param string $path
  249. * @return string
  250. */
  251. public function getLocalFolder($path) {
  252. $parent = substr($path, 0, strrpos($path, '/'));
  253. $path = $this->getAbsolutePath($path);
  254. list($storage, $internalPath) = Filesystem::resolvePath($path);
  255. if (Filesystem::isValidPath($parent) and $storage) {
  256. return $storage->getLocalFolder($internalPath);
  257. } else {
  258. return null;
  259. }
  260. }
  261.  
  262. /**
  263. * the following functions operate with arguments and return values identical
  264. * to those of their PHP built-in equivalents. Mostly they are merely wrappers
  265. * for \OC\Files\Storage\Storage via basicOperation().
  266. */
  267. public function mkdir($path) {
  268. return $this->basicOperation('mkdir', $path, array('create', 'write'));
  269. }
  270.  
  271. /**
  272. * remove mount point
  273. *
  274. * @param \OC\Files\Mount\MoveableMount $mount
  275. * @param string $path relative to data/
  276. * @return boolean
  277. */
  278. protected function removeMount($mount, $path) {
  279. if ($mount instanceof MoveableMount) {
  280. // cut of /user/files to get the relative path to data/user/files
  281. $pathParts = explode('/', $path, 4);
  282. $relPath = '/' . $pathParts[3];
  283. $this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true);
  284. \OC_Hook::emit(
  285. Filesystem::CLASSNAME, "umount",
  286. array(Filesystem::signal_param_path => $relPath)
  287. );
  288. $this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true);
  289. $result = $mount->removeMount();
  290. $this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true);
  291. if ($result) {
  292. \OC_Hook::emit(
  293. Filesystem::CLASSNAME, "post_umount",
  294. array(Filesystem::signal_param_path => $relPath)
  295. );
  296. }
  297. $this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true);
  298. return $result;
  299. } else {
  300. // do not allow deleting the storage's root / the mount point
  301. // because for some storages it might delete the whole contents
  302. // but isn't supposed to work that way
  303. return false;
  304. }
  305. }
  306.  
  307. public function disableCacheUpdate() {
  308. $this->updaterEnabled = false;
  309. }
  310.  
  311. public function enableCacheUpdate() {
  312. $this->updaterEnabled = true;
  313. }
  314.  
  315. protected function writeUpdate(Storage $storage, $internalPath, $time = null) {
  316. if ($this->updaterEnabled) {
  317. if (is_null($time)) {
  318. $time = time();
  319. }
  320. $storage->getUpdater()->update($internalPath, $time);
  321. }
  322. }
  323.  
  324. protected function removeUpdate(Storage $storage, $internalPath) {
  325. if ($this->updaterEnabled) {
  326. $storage->getUpdater()->remove($internalPath);
  327. }
  328. }
  329.  
  330. protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) {
  331. if ($this->updaterEnabled) {
  332. $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
  333. }
  334. }
  335.  
  336. /**
  337. * @param string $path
  338. * @return bool|mixed
  339. */
  340. public function rmdir($path) {
  341. $absolutePath = $this->getAbsolutePath($path);
  342. $mount = Filesystem::getMountManager()->find($absolutePath);
  343. if ($mount->getInternalPath($absolutePath) === '') {
  344. return $this->removeMount($mount, $absolutePath);
  345. }
  346. if ($this->is_dir($path)) {
  347. $result = $this->basicOperation('rmdir', $path, array('delete'));
  348. } else {
  349. $result = false;
  350. }
  351.  
  352. if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
  353. $storage = $mount->getStorage();
  354. $internalPath = $mount->getInternalPath($absolutePath);
  355. $storage->getUpdater()->remove($internalPath);
  356. }
  357. return $result;
  358. }
  359.  
  360. /**
  361. * @param string $path
  362. * @return resource
  363. */
  364. public function opendir($path) {
  365. return $this->basicOperation('opendir', $path, array('read'));
  366. }
  367.  
  368. /**
  369. * @param string $path
  370. * @return bool|mixed
  371. */
  372. public function is_dir($path) {
  373. if ($path == '/') {
  374. return true;
  375. }
  376. return $this->basicOperation('is_dir', $path);
  377. }
  378.  
  379. /**
  380. * @param string $path
  381. * @return bool|mixed
  382. */
  383. public function is_file($path) {
  384. if ($path == '/') {
  385. return false;
  386. }
  387. return $this->basicOperation('is_file', $path);
  388. }
  389.  
  390. /**
  391. * @param string $path
  392. * @return mixed
  393. */
  394. public function stat($path) {
  395. return $this->basicOperation('stat', $path);
  396. }
  397.  
  398. /**
  399. * @param string $path
  400. * @return mixed
  401. */
  402. public function filetype($path) {
  403. return $this->basicOperation('filetype', $path);
  404. }
  405.  
  406. /**
  407. * @param string $path
  408. * @return mixed
  409. */
  410. public function filesize($path) {
  411. return $this->basicOperation('filesize', $path);
  412. }
  413.  
  414. /**
  415. * @param string $path
  416. * @return bool|mixed
  417. * @throws \OCP\Files\InvalidPathException
  418. */
  419. public function readfile($path) {
  420. $this->assertPathLength($path);
  421. @ob_end_clean();
  422. $handle = $this->fopen($path, 'rb');
  423. if ($handle) {
  424. $chunkSize = 8192; // 8 kB chunks
  425. while (!feof($handle)) {
  426. echo fread($handle, $chunkSize);
  427. flush();
  428. }
  429. fclose($handle);
  430. return $this->filesize($path);
  431. }
  432. return false;
  433. }
  434.  
  435. /**
  436. * @param string $path
  437. * @param int $from
  438. * @param int $to
  439. * @return bool|mixed
  440. * @throws \OCP\Files\InvalidPathException
  441. * @throws \OCP\Files\UnseekableException
  442. */
  443. public function readfilePart($path, $from, $to) {
  444. $this->assertPathLength($path);
  445. @ob_end_clean();
  446. $handle = $this->fopen($path, 'rb');
  447. if ($handle) {
  448. $chunkSize = 8192; // 8 kB chunks
  449. $startReading = true;
  450.  
  451. if ($from !== 0 && $from !== '0' && fseek($handle, $from) !== 0) {
  452. // forward file handle via chunked fread because fseek seem to have failed
  453.  
  454. $end = $from + 1;
  455. while (!feof($handle) && ftell($handle) < $end) {
  456. $len = $from - ftell($handle);
  457. if ($len > $chunkSize) {
  458. $len = $chunkSize;
  459. }
  460. $result = fread($handle, $len);
  461.  
  462. if ($result === false) {
  463. $startReading = false;
  464. break;
  465. }
  466. }
  467. }
  468.  
  469. if ($startReading) {
  470. $end = $to + 1;
  471. while (!feof($handle) && ftell($handle) < $end) {
  472. $len = $end - ftell($handle);
  473. if ($len > $chunkSize) {
  474. $len = $chunkSize;
  475. }
  476. echo fread($handle, $len);
  477. flush();
  478. }
  479. return ftell($handle) - $from;
  480. }
  481.  
  482. throw new \OCP\Files\UnseekableException('fseek error');
  483. }
  484. return false;
  485. }
  486.  
  487. /**
  488. * @param string $path
  489. * @return mixed
  490. */
  491. public function isCreatable($path) {
  492. return $this->basicOperation('isCreatable', $path);
  493. }
  494.  
  495. /**
  496. * @param string $path
  497. * @return mixed
  498. */
  499. public function isReadable($path) {
  500. return $this->basicOperation('isReadable', $path);
  501. }
  502.  
  503. /**
  504. * @param string $path
  505. * @return mixed
  506. */
  507. public function isUpdatable($path) {
  508. return $this->basicOperation('isUpdatable', $path);
  509. }
  510.  
  511. /**
  512. * @param string $path
  513. * @return bool|mixed
  514. */
  515. public function isDeletable($path) {
  516. $absolutePath = $this->getAbsolutePath($path);
  517. $mount = Filesystem::getMountManager()->find($absolutePath);
  518. if ($mount->getInternalPath($absolutePath) === '') {
  519. return $mount instanceof MoveableMount;
  520. }
  521. return $this->basicOperation('isDeletable', $path);
  522. }
  523.  
  524. /**
  525. * @param string $path
  526. * @return mixed
  527. */
  528. public function isSharable($path) {
  529. return $this->basicOperation('isSharable', $path);
  530. }
  531.  
  532. /**
  533. * @param string $path
  534. * @return bool|mixed
  535. */
  536. public function file_exists($path) {
  537. if ($path == '/') {
  538. return true;
  539. }
  540. return $this->basicOperation('file_exists', $path);
  541. }
  542.  
  543. /**
  544. * @param string $path
  545. * @return mixed
  546. */
  547. public function filemtime($path) {
  548. return $this->basicOperation('filemtime', $path);
  549. }
  550.  
  551. /**
  552. * @param string $path
  553. * @param int|string $mtime
  554. * @return bool
  555. */
  556. public function touch($path, $mtime = null) {
  557. if (!is_null($mtime) and !is_numeric($mtime)) {
  558. $mtime = strtotime($mtime);
  559. }
  560.  
  561. $hooks = array('touch');
  562.  
  563. if (!$this->file_exists($path)) {
  564. $hooks[] = 'create';
  565. $hooks[] = 'write';
  566. }
  567. $result = $this->basicOperation('touch', $path, $hooks, $mtime);
  568. if (!$result) {
  569. // If create file fails because of permissions on external storage like SMB folders,
  570. // check file exists and return false if not.
  571. if (!$this->file_exists($path)) {
  572. return false;
  573. }
  574. if (is_null($mtime)) {
  575. $mtime = time();
  576. }
  577. //if native touch fails, we emulate it by changing the mtime in the cache
  578. $this->putFileInfo($path, array('mtime' => floor($mtime)));
  579. }
  580. return true;
  581. }
  582.  
  583. /**
  584. * @param string $path
  585. * @return mixed
  586. */
  587. public function file_get_contents($path) {
  588. return $this->basicOperation('file_get_contents', $path, array('read'));
  589. }
  590.  
  591. /**
  592. * @param bool $exists
  593. * @param string $path
  594. * @param bool $run
  595. */
  596. protected function emit_file_hooks_pre($exists, $path, &$run) {
  597. if (!$exists) {
  598. \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array(
  599. Filesystem::signal_param_path => $this->getHookPath($path),
  600. Filesystem::signal_param_run => &$run,
  601. ));
  602. } else {
  603. \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array(
  604. Filesystem::signal_param_path => $this->getHookPath($path),
  605. Filesystem::signal_param_run => &$run,
  606. ));
  607. }
  608. \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array(
  609. Filesystem::signal_param_path => $this->getHookPath($path),
  610. Filesystem::signal_param_run => &$run,
  611. ));
  612. }
  613.  
  614. /**
  615. * @param bool $exists
  616. * @param string $path
  617. */
  618. protected function emit_file_hooks_post($exists, $path) {
  619. if (!$exists) {
  620. \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array(
  621. Filesystem::signal_param_path => $this->getHookPath($path),
  622. ));
  623. } else {
  624. \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array(
  625. Filesystem::signal_param_path => $this->getHookPath($path),
  626. ));
  627. }
  628. \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array(
  629. Filesystem::signal_param_path => $this->getHookPath($path),
  630. ));
  631. }
  632.  
  633. /**
  634. * @param string $path
  635. * @param string|resource $data
  636. * @return bool|mixed
  637. * @throws \Exception
  638. */
  639. public function file_put_contents($path, $data) {
  640. if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier
  641. $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
  642. if (Filesystem::isValidPath($path)
  643. and !Filesystem::isFileBlacklisted($path)
  644. ) {
  645. $path = $this->getRelativePath($absolutePath);
  646.  
  647. $this->lockFile($path, ILockingProvider::LOCK_SHARED);
  648.  
  649. $exists = $this->file_exists($path);
  650. $run = true;
  651. if ($this->shouldEmitHooks($path)) {
  652. $this->emit_file_hooks_pre($exists, $path, $run);
  653. }
  654. if (!$run) {
  655. $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
  656. return false;
  657. }
  658.  
  659. $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
  660.  
  661. /** @var \OC\Files\Storage\Storage $storage */
  662. list($storage, $internalPath) = $this->resolvePath($path);
  663. $target = $storage->fopen($internalPath, 'w');
  664. if ($target) {
  665. list (, $result) = \OC_Helper::streamCopy($data, $target);
  666. fclose($target);
  667. fclose($data);
  668.  
  669. $this->writeUpdate($storage, $internalPath);
  670.  
  671. $this->changeLock($path, ILockingProvider::LOCK_SHARED);
  672.  
  673. if ($this->shouldEmitHooks($path) && $result !== false) {
  674. $this->emit_file_hooks_post($exists, $path);
  675. }
  676. $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
  677. return $result;
  678. } else {
  679. $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
  680. return false;
  681. }
  682. } else {
  683. return false;
  684. }
  685. } else {
  686. $hooks = $this->file_exists($path) ? array('update', 'write') : array('create', 'write');
  687. return $this->basicOperation('file_put_contents', $path, $hooks, $data);
  688. }
  689. }
  690.  
  691. /**
  692. * @param string $path
  693. * @return bool|mixed
  694. */
  695. public function unlink($path) {
  696. if ($path === '' || $path === '/') {
  697. // do not allow deleting the root
  698. return false;
  699. }
  700. $postFix = (substr($path, -1) === '/') ? '/' : '';
  701. $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
  702. $mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
  703. if ($mount and $mount->getInternalPath($absolutePath) === '') {
  704. return $this->removeMount($mount, $absolutePath);
  705. }
  706. if ($this->is_dir($path)) {
  707. $result = $this->basicOperation('rmdir', $path, ['delete']);
  708. } else {
  709. $result = $this->basicOperation('unlink', $path, ['delete']);
  710. }
  711. if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
  712. $storage = $mount->getStorage();
  713. $internalPath = $mount->getInternalPath($absolutePath);
  714. $storage->getUpdater()->remove($internalPath);
  715. return true;
  716. } else {
  717. return $result;
  718. }
  719. }
  720.  
  721. /**
  722. * @param string $directory
  723. * @return bool|mixed
  724. */
  725. public function deleteAll($directory) {
  726. return $this->rmdir($directory);
  727. }
  728.  
  729. /**
  730. * Rename/move a file or folder from the source path to target path.
  731. *
  732. * @param string $path1 source path
  733. * @param string $path2 target path
  734. *
  735. * @return bool|mixed
  736. */
  737. public function rename($path1, $path2) {
  738. $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
  739. $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
  740. $result = false;
  741. if (
  742. Filesystem::isValidPath($path2)
  743. and Filesystem::isValidPath($path1)
  744. and !Filesystem::isFileBlacklisted($path2)
  745. ) {
  746. $path1 = $this->getRelativePath($absolutePath1);
  747. $path2 = $this->getRelativePath($absolutePath2);
  748. $exists = $this->file_exists($path2);
  749.  
  750. if ($path1 == null or $path2 == null) {
  751. return false;
  752. }
  753.  
  754. $this->lockFile($path1, ILockingProvider::LOCK_SHARED, true);
  755. try {
  756. $this->lockFile($path2, ILockingProvider::LOCK_SHARED, true);
  757.  
  758. $run = true;
  759. if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
  760. // if it was a rename from a part file to a regular file it was a write and not a rename operation
  761. $this->emit_file_hooks_pre($exists, $path2, $run);
  762. } elseif ($this->shouldEmitHooks($path1)) {
  763. \OC_Hook::emit(
  764. Filesystem::CLASSNAME, Filesystem::signal_rename,
  765. array(
  766. Filesystem::signal_param_oldpath => $this->getHookPath($path1),
  767. Filesystem::signal_param_newpath => $this->getHookPath($path2),
  768. Filesystem::signal_param_run => &$run
  769. )
  770. );
  771. }
  772. if ($run) {
  773. $this->verifyPath(dirname($path2), basename($path2));
  774.  
  775. $manager = Filesystem::getMountManager();
  776. $mount1 = $this->getMount($path1);
  777. $mount2 = $this->getMount($path2);
  778. $storage1 = $mount1->getStorage();
  779. $storage2 = $mount2->getStorage();
  780. $internalPath1 = $mount1->getInternalPath($absolutePath1);
  781. $internalPath2 = $mount2->getInternalPath($absolutePath2);
  782.  
  783. $this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true);
  784. try {
  785. $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true);
  786.  
  787. if ($internalPath1 === '') {
  788. if ($mount1 instanceof MoveableMount) {
  789. if ($this->isTargetAllowed($absolutePath2)) {
  790. /**
  791. * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
  792. */
  793. $sourceMountPoint = $mount1->getMountPoint();
  794. $result = $mount1->moveMount($absolutePath2);
  795. $manager->moveMount($sourceMountPoint, $mount1->getMountPoint());
  796. } else {
  797. $result = false;
  798. }
  799. } else {
  800. $result = false;
  801. }
  802. // moving a file/folder within the same mount point
  803. } elseif ($storage1 === $storage2) {
  804. if ($storage1) {
  805. $result = $storage1->rename($internalPath1, $internalPath2);
  806. } else {
  807. $result = false;
  808. }
  809. // moving a file/folder between storages (from $storage1 to $storage2)
  810. } else {
  811. $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
  812. }
  813.  
  814. if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
  815. // if it was a rename from a part file to a regular file it was a write and not a rename operation
  816. $this->writeUpdate($storage2, $internalPath2);
  817. } else if ($result) {
  818. if ($internalPath1 !== '') { // don't do a cache update for moved mounts
  819. $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
  820. }
  821. }
  822. } catch(\Exception $e) {
  823. throw $e;
  824. } finally {
  825. $this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
  826. $this->changeLock($path2, ILockingProvider::LOCK_SHARED, true);
  827. }
  828.  
  829. if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
  830. if ($this->shouldEmitHooks()) {
  831. $this->emit_file_hooks_post($exists, $path2);
  832. }
  833. } elseif ($result) {
  834. if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) {
  835. \OC_Hook::emit(
  836. Filesystem::CLASSNAME,
  837. Filesystem::signal_post_rename,
  838. array(
  839. Filesystem::signal_param_oldpath => $this->getHookPath($path1),
  840. Filesystem::signal_param_newpath => $this->getHookPath($path2)
  841. )
  842. );
  843. }
  844. }
  845. }
  846. } catch(\Exception $e) {
  847. throw $e;
  848. } finally {
  849. $this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
  850. $this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true);
  851. }
  852. }
  853. return $result;
  854. }
  855.  
  856. /**
  857. * Copy a file/folder from the source path to target path
  858. *
  859. * @param string $path1 source path
  860. * @param string $path2 target path
  861. * @param bool $preserveMtime whether to preserve mtime on the copy
  862. *
  863. * @return bool|mixed
  864. */
  865. public function copy($path1, $path2, $preserveMtime = false) {
  866. $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
  867. $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
  868. $result = false;
  869. if (
  870. Filesystem::isValidPath($path2)
  871. and Filesystem::isValidPath($path1)
  872. and !Filesystem::isFileBlacklisted($path2)
  873. ) {
  874. $path1 = $this->getRelativePath($absolutePath1);
  875. $path2 = $this->getRelativePath($absolutePath2);
  876.  
  877. if ($path1 == null or $path2 == null) {
  878. return false;
  879. }
  880. $run = true;
  881.  
  882. $this->lockFile($path2, ILockingProvider::LOCK_SHARED);
  883. $this->lockFile($path1, ILockingProvider::LOCK_SHARED);
  884. $lockTypePath1 = ILockingProvider::LOCK_SHARED;
  885. $lockTypePath2 = ILockingProvider::LOCK_SHARED;
  886.  
  887. try {
  888.  
  889. $exists = $this->file_exists($path2);
  890. if ($this->shouldEmitHooks()) {
  891. \OC_Hook::emit(
  892. Filesystem::CLASSNAME,
  893. Filesystem::signal_copy,
  894. array(
  895. Filesystem::signal_param_oldpath => $this->getHookPath($path1),
  896. Filesystem::signal_param_newpath => $this->getHookPath($path2),
  897. Filesystem::signal_param_run => &$run
  898. )
  899. );
  900. $this->emit_file_hooks_pre($exists, $path2, $run);
  901. }
  902. if ($run) {
  903. $mount1 = $this->getMount($path1);
  904. $mount2 = $this->getMount($path2);
  905. $storage1 = $mount1->getStorage();
  906. $internalPath1 = $mount1->getInternalPath($absolutePath1);
  907. $storage2 = $mount2->getStorage();
  908. $internalPath2 = $mount2->getInternalPath($absolutePath2);
  909.  
  910. $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE);
  911. $lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE;
  912.  
  913. if ($mount1->getMountPoint() == $mount2->getMountPoint()) {
  914. if ($storage1) {
  915. $result = $storage1->copy($internalPath1, $internalPath2);
  916. } else {
  917. $result = false;
  918. }
  919. } else {
  920. $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2);
  921. }
  922.  
  923. $this->writeUpdate($storage2, $internalPath2);
  924.  
  925. $this->changeLock($path2, ILockingProvider::LOCK_SHARED);
  926. $lockTypePath2 = ILockingProvider::LOCK_SHARED;
  927.  
  928. if ($this->shouldEmitHooks() && $result !== false) {
  929. \OC_Hook::emit(
  930. Filesystem::CLASSNAME,
  931. Filesystem::signal_post_copy,
  932. array(
  933. Filesystem::signal_param_oldpath => $this->getHookPath($path1),
  934. Filesystem::signal_param_newpath => $this->getHookPath($path2)
  935. )
  936. );
  937. $this->emit_file_hooks_post($exists, $path2);
  938. }
  939.  
  940. }
  941. } catch (\Exception $e) {
  942. $this->unlockFile($path2, $lockTypePath2);
  943. $this->unlockFile($path1, $lockTypePath1);
  944. throw $e;
  945. }
  946.  
  947. $this->unlockFile($path2, $lockTypePath2);
  948. $this->unlockFile($path1, $lockTypePath1);
  949.  
  950. }
  951. return $result;
  952. }
  953.  
  954. /**
  955. * @param string $path
  956. * @param string $mode 'r' or 'w'
  957. * @return resource
  958. */
  959. public function fopen($path, $mode) {
  960. $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support
  961. $hooks = array();
  962. switch ($mode) {
  963. case 'r':
  964. $hooks[] = 'read';
  965. break;
  966. case 'r+':
  967. case 'w+':
  968. case 'x+':
  969. case 'a+':
  970. $hooks[] = 'read';
  971. $hooks[] = 'write';
  972. break;
  973. case 'w':
  974. case 'x':
  975. case 'a':
  976. $hooks[] = 'write';
  977. break;
  978. default:
  979. \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, ILogger::ERROR);
  980. }
  981.  
  982. if ($mode !== 'r' && $mode !== 'w') {
  983. \OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends');
  984. }
  985.  
  986. return $this->basicOperation('fopen', $path, $hooks, $mode);
  987. }
  988.  
  989. /**
  990. * @param string $path
  991. * @return bool|string
  992. * @throws \OCP\Files\InvalidPathException
  993. */
  994. public function toTmpFile($path) {
  995. $this->assertPathLength($path);
  996. if (Filesystem::isValidPath($path)) {
  997. $source = $this->fopen($path, 'r');
  998. if ($source) {
  999. $extension = pathinfo($path, PATHINFO_EXTENSION);
  1000. $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
  1001. file_put_contents($tmpFile, $source);
  1002. return $tmpFile;
  1003. } else {
  1004. return false;
  1005. }
  1006. } else {
  1007. return false;
  1008. }
  1009. }
  1010.  
  1011. /**
  1012. * @param string $tmpFile
  1013. * @param string $path
  1014. * @return bool|mixed
  1015. * @throws \OCP\Files\InvalidPathException
  1016. */
  1017. public function fromTmpFile($tmpFile, $path) {
  1018. $this->assertPathLength($path);
  1019. if (Filesystem::isValidPath($path)) {
  1020.  
  1021. // Get directory that the file is going into
  1022. $filePath = dirname($path);
  1023.  
  1024. // Create the directories if any
  1025. if (!$this->file_exists($filePath)) {
  1026. $result = $this->createParentDirectories($filePath);
  1027. if ($result === false) {
  1028. return false;
  1029. }
  1030. }
  1031.  
  1032. $source = fopen($tmpFile, 'r');
  1033. if ($source) {
  1034. $result = $this->file_put_contents($path, $source);
  1035. // $this->file_put_contents() might have already closed
  1036. // the resource, so we check it, before trying to close it
  1037. // to avoid messages in the error log.
  1038. if (is_resource($source)) {
  1039. fclose($source);
  1040. }
  1041. unlink($tmpFile);
  1042. return $result;
  1043. } else {
  1044. return false;
  1045. }
  1046. } else {
  1047. return false;
  1048. }
  1049. }
  1050.  
  1051.  
  1052. /**
  1053. * @param string $path
  1054. * @return mixed
  1055. * @throws \OCP\Files\InvalidPathException
  1056. */
  1057. public function getMimeType($path) {
  1058. $this->assertPathLength($path);
  1059. return $this->basicOperation('getMimeType', $path);
  1060. }
  1061.  
  1062. /**
  1063. * @param string $type
  1064. * @param string $path
  1065. * @param bool $raw
  1066. * @return bool|null|string
  1067. */
  1068. public function hash($type, $path, $raw = false) {
  1069. $postFix = (substr($path, -1) === '/') ? '/' : '';
  1070. $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
  1071. if (Filesystem::isValidPath($path)) {
  1072. $path = $this->getRelativePath($absolutePath);
  1073. if ($path == null) {
  1074. return false;
  1075. }
  1076. if ($this->shouldEmitHooks($path)) {
  1077. \OC_Hook::emit(
  1078. Filesystem::CLASSNAME,
  1079. Filesystem::signal_read,
  1080. array(Filesystem::signal_param_path => $this->getHookPath($path))
  1081. );
  1082. }
  1083. list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
  1084. if ($storage) {
  1085. return $storage->hash($type, $internalPath, $raw);
  1086. }
  1087. }
  1088. return null;
  1089. }
  1090.  
  1091. /**
  1092. * @param string $path
  1093. * @return mixed
  1094. * @throws \OCP\Files\InvalidPathException
  1095. */
  1096. public function free_space($path = '/') {
  1097. $this->assertPathLength($path);
  1098. $result = $this->basicOperation('free_space', $path);
  1099. if ($result === null) {
  1100. throw new InvalidPathException();
  1101. }
  1102. return $result;
  1103. }
  1104.  
  1105. /**
  1106. * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage
  1107. *
  1108. * @param string $operation
  1109. * @param string $path
  1110. * @param array $hooks (optional)
  1111. * @param mixed $extraParam (optional)
  1112. * @return mixed
  1113. * @throws \Exception
  1114. *
  1115. * This method takes requests for basic filesystem functions (e.g. reading & writing
  1116. * files), processes hooks and proxies, sanitises paths, and finally passes them on to
  1117. * \OC\Files\Storage\Storage for delegation to a storage backend for execution
  1118. */
  1119. private function basicOperation($operation, $path, $hooks = [], $extraParam = null) {
  1120. $postFix = (substr($path, -1) === '/') ? '/' : '';
  1121. $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
  1122. if (Filesystem::isValidPath($path)
  1123. and !Filesystem::isFileBlacklisted($path)
  1124. ) {
  1125. $path = $this->getRelativePath($absolutePath);
  1126. if ($path == null) {
  1127. return false;
  1128. }
  1129.  
  1130. if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) {
  1131. // always a shared lock during pre-hooks so the hook can read the file
  1132. $this->lockFile($path, ILockingProvider::LOCK_SHARED);
  1133. }
  1134.  
  1135. $run = $this->runHooks($hooks, $path);
  1136. /** @var \OC\Files\Storage\Storage $storage */
  1137. list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
  1138. if ($run and $storage) {
  1139. if (in_array('write', $hooks) || in_array('delete', $hooks)) {
  1140. try {
  1141. $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
  1142. } catch (LockedException $e) {
  1143. // release the shared lock we acquired before quiting
  1144. $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
  1145. throw $e;
  1146. }
  1147. }
  1148. try {
  1149. if (!is_null($extraParam)) {
  1150. $result = $storage->$operation($internalPath, $extraParam);
  1151. } else {
  1152. $result = $storage->$operation($internalPath);
  1153. }
  1154. } catch (\Exception $e) {
  1155. if (in_array('write', $hooks) || in_array('delete', $hooks)) {
  1156. $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
  1157. } else if (in_array('read', $hooks)) {
  1158. $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
  1159. }
  1160. throw $e;
  1161. }
  1162.  
  1163. if ($result && in_array('delete', $hooks) and $result) {
  1164. $this->removeUpdate($storage, $internalPath);
  1165. }
  1166. if ($result && in_array('write', $hooks, true) && $operation !== 'fopen' && $operation !== 'touch') {
  1167. $this->writeUpdate($storage, $internalPath);
  1168. }
  1169. if ($result && in_array('touch', $hooks)) {
  1170. $this->writeUpdate($storage, $internalPath, $extraParam);
  1171. }
  1172.  
  1173. if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) {
  1174. $this->changeLock($path, ILockingProvider::LOCK_SHARED);
  1175. }
  1176.  
  1177. $unlockLater = false;
  1178. if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) {
  1179. $unlockLater = true;
  1180. // make sure our unlocking callback will still be called if connection is aborted
  1181. ignore_user_abort(true);
  1182. $result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
  1183. if (in_array('write', $hooks)) {
  1184. $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
  1185. } else if (in_array('read', $hooks)) {
  1186. $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
  1187. }
  1188. });
  1189. }
  1190.  
  1191. if ($this->shouldEmitHooks($path) && $result !== false) {
  1192. if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open
  1193. $this->runHooks($hooks, $path, true);
  1194. }
  1195. }
  1196.  
  1197. if (!$unlockLater
  1198. && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks))
  1199. ) {
  1200. $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
  1201. }
  1202. return $result;
  1203. } else {
  1204. $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
  1205. }
  1206. }
  1207. return null;
  1208. }
  1209.  
  1210. /**
  1211. * get the path relative to the default root for hook usage
  1212. *
  1213. * @param string $path
  1214. * @return string
  1215. */
  1216. private function getHookPath($path) {
  1217. if (!Filesystem::getView()) {
  1218. return $path;
  1219. }
  1220. return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path));
  1221. }
  1222.  
  1223. private function shouldEmitHooks($path = '') {
  1224. if ($path && Cache\Scanner::isPartialFile($path)) {
  1225. return false;
  1226. }
  1227. if (!Filesystem::$loaded) {
  1228. return false;
  1229. }
  1230. $defaultRoot = Filesystem::getRoot();
  1231. if ($defaultRoot === null) {
  1232. return false;
  1233. }
  1234. if ($this->fakeRoot === $defaultRoot) {
  1235. return true;
  1236. }
  1237. $fullPath = $this->getAbsolutePath($path);
  1238.  
  1239. if ($fullPath === $defaultRoot) {
  1240. return true;
  1241. }
  1242.  
  1243. return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
  1244. }
  1245.  
  1246. /**
  1247. * @param string[] $hooks
  1248. * @param string $path
  1249. * @param bool $post
  1250. * @return bool
  1251. */
  1252. private function runHooks($hooks, $path, $post = false) {
  1253. $relativePath = $path;
  1254. $path = $this->getHookPath($path);
  1255. $prefix = $post ? 'post_' : '';
  1256. $run = true;
  1257. if ($this->shouldEmitHooks($relativePath)) {
  1258. foreach ($hooks as $hook) {
  1259. if ($hook != 'read') {
  1260. \OC_Hook::emit(
  1261. Filesystem::CLASSNAME,
  1262. $prefix . $hook,
  1263. array(
  1264. Filesystem::signal_param_run => &$run,
  1265. Filesystem::signal_param_path => $path
  1266. )
  1267. );
  1268. } elseif (!$post) {
  1269. \OC_Hook::emit(
  1270. Filesystem::CLASSNAME,
  1271. $prefix . $hook,
  1272. array(
  1273. Filesystem::signal_param_path => $path
  1274. )
  1275. );
  1276. }
  1277. }
  1278. }
  1279. return $run;
  1280. }
  1281.  
  1282. /**
  1283. * check if a file or folder has been updated since $time
  1284. *
  1285. * @param string $path
  1286. * @param int $time
  1287. * @return bool
  1288. */
  1289. public function hasUpdated($path, $time) {
  1290. return $this->basicOperation('hasUpdated', $path, array(), $time);
  1291. }
  1292.  
  1293. /**
  1294. * @param string $ownerId
  1295. * @return \OC\User\User
  1296. */
  1297. private function getUserObjectForOwner($ownerId) {
  1298. $owner = $this->userManager->get($ownerId);
  1299. if ($owner instanceof IUser) {
  1300. return $owner;
  1301. } else {
  1302. return new User($ownerId, null);
  1303. }
  1304. }
  1305.  
  1306. /**
  1307. * Get file info from cache
  1308. *
  1309. * If the file is not in cached it will be scanned
  1310. * If the file has changed on storage the cache will be updated
  1311. *
  1312. * @param \OC\Files\Storage\Storage $storage
  1313. * @param string $internalPath
  1314. * @param string $relativePath
  1315. * @return ICacheEntry|bool
  1316. */
  1317. private function getCacheEntry($storage, $internalPath, $relativePath) {
  1318. $cache = $storage->getCache($internalPath);
  1319. $data = $cache->get($internalPath);
  1320. $watcher = $storage->getWatcher($internalPath);
  1321.  
  1322. try {
  1323. // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data
  1324. if (!$data || $data['size'] === -1) {
  1325. if (!$storage->file_exists($internalPath)) {
  1326. return false;
  1327. }
  1328. // don't need to get a lock here since the scanner does it's own locking
  1329. $scanner = $storage->getScanner($internalPath);
  1330. $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
  1331. $data = $cache->get($internalPath);
  1332. } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) {
  1333. $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
  1334. $watcher->update($internalPath, $data);
  1335. $storage->getPropagator()->propagateChange($internalPath, time());
  1336. $data = $cache->get($internalPath);
  1337. $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
  1338. }
  1339. } catch (LockedException $e) {
  1340. // if the file is locked we just use the old cache info
  1341. }
  1342.  
  1343. return $data;
  1344. }
  1345.  
  1346. /**
  1347. * get the filesystem info
  1348. *
  1349. * @param string $path
  1350. * @param boolean|string $includeMountPoints true to add mountpoint sizes,
  1351. * 'ext' to add only ext storage mount point sizes. Defaults to true.
  1352. * defaults to true
  1353. * @return \OC\Files\FileInfo|false False if file does not exist
  1354. */
  1355. public function getFileInfo($path, $includeMountPoints = true) {
  1356. $this->assertPathLength($path);
  1357. if (!Filesystem::isValidPath($path)) {
  1358. return false;
  1359. }
  1360. if (Cache\Scanner::isPartialFile($path)) {
  1361. return $this->getPartFileInfo($path);
  1362. }
  1363. $relativePath = $path;
  1364. $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
  1365.  
  1366. $mount = Filesystem::getMountManager()->find($path);
  1367. if (!$mount) {
  1368. \OC::$server->getLogger()->warning('Mountpoint not found for path: ' . $path);
  1369. return false;
  1370. }
  1371. $storage = $mount->getStorage();
  1372. $internalPath = $mount->getInternalPath($path);
  1373. if ($storage) {
  1374. $data = $this->getCacheEntry($storage, $internalPath, $relativePath);
  1375.  
  1376. if (!$data instanceof ICacheEntry) {
  1377. \OC::$server->getLogger()->debug('No cache entry found for ' . $path . ' (storage: ' . $storage->getId() . ', internalPath: ' . $internalPath . ')');
  1378. return false;
  1379. }
  1380.  
  1381. if ($mount instanceof MoveableMount && $internalPath === '') {
  1382. $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
  1383. }
  1384.  
  1385. $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath));
  1386. $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner);
  1387.  
  1388. if ($data and isset($data['fileid'])) {
  1389. if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
  1390. //add the sizes of other mount points to the folder
  1391. $extOnly = ($includeMountPoints === 'ext');
  1392. $mounts = Filesystem::getMountManager()->findIn($path);
  1393. $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
  1394. $subStorage = $mount->getStorage();
  1395. return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
  1396. }));
  1397. }
  1398. }
  1399.  
  1400. return $info;
  1401. } else {
  1402. \OC::$server->getLogger()->warning('Storage not valid for mountpoint: ' . $mount->getMountPoint());
  1403. }
  1404.  
  1405. return false;
  1406. }
  1407.  
  1408. /**
  1409. * get the content of a directory
  1410. *
  1411. * @param string $directory path under datadirectory
  1412. * @param string $mimetype_filter limit returned content to this mimetype or mimepart
  1413. * @return FileInfo[]
  1414. */
  1415. public function getDirectoryContent($directory, $mimetype_filter = '') {
  1416. $this->assertPathLength($directory);
  1417. if (!Filesystem::isValidPath($directory)) {
  1418. return [];
  1419. }
  1420. $path = $this->getAbsolutePath($directory);
  1421. $path = Filesystem::normalizePath($path);
  1422. $mount = $this->getMount($directory);
  1423. if (!$mount) {
  1424. return [];
  1425. }
  1426. $storage = $mount->getStorage();
  1427. $internalPath = $mount->getInternalPath($path);
  1428. if ($storage) {
  1429. $cache = $storage->getCache($internalPath);
  1430. $user = \OC_User::getUser();
  1431.  
  1432. $data = $this->getCacheEntry($storage, $internalPath, $directory);
  1433.  
  1434. if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) {
  1435. return [];
  1436. }
  1437.  
  1438. $folderId = $data['fileid'];
  1439. $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
  1440.  
  1441. $sharingDisabled = \OCP\Util::isSharingDisabledForUser();
  1442.  
  1443. $fileNames = array_map(function(ICacheEntry $content) {
  1444. return $content->getName();
  1445. }, $contents);
  1446. /**
  1447. * @var \OC\Files\FileInfo[] $fileInfos
  1448. */
  1449. $fileInfos = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
  1450. if ($sharingDisabled) {
  1451. $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
  1452. }
  1453. $owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
  1454. return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
  1455. }, $contents);
  1456. $files = array_combine($fileNames, $fileInfos);
  1457.  
  1458. //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
  1459. $mounts = Filesystem::getMountManager()->findIn($path);
  1460. $dirLength = strlen($path);
  1461. foreach ($mounts as $mount) {
  1462. $mountPoint = $mount->getMountPoint();
  1463. $subStorage = $mount->getStorage();
  1464. if ($subStorage) {
  1465. $subCache = $subStorage->getCache('');
  1466.  
  1467. $rootEntry = $subCache->get('');
  1468. if (!$rootEntry) {
  1469. $subScanner = $subStorage->getScanner('');
  1470. try {
  1471. $subScanner->scanFile('');
  1472. } catch (\OCP\Files\StorageNotAvailableException $e) {
  1473. continue;
  1474. } catch (\OCP\Files\StorageInvalidException $e) {
  1475. continue;
  1476. } catch (\Exception $e) {
  1477. // sometimes when the storage is not available it can be any exception
  1478. \OC::$server->getLogger()->logException($e, [
  1479. 'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
  1480. 'level' => ILogger::ERROR,
  1481. 'app' => 'lib',
  1482. ]);
  1483. continue;
  1484. }
  1485. $rootEntry = $subCache->get('');
  1486. }
  1487.  
  1488. if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) {
  1489. $relativePath = trim(substr($mountPoint, $dirLength), '/');
  1490. if ($pos = strpos($relativePath, '/')) {
  1491. //mountpoint inside subfolder add size to the correct folder
  1492. $entryName = substr($relativePath, 0, $pos);
  1493. foreach ($files as &$entry) {
  1494. if ($entry->getName() === $entryName) {
  1495. $entry->addSubEntry($rootEntry, $mountPoint);
  1496. }
  1497. }
  1498. } else { //mountpoint in this folder, add an entry for it
  1499. $rootEntry['name'] = $relativePath;
  1500. $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
  1501. $permissions = $rootEntry['permissions'];
  1502. // do not allow renaming/deleting the mount point if they are not shared files/folders
  1503. // for shared files/folders we use the permissions given by the owner
  1504. if ($mount instanceof MoveableMount) {
  1505. $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
  1506. } else {
  1507. $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
  1508. }
  1509.  
  1510. $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
  1511.  
  1512. // if sharing was disabled for the user we remove the share permissions
  1513. if (\OCP\Util::isSharingDisabledForUser()) {
  1514. $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
  1515. }
  1516.  
  1517. $owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
  1518. $files[$rootEntry->getName()] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
  1519. }
  1520. }
  1521. }
  1522. }
  1523.  
  1524. if ($mimetype_filter) {
  1525. $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
  1526. if (strpos($mimetype_filter, '/')) {
  1527. return $file->getMimetype() === $mimetype_filter;
  1528. } else {
  1529. return $file->getMimePart() === $mimetype_filter;
  1530. }
  1531. });
  1532. }
  1533.  
  1534. return array_values($files);
  1535. } else {
  1536. return [];
  1537. }
  1538. }
  1539.  
  1540. /**
  1541. * change file metadata
  1542. *
  1543. * @param string $path
  1544. * @param array|\OCP\Files\FileInfo $data
  1545. * @return int
  1546. *
  1547. * returns the fileid of the updated file
  1548. */
  1549. public function putFileInfo($path, $data) {
  1550. $this->assertPathLength($path);
  1551. if ($data instanceof FileInfo) {
  1552. $data = $data->getData();
  1553. }
  1554. $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
  1555. /**
  1556. * @var \OC\Files\Storage\Storage $storage
  1557. * @var string $internalPath
  1558. */
  1559. list($storage, $internalPath) = Filesystem::resolvePath($path);
  1560. if ($storage) {
  1561. $cache = $storage->getCache($path);
  1562.  
  1563. if (!$cache->inCache($internalPath)) {
  1564. $scanner = $storage->getScanner($internalPath);
  1565. $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
  1566. }
  1567.  
  1568. return $cache->put($internalPath, $data);
  1569. } else {
  1570. return -1;
  1571. }
  1572. }
  1573.  
  1574. /**
  1575. * search for files with the name matching $query
  1576. *
  1577. * @param string $query
  1578. * @return FileInfo[]
  1579. */
  1580. public function search($query) {
  1581. return $this->searchCommon('search', array('%' . $query . '%'));
  1582. }
  1583.  
  1584. /**
  1585. * search for files with the name matching $query
  1586. *
  1587. * @param string $query
  1588. * @return FileInfo[]
  1589. */
  1590. public function searchRaw($query) {
  1591. return $this->searchCommon('search', array($query));
  1592. }
  1593.  
  1594. /**
  1595. * search for files by mimetype
  1596. *
  1597. * @param string $mimetype
  1598. * @return FileInfo[]
  1599. */
  1600. public function searchByMime($mimetype) {
  1601. return $this->searchCommon('searchByMime', array($mimetype));
  1602. }
  1603.  
  1604. /**
  1605. * search for files by tag
  1606. *
  1607. * @param string|int $tag name or tag id
  1608. * @param string $userId owner of the tags
  1609. * @return FileInfo[]
  1610. */
  1611. public function searchByTag($tag, $userId) {
  1612. return $this->searchCommon('searchByTag', array($tag, $userId));
  1613. }
  1614.  
  1615. /**
  1616. * @param string $method cache method
  1617. * @param array $args
  1618. * @return FileInfo[]
  1619. */
  1620. private function searchCommon($method, $args) {
  1621. $files = array();
  1622. $rootLength = strlen($this->fakeRoot);
  1623.  
  1624. $mount = $this->getMount('');
  1625. $mountPoint = $mount->getMountPoint();
  1626. $storage = $mount->getStorage();
  1627. if ($storage) {
  1628. $cache = $storage->getCache('');
  1629.  
  1630. $results = call_user_func_array(array($cache, $method), $args);
  1631. foreach ($results as $result) {
  1632. if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
  1633. $internalPath = $result['path'];
  1634. $path = $mountPoint . $result['path'];
  1635. $result['path'] = substr($mountPoint . $result['path'], $rootLength);
  1636. $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
  1637. $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
  1638. }
  1639. }
  1640.  
  1641. $mounts = Filesystem::getMountManager()->findIn($this->fakeRoot);
  1642. foreach ($mounts as $mount) {
  1643. $mountPoint = $mount->getMountPoint();
  1644. $storage = $mount->getStorage();
  1645. if ($storage) {
  1646. $cache = $storage->getCache('');
  1647.  
  1648. $relativeMountPoint = substr($mountPoint, $rootLength);
  1649. $results = call_user_func_array(array($cache, $method), $args);
  1650. if ($results) {
  1651. foreach ($results as $result) {
  1652. $internalPath = $result['path'];
  1653. $result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
  1654. $path = rtrim($mountPoint . $internalPath, '/');
  1655. $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
  1656. $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
  1657. }
  1658. }
  1659. }
  1660. }
  1661. }
  1662. return $files;
  1663. }
  1664.  
  1665. /**
  1666. * Get the owner for a file or folder
  1667. *
  1668. * @param string $path
  1669. * @return string the user id of the owner
  1670. * @throws NotFoundException
  1671. */
  1672. public function getOwner($path) {
  1673. $info = $this->getFileInfo($path);
  1674. if (!$info) {
  1675. throw new NotFoundException($path . ' not found while trying to get owner');
  1676. }
  1677. return $info->getOwner()->getUID();
  1678. }
  1679.  
  1680. /**
  1681. * get the ETag for a file or folder
  1682. *
  1683. * @param string $path
  1684. * @return string
  1685. */
  1686. public function getETag($path) {
  1687. /**
  1688. * @var Storage\Storage $storage
  1689. * @var string $internalPath
  1690. */
  1691. list($storage, $internalPath) = $this->resolvePath($path);
  1692. if ($storage) {
  1693. return $storage->getETag($internalPath);
  1694. } else {
  1695. return null;
  1696. }
  1697. }
  1698.  
  1699. /**
  1700. * Get the path of a file by id, relative to the view
  1701. *
  1702. * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
  1703. *
  1704. * @param int $id
  1705. * @throws NotFoundException
  1706. * @return string
  1707. */
  1708. public function getPath($id) {
  1709. $id = (int)$id;
  1710. $manager = Filesystem::getMountManager();
  1711. $mounts = $manager->findIn($this->fakeRoot);
  1712. $mounts[] = $manager->find($this->fakeRoot);
  1713. // reverse the array so we start with the storage this view is in
  1714. // which is the most likely to contain the file we're looking for
  1715. $mounts = array_reverse($mounts);
  1716. foreach ($mounts as $mount) {
  1717. /**
  1718. * @var \OC\Files\Mount\MountPoint $mount
  1719. */
  1720. if ($mount->getStorage()) {
  1721. $cache = $mount->getStorage()->getCache();
  1722. $internalPath = $cache->getPathById($id);
  1723. if (is_string($internalPath)) {
  1724. $fullPath = $mount->getMountPoint() . $internalPath;
  1725. if (!is_null($path = $this->getRelativePath($fullPath))) {
  1726. return $path;
  1727. }
  1728. }
  1729. }
  1730. }
  1731. throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id));
  1732. }
  1733.  
  1734. /**
  1735. * @param string $path
  1736. * @throws InvalidPathException
  1737. */
  1738. private function assertPathLength($path) {
  1739. $maxLen = min(PHP_MAXPATHLEN, 4000);
  1740. // Check for the string length - performed using isset() instead of strlen()
  1741. // because isset() is about 5x-40x faster.
  1742. if (isset($path[$maxLen])) {
  1743. $pathLen = strlen($path);
  1744. throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path");
  1745. }
  1746. }
  1747.  
  1748. /**
  1749. * check if it is allowed to move a mount point to a given target.
  1750. * It is not allowed to move a mount point into a different mount point or
  1751. * into an already shared folder
  1752. *
  1753. * @param string $target path
  1754. * @return boolean
  1755. */
  1756. private function isTargetAllowed($target) {
  1757.  
  1758. list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target);
  1759. if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
  1760. \OCP\Util::writeLog('files',
  1761. 'It is not allowed to move one mount point into another one',
  1762. ILogger::DEBUG);
  1763. return false;
  1764. }
  1765.  
  1766. // note: cannot use the view because the target is already locked
  1767. $fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
  1768. if ($fileId === -1) {
  1769. // target might not exist, need to check parent instead
  1770. $fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
  1771. }
  1772.  
  1773. // check if any of the parents were shared by the current owner (include collections)
  1774. $shares = \OCP\Share::getItemShared(
  1775. 'folder',
  1776. $fileId,
  1777. \OCP\Share::FORMAT_NONE,
  1778. null,
  1779. true
  1780. );
  1781.  
  1782. if (count($shares) > 0) {
  1783. \OCP\Util::writeLog('files',
  1784. 'It is not allowed to move one mount point into a shared folder',
  1785. ILogger::DEBUG);
  1786. return false;
  1787. }
  1788.  
  1789. return true;
  1790. }
  1791.  
  1792. /**
  1793. * Get a fileinfo object for files that are ignored in the cache (part files)
  1794. *
  1795. * @param string $path
  1796. * @return \OCP\Files\FileInfo
  1797. */
  1798. private function getPartFileInfo($path) {
  1799. $mount = $this->getMount($path);
  1800. $storage = $mount->getStorage();
  1801. $internalPath = $mount->getInternalPath($this->getAbsolutePath($path));
  1802. $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
  1803. return new FileInfo(
  1804. $this->getAbsolutePath($path),
  1805. $storage,
  1806. $internalPath,
  1807. [
  1808. 'fileid' => null,
  1809. 'mimetype' => $storage->getMimeType($internalPath),
  1810. 'name' => basename($path),
  1811. 'etag' => null,
  1812. 'size' => $storage->filesize($internalPath),
  1813. 'mtime' => $storage->filemtime($internalPath),
  1814. 'encrypted' => false,
  1815. 'permissions' => \OCP\Constants::PERMISSION_ALL
  1816. ],
  1817. $mount,
  1818. $owner
  1819. );
  1820. }
  1821.  
  1822. /**
  1823. * @param string $path
  1824. * @param string $fileName
  1825. * @throws InvalidPathException
  1826. */
  1827. public function verifyPath($path, $fileName) {
  1828. try {
  1829. /** @type \OCP\Files\Storage $storage */
  1830. list($storage, $internalPath) = $this->resolvePath($path);
  1831. $storage->verifyPath($internalPath, $fileName);
  1832. } catch (ReservedWordException $ex) {
  1833. $l = \OC::$server->getL10N('lib');
  1834. throw new InvalidPathException($l->t('File name is a reserved word'));
  1835. } catch (InvalidCharacterInPathException $ex) {
  1836. $l = \OC::$server->getL10N('lib');
  1837. throw new InvalidPathException($l->t('File name contains at least one invalid character'));
  1838. } catch (FileNameTooLongException $ex) {
  1839. $l = \OC::$server->getL10N('lib');
  1840. throw new InvalidPathException($l->t('File name is too long'));
  1841. } catch (InvalidDirectoryException $ex) {
  1842. $l = \OC::$server->getL10N('lib');
  1843. throw new InvalidPathException($l->t('Dot files are not allowed'));
  1844. } catch (EmptyFileNameException $ex) {
  1845. $l = \OC::$server->getL10N('lib');
  1846. throw new InvalidPathException($l->t('Empty filename is not allowed'));
  1847. }
  1848. }
  1849.  
  1850. /**
  1851. * get all parent folders of $path
  1852. *
  1853. * @param string $path
  1854. * @return string[]
  1855. */
  1856. private function getParents($path) {
  1857. $path = trim($path, '/');
  1858. if (!$path) {
  1859. return [];
  1860. }
  1861.  
  1862. $parts = explode('/', $path);
  1863.  
  1864. // remove the single file
  1865. array_pop($parts);
  1866. $result = array('/');
  1867. $resultPath = '';
  1868. foreach ($parts as $part) {
  1869. if ($part) {
  1870. $resultPath .= '/' . $part;
  1871. $result[] = $resultPath;
  1872. }
  1873. }
  1874. return $result;
  1875. }
  1876.  
  1877. /**
  1878. * Returns the mount point for which to lock
  1879. *
  1880. * @param string $absolutePath absolute path
  1881. * @param bool $useParentMount true to return parent mount instead of whatever
  1882. * is mounted directly on the given path, false otherwise
  1883. * @return \OC\Files\Mount\MountPoint mount point for which to apply locks
  1884. */
  1885. private function getMountForLock($absolutePath, $useParentMount = false) {
  1886. $results = [];
  1887. $mount = Filesystem::getMountManager()->find($absolutePath);
  1888. if (!$mount) {
  1889. return $results;
  1890. }
  1891.  
  1892. if ($useParentMount) {
  1893. // find out if something is mounted directly on the path
  1894. $internalPath = $mount->getInternalPath($absolutePath);
  1895. if ($internalPath === '') {
  1896. // resolve the parent mount instead
  1897. $mount = Filesystem::getMountManager()->find(dirname($absolutePath));
  1898. }
  1899. }
  1900.  
  1901. return $mount;
  1902. }
  1903.  
  1904. /**
  1905. * Lock the given path
  1906. *
  1907. * @param string $path the path of the file to lock, relative to the view
  1908. * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
  1909. * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
  1910. *
  1911. * @return bool False if the path is excluded from locking, true otherwise
  1912. * @throws \OCP\Lock\LockedException if the path is already locked
  1913. */
  1914. private function lockPath($path, $type, $lockMountPoint = false) {
  1915. $absolutePath = $this->getAbsolutePath($path);
  1916. $absolutePath = Filesystem::normalizePath($absolutePath);
  1917. if (!$this->shouldLockFile($absolutePath)) {
  1918. return false;
  1919. }
  1920.  
  1921. $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
  1922. if ($mount) {
  1923. try {
  1924. $storage = $mount->getStorage();
  1925. if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
  1926. $storage->acquireLock(
  1927. $mount->getInternalPath($absolutePath),
  1928. $type,
  1929. $this->lockingProvider
  1930. );
  1931. }
  1932. } catch (\OCP\Lock\LockedException $e) {
  1933. // rethrow with the a human-readable path
  1934. throw new \OCP\Lock\LockedException(
  1935. $this->getPathRelativeToFiles($absolutePath),
  1936. $e
  1937. );
  1938. }
  1939. }
  1940.  
  1941. return true;
  1942. }
  1943.  
  1944. /**
  1945. * Change the lock type
  1946. *
  1947. * @param string $path the path of the file to lock, relative to the view
  1948. * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
  1949. * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
  1950. *
  1951. * @return bool False if the path is excluded from locking, true otherwise
  1952. * @throws \OCP\Lock\LockedException if the path is already locked
  1953. */
  1954. public function changeLock($path, $type, $lockMountPoint = false) {
  1955. $path = Filesystem::normalizePath($path);
  1956. $absolutePath = $this->getAbsolutePath($path);
  1957. $absolutePath = Filesystem::normalizePath($absolutePath);
  1958. if (!$this->shouldLockFile($absolutePath)) {
  1959. return false;
  1960. }
  1961.  
  1962. $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
  1963. if ($mount) {
  1964. try {
  1965. $storage = $mount->getStorage();
  1966. if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
  1967. $storage->changeLock(
  1968. $mount->getInternalPath($absolutePath),
  1969. $type,
  1970. $this->lockingProvider
  1971. );
  1972. }
  1973. } catch (\OCP\Lock\LockedException $e) {
  1974. try {
  1975. // rethrow with the a human-readable path
  1976. throw new \OCP\Lock\LockedException(
  1977. $this->getPathRelativeToFiles($absolutePath),
  1978. $e
  1979. );
  1980. } catch (\InvalidArgumentException $e) {
  1981. throw new \OCP\Lock\LockedException(
  1982. $absolutePath,
  1983. $e
  1984. );
  1985. }
  1986. }
  1987. }
  1988.  
  1989. return true;
  1990. }
  1991.  
  1992. /**
  1993. * Unlock the given path
  1994. *
  1995. * @param string $path the path of the file to unlock, relative to the view
  1996. * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
  1997. * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
  1998. *
  1999. * @return bool False if the path is excluded from locking, true otherwise
  2000. */
  2001. private function unlockPath($path, $type, $lockMountPoint = false) {
  2002. $absolutePath = $this->getAbsolutePath($path);
  2003. $absolutePath = Filesystem::normalizePath($absolutePath);
  2004. if (!$this->shouldLockFile($absolutePath)) {
  2005. return false;
  2006. }
  2007.  
  2008. $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
  2009. if ($mount) {
  2010. $storage = $mount->getStorage();
  2011. if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
  2012. $storage->releaseLock(
  2013. $mount->getInternalPath($absolutePath),
  2014. $type,
  2015. $this->lockingProvider
  2016. );
  2017. }
  2018. }
  2019.  
  2020. return true;
  2021. }
  2022.  
  2023. /**
  2024. * Lock a path and all its parents up to the root of the view
  2025. *
  2026. * @param string $path the path of the file to lock relative to the view
  2027. * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
  2028. * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
  2029. *
  2030. * @return bool False if the path is excluded from locking, true otherwise
  2031. */
  2032. public function lockFile($path, $type, $lockMountPoint = false) {
  2033. $absolutePath = $this->getAbsolutePath($path);
  2034. $absolutePath = Filesystem::normalizePath($absolutePath);
  2035. if (!$this->shouldLockFile($absolutePath)) {
  2036. return false;
  2037. }
  2038.  
  2039. $this->lockPath($path, $type, $lockMountPoint);
  2040.  
  2041. $parents = $this->getParents($path);
  2042. foreach ($parents as $parent) {
  2043. $this->lockPath($parent, ILockingProvider::LOCK_SHARED);
  2044. }
  2045.  
  2046. return true;
  2047. }
  2048.  
  2049. /**
  2050. * Unlock a path and all its parents up to the root of the view
  2051. *
  2052. * @param string $path the path of the file to lock relative to the view
  2053. * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
  2054. * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
  2055. *
  2056. * @return bool False if the path is excluded from locking, true otherwise
  2057. */
  2058. public function unlockFile($path, $type, $lockMountPoint = false) {
  2059. $absolutePath = $this->getAbsolutePath($path);
  2060. $absolutePath = Filesystem::normalizePath($absolutePath);
  2061. if (!$this->shouldLockFile($absolutePath)) {
  2062. return false;
  2063. }
  2064.  
  2065. $this->unlockPath($path, $type, $lockMountPoint);
  2066.  
  2067. $parents = $this->getParents($path);
  2068. foreach ($parents as $parent) {
  2069. $this->unlockPath($parent, ILockingProvider::LOCK_SHARED);
  2070. }
  2071.  
  2072. return true;
  2073. }
  2074.  
  2075. /**
  2076. * Only lock files in data/user/files/
  2077. *
  2078. * @param string $path Absolute path to the file/folder we try to (un)lock
  2079. * @return bool
  2080. */
  2081. protected function shouldLockFile($path) {
  2082. $path = Filesystem::normalizePath($path);
  2083.  
  2084. $pathSegments = explode('/', $path);
  2085. if (isset($pathSegments[2])) {
  2086. // E.g.: /username/files/path-to-file
  2087. return ($pathSegments[2] === 'files') && (count($pathSegments) > 3);
  2088. }
  2089.  
  2090. return strpos($path, '/appdata_') !== 0;
  2091. }
  2092.  
  2093. /**
  2094. * Shortens the given absolute path to be relative to
  2095. * "$user/files".
  2096. *
  2097. * @param string $absolutePath absolute path which is under "files"
  2098. *
  2099. * @return string path relative to "files" with trimmed slashes or null
  2100. * if the path was NOT relative to files
  2101. *
  2102. * @throws \InvalidArgumentException if the given path was not under "files"
  2103. * @since 8.1.0
  2104. */
  2105. public function getPathRelativeToFiles($absolutePath) {
  2106. $path = Filesystem::normalizePath($absolutePath);
  2107. $parts = explode('/', trim($path, '/'), 3);
  2108. // "$user", "files", "path/to/dir"
  2109. if (!isset($parts[1]) || $parts[1] !== 'files') {
  2110. $this->logger->error(
  2111. '$absolutePath must be relative to "files", value is "%s"',
  2112. [
  2113. $absolutePath
  2114. ]
  2115. );
  2116. throw new \InvalidArgumentException('$absolutePath must be relative to "files"');
  2117. }
  2118. if (isset($parts[2])) {
  2119. return $parts[2];
  2120. }
  2121. return '';
  2122. }
  2123.  
  2124. /**
  2125. * @param string $filename
  2126. * @return array
  2127. * @throws \OC\User\NoUserException
  2128. * @throws NotFoundException
  2129. */
  2130. public function getUidAndFilename($filename) {
  2131. $info = $this->getFileInfo($filename);
  2132. if (!$info instanceof \OCP\Files\FileInfo) {
  2133. throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
  2134. }
  2135. $uid = $info->getOwner()->getUID();
  2136. if ($uid != \OCP\User::getUser()) {
  2137. Filesystem::initMountPoints($uid);
  2138. $ownerView = new View('/' . $uid . '/files');
  2139. try {
  2140. $filename = $ownerView->getPath($info['fileid']);
  2141. } catch (NotFoundException $e) {
  2142. throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
  2143. }
  2144. }
  2145. return [$uid, $filename];
  2146. }
  2147.  
  2148. /**
  2149. * Creates parent non-existing folders
  2150. *
  2151. * @param string $filePath
  2152. * @return bool
  2153. */
  2154. private function createParentDirectories($filePath) {
  2155. $directoryParts = explode('/', $filePath);
  2156. $directoryParts = array_filter($directoryParts);
  2157. foreach ($directoryParts as $key => $part) {
  2158. $currentPathElements = array_slice($directoryParts, 0, $key);
  2159. $currentPath = '/' . implode('/', $currentPathElements);
  2160. if ($this->is_file($currentPath)) {
  2161. return false;
  2162. }
  2163. if (!$this->file_exists($currentPath)) {
  2164. $this->mkdir($currentPath);
  2165. }
  2166. }
  2167.  
  2168. return true;
  2169. }
  2170. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement