From 0f5017fb701b221846f04fa610c8429bbaa49136 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Thu, 13 Sep 2018 17:33:59 +0200
Subject: [PATCH 152/682] glocalfile: Fix access::can-trash if parent is
 symlink

G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH can be set to a wrong value if
its parent dir is a symlink. This is because the find_mountpoint_for()
function tries to find mountpoint for a filepath and expands symlinks
only in parent dirs. But in this case the path is already parent dir
and needs to be expanded first...

Closes: https://gitlab.gnome.org/GNOME/glib/issues/1522
---
 gio/glocalfile.c | 104 +++++++++++++++++++++++++++++------------------
 1 file changed, 65 insertions(+), 39 deletions(-)

diff --git a/gio/glocalfile.c b/gio/glocalfile.c
index 30fa2281c..33b5ba3da 100644
--- a/gio/glocalfile.c
+++ b/gio/glocalfile.c
@@ -109,7 +109,7 @@ G_DEFINE_TYPE_WITH_CODE (GLocalFile, g_local_file, G_TYPE_OBJECT,
 			 G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
 						g_local_file_file_iface_init))
 
-static char *find_mountpoint_for (const char *file, dev_t dev);
+static char *find_mountpoint_for (const char *file, dev_t dev, gboolean resolve_basename_symlink);
 
 static void
 g_local_file_finalize (GObject *object)
@@ -791,7 +791,7 @@ get_mount_info (GFileInfo             *fs_info,
     {
       mount_info = 0;
 
-      mountpoint = find_mountpoint_for (path, buf.st_dev);
+      mountpoint = find_mountpoint_for (path, buf.st_dev, FALSE);
       if (mountpoint == NULL)
 	mountpoint = g_strdup ("/");
 
@@ -886,7 +886,7 @@ get_volume_for_path (const char *path)
 }
 
 static char *
-find_mountpoint_for (const char *file, dev_t dev)
+find_mountpoint_for (const char *file, dev_t dev, gboolean resolve_basename_symlink)
 {
   wchar_t *wpath;
   char *utf8_path;
@@ -1132,7 +1132,7 @@ g_local_file_find_enclosing_mount (GFile         *file,
   if (g_lstat (local->filename, &buf) != 0)
     goto error;
 
-  mountpoint = find_mountpoint_for (local->filename, buf.st_dev);
+  mountpoint = find_mountpoint_for (local->filename, buf.st_dev, FALSE);
   if (mountpoint == NULL)
     goto error;
 
@@ -1584,12 +1584,51 @@ expand_symlink (const char *link)
 }
 
 static char *
-get_parent (const char *path, 
-            dev_t      *parent_dev)
+expand_symlinks (const char *path,
+                 dev_t      *dev)
 {
-  char *parent, *tmp;
-  GStatBuf parent_stat;
+  char *tmp, *target;
+  GStatBuf target_stat;
   int num_recursions;
+
+  target = g_strdup (path);
+
+  num_recursions = 0;
+  do
+    {
+      if (g_lstat (target, &target_stat) != 0)
+        {
+          g_free (target);
+          return NULL;
+        }
+
+      if (S_ISLNK (target_stat.st_mode))
+        {
+          tmp = target;
+          target = expand_symlink (target);
+          g_free (tmp);
+        }
+
+      num_recursions++;
+      if (num_recursions > 12)
+        {
+          g_free (target);
+          return NULL;
+        }
+    }
+  while (S_ISLNK (target_stat.st_mode));
+
+  if (dev)
+    *dev = target_stat.st_dev;
+
+  return target;
+}
+
+static char *
+get_parent (const char *path,
+            dev_t      *parent_dev)
+{
+  char *parent, *res;
   char *path_copy;
 
   path_copy = strip_trailing_slashes (path);
@@ -1604,32 +1643,10 @@ get_parent (const char *path,
     }
   g_free (path_copy);
 
-  num_recursions = 0;
-  do {
-    if (g_lstat (parent, &parent_stat) != 0)
-      {
-	g_free (parent);
-	return NULL;
-      }
-    
-    if (S_ISLNK (parent_stat.st_mode))
-      {
-	tmp = parent;
-	parent = expand_symlink (parent);
-	g_free (tmp);
-      }
-    
-    num_recursions++;
-    if (num_recursions > 12)
-      {
-	g_free (parent);
-	return NULL;
-      }
-  } while (S_ISLNK (parent_stat.st_mode));
+  res = expand_symlinks (parent, parent_dev);
+  g_free (parent);
 
-  *parent_dev = parent_stat.st_dev;
-  
-  return parent;
+  return res;
 }
 
 static char *
@@ -1656,13 +1673,22 @@ expand_all_symlinks (const char *path)
 }
 
 static char *
-find_mountpoint_for (const char *file, 
-                     dev_t       dev)
+find_mountpoint_for (const char *file,
+                     dev_t       dev,
+                     gboolean    resolve_basename_symlink)
 {
   char *dir, *parent;
   dev_t dir_dev, parent_dev;
 
-  dir = g_strdup (file);
+  if (resolve_basename_symlink)
+    {
+      dir = expand_symlinks (file, NULL);
+      if (dir == NULL)
+        return g_strdup (file);
+    }
+  else
+    dir = g_strdup (file);
+
   dir_dev = dev;
 
   while (1) 
@@ -1693,7 +1719,7 @@ _g_local_file_find_topdir_for (const char *file)
   if (dir == NULL)
     return NULL;
 
-  mountpoint = find_mountpoint_for (dir, dir_dev);
+  mountpoint = find_mountpoint_for (dir, dir_dev, TRUE);
   g_free (dir);
 
   return mountpoint;
@@ -1789,7 +1815,7 @@ _g_local_file_has_trash_dir (const char *dirname, dev_t dir_dev)
   if (dir_dev == home_dev)
     return TRUE;
 
-  topdir = find_mountpoint_for (dirname, dir_dev);
+  topdir = find_mountpoint_for (dirname, dir_dev, TRUE);
   if (topdir == NULL)
     return FALSE;
 
@@ -1856,7 +1882,7 @@ _g_local_file_is_lost_found_dir (const char *path, dev_t path_dev)
   if (!g_str_has_suffix (path, "/lost+found"))
     goto out;
 
-  mount_dir = find_mountpoint_for (path, path_dev);
+  mount_dir = find_mountpoint_for (path, path_dev, FALSE);
   if (mount_dir == NULL)
     goto out;
 
-- 
2.19.1

