ViewTest.php 82 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496
  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. }