aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTing-Wei Lan <lantw44@gmail.com>2013-07-13 23:00:56 +0800
committerLAN-TW <lantw44@gmail.com>2013-07-13 23:11:19 +0800
commitcb972af21832ededc9534253a86bbf913d7c1368 (patch)
tree517286bc81492de8af6e713fc2dd045c2f9469ea
parentbb257548a99b9027cef90e5acdefb3b7da9769fa (diff)
downloadgsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar
gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar.gz
gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar.bz2
gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar.lz
gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar.xz
gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar.zst
gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.zip
Add new function autoar_extract_start to do the extraction
This function is not tested and does not write files to disks currently.
-rw-r--r--autoarchive/autoar-extract.c230
-rw-r--r--autoarchive/autoar-extract.h2
2 files changed, 230 insertions, 2 deletions
diff --git a/autoarchive/autoar-extract.c b/autoarchive/autoar-extract.c
index ecaf20d8a..1dce79008 100644
--- a/autoarchive/autoar-extract.c
+++ b/autoarchive/autoar-extract.c
@@ -27,10 +27,10 @@
#include "autoar-extract.h"
-#include <gio/gio.h>
-#include <string.h>
#include <archive.h>
#include <archive_entry.h>
+#include <gio/gio.h>
+#include <string.h>
G_DEFINE_TYPE (AutoarExtract, autoar_extract, G_TYPE_OBJECT)
@@ -331,6 +331,49 @@ libarchive_read_read_cb (struct archive *ar_read,
return read_size;
}
+static char*
+_g_filename_basename_remove_extension (const char *filename)
+{
+ char *dot_location;
+ char *basename;
+
+ if (filename == NULL) {
+ return NULL;
+ }
+
+ /* filename must not be directory, so we do not get a bad basename. */
+ basename = g_path_get_basename (filename);
+
+ dot_location = strrchr (basename, '.');
+ if (dot_location == NULL || dot_location == basename) {
+ return basename;
+ }
+
+ if (dot_location - 4 > basename) {
+ if (strcmp (dot_location - 4, ".tar") == 0) {
+ dot_location -= 4;
+ }
+ }
+
+ *dot_location = '\0';
+
+ return basename;
+}
+
+static gboolean
+archive_extract_do_pattern_check (const char *pathname)
+{
+ return TRUE;
+}
+
+static void
+archive_extract_do_write_entry (AutoarExtract *arextract,
+ struct archive *a,
+ struct archive_entry *entry,
+ GFile *dest)
+{
+}
+
/* Additional marshaller generated by glib-genmarshal
* Command: echo "VOID:DOUBLE,DOUBLE" | glib-genmarshal --header */
@@ -574,3 +617,186 @@ autoar_extract_new (const char *source,
output,
NULL);
}
+
+void
+autoar_extract_start (AutoarExtract* arextract)
+{
+ struct archive *a;
+ struct archive_entry *entry;
+
+ char *pathname_prefix;
+ int pathname_prefix_len;
+
+ gboolean has_top_level_dir;
+ char *top_level_dir_basename;
+ char *top_level_dir_basename_modified;
+ GFile *top_level_parent_dir;
+ GFile *top_level_dir;
+
+ GFile *source;
+
+ int i;
+
+ g_return_if_fail (AUTOAR_IS_EXTRACT (arextract));
+ g_return_if_fail (arextract->priv->source != NULL);
+ g_return_if_fail (arextract->priv->output != NULL);
+
+ a = archive_read_new ();
+ archive_read_support_filter_all (a);
+ archive_read_support_format_all (a);
+
+ /* Reset all counter variables */
+ arextract->priv->size = 0;
+ arextract->priv->completed_size = 0;
+ arextract->priv->files = 0;
+ arextract->priv->completed_files = 0;
+
+ /* Step 1: Scan all file names in the archive
+ * We have to check whether the archive contains a top-level directory
+ * before performing the extraction. We emit the "scanned" signal when
+ * the checking is completed. */
+ archive_read_open (a,
+ arextract,
+ libarchive_read_open_cb,
+ libarchive_read_read_cb,
+ libarchive_read_close_cb);
+ pathname_prefix = NULL;
+ has_top_level_dir = TRUE;
+ while (archive_read_next_header (a, &entry) == ARCHIVE_OK) {
+ const char *pathname, *dir_sep_location;
+ size_t skip_len, prefix_len;
+
+ pathname = archive_entry_pathname (entry);
+
+ /* TODO: Add file name pattern check here */
+
+ if (pathname_prefix == NULL) {
+ skip_len = strspn (pathname, "./");
+ dir_sep_location = strchr (pathname + skip_len, '/');
+ if (dir_sep_location == NULL) {
+ prefix_len = strlen (pathname);
+ } else {
+ prefix_len = dir_sep_location - pathname;
+ }
+ pathname_prefix = g_strndup (pathname, prefix_len);
+ pathname_prefix_len = prefix_len;
+ } else {
+ if (!g_str_has_prefix (pathname, pathname_prefix)) {
+ has_top_level_dir = FALSE;
+ }
+ }
+ archive_read_data_skip (a);
+ arextract->priv->files++;
+ }
+ g_free (pathname_prefix);
+ archive_read_close (a);
+ if (arextract->priv->error != NULL) {
+ g_signal_emit_by_name (arextract, "error", arextract->priv->error);
+ archive_read_free (a);
+ return;
+ }
+ g_signal_emit_by_name (arextract, "scanned");
+
+ /* Step 2: Create necessary directories */
+ top_level_dir_basename = _g_filename_basename_remove_extension (arextract->priv->source);
+ top_level_parent_dir = g_file_new_for_commandline_arg (arextract->priv->output);
+ top_level_dir = g_file_get_child (top_level_parent_dir, top_level_dir_basename);
+
+ top_level_dir_basename_modified = NULL;
+ for (i=1; g_file_query_exists (top_level_dir, NULL); i++) {
+ g_free (top_level_dir_basename_modified);
+ g_object_unref (top_level_dir);
+ top_level_dir_basename_modified = g_strdup_printf ("%s (%d)",
+ top_level_dir_basename,
+ i);
+ top_level_dir = g_file_get_child (top_level_parent_dir,
+ top_level_dir_basename_modified);
+ }
+
+ g_file_make_directory_with_parents (top_level_dir, NULL, &(arextract->priv->error));
+
+ g_free (top_level_dir_basename);
+ g_free (top_level_dir_basename_modified);
+ g_object_unref (top_level_parent_dir);
+
+ if (arextract->priv->error != NULL) {
+ g_signal_emit_by_name (arextract, "error", arextract->priv->error);
+ g_object_unref (top_level_dir);
+ archive_read_free (a);
+ return;
+ }
+
+ /* Step 3: Extract files
+ * We have to re-open the archive to extract files */
+ archive_read_open (a,
+ arextract,
+ libarchive_read_open_cb,
+ libarchive_read_read_cb,
+ libarchive_read_close_cb);
+ while (archive_read_next_header (a, &entry) == ARCHIVE_OK) {
+ const char *pathname;
+ const char *pathname_skip_prefix;
+ char **pathname_chunks;
+
+ GFile *extracted_filename;
+ GFileInfo *restored_fileinfo;
+
+ pathname = archive_entry_pathname (entry);
+ if (has_top_level_dir)
+ pathname_skip_prefix = pathname + pathname_prefix_len;
+ else
+ pathname_skip_prefix = pathname + strspn (pathname, "./");
+
+ extracted_filename = g_file_get_child (top_level_dir, pathname_skip_prefix);
+
+ /* Extracted file should not be located outside the top level directory. */
+ if (!g_file_has_prefix (extracted_filename, top_level_dir)) {
+ pathname_chunks = g_strsplit (pathname_skip_prefix, "/", G_MAXINT);
+ for (i = 0; pathname_chunks[i] != NULL; i++) {
+ if (strcmp (pathname_chunks[i], "..") == 0) {
+ char *pathname_sanitized;
+
+ *pathname_chunks[i] = '\0';
+ pathname_sanitized = g_strjoinv ("/", pathname_chunks);
+
+ g_object_unref (extracted_filename);
+ extracted_filename = g_file_get_child (top_level_dir, pathname_sanitized);
+
+ g_free (pathname_sanitized);
+
+ if (g_file_has_prefix (extracted_filename, top_level_dir))
+ break;
+ }
+ }
+ g_strfreev (pathname_chunks);
+ }
+
+ /* TODO: Add file name pattern check here */
+ archive_extract_do_pattern_check (pathname_skip_prefix);
+
+ /* TODO: Write entry to disk */
+ archive_extract_do_write_entry (arextract, a, entry, extracted_filename);
+
+ arextract->priv->completed_files++;
+ g_signal_emit_by_name (arextract,
+ "progress",
+ ((double)(arextract->priv->size)) / ((double)(arextract->priv->completed_size)),
+ ((double)(arextract->priv->files)) / ((double)(arextract->priv->completed_files)));
+ g_object_unref (extracted_filename);
+ }
+
+ g_object_unref (top_level_dir);
+ archive_read_close (a);
+ archive_read_free (a);
+ if (arextract->priv->error != NULL) {
+ g_signal_emit_by_name (arextract, "error", arextract->priv->error);
+ return;
+ }
+
+ /* If the extraction is completed successfully, remove the source file.
+ * Errors are not fatal because we have completed our work. */
+ source = g_file_new_for_commandline_arg (arextract->priv->source);
+ g_file_delete (source, NULL, NULL);
+ g_object_unref (source);
+ g_signal_emit_by_name (arextract, "completed");
+}
diff --git a/autoarchive/autoar-extract.h b/autoarchive/autoar-extract.h
index 2d93228f2..66bdbd367 100644
--- a/autoarchive/autoar-extract.h
+++ b/autoarchive/autoar-extract.h
@@ -67,6 +67,8 @@ GType autoar_extract_get_type (void) G_GNUC_CONST;
AutoarExtract *autoar_extract_new (const char *source,
const char *output);
+void autoar_extract_start (AutoarExtract* arextract);
+
char *autoar_extract_get_source (AutoarExtract *arextract);
char *autoar_extract_get_output (AutoarExtract *arextract);
guint64 autoar_extract_get_size (AutoarExtract *arextract);