diff -Nru file-roller-3.28.0/debian/changelog file-roller-3.28.0/debian/changelog --- file-roller-3.28.0/debian/changelog 2019-09-25 19:32:21.000000000 +0000 +++ file-roller-3.28.0/debian/changelog 2020-06-05 09:40:57.000000000 +0000 @@ -1,8 +1,17 @@ -file-roller (3.28.0-1ubuntu1.1+elementary4~ubuntu5.0.1) bionic; urgency=low +file-roller (3.28.0-1ubuntu1.2+elementary6~ubuntu5.1.5.1) bionic; urgency=low * Auto build. - -- Launchpad Package Builder Wed, 25 Sep 2019 19:32:21 +0000 + -- Launchpad Package Builder Fri, 05 Jun 2020 09:40:57 +0000 + +file-roller (3.28.0-1ubuntu1.2) bionic-security; urgency=medium + + * SECURITY UPDATE: Directory traversal + - debian/patches/CVE-2020-11736.patch: do not follow external + links when extracting files in src/fr-archive-libarchive.c. + - CVE-2020-11736 + + -- Leonidas S. Barbosa Tue, 14 Apr 2020 16:53:44 -0300 file-roller (3.28.0-1ubuntu1.1) bionic-security; urgency=medium diff -Nru file-roller-3.28.0/debian/git-build-recipe.manifest file-roller-3.28.0/debian/git-build-recipe.manifest --- file-roller-3.28.0/debian/git-build-recipe.manifest 2019-09-25 19:32:21.000000000 +0000 +++ file-roller-3.28.0/debian/git-build-recipe.manifest 2020-06-05 09:40:57.000000000 +0000 @@ -1,3 +1,3 @@ -# git-build-recipe format 0.4 deb-version {debversion}+elementary4 -lp:~elementary-os/elementaryos/+git/os-patches git-commit:29495e20f18e55f893850eba5cd45d4cffa76279 -merge elementary-patch lp:~elementary-os/elementaryos/+git/os-patches git-commit:4ab5e02aa79df451f973fd85780ffc5f13d8b538 +# git-build-recipe format 0.4 deb-version {debversion}+elementary6 +lp:~elementary-os/elementaryos/+git/os-patches git-commit:8ea94fd744e4bfe9db0523e112dc76c2ed7a8e7e +merge elementary-patch lp:~elementary-os/elementaryos/+git/os-patches git-commit:5b35139257a877154cb1bb017e1c43522a5ad5b0 diff -Nru file-roller-3.28.0/.pc/applied-patches file-roller-3.28.0/.pc/applied-patches --- file-roller-3.28.0/.pc/applied-patches 2019-09-25 19:32:21.000000000 +0000 +++ file-roller-3.28.0/.pc/applied-patches 2020-06-05 09:40:57.000000000 +0000 @@ -1,2 +1,3 @@ 01_package_names.patch CVE-2019-16680.patch +CVE-2020-11736.patch diff -Nru file-roller-3.28.0/.pc/CVE-2020-11736.patch/src/fr-archive-libarchive.c file-roller-3.28.0/.pc/CVE-2020-11736.patch/src/fr-archive-libarchive.c --- file-roller-3.28.0/.pc/CVE-2020-11736.patch/src/fr-archive-libarchive.c 1970-01-01 00:00:00.000000000 +0000 +++ file-roller-3.28.0/.pc/CVE-2020-11736.patch/src/fr-archive-libarchive.c 2020-06-05 09:40:57.000000000 +0000 @@ -0,0 +1,2199 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2012 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "file-data.h" +#include "file-utils.h" +#include "fr-error.h" +#include "fr-archive-libarchive.h" +#include "gio-utils.h" +#include "glib-utils.h" +#include "typedefs.h" + + +#define BUFFER_SIZE (64 * 1024) +#define FILE_ATTRIBUTES_NEEDED_BY_ARCHIVE_ENTRY ("standard::*,time::*,access::*,unix::*") + + +G_DEFINE_TYPE (FrArchiveLibarchive, fr_archive_libarchive, FR_TYPE_ARCHIVE) + + +struct _FrArchiveLibarchivePrivate { + gssize compressed_size; + gssize uncompressed_size; +}; + + +static void +fr_archive_libarchive_finalize (GObject *object) +{ + /*FrArchiveLibarchive *self;*/ + + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_ARCHIVE_LIBARCHIVE (object)); + + /*self = FR_ARCHIVE_LIBARCHIVE (object);*/ + + if (G_OBJECT_CLASS (fr_archive_libarchive_parent_class)->finalize) + G_OBJECT_CLASS (fr_archive_libarchive_parent_class)->finalize (object); +} + + +const char *libarchiver_mime_types[] = { + "application/vnd.debian.binary-package", + "application/vnd.ms-cab-compressed", + "application/x-7z-compressed", + "application/x-ar", + "application/x-bzip-compressed-tar", + "application/x-cbr", + "application/x-cbz", + "application/x-cd-image", + "application/x-compressed-tar", + "application/x-cpio", + "application/x-deb", + "application/x-lha", + "application/x-lrzip-compressed-tar", + "application/x-lzip-compressed-tar", + "application/x-lzma-compressed-tar", + "application/x-lzop-compressed-tar", + "application/x-rar", + "application/x-rpm", + "application/x-tar", + "application/x-tarz", + "application/x-xar", + "application/x-xz-compressed-tar", + "application/zip", + NULL }; + + +static const char ** +fr_archive_libarchive_get_mime_types (FrArchive *archive) +{ + return libarchiver_mime_types; +} + + +static FrArchiveCap +fr_archive_libarchive_get_capabilities (FrArchive *archive, + const char *mime_type, + gboolean check_command) +{ + FrArchiveCap capabilities; + + capabilities = FR_ARCHIVE_CAN_STORE_MANY_FILES; + + /* write-only formats */ + if (strcmp (mime_type, "application/x-7z-compressed") == 0) { + capabilities |= FR_ARCHIVE_CAN_WRITE; + return capabilities; + } + + /* give priority to 7za that supports CAB files better. */ + if ((strcmp (mime_type, "application/vnd.ms-cab-compressed") == 0) + && _g_program_is_available ("7za", check_command)) + { + return capabilities; + } + + /* give priority to 7z, unzip and zip that supports ZIP files better. */ + if ((strcmp (mime_type, "application/zip") == 0) + || (strcmp (mime_type, "application/x-cbz") == 0)) + { + if (_g_program_is_available ("7z", check_command)) { + return capabilities; + } + if (!_g_program_is_available ("unzip", check_command)) { + capabilities |= FR_ARCHIVE_CAN_READ; + } + if (!_g_program_is_available ("zip", check_command)) { + capabilities |= FR_ARCHIVE_CAN_WRITE; + } + return capabilities; + } + + /* give priority to utilities that support RAR files better. */ + if ((strcmp (mime_type, "application/x-rar") == 0) + || (strcmp (mime_type, "application/x-cbr") == 0)) + { + if (_g_program_is_available ("rar", check_command) + || _g_program_is_available ("unrar", check_command) + || _g_program_is_available ("unar", check_command)) { + return capabilities; + } + } + + /* tar.lrz format requires external lrzip */ + if (strcmp (mime_type, "application/x-lrzip-compressed-tar") == 0) { + if (!_g_program_is_available ("lrzip", check_command)) + return capabilities; + } + + capabilities |= FR_ARCHIVE_CAN_READ; + + /* read-only formats */ + if ((strcmp (mime_type, "application/vnd.ms-cab-compressed") == 0) + || (strcmp (mime_type, "application/x-cbr") == 0) + || (strcmp (mime_type, "application/x-deb") == 0) + || (strcmp (mime_type, "application/vnd.debian.binary-package") == 0) + || (strcmp (mime_type, "application/x-lha") == 0) + || (strcmp (mime_type, "application/x-rar") == 0) + || (strcmp (mime_type, "application/x-rpm") == 0) + || (strcmp (mime_type, "application/x-xar") == 0)) + { + return capabilities; + } + + /* all other formats can be read and written */ + capabilities |= FR_ARCHIVE_CAN_WRITE; + + return capabilities; +} + + +static const char * +fr_archive_libarchive_get_packages (FrArchive *archive, + const char *mime_type) +{ + return NULL; +} + + +/* LoadData */ + + +#define LOAD_DATA(x) ((LoadData *)(x)) + + +typedef struct { + FrArchive *archive; + GCancellable *cancellable; + GSimpleAsyncResult *result; + GInputStream *istream; + void *buffer; + gssize buffer_size; + GError *error; +} LoadData; + + +static void +load_data_init (LoadData *load_data) +{ + load_data->buffer_size = BUFFER_SIZE; + load_data->buffer = g_new (char, load_data->buffer_size); +} + + +static void +load_data_free (LoadData *load_data) +{ + _g_object_unref (load_data->archive); + _g_object_unref (load_data->cancellable); + _g_object_unref (load_data->result); + _g_object_unref (load_data->istream); + g_free (load_data->buffer); + g_free (load_data); +} + + +static int +load_data_open (struct archive *a, + void *client_data) +{ + LoadData *load_data = client_data; + + if (load_data->error != NULL) + return ARCHIVE_FATAL; + + if (g_simple_async_result_get_source_tag (load_data->result) == fr_archive_list) { + FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->compressed_size = 0; + FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->uncompressed_size = 0; + } + + load_data->istream = (GInputStream *) g_file_read (fr_archive_get_file (load_data->archive), + load_data->cancellable, + &load_data->error); + return (load_data->error == NULL) ? ARCHIVE_OK : ARCHIVE_FATAL; +} + + +static ssize_t +load_data_read (struct archive *a, + void *client_data, + const void **buff) +{ + LoadData *load_data = client_data; + gssize bytes; + + if (load_data->error != NULL) + return -1; + + *buff = load_data->buffer; + bytes = g_input_stream_read (load_data->istream, + load_data->buffer, + load_data->buffer_size, + load_data->cancellable, + &load_data->error); + + /* update the progress only if listing the content */ + if (g_simple_async_result_get_source_tag (load_data->result) == fr_archive_list) { + fr_archive_progress_set_completed_bytes (load_data->archive, + g_seekable_tell (G_SEEKABLE (load_data->istream))); + FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->compressed_size += bytes; + } + + return bytes; +} + + +static int +load_data_close (struct archive *a, + void *client_data) +{ + LoadData *load_data = client_data; + + if (load_data->error != NULL) + return ARCHIVE_FATAL; + + if (load_data->istream != NULL) { + _g_object_unref (load_data->istream); + load_data->istream = NULL; + } + + return ARCHIVE_OK; +} + + +/* -- list -- */ + + +static goffset +_g_file_get_size (GFile *file, + GCancellable *cancellable) +{ + GFileInfo *info; + goffset size; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_SIZE, + G_FILE_QUERY_INFO_NONE, + cancellable, + NULL); + if (info == NULL) + return 0; + + size = g_file_info_get_size (info); + g_object_unref (info); + + return size; +} + + +static GError * +_g_error_new_from_archive_error (const char *s) +{ + char *msg; + GError *error; + + msg = (s != NULL) ? g_locale_to_utf8 (s, -1, NULL, NULL, NULL) : NULL; + if (msg == NULL) + msg = g_strdup ("Fatal error"); + error = g_error_new_literal (FR_ERROR, FR_ERROR_COMMAND_ERROR, msg); + + g_free (msg); + + return error; +} + + +static void +list_archive_thread (GSimpleAsyncResult *result, + GObject *object, + GCancellable *cancellable) +{ + LoadData *load_data; + struct archive *a; + struct archive_entry *entry; + int r; + + load_data = g_simple_async_result_get_op_res_gpointer (result); + + fr_archive_progress_set_total_bytes (load_data->archive, + _g_file_get_size (fr_archive_get_file (load_data->archive), cancellable)); + + a = archive_read_new (); + archive_read_support_filter_all (a); + archive_read_support_format_all (a); + archive_read_open (a, load_data, load_data_open, load_data_read, load_data_close); + while ((r = archive_read_next_header (a, &entry)) == ARCHIVE_OK) { + FileData *file_data; + const char *pathname; + + if (g_cancellable_is_cancelled (cancellable)) + break; + + file_data = file_data_new (); + + if (archive_entry_size_is_set (entry)) { + file_data->size = archive_entry_size (entry); + FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->uncompressed_size += file_data->size; + } + + if (archive_entry_mtime_is_set (entry)) + file_data->modified = archive_entry_mtime (entry); + + if (archive_entry_filetype (entry) == AE_IFLNK) + file_data->link = g_strdup (archive_entry_symlink (entry)); + + pathname = archive_entry_pathname (entry); + if (*pathname == '/') { + file_data->full_path = g_strdup (pathname); + file_data->original_path = file_data->full_path; + } + else { + file_data->full_path = g_strconcat ("/", pathname, NULL); + file_data->original_path = file_data->full_path + 1; + } + + file_data->dir = (archive_entry_filetype (entry) == AE_IFDIR); + if (file_data->dir) + file_data->name = _g_path_get_dir_name (file_data->full_path); + else + file_data->name = g_strdup (_g_path_get_basename (file_data->full_path)); + file_data->path = _g_path_remove_level (file_data->full_path); + + /* + g_print ("%s\n", archive_entry_pathname (entry)); + g_print ("\tfull_path: %s\n", file_data->full_path); + g_print ("\toriginal_path: %s\n", file_data->original_path); + g_print ("\tname: %s\n", file_data->name); + g_print ("\tpath: %s\n", file_data->path); + g_print ("\tlink: %s\n", file_data->link); + */ + + fr_archive_add_file (load_data->archive, file_data); + + archive_read_data_skip (a); + } + archive_read_free (a); + + if ((load_data->error == NULL) && (r != ARCHIVE_EOF) && (archive_error_string (a) != NULL)) + load_data->error = _g_error_new_from_archive_error (archive_error_string (a)); + if (load_data->error == NULL) + g_cancellable_set_error_if_cancelled (cancellable, &load_data->error); + if (load_data->error != NULL) + g_simple_async_result_set_from_error (result, load_data->error); + + load_data_free (load_data); +} + + +static void +fr_archive_libarchive_list (FrArchive *archive, + const char *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + LoadData *load_data; + + load_data = g_new0 (LoadData, 1); + load_data_init (load_data); + + load_data->archive = g_object_ref (archive); + load_data->cancellable = _g_object_ref (cancellable); + load_data->result = g_simple_async_result_new (G_OBJECT (archive), + callback, + user_data, + fr_archive_list); + + g_simple_async_result_set_op_res_gpointer (load_data->result, load_data, NULL); + g_simple_async_result_run_in_thread (load_data->result, + list_archive_thread, + G_PRIORITY_DEFAULT, + cancellable); +} + + +/* -- extract -- */ + + +#define NULL_BUFFER_SIZE (16 * 1024) + + +typedef struct { + LoadData parent; + GList *file_list; + GFile *destination; + char *base_dir; + gboolean skip_older; + gboolean overwrite; + gboolean junk_paths; + GHashTable *files_to_extract; + int n_files_to_extract; + GHashTable *usernames; + GHashTable *groupnames; + char *null_buffer; +} ExtractData; + + +static void +extract_data_free (ExtractData *extract_data) +{ + g_free (extract_data->base_dir); + _g_object_unref (extract_data->destination); + _g_string_list_free (extract_data->file_list); + g_hash_table_unref (extract_data->files_to_extract); + g_hash_table_unref (extract_data->usernames); + g_hash_table_unref (extract_data->groupnames); + g_free (extract_data->null_buffer); + load_data_free (LOAD_DATA (extract_data)); +} + + +static gboolean +extract_data_get_extraction_requested (ExtractData *extract_data, + const char *pathname) +{ + if (extract_data->file_list != NULL) + return g_hash_table_lookup (extract_data->files_to_extract, pathname) != NULL; + else + return TRUE; +} + + +static GFileInfo * +_g_file_info_create_from_entry (struct archive_entry *entry, + ExtractData *extract_data) +{ + GFileInfo *info; + + info = g_file_info_new (); + + /* times */ + + if (archive_entry_mtime_is_set (entry)) + g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, archive_entry_mtime (entry)); + + /* username */ + + if (archive_entry_uname (entry) != NULL) { + guint32 uid; + + uid = GPOINTER_TO_INT (g_hash_table_lookup (extract_data->usernames, archive_entry_uname (entry))); + if (uid == 0) { + struct passwd *pwd = getpwnam (archive_entry_uname (entry)); + if (pwd != NULL) { + uid = pwd->pw_uid; + g_hash_table_insert (extract_data->usernames, g_strdup (archive_entry_uname (entry)), GINT_TO_POINTER (uid)); + } + } + if (uid != 0) + g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, uid); + } + + /* groupname */ + + if (archive_entry_gname (entry) != NULL) { + guint32 gid; + + gid = GPOINTER_TO_INT (g_hash_table_lookup (extract_data->groupnames, archive_entry_gname (entry))); + if (gid == 0) { + struct group *grp = getgrnam (archive_entry_gname (entry)); + if (grp != NULL) { + gid = grp->gr_gid; + g_hash_table_insert (extract_data->groupnames, g_strdup (archive_entry_gname (entry)), GINT_TO_POINTER (gid)); + } + } + if (gid != 0) + g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, gid); + } + + /* permsissions */ + + g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, archive_entry_mode (entry)); + + return info; +} + + +static gboolean +_g_file_set_attributes_from_info (GFile *file, + GFileInfo *info, + GCancellable *cancellable, + GError **error) +{ + return g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, cancellable, error); +} + + +static void +restore_original_file_attributes (GHashTable *created_files, + GCancellable *cancellable) +{ + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, created_files); + while (g_hash_table_iter_next (&iter, &key, &value)) { + GFile *file = key; + GFileInfo *info = value; + + _g_file_set_attributes_from_info (file, info, cancellable, NULL); + } +} + + +static gboolean +_g_output_stream_add_padding (ExtractData *extract_data, + GOutputStream *ostream, + gssize target_offset, + gssize actual_offset, + GCancellable *cancellable, + GError **error) +{ + gboolean success = TRUE; + gsize count; + gsize bytes_written; + + if (extract_data->null_buffer == NULL) + extract_data->null_buffer = g_malloc0 (NULL_BUFFER_SIZE); + + while (target_offset > actual_offset) { + count = NULL_BUFFER_SIZE; + if (target_offset < actual_offset + NULL_BUFFER_SIZE) + count = target_offset - actual_offset; + + success = g_output_stream_write_all (ostream, extract_data->null_buffer, count, &bytes_written, cancellable, error); + if (! success) + break; + + actual_offset += bytes_written; + } + + return success; +} + + +static void +extract_archive_thread (GSimpleAsyncResult *result, + GObject *object, + GCancellable *cancellable) +{ + ExtractData *extract_data; + LoadData *load_data; + GHashTable *checked_folders; + GHashTable *created_files; + GHashTable *folders_created_during_extraction; + struct archive *a; + struct archive_entry *entry; + int r; + + extract_data = g_simple_async_result_get_op_res_gpointer (result); + load_data = LOAD_DATA (extract_data); + + 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); + fr_archive_progress_set_total_files (load_data->archive, extract_data->n_files_to_extract); + + a = archive_read_new (); + archive_read_support_filter_all (a); + archive_read_support_format_all (a); + archive_read_open (a, load_data, load_data_open, load_data_read, load_data_close); + while ((r = archive_read_next_header (a, &entry)) == ARCHIVE_OK) { + const char *pathname; + char *fullpath; + const char *relative_path; + GFile *file; + GFile *parent; + GOutputStream *ostream; + const void *buffer; + size_t buffer_size; + int64_t target_offset, actual_offset; + GError *local_error = NULL; + __LA_MODE_T filetype; + + if (g_cancellable_is_cancelled (cancellable)) + break; + + pathname = archive_entry_pathname (entry); + if (! extract_data_get_extraction_requested (extract_data, pathname)) { + archive_read_data_skip (a); + continue; + } + + 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) { + archive_read_data_skip (a); + continue; + } + + file = g_file_get_child (extract_data->destination, relative_path); + + /* honor the skip_older and overwrite options */ + + if ((g_hash_table_lookup (folders_created_during_extraction, file) == NULL) + && (extract_data->skip_older || ! extract_data->overwrite)) + { + GFileInfo *info; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," G_FILE_ATTRIBUTE_TIME_MODIFIED, + G_FILE_QUERY_INFO_NONE, + cancellable, + &local_error); + if (info != NULL) { + gboolean skip = FALSE; + + if (! extract_data->overwrite) { + skip = TRUE; + } + else if (extract_data->skip_older) { + GTimeVal modification_time; + + g_file_info_get_modification_time (info, &modification_time); + if (archive_entry_mtime (entry) < modification_time.tv_sec) + skip = TRUE; + } + + g_object_unref (info); + + if (skip) { + g_object_unref (file); + + archive_read_data_skip (a); + fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0); + + if ((extract_data->file_list != NULL) && (--extract_data->n_files_to_extract == 0)) { + r = ARCHIVE_EOF; + break; + } + + continue; + } + } + else { + if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + load_data->error = local_error; + g_object_unref (info); + break; + } + g_clear_error (&local_error); + } + } + + fr_archive_progress_inc_completed_files (load_data->archive, 1); + + /* create the file parents */ + + parent = g_file_get_parent (file); + + if ((parent != NULL) + && (g_hash_table_lookup (checked_folders, parent) == NULL) + && ! g_file_query_exists (parent, cancellable)) + { + if (! _g_file_make_directory_with_parents (parent, + folders_created_during_extraction, + cancellable, + &local_error)) + { + if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS)) + load_data->error = local_error; + else + g_clear_error (&local_error); + } + + if (load_data->error == NULL) { + GFile *grandparent; + + grandparent = g_object_ref (parent); + while (grandparent != NULL) { + if (g_hash_table_lookup (checked_folders, grandparent) == NULL) + g_hash_table_insert (checked_folders, grandparent, GINT_TO_POINTER (1)); + grandparent = g_file_get_parent (grandparent); + } + } + } + g_object_unref (parent); + + /* create the file */ + + filetype = archive_entry_filetype (entry); + + if (load_data->error == NULL) { + const char *linkname; + + linkname = archive_entry_hardlink (entry); + if (linkname != NULL) { + char *link_fullpath; + const char *relative_path; + GFile *link_file; + char *oldname; + char *newname; + int r; + + link_fullpath = (*linkname == '/') ? g_strdup (linkname) : g_strconcat ("/", linkname, NULL); + relative_path = _g_path_get_relative_basename_safe (link_fullpath, extract_data->base_dir, extract_data->junk_paths); + if (relative_path == NULL) { + g_free (link_fullpath); + archive_read_data_skip (a); + continue; + } + + link_file = g_file_get_child (extract_data->destination, relative_path); + oldname = g_file_get_path (link_file); + newname = g_file_get_path (file); + + if ((oldname != NULL) && (newname != NULL)) + r = link (oldname, newname); + else + r = -1; + + if (r == 0) { + __LA_INT64_T filesize; + + if (archive_entry_size_is_set (entry)) + filesize = archive_entry_size (entry); + else + filesize = -1; + + if (filesize > 0) + filetype = AE_IFREG; /* treat as a regular file to save the data */ + } + else { + char *uri; + char *msg; + + uri = g_file_get_uri (file); + msg = g_strdup_printf ("Could not create the hard link %s", uri); + load_data->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, msg); + + g_free (msg); + g_free (uri); + } + + g_free (newname); + g_free (oldname); + g_object_unref (link_file); + g_free (link_fullpath); + } + } + + if (load_data->error == NULL) { + switch (filetype) { + case AE_IFDIR: + if (! g_file_make_directory (file, cancellable, &local_error)) { + if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS)) + load_data->error = g_error_copy (local_error); + g_clear_error (&local_error); + } + if (load_data->error == NULL) + g_hash_table_insert (created_files, g_object_ref (file), _g_file_info_create_from_entry (entry, extract_data)); + archive_read_data_skip (a); + break; + + case AE_IFREG: + ostream = (GOutputStream *) g_file_replace (file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, cancellable, &load_data->error); + if (ostream == NULL) + break; + + actual_offset = 0; + while ((r = archive_read_data_block (a, &buffer, &buffer_size, &target_offset)) == ARCHIVE_OK) { + gsize bytes_written; + + if (target_offset > actual_offset) { + if (! _g_output_stream_add_padding (extract_data, ostream, target_offset, actual_offset, cancellable, &load_data->error)) + break; + fr_archive_progress_inc_completed_bytes (load_data->archive, target_offset - actual_offset); + actual_offset = target_offset; + } + + if (! g_output_stream_write_all (ostream, buffer, buffer_size, &bytes_written, cancellable, &load_data->error)) + break; + + actual_offset += bytes_written; + fr_archive_progress_inc_completed_bytes (load_data->archive, bytes_written); + } + + if ((r == ARCHIVE_EOF) && (target_offset > actual_offset)) + _g_output_stream_add_padding (extract_data, ostream, target_offset, actual_offset, cancellable, &load_data->error); + + _g_object_unref (ostream); + + if (r != ARCHIVE_EOF) + load_data->error = _g_error_new_from_archive_error (archive_error_string (a)); + else + g_hash_table_insert (created_files, g_object_ref (file), _g_file_info_create_from_entry (entry, extract_data)); + break; + + case AE_IFLNK: + if (! g_file_make_symbolic_link (file, archive_entry_symlink (entry), cancellable, &local_error)) { + if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS)) + load_data->error = g_error_copy (local_error); + g_clear_error (&local_error); + } + archive_read_data_skip (a); + break; + + default: + archive_read_data_skip (a); + break; + } + } + + g_object_unref (file); + g_free (fullpath); + + if (load_data->error != NULL) + break; + + if ((extract_data->file_list != NULL) && (--extract_data->n_files_to_extract == 0)) { + r = ARCHIVE_EOF; + break; + } + } + + if (load_data->error == NULL) + restore_original_file_attributes (created_files, cancellable); + + if ((load_data->error == NULL) && (r != ARCHIVE_EOF)) + load_data->error = _g_error_new_from_archive_error (archive_error_string (a)); + if (load_data->error == NULL) + g_cancellable_set_error_if_cancelled (cancellable, &load_data->error); + if (load_data->error != NULL) + g_simple_async_result_set_from_error (result, load_data->error); + + g_hash_table_unref (folders_created_during_extraction); + g_hash_table_unref (created_files); + g_hash_table_unref (checked_folders); + archive_read_free (a); + extract_data_free (extract_data); +} + + +static void +fr_archive_libarchive_extract_files (FrArchive *archive, + GList *file_list, + GFile *destination, + const char *base_dir, + gboolean skip_older, + gboolean overwrite, + gboolean junk_paths, + const char *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ExtractData *extract_data; + LoadData *load_data; + GList *scan; + + extract_data = g_new0 (ExtractData, 1); + + load_data = LOAD_DATA (extract_data); + load_data->archive = g_object_ref (archive); + load_data->cancellable = _g_object_ref (cancellable); + load_data->result = g_simple_async_result_new (G_OBJECT (archive), + callback, + user_data, + fr_archive_extract); + load_data->buffer_size = BUFFER_SIZE; + load_data->buffer = g_new (char, load_data->buffer_size); + + extract_data->file_list = _g_string_list_dup (file_list); + extract_data->destination = g_object_ref (destination); + extract_data->base_dir = g_strdup (base_dir); + extract_data->skip_older = skip_older; + extract_data->overwrite = overwrite; + extract_data->junk_paths = junk_paths; + extract_data->files_to_extract = g_hash_table_new (g_str_hash, g_str_equal); + extract_data->n_files_to_extract = 0; + extract_data->usernames = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + extract_data->groupnames = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + for (scan = extract_data->file_list; scan; scan = scan->next) { + g_hash_table_insert (extract_data->files_to_extract, scan->data, GINT_TO_POINTER (1)); + extract_data->n_files_to_extract++; + } + + g_simple_async_result_set_op_res_gpointer (load_data->result, extract_data, NULL); + g_simple_async_result_run_in_thread (load_data->result, + extract_archive_thread, + G_PRIORITY_DEFAULT, + cancellable); +} + + +/* -- AddFile -- */ + + +typedef struct { + GFile *file; + char *pathname; +} AddFile; + + +static AddFile * +add_file_new (GFile *file, + const char *archive_pathname) +{ + AddFile *add_file; + + add_file = g_new (AddFile, 1); + add_file->file = g_object_ref (file); + add_file->pathname = g_strdup (archive_pathname); + + return add_file; +} + + +static void +add_file_free (AddFile *add_file) +{ + g_object_unref (add_file->file); + g_free (add_file->pathname); + g_free (add_file); +} + + +/* -- _fr_archive_libarchive_save -- */ + + +#define SAVE_DATA(x) ((SaveData *)(x)) + + +typedef enum { + WRITE_ACTION_ABORT, + WRITE_ACTION_SKIP_ENTRY, + WRITE_ACTION_WRITE_ENTRY +} WriteAction; + + +typedef struct _SaveData SaveData; +typedef void (*SaveDataFunc) (SaveData *, gpointer user_data); +typedef WriteAction (*EntryActionFunc) (SaveData *, struct archive_entry *, gpointer user_data); + + +struct _SaveData { + LoadData parent; + GFile *tmp_file; + GOutputStream *ostream; + GHashTable *usernames; + GHashTable *groupnames; + gboolean update; + char *password; + gboolean encrypt_header; + FrCompression compression; + guint volume_size; + void *buffer; + gsize buffer_size; + SaveDataFunc begin_operation; + SaveDataFunc end_operation; + EntryActionFunc entry_action; + gpointer user_data; + GDestroyNotify user_data_notify; + struct archive *b; +}; + + +static void +save_data_init (SaveData *save_data) +{ + load_data_init (LOAD_DATA (save_data)); + save_data->buffer_size = BUFFER_SIZE; + save_data->buffer = g_new (char, save_data->buffer_size); + save_data->usernames = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_free); + save_data->groupnames = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_free); +} + + +static void +save_data_free (SaveData *save_data) +{ + if (save_data->user_data_notify != NULL) + save_data->user_data_notify (save_data->user_data); + g_free (save_data->buffer); + g_free (save_data->password); + g_hash_table_unref (save_data->groupnames); + g_hash_table_unref (save_data->usernames); + _g_object_unref (save_data->ostream); + _g_object_unref (save_data->tmp_file); + load_data_free (LOAD_DATA (save_data)); +} + + +static int +save_data_open (struct archive *a, + void *client_data) +{ + SaveData *save_data = client_data; + LoadData *load_data = LOAD_DATA (save_data); + GFile *parent; + char *basename; + char *tmpname; + + if (load_data->error != NULL) + return ARCHIVE_FATAL; + + parent = g_file_get_parent (fr_archive_get_file (load_data->archive)); + basename = g_file_get_basename (fr_archive_get_file (load_data->archive)); + tmpname = _g_filename_get_random (16, basename); + save_data->tmp_file = g_file_get_child (parent, tmpname); + save_data->ostream = (GOutputStream *) g_file_create (save_data->tmp_file, G_FILE_CREATE_NONE, load_data->cancellable, &load_data->error); + + g_free (tmpname); + g_free (basename); + g_object_unref (parent); + + return (save_data->ostream != NULL) ? ARCHIVE_OK : ARCHIVE_FATAL; +} + + +static ssize_t +save_data_write (struct archive *a, + void *client_data, + const void *buff, + size_t n) +{ + SaveData *save_data = client_data; + LoadData *load_data = LOAD_DATA (save_data); + + if (load_data->error != NULL) + return -1; + + return g_output_stream_write (save_data->ostream, buff, n, load_data->cancellable, &load_data->error); +} + + +static int +save_data_close (struct archive *a, + void *client_data) +{ + SaveData *save_data = client_data; + LoadData *load_data = LOAD_DATA (save_data); + + if (save_data->ostream != NULL) { + GError *error = NULL; + + g_output_stream_close (save_data->ostream, load_data->cancellable, &error); + if (load_data->error == NULL && error != NULL) + load_data->error = g_error_copy (error); + + _g_error_free (error); + } + + if (load_data->error == NULL) + g_file_move (save_data->tmp_file, + fr_archive_get_file (load_data->archive), + G_FILE_COPY_OVERWRITE | G_FILE_COPY_TARGET_DEFAULT_PERMS, + load_data->cancellable, + NULL, + NULL, + &load_data->error); + else + g_file_delete (save_data->tmp_file, NULL, NULL); + + return ARCHIVE_OK; +} + + +static void +_archive_write_set_format_from_context (struct archive *a, + SaveData *save_data) +{ + const char *mime_type; + int archive_filter; + + /* set format and filter from the mime type */ + + mime_type = fr_archive_get_mime_type (LOAD_DATA (save_data)->archive); + archive_filter = ARCHIVE_FILTER_NONE; + + if (_g_str_equal (mime_type, "application/x-bzip-compressed-tar")) { + archive_write_set_format_pax_restricted (a); + archive_filter = ARCHIVE_FILTER_BZIP2; + } + else if (_g_str_equal (mime_type, "application/x-tarz")) { + archive_write_set_format_pax_restricted (a); + archive_filter = ARCHIVE_FILTER_COMPRESS; + } + else if (_g_str_equal (mime_type, "application/x-compressed-tar")) { + archive_write_set_format_pax_restricted (a); + archive_filter = ARCHIVE_FILTER_GZIP; + } + else if (_g_str_equal (mime_type, "application/x-lrzip-compressed-tar")) { + archive_write_set_format_pax_restricted (a); + archive_filter = ARCHIVE_FILTER_LRZIP; + } + else if (_g_str_equal (mime_type, "application/x-lzip-compressed-tar")) { + archive_write_set_format_pax_restricted (a); + archive_filter = ARCHIVE_FILTER_LZIP; + } + else if (_g_str_equal (mime_type, "application/x-lzma-compressed-tar")) { + archive_write_set_format_pax_restricted (a); + archive_filter = ARCHIVE_FILTER_LZMA; + } + else if (_g_str_equal (mime_type, "application/x-lzop-compressed-tar")) { + archive_write_set_format_pax_restricted (a); + archive_filter = ARCHIVE_FILTER_LZOP; + } + else if (_g_str_equal (mime_type, "application/x-xz-compressed-tar")) { + archive_write_set_format_pax_restricted (a); + archive_filter = ARCHIVE_FILTER_XZ; + } + else if (_g_str_equal (mime_type, "application/x-tar")) { + archive_write_add_filter_none (a); + archive_write_set_format_pax_restricted (a); + } + else if (_g_str_equal (mime_type, "application/x-cd-image")) { + archive_write_set_format_iso9660 (a); + } + else if (_g_str_equal (mime_type, "application/x-cpio")) { + archive_write_set_format_cpio (a); + } + else if (_g_str_equal (mime_type, "application/x-xar")) { + archive_write_set_format_xar (a); + } + else if (_g_str_equal (mime_type, "application/x-ar")) { + archive_write_set_format_ar_svr4 (a); + } + else if (_g_str_equal (mime_type, "application/x-7z-compressed")) { + archive_write_set_format_7zip (a); + } + else if (_g_str_equal (mime_type, "application/zip") + || _g_str_equal (mime_type, "application/x-cbz")) { + archive_write_set_format_zip (a); + } + + /* set the filter */ + + if (archive_filter != ARCHIVE_FILTER_NONE) { + char *compression_level = NULL; + + switch (archive_filter) { + case ARCHIVE_FILTER_BZIP2: + archive_write_add_filter_bzip2 (a); + break; + case ARCHIVE_FILTER_COMPRESS: + archive_write_add_filter_compress (a); + break; + case ARCHIVE_FILTER_GZIP: + archive_write_add_filter_gzip (a); + break; + case ARCHIVE_FILTER_LRZIP: + archive_write_add_filter_lrzip (a); + break; + case ARCHIVE_FILTER_LZIP: + archive_write_add_filter_lzip (a); + break; + case ARCHIVE_FILTER_LZMA: + archive_write_add_filter_lzma (a); + break; + case ARCHIVE_FILTER_LZOP: + archive_write_add_filter_lzop (a); + break; + case ARCHIVE_FILTER_XZ: + archive_write_add_filter_xz (a); + break; + default: + break; + } + + /* set the compression level */ + + compression_level = NULL; + switch (save_data->compression) { + case FR_COMPRESSION_VERY_FAST: + compression_level = "1"; + break; + case FR_COMPRESSION_FAST: + compression_level = "3"; + break; + case FR_COMPRESSION_NORMAL: + compression_level = "6"; + break; + case FR_COMPRESSION_MAXIMUM: + compression_level = "9"; + break; + } + if (compression_level != NULL) + archive_write_set_filter_option (a, NULL, "compression-level", compression_level); + } +} + + +/* -- _archive_write_file -- */ + + +static gint64 * +_g_int64_pointer_new (gint64 i) +{ + gint64 *p; + + p = g_new (gint64, 1); + *p = i; + + return p; +} + + +static gboolean +_archive_entry_copy_file_info (struct archive_entry *entry, + GFileInfo *info, + SaveData *save_data) +{ + int filetype; + char *username; + char *groupname; + gint64 id; + + switch (g_file_info_get_file_type (info)) { + case G_FILE_TYPE_REGULAR: + filetype = AE_IFREG; + break; + case G_FILE_TYPE_DIRECTORY: + filetype = AE_IFDIR; + break; + case G_FILE_TYPE_SYMBOLIC_LINK: + filetype = AE_IFLNK; + break; + default: + return FALSE; + break; + } + archive_entry_set_filetype (entry, filetype); + + archive_entry_set_atime (entry, + g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS), + g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) * 1000); + archive_entry_set_ctime (entry, + g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CREATED), + g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CREATED_USEC) * 1000); + archive_entry_set_mtime (entry, + g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED), + g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) * 1000); + archive_entry_unset_birthtime (entry); + archive_entry_set_dev (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_DEVICE)); + archive_entry_set_gid (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID)); + archive_entry_set_uid (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID)); + archive_entry_set_ino64 (entry, g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE)); + archive_entry_set_mode (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE)); + archive_entry_set_nlink (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_NLINK)); + archive_entry_set_rdev (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_RDEV)); + archive_entry_set_size (entry, g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)); + if (filetype == AE_IFLNK) + archive_entry_set_symlink (entry, g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET)); + + /* username */ + + id = archive_entry_uid (entry); + username = g_hash_table_lookup (save_data->usernames, &id); + if (username == NULL) { + struct passwd *pwd = getpwuid (id); + if (pwd != NULL) { + username = g_strdup (pwd->pw_name); + g_hash_table_insert (save_data->usernames, _g_int64_pointer_new (id), username); + } + } + if (username != NULL) + archive_entry_set_uname (entry, username); + + /* groupname */ + + id = archive_entry_gid (entry); + groupname = g_hash_table_lookup (save_data->groupnames, &id); + if (groupname == NULL) { + struct group *grp = getgrgid (id); + if (grp != NULL) { + groupname = g_strdup (grp->gr_name); + g_hash_table_insert (save_data->groupnames, _g_int64_pointer_new (id), groupname); + } + } + if (groupname != NULL) + archive_entry_set_gname (entry, groupname); + + return TRUE; +} + + +static WriteAction +_archive_write_file (struct archive *b, + SaveData *save_data, + AddFile *add_file, + gboolean follow_link, + struct archive_entry *r_entry, + GCancellable *cancellable) +{ + LoadData *load_data = LOAD_DATA (save_data); + GFileInfo *info; + struct archive_entry *w_entry; + int rb; + + /* write the file header */ + + info = g_file_query_info (add_file->file, + FILE_ATTRIBUTES_NEEDED_BY_ARCHIVE_ENTRY, + (! follow_link ? G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS : 0), + cancellable, + &load_data->error); + if (info == NULL) + return WRITE_ACTION_ABORT; + + w_entry = archive_entry_new (); + if (! _archive_entry_copy_file_info (w_entry, info, save_data)) { + archive_entry_free (w_entry); + g_object_unref (info); + return WRITE_ACTION_SKIP_ENTRY; + } + + /* honor the update flag */ + + if (save_data->update && (r_entry != NULL) && (archive_entry_mtime (w_entry) < archive_entry_mtime (r_entry))) { + archive_entry_free (w_entry); + g_object_unref (info); + return WRITE_ACTION_WRITE_ENTRY; + } + + archive_entry_set_pathname (w_entry, add_file->pathname); + rb = archive_write_header (b, w_entry); + + /* write the file data */ + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR) { + GInputStream *istream; + + istream = (GInputStream *) g_file_read (add_file->file, cancellable, &load_data->error); + if (istream != NULL) { + gssize bytes_read; + + while ((bytes_read = g_input_stream_read (istream, save_data->buffer, save_data->buffer_size, cancellable, &load_data->error)) > 0) { + archive_write_data (b, save_data->buffer, bytes_read); + fr_archive_progress_inc_completed_bytes (load_data->archive, bytes_read); + } + + g_object_unref (istream); + } + } + + rb = archive_write_finish_entry (b); + + if ((load_data->error == NULL) && (rb <= ARCHIVE_FAILED)) + load_data->error = g_error_new_literal (FR_ERROR, FR_ERROR_COMMAND_ERROR, archive_error_string (b)); + + archive_entry_free (w_entry); + g_object_unref (info); + + return (load_data->error == NULL) ? WRITE_ACTION_SKIP_ENTRY : WRITE_ACTION_ABORT; +} + + +static void +save_archive_thread (GSimpleAsyncResult *result, + GObject *object, + GCancellable *cancellable) +{ + SaveData *save_data; + LoadData *load_data; + struct archive *a, *b; + struct archive_entry *r_entry; + int ra = ARCHIVE_OK, rb = ARCHIVE_OK; + + save_data = g_simple_async_result_get_op_res_gpointer (result); + load_data = LOAD_DATA (save_data); + + save_data->b = b = archive_write_new (); + _archive_write_set_format_from_context (b, save_data); + archive_write_open (b, save_data, save_data_open, save_data_write, save_data_close); + archive_write_set_bytes_in_last_block (b, 1); + + a = archive_read_new (); + archive_read_support_filter_all (a); + archive_read_support_format_all (a); + archive_read_open (a, load_data, load_data_open, load_data_read, load_data_close); + + if (save_data->begin_operation != NULL) + save_data->begin_operation (save_data, save_data->user_data); + + while ((load_data->error == NULL) && (ra = archive_read_next_header (a, &r_entry)) == ARCHIVE_OK) { + struct archive_entry *w_entry; + WriteAction action; + + if (g_cancellable_is_cancelled (cancellable)) + break; + + action = WRITE_ACTION_WRITE_ENTRY; + w_entry = archive_entry_clone (r_entry); + if (save_data->entry_action != NULL) + action = save_data->entry_action (save_data, w_entry, save_data->user_data); + + if (action == WRITE_ACTION_WRITE_ENTRY) { + const void *buffer; + size_t buffer_size; + __LA_INT64_T offset; + + rb = archive_write_header (b, w_entry); + if (rb <= ARCHIVE_FAILED) { + load_data->error = _g_error_new_from_archive_error (archive_error_string (b)); + break; + } + + switch (archive_entry_filetype (r_entry)) { + case AE_IFREG: + while ((ra = archive_read_data_block (a, &buffer, &buffer_size, &offset)) == ARCHIVE_OK) { + archive_write_data (b, buffer, buffer_size); + fr_archive_progress_inc_completed_bytes (load_data->archive, buffer_size); + } + + if (ra <= ARCHIVE_FAILED) { + load_data->error = _g_error_new_from_archive_error (archive_error_string (a)); + break; + } + break; + + default: + break; + } + + rb = archive_write_finish_entry (b); + } + else if (action == WRITE_ACTION_SKIP_ENTRY) + fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size (r_entry)); + + archive_entry_free (w_entry); + } + + if (g_error_matches (load_data->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + ra = ARCHIVE_EOF; + + if (save_data->end_operation != NULL) + save_data->end_operation (save_data, save_data->user_data); + + rb = archive_write_close (b); + + if ((load_data->error == NULL) && (ra != ARCHIVE_EOF)) + load_data->error = _g_error_new_from_archive_error (archive_error_string (a)); + if ((load_data->error == NULL) && (rb <= ARCHIVE_FAILED)) + load_data->error = _g_error_new_from_archive_error (archive_error_string (b)); + if (load_data->error == NULL) + g_cancellable_set_error_if_cancelled (cancellable, &load_data->error); + if (load_data->error != NULL) + g_simple_async_result_set_from_error (result, load_data->error); + + archive_read_free (a); + archive_write_free (b); + save_data_free (save_data); +} + + +static void +_fr_archive_libarchive_save (FrArchive *archive, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size, + GCancellable *cancellable, + GSimpleAsyncResult *result, + SaveDataFunc begin_operation, + SaveDataFunc end_operation, + EntryActionFunc entry_action, + gpointer user_data, + GDestroyNotify notify) +{ + SaveData *save_data; + LoadData *load_data; + + save_data = g_new0 (SaveData, 1); + save_data_init (SAVE_DATA (save_data)); + + load_data = LOAD_DATA (save_data); + load_data->archive = g_object_ref (archive); + load_data->cancellable = _g_object_ref (cancellable); + load_data->result = result; + + save_data->update = update; + save_data->password = g_strdup (password); + save_data->encrypt_header = encrypt_header; + save_data->compression = compression; + save_data->volume_size = volume_size; + save_data->begin_operation = begin_operation; + save_data->end_operation = end_operation; + save_data->entry_action = entry_action; + save_data->user_data = user_data; + save_data->user_data_notify = notify; + + g_simple_async_result_set_op_res_gpointer (load_data->result, save_data, NULL); + g_simple_async_result_run_in_thread (load_data->result, + save_archive_thread, + G_PRIORITY_DEFAULT, + cancellable); +} + + +/* -- add_files -- */ + + +typedef struct { + gboolean follow_links; + GHashTable *files_to_add; + int n_files_to_add; +} AddData; + + +static AddData * +add_data_new (void) +{ + AddData *add_data; + + add_data = g_new0 (AddData, 1); + add_data->files_to_add = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) add_file_free); + add_data->n_files_to_add = 0; + add_data->follow_links = TRUE; + + return add_data; +} + + +static void +add_data_free (AddData *add_data) +{ + g_hash_table_unref (add_data->files_to_add); + g_free (add_data); +} + + +static void +_add_files_begin (SaveData *save_data, + gpointer user_data) +{ + AddData *add_data = user_data; + LoadData *load_data = LOAD_DATA (save_data); + + fr_archive_progress_set_total_files (load_data->archive, add_data->n_files_to_add); + + if (load_data->archive->files_to_add_size == 0) { + GList *files_to_add; + GList *scan; + + files_to_add = g_hash_table_get_values (add_data->files_to_add); + for (scan = files_to_add; scan; scan = scan->next) { + AddFile *add_file = scan->data; + + if (g_cancellable_is_cancelled (load_data->cancellable)) + break; + + load_data->archive->files_to_add_size += _g_file_get_size (add_file->file, load_data->cancellable); + } + + g_list_free (files_to_add); + } + + fr_archive_progress_set_total_bytes (load_data->archive, + FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->uncompressed_size + load_data->archive->files_to_add_size); +} + + +static WriteAction +_add_files_entry_action (SaveData *save_data, + struct archive_entry *w_entry, + gpointer user_data) +{ + AddData *add_data = user_data; + LoadData *load_data = LOAD_DATA (save_data); + WriteAction action; + const char *pathname; + AddFile *add_file; + + action = WRITE_ACTION_WRITE_ENTRY; + pathname = archive_entry_pathname (w_entry); + add_file = g_hash_table_lookup (add_data->files_to_add, pathname); + if (add_file != NULL) { + action = _archive_write_file (save_data->b, + save_data, + add_file, + add_data->follow_links, + w_entry, + load_data->cancellable); + fr_archive_progress_inc_completed_files (load_data->archive, 1); + add_data->n_files_to_add--; + g_hash_table_remove (add_data->files_to_add, pathname); + } + + return action; +} + + +static void +_add_files_end (SaveData *save_data, + gpointer user_data) +{ + AddData *add_data = user_data; + LoadData *load_data = LOAD_DATA (save_data); + GList *remaining_files; + GList *scan; + + /* allow to add files to a new archive */ + + if (g_error_matches (load_data->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + g_clear_error (&load_data->error); + + /* add the files that weren't present in the archive already */ + + remaining_files = g_hash_table_get_values (add_data->files_to_add); + for (scan = remaining_files; (load_data->error == NULL) && scan; scan = scan->next) { + AddFile *add_file = scan->data; + + if (g_cancellable_is_cancelled (load_data->cancellable)) + break; + + if (_archive_write_file (save_data->b, + save_data, + add_file, + add_data->follow_links, + NULL, + load_data->cancellable) == WRITE_ACTION_ABORT) + { + break; + } + + fr_archive_progress_inc_completed_files (load_data->archive, 1); + } + + g_list_free (remaining_files); +} + + +static void +fr_archive_libarchive_add_files (FrArchive *archive, + GList *file_list, + GFile *base_dir, + const char *dest_dir, + gboolean update, + gboolean follow_links, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + AddData *add_data; + GList *scan; + + g_return_if_fail (base_dir != NULL); + + add_data = add_data_new (); + add_data->follow_links = follow_links; + + if (dest_dir != NULL) + dest_dir = (dest_dir[0] == '/' ? dest_dir + 1 : dest_dir); + else + dest_dir = ""; + + for (scan = file_list; scan; scan = scan->next) { + GFile *file = G_FILE (scan->data); + char *relative_pathname; + char *archive_pathname; + + relative_pathname = g_file_get_relative_path (base_dir, file); + archive_pathname = g_build_filename (dest_dir, relative_pathname, NULL); + g_hash_table_insert (add_data->files_to_add, + g_strdup (archive_pathname), + add_file_new (file, archive_pathname)); + add_data->n_files_to_add++; + + g_free (archive_pathname); + g_free (relative_pathname); + } + + _fr_archive_libarchive_save (archive, + update, + password, + encrypt_header, + compression, + volume_size, + cancellable, + g_simple_async_result_new (G_OBJECT (archive), + callback, + user_data, + fr_archive_add_files), + _add_files_begin, + _add_files_end, + _add_files_entry_action, + add_data, + (GDestroyNotify) add_data_free); +} + + +/* -- remove -- */ + + +typedef struct { + GHashTable *files_to_remove; + gboolean remove_all_files; + int n_files_to_remove; +} RemoveData; + + +static void +remove_data_free (RemoveData *remove_data) +{ + if (remove_data->files_to_remove != NULL) + g_hash_table_unref (remove_data->files_to_remove); + g_free (remove_data); +} + + +static void +_remove_files_begin (SaveData *save_data, + gpointer user_data) +{ + LoadData *load_data = LOAD_DATA (save_data); + RemoveData *remove_data = user_data; + + fr_archive_progress_set_total_files (load_data->archive, remove_data->n_files_to_remove); + fr_archive_progress_set_total_bytes (load_data->archive, + FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->uncompressed_size); +} + + +static WriteAction +_remove_files_entry_action (SaveData *save_data, + struct archive_entry *w_entry, + gpointer user_data) +{ + RemoveData *remove_data = user_data; + LoadData *load_data = LOAD_DATA (save_data); + WriteAction action; + const char *pathname; + + if (remove_data->remove_all_files) + return WRITE_ACTION_SKIP_ENTRY; + + action = WRITE_ACTION_WRITE_ENTRY; + pathname = archive_entry_pathname (w_entry); + if (g_hash_table_lookup (remove_data->files_to_remove, pathname) != NULL) { + action = WRITE_ACTION_SKIP_ENTRY; + remove_data->n_files_to_remove--; + fr_archive_progress_inc_completed_files (load_data->archive, 1); + g_hash_table_remove (remove_data->files_to_remove, pathname); + } + + return action; +} + + +static void +fr_archive_libarchive_remove_files (FrArchive *archive, + GList *file_list, + FrCompression compression, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RemoveData *remove_data; + GList *scan; + + remove_data = g_new0 (RemoveData, 1); + remove_data->remove_all_files = (file_list == NULL); + if (! remove_data->remove_all_files) { + remove_data->files_to_remove = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + remove_data->n_files_to_remove = 0; + for (scan = file_list; scan; scan = scan->next) { + g_hash_table_insert (remove_data->files_to_remove, g_strdup (scan->data), GINT_TO_POINTER (1)); + remove_data->n_files_to_remove++; + } + } + else + remove_data->n_files_to_remove = archive->files->len; + + _fr_archive_libarchive_save (archive, + FALSE, + archive->password, + archive->encrypt_header, + compression, + 0, + cancellable, + g_simple_async_result_new (G_OBJECT (archive), + callback, + user_data, + fr_archive_remove), + _remove_files_begin, + NULL, + _remove_files_entry_action, + remove_data, + (GDestroyNotify) remove_data_free); +} + + +/* -- fr_archive_libarchive_rename -- */ + + +typedef struct { + GHashTable *files_to_rename; + int n_files_to_rename; +} RenameData; + + +static void +rename_data_free (RenameData *rename_data) +{ + g_hash_table_unref (rename_data->files_to_rename); + g_free (rename_data); +} + + +static void +_rename_files_begin (SaveData *save_data, + gpointer user_data) +{ + LoadData *load_data = LOAD_DATA (save_data); + RenameData *rename_data = user_data; + + fr_archive_progress_set_total_files (load_data->archive, rename_data->n_files_to_rename); + fr_archive_progress_set_total_bytes (load_data->archive, + FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->uncompressed_size); +} + + +static WriteAction +_rename_files_entry_action (SaveData *save_data, + struct archive_entry *w_entry, + gpointer user_data) +{ + LoadData *load_data = LOAD_DATA (save_data); + RenameData *rename_data = user_data; + WriteAction action; + const char *pathname; + char *new_pathname; + + action = WRITE_ACTION_WRITE_ENTRY; + pathname = archive_entry_pathname (w_entry); + new_pathname = g_hash_table_lookup (rename_data->files_to_rename, pathname); + if (new_pathname != NULL) { + archive_entry_set_pathname (w_entry, new_pathname); + rename_data->n_files_to_rename--; + g_hash_table_remove (rename_data->files_to_rename, pathname); + fr_archive_progress_inc_completed_files (load_data->archive, 1); + } + + return action; +} + + +static void +fr_archive_libarchive_rename (FrArchive *archive, + GList *file_list, + const char *old_name, + const char *new_name, + const char *current_dir, + gboolean is_dir, + gboolean dir_in_archive, + const char *original_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RenameData *rename_data; + + rename_data = g_new0 (RenameData, 1); + rename_data->files_to_rename = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + rename_data->n_files_to_rename = 0; + + if (is_dir) { + char *old_dirname; + char *new_dirname; + int old_dirname_len; + GList *scan; + + old_dirname = g_build_filename (current_dir + 1, old_name, "/", NULL); + old_dirname_len = strlen (old_dirname); + new_dirname = g_build_filename (current_dir + 1, new_name, "/", NULL); + + for (scan = file_list; scan; scan = scan->next) { + char *old_pathname = scan->data; + char *new_pathname; + + new_pathname = g_build_filename (new_dirname, old_pathname + old_dirname_len, NULL); + g_hash_table_insert (rename_data->files_to_rename, g_strdup (old_pathname), new_pathname); + rename_data->n_files_to_rename++; + } + + g_free (new_dirname); + g_free (old_dirname); + } + else { + char *old_pathname = (char *) file_list->data; + char *new_pathname; + + new_pathname = g_build_filename (current_dir + 1, new_name, NULL); + g_hash_table_insert (rename_data->files_to_rename, + g_strdup (old_pathname), + new_pathname); + rename_data->n_files_to_rename = 1; + } + + _fr_archive_libarchive_save (archive, + FALSE, + archive->password, + archive->encrypt_header, + archive->compression, + 0, + cancellable, + g_simple_async_result_new (G_OBJECT (archive), + callback, + user_data, + fr_archive_rename), + _rename_files_begin, + NULL, + _rename_files_entry_action, + rename_data, + (GDestroyNotify) rename_data_free); +} + + +/* -- fr_archive_libarchive_paste_clipboard -- */ + + +static void +fr_archive_libarchive_paste_clipboard (FrArchive *archive, + GFile *archive_file, + char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size, + FrClipboardOp op, + char *base_dir, + GList *files, + GFile *tmp_dir, + char *current_dir, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + AddData *add_data; + GList *scan; + + g_return_if_fail (base_dir != NULL); + + add_data = add_data_new (); + + current_dir = current_dir + 1; + for (scan = files; scan; scan = scan->next) { + const char *old_name = (char *) scan->data; + char *new_name; + GFile *file; + + new_name = g_build_filename (current_dir, old_name + strlen (base_dir) - 1, NULL); + file = _g_file_append_path (tmp_dir, old_name, NULL); + g_hash_table_insert (add_data->files_to_add, new_name, add_file_new (file, new_name)); + add_data->n_files_to_add++; + + g_object_unref (file); + } + + _fr_archive_libarchive_save (archive, + FALSE, + password, + encrypt_header, + compression, + volume_size, + cancellable, + g_simple_async_result_new (G_OBJECT (archive), + callback, + user_data, + fr_archive_paste_clipboard), + _add_files_begin, + _add_files_end, + _add_files_entry_action, + add_data, + (GDestroyNotify) add_data_free); +} + + +/* -- fr_archive_libarchive_add_dropped_files -- */ + + +static void +fr_archive_libarchive_add_dropped_files (FrArchive *archive, + GList *file_list, + const char *dest_dir, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + AddData *add_data; + GList *scan; + + add_data = add_data_new (); + + if (dest_dir[0] == '/') + dest_dir += 1; + + for (scan = file_list; scan; scan = scan->next) { + GFile *file = G_FILE (scan->data); + char *basename; + char *archive_pathname; + + basename = g_file_get_basename (file); + archive_pathname = g_build_filename (dest_dir, basename, NULL); + g_hash_table_insert (add_data->files_to_add, + g_strdup (archive_pathname), + add_file_new (file, archive_pathname)); + + g_free (archive_pathname); + g_free (basename); + } + + _fr_archive_libarchive_save (archive, + FALSE, + password, + encrypt_header, + compression, + volume_size, + cancellable, + g_simple_async_result_new (G_OBJECT (archive), + callback, + user_data, + fr_archive_add_dropped_items), + _add_files_begin, + _add_files_end, + _add_files_entry_action, + add_data, + (GDestroyNotify) add_data_free); +} + + +/* -- fr_archive_libarchive_update_open_files -- */ + + +static void +fr_archive_libarchive_update_open_files (FrArchive *archive, + GList *file_list, + GList *dir_list, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + AddData *add_data; + GList *scan_file; + GList *scan_dir; + + add_data = add_data_new (); + + for (scan_file = file_list, scan_dir = dir_list; + scan_file && scan_dir; + scan_file = scan_file->next, scan_dir = scan_dir->next) + { + GFile *temp_dir = G_FILE (scan_dir->data); + GFile *extracted_file = G_FILE (scan_file->data); + char *archive_pathname; + + archive_pathname = g_file_get_relative_path (temp_dir, extracted_file); + g_hash_table_insert (add_data->files_to_add, g_strdup (archive_pathname), add_file_new (extracted_file, archive_pathname)); + add_data->n_files_to_add++; + + g_free (archive_pathname); + } + + _fr_archive_libarchive_save (archive, + FALSE, + password, + encrypt_header, + compression, + volume_size, + cancellable, + g_simple_async_result_new (G_OBJECT (archive), + callback, + user_data, + fr_archive_update_open_files), + _add_files_begin, + _add_files_end, + _add_files_entry_action, + add_data, + (GDestroyNotify) add_data_free); +} + + +static void +fr_archive_libarchive_class_init (FrArchiveLibarchiveClass *klass) +{ + GObjectClass *gobject_class; + FrArchiveClass *archive_class; + + fr_archive_libarchive_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (klass, sizeof (FrArchiveLibarchivePrivate)); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = fr_archive_libarchive_finalize; + + archive_class = FR_ARCHIVE_CLASS (klass); + archive_class->get_mime_types = fr_archive_libarchive_get_mime_types; + archive_class->get_capabilities = fr_archive_libarchive_get_capabilities; + archive_class->get_packages = fr_archive_libarchive_get_packages; + archive_class->list = fr_archive_libarchive_list; + archive_class->extract_files = fr_archive_libarchive_extract_files; + archive_class->add_files = fr_archive_libarchive_add_files; + archive_class->remove_files = fr_archive_libarchive_remove_files; + archive_class->rename = fr_archive_libarchive_rename; + archive_class->paste_clipboard = fr_archive_libarchive_paste_clipboard; + archive_class->add_dropped_files = fr_archive_libarchive_add_dropped_files; + archive_class->update_open_files = fr_archive_libarchive_update_open_files; +} + + +static void +fr_archive_libarchive_init (FrArchiveLibarchive *self) +{ + FrArchive *base = FR_ARCHIVE (self); + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FR_TYPE_ARCHIVE_LIBARCHIVE, FrArchiveLibarchivePrivate); + + base->propAddCanReplace = TRUE; + base->propAddCanUpdate = TRUE; + base->propAddCanStoreFolders = TRUE; + base->propAddCanStoreLinks = TRUE; + base->propExtractCanAvoidOverwrite = TRUE; + base->propExtractCanSkipOlder = TRUE; + base->propExtractCanJunkPaths = TRUE; + base->propCanExtractAll = TRUE; + base->propCanDeleteNonEmptyFolders = TRUE; + base->propCanExtractNonEmptyFolders = TRUE; +} diff -Nru file-roller-3.28.0/.pc/.quilt_patches file-roller-3.28.0/.pc/.quilt_patches --- file-roller-3.28.0/.pc/.quilt_patches 2019-09-25 19:32:21.000000000 +0000 +++ file-roller-3.28.0/.pc/.quilt_patches 2020-06-05 09:40:57.000000000 +0000 @@ -1 +1 @@ -/home/buildd/build-RECIPEBRANCHBUILD-2412358/chroot-autobuild/home/buildd/work/tree/recipe/debian/patches +/home/buildd/build-RECIPEBRANCHBUILD-2581569/chroot-autobuild/home/buildd/work/tree/recipe/debian/patches diff -Nru file-roller-3.28.0/.pc/.quilt_series file-roller-3.28.0/.pc/.quilt_series --- file-roller-3.28.0/.pc/.quilt_series 2019-09-25 19:32:21.000000000 +0000 +++ file-roller-3.28.0/.pc/.quilt_series 2020-06-05 09:40:57.000000000 +0000 @@ -1 +1 @@ -/home/buildd/build-RECIPEBRANCHBUILD-2412358/chroot-autobuild/home/buildd/work/tree/recipe/debian/patches/series +/home/buildd/build-RECIPEBRANCHBUILD-2581569/chroot-autobuild/home/buildd/work/tree/recipe/debian/patches/series diff -Nru file-roller-3.28.0/src/fr-archive-libarchive.c file-roller-3.28.0/src/fr-archive-libarchive.c --- file-roller-3.28.0/src/fr-archive-libarchive.c 2019-09-25 19:32:20.000000000 +0000 +++ file-roller-3.28.0/src/fr-archive-libarchive.c 2020-06-05 09:40:57.000000000 +0000 @@ -601,6 +601,149 @@ } +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, @@ -611,6 +754,7 @@ GHashTable *checked_folders; GHashTable *created_files; GHashTable *folders_created_during_extraction; + GHashTable *external_links; struct archive *a; struct archive_entry *entry; int r; @@ -621,6 +765,7 @@ 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); a = archive_read_new (); @@ -652,6 +797,15 @@ 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; } @@ -860,6 +1014,8 @@ 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; @@ -894,6 +1050,7 @@ 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); }