• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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 android.os;
18 
19 import android.util.Log;
20 import android.util.Slog;
21 
22 import libcore.io.ErrnoException;
23 import libcore.io.IoUtils;
24 import libcore.io.Libcore;
25 import libcore.io.OsConstants;
26 
27 import java.io.BufferedInputStream;
28 import java.io.ByteArrayOutputStream;
29 import java.io.File;
30 import java.io.FileDescriptor;
31 import java.io.FileInputStream;
32 import java.io.FileNotFoundException;
33 import java.io.FileOutputStream;
34 import java.io.FileWriter;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.util.Arrays;
38 import java.util.Comparator;
39 import java.util.regex.Pattern;
40 import java.util.zip.CRC32;
41 import java.util.zip.CheckedInputStream;
42 
43 /**
44  * Tools for managing files.  Not for public consumption.
45  * @hide
46  */
47 public class FileUtils {
48     private static final String TAG = "FileUtils";
49 
50     public static final int S_IRWXU = 00700;
51     public static final int S_IRUSR = 00400;
52     public static final int S_IWUSR = 00200;
53     public static final int S_IXUSR = 00100;
54 
55     public static final int S_IRWXG = 00070;
56     public static final int S_IRGRP = 00040;
57     public static final int S_IWGRP = 00020;
58     public static final int S_IXGRP = 00010;
59 
60     public static final int S_IRWXO = 00007;
61     public static final int S_IROTH = 00004;
62     public static final int S_IWOTH = 00002;
63     public static final int S_IXOTH = 00001;
64 
65     /** Regular expression for safe filenames: no spaces or metacharacters */
66     private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
67 
68     /**
69      * Set owner and mode of of given {@link File}.
70      *
71      * @param mode to apply through {@code chmod}
72      * @param uid to apply through {@code chown}, or -1 to leave unchanged
73      * @param gid to apply through {@code chown}, or -1 to leave unchanged
74      * @return 0 on success, otherwise errno.
75      */
setPermissions(File path, int mode, int uid, int gid)76     public static int setPermissions(File path, int mode, int uid, int gid) {
77         return setPermissions(path.getAbsolutePath(), mode, uid, gid);
78     }
79 
80     /**
81      * Set owner and mode of of given path.
82      *
83      * @param mode to apply through {@code chmod}
84      * @param uid to apply through {@code chown}, or -1 to leave unchanged
85      * @param gid to apply through {@code chown}, or -1 to leave unchanged
86      * @return 0 on success, otherwise errno.
87      */
setPermissions(String path, int mode, int uid, int gid)88     public static int setPermissions(String path, int mode, int uid, int gid) {
89         try {
90             Libcore.os.chmod(path, mode);
91         } catch (ErrnoException e) {
92             Slog.w(TAG, "Failed to chmod(" + path + "): " + e);
93             return e.errno;
94         }
95 
96         if (uid >= 0 || gid >= 0) {
97             try {
98                 Libcore.os.chown(path, uid, gid);
99             } catch (ErrnoException e) {
100                 Slog.w(TAG, "Failed to chown(" + path + "): " + e);
101                 return e.errno;
102             }
103         }
104 
105         return 0;
106     }
107 
108     /**
109      * Set owner and mode of of given {@link FileDescriptor}.
110      *
111      * @param mode to apply through {@code chmod}
112      * @param uid to apply through {@code chown}, or -1 to leave unchanged
113      * @param gid to apply through {@code chown}, or -1 to leave unchanged
114      * @return 0 on success, otherwise errno.
115      */
setPermissions(FileDescriptor fd, int mode, int uid, int gid)116     public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) {
117         try {
118             Libcore.os.fchmod(fd, mode);
119         } catch (ErrnoException e) {
120             Slog.w(TAG, "Failed to fchmod(): " + e);
121             return e.errno;
122         }
123 
124         if (uid >= 0 || gid >= 0) {
125             try {
126                 Libcore.os.fchown(fd, uid, gid);
127             } catch (ErrnoException e) {
128                 Slog.w(TAG, "Failed to fchown(): " + e);
129                 return e.errno;
130             }
131         }
132 
133         return 0;
134     }
135 
136     /**
137      * Return owning UID of given path, otherwise -1.
138      */
getUid(String path)139     public static int getUid(String path) {
140         try {
141             return Libcore.os.stat(path).st_uid;
142         } catch (ErrnoException e) {
143             return -1;
144         }
145     }
146 
147     /**
148      * Perform an fsync on the given FileOutputStream.  The stream at this
149      * point must be flushed but not yet closed.
150      */
sync(FileOutputStream stream)151     public static boolean sync(FileOutputStream stream) {
152         try {
153             if (stream != null) {
154                 stream.getFD().sync();
155             }
156             return true;
157         } catch (IOException e) {
158         }
159         return false;
160     }
161 
162     // copy a file from srcFile to destFile, return true if succeed, return
163     // false if fail
copyFile(File srcFile, File destFile)164     public static boolean copyFile(File srcFile, File destFile) {
165         boolean result = false;
166         try {
167             InputStream in = new FileInputStream(srcFile);
168             try {
169                 result = copyToFile(in, destFile);
170             } finally  {
171                 in.close();
172             }
173         } catch (IOException e) {
174             result = false;
175         }
176         return result;
177     }
178 
179     /**
180      * Copy data from a source stream to destFile.
181      * Return true if succeed, return false if failed.
182      */
copyToFile(InputStream inputStream, File destFile)183     public static boolean copyToFile(InputStream inputStream, File destFile) {
184         try {
185             if (destFile.exists()) {
186                 destFile.delete();
187             }
188             FileOutputStream out = new FileOutputStream(destFile);
189             try {
190                 byte[] buffer = new byte[4096];
191                 int bytesRead;
192                 while ((bytesRead = inputStream.read(buffer)) >= 0) {
193                     out.write(buffer, 0, bytesRead);
194                 }
195             } finally {
196                 out.flush();
197                 try {
198                     out.getFD().sync();
199                 } catch (IOException e) {
200                 }
201                 out.close();
202             }
203             return true;
204         } catch (IOException e) {
205             return false;
206         }
207     }
208 
209     /**
210      * Check if a filename is "safe" (no metacharacters or spaces).
211      * @param file  The file to check
212      */
isFilenameSafe(File file)213     public static boolean isFilenameSafe(File file) {
214         // Note, we check whether it matches what's known to be safe,
215         // rather than what's known to be unsafe.  Non-ASCII, control
216         // characters, etc. are all unsafe by default.
217         return SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
218     }
219 
220     /**
221      * Read a text file into a String, optionally limiting the length.
222      * @param file to read (will not seek, so things like /proc files are OK)
223      * @param max length (positive for head, negative of tail, 0 for no limit)
224      * @param ellipsis to add of the file was truncated (can be null)
225      * @return the contents of the file, possibly truncated
226      * @throws IOException if something goes wrong reading the file
227      */
readTextFile(File file, int max, String ellipsis)228     public static String readTextFile(File file, int max, String ellipsis) throws IOException {
229         InputStream input = new FileInputStream(file);
230         // wrapping a BufferedInputStream around it because when reading /proc with unbuffered
231         // input stream, bytes read not equal to buffer size is not necessarily the correct
232         // indication for EOF; but it is true for BufferedInputStream due to its implementation.
233         BufferedInputStream bis = new BufferedInputStream(input);
234         try {
235             long size = file.length();
236             if (max > 0 || (size > 0 && max == 0)) {  // "head" mode: read the first N bytes
237                 if (size > 0 && (max == 0 || size < max)) max = (int) size;
238                 byte[] data = new byte[max + 1];
239                 int length = bis.read(data);
240                 if (length <= 0) return "";
241                 if (length <= max) return new String(data, 0, length);
242                 if (ellipsis == null) return new String(data, 0, max);
243                 return new String(data, 0, max) + ellipsis;
244             } else if (max < 0) {  // "tail" mode: keep the last N
245                 int len;
246                 boolean rolled = false;
247                 byte[] last = null;
248                 byte[] data = null;
249                 do {
250                     if (last != null) rolled = true;
251                     byte[] tmp = last; last = data; data = tmp;
252                     if (data == null) data = new byte[-max];
253                     len = bis.read(data);
254                 } while (len == data.length);
255 
256                 if (last == null && len <= 0) return "";
257                 if (last == null) return new String(data, 0, len);
258                 if (len > 0) {
259                     rolled = true;
260                     System.arraycopy(last, len, last, 0, last.length - len);
261                     System.arraycopy(data, 0, last, last.length - len, len);
262                 }
263                 if (ellipsis == null || !rolled) return new String(last);
264                 return ellipsis + new String(last);
265             } else {  // "cat" mode: size unknown, read it all in streaming fashion
266                 ByteArrayOutputStream contents = new ByteArrayOutputStream();
267                 int len;
268                 byte[] data = new byte[1024];
269                 do {
270                     len = bis.read(data);
271                     if (len > 0) contents.write(data, 0, len);
272                 } while (len == data.length);
273                 return contents.toString();
274             }
275         } finally {
276             bis.close();
277             input.close();
278         }
279     }
280 
281    /**
282      * Writes string to file. Basically same as "echo -n $string > $filename"
283      *
284      * @param filename
285      * @param string
286      * @throws IOException
287      */
stringToFile(String filename, String string)288     public static void stringToFile(String filename, String string) throws IOException {
289         FileWriter out = new FileWriter(filename);
290         try {
291             out.write(string);
292         } finally {
293             out.close();
294         }
295     }
296 
297     /**
298      * Computes the checksum of a file using the CRC32 checksum routine.
299      * The value of the checksum is returned.
300      *
301      * @param file  the file to checksum, must not be null
302      * @return the checksum value or an exception is thrown.
303      */
checksumCrc32(File file)304     public static long checksumCrc32(File file) throws FileNotFoundException, IOException {
305         CRC32 checkSummer = new CRC32();
306         CheckedInputStream cis = null;
307 
308         try {
309             cis = new CheckedInputStream( new FileInputStream(file), checkSummer);
310             byte[] buf = new byte[128];
311             while(cis.read(buf) >= 0) {
312                 // Just read for checksum to get calculated.
313             }
314             return checkSummer.getValue();
315         } finally {
316             if (cis != null) {
317                 try {
318                     cis.close();
319                 } catch (IOException e) {
320                 }
321             }
322         }
323     }
324 
325     /**
326      * Delete older files in a directory until only those matching the given
327      * constraints remain.
328      *
329      * @param minCount Always keep at least this many files.
330      * @param minAge Always keep files younger than this age.
331      */
deleteOlderFiles(File dir, int minCount, long minAge)332     public static void deleteOlderFiles(File dir, int minCount, long minAge) {
333         if (minCount < 0 || minAge < 0) {
334             throw new IllegalArgumentException("Constraints must be positive or 0");
335         }
336 
337         final File[] files = dir.listFiles();
338         if (files == null) return;
339 
340         // Sort with newest files first
341         Arrays.sort(files, new Comparator<File>() {
342             @Override
343             public int compare(File lhs, File rhs) {
344                 return (int) (rhs.lastModified() - lhs.lastModified());
345             }
346         });
347 
348         // Keep at least minCount files
349         for (int i = minCount; i < files.length; i++) {
350             final File file = files[i];
351 
352             // Keep files newer than minAge
353             final long age = System.currentTimeMillis() - file.lastModified();
354             if (age > minAge) {
355                 Log.d(TAG, "Deleting old file " + file);
356                 file.delete();
357             }
358         }
359     }
360 }
361