• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      * <p>
299      * Android-specific warning: {@link #close()} method doesn't close the {@code fdObj} provided,
300      * because this object doesn't own the file descriptor, but the caller does. The caller can
301      * call {@link android.system.Os#close(FileDescriptor)} to close the fd.
302      *
303      * @param      fdObj   the file descriptor to be opened for writing
304      * @throws     SecurityException  if a security manager exists and its
305      *               {@code checkWrite} method denies
306      *               write access to the file descriptor
307      * @see        java.lang.SecurityManager#checkWrite(java.io.FileDescriptor)
308      */
FileOutputStream(FileDescriptor fdObj)309     public FileOutputStream(FileDescriptor fdObj) {
310         // Android-changed: Delegate to added hidden constructor.
311         this(fdObj, false /* isOwner */);
312     }
313 
314     // Android-added: Internal/hidden constructor for specifying FileDescriptor ownership.
315     // Android-removed: SecurityManager calls.
316     /**
317      * Internal constructor for {@code FileOutputStream} objects where the file descriptor
318      * is owned by this tream.
319      *
320      * @hide
321      */
FileOutputStream(FileDescriptor fdObj, boolean isFdOwner)322     public FileOutputStream(FileDescriptor fdObj, boolean isFdOwner) {
323         if (fdObj == null) {
324             // Android-changed: Improved NullPointerException message.
325             throw new NullPointerException("fdObj == null");
326         }
327 
328         this.fd = fdObj;
329         this.path = null;
330 
331         // Android-changed: FileDescriptor ownership tracking mechanism.
332         // fd.attach(this);
333         this.isFdOwner = isFdOwner;
334         if (isFdOwner) {
335             IoUtils.setFdOwner(this.fd, this);
336         }
337     }
338 
339     // BEGIN Android-changed: Open files using IoBridge to share BlockGuard & StrictMode logic.
340     // http://b/112107427
341     /*
342     /**
343      * Opens a file, with the specified name, for overwriting or appending.
344      * @param name name of file to be opened
345      * @param append whether the file is to be opened in append mode
346      *
347     private native void open0(String name, boolean append)
348         throws FileNotFoundException;
349 
350     // wrap native call to allow instrumentation
351     /**
352      * Opens a file, with the specified name, for overwriting or appending.
353      * @param name name of file to be opened
354      * @param append whether the file is to be opened in append mode
355      *
356     private void open(String name, boolean append)
357         throws FileNotFoundException {
358         open0(name, append);
359     }
360     */
361     // END Android-changed: Open files using IoBridge to share BlockGuard & StrictMode logic.
362 
363     // Android-removed: write(int, boolean), use IoBridge instead.
364     /*
365     /**
366      * Writes the specified byte to this file output stream.
367      *
368      * @param   b   the byte to be written.
369      * @param   append   {@code true} if the write operation first
370      *     advances the position to the end of file
371      *
372     private native void write(int b, boolean append) throws IOException;
373     */
374 
375     /**
376      * Writes the specified byte to this file output stream. Implements
377      * the {@code write} method of {@code OutputStream}.
378      *
379      * @param      b   the byte to be written.
380      * @throws     IOException  if an I/O error occurs.
381      */
write(int b)382     public void write(int b) throws IOException {
383         // Android-changed: Write methods delegate to write(byte[],int,int) to share Android logic.
384         // write(b, fdAccess.getAppend(fd));
385         write(new byte[] { (byte) b }, 0, 1);
386     }
387 
388     // Android-removed: Write methods delegate to write(byte[],int,int) to share Android logic.
389     /*
390     /**
391      * Writes a sub array as a sequence of bytes.
392      * @param b the data to be written
393      * @param off the start offset in the data
394      * @param len the number of bytes that are written
395      * @param append {@code true} to first advance the position to the
396      *     end of file
397      * @throws    IOException If an I/O error has occurred.
398      *
399     private native void writeBytes(byte b[], int off, int len, boolean append)
400         throws IOException;
401     */
402 
403     /**
404      * Writes {@code b.length} bytes from the specified byte array
405      * to this file output stream.
406      *
407      * @param      b   the data.
408      * @throws     IOException  if an I/O error occurs.
409      */
write(byte b[])410     public void write(byte b[]) throws IOException {
411         // Android-changed: Write methods delegate to write(byte[],int,int) to share Android logic.
412         // writeBytes(b, 0, b.length, fdAccess.getAppend(fd));
413         write(b, 0, b.length);
414     }
415 
416     /**
417      * Writes {@code len} bytes from the specified byte array
418      * starting at offset {@code off} to this file output stream.
419      *
420      * @param      b     the data.
421      * @param      off   the start offset in the data.
422      * @param      len   the number of bytes to write.
423      * @throws     IOException  if an I/O error occurs.
424      */
write(byte b[], int off, int len)425     public void write(byte b[], int off, int len) throws IOException {
426         // Android-added: close() check before I/O.
427         if (closed && len > 0) {
428             throw new IOException("Stream Closed");
429         }
430 
431         // Android-added: Tracking of unbuffered I/O.
432         tracker.trackIo(len, IoTracker.Mode.WRITE);
433 
434         // Android-changed: Use IoBridge instead of calling native method.
435         // writeBytes(b, off, len, fdAccess.getAppend(fd));
436         IoBridge.write(fd, b, off, len);
437     }
438 
439     /**
440      * Closes this file output stream and releases any system resources
441      * associated with this stream. This file output stream may no longer
442      * be used for writing bytes.
443      *
444      * <p> If this stream has an associated channel then the channel is closed
445      * as well.
446      *
447      * @apiNote
448      * Overriding {@link #close} to perform cleanup actions is reliable
449      * only when called directly or when called by try-with-resources.
450      * Do not depend on finalization to invoke {@code close};
451      * finalization is not reliable and is deprecated.
452      * If cleanup of native resources is needed, other mechanisms such as
453      * {@linkplain java.lang.ref.Cleaner} should be used.
454      *
455      * @throws     IOException  if an I/O error occurs.
456      *
457      * @revised 1.4
458      */
close()459     public void close() throws IOException {
460         if (closed) {
461             return;
462         }
463         synchronized (closeLock) {
464             if (closed) {
465                 return;
466             }
467             closed = true;
468         }
469 
470         // Android-added: CloseGuard support.
471         guard.close();
472 
473         if (channel != null) {
474             channel.close();
475         }
476 
477         // BEGIN Android-changed: Close handling / notification of blocked threads.
478         /*
479         fd.closeAll(new Closeable() {
480             public void close() throws IOException {
481                fd.close();
482            }
483         });
484          */
485         if (isFdOwner) {
486             IoBridge.closeAndSignalBlockedThreads(fd);
487         }
488         // END Android-changed: Close handling / notification of blocked threads.
489     }
490 
491     /**
492      * Returns the file descriptor associated with this stream.
493      *
494      * @return  the {@code FileDescriptor} object that represents
495      *          the connection to the file in the file system being used
496      *          by this {@code FileOutputStream} object.
497      *
498      * @throws     IOException  if an I/O error occurs.
499      * @see        java.io.FileDescriptor
500      */
501      // Android-added: @ReachabilitySensitive
502      @ReachabilitySensitive
getFD()503      public final FileDescriptor getFD()  throws IOException {
504         if (fd != null) {
505             return fd;
506         }
507         throw new IOException();
508      }
509 
510     /**
511      * Returns the unique {@link java.nio.channels.FileChannel FileChannel}
512      * object associated with this file output stream.
513      *
514      * <p> The initial {@link java.nio.channels.FileChannel#position()
515      * position} of the returned channel will be equal to the
516      * number of bytes written to the file so far unless this stream is in
517      * append mode, in which case it will be equal to the size of the file.
518      * Writing bytes to this stream will increment the channel's position
519      * accordingly.  Changing the channel's position, either explicitly or by
520      * writing, will change this stream's file position.
521      *
522      * @return  the file channel associated with this file output stream
523      *
524      * @since 1.4
525      */
getChannel()526     public FileChannel getChannel() {
527         FileChannel fc = this.channel;
528         if (fc == null) {
529             synchronized (this) {
530                 fc = this.channel;
531                 if (fc == null) {
532                     this.channel = fc = FileChannelImpl.open(fd, path, false,
533                         // Android-changed: TODO: remove patch when FileChannelImpl supports direct.
534                         // This patch should cause no behavior change as direct is off by default.
535                         // true, false, this);
536                         true, this);
537                     if (closed) {
538                         try {
539                             // possible race with close(), benign since
540                             // FileChannel.close is final and idempotent
541                             fc.close();
542                         } catch (IOException ioe) {
543                             throw new InternalError(ioe); // should not happen
544                         }
545                     }
546                 }
547             }
548         }
549         return fc;
550     }
551 
552     // TODO: Remove finalize() when FileCleanable is imported and used.
553     /**
554      * Cleans up the connection to the file, and ensures that the
555      * <code>close</code> method of this file output stream is
556      * called when there are no more references to this stream.
557      *
558      * @exception  IOException  if an I/O error occurs.
559      * @see        java.io.FileInputStream#close()
560      */
finalize()561     protected void finalize() throws IOException {
562         // Android-added: CloseGuard support.
563         if (guard != null) {
564             guard.warnIfOpen();
565         }
566 
567         if (fd != null) {
568             if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
569                 flush();
570             } else {
571                 // Android-removed: Obsoleted comment about shared FileDescriptor handling.
572                 close();
573             }
574         }
575     }
576 
577     // BEGIN Android-removed: Unused code.
578     /*
579     private native void close0() throws IOException;
580 
581     private static native void initIDs();
582 
583     static {
584         initIDs();
585     }
586     */
587     // END Android-removed: Unused code.
588 
589 }
590