From 33c21fc3647af32de5a9c9d49e33765a526910f5 Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Wed, 12 May 2010 11:54:29 +0000 Subject: Bring back support for file caching when gvfs-fuse-daemon is not available The commit 73fa5d77a60861cd35c6f3425d27c88061af081c introduced trouble when user was trying to work with archives on remote GIO mounts and not having the fuse daemon running. This led to segfaults and error messages. Please see bug #617769 for details. --- diff --git a/src/fr-archive.c b/src/fr-archive.c index e4a1259..adf9976 100644 --- a/src/fr-archive.c +++ b/src/fr-archive.c @@ -114,16 +114,51 @@ struct _FrArchivePrivData { FakeLoadFunc add_is_stoppable_func; /* Returns whether the add operation is * stoppable. */ gpointer add_is_stoppable_data; - GCancellable *cancellable; + GCancellable *cancellable; char *temp_dir; gboolean continue_adding_dropped_items; DroppedItemsData *dropped_items_data; + char *temp_extraction_dir; char *extraction_destination; + gboolean remote_extraction; gboolean extract_here; }; +typedef struct { + FrArchive *archive; + char *uri; + FrAction action; + GList *file_list; + char *base_uri; + char *dest_dir; + gboolean update; + char *tmp_dir; + guint source_id; + char *password; + gboolean encrypt_header; + FrCompression compression; + guint volume_size; +} XferData; + + +static void +xfer_data_free (XferData *data) +{ + if (data == NULL) + return; + + g_free (data->uri); + g_free (data->password); + path_list_free (data->file_list); + g_free (data->base_uri); + g_free (data->dest_dir); + g_free (data->tmp_dir); + g_free (data); +} + + #define MAX_CHUNK_LEN (NCARGS * 2 / 3) /* Max command line length */ #define UNKNOWN_TYPE "application/octet-stream" #define SAME_FS (FALSE) @@ -322,6 +357,8 @@ static void fr_archive_init (FrArchive *archive) { archive->file = NULL; + archive->local_copy = NULL; + archive->is_remote = FALSE; archive->command = NULL; archive->is_compressed_file = FALSE; archive->can_create_compressed_file = FALSE; @@ -333,6 +370,7 @@ fr_archive_init (FrArchive *archive) archive->priv->add_is_stoppable_data = NULL; archive->priv->extraction_destination = NULL; + archive->priv->temp_extraction_dir = NULL; archive->priv->cancellable = g_cancellable_new (); archive->process = fr_process_new (); @@ -350,18 +388,73 @@ fr_archive_new (void) } +static GFile * +get_local_copy_for_file (GFile *remote_file) +{ + char *temp_dir; + GFile *local_copy = NULL; + + temp_dir = get_temp_work_dir (NULL); + if (temp_dir != NULL) { + char *archive_name; + char *local_path; + + archive_name = g_file_get_basename (remote_file); + local_path = g_build_filename (temp_dir, archive_name, NULL); + local_copy = g_file_new_for_path (local_path); + + g_free (local_path); + g_free (archive_name); + } + g_free (temp_dir); + + return local_copy; +} + + static void fr_archive_set_uri (FrArchive *archive, const char *uri) { + if ((archive->local_copy != NULL) && archive->is_remote) { + GFile *temp_folder; + GError *err = NULL; + + g_file_delete (archive->local_copy, NULL, &err); + if (err != NULL) { + g_warning ("Failed to delete the local copy: %s", err->message); + g_clear_error (&err); + } + + temp_folder = g_file_get_parent (archive->local_copy); + g_file_delete (temp_folder, NULL, &err); + if (err != NULL) { + g_warning ("Failed to delete temp folder: %s", err->message); + g_clear_error (&err); + } + + g_object_unref (temp_folder); + } + if (archive->file != NULL) { g_object_unref (archive->file); archive->file = NULL; } + if (archive->local_copy != NULL) { + g_object_unref (archive->local_copy); + archive->local_copy = NULL; + } archive->content_type = NULL; - if (uri != NULL) - archive->file = g_file_new_for_uri (uri); + if (uri == NULL) + return; + + archive->file = g_file_new_for_uri (uri); + archive->is_remote = ! g_file_has_uri_scheme (archive->file, "file"); + if (archive->is_remote) + archive->local_copy = get_local_copy_for_file (archive->file); + else + archive->local_copy = g_file_dup (archive->file); } @@ -395,6 +488,7 @@ fr_archive_finalize (GObject *object) dropped_items_data_free (archive->priv->dropped_items_data); archive->priv->dropped_items_data = NULL; } + g_free (archive->priv->temp_extraction_dir); g_free (archive->priv->extraction_destination); g_free (archive->priv); @@ -501,7 +595,7 @@ create_command_from_type (FrArchive *archive, if (command_type == 0) return FALSE; - filename = g_file_get_path (archive->file); + filename = g_file_get_path (archive->local_copy); archive->command = FR_COMMAND (g_object_new (command_type, "process", archive->process, "filename", filename, @@ -588,6 +682,79 @@ action_started (FrCommand *command, } +/* -- copy_to_remote_location -- */ + + +static void +fr_archive_copy_done (FrArchive *archive, + FrAction action, + GError *error) +{ + FrProcErrorType error_type = FR_PROC_ERROR_NONE; + const char *error_details = NULL; + + if (error != NULL) { + error_type = (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ? FR_PROC_ERROR_STOPPED : FR_PROC_ERROR_GENERIC); + error_details = error->message; + } + fr_archive_action_completed (archive, action, error_type, error_details); +} + + +static void +copy_to_remote_location_done (GError *error, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + fr_archive_copy_done (xfer_data->archive, xfer_data->action, error); + xfer_data_free (xfer_data); +} + + +static void +copy_to_remote_location_progress (goffset current_file, + goffset total_files, + GFile *source, + GFile *destination, + goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + g_signal_emit (G_OBJECT (xfer_data->archive), + fr_archive_signals[PROGRESS], + 0, + (double) current_num_bytes / total_num_bytes); +} + + +static void +copy_to_remote_location (FrArchive *archive, + FrAction action) +{ + XferData *xfer_data; + + xfer_data = g_new0 (XferData, 1); + xfer_data->archive = archive; + xfer_data->action = action; + + g_copy_file_async (archive->local_copy, + archive->file, + G_FILE_COPY_OVERWRITE, + G_PRIORITY_DEFAULT, + archive->priv->cancellable, + copy_to_remote_location_progress, + xfer_data, + copy_to_remote_location_done, + xfer_data); +} + + +/* -- copy_extracted_files_to_destination -- */ + + static void move_here (FrArchive *archive) { @@ -656,6 +823,61 @@ move_here (FrArchive *archive) } +static void +copy_extracted_files_done (GError *error, + gpointer user_data) +{ + FrArchive *archive = user_data; + + remove_local_directory (archive->priv->temp_extraction_dir); + g_free (archive->priv->temp_extraction_dir); + archive->priv->temp_extraction_dir = NULL; + + fr_archive_action_completed (archive, + FR_ACTION_COPYING_FILES_TO_REMOTE, + FR_PROC_ERROR_NONE, + NULL); + + if ((error == NULL) && (archive->priv->extract_here)) + move_here (archive); + + fr_archive_copy_done (archive, FR_ACTION_EXTRACTING_FILES, error); +} + + +static void +copy_extracted_files_progress (goffset current_file, + goffset total_files, + GFile *source, + GFile *destination, + goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + FrArchive *archive = user_data; + + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[PROGRESS], + 0, + (double) current_file / (total_files + 1)); +} + + +static void +copy_extracted_files_to_destination (FrArchive *archive) +{ + g_directory_copy_async (archive->priv->temp_extraction_dir, + archive->priv->extraction_destination, + G_FILE_COPY_OVERWRITE, + G_PRIORITY_DEFAULT, + archive->priv->cancellable, + copy_extracted_files_progress, + archive, + copy_extracted_files_done, + archive); +} + + static void add_dropped_items (DroppedItemsData *data); @@ -672,6 +894,11 @@ fr_archive_change_name (FrArchive *archive, g_object_unref (archive->file); archive->file = g_file_get_child (parent, name); g_object_unref (parent); + + parent = g_file_get_parent (archive->local_copy); + g_object_unref (archive->local_copy); + archive->local_copy = g_file_get_child (parent, name); + g_object_unref (parent); } @@ -686,6 +913,15 @@ action_performed (FrCommand *command, #endif switch (action) { + case FR_ACTION_DELETING_FILES: + if (error->type == FR_PROC_ERROR_NONE) { + if (! g_file_has_uri_scheme (archive->file, "file")) { + copy_to_remote_location (archive, action); + return; + } + } + break; + case FR_ACTION_ADDING_FILES: if (error->type == FR_PROC_ERROR_NONE) { fr_archive_remove_temp_work_dir (archive); @@ -701,15 +937,33 @@ action_performed (FrCommand *command, * original name */ if (archive->command->multi_volume) fr_archive_change_name (archive, archive->command->filename); + if (! g_file_has_uri_scheme (archive->file, "file")) { + copy_to_remote_location (archive, action); + return; + } } break; case FR_ACTION_EXTRACTING_FILES: if (error->type == FR_PROC_ERROR_NONE) { - if (archive->priv->extract_here) + if (archive->priv->remote_extraction) { + copy_extracted_files_to_destination (archive); + return; + } + else if (archive->priv->extract_here) move_here (archive); } else { + /* if an error occurred during extraction remove the + * temp extraction dir, if used. */ + g_print ("action_performed: ERROR!\n"); + + if ((archive->priv->remote_extraction) && (archive->priv->temp_extraction_dir != NULL)) { + remove_local_directory (archive->priv->temp_extraction_dir); + g_free (archive->priv->temp_extraction_dir); + archive->priv->temp_extraction_dir = NULL; + } + if (archive->priv->extract_here) remove_directory (archive->priv->extraction_destination); } @@ -817,7 +1071,7 @@ fr_archive_create (FrArchive *archive, tmp_command = archive->command; - mime_type = get_mime_type_from_filename (archive->file); + mime_type = get_mime_type_from_filename (archive->local_copy); if (! create_command_to_create_archive (archive, mime_type)) { archive->command = tmp_command; return FALSE; @@ -878,11 +1132,11 @@ load_local_archive (FrArchive *archive, tmp_command = archive->command; - mime_type = get_mime_type_from_filename (archive->file); + mime_type = get_mime_type_from_filename (archive->local_copy); if (! create_command_to_load_archive (archive, mime_type)) { - mime_type = get_mime_type_from_content (archive->file); + mime_type = get_mime_type_from_content (archive->local_copy); if (! create_command_to_load_archive (archive, mime_type)) { - mime_type = get_mime_type_from_magic_numbers (archive->file); + mime_type = get_mime_type_from_magic_numbers (archive->local_copy); if (! create_command_to_load_archive (archive, mime_type)) { archive->command = tmp_command; archive->content_type = mime_type; @@ -921,6 +1175,86 @@ load_local_archive (FrArchive *archive, } +static void +copy_remote_file_done (GError *error, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + if (error != NULL) + fr_archive_copy_done (xfer_data->archive, FR_ACTION_LOADING_ARCHIVE, error); + else + load_local_archive (xfer_data->archive, xfer_data->password); + xfer_data_free (xfer_data); +} + + +static void +copy_remote_file_progress (goffset current_file, + goffset total_files, + GFile *source, + GFile *destination, + goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + g_signal_emit (G_OBJECT (xfer_data->archive), + fr_archive_signals[PROGRESS], + 0, + (double) current_num_bytes / total_num_bytes); +} + + +static gboolean +copy_remote_file_done_cb (gpointer user_data) +{ + XferData *xfer_data = user_data; + + g_source_remove (xfer_data->source_id); + copy_remote_file_done (NULL, xfer_data); + return FALSE; +} + + +static void +copy_remote_file (FrArchive *archive, + const char *password) +{ + XferData *xfer_data; + + if (! g_file_query_exists (archive->file, NULL)) { + GError *error; + error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("The file doesn't exist")); + fr_archive_copy_done (archive, FR_ACTION_LOADING_ARCHIVE, error); + g_error_free (error); + return; + } + + xfer_data = g_new0 (XferData, 1); + xfer_data->archive = archive; + xfer_data->uri = g_file_get_uri (archive->file); + if (password != NULL) + xfer_data->password = g_strdup (password); + + if (! archive->is_remote) { + xfer_data->source_id = g_idle_add (copy_remote_file_done_cb, xfer_data); + return; + } + + g_copy_file_async (archive->file, + archive->local_copy, + G_FILE_COPY_OVERWRITE, + G_PRIORITY_DEFAULT, + archive->priv->cancellable, + copy_remote_file_progress, + xfer_data, + copy_remote_file_done, + xfer_data); +} + + gboolean fr_archive_load (FrArchive *archive, const char *uri, @@ -934,7 +1268,7 @@ fr_archive_load (FrArchive *archive, FR_ACTION_LOADING_ARCHIVE); fr_archive_set_uri (archive, uri); - load_local_archive (archive, password); + copy_remote_file (archive, password); return TRUE; } @@ -953,7 +1287,7 @@ fr_archive_load_local (FrArchive *archive, FR_ACTION_LOADING_ARCHIVE); fr_archive_set_uri (archive, uri); - load_local_archive (archive, password); + copy_remote_file (archive, password); return TRUE; } @@ -1120,18 +1454,12 @@ convert_to_local_file_list (GList *file_list) GList *scan; for (scan = file_list; scan; scan = scan->next) { - GFile *file; - char *uri = scan->data; - char *local_filename; - - file = g_file_new_for_uri (uri); - local_filename = g_file_get_path (file); - if (local_filename == NULL) - local_filename = g_strdup (uri); + char *uri = scan->data; + char *local_filename; + + local_filename = g_uri_unescape_string (uri, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH); if (local_filename != NULL) local_file_list = g_list_prepend (local_file_list, local_filename); - - g_object_unref (file); } return local_file_list; @@ -1446,6 +1774,145 @@ fr_archive_add_local_files (FrArchive *archive, } +static void +copy_remote_files_done (GError *error, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + fr_archive_copy_done (xfer_data->archive, FR_ACTION_COPYING_FILES_FROM_REMOTE, error); + + if (error == NULL) + fr_archive_add_local_files (xfer_data->archive, + xfer_data->file_list, + xfer_data->tmp_dir, + xfer_data->dest_dir, + FALSE, + xfer_data->password, + xfer_data->encrypt_header, + xfer_data->compression, + xfer_data->volume_size); + xfer_data_free (xfer_data); +} + + +static void +copy_remote_files_progress (goffset current_file, + goffset total_files, + GFile *source, + GFile *destination, + goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + g_signal_emit (G_OBJECT (xfer_data->archive), + fr_archive_signals[PROGRESS], + 0, + (double) current_file / (total_files + 1)); +} + + +static void +copy_remote_files (FrArchive *archive, + GList *file_list, + const char *base_uri, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size, + const char *tmp_dir) +{ + GList *sources = NULL, *destinations = NULL; + GHashTable *created_folders; + GList *scan; + XferData *xfer_data; + + created_folders = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); + for (scan = file_list; scan; scan = scan->next) { + char *partial_filename = scan->data; + char *local_uri; + char *local_folder_uri; + char *remote_uri; + + local_uri = g_strconcat ("file://", tmp_dir, "/", partial_filename, NULL); + local_folder_uri = remove_level_from_path (local_uri); + if (g_hash_table_lookup (created_folders, local_folder_uri) == NULL) { + GError *error = NULL; + if (! ensure_dir_exists (local_folder_uri, 0755, &error)) { + g_free (local_folder_uri); + g_free (local_uri); + gio_file_list_free (sources); + gio_file_list_free (destinations); + g_hash_table_destroy (created_folders); + + fr_archive_action_completed (archive, + FR_ACTION_COPYING_FILES_FROM_REMOTE, + FR_PROC_ERROR_GENERIC, + error->message); + g_clear_error (&error); + return; + } + + g_hash_table_insert (created_folders, local_folder_uri, GINT_TO_POINTER (1)); + } + else + g_free (local_folder_uri); + + remote_uri = g_strconcat (base_uri, "/", partial_filename, NULL); + sources = g_list_append (sources, g_file_new_for_uri (remote_uri)); + g_free (remote_uri); + + destinations = g_list_append (destinations, g_file_new_for_uri (local_uri)); + g_free (local_uri); + } + g_hash_table_destroy (created_folders); + + xfer_data = g_new0 (XferData, 1); + xfer_data->archive = archive; + xfer_data->file_list = path_list_dup (file_list); + xfer_data->base_uri = g_strdup (base_uri); + xfer_data->dest_dir = g_strdup (dest_dir); + xfer_data->update = update; + xfer_data->dest_dir = g_strdup (dest_dir); + xfer_data->password = g_strdup (password); + xfer_data->encrypt_header = encrypt_header; + xfer_data->compression = compression; + xfer_data->volume_size = volume_size; + xfer_data->tmp_dir = g_strdup (tmp_dir); + + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[START], + 0, + FR_ACTION_COPYING_FILES_FROM_REMOTE); + + g_copy_files_async (sources, + destinations, + G_FILE_COPY_OVERWRITE, + G_PRIORITY_DEFAULT, + archive->priv->cancellable, + copy_remote_files_progress, + xfer_data, + copy_remote_files_done, + xfer_data); + + gio_file_list_free (sources); + gio_file_list_free (destinations); +} + + +static char * +fr_archive_get_temp_work_dir (FrArchive *archive) +{ + fr_archive_remove_temp_work_dir (archive); + archive->priv->temp_dir = get_temp_work_dir (NULL); + return archive->priv->temp_dir; +} + + void fr_archive_add_files (FrArchive *archive, GList *file_list, @@ -1457,23 +1924,30 @@ fr_archive_add_files (FrArchive *archive, FrCompression compression, guint volume_size) { - GFile *file; - char *local_dir; - - file = g_file_new_for_uri (base_dir); - local_dir = g_file_get_path (file); - fr_archive_add_local_files (archive, - file_list, - local_dir, - dest_dir, - update, - password, - encrypt_header, - compression, - volume_size); - - g_free (local_dir); - g_object_unref (file); + if (uri_is_local (base_dir)) { + char *local_dir = g_filename_from_uri (base_dir, NULL, NULL); + fr_archive_add_local_files (archive, + file_list, + local_dir, + dest_dir, + update, + password, + encrypt_header, + compression, + volume_size); + g_free (local_dir); + } + else + copy_remote_files (archive, + file_list, + base_dir, + dest_dir, + update, + password, + encrypt_header, + compression, + volume_size, + fr_archive_get_temp_work_dir (archive)); } @@ -2675,25 +3149,38 @@ fr_archive_extract (FrArchive *archive, gboolean junk_paths, const char *password) { - GFile *file; - char *local_destination; - g_free (archive->priv->extraction_destination); archive->priv->extraction_destination = g_strdup (destination); - file = g_file_new_for_uri (destination); - local_destination = g_file_get_path (file); - fr_archive_extract_to_local (archive, - file_list, - local_destination, - base_dir, - skip_older, - overwrite, - junk_paths, - password); - - g_free (local_destination); - g_object_unref (file); + g_free (archive->priv->temp_extraction_dir); + archive->priv->temp_extraction_dir = NULL; + + archive->priv->remote_extraction = ! uri_is_local (destination); + if (archive->priv->remote_extraction) { + archive->priv->temp_extraction_dir = get_temp_work_dir (NULL); + fr_archive_extract_to_local (archive, + file_list, + archive->priv->temp_extraction_dir, + base_dir, + skip_older, + overwrite, + junk_paths, + password); + } + else { + char *local_destination; + + local_destination = g_filename_from_uri (destination, NULL, NULL); + fr_archive_extract_to_local (archive, + file_list, + local_destination, + base_dir, + skip_older, + overwrite, + junk_paths, + password); + g_free (local_destination); + } } diff --git a/src/fr-archive.h b/src/fr-archive.h index 2c485a2..530e49e 100644 --- a/src/fr-archive.h +++ b/src/fr-archive.h @@ -44,6 +44,8 @@ struct _FrArchive { GObject __parent; GFile *file; + GFile *local_copy; + gboolean is_remote; const char *content_type; FrCommand *command; FrProcess *process; diff --git a/src/fr-window.c b/src/fr-window.c index 8eeb833..d8fb08e 100644 --- a/src/fr-window.c +++ b/src/fr-window.c @@ -4226,14 +4226,14 @@ get_selection_data_from_clipboard_data (FrWindow *window, FrClipboardData *data) { GString *list; - char *archive_uri; + char *local_filename; GList *scan; list = g_string_new (NULL); - archive_uri = g_file_get_uri (window->archive->file); - g_string_append (list, archive_uri); - g_free (archive_uri); + local_filename = g_file_get_uri (window->archive->local_copy); + g_string_append (list, local_filename); + g_free (local_filename); g_string_append (list, "\r\n"); if (window->priv->password != NULL) diff --git a/src/gio-utils.c b/src/gio-utils.c index f94e94f..7886b2e 100644 --- a/src/gio-utils.c +++ b/src/gio-utils.c @@ -127,7 +127,7 @@ filter_empty (Filter *filter) typedef struct { - char *base_directory; + GFile *base_directory; gboolean recursive; gboolean follow_links; StartDirCallback start_dir_func; @@ -153,7 +153,8 @@ for_each_child_data_free (ForEachChildData *fec) if (fec == NULL) return; - g_free (fec->base_directory); + if (fec->base_directory != NULL) + g_object_unref (fec->base_directory); if (fec->current != NULL) g_object_unref (fec->current); if (fec->already_visited) @@ -172,7 +173,7 @@ for_each_child_done_cb (gpointer user_data) g_source_remove (fec->source_id); if (fec->current != NULL) { g_object_unref (fec->current); - fec->current = NULL; + fec->current = NULL; } if (fec->done_func) fec->done_func (fec->error, fec->user_data); @@ -212,8 +213,8 @@ for_each_child_start (ForEachChildData *fec) static void -for_each_child_set_current (ForEachChildData *fec, - const char *directory) +for_each_child_set_current_uri (ForEachChildData *fec, + const char *directory) { if (fec->current != NULL) g_object_unref (fec->current); @@ -222,6 +223,15 @@ for_each_child_set_current (ForEachChildData *fec, static void +for_each_child_set_current (ForEachChildData *fec, + GFile *directory) +{ + if (fec->current != NULL) + g_object_unref (fec->current); + fec->current = g_file_dup (directory); +} + +static void for_each_child_start_next_sub_directory (ForEachChildData *fec) { char *sub_directory = NULL; @@ -236,7 +246,7 @@ for_each_child_start_next_sub_directory (ForEachChildData *fec) } if (sub_directory != NULL) { - for_each_child_set_current (fec, sub_directory); + for_each_child_set_current_uri (fec, sub_directory); for_each_child_start (fec); } else @@ -276,7 +286,6 @@ for_each_child_next_files_ready (GObject *source_object, { ForEachChildData *fec = user_data; GList *children, *scan; - char *current_directory; children = g_file_enumerator_next_files_finish (fec->enumerator, result, @@ -291,16 +300,16 @@ for_each_child_next_files_ready (GObject *source_object, return; } - current_directory = g_file_get_uri (fec->current); for (scan = children; scan; scan = scan->next) { GFileInfo *child_info = scan->data; - char *name, *uri; + GFile *f; + char *uri; - name = g_uri_escape_string (g_file_info_get_name (child_info), G_URI_RESERVED_CHARS_ALLOWED_IN_PATH_ELEMENT, FALSE); - uri = g_strconcat (current_directory, "/", name, NULL); + f = g_file_get_child (fec->current, g_file_info_get_name (child_info)); + uri = g_file_get_uri (f); if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY) { - /* avoid to visit a directory more than ones */ + /* avoid to visit a directory more than once */ if (g_hash_table_lookup (fec->already_visited, uri) == NULL) { char *sub_directory; @@ -314,9 +323,8 @@ for_each_child_next_files_ready (GObject *source_object, fec->for_each_file_func (uri, child_info, fec->user_data); g_free (uri); - g_free (name); + g_object_unref (f); } - g_free (current_directory); g_file_enumerator_next_files_async (fec->enumerator, N_FILES_PER_REQUEST, @@ -404,7 +412,7 @@ for_each_child_start_current (ForEachChildData *fec) * Each callback uses the same @user_data additional parameter. */ void -g_directory_foreach_child (const char *directory, +g_directory_foreach_child (GFile *directory, gboolean recursive, gboolean follow_links, GCancellable *cancellable, @@ -419,7 +427,7 @@ g_directory_foreach_child (const char *directory, fec = g_new0 (ForEachChildData, 1); - fec->base_directory = g_strdup (directory); + fec->base_directory = g_object_ref (directory); fec->recursive = recursive; fec->follow_links = follow_links; fec->cancellable = cancellable; @@ -443,8 +451,8 @@ g_directory_foreach_child (const char *directory, typedef struct { GList *files; GList *dirs; - char *directory; - char *base_dir; + GFile *directory; + GFile *base_dir; GCancellable *cancellable; ListReadyCallback done_func; gpointer done_data; @@ -469,8 +477,10 @@ get_file_list_data_free (GetFileListData *gfl) path_list_free (gfl->files); path_list_free (gfl->dirs); path_list_free (gfl->to_visit); - g_free (gfl->directory); - g_free (gfl->base_dir); + if (gfl->directory != NULL) + g_object_unref (gfl->directory); + if (gfl->base_dir != NULL) + g_object_unref (gfl->base_dir); g_free (gfl); } @@ -479,27 +489,25 @@ get_file_list_data_free (GetFileListData *gfl) static GList* -get_relative_file_list (GList *rel_list, - GList *file_list, - const char *base_dir) +get_relative_file_list (GList *rel_list, + GList *file_list, + GFile *base_dir) { GList *scan; - int base_len; if (base_dir == NULL) return NULL; - base_len = 0; - if (strcmp (base_dir, "/") != 0) - base_len = strlen (base_dir); - for (scan = file_list; scan; scan = scan->next) { - char *full_path = scan->data; - - if (path_in_path (base_dir, full_path)) { - char *rel_path = g_uri_unescape_string (full_path + base_len + 1, NULL); - rel_list = g_list_prepend (rel_list, rel_path); - } + char *full_path = scan->data; + GFile *f; + char *relative_path; + + f = g_file_new_for_commandline_arg (full_path); + relative_path = g_file_get_relative_path (base_dir, f); + if (relative_path != NULL) + rel_list = g_list_prepend (rel_list, relative_path); + g_object_unref (f); } return rel_list; @@ -565,6 +573,7 @@ get_file_list_done (GError *error, GetFileListData *gfl = user_data; GHashTable *h_dirs; GList *scan; + char *uri; gfl->files = g_list_reverse (gfl->files); gfl->dirs = g_list_reverse (gfl->dirs); @@ -582,7 +591,7 @@ get_file_list_done (GError *error, if (gfl->base_dir != NULL) { char *dir; - dir = g_strdup (gfl->base_dir); + dir = g_file_get_uri (gfl->base_dir); gfl->dirs = g_list_prepend (gfl->dirs, dir); g_hash_table_insert (h_dirs, dir, GINT_TO_POINTER (1)); } @@ -594,11 +603,13 @@ get_file_list_done (GError *error, for (scan = gfl->dirs; scan; scan = scan->next) g_hash_table_insert (h_dirs, (char*)scan->data, GINT_TO_POINTER (1)); - gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, gfl->base_dir, gfl->files, FALSE)); + uri = g_file_get_uri (gfl->base_dir); + gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->files, FALSE)); if (filter_empty (gfl->include_filter)) - gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, gfl->base_dir, gfl->dirs, TRUE)); + gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->dirs, TRUE)); + g_free (uri); /**/ if (error == NULL) { @@ -680,8 +691,8 @@ g_directory_list_async (const char *directory, FilterOptions filter_options; gfl = g_new0 (GetFileListData, 1); - gfl->directory = g_strdup (directory); - gfl->base_dir = g_strdup (base_dir); + gfl->directory = g_file_new_for_commandline_arg (directory); + gfl->base_dir = g_file_new_for_commandline_arg (base_dir); gfl->done_func = done_func; gfl->done_data = done_data; @@ -696,7 +707,7 @@ g_directory_list_async (const char *directory, gfl->exclude_filter = filter_new (exclude_files, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT); gfl->exclude_folders_filter = filter_new (exclude_folders, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT); - g_directory_foreach_child (directory, + g_directory_foreach_child (gfl->directory, recursive, follow_links, cancellable, @@ -756,7 +767,9 @@ static void get_items_for_current_dir (GetFileListData *gfl) { const char *directory_name; + GFile *directory_file; char *directory_uri; + char *base_dir_uri; if (gfl->current_dir == NULL) { if (gfl->done_func) { @@ -770,19 +783,20 @@ get_items_for_current_dir (GetFileListData *gfl) } directory_name = file_name_from_path ((char*) gfl->current_dir->data); - if (strcmp (gfl->base_dir, "/") == 0) - directory_uri = g_strconcat (gfl->base_dir, directory_name, NULL); - else - directory_uri = g_strconcat (gfl->base_dir, "/", directory_name, NULL); + directory_file = g_file_get_child (gfl->base_dir, directory_name); + directory_uri = g_file_get_uri (directory_file); + base_dir_uri = g_file_get_uri (gfl->base_dir); g_directory_list_all_async (directory_uri, - gfl->base_dir, + base_dir_uri, TRUE, gfl->cancellable, - get_items_for_current_dir_done, - gfl); + get_items_for_current_dir_done, + gfl); g_free (directory_uri); + g_free (base_dir_uri); + g_object_unref (directory_file); } @@ -800,7 +814,7 @@ g_list_items_async (GList *items, g_return_if_fail (base_dir != NULL); gfl = g_new0 (GetFileListData, 1); - gfl->base_dir = g_strdup (base_dir); + gfl->base_dir = g_file_new_for_commandline_arg (base_dir); gfl->cancellable = cancellable; gfl->done_func = done_func; gfl->done_data = done_data; @@ -916,6 +930,19 @@ g_copy_files_ready_cb (GObject *source_object, GError *error = NULL; if (! g_file_copy_finish (source, result, &error)) { + /* source and target are directories, ignore the error */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_MERGE)) + g_clear_error (&error); + /* source is directory, create target directory */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE)) { + g_clear_error (&error); + g_file_make_directory ((GFile*) cfd->destination->data, + cfd->cancellable, + &error); + } + } + + if (error) { if (cfd->callback) cfd->callback (error, cfd->user_data); g_clear_error (&error); @@ -1123,8 +1150,8 @@ child_data_free (ChildData *child) typedef struct { - char *source; - char *destination; + GFile *source; + GFile *destination; GFileCopyFlags flags; int io_priority; GCancellable *cancellable; @@ -1149,8 +1176,10 @@ directory_copy_data_free (DirectoryCopyData *dcd) if (dcd == NULL) return; - g_free (dcd->source); - g_free (dcd->destination); + if (dcd->source != NULL) + g_object_unref (dcd->source); + if (dcd->destination != NULL) + g_object_unref (dcd->destination); if (dcd->current_source != NULL) { g_object_unref (dcd->current_source); dcd->current_source = NULL; @@ -1161,7 +1190,6 @@ directory_copy_data_free (DirectoryCopyData *dcd) } g_list_foreach (dcd->to_copy, (GFunc) child_data_free, NULL); g_list_free (dcd->to_copy); - g_object_unref (dcd->cancellable); g_free (dcd); } @@ -1187,15 +1215,19 @@ static GFile * get_destination_for_uri (DirectoryCopyData *dcd, const char *uri) { - char *destination_uri; + GFile *f_uri; GFile *destination_file; + char *relative_path; - if (strlen (uri) <= strlen (dcd->source)) - return NULL; + f_uri = g_file_new_for_uri (uri); + relative_path = g_file_get_relative_path (dcd->source, f_uri); + if (relative_path != NULL) + destination_file = g_file_resolve_relative_path (dcd->destination, relative_path); + else + destination_file = g_file_dup (dcd->destination); - destination_uri = g_strconcat (dcd->destination, "/", uri + strlen (dcd->source) + 1, NULL); - destination_file = g_file_new_for_uri (destination_uri); - g_free (destination_uri); + g_free (relative_path); + g_object_unref (f_uri); return destination_file; } @@ -1417,9 +1449,10 @@ g_directory_copy_async (const char *source, { DirectoryCopyData *dcd; + /* Creating GFile objects here will save us lot of effort in path construction */ dcd = g_new0 (DirectoryCopyData, 1); - dcd->source = g_strdup (source); - dcd->destination = g_strdup (destination); + dcd->source = g_file_new_for_commandline_arg (source); + dcd->destination = g_file_new_for_commandline_arg (destination); dcd->flags = flags; dcd->io_priority = io_priority; dcd->cancellable = cancellable; diff --git a/src/gio-utils.h b/src/gio-utils.h index 4cd0a49..7dfe306 100644 --- a/src/gio-utils.h +++ b/src/gio-utils.h @@ -58,7 +58,7 @@ typedef void (*CopyDoneCallback) (GError *error, /* asynchronous recursive list functions */ -void g_directory_foreach_child (const char *directory, +void g_directory_foreach_child (GFile *directory, gboolean recursive, gboolean follow_links, GCancellable *cancellable, -- cgit v0.8.3.1