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