blob: ff3e27785b74ffd72f6fd07c5cbe09344e5d5cc4 [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;
Jeff Sharkey184a0102013-07-10 16:19:52 -070020import android.util.Slog;
21
22import libcore.io.ErrnoException;
23import libcore.io.IoUtils;
24import libcore.io.Libcore;
25import libcore.io.OsConstants;
Jeff Sharkeyd9526902013-03-14 14:11:57 -070026
Guang Zhu90619812012-10-12 15:50:44 -070027import java.io.BufferedInputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import java.io.ByteArrayOutputStream;
29import java.io.File;
Jeff Sharkey184a0102013-07-10 16:19:52 -070030import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import java.io.FileInputStream;
Wink Saville6d25a992011-06-03 17:03:51 -070032import java.io.FileNotFoundException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import java.io.FileOutputStream;
Mike Lockwoodda8bb742011-05-28 13:24:04 -040034import java.io.FileWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import java.io.IOException;
36import java.io.InputStream;
Jeff Sharkeyd9526902013-03-14 14:11:57 -070037import java.util.Arrays;
38import java.util.Comparator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import java.util.regex.Pattern;
Wink Saville6d25a992011-06-03 17:03:51 -070040import java.util.zip.CRC32;
41import java.util.zip.CheckedInputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043/**
44 * Tools for managing files. Not for public consumption.
45 * @hide
46 */
Wink Saville6d25a992011-06-03 17:03:51 -070047public class FileUtils {
Jeff Sharkeyd9526902013-03-14 14:11:57 -070048 private static final String TAG = "FileUtils";
49
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
65 /** Regular expression for safe filenames: no spaces or metacharacters */
66 private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
67
Jeff Sharkey184a0102013-07-10 16:19:52 -070068 /**
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 */
76 public static int setPermissions(File path, int mode, int uid, int gid) {
77 return setPermissions(path.getAbsolutePath(), mode, uid, gid);
78 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
Jeff Sharkey184a0102013-07-10 16:19:52 -070080 /**
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 */
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 */
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 */
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 }
Dianne Hackborn053f61d2013-06-26 18:07:43 -0700146
Dianne Hackborn8bdf5932010-10-15 12:54:40 -0700147 /**
148 * Perform an fsync on the given FileOutputStream. The stream at this
149 * point must be flushed but not yet closed.
150 */
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 // copy a file from srcFile to destFile, return true if succeed, return
163 // false if fail
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 }
Guang Zhu90619812012-10-12 15:50:44 -0700178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 /**
180 * Copy data from a source stream to destFile.
181 * Return true if succeed, return false if failed.
182 */
183 public static boolean copyToFile(InputStream inputStream, File destFile) {
184 try {
Dianne Hackborn1afd1c92010-03-18 22:47:17 -0700185 if (destFile.exists()) {
186 destFile.delete();
187 }
Dianne Hackborn8bdf5932010-10-15 12:54:40 -0700188 FileOutputStream out = new FileOutputStream(destFile);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 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 {
Dianne Hackborn8bdf5932010-10-15 12:54:40 -0700196 out.flush();
197 try {
198 out.getFD().sync();
199 } catch (IOException e) {
200 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 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 */
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 */
228 public static String readTextFile(File file, int max, String ellipsis) throws IOException {
229 InputStream input = new FileInputStream(file);
Guang Zhu90619812012-10-12 15:50:44 -0700230 // 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);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 try {
Dan Egnor42471dd2010-01-07 17:25:22 -0800235 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 byte[] data = new byte[max + 1];
Guang Zhu90619812012-10-12 15:50:44 -0700239 int length = bis.read(data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 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;
Dan Egnor42471dd2010-01-07 17:25:22 -0800244 } else if (max < 0) { // "tail" mode: keep the last N
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 int len;
246 boolean rolled = false;
Jeff Sharkeyd9526902013-03-14 14:11:57 -0700247 byte[] last = null;
248 byte[] data = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 do {
250 if (last != null) rolled = true;
251 byte[] tmp = last; last = data; data = tmp;
252 if (data == null) data = new byte[-max];
Guang Zhu90619812012-10-12 15:50:44 -0700253 len = bis.read(data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 } 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);
Dan Egnor42471dd2010-01-07 17:25:22 -0800265 } else { // "cat" mode: size unknown, read it all in streaming fashion
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 ByteArrayOutputStream contents = new ByteArrayOutputStream();
267 int len;
268 byte[] data = new byte[1024];
269 do {
Guang Zhu90619812012-10-12 15:50:44 -0700270 len = bis.read(data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 if (len > 0) contents.write(data, 0, len);
272 } while (len == data.length);
273 return contents.toString();
274 }
275 } finally {
Guang Zhu90619812012-10-12 15:50:44 -0700276 bis.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 input.close();
278 }
279 }
Mike Lockwoodda8bb742011-05-28 13:24:04 -0400280
281 /**
282 * Writes string to file. Basically same as "echo -n $string > $filename"
283 *
284 * @param filename
285 * @param string
286 * @throws IOException
287 */
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 }
Wink Saville1b9a6a62011-06-04 07:31:35 -0700296
Wink Saville6d25a992011-06-03 17:03:51 -0700297 /**
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 */
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 }
Jeff Sharkeyd9526902013-03-14 14:11:57 -0700324
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 */
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 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360}