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