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; 2034385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.Os; 210cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkeyimport android.text.TextUtils; 22d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkeyimport android.util.Log; 23184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkeyimport android.util.Slog; 24184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 2590619816d99154d504a14774c6f2d5f4254ff780Guang Zhuimport java.io.BufferedInputStream; 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.ByteArrayOutputStream; 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File; 28184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkeyimport java.io.FileDescriptor; 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileInputStream; 306d25a990afffd5eb385aba3043d5dfad36f1539aWink Savilleimport java.io.FileNotFoundException; 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileOutputStream; 32da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwoodimport java.io.FileWriter; 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException; 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.InputStream; 35d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkeyimport java.util.Arrays; 36d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkeyimport java.util.Comparator; 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.regex.Pattern; 386d25a990afffd5eb385aba3043d5dfad36f1539aWink Savilleimport java.util.zip.CRC32; 396d25a990afffd5eb385aba3043d5dfad36f1539aWink Savilleimport java.util.zip.CheckedInputStream; 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Tools for managing files. Not for public consumption. 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @hide 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 456d25a990afffd5eb385aba3043d5dfad36f1539aWink Savillepublic class FileUtils { 46d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey private static final String TAG = "FileUtils"; 47d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IRWXU = 00700; 499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IRUSR = 00400; 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IWUSR = 00200; 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IXUSR = 00100; 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IRWXG = 00070; 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IRGRP = 00040; 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IWGRP = 00020; 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IXGRP = 00010; 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IRWXO = 00007; 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IROTH = 00004; 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IWOTH = 00002; 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IXOTH = 00001; 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** Regular expression for safe filenames: no spaces or metacharacters */ 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+"); 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 66184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey /** 67184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * Set owner and mode of of given {@link File}. 68184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * 69184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param mode to apply through {@code chmod} 70184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param uid to apply through {@code chown}, or -1 to leave unchanged 71184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param gid to apply through {@code chown}, or -1 to leave unchanged 72184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @return 0 on success, otherwise errno. 73184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey */ 74184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey public static int setPermissions(File path, int mode, int uid, int gid) { 75184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return setPermissions(path.getAbsolutePath(), mode, uid, gid); 76184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 77184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 78184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey /** 79184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * Set owner and mode of of given path. 80184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * 81184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param mode to apply through {@code chmod} 82184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param uid to apply through {@code chown}, or -1 to leave unchanged 83184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param gid to apply through {@code chown}, or -1 to leave unchanged 84184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @return 0 on success, otherwise errno. 85184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey */ 86184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey public static int setPermissions(String path, int mode, int uid, int gid) { 87184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey try { 8834385d352da19805ae948215e2edbeedd16b7941Elliott Hughes Os.chmod(path, mode); 89184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } catch (ErrnoException e) { 90184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey Slog.w(TAG, "Failed to chmod(" + path + "): " + e); 91184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return e.errno; 92184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 93184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 94184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey if (uid >= 0 || gid >= 0) { 95184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey try { 9634385d352da19805ae948215e2edbeedd16b7941Elliott Hughes Os.chown(path, uid, gid); 97184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } catch (ErrnoException e) { 98184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey Slog.w(TAG, "Failed to chown(" + path + "): " + e); 99184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return e.errno; 100184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 101184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 102184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 103184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return 0; 104184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 106184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey /** 107184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * Set owner and mode of of given {@link FileDescriptor}. 108184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * 109184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param mode to apply through {@code chmod} 110184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param uid to apply through {@code chown}, or -1 to leave unchanged 111184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param gid to apply through {@code chown}, or -1 to leave unchanged 112184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @return 0 on success, otherwise errno. 113184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey */ 114184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) { 115184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey try { 11634385d352da19805ae948215e2edbeedd16b7941Elliott Hughes Os.fchmod(fd, mode); 117184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } catch (ErrnoException e) { 118184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey Slog.w(TAG, "Failed to fchmod(): " + e); 119184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return e.errno; 120184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 121184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 122184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey if (uid >= 0 || gid >= 0) { 123184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey try { 12434385d352da19805ae948215e2edbeedd16b7941Elliott Hughes Os.fchown(fd, uid, gid); 125184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } catch (ErrnoException e) { 126184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey Slog.w(TAG, "Failed to fchown(): " + e); 127184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return e.errno; 128184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 129184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 130184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 131184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return 0; 132184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 133184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 134184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey /** 135184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * Return owning UID of given path, otherwise -1. 136184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey */ 137184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey public static int getUid(String path) { 138184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey try { 13934385d352da19805ae948215e2edbeedd16b7941Elliott Hughes return Os.stat(path).st_uid; 140184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } catch (ErrnoException e) { 141184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return -1; 142184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 143184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 144053f61d6a6e23825e680dc49982e55c5b4299d61Dianne Hackborn 1458bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn /** 1468bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn * Perform an fsync on the given FileOutputStream. The stream at this 1478bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn * point must be flushed but not yet closed. 1488bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn */ 1498bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn public static boolean sync(FileOutputStream stream) { 1508bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn try { 1518bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn if (stream != null) { 1528bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn stream.getFD().sync(); 1538bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn } 1548bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn return true; 1558bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn } catch (IOException e) { 1568bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn } 1578bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn return false; 1588bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn } 1598bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // copy a file from srcFile to destFile, return true if succeed, return 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // false if fail 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static boolean copyFile(File srcFile, File destFile) { 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean result = false; 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project InputStream in = new FileInputStream(srcFile); 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project result = copyToFile(in, destFile); 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project in.close(); 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IOException e) { 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project result = false; 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return result; 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 17690619816d99154d504a14774c6f2d5f4254ff780Guang Zhu 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copy data from a source stream to destFile. 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return true if succeed, return false if failed. 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static boolean copyToFile(InputStream inputStream, File destFile) { 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 1831afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn if (destFile.exists()) { 1841afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn destFile.delete(); 1851afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn } 1868bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn FileOutputStream out = new FileOutputStream(destFile); 1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project byte[] buffer = new byte[4096]; 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int bytesRead; 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while ((bytesRead = inputStream.read(buffer)) >= 0) { 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.write(buffer, 0, bytesRead); 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 1948bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn out.flush(); 1958bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn try { 1968bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn out.getFD().sync(); 1978bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn } catch (IOException e) { 1988bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn } 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.close(); 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IOException e) { 2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Check if a filename is "safe" (no metacharacters or spaces). 2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param file The file to check 2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static boolean isFilenameSafe(File file) { 2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Note, we check whether it matches what's known to be safe, 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // rather than what's known to be unsafe. Non-ASCII, control 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // characters, etc. are all unsafe by default. 2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches(); 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Read a text file into a String, optionally limiting the length. 2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param file to read (will not seek, so things like /proc files are OK) 2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param max length (positive for head, negative of tail, 0 for no limit) 2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param ellipsis to add of the file was truncated (can be null) 2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return the contents of the file, possibly truncated 2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @throws IOException if something goes wrong reading the file 2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static String readTextFile(File file, int max, String ellipsis) throws IOException { 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project InputStream input = new FileInputStream(file); 22890619816d99154d504a14774c6f2d5f4254ff780Guang Zhu // wrapping a BufferedInputStream around it because when reading /proc with unbuffered 22990619816d99154d504a14774c6f2d5f4254ff780Guang Zhu // input stream, bytes read not equal to buffer size is not necessarily the correct 23090619816d99154d504a14774c6f2d5f4254ff780Guang Zhu // indication for EOF; but it is true for BufferedInputStream due to its implementation. 23190619816d99154d504a14774c6f2d5f4254ff780Guang Zhu BufferedInputStream bis = new BufferedInputStream(input); 2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 23342471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor long size = file.length(); 23442471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor if (max > 0 || (size > 0 && max == 0)) { // "head" mode: read the first N bytes 23542471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor if (size > 0 && (max == 0 || size < max)) max = (int) size; 2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project byte[] data = new byte[max + 1]; 23790619816d99154d504a14774c6f2d5f4254ff780Guang Zhu int length = bis.read(data); 2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (length <= 0) return ""; 2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (length <= max) return new String(data, 0, length); 2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ellipsis == null) return new String(data, 0, max); 2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return new String(data, 0, max) + ellipsis; 24242471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor } else if (max < 0) { // "tail" mode: keep the last N 2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int len; 2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean rolled = false; 245d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey byte[] last = null; 246d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey byte[] data = null; 2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project do { 2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (last != null) rolled = true; 2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project byte[] tmp = last; last = data; data = tmp; 2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (data == null) data = new byte[-max]; 25190619816d99154d504a14774c6f2d5f4254ff780Guang Zhu len = bis.read(data); 2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } while (len == data.length); 2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (last == null && len <= 0) return ""; 2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (last == null) return new String(data, 0, len); 2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (len > 0) { 2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project rolled = true; 2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project System.arraycopy(last, len, last, 0, last.length - len); 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project System.arraycopy(data, 0, last, last.length - len, len); 2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ellipsis == null || !rolled) return new String(last); 2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ellipsis + new String(last); 26342471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor } else { // "cat" mode: size unknown, read it all in streaming fashion 2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ByteArrayOutputStream contents = new ByteArrayOutputStream(); 2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int len; 2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project byte[] data = new byte[1024]; 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project do { 26890619816d99154d504a14774c6f2d5f4254ff780Guang Zhu len = bis.read(data); 2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (len > 0) contents.write(data, 0, len); 2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } while (len == data.length); 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return contents.toString(); 2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 27490619816d99154d504a14774c6f2d5f4254ff780Guang Zhu bis.close(); 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project input.close(); 2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 278da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood 279da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood /** 280da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood * Writes string to file. Basically same as "echo -n $string > $filename" 281da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood * 282da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood * @param filename 283da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood * @param string 284da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood * @throws IOException 285da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood */ 286da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood public static void stringToFile(String filename, String string) throws IOException { 287da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood FileWriter out = new FileWriter(filename); 288da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood try { 289da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood out.write(string); 290da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood } finally { 291da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood out.close(); 292da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood } 293da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood } 2941b9a6a6e58fd73b5d1b6a434d17f0a69806858ecWink Saville 2956d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville /** 2966d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville * Computes the checksum of a file using the CRC32 checksum routine. 2976d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville * The value of the checksum is returned. 2986d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville * 2996d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville * @param file the file to checksum, must not be null 3006d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville * @return the checksum value or an exception is thrown. 3016d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville */ 3026d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville public static long checksumCrc32(File file) throws FileNotFoundException, IOException { 3036d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville CRC32 checkSummer = new CRC32(); 3046d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville CheckedInputStream cis = null; 3056d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville 3066d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville try { 3076d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville cis = new CheckedInputStream( new FileInputStream(file), checkSummer); 3086d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville byte[] buf = new byte[128]; 3096d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville while(cis.read(buf) >= 0) { 3106d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville // Just read for checksum to get calculated. 3116d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } 3126d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville return checkSummer.getValue(); 3136d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } finally { 3146d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville if (cis != null) { 3156d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville try { 3166d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville cis.close(); 3176d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } catch (IOException e) { 3186d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } 3196d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } 3206d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } 3216d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } 322d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 323d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey /** 324d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey * Delete older files in a directory until only those matching the given 325d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey * constraints remain. 326d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey * 327d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey * @param minCount Always keep at least this many files. 328d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey * @param minAge Always keep files younger than this age. 329ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey * @return if any files were deleted. 330d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey */ 331ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey public static boolean deleteOlderFiles(File dir, int minCount, long minAge) { 332d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey if (minCount < 0 || minAge < 0) { 333d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey throw new IllegalArgumentException("Constraints must be positive or 0"); 334d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey } 335d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 336d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey final File[] files = dir.listFiles(); 337ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey if (files == null) return false; 338d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 339d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey // Sort with newest files first 340d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey Arrays.sort(files, new Comparator<File>() { 341d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey @Override 342d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey public int compare(File lhs, File rhs) { 343d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey return (int) (rhs.lastModified() - lhs.lastModified()); 344d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey } 345d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey }); 346d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 347d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey // Keep at least minCount files 348ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey boolean deleted = false; 349d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey for (int i = minCount; i < files.length; i++) { 350d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey final File file = files[i]; 351d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 352d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey // Keep files newer than minAge 353d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey final long age = System.currentTimeMillis() - file.lastModified(); 354d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey if (age > minAge) { 355ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey if (file.delete()) { 356ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey Log.d(TAG, "Deleted old file " + file); 357ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey deleted = true; 358ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey } 359d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey } 360d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey } 361ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey return deleted; 362d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey } 3634ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey 3644ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey /** 3654ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * Test if a file lives under the given directory, either as a direct child 3664ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * or a distant grandchild. 3674ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * <p> 3684ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * Both files <em>must</em> have been resolved using 3694ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * {@link File#getCanonicalFile()} to avoid symlink or path traversal 3704ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * attacks. 3714ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey */ 3724ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey public static boolean contains(File dir, File file) { 373d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey if (file == null) return false; 374d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey 37521de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey String dirPath = dir.getAbsolutePath(); 37621de56a94668e0fda1b8bb4ee4f99a09b40d28fdJeff Sharkey String filePath = file.getAbsolutePath(); 3774ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey 3784ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey if (dirPath.equals(filePath)) { 3794ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey return true; 3804ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey } 3814ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey 3824ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey if (!dirPath.endsWith("/")) { 3834ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey dirPath += "/"; 3844ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey } 3854ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey return filePath.startsWith(dirPath); 3864ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey } 3873a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey 38857dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey public static boolean deleteContents(File dir) { 3893a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey File[] files = dir.listFiles(); 39057dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey boolean success = true; 3913a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey if (files != null) { 3923a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey for (File file : files) { 3933a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey if (file.isDirectory()) { 39457dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey success &= deleteContents(file); 3953a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 39673767b9d607d99b3a027619b5c6b7f1a09b7673dJeff Sharkey if (!file.delete()) { 39773767b9d607d99b3a027619b5c6b7f1a09b7673dJeff Sharkey Log.w(TAG, "Failed to delete " + file); 39873767b9d607d99b3a027619b5c6b7f1a09b7673dJeff Sharkey success = false; 39973767b9d607d99b3a027619b5c6b7f1a09b7673dJeff Sharkey } 4003a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 4013a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 40257dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey return success; 4033a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 4043a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey 4050cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey private static boolean isValidExtFilenameChar(char c) { 4060cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey switch (c) { 4070cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '\0': 4080cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '/': 4090cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return false; 4100cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey default: 4110cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return true; 4120cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4130cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4140cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey 4153a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey /** 4160cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * Check if given filename is valid for an ext4 filesystem. 4173a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey */ 4183a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey public static boolean isValidExtFilename(String name) { 4190cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return (name != null) && name.equals(buildValidExtFilename(name)); 4200cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4210cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey 4220cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey /** 4230cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * Mutate the given filename to make it valid for an ext4 filesystem, 4240cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * replacing any invalid characters with "_". 4250cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey */ 4260cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey public static String buildValidExtFilename(String name) { 4273a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) { 4280cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return "(invalid)"; 4293a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 4300cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey final StringBuilder res = new StringBuilder(name.length()); 4313a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey for (int i = 0; i < name.length(); i++) { 4323a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey final char c = name.charAt(i); 4330cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey if (isValidExtFilenameChar(c)) { 4340cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey res.append(c); 4350cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } else { 4360cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey res.append('_'); 4370cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4380cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4390cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return res.toString(); 4400cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4410cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey 4420cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey private static boolean isValidFatFilenameChar(char c) { 4430cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey if ((0x00 <= c && c <= 0x1f)) { 4440cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return false; 4450cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4460cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey switch (c) { 4470cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '"': 4480cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '*': 4490cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '/': 4500cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case ':': 4510cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '<': 4520cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '>': 4530cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '?': 4540cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '\\': 4550cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '|': 4560cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case 0x7F: 4573a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey return false; 4580cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey default: 4590cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return true; 4600cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4610cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4620cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey 4630cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey /** 4640cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * Check if given filename is valid for a FAT filesystem. 4650cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey */ 4660cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey public static boolean isValidFatFilename(String name) { 4670cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return (name != null) && name.equals(buildValidFatFilename(name)); 4680cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4690cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey 4700cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey /** 4710cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * Mutate the given filename to make it valid for a FAT filesystem, 4720cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * replacing any invalid characters with "_". 4730cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey */ 4740cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey public static String buildValidFatFilename(String name) { 4750cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) { 4760cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return "(invalid)"; 4770cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4780cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey final StringBuilder res = new StringBuilder(name.length()); 4790cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey for (int i = 0; i < name.length(); i++) { 4800cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey final char c = name.charAt(i); 4810cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey if (isValidFatFilenameChar(c)) { 4820cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey res.append(c); 4830cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } else { 4840cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey res.append('_'); 4853a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 4863a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 4870cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return res.toString(); 4883a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 48957dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey 49057dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey public static String rewriteAfterRename(File beforeDir, File afterDir, String path) { 491d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey if (path == null) return null; 49257dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey final File result = rewriteAfterRename(beforeDir, afterDir, new File(path)); 49357dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey return (result != null) ? result.getAbsolutePath() : null; 49457dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey } 49557dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey 496d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey public static String[] rewriteAfterRename(File beforeDir, File afterDir, String[] paths) { 497d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey if (paths == null) return null; 498d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey final String[] result = new String[paths.length]; 499d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey for (int i = 0; i < paths.length; i++) { 500d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey result[i] = rewriteAfterRename(beforeDir, afterDir, paths[i]); 501d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey } 502d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey return result; 503d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey } 504d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey 50557dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey /** 50657dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey * Given a path under the "before" directory, rewrite it to live under the 50757dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey * "after" directory. For example, {@code /before/foo/bar.txt} would become 50857dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey * {@code /after/foo/bar.txt}. 50957dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey */ 51057dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey public static File rewriteAfterRename(File beforeDir, File afterDir, File file) { 511d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey if (file == null) return null; 51257dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey if (contains(beforeDir, file)) { 51357dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey final String splice = file.getAbsolutePath().substring( 51457dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey beforeDir.getAbsolutePath().length()); 51557dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey return new File(afterDir, splice); 51657dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey } 51757dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey return null; 51857dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey } 5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 520