Index: trunk/phase3/tests/phpunit/includes/filerepo/FileBackendTest.php |
— | — | @@ -437,13 +437,12 @@ |
438 | 438 | $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ); |
439 | 439 | $this->tearDownFiles(); |
440 | 440 | |
441 | | - # FIXME |
442 | | - #$this->backend = $this->multiBackend; |
443 | | - #$this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ); |
444 | | - #$this->tearDownFiles(); |
| 441 | + $this->backend = $this->multiBackend; |
| 442 | + $this->doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ); |
| 443 | + $this->tearDownFiles(); |
445 | 444 | } |
446 | 445 | |
447 | | - public function doTestConcatenate( $op, $srcs, $srcsContent, $alreadyExists, $okStatus ) { |
| 446 | + public function doTestConcatenate( $params, $srcs, $srcsContent, $alreadyExists, $okStatus ) { |
448 | 447 | $backendName = $this->backendClass(); |
449 | 448 | |
450 | 449 | $expContent = ''; |
— | — | @@ -462,7 +461,7 @@ |
463 | 462 | $this->assertEquals( true, $status->isOK(), |
464 | 463 | "Creation of source files succeeded ($backendName)." ); |
465 | 464 | |
466 | | - $dest = $op['dst']; |
| 465 | + $dest = $params['dst']; |
467 | 466 | if ( $alreadyExists ) { |
468 | 467 | $ok = file_put_contents( $dest, 'blah...blah...waahwaah' ) !== false; |
469 | 468 | $this->assertEquals( true, $ok, |
— | — | @@ -473,8 +472,8 @@ |
474 | 473 | "Creation of 0-byte file at $dest succeeded ($backendName)." ); |
475 | 474 | } |
476 | 475 | |
477 | | - // Combine them |
478 | | - $status = $this->backend->doOperation( $op ); |
| 476 | + // Combine the files into one |
| 477 | + $status = $this->backend->concatenate( $params ); |
479 | 478 | if ( $okStatus ) { |
480 | 479 | $this->assertEquals( array(), $status->errors, |
481 | 480 | "Creation of concat file at $dest succeeded without warnings ($backendName)." ); |
— | — | @@ -534,10 +533,10 @@ |
535 | 534 | 'lkaem;a', |
536 | 535 | 'legma' |
537 | 536 | ); |
538 | | - $op = array( 'op' => 'concatenate', 'srcs' => $srcs, 'dst' => $dest ); |
| 537 | + $params = array( 'srcs' => $srcs, 'dst' => $dest ); |
539 | 538 | |
540 | 539 | $cases[] = array( |
541 | | - $op, // operation |
| 540 | + $params, // operation |
542 | 541 | $srcs, // sources |
543 | 542 | $content, // content for each source |
544 | 543 | false, // no dest already exists |
— | — | @@ -545,7 +544,7 @@ |
546 | 545 | ); |
547 | 546 | |
548 | 547 | $cases[] = array( |
549 | | - $op, // operation |
| 548 | + $params, // operation |
550 | 549 | $srcs, // sources |
551 | 550 | $content, // content for each source |
552 | 551 | true, // dest already exists |
Index: trunk/phase3/includes/filerepo/backend/FileBackendMultiWrite.php |
— | — | @@ -36,9 +36,15 @@ |
37 | 37 | */ |
38 | 38 | public function __construct( array $config ) { |
39 | 39 | parent::__construct( $config ); |
| 40 | + $namesUsed = array(); |
40 | 41 | // Construct backends here rather than via registration |
41 | 42 | // to keep these backends hidden from outside the proxy. |
42 | 43 | foreach ( $config['backends'] as $index => $config ) { |
| 44 | + $name = $config['name']; |
| 45 | + if ( isset( $namesUsed[$name] ) ) { // don't break FileOp predicates |
| 46 | + throw new MWException( "Two or more backends defined with the name $name." ); |
| 47 | + } |
| 48 | + $namesUsed[$name] = 1; |
43 | 49 | if ( !isset( $config['class'] ) ) { |
44 | 50 | throw new MWException( 'No class given for a backend config.' ); |
45 | 51 | } |
— | — | @@ -245,6 +251,15 @@ |
246 | 252 | } |
247 | 253 | |
248 | 254 | /** |
| 255 | + * @see FileBackendBase::getFileList() |
| 256 | + */ |
| 257 | + public function concatenate( array $params ) { |
| 258 | + // We are writing to an FS file, so we don't need to do this per-backend |
| 259 | + $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] ); |
| 260 | + return $this->backends[$this->masterIndex]->concatenate( $realParams ); |
| 261 | + } |
| 262 | + |
| 263 | + /** |
249 | 264 | * @see FileBackendBase::fileExists() |
250 | 265 | */ |
251 | 266 | public function fileExists( array $params ) { |
Index: trunk/phase3/includes/filerepo/backend/FileOp.php |
— | — | @@ -808,61 +808,6 @@ |
809 | 809 | } |
810 | 810 | |
811 | 811 | /** |
812 | | - * Combines files from severals storage paths into a new file in the backend. |
813 | | - * Parameters similar to FileBackend::concatenate(), which include: |
814 | | - * srcs : ordered source storage paths (e.g. chunk1, chunk2, ...) |
815 | | - * dst : destination file system path to 0-byte temp file |
816 | | - */ |
817 | | -class ConcatenateFileOp extends FileOp { |
818 | | - protected function allowedParams() { |
819 | | - return array( 'srcs', 'dst' ); |
820 | | - } |
821 | | - |
822 | | - protected function doPrecheck( array &$predicates ) { |
823 | | - $status = Status::newGood(); |
824 | | - // Check destination temp file |
825 | | - wfSuppressWarnings(); |
826 | | - $ok = ( is_file( $this->params['dst'] ) && !filesize( $this->params['dst'] ) ); |
827 | | - wfRestoreWarnings(); |
828 | | - if ( !$ok ) { // not present or not empty |
829 | | - $status->fatal( 'backend-fail-opentemp', $this->params['dst'] ); |
830 | | - return $status; |
831 | | - } |
832 | | - // Check that source files exists |
833 | | - foreach ( $this->params['srcs'] as $source ) { |
834 | | - if ( !$this->fileExists( $source, $predicates ) ) { |
835 | | - $status->fatal( 'backend-fail-notexists', $source ); |
836 | | - return $status; |
837 | | - } |
838 | | - } |
839 | | - return $status; |
840 | | - } |
841 | | - |
842 | | - protected function doAttempt() { |
843 | | - $status = Status::newGood(); |
844 | | - // Concatenate the file at the destination |
845 | | - $status->merge( $this->backend->concatenateInternal( $this->params ) ); |
846 | | - return $status; |
847 | | - } |
848 | | - |
849 | | - protected function doRevert() { |
850 | | - $status = Status::newGood(); |
851 | | - // Clear out the temp file back to 0-bytes |
852 | | - wfSuppressWarnings(); |
853 | | - $ok = file_put_contents( $this->params['dst'], '' ); |
854 | | - wfRestoreWarnings(); |
855 | | - if ( !$ok ) { |
856 | | - $status->fatal( 'backend-fail-writetemp', $this->params['dst'] ); |
857 | | - } |
858 | | - return $status; |
859 | | - } |
860 | | - |
861 | | - public function storagePathsRead() { |
862 | | - return $this->params['srcs']; |
863 | | - } |
864 | | -} |
865 | | - |
866 | | -/** |
867 | 812 | * Delete a file at the storage path. |
868 | 813 | * Parameters similar to FileBackend::delete(), which include: |
869 | 814 | * src : source storage path |
Index: trunk/phase3/includes/filerepo/backend/FileBackend.php |
— | — | @@ -132,14 +132,8 @@ |
133 | 133 | * 'src' => <storage path>, |
134 | 134 | * 'ignoreMissingSource' => <boolean> |
135 | 135 | * ) |
136 | | - * f) Concatenate a list of files within storage into a single temp file |
| 136 | + * f) Do nothing (no-op) |
137 | 137 | * array( |
138 | | - * 'op' => 'concatenate', |
139 | | - * 'srcs' => <ordered array of storage paths>, |
140 | | - * 'dst' => <file system path to 0-byte temp file> |
141 | | - * ) |
142 | | - * g) Do nothing (no-op) |
143 | | - * array( |
144 | 138 | * 'op' => 'null', |
145 | 139 | * ) |
146 | 140 | * |
— | — | @@ -203,6 +197,21 @@ |
204 | 198 | } |
205 | 199 | |
206 | 200 | /** |
| 201 | + * Performs a single create operation. |
| 202 | + * This sets $params['op'] to 'create' and passes it to doOperation(). |
| 203 | + * |
| 204 | + * @see FileBackendBase::doOperation() |
| 205 | + * |
| 206 | + * @param $params Array Operation parameters |
| 207 | + * @param $opts Array Operation options |
| 208 | + * @return Status |
| 209 | + */ |
| 210 | + final public function create( array $params, array $opts = array() ) { |
| 211 | + $params['op'] = 'create'; |
| 212 | + return $this->doOperation( $params, $opts ); |
| 213 | + } |
| 214 | + |
| 215 | + /** |
207 | 216 | * Performs a single store operation. |
208 | 217 | * This sets $params['op'] to 'store' and passes it to doOperation(). |
209 | 218 | * |
— | — | @@ -263,36 +272,17 @@ |
264 | 273 | } |
265 | 274 | |
266 | 275 | /** |
267 | | - * Performs a single create operation. |
268 | | - * This sets $params['op'] to 'create' and passes it to doOperation(). |
| 276 | + * Concatenate a list of storage files into a single file on the file system |
| 277 | + * $params include: |
| 278 | + * srcs : ordered source storage paths (e.g. chunk1, chunk2, ...) |
| 279 | + * dst : file system path to 0-byte temp file |
269 | 280 | * |
270 | | - * @see FileBackendBase::doOperation() |
271 | | - * |
272 | 281 | * @param $params Array Operation parameters |
273 | | - * @param $opts Array Operation options |
274 | 282 | * @return Status |
275 | 283 | */ |
276 | | - final public function create( array $params, array $opts = array() ) { |
277 | | - $params['op'] = 'create'; |
278 | | - return $this->doOperation( $params, $opts ); |
279 | | - } |
| 284 | + abstract public function concatenate( array $params ); |
280 | 285 | |
281 | 286 | /** |
282 | | - * Performs a single concatenate operation. |
283 | | - * This sets $params['op'] to 'concatenate' and passes it to doOperation(). |
284 | | - * |
285 | | - * @see FileBackendBase::doOperation() |
286 | | - * |
287 | | - * @param $params Array Operation parameters |
288 | | - * @param $opts Array Operation options |
289 | | - * @return Status |
290 | | - */ |
291 | | - final public function concatenate( array $params, array $opts = array() ) { |
292 | | - $params['op'] = 'concatenate'; |
293 | | - return $this->doOperation( $params, $opts ); |
294 | | - } |
295 | | - |
296 | | - /** |
297 | 287 | * Prepare a storage path for usage. This will create containers |
298 | 288 | * that don't yet exist or, on FS backends, create parent directories. |
299 | 289 | * |
— | — | @@ -677,24 +667,27 @@ |
678 | 668 | } |
679 | 669 | |
680 | 670 | /** |
681 | | - * Combines files from several storage paths into a new file in the backend. |
682 | | - * Do not call this function from places outside FileBackend and FileOp. |
683 | | - * $params include: |
684 | | - * srcs : ordered source storage paths (e.g. chunk1, chunk2, ...) |
685 | | - * dst : file system path to 0-byte temp file |
686 | | - * |
687 | | - * @param $params Array |
688 | | - * @return Status |
| 671 | + * @see FileBackendBase::concatenate() |
689 | 672 | */ |
690 | | - final public function concatenateInternal( array $params ) { |
691 | | - $status = $this->doConcatenateInternal( $params ); |
| 673 | + final public function concatenate( array $params ) { |
| 674 | + $status = Status::newGood(); |
| 675 | + |
| 676 | + // Try to lock the source files for the scope of this function |
| 677 | + $scopeLockS = $this->getScopedFileLocks( $params['srcs'], LockManager::LOCK_UW, $status ); |
| 678 | + if ( !$status->isOK() ) { |
| 679 | + return $status; // abort |
| 680 | + } |
| 681 | + |
| 682 | + // Actually do the concatenation |
| 683 | + $status->merge( $this->doConcatenate( $params ) ); |
| 684 | + |
692 | 685 | return $status; |
693 | 686 | } |
694 | 687 | |
695 | 688 | /** |
696 | | - * @see FileBackend::concatenateInternal() |
| 689 | + * @see FileBackend::concatenate() |
697 | 690 | */ |
698 | | - protected function doConcatenateInternal( array $params ) { |
| 691 | + protected function doConcatenate( array $params ) { |
699 | 692 | $status = Status::newGood(); |
700 | 693 | $tmpPath = $params['dst']; // convenience |
701 | 694 | |
— | — | @@ -1035,7 +1028,6 @@ |
1036 | 1029 | 'copy' => 'CopyFileOp', |
1037 | 1030 | 'move' => 'MoveFileOp', |
1038 | 1031 | 'delete' => 'DeleteFileOp', |
1039 | | - 'concatenate' => 'ConcatenateFileOp', |
1040 | 1032 | 'create' => 'CreateFileOp', |
1041 | 1033 | 'null' => 'NullFileOp' |
1042 | 1034 | ); |
Index: trunk/phase3/includes/filerepo/FileRepo.php |
— | — | @@ -791,8 +791,6 @@ |
792 | 792 | */ |
793 | 793 | function concatenate( $srcPaths, $dstPath, $flags = 0 ) { |
794 | 794 | $status = $this->newGood(); |
795 | | - // Resolve target to a storage path if virtual |
796 | | - $dest = $this->resolveToStoragePath( $dstPath ); |
797 | 795 | |
798 | 796 | $sources = array(); |
799 | 797 | $deleteOperations = array(); // post-concatenate ops |
— | — | @@ -805,9 +803,9 @@ |
806 | 804 | } |
807 | 805 | } |
808 | 806 | |
809 | | - // Concatenate the chunks into one file |
810 | | - $op = array( 'op' => 'concatenate', 'srcs' => $sources, 'dst' => $dest ); |
811 | | - $status->merge( $this->backend->doOperation( $op ) ); |
| 807 | + // Concatenate the chunks into one FS file |
| 808 | + $params = array( 'srcs' => $sources, 'dst' => $dstPath ); |
| 809 | + $status->merge( $this->backend->concatenate( $params ) ); |
812 | 810 | if ( !$status->isOK() ) { |
813 | 811 | return $status; |
814 | 812 | } |