1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * File-Roller
5 *
6 * Copyright (C) 2012 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <pwd.h>
26 #include <grp.h>
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gio/gio.h>
30 #include <archive.h>
31 #include <archive_entry.h>
32 #include "file-data.h"
33 #include "file-utils.h"
34 #include "fr-error.h"
35 #include "fr-archive-libarchive.h"
36 #include "gio-utils.h"
37 #include "glib-utils.h"
38 #include "typedefs.h"
39
40
41 #define BUFFER_SIZE (64 * 1024)
42 #define FILE_ATTRIBUTES_NEEDED_BY_ARCHIVE_ENTRY ("standard::*,time::*,access::*,unix::*")
43
44
45 struct _FrArchiveLibarchivePrivate {
46 gssize compressed_size;
47 gssize uncompressed_size;
48 };
49
50
G_DEFINE_TYPE_WITH_PRIVATE(FrArchiveLibarchive,fr_archive_libarchive,FR_TYPE_ARCHIVE)51 G_DEFINE_TYPE_WITH_PRIVATE (FrArchiveLibarchive, fr_archive_libarchive, FR_TYPE_ARCHIVE)
52
53
54 static void
55 fr_archive_libarchive_finalize (GObject *object)
56 {
57 /*FrArchiveLibarchive *self;*/
58
59 g_return_if_fail (object != NULL);
60 g_return_if_fail (FR_IS_ARCHIVE_LIBARCHIVE (object));
61
62 /*self = FR_ARCHIVE_LIBARCHIVE (object);*/
63
64 if (G_OBJECT_CLASS (fr_archive_libarchive_parent_class)->finalize)
65 G_OBJECT_CLASS (fr_archive_libarchive_parent_class)->finalize (object);
66 }
67
68
69 const char *libarchiver_mime_types[] = {
70 "application/epub+zip",
71 "application/vnd.debian.binary-package",
72 "application/vnd.ms-cab-compressed",
73 "application/x-7z-compressed",
74 "application/x-ar",
75 "application/x-bzip-compressed-tar",
76 "application/x-cbr",
77 "application/x-cbz",
78 "application/x-cd-image",
79 "application/x-compressed-tar",
80 "application/x-cpio",
81 "application/x-deb",
82 "application/x-lha",
83 "application/x-lrzip-compressed-tar",
84 "application/x-lzip-compressed-tar",
85 "application/x-lzma-compressed-tar",
86 "application/x-rar",
87 "application/x-rpm",
88 "application/x-tar",
89 "application/x-tarz",
90 "application/x-tzo",
91 "application/x-xar",
92 "application/x-xz-compressed-tar",
93 #if (ARCHIVE_VERSION_NUMBER >= 3003003)
94 "application/x-zstd-compressed-tar",
95 #endif
96 "application/zip",
97 NULL
98 };
99
100
101 static const char **
fr_archive_libarchive_get_mime_types(FrArchive * archive)102 fr_archive_libarchive_get_mime_types (FrArchive *archive)
103 {
104 return libarchiver_mime_types;
105 }
106
107
108 static FrArchiveCap
fr_archive_libarchive_get_capabilities(FrArchive * archive,const char * mime_type,gboolean check_command)109 fr_archive_libarchive_get_capabilities (FrArchive *archive,
110 const char *mime_type,
111 gboolean check_command)
112 {
113 FrArchiveCap capabilities;
114
115 capabilities = FR_ARCHIVE_CAN_STORE_MANY_FILES;
116
117 /* give priority to 7z* for 7z archives. */
118 if (strcmp (mime_type, "application/x-7z-compressed") == 0) {
119 if (_g_program_is_available ("7za", check_command)
120 || _g_program_is_available ("7zr", check_command)
121 || _g_program_is_available ("7z", check_command))
122 {
123 return capabilities;
124 }
125 }
126
127 /* give priority to 7za that supports CAB files better. */
128 if ((strcmp (mime_type, "application/vnd.ms-cab-compressed") == 0)
129 && _g_program_is_available ("7za", check_command))
130 {
131 return capabilities;
132 }
133
134 /* give priority to 7z, unzip and zip that supports ZIP files better. */
135 if ((strcmp (mime_type, "application/zip") == 0)
136 || (strcmp (mime_type, "application/x-cbz") == 0))
137 {
138 if (_g_program_is_available ("7z", check_command)) {
139 return capabilities;
140 }
141 if (!_g_program_is_available ("unzip", check_command)) {
142 capabilities |= FR_ARCHIVE_CAN_READ;
143 }
144 if (!_g_program_is_available ("zip", check_command)) {
145 capabilities |= FR_ARCHIVE_CAN_WRITE;
146 }
147 return capabilities;
148 }
149
150 /* give priority to utilities that support RAR files better. */
151 if ((strcmp (mime_type, "application/x-rar") == 0)
152 || (strcmp (mime_type, "application/x-cbr") == 0))
153 {
154 if (_g_program_is_available ("rar", check_command)
155 || _g_program_is_available ("unrar", check_command)
156 || _g_program_is_available ("unar", check_command)) {
157 return capabilities;
158 }
159 }
160
161 /* tar.lrz format requires external lrzip */
162 if (strcmp (mime_type, "application/x-lrzip-compressed-tar") == 0) {
163 if (!_g_program_is_available ("lrzip", check_command))
164 return capabilities;
165 }
166
167 capabilities |= FR_ARCHIVE_CAN_READ;
168
169 /* read-only formats */
170 if ((strcmp (mime_type, "application/vnd.ms-cab-compressed") == 0)
171 || (strcmp (mime_type, "application/x-cbr") == 0)
172 || (strcmp (mime_type, "application/x-deb") == 0)
173 || (strcmp (mime_type, "application/vnd.debian.binary-package") == 0)
174 || (strcmp (mime_type, "application/x-lha") == 0)
175 || (strcmp (mime_type, "application/x-rar") == 0)
176 || (strcmp (mime_type, "application/x-rpm") == 0)
177 || (strcmp (mime_type, "application/x-xar") == 0))
178 {
179 return capabilities;
180 }
181
182 /* all other formats can be read and written */
183 capabilities |= FR_ARCHIVE_CAN_WRITE;
184
185 return capabilities;
186 }
187
188
189 static const char *
fr_archive_libarchive_get_packages(FrArchive * archive,const char * mime_type)190 fr_archive_libarchive_get_packages (FrArchive *archive,
191 const char *mime_type)
192 {
193 return NULL;
194 }
195
196
197 /* LoadData */
198
199
200 #define LOAD_DATA(x) ((LoadData *)(x))
201
202
203 typedef struct {
204 FrArchive *archive;
205 GCancellable *cancellable;
206 GSimpleAsyncResult *result;
207 GInputStream *istream;
208 void *buffer;
209 gssize buffer_size;
210 GError *error;
211 } LoadData;
212
213
214 static void
load_data_init(LoadData * load_data)215 load_data_init (LoadData *load_data)
216 {
217 load_data->buffer_size = BUFFER_SIZE;
218 load_data->buffer = g_new (char, load_data->buffer_size);
219 }
220
221
222 static void
load_data_free(LoadData * load_data)223 load_data_free (LoadData *load_data)
224 {
225 _g_object_unref (load_data->archive);
226 _g_object_unref (load_data->cancellable);
227 _g_object_unref (load_data->result);
228 _g_object_unref (load_data->istream);
229 g_free (load_data->buffer);
230 g_free (load_data);
231 }
232
233
234 static int
load_data_open(struct archive * a,void * client_data)235 load_data_open (struct archive *a,
236 void *client_data)
237 {
238 LoadData *load_data = client_data;
239
240 if (load_data->error != NULL)
241 return ARCHIVE_FATAL;
242
243 if (g_simple_async_result_get_source_tag (load_data->result) == fr_archive_list) {
244 FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->compressed_size = 0;
245 FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->uncompressed_size = 0;
246 }
247
248 load_data->istream = (GInputStream *) g_file_read (fr_archive_get_file (load_data->archive),
249 load_data->cancellable,
250 &load_data->error);
251 return (load_data->error == NULL) ? ARCHIVE_OK : ARCHIVE_FATAL;
252 }
253
254
255 static ssize_t
load_data_read(struct archive * a,void * client_data,const void ** buff)256 load_data_read (struct archive *a,
257 void *client_data,
258 const void **buff)
259 {
260 LoadData *load_data = client_data;
261 gssize bytes;
262
263 if (load_data->error != NULL)
264 return -1;
265
266 *buff = load_data->buffer;
267 bytes = g_input_stream_read (load_data->istream,
268 load_data->buffer,
269 load_data->buffer_size,
270 load_data->cancellable,
271 &load_data->error);
272
273 /* update the progress only if listing the content */
274 if (g_simple_async_result_get_source_tag (load_data->result) == fr_archive_list) {
275 fr_archive_progress_set_completed_bytes (load_data->archive,
276 g_seekable_tell (G_SEEKABLE (load_data->istream)));
277 FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->compressed_size += bytes;
278 }
279
280 return bytes;
281 }
282
283
284 static gint64
load_data_seek(struct archive * a,void * client_data,gint64 request,int whence)285 load_data_seek (struct archive *a,
286 void *client_data,
287 gint64 request,
288 int whence)
289 {
290 GSeekable *seekable;
291 GSeekType seektype;
292 off_t new_offset;
293
294 LoadData *load_data = client_data;
295
296 seekable = (GSeekable*)(load_data->istream);
297 if ((load_data->error != NULL) || (load_data->istream == NULL))
298 return -1;
299
300 switch (whence) {
301 case SEEK_SET:
302 seektype = G_SEEK_SET;
303 break;
304 case SEEK_CUR:
305 seektype = G_SEEK_CUR;
306 break;
307 case SEEK_END:
308 seektype = G_SEEK_END;
309 break;
310 default:
311 return -1;
312 }
313
314 g_seekable_seek (seekable,
315 request,
316 seektype,
317 load_data->cancellable,
318 &load_data->error);
319 new_offset = g_seekable_tell (seekable);
320 if (load_data->error != NULL)
321 return -1;
322
323 return new_offset;
324 }
325
326
327 static gint64
load_data_skip(struct archive * a,void * client_data,gint64 request)328 load_data_skip (struct archive *a,
329 void *client_data,
330 gint64 request)
331 {
332 GSeekable *seekable;
333 off_t old_offset, new_offset;
334
335 LoadData *load_data = client_data;
336
337 seekable = (GSeekable*)(load_data->istream);
338 if (load_data->error != NULL || load_data->istream == NULL)
339 return -1;
340
341 old_offset = g_seekable_tell (seekable);
342 new_offset = load_data_seek (a, client_data, request, SEEK_CUR);
343 if (new_offset > old_offset)
344 return (new_offset - old_offset);
345
346 return 0;
347 }
348
349
350
351 static int
load_data_close(struct archive * a,void * client_data)352 load_data_close (struct archive *a,
353 void *client_data)
354 {
355 LoadData *load_data = client_data;
356
357 if (load_data->error != NULL)
358 return ARCHIVE_FATAL;
359
360 if (load_data->istream != NULL) {
361 _g_object_unref (load_data->istream);
362 load_data->istream = NULL;
363 }
364
365 return ARCHIVE_OK;
366 }
367
368
369 static int
create_read_object(LoadData * load_data,struct archive ** a)370 create_read_object (LoadData *load_data,
371 struct archive **a)
372 {
373 *a = archive_read_new ();
374 archive_read_support_filter_all (*a);
375 archive_read_support_format_all (*a);
376
377 archive_read_set_open_callback (*a, load_data_open);
378 archive_read_set_read_callback (*a, load_data_read);
379 archive_read_set_close_callback (*a, load_data_close);
380 archive_read_set_seek_callback (*a, load_data_seek);
381 archive_read_set_skip_callback (*a, load_data_skip);
382 archive_read_set_callback_data (*a, load_data);
383
384 return archive_read_open1 (*a);
385 }
386
387
388 /* -- list -- */
389
390
391 static goffset
_g_file_get_size(GFile * file,GCancellable * cancellable)392 _g_file_get_size (GFile *file,
393 GCancellable *cancellable)
394 {
395 GFileInfo *info;
396 goffset size;
397
398 info = g_file_query_info (file,
399 G_FILE_ATTRIBUTE_STANDARD_SIZE,
400 G_FILE_QUERY_INFO_NONE,
401 cancellable,
402 NULL);
403 if (info == NULL)
404 return 0;
405
406 size = g_file_info_get_size (info);
407 g_object_unref (info);
408
409 return size;
410 }
411
412
413 static GError *
_g_error_new_from_archive_error(const char * s)414 _g_error_new_from_archive_error (const char *s)
415 {
416 char *msg;
417 GError *error;
418
419 msg = (s != NULL) ? g_locale_to_utf8 (s, -1, NULL, NULL, NULL) : NULL;
420 if (msg == NULL)
421 msg = g_strdup ("Fatal error");
422 error = g_error_new_literal (FR_ERROR, FR_ERROR_COMMAND_ERROR, msg);
423
424 g_free (msg);
425
426 return error;
427 }
428
429
430 static void
list_archive_thread(GSimpleAsyncResult * result,GObject * object,GCancellable * cancellable)431 list_archive_thread (GSimpleAsyncResult *result,
432 GObject *object,
433 GCancellable *cancellable)
434 {
435 LoadData *load_data;
436 struct archive *a;
437 struct archive_entry *entry;
438 int r;
439
440 load_data = g_simple_async_result_get_op_res_gpointer (result);
441
442 fr_archive_progress_set_total_bytes (load_data->archive,
443 _g_file_get_size (fr_archive_get_file (load_data->archive), cancellable));
444
445 r = create_read_object (load_data, &a);
446 if (r != ARCHIVE_OK) {
447 archive_read_free(a);
448 return;
449 }
450
451 while ((r = archive_read_next_header (a, &entry)) == ARCHIVE_OK) {
452 FileData *file_data;
453 const char *pathname;
454
455 if (g_cancellable_is_cancelled (cancellable))
456 break;
457
458 file_data = file_data_new ();
459
460 if (archive_entry_size_is_set (entry)) {
461 file_data->size = archive_entry_size (entry);
462 FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->uncompressed_size += file_data->size;
463 }
464
465 if (archive_entry_mtime_is_set (entry))
466 file_data->modified = archive_entry_mtime (entry);
467
468 if (archive_entry_filetype (entry) == AE_IFLNK)
469 file_data->link = g_strdup (archive_entry_symlink (entry));
470
471 pathname = archive_entry_pathname (entry);
472 if (*pathname == '/') {
473 file_data->full_path = g_strdup (pathname);
474 file_data->original_path = file_data->full_path;
475 }
476 else {
477 file_data->full_path = g_strconcat ("/", pathname, NULL);
478 file_data->original_path = file_data->full_path + 1;
479 }
480
481 file_data->dir = (archive_entry_filetype (entry) == AE_IFDIR);
482 if (file_data->dir)
483 file_data->name = _g_path_get_dir_name (file_data->full_path);
484 else
485 file_data->name = g_strdup (_g_path_get_basename (file_data->full_path));
486 file_data->path = _g_path_remove_level (file_data->full_path);
487
488 /*
489 g_print ("%s\n", archive_entry_pathname (entry));
490 g_print ("\tfull_path: %s\n", file_data->full_path);
491 g_print ("\toriginal_path: %s\n", file_data->original_path);
492 g_print ("\tname: %s\n", file_data->name);
493 g_print ("\tpath: %s\n", file_data->path);
494 g_print ("\tlink: %s\n", file_data->link);
495 */
496
497 fr_archive_add_file (load_data->archive, file_data);
498
499 archive_read_data_skip (a);
500 }
501
502 if ((load_data->error == NULL) && (r != ARCHIVE_EOF) && (archive_error_string (a) != NULL))
503 load_data->error = _g_error_new_from_archive_error (archive_error_string (a));
504 if (load_data->error == NULL)
505 g_cancellable_set_error_if_cancelled (cancellable, &load_data->error);
506 if (load_data->error != NULL)
507 g_simple_async_result_set_from_error (result, load_data->error);
508
509 archive_read_free (a);
510 load_data_free (load_data);
511 }
512
513
514 static void
fr_archive_libarchive_list(FrArchive * archive,const char * password,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)515 fr_archive_libarchive_list (FrArchive *archive,
516 const char *password,
517 GCancellable *cancellable,
518 GAsyncReadyCallback callback,
519 gpointer user_data)
520 {
521 LoadData *load_data;
522
523 load_data = g_new0 (LoadData, 1);
524 load_data_init (load_data);
525
526 load_data->archive = g_object_ref (archive);
527 load_data->cancellable = _g_object_ref (cancellable);
528 load_data->result = g_simple_async_result_new (G_OBJECT (archive),
529 callback,
530 user_data,
531 fr_archive_list);
532
533 g_simple_async_result_set_op_res_gpointer (load_data->result, load_data, NULL);
534 g_simple_async_result_run_in_thread (load_data->result,
535 list_archive_thread,
536 G_PRIORITY_DEFAULT,
537 cancellable);
538 }
539
540
541 /* -- extract -- */
542
543
544 #define NULL_BUFFER_SIZE (16 * 1024)
545
546
547 typedef struct {
548 LoadData parent;
549 GList *file_list;
550 GFile *destination;
551 char *base_dir;
552 gboolean skip_older;
553 gboolean overwrite;
554 gboolean junk_paths;
555 GHashTable *files_to_extract;
556 int n_files_to_extract;
557 GHashTable *usernames;
558 GHashTable *groupnames;
559 char *null_buffer;
560 } ExtractData;
561
562
563 static void
extract_data_free(ExtractData * extract_data)564 extract_data_free (ExtractData *extract_data)
565 {
566 g_free (extract_data->base_dir);
567 _g_object_unref (extract_data->destination);
568 _g_string_list_free (extract_data->file_list);
569 g_hash_table_unref (extract_data->files_to_extract);
570 g_hash_table_unref (extract_data->usernames);
571 g_hash_table_unref (extract_data->groupnames);
572 g_free (extract_data->null_buffer);
573 load_data_free (LOAD_DATA (extract_data));
574 }
575
576
577 static gboolean
extract_data_get_extraction_requested(ExtractData * extract_data,const char * pathname)578 extract_data_get_extraction_requested (ExtractData *extract_data,
579 const char *pathname)
580 {
581 if (extract_data->file_list != NULL)
582 return g_hash_table_lookup (extract_data->files_to_extract, pathname) != NULL;
583 else
584 return TRUE;
585 }
586
587
588 static GFileInfo *
_g_file_info_create_from_entry(struct archive_entry * entry,ExtractData * extract_data)589 _g_file_info_create_from_entry (struct archive_entry *entry,
590 ExtractData *extract_data)
591 {
592 GFileInfo *info;
593
594 info = g_file_info_new ();
595
596 /* times */
597
598 if (archive_entry_mtime_is_set (entry))
599 g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, archive_entry_mtime (entry));
600
601 /* username */
602
603 if (archive_entry_uname (entry) != NULL) {
604 guint32 uid;
605
606 uid = GPOINTER_TO_INT (g_hash_table_lookup (extract_data->usernames, archive_entry_uname (entry)));
607 if (uid == 0) {
608 struct passwd *pwd = getpwnam (archive_entry_uname (entry));
609 if (pwd != NULL) {
610 uid = pwd->pw_uid;
611 g_hash_table_insert (extract_data->usernames, g_strdup (archive_entry_uname (entry)), GINT_TO_POINTER (uid));
612 }
613 }
614 if (uid != 0)
615 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, uid);
616 }
617
618 /* groupname */
619
620 if (archive_entry_gname (entry) != NULL) {
621 guint32 gid;
622
623 gid = GPOINTER_TO_INT (g_hash_table_lookup (extract_data->groupnames, archive_entry_gname (entry)));
624 if (gid == 0) {
625 struct group *grp = getgrnam (archive_entry_gname (entry));
626 if (grp != NULL) {
627 gid = grp->gr_gid;
628 g_hash_table_insert (extract_data->groupnames, g_strdup (archive_entry_gname (entry)), GINT_TO_POINTER (gid));
629 }
630 }
631 if (gid != 0)
632 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, gid);
633 }
634
635 /* permsissions */
636
637 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, archive_entry_mode (entry));
638
639 return info;
640 }
641
642
643 static gboolean
_g_file_set_attributes_from_info(GFile * file,GFileInfo * info,GCancellable * cancellable,GError ** error)644 _g_file_set_attributes_from_info (GFile *file,
645 GFileInfo *info,
646 GCancellable *cancellable,
647 GError **error)
648 {
649 return g_file_set_attributes_from_info (file, info, G_FILE_QUERY_INFO_NONE, cancellable, error);
650 }
651
652
653 static void
restore_original_file_attributes(GHashTable * created_files,GCancellable * cancellable)654 restore_original_file_attributes (GHashTable *created_files,
655 GCancellable *cancellable)
656 {
657 GHashTableIter iter;
658 gpointer key, value;
659
660 g_hash_table_iter_init (&iter, created_files);
661 while (g_hash_table_iter_next (&iter, &key, &value)) {
662 GFile *file = key;
663 GFileInfo *info = value;
664
665 _g_file_set_attributes_from_info (file, info, cancellable, NULL);
666 }
667 }
668
669
670 static gboolean
_g_output_stream_add_padding(ExtractData * extract_data,GOutputStream * ostream,gssize target_offset,gssize actual_offset,GCancellable * cancellable,GError ** error)671 _g_output_stream_add_padding (ExtractData *extract_data,
672 GOutputStream *ostream,
673 gssize target_offset,
674 gssize actual_offset,
675 GCancellable *cancellable,
676 GError **error)
677 {
678 gboolean success = TRUE;
679 gsize count;
680 gsize bytes_written;
681
682 if (extract_data->null_buffer == NULL)
683 extract_data->null_buffer = g_malloc0 (NULL_BUFFER_SIZE);
684
685 while (target_offset > actual_offset) {
686 count = NULL_BUFFER_SIZE;
687 if (target_offset < actual_offset + NULL_BUFFER_SIZE)
688 count = target_offset - actual_offset;
689
690 success = g_output_stream_write_all (ostream, extract_data->null_buffer, count, &bytes_written, cancellable, error);
691 if (! success)
692 break;
693
694 actual_offset += bytes_written;
695 }
696
697 return success;
698 }
699
700 static gboolean
_g_file_contains_symlinks_in_path(const char * relative_path,GFile * destination,GHashTable * symlinks)701 _g_file_contains_symlinks_in_path (const char *relative_path,
702 GFile *destination,
703 GHashTable *symlinks)
704 {
705 gboolean contains_symlinks = FALSE;
706 GFile *parent;
707 char **components;
708 int i;
709
710 if (relative_path == NULL)
711 return FALSE;
712
713 if (destination == NULL)
714 return TRUE;
715
716 parent = g_object_ref (destination);
717 components = g_strsplit (relative_path, "/", -1);
718 for (i = 0; (components[i] != NULL) && (components[i + 1] != NULL); i++) {
719 GFile *tmp;
720
721 if (components[i][0] == 0)
722 continue;
723
724 tmp = g_file_get_child (parent, components[i]);
725 g_object_unref (parent);
726 parent = tmp;
727
728 if (g_hash_table_contains (symlinks, parent)) {
729 contains_symlinks = TRUE;
730 break;
731 }
732 }
733
734 g_strfreev (components);
735 g_object_unref (parent);
736
737 return contains_symlinks;
738 }
739
740
741 static void
extract_archive_thread(GSimpleAsyncResult * result,GObject * object,GCancellable * cancellable)742 extract_archive_thread (GSimpleAsyncResult *result,
743 GObject *object,
744 GCancellable *cancellable)
745 {
746 ExtractData *extract_data;
747 LoadData *load_data;
748 GHashTable *checked_folders;
749 GHashTable *created_files;
750 GHashTable *folders_created_during_extraction;
751 GHashTable *symlinks;
752 struct archive *a;
753 struct archive_entry *entry;
754 int r;
755
756 extract_data = g_simple_async_result_get_op_res_gpointer (result);
757 load_data = LOAD_DATA (extract_data);
758
759 r = create_read_object (load_data, &a);
760 if (r != ARCHIVE_OK) {
761 archive_read_free(a);
762 return;
763 }
764
765 checked_folders = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
766 created_files = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, g_object_unref);
767 folders_created_during_extraction = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
768 symlinks = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
769 fr_archive_progress_set_total_files (load_data->archive, extract_data->n_files_to_extract);
770
771 while ((r = archive_read_next_header (a, &entry)) == ARCHIVE_OK) {
772 const char *pathname;
773 char *fullpath;
774 const char *relative_path;
775 GFile *file;
776 GFile *parent;
777 GOutputStream *ostream;
778 const void *buffer;
779 size_t buffer_size;
780 int64_t target_offset, actual_offset;
781 GError *local_error = NULL;
782 __LA_MODE_T filetype;
783
784 if (g_cancellable_is_cancelled (cancellable))
785 break;
786
787 pathname = archive_entry_pathname (entry);
788 if (! extract_data_get_extraction_requested (extract_data, pathname)) {
789 archive_read_data_skip (a);
790 continue;
791 }
792
793 fullpath = (*pathname == '/') ? g_strdup (pathname) : g_strconcat ("/", pathname, NULL);
794 relative_path = _g_path_get_relative_basename_safe (fullpath, extract_data->base_dir, extract_data->junk_paths);
795 if (relative_path == NULL) {
796 fr_archive_progress_inc_completed_files (load_data->archive, 1);
797 fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0);
798 archive_read_data_skip (a);
799 continue;
800 }
801
802 /* Symlinks in parents are dangerous as it can easily happen
803 * that files are written outside of the destination. The tar
804 * cmd fails to extract such archives with ENOTDIR. Let's skip
805 * those files here for sure. This is most probably malicious,
806 * or corrupted archive.
807 */
808 if (_g_file_contains_symlinks_in_path (relative_path, extract_data->destination, symlinks)) {
809 g_warning ("Skipping '%s' file as it has symlink in parents.", relative_path);
810 fr_archive_progress_inc_completed_files (load_data->archive, 1);
811 fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0);
812 archive_read_data_skip (a);
813 continue;
814 }
815
816 file = g_file_get_child (extract_data->destination, relative_path);
817
818 /* honor the skip_older and overwrite options */
819
820 if ((g_hash_table_lookup (folders_created_during_extraction, file) == NULL)
821 && (extract_data->skip_older || ! extract_data->overwrite))
822 {
823 GFileInfo *info;
824
825 info = g_file_query_info (file,
826 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," G_FILE_ATTRIBUTE_TIME_MODIFIED,
827 G_FILE_QUERY_INFO_NONE,
828 cancellable,
829 &local_error);
830 if (info != NULL) {
831 gboolean skip = FALSE;
832
833 if (! extract_data->overwrite) {
834 skip = TRUE;
835 }
836 else if (extract_data->skip_older) {
837 GTimeVal modification_time;
838
839 g_file_info_get_modification_time (info, &modification_time);
840 if (archive_entry_mtime (entry) < modification_time.tv_sec)
841 skip = TRUE;
842 }
843
844 g_object_unref (info);
845
846 if (skip) {
847 g_object_unref (file);
848
849 archive_read_data_skip (a);
850 fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0);
851
852 if ((extract_data->file_list != NULL) && (--extract_data->n_files_to_extract == 0)) {
853 r = ARCHIVE_EOF;
854 break;
855 }
856
857 continue;
858 }
859 }
860 else {
861 if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
862 load_data->error = local_error;
863 g_object_unref (info);
864 break;
865 }
866 g_clear_error (&local_error);
867 }
868 }
869
870 fr_archive_progress_inc_completed_files (load_data->archive, 1);
871
872 /* create the file parents */
873
874 parent = g_file_get_parent (file);
875
876 if ((parent != NULL)
877 && (g_hash_table_lookup (checked_folders, parent) == NULL)
878 && ! g_file_query_exists (parent, cancellable))
879 {
880 if (! _g_file_make_directory_with_parents (parent,
881 folders_created_during_extraction,
882 cancellable,
883 &local_error))
884 {
885 if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
886 load_data->error = local_error;
887 else
888 g_clear_error (&local_error);
889 }
890
891 if (load_data->error == NULL) {
892 GFile *grandparent;
893
894 grandparent = g_object_ref (parent);
895 while (grandparent != NULL) {
896 if (g_hash_table_lookup (checked_folders, grandparent) == NULL)
897 g_hash_table_insert (checked_folders, grandparent, GINT_TO_POINTER (1));
898 grandparent = g_file_get_parent (grandparent);
899 }
900 }
901 }
902 g_object_unref (parent);
903
904 /* create the file */
905
906 filetype = archive_entry_filetype (entry);
907
908 if (load_data->error == NULL) {
909 const char *linkname;
910
911 linkname = archive_entry_hardlink (entry);
912 if (linkname != NULL) {
913 char *link_fullpath;
914 const char *relative_path;
915 GFile *link_file;
916 char *oldname;
917 char *newname;
918 int r;
919
920 link_fullpath = (*linkname == '/') ? g_strdup (linkname) : g_strconcat ("/", linkname, NULL);
921 relative_path = _g_path_get_relative_basename_safe (link_fullpath, extract_data->base_dir, extract_data->junk_paths);
922 if (relative_path == NULL) {
923 g_free (link_fullpath);
924 archive_read_data_skip (a);
925 continue;
926 }
927
928 link_file = g_file_get_child (extract_data->destination, relative_path);
929 oldname = g_file_get_path (link_file);
930 newname = g_file_get_path (file);
931
932 if ((oldname != NULL) && (newname != NULL))
933 r = link (oldname, newname);
934 else
935 r = -1;
936
937 if (r == 0) {
938 __LA_INT64_T filesize;
939
940 if (archive_entry_size_is_set (entry))
941 filesize = archive_entry_size (entry);
942 else
943 filesize = -1;
944
945 if (filesize > 0)
946 filetype = AE_IFREG; /* treat as a regular file to save the data */
947 }
948 else {
949 char *uri;
950 char *msg;
951
952 uri = g_file_get_uri (file);
953 msg = g_strdup_printf ("Could not create the hard link %s", uri);
954 load_data->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, msg);
955
956 g_free (msg);
957 g_free (uri);
958 }
959
960 g_free (newname);
961 g_free (oldname);
962 g_object_unref (link_file);
963 g_free (link_fullpath);
964 }
965 }
966
967 if (load_data->error == NULL) {
968 switch (filetype) {
969 case AE_IFDIR:
970 if (! g_file_make_directory (file, cancellable, &local_error)) {
971 if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
972 load_data->error = g_error_copy (local_error);
973 g_clear_error (&local_error);
974 }
975 if (load_data->error == NULL)
976 g_hash_table_insert (created_files, g_object_ref (file), _g_file_info_create_from_entry (entry, extract_data));
977 archive_read_data_skip (a);
978 break;
979
980 case AE_IFREG:
981 ostream = (GOutputStream *) g_file_replace (file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, cancellable, &load_data->error);
982 if (ostream == NULL)
983 break;
984
985 actual_offset = 0;
986 while ((r = archive_read_data_block (a, &buffer, &buffer_size, &target_offset)) == ARCHIVE_OK) {
987 gsize bytes_written;
988
989 if (target_offset > actual_offset) {
990 if (! _g_output_stream_add_padding (extract_data, ostream, target_offset, actual_offset, cancellable, &load_data->error))
991 break;
992 fr_archive_progress_inc_completed_bytes (load_data->archive, target_offset - actual_offset);
993 actual_offset = target_offset;
994 }
995
996 if (! g_output_stream_write_all (ostream, buffer, buffer_size, &bytes_written, cancellable, &load_data->error))
997 break;
998
999 actual_offset += bytes_written;
1000 fr_archive_progress_inc_completed_bytes (load_data->archive, bytes_written);
1001 }
1002
1003 if ((r == ARCHIVE_EOF) && (target_offset > actual_offset))
1004 _g_output_stream_add_padding (extract_data, ostream, target_offset, actual_offset, cancellable, &load_data->error);
1005
1006 _g_object_unref (ostream);
1007
1008 if (r != ARCHIVE_EOF)
1009 load_data->error = _g_error_new_from_archive_error (archive_error_string (a));
1010 else
1011 g_hash_table_insert (created_files, g_object_ref (file), _g_file_info_create_from_entry (entry, extract_data));
1012 break;
1013
1014 case AE_IFLNK:
1015 if (! g_file_make_symbolic_link (file, archive_entry_symlink (entry), cancellable, &local_error)) {
1016 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
1017 g_clear_error (&local_error);
1018 if (g_file_delete (file, cancellable, &local_error)) {
1019 g_clear_error (&local_error);
1020 if (! g_file_make_symbolic_link (file, archive_entry_symlink (entry), cancellable, &local_error))
1021 load_data->error = g_error_copy (local_error);
1022 }
1023 else
1024 load_data->error = g_error_copy (local_error);
1025 }
1026 else
1027 load_data->error = g_error_copy (local_error);
1028 g_clear_error (&local_error);
1029 }
1030 if (load_data->error == NULL)
1031 g_hash_table_add (symlinks, g_object_ref (file));
1032 archive_read_data_skip (a);
1033 break;
1034
1035 default:
1036 archive_read_data_skip (a);
1037 break;
1038 }
1039 }
1040
1041 g_object_unref (file);
1042 g_free (fullpath);
1043
1044 if (load_data->error != NULL)
1045 break;
1046
1047 if ((extract_data->file_list != NULL) && (--extract_data->n_files_to_extract == 0)) {
1048 r = ARCHIVE_EOF;
1049 break;
1050 }
1051 }
1052
1053 if (load_data->error == NULL)
1054 restore_original_file_attributes (created_files, cancellable);
1055
1056 if ((load_data->error == NULL) && (r != ARCHIVE_EOF))
1057 load_data->error = _g_error_new_from_archive_error (archive_error_string (a));
1058 if (load_data->error == NULL)
1059 g_cancellable_set_error_if_cancelled (cancellable, &load_data->error);
1060 if (load_data->error != NULL)
1061 g_simple_async_result_set_from_error (result, load_data->error);
1062
1063 g_hash_table_unref (folders_created_during_extraction);
1064 g_hash_table_unref (created_files);
1065 g_hash_table_unref (checked_folders);
1066 g_hash_table_unref (symlinks);
1067 archive_read_free (a);
1068 extract_data_free (extract_data);
1069 }
1070
1071
1072 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)1073 fr_archive_libarchive_extract_files (FrArchive *archive,
1074 GList *file_list,
1075 GFile *destination,
1076 const char *base_dir,
1077 gboolean skip_older,
1078 gboolean overwrite,
1079 gboolean junk_paths,
1080 const char *password,
1081 GCancellable *cancellable,
1082 GAsyncReadyCallback callback,
1083 gpointer user_data)
1084 {
1085 ExtractData *extract_data;
1086 LoadData *load_data;
1087 GList *scan;
1088
1089 extract_data = g_new0 (ExtractData, 1);
1090
1091 load_data = LOAD_DATA (extract_data);
1092 load_data->archive = g_object_ref (archive);
1093 load_data->cancellable = _g_object_ref (cancellable);
1094 load_data->result = g_simple_async_result_new (G_OBJECT (archive),
1095 callback,
1096 user_data,
1097 fr_archive_extract);
1098 load_data->buffer_size = BUFFER_SIZE;
1099 load_data->buffer = g_new (char, load_data->buffer_size);
1100
1101 extract_data->file_list = _g_string_list_dup (file_list);
1102 extract_data->destination = g_object_ref (destination);
1103 extract_data->base_dir = g_strdup (base_dir);
1104 extract_data->skip_older = skip_older;
1105 extract_data->overwrite = overwrite;
1106 extract_data->junk_paths = junk_paths;
1107 extract_data->files_to_extract = g_hash_table_new (g_str_hash, g_str_equal);
1108 extract_data->n_files_to_extract = 0;
1109 extract_data->usernames = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1110 extract_data->groupnames = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1111
1112 for (scan = extract_data->file_list; scan; scan = scan->next) {
1113 g_hash_table_insert (extract_data->files_to_extract, scan->data, GINT_TO_POINTER (1));
1114 extract_data->n_files_to_extract++;
1115 }
1116
1117 g_simple_async_result_set_op_res_gpointer (load_data->result, extract_data, NULL);
1118 g_simple_async_result_run_in_thread (load_data->result,
1119 extract_archive_thread,
1120 G_PRIORITY_DEFAULT,
1121 cancellable);
1122 }
1123
1124
1125 /* -- AddFile -- */
1126
1127
1128 typedef struct {
1129 GFile *file;
1130 char *pathname;
1131 } AddFile;
1132
1133
1134 static AddFile *
add_file_new(GFile * file,const char * archive_pathname)1135 add_file_new (GFile *file,
1136 const char *archive_pathname)
1137 {
1138 AddFile *add_file;
1139
1140 add_file = g_new (AddFile, 1);
1141 add_file->file = g_object_ref (file);
1142 add_file->pathname = g_strdup (archive_pathname);
1143
1144 return add_file;
1145 }
1146
1147
1148 static void
add_file_free(AddFile * add_file)1149 add_file_free (AddFile *add_file)
1150 {
1151 g_object_unref (add_file->file);
1152 g_free (add_file->pathname);
1153 g_free (add_file);
1154 }
1155
1156
1157 /* -- _fr_archive_libarchive_save -- */
1158
1159
1160 #define SAVE_DATA(x) ((SaveData *)(x))
1161
1162
1163 typedef enum {
1164 WRITE_ACTION_ABORT,
1165 WRITE_ACTION_SKIP_ENTRY,
1166 WRITE_ACTION_WRITE_ENTRY
1167 } WriteAction;
1168
1169
1170 typedef struct _SaveData SaveData;
1171 typedef void (*SaveDataFunc) (SaveData *, gpointer user_data);
1172 typedef WriteAction (*EntryActionFunc) (SaveData *, struct archive_entry *, gpointer user_data);
1173
1174
1175 struct _SaveData {
1176 LoadData parent;
1177 GFile *tmp_file;
1178 GOutputStream *ostream;
1179 GHashTable *usernames;
1180 GHashTable *groupnames;
1181 gboolean update;
1182 char *password;
1183 gboolean encrypt_header;
1184 FrCompression compression;
1185 guint volume_size;
1186 void *buffer;
1187 gsize buffer_size;
1188 SaveDataFunc begin_operation;
1189 SaveDataFunc end_operation;
1190 EntryActionFunc entry_action;
1191 gpointer user_data;
1192 GDestroyNotify user_data_notify;
1193 struct archive *b;
1194 };
1195
1196
1197 static void
save_data_init(SaveData * save_data)1198 save_data_init (SaveData *save_data)
1199 {
1200 load_data_init (LOAD_DATA (save_data));
1201 save_data->buffer_size = BUFFER_SIZE;
1202 save_data->buffer = g_new (char, save_data->buffer_size);
1203 save_data->usernames = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_free);
1204 save_data->groupnames = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_free);
1205 }
1206
1207
1208 static void
save_data_free(SaveData * save_data)1209 save_data_free (SaveData *save_data)
1210 {
1211 if (save_data->user_data_notify != NULL)
1212 save_data->user_data_notify (save_data->user_data);
1213 g_free (save_data->buffer);
1214 g_free (save_data->password);
1215 g_hash_table_unref (save_data->groupnames);
1216 g_hash_table_unref (save_data->usernames);
1217 _g_object_unref (save_data->ostream);
1218 _g_object_unref (save_data->tmp_file);
1219 load_data_free (LOAD_DATA (save_data));
1220 }
1221
1222
1223 static int
save_data_open(struct archive * a,void * client_data)1224 save_data_open (struct archive *a,
1225 void *client_data)
1226 {
1227 SaveData *save_data = client_data;
1228 LoadData *load_data = LOAD_DATA (save_data);
1229 GFile *parent;
1230 char *basename;
1231 char *tmpname;
1232
1233 if (load_data->error != NULL)
1234 return ARCHIVE_FATAL;
1235
1236 parent = g_file_get_parent (fr_archive_get_file (load_data->archive));
1237 basename = g_file_get_basename (fr_archive_get_file (load_data->archive));
1238 tmpname = _g_filename_get_random (16, basename);
1239 save_data->tmp_file = g_file_get_child (parent, tmpname);
1240 save_data->ostream = (GOutputStream *) g_file_create (save_data->tmp_file, G_FILE_CREATE_NONE, load_data->cancellable, &load_data->error);
1241
1242 g_free (tmpname);
1243 g_free (basename);
1244 g_object_unref (parent);
1245
1246 return (save_data->ostream != NULL) ? ARCHIVE_OK : ARCHIVE_FATAL;
1247 }
1248
1249
1250 static ssize_t
save_data_write(struct archive * a,void * client_data,const void * buff,size_t n)1251 save_data_write (struct archive *a,
1252 void *client_data,
1253 const void *buff,
1254 size_t n)
1255 {
1256 SaveData *save_data = client_data;
1257 LoadData *load_data = LOAD_DATA (save_data);
1258
1259 if (load_data->error != NULL)
1260 return -1;
1261
1262 return g_output_stream_write (save_data->ostream, buff, n, load_data->cancellable, &load_data->error);
1263 }
1264
1265
1266 static int
save_data_close(struct archive * a,void * client_data)1267 save_data_close (struct archive *a,
1268 void *client_data)
1269 {
1270 SaveData *save_data = client_data;
1271 LoadData *load_data = LOAD_DATA (save_data);
1272
1273 if (save_data->ostream != NULL) {
1274 GError *error = NULL;
1275
1276 g_output_stream_close (save_data->ostream, load_data->cancellable, &error);
1277 if (load_data->error == NULL && error != NULL)
1278 load_data->error = g_error_copy (error);
1279
1280 _g_error_free (error);
1281 }
1282
1283 if (load_data->error == NULL)
1284 g_file_move (save_data->tmp_file,
1285 fr_archive_get_file (load_data->archive),
1286 G_FILE_COPY_OVERWRITE | G_FILE_COPY_TARGET_DEFAULT_PERMS,
1287 load_data->cancellable,
1288 NULL,
1289 NULL,
1290 &load_data->error);
1291 else
1292 g_file_delete (save_data->tmp_file, NULL, NULL);
1293
1294 return ARCHIVE_OK;
1295 }
1296
1297
1298 static void
_archive_write_set_format_from_context(struct archive * a,SaveData * save_data)1299 _archive_write_set_format_from_context (struct archive *a,
1300 SaveData *save_data)
1301 {
1302 const char *mime_type;
1303 int archive_filter;
1304
1305 /* set format and filter from the mime type */
1306
1307 mime_type = fr_archive_get_mime_type (LOAD_DATA (save_data)->archive);
1308 archive_filter = ARCHIVE_FILTER_NONE;
1309
1310 if (_g_str_equal (mime_type, "application/x-bzip-compressed-tar")) {
1311 archive_write_set_format_pax_restricted (a);
1312 archive_filter = ARCHIVE_FILTER_BZIP2;
1313 }
1314 else if (_g_str_equal (mime_type, "application/x-tarz")) {
1315 archive_write_set_format_pax_restricted (a);
1316 archive_filter = ARCHIVE_FILTER_COMPRESS;
1317 }
1318 else if (_g_str_equal (mime_type, "application/x-compressed-tar")) {
1319 archive_write_set_format_pax_restricted (a);
1320 archive_filter = ARCHIVE_FILTER_GZIP;
1321 }
1322 else if (_g_str_equal (mime_type, "application/x-lrzip-compressed-tar")) {
1323 archive_write_set_format_pax_restricted (a);
1324 archive_filter = ARCHIVE_FILTER_LRZIP;
1325 }
1326 else if (_g_str_equal (mime_type, "application/x-lzip-compressed-tar")) {
1327 archive_write_set_format_pax_restricted (a);
1328 archive_filter = ARCHIVE_FILTER_LZIP;
1329 }
1330 else if (_g_str_equal (mime_type, "application/x-lzma-compressed-tar")) {
1331 archive_write_set_format_pax_restricted (a);
1332 archive_filter = ARCHIVE_FILTER_LZMA;
1333 }
1334 else if (_g_str_equal (mime_type, "application/x-tzo")) {
1335 archive_write_set_format_pax_restricted (a);
1336 archive_filter = ARCHIVE_FILTER_LZOP;
1337 }
1338 else if (_g_str_equal (mime_type, "application/x-xz-compressed-tar")) {
1339 archive_write_set_format_pax_restricted (a);
1340 archive_filter = ARCHIVE_FILTER_XZ;
1341 }
1342 #if (ARCHIVE_VERSION_NUMBER >= 3003003)
1343 else if (_g_str_equal (mime_type, "application/x-zstd-compressed-tar")) {
1344 archive_write_set_format_pax_restricted (a);
1345 archive_filter = ARCHIVE_FILTER_ZSTD;
1346 }
1347 #endif
1348 else if (_g_str_equal (mime_type, "application/x-tar")) {
1349 archive_write_add_filter_none (a);
1350 archive_write_set_format_pax_restricted (a);
1351 }
1352 else if (_g_str_equal (mime_type, "application/x-cd-image")) {
1353 archive_write_set_format_iso9660 (a);
1354 }
1355 else if (_g_str_equal (mime_type, "application/x-cpio")) {
1356 archive_write_set_format_cpio (a);
1357 }
1358 else if (_g_str_equal (mime_type, "application/x-xar")) {
1359 archive_write_set_format_xar (a);
1360 }
1361 else if (_g_str_equal (mime_type, "application/x-ar")) {
1362 archive_write_set_format_ar_svr4 (a);
1363 }
1364 else if (_g_str_equal (mime_type, "application/x-7z-compressed")) {
1365 archive_write_set_format_7zip (a);
1366 }
1367 else if (_g_str_equal (mime_type, "application/zip")
1368 || _g_str_equal (mime_type, "application/x-cbz")) {
1369 archive_write_set_format_zip (a);
1370 }
1371
1372 /* set the filter */
1373
1374 if (archive_filter != ARCHIVE_FILTER_NONE) {
1375 char *compression_level = NULL;
1376
1377 switch (archive_filter) {
1378 case ARCHIVE_FILTER_BZIP2:
1379 archive_write_add_filter_bzip2 (a);
1380 break;
1381 case ARCHIVE_FILTER_COMPRESS:
1382 archive_write_add_filter_compress (a);
1383 break;
1384 case ARCHIVE_FILTER_GZIP:
1385 archive_write_add_filter_gzip (a);
1386 break;
1387 case ARCHIVE_FILTER_LRZIP:
1388 archive_write_add_filter_lrzip (a);
1389 break;
1390 case ARCHIVE_FILTER_LZIP:
1391 archive_write_add_filter_lzip (a);
1392 break;
1393 case ARCHIVE_FILTER_LZMA:
1394 archive_write_add_filter_lzma (a);
1395 break;
1396 case ARCHIVE_FILTER_LZOP:
1397 archive_write_add_filter_lzop (a);
1398 break;
1399 case ARCHIVE_FILTER_XZ:
1400 archive_write_add_filter_xz (a);
1401 break;
1402 #if (ARCHIVE_VERSION_NUMBER >= 3003003)
1403 case ARCHIVE_FILTER_ZSTD:
1404 archive_write_add_filter_zstd (a);
1405 break;
1406 #endif
1407 default:
1408 break;
1409 }
1410
1411 /* set the compression level */
1412
1413 compression_level = NULL;
1414 #if (ARCHIVE_VERSION_NUMBER >= 3003003)
1415 if (archive_filter == ARCHIVE_FILTER_ZSTD) {
1416 switch (save_data->compression) {
1417 case FR_COMPRESSION_VERY_FAST:
1418 compression_level = "1";
1419 break;
1420 case FR_COMPRESSION_FAST:
1421 compression_level = "2";
1422 break;
1423 case FR_COMPRESSION_NORMAL:
1424 compression_level = "3";
1425 break;
1426 case FR_COMPRESSION_MAXIMUM:
1427 compression_level = "22";
1428 break;
1429 }
1430 }
1431 else
1432 #endif
1433 {
1434 switch (save_data->compression) {
1435 case FR_COMPRESSION_VERY_FAST:
1436 compression_level = "1";
1437 break;
1438 case FR_COMPRESSION_FAST:
1439 compression_level = "3";
1440 break;
1441 case FR_COMPRESSION_NORMAL:
1442 compression_level = "6";
1443 break;
1444 case FR_COMPRESSION_MAXIMUM:
1445 compression_level = "9";
1446 break;
1447 }
1448 }
1449 if (compression_level != NULL)
1450 archive_write_set_filter_option (a, NULL, "compression-level", compression_level);
1451
1452 /* set the amount of threads */
1453
1454 if (archive_filter == ARCHIVE_FILTER_XZ) {
1455 archive_write_set_filter_option (a, NULL, "threads", fr_get_thread_count());
1456 }
1457 }
1458 }
1459
1460
1461 /* -- _archive_write_file -- */
1462
1463
1464 static gint64 *
_g_int64_pointer_new(gint64 i)1465 _g_int64_pointer_new (gint64 i)
1466 {
1467 gint64 *p;
1468
1469 p = g_new (gint64, 1);
1470 *p = i;
1471
1472 return p;
1473 }
1474
1475
1476 static gboolean
_archive_entry_copy_file_info(struct archive_entry * entry,GFileInfo * info,SaveData * save_data)1477 _archive_entry_copy_file_info (struct archive_entry *entry,
1478 GFileInfo *info,
1479 SaveData *save_data)
1480 {
1481 int filetype;
1482 char *username;
1483 char *groupname;
1484 gint64 id;
1485
1486 switch (g_file_info_get_file_type (info)) {
1487 case G_FILE_TYPE_REGULAR:
1488 filetype = AE_IFREG;
1489 break;
1490 case G_FILE_TYPE_DIRECTORY:
1491 filetype = AE_IFDIR;
1492 break;
1493 case G_FILE_TYPE_SYMBOLIC_LINK:
1494 filetype = AE_IFLNK;
1495 break;
1496 default:
1497 return FALSE;
1498 break;
1499 }
1500 archive_entry_set_filetype (entry, filetype);
1501
1502 archive_entry_set_atime (entry,
1503 g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS),
1504 g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) * 1000);
1505 archive_entry_set_ctime (entry,
1506 g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CREATED),
1507 g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CREATED_USEC) * 1000);
1508 archive_entry_set_mtime (entry,
1509 g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED),
1510 g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) * 1000);
1511 archive_entry_unset_birthtime (entry);
1512 archive_entry_set_dev (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_DEVICE));
1513 archive_entry_set_gid (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID));
1514 archive_entry_set_uid (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID));
1515 archive_entry_set_ino64 (entry, g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE));
1516 archive_entry_set_mode (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE));
1517 archive_entry_set_nlink (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_NLINK));
1518 archive_entry_set_rdev (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_RDEV));
1519 archive_entry_set_size (entry, g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE));
1520 if (filetype == AE_IFLNK)
1521 archive_entry_set_symlink (entry, g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET));
1522
1523 /* username */
1524
1525 id = archive_entry_uid (entry);
1526 username = g_hash_table_lookup (save_data->usernames, &id);
1527 if (username == NULL) {
1528 struct passwd *pwd = getpwuid (id);
1529 if (pwd != NULL) {
1530 username = g_strdup (pwd->pw_name);
1531 g_hash_table_insert (save_data->usernames, _g_int64_pointer_new (id), username);
1532 }
1533 }
1534 if (username != NULL)
1535 archive_entry_set_uname (entry, username);
1536
1537 /* groupname */
1538
1539 id = archive_entry_gid (entry);
1540 groupname = g_hash_table_lookup (save_data->groupnames, &id);
1541 if (groupname == NULL) {
1542 struct group *grp = getgrgid (id);
1543 if (grp != NULL) {
1544 groupname = g_strdup (grp->gr_name);
1545 g_hash_table_insert (save_data->groupnames, _g_int64_pointer_new (id), groupname);
1546 }
1547 }
1548 if (groupname != NULL)
1549 archive_entry_set_gname (entry, groupname);
1550
1551 return TRUE;
1552 }
1553
1554
1555 static WriteAction
_archive_write_file(struct archive * b,SaveData * save_data,AddFile * add_file,gboolean follow_link,struct archive_entry * r_entry,GCancellable * cancellable)1556 _archive_write_file (struct archive *b,
1557 SaveData *save_data,
1558 AddFile *add_file,
1559 gboolean follow_link,
1560 struct archive_entry *r_entry,
1561 GCancellable *cancellable)
1562 {
1563 LoadData *load_data = LOAD_DATA (save_data);
1564 GFileInfo *info;
1565 struct archive_entry *w_entry;
1566 int rb;
1567
1568 /* write the file header */
1569
1570 info = g_file_query_info (add_file->file,
1571 FILE_ATTRIBUTES_NEEDED_BY_ARCHIVE_ENTRY,
1572 (! follow_link ? G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS : 0),
1573 cancellable,
1574 &load_data->error);
1575 if (info == NULL)
1576 return WRITE_ACTION_ABORT;
1577
1578 w_entry = archive_entry_new ();
1579 if (! _archive_entry_copy_file_info (w_entry, info, save_data)) {
1580 archive_entry_free (w_entry);
1581 g_object_unref (info);
1582 return WRITE_ACTION_SKIP_ENTRY;
1583 }
1584
1585 /* honor the update flag */
1586
1587 if (save_data->update && (r_entry != NULL) && (archive_entry_mtime (w_entry) < archive_entry_mtime (r_entry))) {
1588 archive_entry_free (w_entry);
1589 g_object_unref (info);
1590 return WRITE_ACTION_WRITE_ENTRY;
1591 }
1592
1593 archive_entry_set_pathname (w_entry, add_file->pathname);
1594 rb = archive_write_header (b, w_entry);
1595
1596 /* write the file data */
1597
1598 if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR) {
1599 GInputStream *istream;
1600
1601 istream = (GInputStream *) g_file_read (add_file->file, cancellable, &load_data->error);
1602 if (istream != NULL) {
1603 gssize bytes_read;
1604
1605 while ((bytes_read = g_input_stream_read (istream, save_data->buffer, save_data->buffer_size, cancellable, &load_data->error)) > 0) {
1606 archive_write_data (b, save_data->buffer, bytes_read);
1607 fr_archive_progress_inc_completed_bytes (load_data->archive, bytes_read);
1608 }
1609
1610 g_object_unref (istream);
1611 }
1612 }
1613
1614 rb = archive_write_finish_entry (b);
1615
1616 if ((load_data->error == NULL) && (rb <= ARCHIVE_FAILED))
1617 load_data->error = g_error_new_literal (FR_ERROR, FR_ERROR_COMMAND_ERROR, archive_error_string (b));
1618
1619 archive_entry_free (w_entry);
1620 g_object_unref (info);
1621
1622 return (load_data->error == NULL) ? WRITE_ACTION_SKIP_ENTRY : WRITE_ACTION_ABORT;
1623 }
1624
1625
1626 static void
save_archive_thread(GSimpleAsyncResult * result,GObject * object,GCancellable * cancellable)1627 save_archive_thread (GSimpleAsyncResult *result,
1628 GObject *object,
1629 GCancellable *cancellable)
1630 {
1631 SaveData *save_data;
1632 LoadData *load_data;
1633 struct archive *a, *b;
1634 struct archive_entry *r_entry;
1635 int ra = ARCHIVE_OK, rb = ARCHIVE_OK;
1636
1637 save_data = g_simple_async_result_get_op_res_gpointer (result);
1638 load_data = LOAD_DATA (save_data);
1639
1640 save_data->b = b = archive_write_new ();
1641 _archive_write_set_format_from_context (b, save_data);
1642 archive_write_open (b, save_data, save_data_open, save_data_write, save_data_close);
1643 archive_write_set_bytes_in_last_block (b, 1);
1644
1645 create_read_object (load_data, &a);
1646
1647 if (save_data->begin_operation != NULL)
1648 save_data->begin_operation (save_data, save_data->user_data);
1649
1650 while ((load_data->error == NULL) && (ra = archive_read_next_header (a, &r_entry)) == ARCHIVE_OK) {
1651 struct archive_entry *w_entry;
1652 WriteAction action;
1653
1654 if (g_cancellable_is_cancelled (cancellable))
1655 break;
1656
1657 action = WRITE_ACTION_WRITE_ENTRY;
1658 w_entry = archive_entry_clone (r_entry);
1659 if (save_data->entry_action != NULL)
1660 action = save_data->entry_action (save_data, w_entry, save_data->user_data);
1661
1662 if (action == WRITE_ACTION_WRITE_ENTRY) {
1663 const void *buffer;
1664 size_t buffer_size;
1665 __LA_INT64_T offset;
1666
1667 rb = archive_write_header (b, w_entry);
1668 if (rb <= ARCHIVE_FAILED) {
1669 load_data->error = _g_error_new_from_archive_error (archive_error_string (b));
1670 break;
1671 }
1672
1673 switch (archive_entry_filetype (r_entry)) {
1674 case AE_IFREG:
1675 while ((ra = archive_read_data_block (a, &buffer, &buffer_size, &offset)) == ARCHIVE_OK) {
1676 archive_write_data (b, buffer, buffer_size);
1677 fr_archive_progress_inc_completed_bytes (load_data->archive, buffer_size);
1678 }
1679
1680 if (ra <= ARCHIVE_FAILED) {
1681 load_data->error = _g_error_new_from_archive_error (archive_error_string (a));
1682 break;
1683 }
1684 break;
1685
1686 default:
1687 break;
1688 }
1689
1690 rb = archive_write_finish_entry (b);
1691 }
1692 else if (action == WRITE_ACTION_SKIP_ENTRY)
1693 fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size (r_entry));
1694
1695 archive_entry_free (w_entry);
1696 }
1697
1698 if (g_error_matches (load_data->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
1699 ra = ARCHIVE_EOF;
1700
1701 if (save_data->end_operation != NULL)
1702 save_data->end_operation (save_data, save_data->user_data);
1703
1704 rb = archive_write_close (b);
1705
1706 if ((load_data->error == NULL) && (ra != ARCHIVE_EOF))
1707 load_data->error = _g_error_new_from_archive_error (archive_error_string (a));
1708 if ((load_data->error == NULL) && (rb <= ARCHIVE_FAILED))
1709 load_data->error = _g_error_new_from_archive_error (archive_error_string (b));
1710 if (load_data->error == NULL)
1711 g_cancellable_set_error_if_cancelled (cancellable, &load_data->error);
1712 if (load_data->error != NULL)
1713 g_simple_async_result_set_from_error (result, load_data->error);
1714
1715 archive_read_free (a);
1716 archive_write_free (b);
1717 save_data_free (save_data);
1718 }
1719
1720
1721 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)1722 _fr_archive_libarchive_save (FrArchive *archive,
1723 gboolean update,
1724 const char *password,
1725 gboolean encrypt_header,
1726 FrCompression compression,
1727 guint volume_size,
1728 GCancellable *cancellable,
1729 GSimpleAsyncResult *result,
1730 SaveDataFunc begin_operation,
1731 SaveDataFunc end_operation,
1732 EntryActionFunc entry_action,
1733 gpointer user_data,
1734 GDestroyNotify notify)
1735 {
1736 SaveData *save_data;
1737 LoadData *load_data;
1738
1739 save_data = g_new0 (SaveData, 1);
1740 save_data_init (SAVE_DATA (save_data));
1741
1742 load_data = LOAD_DATA (save_data);
1743 load_data->archive = g_object_ref (archive);
1744 load_data->cancellable = _g_object_ref (cancellable);
1745 load_data->result = result;
1746
1747 save_data->update = update;
1748 save_data->password = g_strdup (password);
1749 save_data->encrypt_header = encrypt_header;
1750 save_data->compression = compression;
1751 save_data->volume_size = volume_size;
1752 save_data->begin_operation = begin_operation;
1753 save_data->end_operation = end_operation;
1754 save_data->entry_action = entry_action;
1755 save_data->user_data = user_data;
1756 save_data->user_data_notify = notify;
1757
1758 g_simple_async_result_set_op_res_gpointer (load_data->result, save_data, NULL);
1759 g_simple_async_result_run_in_thread (load_data->result,
1760 save_archive_thread,
1761 G_PRIORITY_DEFAULT,
1762 cancellable);
1763 }
1764
1765
1766 /* -- add_files -- */
1767
1768
1769 typedef struct {
1770 gboolean follow_links;
1771 GHashTable *files_to_add;
1772 int n_files_to_add;
1773 } AddData;
1774
1775
1776 static AddData *
add_data_new(void)1777 add_data_new (void)
1778 {
1779 AddData *add_data;
1780
1781 add_data = g_new0 (AddData, 1);
1782 add_data->files_to_add = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) add_file_free);
1783 add_data->n_files_to_add = 0;
1784 add_data->follow_links = TRUE;
1785
1786 return add_data;
1787 }
1788
1789
1790 static void
add_data_free(AddData * add_data)1791 add_data_free (AddData *add_data)
1792 {
1793 g_hash_table_unref (add_data->files_to_add);
1794 g_free (add_data);
1795 }
1796
1797
1798 static void
_add_files_begin(SaveData * save_data,gpointer user_data)1799 _add_files_begin (SaveData *save_data,
1800 gpointer user_data)
1801 {
1802 AddData *add_data = user_data;
1803 LoadData *load_data = LOAD_DATA (save_data);
1804
1805 fr_archive_progress_set_total_files (load_data->archive, add_data->n_files_to_add);
1806
1807 if (load_data->archive->files_to_add_size == 0) {
1808 GList *files_to_add;
1809 GList *scan;
1810
1811 files_to_add = g_hash_table_get_values (add_data->files_to_add);
1812 for (scan = files_to_add; scan; scan = scan->next) {
1813 AddFile *add_file = scan->data;
1814
1815 if (g_cancellable_is_cancelled (load_data->cancellable))
1816 break;
1817
1818 load_data->archive->files_to_add_size += _g_file_get_size (add_file->file, load_data->cancellable);
1819 }
1820
1821 g_list_free (files_to_add);
1822 }
1823
1824 fr_archive_progress_set_total_bytes (load_data->archive,
1825 FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->uncompressed_size + load_data->archive->files_to_add_size);
1826 }
1827
1828
1829 static WriteAction
_add_files_entry_action(SaveData * save_data,struct archive_entry * w_entry,gpointer user_data)1830 _add_files_entry_action (SaveData *save_data,
1831 struct archive_entry *w_entry,
1832 gpointer user_data)
1833 {
1834 AddData *add_data = user_data;
1835 LoadData *load_data = LOAD_DATA (save_data);
1836 WriteAction action;
1837 const char *pathname;
1838 AddFile *add_file;
1839
1840 action = WRITE_ACTION_WRITE_ENTRY;
1841 pathname = archive_entry_pathname (w_entry);
1842 add_file = g_hash_table_lookup (add_data->files_to_add, pathname);
1843 if (add_file != NULL) {
1844 action = _archive_write_file (save_data->b,
1845 save_data,
1846 add_file,
1847 add_data->follow_links,
1848 w_entry,
1849 load_data->cancellable);
1850 fr_archive_progress_inc_completed_files (load_data->archive, 1);
1851 add_data->n_files_to_add--;
1852 g_hash_table_remove (add_data->files_to_add, pathname);
1853 }
1854
1855 return action;
1856 }
1857
1858
1859 static void
_add_files_end(SaveData * save_data,gpointer user_data)1860 _add_files_end (SaveData *save_data,
1861 gpointer user_data)
1862 {
1863 AddData *add_data = user_data;
1864 LoadData *load_data = LOAD_DATA (save_data);
1865 GList *remaining_files;
1866 GList *scan;
1867
1868 /* allow to add files to a new archive */
1869
1870 if (g_error_matches (load_data->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
1871 g_clear_error (&load_data->error);
1872
1873 /* add the files that weren't present in the archive already */
1874
1875 remaining_files = g_hash_table_get_values (add_data->files_to_add);
1876 for (scan = remaining_files; (load_data->error == NULL) && scan; scan = scan->next) {
1877 AddFile *add_file = scan->data;
1878
1879 if (g_cancellable_is_cancelled (load_data->cancellable))
1880 break;
1881
1882 if (_archive_write_file (save_data->b,
1883 save_data,
1884 add_file,
1885 add_data->follow_links,
1886 NULL,
1887 load_data->cancellable) == WRITE_ACTION_ABORT)
1888 {
1889 break;
1890 }
1891
1892 fr_archive_progress_inc_completed_files (load_data->archive, 1);
1893 }
1894
1895 g_list_free (remaining_files);
1896 }
1897
1898
1899 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)1900 fr_archive_libarchive_add_files (FrArchive *archive,
1901 GList *file_list,
1902 GFile *base_dir,
1903 const char *dest_dir,
1904 gboolean update,
1905 gboolean follow_links,
1906 const char *password,
1907 gboolean encrypt_header,
1908 FrCompression compression,
1909 guint volume_size,
1910 GCancellable *cancellable,
1911 GAsyncReadyCallback callback,
1912 gpointer user_data)
1913 {
1914 AddData *add_data;
1915 GList *scan;
1916
1917 g_return_if_fail (base_dir != NULL);
1918
1919 add_data = add_data_new ();
1920 add_data->follow_links = follow_links;
1921
1922 if (dest_dir != NULL)
1923 dest_dir = (dest_dir[0] == '/' ? dest_dir + 1 : dest_dir);
1924 else
1925 dest_dir = "";
1926
1927 for (scan = file_list; scan; scan = scan->next) {
1928 GFile *file = G_FILE (scan->data);
1929 char *relative_pathname;
1930 char *archive_pathname;
1931
1932 relative_pathname = g_file_get_relative_path (base_dir, file);
1933 archive_pathname = g_build_filename (dest_dir, relative_pathname, NULL);
1934 g_hash_table_insert (add_data->files_to_add,
1935 g_strdup (archive_pathname),
1936 add_file_new (file, archive_pathname));
1937 add_data->n_files_to_add++;
1938
1939 g_free (archive_pathname);
1940 g_free (relative_pathname);
1941 }
1942
1943 _fr_archive_libarchive_save (archive,
1944 update,
1945 password,
1946 encrypt_header,
1947 compression,
1948 volume_size,
1949 cancellable,
1950 g_simple_async_result_new (G_OBJECT (archive),
1951 callback,
1952 user_data,
1953 fr_archive_add_files),
1954 _add_files_begin,
1955 _add_files_end,
1956 _add_files_entry_action,
1957 add_data,
1958 (GDestroyNotify) add_data_free);
1959 }
1960
1961
1962 /* -- remove -- */
1963
1964
1965 typedef struct {
1966 GHashTable *files_to_remove;
1967 gboolean remove_all_files;
1968 int n_files_to_remove;
1969 } RemoveData;
1970
1971
1972 static void
remove_data_free(RemoveData * remove_data)1973 remove_data_free (RemoveData *remove_data)
1974 {
1975 if (remove_data->files_to_remove != NULL)
1976 g_hash_table_unref (remove_data->files_to_remove);
1977 g_free (remove_data);
1978 }
1979
1980
1981 static void
_remove_files_begin(SaveData * save_data,gpointer user_data)1982 _remove_files_begin (SaveData *save_data,
1983 gpointer user_data)
1984 {
1985 LoadData *load_data = LOAD_DATA (save_data);
1986 RemoveData *remove_data = user_data;
1987
1988 fr_archive_progress_set_total_files (load_data->archive, remove_data->n_files_to_remove);
1989 fr_archive_progress_set_total_bytes (load_data->archive,
1990 FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->uncompressed_size);
1991 }
1992
1993
1994 static WriteAction
_remove_files_entry_action(SaveData * save_data,struct archive_entry * w_entry,gpointer user_data)1995 _remove_files_entry_action (SaveData *save_data,
1996 struct archive_entry *w_entry,
1997 gpointer user_data)
1998 {
1999 RemoveData *remove_data = user_data;
2000 LoadData *load_data = LOAD_DATA (save_data);
2001 WriteAction action;
2002 const char *pathname;
2003
2004 if (remove_data->remove_all_files)
2005 return WRITE_ACTION_SKIP_ENTRY;
2006
2007 action = WRITE_ACTION_WRITE_ENTRY;
2008 pathname = archive_entry_pathname (w_entry);
2009 if (g_hash_table_lookup (remove_data->files_to_remove, pathname) != NULL) {
2010 action = WRITE_ACTION_SKIP_ENTRY;
2011 remove_data->n_files_to_remove--;
2012 fr_archive_progress_inc_completed_files (load_data->archive, 1);
2013 g_hash_table_remove (remove_data->files_to_remove, pathname);
2014 }
2015
2016 return action;
2017 }
2018
2019
2020 static void
fr_archive_libarchive_remove_files(FrArchive * archive,GList * file_list,FrCompression compression,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2021 fr_archive_libarchive_remove_files (FrArchive *archive,
2022 GList *file_list,
2023 FrCompression compression,
2024 GCancellable *cancellable,
2025 GAsyncReadyCallback callback,
2026 gpointer user_data)
2027 {
2028 RemoveData *remove_data;
2029 GList *scan;
2030
2031 remove_data = g_new0 (RemoveData, 1);
2032 remove_data->remove_all_files = (file_list == NULL);
2033 if (! remove_data->remove_all_files) {
2034 remove_data->files_to_remove = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2035 remove_data->n_files_to_remove = 0;
2036 for (scan = file_list; scan; scan = scan->next) {
2037 g_hash_table_insert (remove_data->files_to_remove, g_strdup (scan->data), GINT_TO_POINTER (1));
2038 remove_data->n_files_to_remove++;
2039 }
2040 }
2041 else
2042 remove_data->n_files_to_remove = archive->files->len;
2043
2044 _fr_archive_libarchive_save (archive,
2045 FALSE,
2046 archive->password,
2047 archive->encrypt_header,
2048 compression,
2049 0,
2050 cancellable,
2051 g_simple_async_result_new (G_OBJECT (archive),
2052 callback,
2053 user_data,
2054 fr_archive_remove),
2055 _remove_files_begin,
2056 NULL,
2057 _remove_files_entry_action,
2058 remove_data,
2059 (GDestroyNotify) remove_data_free);
2060 }
2061
2062
2063 /* -- fr_archive_libarchive_rename -- */
2064
2065
2066 typedef struct {
2067 GHashTable *files_to_rename;
2068 int n_files_to_rename;
2069 } RenameData;
2070
2071
2072 static void
rename_data_free(RenameData * rename_data)2073 rename_data_free (RenameData *rename_data)
2074 {
2075 g_hash_table_unref (rename_data->files_to_rename);
2076 g_free (rename_data);
2077 }
2078
2079
2080 static void
_rename_files_begin(SaveData * save_data,gpointer user_data)2081 _rename_files_begin (SaveData *save_data,
2082 gpointer user_data)
2083 {
2084 LoadData *load_data = LOAD_DATA (save_data);
2085 RenameData *rename_data = user_data;
2086
2087 fr_archive_progress_set_total_files (load_data->archive, rename_data->n_files_to_rename);
2088 fr_archive_progress_set_total_bytes (load_data->archive,
2089 FR_ARCHIVE_LIBARCHIVE (load_data->archive)->priv->uncompressed_size);
2090 }
2091
2092
2093 static WriteAction
_rename_files_entry_action(SaveData * save_data,struct archive_entry * w_entry,gpointer user_data)2094 _rename_files_entry_action (SaveData *save_data,
2095 struct archive_entry *w_entry,
2096 gpointer user_data)
2097 {
2098 LoadData *load_data = LOAD_DATA (save_data);
2099 RenameData *rename_data = user_data;
2100 WriteAction action;
2101 const char *pathname;
2102 char *new_pathname;
2103
2104 action = WRITE_ACTION_WRITE_ENTRY;
2105 pathname = archive_entry_pathname (w_entry);
2106 new_pathname = g_hash_table_lookup (rename_data->files_to_rename, pathname);
2107 if (new_pathname != NULL) {
2108 archive_entry_set_pathname (w_entry, new_pathname);
2109 rename_data->n_files_to_rename--;
2110 g_hash_table_remove (rename_data->files_to_rename, pathname);
2111 fr_archive_progress_inc_completed_files (load_data->archive, 1);
2112 }
2113
2114 return action;
2115 }
2116
2117
2118 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)2119 fr_archive_libarchive_rename (FrArchive *archive,
2120 GList *file_list,
2121 const char *old_name,
2122 const char *new_name,
2123 const char *current_dir,
2124 gboolean is_dir,
2125 gboolean dir_in_archive,
2126 const char *original_path,
2127 GCancellable *cancellable,
2128 GAsyncReadyCallback callback,
2129 gpointer user_data)
2130 {
2131 RenameData *rename_data;
2132
2133 rename_data = g_new0 (RenameData, 1);
2134 rename_data->files_to_rename = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
2135 rename_data->n_files_to_rename = 0;
2136
2137 if (is_dir) {
2138 char *old_dirname;
2139 char *new_dirname;
2140 int old_dirname_len;
2141 GList *scan;
2142
2143 old_dirname = g_build_filename (current_dir + 1, old_name, "/", NULL);
2144 old_dirname_len = strlen (old_dirname);
2145 new_dirname = g_build_filename (current_dir + 1, new_name, "/", NULL);
2146
2147 for (scan = file_list; scan; scan = scan->next) {
2148 char *old_pathname = scan->data;
2149 char *new_pathname;
2150
2151 new_pathname = g_build_filename (new_dirname, old_pathname + old_dirname_len, NULL);
2152 g_hash_table_insert (rename_data->files_to_rename, g_strdup (old_pathname), new_pathname);
2153 rename_data->n_files_to_rename++;
2154 }
2155
2156 g_free (new_dirname);
2157 g_free (old_dirname);
2158 }
2159 else {
2160 char *old_pathname = (char *) file_list->data;
2161 char *new_pathname;
2162
2163 new_pathname = g_build_filename (current_dir + 1, new_name, NULL);
2164 g_hash_table_insert (rename_data->files_to_rename,
2165 g_strdup (old_pathname),
2166 new_pathname);
2167 rename_data->n_files_to_rename = 1;
2168 }
2169
2170 _fr_archive_libarchive_save (archive,
2171 FALSE,
2172 archive->password,
2173 archive->encrypt_header,
2174 archive->compression,
2175 0,
2176 cancellable,
2177 g_simple_async_result_new (G_OBJECT (archive),
2178 callback,
2179 user_data,
2180 fr_archive_rename),
2181 _rename_files_begin,
2182 NULL,
2183 _rename_files_entry_action,
2184 rename_data,
2185 (GDestroyNotify) rename_data_free);
2186 }
2187
2188
2189 /* -- fr_archive_libarchive_paste_clipboard -- */
2190
2191
2192 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)2193 fr_archive_libarchive_paste_clipboard (FrArchive *archive,
2194 GFile *archive_file,
2195 char *password,
2196 gboolean encrypt_header,
2197 FrCompression compression,
2198 guint volume_size,
2199 FrClipboardOp op,
2200 char *base_dir,
2201 GList *files,
2202 GFile *tmp_dir,
2203 char *current_dir,
2204 GCancellable *cancellable,
2205 GAsyncReadyCallback callback,
2206 gpointer user_data)
2207 {
2208 AddData *add_data;
2209 GList *scan;
2210
2211 g_return_if_fail (base_dir != NULL);
2212
2213 add_data = add_data_new ();
2214
2215 current_dir = current_dir + 1;
2216 for (scan = files; scan; scan = scan->next) {
2217 const char *old_name = (char *) scan->data;
2218 char *new_name;
2219 GFile *file;
2220
2221 new_name = g_build_filename (current_dir, old_name + strlen (base_dir) - 1, NULL);
2222 file = _g_file_append_path (tmp_dir, old_name, NULL);
2223 g_hash_table_insert (add_data->files_to_add, new_name, add_file_new (file, new_name));
2224 add_data->n_files_to_add++;
2225
2226 g_object_unref (file);
2227 }
2228
2229 _fr_archive_libarchive_save (archive,
2230 FALSE,
2231 password,
2232 encrypt_header,
2233 compression,
2234 volume_size,
2235 cancellable,
2236 g_simple_async_result_new (G_OBJECT (archive),
2237 callback,
2238 user_data,
2239 fr_archive_paste_clipboard),
2240 _add_files_begin,
2241 _add_files_end,
2242 _add_files_entry_action,
2243 add_data,
2244 (GDestroyNotify) add_data_free);
2245 }
2246
2247
2248 /* -- fr_archive_libarchive_add_dropped_files -- */
2249
2250
2251 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)2252 fr_archive_libarchive_add_dropped_files (FrArchive *archive,
2253 GList *file_list,
2254 const char *dest_dir,
2255 const char *password,
2256 gboolean encrypt_header,
2257 FrCompression compression,
2258 guint volume_size,
2259 GCancellable *cancellable,
2260 GAsyncReadyCallback callback,
2261 gpointer user_data)
2262 {
2263 AddData *add_data;
2264 GList *scan;
2265
2266 add_data = add_data_new ();
2267
2268 if (dest_dir[0] == '/')
2269 dest_dir += 1;
2270
2271 for (scan = file_list; scan; scan = scan->next) {
2272 GFile *file = G_FILE (scan->data);
2273 char *basename;
2274 char *archive_pathname;
2275
2276 basename = g_file_get_basename (file);
2277 archive_pathname = g_build_filename (dest_dir, basename, NULL);
2278 g_hash_table_insert (add_data->files_to_add,
2279 g_strdup (archive_pathname),
2280 add_file_new (file, archive_pathname));
2281
2282 g_free (archive_pathname);
2283 g_free (basename);
2284 }
2285
2286 _fr_archive_libarchive_save (archive,
2287 FALSE,
2288 password,
2289 encrypt_header,
2290 compression,
2291 volume_size,
2292 cancellable,
2293 g_simple_async_result_new (G_OBJECT (archive),
2294 callback,
2295 user_data,
2296 fr_archive_add_dropped_items),
2297 _add_files_begin,
2298 _add_files_end,
2299 _add_files_entry_action,
2300 add_data,
2301 (GDestroyNotify) add_data_free);
2302 }
2303
2304
2305 /* -- fr_archive_libarchive_update_open_files -- */
2306
2307
2308 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)2309 fr_archive_libarchive_update_open_files (FrArchive *archive,
2310 GList *file_list,
2311 GList *dir_list,
2312 const char *password,
2313 gboolean encrypt_header,
2314 FrCompression compression,
2315 guint volume_size,
2316 GCancellable *cancellable,
2317 GAsyncReadyCallback callback,
2318 gpointer user_data)
2319 {
2320 AddData *add_data;
2321 GList *scan_file;
2322 GList *scan_dir;
2323
2324 add_data = add_data_new ();
2325
2326 for (scan_file = file_list, scan_dir = dir_list;
2327 scan_file && scan_dir;
2328 scan_file = scan_file->next, scan_dir = scan_dir->next)
2329 {
2330 GFile *temp_dir = G_FILE (scan_dir->data);
2331 GFile *extracted_file = G_FILE (scan_file->data);
2332 char *archive_pathname;
2333
2334 archive_pathname = g_file_get_relative_path (temp_dir, extracted_file);
2335 g_hash_table_insert (add_data->files_to_add, g_strdup (archive_pathname), add_file_new (extracted_file, archive_pathname));
2336 add_data->n_files_to_add++;
2337
2338 g_free (archive_pathname);
2339 }
2340
2341 _fr_archive_libarchive_save (archive,
2342 FALSE,
2343 password,
2344 encrypt_header,
2345 compression,
2346 volume_size,
2347 cancellable,
2348 g_simple_async_result_new (G_OBJECT (archive),
2349 callback,
2350 user_data,
2351 fr_archive_update_open_files),
2352 _add_files_begin,
2353 _add_files_end,
2354 _add_files_entry_action,
2355 add_data,
2356 (GDestroyNotify) add_data_free);
2357 }
2358
2359
2360 static void
fr_archive_libarchive_class_init(FrArchiveLibarchiveClass * klass)2361 fr_archive_libarchive_class_init (FrArchiveLibarchiveClass *klass)
2362 {
2363 GObjectClass *gobject_class;
2364 FrArchiveClass *archive_class;
2365
2366 fr_archive_libarchive_parent_class = g_type_class_peek_parent (klass);
2367
2368 gobject_class = G_OBJECT_CLASS (klass);
2369 gobject_class->finalize = fr_archive_libarchive_finalize;
2370
2371 archive_class = FR_ARCHIVE_CLASS (klass);
2372 archive_class->get_mime_types = fr_archive_libarchive_get_mime_types;
2373 archive_class->get_capabilities = fr_archive_libarchive_get_capabilities;
2374 archive_class->get_packages = fr_archive_libarchive_get_packages;
2375 archive_class->list = fr_archive_libarchive_list;
2376 archive_class->extract_files = fr_archive_libarchive_extract_files;
2377 archive_class->add_files = fr_archive_libarchive_add_files;
2378 archive_class->remove_files = fr_archive_libarchive_remove_files;
2379 archive_class->rename = fr_archive_libarchive_rename;
2380 archive_class->paste_clipboard = fr_archive_libarchive_paste_clipboard;
2381 archive_class->add_dropped_files = fr_archive_libarchive_add_dropped_files;
2382 archive_class->update_open_files = fr_archive_libarchive_update_open_files;
2383 }
2384
2385
2386 static void
fr_archive_libarchive_init(FrArchiveLibarchive * self)2387 fr_archive_libarchive_init (FrArchiveLibarchive *self)
2388 {
2389 FrArchive *base = FR_ARCHIVE (self);
2390
2391 self->priv = fr_archive_libarchive_get_instance_private (self);
2392
2393 base->propAddCanReplace = TRUE;
2394 base->propAddCanUpdate = TRUE;
2395 base->propAddCanStoreFolders = TRUE;
2396 base->propAddCanStoreLinks = TRUE;
2397 base->propExtractCanAvoidOverwrite = TRUE;
2398 base->propExtractCanSkipOlder = TRUE;
2399 base->propExtractCanJunkPaths = TRUE;
2400 base->propCanExtractAll = TRUE;
2401 base->propCanDeleteNonEmptyFolders = TRUE;
2402 base->propCanExtractNonEmptyFolders = TRUE;
2403 }
2404