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