[nautilus/wip/antoniof/try-admin-backend-for-file-ops: 11/11] [Commit message is WIP]




commit d88c2117d11c3bbca50e081c7e62131b9b9c2e6f
Author: Sachin Daluja <30343-sachindaluja users noreply gitlab gnome org>
Date:   Tue Jun 15 23:02:37 2021 -0400

    [Commit message is WIP]
    
    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

 src/meson.build                        |   2 +
 src/nautilus-file-operations-admin.c   | 446 +++++++++++++++++++++++++++++++++
 src/nautilus-file-operations-admin.h   |   9 +
 src/nautilus-file-operations-private.h |   4 +
 src/nautilus-file-operations.c         | 161 ++++++++----
 src/nautilus-file.c                    |  23 ++
 src/nautilus-file.h                    |   2 +
 src/nautilus-files-view.c              |  37 ++-
 8 files changed, 634 insertions(+), 50 deletions(-)
---
diff --git a/src/meson.build b/src/meson.build
index 360832ac9..8c96c049d 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 7b34fd041..e4b9aca7d 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
@@ -131,3 +133,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 d9f43587e..3ed8693ec 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"
@@ -861,7 +862,7 @@ has_invalid_xml_char (char *str)
     return FALSE;
 }
 
-static gchar *
+gchar *
 get_basename (GFile *file)
 {
     GFileInfo *info;
@@ -1782,19 +1783,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))
         {
@@ -1802,11 +1811,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)
         {
@@ -1824,13 +1837,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);
@@ -1994,7 +2013,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);
 
@@ -3225,6 +3245,7 @@ scan_dir (GFile      *dir,
           CommonJob  *job,
           GQueue     *dirs)
 {
+    g_autoptr (GFile) try_dir = NULL;
     GFileInfo *info;
     GError *error;
     GFile *subdir;
@@ -3265,13 +3286,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;
@@ -3353,6 +3380,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);
@@ -4557,6 +4589,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;
@@ -4571,7 +4604,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)
     {
@@ -4593,7 +4631,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;
@@ -4704,6 +4742,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;
@@ -4776,11 +4815,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;
@@ -4887,7 +4932,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);
     }
@@ -5222,6 +5267,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;
@@ -5379,23 +5426,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)
@@ -5673,7 +5732,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);
     }
@@ -6074,6 +6133,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;
@@ -6083,6 +6144,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;
@@ -6157,12 +6219,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)
         {
@@ -6298,7 +6369,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);
     }
diff --git a/src/nautilus-file.c b/src/nautilus-file.c
index c14356d8f..078be3c8f 100644
--- a/src/nautilus-file.c
+++ b/src/nautilus-file.c
@@ -4750,6 +4750,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 7aa0e60be..e866ac08c 100644
--- a/src/nautilus-files-view.c
+++ b/src/nautilus-files-view.c
@@ -7289,7 +7289,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;
     }
@@ -7308,7 +7308,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);
 
@@ -7537,10 +7537,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;
 }
@@ -7679,7 +7692,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);
@@ -9051,7 +9064,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;
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]