MediaWiki  master
FileBackend.php
Go to the documentation of this file.
1 <?php
35 
94 abstract class FileBackend implements LoggerAwareInterface {
96  protected $name;
97 
99  protected $domainId;
100 
102  protected $readOnly;
103 
105  protected $parallelize;
106 
108  protected $concurrency;
109 
111  protected $tmpFileFactory;
112 
114  protected $lockManager;
116  protected $fileJournal;
118  protected $logger;
120  protected $profiler;
121 
123  protected $obResetFunc;
125  protected $streamMimeFunc;
127  protected $statusWrapper;
128 
130  const ATTR_HEADERS = 1; // files can be tagged with standard HTTP headers
131  const ATTR_METADATA = 2; // files can be stored with metadata key/values
132  const ATTR_UNICODE_PATHS = 4; // files can have Unicode paths (not just ASCII)
133 
135  const STAT_ABSENT = false;
136 
138  const STAT_ERROR = null;
140  const LIST_ERROR = null;
142  const TEMPURL_ERROR = null;
144  const EXISTENCE_ERROR = null;
145 
147  const TIMESTAMP_FAIL = false;
149  const CONTENT_FAIL = false;
151  const XATTRS_FAIL = false;
153  const SIZE_FAIL = false;
155  const SHA1_FAIL = false;
156 
190  public function __construct( array $config ) {
191  $this->name = $config['name'];
192  $this->domainId = $config['domainId'] // e.g. "my_wiki-en_"
193  ?? $config['wikiId']; // b/c alias
194  if ( !preg_match( '!^[a-zA-Z0-9-_]{1,255}$!', $this->name ) ) {
195  throw new InvalidArgumentException( "Backend name '{$this->name}' is invalid." );
196  } elseif ( !is_string( $this->domainId ) ) {
197  throw new InvalidArgumentException(
198  "Backend domain ID not provided for '{$this->name}'." );
199  }
200  $this->lockManager = $config['lockManager'] ?? new NullLockManager( [] );
201  $this->fileJournal = $config['fileJournal']
202  ?? FileJournal::factory( [ 'class' => NullFileJournal::class ], $this->name );
203  $this->readOnly = isset( $config['readOnly'] )
204  ? (string)$config['readOnly']
205  : '';
206  $this->parallelize = isset( $config['parallelize'] )
207  ? (string)$config['parallelize']
208  : 'off';
209  $this->concurrency = isset( $config['concurrency'] )
210  ? (int)$config['concurrency']
211  : 50;
212  $this->obResetFunc = $config['obResetFunc'] ?? [ $this, 'resetOutputBuffer' ];
213  $this->streamMimeFunc = $config['streamMimeFunc'] ?? null;
214  $this->statusWrapper = $config['statusWrapper'] ?? null;
215 
216  $this->profiler = $config['profiler'] ?? null;
217  if ( !is_callable( $this->profiler ) ) {
218  $this->profiler = null;
219  }
220  $this->logger = $config['logger'] ?? new NullLogger();
221  $this->statusWrapper = $config['statusWrapper'] ?? null;
222  // tmpDirectory gets precedence for backward compatibility
223  if ( isset( $config['tmpDirectory'] ) ) {
224  $this->tmpFileFactory = new TempFSFileFactory( $config['tmpDirectory'] );
225  } else {
226  $this->tmpFileFactory = $config['tmpFileFactory'] ?? new TempFSFileFactory();
227  }
228  }
229 
230  public function setLogger( LoggerInterface $logger ) {
231  $this->logger = $logger;
232  }
233 
242  final public function getName() {
243  return $this->name;
244  }
245 
252  final public function getDomainId() {
253  return $this->domainId;
254  }
255 
263  final public function getWikiId() {
264  return $this->getDomainId();
265  }
266 
272  final public function isReadOnly() {
273  return ( $this->readOnly != '' );
274  }
275 
281  final public function getReadOnlyReason() {
282  return ( $this->readOnly != '' ) ? $this->readOnly : false;
283  }
284 
291  public function getFeatures() {
292  return self::ATTR_UNICODE_PATHS;
293  }
294 
302  final public function hasFeatures( $bitfield ) {
303  return ( $this->getFeatures() & $bitfield ) === $bitfield;
304  }
305 
458  final public function doOperations( array $ops, array $opts = [] ) {
459  if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
460  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
461  }
462  if ( $ops === [] ) {
463  return $this->newStatus(); // nothing to do
464  }
465 
466  $ops = $this->resolveFSFileObjects( $ops );
467  if ( empty( $opts['force'] ) ) { // sanity
468  unset( $opts['nonLocking'] );
469  }
470 
472  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
473 
474  return $this->doOperationsInternal( $ops, $opts );
475  }
476 
483  abstract protected function doOperationsInternal( array $ops, array $opts );
484 
496  final public function doOperation( array $op, array $opts = [] ) {
497  return $this->doOperations( [ $op ], $opts );
498  }
499 
510  final public function create( array $params, array $opts = [] ) {
511  return $this->doOperation( [ 'op' => 'create' ] + $params, $opts );
512  }
513 
524  final public function store( array $params, array $opts = [] ) {
525  return $this->doOperation( [ 'op' => 'store' ] + $params, $opts );
526  }
527 
538  final public function copy( array $params, array $opts = [] ) {
539  return $this->doOperation( [ 'op' => 'copy' ] + $params, $opts );
540  }
541 
552  final public function move( array $params, array $opts = [] ) {
553  return $this->doOperation( [ 'op' => 'move' ] + $params, $opts );
554  }
555 
566  final public function delete( array $params, array $opts = [] ) {
567  return $this->doOperation( [ 'op' => 'delete' ] + $params, $opts );
568  }
569 
581  final public function describe( array $params, array $opts = [] ) {
582  return $this->doOperation( [ 'op' => 'describe' ] + $params, $opts );
583  }
584 
699  final public function doQuickOperations( array $ops, array $opts = [] ) {
700  if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
701  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
702  }
703  if ( $ops === [] ) {
704  return $this->newStatus(); // nothing to do
705  }
706 
707  $ops = $this->resolveFSFileObjects( $ops );
708  foreach ( $ops as &$op ) {
709  $op['overwrite'] = true; // avoids RTTs in key/value stores
710  }
711 
713  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
714 
715  return $this->doQuickOperationsInternal( $ops );
716  }
717 
724  abstract protected function doQuickOperationsInternal( array $ops );
725 
736  final public function doQuickOperation( array $op ) {
737  return $this->doQuickOperations( [ $op ] );
738  }
739 
750  final public function quickCreate( array $params ) {
751  return $this->doQuickOperation( [ 'op' => 'create' ] + $params );
752  }
753 
764  final public function quickStore( array $params ) {
765  return $this->doQuickOperation( [ 'op' => 'store' ] + $params );
766  }
767 
778  final public function quickCopy( array $params ) {
779  return $this->doQuickOperation( [ 'op' => 'copy' ] + $params );
780  }
781 
792  final public function quickMove( array $params ) {
793  return $this->doQuickOperation( [ 'op' => 'move' ] + $params );
794  }
795 
806  final public function quickDelete( array $params ) {
807  return $this->doQuickOperation( [ 'op' => 'delete' ] + $params );
808  }
809 
820  final public function quickDescribe( array $params ) {
821  return $this->doQuickOperation( [ 'op' => 'describe' ] + $params );
822  }
823 
836  abstract public function concatenate( array $params );
837 
856  final public function prepare( array $params ) {
857  if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
858  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
859  }
861  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
862  return $this->doPrepare( $params );
863  }
864 
870  abstract protected function doPrepare( array $params );
871 
888  final public function secure( array $params ) {
889  if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
890  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
891  }
893  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
894  return $this->doSecure( $params );
895  }
896 
902  abstract protected function doSecure( array $params );
903 
922  final public function publish( array $params ) {
923  if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
924  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
925  }
927  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
928  return $this->doPublish( $params );
929  }
930 
936  abstract protected function doPublish( array $params );
937 
949  final public function clean( array $params ) {
950  if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
951  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
952  }
954  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
955  return $this->doClean( $params );
956  }
957 
963  abstract protected function doClean( array $params );
964 
981  abstract public function fileExists( array $params );
982 
993  abstract public function getFileTimestamp( array $params );
994 
1006  final public function getFileContents( array $params ) {
1007  $contents = $this->getFileContentsMulti( [ 'srcs' => [ $params['src'] ] ] + $params );
1008 
1009  return $contents[$params['src']];
1010  }
1011 
1025  abstract public function getFileContentsMulti( array $params );
1026 
1047  abstract public function getFileXAttributes( array $params );
1048 
1059  abstract public function getFileSize( array $params );
1060 
1077  abstract public function getFileStat( array $params );
1078 
1089  abstract public function getFileSha1Base36( array $params );
1090 
1100  abstract public function getFileProps( array $params );
1101 
1121  abstract public function streamFile( array $params );
1122 
1141  final public function getLocalReference( array $params ) {
1142  $fsFiles = $this->getLocalReferenceMulti( [ 'srcs' => [ $params['src'] ] ] + $params );
1143 
1144  return $fsFiles[$params['src']];
1145  }
1146 
1164  abstract public function getLocalReferenceMulti( array $params );
1165 
1178  final public function getLocalCopy( array $params ) {
1179  $tmpFiles = $this->getLocalCopyMulti( [ 'srcs' => [ $params['src'] ] ] + $params );
1180 
1181  return $tmpFiles[$params['src']];
1182  }
1183 
1199  abstract public function getLocalCopyMulti( array $params );
1200 
1219  abstract public function getFileHttpUrl( array $params );
1220 
1246  abstract public function directoryExists( array $params );
1247 
1270  abstract public function getDirectoryList( array $params );
1271 
1288  final public function getTopDirectoryList( array $params ) {
1289  return $this->getDirectoryList( [ 'topOnly' => true ] + $params );
1290  }
1291 
1312  abstract public function getFileList( array $params );
1313 
1330  final public function getTopFileList( array $params ) {
1331  return $this->getFileList( [ 'topOnly' => true ] + $params );
1332  }
1333 
1342  abstract public function preloadCache( array $paths );
1343 
1352  abstract public function clearCache( array $paths = null );
1353 
1368  abstract public function preloadFileStat( array $params );
1369 
1381  final public function lockFiles( array $paths, $type, $timeout = 0 ) {
1382  $paths = array_map( 'FileBackend::normalizeStoragePath', $paths );
1383 
1384  return $this->wrapStatus( $this->lockManager->lock( $paths, $type, $timeout ) );
1385  }
1386 
1394  final public function unlockFiles( array $paths, $type ) {
1395  $paths = array_map( 'FileBackend::normalizeStoragePath', $paths );
1396 
1397  return $this->wrapStatus( $this->lockManager->unlock( $paths, $type ) );
1398  }
1399 
1416  final public function getScopedFileLocks(
1417  array $paths, $type, StatusValue $status, $timeout = 0
1418  ) {
1419  if ( $type === 'mixed' ) {
1420  foreach ( $paths as &$typePaths ) {
1421  $typePaths = array_map( 'FileBackend::normalizeStoragePath', $typePaths );
1422  }
1423  } else {
1424  $paths = array_map( 'FileBackend::normalizeStoragePath', $paths );
1425  }
1426 
1427  return ScopedLock::factory( $this->lockManager, $paths, $type, $status, $timeout );
1428  }
1429 
1446  abstract public function getScopedLocksForOps( array $ops, StatusValue $status );
1447 
1455  final public function getRootStoragePath() {
1456  return "mwstore://{$this->name}";
1457  }
1458 
1466  final public function getContainerStoragePath( $container ) {
1467  return $this->getRootStoragePath() . "/{$container}";
1468  }
1469 
1475  final public function getJournal() {
1476  return $this->fileJournal;
1477  }
1478 
1488  protected function resolveFSFileObjects( array $ops ) {
1489  foreach ( $ops as &$op ) {
1490  $src = $op['src'] ?? null;
1491  if ( $src instanceof FSFile ) {
1492  $op['srcRef'] = $src;
1493  $op['src'] = $src->getPath();
1494  }
1495  }
1496  unset( $op );
1497 
1498  return $ops;
1499  }
1500 
1508  final public static function isStoragePath( $path ) {
1509  return ( strpos( $path, 'mwstore://' ) === 0 );
1510  }
1511 
1520  final public static function splitStoragePath( $storagePath ) {
1521  if ( self::isStoragePath( $storagePath ) ) {
1522  // Remove the "mwstore://" prefix and split the path
1523  $parts = explode( '/', substr( $storagePath, 10 ), 3 );
1524  if ( count( $parts ) >= 2 && $parts[0] != '' && $parts[1] != '' ) {
1525  if ( count( $parts ) == 3 ) {
1526  return $parts; // e.g. "backend/container/path"
1527  } else {
1528  return [ $parts[0], $parts[1], '' ]; // e.g. "backend/container"
1529  }
1530  }
1531  }
1532 
1533  return [ null, null, null ];
1534  }
1535 
1543  final public static function normalizeStoragePath( $storagePath ) {
1544  list( $backend, $container, $relPath ) = self::splitStoragePath( $storagePath );
1545  if ( $relPath !== null ) { // must be for this backend
1546  $relPath = self::normalizeContainerPath( $relPath );
1547  if ( $relPath !== null ) {
1548  return ( $relPath != '' )
1549  ? "mwstore://{$backend}/{$container}/{$relPath}"
1550  : "mwstore://{$backend}/{$container}";
1551  }
1552  }
1553 
1554  return null;
1555  }
1556 
1565  final public static function parentStoragePath( $storagePath ) {
1566  $storagePath = dirname( $storagePath );
1567  list( , , $rel ) = self::splitStoragePath( $storagePath );
1568 
1569  return ( $rel === null ) ? null : $storagePath;
1570  }
1571 
1579  final public static function extensionFromPath( $path, $case = 'lowercase' ) {
1580  $i = strrpos( $path, '.' );
1581  $ext = $i ? substr( $path, $i + 1 ) : '';
1582 
1583  if ( $case === 'lowercase' ) {
1584  $ext = strtolower( $ext );
1585  } elseif ( $case === 'uppercase' ) {
1586  $ext = strtoupper( $ext );
1587  }
1588 
1589  return $ext;
1590  }
1591 
1599  final public static function isPathTraversalFree( $path ) {
1600  return ( self::normalizeContainerPath( $path ) !== null );
1601  }
1602 
1612  final public static function makeContentDisposition( $type, $filename = '' ) {
1613  $parts = [];
1614 
1615  $type = strtolower( $type );
1616  if ( !in_array( $type, [ 'inline', 'attachment' ] ) ) {
1617  throw new InvalidArgumentException( "Invalid Content-Disposition type '$type'." );
1618  }
1619  $parts[] = $type;
1620 
1621  if ( strlen( $filename ) ) {
1622  $parts[] = "filename*=UTF-8''" . rawurlencode( basename( $filename ) );
1623  }
1624 
1625  return implode( ';', $parts );
1626  }
1627 
1638  final protected static function normalizeContainerPath( $path ) {
1639  // Normalize directory separators
1640  $path = strtr( $path, '\\', '/' );
1641  // Collapse any consecutive directory separators
1642  $path = preg_replace( '![/]{2,}!', '/', $path );
1643  // Remove any leading directory separator
1644  $path = ltrim( $path, '/' );
1645  // Use the same traversal protection as Title::secureAndSplit()
1646  if ( strpos( $path, '.' ) !== false ) {
1647  if (
1648  $path === '.' ||
1649  $path === '..' ||
1650  strpos( $path, './' ) === 0 ||
1651  strpos( $path, '../' ) === 0 ||
1652  strpos( $path, '/./' ) !== false ||
1653  strpos( $path, '/../' ) !== false
1654  ) {
1655  return null;
1656  }
1657  }
1658 
1659  return $path;
1660  }
1661 
1670  final protected function newStatus( ...$args ) {
1671  if ( count( $args ) ) {
1672  $sv = StatusValue::newFatal( ...$args );
1673  } else {
1674  $sv = StatusValue::newGood();
1675  }
1676 
1677  return $this->wrapStatus( $sv );
1678  }
1679 
1684  final protected function wrapStatus( StatusValue $sv ) {
1685  return $this->statusWrapper ? call_user_func( $this->statusWrapper, $sv ) : $sv;
1686  }
1687 
1692  protected function scopedProfileSection( $section ) {
1693  return $this->profiler ? ( $this->profiler )( $section ) : null;
1694  }
1695 
1696  protected function resetOutputBuffer() {
1697  while ( ob_get_status() ) {
1698  if ( !ob_end_clean() ) {
1699  // Could not remove output buffer handler; abort now
1700  // to avoid getting in some kind of infinite loop.
1701  break;
1702  }
1703  }
1704  }
1705 }
doQuickOperationsInternal(array $ops)
static isPathTraversalFree( $path)
Check if a relative path has no directory traversals.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:69
FileJournal $fileJournal
describe(array $params, array $opts=[])
Performs a single describe operation.
unlockFiles(array $paths, $type)
Unlock the files at the given storage paths in the backend.
static splitStoragePath( $storagePath)
Split a storage path into a backend name, a container name, and a relative file path.
doOperations(array $ops, array $opts=[])
This is the main entry point into the backend for write operations.
TempFSFileFactory $tmpFileFactory
doPrepare(array $params)
getFileStat(array $params)
Get quick information about a file at a storage path in the backend.
getWikiId()
Alias to getDomainId()
publish(array $params)
Remove measures to block web access to a storage directory and the container it belongs to...
secure(array $params)
Take measures to block web access to a storage directory and the container it belongs to...
static parentStoragePath( $storagePath)
Get the parent storage directory of a storage path.
setLogger(LoggerInterface $logger)
string $domainId
Unique domain name.
Definition: FileBackend.php:99
scopedProfileSection( $section)
const ATTR_HEADERS
Bitfield flags for supported features.
quickMove(array $params)
Performs a single quick move operation.
getFileHttpUrl(array $params)
Return an HTTP URL to a given file that requires no authentication to use.
quickDelete(array $params)
Performs a single quick delete operation.
getFileXAttributes(array $params)
Get metadata about a file at a storage path in the backend.
getName()
Get the unique backend name.
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.
getFileContentsMulti(array $params)
Like getFileContents() except it takes an array of storage paths and returns an order preserved map o...
getDirectoryList(array $params)
Get an iterator to list all directories under a storage directory.
getDomainId()
Get the domain identifier used for this backend (possibly empty).
doOperation(array $op, array $opts=[])
Same as doOperations() except it takes a single operation.
static factory(LockManager $manager, array $paths, $type, StatusValue $status, $timeout=0)
Get a ScopedLock object representing a lock on resource paths.
Definition: ScopedLock.php:70
doClean(array $params)
static extensionFromPath( $path, $case='lowercase')
Get the final extension from a storage or FS path.
getFileTimestamp(array $params)
Get the last-modified timestamp of the file at a storage path.
static isStoragePath( $path)
Check if a given path is a "mwstore://" path.
static normalizeStoragePath( $storagePath)
Normalize a storage path by cleaning up directory separators.
int $concurrency
How many operations can be done in parallel.
getLocalReferenceMulti(array $params)
Like getLocalReference() except it takes an array of storage paths and yields an order-preserved map ...
directoryExists(array $params)
Check if a directory exists at a given storage path.
wrapStatus(StatusValue $sv)
callable $statusWrapper
resolveFSFileObjects(array $ops)
Convert FSFile &#39;src&#39; paths to string paths (with an &#39;srcRef&#39; field set to the FSFile) ...
if( $line===false) $args
Definition: cdb.php:64
string $readOnly
Read-only explanation message.
create(array $params, array $opts=[])
Performs a single create operation.
move(array $params, array $opts=[])
Performs a single move operation.
quickStore(array $params)
Performs a single quick store operation.
getReadOnlyReason()
Get an explanatory message if this backend is read-only.
quickCreate(array $params)
Performs a single quick create operation.
getTopFileList(array $params)
Same as FileBackend::getFileList() except only lists files that are immediately under the given direc...
lockFiles(array $paths, $type, $timeout=0)
Lock the files at the given storage paths in the backend.
__construct(array $config)
Create a new backend instance from configuration.
hasFeatures( $bitfield)
Check if the backend medium supports a field of extra features.
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
quickCopy(array $params)
Performs a single quick copy operation.
fileExists(array $params)
Check if a file exists at a storage path in the backend.
getFileSha1Base36(array $params)
Get a SHA-1 hash of the content of the file at a storage path in the backend.
callable $obResetFunc
store(array $params, array $opts=[])
Performs a single store operation.
const ATTR_UNICODE_PATHS
doSecure(array $params)
const ATTR_METADATA
callable $streamMimeFunc
newStatus(... $args)
Yields the result of the status wrapper callback on either:
getFileSize(array $params)
Get the size (bytes) of a file at a storage path in the backend.
doQuickOperations(array $ops, array $opts=[])
Perform a set of independent file operations on some files.
prepare(array $params)
Prepare a storage directory for usage.
string $name
Unique backend name.
Definition: FileBackend.php:96
getLocalCopy(array $params)
Get a local copy on disk of the file at a storage path in the backend.
LoggerInterface $logger
getJournal()
Get the file journal object for this backend.
concatenate(array $params)
Concatenate a list of storage files into a single file system file.
getRootStoragePath()
Get the root storage path of this backend.
Class representing a non-directory file on the file system.
Definition: FSFile.php:32
preloadCache(array $paths)
Preload persistent file stat cache and property cache into in-process cache.
copy(array $params, array $opts=[])
Performs a single copy operation.
doOperationsInternal(array $ops, array $opts)
Base class for all file backend classes (including multi-write backends).
Definition: FileBackend.php:94
getFileList(array $params)
Get an iterator to list all stored files under a storage directory.
if(!is_readable( $file)) $ext
Definition: router.php:48
getScopedLocksForOps(array $ops, StatusValue $status)
Get an array of scoped locks needed for a batch of file operations.
getTopDirectoryList(array $params)
Same as FileBackend::getDirectoryList() except only lists directories that are immediately under the ...
getLocalCopyMulti(array $params)
Like getLocalCopy() except it takes an array of storage paths and yields an order preserved-map of st...
quickDescribe(array $params)
Performs a single quick describe operation.
string $parallelize
When to do operations in parallel.
streamFile(array $params)
Stream the content of the file at a storage path in the backend.
clean(array $params)
Delete a storage directory if it is empty.
getFileProps(array $params)
Get the properties of the content of the file at a storage path in the backend.
clearCache(array $paths=null)
Invalidate any in-process file stat and property cache.
LockManager $lockManager
doQuickOperation(array $op)
Same as doQuickOperations() except it takes a single operation.
callable null $profiler
isReadOnly()
Check if this backend is read-only.
preloadFileStat(array $params)
Preload file stat information (concurrently if possible) into in-process cache.
getLocalReference(array $params)
Returns a file system file, identical in content to the file at a storage path.
getFeatures()
Get the a bitfield of extra features supported by the backend medium.
static makeContentDisposition( $type, $filename='')
Build a Content-Disposition header value per RFC 6266.
static factory(array $config, $backend)
Create an appropriate FileJournal object from config.
Definition: FileJournal.php:64
getContainerStoragePath( $container)
Get the storage path for the given container for this backend.
doPublish(array $params)
getFileContents(array $params)
Get the contents of a file at a storage path in the backend.