Merge branch 'fix-overlayfs-trashing-1027' into 'master'

Fix trashing on overlayfs

Closes #1027

See merge request GNOME/glib!182
This commit is contained in:
Ondrej Holy 2018-08-13 18:01:31 +00:00
commit 4a77dd6799
3 changed files with 36 additions and 33 deletions

View File

@ -1677,20 +1677,16 @@ find_mountpoint_for (const char *file,
}
}
static char *
_g_local_file_find_topdir_for_internal (const char *file, dev_t file_dev)
char *
_g_local_file_find_topdir_for (const char *file)
{
char *dir;
char *mountpoint = NULL;
dev_t dir_dev;
dir = get_parent (file, &dir_dev);
if (dir == NULL || dir_dev != file_dev)
{
g_free (dir);
return NULL;
}
if (dir == NULL)
return NULL;
mountpoint = find_mountpoint_for (dir, dir_dev);
g_free (dir);
@ -1698,17 +1694,6 @@ _g_local_file_find_topdir_for_internal (const char *file, dev_t file_dev)
return mountpoint;
}
char *
_g_local_file_find_topdir_for (const char *file)
{
GStatBuf file_stat;
if (g_lstat (file, &file_stat) != 0)
return NULL;
return _g_local_file_find_topdir_for_internal (file, file_stat.st_dev);
}
static char *
get_unique_filename (const char *basename,
int id)
@ -1908,6 +1893,7 @@ g_local_file_trash (GFile *file,
char *original_name, *original_name_escaped;
int i;
char *data;
char *path;
gboolean is_homedir_trash;
char *delete_time = NULL;
int fd;
@ -1932,6 +1918,24 @@ g_local_file_trash (GFile *file,
is_homedir_trash = FALSE;
trashdir = NULL;
/* On overlayfs, a file's st_dev will be different to the home directory's.
* We still want to create our trash directory under the home directory, so
* instead we should stat the directory that the file we're deleting is in as
* this will have the same st_dev.
*/
if (!S_ISDIR (file_stat.st_mode))
{
path = g_path_get_dirname (local->filename);
/* If the parent is a symlink to a different device then it might have
* st_dev equal to the home directory's, in which case we will end up
* trying to rename across a filesystem boundary, which doesn't work. So
* we use g_stat here instead of g_lstat, to know where the symlink
* points to. */
g_stat (path, &file_stat);
g_free (path);
}
if (file_stat.st_dev == home_stat.st_dev)
{
is_homedir_trash = TRUE;
@ -1962,8 +1966,7 @@ g_local_file_trash (GFile *file,
uid = geteuid ();
g_snprintf (uid_str, sizeof (uid_str), "%lu", (unsigned long)uid);
topdir = _g_local_file_find_topdir_for_internal (local->filename,
file_stat.st_dev);
topdir = _g_local_file_find_topdir_for (local->filename);
if (topdir == NULL)
{
g_set_io_error (error,

View File

@ -924,13 +924,9 @@ get_access_rights (GFileAttributeMatcher *attribute_matcher,
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE,
writable);
/* Trashing is supported only if the parent device is the same */
if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH))
_g_file_info_set_attribute_boolean_by_id (info,
G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH,
writable &&
parent_info->has_trash_dir &&
parent_info->device == statbuf->st_dev);
_g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH,
writable && parent_info->has_trash_dir);
}
}

View File

@ -35,23 +35,26 @@ test_trash_not_supported (void)
GFileInfo *info;
GError *error = NULL;
gboolean ret;
GStatBuf file_stat, home_stat;
gchar *parent_dirname;
GStatBuf parent_stat, home_stat;
/* The test assumes that tmp file is located on system internal mount. */
file = g_file_new_tmp ("test-trashXXXXXX", &stream, &error);
parent_dirname = g_path_get_dirname (g_file_peek_path (file));
g_assert_no_error (error);
g_assert_cmpint (g_lstat (g_file_peek_path (file), &file_stat), ==, 0);
g_test_message ("File: %s (dev: %" G_GUINT64_FORMAT ")",
g_file_peek_path (file), (guint64) file_stat.st_dev);
g_assert_cmpint (g_stat (parent_dirname, &parent_stat), ==, 0);
g_test_message ("File: %s (parent st_dev: %" G_GUINT64_FORMAT ")",
g_file_peek_path (file), (guint64) parent_stat.st_dev);
g_assert_cmpint (g_stat (g_get_home_dir (), &home_stat), ==, 0);
g_test_message ("Home: %s (dev: %" G_GUINT64_FORMAT ")",
g_test_message ("Home: %s (st_dev: %" G_GUINT64_FORMAT ")",
g_get_home_dir (), (guint64) home_stat.st_dev);
if (file_stat.st_dev == home_stat.st_dev)
if (parent_stat.st_dev == home_stat.st_dev)
{
g_test_skip ("The file has to be on another filesystem than the home trash to run this test");
g_free (parent_dirname);
g_object_unref (stream);
g_object_unref (file);
@ -85,6 +88,7 @@ test_trash_not_supported (void)
g_io_stream_close (G_IO_STREAM (stream), NULL, &error);
g_assert_no_error (error);
g_free (parent_dirname);
g_object_unref (info);
g_object_unref (stream);
g_object_unref (file);