1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package libcore.io; 18 19 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; 20 21 import android.annotation.SystemApi; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.system.ErrnoException; 24 import android.system.StructStat; 25 26 import java.io.File; 27 import java.io.FileDescriptor; 28 import java.io.FileNotFoundException; 29 import java.io.IOException; 30 import java.io.InterruptedIOException; 31 import java.net.Socket; 32 import java.nio.charset.Charset; 33 import java.nio.charset.StandardCharsets; 34 import java.util.Objects; 35 36 import libcore.util.NonNull; 37 import libcore.util.Nullable; 38 39 import static android.system.OsConstants.F_GETFL; 40 import static android.system.OsConstants.F_SETFL; 41 import static android.system.OsConstants.O_NONBLOCK; 42 import static android.system.OsConstants.O_RDONLY; 43 44 /** @hide */ 45 @SystemApi(client = MODULE_LIBRARIES) 46 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) 47 public final class IoUtils { IoUtils()48 private IoUtils() { 49 } 50 51 /** 52 * Acquires ownership of an integer file descriptor from a {@link FileDescriptor}. 53 * 54 * This method invalidates the {@link FileDescriptor} passed in. 55 * 56 * The important part of this function is that you are taking ownership of a resource that you 57 * must either clean up yourself, or hand off to some other object that does that for you. 58 * 59 * See bionic/include/android/fdsan.h for more details. 60 * 61 * @param fd {@link FileDescriptor} to take ownership from, must be non-{@code null}. 62 * @return raw file descriptor 63 * @throws NullPointerException if fd is null 64 * 65 * @hide 66 */ 67 @SystemApi(client = MODULE_LIBRARIES) 68 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) acquireRawFd(@onNull FileDescriptor fd)69 public static int acquireRawFd(@NonNull FileDescriptor fd) { 70 Objects.requireNonNull(fd); 71 72 FileDescriptor copy = fd.release$(); 73 // Get the numeric Unix file descriptor. -1 means it is invalid; for example if 74 // {@link FileDescriptor#release$()} has already been called on the FileDescriptor. 75 int rawFd = copy.getInt$(); 76 long previousOwnerId = copy.getOwnerId$(); 77 if (rawFd != -1 && previousOwnerId != FileDescriptor.NO_OWNER) { 78 // Clear the file descriptor's owner ID, aborting if the previous value isn't as expected. 79 Libcore.os.android_fdsan_exchange_owner_tag(copy, previousOwnerId, 80 FileDescriptor.NO_OWNER); 81 } 82 return rawFd; 83 } 84 isParcelFileDescriptor(Object object)85 private static boolean isParcelFileDescriptor(Object object) { 86 // We need to look up ParcelFileDescriptor dynamically, because there are cases where the 87 // framework classes will not be found on the classpath such as on-host development. 88 try { 89 Class<?> pfdClass = Class.forName("android.os.ParcelFileDescriptor"); 90 if (pfdClass.isInstance(object)) { 91 return true; 92 } 93 return false; 94 } catch (ClassNotFoundException ex) { 95 return false; 96 } 97 } 98 generateFdOwnerId(Object owner)99 private static long generateFdOwnerId(Object owner) { 100 if (owner == null) { 101 return 0; 102 } 103 104 // Type values from bionic's <android/fdsan.h>. 105 long tagType; 106 if (owner instanceof java.io.FileInputStream) { 107 tagType = 5; 108 } else if (owner instanceof java.io.FileOutputStream) { 109 tagType = 6; 110 } else if (owner instanceof java.io.RandomAccessFile) { 111 tagType = 7; 112 } else if (owner instanceof java.net.DatagramSocketImpl) { 113 tagType = 10; 114 } else if (owner instanceof java.net.SocketImpl) { 115 tagType = 11; 116 } else if (isParcelFileDescriptor(owner)) { 117 tagType = 8; 118 } else { 119 // Generic Java type. 120 tagType = 255; 121 } 122 123 // The owner ID is not required to be unique but should be stable and attempt to avoid 124 // collision with identifiers generated both here and in native code (which are simply the 125 // address of the owning object). identityHashCode(Object) meets these requirements. 126 // 127 // If identityHashCode returns a negative int, it'll be sign-extended, so we need to apply 128 // a mask. fdsan uses bits 48-56 to distinguish between a generic native pointer and a 129 // generic Java type, but since we're only inserting 32-bits of data, we might as well mask 130 // off the entire upper 32 bits. 131 long mask = (1L << 32) - 1; 132 long tagValue = System.identityHashCode(owner) & mask; 133 return tagType << 56 | tagValue; 134 } 135 136 /** 137 * Assigns ownership of an unowned {@link FileDescriptor}. 138 * 139 * Associates the supplied {@link FileDescriptor} and the underlying Unix file descriptor with an owner 140 * ID derived from the supplied {@code owner} object. If the {@link FileDescriptor} already has an 141 * associated owner an {@link IllegalStateException} will be thrown. If the underlying Unix 142 * file descriptor already has an associated owner, the process will abort. 143 * 144 * See bionic/include/android/fdsan.h for more details. 145 * 146 * @param fd {@link FileDescriptor} to take ownership from, must be non-{@code null}. 147 * @param owner owner object 148 * @throws NullPointerException if {@code fd} or {@code owner} are {@code null} 149 * @throws IllegalStateException if {@code fd} is already owned 150 * 151 * @hide 152 */ 153 @SystemApi(client = MODULE_LIBRARIES) 154 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) setFdOwner(@onNull FileDescriptor fd, @NonNull Object owner)155 public static void setFdOwner(@NonNull FileDescriptor fd, @NonNull Object owner) { 156 Objects.requireNonNull(fd); 157 Objects.requireNonNull(owner); 158 159 long previousOwnerId = fd.getOwnerId$(); 160 if (previousOwnerId != FileDescriptor.NO_OWNER) { 161 throw new IllegalStateException("Attempted to take ownership of already-owned " + 162 "FileDescriptor"); 163 } 164 165 long ownerId = generateFdOwnerId(owner); 166 fd.setOwnerId$(ownerId); 167 168 // Set the file descriptor's owner ID, aborting if the previous value isn't as expected. 169 Libcore.os.android_fdsan_exchange_owner_tag(fd, previousOwnerId, ownerId); 170 } 171 172 /** 173 * Closes a file descriptor, so that it no longer refers to any file and may 174 * be reused. Also resets the internal int to -1. 175 * 176 * @param fd is {@link FileDescriptor} instance, invalid value is ignored. 177 * @throws IOException if an I/O error occurred. 178 * 179 * @hide 180 */ 181 @SystemApi(client = MODULE_LIBRARIES) 182 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) close(@ullable FileDescriptor fd)183 public static void close(@Nullable FileDescriptor fd) throws IOException { 184 IoBridge.closeAndSignalBlockedThreads(fd); 185 } 186 187 /** 188 * Closes {@link AutoClosable} instance, ignoring any checked exceptions. 189 * 190 * @param close is AutoClosable instance, null value is ignored. 191 * 192 * @hide 193 */ 194 @UnsupportedAppUsage 195 @SystemApi(client = MODULE_LIBRARIES) 196 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) closeQuietly(@ullable AutoCloseable closeable)197 public static void closeQuietly(@Nullable AutoCloseable closeable) { 198 if (closeable != null) { 199 try { 200 closeable.close(); 201 } catch (RuntimeException rethrown) { 202 throw rethrown; 203 } catch (Exception ignored) { 204 } 205 } 206 } 207 208 /** 209 * Calls {@link #close(FileDescriptor)}, ignoring any exceptions. 210 * 211 * @param fd is {@link FileDescriptor} instance, invalid value is ignored. 212 * 213 * @hide 214 */ 215 @UnsupportedAppUsage 216 @SystemApi(client = MODULE_LIBRARIES) 217 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) closeQuietly(@ullable FileDescriptor fd)218 public static void closeQuietly(@Nullable FileDescriptor fd) { 219 try { 220 IoUtils.close(fd); 221 } catch (IOException ignored) { 222 } 223 } 224 225 /** 226 * Closes socket, ignoring any exceptions. 227 * 228 * @param socket is {@link Socket} instance, {@code null} value is ignored. 229 * 230 * @hide 231 */ 232 @UnsupportedAppUsage 233 @SystemApi(client = MODULE_LIBRARIES) 234 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) closeQuietly(@ullable Socket socket)235 public static void closeQuietly(@Nullable Socket socket) { 236 if (socket != null) { 237 try { 238 socket.close(); 239 } catch (RuntimeException rethrown) { 240 throw rethrown; 241 } catch (Exception ignored) { 242 } 243 } 244 } 245 246 /** 247 * Sets file descriptor to be blocking or non-blocking. 248 * 249 * @param fd is {@link FileDescriptor} instance 250 * @param blocking is a boolean that defines whether fd should be blocking or non-blocking 251 * @throws IOException if system API call fails 252 * 253 * @hide 254 */ 255 @UnsupportedAppUsage 256 @SystemApi(client = MODULE_LIBRARIES) 257 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) setBlocking(@onNull FileDescriptor fd, boolean blocking)258 public static void setBlocking(@NonNull FileDescriptor fd, boolean blocking) throws IOException { 259 try { 260 int flags = Libcore.os.fcntlVoid(fd, F_GETFL); 261 if (!blocking) { 262 flags |= O_NONBLOCK; 263 } else { 264 flags &= ~O_NONBLOCK; 265 } 266 Libcore.os.fcntlInt(fd, F_SETFL, flags); 267 } catch (ErrnoException errnoException) { 268 throw errnoException.rethrowAsIOException(); 269 } 270 } 271 272 /** 273 * Returns the contents of {@code absolutePath} as a byte array. 274 * 275 * @param absolutePath path to a file to read 276 * @return contents of the file at {@code absolutePath} as byte array 277 * @throws IOException if there was I/O error 278 * 279 * @hide 280 */ 281 @UnsupportedAppUsage 282 @SystemApi(client = MODULE_LIBRARIES) 283 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) readFileAsByteArray(@onNull String absolutePath)284 public static @NonNull byte[] readFileAsByteArray(@NonNull String absolutePath) throws IOException { 285 return new FileReader(absolutePath).readFully().toByteArray(); 286 } 287 288 /** 289 * Returns the contents of {@code absolutePath} as a {@link String}. The contents are assumed to be UTF-8. 290 * 291 * @param absolutePath path to a file to read 292 * @return contents of the file at {@code absolutePath} as {@link String} 293 * @throws IOException if there was I/O error 294 * 295 * @hide 296 */ 297 @UnsupportedAppUsage 298 @SystemApi(client = MODULE_LIBRARIES) 299 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE) readFileAsString(@onNull String absolutePath)300 public static @NonNull String readFileAsString(@NonNull String absolutePath) throws IOException { 301 return new FileReader(absolutePath).readFully().toString(StandardCharsets.UTF_8); 302 } 303 304 /** 305 * Do not use. Use createTemporaryDirectory instead. 306 * 307 * Used by frameworks/base unit tests to clean up a temporary directory. 308 * Deliberately ignores errors, on the assumption that test cleanup is only 309 * supposed to be best-effort. 310 * 311 * @deprecated Use {@link TestIoUtils#createTemporaryDirectory} instead. 312 * 313 * @hide 314 */ deleteContents(@onNull File dir)315 public static void deleteContents(@NonNull File dir) throws IOException { 316 File[] files = dir.listFiles(); 317 if (files != null) { 318 for (File file : files) { 319 if (file.isDirectory()) { 320 deleteContents(file); 321 } 322 file.delete(); 323 } 324 } 325 } 326 327 /** 328 * Do not use. This is for System.loadLibrary use only. 329 * 330 * Checks whether {@code path} can be opened read-only. Similar to File.exists, but doesn't 331 * require read permission on the parent, so it'll work in more cases, and allow you to 332 * remove read permission from more directories. Everyone else should just open(2) and then 333 * use the fd, but the loadLibrary API is broken by its need to ask ClassLoaders where to 334 * find a .so rather than just calling dlopen(3). 335 * 336 * @hide 337 */ canOpenReadOnly(String path)338 public static boolean canOpenReadOnly(String path) { 339 try { 340 // Use open(2) rather than stat(2) so we require fewer permissions. http://b/6485312. 341 FileDescriptor fd = Libcore.os.open(path, O_RDONLY, 0); 342 Libcore.os.close(fd); 343 return true; 344 } catch (ErrnoException errnoException) { 345 return false; 346 } 347 } 348 349 /** 350 * @hide 351 */ throwInterruptedIoException()352 public static void throwInterruptedIoException() throws InterruptedIOException { 353 // This is typically thrown in response to an 354 // InterruptedException which does not leave the thread in an 355 // interrupted state, so explicitly interrupt here. 356 Thread.currentThread().interrupt(); 357 // TODO: set InterruptedIOException.bytesTransferred 358 throw new InterruptedIOException(); 359 } 360 361 /** 362 * A convenience class for reading the contents of a file into a {@code String} 363 * or a {@code byte[]}. This class attempts to minimize the number of allocations 364 * and copies required to read this data. 365 * 366 * For the case where we know the "true" length of a file (most ordinary files) 367 * we allocate exactly one byte[] and copy data into that. Calls to 368 * {@link #toByteArray} will then return the internal array and <b>not</b> a copy. 369 * 370 * <b>Note that an absolute path must be supplied. Expect your reads to fail 371 * if one isn't.</b> 372 */ 373 private static class FileReader { 374 private FileDescriptor fd; 375 private boolean unknownLength; 376 377 private byte[] bytes; 378 private int count; 379 FileReader(String absolutePath)380 public FileReader(String absolutePath) throws IOException { 381 // We use IoBridge.open because callers might differentiate 382 // between a FileNotFoundException and a general IOException. 383 // 384 // NOTE: This costs us an additional call to fstat(2) to test whether 385 // "absolutePath" is a directory or not. We can eliminate it 386 // at the cost of copying some code from IoBridge.open. 387 try { 388 fd = IoBridge.open(absolutePath, O_RDONLY); 389 } catch (FileNotFoundException fnfe) { 390 throw fnfe; 391 } 392 393 int capacity; 394 try { 395 final StructStat stat = Libcore.os.fstat(fd); 396 // Like RAF & other APIs, we assume that the file size fits 397 // into a 32 bit integer. 398 capacity = (int) stat.st_size; 399 if (capacity == 0) { 400 unknownLength = true; 401 capacity = 8192; 402 } 403 } catch (ErrnoException exception) { 404 closeQuietly(fd); 405 throw exception.rethrowAsIOException(); 406 } 407 408 bytes = new byte[capacity]; 409 } 410 readFully()411 public FileReader readFully() throws IOException { 412 int read; 413 int capacity = bytes.length; 414 try { 415 while ((read = Libcore.os.read(fd, bytes, count, capacity - count)) != 0) { 416 count += read; 417 if (count == capacity) { 418 if (unknownLength) { 419 // If we don't know the length of this file, we need to continue 420 // reading until we reach EOF. Double the capacity in preparation. 421 final int newCapacity = capacity * 2; 422 byte[] newBytes = new byte[newCapacity]; 423 System.arraycopy(bytes, 0, newBytes, 0, capacity); 424 bytes = newBytes; 425 capacity = newCapacity; 426 } else { 427 // We know the length of this file and we've read the right number 428 // of bytes from it, return. 429 break; 430 } 431 } 432 } 433 434 return this; 435 } catch (ErrnoException e) { 436 throw e.rethrowAsIOException(); 437 } finally { 438 closeQuietly(fd); 439 } 440 } 441 442 @FindBugsSuppressWarnings("EI_EXPOSE_REP") toByteArray()443 public byte[] toByteArray() { 444 if (count == bytes.length) { 445 return bytes; 446 } 447 byte[] result = new byte[count]; 448 System.arraycopy(bytes, 0, result, 0, count); 449 return result; 450 } 451 toString(Charset cs)452 public String toString(Charset cs) { 453 return new String(bytes, 0, count, cs); 454 } 455 } 456 } 457