r104155 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r104154‎ | r104155 | r104156 >
Date:11:02, 24 November 2011
Author:aaron
Status:deferred
Tags:
Comment:
* Made doOperations() check for most errors upfront before changing files around. This is better for performance and reliability as we want to avoid calling revert()
* Remove 'ignoreMissingSource' and 'overwriteSame' params from most of the FileBackend functions as FileOp is now handling these
* Removed exceptions from FileOp classes, using Status istead
* Added FileBackend::getFileHash to get ETags (or Content-MD5 for Azure)
* Made doOperations() actually unlock on abort
* Added prepare() to FileBackendMultiWrite/FSFileBackend
* Added FileBackend::isStoragePath() convenience function
* Tweaked FileOp class names for consistency
Modified paths:
  • /branches/FileBackend/phase3/includes/filerepo/backend/FSFileBackend.php (modified) (history)
  • /branches/FileBackend/phase3/includes/filerepo/backend/FileBackend.php (modified) (history)
  • /branches/FileBackend/phase3/includes/filerepo/backend/FileBackendMultiWrite.php (modified) (history)
  • /branches/FileBackend/phase3/includes/filerepo/backend/FileLockManager.php (modified) (history)
  • /branches/FileBackend/phase3/includes/filerepo/backend/FileOp.php (modified) (history)

Diff [purge]

Index: branches/FileBackend/phase3/includes/filerepo/backend/FileBackendMultiWrite.php
@@ -12,9 +12,11 @@
1313 * The order that the backends are defined sets the priority of which
1414 * backend is read from or written to first. Functions like fileExists()
1515 * and getFileProps() will return information based on the first backend
16 - * that has the file (normally both should have it anyway). Functions like
17 - * getFileList() will return results from the first backend that is not
18 - * declared as non-persistent cache. This is done for consistency.
 16+ * that has the file (normally both should have it anyway). Special cases:
 17+ * a) getFileList() will return results from the first backend that is
 18+ * not declared as non-persistent cache. This is for correctness.
 19+ * b) getFileHash() will always check only the master backend to keep the
 20+ * result format consistent.
1921 *
2022 * All write operations are performed on all backends.
2123 * If an operation fails on one backend it will be rolled back from the others.
@@ -32,20 +34,32 @@
3335 * 'lockManger' : FileLockManager instance
3436 * 'backends' : Array of (backend object, settings) pairs.
3537 * The settings per backend include:
36 - * 'isCache': The backend is non-persistent
 38+ * 'isCache' : The backend is non-persistent
 39+ * 'isMaster': This must be set for one non-persistent backend.
3740 * @param $config Array
3841 */
3942 public function __construct( array $config ) {
4043 $this->name = $config['name'];
4144 $this->lockManager = $config['lockManger'];
 45+
 46+ $hasMaster = false;
4247 foreach ( $config['backends'] as $index => $info ) {
4348 list( $backend, $settings ) = $info;
4449 $this->fileBackends[$index] = $backend;
4550 // Default backend settings
46 - $defaults = array( 'isCache' => false );
 51+ $defaults = array( 'isCache' => false, 'isMaster' => false );
4752 // Apply custom backend settings to defaults
4853 $this->fileBackendsInfo[$index] = $info + $defaults;
 54+ if ( $info['isMaster'] ) {
 55+ if ( $hasMaster ) {
 56+ throw new MWException( 'More than one master backend defined.' );
 57+ }
 58+ $hasMaster = true;
 59+ }
4960 }
 61+ if ( !$hasMaster ) {
 62+ throw new MWException( 'No master backend defined.' );
 63+ }
5064 }
5165
5266 final public function doOperations( array $ops ) {
@@ -74,6 +88,15 @@
7589 return $status; // abort
7690 }
7791
 92+ // Do pre-checks for each operation; abort on failure...
 93+ foreach ( $performOps as $index => $fileOp ) {
 94+ $status->merge( $fileOp->precheck() );
 95+ if ( !$status->isOK() ) { // operation failed?
 96+ $status->merge( $this->unlockFiles( $filesToLock ) );
 97+ return $status;
 98+ }
 99+ }
 100+
78101 // Attempt each operation; abort on failure...
79102 foreach ( $performOps as $index => $fileOp ) {
80103 $status->merge( $fileOp->attempt() );
@@ -85,6 +108,7 @@
86109 $status->merge( $performOps[$pos]->revert() );
87110 $pos--;
88111 }
 112+ $status->merge( $this->unlockFiles( $filesToLock ) );
89113 return $status;
90114 }
91115 }
@@ -137,6 +161,14 @@
138162 return $this->doOperation( array( $op ) );
139163 }
140164
 165+ function prepare( array $params ) {
 166+ $status = Status::newGood();
 167+ foreach ( $this->backends as $backend ) {
 168+ $status->merge( $backend->prepare( $params ) );
 169+ }
 170+ return $status;
 171+ }
 172+
141173 function fileExists( array $params ) {
142174 foreach ( $this->backends as $backend ) {
143175 if ( $backend->fileExists( $params ) ) {
@@ -145,7 +177,27 @@
146178 }
147179 return false;
148180 }
149 -
 181+
 182+ function getFileHash( array $params ) {
 183+ foreach ( $this->backends as $backend ) {
 184+ // Skip non-master for consistent hash formats
 185+ if ( $this->fileBackendsInfo[$index]['isMaster'] ) {
 186+ return $backend->getFileHash( $params );
 187+ }
 188+ }
 189+ return false;
 190+ }
 191+
 192+ function getHashType() {
 193+ foreach ( $this->backends as $backend ) {
 194+ // Skip non-master for consistent hash formats
 195+ if ( $this->fileBackendsInfo[$index]['isMaster'] ) {
 196+ return $backend->getHashType();
 197+ }
 198+ }
 199+ return null; // shouldn't happen
 200+ }
 201+
150202 function getFileProps( array $params ) {
151203 foreach ( $this->backends as $backend ) {
152204 $props = $backend->getFileProps( $params );
Index: branches/FileBackend/phase3/includes/filerepo/backend/FileOp.php
@@ -18,11 +18,13 @@
1919 protected $backend;
2020
2121 protected $state;
22 - protected $failedAttempt;
 22+ protected $failed;
2323
 24+ /* Object life-cycle */
2425 const STATE_NEW = 1;
25 - const STATE_ATTEMPTED = 2;
26 - const STATE_DONE = 3;
 26+ const STATE_CHECKED = 2;
 27+ const STATE_ATTEMPTED = 3;
 28+ const STATE_DONE = 4;
2729
2830 /**
2931 * Build a new file operation transaction
@@ -34,23 +36,42 @@
3537 $this->backend = $backend;
3638 $this->params = $params;
3739 $this->state = self::STATE_NEW;
38 - $this->failedAttempt = false;
 40+ $this->failed = false;
3941 $this->initialize();
4042 }
4143
4244 /**
 45+ * Check preconditions of the operation and possibly stash temp files
 46+ *
 47+ * @return Status
 48+ */
 49+ final public function precheck() {
 50+ if ( $this->state !== self::STATE_NEW ) {
 51+ return Status::newFatal( 'fileop-fail-state', self::STATE_NEW, $this->state );
 52+ }
 53+ $this->state = self::STATE_CHECKED;
 54+ $status = $this->doPrecheck();
 55+ if ( !$status->isOK() ) {
 56+ $this->failed = true;
 57+ }
 58+ return $status;
 59+ }
 60+
 61+ /**
4362 * Attempt the operation; this must be reversible
4463 *
4564 * @return Status
4665 */
4766 final public function attempt() {
48 - if ( $this->state !== self::STATE_NEW ) {
49 - throw new MWException( "Cannot attempt operation called twice." );
 67+ if ( $this->state !== self::STATE_CHECKED ) {
 68+ return Status::newFatal( 'fileop-fail-state', self::STATE_CHECKED, $this->state );
 69+ } elseif ( $this->failed ) { // failed precheck
 70+ return Status::newFatal( 'fileop-fail-attempt-precheck' );
5071 }
5172 $this->state = self::STATE_ATTEMPTED;
5273 $status = $this->doAttempt();
5374 if ( !$status->isOK() ) {
54 - $this->failedAttempt = true;
 75+ $this->failed = true;
5576 }
5677 return $status;
5778 }
@@ -62,10 +83,10 @@
6384 */
6485 final public function revert() {
6586 if ( $this->state !== self::STATE_ATTEMPTED ) {
66 - throw new MWException( "Cannot rollback an unstarted or finished operation." );
 87+ return Status::newFatal( 'fileop-fail-state', self::STATE_ATTEMPTED, $this->state );
6788 }
6889 $this->state = self::STATE_DONE;
69 - if ( $this->failedAttempt ) {
 90+ if ( $this->failed ) {
7091 $status = Status::newGood(); // nothing to revert
7192 } else {
7293 $status = $this->doRevert();
@@ -80,11 +101,11 @@
81102 */
82103 final public function finish() {
83104 if ( $this->state !== self::STATE_ATTEMPTED ) {
84 - throw new MWException( "Cannot cleanup an unstarted or finished operation." );
 105+ return Status::newFatal( 'fileop-fail-state', self::STATE_ATTEMPTED, $this->state );
85106 }
86107 $this->state = self::STATE_DONE;
87 - if ( $this->failedAttempt ) {
88 - $status = Status::newGood(); // nothing to revert
 108+ if ( $this->failed ) {
 109+ $status = Status::newGood(); // nothing to finish
89110 } else {
90111 $status = $this->doFinish();
91112 }
@@ -108,6 +129,11 @@
109130 /**
110131 * @return Status
111132 */
 133+ abstract protected function doPrecheck();
 134+
 135+ /**
 136+ * @return Status
 137+ */
112138 abstract protected function doAttempt();
113139
114140 /**
@@ -119,26 +145,138 @@
120146 * @return Status
121147 */
122148 abstract protected function doFinish();
 149+
 150+ /**
 151+ * Backup any file at the destination to a temporary file.
 152+ * Don't bother backing it up unless we might overwrite the file.
 153+ * This assumes that the destination is in the backend and that
 154+ * the source is either in the backend or on the file system.
 155+ *
 156+ * @return Status
 157+ */
 158+ protected function checkAndBackupDest() {
 159+ $status = Status::newGood();
 160+ // Check if a file already exists at the destination
 161+ if ( !$this->backend->fileExists( $this->params['dest'] ) ) {
 162+ return $status; // nothing to do
 163+ }
 164+
 165+ if ( !empty( $this->params['overwriteDest'] ) ) {
 166+ // Create a temporary backup copy...
 167+ $this->tmpDestFile = $this->getLocalCopy( $this->params['dest'] );
 168+ if ( !$this->tmpDestFile ) {
 169+ $status->fatal( 'backend-fail-backup', $this->params['dest'] );
 170+ return $status;
 171+ }
 172+ } elseif ( !empty( $this->params['overwriteSame'] ) ) {
 173+ // Get the source content hash (if there is a single source)
 174+ $shash = $this->getSourceMD5();
 175+ // If there is a single source, then we can do some checks already.
 176+ // For things like concatenate(), we need to build a temp file first.
 177+ if ( $shash !== null ) {
 178+ $dhash = $this->getFileMD5( $this->params['dest'] );
 179+ if ( !strlen( $shash ) || !strlen( $dhash ) ) {
 180+ $status->fatal( 'backend-fail-hashes' );
 181+ return $status;
 182+ }
 183+ // Give an error if the files are not identical
 184+ if ( $shash !== $dhash ) {
 185+ $status->fatal( 'backend-fail-notsame',
 186+ $this->params['source'], $this->params['dest'] );
 187+ }
 188+ return $status; // do nothing; either OK or bad status
 189+ }
 190+ } else {
 191+ $status->fatal( 'backend-fail-alreadyexists', $params['dest'] );
 192+ return $status;
 193+ }
 194+
 195+ return $status;
 196+ }
 197+
 198+ /**
 199+ * checkAndBackupDest() helper function to get the source file MD5.
 200+ * Returns false on failure and null if there is no single source.
 201+ *
 202+ * @return string|false|null
 203+ */
 204+ protected function getSourceMD5() {
 205+ return null; // N/A
 206+ }
 207+
 208+ /**
 209+ * checkAndBackupDest() helper function to get the MD5 of a file.
 210+ *
 211+ * @return string|false False on failure
 212+ */
 213+ final protected function getFileMD5( $path ) {
 214+ // Source file is in backend
 215+ if ( FileBackend::isStoragePath( $path ) ) {
 216+ // For some backends (e.g. Swift, Azure) we can get
 217+ // standard hashes to use for this types of comparisons.
 218+ if ( $this->backend->getHashType() === 'md5' ) {
 219+ $hash = $this->backend->getFileHash( $path );
 220+ } else {
 221+ $tmp = $this->getLocalCopy( $path );
 222+ if ( !$tmp ) {
 223+ return false; // error
 224+ }
 225+ $hash = md5_file( $tmp->getPath() );
 226+ }
 227+ // Source file is on disk (FS)
 228+ } else {
 229+ $hash = md5_file( $path );
 230+ }
 231+ return $hash;
 232+ }
 233+
 234+ /**
 235+ * Restore any temporary destination backup file
 236+ *
 237+ * @return Status
 238+ */
 239+ protected function restoreDest() {
 240+ $status = Status::newGood();
 241+ // Restore any file that was at the destination
 242+ if ( $this->tmpDestFile ) {
 243+ $params = array(
 244+ 'source' => $this->tmpDestFile->getPath(),
 245+ 'dest' => $this->params['dest']
 246+ );
 247+ $status = $this->backend->store( $params );
 248+ if ( !$status->isOK() ) {
 249+ return $status;
 250+ }
 251+ }
 252+ return $status;
 253+ }
123254 }
124255
125256 /**
126257 * Store a file into the backend from a file on disk.
127 - * Parameters must match FileBackend::store(), which include:
128 - * source : source path on disk
 258+ * Parameters similar to FileBackend::store(), which include:
 259+ * source : source path on disk (FS)
129260 * dest : destination storage path
130261 * overwriteDest : do nothing and pass if an identical file exists at destination
131262 * overwriteSame : override any existing file at destination
132263 */
133 -class FileStoreOp extends FileOp {
 264+class StoreFileOp extends FileOp {
134265 /** @var TempLocalFile|null */
135266 protected $tmpDestFile; // temp copy of existing destination file
136267
137 - function doAttempt() {
138 - // Create a backup copy of any file that exists at destination
139 - $status = $this->backupDest();
140 - if ( !$status->isOK() ) {
 268+ function doPrecheck() {
 269+ $status = Status::newGood();
 270+ // Check if the source files exists on disk (FS)
 271+ if ( !file_exists( $this->params['source'] ) ) {
 272+ $status->fatal( 'backend-fail-notexists', $this->params['source'] );
141273 return $status;
142274 }
 275+ // Create a destination backup copy as needed
 276+ $status->merge( $this->checkAndBackupDest() );
 277+ return $status;
 278+ }
 279+
 280+ function doAttempt() {
143281 // Store the file at the destination
144282 $status = $this->backend->store( $this->params );
145283 return $status;
@@ -164,65 +302,27 @@
165303 return array( $this->params['dest'] );
166304 }
167305
168 - /**
169 - * Backup any file at destination to a temporary file.
170 - * Don't bother backing it up unless we might overwrite the file.
171 - *
172 - * @return Status
173 - */
174 - protected function backupDest() {
175 - $status = Status::newGood();
176 - // Check if a file already exists at the destination...
177 - if ( $this->backend->fileExists( $this->params['dest'] ) ) {
178 - if ( $this->params['overwriteDest'] ) {
179 - // Create a temporary backup copy...
180 - $this->tmpDestFile = $this->getLocalCopy( $this->params['dest'] );
181 - if ( !$this->tmpDestFile ) {
182 - $status->fatal( 'backend-fail-restore', $this->params['dest'] );
183 - return $status;
184 - }
185 - }
186 - }
187 - return $status;
 306+ function getSourceMD5() {
 307+ return md5_file( $this->params['source'] );
188308 }
189 -
190 - /**
191 - * Restore any temporary destination backup file
192 - *
193 - * @return Status
194 - */
195 - protected function restoreDest() {
196 - $status = Status::newGood();
197 - // Restore any file that was at the destination
198 - if ( $this->tmpDestFile ) {
199 - $params = array(
200 - 'source' => $this->tmpDestFile->getPath(),
201 - 'dest' => $this->params['dest']
202 - );
203 - $status = $this->backend->store( $params );
204 - if ( !$status->isOK() ) {
205 - return $status;
206 - }
207 - }
208 - return $status;
209 - }
210309 }
211310
212311 /**
213312 * Create a file in the backend with the given content.
214 - * Parameters must match FileBackend::create(), which include:
 313+ * Parameters similar to FileBackend::create(), which include:
215314 * content : a string of raw file contents
216315 * dest : destination storage path
217316 * overwriteDest : do nothing and pass if an identical file exists at destination
218317 * overwriteSame : override any existing file at destination
219318 */
220 -class FileCreateOp extends FileStoreOp {
 319+class CreateFileOp extends FileOp {
 320+ function doPrecheck() {
 321+ // Create a destination backup copy as needed
 322+ $status = $this->checkAndBackupDest();
 323+ return $status;
 324+ }
 325+
221326 function doAttempt() {
222 - // Create a backup copy of any file that exists at destination
223 - $status = $this->backupDest();
224 - if ( !$status->isOK() ) {
225 - return $status;
226 - }
227327 // Create the file at the destination
228328 $status = $this->backend->create( $this->params );
229329 return $status;
@@ -247,23 +347,35 @@
248348 function storagePathsToLock() {
249349 return array( $this->params['dest'] );
250350 }
 351+
 352+ function getSourceMD5() {
 353+ return md5( $this->params['content'] );
 354+ }
251355 }
252356
253357 /**
254358 * Copy a file from one storage path to another in the backend.
255 - * Parameters must match FileBackend::copy(), which include:
 359+ * Parameters similar to FileBackend::copy(), which include:
256360 * source : source storage path
257361 * dest : destination storage path
258362 * overwriteDest : do nothing and pass if an identical file exists at destination
259363 * overwriteSame : override any existing file at destination
260364 */
261 -class FileCopyOp extends FileStoreOp {
262 - function doAttempt() {
263 - // Create a backup copy of any file that exists at destination
264 - $status = $this->backupDest();
265 - if ( !$status->isOK() ) {
 365+class CopyFileOp extends StoreFileOp {
 366+ function doPrecheck() {
 367+ $status = Status::newGood();
 368+ // Check if the source files exists on disk
 369+ $params = array( 'source' => $this->params['source'] );
 370+ if ( !$this->backend->fileExists( $params ) ) {
 371+ $status->fatal( 'backend-fail-notexists', $this->params['source'] );
266372 return $status;
267373 }
 374+ // Create a destination backup copy as needed
 375+ $status->merge( $this->checkAndBackupDest() );
 376+ return $status;
 377+ }
 378+
 379+ function doAttempt() {
268380 // Copy the file into the destination
269381 $status = $this->backend->copy( $this->params );
270382 return $status;
@@ -288,17 +400,21 @@
289401 function storagePathsToLock() {
290402 return array( $this->params['source'], $this->params['dest'] );
291403 }
 404+
 405+ function getSourceMD5() {
 406+ return $this->getFileMD5( $this->params['source'] );
 407+ }
292408 }
293409
294410 /**
295411 * Move a file from one storage path to another in the backend.
296 - * Parameters must match FileBackend::move(), which include:
 412+ * Parameters similar to FileBackend::move(), which include:
297413 * source : source storage path
298414 * dest : destination storage path
299415 * overwriteDest : do nothing and pass if an identical file exists at destination
300416 * overwriteSame : override any existing file at destination
301417 */
302 -class FileMoveOp extends FileStoreOp {
 418+class MoveFileOp extends FileOp {
303419 protected $usingMove = false; // using backend move() function?
304420
305421 function initialize() {
@@ -306,12 +422,20 @@
307423 $this->usingMove = $this->backend->canMove( $this->params );
308424 }
309425
310 - function doAttempt() {
311 - // Create a backup copy of any file that exists at destination
312 - $status = $this->backupDest();
313 - if ( !$status->isOK() ) {
 426+ function doPrecheck() {
 427+ $status = Status::newGood();
 428+ // Check if the source files exists on disk
 429+ $params = array( 'source' => $this->params['source'] );
 430+ if ( !$this->backend->fileExists( $params ) ) {
 431+ $status->fatal( 'backend-fail-notexists', $this->params['source'] );
314432 return $status;
315433 }
 434+ // Create a destination backup copy as needed
 435+ $status->merge( $this->checkAndBackupDest() );
 436+ return $status;
 437+ }
 438+
 439+ function doAttempt() {
316440 // Native moves: move the file into the destination
317441 if ( $this->usingMove ) {
318442 $status = $this->backend->move( $this->params );
@@ -361,20 +485,30 @@
362486 function storagePathsToLock() {
363487 return array( $this->params['source'], $this->params['dest'] );
364488 }
 489+
 490+ function getSourceMD5() {
 491+ return $this->getFileMD5( $this->params['source'] );
 492+ }
365493 }
366494
367495 /**
368496 * Combines files from severals storage paths into a new file in the backend.
369 - * Parameters must match FileBackend::concatenate(), which include:
 497+ * Parameters similar to FileBackend::concatenate(), which include:
370498 * sources : ordered source storage paths (e.g. chunk1,chunk2,...)
371499 * dest : destination storage path
372500 * overwriteDest : do nothing and pass if an identical file exists at destination
373501 * overwriteSame : override any existing file at destination
374502 */
375 -class FileConcatenateOp extends FileStoreOp {
 503+class ConcatenateFileOp extends FileOp {
 504+ function doPrecheck() {
 505+ // Create a destination backup copy as needed
 506+ $status = $this->checkAndBackupDest();
 507+ return $status;
 508+ }
 509+
376510 function doAttempt() {
377511 // Create a backup copy of any file that exists at destination
378 - $status = $this->backupDest();
 512+ $status = $this->checkAndBackupDest();
379513 if ( !$status->isOK() ) {
380514 return $status;
381515 }
@@ -402,19 +536,24 @@
403537 function storagePathsToLock() {
404538 return array_merge( $this->params['sources'], $this->params['dest'] );
405539 }
 540+
 541+ function getSourceMD5() {
 542+ return null; // defer this until we finish building the new file
 543+ }
406544 }
407545
408546 /**
409547 * Delete a file at the storage path.
410 - * Parameters must match FileBackend::delete(), which include:
 548+ * Parameters similar to FileBackend::delete(), which include:
411549 * source : source storage path
412550 * ignoreMissingSource : don't return an error if the file does not exist
413551 */
414 -class FileDeleteOp extends FileOp {
415 - function doAttempt() {
 552+class DeleteFileOp extends FileOp {
 553+ function doPrecheck() {
416554 $status = Status::newGood();
417 - if ( !$this->params['ignoreMissingSource'] ) {
418 - if ( !$this->backend->fileExists( $this->params['source'] ) ) {
 555+ if ( empty( $this->params['ignoreMissingSource'] ) ) {
 556+ $params = array( 'source' => $this->params['source'] );
 557+ if ( !$this->backend->fileExists( $params ) ) {
419558 $status->fatal( 'backend-fail-notexists', $this->params['source'] );
420559 return $status;
421560 }
@@ -422,6 +561,10 @@
423562 return $status;
424563 }
425564
 565+ function doAttempt() {
 566+ return Status::newGood();
 567+ }
 568+
426569 function doRevert() {
427570 return Status::newGood();
428571 }
Index: branches/FileBackend/phase3/includes/filerepo/backend/FSFileBackend.php
@@ -305,6 +305,22 @@
306306 return $status;
307307 }
308308
 309+ function prepare( array $params ) {
 310+ list( $c, $dir ) = $this->resolveVirtualPath( $params['directory'] );
 311+ if ( $dir === null ) {
 312+ return false; // invalid storage path
 313+ }
 314+ if ( !wfMkdirParents( $dir ) ) {
 315+ $status->fatal( 'directorycreateerror', $param['directory'] );
 316+ return $status;
 317+ }
 318+ if ( !is_writable( $dir ) ) {
 319+ $status->fatal( 'directoryreadonlyerror', $param['directory'] );
 320+ return $status;
 321+ }
 322+ return $status;
 323+ }
 324+
309325 function fileExists( array $params ) {
310326 list( $c, $source ) = $this->resolveVirtualPath( $params['source'] );
311327 if ( $source === null ) {
@@ -313,6 +329,18 @@
314330 return file_exists( $source );
315331 }
316332
 333+ function getHashType() {
 334+ return 'md5';
 335+ }
 336+
 337+ function getFileHash( array $params ) {
 338+ list( $c, $source ) = $this->resolveVirtualPath( $params['source'] );
 339+ if ( $source === null ) {
 340+ return false; // invalid storage path
 341+ }
 342+ return md5_file( $source );
 343+ }
 344+
317345 function getFileProps( array $params ) {
318346 list( $c, $source ) = $this->resolveVirtualPath( $params['source'] );
319347 if ( $source === null ) {
Index: branches/FileBackend/phase3/includes/filerepo/backend/FileLockManager.php
@@ -67,7 +67,9 @@
6868 * Simple version of FileLockManager based on using FS lock files
6969 *
7070 * This should work fine for small sites running off one server.
71 - * Do not use this with 'lockDir' set to an NFS mount.
 71+ * Do not use this with 'lockDir' set to an NFS mount unless the
 72+ * NFS client is at least version 2.6.12. Otherwise, the BSD flock()
 73+ * locks will be ignored; see http://nfs.sourceforge.net/#section_d.
7274 */
7375 class FSFileLockManager extends FileLockManager {
7476 protected $lockDir; // global dir for all servers
Index: branches/FileBackend/phase3/includes/filerepo/backend/FileBackend.php
@@ -88,7 +88,6 @@
8989 * source : source path on disk
9090 * dest : destination storage path
9191 * overwriteDest : do nothing and pass if an identical file exists at destination
92 - * overwriteSame : override any existing file at destination
9392 *
9493 * @param Array $params
9594 * @return Status
@@ -102,7 +101,6 @@
103102 * source : source storage path
104103 * dest : destination storage path
105104 * overwriteDest : do nothing and pass if an identical file exists at destination
106 - * overwriteSame : override any existing file at destination
107105 *
108106 * @param Array $params
109107 * @return Status
@@ -117,7 +115,6 @@
118116 * source : source storage path
119117 * dest : destination storage path
120118 * overwriteDest : do nothing and pass if an identical file exists at destination
121 - * overwriteSame : override any existing file at destination
122119 *
123120 * @param Array $params
124121 * @return Status
@@ -129,7 +126,6 @@
130127 * Do not call this function from places outside FileBackend and FileOp.
131128 * $params include:
132129 * source : source storage path
133 - * ignoreMissingSource : don't return an error if the file does not exist
134130 *
135131 * @param Array $params
136132 * @return Status
@@ -157,7 +153,6 @@
158154 * contents : the raw file contents
159155 * dest : destination storage path
160156 * overwriteDest : do nothing and pass if an identical file exists at destination
161 - * overwriteSame : override any existing file at destination
162157 *
163158 * @param Array $params
164159 * @return Status
@@ -190,6 +185,26 @@
191186 abstract public function fileExists( array $params );
192187
193188 /**
 189+ * Get the format of the hash that getFileHash() uses
 190+ *
 191+ * @return string (md5, sha1, unknown, ...)
 192+ */
 193+ public function getHashType() {
 194+ return 'unknown';
 195+ }
 196+
 197+ /**
 198+ * Get a hash of the file that exists at a storage path in the backend.
 199+ * Typically this will be a SHA-1 hash, MD5 hash, or something similar.
 200+ * $params include:
 201+ * source : source storage path
 202+ *
 203+ * @param Array $params
 204+ * @return string|null Gives null if the file does not exist
 205+ */
 206+ abstract public function getFileHash( array $params );
 207+
 208+ /**
194209 * Get the properties of the file that exists at a storage path in the backend
195210 * $params include:
196211 * source : source storage path
@@ -278,12 +293,12 @@
279294 */
280295 protected function supportedOperations() {
281296 return array(
282 - 'store' => 'FileStoreOp',
283 - 'copy' => 'FileCopyOp',
284 - 'move' => 'FileMoveOp',
285 - 'delete' => 'FileDeleteOp',
286 - 'concatenate' => 'FileConcatenateOp',
287 - 'create' => 'FileCreateOp'
 297+ 'store' => 'StoreFileOp',
 298+ 'copy' => 'CopyFileOp',
 299+ 'move' => 'MoveFileOp',
 300+ 'delete' => 'DeleteFileOp',
 301+ 'concatenate' => 'ConcatenateFileOp',
 302+ 'create' => 'CreateFileOp'
288303 );
289304 }
290305
@@ -336,6 +351,15 @@
337352 return $status; // abort
338353 }
339354
 355+ // Do pre-checks for each operation; abort on failure...
 356+ foreach ( $performOps as $index => $fileOp ) {
 357+ $status->merge( $fileOp->precheck() );
 358+ if ( !$status->isOK() ) { // operation failed?
 359+ $status->merge( $this->unlockFiles( $filesToLock ) );
 360+ return $status;
 361+ }
 362+ }
 363+
340364 // Attempt each operation; abort on failure...
341365 foreach ( $performOps as $index => $fileOp ) {
342366 $status->merge( $fileOp->attempt() );
@@ -347,6 +371,7 @@
348372 $status->merge( $performOps[$pos]->revert() );
349373 $pos--;
350374 }
 375+ $status->merge( $this->unlockFiles( $filesToLock ) );
351376 return $status;
352377 }
353378 }
@@ -366,6 +391,16 @@
367392 }
368393
369394 /**
 395+ * Check if a given path is a mwstore:// path
 396+ *
 397+ * @param $path string
 398+ * @return bool
 399+ */
 400+ final public static function isStoragePath( $path ) {
 401+ return ( strpos( $path, 'mwstore://' ) === 0 );
 402+ }
 403+
 404+ /**
370405 * Split a storage path (e.g. "mwstore://backend/container/path/to/object")
371406 * into a container name and a full object name within that container.
372407 *

Follow-up revisions

RevisionCommit summaryAuthorDate
r104197* Moved several functions from FileBackendBase to FileBackend class. This sim...aaron21:20, 24 November 2011

Status & tagging log