[file-roller] libarchive: do not follow external links when extracting files



commit 21dfcdbfe258984db89fb65243a1a888924e45a0
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sun Apr 12 11:38:35 2020 +0200

    libarchive: do not follow external links when extracting files
    
    Do not extract a file if its parent is a symbolic link to a
    directory external to the destination.

 src/fr-archive-libarchive.c | 157 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 157 insertions(+)
---
diff --git a/src/fr-archive-libarchive.c b/src/fr-archive-libarchive.c
index 18d7cbe6..0df1d923 100644
--- a/src/fr-archive-libarchive.c
+++ b/src/fr-archive-libarchive.c
@@ -698,6 +698,149 @@ _g_output_stream_add_padding (ExtractData    *extract_data,
 }
 
 
+static gboolean
+_symlink_is_external_to_destination (GFile      *file,
+                                    const char *symlink,
+                                    GFile      *destination,
+                                    GHashTable *external_links);
+
+
+static gboolean
+_g_file_is_external_link (GFile      *file,
+                         GFile      *destination,
+                         GHashTable *external_links)
+{
+       GFileInfo *info;
+       gboolean   external;
+
+       if (g_hash_table_lookup (external_links, file) != NULL)
+               return TRUE;
+
+       info = g_file_query_info (file,
+                                 G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK "," 
G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
+                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                 NULL,
+                                 NULL);
+
+       if (info == NULL)
+               return FALSE;
+
+       external = FALSE;
+
+       if (g_file_info_get_is_symlink (info)) {
+               if (_symlink_is_external_to_destination (file,
+                                                        g_file_info_get_symlink_target (info),
+                                                        destination,
+                                                        external_links))
+               {
+                       g_hash_table_insert (external_links, g_object_ref (file), GINT_TO_POINTER (1));
+                       external = TRUE;
+               }
+       }
+
+       g_object_unref (info);
+
+       return external;
+}
+
+
+static gboolean
+_symlink_is_external_to_destination (GFile      *file,
+                                    const char *symlink,
+                                    GFile      *destination,
+                                    GHashTable *external_links)
+{
+       gboolean  external = FALSE;
+       GFile    *parent;
+       char    **components;
+       int       i;
+
+       if ((file == NULL) || (symlink == NULL))
+               return FALSE;
+
+       if (symlink[0] == '/')
+               return TRUE;
+
+       parent = g_file_get_parent (file);
+       components = g_strsplit (symlink, "/", -1);
+       for (i = 0; components[i] != NULL; i++) {
+               char  *name = components[i];
+               GFile *tmp;
+
+               if ((name[0] == 0) || ((name[0] == '.') && (name[1] == 0)))
+                       continue;
+
+               if ((name[0] == '.') && (name[1] == '.') && (name[2] == 0)) {
+                       if (g_file_equal (parent, destination)) {
+                               external = TRUE;
+                               break;
+                       }
+                       else {
+                               tmp = g_file_get_parent (parent);
+                               g_object_unref (parent);
+                               parent = tmp;
+                       }
+               }
+               else {
+                       tmp = g_file_get_child (parent, components[i]);
+                       g_object_unref (parent);
+                       parent = tmp;
+               }
+
+               if (_g_file_is_external_link (parent, destination, external_links)) {
+                       external = TRUE;
+                       break;
+               }
+       }
+
+       g_strfreev (components);
+       g_object_unref (parent);
+
+       return external;
+}
+
+
+static gboolean
+_g_path_is_external_to_destination (const char *relative_path,
+                                   GFile      *destination,
+                                   GHashTable *external_links)
+{
+       gboolean  external = FALSE;
+       GFile    *parent;
+       char    **components;
+       int       i;
+
+       if (relative_path == NULL)
+               return FALSE;
+
+       if (destination == NULL)
+               return TRUE;
+
+       parent = g_object_ref (destination);
+       components = g_strsplit (relative_path, "/", -1);
+       for (i = 0; (components[i] != NULL) && (components[i + 1] != NULL); i++) {
+               GFile *tmp;
+
+               if (components[i][0] == 0)
+                       continue;
+
+               tmp = g_file_get_child (parent, components[i]);
+               g_object_unref (parent);
+               parent = tmp;
+
+               if (_g_file_is_external_link (parent, destination, external_links)) {
+                       external = TRUE;
+                       break;
+               }
+       }
+
+       g_strfreev (components);
+       g_object_unref (parent);
+
+       return external;
+}
+
+
 static void
 extract_archive_thread (GSimpleAsyncResult *result,
                        GObject            *object,
@@ -708,6 +851,7 @@ extract_archive_thread (GSimpleAsyncResult *result,
        GHashTable           *checked_folders;
        GHashTable           *created_files;
        GHashTable           *folders_created_during_extraction;
+       GHashTable           *external_links;
        struct archive       *a;
        struct archive_entry *entry;
        int                   r;
@@ -724,6 +868,7 @@ extract_archive_thread (GSimpleAsyncResult *result,
        checked_folders = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, 
NULL);
        created_files = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, 
g_object_unref);
        folders_created_during_extraction = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, 
g_object_unref, NULL);
+       external_links = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
        fr_archive_progress_set_total_files (load_data->archive, extract_data->n_files_to_extract);
 
        while ((r = archive_read_next_header (a, &entry)) == ARCHIVE_OK) {
@@ -751,6 +896,15 @@ extract_archive_thread (GSimpleAsyncResult *result,
                fullpath = (*pathname == '/') ? g_strdup (pathname) : g_strconcat ("/", pathname, NULL);
                relative_path = _g_path_get_relative_basename_safe (fullpath, extract_data->base_dir, 
extract_data->junk_paths);
                if (relative_path == NULL) {
+                       fr_archive_progress_inc_completed_files (load_data->archive, 1);
+                       fr_archive_progress_inc_completed_bytes (load_data->archive, 
archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0);
+                       archive_read_data_skip (a);
+                       continue;
+               }
+
+               if (_g_path_is_external_to_destination (relative_path, extract_data->destination, 
external_links)) {
+                       fr_archive_progress_inc_completed_files (load_data->archive, 1);
+                       fr_archive_progress_inc_completed_bytes (load_data->archive, 
archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0);
                        archive_read_data_skip (a);
                        continue;
                }
@@ -959,6 +1113,8 @@ extract_archive_thread (GSimpleAsyncResult *result,
                                                load_data->error = g_error_copy (local_error);
                                        g_clear_error (&local_error);
                                }
+                               else if (_symlink_is_external_to_destination (file, archive_entry_symlink 
(entry), extract_data->destination, external_links))
+                                       g_hash_table_insert (external_links, g_object_ref (file), 
GINT_TO_POINTER (1));
                                archive_read_data_skip (a);
                                break;
 
@@ -993,6 +1149,7 @@ extract_archive_thread (GSimpleAsyncResult *result,
        g_hash_table_unref (folders_created_during_extraction);
        g_hash_table_unref (created_files);
        g_hash_table_unref (checked_folders);
+       g_hash_table_unref (external_links);
        archive_read_free (a);
        extract_data_free (extract_data);
 }


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