1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.  Oracle designates this
9  * particular file as subject to the "Classpath" exception as provided
10  * by Oracle in the LICENSE file that accompanied this code.
11  *
12  * This code is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * version 2 for more details (a copy is included in the LICENSE file that
16  * accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License version
19  * 2 along with this work; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23  * or visit www.oracle.com if you need additional information or have any
24  * questions.
25  */
26 
27 package java.io;
28 
29 import java.nio.channels.FileChannel;
30 
31 import dalvik.system.BlockGuard;
32 import dalvik.system.CloseGuard;
33 import sun.nio.ch.FileChannelImpl;
34 import libcore.io.IoBridge;
35 import libcore.io.IoTracker;
36 
37 /**
38  * A file output stream is an output stream for writing data to a
39  * <code>File</code> or to a <code>FileDescriptor</code>. Whether or not
40  * a file is available or may be created depends upon the underlying
41  * platform.  Some platforms, in particular, allow a file to be opened
42  * for writing by only one <tt>FileOutputStream</tt> (or other
43  * file-writing object) at a time.  In such situations the constructors in
44  * this class will fail if the file involved is already open.
45  *
46  * <p><code>FileOutputStream</code> is meant for writing streams of raw bytes
47  * such as image data. For writing streams of characters, consider using
48  * <code>FileWriter</code>.
49  *
50  * @author  Arthur van Hoff
51  * @see     java.io.File
52  * @see     java.io.FileDescriptor
53  * @see     java.io.FileInputStream
54  * @see     java.nio.file.Files#newOutputStream
55  * @since   JDK1.0
56  */
57 public
58 class FileOutputStream extends OutputStream
59 {
60     /**
61      * The system dependent file descriptor.
62      */
63     private final FileDescriptor fd;
64 
65     /**
66      * True if the file is opened for append.
67      */
68     private final boolean append;
69 
70     /**
71      * The associated channel, initialized lazily.
72      */
73     private FileChannel channel;
74 
75     private final Object closeLock = new Object();
76     private volatile boolean closed = false;
77 
78     /**
79      * The path of the referenced file
80      * (null if the stream is created with a file descriptor)
81      */
82     private final String path;
83 
84     private final CloseGuard guard = CloseGuard.get();
85     private final boolean isFdOwner;
86     private final IoTracker tracker = new IoTracker();
87 
88     /**
89      * Creates a file output stream to write to the file with the
90      * specified name. A new <code>FileDescriptor</code> object is
91      * created to represent this file connection.
92      * <p>
93      * First, if there is a security manager, its <code>checkWrite</code>
94      * method is called with <code>name</code> as its argument.
95      * <p>
96      * If the file exists but is a directory rather than a regular file, does
97      * not exist but cannot be created, or cannot be opened for any other
98      * reason then a <code>FileNotFoundException</code> is thrown.
99      *
100      * @param      name   the system-dependent filename
101      * @exception  FileNotFoundException  if the file exists but is a directory
102      *                   rather than a regular file, does not exist but cannot
103      *                   be created, or cannot be opened for any other reason
104      * @exception  SecurityException  if a security manager exists and its
105      *               <code>checkWrite</code> method denies write access
106      *               to the file.
107      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
108      */
FileOutputStream(String name)109     public FileOutputStream(String name) throws FileNotFoundException {
110         this(name != null ? new File(name) : null, false);
111     }
112 
113     /**
114      * Creates a file output stream to write to the file with the specified
115      * name.  If the second argument is <code>true</code>, then
116      * bytes will be written to the end of the file rather than the beginning.
117      * A new <code>FileDescriptor</code> object is created to represent this
118      * file connection.
119      * <p>
120      * First, if there is a security manager, its <code>checkWrite</code>
121      * method is called with <code>name</code> as its argument.
122      * <p>
123      * If the file exists but is a directory rather than a regular file, does
124      * not exist but cannot be created, or cannot be opened for any other
125      * reason then a <code>FileNotFoundException</code> is thrown.
126      *
127      * @param     name        the system-dependent file name
128      * @param     append      if <code>true</code>, then bytes will be written
129      *                   to the end of the file rather than the beginning
130      * @exception  FileNotFoundException  if the file exists but is a directory
131      *                   rather than a regular file, does not exist but cannot
132      *                   be created, or cannot be opened for any other reason.
133      * @exception  SecurityException  if a security manager exists and its
134      *               <code>checkWrite</code> method denies write access
135      *               to the file.
136      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
137      * @since     JDK1.1
138      */
FileOutputStream(String name, boolean append)139     public FileOutputStream(String name, boolean append)
140         throws FileNotFoundException
141     {
142         this(name != null ? new File(name) : null, append);
143     }
144 
145     /**
146      * Creates a file output stream to write to the file represented by
147      * the specified <code>File</code> object. A new
148      * <code>FileDescriptor</code> object is created to represent this
149      * file connection.
150      * <p>
151      * First, if there is a security manager, its <code>checkWrite</code>
152      * method is called with the path represented by the <code>file</code>
153      * argument as its argument.
154      * <p>
155      * If the file exists but is a directory rather than a regular file, does
156      * not exist but cannot be created, or cannot be opened for any other
157      * reason then a <code>FileNotFoundException</code> is thrown.
158      *
159      * @param      file               the file to be opened for writing.
160      * @exception  FileNotFoundException  if the file exists but is a directory
161      *                   rather than a regular file, does not exist but cannot
162      *                   be created, or cannot be opened for any other reason
163      * @exception  SecurityException  if a security manager exists and its
164      *               <code>checkWrite</code> method denies write access
165      *               to the file.
166      * @see        java.io.File#getPath()
167      * @see        java.lang.SecurityException
168      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
169      */
FileOutputStream(File file)170     public FileOutputStream(File file) throws FileNotFoundException {
171         this(file, false);
172     }
173 
174     /**
175      * Creates a file output stream to write to the file represented by
176      * the specified <code>File</code> object. If the second argument is
177      * <code>true</code>, then bytes will be written to the end of the file
178      * rather than the beginning. A new <code>FileDescriptor</code> object is
179      * created to represent this file connection.
180      * <p>
181      * First, if there is a security manager, its <code>checkWrite</code>
182      * method is called with the path represented by the <code>file</code>
183      * argument as its argument.
184      * <p>
185      * If the file exists but is a directory rather than a regular file, does
186      * not exist but cannot be created, or cannot be opened for any other
187      * reason then a <code>FileNotFoundException</code> is thrown.
188      *
189      * @param      file               the file to be opened for writing.
190      * @param     append      if <code>true</code>, then bytes will be written
191      *                   to the end of the file rather than the beginning
192      * @exception  FileNotFoundException  if the file exists but is a directory
193      *                   rather than a regular file, does not exist but cannot
194      *                   be created, or cannot be opened for any other reason
195      * @exception  SecurityException  if a security manager exists and its
196      *               <code>checkWrite</code> method denies write access
197      *               to the file.
198      * @see        java.io.File#getPath()
199      * @see        java.lang.SecurityException
200      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
201      * @since 1.4
202      */
FileOutputStream(File file, boolean append)203     public FileOutputStream(File file, boolean append)
204         throws FileNotFoundException
205     {
206         String name = (file != null ? file.getPath() : null);
207         SecurityManager security = System.getSecurityManager();
208         if (security != null) {
209             security.checkWrite(name);
210         }
211         if (name == null) {
212             throw new NullPointerException();
213         }
214         if (file.isInvalid()) {
215             throw new FileNotFoundException("Invalid file path");
216         }
217         this.fd = new FileDescriptor();
218         this.append = append;
219         this.path = name;
220         this.isFdOwner = true;
221 
222         BlockGuard.getThreadPolicy().onWriteToDisk();
223         open(name, append);
224         guard.open("close");
225     }
226 
227     /**
228      * Creates a file output stream to write to the specified file
229      * descriptor, which represents an existing connection to an actual
230      * file in the file system.
231      * <p>
232      * First, if there is a security manager, its <code>checkWrite</code>
233      * method is called with the file descriptor <code>fdObj</code>
234      * argument as its argument.
235      * <p>
236      * If <code>fdObj</code> is null then a <code>NullPointerException</code>
237      * is thrown.
238      * <p>
239      * This constructor does not throw an exception if <code>fdObj</code>
240      * is {@link java.io.FileDescriptor#valid() invalid}.
241      * However, if the methods are invoked on the resulting stream to attempt
242      * I/O on the stream, an <code>IOException</code> is thrown.
243      *
244      * @param      fdObj   the file descriptor to be opened for writing
245      * @exception  SecurityException  if a security manager exists and its
246      *               <code>checkWrite</code> method denies
247      *               write access to the file descriptor
248      * @see        java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
249      */
FileOutputStream(FileDescriptor fdObj)250     public FileOutputStream(FileDescriptor fdObj) {
251         this(fdObj, false /* isOwner */);
252     }
253 
254     /**
255      * Internal constructor for {@code FileOutputStream} objects where the file descriptor
256      * is owned by this tream.
257      *
258      * @hide
259      */
FileOutputStream(FileDescriptor fdObj, boolean isFdOwner)260     public FileOutputStream(FileDescriptor fdObj, boolean isFdOwner) {
261         if (fdObj == null) {
262             throw new NullPointerException("fdObj == null");
263         }
264 
265         this.fd = fdObj;
266         this.path = null;
267         this.append = false;
268         this.isFdOwner = isFdOwner;
269     }
270 
271     /**
272      * Opens a file, with the specified name, for overwriting or appending.
273      * @param name name of file to be opened
274      * @param append whether the file is to be opened in append mode
275      */
open0(String name, boolean append)276     private native void open0(String name, boolean append)
277         throws FileNotFoundException;
278 
279     // wrap native call to allow instrumentation
280     /**
281      * Opens a file, with the specified name, for overwriting or appending.
282      * @param name name of file to be opened
283      * @param append whether the file is to be opened in append mode
284      */
open(String name, boolean append)285     private void open(String name, boolean append)
286         throws FileNotFoundException {
287         open0(name, append);
288     }
289 
290     /**
291      * Writes the specified byte to this file output stream. Implements
292      * the <code>write</code> method of <code>OutputStream</code>.
293      *
294      * @param      b   the byte to be written.
295      * @exception  IOException  if an I/O error occurs.
296      */
write(int b)297     public void write(int b) throws IOException {
298         write(new byte[] { (byte) b }, 0, 1);
299     }
300 
301     /**
302      * Writes <code>b.length</code> bytes from the specified byte array
303      * to this file output stream.
304      *
305      * @param      b   the data.
306      * @exception  IOException  if an I/O error occurs.
307      */
write(byte b[])308     public void write(byte b[]) throws IOException {
309         write(b, 0, b.length);
310     }
311 
312     /**
313      * Writes <code>len</code> bytes from the specified byte array
314      * starting at offset <code>off</code> to this file output stream.
315      *
316      * @param      b     the data.
317      * @param      off   the start offset in the data.
318      * @param      len   the number of bytes to write.
319      * @exception  IOException  if an I/O error occurs.
320      */
write(byte b[], int off, int len)321     public void write(byte b[], int off, int len) throws IOException {
322         if (closed && len > 0) {
323             throw new IOException("Stream Closed");
324         }
325         tracker.trackIo(len);
326         IoBridge.write(fd, b, off, len);
327     }
328 
329     /**
330      * Closes this file output stream and releases any system resources
331      * associated with this stream. This file output stream may no longer
332      * be used for writing bytes.
333      *
334      * <p> If this stream has an associated channel then the channel is closed
335      * as well.
336      *
337      * @exception  IOException  if an I/O error occurs.
338      *
339      * @revised 1.4
340      * @spec JSR-51
341      */
close()342     public void close() throws IOException {
343         synchronized (closeLock) {
344             if (closed) {
345                 return;
346             }
347             closed = true;
348         }
349 
350         guard.close();
351 
352         if (channel != null) {
353             /*
354              * Decrement FD use count associated with the channel
355              * The use count is incremented whenever a new channel
356              * is obtained from this stream.
357              */
358             channel.close();
359         }
360 
361 
362         if (isFdOwner) {
363             IoBridge.closeAndSignalBlockedThreads(fd);
364         }
365     }
366 
367     /**
368      * Returns the file descriptor associated with this stream.
369      *
370      * @return  the <code>FileDescriptor</code> object that represents
371      *          the connection to the file in the file system being used
372      *          by this <code>FileOutputStream</code> object.
373      *
374      * @exception  IOException  if an I/O error occurs.
375      * @see        java.io.FileDescriptor
376      */
getFD()377      public final FileDescriptor getFD()  throws IOException {
378         if (fd != null) {
379             return fd;
380         }
381         throw new IOException();
382      }
383 
384     /**
385      * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
386      * object associated with this file output stream.
387      *
388      * <p> The initial {@link java.nio.channels.FileChannel#position()
389      * position} of the returned channel will be equal to the
390      * number of bytes written to the file so far unless this stream is in
391      * append mode, in which case it will be equal to the size of the file.
392      * Writing bytes to this stream will increment the channel's position
393      * accordingly.  Changing the channel's position, either explicitly or by
394      * writing, will change this stream's file position.
395      *
396      * @return  the file channel associated with this file output stream
397      *
398      * @since 1.4
399      * @spec JSR-51
400      */
getChannel()401     public FileChannel getChannel() {
402         synchronized (this) {
403             if (channel == null) {
404                 channel = FileChannelImpl.open(fd, path, false, true, append, this);
405             }
406             return channel;
407         }
408     }
409 
410     /**
411      * Cleans up the connection to the file, and ensures that the
412      * <code>close</code> method of this file output stream is
413      * called when there are no more references to this stream.
414      *
415      * @exception  IOException  if an I/O error occurs.
416      * @see        java.io.FileInputStream#close()
417      */
finalize()418     protected void finalize() throws IOException {
419         if (guard != null) {
420             guard.warnIfOpen();
421         }
422 
423         if (fd != null) {
424             if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
425                 flush();
426             } else {
427                 close();
428             }
429         }
430     }
431 }
432