From bedad2aeecc6c65ea36dc2ce44cc9d18dca1c605 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Wojciech=20Mu=C5=82a?= <> Date: Thu, 18 Oct 2018 19:13:07 +0100 Subject: [PATCH] Fix #544: copy symlinks as symlinks instead of dereferencing them https://github.com/BestImageViewer/geeqie/issues/544 Revert back to previous commit. Include new patch to prevent the crash noted in #640 --- src/ui_fileops.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/ui_fileops.c b/src/ui_fileops.c index 541ac5b7..3eea7dfe 100644 --- a/src/ui_fileops.c +++ b/src/ui_fileops.c @@ -546,6 +546,68 @@ gboolean copy_file(const gchar *s, const gchar *t) goto end; } + /* Do not dereference absolute symlinks, but copy them "as is". + * For a relative symlink, we don't know how to properly change it when + * copied/moved to another dir to keep pointing it to same target as + * a relative symlink, so we turn it into absolute symlink using + * realpath() instead. */ + struct stat st; + if (lstat_utf8(sl, &st) && S_ISLNK(st.st_mode)) + { + gchar *link_target; + ssize_t i; + + link_target = g_malloc(st.st_size + 1); + i = readlink(sl, link_target, st.st_size); + if (i<0) + { + g_free(link_target); + goto orig_copy; // try a "normal" copy + } + link_target[st.st_size] = '\0'; + + if (link_target[0] != G_DIR_SEPARATOR) // if it is a relative symlink + { + gchar *absolute; + + gchar *lastslash = strrchr(sl, G_DIR_SEPARATOR); + gint len = lastslash - sl + 1; + + absolute = g_malloc(len + st.st_size + 1); + strncpy(absolute, sl, len); + strcpy(absolute + len, link_target); + g_free(link_target); + link_target = absolute; + + gchar *realPath; + realPath = realpath(link_target, NULL); + + if (realPath != NULL) // successfully resolved into an absolute path + { + g_free(link_target); + link_target = g_strdup(realPath); + g_free(realPath); + } + else // could not get absolute path, got some error instead + { + g_free(link_target); + goto orig_copy; // so try a "normal" copy + } + } + + if (stat_utf8(tl, &st)) unlink(tl); // first try to remove directory entry in destination directory if such entry exists + + gint success = (symlink(link_target, tl) == 0); + g_free(link_target); + + if (success) + { + ret = TRUE; + goto end; + } + } // if symlink did not succeed, continue on to try a copy procedure + orig_copy: + fi = fopen(sl, "rb"); if (!fi) goto end; -- 2.20.1