ide/settings: Improvements to safe config file handling
authorJerome St-Louis <jerome@ecere.com>
Mon, 1 Aug 2016 23:16:45 +0000 (19:16 -0400)
committerJerome St-Louis <jerome@ecere.com>
Mon, 1 Aug 2016 23:16:45 +0000 (19:16 -0400)
- ecere/sys: Added MoveFileEx() function to overwrite and sync

compiler/bootstrap/ecere/bootstrap/System.c
ecere/src/sys/System.c
ecere/src/sys/System.ec
ide/src/IDESettings.ec

index 06185be..815aad7 100644 (file)
@@ -111,7 +111,7 @@ FILE * eC_stdout(void);
 
 FILE * eC_stderr(void);
 
-unsigned int System_MoveFile(const char * source, const char * dest);
+unsigned int System_MoveFile(const char * source, const char * dest, unsigned int replaceAndFlush);
 
 unsigned int System_RenameFile(const char * oldName, const char * newName);
 
@@ -230,7 +230,12 @@ extern struct __ecereNameSpace__ecere__com__Property * __ecereProp___ecereNameSp
 
 unsigned int __ecereNameSpace__ecere__sys__MoveFile(const char * source, const char * dest)
 {
-return System_MoveFile(source, dest);
+return System_MoveFile(source, dest, 0);
+}
+
+unsigned int __ecereNameSpace__ecere__sys__MoveFileEx(const char * source, const char * dest, unsigned int options)
+{
+return System_MoveFile(source, dest, (unsigned int)options);
 }
 
 unsigned int __ecereNameSpace__ecere__sys__RenameFile(const char * oldName, const char * newName)
@@ -679,6 +684,8 @@ static struct __ecereNameSpace__ecere__com__Class * __ecereClass___ecereNameSpac
 
 static struct __ecereNameSpace__ecere__com__Class * __ecereClass___ecereNameSpace__ecere__sys__GuiErrorCode;
 
+static struct __ecereNameSpace__ecere__com__Class * __ecereClass___ecereNameSpace__ecere__sys__MoveFileOptions;
+
 static struct __ecereNameSpace__ecere__com__Class * __ecereClass___ecereNameSpace__ecere__sys__System;
 
 extern struct __ecereNameSpace__ecere__com__Class * __ecereClass___ecereNameSpace__ecere__com__Array_TPL_String_;
@@ -853,6 +860,12 @@ __ecereNameSpace__ecere__com__eEnum_AddFixedValue(class, "windowCreationFailed",
 __ecereNameSpace__ecere__com__eEnum_AddFixedValue(class, "graphicsLoadingFailed", 259);
 __ecereNameSpace__ecere__com__eEnum_AddFixedValue(class, "modeSwitchFailed", 260);
 __ecereNameSpace__ecere__com__eSystem_RegisterFunction("ecere::sys::MoveFile", "bool ecere::sys::MoveFile(const char * source, const char * dest)", __ecereNameSpace__ecere__sys__MoveFile, module, 1);
+class = __ecereNameSpace__ecere__com__eSystem_RegisterClass(2, "ecere::sys::MoveFileOptions", "uint", 0, 0, (void *)0, (void *)0, module, 1, 1);
+if(((struct __ecereNameSpace__ecere__com__Module *)(((char *)module + sizeof(struct __ecereNameSpace__ecere__com__Instance))))->application == ((struct __ecereNameSpace__ecere__com__Module *)(((char *)__thisModule + sizeof(struct __ecereNameSpace__ecere__com__Instance))))->application && class)
+__ecereClass___ecereNameSpace__ecere__sys__MoveFileOptions = class;
+__ecereNameSpace__ecere__com__eClass_AddBitMember(class, "overwrite", "bool", 1, 0, 1);
+__ecereNameSpace__ecere__com__eClass_AddBitMember(class, "sync", "bool", 1, 1, 1);
+__ecereNameSpace__ecere__com__eSystem_RegisterFunction("ecere::sys::MoveFileEx", "bool ecere::sys::MoveFileEx(const char * source, const char * dest, ecere::sys::MoveFileOptions options)", __ecereNameSpace__ecere__sys__MoveFileEx, module, 1);
 __ecereNameSpace__ecere__com__eSystem_RegisterFunction("ecere::sys::RenameFile", "bool ecere::sys::RenameFile(const char * oldName, const char * newName)", __ecereNameSpace__ecere__sys__RenameFile, module, 1);
 __ecereNameSpace__ecere__com__eSystem_RegisterFunction("ecere::sys::DeleteFile", "bool ecere::sys::DeleteFile(const char * fileName)", __ecereNameSpace__ecere__sys__DeleteFile, module, 1);
 __ecereNameSpace__ecere__com__eSystem_RegisterFunction("ecere::sys::MakeDir", "bool ecere::sys::MakeDir(const char * path)", __ecereNameSpace__ecere__sys__MakeDir, module, 1);
index 66cd65c..7fc4af8 100644 (file)
@@ -70,13 +70,14 @@ char * __ecereNameSpace__ecere__sys__SearchString(const char * buffer, int start
 
 FileAttribs FILE_FileExists(const char * fileName);
 
-bool System_MoveFile(const char * source, const char * dest)
+bool System_MoveFile(const char * source, const char * dest, uint replaceAndFlush)
 {
 #ifdef __WIN32__
    bool result;
    uint16 * _wsource = __ecereNameSpace__ecere__sys__UTF8toUTF16(source, null);
    uint16 * _wdest = __ecereNameSpace__ecere__sys__UTF8toUTF16(dest, null);
-   result = MoveFileEx(_wsource, _wdest, MOVEFILE_COPY_ALLOWED) != 0;
+                                                                  // TODO: Select options individually
+   result = MoveFileEx(_wsource, _wdest, MOVEFILE_COPY_ALLOWED | (replaceAndFlush ? MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH : 0)) != 0;
    __ecereNameSpace__ecere__com__eSystem_Delete(_wsource);
    __ecereNameSpace__ecere__com__eSystem_Delete(_wdest);
    return result;
index 6d27861..9dd5afa 100644 (file)
@@ -47,7 +47,7 @@ FILE *eC_stdout(void);
 FILE *eC_stderr(void);
 
 // IMPLEMENTED IN _System.c
-bool System_MoveFile(const char * source, const char * dest);
+bool System_MoveFile(const char * source, const char * dest, uint replaceAndFlush);
 bool System_RenameFile(const char * oldName, const char * newName);
 bool System_DeleteFile(const char * fileName);
 bool System_MakeDir(const char * path);
@@ -144,10 +144,18 @@ static Array<Array<const String>> errorMessages { [ sysErrorMessages, guiErrorMe
 
 // --- File, directory & environment manipulation ---
 #undef MoveFile
+#undef MoveFileEx
 
 public bool MoveFile(const char * source, const char * dest)
 {
-   return System_MoveFile(source, dest);
+   return System_MoveFile(source, dest, 0);
+}
+
+public class MoveFileOptions : uint32 { public: bool overwrite:1; bool sync:1; };
+
+public bool MoveFileEx(const char * source, const char * dest, MoveFileOptions options)
+{
+   return System_MoveFile(source, dest, options);
 }
 
 public bool RenameFile(const char * oldName, const char * newName)
index b83f2a5..d36253a 100644 (file)
@@ -629,11 +629,11 @@ static void getConfigFilePath(char * path, Class _class, char * dir, const char
 static SettingsIOResult writeConfigFile(const char * path, Class dataType, void * data)
 {
    SettingsIOResult result = error;
-   SafeFile sf;
-   sf = safeWriteFileOpen(path, write);
-   if(sf.file)
+   SafeFile sf = SafeFile::open(path, write);
+   if(sf)
    {
       WriteECONObject(sf.file, dataType, data, 0);
+      sf.sync();
       delete sf;
       result = success;
    }
@@ -648,7 +648,7 @@ static SettingsIOResult readConfigFile(const char * path, Class dataType, void *
    SafeFile sf;
    if(!FileExists(path))
       result = fileNotFound;
-   else if((sf = safeWriteFileOpen(path, read)))
+   else if((sf = SafeFile::open(path, read)))
    {
       JSONResult jsonResult;
       {
@@ -678,69 +678,90 @@ class SafeFile
    char path[MAX_LOCATION];
    char tmp[MAX_LOCATION];
 
-   ~SafeFile()
+   SafeFile ::open(const char * path, FileOpenMode mode)
    {
-      delete file;
-      // how to make this atomic ?
-      if(mode == write)
+      SafeFile result = null;
+      if(mode == write || mode == read)
       {
+         SafeFile sf { mode = mode };
          int c;
-         for(c = 0; c < 10; c++)
+         bool locked = false;
+         FileLock lockType = mode == write ? exclusive : shared;
+
+         strcpy(sf.path, path);
+         strcpy(sf.tmp, path);
+         strcat(sf.tmp, ".tmp");
+         if(mode == write && FileExists(sf.tmp).isFile)
+            DeleteFile(sf.tmp);
+
+         sf.file = FileOpen(mode == write ? sf.tmp : path, mode);
+         if(sf.file)
          {
-            if(FileExists(path).isFile)
-               DeleteFile(path);
+            for(c = 0; c < 10 && !(locked = sf.file.Lock(lockType, 0, 0, false)); c++) Sleep(0.01);
+            if(locked)
+               result = sf;
+            else if(mode == write)
+               PrintLn($"warning: SafeFile::open: unable to obtain exclusive lock on temporary file for writing: ", sf.tmp);
             else
-            {
-               MoveFile(tmp, path);
-               if(FileExists(path).isFile)
-                  break;
-            }
+               PrintLn($"warning: SafeFile::open: unable to obtain shared lock on file for reading: ", path);
          }
+         else if(mode == write)
+            PrintLn($"warning: SafeFile::open: unable to open temporary file for writing: ", sf.tmp);
+         else
+            PrintLn($"warning: SafeFile::open: unable to open file for reading: ", path);
+
+         if(!result)
+            delete sf;
       }
+      else
+         PrintLn($"warning: SafeFile::open: does not yet support FileOpenMode::", mode);
+      return result;
    }
-}
 
-SafeFile safeWriteFileOpen(const char * path, FileOpenMode mode)
-{
-   SafeFile sf { mode = mode };
-   int c;
-   bool locked = false;
-   strcpy(sf.path, path);
-   strcpy(sf.tmp, path);
-   strcat(sf.tmp, ".tmp");
-   if(mode == write)
-   {
-      if(FileExists(sf.tmp).isFile)
-         DeleteFile(sf.tmp);
-      sf.file = FileOpen(sf.tmp, write);
-      if(sf.file)
+   void sync()
+   {
+      if(file && mode == write)
       {
-         for(c = 0; c < 10 && !(locked = sf.file.Lock(exclusive, 0, 0, false)); c++);
-         if(!locked)
+         int c;
+         File f = FileOpen(path, FileExists(path) ? read : write);
+         if(f)
          {
-            delete sf.file;
-            PrintLn($"warning: safeWriteFileOpen: unable to obtain exclusive lock on temporary file for writing: ", sf.tmp);
+            bool locked = true;
+            for(c = 0; c < 10 && !(locked = f.Lock(exclusive, 0,0, false)); c++) Sleep(0.01);
+
+            if(locked)
+            {
+               f.Unlock(0,0, false);
+               delete f;
+               file.Unlock(0,0, false);
+               delete file;
+
+               for(c = 0; c < 10; c++)
+               {
+                  if(MoveFileEx(tmp, path, { true, true }))
+                     break;
+                  else
+                     Sleep(0.01);
+               }
+            }
+            else
+            {
+               delete f;
+               PrintLn($"warning: SafeFile::sync: failed to lock file", mode);
+            }
          }
       }
-      else
-         PrintLn($"warning: safeWriteFileOpen: unable to open temporary file for writing: ", sf.tmp);
    }
-   else if(mode == read)
+
+
+   ~SafeFile()
    {
-      sf.file = FileOpen(path, read);
-      if(sf.file)
+      if(file)
       {
-         for(c = 0; c < 10 && !(locked = sf.file.Lock(shared, 0, 0, false)); c++) Sleep(0.01);
-         if(!locked)
-         {
-            delete sf.file;
-            PrintLn($"warning: safeWriteFileOpen: unable to obtain shared lock on file for reading: ", path);
-         }
+         file.Unlock(0,0, false);
+         delete file;
       }
    }
-   else
-      PrintLn($"warning: safeWriteFileOpen: does not yet support FileOpenMode::", mode);
-   return sf;
 }
 
 class RecentFilesData