diff --git a/.github/workflows/system-tests.yml b/.github/workflows/system-tests.yml index 1e94a846..3450ec0c 100644 --- a/.github/workflows/system-tests.yml +++ b/.github/workflows/system-tests.yml @@ -33,7 +33,7 @@ jobs: echo "Create package" cp $f tests/data/layout.yaml G_DEBUG=fatal-warnings partup -d -C tests/data package pkg.partup \ - layout.yaml random.bin lorem.txt lorem.tar root.ext4 + layout.yaml random.bin lorem.txt lorem.tar dir-struct.tar root.ext4 echo "Show package content" sudo G_DEBUG=fatal-warnings partup -s show pkg.partup echo "Install package to loop device" diff --git a/doc/layout-config-reference.rst b/doc/layout-config-reference.rst index 6be91ebb..e9fc81ba 100644 --- a/doc/layout-config-reference.rst +++ b/doc/layout-config-reference.rst @@ -348,6 +348,30 @@ at least a ``filename``. For verifying the checksum of the given input file by checked against the provided file before writing to the target partition or volume. +``exclude`` (sequence) + Paths to exclude for this partition. Paths to be excluded should be + provided as a sequence of strings. This only works on partitions with a valid + filesystem. + + If the input is a ``.tar`` archive, the specified paths are excluded from + extraction using tar's ``--exclude`` option: + https://manpages.debian.org/unstable/tar/tar.1.en.html#exclude + + For other input file types, the specified paths are deleted on the partition + after writing the input files. + + ``exclude`` takes precedence over ``only``. + +``only`` (sequence) + A list of paths to only extract/keep on the corresponding partition. This + only works on partitions with a valid filesystem. + + If the input is a ``.tar`` archive, only the specified members are extracted: + https://manpages.debian.org/unstable/tar/tar.1.en.html + + For other input file types, any other paths are deleted on the partition + after writing the input files, except the ones specified. + .. _supported-file-types: Supported File Types diff --git a/src/pu-emmc.c b/src/pu-emmc.c index 1100c172..376b700a 100644 --- a/src/pu-emmc.c +++ b/src/pu-emmc.c @@ -24,6 +24,8 @@ typedef struct _PuEmmcInput { gchar *filename; gchar *md5sum; gchar *sha256sum; + GList *exclude; + GList *only; /* Internal members */ gsize _size; @@ -375,7 +377,8 @@ pu_emmc_write_data(PuFlash *flash, if (g_regex_match_simple(".tar", path, G_REGEX_CASELESS, 0)) { if (!pu_mount(part_path, part_mount, NULL, NULL, error)) return FALSE; - if (!pu_archive_extract(path, part_mount, error)) + if (!pu_archive_extract(path, part_mount, input->exclude, + input->only, error)) return FALSE; if (!pu_umount(part_mount, error)) return FALSE; @@ -387,6 +390,15 @@ pu_emmc_write_data(PuFlash *flash, return FALSE; if (!pu_set_ext_label(part_path, part->label, error)) return FALSE; + if (input->exclude || input->only) { + if (!pu_mount(part_path, part_mount, NULL, NULL, error)) + return FALSE; + if (!pu_path_remove_exclude_only(part_mount, input->exclude, + input->only, error)) + return FALSE; + if (!pu_umount(part_mount, error)) + return FALSE; + } } else if (!part->filesystem) { if (!pu_write_raw(path, part_path, self->device, 0, 0, 0, error)) return FALSE; @@ -395,6 +407,11 @@ pu_emmc_write_data(PuFlash *flash, return FALSE; if (!pu_file_copy(path, part_mount, error)) return FALSE; + if (input->exclude || input->only) { + if (!pu_path_remove_exclude_only(part_mount, input->exclude, + input->only, error)) + return FALSE; + } if (!pu_umount(part_mount, error)) return FALSE; } @@ -601,6 +618,8 @@ pu_emmc_class_finalize(GObject *object) g_free(in->filename); g_free(in->md5sum); g_free(in->sha256sum); + g_list_free(g_steal_pointer(&in->exclude)); + g_list_free(g_steal_pointer(&in->only)); g_free(in); } g_list_free(g_steal_pointer(&part->input)); @@ -1058,6 +1077,30 @@ pu_emmc_parse_partitions(PuEmmc *emmc, input->filename = pu_hash_table_lookup_string(iv->data.mapping, "filename", ""); input->md5sum = pu_hash_table_lookup_string(iv->data.mapping, "md5sum", ""); input->sha256sum = pu_hash_table_lookup_string(iv->data.mapping, "sha256sum", ""); + GList *exclude_list = pu_hash_table_lookup_list(iv->data.mapping, "exclude", NULL); + if (exclude_list) { + for (GList *e = exclude_list; e; e = e->next) { + PuConfigValue *ev = e->data; + if (ev->type != PU_CONFIG_VALUE_TYPE_STRING) { + g_set_error(error, PU_ERROR, PU_ERROR_EMMC_PARSE, + "'exclude' does not contain a sequence of strings"); + return FALSE; + } + input->exclude = g_list_prepend(input->exclude, ev->data.string); + } + } + GList *only_list = pu_hash_table_lookup_list(iv->data.mapping, "only", NULL); + if (only_list) { + for (GList *o = only_list; o; o = o->next) { + PuConfigValue *ov = o->data; + if (ov->type != PU_CONFIG_VALUE_TYPE_STRING) { + g_set_error(error, PU_ERROR, PU_ERROR_EMMC_PARSE, + "'only' does not contain a sequence of strings"); + return FALSE; + } + input->only = g_list_prepend(input->only, ov->data.string); + } + } part->input = g_list_prepend(part->input, input); g_debug("Parsed partition input: filename=%s md5sum=%s sha256sum=%s", diff --git a/src/pu-utils.c b/src/pu-utils.c index 863e3a3b..420d209f 100644 --- a/src/pu-utils.c +++ b/src/pu-utils.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -59,9 +60,11 @@ pu_spawn_command_line_sync(const gchar *command_line, gboolean pu_archive_extract(const gchar *filename, const gchar *dest, + GList *exclude, + GList *only, GError **error) { - g_autofree gchar *cmd = NULL; + g_autoptr(GString) cmd = NULL; g_return_val_if_fail(filename != NULL, FALSE); g_return_val_if_fail(dest != NULL, FALSE); @@ -69,9 +72,26 @@ pu_archive_extract(const gchar *filename, g_debug("Extracting '%s' to '%s'", filename, dest); - cmd = g_strdup_printf("tar -xf %s -C %s", filename, dest); + cmd = g_string_new("tar"); - if (!pu_spawn_command_line_sync(cmd, error)) { + /* TODO: trailing slashes may cause problems here? */ + for (GList *e = exclude; e; e = e->next) { + gchar *es = e->data; + g_string_append_printf(cmd, " --exclude %s", es); + } + + g_string_append_printf(cmd, " -C %s -xf %s", dest, filename); + + for (GList *o = only; o; o = o->next) { + gchar *os = o->data; + if (g_regex_match_simple("[!^*?\\[\\]]", os, 0, 0)) { + g_string_append_printf(cmd, " --wildcards %s", os); + } else { + g_string_append_printf(cmd, " --no-wildcards %s", os); + } + } + + if (!pu_spawn_command_line_sync(cmd->str, error)) { g_prefix_error(error, "Failed extracting '%s' to '%s': ", filename, dest); return FALSE; } @@ -584,3 +604,185 @@ pu_str_pre_remove(gchar *string, return string; } + +gboolean +pu_file_remove_recursive(GFile *file, + GHashTable *skip, + GError **error) +{ + g_autoptr(GFileEnumerator) dir_enum = NULL; + g_autoptr(GFileInfo) info = NULL; + gboolean skip_delete = FALSE; + + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (skip) { + g_autofree gchar *file_path = NULL; + GHashTableIter iter; + gpointer key; + + file_path = g_file_get_path(file); + g_hash_table_iter_init(&iter, skip); + while (g_hash_table_iter_next(&iter, &key, NULL)) { + if (g_str_equal(key, file_path)) { + /* Skip deletion of 'file_path' (and possible children), because + * it gets retained */ + return TRUE; + } + if (g_str_has_prefix(key, file_path)) { + /* Skip deletion of 'file_path', because prefix 'key' gets + * retained, but still evaluate possible children */ + skip_delete = TRUE; + break; + } + } + } + + dir_enum = g_file_enumerate_children(file, G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, NULL); + if (dir_enum) { + while ((info = g_file_enumerator_next_file(dir_enum, NULL, NULL)) != NULL) { + g_autoptr(GFile) child = NULL; + child = g_file_enumerator_get_child(dir_enum, info); + if (!pu_file_remove_recursive(child, skip, error)) { + if (error) { + g_prefix_error(error, "Failed recursive file removal"); + } else { + g_set_error(error, PU_ERROR, PU_ERROR_FAILED, + "Failed recursive file removal"); + } + return FALSE; + } + } + } + + if (skip_delete) { + return TRUE; + } + + return g_file_delete(file, NULL, error); +} + +static GHashTable * +canonicalize_path_list(GList *paths, + GError **error) +{ + g_autoptr(GHashTable) table = NULL; + glob_t gl; + gboolean first = TRUE; + + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!paths) { + return NULL; + } + + table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + + /* TODO: support wildcard paths, like "*foo.txt", which should match any + * directory containing foo.txt. */ + for (GList *p = paths; p; p = p->next) { + const gchar *ps = p->data; + gint flags = GLOB_NOSORT | (first ? 0 : GLOB_APPEND); + gint ret = glob(ps, flags, NULL, &gl); + g_debug("ps: %s", ps); + + if (ret == 0) { + first = FALSE; + } else if (ret == GLOB_NOMATCH) { + g_debug("glob did not match anything, continue"); + continue; + } else { + g_set_error(error, PU_ERROR, PU_ERROR_FAILED, + "glob() failed on '%s': %d", ps, ret); + return NULL; + } + } + g_debug("gl_pathc: %ld", gl.gl_pathc); + + if (!first) { + for (gsize i = 0; i < gl.gl_pathc; i++) { + gchar *canon = g_canonicalize_filename(gl.gl_pathv[i], NULL); + g_debug("canon: %s", canon); + g_hash_table_replace(table, canon, NULL); + } + globfree(&gl); + } + + return g_steal_pointer(&table); +} + +/* TODO: Reconsider function name: Should be remove recursive exclusion set */ +gboolean +pu_path_remove_exclude_only(const gchar *path, + GList *exclude, + GList *only, + GError **error) +{ + g_autoptr(GHashTable) exclude_table = NULL; + g_autoptr(GHashTable) only_table = NULL; + g_autoptr(GHashTable) all_table = NULL; + g_autofree GList *all = NULL; + + g_return_val_if_fail(g_strcmp0(path, "") > 0, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (exclude) { + /* Create a list of directories and files that match "exclude" */ + g_debug("EXCLUDE"); + exclude_table = canonicalize_path_list(exclude, error); + if (!exclude_table) { + g_prefix_error(error, "Failed parsing 'exclude' paths: "); + return FALSE; + } + + /* Delete all entries in "exclude" */ + GHashTableIter iter; + gpointer key; + + g_hash_table_iter_init(&iter, exclude_table); + while (g_hash_table_iter_next(&iter, &key, NULL)) { + g_autoptr(GFile) file = NULL; + file = g_file_new_for_path(key); + if (!pu_file_remove_recursive(file, NULL, error)) { + return FALSE; + } + } + } + + if (only) { + /* Create a list of directories and files that match "only" */ + g_debug("ONLY"); + only_table = canonicalize_path_list(only, error); + if (!only_table) { + g_prefix_error(error, "Failed parsing 'only' paths: "); + return FALSE; + } + + /* Create a list of directories and files that matches everything */ + g_debug("ALL"); + all = g_list_prepend(all, g_build_filename(path, "*", NULL)); + all_table = canonicalize_path_list(all, error); + if (!all_table) { + g_prefix_error(error, "Failed parsing 'all' paths: "); + return FALSE; + } + + /* Delete everything, except entries in "only" */ + g_debug("REMOVE"); + GHashTableIter iter; + gpointer key; + + g_hash_table_iter_init(&iter, all_table); + while (g_hash_table_iter_next(&iter, &key, NULL)) { + g_autoptr(GFile) file = NULL; + file = g_file_new_for_path(key); + if (!pu_file_remove_recursive(file, only_table, error)) { + return FALSE; + } + } + } + + return TRUE; +} diff --git a/src/pu-utils.h b/src/pu-utils.h index 8b488a98..c206c232 100644 --- a/src/pu-utils.h +++ b/src/pu-utils.h @@ -7,12 +7,15 @@ #define PARTUP_UTILS_H #include +#include #include gboolean pu_spawn_command_line_sync(const gchar *command_line, GError **error); gboolean pu_archive_extract(const gchar *filename, const gchar *dest, + GList *exclude, + GList *only, GError **error); gboolean pu_make_filesystem(const gchar *part, const gchar *type, @@ -65,5 +68,14 @@ gchar * pu_device_get_partition_pattern(const gchar *device, GError **error); gchar * pu_str_pre_remove(gchar *string, guint n); +/*GHashTable * pu_hash_table_intersect(GHashTable *set_a, + GHashTable *set_b);*/ +gboolean pu_file_remove_recursive(GFile *file, + GHashTable *skip, + GError **error); +gboolean pu_path_remove_exclude_only(const gchar *path, + GList *exclude, + GList *only, + GError **error); #endif /* PARTUP_UTILS_H */ diff --git a/tests/config/system-tests/exclude-only.yaml b/tests/config/system-tests/exclude-only.yaml new file mode 100644 index 00000000..a4442629 --- /dev/null +++ b/tests/config/system-tests/exclude-only.yaml @@ -0,0 +1,37 @@ +api-version: 4 +disklabel: gpt + +partitions: + - label: boot + filesystem: fat32 + size: 16MiB + offset: 1MiB + flags: + - boot + input: + - filename: lorem.txt + - label: root1 + filesystem: ext4 + expand: true + input: + - filename: dir-struct.tar + exclude: + - empty + - label: root2 + filesystem: ext4 + expand: true + input: + - filename: dir-struct.tar + only: + - foo + - label: root3 + filesystem: ext4 + expand: true + input: + - filename: dir-struct.tar + exclude: + - empty + - foo/tested.c + only: + - foo + - nested diff --git a/tests/data/dir-struct.tar b/tests/data/dir-struct.tar new file mode 100644 index 00000000..813319e7 Binary files /dev/null and b/tests/data/dir-struct.tar differ diff --git a/tests/data/foobar.tar b/tests/data/foobar.tar new file mode 100644 index 00000000..61cc877d Binary files /dev/null and b/tests/data/foobar.tar differ diff --git a/tests/data/lorem.tar b/tests/data/lorem.tar index 87f90d50..e888db3c 100644 Binary files a/tests/data/lorem.tar and b/tests/data/lorem.tar differ diff --git a/tests/utils.c b/tests/utils.c index 3e4431b0..b648e94c 100644 --- a/tests/utils.c +++ b/tests/utils.c @@ -16,19 +16,108 @@ test_archive_extract(void) { g_autoptr(GError) error = NULL; g_autofree gchar *dest = NULL; - g_autofree gchar *out_file = NULL; - const gchar *source = "data/lorem.tar"; + g_autofree gchar *lorem_file = NULL; + g_autofree gchar *ipsum_file = NULL; + g_autofree gchar *dolor_file = NULL; + g_autofree gchar *foo_dir = NULL; + g_autofree gchar *foo_file = NULL; + g_autofree gchar *bar_dir = NULL; + g_autofree gchar *bar_file = NULL; + g_autofree gchar *baz_dir = NULL; + g_autofree gchar *baz_file = NULL; + g_autoptr(GList) exclude = NULL; + g_autoptr(GList) list_ba = NULL; + g_autoptr(GList) list_notfound = NULL; + g_autoptr(GList) only = NULL; dest = g_dir_make_tmp("partup-XXXXXX", &error); g_assert_no_error(error); - out_file = g_build_filename(dest, "lorem.txt", NULL); + lorem_file = g_build_filename(dest, "lorem.txt", NULL); + ipsum_file = g_build_filename(dest, "ipsum.txt", NULL); + dolor_file = g_build_filename(dest, "dolor.txt", NULL); - g_assert_true(pu_archive_extract(source, dest, &error)); + exclude = g_list_prepend(exclude, "lorem.txt"); + exclude = g_list_prepend(exclude, "ipsum.txt"); + + only = g_list_prepend(only, "ipsum.txt"); + only = g_list_prepend(only, "dolor.txt"); + + list_ba = g_list_prepend(list_ba, "ba*"); + list_notfound = g_list_prepend(list_notfound, "notfound*"); + + foo_dir = g_build_filename(dest, "foo", NULL); + foo_file = g_build_filename(dest, "foo/foo.txt", NULL); + bar_dir = g_build_filename(dest, "bar", NULL); + bar_file = g_build_filename(dest, "bar/bar.cfg", NULL); + baz_dir = g_build_filename(dest, "baz", NULL); + baz_file = g_build_filename(dest, "baz/baz.yaml", NULL); + + /* Extract all */ + g_assert_true(pu_archive_extract("data/lorem.tar", dest, NULL, NULL, &error)); + g_assert_no_error(error); + g_assert_true(g_file_test(lorem_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(ipsum_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(dolor_file, G_FILE_TEST_IS_REGULAR)); + g_assert_cmpint(g_remove(lorem_file), ==, 0); + g_assert_cmpint(g_remove(ipsum_file), ==, 0); + g_assert_cmpint(g_remove(dolor_file), ==, 0); + + /* Extract all excluding two */ + g_assert_true(pu_archive_extract("data/lorem.tar", dest, exclude, NULL, &error)); g_assert_no_error(error); - g_assert_true(g_file_test(out_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(lorem_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(ipsum_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(dolor_file, G_FILE_TEST_IS_REGULAR)); + g_assert_cmpint(g_remove(dolor_file), ==, 0); + + /* Extract only two */ + g_assert_true(pu_archive_extract("data/lorem.tar", dest, NULL, only, &error)); + g_assert_no_error(error); + g_assert_false(g_file_test(lorem_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(ipsum_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(dolor_file, G_FILE_TEST_IS_REGULAR)); + g_assert_cmpint(g_remove(ipsum_file), ==, 0); + g_assert_cmpint(g_remove(dolor_file), ==, 0); + + /* Extract only two excluding two */ + g_assert_true(pu_archive_extract("data/lorem.tar", dest, exclude, only, &error)); + g_assert_no_error(error); + g_assert_false(g_file_test(lorem_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(ipsum_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(dolor_file, G_FILE_TEST_IS_REGULAR)); + g_assert_cmpint(g_remove(dolor_file), ==, 0); + + /* Extract all excluding "ba*" */ + g_assert_true(pu_archive_extract("data/foobar.tar", dest, list_ba, NULL, &error)); + g_assert_no_error(error); + g_assert_true(g_file_test(foo_dir, G_FILE_TEST_IS_DIR)); + g_assert_true(g_file_test(foo_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(bar_dir, G_FILE_TEST_IS_DIR)); + g_assert_false(g_file_test(bar_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(baz_dir, G_FILE_TEST_IS_DIR)); + g_assert_false(g_file_test(baz_file, G_FILE_TEST_IS_REGULAR)); + g_assert_cmpint(g_remove(foo_file), ==, 0); + g_assert_cmpint(g_rmdir(foo_dir), ==, 0); + + /* Extract only "ba*" (includes wildcards) */ + g_assert_true(pu_archive_extract("data/foobar.tar", dest, NULL, list_ba, &error)); + g_assert_no_error(error); + g_assert_false(g_file_test(foo_dir, G_FILE_TEST_IS_DIR)); + g_assert_false(g_file_test(foo_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(bar_dir, G_FILE_TEST_IS_DIR)); + g_assert_true(g_file_test(bar_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(baz_dir, G_FILE_TEST_IS_DIR)); + g_assert_true(g_file_test(baz_file, G_FILE_TEST_IS_REGULAR)); + g_assert_cmpint(g_remove(bar_file), ==, 0); + g_assert_cmpint(g_rmdir(bar_dir), ==, 0); + g_assert_cmpint(g_remove(baz_file), ==, 0); + g_assert_cmpint(g_rmdir(baz_dir), ==, 0); + + /* Member not found in archive */ + g_assert_false(pu_archive_extract("data/foobar.tar", dest, NULL, list_notfound, &error)); + g_assert_error(error, G_SPAWN_EXIT_ERROR, 2); - g_assert_cmpint(g_remove(out_file), ==, 0); g_assert_cmpint(g_rmdir(dest), ==, 0); } @@ -205,6 +294,146 @@ test_is_ext234_image(void) g_assert_false(pu_is_ext234_image("data/lorem.txt")); } +static void +test_remove_recursive_intersect(void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GFile) dest_file = NULL; + g_autofree GList *exclude = NULL; + g_autofree GList *only = NULL; + g_autofree gchar *dest = NULL; + g_autofree gchar *all_dir = NULL; + g_autofree gchar *empty_dir = NULL; + g_autofree gchar *nested_dir = NULL; + g_autofree gchar *nested_file = NULL; + g_autofree gchar *foo_file = NULL; + g_autofree gchar *foobar_file = NULL; + g_autofree gchar *foobarbuz1_file = NULL; + g_autofree gchar *foobarbuz2_file = NULL; + g_autofree gchar *yaml_file = NULL; + + dest = g_dir_make_tmp("partup-XXXXXX", &error); + g_assert_no_error(error); + + all_dir = g_build_filename(dest, "*", NULL); + empty_dir = g_build_filename(dest, "empty", NULL); + nested_dir = g_build_filename(dest, "nested", "one", "two", "three", NULL); + nested_file = g_build_filename(dest, "nested", "one", "two", "three", "four.txt", NULL); + foo_file = g_build_filename(dest, "foo", "test.c", NULL); + foobar_file = g_build_filename(dest, "foo", "bar", "settings.cfg", NULL); + foobarbuz1_file = g_build_filename(dest, "foo", "bar", "buz", "buzzer.yaml", NULL); + foobarbuz2_file = g_build_filename(dest, "foo", "bar", "buz", "dozzer.yaml", NULL); + yaml_file = g_build_filename(dest, "foo", "bar", "buz", "*.yaml", NULL); + + /* NULL checks */ + g_assert_true(pu_archive_extract("data/dir-struct.tar", dest, NULL, NULL, &error)); + g_assert_no_error(error); + + g_assert_true(pu_path_remove_exclude_only(dest, NULL, NULL, &error)); + g_assert_true(g_file_test(empty_dir, G_FILE_TEST_IS_DIR)); + g_assert_true(g_file_test(nested_dir, G_FILE_TEST_IS_DIR)); + g_assert_true(g_file_test(nested_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foo_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foobar_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foobarbuz1_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foobarbuz2_file, G_FILE_TEST_IS_REGULAR)); + + /* Exclude all */ + g_assert_true(pu_archive_extract("data/dir-struct.tar", dest, NULL, NULL, &error)); + g_assert_no_error(error); + + g_list_free(exclude); + exclude = NULL; + exclude = g_list_prepend(exclude, all_dir); + g_list_free(only); + only = NULL; + g_assert_true(pu_path_remove_exclude_only(dest, exclude, only, &error)); + g_assert_false(g_file_test(empty_dir, G_FILE_TEST_IS_DIR)); + g_assert_false(g_file_test(nested_dir, G_FILE_TEST_IS_DIR)); + g_assert_false(g_file_test(nested_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(foo_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(foobar_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(foobarbuz1_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(foobarbuz2_file, G_FILE_TEST_IS_REGULAR)); + + /* Preserve all */ + g_assert_true(pu_archive_extract("data/dir-struct.tar", dest, NULL, NULL, &error)); + g_assert_no_error(error); + + g_list_free(exclude); + exclude = NULL; + g_list_free(only); + only = NULL; + only = g_list_prepend(only, all_dir); + g_assert_true(pu_path_remove_exclude_only(dest, exclude, only, &error)); + g_assert_true(g_file_test(empty_dir, G_FILE_TEST_IS_DIR)); + g_assert_true(g_file_test(nested_dir, G_FILE_TEST_IS_DIR)); + g_assert_true(g_file_test(nested_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foo_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foobar_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foobarbuz1_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foobarbuz2_file, G_FILE_TEST_IS_REGULAR)); + + /* Preserve only nested file */ + g_assert_true(pu_archive_extract("data/dir-struct.tar", dest, NULL, NULL, &error)); + g_assert_no_error(error); + + g_list_free(exclude); + exclude = NULL; + g_list_free(only); + only = NULL; + only = g_list_prepend(only, foo_file); + only = g_list_prepend(only, foobarbuz1_file); + g_assert_true(pu_path_remove_exclude_only(dest, exclude, only, &error)); + g_assert_false(g_file_test(empty_dir, G_FILE_TEST_IS_DIR)); + g_assert_false(g_file_test(nested_dir, G_FILE_TEST_IS_DIR)); + g_assert_false(g_file_test(nested_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foo_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(foobar_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foobarbuz1_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(foobarbuz2_file, G_FILE_TEST_IS_REGULAR)); + + /* Preserve only *.yaml file */ + g_assert_true(pu_archive_extract("data/dir-struct.tar", dest, NULL, NULL, &error)); + g_assert_no_error(error); + + g_list_free(exclude); + exclude = NULL; + g_list_free(only); + only = NULL; + only = g_list_prepend(only, yaml_file); + g_assert_true(pu_path_remove_exclude_only(dest, exclude, only, &error)); + g_assert_false(g_file_test(empty_dir, G_FILE_TEST_IS_DIR)); + g_assert_false(g_file_test(nested_dir, G_FILE_TEST_IS_DIR)); + g_assert_false(g_file_test(nested_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(foo_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(foobar_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foobarbuz1_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foobarbuz2_file, G_FILE_TEST_IS_REGULAR)); + + /* Exclude *.yaml file */ + g_assert_true(pu_archive_extract("data/dir-struct.tar", dest, NULL, NULL, &error)); + g_assert_no_error(error); + + g_list_free(exclude); + exclude = NULL; + exclude = g_list_prepend(exclude, yaml_file); + g_list_free(only); + only = NULL; + g_assert_true(pu_path_remove_exclude_only(dest, exclude, only, &error)); + g_assert_true(g_file_test(empty_dir, G_FILE_TEST_IS_DIR)); + g_assert_true(g_file_test(nested_dir, G_FILE_TEST_IS_DIR)); + g_assert_true(g_file_test(nested_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foo_file, G_FILE_TEST_IS_REGULAR)); + g_assert_true(g_file_test(foobar_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(foobarbuz1_file, G_FILE_TEST_IS_REGULAR)); + g_assert_false(g_file_test(foobarbuz2_file, G_FILE_TEST_IS_REGULAR)); + + /* Clean up */ + dest_file = g_file_new_for_path(dest); + g_assert_true(pu_file_remove_recursive(dest_file, NULL, &error)); +} + int main(int argc, char *argv[]) @@ -235,6 +464,7 @@ main(int argc, g_test_add_func("/utils/str_pre_remove", test_str_pre_remove); g_test_add_func("/utils/device_get_partition_pattern", test_device_get_partition_pattern); g_test_add_func("/utils/is_ext234_image", test_is_ext234_image); + g_test_add_func("/utils/remove_recursive_intersect", test_remove_recursive_intersect); return g_test_run(); }