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 jdk.internal.misc.Blocker;
 32 import sun.nio.ch.FileChannelImpl;
 33 
 34 
 35 /**
 36  * A file output stream is an output stream for writing data to a
 37  * {@code File} or to a {@code FileDescriptor}. Whether or not
 38  * a file is available or may be created depends upon the underlying
 39  * platform.  Some platforms, in particular, allow a file to be opened
 40  * for writing by only one {@code FileOutputStream} (or other
 41  * file-writing object) at a time.  In such situations the constructors in
 42  * this class will fail if the file involved is already open.
 43  *
 44  * <p>{@code FileOutputStream} is meant for writing streams of raw bytes
 45  * such as image data. For writing streams of characters, consider using
 46  * {@code FileWriter}.
 47  *
 48  * @apiNote
 49  * To release resources used by this stream {@link #close} should be called
 50  * directly or by try-with-resources. Subclasses are responsible for the cleanup
 51  * of resources acquired by the subclass.
 52  * Subclasses that override {@link #finalize} in order to perform cleanup
 53  * should be modified to use alternative cleanup mechanisms such as
 54  * {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
 55  *
 56  * @implSpec
 57  * If this FileOutputStream has been subclassed and the {@link #close}
 58  * method has been overridden, the {@link #close} method will be
 59  * called when the FileInputStream is unreachable.
 60  * Otherwise, it is implementation specific how the resource cleanup described in
 61  * {@link #close} is performed.
 62  *
 63  * @author  Arthur van Hoff
 64  * @see     java.io.File
 65  * @see     java.io.FileDescriptor
 66  * @see     java.io.FileInputStream
 67  * @see     java.nio.file.Files#newOutputStream
 68  * @since   1.0
 69  */
 70 public 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} object is
101      * created to represent this file connection.
102      * <p>
103      * First, if there is a security manager, its {@code checkWrite}
104      * method is called with {@code name} 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} 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} 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}, then
130      * bytes will be written to the end of the file rather than the beginning.
131      * A new {@code FileDescriptor} object is created to represent this
132      * file connection.
133      * <p>
134      * First, if there is a security manager, its {@code checkWrite}
135      * method is called with {@code name} 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} is thrown.
140      *
141      * @param     name        the system-dependent file name
142      * @param     append      if {@code true}, 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} 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} object. A new
162      * {@code FileDescriptor} object is created to represent this
163      * file connection.
164      * <p>
165      * First, if there is a security manager, its {@code checkWrite}
166      * method is called with the path represented by the {@code file}
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} 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} 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} object. If the second argument is
191      * {@code true}, then bytes will be written to the end of the file
192      * rather than the beginning. A new {@code FileDescriptor} object is
193      * created to represent this file connection.
194      * <p>
195      * First, if there is a security manager, its {@code checkWrite}
196      * method is called with the path represented by the {@code file}
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} is thrown.
202      *
203      * @param      file               the file to be opened for writing.
204      * @param     append      if {@code true}, 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} 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         @SuppressWarnings("removal")
222         SecurityManager security = System.getSecurityManager();
223         if (security != null) {
224             security.checkWrite(name);
225         }
226         if (name == null) {
227             throw new NullPointerException();
228         }
229         if (file.isInvalid()) {
230             throw new FileNotFoundException("Invalid file path");
231         }
232         this.fd = new FileDescriptor();
233         fd.attach(this);
234         this.path = name;
235 
236         open(name, append);
237         FileCleanable.register(fd);   // open sets the fd, register the cleanup
238     }
239 
240     /**
241      * Creates a file output stream to write to the specified file
242      * descriptor, which represents an existing connection to an actual
243      * file in the file system.
244      * <p>
245      * First, if there is a security manager, its {@code checkWrite}
246      * method is called with the file descriptor {@code fdObj}
247      * argument as its argument.
248      * <p>
249      * If {@code fdObj} is null then a {@code NullPointerException}
250      * is thrown.
251      * <p>
252      * This constructor does not throw an exception if {@code fdObj}
253      * is {@link java.io.FileDescriptor#valid() invalid}.
254      * However, if the methods are invoked on the resulting stream to attempt
255      * I/O on the stream, an {@code IOException} is thrown.
256      *
257      * @param      fdObj   the file descriptor to be opened for writing
258      * @throws     SecurityException  if a security manager exists and its
259      *               {@code checkWrite} method denies
260      *               write access to the file descriptor
261      * @see        java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
262      */
263     public FileOutputStream(FileDescriptor fdObj) {
264         @SuppressWarnings("removal")
265         SecurityManager security = System.getSecurityManager();
266         if (fdObj == null) {
267             throw new NullPointerException();
268         }
269         if (security != null) {
270             security.checkWrite(fdObj);
271         }
272         this.fd = fdObj;
273         this.path = null;
274 
275         fd.attach(this);
276     }
277 
278     /**
279      * Opens a file, with the specified name, for overwriting or appending.
280      * @param name name of file to be opened
281      * @param append whether the file is to be opened in append mode
282      */
283     private native void open0(String name, boolean append)
284         throws FileNotFoundException;
285 
286     // wrap native call to allow instrumentation
287     /**
288      * Opens a file, with the specified name, for overwriting or appending.
289      * @param name name of file to be opened
290      * @param append whether the file is to be opened in append mode
291      */
292     private void open(String name, boolean append) throws FileNotFoundException {
293         if (Thread.currentThread().isVirtual()) {
294             Blocker.managedBlock(() -> open0(name, append));
295         } else {
296             open0(name, append);
297         }
298     }
299 
300     /**
301      * Writes the specified byte to this file output stream.
302      *
303      * @param   b   the byte to be written.
304      * @param   append   {@code true} if the write operation first
305      *     advances the position to the end of file
306      */
307     private native void write(int b, boolean append) throws IOException;
308 
309     /**
310      * Writes the specified byte to this file output stream. Implements
311      * the {@code write} method of {@code OutputStream}.
312      *
313      * @param      b   the byte to be written.
314      * @throws     IOException  if an I/O error occurs.
315      */
316     public void write(int b) throws IOException {
317         boolean append = fdAccess.getAppend(fd);
318         if (Thread.currentThread().isVirtual()) {
319             Blocker.managedBlock(() -> write(b, append));
320         } else {
321             write(b, append);
322         }
323     }
324 
325     /**
326      * Writes a sub array as a sequence of bytes.
327      * @param b the data to be written
328      * @param off the start offset in the data
329      * @param len the number of bytes that are written
330      * @param append {@code true} to first advance the position to the
331      *     end of file
332      * @throws    IOException If an I/O error has occurred.
333      */
334     private native void writeBytes(byte[] b, int off, int len, boolean append)
335         throws IOException;
336 
337     /**
338      * Writes {@code b.length} bytes from the specified byte array
339      * to this file output stream.
340      *
341      * @param      b   the data.
342      * @throws     IOException  if an I/O error occurs.
343      */
344     public void write(byte[] b) throws IOException {
345         boolean append = fdAccess.getAppend(fd);
346         if (Thread.currentThread().isVirtual()) {
347             Blocker.managedBlock(() -> writeBytes(b, 0, b.length, append));
348         } else {
349             writeBytes(b, 0, b.length, append);
350         }
351     }
352 
353     /**
354      * Writes {@code len} bytes from the specified byte array
355      * starting at offset {@code off} to this file output stream.
356      *
357      * @param      b     the data.
358      * @param      off   the start offset in the data.
359      * @param      len   the number of bytes to write.
360      * @throws     IOException  if an I/O error occurs.
361      */
362     public void write(byte[] b, int off, int len) throws IOException {
363         boolean append = fdAccess.getAppend(fd);
364         if (Thread.currentThread().isVirtual()) {
365             Blocker.managedBlock(() -> writeBytes(b, off, len, append));
366         } else {
367             writeBytes(b, off, len, append);
368         }
369     }
370 
371     /**
372      * Closes this file output stream and releases any system resources
373      * associated with this stream. This file output stream may no longer
374      * be used for writing bytes.
375      *
376      * <p> If this stream has an associated channel then the channel is closed
377      * as well.
378      *
379      * @apiNote
380      * Overriding {@link #close} to perform cleanup actions is reliable
381      * only when called directly or when called by try-with-resources.
382      * Do not depend on finalization to invoke {@code close};
383      * finalization is not reliable and is deprecated.
384      * If cleanup of native resources is needed, other mechanisms such as
385      * {@linkplain java.lang.ref.Cleaner} should be used.
386      *
387      * @throws     IOException  if an I/O error occurs.
388      *
389      * @revised 1.4
390      */
391     public void close() throws IOException {
392         if (closed) {
393             return;
394         }
395         synchronized (closeLock) {
396             if (closed) {
397                 return;
398             }
399             closed = true;
400         }
401 
402         FileChannel fc = channel;
403         if (fc != null) {
404             // possible race with getChannel(), benign since
405             // FileChannel.close is final and idempotent
406             fc.close();
407         }
408 
409         fd.closeAll(new Closeable() {
410             public void close() throws IOException {
411                fd.close();
412            }
413         });
414     }
415 
416     /**
417      * Returns the file descriptor associated with this stream.
418      *
419      * @return  the {@code FileDescriptor} object that represents
420      *          the connection to the file in the file system being used
421      *          by this {@code FileOutputStream} object.
422      *
423      * @throws     IOException  if an I/O error occurs.
424      * @see        java.io.FileDescriptor
425      */
426      public final FileDescriptor getFD()  throws IOException {
427         if (fd != null) {
428             return fd;
429         }
430         throw new IOException();
431      }
432 
433     /**
434      * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
435      * object associated with this file output stream.
436      *
437      * <p> The initial {@link java.nio.channels.FileChannel#position()
438      * position} of the returned channel will be equal to the
439      * number of bytes written to the file so far unless this stream is in
440      * append mode, in which case it will be equal to the size of the file.
441      * Writing bytes to this stream will increment the channel's position
442      * accordingly.  Changing the channel's position, either explicitly or by
443      * writing, will change this stream's file position.
444      *
445      * @return  the file channel associated with this file output stream
446      *
447      * @since 1.4
448      */
449     public FileChannel getChannel() {
450         FileChannel fc = this.channel;
451         if (fc == null) {
452             synchronized (this) {
453                 fc = this.channel;
454                 if (fc == null) {
455                     this.channel = fc = FileChannelImpl.open(fd, path, false,
456                         true, false, this);
457                     if (closed) {
458                         try {
459                             // possible race with close(), benign since
460                             // FileChannel.close is final and idempotent
461                             fc.close();
462                         } catch (IOException ioe) {
463                             throw new InternalError(ioe); // should not happen
464                         }
465                     }
466                 }
467             }
468         }
469         return fc;
470     }
471 
472     private static native void initIDs();
473 
474     static {
475         initIDs();
476     }
477 }
--- EOF ---