Correctly implement can_trash by actually looking for a trash dir, not
authorAlexander Larsson <alexl@redhat.com>
Tue, 11 Mar 2008 14:48:28 +0000 (14:48 +0000)
committerAlexander Larsson <alexl@src.gnome.org>
Tue, 11 Mar 2008 14:48:28 +0000 (14:48 +0000)
2008-03-11  Alexander Larsson  <alexl@redhat.com>

        * 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
gio/glocalfile.c
gio/glocalfileinfo.c
gio/glocalfileinfo.h

index 23df8d1..3c09930 100644 (file)
@@ -1,3 +1,11 @@
+2008-03-11  Alexander Larsson  <alexl@redhat.com>
+
+        * 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  <mclasen@redhat.com>
        
        * === Released 2.16.1 ===
index 5683b38..60d8749 100644 (file)
@@ -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,
index 4553ded..23ae2bf 100644 (file)
@@ -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);
     }
 }
 
index 19999ac..397a7ea 100644 (file)
@@ -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);