diff options
-rw-r--r-- | src/nautilus-file-operations.c | 563 |
1 files changed, 223 insertions, 340 deletions
diff --git a/src/nautilus-file-operations.c b/src/nautilus-file-operations.c index 8ec8f064d..26d07044b 100644 --- a/src/nautilus-file-operations.c +++ b/src/nautilus-file-operations.c @@ -1604,229 +1604,150 @@ report_delete_progress (CommonJob *job, } } -static void delete_file (CommonJob *job, GFile *file, - gboolean *skipped_file, - SourceInfo *source_info, - TransferInfo *transfer_info, - gboolean toplevel); +typedef void (*DeleteCallback) (GFile *file, + GError *error, + gpointer callback_data); -static void -delete_dir (CommonJob *job, GFile *dir, - gboolean *skipped_file, - SourceInfo *source_info, - TransferInfo *transfer_info, - gboolean toplevel) +static gboolean +delete_file_recursively (GFile *file, + GCancellable *cancellable, + DeleteCallback callback, + gpointer callback_data) { - GFileInfo *info; - GError *error; - GFile *file; - GFileEnumerator *enumerator; - char *primary, *secondary, *details; - int response; - gboolean skip_error; - gboolean local_skipped_file; + gboolean success; + g_autoptr (GError) error = NULL; - local_skipped_file = FALSE; - - skip_error = should_skip_readdir_error (job, dir); - retry: - error = NULL; - enumerator = g_file_enumerate_children (dir, - G_FILE_ATTRIBUTE_STANDARD_NAME, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - job->cancellable, - &error); - if (enumerator) { - error = NULL; - - while (!job_aborted (job) && - (info = g_file_enumerator_next_file (enumerator, job->cancellable, skip_error?NULL:&error)) != NULL) { - file = g_file_get_child (dir, - g_file_info_get_name (info)); - delete_file (job, file, &local_skipped_file, source_info, transfer_info, FALSE); - g_object_unref (file); - g_object_unref (info); - } - g_file_enumerator_close (enumerator, job->cancellable, NULL); - g_object_unref (enumerator); - - if (error && IS_IO_ERROR (error, CANCELLED)) { - g_error_free (error); - } else if (error) { - primary = f (_("Error while deleting.")); - details = NULL; - - if (IS_IO_ERROR (error, PERMISSION_DENIED)) { - secondary = f (_("Files in the folder “%B” cannot be deleted because you do " - "not have permissions to see them."), dir); - } else { - secondary = f (_("There was an error getting information about the files in the folder “%B”."), dir); - details = error->message; - } - - response = run_warning (job, - primary, - secondary, - details, - FALSE, - CANCEL, _("_Skip files"), - NULL); - - g_error_free (error); - - if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) { - abort_job (job); - } else if (response == 1) { - /* Skip: Do Nothing */ - local_skipped_file = TRUE; - } else { - g_assert_not_reached (); - } - } - - } else if (IS_IO_ERROR (error, CANCELLED)) { - g_error_free (error); - } else { - primary = f (_("Error while deleting.")); - details = NULL; - if (IS_IO_ERROR (error, PERMISSION_DENIED)) { - secondary = f (_("The folder “%B” cannot be deleted because you do not have " - "permissions to read it."), dir); - } else { - secondary = f (_("There was an error reading the folder “%B”."), dir); - details = error->message; - } - - response = run_warning (job, - primary, - secondary, - details, - FALSE, - CANCEL, SKIP, RETRY, - NULL); + do { + g_autoptr (GFileEnumerator) enumerator = NULL; - g_error_free (error); + success = g_file_delete (file, cancellable, &error); + if (success || + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY)) { + break; + } - if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) { - abort_job (job); - } else if (response == 1) { - /* Skip: Do Nothing */ - local_skipped_file = TRUE; - } else if (response == 2) { - goto retry; - } else { - g_assert_not_reached (); - } - } + g_clear_error (&error); - if (!job_aborted (job) && - /* Don't delete dir if there was a skipped file */ - !local_skipped_file) { - if (!g_file_delete (dir, job->cancellable, &error)) { - if (job->skip_all_error) { - goto skip; - } - primary = f (_("Error while deleting.")); - secondary = f (_("Could not remove the folder %B."), dir); - details = error->message; + enumerator = g_file_enumerate_children (file, + G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, + cancellable, &error); - response = run_cancel_or_skip_warning (job, - primary, - secondary, - details, - source_info->num_files, - source_info->num_files - transfer_info->num_files); - - if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) { - abort_job (job); - } else if (response == 1) { /* skip all */ - job->skip_all_error = TRUE; - local_skipped_file = TRUE; - } else if (response == 2) { /* skip */ - local_skipped_file = TRUE; - } else { - g_assert_not_reached (); - } - - skip: - g_error_free (error); - } else { - nautilus_file_changes_queue_file_removed (dir); - transfer_info->num_files ++; - report_delete_progress (job, source_info, transfer_info); - return; - } - } + if (enumerator) { + GFileInfo *info; - if (local_skipped_file) { - *skipped_file = TRUE; - } + success = TRUE; + + info = g_file_enumerator_next_file (enumerator, + cancellable, + &error); + + while (info != NULL) { + g_autoptr (GFile) child; + + child = g_file_enumerator_get_child (enumerator, info); + + success = success && delete_file_recursively (child, + cancellable, + callback, + callback_data); + + g_object_unref (info); + + info = g_file_enumerator_next_file (enumerator, + cancellable, + &error); + } + } + + if (error != NULL) { + success = FALSE; + } + } while (success); + + if (callback) { + callback (file, error, callback_data); + } + + return success; } +typedef struct { + CommonJob *job; + SourceInfo *source_info; + TransferInfo *transfer_info; +} DeleteData; + static void -delete_file (CommonJob *job, GFile *file, - gboolean *skipped_file, - SourceInfo *source_info, - TransferInfo *transfer_info, - gboolean toplevel) -{ - GError *error; - char *primary, *secondary, *details; - int response; +file_deleted_callback (GFile *file, + GError *error, + gpointer callback_data) +{ + DeleteData *data = callback_data; + CommonJob *job; + SourceInfo *source_info; + TransferInfo *transfer_info; + GFileType file_type; + char *primary; + char *secondary; + char *details = NULL; + int response; + + job = data->job; + source_info = data->source_info; + transfer_info = data->transfer_info; + + data->transfer_info->num_files++; + + if (error == NULL) { + nautilus_file_changes_queue_file_removed (file); + report_delete_progress (data->job, data->source_info, data->transfer_info); - if (should_skip_file (job, file)) { - *skipped_file = TRUE; - return; - } - - error = NULL; - if (g_file_delete (file, job->cancellable, &error)) { - nautilus_file_changes_queue_file_removed (file); - transfer_info->num_files ++; - report_delete_progress (job, source_info, transfer_info); - return; - } + return; + } - if (IS_IO_ERROR (error, NOT_EMPTY)) { - g_error_free (error); - delete_dir (job, file, - skipped_file, - source_info, transfer_info, - toplevel); - return; - - } else if (IS_IO_ERROR (error, CANCELLED)) { - g_error_free (error); - - } else { - if (job->skip_all_error) { - goto skip; - } - primary = f (_("Error while deleting.")); - secondary = f (_("There was an error deleting %B."), file); - details = error->message; - - response = run_cancel_or_skip_warning (job, - primary, - secondary, - details, - source_info->num_files, - source_info->num_files - transfer_info->num_files); + if (job_aborted (job) || + job->skip_all_error || + should_skip_file (job, file) || + should_skip_readdir_error (job, file)) { + return; + } - if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) { - abort_job (job); - } else if (response == 1) { /* skip all */ - job->skip_all_error = TRUE; - } else if (response == 2) { /* skip */ - /* do nothing */ - } else { - g_assert_not_reached (); - } - skip: - g_error_free (error); - } - - *skipped_file = TRUE; + primary = f (_("Error while deleting.")); + + file_type = g_file_query_file_type (file, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + job->cancellable); + + if (file_type == G_FILE_TYPE_DIRECTORY) { + secondary = IS_IO_ERROR (error, PERMISSION_DENIED) ? + f (_("There was an error deleting the folder “%B”."), + file) : + f (_("You do not have sufficient permissions to delete the folder “%B”."), + file); + } else { + secondary = IS_IO_ERROR (error, PERMISSION_DENIED) ? + f (_("There was an error deleting the file “%B”."), + file) : + f (_("You do not have sufficient permissions to delete the file “%B”."), + file); + } + + details = error->message; + + response = run_cancel_or_skip_warning (job, + primary, + secondary, + details, + source_info->num_files, + source_info->num_files - transfer_info->num_files); + + if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) { + abort_job (job); + } else if (response == 1) { + /* skip all */ + job->skip_all_error = TRUE; + } } static void @@ -1836,7 +1757,7 @@ delete_files (CommonJob *job, GList *files, int *files_skipped) GFile *file; SourceInfo source_info; TransferInfo transfer_info; - gboolean skipped_file; + DeleteData data; if (job_aborted (job)) { return; @@ -1854,20 +1775,30 @@ delete_files (CommonJob *job, GList *files, int *files_skipped) memset (&transfer_info, 0, sizeof (transfer_info)); report_delete_progress (job, &source_info, &transfer_info); + + data.job = job; + data.source_info = &source_info; + data.transfer_info = &transfer_info; for (l = files; l != NULL && !job_aborted (job); l = l->next) { + gboolean success; + file = l->data; - skipped_file = FALSE; - delete_file (job, file, - &skipped_file, - &source_info, &transfer_info, - TRUE); - if (skipped_file) { - (*files_skipped)++; - } + if (should_skip_file (job, file)) { + (*files_skipped)++; + continue; + } + + success = delete_file_recursively (file, job->cancellable, + file_deleted_callback, + &data); + + if (!success) { + (*files_skipped)++; + } } } @@ -4138,131 +4069,72 @@ copy_move_directory (CopyMoveJob *copy_job, return TRUE; } -static gboolean -remove_target_recursively (CommonJob *job, - GFile *src, - GFile *toplevel_dest, - GFile *file) + +typedef struct { + CommonJob *job; + GFile *source; +} DeleteExistingFileData; + +static void +existing_file_removed_callback (GFile *file, + GError *error, + gpointer callback_data) { - GFileEnumerator *enumerator; - GError *error; - GFile *child; - gboolean stop; - char *primary, *secondary, *details; - int response; - GFileInfo *info; + DeleteExistingFileData *data = callback_data; + CommonJob *job; + GFile *source; + GFileType file_type; + char *primary; + char *secondary; + char *details = NULL; + int response; - stop = FALSE; - - error = NULL; - enumerator = g_file_enumerate_children (file, - G_FILE_ATTRIBUTE_STANDARD_NAME, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - job->cancellable, - &error); - if (enumerator) { - error = NULL; - - while (!job_aborted (job) && - (info = g_file_enumerator_next_file (enumerator, job->cancellable, &error)) != NULL) { - child = g_file_get_child (file, - g_file_info_get_name (info)); - if (!remove_target_recursively (job, src, toplevel_dest, child)) { - stop = TRUE; - break; - } - g_object_unref (child); - g_object_unref (info); - } - g_file_enumerator_close (enumerator, job->cancellable, NULL); - g_object_unref (enumerator); - - } else if (IS_IO_ERROR (error, NOT_DIRECTORY)) { - /* Not a dir, continue */ - g_error_free (error); - - } else if (IS_IO_ERROR (error, CANCELLED)) { - g_error_free (error); - } else { - if (job->skip_all_error) { - goto skip1; - } - - primary = f (_("Error while copying “%B”."), src); - secondary = f (_("Could not remove files from the already existing folder %F."), file); - details = error->message; + job = data->job; + source = data->source; - /* set show_all to TRUE here, as we don't know how many - * files we'll end up processing yet. - */ - response = run_warning (job, - primary, - secondary, - details, - TRUE, - CANCEL, SKIP_ALL, SKIP, - NULL); - - if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) { - abort_job (job); - } else if (response == 1) { /* skip all */ - job->skip_all_error = TRUE; - } else if (response == 2) { /* skip */ - /* do nothing */ - } else { - g_assert_not_reached (); - } - skip1: - g_error_free (error); - - stop = TRUE; - } + if (error == NULL) { + nautilus_file_changes_queue_file_removed (file); - if (stop) { - return FALSE; - } + return; + } - error = NULL; - - if (!g_file_delete (file, job->cancellable, &error)) { - if (job->skip_all_error || - IS_IO_ERROR (error, CANCELLED)) { - goto skip2; - } - primary = f (_("Error while copying “%B”."), src); - secondary = f (_("Could not remove the already existing file %F."), file); - details = error->message; + if (job_aborted (job) || job->skip_all_error) { + return; + } - /* set show_all to TRUE here, as we don't know how many - * files we'll end up processing yet. - */ - response = run_warning (job, - primary, - secondary, - details, - TRUE, - CANCEL, SKIP_ALL, SKIP, - NULL); - - if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) { - abort_job (job); - } else if (response == 1) { /* skip all */ - job->skip_all_error = TRUE; - } else if (response == 2) { /* skip */ - /* do nothing */ - } else { - g_assert_not_reached (); - } + primary = f (_("Error while copying “%B”."), source); - skip2: - g_error_free (error); - - return FALSE; - } - nautilus_file_changes_queue_file_removed (file); - - return TRUE; - + file_type = g_file_query_file_type (file, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + job->cancellable); + + if (file_type == G_FILE_TYPE_DIRECTORY) { + secondary = f (_("Could not remove the already existing folder %F."), + file); + } else { + secondary = f (_("Could not remove the already existing file %F."), + file); + } + + details = error->message; + + /* set show_all to TRUE here, as we don't know how many + * files we'll end up processing yet. + */ + response = run_warning (job, + primary, + secondary, + details, + TRUE, + CANCEL, SKIP_ALL, SKIP, + NULL); + + if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) { + abort_job (job); + } else if (response == 1) { + /* skip all */ + job->skip_all_error = TRUE; + } } typedef struct { @@ -4806,12 +4678,23 @@ copy_move_file (CopyMoveJob *copy_job, else if (overwrite && IS_IO_ERROR (error, IS_DIRECTORY)) { + gboolean existing_file_deleted; + DeleteExistingFileData data; g_error_free (error); - - if (remove_target_recursively (job, src, dest, dest)) { - goto retry; - } + + data.job = job; + data.source = src; + + existing_file_deleted = + delete_file_recursively (dest, + job->cancellable, + existing_file_removed_callback, + &data); + + if (existing_file_deleted) { + goto retry; + } } /* Needs to recurse */ |