summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSachin Daluja <30343-sachindaluja@users.noreply.gitlab.gnome.org>2021-06-15 23:02:37 -0400
committerAntónio Fernandes <antoniof@gnome.org>2021-11-27 21:25:45 +0000
commit6b5e516f54f55c0fd47a41009759c1b23104df3c (patch)
tree025469c4f2bae35ebb522029170473201e21d39e
parentd48bddbd1d853effe649207eef575623d9347939 (diff)
downloadnautilus-wip/antoniof/try-admin-backend-for-file-ops.tar.gz
file-operations: Try admin backend for file ops When the user does not have sufficient permissions. Closes https://gitlab.gnome.org/GNOME/nautilus/-/issues/1282 Closes https://gitlab.gnome.org/GNOME/nautilus/-/issues/257
-rw-r--r--src/meson.build2
-rw-r--r--src/nautilus-file-operations-admin.c446
-rw-r--r--src/nautilus-file-operations-admin.h9
-rw-r--r--src/nautilus-file-operations-private.h4
-rw-r--r--src/nautilus-file-operations.c163
-rw-r--r--src/nautilus-file.c23
-rw-r--r--src/nautilus-file.h2
-rw-r--r--src/nautilus-files-view.c37
8 files changed, 635 insertions, 51 deletions
diff --git a/src/meson.build b/src/meson.build
index d4dd6a4f7..bc50a4640 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -189,6 +189,8 @@ libnautilus_sources = [
'nautilus-file-operations-private.h',
'nautilus-file-operations-dbus-data.c',
'nautilus-file-operations-dbus-data.h',
+ 'nautilus-file-operations-admin.c',
+ 'nautilus-file-operations-admin.h',
'nautilus-file-private.h',
'nautilus-file-queue.c',
'nautilus-file-queue.h',
diff --git a/src/nautilus-file-operations-admin.c b/src/nautilus-file-operations-admin.c
new file mode 100644
index 000000000..4eff66865
--- /dev/null
+++ b/src/nautilus-file-operations-admin.c
@@ -0,0 +1,446 @@
+#include <config.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "nautilus-file-operations-admin.h"
+#include "nautilus-file-operations-private.h"
+#include "nautilus-file-operations-dbus-data.h"
+#include "nautilus-ui-utilities.h"
+#include "nautilus-file-utilities.h"
+#include "nautilus-file.h"
+
+typedef enum
+{
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_INVALID,
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_COPY_TO_DIR,
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_MOVE_TO_DIR,
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_MOVE_FROM_DIR,
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_COPY_FILE,
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_MOVE_FILE,
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_DELETE_FILE,
+} NautilusAdminFileOpPermissionDlgType;
+
+typedef enum
+{
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_INVALID,
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_YES,
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_SKIP,
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_SKIP_ALL,
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_CANCEL,
+} NautilusAdminFileOpPermissionDlgResponse;
+
+
+typedef void (*FileOpAutoAdminMountFinishedCallback) (gboolean success,
+ gpointer callback_data,
+ GError *error);
+
+typedef struct
+{
+ FileOpAutoAdminMountFinishedCallback mount_finished_cb;
+ gpointer mount_finished_cb_data;
+} AdminMountCbData;
+
+typedef struct
+{
+ gboolean completed;
+ gboolean success;
+ GMutex mutex;
+ GCond cond;
+} FileSubopAutoAdminMountData;
+
+typedef struct
+{
+ NautilusAdminFileOpPermissionDlgType dlg_type;
+ GtkWindow *parent_window;
+ GFile *file;
+ gboolean completed;
+ int response;
+ GMutex mutex;
+ GCond cond;
+} AdminOpPermissionDialogData;
+
+static gboolean admin_vfs_mounted = FALSE;
+
+static GFile *
+get_as_admin_file (GFile *file)
+{
+ g_autofree gchar *uri_path = NULL;
+ g_autofree gchar *uri = NULL;
+ g_autofree char *admin_uri = NULL;
+ gboolean uri_op_success;
+
+ if (file == NULL)
+ {
+ return NULL;
+ }
+
+ uri = g_file_get_uri (file);
+ uri_op_success = g_uri_split (uri, G_URI_FLAGS_NONE,
+ NULL, NULL, NULL, NULL,
+ &uri_path,
+ NULL, NULL, NULL);
+
+ g_assert (uri_op_success);
+
+ admin_uri = g_strconcat ("admin://", uri_path, NULL);
+
+ return g_file_new_for_uri (admin_uri);
+}
+
+static void
+file_op_async_auto_admin_vfs_mount_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ /*This is only called in thread-default context of main (aka UI) thread*/
+
+ g_autoptr (GError) error = NULL;
+ AdminMountCbData *cb_data = user_data;
+ gboolean mount_success;
+
+ mount_success = g_file_mount_enclosing_volume_finish (G_FILE (source_object), res, &error);
+
+ if (mount_success || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED))
+ {
+ admin_vfs_mounted = TRUE;
+ mount_success = TRUE;
+ }
+
+ cb_data->mount_finished_cb (mount_success, cb_data->mount_finished_cb_data, error);
+
+ g_slice_free (AdminMountCbData, cb_data);
+}
+
+static void
+mount_admin_vfs_async (FileOpAutoAdminMountFinishedCallback mount_finished_cb,
+ gpointer mount_finished_cb_data)
+{
+ g_autoptr (GFile) admin_root = NULL;
+ AdminMountCbData *cb_data;
+
+ cb_data = g_slice_new0 (AdminMountCbData);
+ cb_data->mount_finished_cb = mount_finished_cb;
+ cb_data->mount_finished_cb_data = mount_finished_cb_data;
+
+ admin_root = g_file_new_for_uri ("admin:///");
+
+ g_file_mount_enclosing_volume (admin_root,
+ G_MOUNT_MOUNT_NONE,
+ NULL,
+ NULL,
+ file_op_async_auto_admin_vfs_mount_cb,
+ cb_data);
+}
+
+static void
+mount_admin_vfs_sync_finished (gboolean success,
+ gpointer callback_data,
+ GError *error)
+{
+ FileSubopAutoAdminMountData *data = callback_data;
+
+ g_mutex_lock (&data->mutex);
+
+ data->success = success;
+ data->completed = TRUE;
+
+ g_cond_signal (&data->cond);
+ g_mutex_unlock (&data->mutex);
+}
+
+static gboolean
+mount_admin_vfs_sync (void)
+{
+ FileSubopAutoAdminMountData data = { 0 };
+
+ data.completed = FALSE;
+ data.success = FALSE;
+
+ g_mutex_init (&data.mutex);
+ g_cond_init (&data.cond);
+
+ g_mutex_lock (&data.mutex);
+
+ mount_admin_vfs_async (mount_admin_vfs_sync_finished,
+ &data);
+
+ while (!data.completed)
+ {
+ g_cond_wait (&data.cond, &data.mutex);
+ }
+
+ g_mutex_unlock (&data.mutex);
+ g_mutex_clear (&data.mutex);
+ g_cond_clear (&data.cond);
+
+ return data.success;
+}
+
+static int
+do_admin_file_op_permission_dialog (GtkWindow *parent_window,
+ NautilusAdminFileOpPermissionDlgType dlg_type,
+ GFile *file)
+{
+ GtkWidget *dialog;
+ int response;
+ GtkWidget *button;
+
+ if (dlg_type == NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_COPY_TO_DIR ||
+ dlg_type == NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_MOVE_TO_DIR ||
+ dlg_type == NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_MOVE_FROM_DIR)
+ {
+ dialog = gtk_message_dialog_new (parent_window,
+ 0,
+ GTK_MESSAGE_OTHER,
+ GTK_BUTTONS_NONE,
+ NULL);
+
+ g_object_set (dialog,
+ "text", _("Destination directory access denied"),
+ "secondary-text", _("You'll need to provide administrator permissions to paste files into this directory."),
+ NULL);
+
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Cancel"),
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_CANCEL);
+ button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Continue"),
+ NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_YES);
+ gtk_style_context_add_class (gtk_widget_get_style_context (button),
+ "suggested-action");
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_CANCEL);
+ }
+ else
+ {
+ g_autofree gchar *secondary_text = NULL;
+ g_autofree gchar *basename = NULL;
+
+ basename = get_basename (file);
+
+ switch (dlg_type)
+ {
+ case NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_COPY_FILE:
+ {
+ secondary_text = g_strdup_printf (_("You'll need to provide administrator permissions to copy“%s”."), basename);
+ }
+ break;
+
+ case NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_MOVE_FILE:
+ {
+ secondary_text = g_strdup_printf (_("You'll need to provide administrator permissions to move “%s”."), basename);
+ }
+ break;
+
+ case NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_DELETE_FILE:
+ {
+ secondary_text = g_strdup_printf (_("You'll need to provide administrator permissions to permanently delete “%s”."), basename);
+ }
+ break;
+
+ default:
+ {
+ g_return_val_if_reached (NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_CANCEL);
+ }
+ }
+
+ dialog = gtk_message_dialog_new (parent_window,
+ 0,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ NULL);
+
+ g_object_set (dialog,
+ "text", _("Insufficient Access"),
+ "secondary-text", secondary_text,
+ NULL);
+
+ /*TODO: Confirm correct pnemonic keys */
+
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Cancel"), NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_CANCEL);
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("Skip _All"), NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_SKIP_ALL);
+ gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Skip"), NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_SKIP);
+ button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Continue"), NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_YES);
+ gtk_style_context_add_class (gtk_widget_get_style_context (button),
+ "suggested-action");
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_CANCEL);
+ }
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+
+ return response;
+}
+
+static gboolean
+do_admin_file_op_permission_dialog2 (gpointer callback_data)
+{
+ AdminOpPermissionDialogData *data = callback_data;
+ int response;
+
+ response = do_admin_file_op_permission_dialog (data->parent_window, data->dlg_type, data->file);
+
+ g_mutex_lock (&data->mutex);
+
+ data->completed = TRUE;
+ data->response = response;
+
+ g_cond_signal (&data->cond);
+ g_mutex_unlock (&data->mutex);
+
+ return FALSE;
+}
+
+static int
+ask_permission_for_admin_file_subop_in_main_thread (GtkWindow *parent_window,
+ NautilusAdminFileOpPermissionDlgType prompt_type,
+ GFile *file)
+{
+ AdminOpPermissionDialogData data = { 0 };
+
+ data.dlg_type = prompt_type;
+ data.parent_window = parent_window;
+ data.file = file;
+ data.completed = FALSE;
+
+ g_mutex_init (&data.mutex);
+ g_cond_init (&data.cond);
+
+ g_mutex_lock (&data.mutex);
+
+ g_main_context_invoke (NULL,
+ do_admin_file_op_permission_dialog2,
+ &data);
+
+ while (!data.completed)
+ {
+ g_cond_wait (&data.cond, &data.mutex);
+ }
+
+ g_mutex_unlock (&data.mutex);
+ g_mutex_clear (&data.mutex);
+ g_cond_clear (&data.cond);
+
+ return data.response;
+}
+
+static GFile *
+get_admin_file_with_permission (CommonJob *job,
+ GFile *file)
+{
+ if (!job->admin_permission_granted && !job->admin_permission_denied)
+ {
+ int dlg_user_response;
+ NautilusAdminFileOpPermissionDlgType prompt_type;
+
+ switch (job->kind)
+ {
+ case OP_KIND_COPY:
+ {
+ prompt_type = NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_COPY_FILE;
+ }
+ break;
+
+ case OP_KIND_MOVE:
+ {
+ prompt_type = NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_MOVE_FILE;
+ }
+ break;
+
+ case OP_KIND_DELETE:
+ {
+ prompt_type = NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_DELETE_FILE;
+ }
+ break;
+
+ default:
+ {
+ g_return_val_if_reached (NULL);
+ }
+ }
+
+ dlg_user_response = ask_permission_for_admin_file_subop_in_main_thread (
+ job->parent_window,
+ prompt_type,
+ file);
+
+ switch (dlg_user_response)
+ {
+ case NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_YES:
+ {
+ job->admin_permission_granted = TRUE;
+ }
+ break;
+
+ case NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_SKIP:
+ {
+ }
+ break;
+
+ case NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_SKIP_ALL:
+ {
+ job->admin_permission_denied = TRUE;
+ }
+ break;
+
+ case NAUTILUS_ADMIN_FILE_OP_PERMISSION_DLG_RESPONSE_CANCEL:
+ {
+ g_cancellable_cancel (job->cancellable);
+ }
+ break;
+
+ default:
+ {
+ g_assert_not_reached ();
+ }
+ }
+ }
+
+ if (!job->admin_permission_granted)
+ {
+ return NULL;
+ }
+
+ if (!admin_vfs_mounted)
+ {
+ if (!mount_admin_vfs_sync ())
+ {
+ return NULL;
+ }
+ }
+
+ return get_as_admin_file (file);
+}
+
+/* Tailored for use in do-while blocks wrapping a GIO call. */
+gboolean
+retry_with_admin_uri (GFile **try_file,
+ CommonJob *job,
+ GError **error)
+{
+ g_autoptr (GFile) admin_file = NULL;
+
+ g_return_val_if_fail (try_file != NULL && G_IS_FILE (*try_file), FALSE);
+
+ /* If an admin:// uri has already been tried, there is no need to try again.
+ * If it's not a permission error, or the file is not native, the admin
+ * backend is not going to help us. */
+ if (g_file_has_uri_scheme (*try_file, "admin") ||
+ !g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED) ||
+ !g_file_is_native (*try_file) ||
+ g_cancellable_is_cancelled (job->cancellable))
+ {
+ return FALSE;
+ }
+
+ admin_file = get_admin_file_with_permission (job, *try_file);
+ if (admin_file == NULL)
+ {
+ return FALSE;
+ }
+
+ /* Try again once with admin backend. */
+ g_set_object (try_file, admin_file);
+ g_clear_error (error);
+ return TRUE;
+}
diff --git a/src/nautilus-file-operations-admin.h b/src/nautilus-file-operations-admin.h
new file mode 100644
index 000000000..fe83edcb7
--- /dev/null
+++ b/src/nautilus-file-operations-admin.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <gio/gio.h>
+
+#include "nautilus-file-operations-private.h"
+
+gboolean retry_with_admin_uri (GFile **try_file,
+ CommonJob *job,
+ GError **error);
diff --git a/src/nautilus-file-operations-private.h b/src/nautilus-file-operations-private.h
index 141ef2aca..f5bfec628 100644
--- a/src/nautilus-file-operations-private.h
+++ b/src/nautilus-file-operations-private.h
@@ -36,6 +36,8 @@ typedef struct
gboolean merge_all;
gboolean replace_all;
gboolean delete_all;
+ gboolean admin_permission_granted;
+ gboolean admin_permission_denied;
} CommonJob;
typedef struct
@@ -134,3 +136,5 @@ typedef struct
NautilusCreateCallback done_callback;
gpointer done_callback_data;
} CompressJob;
+
+gchar *get_basename (GFile *file);
diff --git a/src/nautilus-file-operations.c b/src/nautilus-file-operations.c
index ac96b1142..98aac1f69 100644
--- a/src/nautilus-file-operations.c
+++ b/src/nautilus-file-operations.c
@@ -34,6 +34,7 @@
#include "nautilus-file-operations.h"
#include "nautilus-file-operations-private.h"
+#include "nautilus-file-operations-admin.h"
#include "nautilus-file-changes-queue.h"
#include "nautilus-lib-self-check-functions.h"
@@ -859,7 +860,7 @@ has_invalid_xml_char (char *str)
return FALSE;
}
-static gchar *
+gchar *
get_basename (GFile *file)
{
GFileInfo *info;
@@ -1780,19 +1781,27 @@ typedef void (*DeleteCallback) (GFile *file,
gpointer callback_data);
static gboolean
-delete_file_recursively (GFile *file,
+delete_file_recursively (CommonJob *job,
+ GFile *file,
GCancellable *cancellable,
DeleteCallback callback,
gpointer callback_data)
{
gboolean success;
+ g_autoptr (GFile) try_file = NULL;
g_autoptr (GError) error = NULL;
do
{
g_autoptr (GFileEnumerator) enumerator = NULL;
- success = g_file_delete (file, cancellable, &error);
+ g_set_object (&try_file, file);
+ do
+ {
+ success = g_file_delete (try_file, cancellable, &error);
+ }
+ while (!success && retry_with_admin_uri (&try_file, job, &error));
+
if (success ||
!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY))
{
@@ -1800,11 +1809,15 @@ delete_file_recursively (GFile *file,
}
g_clear_error (&error);
-
- enumerator = g_file_enumerate_children (file,
- G_FILE_ATTRIBUTE_STANDARD_NAME,
- G_FILE_QUERY_INFO_NONE,
- cancellable, &error);
+ g_set_object (&try_file, file);
+ do
+ {
+ enumerator = g_file_enumerate_children (try_file,
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ cancellable, &error);
+ }
+ while (enumerator == NULL && retry_with_admin_uri (&try_file, job, &error));
if (enumerator)
{
@@ -1822,13 +1835,19 @@ delete_file_recursively (GFile *file,
child = g_file_enumerator_get_child (enumerator, info);
- success = success && delete_file_recursively (child,
+ success = success && delete_file_recursively (job,
+ child,
cancellable,
callback,
callback_data);
g_object_unref (info);
+ if (job_aborted (job))
+ {
+ break;
+ }
+
info = g_file_enumerator_next_file (enumerator,
cancellable,
&error);
@@ -1992,7 +2011,8 @@ delete_files (CommonJob *job,
continue;
}
- success = delete_file_recursively (file, job->cancellable,
+ success = delete_file_recursively (job,
+ file, job->cancellable,
file_deleted_callback,
&data);
@@ -3202,6 +3222,7 @@ scan_dir (GFile *dir,
CommonJob *job,
GQueue *dirs)
{
+ g_autoptr (GFile) try_dir = NULL;
GFileInfo *info;
GError *error;
GFile *subdir;
@@ -3242,13 +3263,19 @@ retry:
}
error = NULL;
- enumerator = g_file_enumerate_children (dir,
- G_FILE_ATTRIBUTE_STANDARD_NAME ","
- G_FILE_ATTRIBUTE_STANDARD_TYPE ","
- G_FILE_ATTRIBUTE_STANDARD_SIZE,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- job->cancellable,
- &error);
+ g_set_object (&try_dir, dir);
+ do
+ {
+ enumerator = g_file_enumerate_children (try_dir,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ job->cancellable,
+ &error);
+ }
+ while (enumerator == NULL && retry_with_admin_uri (&try_dir, job, &error));
+
if (enumerator)
{
error = NULL;
@@ -3330,6 +3357,11 @@ retry:
skip_file (job, dir);
skip_subdirs = TRUE;
}
+ else if (job_aborted (job))
+ {
+ g_error_free (error);
+ skip_subdirs = TRUE;
+ }
else if (IS_IO_ERROR (error, CANCELLED))
{
g_error_free (error);
@@ -4534,6 +4566,7 @@ create_dest_dir (CommonJob *job,
gboolean same_fs,
char **dest_fs_type)
{
+ g_autoptr (GFile) try_dest = NULL;
GError *error;
GFile *new_dest, *dest_dir;
char *primary, *secondary, *details;
@@ -4548,7 +4581,12 @@ retry:
* copying the attributes, because we need to be sure we can write to it */
error = NULL;
- res = g_file_make_directory (*dest, job->cancellable, &error);
+ g_set_object (&try_dest, *dest);
+ do
+ {
+ res = g_file_make_directory (try_dest, job->cancellable, &error);
+ }
+ while (!res && retry_with_admin_uri (&try_dest, job, &error));
if (res)
{
@@ -4570,7 +4608,7 @@ retry:
{
g_autofree gchar *basename = NULL;
- if (IS_IO_ERROR (error, CANCELLED))
+ if (IS_IO_ERROR (error, CANCELLED) || job_aborted (job))
{
g_error_free (error);
return CREATE_DEST_DIR_FAILED;
@@ -4681,6 +4719,7 @@ copy_move_directory (CopyMoveJob *copy_job,
gboolean readonly_source_fs)
{
g_autoptr (GFileInfo) src_info = NULL;
+ g_autoptr (GFile) try_src = NULL;
GFileInfo *info;
GError *error;
GFile *src_file;
@@ -4753,11 +4792,17 @@ copy_move_directory (CopyMoveJob *copy_job,
skip_error = should_skip_readdir_error (job, src);
retry:
error = NULL;
- enumerator = g_file_enumerate_children (src,
- G_FILE_ATTRIBUTE_STANDARD_NAME,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- job->cancellable,
- &error);
+ g_set_object (&try_src, src);
+ do
+ {
+ enumerator = g_file_enumerate_children (try_src,
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ job->cancellable,
+ &error);
+ }
+ while (enumerator == NULL && retry_with_admin_uri (&try_src, job, &error));
+
if (enumerator)
{
error = NULL;
@@ -4864,7 +4909,7 @@ retry:
g_hash_table_replace (debuting_files, g_object_ref (*dest), GINT_TO_POINTER (create_dest));
}
}
- else if (IS_IO_ERROR (error, CANCELLED))
+ else if (IS_IO_ERROR (error, CANCELLED) || job_aborted (job))
{
g_error_free (error);
}
@@ -5199,6 +5244,8 @@ copy_move_file (CopyMoveJob *copy_job,
gboolean *skipped_file,
gboolean readonly_source_fs)
{
+ g_autoptr (GFile) try_src = NULL;
+ g_autoptr (GFile) try_dest = NULL;
GFile *dest, *new_dest;
g_autofree gchar *dest_uri = NULL;
GError *error;
@@ -5356,23 +5403,35 @@ retry:
pdata.source_info = source_info;
pdata.transfer_info = transfer_info;
+ g_set_object (&try_src, src);
+ g_set_object (&try_dest, dest);
if (copy_job->is_move)
{
- res = g_file_move (src, dest,
- flags,
- job->cancellable,
- copy_file_progress_callback,
- &pdata,
- &error);
+ do
+ {
+ res = g_file_move (try_src, try_dest,
+ flags,
+ job->cancellable,
+ copy_file_progress_callback,
+ &pdata,
+ &error);
+ }
+ while (!res && (retry_with_admin_uri (&try_src, job, &error) ||
+ retry_with_admin_uri (&try_dest, job, &error)));
}
else
{
- res = g_file_copy (src, dest,
- flags,
- job->cancellable,
- copy_file_progress_callback,
- &pdata,
- &error);
+ do
+ {
+ res = g_file_copy (try_src, try_dest,
+ flags,
+ job->cancellable,
+ copy_file_progress_callback,
+ &pdata,
+ &error);
+ }
+ while (!res && (retry_with_admin_uri (&try_src, job, &error) ||
+ retry_with_admin_uri (&try_dest, job, &error)));
}
if (res)
@@ -5650,7 +5709,7 @@ retry:
g_object_unref (dest);
return;
}
- else if (IS_IO_ERROR (error, CANCELLED))
+ else if (IS_IO_ERROR (error, CANCELLED) || job_aborted (job))
{
g_error_free (error);
}
@@ -6051,6 +6110,8 @@ move_file_prepare (CopyMoveJob *move_job,
GList **fallback_files,
int files_left)
{
+ g_autoptr (GFile) try_src = NULL;
+ g_autoptr (GFile) try_dest = NULL;
GFile *dest, *new_dest;
g_autofree gchar *dest_uri = NULL;
GError *error;
@@ -6060,6 +6121,7 @@ move_file_prepare (CopyMoveJob *move_job,
GFileCopyFlags flags;
MoveFileCopyFallback *fallback;
gboolean handled_invalid_filename;
+ gboolean res;
overwrite = FALSE;
handled_invalid_filename = *dest_fs_type != NULL;
@@ -6134,12 +6196,21 @@ retry:
}
error = NULL;
- if (g_file_move (src, dest,
- flags,
- job->cancellable,
- NULL,
- NULL,
- &error))
+ g_set_object (&try_src, src);
+ g_set_object (&try_dest, dest);
+ do
+ {
+ res = g_file_move (try_src, try_dest,
+ flags,
+ job->cancellable,
+ NULL,
+ NULL,
+ &error);
+ }
+ while (!res && (retry_with_admin_uri (&try_src, job, &error) ||
+ retry_with_admin_uri (&try_dest, job, &error)));
+
+ if (res)
{
if (debuting_files)
{
@@ -6275,7 +6346,7 @@ retry:
overwrite);
*fallback_files = g_list_prepend (*fallback_files, fallback);
}
- else if (IS_IO_ERROR (error, CANCELLED))
+ else if (IS_IO_ERROR (error, CANCELLED) || job_aborted (job))
{
g_error_free (error);
}
@@ -8166,7 +8237,7 @@ extract_job_on_error (AutoarExtractor *extractor,
if (extract_job->destination_decided)
{
destination = extract_job->output_files->data;
- delete_file_recursively (destination, NULL, NULL, NULL);
+ delete_file_recursively ((CommonJob *) extract_job, destination, NULL, NULL, NULL);
extract_job->output_files = g_list_delete_link (extract_job->output_files,
extract_job->output_files);
g_object_unref (destination);
diff --git a/src/nautilus-file.c b/src/nautilus-file.c
index d0a9a8d03..74ccdd901 100644
--- a/src/nautilus-file.c
+++ b/src/nautilus-file.c
@@ -4763,6 +4763,29 @@ nautilus_file_get_filesystem_remote (NautilusFile *file)
return FALSE;
}
+gboolean
+nautilus_file_is_filesystem_readonly (NautilusFile *file)
+{
+ g_assert (NAUTILUS_IS_FILE (file));
+
+ if (nautilus_file_is_directory (file))
+ {
+ return file->details->filesystem_readonly;
+ }
+ else
+ {
+ g_autoptr (NautilusFile) parent = NULL;
+
+ parent = nautilus_file_get_parent (file);
+ if (parent != NULL)
+ {
+ return parent->details->filesystem_readonly;
+ }
+ }
+
+ return FALSE;
+}
+
static gboolean
get_speed_tradeoff_preference_for_file (NautilusFile *file,
NautilusSpeedTradeoffValue value)
diff --git a/src/nautilus-file.h b/src/nautilus-file.h
index 6946be5f8..cf522bfa6 100644
--- a/src/nautilus-file.h
+++ b/src/nautilus-file.h
@@ -260,6 +260,8 @@ char * nautilus_file_get_filesystem_type (Nautilu
gboolean nautilus_file_get_filesystem_remote (NautilusFile *file);
+gboolean nautilus_file_is_filesystem_readonly (NautilusFile *file);
+
NautilusFile * nautilus_file_get_trash_original_file (NautilusFile *file);
/* Permissions. */
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c
index 9ca3adef2..c01ebd3bc 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -7227,7 +7227,7 @@ static gboolean
can_paste_into_file (NautilusFile *file)
{
if (nautilus_file_is_directory (file) &&
- nautilus_file_can_write (file))
+ !nautilus_file_is_filesystem_readonly (file))
{
return TRUE;
}
@@ -7246,7 +7246,7 @@ can_paste_into_file (NautilusFile *file)
* case as can-write */
res = (nautilus_file_get_file_type (activation_file) == G_FILE_TYPE_UNKNOWN) ||
(nautilus_file_get_file_type (activation_file) == G_FILE_TYPE_DIRECTORY &&
- nautilus_file_can_write (activation_file));
+ !nautilus_file_is_filesystem_readonly (activation_file));
nautilus_file_unref (activation_file);
@@ -7475,10 +7475,23 @@ can_delete_all (GList *files)
for (l = files; l != NULL; l = l->next)
{
file = l->data;
- if (!nautilus_file_can_delete (file))
+
+ if (nautilus_file_is_filesystem_readonly (file))
{
return FALSE;
}
+
+ if (!nautilus_file_can_delete (file))
+ {
+ g_autoptr (GFile) location = NULL;
+
+ location = nautilus_file_get_location (file);
+
+ if (!g_file_is_native (location))
+ {
+ return FALSE;
+ }
+ }
}
return TRUE;
}
@@ -7617,7 +7630,7 @@ real_update_actions_state (NautilusFilesView *view)
selection_contains_starred = showing_starred_directory (view);
selection_contains_search = nautilus_view_is_searching (NAUTILUS_VIEW (view));
selection_is_read_only = selection_count == 1 &&
- (!nautilus_file_can_write (NAUTILUS_FILE (selection->data)) &&
+ (nautilus_file_is_filesystem_readonly (NAUTILUS_FILE (selection->data)) &&
!nautilus_file_has_activation_uri (NAUTILUS_FILE (selection->data)));
selection_all_in_trash = all_in_trash (selection);
zoom_level_is_default = nautilus_files_view_is_zoom_level_default (view);
@@ -9006,7 +9019,21 @@ nautilus_files_view_is_read_only (NautilusFilesView *view)
file = nautilus_files_view_get_directory_as_file (view);
if (file != NULL)
{
- return !nautilus_file_can_write (file);
+ g_autoptr (GFile) location = NULL;
+
+ if (nautilus_file_can_write (file))
+ {
+ return FALSE;
+ }
+
+ if (nautilus_file_is_filesystem_readonly (file))
+ {
+ return TRUE;
+ }
+
+ location = nautilus_file_get_location (file);
+
+ return !g_file_is_native (location);
}
return FALSE;
}