Merge pull request #17379 from owncloud/kill-file-mapper

Remove file mapper - was only use in Windows and never worked properly
remotes/origin/handlebars-approach
Vincent Petry 2015-07-03 17:53:47 +07:00
commit 3df27a01be
7 changed files with 282 additions and 1240 deletions

@ -1,305 +0,0 @@
<?php
/**
* @author infoneo <infoneo@yahoo.pl>
* @author Joas Schilling <nickvergessen@owncloud.com>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Lukas Reschke <lukas@owncloud.com>
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Files;
/**
* class Mapper is responsible to translate logical paths to physical paths and reverse
*/
class Mapper
{
private $unchangedPhysicalRoot;
public function __construct($rootDir) {
$this->unchangedPhysicalRoot = $rootDir;
}
/**
* @param string $logicPath
* @param bool $create indicates if the generated physical name shall be stored in the database or not
* @return string the physical path
*/
public function logicToPhysical($logicPath, $create) {
$physicalPath = $this->resolveLogicPath($logicPath);
if ($physicalPath !== null) {
return $physicalPath;
}
return $this->create($logicPath, $create);
}
/**
* @param string $physicalPath
* @return string
*/
public function physicalToLogic($physicalPath) {
$logicPath = $this->resolvePhysicalPath($physicalPath);
if ($logicPath !== null) {
return $logicPath;
}
$this->insert($physicalPath, $physicalPath);
return $physicalPath;
}
/**
* @param string $path
* @param bool $isLogicPath indicates if $path is logical or physical
* @param boolean $recursive
* @return void
*/
public function removePath($path, $isLogicPath, $recursive) {
if ($recursive) {
$path=$path.'%';
}
if ($isLogicPath) {
\OC_DB::executeAudited('DELETE FROM `*PREFIX*file_map` WHERE `logic_path` LIKE ?', array($path));
} else {
\OC_DB::executeAudited('DELETE FROM `*PREFIX*file_map` WHERE `physic_path` LIKE ?', array($path));
}
}
/**
* @param string $path1
* @param string $path2
* @throws \Exception
*/
public function copy($path1, $path2)
{
$path1 = $this->resolveRelativePath($path1);
$path2 = $this->resolveRelativePath($path2);
$physicPath1 = $this->logicToPhysical($path1, true);
$physicPath2 = $this->logicToPhysical($path2, true);
$sql = 'SELECT * FROM `*PREFIX*file_map` WHERE `logic_path` LIKE ?';
$result = \OC_DB::executeAudited($sql, array($path1.'%'));
$updateQuery = \OC_DB::prepare('UPDATE `*PREFIX*file_map`'
.' SET `logic_path` = ?'
.' , `logic_path_hash` = ?'
.' , `physic_path` = ?'
.' , `physic_path_hash` = ?'
.' WHERE `logic_path` = ?');
while( $row = $result->fetchRow()) {
$currentLogic = $row['logic_path'];
$currentPhysic = $row['physic_path'];
$newLogic = $path2.$this->stripRootFolder($currentLogic, $path1);
$newPhysic = $physicPath2.$this->stripRootFolder($currentPhysic, $physicPath1);
if ($path1 !== $currentLogic) {
try {
\OC_DB::executeAudited($updateQuery, array($newLogic, md5($newLogic), $newPhysic, md5($newPhysic),
$currentLogic));
} catch (\Exception $e) {
error_log('Mapper::Copy failed '.$currentLogic.' -> '.$newLogic.'\n'.$e);
throw $e;
}
}
}
}
/**
* @param string $path
* @param string $root
* @return false|string
*/
public function stripRootFolder($path, $root) {
if (strpos($path, $root) !== 0) {
// throw exception ???
return false;
}
if (strlen($path) > strlen($root)) {
return substr($path, strlen($root));
}
return '';
}
/**
* @param string $logicPath
* @return null
* @throws \OC\DatabaseException
*/
private function resolveLogicPath($logicPath) {
$logicPath = $this->resolveRelativePath($logicPath);
$sql = 'SELECT * FROM `*PREFIX*file_map` WHERE `logic_path_hash` = ?';
$result = \OC_DB::executeAudited($sql, array(md5($logicPath)));
$result = $result->fetchRow();
if ($result === false) {
return null;
}
return $result['physic_path'];
}
private function resolvePhysicalPath($physicalPath) {
$physicalPath = $this->resolveRelativePath($physicalPath);
$sql = \OC_DB::prepare('SELECT * FROM `*PREFIX*file_map` WHERE `physic_path_hash` = ?');
$result = \OC_DB::executeAudited($sql, array(md5($physicalPath)));
$result = $result->fetchRow();
return $result['logic_path'];
}
private function resolveRelativePath($path) {
$explodedPath = explode('/', $path);
$pathArray = array();
foreach ($explodedPath as $pathElement) {
if (empty($pathElement) || ($pathElement == '.')) {
continue;
} elseif ($pathElement == '..') {
if (count($pathArray) == 0) {
return false;
}
array_pop($pathArray);
} else {
array_push($pathArray, $pathElement);
}
}
if (substr($path, 0, 1) == '/') {
$path = '/';
} else {
$path = '';
}
return $path.implode('/', $pathArray);
}
/**
* @param string $logicPath
* @param bool $store
* @return string
*/
private function create($logicPath, $store) {
$logicPath = $this->resolveRelativePath($logicPath);
$index = 0;
// create the slugified path
$physicalPath = $this->slugifyPath($logicPath);
// detect duplicates
while ($this->resolvePhysicalPath($physicalPath) !== null) {
$physicalPath = $this->slugifyPath($logicPath, $index++);
}
// insert the new path mapping if requested
if ($store) {
$this->insert($logicPath, $physicalPath);
}
return $physicalPath;
}
private function insert($logicPath, $physicalPath) {
$sql = 'INSERT INTO `*PREFIX*file_map` (`logic_path`, `physic_path`, `logic_path_hash`, `physic_path_hash`)
VALUES (?, ?, ?, ?)';
\OC_DB::executeAudited($sql, array($logicPath, $physicalPath, md5($logicPath), md5($physicalPath)));
}
/**
* @param string $path
* @param int $index
* @return string
*/
public function slugifyPath($path, $index = null) {
$path = $this->stripRootFolder($path, $this->unchangedPhysicalRoot);
$pathElements = explode('/', $path);
$sluggedElements = array();
foreach ($pathElements as $pathElement) {
// remove empty elements
if (empty($pathElement)) {
continue;
}
$sluggedElements[] = $this->slugify($pathElement);
}
// apply index to file name
if ($index !== null) {
$last = array_pop($sluggedElements);
// if filename contains periods - add index number before last period
if (preg_match('~\.[^\.]+$~i', $last, $extension)) {
array_push($sluggedElements, substr($last, 0, -(strlen($extension[0]))) . '-' . $index . $extension[0]);
} else {
// if filename doesn't contain periods add index ofter the last char
array_push($sluggedElements, $last . '-' . $index);
}
}
$sluggedPath = $this->unchangedPhysicalRoot.implode('/', $sluggedElements);
return $this->resolveRelativePath($sluggedPath);
}
/**
* Modifies a string to remove all non ASCII characters and spaces.
*
* @param string $text
* @return string
*/
private function slugify($text) {
$originalText = $text;
// replace non letter or digits or dots by -
$text = preg_replace('~[^\\pL\d\.]+~u', '-', $text);
// trim
$text = trim($text, '-');
// transliterate
if (function_exists('iconv')) {
$text = iconv('utf-8', 'us-ascii//TRANSLIT//IGNORE', $text);
}
// lowercase
$text = strtolower($text);
// remove unwanted characters
$text = preg_replace('~[^-\w\.]+~', '', $text);
// trim ending dots (for security reasons and win compatibility)
$text = preg_replace('~\.+$~', '', $text);
if (empty($text) || \OC\Files\Filesystem::isFileBlacklisted($text)) {
/**
* Item slug would be empty. Previously we used uniqid() here.
* However this means that the behaviour is not reproducible, so
* when uploading files into a "empty" folder, the folders name is
* different.
*
* The other case is, that the slugified name would be a blacklisted
* filename. In this case we just use the same workaround by
* returning the secure md5 hash of the original name.
*
*
* If there would be a md5() hash collision, the deduplicate check
* will spot this and append an index later, so this should not be
* a problem.
*/
return md5($originalText);
}
return $text;
}
}

@ -36,361 +36,354 @@
namespace OC\Files\Storage;
if (\OC_Util::runningOnWindows()) {
class Local extends MappedLocal {
}
} else {
/**
* for local filestore, we only have to map the paths
*/
class Local extends \OC\Files\Storage\Common {
protected $datadir;
/**
* for local filestore, we only have to map the paths
*/
class Local extends \OC\Files\Storage\Common {
protected $datadir;
public function __construct($arguments) {
$this->datadir = $arguments['datadir'];
if (substr($this->datadir, -1) !== '/') {
$this->datadir .= '/';
}
public function __construct($arguments) {
$this->datadir = $arguments['datadir'];
if (substr($this->datadir, -1) !== '/') {
$this->datadir .= '/';
}
}
public function __destruct() {
}
public function __destruct() {
}
public function getId() {
return 'local::' . $this->datadir;
}
public function getId() {
return 'local::' . $this->datadir;
}
public function mkdir($path) {
return @mkdir($this->getSourcePath($path), 0777, true);
}
public function mkdir($path) {
return @mkdir($this->getSourcePath($path), 0777, true);
}
public function rmdir($path) {
if (!$this->isDeletable($path)) {
return false;
}
try {
$it = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->getSourcePath($path)),
\RecursiveIteratorIterator::CHILD_FIRST
);
public function rmdir($path) {
if (!$this->isDeletable($path)) {
return false;
}
try {
$it = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->getSourcePath($path)),
\RecursiveIteratorIterator::CHILD_FIRST
);
/**
* RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
* This bug is fixed in PHP 5.5.9 or before
* See #8376
*/
$it->rewind();
while ($it->valid()) {
/**
* RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
* This bug is fixed in PHP 5.5.9 or before
* See #8376
* @var \SplFileInfo $file
*/
$it->rewind();
while ($it->valid()) {
/**
* @var \SplFileInfo $file
*/
$file = $it->current();
if (in_array($file->getBasename(), array('.', '..'))) {
$it->next();
continue;
} elseif ($file->isDir()) {
rmdir($file->getPathname());
} elseif ($file->isFile() || $file->isLink()) {
unlink($file->getPathname());
}
$file = $it->current();
if (in_array($file->getBasename(), array('.', '..'))) {
$it->next();
continue;
} elseif ($file->isDir()) {
rmdir($file->getPathname());
} elseif ($file->isFile() || $file->isLink()) {
unlink($file->getPathname());
}
return rmdir($this->getSourcePath($path));
} catch (\UnexpectedValueException $e) {
return false;
$it->next();
}
return rmdir($this->getSourcePath($path));
} catch (\UnexpectedValueException $e) {
return false;
}
}
public function opendir($path) {
return opendir($this->getSourcePath($path));
}
public function is_dir($path) {
if (substr($path, -1) == '/') {
$path = substr($path, 0, -1);
}
return is_dir($this->getSourcePath($path));
}
public function opendir($path) {
return opendir($this->getSourcePath($path));
}
public function is_file($path) {
return is_file($this->getSourcePath($path));
public function is_dir($path) {
if (substr($path, -1) == '/') {
$path = substr($path, 0, -1);
}
return is_dir($this->getSourcePath($path));
}
public function stat($path) {
clearstatcache();
$fullPath = $this->getSourcePath($path);
$statResult = stat($fullPath);
if (PHP_INT_SIZE === 4 && !$this->is_dir($path)) {
$filesize = $this->filesize($path);
$statResult['size'] = $filesize;
$statResult[7] = $filesize;
}
return $statResult;
}
public function is_file($path) {
return is_file($this->getSourcePath($path));
}
public function filetype($path) {
$filetype = filetype($this->getSourcePath($path));
if ($filetype == 'link') {
$filetype = filetype(realpath($this->getSourcePath($path)));
}
return $filetype;
public function stat($path) {
clearstatcache();
$fullPath = $this->getSourcePath($path);
$statResult = stat($fullPath);
if (PHP_INT_SIZE === 4 && !$this->is_dir($path)) {
$filesize = $this->filesize($path);
$statResult['size'] = $filesize;
$statResult[7] = $filesize;
}
return $statResult;
}
public function filesize($path) {
if ($this->is_dir($path)) {
return 0;
}
$fullPath = $this->getSourcePath($path);
if (PHP_INT_SIZE === 4) {
$helper = new \OC\LargeFileHelper;
return $helper->getFilesize($fullPath);
}
return filesize($fullPath);
public function filetype($path) {
$filetype = filetype($this->getSourcePath($path));
if ($filetype == 'link') {
$filetype = filetype(realpath($this->getSourcePath($path)));
}
return $filetype;
}
public function isReadable($path) {
return is_readable($this->getSourcePath($path));
public function filesize($path) {
if ($this->is_dir($path)) {
return 0;
}
public function isUpdatable($path) {
return is_writable($this->getSourcePath($path));
$fullPath = $this->getSourcePath($path);
if (PHP_INT_SIZE === 4) {
$helper = new \OC\LargeFileHelper;
return $helper->getFilesize($fullPath);
}
return filesize($fullPath);
}
public function file_exists($path) {
return file_exists($this->getSourcePath($path));
}
public function isReadable($path) {
return is_readable($this->getSourcePath($path));
}
public function filemtime($path) {
clearstatcache($this->getSourcePath($path));
return filemtime($this->getSourcePath($path));
}
public function isUpdatable($path) {
return is_writable($this->getSourcePath($path));
}
public function touch($path, $mtime = null) {
// sets the modification time of the file to the given value.
// If mtime is nil the current time is set.
// note that the access time of the file always changes to the current time.
if ($this->file_exists($path) and !$this->isUpdatable($path)) {
return false;
}
if (!is_null($mtime)) {
$result = touch($this->getSourcePath($path), $mtime);
} else {
$result = touch($this->getSourcePath($path));
}
if ($result) {
clearstatcache(true, $this->getSourcePath($path));
}
public function file_exists($path) {
return file_exists($this->getSourcePath($path));
}
return $result;
}
public function filemtime($path) {
clearstatcache($this->getSourcePath($path));
return filemtime($this->getSourcePath($path));
}
public function file_get_contents($path) {
return file_get_contents($this->getSourcePath($path));
public function touch($path, $mtime = null) {
// sets the modification time of the file to the given value.
// If mtime is nil the current time is set.
// note that the access time of the file always changes to the current time.
if ($this->file_exists($path) and !$this->isUpdatable($path)) {
return false;
}
public function file_put_contents($path, $data) {
return file_put_contents($this->getSourcePath($path), $data);
if (!is_null($mtime)) {
$result = touch($this->getSourcePath($path), $mtime);
} else {
$result = touch($this->getSourcePath($path));
}
public function unlink($path) {
if ($this->is_dir($path)) {
return $this->rmdir($path);
} else if ($this->is_file($path)) {
return unlink($this->getSourcePath($path));
} else {
return false;
}
if ($result) {
clearstatcache(true, $this->getSourcePath($path));
}
public function rename($path1, $path2) {
$srcParent = dirname($path1);
$dstParent = dirname($path2);
return $result;
}
if (!$this->isUpdatable($srcParent)) {
\OC_Log::write('core', 'unable to rename, source directory is not writable : ' . $srcParent, \OC_Log::ERROR);
return false;
}
public function file_get_contents($path) {
return file_get_contents($this->getSourcePath($path));
}
if (!$this->isUpdatable($dstParent)) {
\OC_Log::write('core', 'unable to rename, destination directory is not writable : ' . $dstParent, \OC_Log::ERROR);
return false;
}
public function file_put_contents($path, $data) {
return file_put_contents($this->getSourcePath($path), $data);
}
if (!$this->file_exists($path1)) {
\OC_Log::write('core', 'unable to rename, file does not exists : ' . $path1, \OC_Log::ERROR);
return false;
}
public function unlink($path) {
if ($this->is_dir($path)) {
return $this->rmdir($path);
} else if ($this->is_file($path)) {
return unlink($this->getSourcePath($path));
} else {
return false;
}
if ($this->is_dir($path2)) {
$this->rmdir($path2);
} else if ($this->is_file($path2)) {
$this->unlink($path2);
}
}
if ($this->is_dir($path1)) {
// we cant move folders across devices, use copy instead
$stat1 = stat(dirname($this->getSourcePath($path1)));
$stat2 = stat(dirname($this->getSourcePath($path2)));
if ($stat1['dev'] !== $stat2['dev']) {
$result = $this->copy($path1, $path2);
if ($result) {
$result &= $this->rmdir($path1);
}
return $result;
}
}
public function rename($path1, $path2) {
$srcParent = dirname($path1);
$dstParent = dirname($path2);
return rename($this->getSourcePath($path1), $this->getSourcePath($path2));
if (!$this->isUpdatable($srcParent)) {
\OC_Log::write('core', 'unable to rename, source directory is not writable : ' . $srcParent, \OC_Log::ERROR);
return false;
}
public function copy($path1, $path2) {
if ($this->is_dir($path1)) {
return parent::copy($path1, $path2);
} else {
return copy($this->getSourcePath($path1), $this->getSourcePath($path2));
}
if (!$this->isUpdatable($dstParent)) {
\OC_Log::write('core', 'unable to rename, destination directory is not writable : ' . $dstParent, \OC_Log::ERROR);
return false;
}
public function fopen($path, $mode) {
return fopen($this->getSourcePath($path), $mode);
if (!$this->file_exists($path1)) {
\OC_Log::write('core', 'unable to rename, file does not exists : ' . $path1, \OC_Log::ERROR);
return false;
}
public function hash($type, $path, $raw = false) {
return hash_file($type, $this->getSourcePath($path), $raw);
if ($this->is_dir($path2)) {
$this->rmdir($path2);
} else if ($this->is_file($path2)) {
$this->unlink($path2);
}
public function free_space($path) {
$space = @disk_free_space($this->getSourcePath($path));
if ($space === false || is_null($space)) {
return \OCP\Files\FileInfo::SPACE_UNKNOWN;
if ($this->is_dir($path1)) {
// we cant move folders across devices, use copy instead
$stat1 = stat(dirname($this->getSourcePath($path1)));
$stat2 = stat(dirname($this->getSourcePath($path2)));
if ($stat1['dev'] !== $stat2['dev']) {
$result = $this->copy($path1, $path2);
if ($result) {
$result &= $this->rmdir($path1);
}
return $result;
}
return $space;
}
public function search($query) {
return $this->searchInDir($query);
}
return rename($this->getSourcePath($path1), $this->getSourcePath($path2));
}
public function getLocalFile($path) {
return $this->getSourcePath($path);
public function copy($path1, $path2) {
if ($this->is_dir($path1)) {
return parent::copy($path1, $path2);
} else {
return copy($this->getSourcePath($path1), $this->getSourcePath($path2));
}
}
public function getLocalFolder($path) {
return $this->getSourcePath($path);
}
public function fopen($path, $mode) {
return fopen($this->getSourcePath($path), $mode);
}
/**
* @param string $query
* @param string $dir
* @return array
*/
protected function searchInDir($query, $dir = '') {
$files = array();
$physicalDir = $this->getSourcePath($dir);
foreach (scandir($physicalDir) as $item) {
if ($item == '.' || $item == '..')
continue;
$physicalItem = $physicalDir . '/' . $item;
public function hash($type, $path, $raw = false) {
return hash_file($type, $this->getSourcePath($path), $raw);
}
if (strstr(strtolower($item), strtolower($query)) !== false) {
$files[] = $dir . '/' . $item;
}
if (is_dir($physicalItem)) {
$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
}
}
return $files;
public function free_space($path) {
$space = @disk_free_space($this->getSourcePath($path));
if ($space === false || is_null($space)) {
return \OCP\Files\FileInfo::SPACE_UNKNOWN;
}
return $space;
}
/**
* check if a file or folder has been updated since $time
*
* @param string $path
* @param int $time
* @return bool
*/
public function hasUpdated($path, $time) {
if ($this->file_exists($path)) {
return $this->filemtime($path) > $time;
} else {
return true;
}
}
public function search($query) {
return $this->searchInDir($query);
}
public function getLocalFile($path) {
return $this->getSourcePath($path);
}
public function getLocalFolder($path) {
return $this->getSourcePath($path);
}
/**
* Get the source path (on disk) of a given path
*
* @param string $path
* @return string
*/
public function getSourcePath($path) {
$fullPath = $this->datadir . $path;
return $fullPath;
/**
* @param string $query
* @param string $dir
* @return array
*/
protected function searchInDir($query, $dir = '') {
$files = array();
$physicalDir = $this->getSourcePath($dir);
foreach (scandir($physicalDir) as $item) {
if ($item == '.' || $item == '..')
continue;
$physicalItem = $physicalDir . '/' . $item;
if (strstr(strtolower($item), strtolower($query)) !== false) {
$files[] = $dir . '/' . $item;
}
if (is_dir($physicalItem)) {
$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
}
}
return $files;
}
/**
* {@inheritdoc}
*/
public function isLocal() {
/**
* check if a file or folder has been updated since $time
*
* @param string $path
* @param int $time
* @return bool
*/
public function hasUpdated($path, $time) {
if ($this->file_exists($path)) {
return $this->filemtime($path) > $time;
} else {
return true;
}
}
/**
* get the ETag for a file or folder
*
* @param string $path
* @return string
*/
public function getETag($path) {
if ($this->is_file($path)) {
$stat = $this->stat($path);
return md5(
$stat['mtime'] .
$stat['ino'] .
$stat['dev'] .
$stat['size']
);
} else {
return parent::getETag($path);
}
/**
* Get the source path (on disk) of a given path
*
* @param string $path
* @return string
*/
public function getSourcePath($path) {
$fullPath = $this->datadir . $path;
return $fullPath;
}
/**
* {@inheritdoc}
*/
public function isLocal() {
return true;
}
/**
* get the ETag for a file or folder
*
* @param string $path
* @return string
*/
public function getETag($path) {
if ($this->is_file($path)) {
$stat = $this->stat($path);
return md5(
$stat['mtime'] .
$stat['ino'] .
$stat['dev'] .
$stat['size']
);
} else {
return parent::getETag($path);
}
}
/**
* @param \OCP\Files\Storage $sourceStorage
* @param string $sourceInternalPath
* @param string $targetInternalPath
* @return bool
*/
public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
if($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')){
/**
* @var \OC\Files\Storage\Local $sourceStorage
*/
$rootStorage = new Local(['datadir' => '/']);
return $rootStorage->copy($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
} else {
return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
}
/**
* @param \OCP\Files\Storage $sourceStorage
* @param string $sourceInternalPath
* @param string $targetInternalPath
* @return bool
*/
public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
if($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')){
/**
* @var \OC\Files\Storage\Local $sourceStorage
*/
$rootStorage = new Local(['datadir' => '/']);
return $rootStorage->copy($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
} else {
return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
}
}
/**
* @param \OCP\Files\Storage $sourceStorage
* @param string $sourceInternalPath
* @param string $targetInternalPath
* @return bool
*/
public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')) {
/**
* @var \OC\Files\Storage\Local $sourceStorage
*/
$rootStorage = new Local(['datadir' => '/']);
return $rootStorage->rename($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
} else {
return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
}
/**
* @param \OCP\Files\Storage $sourceStorage
* @param string $sourceInternalPath
* @param string $targetInternalPath
* @return bool
*/
public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')) {
/**
* @var \OC\Files\Storage\Local $sourceStorage
*/
$rootStorage = new Local(['datadir' => '/']);
return $rootStorage->rename($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
} else {
return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
}
}
}

@ -1,456 +0,0 @@
<?php
/**
* @author Andreas Fischer <bantu@owncloud.com>
* @author Arthur Schiwon <blizzz@owncloud.com>
* @author Bart Visscher <bartv@thisnet.nl>
* @author Clark Tomlinson <fallen013@gmail.com>
* @author Joas Schilling <nickvergessen@owncloud.com>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Lukas Reschke <lukas@owncloud.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <icewind@owncloud.com>
* @author Scrutinizer Auto-Fixer <auto-fixer@scrutinizer-ci.com>
* @author Sjors van der Pluijm <sjors@desjors.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Files\Storage;
/**
* for local filestore, we only have to map the paths
*/
class MappedLocal extends \OC\Files\Storage\Common {
protected $datadir;
private $mapper;
public function __construct($arguments) {
$this->datadir = $arguments['datadir'];
if (substr($this->datadir, -1) !== '/') {
$this->datadir .= '/';
}
$this->mapper = new \OC\Files\Mapper($this->datadir);
}
public function __destruct() {
}
public function getId() {
return 'local::' . $this->datadir;
}
public function mkdir($path) {
return @mkdir($this->getSourcePath($path), 0777, true);
}
public function rmdir($path) {
if (!$this->isDeletable($path)) {
return false;
}
try {
$it = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->getSourcePath($path)),
\RecursiveIteratorIterator::CHILD_FIRST
);
/**
* RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
* This bug is fixed in PHP 5.5.9 or before
* See #8376
*/
$it->rewind();
while ($it->valid()) {
/**
* @var \SplFileInfo $file
*/
$file = $it->current();
if (in_array($file->getBasename(), array('.', '..'))) {
$it->next();
continue;
} elseif ($file->isDir()) {
rmdir($file->getPathname());
} elseif ($file->isFile() || $file->isLink()) {
unlink($file->getPathname());
}
$it->next();
}
if ($result = @rmdir($this->getSourcePath($path))) {
$this->cleanMapper($path);
}
return $result;
} catch (\UnexpectedValueException $e) {
return false;
}
}
public function opendir($path) {
$files = array('.', '..');
$physicalPath = $this->getSourcePath($path);
$logicalPath = $this->mapper->physicalToLogic($physicalPath);
$dh = opendir($physicalPath);
if (is_resource($dh)) {
while (($file = readdir($dh)) !== false) {
if ($file === '.' or $file === '..') {
continue;
}
$logicalFilePath = $this->mapper->physicalToLogic($physicalPath . '/' . $file);
$file = $this->mapper->stripRootFolder($logicalFilePath, $logicalPath);
$file = $this->stripLeading($file);
$files[] = $file;
}
}
\OC\Files\Stream\Dir::register('local-win32' . $path, $files);
return opendir('fakedir://local-win32' . $path);
}
public function is_dir($path) {
if (substr($path, -1) == '/') {
$path = substr($path, 0, -1);
}
return is_dir($this->getSourcePath($path));
}
public function is_file($path) {
return is_file($this->getSourcePath($path));
}
public function stat($path) {
clearstatcache();
$fullPath = $this->getSourcePath($path);
$statResult = stat($fullPath);
if (PHP_INT_SIZE === 4 && !$this->is_dir($path)) {
$filesize = $this->filesize($path);
$statResult['size'] = $filesize;
$statResult[7] = $filesize;
}
return $statResult;
}
public function filetype($path) {
$filetype = filetype($this->getSourcePath($path));
if ($filetype == 'link') {
$filetype = filetype(realpath($this->getSourcePath($path)));
}
return $filetype;
}
public function filesize($path) {
if ($this->is_dir($path)) {
return 0;
}
$fullPath = $this->getSourcePath($path);
if (PHP_INT_SIZE === 4) {
$helper = new \OC\LargeFileHelper;
return $helper->getFilesize($fullPath);
}
return filesize($fullPath);
}
public function isReadable($path) {
return is_readable($this->getSourcePath($path));
}
public function isUpdatable($path) {
return is_writable($this->getSourcePath($path));
}
public function file_exists($path) {
return file_exists($this->getSourcePath($path));
}
public function filemtime($path) {
clearstatcache($this->getSourcePath($path));
return filemtime($this->getSourcePath($path));
}
public function touch($path, $mtime = null) {
// sets the modification time of the file to the given value.
// If mtime is nil the current time is set.
// note that the access time of the file always changes to the current time.
if ($this->file_exists($path) and !$this->isUpdatable($path)) {
return false;
}
if (!is_null($mtime)) {
$result = touch($this->getSourcePath($path), $mtime);
} else {
$result = touch($this->getSourcePath($path));
}
if ($result) {
clearstatcache(true, $this->getSourcePath($path));
}
return $result;
}
public function file_get_contents($path) {
return file_get_contents($this->getSourcePath($path));
}
public function file_put_contents($path, $data) {
return file_put_contents($this->getSourcePath($path), $data);
}
public function unlink($path) {
return $this->delTree($path);
}
public function rename($path1, $path2) {
$srcParent = $this->dirname($path1);
$dstParent = $this->dirname($path2);
if (!$this->isUpdatable($srcParent)) {
\OC_Log::write('core', 'unable to rename, source directory is not writable : ' . $srcParent, \OC_Log::ERROR);
return false;
}
if (!$this->isUpdatable($dstParent)) {
\OC_Log::write('core', 'unable to rename, destination directory is not writable : ' . $dstParent, \OC_Log::ERROR);
return false;
}
if (!$this->file_exists($path1)) {
\OC_Log::write('core', 'unable to rename, file does not exists : ' . $path1, \OC_Log::ERROR);
return false;
}
if ($this->is_dir($path2)) {
$this->rmdir($path2);
} else if ($this->is_file($path2)) {
$this->unlink($path2);
}
$physicPath1 = $this->getSourcePath($path1);
$physicPath2 = $this->getSourcePath($path2);
if ($return = rename($physicPath1, $physicPath2)) {
// mapper needs to create copies or all children
$this->copyMapping($path1, $path2);
$this->cleanMapper($physicPath1, false, true);
}
return $return;
}
public function copy($path1, $path2) {
if ($this->is_dir($path1)) {
if ($this->is_dir($path2)) {
$this->rmdir($path2);
} else if ($this->is_file($path2)) {
$this->unlink($path2);
}
$dir = $this->opendir($path1);
$this->mkdir($path2);
while ($file = readdir($dir)) {
if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
return false;
}
}
}
closedir($dir);
return true;
} else {
if ($return = copy($this->getSourcePath($path1), $this->getSourcePath($path2))) {
$this->copyMapping($path1, $path2);
}
return $return;
}
}
public function fopen($path, $mode) {
return fopen($this->getSourcePath($path), $mode);
}
/**
* @param string $dir
* @param bool $isLogicPath
* @return bool
*/
private function delTree($dir, $isLogicPath = true) {
$dirRelative = $dir;
if ($isLogicPath) {
$dir = $this->getSourcePath($dir);
}
if (!file_exists($dir)) {
return true;
}
if (!is_dir($dir) || is_link($dir)) {
if ($return = unlink($dir)) {
$this->cleanMapper($dir, false);
return $return;
}
}
foreach (scandir($dir) as $item) {
if ($item == '.' || $item == '..') {
continue;
}
if (is_file($dir . '/' . $item)) {
if (unlink($dir . '/' . $item)) {
$this->cleanMapper($dir . '/' . $item, false);
}
} elseif (is_dir($dir . '/' . $item)) {
if (!$this->delTree($dir . "/" . $item, false)) {
return false;
};
}
}
if ($return = rmdir($dir)) {
$this->cleanMapper($dir, false);
}
return $return;
}
public function hash($type, $path, $raw = false) {
return hash_file($type, $this->getSourcePath($path), $raw);
}
public function free_space($path) {
$space = @disk_free_space($this->getSourcePath($path));
if ($space === false || is_null($space)) {
return \OCP\Files\FileInfo::SPACE_UNKNOWN;
}
return $space;
}
public function search($query) {
return $this->searchInDir($query);
}
public function getLocalFile($path) {
return $this->getSourcePath($path);
}
public function getLocalFolder($path) {
return $this->getSourcePath($path);
}
/**
* @param string $query
* @param string $dir
* @return array
*/
protected function searchInDir($query, $dir = '') {
$files = array();
$physicalDir = $this->getSourcePath($dir);
foreach (scandir($physicalDir) as $item) {
if ($item == '.' || $item == '..')
continue;
$physicalItem = $this->mapper->physicalToLogic($physicalDir . '/' . $item);
$item = substr($physicalItem, strlen($physicalDir) + 1);
if (strstr(strtolower($item), strtolower($query)) !== false) {
$files[] = $dir . '/' . $item;
}
if (is_dir($physicalItem)) {
$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
}
}
return $files;
}
/**
* check if a file or folder has been updated since $time
*
* @param string $path
* @param int $time
* @return bool
*/
public function hasUpdated($path, $time) {
if ($this->file_exists($path)) {
return $this->filemtime($path) > $time;
} else {
return true;
}
}
/**
* Get the source path (on disk) of a given path
*
* @param string $path
* @return string
*/
protected function getSourcePath($path) {
$path = $this->stripLeading($path);
$fullPath = $this->datadir . $path;
return $this->mapper->logicToPhysical($fullPath, true);
}
/**
* {@inheritdoc}
*/
public function isLocal() {
return true;
}
/**
* @param string $path
* @return string
*/
private function dirName($path) {
$path = dirname($path);
if ($path === '.') {
return '';
} else {
return $path;
}
}
/**
* @param string $path
*/
private function cleanMapper($path, $isLogicPath = true, $recursive = true) {
$fullPath = $path;
if ($isLogicPath) {
$fullPath = $this->datadir . $path;
}
$this->mapper->removePath($fullPath, $isLogicPath, $recursive);
}
/**
* @param string $path1
* @param string $path2
*/
private function copyMapping($path1, $path2) {
$path1 = $this->stripLeading($path1);
$path2 = $this->stripLeading($path2);
$fullPath1 = $this->datadir . $path1;
$fullPath2 = $this->datadir . $path2;
$this->mapper->copy($fullPath1, $fullPath2);
}
/**
* @param string $path
*/
private function stripLeading($path) {
if (strpos($path, '/') === 0) {
$path = substr($path, 1);
}
if (strpos($path, '\\') === 0) {
$path = substr($path, 1);
}
if ($path === false) {
return '';
}
return $path;
}
}

@ -1,89 +0,0 @@
<?php
/**
* ownCloud
*
* @author Thomas Müller
* @copyright 2013 Thomas Müller thomas.mueller@owncloud.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Test\Files;
class Mapper extends \Test\TestCase {
/**
* @var \OC\Files\Mapper
*/
private $mapper = null;
protected function setUp() {
parent::setUp();
$this->mapper = new \OC\Files\Mapper('D:/');
}
public function slugifyPathData() {
return array(
// with extension
array('D:/text.txt', 'D:/text.txt'),
array('D:/text-2.txt', 'D:/text.txt', 2),
array('D:/a/b/text.txt', 'D:/a/b/text.txt'),
// without extension
array('D:/text', 'D:/text'),
array('D:/text-2', 'D:/text', 2),
array('D:/a/b/text', 'D:/a/b/text'),
// with double dot
array('D:/text.text.txt', 'D:/text.text.txt'),
array('D:/text.text-2.txt', 'D:/text.text.txt', 2),
array('D:/a/b/text.text.txt', 'D:/a/b/text.text.txt'),
// foldername and filename with periods
array('D:/folder.name.with.periods', 'D:/folder.name.with.periods'),
array('D:/folder.name.with.periods/test-2.txt', 'D:/folder.name.with.periods/test.txt', 2),
array('D:/folder.name.with.periods/test.txt', 'D:/folder.name.with.periods/test.txt'),
// foldername and filename with periods and spaces
array('D:/folder.name.with.peri-ods', 'D:/folder.name.with.peri ods'),
array('D:/folder.name.with.peri-ods/te-st-2.t-x-t', 'D:/folder.name.with.peri ods/te st.t x t', 2),
array('D:/folder.name.with.peri-ods/te-st.t-x-t', 'D:/folder.name.with.peri ods/te st.t x t'),
/**
* If a foldername is empty, after we stripped out some unicode and other characters,
* the resulting name must be reproducable otherwise uploading a file into that folder
* will not write the file into the same folder.
*/
array('D:/' . md5('ありがとう'), 'D:/ありがとう'),
array('D:/' . md5('ありがとう') . '/issue6722.txt', 'D:/ありがとう/issue6722.txt'),
array('D:/' . md5('.htaccess'), 'D:/.htaccess'),
array('D:/' . md5('.htaccess.'), 'D:/.htaccess.'),
array('D:/' . md5('.htAccess'), 'D:/.htAccess'),
array('D:/' . md5('.htAccess\\…\\') . '/a', 'D:/.htAccess\…\/とa'),
array('D:/' . md5('.htaccess-'), 'D:/.htaccess-'),
array('D:/' . md5('.htaあccess'), 'D:/.htaあccess'),
array('D:/' . md5(' .htaccess'), 'D:/ .htaccess'),
array('D:/' . md5('.htaccess '), 'D:/.htaccess '),
array('D:/' . md5(' .htaccess '), 'D:/ .htaccess '),
);
}
/**
* @dataProvider slugifyPathData
*/
public function testSlugifyPath($slug, $path, $index = null) {
$this->assertEquals($slug, $this->mapper->slugifyPath($path, $index));
}
}

@ -1,43 +0,0 @@
<?php
/**
* ownCloud
*
* @author Robin Appelman
* @copyright 2012 Robin Appelman icewind@owncloud.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Test\Files\Storage;
class MappedLocal extends Storage {
/**
* @var string tmpDir
*/
private $tmpDir;
protected function setUp() {
parent::setUp();
$this->tmpDir=\OC_Helper::tmpFolder();
$this->instance=new \OC\Files\Storage\MappedLocal(array('datadir'=>$this->tmpDir));
}
protected function tearDown() {
\OC_Helper::rmdirr($this->tmpDir);
unset($this->instance);
parent::tearDown();
}
}

@ -1,45 +0,0 @@
<?php
/**
* ownCloud
*
* @author Robin Appelman
* @copyright 2012 Robin Appelman icewind@owncloud.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Test\Files\Storage;
class MappedLocalWithDottedDataDir extends Storage {
/**
* @var string tmpDir
*/
private $tmpDir;
protected function setUp() {
parent::setUp();
$this->tmpDir = \OC_Helper::tmpFolder().'dir.123'.DIRECTORY_SEPARATOR;
mkdir($this->tmpDir);
$this->instance=new \OC\Files\Storage\MappedLocal(array('datadir'=>$this->tmpDir));
}
protected function tearDown() {
\OC_Helper::rmdirr($this->tmpDir);
unset($this->instance);
parent::tearDown();
}
}

@ -99,7 +99,6 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase {
public static function tearDownAfterClass() {
$dataDir = \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data-autotest');
self::tearDownAfterClassCleanFileMapper($dataDir);
self::tearDownAfterClassCleanStorages();
self::tearDownAfterClassCleanFileCache();
self::tearDownAfterClassCleanStrayDataFiles($dataDir);
@ -109,18 +108,6 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase {
parent::tearDownAfterClass();
}
/**
* Remove all entries from the files map table
*
* @param string $dataDir
*/
static protected function tearDownAfterClassCleanFileMapper($dataDir) {
if (\OC_Util::runningOnWindows()) {
$mapper = new \OC\Files\Mapper($dataDir);
$mapper->removePath($dataDir, true, true);
}
}
/**
* Remove all entries from the storages table
*