MediaWiki  1.29.1
FileBackendStore.php
Go to the documentation of this file.
1 <?php
24 use Wikimedia\Timestamp\ConvertibleTimestamp;
25 
39 abstract class FileBackendStore extends FileBackend {
41  protected $memCache;
43  protected $srvCache;
45  protected $cheapCache;
47  protected $expensiveCache;
48 
50  protected $shardViaHashLevels = [];
51 
53  protected $mimeCallback;
54 
55  protected $maxFileSize = 4294967296; // integer bytes (4GiB)
56 
57  const CACHE_TTL = 10; // integer; TTL in seconds for process cache entries
58  const CACHE_CHEAP_SIZE = 500; // integer; max entries in "cheap cache"
59  const CACHE_EXPENSIVE_SIZE = 5; // integer; max entries in "expensive cache"
60 
72  public function __construct( array $config ) {
73  parent::__construct( $config );
74  $this->mimeCallback = isset( $config['mimeCallback'] )
75  ? $config['mimeCallback']
76  : null;
77  $this->srvCache = new EmptyBagOStuff(); // disabled by default
78  $this->memCache = WANObjectCache::newEmpty(); // disabled by default
79  $this->cheapCache = new ProcessCacheLRU( self::CACHE_CHEAP_SIZE );
80  $this->expensiveCache = new ProcessCacheLRU( self::CACHE_EXPENSIVE_SIZE );
81  }
82 
90  final public function maxFileSizeInternal() {
91  return $this->maxFileSize;
92  }
93 
103  abstract public function isPathUsableInternal( $storagePath );
104 
123  final public function createInternal( array $params ) {
124  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
125  if ( strlen( $params['content'] ) > $this->maxFileSizeInternal() ) {
126  $status = $this->newStatus( 'backend-fail-maxsize',
127  $params['dst'], $this->maxFileSizeInternal() );
128  } else {
129  $status = $this->doCreateInternal( $params );
130  $this->clearCache( [ $params['dst'] ] );
131  if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
132  $this->deleteFileCache( $params['dst'] ); // persistent cache
133  }
134  }
135 
136  return $status;
137  }
138 
144  abstract protected function doCreateInternal( array $params );
145 
164  final public function storeInternal( array $params ) {
165  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
166  if ( filesize( $params['src'] ) > $this->maxFileSizeInternal() ) {
167  $status = $this->newStatus( 'backend-fail-maxsize',
168  $params['dst'], $this->maxFileSizeInternal() );
169  } else {
170  $status = $this->doStoreInternal( $params );
171  $this->clearCache( [ $params['dst'] ] );
172  if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
173  $this->deleteFileCache( $params['dst'] ); // persistent cache
174  }
175  }
176 
177  return $status;
178  }
179 
185  abstract protected function doStoreInternal( array $params );
186 
206  final public function copyInternal( array $params ) {
207  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
208  $status = $this->doCopyInternal( $params );
209  $this->clearCache( [ $params['dst'] ] );
210  if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
211  $this->deleteFileCache( $params['dst'] ); // persistent cache
212  }
213 
214  return $status;
215  }
216 
222  abstract protected function doCopyInternal( array $params );
223 
238  final public function deleteInternal( array $params ) {
239  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
240  $status = $this->doDeleteInternal( $params );
241  $this->clearCache( [ $params['src'] ] );
242  $this->deleteFileCache( $params['src'] ); // persistent cache
243  return $status;
244  }
245 
251  abstract protected function doDeleteInternal( array $params );
252 
272  final public function moveInternal( array $params ) {
273  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
274  $status = $this->doMoveInternal( $params );
275  $this->clearCache( [ $params['src'], $params['dst'] ] );
276  $this->deleteFileCache( $params['src'] ); // persistent cache
277  if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
278  $this->deleteFileCache( $params['dst'] ); // persistent cache
279  }
280 
281  return $status;
282  }
283 
289  protected function doMoveInternal( array $params ) {
290  unset( $params['async'] ); // two steps, won't work here :)
291  $nsrc = FileBackend::normalizeStoragePath( $params['src'] );
292  $ndst = FileBackend::normalizeStoragePath( $params['dst'] );
293  // Copy source to dest
294  $status = $this->copyInternal( $params );
295  if ( $nsrc !== $ndst && $status->isOK() ) {
296  // Delete source (only fails due to races or network problems)
297  $status->merge( $this->deleteInternal( [ 'src' => $params['src'] ] ) );
298  $status->setResult( true, $status->value ); // ignore delete() errors
299  }
300 
301  return $status;
302  }
303 
318  final public function describeInternal( array $params ) {
319  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
320  if ( count( $params['headers'] ) ) {
321  $status = $this->doDescribeInternal( $params );
322  $this->clearCache( [ $params['src'] ] );
323  $this->deleteFileCache( $params['src'] ); // persistent cache
324  } else {
325  $status = $this->newStatus(); // nothing to do
326  }
327 
328  return $status;
329  }
330 
336  protected function doDescribeInternal( array $params ) {
337  return $this->newStatus();
338  }
339 
347  final public function nullInternal( array $params ) {
348  return $this->newStatus();
349  }
350 
351  final public function concatenate( array $params ) {
352  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
353  $status = $this->newStatus();
354 
355  // Try to lock the source files for the scope of this function
356  $scopeLockS = $this->getScopedFileLocks( $params['srcs'], LockManager::LOCK_UW, $status );
357  if ( $status->isOK() ) {
358  // Actually do the file concatenation...
359  $start_time = microtime( true );
360  $status->merge( $this->doConcatenate( $params ) );
361  $sec = microtime( true ) - $start_time;
362  if ( !$status->isOK() ) {
363  $this->logger->error( static::class . "-{$this->name}" .
364  " failed to concatenate " . count( $params['srcs'] ) . " file(s) [$sec sec]" );
365  }
366  }
367 
368  return $status;
369  }
370 
376  protected function doConcatenate( array $params ) {
377  $status = $this->newStatus();
378  $tmpPath = $params['dst']; // convenience
379  unset( $params['latest'] ); // sanity
380 
381  // Check that the specified temp file is valid...
382  MediaWiki\suppressWarnings();
383  $ok = ( is_file( $tmpPath ) && filesize( $tmpPath ) == 0 );
384  MediaWiki\restoreWarnings();
385  if ( !$ok ) { // not present or not empty
386  $status->fatal( 'backend-fail-opentemp', $tmpPath );
387 
388  return $status;
389  }
390 
391  // Get local FS versions of the chunks needed for the concatenation...
392  $fsFiles = $this->getLocalReferenceMulti( $params );
393  foreach ( $fsFiles as $path => &$fsFile ) {
394  if ( !$fsFile ) { // chunk failed to download?
395  $fsFile = $this->getLocalReference( [ 'src' => $path ] );
396  if ( !$fsFile ) { // retry failed?
397  $status->fatal( 'backend-fail-read', $path );
398 
399  return $status;
400  }
401  }
402  }
403  unset( $fsFile ); // unset reference so we can reuse $fsFile
404 
405  // Get a handle for the destination temp file
406  $tmpHandle = fopen( $tmpPath, 'ab' );
407  if ( $tmpHandle === false ) {
408  $status->fatal( 'backend-fail-opentemp', $tmpPath );
409 
410  return $status;
411  }
412 
413  // Build up the temp file using the source chunks (in order)...
414  foreach ( $fsFiles as $virtualSource => $fsFile ) {
415  // Get a handle to the local FS version
416  $sourceHandle = fopen( $fsFile->getPath(), 'rb' );
417  if ( $sourceHandle === false ) {
418  fclose( $tmpHandle );
419  $status->fatal( 'backend-fail-read', $virtualSource );
420 
421  return $status;
422  }
423  // Append chunk to file (pass chunk size to avoid magic quotes)
424  if ( !stream_copy_to_stream( $sourceHandle, $tmpHandle ) ) {
425  fclose( $sourceHandle );
426  fclose( $tmpHandle );
427  $status->fatal( 'backend-fail-writetemp', $tmpPath );
428 
429  return $status;
430  }
431  fclose( $sourceHandle );
432  }
433  if ( !fclose( $tmpHandle ) ) {
434  $status->fatal( 'backend-fail-closetemp', $tmpPath );
435 
436  return $status;
437  }
438 
439  clearstatcache(); // temp file changed
440 
441  return $status;
442  }
443 
444  final protected function doPrepare( array $params ) {
445  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
446  $status = $this->newStatus();
447 
448  list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
449  if ( $dir === null ) {
450  $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
451 
452  return $status; // invalid storage path
453  }
454 
455  if ( $shard !== null ) { // confined to a single container/shard
456  $status->merge( $this->doPrepareInternal( $fullCont, $dir, $params ) );
457  } else { // directory is on several shards
458  $this->logger->debug( __METHOD__ . ": iterating over all container shards.\n" );
459  list( , $shortCont, ) = self::splitStoragePath( $params['dir'] );
460  foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
461  $status->merge( $this->doPrepareInternal( "{$fullCont}{$suffix}", $dir, $params ) );
462  }
463  }
464 
465  return $status;
466  }
467 
475  protected function doPrepareInternal( $container, $dir, array $params ) {
476  return $this->newStatus();
477  }
478 
479  final protected function doSecure( array $params ) {
480  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
481  $status = $this->newStatus();
482 
483  list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
484  if ( $dir === null ) {
485  $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
486 
487  return $status; // invalid storage path
488  }
489 
490  if ( $shard !== null ) { // confined to a single container/shard
491  $status->merge( $this->doSecureInternal( $fullCont, $dir, $params ) );
492  } else { // directory is on several shards
493  $this->logger->debug( __METHOD__ . ": iterating over all container shards.\n" );
494  list( , $shortCont, ) = self::splitStoragePath( $params['dir'] );
495  foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
496  $status->merge( $this->doSecureInternal( "{$fullCont}{$suffix}", $dir, $params ) );
497  }
498  }
499 
500  return $status;
501  }
502 
510  protected function doSecureInternal( $container, $dir, array $params ) {
511  return $this->newStatus();
512  }
513 
514  final protected function doPublish( array $params ) {
515  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
516  $status = $this->newStatus();
517 
518  list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
519  if ( $dir === null ) {
520  $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
521 
522  return $status; // invalid storage path
523  }
524 
525  if ( $shard !== null ) { // confined to a single container/shard
526  $status->merge( $this->doPublishInternal( $fullCont, $dir, $params ) );
527  } else { // directory is on several shards
528  $this->logger->debug( __METHOD__ . ": iterating over all container shards.\n" );
529  list( , $shortCont, ) = self::splitStoragePath( $params['dir'] );
530  foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
531  $status->merge( $this->doPublishInternal( "{$fullCont}{$suffix}", $dir, $params ) );
532  }
533  }
534 
535  return $status;
536  }
537 
545  protected function doPublishInternal( $container, $dir, array $params ) {
546  return $this->newStatus();
547  }
548 
549  final protected function doClean( array $params ) {
550  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
551  $status = $this->newStatus();
552 
553  // Recursive: first delete all empty subdirs recursively
554  if ( !empty( $params['recursive'] ) && !$this->directoriesAreVirtual() ) {
555  $subDirsRel = $this->getTopDirectoryList( [ 'dir' => $params['dir'] ] );
556  if ( $subDirsRel !== null ) { // no errors
557  foreach ( $subDirsRel as $subDirRel ) {
558  $subDir = $params['dir'] . "/{$subDirRel}"; // full path
559  $status->merge( $this->doClean( [ 'dir' => $subDir ] + $params ) );
560  }
561  unset( $subDirsRel ); // free directory for rmdir() on Windows (for FS backends)
562  }
563  }
564 
565  list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
566  if ( $dir === null ) {
567  $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
568 
569  return $status; // invalid storage path
570  }
571 
572  // Attempt to lock this directory...
573  $filesLockEx = [ $params['dir'] ];
574  $scopedLockE = $this->getScopedFileLocks( $filesLockEx, LockManager::LOCK_EX, $status );
575  if ( !$status->isOK() ) {
576  return $status; // abort
577  }
578 
579  if ( $shard !== null ) { // confined to a single container/shard
580  $status->merge( $this->doCleanInternal( $fullCont, $dir, $params ) );
581  $this->deleteContainerCache( $fullCont ); // purge cache
582  } else { // directory is on several shards
583  $this->logger->debug( __METHOD__ . ": iterating over all container shards.\n" );
584  list( , $shortCont, ) = self::splitStoragePath( $params['dir'] );
585  foreach ( $this->getContainerSuffixes( $shortCont ) as $suffix ) {
586  $status->merge( $this->doCleanInternal( "{$fullCont}{$suffix}", $dir, $params ) );
587  $this->deleteContainerCache( "{$fullCont}{$suffix}" ); // purge cache
588  }
589  }
590 
591  return $status;
592  }
593 
601  protected function doCleanInternal( $container, $dir, array $params ) {
602  return $this->newStatus();
603  }
604 
605  final public function fileExists( array $params ) {
606  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
607  $stat = $this->getFileStat( $params );
608 
609  return ( $stat === null ) ? null : (bool)$stat; // null => failure
610  }
611 
612  final public function getFileTimestamp( array $params ) {
613  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
614  $stat = $this->getFileStat( $params );
615 
616  return $stat ? $stat['mtime'] : false;
617  }
618 
619  final public function getFileSize( array $params ) {
620  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
621  $stat = $this->getFileStat( $params );
622 
623  return $stat ? $stat['size'] : false;
624  }
625 
626  final public function getFileStat( array $params ) {
628  if ( $path === null ) {
629  return false; // invalid storage path
630  }
631  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
632  $latest = !empty( $params['latest'] ); // use latest data?
633  if ( !$latest && !$this->cheapCache->has( $path, 'stat', self::CACHE_TTL ) ) {
634  $this->primeFileCache( [ $path ] ); // check persistent cache
635  }
636  if ( $this->cheapCache->has( $path, 'stat', self::CACHE_TTL ) ) {
637  $stat = $this->cheapCache->get( $path, 'stat' );
638  // If we want the latest data, check that this cached
639  // value was in fact fetched with the latest available data.
640  if ( is_array( $stat ) ) {
641  if ( !$latest || $stat['latest'] ) {
642  return $stat;
643  }
644  } elseif ( in_array( $stat, [ 'NOT_EXIST', 'NOT_EXIST_LATEST' ] ) ) {
645  if ( !$latest || $stat === 'NOT_EXIST_LATEST' ) {
646  return false;
647  }
648  }
649  }
650  $stat = $this->doGetFileStat( $params );
651  if ( is_array( $stat ) ) { // file exists
652  // Strongly consistent backends can automatically set "latest"
653  $stat['latest'] = isset( $stat['latest'] ) ? $stat['latest'] : $latest;
654  $this->cheapCache->set( $path, 'stat', $stat );
655  $this->setFileCache( $path, $stat ); // update persistent cache
656  if ( isset( $stat['sha1'] ) ) { // some backends store SHA-1 as metadata
657  $this->cheapCache->set( $path, 'sha1',
658  [ 'hash' => $stat['sha1'], 'latest' => $latest ] );
659  }
660  if ( isset( $stat['xattr'] ) ) { // some backends store headers/metadata
661  $stat['xattr'] = self::normalizeXAttributes( $stat['xattr'] );
662  $this->cheapCache->set( $path, 'xattr',
663  [ 'map' => $stat['xattr'], 'latest' => $latest ] );
664  }
665  } elseif ( $stat === false ) { // file does not exist
666  $this->cheapCache->set( $path, 'stat', $latest ? 'NOT_EXIST_LATEST' : 'NOT_EXIST' );
667  $this->cheapCache->set( $path, 'xattr', [ 'map' => false, 'latest' => $latest ] );
668  $this->cheapCache->set( $path, 'sha1', [ 'hash' => false, 'latest' => $latest ] );
669  $this->logger->debug( __METHOD__ . ": File $path does not exist.\n" );
670  } else { // an error occurred
671  $this->logger->warning( __METHOD__ . ": Could not stat file $path.\n" );
672  }
673 
674  return $stat;
675  }
676 
681  abstract protected function doGetFileStat( array $params );
682 
683  public function getFileContentsMulti( array $params ) {
684  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
685 
686  $params = $this->setConcurrencyFlags( $params );
687  $contents = $this->doGetFileContentsMulti( $params );
688 
689  return $contents;
690  }
691 
697  protected function doGetFileContentsMulti( array $params ) {
698  $contents = [];
699  foreach ( $this->doGetLocalReferenceMulti( $params ) as $path => $fsFile ) {
700  MediaWiki\suppressWarnings();
701  $contents[$path] = $fsFile ? file_get_contents( $fsFile->getPath() ) : false;
702  MediaWiki\restoreWarnings();
703  }
704 
705  return $contents;
706  }
707 
708  final public function getFileXAttributes( array $params ) {
710  if ( $path === null ) {
711  return false; // invalid storage path
712  }
713  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
714  $latest = !empty( $params['latest'] ); // use latest data?
715  if ( $this->cheapCache->has( $path, 'xattr', self::CACHE_TTL ) ) {
716  $stat = $this->cheapCache->get( $path, 'xattr' );
717  // If we want the latest data, check that this cached
718  // value was in fact fetched with the latest available data.
719  if ( !$latest || $stat['latest'] ) {
720  return $stat['map'];
721  }
722  }
723  $fields = $this->doGetFileXAttributes( $params );
724  $fields = is_array( $fields ) ? self::normalizeXAttributes( $fields ) : false;
725  $this->cheapCache->set( $path, 'xattr', [ 'map' => $fields, 'latest' => $latest ] );
726 
727  return $fields;
728  }
729 
735  protected function doGetFileXAttributes( array $params ) {
736  return [ 'headers' => [], 'metadata' => [] ]; // not supported
737  }
738 
739  final public function getFileSha1Base36( array $params ) {
741  if ( $path === null ) {
742  return false; // invalid storage path
743  }
744  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
745  $latest = !empty( $params['latest'] ); // use latest data?
746  if ( $this->cheapCache->has( $path, 'sha1', self::CACHE_TTL ) ) {
747  $stat = $this->cheapCache->get( $path, 'sha1' );
748  // If we want the latest data, check that this cached
749  // value was in fact fetched with the latest available data.
750  if ( !$latest || $stat['latest'] ) {
751  return $stat['hash'];
752  }
753  }
754  $hash = $this->doGetFileSha1Base36( $params );
755  $this->cheapCache->set( $path, 'sha1', [ 'hash' => $hash, 'latest' => $latest ] );
756 
757  return $hash;
758  }
759 
765  protected function doGetFileSha1Base36( array $params ) {
766  $fsFile = $this->getLocalReference( $params );
767  if ( !$fsFile ) {
768  return false;
769  } else {
770  return $fsFile->getSha1Base36();
771  }
772  }
773 
774  final public function getFileProps( array $params ) {
775  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
776  $fsFile = $this->getLocalReference( $params );
777  $props = $fsFile ? $fsFile->getProps() : FSFile::placeholderProps();
778 
779  return $props;
780  }
781 
782  final public function getLocalReferenceMulti( array $params ) {
783  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
784 
785  $params = $this->setConcurrencyFlags( $params );
786 
787  $fsFiles = []; // (path => FSFile)
788  $latest = !empty( $params['latest'] ); // use latest data?
789  // Reuse any files already in process cache...
790  foreach ( $params['srcs'] as $src ) {
792  if ( $path === null ) {
793  $fsFiles[$src] = null; // invalid storage path
794  } elseif ( $this->expensiveCache->has( $path, 'localRef' ) ) {
795  $val = $this->expensiveCache->get( $path, 'localRef' );
796  // If we want the latest data, check that this cached
797  // value was in fact fetched with the latest available data.
798  if ( !$latest || $val['latest'] ) {
799  $fsFiles[$src] = $val['object'];
800  }
801  }
802  }
803  // Fetch local references of any remaning files...
804  $params['srcs'] = array_diff( $params['srcs'], array_keys( $fsFiles ) );
805  foreach ( $this->doGetLocalReferenceMulti( $params ) as $path => $fsFile ) {
806  $fsFiles[$path] = $fsFile;
807  if ( $fsFile ) { // update the process cache...
808  $this->expensiveCache->set( $path, 'localRef',
809  [ 'object' => $fsFile, 'latest' => $latest ] );
810  }
811  }
812 
813  return $fsFiles;
814  }
815 
821  protected function doGetLocalReferenceMulti( array $params ) {
822  return $this->doGetLocalCopyMulti( $params );
823  }
824 
825  final public function getLocalCopyMulti( array $params ) {
826  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
827 
828  $params = $this->setConcurrencyFlags( $params );
829  $tmpFiles = $this->doGetLocalCopyMulti( $params );
830 
831  return $tmpFiles;
832  }
833 
839  abstract protected function doGetLocalCopyMulti( array $params );
840 
846  public function getFileHttpUrl( array $params ) {
847  return null; // not supported
848  }
849 
850  final public function streamFile( array $params ) {
851  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
852  $status = $this->newStatus();
853 
854  // Always set some fields for subclass convenience
855  $params['options'] = isset( $params['options'] ) ? $params['options'] : [];
856  $params['headers'] = isset( $params['headers'] ) ? $params['headers'] : [];
857 
858  // Don't stream it out as text/html if there was a PHP error
859  if ( ( empty( $params['headless'] ) || $params['headers'] ) && headers_sent() ) {
860  print "Headers already sent, terminating.\n";
861  $status->fatal( 'backend-fail-stream', $params['src'] );
862  return $status;
863  }
864 
865  $status->merge( $this->doStreamFile( $params ) );
866 
867  return $status;
868  }
869 
875  protected function doStreamFile( array $params ) {
876  $status = $this->newStatus();
877 
878  $flags = 0;
879  $flags |= !empty( $params['headless'] ) ? HTTPFileStreamer::STREAM_HEADLESS : 0;
880  $flags |= !empty( $params['allowOB'] ) ? HTTPFileStreamer::STREAM_ALLOW_OB : 0;
881 
882  $fsFile = $this->getLocalReference( $params );
883  if ( $fsFile ) {
884  $streamer = new HTTPFileStreamer(
885  $fsFile->getPath(),
886  [
887  'obResetFunc' => $this->obResetFunc,
888  'streamMimeFunc' => $this->streamMimeFunc
889  ]
890  );
891  $res = $streamer->stream( $params['headers'], true, $params['options'], $flags );
892  } else {
893  $res = false;
895  }
896 
897  if ( !$res ) {
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  $this->logger->debug( __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  $this->logger->debug( __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  $this->logger->debug( __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 = [
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 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, $this->logger );
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 = [ 'sh' => [], 'ex' => [] ];
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 [
1062  LockManager::LOCK_UW => $paths['sh'],
1063  LockManager::LOCK_EX => $paths['ex']
1064  ];
1065  }
1066 
1067  public function getScopedLocksForOps( array $ops, StatusValue $status ) {
1068  $paths = $this->getPathsToLockForOpsInternal( $this->getOperationsInternal( $ops ) );
1069 
1070  return $this->getScopedFileLocks( $paths, 'mixed', $status );
1071  }
1072 
1073  final protected function doOperationsInternal( array $ops, array $opts ) {
1074  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
1075  $status = $this->newStatus();
1076 
1077  // Fix up custom header name/value pairs...
1078  $ops = array_map( [ $this, 'sanitizeOpHeaders' ], $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 
1089  $scopeLock = $this->getScopedFileLocks( $paths, 'mixed', $status );
1090  if ( !$status->isOK() ) {
1091  return $status; // abort
1092  }
1093  }
1094 
1095  // Clear any file cache entries (after locks acquired)
1096  if ( empty( $opts['preserveCache'] ) ) {
1097  $this->clearCache();
1098  }
1099 
1100  // Build the list of paths involved
1101  $paths = [];
1102  foreach ( $performOps as $performOp ) {
1103  $paths = array_merge( $paths, $performOp->storagePathsRead() );
1104  $paths = array_merge( $paths, $performOp->storagePathsChanged() );
1105  }
1106 
1107  // Enlarge the cache to fit the stat entries of these files
1108  $this->cheapCache->resize( max( 2 * count( $paths ), self::CACHE_CHEAP_SIZE ) );
1109 
1110  // Load from the persistent container caches
1111  $this->primeContainerCache( $paths );
1112  // Get the latest stat info for all the files (having locked them)
1113  $ok = $this->preloadFileStat( [ 'srcs' => $paths, 'latest' => true ] );
1114 
1115  if ( $ok ) {
1116  // Actually attempt the operation batch...
1117  $opts = $this->setConcurrencyFlags( $opts );
1118  $subStatus = FileOpBatch::attempt( $performOps, $opts, $this->fileJournal );
1119  } else {
1120  // If we could not even stat some files, then bail out...
1121  $subStatus = $this->newStatus( 'backend-fail-internal', $this->name );
1122  foreach ( $ops as $i => $op ) { // mark each op as failed
1123  $subStatus->success[$i] = false;
1124  ++$subStatus->failCount;
1125  }
1126  $this->logger->error( static::class . "-{$this->name} " .
1127  " stat failure; aborted operations: " . FormatJson::encode( $ops ) );
1128  }
1129 
1130  // Merge errors into StatusValue fields
1131  $status->merge( $subStatus );
1132  $status->success = $subStatus->success; // not done in merge()
1133 
1134  // Shrink the stat cache back to normal size
1135  $this->cheapCache->resize( self::CACHE_CHEAP_SIZE );
1136 
1137  return $status;
1138  }
1139 
1140  final protected function doQuickOperationsInternal( array $ops ) {
1141  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
1142  $status = $this->newStatus();
1143 
1144  // Fix up custom header name/value pairs...
1145  $ops = array_map( [ $this, 'sanitizeOpHeaders' ], $ops );
1146 
1147  // Clear any file cache entries
1148  $this->clearCache();
1149 
1150  $supportedOps = [ 'create', 'store', 'copy', 'move', 'delete', 'describe', 'null' ];
1151  // Parallel ops may be disabled in config due to dependencies (e.g. needing popen())
1152  $async = ( $this->parallelize === 'implicit' && count( $ops ) > 1 );
1153  $maxConcurrency = $this->concurrency; // throttle
1155  $statuses = []; // array of (index => StatusValue)
1156  $fileOpHandles = []; // list of (index => handle) arrays
1157  $curFileOpHandles = []; // current handle batch
1158  // Perform the sync-only ops and build up op handles for the async ops...
1159  foreach ( $ops as $index => $params ) {
1160  if ( !in_array( $params['op'], $supportedOps ) ) {
1161  throw new FileBackendError( "Operation '{$params['op']}' is not supported." );
1162  }
1163  $method = $params['op'] . 'Internal'; // e.g. "storeInternal"
1164  $subStatus = $this->$method( [ 'async' => $async ] + $params );
1165  if ( $subStatus->value instanceof FileBackendStoreOpHandle ) { // async
1166  if ( count( $curFileOpHandles ) >= $maxConcurrency ) {
1167  $fileOpHandles[] = $curFileOpHandles; // push this batch
1168  $curFileOpHandles = [];
1169  }
1170  $curFileOpHandles[$index] = $subStatus->value; // keep index
1171  } else { // error or completed
1172  $statuses[$index] = $subStatus; // keep index
1173  }
1174  }
1175  if ( count( $curFileOpHandles ) ) {
1176  $fileOpHandles[] = $curFileOpHandles; // last batch
1177  }
1178  // Do all the async ops that can be done concurrently...
1179  foreach ( $fileOpHandles as $fileHandleBatch ) {
1180  $statuses = $statuses + $this->executeOpHandlesInternal( $fileHandleBatch );
1181  }
1182  // Marshall and merge all the responses...
1183  foreach ( $statuses as $index => $subStatus ) {
1184  $status->merge( $subStatus );
1185  if ( $subStatus->isOK() ) {
1186  $status->success[$index] = true;
1187  ++$status->successCount;
1188  } else {
1189  $status->success[$index] = false;
1190  ++$status->failCount;
1191  }
1192  }
1193 
1194  return $status;
1195  }
1196 
1206  final public function executeOpHandlesInternal( array $fileOpHandles ) {
1207  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
1208 
1209  foreach ( $fileOpHandles as $fileOpHandle ) {
1210  if ( !( $fileOpHandle instanceof FileBackendStoreOpHandle ) ) {
1211  throw new InvalidArgumentException( "Expected FileBackendStoreOpHandle object." );
1212  } elseif ( $fileOpHandle->backend->getName() !== $this->getName() ) {
1213  throw new InvalidArgumentException( "Expected handle for this file backend." );
1214  }
1215  }
1216 
1217  $res = $this->doExecuteOpHandlesInternal( $fileOpHandles );
1218  foreach ( $fileOpHandles as $fileOpHandle ) {
1219  $fileOpHandle->closeResources();
1220  }
1221 
1222  return $res;
1223  }
1224 
1233  protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
1234  if ( count( $fileOpHandles ) ) {
1235  throw new LogicException( "Backend does not support asynchronous operations." );
1236  }
1237 
1238  return [];
1239  }
1240 
1252  protected function sanitizeOpHeaders( array $op ) {
1253  static $longs = [ 'content-disposition' ];
1254 
1255  if ( isset( $op['headers'] ) ) { // op sets HTTP headers
1256  $newHeaders = [];
1257  foreach ( $op['headers'] as $name => $value ) {
1258  $name = strtolower( $name );
1259  $maxHVLen = in_array( $name, $longs ) ? INF : 255;
1260  if ( strlen( $name ) > 255 || strlen( $value ) > $maxHVLen ) {
1261  trigger_error( "Header '$name: $value' is too long." );
1262  } else {
1263  $newHeaders[$name] = strlen( $value ) ? $value : ''; // null/false => ""
1264  }
1265  }
1266  $op['headers'] = $newHeaders;
1267  }
1268 
1269  return $op;
1270  }
1271 
1272  final public function preloadCache( array $paths ) {
1273  $fullConts = []; // full container names
1274  foreach ( $paths as $path ) {
1275  list( $fullCont, , ) = $this->resolveStoragePath( $path );
1276  $fullConts[] = $fullCont;
1277  }
1278  // Load from the persistent file and container caches
1279  $this->primeContainerCache( $fullConts );
1280  $this->primeFileCache( $paths );
1281  }
1282 
1283  final public function clearCache( array $paths = null ) {
1284  if ( is_array( $paths ) ) {
1285  $paths = array_map( 'FileBackend::normalizeStoragePath', $paths );
1286  $paths = array_filter( $paths, 'strlen' ); // remove nulls
1287  }
1288  if ( $paths === null ) {
1289  $this->cheapCache->clear();
1290  $this->expensiveCache->clear();
1291  } else {
1292  foreach ( $paths as $path ) {
1293  $this->cheapCache->clear( $path );
1294  $this->expensiveCache->clear( $path );
1295  }
1296  }
1297  $this->doClearCache( $paths );
1298  }
1299 
1307  protected function doClearCache( array $paths = null ) {
1308  }
1309 
1310  final public function preloadFileStat( array $params ) {
1311  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
1312  $success = true; // no network errors
1313 
1314  $params['concurrency'] = ( $this->parallelize !== 'off' ) ? $this->concurrency : 1;
1315  $stats = $this->doGetFileStatMulti( $params );
1316  if ( $stats === null ) {
1317  return true; // not supported
1318  }
1319 
1320  $latest = !empty( $params['latest'] ); // use latest data?
1321  foreach ( $stats as $path => $stat ) {
1323  if ( $path === null ) {
1324  continue; // this shouldn't happen
1325  }
1326  if ( is_array( $stat ) ) { // file exists
1327  // Strongly consistent backends can automatically set "latest"
1328  $stat['latest'] = isset( $stat['latest'] ) ? $stat['latest'] : $latest;
1329  $this->cheapCache->set( $path, 'stat', $stat );
1330  $this->setFileCache( $path, $stat ); // update persistent cache
1331  if ( isset( $stat['sha1'] ) ) { // some backends store SHA-1 as metadata
1332  $this->cheapCache->set( $path, 'sha1',
1333  [ 'hash' => $stat['sha1'], 'latest' => $latest ] );
1334  }
1335  if ( isset( $stat['xattr'] ) ) { // some backends store headers/metadata
1336  $stat['xattr'] = self::normalizeXAttributes( $stat['xattr'] );
1337  $this->cheapCache->set( $path, 'xattr',
1338  [ 'map' => $stat['xattr'], 'latest' => $latest ] );
1339  }
1340  } elseif ( $stat === false ) { // file does not exist
1341  $this->cheapCache->set( $path, 'stat',
1342  $latest ? 'NOT_EXIST_LATEST' : 'NOT_EXIST' );
1343  $this->cheapCache->set( $path, 'xattr',
1344  [ 'map' => false, 'latest' => $latest ] );
1345  $this->cheapCache->set( $path, 'sha1',
1346  [ 'hash' => false, 'latest' => $latest ] );
1347  $this->logger->debug( __METHOD__ . ": File $path does not exist.\n" );
1348  } else { // an error occurred
1349  $success = false;
1350  $this->logger->warning( __METHOD__ . ": Could not stat file $path.\n" );
1351  }
1352  }
1353 
1354  return $success;
1355  }
1356 
1368  protected function doGetFileStatMulti( array $params ) {
1369  return null; // not supported
1370  }
1371 
1379  abstract protected function directoriesAreVirtual();
1380 
1391  final protected static function isValidShortContainerName( $container ) {
1392  // Suffixes like '.xxx' (hex shard chars) or '.seg' (file segments)
1393  // might be used by subclasses. Reserve the dot character for sanity.
1394  // The only way dots end up in containers (e.g. resolveStoragePath)
1395  // is due to the wikiId container prefix or the above suffixes.
1396  return self::isValidContainerName( $container ) && !preg_match( '/[.]/', $container );
1397  }
1398 
1408  final protected static function isValidContainerName( $container ) {
1409  // This accounts for NTFS, Swift, and Ceph restrictions
1410  // and disallows directory separators or traversal characters.
1411  // Note that matching strings URL encode to the same string;
1412  // in Swift/Ceph, the length restriction is *after* URL encoding.
1413  return (bool)preg_match( '/^[a-z0-9][a-z0-9-_.]{0,199}$/i', $container );
1414  }
1415 
1429  final protected function resolveStoragePath( $storagePath ) {
1430  list( $backend, $shortCont, $relPath ) = self::splitStoragePath( $storagePath );
1431  if ( $backend === $this->name ) { // must be for this backend
1432  $relPath = self::normalizeContainerPath( $relPath );
1433  if ( $relPath !== null && self::isValidShortContainerName( $shortCont ) ) {
1434  // Get shard for the normalized path if this container is sharded
1435  $cShard = $this->getContainerShard( $shortCont, $relPath );
1436  // Validate and sanitize the relative path (backend-specific)
1437  $relPath = $this->resolveContainerPath( $shortCont, $relPath );
1438  if ( $relPath !== null ) {
1439  // Prepend any wiki ID prefix to the container name
1440  $container = $this->fullContainerName( $shortCont );
1441  if ( self::isValidContainerName( $container ) ) {
1442  // Validate and sanitize the container name (backend-specific)
1443  $container = $this->resolveContainerName( "{$container}{$cShard}" );
1444  if ( $container !== null ) {
1445  return [ $container, $relPath, $cShard ];
1446  }
1447  }
1448  }
1449  }
1450  }
1451 
1452  return [ null, null, null ];
1453  }
1454 
1470  final protected function resolveStoragePathReal( $storagePath ) {
1471  list( $container, $relPath, $cShard ) = $this->resolveStoragePath( $storagePath );
1472  if ( $cShard !== null && substr( $relPath, -1 ) !== '/' ) {
1473  return [ $container, $relPath ];
1474  }
1475 
1476  return [ null, null ];
1477  }
1478 
1487  final protected function getContainerShard( $container, $relPath ) {
1488  list( $levels, $base, $repeat ) = $this->getContainerHashLevels( $container );
1489  if ( $levels == 1 || $levels == 2 ) {
1490  // Hash characters are either base 16 or 36
1491  $char = ( $base == 36 ) ? '[0-9a-z]' : '[0-9a-f]';
1492  // Get a regex that represents the shard portion of paths.
1493  // The concatenation of the captures gives us the shard.
1494  if ( $levels === 1 ) { // 16 or 36 shards per container
1495  $hashDirRegex = '(' . $char . ')';
1496  } else { // 256 or 1296 shards per container
1497  if ( $repeat ) { // verbose hash dir format (e.g. "a/ab/abc")
1498  $hashDirRegex = $char . '/(' . $char . '{2})';
1499  } else { // short hash dir format (e.g. "a/b/c")
1500  $hashDirRegex = '(' . $char . ')/(' . $char . ')';
1501  }
1502  }
1503  // Allow certain directories to be above the hash dirs so as
1504  // to work with FileRepo (e.g. "archive/a/ab" or "temp/a/ab").
1505  // They must be 2+ chars to avoid any hash directory ambiguity.
1506  $m = [];
1507  if ( preg_match( "!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) {
1508  return '.' . implode( '', array_slice( $m, 1 ) );
1509  }
1510 
1511  return null; // failed to match
1512  }
1513 
1514  return ''; // no sharding
1515  }
1516 
1525  final public function isSingleShardPathInternal( $storagePath ) {
1526  list( , , $shard ) = $this->resolveStoragePath( $storagePath );
1527 
1528  return ( $shard !== null );
1529  }
1530 
1539  final protected function getContainerHashLevels( $container ) {
1540  if ( isset( $this->shardViaHashLevels[$container] ) ) {
1541  $config = $this->shardViaHashLevels[$container];
1542  $hashLevels = (int)$config['levels'];
1543  if ( $hashLevels == 1 || $hashLevels == 2 ) {
1544  $hashBase = (int)$config['base'];
1545  if ( $hashBase == 16 || $hashBase == 36 ) {
1546  return [ $hashLevels, $hashBase, $config['repeat'] ];
1547  }
1548  }
1549  }
1550 
1551  return [ 0, 0, false ]; // no sharding
1552  }
1553 
1560  final protected function getContainerSuffixes( $container ) {
1561  $shards = [];
1562  list( $digits, $base ) = $this->getContainerHashLevels( $container );
1563  if ( $digits > 0 ) {
1564  $numShards = pow( $base, $digits );
1565  for ( $index = 0; $index < $numShards; $index++ ) {
1566  $shards[] = '.' . Wikimedia\base_convert( $index, 10, $base, $digits );
1567  }
1568  }
1569 
1570  return $shards;
1571  }
1572 
1579  final protected function fullContainerName( $container ) {
1580  if ( $this->domainId != '' ) {
1581  return "{$this->domainId}-$container";
1582  } else {
1583  return $container;
1584  }
1585  }
1586 
1595  protected function resolveContainerName( $container ) {
1596  return $container;
1597  }
1598 
1609  protected function resolveContainerPath( $container, $relStoragePath ) {
1610  return $relStoragePath;
1611  }
1612 
1619  private function containerCacheKey( $container ) {
1620  return "filebackend:{$this->name}:{$this->domainId}:container:{$container}";
1621  }
1622 
1629  final protected function setContainerCache( $container, array $val ) {
1630  $this->memCache->set( $this->containerCacheKey( $container ), $val, 14 * 86400 );
1631  }
1632 
1639  final protected function deleteContainerCache( $container ) {
1640  if ( !$this->memCache->delete( $this->containerCacheKey( $container ), 300 ) ) {
1641  trigger_error( "Unable to delete stat cache for container $container." );
1642  }
1643  }
1644 
1652  final protected function primeContainerCache( array $items ) {
1653  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
1654 
1655  $paths = []; // list of storage paths
1656  $contNames = []; // (cache key => resolved container name)
1657  // Get all the paths/containers from the items...
1658  foreach ( $items as $item ) {
1659  if ( self::isStoragePath( $item ) ) {
1660  $paths[] = $item;
1661  } elseif ( is_string( $item ) ) { // full container name
1662  $contNames[$this->containerCacheKey( $item )] = $item;
1663  }
1664  }
1665  // Get all the corresponding cache keys for paths...
1666  foreach ( $paths as $path ) {
1667  list( $fullCont, , ) = $this->resolveStoragePath( $path );
1668  if ( $fullCont !== null ) { // valid path for this backend
1669  $contNames[$this->containerCacheKey( $fullCont )] = $fullCont;
1670  }
1671  }
1672 
1673  $contInfo = []; // (resolved container name => cache value)
1674  // Get all cache entries for these container cache keys...
1675  $values = $this->memCache->getMulti( array_keys( $contNames ) );
1676  foreach ( $values as $cacheKey => $val ) {
1677  $contInfo[$contNames[$cacheKey]] = $val;
1678  }
1679 
1680  // Populate the container process cache for the backend...
1681  $this->doPrimeContainerCache( array_filter( $contInfo, 'is_array' ) );
1682  }
1683 
1691  protected function doPrimeContainerCache( array $containerInfo ) {
1692  }
1693 
1700  private function fileCacheKey( $path ) {
1701  return "filebackend:{$this->name}:{$this->domainId}:file:" . sha1( $path );
1702  }
1703 
1712  final protected function setFileCache( $path, array $val ) {
1714  if ( $path === null ) {
1715  return; // invalid storage path
1716  }
1717  $mtime = ConvertibleTimestamp::convert( TS_UNIX, $val['mtime'] );
1718  $ttl = $this->memCache->adaptiveTTL( $mtime, 7 * 86400, 300, .1 );
1719  $key = $this->fileCacheKey( $path );
1720  // Set the cache unless it is currently salted.
1721  $this->memCache->set( $key, $val, $ttl );
1722  }
1723 
1732  final protected function deleteFileCache( $path ) {
1734  if ( $path === null ) {
1735  return; // invalid storage path
1736  }
1737  if ( !$this->memCache->delete( $this->fileCacheKey( $path ), 300 ) ) {
1738  trigger_error( "Unable to delete stat cache for file $path." );
1739  }
1740  }
1741 
1749  final protected function primeFileCache( array $items ) {
1750  $ps = $this->scopedProfileSection( __METHOD__ . "-{$this->name}" );
1751 
1752  $paths = []; // list of storage paths
1753  $pathNames = []; // (cache key => storage path)
1754  // Get all the paths/containers from the items...
1755  foreach ( $items as $item ) {
1756  if ( self::isStoragePath( $item ) ) {
1757  $paths[] = FileBackend::normalizeStoragePath( $item );
1758  }
1759  }
1760  // Get rid of any paths that failed normalization...
1761  $paths = array_filter( $paths, 'strlen' ); // remove nulls
1762  // Get all the corresponding cache keys for paths...
1763  foreach ( $paths as $path ) {
1764  list( , $rel, ) = $this->resolveStoragePath( $path );
1765  if ( $rel !== null ) { // valid path for this backend
1766  $pathNames[$this->fileCacheKey( $path )] = $path;
1767  }
1768  }
1769  // Get all cache entries for these file cache keys...
1770  $values = $this->memCache->getMulti( array_keys( $pathNames ) );
1771  foreach ( $values as $cacheKey => $val ) {
1772  $path = $pathNames[$cacheKey];
1773  if ( is_array( $val ) ) {
1774  $val['latest'] = false; // never completely trust cache
1775  $this->cheapCache->set( $path, 'stat', $val );
1776  if ( isset( $val['sha1'] ) ) { // some backends store SHA-1 as metadata
1777  $this->cheapCache->set( $path, 'sha1',
1778  [ 'hash' => $val['sha1'], 'latest' => false ] );
1779  }
1780  if ( isset( $val['xattr'] ) ) { // some backends store headers/metadata
1781  $val['xattr'] = self::normalizeXAttributes( $val['xattr'] );
1782  $this->cheapCache->set( $path, 'xattr',
1783  [ 'map' => $val['xattr'], 'latest' => false ] );
1784  }
1785  }
1786  }
1787  }
1788 
1796  final protected static function normalizeXAttributes( array $xattr ) {
1797  $newXAttr = [ 'headers' => [], 'metadata' => [] ];
1798 
1799  foreach ( $xattr['headers'] as $name => $value ) {
1800  $newXAttr['headers'][strtolower( $name )] = $value;
1801  }
1802 
1803  foreach ( $xattr['metadata'] as $name => $value ) {
1804  $newXAttr['metadata'][strtolower( $name )] = $value;
1805  }
1806 
1807  return $newXAttr;
1808  }
1809 
1816  final protected function setConcurrencyFlags( array $opts ) {
1817  $opts['concurrency'] = 1; // off
1818  if ( $this->parallelize === 'implicit' ) {
1819  if ( !isset( $opts['parallelize'] ) || $opts['parallelize'] ) {
1820  $opts['concurrency'] = $this->concurrency;
1821  }
1822  } elseif ( $this->parallelize === 'explicit' ) {
1823  if ( !empty( $opts['parallelize'] ) ) {
1824  $opts['concurrency'] = $this->concurrency;
1825  }
1826  }
1827 
1828  return $opts;
1829  }
1830 
1839  protected function getContentType( $storagePath, $content, $fsPath ) {
1840  if ( $this->mimeCallback ) {
1841  return call_user_func_array( $this->mimeCallback, func_get_args() );
1842  }
1843 
1844  $mime = null;
1845  if ( $fsPath !== null && function_exists( 'finfo_file' ) ) {
1846  $finfo = finfo_open( FILEINFO_MIME_TYPE );
1847  $mime = finfo_file( $finfo, $fsPath );
1848  finfo_close( $finfo );
1849  }
1850 
1851  return is_string( $mime ) ? $mime : 'unknown/unknown';
1852  }
1853 }
1854 
1865  public $params = []; // params to caller functions
1867  public $backend;
1869  public $resourcesToClose = [];
1870 
1871  public $call; // string; name that identifies the function called
1872 
1876  public function closeResources() {
1877  array_map( 'fclose', $this->resourcesToClose );
1878  }
1879 }
1880 
1887 abstract class FileBackendStoreShardListIterator extends FilterIterator {
1889  protected $backend;
1890 
1892  protected $params;
1893 
1895  protected $container;
1896 
1898  protected $directory;
1899 
1901  protected $multiShardPaths = []; // (rel path => 1)
1902 
1910  public function __construct(
1912  ) {
1913  $this->backend = $backend;
1914  $this->container = $container;
1915  $this->directory = $dir;
1916  $this->params = $params;
1917 
1918  $iter = new AppendIterator();
1919  foreach ( $suffixes as $suffix ) {
1920  $iter->append( $this->listFromShard( $this->container . $suffix ) );
1921  }
1922 
1923  parent::__construct( $iter );
1924  }
1925 
1926  public function accept() {
1927  $rel = $this->getInnerIterator()->current(); // path relative to given directory
1928  $path = $this->params['dir'] . "/{$rel}"; // full storage path
1929  if ( $this->backend->isSingleShardPathInternal( $path ) ) {
1930  return true; // path is only on one shard; no issue with duplicates
1931  } elseif ( isset( $this->multiShardPaths[$rel] ) ) {
1932  // Don't keep listing paths that are on multiple shards
1933  return false;
1934  } else {
1935  $this->multiShardPaths[$rel] = 1;
1936 
1937  return true;
1938  }
1939  }
1940 
1941  public function rewind() {
1942  parent::rewind();
1943  $this->multiShardPaths = [];
1944  }
1945 
1952  abstract protected function listFromShard( $container );
1953 }
1954 
1959  protected function listFromShard( $container ) {
1960  $list = $this->backend->getDirectoryListInternal(
1961  $container, $this->directory, $this->params );
1962  if ( $list === null ) {
1963  return new ArrayIterator( [] );
1964  } else {
1965  return is_array( $list ) ? new ArrayIterator( $list ) : $list;
1966  }
1967  }
1968 }
1969 
1974  protected function listFromShard( $container ) {
1975  $list = $this->backend->getFileListInternal(
1976  $container, $this->directory, $this->params );
1977  if ( $list === null ) {
1978  return new ArrayIterator( [] );
1979  } else {
1980  return is_array( $list ) ? new ArrayIterator( $list ) : $list;
1981  }
1982  }
1983 }
FileBackendStore\doGetFileXAttributes
doGetFileXAttributes(array $params)
Definition: FileBackendStore.php:735
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:1448
FileBackendStore\directoryExists
directoryExists(array $params)
Check if a directory exists at a given storage path.
Definition: FileBackendStore.php:904
FileBackendStore\getOperationsInternal
getOperationsInternal(array $ops)
Return a list of FileOp objects from a list of operations.
Definition: FileBackendStore.php:1010
FileBackendStoreShardListIterator\$backend
FileBackendStore $backend
Definition: FileBackendStore.php:1889
FileBackendStore\$memCache
WANObjectCache $memCache
Definition: FileBackendStore.php:41
FileBackendStoreShardListIterator\listFromShard
listFromShard( $container)
Get the list for a given container shard.
FileBackendStore\$maxFileSize
$maxFileSize
Definition: FileBackendStore.php:55
FileBackendStore\isValidShortContainerName
static isValidShortContainerName( $container)
Check if a short container name is valid.
Definition: FileBackendStore.php:1391
FileBackendStoreShardFileIterator
Iterator for listing regular files.
Definition: FileBackendStore.php:1973
StatusValue
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: StatusValue.php:42
FileBackendStore\getFileSha1Base36
getFileSha1Base36(array $params)
Get a SHA-1 hash of the file at a storage path in the backend.
Definition: FileBackendStore.php:739
FileBackend\normalizeContainerPath
static normalizeContainerPath( $path)
Validate and normalize a relative storage path.
Definition: FileBackend.php:1566
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:189
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
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:93
FileBackendStore\$cheapCache
ProcessCacheLRU $cheapCache
Map of paths to small (RAM/disk) cache items.
Definition: FileBackendStore.php:45
EmptyBagOStuff
A BagOStuff object with no objects in it.
Definition: EmptyBagOStuff.php:29
captcha-old.count
count
Definition: captcha-old.py:225
FileBackendStore\resolveStoragePath
resolveStoragePath( $storagePath)
Splits a storage path into an internal container name, an internal relative file name,...
Definition: FileBackendStore.php:1429
LockManager\LOCK_UW
const LOCK_UW
Definition: LockManager.php:69
FileBackendStore\sanitizeOpHeaders
sanitizeOpHeaders(array $op)
Normalize and filter HTTP headers from a file operation.
Definition: FileBackendStore.php:1252
FileBackend\getName
getName()
Get the unique backend name.
Definition: FileBackend.php:214
FileBackendStore\copyInternal
copyInternal(array $params)
Copy a file from one storage path to another in the backend.
Definition: FileBackendStore.php:206
FileBackendError
File backend exception for checked exceptions (e.g.
Definition: FileBackendError.php:8
FileBackend\getScopedFileLocks
getScopedFileLocks(array $paths, $type, StatusValue $status, $timeout=0)
Lock the files at the given storage paths in the backend.
Definition: FileBackend.php:1344
FileBackendStoreShardListIterator
FileBackendStore helper function to handle listings that span container shards.
Definition: FileBackendStore.php:1887
$status
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
Definition: hooks.txt:1049
FileBackendStore\doGetFileSha1Base36
doGetFileSha1Base36(array $params)
Definition: FileBackendStore.php:765
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
FileBackendStore\fileExists
fileExists(array $params)
Check if a file exists at a storage path in the backend.
Definition: FileBackendStore.php:605
FileBackendStoreShardListIterator\rewind
rewind()
Definition: FileBackendStore.php:1941
FileBackendStore\deleteFileCache
deleteFileCache( $path)
Delete the cached stat info for a file path.
Definition: FileBackendStore.php:1732
$params
$params
Definition: styleTest.css.php:40
FileBackend\normalizeStoragePath
static normalizeStoragePath( $storagePath)
Normalize a storage path by cleaning up directory separators.
Definition: FileBackend.php:1471
FileBackendStore\executeOpHandlesInternal
executeOpHandlesInternal(array $fileOpHandles)
Execute a list of FileBackendStoreOpHandle handles in parallel.
Definition: FileBackendStore.php:1206
BagOStuff
interface is intended to be more or less compatible with the PHP memcached client.
Definition: BagOStuff.php:47
FileBackendStoreShardListIterator\$params
array $params
Definition: FileBackendStore.php:1892
FileBackendStore\isSingleShardPathInternal
isSingleShardPathInternal( $storagePath)
Check if a storage path maps to a single shard.
Definition: FileBackendStore.php:1525
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:619
$res
$res
Definition: database.txt:21
FileBackendStore\concatenate
concatenate(array $params)
Concatenate a list of storage files into a single file system file.
Definition: FileBackendStore.php:351
$success
$success
Definition: NoLocalSettings.php:44
FileBackendStore\doPublish
doPublish(array $params)
Definition: FileBackendStore.php:514
FileBackendStore\getContainerHashLevels
getContainerHashLevels( $container)
Get the sharding config for a container.
Definition: FileBackendStore.php:1539
FileBackendStore\doGetFileStat
doGetFileStat(array $params)
FileBackendStore\deleteContainerCache
deleteContainerCache( $container)
Delete the cached info for a container.
Definition: FileBackendStore.php:1639
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:683
$base
$base
Definition: generateLocalAutoload.php:10
FileBackendStore\clearCache
clearCache(array $paths=null)
Invalidate any in-process file stat and property cache.
Definition: FileBackendStore.php:1283
FileBackendStore\doPublishInternal
doPublishInternal( $container, $dir, array $params)
Definition: FileBackendStore.php:545
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:1073
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
FileBackendStoreShardDirIterator
Iterator for listing directories.
Definition: FileBackendStore.php:1958
FileBackendStore\doSecure
doSecure(array $params)
Definition: FileBackendStore.php:479
HTTPFileStreamer
Functions related to the output of file content.
Definition: HTTPFileStreamer.php:29
FileBackendStore\doPrepare
doPrepare(array $params)
Definition: FileBackendStore.php:444
FileBackendStore\isValidContainerName
static isValidContainerName( $container)
Check if a full container name is valid.
Definition: FileBackendStore.php:1408
FSFile\placeholderProps
static placeholderProps()
Placeholder file properties to use for files that don't exist.
Definition: FSFile.php:143
FileBackendStore\deleteInternal
deleteInternal(array $params)
Delete a file at the storage path.
Definition: FileBackendStore.php:238
WANObjectCache\newEmpty
static newEmpty()
Get an instance that wraps EmptyBagOStuff.
Definition: WANObjectCache.php:200
FormatJson\encode
static encode( $value, $pretty=false, $escaping=0)
Returns the JSON representation of a value.
Definition: FormatJson.php:127
FileBackendStore\resolveContainerName
resolveContainerName( $container)
Resolve a container name, checking if it's allowed by the backend.
Definition: FileBackendStore.php:1595
FileBackendStore\doPrimeContainerCache
doPrimeContainerCache(array $containerInfo)
Fill the backend-specific process cache given an array of resolved container names and their correspo...
Definition: FileBackendStore.php:1691
FileBackendStore\$mimeCallback
callable $mimeCallback
Method to get the MIME type of files.
Definition: FileBackendStore.php:53
FileBackendStore\getDirectoryListInternal
getDirectoryListInternal( $container, $dir, array $params)
Do not call this function from places outside FileBackend.
FileBackendStore\getScopedLocksForOps
getScopedLocksForOps(array $ops, StatusValue $status)
Get an array of scoped locks needed for a batch of file operations.
Definition: FileBackendStore.php:1067
FileBackendStore\normalizeXAttributes
static normalizeXAttributes(array $xattr)
Normalize file headers/metadata to the FileBackend::getFileXAttributes() format.
Definition: FileBackendStore.php:1796
FileBackendStore\setConcurrencyFlags
setConcurrencyFlags(array $opts)
Set the 'concurrency' option from a list of operation options.
Definition: FileBackendStore.php:1816
FileBackendStore\CACHE_CHEAP_SIZE
const CACHE_CHEAP_SIZE
Definition: FileBackendStore.php:58
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:107
$content
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content $content
Definition: hooks.txt:1049
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:1867
FileBackendStore\doConcatenate
doConcatenate(array $params)
Definition: FileBackendStore.php:376
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
FileBackendStore\preloadFileStat
preloadFileStat(array $params)
Preload file stat information (concurrently if possible) into in-process cache.
Definition: FileBackendStore.php:1310
FileBackendStore\doGetLocalReferenceMulti
doGetLocalReferenceMulti(array $params)
Definition: FileBackendStore.php:821
FileBackendStore\CACHE_EXPENSIVE_SIZE
const CACHE_EXPENSIVE_SIZE
Definition: FileBackendStore.php:59
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:1049
FileBackendStore\doCreateInternal
doCreateInternal(array $params)
FileBackendStore\getContainerShard
getContainerShard( $container, $relPath)
Get the container name shard suffix for a given path.
Definition: FileBackendStore.php:1487
FileBackendStoreShardFileIterator\listFromShard
listFromShard( $container)
Get the list for a given container shard.
Definition: FileBackendStore.php:1974
HTTPFileStreamer\STREAM_ALLOW_OB
const STREAM_ALLOW_OB
Definition: HTTPFileStreamer.php:40
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
$dir
$dir
Definition: Autoload.php:8
FileBackendStore\getContentType
getContentType( $storagePath, $content, $fsPath)
Get the content type to use in HEAD/GET requests for a file.
Definition: FileBackendStore.php:1839
FileBackendStore\doCleanInternal
doCleanInternal( $container, $dir, array $params)
Definition: FileBackendStore.php:601
FileBackendStore\doCopyInternal
doCopyInternal(array $params)
FileBackendStore\doClearCache
doClearCache(array $paths=null)
Clears any additional stat caches for storage paths.
Definition: FileBackendStore.php:1307
$mime
if( $ext=='php'|| $ext=='php5') $mime
Definition: router.php:65
FileBackend\$obResetFunc
callable $obResetFunc
Definition: FileBackend.php:122
FileBackendStore\doStoreInternal
doStoreInternal(array $params)
FileBackendStore\doMoveInternal
doMoveInternal(array $params)
Definition: FileBackendStore.php:289
$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:626
FileBackend\$streamMimeFunc
callable $streamMimeFunc
Definition: FileBackend.php:124
FileBackendStore\CACHE_TTL
const CACHE_TTL
Definition: FileBackendStore.php:57
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:1619
FileBackendStore\resolveStoragePathReal
resolveStoragePathReal( $storagePath)
Like resolveStoragePath() except null values are returned if the container is sharded and the shard c...
Definition: FileBackendStore.php:1470
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:81
FileBackendStore\moveInternal
moveInternal(array $params)
Move a file from one storage path to another in the backend.
Definition: FileBackendStore.php:272
FileBackendStoreOpHandle\$resourcesToClose
array $resourcesToClose
Definition: FileBackendStore.php:1869
FileBackendStore\preloadCache
preloadCache(array $paths)
Preload persistent file stat cache and property cache into in-process cache.
Definition: FileBackendStore.php:1272
FileBackendStoreShardListIterator\__construct
__construct(FileBackendStore $backend, $container, $dir, array $suffixes, array $params)
Definition: FileBackendStore.php:1910
FileBackendStore\$srvCache
BagOStuff $srvCache
Definition: FileBackendStore.php:43
FileBackendStore\getContainerSuffixes
getContainerSuffixes( $container)
Get a list of full container shard suffixes for a container.
Definition: FileBackendStore.php:1560
FileBackendStore
Base class for all backends using particular storage medium.
Definition: FileBackendStore.php:39
HTTPFileStreamer\STREAM_HEADLESS
const STREAM_HEADLESS
Definition: HTTPFileStreamer.php:38
FileBackendStore\getDirectoryList
getDirectoryList(array $params)
Get an iterator to list all directories under a storage directory.
Definition: FileBackendStore.php:939
FileBackendStoreOpHandle
FileBackendStore helper class for performing asynchronous file operations.
Definition: FileBackendStore.php:1863
HTTPFileStreamer\send404Message
static send404Message( $fname, $flags=0)
Send out a standard 404 message for a file.
Definition: HTTPFileStreamer.php:185
FileBackendStore\fullContainerName
fullContainerName( $container)
Get the full container name, including the wiki ID prefix.
Definition: FileBackendStore.php:1579
FileBackendStore\doDescribeInternal
doDescribeInternal(array $params)
Definition: FileBackendStore.php:336
FileBackendStore\doExecuteOpHandlesInternal
doExecuteOpHandlesInternal(array $fileOpHandles)
Definition: FileBackendStore.php:1233
FileBackendStoreShardListIterator\$multiShardPaths
array $multiShardPaths
Definition: FileBackendStore.php:1901
FileBackendStore\doClean
doClean(array $params)
Definition: FileBackendStore.php:549
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:1652
FileBackendStore\setFileCache
setFileCache( $path, array $val)
Set the cached stat info for a file path.
Definition: FileBackendStore.php:1712
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:1749
FileBackendStore\fileCacheKey
fileCacheKey( $path)
Get the cache key for a file path.
Definition: FileBackendStore.php:1700
FileBackendStore\doPrepareInternal
doPrepareInternal( $container, $dir, array $params)
Definition: FileBackendStore.php:475
FileBackendStore\doGetLocalCopyMulti
doGetLocalCopyMulti(array $params)
FileBackendStore\doGetFileContentsMulti
doGetFileContentsMulti(array $params)
Definition: FileBackendStore.php:697
FileBackend\$name
string $name
Unique backend name.
Definition: FileBackend.php:95
FileBackendStore\$expensiveCache
ProcessCacheLRU $expensiveCache
Map of paths to large (RAM/disk) cache items.
Definition: FileBackendStore.php:47
FileBackendStore\createInternal
createInternal(array $params)
Create a file in the backend with the given contents.
Definition: FileBackendStore.php:123
FileBackendStore\resolveContainerPath
resolveContainerPath( $container, $relStoragePath)
Resolve a relative storage path, checking if it's allowed by the backend.
Definition: FileBackendStore.php:1609
$path
$path
Definition: NoLocalSettings.php:26
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:1895
FileBackendStoreOpHandle\$params
array $params
Definition: FileBackendStore.php:1865
FileBackendStore\storeInternal
storeInternal(array $params)
Store a file into the backend from a file on disk.
Definition: FileBackendStore.php:164
FileBackendStore\doStreamFile
doStreamFile(array $params)
Definition: FileBackendStore.php:875
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:50
FileBackend\getTopDirectoryList
getTopDirectoryList(array $params)
Same as FileBackend::getDirectoryList() except only lists directories that are immediately under the ...
Definition: FileBackend.php:1220
FileBackendStore\getFileXAttributes
getFileXAttributes(array $params)
Get metadata about a file at a storage path in the backend.
Definition: FileBackendStore.php:708
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:1099
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:347
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
ProcessCacheLRU
Handles per process caching of items.
Definition: ProcessCacheLRU.php:29
FileBackendStore\getFileList
getFileList(array $params)
Get an iterator to list all stored files under a storage directory.
Definition: FileBackendStore.php:969
FileBackendStore\maxFileSizeInternal
maxFileSizeInternal()
Get the maximum allowable file size given backend medium restrictions and basic performance constrain...
Definition: FileBackendStore.php:90
LockManager\LOCK_EX
const LOCK_EX
Definition: LockManager.php:70
FileBackendStore\getFileTimestamp
getFileTimestamp(array $params)
Get the last-modified timestamp of the file at a storage path.
Definition: FileBackendStore.php:612
FileBackendStore\describeInternal
describeInternal(array $params)
Alter metadata for a file at the storage path.
Definition: FileBackendStore.php:318
FileBackendStoreShardListIterator\accept
accept()
Definition: FileBackendStore.php:1926
FileBackendStore\doGetFileStatMulti
doGetFileStatMulti(array $params)
Get file stat information (concurrently if possible) for several files.
Definition: FileBackendStore.php:1368
FileBackendStore\__construct
__construct(array $config)
Definition: FileBackendStore.php:72
FileBackendStore\doSecureInternal
doSecureInternal( $container, $dir, array $params)
Definition: FileBackendStore.php:510
FileBackendStoreOpHandle\$call
$call
Definition: FileBackendStore.php:1871
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2749
FileBackendStore\setContainerCache
setContainerCache( $container, array $val)
Set the cached info for a container.
Definition: FileBackendStore.php:1629
FileBackend\scopedProfileSection
scopedProfileSection( $section)
Definition: FileBackend.php:1621
FileBackend\newStatus
newStatus()
Yields the result of the status wrapper callback on either:
Definition: FileBackend.php:1598
FileBackendStoreShardListIterator\$directory
string $directory
Resolved relative path.
Definition: FileBackendStore.php:1898
FileBackendStore\doQuickOperationsInternal
doQuickOperationsInternal(array $ops)
Definition: FileBackendStore.php:1140
array
the array() calling protocol came about after MediaWiki 1.4rc1.
FileBackendStoreOpHandle\closeResources
closeResources()
Close all open file handles.
Definition: FileBackendStore.php:1876
FileBackendStoreShardDirIterator\listFromShard
listFromShard( $container)
Get the list for a given container shard.
Definition: FileBackendStore.php:1959