2121#include <sys/stat.h>
2222#include <dirent.h>
2323
24+ #ifndef RBIMPL_ATTR_NORETURN
25+ #define RBIMPL_ATTR_NORETURN ()
26+ #endif
27+
2428#ifdef __APPLE__
2529 // The symbol is present, however not in the headers
2630 // See: https://github.com/rails/bootsnap/issues/470
@@ -152,6 +156,21 @@ bs_rb_get_path(VALUE self, VALUE fname)
152156}
153157
154158#ifdef HAVE_FSTATAT
159+
160+ RBIMPL_ATTR_NORETURN ()
161+ static void
162+ bs_syserr_fail_path (const char * func_name , int n , VALUE path )
163+ {
164+ rb_syserr_fail_str (n , rb_sprintf ("%s @ %s" , func_name , RSTRING_PTR (path )));
165+ }
166+
167+ RBIMPL_ATTR_NORETURN ()
168+ static void
169+ bs_syserr_fail_dir_entry (const char * func_name , int n , VALUE dir , const char * d_name )
170+ {
171+ rb_syserr_fail_str (n , rb_sprintf ("%s @ %s/%s" , func_name , RSTRING_PTR (dir ), d_name ));
172+ }
173+
155174static VALUE
156175bs_rb_scan_dir (VALUE self , VALUE abspath )
157176{
@@ -167,7 +186,17 @@ bs_rb_scan_dir(VALUE self, VALUE abspath)
167186 if (errno == ENOTDIR || errno == ENOENT ) {
168187 return result ;
169188 }
170- rb_sys_fail ("opendir" );
189+
190+ // BUG: Some users reported a crash here because Ruby's syserr trigger
191+ // a crash if called with `errno == 0`.
192+ // The opendir spec is quite clear that if it returns NULL, then `errno` must
193+ // be set, and yet here we are.
194+ // So turning no errno into EINVAL, and from there I hope to get to the bottom of things.
195+ if (errno == 0 ) {
196+ errno = EINVAL ;
197+ }
198+
199+ bs_syserr_fail_path ("opendir" , errno , abspath );
171200 return Qundef ;
172201 }
173202
@@ -185,17 +214,22 @@ bs_rb_scan_dir(VALUE self, VALUE abspath)
185214 if (dfd < 0 ) {
186215 dfd = dirfd (dirp );
187216 if (dfd < 0 ) {
188- rb_sys_fail ("dirfd" );
217+ int err = errno ;
218+ closedir (dirp );
219+ bs_syserr_fail_path ("dirfd" , err , abspath );
189220 return Qundef ;
190221 }
191222 }
192223
193224 if (fstatat (dfd , entry -> d_name , & st , 0 )) {
194225 if (errno == ENOENT ) {
195226 // Broken symlinK
227+ errno = 0 ;
196228 continue ;
197229 }
198- rb_sys_fail ("fstatat" );
230+ int err = errno ;
231+ closedir (dirp );
232+ bs_syserr_fail_dir_entry ("fstatat" , err , abspath , entry -> d_name );
199233 return Qundef ;
200234 }
201235
@@ -226,7 +260,7 @@ bs_rb_scan_dir(VALUE self, VALUE abspath)
226260 }
227261
228262 if (closedir (dirp )) {
229- rb_sys_fail ("closedir" );
263+ bs_syserr_fail_path ("closedir" , errno , abspath );
230264 return Qundef ;
231265 }
232266 return result ;
0 commit comments