mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-18 10:41:08 +01:00
Add support to Files for file storage formats, to support encryption-at-rest
Summary: Ref T11140. When reading and writing files, we optionally apply a "storage format" to them. The default format is "raw", which means we just store the raw data. This change modularizes formats and adds a "rot13" format, which proves formatting works and is testable. In the future, I'll add real encryption formats. Test Plan: - Added unit tests. - Viewed files in web UI. - Changed a file's format to rot13, saw the data get rotated on display. - Set default format to rot13: - Uploaded a small file, verified data was stored as rot13. - Uploaded a large file, verified metadata was stored as "raw" (just a type, no actual data) and blob data was stored as rot13. Reviewers: chad Reviewed By: chad Maniphest Tasks: T11140 Differential Revision: https://secure.phabricator.com/D16122
This commit is contained in:
parent
f9a58fafba
commit
1049feb0ed
9 changed files with 244 additions and 31 deletions
|
@ -2490,12 +2490,16 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFileLinkView' => 'view/layout/PhabricatorFileLinkView.php',
|
'PhabricatorFileLinkView' => 'view/layout/PhabricatorFileLinkView.php',
|
||||||
'PhabricatorFileListController' => 'applications/files/controller/PhabricatorFileListController.php',
|
'PhabricatorFileListController' => 'applications/files/controller/PhabricatorFileListController.php',
|
||||||
'PhabricatorFileQuery' => 'applications/files/query/PhabricatorFileQuery.php',
|
'PhabricatorFileQuery' => 'applications/files/query/PhabricatorFileQuery.php',
|
||||||
|
'PhabricatorFileROT13StorageFormat' => 'applications/files/format/PhabricatorFileROT13StorageFormat.php',
|
||||||
|
'PhabricatorFileRawStorageFormat' => 'applications/files/format/PhabricatorFileRawStorageFormat.php',
|
||||||
'PhabricatorFileSchemaSpec' => 'applications/files/storage/PhabricatorFileSchemaSpec.php',
|
'PhabricatorFileSchemaSpec' => 'applications/files/storage/PhabricatorFileSchemaSpec.php',
|
||||||
'PhabricatorFileSearchEngine' => 'applications/files/query/PhabricatorFileSearchEngine.php',
|
'PhabricatorFileSearchEngine' => 'applications/files/query/PhabricatorFileSearchEngine.php',
|
||||||
'PhabricatorFileStorageBlob' => 'applications/files/storage/PhabricatorFileStorageBlob.php',
|
'PhabricatorFileStorageBlob' => 'applications/files/storage/PhabricatorFileStorageBlob.php',
|
||||||
'PhabricatorFileStorageConfigurationException' => 'applications/files/exception/PhabricatorFileStorageConfigurationException.php',
|
'PhabricatorFileStorageConfigurationException' => 'applications/files/exception/PhabricatorFileStorageConfigurationException.php',
|
||||||
'PhabricatorFileStorageEngine' => 'applications/files/engine/PhabricatorFileStorageEngine.php',
|
'PhabricatorFileStorageEngine' => 'applications/files/engine/PhabricatorFileStorageEngine.php',
|
||||||
'PhabricatorFileStorageEngineTestCase' => 'applications/files/engine/__tests__/PhabricatorFileStorageEngineTestCase.php',
|
'PhabricatorFileStorageEngineTestCase' => 'applications/files/engine/__tests__/PhabricatorFileStorageEngineTestCase.php',
|
||||||
|
'PhabricatorFileStorageFormat' => 'applications/files/format/PhabricatorFileStorageFormat.php',
|
||||||
|
'PhabricatorFileStorageFormatTestCase' => 'applications/files/format/__tests__/PhabricatorFileStorageFormatTestCase.php',
|
||||||
'PhabricatorFileTemporaryGarbageCollector' => 'applications/files/garbagecollector/PhabricatorFileTemporaryGarbageCollector.php',
|
'PhabricatorFileTemporaryGarbageCollector' => 'applications/files/garbagecollector/PhabricatorFileTemporaryGarbageCollector.php',
|
||||||
'PhabricatorFileTestCase' => 'applications/files/storage/__tests__/PhabricatorFileTestCase.php',
|
'PhabricatorFileTestCase' => 'applications/files/storage/__tests__/PhabricatorFileTestCase.php',
|
||||||
'PhabricatorFileTestDataGenerator' => 'applications/files/lipsum/PhabricatorFileTestDataGenerator.php',
|
'PhabricatorFileTestDataGenerator' => 'applications/files/lipsum/PhabricatorFileTestDataGenerator.php',
|
||||||
|
@ -7134,12 +7138,16 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFileLinkView' => 'AphrontView',
|
'PhabricatorFileLinkView' => 'AphrontView',
|
||||||
'PhabricatorFileListController' => 'PhabricatorFileController',
|
'PhabricatorFileListController' => 'PhabricatorFileController',
|
||||||
'PhabricatorFileQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorFileQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
'PhabricatorFileROT13StorageFormat' => 'PhabricatorFileStorageFormat',
|
||||||
|
'PhabricatorFileRawStorageFormat' => 'PhabricatorFileStorageFormat',
|
||||||
'PhabricatorFileSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
'PhabricatorFileSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||||
'PhabricatorFileSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
'PhabricatorFileSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
|
'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
|
||||||
'PhabricatorFileStorageConfigurationException' => 'Exception',
|
'PhabricatorFileStorageConfigurationException' => 'Exception',
|
||||||
'PhabricatorFileStorageEngine' => 'Phobject',
|
'PhabricatorFileStorageEngine' => 'Phobject',
|
||||||
'PhabricatorFileStorageEngineTestCase' => 'PhabricatorTestCase',
|
'PhabricatorFileStorageEngineTestCase' => 'PhabricatorTestCase',
|
||||||
|
'PhabricatorFileStorageFormat' => 'Phobject',
|
||||||
|
'PhabricatorFileStorageFormatTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorFileTemporaryGarbageCollector' => 'PhabricatorGarbageCollector',
|
'PhabricatorFileTemporaryGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||||
'PhabricatorFileTestCase' => 'PhabricatorTestCase',
|
'PhabricatorFileTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorFileTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
'PhabricatorFileTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
||||||
|
|
|
@ -256,8 +256,10 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
|
||||||
$types[] = pht('Profile');
|
$types[] = pht('Profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($types) {
|
||||||
$types = implode(', ', $types);
|
$types = implode(', ', $types);
|
||||||
$finfo->addProperty(pht('Attributes'), $types);
|
$finfo->addProperty(pht('Attributes'), $types);
|
||||||
|
}
|
||||||
|
|
||||||
$storage_properties = new PHUIPropertyListView();
|
$storage_properties = new PHUIPropertyListView();
|
||||||
$box->addPropertyList($storage_properties, pht('Storage'));
|
$box->addPropertyList($storage_properties, pht('Storage'));
|
||||||
|
@ -266,9 +268,14 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
|
||||||
pht('Engine'),
|
pht('Engine'),
|
||||||
$file->getStorageEngine());
|
$file->getStorageEngine());
|
||||||
|
|
||||||
$storage_properties->addProperty(
|
$format_key = $file->getStorageFormat();
|
||||||
pht('Format'),
|
$format = PhabricatorFileStorageFormat::getFormat($format_key);
|
||||||
$file->getStorageFormat());
|
if ($format) {
|
||||||
|
$format_name = $format->getStorageFormatName();
|
||||||
|
} else {
|
||||||
|
$format_name = pht('Unknown ("%s")', $format_key);
|
||||||
|
}
|
||||||
|
$storage_properties->addProperty(pht('Format'), $format_name);
|
||||||
|
|
||||||
$storage_properties->addProperty(
|
$storage_properties->addProperty(
|
||||||
pht('Handle'),
|
pht('Handle'),
|
||||||
|
|
|
@ -174,7 +174,7 @@ final class PhabricatorChunkedFileStorageEngine
|
||||||
return (4 * 1024 * 1024);
|
return (4 * 1024 * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFileDataIterator(PhabricatorFile $file, $begin, $end) {
|
public function getRawFileDataIterator(PhabricatorFile $file, $begin, $end) {
|
||||||
$chunks = id(new PhabricatorFileChunkQuery())
|
$chunks = id(new PhabricatorFileChunkQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
->withChunkHandles(array($file->getStorageHandle()))
|
->withChunkHandles(array($file->getStorageHandle()))
|
||||||
|
|
|
@ -325,10 +325,10 @@ abstract class PhabricatorFileStorageEngine extends Phobject {
|
||||||
return $engine->getChunkSize();
|
return $engine->getChunkSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFileDataIterator(PhabricatorFile $file, $begin, $end) {
|
public function getRawFileDataIterator(PhabricatorFile $file, $begin, $end) {
|
||||||
// The default implementation is trivial and just loads the entire file
|
// The default implementation is trivial and just loads the entire file
|
||||||
// upfront.
|
// upfront.
|
||||||
$data = $file->loadFileData();
|
$data = $this->readFile($file->getStorageHandle());
|
||||||
|
|
||||||
if ($begin !== null && $end !== null) {
|
if ($begin !== null && $end !== null) {
|
||||||
$data = substr($data, $begin, ($end - $begin));
|
$data = substr($data, $begin, ($end - $begin));
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trivial example of a file storage format for at-rest encryption.
|
||||||
|
*
|
||||||
|
* This format applies ROT13 encoding to file data as it is stored and
|
||||||
|
* reverses it on the way out. This encoding is trivially reversible. This
|
||||||
|
* format is for testing, developing, and understanding encoding formats and
|
||||||
|
* is not intended for production use.
|
||||||
|
*/
|
||||||
|
final class PhabricatorFileROT13StorageFormat
|
||||||
|
extends PhabricatorFileStorageFormat {
|
||||||
|
|
||||||
|
const FORMATKEY = 'rot13';
|
||||||
|
|
||||||
|
public function getStorageFormatName() {
|
||||||
|
return pht('Encoded (ROT13)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newReadIterator($raw_iterator) {
|
||||||
|
$file = $this->getFile();
|
||||||
|
$iterations = $file->getStorageProperty('iterations', 1);
|
||||||
|
|
||||||
|
$value = $file->loadDataFromIterator($raw_iterator);
|
||||||
|
for ($ii = 0; $ii < $iterations; $ii++) {
|
||||||
|
$value = str_rot13($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newWriteIterator($raw_iterator) {
|
||||||
|
return $this->newReadIterator($raw_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newStorageProperties() {
|
||||||
|
// For extreme security, repeatedly encode the data using a random (odd)
|
||||||
|
// number of iterations.
|
||||||
|
return array(
|
||||||
|
'iterations' => (mt_rand(1, 3) * 2) - 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorFileRawStorageFormat
|
||||||
|
extends PhabricatorFileStorageFormat {
|
||||||
|
|
||||||
|
const FORMATKEY = 'raw';
|
||||||
|
|
||||||
|
public function getStorageFormatName() {
|
||||||
|
return pht('Raw Data');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newReadIterator($raw_iterator) {
|
||||||
|
return $raw_iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newWriteIterator($raw_iterator) {
|
||||||
|
return $raw_iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorFileStorageFormat
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
private $file;
|
||||||
|
|
||||||
|
final public function setFile(PhabricatorFile $file) {
|
||||||
|
$this->file = $file;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getFile() {
|
||||||
|
if (!$this->file) {
|
||||||
|
throw new PhutilInvalidStateException('setFile');
|
||||||
|
}
|
||||||
|
return $this->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function getStorageFormatName();
|
||||||
|
|
||||||
|
abstract public function newReadIterator($raw_iterator);
|
||||||
|
abstract public function newWriteIterator($raw_iterator);
|
||||||
|
|
||||||
|
public function newStorageProperties() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getStorageFormatKey() {
|
||||||
|
return $this->getPhobjectClassConstant('FORMATKEY');
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function getAllFormats() {
|
||||||
|
return id(new PhutilClassMapQuery())
|
||||||
|
->setAncestorClass(__CLASS__)
|
||||||
|
->setUniqueMethod('getStorageFormatKey')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function getFormat($key) {
|
||||||
|
$formats = self::getAllFormats();
|
||||||
|
return idx($formats, $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function requireFormat($key) {
|
||||||
|
$format = self::getFormat($key);
|
||||||
|
|
||||||
|
if (!$format) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'No file storage format with key "%s" exists.',
|
||||||
|
$key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $format;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorFileStorageFormatTestCase extends PhabricatorTestCase {
|
||||||
|
|
||||||
|
protected function getPhabricatorTestCaseConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRot13Storage() {
|
||||||
|
$engine = new PhabricatorTestStorageEngine();
|
||||||
|
$rot13_format = PhabricatorFileROT13StorageFormat::FORMATKEY;
|
||||||
|
|
||||||
|
$data = 'The cow jumped over the full moon.';
|
||||||
|
$expect = 'Gur pbj whzcrq bire gur shyy zbba.';
|
||||||
|
|
||||||
|
$params = array(
|
||||||
|
'name' => 'test.dat',
|
||||||
|
'storageEngines' => array(
|
||||||
|
$engine,
|
||||||
|
),
|
||||||
|
'format' => $rot13_format,
|
||||||
|
);
|
||||||
|
|
||||||
|
$file = PhabricatorFile::newFromFileData($data, $params);
|
||||||
|
|
||||||
|
// We should have a file stored as rot13, which reads back the input
|
||||||
|
// data correctly.
|
||||||
|
$this->assertEqual($rot13_format, $file->getStorageFormat());
|
||||||
|
$this->assertEqual($data, $file->loadFileData());
|
||||||
|
|
||||||
|
// The actual raw data in the storage engine should be encoded.
|
||||||
|
$raw_data = $engine->readFile($file->getStorageHandle());
|
||||||
|
$this->assertEqual($expect, $raw_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -26,14 +26,13 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
PhabricatorPolicyInterface,
|
PhabricatorPolicyInterface,
|
||||||
PhabricatorDestructibleInterface {
|
PhabricatorDestructibleInterface {
|
||||||
|
|
||||||
const STORAGE_FORMAT_RAW = 'raw';
|
|
||||||
|
|
||||||
const METADATA_IMAGE_WIDTH = 'width';
|
const METADATA_IMAGE_WIDTH = 'width';
|
||||||
const METADATA_IMAGE_HEIGHT = 'height';
|
const METADATA_IMAGE_HEIGHT = 'height';
|
||||||
const METADATA_CAN_CDN = 'canCDN';
|
const METADATA_CAN_CDN = 'canCDN';
|
||||||
const METADATA_BUILTIN = 'builtin';
|
const METADATA_BUILTIN = 'builtin';
|
||||||
const METADATA_PARTIAL = 'partial';
|
const METADATA_PARTIAL = 'partial';
|
||||||
const METADATA_PROFILE = 'profile';
|
const METADATA_PROFILE = 'profile';
|
||||||
|
const METADATA_STORAGE = 'storage';
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $mimeType;
|
protected $mimeType;
|
||||||
|
@ -233,10 +232,10 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
$hash);
|
$hash);
|
||||||
|
|
||||||
if ($file) {
|
if ($file) {
|
||||||
// copy storageEngine, storageHandle, storageFormat
|
|
||||||
$copy_of_storage_engine = $file->getStorageEngine();
|
$copy_of_storage_engine = $file->getStorageEngine();
|
||||||
$copy_of_storage_handle = $file->getStorageHandle();
|
$copy_of_storage_handle = $file->getStorageHandle();
|
||||||
$copy_of_storage_format = $file->getStorageFormat();
|
$copy_of_storage_format = $file->getStorageFormat();
|
||||||
|
$copy_of_storage_properties = $file->getStorageProperties();
|
||||||
$copy_of_byte_size = $file->getByteSize();
|
$copy_of_byte_size = $file->getByteSize();
|
||||||
$copy_of_mime_type = $file->getMimeType();
|
$copy_of_mime_type = $file->getMimeType();
|
||||||
|
|
||||||
|
@ -248,6 +247,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
$new_file->setStorageEngine($copy_of_storage_engine);
|
$new_file->setStorageEngine($copy_of_storage_engine);
|
||||||
$new_file->setStorageHandle($copy_of_storage_handle);
|
$new_file->setStorageHandle($copy_of_storage_handle);
|
||||||
$new_file->setStorageFormat($copy_of_storage_format);
|
$new_file->setStorageFormat($copy_of_storage_format);
|
||||||
|
$new_file->setStorageProperties($copy_of_storage_properties);
|
||||||
$new_file->setMimeType($copy_of_mime_type);
|
$new_file->setMimeType($copy_of_mime_type);
|
||||||
$new_file->copyDimensions($file);
|
$new_file->copyDimensions($file);
|
||||||
|
|
||||||
|
@ -290,7 +290,11 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
|
|
||||||
$file->setStorageEngine($engine->getEngineIdentifier());
|
$file->setStorageEngine($engine->getEngineIdentifier());
|
||||||
$file->setStorageHandle(PhabricatorFileChunk::newChunkHandle());
|
$file->setStorageHandle(PhabricatorFileChunk::newChunkHandle());
|
||||||
$file->setStorageFormat(self::STORAGE_FORMAT_RAW);
|
|
||||||
|
// Chunked files are always stored raw because they do not actually store
|
||||||
|
// data. The chunks do, and can be individually formatted.
|
||||||
|
$file->setStorageFormat(PhabricatorFileRawStorageFormat::FORMATKEY);
|
||||||
|
|
||||||
$file->setIsPartial(1);
|
$file->setIsPartial(1);
|
||||||
|
|
||||||
$file->readPropertiesFromParameters($params);
|
$file->readPropertiesFromParameters($params);
|
||||||
|
@ -322,6 +326,16 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
|
|
||||||
$file = self::initializeNewFile();
|
$file = self::initializeNewFile();
|
||||||
|
|
||||||
|
$default_key = PhabricatorFileRawStorageFormat::FORMATKEY;
|
||||||
|
$format_key = idx($params, 'format', $default_key);
|
||||||
|
|
||||||
|
$format = id(clone PhabricatorFileStorageFormat::requireFormat($format_key))
|
||||||
|
->setFile($file);
|
||||||
|
|
||||||
|
$properties = $format->newStorageProperties();
|
||||||
|
$file->setStorageFormat($format->getStorageFormatKey());
|
||||||
|
$file->setStorageProperties($properties);
|
||||||
|
|
||||||
$data_handle = null;
|
$data_handle = null;
|
||||||
$engine_identifier = null;
|
$engine_identifier = null;
|
||||||
$exceptions = array();
|
$exceptions = array();
|
||||||
|
@ -361,10 +375,6 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
$file->setStorageEngine($engine_identifier);
|
$file->setStorageEngine($engine_identifier);
|
||||||
$file->setStorageHandle($data_handle);
|
$file->setStorageHandle($data_handle);
|
||||||
|
|
||||||
// TODO: This is probably YAGNI, but allows for us to do encryption or
|
|
||||||
// compression later if we want.
|
|
||||||
$file->setStorageFormat(self::STORAGE_FORMAT_RAW);
|
|
||||||
|
|
||||||
$file->readPropertiesFromParameters($params);
|
$file->readPropertiesFromParameters($params);
|
||||||
|
|
||||||
if (!$file->getMimeType()) {
|
if (!$file->getMimeType()) {
|
||||||
|
@ -434,7 +444,15 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
|
|
||||||
$engine_class = get_class($engine);
|
$engine_class = get_class($engine);
|
||||||
|
|
||||||
$data_handle = $engine->writeFile($data, $params);
|
$key = $this->getStorageFormat();
|
||||||
|
$format = id(clone PhabricatorFileStorageFormat::requireFormat($key))
|
||||||
|
->setFile($this);
|
||||||
|
|
||||||
|
$data_iterator = array($data);
|
||||||
|
$formatted_iterator = $format->newWriteIterator($data_iterator);
|
||||||
|
$formatted_data = $this->loadDataFromIterator($formatted_iterator);
|
||||||
|
|
||||||
|
$data_handle = $engine->writeFile($formatted_data, $params);
|
||||||
|
|
||||||
if (!$data_handle || strlen($data_handle) > 255) {
|
if (!$data_handle || strlen($data_handle) > 255) {
|
||||||
// This indicates an improperly implemented storage engine.
|
// This indicates an improperly implemented storage engine.
|
||||||
|
@ -663,19 +681,8 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadFileData() {
|
public function loadFileData() {
|
||||||
|
$iterator = $this->getFileDataIterator();
|
||||||
$engine = $this->instantiateStorageEngine();
|
return $this->loadDataFromIterator($iterator);
|
||||||
$data = $engine->readFile($this->getStorageHandle());
|
|
||||||
|
|
||||||
switch ($this->getStorageFormat()) {
|
|
||||||
case self::STORAGE_FORMAT_RAW:
|
|
||||||
$data = $data;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception(pht('Unknown storage format.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -688,7 +695,14 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
*/
|
*/
|
||||||
public function getFileDataIterator($begin = null, $end = null) {
|
public function getFileDataIterator($begin = null, $end = null) {
|
||||||
$engine = $this->instantiateStorageEngine();
|
$engine = $this->instantiateStorageEngine();
|
||||||
return $engine->getFileDataIterator($this, $begin, $end);
|
$raw_iterator = $engine->getRawFileDataIterator($this, $begin, $end);
|
||||||
|
|
||||||
|
$key = $this->getStorageFormat();
|
||||||
|
|
||||||
|
$format = id(clone PhabricatorFileStorageFormat::requireFormat($key))
|
||||||
|
->setFile($this);
|
||||||
|
|
||||||
|
return $format->newReadIterator($raw_iterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -917,6 +931,30 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
return Filesystem::readRandomCharacters(20);
|
return Filesystem::readRandomCharacters(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setStorageProperties(array $properties) {
|
||||||
|
$this->metadata[self::METADATA_STORAGE] = $properties;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStorageProperties() {
|
||||||
|
return idx($this->metadata, self::METADATA_STORAGE, array());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStorageProperty($key, $default = null) {
|
||||||
|
$properties = $this->getStorageProperties();
|
||||||
|
return idx($properties, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadDataFromIterator($iterator) {
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
foreach ($iterator as $chunk) {
|
||||||
|
$result .= $chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
public function updateDimensions($save = true) {
|
public function updateDimensions($save = true) {
|
||||||
if (!$this->isViewableImage()) {
|
if (!$this->isViewableImage()) {
|
||||||
throw new Exception(pht('This file is not a viewable image.'));
|
throw new Exception(pht('This file is not a viewable image.'));
|
||||||
|
|
Loading…
Reference in a new issue