[FileBackend] Added "bypassReadOnly" flag.
authorAaron Schulz <aschulz@wikimedia.org>
Thu, 12 Jul 2012 19:48:16 +0000 (12:48 -0700)
committerAaron Schulz <aschulz@wikimedia.org>
Wed, 18 Jul 2012 03:55:49 +0000 (20:55 -0700)
* Added flag to bypass read-only checks to the 6 write functions.
* Made backend copy and sync scripts pass in this new flag.
* Also made sync script use doQuickOperations() since its faster.

Change-Id: Iee47f79ed7ab002cfc2d0adb5321c3a8520f971d

includes/filerepo/backend/FileBackend.php
maintenance/copyFileBackend.php
maintenance/syncFileBackend.php

index de61ef6..efafbd1 100644 (file)
@@ -244,7 +244,8 @@ abstract class FileBackend {
         *                           This has no effect unless the 'force' flag is set.
         *   - nonJournaled        : Don't log this operation batch in the file journal.
         *                           This limits the ability of recovery scripts.
-        *   - parallelize'        : Try to do operations in parallel when possible.
+        *   - parallelize         : Try to do operations in parallel when possible.
+        *   - bypassReadOnly      : Allow writes in read-only mode (@since 1.20).
         *
         * @remarks Remarks on locking:
         * File system paths given to operations should refer to files that are
@@ -266,7 +267,7 @@ abstract class FileBackend {
         * @return Status
         */
        final public function doOperations( array $ops, array $opts = array() ) {
-               if ( $this->isReadOnly() ) {
+               if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
                if ( empty( $opts['force'] ) ) { // sanity
@@ -441,6 +442,9 @@ abstract class FileBackend {
         *   - ignoreMissingSource : The operation will simply succeed and do
         *                           nothing if the source file does not exist.
         *
+        * $opts is an associative of boolean flags, including:
+        *   - bypassReadOnly      : Allow writes in read-only mode (@since 1.20)
+        *
         * @par Return value:
         * This returns a Status, which contains all warnings and fatals that occured
         * during the operation. The 'failCount', 'successCount', and 'success' members
@@ -448,11 +452,12 @@ abstract class FileBackend {
         * considered "OK" as long as no fatal errors occured.
         *
         * @param $ops Array Set of operations to execute
+        * @param $opts Array Batch operation options
         * @return Status
         * @since 1.20
         */
-       final public function doQuickOperations( array $ops ) {
-               if ( $this->isReadOnly() ) {
+       final public function doQuickOperations( array $ops, array $opts = array() ) {
+               if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
                foreach ( $ops as &$op ) {
@@ -575,15 +580,16 @@ abstract class FileBackend {
         * These flags should always be set for directories that have private files.
         *
         * $params include:
-        *   - dir       : storage directory
-        *   - noAccess  : try to deny file access (@since 1.20)
-        *   - noListing : try to deny file listing (@since 1.20)
+        *   - dir            : storage directory
+        *   - noAccess       : try to deny file access (@since 1.20)
+        *   - noListing      : try to deny file listing (@since 1.20)
+        *   - bypassReadOnly : allow writes in read-only mode (@since 1.20)
         *
         * @param $params Array
         * @return Status
         */
        final public function prepare( array $params ) {
-               if ( $this->isReadOnly() ) {
+               if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
                return $this->doPrepare( $params );
@@ -603,13 +609,14 @@ abstract class FileBackend {
         *
         * @param $params Array
         * $params include:
-        *   - dir       : storage directory
-        *   - noAccess  : try to deny file access
-        *   - noListing : try to deny file listing
+        *   - dir            : storage directory
+        *   - noAccess       : try to deny file access
+        *   - noListing      : try to deny file listing
+        *   - bypassReadOnly : allow writes in read-only mode (@since 1.20)
         * @return Status
         */
        final public function secure( array $params ) {
-               if ( $this->isReadOnly() ) {
+               if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
                return $this->doSecure( $params );
@@ -628,16 +635,17 @@ abstract class FileBackend {
         * This essentially can undo the result of secure() calls.
         *
         * $params include:
-        *   - dir     : storage directory
-        *   - access  : try to allow file access
-        *   - listing : try to allow file listing
+        *   - dir            : storage directory
+        *   - access         : try to allow file access
+        *   - listing        : try to allow file listing
+        *   - bypassReadOnly : allow writes in read-only mode (@since 1.20)
         *
         * @param $params Array
         * @return Status
         * @since 1.20
         */
        final public function publish( array $params ) {
-               if ( $this->isReadOnly() ) {
+               if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
                return $this->doPublish( $params );
@@ -655,12 +663,13 @@ abstract class FileBackend {
         *
         * @param $params Array
         * $params include:
-        *   - dir       : storage directory
-        *   - recursive : recursively delete empty subdirectories first (@since 1.20)
+        *   - dir            : storage directory
+        *   - recursive      : recursively delete empty subdirectories first (@since 1.20)
+        *   - bypassReadOnly : allow writes in read-only mode (@since 1.20)
         * @return Status
         */
        final public function clean( array $params ) {
-               if ( $this->isReadOnly() ) {
+               if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
                        return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
                }
                return $this->doClean( $params );
index 548a33b..544f48b 100644 (file)
@@ -141,7 +141,7 @@ class CopyFileBackend extends Maintenance {
                        }
                        $fsFiles[] = $fsFile; // keep TempFSFile objects alive as needed
                        // Note: prepare() is usually fast for key/value backends
-                       $status = $dst->prepare( array( 'dir' => dirname( $dstPath ) ) );
+                       $status = $dst->prepare( array( 'dir' => dirname( $dstPath ), 'bypassReadOnly' => 1 ) );
                        if ( !$status->isOK() ) {
                                $this->error( print_r( $status->getErrorsArray(), true ) );
                                $this->error( "Could not copy $srcPath to $dstPath.", 1 ); // die
@@ -152,10 +152,10 @@ class CopyFileBackend extends Maintenance {
                }
 
                $t_start = microtime( true );
-               $status = $dst->doQuickOperations( $ops );
+               $status = $dst->doQuickOperations( $ops, array( 'bypassReadOnly' => 1 ) );
                if ( !$status->isOK() ) {
                        sleep( 10 ); // wait and retry copy again
-                       $status = $dst->doQuickOperations( $ops );
+                       $status = $dst->doQuickOperations( $ops, array( 'bypassReadOnly' => 1 ) );
                }
                $ellapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
                if ( !$status->isOK() ) {
index 1af33e6..c4ba66e 100644 (file)
@@ -185,7 +185,8 @@ class SyncFileBackend extends Maintenance {
                                }
                                $fsFiles[] = $fsFile; // keep TempFSFile objects alive as needed
                                // Note: prepare() is usually fast for key/value backends
-                               $status->merge( $dst->prepare( array( 'dir' => dirname( $dPath ) ) ) );
+                               $status->merge( $dst->prepare( array(
+                                       'dir' => dirname( $dPath ), 'bypassReadOnly' => 1 ) ) );
                                if ( !$status->isOK() ) {
                                        return $status;
                                }
@@ -201,8 +202,7 @@ class SyncFileBackend extends Maintenance {
                }
 
                $t_start = microtime( true );
-               $status->merge( $dst->doOperations( $ops,
-                       array( 'nonLocking' => 1, 'nonJournaled' => 1 ) ) );
+               $status->merge( $dst->doQuickOperations( $ops, array( 'bypassReadOnly' => 1 ) ) );
                $ellapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
                if ( $status->isOK() && $this->getOption( 'verbose' ) ) {
                        $this->output( "Synchronized these file(s) [{$ellapsed_ms}ms]:\n" .