mappedlocal.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  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. */
  8. namespace OC\Files\Storage;
  9. /**
  10. * for local filestore, we only have to map the paths
  11. */
  12. class MappedLocal extends \OC\Files\Storage\Common {
  13. protected $datadir;
  14. private $mapper;
  15. public function __construct($arguments) {
  16. $this->datadir = $arguments['datadir'];
  17. if (substr($this->datadir, -1) !== '/') {
  18. $this->datadir .= '/';
  19. }
  20. $this->mapper = new \OC\Files\Mapper($this->datadir);
  21. }
  22. public function __destruct() {
  23. }
  24. public function getId() {
  25. return 'local::' . $this->datadir;
  26. }
  27. public function mkdir($path) {
  28. return @mkdir($this->getSourcePath($path), 0777, true);
  29. }
  30. public function rmdir($path) {
  31. if (!$this->isDeletable($path)) {
  32. return false;
  33. }
  34. try {
  35. $it = new \RecursiveIteratorIterator(
  36. new \RecursiveDirectoryIterator($this->getSourcePath($path)),
  37. \RecursiveIteratorIterator::CHILD_FIRST
  38. );
  39. /**
  40. * RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
  41. * This bug is fixed in PHP 5.5.9 or before
  42. * See #8376
  43. */
  44. $it->rewind();
  45. while ($it->valid()) {
  46. /**
  47. * @var \SplFileInfo $file
  48. */
  49. $file = $it->current();
  50. if (in_array($file->getBasename(), array('.', '..'))) {
  51. $it->next();
  52. continue;
  53. } elseif ($file->isDir()) {
  54. rmdir($file->getPathname());
  55. } elseif ($file->isFile() || $file->isLink()) {
  56. unlink($file->getPathname());
  57. }
  58. $it->next();
  59. }
  60. if ($result = @rmdir($this->getSourcePath($path))) {
  61. $this->cleanMapper($path);
  62. }
  63. return $result;
  64. } catch (\UnexpectedValueException $e) {
  65. return false;
  66. }
  67. }
  68. public function opendir($path) {
  69. $files = array('.', '..');
  70. $physicalPath = $this->getSourcePath($path);
  71. $logicalPath = $this->mapper->physicalToLogic($physicalPath);
  72. $dh = opendir($physicalPath);
  73. if (is_resource($dh)) {
  74. while (($file = readdir($dh)) !== false) {
  75. if ($file === '.' or $file === '..') {
  76. continue;
  77. }
  78. $logicalFilePath = $this->mapper->physicalToLogic($physicalPath . '/' . $file);
  79. $file = $this->mapper->stripRootFolder($logicalFilePath, $logicalPath);
  80. $file = $this->stripLeading($file);
  81. $files[] = $file;
  82. }
  83. }
  84. \OC\Files\Stream\Dir::register('local-win32' . $path, $files);
  85. return opendir('fakedir://local-win32' . $path);
  86. }
  87. public function is_dir($path) {
  88. if (substr($path, -1) == '/') {
  89. $path = substr($path, 0, -1);
  90. }
  91. return is_dir($this->getSourcePath($path));
  92. }
  93. public function is_file($path) {
  94. return is_file($this->getSourcePath($path));
  95. }
  96. public function stat($path) {
  97. clearstatcache();
  98. $fullPath = $this->getSourcePath($path);
  99. $statResult = stat($fullPath);
  100. if (PHP_INT_SIZE === 4 && !$this->is_dir($path)) {
  101. $filesize = $this->filesize($path);
  102. $statResult['size'] = $filesize;
  103. $statResult[7] = $filesize;
  104. }
  105. return $statResult;
  106. }
  107. public function filetype($path) {
  108. $filetype = filetype($this->getSourcePath($path));
  109. if ($filetype == 'link') {
  110. $filetype = filetype(realpath($this->getSourcePath($path)));
  111. }
  112. return $filetype;
  113. }
  114. public function filesize($path) {
  115. if ($this->is_dir($path)) {
  116. return 0;
  117. }
  118. $fullPath = $this->getSourcePath($path);
  119. if (PHP_INT_SIZE === 4) {
  120. $helper = new \OC\LargeFileHelper;
  121. return $helper->getFilesize($fullPath);
  122. }
  123. return filesize($fullPath);
  124. }
  125. public function isReadable($path) {
  126. return is_readable($this->getSourcePath($path));
  127. }
  128. public function isUpdatable($path) {
  129. return is_writable($this->getSourcePath($path));
  130. }
  131. public function file_exists($path) {
  132. return file_exists($this->getSourcePath($path));
  133. }
  134. public function filemtime($path) {
  135. clearstatcache($this->getSourcePath($path));
  136. return filemtime($this->getSourcePath($path));
  137. }
  138. public function touch($path, $mtime = null) {
  139. // sets the modification time of the file to the given value.
  140. // If mtime is nil the current time is set.
  141. // note that the access time of the file always changes to the current time.
  142. if ($this->file_exists($path) and !$this->isUpdatable($path)) {
  143. return false;
  144. }
  145. if (!is_null($mtime)) {
  146. $result = touch($this->getSourcePath($path), $mtime);
  147. } else {
  148. $result = touch($this->getSourcePath($path));
  149. }
  150. if ($result) {
  151. clearstatcache(true, $this->getSourcePath($path));
  152. }
  153. return $result;
  154. }
  155. public function file_get_contents($path) {
  156. return file_get_contents($this->getSourcePath($path));
  157. }
  158. public function file_put_contents($path, $data) {
  159. return file_put_contents($this->getSourcePath($path), $data);
  160. }
  161. public function unlink($path) {
  162. return $this->delTree($path);
  163. }
  164. public function rename($path1, $path2) {
  165. $srcParent = $this->dirname($path1);
  166. $dstParent = $this->dirname($path2);
  167. if (!$this->isUpdatable($srcParent)) {
  168. \OC_Log::write('core', 'unable to rename, source directory is not writable : ' . $srcParent, \OC_Log::ERROR);
  169. return false;
  170. }
  171. if (!$this->isUpdatable($dstParent)) {
  172. \OC_Log::write('core', 'unable to rename, destination directory is not writable : ' . $dstParent, \OC_Log::ERROR);
  173. return false;
  174. }
  175. if (!$this->file_exists($path1)) {
  176. \OC_Log::write('core', 'unable to rename, file does not exists : ' . $path1, \OC_Log::ERROR);
  177. return false;
  178. }
  179. if ($this->is_dir($path2)) {
  180. $this->rmdir($path2);
  181. } else if ($this->is_file($path2)) {
  182. $this->unlink($path2);
  183. }
  184. $physicPath1 = $this->getSourcePath($path1);
  185. $physicPath2 = $this->getSourcePath($path2);
  186. if ($return = rename($physicPath1, $physicPath2)) {
  187. // mapper needs to create copies or all children
  188. $this->copyMapping($path1, $path2);
  189. $this->cleanMapper($physicPath1, false, true);
  190. }
  191. return $return;
  192. }
  193. public function copy($path1, $path2) {
  194. if ($this->is_dir($path1)) {
  195. if ($this->is_dir($path2)) {
  196. $this->rmdir($path2);
  197. } else if ($this->is_file($path2)) {
  198. $this->unlink($path2);
  199. }
  200. $dir = $this->opendir($path1);
  201. $this->mkdir($path2);
  202. while ($file = readdir($dir)) {
  203. if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
  204. if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
  205. return false;
  206. }
  207. }
  208. }
  209. closedir($dir);
  210. return true;
  211. } else {
  212. if ($return = copy($this->getSourcePath($path1), $this->getSourcePath($path2))) {
  213. $this->copyMapping($path1, $path2);
  214. }
  215. return $return;
  216. }
  217. }
  218. public function fopen($path, $mode) {
  219. return fopen($this->getSourcePath($path), $mode);
  220. }
  221. /**
  222. * @param string $dir
  223. * @param bool $isLogicPath
  224. * @return bool
  225. */
  226. private function delTree($dir, $isLogicPath = true) {
  227. $dirRelative = $dir;
  228. if ($isLogicPath) {
  229. $dir = $this->getSourcePath($dir);
  230. }
  231. if (!file_exists($dir)) {
  232. return true;
  233. }
  234. if (!is_dir($dir) || is_link($dir)) {
  235. if ($return = unlink($dir)) {
  236. $this->cleanMapper($dir, false);
  237. return $return;
  238. }
  239. }
  240. foreach (scandir($dir) as $item) {
  241. if ($item == '.' || $item == '..') {
  242. continue;
  243. }
  244. if (is_file($dir . '/' . $item)) {
  245. if (unlink($dir . '/' . $item)) {
  246. $this->cleanMapper($dir . '/' . $item, false);
  247. }
  248. } elseif (is_dir($dir . '/' . $item)) {
  249. if (!$this->delTree($dir . "/" . $item, false)) {
  250. return false;
  251. };
  252. }
  253. }
  254. if ($return = rmdir($dir)) {
  255. $this->cleanMapper($dir, false);
  256. }
  257. return $return;
  258. }
  259. public function hash($type, $path, $raw = false) {
  260. return hash_file($type, $this->getSourcePath($path), $raw);
  261. }
  262. public function free_space($path) {
  263. $space = @disk_free_space($this->getSourcePath($path));
  264. if ($space === false || is_null($space)) {
  265. return \OCP\Files\FileInfo::SPACE_UNKNOWN;
  266. }
  267. return $space;
  268. }
  269. public function search($query) {
  270. return $this->searchInDir($query);
  271. }
  272. public function getLocalFile($path) {
  273. return $this->getSourcePath($path);
  274. }
  275. public function getLocalFolder($path) {
  276. return $this->getSourcePath($path);
  277. }
  278. /**
  279. * @param string $query
  280. */
  281. protected function searchInDir($query, $dir = '') {
  282. $files = array();
  283. $physicalDir = $this->getSourcePath($dir);
  284. foreach (scandir($physicalDir) as $item) {
  285. if ($item == '.' || $item == '..')
  286. continue;
  287. $physicalItem = $this->mapper->physicalToLogic($physicalDir . '/' . $item);
  288. $item = substr($physicalItem, strlen($physicalDir) + 1);
  289. if (strstr(strtolower($item), strtolower($query)) !== false) {
  290. $files[] = $dir . '/' . $item;
  291. }
  292. if (is_dir($physicalItem)) {
  293. $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
  294. }
  295. }
  296. return $files;
  297. }
  298. /**
  299. * check if a file or folder has been updated since $time
  300. *
  301. * @param string $path
  302. * @param int $time
  303. * @return bool
  304. */
  305. public function hasUpdated($path, $time) {
  306. if ($this->file_exists($path)) {
  307. return $this->filemtime($path) > $time;
  308. } else {
  309. return true;
  310. }
  311. }
  312. /**
  313. * Get the source path (on disk) of a given path
  314. *
  315. * @param string $path
  316. * @return string
  317. */
  318. protected function getSourcePath($path) {
  319. $path = $this->stripLeading($path);
  320. $fullPath = $this->datadir . $path;
  321. return $this->mapper->logicToPhysical($fullPath, true);
  322. }
  323. /**
  324. * {@inheritdoc}
  325. */
  326. public function isLocal() {
  327. return true;
  328. }
  329. /**
  330. * @param string $path
  331. * @return string
  332. */
  333. private function dirName($path) {
  334. $path = dirname($path);
  335. if ($path === '.') {
  336. return '';
  337. } else {
  338. return $path;
  339. }
  340. }
  341. /**
  342. * @param string $path
  343. */
  344. private function cleanMapper($path, $isLogicPath = true, $recursive = true) {
  345. $fullPath = $path;
  346. if ($isLogicPath) {
  347. $fullPath = $this->datadir . $path;
  348. }
  349. $this->mapper->removePath($fullPath, $isLogicPath, $recursive);
  350. }
  351. /**
  352. * @param string $path1
  353. * @param string $path2
  354. */
  355. private function copyMapping($path1, $path2) {
  356. $path1 = $this->stripLeading($path1);
  357. $path2 = $this->stripLeading($path2);
  358. $fullPath1 = $this->datadir . $path1;
  359. $fullPath2 = $this->datadir . $path2;
  360. $this->mapper->copy($fullPath1, $fullPath2);
  361. }
  362. /**
  363. * @param string $path
  364. */
  365. private function stripLeading($path) {
  366. if (strpos($path, '/') === 0) {
  367. $path = substr($path, 1);
  368. }
  369. if (strpos($path, '\\') === 0) {
  370. $path = substr($path, 1);
  371. }
  372. if ($path === false) {
  373. return '';
  374. }
  375. return $path;
  376. }
  377. }