Skip to content

Commit

Permalink
Preserve literal value of quoted paths in %files
Browse files Browse the repository at this point in the history
Turn off the special meaning of metacharacters within a filename
enclosed in quotes, just like a typical shell does it.

Do it by automatically escaping such chars at parse time, as that
internally reduces the case to the one where the spec author does the
escaping manually (i.e. fewer code paths to worry about).  This will
also allow us to implement partial quoting (e.g. /foo"*") in the future
if we so choose.  Currently, only fully quoted filenames are supported.

This is an extension of the original (and possibly misleading) quotation
semantics that was only meant for whitespace escaping.  It's backward
compatible since globs never worked correctly when used in a quoted
filename anyway, due to rpmGlob() splitting them by whitespace and
globbing the parts separately.  This was only fixed recently in commit
4030062 but hasn't yet been released,
so no spec files in production are really expected to be using such
filenames.

Make the newly added rpmEscapeChars() accept either a string of chars to
escape or a function, with the latter allowing the use of isspace(3)
which is locale-dependent in rpmEscapeSpaces().  For ease of use, expose
the string variant as rpmEscape(), though.
  • Loading branch information
dmnks authored and pmatilai committed Oct 4, 2022
1 parent 055734e commit d44114f
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 7 deletions.
12 changes: 11 additions & 1 deletion build/files.c
Expand Up @@ -926,8 +926,18 @@ static rpmRC parseForSimple(char * buf, FileEntry cur, ARGV_t * fileNames)
if (cur->attrFlags & (RPMFILE_DOC | RPMFILE_LICENSE))
cur->attrFlags |= RPMFILE_SPECIALDIR;
}
rpmUnescape(s, delim);

if (!quotes)
rpmUnescape(s, delim);
else {
rpmUnescape(s, "\"");
s = rpmEscape(s, "?*[]{}");
}

argvAdd(fileNames, s);

if (quotes)
free(s);
}

return res;
Expand Down
8 changes: 8 additions & 0 deletions include/rpm/rpmfileutil.h
Expand Up @@ -144,6 +144,14 @@ int rpmGlob(const char * pattern, int * argcPtr, ARGV_t * argvPtr);
*/
char * rpmEscapeSpaces(const char * s);

/** \ingroup rpmfileutil
* Escape given characters in string.
* @param s string
* @param accept chars to escape
* @return escaped string
*/
char * rpmEscape(const char *s, const char *accept);

/** \ingroup rpmfileutil
* Unescape each char listed in accept by removing a backslash preceding it.
* @param s string
Expand Down
16 changes: 13 additions & 3 deletions rpmio/rpmfileutil.c
Expand Up @@ -380,30 +380,40 @@ char * rpmGetPath(const char *path, ...)
return rpmCleanPath(res);
}

char * rpmEscapeSpaces(const char * s)
static char * rpmEscapeChars(const char *s, const char *accept, int (*fn)(int))
{
const char * se;
char * t;
char * te;
size_t nb = 0;

for (se = s; *se; se++) {
if (isspace(*se))
if ((accept && strchr(accept, *se)) || (fn && fn(*se)))
nb++;
nb++;
}
nb++;

t = te = xmalloc(nb);
for (se = s; *se; se++) {
if (isspace(*se))
if ((accept && strchr(accept, *se)) || (fn && fn(*se)))
*te++ = '\\';
*te++ = *se;
}
*te = '\0';
return t;
}

char * rpmEscapeSpaces(const char *s)
{
return rpmEscapeChars(s, NULL, isspace);
}

char * rpmEscape(const char *s, const char *accept)
{
return rpmEscapeChars(s, accept, NULL);
}

void rpmUnescape(char *s, const char *accept)
{
char *p, *q;
Expand Down
18 changes: 16 additions & 2 deletions tests/data/SPECS/globesctest.spec
Expand Up @@ -24,7 +24,6 @@ mkdir -p %{buildroot}/opt

# Glob escaping
touch '%{buildroot}/opt/foo[bar]'
touch '%{buildroot}/opt/foo[bar baz]'
touch '%{buildroot}/opt/foo\[bar\]'
touch '%{buildroot}/opt/foo*'
touch '%{buildroot}/opt/foo\bar'
Expand All @@ -47,6 +46,14 @@ touch '%{buildroot}/opt/foo b'
touch '%{buildroot}/opt/foo a'
touch '%{buildroot}/opt/foo r'

# Quoting
touch '%{buildroot}/opt/foo bar.conf'
touch '%{buildroot}/opt/foo [baz]'
touch '%{buildroot}/opt/foo[bar baz]'
touch '%{buildroot}/opt/foo\[baz\]'
touch '%{buildroot}/opt/foo\bay'
touch '%{buildroot}/opt/foo"baz"'

# Regression checks
touch '%{buildroot}/opt/foo-bar1'
touch '%{buildroot}/opt/foo-bar2'
Expand All @@ -72,7 +79,6 @@ touch '%{buildroot}/opt/foo[123]'
%doc foo\[bar\]
/opt/foo\[bar\]
/opt/foo\*[bar]
"/opt/foo\[bar baz\]"
/opt/foo\\\[bar\\\]
/opt/foo\*
/opt/foo\\bar
Expand All @@ -92,6 +98,14 @@ touch '%{buildroot}/opt/foo[123]'
/opt/foo"\ bar"
/opt/foo\ [bar]

# Quoting
%config "/opt/foo bar.conf"
"/opt/foo [baz]"
"/opt/foo[bar baz]"
"/opt/foo\\[baz\\]"
"/opt/foo\\bay"
"/opt/foo\"baz\""

# Regression checks
%doc ba* "foo bar"
/opt/foo-bar*
Expand Down
7 changes: 6 additions & 1 deletion tests/rpmbuild.at
Expand Up @@ -476,13 +476,16 @@ runroot rpmbuild -bb --quiet /data/SPECS/globesctest.spec
runroot rpm -qpl /build/RPMS/noarch/globesctest-1.0-1.noarch.rpm
],
[0],
[/opt/foo a
[/opt/foo [[baz]]
/opt/foo a
/opt/foo b
/opt/foo bar
/opt/foo bar.conf
/opt/foo r
/opt/foo" bar"
/opt/foo"'baz"'
/opt/foo"bar"
/opt/foo"baz"
/opt/foo%name
/opt/foo*
/opt/foo*a
Expand All @@ -497,7 +500,9 @@ runroot rpm -qpl /build/RPMS/noarch/globesctest-1.0-1.noarch.rpm
/opt/foo[[bax bay]]
/opt/foo\
/opt/foo\[[bar\]]
/opt/foo\[[baz\]]
/opt/foo\bar
/opt/foo\bay
/opt/foobar
/opt/foobara
/opt/foobarb
Expand Down

0 comments on commit d44114f

Please sign in to comment.