2011-07-27 17:52:24 +00:00
< ? php
2012-09-07 16:30:48 +00:00
/**
2016-07-21 15:07:57 +00:00
* @ copyright Copyright ( c ) 2016 , ownCloud , Inc .
*
2020-12-16 13:54:15 +00:00
* @ author aler9 < 46489434 + aler9 @ users . noreply . github . com >
2019-12-03 18:57:53 +00:00
* @ author Arthur Schiwon < blizzz @ arthur - schiwon . de >
2015-03-26 10:44:34 +00:00
* @ author Bart Visscher < bartv @ thisnet . nl >
2017-11-06 14:56:42 +00:00
* @ author Boris Rybalkin < ribalkin @ gmail . com >
2015-03-26 10:44:34 +00:00
* @ author Brice Maron < brice @ bmaron . net >
2020-03-31 08:49:10 +00:00
* @ author Christoph Wurst < christoph @ winzerhof - wurst . at >
2020-08-24 12:54:25 +00:00
* @ author J0WI < J0WI @ users . noreply . github . com >
2015-03-26 10:44:34 +00:00
* @ author Jakob Sack < mail @ jakobsack . de >
2016-07-21 15:07:57 +00:00
* @ author Joas Schilling < coding @ schilljs . com >
2021-06-04 19:52:51 +00:00
* @ author Johannes Leuker < j . leuker @ hosting . de >
2017-11-06 14:56:42 +00:00
* @ author Jörn Friedrich Dreyer < jfd @ butonic . de >
2015-03-26 10:44:34 +00:00
* @ author Klaas Freitag < freitag @ owncloud . com >
2017-11-06 14:56:42 +00:00
* @ author Lukas Reschke < lukas @ statuscode . ch >
2022-06-03 22:24:35 +00:00
* @ author Martin Brugnara < martin @ 0x6d62 . eu >
2015-03-26 10:44:34 +00:00
* @ author Michael Gapczynski < GapczynskiM @ gmail . com >
* @ author Morris Jobke < hey @ morrisjobke . de >
2016-07-21 16:13:36 +00:00
* @ author Robin Appelman < robin @ icewind . nl >
2017-11-06 14:56:42 +00:00
* @ author Roeland Jago Douma < roeland @ famdouma . nl >
2015-03-26 10:44:34 +00:00
* @ author Sjors van der Pluijm < sjors @ desjors . nl >
2016-05-26 17:56:05 +00:00
* @ author Stefan Weil < sw @ weilnetz . de >
2015-03-26 10:44:34 +00:00
* @ author Thomas Müller < thomas . mueller @ tmit . eu >
* @ author Tigran Mkrtchyan < tigran . mkrtchyan @ desy . de >
2020-12-16 13:54:15 +00:00
* @ author Vincent Petry < vincent @ nextcloud . com >
2015-03-26 10:44:34 +00:00
*
* @ 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 ,
2019-12-03 18:57:53 +00:00
* along with this program . If not , see < http :// www . gnu . org / licenses />
2015-03-26 10:44:34 +00:00
*
2012-09-07 16:30:48 +00:00
*/
namespace OC\Files\Storage ;
2016-05-30 13:44:19 +00:00
2019-06-27 09:10:08 +00:00
use OC\Files\Filesystem ;
2017-04-12 12:56:51 +00:00
use OC\Files\Storage\Wrapper\Jail ;
2020-02-14 21:07:09 +00:00
use OCP\Constants ;
2016-05-30 13:44:19 +00:00
use OCP\Files\ForbiddenException ;
2020-07-01 13:37:47 +00:00
use OCP\Files\GenericFileException ;
2022-03-17 15:05:52 +00:00
use OCP\Files\IMimeTypeDetector ;
2017-07-19 17:44:10 +00:00
use OCP\Files\Storage\IStorage ;
2022-03-17 15:05:52 +00:00
use OCP\IConfig ;
2022-03-31 08:57:10 +00:00
use Psr\Log\LoggerInterface ;
2016-05-30 13:44:19 +00:00
2015-07-03 13:41:29 +00:00
/**
* for local filestore , we only have to map the paths
*/
class Local extends \OC\Files\Storage\Common {
protected $datadir ;
2013-04-26 15:30:55 +00:00
2016-05-30 13:44:19 +00:00
protected $dataDirLength ;
protected $realDataDir ;
2022-03-17 15:05:52 +00:00
private IConfig $config ;
private IMimeTypeDetector $mimeTypeDetector ;
2022-06-03 22:24:35 +00:00
private $defUMask ;
2022-03-31 08:38:25 +00:00
protected bool $unlinkOnTruncate ;
2015-07-03 13:41:29 +00:00
public function __construct ( $arguments ) {
2016-04-25 12:36:53 +00:00
if ( ! isset ( $arguments [ 'datadir' ]) || ! is_string ( $arguments [ 'datadir' ])) {
throw new \InvalidArgumentException ( 'No data directory set for local storage' );
}
2018-01-14 22:44:59 +00:00
$this -> datadir = str_replace ( '//' , '/' , $arguments [ 'datadir' ]);
2016-09-08 10:11:53 +00:00
// some crazy code uses a local storage on root...
if ( $this -> datadir === '/' ) {
$this -> realDataDir = $this -> datadir ;
} else {
2018-01-14 22:44:59 +00:00
$realPath = realpath ( $this -> datadir ) ? : $this -> datadir ;
$this -> realDataDir = rtrim ( $realPath , '/' ) . '/' ;
2016-09-08 10:11:53 +00:00
}
2015-07-03 13:41:29 +00:00
if ( substr ( $this -> datadir , - 1 ) !== '/' ) {
$this -> datadir .= '/' ;
2011-07-27 17:52:24 +00:00
}
2016-05-30 13:44:19 +00:00
$this -> dataDirLength = strlen ( $this -> realDataDir );
2022-03-17 15:05:52 +00:00
$this -> config = \OC :: $server -> get ( IConfig :: class );
$this -> mimeTypeDetector = \OC :: $server -> get ( IMimeTypeDetector :: class );
2022-06-03 22:24:35 +00:00
$this -> defUMask = $this -> config -> getSystemValue ( 'localstorage.umask' , 0022 );
2022-03-31 08:38:25 +00:00
// support Write-Once-Read-Many file systems
$this -> unlinkOnTruncate = $this -> config -> getSystemValue ( 'localstorage.unlink_on_truncate' , false );
2015-07-03 13:41:29 +00:00
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
public function __destruct () {
}
2013-01-02 23:35:57 +00:00
2015-07-03 13:41:29 +00:00
public function getId () {
return 'local::' . $this -> datadir ;
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
public function mkdir ( $path ) {
2021-01-20 16:15:57 +00:00
$sourcePath = $this -> getSourcePath ( $path );
2022-06-03 22:24:35 +00:00
$oldMask = umask ( $this -> defUMask );
2021-01-20 16:15:57 +00:00
$result = @ mkdir ( $sourcePath , 0777 , true );
2021-01-22 14:44:24 +00:00
umask ( $oldMask );
2021-01-20 16:15:57 +00:00
return $result ;
2015-07-03 13:41:29 +00:00
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
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 ()) {
2014-04-28 17:32:25 +00:00
/**
2015-07-03 13:41:29 +00:00
* @ var \SplFileInfo $file
2014-04-28 17:32:25 +00:00
*/
2015-07-03 13:41:29 +00:00
$file = $it -> current ();
2020-07-03 20:42:27 +00:00
clearstatcache ( true , $this -> getSourcePath ( $file ));
2020-03-26 08:30:18 +00:00
if ( in_array ( $file -> getBasename (), [ '.' , '..' ])) {
2014-04-28 08:20:24 +00:00
$it -> next ();
2015-07-03 13:41:29 +00:00
continue ;
2020-04-10 08:35:09 +00:00
} elseif ( $file -> isDir ()) {
2015-07-03 13:41:29 +00:00
rmdir ( $file -> getPathname ());
2020-04-10 08:35:09 +00:00
} elseif ( $file -> isFile () || $file -> isLink ()) {
2015-07-03 13:41:29 +00:00
unlink ( $file -> getPathname ());
2013-06-06 18:47:20 +00:00
}
2015-07-03 13:41:29 +00:00
$it -> next ();
2013-06-06 18:47:20 +00:00
}
2020-07-03 20:42:27 +00:00
clearstatcache ( true , $this -> getSourcePath ( $path ));
2015-07-03 13:41:29 +00:00
return rmdir ( $this -> getSourcePath ( $path ));
} catch ( \UnexpectedValueException $e ) {
return false ;
2013-04-26 15:30:55 +00:00
}
2015-07-03 13:41:29 +00:00
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
public function opendir ( $path ) {
return opendir ( $this -> getSourcePath ( $path ));
}
2013-01-04 22:03:26 +00:00
2015-07-03 13:41:29 +00:00
public function is_dir ( $path ) {
if ( substr ( $path , - 1 ) == '/' ) {
$path = substr ( $path , 0 , - 1 );
2011-07-27 17:52:24 +00:00
}
2015-07-03 13:41:29 +00:00
return is_dir ( $this -> getSourcePath ( $path ));
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
public function is_file ( $path ) {
return is_file ( $this -> getSourcePath ( $path ));
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
public function stat ( $path ) {
$fullPath = $this -> getSourcePath ( $path );
2020-07-03 20:44:31 +00:00
clearstatcache ( true , $fullPath );
2022-08-16 15:17:51 +00:00
if ( ! file_exists ( $fullPath )) {
return false ;
}
2020-10-07 11:50:29 +00:00
$statResult = @ stat ( $fullPath );
if ( PHP_INT_SIZE === 4 && $statResult && ! $this -> is_dir ( $path )) {
2015-07-03 13:41:29 +00:00
$filesize = $this -> filesize ( $path );
$statResult [ 'size' ] = $filesize ;
$statResult [ 7 ] = $filesize ;
2012-02-29 22:42:40 +00:00
}
2022-03-17 15:05:52 +00:00
if ( is_array ( $statResult )) {
$statResult [ 'full_path' ] = $fullPath ;
}
2015-07-03 13:41:29 +00:00
return $statResult ;
}
2013-04-26 15:30:55 +00:00
2020-02-14 21:07:09 +00:00
/**
* @ inheritdoc
*/
public function getMetaData ( $path ) {
2022-04-20 11:01:16 +00:00
try {
$stat = $this -> stat ( $path );
} catch ( ForbiddenException $e ) {
return null ;
}
2020-02-14 21:07:09 +00:00
if ( ! $stat ) {
return null ;
}
$permissions = Constants :: PERMISSION_SHARE ;
$statPermissions = $stat [ 'mode' ];
2021-06-17 10:44:44 +00:00
$isDir = ( $statPermissions & 0x4000 ) === 0x4000 && ! ( $statPermissions & 0x8000 );
2020-02-14 21:07:09 +00:00
if ( $statPermissions & 0x0100 ) {
$permissions += Constants :: PERMISSION_READ ;
}
if ( $statPermissions & 0x0080 ) {
$permissions += Constants :: PERMISSION_UPDATE ;
if ( $isDir ) {
$permissions += Constants :: PERMISSION_CREATE ;
}
}
if ( ! ( $path === '' || $path === '/' )) { // deletable depends on the parents unix permissions
2022-03-17 15:05:52 +00:00
$parent = dirname ( $stat [ 'full_path' ]);
2020-02-14 21:07:09 +00:00
if ( is_writable ( $parent )) {
$permissions += Constants :: PERMISSION_DELETE ;
}
}
$data = [];
2022-03-17 15:05:52 +00:00
$data [ 'mimetype' ] = $isDir ? 'httpd/unix-directory' : $this -> mimeTypeDetector -> detectPath ( $path );
2020-02-14 21:07:09 +00:00
$data [ 'mtime' ] = $stat [ 'mtime' ];
if ( $data [ 'mtime' ] === false ) {
$data [ 'mtime' ] = time ();
}
if ( $isDir ) {
$data [ 'size' ] = - 1 ; //unknown
} else {
$data [ 'size' ] = $stat [ 'size' ];
}
$data [ 'etag' ] = $this -> calculateEtag ( $path , $stat );
$data [ 'storage_mtime' ] = $data [ 'mtime' ];
$data [ 'permissions' ] = $permissions ;
2020-03-27 16:47:20 +00:00
$data [ 'name' ] = basename ( $path );
2020-02-14 21:07:09 +00:00
return $data ;
}
2015-07-03 13:41:29 +00:00
public function filetype ( $path ) {
$filetype = filetype ( $this -> getSourcePath ( $path ));
if ( $filetype == 'link' ) {
$filetype = filetype ( realpath ( $this -> getSourcePath ( $path )));
2012-02-14 08:59:54 +00:00
}
2015-07-03 13:41:29 +00:00
return $filetype ;
}
2012-08-29 06:38:33 +00:00
2015-07-03 13:41:29 +00:00
public function filesize ( $path ) {
2021-10-04 15:21:37 +00:00
if ( ! $this -> is_file ( $path )) {
2015-07-03 13:41:29 +00:00
return 0 ;
2012-04-01 17:30:41 +00:00
}
2015-07-03 13:41:29 +00:00
$fullPath = $this -> getSourcePath ( $path );
if ( PHP_INT_SIZE === 4 ) {
$helper = new \OC\LargeFileHelper ;
2016-10-25 09:37:16 +00:00
return $helper -> getFileSize ( $fullPath );
2011-11-01 21:35:13 +00:00
}
2015-07-03 13:41:29 +00:00
return filesize ( $fullPath );
}
2011-11-01 21:35:13 +00:00
2015-07-03 13:41:29 +00:00
public function isReadable ( $path ) {
return is_readable ( $this -> getSourcePath ( $path ));
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
public function isUpdatable ( $path ) {
return is_writable ( $this -> getSourcePath ( $path ));
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
public function file_exists ( $path ) {
return file_exists ( $this -> getSourcePath ( $path ));
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
public function filemtime ( $path ) {
2016-09-29 09:10:35 +00:00
$fullPath = $this -> getSourcePath ( $path );
2018-01-12 12:59:58 +00:00
clearstatcache ( true , $fullPath );
2016-09-29 09:10:35 +00:00
if ( ! $this -> file_exists ( $path )) {
return false ;
}
if ( PHP_INT_SIZE === 4 ) {
2016-10-25 09:42:16 +00:00
$helper = new \OC\LargeFileHelper ();
return $helper -> getFileMtime ( $fullPath );
2016-09-29 09:10:35 +00:00
}
return filemtime ( $fullPath );
2015-07-03 13:41:29 +00:00
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
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 ;
2013-04-26 15:30:55 +00:00
}
2022-06-03 22:24:35 +00:00
$oldMask = umask ( $this -> defUMask );
2015-07-03 13:41:29 +00:00
if ( ! is_null ( $mtime )) {
2019-07-26 15:26:59 +00:00
$result = @ touch ( $this -> getSourcePath ( $path ), $mtime );
2015-07-03 13:41:29 +00:00
} else {
2019-07-26 15:26:59 +00:00
$result = @ touch ( $this -> getSourcePath ( $path ));
2013-04-26 15:30:55 +00:00
}
2021-01-22 14:44:24 +00:00
umask ( $oldMask );
2015-07-03 13:41:29 +00:00
if ( $result ) {
clearstatcache ( true , $this -> getSourcePath ( $path ));
2013-04-26 15:30:55 +00:00
}
2015-07-03 13:41:29 +00:00
return $result ;
}
2014-07-10 08:54:26 +00:00
2015-07-03 13:41:29 +00:00
public function file_get_contents ( $path ) {
2017-01-12 15:07:41 +00:00
return file_get_contents ( $this -> getSourcePath ( $path ));
2015-07-03 13:41:29 +00:00
}
2014-07-10 08:54:26 +00:00
2015-07-03 13:41:29 +00:00
public function file_put_contents ( $path , $data ) {
2022-06-03 22:24:35 +00:00
$oldMask = umask ( $this -> defUMask );
2022-03-31 08:38:25 +00:00
if ( $this -> unlinkOnTruncate ) {
$this -> unlink ( $path );
}
2021-01-22 14:44:24 +00:00
$result = file_put_contents ( $this -> getSourcePath ( $path ), $data );
umask ( $oldMask );
return $result ;
2015-07-03 13:41:29 +00:00
}
2014-07-10 08:54:26 +00:00
2015-07-03 13:41:29 +00:00
public function unlink ( $path ) {
if ( $this -> is_dir ( $path )) {
return $this -> rmdir ( $path );
2020-04-10 08:35:09 +00:00
} elseif ( $this -> is_file ( $path )) {
2015-07-03 13:41:29 +00:00
return unlink ( $this -> getSourcePath ( $path ));
} else {
return false ;
}
}
2013-07-01 15:45:01 +00:00
2020-06-19 12:57:58 +00:00
private function checkTreeForForbiddenItems ( string $path ) {
2019-06-27 09:10:08 +00:00
$iterator = new \RecursiveIteratorIterator ( new \RecursiveDirectoryIterator ( $path ));
foreach ( $iterator as $file ) {
/** @var \SplFileInfo $file */
if ( Filesystem :: isFileBlacklisted ( $file -> getBasename ())) {
2020-06-19 12:57:58 +00:00
throw new ForbiddenException ( 'Invalid path: ' . $file -> getPathname (), false );
2019-06-27 09:10:08 +00:00
}
}
}
2015-07-03 13:41:29 +00:00
public function rename ( $path1 , $path2 ) {
$srcParent = dirname ( $path1 );
$dstParent = dirname ( $path2 );
2015-03-31 11:47:06 +00:00
2015-07-03 13:41:29 +00:00
if ( ! $this -> isUpdatable ( $srcParent )) {
2022-03-31 08:57:10 +00:00
\OC :: $server -> get ( LoggerInterface :: class ) -> error ( 'unable to rename, source directory is not writable : ' . $srcParent , [ 'app' => 'core' ]);
2015-07-03 13:41:29 +00:00
return false ;
2011-07-27 17:52:24 +00:00
}
2015-07-03 13:41:29 +00:00
if ( ! $this -> isUpdatable ( $dstParent )) {
2022-03-31 08:57:10 +00:00
\OC :: $server -> get ( LoggerInterface :: class ) -> error ( 'unable to rename, destination directory is not writable : ' . $dstParent , [ 'app' => 'core' ]);
2015-07-03 13:41:29 +00:00
return false ;
2011-07-27 17:52:24 +00:00
}
2015-07-03 13:41:29 +00:00
if ( ! $this -> file_exists ( $path1 )) {
2022-03-31 08:57:10 +00:00
\OC :: $server -> get ( LoggerInterface :: class ) -> error ( 'unable to rename, file does not exists : ' . $path1 , [ 'app' => 'core' ]);
2015-07-03 13:41:29 +00:00
return false ;
2011-07-27 17:52:24 +00:00
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
if ( $this -> is_dir ( $path2 )) {
$this -> rmdir ( $path2 );
2020-04-10 08:35:09 +00:00
} elseif ( $this -> is_file ( $path2 )) {
2015-07-03 13:41:29 +00:00
$this -> unlink ( $path2 );
2013-04-26 15:30:55 +00:00
}
2013-01-04 22:03:26 +00:00
2015-07-03 13:41:29 +00:00
if ( $this -> is_dir ( $path1 )) {
2016-04-07 17:51:27 +00:00
// we can't move folders across devices, use copy instead
2015-07-03 13:41:29 +00:00
$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 ;
2013-04-26 15:30:55 +00:00
}
2019-06-27 09:10:08 +00:00
2020-06-19 12:57:58 +00:00
$this -> checkTreeForForbiddenItems ( $this -> getSourcePath ( $path1 ));
2013-04-26 15:30:55 +00:00
}
2011-07-27 17:52:24 +00:00
2015-07-03 13:41:29 +00:00
return rename ( $this -> getSourcePath ( $path1 ), $this -> getSourcePath ( $path2 ));
}
2011-07-27 17:52:24 +00:00
2015-07-03 13:41:29 +00:00
public function copy ( $path1 , $path2 ) {
if ( $this -> is_dir ( $path1 )) {
return parent :: copy ( $path1 , $path2 );
} else {
2022-06-03 22:24:35 +00:00
$oldMask = umask ( $this -> defUMask );
2022-03-31 08:38:25 +00:00
if ( $this -> unlinkOnTruncate ) {
$this -> unlink ( $path2 );
}
2021-01-22 14:44:24 +00:00
$result = copy ( $this -> getSourcePath ( $path1 ), $this -> getSourcePath ( $path2 ));
umask ( $oldMask );
return $result ;
2013-04-26 15:30:55 +00:00
}
2015-07-03 13:41:29 +00:00
}
2011-07-27 17:52:24 +00:00
2015-07-03 13:41:29 +00:00
public function fopen ( $path , $mode ) {
2022-08-16 15:17:51 +00:00
$sourcePath = $this -> getSourcePath ( $path );
if ( ! file_exists ( $sourcePath ) && $mode === 'r' ) {
return false ;
}
2022-06-03 22:24:35 +00:00
$oldMask = umask ( $this -> defUMask );
2022-03-31 08:38:25 +00:00
if (( $mode === 'w' || $mode === 'w+' ) && $this -> unlinkOnTruncate ) {
2020-12-07 17:24:37 +00:00
$this -> unlink ( $path );
}
2022-08-16 15:17:51 +00:00
$result = @ fopen ( $sourcePath , $mode );
2021-01-22 14:44:24 +00:00
umask ( $oldMask );
return $result ;
2015-07-03 13:41:29 +00:00
}
2013-04-26 15:30:55 +00:00
2015-07-03 13:41:29 +00:00
public function hash ( $type , $path , $raw = false ) {
return hash_file ( $type , $this -> getSourcePath ( $path ), $raw );
}
2014-11-26 15:58:25 +00:00
2015-07-03 13:41:29 +00:00
public function free_space ( $path ) {
2016-03-07 09:58:24 +00:00
$sourcePath = $this -> getSourcePath ( $path );
// using !is_dir because $sourcePath might be a part file or
// non-existing file, so we'd still want to use the parent dir
// in such cases
if ( ! is_dir ( $sourcePath )) {
// disk_free_space doesn't work on files
$sourcePath = dirname ( $sourcePath );
}
2021-11-18 10:46:13 +00:00
$space = function_exists ( 'disk_free_space' ) ? disk_free_space ( $sourcePath ) : false ;
2015-07-03 13:41:29 +00:00
if ( $space === false || is_null ( $space )) {
return \OCP\Files\FileInfo :: SPACE_UNKNOWN ;
2011-07-27 17:52:24 +00:00
}
2015-07-03 13:41:29 +00:00
return $space ;
}
2011-07-27 17:52:24 +00:00
2015-07-03 13:41:29 +00:00
public function search ( $query ) {
return $this -> searchInDir ( $query );
}
public function getLocalFile ( $path ) {
return $this -> getSourcePath ( $path );
}
public function getLocalFolder ( $path ) {
return $this -> getSourcePath ( $path );
}
2014-02-04 18:58:49 +00:00
2015-07-03 13:41:29 +00:00
/**
* @ param string $query
* @ param string $dir
* @ return array
*/
protected function searchInDir ( $query , $dir = '' ) {
2020-03-26 08:30:18 +00:00
$files = [];
2015-07-03 13:41:29 +00:00
$physicalDir = $this -> getSourcePath ( $dir );
foreach ( scandir ( $physicalDir ) as $item ) {
2020-04-10 12:19:56 +00:00
if ( \OC\Files\Filesystem :: isIgnoredDir ( $item )) {
2015-07-03 13:41:29 +00:00
continue ;
2020-04-10 12:19:56 +00:00
}
2015-07-03 13:41:29 +00:00
$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 ));
}
2014-11-26 15:58:25 +00:00
}
2015-07-03 13:41:29 +00:00
return $files ;
}
2014-11-26 15:58:25 +00:00
2015-07-03 13:41:29 +00:00
/**
* 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 {
2014-02-04 18:58:49 +00:00
return true ;
}
2015-07-03 13:41:29 +00:00
}
2014-10-24 14:07:45 +00:00
2015-07-03 13:41:29 +00:00
/**
* Get the source path ( on disk ) of a given path
*
* @ param string $path
* @ return string
2016-05-30 13:44:19 +00:00
* @ throws ForbiddenException
2015-07-03 13:41:29 +00:00
*/
public function getSourcePath ( $path ) {
2019-06-27 09:10:08 +00:00
if ( Filesystem :: isFileBlacklisted ( $path )) {
2020-06-19 12:57:58 +00:00
throw new ForbiddenException ( 'Invalid path: ' . $path , false );
2019-06-27 09:10:08 +00:00
}
2015-07-03 13:41:29 +00:00
$fullPath = $this -> datadir . $path ;
2018-01-14 22:44:59 +00:00
$currentPath = $path ;
2022-03-17 15:05:52 +00:00
$allowSymlinks = $this -> config -> getSystemValue ( 'localstorage.allowsymlinks' , false );
2020-12-08 14:12:04 +00:00
if ( $allowSymlinks || $currentPath === '' ) {
2016-05-30 13:44:19 +00:00
return $fullPath ;
}
$pathToResolve = $fullPath ;
$realPath = realpath ( $pathToResolve );
while ( $realPath === false ) { // for non existing files check the parent directory
2018-01-14 22:44:59 +00:00
$currentPath = dirname ( $currentPath );
if ( $currentPath === '' || $currentPath === '.' ) {
return $fullPath ;
}
$realPath = realpath ( $this -> datadir . $currentPath );
2016-05-30 13:44:19 +00:00
}
if ( $realPath ) {
$realPath = $realPath . '/' ;
}
if ( substr ( $realPath , 0 , $this -> dataDirLength ) === $this -> realDataDir ) {
return $fullPath ;
}
2017-03-28 22:34:33 +00:00
2022-03-31 08:57:10 +00:00
\OC :: $server -> get ( LoggerInterface :: class ) -> error ( " Following symlinks is not allowed (' $fullPath ' -> ' $realPath ' not inside ' { $this -> realDataDir } ') " , [ 'app' => 'core' ]);
2017-03-28 22:34:33 +00:00
throw new ForbiddenException ( 'Following symlinks is not allowed' , false );
2015-07-03 13:41:29 +00:00
}
/**
* { @ inheritdoc }
*/
public function isLocal () {
return true ;
}
/**
* get the ETag for a file or folder
*
* @ param string $path
* @ return string
*/
public function getETag ( $path ) {
2020-02-14 21:07:09 +00:00
return $this -> calculateEtag ( $path , $this -> stat ( $path ));
}
2019-11-23 13:14:41 +00:00
2020-02-14 21:07:09 +00:00
private function calculateEtag ( string $path , array $stat ) : string {
2021-06-17 10:44:44 +00:00
if ( $stat [ 'mode' ] & 0x4000 && ! ( $stat [ 'mode' ] & 0x8000 )) { // is_dir & not socket
2020-02-14 21:07:09 +00:00
return parent :: getETag ( $path );
} else {
2019-11-23 13:14:41 +00:00
if ( $stat === false ) {
return md5 ( '' );
}
$toHash = '' ;
if ( isset ( $stat [ 'mtime' ])) {
$toHash .= $stat [ 'mtime' ];
}
if ( isset ( $stat [ 'ino' ])) {
$toHash .= $stat [ 'ino' ];
}
if ( isset ( $stat [ 'dev' ])) {
$toHash .= $stat [ 'dev' ];
}
if ( isset ( $stat [ 'size' ])) {
$toHash .= $stat [ 'size' ];
}
return md5 ( $toHash );
2014-10-24 14:07:45 +00:00
}
2015-07-03 13:41:29 +00:00
}
2015-01-16 12:33:17 +00:00
2015-07-03 13:41:29 +00:00
/**
2017-07-19 17:44:10 +00:00
* @ param IStorage $sourceStorage
2015-07-03 13:41:29 +00:00
* @ param string $sourceInternalPath
* @ param string $targetInternalPath
2017-10-28 09:49:01 +00:00
* @ param bool $preserveMtime
2015-07-03 13:41:29 +00:00
* @ return bool
*/
2017-07-19 17:44:10 +00:00
public function copyFromStorage ( IStorage $sourceStorage , $sourceInternalPath , $targetInternalPath , $preserveMtime = false ) {
2021-10-21 14:57:07 +00:00
// Don't treat ACLStorageWrapper like local storage where copy can be done directly.
// Instead use the slower recursive copying in php from Common::copyFromStorage with
// more permissions checks.
if ( $sourceStorage -> instanceOfStorage ( Local :: class ) && ! $sourceStorage -> instanceOfStorage ( 'OCA\GroupFolders\ACL\ACLStorageWrapper' )) {
2017-08-09 09:56:31 +00:00
if ( $sourceStorage -> instanceOfStorage ( Jail :: class )) {
/**
* @ var \OC\Files\Storage\Wrapper\Jail $sourceStorage
*/
$sourceInternalPath = $sourceStorage -> getUnjailedPath ( $sourceInternalPath );
}
2015-07-03 13:41:29 +00:00
/**
* @ 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 );
2015-01-16 12:33:17 +00:00
}
2015-07-03 13:41:29 +00:00
}
2015-01-16 12:33:17 +00:00
2015-07-03 13:41:29 +00:00
/**
2017-07-19 17:44:10 +00:00
* @ param IStorage $sourceStorage
2015-07-03 13:41:29 +00:00
* @ param string $sourceInternalPath
* @ param string $targetInternalPath
* @ return bool
*/
2017-07-19 17:44:10 +00:00
public function moveFromStorage ( IStorage $sourceStorage , $sourceInternalPath , $targetInternalPath ) {
2017-04-12 12:56:51 +00:00
if ( $sourceStorage -> instanceOfStorage ( Local :: class )) {
if ( $sourceStorage -> instanceOfStorage ( Jail :: class )) {
/**
* @ var \OC\Files\Storage\Wrapper\Jail $sourceStorage
*/
$sourceInternalPath = $sourceStorage -> getUnjailedPath ( $sourceInternalPath );
}
2015-07-03 13:41:29 +00:00
/**
* @ 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 );
2015-01-16 12:33:17 +00:00
}
2012-06-15 14:43:24 +00:00
}
2018-10-26 17:15:23 +00:00
public function writeStream ( string $path , $stream , int $size = null ) : int {
2020-12-07 16:57:07 +00:00
$result = $this -> file_put_contents ( $path , $stream );
2021-09-20 17:20:51 +00:00
if ( is_resource ( $stream )) {
fclose ( $stream );
}
2020-07-01 13:37:47 +00:00
if ( $result === false ) {
2021-09-20 17:20:51 +00:00
throw new GenericFileException ( " Failed write stream to $path " );
2020-07-01 13:37:47 +00:00
} else {
return $result ;
}
2018-10-26 17:15:23 +00:00
}
2011-07-27 17:52:24 +00:00
}