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