1 /*
   2  * Copyright (c) 1994, 2019, 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</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     /**
  99      * Creates a file output stream to write to the file with the
 100      * specified name. A new <code>FileDescriptor</code> object is
 101      * created to represent this file connection.
 102      * <p>
 103      * First, if there is a security manager, its <code>checkWrite</code>
 104      * method is called with <code>name</code> as its argument.
 105      * <p>
 106      * If the file exists but is a directory rather than a regular file, does
 107      * not exist but cannot be created, or cannot be opened for any other
 108      * reason then a <code>FileNotFoundException</code> is thrown.
 109      *
 110      * @implSpec Invoking this constructor with the parameter {@code name} is
 111      * equivalent to invoking {@link #FileOutputStream(String,boolean)
 112      * new FileOutputStream(name, false)}.
 113      *
 114      * @param      name   the system-dependent filename
 115      * @throws     FileNotFoundException  if the file exists but is a directory
 116      *                   rather than a regular file, does not exist but cannot
 117      *                   be created, or cannot be opened for any other reason
 118      * @throws     SecurityException  if a security manager exists and its
 119      *               <code>checkWrite</code> method denies write access
 120      *               to the file.
 121      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
 122      */
 123     public FileOutputStream(String name) throws FileNotFoundException {
 124         this(name != null ? new File(name) : null, false);
 125     }
 126 
 127     /**
 128      * Creates a file output stream to write to the file with the specified
 129      * name.  If the second argument is <code>true</code>, then
 130      * bytes will be written to the end of the file rather than the beginning.
 131      * A new <code>FileDescriptor</code> object is created to represent this
 132      * file connection.
 133      * <p>
 134      * First, if there is a security manager, its <code>checkWrite</code>
 135      * method is called with <code>name</code> as its argument.
 136      * <p>
 137      * If the file exists but is a directory rather than a regular file, does
 138      * not exist but cannot be created, or cannot be opened for any other
 139      * reason then a <code>FileNotFoundException</code> is thrown.
 140      *
 141      * @param     name        the system-dependent file name
 142      * @param     append      if <code>true</code>, then bytes will be written
 143      *                   to the end of the file rather than the beginning
 144      * @throws     FileNotFoundException  if the file exists but is a directory
 145      *                   rather than a regular file, does not exist but cannot
 146      *                   be created, or cannot be opened for any other reason.
 147      * @throws     SecurityException  if a security manager exists and its
 148      *               <code>checkWrite</code> method denies write access
 149      *               to the file.
 150      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
 151      * @since     1.1
 152      */
 153     public FileOutputStream(String name, boolean append)
 154         throws FileNotFoundException
 155     {
 156         this(name != null ? new File(name) : null, append);
 157     }
 158 
 159     /**
 160      * Creates a file output stream to write to the file represented by
 161      * the specified <code>File</code> object. A new
 162      * <code>FileDescriptor</code> object is created to represent this
 163      * file connection.
 164      * <p>
 165      * First, if there is a security manager, its <code>checkWrite</code>
 166      * method is called with the path represented by the <code>file</code>
 167      * argument as its argument.
 168      * <p>
 169      * If the file exists but is a directory rather than a regular file, does
 170      * not exist but cannot be created, or cannot be opened for any other
 171      * reason then a <code>FileNotFoundException</code> is thrown.
 172      *
 173      * @param      file               the file to be opened for writing.
 174      * @throws     FileNotFoundException  if the file exists but is a directory
 175      *                   rather than a regular file, does not exist but cannot
 176      *                   be created, or cannot be opened for any other reason
 177      * @throws     SecurityException  if a security manager exists and its
 178      *               <code>checkWrite</code> method denies write access
 179      *               to the file.
 180      * @see        java.io.File#getPath()
 181      * @see        java.lang.SecurityException
 182      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
 183      */
 184     public FileOutputStream(File file) throws FileNotFoundException {
 185         this(file, false);
 186     }
 187 
 188     /**
 189      * Creates a file output stream to write to the file represented by
 190      * the specified <code>File</code> object. If the second argument is
 191      * <code>true</code>, then bytes will be written to the end of the file
 192      * rather than the beginning. A new <code>FileDescriptor</code> object is
 193      * created to represent this file connection.
 194      * <p>
 195      * First, if there is a security manager, its <code>checkWrite</code>
 196      * method is called with the path represented by the <code>file</code>
 197      * argument as its argument.
 198      * <p>
 199      * If the file exists but is a directory rather than a regular file, does
 200      * not exist but cannot be created, or cannot be opened for any other
 201      * reason then a <code>FileNotFoundException</code> is thrown.
 202      *
 203      * @param      file               the file to be opened for writing.
 204      * @param     append      if <code>true</code>, then bytes will be written
 205      *                   to the end of the file rather than the beginning
 206      * @throws     FileNotFoundException  if the file exists but is a directory
 207      *                   rather than a regular file, does not exist but cannot
 208      *                   be created, or cannot be opened for any other reason
 209      * @throws     SecurityException  if a security manager exists and its
 210      *               <code>checkWrite</code> method denies write access
 211      *               to the file.
 212      * @see        java.io.File#getPath()
 213      * @see        java.lang.SecurityException
 214      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
 215      * @since 1.4
 216      */
 217     public FileOutputStream(File file, boolean append)
 218         throws FileNotFoundException
 219     {
 220         String name = (file != null ? file.getPath() : null);
 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</code>
 245      * method is called with the file descriptor <code>fdObj</code>
 246      * argument as its argument.
 247      * <p>
 248      * If <code>fdObj</code> is null then a <code>NullPointerException</code>
 249      * is thrown.
 250      * <p>
 251      * This constructor does not throw an exception if <code>fdObj</code>
 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</code> 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</code> 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         SecurityManager security = System.getSecurityManager();
 264         if (fdObj == null) {
 265             throw new NullPointerException();
 266         }
 267         if (security != null) {
 268             security.checkWrite(fdObj);
 269         }
 270         this.fd = fdObj;
 271         this.path = null;
 272 
 273         fd.attach(this);
 274     }
 275 
 276     /**
 277      * Opens a file, with the specified name, for overwriting or appending.
 278      * @param name name of file to be opened
 279      * @param append whether the file is to be opened in append mode
 280      */
 281     private native void open0(String name, boolean append)
 282         throws FileNotFoundException;
 283 
 284     // wrap native call to allow instrumentation
 285     /**
 286      * Opens a file, with the specified name, for overwriting or appending.
 287      * @param name name of file to be opened
 288      * @param append whether the file is to be opened in append mode
 289      */
 290     private void open(String name, boolean append)
 291         throws FileNotFoundException {
 292         open0(name, append);
 293     }
 294 
 295     /**
 296      * Writes the specified byte to this file output stream.
 297      *
 298      * @param   b   the byte to be written.
 299      * @param   append   {@code true} if the write operation first
 300      *     advances the position to the end of file
 301      */
 302     private native void write(int b, boolean append) throws IOException;
 303 
 304     /**
 305      * Writes the specified byte to this file output stream. Implements
 306      * the <code>write</code> method of <code>OutputStream</code>.
 307      *
 308      * @param      b   the byte to be written.
 309      * @throws     IOException  if an I/O error occurs.
 310      */
 311     public void write(int b) throws IOException {
 312         write(b, fdAccess.getAppend(fd));
 313     }
 314 
 315     /**
 316      * Writes a sub array as a sequence of bytes.
 317      * @param b the data to be written
 318      * @param off the start offset in the data
 319      * @param len the number of bytes that are written
 320      * @param append {@code true} to first advance the position to the
 321      *     end of file
 322      * @throws    IOException If an I/O error has occurred.
 323      */
 324     private native void writeBytes(byte b[], int off, int len, boolean append)
 325         throws IOException;
 326 
 327     /**
 328      * Writes <code>b.length</code> bytes from the specified byte array
 329      * to this file output stream.
 330      *
 331      * @param      b   the data.
 332      * @throws     IOException  if an I/O error occurs.
 333      */
 334     public void write(byte b[]) throws IOException {
 335         writeBytes(b, 0, b.length, fdAccess.getAppend(fd));
 336     }
 337 
 338     /**
 339      * Writes <code>len</code> bytes from the specified byte array
 340      * starting at offset <code>off</code> to this file output stream.
 341      *
 342      * @param      b     the data.
 343      * @param      off   the start offset in the data.
 344      * @param      len   the number of bytes to write.
 345      * @throws     IOException  if an I/O error occurs.
 346      */
 347     public void write(byte b[], int off, int len) throws IOException {
 348         writeBytes(b, off, len, fdAccess.getAppend(fd));
 349     }
 350 
 351     /**
 352      * Closes this file output stream and releases any system resources
 353      * associated with this stream. This file output stream may no longer
 354      * be used for writing bytes.
 355      *
 356      * <p> If this stream has an associated channel then the channel is closed
 357      * as well.
 358      *
 359      * @apiNote
 360      * Overriding {@link #close} to perform cleanup actions is reliable
 361      * only when called directly or when called by try-with-resources.
 362      * Do not depend on finalization to invoke {@code close};
 363      * finalization is not reliable and is deprecated.
 364      * If cleanup of native resources is needed, other mechanisms such as
 365      * {@linkplain java.lang.ref.Cleaner} should be used.
 366      *
 367      * @throws     IOException  if an I/O error occurs.
 368      *
 369      * @revised 1.4
 370      * @spec JSR-51
 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</code> object that represents
 401      *          the connection to the file in the file system being used
 402      *          by this <code>FileOutputStream</code> 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      * @spec JSR-51
 430      */
 431     public FileChannel getChannel() {
 432         FileChannel fc = this.channel;
 433         if (fc == null) {
 434             synchronized (this) {
 435                 fc = this.channel;
 436                 if (fc == null) {
 437                     this.channel = fc = FileChannelImpl.open(fd, path, false,
 438                         true, false, this);
 439                     if (closed) {
 440                         try {
 441                             // possible race with close(), benign since
 442                             // FileChannel.close is final and idempotent
 443                             fc.close();
 444                         } catch (IOException ioe) {
 445                             throw new InternalError(ioe); // should not happen
 446                         }
 447                     }
 448                 }
 449             }
 450         }
 451         return fc;
 452     }
 453 
 454     private static native void initIDs();
 455 
 456     static {
 457         initIDs();
 458     }
 459 }