From cae933bf661f9c6bbd071edae297c4bbd6c6cb55 Mon Sep 17 00:00:00 2001 From: David Sarkisyan <281478990+srkyn@users.noreply.github.com> Date: Sat, 15 Aug 2026 18:00:46 -0400 Subject: [PATCH 1/2] Preserve POSIX ACLs when replacing archives --- .github/workflows/build.yml | 2 +- CMakeLists.txt | 16 ++++ config.h.in | 5 + lib/CMakeLists.txt | 4 + lib/zip_source_file_stdio_named.c | 26 ++++- regress/CMakeLists.txt | 7 ++ regress/acl-preserve.test | 6 ++ regress/programs/acl_preserve.c | 152 ++++++++++++++++++++++++++++++ 8 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 regress/acl-preserve.test create mode 100644 regress/programs/acl_preserve.c diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c4c69e2f6..4c79dc3c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: - name: install dependencies (Linux) if: ${{ runner.os == 'Linux' }} run: | - sudo apt-get install libzstd-dev + sudo apt-get install libacl1-dev libzstd-dev - name: install latest CMake and Ninja for lukka/run-vcpkg (Windows) if: ${{ runner.os == 'Windows' }} uses: lukka/get-cmake@latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 93e65e19b..774898109 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,22 @@ check_symbol_exists(_stricmp string.h HAVE__STRICMP) check_function_exists(_strtoi64 HAVE__STRTOI64) check_function_exists(_strtoui64 HAVE__STRTOUI64) check_function_exists(_unlink HAVE__UNLINK) +set(_CMAKE_REQUIRED_LIBRARIES_SAVE ${CMAKE_REQUIRED_LIBRARIES}) +find_library(ACL_LIBRARY NAMES acl) +if(ACL_LIBRARY) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${ACL_LIBRARY}) +endif() +check_function_exists(acl_free HAVE_ACL_FREE) +check_function_exists(acl_from_text HAVE_ACL_FROM_TEXT) +check_function_exists(acl_get_file HAVE_ACL_GET_FILE) +check_function_exists(acl_set_file HAVE_ACL_SET_FILE) +check_function_exists(acl_to_text HAVE_ACL_TO_TEXT) +set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_SAVE}) +if(HAVE_ACL_FREE AND HAVE_ACL_GET_FILE AND HAVE_ACL_SET_FILE) + set(ACL_LIBRARIES ${ACL_LIBRARY} CACHE INTERNAL "") +else() + set(ACL_LIBRARIES "" CACHE INTERNAL "") +endif() check_function_exists(arc4random HAVE_ARC4RANDOM) check_function_exists(clonefile HAVE_CLONEFILE) check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO) diff --git a/config.h.in b/config.h.in index 03cd4590f..f38cb2597 100644 --- a/config.h.in +++ b/config.h.in @@ -23,6 +23,11 @@ #cmakedefine HAVE__STRTOI64 #cmakedefine HAVE__STRTOUI64 #cmakedefine HAVE__UNLINK +#cmakedefine HAVE_ACL_FREE +#cmakedefine HAVE_ACL_FROM_TEXT +#cmakedefine HAVE_ACL_GET_FILE +#cmakedefine HAVE_ACL_SET_FILE +#cmakedefine HAVE_ACL_TO_TEXT #cmakedefine HAVE_ARC4RANDOM #cmakedefine HAVE_CLONEFILE #cmakedefine HAVE_COMMONCRYPTO diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 021b91762..2ab8b6919 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,6 +10,10 @@ set(ZIP_INCLUDE_DIRS set(ZIP_LIBRARIES ZLIB::ZLIB) +if(ACL_LIBRARIES) + set(ZIP_LIBRARIES ${ZIP_LIBRARIES} ${ACL_LIBRARIES}) +endif() + set(ZIP_SOURCES siphash.c zip_add.c diff --git a/lib/zip_source_file_stdio_named.c b/lib/zip_source_file_stdio_named.c index c1f8b4e0b..4e6cb43eb 100644 --- a/lib/zip_source_file_stdio_named.c +++ b/lib/zip_source_file_stdio_named.c @@ -39,6 +39,11 @@ #include #include #include +#if defined(HAVE_ACL_FREE) && defined(HAVE_ACL_GET_FILE) && defined(HAVE_ACL_SET_FILE) +#define USE_ACL +#include +#include +#endif #ifdef HAVE_UNISTD_H #include #endif @@ -386,12 +391,31 @@ static FILE *_zip_fopen_close_on_exec(const char *name, bool writeable) { static bool copy_permissions(zip_source_file_context_t *ctx) { zip_os_stat_t st; +#ifdef USE_ACL + acl_t acl; +#endif if (zip_os_stat(ctx->fname, &st) < 0) { zip_error_set(&ctx->error, ZIP_ER_RENAME, errno); return false; } - /* TODO: copy ACLs */ +#ifdef USE_ACL + if ((acl = acl_get_file(ctx->fname, ACL_TYPE_ACCESS)) == NULL) { + if (errno != ENOTSUP && errno != EOPNOTSUPP) { + zip_error_set(&ctx->error, ZIP_ER_RENAME, errno); + return false; + } + } + else { + if (acl_set_file(ctx->tmpname, ACL_TYPE_ACCESS, acl) < 0) { + int saved_errno = errno; + (void)acl_free(acl); + zip_error_set(&ctx->error, ZIP_ER_RENAME, saved_errno); + return false; + } + (void)acl_free(acl); + } +#endif if (chmod(ctx->tmpname, st.st_mode) < 0) { zip_error_set(&ctx->error, ZIP_ER_RENAME, errno); return false; diff --git a/regress/CMakeLists.txt b/regress/CMakeLists.txt index 4d667b2b0..2ce7d4fc2 100644 --- a/regress/CMakeLists.txt +++ b/regress/CMakeLists.txt @@ -9,6 +9,10 @@ set(TEST_PROGRAMS source_seek ) +if(HAVE_ACL_FREE AND HAVE_ACL_FROM_TEXT AND HAVE_ACL_GET_FILE AND HAVE_ACL_SET_FILE AND HAVE_ACL_TO_TEXT) + list(APPEND TEST_PROGRAMS acl_preserve) +endif() + set(GETOPT_USERS fread tryopen @@ -29,6 +33,9 @@ foreach(PROGRAM IN LISTS ZIP_PROGRAMS) else() add_executable(${PROGRAM} programs/${PROGRAM}.c) target_link_libraries(${PROGRAM} zip) + if(PROGRAM STREQUAL "acl_preserve" AND ACL_LIBRARIES) + target_link_libraries(${PROGRAM} ${ACL_LIBRARIES}) + endif() if (ENABLE_COVERAGE) target_compile_options(${PROGRAM} PRIVATE -coverage) target_link_options(${PROGRAM} PRIVATE -coverage) diff --git a/regress/acl-preserve.test b/regress/acl-preserve.test new file mode 100644 index 000000000..c1fb4fa80 --- /dev/null +++ b/regress/acl-preserve.test @@ -0,0 +1,6 @@ +description preserve POSIX access ACL when replacing archive +program acl_preserve +return 0 +stdout +POSIX access ACL preserved +end-of-inline-data diff --git a/regress/programs/acl_preserve.c b/regress/programs/acl_preserve.c new file mode 100644 index 000000000..b4103b3c2 --- /dev/null +++ b/regress/programs/acl_preserve.c @@ -0,0 +1,152 @@ +/* + acl_preserve.c -- verify that replacing an archive preserves its POSIX access ACL + SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "zip.h" + +static char *get_acl_text(const char *path) { + acl_t acl; + char *text; + + if ((acl = acl_get_file(path, ACL_TYPE_ACCESS)) == NULL) { + return NULL; + } + text = acl_to_text(acl, NULL); + (void)acl_free(acl); + return text; +} + +int main(void) { + static const char archive_name[] = "acl-preserve.zip"; + static const char contents[] = "data"; + static const char acl_text[] = "u::rw-,u:65534:rw-,g::---,m::rw-,o::---"; + char *before = NULL; + char *after = NULL; + acl_t acl = NULL; + zip_source_t *source = NULL; + zip_t *archive = NULL; + zip_error_t error; + int error_code; + int result = 1; + + (void)remove(archive_name); + + archive = zip_open(archive_name, ZIP_CREATE | ZIP_TRUNCATE, &error_code); + if (archive == NULL) { + fprintf(stderr, "cannot create archive: %d\n", error_code); + goto done; + } + source = zip_source_buffer(archive, contents, sizeof(contents) - 1, 0); + if (source == NULL || zip_file_add(archive, "data", source, 0) < 0) { + fprintf(stderr, "cannot add archive entry\n"); + if (source != NULL) { + zip_source_free(source); + source = NULL; + } + zip_discard(archive); + archive = NULL; + goto done; + } + source = NULL; /* owned by archive */ + if (zip_close(archive) < 0) { + fprintf(stderr, "cannot finish initial archive\n"); + zip_discard(archive); + archive = NULL; + goto done; + } + archive = NULL; + + if ((acl = acl_from_text(acl_text)) == NULL) { + perror("cannot create test ACL"); + goto done; + } + if (acl_set_file(archive_name, ACL_TYPE_ACCESS, acl) < 0) { + if (errno == ENOTSUP || errno == EOPNOTSUPP || errno == EPERM || errno == EINVAL) { + result = 77; + goto done; + } + perror("cannot set test ACL"); + goto done; + } + (void)acl_free(acl); + acl = NULL; + if ((before = get_acl_text(archive_name)) == NULL) { + perror("cannot read ACL before replacement"); + goto done; + } + + archive = zip_open(archive_name, 0, &error_code); + if (archive == NULL) { + fprintf(stderr, "cannot reopen archive: %d\n", error_code); + goto done; + } + if (zip_set_archive_comment(archive, "changed", 7) < 0 || zip_close(archive) < 0) { + fprintf(stderr, "cannot update archive\n"); + zip_discard(archive); + archive = NULL; + goto done; + } + archive = NULL; + + if ((after = get_acl_text(archive_name)) == NULL || strcmp(before, after) != 0) { + fprintf(stderr, "POSIX access ACL changed during archive replacement\n"); + goto done; + } + (void)acl_free(after); + after = NULL; + + zip_error_init(&error); + source = zip_source_file_create(archive_name, 0, -1, &error); + if (source == NULL) { + fprintf(stderr, "cannot create named file source: %s\n", zip_error_strerror(&error)); + zip_error_fini(&error); + goto done; + } + if (zip_source_begin_write(source) < 0 || zip_source_write(source, contents, sizeof(contents) - 1) != sizeof(contents) - 1 || zip_source_commit_write(source) < 0) { + fprintf(stderr, "cannot replace named file source: %s\n", zip_error_strerror(zip_source_error(source))); + zip_source_free(source); + source = NULL; + zip_error_fini(&error); + goto done; + } + zip_source_free(source); + source = NULL; + zip_error_fini(&error); + + if ((after = get_acl_text(archive_name)) == NULL || strcmp(before, after) != 0) { + fprintf(stderr, "POSIX access ACL changed during direct source replacement\n"); + goto done; + } + + puts("POSIX access ACL preserved"); + result = 0; + +done: + if (archive != NULL) { + zip_discard(archive); + } + if (source != NULL) { + zip_source_free(source); + } + if (acl != NULL) { + (void)acl_free(acl); + } + if (before != NULL) { + (void)acl_free(before); + } + if (after != NULL) { + (void)acl_free(after); + } + (void)remove(archive_name); + return result; +} From e70333b87333993ee40f8a03951e27935d03f911 Mon Sep 17 00:00:00 2001 From: David Sarkisyan <281478990+srkyn@users.noreply.github.com> Date: Wed, 19 Aug 2026 12:00:32 -0400 Subject: [PATCH 2/2] Avoid libacl dependency for ACL preservation --- .github/workflows/build.yml | 2 +- CMakeLists.txt | 18 +----- config.h.in | 7 +-- lib/CMakeLists.txt | 4 -- lib/zip_source_file_stdio_named.c | 36 ++++++++---- regress/CMakeLists.txt | 5 +- regress/programs/acl_preserve.c | 95 ++++++++++++++++++------------- 7 files changed, 86 insertions(+), 81 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c79dc3c5..c4c69e2f6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: - name: install dependencies (Linux) if: ${{ runner.os == 'Linux' }} run: | - sudo apt-get install libacl1-dev libzstd-dev + sudo apt-get install libzstd-dev - name: install latest CMake and Ninja for lukka/run-vcpkg (Windows) if: ${{ runner.os == 'Windows' }} uses: lukka/get-cmake@latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 774898109..b1b18b06d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,22 +114,6 @@ check_symbol_exists(_stricmp string.h HAVE__STRICMP) check_function_exists(_strtoi64 HAVE__STRTOI64) check_function_exists(_strtoui64 HAVE__STRTOUI64) check_function_exists(_unlink HAVE__UNLINK) -set(_CMAKE_REQUIRED_LIBRARIES_SAVE ${CMAKE_REQUIRED_LIBRARIES}) -find_library(ACL_LIBRARY NAMES acl) -if(ACL_LIBRARY) - list(APPEND CMAKE_REQUIRED_LIBRARIES ${ACL_LIBRARY}) -endif() -check_function_exists(acl_free HAVE_ACL_FREE) -check_function_exists(acl_from_text HAVE_ACL_FROM_TEXT) -check_function_exists(acl_get_file HAVE_ACL_GET_FILE) -check_function_exists(acl_set_file HAVE_ACL_SET_FILE) -check_function_exists(acl_to_text HAVE_ACL_TO_TEXT) -set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_SAVE}) -if(HAVE_ACL_FREE AND HAVE_ACL_GET_FILE AND HAVE_ACL_SET_FILE) - set(ACL_LIBRARIES ${ACL_LIBRARY} CACHE INTERNAL "") -else() - set(ACL_LIBRARIES "" CACHE INTERNAL "") -endif() check_function_exists(arc4random HAVE_ARC4RANDOM) check_function_exists(clonefile HAVE_CLONEFILE) check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO) @@ -139,12 +123,14 @@ check_function_exists(fileno HAVE_FILENO) check_function_exists(fseeko HAVE_FSEEKO) check_function_exists(ftello HAVE_FTELLO) check_function_exists(getprogname HAVE_GETPROGNAME) +check_function_exists(getxattr HAVE_GETXATTR) check_function_exists(GetSecurityInfo HAVE_GETSECURITYINFO) check_symbol_exists(localtime_r time.h HAVE_LOCALTIME_R) check_symbol_exists(localtime_s time.h HAVE_LOCALTIME_S) check_function_exists(memcpy_s HAVE_MEMCPY_S) check_function_exists(random HAVE_RANDOM) check_function_exists(setmode HAVE_SETMODE) +check_function_exists(setxattr HAVE_SETXATTR) check_symbol_exists(snprintf stdio.h HAVE_SNPRINTF) check_symbol_exists(snprintf_s stdio.h HAVE_SNPRINTF_S) check_symbol_exists(strcasecmp strings.h HAVE_STRCASECMP) diff --git a/config.h.in b/config.h.in index f38cb2597..88c05331a 100644 --- a/config.h.in +++ b/config.h.in @@ -23,11 +23,6 @@ #cmakedefine HAVE__STRTOI64 #cmakedefine HAVE__STRTOUI64 #cmakedefine HAVE__UNLINK -#cmakedefine HAVE_ACL_FREE -#cmakedefine HAVE_ACL_FROM_TEXT -#cmakedefine HAVE_ACL_GET_FILE -#cmakedefine HAVE_ACL_SET_FILE -#cmakedefine HAVE_ACL_TO_TEXT #cmakedefine HAVE_ARC4RANDOM #cmakedefine HAVE_CLONEFILE #cmakedefine HAVE_COMMONCRYPTO @@ -38,6 +33,7 @@ #cmakedefine HAVE_FSEEKO #cmakedefine HAVE_FTELLO #cmakedefine HAVE_GETPROGNAME +#cmakedefine HAVE_GETXATTR #cmakedefine HAVE_GETSECURITYINFO #cmakedefine HAVE_GNUTLS #cmakedefine HAVE_LIBBZ2 @@ -49,6 +45,7 @@ #cmakedefine HAVE_MKSTEMP #cmakedefine HAVE_OPENSSL #cmakedefine HAVE_SETMODE +#cmakedefine HAVE_SETXATTR #cmakedefine HAVE_SNPRINTF #cmakedefine HAVE_SNPRINTF_S #cmakedefine HAVE_STRCASECMP diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 2ab8b6919..021b91762 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,10 +10,6 @@ set(ZIP_INCLUDE_DIRS set(ZIP_LIBRARIES ZLIB::ZLIB) -if(ACL_LIBRARIES) - set(ZIP_LIBRARIES ${ZIP_LIBRARIES} ${ACL_LIBRARIES}) -endif() - set(ZIP_SOURCES siphash.c zip_add.c diff --git a/lib/zip_source_file_stdio_named.c b/lib/zip_source_file_stdio_named.c index 4e6cb43eb..1283d0939 100644 --- a/lib/zip_source_file_stdio_named.c +++ b/lib/zip_source_file_stdio_named.c @@ -39,10 +39,9 @@ #include #include #include -#if defined(HAVE_ACL_FREE) && defined(HAVE_ACL_GET_FILE) && defined(HAVE_ACL_SET_FILE) +#if defined(__linux__) && defined(HAVE_GETXATTR) && defined(HAVE_SETXATTR) #define USE_ACL -#include -#include +#include #endif #ifdef HAVE_UNISTD_H #include @@ -391,29 +390,42 @@ static FILE *_zip_fopen_close_on_exec(const char *name, bool writeable) { static bool copy_permissions(zip_source_file_context_t *ctx) { zip_os_stat_t st; -#ifdef USE_ACL - acl_t acl; -#endif if (zip_os_stat(ctx->fname, &st) < 0) { zip_error_set(&ctx->error, ZIP_ER_RENAME, errno); return false; } #ifdef USE_ACL - if ((acl = acl_get_file(ctx->fname, ACL_TYPE_ACCESS)) == NULL) { - if (errno != ENOTSUP && errno != EOPNOTSUPP) { + static const char acl_name[] = "system.posix_acl_access"; + void *acl_data; + ssize_t acl_size; + + acl_size = getxattr(ctx->fname, acl_name, NULL, 0); + if (acl_size < 0) { + if (errno != ENODATA && errno != ENOTSUP && errno != EOPNOTSUPP) { zip_error_set(&ctx->error, ZIP_ER_RENAME, errno); return false; } } - else { - if (acl_set_file(ctx->tmpname, ACL_TYPE_ACCESS, acl) < 0) { + else if (acl_size > 0) { + if ((acl_data = malloc((size_t)acl_size)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return false; + } + acl_size = getxattr(ctx->fname, acl_name, acl_data, (size_t)acl_size); + if (acl_size < 0) { + int saved_errno = errno; + free(acl_data); + zip_error_set(&ctx->error, ZIP_ER_RENAME, saved_errno); + return false; + } + if (setxattr(ctx->tmpname, acl_name, acl_data, (size_t)acl_size, 0) < 0) { int saved_errno = errno; - (void)acl_free(acl); + free(acl_data); zip_error_set(&ctx->error, ZIP_ER_RENAME, saved_errno); return false; } - (void)acl_free(acl); + free(acl_data); } #endif if (chmod(ctx->tmpname, st.st_mode) < 0) { diff --git a/regress/CMakeLists.txt b/regress/CMakeLists.txt index 2ce7d4fc2..ba9e36360 100644 --- a/regress/CMakeLists.txt +++ b/regress/CMakeLists.txt @@ -9,7 +9,7 @@ set(TEST_PROGRAMS source_seek ) -if(HAVE_ACL_FREE AND HAVE_ACL_FROM_TEXT AND HAVE_ACL_GET_FILE AND HAVE_ACL_SET_FILE AND HAVE_ACL_TO_TEXT) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND HAVE_GETXATTR AND HAVE_SETXATTR) list(APPEND TEST_PROGRAMS acl_preserve) endif() @@ -33,9 +33,6 @@ foreach(PROGRAM IN LISTS ZIP_PROGRAMS) else() add_executable(${PROGRAM} programs/${PROGRAM}.c) target_link_libraries(${PROGRAM} zip) - if(PROGRAM STREQUAL "acl_preserve" AND ACL_LIBRARIES) - target_link_libraries(${PROGRAM} ${ACL_LIBRARIES}) - endif() if (ENABLE_COVERAGE) target_compile_options(${PROGRAM} PRIVATE -coverage) target_link_options(${PROGRAM} PRIVATE -coverage) diff --git a/regress/programs/acl_preserve.c b/regress/programs/acl_preserve.c index b4103b3c2..4b133775d 100644 --- a/regress/programs/acl_preserve.c +++ b/regress/programs/acl_preserve.c @@ -5,37 +5,61 @@ #include "config.h" +#include #include +#include #include #include #include -#include -#include +#include #include "zip.h" -static char *get_acl_text(const char *path) { - acl_t acl; - char *text; +#define ACL_EA_ACCESS "system.posix_acl_access" +#define ACL_EA_VERSION 0x0002 +#define ACL_UNDEFINED_ID UINT32_MAX - if ((acl = acl_get_file(path, ACL_TYPE_ACCESS)) == NULL) { - return NULL; - } - text = acl_to_text(acl, NULL); - (void)acl_free(acl); - return text; +#define ACL_USER_OBJ 0x01 +#define ACL_USER 0x02 +#define ACL_GROUP_OBJ 0x04 +#define ACL_MASK 0x10 +#define ACL_OTHER 0x20 + +#define ACL_READ 0x04 +#define ACL_WRITE 0x02 + +struct acl_header { + uint32_t version; +}; + +struct acl_entry { + uint16_t tag; + uint16_t permissions; + uint32_t id; +}; + +struct test_acl { + struct acl_header header; + struct acl_entry entries[5]; +}; + +static void set_entry(struct acl_entry *entry, uint16_t tag, uint16_t permissions, uint32_t id) { + entry->tag = htole16(tag); + entry->permissions = htole16(permissions); + entry->id = htole32(id); } int main(void) { static const char archive_name[] = "acl-preserve.zip"; static const char contents[] = "data"; - static const char acl_text[] = "u::rw-,u:65534:rw-,g::---,m::rw-,o::---"; - char *before = NULL; - char *after = NULL; - acl_t acl = NULL; + unsigned char before[sizeof(struct test_acl)]; + unsigned char after[sizeof(struct test_acl)]; + struct test_acl acl; zip_source_t *source = NULL; zip_t *archive = NULL; zip_error_t error; + ssize_t before_length; + ssize_t after_length; int error_code; int result = 1; @@ -66,22 +90,24 @@ int main(void) { } archive = NULL; - if ((acl = acl_from_text(acl_text)) == NULL) { - perror("cannot create test ACL"); - goto done; - } - if (acl_set_file(archive_name, ACL_TYPE_ACCESS, acl) < 0) { - if (errno == ENOTSUP || errno == EOPNOTSUPP || errno == EPERM || errno == EINVAL) { + acl.header.version = htole32(ACL_EA_VERSION); + set_entry(&acl.entries[0], ACL_USER_OBJ, ACL_READ | ACL_WRITE, ACL_UNDEFINED_ID); + set_entry(&acl.entries[1], ACL_USER, ACL_READ | ACL_WRITE, 65534); + set_entry(&acl.entries[2], ACL_GROUP_OBJ, 0, ACL_UNDEFINED_ID); + set_entry(&acl.entries[3], ACL_MASK, ACL_READ | ACL_WRITE, ACL_UNDEFINED_ID); + set_entry(&acl.entries[4], ACL_OTHER, 0, ACL_UNDEFINED_ID); + + if (setxattr(archive_name, ACL_EA_ACCESS, &acl, sizeof(acl), 0) < 0) { + if (errno == ENOTSUP || errno == EOPNOTSUPP || errno == EPERM) { result = 77; goto done; } - perror("cannot set test ACL"); + perror("setxattr"); goto done; } - (void)acl_free(acl); - acl = NULL; - if ((before = get_acl_text(archive_name)) == NULL) { - perror("cannot read ACL before replacement"); + before_length = getxattr(archive_name, ACL_EA_ACCESS, before, sizeof(before)); + if (before_length < 0) { + perror("getxattr before"); goto done; } @@ -98,12 +124,11 @@ int main(void) { } archive = NULL; - if ((after = get_acl_text(archive_name)) == NULL || strcmp(before, after) != 0) { + after_length = getxattr(archive_name, ACL_EA_ACCESS, after, sizeof(after)); + if (after_length != before_length || memcmp(before, after, (size_t)before_length) != 0) { fprintf(stderr, "POSIX access ACL changed during archive replacement\n"); goto done; } - (void)acl_free(after); - after = NULL; zip_error_init(&error); source = zip_source_file_create(archive_name, 0, -1, &error); @@ -123,7 +148,8 @@ int main(void) { source = NULL; zip_error_fini(&error); - if ((after = get_acl_text(archive_name)) == NULL || strcmp(before, after) != 0) { + after_length = getxattr(archive_name, ACL_EA_ACCESS, after, sizeof(after)); + if (after_length != before_length || memcmp(before, after, (size_t)before_length) != 0) { fprintf(stderr, "POSIX access ACL changed during direct source replacement\n"); goto done; } @@ -138,15 +164,6 @@ int main(void) { if (source != NULL) { zip_source_free(source); } - if (acl != NULL) { - (void)acl_free(acl); - } - if (before != NULL) { - (void)acl_free(before); - } - if (after != NULL) { - (void)acl_free(after); - } (void)remove(archive_name); return result; }