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