From 9ab96cfdb5e0b77f77b47103a8e42af620416d3e Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Tue, 12 Aug 2008 18:52:04 +0400 Subject: [PATCH 2/2] Use symlink-safe function for rewriting configuration files Claws configuration files are backed up on each configuration update and the sequence is the following: create temporary file with new configuration, rename() target file to the .bak, rename() temporary file to the target file. But when the target file is symlink, situation is not good: symlink is moved to the .bak file (so now the .bak file is the symlink pointing to the original symlink location) and new plain file is created in the place of the symlink. This is not what user expects. I had implemented symlink translation of both arguments of the rename() function, resulting in the new routine, symsafe_rename(), that is used internally by prefs.c. Signed-off-by: Eygene Ryabinkin --- src/common/prefs.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/common/prefs.c b/src/common/prefs.c index f306ab8..f12b60b 100644 --- a/src/common/prefs.c +++ b/src/common/prefs.c @@ -24,6 +24,7 @@ #include "defs.h" #include +#include #include "prefs.h" #include "utils.h" @@ -102,6 +103,51 @@ PrefFile *prefs_write_open(const gchar *path) } /*! + *\brief Helper for prefs_file_close(). + * Acts as rename() call, but resolves symbolic links + * rather then destroying (when 'to' is symbolic link) + * or copying (when 'from' is symbolic link) them. + * + *\param from Source name. + * + *\param to Destination name. + * + *\return 0 on success, -1 on failure. + */ +static int +symsafe_rename(const char *from, const char *to) +{ + char target[PATH_MAX + 1], source[PATH_MAX + 1]; + + memset((void *)source, 0, sizeof(source)); + if (readlink(from, source, sizeof(source) - 1) == -1) { + switch (errno) { + case EINVAL: /* Not a symbolic link */ + break; + default: + return -1; + } + } else { + from = source; + } + + memset((void *)target, 0, sizeof(target)); + if (readlink(to, target, sizeof(target) - 1) == -1) { + switch (errno) { + case EINVAL: /* Not a symbolic link */ + case ENOENT: /* No such file at all */ + break; + default: + return -1; + } + } else { + to = target; + } + + return rename(from, to); +} + +/*! *\brief Close and free preferences file * Creates final file from temp, creates backup * @@ -175,7 +221,7 @@ gint prefs_file_close(PrefFile *pfile) #ifdef G_OS_WIN32 claws_unlink(bakpath); #endif - if (rename(path, bakpath) < 0) { + if (symsafe_rename(path, bakpath) < 0) { FILE_OP_ERROR(path, "rename"); claws_unlink(tmppath); g_free(path); @@ -188,7 +234,7 @@ gint prefs_file_close(PrefFile *pfile) #ifdef G_OS_WIN32 claws_unlink(path); #endif - if (rename(tmppath, path) < 0) { + if (symsafe_rename(tmppath, path) < 0) { FILE_OP_ERROR(tmppath, "rename"); claws_unlink(tmppath); g_free(path); -- 1.5.6.4