blob: 97ea99ddb4194cf7129e03bd60de5cafd7109077 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.os;
18
Jeff Sharkeyd9526902013-03-14 14:11:57 -070019import android.util.Log;
20
Guang Zhu90619812012-10-12 15:50:44 -070021import java.io.BufferedInputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import java.io.ByteArrayOutputStream;
23import java.io.File;
24import java.io.FileInputStream;
Wink Saville6d25a992011-06-03 17:03:51 -070025import java.io.FileNotFoundException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import java.io.FileOutputStream;
Mike Lockwoodda8bb742011-05-28 13:24:04 -040027import java.io.FileWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import java.io.IOException;
29import java.io.InputStream;
Jeff Sharkeyd9526902013-03-14 14:11:57 -070030import java.util.Arrays;
31import java.util.Comparator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import java.util.regex.Pattern;
Wink Saville6d25a992011-06-03 17:03:51 -070033import java.util.zip.CRC32;
34import java.util.zip.CheckedInputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036/**
37 * Tools for managing files. Not for public consumption.
38 * @hide
39 */
Wink Saville6d25a992011-06-03 17:03:51 -070040public class FileUtils {
Jeff Sharkeyd9526902013-03-14 14:11:57 -070041 private static final String TAG = "FileUtils";
42
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057
58 /** Regular expression for safe filenames: no spaces or metacharacters */
59 private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
60
61 public static native int setPermissions(String file, int mode, int uid, int gid);
62
Dianne Hackborn053f61d2013-06-26 18:07:43 -070063 public static native int getUid(String file);
64
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 /** returns the FAT file system volume ID for the volume mounted
66 * at the given mount point, or -1 for failure
Wink Saville6d25a992011-06-03 17:03:51 -070067 * @param mountPoint point for FAT volume
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 * @return volume ID or -1
69 */
70 public static native int getFatVolumeId(String mountPoint);
Dianne Hackborn8bdf5932010-10-15 12:54:40 -070071
72 /**
73 * Perform an fsync on the given FileOutputStream. The stream at this
74 * point must be flushed but not yet closed.
75 */
76 public static boolean sync(FileOutputStream stream) {
77 try {
78 if (stream != null) {
79 stream.getFD().sync();
80 }
81 return true;
82 } catch (IOException e) {
83 }
84 return false;
85 }
86
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 // copy a file from srcFile to destFile, return true if succeed, return
88 // false if fail
89 public static boolean copyFile(File srcFile, File destFile) {
90 boolean result = false;
91 try {
92 InputStream in = new FileInputStream(srcFile);
93 try {
94 result = copyToFile(in, destFile);
95 } finally {
96 in.close();
97 }
98 } catch (IOException e) {
99 result = false;
100 }
101 return result;
102 }
Guang Zhu90619812012-10-12 15:50:44 -0700103
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 /**
105 * Copy data from a source stream to destFile.
106 * Return true if succeed, return false if failed.
107 */
108 public static boolean copyToFile(InputStream inputStream, File destFile) {
109 try {
Dianne Hackborn1afd1c92010-03-18 22:47:17 -0700110 if (destFile.exists()) {
111 destFile.delete();
112 }
Dianne Hackborn8bdf5932010-10-15 12:54:40 -0700113 FileOutputStream out = new FileOutputStream(destFile);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 try {
115 byte[] buffer = new byte[4096];
116 int bytesRead;
117 while ((bytesRead = inputStream.read(buffer)) >= 0) {
118 out.write(buffer, 0, bytesRead);
119 }
120 } finally {
Dianne Hackborn8bdf5932010-10-15 12:54:40 -0700121 out.flush();
122 try {
123 out.getFD().sync();
124 } catch (IOException e) {
125 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 out.close();
127 }
128 return true;
129 } catch (IOException e) {
130 return false;
131 }
132 }
133
134 /**
135 * Check if a filename is "safe" (no metacharacters or spaces).
136 * @param file The file to check
137 */
138 public static boolean isFilenameSafe(File file) {
139 // Note, we check whether it matches what's known to be safe,
140 // rather than what's known to be unsafe. Non-ASCII, control
141 // characters, etc. are all unsafe by default.
142 return SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
143 }
144
145 /**
146 * Read a text file into a String, optionally limiting the length.
147 * @param file to read (will not seek, so things like /proc files are OK)
148 * @param max length (positive for head, negative of tail, 0 for no limit)
149 * @param ellipsis to add of the file was truncated (can be null)
150 * @return the contents of the file, possibly truncated
151 * @throws IOException if something goes wrong reading the file
152 */
153 public static String readTextFile(File file, int max, String ellipsis) throws IOException {
154 InputStream input = new FileInputStream(file);
Guang Zhu90619812012-10-12 15:50:44 -0700155 // wrapping a BufferedInputStream around it because when reading /proc with unbuffered
156 // input stream, bytes read not equal to buffer size is not necessarily the correct
157 // indication for EOF; but it is true for BufferedInputStream due to its implementation.
158 BufferedInputStream bis = new BufferedInputStream(input);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 try {
Dan Egnor42471dd2010-01-07 17:25:22 -0800160 long size = file.length();
161 if (max > 0 || (size > 0 && max == 0)) { // "head" mode: read the first N bytes
162 if (size > 0 && (max == 0 || size < max)) max = (int) size;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 byte[] data = new byte[max + 1];
Guang Zhu90619812012-10-12 15:50:44 -0700164 int length = bis.read(data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 if (length <= 0) return "";
166 if (length <= max) return new String(data, 0, length);
167 if (ellipsis == null) return new String(data, 0, max);
168 return new String(data, 0, max) + ellipsis;
Dan Egnor42471dd2010-01-07 17:25:22 -0800169 } else if (max < 0) { // "tail" mode: keep the last N
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 int len;
171 boolean rolled = false;
Jeff Sharkeyd9526902013-03-14 14:11:57 -0700172 byte[] last = null;
173 byte[] data = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 do {
175 if (last != null) rolled = true;
176 byte[] tmp = last; last = data; data = tmp;
177 if (data == null) data = new byte[-max];
Guang Zhu90619812012-10-12 15:50:44 -0700178 len = bis.read(data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 } while (len == data.length);
180
181 if (last == null && len <= 0) return "";
182 if (last == null) return new String(data, 0, len);
183 if (len > 0) {
184 rolled = true;
185 System.arraycopy(last, len, last, 0, last.length - len);
186 System.arraycopy(data, 0, last, last.length - len, len);
187 }
188 if (ellipsis == null || !rolled) return new String(last);
189 return ellipsis + new String(last);
Dan Egnor42471dd2010-01-07 17:25:22 -0800190 } else { // "cat" mode: size unknown, read it all in streaming fashion
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 ByteArrayOutputStream contents = new ByteArrayOutputStream();
192 int len;
193 byte[] data = new byte[1024];
194 do {
Guang Zhu90619812012-10-12 15:50:44 -0700195 len = bis.read(data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 if (len > 0) contents.write(data, 0, len);
197 } while (len == data.length);
198 return contents.toString();
199 }
200 } finally {
Guang Zhu90619812012-10-12 15:50:44 -0700201 bis.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 input.close();
203 }
204 }
Mike Lockwoodda8bb742011-05-28 13:24:04 -0400205
206 /**
207 * Writes string to file. Basically same as "echo -n $string > $filename"
208 *
209 * @param filename
210 * @param string
211 * @throws IOException
212 */
213 public static void stringToFile(String filename, String string) throws IOException {
214 FileWriter out = new FileWriter(filename);
215 try {
216 out.write(string);
217 } finally {
218 out.close();
219 }
220 }
Wink Saville1b9a6a62011-06-04 07:31:35 -0700221
Wink Saville6d25a992011-06-03 17:03:51 -0700222 /**
223 * Computes the checksum of a file using the CRC32 checksum routine.
224 * The value of the checksum is returned.
225 *
226 * @param file the file to checksum, must not be null
227 * @return the checksum value or an exception is thrown.
228 */
229 public static long checksumCrc32(File file) throws FileNotFoundException, IOException {
230 CRC32 checkSummer = new CRC32();
231 CheckedInputStream cis = null;
232
233 try {
234 cis = new CheckedInputStream( new FileInputStream(file), checkSummer);
235 byte[] buf = new byte[128];
236 while(cis.read(buf) >= 0) {
237 // Just read for checksum to get calculated.
238 }
239 return checkSummer.getValue();
240 } finally {
241 if (cis != null) {
242 try {
243 cis.close();
244 } catch (IOException e) {
245 }
246 }
247 }
248 }
Jeff Sharkeyd9526902013-03-14 14:11:57 -0700249
250 /**
251 * Delete older files in a directory until only those matching the given
252 * constraints remain.
253 *
254 * @param minCount Always keep at least this many files.
255 * @param minAge Always keep files younger than this age.
256 */
257 public static void deleteOlderFiles(File dir, int minCount, long minAge) {
258 if (minCount < 0 || minAge < 0) {
259 throw new IllegalArgumentException("Constraints must be positive or 0");
260 }
261
262 final File[] files = dir.listFiles();
263 if (files == null) return;
264
265 // Sort with newest files first
266 Arrays.sort(files, new Comparator<File>() {
267 @Override
268 public int compare(File lhs, File rhs) {
269 return (int) (rhs.lastModified() - lhs.lastModified());
270 }
271 });
272
273 // Keep at least minCount files
274 for (int i = minCount; i < files.length; i++) {
275 final File file = files[i];
276
277 // Keep files newer than minAge
278 final long age = System.currentTimeMillis() - file.lastModified();
279 if (age > minAge) {
280 Log.d(TAG, "Deleting old file " + file);
281 file.delete();
282 }
283 }
284 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285}