1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 1994, 2021, 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 static android.system.OsConstants.O_APPEND;
30 import static android.system.OsConstants.O_CREAT;
31 import static android.system.OsConstants.O_TRUNC;
32 import static android.system.OsConstants.O_WRONLY;
33 import java.nio.channels.FileChannel;
34 import jdk.internal.access.SharedSecrets;
35 import jdk.internal.access.JavaIOFileDescriptorAccess;
36 import sun.nio.ch.FileChannelImpl;
37 
38 import dalvik.annotation.optimization.ReachabilitySensitive;
39 import dalvik.system.BlockGuard;
40 import dalvik.system.CloseGuard;
41 import libcore.io.IoBridge;
42 import libcore.io.IoTracker;
43 import libcore.io.IoUtils;
44 
45 /**
46  * A file output stream is an output stream for writing data to a
47  * {@code File} or to a {@code FileDescriptor}. Whether or not
48  * a file is available or may be created depends upon the underlying
49  * platform.  Some platforms, in particular, allow a file to be opened
50  * for writing by only one {@code FileOutputStream} (or other
51  * file-writing object) at a time.  In such situations the constructors in
52  * this class will fail if the file involved is already open.
53  *
54  * <p>{@code FileOutputStream} is meant for writing streams of raw bytes
55  * such as image data. For writing streams of characters, consider using
56  * {@code FileWriter}.
57  *
58  * @apiNote
59  * To release resources used by this stream {@link #close} should be called
60  * directly or by try-with-resources. Subclasses are responsible for the cleanup
61  * of resources acquired by the subclass.
62  * Subclasses that override {@link #finalize} in order to perform cleanup
63  * should be modified to use alternative cleanup mechanisms such as
64  * {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
65  *
66  * @implSpec
67  * If this FileOutputStream has been subclassed and the {@link #close}
68  * method has been overridden, the {@link #close} method will be
69  * called when the FileInputStream is unreachable.
70  * Otherwise, it is implementation specific how the resource cleanup described in
71  * {@link #close} is performed.
72  *
73  * @author  Arthur van Hoff
74  * @see     java.io.File
75  * @see     java.io.FileDescriptor
76  * @see     java.io.FileInputStream
77  * @see     java.nio.file.Files#newOutputStream
78  * @since   1.0
79  */
80 public class FileOutputStream extends OutputStream
81 {
82     /**
83      * Access to FileDescriptor internals.
84      */
85     // Android-removed: Remove unused fdAccess.
86     // private static final JavaIOFileDescriptorAccess fdAccess =
87     //     SharedSecrets.getJavaIOFileDescriptorAccess();
88 
89     /**
90      * The system dependent file descriptor.
91      */
92     // Android-added: @ReachabilitySensitive
93     @ReachabilitySensitive
94     private final FileDescriptor fd;
95 
96     /**
97      * The associated channel, initialized lazily.
98      */
99     private volatile FileChannel channel;
100 
101     /**
102      * The path of the referenced file
103      * (null if the stream is created with a file descriptor)
104      */
105     private final String path;
106 
107     private final Object closeLock = new Object();
108 
109     private volatile boolean closed;
110 
111     // Android-added: CloseGuard support: Log if the stream is not closed.
112     @ReachabilitySensitive
113     private final CloseGuard guard = CloseGuard.get();
114 
115     // Android-added: Field for tracking whether the stream owns the underlying FileDescriptor.
116     private final boolean isFdOwner;
117 
118     // Android-added: Tracking of unbuffered I/O.
119     private final IoTracker tracker = new IoTracker();
120 
121     /**
122      * Creates a file output stream to write to the file with the
123      * specified name. A new {@code FileDescriptor} object is
124      * created to represent this file connection.
125      * <p>
126      * First, if there is a security manager, its {@code checkWrite}
127      * method is called with {@code name} as its argument.
128      * <p>
129      * If the file exists but is a directory rather than a regular file, does
130      * not exist but cannot be created, or cannot be opened for any other
131      * reason then a {@code FileNotFoundException} is thrown.
132      *
133      * @implSpec Invoking this constructor with the parameter {@code name} is
134      * equivalent to invoking {@link #FileOutputStream(String,boolean)
135      * new FileOutputStream(name, false)}.
136      *
137      * @param      name   the system-dependent filename
138      * @throws     FileNotFoundException  if the file exists but is a directory
139      *                   rather than a regular file, does not exist but cannot
140      *                   be created, or cannot be opened for any other reason
141      * @throws     SecurityException  if a security manager exists and its
142      *               {@code checkWrite} method denies write access
143      *               to the file.
144      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
145      */
FileOutputStream(String name)146     public FileOutputStream(String name) throws FileNotFoundException {
147         this(name != null ? new File(name) : null, false);
148     }
149 
150     /**
151      * Creates a file output stream to write to the file with the specified
152      * name.  If the second argument is {@code true}, then
153      * bytes will be written to the end of the file rather than the beginning.
154      * A new {@code FileDescriptor} object is created to represent this
155      * file connection.
156      * <p>
157      * First, if there is a security manager, its {@code checkWrite}
158      * method is called with {@code name} as its argument.
159      * <p>
160      * If the file exists but is a directory rather than a regular file, does
161      * not exist but cannot be created, or cannot be opened for any other
162      * reason then a {@code FileNotFoundException} is thrown.
163      *
164      * @param     name        the system-dependent file name
165      * @param     append      if {@code true}, then bytes will be written
166      *                   to the end of the file rather than the beginning
167      * @throws     FileNotFoundException  if the file exists but is a directory
168      *                   rather than a regular file, does not exist but cannot
169      *                   be created, or cannot be opened for any other reason.
170      * @throws     SecurityException  if a security manager exists and its
171      *               {@code checkWrite} method denies write access
172      *               to the file.
173      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
174      * @since     1.1
175      */
FileOutputStream(String name, boolean append)176     public FileOutputStream(String name, boolean append)
177         throws FileNotFoundException
178     {
179         this(name != null ? new File(name) : null, append);
180     }
181 
182     /**
183      * Creates a file output stream to write to the file represented by
184      * the specified {@code File} object. A new
185      * {@code FileDescriptor} object is created to represent this
186      * file connection.
187      * <p>
188      * First, if there is a security manager, its {@code checkWrite}
189      * method is called with the path represented by the {@code file}
190      * argument as its argument.
191      * <p>
192      * If the file exists but is a directory rather than a regular file, does
193      * not exist but cannot be created, or cannot be opened for any other
194      * reason then a {@code FileNotFoundException} is thrown.
195      *
196      * @param      file               the file to be opened for writing.
197      * @throws     FileNotFoundException  if the file exists but is a directory
198      *                   rather than a regular file, does not exist but cannot
199      *                   be created, or cannot be opened for any other reason
200      * @throws     SecurityException  if a security manager exists and its
201      *               {@code checkWrite} method denies write access
202      *               to the file.
203      * @see        java.io.File#getPath()
204      * @see        java.lang.SecurityException
205      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
206      */
FileOutputStream(File file)207     public FileOutputStream(File file) throws FileNotFoundException {
208         this(file, false);
209     }
210 
211     /**
212      * Creates a file output stream to write to the file represented by
213      * the specified {@code File} object. If the second argument is
214      * {@code true}, then bytes will be written to the end of the file
215      * rather than the beginning. A new {@code FileDescriptor} object is
216      * created to represent this file connection.
217      * <p>
218      * First, if there is a security manager, its {@code checkWrite}
219      * method is called with the path represented by the {@code file}
220      * argument as its argument.
221      * <p>
222      * If the file exists but is a directory rather than a regular file, does
223      * not exist but cannot be created, or cannot be opened for any other
224      * reason then a {@code FileNotFoundException} is thrown.
225      *
226      * @param      file               the file to be opened for writing.
227      * @param     append      if {@code true}, then bytes will be written
228      *                   to the end of the file rather than the beginning
229      * @throws     FileNotFoundException  if the file exists but is a directory
230      *                   rather than a regular file, does not exist but cannot
231      *                   be created, or cannot be opened for any other reason
232      * @throws     SecurityException  if a security manager exists and its
233      *               {@code checkWrite} method denies write access
234      *               to the file.
235      * @see        java.io.File#getPath()
236      * @see        java.lang.SecurityException
237      * @see        java.lang.SecurityManager#checkWrite(java.lang.String)
238      * @since 1.4
239      */
FileOutputStream(File file, boolean append)240     public FileOutputStream(File file, boolean append)
241         throws FileNotFoundException
242     {
243         String name = (file != null ? file.getPath() : null);
244         @SuppressWarnings("removal")
245         SecurityManager security = System.getSecurityManager();
246         if (security != null) {
247             security.checkWrite(name);
248         }
249         if (name == null) {
250             throw new NullPointerException();
251         }
252         if (file.isInvalid()) {
253             throw new FileNotFoundException("Invalid file path");
254         }
255         // BEGIN Android-changed: Open files using IoBridge to share BlockGuard & StrictMode logic.
256         // http://b/111268862
257         // this.fd = new FileDescriptor();
258         int flags = O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC);
259         this.fd = IoBridge.open(name, flags);
260         // END Android-changed: Open files using IoBridge to share BlockGuard & StrictMode logic.
261 
262         // Android-changed: Tracking mechanism for FileDescriptor sharing.
263         // fd.attach(this);
264         this.isFdOwner = true;
265 
266         this.path = name;
267 
268         // Android-removed: Open files using IoBridge to share BlockGuard & StrictMode logic.
269         // open(name, append);
270 
271         // Android-added: File descriptor ownership tracking.
272         IoUtils.setFdOwner(this.fd, this);
273 
274         // Android-added: CloseGuard support.
275         guard.open("close");
276         // Android-removed: TODO: Enable this when FileCleanable is imported to replace finalize().
277         // FileCleanable.register(fd);   // open sets the fd, register the cleanup
278     }
279 
280     // Android-removed: Documentation around SecurityException. Not thrown on Android.
281     // Android-changed: Added doc for the Android-specific file descriptor ownership.
282     /**
283      * Creates a file output stream to write to the specified file
284      * descriptor, which represents an existing connection to an actual
285      * file in the file system.
286      * <p>
287      * First, if there is a security manager, its {@code checkWrite}
288      * method is called with the file descriptor {@code fdObj}
289      * argument as its argument.
290      * <p>
291      * If {@code fdObj} is null then a {@code NullPointerException}
292      * is thrown.
293      * <p>
294      * This constructor does not throw an exception if {@code fdObj}
295      * is {@link java.io.FileDescriptor#valid() invalid}.
296      * However, if the methods are invoked on the resulting stream to attempt
297      * I/O on the stream, an {@code IOException} is thrown.
298      *
299      * @param      fdObj   the file descriptor to be opened for writing
300      * @throws     SecurityException  if a security manager exists and its
301      *               {@code checkWrite} method denies
302      *               write access to the file descriptor
303      * @see        java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
304      */
FileOutputStream(FileDescriptor fdObj)305     public FileOutputStream(FileDescriptor fdObj) {
306         // Android-changed: Delegate to added hidden constructor.
307         this(fdObj, false /* isOwner */);
308     }
309 
310     // Android-added: Internal/hidden constructor for specifying FileDescriptor ownership.
311     // Android-removed: SecurityManager calls.
312     /**
313      * Internal constructor for {@code FileOutputStream} objects where the file descriptor
314      * is owned by this tream.
315      *
316      * @hide
317      */
FileOutputStream(FileDescriptor fdObj, boolean isFdOwner)318     public FileOutputStream(FileDescriptor fdObj, boolean isFdOwner) {
319         if (fdObj == null) {
320             // Android-changed: Improved NullPointerException message.
321             throw new NullPointerException("fdObj == null");
322         }
323 
324         this.fd = fdObj;
325         this.path = null;
326 
327         // Android-changed: FileDescriptor ownership tracking mechanism.
328         // fd.attach(this);
329         this.isFdOwner = isFdOwner;
330         if (isFdOwner) {
331             IoUtils.setFdOwner(this.fd, this);
332         }
333     }
334 
335     // BEGIN Android-changed: Open files using IoBridge to share BlockGuard & StrictMode logic.
336     // http://b/112107427
337     /*
338     /**
339      * Opens a file, with the specified name, for overwriting or appending.
340      * @param name name of file to be opened
341      * @param append whether the file is to be opened in append mode
342      *
343     private native void open0(String name, boolean append)
344         throws FileNotFoundException;
345 
346     // wrap native call to allow instrumentation
347     /**
348      * Opens a file, with the specified name, for overwriting or appending.
349      * @param name name of file to be opened
350      * @param append whether the file is to be opened in append mode
351      *
352     private void open(String name, boolean append)
353         throws FileNotFoundException {
354         open0(name, append);
355     }
356     */
357     // END Android-changed: Open files using IoBridge to share BlockGuard & StrictMode logic.
358 
359     // Android-removed: write(int, boolean), use IoBridge instead.
360     /*
361     /**
362      * Writes the specified byte to this file output stream.
363      *
364      * @param   b   the byte to be written.
365      * @param   append   {@code true} if the write operation first
366      *     advances the position to the end of file
367      *
368     private native void write(int b, boolean append) throws IOException;
369     */
370 
371     /**
372      * Writes the specified byte to this file output stream. Implements
373      * the {@code write} method of {@code OutputStream}.
374      *
375      * @param      b   the byte to be written.
376      * @throws     IOException  if an I/O error occurs.
377      */
write(int b)378     public void write(int b) throws IOException {
379         // Android-changed: Write methods delegate to write(byte[],int,int) to share Android logic.
380         // write(b, fdAccess.getAppend(fd));
381         write(new byte[] { (byte) b }, 0, 1);
382     }
383 
384     // Android-removed: Write methods delegate to write(byte[],int,int) to share Android logic.
385     /*
386     /**
387      * Writes a sub array as a sequence of bytes.
388      * @param b the data to be written
389      * @param off the start offset in the data
390      * @param len the number of bytes that are written
391      * @param append {@code true} to first advance the position to the
392      *     end of file
393      * @throws    IOException If an I/O error has occurred.
394      *
395     private native void writeBytes(byte b[], int off, int len, boolean append)
396         throws IOException;
397     */
398 
399     /**
400      * Writes {@code b.length} bytes from the specified byte array
401      * to this file output stream.
402      *
403      * @param      b   the data.
404      * @throws     IOException  if an I/O error occurs.
405      */
write(byte b[])406     public void write(byte b[]) throws IOException {
407         // Android-changed: Write methods delegate to write(byte[],int,int) to share Android logic.
408         // writeBytes(b, 0, b.length, fdAccess.getAppend(fd));
409         write(b, 0, b.length);
410     }
411 
412     /**
413      * Writes {@code len} bytes from the specified byte array
414      * starting at offset {@code off} to this file output stream.
415      *
416      * @param      b     the data.
417      * @param      off   the start offset in the data.
418      * @param      len   the number of bytes to write.
419      * @throws     IOException  if an I/O error occurs.
420      */
write(byte b[], int off, int len)421     public void write(byte b[], int off, int len) throws IOException {
422         // Android-added: close() check before I/O.
423         if (closed && len > 0) {
424             throw new IOException("Stream Closed");
425         }
426 
427         // Android-added: Tracking of unbuffered I/O.
428         tracker.trackIo(len, IoTracker.Mode.WRITE);
429 
430         // Android-changed: Use IoBridge instead of calling native method.
431         // writeBytes(b, off, len, fdAccess.getAppend(fd));
432         IoBridge.write(fd, b, off, len);
433     }
434 
435     /**
436      * Closes this file output stream and releases any system resources
437      * associated with this stream. This file output stream may no longer
438      * be used for writing bytes.
439      *
440      * <p> If this stream has an associated channel then the channel is closed
441      * as well.
442      *
443      * @apiNote
444      * Overriding {@link #close} to perform cleanup actions is reliable
445      * only when called directly or when called by try-with-resources.
446      * Do not depend on finalization to invoke {@code close};
447      * finalization is not reliable and is deprecated.
448      * If cleanup of native resources is needed, other mechanisms such as
449      * {@linkplain java.lang.ref.Cleaner} should be used.
450      *
451      * @throws     IOException  if an I/O error occurs.
452      *
453      * @revised 1.4
454      */
close()455     public void close() throws IOException {
456         if (closed) {
457             return;
458         }
459         synchronized (closeLock) {
460             if (closed) {
461                 return;
462             }
463             closed = true;
464         }
465 
466         // Android-added: CloseGuard support.
467         guard.close();
468 
469         if (channel != null) {
470             channel.close();
471         }
472 
473         // BEGIN Android-changed: Close handling / notification of blocked threads.
474         /*
475         fd.closeAll(new Closeable() {
476             public void close() throws IOException {
477                fd.close();
478            }
479         });
480          */
481         if (isFdOwner) {
482             IoBridge.closeAndSignalBlockedThreads(fd);
483         }
484         // END Android-changed: Close handling / notification of blocked threads.
485     }
486 
487     /**
488      * Returns the file descriptor associated with this stream.
489      *
490      * @return  the {@code FileDescriptor} object that represents
491      *          the connection to the file in the file system being used
492      *          by this {@code FileOutputStream} object.
493      *
494      * @throws     IOException  if an I/O error occurs.
495      * @see        java.io.FileDescriptor
496      */
497      // Android-added: @ReachabilitySensitive
498      @ReachabilitySensitive
getFD()499      public final FileDescriptor getFD()  throws IOException {
500         if (fd != null) {
501             return fd;
502         }
503         throw new IOException();
504      }
505 
506     /**
507      * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
508      * object associated with this file output stream.
509      *
510      * <p> The initial {@link java.nio.channels.FileChannel#position()
511      * position} of the returned channel will be equal to the
512      * number of bytes written to the file so far unless this stream is in
513      * append mode, in which case it will be equal to the size of the file.
514      * Writing bytes to this stream will increment the channel's position
515      * accordingly.  Changing the channel's position, either explicitly or by
516      * writing, will change this stream's file position.
517      *
518      * @return  the file channel associated with this file output stream
519      *
520      * @since 1.4
521      */
getChannel()522     public FileChannel getChannel() {
523         FileChannel fc = this.channel;
524         if (fc == null) {
525             synchronized (this) {
526                 fc = this.channel;
527                 if (fc == null) {
528                     this.channel = fc = FileChannelImpl.open(fd, path, false,
529                         // Android-changed: TODO: remove patch when FileChannelImpl supports direct.
530                         // This patch should cause no behavior change as direct is off by default.
531                         // true, false, this);
532                         true, this);
533                     if (closed) {
534                         try {
535                             // possible race with close(), benign since
536                             // FileChannel.close is final and idempotent
537                             fc.close();
538                         } catch (IOException ioe) {
539                             throw new InternalError(ioe); // should not happen
540                         }
541                     }
542                 }
543             }
544         }
545         return fc;
546     }
547 
548     // TODO: Remove finalize() when FileCleanable is imported and used.
549     /**
550      * Cleans up the connection to the file, and ensures that the
551      * <code>close</code> method of this file output stream is
552      * called when there are no more references to this stream.
553      *
554      * @exception  IOException  if an I/O error occurs.
555      * @see        java.io.FileInputStream#close()
556      */
finalize()557     protected void finalize() throws IOException {
558         // Android-added: CloseGuard support.
559         if (guard != null) {
560             guard.warnIfOpen();
561         }
562 
563         if (fd != null) {
564             if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
565                 flush();
566             } else {
567                 // Android-removed: Obsoleted comment about shared FileDescriptor handling.
568                 close();
569             }
570         }
571     }
572 
573     // BEGIN Android-removed: Unused code.
574     /*
575     private native void close0() throws IOException;
576 
577     private static native void initIDs();
578 
579     static {
580         initIDs();
581     }
582     */
583     // END Android-removed: Unused code.
584 
585 }
586