23#include <wx/filename.h>
51 static std::atomic<unsigned> s_counter{ 0 };
53 unsigned counter = s_counter.fetch_add( 1, std::memory_order_relaxed );
56 unsigned pid =
static_cast<unsigned>( _getpid() );
58 unsigned pid =
static_cast<unsigned>( getpid() );
61 return aTargetPath + wxString::Format( wxT(
".kicad-save-%u-%u" ), pid, counter );
68 wxString* aTempPathOut, wxString* aError )
72 for(
unsigned attempt = 0; attempt < 32; ++attempt )
75 int fd = open( candidate.fn_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0600 );
79 FILE* fp = fdopen( fd, aMode.mb_str() );
85 unlink( candidate.fn_str() );
89 *aError = wxString::Format( wxT(
"fdopen failed for temp file '%s': %s" ),
90 candidate, wxString::FromUTF8( strerror( err ) ) );
97 *aTempPathOut = candidate;
102 if( errno != EEXIST )
106 *aError = wxString::Format( wxT(
"Cannot create temp file '%s': %s" ), candidate,
107 wxString::FromUTF8( strerror( errno ) ) );
115 *aError = wxT(
"Exhausted temp-file retry budget" );
128 if( lstat( aPath.fn_str(), &st ) != 0 || !S_ISLNK( st.st_mode ) )
131 char resolved[PATH_MAX];
133 if( realpath( aPath.fn_str(), resolved ) )
134 return wxString::FromUTF8( resolved );
142 int fd = open( aDirPath.fn_str(), O_RDONLY
143#
if defined( O_DIRECTORY )
152 return errno == EINVAL || errno == ENOTSUP;
155 int rc = fsync( fd );
159 return rc == 0 || err == EINVAL;
165 if( rename( aSrc.fn_str(), aDst.fn_str() ) == 0 )
169 *aError = wxString::FromUTF8( strerror( errno ) );
181 const bool targetExists = wxFileName::FileExists( aTargetPath );
197 *aError = wxString::Format( wxT(
"Cannot copy permissions from '%s' to '%s'" ),
198 aTargetPath, aTempPath );
213 wxString renameError;
214 const bool renamed =
AtomicRename( aTempPath, aTargetPath, &renameError );
227 wxLogWarning( wxT(
"Could not restore file attributes on '%s' after save" ),
236 *aError = wxString::Format( wxT(
"Cannot rename temp file over '%s': %s" ),
237 aTargetPath, renameError );
243 wxFileName dst( aTargetPath );
244 wxString dirPath = dst.GetPath();
248 if( dirPath.IsEmpty() )
249 dirPath = wxT(
"." );
256 *aError = wxString::Format( wxT(
"Cannot flush directory '%s' to disk" ), dirPath );
275 if( aSize > 0 && std::fwrite( aData, 1, aSize, fp ) != aSize )
278 *aError = wxString::Format( wxT(
"Write failed to '%s'" ), tempPath );
281 wxRemoveFile( tempPath );
288 *aError = wxString::Format( wxT(
"fsync failed on '%s'" ), tempPath );
291 wxRemoveFile( tempPath );
297 if( std::fclose( fp ) != 0 )
303 *aError = wxString::Format( wxT(
"Cannot close temp file '%s': %s" ), tempPath,
304 wxString::FromUTF8( strerror( err ) ) );
307 wxRemoveFile( tempPath );
316 wxRemoveFile( tempPath );
329 FILE* fp = wxFopen( aFileName, wxS(
"rb" ) );
332 throw std::runtime_error( std::string(
"Cannot open file: " ) + aFileName.ToStdString() );
334 fseek( fp, 0, SEEK_END );
335 long len = ftell( fp );
340 throw std::runtime_error( std::string(
"Cannot determine file size: " )
341 + aFileName.ToStdString() );
345 fseek( fp, 0, SEEK_SET );
347 size_t bytesRead = fread(
m_fallbackBuffer.data(), 1,
static_cast<size_t>( len ), fp );
350 if( bytesRead !=
static_cast<size_t>( len ) )
352 throw std::runtime_error( std::string(
"Failed to read file: " )
353 + aFileName.ToStdString() );
361#if !defined( _WIN32 )
369 int fd = open( aPath.fn_str(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0666 );
374 fd = open( aPath.fn_str(), O_RDWR | O_CLOEXEC );
379 m_fd = open( aPath.fn_str(), O_RDONLY | O_CLOEXEC );
389 if( flock( fd, LOCK_EX | LOCK_NB ) == 0 )
391 else if( errno == EWOULDBLOCK )
404 aHeldByAnother =
false;
406 m_fd = open( aPath.fn_str(), O_RDONLY | O_CLOEXEC );
412 if( flock(
m_fd, LOCK_EX | LOCK_NB ) == 0 )
413 flock(
m_fd, LOCK_UN );
414 else if( errno == EWOULDBLOCK )
415 aHeldByAnother =
true;
429 if( !
IsOpen() || lseek(
m_fd, 0, SEEK_SET ) < 0 )
437 while( ( bytes = read(
m_fd, buffer,
sizeof( buffer ) ) ) > 0 )
438 aContents.append( buffer,
static_cast<size_t>( bytes ) );
446 if( !
IsOpen() || ftruncate(
m_fd, 0 ) < 0 || lseek(
m_fd, 0, SEEK_SET ) < 0 )
451 while( written < aContents.size() )
453 ssize_t bytes = write(
m_fd, aContents.data() + written, aContents.size() - written );
458 written +=
static_cast<size_t>( bytes );
489 *
this = std::move( aOther );
495 if(
this == &aOther )
501 m_handle = aOther.m_handle;
502 aOther.m_handle =
nullptr;