Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2009 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 com.android.internal.os;
     18 
     19 import android.os.FileUtils;
     20 import android.util.Log;
     21 
     22 import java.io.File;
     23 import java.io.FileInputStream;
     24 import java.io.FileNotFoundException;
     25 import java.io.FileOutputStream;
     26 import java.io.IOException;
     27 
     28 /**
     29  * Helper class for performing atomic operations on a file, by creating a
     30  * backup file until a write has successfully completed.
     31  */
     32 public class AtomicFile {
     33     private final File mBaseName;
     34     private final File mBackupName;
     35 
     36     public AtomicFile(File baseName) {
     37         mBaseName = baseName;
     38         mBackupName = new File(baseName.getPath() + ".bak");
     39     }
     40 
     41     public File getBaseFile() {
     42         return mBaseName;
     43     }
     44 
     45     public FileOutputStream startWrite() throws IOException {
     46         // Rename the current file so it may be used as a backup during the next read
     47         if (mBaseName.exists()) {
     48             if (!mBackupName.exists()) {
     49                 if (!mBaseName.renameTo(mBackupName)) {
     50                     Log.w("AtomicFile", "Couldn't rename file " + mBaseName
     51                             + " to backup file " + mBackupName);
     52                 }
     53             } else {
     54                 mBaseName.delete();
     55             }
     56         }
     57         FileOutputStream str = null;
     58         try {
     59             str = new FileOutputStream(mBaseName);
     60         } catch (FileNotFoundException e) {
     61             File parent = mBaseName.getParentFile();
     62             if (!parent.mkdir()) {
     63                 throw new IOException("Couldn't create directory " + mBaseName);
     64             }
     65             FileUtils.setPermissions(
     66                 parent.getPath(),
     67                 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
     68                 -1, -1);
     69             try {
     70                 str = new FileOutputStream(mBaseName);
     71             } catch (FileNotFoundException e2) {
     72                 throw new IOException("Couldn't create " + mBaseName);
     73             }
     74         }
     75         return str;
     76     }
     77 
     78     public void finishWrite(FileOutputStream str) {
     79         if (str != null) {
     80             FileUtils.sync(str);
     81             try {
     82                 str.close();
     83                 mBackupName.delete();
     84             } catch (IOException e) {
     85                 Log.w("AtomicFile", "finishWrite: Got exception:", e);
     86             }
     87         }
     88     }
     89 
     90     public void failWrite(FileOutputStream str) {
     91         if (str != null) {
     92             FileUtils.sync(str);
     93             try {
     94                 str.close();
     95                 mBaseName.delete();
     96                 mBackupName.renameTo(mBaseName);
     97             } catch (IOException e) {
     98                 Log.w("AtomicFile", "failWrite: Got exception:", e);
     99             }
    100         }
    101     }
    102 
    103     public FileOutputStream openAppend() throws IOException {
    104         try {
    105             return new FileOutputStream(mBaseName, true);
    106         } catch (FileNotFoundException e) {
    107             throw new IOException("Couldn't append " + mBaseName);
    108         }
    109     }
    110 
    111     public void truncate() throws IOException {
    112         try {
    113             FileOutputStream fos = new FileOutputStream(mBaseName);
    114             FileUtils.sync(fos);
    115             fos.close();
    116         } catch (FileNotFoundException e) {
    117             throw new IOException("Couldn't append " + mBaseName);
    118         } catch (IOException e) {
    119         }
    120     }
    121 
    122     public FileInputStream openRead() throws FileNotFoundException {
    123         if (mBackupName.exists()) {
    124             mBaseName.delete();
    125             mBackupName.renameTo(mBaseName);
    126         }
    127         return new FileInputStream(mBaseName);
    128     }
    129 
    130     public byte[] readFully() throws IOException {
    131         FileInputStream stream = openRead();
    132         try {
    133             int pos = 0;
    134             int avail = stream.available();
    135             byte[] data = new byte[avail];
    136             while (true) {
    137                 int amt = stream.read(data, pos, data.length-pos);
    138                 //Log.i("foo", "Read " + amt + " bytes at " + pos
    139                 //        + " of avail " + data.length);
    140                 if (amt <= 0) {
    141                     //Log.i("foo", "**** FINISHED READING: pos=" + pos
    142                     //        + " len=" + data.length);
    143                     return data;
    144                 }
    145                 pos += amt;
    146                 avail = stream.available();
    147                 if (avail > data.length-pos) {
    148                     byte[] newData = new byte[pos+avail];
    149                     System.arraycopy(data, 0, newData, 0, pos);
    150                     data = newData;
    151                 }
    152             }
    153         } finally {
    154             stream.close();
    155         }
    156     }
    157 }
    158