24use Wikimedia\AtEase\AtEase;
25use Wikimedia\Timestamp\ConvertibleTimestamp;
84 parent::__construct( $config );
85 $this->mimeCallback = $config[
'mimeCallback'] ??
null;
87 $this->memCache = WANObjectCache::newEmpty();
88 $this->cheapCache =
new MapCacheLRU( self::CACHE_CHEAP_SIZE );
89 $this->expensiveCache =
new MapCacheLRU( self::CACHE_EXPENSIVE_SIZE );
138 $status = $this->
newStatus(
'backend-fail-maxsize',
143 if ( !isset( $params[
'dstExists'] ) || $params[
'dstExists'] ) {
181 $status = $this->
newStatus(
'backend-fail-maxsize',
186 if ( !isset( $params[
'dstExists'] ) || $params[
'dstExists'] ) {
226 if ( !isset( $params[
'dstExists'] ) || $params[
'dstExists'] ) {
295 $this->
clearCache( [ $params[
'src'], $params[
'dst'] ] );
297 if ( !isset( $params[
'dstExists'] ) || $params[
'dstExists'] ) {
310 unset( $params[
'async'] );
315 if ( $nsrc !== $ndst && $status->isOK() ) {
317 $status->merge( $this->
deleteInternal( [
'src' => $params[
'src'] ] ) );
318 $status->setResult(
true, $status->value );
342 if ( count( $params[
'headers'] ) ) {
380 $scopeLockS = $this->
getScopedFileLocks( $params[
'srcs'], LockManager::LOCK_UW, $status );
381 if ( $status->isOK() ) {
383 $start_time = microtime(
true );
385 $sec = microtime(
true ) - $start_time;
386 if ( !$status->isOK() ) {
387 $this->logger->error( static::class .
"-{$this->name}" .
388 " failed to concatenate " . count( $params[
'srcs'] ) .
" file(s) [$sec sec]" );
402 $tmpPath = $params[
'dst'];
403 unset( $params[
'latest'] );
406 AtEase::suppressWarnings();
407 $ok = ( is_file( $tmpPath ) && filesize( $tmpPath ) == 0 );
408 AtEase::restoreWarnings();
410 $status->fatal(
'backend-fail-opentemp', $tmpPath );
417 foreach ( $fsFiles as
$path => &$fsFile ) {
421 $status->fatal(
'backend-fail-read',
$path );
430 $tmpHandle = fopen( $tmpPath,
'ab' );
431 if ( $tmpHandle ===
false ) {
432 $status->fatal(
'backend-fail-opentemp', $tmpPath );
438 foreach ( $fsFiles as $virtualSource => $fsFile ) {
440 $sourceHandle = fopen( $fsFile->getPath(),
'rb' );
441 if ( $sourceHandle ===
false ) {
442 fclose( $tmpHandle );
443 $status->fatal(
'backend-fail-read', $virtualSource );
448 if ( !stream_copy_to_stream( $sourceHandle, $tmpHandle ) ) {
449 fclose( $sourceHandle );
450 fclose( $tmpHandle );
451 $status->fatal(
'backend-fail-writetemp', $tmpPath );
455 fclose( $sourceHandle );
457 if ( !fclose( $tmpHandle ) ) {
458 $status->fatal(
'backend-fail-closetemp', $tmpPath );
474 if ( $dir ===
null ) {
475 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
480 if ( $shard !==
null ) {
483 $this->logger->debug( __METHOD__ .
": iterating over all container shards.\n" );
486 $status->merge( $this->
doPrepareInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
504 final protected function doSecure( array $params ) {
510 if ( $dir ===
null ) {
511 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
516 if ( $shard !==
null ) {
519 $this->logger->debug( __METHOD__ .
": iterating over all container shards.\n" );
522 $status->merge( $this->
doSecureInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
546 if ( $dir ===
null ) {
547 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
552 if ( $shard !==
null ) {
555 $this->logger->debug( __METHOD__ .
": iterating over all container shards.\n" );
558 $status->merge( $this->
doPublishInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
576 final protected function doClean( array $params ) {
584 if ( $subDirsRel !==
null ) {
585 foreach ( $subDirsRel as $subDirRel ) {
586 $subDir = $params[
'dir'] .
"/{$subDirRel}";
587 $status->merge( $this->
doClean( [
'dir' => $subDir ] + $params ) );
589 unset( $subDirsRel );
594 if ( $dir ===
null ) {
595 $status->fatal(
'backend-fail-invalidpath', $params[
'dir'] );
601 $filesLockEx = [ $params[
'dir'] ];
603 $scopedLockE = $this->
getScopedFileLocks( $filesLockEx, LockManager::LOCK_EX, $status );
604 if ( !$status->isOK() ) {
608 if ( $shard !==
null ) {
612 $this->logger->debug( __METHOD__ .
": iterating over all container shards.\n" );
615 $status->merge( $this->
doCleanInternal(
"{$fullCont}{$suffix}", $dir, $params ) );
639 if ( is_array( $stat ) ) {
643 return ( $stat === self::$RES_ABSENT ) ? false : self::EXISTENCE_ERROR;
651 if ( is_array( $stat ) ) {
652 return $stat[
'mtime'];
655 return self::TIMESTAMP_FAIL;
663 if ( is_array( $stat ) ) {
664 return $stat[
'size'];
667 return self::SIZE_FAIL;
675 if (
$path ===
null ) {
676 return self::STAT_ERROR;
681 $latest = !empty( $params[
'latest'] );
683 $requireSHA1 = !empty( $params[
'requireSHA1'] );
685 $stat = $this->cheapCache->getField(
$path,
'stat', self::CACHE_TTL );
694 ( $requireSHA1 && is_array( $stat ) && !isset( $stat[
'sha1'] ) )
698 $stat = $this->cheapCache->getField(
$path,
'stat', self::CACHE_TTL );
702 if ( is_array( $stat ) ) {
704 ( !$latest || $stat[
'latest'] ) &&
705 ( !$requireSHA1 || isset( $stat[
'sha1'] ) )
709 } elseif ( $stat === self::$ABSENT_LATEST ) {
710 return self::STAT_ABSENT;
711 } elseif ( $stat === self::$ABSENT_NORMAL ) {
713 return self::STAT_ABSENT;
721 if ( is_array( $stat ) ) {
725 return ( $stat === self::$RES_ERROR ) ? self::STAT_ERROR : self::STAT_ABSENT;
738 foreach ( $stats as
$path => $stat ) {
739 if ( is_array( $stat ) ) {
741 $stat[
'latest'] = $stat[
'latest'] ?? $latest;
743 $this->cheapCache->setField(
$path,
'stat', $stat );
744 if ( isset( $stat[
'sha1'] ) ) {
746 $this->cheapCache->setField(
749 [
'hash' => $stat[
'sha1'],
'latest' => $latest ]
752 if ( isset( $stat[
'xattr'] ) ) {
755 $this->cheapCache->setField(
758 [
'map' => $stat[
'xattr'],
'latest' => $latest ]
763 } elseif ( $stat === self::$RES_ABSENT ) {
764 $this->cheapCache->setField(
769 $this->cheapCache->setField(
772 [
'map' => self::XATTRS_FAIL,
'latest' => $latest ]
774 $this->cheapCache->setField(
777 [
'hash' => self::SHA1_FAIL,
'latest' => $latest ]
779 $this->logger->debug(
780 __METHOD__ .
': File {path} does not exist',
785 $this->logger->error(
786 __METHOD__ .
': Could not stat file {path}',
809 $contents[
$path] = self::CONTENT_FAIL;
824 if ( $fsFile instanceof
FSFile ) {
825 AtEase::suppressWarnings();
826 $content = file_get_contents( $fsFile->getPath() );
827 AtEase::restoreWarnings();
829 } elseif ( $fsFile === self::$RES_ABSENT ) {
844 if (
$path ===
null ) {
845 return self::XATTRS_FAIL;
847 $latest = !empty( $params[
'latest'] );
848 if ( $this->cheapCache->hasField(
$path,
'xattr', self::CACHE_TTL ) ) {
849 $stat = $this->cheapCache->getField(
$path,
'xattr' );
852 if ( !$latest || $stat[
'latest'] ) {
857 if ( is_array( $fields ) ) {
859 $this->cheapCache->setField(
862 [
'map' => $fields,
'latest' => $latest ]
864 } elseif ( $fields === self::$RES_ABSENT ) {
865 $this->cheapCache->setField(
868 [
'map' => self::XATTRS_FAIL,
'latest' => $latest ]
871 $fields = self::XATTRS_FAIL;
883 return [
'headers' => [],
'metadata' => [] ];
891 if (
$path ===
null ) {
892 return self::SHA1_FAIL;
894 $latest = !empty( $params[
'latest'] );
895 if ( $this->cheapCache->hasField(
$path,
'sha1', self::CACHE_TTL ) ) {
896 $stat = $this->cheapCache->getField(
$path,
'sha1' );
899 if ( !$latest || $stat[
'latest'] ) {
900 return $stat[
'hash'];
904 if ( is_string( $sha1 ) ) {
905 $this->cheapCache->setField(
908 [
'hash' => $sha1,
'latest' => $latest ]
910 } elseif ( $sha1 === self::$RES_ABSENT ) {
911 $this->cheapCache->setField(
914 [
'hash' => self::SHA1_FAIL,
'latest' => $latest ]
917 $sha1 = self::SHA1_FAIL;
930 if ( $fsFile instanceof
FSFile ) {
931 $sha1 = $fsFile->getSha1Base36();
955 $latest = !empty( $params[
'latest'] );
957 foreach ( $params[
'srcs'] as $src ) {
959 if (
$path ===
null ) {
960 $fsFiles[$src] =
null;
961 } elseif ( $this->expensiveCache->hasField(
$path,
'localRef' ) ) {
962 $val = $this->expensiveCache->getField(
$path,
'localRef' );
965 if ( !$latest || $val[
'latest'] ) {
966 $fsFiles[$src] = $val[
'object'];
971 $params[
'srcs'] = array_diff( $params[
'srcs'], array_keys( $fsFiles ) );
973 if ( $fsFile instanceof
FSFile ) {
974 $fsFiles[
$path] = $fsFile;
975 $this->expensiveCache->setField(
978 [
'object' => $fsFile,
'latest' => $latest ]
981 $fsFiles[
$path] =
null;
1003 foreach ( $tmpFiles as
$path => $tmpFile ) {
1005 $tmpFiles[
$path] =
null;
1025 return self::TEMPURL_ERROR;
1034 $params[
'options'] = $params[
'options'] ?? [];
1035 $params[
'headers'] = $params[
'headers'] ?? [];
1038 if ( ( empty( $params[
'headless'] ) || $params[
'headers'] ) && headers_sent() ) {
1039 print "Headers already sent, terminating.\n";
1040 $status->fatal(
'backend-fail-stream', $params[
'src'] );
1066 'obResetFunc' => $this->obResetFunc,
1067 'streamMimeFunc' => $this->streamMimeFunc
1070 $res = $streamer->stream( $params[
'headers'],
true, $params[
'options'], $flags );
1077 $status->fatal(
'backend-fail-stream', $params[
'src'] );
1085 if ( $dir ===
null ) {
1086 return self::EXISTENCE_ERROR;
1088 if ( $shard !==
null ) {
1091 $this->logger->debug( __METHOD__ .
": iterating over all container shards.\n" );
1096 if ( $exists ===
true ) {
1099 } elseif ( $exists === self::$RES_ERROR ) {
1100 $res = self::EXISTENCE_ERROR;
1120 if ( $dir ===
null ) {
1121 return self::EXISTENCE_ERROR;
1123 if ( $shard !==
null ) {
1127 $this->logger->debug( __METHOD__ .
": iterating over all container shards.\n" );
1150 if ( $dir ===
null ) {
1151 return self::LIST_ERROR;
1153 if ( $shard !==
null ) {
1157 $this->logger->debug( __METHOD__ .
": iterating over all container shards.\n" );
1191 'store' => StoreFileOp::class,
1192 'copy' => CopyFileOp::class,
1193 'move' => MoveFileOp::class,
1194 'delete' => DeleteFileOp::class,
1195 'create' => CreateFileOp::class,
1196 'describe' => DescribeFileOp::class,
1197 'null' => NullFileOp::class
1202 foreach ( $ops as $operation ) {
1203 $opName = $operation[
'op'];
1204 if ( isset( $supportedOps[$opName] ) ) {
1205 $class = $supportedOps[$opName];
1207 $params = $operation;
1209 $performOps[] =
new $class( $this, $params, $this->logger );
1230 $paths = [
'sh' => [],
'ex' => [] ];
1231 foreach ( $performOps as $fileOp ) {
1232 $paths[
'sh'] = array_merge( $paths[
'sh'], $fileOp->storagePathsRead() );
1233 $paths[
'ex'] = array_merge( $paths[
'ex'], $fileOp->storagePathsChanged() );
1236 $paths[
'sh'] = array_diff( $paths[
'sh'], $paths[
'ex'] );
1238 $paths[
'sh'] = array_merge( $paths[
'sh'], array_map(
'dirname', $paths[
'ex'] ) );
1241 LockManager::LOCK_UW => $paths[
'sh'],
1242 LockManager::LOCK_EX => $paths[
'ex']
1258 $ops = array_map( [ $this,
'sanitizeOpHeaders' ], $ops );
1264 if ( empty( $opts[
'nonLocking'] ) ) {
1270 if ( !$status->isOK() ) {
1276 if ( empty( $opts[
'preserveCache'] ) ) {
1282 foreach ( $performOps as $performOp ) {
1283 $paths = array_merge( $paths, $performOp->storagePathsRead() );
1284 $paths = array_merge( $paths, $performOp->storagePathsChanged() );
1288 $this->cheapCache->setMaxSize( max( 2 * count( $paths ), self::CACHE_CHEAP_SIZE ) );
1293 $ok = $this->
preloadFileStat( [
'srcs' => $paths,
'latest' =>
true ] );
1301 $subStatus = $this->
newStatus(
'backend-fail-internal', $this->name );
1302 foreach ( $ops as $i => $op ) {
1303 $subStatus->success[$i] =
false;
1304 ++$subStatus->failCount;
1306 $this->logger->error( static::class .
"-{$this->name} " .
1307 " stat failure; aborted operations: " . FormatJson::encode( $ops ) );
1311 $status->merge( $subStatus );
1312 $status->success = $subStatus->success;
1315 $this->cheapCache->setMaxSize( self::CACHE_CHEAP_SIZE );
1326 $ops = array_map( [ $this,
'sanitizeOpHeaders' ], $ops );
1331 $supportedOps = [
'create',
'store',
'copy',
'move',
'delete',
'describe',
'null' ];
1333 $async = ( $this->parallelize ===
'implicit' && count( $ops ) > 1 );
1337 $fileOpHandles = [];
1338 $curFileOpHandles = [];
1340 foreach ( $ops as $index => $params ) {
1341 if ( !in_array( $params[
'op'], $supportedOps ) ) {
1342 throw new FileBackendError(
"Operation '{$params['op']}' is not supported." );
1344 $method = $params[
'op'] .
'Internal';
1345 $subStatus = $this->$method( [
'async' => $async ] + $params );
1347 if ( count( $curFileOpHandles ) >= $maxConcurrency ) {
1348 $fileOpHandles[] = $curFileOpHandles;
1349 $curFileOpHandles = [];
1351 $curFileOpHandles[$index] = $subStatus->value;
1353 $statuses[$index] = $subStatus;
1356 if ( count( $curFileOpHandles ) ) {
1357 $fileOpHandles[] = $curFileOpHandles;
1360 foreach ( $fileOpHandles as $fileHandleBatch ) {
1364 foreach ( $statuses as $index => $subStatus ) {
1365 $status->merge( $subStatus );
1366 if ( $subStatus->isOK() ) {
1367 $status->success[$index] =
true;
1368 ++$status->successCount;
1370 $status->success[$index] =
false;
1371 ++$status->failCount;
1391 foreach ( $fileOpHandles as $fileOpHandle ) {
1393 throw new InvalidArgumentException(
"Expected FileBackendStoreOpHandle object." );
1394 } elseif ( $fileOpHandle->backend->getName() !== $this->getName() ) {
1395 throw new InvalidArgumentException(
"Expected handle for this file backend." );
1400 foreach ( $fileOpHandles as $fileOpHandle ) {
1401 $fileOpHandle->closeResources();
1416 if ( count( $fileOpHandles ) ) {
1417 throw new FileBackendError(
"Backend does not support asynchronous operations." );
1435 static $longs = [
'content-disposition' ];
1437 if ( isset( $op[
'headers'] ) ) {
1439 foreach ( $op[
'headers'] as
$name => $value ) {
1441 $maxHVLen = in_array(
$name, $longs ) ? INF : 255;
1442 if ( strlen(
$name ) > 255 || strlen( $value ) > $maxHVLen ) {
1443 $this->logger->error(
"Header '{header}' is too long.", [
1444 'filebackend' => $this->name,
1445 'header' =>
"$name: $value",
1448 $newHeaders[
$name] = strlen( $value ) ? $value :
'';
1451 $op[
'headers'] = $newHeaders;
1459 foreach ( $paths as
$path ) {
1461 $fullConts[] = $fullCont;
1469 if ( is_array( $paths ) ) {
1470 $paths = array_map(
'FileBackend::normalizeStoragePath', $paths );
1471 $paths = array_filter( $paths,
'strlen' );
1473 if ( $paths ===
null ) {
1474 $this->cheapCache->clear();
1475 $this->expensiveCache->clear();
1477 foreach ( $paths as
$path ) {
1478 $this->cheapCache->clear(
$path );
1479 $this->expensiveCache->clear(
$path );
1499 $params[
'concurrency'] = ( $this->parallelize !==
'off' ) ? $this->concurrency : 1;
1501 if ( $stats ===
null ) {
1506 $latest = !empty( $params[
'latest'] );
1567 return (
bool)preg_match(
'/^[a-z0-9][a-z0-9-_.]{0,199}$/i', $container );
1585 if ( $backend === $this->name ) {
1587 if ( $relPath !==
null && self::isValidShortContainerName( $shortCont ) ) {
1592 if ( $relPath !==
null ) {
1595 if ( self::isValidContainerName( $container ) ) {
1598 if ( $container !==
null ) {
1599 return [ $container, $relPath, $cShard ];
1606 return [
null,
null, null ];
1626 if ( $cShard !==
null && substr( $relPath, -1 ) !==
'/' ) {
1627 return [ $container, $relPath ];
1630 return [
null, null ];
1643 if ( $levels == 1 || $levels == 2 ) {
1645 $char = (
$base == 36 ) ?
'[0-9a-z]' :
'[0-9a-f]';
1648 if ( $levels === 1 ) {
1649 $hashDirRegex =
'(' . $char .
')';
1652 $hashDirRegex = $char .
'/(' . $char .
'{2})';
1654 $hashDirRegex =
'(' . $char .
')/(' . $char .
')';
1661 if ( preg_match(
"!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) {
1662 return '.' . implode(
'', array_slice( $m, 1 ) );
1682 return ( $shard !==
null );
1694 if ( isset( $this->shardViaHashLevels[$container] ) ) {
1695 $config = $this->shardViaHashLevels[$container];
1696 $hashLevels = (int)$config[
'levels'];
1697 if ( $hashLevels == 1 || $hashLevels == 2 ) {
1698 $hashBase = (int)$config[
'base'];
1699 if ( $hashBase == 16 || $hashBase == 36 ) {
1700 return [ $hashLevels, $hashBase, $config[
'repeat'] ];
1705 return [ 0, 0, false ];
1717 if ( $digits > 0 ) {
1718 $numShards =
$base ** $digits;
1719 for ( $index = 0; $index < $numShards; $index++ ) {
1720 $shards[] =
'.' . Wikimedia\base_convert( $index, 10,
$base, $digits );
1734 if ( $this->domainId !=
'' ) {
1735 return "{$this->domainId}-$container";
1764 return $relStoragePath;
1774 return "filebackend:{$this->name}:{$this->domainId}:container:{$container}";
1784 $this->memCache->set( $this->
containerCacheKey( $container ), $val, 14 * 86400 );
1794 if ( !$this->memCache->delete( $this->containerCacheKey( $container ), 300 ) ) {
1795 $this->logger->warning(
"Unable to delete stat cache for container {container}.",
1796 [
'filebackend' => $this->name,
'container' => $container ]
1815 foreach ( $items as $item ) {
1816 if ( self::isStoragePath( $item ) ) {
1818 } elseif ( is_string( $item ) ) {
1823 foreach ( $paths as
$path ) {
1825 if ( $fullCont !==
null ) {
1832 $values = $this->memCache->getMulti( array_keys( $contNames ) );
1833 foreach ( $values as $cacheKey => $val ) {
1834 $contInfo[$contNames[$cacheKey]] = $val;
1858 return "filebackend:{$this->name}:{$this->domainId}:file:" . sha1(
$path );
1871 if (
$path ===
null ) {
1874 $mtime = ConvertibleTimestamp::convert( TS_UNIX, $val[
'mtime'] );
1875 $ttl = $this->memCache->adaptiveTTL( $mtime, 7 * 86400, 300, 0.1 );
1878 $this->memCache->set( $key, $val, $ttl );
1891 if (
$path ===
null ) {
1894 if ( !$this->memCache->delete( $this->fileCacheKey(
$path ), 300 ) ) {
1895 $this->logger->warning(
"Unable to delete stat cache for file {path}.",
1896 [
'filebackend' => $this->name,
'path' =>
$path ]
1915 foreach ( $items as $item ) {
1916 if ( self::isStoragePath( $item ) ) {
1921 $paths = array_filter( $paths,
'strlen' );
1923 foreach ( $paths as
$path ) {
1925 if ( $rel !==
null ) {
1931 $values = $this->memCache->getMulti( array_keys( $pathNames ) );
1933 foreach ( array_filter( $values,
'is_array' ) as $cacheKey => $stat ) {
1934 $path = $pathNames[$cacheKey];
1937 unset( $stat[
'latest'] );
1939 $this->cheapCache->setField(
$path,
'stat', $stat );
1940 if ( isset( $stat[
'sha1'] ) && strlen( $stat[
'sha1'] ) == 31 ) {
1942 $this->cheapCache->setField(
1945 [
'hash' => $stat[
'sha1'],
'latest' =>
false ]
1948 if ( isset( $stat[
'xattr'] ) && is_array( $stat[
'xattr'] ) ) {
1951 $this->cheapCache->setField(
1954 [
'map' => $stat[
'xattr'],
'latest' =>
false ]
1968 $newXAttr = [
'headers' => [],
'metadata' => [] ];
1970 foreach ( $xattr[
'headers'] as
$name => $value ) {
1971 $newXAttr[
'headers'][strtolower(
$name )] = $value;
1974 foreach ( $xattr[
'metadata'] as
$name => $value ) {
1975 $newXAttr[
'metadata'][strtolower(
$name )] = $value;
1988 $opts[
'concurrency'] = 1;
1989 if ( $this->parallelize ===
'implicit' ) {
1990 if ( !isset( $opts[
'parallelize'] ) || $opts[
'parallelize'] ) {
1993 } elseif ( $this->parallelize ===
'explicit' ) {
1994 if ( !empty( $opts[
'parallelize'] ) ) {
2011 if ( $this->mimeCallback ) {
2012 return call_user_func_array( $this->mimeCallback, func_get_args() );
2015 $mime = ( $fsPath !== null ) ? mime_content_type( $fsPath ) :
false;
2016 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.
doQuickOperationsInternal(array $ops)
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.
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)
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