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