1 /*
  2  * Copyright (c) 1994, 2021, 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 jdk.internal.access.SharedSecrets;
 30 import jdk.internal.access.JavaIOFileDescriptorAccess;
 31 import sun.nio.ch.FileChannelImpl;
 32 
 33 
 34 /**
 35  * A file output stream is an output stream for writing data to a
 36  * {@code File} or to a {@code FileDescriptor}. Whether or not
 37  * a file is available or may be created depends upon the underlying
 38  * platform.  Some platforms, in particular, allow a file to be opened
 39  * for writing by only one {@code FileOutputStream} (or other
 40  * file-writing object) at a time.  In such situations the constructors in
 41  * this class will fail if the file involved is already open.
 42  *
 43  * <p>{@code FileOutputStream} is meant for writing streams of raw bytes
 44  * such as image data. For writing streams of characters, consider using
 45  * {@code FileWriter}.
 46  *
 47  * @apiNote
 48  * To release resources used by this stream {@link #close} should be called
 49  * directly or by try-with-resources. Subclasses are responsible for the cleanup
 50  * of resources acquired by the subclass.
 51  * Subclasses that override {@link #finalize} in order to perform cleanup
 52  * should be modified to use alternative cleanup mechanisms such as
 53  * {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
 54  *
 55  * @implSpec
 56  * If this FileOutputStream has been subclassed and the {@link #close}
 57  * method has been overridden, the {@link #close} method will be
 58  * called when the FileInputStream is unreachable.
 59  * Otherwise, it is implementation specific how the resource cleanup described in
 60  * {@link #close} is performed.
 61  *
 62  * @author  Arthur van Hoff
 63  * @see     java.io.File
 64  * @see     java.io.FileDescriptor
 65  * @see     java.io.FileInputStream
 66  * @see     java.nio.file.Files#newOutputStream
 67  * @since   1.0
 68  */
 69 public class FileOutputStream extends OutputStream
 70 {
 71     /**
 72      * Access to FileDescriptor internals.
 73      */
 74     private static final JavaIOFileDescriptorAccess fdAccess =
 75         SharedSecrets.getJavaIOFileDescriptorAccess();
 76 
 77     /**
 78      * The system dependent file descriptor.
 79      */
 80     private final FileDescriptor fd;
 81 
 82     /**
 83      * The associated channel, initialized lazily.
 84      */
 85     private volatile FileChannel channel;
 86 
 87     /**
 88      * The path of the referenced file
 89      * (null if the stream is created with a file descriptor)
 90      */
 91     private final String path;
 92 
 93     private final Object closeLock = new Object();
 94 
 95     private volatile boolean closed;
 96 
 97     /**
 98      * Creates a file output stream to write to the file with the
 99      * specified name. A new {@code FileDescriptor} object is
100      * created to represent this file connection.
101      * <p>
102      * First, if there is a security manager, its {@code checkWrite}
103      * method is called with {@code name} as its argument.
104      * <p>
105      * If the file exists but is a directory rather than a regular file, does
106      * not exist but cannot be created, or cannot be opened for any other
107      * reason then a {@code FileNotFoundException} is thrown.
108      *
109      * @implSpec Invoking this constructor with the parameter {@code name} is
110      * equivalent to invoking {@link #FileOutputStream(String,boolean)
111      * new FileOutputStream(name, false)}.
112      *
113      * @param      name   the system-dependent filename
114      * @throws     FileNotFoundException  if the file exists but is a directory
115      *                   rather than a regular file, does not exist but cannot
116      *                   be created, or cannot be opened for any other reason
117      * @throws     SecurityException  if a security manager exists and its
118      *               {@code checkWrite} method denies write access
119      *               to the file.
120      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
121      */
122     public FileOutputStream(String name) throws FileNotFoundException {
123         this(name != null ? new File(name) : null, false);
124     }
125 
126     /**
127      * Creates a file output stream to write to the file with the specified
128      * name.  If the second argument is {@code true}, then
129      * bytes will be written to the end of the file rather than the beginning.
130      * A new {@code FileDescriptor} object is created to represent this
131      * file connection.
132      * <p>
133      * First, if there is a security manager, its {@code checkWrite}
134      * method is called with {@code name} as its argument.
135      * <p>
136      * If the file exists but is a directory rather than a regular file, does
137      * not exist but cannot be created, or cannot be opened for any other
138      * reason then a {@code FileNotFoundException} is thrown.
139      *
140      * @param     name        the system-dependent file name
141      * @param     append      if {@code true}, then bytes will be written
142      *                   to the end of the file rather than the beginning
143      * @throws     FileNotFoundException  if the file exists but is a directory
144      *                   rather than a regular file, does not exist but cannot
145      *                   be created, or cannot be opened for any other reason.
146      * @throws     SecurityException  if a security manager exists and its
147      *               {@code checkWrite} method denies write access
148      *               to the file.
149      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
150      * @since     1.1
151      */
152     public FileOutputStream(String name, boolean append)
153         throws FileNotFoundException
154     {
155         this(name != null ? new File(name) : null, append);
156     }
157 
158     /**
159      * Creates a file output stream to write to the file represented by
160      * the specified {@code File} object. A new
161      * {@code FileDescriptor} object is created to represent this
162      * file connection.
163      * <p>
164      * First, if there is a security manager, its {@code checkWrite}
165      * method is called with the path represented by the {@code file}
166      * argument as its argument.
167      * <p>
168      * If the file exists but is a directory rather than a regular file, does
169      * not exist but cannot be created, or cannot be opened for any other
170      * reason then a {@code FileNotFoundException} is thrown.
171      *
172      * @param      file               the file to be opened for writing.
173      * @throws     FileNotFoundException  if the file exists but is a directory
174      *                   rather than a regular file, does not exist but cannot
175      *                   be created, or cannot be opened for any other reason
176      * @throws     SecurityException  if a security manager exists and its
177      *               {@code checkWrite} method denies write access
178      *               to the file.
179      * @see        java.io.File#getPath()
180      * @see        java.lang.SecurityException
181      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
182      */
183     public FileOutputStream(File file) throws FileNotFoundException {
184         this(file, false);
185     }
186 
187     /**
188      * Creates a file output stream to write to the file represented by
189      * the specified {@code File} object. If the second argument is
190      * {@code true}, then bytes will be written to the end of the file
191      * rather than the beginning. A new {@code FileDescriptor} object is
192      * created to represent this file connection.
193      * <p>
194      * First, if there is a security manager, its {@code checkWrite}
195      * method is called with the path represented by the {@code file}
196      * argument as its argument.
197      * <p>
198      * If the file exists but is a directory rather than a regular file, does
199      * not exist but cannot be created, or cannot be opened for any other
200      * reason then a {@code FileNotFoundException} is thrown.
201      *
202      * @param      file               the file to be opened for writing.
203      * @param     append      if {@code true}, then bytes will be written
204      *                   to the end of the file rather than the beginning
205      * @throws     FileNotFoundException  if the file exists but is a directory
206      *                   rather than a regular file, does not exist but cannot
207      *                   be created, or cannot be opened for any other reason
208      * @throws     SecurityException  if a security manager exists and its
209      *               {@code checkWrite} method denies write access
210      *               to the file.
211      * @see        java.io.File#getPath()
212      * @see        java.lang.SecurityException
213      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
214      * @since 1.4
215      */
216     public FileOutputStream(File file, boolean append)
217         throws FileNotFoundException
218     {
219         String name = (file != null ? file.getPath() : null);
220         @SuppressWarnings("removal")
221         SecurityManager security = System.getSecurityManager();
222         if (security != null) {
223             security.checkWrite(name);
224         }
225         if (name == null) {
226             throw new NullPointerException();
227         }
228         if (file.isInvalid()) {
229             throw new FileNotFoundException("Invalid file path");
230         }
231         this.fd = new FileDescriptor();
232         fd.attach(this);
233         this.path = name;
234 
235         open(name, append);
236         FileCleanable.register(fd);   // open sets the fd, register the cleanup
237     }
238 
239     /**
240      * Creates a file output stream to write to the specified file
241      * descriptor, which represents an existing connection to an actual
242      * file in the file system.
243      * <p>
244      * First, if there is a security manager, its {@code checkWrite}
245      * method is called with the file descriptor {@code fdObj}
246      * argument as its argument.
247      * <p>
248      * If {@code fdObj} is null then a {@code NullPointerException}
249      * is thrown.
250      * <p>
251      * This constructor does not throw an exception if {@code fdObj}
252      * is {@link java.io.FileDescriptor#valid() invalid}.
253      * However, if the methods are invoked on the resulting stream to attempt
254      * I/O on the stream, an {@code IOException} is thrown.
255      *
256      * @param      fdObj   the file descriptor to be opened for writing
257      * @throws     SecurityException  if a security manager exists and its
258      *               {@code checkWrite} method denies
259      *               write access to the file descriptor
260      * @see        java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
261      */
262     public FileOutputStream(FileDescriptor fdObj) {
263         @SuppressWarnings("removal")
264         SecurityManager security = System.getSecurityManager();
265         if (fdObj == null) {
266             throw new NullPointerException();
267         }
268         if (security != null) {
269             security.checkWrite(fdObj);
270         }
271         this.fd = fdObj;
272         this.path = null;
273 
274         fd.attach(this);
275     }
276 
277     /**
278      * Opens a file, with the specified name, for overwriting or appending.
279      * @param name name of file to be opened
280      * @param append whether the file is to be opened in append mode
281      */
282     private native void open0(String name, boolean append)
283         throws FileNotFoundException;
284 
285     // wrap native call to allow instrumentation
286     /**
287      * Opens a file, with the specified name, for overwriting or appending.
288      * @param name name of file to be opened
289      * @param append whether the file is to be opened in append mode
290      */
291     private void open(String name, boolean append)
292         throws FileNotFoundException {
293         open0(name, append);
294     }
295 
296     /**
297      * Writes the specified byte to this file output stream.
298      *
299      * @param   b   the byte to be written.
300      * @param   append   {@code true} if the write operation first
301      *     advances the position to the end of file
302      */
303     private native void write(int b, boolean append) throws IOException;
304 
305     /**
306      * Writes the specified byte to this file output stream. Implements
307      * the {@code write} method of {@code OutputStream}.
308      *
309      * @param      b   the byte to be written.
310      * @throws     IOException  if an I/O error occurs.
311      */
312     public void write(int b) throws IOException {
313         write(b, fdAccess.getAppend(fd));
314     }
315 
316     /**
317      * Writes a sub array as a sequence of bytes.
318      * @param b the data to be written
319      * @param off the start offset in the data
320      * @param len the number of bytes that are written
321      * @param append {@code true} to first advance the position to the
322      *     end of file
323      * @throws    IOException If an I/O error has occurred.
324      */
325     private native void writeBytes(byte[] b, int off, int len, boolean append)
326         throws IOException;
327 
328     /**
329      * Writes {@code b.length} bytes from the specified byte array
330      * to this file output stream.
331      *
332      * @param      b   the data.
333      * @throws     IOException  if an I/O error occurs.
334      */
335     public void write(byte[] b) throws IOException {
336         writeBytes(b, 0, b.length, fdAccess.getAppend(fd));
337     }
338 
339     /**
340      * Writes {@code len} bytes from the specified byte array
341      * starting at offset {@code off} to this file output stream.
342      *
343      * @param      b     the data.
344      * @param      off   the start offset in the data.
345      * @param      len   the number of bytes to write.
346      * @throws     IOException  if an I/O error occurs.
347      */
348     public void write(byte[] b, int off, int len) throws IOException {
349         writeBytes(b, off, len, fdAccess.getAppend(fd));
350     }
351 
352     /**
353      * Closes this file output stream and releases any system resources
354      * associated with this stream. This file output stream may no longer
355      * be used for writing bytes.
356      *
357      * <p> If this stream has an associated channel then the channel is closed
358      * as well.
359      *
360      * @apiNote
361      * Overriding {@link #close} to perform cleanup actions is reliable
362      * only when called directly or when called by try-with-resources.
363      * Do not depend on finalization to invoke {@code close};
364      * finalization is not reliable and is deprecated.
365      * If cleanup of native resources is needed, other mechanisms such as
366      * {@linkplain java.lang.ref.Cleaner} should be used.
367      *
368      * @throws     IOException  if an I/O error occurs.
369      *
370      * @revised 1.4
371      */
372     public void close() throws IOException {
373         if (closed) {
374             return;
375         }
376         synchronized (closeLock) {
377             if (closed) {
378                 return;
379             }
380             closed = true;
381         }
382 
383         FileChannel fc = channel;
384         if (fc != null) {
385             // possible race with getChannel(), benign since
386             // FileChannel.close is final and idempotent
387             fc.close();
388         }
389 
390         fd.closeAll(new Closeable() {
391             public void close() throws IOException {
392                fd.close();
393            }
394         });
395     }
396 
397     /**
398      * Returns the file descriptor associated with this stream.
399      *
400      * @return  the {@code FileDescriptor} object that represents
401      *          the connection to the file in the file system being used
402      *          by this {@code FileOutputStream} object.
403      *
404      * @throws     IOException  if an I/O error occurs.
405      * @see        java.io.FileDescriptor
406      */
407      public final FileDescriptor getFD()  throws IOException {
408         if (fd != null) {
409             return fd;
410         }
411         throw new IOException();
412      }
413 
414     /**
415      * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
416      * object associated with this file output stream.
417      *
418      * <p> The initial {@link java.nio.channels.FileChannel#position()
419      * position} of the returned channel will be equal to the
420      * number of bytes written to the file so far unless this stream is in
421      * append mode, in which case it will be equal to the size of the file.
422      * Writing bytes to this stream will increment the channel's position
423      * accordingly.  Changing the channel's position, either explicitly or by
424      * writing, will change this stream's file position.
425      *
426      * @return  the file channel associated with this file output stream
427      *
428      * @since 1.4
429      */
430     public FileChannel getChannel() {
431         FileChannel fc = this.channel;
432         if (fc == null) {
433             synchronized (this) {
434                 fc = this.channel;
435                 if (fc == null) {
436                     this.channel = fc = FileChannelImpl.open(fd, path, false,
437                         true, false, this);
438                     if (closed) {
439                         try {
440                             // possible race with close(), benign since
441                             // FileChannel.close is final and idempotent
442                             fc.close();
443                         } catch (IOException ioe) {
444                             throw new InternalError(ioe); // should not happen
445                         }
446                     }
447                 }
448             }
449         }
450         return fc;
451     }
452 
453     private static native void initIDs();
454 
455     static {
456         initIDs();
457     }
458 }