Fix #544: copy symlinks as symlinks instead of dereferencing them
authorWojciech Muła <>
Thu, 18 Oct 2018 18:13:07 +0000 (19:13 +0100)
committerColin Clark <colin.clark@cclark.uk>
Thu, 18 Oct 2018 18:13:07 +0000 (19:13 +0100)
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

index 541ac5b..3eea7df 100644 (file)
@@ -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;