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