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