AtomicFile.java revision 8bdf5935c0db4a66ab33a10b43398d2523cfa15d
1231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn/* 2231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * Copyright (C) 2009 The Android Open Source Project 3231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * 4231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * Licensed under the Apache License, Version 2.0 (the "License"); 5231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * you may not use this file except in compliance with the License. 6231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * You may obtain a copy of the License at 7231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * 8231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * http://www.apache.org/licenses/LICENSE-2.0 9231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * 10231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * Unless required by applicable law or agreed to in writing, software 11231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * distributed under the License is distributed on an "AS IS" BASIS, 12231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * See the License for the specific language governing permissions and 14231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * limitations under the License. 15231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn */ 16231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 17231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornpackage com.android.internal.os; 18231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 19231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport android.os.FileUtils; 20231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport android.util.Log; 21231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 22231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport java.io.File; 23231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport java.io.FileInputStream; 24231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport java.io.FileNotFoundException; 25231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport java.io.FileOutputStream; 26231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornimport java.io.IOException; 27231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 28231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn/** 29231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * Helper class for performing atomic operations on a file, by creating a 30231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn * backup file until a write has successfully completed. 31231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn */ 32231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackbornpublic class AtomicFile { 33231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn private final File mBaseName; 34231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn private final File mBackupName; 35231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 36231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public AtomicFile(File baseName) { 37231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBaseName = baseName; 38231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName = new File(baseName.getPath() + ".bak"); 39231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 40231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 41231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public File getBaseFile() { 42231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return mBaseName; 43231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 44231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 45231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public FileOutputStream startWrite() throws IOException { 46231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn // Rename the current file so it may be used as a backup during the next read 47231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (mBaseName.exists()) { 481afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn if (!mBackupName.exists()) { 49231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (!mBaseName.renameTo(mBackupName)) { 50231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn Log.w("AtomicFile", "Couldn't rename file " + mBaseName 51231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn + " to backup file " + mBackupName); 52231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 531afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn } else { 541afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn mBaseName.delete(); 55231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 56231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 57231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileOutputStream str = null; 58231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 59231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str = new FileOutputStream(mBaseName); 60231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e) { 61231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn File parent = mBaseName.getParentFile(); 62231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (!parent.mkdir()) { 63231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't create directory " + mBaseName); 64231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 65231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileUtils.setPermissions( 66231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn parent.getPath(), 67231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, 68231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn -1, -1); 69231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 70231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str = new FileOutputStream(mBaseName); 71231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e2) { 72231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't create " + mBaseName); 73231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 74231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 75231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return str; 76231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 77231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 78231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public void finishWrite(FileOutputStream str) { 79231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (str != null) { 808bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn FileUtils.sync(str); 81231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 82231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str.close(); 83231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName.delete(); 84231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (IOException e) { 85231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn Log.w("AtomicFile", "finishWrite: Got exception:", e); 86231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 87231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 88231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 89231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 90231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public void failWrite(FileOutputStream str) { 91231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (str != null) { 928bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn FileUtils.sync(str); 93231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 94231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str.close(); 95231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBaseName.delete(); 96231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName.renameTo(mBaseName); 97231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (IOException e) { 98231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn Log.w("AtomicFile", "failWrite: Got exception:", e); 99231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 100231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 101231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 102231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 103231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public FileOutputStream openAppend() throws IOException { 104231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 105231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return new FileOutputStream(mBaseName, true); 106231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e) { 107231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't append " + mBaseName); 108231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 109231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 110231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 111231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public void truncate() throws IOException { 112231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 113231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileOutputStream fos = new FileOutputStream(mBaseName); 1148bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn FileUtils.sync(fos); 115231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn fos.close(); 116231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e) { 117231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't append " + mBaseName); 118231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (IOException e) { 119231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 120231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 121231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 122231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public FileInputStream openRead() throws FileNotFoundException { 123231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (mBackupName.exists()) { 124231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBaseName.delete(); 125231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName.renameTo(mBaseName); 126231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 127231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return new FileInputStream(mBaseName); 128231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 129231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 130231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public byte[] readFully() throws IOException { 131231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileInputStream stream = openRead(); 132231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 133231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn int pos = 0; 134231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn int avail = stream.available(); 135231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn byte[] data = new byte[avail]; 136231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn while (true) { 137231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn int amt = stream.read(data, pos, data.length-pos); 138231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn //Log.i("foo", "Read " + amt + " bytes at " + pos 139231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn // + " of avail " + data.length); 140231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (amt <= 0) { 141231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn //Log.i("foo", "**** FINISHED READING: pos=" + pos 142231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn // + " len=" + data.length); 143231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return data; 144231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 145231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn pos += amt; 146231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn avail = stream.available(); 147231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (avail > data.length-pos) { 148231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn byte[] newData = new byte[pos+avail]; 149231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn System.arraycopy(data, 0, newData, 0, pos); 150231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn data = newData; 151231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 152231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 153231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } finally { 154231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn stream.close(); 155231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 156231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 157231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn} 158