* @param string $path Storage path to object
* @return array New headers
*/
- protected function addMissingMetadata( array $objHdrs, $path ) {
+ protected function addMissingHashMetadata( array $objHdrs, $path ) {
if ( isset( $objHdrs['x-object-meta-sha1base36'] ) ) {
return $objHdrs; // nothing to do
}
$auth = $this->getAuthentication();
$ep = array_diff_key( $params, [ 'srcs' => 1 ] ); // for error logging
- // Blindly create tmp files and stream to them, catching any exception if the file does
- // not exist. Doing stats here is useless and will loop infinitely in addMissingMetadata().
+ // Blindly create tmp files and stream to them, catching any exception
+ // if the file does not exist. Do not waste time doing file stats here.
$reqs = []; // (path => op)
foreach ( $params['srcs'] as $path ) { // each path in this concurrent batch
}
protected function doGetFileSha1base36( array $params ) {
+ // Avoid using stat entries from file listings, which never include the SHA-1 hash.
+ // Also, recompute the hash if it's not part of the metadata headers for some reason.
+ $params['requireSHA1'] = true;
+
$stat = $this->getFileStat( $params );
if ( $stat ) {
- if ( !isset( $stat['sha1'] ) ) {
- // Stat entries filled by file listings don't include SHA1
- $this->clearCache( [ $params['src'] ] );
- $stat = $this->getFileStat( $params );
- }
-
return $stat['sha1'];
} else {
return false;
$auth = $this->getAuthentication();
$ep = array_diff_key( $params, [ 'srcs' => 1 ] ); // for error logging
- // Blindly create tmp files and stream to them, catching any exception if the file does
- // not exist. Doing a stat here is useless causes infinite loops in addMissingMetadata().
+ // Blindly create tmp files and stream to them, catching any exception
+ // if the file does not exist. Do not waste time doing file stats here.
$reqs = []; // (path => op)
foreach ( $params['srcs'] as $path ) { // each path in this concurrent batch
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $reqs[$path]['response'];
if ( $rcode === 200 || $rcode === 204 ) {
// Update the object if it is missing some headers
- $rhdrs = $this->addMissingMetadata( $rhdrs, $path );
+ if ( !empty( $params['requireSHA1'] ) ) {
+ $rhdrs = $this->addMissingHashMetadata( $rhdrs, $path );
+ }
// Load the stat array from the headers
$stat = $this->getStatFromHeaders( $rhdrs );
if ( $this->isRGW ) {
if ( $rcode >= 200 && $rcode <= 299 ) { // OK
$this->authCreds = [
'auth_token' => $rhdrs['x-auth-token'],
- 'storage_url' => ( $this->swiftStorageUrl !== null )
- ? $this->swiftStorageUrl
- : $rhdrs['x-storage-url']
+ 'storage_url' => $this->swiftStorageUrl ?? $rhdrs['x-storage-url']
];
$this->srvCache->set( $cacheKey, $this->authCreds, ceil( $this->authTTL / 2 ) );
$this->logger->error( $msg, $msgParams );
}
}
-
-/**
- * @see FileBackendStoreOpHandle
- */
-class SwiftFileOpHandle extends FileBackendStoreOpHandle {
- /** @var array List of Requests for MultiHttpClient */
- public $httpOp;
- /** @var Closure */
- public $callback;
-
- /**
- * @param SwiftFileBackend $backend
- * @param Closure $callback Function that takes (HTTP request array, status)
- * @param array $httpOp MultiHttpClient op
- */
- public function __construct( SwiftFileBackend $backend, Closure $callback, array $httpOp ) {
- $this->backend = $backend;
- $this->callback = $callback;
- $this->httpOp = $httpOp;
- }
-}
-
-/**
- * SwiftFileBackend helper class to page through listings.
- * Swift also has a listing limit of 10,000 objects for sanity.
- * Do not use this class from places outside SwiftFileBackend.
- *
- * @ingroup FileBackend
- */
-abstract class SwiftFileBackendList implements Iterator {
- /** @var array List of path or (path,stat array) entries */
- protected $bufferIter = [];
-
- /** @var string List items *after* this path */
- protected $bufferAfter = null;
-
- /** @var int */
- protected $pos = 0;
-
- /** @var array */
- protected $params = [];
-
- /** @var SwiftFileBackend */
- protected $backend;
-
- /** @var string Container name */
- protected $container;
-
- /** @var string Storage directory */
- protected $dir;
-
- /** @var int */
- protected $suffixStart;
-
- const PAGE_SIZE = 9000; // file listing buffer size
-
- /**
- * @param SwiftFileBackend $backend
- * @param string $fullCont Resolved container name
- * @param string $dir Resolved directory relative to container
- * @param array $params
- */
- public function __construct( SwiftFileBackend $backend, $fullCont, $dir, array $params ) {
- $this->backend = $backend;
- $this->container = $fullCont;
- $this->dir = $dir;
- if ( substr( $this->dir, -1 ) === '/' ) {
- $this->dir = substr( $this->dir, 0, -1 ); // remove trailing slash
- }
- if ( $this->dir == '' ) { // whole container
- $this->suffixStart = 0;
- } else { // dir within container
- $this->suffixStart = strlen( $this->dir ) + 1; // size of "path/to/dir/"
- }
- $this->params = $params;
- }
-
- /**
- * @see Iterator::key()
- * @return int
- */
- public function key() {
- return $this->pos;
- }
-
- /**
- * @see Iterator::next()
- */
- public function next() {
- // Advance to the next file in the page
- next( $this->bufferIter );
- ++$this->pos;
- // Check if there are no files left in this page and
- // advance to the next page if this page was not empty.
- if ( !$this->valid() && count( $this->bufferIter ) ) {
- $this->bufferIter = $this->pageFromList(
- $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
- ); // updates $this->bufferAfter
- }
- }
-
- /**
- * @see Iterator::rewind()
- */
- public function rewind() {
- $this->pos = 0;
- $this->bufferAfter = null;
- $this->bufferIter = $this->pageFromList(
- $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
- ); // updates $this->bufferAfter
- }
-
- /**
- * @see Iterator::valid()
- * @return bool
- */
- public function valid() {
- if ( $this->bufferIter === null ) {
- return false; // some failure?
- } else {
- return ( current( $this->bufferIter ) !== false ); // no paths can have this value
- }
- }
-
- /**
- * Get the given list portion (page)
- *
- * @param string $container Resolved container name
- * @param string $dir Resolved path relative to container
- * @param string &$after
- * @param int $limit
- * @param array $params
- * @return Traversable|array
- */
- abstract protected function pageFromList( $container, $dir, &$after, $limit, array $params );
-}
-
-/**
- * Iterator for listing directories
- */
-class SwiftFileBackendDirList extends SwiftFileBackendList {
- /**
- * @see Iterator::current()
- * @return string|bool String (relative path) or false
- */
- public function current() {
- return substr( current( $this->bufferIter ), $this->suffixStart, -1 );
- }
-
- protected function pageFromList( $container, $dir, &$after, $limit, array $params ) {
- return $this->backend->getDirListPageInternal( $container, $dir, $after, $limit, $params );
- }
-}
-
-/**
- * Iterator for listing regular files
- */
-class SwiftFileBackendFileList extends SwiftFileBackendList {
- /**
- * @see Iterator::current()
- * @return string|bool String (relative path) or false
- */
- public function current() {
- list( $path, $stat ) = current( $this->bufferIter );
- $relPath = substr( $path, $this->suffixStart );
- if ( is_array( $stat ) ) {
- $storageDir = rtrim( $this->params['dir'], '/' );
- $this->backend->loadListingStatInternal( "$storageDir/$relPath", $stat );
- }
-
- return $relPath;
- }
-
- protected function pageFromList( $container, $dir, &$after, $limit, array $params ) {
- return $this->backend->getFileListPageInternal( $container, $dir, $after, $limit, $params );
- }
-}