summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/regex/glob.c17
1 files changed, 12 insertions, 5 deletions
diff --git a/src/regex/glob.c b/src/regex/glob.c
index 58248675..9de080ed 100644
--- a/src/regex/glob.c
+++ b/src/regex/glob.c
@@ -92,16 +92,23 @@ static int do_glob(char *buf, size_t pos, int type, char *pat, int flags, int (*
if (!*pat) {
/* If we consumed any components above, or if GLOB_MARK is
* requested and we don't yet know if the match is a dir,
- * we must call stat to confirm the file exists and/or
- * determine its type. */
+ * we must confirm the file exists and/or determine its type.
+ *
+ * If marking dirs, symlink type is inconclusive; we need the
+ * type for the symlink target, and therefore must try stat
+ * first unless type is known not to be a symlink. Otherwise,
+ * or if that fails, use lstat for determining existence to
+ * avoid false negatives in the case of broken symlinks. */
struct stat st;
- if ((flags & GLOB_MARK) && type==DT_LNK) type = 0;
- if (!type && stat(buf, &st)) {
+ if ((flags & GLOB_MARK) && (!type||type==DT_LNK) && !stat(buf, &st)) {
+ if (S_ISDIR(st.st_mode)) type = DT_DIR;
+ else type = DT_REG;
+ }
+ if (!type && lstat(buf, &st)) {
if (errno!=ENOENT && (errfunc(buf, errno) || (flags & GLOB_ERR)))
return GLOB_ABORTED;
return 0;
}
- if (!type && S_ISDIR(st.st_mode)) type = DT_DIR;
if (append(tail, buf, pos, (flags & GLOB_MARK) && type==DT_DIR))
return GLOB_NOSPACE;
return 0;