diff options
-rw-r--r-- | src/meson.build | 2 | ||||
-rw-r--r-- | src/nautilus-file-operations-admin.c | 446 | ||||
-rw-r--r-- | src/nautilus-file-operations-admin.h | 9 | ||||
-rw-r--r-- | src/nautilus-file-operations-private.h | 4 | ||||
-rw-r--r-- | src/nautilus-file-operations.c | 163 | ||||
-rw-r--r-- | src/nautilus-file.c | 23 | ||||
-rw-r--r-- | src/nautilus-file.h | 2 | ||||
-rw-r--r-- | src/nautilus-files-view.c | 37 |
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; } |