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