As context, Linux 3.8 now supports user namespaces, which means we can have unprivileged users running as root in the system, with capabilities valid only in the namespace they operate on. This works by establishing a 1:1 mapping between a namespace user and a host user, where (for instance) uid 0 maps to 10000, 1 to 10001, and so on.
With that, it is extremely useful when unpacking tar archives, to be able to add that offset to the end result. Specifying a user won't help, since the tar archive can have many, and keeping permissions will require a post processing step where all ownership is adjusted. * src/common.h: global variables for gid and uid offset. * src/extract.c (set_stat): adjust stat information. * src/tar.c: new option processing. * src/list.c (uid_from_header, gid_from_header): use numeric offset * doc/tar.texi: Update. --- doc/tar.texi | 11 +++++++++++ src/common.h | 5 +++++ src/extract.c | 2 +- src/list.c | 4 ++-- src/tar.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 3 deletions(-) diff --git a/doc/tar.texi b/doc/tar.texi index 480fe89..ae5f1b0 100644 --- a/doc/tar.texi +++ b/doc/tar.texi @@ -3028,6 +3028,17 @@ This option will notify @command{tar} that it should use numeric user and group IDs when creating a @command{tar} file, rather than names. @xref{Attributes}. +@opsummary{owner-offset} +@item --owner-offset=@var{uid_offset:gid_offset} + +This option will notify @command{tar} that it should add a fixed offset +to user and group IDs when extracting a @command{tar} file. It implies +that all user and group IDs are numeric. It takes as a parameter in the +form @var{uid_offset:gid_offset} denoting the desiring offsets. + +This option is useful for example when unpacking files intended to be +used within a Linux namespace. + @item -o The function of this option depends on the action @command{tar} is performing. When extracting files, @option{-o} is a synonym for diff --git a/src/common.h b/src/common.h index 4f7c19f..4def7d0 100644 --- a/src/common.h +++ b/src/common.h @@ -236,6 +236,11 @@ GLOBAL int recursion_option; GLOBAL bool numeric_owner_option; +GLOBAL int numeric_owner_uid_offset; +GLOBAL int numeric_owner_gid_offset; + +#define HAS_OWNER_OFFSET (numeric_owner_uid_offset || numeric_owner_gid_offset) + GLOBAL bool one_file_system_option; /* Specified value to be put into tar file in place of stat () results, or diff --git a/src/extract.c b/src/extract.c index 327b67f..4de6797 100644 --- a/src/extract.c +++ b/src/extract.c @@ -360,7 +360,7 @@ set_stat (char const *file_name, utime_error (file_name); } - if (0 < same_owner_option && ! interdir) + if ((0 < same_owner_option && ! interdir) || HAS_OWNER_OFFSET) { /* Some systems allow non-root users to give files away. Once this done, it is not possible anymore to change file permissions. diff --git a/src/list.c b/src/list.c index 858aa73..5b8eb72 100644 --- a/src/list.c +++ b/src/list.c @@ -915,7 +915,7 @@ gid_from_header (const char *p, size_t s) { return from_header (p, s, "gid_t", TYPE_MINIMUM (gid_t), TYPE_MAXIMUM (gid_t), - false, false); + false, false) + numeric_owner_gid_offset; } static major_t @@ -981,7 +981,7 @@ uid_from_header (const char *p, size_t s) { return from_header (p, s, "uid_t", TYPE_MINIMUM (uid_t), TYPE_MAXIMUM (uid_t), - false, false); + false, false) + numeric_owner_uid_offset; } uintmax_t diff --git a/src/tar.c b/src/tar.c index c29b4fa..cede1ad 100644 --- a/src/tar.c +++ b/src/tar.c @@ -315,6 +315,7 @@ enum NO_XATTR_OPTION, NULL_OPTION, NUMERIC_OWNER_OPTION, + NUMERIC_OWNER_OFFSET_OPTION, OCCURRENCE_OPTION, OLD_ARCHIVE_OPTION, ONE_FILE_SYSTEM_OPTION, @@ -525,6 +526,8 @@ static struct argp_option options[] = { N_("extract files as yourself (default for ordinary users)"), GRID+1 }, {"numeric-owner", NUMERIC_OWNER_OPTION, 0, 0, N_("always use numbers for user/group names"), GRID+1 }, + {"owner-offset", NUMERIC_OWNER_OFFSET_OPTION, N_("UID_OFFSET:GID_OFFSET"), 0, + N_("add numeric offset for file owner"), GRID+1 }, {"preserve-permissions", 'p', 0, 0, N_("extract information about file permissions (default for superuser)"), GRID+1 }, @@ -2000,6 +2003,43 @@ parse_opt (int key, char *arg, struct argp_state *state) numeric_owner_option = true; break; + case NUMERIC_OWNER_OFFSET_OPTION: + { + char const *num = NULL; + char *colon = strchr (arg, ':'); + + if (colon) + { + *colon = '\0'; + num = colon + 1; + } + + if (colon != arg) + { + uintmax_t uid; + + if (! (xstrtoumax (arg, 0, 10, &uid, "") == LONGINT_OK + && uid == (size_t) uid)) + { + USAGE_ERROR ((0, 0, "%s", + _("--owner-offset requires a numeric offset argument"))); + } + numeric_owner_uid_offset = uid; + } + + if (num) + { + uintmax_t gid; + + if (! (xstrtoumax (num, 0, 10, &gid, "") == LONGINT_OK + && gid == (size_t) gid)) + USAGE_ERROR ((0, 0, "%s", + _("--owner-offset received an invalid gid"))); + numeric_owner_gid_offset = gid; + } + + break; + } case OCCURRENCE_OPTION: if (!arg) occurrence_option = 1; @@ -2738,6 +2778,10 @@ main (int argc, char **argv) filename_terminator = '\n'; set_quoting_style (0, DEFAULT_QUOTING_STYLE); + /* in case not set */ + numeric_owner_uid_offset = 0; + numeric_owner_gid_offset = 0; + /* Make sure we have first three descriptors available */ stdopen (); -- 1.8.1.2