aboutsummaryrefslogtreecommitdiffstats
path: root/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'file.c')
-rw-r--r--file.c86
1 files changed, 83 insertions, 3 deletions
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)
{