1 /* 2 * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.io; 27 28 import java.nio.channels.FileChannel; 29 import jdk.internal.access.SharedSecrets; 30 import jdk.internal.access.JavaIOFileDescriptorAccess; 31 import jdk.internal.misc.Blocker; 32 import sun.nio.ch.FileChannelImpl; 33 34 35 /** 36 * A file output stream is an output stream for writing data to a 37 * {@code File} or to a {@code FileDescriptor}. Whether or not 38 * a file is available or may be created depends upon the underlying 39 * platform. Some platforms, in particular, allow a file to be opened 40 * for writing by only one {@code FileOutputStream} (or other 41 * file-writing object) at a time. In such situations the constructors in 42 * this class will fail if the file involved is already open. 43 * 44 * <p>{@code FileOutputStream} is meant for writing streams of raw bytes 45 * such as image data. For writing streams of characters, consider using 46 * {@code FileWriter}. 47 * 48 * @apiNote 49 * The {@link #close} method should be called to release resources used by this 50 * stream, either directly, or with the {@code try}-with-resources statement. 51 * 52 * @implSpec 53 * Subclasses are responsible for the cleanup of resources acquired by the subclass. 54 * Subclasses requiring that resource cleanup take place after a stream becomes 55 * unreachable should use {@link java.lang.ref.Cleaner} or some other mechanism. 56 * 57 * @author Arthur van Hoff 58 * @see java.io.File 59 * @see java.io.FileDescriptor 60 * @see java.io.FileInputStream 61 * @see java.nio.file.Files#newOutputStream 62 * @since 1.0 63 */ 64 public class FileOutputStream extends OutputStream 65 { 66 /** 67 * Access to FileDescriptor internals. 68 */ 69 private static final JavaIOFileDescriptorAccess FD_ACCESS = 70 SharedSecrets.getJavaIOFileDescriptorAccess(); 71 72 /** 73 * The system dependent file descriptor. 74 */ 75 private final FileDescriptor fd; 76 77 /** 78 * The associated channel, initialized lazily. 79 */ 80 private volatile FileChannel channel; 81 82 /** 83 * The path of the referenced file 84 * (null if the stream is created with a file descriptor) 85 */ 86 private final String path; 87 88 private final Object closeLock = new Object(); 89 90 private volatile boolean closed; 91 92 /** 93 * Creates a file output stream to write to the file with the 94 * specified name. A new {@code FileDescriptor} object is 95 * created to represent this file connection. 96 * <p> 97 * First, if there is a security manager, its {@code checkWrite} 98 * method is called with {@code name} as its argument. 99 * <p> 100 * If the file exists but is a directory rather than a regular file, does 101 * not exist but cannot be created, or cannot be opened for any other 102 * reason then a {@code FileNotFoundException} is thrown. 103 * 104 * @implSpec Invoking this constructor with the parameter {@code name} is 105 * equivalent to invoking {@link #FileOutputStream(String,boolean) 106 * new FileOutputStream(name, false)}. 107 * 108 * @param name the system-dependent filename 109 * @throws FileNotFoundException if the file exists but is a directory 110 * rather than a regular file, does not exist but cannot 111 * be created, or cannot be opened for any other reason 112 * @throws SecurityException if a security manager exists and its 113 * {@code checkWrite} method denies write access 114 * to the file. 115 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 116 */ 117 public FileOutputStream(String name) throws FileNotFoundException { 118 this(name != null ? new File(name) : null, false); 119 } 120 121 /** 122 * Creates a file output stream to write to the file with the specified 123 * name. If the second argument is {@code true}, then 124 * bytes will be written to the end of the file rather than the beginning. 125 * A new {@code FileDescriptor} object is created to represent this 126 * file connection. 127 * <p> 128 * First, if there is a security manager, its {@code checkWrite} 129 * method is called with {@code name} as its argument. 130 * <p> 131 * If the file exists but is a directory rather than a regular file, does 132 * not exist but cannot be created, or cannot be opened for any other 133 * reason then a {@code FileNotFoundException} is thrown. 134 * 135 * @param name the system-dependent file name 136 * @param append if {@code true}, then bytes will be written 137 * to the end of the file rather than the beginning 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 * @since 1.1 146 */ 147 public FileOutputStream(String name, boolean append) 148 throws FileNotFoundException 149 { 150 this(name != null ? new File(name) : null, append); 151 } 152 153 /** 154 * Creates a file output stream to write to the file represented by 155 * the specified {@code File} object. A new 156 * {@code FileDescriptor} object is created to represent this 157 * file connection. 158 * <p> 159 * First, if there is a security manager, its {@code checkWrite} 160 * method is called with the path represented by the {@code file} 161 * argument as its argument. 162 * <p> 163 * If the file exists but is a directory rather than a regular file, does 164 * not exist but cannot be created, or cannot be opened for any other 165 * reason then a {@code FileNotFoundException} is thrown. 166 * 167 * @param file the file to be opened for writing. 168 * @throws FileNotFoundException if the file exists but is a directory 169 * rather than a regular file, does not exist but cannot 170 * be created, or cannot be opened for any other reason 171 * @throws SecurityException if a security manager exists and its 172 * {@code checkWrite} method denies write access 173 * to the file. 174 * @see java.io.File#getPath() 175 * @see java.lang.SecurityException 176 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 177 */ 178 public FileOutputStream(File file) throws FileNotFoundException { 179 this(file, false); 180 } 181 182 /** 183 * Creates a file output stream to write to the file represented by 184 * the specified {@code File} object. If the second argument is 185 * {@code true}, then bytes will be written to the end of the file 186 * rather than the beginning. A new {@code FileDescriptor} object is 187 * created to represent this file connection. 188 * <p> 189 * First, if there is a security manager, its {@code checkWrite} 190 * method is called with the path represented by the {@code file} 191 * argument as its argument. 192 * <p> 193 * If the file exists but is a directory rather than a regular file, does 194 * not exist but cannot be created, or cannot be opened for any other 195 * reason then a {@code FileNotFoundException} is thrown. 196 * 197 * @param file the file to be opened for writing. 198 * @param append if {@code true}, then bytes will be written 199 * to the end of the file rather than the beginning 200 * @throws FileNotFoundException if the file exists but is a directory 201 * rather than a regular file, does not exist but cannot 202 * be created, or cannot be opened for any other reason 203 * @throws SecurityException if a security manager exists and its 204 * {@code checkWrite} method denies write access 205 * to the file. 206 * @see java.io.File#getPath() 207 * @see java.lang.SecurityException 208 * @see java.lang.SecurityManager#checkWrite(java.lang.String) 209 * @since 1.4 210 */ 211 @SuppressWarnings("this-escape") 212 public FileOutputStream(File file, boolean append) 213 throws FileNotFoundException 214 { 215 String name = (file != null ? file.getPath() : null); 216 @SuppressWarnings("removal") 217 SecurityManager security = System.getSecurityManager(); 218 if (security != null) { 219 security.checkWrite(name); 220 } 221 if (name == null) { 222 throw new NullPointerException(); 223 } 224 if (file.isInvalid()) { 225 throw new FileNotFoundException("Invalid file path"); 226 } 227 this.fd = new FileDescriptor(); 228 fd.attach(this); 229 this.path = name; 230 231 open(name, append); 232 FileCleanable.register(fd); // open sets the fd, register the cleanup 233 } 234 235 /** 236 * Creates a file output stream to write to the specified file 237 * descriptor, which represents an existing connection to an actual 238 * file in the file system. 239 * <p> 240 * First, if there is a security manager, its {@code checkWrite} 241 * method is called with the file descriptor {@code fdObj} 242 * argument as its argument. 243 * <p> 244 * If {@code fdObj} is null then a {@code NullPointerException} 245 * is thrown. 246 * <p> 247 * This constructor does not throw an exception if {@code fdObj} 248 * is {@link java.io.FileDescriptor#valid() invalid}. 249 * However, if the methods are invoked on the resulting stream to attempt 250 * I/O on the stream, an {@code IOException} is thrown. 251 * 252 * @param fdObj the file descriptor to be opened for writing 253 * @throws SecurityException if a security manager exists and its 254 * {@code checkWrite} method denies 255 * write access to the file descriptor 256 * @see java.lang.SecurityManager#checkWrite(java.io.FileDescriptor) 257 */ 258 @SuppressWarnings("this-escape") 259 public FileOutputStream(FileDescriptor fdObj) { 260 @SuppressWarnings("removal") 261 SecurityManager security = System.getSecurityManager(); 262 if (fdObj == null) { 263 throw new NullPointerException(); 264 } 265 if (security != null) { 266 security.checkWrite(fdObj); 267 } 268 this.fd = fdObj; 269 this.path = null; 270 271 fd.attach(this); 272 } 273 274 /** 275 * Opens a file, with the specified name, for overwriting or appending. 276 * @param name name of file to be opened 277 * @param append whether the file is to be opened in append mode 278 */ 279 private native void open0(String name, boolean append) 280 throws FileNotFoundException; 281 282 // wrap native call to allow instrumentation 283 /** 284 * Opens a file, with the specified name, for overwriting or appending. 285 * @param name name of file to be opened 286 * @param append whether the file is to be opened in append mode 287 */ 288 private void open(String name, boolean append) throws FileNotFoundException { 289 long comp = Blocker.begin(); 290 try { 291 open0(name, append); 292 } finally { 293 Blocker.end(comp); 294 } 295 } 296 297 /** 298 * Writes the specified byte to this file output stream. 299 * 300 * @param b the byte to be written. 301 * @param append {@code true} if the write operation first 302 * advances the position to the end of file 303 */ 304 private native void write(int b, boolean append) throws IOException; 305 306 /** 307 * Writes the specified byte to this file output stream. Implements 308 * the {@code write} method of {@code OutputStream}. 309 * 310 * @param b the byte to be written. 311 * @throws IOException if an I/O error occurs. 312 */ 313 @Override 314 public void write(int b) throws IOException { 315 boolean append = FD_ACCESS.getAppend(fd); 316 long comp = Blocker.begin(); 317 try { 318 write(b, append); 319 } finally { 320 Blocker.end(comp); 321 } 322 } 323 324 /** 325 * Writes a sub array as a sequence of bytes. 326 * @param b the data to be written 327 * @param off the start offset in the data 328 * @param len the number of bytes that are written 329 * @param append {@code true} to first advance the position to the 330 * end of file 331 * @throws IOException If an I/O error has occurred. 332 */ 333 private native void writeBytes(byte[] b, int off, int len, boolean append) 334 throws IOException; 335 336 /** 337 * Writes {@code b.length} bytes from the specified byte array 338 * to this file output stream. 339 * 340 * @param b {@inheritDoc} 341 * @throws IOException {@inheritDoc} 342 */ 343 @Override 344 public void write(byte[] b) throws IOException { 345 boolean append = FD_ACCESS.getAppend(fd); 346 long comp = Blocker.begin(); 347 try { 348 writeBytes(b, 0, b.length, append); 349 } finally { 350 Blocker.end(comp); 351 } 352 } 353 354 /** 355 * Writes {@code len} bytes from the specified byte array 356 * starting at offset {@code off} to this file output stream. 357 * 358 * @param b {@inheritDoc} 359 * @param off {@inheritDoc} 360 * @param len {@inheritDoc} 361 * @throws IOException if an I/O error occurs. 362 * @throws IndexOutOfBoundsException {@inheritDoc} 363 */ 364 @Override 365 public void write(byte[] b, int off, int len) throws IOException { 366 boolean append = FD_ACCESS.getAppend(fd); 367 long comp = Blocker.begin(); 368 try { 369 writeBytes(b, off, len, append); 370 } finally { 371 Blocker.end(comp); 372 } 373 } 374 375 /** 376 * Closes this file output stream and releases any system resources 377 * associated with this stream. This file output stream may no longer 378 * be used for writing bytes. 379 * 380 * <p> If this stream has an associated channel then the channel is closed 381 * as well. 382 * 383 * @apiNote 384 * Overriding {@link #close} to perform cleanup actions is reliable 385 * only when called directly or when called by try-with-resources. 386 * 387 * @implSpec 388 * Subclasses requiring that resource cleanup take place after a stream becomes 389 * unreachable should use the {@link java.lang.ref.Cleaner} mechanism. 390 * 391 * <p> 392 * If this stream has an associated channel then this method will close the 393 * channel, which in turn will close this stream. Subclasses that override 394 * this method should be prepared to handle possible reentrant invocation. 395 * 396 * @throws IOException if an I/O error occurs. 397 */ 398 @Override 399 public void close() throws IOException { 400 if (closed) { 401 return; 402 } 403 synchronized (closeLock) { 404 if (closed) { 405 return; 406 } 407 closed = true; 408 } 409 410 FileChannel fc = channel; 411 if (fc != null) { 412 // possible race with getChannel(), benign since 413 // FileChannel.close is final and idempotent 414 fc.close(); 415 } 416 417 fd.closeAll(new Closeable() { 418 public void close() throws IOException { 419 fd.close(); 420 } 421 }); 422 } 423 424 /** 425 * Returns the file descriptor associated with this stream. 426 * 427 * @return the {@code FileDescriptor} object that represents 428 * the connection to the file in the file system being used 429 * by this {@code FileOutputStream} object. 430 * 431 * @throws IOException if an I/O error occurs. 432 * @see java.io.FileDescriptor 433 */ 434 public final FileDescriptor getFD() throws IOException { 435 if (fd != null) { 436 return fd; 437 } 438 throw new IOException(); 439 } 440 441 /** 442 * Returns the unique {@link java.nio.channels.FileChannel FileChannel} 443 * object associated with this file output stream. 444 * 445 * <p> The initial {@link java.nio.channels.FileChannel#position() 446 * position} of the returned channel will be equal to the 447 * number of bytes written to the file so far unless this stream is in 448 * append mode, in which case it will be equal to the size of the file. 449 * Writing bytes to this stream will increment the channel's position 450 * accordingly. Changing the channel's position, either explicitly or by 451 * writing, will change this stream's file position. 452 * 453 * @return the file channel associated with this file output stream 454 * 455 * @since 1.4 456 */ 457 public FileChannel getChannel() { 458 FileChannel fc = this.channel; 459 if (fc == null) { 460 synchronized (this) { 461 fc = this.channel; 462 if (fc == null) { 463 this.channel = fc = FileChannelImpl.open(fd, path, false, 464 true, false, this); 465 if (closed) { 466 try { 467 // possible race with close(), benign since 468 // FileChannel.close is final and idempotent 469 fc.close(); 470 } catch (IOException ioe) { 471 throw new InternalError(ioe); // should not happen 472 } 473 } 474 } 475 } 476 } 477 return fc; 478 } 479 480 private static native void initIDs(); 481 482 static { 483 initIDs(); 484 } 485 }