MediaWiki  1.23.6
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  // Parallel ops may be disabled in config due to dependencies (e.g. needing popen())
1133  $async = ( $this->parallelize === 'implicit' && count( $ops ) > 1 );
1134  $maxConcurrency = $this->concurrency; // throttle
1135 
1136  $statuses = array(); // array of (index => Status)
1137  $fileOpHandles = array(); // list of (index => handle) arrays
1138  $curFileOpHandles = array(); // current handle batch
1139  // Perform the sync-only ops and build up op handles for the async ops...
1140  foreach ( $ops as $index => $params ) {
1141  if ( !in_array( $params['op'], $supportedOps ) ) {
1142  throw new FileBackendError( "Operation '{$params['op']}' is not supported." );
1143  }
1144  $method = $params['op'] . 'Internal'; // e.g. "storeInternal"
1145  $subStatus = $this->$method( array( 'async' => $async ) + $params );
1146  if ( $subStatus->value instanceof FileBackendStoreOpHandle ) { // async
1147  if ( count( $curFileOpHandles ) >= $maxConcurrency ) {
1148  $fileOpHandles[] = $curFileOpHandles; // push this batch
1149  $curFileOpHandles = array();
1150  }
1151  $curFileOpHandles[$index] = $subStatus->value; // keep index
1152  } else { // error or completed
1153  $statuses[$index] = $subStatus; // keep index
1154  }
1155  }
1156  if ( count( $curFileOpHandles ) ) {
1157  $fileOpHandles[] = $curFileOpHandles; // last batch
1158  }
1159  // Do all the async ops that can be done concurrently...
1160  foreach ( $fileOpHandles as $fileHandleBatch ) {
1161  $statuses = $statuses + $this->executeOpHandlesInternal( $fileHandleBatch );
1162  }
1163  // Marshall and merge all the responses...
1164  foreach ( $statuses as $index => $subStatus ) {
1165  $status->merge( $subStatus );
1166  if ( $subStatus->isOK() ) {
1167  $status->success[$index] = true;
1168  ++$status->successCount;
1169  } else {
1170  $status->success[$index] = false;
1171  ++$status->failCount;
1172  }
1173  }
1174 
1175  return $status;
1176  }
1177 
1188  final public function executeOpHandlesInternal( array $fileOpHandles ) {
1189  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
1190 
1191  foreach ( $fileOpHandles as $fileOpHandle ) {
1192  if ( !( $fileOpHandle instanceof FileBackendStoreOpHandle ) ) {
1193  throw new FileBackendError( "Given a non-FileBackendStoreOpHandle object." );
1194  } elseif ( $fileOpHandle->backend->getName() !== $this->getName() ) {
1195  throw new FileBackendError( "Given a FileBackendStoreOpHandle for the wrong backend." );
1196  }
1197  }
1198  $res = $this->doExecuteOpHandlesInternal( $fileOpHandles );
1199  foreach ( $fileOpHandles as $fileOpHandle ) {
1200  $fileOpHandle->closeResources();
1201  }
1202 
1203  return $res;
1204  }
1205 
1212  protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
1213  if ( count( $fileOpHandles ) ) {
1214  throw new FileBackendError( "This backend supports no asynchronous operations." );
1215  }
1216 
1217  return array();
1218  }
1219 
1229  protected function stripInvalidHeadersFromOp( array $op ) {
1230  static $longs = array( 'Content-Disposition' );
1231  if ( isset( $op['headers'] ) ) { // op sets HTTP headers
1232  foreach ( $op['headers'] as $name => $value ) {
1233  $maxHVLen = in_array( $name, $longs ) ? INF : 255;
1234  if ( strlen( $name ) > 255 || strlen( $value ) > $maxHVLen ) {
1235  trigger_error( "Header '$name: $value' is too long." );
1236  unset( $op['headers'][$name] );
1237  } elseif ( !strlen( $value ) ) {
1238  $op['headers'][$name] = ''; // null/false => ""
1239  }
1240  }
1241  }
1242 
1243  return $op;
1244  }
1245 
1246  final public function preloadCache( array $paths ) {
1247  $fullConts = array(); // full container names
1248  foreach ( $paths as $path ) {
1249  list( $fullCont, , ) = $this->resolveStoragePath( $path );
1250  $fullConts[] = $fullCont;
1251  }
1252  // Load from the persistent file and container caches
1253  $this->primeContainerCache( $fullConts );
1254  $this->primeFileCache( $paths );
1255  }
1256 
1257  final public function clearCache( array $paths = null ) {
1258  if ( is_array( $paths ) ) {
1259  $paths = array_map( 'FileBackend::normalizeStoragePath', $paths );
1260  $paths = array_filter( $paths, 'strlen' ); // remove nulls
1261  }
1262  if ( $paths === null ) {
1263  $this->cheapCache->clear();
1264  $this->expensiveCache->clear();
1265  } else {
1266  foreach ( $paths as $path ) {
1267  $this->cheapCache->clear( $path );
1268  $this->expensiveCache->clear( $path );
1269  }
1270  }
1271  $this->doClearCache( $paths );
1272  }
1273 
1281  protected function doClearCache( array $paths = null ) {
1282  }
1283 
1284  final public function preloadFileStat( array $params ) {
1285  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
1286 
1287  $params['concurrency'] = ( $this->parallelize !== 'off' ) ? $this->concurrency : 1;
1288  $stats = $this->doGetFileStatMulti( $params );
1289  if ( $stats === null ) {
1290  return; // not supported
1291  }
1292 
1293  $latest = !empty( $params['latest'] ); // use latest data?
1294  foreach ( $stats as $path => $stat ) {
1296  if ( $path === null ) {
1297  continue; // this shouldn't happen
1298  }
1299  if ( is_array( $stat ) ) { // file exists
1300  // Strongly consistent backends can automatically set "latest"
1301  $stat['latest'] = isset( $stat['latest'] ) ? $stat['latest'] : $latest;
1302  $this->cheapCache->set( $path, 'stat', $stat );
1303  $this->setFileCache( $path, $stat ); // update persistent cache
1304  if ( isset( $stat['sha1'] ) ) { // some backends store SHA-1 as metadata
1305  $this->cheapCache->set( $path, 'sha1',
1306  array( 'hash' => $stat['sha1'], 'latest' => $latest ) );
1307  }
1308  if ( isset( $stat['xattr'] ) ) { // some backends store headers/metadata
1309  $stat['xattr'] = self::normalizeXAttributes( $stat['xattr'] );
1310  $this->cheapCache->set( $path, 'xattr',
1311  array( 'map' => $stat['xattr'], 'latest' => $latest ) );
1312  }
1313  } elseif ( $stat === false ) { // file does not exist
1314  $this->cheapCache->set( $path, 'stat',
1315  $latest ? 'NOT_EXIST_LATEST' : 'NOT_EXIST' );
1316  $this->cheapCache->set( $path, 'xattr',
1317  array( 'map' => false, 'latest' => $latest ) );
1318  $this->cheapCache->set( $path, 'sha1',
1319  array( 'hash' => false, 'latest' => $latest ) );
1320  wfDebug( __METHOD__ . ": File $path does not exist.\n" );
1321  } else { // an error occurred
1322  wfDebug( __METHOD__ . ": Could not stat file $path.\n" );
1323  }
1324  }
1325  }
1326 
1338  protected function doGetFileStatMulti( array $params ) {
1339  return null; // not supported
1340  }
1341 
1349  abstract protected function directoriesAreVirtual();
1350 
1358  final protected static function isValidContainerName( $container ) {
1359  // This accounts for Swift and S3 restrictions while leaving room
1360  // for things like '.xxx' (hex shard chars) or '.seg' (segments).
1361  // This disallows directory separators or traversal characters.
1362  // Note that matching strings URL encode to the same string;
1363  // in Swift, the length restriction is *after* URL encoding.
1364  return preg_match( '/^[a-z0-9][a-z0-9-_]{0,199}$/i', $container );
1365  }
1366 
1380  final protected function resolveStoragePath( $storagePath ) {
1381  list( $backend, $container, $relPath ) = self::splitStoragePath( $storagePath );
1382  if ( $backend === $this->name ) { // must be for this backend
1383  $relPath = self::normalizeContainerPath( $relPath );
1384  if ( $relPath !== null ) {
1385  // Get shard for the normalized path if this container is sharded
1386  $cShard = $this->getContainerShard( $container, $relPath );
1387  // Validate and sanitize the relative path (backend-specific)
1388  $relPath = $this->resolveContainerPath( $container, $relPath );
1389  if ( $relPath !== null ) {
1390  // Prepend any wiki ID prefix to the container name
1391  $container = $this->fullContainerName( $container );
1392  if ( self::isValidContainerName( $container ) ) {
1393  // Validate and sanitize the container name (backend-specific)
1394  $container = $this->resolveContainerName( "{$container}{$cShard}" );
1395  if ( $container !== null ) {
1396  return array( $container, $relPath, $cShard );
1397  }
1398  }
1399  }
1400  }
1401  }
1402 
1403  return array( null, null, null );
1404  }
1405 
1421  final protected function resolveStoragePathReal( $storagePath ) {
1422  list( $container, $relPath, $cShard ) = $this->resolveStoragePath( $storagePath );
1423  if ( $cShard !== null && substr( $relPath, -1 ) !== '/' ) {
1424  return array( $container, $relPath );
1425  }
1426 
1427  return array( null, null );
1428  }
1429 
1438  final protected function getContainerShard( $container, $relPath ) {
1439  list( $levels, $base, $repeat ) = $this->getContainerHashLevels( $container );
1440  if ( $levels == 1 || $levels == 2 ) {
1441  // Hash characters are either base 16 or 36
1442  $char = ( $base == 36 ) ? '[0-9a-z]' : '[0-9a-f]';
1443  // Get a regex that represents the shard portion of paths.
1444  // The concatenation of the captures gives us the shard.
1445  if ( $levels === 1 ) { // 16 or 36 shards per container
1446  $hashDirRegex = '(' . $char . ')';
1447  } else { // 256 or 1296 shards per container
1448  if ( $repeat ) { // verbose hash dir format (e.g. "a/ab/abc")
1449  $hashDirRegex = $char . '/(' . $char . '{2})';
1450  } else { // short hash dir format (e.g. "a/b/c")
1451  $hashDirRegex = '(' . $char . ')/(' . $char . ')';
1452  }
1453  }
1454  // Allow certain directories to be above the hash dirs so as
1455  // to work with FileRepo (e.g. "archive/a/ab" or "temp/a/ab").
1456  // They must be 2+ chars to avoid any hash directory ambiguity.
1457  $m = array();
1458  if ( preg_match( "!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) {
1459  return '.' . implode( '', array_slice( $m, 1 ) );
1460  }
1461 
1462  return null; // failed to match
1463  }
1464 
1465  return ''; // no sharding
1466  }
1467 
1476  final public function isSingleShardPathInternal( $storagePath ) {
1477  list( , , $shard ) = $this->resolveStoragePath( $storagePath );
1478 
1479  return ( $shard !== null );
1480  }
1481 
1490  final protected function getContainerHashLevels( $container ) {
1491  if ( isset( $this->shardViaHashLevels[$container] ) ) {
1492  $config = $this->shardViaHashLevels[$container];
1493  $hashLevels = (int)$config['levels'];
1494  if ( $hashLevels == 1 || $hashLevels == 2 ) {
1495  $hashBase = (int)$config['base'];
1496  if ( $hashBase == 16 || $hashBase == 36 ) {
1497  return array( $hashLevels, $hashBase, $config['repeat'] );
1498  }
1499  }
1500  }
1501 
1502  return array( 0, 0, false ); // no sharding
1503  }
1504 
1511  final protected function getContainerSuffixes( $container ) {
1512  $shards = array();
1513  list( $digits, $base ) = $this->getContainerHashLevels( $container );
1514  if ( $digits > 0 ) {
1515  $numShards = pow( $base, $digits );
1516  for ( $index = 0; $index < $numShards; $index++ ) {
1517  $shards[] = '.' . wfBaseConvert( $index, 10, $base, $digits );
1518  }
1519  }
1520 
1521  return $shards;
1522  }
1523 
1530  final protected function fullContainerName( $container ) {
1531  if ( $this->wikiId != '' ) {
1532  return "{$this->wikiId}-$container";
1533  } else {
1534  return $container;
1535  }
1536  }
1537 
1546  protected function resolveContainerName( $container ) {
1547  return $container;
1548  }
1549 
1560  protected function resolveContainerPath( $container, $relStoragePath ) {
1561  return $relStoragePath;
1562  }
1563 
1570  private function containerCacheKey( $container ) {
1571  return "filebackend:{$this->name}:{$this->wikiId}:container:{$container}";
1572  }
1573 
1580  final protected function setContainerCache( $container, array $val ) {
1581  $this->memCache->add( $this->containerCacheKey( $container ), $val, 14 * 86400 );
1582  }
1583 
1590  final protected function deleteContainerCache( $container ) {
1591  if ( !$this->memCache->set( $this->containerCacheKey( $container ), 'PURGED', 300 ) ) {
1592  trigger_error( "Unable to delete stat cache for container $container." );
1593  }
1594  }
1595 
1603  final protected function primeContainerCache( array $items ) {
1604  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
1605 
1606  $paths = array(); // list of storage paths
1607  $contNames = array(); // (cache key => resolved container name)
1608  // Get all the paths/containers from the items...
1609  foreach ( $items as $item ) {
1610  if ( self::isStoragePath( $item ) ) {
1611  $paths[] = $item;
1612  } elseif ( is_string( $item ) ) { // full container name
1613  $contNames[$this->containerCacheKey( $item )] = $item;
1614  }
1615  }
1616  // Get all the corresponding cache keys for paths...
1617  foreach ( $paths as $path ) {
1618  list( $fullCont, , ) = $this->resolveStoragePath( $path );
1619  if ( $fullCont !== null ) { // valid path for this backend
1620  $contNames[$this->containerCacheKey( $fullCont )] = $fullCont;
1621  }
1622  }
1623 
1624  $contInfo = array(); // (resolved container name => cache value)
1625  // Get all cache entries for these container cache keys...
1626  $values = $this->memCache->getMulti( array_keys( $contNames ) );
1627  foreach ( $values as $cacheKey => $val ) {
1628  $contInfo[$contNames[$cacheKey]] = $val;
1629  }
1630 
1631  // Populate the container process cache for the backend...
1632  $this->doPrimeContainerCache( array_filter( $contInfo, 'is_array' ) );
1633  }
1634 
1642  protected function doPrimeContainerCache( array $containerInfo ) {
1643  }
1644 
1651  private function fileCacheKey( $path ) {
1652  return "filebackend:{$this->name}:{$this->wikiId}:file:" . sha1( $path );
1653  }
1654 
1663  final protected function setFileCache( $path, array $val ) {
1665  if ( $path === null ) {
1666  return; // invalid storage path
1667  }
1668  $age = time() - wfTimestamp( TS_UNIX, $val['mtime'] );
1669  $ttl = min( 7 * 86400, max( 300, floor( .1 * $age ) ) );
1670  $key = $this->fileCacheKey( $path );
1671  // Set the cache unless it is currently salted with the value "PURGED".
1672  // Using add() handles this except it also is a no-op in that case where
1673  // the current value is not "latest" but $val is, so use CAS in that case.
1674  if ( !$this->memCache->add( $key, $val, $ttl ) && !empty( $val['latest'] ) ) {
1675  $this->memCache->merge(
1676  $key,
1677  function( BagOStuff $cache, $key, $cValue ) use ( $val ) {
1678  return ( is_array( $cValue ) && empty( $cValue['latest'] ) )
1679  ? $val // update the stat cache with the lastest info
1680  : false; // do nothing (cache is salted or some error happened)
1681  },
1682  $ttl,
1683  1
1684  );
1685  }
1686  }
1687 
1696  final protected function deleteFileCache( $path ) {
1698  if ( $path === null ) {
1699  return; // invalid storage path
1700  }
1701  if ( !$this->memCache->set( $this->fileCacheKey( $path ), 'PURGED', 300 ) ) {
1702  trigger_error( "Unable to delete stat cache for file $path." );
1703  }
1704  }
1705 
1713  final protected function primeFileCache( array $items ) {
1714  $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
1715 
1716  $paths = array(); // list of storage paths
1717  $pathNames = array(); // (cache key => storage path)
1718  // Get all the paths/containers from the items...
1719  foreach ( $items as $item ) {
1720  if ( self::isStoragePath( $item ) ) {
1721  $paths[] = FileBackend::normalizeStoragePath( $item );
1722  }
1723  }
1724  // Get rid of any paths that failed normalization...
1725  $paths = array_filter( $paths, 'strlen' ); // remove nulls
1726  // Get all the corresponding cache keys for paths...
1727  foreach ( $paths as $path ) {
1728  list( , $rel, ) = $this->resolveStoragePath( $path );
1729  if ( $rel !== null ) { // valid path for this backend
1730  $pathNames[$this->fileCacheKey( $path )] = $path;
1731  }
1732  }
1733  // Get all cache entries for these container cache keys...
1734  $values = $this->memCache->getMulti( array_keys( $pathNames ) );
1735  foreach ( $values as $cacheKey => $val ) {
1736  if ( is_array( $val ) ) {
1737  $path = $pathNames[$cacheKey];
1738  $this->cheapCache->set( $path, 'stat', $val );
1739  if ( isset( $val['sha1'] ) ) { // some backends store SHA-1 as metadata
1740  $this->cheapCache->set( $path, 'sha1',
1741  array( 'hash' => $val['sha1'], 'latest' => $val['latest'] ) );
1742  }
1743  if ( isset( $val['xattr'] ) ) { // some backends store headers/metadata
1744  $val['xattr'] = self::normalizeXAttributes( $val['xattr'] );
1745  $this->cheapCache->set( $path, 'xattr',
1746  array( 'map' => $val['xattr'], 'latest' => $val['latest'] ) );
1747  }
1748  }
1749  }
1750  }
1751 
1759  final protected static function normalizeXAttributes( array $xattr ) {
1760  $newXAttr = array( 'headers' => array(), 'metadata' => array() );
1761 
1762  foreach ( $xattr['headers'] as $name => $value ) {
1763  $newXAttr['headers'][strtolower( $name )] = $value;
1764  }
1765 
1766  foreach ( $xattr['metadata'] as $name => $value ) {
1767  $newXAttr['metadata'][strtolower( $name )] = $value;
1768  }
1769 
1770  return $newXAttr;
1771  }
1772 
1779  final protected function setConcurrencyFlags( array $opts ) {
1780  $opts['concurrency'] = 1; // off
1781  if ( $this->parallelize === 'implicit' ) {
1782  if ( !isset( $opts['parallelize'] ) || $opts['parallelize'] ) {
1783  $opts['concurrency'] = $this->concurrency;
1784  }
1785  } elseif ( $this->parallelize === 'explicit' ) {
1786  if ( !empty( $opts['parallelize'] ) ) {
1787  $opts['concurrency'] = $this->concurrency;
1788  }
1789  }
1790 
1791  return $opts;
1792  }
1793 
1802  protected function getContentType( $storagePath, $content, $fsPath ) {
1803  return call_user_func_array( $this->mimeCallback, func_get_args() );
1804  }
1805 }
1806 
1817  public $params = array(); // params to caller functions
1819  public $backend;
1821  public $resourcesToClose = array();
1823  public $call; // string; name that identifies the function called
1824 
1828  public function closeResources() {
1829  array_map( 'fclose', $this->resourcesToClose );
1830  }
1831 }
1832 
1839 abstract class FileBackendStoreShardListIterator extends FilterIterator {
1841  protected $backend;
1844  protected $params;
1845 
1847  protected $container;
1848 
1850  protected $directory;
1853  protected $multiShardPaths = array(); // (rel path => 1)
1854 
1862  public function __construct(
1864  ) {
1865  $this->backend = $backend;
1866  $this->container = $container;
1867  $this->directory = $dir;
1868  $this->params = $params;
1869 
1870  $iter = new AppendIterator();
1871  foreach ( $suffixes as $suffix ) {
1872  $iter->append( $this->listFromShard( $this->container . $suffix ) );
1873  }
1874 
1875  parent::__construct( $iter );
1876  }
1877 
1878  public function accept() {
1879  $rel = $this->getInnerIterator()->current(); // path relative to given directory
1880  $path = $this->params['dir'] . "/{$rel}"; // full storage path
1881  if ( $this->backend->isSingleShardPathInternal( $path ) ) {
1882  return true; // path is only on one shard; no issue with duplicates
1883  } elseif ( isset( $this->multiShardPaths[$rel] ) ) {
1884  // Don't keep listing paths that are on multiple shards
1885  return false;
1886  } else {
1887  $this->multiShardPaths[$rel] = 1;
1888 
1889  return true;
1890  }
1891  }
1892 
1893  public function rewind() {
1894  parent::rewind();
1895  $this->multiShardPaths = array();
1896  }
1897 
1904  abstract protected function listFromShard( $container );
1905 }
1906 
1911  protected function listFromShard( $container ) {
1912  $list = $this->backend->getDirectoryListInternal(
1913  $container, $this->directory, $this->params );
1914  if ( $list === null ) {
1915  return new ArrayIterator( array() );
1916  } else {
1917  return is_array( $list ) ? new ArrayIterator( $list ) : $list;
1918  }
1919  }
1920 }
1921 
1926  protected function listFromShard( $container ) {
1927  $list = $this->backend->getFileListInternal(
1928  $container, $this->directory, $this->params );
1929  if ( $list === null ) {
1930  return new ArrayIterator( array() );
1931  } else {
1932  return is_array( $list ) ? new ArrayIterator( $list ) : $list;
1933  }
1934  }
1935 }
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:1834
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:1914
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:1377
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:1833
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:1882
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:1693
$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:1185
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:1836
FileBackendStore\isSingleShardPathInternal
isSingleShardPathInternal( $storagePath)
Check if a storage path maps to a single shard.
Definition: FileBackendStore.php:1473
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:1487
FileBackendStore\doGetFileStat
doGetFileStat(array $params)
FileBackendStore\deleteContainerCache
deleteContainerCache( $container)
Delete the cached info for a container.
Definition: FileBackendStore.php:1587
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:1254
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:1899
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:1355
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:1543
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:1639
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:1756
FileBackendStore\setConcurrencyFlags
setConcurrencyFlags(array $opts)
Set the 'concurrency' option from a list of operation options.
Definition: FileBackendStore.php:1776
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:1814
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:1281
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:1435
FileBackendStoreShardFileIterator\listFromShard
listFromShard( $container)
Get the list for a given container shard.
Definition: FileBackendStore.php:1915
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:1799
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:1278
$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:1567
FileBackendStore\resolveStoragePathReal
resolveStoragePathReal( $storagePath)
Like resolveStoragePath() except null values are returned if the container is sharded and the shard c...
Definition: FileBackendStore.php:1418
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:1815
FileBackendStore\preloadCache
preloadCache(array $paths)
Preload persistent file stat cache and property cache into in-process cache.
Definition: FileBackendStore.php:1243
FileBackendStoreShardListIterator\__construct
__construct(FileBackendStore $backend, $container, $dir, array $suffixes, array $params)
Definition: FileBackendStore.php:1851
FileBackendStore\getContainerSuffixes
getContainerSuffixes( $container)
Get a list of full container shard suffixes for a container.
Definition: FileBackendStore.php:1508
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:1226
FileBackendStoreOpHandle
FileBackendStore helper class for performing asynchronous file operations.
Definition: FileBackendStore.php:1812
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:1527
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:1209
FileBackendStoreShardListIterator\$multiShardPaths
array $multiShardPaths
Definition: FileBackendStore.php:1842
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:1600
FileBackendStore\setFileCache
setFileCache( $path, array $val)
Set the cached stat info for a file path.
Definition: FileBackendStore.php:1660
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:1710
FileBackendStore\fileCacheKey
fileCacheKey( $path)
Get the cache key for a file path.
Definition: FileBackendStore.php:1648
$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:3377
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:1557
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:1838
FileBackendStoreOpHandle\$params
array $params
Definition: FileBackendStore.php:1813
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:1867
FileBackendStore\doGetFileStatMulti
doGetFileStatMulti(array $params)
Get file stat information (concurrently if possible) for several files.
Definition: FileBackendStore.php:1335
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:1817
FileBackendStore\setContainerCache
setContainerCache( $container, array $val)
Set the cached info for a container.
Definition: FileBackendStore.php:1577
FileBackendStoreShardListIterator\$directory
string $directory
Resolved relative path *.
Definition: FileBackendStore.php:1840
FileBackendStore\doQuickOperationsInternal
doQuickOperationsInternal(array $ops)
Definition: FileBackendStore.php:1118
FileBackendStoreOpHandle\closeResources
closeResources()
Close all open file handles.
Definition: FileBackendStore.php:1822
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:1900