summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-12-14 17:13:05 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-12-14 17:13:05 +0200
commitbb561bcec3d386801c6b05e5565d76acc7786f3d (patch)
tree225b8589ed7cb8e1f3908942c54091888524ec02
parent5a6f7804d9a486ee0dd1e04df18cd40c4639c1ce (diff)
App: Prevent state corruption due to interrupted file write
-rw-r--r--src/app.c13
1 files changed, 11 insertions, 2 deletions
diff --git a/src/app.c b/src/app.c
index 28e320fe..828f5097 100644
--- a/src/app.c
+++ b/src/app.c
@@ -105,6 +105,7 @@ static const char *defaultDataDir_App_ = "~/config/settings/lagrange";
105static const char *prefsFileName_App_ = "prefs.cfg"; 105static const char *prefsFileName_App_ = "prefs.cfg";
106static const char *oldStateFileName_App_ = "state.binary"; 106static const char *oldStateFileName_App_ = "state.binary";
107static const char *stateFileName_App_ = "state.lgr"; 107static const char *stateFileName_App_ = "state.lgr";
108static const char *tempStateFileName_App_ = "state.lgr.tmp";
108static const char *defaultDownloadDir_App_ = "~/Downloads"; 109static const char *defaultDownloadDir_App_ = "~/Downloads";
109 110
110static const int idleThreshold_App_ = 1000; /* ms */ 111static const int idleThreshold_App_ = 1000; /* ms */
@@ -554,7 +555,7 @@ static void saveState_App_(const iApp *d) {
554 navigation history, cached content) and depends closely on the widget 555 navigation history, cached content) and depends closely on the widget
555 tree. The data is largely not reorderable and should not be modified 556 tree. The data is largely not reorderable and should not be modified
556 by the user manually. */ 557 by the user manually. */
557 iFile *f = newCStr_File(concatPath_CStr(dataDir_App_(), stateFileName_App_)); 558 iFile *f = newCStr_File(concatPath_CStr(dataDir_App_(), tempStateFileName_App_));
558 if (open_File(f, writeOnly_FileMode)) { 559 if (open_File(f, writeOnly_FileMode)) {
559 writeData_File(f, magicState_App_, 4); 560 writeData_File(f, magicState_App_, 4);
560 writeU32_File(f, latest_FileVersion); /* version */ 561 writeU32_File(f, latest_FileVersion); /* version */
@@ -596,11 +597,19 @@ static void saveState_App_(const iApp *d) {
596 write8_File(f, flags); 597 write8_File(f, flags);
597 serializeState_DocumentWidget(i.object, stream_File(f)); 598 serializeState_DocumentWidget(i.object, stream_File(f));
598 } 599 }
600 iRelease(f);
599 } 601 }
600 else { 602 else {
603 iRelease(f);
601 fprintf(stderr, "[App] failed to save state: %s\n", strerror(errno)); 604 fprintf(stderr, "[App] failed to save state: %s\n", strerror(errno));
605 return;
602 } 606 }
603 iRelease(f); 607 /* Copy it over to the real file. This avoids truncation if the app for any reason crashes
608 before the state file is fully written. */
609 const char *tempName = concatPath_CStr(dataDir_App_(), tempStateFileName_App_);
610 const char *finalName = concatPath_CStr(dataDir_App_(), stateFileName_App_);
611 remove(finalName);
612 rename(tempName, finalName);
604} 613}
605 614
606#if defined (LAGRANGE_ENABLE_IDLE_SLEEP) 615#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)