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. 31763631cd247e21b167063023e7dd1395e05ebbf6Jeff Brown * <p> 32763631cd247e21b167063023e7dd1395e05ebbf6Jeff Brown * Atomic file guarantees file integrity by ensuring that a file has 33763631cd247e21b167063023e7dd1395e05ebbf6Jeff Brown * been completely written and sync'd to disk before removing its backup. 34763631cd247e21b167063023e7dd1395e05ebbf6Jeff Brown * As long as the backup file exists, the original file is considered 35763631cd247e21b167063023e7dd1395e05ebbf6Jeff Brown * to be invalid (left over from a previous attempt to write the file). 36763631cd247e21b167063023e7dd1395e05ebbf6Jeff Brown * </p><p> 37763631cd247e21b167063023e7dd1395e05ebbf6Jeff Brown * Atomic file does not confer any file locking semantics. 38763631cd247e21b167063023e7dd1395e05ebbf6Jeff Brown * Do not use this class when the file may be accessed or modified concurrently 39763631cd247e21b167063023e7dd1395e05ebbf6Jeff Brown * by multiple threads or processes. The caller is responsible for ensuring 40763631cd247e21b167063023e7dd1395e05ebbf6Jeff Brown * appropriate mutual exclusion invariants whenever it accesses the file. 41763631cd247e21b167063023e7dd1395e05ebbf6Jeff Brown * </p> 42231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn */ 430068d3dcf1f1a804554a1a09e3b173ac12651786Dianne Hackbornpublic final class AtomicFile { 44231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn private final File mBaseName; 45231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn private final File mBackupName; 46231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 47231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public AtomicFile(File baseName) { 48231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBaseName = baseName; 49231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName = new File(baseName.getPath() + ".bak"); 50231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 51231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 52231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public File getBaseFile() { 53231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return mBaseName; 54231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 55231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 56231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public FileOutputStream startWrite() throws IOException { 57231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn // Rename the current file so it may be used as a backup during the next read 58231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (mBaseName.exists()) { 591afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn if (!mBackupName.exists()) { 60231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (!mBaseName.renameTo(mBackupName)) { 61231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn Log.w("AtomicFile", "Couldn't rename file " + mBaseName 62231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn + " to backup file " + mBackupName); 63231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 641afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn } else { 651afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn mBaseName.delete(); 66231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 67231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 68231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileOutputStream str = null; 69231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 70231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str = new FileOutputStream(mBaseName); 71231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e) { 72231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn File parent = mBaseName.getParentFile(); 73231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (!parent.mkdir()) { 74231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't create directory " + mBaseName); 75231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 76231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileUtils.setPermissions( 77231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn parent.getPath(), 78231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, 79231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn -1, -1); 80231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 81231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str = new FileOutputStream(mBaseName); 82231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e2) { 83231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't create " + mBaseName); 84231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 85231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 86231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return str; 87231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 88231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 89231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public void finishWrite(FileOutputStream str) { 90231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (str != null) { 918bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn FileUtils.sync(str); 92231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 93231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str.close(); 94231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName.delete(); 95231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (IOException e) { 96231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn Log.w("AtomicFile", "finishWrite: Got exception:", e); 97231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 98231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 99231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 100231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 101231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public void failWrite(FileOutputStream str) { 102231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (str != null) { 1038bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn FileUtils.sync(str); 104231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 105231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn str.close(); 106231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBaseName.delete(); 107231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName.renameTo(mBaseName); 108231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (IOException e) { 109231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn Log.w("AtomicFile", "failWrite: Got exception:", e); 110231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 111231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 112231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 113231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 114231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public FileOutputStream openAppend() throws IOException { 115231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 116231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return new FileOutputStream(mBaseName, true); 117231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e) { 118231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't append " + mBaseName); 119231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 120231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 121231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 122231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public void truncate() throws IOException { 123231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 124231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileOutputStream fos = new FileOutputStream(mBaseName); 1258bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn FileUtils.sync(fos); 126231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn fos.close(); 127231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (FileNotFoundException e) { 128231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn throw new IOException("Couldn't append " + mBaseName); 129231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } catch (IOException e) { 130231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 131231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 1320068d3dcf1f1a804554a1a09e3b173ac12651786Dianne Hackborn 1330068d3dcf1f1a804554a1a09e3b173ac12651786Dianne Hackborn public boolean exists() { 1340068d3dcf1f1a804554a1a09e3b173ac12651786Dianne Hackborn return mBaseName.exists() || mBackupName.exists(); 1350068d3dcf1f1a804554a1a09e3b173ac12651786Dianne Hackborn } 1360068d3dcf1f1a804554a1a09e3b173ac12651786Dianne Hackborn 1370068d3dcf1f1a804554a1a09e3b173ac12651786Dianne Hackborn public void delete() { 1380068d3dcf1f1a804554a1a09e3b173ac12651786Dianne Hackborn mBaseName.delete(); 1390068d3dcf1f1a804554a1a09e3b173ac12651786Dianne Hackborn mBackupName.delete(); 1400068d3dcf1f1a804554a1a09e3b173ac12651786Dianne Hackborn } 1410068d3dcf1f1a804554a1a09e3b173ac12651786Dianne Hackborn 142231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public FileInputStream openRead() throws FileNotFoundException { 143231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (mBackupName.exists()) { 144231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBaseName.delete(); 145231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn mBackupName.renameTo(mBaseName); 146231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 147231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return new FileInputStream(mBaseName); 148231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 149231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn 150231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn public byte[] readFully() throws IOException { 151231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn FileInputStream stream = openRead(); 152231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn try { 153231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn int pos = 0; 154231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn int avail = stream.available(); 155231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn byte[] data = new byte[avail]; 156231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn while (true) { 157231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn int amt = stream.read(data, pos, data.length-pos); 158231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn //Log.i("foo", "Read " + amt + " bytes at " + pos 159231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn // + " of avail " + data.length); 160231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (amt <= 0) { 161231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn //Log.i("foo", "**** FINISHED READING: pos=" + pos 162231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn // + " len=" + data.length); 163231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn return data; 164231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 165231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn pos += amt; 166231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn avail = stream.available(); 167231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn if (avail > data.length-pos) { 168231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn byte[] newData = new byte[pos+avail]; 169231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn System.arraycopy(data, 0, newData, 0, pos); 170231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn data = newData; 171231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 172231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 173231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } finally { 174231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn stream.close(); 175231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 176231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn } 177231cc608d06ffc31c24bf8aa8c8275bdd2636581Dianne Hackborn} 178