$OpenBSD: patch-file_c,v 1.1 2019/08/30 15:58:26 jeremy Exp $

Backport use of realpath(3) for File.realpath to allow unveil(2) to work.

Index: file.c
--- file.c.orig
+++ file.c
@@ -132,6 +132,9 @@ int flock(int, int);
 #  define UTIME_EINVAL
 #endif
 
+#include <limits.h>
+#include <stdlib.h>
+
 VALUE rb_cFile;
 VALUE rb_mFileTest;
 VALUE rb_cStat;
@@ -4064,7 +4067,7 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const
 }
 
 static VALUE
-rb_check_realpath_internal(VALUE basedir, VALUE path, enum rb_realpath_mode mode)
+rb_check_realpath_emulate(VALUE basedir, VALUE path, enum rb_realpath_mode mode)
 {
     long prefixlen;
     VALUE resolved;
@@ -4158,6 +4161,76 @@ rb_check_realpath_internal(VALUE basedir, VALUE path, 
     return resolved;
 }
 
+static VALUE rb_file_join(VALUE ary);
+
+static VALUE
+rb_check_realpath_internal(VALUE basedir, VALUE path, enum rb_realpath_mode mode)
+{
+    VALUE unresolved_path;
+    rb_encoding *origenc;
+    char *resolved_ptr = NULL;
+    VALUE resolved;
+
+    if (mode == RB_REALPATH_DIR) {
+	return rb_check_realpath_emulate(basedir, path, mode);
+    }
+
+    unresolved_path = rb_str_dup_frozen(path);
+    origenc = rb_enc_get(unresolved_path);
+    if (*RSTRING_PTR(unresolved_path) != '/' && !NIL_P(basedir)) {
+	unresolved_path = rb_file_join(rb_ary_new_from_args(2, basedir, unresolved_path));
+    }
+    unresolved_path = TO_OSPATH(unresolved_path);
+
+    if((resolved_ptr = realpath(RSTRING_PTR(unresolved_path), NULL)) == NULL) {
+	/* glibc realpath(3) does not allow /path/to/file.rb/../other_file.rb,
+	   returning ENOTDIR in that case.
+	   glibc realpath(3) can also return ENOENT for paths that exist,
+	   such as /dev/fd/5.
+	   Fallback to the emulated approach in either of those cases. */
+	if (errno == ENOTDIR ||
+	    (errno == ENOENT && rb_file_exist_p(0, unresolved_path))) {
+	    return rb_check_realpath_emulate(basedir, path, mode);
+
+	}
+	if (mode == RB_REALPATH_CHECK) {
+	    return Qnil;
+	}
+	rb_sys_fail_path(unresolved_path);
+    }
+    resolved = ospath_new(resolved_ptr, strlen(resolved_ptr), rb_filesystem_encoding());
+    free(resolved_ptr);
+
+    if (mode == RB_REALPATH_STRICT || mode == RB_REALPATH_CHECK) {
+	struct stat st;
+
+	if (rb_stat(resolved, &st) < 0) {
+	    if (mode == RB_REALPATH_STRICT) {
+		rb_sys_fail_path(unresolved_path);
+	    }
+	    return Qnil;
+	}
+    }
+
+    if (origenc != rb_enc_get(resolved)) {
+	if (!rb_enc_str_asciionly_p(resolved)) {
+	    resolved = rb_str_conv_enc(resolved, NULL, origenc);
+	}
+	rb_enc_associate(resolved, origenc);
+    }
+
+    if(rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) {
+	rb_enc_associate(resolved, rb_filesystem_encoding());
+	if(rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) {
+	    rb_enc_associate(resolved, rb_ascii8bit_encoding());
+	}
+    }
+
+    rb_obj_taint(resolved);
+    RB_GC_GUARD(unresolved_path);
+    return resolved;
+}
+
 VALUE
 rb_realpath_internal(VALUE basedir, VALUE path, int strict)
 {
@@ -4579,8 +4652,6 @@ rb_file_s_split(VALUE klass, VALUE path)
     FilePathStringValue(path);		/* get rid of converting twice */
     return rb_assoc_new(rb_file_dirname(path), rb_file_s_basename(1,&path));
 }
-
-static VALUE rb_file_join(VALUE ary);
 
 static VALUE
 file_inspect_join(VALUE ary, VALUE arg, int recur)
