[gnome-autoar] AutoarExtract: add signal for name conflicts
- From: Răzvan-Mihai Chițu <razvanchitu src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-autoar] AutoarExtract: add signal for name conflicts
- Date: Mon, 22 Aug 2016 09:44:42 +0000 (UTC)
commit e08008e3b25bfcc8d51d4fc92b3313b803d70007
Author: Razvan Chitu <razvan ch95 gmail com>
Date: Sun May 29 13:13:01 2016 +0300
AutoarExtract: add signal for name conflicts
Extraction was previously done with overwrite as the default option for existing
files. A better solution would be to detect conflicts and notify the user
application, offering the possibility to change the destination of the extracted
file.
In order to fix this, add a signal for notifying conflicts. This signal gets
emitted before actually extracting a file.
https://bugzilla.gnome.org/show_bug.cgi?id=768645
gnome-autoar/autoar-extract.c | 159 +++++++++++++++++++++++++++++++++++++---
gnome-autoar/autoar-extract.h | 6 ++
tests/test-extract.c | 16 ++++
3 files changed, 169 insertions(+), 12 deletions(-)
---
diff --git a/gnome-autoar/autoar-extract.c b/gnome-autoar/autoar-extract.c
index e54bb79..5013b20 100644
--- a/gnome-autoar/autoar-extract.c
+++ b/gnome-autoar/autoar-extract.c
@@ -163,6 +163,7 @@ enum
SCANNED,
DECIDE_DESTINATION,
PROGRESS,
+ CONFLICT,
CANCELLED,
COMPLETED,
AR_ERROR,
@@ -726,6 +727,32 @@ autoar_extract_signal_progress (AutoarExtract *arextract)
}
}
+static AutoarConflictAction
+autoar_extract_signal_conflict (AutoarExtract *arextract,
+ GFile *file,
+ GFile **new_file)
+{
+ AutoarConflictAction action = AUTOAR_CONFLICT_OVERWRITE;
+
+ autoar_common_g_signal_emit (arextract, arextract->priv->in_thread,
+ autoar_extract_signals[CONFLICT], 0,
+ file,
+ new_file,
+ &action);
+
+ if (*new_file) {
+ g_autofree char *previous_path;
+ g_autofree char *new_path;
+
+ previous_path = g_file_get_path (file);
+ new_path = g_file_get_path (*new_file);
+
+ g_debug ("autoar_extract_signal_conflict: %s => %s", previous_path, new_path);
+ }
+
+ return action;
+}
+
static inline void
autoar_extract_signal_cancelled (AutoarExtract *arextract)
{
@@ -839,6 +866,43 @@ autoar_extract_do_sanitize_pathname (AutoarExtract *arextract,
return extracted_filename;
}
+static gboolean
+autoar_extract_check_file_conflict (GFile *file,
+ mode_t extracted_filetype)
+{
+ GFileType file_type;
+ gboolean conflict = FALSE;
+
+ file_type = g_file_query_file_type (file,
+ G_FILE_QUERY_INFO_NONE,
+ NULL);
+ /* If there is no file with the given name, there will be no conflict */
+ if (file_type == G_FILE_TYPE_UNKNOWN) {
+ return FALSE;
+ }
+
+ switch (extracted_filetype) {
+ case AE_IFDIR:
+ break;
+ case AE_IFREG:
+ case AE_IFLNK:
+#if defined HAVE_MKFIFO || defined HAVE_MKNOD
+ case AE_IFIFO:
+#endif
+#ifdef HAVE_MKNOD
+ case AE_IFSOCK:
+ case AE_IFBLK:
+ case AE_IFCHR:
+#endif
+ conflict = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ return conflict;
+}
+
static void
autoar_extract_do_write_entry (AutoarExtract *arextract,
struct archive *a,
@@ -980,6 +1044,7 @@ autoar_extract_do_write_entry (AutoarExtract *arextract,
g_debug ("autoar_extract_do_write_entry: writing");
r = 0;
+
switch (filetype = archive_entry_filetype (entry)) {
default:
case AE_IFREG:
@@ -990,16 +1055,18 @@ autoar_extract_do_write_entry (AutoarExtract *arextract,
gint64 offset;
g_debug ("autoar_extract_do_write_entry: case REG");
+
ostream = (GOutputStream*)g_file_replace (dest,
NULL,
FALSE,
G_FILE_CREATE_NONE,
priv->cancellable,
&(priv->error));
- if (arextract->priv->error != NULL) {
+ if (priv->error != NULL) {
g_object_unref (info);
return;
}
+
if (ostream != NULL) {
/* Archive entry size may be zero if we use raw format. */
if (archive_entry_size(entry) > 0 || priv->use_raw_format) {
@@ -1041,17 +1108,28 @@ autoar_extract_do_write_entry (AutoarExtract *arextract,
GFileAndInfo fileandinfo;
g_debug ("autoar_extract_do_write_entry: case DIR");
+
g_file_make_directory_with_parents (dest, priv->cancellable, &(priv->error));
+
if (priv->error != NULL) {
- /* "File exists" is not a fatal error */
- if (priv->error->code == G_IO_ERROR_EXISTS) {
- g_error_free (priv->error);
- priv->error = NULL;
+ /* "File exists" is not a fatal error, as long as the existing file
+ * is a directory
+ */
+ GFileType file_type;
+
+ file_type = g_file_query_file_type (dest,
+ G_FILE_QUERY_INFO_NONE,
+ NULL);
+
+ if (g_error_matches (priv->error, G_IO_ERROR, G_IO_ERROR_EXISTS) &&
+ file_type == G_FILE_TYPE_DIRECTORY) {
+ g_clear_error (&priv->error);
} else {
g_object_unref (info);
return;
}
}
+
fileandinfo.file = g_object_ref (dest);
fileandinfo.info = g_object_ref (info);
g_array_append_val (priv->extracted_dir_list, fileandinfo);
@@ -1295,6 +1373,28 @@ autoar_extract_class_init (AutoarExtractClass *klass)
G_TYPE_UINT);
/**
+ * AutoarExtract::conflict:
+ * @arextract: the #AutoarExtract
+ * @file: the file that caused a conflict
+ * @new_file: an address to store the new destination for a conflict file
+ *
+ * Returns: the action to be performed by #AutoarExtract
+ *
+ * This signal is used to report and offer the possibility to solve name
+ * conflicts when extracting files.
+ **/
+ autoar_extract_signals[CONFLICT] =
+ g_signal_new ("conflict",
+ type,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_UINT,
+ 2,
+ G_TYPE_FILE,
+ G_TYPE_POINTER);
+
+/**
* AutoarExtract::cancelled:
* @arextract: the #AutoarExtract
*
@@ -1662,8 +1762,10 @@ autoar_extract_step_extract (AutoarExtract *arextract) {
while ((r = archive_read_next_header (a, &entry)) == ARCHIVE_OK) {
const char *pathname;
const char *hardlink;
- GFile *extracted_filename;
- GFile *hardlink_filename;
+ g_autoptr (GFile) extracted_filename = NULL;
+ g_autoptr (GFile) hardlink_filename = NULL;
+ AutoarConflictAction action;
+ gboolean file_conflict;
if (g_cancellable_is_cancelled (priv->cancellable)) {
archive_read_free (a);
@@ -1672,7 +1774,6 @@ autoar_extract_step_extract (AutoarExtract *arextract) {
pathname = archive_entry_pathname (entry);
hardlink = archive_entry_hardlink (entry);
- hardlink_filename = NULL;
extracted_filename =
autoar_extract_do_sanitize_pathname (arextract, pathname);
@@ -1682,13 +1783,47 @@ autoar_extract_step_extract (AutoarExtract *arextract) {
autoar_extract_do_sanitize_pathname (arextract, hardlink);
}
+ /* Attempt to solve any name conflict before doing any operations */
+ file_conflict = autoar_extract_check_file_conflict (extracted_filename,
+ archive_entry_filetype (entry));
+ while (file_conflict) {
+ GFile *new_extracted_filename = NULL;
+
+ action = autoar_extract_signal_conflict (arextract,
+ extracted_filename,
+ &new_extracted_filename);
+
+ switch (action) {
+ case AUTOAR_CONFLICT_OVERWRITE:
+ break;
+ case AUTOAR_CONFLICT_CHANGE_DESTINATION:
+ g_assert_nonnull (new_extracted_filename);
+ g_clear_object (&extracted_filename);
+ extracted_filename = new_extracted_filename;
+ break;
+ case AUTOAR_CONFLICT_SKIP:
+ archive_read_data_skip (a);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (action != AUTOAR_CONFLICT_CHANGE_DESTINATION) {
+ break;
+ }
+
+ file_conflict = autoar_extract_check_file_conflict (extracted_filename,
+ archive_entry_filetype (entry));
+ }
+
+ if (file_conflict && action == AUTOAR_CONFLICT_SKIP) {
+ continue;
+ }
+
autoar_extract_do_write_entry (arextract, a, entry,
extracted_filename, hardlink_filename);
- g_object_unref (extracted_filename);
- if (hardlink_filename != NULL)
- g_object_unref (hardlink_filename);
-
if (priv->error != NULL) {
archive_read_free (a);
return;
diff --git a/gnome-autoar/autoar-extract.h b/gnome-autoar/autoar-extract.h
index ba7244e..772705a 100644
--- a/gnome-autoar/autoar-extract.h
+++ b/gnome-autoar/autoar-extract.h
@@ -92,6 +92,12 @@ void autoar_extract_set_output_is_dest (AutoarExtract *arextract,
void autoar_extract_set_notify_interval (AutoarExtract *arextract,
gint64 notify_interval);
+typedef enum {
+ AUTOAR_CONFLICT_SKIP = 0,
+ AUTOAR_CONFLICT_OVERWRITE,
+ AUTOAR_CONFLICT_CHANGE_DESTINATION
+} AutoarConflictAction;
+
G_END_DECLS
#endif /* AUTOAR_EXTRACT_H */
diff --git a/tests/test-extract.c b/tests/test-extract.c
index a4e9d76..3c92bcc 100644
--- a/tests/test-extract.c
+++ b/tests/test-extract.c
@@ -53,6 +53,21 @@ my_handler_progress (AutoarExtract *arextract,
((double)(completed_files)) * 100 / autoar_extract_get_files (arextract));
}
+static AutoarConflictAction
+my_handler_conflict (AutoarExtract *arextract,
+ GFile *file,
+ GFile **new_file,
+ gpointer data)
+{
+ g_autofree char *path;
+
+ path = g_file_get_path (file);
+
+ g_print ("Conflict on: %s\n", path);
+
+ return AUTOAR_CONFLICT_OVERWRITE;
+}
+
static void
my_handler_error (AutoarExtract *arextract,
GError *error,
@@ -101,6 +116,7 @@ main (int argc,
g_signal_connect (arextract, "scanned", G_CALLBACK (my_handler_scanned), NULL);
g_signal_connect (arextract, "decide-destination", G_CALLBACK (my_handler_decide_destination), NULL);
g_signal_connect (arextract, "progress", G_CALLBACK (my_handler_progress), NULL);
+ g_signal_connect (arextract, "conflict", G_CALLBACK (my_handler_conflict), NULL);
g_signal_connect (arextract, "error", G_CALLBACK (my_handler_error), NULL);
g_signal_connect (arextract, "completed", G_CALLBACK (my_handler_completed), NULL);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]