Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Refactor channeldescriptor to use FileResource #1587

Closed
wants to merge 3 commits into from

2 participants

@ratnikov

This is part of the #1452 effort.

This PR is a bit more invasive, since it introduces the concept of RaisableException and removes some of the more specific exceptions in org.jruby.util.io package.

In future, ChannelDescriptor.open method can probably be abolished altogether by introducing a NullResource (which will cover /dev/nul, nul:) and ClassPathResource (which at its first iteration would support just opening a descriptor, but eventually should be able to also support list[] and other File related methods).

@enebo
Owner

Talked to @ratnikov about making this slightly more backwards compat for 1.7 branch and is just closing because I keep clicking on this thinking it has been updated...just re-open or submit a new one when it is ready.

@enebo enebo closed this
@enebo enebo added this to the JRuby 1.7.12 milestone
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 270 additions and 352 deletions.
  1. +6 −30 core/src/main/java/org/jruby/RubyFile.java
  2. +5 −16 core/src/main/java/org/jruby/RubyIO.java
  3. +7 −0 core/src/main/java/org/jruby/exceptions/RaisableException.java
  4. +8 −1 core/src/main/java/org/jruby/util/EmptyFileResource.java
  5. +5 −0 core/src/main/java/org/jruby/util/FileResource.java
  6. +14 −0 core/src/main/java/org/jruby/util/JarDirectoryResource.java
  7. +17 −2 core/src/main/java/org/jruby/util/JarFileResource.java
  8. +14 −4 core/src/main/java/org/jruby/util/JarResource.java
  9. +128 −1 core/src/main/java/org/jruby/util/RegularFileResource.java
  10. +36 −0 core/src/main/java/org/jruby/util/ResourceException.java
  11. +1 −1  core/src/main/java/org/jruby/util/io/CRLFStreamWrapper.java
  12. +12 −151 core/src/main/java/org/jruby/util/io/ChannelDescriptor.java
  13. +13 −6 core/src/main/java/org/jruby/util/io/ChannelStream.java
  14. +0 −34 core/src/main/java/org/jruby/util/io/DirectoryAsFileException.java
  15. +0 −35 core/src/main/java/org/jruby/util/io/FileExistsException.java
  16. +0 −65 core/src/main/java/org/jruby/util/io/PermissionDeniedException.java
  17. +1 −1  core/src/main/java/org/jruby/util/io/Stream.java
  18. +3 −5 ext/openssl/src/main/java/org/jruby/ext/openssl/x509store/Lookup.java
View
36 core/src/main/java/org/jruby/RubyFile.java
@@ -35,6 +35,9 @@
***** END LICENSE BLOCK *****/
package org.jruby;
+import static org.jruby.CompatVersion.*;
+import static org.jruby.runtime.Visibility.*;
+
import jnr.constants.platform.OpenFlags;
import org.jcodings.Encoding;
import org.jruby.util.io.ModeFlags;
@@ -70,23 +73,19 @@
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
-import static org.jruby.runtime.Visibility.*;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.encoding.EncodingCapable;
import org.jruby.util.ByteList;
import org.jruby.util.FileResource;
import org.jruby.util.JRubyFile;
import org.jruby.util.TypeConverter;
-import org.jruby.util.io.DirectoryAsFileException;
-import org.jruby.util.io.PermissionDeniedException;
import org.jruby.util.io.Stream;
import org.jruby.util.io.ChannelStream;
import org.jruby.util.io.IOOptions;
import org.jruby.util.io.BadDescriptorException;
-import org.jruby.util.io.FileExistsException;
import org.jruby.util.io.InvalidValueException;
import org.jruby.util.io.PipeException;
-import static org.jruby.CompatVersion.*;
+import org.jruby.exceptions.RaisableException;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.encoding.EncodingService;
@@ -1299,23 +1298,8 @@ private ChannelDescriptor sysopen(String path, ModeFlags modes, int perm) {
// TODO: check if too many open files, GC and try again
return descriptor;
- } catch (PermissionDeniedException pde) {
- // PDException can be thrown only when creating the file and
- // permission is denied. See JavaDoc of PermissionDeniedException.
- throw getRuntime().newErrnoEACCESError(path);
- } catch (FileNotFoundException fnfe) {
- // FNFException can be thrown in both cases, when the file
- // is not found, or when permission is denied.
- if (Ruby.isSecurityRestricted() || new File(path).exists()) {
- throw getRuntime().newErrnoEACCESError(path);
- }
- throw getRuntime().newErrnoENOENTError(path);
- } catch (DirectoryAsFileException dafe) {
- throw getRuntime().newErrnoEISDirError();
- } catch (FileExistsException fee) {
- throw getRuntime().newErrnoEEXISTError(path);
- } catch (IOException ioe) {
- throw getRuntime().newIOErrorFromException(ioe);
+ } catch (RaisableException raisable) {
+ throw raisable.newRaiseException(getRuntime());
}
}
@@ -1327,10 +1311,6 @@ private Stream fopen(String path, ModeFlags flags) {
flags);
} catch (BadDescriptorException e) {
throw getRuntime().newErrnoEBADFError();
- } catch (PermissionDeniedException pde) {
- // PDException can be thrown only when creating the file and
- // permission is denied. See JavaDoc of PermissionDeniedException.
- throw getRuntime().newErrnoEACCESError(path);
} catch (FileNotFoundException ex) {
// FNFException can be thrown in both cases, when the file
// is not found, or when permission is denied.
@@ -1343,10 +1323,6 @@ private Stream fopen(String path, ModeFlags flags) {
}
throw getRuntime().newErrnoENOENTError(path);
- } catch (DirectoryAsFileException ex) {
- throw getRuntime().newErrnoEISDirError();
- } catch (FileExistsException ex) {
- throw getRuntime().newErrnoEEXISTError(path);
} catch (IOException ex) {
throw getRuntime().newIOErrorFromException(ex);
} catch (InvalidValueException ex) {
View
21 core/src/main/java/org/jruby/RubyIO.java
@@ -68,6 +68,7 @@
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyClass;
import org.jruby.common.IRubyWarnings.ID;
+import org.jruby.exceptions.RaisableException;
import org.jruby.exceptions.RaiseException;
import org.jruby.ext.fcntl.FcntlLibrary;
import org.jruby.platform.Platform;
@@ -87,8 +88,6 @@
import org.jruby.util.io.ChannelStream;
import org.jruby.util.io.InvalidValueException;
import org.jruby.util.io.PipeException;
-import org.jruby.util.io.FileExistsException;
-import org.jruby.util.io.DirectoryAsFileException;
import org.jruby.util.io.STDIO;
import org.jruby.util.io.OpenFile;
import org.jruby.util.io.ChannelDescriptor;
@@ -348,11 +347,7 @@ protected void reopenPath(Ruby runtime, IRubyObject[] args) {
openFile.setPath(path);
if (openFile.getMainStream() == null) {
- try {
- openFile.setMainStream(ChannelStream.fopen(runtime, path, modes.getModeFlags()));
- } catch (FileExistsException fee) {
- throw runtime.newErrnoEEXISTError(path);
- }
+ openFile.setMainStream(ChannelStream.fopen(runtime, path, modes.getModeFlags()));
if (openFile.getPipeStream() != null) {
openFile.getPipeStream().fclose();
@@ -1241,15 +1236,9 @@ private static IRubyObject sysopenCommon(IRubyObject recv, IRubyObject[] args, B
runtime.getJRubyClassLoader());
// always a new fileno, so ok to use internal only
fileno = descriptor.getFileno();
- }
- catch (FileNotFoundException fnfe) {
- throw runtime.newErrnoENOENTError(path);
- } catch (DirectoryAsFileException dafe) {
- throw runtime.newErrnoEISDirError(path);
- } catch (FileExistsException fee) {
- throw runtime.newErrnoEEXISTError(path);
- } catch (IOException ioe) {
- throw runtime.newIOErrorFromException(ioe);
+ } catch (RaisableException raisable) {
+ raisable.printStackTrace();
+ throw raisable.newRaiseException(runtime);
}
return runtime.newFixnum(fileno);
}
View
7 core/src/main/java/org/jruby/exceptions/RaisableException.java
@@ -0,0 +1,7 @@
+package org.jruby.exceptions;
+
+import org.jruby.Ruby;
+
+public abstract class RaisableException extends Exception {
+ abstract public RaiseException newRaiseException(Ruby ruby);
+}
View
9 core/src/main/java/org/jruby/util/EmptyFileResource.java
@@ -2,8 +2,10 @@
import jnr.posix.FileStat;
import jnr.posix.POSIX;
-import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.ThreadContext;
+import org.jruby.exceptions.RaisableException;
+import org.jruby.util.io.ChannelDescriptor;
+import org.jruby.util.io.ModeFlags;
class EmptyFileResource implements FileResource {
// All empty resources are the same and immutable, so may as well
@@ -82,4 +84,9 @@ public JRubyFile hackyGetJRubyFile() {
// should be okay for now.
return JRubyNonExistentFile.NOT_EXIST;
}
+
+ @Override
+ public ChannelDescriptor openDescriptor(ModeFlags flags, POSIX posix, int perm) throws RaisableException {
+ throw new ResourceException.NotFound(absolutePath());
+ }
}
View
5 core/src/main/java/org/jruby/util/FileResource.java
@@ -2,6 +2,9 @@
import jnr.posix.FileStat;
import jnr.posix.POSIX;
+import org.jruby.exceptions.RaisableException;
+import org.jruby.util.io.ChannelDescriptor;
+import org.jruby.util.io.ModeFlags;
/**
* This is a shared interface for files loaded as {@link java.io.File} and {@link java.util.zip.ZipEntry}.
@@ -33,4 +36,6 @@
// JRubyFile if this resource is backed by one, and NOT_FOUND JRubyFile
// otherwise.
JRubyFile hackyGetJRubyFile();
+
+ ChannelDescriptor openDescriptor(ModeFlags flags, POSIX posix, int perm) throws RaisableException;
}
View
14 core/src/main/java/org/jruby/util/JarDirectoryResource.java
@@ -1,5 +1,12 @@
package org.jruby.util;
+import jnr.posix.POSIX;
+import org.jruby.Ruby;
+import org.jruby.util.io.ChannelDescriptor;
+import org.jruby.util.io.ModeFlags;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.exceptions.RaisableException;
+
/**
* Represents a directory in a jar.
*
@@ -53,4 +60,11 @@ public long length() {
public boolean isRoot() {
return "/".equals(path);
}
+
+ @Override
+ public ChannelDescriptor openDescriptor(ModeFlags flags, POSIX posix, int perm) throws RaisableException {
+ // opening a directory seems to blow up with EACCESS in jruby (although MRI allows instantiation but blows up on read).
+ // So mimicking that for now.
+ throw new ResourceException.PermissionDenied(absolutePath());
+ }
}
View
19 core/src/main/java/org/jruby/util/JarFileResource.java
@@ -1,6 +1,14 @@
package org.jruby.util;
-import java.util.jar.JarFile;
+import jnr.posix.POSIX;
+import org.jruby.Ruby;
+import org.jruby.util.io.ChannelDescriptor;
+import org.jruby.util.io.ModeFlags;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.exceptions.RaisableException;
+
+import java.io.InputStream;
+import java.nio.channels.Channels;
import java.util.jar.JarEntry;
/**
@@ -14,10 +22,12 @@
*/
class JarFileResource extends JarResource {
private final JarEntry entry;
+ private final InputStream entryStream;
- JarFileResource(String jarPath, JarEntry entry) {
+ JarFileResource(String jarPath, JarEntry entry, InputStream entryStream) {
super(jarPath);
this.entry = entry;
+ this.entryStream = entryStream;
}
@Override
@@ -50,4 +60,9 @@ public long lastModified() {
// Files cannot be listed
return null;
}
+
+ @Override
+ public ChannelDescriptor openDescriptor(ModeFlags flags, POSIX posix, int perm) throws RaisableException {
+ return new ChannelDescriptor(Channels.newChannel(entryStream), flags);
+ }
}
View
18 core/src/main/java/org/jruby/util/JarResource.java
@@ -2,13 +2,18 @@
import jnr.posix.FileStat;
import jnr.posix.POSIX;
+import org.jruby.exceptions.RaisableException;
+import org.jruby.util.io.ChannelDescriptor;
+import org.jruby.util.io.ModeFlags;
+
import java.io.IOException;
+import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
-public abstract class JarResource implements FileResource {
+abstract class JarResource implements FileResource {
private static Pattern PREFIX_MATCH = Pattern.compile("^(?:jar:)?(?:file:)?(.*)$");
private static final JarCache jarCache = new JarCache();
@@ -53,9 +58,14 @@ private static JarResource createJarResource(String jarPath, String path) {
return new JarDirectoryResource(jarPath, path, entries);
}
- JarEntry jarEntry = index.getJarEntry(path);
- if (jarEntry != null) {
- return new JarFileResource(jarPath, jarEntry);
+ try {
+ JarEntry jarEntry = index.getJarEntry(path);
+ if (jarEntry != null) {
+ InputStream jarEntryStream = index.jar.getInputStream(jarEntry);
+ return new JarFileResource(path, jarEntry, jarEntryStream);
+ }
+ } catch (IOException ioe) {
+ // Probably not a jar entry then
}
return null;
View
129 core/src/main/java/org/jruby/util/RegularFileResource.java
@@ -4,15 +4,24 @@
import jnr.posix.POSIX;
import jnr.posix.POSIXFactory;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileFilter;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
import org.jruby.Ruby;
import org.jruby.RubyFile;
+import org.jruby.platform.Platform;
+import org.jruby.util.io.ChannelDescriptor;
+import org.jruby.util.io.ModeFlags;
+import org.jruby.exceptions.RaisableException;
+import org.jruby.exceptions.RaiseException;
import jnr.posix.JavaSecuredFile;
-import org.jruby.platform.Platform;
import java.util.jar.JarFile;
import java.util.jar.JarEntry;
import java.util.zip.ZipEntry;
@@ -121,4 +130,122 @@ public String toString() {
public JRubyFile hackyGetJRubyFile() {
return file;
}
+
+ @Override
+ public ChannelDescriptor openDescriptor(ModeFlags flags, POSIX posix, int perm) throws RaisableException {
+ if (flags.isCreate()) {
+ boolean fileCreated;
+ try {
+ fileCreated = file.createNewFile();
+ } catch (IOException ioe) {
+ // See JRUBY-4380.
+ // MRI behavior: raise Errno::ENOENT in case
+ // when the directory for the file doesn't exist.
+ // Java in such cases just throws IOException.
+ File parent = file.getParentFile();
+ if (parent != null && parent != file && !parent.exists()) {
+ throw new ResourceException.NotFound(absolutePath());
+ } else if (!file.canWrite()) {
+ throw new ResourceException.PermissionDenied(absolutePath());
+ } else {
+ // for all other IO errors, we report it as general IO error
+ throw new IOError(ioe);
+ }
+ }
+
+ if (!fileCreated && flags.isExclusive()) {
+ throw new ResourceException.FileExists(absolutePath());
+ }
+
+ ChannelDescriptor descriptor = createDescriptor(flags);
+
+ // attempt to set the permissions, if we have been passed a POSIX instance,
+ // perm is > 0, and only if the file was created in this call.
+ if (fileCreated && posix != null && perm > 0) {
+ if (posix != null && perm > 0) {
+ posix.chmod(file.getPath(), perm);
+ }
+ }
+
+ return descriptor;
+ }
+
+ if (file.isDirectory() && flags.isWritable()) {
+ throw new ResourceException.FileIsDirectory(absolutePath());
+ }
+
+ if (!file.exists()) {
+ throw new ResourceException.NotFound(absolutePath());
+ }
+
+ return createDescriptor(flags);
+ }
+
+ private ChannelDescriptor createDescriptor(ModeFlags flags) throws RaisableException {
+ FileDescriptor fileDescriptor;
+ FileChannel fileChannel;
+
+ /* Because RandomAccessFile does not provide a way to pass append
+ * mode, we must manually seek if using RAF. FileOutputStream,
+ * however, does properly honor append mode at the lowest levels,
+ * reducing append write costs when we're only doing writes.
+ *
+ * The code here will use a FileOutputStream if we're only writing,
+ * setting isInAppendMode to true to disable our manual seeking.
+ *
+ * RandomAccessFile does not handle append for us, so if we must
+ * also be readable we pass false for isInAppendMode to indicate
+ * we need manual seeking.
+ */
+ boolean isInAppendMode;
+ try{
+ if (flags.isWritable() && !flags.isReadable()) {
+ FileOutputStream fos = new FileOutputStream(file, flags.isAppendable());
+ fileChannel = fos.getChannel();
+ fileDescriptor = fos.getFD();
+ isInAppendMode = true;
+ } else {
+ RandomAccessFile raf = new RandomAccessFile(file, flags.toJavaModeString());
+ fileChannel = raf.getChannel();
+ fileDescriptor = raf.getFD();
+ isInAppendMode = false;
+ }
+ } catch (FileNotFoundException fnfe) {
+ // Jave throws FileNotFoundException both if the file doesn't exist or there were
+ // permission issues, but Ruby needs to disambiguate those two cases
+ throw file.exists() ?
+ new ResourceException.PermissionDenied(absolutePath()) :
+ new ResourceException.NotFound(absolutePath());
+ } catch (IOException ioe) {
+ throw new IOError(ioe);
+ }
+
+ try {
+ if (flags.isTruncate()) fileChannel.truncate(0);
+ } catch (IOException ioe) {
+ if (ioe.getMessage().equals("Illegal seek")) {
+ // ignore; it's a pipe or fifo that can't be truncated
+ } else {
+ throw new IOError(ioe);
+ }
+ }
+
+ // TODO: append should set the FD to end, no? But there is no seek(int) in libc!
+ //if (modes.isAppendable()) seek(0, Stream.SEEK_END);
+
+ return new ChannelDescriptor(fileChannel, flags, fileDescriptor, isInAppendMode);
+ }
+
+ static class IOError extends RaisableException {
+ private final IOException ioe;
+
+ IOError(IOException ioe) {
+ this.ioe = ioe;
+ }
+
+ @Override
+ public RaiseException newRaiseException(Ruby runtime) {
+ return runtime.newIOErrorFromException(ioe);
+ }
+ }
}
View
36 core/src/main/java/org/jruby/util/ResourceException.java
@@ -0,0 +1,36 @@
+package org.jruby.util;
+
+import org.jruby.Ruby;
+import org.jruby.exceptions.RaisableException;
+import org.jruby.exceptions.RaiseException;
+
+class ResourceException extends RaisableException {
+ static class FileIsDirectory extends ResourceException {
+ public FileIsDirectory(String path) { super("EISDIR", path); }
+ }
+
+ static class FileExists extends ResourceException {
+ public FileExists(String path) { super("EEXIST", path); }
+ }
+
+ static class NotFound extends ResourceException {
+ public NotFound(String path) { super("ENOENT", path); }
+ }
+
+ static class PermissionDenied extends ResourceException {
+ public PermissionDenied(String path) { super("EACCES", path); }
+ }
+
+ private final String path;
+ private final String errnoClass;
+
+ protected ResourceException(String errnoClass, String path) {
+ this.errnoClass = errnoClass;
+ this.path = path;
+ }
+
+ @Override
+ public RaiseException newRaiseException(Ruby runtime) {
+ return runtime.newRaiseException(runtime.getErrno().getClass(errnoClass), path);
+ }
+}
View
2  core/src/main/java/org/jruby/util/io/CRLFStreamWrapper.java
@@ -217,7 +217,7 @@ public void setBlocking(boolean blocking) throws IOException {
stream.setBlocking(blocking);
}
- public void freopen(Ruby runtime, String path, ModeFlags modes) throws DirectoryAsFileException, IOException, InvalidValueException, PipeException, BadDescriptorException {
+ public void freopen(Ruby runtime, String path, ModeFlags modes) throws IOException, InvalidValueException, PipeException, BadDescriptorException {
stream.freopen(runtime, path, modes);
}
View
163 core/src/main/java/org/jruby/util/io/ChannelDescriptor.java
@@ -51,13 +51,14 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.jar.JarFile;
-import java.util.zip.ZipEntry;
+import org.jruby.Ruby;
import org.jruby.RubyFile;
import jnr.posix.POSIX;
import jnr.unixsocket.UnixServerSocketChannel;
import jnr.unixsocket.UnixSocketChannel;
+import org.jruby.exceptions.RaisableException;
+import org.jruby.exceptions.RaiseException;
import org.jruby.util.ByteList;
import org.jruby.util.JRubyFile;
import org.jruby.util.log.Logger;
@@ -82,7 +83,6 @@
* POSIX dup also allows independent positioning information.
*/
public class ChannelDescriptor {
-
private static final Logger LOG = LoggerFactory.getLogger("ChannelDescriptor");
/** Whether to log debugging information */
@@ -714,15 +714,8 @@ public int write(int c) throws IOException, BadDescriptorException {
* @param path the file path to open
* @param flags the mode flags to use for opening the file
* @return a new ChannelDescriptor based on the specified parameters
- * @throws java.io.FileNotFoundException if the target file could not be found
- * and the create flag was not specified
- * @throws org.jruby.util.io.DirectoryAsFileException if the target file is
- * a directory being opened as a file
- * @throws org.jruby.util.io.FileExistsException if the target file should
- * be created anew, but already exists
- * @throws java.io.IOException if there is an exception during IO
*/
- public static ChannelDescriptor open(String cwd, String path, ModeFlags flags) throws FileNotFoundException, DirectoryAsFileException, FileExistsException, IOException {
+ public static ChannelDescriptor open(String cwd, String path, ModeFlags flags) throws RaisableException {
return open(cwd, path, flags, 0, null, null);
}
@@ -737,15 +730,8 @@ public static ChannelDescriptor open(String cwd, String path, ModeFlags flags) t
* @param flags the mode flags to use for opening the file
* @param classLoader a ClassLoader to use for classpath: resources
* @return a new ChannelDescriptor based on the specified parameters
- * @throws java.io.FileNotFoundException if the target file could not be found
- * and the create flag was not specified
- * @throws org.jruby.util.io.DirectoryAsFileException if the target file is
- * a directory being opened as a file
- * @throws org.jruby.util.io.FileExistsException if the target file should
- * be created anew, but already exists
- * @throws java.io.IOException if there is an exception during IO
*/
- public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, ClassLoader classLoader) throws FileNotFoundException, DirectoryAsFileException, FileExistsException, IOException {
+ public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, ClassLoader classLoader) throws RaisableException {
return open(cwd, path, flags, 0, null, classLoader);
}
@@ -761,15 +747,8 @@ public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, C
* unobserved)
* @param posix a POSIX api implementation, used for setting permissions; if null, permissions are ignored
* @return a new ChannelDescriptor based on the specified parameters
- * @throws java.io.FileNotFoundException if the target file could not be found
- * and the create flag was not specified
- * @throws org.jruby.util.io.DirectoryAsFileException if the target file is
- * a directory being opened as a file
- * @throws org.jruby.util.io.FileExistsException if the target file should
- * be created anew, but already exists
- * @throws java.io.IOException if there is an exception during IO
*/
- public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, int perm, POSIX posix) throws FileNotFoundException, DirectoryAsFileException, FileExistsException, IOException {
+ public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, int perm, POSIX posix) throws RaisableException {
return open(cwd, path, flags, perm, posix, null);
}
@@ -786,142 +765,24 @@ public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, i
* @param posix a POSIX api implementation, used for setting permissions; if null, permissions are ignored
* @param classLoader a ClassLoader to use for classpath: resources
* @return a new ChannelDescriptor based on the specified parameters
- * @throws java.io.FileNotFoundException if the target file could not be found
- * and the create flag was not specified
- * @throws org.jruby.util.io.DirectoryAsFileException if the target file is
- * a directory being opened as a file
- * @throws org.jruby.util.io.FileExistsException if the target file should
- * be created anew, but already exists
- * @throws java.io.IOException if there is an exception during IO
*/
- public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, int perm, POSIX posix, ClassLoader classLoader) throws FileNotFoundException, DirectoryAsFileException, FileExistsException, IOException {
- boolean fileCreated = false;
+ public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, int perm, POSIX posix, ClassLoader classLoader) throws RaisableException {
if (path.equals("/dev/null") || path.equalsIgnoreCase("nul:") || path.equalsIgnoreCase("nul")) {
Channel nullChannel = new NullChannel();
// FIXME: don't use RubyIO for this
return new ChannelDescriptor(nullChannel, flags);
- } else if (path.startsWith("file:")) {
- int bangIndex = path.indexOf("!");
- if (bangIndex > 0) {
- String filePath = path.substring(5, bangIndex);
- String internalPath = path.substring(bangIndex + 2);
-
- if (!new File(filePath).exists()) {
- throw new FileNotFoundException(path);
- }
-
- JarFile jf = new JarFile(filePath);
- ZipEntry entry = RubyFile.getFileEntry(jf, internalPath);
-
- if (entry == null) {
- throw new FileNotFoundException(path);
- }
+ }
- InputStream is = jf.getInputStream(entry);
- // FIXME: don't use RubyIO for this
- return new ChannelDescriptor(Channels.newChannel(is), flags);
- } else {
- // raw file URL, just open directly
- URL url = new URL(path);
- InputStream is = url.openStream();
- // FIXME: don't use RubyIO for this
- return new ChannelDescriptor(Channels.newChannel(is), flags);
- }
- } else if (path.startsWith("classpath:/") && classLoader != null) {
+ if (path.startsWith("classpath:/") && classLoader != null) {
path = path.substring("classpath:/".length());
InputStream is = classLoader.getResourceAsStream(path);
// FIXME: don't use RubyIO for this
return new ChannelDescriptor(Channels.newChannel(is), flags);
- } else {
- JRubyFile theFile = JRubyFile.create(cwd,path);
-
- if (theFile.isDirectory() && flags.isWritable()) {
- throw new DirectoryAsFileException();
- }
-
- if (flags.isCreate()) {
- try {
- fileCreated = theFile.createNewFile();
-
- if (!fileCreated && flags.isExclusive()) {
- throw new FileExistsException(path);
- }
- } catch (IOException ioe) {
- // See JRUBY-4380.
- // MRI behavior: raise Errno::ENOENT in case
- // when the directory for the file doesn't exist.
- // Java in such cases just throws IOException.
- File parent = theFile.getParentFile();
- if (parent != null && parent != theFile && !parent.exists()) {
- throw new FileNotFoundException(path);
- } else if (!theFile.canWrite()) {
- throw new PermissionDeniedException(path);
- } else {
- // for all other IO errors, just re-throw the original exception
- throw ioe;
- }
- }
- } else {
- if (!theFile.exists()) {
- throw new FileNotFoundException(path);
- }
- }
-
- FileDescriptor fileDescriptor;
- FileChannel fileChannel;
-
- /* Because RandomAccessFile does not provide a way to pass append
- * mode, we must manually seek if using RAF. FileOutputStream,
- * however, does properly honor append mode at the lowest levels,
- * reducing append write costs when we're only doing writes.
- *
- * The code here will use a FileOutputStream if we're only writing,
- * setting isInAppendMode to true to disable our manual seeking.
- *
- * RandomAccessFile does not handle append for us, so if we must
- * also be readable we pass false for isInAppendMode to indicate
- * we need manual seeking.
- */
- boolean isInAppendMode;
- if (flags.isWritable() && !flags.isReadable()) {
- FileOutputStream fos = new FileOutputStream(theFile, flags.isAppendable());
- fileChannel = fos.getChannel();
- fileDescriptor = fos.getFD();
- isInAppendMode = true;
- } else {
- RandomAccessFile raf = new RandomAccessFile(theFile, flags.toJavaModeString());
- fileChannel = raf.getChannel();
- fileDescriptor = raf.getFD();
- isInAppendMode = false;
- }
-
- // call chmod after we created the RandomAccesFile
- // because otherwise, the file could be read-only
- if (fileCreated) {
- // attempt to set the permissions, if we have been passed a POSIX instance,
- // perm is > 0, and only if the file was created in this call.
- if (posix != null && perm > 0) {
- posix.chmod(theFile.getPath(), perm);
- }
- }
-
- try {
- if (flags.isTruncate()) fileChannel.truncate(0);
- } catch (IOException ioe) {
- if (ioe.getMessage().equals("Illegal seek")) {
- // ignore; it's a pipe or fifo that can't be truncated
- } else {
- throw ioe;
- }
- }
-
- // TODO: append should set the FD to end, no? But there is no seek(int) in libc!
- //if (modes.isAppendable()) seek(0, Stream.SEEK_END);
-
- return new ChannelDescriptor(fileChannel, flags, fileDescriptor, isInAppendMode);
}
+
+ return JRubyFile.createResource(cwd, path).openDescriptor(flags, posix, perm);
}
-
+
/**
* Close this descriptor. If in closing the last ChannelDescriptor reference
* to the associate channel is closed, the channel itself will be closed.
View
19 core/src/main/java/org/jruby/util/io/ChannelStream.java
@@ -50,6 +50,7 @@
import org.jruby.Finalizable;
import org.jruby.Ruby;
+import org.jruby.exceptions.RaisableException;
import org.jruby.platform.Platform;
import org.jruby.util.ByteList;
import org.jruby.util.JRubyFile;
@@ -1420,7 +1421,7 @@ public boolean isBlocking() {
return blocking;
}
- public synchronized void freopen(Ruby runtime, String path, ModeFlags modes) throws DirectoryAsFileException, IOException, InvalidValueException, PipeException, BadDescriptorException {
+ public synchronized void freopen(Ruby runtime, String path, ModeFlags modes) throws IOException, InvalidValueException, PipeException, BadDescriptorException {
// flush first
flushWrite();
@@ -1442,7 +1443,9 @@ public synchronized void freopen(Ruby runtime, String path, ModeFlags modes) thr
String cwd = runtime.getCurrentDirectory();
JRubyFile theFile = JRubyFile.create(cwd,path);
- if (theFile.isDirectory() && modes.isWritable()) throw new DirectoryAsFileException();
+ if (theFile.isDirectory() && modes.isWritable()) {
+ throw runtime.newErrnoEISDirError(path);
+ }
if (modes.isCreate()) {
if (theFile.exists() && modes.isExclusive()) {
@@ -1498,11 +1501,15 @@ private static Stream maybeWrapWithLineEndingWrapper(Stream stream, ModeFlags mo
return stream;
}
- public static Stream fopen(Ruby runtime, String path, ModeFlags modes) throws FileNotFoundException, DirectoryAsFileException, FileExistsException, IOException, InvalidValueException, PipeException, BadDescriptorException {
- ChannelDescriptor descriptor = ChannelDescriptor.open(runtime.getCurrentDirectory(), path, modes, runtime.getClassLoader());
- Stream stream = fdopen(runtime, descriptor, modes);
+ public static Stream fopen(Ruby runtime, String path, ModeFlags modes) throws FileNotFoundException, IOException, InvalidValueException, PipeException, BadDescriptorException {
+ try {
+ ChannelDescriptor descriptor = ChannelDescriptor.open(runtime.getCurrentDirectory(), path, modes, runtime.getClassLoader());
+ Stream stream = fdopen(runtime, descriptor, modes);
- return stream;
+ return stream;
+ } catch (RaisableException raisable) {
+ throw raisable.newRaiseException(runtime);
+ }
}
public Channel getChannel() {
View
34 core/src/main/java/org/jruby/util/io/DirectoryAsFileException.java
@@ -1,34 +0,0 @@
-/*
- ***** BEGIN LICENSE BLOCK *****
- * Version: EPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Eclipse Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/epl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2007 Thomas E Enebo <enebo@acm.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the EPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the EPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
-package org.jruby.util.io;
-
-import java.io.IOException;
-
-public class DirectoryAsFileException extends IOException {
-}
View
35 core/src/main/java/org/jruby/util/io/FileExistsException.java
@@ -1,35 +0,0 @@
-/*
- **** BEGIN LICENSE BLOCK *****
- * Version: EPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Eclipse Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/epl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2008 The JRuby Community <www.jruby.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the EPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the EPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
-package org.jruby.util.io;
-
-public class FileExistsException extends Throwable {
- public FileExistsException(String path) {
- super("file exists: " + path);
- }
-}
View
65 core/src/main/java/org/jruby/util/io/PermissionDeniedException.java
@@ -1,65 +0,0 @@
-/*
- ***** BEGIN LICENSE BLOCK *****
- * Version: EPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Eclipse Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/epl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2010 Hiroshi Nakamura <nahi@ruby-lang.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the EPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the EPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
-package org.jruby.util.io;
-
-import java.io.FileNotFoundException;
-
-/**
- * Signals that an attempt to open the file denoted by a specified pathname
- * has failed by 'Permission Denied'.
- *
- * <p>This exception might be thrown by the {@link ChannelDescriptor#open}
- * when trying to create new file and the specified pathname cannot be written.
- * Bear in mind that {@link ChannelDescriptor#open} throws
- * not PermissionDeniedException but FileNotFindException as same as Java
- * manner when trying to read existing but unreadable file.
- * See org.jruby.RubyFile#fopen and sysopen how we handle that situation.</p>
- */
-public class PermissionDeniedException extends FileNotFoundException {
-
- /**
- * Constructs a PermissionDeniedException with null as its error detail
- * message.
- */
- public PermissionDeniedException() {
- super();
- }
-
- /**
- * Constructs a PermissionDeniedException with the specified detail
- * message. The string msg can be retrieved later by the
- * {@link java.lang.Throwable#getMessage} method of class
- * java.lang.Throwable.
- *
- * @param msg the detail message.
- */
- public PermissionDeniedException(String msg) {
- super(msg);
- }
-}
View
2  core/src/main/java/org/jruby/util/io/Stream.java
@@ -170,7 +170,7 @@ void ftruncate(long newLength) throws IOException, PipeException,
void setBlocking(boolean blocking) throws IOException;
- void freopen(Ruby runtime, String path, ModeFlags modes) throws DirectoryAsFileException, IOException, InvalidValueException, PipeException, BadDescriptorException;
+ void freopen(Ruby runtime, String path, ModeFlags modes) throws IOException, InvalidValueException, PipeException, BadDescriptorException;
void setBinmode();
boolean isBinmode();
View
8 ext/openssl/src/main/java/org/jruby/ext/openssl/x509store/Lookup.java
@@ -47,9 +47,9 @@
import java.util.List;
import org.jruby.Ruby;
import org.jruby.RubyHash;
+import org.jruby.exceptions.RaisableException;
import org.jruby.util.io.ChannelDescriptor;
import org.jruby.util.io.ChannelStream;
-import org.jruby.util.io.FileExistsException;
import org.jruby.util.io.InvalidValueException;
import org.jruby.util.io.ModeFlags;
@@ -299,12 +299,10 @@ private InputStream wrapJRubyNormalizedInputStream(String file) throws IOExcepti
try {
ChannelDescriptor descriptor = ChannelDescriptor.open(runtime.getCurrentDirectory(), file, new ModeFlags(ModeFlags.RDONLY));
return ChannelStream.open(runtime, descriptor).newInputStream();
+ } catch (RaisableException raisable) {
+ throw raisable.newRaiseException(runtime);
} catch (NoSuchMethodError nsme) {
return new BufferedInputStream(new FileInputStream(file));
- } catch (FileExistsException fee) {
- // should not happen because ModeFlag does not contain CREAT.
- fee.printStackTrace(System.err);
- throw new IllegalStateException(fee.getMessage(), fee);
} catch (InvalidValueException ive) {
// should not happen because ModeFlasg does not contain APPEND.
ive.printStackTrace(System.err);
Something went wrong with that request. Please try again.