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.ByteArrayOutputStream;
20import java.io.File;
21import java.io.FileInputStream;
22import java.io.FileNotFoundException;
23import java.io.FileOutputStream;
24import java.io.FileWriter;
25import java.io.IOException;
26import java.io.InputStream;
27import java.util.regex.Pattern;
28import java.util.zip.CRC32;
29import java.util.zip.CheckedInputStream;
30
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
53    /**
54     * File status information. This class maps directly to the POSIX stat structure.
55     * @hide
56     */
57    public static final class FileStatus {
58        public int dev;
59        public int ino;
60        public int mode;
61        public int nlink;
62        public int uid;
63        public int gid;
64        public int rdev;
65        public long size;
66        public int blksize;
67        public long blocks;
68        public long atime;
69        public long mtime;
70        public long ctime;
71    }
72
73    /**
74     * Get the status for the given path. This is equivalent to the POSIX stat(2) system call.
75     * @param path The path of the file to be stat'd.
76     * @param status Optional argument to fill in. It will only fill in the status if the file
77     * exists.
78     * @return true if the file exists and false if it does not exist. If you do not have
79     * permission to stat the file, then this method will return false.
80     */
81    public static boolean getFileStatus(String path, FileStatus status) {
82        StrictMode.noteDiskRead();
83        return getFileStatusNative(path, status);
84    }
85
86    private static native boolean getFileStatusNative(String path, FileStatus status);
87
88    /** Regular expression for safe filenames: no spaces or metacharacters */
89    private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
90
91    public static native int setPermissions(String file, int mode, int uid, int gid);
92
93    public static native int getPermissions(String file, int[] outPermissions);
94
95    public static native int setUMask(int mask);
96
97    /** returns the FAT file system volume ID for the volume mounted
98     * at the given mount point, or -1 for failure
99     * @param mountPoint point for FAT volume
100     * @return volume ID or -1
101     */
102    public static native int getFatVolumeId(String mountPoint);
103
104    /**
105     * Perform an fsync on the given FileOutputStream.  The stream at this
106     * point must be flushed but not yet closed.
107     */
108    public static boolean sync(FileOutputStream stream) {
109        try {
110            if (stream != null) {
111                stream.getFD().sync();
112            }
113            return true;
114        } catch (IOException e) {
115        }
116        return false;
117    }
118
119    // copy a file from srcFile to destFile, return true if succeed, return
120    // false if fail
121    public static boolean copyFile(File srcFile, File destFile) {
122        boolean result = false;
123        try {
124            InputStream in = new FileInputStream(srcFile);
125            try {
126                result = copyToFile(in, destFile);
127            } finally  {
128                in.close();
129            }
130        } catch (IOException e) {
131            result = false;
132        }
133        return result;
134    }
135
136    /**
137     * Copy data from a source stream to destFile.
138     * Return true if succeed, return false if failed.
139     */
140    public static boolean copyToFile(InputStream inputStream, File destFile) {
141        try {
142            if (destFile.exists()) {
143                destFile.delete();
144            }
145            FileOutputStream out = new FileOutputStream(destFile);
146            try {
147                byte[] buffer = new byte[4096];
148                int bytesRead;
149                while ((bytesRead = inputStream.read(buffer)) >= 0) {
150                    out.write(buffer, 0, bytesRead);
151                }
152            } finally {
153                out.flush();
154                try {
155                    out.getFD().sync();
156                } catch (IOException e) {
157                }
158                out.close();
159            }
160            return true;
161        } catch (IOException e) {
162            return false;
163        }
164    }
165
166    /**
167     * Check if a filename is "safe" (no metacharacters or spaces).
168     * @param file  The file to check
169     */
170    public static boolean isFilenameSafe(File file) {
171        // Note, we check whether it matches what's known to be safe,
172        // rather than what's known to be unsafe.  Non-ASCII, control
173        // characters, etc. are all unsafe by default.
174        return SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
175    }
176
177    /**
178     * Read a text file into a String, optionally limiting the length.
179     * @param file to read (will not seek, so things like /proc files are OK)
180     * @param max length (positive for head, negative of tail, 0 for no limit)
181     * @param ellipsis to add of the file was truncated (can be null)
182     * @return the contents of the file, possibly truncated
183     * @throws IOException if something goes wrong reading the file
184     */
185    public static String readTextFile(File file, int max, String ellipsis) throws IOException {
186        InputStream input = new FileInputStream(file);
187        try {
188            long size = file.length();
189            if (max > 0 || (size > 0 && max == 0)) {  // "head" mode: read the first N bytes
190                if (size > 0 && (max == 0 || size < max)) max = (int) size;
191                byte[] data = new byte[max + 1];
192                int length = input.read(data);
193                if (length <= 0) return "";
194                if (length <= max) return new String(data, 0, length);
195                if (ellipsis == null) return new String(data, 0, max);
196                return new String(data, 0, max) + ellipsis;
197            } else if (max < 0) {  // "tail" mode: keep the last N
198                int len;
199                boolean rolled = false;
200                byte[] last = null, data = null;
201                do {
202                    if (last != null) rolled = true;
203                    byte[] tmp = last; last = data; data = tmp;
204                    if (data == null) data = new byte[-max];
205                    len = input.read(data);
206                } while (len == data.length);
207
208                if (last == null && len <= 0) return "";
209                if (last == null) return new String(data, 0, len);
210                if (len > 0) {
211                    rolled = true;
212                    System.arraycopy(last, len, last, 0, last.length - len);
213                    System.arraycopy(data, 0, last, last.length - len, len);
214                }
215                if (ellipsis == null || !rolled) return new String(last);
216                return ellipsis + new String(last);
217            } else {  // "cat" mode: size unknown, read it all in streaming fashion
218                ByteArrayOutputStream contents = new ByteArrayOutputStream();
219                int len;
220                byte[] data = new byte[1024];
221                do {
222                    len = input.read(data);
223                    if (len > 0) contents.write(data, 0, len);
224                } while (len == data.length);
225                return contents.toString();
226            }
227        } finally {
228            input.close();
229        }
230    }
231
232   /**
233     * Writes string to file. Basically same as "echo -n $string > $filename"
234     *
235     * @param filename
236     * @param string
237     * @throws IOException
238     */
239    public static void stringToFile(String filename, String string) throws IOException {
240        FileWriter out = new FileWriter(filename);
241        try {
242            out.write(string);
243        } finally {
244            out.close();
245        }
246    }
247
248    /**
249     * Computes the checksum of a file using the CRC32 checksum routine.
250     * The value of the checksum is returned.
251     *
252     * @param file  the file to checksum, must not be null
253     * @return the checksum value or an exception is thrown.
254     */
255    public static long checksumCrc32(File file) throws FileNotFoundException, IOException {
256        CRC32 checkSummer = new CRC32();
257        CheckedInputStream cis = null;
258
259        try {
260            cis = new CheckedInputStream( new FileInputStream(file), checkSummer);
261            byte[] buf = new byte[128];
262            while(cis.read(buf) >= 0) {
263                // Just read for checksum to get calculated.
264            }
265            return checkSummer.getValue();
266        } finally {
267            if (cis != null) {
268                try {
269                    cis.close();
270                } catch (IOException e) {
271                }
272            }
273        }
274    }
275}
276