AtomicFile.java revision 231cc608d06ffc31c24bf8aa8c8275bdd2636581
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()) { 48231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (!mBaseName.renameTo(mBackupName)) { 49231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName.delete(); 50231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (!mBaseName.renameTo(mBackupName)) { 51231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn Log.w("AtomicFile", "Couldn't rename file " + mBaseName 52231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn + " to backup file " + mBackupName); 53231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 54231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 55231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 56231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileOutputStream str = null; 57231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 58231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str = new FileOutputStream(mBaseName); 59231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e) { 60231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn File parent = mBaseName.getParentFile(); 61231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (!parent.mkdir()) { 62231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't create directory " + mBaseName); 63231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 64231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileUtils.setPermissions( 65231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn parent.getPath(), 66231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, 67231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn -1, -1); 68231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 69231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str = new FileOutputStream(mBaseName); 70231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e2) { 71231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't create " + mBaseName); 72231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 73231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 74231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return str; 75231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 76231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 77231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public void finishWrite(FileOutputStream str) { 78231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (str != null) { 79231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 80231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str.close(); 81231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName.delete(); 82231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (IOException e) { 83231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn Log.w("AtomicFile", "finishWrite: Got exception:", e); 84231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 85231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 86231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 87231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 88231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public void failWrite(FileOutputStream str) { 89231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (str != null) { 90231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 91231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str.close(); 92231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBaseName.delete(); 93231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName.renameTo(mBaseName); 94231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (IOException e) { 95231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn Log.w("AtomicFile", "failWrite: Got exception:", e); 96231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 97231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 98231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 99231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 100231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public FileOutputStream openAppend() throws IOException { 101231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 102231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return new FileOutputStream(mBaseName, true); 103231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e) { 104231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't append " + mBaseName); 105231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 106231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 107231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 108231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public void truncate() throws IOException { 109231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 110231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileOutputStream fos = new FileOutputStream(mBaseName); 111231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn fos.close(); 112231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e) { 113231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't append " + mBaseName); 114231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (IOException e) { 115231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 116231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 117231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 118231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public FileInputStream openRead() throws FileNotFoundException { 119231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (mBackupName.exists()) { 120231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBaseName.delete(); 121231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName.renameTo(mBaseName); 122231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 123231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return new FileInputStream(mBaseName); 124231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 125231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 126231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public byte[] readFully() throws IOException { 127231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileInputStream stream = openRead(); 128231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 129231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn int pos = 0; 130231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn int avail = stream.available(); 131231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn byte[] data = new byte[avail]; 132231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn while (true) { 133231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn int amt = stream.read(data, pos, data.length-pos); 134231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn //Log.i("foo", "Read " + amt + " bytes at " + pos 135231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn // + " of avail " + data.length); 136231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (amt <= 0) { 137231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn //Log.i("foo", "**** FINISHED READING: pos=" + pos 138231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn // + " len=" + data.length); 139231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return data; 140231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 141231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn pos += amt; 142231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn avail = stream.available(); 143231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (avail > data.length-pos) { 144231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn byte[] newData = new byte[pos+avail]; 145231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn System.arraycopy(data, 0, newData, 0, pos); 146231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn data = newData; 147231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 148231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 149231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } finally { 150231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn stream.close(); 151231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 152231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 153231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn} 154