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