diff --git a/HISTORY b/HISTORY index a85bc43a..50550cc4 100644 --- a/HISTORY +++ b/HISTORY @@ -340,3 +340,5 @@ Video Disk Recorder Revision History - Changed the value for Diseqc to '0' in the default 'channels.conf'. - Fixed displaying channels and recording status in the RCU's LED display when a recording is interrupted due to higher priority. +- Implemented safe writing of config files (first writes into a temporary file + and then renames it). diff --git a/config.c b/config.c index 781900d7..a20c801b 100644 --- a/config.c +++ b/config.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 1.37 2001/01/13 12:36:32 kls Exp $ + * $Id: config.c 1.38 2001/01/13 15:36:31 kls Exp $ */ #include "config.h" @@ -119,10 +119,9 @@ bool cKeys::Load(const char *FileName) bool cKeys::Save(void) { - //TODO make backup copies??? bool result = true; - FILE *f = fopen(fileName, "w"); - if (f) { + cSafeFile f(fileName); + if (f.Open()) { if (fprintf(f, "Code\t%c\nAddress\t%04X\n", code, address) > 0) { for (tKey *k = keys; k->type != kNone; k++) { if (fprintf(f, "%s\t%08X\n", k->name, k->code) <= 0) { @@ -133,7 +132,7 @@ bool cKeys::Save(void) } else result = false; - fclose(f); + f.Close(); } else result = false; @@ -782,8 +781,8 @@ bool cSetup::Save(const char *FileName) if (!FileName) FileName = fileName; if (FileName) { - FILE *f = fopen(FileName, "w"); - if (f) { + cSafeFile f(FileName); + if (f.Open()) { fprintf(f, "# VDR Setup\n"); fprintf(f, "OSDLanguage = %d\n", OSDLanguage); fprintf(f, "PrimaryDVB = %d\n", PrimaryDVB); @@ -796,7 +795,7 @@ bool cSetup::Save(const char *FileName) fprintf(f, "MarginStart = %d\n", MarginStart); fprintf(f, "MarginStop = %d\n", MarginStop); fprintf(f, "EPGScanTimeout = %d\n", EPGScanTimeout); - fclose(f); + f.Close(); isyslog(LOG_INFO, "saved setup to %s", FileName); return true; } diff --git a/config.h b/config.h index 34e20182..8f3cc77a 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 1.36 2000/12/25 14:20:09 kls Exp $ + * $Id: config.h 1.37 2001/01/13 14:56:29 kls Exp $ */ #ifndef __CONFIG_H @@ -202,11 +202,10 @@ public: } bool Save(void) { - //TODO make backup copies??? bool result = true; T *l = (T *)First(); - FILE *f = fopen(fileName, "w"); - if (f) { + cSafeFile f(fileName); + if (f.Open()) { while (l) { if (!l->Save(f)) { result = false; @@ -214,12 +213,10 @@ public: } l = (T *)l->Next(); } - fclose(f); + f.Close(); } - else { - LOG_ERROR_STR(fileName); + else result = false; - } return result; } }; diff --git a/tools.c b/tools.c index 1caaa9fd..cd2c60f8 100644 --- a/tools.c +++ b/tools.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.c 1.26 2001/01/13 12:17:30 kls Exp $ + * $Id: tools.c 1.27 2001/01/13 15:35:02 kls Exp $ */ #define _GNU_SOURCE @@ -247,6 +247,27 @@ bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks) return true; } +char *ReadLink(const char *FileName) +{ + char RealName[_POSIX_PATH_MAX]; + const char *TargetName = NULL; + int n = readlink(FileName, RealName, sizeof(RealName) - 1); + if (n < 0) { + if (errno == ENOENT || errno == EINVAL) // file doesn't exist or is not a symlink + TargetName = FileName; + else { // some other error occurred + LOG_ERROR_STR(FileName); + } + } + else if (n < int(sizeof(RealName))) { // got it! + RealName[n] = 0; + TargetName = RealName; + } + else + esyslog(LOG_ERR, "ERROR: symlink's target name too long: %s", FileName); + return TargetName ? strdup(TargetName) : NULL; +} + // --- cFile ----------------------------------------------------------------- bool cFile::files[FD_SETSIZE] = { false }; @@ -367,6 +388,45 @@ bool cFile::FileReady(int FileDes, int TimeoutMs) return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set); } +// --- cSafeFile ------------------------------------------------------------- + +cSafeFile::cSafeFile(const char *FileName) +{ + f = NULL; + fileName = ReadLink(FileName); + tempName = fileName ? new char[strlen(fileName) + 5] : NULL; + if (tempName) + strcat(strcpy(tempName, fileName), ".$$$"); +} + +cSafeFile::~cSafeFile() +{ + if (f) + fclose(f); + unlink(tempName); + delete fileName; + delete tempName; +} + +bool cSafeFile::Open(void) +{ + if (!f && fileName && tempName) { + f = fopen(tempName, "w"); + if (!f) + LOG_ERROR_STR(tempName); + } + return f != NULL; +} + +void cSafeFile::Close(void) +{ + if (f) { + fclose(f); + f = NULL; + rename(tempName, fileName); + } +} + // --- cListObject ----------------------------------------------------------- cListObject::cListObject(void) diff --git a/tools.h b/tools.h index 83f3fa0c..539dba01 100644 --- a/tools.h +++ b/tools.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.h 1.22 2000/12/10 11:49:42 kls Exp $ + * $Id: tools.h 1.23 2001/01/13 15:36:00 kls Exp $ */ #ifndef __TOOLS_H @@ -48,6 +48,7 @@ uint FreeDiskSpaceMB(const char *Directory); bool DirectoryOk(const char *DirName, bool LogErrors = false); bool MakeDirs(const char *FileName, bool IsDirectory = false); bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false); +char *ReadLink(const char *FileName); class cFile { private: @@ -68,6 +69,19 @@ public: static bool FileReady(int FileDes, int TimeoutMs = 1000); }; +class cSafeFile { +private: + FILE *f; + char *fileName; + char *tempName; +public: + cSafeFile(const char *FileName); + ~cSafeFile(); + operator FILE* () { return f; } + bool Open(void); + void Close(void); + }; + class cListObject { private: cListObject *prev, *next;