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