summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--benchmark/realpath.yml30
-rw-r--r--configure.ac1
-rw-r--r--file.c86
3 files changed, 114 insertions, 3 deletions
diff --git a/benchmark/realpath.yml b/benchmark/realpath.yml
new file mode 100644
index 0000000000..90a029d5b9
--- /dev/null
+++ b/benchmark/realpath.yml
@@ -0,0 +1,30 @@
+prelude: |
+ f = File
+ pwd = Dir.pwd
+ Dir.mkdir('b') unless f.directory?('b')
+ f.write('b/a', '') unless f.file?('b/a')
+
+ relative = 'b/a'
+ absolute = File.join(pwd, relative)
+ dir = 'b'
+ file = 'a'
+
+ relative_dir = 'b/c'
+ absolute_dir = File.join(pwd, relative_dir)
+ file_dir = 'c'
+benchmark:
+ relative_nil: "f.realpath(relative, nil)"
+ absolute_nil: "f.realpath(absolute, nil)"
+ relative_relative: "f.realpath(file, dir)"
+ absolute_relative: "f.realpath(absolute, dir)"
+ relative_absolute: "f.realpath(relative, pwd)"
+ relative_nil_dir: "f.realdirpath(relative_dir, nil)"
+ absolute_nil_dir: "f.realdirpath(absolute_dir, nil)"
+ relative_relative_dir: "f.realdirpath(file_dir, dir)"
+ absolute_relative_dir: "f.realdirpath(absolute_dir, dir)"
+ relative_absolute_dir: "f.realdirpath(relative_dir, pwd)"
+ relative_nil_notexist: "f.realpath(relative_dir, nil) rescue nil"
+ absolute_nil_notexist: "f.realpath(absolute_dir, nil) rescue nil"
+ relative_relative_notexist: "f.realpath(file_dir, dir) rescue nil"
+ absolute_relative_notexist: "f.realpath(absolute_dir, dir) rescue nil"
+ relative_absolute_notexist: "f.realpath(relative_dir, pwd) rescue nil"
diff --git a/configure.ac b/configure.ac
index a2f623e22a..ce2630dd9d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1827,6 +1827,7 @@ AC_CHECK_FUNCS(pwrite)
AC_CHECK_FUNCS(qsort_r)
AC_CHECK_FUNCS(qsort_s)
AC_CHECK_FUNCS(readlink)
+AC_CHECK_FUNCS(realpath)
AC_CHECK_FUNCS(round)
AC_CHECK_FUNCS(sched_getaffinity)
AC_CHECK_FUNCS(seekdir)
diff --git a/file.c b/file.c
index 0dd35161be..7574ede636 100644
--- a/file.c
+++ b/file.c
@@ -123,6 +123,12 @@ int flock(int, int);
#define rename(f, t) rb_w32_urename((f), (t))
#undef symlink
#define symlink(s, l) rb_w32_usymlink((s), (l))
+
+#ifdef HAVE_REALPATH
+/* Don't use native realpath(3) on Windows, as the check for
+ absolute paths does not work for drive letters. */
+#undef HAVE_REALPATH
+#endif
#else
#define STAT(p, s) stat((p), (s))
#endif
@@ -140,6 +146,11 @@ int flock(int, int);
# define UTIME_EINVAL
#endif
+#ifdef HAVE_REALPATH
+#include <limits.h>
+#include <stdlib.h>
+#endif
+
VALUE rb_cFile;
VALUE rb_mFileTest;
VALUE rb_cStat;
@@ -4198,7 +4209,7 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const char *unresolved, VALUE f
}
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;
@@ -4292,6 +4303,77 @@ rb_check_realpath_internal(VALUE basedir, VALUE path, enum rb_realpath_mode mode
return resolved;
}
+static VALUE rb_file_join(VALUE ary);
+
+static VALUE
+rb_check_realpath_internal(VALUE basedir, VALUE path, enum rb_realpath_mode mode)
+{
+#ifdef HAVE_REALPATH
+ VALUE unresolved_path;
+ rb_encoding *origenc;
+ char *resolved_ptr = NULL;
+ VALUE resolved;
+ struct stat st;
+
+ 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_assoc_new(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 (rb_stat(resolved, &st) < 0) {
+ if (mode == RB_REALPATH_CHECK) {
+ return Qnil;
+ }
+ rb_sys_fail_path(unresolved_path);
+ }
+
+ 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;
+#else
+ return rb_check_realpath_emulate(basedir, path, mode);
+#endif /* HAVE_REALPATH */
+}
+
VALUE
rb_realpath_internal(VALUE basedir, VALUE path, int strict)
{
@@ -4713,8 +4795,6 @@ rb_file_s_split(VALUE klass, VALUE path)
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)
{