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 ) {
428 $fsFile === self::$RES_ERROR ?
'backend-fail-read' :
'backend-fail-notexists',
439 $tmpHandle = fopen( $tmpPath,
'ab' );
440 if ( $tmpHandle ===
false ) {
441 $status->fatal(
'backend-fail-opentemp', $tmpPath );
447 foreach ( $fsFiles as $virtualSource => $fsFile ) {
449 $sourceHandle = fopen( $fsFile->getPath(),
'rb' );
450 if ( $sourceHandle ===
false ) {
451 fclose( $tmpHandle );
452 $status->fatal(
'backend-fail-read', $virtualSource );
457 if ( !stream_copy_to_stream( $sourceHandle, $tmpHandle ) ) {
458 fclose( $sourceHandle );
459 fclose( $tmpHandle );
460 $status->fatal(
'backend-fail-writetemp', $tmpPath );
464 fclose( $sourceHandle );
466 if ( !fclose( $tmpHandle ) ) {
467 $status->fatal(
'backend-fail-closetemp', $tmpPath );
486 if ( $dir ===
null ) {
487 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
492 if ( $shard !==
null ) {
495 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
498 $status->merge( $this->
doPrepareInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
517 final protected function doSecure( array $params ) {
523 if ( $dir ===
null ) {
524 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
529 if ( $shard !==
null ) {
532 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
535 $status->merge( $this->
doSecureInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
560 if ( $dir ===
null ) {
561 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
566 if ( $shard !==
null ) {
569 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
572 $status->merge( $this->
doPublishInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
591 final protected function doClean( array $params ) {
599 if ( $subDirsRel !==
null ) {
600 foreach ( $subDirsRel as $subDirRel ) {
601 $subDir = $params[
'dir'] .
"/{$subDirRel}";
602 $status->merge( $this->
doClean( [
'dir' => $subDir ] + $params ) );
604 unset( $subDirsRel );
609 if ( $dir ===
null ) {
610 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
616 $filesLockEx = [ $params[
'dir'] ];
618 $scopedLockE = $this->
getScopedFileLocks( $filesLockEx, LockManager::LOCK_EX, $status );
619 if ( !$status->isOK() ) {
623 if ( $shard !==
null ) {
627 $this->logger->debug( __METHOD__ .
": iterating over all container shards." );
630 $status->merge( $this->
doCleanInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
655 if ( is_array( $stat ) ) {
659 return ( $stat === self::$RES_ABSENT ) ? false : self::EXISTENCE_ERROR;
667 if ( is_array( $stat ) ) {
668 return $stat[
'mtime'];
671 return self::TIMESTAMP_FAIL;
679 if ( is_array( $stat ) ) {
680 return $stat[
'size'];
683 return self::SIZE_FAIL;
691 if (
$path ===
null ) {
692 return self::STAT_ERROR;
697 $latest = !empty( $params[
'latest'] );
699 $requireSHA1 = !empty( $params[
'requireSHA1'] );
701 $stat = $this->cheapCache->getField(
$path,
'stat', self::CACHE_TTL );
710 ( $requireSHA1 && is_array( $stat ) && !isset( $stat[
'sha1'] ) )
714 $stat = $this->cheapCache->getField(
$path,
'stat', self::CACHE_TTL );
718 if ( is_array( $stat ) ) {
720 ( !$latest || !empty( $stat[
'latest'] ) ) &&
721 ( !$requireSHA1 || isset( $stat[
'sha1'] ) )
725 } elseif ( $stat === self::$ABSENT_LATEST ) {
726 return self::STAT_ABSENT;
727 } elseif ( $stat === self::$ABSENT_NORMAL ) {
729 return self::STAT_ABSENT;
737 if ( is_array( $stat ) ) {
741 return ( $stat === self::$RES_ERROR ) ? self::STAT_ERROR : self::STAT_ABSENT;
754 foreach ( $stats as
$path => $stat ) {
755 if ( is_array( $stat ) ) {
757 $stat[
'latest'] ??= $latest;
759 $this->cheapCache->setField(
$path,
'stat', $stat );
760 if ( isset( $stat[
'sha1'] ) ) {
762 $this->cheapCache->setField(
765 [
'hash' => $stat[
'sha1'],
'latest' => $latest ]
768 if ( isset( $stat[
'xattr'] ) ) {
771 $this->cheapCache->setField(
774 [
'map' => $stat[
'xattr'],
'latest' => $latest ]
779 } elseif ( $stat === self::$RES_ABSENT ) {
780 $this->cheapCache->setField(
785 $this->cheapCache->setField(
788 [
'map' => self::XATTRS_FAIL,
'latest' => $latest ]
790 $this->cheapCache->setField(
793 [
'hash' => self::SHA1_FAIL,
'latest' => $latest ]
795 $this->logger->debug(
796 __METHOD__ .
': File {path} does not exist',
801 $this->logger->error(
802 __METHOD__ .
': Could not stat file {path}',
825 $contents[
$path] = self::CONTENT_FAIL;
841 if ( $fsFile instanceof
FSFile ) {
842 AtEase::suppressWarnings();
843 $content = file_get_contents( $fsFile->getPath() );
844 AtEase::restoreWarnings();
848 $contents[
$path] = $fsFile;
860 if (
$path ===
null ) {
861 return self::XATTRS_FAIL;
863 $latest = !empty( $params[
'latest'] );
864 if ( $this->cheapCache->hasField(
$path,
'xattr', self::CACHE_TTL ) ) {
865 $stat = $this->cheapCache->getField(
$path,
'xattr' );
868 if ( !$latest || $stat[
'latest'] ) {
873 if ( is_array( $fields ) ) {
875 $this->cheapCache->setField(
878 [
'map' => $fields,
'latest' => $latest ]
880 } elseif ( $fields === self::$RES_ABSENT ) {
881 $this->cheapCache->setField(
884 [
'map' => self::XATTRS_FAIL,
'latest' => $latest ]
887 $fields = self::XATTRS_FAIL;
900 return [
'headers' => [],
'metadata' => [] ];
908 if (
$path ===
null ) {
909 return self::SHA1_FAIL;
911 $latest = !empty( $params[
'latest'] );
912 if ( $this->cheapCache->hasField(
$path,
'sha1', self::CACHE_TTL ) ) {
913 $stat = $this->cheapCache->getField(
$path,
'sha1' );
916 if ( !$latest || $stat[
'latest'] ) {
917 return $stat[
'hash'];
921 if ( is_string( $sha1 ) ) {
922 $this->cheapCache->setField(
925 [
'hash' => $sha1,
'latest' => $latest ]
927 } elseif ( $sha1 === self::$RES_ABSENT ) {
928 $this->cheapCache->setField(
931 [
'hash' => self::SHA1_FAIL,
'latest' => $latest ]
934 $sha1 = self::SHA1_FAIL;
948 if ( $fsFile instanceof
FSFile ) {
949 $sha1 = $fsFile->getSha1Base36();
973 $latest = !empty( $params[
'latest'] );
975 foreach ( $params[
'srcs'] as $src ) {
977 if (
$path ===
null ) {
979 } elseif ( $this->expensiveCache->hasField(
$path,
'localRef' ) ) {
980 $val = $this->expensiveCache->getField(
$path,
'localRef' );
983 if ( !$latest || $val[
'latest'] ) {
984 $fsFiles[$src] = $val[
'object'];
989 $params[
'srcs'] = array_diff( $params[
'srcs'], array_keys( $fsFiles ) );
991 if ( $fsFile instanceof
FSFile ) {
992 $fsFiles[
$path] = $fsFile;
993 $this->expensiveCache->setField(
996 [
'object' => $fsFile,
'latest' => $latest ]
1000 $fsFiles[
$path] = $fsFile;
1040 return self::TEMPURL_ERROR;
1049 $params[
'options'] ??= [];
1050 $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'] );
1074 $flags |= !empty( $params[
'headless'] ) ? HTTPFileStreamer::STREAM_HEADLESS : 0;
1075 $flags |= !empty( $params[
'allowOB'] ) ? HTTPFileStreamer::STREAM_ALLOW_OB : 0;
1082 'obResetFunc' => $this->obResetFunc,
1083 'streamMimeFunc' => $this->streamMimeFunc
1086 $res = $streamer->stream( $params[
'headers'],
true, $params[
'options'], $flags );
1089 HTTPFileStreamer::send404Message( $params[
'src'], $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 ) {
1364 $curFileOpHandles = [];
1366 $curFileOpHandles[$index] = $subStatus->value;
1368 $statuses[$index] = $subStatus;
1371 if ( count( $curFileOpHandles ) ) {
1372 $fileOpHandles[] = $curFileOpHandles;
1375 foreach ( $fileOpHandles as $fileHandleBatch ) {
1379 foreach ( $statuses as $index => $subStatus ) {
1380 $status->merge( $subStatus );
1381 if ( $subStatus->isOK() ) {
1382 $status->success[$index] =
true;
1383 ++$status->successCount;
1385 $status->success[$index] =
false;
1386 ++$status->failCount;
1408 foreach ( $fileOpHandles as $fileOpHandle ) {
1410 throw new InvalidArgumentException(
"Expected FileBackendStoreOpHandle object." );
1411 } elseif ( $fileOpHandle->backend->getName() !== $this->getName() ) {
1412 throw new InvalidArgumentException(
"Expected handle for this file backend." );
1417 foreach ( $fileOpHandles as $fileOpHandle ) {
1418 $fileOpHandle->closeResources();
1434 if ( count( $fileOpHandles ) ) {
1435 throw new FileBackendError(
"Backend does not support asynchronous operations." );
1453 static $longs = [
'content-disposition' ];
1455 if ( isset( $op[
'headers'] ) ) {
1457 foreach ( $op[
'headers'] as
$name => $value ) {
1459 $maxHVLen = in_array(
$name, $longs ) ? INF : 255;
1460 if ( strlen(
$name ) > 255 || strlen( $value ) > $maxHVLen ) {
1461 $this->logger->error(
"Header '{header}' is too long.", [
1462 'filebackend' => $this->name,
1463 'header' =>
"$name: $value",
1466 $newHeaders[
$name] = strlen( $value ) ? $value :
'';
1469 $op[
'headers'] = $newHeaders;
1477 foreach ( $paths as
$path ) {
1479 $fullConts[] = $fullCont;
1487 if ( is_array( $paths ) ) {
1488 $paths = array_map( [ FileBackend::class,
'normalizeStoragePath' ], $paths );
1489 $paths = array_filter( $paths,
'strlen' );
1491 if ( $paths ===
null ) {
1492 $this->cheapCache->clear();
1493 $this->expensiveCache->clear();
1495 foreach ( $paths as
$path ) {
1496 $this->cheapCache->clear(
$path );
1497 $this->expensiveCache->clear(
$path );
1518 $params[
'concurrency'] = ( $this->parallelize !==
'off' ) ? $this->concurrency : 1;
1520 if ( $stats ===
null ) {
1525 $latest = !empty( $params[
'latest'] );
1587 return (
bool)preg_match(
'/^[a-z0-9][a-z0-9-_.]{0,199}$/i', $container );
1605 if ( $backend === $this->name ) {
1607 if ( $relPath !==
null && self::isValidShortContainerName( $shortCont ) ) {
1612 if ( $relPath !==
null ) {
1615 if ( self::isValidContainerName( $container ) ) {
1618 if ( $container !==
null ) {
1619 return [ $container, $relPath, $cShard ];
1626 return [
null,
null, null ];
1646 if ( $cShard !==
null && substr( $relPath, -1 ) !==
'/' ) {
1647 return [ $container, $relPath ];
1650 return [
null, null ];
1663 if ( $levels == 1 || $levels == 2 ) {
1665 $char = ( $base == 36 ) ?
'[0-9a-z]' :
'[0-9a-f]';
1668 if ( $levels === 1 ) {
1669 $hashDirRegex =
'(' . $char .
')';
1672 $hashDirRegex = $char .
'/(' . $char .
'{2})';
1674 $hashDirRegex =
'(' . $char .
')/(' . $char .
')';
1681 if ( preg_match(
"!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) {
1682 return '.' . implode(
'', array_slice( $m, 1 ) );
1702 return ( $shard !==
null );
1714 if ( isset( $this->shardViaHashLevels[$container] ) ) {
1715 $config = $this->shardViaHashLevels[$container];
1716 $hashLevels = (int)$config[
'levels'];
1717 if ( $hashLevels == 1 || $hashLevels == 2 ) {
1718 $hashBase = (int)$config[
'base'];
1719 if ( $hashBase == 16 || $hashBase == 36 ) {
1720 return [ $hashLevels, $hashBase, $config[
'repeat'] ];
1725 return [ 0, 0, false ];
1737 if ( $digits > 0 ) {
1738 $numShards = $base ** $digits;
1739 for ( $index = 0; $index < $numShards; $index++ ) {
1740 $shards[] =
'.' . Wikimedia\base_convert( (
string)$index, 10, $base, $digits );
1754 if ( $this->domainId !=
'' ) {
1755 return "{$this->domainId}-$container";
1786 return $relStoragePath;
1795 private function containerCacheKey( $container ) {
1796 return "filebackend:{$this->name}:{$this->domainId}:container:{$container}";
1806 if ( !$this->memCache->set( $this->containerCacheKey( $container ), $val, 14 * 86400 ) ) {
1807 $this->logger->warning(
"Unable to set stat cache for container {container}.",
1808 [
'filebackend' => $this->name,
'container' => $container ]
1820 if ( !$this->memCache->delete( $this->containerCacheKey( $container ), 300 ) ) {
1821 $this->logger->warning(
"Unable to delete stat cache for container {container}.",
1822 [
'filebackend' => $this->name,
'container' => $container ]
1841 foreach ( $items as $item ) {
1842 if ( self::isStoragePath( $item ) ) {
1844 } elseif ( is_string( $item ) ) {
1845 $contNames[$this->containerCacheKey( $item )] = $item;
1849 foreach ( $paths as
$path ) {
1851 if ( $fullCont !==
null ) {
1852 $contNames[$this->containerCacheKey( $fullCont )] = $fullCont;
1858 $values = $this->memCache->getMulti( array_keys( $contNames ) );
1859 foreach ( $values as $cacheKey => $val ) {
1860 $contInfo[$contNames[$cacheKey]] = $val;
1884 private function fileCacheKey(
$path ) {
1885 return "filebackend:{$this->name}:{$this->domainId}:file:" . sha1(
$path );
1898 if (
$path ===
null ) {
1901 $mtime = (int)ConvertibleTimestamp::convert( TS_UNIX, $val[
'mtime'] );
1902 $ttl = $this->memCache->adaptiveTTL( $mtime, 7 * 86400, 300, 0.1 );
1903 $key = $this->fileCacheKey(
$path );
1905 if ( !$this->memCache->set( $key, $val, $ttl ) ) {
1906 $this->logger->warning(
"Unable to set stat cache for file {path}.",
1907 [
'filebackend' => $this->name,
'path' =>
$path ]
1922 if (
$path ===
null ) {
1925 if ( !$this->memCache->delete( $this->fileCacheKey(
$path ), 300 ) ) {
1926 $this->logger->warning(
"Unable to delete stat cache for file {path}.",
1927 [
'filebackend' => $this->name,
'path' =>
$path ]
1946 foreach ( $items as $item ) {
1947 if ( self::isStoragePath( $item ) ) {
1949 if (
$path !==
null ) {
1955 foreach ( $paths as
$path ) {
1957 if ( $rel !==
null ) {
1958 $pathNames[$this->fileCacheKey(
$path )] =
$path;
1963 $values = $this->memCache->getMulti( array_keys( $pathNames ) );
1965 foreach ( array_filter( $values,
'is_array' ) as $cacheKey => $stat ) {
1966 $path = $pathNames[$cacheKey];
1969 unset( $stat[
'latest'] );
1971 $this->cheapCache->setField(
$path,
'stat', $stat );
1972 if ( isset( $stat[
'sha1'] ) && strlen( $stat[
'sha1'] ) == 31 ) {
1974 $this->cheapCache->setField(
1977 [
'hash' => $stat[
'sha1'],
'latest' =>
false ]
1980 if ( isset( $stat[
'xattr'] ) && is_array( $stat[
'xattr'] ) ) {
1983 $this->cheapCache->setField(
1986 [
'map' => $stat[
'xattr'],
'latest' =>
false ]
2000 $newXAttr = [
'headers' => [],
'metadata' => [] ];
2002 foreach ( $xattr[
'headers'] as
$name => $value ) {
2003 $newXAttr[
'headers'][strtolower(
$name )] = $value;
2006 foreach ( $xattr[
'metadata'] as
$name => $value ) {
2007 $newXAttr[
'metadata'][strtolower(
$name )] = $value;
2020 $opts[
'concurrency'] = 1;
2021 if ( $this->parallelize ===
'implicit' ) {
2022 if ( $opts[
'parallelize'] ??
true ) {
2025 } elseif ( $this->parallelize ===
'explicit' ) {
2026 if ( !empty( $opts[
'parallelize'] ) ) {
2044 if ( $this->mimeCallback ) {
2045 return call_user_func_array( $this->mimeCallback, func_get_args() );
2048 $mime = ( $fsPath !== null ) ? mime_content_type( $fsPath ) :
false;
2049 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.
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.
Store key-value entries in a size-limited in-memory LRU cache.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Multi-datacenter aware caching interface.