Bug 1339081 - Part 7: Implement safe metadata writting using temp files; r=asuth

This commit is contained in:
Jan Varga 2017-03-06 18:39:40 +01:00
parent f0b8133f92
commit a2571e9f2b
5 changed files with 214 additions and 63 deletions

View File

@ -179,7 +179,9 @@ enum AppId {
// origin.
// XXX We should get rid of old metadata files at some point, bug 1343576.
#define METADATA_FILE_NAME ".metadata"
#define METADATA_TMP_FILE_NAME ".metadata-tmp"
#define METADATA_V2_FILE_NAME ".metadata-v2"
#define METADATA_V2_TMP_FILE_NAME ".metadata-v2-tmp"
/******************************************************************************
* SQLite functions
@ -1287,6 +1289,27 @@ AssertNoUnderflow(T aDest, U aArg)
MOZ_ASSERT(uint64_t(aDest) >= uint64_t(aArg));
}
bool
IsOSMetadata(const nsAString& aFileName)
{
return aFileName.EqualsLiteral(DSSTORE_FILE_NAME);
}
bool
IsOriginMetadata(const nsAString& aFileName)
{
return aFileName.EqualsLiteral(METADATA_FILE_NAME) ||
aFileName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
IsOSMetadata(aFileName);
}
bool
IsTempMetadata(const nsAString& aFileName)
{
return aFileName.EqualsLiteral(METADATA_TMP_FILE_NAME) ||
aFileName.EqualsLiteral(METADATA_V2_TMP_FILE_NAME);
}
} // namespace
BackgroundThreadObject::BackgroundThreadObject()
@ -1688,9 +1711,8 @@ GetLastModifiedTime(nsIFile* aFile, int64_t* aTimestamp)
return rv;
}
if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
leafName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
if (IsOriginMetadata(leafName) ||
IsTempMetadata(leafName)) {
return NS_OK;
}
@ -1780,29 +1802,19 @@ enum FileFlag {
};
nsresult
GetOutputStream(nsIFile* aDirectory,
const nsAString& aFilename,
GetOutputStream(nsIFile* aFile,
FileFlag aFileFlag,
nsIOutputStream** aStream)
{
AssertIsOnIOThread();
nsCOMPtr<nsIFile> file;
nsresult rv = aDirectory->Clone(getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = file->Append(aFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsresult rv;
nsCOMPtr<nsIOutputStream> outputStream;
switch (aFileFlag) {
case kTruncateFileFlag: {
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
file);
aFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1812,7 +1824,7 @@ GetOutputStream(nsIFile* aDirectory,
case kUpdateFileFlag: {
bool exists;
rv = file->Exists(&exists);
rv = aFile->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1823,7 +1835,7 @@ GetOutputStream(nsIFile* aDirectory,
}
nsCOMPtr<nsIFileStream> stream;
rv = NS_NewLocalFileStream(getter_AddRefs(stream), file);
rv = NS_NewLocalFileStream(getter_AddRefs(stream), aFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1838,7 +1850,7 @@ GetOutputStream(nsIFile* aDirectory,
case kAppendFileFlag: {
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
file,
aFile,
PR_WRONLY | PR_CREATE_FILE | PR_APPEND);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -1856,21 +1868,18 @@ GetOutputStream(nsIFile* aDirectory,
}
nsresult
GetBinaryOutputStream(nsIFile* aDirectory,
const nsAString& aFilename,
GetBinaryOutputStream(nsIFile* aFile,
FileFlag aFileFlag,
nsIBinaryOutputStream** aStream)
{
nsCOMPtr<nsIOutputStream> outputStream;
nsresult rv = GetOutputStream(aDirectory,
aFilename,
nsresult rv = GetOutputStream(aFile,
aFileFlag,
getter_AddRefs(outputStream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIBinaryOutputStream> binaryStream =
do_CreateInstance("@mozilla.org/binaryoutputstream;1");
if (NS_WARN_IF(!binaryStream)) {
@ -1950,11 +1959,19 @@ CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
MOZ_ASSERT(groupPrefix == originPrefix);
nsCOMPtr<nsIFile> file;
nsresult rv = aDirectory->Clone(getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = file->Append(NS_LITERAL_STRING(METADATA_TMP_FILE_NAME));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIBinaryOutputStream> stream;
nsresult rv = GetBinaryOutputStream(aDirectory,
NS_LITERAL_STRING(METADATA_FILE_NAME),
kTruncateFileFlag,
getter_AddRefs(stream));
rv = GetBinaryOutputStream(file, kTruncateFileFlag, getter_AddRefs(stream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1982,21 +1999,47 @@ CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
return rv;
}
rv = stream->Flush();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = stream->Close();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = file->RenameTo(nullptr, NS_LITERAL_STRING(METADATA_FILE_NAME));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
CreateDirectoryMetadata2(nsIFile* aDirectory, int64_t aTimestamp,
const nsACString& aSuffix, const nsACString& aGroup,
CreateDirectoryMetadata2(nsIFile* aDirectory,
int64_t aTimestamp,
const nsACString& aSuffix,
const nsACString& aGroup,
const nsACString& aOrigin)
{
AssertIsOnIOThread();
MOZ_ASSERT(aDirectory);
nsCOMPtr<nsIFile> file;
nsresult rv = aDirectory->Clone(getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = file->Append(NS_LITERAL_STRING(METADATA_V2_TMP_FILE_NAME));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIBinaryOutputStream> stream;
nsresult rv = GetBinaryOutputStream(aDirectory,
NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
kTruncateFileFlag,
getter_AddRefs(stream));
rv = GetBinaryOutputStream(file, kTruncateFileFlag, getter_AddRefs(stream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -2047,6 +2090,21 @@ CreateDirectoryMetadata2(nsIFile* aDirectory, int64_t aTimestamp,
return rv;
}
rv = stream->Flush();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = stream->Close();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = file->RenameTo(nullptr, NS_LITERAL_STRING(METADATA_V2_FILE_NAME));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
@ -3753,7 +3811,7 @@ QuotaManager::InitializeRepository(PersistenceType aPersistenceType)
return rv;
}
if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
if (IsOSMetadata(leafName)) {
continue;
}
@ -3865,9 +3923,16 @@ QuotaManager::InitializeOrigin(PersistenceType aPersistenceType,
}
if (!isDirectory) {
if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
leafName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
if (IsOriginMetadata(leafName)) {
continue;
}
if (IsTempMetadata(leafName)) {
rv = file->Remove(/* recursive */ false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
continue;
}
@ -5708,20 +5773,22 @@ SaveOriginAccessTimeOp::DoDirectoryWork(QuotaManager* aQuotaManager)
PROFILER_LABEL("Quota", "SaveOriginAccessTimeOp::DoDirectoryWork",
js::ProfileEntry::Category::OTHER);
nsCOMPtr<nsIFile> directory;
nsCOMPtr<nsIFile> file;
nsresult rv =
aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
mOriginScope.GetOrigin(),
getter_AddRefs(directory));
getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = file->Append(NS_LITERAL_STRING(METADATA_V2_FILE_NAME));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIBinaryOutputStream> stream;
rv = GetBinaryOutputStream(directory,
NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
kUpdateFileFlag,
getter_AddRefs(stream));
rv = GetBinaryOutputStream(file, kUpdateFileFlag, getter_AddRefs(stream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -6075,9 +6142,18 @@ GetUsageOp::AddToUsage(QuotaManager* aQuotaManager,
// not yet initialized or just continuing otherwise).
// This can possibly be used by developers to add temporary backups into
// origin directories without losing get usage functionality.
if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
leafName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
if (IsOriginMetadata(leafName)) {
continue;
}
if (IsTempMetadata(leafName)) {
if (!initialized) {
rv = file->Remove(/* recursive */ false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
continue;
}
@ -6526,7 +6602,7 @@ ClearRequestBase::DeleteFiles(QuotaManager* aQuotaManager,
if (!isDirectory) {
// Unknown files during clearing are allowed. Just warn if we find them.
if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
if (!IsOSMetadata(leafName)) {
UNKNOWN_FILE_WARNING(leafName);
}
continue;
@ -7305,7 +7381,7 @@ CreateOrUpgradeDirectoryMetadataHelper::CreateOrUpgradeMetadataFiles()
}
} else {
// Unknown files during upgrade are allowed. Just warn if we find them.
if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
if (!IsOSMetadata(leafName)) {
UNKNOWN_FILE_WARNING(leafName);
}
continue;
@ -7619,11 +7695,19 @@ CreateOrUpgradeDirectoryMetadataHelper::DoProcessOriginDirectories()
return rv;
}
} else if (!originProps.mIgnore) {
nsCOMPtr<nsIFile> file;
rv = originProps.mDirectory->Clone(getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = file->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIBinaryOutputStream> stream;
rv = GetBinaryOutputStream(originProps.mDirectory,
NS_LITERAL_STRING(METADATA_FILE_NAME),
kAppendFileFlag,
getter_AddRefs(stream));
rv = GetBinaryOutputStream(file, kAppendFileFlag, getter_AddRefs(stream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -7687,7 +7771,7 @@ UpgradeDirectoryMetadataFrom1To2Helper::UpgradeMetadataFiles()
}
// Unknown files during upgrade are allowed. Just warn if we find them.
if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
if (!IsOSMetadata(leafName)) {
UNKNOWN_FILE_WARNING(leafName);
}
continue;

Binary file not shown.

View File

@ -8,8 +8,28 @@ var testGenerator = testSteps();
function* testSteps()
{
const storageFile = "storage.sqlite";
const metadataFile = "storage/permanent/chrome/.metadata";
const metadata2File = "storage/permanent/chrome/.metadata-v2";
const metadataFiles = [
{
path: "storage/permanent/chrome/.metadata",
tmp: false
},
{
path: "storage/permanent/chrome/.metadata-tmp",
tmp: true
},
{
path: "storage/permanent/chrome/.metadata-v2",
tmp: false
},
{
path: "storage/permanent/chrome/.metadata-v2-tmp",
tmp: true
}
]
info("Clearing");
@ -69,15 +89,17 @@ function* testSteps()
ok(request.result, "Origin directory was created");
file = getRelativeFile(metadataFile);
for (let metadataFile of metadataFiles) {
file = getRelativeFile(metadataFile.path);
exists = file.exists();
ok(exists, "Metadata file does exist");
exists = file.exists();
file = getRelativeFile(metadata2File);
exists = file.exists();
ok(exists, "Metadata v2 file does exist");
if (metadataFile.tmp) {
ok(!exists, "Metadata file doesn't exist");
} else {
ok(exists, "Metadata file does exist");
}
}
finishTest();
}

View File

@ -0,0 +1,43 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function* testSteps()
{
const tempMetadataFiles = [
"storage/permanent/chrome/.metadata-tmp",
"storage/permanent/chrome/.metadata-v2-tmp",
];
info("Clearing");
clear(continueToNextStepSync);
yield undefined;
info("Installing package");
installPackage("tempMetadataCleanup_profile");
info("Initializing origin");
let request = initChromeOrigin("persistent", continueToNextStepSync);
yield undefined;
ok(request.resultCode == NS_OK, "Initialization succeeded");
ok(!request.result, "Origin directory wasn't created");
for (let tempMetadataFile of tempMetadataFiles) {
info("Checking temp metadata file");
let file = getRelativeFile(tempMetadataFile);
let exists = file.exists();
ok(!exists, "Temp metadata file doesn't exist");
}
finishTest();
}

View File

@ -6,6 +6,8 @@
head = head.js
support-files =
basics_profile.zip
tempMetadataCleanup_profile.zip
[test_basics.js]
[test_tempMetadataCleanup.js]
[test_unknownFiles.js]