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
45
46
47
48 package org.eclipse.jgit.transport;
49
50 import static org.eclipse.jgit.lib.RefDatabase.ALL;
51
52 import java.io.BufferedInputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.text.MessageFormat;
56 import java.util.ArrayList;
57 import java.util.Collection;
58 import java.util.Collections;
59 import java.util.HashMap;
60 import java.util.LinkedHashMap;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Set;
64
65 import org.eclipse.jgit.errors.MissingBundlePrerequisiteException;
66 import org.eclipse.jgit.errors.MissingObjectException;
67 import org.eclipse.jgit.errors.PackProtocolException;
68 import org.eclipse.jgit.errors.TransportException;
69 import org.eclipse.jgit.internal.JGitText;
70 import org.eclipse.jgit.internal.storage.file.PackLock;
71 import org.eclipse.jgit.lib.Constants;
72 import org.eclipse.jgit.lib.NullProgressMonitor;
73 import org.eclipse.jgit.lib.ObjectId;
74 import org.eclipse.jgit.lib.ObjectIdRef;
75 import org.eclipse.jgit.lib.ObjectInserter;
76 import org.eclipse.jgit.lib.ProgressMonitor;
77 import org.eclipse.jgit.lib.Ref;
78 import org.eclipse.jgit.revwalk.RevCommit;
79 import org.eclipse.jgit.revwalk.RevFlag;
80 import org.eclipse.jgit.revwalk.RevObject;
81 import org.eclipse.jgit.revwalk.RevWalk;
82 import org.eclipse.jgit.util.IO;
83 import org.eclipse.jgit.util.RawParseUtils;
84
85
86
87
88
89 class BundleFetchConnection extends BaseFetchConnection {
90
91 private final Transport transport;
92
93 InputStream bin;
94
95 final Map<ObjectId, String> prereqs = new HashMap<>();
96
97 private String lockMessage;
98
99 private PackLock packLock;
100
101 BundleFetchConnection(Transport transportBundle, final InputStream src) throws TransportException {
102 transport = transportBundle;
103 bin = new BufferedInputStream(src);
104 try {
105 switch (readSignature()) {
106 case 2:
107 readBundleV2();
108 break;
109 default:
110 throw new TransportException(transport.uri, JGitText.get().notABundle);
111 }
112 } catch (TransportException err) {
113 close();
114 throw err;
115 } catch (IOException err) {
116 close();
117 throw new TransportException(transport.uri, err.getMessage(), err);
118 } catch (RuntimeException err) {
119 close();
120 throw new TransportException(transport.uri, err.getMessage(), err);
121 }
122 }
123
124 private int readSignature() throws IOException {
125 final String rev = readLine(new byte[1024]);
126 if (TransportBundle.V2_BUNDLE_SIGNATURE.equals(rev))
127 return 2;
128 throw new TransportException(transport.uri, JGitText.get().notABundle);
129 }
130
131 private void readBundleV2() throws IOException {
132 final byte[] hdrbuf = new byte[1024];
133 final LinkedHashMap<String, Ref> avail = new LinkedHashMap<>();
134 for (;;) {
135 String line = readLine(hdrbuf);
136 if (line.length() == 0)
137 break;
138
139 if (line.charAt(0) == '-') {
140 ObjectId id = ObjectId.fromString(line.substring(1, 41));
141 String shortDesc = null;
142 if (line.length() > 42)
143 shortDesc = line.substring(42);
144 prereqs.put(id, shortDesc);
145 continue;
146 }
147
148 final String name = line.substring(41, line.length());
149 final ObjectId id = ObjectId.fromString(line.substring(0, 40));
150 final Ref prior = avail.put(name, new ObjectIdRef.Unpeeled(
151 Ref.Storage.NETWORK, name, id));
152 if (prior != null)
153 throw duplicateAdvertisement(name);
154 }
155 available(avail);
156 }
157
158 private PackProtocolException duplicateAdvertisement(final String name) {
159 return new PackProtocolException(transport.uri,
160 MessageFormat.format(JGitText.get().duplicateAdvertisementsOf, name));
161 }
162
163 private String readLine(final byte[] hdrbuf) throws IOException {
164 StringBuilder line = new StringBuilder();
165 boolean done = false;
166 while (!done) {
167 bin.mark(hdrbuf.length);
168 final int cnt = bin.read(hdrbuf);
169 int lf = 0;
170 while (lf < cnt && hdrbuf[lf] != '\n')
171 lf++;
172 bin.reset();
173 IO.skipFully(bin, lf);
174 if (lf < cnt && hdrbuf[lf] == '\n') {
175 IO.skipFully(bin, 1);
176 done = true;
177 }
178 line.append(RawParseUtils.decode(Constants.CHARSET, hdrbuf, 0, lf));
179 }
180 return line.toString();
181 }
182
183 @Override
184 public boolean didFetchTestConnectivity() {
185 return false;
186 }
187
188 @Override
189 protected void doFetch(final ProgressMonitor monitor,
190 final Collection<Ref> want, final Set<ObjectId> have)
191 throws TransportException {
192 verifyPrerequisites();
193 try {
194 try (ObjectInserter ins = transport.local.newObjectInserter()) {
195 PackParser parser = ins.newPackParser(bin);
196 parser.setAllowThin(true);
197 parser.setObjectChecker(transport.getObjectChecker());
198 parser.setLockMessage(lockMessage);
199 packLock = parser.parse(NullProgressMonitor.INSTANCE);
200 ins.flush();
201 }
202 } catch (IOException err) {
203 close();
204 throw new TransportException(transport.uri, err.getMessage(), err);
205 } catch (RuntimeException err) {
206 close();
207 throw new TransportException(transport.uri, err.getMessage(), err);
208 }
209 }
210
211 @Override
212 public void setPackLockMessage(final String message) {
213 lockMessage = message;
214 }
215
216 @Override
217 public Collection<PackLock> getPackLocks() {
218 if (packLock != null)
219 return Collections.singleton(packLock);
220 return Collections.<PackLock> emptyList();
221 }
222
223 private void verifyPrerequisites() throws TransportException {
224 if (prereqs.isEmpty())
225 return;
226
227 try (final RevWalk rw = new RevWalk(transport.local)) {
228 final RevFlag PREREQ = rw.newFlag("PREREQ");
229 final RevFlag SEEN = rw.newFlag("SEEN");
230
231 final Map<ObjectId, String> missing = new HashMap<>();
232 final List<RevObject> commits = new ArrayList<>();
233 for (final Map.Entry<ObjectId, String> e : prereqs.entrySet()) {
234 ObjectId p = e.getKey();
235 try {
236 final RevCommit c = rw.parseCommit(p);
237 if (!c.has(PREREQ)) {
238 c.add(PREREQ);
239 commits.add(c);
240 }
241 } catch (MissingObjectException notFound) {
242 missing.put(p, e.getValue());
243 } catch (IOException err) {
244 throw new TransportException(transport.uri, MessageFormat
245 .format(JGitText.get().cannotReadCommit, p.name()),
246 err);
247 }
248 }
249 if (!missing.isEmpty())
250 throw new MissingBundlePrerequisiteException(transport.uri,
251 missing);
252
253 Map<String, Ref> localRefs;
254 try {
255 localRefs = transport.local.getRefDatabase().getRefs(ALL);
256 } catch (IOException e) {
257 throw new TransportException(transport.uri, e.getMessage(), e);
258 }
259 for (final Ref r : localRefs.values()) {
260 try {
261 rw.markStart(rw.parseCommit(r.getObjectId()));
262 } catch (IOException readError) {
263
264 }
265 }
266
267 int remaining = commits.size();
268 try {
269 RevCommit c;
270 while ((c = rw.next()) != null) {
271 if (c.has(PREREQ)) {
272 c.add(SEEN);
273 if (--remaining == 0)
274 break;
275 }
276 }
277 } catch (IOException err) {
278 throw new TransportException(transport.uri,
279 JGitText.get().cannotReadObject, err);
280 }
281
282 if (remaining > 0) {
283 for (final RevObject o : commits) {
284 if (!o.has(SEEN))
285 missing.put(o, prereqs.get(o));
286 }
287 throw new MissingBundlePrerequisiteException(transport.uri,
288 missing);
289 }
290 }
291 }
292
293 @Override
294 public void close() {
295 if (bin != null) {
296 try {
297 bin.close();
298 } catch (IOException ie) {
299
300 } finally {
301 bin = null;
302 }
303 }
304 }
305 }