changeset 2850:667eb6d5a0b1

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
author Wojciech Muła <>
date Thu, 18 Oct 2018 19:13:07 +0100
parents 17b72aa3147b
children e88128f85953
files src/ui_fileops.c
diffstat 1 files changed, 62 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/ui_fileops.c	Tue Oct 16 19:07:11 2018 +0100
+++ b/src/ui_fileops.c	Thu Oct 18 19:13:07 2018 +0100
@@ -546,6 +546,68 @@
 		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;