24use Wikimedia\AtEase\AtEase;
25use Wikimedia\Timestamp\ConvertibleTimestamp;
87 parent::__construct( $config );
88 $this->mimeCallback = $config[
'mimeCallback'] ??
null;
90 $this->memCache = WANObjectCache::newEmpty();
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'] ) ) {
385 $scopeLockS = $this->
getScopedFileLocks( $params[
'srcs'], LockManager::LOCK_UW, $status );
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 );
480 if ( $dir ===
null ) {
481 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
486 if ( $shard !==
null ) {
489 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
492 $status->merge( $this->
doPrepareInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
511 final protected function doSecure( array $params ) {
517 if ( $dir ===
null ) {
518 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
523 if ( $shard !==
null ) {
526 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
529 $status->merge( $this->
doSecureInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
554 if ( $dir ===
null ) {
555 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
560 if ( $shard !==
null ) {
563 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
566 $status->merge( $this->
doPublishInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
585 final protected function doClean( array $params ) {
593 if ( $subDirsRel !==
null ) {
594 foreach ( $subDirsRel as $subDirRel ) {
595 $subDir = $params[
'dir'] .
"/{$subDirRel}";
596 $status->merge( $this->
doClean( [
'dir' => $subDir ] + $params ) );
598 unset( $subDirsRel );
603 if ( $dir ===
null ) {
604 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
610 $filesLockEx = [ $params[
'dir'] ];
612 $scopedLockE = $this->
getScopedFileLocks( $filesLockEx, LockManager::LOCK_EX, $status );
613 if ( !$status->isOK() ) {
617 if ( $shard !==
null ) {
621 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
624 $status->merge( $this->
doCleanInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
649 if ( is_array( $stat ) ) {
653 return ( $stat === self::$RES_ABSENT ) ? false : self::EXISTENCE_ERROR;
661 if ( is_array( $stat ) ) {
662 return $stat[
'mtime'];
665 return self::TIMESTAMP_FAIL;
673 if ( is_array( $stat ) ) {
674 return $stat[
'size'];
677 return self::SIZE_FAIL;
685 if (
$path ===
null ) {
686 return self::STAT_ERROR;
691 $latest = !empty( $params[
'latest'] );
693 $requireSHA1 = !empty( $params[
'requireSHA1'] );
695 $stat = $this->cheapCache->getField(
$path,
'stat', self::CACHE_TTL );
704 ( $requireSHA1 && is_array( $stat ) && !isset( $stat[
'sha1'] ) )
708 $stat = $this->cheapCache->getField(
$path,
'stat', self::CACHE_TTL );
712 if ( is_array( $stat ) ) {
714 ( !$latest || $stat[
'latest'] ) &&
715 ( !$requireSHA1 || isset( $stat[
'sha1'] ) )
719 } elseif ( $stat === self::$ABSENT_LATEST ) {
720 return self::STAT_ABSENT;
721 } elseif ( $stat === self::$ABSENT_NORMAL ) {
723 return self::STAT_ABSENT;
731 if ( is_array( $stat ) ) {
735 return ( $stat === self::$RES_ERROR ) ? self::STAT_ERROR : self::STAT_ABSENT;
748 foreach ( $stats as
$path => $stat ) {
749 if ( is_array( $stat ) ) {
751 $stat[
'latest'] = $stat[
'latest'] ?? $latest;
753 $this->cheapCache->setField(
$path,
'stat', $stat );
754 if ( isset( $stat[
'sha1'] ) ) {
756 $this->cheapCache->setField(
759 [
'hash' => $stat[
'sha1'],
'latest' => $latest ]
762 if ( isset( $stat[
'xattr'] ) ) {
765 $this->cheapCache->setField(
768 [
'map' => $stat[
'xattr'],
'latest' => $latest ]
773 } elseif ( $stat === self::$RES_ABSENT ) {
774 $this->cheapCache->setField(
779 $this->cheapCache->setField(
782 [
'map' => self::XATTRS_FAIL,
'latest' => $latest ]
784 $this->cheapCache->setField(
787 [
'hash' => self::SHA1_FAIL,
'latest' => $latest ]
789 $this->logger->debug(
790 __METHOD__ .
': File {path} does not exist',
795 $this->logger->error(
796 __METHOD__ .
': Could not stat file {path}',
819 $contents[
$path] = self::CONTENT_FAIL;
835 if ( $fsFile instanceof
FSFile ) {
836 AtEase::suppressWarnings();
837 $content = file_get_contents( $fsFile->getPath() );
838 AtEase::restoreWarnings();
840 } elseif ( $fsFile === self::$RES_ABSENT ) {
855 if (
$path ===
null ) {
856 return self::XATTRS_FAIL;
858 $latest = !empty( $params[
'latest'] );
859 if ( $this->cheapCache->hasField(
$path,
'xattr', self::CACHE_TTL ) ) {
860 $stat = $this->cheapCache->getField(
$path,
'xattr' );
863 if ( !$latest || $stat[
'latest'] ) {
868 if ( is_array( $fields ) ) {
870 $this->cheapCache->setField(
873 [
'map' => $fields,
'latest' => $latest ]
875 } elseif ( $fields === self::$RES_ABSENT ) {
876 $this->cheapCache->setField(
879 [
'map' => self::XATTRS_FAIL,
'latest' => $latest ]
882 $fields = self::XATTRS_FAIL;
895 return [
'headers' => [],
'metadata' => [] ];
903 if (
$path ===
null ) {
904 return self::SHA1_FAIL;
906 $latest = !empty( $params[
'latest'] );
907 if ( $this->cheapCache->hasField(
$path,
'sha1', self::CACHE_TTL ) ) {
908 $stat = $this->cheapCache->getField(
$path,
'sha1' );
911 if ( !$latest || $stat[
'latest'] ) {
912 return $stat[
'hash'];
916 if ( is_string( $sha1 ) ) {
917 $this->cheapCache->setField(
920 [
'hash' => $sha1,
'latest' => $latest ]
922 } elseif ( $sha1 === self::$RES_ABSENT ) {
923 $this->cheapCache->setField(
926 [
'hash' => self::SHA1_FAIL,
'latest' => $latest ]
929 $sha1 = self::SHA1_FAIL;
943 if ( $fsFile instanceof
FSFile ) {
944 $sha1 = $fsFile->getSha1Base36();
968 $latest = !empty( $params[
'latest'] );
970 foreach ( $params[
'srcs'] as $src ) {
972 if (
$path ===
null ) {
973 $fsFiles[$src] =
null;
974 } elseif ( $this->expensiveCache->hasField(
$path,
'localRef' ) ) {
975 $val = $this->expensiveCache->getField(
$path,
'localRef' );
978 if ( !$latest || $val[
'latest'] ) {
979 $fsFiles[$src] = $val[
'object'];
984 $params[
'srcs'] = array_diff( $params[
'srcs'], array_keys( $fsFiles ) );
986 if ( $fsFile instanceof
FSFile ) {
987 $fsFiles[
$path] = $fsFile;
988 $this->expensiveCache->setField(
991 [
'object' => $fsFile,
'latest' => $latest ]
994 $fsFiles[
$path] =
null;
1017 foreach ( $tmpFiles as
$path => $tmpFile ) {
1019 $tmpFiles[
$path] =
null;
1040 return self::TEMPURL_ERROR;
1049 $params[
'options'] = $params[
'options'] ?? [];
1050 $params[
'headers'] = $params[
'headers'] ?? [];
1053 if ( ( empty( $params[
'headless'] ) || $params[
'headers'] ) && headers_sent() ) {
1054 print "Headers already sent, terminating.\n";
1055 $status->fatal(
'backend-fail-stream', $params[
'src'] );
1082 'obResetFunc' => $this->obResetFunc,
1083 'streamMimeFunc' => $this->streamMimeFunc
1086 $res = $streamer->stream( $params[
'headers'],
true, $params[
'options'], $flags );
1093 $status->fatal(
'backend-fail-stream', $params[
'src'] );
1101 if ( $dir ===
null ) {
1102 return self::EXISTENCE_ERROR;
1104 if ( $shard !==
null ) {
1107 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
1112 if ( $exists ===
true ) {
1115 } elseif ( $exists === self::$RES_ERROR ) {
1116 $res = self::EXISTENCE_ERROR;
1136 if ( $dir ===
null ) {
1137 return self::EXISTENCE_ERROR;
1139 if ( $shard !==
null ) {
1143 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
1166 if ( $dir ===
null ) {
1167 return self::LIST_ERROR;
1169 if ( $shard !==
null ) {
1173 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
1207 'store' => StoreFileOp::class,
1208 'copy' => CopyFileOp::class,
1209 'move' => MoveFileOp::class,
1210 'delete' => DeleteFileOp::class,
1211 'create' => CreateFileOp::class,
1212 'describe' => DescribeFileOp::class,
1213 'null' => NullFileOp::class
1218 foreach ( $ops as $operation ) {
1219 $opName = $operation[
'op'];
1220 if ( isset( $supportedOps[$opName] ) ) {
1221 $class = $supportedOps[$opName];
1223 $params = $operation;
1225 $performOps[] =
new $class( $this, $params, $this->logger );
1246 $paths = [
'sh' => [],
'ex' => [] ];
1247 foreach ( $performOps as $fileOp ) {
1248 $paths[
'sh'] = array_merge( $paths[
'sh'], $fileOp->storagePathsRead() );
1249 $paths[
'ex'] = array_merge( $paths[
'ex'], $fileOp->storagePathsChanged() );
1252 $paths[
'sh'] = array_diff( $paths[
'sh'], $paths[
'ex'] );
1254 $paths[
'sh'] = array_merge( $paths[
'sh'], array_map(
'dirname', $paths[
'ex'] ) );
1257 LockManager::LOCK_UW => $paths[
'sh'],
1258 LockManager::LOCK_EX => $paths[
'ex']
1274 $ops = array_map( [ $this,
'sanitizeOpHeaders' ], $ops );
1278 foreach ( $fileOps as $fileOp ) {
1279 $pathsUsed = array_merge( $pathsUsed, $fileOp->storagePathsReadOrChanged() );
1283 if ( empty( $opts[
'nonLocking'] ) ) {
1287 if ( !$status->isOK() ) {
1293 if ( empty( $opts[
'preserveCache'] ) ) {
1298 $this->cheapCache->setMaxSize( max( 2 * count( $pathsUsed ), self::CACHE_CHEAP_SIZE ) );
1303 $ok = $this->
preloadFileStat( [
'srcs' => $pathsUsed,
'latest' =>
true ] );
1311 $subStatus = $this->
newStatus(
'backend-fail-internal', $this->name );
1312 foreach ( $ops as $i => $op ) {
1313 $subStatus->success[$i] =
false;
1314 ++$subStatus->failCount;
1316 $this->logger->error( static::class .
"-{$this->name} " .
1317 " stat failure; aborted operations: " . FormatJson::encode( $ops ) );
1321 $status->merge( $subStatus );
1322 $status->success = $subStatus->success;
1325 $this->cheapCache->setMaxSize( self::CACHE_CHEAP_SIZE );
1336 $ops = array_map( [ $this,
'sanitizeOpHeaders' ], $ops );
1340 foreach ( $fileOps as $fileOp ) {
1341 $pathsUsed = array_merge( $pathsUsed, $fileOp->storagePathsReadOrChanged() );
1348 $async = ( $this->parallelize ===
'implicit' && count( $ops ) > 1 );
1352 $fileOpHandles = [];
1353 $curFileOpHandles = [];
1355 foreach ( $fileOps as $index => $fileOp ) {
1357 ? $fileOp->attemptAsyncQuick()
1358 : $fileOp->attemptQuick();
1360 if ( count( $curFileOpHandles ) >= $maxConcurrency ) {
1361 $fileOpHandles[] = $curFileOpHandles;
1362 $curFileOpHandles = [];
1364 $curFileOpHandles[$index] = $subStatus->value;
1366 $statuses[$index] = $subStatus;
1369 if ( count( $curFileOpHandles ) ) {
1370 $fileOpHandles[] = $curFileOpHandles;
1373 foreach ( $fileOpHandles as $fileHandleBatch ) {
1377 foreach ( $statuses as $index => $subStatus ) {
1378 $status->merge( $subStatus );
1379 if ( $subStatus->isOK() ) {
1380 $status->success[$index] =
true;
1381 ++$status->successCount;
1383 $status->success[$index] =
false;
1384 ++$status->failCount;
1406 foreach ( $fileOpHandles as $fileOpHandle ) {
1408 throw new InvalidArgumentException(
"Expected FileBackendStoreOpHandle object." );
1409 } elseif ( $fileOpHandle->backend->getName() !== $this->getName() ) {
1410 throw new InvalidArgumentException(
"Expected handle for this file backend." );
1415 foreach ( $fileOpHandles as $fileOpHandle ) {
1416 $fileOpHandle->closeResources();
1432 if ( count( $fileOpHandles ) ) {
1433 throw new FileBackendError(
"Backend does not support asynchronous operations." );
1451 static $longs = [
'content-disposition' ];
1453 if ( isset( $op[
'headers'] ) ) {
1455 foreach ( $op[
'headers'] as
$name => $value ) {
1457 $maxHVLen = in_array(
$name, $longs ) ? INF : 255;
1458 if ( strlen(
$name ) > 255 || strlen( $value ) > $maxHVLen ) {
1459 $this->logger->error(
"Header '{header}' is too long.", [
1460 'filebackend' => $this->name,
1461 'header' =>
"$name: $value",
1464 $newHeaders[
$name] = strlen( $value ) ? $value :
'';
1467 $op[
'headers'] = $newHeaders;
1475 foreach ( $paths as
$path ) {
1477 $fullConts[] = $fullCont;
1485 if ( is_array( $paths ) ) {
1486 $paths = array_map(
'FileBackend::normalizeStoragePath', $paths );
1487 $paths = array_filter( $paths,
'strlen' );
1489 if ( $paths ===
null ) {
1490 $this->cheapCache->clear();
1491 $this->expensiveCache->clear();
1493 foreach ( $paths as
$path ) {
1494 $this->cheapCache->clear(
$path );
1495 $this->expensiveCache->clear(
$path );
1516 $params[
'concurrency'] = ( $this->parallelize !==
'off' ) ? $this->concurrency : 1;
1518 if ( $stats ===
null ) {
1523 $latest = !empty( $params[
'latest'] );
1585 return (
bool)preg_match(
'/^[a-z0-9][a-z0-9-_.]{0,199}$/i', $container );
1603 if ( $backend === $this->name ) {
1605 if ( $relPath !==
null && self::isValidShortContainerName( $shortCont ) ) {
1610 if ( $relPath !==
null ) {
1613 if ( self::isValidContainerName( $container ) ) {
1616 if ( $container !==
null ) {
1617 return [ $container, $relPath, $cShard ];
1624 return [
null,
null, null ];
1644 if ( $cShard !==
null && substr( $relPath, -1 ) !==
'/' ) {
1645 return [ $container, $relPath ];
1648 return [
null, null ];
1661 if ( $levels == 1 || $levels == 2 ) {
1663 $char = (
$base == 36 ) ?
'[0-9a-z]' :
'[0-9a-f]';
1666 if ( $levels === 1 ) {
1667 $hashDirRegex =
'(' . $char .
')';
1670 $hashDirRegex = $char .
'/(' . $char .
'{2})';
1672 $hashDirRegex =
'(' . $char .
')/(' . $char .
')';
1679 if ( preg_match(
"!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) {
1680 return '.' . implode(
'', array_slice( $m, 1 ) );
1700 return ( $shard !==
null );
1712 if ( isset( $this->shardViaHashLevels[$container] ) ) {
1713 $config = $this->shardViaHashLevels[$container];
1714 $hashLevels = (int)$config[
'levels'];
1715 if ( $hashLevels == 1 || $hashLevels == 2 ) {
1716 $hashBase = (int)$config[
'base'];
1717 if ( $hashBase == 16 || $hashBase == 36 ) {
1718 return [ $hashLevels, $hashBase, $config[
'repeat'] ];
1723 return [ 0, 0, false ];
1735 if ( $digits > 0 ) {
1736 $numShards =
$base ** $digits;
1737 for ( $index = 0; $index < $numShards; $index++ ) {
1738 $shards[] =
'.' . Wikimedia\base_convert( $index, 10,
$base, $digits );
1752 if ( $this->domainId !=
'' ) {
1753 return "{$this->domainId}-$container";
1784 return $relStoragePath;
1794 return "filebackend:{$this->name}:{$this->domainId}:container:{$container}";
1804 $this->memCache->set( $this->
containerCacheKey( $container ), $val, 14 * 86400 );
1814 if ( !$this->memCache->delete( $this->containerCacheKey( $container ), 300 ) ) {
1815 $this->logger->warning(
"Unable to delete stat cache for container {container}.",
1816 [
'filebackend' => $this->name,
'container' => $container ]
1835 foreach ( $items as $item ) {
1836 if ( self::isStoragePath( $item ) ) {
1838 } elseif ( is_string( $item ) ) {
1843 foreach ( $paths as
$path ) {
1845 if ( $fullCont !==
null ) {
1852 $values = $this->memCache->getMulti( array_keys( $contNames ) );
1853 foreach ( $values as $cacheKey => $val ) {
1854 $contInfo[$contNames[$cacheKey]] = $val;
1879 return "filebackend:{$this->name}:{$this->domainId}:file:" . sha1(
$path );
1892 if (
$path ===
null ) {
1895 $mtime = ConvertibleTimestamp::convert( TS_UNIX, $val[
'mtime'] );
1896 $ttl = $this->memCache->adaptiveTTL( $mtime, 7 * 86400, 300, 0.1 );
1899 $this->memCache->set( $key, $val, $ttl );
1912 if (
$path ===
null ) {
1915 if ( !$this->memCache->delete( $this->fileCacheKey(
$path ), 300 ) ) {
1916 $this->logger->warning(
"Unable to delete stat cache for file {path}.",
1917 [
'filebackend' => $this->name,
'path' =>
$path ]
1936 foreach ( $items as $item ) {
1937 if ( self::isStoragePath( $item ) ) {
1942 $paths = array_filter( $paths,
'strlen' );
1944 foreach ( $paths as
$path ) {
1946 if ( $rel !==
null ) {
1952 $values = $this->memCache->getMulti( array_keys( $pathNames ) );
1954 foreach ( array_filter( $values,
'is_array' ) as $cacheKey => $stat ) {
1955 $path = $pathNames[$cacheKey];
1958 unset( $stat[
'latest'] );
1960 $this->cheapCache->setField(
$path,
'stat', $stat );
1961 if ( isset( $stat[
'sha1'] ) && strlen( $stat[
'sha1'] ) == 31 ) {
1963 $this->cheapCache->setField(
1966 [
'hash' => $stat[
'sha1'],
'latest' =>
false ]
1969 if ( isset( $stat[
'xattr'] ) && is_array( $stat[
'xattr'] ) ) {
1972 $this->cheapCache->setField(
1975 [
'map' => $stat[
'xattr'],
'latest' =>
false ]
1989 $newXAttr = [
'headers' => [],
'metadata' => [] ];
1991 foreach ( $xattr[
'headers'] as
$name => $value ) {
1992 $newXAttr[
'headers'][strtolower(
$name )] = $value;
1995 foreach ( $xattr[
'metadata'] as
$name => $value ) {
1996 $newXAttr[
'metadata'][strtolower(
$name )] = $value;
2009 $opts[
'concurrency'] = 1;
2010 if ( $this->parallelize ===
'implicit' ) {
2011 if ( $opts[
'parallelize'] ??
true ) {
2014 } elseif ( $this->parallelize ===
'explicit' ) {
2015 if ( !empty( $opts[
'parallelize'] ) ) {
2033 if ( $this->mimeCallback ) {
2034 return call_user_func_array( $this->mimeCallback, func_get_args() );
2037 $mime = ( $fsPath !== null ) ? mime_content_type( $fsPath ) :
false;
2038 return $mime ?:
'unknown/unknown';
Class representing a cache/ephemeral data store.
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.
containerCacheKey( $container)
Get the cache key for a container.
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 Stable to override.
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 Stable to override.
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 Stable to override.
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)
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.
fileCacheKey( $path)
Get the cache key for a file path.
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, FileJournal $journal)
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.
Multi-datacenter aware caching interface.
while(( $__line=Maintenance::readconsole()) !==false) print