diff --git a/build/files.c b/build/files.c index 4cc8df5bec..d644a192e9 100644 --- a/build/files.c +++ b/build/files.c @@ -2152,13 +2152,14 @@ static int generateBuildIDs(FileList fl, ARGV_t *files) * Add a file to a binary package. * @param pkg * @param fl package file tree walk data - * @param fileName file to add + * @param fn file to add * @return RPMRC_OK on success */ -static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName) +static rpmRC processBinaryFile(Package pkg, FileList fl, const char *fn) { int quote = 1; /* XXX permit quoted glob characters. */ int doGlob; + char *fileName = xstrdup(fn); char *diskPath = NULL; rpmRC rc = RPMRC_OK; size_t fnlen = strlen(fileName); @@ -2168,7 +2169,8 @@ static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName) if (trailing_slash && !fl->cur.isDir) fl->cur.isDir = -1; - doGlob = rpmIsGlob(fileName, quote); + if (!(doGlob = rpmIsGlob(fileName, quote))) + rpmUnescape(fileName, NULL); /* Check that file starts with leading "/" */ if (*fileName != '/') { @@ -2220,6 +2222,7 @@ static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName) } exit: + free(fileName); free(diskPath); if (rc) { fl->processingFailed = 1; @@ -2416,7 +2419,7 @@ static void processSpecialDir(rpmSpec spec, Package pkg, FileList fl, fi = 0; while (*files != NULL) { char *origfile = rpmGenPath(basepath, *files, NULL); - char *eorigfile = rpmEscapeSpaces(origfile); + char *eorigfile = NULL; ARGV_t globFiles; int globFilesCount, i; char *newfile; @@ -2426,7 +2429,17 @@ static void processSpecialDir(rpmSpec spec, Package pkg, FileList fl, copyFileEntry(&sd->entries[fi].curEntry, &fl->cur); copyFileEntry(&sd->entries[fi].defEntry, &fl->def); fi++; + files++; + if (!rpmIsGlob(origfile, 1)) { + rasprintf(&newfile, "%s/%s", sd->dirname, basename(origfile)); + processBinaryFile(pkg, fl, newfile); + free(newfile); + free(origfile); + continue; + } + + eorigfile = rpmEscapeSpaces(origfile); if (rpmGlob(eorigfile, &globFilesCount, &globFiles) == 0) { for (i = 0; i < globFilesCount; i++) { rasprintf(&newfile, "%s/%s", sd->dirname, basename(globFiles[i])); @@ -2440,7 +2453,6 @@ static void processSpecialDir(rpmSpec spec, Package pkg, FileList fl, } free(eorigfile); free(origfile); - files++; } free(basepath); diff --git a/include/rpm/rpmfileutil.h b/include/rpm/rpmfileutil.h index 215db129c9..dd620d5492 100644 --- a/include/rpm/rpmfileutil.h +++ b/include/rpm/rpmfileutil.h @@ -131,6 +131,13 @@ int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr); */ char * rpmEscapeSpaces(const char * s); +/** \ingroup rpmfileutil + * Unescape each char listed in accept by removing a backslash preceding it. + * @param s string + * @param accept chars to escape (NULL for all) + */ +void rpmUnescape(char *s, const char *accept); + /** \ingroup rpmfileutil * Return type of compression used in file. * @param file name of file diff --git a/rpmio/rpmfileutil.c b/rpmio/rpmfileutil.c index 4a939dbfe0..b8e43bc6f6 100644 --- a/rpmio/rpmfileutil.c +++ b/rpmio/rpmfileutil.c @@ -410,6 +410,22 @@ char * rpmEscapeSpaces(const char * s) return t; } +void rpmUnescape(char *s, const char *accept) +{ + char *p, *q; + int esc = 0; + p = q = s; + while (*q != '\0') { + *p = *q++; + esc = (*p == '\\') && \ + (accept == NULL || ((*q != '\0') && strchr(accept, *q))) && \ + !esc; + if (!esc) + p++; + } + *p = '\0'; +} + int rpmFileHasSuffix(const char *path, const char *suffix) { size_t plen = strlen(path); diff --git a/rpmio/rpmglob.c b/rpmio/rpmglob.c index 93f7fa54dc..a299491566 100644 --- a/rpmio/rpmglob.c +++ b/rpmio/rpmglob.c @@ -126,7 +126,7 @@ static inline const char *next_brace_sub(const char *begin) return *cp != '\0' ? cp : NULL; } -static int __glob_pattern_p(const char *pattern, int quote); +static int __glob_pattern_p(const char *pattern, int flags); /* Do glob searching for PATTERN, placing results in PGLOB. The bits defined above may be set in FLAGS. @@ -395,7 +395,7 @@ glob(const char *pattern, int flags, return GLOB_NOMATCH; } - if (__glob_pattern_p(dirname, !(flags & GLOB_NOESCAPE))) { + if (__glob_pattern_p(dirname, flags & ~GLOB_BRACE)) { /* The directory name contains metacharacters, so we have to glob for the directory, and then glob for the pattern in each directory found. */ @@ -621,10 +621,11 @@ static int prefix_array(const char *dirname, char **array, size_t n) } /* Return nonzero if PATTERN contains any metacharacters. - Metacharacters can be quoted with backslashes if QUOTE is nonzero. */ -static int __glob_pattern_p(const char *pattern, int quote) + Metacharacters can be quoted with backslashes if FLAGS does not contain + GLOB_NOESCAPE. */ +static int __glob_pattern_p(const char *pattern, int flags) { - register const char *p; + register const char *p, *q; int openBrackets = 0; for (p = pattern; *p != '\0'; ++p) @@ -634,7 +635,7 @@ static int __glob_pattern_p(const char *pattern, int quote) return 1; case '\\': - if (quote && p[1] != '\0') + if (!(flags & GLOB_NOESCAPE) && p[1] != '\0') ++p; break; @@ -646,6 +647,15 @@ static int __glob_pattern_p(const char *pattern, int quote) if (openBrackets) return 1; break; + + case '{': + if (!(flags & GLOB_BRACE)) + break; + q = p; + while (*q != '}') + if ((q = next_brace_sub(q + 1)) == NULL) + break; + return 1; } return 0; @@ -670,7 +680,7 @@ glob_in_dir(const char *pattern, const char *directory, int flags, int meta; int save; - meta = __glob_pattern_p(pattern, !(flags & GLOB_NOESCAPE)); + meta = __glob_pattern_p(pattern, flags & ~GLOB_BRACE); if (meta == 0) { if (flags & (GLOB_NOCHECK | GLOB_NOMAGIC)) /* We need not do any tests. The PATTERN contains no meta @@ -942,32 +952,8 @@ int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr) int rpmIsGlob(const char * pattern, int quote) { - if (!__glob_pattern_p(pattern, quote)) { - - const char *begin; - const char *next; - const char *rest; - - begin = strchr(pattern, '{'); - if (begin == NULL) - return 0; - /* - * Find the first sub-pattern and at the same time find the - * rest after the closing brace. - */ - next = next_brace_sub(begin + 1); - if (next == NULL) - return 0; - - /* Now find the end of the whole brace expression. */ - rest = next; - while (*rest != '}') { - rest = next_brace_sub(rest + 1); - if (rest == NULL) - return 0; - } - /* Now we can be sure that brace expression is well-foermed. */ - } - - return 1; + int flags = GLOB_BRACE; + if (!quote) + flags |= GLOB_NOESCAPE; + return __glob_pattern_p(pattern, flags); } diff --git a/tests/Makefile.am b/tests/Makefile.am index 1e87715508..f2335268e1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -61,6 +61,7 @@ EXTRA_DIST += data/SPECS/hello2-suid.spec EXTRA_DIST += data/SPECS/hello-g3.spec EXTRA_DIST += data/SPECS/foo.spec EXTRA_DIST += data/SPECS/globtest.spec +EXTRA_DIST += data/SPECS/globesctest.spec EXTRA_DIST += data/SPECS/versiontest.spec EXTRA_DIST += data/SPECS/conflicttest.spec EXTRA_DIST += data/SPECS/configtest.spec @@ -192,7 +193,7 @@ populate_testing: $(check_DATA) for d in dev etc magic tmp var; do if [ ! -d testing/$${d} ]; then mkdir testing/$${d}; fi; done for node in urandom stdin stderr stdout null full; do ln -s /dev/$${node} testing/dev/$${node}; done for cf in hosts resolv.conf passwd shadow group gshadow mtab ; do [ -f /etc/$${cf} ] && ln -s /etc/$${cf} testing/etc/$${cf}; done - for prog in gzip cat patch tar sh ln chmod rm mkdir uname grep sed find file ionice mktemp nice cut sort diff touch install wc coreutils xargs; do p=`which $${prog}`; if [ "$${p}" != "" ]; then ln -s $${p} testing/$(bindir)/; fi; done + for prog in gzip cat cp patch tar sh ln chmod rm mkdir uname grep sed find file ionice mktemp nice cut sort diff touch install wc coreutils xargs; do p=`which $${prog}`; if [ "$${p}" != "" ]; then ln -s $${p} testing/$(bindir)/; fi; done for d in /proc /sys /selinux /etc/selinux; do if [ -d $${d} ]; then ln -s $${d} testing/$${d}; fi; done (cd testing/magic && file -C) chmod -R u-w testing/ diff --git a/tests/data/SPECS/globesctest.spec b/tests/data/SPECS/globesctest.spec new file mode 100644 index 0000000000..92a3478555 --- /dev/null +++ b/tests/data/SPECS/globesctest.spec @@ -0,0 +1,70 @@ +# We need to hardcode the install prefix so that we can compare the %%doc +# filenames in the resulting package (with "rpm -qpl") against a static list in +# tests/rpmbuild.at. +%global _prefix /opt + +Name: globesctest +Version: 1.0 +Release: 1 +Summary: Testing file glob escape behavior +Group: Testing +License: GPL +BuildArch: noarch + +%description +%{summary}. + + +%build +touch 'foo[bar]' bar baz 'foo bar' + +%install +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' +touch %{buildroot}/opt/foo\\ +touch '%{buildroot}/opt/foo?bar' +touch '%{buildroot}/opt/foo{bar,baz}' + +# Regression checks +touch '%{buildroot}/opt/foo-bar1' +touch '%{buildroot}/opt/foo-bar2' +touch '%{buildroot}/opt/fooxbarybaz' +touch "%{buildroot}/opt/foo'baz" +touch '%{buildroot}/opt/foobar' +touch '%{buildroot}/opt/foobaz' +touch '%{buildroot}/opt/foobara' +touch '%{buildroot}/opt/foobarb' +touch '%{buildroot}/opt/foobaza' +touch '%{buildroot}/opt/foobazb' +touch '%{buildroot}/opt/foobaya' +touch '%{buildroot}/opt/foobayb' +touch '%{buildroot}/opt/foobawa' +touch '%{buildroot}/opt/foobawb' + +%files + +%doc foo\[bar\] ba* "foo bar" + +# Glob escaping +/opt/foo\[bar\] +"/opt/foo\[bar baz\]" +/opt/foo\\\[bar\\\] +/opt/foo\* +/opt/foo\\bar +/opt/foo\\ +/opt/foo\?bar +/opt/foo\{bar,baz\} + +# Regression checks +/opt/foo-bar* +/opt/foo?bar?baz +/opt/foo'baz +/opt/foo{bar,baz} +/opt/foo{bar{a,b},baz{a,b}} +/opt/foo{bay*,baw*} diff --git a/tests/rpmbuild.at b/tests/rpmbuild.at index b7bd0d7f02..92deddf4fa 100644 --- a/tests/rpmbuild.at +++ b/tests/rpmbuild.at @@ -433,6 +433,47 @@ warning: absolute symlink: /opt/globtest/linkgood -> /opt/globtest/zab ]) AT_CLEANUP +AT_SETUP([rpmbuild glob escape]) +AT_KEYWORDS([build]) +AT_CHECK([ +RPMDB_INIT + +runroot rpmbuild -bb --quiet /data/SPECS/globesctest.spec +runroot rpm -qpl /build/RPMS/noarch/globesctest-1.0-1.noarch.rpm +], +[0], +[/opt/foo'baz +/opt/foo* +/opt/foo-bar1 +/opt/foo-bar2 +/opt/foo?bar +/opt/foo[[bar baz]] +/opt/foo[[bar]] +/opt/foo\ +/opt/foo\[[bar\]] +/opt/foo\bar +/opt/foobar +/opt/foobara +/opt/foobarb +/opt/foobawa +/opt/foobawb +/opt/foobaya +/opt/foobayb +/opt/foobaz +/opt/foobaza +/opt/foobazb +/opt/fooxbarybaz +/opt/foo{bar,baz} +/opt/share/doc/globesctest-1.0 +/opt/share/doc/globesctest-1.0/bar +/opt/share/doc/globesctest-1.0/baz +/opt/share/doc/globesctest-1.0/foo bar +/opt/share/doc/globesctest-1.0/foo[[bar]] +], +[], +) +AT_CLEANUP + AT_SETUP([rpmbuild prefixpostfix]) AT_KEYWORDS([build]) AT_CHECK([