Bug 928321 - Implement a variant of safe-file-output-stream that doesn't flush by default. r=Yoric

This commit is contained in:
Sumit Agrawal 2013-12-02 12:51:25 -05:00
parent 56a6822193
commit c4928abe6f
6 changed files with 100 additions and 16 deletions

View File

@ -997,6 +997,25 @@ NS_NewLocalFileOutputStream(nsIOutputStream **result,
return rv;
}
// returns a file output stream which can be QI'ed to nsISafeOutputStream.
inline nsresult
NS_NewAtomicFileOutputStream(nsIOutputStream **result,
nsIFile *file,
int32_t ioFlags = -1,
int32_t perm = -1,
int32_t behaviorFlags = 0)
{
nsresult rv;
nsCOMPtr<nsIFileOutputStream> out =
do_CreateInstance(NS_ATOMICLOCALFILEOUTPUTSTREAM_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
rv = out->Init(file, ioFlags, perm, behaviorFlags);
if (NS_SUCCEEDED(rv))
out.forget(result);
}
return rv;
}
// returns a file output stream which can be QI'ed to nsISafeOutputStream.
inline nsresult
NS_NewSafeLocalFileOutputStream(nsIOutputStream **result,

View File

@ -832,23 +832,23 @@ nsFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
}
////////////////////////////////////////////////////////////////////////////////
// nsSafeFileOutputStream
// nsAtomicFileOutputStream
NS_IMPL_ISUPPORTS_INHERITED3(nsSafeFileOutputStream,
NS_IMPL_ISUPPORTS_INHERITED3(nsAtomicFileOutputStream,
nsFileOutputStream,
nsISafeOutputStream,
nsIOutputStream,
nsIFileOutputStream)
NS_IMETHODIMP
nsSafeFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
nsAtomicFileOutputStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
int32_t behaviorFlags)
{
return nsFileOutputStream::Init(file, ioFlags, perm, behaviorFlags);
}
nsresult
nsSafeFileOutputStream::DoOpen()
nsAtomicFileOutputStream::DoOpen()
{
// Make sure mOpenParams.localFile will be empty if we bail somewhere in
// this function
@ -896,7 +896,7 @@ nsSafeFileOutputStream::DoOpen()
}
NS_IMETHODIMP
nsSafeFileOutputStream::Close()
nsAtomicFileOutputStream::Close()
{
nsresult rv = nsFileOutputStream::Close();
@ -911,9 +911,8 @@ nsSafeFileOutputStream::Close()
}
NS_IMETHODIMP
nsSafeFileOutputStream::Finish()
nsAtomicFileOutputStream::Finish()
{
Flush();
nsresult rv = nsFileOutputStream::Close();
// if there is no temp file, don't try to move it over the original target.
@ -930,7 +929,7 @@ nsSafeFileOutputStream::Finish()
// temp file we gave out was actually a reference to the target file.
// since we succeeded in writing to the temp file (and hence succeeded
// in writing to the target file), there is nothing more to do.
#ifdef DEBUG
#ifdef DEBUG
bool equal;
if (NS_FAILED(mTargetFile->Equals(mTempFile, &equal)) || !equal)
NS_ERROR("mTempFile not equal to mTargetFile");
@ -959,7 +958,7 @@ nsSafeFileOutputStream::Finish()
}
NS_IMETHODIMP
nsSafeFileOutputStream::Write(const char *buf, uint32_t count, uint32_t *result)
nsAtomicFileOutputStream::Write(const char *buf, uint32_t count, uint32_t *result)
{
nsresult rv = nsFileOutputStream::Write(buf, count, result);
if (NS_SUCCEEDED(mWriteResult)) {
@ -970,10 +969,20 @@ nsSafeFileOutputStream::Write(const char *buf, uint32_t count, uint32_t *result)
if (NS_FAILED(mWriteResult) && count > 0)
NS_WARNING("writing to output stream failed! data may be lost");
}
}
return rv;
}
////////////////////////////////////////////////////////////////////////////////
// nsSafeFileOutputStream
NS_IMETHODIMP
nsSafeFileOutputStream::Finish()
{
(void) Flush();
return nsAtomicFileOutputStream::Finish();
}
////////////////////////////////////////////////////////////////////////////////
// nsFileStream

View File

@ -225,23 +225,28 @@ public:
////////////////////////////////////////////////////////////////////////////////
class nsSafeFileOutputStream : public nsFileOutputStream,
public nsISafeOutputStream
/**
* A safe file output stream that overwrites the destination file only
* once writing is complete. This protects against incomplete writes
* due to the process or the thread being interrupted or crashed.
*/
class nsAtomicFileOutputStream : public nsFileOutputStream,
public nsISafeOutputStream
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSISAFEOUTPUTSTREAM
nsSafeFileOutputStream() :
nsAtomicFileOutputStream() :
mTargetFileExists(true),
mWriteResult(NS_OK) {}
virtual ~nsSafeFileOutputStream()
virtual ~nsAtomicFileOutputStream()
{
Close();
}
virtual nsresult DoOpen();
virtual nsresult DoOpen() MOZ_OVERRIDE;
NS_IMETHODIMP Close();
NS_IMETHODIMP Write(const char *buf, uint32_t count, uint32_t *result);
@ -253,6 +258,22 @@ protected:
bool mTargetFileExists;
nsresult mWriteResult; // Internally set in Write()
};
////////////////////////////////////////////////////////////////////////////////
/**
* A safe file output stream that overwrites the destination file only
* once writing + flushing is complete. This protects against more
* classes of software/hardware errors than nsAtomicFileOutputStream,
* at the expense of being more costly to the disk, OS and battery.
*/
class nsSafeFileOutputStream : public nsAtomicFileOutputStream
{
public:
NS_IMETHOD Finish();
};
////////////////////////////////////////////////////////////////////////////////

View File

@ -396,7 +396,17 @@
{0x8c, 0xda, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \
}
// component implementing nsISafeOutputStream
// components implementing nsISafeOutputStream
#define NS_ATOMICLOCALFILEOUTPUTSTREAM_CONTRACTID \
"@mozilla.org/network/atomic-file-output-stream;1"
#define NS_ATOMICLOCALFILEOUTPUTSTREAM_CID \
{ /* 6EAE857E-4BA9-11E3-9B39-B4036188709B */ \
0x6EAE857E, \
0x4BA9, \
0x11E3, \
{0x9b, 0x39, 0xb4, 0x03, 0x61, 0x88, 0x70, 0x9b} \
}
#define NS_SAFELOCALFILEOUTPUTSTREAM_CONTRACTID \
"@mozilla.org/network/safe-file-output-stream;1"
#define NS_SAFELOCALFILEOUTPUTSTREAM_CID \

View File

@ -102,6 +102,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(BackgroundFileSaverStreamListener, Init)
#include "nsSyncStreamListener.h"
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSyncStreamListener, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsAtomicFileOutputStream)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSafeFileOutputStream)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsFileStream)
@ -705,6 +707,7 @@ NS_DEFINE_NAMED_CID(NS_LOADGROUP_CID);
NS_DEFINE_NAMED_CID(NS_LOCALFILEINPUTSTREAM_CID);
NS_DEFINE_NAMED_CID(NS_LOCALFILEOUTPUTSTREAM_CID);
NS_DEFINE_NAMED_CID(NS_PARTIALLOCALFILEINPUTSTREAM_CID);
NS_DEFINE_NAMED_CID(NS_ATOMICLOCALFILEOUTPUTSTREAM_CID);
NS_DEFINE_NAMED_CID(NS_SAFELOCALFILEOUTPUTSTREAM_CID);
NS_DEFINE_NAMED_CID(NS_LOCALFILESTREAM_CID);
NS_DEFINE_NAMED_CID(NS_URICHECKER_CID);
@ -845,6 +848,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
{ &kNS_LOCALFILEINPUTSTREAM_CID, false, nullptr, nsFileInputStream::Create },
{ &kNS_LOCALFILEOUTPUTSTREAM_CID, false, nullptr, nsFileOutputStream::Create },
{ &kNS_PARTIALLOCALFILEINPUTSTREAM_CID, false, nullptr, nsPartialFileInputStream::Create },
{ &kNS_ATOMICLOCALFILEOUTPUTSTREAM_CID, false, nullptr, nsAtomicFileOutputStreamConstructor },
{ &kNS_SAFELOCALFILEOUTPUTSTREAM_CID, false, nullptr, nsSafeFileOutputStreamConstructor },
{ &kNS_LOCALFILESTREAM_CID, false, nullptr, nsFileStreamConstructor },
{ &kNS_URICHECKER_CID, false, nullptr, nsURICheckerConstructor },
@ -987,6 +991,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
{ NS_LOCALFILEINPUTSTREAM_CONTRACTID, &kNS_LOCALFILEINPUTSTREAM_CID },
{ NS_LOCALFILEOUTPUTSTREAM_CONTRACTID, &kNS_LOCALFILEOUTPUTSTREAM_CID },
{ NS_PARTIALLOCALFILEINPUTSTREAM_CONTRACTID, &kNS_PARTIALLOCALFILEINPUTSTREAM_CID },
{ NS_ATOMICLOCALFILEOUTPUTSTREAM_CONTRACTID, &kNS_ATOMICLOCALFILEOUTPUTSTREAM_CID },
{ NS_SAFELOCALFILEOUTPUTSTREAM_CONTRACTID, &kNS_SAFELOCALFILEOUTPUTSTREAM_CID },
{ NS_LOCALFILESTREAM_CONTRACTID, &kNS_LOCALFILESTREAM_CID },
{ NS_URICHECKER_CONTRACT_ID, &kNS_URICHECKER_CID },

View File

@ -3,6 +3,20 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function write_atomic(file, str) {
var stream = Cc["@mozilla.org/network/atomic-file-output-stream;1"]
.createInstance(Ci.nsIFileOutputStream);
stream.init(file, -1, -1, 0);
do {
var written = stream.write(str, str.length);
if (written == str.length)
break;
str = str.substring(written);
} while (1);
stream.QueryInterface(Ci.nsISafeOutputStream).finish();
stream.close();
}
function write(file, str) {
var stream = Cc["@mozilla.org/network/safe-file-output-stream;1"]
.createInstance(Ci.nsIFileOutputStream);
@ -43,4 +57,10 @@ function run_test()
write(file, "Second write");
checkFile(file, "Second write");
write_atomic(file, "First write: Atomic");
checkFile(file, "First write: Atomic");
write_atomic(file, "Second write: Atomic");
checkFile(file, "Second write: Atomic");
}