1/*
2 * Copyright (C) 2006 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
17package android.os;
18
19import java.io.BufferedInputStream;
20import java.io.ByteArrayOutputStream;
21import java.io.File;
22import java.io.FileInputStream;
23import java.io.FileNotFoundException;
24import java.io.FileOutputStream;
25import java.io.FileWriter;
26import java.io.IOException;
27import java.io.InputStream;
28import java.util.regex.Pattern;
29import java.util.zip.CRC32;
30import java.util.zip.CheckedInputStream;
31
32/**
33 * Tools for managing files.  Not for public consumption.
34 * @hide
35 */
36public class FileUtils {
37    public static final int S_IRWXU = 00700;
38    public static final int S_IRUSR = 00400;
39    public static final int S_IWUSR = 00200;
40    public static final int S_IXUSR = 00100;
41
42    public static final int S_IRWXG = 00070;
43    public static final int S_IRGRP = 00040;
44    public static final int S_IWGRP = 00020;
45    public static final int S_IXGRP = 00010;
46
47    public static final int S_IRWXO = 00007;
48    public static final int S_IROTH = 00004;
49    public static final int S_IWOTH = 00002;
50    public static final int S_IXOTH = 00001;
51
52    /** Regular expression for safe filenames: no spaces or metacharacters */
53    private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
54
55    public static native int setPermissions(String file, int mode, int uid, int gid);
56
57    /** returns the FAT file system volume ID for the volume mounted
58     * at the given mount point, or -1 for failure
59     * @param mountPoint point for FAT volume
60     * @return volume ID or -1
61     */
62    public static native int getFatVolumeId(String mountPoint);
63
64    /**
65     * Perform an fsync on the given FileOutputStream.  The stream at this
66     * point must be flushed but not yet closed.
67     */
68    public static boolean sync(FileOutputStream stream) {
69        try {
70            if (stream != null) {
71                stream.getFD().sync();
72            }
73            return true;
74        } catch (IOException e) {
75        }
76        return false;
77    }
78
79    // copy a file from srcFile to destFile, return true if succeed, return
80    // false if fail
81    public static boolean copyFile(File srcFile, File destFile) {
82        boolean result = false;
83        try {
84            InputStream in = new FileInputStream(srcFile);
85            try {
86                result = copyToFile(in, destFile);
87            } finally  {
88                in.close();
89            }
90        } catch (IOException e) {
91            result = false;
92        }
93        return result;
94    }
95
96    /**
97     * Copy data from a source stream to destFile.
98     * Return true if succeed, return false if failed.
99     */
100    public static boolean copyToFile(InputStream inputStream, File destFile) {
101        try {
102            if (destFile.exists()) {
103                destFile.delete();
104            }
105            FileOutputStream out = new FileOutputStream(destFile);
106            try {
107                byte[] buffer = new byte[4096];
108                int bytesRead;
109                while ((bytesRead = inputStream.read(buffer)) >= 0) {
110                    out.write(buffer, 0, bytesRead);
111                }
112            } finally {
113                out.flush();
114                try {
115                    out.getFD().sync();
116                } catch (IOException e) {
117                }
118                out.close();
119            }
120            return true;
121        } catch (IOException e) {
122            return false;
123        }
124    }
125
126    /**
127     * Check if a filename is "safe" (no metacharacters or spaces).
128     * @param file  The file to check
129     */
130    public static boolean isFilenameSafe(File file) {
131        // Note, we check whether it matches what's known to be safe,
132        // rather than what's known to be unsafe.  Non-ASCII, control
133        // characters, etc. are all unsafe by default.
134        return SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
135    }
136
137    /**
138     * Read a text file into a String, optionally limiting the length.
139     * @param file to read (will not seek, so things like /proc files are OK)
140     * @param max length (positive for head, negative of tail, 0 for no limit)
141     * @param ellipsis to add of the file was truncated (can be null)
142     * @return the contents of the file, possibly truncated
143     * @throws IOException if something goes wrong reading the file
144     */
145    public static String readTextFile(File file, int max, String ellipsis) throws IOException {
146        InputStream input = new FileInputStream(file);
147        // wrapping a BufferedInputStream around it because when reading /proc with unbuffered
148        // input stream, bytes read not equal to buffer size is not necessarily the correct
149        // indication for EOF; but it is true for BufferedInputStream due to its implementation.
150        BufferedInputStream bis = new BufferedInputStream(input);
151        try {
152            long size = file.length();
153            if (max > 0 || (size > 0 && max == 0)) {  // "head" mode: read the first N bytes
154                if (size > 0 && (max == 0 || size < max)) max = (int) size;
155                byte[] data = new byte[max + 1];
156                int length = bis.read(data);
157                if (length <= 0) return "";
158                if (length <= max) return new String(data, 0, length);
159                if (ellipsis == null) return new String(data, 0, max);
160                return new String(data, 0, max) + ellipsis;
161            } else if (max < 0) {  // "tail" mode: keep the last N
162                int len;
163                boolean rolled = false;
164                byte[] last = null, data = null;
165                do {
166                    if (last != null) rolled = true;
167                    byte[] tmp = last; last = data; data = tmp;
168                    if (data == null) data = new byte[-max];
169                    len = bis.read(data);
170                } while (len == data.length);
171
172                if (last == null && len <= 0) return "";
173                if (last == null) return new String(data, 0, len);
174                if (len > 0) {
175                    rolled = true;
176                    System.arraycopy(last, len, last, 0, last.length - len);
177                    System.arraycopy(data, 0, last, last.length - len, len);
178                }
179                if (ellipsis == null || !rolled) return new String(last);
180                return ellipsis + new String(last);
181            } else {  // "cat" mode: size unknown, read it all in streaming fashion
182                ByteArrayOutputStream contents = new ByteArrayOutputStream();
183                int len;
184                byte[] data = new byte[1024];
185                do {
186                    len = bis.read(data);
187                    if (len > 0) contents.write(data, 0, len);
188                } while (len == data.length);
189                return contents.toString();
190            }
191        } finally {
192            bis.close();
193            input.close();
194        }
195    }
196
197   /**
198     * Writes string to file. Basically same as "echo -n $string > $filename"
199     *
200     * @param filename
201     * @param string
202     * @throws IOException
203     */
204    public static void stringToFile(String filename, String string) throws IOException {
205        FileWriter out = new FileWriter(filename);
206        try {
207            out.write(string);
208        } finally {
209            out.close();
210        }
211    }
212
213    /**
214     * Computes the checksum of a file using the CRC32 checksum routine.
215     * The value of the checksum is returned.
216     *
217     * @param file  the file to checksum, must not be null
218     * @return the checksum value or an exception is thrown.
219     */
220    public static long checksumCrc32(File file) throws FileNotFoundException, IOException {
221        CRC32 checkSummer = new CRC32();
222        CheckedInputStream cis = null;
223
224        try {
225            cis = new CheckedInputStream( new FileInputStream(file), checkSummer);
226            byte[] buf = new byte[128];
227            while(cis.read(buf) >= 0) {
228                // Just read for checksum to get calculated.
229            }
230            return checkSummer.getValue();
231        } finally {
232            if (cis != null) {
233                try {
234                    cis.close();
235                } catch (IOException e) {
236                }
237            }
238        }
239    }
240}
241