[gnome-settings-daemon] housekeeping: Implement automatic purging of trash://
- From: Bastien Nocera <hadess src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-settings-daemon] housekeeping: Implement automatic purging of trash://
- Date: Mon, 3 Dec 2012 10:04:07 +0000 (UTC)
commit 857e00dd831143ed5cd1e95772130559b550520c
Author: Matthias Clasen <mclasen redhat com>
Date: Sun Nov 25 00:19:27 2012 -0500
housekeeping: Implement automatic purging of trash://
This commit reimplements the 'empty trash' functionality not
to rely on nautilus, and adds an 'age' parameter.
We install timeouts that check once every hour and purges
trash and temporary files if they are older than the 'purge-after'
setting allows. The timeouts only act if the 'purge-trash' or
'purge-temp-files' settings are TRUE.
Temporary files are looked for in g_get_tmp_dir(), /tmp and /var/tmp.
https://bugzilla.gnome.org/show_bug.cgi?id=149572
plugins/housekeeping/gsd-disk-space.c | 346 +++++++++++++++++++++++++++++----
plugins/housekeeping/gsd-disk-space.h | 2 +
2 files changed, 307 insertions(+), 41 deletions(-)
---
diff --git a/plugins/housekeeping/gsd-disk-space.c b/plugins/housekeeping/gsd-disk-space.c
index cc4a7ee..469bcba 100644
--- a/plugins/housekeeping/gsd-disk-space.c
+++ b/plugins/housekeeping/gsd-disk-space.c
@@ -52,6 +52,11 @@
#define SETTINGS_MIN_NOTIFY_PERIOD "min-notify-period"
#define SETTINGS_IGNORE_PATHS "ignore-paths"
+#define PRIVACY_SETTINGS "org.gnome.desktop.privacy"
+#define SETTINGS_PURGE_TRASH "remove-old-trash-files"
+#define SETTINGS_PURGE_TEMP_FILES "remove-old-temp-files"
+#define SETTINGS_PURGE_AFTER "old-files-age"
+
typedef struct
{
GUnixMountEntry *mount;
@@ -68,11 +73,18 @@ static unsigned int free_size_gb_no_notify = 2;
static unsigned int min_notify_period = 10;
static GSList *ignore_paths = NULL;
static GSettings *settings = NULL;
+static GSettings *privacy_settings = NULL;
static GsdLdsmDialog *dialog = NULL;
static NotifyNotification *notification = NULL;
static guint64 *time_read;
+static gboolean purge_trash;
+static gboolean purge_temp_files;
+static guint purge_after;
+static guint purge_trash_id = 0;
+static guint purge_temp_id = 0;
+
static gchar*
ldsm_get_fs_id_for_path (const gchar *path)
{
@@ -215,77 +227,312 @@ examine_callback (NotifyNotification *n,
notify_notification_close (n, NULL);
}
+static gboolean
+should_purge_file (GFile *file,
+ GCancellable *cancellable,
+ GDateTime *old)
+{
+ GFileInfo *info;
+ GDateTime *date;
+ gboolean should_purge;
+
+ should_purge = FALSE;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_TRASH_DELETION_DATE ","
+ G_FILE_ATTRIBUTE_UNIX_UID ","
+ G_FILE_ATTRIBUTE_TIME_CHANGED,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable,
+ NULL);
+
+ date = g_file_info_get_deletion_date (info);
+ if (date == NULL) {
+ guint uid;
+ guint64 ctime;
+
+ uid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID);
+ if (uid != getuid ()) {
+ should_purge = FALSE;
+ goto out;
+ }
+
+ ctime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CHANGED);
+ date = g_date_time_new_from_unix_local ((gint64) ctime);
+ }
+
+ should_purge = g_date_time_difference (old, date) >= 0;
+ g_date_time_unref (date);
+
+out:
+ g_object_unref (info);
+
+ return should_purge;
+}
+
+typedef struct {
+ gint ref_count;
+ GFile *file;
+ GCancellable *cancellable;
+ GDateTime *old;
+ gboolean dry_run;
+ gboolean trash;
+ gchar *name;
+ gint depth;
+} DeleteData;
+
+static DeleteData *
+delete_data_new (GFile *file,
+ GCancellable *cancellable,
+ GDateTime *old,
+ gboolean dry_run,
+ gboolean trash,
+ gint depth)
+{
+ DeleteData *data;
+
+ data = g_new (DeleteData, 1);
+ data->ref_count = 1;
+ data->file = g_object_ref (file);
+ data->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ data->old = g_date_time_ref (old);
+ data->dry_run = dry_run;
+ data->trash = trash;
+ data->depth = depth;
+ data->name = g_file_get_parse_name (data->file);
+
+ return data;
+}
+
+static DeleteData *
+delete_data_ref (DeleteData *data)
+{
+ data->ref_count += 1;
+ return data;
+}
+
static void
-nautilus_empty_trash_cb (GObject *object,
- GAsyncResult *res,
- gpointer _unused)
+delete_data_unref (DeleteData *data)
{
- GDBusProxy *proxy = G_DBUS_PROXY (object);
- GError *error = NULL;
+ data->ref_count -= 1;
+ if (data->ref_count > 0)
+ return;
- g_dbus_proxy_call_finish (proxy, res, &error);
+ g_object_unref (data->file);
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_date_time_unref (data->old);
+ g_free (data->name);
+ g_free (data);
+}
- if (error != NULL) {
- g_warning ("Unable to call EmptyTrash() on the Nautilus DBus interface: %s",
- error->message);
- g_error_free (error);
+static void delete_recursively_by_age (DeleteData *data);
+
+static void
+delete_batch (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source);
+ DeleteData *data = user_data;
+ GList *files, *f;
+ GFile *child_file;
+ DeleteData *child;
+ GFileInfo *info;
+ GError *error = NULL;
+
+ files = g_file_enumerator_next_files_finish (enumerator, res, &error);
+
+ g_debug ("GsdHousekeeping: purging %d children of %s", g_list_length (files), data->name);
+
+ if (files) {
+ for (f = files; f; f = f->next) {
+ if (g_cancellable_is_cancelled (data->cancellable))
+ break;
+ info = f->data;
+
+ child_file = g_file_get_child (data->file, g_file_info_get_name (info));
+ child = delete_data_new (child_file,
+ data->cancellable,
+ data->old,
+ data->dry_run,
+ data->trash,
+ data->depth + 1);
+ delete_recursively_by_age (child);
+ delete_data_unref (child);
+ g_object_unref (child_file);
+ }
+ g_list_free_full (files, g_object_unref);
+ if (!g_cancellable_is_cancelled (data->cancellable)) {
+ g_file_enumerator_next_files_async (enumerator, 20,
+ 0,
+ data->cancellable,
+ delete_batch,
+ data);
+ return;
+ }
}
- /* clean up the proxy object */
- g_object_unref (proxy);
+ g_file_enumerator_close (enumerator, data->cancellable, NULL);
+ g_object_unref (enumerator);
+
+ if (data->depth > 0 && !g_cancellable_is_cancelled (data->cancellable)) {
+ if ((data->trash && data->depth > 1) ||
+ should_purge_file (data->file, data->cancellable, data->old)) {
+ g_debug ("GsdHousekeeping: purging %s\n", data->name);
+ if (!data->dry_run) {
+ g_file_delete (data->file, data->cancellable, NULL);
+ }
+ }
+ }
+ delete_data_unref (data);
}
static void
-nautilus_proxy_ready_cb (GObject *object,
- GAsyncResult *res,
- gpointer _unused)
+delete_subdir (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
{
- GDBusProxy *proxy = NULL;
+ GFile *file = G_FILE (source);
+ DeleteData *data = user_data;
+ GFileEnumerator *enumerator;
GError *error = NULL;
- proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+ g_debug ("GsdHousekeeping: purging %s in %s\n",
+ data->trash ? "trash" : "temporary files", data->name);
- if (proxy == NULL) {
- g_warning ("Unable to create a proxy object for the Nautilus DBus interface: %s",
- error->message);
+ enumerator = g_file_enumerate_children_finish (file, res, &error);
+ if (error) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
+ g_warning ("Failed to enumerate children of %s: %s\n", data->name, error->message);
g_error_free (error);
+ }
+ if (enumerator) {
+ g_file_enumerator_next_files_async (enumerator, 20,
+ 0,
+ data->cancellable,
+ delete_batch,
+ delete_data_ref (data));
+ }
+ delete_data_unref (data);
+}
+static void
+delete_recursively_by_age (DeleteData *data)
+{
+ if (data->trash && (data->depth == 1) &&
+ !should_purge_file (data->file, data->cancellable, data->old)) {
+ /* no need to recurse into trashed directories */
return;
}
- g_dbus_proxy_call (proxy,
- "EmptyTrash",
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- NULL,
- nautilus_empty_trash_cb,
- NULL);
+ g_file_enumerate_children_async (data->file,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ 0,
+ data->cancellable,
+ delete_subdir,
+ delete_data_ref (data));
+}
+
+void
+gsd_ldsm_purge_trash (GDateTime *old)
+{
+ GFile *file;
+ DeleteData *data;
+
+ file = g_file_new_for_uri ("trash:");
+ data = delete_data_new (file, NULL, old, FALSE, TRUE, 0);
+ delete_recursively_by_age (data);
+ delete_data_unref (data);
+ g_object_unref (file);
+}
+
+void
+gsd_ldsm_purge_temp_files (GDateTime *old)
+{
+ DeleteData *data;
+ GFile *file;
+
+ file = g_file_new_for_path (g_get_tmp_dir ());
+ data = delete_data_new (file, NULL, old, FALSE, FALSE, 0);
+ delete_recursively_by_age (data);
+
+ if (g_strcmp0 (g_get_tmp_dir (), "/var/tmp") != 0) {
+ g_assert (data->ref_count == 1);
+ g_assert (data->depth == 0);
+ g_object_unref (data->file);
+ data->file = g_file_new_for_path ("/var/tmp");
+ data->depth = 0;
+ delete_recursively_by_age (data);
+ }
+
+ if (g_strcmp0 (g_get_tmp_dir (), "/tmp") != 0) {
+ g_assert (data->ref_count == 1);
+ g_assert (data->depth == 0);
+ g_object_unref (data->file);
+ data->file = g_file_new_for_path ("/tmp");
+ data->depth = 0;
+ delete_recursively_by_age (data);
+ }
+
+ delete_data_unref (data);
+ g_object_unref (file);
}
void
gsd_ldsm_show_empty_trash (void)
{
- /* prepare the Nautilus proxy object */
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- "org.gnome.Nautilus",
- "/org/gnome/Nautilus",
- "org.gnome.Nautilus.FileOperations",
- NULL,
- nautilus_proxy_ready_cb,
- NULL);
+ GFile *file;
+ GDateTime *old;
+ DeleteData *data;
+
+ old = g_date_time_new_now_local ();
+ file = g_file_new_for_uri ("trash:");
+ data = delete_data_new (file, NULL, old, TRUE, TRUE, 0);
+ g_object_unref (file);
+ g_date_time_unref (old);
+
+ delete_recursively_by_age (data);
+ delete_data_unref (data);
+}
+
+static gboolean
+ldsm_purge_trash_and_temp (gpointer data)
+{
+ GDateTime *now, *old;
+
+ now = g_date_time_new_now_local ();
+ old = g_date_time_add_days (now, - purge_after);
+
+ if (purge_trash) {
+ g_debug ("housekeeping: purge trash older than %u days", purge_after);
+ gsd_ldsm_purge_trash (old);
+ }
+ if (purge_temp_files) {
+ g_debug ("housekeeping: purge temp files older than %u days", purge_after);
+ gsd_ldsm_purge_temp_files (old);
+ }
+
+ g_date_time_unref (old);
+ g_date_time_unref (now);
+
+ return G_SOURCE_CONTINUE;
}
static void
empty_trash_callback (NotifyNotification *n,
const char *action)
{
+ GDateTime *old;
+
g_assert (action != NULL);
g_assert (strcmp (action, "empty-trash") == 0);
- gsd_ldsm_show_empty_trash ();
+ old = g_date_time_new_now_local ();
+ gsd_ldsm_purge_trash (old);
+ g_date_time_unref (old);
notify_notification_close (n, NULL);
}
@@ -445,7 +692,7 @@ ldsm_mount_has_space (LdsmMountInfo *mount)
/* enough free space, nothing to do */
if (free_space > free_percent_notify)
return TRUE;
-
+
if (((gint64) mount->buf.f_frsize * (gint64) mount->buf.f_bavail) > ((gint64) free_size_gb_no_notify * GIGABYTE))
return TRUE;
@@ -478,7 +725,7 @@ ldsm_mount_is_user_ignore (const gchar *path)
return TRUE;
else
return FALSE;
-}
+}
static void
@@ -746,6 +993,10 @@ gsd_ldsm_get_config (void)
g_strfreev (settings_list);
}
+
+ purge_trash = g_settings_get_boolean (privacy_settings, SETTINGS_PURGE_TRASH);
+ purge_temp_files = g_settings_get_boolean (privacy_settings, SETTINGS_PURGE_TEMP_FILES);
+ purge_after = g_settings_get_uint (privacy_settings, SETTINGS_PURGE_AFTER);
}
static void
@@ -769,6 +1020,7 @@ gsd_ldsm_setup (gboolean check_now)
ldsm_free_mount_info);
settings = g_settings_new (SETTINGS_HOUSEKEEPING_DIR);
+ privacy_settings = g_settings_new (PRIVACY_SETTINGS);
gsd_ldsm_get_config ();
g_signal_connect (G_OBJECT (settings), "changed",
G_CALLBACK (gsd_ldsm_update_config), NULL);
@@ -783,11 +1035,21 @@ gsd_ldsm_setup (gboolean check_now)
ldsm_timeout_id = g_timeout_add_seconds (CHECK_EVERY_X_SECONDS,
ldsm_check_all_mounts, NULL);
+
+ purge_trash_id = g_timeout_add_seconds (3600, ldsm_purge_trash_and_temp, NULL);
}
void
gsd_ldsm_clean (void)
{
+ if (purge_trash_id)
+ g_source_remove (purge_trash_id);
+ purge_trash_id = 0;
+
+ if (purge_temp_id)
+ g_source_remove (purge_temp_id);
+ purge_temp_id = 0;
+
if (ldsm_timeout_id)
g_source_remove (ldsm_timeout_id);
ldsm_timeout_id = 0;
@@ -804,6 +1066,8 @@ gsd_ldsm_clean (void)
g_object_unref (settings);
}
+ g_clear_object (&privacy_settings);
+
if (dialog) {
gtk_widget_destroy (GTK_WIDGET (dialog));
dialog = NULL;
diff --git a/plugins/housekeeping/gsd-disk-space.h b/plugins/housekeeping/gsd-disk-space.h
index 363b73a..8f2d5d9 100644
--- a/plugins/housekeeping/gsd-disk-space.h
+++ b/plugins/housekeeping/gsd-disk-space.h
@@ -33,6 +33,8 @@ void gsd_ldsm_clean (void);
/* for the test */
void gsd_ldsm_show_empty_trash (void);
+void gsd_ldsm_purge_trash (GDateTime *old);
+void gsd_ldsm_purge_temp_files (GDateTime *old);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]