Bug2550 residual: also move -shm file if present...
... Also much other extra care in the handling of -shm and -wal files
This commit is contained in:
parent
b32ce8ab21
commit
3e0120be05
|
@ -31,10 +31,17 @@ Paul Licameli split from AudacityProject.cpp
|
||||||
#include "ViewInfo.h"
|
#include "ViewInfo.h"
|
||||||
#include "WaveTrack.h"
|
#include "WaveTrack.h"
|
||||||
#include "widgets/AudacityMessageBox.h"
|
#include "widgets/AudacityMessageBox.h"
|
||||||
|
#include "widgets/ErrorDialog.h"
|
||||||
#include "widgets/NumericTextCtrl.h"
|
#include "widgets/NumericTextCtrl.h"
|
||||||
#include "widgets/ProgressDialog.h"
|
#include "widgets/ProgressDialog.h"
|
||||||
|
#include "wxFileNameWrapper.h"
|
||||||
#include "xml/XMLFileReader.h"
|
#include "xml/XMLFileReader.h"
|
||||||
|
|
||||||
|
#undef NO_SHM
|
||||||
|
#if !defined(__WXMSW__)
|
||||||
|
#define NO_SHM
|
||||||
|
#endif
|
||||||
|
|
||||||
wxDEFINE_EVENT(EVT_PROJECT_TITLE_CHANGE, wxCommandEvent);
|
wxDEFINE_EVENT(EVT_PROJECT_TITLE_CHANGE, wxCommandEvent);
|
||||||
|
|
||||||
static const int ProjectFileID = ('A' << 24 | 'U' << 16 | 'D' << 8 | 'Y');
|
static const int ProjectFileID = ('A' << 24 | 'U' << 16 | 'D' << 8 | 'Y');
|
||||||
|
@ -148,7 +155,7 @@ public:
|
||||||
mRc = sqlite3_initialize();
|
mRc = sqlite3_initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(__WXMSW__)
|
#ifdef NO_SHM
|
||||||
if (mRc == SQLITE_OK)
|
if (mRc == SQLITE_OK)
|
||||||
{
|
{
|
||||||
// Use the "unix-excl" VFS to make access to the DB exclusive. This gets
|
// Use the "unix-excl" VFS to make access to the DB exclusive. This gets
|
||||||
|
@ -941,29 +948,148 @@ Connection &ProjectFileIO::CurrConn()
|
||||||
return connectionPtr.mpConnection;
|
return connectionPtr.mpConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
const std::vector<wxString> &ProjectFileIO::AuxiliaryFileSuffixes()
|
||||||
bool MoveProject(const FilePath &src, const FilePath &dst)
|
|
||||||
{
|
{
|
||||||
// Assume the src database file is not busy.
|
static const std::vector<wxString> strings {
|
||||||
if (!wxRenameFile(src, dst))
|
"-wal",
|
||||||
|
#ifndef NO_SHM
|
||||||
|
"-shm",
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
return strings;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePath ProjectFileIO::SafetyFileName(const FilePath &src)
|
||||||
|
{
|
||||||
|
wxFileNameWrapper fn{ src };
|
||||||
|
|
||||||
|
// Extra characters inserted into filename before extension
|
||||||
|
wxString extra =
|
||||||
|
#ifdef __WXGTK__
|
||||||
|
wxT("~")
|
||||||
|
#else
|
||||||
|
wxT(".bak")
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
|
int nn = 1;
|
||||||
|
auto numberString = [](int num) -> wxString {
|
||||||
|
return num == 1 ? "" : wxString::Format(".%d", num);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto suffixes = AuxiliaryFileSuffixes();
|
||||||
|
suffixes.push_back({});
|
||||||
|
|
||||||
|
// Find backup paths not already occupied; check all auxiliary suffixes
|
||||||
|
const auto name = fn.GetName();
|
||||||
|
FilePath result;
|
||||||
|
do {
|
||||||
|
fn.SetName( name + numberString(nn++) + extra );
|
||||||
|
result = fn.GetFullPath();
|
||||||
|
}
|
||||||
|
while( std::any_of(suffixes.begin(), suffixes.end(), [&](auto &suffix){
|
||||||
|
return wxFileExists(result + suffix);
|
||||||
|
}) );
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProjectFileIO::RenameOrWarn(const FilePath &src, const FilePath &dst)
|
||||||
|
{
|
||||||
|
if ( !wxRenameFile(src, dst) ) {
|
||||||
|
auto &window = GetProjectFrame( mProject );
|
||||||
|
ShowErrorDialog(
|
||||||
|
&window,
|
||||||
|
XO("Error Writing to File"),
|
||||||
|
XO("Audacity failed to write file %s.\n"
|
||||||
|
"Perhaps disk is full or not writable.\n"
|
||||||
|
"For tips on freeing up space, click the help button.")
|
||||||
|
.Format(dst),
|
||||||
|
"Error:_Disk_full_or_not_writable"
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
// So far so good, but the separate -wal file might yet exist, as when
|
|
||||||
// checkpointing failed for limited space on the drive. If so move it too
|
|
||||||
// or else lose data.
|
|
||||||
auto srcWalFilename = src + wxT("-wal");
|
|
||||||
if (wxFileExists(srcWalFilename)) {
|
|
||||||
auto dstWalFilename = dst + wxT("-wal");
|
|
||||||
if (!wxRenameFile(srcWalFilename, dstWalFilename)) {
|
|
||||||
// undo the first move
|
|
||||||
if (!wxRenameFile(dst, src)) {
|
|
||||||
// ... ???
|
|
||||||
wxASSERT(false);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ProjectFileIO::MoveProject(const FilePath &src, const FilePath &dst)
|
||||||
|
{
|
||||||
|
// Assume the src database file is not busy.
|
||||||
|
if (!RenameOrWarn(src, dst))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// So far so good, but the separate -wal and -shm files might yet exist,
|
||||||
|
// as when checkpointing failed for limited space on the drive.
|
||||||
|
// If so move them too or else lose data.
|
||||||
|
|
||||||
|
std::vector< std::pair<FilePath, FilePath> > pairs{ { src, dst } };
|
||||||
|
bool success = false;
|
||||||
|
auto cleanup = finally([&]{
|
||||||
|
if (!success) {
|
||||||
|
// If any one of the renames failed, back out the previous ones.
|
||||||
|
// This should be a no-fail recovery! Not clear what to do if any
|
||||||
|
// of these renames fails.
|
||||||
|
for (auto &pair : pairs) {
|
||||||
|
if (!(pair.first.empty() && pair.second.empty()))
|
||||||
|
wxRenameFile(pair.second, pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto &suffix : AuxiliaryFileSuffixes()) {
|
||||||
|
auto srcName = src + suffix;
|
||||||
|
if (wxFileExists(srcName)) {
|
||||||
|
auto dstName = dst + suffix;
|
||||||
|
if (!RenameOrWarn(srcName, dstName))
|
||||||
|
return false;
|
||||||
|
pairs.push_back({ srcName, dstName });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (success = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectFileIO::BackupProject::BackupProject(
|
||||||
|
ProjectFileIO &projectFileIO, const FilePath &path )
|
||||||
|
{
|
||||||
|
auto safety = SafetyFileName(path);
|
||||||
|
if (!projectFileIO.MoveProject(path, safety))
|
||||||
|
return;
|
||||||
|
|
||||||
|
mPath = path;
|
||||||
|
mSafety = safety;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectFileIO::BackupProject::Discard()
|
||||||
|
{
|
||||||
|
if (!mPath.empty()) {
|
||||||
|
// Succeeded; don't need the safety files
|
||||||
|
auto suffixes = AuxiliaryFileSuffixes();
|
||||||
|
suffixes.push_back({});
|
||||||
|
for (const auto &suffix : suffixes) {
|
||||||
|
auto path = mSafety + suffix;
|
||||||
|
if (wxFileExists(path))
|
||||||
|
wxRemoveFile(path);
|
||||||
|
}
|
||||||
|
mSafety.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectFileIO::BackupProject::~BackupProject()
|
||||||
|
{
|
||||||
|
if (!mPath.empty()) {
|
||||||
|
if (!mSafety.empty()) {
|
||||||
|
// Failed; restore from safety files
|
||||||
|
auto suffixes = AuxiliaryFileSuffixes();
|
||||||
|
suffixes.push_back({});
|
||||||
|
for (const auto &suffix : suffixes) {
|
||||||
|
auto path = mPath + suffix;
|
||||||
|
if (wxFileExists(path))
|
||||||
|
wxRemoveFile(path);
|
||||||
|
wxRenameFile(mSafety + suffix, mPath + suffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectFileIO::Compact(
|
void ProjectFileIO::Compact(
|
||||||
|
|
|
@ -121,6 +121,37 @@ public:
|
||||||
// ProjectManager::OnCloseWindow()
|
// ProjectManager::OnCloseWindow()
|
||||||
void SetBypass();
|
void SetBypass();
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! Strings like -wal that may be appended to main project name to get other files created by
|
||||||
|
//! the database system
|
||||||
|
static const std::vector<wxString> &AuxiliaryFileSuffixes();
|
||||||
|
|
||||||
|
//! Generate a name for short-lived backup project files from an existing project
|
||||||
|
static FilePath SafetyFileName(const FilePath &src);
|
||||||
|
|
||||||
|
//! Rename a file or put up appropriate warning message.
|
||||||
|
/*! Failure might happen when renaming onto another device, doing copy of contents */
|
||||||
|
bool RenameOrWarn(const FilePath &src, const FilePath &dst);
|
||||||
|
|
||||||
|
bool MoveProject(const FilePath &src, const FilePath &dst);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Object manages the temporary backing-up of project paths while
|
||||||
|
// trying to overwrite with new contents, and restoration in case of failure
|
||||||
|
class BackupProject {
|
||||||
|
public:
|
||||||
|
//! Rename project file at path, and any auxiliary files, to backup path names
|
||||||
|
BackupProject( ProjectFileIO &projectFileIO, const FilePath &path );
|
||||||
|
//! Returns false if the renaming in the constructor failed
|
||||||
|
bool IsOk() { return !mPath.empty(); }
|
||||||
|
//! if `!IsOk()` do nothing; else remove backup files
|
||||||
|
void Discard();
|
||||||
|
//! if `!IsOk()` do nothing; else if `Discard()` was not called, undo the renaming
|
||||||
|
~BackupProject();
|
||||||
|
private:
|
||||||
|
FilePath mPath, mSafety;
|
||||||
|
};
|
||||||
|
|
||||||
// Remove all unused space within a project file
|
// Remove all unused space within a project file
|
||||||
void Compact(
|
void Compact(
|
||||||
const std::vector<const TrackList *> &tracks, bool force = false);
|
const std::vector<const TrackList *> &tracks, bool force = false);
|
||||||
|
|
|
@ -288,33 +288,12 @@ bool ProjectFileManager::DoSave(const FilePath & fileName, const bool fromSaveAs
|
||||||
// End of confirmations
|
// End of confirmations
|
||||||
|
|
||||||
// Always save a backup of the original project file
|
// Always save a backup of the original project file
|
||||||
wxString safetyFileName;
|
Optional<ProjectFileIO::BackupProject> pBackupProject;
|
||||||
if (fromSaveAs && wxFileExists(fileName))
|
if (fromSaveAs && wxFileExists(fileName))
|
||||||
{
|
{
|
||||||
#ifdef __WXGTK__
|
pBackupProject.emplace(projectFileIO, fileName);
|
||||||
safetyFileName = fileName + wxT("~");
|
if (!pBackupProject->IsOk())
|
||||||
#else
|
|
||||||
safetyFileName = fileName + wxT(".bak");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (wxFileExists(safetyFileName))
|
|
||||||
{
|
|
||||||
wxRemoveFile(safetyFileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !wxRenameFile(fileName, safetyFileName) )
|
|
||||||
{
|
|
||||||
ShowErrorDialog(
|
|
||||||
&window,
|
|
||||||
XO("Error Writing to File"),
|
|
||||||
XO("Audacity failed to write file %s.\n"
|
|
||||||
"Perhaps disk is full or not writable.\n"
|
|
||||||
"For tips on freeing up space, click the help button.")
|
|
||||||
.Format(safetyFileName),
|
|
||||||
"Error:_Disk_full_or_not_writable"
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = projectFileIO.SaveProject(fileName, mLastSavedTracks.get());
|
bool success = projectFileIO.SaveProject(fileName, mLastSavedTracks.get());
|
||||||
|
@ -329,16 +308,6 @@ bool ProjectFileManager::DoSave(const FilePath & fileName, const bool fromSaveAs
|
||||||
.Format(fileName),
|
.Format(fileName),
|
||||||
"Error:_Disk_full_or_not_writable"
|
"Error:_Disk_full_or_not_writable"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (fromSaveAs)
|
|
||||||
{
|
|
||||||
if (wxFileExists(fileName))
|
|
||||||
{
|
|
||||||
wxRemoveFile(fileName);
|
|
||||||
}
|
|
||||||
wxRename(safetyFileName, fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,12 +330,9 @@ bool ProjectFileManager::DoSave(const FilePath & fileName, const bool fromSaveAs
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, saving the project was successful, so we can DELETE
|
// If we get here, saving the project was successful, so we can DELETE
|
||||||
// the .bak file (because it now does not fit our block files anymore
|
// any backup project.
|
||||||
// anyway).
|
if (pBackupProject)
|
||||||
if (!safetyFileName.empty())
|
pBackupProject->Discard();
|
||||||
{
|
|
||||||
wxRemoveFile(safetyFileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue