1 /*
  2  * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package java.io;
 27 
 28 import java.nio.channels.FileChannel;
 29 import java.util.Arrays;
 30 import jdk.internal.misc.Blocker;
 31 import jdk.internal.util.ArraysSupport;
 32 import sun.nio.ch.FileChannelImpl;
 33 
 34 /**
 35  * A {@code FileInputStream} obtains input bytes
 36  * from a file in a file system. What files
 37  * are  available depends on the host environment.
 38  *
 39  * <p>{@code FileInputStream} is meant for reading streams of raw bytes
 40  * such as image data. For reading streams of characters, consider using
 41  * {@code FileReader}.
 42  *
 43  * @apiNote
 44  * The {@link #close} method should be called to release resources used by this
 45  * stream, either directly, or with the {@code try}-with-resources statement.
 46  *
 47  * @implSpec
 48  * Subclasses are responsible for the cleanup of resources acquired by the subclass.
 49  * Subclasses requiring that resource cleanup take place after a stream becomes
 50  * unreachable should use {@link java.lang.ref.Cleaner} or some other mechanism.
 51  *
 52  * @author  Arthur van Hoff
 53  * @see     java.io.File
 54  * @see     java.io.FileDescriptor
 55  * @see     java.io.FileOutputStream
 56  * @see     java.nio.file.Files#newInputStream
 57  * @since   1.0
 58  */
 59 public class FileInputStream extends InputStream
 60 {
 61     private static final int DEFAULT_BUFFER_SIZE = 8192;
 62 
 63     /* File Descriptor - handle to the open file */
 64     private final FileDescriptor fd;
 65 
 66     /**
 67      * The path of the referenced file
 68      * (null if the stream is created with a file descriptor)
 69      */
 70     private final String path;
 71 
 72     private volatile FileChannel channel;
 73 
 74     private final Object closeLock = new Object();
 75 
 76     private volatile boolean closed;
 77 
 78     /**
 79      * Creates a {@code FileInputStream} by
 80      * opening a connection to an actual file,
 81      * the file named by the path name {@code name}
 82      * in the file system.  A new {@code FileDescriptor}
 83      * object is created to represent this file
 84      * connection.
 85      * <p>
 86      * First, if there is a security
 87      * manager, its {@code checkRead} method
 88      * is called with the {@code name} argument
 89      * as its argument.
 90      * <p>
 91      * If the named file does not exist, is a directory rather than a regular
 92      * file, or for some other reason cannot be opened for reading then a
 93      * {@code FileNotFoundException} is thrown.
 94      *
 95      * @param      name   the system-dependent file name.
 96      * @throws     FileNotFoundException  if the file does not exist,
 97      *             is a directory rather than a regular file,
 98      *             or for some other reason cannot be opened for
 99      *             reading.
100      * @throws     SecurityException      if a security manager exists and its
101      *             {@code checkRead} method denies read access
102      *             to the file.
103      * @see        java.lang.SecurityManager#checkRead(java.lang.String)
104      */
105     public FileInputStream(String name) throws FileNotFoundException {
106         this(name != null ? new File(name) : null);
107     }
108 
109     /**
110      * Creates a {@code FileInputStream} by
111      * opening a connection to an actual file,
112      * the file named by the {@code File}
113      * object {@code file} in the file system.
114      * A new {@code FileDescriptor} object
115      * is created to represent this file connection.
116      * <p>
117      * First, if there is a security manager,
118      * its {@code checkRead} method  is called
119      * with the path represented by the {@code file}
120      * argument as its argument.
121      * <p>
122      * If the named file does not exist, is a directory rather than a regular
123      * file, or for some other reason cannot be opened for reading then a
124      * {@code FileNotFoundException} is thrown.
125      *
126      * @param      file   the file to be opened for reading.
127      * @throws     FileNotFoundException  if the file does not exist,
128      *             is a directory rather than a regular file,
129      *             or for some other reason cannot be opened for
130      *             reading.
131      * @throws     SecurityException      if a security manager exists and its
132      *             {@code checkRead} method denies read access to the file.
133      * @see        java.io.File#getPath()
134      * @see        java.lang.SecurityManager#checkRead(java.lang.String)
135      */
136     @SuppressWarnings("this-escape")
137     public FileInputStream(File file) throws FileNotFoundException {
138         String name = (file != null ? file.getPath() : null);
139         @SuppressWarnings("removal")
140         SecurityManager security = System.getSecurityManager();
141         if (security != null) {
142             security.checkRead(name);
143         }
144         if (name == null) {
145             throw new NullPointerException();
146         }
147         if (file.isInvalid()) {
148             throw new FileNotFoundException("Invalid file path");
149         }
150         fd = new FileDescriptor();
151         fd.attach(this);
152         path = name;
153         open(name);
154         FileCleanable.register(fd);       // open set the fd, register the cleanup
155     }
156 
157     /**
158      * Creates a {@code FileInputStream} by using the file descriptor
159      * {@code fdObj}, which represents an existing connection to an
160      * actual file in the file system.
161      * <p>
162      * If there is a security manager, its {@code checkRead} method is
163      * called with the file descriptor {@code fdObj} as its argument to
164      * see if it's ok to read the file descriptor. If read access is denied
165      * to the file descriptor a {@code SecurityException} is thrown.
166      * <p>
167      * If {@code fdObj} is null then a {@code NullPointerException}
168      * is thrown.
169      * <p>
170      * This constructor does not throw an exception if {@code fdObj}
171      * is {@link java.io.FileDescriptor#valid() invalid}.
172      * However, if the methods are invoked on the resulting stream to attempt
173      * I/O on the stream, an {@code IOException} is thrown.
174      *
175      * @param      fdObj   the file descriptor to be opened for reading.
176      * @throws     SecurityException      if a security manager exists and its
177      *             {@code checkRead} method denies read access to the
178      *             file descriptor.
179      * @see        SecurityManager#checkRead(java.io.FileDescriptor)
180      */
181     @SuppressWarnings("this-escape")
182     public FileInputStream(FileDescriptor fdObj) {
183         @SuppressWarnings("removal")
184         SecurityManager security = System.getSecurityManager();
185         if (fdObj == null) {
186             throw new NullPointerException();
187         }
188         if (security != null) {
189             security.checkRead(fdObj);
190         }
191         fd = fdObj;
192         path = null;
193 
194         /*
195          * FileDescriptor is being shared by streams.
196          * Register this stream with FileDescriptor tracker.
197          */
198         fd.attach(this);
199     }
200 
201     /**
202      * Opens the specified file for reading.
203      * @param name the name of the file
204      */
205     private native void open0(String name) throws FileNotFoundException;
206 
207     // wrap native call to allow instrumentation
208     /**
209      * Opens the specified file for reading.
210      * @param name the name of the file
211      */
212     private void open(String name) throws FileNotFoundException {
213         long comp = Blocker.begin();
214         try {
215             open0(name);
216         } finally {
217             Blocker.end(comp);
218         }
219     }
220 
221     /**
222      * Reads a byte of data from this input stream. This method blocks
223      * if no input is yet available.
224      *
225      * @return     the next byte of data, or {@code -1} if the end of the
226      *             file is reached.
227      * @throws     IOException {@inheritDoc}
228      */
229     @Override
230     public int read() throws IOException {
231         long comp = Blocker.begin();
232         try {
233             return read0();
234         } finally {
235             Blocker.end(comp);
236         }
237     }
238 
239     private native int read0() throws IOException;
240 
241     /**
242      * Reads a subarray as a sequence of bytes.
243      * @param     b the data to be written
244      * @param     off the start offset in the data
245      * @param     len the number of bytes that are written
246      * @throws    IOException If an I/O error has occurred.
247      */
248     private native int readBytes(byte[] b, int off, int len) throws IOException;
249 
250     /**
251      * Reads up to {@code b.length} bytes of data from this input
252      * stream into an array of bytes. This method blocks until some input
253      * is available.
254      *
255      * @param      b   {@inheritDoc}
256      * @return     the total number of bytes read into the buffer, or
257      *             {@code -1} if there is no more data because the end of
258      *             the file has been reached.
259      * @throws     IOException  if an I/O error occurs.
260      */
261     @Override
262     public int read(byte[] b) throws IOException {
263         long comp = Blocker.begin();
264         try {
265             return readBytes(b, 0, b.length);
266         } finally {
267             Blocker.end(comp);
268         }
269     }
270 
271     /**
272      * Reads up to {@code len} bytes of data from this input stream
273      * into an array of bytes. If {@code len} is not zero, the method
274      * blocks until some input is available; otherwise, no
275      * bytes are read and {@code 0} is returned.
276      *
277      * @param      b     {@inheritDoc}
278      * @param      off   {@inheritDoc}
279      * @param      len   {@inheritDoc}
280      * @return     {@inheritDoc}
281      * @throws     NullPointerException {@inheritDoc}
282      * @throws     IndexOutOfBoundsException {@inheritDoc}
283      * @throws     IOException  if an I/O error occurs.
284      */
285     @Override
286     public int read(byte[] b, int off, int len) throws IOException {
287         long comp = Blocker.begin();
288         try {
289             return readBytes(b, off, len);
290         } finally {
291             Blocker.end(comp);
292         }
293     }
294 
295     @Override
296     public byte[] readAllBytes() throws IOException {
297         long length = length();
298         long position = position();
299         long size = length - position;
300 
301         if (length <= 0 || size <= 0)
302             return super.readAllBytes();
303 
304         if (size > (long) Integer.MAX_VALUE) {
305             String msg =
306                 String.format("Required array size too large for %s: %d = %d - %d",
307                     path, size, length, position);
308             throw new OutOfMemoryError(msg);
309         }
310 
311         int capacity = (int)size;
312         byte[] buf = new byte[capacity];
313 
314         int nread = 0;
315         int n;
316         for (;;) {
317             // read to EOF which may read more or less than initial size, e.g.,
318             // file is truncated while we are reading
319             while ((n = read(buf, nread, capacity - nread)) > 0)
320                 nread += n;
321 
322             // if last call to read() returned -1, we are done; otherwise,
323             // try to read one more byte and if that fails we're done too
324             if (n < 0 || (n = read()) < 0)
325                 break;
326 
327             // one more byte was read; need to allocate a larger buffer
328             capacity = Math.max(ArraysSupport.newLength(capacity,
329                                                         1,         // min growth
330                                                         capacity), // pref growth
331                                 DEFAULT_BUFFER_SIZE);
332             buf = Arrays.copyOf(buf, capacity);
333             buf[nread++] = (byte)n;
334         }
335         return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
336     }
337 
338     @Override
339     public byte[] readNBytes(int len) throws IOException {
340         if (len < 0)
341             throw new IllegalArgumentException("len < 0");
342         if (len == 0)
343             return new byte[0];
344 
345         long length = length();
346         long position = position();
347         long size = length - position;
348 
349         if (length <= 0 || size <= 0)
350             return super.readNBytes(len);
351 
352         int capacity = (int)Math.min(len, size);
353         byte[] buf = new byte[capacity];
354 
355         int remaining = capacity;
356         int nread = 0;
357         int n;
358         do {
359             n = read(buf, nread, remaining);
360             if (n > 0) {
361                 nread += n;
362                 remaining -= n;
363             } else if (n == 0) {
364                 // Block until a byte is read or EOF is detected
365                 byte b = (byte)read();
366                 if (b == -1 )
367                     break;
368                 buf[nread++] = b;
369                 remaining--;
370             }
371         } while (n >= 0 && remaining > 0);
372         return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
373     }
374 
375     /**
376      * {@inheritDoc}
377      */
378     @Override
379     public long transferTo(OutputStream out) throws IOException {
380         long transferred = 0L;
381         if (out instanceof FileOutputStream fos) {
382             FileChannel fc = getChannel();
383             long pos = fc.position();
384             transferred = fc.transferTo(pos, Long.MAX_VALUE, fos.getChannel());
385             long newPos = pos + transferred;
386             fc.position(newPos);
387             if (newPos >= fc.size()) {
388                 return transferred;
389             }
390         }
391         try {
392             return Math.addExact(transferred, super.transferTo(out));
393         } catch (ArithmeticException ignore) {
394             return Long.MAX_VALUE;
395         }
396     }
397 
398     private long length() throws IOException {
399         long comp = Blocker.begin();
400         try {
401             return length0();
402         } finally {
403             Blocker.end(comp);
404         }
405     }
406     private native long length0() throws IOException;
407 
408     private long position() throws IOException {
409         long comp = Blocker.begin();
410         try {
411             return position0();
412         } finally {
413             Blocker.end(comp);
414         }
415     }
416     private native long position0() throws IOException;
417 
418     /**
419      * Skips over and discards {@code n} bytes of data from the
420      * input stream.
421      *
422      * <p>The {@code skip} method may, for a variety of
423      * reasons, end up skipping over some smaller number of bytes,
424      * possibly {@code 0}. If {@code n} is negative, the method
425      * will try to skip backwards. In case the backing file does not support
426      * backward skip at its current position, an {@code IOException} is
427      * thrown. The actual number of bytes skipped is returned. If it skips
428      * forwards, it returns a positive value. If it skips backwards, it
429      * returns a negative value.
430      *
431      * <p>This method may skip more bytes than what are remaining in the
432      * backing file. This produces no exception and the number of bytes skipped
433      * may include some number of bytes that were beyond the EOF of the
434      * backing file. Attempting to read from the stream after skipping past
435      * the end will result in -1 indicating the end of the file.
436      *
437      * @param      n   {@inheritDoc}
438      * @return     the actual number of bytes skipped.
439      * @throws     IOException  if n is negative, if the stream does not
440      *             support seek, or if an I/O error occurs.
441      */
442     @Override
443     public long skip(long n) throws IOException {
444         long comp = Blocker.begin();
445         try {
446             return skip0(n);
447         } finally {
448             Blocker.end(comp);
449         }
450     }
451 
452     private native long skip0(long n) throws IOException;
453 
454     /**
455      * Returns an estimate of the number of remaining bytes that can be read (or
456      * skipped over) from this input stream without blocking by the next
457      * invocation of a method for this input stream. Returns 0 when the file
458      * position is beyond EOF. The next invocation might be the same thread
459      * or another thread. A single read or skip of this many bytes will not
460      * block, but may read or skip fewer bytes.
461      *
462      * <p> In some cases, a non-blocking read (or skip) may appear to be
463      * blocked when it is merely slow, for example when reading large
464      * files over slow networks.
465      *
466      * @return     an estimate of the number of remaining bytes that can be read
467      *             (or skipped over) from this input stream without blocking.
468      * @throws     IOException  if this file input stream has been closed by calling
469      *             {@code close} or an I/O error occurs.
470      */
471     @Override
472     public int available() throws IOException {
473         long comp = Blocker.begin();
474         try {
475             return available0();
476         } finally {
477             Blocker.end(comp);
478         }
479     }
480 
481     private native int available0() throws IOException;
482 
483     /**
484      * Closes this file input stream and releases any system resources
485      * associated with the stream.
486      *
487      * <p> If this stream has an associated channel then the channel is closed
488      * as well.
489      *
490      * @apiNote
491      * Overriding {@link #close} to perform cleanup actions is reliable
492      * only when called directly or when called by try-with-resources.
493      *
494      * @implSpec
495      * Subclasses requiring that resource cleanup take place after a stream becomes
496      * unreachable should use the {@link java.lang.ref.Cleaner} mechanism.
497      *
498      * <p>
499      * If this stream has an associated channel then this method will close the
500      * channel, which in turn will close this stream. Subclasses that override
501      * this method should be prepared to handle possible reentrant invocation.
502      *
503      * @throws     IOException  {@inheritDoc}
504      */
505     @Override
506     public void close() throws IOException {
507         if (closed) {
508             return;
509         }
510         synchronized (closeLock) {
511             if (closed) {
512                 return;
513             }
514             closed = true;
515         }
516 
517         FileChannel fc = channel;
518         if (fc != null) {
519             // possible race with getChannel(), benign since
520             // FileChannel.close is final and idempotent
521             fc.close();
522         }
523 
524         fd.closeAll(new Closeable() {
525             public void close() throws IOException {
526                fd.close();
527            }
528         });
529     }
530 
531     /**
532      * Returns the {@code FileDescriptor}
533      * object  that represents the connection to
534      * the actual file in the file system being
535      * used by this {@code FileInputStream}.
536      *
537      * @return     the file descriptor object associated with this stream.
538      * @throws     IOException  if an I/O error occurs.
539      * @see        java.io.FileDescriptor
540      */
541     public final FileDescriptor getFD() throws IOException {
542         if (fd != null) {
543             return fd;
544         }
545         throw new IOException();
546     }
547 
548     /**
549      * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
550      * object associated with this file input stream.
551      *
552      * <p> The initial {@link java.nio.channels.FileChannel#position()
553      * position} of the returned channel will be equal to the
554      * number of bytes read from the file so far.  Reading bytes from this
555      * stream will increment the channel's position.  Changing the channel's
556      * position, either explicitly or by reading, will change this stream's
557      * file position.
558      *
559      * @return  the file channel associated with this file input stream
560      *
561      * @since 1.4
562      */
563     public FileChannel getChannel() {
564         FileChannel fc = this.channel;
565         if (fc == null) {
566             synchronized (this) {
567                 fc = this.channel;
568                 if (fc == null) {
569                     this.channel = fc = FileChannelImpl.open(fd, path, true,
570                         false, false, this);
571                     if (closed) {
572                         try {
573                             // possible race with close(), benign since
574                             // FileChannel.close is final and idempotent
575                             fc.close();
576                         } catch (IOException ioe) {
577                             throw new InternalError(ioe); // should not happen
578                         }
579                     }
580                 }
581             }
582         }
583         return fc;
584     }
585 
586     private static native void initIDs();
587 
588     static {
589         initIDs();
590     }
591 }
--- EOF ---