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.system.ErrnoException;
20 import android.system.StructStat;
21 import java.io.File;
22 import java.io.FileDescriptor;
23 import java.io.FileNotFoundException;
24 import java.io.IOException;
25 import java.io.InterruptedIOException;
26 import java.net.Socket;
27 import java.nio.charset.Charset;
28 import java.nio.charset.StandardCharsets;
29 import java.util.Random;
30 import static android.system.OsConstants.*;
31 
32 public final class IoUtils {
IoUtils()33     private IoUtils() {
34     }
35 
36     /**
37      * Calls close(2) on 'fd'. Also resets the internal int to -1. Does nothing if 'fd' is null
38      * or invalid.
39      */
close(FileDescriptor fd)40     public static void close(FileDescriptor fd) throws IOException {
41         try {
42             if (fd != null && fd.valid()) {
43                 Libcore.os.close(fd);
44             }
45         } catch (ErrnoException errnoException) {
46             throw errnoException.rethrowAsIOException();
47         }
48     }
49 
50     /**
51      * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
52      */
closeQuietly(AutoCloseable closeable)53     public static void closeQuietly(AutoCloseable closeable) {
54         if (closeable != null) {
55             try {
56                 closeable.close();
57             } catch (RuntimeException rethrown) {
58                 throw rethrown;
59             } catch (Exception ignored) {
60             }
61         }
62     }
63 
64     /**
65      * Closes 'fd', ignoring any exceptions. Does nothing if 'fd' is null or invalid.
66      */
closeQuietly(FileDescriptor fd)67     public static void closeQuietly(FileDescriptor fd) {
68         try {
69             IoUtils.close(fd);
70         } catch (IOException ignored) {
71         }
72     }
73 
74     /**
75      * Closes 'socket', ignoring any exceptions. Does nothing if 'socket' is null.
76      */
closeQuietly(Socket socket)77     public static void closeQuietly(Socket socket) {
78         if (socket != null) {
79             try {
80                 socket.close();
81             } catch (Exception ignored) {
82             }
83         }
84     }
85 
86     /**
87      * Sets 'fd' to be blocking or non-blocking, according to the state of 'blocking'.
88      */
setBlocking(FileDescriptor fd, boolean blocking)89     public static void setBlocking(FileDescriptor fd, boolean blocking) throws IOException {
90         try {
91             int flags = Libcore.os.fcntlVoid(fd, F_GETFL);
92             if (!blocking) {
93                 flags |= O_NONBLOCK;
94             } else {
95                 flags &= ~O_NONBLOCK;
96             }
97             Libcore.os.fcntlInt(fd, F_SETFL, flags);
98         } catch (ErrnoException errnoException) {
99             throw errnoException.rethrowAsIOException();
100         }
101     }
102 
103     /**
104      * Returns the contents of 'path' as a byte array.
105      */
readFileAsByteArray(String absolutePath)106     public static byte[] readFileAsByteArray(String absolutePath) throws IOException {
107         return new FileReader(absolutePath).readFully().toByteArray();
108     }
109 
110     /**
111      * Returns the contents of 'path' as a string. The contents are assumed to be UTF-8.
112      */
readFileAsString(String absolutePath)113     public static String readFileAsString(String absolutePath) throws IOException {
114         return new FileReader(absolutePath).readFully().toString(StandardCharsets.UTF_8);
115     }
116 
117     /**
118      * Do not use. Use createTemporaryDirectory instead.
119      *
120      * Used by frameworks/base unit tests to clean up a temporary directory.
121      * Deliberately ignores errors, on the assumption that test cleanup is only
122      * supposed to be best-effort.
123      *
124      * @deprecated Use {@link #createTemporaryDirectory} instead.
125      */
deleteContents(File dir)126     public static void deleteContents(File dir) throws IOException {
127         File[] files = dir.listFiles();
128         if (files != null) {
129             for (File file : files) {
130                 if (file.isDirectory()) {
131                     deleteContents(file);
132                 }
133                 file.delete();
134             }
135         }
136     }
137 
138     /**
139      * Creates a unique new temporary directory under "java.io.tmpdir".
140      */
createTemporaryDirectory(String prefix)141     public static File createTemporaryDirectory(String prefix) {
142         while (true) {
143             String candidateName = prefix + Math.randomIntInternal();
144             File result = new File(System.getProperty("java.io.tmpdir"), candidateName);
145             if (result.mkdir()) {
146                 return result;
147             }
148         }
149     }
150 
151     /**
152      * Do not use. This is for System.loadLibrary use only.
153      *
154      * Checks whether {@code path} can be opened read-only. Similar to File.exists, but doesn't
155      * require read permission on the parent, so it'll work in more cases, and allow you to
156      * remove read permission from more directories. Everyone else should just open(2) and then
157      * use the fd, but the loadLibrary API is broken by its need to ask ClassLoaders where to
158      * find a .so rather than just calling dlopen(3).
159      */
canOpenReadOnly(String path)160     public static boolean canOpenReadOnly(String path) {
161         try {
162             // Use open(2) rather than stat(2) so we require fewer permissions. http://b/6485312.
163             FileDescriptor fd = Libcore.os.open(path, O_RDONLY, 0);
164             Libcore.os.close(fd);
165             return true;
166         } catch (ErrnoException errnoException) {
167             return false;
168         }
169     }
170 
throwInterruptedIoException()171     public static void throwInterruptedIoException() throws InterruptedIOException {
172         // This is typically thrown in response to an
173         // InterruptedException which does not leave the thread in an
174         // interrupted state, so explicitly interrupt here.
175         Thread.currentThread().interrupt();
176         // TODO: set InterruptedIOException.bytesTransferred
177         throw new InterruptedIOException();
178     }
179 
180     /**
181      * A convenience class for reading the contents of a file into a {@code String}
182      * or a {@code byte[]}. This class attempts to minimize the number of allocations
183      * and copies required to read this data.
184      *
185      * For the case where we know the "true" length of a file (most ordinary files)
186      * we allocate exactly one byte[] and copy data into that. Calls to
187      * {@link #toByteArray} will then return the internal array and <b>not</b> a copy.
188      *
189      * <b>Note that an absolute path must be supplied. Expect your reads to fail
190      * if one isn't.</b>
191      */
192     private static class FileReader {
193         private FileDescriptor fd;
194         private boolean unknownLength;
195 
196         private byte[] bytes;
197         private int count;
198 
FileReader(String absolutePath)199         public FileReader(String absolutePath) throws IOException {
200             // We use IoBridge.open because callers might differentiate
201             // between a FileNotFoundException and a general IOException.
202             //
203             // NOTE: This costs us an additional call to fstat(2) to test whether
204             // "absolutePath" is a directory or not. We can eliminate it
205             // at the cost of copying some code from IoBridge.open.
206             try {
207                 fd = IoBridge.open(absolutePath, O_RDONLY);
208             } catch (FileNotFoundException fnfe) {
209                 throw fnfe;
210             }
211 
212             int capacity;
213             try {
214                 final StructStat stat = Libcore.os.fstat(fd);
215                 // Like RAF & other APIs, we assume that the file size fits
216                 // into a 32 bit integer.
217                 capacity = (int) stat.st_size;
218                 if (capacity == 0) {
219                     unknownLength = true;
220                     capacity = 8192;
221                 }
222             } catch (ErrnoException exception) {
223                 closeQuietly(fd);
224                 throw exception.rethrowAsIOException();
225             }
226 
227             bytes = new byte[capacity];
228         }
229 
readFully()230         public FileReader readFully() throws IOException {
231             int read;
232             int capacity = bytes.length;
233             try {
234                 while ((read = Libcore.os.read(fd, bytes, count, capacity - count)) != 0) {
235                     count += read;
236                     if (count == capacity) {
237                         if (unknownLength) {
238                             // If we don't know the length of this file, we need to continue
239                             // reading until we reach EOF. Double the capacity in preparation.
240                             final int newCapacity = capacity * 2;
241                             byte[] newBytes = new byte[newCapacity];
242                             System.arraycopy(bytes, 0, newBytes, 0, capacity);
243                             bytes = newBytes;
244                             capacity = newCapacity;
245                         } else {
246                             // We know the length of this file and we've read the right number
247                             // of bytes from it, return.
248                             break;
249                         }
250                     }
251                 }
252 
253                 return this;
254             } catch (ErrnoException e) {
255                 throw e.rethrowAsIOException();
256             } finally {
257                 closeQuietly(fd);
258             }
259         }
260 
261         @FindBugsSuppressWarnings("EI_EXPOSE_REP")
toByteArray()262         public byte[] toByteArray() {
263             if (count == bytes.length) {
264                 return bytes;
265             }
266             byte[] result = new byte[count];
267             System.arraycopy(bytes, 0, result, 0, count);
268             return result;
269         }
270 
toString(Charset cs)271         public String toString(Charset cs) {
272             return new String(bytes, 0, count, cs);
273         }
274     }
275 }
276