1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.internal.fsck;
45
46 import java.io.IOException;
47 import java.nio.ByteBuffer;
48 import java.nio.channels.Channels;
49 import java.text.MessageFormat;
50 import java.util.Arrays;
51 import java.util.HashSet;
52 import java.util.Set;
53 import java.util.zip.CRC32;
54
55 import org.eclipse.jgit.errors.CorruptObjectException;
56 import org.eclipse.jgit.errors.CorruptPackIndexException;
57 import org.eclipse.jgit.errors.CorruptPackIndexException.ErrorType;
58 import org.eclipse.jgit.errors.MissingObjectException;
59 import org.eclipse.jgit.internal.JGitText;
60 import org.eclipse.jgit.internal.fsck.FsckError.CorruptObject;
61 import org.eclipse.jgit.internal.storage.dfs.ReadableChannel;
62 import org.eclipse.jgit.internal.storage.file.PackIndex;
63 import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
64 import org.eclipse.jgit.lib.AnyObjectId;
65 import org.eclipse.jgit.lib.ObjectDatabase;
66 import org.eclipse.jgit.lib.ObjectIdOwnerMap;
67 import org.eclipse.jgit.transport.PackParser;
68 import org.eclipse.jgit.transport.PackedObjectInfo;
69
70
71
72
73 public class FsckPackParser extends PackParser {
74 private final CRC32 crc;
75
76 private final ReadableChannel channel;
77
78 private final Set<CorruptObject> corruptObjects = new HashSet<>();
79
80 private long expectedObjectCount = -1L;
81
82 private long offset;
83
84 private int blockSize;
85
86
87
88
89
90
91
92
93
94 public FsckPackParser(ObjectDatabase db, ReadableChannel channel) {
95 super(db, Channels.newInputStream(channel));
96 this.channel = channel;
97 setCheckObjectCollisions(false);
98 this.crc = new CRC32();
99 this.blockSize = channel.blockSize() > 0 ? channel.blockSize() : 65536;
100 }
101
102
103 @Override
104 protected void onPackHeader(long objCnt) throws IOException {
105 if (expectedObjectCount >= 0) {
106
107
108
109
110 setExpectedObjectCount(expectedObjectCount);
111 }
112 }
113
114
115 @Override
116 protected void onBeginWholeObject(long streamPosition, int type,
117 long inflatedSize) throws IOException {
118 crc.reset();
119 }
120
121
122 @Override
123 protected void onObjectHeader(Source src, byte[] raw, int pos, int len)
124 throws IOException {
125 crc.update(raw, pos, len);
126 }
127
128
129 @Override
130 protected void onObjectData(Source src, byte[] raw, int pos, int len)
131 throws IOException {
132 crc.update(raw, pos, len);
133 }
134
135
136 @Override
137 protected void onEndWholeObject(PackedObjectInfo info) throws IOException {
138 info.setCRC((int) crc.getValue());
139 }
140
141
142 @Override
143 protected void onBeginOfsDelta(long deltaStreamPosition,
144 long baseStreamPosition, long inflatedSize) throws IOException {
145 crc.reset();
146 }
147
148
149 @Override
150 protected void onBeginRefDelta(long deltaStreamPosition, AnyObjectId baseId,
151 long inflatedSize) throws IOException {
152 crc.reset();
153 }
154
155
156 @Override
157 protected UnresolvedDelta onEndDelta() throws IOException {
158 UnresolvedDelta delta = new UnresolvedDelta();
159 delta.setCRC((int) crc.getValue());
160 return delta;
161 }
162
163
164 @Override
165 protected void onInflatedObjectData(PackedObjectInfo obj, int typeCode,
166 byte[] data) throws IOException {
167
168 }
169
170
171 @Override
172 protected void verifySafeObject(final AnyObjectId id, final int type,
173 final byte[] data) {
174 try {
175 super.verifySafeObject(id, type, data);
176 } catch (CorruptObjectException e) {
177
178 CorruptObject o = new CorruptObject(id.toObjectId(), type);
179 if (e.getErrorType() != null) {
180 o.setErrorType(e.getErrorType());
181 }
182 corruptObjects.add(o);
183 }
184 }
185
186
187 @Override
188 protected void onPackFooter(byte[] hash) throws IOException {
189
190 }
191
192
193 @Override
194 protected boolean onAppendBase(int typeCode, byte[] data,
195 PackedObjectInfo info) throws IOException {
196
197 return false;
198 }
199
200
201 @Override
202 protected void onEndThinPack() throws IOException {
203
204 }
205
206
207 @Override
208 protected ObjectTypeAndSize seekDatabase(PackedObjectInfo obj,
209 ObjectTypeAndSize info) throws IOException {
210 crc.reset();
211 offset = obj.getOffset();
212 return readObjectHeader(info);
213 }
214
215
216 @Override
217 protected ObjectTypeAndSize seekDatabase(UnresolvedDelta delta,
218 ObjectTypeAndSize info) throws IOException {
219 crc.reset();
220 offset = delta.getOffset();
221 return readObjectHeader(info);
222 }
223
224
225 @Override
226 protected int readDatabase(byte[] dst, int pos, int cnt)
227 throws IOException {
228
229 int n = read(offset, dst, pos, cnt);
230 if (n > 0) {
231 offset += n;
232 }
233 return n;
234 }
235
236 int read(long channelPosition, byte[] dst, int pos, int cnt)
237 throws IOException {
238 long block = channelPosition / blockSize;
239 byte[] bytes = readFromChannel(block);
240 if (bytes == null) {
241 return -1;
242 }
243 int offs = (int) (channelPosition - block * blockSize);
244 int bytesToCopy = Math.min(cnt, bytes.length - offs);
245 if (bytesToCopy < 1) {
246 return -1;
247 }
248 System.arraycopy(bytes, offs, dst, pos, bytesToCopy);
249 return bytesToCopy;
250 }
251
252 private byte[] readFromChannel(long block) throws IOException {
253 channel.position(block * blockSize);
254 ByteBuffer buf = ByteBuffer.allocate(blockSize);
255 int totalBytesRead = 0;
256 while (totalBytesRead < blockSize) {
257 int bytesRead = channel.read(buf);
258 if (bytesRead == -1) {
259 if (totalBytesRead == 0) {
260 return null;
261 }
262 return Arrays.copyOf(buf.array(), totalBytesRead);
263 }
264 totalBytesRead += bytesRead;
265 }
266 return buf.array();
267 }
268
269
270 @Override
271 protected boolean checkCRC(int oldCRC) {
272 return oldCRC == (int) crc.getValue();
273 }
274
275
276 @Override
277 protected void onStoreStream(byte[] raw, int pos, int len)
278 throws IOException {
279
280 }
281
282
283
284
285
286
287
288
289 public Set<CorruptObject> getCorruptObjects() {
290 return corruptObjects;
291 }
292
293
294
295
296
297
298
299
300
301 public void verifyIndex(PackIndex idx)
302 throws CorruptPackIndexException {
303 ObjectIdOwnerMap<ObjFromPack> inPack = new ObjectIdOwnerMap<>();
304 for (int i = 0; i < getObjectCount(); i++) {
305 PackedObjectInfo entry = getObject(i);
306 inPack.add(new ObjFromPack(entry));
307
308 long offs = idx.findOffset(entry);
309 if (offs == -1) {
310 throw new CorruptPackIndexException(
311 MessageFormat.format(JGitText.get().missingObject,
312 Integer.valueOf(entry.getType()),
313 entry.getName()),
314 ErrorType.MISSING_OBJ);
315 } else if (offs != entry.getOffset()) {
316 throw new CorruptPackIndexException(MessageFormat
317 .format(JGitText.get().mismatchOffset, entry.getName()),
318 ErrorType.MISMATCH_OFFSET);
319 }
320
321 try {
322 if (idx.hasCRC32Support()
323 && (int) idx.findCRC32(entry) != entry.getCRC()) {
324 throw new CorruptPackIndexException(
325 MessageFormat.format(JGitText.get().mismatchCRC,
326 entry.getName()),
327 ErrorType.MISMATCH_CRC);
328 }
329 } catch (MissingObjectException e) {
330 throw new CorruptPackIndexException(MessageFormat
331 .format(JGitText.get().missingCRC, entry.getName()),
332 ErrorType.MISSING_CRC);
333 }
334 }
335
336 for (MutableEntry entry : idx) {
337 if (!inPack.contains(entry.toObjectId())) {
338 throw new CorruptPackIndexException(MessageFormat.format(
339 JGitText.get().unknownObjectInIndex, entry.name()),
340 ErrorType.UNKNOWN_OBJ);
341 }
342 }
343 }
344
345
346
347
348
349
350
351
352 public void overwriteObjectCount(long objectCount) {
353 this.expectedObjectCount = objectCount;
354 }
355
356 static class ObjFromPack extends ObjectIdOwnerMap.Entry {
357 ObjFromPack(AnyObjectId id) {
358 super(id);
359 }
360 }
361 }