summaryrefslogtreecommitdiff
path: root/includes/filebackend/FSFileBackend.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/filebackend/FSFileBackend.php')
-rw-r--r--includes/filebackend/FSFileBackend.php201
1 files changed, 121 insertions, 80 deletions
diff --git a/includes/filebackend/FSFileBackend.php b/includes/filebackend/FSFileBackend.php
index 6d642162..b99ffb62 100644
--- a/includes/filebackend/FSFileBackend.php
+++ b/includes/filebackend/FSFileBackend.php
@@ -39,14 +39,22 @@
* @since 1.19
*/
class FSFileBackend extends FileBackendStore {
- protected $basePath; // string; directory holding the container directories
- /** @var Array Map of container names to root paths */
- protected $containerPaths = array(); // for custom container paths
- protected $fileMode; // integer; file permission mode
- protected $fileOwner; // string; required OS username to own files
- protected $currentUser; // string; OS username running this script
-
- /** @var Array */
+ /** @var string Directory holding the container directories */
+ protected $basePath;
+
+ /** @var array Map of container names to root paths for custom container paths */
+ protected $containerPaths = array();
+
+ /** @var int File permission mode */
+ protected $fileMode;
+
+ /** @var string Required OS username to own files */
+ protected $fileOwner;
+
+ /** @var string OS username running this script */
+ protected $currentUser;
+
+ /** @var array */
protected $hadWarningErrors = array();
/**
@@ -82,6 +90,10 @@ class FSFileBackend extends FileBackendStore {
}
}
+ public function getFeatures() {
+ return !wfIsWindows() ? FileBackend::ATTR_UNICODE_PATHS : 0;
+ }
+
protected function resolveContainerPath( $container, $relStoragePath ) {
// Check that container has a root directory
if ( isset( $this->containerPaths[$container] ) || isset( $this->basePath ) ) {
@@ -90,6 +102,7 @@ class FSFileBackend extends FileBackendStore {
return $relStoragePath;
}
}
+
return null;
}
@@ -125,6 +138,7 @@ class FSFileBackend extends FileBackendStore {
} elseif ( isset( $this->basePath ) ) {
return "{$this->basePath}/{$fullCont}";
}
+
return null; // no container base path defined
}
@@ -144,6 +158,7 @@ class FSFileBackend extends FileBackendStore {
if ( $relPath != '' ) {
$fsPath .= "/{$relPath}";
}
+
return $fsPath;
}
@@ -174,6 +189,7 @@ class FSFileBackend extends FileBackendStore {
$dest = $this->resolveToFSPath( $params['dst'] );
if ( $dest === null ) {
$status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+
return $status;
}
@@ -181,6 +197,7 @@ class FSFileBackend extends FileBackendStore {
$tempFile = TempFSFile::factory( 'create_', 'tmp' );
if ( !$tempFile ) {
$status->fatal( 'backend-fail-create', $params['dst'] );
+
return $status;
}
$this->trapWarnings();
@@ -188,6 +205,7 @@ class FSFileBackend extends FileBackendStore {
$this->untrapWarnings();
if ( $bytes === false ) {
$status->fatal( 'backend-fail-create', $params['dst'] );
+
return $status;
}
$cmd = implode( ' ', array(
@@ -195,7 +213,13 @@ class FSFileBackend extends FileBackendStore {
wfEscapeShellArg( $this->cleanPathSlashes( $tempFile->getPath() ) ),
wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
) );
- $status->value = new FSFileOpHandle( $this, $params, 'Create', $cmd, $dest );
+ $handler = function ( $errors, Status $status, array $params, $cmd ) {
+ if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
+ $status->fatal( 'backend-fail-create', $params['dst'] );
+ trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
+ }
+ };
+ $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd, $dest );
$tempFile->bind( $status->value );
} else { // immediate write
$this->trapWarnings();
@@ -203,6 +227,7 @@ class FSFileBackend extends FileBackendStore {
$this->untrapWarnings();
if ( $bytes === false ) {
$status->fatal( 'backend-fail-create', $params['dst'] );
+
return $status;
}
$this->chmod( $dest );
@@ -211,22 +236,13 @@ class FSFileBackend extends FileBackendStore {
return $status;
}
- /**
- * @see FSFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseCreate( $errors, Status $status, array $params, $cmd ) {
- if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
- $status->fatal( 'backend-fail-create', $params['dst'] );
- trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
- }
- }
-
protected function doStoreInternal( array $params ) {
$status = Status::newGood();
$dest = $this->resolveToFSPath( $params['dst'] );
if ( $dest === null ) {
$status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+
return $status;
}
@@ -236,7 +252,13 @@ class FSFileBackend extends FileBackendStore {
wfEscapeShellArg( $this->cleanPathSlashes( $params['src'] ) ),
wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
) );
- $status->value = new FSFileOpHandle( $this, $params, 'Store', $cmd, $dest );
+ $handler = function ( $errors, Status $status, array $params, $cmd ) {
+ if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
+ $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
+ trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
+ }
+ };
+ $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd, $dest );
} else { // immediate write
$this->trapWarnings();
$ok = copy( $params['src'], $dest );
@@ -248,6 +270,7 @@ class FSFileBackend extends FileBackendStore {
trigger_error( __METHOD__ . ": copy() failed but returned true." );
}
$status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
+
return $status;
}
$this->chmod( $dest );
@@ -256,28 +279,20 @@ class FSFileBackend extends FileBackendStore {
return $status;
}
- /**
- * @see FSFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseStore( $errors, Status $status, array $params, $cmd ) {
- if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
- $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
- trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
- }
- }
-
protected function doCopyInternal( array $params ) {
$status = Status::newGood();
$source = $this->resolveToFSPath( $params['src'] );
if ( $source === null ) {
$status->fatal( 'backend-fail-invalidpath', $params['src'] );
+
return $status;
}
$dest = $this->resolveToFSPath( $params['dst'] );
if ( $dest === null ) {
$status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+
return $status;
}
@@ -285,6 +300,7 @@ class FSFileBackend extends FileBackendStore {
if ( empty( $params['ignoreMissingSource'] ) ) {
$status->fatal( 'backend-fail-copy', $params['src'] );
}
+
return $status; // do nothing; either OK or bad status
}
@@ -294,7 +310,13 @@ class FSFileBackend extends FileBackendStore {
wfEscapeShellArg( $this->cleanPathSlashes( $source ) ),
wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
) );
- $status->value = new FSFileOpHandle( $this, $params, 'Copy', $cmd, $dest );
+ $handler = function ( $errors, Status $status, array $params, $cmd ) {
+ if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+ trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
+ }
+ };
+ $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd, $dest );
} else { // immediate write
$this->trapWarnings();
$ok = ( $source === $dest ) ? true : copy( $source, $dest );
@@ -308,6 +330,7 @@ class FSFileBackend extends FileBackendStore {
trigger_error( __METHOD__ . ": copy() failed but returned true." );
}
$status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
+
return $status;
}
$this->chmod( $dest );
@@ -316,28 +339,20 @@ class FSFileBackend extends FileBackendStore {
return $status;
}
- /**
- * @see FSFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseCopy( $errors, Status $status, array $params, $cmd ) {
- if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
- $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
- trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
- }
- }
-
protected function doMoveInternal( array $params ) {
$status = Status::newGood();
$source = $this->resolveToFSPath( $params['src'] );
if ( $source === null ) {
$status->fatal( 'backend-fail-invalidpath', $params['src'] );
+
return $status;
}
$dest = $this->resolveToFSPath( $params['dst'] );
if ( $dest === null ) {
$status->fatal( 'backend-fail-invalidpath', $params['dst'] );
+
return $status;
}
@@ -345,6 +360,7 @@ class FSFileBackend extends FileBackendStore {
if ( empty( $params['ignoreMissingSource'] ) ) {
$status->fatal( 'backend-fail-move', $params['src'] );
}
+
return $status; // do nothing; either OK or bad status
}
@@ -354,7 +370,13 @@ class FSFileBackend extends FileBackendStore {
wfEscapeShellArg( $this->cleanPathSlashes( $source ) ),
wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
) );
- $status->value = new FSFileOpHandle( $this, $params, 'Move', $cmd );
+ $handler = function ( $errors, Status $status, array $params, $cmd ) {
+ if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
+ $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
+ trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
+ }
+ };
+ $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd );
} else { // immediate write
$this->trapWarnings();
$ok = ( $source === $dest ) ? true : rename( $source, $dest );
@@ -362,6 +384,7 @@ class FSFileBackend extends FileBackendStore {
clearstatcache(); // file no longer at source
if ( !$ok ) {
$status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
+
return $status;
}
}
@@ -369,22 +392,13 @@ class FSFileBackend extends FileBackendStore {
return $status;
}
- /**
- * @see FSFileBackend::doExecuteOpHandlesInternal()
- */
- protected function _getResponseMove( $errors, Status $status, array $params, $cmd ) {
- if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
- $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
- trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
- }
- }
-
protected function doDeleteInternal( array $params ) {
$status = Status::newGood();
$source = $this->resolveToFSPath( $params['src'] );
if ( $source === null ) {
$status->fatal( 'backend-fail-invalidpath', $params['src'] );
+
return $status;
}
@@ -392,6 +406,7 @@ class FSFileBackend extends FileBackendStore {
if ( empty( $params['ignoreMissingSource'] ) ) {
$status->fatal( 'backend-fail-delete', $params['src'] );
}
+
return $status; // do nothing; either OK or bad status
}
@@ -400,13 +415,20 @@ class FSFileBackend extends FileBackendStore {
wfIsWindows() ? 'DEL' : 'unlink',
wfEscapeShellArg( $this->cleanPathSlashes( $source ) )
) );
- $status->value = new FSFileOpHandle( $this, $params, 'Copy', $cmd );
+ $handler = function ( $errors, Status $status, array $params, $cmd ) {
+ if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
+ $status->fatal( 'backend-fail-delete', $params['src'] );
+ trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
+ }
+ };
+ $status->value = new FSFileOpHandle( $this, $params, $handler, $cmd );
} else { // immediate write
$this->trapWarnings();
$ok = unlink( $source );
$this->untrapWarnings();
if ( !$ok ) {
$status->fatal( 'backend-fail-delete', $params['src'] );
+
return $status;
}
}
@@ -415,15 +437,11 @@ class FSFileBackend extends FileBackendStore {
}
/**
- * @see FSFileBackend::doExecuteOpHandlesInternal()
+ * @param string $fullCont
+ * @param string $dirRel
+ * @param array $params
+ * @return Status
*/
- protected function _getResponseDelete( $errors, Status $status, array $params, $cmd ) {
- if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
- $status->fatal( 'backend-fail-delete', $params['src'] );
- trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
- }
- }
-
protected function doPrepareInternal( $fullCont, $dirRel, array $params ) {
$status = Status::newGood();
list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
@@ -444,6 +462,7 @@ class FSFileBackend extends FileBackendStore {
if ( is_dir( $dir ) && !$existed ) {
$status->merge( $this->doSecureInternal( $fullCont, $dirRel, $params ) );
}
+
return $status;
}
@@ -471,6 +490,7 @@ class FSFileBackend extends FileBackendStore {
$status->fatal( 'backend-fail-create', "{$storeDir}/.htaccess" );
}
}
+
return $status;
}
@@ -498,6 +518,7 @@ class FSFileBackend extends FileBackendStore {
}
$this->untrapWarnings();
}
+
return $status;
}
@@ -511,6 +532,7 @@ class FSFileBackend extends FileBackendStore {
rmdir( $dir ); // remove directory if empty
}
$this->untrapWarnings();
+
return $status;
}
@@ -557,7 +579,10 @@ class FSFileBackend extends FileBackendStore {
/**
* @see FileBackendStore::getDirectoryListInternal()
- * @return Array|null
+ * @param string $fullCont
+ * @param string $dirRel
+ * @param array $params
+ * @return array|null
*/
public function getDirectoryListInternal( $fullCont, $dirRel, array $params ) {
list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
@@ -566,17 +591,23 @@ class FSFileBackend extends FileBackendStore {
$exists = is_dir( $dir );
if ( !$exists ) {
wfDebug( __METHOD__ . "() given directory does not exist: '$dir'\n" );
+
return array(); // nothing under this dir
} elseif ( !is_readable( $dir ) ) {
wfDebug( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
+
return null; // bad permissions?
}
+
return new FSFileBackendDirList( $dir, $params );
}
/**
* @see FileBackendStore::getFileListInternal()
- * @return Array|FSFileBackendFileList|null
+ * @param string $fullCont
+ * @param string $dirRel
+ * @param array $params
+ * @return array|FSFileBackendFileList|null
*/
public function getFileListInternal( $fullCont, $dirRel, array $params ) {
list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
@@ -585,11 +616,14 @@ class FSFileBackend extends FileBackendStore {
$exists = is_dir( $dir );
if ( !$exists ) {
wfDebug( __METHOD__ . "() given directory does not exist: '$dir'\n" );
+
return array(); // nothing under this dir
} elseif ( !is_readable( $dir ) ) {
wfDebug( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
+
return null; // bad permissions?
}
+
return new FSFileBackendFileList( $dir, $params );
}
@@ -662,8 +696,8 @@ class FSFileBackend extends FileBackendStore {
foreach ( $fileOpHandles as $index => $fileOpHandle ) {
$status = Status::newGood();
- $function = '_getResponse' . $fileOpHandle->call;
- $this->$function( $errs[$index], $status, $fileOpHandle->params, $fileOpHandle->cmd );
+ $function = $fileOpHandle->call;
+ $function( $errs[$index], $status, $fileOpHandle->params, $fileOpHandle->cmd );
$statuses[$index] = $status;
if ( $status->isOK() && $fileOpHandle->chmodPath ) {
$this->chmod( $fileOpHandle->chmodPath );
@@ -718,8 +752,6 @@ class FSFileBackend extends FileBackendStore {
/**
* Listen for E_WARNING errors and track whether any happen
- *
- * @return void
*/
protected function trapWarnings() {
$this->hadWarningErrors[] = false; // push to stack
@@ -737,7 +769,7 @@ class FSFileBackend extends FileBackendStore {
}
/**
- * @param integer $errno
+ * @param int $errno
* @param string $errstr
* @return bool
* @access private
@@ -745,6 +777,7 @@ class FSFileBackend extends FileBackendStore {
public function handleWarning( $errno, $errstr ) {
wfDebugLog( 'FSFileBackend', $errstr ); // more detailed error logging
$this->hadWarningErrors[count( $this->hadWarningErrors ) - 1] = true;
+
return true; // suppress from PHP handler
}
}
@@ -759,9 +792,9 @@ class FSFileOpHandle extends FileBackendStoreOpHandle {
/**
* @param FSFileBackend $backend
* @param array $params
- * @param string $call
+ * @param callable $call
* @param string $cmd
- * @param integer|null $chmodPath
+ * @param int|null $chmodPath
*/
public function __construct(
FSFileBackend $backend, array $params, $call, $cmd, $chmodPath = null
@@ -784,13 +817,18 @@ class FSFileOpHandle extends FileBackendStoreOpHandle {
abstract class FSFileBackendList implements Iterator {
/** @var Iterator */
protected $iter;
- protected $suffixStart; // integer
- protected $pos = 0; // integer
- /** @var Array */
+
+ /** @var int */
+ protected $suffixStart;
+
+ /** @var int */
+ protected $pos = 0;
+
+ /** @var array */
protected $params = array();
/**
- * @param string $dir file system directory
+ * @param string $dir File system directory
* @param array $params
*/
public function __construct( $dir, array $params ) {
@@ -811,7 +849,7 @@ abstract class FSFileBackendList implements Iterator {
/**
* Return an appropriate iterator object to wrap
*
- * @param string $dir file system directory
+ * @param string $dir File system directory
* @return Iterator
*/
protected function initIterator( $dir ) {
@@ -823,6 +861,7 @@ abstract class FSFileBackendList implements Iterator {
# RecursiveDirectoryIterator extends FilesystemIterator.
# FilesystemIterator::SKIP_DOTS default is inconsistent in PHP 5.3.x.
$flags = FilesystemIterator::CURRENT_AS_SELF | FilesystemIterator::SKIP_DOTS;
+
return new RecursiveIteratorIterator(
new RecursiveDirectoryIterator( $dir, $flags ),
RecursiveIteratorIterator::CHILD_FIRST // include dirs
@@ -832,7 +871,7 @@ abstract class FSFileBackendList implements Iterator {
/**
* @see Iterator::key()
- * @return integer
+ * @return int
*/
public function key() {
return $this->pos;
@@ -848,7 +887,7 @@ abstract class FSFileBackendList implements Iterator {
/**
* @see Iterator::next()
- * @return void
+ * @throws FileBackendError
*/
public function next() {
try {
@@ -862,7 +901,7 @@ abstract class FSFileBackendList implements Iterator {
/**
* @see Iterator::rewind()
- * @return void
+ * @throws FileBackendError
*/
public function rewind() {
$this->pos = 0;
@@ -885,13 +924,14 @@ abstract class FSFileBackendList implements Iterator {
/**
* Filter out items by advancing to the next ones
*/
- protected function filterViaNext() {}
+ protected function filterViaNext() {
+ }
/**
* Return only the relative path and normalize slashes to FileBackend-style.
* Uses the "real path" since the suffix is based upon that.
*
- * @param string $path
+ * @param string $dir
* @return string
*/
protected function getRelPath( $dir ) {
@@ -899,6 +939,7 @@ abstract class FSFileBackendList implements Iterator {
if ( $path === false ) {
$path = $dir;
}
+
return strtr( substr( $path, $this->suffixStart ), '\\', '/' );
}
}