aboutsummaryrefslogtreecommitdiffstats
path: root/includes/filebackend
diff options
context:
space:
mode:
authorAaron Schulz <aschulz@wikimedia.org>2013-09-30 00:12:10 -0700
committerAaron Schulz <aschulz@wikimedia.org>2014-01-08 13:22:11 -0800
commitac14c1c8a65d154f7a9bc47d1b51f6db67188f86 (patch)
treed581f21014ede6fcd8b53a0a9489adef76b5cabf /includes/filebackend
parentb8ef0a54700fca3fb6d119cedb657c28df926906 (diff)
downloadmediawikicore-ac14c1c8a65d154f7a9bc47d1b51f6db67188f86.tar.gz
mediawikicore-ac14c1c8a65d154f7a9bc47d1b51f6db67188f86.zip
filebackend: Added supported for retrieving file metadata/headers
* This can be useful for carrying over metadata when copying files around * Also fixed a bug in sanitizeHdrs() for Swift (broken content-disposition) Change-Id: I4534e9acac2b306086797b3677f85c05b98e39fc
Diffstat (limited to 'includes/filebackend')
-rw-r--r--includes/filebackend/FileBackend.php45
-rw-r--r--includes/filebackend/FileBackendMultiWrite.php9
-rw-r--r--includes/filebackend/FileBackendStore.php65
-rw-r--r--includes/filebackend/SwiftFileBackend.php22
4 files changed, 138 insertions, 3 deletions
diff --git a/includes/filebackend/FileBackend.php b/includes/filebackend/FileBackend.php
index bb21f1b5e492..f5d63b9df78e 100644
--- a/includes/filebackend/FileBackend.php
+++ b/includes/filebackend/FileBackend.php
@@ -104,6 +104,10 @@ abstract class FileBackend {
/** @var FileJournal */
protected $fileJournal;
+ /** Flags for supported features */
+ const ATTR_HEADERS = 1;
+ const ATTR_METADATA = 2;
+
/**
* Create a new backend instance from configuration.
* This should only be called from within FileBackendGroup.
@@ -201,6 +205,27 @@ abstract class FileBackend {
}
/**
+ * Get the a bitfield of extra features supported by the backend medium
+ *
+ * @return integer Bitfield of FileBackend::ATTR_* flags
+ * @since 1.23
+ */
+ public function getFeatures() {
+ return 0;
+ }
+
+ /**
+ * Check if the backend medium supports a field of extra features
+ *
+ * @return integer Bitfield of FileBackend::ATTR_* flags
+ * @return bool
+ * @since 1.23
+ */
+ final public function hasFeatures( $bitfield ) {
+ return ( $this->getFeatures() & $bitfield ) === $bitfield;
+ }
+
+ /**
* This is the main entry point into the backend for write operations.
* Callers supply an ordered list of operations to perform as a transaction.
* Files will be locked, the stat cache cleared, and then the operations attempted.
@@ -902,6 +927,26 @@ abstract class FileBackend {
abstract public function getFileContentsMulti( array $params );
/**
+ * Get metadata about a file at a storage path in the backend.
+ * If the file does not exist, then this returns false.
+ * Otherwise, the result is an associative array that includes:
+ * - headers : map of HTTP headers used for GET/HEAD requests (name => value)
+ * - metadata : map of file metadata (name => value)
+ * Metadata keys and headers names will be returned in all lower-case.
+ * Additional values may be included for internal use only.
+ *
+ * Use FileBackend::hasFeatures() to check how well this is supported.
+ *
+ * @param array $params
+ * $params include:
+ * - src : source storage path
+ * - latest : use the latest available data
+ * @return Array|bool Returns false on failure
+ * @since 1.23
+ */
+ abstract public function getFileXAttributes( array $params );
+
+ /**
* Get the size (bytes) of a file at a storage path in the backend.
*
* @param array $params Parameters include:
diff --git a/includes/filebackend/FileBackendMultiWrite.php b/includes/filebackend/FileBackendMultiWrite.php
index 1c9832de74ba..1b2860a201da 100644
--- a/includes/filebackend/FileBackendMultiWrite.php
+++ b/includes/filebackend/FileBackendMultiWrite.php
@@ -567,6 +567,11 @@ class FileBackendMultiWrite extends FileBackend {
return $this->backends[$this->masterIndex]->getFileStat( $realParams );
}
+ public function getFileXAttributes( array $params ) {
+ $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+ return $this->backends[$this->masterIndex]->getFileXAttributes( $realParams );
+ }
+
public function getFileContentsMulti( array $params ) {
$realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
$contentsM = $this->backends[$this->masterIndex]->getFileContentsMulti( $realParams );
@@ -645,6 +650,10 @@ class FileBackendMultiWrite extends FileBackend {
return $this->backends[$this->masterIndex]->getFileList( $realParams );
}
+ public function getFeatures() {
+ return $this->backends[$this->masterIndex]->getFeatures();
+ }
+
public function clearCache( array $paths = null ) {
foreach ( $this->backends as $backend ) {
$realPaths = is_array( $paths ) ? $this->substPaths( $paths, $backend ) : null;
diff --git a/includes/filebackend/FileBackendStore.php b/includes/filebackend/FileBackendStore.php
index fe3a068456b1..7980d100675e 100644
--- a/includes/filebackend/FileBackendStore.php
+++ b/includes/filebackend/FileBackendStore.php
@@ -655,10 +655,15 @@ abstract class FileBackendStore extends FileBackend {
$this->cheapCache->set( $path, 'sha1',
array( 'hash' => $stat['sha1'], 'latest' => $latest ) );
}
+ if ( isset( $stat['xattr'] ) ) { // some backends store headers/metadata
+ $stat['xattr'] = self::normalizeXAttributes( $stat['xattr'] );
+ $this->cheapCache->set( $path, 'xattr',
+ array( 'map' => $stat['xattr'], 'latest' => $latest ) );
+ }
} elseif ( $stat === false ) { // file does not exist
$this->cheapCache->set( $path, 'stat', $latest ? 'NOT_EXIST_LATEST' : 'NOT_EXIST' );
- $this->cheapCache->set( $path, 'sha1', // the SHA-1 must be false too
- array( 'hash' => false, 'latest' => $latest ) );
+ $this->cheapCache->set( $path, 'xattr', array( 'map' => false, 'latest' => $latest ) );
+ $this->cheapCache->set( $path, 'sha1', array( 'hash' => false, 'latest' => $latest ) );
wfDebug( __METHOD__ . ": File $path does not exist.\n" );
} else { // an error occurred
wfDebug( __METHOD__ . ": Could not stat file $path.\n" );
@@ -697,6 +702,39 @@ abstract class FileBackendStore extends FileBackend {
return $contents;
}
+ final public function getFileXAttributes( array $params ) {
+ $path = self::normalizeStoragePath( $params['src'] );
+ if ( $path === null ) {
+ return false; // invalid storage path
+ }
+ $section = new ProfileSection( __METHOD__ . "-{$this->name}" );
+ $latest = !empty( $params['latest'] ); // use latest data?
+ if ( $this->cheapCache->has( $path, 'xattr', self::CACHE_TTL ) ) {
+ $stat = $this->cheapCache->get( $path, 'xattr' );
+ // If we want the latest data, check that this cached
+ // value was in fact fetched with the latest available data.
+ if ( !$latest || $stat['latest'] ) {
+ return $stat['map'];
+ }
+ }
+ wfProfileIn( __METHOD__ . '-miss' );
+ wfProfileIn( __METHOD__ . '-miss-' . $this->name );
+ $fields = $this->doGetFileXAttributes( $params );
+ $fields = is_array( $fields ) ? self::normalizeXAttributes( $fields ) : false;
+ wfProfileOut( __METHOD__ . '-miss-' . $this->name );
+ wfProfileOut( __METHOD__ . '-miss' );
+ $this->cheapCache->set( $path, 'xattr', array( 'map' => $fields, 'latest' => $latest ) );
+ return $fields;
+ }
+
+ /**
+ * @see FileBackendStore::getFileXAttributes()
+ * @return bool|string
+ */
+ protected function doGetFileXAttributes( array $params ) {
+ return array( 'headers' => array(), 'metadata' => array() ); // not supported
+ }
+
final public function getFileSha1Base36( array $params ) {
$path = self::normalizeStoragePath( $params['src'] );
if ( $path === null ) {
@@ -1625,11 +1663,34 @@ abstract class FileBackendStore extends FileBackend {
$this->cheapCache->set( $path, 'sha1',
array( 'hash' => $val['sha1'], 'latest' => $val['latest'] ) );
}
+ if ( isset( $val['xattr'] ) ) { // some backends store headers/metadata
+ $stat['xattr'] = self::normalizeXAttributes( $stat['xattr'] );
+ $this->cheapCache->set( $path, 'xattr',
+ array( 'map' => $val['xattr'], 'latest' => $val['latest'] ) );
+ }
}
}
}
/**
+ * Normalize file headers/metadata to the FileBackend::getFileXAttributes() format
+ *
+ * @param array $xattr
+ * @return array
+ * @since 1.22
+ */
+ final protected static function normalizeXAttributes( array $xattr ) {
+ $newXAttr = array( 'headers' => array(), 'metadata' => array() );
+ foreach ( $xattr['headers'] as $name => $value ) {
+ $newXAttr['headers'][strtolower( $name )] = $value;
+ }
+ foreach ( $xattr['metadata'] as $name => $value ) {
+ $newXAttr['metadata'][strtolower( $name )] = $value;
+ }
+ return $newXAttr;
+ }
+
+ /**
* Set the 'concurrency' option from a list of operation options
*
* @param array $opts Map of operation options
diff --git a/includes/filebackend/SwiftFileBackend.php b/includes/filebackend/SwiftFileBackend.php
index 528889b69bf6..037b1c3c3043 100644
--- a/includes/filebackend/SwiftFileBackend.php
+++ b/includes/filebackend/SwiftFileBackend.php
@@ -142,6 +142,10 @@ class SwiftFileBackend extends FileBackendStore {
$this->srvCache = $this->srvCache ?: new EmptyBagOStuff();
}
+ public function getFeatures() {
+ return ( FileBackend::ATTR_HEADERS | FileBackend::ATTR_METADATA );
+ }
+
protected function resolveContainerPath( $container, $relStoragePath ) {
if ( !mb_check_encoding( $relStoragePath, 'UTF-8' ) ) { // mb_string required by CF
return null; // not UTF-8, makes it hard to use CF and the swift HTTP API
@@ -179,6 +183,8 @@ class SwiftFileBackend extends FileBackendStore {
continue; // blacklisted
} elseif ( preg_match( '/^(x-)?content-/', $name ) ) {
$headers[$name] = $value; // allowed
+ } elseif ( preg_match( '/^content-(disposition)/', $name ) ) {
+ $headers[$name] = $value; // allowed
}
}
}
@@ -189,7 +195,7 @@ class SwiftFileBackend extends FileBackendStore {
$part = trim( $part );
$new = ( $disposition === '' ) ? $part : "{$disposition};{$part}";
if ( strlen( $new ) <= 255 ) {
- $res = $new;
+ $disposition = $new;
} else {
break; // too long; sigh
}
@@ -986,6 +992,20 @@ class SwiftFileBackend extends FileBackendStore {
$this->cheapCache->set( $path, 'stat', $val );
}
+ protected function doGetFileXAttributes( array $params ) {
+ $stat = $this->getFileStat( $params );
+ if ( $stat ) {
+ if ( !isset( $stat['xattr'] ) ) {
+ // Stat entries filled by file listings don't include metadata/headers
+ $this->clearCache( array( $params['src'] ) );
+ $stat = $this->getFileStat( $params );
+ }
+ return $stat['xattr'];
+ } else {
+ return false;
+ }
+ }
+
protected function doGetFileSha1base36( array $params ) {
$stat = $this->getFileStat( $params );
if ( $stat ) {