Index: branches/FileBackend/phase3/includes/filerepo/backend/FileBackendMultiWrite.php |
— | — | @@ -84,7 +84,7 @@ |
85 | 85 | } |
86 | 86 | |
87 | 87 | // Try to lock those files for the scope of this function... |
88 | | - $scopedLock = $this->getScopedFileLocks( $filesToLock, $status ); |
| 88 | + $scopedLock = $this->getScopedFileLocks( $filesToLock, LockManager::LOCK_EX, $status ); |
89 | 89 | if ( !$status->isOK() ) { |
90 | 90 | return $status; // abort |
91 | 91 | } |
Index: branches/FileBackend/phase3/includes/filerepo/backend/FileOp.php |
— | — | @@ -18,11 +18,12 @@ |
19 | 19 | protected $params; |
20 | 20 | /** $var FileBackendBase */ |
21 | 21 | protected $backend; |
22 | | - /** @var TempLocalFile|null */ |
| 22 | + /** @var TempFSFile|null */ |
23 | 23 | protected $tmpSourceFile, $tmpDestFile; |
24 | 24 | |
25 | | - protected $state; |
26 | | - protected $failed; |
| 25 | + protected $state; // integer |
| 26 | + protected $failed; // boolean |
| 27 | + protected $destSameAsSource; // boolean |
27 | 28 | |
28 | 29 | /* Object life-cycle */ |
29 | 30 | const STATE_NEW = 1; |
— | — | @@ -41,10 +42,11 @@ |
42 | 43 | $this->params = $params; |
43 | 44 | $this->state = self::STATE_NEW; |
44 | 45 | $this->failed = false; |
| 46 | + $this->destSameAsSource = false; |
45 | 47 | $this->initialize(); |
46 | 48 | } |
47 | 49 | |
48 | | - /* |
| 50 | + /** |
49 | 51 | * Get the value of the parameter with the given name. |
50 | 52 | * Returns null if the parameter is not set. |
51 | 53 | * |
— | — | @@ -148,15 +150,33 @@ |
149 | 151 | } |
150 | 152 | |
151 | 153 | /** |
152 | | - * Get a list of storage paths to lock for this operation |
| 154 | + * Get a list of storage paths used by this operation |
153 | 155 | * |
154 | 156 | * @return Array |
155 | 157 | */ |
156 | | - public function storagePathsUsed() { |
| 158 | + final public function storagePathsUsed() { |
| 159 | + return array_merge( $this->storagePathsRead(), $this->storagePathsChanged() ); |
| 160 | + } |
| 161 | + |
| 162 | + /** |
| 163 | + * Get a list of storage paths read from for this operation |
| 164 | + * |
| 165 | + * @return Array |
| 166 | + */ |
| 167 | + public function storagePathsRead() { |
157 | 168 | return array(); |
158 | 169 | } |
159 | 170 | |
160 | 171 | /** |
| 172 | + * Get a list of storage paths written to for this operation |
| 173 | + * |
| 174 | + * @return Array |
| 175 | + */ |
| 176 | + public function storagePathsChanged() { |
| 177 | + return array(); |
| 178 | + } |
| 179 | + |
| 180 | + /** |
161 | 181 | * @return void |
162 | 182 | */ |
163 | 183 | protected function initialize() {} |
— | — | @@ -164,7 +184,9 @@ |
165 | 185 | /** |
166 | 186 | * @return Status |
167 | 187 | */ |
168 | | - abstract protected function doPrecheck( array &$predicates ); |
| 188 | + protected function doPrecheck( array &$predicates ) { |
| 189 | + return Status::newGood(); |
| 190 | + } |
169 | 191 | |
170 | 192 | /** |
171 | 193 | * @return Status |
— | — | @@ -224,19 +246,19 @@ |
225 | 247 | return $status; |
226 | 248 | } |
227 | 249 | } elseif ( $this->getParam( 'overwriteSame' ) ) { |
228 | | - // Get the source content hash (if there is a single source) |
229 | 250 | $shash = $this->getSourceMD5(); |
230 | 251 | // If there is a single source, then we can do some checks already. |
231 | | - // For things like concatenate(), we need to build a temp file first. |
| 252 | + // For things like concatenate(), we would need to build a temp file |
| 253 | + // first and thus don't support 'overwriteSame' ($shash is null). |
232 | 254 | if ( $shash !== null ) { |
233 | 255 | $dhash = $this->getFileMD5( $this->params['dst'] ); |
234 | 256 | if ( !strlen( $shash ) || !strlen( $dhash ) ) { |
235 | 257 | $status->fatal( 'backend-fail-hashes' ); |
236 | | - return $status; |
237 | | - } |
238 | | - // Give an error if the files are not identical |
239 | | - if ( $shash !== $dhash ) { |
| 258 | + } elseif ( $shash !== $dhash ) { |
| 259 | + // Give an error if the files are not identical |
240 | 260 | $status->fatal( 'backend-fail-notsame', $this->params['dst'] ); |
| 261 | + } else { |
| 262 | + $this->destSameAsSource = true; |
241 | 263 | } |
242 | 264 | return $status; // do nothing; either OK or bad status |
243 | 265 | } |
— | — | @@ -263,13 +285,13 @@ |
264 | 286 | * |
265 | 287 | * @return string|false False on failure |
266 | 288 | */ |
267 | | - final protected function getFileMD5( $path ) { |
| 289 | + protected function getFileMD5( $path ) { |
268 | 290 | // Source file is in backend |
269 | 291 | if ( FileBackend::isStoragePath( $path ) ) { |
270 | 292 | // For some backends (e.g. Swift, Azure) we can get |
271 | 293 | // standard hashes to use for this types of comparisons. |
272 | 294 | if ( $this->backend->getHashType() === 'md5' ) { |
273 | | - $hash = $this->backend->getFileHash( $path ); |
| 295 | + $hash = $this->backend->getFileHash( array( 'src' => $path ) ); |
274 | 296 | } else { |
275 | 297 | $tmp = $this->backend->getLocalCopy( array( 'src' => $path ) ); |
276 | 298 | if ( !$tmp ) { |
— | — | @@ -327,13 +349,13 @@ |
328 | 350 | } |
329 | 351 | |
330 | 352 | /** |
331 | | - * Check if a file will exist when this operation is attempted |
| 353 | + * Check if a file will exist in storage when this operation is attempted |
332 | 354 | * |
333 | | - * @param $source string |
| 355 | + * @param $source string Storage path |
334 | 356 | * @param $predicates Array |
335 | 357 | * @return bool |
336 | 358 | */ |
337 | | - final protected function fileExists( $source, $predicates ) { |
| 359 | + final protected function fileExists( $source, array $predicates ) { |
338 | 360 | if ( isset( $predicates['exists'][$source] ) ) { |
339 | 361 | return $predicates['exists'][$source]; // previous op assures this |
340 | 362 | } else { |
— | — | @@ -351,21 +373,21 @@ |
352 | 374 | * overwriteSame : override any existing file at destination |
353 | 375 | */ |
354 | 376 | class StoreFileOp extends FileOp { |
355 | | - protected $checkDest = true; |
| 377 | + protected $destAlreadyExists = true; |
356 | 378 | |
357 | 379 | protected function doPrecheck( array &$predicates ) { |
358 | 380 | $status = Status::newGood(); |
359 | 381 | // Check if destination file exists |
360 | 382 | if ( $this->fileExists( $this->params['dst'], $predicates ) ) { |
361 | | - if ( !$this->getParam( 'overwriteDest' ) ) { |
| 383 | + if ( !$this->getParam( 'overwriteDest' ) && !$this->getParam( 'overwriteSame' ) ) { |
362 | 384 | $status->fatal( 'backend-fail-alreadyexists', $this->params['dst'] ); |
363 | 385 | return $status; |
364 | 386 | } |
365 | 387 | } else { |
366 | | - $this->checkDest = false; |
| 388 | + $this->destAlreadyExists = false; |
367 | 389 | } |
368 | 390 | // Check if the source file exists on the file system |
369 | | - if ( !file_exists( $this->params['src'] ) ) { |
| 391 | + if ( !is_file( $this->params['src'] ) ) { |
370 | 392 | $status->fatal( 'backend-fail-notexists', $this->params['src'] ); |
371 | 393 | return $status; |
372 | 394 | } |
— | — | @@ -377,26 +399,31 @@ |
378 | 400 | protected function doAttempt() { |
379 | 401 | $status = Status::newGood(); |
380 | 402 | // Create a destination backup copy as needed |
381 | | - if ( $this->checkDest ) { |
| 403 | + if ( $this->destAlreadyExists ) { |
382 | 404 | $status->merge( $this->checkAndBackupDest() ); |
383 | 405 | if ( !$status->isOK() ) { |
384 | 406 | return $status; |
385 | 407 | } |
386 | 408 | } |
387 | 409 | // Store the file at the destination |
388 | | - $status->merge( $this->backend->store( $this->params ) ); |
| 410 | + if ( !$this->destSameAsSource ) { |
| 411 | + $status->merge( $this->backend->store( $this->params ) ); |
| 412 | + } |
389 | 413 | return $status; |
390 | 414 | } |
391 | 415 | |
392 | 416 | protected function doRevert() { |
393 | | - // Remove the file saved to the destination |
394 | | - $params = array( 'src' => $this->params['dst'] ); |
395 | | - $status = $this->backend->delete( $params ); |
396 | | - if ( !$status->isOK() ) { |
397 | | - return $status; // also can't restore any dest file |
| 417 | + $status = Status::newGood(); |
| 418 | + if ( !$this->destSameAsSource ) { |
| 419 | + // Remove the file saved to the destination |
| 420 | + $params = array( 'src' => $this->params['dst'] ); |
| 421 | + $status->merge( $this->backend->delete( $params ) ); |
| 422 | + if ( !$status->isOK() ) { |
| 423 | + return $status; // also can't restore any dest file |
| 424 | + } |
| 425 | + // Restore any file that was at the destination |
| 426 | + $status->merge( $this->restoreDest() ); |
398 | 427 | } |
399 | | - // Restore any file that was at the destination |
400 | | - $status->merge( $this->restoreDest() ); |
401 | 428 | return $status; |
402 | 429 | } |
403 | 430 | |
— | — | @@ -404,7 +431,7 @@ |
405 | 432 | return md5_file( $this->params['src'] ); |
406 | 433 | } |
407 | 434 | |
408 | | - function storagePathsUsed() { |
| 435 | + public function storagePathsChanged() { |
409 | 436 | return array( $this->params['dst'] ); |
410 | 437 | } |
411 | 438 | } |
— | — | @@ -418,18 +445,18 @@ |
419 | 446 | * overwriteSame : override any existing file at destination |
420 | 447 | */ |
421 | 448 | class CreateFileOp extends FileOp { |
422 | | - protected $checkDest = true; |
| 449 | + protected $destAlreadyExists = true; |
423 | 450 | |
424 | 451 | protected function doPrecheck( array &$predicates ) { |
425 | 452 | $status = Status::newGood(); |
426 | 453 | // Check if destination file exists |
427 | 454 | if ( $this->fileExists( $this->params['dst'], $predicates ) ) { |
428 | | - if ( !$this->getParam( 'overwriteDest' ) ) { |
| 455 | + if ( !$this->getParam( 'overwriteDest' ) && !$this->getParam( 'overwriteSame' ) ) { |
429 | 456 | $status->fatal( 'backend-fail-alreadyexists', $this->params['dst'] ); |
430 | 457 | return $status; |
431 | 458 | } |
432 | 459 | } else { |
433 | | - $this->checkDest = false; |
| 460 | + $this->destAlreadyExists = false; |
434 | 461 | } |
435 | 462 | // Update file existence predicates |
436 | 463 | $predicates['exists'][$this->params['dst']] = true; |
— | — | @@ -439,26 +466,31 @@ |
440 | 467 | protected function doAttempt() { |
441 | 468 | $status = Status::newGood(); |
442 | 469 | // Create a destination backup copy as needed |
443 | | - if ( $this->checkDest ) { |
| 470 | + if ( $this->destAlreadyExists ) { |
444 | 471 | $status->merge( $this->checkAndBackupDest() ); |
445 | 472 | if ( !$status->isOK() ) { |
446 | 473 | return $status; |
447 | 474 | } |
448 | 475 | } |
449 | 476 | // Create the file at the destination |
450 | | - $status->merge( $this->backend->create( $this->params ) ); |
| 477 | + if ( !$this->destSameAsSource ) { |
| 478 | + $status->merge( $this->backend->create( $this->params ) ); |
| 479 | + } |
451 | 480 | return $status; |
452 | 481 | } |
453 | 482 | |
454 | 483 | protected function doRevert() { |
455 | | - // Remove the file saved to the destination |
456 | | - $params = array( 'src' => $this->params['dst'] ); |
457 | | - $status = $this->backend->delete( $params ); |
458 | | - if ( !$status->isOK() ) { |
459 | | - return $status; // also can't restore any dest file |
| 484 | + $status = Status::newGood(); |
| 485 | + if ( !$this->destSameAsSource ) { |
| 486 | + // Remove the file saved to the destination |
| 487 | + $params = array( 'src' => $this->params['dst'] ); |
| 488 | + $status->merge( $this->backend->delete( $params ) ); |
| 489 | + if ( !$status->isOK() ) { |
| 490 | + return $status; // also can't restore any dest file |
| 491 | + } |
| 492 | + // Restore any file that was at the destination |
| 493 | + $status->merge( $this->restoreDest() ); |
460 | 494 | } |
461 | | - // Restore any file that was at the destination |
462 | | - $status->merge( $this->restoreDest() ); |
463 | 495 | return $status; |
464 | 496 | } |
465 | 497 | |
— | — | @@ -466,7 +498,7 @@ |
467 | 499 | return md5( $this->params['content'] ); |
468 | 500 | } |
469 | 501 | |
470 | | - function storagePathsUsed() { |
| 502 | + public function storagePathsChanged() { |
471 | 503 | return array( $this->params['dst'] ); |
472 | 504 | } |
473 | 505 | } |
— | — | @@ -480,18 +512,18 @@ |
481 | 513 | * overwriteSame : override any existing file at destination |
482 | 514 | */ |
483 | 515 | class CopyFileOp extends FileOp { |
484 | | - protected $checkDest = true; |
| 516 | + protected $destAlreadyExists = true; |
485 | 517 | |
486 | 518 | protected function doPrecheck( array &$predicates ) { |
487 | 519 | $status = Status::newGood(); |
488 | 520 | // Check if destination file exists |
489 | 521 | if ( $this->fileExists( $this->params['dst'], $predicates ) ) { |
490 | | - if ( !$this->getParam( 'overwriteDest' ) ) { |
| 522 | + if ( !$this->getParam( 'overwriteDest' ) && !$this->getParam( 'overwriteSame' ) ) { |
491 | 523 | $status->fatal( 'backend-fail-alreadyexists', $this->params['dst'] ); |
492 | 524 | return $status; |
493 | 525 | } |
494 | 526 | } else { |
495 | | - $this->checkDest = false; |
| 527 | + $this->destAlreadyExists = false; |
496 | 528 | } |
497 | 529 | // Check if the source file exists |
498 | 530 | if ( !$this->fileExists( $this->params['src'], $predicates ) ) { |
— | — | @@ -506,27 +538,31 @@ |
507 | 539 | protected function doAttempt() { |
508 | 540 | $status = Status::newGood(); |
509 | 541 | // Create a destination backup copy as needed |
510 | | - if ( $this->checkDest ) { |
| 542 | + if ( $this->destAlreadyExists ) { |
511 | 543 | $status->merge( $this->checkAndBackupDest() ); |
512 | 544 | if ( !$status->isOK() ) { |
513 | 545 | return $status; |
514 | 546 | } |
515 | 547 | } |
516 | 548 | // Copy the file into the destination |
517 | | - $status->merge( $this->backend->copy( $this->params ) ); |
| 549 | + if ( !$this->destSameAsSource ) { |
| 550 | + $status->merge( $this->backend->copy( $this->params ) ); |
| 551 | + } |
518 | 552 | return $status; |
519 | 553 | } |
520 | 554 | |
521 | 555 | protected function doRevert() { |
522 | 556 | $status = Status::newGood(); |
523 | | - // Remove the file saved to the destination |
524 | | - $params = array( 'src' => $this->params['dst'] ); |
525 | | - $status->merge( $this->backend->delete( $params ) ); |
526 | | - if ( !$status->isOK() ) { |
527 | | - return $status; // also can't restore any dest file |
| 557 | + if ( !$this->destSameAsSource ) { |
| 558 | + // Remove the file saved to the destination |
| 559 | + $params = array( 'src' => $this->params['dst'] ); |
| 560 | + $status->merge( $this->backend->delete( $params ) ); |
| 561 | + if ( !$status->isOK() ) { |
| 562 | + return $status; // also can't restore any dest file |
| 563 | + } |
| 564 | + // Restore any file that was at the destination |
| 565 | + $status->merge( $this->restoreDest() ); |
528 | 566 | } |
529 | | - // Restore any file that was at the destination |
530 | | - $status->merge( $this->restoreDest() ); |
531 | 567 | return $status; |
532 | 568 | } |
533 | 569 | |
— | — | @@ -534,9 +570,13 @@ |
535 | 571 | return $this->getFileMD5( $this->params['src'] ); |
536 | 572 | } |
537 | 573 | |
538 | | - function storagePathsUsed() { |
539 | | - return array( $this->params['src'], $this->params['dst'] ); |
| 574 | + public function storagePathsRead() { |
| 575 | + return array( $this->params['src'] ); |
540 | 576 | } |
| 577 | + |
| 578 | + public function storagePathsChanged() { |
| 579 | + return array( $this->params['dst'] ); |
| 580 | + } |
541 | 581 | } |
542 | 582 | |
543 | 583 | /** |
— | — | @@ -549,7 +589,7 @@ |
550 | 590 | */ |
551 | 591 | class MoveFileOp extends FileOp { |
552 | 592 | protected $usingMove = false; // using backend move() function? |
553 | | - protected $checkDest = true; |
| 593 | + protected $destAlreadyExists = true; |
554 | 594 | |
555 | 595 | function initialize() { |
556 | 596 | // Use faster, native, move() if applicable |
— | — | @@ -560,12 +600,12 @@ |
561 | 601 | $status = Status::newGood(); |
562 | 602 | // Check if destination file exists |
563 | 603 | if ( $this->fileExists( $this->params['dst'], $predicates ) ) { |
564 | | - if ( !$this->getParam( 'overwriteDest' ) ) { |
| 604 | + if ( !$this->getParam( 'overwriteDest' ) && !$this->getParam( 'overwriteSame' ) ) { |
565 | 605 | $status->fatal( 'backend-fail-alreadyexists', $this->params['dst'] ); |
566 | 606 | return $status; |
567 | 607 | } |
568 | 608 | } else { |
569 | | - $this->checkDest = false; |
| 609 | + $this->destAlreadyExists = false; |
570 | 610 | } |
571 | 611 | // Check if the source file exists |
572 | 612 | if ( !$this->fileExists( $this->params['src'], $predicates ) ) { |
— | — | @@ -581,23 +621,37 @@ |
582 | 622 | protected function doAttempt() { |
583 | 623 | $status = Status::newGood(); |
584 | 624 | // Create a destination backup copy as needed |
585 | | - if ( $this->checkDest ) { |
| 625 | + if ( $this->destAlreadyExists ) { |
586 | 626 | $status->merge( $this->checkAndBackupDest() ); |
587 | 627 | if ( !$status->isOK() ) { |
588 | 628 | return $status; |
589 | 629 | } |
590 | 630 | } |
591 | | - // Native moves: move the file into the destination |
592 | | - if ( $this->usingMove ) { |
593 | | - $status->merge( $this->backend->move( $this->params ) ); |
594 | | - // Non-native moves: copy the file into the destination & delete source |
| 631 | + if ( !$this->destSameAsSource ) { |
| 632 | + // Native moves: move the file into the destination |
| 633 | + if ( $this->usingMove ) { |
| 634 | + $status->merge( $this->backend->move( $this->params ) ); |
| 635 | + // Non-native moves: copy the file into the destination & delete source |
| 636 | + } else { |
| 637 | + // Copy source to dest |
| 638 | + $status->merge( $this->backend->copy( $this->params ) ); |
| 639 | + if ( !$status->isOK() ) { |
| 640 | + return $status; |
| 641 | + } |
| 642 | + // Delete source |
| 643 | + $params = array( 'src' => $this->params['src'] ); |
| 644 | + $status->merge( $this->backend->delete( $params ) ); |
| 645 | + if ( !$status->isOK() ) { |
| 646 | + return $status; |
| 647 | + } |
| 648 | + } |
595 | 649 | } else { |
596 | | - // Copy source to dest |
597 | | - $status->merge( $this->backend->copy( $this->params ) ); |
| 650 | + // Create a source backup copy as needed |
| 651 | + $status->merge( $this->backupSource() ); |
598 | 652 | if ( !$status->isOK() ) { |
599 | 653 | return $status; |
600 | 654 | } |
601 | | - // Delete source |
| 655 | + // Just delete source as the destination needs no changes |
602 | 656 | $params = array( 'src' => $this->params['src'] ); |
603 | 657 | $status->merge( $this->backend->delete( $params ) ); |
604 | 658 | if ( !$status->isOK() ) { |
— | — | @@ -609,33 +663,39 @@ |
610 | 664 | |
611 | 665 | protected function doRevert() { |
612 | 666 | $status = Status::newGood(); |
613 | | - // Native moves: move the file back to the source |
614 | | - if ( $this->usingMove ) { |
615 | | - $params = array( |
616 | | - 'src' => $this->params['dst'], |
617 | | - 'dst' => $this->params['src'] |
618 | | - ); |
619 | | - $status->merge( $this->backend->move( $params ) ); |
620 | | - if ( !$status->isOK() ) { |
621 | | - return $status; // also can't restore any dest file |
| 667 | + if ( !$this->destSameAsSource ) { |
| 668 | + // Native moves: move the file back to the source |
| 669 | + if ( $this->usingMove ) { |
| 670 | + $params = array( |
| 671 | + 'src' => $this->params['dst'], |
| 672 | + 'dst' => $this->params['src'] |
| 673 | + ); |
| 674 | + $status->merge( $this->backend->move( $params ) ); |
| 675 | + if ( !$status->isOK() ) { |
| 676 | + return $status; // also can't restore any dest file |
| 677 | + } |
| 678 | + // Non-native moves: remove the file saved to the destination |
| 679 | + } else { |
| 680 | + // Copy destination back to source |
| 681 | + $params = array( 'src' => $this->params['dst'], 'dst' => $this->params['src'] ); |
| 682 | + $status = $this->backend->copy( $params ); |
| 683 | + if ( !$status->isOK() ) { |
| 684 | + return $status; // also can't restore any dest file |
| 685 | + } |
| 686 | + // Delete destination |
| 687 | + $params = array( 'src' => $this->params['dst'] ); |
| 688 | + $status = $this->backend->delete( $params ); |
| 689 | + if ( !$status->isOK() ) { |
| 690 | + return $status; // also can't restore any dest file |
| 691 | + } |
622 | 692 | } |
623 | | - // Non-native moves: remove the file saved to the destination |
| 693 | + // Restore any file that was at the destination |
| 694 | + $status->merge( $this->restoreDest() ); |
624 | 695 | } else { |
625 | | - // Copy destination back to source |
626 | | - $params = array( 'src' => $this->params['dst'], 'dst' => $this->params['src'] ); |
627 | | - $status = $this->backend->copy( $params ); |
628 | | - if ( !$status->isOK() ) { |
629 | | - return $status; // also can't restore any dest file |
630 | | - } |
631 | | - // Delete destination |
632 | | - $params = array( 'src' => $this->params['dst'] ); |
633 | | - $status = $this->backend->delete( $params ); |
634 | | - if ( !$status->isOK() ) { |
635 | | - return $status; // also can't restore any dest file |
636 | | - } |
| 696 | + // Restore any source file |
| 697 | + return $this->restoreSource(); |
637 | 698 | } |
638 | | - // Restore any file that was at the destination |
639 | | - $status->merge( $this->restoreDest() ); |
| 699 | + |
640 | 700 | return $status; |
641 | 701 | } |
642 | 702 | |
— | — | @@ -643,9 +703,13 @@ |
644 | 704 | return $this->getFileMD5( $this->params['src'] ); |
645 | 705 | } |
646 | 706 | |
647 | | - function storagePathsUsed() { |
648 | | - return array( $this->params['src'], $this->params['dst'] ); |
| 707 | + public function storagePathsRead() { |
| 708 | + return array( $this->params['src'] ); |
649 | 709 | } |
| 710 | + |
| 711 | + public function storagePathsChanged() { |
| 712 | + return array( $this->params['dst'] ); |
| 713 | + } |
650 | 714 | } |
651 | 715 | |
652 | 716 | /** |
— | — | @@ -656,7 +720,7 @@ |
657 | 721 | * overwriteDest : do nothing and pass if an identical file exists at destination |
658 | 722 | */ |
659 | 723 | class ConcatenateFileOp extends FileOp { |
660 | | - protected $checkDest = true; |
| 724 | + protected $destAlreadyExists = true; |
661 | 725 | |
662 | 726 | protected function doPrecheck( array &$predicates ) { |
663 | 727 | $status = Status::newGood(); |
— | — | @@ -667,7 +731,7 @@ |
668 | 732 | return $status; |
669 | 733 | } |
670 | 734 | } else { |
671 | | - $this->checkDest = false; |
| 735 | + $this->destAlreadyExists = false; |
672 | 736 | } |
673 | 737 | // Check that source files exists |
674 | 738 | foreach ( $this->params['srcs'] as $source ) { |
— | — | @@ -684,7 +748,7 @@ |
685 | 749 | protected function doAttempt() { |
686 | 750 | $status = Status::newGood(); |
687 | 751 | // Create a destination backup copy as needed |
688 | | - if ( $this->checkDest ) { |
| 752 | + if ( $this->destAlreadyExists ) { |
689 | 753 | $status->merge( $this->checkAndBackupDest() ); |
690 | 754 | if ( !$status->isOK() ) { |
691 | 755 | return $status; |
— | — | @@ -711,9 +775,13 @@ |
712 | 776 | return null; // defer this until we finish building the new file |
713 | 777 | } |
714 | 778 | |
715 | | - function storagePathsUsed() { |
716 | | - return array_merge( $this->params['srcs'], $this->params['dst'] ); |
| 779 | + public function storagePathsRead() { |
| 780 | + return $this->params['srcs']; |
717 | 781 | } |
| 782 | + |
| 783 | + public function storagePathsChanged() { |
| 784 | + return array( $this->params['dst'] ); |
| 785 | + } |
718 | 786 | } |
719 | 787 | |
720 | 788 | /** |
— | — | @@ -762,7 +830,7 @@ |
763 | 831 | return $this->restoreSource(); |
764 | 832 | } |
765 | 833 | |
766 | | - function storagePathsUsed() { |
| 834 | + public function storagePathsChanged() { |
767 | 835 | return array( $this->params['src'] ); |
768 | 836 | } |
769 | 837 | } |
— | — | @@ -771,10 +839,6 @@ |
772 | 840 | * Placeholder operation that has no params and does nothing |
773 | 841 | */ |
774 | 842 | class NullFileOp extends FileOp { |
775 | | - protected function doPrecheck( array &$predicates ) { |
776 | | - return Status::newGood(); |
777 | | - } |
778 | | - |
779 | 843 | protected function doAttempt() { |
780 | 844 | return Status::newGood(); |
781 | 845 | } |
Index: branches/FileBackend/phase3/includes/filerepo/backend/LockManager.php |
— | — | @@ -267,9 +267,8 @@ |
268 | 268 | * cache can be setup to track servers that recently missed queries; such |
269 | 269 | * servers will not be trusted for obtaining locks. |
270 | 270 | * |
271 | | - * For performance, deadlock detection should be disabled and a small |
272 | | - * lock-wait timeout should be set via server config. In innoDB, this can |
273 | | - * done via the innodb_deadlock_detect and innodb_lock_wait_timeout settings. |
| 271 | + * For performance, a small lock-wait timeout should be set via server config. |
| 272 | + * In innoDB, this can done via the innodb_lock_wait_timeout setting. |
274 | 273 | */ |
275 | 274 | class DBLockManager extends LockManager { |
276 | 275 | /** @var Array Map of bucket indexes to peer sets */ |
Index: branches/FileBackend/phase3/includes/filerepo/backend/FileBackend.php |
— | — | @@ -254,10 +254,11 @@ |
255 | 255 | * Avoid using this function outside of FileBackendScopedLock. |
256 | 256 | * |
257 | 257 | * @param $paths Array Storage paths |
| 258 | + * @param $type integer LockManager::LOCK_EX, LockManager::LOCK_SH |
258 | 259 | * @return Status |
259 | 260 | */ |
260 | | - final public function lockFiles( array $paths ) { |
261 | | - return $this->lockManager->lock( $paths, LockManager::LOCK_EX ); |
| 261 | + final public function lockFiles( array $paths, $type ) { |
| 262 | + return $this->lockManager->lock( $paths, $type ); |
262 | 263 | } |
263 | 264 | |
264 | 265 | /** |
— | — | @@ -266,10 +267,11 @@ |
267 | 268 | * Avoid using this function outside of FileBackendScopedLock. |
268 | 269 | * |
269 | 270 | * @param $paths Array Storage paths |
| 271 | + * @param $type integer LockManager::LOCK_EX, LockManager::LOCK_SH |
270 | 272 | * @return Status |
271 | 273 | */ |
272 | | - final public function unlockFiles( array $paths ) { |
273 | | - return $this->lockManager->unlock( $paths, LockManager::LOCK_EX ); |
| 274 | + final public function unlockFiles( array $paths, $type ) { |
| 275 | + return $this->lockManager->unlock( $paths, $type ); |
274 | 276 | } |
275 | 277 | |
276 | 278 | /** |
— | — | @@ -281,11 +283,12 @@ |
282 | 284 | * the status updated. Unlock fatals will not change the status "OK" value. |
283 | 285 | * |
284 | 286 | * @param $paths Array Storage paths |
| 287 | + * @param $type integer LockManager::LOCK_EX, LockManager::LOCK_SH |
285 | 288 | * @param $status Status Status to update on lock/unlock |
286 | 289 | * @return FileBackendScopedLock|null Returns null on failure |
287 | 290 | */ |
288 | | - final public function getScopedFileLocks( array $paths, Status $status ) { |
289 | | - return FileBackendScopedLock::factory( $this, $paths, $status ); |
| 291 | + final public function getScopedFileLocks( array $paths, $type, Status $status ) { |
| 292 | + return FileBackendScopedLock::factory( $this, $paths, $type, $status ); |
290 | 293 | } |
291 | 294 | } |
292 | 295 | |
— | — | @@ -499,7 +502,7 @@ |
500 | 503 | } |
501 | 504 | |
502 | 505 | // Try to lock those files for the scope of this function... |
503 | | - $scopedLock = $this->getScopedFileLocks( $filesToLock, $status ); |
| 506 | + $scopedLock = $this->getScopedFileLocks( $filesToLock, LockManager::LOCK_EX, $status ); |
504 | 507 | if ( !$status->isOK() ) { |
505 | 508 | return $status; // abort |
506 | 509 | } |
— | — | @@ -686,16 +689,21 @@ |
687 | 690 | protected $status; |
688 | 691 | /** @var Array List of storage paths*/ |
689 | 692 | protected $paths; |
| 693 | + protected $type; // integer lock type |
690 | 694 | |
691 | 695 | /** |
692 | 696 | * @param $backend FileBackendBase |
693 | 697 | * @param $paths Array List of storage paths |
| 698 | + * @param $type integer LockManager::LOCK_EX, LockManager::LOCK_SH |
694 | 699 | * @param $status Status |
695 | 700 | */ |
696 | | - protected function __construct( FileBackendBase $backend, array $paths, Status $status ) { |
| 701 | + protected function __construct( |
| 702 | + FileBackendBase $backend, array $paths, $type, Status $status |
| 703 | + ) { |
697 | 704 | $this->backend = $backend; |
698 | 705 | $this->paths = $paths; |
699 | 706 | $this->status = $status; |
| 707 | + $this->type = $type; |
700 | 708 | } |
701 | 709 | |
702 | 710 | protected function __clone() {} |
— | — | @@ -708,21 +716,24 @@ |
709 | 717 | * |
710 | 718 | * @param $backend FileBackendBase |
711 | 719 | * @param $files Array List of storage paths |
| 720 | + * @param $type integer LockManager::LOCK_EX, LockManager::LOCK_SH |
712 | 721 | * @param $status Status |
713 | 722 | * @return FileBackendScopedLock|null |
714 | 723 | */ |
715 | | - public static function factory( FileBackendBase $backend, array $files, Status $status ) { |
716 | | - $lockStatus = $backend->lockFiles( $files ); |
| 724 | + public static function factory( |
| 725 | + FileBackendBase $backend, array $files, $type, Status $status |
| 726 | + ) { |
| 727 | + $lockStatus = $backend->lockFiles( $files, $type ); |
717 | 728 | $status->merge( $lockStatus ); |
718 | 729 | if ( $lockStatus->isOK() ) { |
719 | | - return new self( $backend, $files, $status ); |
| 730 | + return new self( $backend, $files, $type, $status ); |
720 | 731 | } |
721 | 732 | return null; |
722 | 733 | } |
723 | 734 | |
724 | 735 | function __destruct() { |
725 | 736 | $wasOk = $this->status->isOK(); |
726 | | - $this->status->merge( $this->backend->unlockFiles( $this->paths ) ); |
| 737 | + $this->status->merge( $this->backend->unlockFiles( $this->paths, $this->type ) ); |
727 | 738 | if ( $wasOk ) { |
728 | 739 | // Make sure status is OK, despite any unlockFiles() fatals |
729 | 740 | $this->status->setResult( true, $this->status->value ); |