Create a gist now

Instantly share code, notes, and snippets.

@burke /total.diff Secret
Last active Mar 28, 2017

What would you like to do?
commit bdbb00871f63edd931b6d8bb6a2ea0061e9b1daa
Author: Burke Libbey <burke@libbey.me>
Date: Tue Mar 28 12:39:53 2017 -0400
defer load check
diff --git a/file.c b/file.c
index c57447a..2598574 100644
--- a/file.c
+++ b/file.c
@@ -5796,11 +5796,11 @@ rb_find_file_ext_safe(VALUE *filep, const char *const *ext, int safe_level)
VALUE
rb_find_file(VALUE path)
{
- return rb_find_file_safe(path, rb_safe_level());
+ return rb_find_file_safe(path, rb_safe_level(), 0);
}
VALUE
-rb_find_file_safe(VALUE path, int safe_level)
+rb_find_file_safe(VALUE path, int safe_level, int defer_load_check)
{
VALUE tmp, load_path;
const char *f = StringValueCStr(path);
@@ -5820,7 +5820,9 @@ rb_find_file_safe(VALUE path, int safe_level)
if (safe_level >= 1 && !fpath_check(path)) {
rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
}
- if (!rb_file_load_ok(f)) return 0;
+ if (!defer_load_check && !rb_file_load_ok(f)) {
+ return 0;
+ }
if (!expanded)
path = copy_path_class(file_expand_path_1(path), path);
return path;
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index aec2d52..c7a952e 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -478,7 +478,7 @@ VALUE rb_file_s_absolute_path(int, const VALUE *);
VALUE rb_file_absolute_path(VALUE, VALUE);
VALUE rb_file_dirname(VALUE fname);
int rb_find_file_ext_safe(VALUE*, const char* const*, int);
-VALUE rb_find_file_safe(VALUE, int);
+VALUE rb_find_file_safe(VALUE, int, int);
int rb_find_file_ext(VALUE*, const char* const*);
VALUE rb_find_file(VALUE);
VALUE rb_file_directory_p(VALUE,VALUE);
diff --git a/load.c b/load.c
index 70c7345..a65446a 100644
--- a/load.c
+++ b/load.c
@@ -859,7 +859,7 @@ search_required(VALUE fname, volatile VALUE *path, int safe_level)
if (loading) *path = rb_filesystem_str_new_cstr(loading);
return 'r';
}
- if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
+ if ((tmp = rb_find_file_safe(fname, safe_level, 1)) != 0) {
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
if (!rb_feature_p(ftptr, ext, TRUE, TRUE, &loading) || loading)
*path = tmp;
@@ -884,7 +884,7 @@ search_required(VALUE fname, volatile VALUE *path, int safe_level)
#else
rb_str_cat2(tmp, DLEXT);
OBJ_FREEZE(tmp);
- if ((tmp = rb_find_file_safe(tmp, safe_level)) != 0) {
+ if ((tmp = rb_find_file_safe(tmp, safe_level, 0)) != 0) {
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
*path = tmp;
@@ -897,7 +897,7 @@ search_required(VALUE fname, volatile VALUE *path, int safe_level)
if (loading) *path = rb_filesystem_str_new_cstr(loading);
return 's';
}
- if ((tmp = rb_find_file_safe(fname, safe_level)) != 0) {
+ if ((tmp = rb_find_file_safe(fname, safe_level, 0)) != 0) {
ext = strrchr(ftptr = RSTRING_PTR(tmp), '.');
if (!rb_feature_p(ftptr, ext, FALSE, TRUE, &loading) || loading)
*path = tmp;
diff --git a/ruby.c b/ruby.c
index c780937..317797b 100644
--- a/ruby.c
+++ b/ruby.c
@@ -1762,17 +1762,17 @@ load_file_internal(VALUE argp_v)
}
static VALUE
-open_load_file(VALUE fname_v, int *xflag)
+open_load_file(VALUE fname_v, int *xflag, int script)
{
const char *fname = StringValueCStr(fname_v);
VALUE f;
- int e;
+ int e = 0;
+ int fd = 0;
if (RSTRING_LEN(fname_v) == 1 && fname[0] == '-') {
f = rb_stdin;
}
else {
- int fd;
/* open(2) may block if fname is point to FIFO and it's empty. Let's
use O_NONBLOCK. */
#if defined O_NONBLOCK && HAVE_FCNTL && !(O_NONBLOCK & O_ACCMODE)
@@ -1795,7 +1795,8 @@ open_load_file(VALUE fname_v, int *xflag)
#endif
if ((fd = rb_cloexec_open(fname, mode, 0)) < 0) {
- rb_load_fail(fname_v, strerror(errno));
+ e = errno;
+ goto fail;
}
rb_update_max_fd(fd);
@@ -1803,8 +1804,7 @@ open_load_file(VALUE fname_v, int *xflag)
/* disabling O_NONBLOCK */
if (fcntl(fd, F_SETFL, 0) < 0) {
e = errno;
- (void)close(fd);
- rb_load_fail(fname_v, strerror(e));
+ goto fail;
}
#endif
@@ -1813,8 +1813,7 @@ open_load_file(VALUE fname_v, int *xflag)
struct stat st;
if (fstat(fd, &st) != 0) {
e = errno;
- (void)close(fd);
- rb_load_fail(fname_v, strerror(e));
+ goto fail;
}
if (S_ISFIFO(st.st_mode)) {
/*
@@ -1822,18 +1821,36 @@ open_load_file(VALUE fname_v, int *xflag)
rb_thread_wait_fd() release GVL. So, it's safe.
*/
rb_thread_wait_fd(fd);
+ } else if (S_ISDIR(st.st_mode)) {
+ e = EISDIR;
+ goto fail;
+ } else if (!S_ISREG(st.st_mode)) {
+ e = ENXIO;
+ goto fail;
}
}
-#endif
+#else
+ /* Note that we've replicated ruby_is_fd_loadable in S_ISFIFO without
+ * calling fstat64 twice. */
if (!ruby_is_fd_loadable(fd)) {
e = errno;
- (void)close(fd);
- rb_load_fail(fname_v, strerror(e));
+ goto fail;
}
+#endif
f = rb_io_fdopen(fd, mode, fname);
}
return f;
+fail:
+ if (fd > 0) (void)close(fd);
+ if (script) {
+ /* when we reach this from `$ ruby bad.rb`, show the reason */
+ rb_load_fail(fname_v, strerror(e));
+ } else {
+ /* when called from require/load/etc., just show:
+ * "cannot load such file", same as `load_failed`. */
+ rb_load_fail(fname_v, "cannot load such file");
+ }
}
static VALUE
@@ -1874,7 +1891,7 @@ load_file(VALUE parser, VALUE fname, int script, struct cmdline_options *opt)
arg.opt = opt;
arg.xflag = 0;
arg.lineno = rb_gv_get("$.");
- arg.f = open_load_file(rb_str_encode_ospath(fname), &arg.xflag);
+ arg.f = open_load_file(rb_str_encode_ospath(fname), &arg.xflag, script);
return (NODE *)rb_ensure(load_file_internal, (VALUE)&arg,
restore_load_file, (VALUE)&arg);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment