diff --git a/examples/list_xattrs.c b/examples/list_xattrs.c new file mode 100644 index 000000000..600c468fd --- /dev/null +++ b/examples/list_xattrs.c @@ -0,0 +1,34 @@ +/** + * @author Enno Boland (mail@eboland.de) + * @file list_xattrs.c + * + * This is an example program that prints all extended attributes of a file in + * a squashfs archive. + */ + +#include +#include +#include +#include + +int +main(int argc, char *argv[]) { + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + return 1; + } + struct SqshArchive *archive = sqsh_archive_open(argv[1], NULL, NULL); + assert(archive != NULL); + + char **keys = sqsh_easy_xattr_keys(archive, argv[2], NULL); + assert(keys != NULL); + for (int i = 0; keys[i] != NULL; i++) { + char *value = sqsh_easy_xattr_get(archive, argv[2], keys[i], NULL); + printf("%s=%s\n", keys[i], value); + free(value); + } + free(keys); + + sqsh_archive_close(archive); + return 0; +} diff --git a/examples/list_xattrs_ll.c b/examples/list_xattrs_ll.c new file mode 100644 index 000000000..2394d5f7b --- /dev/null +++ b/examples/list_xattrs_ll.c @@ -0,0 +1,70 @@ +/** + * @author Enno Boland (mail@eboland.de) + * @file list_xattrs_ll.c + * + * This is an example program that prints all extended attributes of a file in + * a squashfs archive. It uses low level variants of the API. + */ + +#include +#include +#include + +int +main(int argc, char *argv[]) { + int error_code = 0; + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + return 1; + } + struct SqshConfig config = { + // Read the header file to find documentation on these fields. + // It's safe to set them all to 0. + .source_mapper = sqsh_mapper_impl_mmap, + .source_size = 0, + .mapper_block_size = 0, + .mapper_lru_size = 0, + .compression_lru_size = 0, + .archive_offset = 0, + .max_symlink_depth = 0, + }; + struct SqshArchive *archive = + sqsh_archive_open(argv[1], &config, &error_code); + if (error_code != 0) { + sqsh_perror(error_code, "sqsh_archive_new"); + return 1; + } + struct SqshFile *file = sqsh_open(archive, argv[2], &error_code); + if (error_code != 0) { + sqsh_perror(error_code, "sqsh_open"); + return 1; + } + + struct SqshXattrIterator *iterator = + sqsh_xattr_iterator_new(file, &error_code); + if (error_code != 0) { + sqsh_perror(error_code, "sqsh_xattr_iterator_new"); + return 1; + } + + while (sqsh_xattr_iterator_next(iterator, &error_code)) { + const char *prefix = sqsh_xattr_iterator_prefix(iterator); + const size_t prefix_size = sqsh_xattr_iterator_prefix_size(iterator); + const char *name = sqsh_xattr_iterator_name(iterator); + const size_t name_size = sqsh_xattr_iterator_name_size(iterator); + const char *value = sqsh_xattr_iterator_value(iterator); + const size_t value_size = sqsh_xattr_iterator_value_size(iterator); + + printf("%.*s%.*s=%.*s\n", (int)prefix_size, prefix, (int)name_size, + name, (int)value_size, value); + } + if (error_code < 0) { + sqsh_perror(error_code, "sqsh_xattr_iterator_next"); + return 1; + } + + sqsh_xattr_iterator_free(iterator); + sqsh_close(file); + sqsh_archive_close(archive); + return 0; +} diff --git a/examples/meson.build b/examples/meson.build index e046f4ee3..80666c0d2 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -22,6 +22,18 @@ executable( install: false, dependencies: libsqsh_dep, ) +executable( + 'list_xattrs', + 'list_xattrs.c', + install: false, + dependencies: libsqsh_dep, +) +executable( + 'list_xattrs_ll', + 'list_xattrs_ll.c', + install: false, + dependencies: libsqsh_dep, +) executable( 'readme_example', 'readme_example.c', diff --git a/include/sqsh_easy.h b/include/sqsh_easy.h index 3f70e172d..2d0772f5a 100644 --- a/include/sqsh_easy.h +++ b/include/sqsh_easy.h @@ -48,7 +48,7 @@ struct SqshArchive; struct SqshFile; /*************************************** - * chrome/file.c + * easy/file.c */ /** @@ -115,7 +115,7 @@ time_t sqsh_easy_file_mtime(struct SqshArchive *archive, const char *path, int *err); /*************************************** - * chrome/directory.c + * easy/directory.c */ /** @@ -132,6 +132,40 @@ sqsh_easy_file_mtime(struct SqshArchive *archive, const char *path, int *err); char **sqsh_easy_directory_list( struct SqshArchive *archive, const char *path, int *err); +/*************************************** + * easy/xattr.c + */ + +/** + * @brief retrieves all xattr keys of a file or directory. + * + * The returned list needs to be released with `free()`. + * + * @param[in] archive The sqsh archive context. + * @param[in] path The path the file or directory. + * @param[out] err Pointer to an int where the error code will be stored. + * + * @return A list of xattr keys on success, NULL on error. + */ +char ** +sqsh_easy_xattr_keys(struct SqshArchive *archive, const char *path, int *err); + +/** + * @brief retrieves the value of a xattr key of a file or directory. + * + * The returned pointer needs to be released with `free()`. + * + * @param[in] archive The sqsh archive context. + * @param[in] path The path the file or directory. + * @param[in] key The xattr key. + * @param[out] err Pointer to an int where the error code will be stored. + * + * @return The value of the given xattr key on success, NULL on error. + */ +char *sqsh_easy_xattr_get( + struct SqshArchive *archive, const char *path, const char *key, + int *err); + #ifdef __cplusplus } #endif diff --git a/lib/easy/xattr.c b/lib/easy/xattr.c new file mode 100644 index 000000000..6dd725865 --- /dev/null +++ b/lib/easy/xattr.c @@ -0,0 +1,132 @@ +/****************************************************************************** + * * + * Copyright (c) 2023, Enno Boland * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions are * + * met: * + * * + * * Redistributions of source code must retain the above copyright notice, * + * this list of conditions and the following disclaimer. * + * * Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the following disclaimer in the * + * documentation and/or other materials provided with the distribution. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + * * + ******************************************************************************/ + +/** + * @author Enno Boland (mail@eboland.de) + * @file xattr.c + */ + +#define _DEFAULT_SOURCE + +#include "../../include/sqsh_easy.h" + +#include +#include +#include +#include +#include +#include + +#include "../../include/sqsh_error.h" +#include "../../include/sqsh_tree_private.h" +#include "../../include/sqsh_xattr_private.h" + +char * +sqsh_easy_xattr_get( + struct SqshArchive *archive, const char *path, const char *key, + int *err) { + int rv = 0; + struct SqshFile *file = NULL; + struct SqshXattrIterator iterator = {0}; + char *xattr_value = NULL; + + file = sqsh_open(archive, path, &rv); + if (rv < 0) { + goto out; + } + + rv = sqsh__xattr_iterator_init(&iterator, file); + if (rv < 0) { + goto out; + } + + rv = sqsh_xattr_iterator_lookup(&iterator, key); + if (rv < 0) { + goto out; + } + + xattr_value = sqsh_xattr_iterator_value_dup(&iterator); + +out: + sqsh__xattr_iterator_cleanup(&iterator); + sqsh_close(file); + if (err) { + *err = rv; + } + return xattr_value; +} + +struct XattrEasyIter { + struct SqshXattrIterator iterator; + char *key_buffer; +}; + +static int +xattr_collector_next(void *iterator, const char **value, size_t *size) { + struct XattrEasyIter *i = iterator; + int rv = 0; + free(i->key_buffer); + if (sqsh_xattr_iterator_next(&i->iterator, &rv)) { + i->key_buffer = sqsh_xattr_iterator_fullname_dup(&i->iterator); + + *value = i->key_buffer; + *size = (size_t)sqsh_xattr_iterator_value_size(&i->iterator); + } + return rv; +} + +char ** +sqsh_easy_xattr_keys(struct SqshArchive *archive, const char *path, int *err) { + int rv = 0; + struct SqshFile *file = NULL; + struct XattrEasyIter iterator = {0}; + char **list = NULL; + + file = sqsh_open(archive, path, &rv); + if (rv < 0) { + goto out; + } + + rv = sqsh__xattr_iterator_init(&iterator.iterator, file); + if (rv < 0) { + goto out; + } + + rv = cx_collect(&list, xattr_collector_next, &iterator); + if (rv < 0) { + goto out; + } + +out: + sqsh__xattr_iterator_cleanup(&iterator.iterator); + sqsh_close(file); + if (err) { + *err = rv; + } + return list; +} diff --git a/lib/meson.build b/lib/meson.build index f9b232d65..b16c6c69b 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -16,6 +16,7 @@ libsqsh_sources = files( 'directory/directory_iterator.c', 'easy/directory.c', 'easy/file.c', + 'easy/xattr.c', 'extract/extract_manager.c', 'extract/extract_view.c', 'extract/extractor.c',