2010-05-08 00:50:59 +04:00
< ? php
/**
* ownCloud
*
* @ author Frank Karlitschek
* @ copyright 2010 Frank Karlitschek karlitschek @ kde . org
*
* 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 .
*
2011-02-09 17:50:27 +03:00
* You should have received a copy of the GNU Affero General Public
2010-05-08 00:50:59 +04:00
* License along with this library . If not , see < http :// www . gnu . org / licenses />.
*
*/
/**
* Class for abstraction of filesystem functions
2011-07-29 23:36:03 +04:00
* This class won ' t call any filesystem functions for itself but but will pass them to the correct OC_Filestorage object
2010-05-08 00:50:59 +04:00
* this class should also handle all the file premission related stuff
2011-04-18 14:16:47 +04:00
*
* Hooks provided :
* read ( path )
2011-06-17 00:44:16 +04:00
* write ( path , & run )
2011-06-20 00:23:05 +04:00
* post_write ( path )
2011-06-17 00:44:16 +04:00
* create ( path , & run ) ( when a file is created , both create and write will be emited in that order )
2011-06-20 00:23:05 +04:00
* post_create ( path )
2011-06-17 00:44:16 +04:00
* delete ( path , & run )
2011-06-20 00:23:05 +04:00
* post_delete ( path )
2011-06-17 00:44:16 +04:00
* rename ( oldpath , newpath , & run )
2011-06-20 00:23:05 +04:00
* post_rename ( oldpath , newpath )
2011-06-17 00:44:16 +04:00
* copy ( oldpath , newpath , & run ) ( if the newpath doesn ' t exists yes , copy , create and write will be emited in that order )
2011-06-20 00:23:05 +04:00
* post_rename ( oldpath , newpath )
2011-06-17 00:44:16 +04:00
*
* the & run parameter can be set to false to prevent the operation from occuring
2010-05-08 00:50:59 +04:00
*/
2011-11-09 21:41:57 +04:00
2011-07-29 23:36:03 +04:00
class OC_Filesystem {
2010-05-08 00:50:59 +04:00
static private $storages = array ();
2011-10-18 23:01:49 +04:00
static private $mounts = array ();
2010-09-02 22:47:15 +04:00
static private $fakeRoot = '' ;
2010-09-06 22:02:17 +04:00
2011-06-12 02:57:43 +04:00
/**
* tear down the filesystem , removing all storage providers
*/
static public function tearDown (){
foreach ( self :: $storages as $mountpoint => $storage ){
unset ( self :: $storages [ $mountpoint ]);
}
$fakeRoot = '' ;
}
2010-09-06 22:02:17 +04:00
/**
* create a new storage of a specific type
* @ param string type
* @ param array arguments
2011-07-29 23:36:03 +04:00
* @ return OC_Filestorage
2010-09-06 22:02:17 +04:00
*/
2011-11-09 01:21:25 +04:00
static private function createStorage ( $class , $arguments ){
if ( class_exists ( $class )){
return new $class ( $arguments );
2010-09-06 22:02:17 +04:00
} else {
return false ;
}
}
2010-09-02 22:47:15 +04:00
/**
* change the root to a fake toor
* @ param string fakeRoot
* @ return bool
*/
static public function chroot ( $fakeRoot ){
2011-06-12 02:57:43 +04:00
if ( ! $fakeRoot == '' ){
if ( $fakeRoot [ 0 ] !== '/' ){
$fakeRoot = '/' . $fakeRoot ;
}
2010-09-02 22:47:15 +04:00
}
self :: $fakeRoot = $fakeRoot ;
}
2011-11-09 21:41:57 +04:00
/**
* get the fake root
* @ return string
*/
static public function getRoot (){
return self :: $fakeRoot ;
}
2010-09-02 22:47:15 +04:00
/**
* get the part of the path relative to the mountpoint of the storage it ' s stored in
* @ param string path
* @ return bool
*/
static public function getInternalPath ( $path ){
$mountPoint = self :: getMountPoint ( $path );
$path = self :: $fakeRoot . $path ;
$internalPath = substr ( $path , strlen ( $mountPoint ));
return $internalPath ;
}
2010-05-08 00:50:59 +04:00
/**
* check if the current users has the right premissions to read a file
* @ param string path
* @ return bool
*/
2010-06-25 15:24:27 +04:00
static private function canRead ( $path ){
if ( substr ( $path , 0 , 1 ) !== '/' ){
$path = '/' . $path ;
}
2011-01-04 01:46:18 +03:00
if ( strstr ( $path , '/../' ) || strrchr ( $path , '/' ) === '/..' ){
2010-06-25 15:24:27 +04:00
return false ;
}
2010-05-08 00:50:59 +04:00
return true ; //dummy untill premissions are correctly implemented, also the correcty value because for now users are locked in their seperate data dir and can read/write everything in there
}
/**
* check if the current users has the right premissions to write a file
* @ param string path
* @ return bool
*/
2010-06-25 15:24:27 +04:00
static private function canWrite ( $path ){
if ( substr ( $path , 0 , 1 ) !== '/' ){
$path = '/' . $path ;
}
2011-01-04 01:58:49 +03:00
if ( strstr ( $path , '/../' ) || strrchr ( $path , '/' ) === '/..' ){
2010-06-25 15:24:27 +04:00
return false ;
}
2010-05-08 00:50:59 +04:00
return true ; //dummy untill premissions are correctly implemented, also the correcty value because for now users are locked in their seperate data dir and can read/write everything in there
}
/**
2011-07-29 23:36:03 +04:00
* mount an OC_Filestorage in our virtual filesystem
* @ param OC_Filestorage storage
2010-05-08 00:50:59 +04:00
* @ param string mountpoint
*/
2011-11-09 01:21:25 +04:00
static public function mount ( $class , $arguments , $mountpoint ){
2010-05-08 00:50:59 +04:00
if ( substr ( $mountpoint , 0 , 1 ) !== '/' ){
$mountpoint = '/' . $mountpoint ;
}
2011-11-09 01:21:25 +04:00
self :: $mounts [ $mountpoint ] = array ( 'class' => $class , 'arguments' => $arguments );
2010-05-08 00:50:59 +04:00
}
/**
* get the storage object for a path
* @ param string path
2011-07-29 23:36:03 +04:00
* @ return OC_Filestorage
2010-05-08 00:50:59 +04:00
*/
2011-06-16 22:40:21 +04:00
static public function getStorage ( $path ){
2010-05-08 00:50:59 +04:00
$mountpoint = self :: getMountPoint ( $path );
if ( $mountpoint ){
2011-10-18 23:01:49 +04:00
if ( ! isset ( self :: $storages [ $mountpoint ])){
$mount = self :: $mounts [ $mountpoint ];
2011-11-09 01:21:25 +04:00
self :: $storages [ $mountpoint ] = self :: createStorage ( $mount [ 'class' ], $mount [ 'arguments' ]);
2011-10-18 23:01:49 +04:00
}
2010-05-08 00:50:59 +04:00
return self :: $storages [ $mountpoint ];
}
}
/**
* get the mountpoint of the storage object for a path
2010-09-02 22:47:15 +04:00
( note : because a storage is not always mounted inside the fakeroot , the returned mountpoint is relative to the absolute root of the filesystem and doesn ' t take the chroot into account
*
2010-05-08 00:50:59 +04:00
* @ param string path
* @ return string
*/
2011-06-18 21:49:52 +04:00
static public function getMountPoint ( $path ){
2010-05-08 00:50:59 +04:00
if ( ! $path ){
$path = '/' ;
}
if ( substr ( $path , 0 , 1 ) !== '/' ){
$path = '/' . $path ;
}
2010-10-04 03:07:55 +04:00
if ( substr ( $path , - 1 ) !== '/' ){
$path = $path . '/' ;
}
2010-09-02 22:47:15 +04:00
$path = self :: $fakeRoot . $path ;
2010-05-08 00:50:59 +04:00
$foundMountPoint = '' ;
2011-10-18 23:01:49 +04:00
foreach ( self :: $mounts as $mountpoint => $storage ){
2010-10-04 03:07:55 +04:00
if ( substr ( $mountpoint , - 1 ) !== '/' ){
$mountpoint = $mountpoint . '/' ;
}
2010-05-08 00:50:59 +04:00
if ( $mountpoint == $path ){
return $mountpoint ;
}
if ( strpos ( $path , $mountpoint ) === 0 and strlen ( $mountpoint ) > strlen ( $foundMountPoint )){
$foundMountPoint = $mountpoint ;
}
}
return $foundMountPoint ;
}
2011-07-05 19:56:02 +04:00
2011-04-22 19:50:04 +04:00
/**
* return the path to a local version of the file
* we need this because we can ' t know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed
* @ param string path
* @ return string
*/
static public function getLocalFile ( $path ){
$parent = substr ( $path , 0 , strrpos ( $path , '/' ));
if ( self :: canRead ( $parent ) and $storage = self :: getStorage ( $path )){
return $storage -> getLocalFile ( self :: getInternalPath ( $path ));
}
}
2010-05-08 00:50:59 +04:00
2011-11-09 01:36:05 +04:00
/**
* following functions are equivilent to their php buildin equivilents for arguments / return values .
*/
2010-05-08 00:50:59 +04:00
static public function mkdir ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'mkdir' , $path , array ( 'create' , 'write' ));
2010-05-08 00:50:59 +04:00
}
static public function rmdir ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'rmdir' , $path , array ( 'delete' ));
2010-05-08 00:50:59 +04:00
}
static public function opendir ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'opendir' , $path , array ( 'read' ));
2010-05-08 00:50:59 +04:00
}
static public function is_dir ( $path ){
if ( $path == '/' ){
return true ;
}
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'is_dir' , $path );
2010-05-08 00:50:59 +04:00
}
static public function is_file ( $path ){
if ( $path == '/' ){
return false ;
}
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'is_file' , $path );
2010-05-08 00:50:59 +04:00
}
static public function stat ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'stat' , $path );
2010-05-08 00:50:59 +04:00
}
static public function filetype ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'filetype' , $path );
2010-05-08 00:50:59 +04:00
}
static public function filesize ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'filesize' , $path );
2010-05-08 00:50:59 +04:00
}
static public function readfile ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'readfile' , $path , array ( 'read' ));
2010-05-08 00:50:59 +04:00
}
static public function is_readable ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'is_readable' , $path );
2010-05-08 00:50:59 +04:00
}
static public function is_writeable ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'is_writeable' , $path );
2010-05-08 00:50:59 +04:00
}
static public function file_exists ( $path ){
if ( $path == '/' ){
return true ;
}
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'file_exists' , $path );
2010-05-08 00:50:59 +04:00
}
static public function filectime ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'filectime' , $path );
2010-05-08 00:50:59 +04:00
}
static public function filemtime ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'filemtime' , $path );
2010-05-08 00:50:59 +04:00
}
static public function file_get_contents ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'file_get_contents' , $path , array ( 'read' ));
2010-05-08 00:50:59 +04:00
}
2010-07-07 14:30:30 +04:00
static public function file_put_contents ( $path , $data ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'file_put_contents' , $path , array ( 'create' , 'write' ), $data );
2010-05-08 00:50:59 +04:00
}
static public function unlink ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'unlink' , $path , array ( 'delete' ));
2010-05-08 00:50:59 +04:00
}
static public function rename ( $path1 , $path2 ){
2011-08-15 22:37:50 +04:00
if ( OC_FileProxy :: runPreProxies ( 'rename' , $path1 , $path2 ) and self :: canWrite ( $path1 ) and self :: canWrite ( $path2 )){
2011-06-17 00:44:16 +04:00
$run = true ;
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'rename' , array ( 'oldpath' => $path1 , 'newpath' => $path2 , 'run' => & $run ));
2011-06-17 00:44:16 +04:00
if ( $run ){
$mp1 = self :: getMountPoint ( $path1 );
$mp2 = self :: getMountPoint ( $path2 );
if ( $mp1 == $mp2 ){
if ( $storage = self :: getStorage ( $path1 )){
2011-06-20 00:23:05 +04:00
$result = $storage -> rename ( self :: getInternalPath ( $path1 ), self :: getInternalPath ( $path2 ));
2011-06-17 00:44:16 +04:00
}
} elseif ( $storage1 = self :: getStorage ( $path1 ) and $storage2 = self :: getStorage ( $path2 )){
$tmpFile = $storage1 -> toTmpFile ( self :: getInternalPath ( $path1 ));
2011-07-13 21:30:22 +04:00
$result = $storage2 -> fromTmpFile ( $tmpFile , self :: getInternalPath ( $path2 ));
2011-06-17 00:44:16 +04:00
$storage1 -> unlink ( self :: getInternalPath ( $path1 ));
2010-05-08 00:50:59 +04:00
}
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'post_rename' , array ( 'oldpath' => $path1 , 'newpath' => $path2 ));
2010-05-08 00:50:59 +04:00
return $result ;
}
}
}
static public function copy ( $path1 , $path2 ){
2011-08-15 22:37:50 +04:00
if ( OC_FileProxy :: runPreProxies ( 'copy' , $path1 , $path2 ) and self :: canRead ( $path1 ) and self :: canWrite ( $path2 )){
2011-06-17 00:44:16 +04:00
$run = true ;
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'copy' , array ( 'oldpath' => $path1 , 'newpath' => $path2 , 'run' => & $run ));
2011-06-20 00:23:05 +04:00
$exists = self :: file_exists ( $path2 );
if ( $run and ! $exists ){
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'create' , array ( 'path' => $path2 , 'run' => & $run ));
2011-06-17 00:44:16 +04:00
}
if ( $run ){
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'write' , array ( 'path' => $path2 , 'run' => & $run ));
2011-06-17 00:44:16 +04:00
}
if ( $run ){
$mp1 = self :: getMountPoint ( $path1 );
$mp2 = self :: getMountPoint ( $path2 );
if ( $mp1 == $mp2 ){
if ( $storage = self :: getStorage ( $path1 )){
2011-06-20 00:23:05 +04:00
$result = $storage -> copy ( self :: getInternalPath ( $path1 ), self :: getInternalPath ( $path2 ));
2011-06-17 00:44:16 +04:00
}
} elseif ( $storage1 = self :: getStorage ( $path1 ) and $storage2 = self :: getStorage ( $path2 )){
$tmpFile = $storage1 -> toTmpFile ( self :: getInternalPath ( $path1 ));
2011-07-13 04:50:04 +04:00
$result = $storage2 -> fromTmpFile ( $tmpFile , self :: getInternalPath ( $path2 ));
2010-05-08 00:50:59 +04:00
}
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'post_copy' , array ( 'oldpath' => $path1 , 'newpath' => $path2 ));
2011-06-20 00:23:05 +04:00
if ( ! $exists ){
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'post_create' , array ( 'path' => $path2 ));
2010-05-08 00:50:59 +04:00
}
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'post_write' , array ( 'path' => $path2 ));
2011-06-20 00:23:05 +04:00
return $result ;
2010-05-08 00:50:59 +04:00
}
}
}
static public function fopen ( $path , $mode ){
2011-08-15 22:37:50 +04:00
$hooks = array ();
switch ( $mode ){
case 'r' :
$hooks [] = 'read' ;
break ;
case 'r+' :
case 'w+' :
case 'x+' :
case 'a+' :
$hooks [] = 'read' ;
$hooks [] = 'write' ;
break ;
case 'w' :
case 'x' :
case 'a' :
$hooks [] = 'write' ;
break ;
2011-10-22 16:10:15 +04:00
default :
OC_Log :: write ( 'core' , 'invalid mode (' . $mode . ') for ' . $path , OC_Log :: ERROR );
2010-05-08 00:50:59 +04:00
}
2011-08-15 22:37:50 +04:00
2011-10-22 16:10:15 +04:00
return self :: basicOperation ( 'fopen' , $path , $hooks , $mode );
2010-05-08 00:50:59 +04:00
}
static public function toTmpFile ( $path ){
2011-08-15 22:37:50 +04:00
if ( OC_FileProxy :: runPreProxies ( 'toTmpFile' , $path ) and self :: canRead ( $path ) and $storage = self :: getStorage ( $path )){
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'read' , array ( 'path' => $path ));
2010-09-02 22:47:15 +04:00
return $storage -> toTmpFile ( self :: getInternalPath ( $path ));
2010-05-08 00:50:59 +04:00
}
}
static public function fromTmpFile ( $tmpFile , $path ){
2011-08-15 22:37:50 +04:00
if ( OC_FileProxy :: runPreProxies ( 'copy' , $tmpFile , $path ) and self :: canWrite ( $path ) and $storage = self :: getStorage ( $path )){
2011-06-17 00:44:16 +04:00
$run = true ;
2011-06-20 00:23:05 +04:00
$exists = self :: file_exists ( $path );
if ( ! $exists ){
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'create' , array ( 'path' => $path , 'run' => & $run ));
2011-06-17 00:44:16 +04:00
}
if ( $run ){
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'write' , array ( 'path' => $path , 'run' => & $run ));
2011-06-17 00:44:16 +04:00
}
if ( $run ){
2011-06-20 00:23:05 +04:00
$result = $storage -> fromTmpFile ( $tmpFile , self :: getInternalPath ( $path ));
if ( ! $exists ){
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'post_create' , array ( 'path' => $path ));
2011-06-20 00:23:05 +04:00
}
2011-07-29 23:36:03 +04:00
OC_Hook :: emit ( 'OC_Filesystem' , 'post_write' , array ( 'path' => $path ));
2011-06-20 00:23:05 +04:00
return $result ;
2011-04-18 14:16:47 +04:00
}
2010-05-08 00:50:59 +04:00
}
}
static public function getMimeType ( $path ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'getMimeType' , $path );
2010-05-08 00:50:59 +04:00
}
2011-08-15 22:37:50 +04:00
static public function hash ( $type , $path ){
return self :: basicOperation ( 'hash' , $path , array ( 'read' ));
2011-03-16 19:28:01 +03:00
}
2011-04-17 18:40:44 +04:00
static public function free_space ( $path = '/' ){
2011-08-15 22:37:50 +04:00
return self :: basicOperation ( 'free_space' , $path );
2011-04-17 18:40:44 +04:00
}
2011-04-24 18:09:07 +04:00
static public function search ( $query ){
$files = array ();
2011-07-31 17:35:37 +04:00
$fakeRoot = self :: $fakeRoot ;
$fakeRootLength = strlen ( $fakeRoot );
2011-04-24 18:09:07 +04:00
foreach ( self :: $storages as $mountpoint => $storage ){
$results = $storage -> search ( $query );
2011-06-17 00:44:16 +04:00
if ( is_array ( $results )){
foreach ( $results as $result ){
$file = str_replace ( '//' , '/' , $mountpoint . $result );
2011-07-31 17:35:37 +04:00
if ( substr ( $file , 0 , $fakeRootLength ) == $fakeRoot ){
$file = substr ( $file , $fakeRootLength );
$files [] = $file ;
}
2011-06-17 00:44:16 +04:00
}
2011-04-24 18:09:07 +04:00
}
}
return $files ;
}
2011-09-28 18:15:04 +04:00
2011-08-15 22:37:50 +04:00
/**
* abstraction for running most basic operations
* @ param string $operation
* @ param string #path
* @ param array ( optional ) hooks
* @ param mixed ( optional ) $extraParam
* @ return mixed
*/
private static function basicOperation ( $operation , $path , $hooks = array (), $extraParam = null ){
2011-09-23 19:32:14 +04:00
if ( OC_FileProxy :: runPreProxies ( $operation , $path , $extraParam ) and self :: canRead ( $path ) and $storage = self :: getStorage ( $path )){
2011-08-15 22:37:50 +04:00
$interalPath = self :: getInternalPath ( $path );
$run = true ;
foreach ( $hooks as $hook ){
if ( $hook != 'read' ){
OC_Hook :: emit ( 'OC_Filesystem' , $hook , array ( 'path' => $path , 'run' => & $run ));
} else {
OC_Hook :: emit ( 'OC_Filesystem' , $hook , array ( 'path' => $path ));
}
}
if ( $run ){
if ( $extraParam ){
$result = $storage -> $operation ( $interalPath , $extraParam );
} else {
$result = $storage -> $operation ( $interalPath );
}
$result = OC_FileProxy :: runPostProxies ( $operation , $path , $result );
foreach ( $hooks as $hook ){
if ( $hook != 'read' ){
OC_Hook :: emit ( 'OC_Filesystem' , 'post_' . $hook , array ( 'path' => $path ));
}
}
return $result ;
}
}
return null ;
}
2010-05-08 00:50:59 +04:00
}
2011-11-10 19:53:08 +04:00
require_once ( 'filecache.php' );