Skip to content

Commit c9a4941

Browse files
committed
Dir.scan: fallback to lstat when encountering DT_UNKNOWN
This was part of the spec accepted in [Feature #17001] but that I forgot to include in the initial implementation. to ensure coverage, we always go though the fallback path in debug mode.
1 parent 72beb17 commit c9a4941

File tree

1 file changed

+72
-20
lines changed

1 file changed

+72
-20
lines changed

dir.c

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -506,16 +506,16 @@ fnmatch(
506506
VALUE rb_cDir;
507507
static VALUE sym_directory, sym_link, sym_file, sym_unknown;
508508

509-
#ifdef DT_BLK
509+
#if defined(DT_BLK) || defined(S_IFBLK)
510510
static VALUE sym_block_device;
511511
#endif
512-
#ifdef DT_CHR
512+
#if defined(DT_CHR) || defined(S_IFCHR)
513513
static VALUE sym_character_device;
514514
#endif
515-
#ifdef DT_FIFO
515+
#if defined(DT_FIFO) || defined(S_IFIFO)
516516
static VALUE sym_fifo;
517517
#endif
518-
#ifdef DT_SOCK
518+
#if defined(DT_SOCK) || defined(S_IFSOCK)
519519
static VALUE sym_socket;
520520
#endif
521521

@@ -919,19 +919,26 @@ dir_read(VALUE dir)
919919
}
920920
}
921921

922-
static VALUE dir_each_entry(VALUE, VALUE (*)(VALUE, VALUE, unsigned char), VALUE, int);
922+
struct dir_entry_args {
923+
struct dir_data *dirp;
924+
struct dirent *dp;
925+
};
926+
927+
static VALUE dir_each_entry(VALUE, VALUE (*)(VALUE, VALUE, struct dir_entry_args *), VALUE, int);
923928

924929
static VALUE
925-
dir_yield(VALUE arg, VALUE path, unsigned char dtype)
930+
dir_yield(VALUE arg, VALUE path, struct dir_entry_args *_unused)
926931
{
927932
return rb_yield(path);
928933
}
929934

935+
static int do_lstat(int fd, const char *path, struct stat *pst, int flags, rb_encoding *enc);
936+
930937
static VALUE
931-
dir_yield_with_type(VALUE arg, VALUE path, unsigned char dtype)
938+
dir_yield_with_type(VALUE arg, VALUE path, struct dir_entry_args *dir_entry)
932939
{
933940
VALUE type;
934-
switch (dtype) {
941+
switch (dir_entry->dp->d_type) {
935942
#ifdef DT_BLK
936943
case DT_BLK:
937944
type = sym_block_device;
@@ -966,6 +973,47 @@ dir_yield_with_type(VALUE arg, VALUE path, unsigned char dtype)
966973
break;
967974
}
968975

976+
#ifdef HAVE_DIRFD
977+
if (RUBY_DEBUG || RB_UNLIKELY(type == sym_unknown)) {
978+
struct stat st;
979+
if (do_lstat(dirfd(dir_entry->dirp->dir), dir_entry->dp->d_name, &st, 0, rb_filesystem_encoding()) == 0) {
980+
switch (st.st_mode & S_IFMT) {
981+
case S_IFDIR:
982+
type = sym_directory;
983+
break;
984+
case S_IFLNK:
985+
type = sym_link;
986+
break;
987+
case S_IFREG:
988+
type = sym_file;
989+
break;
990+
#ifdef S_IFSOCK
991+
case S_IFSOCK:
992+
type = sym_socket;
993+
break;
994+
#endif
995+
#ifdef S_IFIFO
996+
case S_IFIFO:
997+
type = sym_fifo;
998+
break;
999+
#endif
1000+
#ifdef S_IFBLK
1001+
case S_IFBLK:
1002+
type = sym_block_device;
1003+
break;
1004+
#endif
1005+
#ifdef S_IFCHR
1006+
case S_IFCHR:
1007+
type = sym_character_device;
1008+
break;
1009+
#endif
1010+
default:
1011+
break;
1012+
}
1013+
}
1014+
}
1015+
#endif // HAVE_DIRFD
1016+
9691017
if (NIL_P(arg)) {
9701018
return rb_yield_values(2, path, type);
9711019
}
@@ -1001,7 +1049,7 @@ dir_each(VALUE dir)
10011049
}
10021050

10031051
static VALUE
1004-
dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE, unsigned char), VALUE arg, int children_only)
1052+
dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE, struct dir_entry_args *), VALUE arg, int children_only)
10051053
{
10061054
struct dir_data *dirp;
10071055
struct dirent *dp;
@@ -1027,7 +1075,11 @@ dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE, unsigned char), VALUE arg,
10271075
else
10281076
#endif
10291077
path = rb_external_str_new_with_enc(name, namlen, dirp->enc);
1030-
(*each)(arg, path, dp->d_type);
1078+
struct dir_entry_args each_args = {
1079+
.dirp = dirp,
1080+
.dp = dp,
1081+
};
1082+
(*each)(arg, path, &each_args);
10311083
}
10321084
return dir;
10331085
}
@@ -1865,7 +1917,7 @@ nogvl_stat(void *args)
18651917

18661918
/* System call with warning */
18671919
static int
1868-
do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
1920+
do_stat(int fd, const char *path, struct stat *pst, int flags, rb_encoding *enc)
18691921
{
18701922
#if USE_OPENDIR_AT
18711923
struct fstatat_args args;
@@ -1897,7 +1949,7 @@ nogvl_lstat(void *args)
18971949
#endif
18981950

18991951
static int
1900-
do_lstat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
1952+
do_lstat(int fd, const char *path, struct stat *pst, int flags, rb_encoding *enc)
19011953
{
19021954
#if USE_OPENDIR_AT
19031955
struct fstatat_args args;
@@ -2840,15 +2892,15 @@ glob_helper(
28402892

28412893
if (*path) {
28422894
if (match_all && pathtype == path_unknown) {
2843-
if (do_lstat(fd, baselen, path, &st, flags, enc) == 0) {
2895+
if (do_lstat(fd, path, &st, flags, enc) == 0) {
28442896
pathtype = IFTODT(st.st_mode);
28452897
}
28462898
else {
28472899
pathtype = path_noent;
28482900
}
28492901
}
28502902
if (match_dir && (pathtype == path_unknown || pathtype == path_symlink)) {
2851-
if (do_stat(fd, baselen, path, &st, flags, enc) == 0) {
2903+
if (do_stat(fd, path, &st, flags, enc) == 0) {
28522904
pathtype = IFTODT(st.st_mode);
28532905
}
28542906
else {
@@ -2976,7 +3028,7 @@ glob_helper(
29763028
if (recursive && dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1) &&
29773029
new_pathtype == path_unknown) {
29783030
/* RECURSIVE never match dot files unless FNM_DOTMATCH is set */
2979-
if (do_lstat(fd, baselen, buf, &st, flags, enc) == 0)
3031+
if (do_lstat(fd, buf, &st, flags, enc) == 0)
29803032
new_pathtype = IFTODT(st.st_mode);
29813033
else
29823034
new_pathtype = path_noent;
@@ -3532,7 +3584,7 @@ dir_foreach(int argc, VALUE *argv, VALUE io)
35323584
}
35333585

35343586
static VALUE
3535-
dir_entry_ary_push(VALUE ary, VALUE entry, unsigned char ftype)
3587+
dir_entry_ary_push(VALUE ary, VALUE entry, struct dir_entry_args *_unused)
35363588
{
35373589
return rb_ary_push(ary, entry);
35383590
}
@@ -3935,16 +3987,16 @@ Init_Dir(void)
39353987
sym_file = ID2SYM(rb_intern("file"));
39363988
sym_unknown = ID2SYM(rb_intern("unknown"));
39373989

3938-
#ifdef DT_BLK
3990+
#if defined(DT_BLK) || defined(S_IFBLK)
39393991
sym_block_device = ID2SYM(rb_intern("blockSpecial"));
39403992
#endif
3941-
#ifdef DT_CHR
3993+
#if defined(DT_CHR) || defined(S_IFCHR)
39423994
sym_character_device = ID2SYM(rb_intern("characterSpecial"));
39433995
#endif
3944-
#ifdef DT_FIFO
3996+
#if defined(DT_FIFO) || defined(S_IFIFO)
39453997
sym_fifo = ID2SYM(rb_intern("fifo"));
39463998
#endif
3947-
#ifdef DT_SOCK
3999+
#if defined(DT_SOCK) || defined(S_IFSOCK)
39484000
sym_socket = ID2SYM(rb_intern("socket"));
39494001
#endif
39504002

0 commit comments

Comments
 (0)