FilesystemTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Robin Appelman
  6. * @copyright 2012 Robin Appelman icewind@owncloud.com
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10. * License as published by the Free Software Foundation; either
  11. * version 3 of the License, or any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public
  19. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. namespace Test\Files;
  23. use OC\Files\Mount\MountPoint;
  24. use OC\Files\Storage\Temporary;
  25. use OC\User\NoUserException;
  26. use OCP\Files\Config\IMountProvider;
  27. use OCP\Files\Storage\IStorageFactory;
  28. use OCP\IUser;
  29. class DummyMountProvider implements IMountProvider {
  30. private $mounts = [];
  31. /**
  32. * @param array $mounts
  33. */
  34. public function __construct(array $mounts) {
  35. $this->mounts = $mounts;
  36. }
  37. /**
  38. * Get the pre-registered mount points
  39. *
  40. * @param IUser $user
  41. * @param IStorageFactory $loader
  42. * @return \OCP\Files\Mount\IMountPoint[]
  43. */
  44. public function getMountsForUser(IUser $user, IStorageFactory $loader) {
  45. return isset($this->mounts[$user->getUID()]) ? $this->mounts[$user->getUID()] : [];
  46. }
  47. }
  48. /**
  49. * Class FilesystemTest
  50. *
  51. * @group DB
  52. *
  53. * @package Test\Files
  54. */
  55. class FilesystemTest extends \Test\TestCase {
  56. const TEST_FILESYSTEM_USER1 = "test-filesystem-user1";
  57. const TEST_FILESYSTEM_USER2 = "test-filesystem-user1";
  58. /**
  59. * @var array tmpDirs
  60. */
  61. private $tmpDirs = array();
  62. /**
  63. * @return array
  64. */
  65. private function getStorageData() {
  66. $dir = \OC::$server->getTempManager()->getTemporaryFolder();
  67. $this->tmpDirs[] = $dir;
  68. return array('datadir' => $dir);
  69. }
  70. protected function setUp() {
  71. parent::setUp();
  72. $userBackend = new \Test\Util\User\Dummy();
  73. $userBackend->createUser(self::TEST_FILESYSTEM_USER1, self::TEST_FILESYSTEM_USER1);
  74. $userBackend->createUser(self::TEST_FILESYSTEM_USER2, self::TEST_FILESYSTEM_USER2);
  75. \OC::$server->getUserManager()->registerBackend($userBackend);
  76. $this->loginAsUser();
  77. }
  78. protected function tearDown() {
  79. foreach ($this->tmpDirs as $dir) {
  80. \OC_Helper::rmdirr($dir);
  81. }
  82. $this->logout();
  83. $this->invokePrivate('\OC\Files\Filesystem', 'normalizedPathCache', [null]);
  84. parent::tearDown();
  85. }
  86. public function testMount() {
  87. \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', self::getStorageData(), '/');
  88. $this->assertEquals('/', \OC\Files\Filesystem::getMountPoint('/'));
  89. $this->assertEquals('/', \OC\Files\Filesystem::getMountPoint('/some/folder'));
  90. list(, $internalPath) = \OC\Files\Filesystem::resolvePath('/');
  91. $this->assertEquals('', $internalPath);
  92. list(, $internalPath) = \OC\Files\Filesystem::resolvePath('/some/folder');
  93. $this->assertEquals('some/folder', $internalPath);
  94. \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', self::getStorageData(), '/some');
  95. $this->assertEquals('/', \OC\Files\Filesystem::getMountPoint('/'));
  96. $this->assertEquals('/some/', \OC\Files\Filesystem::getMountPoint('/some/folder'));
  97. $this->assertEquals('/some/', \OC\Files\Filesystem::getMountPoint('/some/'));
  98. $this->assertEquals('/some/', \OC\Files\Filesystem::getMountPoint('/some'));
  99. list(, $internalPath) = \OC\Files\Filesystem::resolvePath('/some/folder');
  100. $this->assertEquals('folder', $internalPath);
  101. }
  102. public function normalizePathData() {
  103. return array(
  104. array('/', ''),
  105. array('/', '/'),
  106. array('/', '//'),
  107. array('/', '/', false),
  108. array('/', '//', false),
  109. array('/path', '/path/'),
  110. array('/path/', '/path/', false),
  111. array('/path', 'path'),
  112. array('/foo/bar', '/foo//bar/'),
  113. array('/foo/bar/', '/foo//bar/', false),
  114. array('/foo/bar', '/foo////bar'),
  115. array('/foo/bar', '/foo/////bar'),
  116. array('/foo/bar', '/foo/bar/.'),
  117. array('/foo/bar', '/foo/bar/./'),
  118. array('/foo/bar/', '/foo/bar/./', false),
  119. array('/foo/bar', '/foo/bar/./.'),
  120. array('/foo/bar', '/foo/bar/././'),
  121. array('/foo/bar/', '/foo/bar/././', false),
  122. array('/foo/bar', '/foo/./bar/'),
  123. array('/foo/bar/', '/foo/./bar/', false),
  124. array('/foo/.bar', '/foo/.bar/'),
  125. array('/foo/.bar/', '/foo/.bar/', false),
  126. array('/foo/.bar/tee', '/foo/.bar/tee'),
  127. // Windows paths
  128. array('/', ''),
  129. array('/', '\\'),
  130. array('/', '\\', false),
  131. array('/', '\\\\'),
  132. array('/', '\\\\', false),
  133. array('/path', '\\path'),
  134. array('/path', '\\path', false),
  135. array('/path', '\\path\\'),
  136. array('/path/', '\\path\\', false),
  137. array('/foo/bar', '\\foo\\\\bar\\'),
  138. array('/foo/bar/', '\\foo\\\\bar\\', false),
  139. array('/foo/bar', '\\foo\\\\\\\\bar'),
  140. array('/foo/bar', '\\foo\\\\\\\\\\bar'),
  141. array('/foo/bar', '\\foo\\bar\\.'),
  142. array('/foo/bar', '\\foo\\bar\\.\\'),
  143. array('/foo/bar/', '\\foo\\bar\\.\\', false),
  144. array('/foo/bar', '\\foo\\bar\\.\\.'),
  145. array('/foo/bar', '\\foo\\bar\\.\\.\\'),
  146. array('/foo/bar/', '\\foo\\bar\\.\\.\\', false),
  147. array('/foo/bar', '\\foo\\.\\bar\\'),
  148. array('/foo/bar/', '\\foo\\.\\bar\\', false),
  149. array('/foo/.bar', '\\foo\\.bar\\'),
  150. array('/foo/.bar/', '\\foo\\.bar\\', false),
  151. array('/foo/.bar/tee', '\\foo\\.bar\\tee'),
  152. // Absolute windows paths NOT marked as absolute
  153. array('/C:', 'C:\\'),
  154. array('/C:/', 'C:\\', false),
  155. array('/C:/tests', 'C:\\tests'),
  156. array('/C:/tests', 'C:\\tests', false),
  157. array('/C:/tests', 'C:\\tests\\'),
  158. array('/C:/tests/', 'C:\\tests\\', false),
  159. // normalize does not resolve '..' (by design)
  160. array('/foo/..', '/foo/../'),
  161. array('/foo/..', '\\foo\\..\\'),
  162. );
  163. }
  164. /**
  165. * @dataProvider normalizePathData
  166. */
  167. public function testNormalizePath($expected, $path, $stripTrailingSlash = true) {
  168. $this->assertEquals($expected, \OC\Files\Filesystem::normalizePath($path, $stripTrailingSlash));
  169. }
  170. public function normalizePathKeepUnicodeData() {
  171. $nfdName = 'ümlaut';
  172. $nfcName = 'ümlaut';
  173. return [
  174. ['/' . $nfcName, $nfcName, true],
  175. ['/' . $nfcName, $nfcName, false],
  176. ['/' . $nfdName, $nfdName, true],
  177. ['/' . $nfcName, $nfdName, false],
  178. ];
  179. }
  180. /**
  181. * @dataProvider normalizePathKeepUnicodeData
  182. */
  183. public function testNormalizePathKeepUnicode($expected, $path, $keepUnicode = false) {
  184. $this->assertEquals($expected, \OC\Files\Filesystem::normalizePath($path, true, false, $keepUnicode));
  185. }
  186. public function testNormalizePathKeepUnicodeCache() {
  187. $nfdName = 'ümlaut';
  188. $nfcName = 'ümlaut';
  189. // call in succession due to cache
  190. $this->assertEquals('/' . $nfcName, \OC\Files\Filesystem::normalizePath($nfdName, true, false, false));
  191. $this->assertEquals('/' . $nfdName, \OC\Files\Filesystem::normalizePath($nfdName, true, false, true));
  192. }
  193. public function isValidPathData() {
  194. return array(
  195. array('/', true),
  196. array('/path', true),
  197. array('/foo/bar', true),
  198. array('/foo//bar/', true),
  199. array('/foo////bar', true),
  200. array('/foo//\///bar', true),
  201. array('/foo/bar/.', true),
  202. array('/foo/bar/./', true),
  203. array('/foo/bar/./.', true),
  204. array('/foo/bar/././', true),
  205. array('/foo/bar/././..bar', true),
  206. array('/foo/bar/././..bar/a', true),
  207. array('/foo/bar/././..', false),
  208. array('/foo/bar/././../', false),
  209. array('/foo/bar/.././', false),
  210. array('/foo/bar/../../', false),
  211. array('/foo/bar/../..\\', false),
  212. array('..', false),
  213. array('../', false),
  214. array('../foo/bar', false),
  215. array('..\foo/bar', false),
  216. );
  217. }
  218. /**
  219. * @dataProvider isValidPathData
  220. */
  221. public function testIsValidPath($path, $expected) {
  222. $this->assertSame($expected, \OC\Files\Filesystem::isValidPath($path));
  223. }
  224. public function isFileBlacklistedData() {
  225. return array(
  226. array('/etc/foo/bar/foo.txt', false),
  227. array('\etc\foo/bar\foo.txt', false),
  228. array('.htaccess', true),
  229. array('.htaccess/', true),
  230. array('.htaccess\\', true),
  231. array('/etc/foo\bar/.htaccess\\', true),
  232. array('/etc/foo\bar/.htaccess/', true),
  233. array('/etc/foo\bar/.htaccess/foo', false),
  234. array('//foo//bar/\.htaccess/', true),
  235. array('\foo\bar\.HTAccess', true),
  236. );
  237. }
  238. /**
  239. * @dataProvider isFileBlacklistedData
  240. */
  241. public function testIsFileBlacklisted($path, $expected) {
  242. $this->assertSame($expected, \OC\Files\Filesystem::isFileBlacklisted($path));
  243. }
  244. public function testNormalizePathUTF8() {
  245. if (!class_exists('Patchwork\PHP\Shim\Normalizer')) {
  246. $this->markTestSkipped('UTF8 normalizer Patchwork was not found');
  247. }
  248. $this->assertEquals("/foo/bar\xC3\xBC", \OC\Files\Filesystem::normalizePath("/foo/baru\xCC\x88"));
  249. $this->assertEquals("/foo/bar\xC3\xBC", \OC\Files\Filesystem::normalizePath("\\foo\\baru\xCC\x88"));
  250. }
  251. public function testHooks() {
  252. if (\OC\Files\Filesystem::getView()) {
  253. $user = \OC_User::getUser();
  254. } else {
  255. $user = self::TEST_FILESYSTEM_USER1;
  256. $backend = new \Test\Util\User\Dummy();
  257. \OC_User::useBackend($backend);
  258. $backend->createUser($user, $user);
  259. $userObj = \OC::$server->getUserManager()->get($user);
  260. \OC::$server->getUserSession()->setUser($userObj);
  261. \OC\Files\Filesystem::init($user, '/' . $user . '/files');
  262. }
  263. \OC_Hook::clear('OC_Filesystem');
  264. \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
  265. \OC\Files\Filesystem::mount('OC\Files\Storage\Temporary', array(), '/');
  266. $rootView = new \OC\Files\View('');
  267. $rootView->mkdir('/' . $user);
  268. $rootView->mkdir('/' . $user . '/files');
  269. // \OC\Files\Filesystem::file_put_contents('/foo', 'foo');
  270. \OC\Files\Filesystem::mkdir('/bar');
  271. // \OC\Files\Filesystem::file_put_contents('/bar//foo', 'foo');
  272. $tmpFile = \OC::$server->getTempManager()->getTemporaryFile();
  273. file_put_contents($tmpFile, 'foo');
  274. $fh = fopen($tmpFile, 'r');
  275. // \OC\Files\Filesystem::file_put_contents('/bar//foo', $fh);
  276. }
  277. /**
  278. * Tests that an exception is thrown when passed user does not exist.
  279. *
  280. * @expectedException \OC\User\NoUserException
  281. */
  282. public function testLocalMountWhenUserDoesNotExist() {
  283. $userId = $this->getUniqueID('user_');
  284. \OC\Files\Filesystem::initMountPoints($userId);
  285. }
  286. /**
  287. * @expectedException \OC\User\NoUserException
  288. */
  289. public function testNullUserThrows() {
  290. \OC\Files\Filesystem::initMountPoints(null);
  291. }
  292. public function testNullUserThrowsTwice() {
  293. $thrown = 0;
  294. try {
  295. \OC\Files\Filesystem::initMountPoints(null);
  296. } catch (NoUserException $e) {
  297. $thrown++;
  298. }
  299. try {
  300. \OC\Files\Filesystem::initMountPoints(null);
  301. } catch (NoUserException $e) {
  302. $thrown++;
  303. }
  304. $this->assertEquals(2, $thrown);
  305. }
  306. /**
  307. * Tests that an exception is thrown when passed user does not exist.
  308. */
  309. public function testLocalMountWhenUserDoesNotExistTwice() {
  310. $thrown = 0;
  311. $userId = $this->getUniqueID('user_');
  312. try {
  313. \OC\Files\Filesystem::initMountPoints($userId);
  314. } catch (NoUserException $e) {
  315. $thrown++;
  316. }
  317. try {
  318. \OC\Files\Filesystem::initMountPoints($userId);
  319. } catch (NoUserException $e) {
  320. $thrown++;
  321. }
  322. $this->assertEquals(2, $thrown);
  323. }
  324. public function testUserNameCasing() {
  325. $this->logout();
  326. $userId = $this->getUniqueID('user_');
  327. \OC_User::clearBackends();
  328. // needed for loginName2UserName mapping
  329. $userBackend = $this->createMock(\OC\User\Database::class);
  330. \OC::$server->getUserManager()->registerBackend($userBackend);
  331. $userBackend->expects($this->once())
  332. ->method('userExists')
  333. ->with(strtoupper($userId))
  334. ->will($this->returnValue(true));
  335. $userBackend->expects($this->once())
  336. ->method('loginName2UserName')
  337. ->with(strtoupper($userId))
  338. ->will($this->returnValue($userId));
  339. $view = new \OC\Files\View();
  340. $this->assertFalse($view->file_exists('/' . $userId));
  341. \OC\Files\Filesystem::initMountPoints(strtoupper($userId));
  342. list($storage1, $path1) = $view->resolvePath('/' . $userId);
  343. list($storage2, $path2) = $view->resolvePath('/' . strtoupper($userId));
  344. $this->assertTrue($storage1->instanceOfStorage('\OCP\Files\IHomeStorage'));
  345. $this->assertEquals('', $path1);
  346. // not mounted, still on the local root storage
  347. $this->assertEquals(strtoupper($userId), $path2);
  348. }
  349. /**
  350. * Tests that the home storage is used for the user's mount point
  351. */
  352. public function testHomeMount() {
  353. $userId = $this->getUniqueID('user_');
  354. \OC::$server->getUserManager()->createUser($userId, $userId);
  355. \OC\Files\Filesystem::initMountPoints($userId);
  356. $homeMount = \OC\Files\Filesystem::getStorage('/' . $userId . '/');
  357. $this->assertTrue($homeMount->instanceOfStorage('\OCP\Files\IHomeStorage'));
  358. if ($homeMount->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')) {
  359. $this->assertEquals('object::user:' . $userId, $homeMount->getId());
  360. } else if ($homeMount->instanceOfStorage('\OC\Files\Storage\Home')) {
  361. $this->assertEquals('home::' . $userId, $homeMount->getId());
  362. }
  363. $user = \OC::$server->getUserManager()->get($userId);
  364. if ($user !== null) { $user->delete(); }
  365. }
  366. public function dummyHook($arguments) {
  367. $path = $arguments['path'];
  368. $this->assertEquals($path, \OC\Files\Filesystem::normalizePath($path)); //the path passed to the hook should already be normalized
  369. }
  370. /**
  371. * Test that the default cache dir is part of the user's home
  372. */
  373. public function testMountDefaultCacheDir() {
  374. $userId = $this->getUniqueID('user_');
  375. $config = \OC::$server->getConfig();
  376. $oldCachePath = $config->getSystemValue('cache_path', '');
  377. // no cache path configured
  378. $config->setSystemValue('cache_path', '');
  379. \OC::$server->getUserManager()->createUser($userId, $userId);
  380. \OC\Files\Filesystem::initMountPoints($userId);
  381. $this->assertEquals(
  382. '/' . $userId . '/',
  383. \OC\Files\Filesystem::getMountPoint('/' . $userId . '/cache')
  384. );
  385. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath('/' . $userId . '/cache');
  386. $this->assertTrue($storage->instanceOfStorage('\OCP\Files\IHomeStorage'));
  387. $this->assertEquals('cache', $internalPath);
  388. $user = \OC::$server->getUserManager()->get($userId);
  389. if ($user !== null) { $user->delete(); }
  390. $config->setSystemValue('cache_path', $oldCachePath);
  391. }
  392. /**
  393. * Test that an external cache is mounted into
  394. * the user's home
  395. */
  396. public function testMountExternalCacheDir() {
  397. $userId = $this->getUniqueID('user_');
  398. $config = \OC::$server->getConfig();
  399. $oldCachePath = $config->getSystemValue('cache_path', '');
  400. // set cache path to temp dir
  401. $cachePath = \OC::$server->getTempManager()->getTemporaryFolder() . '/extcache';
  402. $config->setSystemValue('cache_path', $cachePath);
  403. \OC::$server->getUserManager()->createUser($userId, $userId);
  404. \OC\Files\Filesystem::initMountPoints($userId);
  405. $this->assertEquals(
  406. '/' . $userId . '/cache/',
  407. \OC\Files\Filesystem::getMountPoint('/' . $userId . '/cache')
  408. );
  409. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath('/' . $userId . '/cache');
  410. $this->assertTrue($storage->instanceOfStorage('\OC\Files\Storage\Local'));
  411. $this->assertEquals('', $internalPath);
  412. $user = \OC::$server->getUserManager()->get($userId);
  413. if ($user !== null) { $user->delete(); }
  414. $config->setSystemValue('cache_path', $oldCachePath);
  415. }
  416. public function testRegisterMountProviderAfterSetup() {
  417. \OC\Files\Filesystem::initMountPoints(self::TEST_FILESYSTEM_USER2);
  418. $this->assertEquals('/', \OC\Files\Filesystem::getMountPoint('/foo/bar'));
  419. $mount = new MountPoint(new Temporary([]), '/foo/bar');
  420. $mountProvider = new DummyMountProvider([self::TEST_FILESYSTEM_USER2 => [$mount]]);
  421. \OC::$server->getMountProviderCollection()->registerProvider($mountProvider);
  422. $this->assertEquals('/foo/bar/', \OC\Files\Filesystem::getMountPoint('/foo/bar'));
  423. }
  424. }