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