1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.eclipse.jgit.storage.file;
18
19 import static java.nio.charset.StandardCharsets.UTF_8;
20
21 import java.io.ByteArrayOutputStream;
22 import java.io.File;
23 import java.io.IOException;
24 import java.text.MessageFormat;
25
26 import org.eclipse.jgit.errors.ConfigInvalidException;
27 import org.eclipse.jgit.errors.LockFailedException;
28 import org.eclipse.jgit.internal.JGitText;
29 import org.eclipse.jgit.internal.storage.file.FileSnapshot;
30 import org.eclipse.jgit.internal.storage.file.LockFile;
31 import org.eclipse.jgit.lib.Config;
32 import org.eclipse.jgit.lib.Constants;
33 import org.eclipse.jgit.lib.ObjectId;
34 import org.eclipse.jgit.lib.StoredConfig;
35 import org.eclipse.jgit.util.FS;
36 import org.eclipse.jgit.util.FileUtils;
37 import org.eclipse.jgit.util.IO;
38 import org.eclipse.jgit.util.RawParseUtils;
39
40
41
42
43 public class FileBasedConfig extends StoredConfig {
44
45 private final File configFile;
46
47 private final FS fs;
48
49 private boolean utf8Bom;
50
51 private volatile FileSnapshot snapshot;
52
53 private volatile ObjectId hash;
54
55
56
57
58
59
60
61
62
63
64 public FileBasedConfig(File cfgLocation, FS fs) {
65 this(null, cfgLocation, fs);
66 }
67
68
69
70
71
72
73
74
75
76
77
78
79 public FileBasedConfig(Config base, File cfgLocation, FS fs) {
80 super(base);
81 configFile = cfgLocation;
82 this.fs = fs;
83 this.snapshot = FileSnapshot.DIRTY;
84 this.hash = ObjectId.zeroId();
85 }
86
87
88 @Override
89 protected boolean notifyUponTransientChanges() {
90
91 return false;
92 }
93
94
95
96
97
98
99 public final File getFile() {
100 return configFile;
101 }
102
103
104
105
106
107
108
109
110
111 @Override
112 public void load() throws IOException, ConfigInvalidException {
113 try {
114 FileSnapshot[] lastSnapshot = { null };
115 Boolean wasRead = FileUtils.readWithRetries(getFile(), f -> {
116 final FileSnapshot oldSnapshot = snapshot;
117 final FileSnapshot newSnapshot;
118
119 newSnapshot = FileSnapshot.saveNoConfig(f);
120 lastSnapshot[0] = newSnapshot;
121 final byte[] in = IO.readFully(f);
122 final ObjectId newHash = hash(in);
123 if (hash.equals(newHash)) {
124 if (oldSnapshot.equals(newSnapshot)) {
125 oldSnapshot.setClean(newSnapshot);
126 } else {
127 snapshot = newSnapshot;
128 }
129 } else {
130 final String decoded;
131 if (isUtf8(in)) {
132 decoded = RawParseUtils.decode(UTF_8,
133 in, 3, in.length);
134 utf8Bom = true;
135 } else {
136 decoded = RawParseUtils.decode(in);
137 }
138 fromText(decoded);
139 snapshot = newSnapshot;
140 hash = newHash;
141 }
142 return Boolean.TRUE;
143 });
144 if (wasRead == null) {
145 clear();
146 snapshot = lastSnapshot[0];
147 }
148 } catch (IOException e) {
149 throw e;
150 } catch (Exception e) {
151 throw new ConfigInvalidException(MessageFormat
152 .format(JGitText.get().cannotReadFile, getFile()), e);
153 }
154 }
155
156
157
158
159
160
161
162
163
164
165
166
167 @Override
168 public void save() throws IOException {
169 final byte[] out;
170 final String text = toText();
171 if (utf8Bom) {
172 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
173 bos.write(0xEF);
174 bos.write(0xBB);
175 bos.write(0xBF);
176 bos.write(text.getBytes(UTF_8));
177 out = bos.toByteArray();
178 } else {
179 out = Constants.encode(text);
180 }
181
182 final LockFile lf = new LockFile(getFile());
183 try {
184 if (!lf.lock()) {
185 throw new LockFailedException(getFile());
186 }
187 lf.setNeedSnapshotNoConfig(true);
188 lf.write(out);
189 if (!lf.commit())
190 throw new IOException(MessageFormat.format(JGitText.get().cannotCommitWriteTo, getFile()));
191 } finally {
192 lf.unlock();
193 }
194 snapshot = lf.getCommitSnapshot();
195 hash = hash(out);
196
197 fireConfigChangedEvent();
198 }
199
200
201 @Override
202 public void clear() {
203 hash = hash(new byte[0]);
204 super.clear();
205 }
206
207 private static ObjectId hash(byte[] rawText) {
208 return ObjectId.fromRaw(Constants.newMessageDigest().digest(rawText));
209 }
210
211
212 @SuppressWarnings("nls")
213 @Override
214 public String toString() {
215 return getClass().getSimpleName() + "[" + getFile().getPath() + "]";
216 }
217
218
219
220
221
222
223
224 public boolean isOutdated() {
225 return snapshot.isModified(getFile());
226 }
227
228
229
230
231
232
233 @Override
234 protected byte[] readIncludedConfig(String relPath)
235 throws ConfigInvalidException {
236 final File file;
237 if (relPath.startsWith("~/")) {
238 file = fs.resolve(fs.userHome(), relPath.substring(2));
239 } else {
240 file = fs.resolve(configFile.getParentFile(), relPath);
241 }
242
243 if (!file.exists()) {
244 return null;
245 }
246
247 try {
248 return IO.readFully(file);
249 } catch (IOException ioe) {
250 throw new ConfigInvalidException(MessageFormat
251 .format(JGitText.get().cannotReadFile, relPath), ioe);
252 }
253 }
254 }