ruby-changes:46085
From: nobu <ko1@a...>
Date: Mon, 27 Mar 2017 23:57:14 +0900 (JST)
Subject: [ruby-changes:46085] nobu:r58156 (trunk): dir.c: err at glob failure
nobu 2017-03-27 23:57:08 +0900 (Mon, 27 Mar 2017) New Revision: 58156 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=58156 Log: dir.c: err at glob failure * dir.c (glob_helper): raise a SystemCallError exception when opendir() failed, except for ENOENT, ENOTDIR, and EACCES. this behavior predates 1.0; the comments in glob.c claimed that glob() returned -1 on error but actualy the pointer to a global variable, then dir_glob() did check only -1 as the comments, and ignored actual errors. [ruby-core:80226] [Bug #13276] dir.c: ruby_glob_funcs_t Modified files: trunk/dir.c trunk/error.c trunk/test/ruby/test_dir.rb Index: error.c =================================================================== --- error.c (revision 58155) +++ error.c (revision 58156) @@ -2470,7 +2470,6 @@ rb_sys_enc_warning(rb_encoding *enc, con https://github.com/ruby/ruby/blob/trunk/error.c#L2470 } } -#if 0 void rb_syserr_enc_warning(int err, rb_encoding *enc, const char *fmt, ...) { @@ -2480,7 +2479,6 @@ rb_syserr_enc_warning(int err, rb_encodi https://github.com/ruby/ruby/blob/trunk/error.c#L2479 } } } -#endif void rb_load_fail(VALUE path, const char *err) Index: dir.c =================================================================== --- dir.c (revision 58155) +++ dir.c (revision 58156) @@ -1247,6 +1247,12 @@ to_be_ignored(int e) https://github.com/ruby/ruby/blob/trunk/dir.c#L1247 #define STAT(p, s) stat((p), (s)) #endif +typedef int ruby_glob_errfunc(const char*, VALUE, const void*, int); +typedef struct { + ruby_glob_func *match; + ruby_glob_errfunc *error; +} ruby_glob_funcs_t; + /* System call with warning */ static int do_stat(const char *path, struct stat *pst, int flags, rb_encoding *enc) @@ -1273,7 +1279,8 @@ do_lstat(const char *path, struct stat * https://github.com/ruby/ruby/blob/trunk/dir.c#L1279 #endif static DIR * -do_opendir(const char *path, int flags, rb_encoding *enc) +do_opendir(const char *path, int flags, rb_encoding *enc, + ruby_glob_errfunc *errfunc, VALUE arg, int *status) { DIR *dirp; #ifdef _WIN32 @@ -1294,7 +1301,12 @@ do_opendir(const char *path, int flags, https://github.com/ruby/ruby/blob/trunk/dir.c#L1301 e = errno; /* fallback */ case 0: + *status = 0; if (to_be_ignored(e)) break; + if (errfunc) { + *status = (*errfunc)(path, arg, enc, e); + break; + } sys_warning(path, enc); } } @@ -1704,6 +1716,61 @@ glob_func_caller(VALUE val) https://github.com/ruby/ruby/blob/trunk/dir.c#L1716 return Qnil; } +struct glob_error_args { + const char *path; + rb_encoding *enc; + int error; +}; + +static VALUE +glob_func_warning(VALUE val) +{ + struct glob_error_args *arg = (struct glob_error_args *)val; + rb_syserr_enc_warning(arg->error, arg->enc, "%s", arg->path); + return Qnil; +} + +#if 0 +static int +rb_glob_warning(const char *path, VALUE a, const void *enc, int error) +{ + int status; + struct glob_error_args args; + + args.path = path; + args.enc = enc; + args.error = error; + rb_protect(glob_func_warning, (VALUE)&args, &status); + return status; +} +#endif + +static VALUE +glob_func_error(VALUE val) +{ + struct glob_error_args *arg = (struct glob_error_args *)val; + VALUE path = rb_enc_str_new_cstr(arg->path, arg->enc); + rb_syserr_fail_str(arg->error, path); + return Qnil; +} + +static int +rb_glob_error(const char *path, VALUE a, const void *enc, int error) +{ + int status; + struct glob_error_args args; + VALUE (*errfunc)(VALUE) = glob_func_error; + + if (error == EACCES) { + errfunc = glob_func_warning; + } + args.path = path; + args.enc = enc; + args.error = error; + rb_protect(errfunc, (VALUE)&args, &status); + return status; +} + static inline int dirent_match(const char *pat, rb_encoding *enc, const char *name, const struct dirent *dp, int flags) { @@ -1725,7 +1792,7 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L1792 struct glob_pattern **beg, struct glob_pattern **end, int flags, - ruby_glob_func *func, + const ruby_glob_funcs_t *funcs, VALUE arg, rb_encoding *enc) { @@ -1784,13 +1851,13 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L1851 } } if (match_all && pathtype > path_noent) { - status = glob_call_func(func, path, arg, enc); + status = glob_call_func(funcs->match, path, arg, enc); if (status) return status; } if (match_dir && pathtype == path_directory) { char *tmp = join_path(path, pathlen, dirsep, "", 0); if (!tmp) return -1; - status = glob_call_func(func, tmp, arg, enc); + status = glob_call_func(funcs->match, tmp, arg, enc); GLOB_FREE(tmp); if (status) return status; } @@ -1809,12 +1876,14 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L1876 if (cur + 1 == end && (*cur)->type <= ALPHA) { plainname = join_path(path, pathlen, dirsep, (*cur)->str, strlen((*cur)->str)); if (!plainname) return -1; - dirp = do_opendir(plainname, flags, enc); + dirp = do_opendir(plainname, flags, enc, funcs->error, arg, &status); GLOB_FREE(plainname); } else +# else + ; # endif - dirp = do_opendir(*path ? path : ".", flags, enc); + dirp = do_opendir(*path ? path : ".", flags, enc, funcs->error, arg, &status); if (dirp == NULL) { # if FNM_SYSCASE || NORMALIZE_UTF8PATH if ((magical < 2) && !recursive && (errno == EACCES)) { @@ -1822,7 +1891,7 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L1891 goto literally; } # endif - return 0; + return status; } IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : ".")); @@ -1922,7 +1991,7 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L1991 status = glob_helper(buf, name - buf + namlen, 1, new_pathtype, new_beg, new_end, - flags, func, arg, enc); + flags, funcs, arg, enc); GLOB_FREE(buf); GLOB_FREE(new_beg); if (status) break; @@ -1982,11 +2051,12 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L2051 long base = pathlen + (dirsep != 0); buf = replace_real_basename(buf, base, enc, IF_NORMALIZE_UTF8PATH(1)+0, flags, &new_pathtype); + if (!buf) break; } #endif status = glob_helper(buf, pathlen + strlen(buf + pathlen), 1, new_pathtype, new_beg, new_end, - flags, func, arg, enc); + flags, funcs, arg, enc); GLOB_FREE(buf); GLOB_FREE(new_beg); if (status) break; @@ -2000,7 +2070,9 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/dir.c#L2070 } static int -ruby_glob0(const char *path, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc) +ruby_glob0(const char *path, int flags, + const ruby_glob_funcs_t *funcs, VALUE arg, + rb_encoding *enc) { struct glob_pattern *list; const char *root, *start; @@ -2028,7 +2100,7 @@ ruby_glob0(const char *path, int flags, https://github.com/ruby/ruby/blob/trunk/dir.c#L2100 return -1; } status = glob_helper(buf, n, 0, path_unknown, &list, &list + 1, - flags, func, arg, enc); + flags, funcs, arg, enc); glob_free_pattern(list); GLOB_FREE(buf); @@ -2038,8 +2110,11 @@ ruby_glob0(const char *path, int flags, https://github.com/ruby/ruby/blob/trunk/dir.c#L2110 int ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg) { - return ruby_glob0(path, flags & ~GLOB_VERBOSE, func, arg, - rb_ascii8bit_encoding()); + ruby_glob_funcs_t funcs; + funcs.match = func; + funcs.error = NULL; + return ruby_glob0(path, flags & ~GLOB_VERBOSE, + &funcs, arg, rb_ascii8bit_encoding()); } static int @@ -2053,6 +2128,10 @@ rb_glob_caller(const char *path, VALUE a https://github.com/ruby/ruby/blob/trunk/dir.c#L2128 return status; } +static const ruby_glob_funcs_t rb_glob_funcs = { + rb_glob_caller, rb_glob_error, +}; + void rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg) { @@ -2063,8 +2142,8 @@ rb_glob(const char *path, void (*func)(c https://github.com/ruby/ruby/blob/trunk/dir.c#L2142 args.value = arg; args.enc = rb_ascii8bit_encoding(); - status = ruby_glob0(path, GLOB_VERBOSE, rb_glob_caller, (VALUE)&args, - args.enc); + status = ruby_glob0(path, GLOB_VERBOSE, &rb_glob_funcs, + (VALUE)&args, args.enc); if (status) GLOB_JUMP_TAG(status); } @@ -2142,7 +2221,7 @@ ruby_brace_expand(const char *str, int f https://github.com/ruby/ruby/blob/trunk/dir.c#L2221 } struct brace_args { - ruby_glob_func *func; + ruby_glob_funcs_t funcs; VALUE value; int flags; }; @@ -2152,7 +2231,7 @@ glob_brace(const char *path, VALUE val, https://github.com/ruby/ruby/blob/trunk/dir.c#L2231 { struct brace_args *arg = (struct brace_args *)val; - return ruby_glob0(path, arg->flags, arg->func, arg->value, enc); + return ruby_glob0(path, arg->flags, &arg->funcs, arg->value, enc); } int @@ -2161,7 +2240,8 @@ ruby_brace_glob_with_enc(const char *str https://github.com/ruby/ruby/blob/trunk/dir.c#L2240 struct brace_args args; flags &= ~GLOB_VERBOSE; - args.func = func; + args.funcs.match = func; + args.funcs.error = NULL; args.value = arg; args.flags = flags; return ruby_brace_expand(str, flags, glob_brace, (VALUE)&args, enc); @@ -2183,7 +2263,8 @@ push_caller(const char *path, VALUE val, https://github.com/ruby/ruby/blob/trunk/dir.c#L2263 { struct push_glob_args *arg = (struct push_glob_args *)val; - return ruby_glob0(path, arg->flags, rb_glob_caller, (VALUE)&arg->glob, enc); + return ruby_glob0(path, arg->flags, &rb_glob_funcs, + (VALUE)&arg->glob, enc); } static int Index: test/ruby/test_dir.rb =================================================================== --- test/ruby/test_dir.rb (revision 58155) +++ test/ruby/test_dir.rb (revision 58156) @@ -184,6 +184,24 @@ class TestDir < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_dir.rb#L184 end end + if Process.const_defined?(:RLIMIT_NOFILE) + def test_glob_too_may_open_files + assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}", chdir: @root) + begin; + n = 16 + Process.setrlimit(Process::RLIMIT_NOFILE, n) + files = [] + begin + n.times {files << File.open('b')} + rescue Errno::EMFILE, Errno::ENFILE => e + end + assert_raise(e.class) { + Dir.glob('*') + } + end; + end + end + def assert_entries(entries) entries.sort! assert_equal(%w(. ..) + ("a".."z").to_a, entries) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/