1 /*
   2  * Copyright (c) 1994, 2017, 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.misc.SharedSecrets;
  30 import jdk.internal.misc.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</code> or to a <code>FileDescriptor</code>. 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</code> is meant for writing streams of raw bytes
  44  * such as image data. For writing streams of characters, consider using
  45  * <code>FileWriter</code>.
  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
  70 class FileOutputStream extends OutputStream
  71 {
  72     /**
  73      * Access to FileDescriptor internals.
  74      */
  75     private static final JavaIOFileDescriptorAccess fdAccess =
  76         SharedSecrets.getJavaIOFileDescriptorAccess();
  77 
  78     /**
  79      * The system dependent file descriptor.
  80      */
  81     private final FileDescriptor fd;
  82 
  83     /**
  84      * The associated channel, initialized lazily.
  85      */
  86     private volatile FileChannel channel;
  87 
  88     /**
  89      * The path of the referenced file
  90      * (null if the stream is created with a file descriptor)
  91      */
  92     private final String path;
  93 
  94     private final Object closeLock = new Object();
  95 
  96     private volatile boolean closed;
  97 
  98     private final Object altFinalizer;
  99 
 100     /**
 101      * Creates a file output stream to write to the file with the
 102      * specified name. A new <code>FileDescriptor</code> object is
 103      * created to represent this file connection.
 104      * <p>
 105      * First, if there is a security manager, its <code>checkWrite</code>
 106      * method is called with <code>name</code> as its argument.
 107      * <p>
 108      * If the file exists but is a directory rather than a regular file, does
 109      * not exist but cannot be created, or cannot be opened for any other
 110      * reason then a <code>FileNotFoundException</code> is thrown.
 111      *
 112      * @implSpec Invoking this constructor with the parameter {@code name} is
 113      * equivalent to invoking {@link #FileOutputStream(String,boolean)
 114      * new FileOutputStream(name, false)}.
 115      *
 116      * @param      name   the system-dependent filename
 117      * @exception  FileNotFoundException  if the file exists but is a directory
 118      *                   rather than a regular file, does not exist but cannot
 119      *                   be created, or cannot be opened for any other reason
 120      * @exception  SecurityException  if a security manager exists and its
 121      *               <code>checkWrite</code> method denies write access
 122      *               to the file.
 123      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
 124      */
 125     public FileOutputStream(String name) throws FileNotFoundException {
 126         this(name != null ? new File(name) : null, false);
 127     }
 128 
 129     /**
 130      * Creates a file output stream to write to the file with the specified
 131      * name.  If the second argument is <code>true</code>, then
 132      * bytes will be written to the end of the file rather than the beginning.
 133      * A new <code>FileDescriptor</code> object is created to represent this
 134      * file connection.
 135      * <p>
 136      * First, if there is a security manager, its <code>checkWrite</code>
 137      * method is called with <code>name</code> as its argument.
 138      * <p>
 139      * If the file exists but is a directory rather than a regular file, does
 140      * not exist but cannot be created, or cannot be opened for any other
 141      * reason then a <code>FileNotFoundException</code> is thrown.
 142      *
 143      * @param     name        the system-dependent file name
 144      * @param     append      if <code>true</code>, then bytes will be written
 145      *                   to the end of the file rather than the beginning
 146      * @exception  FileNotFoundException  if the file exists but is a directory
 147      *                   rather than a regular file, does not exist but cannot
 148      *                   be created, or cannot be opened for any other reason.
 149      * @exception  SecurityException  if a security manager exists and its
 150      *               <code>checkWrite</code> method denies write access
 151      *               to the file.
 152      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
 153      * @since     1.1
 154      */
 155     public FileOutputStream(String name, boolean append)
 156         throws FileNotFoundException
 157     {
 158         this(name != null ? new File(name) : null, append);
 159     }
 160 
 161     /**
 162      * Creates a file output stream to write to the file represented by
 163      * the specified <code>File</code> object. A new
 164      * <code>FileDescriptor</code> object is created to represent this
 165      * file connection.
 166      * <p>
 167      * First, if there is a security manager, its <code>checkWrite</code>
 168      * method is called with the path represented by the <code>file</code>
 169      * argument as its argument.
 170      * <p>
 171      * If the file exists but is a directory rather than a regular file, does
 172      * not exist but cannot be created, or cannot be opened for any other
 173      * reason then a <code>FileNotFoundException</code> is thrown.
 174      *
 175      * @param      file               the file to be opened for writing.
 176      * @exception  FileNotFoundException  if the file exists but is a directory
 177      *                   rather than a regular file, does not exist but cannot
 178      *                   be created, or cannot be opened for any other reason
 179      * @exception  SecurityException  if a security manager exists and its
 180      *               <code>checkWrite</code> method denies write access
 181      *               to the file.
 182      * @see        java.io.File#getPath()
 183      * @see        java.lang.SecurityException
 184      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
 185      */
 186     public FileOutputStream(File file) throws FileNotFoundException {
 187         this(file, false);
 188     }
 189 
 190     /**
 191      * Creates a file output stream to write to the file represented by
 192      * the specified <code>File</code> object. If the second argument is
 193      * <code>true</code>, then bytes will be written to the end of the file
 194      * rather than the beginning. A new <code>FileDescriptor</code> object is
 195      * created to represent this file connection.
 196      * <p>
 197      * First, if there is a security manager, its <code>checkWrite</code>
 198      * method is called with the path represented by the <code>file</code>
 199      * argument as its argument.
 200      * <p>
 201      * If the file exists but is a directory rather than a regular file, does
 202      * not exist but cannot be created, or cannot be opened for any other
 203      * reason then a <code>FileNotFoundException</code> is thrown.
 204      *
 205      * @param      file               the file to be opened for writing.
 206      * @param     append      if <code>true</code>, then bytes will be written
 207      *                   to the end of the file rather than the beginning
 208      * @exception  FileNotFoundException  if the file exists but is a directory
 209      *                   rather than a regular file, does not exist but cannot
 210      *                   be created, or cannot be opened for any other reason
 211      * @exception  SecurityException  if a security manager exists and its
 212      *               <code>checkWrite</code> method denies write access
 213      *               to the file.
 214      * @see        java.io.File#getPath()
 215      * @see        java.lang.SecurityException
 216      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
 217      * @since 1.4
 218      */
 219     public FileOutputStream(File file, boolean append)
 220         throws FileNotFoundException
 221     {
 222         String name = (file != null ? file.getPath() : null);
 223         SecurityManager security = System.getSecurityManager();
 224         if (security != null) {
 225             security.checkWrite(name);
 226         }
 227         if (name == null) {
 228             throw new NullPointerException();
 229         }
 230         if (file.isInvalid()) {
 231             throw new FileNotFoundException("Invalid file path");
 232         }
 233         this.fd = new FileDescriptor();
 234         fd.attach(this);
 235         this.path = name;
 236 
 237         open(name, append);
 238         altFinalizer = getFinalizer(this);
 239         if (altFinalizer == null) {
 240             FileCleanable.register(fd);   // open sets the fd, register the cleanup
 241         }
 242     }
 243 
 244     /**
 245      * Creates a file output stream to write to the specified file
 246      * descriptor, which represents an existing connection to an actual
 247      * file in the file system.
 248      * <p>
 249      * First, if there is a security manager, its <code>checkWrite</code>
 250      * method is called with the file descriptor <code>fdObj</code>
 251      * argument as its argument.
 252      * <p>
 253      * If <code>fdObj</code> is null then a <code>NullPointerException</code>
 254      * is thrown.
 255      * <p>
 256      * This constructor does not throw an exception if <code>fdObj</code>
 257      * is {@link java.io.FileDescriptor#valid() invalid}.
 258      * However, if the methods are invoked on the resulting stream to attempt
 259      * I/O on the stream, an <code>IOException</code> is thrown.
 260      *
 261      * @param      fdObj   the file descriptor to be opened for writing
 262      * @exception  SecurityException  if a security manager exists and its
 263      *               <code>checkWrite</code> method denies
 264      *               write access to the file descriptor
 265      * @see        java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
 266      */
 267     public FileOutputStream(FileDescriptor fdObj) {
 268         SecurityManager security = System.getSecurityManager();
 269         if (fdObj == null) {
 270             throw new NullPointerException();
 271         }
 272         if (security != null) {
 273             security.checkWrite(fdObj);
 274         }
 275         this.fd = fdObj;
 276         this.path = null;
 277         this.altFinalizer = null;
 278 
 279         fd.attach(this);
 280     }
 281 
 282     /**
 283      * Opens a file, with the specified name, for overwriting or appending.
 284      * @param name name of file to be opened
 285      * @param append whether the file is to be opened in append mode
 286      */
 287     private native void open0(String name, boolean append)
 288         throws FileNotFoundException;
 289 
 290     // wrap native call to allow instrumentation
 291     /**
 292      * Opens a file, with the specified name, for overwriting or appending.
 293      * @param name name of file to be opened
 294      * @param append whether the file is to be opened in append mode
 295      */
 296     private void open(String name, boolean append)
 297         throws FileNotFoundException {
 298         open0(name, append);
 299     }
 300 
 301     /**
 302      * Writes the specified byte to this file output stream.
 303      *
 304      * @param   b   the byte to be written.
 305      * @param   append   {@code true} if the write operation first
 306      *     advances the position to the end of file
 307      */
 308     private native void write(int b, boolean append) throws IOException;
 309 
 310     /**
 311      * Writes the specified byte to this file output stream. Implements
 312      * the <code>write</code> method of <code>OutputStream</code>.
 313      *
 314      * @param      b   the byte to be written.
 315      * @exception  IOException  if an I/O error occurs.
 316      */
 317     public void write(int b) throws IOException {
 318         write(b, fdAccess.getAppend(fd));
 319     }
 320 
 321     /**
 322      * Writes a sub array as a sequence of bytes.
 323      * @param b the data to be written
 324      * @param off the start offset in the data
 325      * @param len the number of bytes that are written
 326      * @param append {@code true} to first advance the position to the
 327      *     end of file
 328      * @exception IOException If an I/O error has occurred.
 329      */
 330     private native void writeBytes(byte[] b, int off, int len, boolean append)
 331         throws IOException;
 332 
 333     /**
 334      * Writes <code>b.length</code> bytes from the specified byte array
 335      * to this file output stream.
 336      *
 337      * @param      b   the data.
 338      * @exception  IOException  if an I/O error occurs.
 339      */
 340     public void write(byte[] b) throws IOException {
 341         writeBytes(b, 0, b.length, fdAccess.getAppend(fd));
 342     }
 343 
 344     /**
 345      * Writes <code>len</code> bytes from the specified byte array
 346      * starting at offset <code>off</code> to this file output stream.
 347      *
 348      * @param      b     the data.
 349      * @param      off   the start offset in the data.
 350      * @param      len   the number of bytes to write.
 351      * @exception  IOException  if an I/O error occurs.
 352      */
 353     public void write(byte[] b, int off, int len) throws IOException {
 354         writeBytes(b, off, len, fdAccess.getAppend(fd));
 355     }
 356 
 357     /**
 358      * Closes this file output stream and releases any system resources
 359      * associated with this stream. This file output stream may no longer
 360      * be used for writing bytes.
 361      *
 362      * <p> If this stream has an associated channel then the channel is closed
 363      * as well.
 364      *
 365      * @apiNote
 366      * Overriding {@link #close} to perform cleanup actions is reliable
 367      * only when called directly or when called by try-with-resources.
 368      * Do not depend on finalization to invoke {@code close};
 369      * finalization is not reliable and is deprecated.
 370      * If cleanup of native resources is needed, other mechanisms such as
 371      * {@linkplain java.lang.ref.Cleaner} should be used.
 372      *
 373      * @exception  IOException  if an I/O error occurs.
 374      *
 375      * @revised 1.4
 376      * @spec JSR-51
 377      */
 378     public void close() throws IOException {
 379         if (closed) {
 380             return;
 381         }
 382         synchronized (closeLock) {
 383             if (closed) {
 384                 return;
 385             }
 386             closed = true;
 387         }
 388 
 389         FileChannel fc = channel;
 390         if (fc != null) {
 391             // possible race with getChannel(), benign since
 392             // FileChannel.close is final and idempotent
 393             fc.close();
 394         }
 395 
 396         fd.closeAll(new Closeable() {
 397             public void close() throws IOException {
 398                fd.close();
 399            }
 400         });
 401     }
 402 
 403     /**
 404      * Returns the file descriptor associated with this stream.
 405      *
 406      * @return  the <code>FileDescriptor</code> object that represents
 407      *          the connection to the file in the file system being used
 408      *          by this <code>FileOutputStream</code> object.
 409      *
 410      * @exception  IOException  if an I/O error occurs.
 411      * @see        java.io.FileDescriptor
 412      */
 413      public final FileDescriptor getFD()  throws IOException {
 414         if (fd != null) {
 415             return fd;
 416         }
 417         throw new IOException();
 418      }
 419 
 420     /**
 421      * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
 422      * object associated with this file output stream.
 423      *
 424      * <p> The initial {@link java.nio.channels.FileChannel#position()
 425      * position} of the returned channel will be equal to the
 426      * number of bytes written to the file so far unless this stream is in
 427      * append mode, in which case it will be equal to the size of the file.
 428      * Writing bytes to this stream will increment the channel's position
 429      * accordingly.  Changing the channel's position, either explicitly or by
 430      * writing, will change this stream's file position.
 431      *
 432      * @return  the file channel associated with this file output stream
 433      *
 434      * @since 1.4
 435      * @spec JSR-51
 436      */
 437     public FileChannel getChannel() {
 438         FileChannel fc = this.channel;
 439         if (fc == null) {
 440             synchronized (this) {
 441                 fc = this.channel;
 442                 if (fc == null) {
 443                     this.channel = fc = FileChannelImpl.open(fd, path, false,
 444                         true, false, this);
 445                     if (closed) {
 446                         try {
 447                             // possible race with close(), benign since
 448                             // FileChannel.close is final and idempotent
 449                             fc.close();
 450                         } catch (IOException ioe) {
 451                             throw new InternalError(ioe); // should not happen
 452                         }
 453                     }
 454                 }
 455             }
 456         }
 457         return fc;
 458     }
 459 
 460     /**
 461      * Cleans up the connection to the file, and ensures that the
 462      * {@link #close} method of this file output stream is
 463      * called when there are no more references to this stream.
 464      * The {@link #finalize} method does not call {@link #close} directly.
 465      *
 466      * @apiNote
 467      * To release resources used by this stream {@link #close} should be called
 468      * directly or by try-with-resources.
 469      *
 470      * @implSpec
 471      * If this FileOutputStream has been subclassed and the {@link #close}
 472      * method has been overridden, the {@link #close} method will be
 473      * called when the FileOutputStream is unreachable.
 474      * Otherwise, it is implementation specific how the resource cleanup described in
 475      * {@link #close} is performed.
 476      *
 477      * @deprecated The {@code finalize} method has been deprecated and will be removed.
 478      *     Subclasses that override {@code finalize} in order to perform cleanup
 479      *     should be modified to use alternative cleanup mechanisms and
 480      *     to remove the overriding {@code finalize} method.
 481      *     When overriding the {@code finalize} method, its implementation must explicitly
 482      *     ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
 483      *     See the specification for {@link Object#finalize()} for further
 484      *     information about migration options.
 485      *
 486      * @exception  IOException  if an I/O error occurs.
 487      * @see        java.io.FileInputStream#close()
 488      */
 489     @Deprecated(since="9", forRemoval = true)
 490     protected void finalize() throws IOException {
 491     }
 492 
 493     private static native void initIDs();
 494 
 495     static {
 496         initIDs();
 497     }
 498 
 499     /*
 500      * Returns a finalizer object if the FOS needs a finalizer; otherwise null.
 501      * If the FOS has a close method; it needs an AltFinalizer.
 502      */
 503     private static Object getFinalizer(FileOutputStream fos) {
 504         Class<?> clazz = fos.getClass();
 505         while (clazz != FileOutputStream.class) {
 506             try {
 507                 clazz.getDeclaredMethod("close");
 508                 return new AltFinalizer(fos);
 509             } catch (NoSuchMethodException nsme) {
 510                 // ignore
 511             }
 512             clazz = clazz.getSuperclass();
 513         }
 514         return null;
 515     }
 516 
 517     /**
 518      * Class to call {@code FileOutputStream.close} when finalized.
 519      * If finalization of the stream is needed, an instance is created
 520      * in its constructor(s).  When the set of instances
 521      * related to the stream is unreachable, the AltFinalizer performs
 522      * the needed call to the stream's {@code close} method.
 523      */
 524     static class AltFinalizer {
 525         private final FileOutputStream fos;
 526 
 527         AltFinalizer(FileOutputStream fos) {
 528             this.fos = fos;
 529         }
 530 
 531         @Override
 532         @SuppressWarnings("deprecation")
 533         protected final void finalize() {
 534             try {
 535                 if (fos.fd != null) {
 536                     if (fos.fd == FileDescriptor.out || fos.fd == FileDescriptor.err) {
 537                         // Subclass may override flush; otherwise it is no-op
 538                         fos.flush();
 539                     } else {
 540                         /* if fd is shared, the references in FileDescriptor
 541                          * will ensure that finalizer is only called when
 542                          * safe to do so. All references using the fd have
 543                          * become unreachable. We can call close()
 544                          */
 545                         fos.close();
 546                     }
 547                 }
 548             } catch (IOException ioe) {
 549                 // ignore
 550             }
 551         }
 552     }
 553 
 554 }