The DT_* values returned by getdents (readdir) are only hints and not required. In fact, some Linux filesystems return DT_UNKNOWN for most entries, regardless of actual type. This causes make to mis-match patterns with a trailing slash (via GLOB_ONLYDIR) (see make's functions/wildcard test case). Thus, this patch detects that case and uses is_dir() to make the type known enough for proper operation.
Performance in non-DT_UNKNOWN cases is not affected. The lack of DT_* is a well known issue on older XFS installations (for example, RHEL 7 and 8, Fedora 28) but can be recreated by creating an XFS filesystem with flags that mimic older behavior: $ fallocate -l 10G /xfs.fs $ mkfs.xfs -n ftype=0 -m crc=0 -f /xfs.fs $ mkdir /xfs $ mount -o loop /xfs.fs /xfs [tested by importing this change to glibc, and using that to run make's testsuite] diff --git a/lib/glob.c b/lib/glob.c index 22c459574..d0521bb4a 100644 --- a/lib/glob.c +++ b/lib/glob.c @@ -1381,7 +1381,26 @@ glob_in_dir (const char *pattern, const char *directory, int flags, if (flags & GLOB_ONLYDIR) switch (readdir_result_type (d)) { - case DT_DIR: case DT_LNK: case DT_UNKNOWN: break; + case DT_DIR: case DT_LNK: break; + case DT_UNKNOWN: + { + /* The filesystem was too lazy to give us a hint, + so we have to do it the hard way. */ + char *fullpath, *p; + bool isdir; + fullpath = malloc (strlen (directory) + strlen (d.name) + 2); + if (fullpath == NULL) + /* This matches old behavior wrt DT_UNKNOWN. */ + break; + p = stpcpy (fullpath, directory); + *p++ = '/'; + strcpy (p, d.name); + isdir = is_dir (fullpath); + free (fullpath); + if (isdir) + break; + continue; + } default: continue; }