FileUtils.java revision 3a44f3f1b446315ef894e01d2ab9b5388c2bd8c4
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.os;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1934385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.ErrnoException;
203a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.text.TextUtils;
2134385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.Os;
2234385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.OsConstants;
23d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkeyimport android.util.Log;
24184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkeyimport android.util.Slog;
25184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey
2690619816d99154d504a14774c6f2d5f4254ff780Guang Zhuimport java.io.BufferedInputStream;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.ByteArrayOutputStream;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File;
29184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkeyimport java.io.FileDescriptor;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileInputStream;
316d25a990afffd5eb385aba3043d5dfad36f1539aWink Savilleimport java.io.FileNotFoundException;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileOutputStream;
33da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwoodimport java.io.FileWriter;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.InputStream;
36d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkeyimport java.util.Arrays;
37d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkeyimport java.util.Comparator;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.regex.Pattern;
396d25a990afffd5eb385aba3043d5dfad36f1539aWink Savilleimport java.util.zip.CRC32;
406d25a990afffd5eb385aba3043d5dfad36f1539aWink Savilleimport java.util.zip.CheckedInputStream;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Tools for managing files.  Not for public consumption.
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @hide
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
466d25a990afffd5eb385aba3043d5dfad36f1539aWink Savillepublic class FileUtils {
47d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey    private static final String TAG = "FileUtils";
48d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IRWXU = 00700;
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IRUSR = 00400;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IWUSR = 00200;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IXUSR = 00100;
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IRWXG = 00070;
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IRGRP = 00040;
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IWGRP = 00020;
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IXGRP = 00010;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IRWXO = 00007;
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IROTH = 00004;
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IWOTH = 00002;
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int S_IXOTH = 00001;
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Regular expression for safe filenames: no spaces or metacharacters */
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
67184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    /**
68184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * Set owner and mode of of given {@link File}.
69184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     *
70184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @param mode to apply through {@code chmod}
71184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @param uid to apply through {@code chown}, or -1 to leave unchanged
72184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @param gid to apply through {@code chown}, or -1 to leave unchanged
73184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @return 0 on success, otherwise errno.
74184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     */
75184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    public static int setPermissions(File path, int mode, int uid, int gid) {
76184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        return setPermissions(path.getAbsolutePath(), mode, uid, gid);
77184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    }
78184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey
79184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    /**
80184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * Set owner and mode of of given path.
81184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     *
82184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @param mode to apply through {@code chmod}
83184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @param uid to apply through {@code chown}, or -1 to leave unchanged
84184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @param gid to apply through {@code chown}, or -1 to leave unchanged
85184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @return 0 on success, otherwise errno.
86184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     */
87184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    public static int setPermissions(String path, int mode, int uid, int gid) {
88184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        try {
8934385d352da19805ae948215e2edbeedd16b7941Elliott Hughes            Os.chmod(path, mode);
90184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        } catch (ErrnoException e) {
91184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey            Slog.w(TAG, "Failed to chmod(" + path + "): " + e);
92184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey            return e.errno;
93184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        }
94184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey
95184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        if (uid >= 0 || gid >= 0) {
96184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey            try {
9734385d352da19805ae948215e2edbeedd16b7941Elliott Hughes                Os.chown(path, uid, gid);
98184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey            } catch (ErrnoException e) {
99184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey                Slog.w(TAG, "Failed to chown(" + path + "): " + e);
100184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey                return e.errno;
101184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey            }
102184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        }
103184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey
104184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        return 0;
105184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    }
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
107184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    /**
108184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * Set owner and mode of of given {@link FileDescriptor}.
109184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     *
110184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @param mode to apply through {@code chmod}
111184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @param uid to apply through {@code chown}, or -1 to leave unchanged
112184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @param gid to apply through {@code chown}, or -1 to leave unchanged
113184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * @return 0 on success, otherwise errno.
114184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     */
115184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) {
116184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        try {
11734385d352da19805ae948215e2edbeedd16b7941Elliott Hughes            Os.fchmod(fd, mode);
118184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        } catch (ErrnoException e) {
119184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey            Slog.w(TAG, "Failed to fchmod(): " + e);
120184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey            return e.errno;
121184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        }
122184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey
123184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        if (uid >= 0 || gid >= 0) {
124184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey            try {
12534385d352da19805ae948215e2edbeedd16b7941Elliott Hughes                Os.fchown(fd, uid, gid);
126184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey            } catch (ErrnoException e) {
127184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey                Slog.w(TAG, "Failed to fchown(): " + e);
128184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey                return e.errno;
129184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey            }
130184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        }
131184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey
132184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        return 0;
133184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    }
134184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey
135184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    /**
136184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     * Return owning UID of given path, otherwise -1.
137184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey     */
138184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    public static int getUid(String path) {
139184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        try {
14034385d352da19805ae948215e2edbeedd16b7941Elliott Hughes            return Os.stat(path).st_uid;
141184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        } catch (ErrnoException e) {
142184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey            return -1;
143184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey        }
144184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey    }
145053f61d6a6e23825e680dc49982e55c5b4299d61Dianne Hackborn
1468bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn    /**
1478bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn     * Perform an fsync on the given FileOutputStream.  The stream at this
1488bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn     * point must be flushed but not yet closed.
1498bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn     */
1508bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn    public static boolean sync(FileOutputStream stream) {
1518bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn        try {
1528bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn            if (stream != null) {
1538bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn                stream.getFD().sync();
1548bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn            }
1558bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn            return true;
1568bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn        } catch (IOException e) {
1578bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn        }
1588bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn        return false;
1598bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn    }
1608bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // copy a file from srcFile to destFile, return true if succeed, return
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // false if fail
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean copyFile(File srcFile, File destFile) {
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean result = false;
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            InputStream in = new FileInputStream(srcFile);
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                result = copyToFile(in, destFile);
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } finally  {
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                in.close();
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            result = false;
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return result;
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17790619816d99154d504a14774c6f2d5f4254ff780Guang Zhu
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Copy data from a source stream to destFile.
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return true if succeed, return false if failed.
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean copyToFile(InputStream inputStream, File destFile) {
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
1841afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn            if (destFile.exists()) {
1851afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                destFile.delete();
1861afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn            }
1878bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn            FileOutputStream out = new FileOutputStream(destFile);
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                byte[] buffer = new byte[4096];
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int bytesRead;
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while ((bytesRead = inputStream.read(buffer)) >= 0) {
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.write(buffer, 0, bytesRead);
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } finally {
1958bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn                out.flush();
1968bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn                try {
1978bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn                    out.getFD().sync();
1988bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn                } catch (IOException e) {
1998bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn                }
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.close();
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Check if a filename is "safe" (no metacharacters or spaces).
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param file  The file to check
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean isFilenameSafe(File file) {
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Note, we check whether it matches what's known to be safe,
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // rather than what's known to be unsafe.  Non-ASCII, control
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // characters, etc. are all unsafe by default.
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Read a text file into a String, optionally limiting the length.
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param file to read (will not seek, so things like /proc files are OK)
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param max length (positive for head, negative of tail, 0 for no limit)
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param ellipsis to add of the file was truncated (can be null)
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the contents of the file, possibly truncated
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws IOException if something goes wrong reading the file
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String readTextFile(File file, int max, String ellipsis) throws IOException {
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        InputStream input = new FileInputStream(file);
22990619816d99154d504a14774c6f2d5f4254ff780Guang Zhu        // wrapping a BufferedInputStream around it because when reading /proc with unbuffered
23090619816d99154d504a14774c6f2d5f4254ff780Guang Zhu        // input stream, bytes read not equal to buffer size is not necessarily the correct
23190619816d99154d504a14774c6f2d5f4254ff780Guang Zhu        // indication for EOF; but it is true for BufferedInputStream due to its implementation.
23290619816d99154d504a14774c6f2d5f4254ff780Guang Zhu        BufferedInputStream bis = new BufferedInputStream(input);
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
23442471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor            long size = file.length();
23542471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor            if (max > 0 || (size > 0 && max == 0)) {  // "head" mode: read the first N bytes
23642471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor                if (size > 0 && (max == 0 || size < max)) max = (int) size;
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                byte[] data = new byte[max + 1];
23890619816d99154d504a14774c6f2d5f4254ff780Guang Zhu                int length = bis.read(data);
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (length <= 0) return "";
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (length <= max) return new String(data, 0, length);
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (ellipsis == null) return new String(data, 0, max);
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return new String(data, 0, max) + ellipsis;
24342471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor            } else if (max < 0) {  // "tail" mode: keep the last N
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int len;
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                boolean rolled = false;
246d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey                byte[] last = null;
247d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey                byte[] data = null;
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                do {
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (last != null) rolled = true;
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    byte[] tmp = last; last = data; data = tmp;
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (data == null) data = new byte[-max];
25290619816d99154d504a14774c6f2d5f4254ff780Guang Zhu                    len = bis.read(data);
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } while (len == data.length);
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (last == null && len <= 0) return "";
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (last == null) return new String(data, 0, len);
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (len > 0) {
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    rolled = true;
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    System.arraycopy(last, len, last, 0, last.length - len);
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    System.arraycopy(data, 0, last, last.length - len, len);
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (ellipsis == null || !rolled) return new String(last);
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return ellipsis + new String(last);
26442471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor            } else {  // "cat" mode: size unknown, read it all in streaming fashion
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ByteArrayOutputStream contents = new ByteArrayOutputStream();
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int len;
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                byte[] data = new byte[1024];
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                do {
26990619816d99154d504a14774c6f2d5f4254ff780Guang Zhu                    len = bis.read(data);
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (len > 0) contents.write(data, 0, len);
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } while (len == data.length);
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return contents.toString();
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
27590619816d99154d504a14774c6f2d5f4254ff780Guang Zhu            bis.close();
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            input.close();
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
279da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood
280da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood   /**
281da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood     * Writes string to file. Basically same as "echo -n $string > $filename"
282da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood     *
283da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood     * @param filename
284da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood     * @param string
285da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood     * @throws IOException
286da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood     */
287da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood    public static void stringToFile(String filename, String string) throws IOException {
288da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood        FileWriter out = new FileWriter(filename);
289da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood        try {
290da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood            out.write(string);
291da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood        } finally {
292da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood            out.close();
293da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood        }
294da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood    }
2951b9a6a6e58fd73b5d1b6a434d17f0a69806858ecWink Saville
2966d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville    /**
2976d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville     * Computes the checksum of a file using the CRC32 checksum routine.
2986d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville     * The value of the checksum is returned.
2996d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville     *
3006d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville     * @param file  the file to checksum, must not be null
3016d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville     * @return the checksum value or an exception is thrown.
3026d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville     */
3036d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville    public static long checksumCrc32(File file) throws FileNotFoundException, IOException {
3046d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville        CRC32 checkSummer = new CRC32();
3056d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville        CheckedInputStream cis = null;
3066d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville
3076d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville        try {
3086d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville            cis = new CheckedInputStream( new FileInputStream(file), checkSummer);
3096d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville            byte[] buf = new byte[128];
3106d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville            while(cis.read(buf) >= 0) {
3116d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville                // Just read for checksum to get calculated.
3126d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville            }
3136d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville            return checkSummer.getValue();
3146d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville        } finally {
3156d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville            if (cis != null) {
3166d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville                try {
3176d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville                    cis.close();
3186d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville                } catch (IOException e) {
3196d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville                }
3206d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville            }
3216d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville        }
3226d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville    }
323d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey
324d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey    /**
325d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey     * Delete older files in a directory until only those matching the given
326d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey     * constraints remain.
327d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey     *
328d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey     * @param minCount Always keep at least this many files.
329d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey     * @param minAge Always keep files younger than this age.
330ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey     * @return if any files were deleted.
331d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey     */
332ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey    public static boolean deleteOlderFiles(File dir, int minCount, long minAge) {
333d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey        if (minCount < 0 || minAge < 0) {
334d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey            throw new IllegalArgumentException("Constraints must be positive or 0");
335d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey        }
336d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey
337d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey        final File[] files = dir.listFiles();
338ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey        if (files == null) return false;
339d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey
340d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey        // Sort with newest files first
341d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey        Arrays.sort(files, new Comparator<File>() {
342d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey            @Override
343d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey            public int compare(File lhs, File rhs) {
344d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey                return (int) (rhs.lastModified() - lhs.lastModified());
345d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey            }
346d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey        });
347d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey
348d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey        // Keep at least minCount files
349ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey        boolean deleted = false;
350d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey        for (int i = minCount; i < files.length; i++) {
351d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey            final File file = files[i];
352d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey
353d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey            // Keep files newer than minAge
354d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey            final long age = System.currentTimeMillis() - file.lastModified();
355d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey            if (age > minAge) {
356ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey                if (file.delete()) {
357ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey                    Log.d(TAG, "Deleted old file " + file);
358ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey                    deleted = true;
359ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey                }
360d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey            }
361d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey        }
362ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey        return deleted;
363d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey    }
3644ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey
3654ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey    /**
3664ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey     * Test if a file lives under the given directory, either as a direct child
3674ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey     * or a distant grandchild.
3684ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey     * <p>
3694ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey     * Both files <em>must</em> have been resolved using
3704ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey     * {@link File#getCanonicalFile()} to avoid symlink or path traversal
3714ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey     * attacks.
3724ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey     */
3734ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey    public static boolean contains(File dir, File file) {
37421de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        String dirPath = dir.getAbsolutePath();
37521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey        String filePath = file.getAbsolutePath();
3764ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey
3774ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey        if (dirPath.equals(filePath)) {
3784ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey            return true;
3794ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey        }
3804ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey
3814ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey        if (!dirPath.endsWith("/")) {
3824ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey            dirPath += "/";
3834ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey        }
3844ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey        return filePath.startsWith(dirPath);
3854ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey    }
3863a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
3873a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    public static void deleteContents(File dir) {
3883a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        File[] files = dir.listFiles();
3893a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        if (files != null) {
3903a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            for (File file : files) {
3913a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                if (file.isDirectory()) {
3923a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                    deleteContents(file);
3933a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                }
3943a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                file.delete();
3953a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
3963a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
3973a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
3983a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
3993a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    /**
4003a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey     * Assert that given filename is valid on ext4.
4013a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey     */
4023a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    public static boolean isValidExtFilename(String name) {
4033a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
4043a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            return false;
4053a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
4063a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        for (int i = 0; i < name.length(); i++) {
4073a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            final char c = name.charAt(i);
4083a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            if (c == '\0' || c == '/') {
4093a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                return false;
4103a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
4113a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
4123a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        return true;
4133a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
415