• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package libcore.tzdata.update;
17 
18 import java.io.BufferedReader;
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.nio.charset.StandardCharsets;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.zip.CRC32;
29 
30 /**
31  * Utility methods for files operations.
32  */
33 public final class FileUtils {
34 
FileUtils()35     private FileUtils() {
36     }
37 
38     /**
39      * Creates a new {@link java.io.File} from the {@code parentDir} and {@code name}, but only if
40      * the resulting file would exist beneath {@code parentDir}. Useful if {@code name} could
41      * contain "/../" or symlinks. The returned object has a canonicalized path.
42      *
43      * @throws java.io.IOException if the file would not exist beneath {@code parentDir}
44      */
createSubFile(File parentDir, String name)45     public static File createSubFile(File parentDir, String name) throws IOException {
46         // The subFile must exist beneath parentDir. If name contains "/../" this may not be the
47         // case so we check.
48         File subFile = new File(parentDir, name).getCanonicalFile();
49         if (!subFile.getPath().startsWith(parentDir.getCanonicalPath())) {
50             throw new IOException(name + " must exist beneath " + parentDir +
51                     ". Canonicalized subpath: " + subFile);
52         }
53         return subFile;
54     }
55 
56     /**
57      * Makes sure a directory exists. If it doesn't exist, it is created. Parent directories are
58      * also created as needed. If {@code makeWorldReadable} is {@code true} the directory's default
59      * permissions will be set. Even when {@code makeWorldReadable} is {@code true}, only
60      * directories explicitly created will have their permissions set; existing directories are
61      * untouched.
62      *
63      * @throws IOException if the directory or one of its parents did not already exist and could
64      *     not be created
65      */
ensureDirectoriesExist(File dir, boolean makeWorldReadable)66     public static void ensureDirectoriesExist(File dir, boolean makeWorldReadable)
67             throws IOException {
68         LinkedList<File> dirs = new LinkedList<>();
69         File currentDir = dir;
70         do {
71             dirs.addFirst(currentDir);
72             currentDir = currentDir.getParentFile();
73         } while (currentDir != null);
74 
75         for (File dirToCheck : dirs) {
76             if (!dirToCheck.exists()) {
77                 if (!dirToCheck.mkdir()) {
78                     throw new IOException("Unable to create directory: " + dir);
79                 }
80                 if (makeWorldReadable) {
81                     makeDirectoryWorldAccessible(dirToCheck);
82                 }
83             } else if (!dirToCheck.isDirectory()) {
84                 throw new IOException(dirToCheck + " exists but is not a directory");
85             }
86         }
87     }
88 
makeDirectoryWorldAccessible(File directory)89     public static void makeDirectoryWorldAccessible(File directory) throws IOException {
90         if (!directory.isDirectory()) {
91             throw new IOException(directory + " must be a directory");
92         }
93         makeWorldReadable(directory);
94         if (!directory.setExecutable(true, false /* ownerOnly */)) {
95             throw new IOException("Unable to make " + directory + " world-executable");
96         }
97     }
98 
makeWorldReadable(File file)99     public static void makeWorldReadable(File file) throws IOException {
100         if (!file.setReadable(true, false /* ownerOnly */)) {
101             throw new IOException("Unable to make " + file + " world-readable");
102         }
103     }
104 
105     /**
106      * Calculates the checksum from the contents of a file.
107      */
calculateChecksum(File file)108     public static long calculateChecksum(File file) throws IOException {
109         final int BUFFER_SIZE = 8196;
110         CRC32 crc32 = new CRC32();
111         try (FileInputStream fis = new FileInputStream(file)) {
112             byte[] buffer = new byte[BUFFER_SIZE];
113             int count;
114             while ((count = fis.read(buffer)) != -1) {
115                 crc32.update(buffer, 0, count);
116             }
117         }
118         return crc32.getValue();
119     }
120 
rename(File from, File to)121     public static void rename(File from, File to) throws IOException {
122         ensureFileDoesNotExist(to);
123         if (!from.renameTo(to)) {
124             throw new IOException("Unable to rename " + from + " to " + to);
125         }
126     }
127 
ensureFileDoesNotExist(File file)128     public static void ensureFileDoesNotExist(File file) throws IOException {
129         if (file.exists()) {
130             if (!file.isFile()) {
131                 throw new IOException(file + " is not a file");
132             }
133             doDelete(file);
134         }
135     }
136 
doDelete(File file)137     public static void doDelete(File file) throws IOException {
138         if (!file.delete()) {
139             throw new IOException("Unable to delete: " + file);
140         }
141     }
142 
isSymlink(File file)143     public static boolean isSymlink(File file) throws IOException {
144         String baseName = file.getName();
145         String canonicalPathExceptBaseName =
146                 new File(file.getParentFile().getCanonicalFile(), baseName).getPath();
147         return !file.getCanonicalPath().equals(canonicalPathExceptBaseName);
148     }
149 
deleteRecursive(File toDelete)150     public static void deleteRecursive(File toDelete) throws IOException {
151         if (toDelete.isDirectory()) {
152             for (File file : toDelete.listFiles()) {
153                 if (file.isDirectory() && !FileUtils.isSymlink(file)) {
154                     // The isSymlink() check is important so that we don't delete files in other
155                     // directories: only the symlink itself.
156                     deleteRecursive(file);
157                 } else {
158                     // Delete symlinks to directories or files.
159                     FileUtils.doDelete(file);
160                 }
161             }
162             String[] remainingFiles = toDelete.list();
163             if (remainingFiles.length != 0) {
164                 throw new IOException("Unable to delete files: " + Arrays
165                         .toString(remainingFiles));
166             }
167         }
168         FileUtils.doDelete(toDelete);
169     }
170 
filesExist(File rootDir, String... fileNames)171     public static boolean filesExist(File rootDir, String... fileNames) throws IOException {
172         for (String fileName : fileNames) {
173             File file = new File(rootDir, fileName);
174             if (!file.exists()) {
175                 return false;
176             }
177         }
178         return true;
179     }
180 
181     /**
182      * Read all lines from a UTF-8 encoded file, returning them as a list of strings.
183      */
readLines(File file)184     public static List<String> readLines(File file) throws IOException {
185         FileInputStream in = new FileInputStream(file);
186         try (BufferedReader fileReader = new BufferedReader(
187                 new InputStreamReader(in, StandardCharsets.UTF_8));
188         ) {
189             List<String> lines = new ArrayList<>();
190             String line;
191             while ((line = fileReader.readLine()) != null) {
192                 lines.add(line);
193             }
194             return lines;
195         }
196     }
197 }
198