/* * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.io; import java.util.ArrayList; import java.util.List; import java.util.Objects; import jdk.internal.access.JavaIOFileDescriptorAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.ref.PhantomCleanable; /** * Instances of the file descriptor class serve as an opaque handle * to the underlying machine-specific structure representing an open * file, an open socket, or another source or sink of bytes. * The main practical use for a file descriptor is to create a * {@link FileInputStream} or {@link FileOutputStream} to contain it. *

* Applications should not create their own file descriptors. * * @author Pavani Diwanji * @since 1.0 */ public final class FileDescriptor { private int fd; private long handle; private Closeable parent; private List otherParents; private boolean closed; /** * true, if file is opened for appending. */ private boolean append; static { initIDs(); } // Set up JavaIOFileDescriptorAccess in SharedSecrets static { SharedSecrets.setJavaIOFileDescriptorAccess( new JavaIOFileDescriptorAccess() { public void set(FileDescriptor fdo, int fd) { fdo.set(fd); } public int get(FileDescriptor fdo) { return fdo.fd; } public void setAppend(FileDescriptor fdo, boolean append) { fdo.append = append; } public boolean getAppend(FileDescriptor fdo) { return fdo.append; } public void close(FileDescriptor fdo) throws IOException { fdo.close(); } /* Register for a normal FileCleanable fd/handle cleanup. */ public void registerCleanup(FileDescriptor fdo) { FileCleanable.register(fdo); } /* Register a custom PhantomCleanup. */ public void registerCleanup(FileDescriptor fdo, PhantomCleanable cleanup) { fdo.registerCleanup(cleanup); } public void unregisterCleanup(FileDescriptor fdo) { fdo.unregisterCleanup(); } public void setHandle(FileDescriptor fdo, long handle) { fdo.setHandle(handle); } public long getHandle(FileDescriptor fdo) { return fdo.handle; } } ); } /** * Cleanup in case FileDescriptor is not explicitly closed. */ private PhantomCleanable cleanup; /** * Constructs an (invalid) FileDescriptor object. * The fd or handle is set later. */ public FileDescriptor() { fd = -1; handle = -1; } /** * Used for standard input, output, and error only. * For Windows the corresponding handle is initialized. * For Unix the append mode is cached. * @param fd the raw fd number (0, 1, 2) */ private FileDescriptor(int fd) { this.fd = fd; this.handle = getHandle(fd); this.append = getAppend(fd); } /** * A handle to the standard input stream. Usually, this file * descriptor is not used directly, but rather via the input stream * known as {@code System.in}. * * @see java.lang.System#in */ public static final FileDescriptor in = new FileDescriptor(0); /** * A handle to the standard output stream. Usually, this file * descriptor is not used directly, but rather via the output stream * known as {@code System.out}. * @see java.lang.System#out */ public static final FileDescriptor out = new FileDescriptor(1); /** * A handle to the standard error stream. Usually, this file * descriptor is not used directly, but rather via the output stream * known as {@code System.err}. * * @see java.lang.System#err */ public static final FileDescriptor err = new FileDescriptor(2); /** * Tests if this file descriptor object is valid. * * @return {@code true} if the file descriptor object represents a * valid, open file, socket, or other active I/O connection; * {@code false} otherwise. */ public boolean valid() { return (handle != -1) || (fd != -1); } /** * Force all system buffers to synchronize with the underlying * device. This method returns after all modified data and * attributes of this FileDescriptor have been written to the * relevant device(s). In particular, if this FileDescriptor * refers to a physical storage medium, such as a file in a file * system, sync will not return until all in-memory modified copies * of buffers associated with this FileDescriptor have been * written to the physical medium. * * sync is meant to be used by code that requires physical * storage (such as a file) to be in a known state For * example, a class that provided a simple transaction facility * might use sync to ensure that all changes to a file caused * by a given transaction were recorded on a storage medium. * * sync only affects buffers downstream of this FileDescriptor. If * any in-memory buffering is being done by the application (for * example, by a BufferedOutputStream object), those buffers must * be flushed into the FileDescriptor (for example, by invoking * OutputStream.flush) before that data will be affected by sync. * * @exception SyncFailedException * Thrown when the buffers cannot be flushed, * or because the system cannot guarantee that all the * buffers have been synchronized with physical media. * @since 1.1 */ public native void sync() throws SyncFailedException; /* This routine initializes JNI field offsets for the class */ private static native void initIDs(); /* * On Windows return the handle for the standard streams. */ private static native long getHandle(int d); /** * Returns true, if the file was opened for appending. */ private static native boolean getAppend(int fd); /** * Set the fd. * Used on Unix and for sockets on Windows and Unix. * If setting to -1, clear the cleaner. * The {@link #registerCleanup} method should be called for new fds. * @param fd the raw fd or -1 to indicate closed */ @SuppressWarnings("unchecked") synchronized void set(int fd) { if (fd == -1 && cleanup != null) { cleanup.clear(); cleanup = null; } this.fd = fd; } /** * Set the handle. * Used on Windows for regular files. * If setting to -1, clear the cleaner. * The {@link #registerCleanup} method should be called for new handles. * @param handle the handle or -1 to indicate closed */ @SuppressWarnings("unchecked") void setHandle(long handle) { if (handle == -1 && cleanup != null) { cleanup.clear(); cleanup = null; } this.handle = handle; } /** * Register a cleanup for the current handle. * Used directly in java.io and indirectly via fdAccess. * The cleanup should be registered after the handle is set in the FileDescriptor. * @param cleanable a PhantomCleanable to register */ @SuppressWarnings("unchecked") synchronized void registerCleanup(PhantomCleanable cleanable) { Objects.requireNonNull(cleanable, "cleanable"); if (cleanup != null) { cleanup.clear(); } cleanup = cleanable; } /** * Unregister a cleanup for the current raw fd or handle. * Used directly in java.io and indirectly via fdAccess. * Normally {@link #close()} should be used except in cases where * it is certain the caller will close the raw fd and the cleanup * must not close the raw fd. {@link #unregisterCleanup()} must be * called before the raw fd is closed to prevent a race that makes * it possible for the fd to be reallocated to another use and later * the cleanup might be invoked. */ synchronized void unregisterCleanup() { if (cleanup != null) { cleanup.clear(); } cleanup = null; } /** * Close the raw file descriptor or handle, if it has not already been closed. * The native code sets the fd and handle to -1. * Clear the cleaner so the close does not happen twice. * Package private to allow it to be used in java.io. * @throws IOException if close fails */ @SuppressWarnings("unchecked") synchronized void close() throws IOException { unregisterCleanup(); close0(); } /* * Close the raw file descriptor or handle, if it has not already been closed * and set the fd and handle to -1. */ private native void close0() throws IOException; /* * Package private methods to track referents. * If multiple streams point to the same FileDescriptor, we cycle * through the list of all referents and call close() */ /** * Attach a Closeable to this FD for tracking. * parent reference is added to otherParents when * needed to make closeAll simpler. */ synchronized void attach(Closeable c) { if (parent == null) { // first caller gets to do this parent = c; } else if (otherParents == null) { otherParents = new ArrayList<>(); otherParents.add(parent); otherParents.add(c); } else { otherParents.add(c); } } /** * Cycle through all Closeables sharing this FD and call * close() on each one. * * The caller closeable gets to call close0(). */ @SuppressWarnings("try") synchronized void closeAll(Closeable releaser) throws IOException { if (!closed) { closed = true; IOException ioe = null; try (releaser) { if (otherParents != null) { for (Closeable referent : otherParents) { try { referent.close(); } catch(IOException x) { if (ioe == null) { ioe = x; } else { ioe.addSuppressed(x); } } } } } catch(IOException ex) { /* * If releaser close() throws IOException * add other exceptions as suppressed. */ if (ioe != null) ex.addSuppressed(ioe); ioe = ex; } finally { if (ioe != null) throw ioe; } } } }