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