From 2039275a708e1371b0529954c60638722f9613b0 Mon Sep 17 00:00:00 2001 From: Haelwenn Monnier Date: Thu, 8 Feb 2024 22:10:17 +0100 Subject: [PATCH] tar: Add support for --group and --owner (#2054) Closes: https://github.com/libarchive/libarchive/issues/278 --- Makefile.am | 2 + tar/bsdtar.1 | 18 ++++++++ tar/bsdtar.c | 62 +++++++++++++++++++++++++- tar/bsdtar.h | 2 + tar/cmdline.c | 2 + tar/test/CMakeLists.txt | 2 + tar/test/test_option_group.c | 84 ++++++++++++++++++++++++++++++++++++ tar/test/test_option_owner.c | 84 ++++++++++++++++++++++++++++++++++++ 8 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 tar/test/test_option_group.c create mode 100644 tar/test/test_option_owner.c diff --git a/Makefile.am b/Makefile.am index b5ccae02e..c7fbd1439 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1112,6 +1112,7 @@ bsdtar_test_SOURCES= \ tar/test/test_option_exclude_vcs.c \ tar/test/test_option_fflags.c \ tar/test/test_option_gid_gname.c \ + tar/test/test_option_group.c \ tar/test/test_option_grzip.c \ tar/test/test_option_ignore_zeros.c \ tar/test/test_option_j.c \ @@ -1125,6 +1126,7 @@ bsdtar_test_SOURCES= \ tar/test/test_option_newer_than.c \ tar/test/test_option_nodump.c \ tar/test/test_option_older_than.c \ + tar/test/test_option_owner.c \ tar/test/test_option_passphrase.c \ tar/test/test_option_q.c \ tar/test/test_option_r.c \ diff --git a/tar/bsdtar.1 b/tar/bsdtar.1 index 9aebdfbeb..ae84e00fc 100644 --- a/tar/bsdtar.1 +++ b/tar/bsdtar.1 @@ -272,6 +272,15 @@ will be used instead. On create, this sets the group name that will be stored in the archive; the name will not be verified against the system group database. +.It Fl Fl group Ar name Ns Op : Ns Ar gid +Use the provided group, if +.Ar gid +is not provided, +.Ar name +can be either a group name or numeric id. +See the +.Fl Fl gname +option for details. .It Fl H (c and r modes only) Symbolic links named on the command line will be followed; the @@ -904,6 +913,15 @@ the name is not verified against the system user database. Pipe the input (in x or t mode) or the output (in c mode) through .Pa program instead of using the builtin compression support. +.It Fl Fl owner Ar name Ns Op : Ns Ar uid +Use the provided user, if +.Ar uid +is not provided, +.Ar name +can be either an username or numeric id. +See the +.Fl Fl uname +option for details. .It Fl v , Fl Fl verbose Produce verbose output. In create and extract modes, diff --git a/tar/bsdtar.c b/tar/bsdtar.c index 2834a6063..641151985 100644 --- a/tar/bsdtar.c +++ b/tar/bsdtar.c @@ -154,7 +154,7 @@ main(int argc, char **argv) char compression, compression2; const char *compression_name, *compression2_name; const char *compress_program; - char *tptr; + char *tptr, *uptr; char possible_help_request; char buff[16]; @@ -381,6 +381,36 @@ main(int argc, char **argv) case OPTION_GNAME: /* cpio */ bsdtar->gname = bsdtar->argument; break; + case OPTION_GROUP: /* GNU tar */ + errno = 0; + tptr = NULL; + + uptr = strchr(bsdtar->argument, ':'); + if(uptr != NULL) { + if(uptr[1] == 0) { + lafe_errc(1, 0, "Invalid argument to --group (missing id after :)"); + } + uptr[0] = 0; + uptr++; + t = (int)strtol(uptr, &tptr, 10); + if (errno || t < 0 || *uptr == '\0' || + tptr == NULL || *tptr != '\0') { + lafe_errc(1, 0, "Invalid argument to --group (%s is not a number)", uptr); + } else { + bsdtar->gid = t; + } + bsdtar->gname = bsdtar->argument; + } else { + t = (int)strtol(bsdtar->argument, &tptr, 10); + if (errno || t < 0 || *(bsdtar->argument) == '\0' || + tptr == NULL || *tptr != '\0') { + bsdtar->gname = bsdtar->argument; + } else { + bsdtar->gid = t; + bsdtar->gname = ""; + } + } + break; case OPTION_GRZIP: if (compression != '\0') lafe_errc(1, 0, @@ -626,6 +656,36 @@ main(int argc, char **argv) case OPTION_OPTIONS: bsdtar->option_options = bsdtar->argument; break; + case OPTION_OWNER: /* GNU tar */ + errno = 0; + tptr = NULL; + + uptr = strchr(bsdtar->argument, ':'); + if(uptr != NULL) { + if(uptr[1] == 0) { + lafe_errc(1, 0, "Invalid argument to --owner (missing id after :)"); + } + uptr[0] = 0; + uptr++; + t = (int)strtol(uptr, &tptr, 10); + if (errno || t < 0 || *uptr == '\0' || + tptr == NULL || *tptr != '\0') { + lafe_errc(1, 0, "Invalid argument to --owner (%s is not a number)", uptr); + } else { + bsdtar->uid = t; + } + bsdtar->uname = bsdtar->argument; + } else { + t = (int)strtol(bsdtar->argument, &tptr, 10); + if (errno || t < 0 || *(bsdtar->argument) == '\0' || + tptr == NULL || *tptr != '\0') { + bsdtar->uname = bsdtar->argument; + } else { + bsdtar->uid = t; + bsdtar->uname = ""; + } + } + break; #if 0 /* * The common BSD -P option is not necessary, since diff --git a/tar/bsdtar.h b/tar/bsdtar.h index b4d9157f3..22056c792 100644 --- a/tar/bsdtar.h +++ b/tar/bsdtar.h @@ -143,6 +143,7 @@ enum { OPTION_FORMAT, OPTION_GID, OPTION_GNAME, + OPTION_GROUP, OPTION_GRZIP, OPTION_HELP, OPTION_HFS_COMPRESSION, @@ -177,6 +178,7 @@ enum { OPTION_OLDER_MTIME_THAN, OPTION_ONE_FILE_SYSTEM, OPTION_OPTIONS, + OPTION_OWNER, OPTION_PASSPHRASE, OPTION_POSIX, OPTION_READ_SPARSE, diff --git a/tar/cmdline.c b/tar/cmdline.c index 89e75f7c0..72292e8f2 100644 --- a/tar/cmdline.c +++ b/tar/cmdline.c @@ -93,6 +93,7 @@ static const struct bsdtar_option { { "format", 1, OPTION_FORMAT }, { "gid", 1, OPTION_GID }, { "gname", 1, OPTION_GNAME }, + { "group", 1, OPTION_GROUP }, { "grzip", 0, OPTION_GRZIP }, { "gunzip", 0, 'z' }, { "gzip", 0, 'z' }, @@ -141,6 +142,7 @@ static const struct bsdtar_option { { "older-than", 1, OPTION_OLDER_CTIME_THAN }, { "one-file-system", 0, OPTION_ONE_FILE_SYSTEM }, { "options", 1, OPTION_OPTIONS }, + { "owner", 1, OPTION_OWNER }, { "passphrase", 1, OPTION_PASSPHRASE }, { "posix", 0, OPTION_POSIX }, { "preserve-permissions", 0, 'p' }, diff --git a/tar/test/CMakeLists.txt b/tar/test/CMakeLists.txt index 3b6675393..7808a8d1f 100644 --- a/tar/test/CMakeLists.txt +++ b/tar/test/CMakeLists.txt @@ -43,6 +43,7 @@ IF(ENABLE_TAR AND ENABLE_TEST) test_option_exclude_vcs.c test_option_fflags.c test_option_gid_gname.c + test_option_group.c test_option_grzip.c test_option_ignore_zeros.c test_option_j.c @@ -56,6 +57,7 @@ IF(ENABLE_TAR AND ENABLE_TEST) test_option_newer_than.c test_option_nodump.c test_option_older_than.c + test_option_owner.c test_option_passphrase.c test_option_q.c test_option_r.c diff --git a/tar/test/test_option_group.c b/tar/test/test_option_group.c new file mode 100644 index 000000000..d429ddab9 --- /dev/null +++ b/tar/test/test_option_group.c @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 2003-2010 Tim Kientzle + * Copyright (c) 2024 Haelwenn (lanodan) Monnier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR(S) ``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 AUTHOR(S) 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. + */ +#include "test.h" + +DEFINE_TEST(test_option_group) +{ + char *reference, *data; + size_t s; + + assertUmask(0); + assertMakeFile("file", 0644, "1234567890"); + + /* Create archive with no special options. */ + failure("Error invoking %s c", testprog); + assertEqualInt(0, + systemf("%s cf archive1 --format=ustar file >stdout1.txt 2>stderr1.txt", + testprog)); + assertEmptyFile("stdout1.txt"); + assertEmptyFile("stderr1.txt"); + reference = slurpfile(&s, "archive1"); + + /* Create archive with --group (numeric) */ + failure("Error invoking %s c", testprog); + assertEqualInt(0, + systemf("%s cf archive2 --group=17 --format=ustar file >stdout2.txt 2>stderr2.txt", + testprog)); + assertEmptyFile("stdout2.txt"); + assertEmptyFile("stderr2.txt"); + data = slurpfile(&s, "archive2"); + assertEqualMem(data + 116, "000021 \0", 8); + /* Gname field in ustar header should be empty. */ + assertEqualMem(data + 297, "\0", 1); + free(data); + + /* Again with --group (name) */ + failure("Error invoking %s c", testprog); + assertEqualInt(0, + systemf("%s cf archive3 --group=foofoofoo --format=ustar file >stdout3.txt 2>stderr3.txt", + testprog)); + assertEmptyFile("stdout3.txt"); + assertEmptyFile("stderr3.txt"); + data = slurpfile(&s, "archive3"); + /* Gid should be unchanged from original reference. */ + assertEqualMem(data + 116, reference + 116, 8); + assertEqualMem(data + 297, "foofoofoo\0", 10); + free(data); + + /* Again with --group (name:id) */ + failure("Error invoking %s c", testprog); + assertEqualInt(0, + systemf("%s cf archive4 --group=foofoofoo:17 --format=ustar file >stdout4.txt 2>stderr4.txt", + testprog)); + assertEmptyFile("stdout4.txt"); + assertEmptyFile("stderr4.txt"); + data = slurpfile(&s, "archive4"); + assertEqualMem(data + 116, "000021 \0", 8); + assertEqualMem(data + 297, "foofoofoo\0", 10); + free(data); + + free(reference); +} diff --git a/tar/test/test_option_owner.c b/tar/test/test_option_owner.c new file mode 100644 index 000000000..04c86cc0e --- /dev/null +++ b/tar/test/test_option_owner.c @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 2003-2010 Tim Kientzle + * Copyright (c) 2024 Haelwenn (lanodan) Monnier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR(S) ``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 AUTHOR(S) 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. + */ +#include "test.h" + +DEFINE_TEST(test_option_owner) +{ + char *reference, *data; + size_t s; + + assertUmask(0); + assertMakeFile("file", 0644, "1234567890"); + + /* Create archive with no special options. */ + failure("Error invoking %s c", testprog); + assertEqualInt(0, + systemf("%s cf archive1 --format=ustar file >stdout1.txt 2>stderr1.txt", + testprog)); + assertEmptyFile("stdout1.txt"); + assertEmptyFile("stderr1.txt"); + reference = slurpfile(&s, "archive1"); + + /* Create archive with --owner (numeric) */ + failure("Error invoking %s c", testprog); + assertEqualInt(0, + systemf("%s cf archive2 --owner=65123 --format=ustar file >stdout2.txt 2>stderr2.txt", + testprog)); + assertEmptyFile("stdout2.txt"); + assertEmptyFile("stderr2.txt"); + data = slurpfile(&s, "archive2"); + assertEqualMem(data + 108, "177143 \0", 8); + /* Uname field in ustar header should be empty. */ + assertEqualMem(data + 265, "\0", 1); + free(data); + + /* Again with just --owner (name) */ + failure("Error invoking %s c", testprog); + assertEqualInt(0, + systemf("%s cf archive3 --owner=foofoofoo --format=ustar file >stdout3.txt 2>stderr3.txt", + testprog)); + assertEmptyFile("stdout3.txt"); + assertEmptyFile("stderr3.txt"); + data = slurpfile(&s, "archive3"); + /* Uid should be unchanged from original reference. */ + assertEqualMem(data + 108, reference + 108, 8); + assertEqualMem(data + 265, "foofoofoo\0", 10); + free(data); + + /* Again with just --owner (name:id) */ + failure("Error invoking %s c", testprog); + assertEqualInt(0, + systemf("%s cf archive4 --owner=foofoofoo:65123 --format=ustar file >stdout4.txt 2>stderr4.txt", + testprog)); + assertEmptyFile("stdout4.txt"); + assertEmptyFile("stderr4.txt"); + data = slurpfile(&s, "archive4"); + assertEqualMem(data + 108, "177143 \0", 8); + assertEqualMem(data + 265, "foofoofoo\0", 10); + free(data); + + free(reference); +} -- 2.47.2