>From c2afa4fd0a3e71b7a069af5d14b61db217563f3c Mon Sep 17 00:00:00 2001 From: Pavel Raiskup Date: Tue, 9 Oct 2012 15:38:38 +0200 Subject: [PATCH] Allow to store/extract '=' character in xattr keyword --- src/xheader.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++--- tests/Makefile.am | 1 + tests/testsuite.at | 1 + tests/xattr/xattr05.at | 49 +++++++++++++++++++++ 4 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 tests/xattr/xattr05.at diff --git a/src/xheader.c b/src/xheader.c index e785248..de47388 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -499,6 +499,43 @@ static void xheader_xattr__add (struct xattr_array **xattr_map, (*xattr_map)[pos].xval_len = len; } +/* This is reversal function for xattr_encode_keyword. See comment for + xattr_encode_keyword() for more info. */ +static void xattr_decode_keyword (char *keyword) +{ + char *kpr, *kpl; /* keyword pointer left/right */ + kpr = kpl = keyword; + + for (;;) + { + if (*kpr == '%') + { + if (kpr[1] == '3' && kpr[2] == 'D') + { + *kpl = '='; + kpr += 3; + kpl ++; + continue; + } + else if (kpr[1] == '2' && kpr[2] == '5') + { + *kpl = '%'; + kpr += 3; + kpl ++; + continue; + } + } + + *kpl = *kpr; + + if (*kpr == 0) + break; + + kpr++; + kpl++; + } +} + void xheader_xattr_add(struct tar_stat_info *st, const char *key, const char *val, size_t len) { @@ -807,15 +844,70 @@ xheader_read (struct xheader *xhdr, union block *p, size_t size) while (size > 0); } +/* xattr_encode_keyword() substitutes '=' ~~> '%3D' and '%' ~~> '%25' + in extended attribute keywords. This is needed because the '=' character + has special purpose in extended attribute header - it splits keyword and + value part of header. If there was the '=' occurrence allowed inside + keyword, there would be no unambiguous way how to decode this extended + attribute. + + (http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00017.html) + */ +static char *xattr_encode_keyword(const char *keyword) +{ + static char *encode_buffer = NULL; + static size_t encode_buffer_size = 0; + size_t bp; /* keyword/buffer pointers */ + + if (!encode_buffer) + { + encode_buffer_size = 256; + encode_buffer = xmalloc (encode_buffer_size); + } + else + *encode_buffer = 0; + + for (bp = 0; *keyword != 0; ++bp, ++keyword) + { + char c = *keyword; + + if (bp + 2 /* enough for URL encoding also.. */ >= encode_buffer_size) + { + encode_buffer = x2realloc (encode_buffer, &encode_buffer_size); + } + + if (c == '%') + { + strcpy (encode_buffer + bp, "%25"); + bp += 2; + } + else if (c == '=') + { + strcpy (encode_buffer + bp, "%3D"); + bp += 2; + } + else + encode_buffer[bp] = c; + } + + encode_buffer[bp] = 0; + + return encode_buffer; +} + static void xheader_print_n (struct xheader *xhdr, char const *keyword, char const *value, size_t vsize) { - size_t len = strlen (keyword) + vsize + 3; /* ' ' + '=' + '\n' */ size_t p; size_t n = 0; char nbuf[UINTMAX_STRSIZE_BOUND]; char const *np; + size_t len, klen; + + keyword = xattr_encode_keyword (keyword); + klen = strlen (keyword); + len = klen + vsize + 3; /* ' ' + '=' + '\n' */ do { @@ -827,7 +919,7 @@ xheader_print_n (struct xheader *xhdr, char const *keyword, x_obstack_grow (xhdr, np, n); x_obstack_1grow (xhdr, ' '); - x_obstack_grow (xhdr, keyword, strlen (keyword)); + x_obstack_grow (xhdr, keyword, klen); x_obstack_1grow (xhdr, '='); x_obstack_grow (xhdr, value, vsize); x_obstack_1grow (xhdr, '\n'); @@ -1613,11 +1705,20 @@ static void xattr_decoder (struct tar_stat_info *st, char const *keyword, char const *arg, size_t size) { - char *xstr = NULL; + char *xstr, *xkey; + + /* copy keyword */ + size_t klen_raw = strlen (keyword); + xkey = alloca (klen_raw + 1); + memcpy (xkey, keyword, klen_raw + 1) /* including null-terminating */; + + /* copy value */ + xstr = alloca (size + 1); + memcpy (xstr, arg, size + 1); /* separator included, for GNU tar '\n' */; + + xattr_decode_keyword (xkey); - xstr = xmemdup(arg, size + 1); - xheader_xattr_add(st, keyword + strlen("SCHILY.xattr."), xstr, size); - free(xstr); + xheader_xattr_add (st, xkey + strlen("SCHILY.xattr."), xstr, size); } static void diff --git a/tests/Makefile.am b/tests/Makefile.am index 88942b3..449bbb4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -177,6 +177,7 @@ TESTSUITE_AT = \ xattr/xattr02.at\ xattr/xattr03.at\ xattr/xattr04.at\ + xattr/xattr05.at\ xattr/acls01.at\ xattr/acls02.at\ xattr/selnx01.at\ diff --git a/tests/testsuite.at b/tests/testsuite.at index 63be9f0..d943e1f 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -343,6 +343,7 @@ m4_include([xattr/xattr01.at]) m4_include([xattr/xattr02.at]) m4_include([xattr/xattr03.at]) m4_include([xattr/xattr04.at]) +m4_include([xattr/xattr05.at]) m4_include([xattr/acls01.at]) m4_include([xattr/acls02.at]) diff --git a/tests/xattr/xattr05.at b/tests/xattr/xattr05.at new file mode 100644 index 0000000..27dc469 --- /dev/null +++ b/tests/xattr/xattr05.at @@ -0,0 +1,49 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- +# +# Test suite for GNU tar. +# Copyright (C) 2012 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Test description: Test for archiving/extracting of extended attributes +# having the '=' character in its keyword. +# +# Relevant mailing list thread: +# +# http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00017.html + +AT_SETUP([xattrs: keywords with '=' and '%']) +AT_KEYWORDS([xattrs xattr05]) + +AT_TAR_CHECK([ +AT_XATTRS_PREREQ + +mkdir dir +mkdir output +genfile --file dir/file + +setfattr -n user.=NAME%3D= -v value dir/file +getfattr -d dir/file | grep -v '# ' > before + +# archive whole directory including binary xattrs +tar --xattrs -cf archive.tar -C dir . + +tar --xattrs -xf archive.tar -C output +getfattr -d output/file | grep -v '# ' > after +diff before after +], +[0], +[]) + +AT_CLEANUP -- 1.7.11.4