ViewTest.php 82 KB

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