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 );
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'] ];
615 $scopedLockE = $this->
getScopedFileLocks( $filesLockEx, LockManager::LOCK_EX, $status );
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(
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'] ) );
1260 LockManager::LOCK_UW => $paths[
'sh'],
1261 LockManager::LOCK_EX => $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} " .
1320 " stat failure; aborted operations: " . FormatJson::encode( $ops ) );
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 ) {
1367 $curFileOpHandles = [];
1369 $curFileOpHandles[$index] = $subStatus->value;
1371 $statuses[$index] = $subStatus;
1374 if ( count( $curFileOpHandles ) ) {
1375 $fileOpHandles[] = $curFileOpHandles;
1378 foreach ( $fileOpHandles as $fileHandleBatch ) {
1382 foreach ( $statuses as $index => $subStatus ) {
1383 $status->merge( $subStatus );
1384 if ( $subStatus->isOK() ) {
1385 $status->success[$index] =
true;
1386 ++$status->successCount;
1388 $status->success[$index] =
false;
1389 ++$status->failCount;
1411 foreach ( $fileOpHandles as $fileOpHandle ) {
1413 throw new InvalidArgumentException(
"Expected FileBackendStoreOpHandle object." );
1414 } elseif ( $fileOpHandle->backend->getName() !== $this->getName() ) {
1415 throw new InvalidArgumentException(
"Expected handle for this file backend." );
1420 foreach ( $fileOpHandles as $fileOpHandle ) {
1421 $fileOpHandle->closeResources();
1437 if ( count( $fileOpHandles ) ) {
1438 throw new FileBackendError(
"Backend does not support asynchronous operations." );
1456 static $longs = [
'content-disposition' ];
1458 if ( isset( $op[
'headers'] ) ) {
1460 foreach ( $op[
'headers'] as
$name => $value ) {
1462 $maxHVLen = in_array(
$name, $longs ) ? INF : 255;
1463 if ( strlen(
$name ) > 255 || strlen( $value ) > $maxHVLen ) {
1464 $this->logger->error(
"Header '{header}' is too long.", [
1465 'filebackend' => $this->name,
1466 'header' =>
"$name: $value",
1469 $newHeaders[
$name] = strlen( $value ) ? $value :
'';
1472 $op[
'headers'] = $newHeaders;
1480 foreach ( $paths as
$path ) {
1482 $fullConts[] = $fullCont;
1490 if ( is_array( $paths ) ) {
1491 $paths = array_map( [ FileBackend::class,
'normalizeStoragePath' ], $paths );
1492 $paths = array_filter( $paths,
'strlen' );
1494 if ( $paths ===
null ) {
1495 $this->cheapCache->clear();
1496 $this->expensiveCache->clear();
1498 foreach ( $paths as
$path ) {
1499 $this->cheapCache->clear(
$path );
1500 $this->expensiveCache->clear(
$path );
1521 $params[
'concurrency'] = ( $this->parallelize !==
'off' ) ? $this->concurrency : 1;
1523 if ( $stats ===
null ) {
1528 $latest = !empty( $params[
'latest'] );
1590 return (
bool)preg_match(
'/^[a-z0-9][a-z0-9-_.]{0,199}$/i', $container );
1608 if ( $backend === $this->name ) {
1610 if ( $relPath !==
null && self::isValidShortContainerName( $shortCont ) ) {
1615 if ( $relPath !==
null ) {
1618 if ( self::isValidContainerName( $container ) ) {
1621 if ( $container !==
null ) {
1622 return [ $container, $relPath, $cShard ];
1629 return [
null,
null, null ];
1649 if ( $cShard !==
null && substr( $relPath, -1 ) !==
'/' ) {
1650 return [ $container, $relPath ];
1653 return [
null, null ];
1666 if ( $levels == 1 || $levels == 2 ) {
1668 $char = (
$base == 36 ) ?
'[0-9a-z]' :
'[0-9a-f]';
1671 if ( $levels === 1 ) {
1672 $hashDirRegex =
'(' . $char .
')';
1675 $hashDirRegex = $char .
'/(' . $char .
'{2})';
1677 $hashDirRegex =
'(' . $char .
')/(' . $char .
')';
1684 if ( preg_match(
"!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) {
1685 return '.' . implode(
'', array_slice( $m, 1 ) );
1705 return ( $shard !==
null );
1717 if ( isset( $this->shardViaHashLevels[$container] ) ) {
1718 $config = $this->shardViaHashLevels[$container];
1719 $hashLevels = (int)$config[
'levels'];
1720 if ( $hashLevels == 1 || $hashLevels == 2 ) {
1721 $hashBase = (int)$config[
'base'];
1722 if ( $hashBase == 16 || $hashBase == 36 ) {
1723 return [ $hashLevels, $hashBase, $config[
'repeat'] ];
1728 return [ 0, 0, false ];
1740 if ( $digits > 0 ) {
1741 $numShards =
$base ** $digits;
1742 for ( $index = 0; $index < $numShards; $index++ ) {
1743 $shards[] =
'.' . Wikimedia\base_convert( (
string)$index, 10,
$base, $digits );
1757 if ( $this->domainId !=
'' ) {
1758 return "{$this->domainId}-$container";
1789 return $relStoragePath;
1798 private function containerCacheKey( $container ) {
1799 return "filebackend:{$this->name}:{$this->domainId}:container:{$container}";
1809 $this->memCache->set( $this->containerCacheKey( $container ), $val, 14 * 86400 );
1819 if ( !$this->memCache->delete( $this->containerCacheKey( $container ), 300 ) ) {
1820 $this->logger->warning(
"Unable to delete stat cache for container {container}.",
1821 [
'filebackend' => $this->name,
'container' => $container ]
1840 foreach ( $items as $item ) {
1841 if ( self::isStoragePath( $item ) ) {
1843 } elseif ( is_string( $item ) ) {
1844 $contNames[$this->containerCacheKey( $item )] = $item;
1848 foreach ( $paths as
$path ) {
1850 if ( $fullCont !==
null ) {
1851 $contNames[$this->containerCacheKey( $fullCont )] = $fullCont;
1857 $values = $this->memCache->getMulti( array_keys( $contNames ) );
1858 foreach ( $values as $cacheKey => $val ) {
1859 $contInfo[$contNames[$cacheKey]] = $val;
1883 private function fileCacheKey(
$path ) {
1884 return "filebackend:{$this->name}:{$this->domainId}:file:" . sha1(
$path );
1897 if (
$path ===
null ) {
1900 $mtime = (int)ConvertibleTimestamp::convert( TS_UNIX, $val[
'mtime'] );
1901 $ttl = $this->memCache->adaptiveTTL( $mtime, 7 * 86400, 300, 0.1 );
1902 $key = $this->fileCacheKey(
$path );
1904 $this->memCache->set( $key, $val, $ttl );
1917 if (
$path ===
null ) {
1920 if ( !$this->memCache->delete( $this->fileCacheKey(
$path ), 300 ) ) {
1921 $this->logger->warning(
"Unable to delete stat cache for file {path}.",
1922 [
'filebackend' => $this->name,
'path' =>
$path ]
1941 foreach ( $items as $item ) {
1942 if ( self::isStoragePath( $item ) ) {
1944 if (
$path !==
null ) {
1950 $paths = array_filter( $paths,
'strlen' );
1952 foreach ( $paths as
$path ) {
1954 if ( $rel !==
null ) {
1955 $pathNames[$this->fileCacheKey(
$path )] =
$path;
1960 $values = $this->memCache->getMulti( array_keys( $pathNames ) );
1962 foreach ( array_filter( $values,
'is_array' ) as $cacheKey => $stat ) {
1963 $path = $pathNames[$cacheKey];
1966 unset( $stat[
'latest'] );
1968 $this->cheapCache->setField(
$path,
'stat', $stat );
1969 if ( isset( $stat[
'sha1'] ) && strlen( $stat[
'sha1'] ) == 31 ) {
1971 $this->cheapCache->setField(
1974 [
'hash' => $stat[
'sha1'],
'latest' =>
false ]
1977 if ( isset( $stat[
'xattr'] ) && is_array( $stat[
'xattr'] ) ) {
1980 $this->cheapCache->setField(
1983 [
'map' => $stat[
'xattr'],
'latest' =>
false ]
1997 $newXAttr = [
'headers' => [],
'metadata' => [] ];
1999 foreach ( $xattr[
'headers'] as
$name => $value ) {
2000 $newXAttr[
'headers'][strtolower(
$name )] = $value;
2003 foreach ( $xattr[
'metadata'] as
$name => $value ) {
2004 $newXAttr[
'metadata'][strtolower(
$name )] = $value;
2017 $opts[
'concurrency'] = 1;
2018 if ( $this->parallelize ===
'implicit' ) {
2019 if ( $opts[
'parallelize'] ??
true ) {
2022 } elseif ( $this->parallelize ===
'explicit' ) {
2023 if ( !empty( $opts[
'parallelize'] ) ) {
2041 if ( $this->mimeCallback ) {
2042 return call_user_func_array( $this->mimeCallback, func_get_args() );
2045 $mime = ( $fsPath !== null ) ? mime_content_type( $fsPath ) :
false;
2046 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.
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