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() ) if( backupFileName.FileExists() )
wxRemoveFile( backupFileName.GetFullPath() ); wxRemoveFile( backupFileName.GetFullPath() );
if( !wxRenameFile( schematicFileName.GetFullPath(), backupFileName.GetFullPath() ) ) if( !wxCopyFile( schematicFileName.GetFullPath(), backupFileName.GetFullPath() ) )
{ {
msg.Printf( _( "Could not save backup of file \"%s\"" ), msg.Printf( _( "Could not save backup of file \"%s\"" ),
schematicFileName.GetFullPath() ); 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 // Save
wxLogTrace( traceAutoSave, wxLogTrace( traceAutoSave,
wxT( "Saving file <" ) + schematicFileName.GetFullPath() + wxT( ">" ) ); wxT( "Saving file <" ) + schematicFileName.GetFullPath() + wxT( ">" ) );
@ -128,7 +132,7 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SHEET* aSheet, bool aSaveUnderNewName,
try try
{ {
pi->Save( schematicFileName.GetFullPath(), aSheet, &Schematic() ); pi->Save( tempFile.GetFullPath(), aSheet, &Schematic() );
success = true; success = true;
} }
catch( const IO_ERROR& ioe ) catch( const IO_ERROR& ioe )
@ -137,12 +141,32 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SHEET* aSheet, bool aSaveUnderNewName,
schematicFileName.GetFullPath(), ioe.What() ); schematicFileName.GetFullPath(), ioe.What() );
DisplayError( this, msg ); 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 ); AppendMsgPanel( wxEmptyString, msg, CYAN );
// In case we started a file but didn't fully write it, clean up
wxRemoveFile( tempFile.GetFullPath() );
success = false; 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 ) if( success )
{ {
// Delete auto save file. // 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 ) 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(); GetScreen()->ClrModify();
return true; return true;
} }

View File

@ -685,8 +685,8 @@ wxString PCB_EDIT_FRAME::createBackupFile( const wxString& aFileName )
if( backupFileName.FileExists() ) if( backupFileName.FileExists() )
wxRemoveFile( backupFileName.GetFullPath() ); wxRemoveFile( backupFileName.GetFullPath() );
// Rename the current file from <xxx>.kicad_pcb to <xxx>.kicad_pcb-bak // Copy the current file from <xxx>.kicad_pcb to <xxx>.kicad_pcb-bak
if( !wxRenameFile( fn.GetFullPath(), backupFileName.GetFullPath() ) ) if( !wxCopyFile( fn.GetFullPath(), backupFileName.GetFullPath() ) )
{ {
wxString msg = wxString::Format( _( wxString msg = wxString::Format( _(
"Warning: unable to create backup file \"%s\"" ), "Warning: unable to create backup file \"%s\"" ),
@ -729,6 +729,10 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupF
backupFileName = createBackupFile( aFileName ); backupFileName = createBackupFile( aFileName );
} }
wxFileName tempFile( aFileName );
tempFile.SetName( wxT( "." ) + tempFile.GetName() );
tempFile.SetExt( tempFile.GetExt() + wxT( "$" ) );
GetBoard()->SynchronizeNetsAndNetClasses(); GetBoard()->SynchronizeNetsAndNetClasses();
// Select default Netclass before writing file. Useful to save default values in headers. // 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 ) ); 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 ) catch( const IO_ERROR& ioe )
{ {
@ -759,7 +763,26 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupF
); );
DisplayError( this, msg ); 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 ); AppendMsgPanel( upperTxt, lowerTxt, CYAN );