Skip to content

Commit

Permalink
Make filepaths with null byte fail
Browse files Browse the repository at this point in the history
This patch applies to several methods that deal with filepaths in Dir,
File, File::Stat, IO, and Kernel. All of these methods now raise an
argument error if a null byte is detected in a path argument.

Some of these methods are also slightly refactored, mostly by replacing
context.runtime with just runtime where applicable.

Conflicts:
	core/src/main/java/org/jruby/RubyDir.java
	core/src/main/java/org/jruby/RubyFile.java
	core/src/main/java/org/jruby/RubyFileStat.java
	core/src/main/java/org/jruby/RubyIO.java
	core/src/main/java/org/jruby/RubyKernel.java
	test/mri/excludes/TestFile.rb
lumeet authored and mkristian committed Apr 13, 2015
1 parent f959d12 commit f41b6d9
Showing 8 changed files with 186 additions and 57 deletions.
34 changes: 23 additions & 11 deletions core/src/main/java/org/jruby/RubyDir.java
Original file line number Diff line number Diff line change
@@ -64,6 +64,7 @@
import org.jruby.util.JRubyFile;
import org.jruby.util.ByteList;
import static org.jruby.CompatVersion.*;
import org.jruby.util.StringSupport;

/**
* .The Ruby built-in class Dir.
@@ -134,11 +135,12 @@ public IRubyObject initialize(IRubyObject arg) {
*/
@JRubyMethod(compat = RUBY1_8, visibility = Visibility.PRIVATE)
public IRubyObject initialize(ThreadContext context, IRubyObject arg) {
RubyString newPath = arg.convertToString();
Ruby runtime = context.runtime;
RubyString newPath = StringSupport.checkEmbeddedNulls(runtime, RubyFile.get_path(context, arg.convertToString()));
path = newPath;
pos = 0;

String adjustedPath = RubyFile.adjustRootPathOnWindows(context.runtime, newPath.toString(), null);
String adjustedPath = RubyFile.adjustRootPathOnWindows(runtime, newPath.toString(), null);
checkDirIsTwoSlashesOnWindows(getRuntime(), adjustedPath);

dir = JRubyFile.createResource(context, adjustedPath);
@@ -269,13 +271,15 @@ public static RubyArray entries(ThreadContext context, IRubyObject recv, IRubyOb

@JRubyMethod(name = "entries", meta = true, compat = RUBY1_9)
public static RubyArray entries19(ThreadContext context, IRubyObject recv, IRubyObject arg) {
return entriesCommon(context, RubyFile.get_path(context, arg).asJavaString());
RubyString path = StringSupport.checkEmbeddedNulls(context.runtime, RubyFile.get_path(context, arg));
return entriesCommon(context, path.asJavaString());
}

@JRubyMethod(name = "entries", meta = true, compat = RUBY1_9)
public static RubyArray entries19(ThreadContext context, IRubyObject recv, IRubyObject arg, IRubyObject opts) {
RubyString path = StringSupport.checkEmbeddedNulls(context.runtime, RubyFile.get_path(context, arg));
// FIXME: do something with opts
return entriesCommon(context, RubyFile.get_path(context, arg).asJavaString());
return entriesCommon(context, path.asJavaString());
}

private static RubyArray entriesCommon(ThreadContext context, String path) {
@@ -310,7 +314,8 @@ private static void checkDirIsTwoSlashesOnWindows(Ruby runtime, String path) {
public static IRubyObject chdir(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
Ruby runtime = context.runtime;
RubyString path = args.length == 1 ?
RubyFile.get_path(context, args[0]) : getHomeDirectoryPath(context);
StringSupport.checkEmbeddedNulls(runtime, RubyFile.get_path(context, args[0])) :
getHomeDirectoryPath(context);
String adjustedPath = RubyFile.adjustRootPathOnWindows(runtime, path.asJavaString(), null);
checkDirIsTwoSlashesOnWindows(runtime, adjustedPath);
String realPath = null;
@@ -370,7 +375,9 @@ public static IRubyObject rmdir(IRubyObject recv, IRubyObject path) {

@JRubyMethod(name = {"rmdir", "unlink", "delete"}, required = 1, meta = true, compat = RUBY1_9)
public static IRubyObject rmdir19(ThreadContext context, IRubyObject recv, IRubyObject path) {
return rmdirCommon(context.runtime, RubyFile.get_path(context, path).asJavaString());
Ruby runtime = context.runtime;
RubyString cleanPath = StringSupport.checkEmbeddedNulls(runtime, RubyFile.get_path(context, path));
return rmdirCommon(runtime, cleanPath.asJavaString());
}

private static IRubyObject rmdirCommon(Ruby runtime, String path) {
@@ -449,7 +456,9 @@ public static IRubyObject mkdir(IRubyObject recv, IRubyObject[] args) {

@JRubyMethod(name = "mkdir", required = 1, optional = 1, meta = true, compat = RUBY1_9)
public static IRubyObject mkdir19(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
return mkdirCommon(context.runtime, RubyFile.get_path(context, args[0]).asJavaString(), args);
Ruby runtime = context.runtime;
RubyString path = StringSupport.checkEmbeddedNulls(runtime, RubyFile.get_path(context, args[0]));
return mkdirCommon(runtime, path.asJavaString(), args);
}

private static IRubyObject mkdirCommon(Ruby runtime, String path, IRubyObject[] args) {
@@ -612,14 +621,17 @@ public IRubyObject rewind() {

@JRubyMethod(name = {"exists?", "exist?"}, meta = true, compat = RUBY1_9)
public static IRubyObject exist(ThreadContext context, IRubyObject recv, IRubyObject arg) {
Ruby runtime = context.runtime;
// Capture previous exception if any.
IRubyObject exception = context.runtime.getGlobalVariables().get("$!");
IRubyObject exception = runtime.getGlobalVariables().get("$!");
RubyString path = StringSupport.checkEmbeddedNulls(runtime, RubyFile.get_path(context, arg));

try {
return context.runtime.newFileStat(RubyFile.get_path(context, arg).asJavaString(), false).directory_p();
return runtime.newFileStat(path.asJavaString(), false).directory_p();
} catch (Exception e) {
// Restore $!
context.runtime.getGlobalVariables().set("$!", exception);
return context.runtime.newBoolean(false);
runtime.getGlobalVariables().set("$!", exception);
return runtime.newBoolean(false);
}
}

79 changes: 48 additions & 31 deletions core/src/main/java/org/jruby/RubyFile.java
Original file line number Diff line number Diff line change
@@ -75,6 +75,7 @@
import org.jruby.util.FileResource;
import org.jruby.util.JRubyFile;
import org.jruby.util.ResourceException;
import org.jruby.util.StringSupport;
import org.jruby.util.TypeConverter;
import org.jruby.util.encoding.Transcoder;
import org.jruby.util.io.BadDescriptorException;
@@ -451,7 +452,7 @@ public IRubyObject mtime(ThreadContext context) {

@JRubyMethod(meta = true, compat = RUBY1_9)
public static IRubyObject path(ThreadContext context, IRubyObject self, IRubyObject str) {
return get_path(context, str);
return StringSupport.checkEmbeddedNulls(context.runtime, get_path(context, str));
}

@JRubyMethod(name = {"path", "to_path"})
@@ -511,7 +512,7 @@ public IRubyObject inspect() {
public static IRubyObject basename(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
Ruby runtime = context.runtime;

RubyString origString = get_path(context,args[0]);
RubyString origString = StringSupport.checkEmbeddedNulls(runtime, get_path(context, args[0]));
Encoding origEncoding = origString.getEncoding();
String name = origString.toString();

@@ -652,11 +653,12 @@ public static IRubyObject chown(ThreadContext context, IRubyObject recv, IRubyOb

@JRubyMethod(required = 1, meta = true)
public static IRubyObject dirname(ThreadContext context, IRubyObject recv, IRubyObject arg) {
RubyString filename = get_path(context, arg);
Ruby runtime = context.runtime;
RubyString filename = StringSupport.checkEmbeddedNulls(runtime, get_path(context, arg));

String jfilename = filename.asJavaString();

return context.runtime.newString(dirname(context, jfilename)).infectBy(filename);
return runtime.newString(dirname(context, jfilename)).infectBy(filename);
}

public static String dirname(ThreadContext context, String jfilename) {
@@ -828,20 +830,23 @@ public static IRubyObject realpath(ThreadContext context, IRubyObject recv, IRub
*/
@JRubyMethod(name = {"fnmatch", "fnmatch?"}, required = 2, optional = 1, meta = true)
public static IRubyObject fnmatch(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
Ruby runtime = context.runtime;
int flags = args.length == 3 ? RubyNumeric.num2int(args[2]) : 0;

ByteList pattern = args[0].convertToString().getByteList();
ByteList path = get_path(context, args[1]).getByteList();
ByteList path = StringSupport.checkEmbeddedNulls(runtime, get_path(context, args[1])).getByteList();

if (org.jruby.util.Dir.fnmatch(pattern.getUnsafeBytes(), pattern.getBegin(), pattern.getBegin()+pattern.getRealSize(), path.getUnsafeBytes(), path.getBegin(), path.getBegin()+path.getRealSize(), flags) == 0) {
return context.runtime.getTrue();
}
return context.runtime.getFalse();
return runtime.getFalse();
}

@JRubyMethod(name = "ftype", required = 1, meta = true)
public static IRubyObject ftype(ThreadContext context, IRubyObject recv, IRubyObject filename) {
return context.runtime.newFileStat(get_path(context, filename).getUnicodeValue(), true).ftype();
Ruby runtime = context.runtime;
RubyString path = StringSupport.checkEmbeddedNulls(runtime, get_path(context, filename));
return runtime.newFileStat(path.getUnicodeValue(), true).ftype();
}

/*
@@ -855,26 +860,30 @@ public static RubyString join(ThreadContext context, IRubyObject recv, IRubyObje

@JRubyMethod(name = "lstat", required = 1, meta = true)
public static IRubyObject lstat(ThreadContext context, IRubyObject recv, IRubyObject filename) {
String f = get_path(context, filename).getUnicodeValue();
return context.runtime.newFileStat(f, true);
Ruby runtime = context.runtime;
String f = StringSupport.checkEmbeddedNulls(runtime, get_path(context, filename)).getUnicodeValue();
return runtime.newFileStat(f, true);
}

@JRubyMethod(name = "stat", required = 1, meta = true)
public static IRubyObject stat(ThreadContext context, IRubyObject recv, IRubyObject filename) {
String f = get_path(context, filename).getUnicodeValue();
return context.runtime.newFileStat(f, false);
Ruby runtime = context.runtime;
String f = StringSupport.checkEmbeddedNulls(runtime, get_path(context, filename)).getUnicodeValue();
return runtime.newFileStat(f, false);
}

@JRubyMethod(name = "atime", required = 1, meta = true)
public static IRubyObject atime(ThreadContext context, IRubyObject recv, IRubyObject filename) {
String f = get_path(context, filename).getUnicodeValue();
return context.runtime.newFileStat(f, false).atime();
Ruby runtime = context.runtime;
String f = StringSupport.checkEmbeddedNulls(runtime, get_path(context, filename)).getUnicodeValue();
return runtime.newFileStat(f, false).atime();
}

@JRubyMethod(name = "ctime", required = 1, meta = true)
public static IRubyObject ctime(ThreadContext context, IRubyObject recv, IRubyObject filename) {
String f = get_path(context, filename).getUnicodeValue();
return context.runtime.newFileStat(f, false).ctime();
Ruby runtime = context.runtime;
String f = StringSupport.checkEmbeddedNulls(runtime, get_path(context, filename)).getUnicodeValue();
return runtime.newFileStat(f, false).ctime();
}

@JRubyMethod(required = 1, rest = true, meta = true)
@@ -938,14 +947,16 @@ public static IRubyObject link(ThreadContext context, IRubyObject recv, IRubyObj

@JRubyMethod(name = "mtime", required = 1, meta = true)
public static IRubyObject mtime(ThreadContext context, IRubyObject recv, IRubyObject filename) {
return context.runtime.newFileStat(get_path(context, filename).getUnicodeValue(), false).mtime();
Ruby runtime = context.runtime;
String f = StringSupport.checkEmbeddedNulls(runtime, get_path(context, filename)).getUnicodeValue();
return runtime.newFileStat(f, false).mtime();
}

@JRubyMethod(required = 2, meta = true)
public static IRubyObject rename(ThreadContext context, IRubyObject recv, IRubyObject oldName, IRubyObject newName) {
Ruby runtime = context.runtime;
RubyString oldNameString = get_path(context, oldName);
RubyString newNameString = get_path(context, newName);
RubyString oldNameString = StringSupport.checkEmbeddedNulls(runtime, get_path(context, oldName));
RubyString newNameString = StringSupport.checkEmbeddedNulls(runtime, get_path(context, newName));

String newNameJavaString = newNameString.getUnicodeValue();
String oldNameJavaString = oldNameString.getUnicodeValue();
@@ -978,17 +989,18 @@ public static IRubyObject rename(ThreadContext context, IRubyObject recv, IRubyO

@JRubyMethod(required = 1, meta = true)
public static RubyArray split(ThreadContext context, IRubyObject recv, IRubyObject arg) {
RubyString filename = get_path(context, arg);
Ruby runtime = context.runtime;
RubyString filename = StringSupport.checkEmbeddedNulls(runtime, get_path(context, arg));

return context.runtime.newArray(dirname(context, recv, filename),
return runtime.newArray(dirname(context, recv, filename),
basename(context, recv, new IRubyObject[]{filename}));
}

@JRubyMethod(required = 2, meta = true)
public static IRubyObject symlink(ThreadContext context, IRubyObject recv, IRubyObject from, IRubyObject to) {
Ruby runtime = context.runtime;
RubyString fromStr = get_path(context, from);
RubyString toStr = get_path(context, to);
RubyString fromStr = StringSupport.checkEmbeddedNulls(runtime, get_path(context, from));
RubyString toStr = StringSupport.checkEmbeddedNulls(runtime, get_path(context, to));
String tovalue = toStr.getUnicodeValue();
tovalue = JRubyFile.create(runtime.getCurrentDirectory(), tovalue).getAbsolutePath();
try {
@@ -1032,7 +1044,8 @@ public static IRubyObject truncate(ThreadContext context, IRubyObject recv, IRub

@JRubyMethod(name = "truncate", required = 2, meta = true, compat = RUBY1_9)
public static IRubyObject truncate19(ThreadContext context, IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
return truncateCommon(context, recv, get_path(context, arg1), arg2);
RubyString path = StringSupport.checkEmbeddedNulls(context.runtime, get_path(context, arg1));
return truncateCommon(context, recv, path, arg2);
}

@JRubyMethod(meta = true, optional = 1)
@@ -1066,7 +1079,7 @@ public static IRubyObject utime(ThreadContext context, IRubyObject recv, IRubyOb
}

for (int i = 2, j = args.length; i < j; i++) {
RubyString filename = get_path(context, args[i]);
RubyString filename = StringSupport.checkEmbeddedNulls(runtime, get_path(context, args[i]));

JRubyFile fileToTouch = JRubyFile.create(runtime.getCurrentDirectory(),filename.getUnicodeValue());

@@ -1088,7 +1101,7 @@ public static IRubyObject unlink(ThreadContext context, IRubyObject recv, IRubyO
Ruby runtime = context.runtime;

for (int i = 0; i < args.length; i++) {
RubyString filename = get_path(context, args[i]);
RubyString filename = StringSupport.checkEmbeddedNulls(runtime, get_path(context, args[i]));
JRubyFile lToDelete = JRubyFile.create(runtime.getCurrentDirectory(), filename.getUnicodeValue());

boolean isSymlink = RubyFileTest.symlink_p(recv, filename).isTrue();
@@ -1149,7 +1162,7 @@ public void setEncoding(Encoding encoding) {
// mri: rb_open_file + rb_scan_open_args
private IRubyObject openFile19(ThreadContext context, IRubyObject args[]) {
Ruby runtime = context.runtime;
RubyString filename = get_path(context, args[0]);
RubyString filename = StringSupport.checkEmbeddedNulls(runtime, get_path(context, args[0]));

path = adjustRootPathOnWindows(runtime, filename.asJavaString(), runtime.getCurrentDirectory());

@@ -1393,7 +1406,8 @@ public static FileResource fileResource(ThreadContext context, IRubyObject pathO

}

return JRubyFile.createResource(runtime, get_path(context, pathOrFile).toString());
RubyString path = StringSupport.checkEmbeddedNulls(runtime, get_path(context, pathOrFile));
return JRubyFile.createResource(runtime, path.toString());
}
/**
* Get the fully-qualified JRubyFile object for the path, taking into
@@ -1408,7 +1422,9 @@ public static FileResource fileResource(IRubyObject pathOrFile) {
return JRubyFile.createResource(runtime, ((RubyIO) pathOrFile).openFile.getPath());
}

return JRubyFile.createResource(runtime, get_path(runtime.getCurrentContext(), pathOrFile).toString());
ThreadContext context = runtime.getCurrentContext();
RubyString path = StringSupport.checkEmbeddedNulls(runtime, get_path(context, pathOrFile));
return JRubyFile.createResource(runtime, path.toString());
}

@Deprecated // Use fileResource instead
@@ -1579,7 +1595,8 @@ private static boolean isWindowsDriveLetter(char c) {
private static IRubyObject expandPathInternal(ThreadContext context, IRubyObject recv, IRubyObject[] args, boolean expandUser, boolean canonicalize) {
Ruby runtime = context.runtime;

String relativePath = get_path(context, args[0]).getUnicodeValue();
RubyString origPath = StringSupport.checkEmbeddedNulls(runtime, get_path(context, args[0]));
String relativePath = origPath.getUnicodeValue();

// Special /dev/null of windows
if (Platform.IS_WINDOWS && ("NUL:".equalsIgnoreCase(relativePath) || "NUL".equalsIgnoreCase(relativePath))) {
@@ -1617,7 +1634,7 @@ private static IRubyObject expandPathInternal(ThreadContext context, IRubyObject
if ((args[1] instanceof RubyString) && args[1].asJavaString().startsWith("uri:")) {
cwd = args[1].asJavaString();
} else {
cwd = get_path(context, args[1]).getUnicodeValue();
cwd = StringSupport.checkEmbeddedNulls(runtime, get_path(context, args[1])).getUnicodeValue();

// Handle ~user paths.
if (expandUser) {
@@ -1941,7 +1958,7 @@ private static RubyString join(ThreadContext context, IRubyObject recv, RubyArra
element = inspectJoin(context, recv, ary, ((RubyArray)args[i]));
}
} else {
RubyString path = get_path(context, args[i]);
RubyString path = StringSupport.checkEmbeddedNulls(runtime, get_path(context, args[i]));
element = path.getUnicodeValue();
}

4 changes: 4 additions & 0 deletions core/src/main/java/org/jruby/RubyFileStat.java
Original file line number Diff line number Diff line change
@@ -49,6 +49,7 @@
import org.jruby.util.FileResource;
import org.jruby.util.JRubyFile;
import org.jruby.util.JRubyNonExistentFile;
import org.jruby.util.StringSupport;

/**
* Implements File::Stat
@@ -126,6 +127,9 @@ private void setup(String filename, boolean lstat) {

@JRubyMethod(name = "initialize", required = 1, visibility = Visibility.PRIVATE, compat = CompatVersion.RUBY1_8)
public IRubyObject initialize(IRubyObject fname, Block unusedBlock) {
Ruby runtime = getRuntime();
ThreadContext context = runtime.getCurrentContext();
RubyString path = StringSupport.checkEmbeddedNulls(runtime, RubyFile.get_path(context, fname));
setup(fname.convertToString().toString(), false);

return this;
12 changes: 7 additions & 5 deletions core/src/main/java/org/jruby/RubyIO.java
Original file line number Diff line number Diff line change
@@ -328,6 +328,8 @@ protected void reopenPath(Ruby runtime, IRubyObject[] args) {
pathString = args[0].convertToString();
}

pathString = StringSupport.checkEmbeddedNulls(runtime, pathString);

// TODO: check safe, taint on incoming string

try {
@@ -3795,7 +3797,7 @@ private static IRubyObject openKeyArgs(ThreadContext context, IRubyObject recv,
Ruby runtime = context.runtime;
IRubyObject path, v;

path = RubyFile.get_path(context, argv[0]);
path = StringSupport.checkEmbeddedNulls(runtime, RubyFile.get_path(context, argv[0]));
failIfDirectory(runtime, (RubyString)path); // only in JRuby
// MRI increments args past 0 now, so remaining uses of args only see non-path args

@@ -3867,8 +3869,8 @@ public static IRubyObject checkPipeCommand(ThreadContext context, IRubyObject fi
* open_args: array of string
*/
private static IRubyObject write19(ThreadContext context, IRubyObject recv, IRubyObject path, IRubyObject str, IRubyObject offset, RubyHash options) {
RubyString pathStr = RubyFile.get_path(context, path);
Ruby runtime = context.runtime;
RubyString pathStr = StringSupport.checkEmbeddedNulls(runtime, RubyFile.get_path(context, path));
failIfDirectory(runtime, pathStr);

RubyIO file = null;
@@ -3909,11 +3911,11 @@ private static IRubyObject write19(ThreadContext context, IRubyObject recv, IRub
*/
@JRubyMethod(meta = true, required = 1, optional = 2, compat = RUBY1_9)
public static IRubyObject binread(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
IRubyObject nil = context.runtime.getNil();
IRubyObject path = RubyFile.get_path(context, args[0]);
Ruby runtime = context.runtime;
IRubyObject nil = runtime.getNil();
IRubyObject path = StringSupport.checkEmbeddedNulls(runtime, RubyFile.get_path(context, args[0]));
IRubyObject length = nil;
IRubyObject offset = nil;
Ruby runtime = context.runtime;

if (args.length > 2) {
offset = args[2];
22 changes: 13 additions & 9 deletions core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
@@ -66,6 +66,7 @@
import org.jruby.util.ConvertBytes;
import org.jruby.util.IdUtil;
import org.jruby.util.ShellLauncher;
import org.jruby.util.StringSupport;
import org.jruby.util.TypeConverter;
import org.jruby.util.cli.Options;

@@ -193,20 +194,20 @@ public static IRubyObject autoload_p(ThreadContext context, final IRubyObject re
public static IRubyObject autoload(final IRubyObject recv, IRubyObject symbol, IRubyObject file) {
Ruby runtime = recv.getRuntime();
String nonInternedName = symbol.asJavaString();

if (!IdUtil.isValidConstantName(nonInternedName)) {
throw runtime.newNameError("autoload must be constant name", nonInternedName);
}

final RubyString fileString;
RubyString fileString_;
if (runtime.is1_9()) {
fileString = RubyFile.get_path(runtime.getCurrentContext(), file);
fileString_ = RubyFile.get_path(runtime.getCurrentContext(), file);
} else if (!(file instanceof RubyString)) {
throw runtime.newTypeError(file, runtime.getString());
} else {
fileString = (RubyString) file;
fileString_ = (RubyString) file;
}
final RubyString fileString = StringSupport.checkEmbeddedNulls(runtime, fileString_);

if (!IdUtil.isValidConstantName(nonInternedName)) {
throw runtime.newNameError("autoload must be constant name", nonInternedName);
}
if (fileString.isEmpty()) throw runtime.newArgumentError("empty file name");

final String baseName = symbol.asJavaString().intern(); // interned, OK for "fast" methods
@@ -1074,7 +1075,8 @@ public static IRubyObject require19(ThreadContext context, IRubyObject recv, IRu
}

private static IRubyObject requireCommon(Ruby runtime, IRubyObject recv, IRubyObject name, Block block) {
return runtime.newBoolean(runtime.getLoadService().require(name.convertToString().toString()));
RubyString path = StringSupport.checkEmbeddedNulls(runtime, name);
return runtime.newBoolean(runtime.getLoadService().require(path.toString()));
}

@JRubyMethod(required = 1, optional = 1, module = true, visibility = PRIVATE, compat = RUBY1_8)
@@ -1084,7 +1086,9 @@ public static IRubyObject load(IRubyObject recv, IRubyObject[] args, Block block

@JRubyMethod(name = "load", required = 1, optional = 1, module = true, visibility = PRIVATE, compat = RUBY1_9)
public static IRubyObject load19(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
return loadCommon(RubyFile.get_path(context, args[0]), context.runtime, args, block);
Ruby runtime = context.runtime;
RubyString path = StringSupport.checkEmbeddedNulls(runtime, RubyFile.get_path(context, args[0]));
return loadCommon(path, runtime, args, block);
}

private static IRubyObject loadCommon(IRubyObject fileName, Ruby runtime, IRubyObject[] args, Block block) {
15 changes: 15 additions & 0 deletions core/src/main/java/org/jruby/RubyString.java
Original file line number Diff line number Diff line change
@@ -684,6 +684,21 @@ public static RubyString newStringNoCopy(Ruby runtime, byte[] bytes) {
return newStringNoCopy(runtime, new ByteList(bytes, false));
}

// str_independent
public boolean independent() {
return shareLevel == SHARE_LEVEL_NONE;
}

// str_make_independent, modified to create a new String rather than possibly modifying a frozen one
public RubyString makeIndependent() {
RubyClass klass = metaClass;
RubyString str = strDup(klass.getClassRuntime(), klass);
str.modify();
str.setFrozen(true);
str.infectBy(this);
return str;
}

/** Encoding aware String construction routines for 1.9
*
*/
76 changes: 76 additions & 0 deletions core/src/main/java/org/jruby/util/StringSupport.java
Original file line number Diff line number Diff line change
@@ -37,6 +37,8 @@

import sun.misc.Unsafe;

import java.util.Arrays;

public final class StringSupport {
public static final int CR_MASK = RubyObject.USER0_F | RubyObject.USER1_F;
public static final int CR_UNKNOWN = 0;
@@ -564,4 +566,78 @@ public static int bytesToFixBrokenTrailingCharacter(byte[] bytes, int begin, int
return 0;
}

public static int memchr(byte[] ptr, int start, int find, int len) {
for (int i = start; i < start + len; i++) {
if (ptr[i] == find) return i;
}
return -1;
}

// StringValueCstr, rb_string_value_cstr without trailing null addition
public static RubyString checkEmbeddedNulls(Ruby runtime, IRubyObject ptr) {
RubyString str = ptr.convertToString();
ByteList strByteList = str.getByteList();
byte[] sBytes = strByteList.unsafeBytes();
int s = strByteList.begin();
int len = strByteList.length();
Encoding enc = str.getEncoding();
final int minlen = enc.minLength();

if (minlen > 1) {
if (strNullChar(sBytes, s, len, minlen, enc) != -1) {
throw runtime.newArgumentError("string contains null char");
}
return strFillTerm(str, sBytes, s, len, minlen, minlen);
}
if (memchr(sBytes, s, 0, len) != -1) {
throw runtime.newArgumentError("string contains null byte");
}
// if (s[len]) {
// rb_str_modify(str);
// s = RSTRING_PTR(str);
// s[RSTRING_LEN(str)] = 0;
// }
return str;
}

// MRI: str_null_char
public static int strNullChar(byte[] sBytes, int s, int len, final int minlen, Encoding enc) {
int e = s + len;

for (; s + minlen <= e; s += enc.length(sBytes, s, e)) {
if (zeroFilled(sBytes, s, minlen)) return s;
}
return -1;
}

public static boolean zeroFilled(byte[] sBytes, int s, int n) {
for (; n > 0; --n) {
if (sBytes[s++] != 0) return false;
}
return true;
}

public static RubyString strFillTerm(RubyString str, byte[] sBytes, int s, int len, int oldtermlen, int termlen) {
int capa = str.getByteList().getUnsafeBytes().length - str.getByteList().begin();

if (capa < len + termlen) {
str.modify(len + termlen);
}
else if (!str.independent()) {
if (zeroFilled(sBytes, s + len, termlen)) return str;
str.makeIndependent();
}
sBytes = str.getByteList().getUnsafeBytes();
s = str.getByteList().begin();
TERM_FILL(sBytes, s + len, termlen);
return str;
}

public static void TERM_FILL(byte[] ptrBytes, int ptr, int termlen) {
int term_fill_ptr = ptr;
int term_fill_len = termlen;
ptrBytes[term_fill_ptr] = '\0';
if (term_fill_len > 1)
Arrays.fill(ptrBytes, term_fill_ptr, term_fill_len, (byte)0);
}
}
1 change: 0 additions & 1 deletion test/externals/ruby1.9/excludes/TestFile.rb
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@
exclude :test_getc_extended_file , "needs investigation"
exclude :test_gets_extended_file , "needs investigation"
exclude :test_gets_para_extended_file , "needs investigation"
exclude :test_open_nul, "needs investigation"
exclude :test_read_all_extended_file , "needs investigation"
exclude :test_realdirpath , "needs investigation"
exclude :test_realpath , "needs investigation"

0 comments on commit f41b6d9

Please sign in to comment.