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