MediaWiki  1.23.0
FileBackendStore.php
Go to the documentation of this file.
1 <?php
38 abstract class FileBackendStore extends FileBackend {
40  protected $memCache;
42  protected $cheapCache;
44  protected $expensiveCache;
45 
47  protected $shardViaHashLevels = array();
48 
50  protected $mimeCallback;
51 
52  protected $maxFileSize = 4294967296; // integer bytes (4GiB)
53 
54  const CACHE_TTL = 10; // integer; TTL in seconds for process cache entries
55  const CACHE_CHEAP_SIZE = 500; // integer; max entries in "cheap cache"
56  const CACHE_EXPENSIVE_SIZE = 5; // integer; max entries in "expensive cache"
57 
67  public function __construct( array $config ) {
68  parent::__construct( $config );
69  $this->mimeCallback = isset( $config['mimeCallback'] )
70  ? $config['mimeCallback']
71  : function ( $storagePath, $content, $fsPath ) {
72  // @TODO: handle the case of extension-less files using the contents
73  return StreamFile::contentTypeFromPath( $storagePath ) ?: 'unknown/unknown';
74  };
75  $this->memCache = new EmptyBagOStuff(); // disabled by default
76  $this->cheapCache = new ProcessCacheLRU( self::CACHE_CHEAP_SIZE );
77  $this->expensiveCache = new ProcessCacheLRU( self::CACHE_EXPENSIVE_SIZE );
78  }
79 
87  final public function maxFileSizeInternal() {
88  return $this->maxFileSize;
89  }
90 
100  abstract public function isPathUsableInternal( $storagePath );
101 
120  final public function createInternal( array $params ) {
121  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
122  if ( strlen( $params['content'] ) > $this->maxFileSizeInternal() ) {
123  $status = Status::newFatal( 'backend-fail-maxsize',
124  $params['dst'], $this->maxFileSizeInternal() );
125  } else {
126  $status = $this->doCreateInternal( $params );
127  $this->clearCache( array( $params['dst'] ) );
128  if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
129  $this->deleteFileCache( $params['dst'] ); // persistent cache
130  }
131  }
132 
133  return $status;
134  }
135 
141  abstract protected function doCreateInternal( array $params );
142 
161  final public function storeInternal( array $params ) {
162  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
163  if ( filesize( $params['src'] ) > $this->maxFileSizeInternal() ) {
164  $status = Status::newFatal( 'backend-fail-maxsize',
165  $params['dst'], $this->maxFileSizeInternal() );
166  } else {
167  $status = $this->doStoreInternal( $params );
168  $this->clearCache( array( $params['dst'] ) );
169  if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
170  $this->deleteFileCache( $params['dst'] ); // persistent cache
171  }
172  }
173 
174  return $status;
175  }
176 
182  abstract protected function doStoreInternal( array $params );
183 
203  final public function copyInternal( array $params ) {
204  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
205  $status = $this->doCopyInternal( $params );
206  $this->clearCache( array( $params['dst'] ) );
207  if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
208  $this->deleteFileCache( $params['dst'] ); // persistent cache
209  }
210 
211  return $status;
212  }
213 
219  abstract protected function doCopyInternal( array $params );
220 
235  final public function deleteInternal( array $params ) {
236  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
237  $status = $this->doDeleteInternal( $params );
238  $this->clearCache( array( $params['src'] ) );
239  $this->deleteFileCache( $params['src'] ); // persistent cache
240  return $status;
241  }
242 
248  abstract protected function doDeleteInternal( array $params );
249 
269  final public function moveInternal( array $params ) {
270  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
271  $status = $this->doMoveInternal( $params );
272  $this->clearCache( array( $params['src'], $params['dst'] ) );
273  $this->deleteFileCache( $params['src'] ); // persistent cache
274  if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
275  $this->deleteFileCache( $params['dst'] ); // persistent cache
276  }
277 
278  return $status;
279  }
280 
286  protected function doMoveInternal( array $params ) {
287  unset( $params['async'] ); // two steps, won't work here :)
288  $nsrc = FileBackend::normalizeStoragePath( $params['src'] );
289  $ndst = FileBackend::normalizeStoragePath( $params['dst'] );
290  // Copy source to dest
291  $status = $this->copyInternal( $params );
292  if ( $nsrc !== $ndst && $status->isOK() ) {
293  // Delete source (only fails due to races or network problems)
294  $status->merge( $this->deleteInternal( array( 'src' => $params['src'] ) ) );
295  $status->setResult( true, $status->value ); // ignore delete() errors
296  }
297 
298  return $status;
299  }
300 
315  final public function describeInternal( array $params ) {
316  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
317  if ( count( $params['headers'] ) ) {
318  $status = $this->doDescribeInternal( $params );
319  $this->clearCache( array( $params['src'] ) );
320  $this->deleteFileCache( $params['src'] ); // persistent cache
321  } else {
322  $status = Status::newGood(); // nothing to do
323  }
324 
325  return $status;
326  }
327 
333  protected function doDescribeInternal( array $params ) {
334  return Status::newGood();
335  }
336 
344  final public function nullInternal( array $params ) {
345  return Status::newGood();
346  }
347 
348  final public function concatenate( array $params ) {
349  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
350  $status = Status::newGood();
351 
352  // Try to lock the source files for the scope of this function
353  $scopeLockS = $this->getScopedFileLocks( $params['srcs'], LockManager::LOCK_UW, $status );
354  if ( $status->isOK() ) {
355  // Actually do the file concatenation...
356  $start_time = microtime( true );
357  $status->merge( $this->doConcatenate( $params ) );
358  $sec = microtime( true ) - $start_time;
359  if ( !$status->isOK() ) {
360  wfDebugLog( 'FileOperation', get_class( $this ) . " failed to concatenate " .
361  count( $params['srcs'] ) . " file(s) [$sec sec]" );
362  }
363  }
364 
365  return $status;
366  }
367 
373  protected function doConcatenate( array $params ) {
374  $status = Status::newGood();
375  $tmpPath = $params['dst']; // convenience
376  unset( $params['latest'] ); // sanity
377 
378  // Check that the specified temp file is valid...
380  $ok = ( is_file( $tmpPath ) && filesize( $tmpPath ) == 0 );
382  if ( !$ok ) { // not present or not empty
383  $status->fatal( 'backend-fail-opentemp', $tmpPath );
384 
385  return $status;
386  }
387 
388  // Get local FS versions of the chunks needed for the concatenation...
389  $fsFiles = $this->getLocalReferenceMulti( $params );
390  foreach ( $fsFiles as $path => &$fsFile ) {
391  if ( !$fsFile ) { // chunk failed to download?
392  $fsFile = $this->getLocalReference( array( 'src' => $path ) );
393  if ( !$fsFile ) { // retry failed?
394  $status->fatal( 'backend-fail-read', $path );
395 
396  return $status;
397  }
398  }
399  }
400  unset( $fsFile ); // unset reference so we can reuse $fsFile
401 
402  // Get a handle for the destination temp file
403  $tmpHandle = fopen( $tmpPath, 'ab' );
404  if ( $tmpHandle === false ) {
405  $status->fatal( 'backend-fail-opentemp', $tmpPath );
406 
407  return $status;
408  }
409 
410  // Build up the temp file using the source chunks (in order)...
411  foreach ( $fsFiles as $virtualSource => $fsFile ) {
412  // Get a handle to the local FS version
413  $sourceHandle = fopen( $fsFile->getPath(), 'rb' );
414  if ( $sourceHandle === false ) {
415  fclose( $tmpHandle );
416  $status->fatal( 'backend-fail-read', $virtualSource );
417 
418  return $status;
419  }
420  // Append chunk to file (pass chunk size to avoid magic quotes)
421  if ( !stream_copy_to_stream( $sourceHandle, $tmpHandle ) ) {
422  fclose( $sourceHandle );
423  fclose( $tmpHandle );
424  $status->fatal( 'backend-fail-writetemp', $tmpPath );
425 
426  return $status;
427  }
428  fclose( $sourceHandle );
429  }
430  if ( !fclose( $tmpHandle ) ) {
431  $status->fatal( 'backend-fail-closetemp', $tmpPath );
432 
433  return $status;
434  }
435 
436  clearstatcache(); // temp file changed
437 
438  return $status;
439  }
440 
441  final protected function doPrepare( array $params ) {
442  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
443  $status = Status::newGood();
444 
445  list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
446  if ( $dir === null ) {
447  $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
448 
449  return $status; // invalid storage path
450  }
451 
452  if ( $shard !== null ) { // confined to a single container/shard
453  $status->merge( $this->doPrepareInternal( $fullCont, $dir, $params ) );
454  } else { // directory is on several shards
455  wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
456  list( , $shortCont, ) = self::splitStoragePath( $params['dir'] );
457  foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
458  $status->merge( $this->doPrepareInternal( "{$fullCont}{$suffix}", $dir, $params ) );
459  }
460  }
461 
462  return $status;
463  }
464 
472  protected function doPrepareInternal( $container, $dir, array $params ) {
473  return Status::newGood();
474  }
475 
476  final protected function doSecure( array $params ) {
477  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
478  $status = Status::newGood();
479 
480  list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
481  if ( $dir === null ) {
482  $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
483 
484  return $status; // invalid storage path
485  }
486 
487  if ( $shard !== null ) { // confined to a single container/shard
488  $status->merge( $this->doSecureInternal( $fullCont, $dir, $params ) );
489  } else { // directory is on several shards
490  wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
491  list( , $shortCont, ) = self::splitStoragePath( $params['dir'] );
492  foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
493  $status->merge( $this->doSecureInternal( "{$fullCont}{$suffix}", $dir, $params ) );
494  }
495  }
496 
497  return $status;
498  }
499 
507  protected function doSecureInternal( $container, $dir, array $params ) {
508  return Status::newGood();
509  }
510 
511  final protected function doPublish( array $params ) {
512  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
513  $status = Status::newGood();
514 
515  list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
516  if ( $dir === null ) {
517  $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
518 
519  return $status; // invalid storage path
520  }
521 
522  if ( $shard !== null ) { // confined to a single container/shard
523  $status->merge( $this->doPublishInternal( $fullCont, $dir, $params ) );
524  } else { // directory is on several shards
525  wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
526  list( , $shortCont, ) = self::splitStoragePath( $params['dir'] );
527  foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
528  $status->merge( $this->doPublishInternal( "{$fullCont}{$suffix}", $dir, $params ) );
529  }
530  }
531 
532  return $status;
533  }
534 
542  protected function doPublishInternal( $container, $dir, array $params ) {
543  return Status::newGood();
544  }
545 
546  final protected function doClean( array $params ) {
547  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
548  $status = Status::newGood();
549 
550  // Recursive: first delete all empty subdirs recursively
551  if ( !empty( $params['recursive'] ) && !$this->directoriesAreVirtual() ) {
552  $subDirsRel = $this->getTopDirectoryList( array( 'dir' => $params['dir'] ) );
553  if ( $subDirsRel !== null ) { // no errors
554  foreach ( $subDirsRel as $subDirRel ) {
555  $subDir = $params['dir'] . "/{$subDirRel}"; // full path
556  $status->merge( $this->doClean( array( 'dir' => $subDir ) + $params ) );
557  }
558  unset( $subDirsRel ); // free directory for rmdir() on Windows (for FS backends)
559  }
560  }
561 
562  list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
563  if ( $dir === null ) {
564  $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
565 
566  return $status; // invalid storage path
567  }
568 
569  // Attempt to lock this directory...
570  $filesLockEx = array( $params['dir'] );
571  $scopedLockE = $this->getScopedFileLocks( $filesLockEx, LockManager::LOCK_EX, $status );
572  if ( !$status->isOK() ) {
573  return $status; // abort
574  }
575 
576  if ( $shard !== null ) { // confined to a single container/shard
577  $status->merge( $this->doCleanInternal( $fullCont, $dir, $params ) );
578  $this->deleteContainerCache( $fullCont ); // purge cache
579  } else { // directory is on several shards
580  wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
581  list( , $shortCont, ) = self::splitStoragePath( $params['dir'] );
582  foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
583  $status->merge( $this->doCleanInternal( "{$fullCont}{$suffix}", $dir, $params ) );
584  $this->deleteContainerCache( "{$fullCont}{$suffix}" ); // purge cache
585  }
586  }
587 
588  return $status;
589  }
590 
598  protected function doCleanInternal( $container, $dir, array $params ) {
599  return Status::newGood();
600  }
601 
602  final public function fileExists( array $params ) {
603  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
604  $stat = $this->getFileStat( $params );
605 
606  return ( $stat === null ) ? null : (bool)$stat; // null => failure
607  }
608 
609  final public function getFileTimestamp( array $params ) {
610  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
611  $stat = $this->getFileStat( $params );
612 
613  return $stat ? $stat['mtime'] : false;
614  }
615 
616  final public function getFileSize( array $params ) {
617  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
618  $stat = $this->getFileStat( $params );
619 
620  return $stat ? $stat['size'] : false;
621  }
622 
623  final public function getFileStat( array $params ) {
625  if ( $path === null ) {
626  return false; // invalid storage path
627  }
628  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
629  $latest = !empty( $params['latest'] ); // use latest data?
630  if ( !$this->cheapCache->has( $path, 'stat', self::CACHE_TTL ) ) {
631  $this->primeFileCache( array( $path ) ); // check persistent cache
632  }
633  if ( $this->cheapCache->has( $path, 'stat', self::CACHE_TTL ) ) {
634  $stat = $this->cheapCache->get( $path, 'stat' );
635  // If we want the latest data, check that this cached
636  // value was in fact fetched with the latest available data.
637  if ( is_array( $stat ) ) {
638  if ( !$latest || $stat['latest'] ) {
639  return $stat;
640  }
641  } elseif ( in_array( $stat, array( 'NOT_EXIST', 'NOT_EXIST_LATEST' ) ) ) {
642  if ( !$latest || $stat === 'NOT_EXIST_LATEST' ) {
643  return false;
644  }
645  }
646  }
647  wfProfileIn( __METHOD__ . '-miss-' . $this->name );
648  $stat = $this->doGetFileStat( $params );
649  wfProfileOut( __METHOD__ . '-miss-' . $this->name );
650  if ( is_array( $stat ) ) { // file exists
651  // Strongly consistent backends can automatically set "latest"
652  $stat['latest'] = isset( $stat['latest'] ) ? $stat['latest'] : $latest;
653  $this->cheapCache->set( $path, 'stat', $stat );
654  $this->setFileCache( $path, $stat ); // update persistent cache
655  if ( isset( $stat['sha1'] ) ) { // some backends store SHA-1 as metadata
656  $this->cheapCache->set( $path, 'sha1',
657  array( 'hash' => $stat['sha1'], 'latest' => $latest ) );
658  }
659  if ( isset( $stat['xattr'] ) ) { // some backends store headers/metadata
660  $stat['xattr'] = self::normalizeXAttributes( $stat['xattr'] );
661  $this->cheapCache->set( $path, 'xattr',
662  array( 'map' => $stat['xattr'], 'latest' => $latest ) );
663  }
664  } elseif ( $stat === false ) { // file does not exist
665  $this->cheapCache->set( $path, 'stat', $latest ? 'NOT_EXIST_LATEST' : 'NOT_EXIST' );
666  $this->cheapCache->set( $path, 'xattr', array( 'map' => false, 'latest' => $latest ) );
667  $this->cheapCache->set( $path, 'sha1', array( 'hash' => false, 'latest' => $latest ) );
668  wfDebug( __METHOD__ . ": File $path does not exist.\n" );
669  } else { // an error occurred
670  wfDebug( __METHOD__ . ": Could not stat file $path.\n" );
671  }
672 
673  return $stat;
674  }
675 
679  abstract protected function doGetFileStat( array $params );
680 
681  public function getFileContentsMulti( array $params ) {
682  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
683 
684  $params = $this->setConcurrencyFlags( $params );
685  $contents = $this->doGetFileContentsMulti( $params );
686 
687  return $contents;
688  }
689 
695  protected function doGetFileContentsMulti( array $params ) {
696  $contents = array();
697  foreach ( $this->doGetLocalReferenceMulti( $params ) as $path => $fsFile ) {
699  $contents[$path] = $fsFile ? file_get_contents( $fsFile->getPath() ) : false;
701  }
702 
703  return $contents;
704  }
705 
706  final public function getFileXAttributes( array $params ) {
708  if ( $path === null ) {
709  return false; // invalid storage path
710  }
711  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
712  $latest = !empty( $params['latest'] ); // use latest data?
713  if ( $this->cheapCache->has( $path, 'xattr', self::CACHE_TTL ) ) {
714  $stat = $this->cheapCache->get( $path, 'xattr' );
715  // If we want the latest data, check that this cached
716  // value was in fact fetched with the latest available data.
717  if ( !$latest || $stat['latest'] ) {
718  return $stat['map'];
719  }
720  }
721  wfProfileIn( __METHOD__ . '-miss' );
722  wfProfileIn( __METHOD__ . '-miss-' . $this->name );
723  $fields = $this->doGetFileXAttributes( $params );
724  $fields = is_array( $fields ) ? self::normalizeXAttributes( $fields ) : false;
725  wfProfileOut( __METHOD__ . '-miss-' . $this->name );
726  wfProfileOut( __METHOD__ . '-miss' );
727  $this->cheapCache->set( $path, 'xattr', array( 'map' => $fields, 'latest' => $latest ) );
728 
729  return $fields;
730  }
731 
736  protected function doGetFileXAttributes( array $params ) {
737  return array( 'headers' => array(), 'metadata' => array() ); // not supported
738  }
739 
740  final public function getFileSha1Base36( array $params ) {
742  if ( $path === null ) {
743  return false; // invalid storage path
744  }
745  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
746  $latest = !empty( $params['latest'] ); // use latest data?
747  if ( $this->cheapCache->has( $path, 'sha1', self::CACHE_TTL ) ) {
748  $stat = $this->cheapCache->get( $path, 'sha1' );
749  // If we want the latest data, check that this cached
750  // value was in fact fetched with the latest available data.
751  if ( !$latest || $stat['latest'] ) {
752  return $stat['hash'];
753  }
754  }
755  wfProfileIn( __METHOD__ . '-miss-' . $this->name );
756  $hash = $this->doGetFileSha1Base36( $params );
757  wfProfileOut( __METHOD__ . '-miss-' . $this->name );
758  $this->cheapCache->set( $path, 'sha1', array( 'hash' => $hash, 'latest' => $latest ) );
759 
760  return $hash;
761  }
762 
768  protected function doGetFileSha1Base36( array $params ) {
769  $fsFile = $this->getLocalReference( $params );
770  if ( !$fsFile ) {
771  return false;
772  } else {
773  return $fsFile->getSha1Base36();
774  }
775  }
776 
777  final public function getFileProps( array $params ) {
778  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
779  $fsFile = $this->getLocalReference( $params );
780  $props = $fsFile ? $fsFile->getProps() : FSFile::placeholderProps();
781 
782  return $props;
783  }
784 
785  final public function getLocalReferenceMulti( array $params ) {
786  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
787 
788  $params = $this->setConcurrencyFlags( $params );
789 
790  $fsFiles = array(); // (path => FSFile)
791  $latest = !empty( $params['latest'] ); // use latest data?
792  // Reuse any files already in process cache...
793  foreach ( $params['srcs'] as $src ) {
795  if ( $path === null ) {
796  $fsFiles[$src] = null; // invalid storage path
797  } elseif ( $this->expensiveCache->has( $path, 'localRef' ) ) {
798  $val = $this->expensiveCache->get( $path, 'localRef' );
799  // If we want the latest data, check that this cached
800  // value was in fact fetched with the latest available data.
801  if ( !$latest || $val['latest'] ) {
802  $fsFiles[$src] = $val['object'];
803  }
804  }
805  }
806  // Fetch local references of any remaning files...
807  $params['srcs'] = array_diff( $params['srcs'], array_keys( $fsFiles ) );
808  foreach ( $this->doGetLocalReferenceMulti( $params ) as $path => $fsFile ) {
809  $fsFiles[$path] = $fsFile;
810  if ( $fsFile ) { // update the process cache...
811  $this->expensiveCache->set( $path, 'localRef',
812  array( 'object' => $fsFile, 'latest' => $latest ) );
813  }
814  }
815 
816  return $fsFiles;
817  }
818 
824  protected function doGetLocalReferenceMulti( array $params ) {
825  return $this->doGetLocalCopyMulti( $params );
826  }
827 
828  final public function getLocalCopyMulti( array $params ) {
829  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
830 
831  $params = $this->setConcurrencyFlags( $params );
832  $tmpFiles = $this->doGetLocalCopyMulti( $params );
833 
834  return $tmpFiles;
835  }
836 
842  abstract protected function doGetLocalCopyMulti( array $params );
843 
849  public function getFileHttpUrl( array $params ) {
850  return null; // not supported
851  }
852 
853  final public function streamFile( array $params ) {
854  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
855  $status = Status::newGood();
856 
857  $info = $this->getFileStat( $params );
858  if ( !$info ) { // let StreamFile handle the 404
859  $status->fatal( 'backend-fail-notexists', $params['src'] );
860  }
861 
862  // Set output buffer and HTTP headers for stream
863  $extraHeaders = isset( $params['headers'] ) ? $params['headers'] : array();
864  $res = StreamFile::prepareForStream( $params['src'], $info, $extraHeaders );
865  if ( $res == StreamFile::NOT_MODIFIED ) {
866  // do nothing; client cache is up to date
867  } elseif ( $res == StreamFile::READY_STREAM ) {
868  wfProfileIn( __METHOD__ . '-send-' . $this->name );
869  $status = $this->doStreamFile( $params );
870  wfProfileOut( __METHOD__ . '-send-' . $this->name );
871  if ( !$status->isOK() ) {
872  // Per bug 41113, nasty things can happen if bad cache entries get
873  // stuck in cache. It's also possible that this error can come up
874  // with simple race conditions. Clear out the stat cache to be safe.
875  $this->clearCache( array( $params['src'] ) );
876  $this->deleteFileCache( $params['src'] );
877  trigger_error( "Bad stat cache or race condition for file {$params['src']}." );
878  }
879  } else {
880  $status->fatal( 'backend-fail-stream', $params['src'] );
881  }
882 
883  return $status;
884  }
885 
891  protected function doStreamFile( array $params ) {
892  $status = Status::newGood();
893 
894  $fsFile = $this->getLocalReference( $params );
895  if ( !$fsFile ) {
896  $status->fatal( 'backend-fail-stream', $params['src'] );
897  } elseif ( !readfile( $fsFile->getPath() ) ) {
898  $status->fatal( 'backend-fail-stream', $params['src'] );
899  }
900 
901  return $status;
902  }
903 
904  final public function directoryExists( array $params ) {
905  list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
906  if ( $dir === null ) {
907  return false; // invalid storage path
908  }
909  if ( $shard !== null ) { // confined to a single container/shard
910  return $this->doDirectoryExists( $fullCont, $dir, $params );
911  } else { // directory is on several shards
912  wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
913  list( , $shortCont, ) = self::splitStoragePath( $params['dir'] );
914  $res = false; // response
915  foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
916  $exists = $this->doDirectoryExists( "{$fullCont}{$suffix}", $dir, $params );
917  if ( $exists ) {
918  $res = true;
919  break; // found one!
920  } elseif ( $exists === null ) { // error?
921  $res = null; // if we don't find anything, it is indeterminate
922  }
923  }
924 
925  return $res;
926  }
927  }
928 
937  abstract protected function doDirectoryExists( $container, $dir, array $params );
938 
939  final public function getDirectoryList( array $params ) {
940  list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
941  if ( $dir === null ) { // invalid storage path
942  return null;
943  }
944  if ( $shard !== null ) {
945  // File listing is confined to a single container/shard
946  return $this->getDirectoryListInternal( $fullCont, $dir, $params );
947  } else {
948  wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
949  // File listing spans multiple containers/shards
950  list( , $shortCont, ) = self::splitStoragePath( $params['dir'] );
951 
952  return new FileBackendStoreShardDirIterator( $this,
953  $fullCont, $dir, $this->getContainerSuffixes( $shortCont ), $params );
954  }
955  }
956 
967  abstract public function getDirectoryListInternal( $container, $dir, array $params );
968 
969  final public function getFileList( array $params ) {
970  list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
971  if ( $dir === null ) { // invalid storage path
972  return null;
973  }
974  if ( $shard !== null ) {
975  // File listing is confined to a single container/shard
976  return $this->getFileListInternal( $fullCont, $dir, $params );
977  } else {
978  wfDebug( __METHOD__ . ": iterating over all container shards.\n" );
979  // File listing spans multiple containers/shards
980  list( , $shortCont, ) = self::splitStoragePath( $params['dir'] );
981 
982  return new FileBackendStoreShardFileIterator( $this,
983  $fullCont, $dir, $this->getContainerSuffixes( $shortCont ), $params );
984  }
985  }
986 
997  abstract public function getFileListInternal( $container, $dir, array $params );
998 
1010  final public function getOperationsInternal( array $ops ) {
1011  $supportedOps = array(
1012  'store' => 'StoreFileOp',
1013  'copy' => 'CopyFileOp',
1014  'move' => 'MoveFileOp',
1015  'delete' => 'DeleteFileOp',
1016  'create' => 'CreateFileOp',
1017  'describe' => 'DescribeFileOp',
1018  'null' => 'NullFileOp'
1019  );
1020 
1021  $performOps = array(); // array of FileOp objects
1022  // Build up ordered array of FileOps...
1023  foreach ( $ops as $operation ) {
1024  $opName = $operation['op'];
1025  if ( isset( $supportedOps[$opName] ) ) {
1026  $class = $supportedOps[$opName];
1027  // Get params for this operation
1028  $params = $operation;
1029  // Append the FileOp class
1030  $performOps[] = new $class( $this, $params );
1031  } else {
1032  throw new FileBackendError( "Operation '$opName' is not supported." );
1033  }
1034  }
1035 
1036  return $performOps;
1037  }
1038 
1049  final public function getPathsToLockForOpsInternal( array $performOps ) {
1050  // Build up a list of files to lock...
1051  $paths = array( 'sh' => array(), 'ex' => array() );
1052  foreach ( $performOps as $fileOp ) {
1053  $paths['sh'] = array_merge( $paths['sh'], $fileOp->storagePathsRead() );
1054  $paths['ex'] = array_merge( $paths['ex'], $fileOp->storagePathsChanged() );
1055  }
1056  // Optimization: if doing an EX lock anyway, don't also set an SH one
1057  $paths['sh'] = array_diff( $paths['sh'], $paths['ex'] );
1058  // Get a shared lock on the parent directory of each path changed
1059  $paths['sh'] = array_merge( $paths['sh'], array_map( 'dirname', $paths['ex'] ) );
1060 
1061  return array(
1062  LockManager::LOCK_UW => $paths['sh'],
1063  LockManager::LOCK_EX => $paths['ex']
1064  );
1065  }
1066 
1067  public function getScopedLocksForOps( array $ops, Status $status ) {
1068  $paths = $this->getPathsToLockForOpsInternal( $this->getOperationsInternal( $ops ) );
1069 
1070  return array( $this->getScopedFileLocks( $paths, 'mixed', $status ) );
1071  }
1072 
1073  final protected function doOperationsInternal( array $ops, array $opts ) {
1074  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
1075  $status = Status::newGood();
1076 
1077  // Fix up custom header name/value pairs...
1078  $ops = array_map( array( $this, 'stripInvalidHeadersFromOp' ), $ops );
1079 
1080  // Build up a list of FileOps...
1081  $performOps = $this->getOperationsInternal( $ops );
1082 
1083  // Acquire any locks as needed...
1084  if ( empty( $opts['nonLocking'] ) ) {
1085  // Build up a list of files to lock...
1086  $paths = $this->getPathsToLockForOpsInternal( $performOps );
1087  // Try to lock those files for the scope of this function...
1088  $scopeLock = $this->getScopedFileLocks( $paths, 'mixed', $status );
1089  if ( !$status->isOK() ) {
1090  return $status; // abort
1091  }
1092  }
1093 
1094  // Clear any file cache entries (after locks acquired)
1095  if ( empty( $opts['preserveCache'] ) ) {
1096  $this->clearCache();
1097  }
1098 
1099  // Build the list of paths involved
1100  $paths = array();
1101  foreach ( $performOps as $op ) {
1102  $paths = array_merge( $paths, $op->storagePathsRead() );
1103  $paths = array_merge( $paths, $op->storagePathsChanged() );
1104  }
1105  // Load from the persistent container caches
1106  $this->primeContainerCache( $paths );
1107  // Get the latest stat info for all the files (having locked them)
1108  $this->preloadFileStat( array( 'srcs' => $paths, 'latest' => true ) );
1109 
1110  // Actually attempt the operation batch...
1111  $opts = $this->setConcurrencyFlags( $opts );
1112  $subStatus = FileOpBatch::attempt( $performOps, $opts, $this->fileJournal );
1113 
1114  // Merge errors into status fields
1115  $status->merge( $subStatus );
1116  $status->success = $subStatus->success; // not done in merge()
1117 
1118  return $status;
1119  }
1120 
1121  final protected function doQuickOperationsInternal( array $ops ) {
1122  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
1123  $status = Status::newGood();
1124 
1125  // Fix up custom header name/value pairs...
1126  $ops = array_map( array( $this, 'stripInvalidHeadersFromOp' ), $ops );
1127 
1128  // Clear any file cache entries
1129  $this->clearCache();
1130 
1131  $supportedOps = array( 'create', 'store', 'copy', 'move', 'delete', 'describe', 'null' );
1132  $async = ( $this->parallelize === 'implicit' && count( $ops ) > 1 );
1133  $maxConcurrency = $this->concurrency; // throttle
1134 
1135  $statuses = array(); // array of (index => Status)
1136  $fileOpHandles = array(); // list of (index => handle) arrays
1137  $curFileOpHandles = array(); // current handle batch
1138  // Perform the sync-only ops and build up op handles for the async ops...
1139  foreach ( $ops as $index => $params ) {
1140  if ( !in_array( $params['op'], $supportedOps ) ) {
1141  throw new FileBackendError( "Operation '{$params['op']}' is not supported." );
1142  }
1143  $method = $params['op'] . 'Internal'; // e.g. "storeInternal"
1144  $subStatus = $this->$method( array( 'async' => $async ) + $params );
1145  if ( $subStatus->value instanceof FileBackendStoreOpHandle ) { // async
1146  if ( count( $curFileOpHandles ) >= $maxConcurrency ) {
1147  $fileOpHandles[] = $curFileOpHandles; // push this batch
1148  $curFileOpHandles = array();
1149  }
1150  $curFileOpHandles[$index] = $subStatus->value; // keep index
1151  } else { // error or completed
1152  $statuses[$index] = $subStatus; // keep index
1153  }
1154  }
1155  if ( count( $curFileOpHandles ) ) {
1156  $fileOpHandles[] = $curFileOpHandles; // last batch
1157  }
1158  // Do all the async ops that can be done concurrently...
1159  foreach ( $fileOpHandles as $fileHandleBatch ) {
1160  $statuses = $statuses + $this->executeOpHandlesInternal( $fileHandleBatch );
1161  }
1162  // Marshall and merge all the responses...
1163  foreach ( $statuses as $index => $subStatus ) {
1164  $status->merge( $subStatus );
1165  if ( $subStatus->isOK() ) {
1166  $status->success[$index] = true;
1167  ++$status->successCount;
1168  } else {
1169  $status->success[$index] = false;
1170  ++$status->failCount;
1171  }
1172  }
1173 
1174  return $status;
1175  }
1176 
1187  final public function executeOpHandlesInternal( array $fileOpHandles ) {
1188  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
1189 
1190  foreach ( $fileOpHandles as $fileOpHandle ) {
1191  if ( !( $fileOpHandle instanceof FileBackendStoreOpHandle ) ) {
1192  throw new FileBackendError( "Given a non-FileBackendStoreOpHandle object." );
1193  } elseif ( $fileOpHandle->backend->getName() !== $this->getName() ) {
1194  throw new FileBackendError( "Given a FileBackendStoreOpHandle for the wrong backend." );
1195  }
1196  }
1197  $res = $this->doExecuteOpHandlesInternal( $fileOpHandles );
1198  foreach ( $fileOpHandles as $fileOpHandle ) {
1199  $fileOpHandle->closeResources();
1200  }
1201 
1202  return $res;
1203  }
1204 
1211  protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
1212  if ( count( $fileOpHandles ) ) {
1213  throw new FileBackendError( "This backend supports no asynchronous operations." );
1214  }
1215 
1216  return array();
1217  }
1218 
1228  protected function stripInvalidHeadersFromOp( array $op ) {
1229  static $longs = array( 'Content-Disposition' );
1230  if ( isset( $op['headers'] ) ) { // op sets HTTP headers
1231  foreach ( $op['headers'] as $name => $value ) {
1232  $maxHVLen = in_array( $name, $longs ) ? INF : 255;
1233  if ( strlen( $name ) > 255 || strlen( $value ) > $maxHVLen ) {
1234  trigger_error( "Header '$name: $value' is too long." );
1235  unset( $op['headers'][$name] );
1236  } elseif ( !strlen( $value ) ) {
1237  $op['headers'][$name] = ''; // null/false => ""
1238  }
1239  }
1240  }
1241 
1242  return $op;
1243  }
1244 
1245  final public function preloadCache( array $paths ) {
1246  $fullConts = array(); // full container names
1247  foreach ( $paths as $path ) {
1248  list( $fullCont, , ) = $this->resolveStoragePath( $path );
1249  $fullConts[] = $fullCont;
1250  }
1251  // Load from the persistent file and container caches
1252  $this->primeContainerCache( $fullConts );
1253  $this->primeFileCache( $paths );
1254  }
1255 
1256  final public function clearCache( array $paths = null ) {
1257  if ( is_array( $paths ) ) {
1258  $paths = array_map( 'FileBackend::normalizeStoragePath', $paths );
1259  $paths = array_filter( $paths, 'strlen' ); // remove nulls
1260  }
1261  if ( $paths === null ) {
1262  $this->cheapCache->clear();
1263  $this->expensiveCache->clear();
1264  } else {
1265  foreach ( $paths as $path ) {
1266  $this->cheapCache->clear( $path );
1267  $this->expensiveCache->clear( $path );
1268  }
1269  }
1270  $this->doClearCache( $paths );
1271  }
1272 
1280  protected function doClearCache( array $paths = null ) {
1281  }
1282 
1283  final public function preloadFileStat( array $params ) {
1284  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
1285 
1286  $params['concurrency'] = ( $this->parallelize !== 'off' ) ? $this->concurrency : 1;
1287  $stats = $this->doGetFileStatMulti( $params );
1288  if ( $stats === null ) {
1289  return; // not supported
1290  }
1291 
1292  $latest = !empty( $params['latest'] ); // use latest data?
1293  foreach ( $stats as $path => $stat ) {
1295  if ( $path === null ) {
1296  continue; // this shouldn't happen
1297  }
1298  if ( is_array( $stat ) ) { // file exists
1299  // Strongly consistent backends can automatically set "latest"
1300  $stat['latest'] = isset( $stat['latest'] ) ? $stat['latest'] : $latest;
1301  $this->cheapCache->set( $path, 'stat', $stat );
1302  $this->setFileCache( $path, $stat ); // update persistent cache
1303  if ( isset( $stat['sha1'] ) ) { // some backends store SHA-1 as metadata
1304  $this->cheapCache->set( $path, 'sha1',
1305  array( 'hash' => $stat['sha1'], 'latest' => $latest ) );
1306  }
1307  if ( isset( $stat['xattr'] ) ) { // some backends store headers/metadata
1308  $stat['xattr'] = self::normalizeXAttributes( $stat['xattr'] );
1309  $this->cheapCache->set( $path, 'xattr',
1310  array( 'map' => $stat['xattr'], 'latest' => $latest ) );
1311  }
1312  } elseif ( $stat === false ) { // file does not exist
1313  $this->cheapCache->set( $path, 'stat',
1314  $latest ? 'NOT_EXIST_LATEST' : 'NOT_EXIST' );
1315  $this->cheapCache->set( $path, 'xattr',
1316  array( 'map' => false, 'latest' => $latest ) );
1317  $this->cheapCache->set( $path, 'sha1',
1318  array( 'hash' => false, 'latest' => $latest ) );
1319  wfDebug( __METHOD__ . ": File $path does not exist.\n" );
1320  } else { // an error occurred
1321  wfDebug( __METHOD__ . ": Could not stat file $path.\n" );
1322  }
1323  }
1324  }
1325 
1337  protected function doGetFileStatMulti( array $params ) {
1338  return null; // not supported
1339  }
1340 
1348  abstract protected function directoriesAreVirtual();
1349 
1357  final protected static function isValidContainerName( $container ) {
1358  // This accounts for Swift and S3 restrictions while leaving room
1359  // for things like '.xxx' (hex shard chars) or '.seg' (segments).
1360  // This disallows directory separators or traversal characters.
1361  // Note that matching strings URL encode to the same string;
1362  // in Swift, the length restriction is *after* URL encoding.
1363  return preg_match( '/^[a-z0-9][a-z0-9-_]{0,199}$/i', $container );
1364  }
1365 
1379  final protected function resolveStoragePath( $storagePath ) {
1380  list( $backend, $container, $relPath ) = self::splitStoragePath( $storagePath );
1381  if ( $backend === $this->name ) { // must be for this backend
1382  $relPath = self::normalizeContainerPath( $relPath );
1383  if ( $relPath !== null ) {
1384  // Get shard for the normalized path if this container is sharded
1385  $cShard = $this->getContainerShard( $container, $relPath );
1386  // Validate and sanitize the relative path (backend-specific)
1387  $relPath = $this->resolveContainerPath( $container, $relPath );
1388  if ( $relPath !== null ) {
1389  // Prepend any wiki ID prefix to the container name
1390  $container = $this->fullContainerName( $container );
1391  if ( self::isValidContainerName( $container ) ) {
1392  // Validate and sanitize the container name (backend-specific)
1393  $container = $this->resolveContainerName( "{$container}{$cShard}" );
1394  if ( $container !== null ) {
1395  return array( $container, $relPath, $cShard );
1396  }
1397  }
1398  }
1399  }
1400  }
1401 
1402  return array( null, null, null );
1403  }
1404 
1420  final protected function resolveStoragePathReal( $storagePath ) {
1421  list( $container, $relPath, $cShard ) = $this->resolveStoragePath( $storagePath );
1422  if ( $cShard !== null && substr( $relPath, -1 ) !== '/' ) {
1423  return array( $container, $relPath );
1424  }
1425 
1426  return array( null, null );
1427  }
1428 
1437  final protected function getContainerShard( $container, $relPath ) {
1438  list( $levels, $base, $repeat ) = $this->getContainerHashLevels( $container );
1439  if ( $levels == 1 || $levels == 2 ) {
1440  // Hash characters are either base 16 or 36
1441  $char = ( $base == 36 ) ? '[0-9a-z]' : '[0-9a-f]';
1442  // Get a regex that represents the shard portion of paths.
1443  // The concatenation of the captures gives us the shard.
1444  if ( $levels === 1 ) { // 16 or 36 shards per container
1445  $hashDirRegex = '(' . $char . ')';
1446  } else { // 256 or 1296 shards per container
1447  if ( $repeat ) { // verbose hash dir format (e.g. "a/ab/abc")
1448  $hashDirRegex = $char . '/(' . $char . '{2})';
1449  } else { // short hash dir format (e.g. "a/b/c")
1450  $hashDirRegex = '(' . $char . ')/(' . $char . ')';
1451  }
1452  }
1453  // Allow certain directories to be above the hash dirs so as
1454  // to work with FileRepo (e.g. "archive/a/ab" or "temp/a/ab").
1455  // They must be 2+ chars to avoid any hash directory ambiguity.
1456  $m = array();
1457  if ( preg_match( "!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) {
1458  return '.' . implode( '', array_slice( $m, 1 ) );
1459  }
1460 
1461  return null; // failed to match
1462  }
1463 
1464  return ''; // no sharding
1465  }
1466 
1475  final public function isSingleShardPathInternal( $storagePath ) {
1476  list( , , $shard ) = $this->resolveStoragePath( $storagePath );
1477 
1478  return ( $shard !== null );
1479  }
1480 
1489  final protected function getContainerHashLevels( $container ) {
1490  if ( isset( $this->shardViaHashLevels[$container] ) ) {
1491  $config = $this->shardViaHashLevels[$container];
1492  $hashLevels = (int)$config['levels'];
1493  if ( $hashLevels == 1 || $hashLevels == 2 ) {
1494  $hashBase = (int)$config['base'];
1495  if ( $hashBase == 16 || $hashBase == 36 ) {
1496  return array( $hashLevels, $hashBase, $config['repeat'] );
1497  }
1498  }
1499  }
1500 
1501  return array( 0, 0, false ); // no sharding
1502  }
1503 
1510  final protected function getContainerSuffixes( $container ) {
1511  $shards = array();
1512  list( $digits, $base ) = $this->getContainerHashLevels( $container );
1513  if ( $digits > 0 ) {
1514  $numShards = pow( $base, $digits );
1515  for ( $index = 0; $index < $numShards; $index++ ) {
1516  $shards[] = '.' . wfBaseConvert( $index, 10, $base, $digits );
1517  }
1518  }
1519 
1520  return $shards;
1521  }
1522 
1529  final protected function fullContainerName( $container ) {
1530  if ( $this->wikiId != '' ) {
1531  return "{$this->wikiId}-$container";
1532  } else {
1533  return $container;
1534  }
1535  }
1536 
1545  protected function resolveContainerName( $container ) {
1546  return $container;
1547  }
1548 
1559  protected function resolveContainerPath( $container, $relStoragePath ) {
1560  return $relStoragePath;
1561  }
1562 
1569  private function containerCacheKey( $container ) {
1570  return "filebackend:{$this->name}:{$this->wikiId}:container:{$container}";
1571  }
1572 
1579  final protected function setContainerCache( $container, array $val ) {
1580  $this->memCache->add( $this->containerCacheKey( $container ), $val, 14 * 86400 );
1581  }
1582 
1589  final protected function deleteContainerCache( $container ) {
1590  if ( !$this->memCache->set( $this->containerCacheKey( $container ), 'PURGED', 300 ) ) {
1591  trigger_error( "Unable to delete stat cache for container $container." );
1592  }
1593  }
1594 
1602  final protected function primeContainerCache( array $items ) {
1603  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
1604 
1605  $paths = array(); // list of storage paths
1606  $contNames = array(); // (cache key => resolved container name)
1607  // Get all the paths/containers from the items...
1608  foreach ( $items as $item ) {
1609  if ( self::isStoragePath( $item ) ) {
1610  $paths[] = $item;
1611  } elseif ( is_string( $item ) ) { // full container name
1612  $contNames[$this->containerCacheKey( $item )] = $item;
1613  }
1614  }
1615  // Get all the corresponding cache keys for paths...
1616  foreach ( $paths as $path ) {
1617  list( $fullCont, , ) = $this->resolveStoragePath( $path );
1618  if ( $fullCont !== null ) { // valid path for this backend
1619  $contNames[$this->containerCacheKey( $fullCont )] = $fullCont;
1620  }
1621  }
1622 
1623  $contInfo = array(); // (resolved container name => cache value)
1624  // Get all cache entries for these container cache keys...
1625  $values = $this->memCache->getMulti( array_keys( $contNames ) );
1626  foreach ( $values as $cacheKey => $val ) {
1627  $contInfo[$contNames[$cacheKey]] = $val;
1628  }
1629 
1630  // Populate the container process cache for the backend...
1631  $this->doPrimeContainerCache( array_filter( $contInfo, 'is_array' ) );
1632  }
1633 
1641  protected function doPrimeContainerCache( array $containerInfo ) {
1642  }
1643 
1650  private function fileCacheKey( $path ) {
1651  return "filebackend:{$this->name}:{$this->wikiId}:file:" . sha1( $path );
1652  }
1653 
1662  final protected function setFileCache( $path, array $val ) {
1664  if ( $path === null ) {
1665  return; // invalid storage path
1666  }
1667  $age = time() - wfTimestamp( TS_UNIX, $val['mtime'] );
1668  $ttl = min( 7 * 86400, max( 300, floor( .1 * $age ) ) );
1669  $key = $this->fileCacheKey( $path );
1670  // Set the cache unless it is currently salted with the value "PURGED".
1671  // Using add() handles this except it also is a no-op in that case where
1672  // the current value is not "latest" but $val is, so use CAS in that case.
1673  if ( !$this->memCache->add( $key, $val, $ttl ) && !empty( $val['latest'] ) ) {
1674  $this->memCache->merge(
1675  $key,
1676  function( BagOStuff $cache, $key, $cValue ) use ( $val ) {
1677  return ( is_array( $cValue ) && empty( $cValue['latest'] ) )
1678  ? $val // update the stat cache with the lastest info
1679  : false; // do nothing (cache is salted or some error happened)
1680  },
1681  $ttl,
1682  1
1683  );
1684  }
1685  }
1686 
1695  final protected function deleteFileCache( $path ) {
1697  if ( $path === null ) {
1698  return; // invalid storage path
1699  }
1700  if ( !$this->memCache->set( $this->fileCacheKey( $path ), 'PURGED', 300 ) ) {
1701  trigger_error( "Unable to delete stat cache for file $path." );
1702  }
1703  }
1704 
1712  final protected function primeFileCache( array $items ) {
1713  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
1714 
1715  $paths = array(); // list of storage paths
1716  $pathNames = array(); // (cache key => storage path)
1717  // Get all the paths/containers from the items...
1718  foreach ( $items as $item ) {
1719  if ( self::isStoragePath( $item ) ) {
1720  $paths[] = FileBackend::normalizeStoragePath( $item );
1721  }
1722  }
1723  // Get rid of any paths that failed normalization...
1724  $paths = array_filter( $paths, 'strlen' ); // remove nulls
1725  // Get all the corresponding cache keys for paths...
1726  foreach ( $paths as $path ) {
1727  list( , $rel, ) = $this->resolveStoragePath( $path );
1728  if ( $rel !== null ) { // valid path for this backend
1729  $pathNames[$this->fileCacheKey( $path )] = $path;
1730  }
1731  }
1732  // Get all cache entries for these container cache keys...
1733  $values = $this->memCache->getMulti( array_keys( $pathNames ) );
1734  foreach ( $values as $cacheKey => $val ) {
1735  if ( is_array( $val ) ) {
1736  $path = $pathNames[$cacheKey];
1737  $this->cheapCache->set( $path, 'stat', $val );
1738  if ( isset( $val['sha1'] ) ) { // some backends store SHA-1 as metadata
1739  $this->cheapCache->set( $path, 'sha1',
1740  array( 'hash' => $val['sha1'], 'latest' => $val['latest'] ) );
1741  }
1742  if ( isset( $val['xattr'] ) ) { // some backends store headers/metadata
1743  $val['xattr'] = self::normalizeXAttributes( $val['xattr'] );
1744  $this->cheapCache->set( $path, 'xattr',
1745  array( 'map' => $val['xattr'], 'latest' => $val['latest'] ) );
1746  }
1747  }
1748  }
1749  }
1750 
1758  final protected static function normalizeXAttributes( array $xattr ) {
1759  $newXAttr = array( 'headers' => array(), 'metadata' => array() );
1760 
1761  foreach ( $xattr['headers'] as $name => $value ) {
1762  $newXAttr['headers'][strtolower( $name )] = $value;
1763  }
1764 
1765  foreach ( $xattr['metadata'] as $name => $value ) {
1766  $newXAttr['metadata'][strtolower( $name )] = $value;
1767  }
1768 
1769  return $newXAttr;
1770  }
1771 
1778  final protected function setConcurrencyFlags( array $opts ) {
1779  $opts['concurrency'] = 1; // off
1780  if ( $this->parallelize === 'implicit' ) {
1781  if ( !isset( $opts['parallelize'] ) || $opts['parallelize'] ) {
1782  $opts['concurrency'] = $this->concurrency;
1783  }
1784  } elseif ( $this->parallelize === 'explicit' ) {
1785  if ( !empty( $opts['parallelize'] ) ) {
1786  $opts['concurrency'] = $this->concurrency;
1787  }
1788  }
1789 
1790  return $opts;
1791  }
1792 
1801  protected function getContentType( $storagePath, $content, $fsPath ) {
1802  return call_user_func_array( $this->mimeCallback, func_get_args() );
1803  }
1804 }
1805 
1816  public $params = array(); // params to caller functions
1818  public $backend;
1820  public $resourcesToClose = array();
1822  public $call; // string; name that identifies the function called
1823 
1827  public function closeResources() {
1828  array_map( 'fclose', $this->resourcesToClose );
1829  }
1830 }
1831 
1838 abstract class FileBackendStoreShardListIterator extends FilterIterator {
1840  protected $backend;
1843  protected $params;
1844 
1846  protected $container;
1847 
1849  protected $directory;
1852  protected $multiShardPaths = array(); // (rel path => 1)
1853 
1861  public function __construct(
1863  ) {
1864  $this->backend = $backend;
1865  $this->container = $container;
1866  $this->directory = $dir;
1867  $this->params = $params;
1868 
1869  $iter = new AppendIterator();
1870  foreach ( $suffixes as $suffix ) {
1871  $iter->append( $this->listFromShard( $this->container . $suffix ) );
1872  }
1873 
1874  parent::__construct( $iter );
1875  }
1876 
1877  public function accept() {
1878  $rel = $this->getInnerIterator()->current(); // path relative to given directory
1879  $path = $this->params['dir'] . "/{$rel}"; // full storage path
1880  if ( $this->backend->isSingleShardPathInternal( $path ) ) {
1881  return true; // path is only on one shard; no issue with duplicates
1882  } elseif ( isset( $this->multiShardPaths[$rel] ) ) {
1883  // Don't keep listing paths that are on multiple shards
1884  return false;
1885  } else {
1886  $this->multiShardPaths[$rel] = 1;
1887 
1888  return true;
1889  }
1890  }
1891 
1892  public function rewind() {
1893  parent::rewind();
1894  $this->multiShardPaths = array();
1895  }
1896 
1903  abstract protected function listFromShard( $container );
1904 }
1905 
1910  protected function listFromShard( $container ) {
1911  $list = $this->backend->getDirectoryListInternal(
1912  $container, $this->directory, $this->params );
1913  if ( $list === null ) {
1914  return new ArrayIterator( array() );
1915  } else {
1916  return is_array( $list ) ? new ArrayIterator( $list ) : $list;
1917  }
1918  }
1919 }
1920 
1925  protected function listFromShard( $container ) {
1926  $list = $this->backend->getFileListInternal(
1927  $container, $this->directory, $this->params );
1928  if ( $list === null ) {
1929  return new ArrayIterator( array() );
1930  } else {
1931  return is_array( $list ) ? new ArrayIterator( $list ) : $list;
1932  }
1933  }
1934 }
FileBackendStore\doGetFileXAttributes
doGetFileXAttributes(array $params)
Definition: FileBackendStore.php:733
FileBackendStore\isPathUsableInternal
isPathUsableInternal( $storagePath)
Check if a file can be created or changed at a given storage path.
FileBackend\splitStoragePath
static splitStoragePath( $storagePath)
Split a storage path into a backend name, a container name, and a relative file path.
Definition: FileBackend.php:1342
FileBackendStore\directoryExists
directoryExists(array $params)
Check if a directory exists at a given storage path.
Definition: FileBackendStore.php:901
FileBackendStore\getOperationsInternal
getOperationsInternal(array $ops)
Return a list of FileOp objects from a list of operations.
Definition: FileBackendStore.php:1007
FileBackendStoreShardListIterator\$backend
FileBackendStore $backend
Definition: FileBackendStore.php:1833
FileBackendStoreShardListIterator\listFromShard
listFromShard( $container)
Get the list for a given container shard.
FileBackendStore\$maxFileSize
$maxFileSize
Definition: FileBackendStore.php:49
FileBackendStoreShardFileIterator
Iterator for listing regular files.
Definition: FileBackendStore.php:1913
FileBackendStore\getFileSha1Base36
getFileSha1Base36(array $params)
Get a SHA-1 hash of the file at a storage path in the backend.
Definition: FileBackendStore.php:737
FileBackend\normalizeContainerPath
static normalizeContainerPath( $path)
Validate and normalize a relative storage path.
Definition: FileBackend.php:1452
directory
The most up to date schema for the tables in the database will always be tables sql in the maintenance directory
Definition: schema.txt:2
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
FileBackendStore\directoriesAreVirtual
directoriesAreVirtual()
Is this a key/value store where directories are just virtual? Virtual directories exists in so much a...
FileBackend
Base class for all file backend classes (including multi-write backends).
Definition: FileBackend.php:85
StreamFile\contentTypeFromPath
static contentTypeFromPath( $filename, $safe=true)
Determine the file type of a file based on the path.
Definition: StreamFile.php:151
EmptyBagOStuff
A BagOStuff object with no objects in it.
Definition: EmptyBagOStuff.php:29
Status\isOK
isOK()
Returns whether the operation completed.
Definition: Status.php:109
StreamFile\NOT_MODIFIED
const NOT_MODIFIED
Definition: StreamFile.php:28
Status\merge
merge( $other, $overwriteValue=false)
Merge another status object into this one.
Definition: Status.php:325
FileBackendStore\resolveStoragePath
resolveStoragePath( $storagePath)
Splits a storage path into an internal container name, an internal relative file name,...
Definition: FileBackendStore.php:1376
LockManager\LOCK_UW
const LOCK_UW
Definition: LockManager.php:59
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2483
FileBackend\getName
getName()
Get the unique backend name.
Definition: FileBackend.php:167
FileBackendStore\copyInternal
copyInternal(array $params)
Copy a file from one storage path to another in the backend.
Definition: FileBackendStore.php:200
FileBackendError
File backend exception for checked exceptions (e.g.
Definition: FileBackend.php:1492
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all')
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1040
FileBackendStoreShardListIterator
FileBackendStore helper function to handle listings that span container shards.
Definition: FileBackendStore.php:1832
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
FileBackendStore\doGetFileSha1Base36
doGetFileSha1Base36(array $params)
Definition: FileBackendStore.php:765
wfSuppressWarnings
wfSuppressWarnings( $end=false)
Reference-counted warning suppression.
Definition: GlobalFunctions.php:2387
FileBackendStore\fileExists
fileExists(array $params)
Check if a file exists at a storage path in the backend.
Definition: FileBackendStore.php:599
FileBackendStoreShardListIterator\rewind
rewind()
Definition: FileBackendStore.php:1881
Status\newGood
static newGood( $value=null)
Factory function for good results.
Definition: Status.php:77
FileBackendStore\deleteFileCache
deleteFileCache( $path)
Delete the cached stat info for a file path.
Definition: FileBackendStore.php:1692
$params
$params
Definition: styleTest.css.php:40
FileBackend\normalizeStoragePath
static normalizeStoragePath( $storagePath)
Normalize a storage path by cleaning up directory separators.
Definition: FileBackend.php:1365
FileBackendStore\executeOpHandlesInternal
executeOpHandlesInternal(array $fileOpHandles)
Execute a list of FileBackendStoreOpHandle handles in parallel.
Definition: FileBackendStore.php:1184
BagOStuff
interface is intended to be more or less compatible with the PHP memcached client.
Definition: BagOStuff.php:43
FileBackendStoreShardListIterator\$params
array $params
Definition: FileBackendStore.php:1835
FileBackendStore\isSingleShardPathInternal
isSingleShardPathInternal( $storagePath)
Check if a storage path maps to a single shard.
Definition: FileBackendStore.php:1472
FileBackendStore\doDeleteInternal
doDeleteInternal(array $params)
FileBackendStore\getFileSize
getFileSize(array $params)
Get the size (bytes) of a file at a storage path in the backend.
Definition: FileBackendStore.php:613
FileBackendStore\concatenate
concatenate(array $params)
Concatenate a list of storage files into a single file system file.
Definition: FileBackendStore.php:345
FileBackendStore\doPublish
doPublish(array $params)
Definition: FileBackendStore.php:508
FileBackendStore\getContainerHashLevels
getContainerHashLevels( $container)
Get the sharding config for a container.
Definition: FileBackendStore.php:1486
FileBackendStore\doGetFileStat
doGetFileStat(array $params)
FileBackendStore\deleteContainerCache
deleteContainerCache( $container)
Delete the cached info for a container.
Definition: FileBackendStore.php:1586
FileBackendStore\getFileContentsMulti
getFileContentsMulti(array $params)
Like getFileContents() except it takes an array of storage paths and returns a map of storage paths t...
Definition: FileBackendStore.php:678
FileBackendStore\clearCache
clearCache(array $paths=null)
Invalidate any in-process file stat and property cache.
Definition: FileBackendStore.php:1253
FileBackendStore\doPublishInternal
doPublishInternal( $container, $dir, array $params)
Definition: FileBackendStore.php:539
FileBackendStore\getFileListInternal
getFileListInternal( $container, $dir, array $params)
Do not call this function from places outside FileBackend.
FileBackendStore\doOperationsInternal
doOperationsInternal(array $ops, array $opts)
Definition: FileBackendStore.php:1070
FileBackendStoreShardDirIterator
Iterator for listing directories.
Definition: FileBackendStore.php:1898
FileBackendStore\$expensiveCache
$expensiveCache
Definition: FileBackendStore.php:43
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:40
FileBackendStore\doSecure
doSecure(array $params)
Definition: FileBackendStore.php:473
FileBackendStore\$cheapCache
$cheapCache
Definition: FileBackendStore.php:41
FileBackendStore\doPrepare
doPrepare(array $params)
Definition: FileBackendStore.php:438
FileBackendStore\isValidContainerName
static isValidContainerName( $container)
Check if a container name is valid.
Definition: FileBackendStore.php:1354
FSFile\placeholderProps
static placeholderProps()
Placeholder file properties to use for files that don't exist.
Definition: FSFile.php:157
FileBackendStore\deleteInternal
deleteInternal(array $params)
Delete a file at the storage path.
Definition: FileBackendStore.php:232
FileBackend\getScopedFileLocks
getScopedFileLocks(array $paths, $type, Status $status)
Lock the files at the given storage paths in the backend.
Definition: FileBackend.php:1262
FileBackendStore\resolveContainerName
resolveContainerName( $container)
Resolve a container name, checking if it's allowed by the backend.
Definition: FileBackendStore.php:1542
ProfileSection
Class for handling function-scope profiling.
Definition: Profiler.php:60
FileBackendStore\doPrimeContainerCache
doPrimeContainerCache(array $containerInfo)
Fill the backend-specific process cache given an array of resolved container names and their correspo...
Definition: FileBackendStore.php:1638
FileBackendStore\getDirectoryListInternal
getDirectoryListInternal( $container, $dir, array $params)
Do not call this function from places outside FileBackend.
wfRestoreWarnings
wfRestoreWarnings()
Restore error level to previous value.
Definition: GlobalFunctions.php:2417
FileBackendStore\normalizeXAttributes
static normalizeXAttributes(array $xattr)
Normalize file headers/metadata to the FileBackend::getFileXAttributes() format.
Definition: FileBackendStore.php:1755
FileBackendStore\setConcurrencyFlags
setConcurrencyFlags(array $opts)
Set the 'concurrency' option from a list of operation options.
Definition: FileBackendStore.php:1775
FileBackendStore\CACHE_CHEAP_SIZE
const CACHE_CHEAP_SIZE
Definition: FileBackendStore.php:52
FileBackendStore\getLocalCopyMulti
getLocalCopyMulti(array $params)
Like getLocalCopy() except it takes an array of storage paths and returns a map of storage paths to T...
Definition: FileBackendStore.php:825
FileBackend\$concurrency
int $concurrency
How many operations can be done in parallel *.
Definition: FileBackend.php:94
FileBackendStore\getFileProps
getFileProps(array $params)
Get the properties of the file at a storage path in the backend.
Definition: FileBackendStore.php:774
FileBackendStoreOpHandle\$backend
FileBackendStore $backend
Definition: FileBackendStore.php:1813
FileBackendStore\doConcatenate
doConcatenate(array $params)
Definition: FileBackendStore.php:370
FileBackendStore\doDirectoryExists
doDirectoryExists( $container, $dir, array $params)
FileBackendStore\getLocalReferenceMulti
getLocalReferenceMulti(array $params)
Like getLocalReference() except it takes an array of storage paths and returns a map of storage paths...
Definition: FileBackendStore.php:782
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
FileBackendStore\preloadFileStat
preloadFileStat(array $params)
Preload file stat information (concurrently if possible) into in-process cache.
Definition: FileBackendStore.php:1280
FileBackendStore\doGetLocalReferenceMulti
doGetLocalReferenceMulti(array $params)
Definition: FileBackendStore.php:821
FileBackendStore\CACHE_EXPENSIVE_SIZE
const CACHE_EXPENSIVE_SIZE
Definition: FileBackendStore.php:53
StreamFile\READY_STREAM
const READY_STREAM
Definition: StreamFile.php:27
FileBackendStore\getPathsToLockForOpsInternal
getPathsToLockForOpsInternal(array $performOps)
Get a list of storage paths to lock for a list of operations Returns an array with LockManager::LOCK_...
Definition: FileBackendStore.php:1046
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
FileBackendStore\$memCache
BagOStuff $memCache
Definition: FileBackendStore.php:39
FileBackendStore\doCreateInternal
doCreateInternal(array $params)
FileBackendStore\getContainerShard
getContainerShard( $container, $relPath)
Get the container name shard suffix for a given path.
Definition: FileBackendStore.php:1434
FileBackendStoreShardFileIterator\listFromShard
listFromShard( $container)
Get the list for a given container shard.
Definition: FileBackendStore.php:1914
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:188
FileBackendStore\getContentType
getContentType( $storagePath, $content, $fsPath)
Get the content type to use in HEAD/GET requests for a file.
Definition: FileBackendStore.php:1798
FileBackendStore\doCleanInternal
doCleanInternal( $container, $dir, array $params)
Definition: FileBackendStore.php:595
FileBackendStore\doCopyInternal
doCopyInternal(array $params)
FileBackendStore\doClearCache
doClearCache(array $paths=null)
Clears any additional stat caches for storage paths.
Definition: FileBackendStore.php:1277
$ok
$ok
Definition: UtfNormalTest.php:71
$section
$section
Definition: Utf8Test.php:88
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:933
FileBackendStore\doStoreInternal
doStoreInternal(array $params)
FileBackendStore\$mimeCallback
callback $mimeCallback
Method to get the MIME type of files *.
Definition: FileBackendStore.php:47
FileBackendStore\doMoveInternal
doMoveInternal(array $params)
Definition: FileBackendStore.php:283
$value
$value
Definition: styleTest.css.php:45
FileBackendStore\getFileStat
getFileStat(array $params)
Get quick information about a file at a storage path in the backend.
Definition: FileBackendStore.php:620
FileBackendStore\CACHE_TTL
const CACHE_TTL
Definition: FileBackendStore.php:51
FileOpBatch\attempt
static attempt(array $performOps, array $opts, FileJournal $journal)
Attempt to perform a series of file operations.
Definition: FileOpBatch.php:57
FileBackendStore\containerCacheKey
containerCacheKey( $container)
Get the cache key for a container.
Definition: FileBackendStore.php:1566
FileBackendStore\resolveStoragePathReal
resolveStoragePathReal( $storagePath)
Like resolveStoragePath() except null values are returned if the container is sharded and the shard c...
Definition: FileBackendStore.php:1417
FileBackendStore\moveInternal
moveInternal(array $params)
Move a file from one storage path to another in the backend.
Definition: FileBackendStore.php:266
FileBackendStoreOpHandle\$resourcesToClose
array $resourcesToClose
Definition: FileBackendStore.php:1814
FileBackendStore\preloadCache
preloadCache(array $paths)
Preload persistent file stat cache and property cache into in-process cache.
Definition: FileBackendStore.php:1242
FileBackendStoreShardListIterator\__construct
__construct(FileBackendStore $backend, $container, $dir, array $suffixes, array $params)
Definition: FileBackendStore.php:1850
FileBackendStore\getContainerSuffixes
getContainerSuffixes( $container)
Get a list of full container shard suffixes for a container.
Definition: FileBackendStore.php:1507
FileBackendStore
Base class for all backends using particular storage medium.
Definition: FileBackendStore.php:38
FileBackendStore\getDirectoryList
getDirectoryList(array $params)
Get an iterator to list all directories under a storage directory.
Definition: FileBackendStore.php:936
FileBackendStore\stripInvalidHeadersFromOp
stripInvalidHeadersFromOp(array $op)
Strip long HTTP headers from a file operation.
Definition: FileBackendStore.php:1225
FileBackendStoreOpHandle
FileBackendStore helper class for performing asynchronous file operations.
Definition: FileBackendStore.php:1811
StreamFile\prepareForStream
static prepareForStream( $path, $info, $headers=array(), $sendErrors=true)
Call this function used in preparation before streaming a file.
Definition: StreamFile.php:81
FileBackendStore\fullContainerName
fullContainerName( $container)
Get the full container name, including the wiki ID prefix.
Definition: FileBackendStore.php:1526
FileBackendStore\doDescribeInternal
doDescribeInternal(array $params)
Definition: FileBackendStore.php:330
$hash
return false to override stock group addition can be modified try getUserPermissionsErrors userCan checks are continued by internal code can override on output return false to not delete it return false to override the default password checks & $hash
Definition: hooks.txt:2697
FileBackendStore\doExecuteOpHandlesInternal
doExecuteOpHandlesInternal(array $fileOpHandles)
Definition: FileBackendStore.php:1208
FileBackendStoreShardListIterator\$multiShardPaths
array $multiShardPaths
Definition: FileBackendStore.php:1841
FileBackendStore\doClean
doClean(array $params)
Definition: FileBackendStore.php:543
FileBackendStore\primeContainerCache
primeContainerCache(array $items)
Do a batch lookup from cache for container stats for all containers used in a list of container names...
Definition: FileBackendStore.php:1599
FileBackendStore\setFileCache
setFileCache( $path, array $val)
Set the cached stat info for a file path.
Definition: FileBackendStore.php:1659
FileBackendStore\primeFileCache
primeFileCache(array $items)
Do a batch lookup from cache for file stats for all paths used in a list of storage paths or FileOp o...
Definition: FileBackendStore.php:1709
FileBackendStore\fileCacheKey
fileCacheKey( $path)
Get the cache key for a file path.
Definition: FileBackendStore.php:1647
$cache
$cache
Definition: mcc.php:32
FileBackendStore\doPrepareInternal
doPrepareInternal( $container, $dir, array $params)
Definition: FileBackendStore.php:469
$dir
if(count( $args)==0) $dir
Definition: importImages.php:49
FileBackendStore\doGetLocalCopyMulti
doGetLocalCopyMulti(array $params)
FileBackendStore\doGetFileContentsMulti
doGetFileContentsMulti(array $params)
Definition: FileBackendStore.php:692
FileBackend\$name
string $name
Unique backend name *.
Definition: FileBackend.php:86
wfBaseConvert
wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true, $engine='auto')
Convert an arbitrarily-long digit string from one numeric base to another, optionally zero-padding to...
Definition: GlobalFunctions.php:3368
FileBackendStore\createInternal
createInternal(array $params)
Create a file in the backend with the given contents.
Definition: FileBackendStore.php:117
FileBackendStore\resolveContainerPath
resolveContainerPath( $container, $relStoragePath)
Resolve a relative storage path, checking if it's allowed by the backend.
Definition: FileBackendStore.php:1556
TS_UNIX
const TS_UNIX
Unix time - the number of seconds since 1970-01-01 00:00:00 UTC.
Definition: GlobalFunctions.php:2426
$path
$path
Definition: NoLocalSettings.php:35
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
FileBackendStoreShardListIterator\$container
string $container
Full container name *.
Definition: FileBackendStore.php:1837
FileBackendStoreOpHandle\$params
array $params
Definition: FileBackendStore.php:1812
FileBackendStore\storeInternal
storeInternal(array $params)
Store a file into the backend from a file on disk.
Definition: FileBackendStore.php:158
FileBackendStore\doStreamFile
doStreamFile(array $params)
Definition: FileBackendStore.php:888
FileBackendStore\streamFile
streamFile(array $params)
Stream the file at a storage path in the backend.
Definition: FileBackendStore.php:850
FileBackendStore\$shardViaHashLevels
array $shardViaHashLevels
Map of container names to sharding config *.
Definition: FileBackendStore.php:45
FileBackend\getTopDirectoryList
getTopDirectoryList(array $params)
Same as FileBackend::getDirectoryList() except only lists directories that are immediately under the ...
Definition: FileBackend.php:1142
FileBackendStore\getFileXAttributes
getFileXAttributes(array $params)
Get metadata about a file at a storage path in the backend.
Definition: FileBackendStore.php:703
FileBackendStore\getFileHttpUrl
getFileHttpUrl(array $params)
Definition: FileBackendStore.php:846
FileBackend\getLocalReference
getLocalReference(array $params)
Returns a file system file, identical to the file at a storage path.
Definition: FileBackend.php:1021
name
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at name
Definition: design.txt:12
FileBackendStore\nullInternal
nullInternal(array $params)
No-op file operation that does nothing.
Definition: FileBackendStore.php:341
ProcessCacheLRU
Handles per process caching of items.
Definition: ProcessCacheLRU.php:28
FileBackendStore\getFileList
getFileList(array $params)
Get an iterator to list all stored files under a storage directory.
Definition: FileBackendStore.php:966
FileBackendStore\getScopedLocksForOps
getScopedLocksForOps(array $ops, Status $status)
Get an array of scoped locks needed for a batch of file operations.
Definition: FileBackendStore.php:1064
FileBackendStore\maxFileSizeInternal
maxFileSizeInternal()
Get the maximum allowable file size given backend medium restrictions and basic performance constrain...
Definition: FileBackendStore.php:84
LockManager\LOCK_EX
const LOCK_EX
Definition: LockManager.php:60
FileBackendStore\getFileTimestamp
getFileTimestamp(array $params)
Get the last-modified timestamp of the file at a storage path.
Definition: FileBackendStore.php:606
FileBackendStore\describeInternal
describeInternal(array $params)
Alter metadata for a file at the storage path.
Definition: FileBackendStore.php:312
$res
$res
Definition: database.txt:21
FileBackendStoreShardListIterator\accept
accept()
Definition: FileBackendStore.php:1866
FileBackendStore\doGetFileStatMulti
doGetFileStatMulti(array $params)
Get file stat information (concurrently if possible) for several files.
Definition: FileBackendStore.php:1334
FileBackendStore\__construct
__construct(array $config)
Definition: FileBackendStore.php:64
FileBackendStore\doSecureInternal
doSecureInternal( $container, $dir, array $params)
Definition: FileBackendStore.php:504
FileBackendStoreOpHandle\$call
$call
Definition: FileBackendStore.php:1816
FileBackendStore\setContainerCache
setContainerCache( $container, array $val)
Set the cached info for a container.
Definition: FileBackendStore.php:1576
FileBackendStoreShardListIterator\$directory
string $directory
Resolved relative path *.
Definition: FileBackendStore.php:1839
FileBackendStore\doQuickOperationsInternal
doQuickOperationsInternal(array $ops)
Definition: FileBackendStore.php:1118
FileBackendStoreOpHandle\closeResources
closeResources()
Close all open file handles.
Definition: FileBackendStore.php:1821
Status\newFatal
static newFatal( $message)
Factory function for fatal errors.
Definition: Status.php:63
FileBackendStoreShardDirIterator\listFromShard
listFromShard( $container)
Get the list for a given container shard.
Definition: FileBackendStore.php:1899