mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 05:50:55 +01:00
Fix an issue where migrating files could prematurely destroy duplicates
Summary: Fixes T5912. When migrating files, we try to clean up the old data. However, this code isn't aware of reference counting, and unconditionally destroys the old data. For example, if you migrate files `F1` and `F2` and they have the same data, we'll delete the shared data when we migrate `F1`. Then you'll get an error when you migrate `F2`. Since this only affects duplicate files, it primarily hits default profile pictures, which are the most numerous duplicate files on most installs. Test Plan: - Verified that the theory was correct by uploading two copies of a file and migrating the first one, before applying the patch. The second one's data was nuked and it couldn't be migrated. - Applied patch. - Uploaded two copies of a new file, migrated the first one (no data deletion), migrated the second one (data correctly deleted). - Uploaded two copies of another new file, `bin/remove destory'd` the first one (no data deletion), then did it to the second one (data correctly deleted). Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T5912 Differential Revision: https://secure.phabricator.com/D10312
This commit is contained in:
parent
e5acdd85e6
commit
66fa59d04d
1 changed files with 37 additions and 20 deletions
|
@ -318,13 +318,17 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
$params);
|
||||
|
||||
$old_engine = $this->instantiateStorageEngine();
|
||||
$old_identifier = $this->getStorageEngine();
|
||||
$old_handle = $this->getStorageHandle();
|
||||
|
||||
$this->setStorageEngine($new_identifier);
|
||||
$this->setStorageHandle($new_handle);
|
||||
$this->save();
|
||||
|
||||
$old_engine->deleteFile($old_handle);
|
||||
$this->deleteFileDataIfUnused(
|
||||
$old_engine,
|
||||
$old_identifier,
|
||||
$old_handle);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -438,30 +442,43 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
$ret = parent::delete();
|
||||
$this->saveTransaction();
|
||||
|
||||
// Check to see if other files are using storage
|
||||
$other_file = id(new PhabricatorFile())->loadAllWhere(
|
||||
'storageEngine = %s AND storageHandle = %s AND
|
||||
storageFormat = %s AND id != %d LIMIT 1',
|
||||
$this->deleteFileDataIfUnused(
|
||||
$this->instantiateStorageEngine(),
|
||||
$this->getStorageEngine(),
|
||||
$this->getStorageHandle(),
|
||||
$this->getStorageFormat(),
|
||||
$this->getID());
|
||||
|
||||
// If this is the only file using the storage, delete storage
|
||||
if (!$other_file) {
|
||||
$engine = $this->instantiateStorageEngine();
|
||||
try {
|
||||
$engine->deleteFile($this->getStorageHandle());
|
||||
} catch (Exception $ex) {
|
||||
// In the worst case, we're leaving some data stranded in a storage
|
||||
// engine, which is fine.
|
||||
phlog($ex);
|
||||
}
|
||||
}
|
||||
$this->getStorageHandle());
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destroy stored file data if there are no remaining files which reference
|
||||
* it.
|
||||
*/
|
||||
private function deleteFileDataIfUnused(
|
||||
PhabricatorFileStorageEngine $engine,
|
||||
$engine_identifier,
|
||||
$handle) {
|
||||
|
||||
// Check to see if any files are using storage.
|
||||
$usage = id(new PhabricatorFile())->loadAllWhere(
|
||||
'storageEngine = %s AND storageHandle = %s LIMIT 1',
|
||||
$engine_identifier,
|
||||
$handle);
|
||||
|
||||
// If there are no files using the storage, destroy the actual storage.
|
||||
if (!$usage) {
|
||||
try {
|
||||
$engine->deleteFile($handle);
|
||||
} catch (Exception $ex) {
|
||||
// In the worst case, we're leaving some data stranded in a storage
|
||||
// engine, which is not a big deal.
|
||||
phlog($ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function hashFileContent($data) {
|
||||
return sha1($data);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue