Use a temporary file when saving boards/schematics to make the operation more atomic

Fixes https://gitlab.com/kicad/code/kicad/-/issues/4517
This commit is contained in:
Jon Evans 2020-06-19 23:31:26 +00:00 committed by Ian McInerney
parent dd42a19319
commit 09cb75b8a1
3 changed files with 76 additions and 11 deletions

View File

@ -110,7 +110,7 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SHEET* aSheet, bool aSaveUnderNewName,
if( backupFileName.FileExists() )
wxRemoveFile( backupFileName.GetFullPath() );
if( !wxRenameFile( schematicFileName.GetFullPath(), backupFileName.GetFullPath() ) )
if( !wxCopyFile( schematicFileName.GetFullPath(), backupFileName.GetFullPath() ) )
{
msg.Printf( _( "Could not save backup of file \"%s\"" ),
schematicFileName.GetFullPath() );
@ -118,6 +118,10 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SHEET* aSheet, bool aSaveUnderNewName,
}
}
wxFileName tempFile( schematicFileName );
tempFile.SetName( wxT( "." ) + tempFile.GetName() );
tempFile.SetExt( tempFile.GetExt() + wxT( "$" ) );
// Save
wxLogTrace( traceAutoSave,
wxT( "Saving file <" ) + schematicFileName.GetFullPath() + wxT( ">" ) );
@ -128,7 +132,7 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SHEET* aSheet, bool aSaveUnderNewName,
try
{
pi->Save( schematicFileName.GetFullPath(), aSheet, &Schematic() );
pi->Save( tempFile.GetFullPath(), aSheet, &Schematic() );
success = true;
}
catch( const IO_ERROR& ioe )
@ -137,12 +141,32 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SHEET* aSheet, bool aSaveUnderNewName,
schematicFileName.GetFullPath(), ioe.What() );
DisplayError( this, msg );
msg.Printf( _( "Failed to save \"%s\"" ), schematicFileName.GetFullPath() );
msg.Printf( _( "Failed to create temporary file \"%s\"" ), tempFile.GetFullPath() );
AppendMsgPanel( wxEmptyString, msg, CYAN );
// In case we started a file but didn't fully write it, clean up
wxRemoveFile( tempFile.GetFullPath() );
success = false;
}
if( success )
{
// Replace the original with the temporary file we just wrote
success = wxRenameFile( tempFile.GetFullPath(), schematicFileName.GetFullPath() );
if( !success )
{
msg.Printf( _( "Error saving schematic file \"%s\".\n"
"Failed to rename temporary file %s" ),
schematicFileName.GetFullPath(), tempFile.GetFullPath() );
DisplayError( this, msg );
msg.Printf( _( "Failed to rename temporary file \"%s\"" ), tempFile.GetFullPath() );
AppendMsgPanel( wxEmptyString, msg, CYAN );
}
}
if( success )
{
// Delete auto save file.

View File

@ -263,9 +263,27 @@ bool PL_EDITOR_FRAME::InsertPageLayoutDescrFile( const wxString& aFullFileName )
bool PL_EDITOR_FRAME::SavePageLayoutDescrFile( const wxString& aFullFileName )
{
if( ! aFullFileName.IsEmpty() )
if( !aFullFileName.IsEmpty() )
{
WS_DATA_MODEL::GetTheInstance().Save( aFullFileName );
wxFileName tempFile( aFullFileName );
tempFile.SetName( wxT( "." ) + tempFile.GetName() );
tempFile.SetExt( tempFile.GetExt() + wxT( "$" ) );
try
{
WS_DATA_MODEL::GetTheInstance().Save( tempFile.GetFullPath() );
}
catch( const IO_ERROR& ioe )
{
// In case we started a file but didn't fully write it, clean up
wxRemoveFile( tempFile.GetFullPath() );
return false;
}
if( !wxRenameFile( tempFile.GetFullPath(), aFullFileName ) )
return false;
GetScreen()->ClrModify();
return true;
}

View File

@ -685,8 +685,8 @@ wxString PCB_EDIT_FRAME::createBackupFile( const wxString& aFileName )
if( backupFileName.FileExists() )
wxRemoveFile( backupFileName.GetFullPath() );
// Rename the current file from <xxx>.kicad_pcb to <xxx>.kicad_pcb-bak
if( !wxRenameFile( fn.GetFullPath(), backupFileName.GetFullPath() ) )
// Copy the current file from <xxx>.kicad_pcb to <xxx>.kicad_pcb-bak
if( !wxCopyFile( fn.GetFullPath(), backupFileName.GetFullPath() ) )
{
wxString msg = wxString::Format( _(
"Warning: unable to create backup file \"%s\"" ),
@ -707,7 +707,7 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupF
{
// please, keep it simple. prompting goes elsewhere.
wxFileName pcbFileName = aFileName;
wxFileName pcbFileName = aFileName;
if( pcbFileName.GetExt() == LegacyPcbFileExtension )
pcbFileName.SetExt( KiCadPcbFileExtension );
@ -729,6 +729,10 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupF
backupFileName = createBackupFile( aFileName );
}
wxFileName tempFile( aFileName );
tempFile.SetName( wxT( "." ) + tempFile.GetName() );
tempFile.SetExt( tempFile.GetExt() + wxT( "$" ) );
GetBoard()->SynchronizeNetsAndNetClasses();
// Select default Netclass before writing file. Useful to save default values in headers.
@ -747,9 +751,9 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupF
{
PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::KICAD_SEXP ) );
wxASSERT( pcbFileName.IsAbsolute() );
wxASSERT( tempFile.IsAbsolute() );
pi->Save( pcbFileName.GetFullPath(), GetBoard(), NULL );
pi->Save( tempFile.GetFullPath(), GetBoard(), NULL );
}
catch( const IO_ERROR& ioe )
{
@ -759,7 +763,26 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupF
);
DisplayError( this, msg );
lowerTxt.Printf( _( "Failed to create \"%s\"" ), pcbFileName.GetFullPath() );
lowerTxt.Printf( _( "Failed to create temporary file \"%s\"" ), tempFile.GetFullPath() );
AppendMsgPanel( upperTxt, lowerTxt, CYAN );
// In case we started a file but didn't fully write it, clean up
wxRemoveFile( tempFile.GetFullPath() );
return false;
}
// If save succeeded, replace the original with what we just wrote
if( !wxRenameFile( tempFile.GetFullPath(), pcbFileName.GetFullPath() ) )
{
wxString msg = wxString::Format( _(
"Error saving board file \"%s\".\nFailed to rename temporary file \"%s\"" ),
pcbFileName.GetFullPath(), tempFile.GetFullPath()
);
DisplayError( this, msg );
lowerTxt.Printf( _( "Failed to rename temporary file \"%s\"" ), tempFile.GetFullPath() );
AppendMsgPanel( upperTxt, lowerTxt, CYAN );