From 03ed413c935d7beafc39edd95be08eb1e872bfbc Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Wed, 4 Jan 2012 02:15:07 +0000 Subject: [PATCH] * Added FileBackendBase::getFileContents() function with a default FileBackend version. * Added read-only mode to FileBackendBase config. * Moved FileBackendBase::getFileTimestamp() up slightly. --- includes/filerepo/backend/FileBackend.php | 52 ++++++++++++++++--- .../backend/FileBackendMultiWrite.php | 15 ++++++ languages/messages/MessagesEn.php | 1 + maintenance/language/messages.inc | 1 + .../includes/filerepo/FileBackendTest.php | 30 ++++++++++- 5 files changed, 89 insertions(+), 10 deletions(-) diff --git a/includes/filerepo/backend/FileBackend.php b/includes/filerepo/backend/FileBackend.php index 07f7ed702c..71bc5e2c45 100644 --- a/includes/filerepo/backend/FileBackend.php +++ b/includes/filerepo/backend/FileBackend.php @@ -28,6 +28,7 @@ abstract class FileBackendBase { protected $name; // unique backend name protected $wikiId; // unique wiki name + protected $readOnly; // string /** @var LockManager */ protected $lockManager; @@ -36,9 +37,11 @@ abstract class FileBackendBase { * This should only be called from within FileBackendGroup. * * $config includes: - * 'name' : The name of this backend + * 'name' : The unique name of this backend * 'wikiId' : Prefix to container names that is unique to this wiki - * 'lockManager' : Registered name of the file lock manager to use + * 'lockManager' : Registered name of a file lock manager to use + * 'readOnly' : Write operations are disallowed if this is a non-empty string. + * It should be an explanation for the backend being read-only. * * @param $config Array */ @@ -48,6 +51,9 @@ abstract class FileBackendBase { ? $config['wikiId'] : wfWikiID(); $this->lockManager = LockManagerGroup::singleton()->get( $config['lockManager'] ); + $this->readOnly = isset( $config['readOnly'] ) + ? (string)$config['readOnly'] + : ''; } /** @@ -149,6 +155,9 @@ abstract class FileBackendBase { * @return Status */ final public function doOperations( array $ops, array $opts = array() ) { + if ( $this->readOnly != '' ) { + return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly ); + } if ( empty( $opts['ignoreErrors'] ) ) { // sanity unset( $opts['nonLocking'] ); unset( $opts['allowStale'] ); @@ -320,28 +329,41 @@ abstract class FileBackendBase { abstract public function fileExists( array $params ); /** - * Get a SHA-1 hash of the file at a storage path in the backend. + * Get the last-modified timestamp of the file at a storage path. * * $params include: * src : source storage path * latest : use the latest available data * * @param $params Array - * @return string|false Hash string or false on failure + * @return string|false TS_MW timestamp or false on failure */ - abstract public function getFileSha1Base36( array $params ); + abstract public function getFileTimestamp( array $params ); /** - * Get the last-modified timestamp of the file at a storage path. + * Get the contents of a file at a storage path in the backend. + * This should be avoided for potentially large files. * * $params include: * src : source storage path * latest : use the latest available data * * @param $params Array - * @return string|false TS_MW timestamp or false on failure + * @return string|false Returns false on failure */ - abstract public function getFileTimestamp( array $params ); + abstract public function getFileContents( array $params ); + + /** + * Get a SHA-1 hash of the file at a storage path in the backend. + * + * $params include: + * src : source storage path + * latest : use the latest available data + * + * @param $params Array + * @return string|false Hash string or false on failure + */ + abstract public function getFileSha1Base36( array $params ); /** * Get the properties of the file at a storage path in the backend. @@ -753,6 +775,20 @@ abstract class FileBackend extends FileBackendBase { return Status::newGood(); } + /** + * @see FileBackendBase::getFileContents() + */ + public function getFileContents( array $params ) { + $tmpFile = $this->getLocalReference( $params ); + if ( !$tmpFile ) { + return false; + } + wfSuppressWarnings(); + $data = file_get_contents( $tmpFile->getPath() ); + wfRestoreWarnings(); + return $data; + } + /** * @see FileBackendBase::getFileSha1Base36() */ diff --git a/includes/filerepo/backend/FileBackendMultiWrite.php b/includes/filerepo/backend/FileBackendMultiWrite.php index 114424c2db..241858ee20 100644 --- a/includes/filerepo/backend/FileBackendMultiWrite.php +++ b/includes/filerepo/backend/FileBackendMultiWrite.php @@ -209,6 +209,21 @@ class FileBackendMultiWrite extends FileBackendBase { return $this->backends[$this->masterIndex]->getFileTimestamp( $realParams ); } + /** + * @see FileBackendBase::getFileContents() + */ + function getFileContents( array $params ) { + # Hit all backends in case of failed operations (out of sync) + foreach ( $this->backends as $backend ) { + $realParams = $this->substOpPaths( $params, $backend ); + $data = $backend->getFileContents( $realParams ); + if ( $data !== false ) { + return $data; + } + } + return false; + } + /** * @see FileBackendBase::getFileSha1Base36() */ diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index f21b6fcfbd..32aef6f3f6 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -2253,6 +2253,7 @@ If the problem persists, contact an [[Special:ListUsers/sysop|administrator]].', 'backend-fail-closetemp' => 'Could not close temporary file.', 'backend-fail-read' => 'Could not read file $1.', 'backend-fail-create' => 'Could not create file $1.', +'backend-fail-readonly' => 'The backend "$1" is currently read-only. The reason given is: "$2"', # Lock manager 'lockmanager-notlocked' => 'Could not unlock "$1"; it is not locked.', diff --git a/maintenance/language/messages.inc b/maintenance/language/messages.inc index 3133d779f4..4ff4c61d7c 100644 --- a/maintenance/language/messages.inc +++ b/maintenance/language/messages.inc @@ -1362,6 +1362,7 @@ $wgMessageStructure = array( 'backend-fail-closetemp', 'backend-fail-read', 'backend-fail-create', + 'backend-fail-readonly' ), 'lockmanager-errors' => array( diff --git a/tests/phpunit/includes/filerepo/FileBackendTest.php b/tests/phpunit/includes/filerepo/FileBackendTest.php index aea98cecfd..c1cc55f23c 100644 --- a/tests/phpunit/includes/filerepo/FileBackendTest.php +++ b/tests/phpunit/includes/filerepo/FileBackendTest.php @@ -432,6 +432,32 @@ class FileBackendTest extends MediaWikiTestCase { return $cases; } + /** + * @dataProvider provider_testGetFileContents + */ + public function testGetFileContents( $src, $content ) { + $this->pathsToPrune[] = $src; + + $status = $this->backend->doOperation( + array( 'op' => 'create', 'content' => $content, 'dst' => $src ) ); + $this->assertEquals( true, $status->isOK(), "Creation of file at $src succeeded." ); + + $newContents = $this->backend->getFileContents( array( 'src' => $src ) ); + $this->assertNotEquals( false, $newContents, "Read of file at $src succeeded." ); + + $this->assertEquals( $content, $newContents, "Contents read match data at $src." ); + } + + function provider_testGetFileContents() { + $cases = array(); + + $base = $this->singleBasePath(); + $cases[] = array( "$base/cont1/b/z/some_file.txt", "some file contents" ); + $cases[] = array( "$base/cont1/b/some-other_file.txt", "more file contents" ); + + return $cases; + } + /** * @dataProvider provider_testGetLocalCopy */ @@ -460,7 +486,7 @@ class FileBackendTest extends MediaWikiTestCase { } /** - * @dataProvider provider_testGetReference + * @dataProvider provider_testGetLocalReference */ public function testGetLocalReference( $src, $content ) { $this->pathsToPrune[] = $src; @@ -476,7 +502,7 @@ class FileBackendTest extends MediaWikiTestCase { $this->assertNotEquals( false, $contents, "Local copy of $src exists." ); } - function provider_testGetReference() { + function provider_testGetLocalReference() { $cases = array(); $base = $this->singleBasePath(); -- 2.20.1