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

2496 lines
82 KiB

12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. <?php
  2. /**
  3. * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file. */
  7. namespace Test\Files;
  8. use OC\Files\Cache\Watcher;
  9. use OC\Files\Storage\Common;
  10. use OC\Files\Mount\MountPoint;
  11. use OC\Files\Storage\Temporary;
  12. use OCP\Files\FileInfo;
  13. use OCP\Lock\ILockingProvider;
  14. class TemporaryNoTouch extends \OC\Files\Storage\Temporary {
  15. public function touch($path, $mtime = null) {
  16. return false;
  17. }
  18. }
  19. class TemporaryNoCross extends \OC\Files\Storage\Temporary {
  20. public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
  21. return Common::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
  22. }
  23. public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
  24. return Common::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
  25. }
  26. }
  27. class TemporaryNoLocal extends \OC\Files\Storage\Temporary {
  28. public function instanceOfStorage($className) {
  29. if ($className === '\OC\Files\Storage\Local') {
  30. return false;
  31. } else {
  32. return parent::instanceOfStorage($className);
  33. }
  34. }
  35. }
  36. /**
  37. * Class ViewTest
  38. *
  39. * @group DB
  40. *
  41. * @package Test\Files
  42. */
  43. class ViewTest extends \Test\TestCase {
  44. /**
  45. * @var \OC\Files\Storage\Storage[] $storages
  46. */
  47. private $storages = array();
  48. /**
  49. * @var string
  50. */
  51. private $user;
  52. /**
  53. * @var \OCP\IUser
  54. */
  55. private $userObject;
  56. /**
  57. * @var \OCP\IGroup
  58. */
  59. private $groupObject;
  60. /** @var \OC\Files\Storage\Storage */
  61. private $tempStorage;
  62. protected function setUp() {
  63. parent::setUp();
  64. \OC_Hook::clear();
  65. \OC_User::clearBackends();
  66. \OC_User::useBackend(new \Test\Util\User\Dummy());
  67. //login
  68. $userManager = \OC::$server->getUserManager();
  69. $groupManager = \OC::$server->getGroupManager();
  70. $this->user = 'test';
  71. $this->userObject = $userManager->createUser('test', 'test');
  72. $this->groupObject = $groupManager->createGroup('group1');
  73. $this->groupObject->addUser($this->userObject);
  74. $this->loginAsUser($this->user);
  75. // clear mounts but somehow keep the root storage
  76. // that was initialized above...
  77. \OC\Files\Filesystem::clearMounts();
  78. $this->tempStorage = null;
  79. }
  80. protected function tearDown() {
  81. \OC_User::setUserId($this->user);
  82. foreach ($this->storages as $storage) {
  83. $cache = $storage->getCache();
  84. $ids = $cache->getAll();
  85. $cache->clear();
  86. }
  87. if ($this->tempStorage && !\OC_Util::runningOnWindows()) {
  88. system('rm -rf ' . escapeshellarg($this->tempStorage->getDataDir()));
  89. }
  90. $this->logout();
  91. $this->userObject->delete();
  92. $this->groupObject->delete();
  93. $mountProviderCollection = \OC::$server->getMountProviderCollection();
  94. \Test\TestCase::invokePrivate($mountProviderCollection, 'providers', [[]]);
  95. parent::tearDown();
  96. }
  97. /**
  98. * @medium
  99. */
  100. public function testCacheAPI() {
  101. $storage1 = $this->getTestStorage();
  102. $storage2 = $this->getTestStorage();
  103. $storage3 = $this->getTestStorage();
  104. $root = $this->getUniqueID('/');
  105. \OC\Files\Filesystem::mount($storage1, array(), $root . '/');
  106. \OC\Files\Filesystem::mount($storage2, array(), $root . '/substorage');
  107. \OC\Files\Filesystem::mount($storage3, array(), $root . '/folder/anotherstorage');
  108. $textSize = strlen("dummy file data\n");
  109. $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png');
  110. $storageSize = $textSize * 2 + $imageSize;
  111. $storageInfo = $storage3->getCache()->get('');
  112. $this->assertEquals($storageSize, $storageInfo['size']);
  113. $rootView = new \OC\Files\View($root);
  114. $cachedData = $rootView->getFileInfo('/foo.txt');
  115. $this->assertEquals($textSize, $cachedData['size']);
  116. $this->assertEquals('text/plain', $cachedData['mimetype']);
  117. $this->assertNotEquals(-1, $cachedData['permissions']);
  118. $cachedData = $rootView->getFileInfo('/');
  119. $this->assertEquals($storageSize * 3, $cachedData['size']);
  120. $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
  121. // get cached data excluding mount points
  122. $cachedData = $rootView->getFileInfo('/', false);
  123. $this->assertEquals($storageSize, $cachedData['size']);
  124. $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
  125. $cachedData = $rootView->getFileInfo('/folder');
  126. $this->assertEquals($storageSize + $textSize, $cachedData['size']);
  127. $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
  128. $folderData = $rootView->getDirectoryContent('/');
  129. /**
  130. * expected entries:
  131. * folder
  132. * foo.png
  133. * foo.txt
  134. * substorage
  135. */
  136. $this->assertEquals(4, count($folderData));
  137. $this->assertEquals('folder', $folderData[0]['name']);
  138. $this->assertEquals('foo.png', $folderData[1]['name']);
  139. $this->assertEquals('foo.txt', $folderData[2]['name']);
  140. $this->assertEquals('substorage', $folderData[3]['name']);
  141. $this->assertEquals($storageSize + $textSize, $folderData[0]['size']);
  142. $this->assertEquals($imageSize, $folderData[1]['size']);
  143. $this->assertEquals($textSize, $folderData[2]['size']);
  144. $this->assertEquals($storageSize, $folderData[3]['size']);
  145. $folderData = $rootView->getDirectoryContent('/substorage');
  146. /**
  147. * expected entries:
  148. * folder
  149. * foo.png
  150. * foo.txt
  151. */
  152. $this->assertEquals(3, count($folderData));
  153. $this->assertEquals('folder', $folderData[0]['name']);
  154. $this->assertEquals('foo.png', $folderData[1]['name']);
  155. $this->assertEquals('foo.txt', $folderData[2]['name']);
  156. $folderView = new \OC\Files\View($root . '/folder');
  157. $this->assertEquals($rootView->getFileInfo('/folder'), $folderView->getFileInfo('/'));
  158. $cachedData = $rootView->getFileInfo('/foo.txt');
  159. $this->assertFalse($cachedData['encrypted']);
  160. $id = $rootView->putFileInfo('/foo.txt', array('encrypted' => true));
  161. $cachedData = $rootView->getFileInfo('/foo.txt');
  162. $this->assertTrue($cachedData['encrypted']);
  163. $this->assertEquals($cachedData['fileid'], $id);
  164. $this->assertFalse($rootView->getFileInfo('/non/existing'));
  165. $this->assertEquals(array(), $rootView->getDirectoryContent('/non/existing'));
  166. }
  167. /**
  168. * @medium
  169. */
  170. public function testGetPath() {
  171. $storage1 = $this->getTestStorage();
  172. $storage2 = $this->getTestStorage();
  173. $storage3 = $this->getTestStorage();
  174. \OC\Files\Filesystem::mount($storage1, array(), '/');
  175. \OC\Files\Filesystem::mount($storage2, array(), '/substorage');
  176. \OC\Files\Filesystem::mount($storage3, array(), '/folder/anotherstorage');
  177. $rootView = new \OC\Files\View('');
  178. $cachedData = $rootView->getFileInfo('/foo.txt');
  179. /** @var int $id1 */
  180. $id1 = $cachedData['fileid'];
  181. $this->assertEquals('/foo.txt', $rootView->getPath($id1));
  182. $cachedData = $rootView->getFileInfo('/substorage/foo.txt');
  183. /** @var int $id2 */
  184. $id2 = $cachedData['fileid'];
  185. $this->assertEquals('/substorage/foo.txt', $rootView->getPath($id2));
  186. $folderView = new \OC\Files\View('/substorage');
  187. $this->assertEquals('/foo.txt', $folderView->getPath($id2));
  188. }
  189. /**
  190. * @expectedException \OCP\Files\NotFoundException
  191. */
  192. function testGetPathNotExisting() {
  193. $storage1 = $this->getTestStorage();
  194. \OC\Files\Filesystem::mount($storage1, [], '/');
  195. $rootView = new \OC\Files\View('');
  196. $cachedData = $rootView->getFileInfo('/foo.txt');
  197. /** @var int $id1 */
  198. $id1 = $cachedData['fileid'];
  199. $folderView = new \OC\Files\View('/substorage');
  200. $this->assertNull($folderView->getPath($id1));
  201. }
  202. /**
  203. * @medium
  204. */
  205. public function testMountPointOverwrite() {
  206. $storage1 = $this->getTestStorage(false);
  207. $storage2 = $this->getTestStorage();
  208. $storage1->mkdir('substorage');
  209. \OC\Files\Filesystem::mount($storage1, array(), '/');
  210. \OC\Files\Filesystem::mount($storage2, array(), '/substorage');
  211. $rootView = new \OC\Files\View('');
  212. $folderContent = $rootView->getDirectoryContent('/');
  213. $this->assertEquals(4, count($folderContent));
  214. }
  215. public function sharingDisabledPermissionProvider() {
  216. return [
  217. ['no', '', true],
  218. ['yes', 'group1', false],
  219. ];
  220. }
  221. /**
  222. * @dataProvider sharingDisabledPermissionProvider
  223. */
  224. public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable) {
  225. $appConfig = \OC::$server->getAppConfig();
  226. $oldExcludeGroupsFlag = $appConfig->getValue('core', 'shareapi_exclude_groups', 'no');
  227. $oldExcludeGroupsList = $appConfig->getValue('core', 'shareapi_exclude_groups_list', '');
  228. $appConfig->setValue('core', 'shareapi_exclude_groups', $excludeGroups);
  229. $appConfig->setValue('core', 'shareapi_exclude_groups_list', $excludeGroupsList);
  230. $storage1 = $this->getTestStorage();
  231. $storage2 = $this->getTestStorage();
  232. \OC\Files\Filesystem::mount($storage1, array(), '/');
  233. \OC\Files\Filesystem::mount($storage2, array(), '/mount');
  234. $view = new \OC\Files\View('/');
  235. $folderContent = $view->getDirectoryContent('');
  236. $this->assertEquals($expectedShareable, $folderContent[0]->isShareable());
  237. $folderContent = $view->getDirectoryContent('mount');
  238. $this->assertEquals($expectedShareable, $folderContent[0]->isShareable());
  239. $appConfig->setValue('core', 'shareapi_exclude_groups', $oldExcludeGroupsFlag);
  240. $appConfig->setValue('core', 'shareapi_exclude_groups_list', $oldExcludeGroupsList);
  241. }
  242. public function testCacheIncompleteFolder() {
  243. $storage1 = $this->getTestStorage(false);
  244. \OC\Files\Filesystem::clearMounts();
  245. \OC\Files\Filesystem::mount($storage1, array(), '/incomplete');
  246. $rootView = new \OC\Files\View('/incomplete');
  247. $entries = $rootView->getDirectoryContent('/');
  248. $this->assertEquals(3, count($entries));
  249. // /folder will already be in the cache but not scanned
  250. $entries = $rootView->getDirectoryContent('/folder');
  251. $this->assertEquals(1, count($entries));
  252. }
  253. public function testAutoScan() {
  254. $storage1 = $this->getTestStorage(false);
  255. $storage2 = $this->getTestStorage(false);
  256. \OC\Files\Filesystem::mount($storage1, array(), '/');
  257. \OC\Files\Filesystem::mount($storage2, array(), '/substorage');
  258. $textSize = strlen("dummy file data\n");
  259. $rootView = new \OC\Files\View('');
  260. $cachedData = $rootView->getFileInfo('/');
  261. $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
  262. $this->assertEquals(-1, $cachedData['size']);
  263. $folderData = $rootView->getDirectoryContent('/substorage/folder');
  264. $this->assertEquals('text/plain', $folderData[0]['mimetype']);
  265. $this->assertEquals($textSize, $folderData[0]['size']);
  266. }
  267. /**
  268. * @medium
  269. */
  270. public function testSearch() {
  271. $storage1 = $this->getTestStorage();
  272. $storage2 = $this->getTestStorage();
  273. $storage3 = $this->getTestStorage();
  274. \OC\Files\Filesystem::mount($storage1, array(), '/');
  275. \OC\Files\Filesystem::mount($storage2, array(), '/substorage');
  276. \OC\Files\Filesystem::mount($storage3, array(), '/folder/anotherstorage');
  277. $rootView = new \OC\Files\View('');
  278. $results = $rootView->search('foo');
  279. $this->assertEquals(6, count($results));
  280. $paths = array();
  281. foreach ($results as $result) {
  282. $this->assertEquals($result['path'], \OC\Files\Filesystem::normalizePath($result['path']));
  283. $paths[] = $result['path'];
  284. }
  285. $this->assertContains('/foo.txt', $paths);
  286. $this->assertContains('/foo.png', $paths);
  287. $this->assertContains('/substorage/foo.txt', $paths);
  288. $this->assertContains('/substorage/foo.png', $paths);
  289. $this->assertContains('/folder/anotherstorage/foo.txt', $paths);
  290. $this->assertContains('/folder/anotherstorage/foo.png', $paths);
  291. $folderView = new \OC\Files\View('/folder');
  292. $results = $folderView->search('bar');
  293. $this->assertEquals(2, count($results));
  294. $paths = array();
  295. foreach ($results as $result) {
  296. $paths[] = $result['path'];
  297. }
  298. $this->assertContains('/anotherstorage/folder/bar.txt', $paths);
  299. $this->assertContains('/bar.txt', $paths);
  300. $results = $folderView->search('foo');
  301. $this->assertEquals(2, count($results));
  302. $paths = array();
  303. foreach ($results as $result) {
  304. $paths[] = $result['path'];
  305. }
  306. $this->assertContains('/anotherstorage/foo.txt', $paths);
  307. $this->assertContains('/anotherstorage/foo.png', $paths);
  308. $this->assertEquals(6, count($rootView->searchByMime('text')));
  309. $this->assertEquals(3, count($folderView->searchByMime('text')));
  310. }
  311. /**
  312. * @medium
  313. */
  314. public function testWatcher() {
  315. $storage1 = $this->getTestStorage();
  316. \OC\Files\Filesystem::mount($storage1, array(), '/');
  317. $storage1->getWatcher()->setPolicy(Watcher::CHECK_ALWAYS);
  318. $rootView = new \OC\Files\View('');
  319. $cachedData = $rootView->getFileInfo('foo.txt');
  320. $this->assertEquals(16, $cachedData['size']);
  321. $rootView->putFileInfo('foo.txt', array('storage_mtime' => 10));
  322. $storage1->file_put_contents('foo.txt', 'foo');
  323. clearstatcache();
  324. $cachedData = $rootView->getFileInfo('foo.txt');
  325. $this->assertEquals(3, $cachedData['size']);
  326. }
  327. /**
  328. * @medium
  329. */
  330. public function testCopyBetweenStorageNoCross() {
  331. $storage1 = $this->getTestStorage(true, '\Test\Files\TemporaryNoCross');
  332. $storage2 = $this->getTestStorage(true, '\Test\Files\TemporaryNoCross');
  333. $this->copyBetweenStorages($storage1, $storage2);
  334. }
  335. /**
  336. * @medium
  337. */
  338. public function testCopyBetweenStorageCross() {
  339. $storage1 = $this->getTestStorage();
  340. $storage2 = $this->getTestStorage();
  341. $this->copyBetweenStorages($storage1, $storage2);
  342. }
  343. /**
  344. * @medium
  345. */
  346. public function testCopyBetweenStorageCrossNonLocal() {
  347. $storage1 = $this->getTestStorage(true, '\Test\Files\TemporaryNoLocal');
  348. $storage2 = $this->getTestStorage(true, '\Test\Files\TemporaryNoLocal');
  349. $this->copyBetweenStorages($storage1, $storage2);
  350. }
  351. function copyBetweenStorages($storage1, $storage2) {
  352. \OC\Files\Filesystem::mount($storage1, array(), '/');
  353. \OC\Files\Filesystem::mount($storage2, array(), '/substorage');
  354. $rootView = new \OC\Files\View('');
  355. $rootView->mkdir('substorage/emptyfolder');
  356. $rootView->copy('substorage', 'anotherfolder');
  357. $this->assertTrue($rootView->is_dir('/anotherfolder'));
  358. $this->assertTrue($rootView->is_dir('/substorage'));
  359. $this->assertTrue($rootView->is_dir('/anotherfolder/emptyfolder'));
  360. $this->assertTrue($rootView->is_dir('/substorage/emptyfolder'));
  361. $this->assertTrue($rootView->file_exists('/anotherfolder/foo.txt'));
  362. $this->assertTrue($rootView->file_exists('/anotherfolder/foo.png'));
  363. $this->assertTrue($rootView->file_exists('/anotherfolder/folder/bar.txt'));
  364. $this->assertTrue($rootView->file_exists('/substorage/foo.txt'));
  365. $this->assertTrue($rootView->file_exists('/substorage/foo.png'));
  366. $this->assertTrue($rootView->file_exists('/substorage/folder/bar.txt'));
  367. }
  368. /**
  369. * @medium
  370. */
  371. public function testMoveBetweenStorageNoCross() {
  372. $storage1 = $this->getTestStorage(true, '\Test\Files\TemporaryNoCross');
  373. $storage2 = $this->getTestStorage(true, '\Test\Files\TemporaryNoCross');
  374. $this->moveBetweenStorages($storage1, $storage2);
  375. }
  376. /**
  377. * @medium
  378. */
  379. public function testMoveBetweenStorageCross() {
  380. $storage1 = $this->getTestStorage();
  381. $storage2 = $this->getTestStorage();
  382. $this->moveBetweenStorages($storage1, $storage2);
  383. }
  384. /**
  385. * @medium
  386. */
  387. public function testMoveBetweenStorageCrossNonLocal() {
  388. $storage1 = $this->getTestStorage(true, '\Test\Files\TemporaryNoLocal');
  389. $storage2 = $this->getTestStorage(true, '\Test\Files\TemporaryNoLocal');
  390. $this->moveBetweenStorages($storage1, $storage2);
  391. }
  392. function moveBetweenStorages($storage1, $storage2) {
  393. \OC\Files\Filesystem::mount($storage1, array(), '/');
  394. \OC\Files\Filesystem::mount($storage2, array(), '/substorage');
  395. $rootView = new \OC\Files\View('');
  396. $rootView->rename('foo.txt', 'substorage/folder/foo.txt');
  397. $this->assertFalse($rootView->file_exists('foo.txt'));
  398. $this->assertTrue($rootView->file_exists('substorage/folder/foo.txt'));
  399. $rootView->rename('substorage/folder', 'anotherfolder');
  400. $this->assertFalse($rootView->is_dir('substorage/folder'));
  401. $this->assertTrue($rootView->file_exists('anotherfolder/foo.txt'));
  402. $this->assertTrue($rootView->file_exists('anotherfolder/bar.txt'));
  403. }
  404. /**
  405. * @medium
  406. */
  407. public function testUnlink() {
  408. $storage1 = $this->getTestStorage();
  409. $storage2 = $this->getTestStorage();
  410. \OC\Files\Filesystem::mount($storage1, array(), '/');
  411. \OC\Files\Filesystem::mount($storage2, array(), '/substorage');
  412. $rootView = new \OC\Files\View('');
  413. $rootView->file_put_contents('/foo.txt', 'asd');
  414. $rootView->file_put_contents('/substorage/bar.txt', 'asd');
  415. $this->assertTrue($rootView->file_exists('foo.txt'));
  416. $this->assertTrue($rootView->file_exists('substorage/bar.txt'));
  417. $this->assertTrue($rootView->unlink('foo.txt'));
  418. $this->assertTrue($rootView->unlink('substorage/bar.txt'));
  419. $this->assertFalse($rootView->file_exists('foo.txt'));
  420. $this->assertFalse($rootView->file_exists('substorage/bar.txt'));
  421. }
  422. /**
  423. * @medium
  424. */
  425. public function testUnlinkRootMustFail() {
  426. $storage1 = $this->getTestStorage();
  427. $storage2 = $this->getTestStorage();
  428. \OC\Files\Filesystem::mount($storage1, array(), '/');
  429. \OC\Files\Filesystem::mount($storage2, array(), '/substorage');
  430. $rootView = new \OC\Files\View('');
  431. $rootView->file_put_contents('/foo.txt', 'asd');
  432. $rootView->file_put_contents('/substorage/bar.txt', 'asd');
  433. $this->assertFalse($rootView->unlink(''));
  434. $this->assertFalse($rootView->unlink('/'));
  435. $this->assertFalse($rootView->unlink('substorage'));
  436. $this->assertFalse($rootView->unlink('/substorage'));
  437. }
  438. /**
  439. * @medium
  440. */
  441. public function testTouch() {
  442. $storage = $this->getTestStorage(true, '\Test\Files\TemporaryNoTouch');
  443. \OC\Files\Filesystem::mount($storage, array(), '/');
  444. $rootView = new \OC\Files\View('');
  445. $oldCachedData = $rootView->getFileInfo('foo.txt');
  446. $rootView->touch('foo.txt', 500);
  447. $cachedData = $rootView->getFileInfo('foo.txt');
  448. $this->assertEquals(500, $cachedData['mtime']);
  449. $this->assertEquals($oldCachedData['storage_mtime'], $cachedData['storage_mtime']);
  450. $rootView->putFileInfo('foo.txt', array('storage_mtime' => 1000)); //make sure the watcher detects the change
  451. $rootView->file_put_contents('foo.txt', 'asd');
  452. $cachedData = $rootView->getFileInfo('foo.txt');
  453. $this->assertGreaterThanOrEqual($oldCachedData['mtime'], $cachedData['mtime']);
  454. $this->assertEquals($cachedData['storage_mtime'], $cachedData['mtime']);
  455. }
  456. /**
  457. * @medium
  458. */
  459. public function testViewHooks() {
  460. $storage1 = $this->getTestStorage();
  461. $storage2 = $this->getTestStorage();
  462. $defaultRoot = \OC\Files\Filesystem::getRoot();
  463. \OC\Files\Filesystem::mount($storage1, array(), '/');
  464. \OC\Files\Filesystem::mount($storage2, array(), $defaultRoot . '/substorage');
  465. \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
  466. $rootView = new \OC\Files\View('');
  467. $subView = new \OC\Files\View($defaultRoot . '/substorage');
  468. $this->hookPath = null;
  469. $rootView->file_put_contents('/foo.txt', 'asd');
  470. $this->assertNull($this->hookPath);
  471. $subView->file_put_contents('/foo.txt', 'asd');
  472. $this->assertEquals('/substorage/foo.txt', $this->hookPath);
  473. }
  474. private $hookPath;
  475. public function dummyHook($params) {
  476. $this->hookPath = $params['path'];
  477. }
  478. public function testSearchNotOutsideView() {
  479. $storage1 = $this->getTestStorage();
  480. \OC\Files\Filesystem::mount($storage1, array(), '/');
  481. $storage1->rename('folder', 'foo');
  482. $scanner = $storage1->getScanner();
  483. $scanner->scan('');
  484. $view = new \OC\Files\View('/foo');
  485. $result = $view->search('.txt');
  486. $this->assertCount(1, $result);
  487. }
  488. /**
  489. * @param bool $scan
  490. * @param string $class
  491. * @return \OC\Files\Storage\Storage
  492. */
  493. private function getTestStorage($scan = true, $class = '\OC\Files\Storage\Temporary') {
  494. /**
  495. * @var \OC\Files\Storage\Storage $storage
  496. */
  497. $storage = new $class(array());
  498. $textData = "dummy file data\n";
  499. $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png');
  500. $storage->mkdir('folder');
  501. $storage->file_put_contents('foo.txt', $textData);
  502. $storage->file_put_contents('foo.png', $imgData);
  503. $storage->file_put_contents('folder/bar.txt', $textData);
  504. if ($scan) {
  505. $scanner = $storage->getScanner();
  506. $scanner->scan('');
  507. }
  508. $this->storages[] = $storage;
  509. return $storage;
  510. }
  511. /**
  512. * @medium
  513. */
  514. public function testViewHooksIfRootStartsTheSame() {
  515. $storage1 = $this->getTestStorage();
  516. $storage2 = $this->getTestStorage();
  517. $defaultRoot = \OC\Files\Filesystem::getRoot();
  518. \OC\Files\Filesystem::mount($storage1, array(), '/');
  519. \OC\Files\Filesystem::mount($storage2, array(), $defaultRoot . '_substorage');
  520. \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
  521. $subView = new \OC\Files\View($defaultRoot . '_substorage');
  522. $this->hookPath = null;
  523. $subView->file_put_contents('/foo.txt', 'asd');
  524. $this->assertNull($this->hookPath);
  525. }
  526. private $hookWritePath;
  527. private $hookCreatePath;
  528. private $hookUpdatePath;
  529. public function dummyHookWrite($params) {
  530. $this->hookWritePath = $params['path'];
  531. }
  532. public function dummyHookUpdate($params) {
  533. $this->hookUpdatePath = $params['path'];
  534. }
  535. public function dummyHookCreate($params) {
  536. $this->hookCreatePath = $params['path'];
  537. }
  538. public function testEditNoCreateHook() {
  539. $storage1 = $this->getTestStorage();
  540. $storage2 = $this->getTestStorage();
  541. $defaultRoot = \OC\Files\Filesystem::getRoot();
  542. \OC\Files\Filesystem::mount($storage1, array(), '/');
  543. \OC\Files\Filesystem::mount($storage2, array(), $defaultRoot);
  544. \OC_Hook::connect('OC_Filesystem', 'post_create', $this, 'dummyHookCreate');
  545. \OC_Hook::connect('OC_Filesystem', 'post_update', $this, 'dummyHookUpdate');
  546. \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHookWrite');
  547. $view = new \OC\Files\View($defaultRoot);
  548. $this->hookWritePath = $this->hookUpdatePath = $this->hookCreatePath = null;
  549. $view->file_put_contents('/asd.txt', 'foo');
  550. $this->assertEquals('/asd.txt', $this->hookCreatePath);
  551. $this->assertNull($this->hookUpdatePath);
  552. $this->assertEquals('/asd.txt', $this->hookWritePath);
  553. $this->hookWritePath = $this->hookUpdatePath = $this->hookCreatePath = null;
  554. $view->file_put_contents('/asd.txt', 'foo');
  555. $this->assertNull($this->hookCreatePath);
  556. $this->assertEquals('/asd.txt', $this->hookUpdatePath);
  557. $this->assertEquals('/asd.txt', $this->hookWritePath);
  558. \OC_Hook::clear('OC_Filesystem', 'post_create');
  559. \OC_Hook::clear('OC_Filesystem', 'post_update');
  560. \OC_Hook::clear('OC_Filesystem', 'post_write');
  561. }
  562. /**
  563. * @dataProvider resolvePathTestProvider
  564. */
  565. public function testResolvePath($expected, $pathToTest) {
  566. $storage1 = $this->getTestStorage();
  567. \OC\Files\Filesystem::mount($storage1, array(), '/');
  568. $view = new \OC\Files\View('');
  569. $result = $view->resolvePath($pathToTest);
  570. $this->assertEquals($expected, $result[1]);
  571. $exists = $view->file_exists($pathToTest);
  572. $this->assertTrue($exists);
  573. $exists = $view->file_exists($result[1]);
  574. $this->assertTrue($exists);
  575. }
  576. function resolvePathTestProvider() {
  577. return array(
  578. array('foo.txt', 'foo.txt'),
  579. array('foo.txt', '/foo.txt'),
  580. array('folder', 'folder'),
  581. array('folder', '/folder'),
  582. array('folder', 'folder/'),
  583. array('folder', '/folder/'),
  584. array('folder/bar.txt', 'folder/bar.txt'),
  585. array('folder/bar.txt', '/folder/bar.txt'),
  586. array('', ''),
  587. array('', '/'),
  588. );
  589. }
  590. public function testUTF8Names() {
  591. $names = array('虚', '和知しゃ和で', 'regular ascii', 'sɨˈrɪlɪk', 'ѨѬ', 'أنا أحب القراءة كثيرا');
  592. $storage = new \OC\Files\Storage\Temporary(array());
  593. \OC\Files\Filesystem::mount($storage, array(), '/');
  594. $rootView = new \OC\Files\View('');
  595. foreach ($names as $name) {
  596. $rootView->file_put_contents('/' . $name, 'dummy content');
  597. }
  598. $list = $rootView->getDirectoryContent('/');
  599. $this->assertCount(count($names), $list);
  600. foreach ($list as $item) {
  601. $this->assertContains($item['name'], $names);
  602. }
  603. $cache = $storage->getCache();
  604. $scanner = $storage->getScanner();
  605. $scanner->scan('');
  606. $list = $cache->getFolderContents('');
  607. $this->assertCount(count($names), $list);
  608. foreach ($list as $item) {
  609. $this->assertContains($item['name'], $names);
  610. }
  611. }
  612. public function xtestLongPath() {
  613. $storage = new \OC\Files\Storage\Temporary(array());
  614. \OC\Files\Filesystem::mount($storage, array(), '/');
  615. $rootView = new \OC\Files\View('');
  616. $longPath = '';
  617. $ds = DIRECTORY_SEPARATOR;
  618. /*
  619. * 4096 is the maximum path length in file_cache.path in *nix
  620. * 1024 is the max path length in mac
  621. * 228 is the max path length in windows
  622. */
  623. $folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789';
  624. $tmpdirLength = strlen(\OC::$server->getTempManager()->getTemporaryFolder());
  625. if (\OC_Util::runningOnWindows()) {
  626. $this->markTestSkipped('[Windows] ');
  627. $depth = ((260 - $tmpdirLength) / 57);
  628. } elseif (\OC_Util::runningOnMac()) {
  629. $depth = ((1024 - $tmpdirLength) / 57);
  630. } else {
  631. $depth = ((4000 - $tmpdirLength) / 57);
  632. }
  633. foreach (range(0, $depth - 1) as $i) {
  634. $longPath .= $ds . $folderName;
  635. $result = $rootView->mkdir($longPath);
  636. $this->assertTrue($result, "mkdir failed on $i - path length: " . strlen($longPath));
  637. $result = $rootView->file_put_contents($longPath . "{$ds}test.txt", 'lorem');
  638. $this->assertEquals(5, $result, "file_put_contents failed on $i");
  639. $this->assertTrue($rootView->file_exists($longPath));
  640. $this->assertTrue($rootView->file_exists($longPath . "{$ds}test.txt"));
  641. }
  642. $cache = $storage->getCache();
  643. $scanner = $storage->getScanner();
  644. $scanner->scan('');
  645. $longPath = $folderName;
  646. foreach (range(0, $depth - 1) as $i) {
  647. $cachedFolder = $cache->get($longPath);
  648. $this->assertTrue(is_array($cachedFolder), "No cache entry for folder at $i");
  649. $this->assertEquals($folderName, $cachedFolder['name'], "Wrong cache entry for folder at $i");
  650. $cachedFile = $cache->get($longPath . '/test.txt');
  651. $this->assertTrue(is_array($cachedFile), "No cache entry for file at $i");
  652. $this->assertEquals('test.txt', $cachedFile['name'], "Wrong cache entry for file at $i");
  653. $longPath .= $ds . $folderName;
  654. }
  655. }
  656. public function testTouchNotSupported() {
  657. $storage = new TemporaryNoTouch(array());
  658. $scanner = $storage->getScanner();
  659. \OC\Files\Filesystem::mount($storage, array(), '/test/');
  660. $past = time() - 100;
  661. $storage->file_put_contents('test', 'foobar');
  662. $scanner->scan('');
  663. $view = new \OC\Files\View('');
  664. $info = $view->getFileInfo('/test/test');
  665. $view->touch('/test/test', $past);
  666. $scanner->scanFile('test', \OC\Files\Cache\Scanner::REUSE_ETAG);
  667. $info2 = $view->getFileInfo('/test/test');
  668. $this->assertSame($info['etag'], $info2['etag']);
  669. }
  670. public function testWatcherEtagCrossStorage() {
  671. $storage1 = new Temporary(array());
  672. $storage2 = new Temporary(array());
  673. $scanner1 = $storage1->getScanner();
  674. $scanner2 = $storage2->getScanner();
  675. $storage1->mkdir('sub');
  676. \OC\Files\Filesystem::mount($storage1, array(), '/test/');
  677. \OC\Files\Filesystem::mount($storage2, array(), '/test/sub/storage');
  678. $past = time() - 100;
  679. $storage2->file_put_contents('test.txt', 'foobar');
  680. $scanner1->scan('');
  681. $scanner2->scan('');
  682. $view = new \OC\Files\View('');
  683. $storage2->getWatcher('')->setPolicy(Watcher::CHECK_ALWAYS);
  684. $oldFileInfo = $view->getFileInfo('/test/sub/storage/test.txt');
  685. $oldFolderInfo = $view->getFileInfo('/test');
  686. $storage2->getCache()->update($oldFileInfo->getId(), array(
  687. 'storage_mtime' => $past
  688. ));
  689. $view->getFileInfo('/test/sub/storage/test.txt');
  690. $newFolderInfo = $view->getFileInfo('/test');
  691. $this->assertNotEquals($newFolderInfo->getEtag(), $oldFolderInfo->getEtag());
  692. }
  693. /**
  694. * @dataProvider absolutePathProvider
  695. */
  696. public function testGetAbsolutePath($expectedPath, $relativePath) {
  697. $view = new \OC\Files\View('/files');
  698. $this->assertEquals($expectedPath, $view->getAbsolutePath($relativePath));
  699. }
  700. public function testPartFileInfo() {
  701. $storage = new Temporary(array());
  702. $scanner = $storage->getScanner();
  703. \OC\Files\Filesystem::mount($storage, array(), '/test/');
  704. $storage->file_put_contents('test.part', 'foobar');
  705. $scanner->scan('');
  706. $view = new \OC\Files\View('/test');
  707. $info = $view->getFileInfo('test.part');
  708. $this->assertInstanceOf('\OCP\Files\FileInfo', $info);
  709. $this->assertNull($info->getId());
  710. $this->assertEquals(6, $info->getSize());
  711. }
  712. function absolutePathProvider() {
  713. return array(
  714. array('/files/', ''),
  715. array('/files/0', '0'),
  716. array('/files/false', 'false'),
  717. array('/files/true', 'true'),
  718. array('/files/', '/'),
  719. array('/files/test', 'test'),
  720. array('/files/test', '/test'),
  721. );
  722. }
  723. /**
  724. * @dataProvider chrootRelativePathProvider
  725. */
  726. function testChrootGetRelativePath($root, $absolutePath, $expectedPath) {
  727. $view = new \OC\Files\View('/files');
  728. $view->chroot($root);
  729. $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath));
  730. }
  731. public function chrootRelativePathProvider() {
  732. return $this->relativePathProvider('/');
  733. }
  734. /**
  735. * @dataProvider initRelativePathProvider
  736. */
  737. public function testInitGetRelativePath($root, $absolutePath, $expectedPath) {
  738. $view = new \OC\Files\View($root);
  739. $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath));
  740. }
  741. public function initRelativePathProvider() {
  742. return $this->relativePathProvider(null);
  743. }
  744. public function relativePathProvider($missingRootExpectedPath) {
  745. return array(
  746. // No root - returns the path
  747. array('', '/files', '/files'),
  748. array('', '/files/', '/files/'),
  749. // Root equals path - /
  750. array('/files/', '/files/', '/'),
  751. array('/files/', '/files', '/'),
  752. array('/files', '/files/', '/'),
  753. array('/files', '/files', '/'),
  754. // False negatives: chroot fixes those by adding the leading slash.
  755. // But setting them up with this root (instead of chroot($root))
  756. // will fail them, although they should be the same.
  757. // TODO init should be fixed, so it also adds the leading slash
  758. array('files/', '/files/', $missingRootExpectedPath),
  759. array('files', '/files/', $missingRootExpectedPath),
  760. array('files/', '/files', $missingRootExpectedPath),
  761. array('files', '/files', $missingRootExpectedPath),
  762. // False negatives: Paths provided to the method should have a leading slash
  763. // TODO input should be checked to have a leading slash
  764. array('/files/', 'files/', null),
  765. array('/files', 'files/', null),
  766. array('/files/', 'files', null),
  767. array('/files', 'files', null),
  768. // with trailing slashes
  769. array('/files/', '/files/0', '0'),
  770. array('/files/', '/files/false', 'false'),
  771. array('/files/', '/files/true', 'true'),
  772. array('/files/', '/files/test', 'test'),
  773. array('/files/', '/files/test/foo', 'test/foo'),
  774. // without trailing slashes
  775. // TODO false expectation: Should match "with trailing slashes"
  776. array('/files', '/files/0', '/0'),
  777. array('/files', '/files/false', '/false'),
  778. array('/files', '/files/true', '/true'),
  779. array('/files', '/files/test', '/test'),
  780. array('/files', '/files/test/foo', '/test/foo'),
  781. // leading slashes
  782. array('/files/', '/files_trashbin/', null),
  783. array('/files', '/files_trashbin/', null),
  784. array('/files/', '/files_trashbin', null),
  785. array('/files', '/files_trashbin', null),
  786. // no leading slashes
  787. array('files/', 'files_trashbin/', null),
  788. array('files', 'files_trashbin/', null),
  789. array('files/', 'files_trashbin', null),
  790. array('files', 'files_trashbin', null),
  791. // mixed leading slashes
  792. array('files/', '/files_trashbin/', null),
  793. array('/files/', 'files_trashbin/', null),
  794. array('files', '/files_trashbin/', null),
  795. array('/files', 'files_trashbin/', null),
  796. array('files/', '/files_trashbin', null),
  797. array('/files/', 'files_trashbin', null),
  798. array('files', '/files_trashbin', null),
  799. array('/files', 'files_trashbin', null),
  800. array('files', 'files_trashbin/test', null),
  801. array('/files', '/files_trashbin/test', null),
  802. array('/files', 'files_trashbin/test', null),
  803. );
  804. }
  805. public function testFileView() {
  806. $storage = new Temporary(array());
  807. $scanner = $storage->getScanner();
  808. $storage->file_put_contents('foo.txt', 'bar');
  809. \OC\Files\Filesystem::mount($storage, array(), '/test/');
  810. $scanner->scan('');
  811. $view = new \OC\Files\View('/test/foo.txt');
  812. $this->assertEquals('bar', $view->file_get_contents(''));
  813. $fh = tmpfile();
  814. fwrite($fh, 'foo');
  815. rewind($fh);
  816. $view->file_put_contents('', $fh);
  817. $this->assertEquals('foo', $view->file_get_contents(''));
  818. }
  819. /**
  820. * @dataProvider tooLongPathDataProvider
  821. * @expectedException \OCP\Files\InvalidPathException
  822. */
  823. public function testTooLongPath($operation, $param0 = null) {
  824. $longPath = '';
  825. // 4000 is the maximum path length in file_cache.path
  826. $folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789';
  827. $depth = (4000 / 57);
  828. foreach (range(0, $depth + 1) as $i) {
  829. $longPath .= '/' . $folderName;
  830. }
  831. $storage = new \OC\Files\Storage\Temporary(array());
  832. $this->tempStorage = $storage; // for later hard cleanup
  833. \OC\Files\Filesystem::mount($storage, array(), '/');
  834. $rootView = new \OC\Files\View('');
  835. if ($param0 === '@0') {
  836. $param0 = $longPath;
  837. }
  838. if ($operation === 'hash') {
  839. $param0 = $longPath;
  840. $longPath = 'md5';
  841. }
  842. call_user_func(array($rootView, $operation), $longPath, $param0);
  843. }
  844. public function tooLongPathDataProvider() {
  845. return array(
  846. array('getAbsolutePath'),
  847. array('getRelativePath'),
  848. array('getMountPoint'),
  849. array('resolvePath'),
  850. array('getLocalFile'),
  851. array('getLocalFolder'),
  852. array('mkdir'),
  853. array('rmdir'),
  854. array('opendir'),
  855. array('is_dir'),
  856. array('is_file'),
  857. array('stat'),
  858. array('filetype'),
  859. array('filesize'),
  860. array('readfile'),
  861. array('isCreatable'),
  862. array('isReadable'),
  863. array('isUpdatable'),
  864. array('isDeletable'),
  865. array('isSharable'),
  866. array('file_exists'),
  867. array('filemtime'),
  868. array('touch'),
  869. array('file_get_contents'),
  870. array('unlink'),
  871. array('deleteAll'),
  872. array('toTmpFile'),
  873. array('getMimeType'),
  874. array('free_space'),
  875. array('getFileInfo'),
  876. array('getDirectoryContent'),
  877. array('getOwner'),
  878. array('getETag'),
  879. array('file_put_contents', 'ipsum'),
  880. array('rename', '@0'),
  881. array('copy', '@0'),
  882. array('fopen', 'r'),
  883. array('fromTmpFile', '@0'),
  884. array('hash'),
  885. array('hasUpdated', 0),
  886. array('putFileInfo', array()),
  887. );
  888. }
  889. public function testRenameCrossStoragePreserveMtime() {
  890. $storage1 = new Temporary(array());
  891. $storage2 = new Temporary(array());
  892. $scanner1 = $storage1->getScanner();
  893. $scanner2 = $storage2->getScanner();
  894. $storage1->mkdir('sub');
  895. $storage1->mkdir('foo');
  896. $storage1->file_put_contents('foo.txt', 'asd');
  897. $storage1->file_put_contents('foo/bar.txt', 'asd');
  898. \OC\Files\Filesystem::mount($storage1, array(), '/test/');
  899. \OC\Files\Filesystem::mount($storage2, array(), '/test/sub/storage');
  900. $view = new \OC\Files\View('');
  901. $time = time() - 200;
  902. $view->touch('/test/foo.txt', $time);
  903. $view->touch('/test/foo', $time);
  904. $view->touch('/test/foo/bar.txt', $time);
  905. $view->rename('/test/foo.txt', '/test/sub/storage/foo.txt');
  906. $this->assertEquals($time, $view->filemtime('/test/sub/storage/foo.txt'));
  907. $view->rename('/test/foo', '/test/sub/storage/foo');
  908. $this->assertEquals($time, $view->filemtime('/test/sub/storage/foo/bar.txt'));
  909. }
  910. public function testRenameFailDeleteTargetKeepSource() {
  911. $this->doTestCopyRenameFail('rename');
  912. }
  913. public function testCopyFailDeleteTargetKeepSource() {
  914. $this->doTestCopyRenameFail('copy');
  915. }
  916. private function doTestCopyRenameFail($operation) {
  917. $storage1 = new Temporary(array());
  918. /** @var \PHPUnit_Framework_MockObject_MockObject | \OC\Files\Storage\Temporary $storage2 */
  919. $storage2 = $this->getMockBuilder('\Test\Files\TemporaryNoCross')
  920. ->setConstructorArgs([[]])
  921. ->setMethods(['fopen'])
  922. ->getMock();
  923. $storage2->expects($this->any())
  924. ->method('fopen')
  925. ->will($this->returnCallback(function ($path, $mode) use ($storage2) {
  926. /** @var \PHPUnit_Framework_MockObject_MockObject | \OC\Files\Storage\Temporary $storage2 */
  927. $source = fopen($storage2->getSourcePath($path), $mode);
  928. return \OC\Files\Stream\Quota::wrap($source, 9);
  929. }));
  930. $storage1->mkdir('sub');
  931. $storage1->file_put_contents('foo.txt', '0123456789ABCDEFGH');
  932. $storage1->mkdir('dirtomove');
  933. $storage1->file_put_contents('dirtomove/indir1.txt', '0123456'); // fits
  934. $storage1->file_put_contents('dirtomove/indir2.txt', '0123456789ABCDEFGH'); // doesn't fit
  935. $storage2->file_put_contents('existing.txt', '0123');
  936. $storage1->getScanner()->scan('');
  937. $storage2->getScanner()->scan('');
  938. \OC\Files\Filesystem::mount($storage1, array(), '/test/');
  939. \OC\Files\Filesystem::mount($storage2, array(), '/test/sub/storage');
  940. // move file
  941. $view = new \OC\Files\View('');
  942. $this->assertTrue($storage1->file_exists('foo.txt'));
  943. $this->assertFalse($storage2->file_exists('foo.txt'));
  944. $this->assertFalse($view->$operation('/test/foo.txt', '/test/sub/storage/foo.txt'));
  945. $this->assertFalse($storage2->file_exists('foo.txt'));
  946. $this->assertFalse($storage2->getCache()->get('foo.txt'));
  947. $this->assertTrue($storage1->file_exists('foo.txt'));
  948. // if target exists, it will be deleted too
  949. $this->assertFalse($view->$operation('/test/foo.txt', '/test/sub/storage/existing.txt'));
  950. $this->assertFalse($storage2->file_exists('existing.txt'));
  951. $this->assertFalse($storage2->getCache()->get('existing.txt'));
  952. $this->assertTrue($storage1->file_exists('foo.txt'));
  953. // move folder
  954. $this->assertFalse($view->$operation('/test/dirtomove/', '/test/sub/storage/dirtomove/'));
  955. // since the move failed, the full source tree is kept
  956. $this->assertTrue($storage1->file_exists('dirtomove/indir1.txt'));
  957. $this->assertTrue($storage1->file_exists('dirtomove/indir2.txt'));
  958. // second file not moved/copied
  959. $this->assertFalse($storage2->file_exists('dirtomove/indir2.txt'));
  960. $this->assertFalse($storage2->getCache()->get('dirtomove/indir2.txt'));
  961. }
  962. public function testDeleteFailKeepCache() {
  963. /**
  964. * @var \PHPUnit_Framework_MockObject_MockObject | \OC\Files\Storage\Temporary $storage
  965. */
  966. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  967. ->setConstructorArgs(array(array()))
  968. ->setMethods(array('unlink'))
  969. ->getMock();
  970. $storage->expects($this->once())
  971. ->method('unlink')
  972. ->will($this->returnValue(false));
  973. $scanner = $storage->getScanner();
  974. $cache = $storage->getCache();
  975. $storage->file_put_contents('foo.txt', 'asd');
  976. $scanner->scan('');
  977. \OC\Files\Filesystem::mount($storage, array(), '/test/');
  978. $view = new \OC\Files\View('/test');
  979. $this->assertFalse($view->unlink('foo.txt'));
  980. $this->assertTrue($cache->inCache('foo.txt'));
  981. }
  982. function directoryTraversalProvider() {
  983. return [
  984. ['../test/'],
  985. ['..\\test\\my/../folder'],
  986. ['/test/my/../foo\\'],
  987. ];
  988. }
  989. /**
  990. * @dataProvider directoryTraversalProvider
  991. * @expectedException \Exception
  992. * @param string $root
  993. */
  994. public function testConstructDirectoryTraversalException($root) {
  995. new \OC\Files\View($root);
  996. }
  997. public function testRenameOverWrite() {
  998. $storage = new Temporary(array());
  999. $scanner = $storage->getScanner();
  1000. $storage->mkdir('sub');
  1001. $storage->mkdir('foo');
  1002. $storage->file_put_contents('foo.txt', 'asd');
  1003. $storage->file_put_contents('foo/bar.txt', 'asd');
  1004. $scanner->scan('');
  1005. \OC\Files\Filesystem::mount($storage, array(), '/test/');
  1006. $view = new \OC\Files\View('');
  1007. $this->assertTrue($view->rename('/test/foo.txt', '/test/foo/bar.txt'));
  1008. }
  1009. public function testSetMountOptionsInStorage() {
  1010. $mount = new MountPoint('\OC\Files\Storage\Temporary', '/asd/', [[]], \OC\Files\Filesystem::getLoader(), ['foo' => 'bar']);
  1011. \OC\Files\Filesystem::getMountManager()->addMount($mount);
  1012. /** @var \OC\Files\Storage\Common $storage */
  1013. $storage = $mount->getStorage();
  1014. $this->assertEquals($storage->getMountOption('foo'), 'bar');
  1015. }
  1016. public function testSetMountOptionsWatcherPolicy() {
  1017. $mount = new MountPoint('\OC\Files\Storage\Temporary', '/asd/', [[]], \OC\Files\Filesystem::getLoader(), ['filesystem_check_changes' => Watcher::CHECK_NEVER]);
  1018. \OC\Files\Filesystem::getMountManager()->addMount($mount);
  1019. /** @var \OC\Files\Storage\Common $storage */
  1020. $storage = $mount->getStorage();
  1021. $watcher = $storage->getWatcher();
  1022. $this->assertEquals(Watcher::CHECK_NEVER, $watcher->getPolicy());
  1023. }
  1024. public function testGetAbsolutePathOnNull() {
  1025. $view = new \OC\Files\View();
  1026. $this->assertNull($view->getAbsolutePath(null));
  1027. }
  1028. public function testGetRelativePathOnNull() {
  1029. $view = new \OC\Files\View();
  1030. $this->assertNull($view->getRelativePath(null));
  1031. }
  1032. /**
  1033. * @expectedException \InvalidArgumentException
  1034. */
  1035. public function testNullAsRoot() {
  1036. new \OC\Files\View(null);
  1037. }
  1038. /**
  1039. * e.g. reading from a folder that's being renamed
  1040. *
  1041. * @expectedException \OCP\Lock\LockedException
  1042. *
  1043. * @dataProvider dataLockPaths
  1044. *
  1045. * @param string $rootPath
  1046. * @param string $pathPrefix
  1047. */
  1048. public function testReadFromWriteLockedPath($rootPath, $pathPrefix) {
  1049. $rootPath = str_replace('{folder}', 'files', $rootPath);
  1050. $pathPrefix = str_replace('{folder}', 'files', $pathPrefix);
  1051. $view = new \OC\Files\View($rootPath);
  1052. $storage = new Temporary(array());
  1053. \OC\Files\Filesystem::mount($storage, [], '/');
  1054. $this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
  1055. $view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED);
  1056. }
  1057. /**
  1058. * Reading from a files_encryption folder that's being renamed
  1059. *
  1060. * @dataProvider dataLockPaths
  1061. *
  1062. * @param string $rootPath
  1063. * @param string $pathPrefix
  1064. */
  1065. public function testReadFromWriteUnlockablePath($rootPath, $pathPrefix) {
  1066. $rootPath = str_replace('{folder}', 'files_encryption', $rootPath);
  1067. $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix);
  1068. $view = new \OC\Files\View($rootPath);
  1069. $storage = new Temporary(array());
  1070. \OC\Files\Filesystem::mount($storage, [], '/');
  1071. $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
  1072. $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED));
  1073. }
  1074. /**
  1075. * e.g. writing a file that's being downloaded
  1076. *
  1077. * @expectedException \OCP\Lock\LockedException
  1078. *
  1079. * @dataProvider dataLockPaths
  1080. *
  1081. * @param string $rootPath
  1082. * @param string $pathPrefix
  1083. */
  1084. public function testWriteToReadLockedFile($rootPath, $pathPrefix) {
  1085. $rootPath = str_replace('{folder}', 'files', $rootPath);
  1086. $pathPrefix = str_replace('{folder}', 'files', $pathPrefix);
  1087. $view = new \OC\Files\View($rootPath);
  1088. $storage = new Temporary(array());
  1089. \OC\Files\Filesystem::mount($storage, [], '/');
  1090. $this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED));
  1091. $view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE);
  1092. }
  1093. /**
  1094. * Writing a file that's being downloaded
  1095. *
  1096. * @dataProvider dataLockPaths
  1097. *
  1098. * @param string $rootPath
  1099. * @param string $pathPrefix
  1100. */
  1101. public function testWriteToReadUnlockableFile($rootPath, $pathPrefix) {
  1102. $rootPath = str_replace('{folder}', 'files_encryption', $rootPath);
  1103. $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix);
  1104. $view = new \OC\Files\View($rootPath);
  1105. $storage = new Temporary(array());
  1106. \OC\Files\Filesystem::mount($storage, [], '/');
  1107. $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED));
  1108. $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
  1109. }
  1110. /**
  1111. * Test that locks are on mount point paths instead of mount root
  1112. */
  1113. public function testLockLocalMountPointPathInsteadOfStorageRoot() {
  1114. $lockingProvider = \OC::$server->getLockingProvider();
  1115. $view = new \OC\Files\View('/testuser/files/');
  1116. $storage = new Temporary([]);
  1117. \OC\Files\Filesystem::mount($storage, [], '/');
  1118. $mountedStorage = new Temporary([]);
  1119. \OC\Files\Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint');
  1120. $this->assertTrue(
  1121. $view->lockFile('/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, true),
  1122. 'Can lock mount point'
  1123. );
  1124. // no exception here because storage root was not locked
  1125. $mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
  1126. $thrown = false;
  1127. try {
  1128. $storage->acquireLock('/testuser/files/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
  1129. } catch (\OCP\Lock\LockedException $e) {
  1130. $thrown = true;
  1131. }
  1132. $this->assertTrue($thrown, 'Mount point path was locked on root storage');
  1133. $lockingProvider->releaseAll();
  1134. }
  1135. /**
  1136. * Test that locks are on mount point paths and also mount root when requested
  1137. */
  1138. public function testLockStorageRootButNotLocalMountPoint() {
  1139. $lockingProvider = \OC::$server->getLockingProvider();
  1140. $view = new \OC\Files\View('/testuser/files/');
  1141. $storage = new Temporary([]);
  1142. \OC\Files\Filesystem::mount($storage, [], '/');
  1143. $mountedStorage = new Temporary([]);
  1144. \OC\Files\Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint');
  1145. $this->assertTrue(
  1146. $view->lockFile('/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, false),
  1147. 'Can lock mount point'
  1148. );
  1149. $thrown = false;
  1150. try {
  1151. $mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
  1152. } catch (\OCP\Lock\LockedException $e) {
  1153. $thrown = true;
  1154. }
  1155. $this->assertTrue($thrown, 'Mount point storage root was locked on original storage');
  1156. // local mount point was not locked
  1157. $storage->acquireLock('/testuser/files/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
  1158. $lockingProvider->releaseAll();
  1159. }
  1160. /**
  1161. * Test that locks are on mount point paths and also mount root when requested
  1162. */
  1163. public function testLockMountPointPathFailReleasesBoth() {
  1164. $lockingProvider = \OC::$server->getLockingProvider();
  1165. $view = new \OC\Files\View('/testuser/files/');
  1166. $storage = new Temporary([]);
  1167. \OC\Files\Filesystem::mount($storage, [], '/');
  1168. $mountedStorage = new Temporary([]);
  1169. \OC\Files\Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint.txt');
  1170. // this would happen if someone is writing on the mount point
  1171. $mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
  1172. $thrown = false;
  1173. try {
  1174. // this actually acquires two locks, one on the mount point and one on the storage root,
  1175. // but the one on the storage root will fail
  1176. $view->lockFile('/mountpoint.txt', ILockingProvider::LOCK_SHARED);
  1177. } catch (\OCP\Lock\LockedException $e) {
  1178. $thrown = true;
  1179. }
  1180. $this->assertTrue($thrown, 'Cannot acquire shared lock because storage root is already locked');
  1181. // from here we expect that the lock on the local mount point was released properly
  1182. // so acquiring an exclusive lock will succeed
  1183. $storage->acquireLock('/testuser/files/mountpoint.txt', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
  1184. $lockingProvider->releaseAll();
  1185. }
  1186. public function dataLockPaths() {
  1187. return [
  1188. ['/testuser/{folder}', ''],
  1189. ['/testuser', '/{folder}'],
  1190. ['', '/testuser/{folder}'],
  1191. ];
  1192. }
  1193. public function pathRelativeToFilesProvider() {
  1194. return [
  1195. ['admin/files', ''],
  1196. ['admin/files/x', 'x'],
  1197. ['/admin/files', ''],
  1198. ['/admin/files/sub', 'sub'],
  1199. ['/admin/files/sub/', 'sub'],
  1200. ['/admin/files/sub/sub2', 'sub/sub2'],
  1201. ['//admin//files/sub//sub2', 'sub/sub2'],
  1202. ];
  1203. }
  1204. /**
  1205. * @dataProvider pathRelativeToFilesProvider
  1206. */
  1207. public function testGetPathRelativeToFiles($path, $expectedPath) {
  1208. $view = new \OC\Files\View();
  1209. $this->assertEquals($expectedPath, $view->getPathRelativeToFiles($path));
  1210. }
  1211. public function pathRelativeToFilesProviderExceptionCases() {
  1212. return [
  1213. [''],
  1214. ['x'],
  1215. ['files'],
  1216. ['/files'],
  1217. ['/admin/files_versions/abc'],
  1218. ];
  1219. }
  1220. /**
  1221. * @dataProvider pathRelativeToFilesProviderExceptionCases
  1222. * @expectedException \InvalidArgumentException
  1223. */
  1224. public function testGetPathRelativeToFilesWithInvalidArgument($path) {
  1225. $view = new \OC\Files\View();
  1226. $view->getPathRelativeToFiles($path);
  1227. }
  1228. public function testChangeLock() {
  1229. $view = new \OC\Files\View('/testuser/files/');
  1230. $storage = new Temporary(array());
  1231. \OC\Files\Filesystem::mount($storage, [], '/');
  1232. $view->lockFile('/test/sub', ILockingProvider::LOCK_SHARED);
  1233. $this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
  1234. $this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
  1235. $view->changeLock('//test/sub', ILockingProvider::LOCK_EXCLUSIVE);
  1236. $this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
  1237. $view->changeLock('test/sub', ILockingProvider::LOCK_SHARED);
  1238. $this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
  1239. $view->unlockFile('/test/sub/', ILockingProvider::LOCK_SHARED);
  1240. $this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
  1241. $this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
  1242. }
  1243. public function hookPathProvider() {
  1244. return [
  1245. ['/foo/files', '/foo', true],
  1246. ['/foo/files/bar', '/foo', true],
  1247. ['/foo', '/foo', false],
  1248. ['/foo', '/files/foo', true],
  1249. ['/foo', 'filesfoo', false],
  1250. ['', '/foo/files', true],
  1251. ['', '/foo/files/bar.txt', true]
  1252. ];
  1253. }
  1254. /**
  1255. * @dataProvider hookPathProvider
  1256. * @param $root
  1257. * @param $path
  1258. * @param $shouldEmit
  1259. */
  1260. public function testHookPaths($root, $path, $shouldEmit) {
  1261. $filesystemReflection = new \ReflectionClass('\OC\Files\Filesystem');
  1262. $defaultRootValue = $filesystemReflection->getProperty('defaultInstance');
  1263. $defaultRootValue->setAccessible(true);
  1264. $oldRoot = $defaultRootValue->getValue();
  1265. $defaultView = new \OC\Files\View('/foo/files');
  1266. $defaultRootValue->setValue($defaultView);
  1267. $view = new \OC\Files\View($root);
  1268. $result = $this->invokePrivate($view, 'shouldEmitHooks', [$path]);
  1269. $defaultRootValue->setValue($oldRoot);
  1270. $this->assertEquals($shouldEmit, $result);
  1271. }
  1272. /**
  1273. * Create test movable mount points
  1274. *
  1275. * @param array $mountPoints array of mount point locations
  1276. * @return array array of MountPoint objects
  1277. */
  1278. private function createTestMovableMountPoints($mountPoints) {
  1279. $mounts = [];
  1280. foreach ($mountPoints as $mountPoint) {
  1281. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  1282. ->setMethods([])
  1283. ->getMock();
  1284. $mounts[] = $this->getMock(
  1285. '\Test\TestMoveableMountPoint',
  1286. ['moveMount'],
  1287. [$storage, $mountPoint]
  1288. );
  1289. }
  1290. $mountProvider = $this->getMock('\OCP\Files\Config\IMountProvider');
  1291. $mountProvider->expects($this->any())
  1292. ->method('getMountsForUser')
  1293. ->will($this->returnValue($mounts));
  1294. $mountProviderCollection = \OC::$server->getMountProviderCollection();
  1295. $mountProviderCollection->registerProvider($mountProvider);
  1296. return $mounts;
  1297. }
  1298. /**
  1299. * Test mount point move
  1300. */
  1301. public function testMountPointMove() {
  1302. $this->loginAsUser($this->user);
  1303. list($mount1, $mount2) = $this->createTestMovableMountPoints([
  1304. $this->user . '/files/mount1',
  1305. $this->user . '/files/mount2',
  1306. ]);
  1307. $mount1->expects($this->once())
  1308. ->method('moveMount')
  1309. ->will($this->returnValue(true));
  1310. $mount2->expects($this->once())
  1311. ->method('moveMount')
  1312. ->will($this->returnValue(true));
  1313. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1314. $view->mkdir('sub');
  1315. $this->assertTrue($view->rename('mount1', 'renamed_mount'), 'Can rename mount point');
  1316. $this->assertTrue($view->rename('mount2', 'sub/moved_mount'), 'Can move a mount point into a subdirectory');
  1317. }
  1318. /**
  1319. * Test that moving a mount point into another is forbidden
  1320. */
  1321. public function testMoveMountPointIntoAnother() {
  1322. $this->loginAsUser($this->user);
  1323. list($mount1, $mount2) = $this->createTestMovableMountPoints([
  1324. $this->user . '/files/mount1',
  1325. $this->user . '/files/mount2',
  1326. ]);
  1327. $mount1->expects($this->never())
  1328. ->method('moveMount');
  1329. $mount2->expects($this->never())
  1330. ->method('moveMount');
  1331. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1332. $this->assertFalse($view->rename('mount1', 'mount2'), 'Cannot overwrite another mount point');
  1333. $this->assertFalse($view->rename('mount1', 'mount2/sub'), 'Cannot move a mount point into another');
  1334. }
  1335. /**
  1336. * Test that moving a mount point into a shared folder is forbidden
  1337. */
  1338. public function testMoveMountPointIntoSharedFolder() {
  1339. $this->loginAsUser($this->user);
  1340. list($mount1) = $this->createTestMovableMountPoints([
  1341. $this->user . '/files/mount1',
  1342. ]);
  1343. $mount1->expects($this->never())
  1344. ->method('moveMount');
  1345. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1346. $view->mkdir('shareddir');
  1347. $view->mkdir('shareddir/sub');
  1348. $view->mkdir('shareddir/sub2');
  1349. $fileId = $view->getFileInfo('shareddir')->getId();
  1350. $userObject = \OC::$server->getUserManager()->createUser('test2', 'IHateNonMockableStaticClasses');
  1351. $this->assertTrue(\OCP\Share::shareItem('folder', $fileId, \OCP\Share::SHARE_TYPE_USER, 'test2', \OCP\Constants::PERMISSION_READ));
  1352. $this->assertFalse($view->rename('mount1', 'shareddir'), 'Cannot overwrite shared folder');
  1353. $this->assertFalse($view->rename('mount1', 'shareddir/sub'), 'Cannot move mount point into shared folder');
  1354. $this->assertFalse($view->rename('mount1', 'shareddir/sub/sub2'), 'Cannot move mount point into shared subfolder');
  1355. $this->assertTrue(\OCP\Share::unshare('folder', $fileId, \OCP\Share::SHARE_TYPE_USER, 'test2'));
  1356. $userObject->delete();
  1357. }
  1358. public function basicOperationProviderForLocks() {
  1359. return [
  1360. // --- write hook ----
  1361. [
  1362. 'touch',
  1363. ['touch-create.txt'],
  1364. 'touch-create.txt',
  1365. 'create',
  1366. ILockingProvider::LOCK_SHARED,
  1367. ILockingProvider::LOCK_EXCLUSIVE,
  1368. ILockingProvider::LOCK_SHARED,
  1369. ],
  1370. [
  1371. 'fopen',
  1372. ['test-write.txt', 'w'],
  1373. 'test-write.txt',
  1374. 'write',
  1375. ILockingProvider::LOCK_SHARED,
  1376. ILockingProvider::LOCK_EXCLUSIVE,
  1377. null,
  1378. // exclusive lock stays until fclose
  1379. ILockingProvider::LOCK_EXCLUSIVE,
  1380. ],
  1381. [
  1382. 'mkdir',
  1383. ['newdir'],
  1384. 'newdir',
  1385. 'write',
  1386. ILockingProvider::LOCK_SHARED,
  1387. ILockingProvider::LOCK_EXCLUSIVE,
  1388. ILockingProvider::LOCK_SHARED,
  1389. ],
  1390. [
  1391. 'file_put_contents',
  1392. ['file_put_contents.txt', 'blah'],
  1393. 'file_put_contents.txt',
  1394. 'write',
  1395. ILockingProvider::LOCK_SHARED,
  1396. ILockingProvider::LOCK_EXCLUSIVE,
  1397. ILockingProvider::LOCK_SHARED,
  1398. ],
  1399. // ---- delete hook ----
  1400. [
  1401. 'rmdir',
  1402. ['dir'],
  1403. 'dir',
  1404. 'delete',
  1405. ILockingProvider::LOCK_SHARED,
  1406. ILockingProvider::LOCK_EXCLUSIVE,
  1407. ILockingProvider::LOCK_SHARED,
  1408. ],
  1409. [
  1410. 'unlink',
  1411. ['test.txt'],
  1412. 'test.txt',
  1413. 'delete',
  1414. ILockingProvider::LOCK_SHARED,
  1415. ILockingProvider::LOCK_EXCLUSIVE,
  1416. ILockingProvider::LOCK_SHARED,
  1417. ],
  1418. // ---- read hook (no post hooks) ----
  1419. [
  1420. 'file_get_contents',
  1421. ['test.txt'],
  1422. 'test.txt',
  1423. 'read',
  1424. ILockingProvider::LOCK_SHARED,
  1425. ILockingProvider::LOCK_SHARED,
  1426. null,
  1427. ],
  1428. [
  1429. 'fopen',
  1430. ['test.txt', 'r'],
  1431. 'test.txt',
  1432. 'read',
  1433. ILockingProvider::LOCK_SHARED,
  1434. ILockingProvider::LOCK_SHARED,
  1435. null,
  1436. ],
  1437. [
  1438. 'opendir',
  1439. ['dir'],
  1440. 'dir',
  1441. 'read',
  1442. ILockingProvider::LOCK_SHARED,
  1443. ILockingProvider::LOCK_SHARED,
  1444. null,
  1445. ],
  1446. // ---- no lock, touch hook ---
  1447. ['touch', ['test.txt'], 'test.txt', 'touch', null, null, null],
  1448. // ---- no hooks, no locks ---
  1449. ['is_dir', ['dir'], 'dir', null],
  1450. ['is_file', ['dir'], 'dir', null],
  1451. ['stat', ['dir'], 'dir', null],
  1452. ['filetype', ['dir'], 'dir', null],
  1453. ['filesize', ['dir'], 'dir', null],
  1454. ['isCreatable', ['dir'], 'dir', null],
  1455. ['isReadable', ['dir'], 'dir', null],
  1456. ['isUpdatable', ['dir'], 'dir', null],
  1457. ['isDeletable', ['dir'], 'dir', null],
  1458. ['isSharable', ['dir'], 'dir', null],
  1459. ['file_exists', ['dir'], 'dir', null],
  1460. ['filemtime', ['dir'], 'dir', null],
  1461. ];
  1462. }
  1463. /**
  1464. * Test whether locks are set before and after the operation
  1465. *
  1466. * @dataProvider basicOperationProviderForLocks
  1467. *
  1468. * @param string $operation operation name on the view
  1469. * @param array $operationArgs arguments for the operation
  1470. * @param string $lockedPath path of the locked item to check
  1471. * @param string $hookType hook type
  1472. * @param int $expectedLockBefore expected lock during pre hooks
  1473. * @param int $expectedLockduring expected lock during operation
  1474. * @param int $expectedLockAfter expected lock during post hooks
  1475. * @param int $expectedStrayLock expected lock after returning, should
  1476. * be null (unlock) for most operations
  1477. */
  1478. public function testLockBasicOperation(
  1479. $operation,
  1480. $operationArgs,
  1481. $lockedPath,
  1482. $hookType,
  1483. $expectedLockBefore = ILockingProvider::LOCK_SHARED,
  1484. $expectedLockDuring = ILockingProvider::LOCK_SHARED,
  1485. $expectedLockAfter = ILockingProvider::LOCK_SHARED,
  1486. $expectedStrayLock = null
  1487. ) {
  1488. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1489. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  1490. ->setMethods([$operation])
  1491. ->getMock();
  1492. \OC\Files\Filesystem::mount($storage, array(), $this->user . '/');
  1493. // work directly on disk because mkdir might be mocked
  1494. $realPath = $storage->getSourcePath('');
  1495. mkdir($realPath . '/files');
  1496. mkdir($realPath . '/files/dir');
  1497. file_put_contents($realPath . '/files/test.txt', 'blah');
  1498. $storage->getScanner()->scan('files');
  1499. $storage->expects($this->once())
  1500. ->method($operation)
  1501. ->will($this->returnCallback(
  1502. function () use ($view, $lockedPath, &$lockTypeDuring) {
  1503. $lockTypeDuring = $this->getFileLockType($view, $lockedPath);
  1504. return true;
  1505. }
  1506. ));
  1507. $this->assertNull($this->getFileLockType($view, $lockedPath), 'File not locked before operation');
  1508. $this->connectMockHooks($hookType, $view, $lockedPath, $lockTypePre, $lockTypePost);
  1509. // do operation
  1510. call_user_func_array(array($view, $operation), $operationArgs);
  1511. if ($hookType !== null) {
  1512. $this->assertEquals($expectedLockBefore, $lockTypePre, 'File locked properly during pre-hook');
  1513. $this->assertEquals($expectedLockAfter, $lockTypePost, 'File locked properly during post-hook');
  1514. $this->assertEquals($expectedLockDuring, $lockTypeDuring, 'File locked properly during operation');
  1515. } else {
  1516. $this->assertNull($lockTypeDuring, 'File not locked during operation');
  1517. }
  1518. $this->assertEquals($expectedStrayLock, $this->getFileLockType($view, $lockedPath));
  1519. }
  1520. /**
  1521. * Test locks for file_put_content with stream.
  1522. * This code path uses $storage->fopen instead
  1523. */
  1524. public function testLockFilePutContentWithStream() {
  1525. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1526. $path = 'test_file_put_contents.txt';
  1527. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  1528. ->setMethods(['fopen'])
  1529. ->getMock();
  1530. \OC\Files\Filesystem::mount($storage, array(), $this->user . '/');
  1531. $storage->mkdir('files');
  1532. $storage->expects($this->once())
  1533. ->method('fopen')
  1534. ->will($this->returnCallback(
  1535. function () use ($view, $path, &$lockTypeDuring) {
  1536. $lockTypeDuring = $this->getFileLockType($view, $path);
  1537. return fopen('php://temp', 'r+');
  1538. }
  1539. ));
  1540. $this->connectMockHooks('write', $view, $path, $lockTypePre, $lockTypePost);
  1541. $this->assertNull($this->getFileLockType($view, $path), 'File not locked before operation');
  1542. // do operation
  1543. $view->file_put_contents($path, fopen('php://temp', 'r+'));
  1544. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePre, 'File locked properly during pre-hook');
  1545. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePost, 'File locked properly during post-hook');
  1546. $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File locked properly during operation');
  1547. $this->assertNull($this->getFileLockType($view, $path));
  1548. }
  1549. /**
  1550. * Test locks for fopen with fclose at the end
  1551. */
  1552. public function testLockFopen() {
  1553. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1554. $path = 'test_file_put_contents.txt';
  1555. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  1556. ->setMethods(['fopen'])
  1557. ->getMock();
  1558. \OC\Files\Filesystem::mount($storage, array(), $this->user . '/');
  1559. $storage->mkdir('files');
  1560. $storage->expects($this->once())
  1561. ->method('fopen')
  1562. ->will($this->returnCallback(
  1563. function () use ($view, $path, &$lockTypeDuring) {
  1564. $lockTypeDuring = $this->getFileLockType($view, $path);
  1565. return fopen('php://temp', 'r+');
  1566. }
  1567. ));
  1568. $this->connectMockHooks('write', $view, $path, $lockTypePre, $lockTypePost);
  1569. $this->assertNull($this->getFileLockType($view, $path), 'File not locked before operation');
  1570. // do operation
  1571. $res = $view->fopen($path, 'w');
  1572. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePre, 'File locked properly during pre-hook');
  1573. $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File locked properly during operation');
  1574. $this->assertNull($lockTypePost, 'No post hook, no lock check possible');
  1575. $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File still locked after fopen');
  1576. fclose($res);
  1577. $this->assertNull($this->getFileLockType($view, $path), 'File unlocked after fclose');
  1578. }
  1579. /**
  1580. * Test locks for fopen with fclose at the end
  1581. *
  1582. * @dataProvider basicOperationProviderForLocks
  1583. *
  1584. * @param string $operation operation name on the view
  1585. * @param array $operationArgs arguments for the operation
  1586. * @param string $path path of the locked item to check
  1587. */
  1588. public function testLockBasicOperationUnlocksAfterException(
  1589. $operation,
  1590. $operationArgs,
  1591. $path
  1592. ) {
  1593. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1594. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  1595. ->setMethods([$operation])
  1596. ->getMock();
  1597. \OC\Files\Filesystem::mount($storage, array(), $this->user . '/');
  1598. // work directly on disk because mkdir might be mocked
  1599. $realPath = $storage->getSourcePath('');
  1600. mkdir($realPath . '/files');
  1601. mkdir($realPath . '/files/dir');
  1602. file_put_contents($realPath . '/files/test.txt', 'blah');
  1603. $storage->getScanner()->scan('files');
  1604. $storage->expects($this->once())
  1605. ->method($operation)
  1606. ->will($this->returnCallback(
  1607. function () {
  1608. throw new \Exception('Simulated exception');
  1609. }
  1610. ));
  1611. $thrown = false;
  1612. try {
  1613. call_user_func_array(array($view, $operation), $operationArgs);
  1614. } catch (\Exception $e) {
  1615. $thrown = true;
  1616. $this->assertEquals('Simulated exception', $e->getMessage());
  1617. }
  1618. $this->assertTrue($thrown, 'Exception was rethrown');
  1619. $this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception');
  1620. }
  1621. /**
  1622. * Test locks for fopen with fclose at the end
  1623. *
  1624. * @dataProvider basicOperationProviderForLocks
  1625. *
  1626. * @param string $operation operation name on the view
  1627. * @param array $operationArgs arguments for the operation
  1628. * @param string $path path of the locked item to check
  1629. * @param string $hookType hook type
  1630. */
  1631. public function testLockBasicOperationUnlocksAfterCancelledHook(
  1632. $operation,
  1633. $operationArgs,
  1634. $path,
  1635. $hookType
  1636. ) {
  1637. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1638. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  1639. ->setMethods([$operation])
  1640. ->getMock();
  1641. \OC\Files\Filesystem::mount($storage, array(), $this->user . '/');
  1642. $storage->mkdir('files');
  1643. \OCP\Util::connectHook(
  1644. \OC\Files\Filesystem::CLASSNAME,
  1645. $hookType,
  1646. '\Test\HookHelper',
  1647. 'cancellingCallback'
  1648. );
  1649. call_user_func_array(array($view, $operation), $operationArgs);
  1650. $this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception');
  1651. }
  1652. public function lockFileRenameOrCopyDataProvider() {
  1653. return [
  1654. ['rename', ILockingProvider::LOCK_EXCLUSIVE],
  1655. ['copy', ILockingProvider::LOCK_SHARED],
  1656. ];
  1657. }
  1658. /**
  1659. * Test locks for rename or copy operation
  1660. *
  1661. * @dataProvider lockFileRenameOrCopyDataProvider
  1662. *
  1663. * @param string $operation operation to be done on the view
  1664. * @param int $expectedLockTypeSourceDuring expected lock type on source file during
  1665. * the operation
  1666. */
  1667. public function testLockFileRename($operation, $expectedLockTypeSourceDuring) {
  1668. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1669. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  1670. ->setMethods([$operation, 'filemtime'])
  1671. ->getMock();
  1672. $storage->expects($this->any())
  1673. ->method('filemtime')
  1674. ->will($this->returnValue(123456789));
  1675. $sourcePath = 'original.txt';
  1676. $targetPath = 'target.txt';
  1677. \OC\Files\Filesystem::mount($storage, array(), $this->user . '/');
  1678. $storage->mkdir('files');
  1679. $view->file_put_contents($sourcePath, 'meh');
  1680. $storage->expects($this->once())
  1681. ->method($operation)
  1682. ->will($this->returnCallback(
  1683. function () use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
  1684. $lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath);
  1685. $lockTypeTargetDuring = $this->getFileLockType($view, $targetPath);
  1686. return true;
  1687. }
  1688. ));
  1689. $this->connectMockHooks($operation, $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
  1690. $this->connectMockHooks($operation, $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
  1691. $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
  1692. $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
  1693. $view->$operation($sourcePath, $targetPath);
  1694. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source file locked properly during pre-hook');
  1695. $this->assertEquals($expectedLockTypeSourceDuring, $lockTypeSourceDuring, 'Source file locked properly during operation');
  1696. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source file locked properly during post-hook');
  1697. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target file locked properly during pre-hook');
  1698. $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target file locked properly during operation');
  1699. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target file locked properly during post-hook');
  1700. $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
  1701. $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
  1702. }
  1703. /**
  1704. * simulate a failed copy operation.
  1705. * We expect that we catch the exception, free the lock and re-throw it.
  1706. *
  1707. * @expectedException \Exception
  1708. */
  1709. public function testLockFileCopyException() {
  1710. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1711. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  1712. ->setMethods(['copy'])
  1713. ->getMock();
  1714. $sourcePath = 'original.txt';
  1715. $targetPath = 'target.txt';
  1716. \OC\Files\Filesystem::mount($storage, array(), $this->user . '/');
  1717. $storage->mkdir('files');
  1718. $view->file_put_contents($sourcePath, 'meh');
  1719. $storage->expects($this->once())
  1720. ->method('copy')
  1721. ->will($this->returnCallback(
  1722. function () {
  1723. throw new \Exception();
  1724. }
  1725. ));
  1726. $this->connectMockHooks('copy', $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
  1727. $this->connectMockHooks('copy', $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
  1728. $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
  1729. $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
  1730. try {
  1731. $view->copy($sourcePath, $targetPath);
  1732. } catch (\Exception $e) {
  1733. $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
  1734. $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
  1735. throw $e;
  1736. }
  1737. }
  1738. /**
  1739. * Test rename operation: unlock first path when second path was locked
  1740. */
  1741. public function testLockFileRenameUnlockOnException() {
  1742. $this->loginAsUser('test');
  1743. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1744. $sourcePath = 'original.txt';
  1745. $targetPath = 'target.txt';
  1746. $view->file_put_contents($sourcePath, 'meh');
  1747. // simulate that the target path is already locked
  1748. $view->lockFile($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
  1749. $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
  1750. $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $this->getFileLockType($view, $targetPath), 'Target file is locked before operation');
  1751. $thrown = false;
  1752. try {
  1753. $view->rename($sourcePath, $targetPath);
  1754. } catch (\OCP\Lock\LockedException $e) {
  1755. $thrown = true;
  1756. }
  1757. $this->assertTrue($thrown, 'LockedException thrown');
  1758. $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
  1759. $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $this->getFileLockType($view, $targetPath), 'Target file still locked after operation');
  1760. $view->unlockFile($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
  1761. }
  1762. /**
  1763. * Test rename operation: unlock first path when second path was locked
  1764. */
  1765. public function testGetOwner() {
  1766. $this->loginAsUser('test');
  1767. $view = new \OC\Files\View('/test/files/');
  1768. $path = 'foo.txt';
  1769. $view->file_put_contents($path, 'meh');
  1770. $this->assertEquals('test', $view->getFileInfo($path)->getOwner()->getUID());
  1771. $folderInfo = $view->getDirectoryContent('');
  1772. $folderInfo = array_values(array_filter($folderInfo, function (FileInfo $info) {
  1773. return $info->getName() === 'foo.txt';
  1774. }));
  1775. $this->assertEquals('test', $folderInfo[0]->getOwner()->getUID());
  1776. $subStorage = new Temporary();
  1777. \OC\Files\Filesystem::mount($subStorage, [], '/test/files/asd');
  1778. $folderInfo = $view->getDirectoryContent('');
  1779. $folderInfo = array_values(array_filter($folderInfo, function (FileInfo $info) {
  1780. return $info->getName() === 'asd';
  1781. }));
  1782. $this->assertEquals('test', $folderInfo[0]->getOwner()->getUID());
  1783. }
  1784. public function lockFileRenameOrCopyCrossStorageDataProvider() {
  1785. return [
  1786. ['rename', 'moveFromStorage', ILockingProvider::LOCK_EXCLUSIVE],
  1787. ['copy', 'copyFromStorage', ILockingProvider::LOCK_SHARED],
  1788. ];
  1789. }
  1790. /**
  1791. * Test locks for rename or copy operation cross-storage
  1792. *
  1793. * @dataProvider lockFileRenameOrCopyCrossStorageDataProvider
  1794. *
  1795. * @param string $viewOperation operation to be done on the view
  1796. * @param string $storageOperation operation to be mocked on the storage
  1797. * @param int $expectedLockTypeSourceDuring expected lock type on source file during
  1798. * the operation
  1799. */
  1800. public function testLockFileRenameCrossStorage($viewOperation, $storageOperation, $expectedLockTypeSourceDuring) {
  1801. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1802. $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  1803. ->setMethods([$storageOperation])
  1804. ->getMock();
  1805. $storage2 = $this->getMockBuilder('\OC\Files\Storage\Temporary')
  1806. ->setMethods([$storageOperation, 'filemtime'])
  1807. ->getMock();
  1808. $storage2->expects($this->any())
  1809. ->method('filemtime')
  1810. ->will($this->returnValue(123456789));
  1811. $sourcePath = 'original.txt';
  1812. $targetPath = 'substorage/target.txt';
  1813. \OC\Files\Filesystem::mount($storage, array(), $this->user . '/');
  1814. \OC\Files\Filesystem::mount($storage2, array(), $this->user . '/files/substorage');
  1815. $storage->mkdir('files');
  1816. $view->file_put_contents($sourcePath, 'meh');
  1817. $storage->expects($this->never())
  1818. ->method($storageOperation);
  1819. $storage2->expects($this->once())
  1820. ->method($storageOperation)
  1821. ->will($this->returnCallback(
  1822. function () use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
  1823. $lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath);
  1824. $lockTypeTargetDuring = $this->getFileLockType($view, $targetPath);
  1825. return true;
  1826. }
  1827. ));
  1828. $this->connectMockHooks($viewOperation, $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
  1829. $this->connectMockHooks($viewOperation, $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
  1830. $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
  1831. $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
  1832. $view->$viewOperation($sourcePath, $targetPath);
  1833. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source file locked properly during pre-hook');
  1834. $this->assertEquals($expectedLockTypeSourceDuring, $lockTypeSourceDuring, 'Source file locked properly during operation');
  1835. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source file locked properly during post-hook');
  1836. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target file locked properly during pre-hook');
  1837. $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target file locked properly during operation');
  1838. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target file locked properly during post-hook');
  1839. $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
  1840. $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
  1841. }
  1842. /**
  1843. * Test locks when moving a mount point
  1844. */
  1845. public function testLockMoveMountPoint() {
  1846. $this->loginAsUser('test');
  1847. list($mount) = $this->createTestMovableMountPoints([
  1848. $this->user . '/files/substorage',
  1849. ]);
  1850. $view = new \OC\Files\View('/' . $this->user . '/files/');
  1851. $view->mkdir('subdir');
  1852. $sourcePath = 'substorage';
  1853. $targetPath = 'subdir/substorage_moved';
  1854. $mount->expects($this->once())
  1855. ->method('moveMount')
  1856. ->will($this->returnCallback(
  1857. function ($target) use ($mount, $view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring, &$lockTypeSharedRootDuring) {
  1858. $lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath, true);
  1859. $lockTypeTargetDuring = $this->getFileLockType($view, $targetPath, true);
  1860. $lockTypeSharedRootDuring = $this->getFileLockType($view, $sourcePath, false);
  1861. $mount->setMountPoint($target);
  1862. return true;
  1863. }
  1864. ));
  1865. $this->connectMockHooks('rename', $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost, true);
  1866. $this->connectMockHooks('rename', $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost, true);
  1867. // in pre-hook, mount point is still on $sourcePath
  1868. $this->connectMockHooks('rename', $view, $sourcePath, $lockTypeSharedRootPre, $dummy, false);
  1869. // in post-hook, mount point is now on $targetPath
  1870. $this->connectMockHooks('rename', $view, $targetPath, $dummy, $lockTypeSharedRootPost, false);
  1871. $this->assertNull($this->getFileLockType($view, $sourcePath, false), 'Shared storage root not locked before operation');
  1872. $this->assertNull($this->getFileLockType($view, $sourcePath, true), 'Source path not locked before operation');
  1873. $this->assertNull($this->getFileLockType($view, $targetPath, true), 'Target path not locked before operation');
  1874. $view->rename($sourcePath, $targetPath);
  1875. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source path locked properly during pre-hook');
  1876. $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeSourceDuring, 'Source path locked properly during operation');
  1877. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source path locked properly during post-hook');
  1878. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target path locked properly during pre-hook');
  1879. $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target path locked properly during operation');
  1880. $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target path locked properly during post-hook');
  1881. $this->assertNull($lockTypeSharedRootPre, 'Shared storage root not locked during pre-hook');
  1882. $this->assertNull($lockTypeSharedRootDuring, 'Shared storage root not locked during move');
  1883. $this->assertNull($lockTypeSharedRootPost, 'Shared storage root not locked during post-hook');
  1884. $this->assertNull($this->getFileLockType($view, $sourcePath, false), 'Shared storage root not locked after operation');
  1885. $this->assertNull($this->getFileLockType($view, $sourcePath, true), 'Source path not locked after operation');
  1886. $this->assertNull($this->getFileLockType($view, $targetPath, true), 'Target path not locked after operation');
  1887. }
  1888. /**
  1889. * Connect hook callbacks for hook type
  1890. *
  1891. * @param string $hookType hook type or null for none
  1892. * @param \OC\Files\View $view view to check the lock on
  1893. * @param string $path path for which to check the lock
  1894. * @param int $lockTypePre variable to receive lock type that was active in the pre-hook
  1895. * @param int $lockTypePost variable to receive lock type that was active in the post-hook
  1896. * @param bool $onMountPoint true to check the mount point instead of the
  1897. * mounted storage
  1898. */
  1899. private function connectMockHooks($hookType, $view, $path, &$lockTypePre, &$lockTypePost, $onMountPoint = false) {
  1900. if ($hookType === null) {
  1901. return;
  1902. }
  1903. $eventHandler = $this->getMockBuilder('\stdclass')
  1904. ->setMethods(['preCallback', 'postCallback'])
  1905. ->getMock();
  1906. $eventHandler->expects($this->any())
  1907. ->method('preCallback')
  1908. ->will($this->returnCallback(
  1909. function () use ($view, $path, $onMountPoint, &$lockTypePre) {
  1910. $lockTypePre = $this->getFileLockType($view, $path, $onMountPoint);
  1911. }
  1912. ));
  1913. $eventHandler->expects($this->any())
  1914. ->method('postCallback')
  1915. ->will($this->returnCallback(
  1916. function () use ($view, $path, $onMountPoint, &$lockTypePost) {
  1917. $lockTypePost = $this->getFileLockType($view, $path, $onMountPoint);
  1918. }
  1919. ));
  1920. if ($hookType !== null) {
  1921. \OCP\Util::connectHook(
  1922. \OC\Files\Filesystem::CLASSNAME,
  1923. $hookType,
  1924. $eventHandler,
  1925. 'preCallback'
  1926. );
  1927. \OCP\Util::connectHook(
  1928. \OC\Files\Filesystem::CLASSNAME,
  1929. 'post_' . $hookType,
  1930. $eventHandler,
  1931. 'postCallback'
  1932. );
  1933. }
  1934. }
  1935. /**
  1936. * Returns the file lock type
  1937. *
  1938. * @param \OC\Files\View $view view
  1939. * @param string $path path
  1940. * @param bool $onMountPoint true to check the mount point instead of the
  1941. * mounted storage
  1942. *
  1943. * @return int lock type or null if file was not locked
  1944. */
  1945. private function getFileLockType(\OC\Files\View $view, $path, $onMountPoint = false) {
  1946. if ($this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE, $onMountPoint)) {
  1947. return ILockingProvider::LOCK_EXCLUSIVE;
  1948. } else if ($this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED, $onMountPoint)) {
  1949. return ILockingProvider::LOCK_SHARED;
  1950. }
  1951. return null;
  1952. }
  1953. public function testRemoveMoveableMountPoint() {
  1954. $mountPoint = '/' . $this->user . '/files/mount/';
  1955. // Mock the mount point
  1956. $mount = $this->getMockBuilder('\Test\TestMoveableMountPoint')
  1957. ->disableOriginalConstructor()
  1958. ->getMock();
  1959. $mount->expects($this->once())
  1960. ->method('getMountPoint')
  1961. ->willReturn($mountPoint);
  1962. $mount->expects($this->once())
  1963. ->method('removeMount')
  1964. ->willReturn('foo');
  1965. $mount->expects($this->any())
  1966. ->method('getInternalPath')
  1967. ->willReturn('');
  1968. // Register mount
  1969. \OC\Files\Filesystem::getMountManager()->addMount($mount);
  1970. // Listen for events
  1971. $eventHandler = $this->getMockBuilder('\stdclass')
  1972. ->setMethods(['umount', 'post_umount'])
  1973. ->getMock();
  1974. $eventHandler->expects($this->once())
  1975. ->method('umount')
  1976. ->with([\OC\Files\Filesystem::signal_param_path => '/mount']);
  1977. $eventHandler->expects($this->once())
  1978. ->method('post_umount')
  1979. ->with([\OC\Files\Filesystem::signal_param_path => '/mount']);
  1980. \OCP\Util::connectHook(
  1981. \OC\Files\Filesystem::CLASSNAME,
  1982. 'umount',
  1983. $eventHandler,
  1984. 'umount'
  1985. );
  1986. \OCP\Util::connectHook(
  1987. \OC\Files\Filesystem::CLASSNAME,
  1988. 'post_umount',
  1989. $eventHandler,
  1990. 'post_umount'
  1991. );
  1992. //Delete the mountpoint
  1993. $view = new \OC\Files\View('/' . $this->user . '/files');
  1994. $this->assertEquals('foo', $view->rmdir('mount'));
  1995. }
  1996. public function mimeFilterProvider() {
  1997. return [
  1998. [null, ['test1.txt', 'test2.txt', 'test3.md', 'test4.png']],
  1999. ['text/plain', ['test1.txt', 'test2.txt']],
  2000. ['text/markdown', ['test3.md']],
  2001. ['text', ['test1.txt', 'test2.txt', 'test3.md']],
  2002. ];
  2003. }
  2004. /**
  2005. * @param string $filter
  2006. * @param string[] $expected
  2007. * @dataProvider mimeFilterProvider
  2008. */
  2009. public function testGetDirectoryContentMimeFilter($filter, $expected) {
  2010. $storage1 = new Temporary();
  2011. $root = $this->getUniqueID('/');
  2012. \OC\Files\Filesystem::mount($storage1, array(), $root . '/');
  2013. $view = new \OC\Files\View($root);
  2014. $view->file_put_contents('test1.txt', 'asd');
  2015. $view->file_put_contents('test2.txt', 'asd');
  2016. $view->file_put_contents('test3.md', 'asd');
  2017. $view->file_put_contents('test4.png', '');
  2018. $content = $view->getDirectoryContent('', $filter);
  2019. $files = array_map(function (FileInfo $info) {
  2020. return $info->getName();
  2021. }, $content);
  2022. sort($files);
  2023. $this->assertEquals($expected, $files);
  2024. }
  2025. public function testFilePutContentsClearsChecksum() {
  2026. $storage = new Temporary(array());
  2027. $scanner = $storage->getScanner();
  2028. $storage->file_put_contents('foo.txt', 'bar');
  2029. \OC\Files\Filesystem::mount($storage, array(), '/test/');
  2030. $scanner->scan('');
  2031. $view = new \OC\Files\View('/test/foo.txt');
  2032. $view->putFileInfo('.', ['checksum' => '42']);
  2033. $this->assertEquals('bar', $view->file_get_contents(''));
  2034. $fh = tmpfile();
  2035. fwrite($fh, 'fooo');
  2036. rewind($fh);
  2037. $view->file_put_contents('', $fh);
  2038. $this->assertEquals('fooo', $view->file_get_contents(''));
  2039. $data = $view->getFileInfo('.');
  2040. $this->assertEquals('', $data->getChecksum());
  2041. }
  2042. public function testDeleteGhostFile() {
  2043. $storage = new Temporary(array());
  2044. $scanner = $storage->getScanner();
  2045. $cache = $storage->getCache();
  2046. $storage->file_put_contents('foo.txt', 'bar');
  2047. \OC\Files\Filesystem::mount($storage, array(), '/test/');
  2048. $scanner->scan('');
  2049. $storage->unlink('foo.txt');
  2050. $this->assertTrue($cache->inCache('foo.txt'));
  2051. $view = new \OC\Files\View('/test');
  2052. $rootInfo = $view->getFileInfo('');
  2053. $this->assertEquals(3, $rootInfo->getSize());
  2054. $view->unlink('foo.txt');
  2055. $newInfo = $view->getFileInfo('');
  2056. $this->assertFalse($cache->inCache('foo.txt'));
  2057. $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag());
  2058. $this->assertEquals(0, $newInfo->getSize());
  2059. }
  2060. public function testDeleteGhostFolder() {
  2061. $storage = new Temporary(array());
  2062. $scanner = $storage->getScanner();
  2063. $cache = $storage->getCache();
  2064. $storage->mkdir('foo');
  2065. $storage->file_put_contents('foo/foo.txt', 'bar');
  2066. \OC\Files\Filesystem::mount($storage, array(), '/test/');
  2067. $scanner->scan('');
  2068. $storage->rmdir('foo');
  2069. $this->assertTrue($cache->inCache('foo'));
  2070. $this->assertTrue($cache->inCache('foo/foo.txt'));
  2071. $view = new \OC\Files\View('/test');
  2072. $rootInfo = $view->getFileInfo('');
  2073. $this->assertEquals(3, $rootInfo->getSize());
  2074. $view->rmdir('foo');
  2075. $newInfo = $view->getFileInfo('');
  2076. $this->assertFalse($cache->inCache('foo'));
  2077. $this->assertFalse($cache->inCache('foo/foo.txt'));
  2078. $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag());
  2079. $this->assertEquals(0, $newInfo->getSize());
  2080. }
  2081. }