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