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
This commit is contained in:
Ondrej Holy 2018-09-13 17:33:59 +02:00
parent 035975da09
commit 0f5017fb70

View File

@ -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;
@ -1583,13 +1583,52 @@ expand_symlink (const char *link)
}
}
static char *
expand_symlinks (const char *path,
dev_t *dev)
{
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, *tmp;
GStatBuf parent_stat;
int num_recursions;
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;
}
res = expand_symlinks (parent, parent_dev);
g_free (parent);
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));
*parent_dev = parent_stat.st_dev;
return parent;
return res;
}
static char *
@ -1657,12 +1674,21 @@ expand_all_symlinks (const char *path)
static char *
find_mountpoint_for (const char *file,
dev_t dev)
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;