1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.internal.storage.file;
12
13 import java.io.File;
14 import java.io.FileInputStream;
15 import java.io.FileNotFoundException;
16 import java.io.IOException;
17 import java.nio.file.Files;
18 import java.nio.file.NoSuchFileException;
19 import java.nio.file.StandardCopyOption;
20 import java.text.MessageFormat;
21 import java.util.Set;
22
23 import org.eclipse.jgit.internal.JGitText;
24 import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.InsertLooseObjectResult;
25 import org.eclipse.jgit.lib.AbbreviatedObjectId;
26 import org.eclipse.jgit.lib.AnyObjectId;
27 import org.eclipse.jgit.lib.Constants;
28 import org.eclipse.jgit.lib.ObjectId;
29 import org.eclipse.jgit.lib.ObjectLoader;
30 import org.eclipse.jgit.util.FileUtils;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34
35
36
37
38
39
40
41 class LooseObjects {
42 private static final Logger LOG = LoggerFactory
43 .getLogger(LooseObjects.class);
44
45
46
47
48
49 private final static int MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS = 5;
50
51 private final File directory;
52
53 private final UnpackedObjectCache unpackedObjectCache;
54
55
56
57
58
59
60
61 LooseObjects(File dir) {
62 directory = dir;
63 unpackedObjectCache = new UnpackedObjectCache();
64 }
65
66
67
68
69
70
71 File getDirectory() {
72 return directory;
73 }
74
75 void create() throws IOException {
76 FileUtils.mkdirs(directory);
77 }
78
79 void close() {
80 unpackedObjectCache().clear();
81 }
82
83
84 @Override
85 public String toString() {
86 return "LooseObjects[" + directory + "]";
87 }
88
89 boolean hasCached(AnyObjectId id) {
90 return unpackedObjectCache().isUnpacked(id);
91 }
92
93
94
95
96
97
98
99
100 boolean has(AnyObjectId objectId) {
101 return fileFor(objectId).exists();
102 }
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 boolean resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
119 int matchLimit) {
120 String fanOut = id.name().substring(0, 2);
121 String[] entries = new File(directory, fanOut).list();
122 if (entries != null) {
123 for (String e : entries) {
124 if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2) {
125 continue;
126 }
127 try {
128 ObjectId entId = ObjectId.fromString(fanOut + e);
129 if (id.prefixCompare(entId) == 0) {
130 matches.add(entId);
131 }
132 } catch (IllegalArgumentException notId) {
133 continue;
134 }
135 if (matches.size() > matchLimit) {
136 return false;
137 }
138 }
139 }
140 return true;
141 }
142
143 ObjectLoader open(WindowCursor curs, AnyObjectId id) throws IOException {
144 int readAttempts = 0;
145 while (readAttempts < MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS) {
146 readAttempts++;
147 File path = fileFor(id);
148 try {
149 return getObjectLoader(curs, path, id);
150 } catch (FileNotFoundException noFile) {
151 if (path.exists()) {
152 throw noFile;
153 }
154 break;
155 } catch (IOException e) {
156 if (!FileUtils.isStaleFileHandleInCausalChain(e)) {
157 throw e;
158 }
159 if (LOG.isDebugEnabled()) {
160 LOG.debug(MessageFormat.format(
161 JGitText.get().looseObjectHandleIsStale, id.name(),
162 Integer.valueOf(readAttempts), Integer.valueOf(
163 MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS)));
164 }
165 }
166 }
167 unpackedObjectCache().remove(id);
168 return null;
169 }
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184 ObjectLoader getObjectLoader(WindowCursor curs, File path, AnyObjectId id)
185 throws IOException {
186 try (FileInputStream in = new FileInputStream(path)) {
187 unpackedObjectCache().add(id);
188 return UnpackedObject.open(in, path, id, curs);
189 }
190 }
191
192
193
194
195
196
197
198
199
200
201 UnpackedObjectCache unpackedObjectCache() {
202 return unpackedObjectCache;
203 }
204
205 long getSize(WindowCursor curs, AnyObjectId id) throws IOException {
206 File f = fileFor(id);
207 try (FileInputStream in = new FileInputStream(f)) {
208 unpackedObjectCache().add(id);
209 return UnpackedObject.getSize(in, id, curs);
210 } catch (FileNotFoundException noFile) {
211 if (f.exists()) {
212 throw noFile;
213 }
214 unpackedObjectCache().remove(id);
215 return -1;
216 }
217 }
218
219 InsertLooseObjectResult insert(File tmp, ObjectId id) throws IOException {
220 final File dst = fileFor(id);
221 if (dst.exists()) {
222
223
224
225
226 FileUtils.delete(tmp, FileUtils.RETRY);
227 return InsertLooseObjectResult.EXISTS_LOOSE;
228 }
229
230 try {
231 return tryMove(tmp, dst, id);
232 } catch (NoSuchFileException e) {
233
234
235
236
237
238
239 FileUtils.mkdir(dst.getParentFile(), true);
240 } catch (IOException e) {
241
242
243 LOG.error(e.getMessage(), e);
244 FileUtils.delete(tmp, FileUtils.RETRY);
245 return InsertLooseObjectResult.FAILURE;
246 }
247
248 try {
249 return tryMove(tmp, dst, id);
250 } catch (IOException e) {
251
252
253
254
255 LOG.error(e.getMessage(), e);
256 FileUtils.delete(tmp, FileUtils.RETRY);
257 return InsertLooseObjectResult.FAILURE;
258 }
259 }
260
261 private InsertLooseObjectResult tryMove(File tmp, File dst, ObjectId id)
262 throws IOException {
263 Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst),
264 StandardCopyOption.ATOMIC_MOVE);
265 dst.setReadOnly();
266 unpackedObjectCache().add(id);
267 return InsertLooseObjectResult.INSERTED;
268 }
269
270
271
272
273
274
275
276
277 File fileFor(AnyObjectId objectId) {
278 String n = objectId.name();
279 String d = n.substring(0, 2);
280 String f = n.substring(2);
281 return new File(new File(getDirectory(), d), f);
282 }
283 }