From fe0934fba84f8cfedc636ea4970da3d6c4ce85ba Mon Sep 17 00:00:00 2001 From: Razvan Chitu Date: Sun, 17 Jul 2016 21:21:04 +0300 Subject: file-operations: refactor deleting operation Both the delete and copy operations in Nautilus deal with deleting files recursively. Each operation has its own implementation of the same functionality, mixed with specific logic, like error reporting through dialogs. In order to remove duplicated code, extract delete logic to a separate function which can be used by both operations. Implement a function for deleting files recursively which reports errors and success through an user provided callback. https://bugzilla.gnome.org/show_bug.cgi?id=770109 --- src/nautilus-file-operations.c | 563 ++++++++++++++++------------------------- 1 file 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 */ -- cgit v1.2.1