From c5a10d2650e11b9a700b605640a41275a4ee1a5d Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 11 Mar 2008 14:48:28 +0000 Subject: [PATCH] Correctly implement can_trash by actually looking for a trash dir, not 2008-03-11 Alexander Larsson * glocalfile.c: * glocalfileinfo.[ch]: Correctly implement can_trash by actually looking for a trash dir, not just assuming one exists. svn path=/trunk/; revision=6679 --- gio/ChangeLog | 8 ++++++ gio/glocalfile.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ gio/glocalfileinfo.c | 10 ++++--- gio/glocalfileinfo.h | 3 +++ 4 files changed, 91 insertions(+), 3 deletions(-) diff --git a/gio/ChangeLog b/gio/ChangeLog index 23df8d1..3c09930 100644 --- a/gio/ChangeLog +++ b/gio/ChangeLog @@ -1,3 +1,11 @@ +2008-03-11 Alexander Larsson + + * glocalfile.c: + * glocalfileinfo.[ch]: + Correctly implement can_trash by actually + looking for a trash dir, not just assuming + one exists. + 2008-03-10 Matthias Clasen * === Released 2.16.1 === diff --git a/gio/glocalfile.c b/gio/glocalfile.c index 5683b38..60d8749 100644 --- a/gio/glocalfile.c +++ b/gio/glocalfile.c @@ -1576,6 +1576,79 @@ escape_trash_name (char *name) return g_string_free (str, FALSE); } +gboolean +_g_local_file_has_trash_dir (const char *dirname, dev_t dir_dev) +{ + static gsize home_dev = 0; + char *topdir, *globaldir, *trashdir, *tmpname; + uid_t uid; + char uid_str[32]; + struct stat global_stat, trash_stat; + gboolean res; + int statres; + + if (g_once_init_enter (&home_dev)) + { + gsize setup_value = 0; + struct stat home_stat; + + g_stat (g_get_home_dir (), &home_stat); + setup_value = home_stat.st_dev; + g_once_init_leave (&home_dev, setup_value); + } + + /* Assume we can trash to the home */ + if (dir_dev == (dev_t)home_dev) + return TRUE; + + topdir = find_mountpoint_for (dirname, dir_dev); + if (topdir == NULL) + return FALSE; + + globaldir = g_build_filename (topdir, ".Trash", NULL); + statres = g_lstat (globaldir, &global_stat); + if (g_lstat (globaldir, &global_stat) == 0 && + S_ISDIR (global_stat.st_mode) && + (global_stat.st_mode & S_ISVTX) != 0) + { + /* got a toplevel sysadmin created dir, assume we + * can trash to it (we should be able to create a dir) + * This fails for the FAT case where the ownership of + * that dir would be wrong though.. + */ + g_free (globaldir); + g_free (topdir); + return TRUE; + } + g_free (globaldir); + + /* No global trash dir, or it failed the tests, fall back to $topdir/.Trash-$uid */ + uid = geteuid (); + g_snprintf (uid_str, sizeof (uid_str), "%lu", (unsigned long) uid); + + tmpname = g_strdup_printf (".Trash-%s", uid_str); + trashdir = g_build_filename (topdir, tmpname, NULL); + g_free (tmpname); + + if (g_lstat (trashdir, &trash_stat) == 0) + { + g_free (topdir); + g_free (trashdir); + return + S_ISDIR (trash_stat.st_mode) && + trash_stat.st_uid == uid; + } + g_free (trashdir); + + /* User specific trash didn't exist, can we create it? */ + res = g_access (topdir, W_OK) == 0; + + g_free (topdir); + + return res; +} + + static gboolean g_local_file_trash (GFile *file, GCancellable *cancellable, diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c index 4553ded..23ae2bf 100644 --- a/gio/glocalfileinfo.c +++ b/gio/glocalfileinfo.c @@ -781,6 +781,7 @@ _g_local_file_info_get_parent_info (const char *dir, parent_info->writable = FALSE; parent_info->is_sticky = FALSE; + parent_info->has_trash_dir = FALSE; parent_info->device = 0; if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME) || @@ -806,6 +807,10 @@ _g_local_file_info_get_parent_info (const char *dir, #endif parent_info->owner = statbuf.st_uid; parent_info->device = statbuf.st_dev; + /* No need to find trash dir if its not writable anyway */ + if (parent_info->writable && + g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH)) + parent_info->has_trash_dir = _g_local_file_has_trash_dir (dir, statbuf.st_dev); } } } @@ -863,10 +868,9 @@ get_access_rights (GFileAttributeMatcher *attribute_matcher, g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, writable); - /* TODO: This means we can move it, but we should also look for a trash dir */ if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH)) - g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, - writable); + g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, + writable && parent_info->has_trash_dir); } } diff --git a/gio/glocalfileinfo.h b/gio/glocalfileinfo.h index 19999ac..397a7ea 100644 --- a/gio/glocalfileinfo.h +++ b/gio/glocalfileinfo.h @@ -35,10 +35,13 @@ G_BEGIN_DECLS typedef struct { gboolean writable; gboolean is_sticky; + gboolean has_trash_dir; int owner; dev_t device; } GLocalParentFileInfo; +gboolean _g_local_file_has_trash_dir (const char *dirname, + dev_t dir_dev); void _g_local_file_info_get_parent_info (const char *dir, GFileAttributeMatcher *attribute_matcher, GLocalParentFileInfo *parent_info); -- 2.7.4