191c98d778c80e53a7f458264233375f982dcae14Neil Fuller/* 291c98d778c80e53a7f458264233375f982dcae14Neil Fuller * Copyright (C) 2015 The Android Open Source Project 391c98d778c80e53a7f458264233375f982dcae14Neil Fuller * 491c98d778c80e53a7f458264233375f982dcae14Neil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 591c98d778c80e53a7f458264233375f982dcae14Neil Fuller * you may not use this file except in compliance with the License. 691c98d778c80e53a7f458264233375f982dcae14Neil Fuller * You may obtain a copy of the License at 791c98d778c80e53a7f458264233375f982dcae14Neil Fuller * 891c98d778c80e53a7f458264233375f982dcae14Neil Fuller * http://www.apache.org/licenses/LICENSE-2.0 991c98d778c80e53a7f458264233375f982dcae14Neil Fuller * 1091c98d778c80e53a7f458264233375f982dcae14Neil Fuller * Unless required by applicable law or agreed to in writing, software 1191c98d778c80e53a7f458264233375f982dcae14Neil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 1291c98d778c80e53a7f458264233375f982dcae14Neil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1391c98d778c80e53a7f458264233375f982dcae14Neil Fuller * See the License for the specific language governing permissions and 1491c98d778c80e53a7f458264233375f982dcae14Neil Fuller * limitations under the License. 1591c98d778c80e53a7f458264233375f982dcae14Neil Fuller */ 1691c98d778c80e53a7f458264233375f982dcae14Neil Fullerpackage libcore.tzdata.update; 1791c98d778c80e53a7f458264233375f982dcae14Neil Fuller 1891c98d778c80e53a7f458264233375f982dcae14Neil Fullerimport java.io.BufferedReader; 1991c98d778c80e53a7f458264233375f982dcae14Neil Fullerimport java.io.File; 2091c98d778c80e53a7f458264233375f982dcae14Neil Fullerimport java.io.FileInputStream; 2191c98d778c80e53a7f458264233375f982dcae14Neil Fullerimport java.io.IOException; 2291c98d778c80e53a7f458264233375f982dcae14Neil Fullerimport java.io.InputStreamReader; 2391c98d778c80e53a7f458264233375f982dcae14Neil Fullerimport java.nio.charset.StandardCharsets; 2491c98d778c80e53a7f458264233375f982dcae14Neil Fullerimport java.util.ArrayList; 2591c98d778c80e53a7f458264233375f982dcae14Neil Fullerimport java.util.Arrays; 2691c98d778c80e53a7f458264233375f982dcae14Neil Fullerimport java.util.LinkedList; 2791c98d778c80e53a7f458264233375f982dcae14Neil Fullerimport java.util.List; 2891c98d778c80e53a7f458264233375f982dcae14Neil Fullerimport java.util.zip.CRC32; 2991c98d778c80e53a7f458264233375f982dcae14Neil Fuller 3091c98d778c80e53a7f458264233375f982dcae14Neil Fuller/** 3191c98d778c80e53a7f458264233375f982dcae14Neil Fuller * Utility methods for files operations. 3291c98d778c80e53a7f458264233375f982dcae14Neil Fuller */ 3391c98d778c80e53a7f458264233375f982dcae14Neil Fullerpublic final class FileUtils { 3491c98d778c80e53a7f458264233375f982dcae14Neil Fuller 3591c98d778c80e53a7f458264233375f982dcae14Neil Fuller private FileUtils() { 3691c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 3791c98d778c80e53a7f458264233375f982dcae14Neil Fuller 3891c98d778c80e53a7f458264233375f982dcae14Neil Fuller /** 3991c98d778c80e53a7f458264233375f982dcae14Neil Fuller * Creates a new {@link java.io.File} from the {@code parentDir} and {@code name}, but only if 40e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller * the resulting file would exist beneath {@code parentDir}. Useful if {@code name} could 41e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller * contain "/../" or symlinks. The returned object has a canonicalized path. 4291c98d778c80e53a7f458264233375f982dcae14Neil Fuller * 43e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller * @throws java.io.IOException if the file would not exist beneath {@code parentDir} 4491c98d778c80e53a7f458264233375f982dcae14Neil Fuller */ 4591c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static File createSubFile(File parentDir, String name) throws IOException { 4691c98d778c80e53a7f458264233375f982dcae14Neil Fuller // The subFile must exist beneath parentDir. If name contains "/../" this may not be the 4791c98d778c80e53a7f458264233375f982dcae14Neil Fuller // case so we check. 48e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller File subFile = new File(parentDir, name).getCanonicalFile(); 4991c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (!subFile.getPath().startsWith(parentDir.getCanonicalPath())) { 50e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller throw new IOException(name + " must exist beneath " + parentDir + 51e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller ". Canonicalized subpath: " + subFile); 5291c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 5391c98d778c80e53a7f458264233375f982dcae14Neil Fuller return subFile; 5491c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 5591c98d778c80e53a7f458264233375f982dcae14Neil Fuller 5691c98d778c80e53a7f458264233375f982dcae14Neil Fuller /** 5791c98d778c80e53a7f458264233375f982dcae14Neil Fuller * Makes sure a directory exists. If it doesn't exist, it is created. Parent directories are 5891c98d778c80e53a7f458264233375f982dcae14Neil Fuller * also created as needed. If {@code makeWorldReadable} is {@code true} the directory's default 5991c98d778c80e53a7f458264233375f982dcae14Neil Fuller * permissions will be set. Even when {@code makeWorldReadable} is {@code true}, only 6091c98d778c80e53a7f458264233375f982dcae14Neil Fuller * directories explicitly created will have their permissions set; existing directories are 6191c98d778c80e53a7f458264233375f982dcae14Neil Fuller * untouched. 6291c98d778c80e53a7f458264233375f982dcae14Neil Fuller * 63e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller * @throws IOException if the directory or one of its parents did not already exist and could 64e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller * not be created 6591c98d778c80e53a7f458264233375f982dcae14Neil Fuller */ 6691c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static void ensureDirectoriesExist(File dir, boolean makeWorldReadable) 6791c98d778c80e53a7f458264233375f982dcae14Neil Fuller throws IOException { 6891c98d778c80e53a7f458264233375f982dcae14Neil Fuller LinkedList<File> dirs = new LinkedList<>(); 6991c98d778c80e53a7f458264233375f982dcae14Neil Fuller File currentDir = dir; 7091c98d778c80e53a7f458264233375f982dcae14Neil Fuller do { 7191c98d778c80e53a7f458264233375f982dcae14Neil Fuller dirs.addFirst(currentDir); 7291c98d778c80e53a7f458264233375f982dcae14Neil Fuller currentDir = currentDir.getParentFile(); 7391c98d778c80e53a7f458264233375f982dcae14Neil Fuller } while (currentDir != null); 7491c98d778c80e53a7f458264233375f982dcae14Neil Fuller 7591c98d778c80e53a7f458264233375f982dcae14Neil Fuller for (File dirToCheck : dirs) { 7691c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (!dirToCheck.exists()) { 7791c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (!dirToCheck.mkdir()) { 7891c98d778c80e53a7f458264233375f982dcae14Neil Fuller throw new IOException("Unable to create directory: " + dir); 7991c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 8091c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (makeWorldReadable) { 8191c98d778c80e53a7f458264233375f982dcae14Neil Fuller makeDirectoryWorldAccessible(dirToCheck); 8291c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 8391c98d778c80e53a7f458264233375f982dcae14Neil Fuller } else if (!dirToCheck.isDirectory()) { 8491c98d778c80e53a7f458264233375f982dcae14Neil Fuller throw new IOException(dirToCheck + " exists but is not a directory"); 8591c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 8691c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 8791c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 8891c98d778c80e53a7f458264233375f982dcae14Neil Fuller 8991c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static void makeDirectoryWorldAccessible(File directory) throws IOException { 9091c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (!directory.isDirectory()) { 9191c98d778c80e53a7f458264233375f982dcae14Neil Fuller throw new IOException(directory + " must be a directory"); 9291c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 9391c98d778c80e53a7f458264233375f982dcae14Neil Fuller makeWorldReadable(directory); 9491c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (!directory.setExecutable(true, false /* ownerOnly */)) { 9591c98d778c80e53a7f458264233375f982dcae14Neil Fuller throw new IOException("Unable to make " + directory + " world-executable"); 9691c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 9791c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 9891c98d778c80e53a7f458264233375f982dcae14Neil Fuller 9991c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static void makeWorldReadable(File file) throws IOException { 10091c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (!file.setReadable(true, false /* ownerOnly */)) { 10191c98d778c80e53a7f458264233375f982dcae14Neil Fuller throw new IOException("Unable to make " + file + " world-readable"); 10291c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 10391c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 10491c98d778c80e53a7f458264233375f982dcae14Neil Fuller 10591c98d778c80e53a7f458264233375f982dcae14Neil Fuller /** 10691c98d778c80e53a7f458264233375f982dcae14Neil Fuller * Calculates the checksum from the contents of a file. 10791c98d778c80e53a7f458264233375f982dcae14Neil Fuller */ 10891c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static long calculateChecksum(File file) throws IOException { 10991c98d778c80e53a7f458264233375f982dcae14Neil Fuller final int BUFFER_SIZE = 8196; 11091c98d778c80e53a7f458264233375f982dcae14Neil Fuller CRC32 crc32 = new CRC32(); 11191c98d778c80e53a7f458264233375f982dcae14Neil Fuller try (FileInputStream fis = new FileInputStream(file)) { 11291c98d778c80e53a7f458264233375f982dcae14Neil Fuller byte[] buffer = new byte[BUFFER_SIZE]; 11391c98d778c80e53a7f458264233375f982dcae14Neil Fuller int count; 11491c98d778c80e53a7f458264233375f982dcae14Neil Fuller while ((count = fis.read(buffer)) != -1) { 11591c98d778c80e53a7f458264233375f982dcae14Neil Fuller crc32.update(buffer, 0, count); 11691c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 11791c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 11891c98d778c80e53a7f458264233375f982dcae14Neil Fuller return crc32.getValue(); 11991c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 12091c98d778c80e53a7f458264233375f982dcae14Neil Fuller 12191c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static void rename(File from, File to) throws IOException { 12291c98d778c80e53a7f458264233375f982dcae14Neil Fuller ensureFileDoesNotExist(to); 12391c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (!from.renameTo(to)) { 12491c98d778c80e53a7f458264233375f982dcae14Neil Fuller throw new IOException("Unable to rename " + from + " to " + to); 12591c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 12691c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 12791c98d778c80e53a7f458264233375f982dcae14Neil Fuller 12891c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static void ensureFileDoesNotExist(File file) throws IOException { 12991c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (file.exists()) { 13091c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (!file.isFile()) { 13191c98d778c80e53a7f458264233375f982dcae14Neil Fuller throw new IOException(file + " is not a file"); 13291c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 13391c98d778c80e53a7f458264233375f982dcae14Neil Fuller doDelete(file); 13491c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 13591c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 13691c98d778c80e53a7f458264233375f982dcae14Neil Fuller 13791c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static void doDelete(File file) throws IOException { 13891c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (!file.delete()) { 13991c98d778c80e53a7f458264233375f982dcae14Neil Fuller throw new IOException("Unable to delete: " + file); 14091c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 14191c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 14291c98d778c80e53a7f458264233375f982dcae14Neil Fuller 14391c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static boolean isSymlink(File file) throws IOException { 144e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller String baseName = file.getName(); 145e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller String canonicalPathExceptBaseName = 146e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller new File(file.getParentFile().getCanonicalFile(), baseName).getPath(); 147e9a8e16980a5733852a829c48ed38497cff610b1Neil Fuller return !file.getCanonicalPath().equals(canonicalPathExceptBaseName); 14891c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 14991c98d778c80e53a7f458264233375f982dcae14Neil Fuller 15091c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static void deleteRecursive(File toDelete) throws IOException { 15191c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (toDelete.isDirectory()) { 15291c98d778c80e53a7f458264233375f982dcae14Neil Fuller for (File file : toDelete.listFiles()) { 15391c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (file.isDirectory() && !FileUtils.isSymlink(file)) { 15491c98d778c80e53a7f458264233375f982dcae14Neil Fuller // The isSymlink() check is important so that we don't delete files in other 15591c98d778c80e53a7f458264233375f982dcae14Neil Fuller // directories: only the symlink itself. 15691c98d778c80e53a7f458264233375f982dcae14Neil Fuller deleteRecursive(file); 15791c98d778c80e53a7f458264233375f982dcae14Neil Fuller } else { 15891c98d778c80e53a7f458264233375f982dcae14Neil Fuller // Delete symlinks to directories or files. 15991c98d778c80e53a7f458264233375f982dcae14Neil Fuller FileUtils.doDelete(file); 16091c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 16191c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 16291c98d778c80e53a7f458264233375f982dcae14Neil Fuller String[] remainingFiles = toDelete.list(); 16391c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (remainingFiles.length != 0) { 16491c98d778c80e53a7f458264233375f982dcae14Neil Fuller throw new IOException("Unable to delete files: " + Arrays 16591c98d778c80e53a7f458264233375f982dcae14Neil Fuller .toString(remainingFiles)); 16691c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 16791c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 16891c98d778c80e53a7f458264233375f982dcae14Neil Fuller FileUtils.doDelete(toDelete); 16991c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 17091c98d778c80e53a7f458264233375f982dcae14Neil Fuller 17191c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static boolean filesExist(File rootDir, String... fileNames) throws IOException { 17291c98d778c80e53a7f458264233375f982dcae14Neil Fuller for (String fileName : fileNames) { 17391c98d778c80e53a7f458264233375f982dcae14Neil Fuller File file = new File(rootDir, fileName); 17491c98d778c80e53a7f458264233375f982dcae14Neil Fuller if (!file.exists()) { 17591c98d778c80e53a7f458264233375f982dcae14Neil Fuller return false; 17691c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 17791c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 17891c98d778c80e53a7f458264233375f982dcae14Neil Fuller return true; 17991c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 18091c98d778c80e53a7f458264233375f982dcae14Neil Fuller 18191c98d778c80e53a7f458264233375f982dcae14Neil Fuller /** 18291c98d778c80e53a7f458264233375f982dcae14Neil Fuller * Read all lines from a UTF-8 encoded file, returning them as a list of strings. 18391c98d778c80e53a7f458264233375f982dcae14Neil Fuller */ 18491c98d778c80e53a7f458264233375f982dcae14Neil Fuller public static List<String> readLines(File file) throws IOException { 18591c98d778c80e53a7f458264233375f982dcae14Neil Fuller FileInputStream in = new FileInputStream(file); 18691c98d778c80e53a7f458264233375f982dcae14Neil Fuller try (BufferedReader fileReader = new BufferedReader( 18791c98d778c80e53a7f458264233375f982dcae14Neil Fuller new InputStreamReader(in, StandardCharsets.UTF_8)); 18891c98d778c80e53a7f458264233375f982dcae14Neil Fuller ) { 18991c98d778c80e53a7f458264233375f982dcae14Neil Fuller List<String> lines = new ArrayList<>(); 19091c98d778c80e53a7f458264233375f982dcae14Neil Fuller String line; 19191c98d778c80e53a7f458264233375f982dcae14Neil Fuller while ((line = fileReader.readLine()) != null) { 19291c98d778c80e53a7f458264233375f982dcae14Neil Fuller lines.add(line); 19391c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 19491c98d778c80e53a7f458264233375f982dcae14Neil Fuller return lines; 19591c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 19691c98d778c80e53a7f458264233375f982dcae14Neil Fuller } 19791c98d778c80e53a7f458264233375f982dcae14Neil Fuller} 198