diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt index 442b2247b..39999c452 100644 --- a/docs/reference/gio/gio-sections-common.txt +++ b/docs/reference/gio/gio-sections-common.txt @@ -1572,6 +1572,7 @@ g_unix_mount_point_guess_symbolic_icon g_unix_mount_point_guess_name g_unix_mount_point_guess_can_eject g_unix_mount_points_get +g_unix_mount_point_at g_unix_mounts_get g_unix_mount_at g_unix_mount_for diff --git a/gio/gfile.c b/gio/gfile.c index a2ded14ea..a8d12aa2c 100644 --- a/gio/gfile.c +++ b/gio/gfile.c @@ -4134,7 +4134,9 @@ g_file_delete_finish (GFile *file, * Sends @file to the "Trashcan", if possible. This is similar to * deleting it, but the user can recover it before emptying the trashcan. * Not all file systems support trashing, so this call can return the - * %G_IO_ERROR_NOT_SUPPORTED error. + * %G_IO_ERROR_NOT_SUPPORTED error. Since GLib 2.66, the `x-gvfs-notrash` unix + * mount option can be used to disable g_file_trash() support for certain + * mounts, the %G_IO_ERROR_NOT_SUPPORTED error will be returned in that case. * * If @cancellable is not %NULL, then the operation can be cancelled by * triggering the cancellable object from another thread. If the operation diff --git a/gio/glocalfile.c b/gio/glocalfile.c index dc97867e1..4d61663ba 100644 --- a/gio/glocalfile.c +++ b/gio/glocalfile.c @@ -1777,6 +1777,52 @@ try_make_relative (const char *path, return g_strdup (path); } +static gboolean +ignore_trash_mount (GUnixMountEntry *mount) +{ + GUnixMountPoint *mount_point = NULL; + const gchar *mount_options; + gboolean retval = TRUE; + + if (g_unix_mount_is_system_internal (mount)) + return TRUE; + + mount_options = g_unix_mount_get_options (mount); + if (mount_options == NULL) + { + mount_point = g_unix_mount_point_at (g_unix_mount_get_mount_path (mount), + NULL); + if (mount_point != NULL) + mount_options = g_unix_mount_point_get_options (mount_point); + } + + if (mount_options == NULL || + strstr (mount_options, "x-gvfs-notrash") == NULL) + retval = FALSE; + + g_clear_pointer (&mount_point, g_unix_mount_point_free); + + return retval; +} + +static gboolean +ignore_trash_path (const gchar *topdir) +{ + GUnixMountEntry *mount; + gboolean retval = TRUE; + + mount = g_unix_mount_at (topdir, NULL); + if (mount == NULL) + goto out; + + retval = ignore_trash_mount (mount); + + out: + g_clear_pointer (&mount, g_unix_mount_free); + + return retval; +} + gboolean _g_local_file_has_trash_dir (const char *dirname, dev_t dir_dev) { @@ -1787,7 +1833,6 @@ _g_local_file_has_trash_dir (const char *dirname, dev_t dir_dev) char uid_str[32]; GStatBuf global_stat, trash_stat; gboolean res; - GUnixMountEntry *mount; if (g_once_init_enter (&home_dev_set)) { @@ -1806,17 +1851,13 @@ _g_local_file_has_trash_dir (const char *dirname, dev_t dir_dev) if (topdir == NULL) return FALSE; - mount = g_unix_mount_at (topdir, NULL); - if (mount == NULL || g_unix_mount_is_system_internal (mount)) + if (ignore_trash_path (topdir)) { - g_clear_pointer (&mount, g_unix_mount_free); g_free (topdir); return FALSE; } - g_clear_pointer (&mount, g_unix_mount_free); - globaldir = g_build_filename (topdir, ".Trash", NULL); if (g_lstat (globaldir, &global_stat) == 0 && S_ISDIR (global_stat.st_mode) && @@ -1982,7 +2023,6 @@ g_local_file_trash (GFile *file, { uid_t uid; char uid_str[32]; - GUnixMountEntry *mount; uid = geteuid (); g_snprintf (uid_str, sizeof (uid_str), "%lu", (unsigned long)uid); @@ -1996,20 +2036,15 @@ g_local_file_trash (GFile *file, return FALSE; } - mount = g_unix_mount_at (topdir, NULL); - if (mount == NULL || g_unix_mount_is_system_internal (mount)) + if (ignore_trash_path (topdir)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Trashing on system internal mounts is not supported")); - - g_clear_pointer (&mount, g_unix_mount_free); g_free (topdir); return FALSE; } - g_clear_pointer (&mount, g_unix_mount_free); - /* Try looking for global trash dir $topdir/.Trash/$uid */ globaldir = g_build_filename (topdir, ".Trash", NULL); if (g_lstat (globaldir, &global_stat) == 0 && diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c index 0d3fa68b0..b0fdfa8c0 100644 --- a/gio/gunixmounts.c +++ b/gio/gunixmounts.c @@ -1660,6 +1660,52 @@ g_unix_mount_points_get (guint64 *time_read) return _g_get_unix_mount_points (); } +/** + * g_unix_mount_point_at: + * @mount_path: (type filename): path for a possible unix mount point. + * @time_read: (out) (optional): guint64 to contain a timestamp. + * + * Gets a #GUnixMountPoint for a given mount path. If @time_read is set, it + * will be filled with a unix timestamp for checking if the mount points have + * changed since with g_unix_mount_points_changed_since(). + * + * If more mount points have the same mount path, the last matching mount point + * is returned. + * + * Returns: (transfer full) (nullable): a #GUnixMountPoint, or %NULL if no match + * is found. + * + * Since: 2.66 + **/ +GUnixMountPoint * +g_unix_mount_point_at (const char *mount_path, + guint64 *time_read) +{ + GList *mount_points, *l; + GUnixMountPoint *mount_point, *found; + + mount_points = g_unix_mount_points_get (time_read); + + found = NULL; + for (l = mount_points; l != NULL; l = l->next) + { + mount_point = l->data; + + if (strcmp (mount_path, mount_point->mount_path) == 0) + { + if (found != NULL) + g_unix_mount_point_free (found); + + found = mount_point; + } + else + g_unix_mount_point_free (mount_point); + } + g_list_free (mount_points); + + return found; +} + /** * g_unix_mounts_changed_since: * @time: guint64 to contain a timestamp. diff --git a/gio/gunixmounts.h b/gio/gunixmounts.h index fe8e24160..2553e1ca1 100644 --- a/gio/gunixmounts.h +++ b/gio/gunixmounts.h @@ -132,6 +132,9 @@ GIcon * g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount GLIB_AVAILABLE_IN_ALL GList * g_unix_mount_points_get (guint64 *time_read); +GLIB_AVAILABLE_IN_2_66 +GUnixMountPoint *g_unix_mount_point_at (const char *mount_path, + guint64 *time_read); GLIB_AVAILABLE_IN_ALL GList * g_unix_mounts_get (guint64 *time_read); GLIB_AVAILABLE_IN_ALL