23#include <wx/filename.h>
50 static std::atomic<unsigned> s_counter{ 0 };
52 unsigned counter = s_counter.fetch_add( 1, std::memory_order_relaxed );
55 unsigned pid =
static_cast<unsigned>( _getpid() );
57 unsigned pid =
static_cast<unsigned>( getpid() );
60 return aTargetPath + wxString::Format( wxT(
".kicad-save-%u-%u" ), pid, counter );
67 wxString* aTempPathOut, wxString* aError )
71 for(
unsigned attempt = 0; attempt < 32; ++attempt )
74 int fd = open( candidate.fn_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0600 );
78 FILE* fp = fdopen( fd, aMode.mb_str() );
84 unlink( candidate.fn_str() );
88 *aError = wxString::Format( wxT(
"fdopen failed for temp file '%s': %s" ),
89 candidate, wxString::FromUTF8( strerror( err ) ) );
96 *aTempPathOut = candidate;
101 if( errno != EEXIST )
105 *aError = wxString::Format( wxT(
"Cannot create temp file '%s': %s" ), candidate,
106 wxString::FromUTF8( strerror( errno ) ) );
114 *aError = wxT(
"Exhausted temp-file retry budget" );
127 if( lstat( aPath.fn_str(), &st ) != 0 || !S_ISLNK( st.st_mode ) )
130 char resolved[PATH_MAX];
132 if( realpath( aPath.fn_str(), resolved ) )
133 return wxString::FromUTF8( resolved );
141 int fd = open( aDirPath.fn_str(), O_RDONLY
142#
if defined( O_DIRECTORY )
151 return errno == EINVAL || errno == ENOTSUP;
154 int rc = fsync( fd );
158 return rc == 0 || err == EINVAL;
164 if( rename( aSrc.fn_str(), aDst.fn_str() ) == 0 )
168 *aError = wxString::FromUTF8( strerror( errno ) );
180 const bool targetExists = wxFileName::FileExists( aTargetPath );
196 *aError = wxString::Format( wxT(
"Cannot copy permissions from '%s' to '%s'" ),
197 aTargetPath, aTempPath );
212 wxString renameError;
213 const bool renamed =
AtomicRename( aTempPath, aTargetPath, &renameError );
226 wxLogWarning( wxT(
"Could not restore file attributes on '%s' after save" ),
235 *aError = wxString::Format( wxT(
"Cannot rename temp file over '%s': %s" ),
236 aTargetPath, renameError );
242 wxFileName dst( aTargetPath );
243 wxString dirPath = dst.GetPath();
247 if( dirPath.IsEmpty() )
248 dirPath = wxT(
"." );
255 *aError = wxString::Format( wxT(
"Cannot flush directory '%s' to disk" ), dirPath );
274 if( aSize > 0 && std::fwrite( aData, 1, aSize, fp ) != aSize )
277 *aError = wxString::Format( wxT(
"Write failed to '%s'" ), tempPath );
280 wxRemoveFile( tempPath );
287 *aError = wxString::Format( wxT(
"fsync failed on '%s'" ), tempPath );
290 wxRemoveFile( tempPath );
301 wxRemoveFile( tempPath );
314 FILE* fp = wxFopen( aFileName, wxS(
"rb" ) );
317 throw std::runtime_error( std::string(
"Cannot open file: " ) + aFileName.ToStdString() );
319 fseek( fp, 0, SEEK_END );
320 long len = ftell( fp );
325 throw std::runtime_error( std::string(
"Cannot determine file size: " )
326 + aFileName.ToStdString() );
330 fseek( fp, 0, SEEK_SET );
332 size_t bytesRead = fread(
m_fallbackBuffer.data(), 1,
static_cast<size_t>( len ), fp );
335 if( bytesRead !=
static_cast<size_t>( len ) )
337 throw std::runtime_error( std::string(
"Failed to read file: " )
338 + aFileName.ToStdString() );