24 use Wikimedia\AtEase\AtEase;
25 use Wikimedia\Timestamp\ConvertibleTimestamp;
87 parent::__construct( $config );
88 $this->mimeCallback = $config[
'mimeCallback'] ??
null;
91 $this->cheapCache =
new MapCacheLRU( self::CACHE_CHEAP_SIZE );
92 $this->expensiveCache =
new MapCacheLRU( self::CACHE_EXPENSIVE_SIZE );
141 $status = $this->
newStatus(
'backend-fail-maxsize',
146 if ( $params[
'dstExists'] ??
true ) {
184 $status = $this->
newStatus(
'backend-fail-maxsize',
189 if ( $params[
'dstExists'] ??
true ) {
229 if ( $params[
'dstExists'] ??
true ) {
298 $this->
clearCache( [ $params[
'src'], $params[
'dst'] ] );
300 if ( $params[
'dstExists'] ??
true ) {
314 unset( $params[
'async'] );
319 if ( $nsrc !== $ndst && $status->isOK() ) {
321 $status->merge( $this->
deleteInternal( [
'src' => $params[
'src'] ] ) );
322 $status->setResult(
true, $status->value );
346 if ( count( $params[
'headers'] ) ) {
386 if ( $status->isOK() ) {
388 $start_time = microtime(
true );
390 $sec = microtime(
true ) - $start_time;
391 if ( !$status->isOK() ) {
392 $this->logger->error( static::class .
"-{$this->name}" .
393 " failed to concatenate " . count( $params[
'srcs'] ) .
" file(s) [$sec sec]" );
408 $tmpPath = $params[
'dst'];
409 unset( $params[
'latest'] );
412 AtEase::suppressWarnings();
413 $ok = ( is_file( $tmpPath ) && filesize( $tmpPath ) == 0 );
414 AtEase::restoreWarnings();
416 $status->fatal(
'backend-fail-opentemp', $tmpPath );
423 foreach ( $fsFiles as
$path => &$fsFile ) {
427 $status->fatal(
'backend-fail-read',
$path );
436 $tmpHandle = fopen( $tmpPath,
'ab' );
437 if ( $tmpHandle ===
false ) {
438 $status->fatal(
'backend-fail-opentemp', $tmpPath );
444 foreach ( $fsFiles as $virtualSource => $fsFile ) {
446 $sourceHandle = fopen( $fsFile->getPath(),
'rb' );
447 if ( $sourceHandle ===
false ) {
448 fclose( $tmpHandle );
449 $status->fatal(
'backend-fail-read', $virtualSource );
454 if ( !stream_copy_to_stream( $sourceHandle, $tmpHandle ) ) {
455 fclose( $sourceHandle );
456 fclose( $tmpHandle );
457 $status->fatal(
'backend-fail-writetemp', $tmpPath );
461 fclose( $sourceHandle );
463 if ( !fclose( $tmpHandle ) ) {
464 $status->fatal(
'backend-fail-closetemp', $tmpPath );
483 if ( $dir ===
null ) {
484 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
489 if ( $shard !==
null ) {
492 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
495 $status->merge( $this->
doPrepareInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
514 final protected function doSecure( array $params ) {
520 if ( $dir ===
null ) {
521 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
526 if ( $shard !==
null ) {
529 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
532 $status->merge( $this->
doSecureInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
557 if ( $dir ===
null ) {
558 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
563 if ( $shard !==
null ) {
566 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
569 $status->merge( $this->
doPublishInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
588 final protected function doClean( array $params ) {
596 if ( $subDirsRel !==
null ) {
597 foreach ( $subDirsRel as $subDirRel ) {
598 $subDir = $params[
'dir'] .
"/{$subDirRel}";
599 $status->merge( $this->
doClean( [
'dir' => $subDir ] + $params ) );
601 unset( $subDirsRel );
606 if ( $dir ===
null ) {
607 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
613 $filesLockEx = [ $params[
'dir'] ];
616 if ( !$status->isOK() ) {
620 if ( $shard !==
null ) {
624 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
627 $status->merge( $this->
doCleanInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
652 if ( is_array( $stat ) ) {
656 return ( $stat === self::$RES_ABSENT ) ? false : self::EXISTENCE_ERROR;
664 if ( is_array( $stat ) ) {
665 return $stat[
'mtime'];
668 return self::TIMESTAMP_FAIL;
676 if ( is_array( $stat ) ) {
677 return $stat[
'size'];
680 return self::SIZE_FAIL;
688 if (
$path ===
null ) {
689 return self::STAT_ERROR;
694 $latest = !empty( $params[
'latest'] );
696 $requireSHA1 = !empty( $params[
'requireSHA1'] );
698 $stat = $this->cheapCache->getField(
$path,
'stat', self::CACHE_TTL );
707 ( $requireSHA1 && is_array( $stat ) && !isset( $stat[
'sha1'] ) )
711 $stat = $this->cheapCache->getField(
$path,
'stat', self::CACHE_TTL );
715 if ( is_array( $stat ) ) {
717 ( !$latest || $stat[
'latest'] ) &&
718 ( !$requireSHA1 || isset( $stat[
'sha1'] ) )
722 } elseif ( $stat === self::$ABSENT_LATEST ) {
723 return self::STAT_ABSENT;
724 } elseif ( $stat === self::$ABSENT_NORMAL ) {
726 return self::STAT_ABSENT;
734 if ( is_array( $stat ) ) {
738 return ( $stat === self::$RES_ERROR ) ? self::STAT_ERROR : self::STAT_ABSENT;
751 foreach ( $stats as
$path => $stat ) {
752 if ( is_array( $stat ) ) {
754 $stat[
'latest'] = $stat[
'latest'] ?? $latest;
756 $this->cheapCache->setField(
$path,
'stat', $stat );
757 if ( isset( $stat[
'sha1'] ) ) {
759 $this->cheapCache->setField(
762 [
'hash' => $stat[
'sha1'],
'latest' => $latest ]
765 if ( isset( $stat[
'xattr'] ) ) {
768 $this->cheapCache->setField(
771 [
'map' => $stat[
'xattr'],
'latest' => $latest ]
776 } elseif ( $stat === self::$RES_ABSENT ) {
777 $this->cheapCache->setField(
780 $latest ? self::$ABSENT_LATEST : self::$ABSENT_NORMAL
782 $this->cheapCache->setField(
785 [
'map' => self::XATTRS_FAIL,
'latest' => $latest ]
787 $this->cheapCache->setField(
790 [
'hash' => self::SHA1_FAIL,
'latest' => $latest ]
792 $this->logger->debug(
793 __METHOD__ .
': File {path} does not exist',
798 $this->logger->error(
799 __METHOD__ .
': Could not stat file {path}',
822 $contents[
$path] = self::CONTENT_FAIL;
838 if ( $fsFile instanceof
FSFile ) {
839 AtEase::suppressWarnings();
840 $content = file_get_contents( $fsFile->getPath() );
841 AtEase::restoreWarnings();
843 } elseif ( $fsFile === self::$RES_ABSENT ) {
858 if (
$path ===
null ) {
859 return self::XATTRS_FAIL;
861 $latest = !empty( $params[
'latest'] );
862 if ( $this->cheapCache->hasField(
$path,
'xattr', self::CACHE_TTL ) ) {
863 $stat = $this->cheapCache->getField(
$path,
'xattr' );
866 if ( !$latest || $stat[
'latest'] ) {
871 if ( is_array( $fields ) ) {
873 $this->cheapCache->setField(
876 [
'map' => $fields,
'latest' => $latest ]
878 } elseif ( $fields === self::$RES_ABSENT ) {
879 $this->cheapCache->setField(
882 [
'map' => self::XATTRS_FAIL,
'latest' => $latest ]
885 $fields = self::XATTRS_FAIL;
898 return [
'headers' => [],
'metadata' => [] ];
906 if (
$path ===
null ) {
907 return self::SHA1_FAIL;
909 $latest = !empty( $params[
'latest'] );
910 if ( $this->cheapCache->hasField(
$path,
'sha1', self::CACHE_TTL ) ) {
911 $stat = $this->cheapCache->getField(
$path,
'sha1' );
914 if ( !$latest || $stat[
'latest'] ) {
915 return $stat[
'hash'];
919 if ( is_string( $sha1 ) ) {
920 $this->cheapCache->setField(
923 [
'hash' => $sha1,
'latest' => $latest ]
925 } elseif ( $sha1 === self::$RES_ABSENT ) {
926 $this->cheapCache->setField(
929 [
'hash' => self::SHA1_FAIL,
'latest' => $latest ]
932 $sha1 = self::SHA1_FAIL;
946 if ( $fsFile instanceof
FSFile ) {
947 $sha1 = $fsFile->getSha1Base36();
971 $latest = !empty( $params[
'latest'] );
973 foreach ( $params[
'srcs'] as $src ) {
975 if (
$path ===
null ) {
976 $fsFiles[$src] =
null;
977 } elseif ( $this->expensiveCache->hasField(
$path,
'localRef' ) ) {
978 $val = $this->expensiveCache->getField(
$path,
'localRef' );
981 if ( !$latest || $val[
'latest'] ) {
982 $fsFiles[$src] = $val[
'object'];
987 $params[
'srcs'] = array_diff( $params[
'srcs'], array_keys( $fsFiles ) );
989 if ( $fsFile instanceof
FSFile ) {
990 $fsFiles[
$path] = $fsFile;
991 $this->expensiveCache->setField(
994 [
'object' => $fsFile,
'latest' => $latest ]
997 $fsFiles[
$path] =
null;
1020 foreach ( $tmpFiles as
$path => $tmpFile ) {
1022 $tmpFiles[
$path] =
null;
1043 return self::TEMPURL_ERROR;
1052 $params[
'options'] = $params[
'options'] ?? [];
1053 $params[
'headers'] = $params[
'headers'] ?? [];
1056 if ( ( empty( $params[
'headless'] ) || $params[
'headers'] ) && headers_sent() ) {
1057 print
"Headers already sent, terminating.\n";
1058 $status->fatal(
'backend-fail-stream', $params[
'src'] );
1085 'obResetFunc' => $this->obResetFunc,
1086 'streamMimeFunc' => $this->streamMimeFunc
1089 $res = $streamer->stream( $params[
'headers'],
true, $params[
'options'], $flags );
1096 $status->fatal(
'backend-fail-stream', $params[
'src'] );
1104 if ( $dir ===
null ) {
1105 return self::EXISTENCE_ERROR;
1107 if ( $shard !==
null ) {
1110 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
1115 if ( $exists ===
true ) {
1118 } elseif ( $exists === self::$RES_ERROR ) {
1119 $res = self::EXISTENCE_ERROR;
1139 if ( $dir ===
null ) {
1140 return self::EXISTENCE_ERROR;
1142 if ( $shard !==
null ) {
1146 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
1169 if ( $dir ===
null ) {
1170 return self::LIST_ERROR;
1172 if ( $shard !==
null ) {
1176 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
1210 'store' => StoreFileOp::class,
1211 'copy' => CopyFileOp::class,
1212 'move' => MoveFileOp::class,
1213 'delete' => DeleteFileOp::class,
1214 'create' => CreateFileOp::class,
1215 'describe' => DescribeFileOp::class,
1216 'null' => NullFileOp::class
1221 foreach ( $ops as $operation ) {
1222 $opName = $operation[
'op'];
1223 if ( isset( $supportedOps[$opName] ) ) {
1224 $class = $supportedOps[$opName];
1226 $params = $operation;
1228 $performOps[] =
new $class( $this, $params, $this->logger );
1249 $paths = [
'sh' => [],
'ex' => [] ];
1250 foreach ( $performOps as $fileOp ) {
1251 $paths[
'sh'] = array_merge( $paths[
'sh'], $fileOp->storagePathsRead() );
1252 $paths[
'ex'] = array_merge( $paths[
'ex'], $fileOp->storagePathsChanged() );
1255 $paths[
'sh'] = array_diff( $paths[
'sh'], $paths[
'ex'] );
1257 $paths[
'sh'] = array_merge( $paths[
'sh'], array_map(
'dirname', $paths[
'ex'] ) );
1277 $ops = array_map( [ $this,
'sanitizeOpHeaders' ], $ops );
1281 foreach ( $fileOps as $fileOp ) {
1282 $pathsUsed = array_merge( $pathsUsed, $fileOp->storagePathsReadOrChanged() );
1286 if ( empty( $opts[
'nonLocking'] ) ) {
1290 if ( !$status->isOK() ) {
1296 if ( empty( $opts[
'preserveCache'] ) ) {
1301 $this->cheapCache->setMaxSize( max( 2 * count( $pathsUsed ), self::CACHE_CHEAP_SIZE ) );
1306 $ok = $this->
preloadFileStat( [
'srcs' => $pathsUsed,
'latest' =>
true ] );
1314 $subStatus = $this->
newStatus(
'backend-fail-internal', $this->name );
1315 foreach ( $ops as $i => $op ) {
1316 $subStatus->success[$i] =
false;
1317 ++$subStatus->failCount;
1319 $this->logger->error( static::class .
"-{$this->name} " .
1324 $status->merge( $subStatus );
1325 $status->success = $subStatus->success;
1328 $this->cheapCache->setMaxSize( self::CACHE_CHEAP_SIZE );
1339 $ops = array_map( [ $this,
'sanitizeOpHeaders' ], $ops );
1343 foreach ( $fileOps as $fileOp ) {
1344 $pathsUsed = array_merge( $pathsUsed, $fileOp->storagePathsReadOrChanged() );
1351 $async = ( $this->parallelize ===
'implicit' && count( $ops ) > 1 );
1355 $fileOpHandles = [];
1356 $curFileOpHandles = [];
1358 foreach ( $fileOps as $index => $fileOp ) {
1360 ? $fileOp->attemptAsyncQuick()
1361 : $fileOp->attemptQuick();
1363 if ( count( $curFileOpHandles ) >= $maxConcurrency ) {
1364 $fileOpHandles[] = $curFileOpHandles;
1365 $curFileOpHandles = [];
1367 $curFileOpHandles[$index] = $subStatus->value;
1369 $statuses[$index] = $subStatus;
1372 if ( count( $curFileOpHandles ) ) {
1373 $fileOpHandles[] = $curFileOpHandles;
1376 foreach ( $fileOpHandles as $fileHandleBatch ) {
1380 foreach ( $statuses as $index => $subStatus ) {
1381 $status->merge( $subStatus );
1382 if ( $subStatus->isOK() ) {
1383 $status->success[$index] =
true;
1384 ++$status->successCount;
1386 $status->success[$index] =
false;
1387 ++$status->failCount;
1409 foreach ( $fileOpHandles as $fileOpHandle ) {
1411 throw new InvalidArgumentException(
"Expected FileBackendStoreOpHandle object." );
1412 } elseif ( $fileOpHandle->backend->getName() !== $this->getName() ) {
1413 throw new InvalidArgumentException(
"Expected handle for this file backend." );
1418 foreach ( $fileOpHandles as $fileOpHandle ) {
1419 $fileOpHandle->closeResources();
1435 if ( count( $fileOpHandles ) ) {
1436 throw new FileBackendError(
"Backend does not support asynchronous operations." );
1454 static $longs = [
'content-disposition' ];
1456 if ( isset( $op[
'headers'] ) ) {
1458 foreach ( $op[
'headers'] as
$name => $value ) {
1460 $maxHVLen = in_array(
$name, $longs ) ? INF : 255;
1461 if ( strlen(
$name ) > 255 || strlen( $value ) > $maxHVLen ) {
1462 $this->logger->error(
"Header '{header}' is too long.", [
1463 'filebackend' => $this->name,
1464 'header' =>
"$name: $value",
1467 $newHeaders[
$name] = strlen( $value ) ? $value :
'';
1470 $op[
'headers'] = $newHeaders;
1478 foreach ( $paths as
$path ) {
1480 $fullConts[] = $fullCont;
1488 if ( is_array( $paths ) ) {
1489 $paths = array_map( [ FileBackend::class,
'normalizeStoragePath' ], $paths );
1490 $paths = array_filter( $paths,
'strlen' );
1492 if ( $paths ===
null ) {
1493 $this->cheapCache->clear();
1494 $this->expensiveCache->clear();
1496 foreach ( $paths as
$path ) {
1497 $this->cheapCache->clear(
$path );
1498 $this->expensiveCache->clear(
$path );
1519 $params[
'concurrency'] = ( $this->parallelize !==
'off' ) ? $this->concurrency : 1;
1521 if ( $stats ===
null ) {
1526 $latest = !empty( $params[
'latest'] );
1588 return (
bool)preg_match(
'/^[a-z0-9][a-z0-9-_.]{0,199}$/i', $container );
1606 if ( $backend === $this->name ) {
1608 if ( $relPath !==
null && self::isValidShortContainerName( $shortCont ) ) {
1613 if ( $relPath !==
null ) {
1616 if ( self::isValidContainerName( $container ) ) {
1619 if ( $container !==
null ) {
1620 return [ $container, $relPath, $cShard ];
1627 return [
null,
null, null ];
1647 if ( $cShard !==
null && substr( $relPath, -1 ) !==
'/' ) {
1648 return [ $container, $relPath ];
1651 return [
null, null ];
1664 if ( $levels == 1 || $levels == 2 ) {
1666 $char = (
$base == 36 ) ?
'[0-9a-z]' :
'[0-9a-f]';
1669 if ( $levels === 1 ) {
1670 $hashDirRegex =
'(' . $char .
')';
1673 $hashDirRegex = $char .
'/(' . $char .
'{2})';
1675 $hashDirRegex =
'(' . $char .
')/(' . $char .
')';
1682 if ( preg_match(
"!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) {
1683 return '.' . implode(
'', array_slice( $m, 1 ) );
1703 return ( $shard !==
null );
1715 if ( isset( $this->shardViaHashLevels[$container] ) ) {
1716 $config = $this->shardViaHashLevels[$container];
1717 $hashLevels = (int)$config[
'levels'];
1718 if ( $hashLevels == 1 || $hashLevels == 2 ) {
1719 $hashBase = (int)$config[
'base'];
1720 if ( $hashBase == 16 || $hashBase == 36 ) {
1721 return [ $hashLevels, $hashBase, $config[
'repeat'] ];
1726 return [ 0, 0, false ];
1738 if ( $digits > 0 ) {
1739 $numShards =
$base ** $digits;
1740 for ( $index = 0; $index < $numShards; $index++ ) {
1741 $shards[] =
'.' . Wikimedia\base_convert( (
string)$index, 10,
$base, $digits );
1755 if ( $this->domainId !=
'' ) {
1756 return "{$this->domainId}-$container";
1787 return $relStoragePath;
1796 private function containerCacheKey( $container ) {
1797 return "filebackend:{$this->name}:{$this->domainId}:container:{$container}";
1807 $this->memCache->set( $this->containerCacheKey( $container ), $val, 14 * 86400 );
1817 if ( !$this->memCache->delete( $this->containerCacheKey( $container ), 300 ) ) {
1818 $this->logger->warning(
"Unable to delete stat cache for container {container}.",
1819 [
'filebackend' => $this->name,
'container' => $container ]
1838 foreach ( $items as $item ) {
1839 if ( self::isStoragePath( $item ) ) {
1841 } elseif ( is_string( $item ) ) {
1842 $contNames[$this->containerCacheKey( $item )] = $item;
1846 foreach ( $paths as
$path ) {
1848 if ( $fullCont !==
null ) {
1849 $contNames[$this->containerCacheKey( $fullCont )] = $fullCont;
1855 $values = $this->memCache->getMulti( array_keys( $contNames ) );
1856 foreach ( $values as $cacheKey => $val ) {
1857 $contInfo[$contNames[$cacheKey]] = $val;
1881 private function fileCacheKey(
$path ) {
1882 return "filebackend:{$this->name}:{$this->domainId}:file:" . sha1(
$path );
1895 if (
$path ===
null ) {
1898 $mtime = (int)ConvertibleTimestamp::convert( TS_UNIX, $val[
'mtime'] );
1899 $ttl = $this->memCache->adaptiveTTL( $mtime, 7 * 86400, 300, 0.1 );
1900 $key = $this->fileCacheKey(
$path );
1902 $this->memCache->set( $key, $val, $ttl );
1915 if (
$path ===
null ) {
1918 if ( !$this->memCache->delete( $this->fileCacheKey(
$path ), 300 ) ) {
1919 $this->logger->warning(
"Unable to delete stat cache for file {path}.",
1920 [
'filebackend' => $this->name,
'path' =>
$path ]
1939 foreach ( $items as $item ) {
1940 if ( self::isStoragePath( $item ) ) {
1942 if (
$path !==
null ) {
1948 $paths = array_filter( $paths,
'strlen' );
1950 foreach ( $paths as
$path ) {
1952 if ( $rel !==
null ) {
1953 $pathNames[$this->fileCacheKey(
$path )] =
$path;
1958 $values = $this->memCache->getMulti( array_keys( $pathNames ) );
1960 foreach ( array_filter( $values,
'is_array' ) as $cacheKey => $stat ) {
1961 $path = $pathNames[$cacheKey];
1964 unset( $stat[
'latest'] );
1966 $this->cheapCache->setField(
$path,
'stat', $stat );
1967 if ( isset( $stat[
'sha1'] ) && strlen( $stat[
'sha1'] ) == 31 ) {
1969 $this->cheapCache->setField(
1972 [
'hash' => $stat[
'sha1'],
'latest' =>
false ]
1975 if ( isset( $stat[
'xattr'] ) && is_array( $stat[
'xattr'] ) ) {
1978 $this->cheapCache->setField(
1981 [
'map' => $stat[
'xattr'],
'latest' =>
false ]
1995 $newXAttr = [
'headers' => [],
'metadata' => [] ];
1997 foreach ( $xattr[
'headers'] as
$name => $value ) {
1998 $newXAttr[
'headers'][strtolower(
$name )] = $value;
2001 foreach ( $xattr[
'metadata'] as
$name => $value ) {
2002 $newXAttr[
'metadata'][strtolower(
$name )] = $value;
2015 $opts[
'concurrency'] = 1;
2016 if ( $this->parallelize ===
'implicit' ) {
2017 if ( $opts[
'parallelize'] ??
true ) {
2020 } elseif ( $this->parallelize ===
'explicit' ) {
2021 if ( !empty( $opts[
'parallelize'] ) ) {
2039 if ( $this->mimeCallback ) {
2040 return call_user_func_array( $this->mimeCallback, func_get_args() );
2043 $mime = ( $fsPath !== null ) ? mime_content_type( $fsPath ) :
false;
2044 return $mime ?:
'unknown/unknown';
A BagOStuff object with no objects in it.
Class representing a non-directory file on the file system.
static placeholderProps()
Placeholder file properties to use for files that don't exist.
File backend exception for checked exceptions (e.g.
FileBackendStore helper class for performing asynchronous file operations.
Iterator for listing directories.
Iterator for listing regular files.
Base class for all backends using particular storage medium.
preloadFileStat(array $params)
Preload file stat information (concurrently if possible) into in-process cache.
doGetLocalReferenceMulti(array $params)
resolveContainerName( $container)
Resolve a container name, checking if it's allowed by the backend.
static normalizeXAttributes(array $xattr)
Normalize file headers/metadata to the FileBackend::getFileXAttributes() format.
getLocalReferenceMulti(array $params)
Like getLocalReference() except it takes an array of storage paths and yields an order-preserved map ...
setFileCache( $path, array $val)
Set the cached stat info for a file path.
moveInternal(array $params)
Move a file from one storage path to another in the backend.
setContainerCache( $container, array $val)
Set the cached info for a container.
isPathUsableInternal( $storagePath)
Check if a file can be created or changed at a given storage path in the backend.
doGetFileContentsMulti(array $params)
static string $ABSENT_NORMAL
File does not exist according to a normal stat query.
doStreamFile(array $params)
const CACHE_EXPENSIVE_SIZE
MapCacheLRU $expensiveCache
Map of paths to large (RAM/disk) cache items.
getFileTimestamp(array $params)
Get the last-modified timestamp of the file at a storage path.
doGetLocalCopyMulti(array $params)
getScopedLocksForOps(array $ops, StatusValue $status)
Get an array of scoped locks needed for a batch of file operations.
doCleanInternal( $container, $dir, array $params)
callable $mimeCallback
Method to get the MIME type of files.
getPathsToLockForOpsInternal(array $performOps)
Get a list of storage paths to lock for a list of operations Returns an array with LockManager::LOCK_...
executeOpHandlesInternal(array $fileOpHandles)
Execute a list of FileBackendStoreOpHandle handles in parallel.
doPrepareInternal( $container, $dir, array $params)
concatenate(array $params)
Concatenate a list of storage files into a single file system file.
storeInternal(array $params)
Store a file into the backend from a file on disk.
getFileProps(array $params)
Get the properties of the content of the file at a storage path in the backend.
getDirectoryList(array $params)
Get an iterator to list all directories under a storage directory.
doQuickOperationsInternal(array $ops, array $opts)
resolveStoragePath( $storagePath)
Splits a storage path into an internal container name, an internal relative file name,...
copyInternal(array $params)
Copy a file from one storage path to another in the backend.
doStoreInternal(array $params)
directoriesAreVirtual()
Is this a key/value store where directories are just virtual? Virtual directories exists in so much a...
doGetFileStat(array $params)
getFileStat(array $params)
Get quick information about a file at a storage path in the backend.
MapCacheLRU $cheapCache
Map of paths to small (RAM/disk) cache items.
resolveStoragePathReal( $storagePath)
Like resolveStoragePath() except null values are returned if the container is sharded and the shard c...
clearCache(array $paths=null)
Invalidate any in-process file stat and property cache.
doPublishInternal( $container, $dir, array $params)
doGetFileStatMulti(array $params)
Get file stat information (concurrently if possible) for several files.
getFileSha1Base36(array $params)
Get a SHA-1 hash of the content of the file at a storage path in the backend.
static isValidContainerName( $container)
Check if a full container name is valid.
ingestFreshFileStats(array $stats, $latest)
Ingest file stat entries that just came from querying the backend (not cache)
getFileListInternal( $container, $dir, array $params)
Do not call this function from places outside FileBackend.
doCopyInternal(array $params)
getLocalCopyMulti(array $params)
Like getLocalCopy() except it takes an array of storage paths and yields an order preserved-map of st...
getFileContentsMulti(array $params)
Like getFileContents() except it takes an array of storage paths and returns an order preserved map o...
doClearCache(array $paths=null)
Clears any additional stat caches for storage paths.
fullContainerName( $container)
Get the full container name, including the domain ID prefix.
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...
doOperationsInternal(array $ops, array $opts)
isSingleShardPathInternal( $storagePath)
Check if a storage path maps to a single shard.
directoryExists(array $params)
Check if a directory exists at a given storage path.
doDeleteInternal(array $params)
static string $ABSENT_LATEST
File does not exist according to a "latest"-mode stat query.
getContainerSuffixes( $container)
Get a list of full container shard suffixes for a container.
primeContainerCache(array $items)
Do a batch lookup from cache for container stats for all containers used in a list of container names...
sanitizeOpHeaders(array $op)
Normalize and filter HTTP headers from a file operation.
doDescribeInternal(array $params)
doGetFileSha1Base36(array $params)
deleteInternal(array $params)
Delete a file at the storage path.
doPrimeContainerCache(array $containerInfo)
Fill the backend-specific process cache given an array of resolved container names and their correspo...
doMoveInternal(array $params)
maxFileSizeInternal()
Get the maximum allowable file size given backend medium restrictions and basic performance constrain...
describeInternal(array $params)
Alter metadata for a file at the storage path.
streamFile(array $params)
Stream the content of the file at a storage path in the backend.
getOperationsInternal(array $ops)
Return a list of FileOp objects from a list of operations.
deleteFileCache( $path)
Delete the cached stat info for a file path.
setConcurrencyFlags(array $opts)
Set the 'concurrency' option from a list of operation options.
getFileXAttributes(array $params)
Get metadata about a file at a storage path in the backend.
getContentType( $storagePath, $content, $fsPath)
Get the content type to use in HEAD/GET requests for a file.
preloadCache(array $paths)
Preload persistent file stat cache and property cache into in-process cache.
resolveContainerPath( $container, $relStoragePath)
Resolve a relative storage path, checking if it's allowed by the backend.
doDirectoryExists( $container, $dir, array $params)
static false $RES_ABSENT
Idiom for "no result due to missing file" (since 1.34)
doPrepare(array $params)
FileBackend::prepare() StatusValue Good status without value for success, fatal otherwise.
getFileHttpUrl(array $params)
__construct(array $config)
doGetFileXAttributes(array $params)
static null $RES_ERROR
Idiom for "no result due to I/O errors" (since 1.34)
doExecuteOpHandlesInternal(array $fileOpHandles)
nullInternal(array $params)
No-op file operation that does nothing.
getFileSize(array $params)
Get the size (bytes) of a file at a storage path in the backend.
array $shardViaHashLevels
Map of container names to sharding config.
doConcatenate(array $params)
doSecureInternal( $container, $dir, array $params)
doCreateInternal(array $params)
fileExists(array $params)
Check if a file exists at a storage path in the backend.
getDirectoryListInternal( $container, $dir, array $params)
Do not call this function from places outside FileBackend.
static isValidShortContainerName( $container)
Check if a short container name is valid.
getContainerHashLevels( $container)
Get the sharding config for a container.
getContainerShard( $container, $relPath)
Get the container name shard suffix for a given path.
deleteContainerCache( $container)
Delete the cached info for a container.
createInternal(array $params)
Create a file in the backend with the given contents.
getFileList(array $params)
Get an iterator to list all stored files under a storage directory.
Base class for all file backend classes (including multi-write backends).
string $name
Unique backend name.
static splitStoragePath( $storagePath)
Split a storage path into a backend name, a container name, and a relative file path.
static normalizeContainerPath( $path)
Validate and normalize a relative storage path.
getScopedFileLocks(array $paths, $type, StatusValue $status, $timeout=0)
Lock the files at the given storage paths in the backend.
getTopDirectoryList(array $params)
Same as FileBackend::getDirectoryList() except only lists directories that are immediately under the ...
scopedProfileSection( $section)
newStatus(... $args)
Yields the result of the status wrapper callback on either:
static normalizeStoragePath( $storagePath)
Normalize a storage path by cleaning up directory separators.
int $concurrency
How many operations can be done in parallel.
getLocalReference(array $params)
Returns a file system file, identical in content to the file at a storage path.
static attempt(array $performOps, array $opts)
Attempt to perform a series of file operations.
Functions related to the output of file content.
static send404Message( $fname, $flags=0)
Send out a standard 404 message for a file.
Handles a simple LRU key/value map with a maximum number of entries.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
static newEmpty()
Get an instance that wraps EmptyBagOStuff.