1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-04 17:49:45 +02:00

Still better disk error detection in exports

This commit is contained in:
Paul Licameli 2018-01-22 16:49:04 -05:00
commit f52f2837be
13 changed files with 126 additions and 31 deletions

View File

@ -1469,7 +1469,8 @@ FLAC_API FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder)
if(0 != encoder->private_->file) {
if(encoder->private_->file != stdout)
fclose(encoder->private_->file);
error = fflush(encoder->private_->file) ||
fclose(encoder->private_->file);
encoder->private_->file = 0;
}

View File

@ -209,10 +209,17 @@ int ufile_close(AVIOContext *pb)
{
std::unique_ptr<wxFile> f{ (wxFile *)pb->opaque };
if (f)
f->Close();
bool success = true;
if (f) {
success = f->Flush() && f->Close();
pb->opaque = nullptr;
}
return 0;
// We're not certain that a close error is for want of space, but we'll
// guess that
return success ? 0 : -ENOSPC;
// Implicitly destroy the wxFile object here
}
// Open a file with a (possibly) Unicode filename

View File

@ -928,6 +928,18 @@ private:
// Deleter adaptor for functions like av_free that take a pointer
template<typename T, typename R, R(*Fn)(T*)> struct AV_Deleter {
inline R operator() (T* p) const
{
R result{};
if (p)
result = Fn(p);
return result;
}
};
// Specialization of previous for void return
template<typename T, void(*Fn)(T*)>
struct AV_Deleter<T, void, Fn> {
inline void operator() (T* p) const
{
if (p)
@ -959,9 +971,19 @@ using AVCodecContextHolder = std::unique_ptr<
using AVDictionaryCleanup = std::unique_ptr<
AVDictionary*, AV_Deleter<AVDictionary*, void, av_dict_free>
>;
using UFileHolder = std::unique_ptr<
struct UFileHolder : public std::unique_ptr<
AVIOContext, AV_Deleter<AVIOContext, int, ufile_close>
>;
>
{
// Close explicitly, not ignoring return values.
int close()
{
auto result = get_deleter() ( get() );
release();
return result;
}
};
template<typename T> using AVMallocHolder = std::unique_ptr<
T, AV_Deleter<void, void, av_free>
>;

View File

@ -288,7 +288,7 @@ OSType sf_header_mactype(int format)
ODLock libSndFileMutex;
void SFFileCloser::operator() (SNDFILE *sf) const
int SFFileCloser::operator() (SNDFILE *sf) const
{
auto err = SFCall<int>(sf_close, sf);
if (err) {
@ -299,4 +299,5 @@ void SFFileCloser::operator() (SNDFILE *sf) const
(_("Error (file may not have been written): %s"),
buffer));
}
return err;
}

View File

@ -127,7 +127,16 @@ inline R SFCall(F fun, Args&&... args)
}
//RAII for SNDFILE*
struct SFFileCloser { void operator () (SNDFILE*) const; };
using SFFile = std::unique_ptr<SNDFILE, SFFileCloser>;
struct SFFileCloser { int operator () (SNDFILE*) const; };
struct SFFile : public std::unique_ptr<SNDFILE, SFFileCloser>
{
// Close explicitly, not ignoring return values.
int close()
{
auto result = get_deleter() ( get() );
release();
return result;
}
};
#endif

View File

@ -52,11 +52,18 @@ bool FileIO::IsOpened()
return mOpen;
}
void FileIO::Close()
bool FileIO::Close()
{
mOutputStream.reset();
bool success = true;
if (mOutputStream) {
// mOutputStream->Sync() returns void! Rrr!
success = mOutputStream->GetFile()->Flush() &&
mOutputStream->Close();
mOutputStream.reset();
}
mInputStream.reset();
mOpen = false;
return success;
}
wxInputStream & FileIO::Read(void *buf, size_t size)

View File

@ -32,7 +32,7 @@ class FileIO
bool IsOpened();
void Close();
bool Close();
wxInputStream & Read(void *buffer, size_t size);
wxOutputStream & Write(const void *buffer, size_t size);
@ -41,7 +41,7 @@ class FileIO
wxString mName;
FileIOMode mMode;
std::unique_ptr<wxInputStream> mInputStream;
std::unique_ptr<wxOutputStream> mOutputStream;
std::unique_ptr<wxFFileOutputStream> mOutputStream;
bool mOpen;
};

View File

@ -945,6 +945,12 @@ ProgressResult ExportFFmpeg::Export(AudacityProject *project,
if ( !Finalize() ) // Finalize makes its own messages
return ProgressResult::Cancelled;
if ( mUfileCloser.close() != 0 ) {
// TODO: more precise message
AudacityMessageBox(_("Unable to export"));
return ProgressResult::Cancelled;
}
return updateResult;
}

View File

@ -335,10 +335,13 @@ ProgressResult ExportFLAC::Export(AudacityProject *project,
mMetadata.reset();
auto cleanup2 = finally( [&] {
if (!(updateResult == ProgressResult::Success ||
updateResult == ProgressResult::Stopped)) {
#ifndef LEGACY_FLAC
f.Detach(); // libflac closes the file
f.Detach(); // libflac closes the file
#endif
encoder.finish();
encoder.finish();
}
} );
const WaveTrackConstArray waveTracks =
@ -389,6 +392,20 @@ ProgressResult ExportFLAC::Export(AudacityProject *project,
}
}
if (updateResult == ProgressResult::Success ||
updateResult == ProgressResult::Stopped) {
#ifndef LEGACY_FLAC
f.Detach(); // libflac closes the file
#endif
if (!encoder.finish())
// Do not reassign updateResult, see cleanup2
return ProgressResult::Failed;
#ifdef LEGACY_FLAC
if (!f.Flush() || !f.Close())
return ProgressResult::Failed;
#endif
}
return updateResult;
}

View File

@ -330,7 +330,11 @@ ProgressResult ExportMP2::Export(AudacityProject *project,
return ProgressResult::Cancelled;
}
outFile.Close();
if ( !outFile.Close() ) {
// TODO: more precise message
AudacityMessageBox(_("Unable to export"));
return ProgressResult::Cancelled;
}
return updateResult;
}

View File

@ -854,7 +854,7 @@ public:
int FinishStream(unsigned char outbuffer[]);
void CancelEncoding();
void PutInfoTag(wxFFile & f, wxFileOffset off);
bool PutInfoTag(wxFFile & f, wxFileOffset off);
private:
@ -1393,17 +1393,21 @@ void MP3Exporter::CancelEncoding()
mEncoding = false;
}
void MP3Exporter::PutInfoTag(wxFFile & f, wxFileOffset off)
bool MP3Exporter::PutInfoTag(wxFFile & f, wxFileOffset off)
{
if (mGF) {
if (mInfoTagLen > 0) {
// FIXME: TRAP_ERR Seek and writ ein MP3 exporter could fail.
f.Seek(off, wxFromStart);
f.Write(mInfoTagBuf, mInfoTagLen);
if ( !f.Seek(off, wxFromStart))
return false;
if (mInfoTagLen > f.Write(mInfoTagBuf, mInfoTagLen))
return false;
}
#if defined(__WXMSW__)
else if (beWriteInfoTag) {
f.Flush();
if ( !f.Flush() )
return false;
// PRL: What is the correct error check on the return value?
beWriteInfoTag(mGF, OSOUTPUT(f.GetName()));
mGF = NULL;
}
@ -1413,7 +1417,10 @@ void MP3Exporter::PutInfoTag(wxFFile & f, wxFileOffset off)
}
}
f.SeekEnd();
if ( !f.SeekEnd() )
return false;
return true;
}
#if defined(__WXMSW__)
@ -1801,7 +1808,7 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
bool endOfFile;
id3len = AddTags(project, id3buffer, &endOfFile, metadata);
if (id3len && !endOfFile) {
if (id3len < outFile.Write(id3buffer.get(), id3len)) {
if (id3len > outFile.Write(id3buffer.get(), id3len)) {
// TODO: more precise message
AudacityMessageBox(_("Unable to export"));
return ProgressResult::Cancelled;
@ -1887,7 +1894,7 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
break;
}
if (bytes < outFile.Write(buffer.get(), bytes)) {
if (bytes > outFile.Write(buffer.get(), bytes)) {
// TODO: more precise message
AudacityMessageBox(_("Unable to export"));
updateResult = ProgressResult::Cancelled;
@ -1909,7 +1916,7 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
}
if (bytes > 0) {
if (bytes < outFile.Write(buffer.get(), bytes)) {
if (bytes > outFile.Write(buffer.get(), bytes)) {
// TODO: more precise message
AudacityMessageBox(_("Unable to export"));
return ProgressResult::Cancelled;
@ -1918,7 +1925,7 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
// Write ID3 tag if it was supposed to be at the end of the file
if (id3len > 0 && endOfFile) {
if (bytes < outFile.Write(id3buffer.get(), id3len)) {
if (bytes > outFile.Write(id3buffer.get(), id3len)) {
// TODO: more precise message
AudacityMessageBox(_("Unable to export"));
return ProgressResult::Cancelled;
@ -1931,9 +1938,9 @@ ProgressResult ExportMP3::Export(AudacityProject *project,
//
// Also, if beWriteInfoTag() is used, mGF will no longer be valid after
// this call, so do not use it.
exporter.PutInfoTag(outFile, pos);
if (!outFile.Close()) {
if (!exporter.PutInfoTag(outFile, pos) ||
!outFile.Flush() ||
!outFile.Close()) {
// TODO: more precise message
AudacityMessageBox(_("Unable to export"));
return ProgressResult::Cancelled;

View File

@ -359,7 +359,11 @@ ProgressResult ExportOGG::Export(AudacityProject *project,
}
}
outFile.Close();
if ( !outFile.Close() ) {
updateResult = ProgressResult::Cancelled;
// TODO: more precise message
AudacityMessageBox(_("Unable to export"));
}
return updateResult;
}

View File

@ -530,7 +530,7 @@ ProgressResult ExportPCM::Export(AudacityProject *project,
// Install the WAV metata in a "LIST" chunk at the end of the file
if (updateResult == ProgressResult::Success ||
updateResult == ProgressResult::Stopped)
updateResult == ProgressResult::Stopped) {
if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV ||
(sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAVEX) {
if (!AddStrings(project, sf.get(), metadata, sf_format)) {
@ -538,6 +538,12 @@ ProgressResult ExportPCM::Export(AudacityProject *project,
AudacityMessageBox(_("Unable to export"));
return ProgressResult::Cancelled;
}
}
if (0 != sf.close()) {
// TODO: more precise message
AudacityMessageBox(_("Unable to export"));
return ProgressResult::Cancelled;
}
}
}
@ -545,6 +551,7 @@ ProgressResult ExportPCM::Export(AudacityProject *project,
updateResult == ProgressResult::Stopped)
if (((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) ||
((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV))
// Note: file has closed, and gets reopened and closed again here:
if (!AddID3Chunk(fName, metadata, sf_format) ) {
// TODO: more precise message
AudacityMessageBox(_("Unable to export"));
@ -845,6 +852,9 @@ bool ExportPCM::AddID3Chunk(wxString fName, const Tags *tags, int sf_format)
if (4 != f.Write(&sz, 4))
return false;
if (!f.Flush())
return false;
if (!f.Close())
return false;
}