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